using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using FreeSql.DataAnnotations; using ZhonTai.Admin.Core.Entities; namespace NPP.SmartSchedue.Api.Contracts.Domain.Integration { /// /// 任务变更事件实体 /// 记录任务变更事件,用于变更感知和自动触发机制 /// /// 业务思考: /// 1. 事件驱动架构:基于事件的变更检测和处理机制 /// 2. 变更溯源:完整记录变更的前因后果和处理过程 /// 3. 批量处理:支持批量变更事件的聚合处理 /// 4. 性能优化:通过事件队列机制避免实时处理对系统性能的影响 /// [Table(Name = "sse_task_change_event")] [Index("idx_task_id", "TaskId")] // 任务ID索引 [Index("idx_integration_record", "IntegrationRecordId")] // 整合记录索引 [Index("idx_event_time", "EventTime")] // 事件时间索引 [Index("idx_processing_status", "ProcessingStatus")] // 处理状态索引 [Index("idx_tenant_time", "TenantId,EventTime")] // 租户+时间组合索引 public class TaskChangeEventEntity : EntityTenant { /// /// 任务ID /// 发生变更的任务ID /// [Required] public long TaskId { get; set; } /// /// 任务代码 /// 冗余存储,提高查询性能 /// [Column(StringLength = 50)] [Required] public string TaskCode { get; set; } = string.Empty; /// /// 关联的整合记录ID /// 指向受影响的整合记录 /// public long? IntegrationRecordId { get; set; } /// /// 根版本整合记录ID /// 指向版本链的根节点,用于快速定位整个版本链 /// public long? RootIntegrationRecordId { get; set; } /// /// 事件类型 /// TaskModified, TaskDeleted, TaskStatusChanged, TaskPriorityChanged, /// TaskTimeChanged, TaskPersonnelChanged, TaskEquipmentChanged, TaskCompleted /// [Column(StringLength = 50)] [Required] public string EventType { get; set; } = string.Empty; /// /// 变更字段名称 /// 具体发生变更的字段名,多个字段用逗号分隔 /// [Column(StringLength = 200)] public string ChangedFields { get; set; } = string.Empty; /// /// 变更前的值(JSON格式) /// 存储变更前的字段值快照 /// [Column(DbType = "json")] public string BeforeValueJson { get; set; } = "{}"; /// /// 变更后的值(JSON格式) /// 存储变更后的字段值快照 /// [Column(DbType = "json")] public string AfterValueJson { get; set; } = "{}"; /// /// 事件发生时间 /// 任务实际发生变更的时间 /// [Required] public DateTime EventTime { get; set; } = DateTime.Now; /// /// 变更触发用户ID /// 执行变更操作的用户ID /// public long? TriggeredByUserId { get; set; } /// /// 变更触发用户名 /// [Column(StringLength = 50)] public string TriggeredByUserName { get; set; } = string.Empty; /// /// 变更触发用户真实姓名 /// [Column(StringLength = 50)] public string TriggeredByRealName { get; set; } = string.Empty; /// /// 事件来源 /// Manual-手动操作, System-系统自动, Import-数据导入, API-API调用, Sync-同步操作 /// [Column(StringLength = 20)] [Required] public string EventSource { get; set; } = "Manual"; /// /// 事件优先级 /// 1-9,9为最高优先级,影响处理顺序 /// [Required] public int EventPriority { get; set; } = 5; /// /// 影响评估评分 /// 0-100分,评估该变更对整合记录的潜在影响 /// public int ImpactAssessmentScore { get; set; } = 0; /// /// 处理状态 /// Pending-待处理, Processing-处理中, Completed-已完成, Failed-处理失败, Ignored-已忽略 /// [Column(StringLength = 20)] [Required] public string ProcessingStatus { get; set; } = "Pending"; /// /// 处理开始时间 /// public DateTime? ProcessingStartTime { get; set; } /// /// 处理完成时间 /// public DateTime? ProcessingCompletedTime { get; set; } /// /// 处理人员用户ID /// 实际处理该事件的用户ID /// public long? ProcessedByUserId { get; set; } /// /// 处理人员用户名 /// [Column(StringLength = 50)] public string ProcessedByUserName { get; set; } = string.Empty; /// /// 处理结果摘要 /// 简述处理结果和采取的行动 /// [Column(StringLength = 500)] public string ProcessingResultSummary { get; set; } = string.Empty; /// /// 处理详情JSON /// 存储详细的处理过程和结果信息 /// [Column(DbType = "json")] public string ProcessingDetailsJson { get; set; } = "{}"; /// /// 是否触发了重新分配 /// [Required] public bool TriggeredReallocation { get; set; } = false; /// /// 创建的新版本ID /// 如果该事件导致创建了新版本 /// public long? CreatedNewVersionId { get; set; } /// /// 事件分组ID /// 用于将相关的多个事件分组处理,如批量修改 /// public long? EventGroupId { get; set; } /// /// 事件分组序号 /// 在同一分组内的事件序号 /// public int EventGroupSequence { get; set; } = 1; /// /// 是否需要通知 /// 标记该事件是否需要发送通知给相关人员 /// [Required] public bool RequiresNotification { get; set; } = true; /// /// 通知状态 /// NotSent-未发送, Sent-已发送, Failed-发送失败, NotRequired-无需发送 /// [Column(StringLength = 20)] [Required] public string NotificationStatus { get; set; } = "NotSent"; /// /// 通知发送时间 /// public DateTime? NotificationSentTime { get; set; } /// /// 重试次数 /// 处理失败后的重试次数 /// [Required] public int RetryCount { get; set; } = 0; /// /// 最大重试次数 /// [Required] public int MaxRetryCount { get; set; } = 3; /// /// 下次重试时间 /// public DateTime? NextRetryTime { get; set; } /// /// 错误信息 /// 记录处理过程中的错误信息 /// [Column(StringLength = 1000)] public string ErrorMessage { get; set; } = string.Empty; /// /// 事件标签 /// 用于事件分类和筛选,多个标签用逗号分隔 /// [Column(StringLength = 200)] public string EventTags { get; set; } = string.Empty; /// /// 业务备注 /// [Column(StringLength = 500)] public string Remarks { get; set; } = string.Empty; /// /// 是否为测试事件 /// [Required] public bool IsTestEvent { get; set; } = false; /// /// 数据版本 /// [Required] public int DataVersion { get; set; } = 1; #region 计算属性 /// /// 处理耗时(毫秒) /// [Navigate(nameof(ProcessingStartTime))] public long ProcessingElapsedMilliseconds { get { if (ProcessingStartTime.HasValue && ProcessingCompletedTime.HasValue) { return (long)(ProcessingCompletedTime.Value - ProcessingStartTime.Value).TotalMilliseconds; } return 0; } set { } // FreeSql需要set访问器,但这是计算属性,所以提供空实现 } /// /// 事件年龄(自事件发生到现在的分钟数) /// [Navigate(nameof(EventTime))] public int EventAgeMinutes { get { return (int)(DateTime.Now - EventTime).TotalMinutes; } set { } // FreeSql需要set访问器,但这是计算属性,所以提供空实现 } /// /// 是否超时 /// 判断事件是否长时间未处理 /// [Navigate(nameof(EventTime))] public bool IsOverdue { get { if (ProcessingStatus == "Completed" || ProcessingStatus == "Ignored") return false; var timeoutMinutes = EventPriority >= 8 ? 30 : EventPriority >= 5 ? 120 : 480; // 高优先级30分钟,中等2小时,低优先级8小时 return EventAgeMinutes > timeoutMinutes; } set { } // FreeSql需要set访问器,但这是计算属性,所以提供空实现 } #endregion #region 业务方法 /// /// 获取变更前的值 /// 将JSON反序列化为字典 /// public Dictionary GetBeforeValues() { try { if (string.IsNullOrWhiteSpace(BeforeValueJson) || BeforeValueJson == "{}") return new Dictionary(); return System.Text.Json.JsonSerializer.Deserialize>(BeforeValueJson) ?? new Dictionary(); } catch { return new Dictionary(); } } /// /// 获取变更后的值 /// 将JSON反序列化为字典 /// public Dictionary GetAfterValues() { try { if (string.IsNullOrWhiteSpace(AfterValueJson) || AfterValueJson == "{}") return new Dictionary(); return System.Text.Json.JsonSerializer.Deserialize>(AfterValueJson) ?? new Dictionary(); } catch { return new Dictionary(); } } /// /// 设置变更前的值 /// public void SetBeforeValues(Dictionary values) { BeforeValueJson = System.Text.Json.JsonSerializer.Serialize(values ?? new Dictionary()); } /// /// 设置变更后的值 /// public void SetAfterValues(Dictionary values) { AfterValueJson = System.Text.Json.JsonSerializer.Serialize(values ?? new Dictionary()); } /// /// 获取处理详情 /// public Dictionary GetProcessingDetails() { try { if (string.IsNullOrWhiteSpace(ProcessingDetailsJson) || ProcessingDetailsJson == "{}") return new Dictionary(); return System.Text.Json.JsonSerializer.Deserialize>(ProcessingDetailsJson) ?? new Dictionary(); } catch { return new Dictionary(); } } /// /// 设置处理详情 /// public void SetProcessingDetails(Dictionary details) { ProcessingDetailsJson = System.Text.Json.JsonSerializer.Serialize(details ?? new Dictionary()); } /// /// 开始处理事件 /// 设置处理开始时间和状态 /// public void StartProcessing(long processedByUserId, string processedByUserName) { ProcessingStatus = "Processing"; ProcessingStartTime = DateTime.Now; ProcessedByUserId = processedByUserId; ProcessedByUserName = processedByUserName; } /// /// 完成处理事件 /// 设置处理完成时间和状态 /// public void CompleteProcessing(string resultSummary, bool triggeredReallocation = false, long? createdVersionId = null) { ProcessingStatus = "Completed"; ProcessingCompletedTime = DateTime.Now; ProcessingResultSummary = resultSummary; TriggeredReallocation = triggeredReallocation; CreatedNewVersionId = createdVersionId; } /// /// 处理失败 /// 设置失败状态和错误信息 /// public void FailProcessing(string errorMessage) { ProcessingStatus = "Failed"; ProcessingCompletedTime = DateTime.Now; ErrorMessage = errorMessage; RetryCount++; // 计算下次重试时间(指数退避) if (RetryCount <= MaxRetryCount) { var delayMinutes = Math.Pow(2, RetryCount) * 5; // 5, 10, 20 分钟 NextRetryTime = DateTime.Now.AddMinutes(delayMinutes); ProcessingStatus = "Pending"; // 重置为待处理状态以便重试 } } /// /// 忽略事件 /// 设置忽略状态,不再处理 /// public void IgnoreEvent(string reason) { ProcessingStatus = "Ignored"; ProcessingCompletedTime = DateTime.Now; ProcessingResultSummary = $"事件已忽略: {reason}"; } /// /// 判断是否可以重试 /// public bool CanRetry() { return ProcessingStatus == "Pending" && RetryCount < MaxRetryCount && (!NextRetryTime.HasValue || NextRetryTime.Value <= DateTime.Now); } /// /// 计算影响评估评分 /// 基于变更类型、字段和业务规则计算影响评分 /// public int CalculateImpactScore() { int score = 0; // 基于事件类型的基础分数 score += EventType switch { "TaskDeleted" => 90, "TaskStatusChanged" => 80, "TaskTimeChanged" => 70, "TaskPriorityChanged" => 60, "TaskPersonnelChanged" => 75, "TaskEquipmentChanged" => 75, "TaskModified" => 50, "TaskCompleted" => 30, _ => 20 }; // 基于变更字段的影响调整 if (ChangedFields.Contains("WorkOrderDate")) score += 20; if (ChangedFields.Contains("ShiftId")) score += 15; if (ChangedFields.Contains("Priority")) score += 10; if (ChangedFields.Contains("AssignedPersonnelId")) score += 15; if (ChangedFields.Contains("AssignedEquipmentId")) score += 15; if (ChangedFields.Contains("Status")) score += 25; // 基于事件优先级调整 score += EventPriority * 2; return Math.Min(100, Math.Max(0, score)); } #endregion } /// /// 事件处理状态枚举 /// public static class TaskChangeEventProcessingStatus { /// /// 待处理 /// public const string Pending = "Pending"; /// /// 处理中 /// public const string Processing = "Processing"; /// /// 已完成 /// public const string Completed = "Completed"; /// /// 处理失败 /// public const string Failed = "Failed"; /// /// 已忽略 /// public const string Ignored = "Ignored"; } /// /// 事件类型枚举 /// public static class TaskChangeEventType { /// /// 任务修改 /// public const string TaskModified = "TaskModified"; /// /// 任务删除 /// public const string TaskDeleted = "TaskDeleted"; /// /// 任务状态变更 /// public const string TaskStatusChanged = "TaskStatusChanged"; /// /// 任务优先级变更 /// public const string TaskPriorityChanged = "TaskPriorityChanged"; /// /// 任务时间变更 /// public const string TaskTimeChanged = "TaskTimeChanged"; /// /// 任务人员变更 /// public const string TaskPersonnelChanged = "TaskPersonnelChanged"; /// /// 任务设备变更 /// public const string TaskEquipmentChanged = "TaskEquipmentChanged"; /// /// 任务完成 /// public const string TaskCompleted = "TaskCompleted"; } }