using AutoMapper; using NPOI.Util; using OASystem.Domain; using OASystem.Domain.Dtos.Groups; using OASystem.Domain.Dtos.Task; using OASystem.Domain.Entities.Groups; using OASystem.Domain.ViewModels.Groups; using OASystem.Domain.ViewModels.SmallFun; using OASystem.Infrastructure.Tools; using System.Reflection; using System.Text.Json.Serialization; namespace OASystem.Infrastructure.Repositories.Groups { /// /// 签证流程步骤仓储 /// public class VisaProcessRepository : BaseRepository { private readonly IMapper _mapper; public VisaProcessRepository(SqlSugarClient sqlSugar, IMapper mapper) : base(sqlSugar) { _mapper = mapper; } /// /// 创建签证流程步骤 /// /// /// /// public async Task Create(int createUderId,int groupId) { //团组有效验证 var groupIsValid = await _sqlSugar.Queryable() .Where(g => g.Id == groupId && g.IsDel == 0) .AnyAsync(); if (!groupIsValid) { return new Result(400, "团组无效,无法创建签证流程步骤。"); } var existingSteps = await _sqlSugar.Queryable() .Where(s => s.GroupId == groupId && s.IsDel == 0) .AnyAsync(); if (existingSteps) { return new Result(400, "该团组的签证流程步骤已存在,无法重复创建。"); } var steps = Grp_VisaProcessSteps.StepsInit(groupId, createUderId); var add = await _sqlSugar.Insertable(steps).ExecuteCommandAsync(); if (add < 1) return new Result(400, "签证流程步骤创建失败。"); // 记录创建日志 foreach (var step in steps) { await LogOperationAsync(null, step, "Create", createUderId); } return new Result(200,"Success"); } /// /// 签证流程 Info /// /// /// /// public async Task Info(VisaProcessInfoByGroupIdDto dto) { //团组有效验证 //var groupIsValid = await _sqlSugar.Queryable() // .Where(g => g.Id == dto.GroupId && g.IsDel == 0) // .AnyAsync(); //if (!groupIsValid) //{ // return new Result(400, "团组无效,无法查询签证流程步骤。"); //} var query = await _sqlSugar.Queryable() .Where(s => s.GroupId == dto.GroupId && s.IsDel == 0) .OrderBy(s => s.Step) .ToListAsync(); var infos = query.Select(s => new { s.Id, s.GroupId, s.Step, s.DataType, s.TypedValue, //s.StoreVal, s.IsCompleted, s.AttachUrl, s.TypedFileNameValue, s.Remark }).ToList(); string msg = "Success"; //如果不存在,则返回默认数据 if (infos == null || infos.Count < 1) { infos = Grp_VisaProcessSteps.StepsInit(dto.GroupId, 208) .Select(s => new { s.Id, s.GroupId, s.Step, s.DataType, s.TypedValue, //s.StoreVal, s.IsCompleted, s.AttachUrl, s.TypedFileNameValue, s.Remark }).ToList(); msg = "签证流程步骤信息不存在,展示默认数据。"; } //数据按照类型处理 var datas = new List(); var view = new VisaProcessStepsInfoView(); foreach (var item in infos) { if (item.DataType == "string") { var info = new VisaProcessStepsInfoByStringView { Id = item.Id, GroupId = item.GroupId, Step = item.Step, DataType = item.DataType, TypedValue = item.TypedValue == null ? "" : item.TypedValue.ToString(), //StoreVal = item.StoreVal, IsCompleted = item.IsCompleted, AttachUrl = item.TypedFileNameValue, Remark = item.Remark }; if (item.Step == 1) view.Step1 = info; else if (item.Step == 2) view.Step2 = info; else if (item.Step == 3) view.Step3 = info; else if (item.Step == 4) view.Step4 = info; else if (item.Step == 5) view.Step5 = info; else if (item.Step == 8) view.Step8 = info; datas.Add(info); } else if (item.DataType == "bool") { bool boolVal = false; if (item.TypedValue != null) { boolVal = (bool)item.TypedValue; } var info = new VisaProcessStepsInfoByBoolView { Id = item.Id, GroupId = item.GroupId, Step = item.Step, DataType = item.DataType, TypedValue = boolVal, IsCompleted = item.IsCompleted, AttachUrl = item.TypedFileNameValue, Remark = item.Remark }; if (item.Step == 6) view.Step6 = info; datas.Add(info); } else if (item.DataType == "string[]") { var listVal = new List(); var contnet = new VisaProcessSteps7Content() { IsSelected = false }; if (item.TypedValue != null) { listVal = item.TypedValue switch { List stringList => stringList, IEnumerable stringEnumerable => stringEnumerable.ToList(), string str => new List { str }, IEnumerable objectEnumerable => objectEnumerable.Select(x => x?.ToString() ?? "").ToList(), _ => new List { item.TypedValue.ToString() ?? "" } }; if (listVal != null && listVal.Count > 0) { if (bool.TryParse(CommonFun.GetValueOrDefault(listVal, 0), out bool val)) { contnet.IsSelected = val; } contnet.ProjectedDate = CommonFun.GetValueOrDefault(listVal, 1); } } var info = new VisaProcessStepsInfoByListView { Id = item.Id, GroupId = item.GroupId, Step = item.Step, DataType = item.DataType, TypedValue = contnet, IsCompleted = item.IsCompleted, AttachUrl = item.TypedFileNameValue, Remark = item.Remark }; if (item.Step == 7) view.Step7 = info; datas.Add(info); } } if (dto.PortType == 2 ) { return new Result(200, msg, infos); } return new Result(200, msg, view); } /// /// 修改签证流程步骤 /// /// /// /// public async Task Update(Grp_VisaProcessSteps info) { var step = await _sqlSugar.Queryable().FirstAsync(x => x.Id == info.Id); var before = ManualClone(step); step.StoreVal = info.StoreVal; step.LastUpdateUserId = info.LastUpdateUserId; step.LastUpdateTime = DateTime.Now; step.Remark = info.Remark; var update = await _sqlSugar.Updateable(step) .Where(s => s.Id == info.Id && s.IsDel == 0) .ExecuteCommandAsync(); if (update < 1) return new Result(400, "更新失败!"); // 记录更新日志 await LogOperationAsync(before, step, "Update", info.LastUpdateUserId); return new Result(200, "Success"); } /// /// 批量修改top4Steps /// /// /// public async Task UpdateBatchTop4Step(int groupId,int currUserId) { var steps = new List() { 1, //签证启动日 2, //分配工作 3, //送外办时间 4, //预计出签时间 }; var visaSteps = await _sqlSugar.Queryable() .Where(s => s.GroupId == groupId && s.IsDel == 0 && steps.Contains(s.Step)) .ToListAsync(); if (visaSteps.Count < 1) return new Result(400, "签证流程信息不存在。"); //倒推表信息 var invertedInfo = await _sqlSugar.Queryable() .Where(i => i.DiId == groupId && i.IsDel == 0) .FirstAsync(); if (invertedInfo == null) return new Result(400, "倒推表信息不存在。"); //步骤更改前的值 var beforeStep1 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 1)); var beforeStep2 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 2)); var beforeStep3 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 3)); var beforeStep4 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 4)); //设置操作人和时间 foreach (var item in visaSteps) { //默认确认完成 item.IsCompleted = true; item.LastUpdateUserId = currUserId; item.LastUpdateTime = DateTime.Now; } //step=1 设置启动日值 visaSteps.FirstOrDefault(x => x.Step == 1).TypedValue = DateTime.Now.ToString("yyyy-MM-dd"); //step=2 分配工作 visaSteps.FirstOrDefault(x => x.Step == 2).TypedValue = invertedInfo.IssueApprovalDt; //step=3 送外办时间 visaSteps.FirstOrDefault(x => x.Step == 3).TypedValue = invertedInfo.SendVisaDt; //step=4 预计出签时间 visaSteps.FirstOrDefault(x => x.Step == 4).TypedValue = invertedInfo.IssueVisaDt; var update = await _sqlSugar.Updateable(visaSteps) .UpdateColumns(it => new { it.StoreVal, it.LastUpdateUserId,it.LastUpdateTime }) .ExecuteCommandAsync(); if (update < 1) return new Result(400, "更新失败!"); //更改后的值 var afterStep1 = visaSteps.FirstOrDefault(x => x.Step == 1); var afterStep2 = visaSteps.FirstOrDefault(x => x.Step == 2); var afterStep3 = visaSteps.FirstOrDefault(x => x.Step == 3); var afterStep4 = visaSteps.FirstOrDefault(x => x.Step == 4); // 记录更新日志 await LogOperationAsync(beforeStep1, afterStep1, "Update", currUserId); await LogOperationAsync(beforeStep2, afterStep2, "Update", currUserId); await LogOperationAsync(beforeStep3, afterStep3, "Update", currUserId); await LogOperationAsync(beforeStep4, afterStep4, "Update", currUserId); return new Result(200, "Success"); } /// /// 设置已完成的步骤 /// /// /// /// /// public async Task SetCompleted(int id, int currUserId, bool isCompleted = true) { //步骤信息验证 var stepInfo = await _sqlSugar.Queryable() .Where(s => s.Id == id && s.IsDel == 0) .FirstAsync(); if (stepInfo == null) return new Result(400, "步骤信息不存在,无法设置。"); if (stepInfo.IsCompleted) return new Result(400, "步骤信息已完成,无法重复设置。"); var before = ManualClone(stepInfo); stepInfo.IsCompleted = isCompleted; stepInfo.LastUpdateUserId = currUserId; stepInfo.LastUpdateTime = DateTime.Now; var update = await _sqlSugar.Updateable() .SetColumns(s => new Grp_VisaProcessSteps { IsCompleted = isCompleted, LastUpdateUserId = currUserId, LastUpdateTime = DateTime.Now }) .Where(s => s.Id == id && s.IsDel == 0 && !s.IsCompleted) .ExecuteCommandAsync(); if (update < 1) return new Result(400, "状态设置失败!"); // 记录更新日志 await LogOperationAsync(before, stepInfo, "Update", currUserId); return new Result(200, "Success"); } #region 操作日志 /// /// 记录签证流程步骤的操作日志 /// /// 操作前的步骤数据实体 /// 操作后的步骤数据实体 /// 操作类型(Create-创建, Update-更新, Delete-删除, Complete-完成, Upload-上传附件, Download-下载文件) /// 操作人用户ID /// 表示异步操作的任务 /// 此方法会自动比较前后数据的差异,生成变更字段列表和操作描述 public async Task LogOperationAsync(Grp_VisaProcessSteps before, Grp_VisaProcessSteps after,string opType, int operatorId) { // 合并基础排除字段和额外排除字段 var allExcludedFields = GetDefaultExcludedFields(); var changedFields = GetChangeDetails(before, after, allExcludedFields); var log = new Grp_VisaProcessSteps_Log { StepId = after?.Id ?? before?.Id ?? 0, GroupId = after?.GroupId ?? before?.GroupId ?? 0, Step = after?.Step ?? before?.Step ?? 0, OperationType = opType, OperationDescription = GenerateOpDesc(opType, before, after, changedFields), BeforeData = before != null ? JsonSerializer.Serialize(before, new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles, WriteIndented = false }) : null, AfterData = after != null ? JsonSerializer.Serialize(after, new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles, WriteIndented = false }) : null, ChangedFields = string.Join(",", changedFields), CreateUserId = operatorId, }; await _sqlSugar.Insertable(log).ExecuteCommandAsync(); } /// /// 获取字段变更详情 /// /// 变更前 /// 变更后 /// 排除字段 /// 变更详情列表 private List GetChangeDetails(Grp_VisaProcessSteps before, Grp_VisaProcessSteps after, List exclFields = null) { var changeDetails = new List(); var defaultExclFields = GetDefaultExcludedFields(); // 合并排除字段 var allExclFields = defaultExclFields; if (exclFields != null && exclFields.Any()) { allExclFields = defaultExclFields.Union(exclFields).ToList(); } if (before == null || after == null) return changeDetails; var properties = typeof(Grp_VisaProcessSteps).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in properties) { // 跳过排除的字段 if (allExclFields.Contains(prop.Name)) { continue; } var beforeValue = prop.GetValue(before); var afterValue = prop.GetValue(after); // 处理字符串类型的特殊比较(忽略前后空格) if (prop.PropertyType == typeof(string)) { var beforeString = beforeValue?.ToString()?.Trim(); var afterString = afterValue?.ToString()?.Trim(); if (beforeString != afterString) { changeDetails.Add(new FieldChangeDetail { FieldName = prop.Name, BeforeValue = FormatValue(beforeString), AfterValue = FormatValue(afterString) }); } } else { // 其他类型使用默认比较 if (!Equals(beforeValue, afterValue)) { changeDetails.Add(new FieldChangeDetail { FieldName = prop.Name, BeforeValue = FormatValue(beforeValue), AfterValue = FormatValue(afterValue) }); } } } return changeDetails; } /// /// 格式化值显示 /// /// 原始值 /// 格式化后的值 private static string FormatValue(object value) { if (value == null) return "null"; var strValue = value.ToString(); // 处理空字符串 if (string.IsNullOrEmpty(strValue)) return "空"; // 处理布尔值 if (value is bool boolValue) return boolValue ? "是" : "否"; // 处理日期时间 if (value is DateTime dateTimeValue) return dateTimeValue.ToString("yyyy-MM-dd HH:mm"); // 处理长文本截断 if (strValue.Length > 50) return strValue.Substring(0, 47) + "..."; return strValue; } /// /// 生成操作描述 /// /// 操作类型 /// 操作前 /// 操作后 /// 变更详情 /// 操作描述 private static string GenerateOpDesc(string opType, Grp_VisaProcessSteps before, Grp_VisaProcessSteps after, List chgDetails) { if (!chgDetails.Any()) { return opType switch { "Create" => $"创建步骤:团组{after.GroupId}-步骤{after.Step}", "Update" => $"更新步骤:无变更", "Complete" => $"完成步骤:团组{after.GroupId}-步骤{after.Step}", "Uncomplete" => $"取消完成:团组{after.GroupId}-步骤{after.Step}", "Delete" => $"删除步骤:团组{before.GroupId}-步骤{before.Step}", "Upload" => $"上传附件:团组{after.GroupId}-步骤{after.Step}", _ => $"{opType}:团组{after?.GroupId ?? before?.GroupId}-步骤{after?.Step ?? before?.Step}" }; } var changeDesc = string.Join("; ", chgDetails.Select(x => $"{x.FieldName} ({x.BeforeValue} -> {x.AfterValue})")); return opType switch { "Create" => $"创建步骤:团组{after.GroupId}-步骤{after.Step}", "Update" => $"更新步骤:{changeDesc}", "Complete" => $"完成步骤:团组{after.GroupId}-步骤{after.Step}", "Uncomplete" => $"取消完成:团组{after.GroupId}-步骤{after.Step}", "Delete" => $"删除步骤:团组{before.GroupId}-步骤{before.Step}", "Upload" => $"上传附件:团组{after.GroupId}-步骤{after.Step}", _ => $"{opType}:{changeDesc}" }; } /// /// 字段变更详情类 /// private class FieldChangeDetail { public string FieldName { get; set; } public string BeforeValue { get; set; } public string AfterValue { get; set; } } /// /// 获取默认排除的字段列表(包含系统字段和忽略字段) /// /// 默认排除的字段列表 private static List GetDefaultExcludedFields() { var defaultExcludedFields = new List { nameof(Grp_VisaProcessSteps.Id), nameof(Grp_VisaProcessSteps.CreateTime), nameof(Grp_VisaProcessSteps.CreateUserId), //nameof(Grp_VisaProcessSteps.LastUpdateTime), //nameof(Grp_VisaProcessSteps.LastUpdateUserId), // 计算字段(原本标记为 [SugarColumn(IsIgnore = true)] 的字段) "TypedFileNameValue", "TypedValue", "StringValue", "IntValue", "DecimalValue", "BooleanValue", "DateTimeValue" }; return defaultExcludedFields.Distinct().ToList(); } /// /// 手动创建步骤实体的副本 /// /// 源步骤实体 /// 新的步骤实体副本 private Grp_VisaProcessSteps ManualClone(Grp_VisaProcessSteps source) { if (source == null) return null; return new Grp_VisaProcessSteps { Id = source.Id, GroupId = source.GroupId, Step = source.Step, DataType = source.DataType, StoreVal = source.StoreVal, AttachUrl = source.AttachUrl, IsCompleted = source.IsCompleted, LastUpdateUserId = source.LastUpdateUserId, LastUpdateTime = source.LastUpdateTime, CreateUserId = source.CreateUserId, CreateTime = source.CreateTime // 注意:这里不拷贝计算属性(TypedValue 等),因为它们会被重新计算 }; } /// /// 根据步骤记录ID获取该步骤的所有操作日志 /// /// 步骤记录的主键ID /// 步骤操作日志列表,按操作时间倒序排列 public async Task> GetStepLogsAsync(int stepId) { return await _sqlSugar.Queryable() .LeftJoin((x, y) => x.CreateUserId == y.Id) .Where((x, y) => x.StepId == stepId) .Select((x, y) => new VisaProcessStepsLogView { StepId = x.StepId, GroupId = x.GroupId, Step = x.Step, OperationType = x.OperationType, OperationDescription = x.OperationDescription, Operator = y.CnName, OperationTime = x.CreateTime, }) .OrderByDescending(x => x.OperationTime) .ToListAsync(); } /// /// 根据团组ID获取该团组下所有步骤的操作日志 /// /// 团组ID /// 团组步骤操作日志列表,按操作时间倒序排列 public async Task> GetGroupStepLogsAsync(int groupId) { return await _sqlSugar.Queryable() .LeftJoin((x, y) => x.CreateUserId == y.Id) .Where((x, y) => x.GroupId == groupId) .Select((x, y) => new VisaProcessStepsLogView { StepId = x.StepId, GroupId = x.GroupId, Step = x.Step, OperationType = x.OperationType, OperationDescription = x.OperationDescription, Operator = y.CnName, OperationTime = x.CreateTime, }) .OrderByDescending(x => x.OperationTime) .ToListAsync(); } #endregion } }