Asoka.Wang 21f044712c 1
2025-08-27 18:39:19 +08:00

721 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using FreeSql.DataAnnotations;
using ZhonTai.Admin.Core.Entities;
namespace NPP.SmartSchedue.Api.Contracts.Domain.Integration
{
/// <summary>
/// 智能整合记录实体
/// 用于记录每次智能任务整合的完整过程和结果
///
/// 业务思考:
/// 1. 作为重要的业务审计数据,需要完整记录整合的全过程
/// 2. 支持多租户模式,确保数据安全隔离
/// 3. 提供丰富的查询维度,支持历史分析和决策支持
/// 4. 考虑JSON存储复杂结果数据平衡存储效率和查询性能
/// </summary>
[Table(Name = "sse_integration_record")]
[Index("idx_integration_batch_code", "IntegrationBatchCode", true)] // 批次编码唯一索引
[Index("idx_integration_time", "IntegrationTime")] // 时间索引,支持时间范围查询
[Index("idx_operator", "OperatorUserId")] // 操作员索引
[Index("idx_tenant_time", "TenantId,IntegrationTime")] // 租户+时间组合索引
public class IntegrationRecordEntity : EntityTenant
{
/// <summary>
/// 整合批次编码
/// 格式INT + yyyyMMddHHmmss + XXXINT20240314143025123
/// 业务意义:唯一标识一次整合操作,便于追踪和关联
/// </summary>
[Column(StringLength = 30)]
[Required(ErrorMessage = "整合批次编码不能为空")]
public string IntegrationBatchCode { get; set; } = string.Empty;
/// <summary>
/// 整合执行时间
/// 记录实际执行整合操作的时间点
/// </summary>
[Required]
public DateTime IntegrationTime { get; set; } = DateTime.Now;
/// <summary>
/// 操作人员用户ID
/// 关联到系统用户表,记录执行整合的操作员
/// </summary>
[Required(ErrorMessage = "操作人员ID不能为空")]
public long OperatorUserId { get; set; }
/// <summary>
/// 操作人员用户名
/// 冗余存储,提高查询性能,避免频繁关联用户表
/// </summary>
[Column(StringLength = 50)]
[Required(ErrorMessage = "操作人员用户名不能为空")]
public string OperatorUserName { get; set; } = string.Empty;
/// <summary>
/// 操作人员真实姓名
/// 用于业务场景下的可读性显示
/// </summary>
[Column(StringLength = 50)]
[Required(ErrorMessage = "操作人员姓名不能为空")]
public string OperatorRealName { get; set; } = string.Empty;
/// <summary>
/// 整合的任务ID列表JSON格式存储
/// 存储格式:[1,2,3,4,5]
/// 业务意义:记录本次整合涉及的所有任务,支持反向查询
/// </summary>
[Column(DbType = "longtext")]
[Required(ErrorMessage = "任务ID列表不能为空")]
public string TaskIdsJson { get; set; } = "[]";
/// <summary>
/// 整合策略配置JSON格式存储
/// 包含:人员分配策略、设备分配策略、各种配置参数
/// 业务价值:记录决策依据,支持策略效果分析和优化
/// </summary>
[Column(DbType = "json")]
[Required]
public string StrategyConfigJson { get; set; } = "{}";
/// <summary>
/// 人员分配结果详情JSON格式存储
/// 包含:成功匹配列表、失败原因、工作负载分析等
/// 存储完整的PersonnelAllocationResult对象
/// </summary>
[Column(DbType = "json")]
public string PersonnelAllocationResultJson { get; set; } = "{}";
/// <summary>
/// 设备分配结果详情JSON格式存储
/// 包含:成功匹配列表、失败原因、利用率分析等
/// 存储完整的EquipmentAllocationResult对象
/// </summary>
[Column(DbType = "json")]
public string EquipmentAllocationResultJson { get; set; } = "{}";
/// <summary>
/// 成功分配的任务数量
/// 统计字段,提高查询性能
/// </summary>
[Required]
public int SuccessTaskCount { get; set; } = 0;
/// <summary>
/// 分配失败的任务数量
/// 统计字段,用于成功率计算和分析
/// </summary>
[Required]
public int FailedTaskCount { get; set; } = 0;
/// <summary>
/// 整合执行总耗时(毫秒)
/// 性能监控指标,用于系统优化分析
/// </summary>
[Required]
public long ElapsedMilliseconds { get; set; } = 0;
/// <summary>
/// 人员分配公平性评分0-100
/// 业务指标,评估人员工作负载分配的公平程度
/// </summary>
public int PersonnelFairnessScore { get; set; } = 0;
/// <summary>
/// 设备整体利用率百分比保留1位小数
/// 业务指标,评估设备资源利用效率
/// </summary>
[Column(DbType = "decimal(5,1)")]
public decimal EquipmentUtilizationRate { get; set; } = 0.0m;
/// <summary>
/// 整合结果摘要描述
/// 简洁的文本描述,便于快速了解整合结果
/// 示例:总任务数: 25, 成功: 23, 失败: 2, 成功率: 92%
/// </summary>
[Column(StringLength = 500)]
public string ResultSummary { get; set; } = string.Empty;
/// <summary>
/// 失败原因分类统计JSON格式存储
/// 格式:{"PersonnelAllocationFailed": 2, "EquipmentAllocationFailed": 1}
/// 业务价值:支持失败原因分析和系统改进
/// </summary>
[Column(DbType = "json")]
public string FailureReasonStats { get; set; } = "{}";
/// <summary>
/// 涉及的项目编号列表(逗号分隔)
/// 便于按项目查询整合历史,支持项目级别的数据分析
/// 示例PRJ001,PRJ002,PRJ003
/// </summary>
[Column(StringLength = 500)]
public string ProjectNumbers { get; set; } = string.Empty;
/// <summary>
/// 整合类型
/// 标识整合的触发方式Manual-手动触发, Scheduled-定时触发, Auto-自动触发
/// </summary>
[Column(StringLength = 20)]
[Required]
public string IntegrationType { get; set; } = "Manual";
/// <summary>
/// 业务备注信息
/// 操作员填写的备注,记录特殊情况或业务背景
/// </summary>
[Column(StringLength = 1000)]
public string Remarks { get; set; } = string.Empty;
/// <summary>
/// 是否为测试数据
/// 区分生产数据和测试数据,便于数据清理和分析
/// </summary>
[Required]
public bool IsTestData { get; set; } = false;
/// <summary>
/// 数据版本号
/// 支持数据结构演进和向后兼容
/// </summary>
[Required]
public int DataVersion { get; set; } = 1;
/// <summary>
/// 整合记录发布状态
/// 控制整合记录的生命周期管理
/// Draft-草稿, Published-已发布, Completed-已完成, Cancelled-已撤销
/// </summary>
[Column(StringLength = 20)]
[Required]
public string PublishStatus { get; set; } = IntegrationRecordPublishStatus.Draft;
/// <summary>
/// 发布时间
/// 记录正式发布生效的时间点
/// </summary>
public DateTime? PublishedTime { get; set; }
/// <summary>
/// 发布操作员用户ID
/// 记录执行发布操作的用户,可能与创建者不同
/// </summary>
public long? PublishedByUserId { get; set; }
/// <summary>
/// 发布操作员用户名
/// </summary>
[Column(StringLength = 50)]
public string PublishedByUserName { get; set; } = string.Empty;
/// <summary>
/// 发布操作员真实姓名
/// </summary>
[Column(StringLength = 50)]
public string PublishedByRealName { get; set; } = string.Empty;
/// <summary>
/// 发布说明
/// 记录发布时的业务说明或特殊情况备注
/// </summary>
[Column(StringLength = 500)]
public string PublishRemarks { get; set; } = string.Empty;
/// <summary>
/// 完成时间
/// 所有任务执行完毕的时间点
/// </summary>
public DateTime? CompletedTime { get; set; }
/// <summary>
/// 撤销时间
/// 记录被撤销的时间点
/// </summary>
public DateTime? CancelledTime { get; set; }
/// <summary>
/// 撤销操作员用户ID
/// </summary>
public long? CancelledByUserId { get; set; }
/// <summary>
/// 撤销操作员用户名
/// </summary>
[Column(StringLength = 50)]
public string CancelledByUserName { get; set; } = string.Empty;
/// <summary>
/// 撤销原因
/// 记录撤销的具体原因和说明
/// </summary>
[Column(StringLength = 500)]
public string CancelReason { get; set; } = string.Empty;
#region
/// <summary>
/// 当前版本号
/// 每次修改后自增,用于版本管理和冲突检测
/// </summary>
[Required]
public int CurrentVersion { get; set; } = 1;
/// <summary>
/// 父版本ID
/// 指向上一个版本的整合记录,构建版本链
/// null表示这是初始版本
/// </summary>
public long? ParentVersionId { get; set; }
/// <summary>
/// 根版本ID
/// 指向版本链的根节点,用于快速定位同一整合记录的所有版本
/// </summary>
[Required]
public long RootVersionId { get; set; }
/// <summary>
/// 版本创建原因
/// 记录为什么创建这个新版本TaskModified-任务修改, ManualAdjustment-手动调整, SystemReallocation-系统重新分配
/// </summary>
[Column(StringLength = 50)]
public string VersionReason { get; set; } = string.Empty;
/// <summary>
/// 变更摘要
/// 简述本版本相对于上一版本的主要变更内容
/// </summary>
[Column(StringLength = 1000)]
public string ChangeSummary { get; set; } = string.Empty;
/// <summary>
/// 变更详情JSON
/// 存储详细的变更信息,包括变更类型、影响的任务、前后对比等
/// 格式:{"changedTasks": [...], "modifications": [...], "affectedPersonnel": [...]}
/// </summary>
[Column(DbType = "json")]
public string ChangeDetailsJson { get; set; } = "{}";
/// <summary>
/// 是否为活跃版本
/// true表示这是当前生效的版本每个根版本只能有一个活跃版本
/// </summary>
[Required]
public bool IsActiveVersion { get; set; } = true;
/// <summary>
/// 变更触发源
/// 记录变更的触发来源TaskChange-任务变更, UserModification-用户修改, SystemOptimization-系统优化
/// </summary>
[Column(StringLength = 30)]
public string ChangeTriggerSource { get; set; } = "Initial";
/// <summary>
/// 原始任务数量
/// 记录初始分配时的任务数量,用于对比分析
/// </summary>
public int OriginalTaskCount { get; set; } = 0;
/// <summary>
/// 变更影响评分
/// 0-100分评估本次变更对整体分配方案的影响程度
/// </summary>
public int ChangeImpactScore { get; set; } = 0;
/// <summary>
/// 自动重新分配标志
/// 标记是否可以自动进行重新分配,还是需要人工干预
/// </summary>
[Required]
public bool AllowAutoReallocation { get; set; } = true;
/// <summary>
/// 上次检查变更时间
/// 用于变更检测和增量更新机制
/// </summary>
public DateTime? LastChangeCheckTime { get; set; }
/// <summary>
/// 快照数据JSON
/// 存储关键业务数据的快照,用于版本对比和回滚
/// 包含:任务分配快照、人员工作负载快照、设备利用率快照等
/// </summary>
[Column(DbType = "json")]
public string SnapshotDataJson { get; set; } = "{}";
#endregion
#region
/// <summary>
/// 整合成功率(计算属性)
/// 返回成功率百分比保留1位小数
/// </summary>
[Navigate(nameof(SuccessTaskCount))]
public decimal SuccessRate
{
get
{
var totalTasks = SuccessTaskCount + FailedTaskCount;
return totalTasks > 0 ? Math.Round((decimal)SuccessTaskCount / totalTasks * 100, 1) : 0;
}
set { } // FreeSql需要set访问器但这是计算属性所以提供空实现
}
/// <summary>
/// 任务总数(计算属性)
/// </summary>
[Navigate(nameof(SuccessTaskCount))]
public int TotalTaskCount
{
get => SuccessTaskCount + FailedTaskCount;
set { } // FreeSql需要set访问器但这是计算属性所以提供空实现
}
/// <summary>
/// 整体匹配成功率(计算属性)
/// </summary>
public decimal OverallMatchSuccessRate => SuccessRate / 100;
/// <summary>
/// 平均任务复杂度(计算属性)
/// </summary>
public decimal AverageTaskComplexity => 2.5m; // 暂时使用固定值
/// <summary>
/// 平均每任务耗时(计算属性)
/// 返回毫秒数,用于性能分析
/// </summary>
[Navigate(nameof(ElapsedMilliseconds))]
public long AverageTimePerTask
{
get
{
var totalTasks = TotalTaskCount;
return totalTasks > 0 ? ElapsedMilliseconds / totalTasks : 0;
}
set { } // FreeSql需要set访问器但这是计算属性所以提供空实现
}
#endregion
#region
/// <summary>
/// 反序列化任务ID列表
/// 将JSON格式的TaskIdsJson转换为List&lt;long&gt;
/// </summary>
public List<long> GetTaskIds()
{
try
{
if (string.IsNullOrWhiteSpace(TaskIdsJson))
return new List<long>();
// 优先尝试JSON反序列化
try
{
return System.Text.Json.JsonSerializer.Deserialize<List<long>>(TaskIdsJson) ?? new List<long>();
}
catch
{
// 兼容旧格式(逗号分隔)
return TaskIdsJson.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Trim())
.Where(p => !string.IsNullOrEmpty(p))
.Select(long.Parse)
.ToList();
}
}
catch
{
return new List<long>();
}
}
/// <summary>
/// 设置任务ID列表
/// 将List&lt;long&gt;序列化为JSON存储
/// </summary>
public void SetTaskIds(List<long> taskIds)
{
TaskIdsJson = System.Text.Json.JsonSerializer.Serialize(taskIds ?? new List<long>());
}
/// <summary>
/// 获取项目编号列表
/// 将逗号分隔的项目编号字符串转换为列表
/// </summary>
public List<string> GetProjectNumbers()
{
if (string.IsNullOrWhiteSpace(ProjectNumbers))
return new List<string>();
return ProjectNumbers.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Trim())
.Where(p => !string.IsNullOrEmpty(p))
.ToList();
}
/// <summary>
/// 设置项目编号列表
/// 将列表转换为逗号分隔的字符串存储
/// </summary>
public void SetProjectNumbers(List<string> projectNumbers)
{
if (projectNumbers == null || !projectNumbers.Any())
{
ProjectNumbers = string.Empty;
return;
}
ProjectNumbers = string.Join(",", projectNumbers.Where(p => !string.IsNullOrWhiteSpace(p)));
}
/// <summary>
/// 更新统计信息
/// 根据分配结果更新各项统计字段
/// </summary>
public void UpdateStatistics(int successCount, int failedCount, long elapsedMs, int fairnessScore, decimal utilizationRate)
{
SuccessTaskCount = successCount;
FailedTaskCount = failedCount;
ElapsedMilliseconds = elapsedMs;
PersonnelFairnessScore = fairnessScore;
EquipmentUtilizationRate = utilizationRate;
ResultSummary = $"总任务数: {TotalTaskCount}, 成功: {SuccessTaskCount}, 失败: {FailedTaskCount}, 成功率: {SuccessRate}%";
}
#region
/// <summary>
/// 创建新版本
/// 基于当前版本创建一个新的版本记录,用于变更追踪
/// </summary>
/// <param name="reason">版本创建原因</param>
/// <param name="changeSummary">变更摘要</param>
/// <param name="operatorUserId">操作员用户ID</param>
/// <param name="operatorName">操作员姓名</param>
/// <returns>新版本的整合记录实体</returns>
public IntegrationRecordEntity CreateNewVersion(string reason, string changeSummary, long operatorUserId, string operatorName)
{
var newVersion = new IntegrationRecordEntity
{
// 继承基本信息
IntegrationBatchCode = this.IntegrationBatchCode,
TenantId = this.TenantId,
RootVersionId = this.RootVersionId == 0 ? this.Id : this.RootVersionId,
ParentVersionId = this.Id,
CurrentVersion = this.CurrentVersion + 1,
// 版本变更信息
VersionReason = reason,
ChangeSummary = changeSummary,
ChangeTriggerSource = reason,
IsActiveVersion = true, // 新版本为活跃版本
// 操作员信息
OperatorUserId = operatorUserId,
OperatorUserName = operatorName,
OperatorRealName = operatorName,
IntegrationTime = DateTime.Now,
// 继承配置信息
StrategyConfigJson = this.StrategyConfigJson,
IntegrationType = this.IntegrationType,
// 继承发布状态相关信息
PublishStatus = this.PublishStatus,
PublishedTime = this.PublishedTime,
PublishedByUserId = this.PublishedByUserId,
PublishedByUserName = this.PublishedByUserName,
PublishedByRealName = this.PublishedByRealName,
PublishRemarks = this.PublishRemarks,
// 初始化新版本的其他字段
TaskIdsJson = "[]",
PersonnelAllocationResultJson = "{}",
EquipmentAllocationResultJson = "{}",
FailureReasonStats = "{}",
ChangeDetailsJson = "{}",
SnapshotDataJson = "{}",
DataVersion = this.DataVersion,
AllowAutoReallocation = true,
LastChangeCheckTime = DateTime.Now
};
// 当前版本标记为非活跃
this.IsActiveVersion = false;
return newVersion;
}
/// <summary>
/// 获取变更详情
/// 解析ChangeDetailsJson并返回结构化的变更详情
/// </summary>
public IntegrationChangeDetails GetChangeDetails()
{
try
{
if (string.IsNullOrWhiteSpace(ChangeDetailsJson) || ChangeDetailsJson == "{}")
return new IntegrationChangeDetails();
return System.Text.Json.JsonSerializer.Deserialize<IntegrationChangeDetails>(ChangeDetailsJson)
?? new IntegrationChangeDetails();
}
catch
{
return new IntegrationChangeDetails();
}
}
/// <summary>
/// 设置变更详情
/// 将变更详情对象序列化为JSON存储
/// </summary>
public void SetChangeDetails(IntegrationChangeDetails changeDetails)
{
ChangeDetailsJson = System.Text.Json.JsonSerializer.Serialize(changeDetails ?? new IntegrationChangeDetails());
}
/// <summary>
/// 获取快照数据
/// 解析SnapshotDataJson并返回快照数据对象
/// </summary>
public IntegrationSnapshot GetSnapshotData()
{
try
{
if (string.IsNullOrWhiteSpace(SnapshotDataJson) || SnapshotDataJson == "{}")
return new IntegrationSnapshot();
return System.Text.Json.JsonSerializer.Deserialize<IntegrationSnapshot>(SnapshotDataJson)
?? new IntegrationSnapshot();
}
catch
{
return new IntegrationSnapshot();
}
}
/// <summary>
/// 设置快照数据
/// 将快照数据对象序列化为JSON存储
/// </summary>
public void SetSnapshotData(IntegrationSnapshot snapshotData)
{
SnapshotDataJson = System.Text.Json.JsonSerializer.Serialize(snapshotData ?? new IntegrationSnapshot());
}
/// <summary>
/// 计算变更影响评分
/// 基于变更的任务数量、人员影响、设备影响等计算影响评分
/// </summary>
public int CalculateChangeImpactScore(IntegrationChangeDetails changeDetails)
{
if (changeDetails == null) return 0;
int score = 0;
int totalTasks = TotalTaskCount > 0 ? TotalTaskCount : 1;
// 基于变更任务比例计算基础分数 (40%)
var changedTaskRatio = (double)changeDetails.ChangedTaskIds.Count / totalTasks;
score += (int)(changedTaskRatio * 40);
// 基于人员变更数量计算分数 (30%)
var personnelChangeRatio = Math.Min(1.0, (double)changeDetails.AffectedPersonnelIds.Count / 10);
score += (int)(personnelChangeRatio * 30);
// 基于设备变更数量计算分数 (20%)
var equipmentChangeRatio = Math.Min(1.0, (double)changeDetails.AffectedEquipmentIds.Count / 10);
score += (int)(equipmentChangeRatio * 20);
// 基于变更类型的严重程度 (10%)
var severityScore = changeDetails.ChangeTypes.Contains("TaskTimeConflict") ? 10 :
changeDetails.ChangeTypes.Contains("PersonnelUnavailable") ? 8 :
changeDetails.ChangeTypes.Contains("EquipmentUnavailable") ? 6 : 2;
score += severityScore;
return Math.Min(100, Math.Max(0, score));
}
/// <summary>
/// 检查是否需要重新分配
/// 基于变更影响评分和业务规则判断是否需要重新分配
/// </summary>
public bool ShouldTriggerReallocation()
{
// 如果不允许自动重新分配返回false
if (!AllowAutoReallocation) return false;
// 如果变更影响评分超过阈值,需要重新分配
if (ChangeImpactScore >= 30) return true;
// 如果有任务时间冲突,必须重新分配
var changeDetails = GetChangeDetails();
if (changeDetails.ChangeTypes.Contains("TaskTimeConflict")) return true;
// 如果有关键人员不可用,需要重新分配
if (changeDetails.ChangeTypes.Contains("PersonnelUnavailable")) return true;
return false;
}
#endregion
#endregion
}
/// <summary>
/// 整合类型枚举
/// </summary>
public static class IntegrationType
{
/// <summary>
/// 手动触发
/// </summary>
public const string Manual = "Manual";
/// <summary>
/// 定时触发
/// </summary>
public const string Scheduled = "Scheduled";
/// <summary>
/// 自动触发
/// </summary>
public const string Auto = "Auto";
}
/// <summary>
/// 整合记录发布状态枚举
/// 定义整合记录的生命周期状态
/// </summary>
public static class IntegrationRecordPublishStatus
{
/// <summary>
/// 草稿状态 - 刚生成的整合记录,未正式生效
/// </summary>
public const string Draft = "Draft";
/// <summary>
/// 已发布状态 - 已发布生效,任务开始执行
/// </summary>
public const string Published = "Published";
/// <summary>
/// 已完成状态 - 所有任务执行完毕
/// </summary>
public const string Completed = "Completed";
/// <summary>
/// 已撤销状态 - 发布后被撤销
/// </summary>
public const string Cancelled = "Cancelled";
}
}