diff --git a/NPP.SmartSchedue.Api.Contracts/Core/Configuration/EmailConfiguration.cs b/NPP.SmartSchedue.Api.Contracts/Core/Configuration/EmailConfiguration.cs
new file mode 100644
index 0000000..cbf6a7b
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Core/Configuration/EmailConfiguration.cs
@@ -0,0 +1,63 @@
+namespace NPP.SmartSchedue.Api.Contracts.Core.Configuration;
+
+///
+/// 邮件配置
+///
+public class EmailConfiguration
+{
+ ///
+ /// 配置节点名称
+ ///
+ public const string SectionName = "EmailNotification";
+
+ ///
+ /// SMTP服务器地址
+ ///
+ public string SmtpServer { get; set; } = "";
+
+ ///
+ /// SMTP端口
+ ///
+ public int SmtpPort { get; set; } = 587;
+
+ ///
+ /// 发送者邮箱
+ ///
+ public string SenderEmail { get; set; } = "";
+
+ ///
+ /// 发送者密码
+ ///
+ public string SenderPassword { get; set; } = "";
+
+ ///
+ /// 发送者名称
+ ///
+ public string SenderName { get; set; } = "NPP智能生产调度系统";
+
+ ///
+ /// 是否启用SSL
+ ///
+ public bool EnableSsl { get; set; } = true;
+
+ ///
+ /// 超时时间(秒)
+ ///
+ public int TimeoutSeconds { get; set; } = 30;
+
+ ///
+ /// 是否启用邮件服务
+ ///
+ public bool Enabled { get; set; } = true;
+
+ ///
+ /// 验证配置是否有效
+ ///
+ public bool IsValid()
+ {
+ return !string.IsNullOrWhiteSpace(SmtpServer) &&
+ SmtpPort > 0 &&
+ !string.IsNullOrWhiteSpace(SenderEmail) &&
+ !string.IsNullOrWhiteSpace(SenderPassword);
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Core/Enums/SystemMessageTypeEnum.cs b/NPP.SmartSchedue.Api.Contracts/Core/Enums/SystemMessageTypeEnum.cs
new file mode 100644
index 0000000..424aa5b
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Core/Enums/SystemMessageTypeEnum.cs
@@ -0,0 +1,32 @@
+namespace NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+///
+/// 系统消息类型枚举
+///
+public enum SystemMessageTypeEnum
+{
+ ///
+ /// 信息
+ ///
+ Info = 1,
+
+ ///
+ /// 成功
+ ///
+ Success = 2,
+
+ ///
+ /// 警告
+ ///
+ Warning = 3,
+
+ ///
+ /// 错误
+ ///
+ Error = 4,
+
+ ///
+ /// 紧急
+ ///
+ Urgent = 5
+}
diff --git a/NPP.SmartSchedue.Api.Contracts/Core/Enums/WorkOrderStatusEnum.cs b/NPP.SmartSchedue.Api.Contracts/Core/Enums/WorkOrderStatusEnum.cs
index 61e6697..4bade48 100644
--- a/NPP.SmartSchedue.Api.Contracts/Core/Enums/WorkOrderStatusEnum.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Core/Enums/WorkOrderStatusEnum.cs
@@ -38,5 +38,10 @@ public enum WorkOrderStatusEnum
///
/// 已完成
///
- Completed = 7
+ Completed = 7,
+
+ ///
+ /// 已取消
+ ///
+ Cancelled = 99
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Domain/Notification/INotificationHistoryRepository.cs b/NPP.SmartSchedue.Api.Contracts/Domain/Notification/INotificationHistoryRepository.cs
new file mode 100644
index 0000000..e34af80
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Domain/Notification/INotificationHistoryRepository.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using ZhonTai.Admin.Core.Repositories;
+
+namespace NPP.SmartSchedue.Api.Contracts.Domain.Notification;
+
+///
+/// 通知历史记录仓储接口
+///
+public interface INotificationHistoryRepository : IRepositoryBase
+{
+ ///
+ /// 根据通知设置ID获取历史记录列表
+ ///
+ Task> GetByNotificationSettingIdAsync(long notificationSettingId);
+
+ ///
+ /// 根据接收人员ID获取历史记录列表
+ ///
+ Task> GetByRecipientPersonnelIdAsync(long recipientPersonnelId);
+
+ ///
+ /// 根据发送状态获取历史记录列表
+ ///
+ Task> GetBySendStatusAsync(NotificationStatusEnum sendStatus);
+
+ ///
+ /// 根据通知方式获取历史记录列表
+ ///
+ Task> GetByNotificationTypeAsync(NotificationTypeEnum notificationType);
+
+ ///
+ /// 根据业务类型和业务ID获取历史记录列表
+ ///
+ Task> GetByBusinessAsync(string businessType, long? businessId = null);
+
+ ///
+ /// 获取需要重试的失败通知列表
+ ///
+ Task> GetFailedNotificationsForRetryAsync(int? maxRetryCount = null);
+
+ ///
+ /// 获取指定时间范围内的通知统计信息
+ ///
+ Task> GetNotificationStatisticsAsync(
+ DateTime startTime,
+ DateTime endTime,
+ long? notificationSettingId = null);
+
+ ///
+ /// 根据日期范围获取历史记录列表
+ ///
+ Task> GetByDateRangeAsync(DateTime startDate, DateTime endDate);
+
+ ///
+ /// 更新通知发送状态
+ ///
+ Task UpdateSendStatusAsync(long id, NotificationStatusEnum sendStatus, string sendResult = "", string errorMessage = "");
+
+ ///
+ /// 批量更新通知发送状态
+ ///
+ Task BatchUpdateSendStatusAsync(List<(long Id, NotificationStatusEnum Status, string Result, string Error)> updates);
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Domain/Notification/INotificationSettingRepository.cs b/NPP.SmartSchedue.Api.Contracts/Domain/Notification/INotificationSettingRepository.cs
new file mode 100644
index 0000000..6c2d7a0
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Domain/Notification/INotificationSettingRepository.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using ZhonTai.Admin.Core.Repositories;
+
+namespace NPP.SmartSchedue.Api.Contracts.Domain.Notification;
+
+///
+/// 通知设置仓储接口
+///
+public interface INotificationSettingRepository : IRepositoryBase
+{
+ ///
+ /// 根据启用状态获取通知设置列表
+ ///
+ Task> GetByEnabledAsync(bool enabled);
+
+ ///
+ /// 根据人员组ID获取通知设置列表
+ ///
+ Task> GetByPersonnelGroupIdAsync(long personnelGroupId);
+
+ ///
+ /// 根据通知方式获取通知设置列表
+ ///
+ Task> GetByNotificationTypeAsync(int notificationType);
+
+ ///
+ /// 根据触发条件获取匹配的通知设置列表
+ /// 这里实现一个简化的匹配逻辑,实际项目中可能需要更复杂的条件匹配引擎
+ ///
+ Task> GetMatchingNotificationSettingsAsync(
+ string businessType,
+ Dictionary businessContext);
+
+ ///
+ /// 检查通知设置名称是否存在
+ ///
+ Task ExistsNotificationNameAsync(string notificationName, long? excludeId = null);
+
+ ///
+ /// 获取需要在当前时间执行的通知设置列表
+ /// 根据决策点2:简单时间段,只支持开始时间-结束时间
+ ///
+ Task> GetActiveNotificationSettingsForTimeAsync(DateTime currentTime);
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Domain/Notification/IPersonnelGroupRepository.cs b/NPP.SmartSchedue.Api.Contracts/Domain/Notification/IPersonnelGroupRepository.cs
new file mode 100644
index 0000000..2285067
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Domain/Notification/IPersonnelGroupRepository.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using ZhonTai.Admin.Core.Repositories;
+
+namespace NPP.SmartSchedue.Api.Contracts.Domain.Notification;
+
+///
+/// 人员组仓储接口
+///
+public interface IPersonnelGroupRepository : IRepositoryBase
+{
+ ///
+ /// 根据启用状态获取人员组列表
+ ///
+ Task> GetByEnabledAsync(bool enabled);
+
+ ///
+ /// 根据人员组类型获取人员组列表
+ ///
+ Task> GetByGroupTypeAsync(PersonnelGroupTypeEnum groupType);
+
+ ///
+ /// 检查人员组名称是否存在
+ ///
+ Task ExistsGroupNameAsync(string groupName, long? excludeId = null);
+
+ ///
+ /// 获取包含指定人员的人员组列表
+ ///
+ Task> GetGroupsContainingPersonnelAsync(long personnelId);
+
+ ///
+ /// 获取包含指定部门的人员组列表
+ ///
+ Task> GetGroupsContainingDepartmentAsync(long departmentId);
+
+ ///
+ /// 获取包含指定职位的人员组列表
+ ///
+ Task> GetGroupsContainingPositionAsync(string position);
+
+ ///
+ /// 计算人员组的实际人员数量
+ /// 这个方法返回一个估算值,实际计算需要在Service层进行
+ /// 因为需要查询人员表和考虑动态规则
+ ///
+ Task CalculatePersonnelCountAsync(long personnelGroupId);
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Domain/Personnel/PersonnelQualificationStatistics.cs b/NPP.SmartSchedue.Api.Contracts/Domain/Personnel/PersonnelQualificationStatistics.cs
new file mode 100644
index 0000000..3791790
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Domain/Personnel/PersonnelQualificationStatistics.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Domain.Personnel
+{
+ ///
+ /// 人员资质统计信息
+ ///
+ public class PersonnelQualificationStatistics
+ {
+ ///
+ /// 资质ID
+ ///
+ public long QualificationId { get; set; }
+
+ ///
+ /// 资质名称
+ ///
+ public string QualificationName { get; set; }
+
+ ///
+ /// 持有该资质的人员数量
+ ///
+ public int PersonnelCount { get; set; }
+ }
+
+ ///
+ /// 人员资质统计结果
+ ///
+ public class PersonnelQualificationStatisticsResult
+ {
+ ///
+ /// 统计数据列表
+ /// 横坐标为资质,纵坐标为人员数量
+ ///
+ public List Statistics { get; set; } = new List();
+
+ ///
+ /// 统计时间
+ ///
+ public DateTime GeneratedTime { get; set; } = DateTime.Now;
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/NPP.SmartSchedue.Api.Contracts.csproj b/NPP.SmartSchedue.Api.Contracts/NPP.SmartSchedue.Api.Contracts.csproj
index efdd3c3..17b060a 100644
--- a/NPP.SmartSchedue.Api.Contracts/NPP.SmartSchedue.Api.Contracts.csproj
+++ b/NPP.SmartSchedue.Api.Contracts/NPP.SmartSchedue.Api.Contracts.csproj
@@ -35,6 +35,7 @@
+
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/IIntegrationRecordService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/IIntegrationRecordService.cs
index ffd6309..08027c5 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/IIntegrationRecordService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/IIntegrationRecordService.cs
@@ -66,6 +66,18 @@ namespace NPP.SmartSchedue.Api.Contracts.Services.Integration
#endregion
+ #region 任务修改接口
+
+ ///
+ /// 修改整合记录中的任务分配
+ /// 根据选中的人员修改任务的指定人员ID和姓名,并更新整合记录
+ ///
+ /// 任务修改输入参数
+ /// 修改结果
+ Task ModifyIntegrationRecordTaskAsync(IntegrationRecordTaskModifyInput input);
+
+ #endregion
+
#region 通知管理接口(预留)
///
@@ -77,7 +89,7 @@ namespace NPP.SmartSchedue.Api.Contracts.Services.Integration
/// 目标人员ID列表
/// 自定义消息
/// 通知发送结果
- Task SendPublishNotificationAsync(long recordId, string notificationType, List targetPersonnelIds, string customMessage = "");
+ Task SendPublishNotificationAsync(long recordId, string notificationType, List targetPersonnelIds, string customMessage = "");
///
/// 创建待办任务(预留接口)
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/ISmartScheduleOrchestratorService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/ISmartScheduleOrchestratorService.cs
index f6729a0..735d806 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/ISmartScheduleOrchestratorService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/ISmartScheduleOrchestratorService.cs
@@ -1,6 +1,15 @@
+using System.Threading.Tasks;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
+
namespace NPP.SmartSchedue.Api.Contracts.Services.Integration;
public interface ISmartScheduleOrchestratorService
{
-
+ ///
+ /// 修改整合记录中的任务分配
+ /// 根据选中的人员修改任务的指定人员ID和姓名,并更新整合记录
+ ///
+ /// 任务修改输入参数
+ /// 修改结果
+ Task ModifyIntegrationRecordTaskAsync(IntegrationRecordTaskModifyInput input);
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/IWorkOrderIntegrationOperationService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/IWorkOrderIntegrationOperationService.cs
new file mode 100644
index 0000000..1a50b94
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/IWorkOrderIntegrationOperationService.cs
@@ -0,0 +1,23 @@
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output;
+using System.Threading.Tasks;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration
+{
+ ///
+ /// 工作订单整合操作统一服务
+ /// 简化版:专注核心业务流程,去除复杂的权限、版本、审计功能
+ ///
+ public interface IWorkOrderIntegrationOperationService
+ {
+ ///
+ /// 修改任务
+ ///
+ Task ModifyWorkOrderAsync(WorkOrderModifyOperationInput input);
+
+ ///
+ /// 删除任务
+ ///
+ Task DeleteWorkOrderAsync(WorkOrderDeleteOperationInput input);
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/BatchWorkOrderOperationInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/BatchWorkOrderOperationInput.cs
new file mode 100644
index 0000000..525ea53
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/BatchWorkOrderOperationInput.cs
@@ -0,0 +1,61 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Input
+{
+ ///
+ /// 批量操作类型
+ ///
+ public enum BatchOperationType
+ {
+ ///
+ /// 批量修改
+ ///
+ BatchModify = 1,
+
+ ///
+ /// 批量删除
+ ///
+ BatchDelete = 2,
+
+ ///
+ /// 批量取消
+ ///
+ BatchCancel = 3
+ }
+
+ ///
+ /// 批量任务操作输入
+ ///
+ public class BatchWorkOrderOperationInput
+ {
+ ///
+ /// 操作类型
+ ///
+ public BatchOperationType OperationType { get; set; }
+
+ ///
+ /// 任务ID列表
+ ///
+ public List TaskIds { get; set; } = new List();
+
+ ///
+ /// 操作参数(根据操作类型不同,内容不同)
+ ///
+ public object OperationParameters { get; set; }
+
+ ///
+ /// 操作员用户ID
+ ///
+ public long OperatorUserId { get; set; }
+
+ ///
+ /// 操作员姓名
+ ///
+ public string OperatorName { get; set; }
+
+ ///
+ /// 操作备注
+ ///
+ public string Remarks { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderCancelOperationInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderCancelOperationInput.cs
new file mode 100644
index 0000000..95fb892
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderCancelOperationInput.cs
@@ -0,0 +1,28 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Input
+{
+ ///
+ /// 任务取消操作输入
+ ///
+ public class WorkOrderCancelOperationInput
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 取消原因
+ ///
+ public string CancelReason { get; set; } = string.Empty;
+
+ ///
+ /// 操作员用户ID
+ ///
+ public long OperatorUserId { get; set; }
+
+ ///
+ /// 操作员姓名
+ ///
+ public string OperatorName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderDeleteOperationInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderDeleteOperationInput.cs
new file mode 100644
index 0000000..dcccea3
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderDeleteOperationInput.cs
@@ -0,0 +1,33 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Input
+{
+ ///
+ /// 任务删除操作输入
+ ///
+ public class WorkOrderDeleteOperationInput
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 是否软删除(true=软删除,false=物理删除)
+ ///
+ public bool IsSoftDelete { get; set; } = true;
+
+ ///
+ /// 删除原因
+ ///
+ public string DeleteReason { get; set; } = string.Empty;
+
+ ///
+ /// 操作员用户ID
+ ///
+ public long OperatorUserId { get; set; }
+
+ ///
+ /// 操作员姓名
+ ///
+ public string OperatorName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderModifyOperationInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderModifyOperationInput.cs
new file mode 100644
index 0000000..bfe3910
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Input/WorkOrderModifyOperationInput.cs
@@ -0,0 +1,25 @@
+using NPP.SmartSchedue.Api.Contracts.Services.Work.Input;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Input
+{
+ ///
+ /// 任务修改操作输入
+ ///
+ public class WorkOrderModifyOperationInput
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 修改的任务数据
+ ///
+ public WorkOrderUpdateInput TaskModifyData { get; set; }
+
+ ///
+ /// 操作备注
+ ///
+ public string Remarks { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Models/WorkOrderValidationResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Models/WorkOrderValidationResult.cs
new file mode 100644
index 0000000..e884abc
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Models/WorkOrderValidationResult.cs
@@ -0,0 +1,34 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Models
+{
+ ///
+ /// 工作订单验证结果
+ ///
+ public class WorkOrderValidationResult
+ {
+ ///
+ /// 验证是否通过
+ ///
+ public bool IsValid { get; set; }
+
+ ///
+ /// 错误消息
+ ///
+ public string ErrorMessage { get; set; } = string.Empty;
+
+ ///
+ /// 创建成功结果
+ ///
+ public static WorkOrderValidationResult Success()
+ {
+ return new WorkOrderValidationResult { IsValid = true };
+ }
+
+ ///
+ /// 创建失败结果
+ ///
+ public static WorkOrderValidationResult Fail(string errorMessage)
+ {
+ return new WorkOrderValidationResult { IsValid = false, ErrorMessage = errorMessage };
+ }
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/BatchWorkOrderOperationResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/BatchWorkOrderOperationResult.cs
new file mode 100644
index 0000000..d39d9a3
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/BatchWorkOrderOperationResult.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Output
+{
+ ///
+ /// 单个任务操作结果
+ ///
+ public class SingleTaskOperationResult
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 操作是否成功
+ ///
+ public bool IsSuccess { get; set; }
+
+ ///
+ /// 操作消息
+ ///
+ public string Message { get; set; } = string.Empty;
+
+ ///
+ /// 错误详情
+ ///
+ public List ErrorDetails { get; set; } = new List();
+ }
+
+ ///
+ /// 批量操作结果输出
+ ///
+ public class BatchWorkOrderOperationResult
+ {
+ ///
+ /// 总体操作是否成功
+ ///
+ public bool IsSuccess { get; set; }
+
+ ///
+ /// 总体操作消息
+ ///
+ public string Message { get; set; } = string.Empty;
+
+ ///
+ /// 总任务数
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 成功任务数
+ ///
+ public int SuccessCount { get; set; }
+
+ ///
+ /// 失败任务数
+ ///
+ public int FailedCount { get; set; }
+
+ ///
+ /// 每个任务的操作结果
+ ///
+ public List TaskResults { get; set; } = new List();
+
+ ///
+ /// 影响的整合记录ID列表
+ ///
+ public List AffectedIntegrationRecordIds { get; set; } = new List();
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/CancelIntegrationRecordResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/CancelIntegrationRecordResult.cs
index 21d9fbf..3b7e7bf 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/CancelIntegrationRecordResult.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/CancelIntegrationRecordResult.cs
@@ -64,7 +64,7 @@ public class CancelIntegrationRecordResult
///
/// 通知发送结果
///
- public List NotificationResults { get; set; } = new();
+ public List NotificationResults { get; set; } = new();
///
/// 撤销统计信息
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/ProcessTaskChangesResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/ProcessTaskChangesResult.cs
index 8604500..d54074e 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/ProcessTaskChangesResult.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/ProcessTaskChangesResult.cs
@@ -86,7 +86,7 @@ namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Output
///
/// 通知发送结果
///
- public List NotificationResults { get; set; } = new();
+ public List NotificationResults { get; set; } = new();
///
/// 警告信息
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/PublishIntegrationRecordResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/PublishIntegrationRecordResult.cs
index 452ced9..d8034b5 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/PublishIntegrationRecordResult.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/PublishIntegrationRecordResult.cs
@@ -67,7 +67,7 @@ public class PublishIntegrationRecordResult
/// 通知发送结果
/// 记录发布后通知的发送情况
///
- public List NotificationResults { get; set; } = new();
+ public List NotificationResults { get; set; } = new();
///
/// 发布统计信息
@@ -272,9 +272,9 @@ public class PublishValidationResult
}
///
-/// 通知发送结果
+/// 整合通知发送结果
///
-public class NotificationSendResult
+public class IntegrationNotificationResult
{
///
/// 通知类型
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/SmartReallocationResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/SmartReallocationResult.cs
index 49a2163..b090429 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/SmartReallocationResult.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/SmartReallocationResult.cs
@@ -87,7 +87,7 @@ namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Output
///
/// 通知发送结果
///
- public List NotificationResults { get; set; } = new();
+ public List NotificationResults { get; set; } = new();
///
/// 验证结果
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/VersionManagementResults.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/VersionManagementResults.cs
index 86f3897..8b4b80b 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/VersionManagementResults.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/VersionManagementResults.cs
@@ -336,7 +336,7 @@ namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Output
///
/// 通知发送结果
///
- public List NotificationResults { get; set; } = new();
+ public List NotificationResults { get; set; } = new();
///
/// 警告信息
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/WorkOrderOperationResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/WorkOrderOperationResult.cs
new file mode 100644
index 0000000..e043022
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Integration/Output/WorkOrderOperationResult.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Output
+{
+ ///
+ /// 操作结果输出
+ ///
+ public class WorkOrderOperationResult
+ {
+ ///
+ /// 操作是否成功
+ ///
+ public bool IsSuccess { get; set; }
+
+ ///
+ /// 操作消息
+ ///
+ public string Message { get; set; } = string.Empty;
+
+ ///
+ /// 影响的整合记录ID
+ ///
+ public long? AffectedIntegrationRecordId { get; set; }
+
+ ///
+ /// 操作详情数据
+ ///
+ public object Data { get; set; }
+
+ ///
+ /// 错误详情(操作失败时)
+ ///
+ public List ErrorDetails { get; set; } = new List();
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/IEmailNotificationService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/IEmailNotificationService.cs
index 8593c8d..95baa89 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Notification/IEmailNotificationService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/IEmailNotificationService.cs
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
-using System.Collections.Generic;
-using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
namespace NPP.SmartSchedue.Api.Contracts.Services.Notification;
@@ -10,132 +10,33 @@ namespace NPP.SmartSchedue.Api.Contracts.Services.Notification;
///
public interface IEmailNotificationService
{
- #region 单个邮件发送
-
///
- /// 发送邮件通知
+ /// 发送单个邮件
///
- /// 接收人邮箱
- /// 邮件主题
- /// 邮件内容
- /// 是否HTML格式
- ///
- Task SendEmailAsync(string recipientEmail, string subject, string content, bool isHtml = true);
+ Task SendEmailAsync(SendEmailInput input);
///
- /// 发送邮件通知(带附件)
+ /// 批量发送邮件
///
- /// 接收人邮箱
- /// 邮件主题
- /// 邮件内容
- /// 附件文件路径列表
- /// 是否HTML格式
- ///
- Task SendEmailWithAttachmentsAsync(
- string recipientEmail,
- string subject,
- string content,
- List attachments,
- bool isHtml = true);
-
- #endregion
-
- #region 批量邮件发送
-
- ///
- /// 批量发送邮件通知
- ///
- /// 接收人邮箱列表
- /// 邮件主题
- /// 邮件内容
- /// 是否HTML格式
- /// 发送结果,Key为邮箱地址,Value为是否发送成功
- Task> BatchSendEmailAsync(
- List recipients,
- string subject,
- string content,
- bool isHtml = true);
+ Task BatchSendEmailAsync(BatchSendEmailInput input);
///
- /// 个性化批量发送邮件通知
- /// 每个收件人可以有不同的邮件内容
+ /// 个性化批量发送邮件
///
- /// 邮件项列表
- /// 发送结果,Key为邮箱地址,Value为是否发送成功
- Task> BatchSendPersonalizedEmailAsync(List emailItems);
+ Task BatchSendPersonalizedEmailAsync(BatchSendPersonalizedEmailInput input);
- #endregion
-
- #region 邮件模板
-
///
/// 使用模板发送邮件
///
- /// 接收人邮箱
- /// 邮件主题模板
- /// 邮件内容模板
- /// 模板变量
- /// 是否HTML格式
- ///
- Task SendEmailByTemplateAsync(
- string recipientEmail,
- string subjectTemplate,
- string contentTemplate,
- Dictionary variables,
- bool isHtml = true);
+ Task SendEmailByTemplateAsync(SendEmailByTemplateInput input);
- #endregion
-
- #region 邮件发送状态检查
-
///
/// 验证邮箱地址格式
///
- /// 邮箱地址
- ///
- bool IsValidEmail(string email);
+ Task IsValidEmailAsync(string email);
///
/// 检查邮件服务器连接状态
///
- ///
- Task CheckEmailServerConnectionAsync();
-
- #endregion
-}
-
-///
-/// 邮件项
-///
-public class EmailItem
-{
- ///
- /// 接收人邮箱
- ///
- public string RecipientEmail { get; set; } = "";
-
- ///
- /// 邮件主题
- ///
- public string Subject { get; set; } = "";
-
- ///
- /// 邮件内容
- ///
- public string Content { get; set; } = "";
-
- ///
- /// 是否HTML格式
- ///
- public bool IsHtml { get; set; } = true;
-
- ///
- /// 附件文件路径列表
- ///
- public List Attachments { get; set; } = new List();
-
- ///
- /// 个性化变量
- ///
- public Dictionary Variables { get; set; } = new Dictionary();
+ Task CheckEmailServerStatusAsync();
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationService.cs
index 20c3395..d29c618 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationService.cs
@@ -29,7 +29,15 @@ public interface INotificationService
///
///
///
- Task> GetNotificationSettingPageAsync(PageInput input);
+ Task> GetNotificationSettingPageAsync(PageInput input);
+
+ ///
+ /// 获取通知设置列表(可选过滤,含缓存)
+ ///
+ /// 是否启用(可选)
+ /// 人员组ID(可选)
+ ///
+ Task> GetNotificationSettingListAsync(bool? isEnabled = null, long? personnelGroupId = null);
///
/// 创建通知设置
@@ -76,7 +84,7 @@ public interface INotificationService
///
///
///
- Task> GetPersonnelGroupPageAsync(PageInput input);
+ Task> GetPersonnelGroupPageAsync(PageInput input);
///
/// 创建人员组
@@ -90,7 +98,7 @@ public interface INotificationService
///
///
///
- Task UpdatePersonnelGroupAsync(PersonnelGroupCreateInput input);
+ Task UpdatePersonnelGroupAsync(PersonnelGroupUpdateInput input);
///
/// 删除人员组
@@ -119,6 +127,13 @@ public interface INotificationService
///
Task SendNotificationAsync(SendNotificationInput input);
+ ///
+ /// 发送群组通知(邮件发送一封给所有人,系统消息仍然单独发送)
+ ///
+ ///
+ ///
+ Task SendGroupNotificationAsync(SendNotificationInput input);
+
///
/// 批量发送通知
///
@@ -146,15 +161,6 @@ public interface INotificationService
#region 通知模板引擎(决策点7)
- ///
- /// 渲染通知模板
- /// 支持通知内容模板,可替换变量
- ///
- /// 模板内容
- /// 变量字典
- ///
- Task RenderTemplateAsync(string template, Dictionary variables);
-
///
/// 验证模板语法
///
@@ -178,7 +184,7 @@ public interface INotificationService
///
///
///
- Task> GetNotificationHistoryPageAsync(PageInput input);
+ Task> GetNotificationHistoryPageAsync(PageInput input);
///
/// 重试失败的通知
@@ -194,6 +200,20 @@ public interface INotificationService
///
Task BatchRetryFailedNotificationsAsync(List notificationHistoryIds);
+ ///
+ /// 取消通知
+ ///
+ /// 通知历史记录ID
+ ///
+ Task CancelNotificationAsync(long notificationHistoryId);
+
+ ///
+ /// 批量取消通知
+ ///
+ /// 通知历史记录ID列表
+ /// 成功取消的数量
+ Task BatchCancelNotificationsAsync(List notificationHistoryIds);
+
#endregion
#region 定时任务管理(决策点6)
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationTemplateService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationTemplateService.cs
index 6dd17ca..82b397a 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationTemplateService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/INotificationTemplateService.cs
@@ -1,5 +1,6 @@
-using System.Collections.Generic;
using System.Threading.Tasks;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
namespace NPP.SmartSchedue.Api.Contracts.Services.Notification;
@@ -15,18 +16,9 @@ public interface INotificationTemplateService
/// 渲染通知模板
/// 支持变量替换,如:{变量名}
///
- /// 模板内容
- /// 变量字典
- /// 渲染后的内容
- Task RenderTemplateAsync(string template, Dictionary variables);
-
- ///
- /// 同步渲染通知模板
- ///
- /// 模板内容
- /// 变量字典
- /// 渲染后的内容
- string RenderTemplate(string template, Dictionary variables);
+ /// 渲染模板输入参数
+ /// 渲染结果
+ Task RenderTemplateAsync(RenderTemplateInput input);
#endregion
@@ -35,16 +27,9 @@ public interface INotificationTemplateService
///
/// 验证模板语法是否正确
///
- /// 模板内容
+ /// 验证模板输入参数
/// 验证结果
- Task ValidateTemplateAsync(string template);
-
- ///
- /// 同步验证模板语法
- ///
- /// 模板内容
- /// 验证结果
- TemplateValidationResult ValidateTemplate(string template);
+ Task ValidateTemplateAsync(ValidateTemplateInput input);
#endregion
@@ -53,16 +38,9 @@ public interface INotificationTemplateService
///
/// 提取模板中的变量列表
///
- /// 模板内容
- /// 变量名列表
- Task> ExtractVariablesAsync(string template);
-
- ///
- /// 同步提取模板中的变量列表
- ///
- /// 模板内容
- /// 变量名列表
- List ExtractVariables(string template);
+ /// 提取变量输入参数
+ /// 变量列表
+ Task ExtractVariablesAsync(ExtractVariablesInput input);
#endregion
@@ -71,20 +49,15 @@ public interface INotificationTemplateService
///
/// 获取系统内置变量
///
- /// 内置变量字典
- Task> GetSystemVariablesAsync();
+ /// 系统变量字典
+ Task GetSystemVariablesAsync();
///
/// 获取业务相关变量
///
- /// 业务类型
- /// 业务ID
- /// 业务数据
+ /// 获取业务变量输入参数
/// 业务变量字典
- Task> GetBusinessVariablesAsync(
- string businessType,
- long? businessId = null,
- string businessData = "");
+ Task GetBusinessVariablesAsync(GetBusinessVariablesInput input);
#endregion
@@ -93,148 +66,17 @@ public interface INotificationTemplateService
///
/// 获取预定义模板列表
///
- /// 模板分类
+ /// 获取模板列表输入参数
/// 预定义模板列表
- Task> GetPredefinedTemplatesAsync(string category = "");
+ Task GetPredefinedTemplatesAsync(GetPredefinedTemplatesInput input);
///
/// 获取指定预定义模板
///
- /// 模板ID
+ /// 获取指定模板输入参数
/// 预定义模板
- Task GetPredefinedTemplateAsync(string templateId);
+ Task GetPredefinedTemplateAsync(GetPredefinedTemplateInput input);
#endregion
}
-///
-/// 模板验证结果
-///
-public class TemplateValidationResult
-{
- ///
- /// 是否验证通过
- ///
- public bool IsValid { get; set; } = true;
-
- ///
- /// 错误信息列表
- ///
- public List Errors { get; set; } = new List();
-
- ///
- /// 警告信息列表
- ///
- public List Warnings { get; set; } = new List();
-
- ///
- /// 发现的变量列表
- ///
- public List Variables { get; set; } = new List();
-
- ///
- /// 添加错误信息
- ///
- /// 错误信息
- public void AddError(string error)
- {
- IsValid = false;
- Errors.Add(error);
- }
-
- ///
- /// 添加警告信息
- ///
- /// 警告信息
- public void AddWarning(string warning)
- {
- Warnings.Add(warning);
- }
-}
-
-///
-/// 预定义模板
-///
-public class PredefinedTemplate
-{
- ///
- /// 模板ID
- ///
- public string TemplateId { get; set; } = "";
-
- ///
- /// 模板名称
- ///
- public string TemplateName { get; set; } = "";
-
- ///
- /// 模板分类
- ///
- public string Category { get; set; } = "";
-
- ///
- /// 模板描述
- ///
- public string Description { get; set; } = "";
-
- ///
- /// 邮件主题模板
- ///
- public string EmailSubjectTemplate { get; set; } = "";
-
- ///
- /// 邮件内容模板
- ///
- public string EmailContentTemplate { get; set; } = "";
-
- ///
- /// 系统消息标题模板
- ///
- public string SystemMessageTitleTemplate { get; set; } = "";
-
- ///
- /// 系统消息内容模板
- ///
- public string SystemMessageContentTemplate { get; set; } = "";
-
- ///
- /// 支持的变量列表
- ///
- public List SupportedVariables { get; set; } = new List();
-}
-
-///
-/// 模板变量定义
-///
-public class TemplateVariable
-{
- ///
- /// 变量名
- ///
- public string Name { get; set; } = "";
-
- ///
- /// 变量描述
- ///
- public string Description { get; set; } = "";
-
- ///
- /// 变量类型
- ///
- public string Type { get; set; } = "string";
-
- ///
- /// 是否必需
- ///
- public bool IsRequired { get; set; } = false;
-
- ///
- /// 默认值
- ///
- public string DefaultValue { get; set; } = "";
-
- ///
- /// 示例值
- ///
- public string ExampleValue { get; set; } = "";
-}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/ISystemMessageService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/ISystemMessageService.cs
index d4488a0..b4f304c 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Notification/ISystemMessageService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/ISystemMessageService.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using System.Collections.Generic;
-using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
namespace NPP.SmartSchedue.Api.Contracts.Services.Notification;
@@ -15,40 +16,16 @@ public interface ISystemMessageService
///
/// 发送系统消息
///
- /// 接收人员ID
- /// 消息标题
- /// 消息内容
- /// 消息类型(通知、警告、错误等)
- /// 业务类型
- /// 业务ID
- ///
- Task SendSystemMessageAsync(
- long recipientPersonnelId,
- string title,
- string content,
- SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info,
- string businessType = "",
- long? businessId = null);
+ /// 发送系统消息输入模型
+ /// 发送结果
+ Task SendSystemMessageAsync(SendSystemMessageInput input);
///
/// 发送带操作按钮的系统消息
///
- /// 接收人员ID
- /// 消息标题
- /// 消息内容
- /// 操作按钮列表
- /// 消息类型
- /// 业务类型
- /// 业务ID
- ///
- Task SendSystemMessageWithActionsAsync(
- long recipientPersonnelId,
- string title,
- string content,
- List actions,
- SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info,
- string businessType = "",
- long? businessId = null);
+ /// 发送带操作按钮的系统消息输入模型
+ /// 发送结果
+ Task SendSystemMessageWithActionsAsync(SendSystemMessageWithActionsInput input);
#endregion
@@ -57,28 +34,17 @@ public interface ISystemMessageService
///
/// 批量发送系统消息
///
- /// 接收人员ID列表
- /// 消息标题
- /// 消息内容
- /// 消息类型
- /// 业务类型
- /// 业务ID
- /// 发送结果,Key为人员ID,Value为是否发送成功
- Task> BatchSendSystemMessageAsync(
- List recipientPersonnelIds,
- string title,
- string content,
- SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info,
- string businessType = "",
- long? businessId = null);
+ /// 批量发送系统消息输入模型
+ /// 批量发送结果
+ Task BatchSendSystemMessageAsync(BatchSendSystemMessageInput input);
///
/// 个性化批量发送系统消息
/// 每个收件人可以有不同的消息内容
///
/// 消息项列表
- /// 发送结果,Key为人员ID,Value为是否发送成功
- Task> BatchSendPersonalizedSystemMessageAsync(List messageItems);
+ /// 批量发送结果
+ Task BatchSendPersonalizedSystemMessageAsync(List messageItems);
#endregion
@@ -87,22 +53,9 @@ public interface ISystemMessageService
///
/// 使用模板发送系统消息
///
- /// 接收人员ID
- /// 消息标题模板
- /// 消息内容模板
- /// 模板变量
- /// 消息类型
- /// 业务类型
- /// 业务ID
- ///
- Task SendSystemMessageByTemplateAsync(
- long recipientPersonnelId,
- string titleTemplate,
- string contentTemplate,
- Dictionary variables,
- SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info,
- string businessType = "",
- long? businessId = null);
+ /// 使用模板发送系统消息输入模型
+ /// 发送结果
+ Task SendSystemMessageByTemplateAsync(SendSystemMessageByTemplateInput input);
#endregion
@@ -113,139 +66,31 @@ public interface ISystemMessageService
///
/// 消息ID
/// 接收人员ID
- ///
- Task MarkMessageAsReadAsync(long messageId, long recipientPersonnelId);
+ /// 操作结果
+ Task MarkMessageAsReadAsync(long messageId, long recipientPersonnelId);
///
/// 批量标记消息为已读
///
/// 消息ID列表
/// 接收人员ID
- ///
- Task BatchMarkMessagesAsReadAsync(List messageIds, long recipientPersonnelId);
+ /// 操作结果
+ Task BatchMarkMessagesAsReadAsync(List messageIds, long recipientPersonnelId);
///
/// 删除消息
///
/// 消息ID
/// 接收人员ID
- ///
- Task DeleteMessageAsync(long messageId, long recipientPersonnelId);
+ /// 操作结果
+ Task DeleteMessageAsync(long messageId, long recipientPersonnelId);
///
/// 获取用户未读消息数量
///
/// 接收人员ID
- ///
+ /// 未读消息数量
Task GetUnreadMessageCountAsync(long recipientPersonnelId);
#endregion
-}
-
-///
-/// 系统消息类型枚举
-///
-public enum SystemMessageTypeEnum
-{
- ///
- /// 信息
- ///
- Info = 1,
-
- ///
- /// 成功
- ///
- Success = 2,
-
- ///
- /// 警告
- ///
- Warning = 3,
-
- ///
- /// 错误
- ///
- Error = 4,
-
- ///
- /// 紧急
- ///
- Urgent = 5
-}
-
-///
-/// 系统消息项
-///
-public class SystemMessageItem
-{
- ///
- /// 接收人员ID
- ///
- public long RecipientPersonnelId { get; set; }
-
- ///
- /// 消息标题
- ///
- public string Title { get; set; } = "";
-
- ///
- /// 消息内容
- ///
- public string Content { get; set; } = "";
-
- ///
- /// 消息类型
- ///
- public SystemMessageTypeEnum MessageType { get; set; } = SystemMessageTypeEnum.Info;
-
- ///
- /// 业务类型
- ///
- public string BusinessType { get; set; } = "";
-
- ///
- /// 业务ID
- ///
- public long? BusinessId { get; set; }
-
- ///
- /// 操作按钮列表
- ///
- public List Actions { get; set; } = new List();
-
- ///
- /// 个性化变量
- ///
- public Dictionary Variables { get; set; } = new Dictionary();
-}
-
-///
-/// 系统消息操作按钮
-///
-public class SystemMessageAction
-{
- ///
- /// 操作ID
- ///
- public string ActionId { get; set; } = "";
-
- ///
- /// 操作名称
- ///
- public string ActionName { get; set; } = "";
-
- ///
- /// 操作URL
- ///
- public string ActionUrl { get; set; } = "";
-
- ///
- /// 操作类型(按钮、链接等)
- ///
- public string ActionType { get; set; } = "button";
-
- ///
- /// 是否主要操作
- ///
- public bool IsPrimary { get; set; } = false;
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendEmailInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendEmailInput.cs
new file mode 100644
index 0000000..5812510
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendEmailInput.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 批量发送邮件输入
+///
+public class BatchSendEmailInput
+{
+ ///
+ /// 接收人邮箱列表
+ ///
+ [Required(ErrorMessage = "接收人邮箱列表不能为空")]
+ [MinLength(1, ErrorMessage = "至少需要一个接收人")]
+ public List Recipients { get; set; } = new List();
+
+ ///
+ /// 邮件主题
+ ///
+ [Required(ErrorMessage = "邮件主题不能为空")]
+ [MaxLength(500, ErrorMessage = "邮件主题长度不能超过500个字符")]
+ public string Subject { get; set; } = "";
+
+ ///
+ /// 邮件内容
+ ///
+ [Required(ErrorMessage = "邮件内容不能为空")]
+ public string Content { get; set; } = "";
+
+ ///
+ /// 是否HTML格式
+ ///
+ public bool IsHtml { get; set; } = true;
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendPersonalizedEmailInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendPersonalizedEmailInput.cs
new file mode 100644
index 0000000..fcfd6d1
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendPersonalizedEmailInput.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 个性化批量发送邮件输入
+///
+public class BatchSendPersonalizedEmailInput
+{
+ ///
+ /// 邮件项列表
+ ///
+ [Required(ErrorMessage = "邮件项列表不能为空")]
+ [MinLength(1, ErrorMessage = "至少需要一个邮件项")]
+ public List EmailItems { get; set; } = new List();
+}
+
+///
+/// 邮件项
+///
+public class EmailItem
+{
+ ///
+ /// 接收人邮箱
+ ///
+ [Required(ErrorMessage = "接收人邮箱不能为空")]
+ [EmailAddress(ErrorMessage = "邮箱地址格式无效")]
+ public string RecipientEmail { get; set; } = "";
+
+ ///
+ /// 邮件主题
+ ///
+ [Required(ErrorMessage = "邮件主题不能为空")]
+ [MaxLength(500, ErrorMessage = "邮件主题长度不能超过500个字符")]
+ public string Subject { get; set; } = "";
+
+ ///
+ /// 邮件内容
+ ///
+ [Required(ErrorMessage = "邮件内容不能为空")]
+ public string Content { get; set; } = "";
+
+ ///
+ /// 是否HTML格式
+ ///
+ public bool IsHtml { get; set; } = true;
+
+ ///
+ /// 附件文件路径列表
+ ///
+ public List? Attachments { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendSystemMessageInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendSystemMessageInput.cs
new file mode 100644
index 0000000..d899101
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/BatchSendSystemMessageInput.cs
@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 批量发送系统消息输入模型
+///
+public class BatchSendSystemMessageInput
+{
+ ///
+ /// 接收人员ID列表
+ ///
+ [Required(ErrorMessage = "接收人员ID列表不能为空")]
+ public List RecipientPersonnelIds { get; set; } = new List();
+
+ ///
+ /// 消息标题
+ ///
+ [Required(ErrorMessage = "消息标题不能为空")]
+ [StringLength(200, ErrorMessage = "消息标题长度不能超过200个字符")]
+ public string Title { get; set; } = "";
+
+ ///
+ /// 消息内容
+ ///
+ [Required(ErrorMessage = "消息内容不能为空")]
+ [StringLength(4000, ErrorMessage = "消息内容长度不能超过4000个字符")]
+ public string Content { get; set; } = "";
+
+ ///
+ /// 消息类型
+ ///
+ public SystemMessageTypeEnum MessageType { get; set; } = SystemMessageTypeEnum.Info;
+
+ ///
+ /// 业务类型
+ ///
+ [StringLength(100, ErrorMessage = "业务类型长度不能超过100个字符")]
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 业务ID
+ ///
+ public long? BusinessId { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/ExtractVariablesInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/ExtractVariablesInput.cs
new file mode 100644
index 0000000..523dbee
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/ExtractVariablesInput.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 提取变量输入模型
+///
+public class ExtractVariablesInput
+{
+ ///
+ /// 模板内容
+ ///
+ [Required(ErrorMessage = "模板内容不能为空")]
+ public string Template { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetBusinessVariablesInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetBusinessVariablesInput.cs
new file mode 100644
index 0000000..4c8aa7c
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetBusinessVariablesInput.cs
@@ -0,0 +1,25 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 获取业务变量输入模型
+///
+public class GetBusinessVariablesInput
+{
+ ///
+ /// 业务类型
+ ///
+ [Required(ErrorMessage = "业务类型不能为空")]
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 业务ID
+ ///
+ public long? BusinessId { get; set; }
+
+ ///
+ /// 业务数据
+ ///
+ public string BusinessData { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetPredefinedTemplateInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetPredefinedTemplateInput.cs
new file mode 100644
index 0000000..64bff02
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetPredefinedTemplateInput.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 获取指定预定义模板输入模型
+///
+public class GetPredefinedTemplateInput
+{
+ ///
+ /// 模板ID
+ ///
+ [Required(ErrorMessage = "模板ID不能为空")]
+ public string TemplateId { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetPredefinedTemplatesInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetPredefinedTemplatesInput.cs
new file mode 100644
index 0000000..8d810f9
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/GetPredefinedTemplatesInput.cs
@@ -0,0 +1,12 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 获取预定义模板列表输入模型
+///
+public class GetPredefinedTemplatesInput
+{
+ ///
+ /// 模板分类
+ ///
+ public string Category { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/NotificationHistoryPageInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/NotificationHistoryPageInput.cs
new file mode 100644
index 0000000..0b7ecfb
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/NotificationHistoryPageInput.cs
@@ -0,0 +1,47 @@
+using System;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+public class NotificationHistoryPageInput
+{
+ ///
+ /// 通知设置ID
+ ///
+ public long? NotificationSettingId { get; set; }
+
+ ///
+ /// 接收人员ID
+ ///
+ public long? RecipientPersonnelId { get; set; }
+
+ ///
+ /// 接收人员姓名
+ ///
+ public string RecipientPersonnelName { get; set; } = "";
+
+ ///
+ /// 通知方式
+ ///
+ public NotificationTypeEnum? NotificationType { get; set; }
+
+ ///
+ /// 发送状态
+ ///
+ public NotificationStatusEnum? SendStatus { get; set; }
+
+ ///
+ /// 业务类型
+ ///
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 开始时间
+ ///
+ public DateTime? StartTime { get; set; }
+
+ ///
+ /// 结束时间
+ ///
+ public DateTime? EndTime { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/NotificationSettingPageInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/NotificationSettingPageInput.cs
new file mode 100644
index 0000000..62e42cc
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/NotificationSettingPageInput.cs
@@ -0,0 +1,10 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+public class NotificationSettingPageInput
+{
+ public string? NotificationName { get; set; }
+
+ public bool? IsEnabled { get; set; }
+
+ public long? PersonnelGroupId { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/PersonnelGroupGetPageInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/PersonnelGroupGetPageInput.cs
new file mode 100644
index 0000000..a43262f
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/PersonnelGroupGetPageInput.cs
@@ -0,0 +1,18 @@
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+public class PersonnelGroupGetPageInput
+{
+ public string GroupName { get; set; } = "";
+
+ ///
+ /// 人员组类型
+ ///
+ public PersonnelGroupTypeEnum? GroupType { get; set; }
+
+ ///
+ /// 是否启用
+ ///
+ public bool? IsEnabled { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/PersonnelGroupUpdateInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/PersonnelGroupUpdateInput.cs
new file mode 100644
index 0000000..0c253bb
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/PersonnelGroupUpdateInput.cs
@@ -0,0 +1,16 @@
+using System.ComponentModel.DataAnnotations;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 更新人员组输入
+///
+public class PersonnelGroupUpdateInput : PersonnelGroupCreateInput
+{
+ ///
+ /// 人员组ID
+ ///
+ [Required(ErrorMessage = "人员组ID不能为空")]
+ public long Id { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/RenderTemplateInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/RenderTemplateInput.cs
new file mode 100644
index 0000000..3dafe5c
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/RenderTemplateInput.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 渲染模板输入模型
+///
+public class RenderTemplateInput
+{
+ ///
+ /// 模板内容
+ ///
+ [Required(ErrorMessage = "模板内容不能为空")]
+ public string Template { get; set; } = "";
+
+ ///
+ /// 变量字典
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendEmailByTemplateInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendEmailByTemplateInput.cs
new file mode 100644
index 0000000..e562d1e
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendEmailByTemplateInput.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 使用模板发送邮件输入
+///
+public class SendEmailByTemplateInput
+{
+ ///
+ /// 接收人邮箱
+ ///
+ [Required(ErrorMessage = "接收人邮箱不能为空")]
+ [EmailAddress(ErrorMessage = "邮箱地址格式无效")]
+ public string RecipientEmail { get; set; } = "";
+
+ ///
+ /// 邮件主题模板
+ ///
+ [Required(ErrorMessage = "邮件主题模板不能为空")]
+ public string SubjectTemplate { get; set; } = "";
+
+ ///
+ /// 邮件内容模板
+ ///
+ [Required(ErrorMessage = "邮件内容模板不能为空")]
+ public string ContentTemplate { get; set; } = "";
+
+ ///
+ /// 模板变量
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+
+ ///
+ /// 是否HTML格式
+ ///
+ public bool IsHtml { get; set; } = true;
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendEmailInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendEmailInput.cs
new file mode 100644
index 0000000..838349d
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendEmailInput.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 发送邮件输入
+///
+public class SendEmailInput
+{
+ ///
+ /// 接收人邮箱
+ ///
+ [Required(ErrorMessage = "接收人邮箱不能为空")]
+ [EmailAddress(ErrorMessage = "邮箱地址格式无效")]
+ public string RecipientEmail { get; set; } = "";
+
+ ///
+ /// 邮件主题
+ ///
+ [Required(ErrorMessage = "邮件主题不能为空")]
+ [MaxLength(500, ErrorMessage = "邮件主题长度不能超过500个字符")]
+ public string Subject { get; set; } = "";
+
+ ///
+ /// 邮件内容
+ ///
+ [Required(ErrorMessage = "邮件内容不能为空")]
+ public string Content { get; set; } = "";
+
+ ///
+ /// 是否HTML格式
+ ///
+ public bool IsHtml { get; set; } = true;
+
+ ///
+ /// 附件文件路径列表
+ ///
+ public List? Attachments { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendNotificationInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendNotificationInput.cs
index c0a3fc2..18f0b71 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendNotificationInput.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendNotificationInput.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
@@ -16,7 +17,9 @@ public class SendNotificationInput
///
[Required(ErrorMessage = "通知方式不能为空")]
public NotificationTypeEnum NotificationType { get; set; }
-
+
+ public long SettingId { get; set; }
+
///
/// 通知标题
///
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageByTemplateInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageByTemplateInput.cs
new file mode 100644
index 0000000..98ab527
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageByTemplateInput.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 使用模板发送系统消息输入模型
+///
+public class SendSystemMessageByTemplateInput
+{
+ ///
+ /// 接收人员ID
+ ///
+ [Required(ErrorMessage = "接收人员ID不能为空")]
+ [Range(1, long.MaxValue, ErrorMessage = "接收人员ID必须大于0")]
+ public long RecipientPersonnelId { get; set; }
+
+ ///
+ /// 消息标题模板
+ ///
+ [Required(ErrorMessage = "消息标题模板不能为空")]
+ [StringLength(200, ErrorMessage = "消息标题模板长度不能超过200个字符")]
+ public string TitleTemplate { get; set; } = "";
+
+ ///
+ /// 消息内容模板
+ ///
+ [Required(ErrorMessage = "消息内容模板不能为空")]
+ [StringLength(4000, ErrorMessage = "消息内容模板长度不能超过4000个字符")]
+ public string ContentTemplate { get; set; } = "";
+
+ ///
+ /// 模板变量
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+
+ ///
+ /// 消息类型
+ ///
+ public SystemMessageTypeEnum MessageType { get; set; } = SystemMessageTypeEnum.Info;
+
+ ///
+ /// 业务类型
+ ///
+ [StringLength(100, ErrorMessage = "业务类型长度不能超过100个字符")]
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 业务ID
+ ///
+ public long? BusinessId { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageInput.cs
new file mode 100644
index 0000000..87ebf57
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageInput.cs
@@ -0,0 +1,49 @@
+using System.ComponentModel.DataAnnotations;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 发送系统消息输入模型
+///
+public class SendSystemMessageInput
+{
+ ///
+ /// 接收人员ID
+ ///
+ [Required(ErrorMessage = "接收人员ID不能为空")]
+ [Range(1, long.MaxValue, ErrorMessage = "接收人员ID必须大于0")]
+ public long RecipientPersonnelId { get; set; }
+
+ public long NotificationSettingId { get; set; }
+
+ ///
+ /// 消息标题
+ ///
+ [Required(ErrorMessage = "消息标题不能为空")]
+ [StringLength(200, ErrorMessage = "消息标题长度不能超过200个字符")]
+ public string Title { get; set; } = "";
+
+ ///
+ /// 消息内容
+ ///
+ [Required(ErrorMessage = "消息内容不能为空")]
+ [StringLength(4000, ErrorMessage = "消息内容长度不能超过4000个字符")]
+ public string Content { get; set; } = "";
+
+ ///
+ /// 消息类型
+ ///
+ public SystemMessageTypeEnum MessageType { get; set; } = SystemMessageTypeEnum.Info;
+
+ ///
+ /// 业务类型
+ ///
+ [StringLength(100, ErrorMessage = "业务类型长度不能超过100个字符")]
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 业务ID
+ ///
+ public long? BusinessId { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageWithActionsInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageWithActionsInput.cs
new file mode 100644
index 0000000..264255c
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/SendSystemMessageWithActionsInput.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 发送带操作按钮的系统消息输入模型
+///
+public class SendSystemMessageWithActionsInput
+{
+ ///
+ /// 接收人员ID
+ ///
+ [Required(ErrorMessage = "接收人员ID不能为空")]
+ [Range(1, long.MaxValue, ErrorMessage = "接收人员ID必须大于0")]
+ public long RecipientPersonnelId { get; set; }
+
+ ///
+ /// 消息标题
+ ///
+ [Required(ErrorMessage = "消息标题不能为空")]
+ [StringLength(200, ErrorMessage = "消息标题长度不能超过200个字符")]
+ public string Title { get; set; } = "";
+
+ ///
+ /// 消息内容
+ ///
+ [Required(ErrorMessage = "消息内容不能为空")]
+ [StringLength(4000, ErrorMessage = "消息内容长度不能超过4000个字符")]
+ public string Content { get; set; } = "";
+
+ ///
+ /// 操作按钮列表
+ ///
+ public List Actions { get; set; } = new List();
+
+ ///
+ /// 消息类型
+ ///
+ public SystemMessageTypeEnum MessageType { get; set; } = SystemMessageTypeEnum.Info;
+
+ ///
+ /// 业务类型
+ ///
+ [StringLength(100, ErrorMessage = "业务类型长度不能超过100个字符")]
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 业务ID
+ ///
+ public long? BusinessId { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/ValidateTemplateInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/ValidateTemplateInput.cs
new file mode 100644
index 0000000..246998d
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Input/ValidateTemplateInput.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
+
+///
+/// 验证模板输入模型
+///
+public class ValidateTemplateInput
+{
+ ///
+ /// 模板内容
+ ///
+ [Required(ErrorMessage = "模板内容不能为空")]
+ public string Template { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/BatchSendEmailOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/BatchSendEmailOutput.cs
new file mode 100644
index 0000000..2522f29
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/BatchSendEmailOutput.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 批量发送邮件输出
+///
+public class BatchSendEmailOutput
+{
+ ///
+ /// 总发送数量
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 成功发送数量
+ ///
+ public int SuccessCount { get; set; }
+
+ ///
+ /// 失败发送数量
+ ///
+ public int FailedCount { get; set; }
+
+ ///
+ /// 发送时间
+ ///
+ public DateTime SendTime { get; set; } = DateTime.Now;
+
+ ///
+ /// 详细发送结果
+ ///
+ public List Results { get; set; } = new List();
+
+ ///
+ /// 整体是否成功(所有邮件都发送成功)
+ ///
+ public bool IsAllSuccess => FailedCount == 0;
+
+ ///
+ /// 成功率
+ ///
+ public decimal SuccessRate => TotalCount > 0 ? (decimal)SuccessCount / TotalCount : 0;
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/BatchSendSystemMessageOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/BatchSendSystemMessageOutput.cs
new file mode 100644
index 0000000..fee4b19
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/BatchSendSystemMessageOutput.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 批量发送系统消息输出模型
+///
+public class BatchSendSystemMessageOutput
+{
+ ///
+ /// 总发送数量
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 成功发送数量
+ ///
+ public int SuccessCount { get; set; }
+
+ ///
+ /// 失败发送数量
+ ///
+ public int FailureCount { get; set; }
+
+ ///
+ /// 详细发送结果
+ /// Key: 人员ID, Value: 发送结果
+ ///
+ public Dictionary Results { get; set; } = new Dictionary();
+
+ ///
+ /// 批量发送时间
+ ///
+ public DateTime SentTime { get; set; }
+
+ ///
+ /// 是否全部发送成功
+ ///
+ public bool AllSuccess => FailureCount == 0;
+
+ ///
+ /// 成功率(百分比)
+ ///
+ public double SuccessRate => TotalCount > 0 ? (double)SuccessCount / TotalCount * 100 : 0;
+
+ ///
+ /// 创建批量发送结果
+ ///
+ public static BatchSendSystemMessageOutput Create(Dictionary results)
+ {
+ var output = new BatchSendSystemMessageOutput
+ {
+ Results = results,
+ TotalCount = results.Count,
+ SuccessCount = results.Values.Count(r => r.Success),
+ SentTime = DateTime.Now
+ };
+
+ output.FailureCount = output.TotalCount - output.SuccessCount;
+ return output;
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/EmailItem.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/EmailItem.cs
new file mode 100644
index 0000000..903b7aa
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/EmailItem.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 邮件项
+///
+public class EmailItem
+{
+ ///
+ /// 接收人邮箱
+ ///
+ public string RecipientEmail { get; set; } = "";
+
+ ///
+ /// 邮件主题
+ ///
+ public string Subject { get; set; } = "";
+
+ ///
+ /// 邮件内容
+ ///
+ public string Content { get; set; } = "";
+
+ ///
+ /// 是否HTML格式
+ ///
+ public bool IsHtml { get; set; } = true;
+
+ ///
+ /// 附件文件路径列表
+ ///
+ public List Attachments { get; set; } = new List();
+
+ ///
+ /// 个性化变量
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/EmailServerStatusOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/EmailServerStatusOutput.cs
new file mode 100644
index 0000000..ca9695e
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/EmailServerStatusOutput.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 邮件服务器状态输出
+///
+public class EmailServerStatusOutput
+{
+ ///
+ /// 服务器是否可用
+ ///
+ public bool IsAvailable { get; set; }
+
+ ///
+ /// SMTP服务器地址
+ ///
+ public string SmtpServer { get; set; } = "";
+
+ ///
+ /// SMTP端口
+ ///
+ public int SmtpPort { get; set; }
+
+ ///
+ /// 检查时间
+ ///
+ public DateTime CheckTime { get; set; } = DateTime.Now;
+
+ ///
+ /// 响应时间(毫秒)
+ ///
+ public int ResponseTimeMs { get; set; }
+
+ ///
+ /// 错误消息
+ ///
+ public string? ErrorMessage { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/ExtractVariablesOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/ExtractVariablesOutput.cs
new file mode 100644
index 0000000..2c16eee
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/ExtractVariablesOutput.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 提取变量输出模型
+///
+public class ExtractVariablesOutput
+{
+ ///
+ /// 提取到的变量列表
+ ///
+ public List Variables { get; set; } = new List();
+
+ ///
+ /// 是否提取成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetBusinessVariablesOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetBusinessVariablesOutput.cs
new file mode 100644
index 0000000..561271c
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetBusinessVariablesOutput.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 获取业务变量输出模型
+///
+public class GetBusinessVariablesOutput
+{
+ ///
+ /// 业务变量字典
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+
+ ///
+ /// 是否获取成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetPredefinedTemplateOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetPredefinedTemplateOutput.cs
new file mode 100644
index 0000000..3acac2c
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetPredefinedTemplateOutput.cs
@@ -0,0 +1,22 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 获取指定预定义模板输出模型
+///
+public class GetPredefinedTemplateOutput
+{
+ ///
+ /// 预定义模板
+ ///
+ public PredefinedTemplate? Template { get; set; }
+
+ ///
+ /// 是否获取成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetPredefinedTemplatesOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetPredefinedTemplatesOutput.cs
new file mode 100644
index 0000000..34cc883
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetPredefinedTemplatesOutput.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 获取预定义模板列表输出模型
+///
+public class GetPredefinedTemplatesOutput
+{
+ ///
+ /// 预定义模板列表
+ ///
+ public List Templates { get; set; } = new List();
+
+ ///
+ /// 模板总数
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 是否获取成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetSystemVariablesOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetSystemVariablesOutput.cs
new file mode 100644
index 0000000..bd1bd31
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/GetSystemVariablesOutput.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 获取系统变量输出模型
+///
+public class GetSystemVariablesOutput
+{
+ ///
+ /// 系统变量字典
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+
+ ///
+ /// 是否获取成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/PredefinedTemplate.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/PredefinedTemplate.cs
new file mode 100644
index 0000000..37943f6
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/PredefinedTemplate.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 预定义模板
+///
+public class PredefinedTemplate
+{
+ ///
+ /// 模板ID
+ ///
+ public string TemplateId { get; set; } = "";
+
+ ///
+ /// 模板名称
+ ///
+ public string TemplateName { get; set; } = "";
+
+ ///
+ /// 模板分类
+ ///
+ public string Category { get; set; } = "";
+
+ ///
+ /// 模板描述
+ ///
+ public string Description { get; set; } = "";
+
+ ///
+ /// 邮件主题模板
+ ///
+ public string EmailSubjectTemplate { get; set; } = "";
+
+ ///
+ /// 邮件内容模板
+ ///
+ public string EmailContentTemplate { get; set; } = "";
+
+ ///
+ /// 系统消息标题模板
+ ///
+ public string SystemMessageTitleTemplate { get; set; } = "";
+
+ ///
+ /// 系统消息内容模板
+ ///
+ public string SystemMessageContentTemplate { get; set; } = "";
+
+ ///
+ /// 支持的变量列表
+ ///
+ public List SupportedVariables { get; set; } = new List();
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/RenderTemplateOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/RenderTemplateOutput.cs
new file mode 100644
index 0000000..e2b40ee
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/RenderTemplateOutput.cs
@@ -0,0 +1,22 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 渲染模板输出模型
+///
+public class RenderTemplateOutput
+{
+ ///
+ /// 渲染后的内容
+ ///
+ public string RenderedContent { get; set; } = "";
+
+ ///
+ /// 是否渲染成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SendEmailOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SendEmailOutput.cs
new file mode 100644
index 0000000..da4b5d1
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SendEmailOutput.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 发送邮件输出
+///
+public class SendEmailOutput
+{
+ ///
+ /// 是否发送成功
+ ///
+ public bool IsSuccess { get; set; }
+
+ ///
+ /// 错误消息
+ ///
+ public string? ErrorMessage { get; set; }
+
+ ///
+ /// 发送时间
+ ///
+ public DateTime SendTime { get; set; } = DateTime.Now;
+
+ ///
+ /// 接收人邮箱
+ ///
+ public string RecipientEmail { get; set; } = "";
+
+ ///
+ /// 邮件主题
+ ///
+ public string Subject { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SendSystemMessageOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SendSystemMessageOutput.cs
new file mode 100644
index 0000000..f537ad9
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SendSystemMessageOutput.cs
@@ -0,0 +1,61 @@
+using System;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 发送系统消息输出模型
+///
+public class SendSystemMessageOutput
+{
+ ///
+ /// 是否发送成功
+ ///
+ public bool Success { get; set; }
+
+ ///
+ /// 消息ID(发送成功时返回)
+ ///
+ public long? MessageId { get; set; }
+
+ ///
+ /// 错误消息(发送失败时返回)
+ ///
+ public string ErrorMessage { get; set; } = "";
+
+ ///
+ /// 发送时间
+ ///
+ public DateTime? SentTime { get; set; }
+
+ ///
+ /// 接收人员ID
+ ///
+ public long RecipientPersonnelId { get; set; }
+
+ ///
+ /// 创建成功结果
+ ///
+ public static SendSystemMessageOutput CreateSuccess(long messageId, long recipientPersonnelId, DateTime sentTime)
+ {
+ return new SendSystemMessageOutput
+ {
+ Success = true,
+ MessageId = messageId,
+ RecipientPersonnelId = recipientPersonnelId,
+ SentTime = sentTime
+ };
+ }
+
+ ///
+ /// 创建失败结果
+ ///
+ public static SendSystemMessageOutput CreateFailure(long recipientPersonnelId, string errorMessage)
+ {
+ return new SendSystemMessageOutput
+ {
+ Success = false,
+ RecipientPersonnelId = recipientPersonnelId,
+ ErrorMessage = errorMessage
+ };
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageAction.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageAction.cs
new file mode 100644
index 0000000..fdc4e30
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageAction.cs
@@ -0,0 +1,32 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 系统消息操作按钮
+///
+public class SystemMessageAction
+{
+ ///
+ /// 操作ID
+ ///
+ public string ActionId { get; set; } = "";
+
+ ///
+ /// 操作名称
+ ///
+ public string ActionName { get; set; } = "";
+
+ ///
+ /// 操作URL
+ ///
+ public string ActionUrl { get; set; } = "";
+
+ ///
+ /// 操作类型(按钮、链接等)
+ ///
+ public string ActionType { get; set; } = "button";
+
+ ///
+ /// 是否主要操作
+ ///
+ public bool IsPrimary { get; set; } = false;
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageItem.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageItem.cs
new file mode 100644
index 0000000..5f41b89
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageItem.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 系统消息项
+///
+public class SystemMessageItem
+{
+ ///
+ /// 接收人员ID
+ ///
+ public long RecipientPersonnelId { get; set; }
+
+ ///
+ /// 消息标题
+ ///
+ public string Title { get; set; } = "";
+
+ ///
+ /// 消息内容
+ ///
+ public string Content { get; set; } = "";
+
+ ///
+ /// 消息类型
+ ///
+ public SystemMessageTypeEnum MessageType { get; set; } = SystemMessageTypeEnum.Info;
+
+ ///
+ /// 业务类型
+ ///
+ public string BusinessType { get; set; } = "";
+
+ ///
+ /// 业务ID
+ ///
+ public long? BusinessId { get; set; }
+
+ ///
+ /// 操作按钮列表
+ ///
+ public List Actions { get; set; } = new List();
+
+ ///
+ /// 个性化变量
+ ///
+ public Dictionary Variables { get; set; } = new Dictionary();
+}
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageStatusOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageStatusOutput.cs
new file mode 100644
index 0000000..6ed28ee
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/SystemMessageStatusOutput.cs
@@ -0,0 +1,64 @@
+using System;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 系统消息状态输出模型
+///
+public class SystemMessageStatusOutput
+{
+ ///
+ /// 操作是否成功
+ ///
+ public bool Success { get; set; }
+
+ ///
+ /// 操作结果消息
+ ///
+ public string Message { get; set; } = "";
+
+ ///
+ /// 影响的消息数量
+ ///
+ public int AffectedCount { get; set; }
+
+ ///
+ /// 操作时间
+ ///
+ public DateTime OperationTime { get; set; }
+
+ ///
+ /// 人员ID
+ ///
+ public long PersonnelId { get; set; }
+
+ ///
+ /// 创建成功结果
+ ///
+ public static SystemMessageStatusOutput CreateSuccess(long personnelId, int affectedCount, string message = "操作成功")
+ {
+ return new SystemMessageStatusOutput
+ {
+ Success = true,
+ Message = message,
+ AffectedCount = affectedCount,
+ PersonnelId = personnelId,
+ OperationTime = DateTime.Now
+ };
+ }
+
+ ///
+ /// 创建失败结果
+ ///
+ public static SystemMessageStatusOutput CreateFailure(long personnelId, string errorMessage)
+ {
+ return new SystemMessageStatusOutput
+ {
+ Success = false,
+ Message = errorMessage,
+ AffectedCount = 0,
+ PersonnelId = personnelId,
+ OperationTime = DateTime.Now
+ };
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/TemplateValidationResult.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/TemplateValidationResult.cs
new file mode 100644
index 0000000..b6aa861
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/TemplateValidationResult.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 模板验证结果
+///
+public class TemplateValidationResult
+{
+ ///
+ /// 是否验证通过
+ ///
+ public bool IsValid { get; set; } = true;
+
+ ///
+ /// 错误信息列表
+ ///
+ public List Errors { get; set; } = new List();
+
+ ///
+ /// 警告信息列表
+ ///
+ public List Warnings { get; set; } = new List();
+
+ ///
+ /// 发现的变量列表
+ ///
+ public List Variables { get; set; } = new List();
+
+ ///
+ /// 添加错误信息
+ ///
+ /// 错误信息
+ public void AddError(string error)
+ {
+ IsValid = false;
+ Errors.Add(error);
+ }
+
+ ///
+ /// 添加警告信息
+ ///
+ /// 警告信息
+ public void AddWarning(string warning)
+ {
+ Warnings.Add(warning);
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/TemplateVariable.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/TemplateVariable.cs
new file mode 100644
index 0000000..84d409a
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/TemplateVariable.cs
@@ -0,0 +1,37 @@
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 模板变量定义
+///
+public class TemplateVariable
+{
+ ///
+ /// 变量名
+ ///
+ public string Name { get; set; } = "";
+
+ ///
+ /// 变量描述
+ ///
+ public string Description { get; set; } = "";
+
+ ///
+ /// 变量类型
+ ///
+ public string Type { get; set; } = "string";
+
+ ///
+ /// 是否必需
+ ///
+ public bool IsRequired { get; set; } = false;
+
+ ///
+ /// 默认值
+ ///
+ public string DefaultValue { get; set; } = "";
+
+ ///
+ /// 示例值
+ ///
+ public string ExampleValue { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/ValidateTemplateOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/ValidateTemplateOutput.cs
new file mode 100644
index 0000000..0684e41
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Notification/Output/ValidateTemplateOutput.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
+
+///
+/// 验证模板输出模型
+///
+public class ValidateTemplateOutput
+{
+ ///
+ /// 验证结果详情
+ ///
+ public TemplateValidationResult ValidationResult { get; set; } = new TemplateValidationResult();
+
+ ///
+ /// 是否验证成功
+ ///
+ public bool Success { get; set; } = true;
+
+ ///
+ /// 错误信息
+ ///
+ public string ErrorMessage { get; set; } = "";
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Personnel/IPersonnelQualificationService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Personnel/IPersonnelQualificationService.cs
index 7a64bd8..dec646e 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Personnel/IPersonnelQualificationService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Personnel/IPersonnelQualificationService.cs
@@ -4,6 +4,7 @@ using NPP.SmartSchedue.Api.Contracts.Domain.Personnel;
using ZhonTai.Admin.Core.Dto;
using NPP.SmartSchedue.Api.Contracts.Services.Personnel.Input;
using NPP.SmartSchedue.Api.Contracts.Services.Personnel.Output;
+using System;
namespace NPP.SmartSchedue.Api.Contracts.Services.Personnel;
@@ -68,4 +69,20 @@ public interface IPersonnelQualificationService
/// 人员ID
/// 人员有效资质实体列表
Task> GetPersonnelQualificationsAsync(long personnelId);
+
+ ///
+ /// 获取人员资质统计信息(横坐标为资质,纵坐标为人员数量)
+ ///
+ /// 人员资质统计结果
+ Task GetPersonnelQualificationStatisticsAsync();
+
+
+ ///
+ /// 获取即将过期的资质列表
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetExpiringAsync(DateTime today, IList personnelIds = null, bool includeExpired = false);
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Personnel/Output/ExpiringQualificationItemOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Personnel/Output/ExpiringQualificationItemOutput.cs
new file mode 100644
index 0000000..9186f4f
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Personnel/Output/ExpiringQualificationItemOutput.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Personnel.Output
+{
+ public class ExpiringQualificationItemOutput
+ {
+ public long PersonnelId { get; set; }
+ public string PersonnelName { get; set; }
+ public string PersonnelCode { get; set; }
+ public long QualificationId { get; set; }
+ public string QualificationLevel { get; set; }
+ public DateTime? ExpiryDate { get; set; }
+ public int DaysLeft { get; set; }
+
+ public string QualificationName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Work/IWorkOrderService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Work/IWorkOrderService.cs
index f9ed001..fe42377 100644
--- a/NPP.SmartSchedue.Api.Contracts/Services/Work/IWorkOrderService.cs
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Work/IWorkOrderService.cs
@@ -5,6 +5,7 @@ using ZhonTai.Admin.Core.Dto;
using NPP.SmartSchedue.Api.Contracts.Services.Work.Input;
using NPP.SmartSchedue.Api.Contracts.Services.Work.Output;
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using NPP.SmartSchedue.Api.Contracts.Domain.Work;
namespace NPP.SmartSchedue.Api.Contracts.Services.Work;
@@ -112,4 +113,13 @@ public interface IWorkOrderService
/// 用户ID
///
Task> GetByUserIdAsync(long userId);
+
+ ///
+ /// 转换任务班次的任务时间
+ ///
+ ///
+ ///
+ Task ConvertWorkOrderShift(WorkOrderEntity workOrderEntity);
+
+ Task> GetPendingSubmitCountByCreatorAsync();
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Workbench/IWorkbenchService.cs b/NPP.SmartSchedue.Api.Contracts/Services/Workbench/IWorkbenchService.cs
new file mode 100644
index 0000000..509206f
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Workbench/IWorkbenchService.cs
@@ -0,0 +1,20 @@
+using System.Threading.Tasks;
+using NPP.SmartSchedue.Api.Contracts.Services.Workbench.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Workbench.Output;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Workbench;
+
+///
+/// 工作台服务接口
+/// 提供工作台相关的数据查询和业务逻辑处理
+///
+public interface IWorkbenchService
+{
+ ///
+ /// 获取用户本周日历数据
+ /// 包含任务排班信息和班次不可用性信息,用于日历视图展示
+ ///
+ /// 查询参数
+ /// 本周日历数据
+ Task GetWeeklyCalendarAsync(WeeklyCalendarInput input);
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Workbench/Input/WeeklyCalendarInput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Workbench/Input/WeeklyCalendarInput.cs
new file mode 100644
index 0000000..4ee4aff
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Workbench/Input/WeeklyCalendarInput.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Workbench.Input;
+
+///
+/// 获取用户本周日历数据输入参数
+///
+public class WeeklyCalendarInput
+{
+ ///
+ /// 指定周的日期(可选,默认为当前周)
+ /// 可以是该周内任意一天,系统会自动计算周的开始和结束日期
+ ///
+ public DateTime? WeekDate { get; set; }
+
+ ///
+ /// 指定人员ID(可选,默认为当前登录用户)
+ /// 如果指定则获取该人员的日历数据,否则获取当前登录用户的数据
+ ///
+ public long? PersonnelId { get; set; }
+
+ ///
+ /// 是否包含已完成的任务(默认true)
+ ///
+ public bool IncludeCompletedTasks { get; set; } = true;
+
+ ///
+ /// 是否包含不可用时间段(默认true)
+ ///
+ public bool IncludeUnavailableSlots { get; set; } = true;
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api.Contracts/Services/Workbench/Output/WeeklyCalendarOutput.cs b/NPP.SmartSchedue.Api.Contracts/Services/Workbench/Output/WeeklyCalendarOutput.cs
new file mode 100644
index 0000000..de2ebb8
--- /dev/null
+++ b/NPP.SmartSchedue.Api.Contracts/Services/Workbench/Output/WeeklyCalendarOutput.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+
+namespace NPP.SmartSchedue.Api.Contracts.Services.Workbench.Output;
+
+///
+/// 用户本周日历数据输出
+///
+public class WeeklyCalendarOutput
+{
+ ///
+ /// 周开始日期(周一)
+ ///
+ public DateTime WeekStartDate { get; set; }
+
+ ///
+ /// 周结束日期(周日)
+ ///
+ public DateTime WeekEndDate { get; set; }
+
+ ///
+ /// 人员ID
+ ///
+ public long PersonnelId { get; set; }
+
+ ///
+ /// 人员姓名
+ ///
+ public string PersonnelName { get; set; }
+
+ ///
+ /// 每日日历数据
+ ///
+ public List Days { get; set; } = new List();
+}
+
+///
+/// 单日日历数据项
+///
+public class CalendarDayItem
+{
+ ///
+ /// 日期
+ ///
+ public DateTime Date { get; set; }
+
+ ///
+ /// 星期几(1=周一,7=周日)
+ ///
+ public int DayOfWeek { get; set; }
+
+ ///
+ /// 星期几中文名称
+ ///
+ public string DayOfWeekName { get; set; }
+
+ ///
+ /// 该日的班次数据
+ ///
+ public List Shifts { get; set; } = new List();
+}
+
+///
+/// 班次日历数据项
+///
+public class CalendarShiftItem
+{
+ ///
+ /// 班次ID
+ ///
+ public long ShiftId { get; set; }
+
+ ///
+ /// 班次名称
+ ///
+ public string ShiftName { get; set; }
+
+ ///
+ /// 班次开始时间
+ ///
+ public TimeSpan StartTime { get; set; }
+
+ ///
+ /// 班次结束时间
+ ///
+ public TimeSpan EndTime { get; set; }
+
+ ///
+ /// 该班次的工作任务
+ ///
+ public List Tasks { get; set; } = new List();
+
+ ///
+ /// 该班次的不可用时间段
+ ///
+ public List UnavailableItems { get; set; } = new List();
+}
+
+///
+/// 任务日历项
+///
+public class CalendarTaskItem
+{
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务代码
+ ///
+ public string TaskCode { get; set; }
+
+ ///
+ /// 项目号
+ ///
+ public string ProjectNumber { get; set; }
+
+ ///
+ /// 工序名称
+ ///
+ public string ProcessName { get; set; }
+
+ ///
+ /// 任务状态
+ ///
+ public int Status { get; set; }
+
+ ///
+ /// 状态名称
+ ///
+ public string StatusName { get; set; }
+
+ ///
+ /// 优先级
+ ///
+ public int Priority { get; set; }
+
+ ///
+ /// 预计工时
+ ///
+ public decimal? EstimatedHours { get; set; }
+
+ ///
+ /// 计划开始时间
+ ///
+ public DateTime PlannedStartTime { get; set; }
+
+ ///
+ /// 计划结束时间
+ ///
+ public DateTime PlannedEndTime { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Remarks { get; set; }
+}
+
+///
+/// 不可用时间段项
+///
+public class CalendarUnavailableItem
+{
+ ///
+ /// 不可用记录ID
+ ///
+ public long UnavailabilityId { get; set; }
+
+ ///
+ /// 不可用原因类型
+ ///
+ public int ReasonType { get; set; }
+
+ ///
+ /// 原因类型名称
+ ///
+ public string ReasonTypeName { get; set; }
+
+ ///
+ /// 备注说明
+ ///
+ public string Remark { get; set; }
+
+ ///
+ /// 生效开始时间(可选)
+ ///
+ public TimeSpan? EffectiveStartTime { get; set; }
+
+ ///
+ /// 生效结束时间(可选)
+ ///
+ public TimeSpan? EffectiveEndTime { get; set; }
+
+ ///
+ /// 优先级权重
+ ///
+ public int Priority { get; set; }
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api/Core/Consts/CacheKeys.cs b/NPP.SmartSchedue.Api/Core/Consts/CacheKeys.cs
index fe9b2da..1b2d384 100644
--- a/NPP.SmartSchedue.Api/Core/Consts/CacheKeys.cs
+++ b/NPP.SmartSchedue.Api/Core/Consts/CacheKeys.cs
@@ -18,5 +18,29 @@ public static partial class CacheKeys
/// 模块Id
///
public static string GetModuleActionKey(long id) => $"{ModuleActionKey}{id}";
+
+ ///
+ /// 通知设置缓存
+ ///
+ [Description("通知设置缓存")]
+ public const string NotificationSettingsKey = "notification:settings:list:";
+ ///
+ /// DynamicDepartmentIds
+ ///
+ [Description("动态部门人员数据")] public const string DynamicDepartment = "dynamic:department:list:";
+
+ ///
+ /// 获取动态部门人员数据
+ ///
+ ///
+ ///
+ public static string GetDynamicDepartmentKey(long id) => $"{DynamicDepartment}{id}";
+
+
+ ///
+ /// 人员缓存
+ ///
+ [Description("人员缓存")]
+ public const string UserListKey = "user:list:all:";
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api/NPP.SmartSchedue.Api.csproj b/NPP.SmartSchedue.Api/NPP.SmartSchedue.Api.csproj
index 003fb7d..abd644c 100644
--- a/NPP.SmartSchedue.Api/NPP.SmartSchedue.Api.csproj
+++ b/NPP.SmartSchedue.Api/NPP.SmartSchedue.Api.csproj
@@ -25,7 +25,6 @@
-
@@ -34,11 +33,12 @@
-
+
+
diff --git a/NPP.SmartSchedue.Api/Repositories/Notification/NotificationHistoryRepository.cs b/NPP.SmartSchedue.Api/Repositories/Notification/NotificationHistoryRepository.cs
index 6f4d4ce..5bc6077 100644
--- a/NPP.SmartSchedue.Api/Repositories/Notification/NotificationHistoryRepository.cs
+++ b/NPP.SmartSchedue.Api/Repositories/Notification/NotificationHistoryRepository.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
-using NPP.SmartSchedue.Api.Contracts.Core.Repositories;
using NPP.SmartSchedue.Api.Contracts.Domain.Notification;
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
using NPP.SmartSchedue.Api.Core.Repositories;
@@ -166,13 +165,10 @@ public class NotificationHistoryRepository : AppRepositoryBase
+ // 使用FreeSql的批量更新来提高性能
+ foreach (var update in updates)
{
- foreach (var update in updates)
- {
- await UpdateSendStatusAsync(update.Id, update.Status, update.Result, update.Error);
- }
- });
+ await UpdateSendStatusAsync(update.Id, update.Status, update.Result, update.Error);
+ }
}
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api/Repositories/Notification/NotificationSettingRepository.cs b/NPP.SmartSchedue.Api/Repositories/Notification/NotificationSettingRepository.cs
index 2bf5042..6d48439 100644
--- a/NPP.SmartSchedue.Api/Repositories/Notification/NotificationSettingRepository.cs
+++ b/NPP.SmartSchedue.Api/Repositories/Notification/NotificationSettingRepository.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json;
-using NPP.SmartSchedue.Api.Contracts.Core.Repositories;
using NPP.SmartSchedue.Api.Contracts.Domain.Notification;
using NPP.SmartSchedue.Api.Core.Repositories;
using ZhonTai.Admin.Core.Db.Transaction;
@@ -128,12 +127,48 @@ public class NotificationSettingRepository : AppRepositoryBase
public async Task> GetActiveNotificationSettingsForTimeAsync(DateTime currentTime)
{
- var currentTimeString = currentTime.ToString("HH:mm");
-
- return await Select
+ var allEnabledSettings = await Select
.Where(n => n.IsEnabled == true)
- .Where(n => n.StartTime <= currentTimeString && n.EndTime >= currentTimeString)
+ .Where(n => !string.IsNullOrWhiteSpace(n.StartTime) && !string.IsNullOrWhiteSpace(n.EndTime))
.Include(n => n.PersonnelGroup)
.ToListAsync();
+
+ var activeSettings = new List();
+ var currentTimeOnly = TimeOnly.FromDateTime(currentTime);
+
+ foreach (var setting in allEnabledSettings)
+ {
+ try
+ {
+ if (TimeOnly.TryParseExact(setting.StartTime, "HH:mm", out var startTime) &&
+ TimeOnly.TryParseExact(setting.EndTime, "HH:mm", out var endTime))
+ {
+ // 处理跨天的情况(如:22:00-06:00)
+ if (startTime <= endTime)
+ {
+ // 同一天内的时间段
+ if (currentTimeOnly >= startTime && currentTimeOnly <= endTime)
+ {
+ activeSettings.Add(setting);
+ }
+ }
+ else
+ {
+ // 跨天的时间段
+ if (currentTimeOnly >= startTime || currentTimeOnly <= endTime)
+ {
+ activeSettings.Add(setting);
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 时间格式解析失败,跳过此设置
+ continue;
+ }
+ }
+
+ return activeSettings;
}
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api/Repositories/Notification/PersonnelGroupRepository.cs b/NPP.SmartSchedue.Api/Repositories/Notification/PersonnelGroupRepository.cs
index 7aba444..56c996a 100644
--- a/NPP.SmartSchedue.Api/Repositories/Notification/PersonnelGroupRepository.cs
+++ b/NPP.SmartSchedue.Api/Repositories/Notification/PersonnelGroupRepository.cs
@@ -2,7 +2,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json;
-using NPP.SmartSchedue.Api.Contracts.Core.Repositories;
using NPP.SmartSchedue.Api.Contracts.Domain.Notification;
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
using NPP.SmartSchedue.Api.Core.Repositories;
diff --git a/NPP.SmartSchedue.Api/ServiceCollectionExtensions.cs b/NPP.SmartSchedue.Api/ServiceCollectionExtensions.cs
index 6e82eaa..dd1ff01 100644
--- a/NPP.SmartSchedue.Api/ServiceCollectionExtensions.cs
+++ b/NPP.SmartSchedue.Api/ServiceCollectionExtensions.cs
@@ -9,6 +9,10 @@ using NPP.SmartSchedue.Api.Services.Equipment;
using NPP.SmartSchedue.Api.Contracts.Domain.Equipment;
using NPP.SmartSchedue.Api.Repositories.Equipment;
using NPP.SmartSchedue.Api.Repositories.Personnel;
+using NPP.SmartSchedue.Api.Contracts.Services.Notification;
+using NPP.SmartSchedue.Api.Services.Notification;
+using NPP.SmartSchedue.Api.Contracts.Domain.Notification;
+using NPP.SmartSchedue.Api.Repositories.Notification;
using NPP.SmartSchedue.Api.Contracts;
using NPP.SmartSchedue.Api.Services.Integration.Algorithms;
using System;
@@ -43,6 +47,7 @@ namespace NPP.SmartSchedue.Api
// 注册人员资质Repository
services.AddScoped();
+ services.AddScoped();
return services;
}
@@ -62,5 +67,38 @@ namespace NPP.SmartSchedue.Api
return services;
}
+
+ ///
+ /// 注册通知相关服务
+ ///
+ public static IServiceCollection AddNotificationServices(this IServiceCollection services)
+ {
+ // 注册通知相关Repository
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ // 注册通知相关Service
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ return services;
+ }
+
+ ///
+ /// 注册工作订单整合操作相关服务
+ ///
+ public static IServiceCollection AddWorkOrderIntegrationOperationServices(this IServiceCollection services)
+ {
+ // 注册状态验证器
+ services.AddScoped();
+
+ // 注册统一操作服务接口和实现
+ services.AddScoped();
+
+ return services;
+ }
}
}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api/Services/Integration/Algorithms/ContextBuilderEngine.cs b/NPP.SmartSchedue.Api/Services/Integration/Algorithms/ContextBuilderEngine.cs
index 4be328d..2cb4e2d 100644
--- a/NPP.SmartSchedue.Api/Services/Integration/Algorithms/ContextBuilderEngine.cs
+++ b/NPP.SmartSchedue.Api/Services/Integration/Algorithms/ContextBuilderEngine.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using FreeSql;
using Microsoft.Extensions.Logging;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
using NPP.SmartSchedue.Api.Contracts.Domain.Time;
using NPP.SmartSchedue.Api.Contracts.Domain.Work;
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
@@ -497,21 +498,14 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
try
{
_logger.LogDebug("📚 开始加载人员历史任务数据");
-
- var personnelIds = context.AvailablePersonnel?.Select(p => p.Id).ToList() ?? new List();
- if (!personnelIds.Any())
- {
- _logger.LogDebug("⏭️ 跳过历史任务加载,无人员数据");
- return;
- }
-
// 查询最近的历史任务(例如最近30天)
var recentDate = DateTime.Now.AddDays(-30);
var historyTasks = await _workOrderRepository.Select
.Where(w => w.WorkOrderDate >= recentDate &&
w.AssignedPersonnelId.HasValue &&
- personnelIds.Contains(w.AssignedPersonnelId.Value))
+ w.Status >= (int)WorkOrderStatusEnum.Assigned &&
+ w.Status < (int)WorkOrderStatusEnum.Cancelled)
.ToListAsync();
// 按人员分组
@@ -861,7 +855,6 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
// 通过所有筛选条件
result.IsFeasible = true;
-
context.PrefilterResults[key] = result;
}
}
@@ -1143,8 +1136,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
if (context.PersonnelHistoryTasks.TryGetValue(personnel.Id, out var historyTasks))
{
var conflictingTasks = historyTasks.Where(h =>
- h.WorkDate.Date == task.WorkOrderDate.Date &&
- h.ShiftId == task.ShiftId).ToList();
+ h.WorkDate.Date == task.WorkOrderDate.Date).ToList();
if (conflictingTasks.Any())
return false;
diff --git a/NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs b/NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs
index 12b3c6c..ad3c992 100644
--- a/NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs
+++ b/NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs
@@ -54,7 +54,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
try
{
- _logger.LogInformation("🚀 启动增强版线性规划优化 - 任务数:{TaskCount},人员数:{PersonnelCount},预筛选缓存:{CacheSize}",
+ _logger.LogDebug("🚀 启动增强版线性规划优化 - 任务数:{TaskCount},人员数:{PersonnelCount},预筛选缓存:{CacheSize}",
context.Tasks.Count, context.AvailablePersonnel.Count, context.PrefilterResults.Count);
// 第一步:创建高性能求解器
@@ -62,11 +62,11 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
// 第二步:构建决策变量矩阵 (x[i,j] = 1表示任务i分配给人员j)
var decisionVariables = BuildDecisionVariables(solver, context);
- _logger.LogInformation("📊 决策变量构建完成 - 变量数量:{VariableCount}", decisionVariables.Count);
+ _logger.LogDebug("📊 决策变量构建完成 - 变量数量:{VariableCount}", decisionVariables.Count);
// 第三步:构建辅助变量 (支持复杂约束建模)
var auxiliaryVariables = BuildAuxiliaryVariables(solver, context, decisionVariables);
- _logger.LogInformation("🔧 辅助变量构建完成 - 辅助变量数量:{AuxCount}", auxiliaryVariables.Count);
+ _logger.LogDebug("🔧 辅助变量构建完成 - 辅助变量数量:{AuxCount}", auxiliaryVariables.Count);
// 第四步:Level 1 基础约束 - 对应遗传算法的基础约束验证
await AddLevel1BasicConstraints(solver, decisionVariables, auxiliaryVariables, context);
@@ -157,7 +157,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
{
unavailabilitySkippedCount++;
skippedCount++;
- _logger.LogTrace("🚫 跳过不可用组合 - 任务{TaskId}人员{PersonnelId}在{Date}班次{ShiftId}不可用",
+ _logger.LogDebug("🚫 跳过不可用组合 - 任务{TaskId}人员{PersonnelId}在{Date}班次{ShiftId}不可用",
task.Id, personnel.Id, task.WorkOrderDate.ToString("yyyy-MM-dd"), task.ShiftId);
continue;
}
@@ -190,7 +190,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
}
}
- _logger.LogInformation("📊 【增强版】决策变量构建统计 - 创建:{Created}个,跳过:{Skipped}个(不可用性:{UnavailabilitySkipped}),变量密度:{Density:P2},不可用性优化率:{UnavailabilityRate:P2}",
+ _logger.LogDebug("📊 【增强版】决策变量构建统计 - 创建:{Created}个,跳过:{Skipped}个(不可用性:{UnavailabilitySkipped}),变量密度:{Density:P2},不可用性优化率:{UnavailabilityRate:P2}",
createdCount, skippedCount, unavailabilitySkippedCount,
(double)createdCount / (createdCount + skippedCount),
(double)unavailabilitySkippedCount / (createdCount + skippedCount));
@@ -229,15 +229,15 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
}
// 3. 时间冲突指示变量 (二进制变量,标识是否存在时间冲突)
- var timeSlots = context.Tasks.Select(t => new { Date = t.WorkOrderDate.Date, ShiftId = t.ShiftId })
+ var timeSlots = context.Tasks.Select(t => new { Date = t.WorkOrderDate.Date })
.Distinct().ToList();
foreach (var personnel in context.AvailablePersonnel)
{
foreach (var timeSlot in timeSlots)
{
- var conflictVar = solver.MakeBoolVar($"conflict_{personnel.Id}_{timeSlot.Date:yyyyMMdd}_{timeSlot.ShiftId}");
- auxiliaryVars[$"conflict_{personnel.Id}_{timeSlot.Date:yyyyMMdd}_{timeSlot.ShiftId}"] = conflictVar;
+ var conflictVar = solver.MakeBoolVar($"conflict_{personnel.Id}_{timeSlot.Date:yyyyMMdd}");
+ auxiliaryVars[$"conflict_{personnel.Id}_{timeSlot.Date:yyyyMMdd}"] = conflictVar;
}
}
@@ -256,9 +256,9 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
auxiliaryVars[$"fl_violation_{task.Id}"] = flViolationVar;
}*/
- _logger.LogInformation("🔧 辅助变量构建完成 - 总数:{AuxVarCount}(工作负载:{WorkloadCount},冲突指示:{ConflictCount},班次违规:{ShiftCount},FL违规:{FLCount})",
+ _logger.LogDebug("🔧 辅助变量构建完成 - 总数:{AuxVarCount}(工作负载:{WorkloadCount},冲突指示:{ConflictCount},班次违规:{ShiftCount},FL违规:{FLCount})",
auxiliaryVars.Count,
- context.AvailablePersonnel.Count,
+ context.Tasks.Count / context.AvailablePersonnel.Count,
context.AvailablePersonnel.Count * timeSlots.Count,
context.AvailablePersonnel.Count,
context.Tasks.Count);
@@ -284,7 +284,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
await Task.CompletedTask;
var constraintCount = 0;
- _logger.LogInformation("🏗️ 开始添加Level 1基础约束...");
+ _logger.LogDebug("🏗️ 开始添加Level 1基础约束...");
// 基础约束1:任务分配完整性 - 每个任务必须且只能分配给一个人员
foreach (var task in context.Tasks)
@@ -306,7 +306,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
foreach (var personnel in context.AvailablePersonnel)
{
// 按时间段分组检查冲突
- var timeGroups = context.Tasks.GroupBy(t => new { Date = t.WorkOrderDate.Date, ShiftId = t.ShiftId });
+ var timeGroups = context.Tasks.GroupBy(t => new { Date = t.WorkOrderDate.Date });
foreach (var timeGroup in timeGroups)
{
@@ -322,7 +322,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
{
// 约束:同一人员在同一时间段最多执行1个任务
var constraint = solver.MakeConstraint(0, 1,
- $"time_conflict_{personnel.Id}_{timeGroup.Key.Date:yyyyMMdd}_{timeGroup.Key.ShiftId}");
+ $"time_conflict_{personnel.Id}_{timeGroup.Key.Date:yyyyMMdd}");
foreach (var variable in timeSlotVariables)
{
@@ -331,12 +331,12 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
constraintCount++;
// 连接辅助变量(用于目标函数惩罚)
- var conflictVarKey = $"conflict_{personnel.Id}_{timeGroup.Key.Date:yyyyMMdd}_{timeGroup.Key.ShiftId}";
+ var conflictVarKey = $"conflict_{personnel.Id}_{timeGroup.Key.Date:yyyyMMdd}";
if (auxiliaryVars.ContainsKey(conflictVarKey))
{
// 如果分配了多个任务,冲突变量=1
var conflictConstraint = solver.MakeConstraint(0, double.PositiveInfinity,
- $"conflict_indicator_{personnel.Id}_{timeGroup.Key.Date:yyyyMMdd}_{timeGroup.Key.ShiftId}");
+ $"conflict_indicator_{personnel.Id}_{timeGroup.Key.Date:yyyyMMdd}");
foreach (var variable in timeSlotVariables)
{
@@ -367,7 +367,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
}
}
- _logger.LogInformation("✅ Level 1基础约束添加完成 - 约束数量:{ConstraintCount}", constraintCount);
+ _logger.LogDebug("✅ Level 1基础约束添加完成 - 约束数量:{ConstraintCount}", constraintCount);
}
#endregion
@@ -387,15 +387,15 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
{
var constraintCount = 0;
- _logger.LogInformation("🔄 开始添加Level 2组合约束...");
+ _logger.LogDebug("🔄 开始添加Level 2组合约束...");
// 组合约束1:动态班次规则约束 - 对应CalculateDynamicShiftRuleScore
- constraintCount += await AddDynamicShiftRuleConstraints(solver, decisionVars, auxiliaryVars, context);
+ //constraintCount += await AddDynamicShiftRuleConstraints(solver, decisionVars, auxiliaryVars, context);
// 组合约束3:人员日工作量限制约束
- constraintCount += AddDailyWorkloadConstraints(solver, decisionVars, auxiliaryVars, context);
+ //constraintCount += AddDailyWorkloadConstraints(solver, decisionVars, auxiliaryVars, context);
- _logger.LogInformation("✅ Level 2组合约束添加完成 - 约束数量:{ConstraintCount}", constraintCount);
+ _logger.LogDebug("✅ Level 2组合约束添加完成 - 约束数量:{ConstraintCount}", constraintCount);
}
///
@@ -582,7 +582,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
await Task.CompletedTask;
var constraintCount = 0;
- _logger.LogInformation("💼 开始添加Level 3业务约束...");
+ _logger.LogDebug("💼 开始添加Level 3业务约束...");
// 业务约束1:FL优先级规则 - 对应CalculateFLPriorityRuleScore
//constraintCount += AddFLPriorityConstraints(solver, decisionVars, auxiliaryVars, context);
@@ -594,12 +594,12 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
//constraintCount += AddProjectContinuityConstraints(solver, decisionVars, auxiliaryVars, context);
// 业务约束4:人员资质约束 - 确保只分配给具备相应资质的人员
- constraintCount += await AddQualificationConstraints(solver, decisionVars, context);
+ //constraintCount += await AddQualificationConstraints(solver, decisionVars, context);
// 业务约束5:【Ultra think】人员班次不可用性硬约束 - 确保不会分配给不可用人员
- constraintCount += await AddPersonnelUnavailabilityConstraints(solver, decisionVars, context);
+ //constraintCount += await AddPersonnelUnavailabilityConstraints(solver, decisionVars, context);
- _logger.LogInformation("✅ Level 3业务约束添加完成 - 约束数量:{ConstraintCount}", constraintCount);
+ _logger.LogDebug("✅ Level 3业务约束添加完成 - 约束数量:{ConstraintCount}", constraintCount);
}
///
@@ -916,7 +916,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
}
constraintCount++;
- _logger.LogInformation("✅ 公平性约束添加完成 - 约束数量:{ConstraintCount},平均负载:{AvgWorkload:F2},最大负载:{MaxWorkload:F2},最小参与人数:{MinParticipation}",
+ _logger.LogDebug("✅ 公平性约束添加完成 - 约束数量:{ConstraintCount},平均负载:{AvgWorkload:F2},最大负载:{MaxWorkload:F2},最小参与人数:{MinParticipation}",
constraintCount, avgWorkload, maxReasonableWorkload, minPersonnelUtilization);
}
@@ -950,7 +950,7 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
AddFairnessRewards(objective, auxiliaryVars, context);
// 目标组件4:业务优先级奖励 - 对应遗传算法的业务逻辑优化
- AddBusinessPriorityRewards(objective, decisionVars, auxiliaryVars, context);
+ //AddBusinessPriorityRewards(objective, decisionVars, auxiliaryVars, context);
_logger.LogInformation("✅ 增强版目标函数构建完成,包含4大评分维度");
}
@@ -1004,16 +1004,16 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
}
// Level 2惩罚:班次规则违规重度惩罚
- foreach (var kvp in auxiliaryVars.Where(v => v.Key.StartsWith("shift_violation_")))
- {
- objective.SetCoefficient(kvp.Value, -LEVEL2_CONSTRAINT_PENALTY);
- }
+ // foreach (var kvp in auxiliaryVars.Where(v => v.Key.StartsWith("shift_violation_")))
+ // {
+ // objective.SetCoefficient(kvp.Value, -LEVEL2_CONSTRAINT_PENALTY);
+ // }
// Level 3惩罚:业务逻辑违规中度惩罚
- foreach (var kvp in auxiliaryVars.Where(v => v.Key.StartsWith("fl_violation_")))
- {
- objective.SetCoefficient(kvp.Value, -LEVEL3_CONSTRAINT_PENALTY);
- }
+ // foreach (var kvp in auxiliaryVars.Where(v => v.Key.StartsWith("fl_violation_")))
+ // {
+ // objective.SetCoefficient(kvp.Value, -LEVEL3_CONSTRAINT_PENALTY);
+ // }
}
///
@@ -1025,11 +1025,11 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
GlobalAllocationContext context)
{
// 负载均衡奖励:减少偏差获得奖励
- foreach (var kvp in auxiliaryVars.Where(v => v.Key.StartsWith("pos_dev_") || v.Key.StartsWith("neg_dev_")))
- {
- // 偏差越小,奖励越多(通过负的偏差系数实现)
- objective.SetCoefficient(kvp.Value, -FAIRNESS_REWARD_FACTOR);
- }
+ // foreach (var kvp in auxiliaryVars.Where(v => v.Key.StartsWith("pos_dev_") || v.Key.StartsWith("neg_dev_")))
+ // {
+ // // 偏差越小,奖励越多(通过负的偏差系数实现)
+ // objective.SetCoefficient(kvp.Value, -FAIRNESS_REWARD_FACTOR);
+ // }
}
///
@@ -2282,7 +2282,18 @@ namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
}
}
}
+
+ // 检查历史任务人员的分配
+ if (context.PersonnelHistoryTasks.TryGetValue(personnelId, out var historyTask))
+ {
+ var taskDate = task.WorkOrderDate.Date;
+ if (historyTask.Any(m => m.WorkDate.Date == taskDate))
+ {
+ return true;
+ }
+ }
+
return false; // 人员可用
}
catch (Exception ex)
diff --git a/NPP.SmartSchedue.Api/Services/Integration/IntegrationRecordService.cs b/NPP.SmartSchedue.Api/Services/Integration/IntegrationRecordService.cs
index c84e7c6..8afdcb6 100644
--- a/NPP.SmartSchedue.Api/Services/Integration/IntegrationRecordService.cs
+++ b/NPP.SmartSchedue.Api/Services/Integration/IntegrationRecordService.cs
@@ -9,6 +9,7 @@ using NPP.SmartSchedue.Api.Contracts.Services.Integration;
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output;
using NPP.SmartSchedue.Api.Repositories.Work;
+using NPP.SmartSchedue.Api.Repositories.Equipment;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -28,15 +29,18 @@ namespace NPP.SmartSchedue.Api.Services.Integration
{
private readonly IIntegrationRecordRepository _integrationRecordRepository;
private readonly WorkOrderRepository _workOrderRepository;
+ private readonly EquipmentRepository _equipmentRepository;
private readonly ILogger _logger;
public IntegrationRecordService(
IIntegrationRecordRepository integrationRecordRepository,
WorkOrderRepository workOrderRepository,
+ EquipmentRepository equipmentRepository,
ILogger logger)
{
_integrationRecordRepository = integrationRecordRepository;
_workOrderRepository = workOrderRepository;
+ _equipmentRepository = equipmentRepository;
_logger = logger;
}
@@ -922,6 +926,112 @@ namespace NPP.SmartSchedue.Api.Services.Integration
#endregion
+ #region 任务修改接口实现
+
+ ///
+ /// 修改整合记录中的任务分配
+ /// 根据选中的人员修改任务的指定人员ID和姓名,并更新整合记录
+ ///
+ [HttpPost]
+ public async Task ModifyIntegrationRecordTaskAsync(IntegrationRecordTaskModifyInput input)
+ {
+ try
+ {
+ _logger.LogInformation("开始修改整合记录任务: RecordId={RecordId}, ModificationsCount={Count}",
+ input.IntegrationRecordId, input.TaskModifications.Count);
+
+ // 获取整合记录
+ var integrationRecord = await _integrationRecordRepository.GetAsync(input.IntegrationRecordId);
+ if (integrationRecord == null)
+ {
+ _logger.LogWarning("未找到整合记录: RecordId={RecordId}", input.IntegrationRecordId);
+ return false;
+ }
+
+ // 获取需要修改的任务
+ var taskIds = input.TaskModifications.Select(m => m.TaskId).ToList();
+ var tasks = await _workOrderRepository.Select
+ .Where(t => taskIds.Contains(t.Id))
+ .ToListAsync();
+
+ if (!tasks.Any())
+ {
+ _logger.LogWarning("未找到需要修改的任务: RecordId={RecordId}", input.IntegrationRecordId);
+ return false;
+ }
+
+ // 执行任务修改
+ foreach (var modification in input.TaskModifications)
+ {
+ var task = tasks.FirstOrDefault(t => t.Id == modification.TaskId);
+ if (task == null) continue;
+
+ _logger.LogInformation("修改任务分配: TaskId={TaskId}, TaskCode={TaskCode}",
+ modification.TaskId, modification.TaskCode ?? "N/A");
+
+ // 修改人员分配
+ if (modification.NewPersonnelId.HasValue)
+ {
+ task.AssignedPersonnelId = modification.NewPersonnelId.Value;
+ task.AssignedPersonnelName = modification.NewPersonnelName ?? string.Empty;
+ _logger.LogInformation(" 更新人员分配: PersonnelId={PersonnelId}, PersonnelName={PersonnelName}",
+ modification.NewPersonnelId.Value, modification.NewPersonnelName ?? "N/A");
+ }
+
+ // 修改设备分配
+ if (modification.NewEquipmentId.HasValue)
+ {
+ task.AssignedEquipmentId = modification.NewEquipmentId.Value;
+ task.AssignedEquipmentName = modification.NewEquipmentName ?? string.Empty;
+ _logger.LogInformation(" 更新设备分配: EquipmentId={EquipmentId}, EquipmentName={EquipmentName}",
+ modification.NewEquipmentId.Value, modification.NewEquipmentName ?? "N/A");
+ }
+
+ // 更新任务备注
+ if (!string.IsNullOrWhiteSpace(modification.ModificationNote))
+ {
+ task.Remarks = string.IsNullOrWhiteSpace(task.Remarks)
+ ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modification.ModificationNote}"
+ : $"{task.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modification.ModificationNote}";
+ }
+
+ task.ModifiedTime = DateTime.Now;
+ task.ModifiedUserId = input.OperatorUserId;
+ task.ModifiedUserName = input.OperatorUserName;
+ }
+
+ // 批量更新任务
+ await _workOrderRepository.UpdateAsync(tasks);
+
+ // 更新整合记录备注
+ if (!string.IsNullOrWhiteSpace(input.ModificationReason))
+ {
+ integrationRecord.Remarks = string.IsNullOrWhiteSpace(integrationRecord.Remarks)
+ ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {input.ModificationReason}"
+ : $"{integrationRecord.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {input.ModificationReason}";
+
+ integrationRecord.ModifiedTime = DateTime.Now;
+ integrationRecord.ModifiedUserId = input.OperatorUserId;
+ integrationRecord.ModifiedUserName = input.OperatorUserName;
+
+ await _integrationRecordRepository.UpdateAsync(integrationRecord);
+ }
+
+ _logger.LogInformation("整合记录任务修改完成: RecordId={RecordId}, SuccessCount={Count}",
+ input.IntegrationRecordId, input.TaskModifications.Count);
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "修改整合记录任务失败: RecordId={RecordId}, Error={Message}",
+ input.IntegrationRecordId, ex.Message);
+ return false;
+ }
+ }
+
+ #endregion
+
#region 通知管理接口实现(预留)
///
@@ -929,10 +1039,10 @@ namespace NPP.SmartSchedue.Api.Services.Integration
/// 深度业务思考:为未来的通知系统集成预留完整的接口结构
///
[HttpPost]
- public async Task SendPublishNotificationAsync(long recordId, string notificationType, List targetPersonnelIds, string customMessage = "")
+ public async Task SendPublishNotificationAsync(long recordId, string notificationType, List targetPersonnelIds, string customMessage = "")
{
// 预留实现:当前返回模拟结果,未来集成真实通知服务
- var result = new NotificationSendResult
+ var result = new IntegrationNotificationResult
{
NotificationType = notificationType,
IsSent = true, // 预留:假设发送成功
@@ -1056,6 +1166,11 @@ namespace NPP.SmartSchedue.Api.Services.Integration
///
/// 执行任务发布
+ /// 业务思考:
+ /// 1. 批量处理任务发布,但每个任务独立处理避免相互影响
+ /// 2. 详细记录发布过程和结果,便于问题排查和审计
+ /// 3. 支持部分成功的发布场景,返回成功和失败的详细信息
+ /// 4. 在发布失败时提供具体的失败原因和建议
///
private async Task<(List PublishedTasks, List FailedPublishTasks)> ExecuteTaskPublishAsync(
List tasks,
@@ -1065,18 +1180,29 @@ namespace NPP.SmartSchedue.Api.Services.Integration
var publishedTasks = new List();
var failedTasks = new List();
+ _logger.LogInformation("开始执行任务批量发布: RecordId={RecordId}, TaskCount={Count}, AutoStart={AutoStart}",
+ integrationRecord.Id, tasks.Count, input.AutoStartTasksAfterPublish);
+
foreach (var task in tasks)
{
try
{
// 记录发布前状态
- var previousStatus = task.Status.ToString();
+ var previousStatus = (WorkOrderStatusEnum)task.Status;
+ var previousStatusName = previousStatus.ToString();
+
+ _logger.LogDebug("开始发布任务: TaskId={TaskId}, TaskCode={Code}, CurrentStatus={Status}",
+ task.Id, task.WorkOrderCode, previousStatusName);
// 执行任务发布逻辑
var publishSuccess = await PublishSingleTaskAsync(task, integrationRecord, input);
if (publishSuccess)
{
+ // 重新获取更新后的状态
+ var currentStatus = (WorkOrderStatusEnum)task.Status;
+ var currentStatusName = currentStatus.ToString();
+
publishedTasks.Add(new PublishedTaskDetail
{
TaskId = task.Id,
@@ -1085,20 +1211,44 @@ namespace NPP.SmartSchedue.Api.Services.Integration
AssignedPersonnelName = task.AssignedPersonnelName ?? "",
AssignedEquipmentId = task.AssignedEquipmentId,
AssignedEquipmentName = task.AssignedEquipmentName ?? "",
- PreviousStatus = previousStatus,
- CurrentStatus = task.Status.ToString(),
+ PreviousStatus = previousStatusName,
+ CurrentStatus = currentStatusName,
PublishTime = DateTime.Now
});
+
+ _logger.LogInformation("任务发布成功: TaskId={TaskId}, StatusChange={Previous}->{Current}",
+ task.Id, previousStatusName, currentStatusName);
}
else
{
+ // 分析发布失败的具体原因
+ var failureReasons = new List();
+
+ if (previousStatus != WorkOrderStatusEnum.PendingIntegration)
+ {
+ failureReasons.Add($"任务状态不正确,当前状态:{previousStatusName},期望状态:PendingIntegration");
+ }
+
+ if (task.AssignedPersonnelId == null || task.AssignedPersonnelId <= 0)
+ {
+ failureReasons.Add("任务未分配人员");
+ }
+
+ if (string.IsNullOrEmpty(task.WorkOrderCode))
+ {
+ failureReasons.Add("任务编号为空");
+ }
+
failedTasks.Add(new FailedPublishTaskDetail
{
TaskId = task.Id,
TaskCode = task.WorkOrderCode ?? $"TASK-{task.Id}",
FailureReason = "任务发布失败",
- DetailedErrors = new List { "发布过程中遇到业务规则限制" }
+ DetailedErrors = failureReasons.Any() ? failureReasons : new List { "发布过程中遇到未知问题" }
});
+
+ _logger.LogWarning("任务发布失败: TaskId={TaskId}, Reasons={Reasons}",
+ task.Id, string.Join("; ", failureReasons));
}
}
catch (Exception ex)
@@ -1108,36 +1258,95 @@ namespace NPP.SmartSchedue.Api.Services.Integration
TaskId = task.Id,
TaskCode = task.WorkOrderCode ?? $"TASK-{task.Id}",
FailureReason = "发布异常",
- DetailedErrors = new List { ex.Message }
+ DetailedErrors = new List { ex.Message, ex.StackTrace ?? "" }
});
- _logger.LogError(ex, "任务发布失败: TaskId={TaskId}, Error={Message}", task.Id, ex.Message);
+ _logger.LogError(ex, "任务发布异常: TaskId={TaskId}, TaskCode={Code}, Error={Message}",
+ task.Id, task.WorkOrderCode, ex.Message);
}
}
+ _logger.LogInformation("任务批量发布完成: RecordId={RecordId}, Success={Success}, Failed={Failed}",
+ integrationRecord.Id, publishedTasks.Count, failedTasks.Count);
+
return (publishedTasks, failedTasks);
}
///
/// 发布单个任务
+ /// 深度业务思考:
+ /// 1. 验证任务状态转换的合法性(只有PendingIntegration状态的任务可以发布)
+ /// 2. 支持AutoStartTasksAfterPublish选项,发布后可直接开始任务
+ /// 3. 完整的异常处理和日志记录
+ /// 4. 原子性操作确保数据一致性
///
private async Task PublishSingleTaskAsync(WorkOrderEntity task, IntegrationRecordEntity integrationRecord, PublishIntegrationRecordInput input)
{
try
{
- // 更新任务状态为已分配(发布后的任务状态)
- task.Status = (int)WorkOrderStatusEnum.Assigned;
+ // 验证任务当前状态是否可以发布
+ var currentStatus = (WorkOrderStatusEnum)task.Status;
+ if (currentStatus != WorkOrderStatusEnum.PendingIntegration)
+ {
+ _logger.LogWarning("任务状态不允许发布: TaskId={TaskId}, CurrentStatus={Status}",
+ task.Id, currentStatus);
+ return false;
+ }
+
+ // 确定发布后的目标状态
+ WorkOrderStatusEnum targetStatus;
+ if (input.AutoStartTasksAfterPublish)
+ {
+ // 自动开始任务:PendingIntegration -> Assigned -> InProgress
+ targetStatus = WorkOrderStatusEnum.InProgress;
+ _logger.LogInformation("任务将发布并自动开始: TaskId={TaskId}", task.Id);
+ }
+ else
+ {
+ // 常规发布:PendingIntegration -> Assigned
+ targetStatus = WorkOrderStatusEnum.Assigned;
+ _logger.LogInformation("任务将发布为已分配状态: TaskId={TaskId}", task.Id);
+ }
+
+ // 更新任务状态和相关字段
+ task.Status = (int)targetStatus;
task.ModifiedTime = DateTime.Now;
task.ModifiedUserId = input.PublishedByUserId;
task.ModifiedUserName = input.PublishedByUserName;
+
+ // 如果是自动开始任务,设置开始时间
+ if (input.AutoStartTasksAfterPublish)
+ {
+ task.ActualStartTime = DateTime.Now;
+ }
- // 保存任务更新
+ // 设置发布时间(如果是定时发布)
+ if (input.ScheduledPublishTime.HasValue)
+ {
+ task.PlannedStartTime = input.ScheduledPublishTime.Value;
+ }
+
+ // 保存任务更新(原子操作)
await _workOrderRepository.UpdateAsync(task);
+ // 验证更新结果
+ var updatedTask = await _workOrderRepository.GetAsync(task.Id);
+ if (updatedTask == null || updatedTask.Status != (int)targetStatus)
+ {
+ _logger.LogError("任务状态更新验证失败: TaskId={TaskId}, ExpectedStatus={Expected}, ActualStatus={Actual}",
+ task.Id, targetStatus, updatedTask?.Status);
+ return false;
+ }
+
+ _logger.LogInformation("任务发布成功: TaskId={TaskId}, OldStatus={OldStatus}, NewStatus={NewStatus}",
+ task.Id, currentStatus, targetStatus);
+
return true;
}
- catch
+ catch (Exception ex)
{
+ _logger.LogError(ex, "任务发布失败: TaskId={TaskId}, IntegrationRecordId={RecordId}, Error={Message}",
+ task.Id, integrationRecord.Id, ex.Message);
return false;
}
}
@@ -1164,9 +1373,9 @@ namespace NPP.SmartSchedue.Api.Services.Integration
///
/// 发送发布通知
///
- private async Task> SendPublishNotificationsAsync(IntegrationRecordEntity integrationRecord, PublishIntegrationRecordInput input)
+ private async Task> SendPublishNotificationsAsync(IntegrationRecordEntity integrationRecord, PublishIntegrationRecordInput input)
{
- var results = new List();
+ var results = new List();
// 预留:根据发布规则确定通知对象和内容
var targetPersonnelIds = new List(); // 从业务规则中获取
diff --git a/NPP.SmartSchedue.Api/Services/Integration/SmartScheduleOrchestratorService.cs b/NPP.SmartSchedue.Api/Services/Integration/SmartScheduleOrchestratorService.cs
index 3a0ddd3..f3652df 100644
--- a/NPP.SmartSchedue.Api/Services/Integration/SmartScheduleOrchestratorService.cs
+++ b/NPP.SmartSchedue.Api/Services/Integration/SmartScheduleOrchestratorService.cs
@@ -1057,6 +1057,17 @@ namespace NPP.SmartSchedue.Api.Services.Integration
return await _integrationRecordService.PublishIntegrationRecordAsync(input);
}
+ ///
+ /// 修改整合记录中的任务分配
+ /// 根据选中的人员修改任务的指定人员ID和姓名,并更新整合记录
+ ///
+ /// 任务修改输入参数
+ /// 修改结果
+ [HttpPost]
+ public async Task ModifyIntegrationRecordTaskAsync(IntegrationRecordTaskModifyInput input)
+ {
+ return await _integrationRecordService.ModifyIntegrationRecordTaskAsync(input);
+ }
///
/// 转换全局分配结果为人员分配结果
diff --git a/NPP.SmartSchedue.Api/Services/Integration/TaskAssignmentModificationService.cs b/NPP.SmartSchedue.Api/Services/Integration/TaskAssignmentModificationService.cs
new file mode 100644
index 0000000..59317e9
--- /dev/null
+++ b/NPP.SmartSchedue.Api/Services/Integration/TaskAssignmentModificationService.cs
@@ -0,0 +1,1123 @@
+using FreeSql;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using NPP.SmartSchedue.Api.Contracts.Domain.Integration;
+using NPP.SmartSchedue.Api.Contracts.Domain.Work;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output;
+using NPP.SmartSchedue.Api.Repositories.Work;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using ZhonTai.Admin.Services;
+using ZhonTai.DynamicApi;
+using ZhonTai.DynamicApi.Attributes;
+
+namespace NPP.SmartSchedue.Api.Services.Integration
+{
+ ///
+ /// 任务分配修改服务
+ /// 专门处理任务人员分配的修改操作
+ /// 深度业务思考:实现分配结果的精确修改,确保数据一致性和业务完整性
+ ///
+ [DynamicApi(Area = "app")]
+ public class TaskAssignmentModificationService : BaseService, IDynamicApi
+ {
+ private readonly IIntegrationRecordRepository _integrationRecordRepository;
+ private readonly WorkOrderRepository _workOrderRepository;
+ private readonly ILogger _logger;
+
+ public TaskAssignmentModificationService(
+ IIntegrationRecordRepository integrationRecordRepository,
+ WorkOrderRepository workOrderRepository,
+ ILogger logger)
+ {
+ _integrationRecordRepository = integrationRecordRepository;
+ _workOrderRepository = workOrderRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 修改任务人员分配
+ /// 深度业务思考:根据选中的人员精确修改任务分配,同时保持整合记录的一致性
+ /// 核心功能:
+ /// 1. 根据选中的人员修改任务的指定人员ID和姓名
+ /// 2. 更新整合记录的修改状态和备注
+ /// 3. 记录修改历史和审计信息
+ ///
+ [HttpPost]
+ public async Task ModifyTaskAssignmentAsync(TaskAssignmentModificationInput input)
+ {
+ var result = new TaskAssignmentModificationResult
+ {
+ StartTime = DateTime.Now,
+ IsSuccess = false
+ };
+
+ try
+ {
+ _logger.LogInformation("开始修改任务人员分配: IntegrationRecordId={IntegrationRecordId}, TaskCount={TaskCount}",
+ input.IntegrationRecordId, input.TaskAssignmentModifications.Count);
+
+ // 第一步:验证整合记录存在性
+ var integrationRecord = await ValidateIntegrationRecordAsync(input.IntegrationRecordId);
+ if (integrationRecord == null)
+ {
+ result.ErrorMessage = $"未找到ID为{input.IntegrationRecordId}的整合记录";
+ return result;
+ }
+
+ result.IntegrationBatchCode = integrationRecord.IntegrationBatchCode;
+
+ // 第二步:获取和验证任务
+ var validationResult = await ValidateAndGetTasksAsync(input.TaskAssignmentModifications);
+ if (!validationResult.IsValid)
+ {
+ result.ErrorMessage = validationResult.ErrorMessage;
+ result.ValidationErrors = validationResult.ValidationErrors;
+ return result;
+ }
+
+ var tasks = validationResult.Tasks;
+ result.TotalTaskCount = tasks.Count;
+
+ // 第三步:执行任务分配修改
+ var modificationResults = await ExecuteTaskAssignmentModificationsAsync(
+ tasks,
+ input.TaskAssignmentModifications,
+ input.OperatorUserId,
+ input.OperatorUserName);
+
+ result.SuccessfulModifications = modificationResults.SuccessfulModifications;
+ result.FailedModifications = modificationResults.FailedModifications;
+ result.SuccessfulCount = modificationResults.SuccessfulModifications.Count;
+ result.FailedCount = modificationResults.FailedModifications.Count;
+
+ // 第四步:更新整合记录
+ await UpdateIntegrationRecordAsync(integrationRecord, input, result);
+
+ // 第五步:生成修改摘要
+ result.ModificationSummary = GenerateModificationSummary(result);
+ result.IsSuccess = result.FailedCount == 0;
+ result.CompletedTime = DateTime.Now;
+ result.ElapsedMilliseconds = (int)(result.CompletedTime - result.StartTime).TotalMilliseconds;
+
+ _logger.LogInformation("任务人员分配修改完成: Success={IsSuccess}, SuccessCount={SuccessCount}, FailedCount={FailedCount}",
+ result.IsSuccess, result.SuccessfulCount, result.FailedCount);
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ result.IsSuccess = false;
+ result.ErrorMessage = $"修改任务人员分配时发生异常: {ex.Message}";
+ result.CompletedTime = DateTime.Now;
+ result.ElapsedMilliseconds = (int)(result.CompletedTime - result.StartTime).TotalMilliseconds;
+
+ _logger.LogError(ex, "修改任务人员分配失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ input.IntegrationRecordId, ex.Message);
+
+ return result;
+ }
+ }
+
+ ///
+ /// 批量修改任务设备分配
+ /// 深度业务思考:支持批量设备分配修改,提高操作效率
+ ///
+ [HttpPost]
+ public async Task ModifyTaskEquipmentAssignmentAsync(TaskEquipmentAssignmentModificationInput input)
+ {
+ var result = new TaskAssignmentModificationResult
+ {
+ StartTime = DateTime.Now,
+ IsSuccess = false
+ };
+
+ try
+ {
+ _logger.LogInformation("开始修改任务设备分配: IntegrationRecordId={IntegrationRecordId}, TaskCount={TaskCount}",
+ input.IntegrationRecordId, input.TaskEquipmentModifications.Count);
+
+ // 验证整合记录
+ var integrationRecord = await ValidateIntegrationRecordAsync(input.IntegrationRecordId);
+ if (integrationRecord == null)
+ {
+ result.ErrorMessage = $"未找到ID为{input.IntegrationRecordId}的整合记录";
+ return result;
+ }
+
+ result.IntegrationBatchCode = integrationRecord.IntegrationBatchCode;
+
+ // 获取任务并执行设备分配修改
+ var taskIds = input.TaskEquipmentModifications.Select(m => m.TaskId).ToList();
+ var tasks = await GetTasksByIdsAsync(taskIds);
+
+ result.TotalTaskCount = tasks.Count;
+ var successfulModifications = new List();
+ var failedModifications = new List();
+
+ foreach (var modification in input.TaskEquipmentModifications)
+ {
+ var task = tasks.FirstOrDefault(t => t.Id == modification.TaskId);
+ if (task == null)
+ {
+ failedModifications.Add(new TaskModificationFailure
+ {
+ TaskId = modification.TaskId,
+ TaskCode = modification.TaskCode ?? "未知",
+ FailureReason = "任务不存在",
+ ErrorDetails = "未找到指定的任务"
+ });
+ continue;
+ }
+
+ try
+ {
+ // 记录修改前的值
+ var previousEquipmentId = task.AssignedEquipmentId;
+ var previousEquipmentName = task.AssignedEquipmentName;
+
+ // 执行设备分配修改
+ task.AssignedEquipmentId = modification.NewEquipmentId;
+ task.AssignedEquipmentName = modification.NewEquipmentName ?? string.Empty;
+
+ // 添加修改备注
+ if (!string.IsNullOrWhiteSpace(modification.ModificationNote))
+ {
+ task.Remarks = string.IsNullOrWhiteSpace(task.Remarks)
+ ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modification.ModificationNote}"
+ : $"{task.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modification.ModificationNote}";
+ }
+
+ task.ModifiedTime = DateTime.Now;
+ task.ModifiedUserId = input.OperatorUserId;
+ task.ModifiedUserName = input.OperatorUserName;
+
+ successfulModifications.Add(new TaskModificationDetail
+ {
+ TaskId = task.Id,
+ TaskCode = task.WorkOrderCode ?? task.Code ?? $"TASK-{task.Id}",
+ ModificationType = "设备分配修改",
+ PreviousValue = $"设备ID:{previousEquipmentId}, 设备名:{previousEquipmentName}",
+ NewValue = $"设备ID:{task.AssignedEquipmentId}, 设备名:{task.AssignedEquipmentName}",
+ ModificationTime = DateTime.Now,
+ ModificationNote = modification.ModificationNote ?? ""
+ });
+ }
+ catch (Exception ex)
+ {
+ failedModifications.Add(new TaskModificationFailure
+ {
+ TaskId = modification.TaskId,
+ TaskCode = modification.TaskCode ?? "未知",
+ FailureReason = "修改异常",
+ ErrorDetails = ex.Message
+ });
+ }
+ }
+
+ // 批量更新任务
+ if (successfulModifications.Any())
+ {
+ var modifiedTasks = tasks.Where(t => successfulModifications.Any(s => s.TaskId == t.Id)).ToList();
+ await _workOrderRepository.UpdateAsync(modifiedTasks);
+ }
+
+ result.SuccessfulModifications = successfulModifications;
+ result.FailedModifications = failedModifications;
+ result.SuccessfulCount = successfulModifications.Count;
+ result.FailedCount = failedModifications.Count;
+
+ // 更新整合记录
+ await UpdateIntegrationRecordForEquipmentModificationAsync(integrationRecord, input, result);
+
+ result.ModificationSummary = $"设备分配修改完成:成功{result.SuccessfulCount}个,失败{result.FailedCount}个";
+ result.IsSuccess = result.FailedCount == 0;
+ result.CompletedTime = DateTime.Now;
+ result.ElapsedMilliseconds = (int)(result.CompletedTime - result.StartTime).TotalMilliseconds;
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ result.IsSuccess = false;
+ result.ErrorMessage = $"修改任务设备分配时发生异常: {ex.Message}";
+ result.CompletedTime = DateTime.Now;
+ result.ElapsedMilliseconds = (int)(result.CompletedTime - result.StartTime).TotalMilliseconds;
+
+ _logger.LogError(ex, "修改任务设备分配失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ input.IntegrationRecordId, ex.Message);
+
+ return result;
+ }
+ }
+
+ ///
+ /// 查询任务分配修改历史
+ /// 深度业务思考:提供完整的修改历史追踪,支持审计和分析
+ ///
+ [HttpGet]
+ public async Task> GetTaskAssignmentModificationHistoryAsync(long integrationRecordId)
+ {
+ try
+ {
+ // 查询整合记录
+ var integrationRecord = await _integrationRecordRepository.GetAsync(integrationRecordId);
+ if (integrationRecord == null)
+ {
+ return new List();
+ }
+
+ // 获取相关任务的修改历史
+ var taskIds = integrationRecord.GetTaskIds();
+ if (!taskIds.Any())
+ {
+ return new List();
+ }
+
+ var tasks = await _workOrderRepository.Select
+ .Where(t => taskIds.Contains(t.Id))
+ .Where(t => !string.IsNullOrEmpty(t.Remarks) && t.Remarks.Contains("人员分配修改"))
+ .OrderByDescending(t => t.ModifiedTime)
+ .ToListAsync();
+
+ // 解析修改历史
+ var histories = new List();
+ foreach (var task in tasks)
+ {
+ var history = ParseModificationHistory(task);
+ if (history != null)
+ {
+ histories.Add(history);
+ }
+ }
+
+ return histories;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "查询任务分配修改历史失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ integrationRecordId, ex.Message);
+ return new List();
+ }
+ }
+
+ #region 私有辅助方法
+
+ ///
+ /// 验证整合记录存在性
+ ///
+ private async Task ValidateIntegrationRecordAsync(long integrationRecordId)
+ {
+ return await _integrationRecordRepository.GetAsync(integrationRecordId);
+ }
+
+ ///
+ /// 验证和获取任务
+ ///
+ private async Task ValidateAndGetTasksAsync(List modifications)
+ {
+ var result = new TaskValidationResult { IsValid = true };
+
+ if (!modifications.Any())
+ {
+ result.IsValid = false;
+ result.ErrorMessage = "没有提供需要修改的任务";
+ return result;
+ }
+
+ var taskIds = modifications.Select(m => m.TaskId).ToList();
+ var tasks = await GetTasksByIdsAsync(taskIds);
+
+ if (tasks.Count != taskIds.Count)
+ {
+ var foundTaskIds = tasks.Select(t => t.Id).ToList();
+ var missingTaskIds = taskIds.Except(foundTaskIds).ToList();
+ result.IsValid = false;
+ result.ErrorMessage = $"未找到以下任务: {string.Join(", ", missingTaskIds)}";
+ result.ValidationErrors = missingTaskIds.Select(id => $"任务ID {id} 不存在").ToList();
+ return result;
+ }
+
+ result.Tasks = tasks;
+ return result;
+ }
+
+ ///
+ /// 根据任务ID列表获取任务
+ ///
+ private async Task> GetTasksByIdsAsync(List taskIds)
+ {
+ return await _workOrderRepository.Select
+ .Where(t => taskIds.Contains(t.Id))
+ .ToListAsync();
+ }
+
+ ///
+ /// 执行任务分配修改
+ ///
+ private async Task ExecuteTaskAssignmentModificationsAsync(
+ List tasks,
+ List modifications,
+ long operatorUserId,
+ string operatorUserName)
+ {
+ var result = new TaskModificationExecutionResult();
+
+ foreach (var modification in modifications)
+ {
+ var task = tasks.FirstOrDefault(t => t.Id == modification.TaskId);
+ if (task == null) continue;
+
+ try
+ {
+ _logger.LogInformation("修改任务人员分配: TaskId={TaskId}, TaskCode={TaskCode}, NewPersonnelId={PersonnelId}",
+ modification.TaskId, task.WorkOrderCode ?? task.Code, modification.NewPersonnelId);
+
+ // 记录修改前的值
+ var previousPersonnelId = task.AssignedPersonnelId;
+ var previousPersonnelName = task.AssignedPersonnelName;
+
+ // 执行人员分配修改
+ task.AssignedPersonnelId = modification.NewPersonnelId;
+ task.AssignedPersonnelName = modification.NewPersonnelName ?? string.Empty;
+
+ // 如果同时修改设备分配
+ var previousEquipmentId = task.AssignedEquipmentId;
+ var previousEquipmentName = task.AssignedEquipmentName;
+
+ if (modification.NewEquipmentId.HasValue)
+ {
+ task.AssignedEquipmentId = modification.NewEquipmentId;
+ task.AssignedEquipmentName = modification.NewEquipmentName ?? string.Empty;
+ }
+
+ // 更新任务备注
+ var modificationNote = $"人员分配修改:{previousPersonnelName}({previousPersonnelId}) → {task.AssignedPersonnelName}({task.AssignedPersonnelId})";
+
+ if (modification.NewEquipmentId.HasValue)
+ {
+ modificationNote += $";设备分配修改:{previousEquipmentName}({previousEquipmentId}) → {task.AssignedEquipmentName}({task.AssignedEquipmentId})";
+ }
+
+ if (!string.IsNullOrWhiteSpace(modification.ModificationNote))
+ {
+ modificationNote += $";备注:{modification.ModificationNote}";
+ }
+
+ task.Remarks = string.IsNullOrWhiteSpace(task.Remarks)
+ ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modificationNote}"
+ : $"{task.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modificationNote}";
+
+ task.ModifiedTime = DateTime.Now;
+ task.ModifiedUserId = operatorUserId;
+ task.ModifiedUserName = operatorUserName;
+
+ result.SuccessfulModifications.Add(new TaskModificationDetail
+ {
+ TaskId = task.Id,
+ TaskCode = task.WorkOrderCode ?? task.Code ?? $"TASK-{task.Id}",
+ ModificationType = "人员分配修改",
+ PreviousValue = $"人员ID:{previousPersonnelId}, 人员名:{previousPersonnelName}",
+ NewValue = $"人员ID:{task.AssignedPersonnelId}, 人员名:{task.AssignedPersonnelName}",
+ ModificationTime = DateTime.Now,
+ ModificationNote = modification.ModificationNote ?? ""
+ });
+ }
+ catch (Exception ex)
+ {
+ result.FailedModifications.Add(new TaskModificationFailure
+ {
+ TaskId = modification.TaskId,
+ TaskCode = task.WorkOrderCode ?? task.Code ?? $"TASK-{task.Id}",
+ FailureReason = "修改异常",
+ ErrorDetails = ex.Message
+ });
+
+ _logger.LogError(ex, "修改任务人员分配失败: TaskId={TaskId}, Error={Message}",
+ modification.TaskId, ex.Message);
+ }
+ }
+
+ // 批量更新成功修改的任务
+ if (result.SuccessfulModifications.Any())
+ {
+ var modifiedTasks = tasks.Where(t => result.SuccessfulModifications.Any(s => s.TaskId == t.Id)).ToList();
+ await _workOrderRepository.UpdateAsync(modifiedTasks);
+ }
+
+ return result;
+ }
+
+ ///
+ /// 更新整合记录
+ ///
+ private async Task UpdateIntegrationRecordAsync(
+ IntegrationRecordEntity integrationRecord,
+ TaskAssignmentModificationInput input,
+ TaskAssignmentModificationResult result)
+ {
+ try
+ {
+ // 更新整合记录的修改信息
+ var modificationSummary = $"任务人员分配修改:成功{result.SuccessfulCount}个,失败{result.FailedCount}个";
+
+ if (!string.IsNullOrWhiteSpace(input.ModificationReason))
+ {
+ modificationSummary = $"{input.ModificationReason} - {modificationSummary}";
+ }
+
+ integrationRecord.Remarks = string.IsNullOrWhiteSpace(integrationRecord.Remarks)
+ ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modificationSummary}"
+ : $"{integrationRecord.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modificationSummary}";
+
+ integrationRecord.ModifiedTime = DateTime.Now;
+ integrationRecord.ModifiedUserId = input.OperatorUserId;
+ integrationRecord.ModifiedUserName = input.OperatorUserName;
+
+ // 更新人员分配结果中的successfulMatches
+ if (result.SuccessfulModifications.Any())
+ {
+ try
+ {
+ // 反序列化现有的人员分配结果
+ if (!string.IsNullOrEmpty(integrationRecord.PersonnelAllocationResultJson))
+ {
+ var personnelAllocationResult = JsonSerializer.Deserialize(
+ integrationRecord.PersonnelAllocationResultJson,
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+
+ if (personnelAllocationResult?.SuccessfulMatches != null)
+ {
+ // 更新successfulMatches中对应的匹配信息
+ foreach (var modification in result.SuccessfulModifications)
+ {
+ var existingMatch = personnelAllocationResult.SuccessfulMatches
+ .FirstOrDefault(m => m.TaskId == modification.TaskId);
+
+ if (existingMatch != null)
+ {
+ // 更新人员信息
+ existingMatch.PersonnelId = modification.NewValue.Contains("人员ID:")
+ ? long.Parse(modification.NewValue.Split(',')[0].Split(':')[1])
+ : existingMatch.PersonnelId;
+
+ existingMatch.PersonnelName = modification.NewValue.Contains("人员名:")
+ ? modification.NewValue.Split(',')[1].Split(':')[1]
+ : existingMatch.PersonnelName;
+ }
+ }
+
+ // 重新序列化并更新
+ integrationRecord.PersonnelAllocationResultJson = JsonSerializer.Serialize(
+ personnelAllocationResult,
+ new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ });
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "更新人员分配结果失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ integrationRecord.Id, ex.Message);
+ }
+
+ // 更新设备分配结果中的successfulMatches(如果同时修改了设备)
+ try
+ {
+ // 反序列化现有的设备分配结果
+ if (!string.IsNullOrEmpty(integrationRecord.EquipmentAllocationResultJson))
+ {
+ var equipmentAllocationResult = JsonSerializer.Deserialize(
+ integrationRecord.EquipmentAllocationResultJson,
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+
+ if (equipmentAllocationResult?.SuccessfulMatches != null)
+ {
+ // 更新successfulMatches中对应的匹配信息
+ foreach (var modification in result.SuccessfulModifications)
+ {
+ // 检查修改备注中是否包含设备信息
+ if (modification.NewValue.Contains("设备分配修改"))
+ {
+ var existingMatch = equipmentAllocationResult.SuccessfulMatches
+ .FirstOrDefault(m => m.TaskId == modification.TaskId);
+
+ if (existingMatch != null)
+ {
+ // 从备注中提取设备信息
+ var equipmentInfo = ExtractEquipmentInfoFromModification(modification.NewValue);
+ if (equipmentInfo.HasValue)
+ {
+ existingMatch.EquipmentId = equipmentInfo.Value.equipmentId;
+ existingMatch.EquipmentName = equipmentInfo.Value.equipmentName;
+ }
+ }
+ }
+ }
+
+ // 重新序列化并更新
+ integrationRecord.EquipmentAllocationResultJson = JsonSerializer.Serialize(
+ equipmentAllocationResult,
+ new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ });
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "更新设备分配结果失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ integrationRecord.Id, ex.Message);
+ }
+ }
+
+ await _integrationRecordRepository.UpdateAsync(integrationRecord);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新整合记录失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ integrationRecord.Id, ex.Message);
+ }
+ }
+
+ ///
+ /// 从修改备注中提取设备信息
+ ///
+ private (long equipmentId, string equipmentName)? ExtractEquipmentInfoFromModification(string modificationValue)
+ {
+ try
+ {
+ // 解析类似 "设备分配修改:设备ID:1001, 设备名:设备A" 的字符串
+ if (modificationValue.Contains("设备ID:") && modificationValue.Contains("设备名:"))
+ {
+ var equipmentIdStr = modificationValue.Split(new[] { "设备ID:", "," }, StringSplitOptions.None)[1];
+ var equipmentNameStr = modificationValue.Split(new[] { "设备名:" }, StringSplitOptions.None)[1];
+
+ if (long.TryParse(equipmentIdStr, out long equipmentId))
+ {
+ return (equipmentId, equipmentNameStr);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "解析设备信息失败: ModificationValue={ModificationValue}", modificationValue);
+ }
+
+ return null;
+ }
+
+ ///
+ /// 为设备修改更新整合记录
+ ///
+ private async Task UpdateIntegrationRecordForEquipmentModificationAsync(
+ IntegrationRecordEntity integrationRecord,
+ TaskEquipmentAssignmentModificationInput input,
+ TaskAssignmentModificationResult result)
+ {
+ try
+ {
+ var modificationSummary = $"任务设备分配修改:成功{result.SuccessfulCount}个,失败{result.FailedCount}个";
+
+ if (!string.IsNullOrWhiteSpace(input.ModificationReason))
+ {
+ modificationSummary = $"{input.ModificationReason} - {modificationSummary}";
+ }
+
+ integrationRecord.Remarks = string.IsNullOrWhiteSpace(integrationRecord.Remarks)
+ ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modificationSummary}"
+ : $"{integrationRecord.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modificationSummary}";
+
+ integrationRecord.ModifiedTime = DateTime.Now;
+ integrationRecord.ModifiedUserId = input.OperatorUserId;
+ integrationRecord.ModifiedUserName = input.OperatorUserName;
+
+ // 更新设备分配结果中的successfulMatches
+ if (result.SuccessfulModifications.Any())
+ {
+ try
+ {
+ // 反序列化现有的设备分配结果
+ if (!string.IsNullOrEmpty(integrationRecord.EquipmentAllocationResultJson))
+ {
+ var equipmentAllocationResult = JsonSerializer.Deserialize(
+ integrationRecord.EquipmentAllocationResultJson,
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+
+ if (equipmentAllocationResult?.SuccessfulMatches != null)
+ {
+ // 更新successfulMatches中对应的匹配信息
+ foreach (var modification in result.SuccessfulModifications)
+ {
+ var existingMatch = equipmentAllocationResult.SuccessfulMatches
+ .FirstOrDefault(m => m.TaskId == modification.TaskId);
+
+ if (existingMatch != null)
+ {
+ // 更新设备信息
+ existingMatch.EquipmentId = modification.NewValue.Contains("设备ID:")
+ ? long.Parse(modification.NewValue.Split(',')[0].Split(':')[1])
+ : existingMatch.EquipmentId;
+
+ existingMatch.EquipmentName = modification.NewValue.Contains("设备名:")
+ ? modification.NewValue.Split(',')[1].Split(':')[1]
+ : existingMatch.EquipmentName;
+ }
+ }
+
+ // 重新序列化并更新
+ integrationRecord.EquipmentAllocationResultJson = JsonSerializer.Serialize(
+ equipmentAllocationResult,
+ new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ });
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "更新设备分配结果失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ integrationRecord.Id, ex.Message);
+ }
+ }
+
+ await _integrationRecordRepository.UpdateAsync(integrationRecord);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新整合记录失败: IntegrationRecordId={IntegrationRecordId}, Error={Message}",
+ integrationRecord.Id, ex.Message);
+ }
+ }
+
+ ///
+ /// 生成修改摘要
+ ///
+ private string GenerateModificationSummary(TaskAssignmentModificationResult result)
+ {
+ var summary = $"任务分配修改完成:总计{result.TotalTaskCount}个任务,成功{result.SuccessfulCount}个,失败{result.FailedCount}个";
+
+ if (result.FailedCount > 0)
+ {
+ var failureReasons = result.FailedModifications
+ .GroupBy(f => f.FailureReason)
+ .Select(g => $"{g.Key}({g.Count()}个)")
+ .ToList();
+ summary += $"。失败原因:{string.Join("、", failureReasons)}";
+ }
+
+ summary += $"。耗时{result.ElapsedMilliseconds}毫秒";
+ return summary;
+ }
+
+ ///
+ /// 解析修改历史
+ ///
+ private TaskAssignmentModificationHistory ParseModificationHistory(WorkOrderEntity task)
+ {
+ try
+ {
+ // 简单的历史解析实现
+ return new TaskAssignmentModificationHistory
+ {
+ TaskId = task.Id,
+ TaskCode = task.WorkOrderCode ?? task.Code ?? $"TASK-{task.Id}",
+ ModificationTime = task.ModifiedTime ?? DateTime.Now,
+ ModificationDetails = task.Remarks ?? "",
+ ModifiedByUserId = task.ModifiedUserId,
+ ModifiedByUserName = task.ModifiedUserName ?? "",
+ CurrentPersonnelId = task.AssignedPersonnelId,
+ CurrentPersonnelName = task.AssignedPersonnelName ?? "",
+ CurrentEquipmentId = task.AssignedEquipmentId,
+ CurrentEquipmentName = task.AssignedEquipmentName ?? ""
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "解析任务修改历史失败: TaskId={TaskId}, Error={Message}", task.Id, ex.Message);
+ return null;
+ }
+ }
+
+ #endregion
+
+ #region 内部数据模型
+
+ ///
+ /// 任务验证结果
+ ///
+ private class TaskValidationResult
+ {
+ public bool IsValid { get; set; }
+ public string ErrorMessage { get; set; } = "";
+ public List ValidationErrors { get; set; } = new();
+ public List Tasks { get; set; } = new();
+ }
+
+ ///
+ /// 任务修改执行结果
+ ///
+ private class TaskModificationExecutionResult
+ {
+ public List SuccessfulModifications { get; set; } = new();
+ public List FailedModifications { get; set; } = new();
+ }
+
+ #endregion
+ }
+
+ #region 输入输出模型
+
+ ///
+ /// 任务人员分配修改输入
+ ///
+ public class TaskAssignmentModificationInput
+ {
+ ///
+ /// 整合记录ID
+ ///
+ public long IntegrationRecordId { get; set; }
+
+ ///
+ /// 操作员用户ID
+ ///
+ public long OperatorUserId { get; set; }
+
+ ///
+ /// 操作员用户名
+ ///
+ public string OperatorUserName { get; set; } = string.Empty;
+
+ ///
+ /// 任务分配修改列表
+ ///
+ public List TaskAssignmentModifications { get; set; } = new();
+
+ ///
+ /// 修改原因说明
+ ///
+ public string ModificationReason { get; set; } = string.Empty;
+
+ ///
+ /// 是否强制执行(忽略部分警告)
+ ///
+ public bool ForceExecute { get; set; } = false;
+ }
+
+ ///
+ /// 任务设备分配修改输入
+ ///
+ public class TaskEquipmentAssignmentModificationInput
+ {
+ ///
+ /// 整合记录ID
+ ///
+ public long IntegrationRecordId { get; set; }
+
+ ///
+ /// 操作员用户ID
+ ///
+ public long OperatorUserId { get; set; }
+
+ ///
+ /// 操作员用户名
+ ///
+ public string OperatorUserName { get; set; } = string.Empty;
+
+ ///
+ /// 任务设备分配修改列表
+ ///
+ public List TaskEquipmentModifications { get; set; } = new();
+
+ ///
+ /// 修改原因说明
+ ///
+ public string ModificationReason { get; set; } = string.Empty;
+
+ ///
+ /// 是否强制执行(忽略部分警告)
+ ///
+ public bool ForceExecute { get; set; } = false;
+ }
+
+ ///
+ /// 单个任务人员分配修改
+ ///
+ public class TaskAssignmentModification
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务代码(可选,用于显示)
+ ///
+ public string? TaskCode { get; set; }
+
+ ///
+ /// 新的人员ID
+ ///
+ public long? NewPersonnelId { get; set; }
+
+ ///
+ /// 新的人员姓名
+ ///
+ public string? NewPersonnelName { get; set; }
+
+ ///
+ /// 新的设备ID(可选,支持同时修改设备)
+ ///
+ public long? NewEquipmentId { get; set; }
+
+ ///
+ /// 新的设备名称(可选)
+ ///
+ public string? NewEquipmentName { get; set; }
+
+ ///
+ /// 修改备注
+ ///
+ public string ModificationNote { get; set; } = string.Empty;
+ }
+
+ ///
+ /// 单个任务设备分配修改
+ ///
+ public class TaskEquipmentAssignmentModification
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务代码(可选,用于显示)
+ ///
+ public string? TaskCode { get; set; }
+
+ ///
+ /// 新的设备ID
+ ///
+ public long? NewEquipmentId { get; set; }
+
+ ///
+ /// 新的设备名称
+ ///
+ public string? NewEquipmentName { get; set; }
+
+ ///
+ /// 修改备注
+ ///
+ public string ModificationNote { get; set; } = string.Empty;
+ }
+
+ ///
+ /// 任务分配修改结果
+ ///
+ public class TaskAssignmentModificationResult
+ {
+ ///
+ /// 是否修改成功
+ ///
+ public bool IsSuccess { get; set; }
+
+ ///
+ /// 开始时间
+ ///
+ public DateTime StartTime { get; set; }
+
+ ///
+ /// 完成时间
+ ///
+ public DateTime CompletedTime { get; set; }
+
+ ///
+ /// 耗时毫秒数
+ ///
+ public int ElapsedMilliseconds { get; set; }
+
+ ///
+ /// 整合批次编码
+ ///
+ public string IntegrationBatchCode { get; set; } = string.Empty;
+
+ ///
+ /// 总任务数量
+ ///
+ public int TotalTaskCount { get; set; }
+
+ ///
+ /// 成功修改数量
+ ///
+ public int SuccessfulCount { get; set; }
+
+ ///
+ /// 失败修改数量
+ ///
+ public int FailedCount { get; set; }
+
+ ///
+ /// 成功修改的任务详情
+ ///
+ public List SuccessfulModifications { get; set; } = new();
+
+ ///
+ /// 失败修改的任务详情
+ ///
+ public List FailedModifications { get; set; } = new();
+
+ ///
+ /// 修改摘要
+ ///
+ public string ModificationSummary { get; set; } = string.Empty;
+
+ ///
+ /// 错误消息
+ ///
+ public string ErrorMessage { get; set; } = string.Empty;
+
+ ///
+ /// 验证错误列表
+ ///
+ public List ValidationErrors { get; set; } = new();
+ }
+
+ ///
+ /// 任务修改详情
+ ///
+ public class TaskModificationDetail
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务代码
+ ///
+ public string TaskCode { get; set; } = string.Empty;
+
+ ///
+ /// 修改类型
+ ///
+ public string ModificationType { get; set; } = string.Empty;
+
+ ///
+ /// 修改前的值
+ ///
+ public string PreviousValue { get; set; } = string.Empty;
+
+ ///
+ /// 修改后的值
+ ///
+ public string NewValue { get; set; } = string.Empty;
+
+ ///
+ /// 修改时间
+ ///
+ public DateTime ModificationTime { get; set; }
+
+ ///
+ /// 修改备注
+ ///
+ public string ModificationNote { get; set; } = string.Empty;
+ }
+
+ ///
+ /// 任务修改失败详情
+ ///
+ public class TaskModificationFailure
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务代码
+ ///
+ public string TaskCode { get; set; } = string.Empty;
+
+ ///
+ /// 失败原因
+ ///
+ public string FailureReason { get; set; } = string.Empty;
+
+ ///
+ /// 错误详情
+ ///
+ public string ErrorDetails { get; set; } = string.Empty;
+ }
+
+ ///
+ /// 任务分配修改历史
+ ///
+ public class TaskAssignmentModificationHistory
+ {
+ ///
+ /// 任务ID
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务代码
+ ///
+ public string TaskCode { get; set; } = string.Empty;
+
+ ///
+ /// 修改时间
+ ///
+ public DateTime ModificationTime { get; set; }
+
+ ///
+ /// 修改详情
+ ///
+ public string ModificationDetails { get; set; } = string.Empty;
+
+ ///
+ /// 修改人用户ID
+ ///
+ public long? ModifiedByUserId { get; set; }
+
+ ///
+ /// 修改人用户名
+ ///
+ public string ModifiedByUserName { get; set; } = string.Empty;
+
+ ///
+ /// 当前人员ID
+ ///
+ public long? CurrentPersonnelId { get; set; }
+
+ ///
+ /// 当前人员姓名
+ ///
+ public string CurrentPersonnelName { get; set; } = string.Empty;
+
+ ///
+ /// 当前设备ID
+ ///
+ public long? CurrentEquipmentId { get; set; }
+
+ ///
+ /// 当前设备名称
+ ///
+ public string CurrentEquipmentName { get; set; } = string.Empty;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/NPP.SmartSchedue.Api/Services/Integration/WorkOrderIntegrationOperationService.cs b/NPP.SmartSchedue.Api/Services/Integration/WorkOrderIntegrationOperationService.cs
new file mode 100644
index 0000000..3402dcb
--- /dev/null
+++ b/NPP.SmartSchedue.Api/Services/Integration/WorkOrderIntegrationOperationService.cs
@@ -0,0 +1,987 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using NPP.SmartSchedue.Api.Contracts.Core.Enums;
+using NPP.SmartSchedue.Api.Contracts.Domain.Integration;
+using NPP.SmartSchedue.Api.Contracts.Domain.Work;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Models;
+using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output;
+using NPP.SmartSchedue.Api.Contracts.Services.Work;
+using NPP.SmartSchedue.Api.Services.Notification;
+using NPP.SmartSchedue.Api.Contracts.Services.Work.Input;
+using NPP.SmartSchedue.Api.Repositories.Work;
+using NPP.SmartSchedue.Api.Repositories.Integration;
+using ZhonTai.Admin.Services;
+using ZhonTai.DynamicApi;
+using ZhonTai.DynamicApi.Attributes;
+
+namespace NPP.SmartSchedue.Api.Services.Integration
+{
+ ///
+ /// 工作订单整合操作统一服务实现
+ ///
+ [DynamicApi(Area = "app")]
+ public class WorkOrderIntegrationOperationService : BaseService, IWorkOrderIntegrationOperationService, IDynamicApi
+ {
+ private readonly IWorkOrderService _workOrderService;
+ private readonly IIntegrationRecordService _integrationRecordService;
+ private readonly WorkOrderRepository _workOrderRepository;
+ private readonly IntegrationRecordRepository _integrationRecordRepository;
+ private readonly WorkOrderStatusValidator _statusValidator;
+ private readonly ILogger _logger;
+ private readonly NotificationScheduledService _notificationScheduledService;
+
+ public WorkOrderIntegrationOperationService(
+ IWorkOrderService workOrderService,
+ IIntegrationRecordService integrationRecordService,
+ WorkOrderRepository workOrderRepository,
+ IntegrationRecordRepository integrationRecordRepository,
+ WorkOrderStatusValidator statusValidator,
+ ILogger logger,
+ NotificationScheduledService notificationScheduledService)
+ {
+ _workOrderService = workOrderService;
+ _integrationRecordService = integrationRecordService;
+ _workOrderRepository = workOrderRepository;
+ _integrationRecordRepository = integrationRecordRepository;
+ _statusValidator = statusValidator;
+ _logger = logger;
+ _notificationScheduledService = notificationScheduledService;
+ }
+
+ ///
+ /// 修改任务统一入口
+ ///
+ [HttpPost]
+ public async Task ModifyWorkOrderAsync(WorkOrderModifyOperationInput input)
+ {
+ try
+ {
+ // 1. 参数验证
+ var paramValidation = ValidateModifyParameters(input);
+ if (!paramValidation.IsValid)
+ {
+ return CreateFailResult(paramValidation.ErrorMessage);
+ }
+
+ // 2. 一次性查询任务,获取所有需要的信息
+ var task = await _workOrderRepository.GetAsync(input.TaskId);
+ if (task == null)
+ {
+ return CreateFailResult("任务不存在");
+ }
+
+ // 3. 基于已查询的任务进行状态验证
+ var statusValidation = _statusValidator.ValidateModifyStatus(task);
+ if (!statusValidation.IsValid)
+ {
+ return CreateFailResult($"状态验证失败:{statusValidation.ErrorMessage}");
+ }
+
+ // 4. 业务规则检查
+ var businessValidation = await ValidateModifyBusinessRulesAsync(input);
+ if (!businessValidation.IsValid)
+ {
+ return CreateFailResult($"业务规则验证失败:{businessValidation.ErrorMessage}");
+ }
+
+ // 5. 保存原始任务信息,用于变更分析
+ var originalTask = new WorkOrderEntity
+ {
+ Id = task.Id,
+ WorkOrderDate = task.WorkOrderDate,
+ AssignedPersonnelId = task.AssignedPersonnelId,
+ AssignedEquipmentId = task.AssignedEquipmentId,
+ ProcessId = task.ProcessId,
+ ShiftId = task.ShiftId,
+ Priority = task.Priority,
+ Urgency = task.Urgency,
+ EstimatedHours = task.EstimatedHours,
+ Status = task.Status
+ };
+
+ // 6. 判断是否需要重新分配
+ bool needsReallocation = ShouldTriggerReallocation(originalTask, input.TaskModifyData);
+
+ if (needsReallocation)
+ {
+ _logger.LogInformation("任务{TaskId}修改触发重新分配条件", input.TaskId);
+ }
+
+ // 8. 更新整合记录(仅对已分配状态的任务)
+ long? affectedIntegrationRecordId = null;
+ bool reallocationTriggered = false;
+
+ if (originalTask.Status == (int)WorkOrderStatusEnum.Assigned && task.IntegrationRecordId.HasValue)
+ {
+ var integrationRecord = await _integrationRecordRepository.GetAsync(task.IntegrationRecordId.Value);
+ if (integrationRecord != null)
+ {
+ await UpdateIntegrationRecordAfterModifyAsync(integrationRecord, input, originalTask, needsReallocation);
+ affectedIntegrationRecordId = integrationRecord.Id;
+ }
+ }
+
+ Mapper.Map(input, task);
+ task.Status = (int)WorkOrderStatusEnum.PendingSubmit;
+ task.Remarks = "任务修改需重新智能整合";
+
+ await _workOrderRepository.UpdateAsync(task);
+
+ // 9. 发送通知
+ try
+ {
+ // 合并指定人员与任务FL人员
+ var designatedIds_Modify = new List();
+ if (task.AssignedPersonnelId != null && task.AssignedPersonnelId.HasValue) designatedIds_Modify.AddRange(task.AssignedPersonnelId.Value);
+ if (task != null && task.WorkOrderFLPersonnels != null && task.WorkOrderFLPersonnels.Any()) designatedIds_Modify.AddRange(task.WorkOrderFLPersonnels.Select(x => x.FLPersonnelId));
+ designatedIds_Modify = designatedIds_Modify.Distinct().ToList();
+
+ await _notificationScheduledService.SendWorkOrderOperationNotificationAsync(
+ "Modify", input.TaskId, User.Id, User.Name, input.Remarks, designatedIds_Modify);
+ }
+ catch (Exception notifyEx)
+ {
+ _logger.LogWarning(notifyEx, "修改任务后发送通知失败,TaskId: {TaskId}", input.TaskId);
+ }
+
+
+ // 10. 返回结果
+ var resultMessage = needsReallocation ? "任务修改成功,已触发重新分配" : "任务修改成功";
+
+ return new WorkOrderOperationResult
+ {
+ IsSuccess = true,
+ Message = resultMessage,
+ AffectedIntegrationRecordId = affectedIntegrationRecordId,
+ Data = new {
+ TaskId = input.TaskId,
+ OperationType = "Modify",
+ NeedsReallocation = needsReallocation,
+ ReallocationTriggered = reallocationTriggered
+ }
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "修改任务时发生错误,TaskId: {TaskId}", input.TaskId);
+ return CreateFailResult($"修改任务失败:{ex.Message}");
+ }
+ }
+
+ ///
+ /// 删除任务统一入口
+ ///
+ [HttpPost]
+ public async Task DeleteWorkOrderAsync(WorkOrderDeleteOperationInput input)
+ {
+ try
+ {
+ // 1. 参数验证
+ var paramValidation = ValidateDeleteParameters(input);
+ if (!paramValidation.IsValid)
+ {
+ return CreateFailResult(paramValidation.ErrorMessage);
+ }
+
+ // 2. 一次性查询任务,获取所有需要的信息
+ var task = await _workOrderRepository.GetAsync(input.TaskId);
+ if (task == null)
+ {
+ return CreateFailResult("任务不存在");
+ }
+
+ // 3. 基于已查询的任务进行状态验证
+ var statusValidation = _statusValidator.ValidateDeleteStatus(task);
+ if (!statusValidation.IsValid)
+ {
+ return CreateFailResult($"状态验证失败:{statusValidation.ErrorMessage}");
+ }
+
+ // 4. 业务规则检查
+ var businessValidation = await ValidateDeleteBusinessRulesAsync(input);
+ if (!businessValidation.IsValid)
+ {
+ return CreateFailResult($"业务规则验证失败:{businessValidation.ErrorMessage}");
+ }
+
+ // 5. 获取整合记录信息(仅对已分配任务且有关联记录的情况)
+ IntegrationRecordEntity integrationRecord = null;
+ if (task.Status == (int)WorkOrderStatusEnum.Assigned && task.IntegrationRecordId.HasValue)
+ {
+ integrationRecord = await _integrationRecordRepository.GetAsync(task.IntegrationRecordId.Value);
+ }
+
+ // 6. 执行任务操作
+ await _workOrderService.SoftDeleteAsync(input.TaskId);
+
+ // 7. 更新整合记录(仅对已分配状态的任务)
+ if (integrationRecord != null)
+ {
+ await UpdateIntegrationRecordAfterDeleteAsync(integrationRecord, input);
+ }
+
+ // 8. 发送通知
+ try
+ {
+ // 合并指定人员与任务FL人员
+ // 合并指定人员与任务FL人员
+ var designatedIds_Delete = new List();
+ if (task.AssignedPersonnelId != null && task.AssignedPersonnelId.HasValue) designatedIds_Delete.AddRange(task.AssignedPersonnelId.Value);
+ if (task != null && task.WorkOrderFLPersonnels != null && task.WorkOrderFLPersonnels.Any()) designatedIds_Delete.AddRange(task.WorkOrderFLPersonnels.Select(x => x.FLPersonnelId));
+ designatedIds_Delete = designatedIds_Delete.Distinct().ToList();
+
+ await _notificationScheduledService.SendWorkOrderOperationNotificationAsync(
+ "Delete", input.TaskId, User.Id, User.Name, input.DeleteReason, designatedIds_Delete);
+ }
+ catch (Exception notifyEx)
+ {
+ _logger.LogWarning(notifyEx, "删除任务后发送通知失败,TaskId: {TaskId}", input.TaskId);
+ }
+
+ // 9. 返回结果
+ return new WorkOrderOperationResult
+ {
+ IsSuccess = true,
+ Message = input.IsSoftDelete ? "任务软删除成功" : "任务删除成功",
+ AffectedIntegrationRecordId = integrationRecord?.Id,
+ Data = new { TaskId = input.TaskId, OperationType = "Delete", IsSoftDelete = input.IsSoftDelete }
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "删除任务时发生错误,TaskId: {TaskId}", input.TaskId);
+ return CreateFailResult($"删除任务失败:{ex.Message}");
+ }
+ }
+
+ ///
+ /// 取消任务统一入口
+ ///
+ [HttpPost]
+ public async Task CancelWorkOrderAsync(WorkOrderCancelOperationInput input)
+ {
+ try
+ {
+ // 1. 参数验证
+ var paramValidation = ValidateCancelParameters(input);
+ if (!paramValidation.IsValid)
+ {
+ return CreateFailResult(paramValidation.ErrorMessage);
+ }
+
+ // 2. 一次性查询任务,获取所有需要的信息
+ var task = await _workOrderRepository.GetAsync(input.TaskId);
+ if (task == null)
+ {
+ return CreateFailResult("任务不存在");
+ }
+
+ // 3. 基于已查询的任务进行状态验证
+ var statusValidation = _statusValidator.ValidateCancelStatus(task);
+ if (!statusValidation.IsValid)
+ {
+ return CreateFailResult($"状态验证失败:{statusValidation.ErrorMessage}");
+ }
+
+ // 4. 业务规则检查
+ var businessValidation = await ValidateCancelBusinessRulesAsync(input);
+ if (!businessValidation.IsValid)
+ {
+ return CreateFailResult($"业务规则验证失败:{businessValidation.ErrorMessage}");
+ }
+
+ // 5. 获取整合记录信息(仅对已分配任务且有关联记录的情况)
+ IntegrationRecordEntity integrationRecord = null;
+ if (task.Status == (int)WorkOrderStatusEnum.Assigned && task.IntegrationRecordId.HasValue)
+ {
+ integrationRecord = await _integrationRecordRepository.GetAsync(task.IntegrationRecordId.Value);
+ }
+
+ // 6. 执行任务操作 - 更新状态为取消
+ task.Status = (int)WorkOrderStatusEnum.Cancelled;
+ task.Remarks = "任务已取消";
+ await _workOrderRepository.UpdateAsync(task);
+
+ // 7. 更新整合记录(仅对已分配状态的任务)
+ if (integrationRecord != null)
+ {
+ await UpdateIntegrationRecordAfterCancelAsync(integrationRecord, input);
+ }
+
+ // 8. 发送通知
+ try
+ {
+ // 合并指定人员与任务FL人员
+ var designatedIds_Cancel = new List();
+ if (task.AssignedPersonnelId != null && task.AssignedPersonnelId.HasValue) designatedIds_Cancel.AddRange(task.AssignedPersonnelId.Value);
+ if (task != null && task.WorkOrderFLPersonnels != null && task.WorkOrderFLPersonnels.Any()) designatedIds_Cancel.AddRange(task.WorkOrderFLPersonnels.Select(x => x.FLPersonnelId));
+ designatedIds_Cancel = designatedIds_Cancel.Distinct().ToList();
+
+ await _notificationScheduledService.SendWorkOrderOperationNotificationAsync(
+ "Cancel", input.TaskId, User.Id, User.Name, input.CancelReason, designatedIds_Cancel);
+ }
+ catch (Exception notifyEx)
+ {
+ _logger.LogWarning(notifyEx, "取消任务后发送通知失败,TaskId: {TaskId}", input.TaskId);
+ }
+
+ // 9. 返回结果
+ return new WorkOrderOperationResult
+ {
+ IsSuccess = true,
+ Message = "任务取消成功",
+ AffectedIntegrationRecordId = integrationRecord?.Id,
+ Data = new { TaskId = input.TaskId, OperationType = "Cancel" }
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "取消任务时发生错误,TaskId: {TaskId}", input.TaskId);
+ return CreateFailResult($"取消任务失败:{ex.Message}");
+ }
+ }
+
+
+ #region 私有辅助方法
+
+ ///
+ /// 判断任务修改是否需要触发重新分配
+ /// 基于业务关键字段分析,确保分配策略的准确性
+ ///
+ private bool ShouldTriggerReallocation(WorkOrderEntity originalTask, WorkOrderUpdateInput modifyData)
+ {
+ // 1. 时间相关变更(高优先级)
+ // WorkOrderDate 变更直接影响班次安排和资源可用性
+ if (originalTask.WorkOrderDate.Date != modifyData.WorkOrderDate.Date)
+ {
+ _logger.LogInformation("任务{TaskId}日期从{OriginalDate}变更为{NewDate},触发重新分配",
+ originalTask.Id, originalTask.WorkOrderDate.ToString("yyyy-MM-dd"),
+ modifyData.WorkOrderDate.ToString("yyyy-MM-dd"));
+ return true;
+ }
+
+ // 2. 人员设备约束变更(直接影响资源分配)
+ if (originalTask.AssignedPersonnelId != modifyData.AssignedPersonnelId ||
+ originalTask.AssignedEquipmentId != modifyData.AssignedEquipmentId)
+ {
+ _logger.LogInformation("任务{TaskId}资源分配变更,人员:{OriginalPersonnel}->{NewPersonnel},设备:{OriginalEquipment}->{NewEquipment},触发重新分配",
+ originalTask.Id,
+ originalTask.AssignedPersonnelId, modifyData.AssignedPersonnelId,
+ originalTask.AssignedEquipmentId, modifyData.AssignedEquipmentId);
+ return true;
+ }
+
+ // 3. 工序或班次变更(影响分配策略和资源池)
+ if (originalTask.ProcessId != modifyData.ProcessId ||
+ originalTask.ShiftId != modifyData.ShiftId)
+ {
+ _logger.LogInformation("任务{TaskId}工序或班次变更,工序:{OriginalProcess}->{NewProcess},班次:{OriginalShift}->{NewShift},触发重新分配",
+ originalTask.Id,
+ originalTask.ProcessId, modifyData.ProcessId,
+ originalTask.ShiftId, modifyData.ShiftId);
+ return true;
+ }
+
+ return false;
+ }
+
+
+ ///
+ /// 修改任务后更新整合记录(仅对已分配任务生效)
+ ///
+ private async Task UpdateIntegrationRecordAfterModifyAsync(IntegrationRecordEntity record, WorkOrderModifyOperationInput input, WorkOrderEntity originalTask, bool needsReallocation)
+ {
+ if (record == null) return;
+
+ try
+ {
+ var modifyData = input.TaskModifyData;
+
+ // === 1. 版本管理字段(每次修改都更新) ===
+ record.CurrentVersion += 1;
+ record.ChangeTriggerSource = "TaskModification";
+
+ // === 2. 根据变更类型更新对应字段 ===
+ var changeTypes = new List();
+
+ // 2.1 日期变更处理
+ if (originalTask.WorkOrderDate.Date != modifyData.WorkOrderDate.Date)
+ {
+ changeTypes.Add("WorkOrderDateChanged");
+ // 日期变更影响所有基于时间的分配,需要标记为需要重新分配
+ await UpdateAllocationResultsForDateChange(record, input.TaskId, originalTask, modifyData);
+ }
+
+ // 2.2 人员分配变更处理
+ if (originalTask.AssignedPersonnelId != modifyData.AssignedPersonnelId)
+ {
+ changeTypes.Add("PersonnelAssignmentChanged");
+ await UpdatePersonnelAllocationResultAfterTaskCancel(record, input.TaskId, "Task AssignmentChanged");
+ await RecalculatePersonnelFairnessScore(record);
+ }
+
+ // 2.3 设备分配变更处理
+ if (originalTask.AssignedEquipmentId != modifyData.AssignedEquipmentId)
+ {
+ changeTypes.Add("EquipmentAssignmentChanged");
+ await UpdateEquipmentAllocationResultAfterTaskCancel(record, input.TaskId, "Task AssignmentChanged");
+ await RecalculateEquipmentUtilizationRate(record);
+ }
+
+ // 2.4 工序/班次变更处理
+ if (originalTask.ProcessId != modifyData.ProcessId || originalTask.ShiftId != modifyData.ShiftId)
+ {
+ changeTypes.Add("ProcessOrShiftChanged");
+ // 工序班次变更可能需要更新分配策略配置
+ await UpdateStrategyConfigForProcessShiftChange(record, originalTask, modifyData);
+ // 重新评估所有分配结果
+ await ReEvaluateAllAllocationResults(record, input.TaskId);
+ }
+
+ // === 3. 统计信息更新 ===
+ if (needsReallocation)
+ {
+ // 如果需要重新分配,可能会影响成功失败统计
+ // 这里可以标记状态,等待重新分配完成后再更新统计
+ record.AllowAutoReallocation = true;
+ record.ChangeImpactScore = CalculateChangeImpact(changeTypes, originalTask, modifyData);
+ }
+
+ // === 4. 变更详情记录 ===
+ var changeDetails = record.GetChangeDetails();
+ if (!changeDetails.ChangedTaskIds.Contains(input.TaskId))
+ {
+ changeDetails.ChangedTaskIds.Add(input.TaskId);
+ }
+ changeDetails.ChangeTypes.AddRange(changeTypes);
+ changeDetails.DetectedTime = DateTime.Now;
+
+ // 记录具体变更内容到TaskModifications
+ if (originalTask.WorkOrderDate.Date != modifyData.WorkOrderDate.Date)
+ {
+ changeDetails.TaskModifications.Add(new TaskChangeDetail
+ {
+ TaskId = input.TaskId,
+ ModificationType = "TimeChanged",
+ BeforeValue = System.Text.Json.JsonSerializer.Serialize(new { WorkOrderDate = originalTask.WorkOrderDate }),
+ AfterValue = System.Text.Json.JsonSerializer.Serialize(new { WorkOrderDate = modifyData.WorkOrderDate }),
+ Description = "任务日期变更"
+ });
+ }
+
+ if (originalTask.AssignedPersonnelId != modifyData.AssignedPersonnelId)
+ {
+ changeDetails.PersonnelChanges.Add(new PersonnelAssignmentChange
+ {
+ TaskId = input.TaskId,
+ PreviousPersonnelId = originalTask.AssignedPersonnelId,
+ NewPersonnelId = modifyData.AssignedPersonnelId,
+ ChangeReason = "任务修改导致人员分配变更"
+ });
+ }
+
+ if (originalTask.AssignedEquipmentId != modifyData.AssignedEquipmentId)
+ {
+ changeDetails.EquipmentChanges.Add(new EquipmentAssignmentChange
+ {
+ TaskId = input.TaskId,
+ PreviousEquipmentId = originalTask.AssignedEquipmentId,
+ NewEquipmentId = modifyData.AssignedEquipmentId,
+ ChangeReason = "任务修改导致设备分配变更"
+ });
+ }
+
+ if (originalTask.ProcessId != modifyData.ProcessId)
+ {
+ changeDetails.TaskModifications.Add(new TaskChangeDetail
+ {
+ TaskId = input.TaskId,
+ ModificationType = "ProcessChanged",
+ BeforeValue = System.Text.Json.JsonSerializer.Serialize(new { ProcessId = originalTask.ProcessId }),
+ AfterValue = System.Text.Json.JsonSerializer.Serialize(new { ProcessId = modifyData.ProcessId }),
+ Description = "工序变更"
+ });
+ }
+
+ if (originalTask.ShiftId != modifyData.ShiftId)
+ {
+ changeDetails.TaskModifications.Add(new TaskChangeDetail
+ {
+ TaskId = input.TaskId,
+ ModificationType = "ShiftChanged",
+ BeforeValue = System.Text.Json.JsonSerializer.Serialize(new { ShiftId = originalTask.ShiftId }),
+ AfterValue = System.Text.Json.JsonSerializer.Serialize(new { ShiftId = modifyData.ShiftId }),
+ Description = "班次变更"
+ });
+ }
+ record.SetChangeDetails(changeDetails);
+
+ // === 5. 更新结果摘要 ===
+ var reallocationNote = needsReallocation ? " (需要重新分配)" : "";
+ record.ResultSummary = $"总任务数: {record.TotalTaskCount}, 成功: {record.SuccessTaskCount}, 失败: {record.FailedTaskCount}, 成功率: {record.SuccessRate}%{reallocationNote}";
+
+ // === 6. 操作日志 ===
+ var changeTypeStr = string.Join(", ", changeTypes);
+ record.Remarks = $"{record.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 任务{input.TaskId}修改({changeTypeStr}){reallocationNote}: {input.Remarks}";
+
+ await _integrationRecordRepository.UpdateAsync(record);
+
+ _logger.LogInformation("任务{TaskId}修改后,整合记录{IntegrationRecordId}已更新,变更类型:{ChangeTypes},需要重新分配:{NeedsReallocation}",
+ input.TaskId, record.Id, changeTypeStr, needsReallocation);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新整合记录时发生错误,TaskId: {TaskId}, IntegrationRecordId: {IntegrationRecordId}",
+ input.TaskId, record.Id);
+ throw;
+ }
+ }
+
+ ///
+ /// 删除任务后更新整合记录(仅对已分配任务生效)
+ ///
+ private async Task UpdateIntegrationRecordAfterDeleteAsync(IntegrationRecordEntity record, WorkOrderDeleteOperationInput input)
+ {
+ if (record == null) return;
+
+ try
+ {
+ // 1. 从 TaskIdsJson 中移除被删除的任务ID
+ var taskIds = record.GetTaskIds();
+ if (taskIds.Contains(input.TaskId))
+ {
+ taskIds.Remove(input.TaskId);
+ record.SetTaskIds(taskIds);
+ }
+
+ // 2. 更新任务计数统计(删除的任务直接减少成功数,不增加失败数)
+ record.SuccessTaskCount = Math.Max(0, record.SuccessTaskCount - 1);
+
+ // 3. 更新人员分配结果(从JSON中移除该任务的分配信息)
+ await UpdatePersonnelAllocationResultAfterTaskRemoval(record, input.TaskId);
+
+ // 4. 更新设备分配结果(从JSON中移除该任务的分配信息)
+ await UpdateEquipmentAllocationResultAfterTaskRemoval(record, input.TaskId);
+
+ // 5. 更新结果摘要
+ record.ResultSummary = $"总任务数: {record.TotalTaskCount}, 成功: {record.SuccessTaskCount}, 失败: {record.FailedTaskCount}, 成功率: {record.SuccessRate}%";
+
+ // 6. 记录操作日志
+ var deleteType = input.IsSoftDelete ? "软删除" : "物理删除";
+ record.Remarks = $"{record.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 任务{input.TaskId}被{input.OperatorName}{deleteType}: {input.DeleteReason}";
+
+ await _integrationRecordRepository.UpdateAsync(record);
+
+ _logger.LogInformation("已分配任务{TaskId}{DeleteType}后,整合记录{IntegrationRecordId}已更新(包含TaskIds、分配结果等)",
+ input.TaskId, deleteType, record.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "删除任务后更新整合记录时发生错误,TaskId: {TaskId}, IntegrationRecordId: {IntegrationRecordId}",
+ input.TaskId, record.Id);
+ }
+ }
+
+ ///
+ /// 取消任务后更新整合记录(仅对已分配任务生效)
+ ///
+ private async Task UpdateIntegrationRecordAfterCancelAsync(IntegrationRecordEntity record, WorkOrderCancelOperationInput input)
+ {
+ if (record == null) return;
+
+ try
+ {
+ // 1. 任务取消后不从TaskIdsJson中移除(取消的任务仍然是原始整合的一部分)
+ // 2. 更新任务计数统计
+ record.SuccessTaskCount = Math.Max(0, record.SuccessTaskCount - 1);
+
+ // 3. 更新人员分配结果(将该任务标记为取消状态)
+ await UpdatePersonnelAllocationResultAfterTaskCancel(record, input.TaskId, "Task Cancelled");
+
+ // 4. 更新设备分配结果(将该任务标记为取消状态)
+ await UpdateEquipmentAllocationResultAfterTaskCancel(record, input.TaskId, "Task Cancelled");
+
+ // 5. 更新结果摘要
+ record.ResultSummary = $"总任务数: {record.TotalTaskCount}, 成功: {record.SuccessTaskCount}, 失败: {record.FailedTaskCount}, 成功率: {record.SuccessRate}%";
+
+ // 6. 记录操作日志
+ record.Remarks = $"{record.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 任务{input.TaskId}被{input.OperatorName}取消: {input.CancelReason}";
+
+ await _integrationRecordRepository.UpdateAsync(record);
+
+ _logger.LogInformation("已分配任务{TaskId}取消后,整合记录{IntegrationRecordId}已更新(包含分配结果状态等)", input.TaskId, record.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "取消任务后更新整合记录时发生错误,TaskId: {TaskId}, IntegrationRecordId: {IntegrationRecordId}",
+ input.TaskId, record.Id);
+ }
+ }
+
+
+ ///
+ /// 创建失败结果
+ ///
+ private WorkOrderOperationResult CreateFailResult(string message)
+ {
+ return new WorkOrderOperationResult
+ {
+ IsSuccess = false,
+ Message = message,
+ ErrorDetails = { message }
+ };
+ }
+
+ ///
+ /// 验证修改操作参数
+ ///
+ private WorkOrderValidationResult ValidateModifyParameters(WorkOrderModifyOperationInput input)
+ {
+ if (input == null)
+ return WorkOrderValidationResult.Fail("输入参数不能为空");
+
+ if (input.TaskId <= 0)
+ return WorkOrderValidationResult.Fail("任务ID必须大于0");
+
+ if (input.TaskModifyData == null)
+ return WorkOrderValidationResult.Fail("修改数据不能为空");
+
+ return WorkOrderValidationResult.Success();
+ }
+
+ ///
+ /// 验证删除操作参数
+ ///
+ private WorkOrderValidationResult ValidateDeleteParameters(WorkOrderDeleteOperationInput input)
+ {
+ if (input == null)
+ return WorkOrderValidationResult.Fail("输入参数不能为空");
+
+ if (input.TaskId <= 0)
+ return WorkOrderValidationResult.Fail("任务ID必须大于0");
+
+ return WorkOrderValidationResult.Success();
+ }
+
+ ///
+ /// 验证取消操作参数
+ ///
+ private WorkOrderValidationResult ValidateCancelParameters(WorkOrderCancelOperationInput input)
+ {
+ if (input == null)
+ return WorkOrderValidationResult.Fail("输入参数不能为空");
+
+ if (input.TaskId <= 0)
+ return WorkOrderValidationResult.Fail("任务ID必须大于0");
+
+ return WorkOrderValidationResult.Success();
+ }
+
+ ///
+ /// 验证修改操作的业务规则
+ ///
+ private Task ValidateModifyBusinessRulesAsync(WorkOrderModifyOperationInput input)
+ {
+ // 这里可以添加具体的业务规则验证
+ // 例如:检查任务是否与其他任务有依赖关系等
+ return Task.FromResult(WorkOrderValidationResult.Success());
+ }
+
+ ///
+ /// 验证删除操作的业务规则
+ ///
+ private Task ValidateDeleteBusinessRulesAsync(WorkOrderDeleteOperationInput input)
+ {
+ // 这里可以添加具体的业务规则验证
+ // 例如:检查任务是否与其他任务有依赖关系等
+ return Task.FromResult(WorkOrderValidationResult.Success());
+ }
+
+ ///
+ /// 验证取消操作的业务规则
+ ///
+ private Task ValidateCancelBusinessRulesAsync(WorkOrderCancelOperationInput input)
+ {
+ // 这里可以添加具体的业务规则验证
+ // 例如:检查任务是否与其他任务有依赖关系等
+ return Task.FromResult(WorkOrderValidationResult.Success());
+ }
+
+ ///
+ /// 任务删除后更新人员分配结果
+ ///
+ private async Task UpdatePersonnelAllocationResultAfterTaskRemoval(IntegrationRecordEntity record, long taskId)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(record.PersonnelAllocationResultJson) || record.PersonnelAllocationResultJson == "{}")
+ return;
+
+ // 解析人员分配结果
+ var allocationResult = System.Text.Json.JsonSerializer.Deserialize>(record.PersonnelAllocationResultJson);
+ if (allocationResult == null) return;
+
+ // 从成功分配列表中移除该任务
+ if (allocationResult.ContainsKey("successfulAllocations"))
+ {
+ var successfulAllocations = allocationResult["successfulAllocations"] as JsonElement?;
+ if (successfulAllocations.HasValue && successfulAllocations.Value.ValueKind == JsonValueKind.Array)
+ {
+ var updatedAllocations = new List