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