VisaProcessRepository.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. using AutoMapper;
  2. using NPOI.Util;
  3. using OASystem.Domain;
  4. using OASystem.Domain.Dtos.Groups;
  5. using OASystem.Domain.Dtos.Task;
  6. using OASystem.Domain.Entities.Groups;
  7. using OASystem.Domain.ViewModels.Groups;
  8. using OASystem.Domain.ViewModels.SmallFun;
  9. using OASystem.Infrastructure.Tools;
  10. using System.Reflection;
  11. using System.Text.Json.Serialization;
  12. namespace OASystem.Infrastructure.Repositories.Groups
  13. {
  14. /// <summary>
  15. /// 签证流程步骤仓储
  16. /// </summary>
  17. public class VisaProcessRepository : BaseRepository<Grp_VisaProcessSteps, Grp_VisaProcessSteps>
  18. {
  19. private readonly IMapper _mapper;
  20. public VisaProcessRepository(SqlSugarClient sqlSugar, IMapper mapper)
  21. : base(sqlSugar)
  22. {
  23. _mapper = mapper;
  24. }
  25. /// <summary>
  26. /// 创建签证流程步骤
  27. /// </summary>
  28. /// <param name="groupId"></param>
  29. /// <param name="createUderId"></param>
  30. /// <returns></returns>
  31. public async Task<Result> Create(int createUderId,int groupId)
  32. {
  33. //团组有效验证
  34. var groupIsValid = await _sqlSugar.Queryable<Grp_DelegationInfo>()
  35. .Where(g => g.Id == groupId && g.IsDel == 0)
  36. .AnyAsync();
  37. if (!groupIsValid)
  38. {
  39. return new Result(400, "团组无效,无法创建签证流程步骤。");
  40. }
  41. var existingSteps = await _sqlSugar.Queryable<Grp_VisaProcessSteps>()
  42. .Where(s => s.GroupId == groupId && s.IsDel == 0)
  43. .AnyAsync();
  44. if (existingSteps)
  45. {
  46. return new Result(400, "该团组的签证流程步骤已存在,无法重复创建。");
  47. }
  48. var steps = Grp_VisaProcessSteps.StepsInit(groupId, createUderId);
  49. var add = await _sqlSugar.Insertable(steps).ExecuteCommandAsync();
  50. if (add < 1) return new Result(400, "签证流程步骤创建失败。");
  51. // 记录创建日志
  52. foreach (var step in steps)
  53. {
  54. await LogOperationAsync(null, step, "Create", createUderId);
  55. }
  56. return new Result(200,"Success");
  57. }
  58. /// <summary>
  59. /// 签证流程 Info
  60. /// </summary>
  61. /// <param name="dto"></param>
  62. /// <param name="createUderId"></param>
  63. /// <returns></returns>
  64. public async Task<Result> Info(VisaProcessInfoByGroupIdDto dto)
  65. {
  66. //团组有效验证
  67. //var groupIsValid = await _sqlSugar.Queryable<Grp_DelegationInfo>()
  68. // .Where(g => g.Id == dto.GroupId && g.IsDel == 0)
  69. // .AnyAsync();
  70. //if (!groupIsValid)
  71. //{
  72. // return new Result(400, "团组无效,无法查询签证流程步骤。");
  73. //}
  74. var query = await _sqlSugar.Queryable<Grp_VisaProcessSteps>()
  75. .Where(s => s.GroupId == dto.GroupId && s.IsDel == 0)
  76. .OrderBy(s => s.Step)
  77. .ToListAsync();
  78. var infos = query.Select(s => new
  79. {
  80. s.Id,
  81. s.GroupId,
  82. s.Step,
  83. s.DataType,
  84. s.TypedValue,
  85. //s.StoreVal,
  86. s.IsCompleted,
  87. s.AttachUrl,
  88. s.TypedFileNameValue,
  89. s.Remark
  90. }).ToList();
  91. string msg = "Success";
  92. //如果不存在,则返回默认数据
  93. if (infos == null || infos.Count < 1)
  94. {
  95. infos = Grp_VisaProcessSteps.StepsInit(dto.GroupId, 208)
  96. .Select(s => new {
  97. s.Id,
  98. s.GroupId,
  99. s.Step,
  100. s.DataType,
  101. s.TypedValue,
  102. //s.StoreVal,
  103. s.IsCompleted,
  104. s.AttachUrl,
  105. s.TypedFileNameValue,
  106. s.Remark
  107. }).ToList();
  108. msg = "签证流程步骤信息不存在,展示默认数据。";
  109. }
  110. //数据按照类型处理
  111. var datas = new List<object>();
  112. var view = new VisaProcessStepsInfoView();
  113. foreach (var item in infos)
  114. {
  115. if (item.DataType == "string")
  116. {
  117. var info = new VisaProcessStepsInfoByStringView
  118. {
  119. Id = item.Id,
  120. GroupId = item.GroupId,
  121. Step = item.Step,
  122. DataType = item.DataType,
  123. TypedValue = item.TypedValue == null ? "" : item.TypedValue.ToString(),
  124. //StoreVal = item.StoreVal,
  125. IsCompleted = item.IsCompleted,
  126. AttachUrl = item.TypedFileNameValue,
  127. Remark = item.Remark
  128. };
  129. if (item.Step == 1) view.Step1 = info;
  130. else if (item.Step == 2) view.Step2 = info;
  131. else if (item.Step == 3) view.Step3 = info;
  132. else if (item.Step == 4) view.Step4 = info;
  133. else if (item.Step == 5) view.Step5 = info;
  134. else if (item.Step == 8) view.Step8 = info;
  135. datas.Add(info);
  136. }
  137. else if (item.DataType == "bool")
  138. {
  139. bool boolVal = false;
  140. if (item.TypedValue != null)
  141. {
  142. boolVal = (bool)item.TypedValue;
  143. }
  144. var info = new VisaProcessStepsInfoByBoolView
  145. {
  146. Id = item.Id,
  147. GroupId = item.GroupId,
  148. Step = item.Step,
  149. DataType = item.DataType,
  150. TypedValue = boolVal,
  151. IsCompleted = item.IsCompleted,
  152. AttachUrl = item.TypedFileNameValue,
  153. Remark = item.Remark
  154. };
  155. if (item.Step == 6) view.Step6 = info;
  156. datas.Add(info);
  157. }
  158. else if (item.DataType == "string[]")
  159. {
  160. var listVal = new List<string>();
  161. var contnet = new VisaProcessSteps7Content() { IsSelected = false };
  162. if (item.TypedValue != null)
  163. {
  164. listVal = item.TypedValue switch
  165. {
  166. List<string> stringList => stringList,
  167. IEnumerable<string> stringEnumerable => stringEnumerable.ToList(),
  168. string str => new List<string> { str },
  169. IEnumerable<object> objectEnumerable => objectEnumerable.Select(x => x?.ToString() ?? "").ToList(),
  170. _ => new List<string> { item.TypedValue.ToString() ?? "" }
  171. };
  172. if (listVal != null && listVal.Count > 0)
  173. {
  174. if (bool.TryParse(CommonFun.GetValueOrDefault(listVal, 0), out bool val))
  175. {
  176. contnet.IsSelected = val;
  177. }
  178. contnet.ProjectedDate = CommonFun.GetValueOrDefault(listVal, 1);
  179. }
  180. }
  181. var info = new VisaProcessStepsInfoByListView
  182. {
  183. Id = item.Id,
  184. GroupId = item.GroupId,
  185. Step = item.Step,
  186. DataType = item.DataType,
  187. TypedValue = contnet,
  188. IsCompleted = item.IsCompleted,
  189. AttachUrl = item.TypedFileNameValue,
  190. Remark = item.Remark
  191. };
  192. if (item.Step == 7) view.Step7 = info;
  193. datas.Add(info);
  194. }
  195. }
  196. if (dto.PortType == 2 )
  197. {
  198. return new Result(200, msg, infos);
  199. }
  200. return new Result(200, msg, view);
  201. }
  202. /// <summary>
  203. /// 修改签证流程步骤
  204. /// </summary>
  205. /// <param name="groupId"></param>
  206. /// <param name="createUderId"></param>
  207. /// <returns></returns>
  208. public async Task<Result> Update(Grp_VisaProcessSteps info)
  209. {
  210. var step = await _sqlSugar.Queryable<Grp_VisaProcessSteps>().FirstAsync(x => x.Id == info.Id);
  211. var before = ManualClone(step);
  212. step.StoreVal = info.StoreVal;
  213. step.LastUpdateUserId = info.LastUpdateUserId;
  214. step.LastUpdateTime = DateTime.Now;
  215. step.Remark = info.Remark;
  216. var update = await _sqlSugar.Updateable<Grp_VisaProcessSteps>(step)
  217. .Where(s => s.Id == info.Id && s.IsDel == 0)
  218. .ExecuteCommandAsync();
  219. if (update < 1) return new Result(400, "更新失败!");
  220. // 记录更新日志
  221. await LogOperationAsync(before, step, "Update", info.LastUpdateUserId);
  222. return new Result(200, "Success");
  223. }
  224. /// <summary>
  225. /// 批量修改top4Steps
  226. /// </summary>
  227. /// <param name="infos"></param>
  228. /// <returns></returns>
  229. public async Task<Result> UpdateBatchTop4Step(int groupId,int currUserId)
  230. {
  231. var steps = new List<int>() {
  232. 1, //签证启动日
  233. 2, //分配工作
  234. 3, //送外办时间
  235. 4, //预计出签时间
  236. };
  237. var visaSteps = await _sqlSugar.Queryable<Grp_VisaProcessSteps>()
  238. .Where(s => s.GroupId == groupId && s.IsDel == 0 && steps.Contains(s.Step))
  239. .ToListAsync();
  240. if (visaSteps.Count < 1) return new Result(400, "签证流程信息不存在。");
  241. //倒推表信息
  242. var invertedInfo = await _sqlSugar.Queryable<Grp_InvertedList>()
  243. .Where(i => i.DiId == groupId && i.IsDel == 0)
  244. .FirstAsync();
  245. if (invertedInfo == null) return new Result(400, "倒推表信息不存在。");
  246. //步骤更改前的值
  247. var beforeStep1 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 1));
  248. var beforeStep2 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 2));
  249. var beforeStep3 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 3));
  250. var beforeStep4 = ManualClone(visaSteps.FirstOrDefault(x => x.Step == 4));
  251. //设置操作人和时间
  252. foreach (var item in visaSteps)
  253. {
  254. //默认确认完成
  255. item.IsCompleted = true;
  256. item.LastUpdateUserId = currUserId;
  257. item.LastUpdateTime = DateTime.Now;
  258. }
  259. //step=1 设置启动日值
  260. visaSteps.FirstOrDefault(x => x.Step == 1).TypedValue = DateTime.Now.ToString("yyyy-MM-dd");
  261. //step=2 分配工作
  262. visaSteps.FirstOrDefault(x => x.Step == 2).TypedValue = invertedInfo.IssueApprovalDt;
  263. //step=3 送外办时间
  264. visaSteps.FirstOrDefault(x => x.Step == 3).TypedValue = invertedInfo.SendVisaDt;
  265. //step=4 预计出签时间
  266. visaSteps.FirstOrDefault(x => x.Step == 4).TypedValue = invertedInfo.IssueVisaDt;
  267. var update = await _sqlSugar.Updateable(visaSteps)
  268. .UpdateColumns(it => new { it.IsCompleted, it.StoreVal, it.LastUpdateUserId, it.LastUpdateTime })
  269. .ExecuteCommandAsync();
  270. if (update < 1) return new Result(400, "更新失败!");
  271. //更改后的值
  272. var afterStep1 = visaSteps.FirstOrDefault(x => x.Step == 1);
  273. var afterStep2 = visaSteps.FirstOrDefault(x => x.Step == 2);
  274. var afterStep3 = visaSteps.FirstOrDefault(x => x.Step == 3);
  275. var afterStep4 = visaSteps.FirstOrDefault(x => x.Step == 4);
  276. // 记录更新日志
  277. await LogOperationAsync(beforeStep1, afterStep1, "Update", currUserId);
  278. await LogOperationAsync(beforeStep2, afterStep2, "Update", currUserId);
  279. await LogOperationAsync(beforeStep3, afterStep3, "Update", currUserId);
  280. await LogOperationAsync(beforeStep4, afterStep4, "Update", currUserId);
  281. return new Result(200, "Success");
  282. }
  283. /// <summary>
  284. /// 设置状态步骤 完成/未完成
  285. /// </summary>
  286. /// <param name="id"></param>
  287. /// <param name="currUserId"></param>
  288. /// <param name="isCompleted"></param>
  289. /// <returns></returns>
  290. public async Task<Result> SetCompleted(int id, int currUserId, bool isCompleted = true)
  291. {
  292. //步骤信息验证
  293. var stepInfo = await _sqlSugar.Queryable<Grp_VisaProcessSteps>()
  294. .Where(s => s.Id == id && s.IsDel == 0)
  295. .FirstAsync();
  296. if (stepInfo == null) return new Result(400, "步骤信息不存在,无法设置。");
  297. if (stepInfo.IsCompleted == isCompleted) return new Result(400, $"步骤信息已设置,无法重复设置。");
  298. var before = ManualClone(stepInfo);
  299. stepInfo.IsCompleted = isCompleted;
  300. stepInfo.LastUpdateUserId = currUserId;
  301. stepInfo.LastUpdateTime = DateTime.Now;
  302. var update = await _sqlSugar.Updateable<Grp_VisaProcessSteps>()
  303. .SetColumns(s => new Grp_VisaProcessSteps
  304. {
  305. IsCompleted = isCompleted,
  306. LastUpdateUserId = currUserId,
  307. LastUpdateTime = DateTime.Now
  308. })
  309. .Where(s => s.Id == id && s.IsDel == 0)
  310. .Where(s => s.IsCompleted != isCompleted) // 只有状态变化时才更新
  311. .ExecuteCommandAsync();
  312. if (update < 1) return new Result(400, "状态设置失败!");
  313. // 记录更新日志
  314. await LogOperationAsync(before, stepInfo, "Update", currUserId);
  315. return new Result(200, "Success");
  316. }
  317. #region 操作日志
  318. /// <summary>
  319. /// 记录签证流程步骤的操作日志
  320. /// </summary>
  321. /// <param name="before">操作前的步骤数据实体</param>
  322. /// <param name="after">操作后的步骤数据实体</param>
  323. /// <param name="opType">操作类型(Create-创建, Update-更新, Delete-删除, Complete-完成, Upload-上传附件, Download-下载文件)</param>
  324. /// <param name="operatorId">操作人用户ID</param>
  325. /// <returns>表示异步操作的任务</returns>
  326. /// <remarks>此方法会自动比较前后数据的差异,生成变更字段列表和操作描述</remarks>
  327. public async Task LogOperationAsync(Grp_VisaProcessSteps before, Grp_VisaProcessSteps after,string opType, int operatorId)
  328. {
  329. // 合并基础排除字段和额外排除字段
  330. var allExcludedFields = GetDefaultExcludedFields();
  331. var changedFields = GetChangeDetails(before, after, allExcludedFields);
  332. var log = new Grp_VisaProcessSteps_Log
  333. {
  334. StepId = after?.Id ?? before?.Id ?? 0,
  335. GroupId = after?.GroupId ?? before?.GroupId ?? 0,
  336. Step = after?.Step ?? before?.Step ?? 0,
  337. OperationType = opType,
  338. OperationDescription = GenerateOpDesc(opType, before, after, changedFields),
  339. BeforeData = before != null ? JsonSerializer.Serialize(before, new JsonSerializerOptions
  340. {
  341. ReferenceHandler = ReferenceHandler.IgnoreCycles,
  342. WriteIndented = false
  343. }) : null,
  344. AfterData = after != null ? JsonSerializer.Serialize(after, new JsonSerializerOptions
  345. {
  346. ReferenceHandler = ReferenceHandler.IgnoreCycles,
  347. WriteIndented = false
  348. }) : null,
  349. ChangedFields = string.Join(",", changedFields),
  350. CreateUserId = operatorId,
  351. };
  352. await _sqlSugar.Insertable(log).ExecuteCommandAsync();
  353. }
  354. /// <summary>
  355. /// 获取字段变更详情
  356. /// </summary>
  357. /// <param name="before">变更前</param>
  358. /// <param name="after">变更后</param>
  359. /// <param name="exclFields">排除字段</param>
  360. /// <returns>变更详情列表</returns>
  361. private List<FieldChangeDetail> GetChangeDetails(Grp_VisaProcessSteps before, Grp_VisaProcessSteps after, List<string> exclFields = null)
  362. {
  363. var changeDetails = new List<FieldChangeDetail>();
  364. var defaultExclFields = GetDefaultExcludedFields();
  365. // 合并排除字段
  366. var allExclFields = defaultExclFields;
  367. if (exclFields != null && exclFields.Any())
  368. {
  369. allExclFields = defaultExclFields.Union(exclFields).ToList();
  370. }
  371. if (before == null || after == null) return changeDetails;
  372. var properties = typeof(Grp_VisaProcessSteps).GetProperties(BindingFlags.Public | BindingFlags.Instance);
  373. foreach (var prop in properties)
  374. {
  375. // 跳过排除的字段
  376. if (allExclFields.Contains(prop.Name))
  377. {
  378. continue;
  379. }
  380. var beforeValue = prop.GetValue(before);
  381. var afterValue = prop.GetValue(after);
  382. // 处理字符串类型的特殊比较(忽略前后空格)
  383. if (prop.PropertyType == typeof(string))
  384. {
  385. var beforeString = beforeValue?.ToString()?.Trim();
  386. var afterString = afterValue?.ToString()?.Trim();
  387. if (beforeString != afterString)
  388. {
  389. changeDetails.Add(new FieldChangeDetail
  390. {
  391. FieldName = prop.Name,
  392. BeforeValue = FormatValue(beforeString),
  393. AfterValue = FormatValue(afterString)
  394. });
  395. }
  396. }
  397. else
  398. {
  399. // 其他类型使用默认比较
  400. if (!Equals(beforeValue, afterValue))
  401. {
  402. changeDetails.Add(new FieldChangeDetail
  403. {
  404. FieldName = prop.Name,
  405. BeforeValue = FormatValue(beforeValue),
  406. AfterValue = FormatValue(afterValue)
  407. });
  408. }
  409. }
  410. }
  411. return changeDetails;
  412. }
  413. /// <summary>
  414. /// 格式化值显示
  415. /// </summary>
  416. /// <param name="value">原始值</param>
  417. /// <returns>格式化后的值</returns>
  418. private static string FormatValue(object value)
  419. {
  420. if (value == null) return "null";
  421. var strValue = value.ToString();
  422. // 处理空字符串
  423. if (string.IsNullOrEmpty(strValue)) return "空";
  424. // 处理布尔值
  425. if (value is bool boolValue) return boolValue ? "是" : "否";
  426. // 处理日期时间
  427. if (value is DateTime dateTimeValue) return dateTimeValue.ToString("yyyy-MM-dd HH:mm");
  428. // 处理长文本截断
  429. if (strValue.Length > 50) return strValue.Substring(0, 47) + "...";
  430. return strValue;
  431. }
  432. /// <summary>
  433. /// 生成操作描述
  434. /// </summary>
  435. /// <param name="opType">操作类型</param>
  436. /// <param name="before">操作前</param>
  437. /// <param name="after">操作后</param>
  438. /// <param name="chgDetails">变更详情</param>
  439. /// <returns>操作描述</returns>
  440. private static string GenerateOpDesc(string opType, Grp_VisaProcessSteps before,
  441. Grp_VisaProcessSteps after, List<FieldChangeDetail> chgDetails)
  442. {
  443. if (!chgDetails.Any())
  444. {
  445. return opType switch
  446. {
  447. "Create" => $"创建步骤:团组{after.GroupId}-步骤{after.Step}",
  448. "Update" => $"更新步骤:无变更",
  449. "Complete" => $"完成步骤:团组{after.GroupId}-步骤{after.Step}",
  450. "Uncomplete" => $"取消完成:团组{after.GroupId}-步骤{after.Step}",
  451. "Delete" => $"删除步骤:团组{before.GroupId}-步骤{before.Step}",
  452. "Upload" => $"上传附件:团组{after.GroupId}-步骤{after.Step}",
  453. _ => $"{opType}:团组{after?.GroupId ?? before?.GroupId}-步骤{after?.Step ?? before?.Step}"
  454. };
  455. }
  456. var changeDesc = string.Join("; ", chgDetails.Select(x =>
  457. $"{x.FieldName} ({x.BeforeValue} -> {x.AfterValue})"));
  458. return opType switch
  459. {
  460. "Create" => $"创建步骤:团组{after.GroupId}-步骤{after.Step}",
  461. "Update" => $"更新步骤:{changeDesc}",
  462. "Complete" => $"完成步骤:团组{after.GroupId}-步骤{after.Step}",
  463. "Uncomplete" => $"取消完成:团组{after.GroupId}-步骤{after.Step}",
  464. "Delete" => $"删除步骤:团组{before.GroupId}-步骤{before.Step}",
  465. "Upload" => $"上传附件:团组{after.GroupId}-步骤{after.Step}",
  466. _ => $"{opType}:{changeDesc}"
  467. };
  468. }
  469. /// <summary>
  470. /// 字段变更详情类
  471. /// </summary>
  472. private class FieldChangeDetail
  473. {
  474. public string FieldName { get; set; }
  475. public string BeforeValue { get; set; }
  476. public string AfterValue { get; set; }
  477. }
  478. /// <summary>
  479. /// 获取默认排除的字段列表(包含系统字段和忽略字段)
  480. /// </summary>
  481. /// <returns>默认排除的字段列表</returns>
  482. private static List<string> GetDefaultExcludedFields()
  483. {
  484. var defaultExcludedFields = new List<string>
  485. {
  486. nameof(Grp_VisaProcessSteps.Id),
  487. nameof(Grp_VisaProcessSteps.CreateTime),
  488. nameof(Grp_VisaProcessSteps.CreateUserId),
  489. //nameof(Grp_VisaProcessSteps.LastUpdateTime),
  490. //nameof(Grp_VisaProcessSteps.LastUpdateUserId),
  491. // 计算字段(原本标记为 [SugarColumn(IsIgnore = true)] 的字段)
  492. "TypedFileNameValue",
  493. "TypedValue",
  494. "StringValue",
  495. "IntValue",
  496. "DecimalValue",
  497. "BooleanValue",
  498. "DateTimeValue"
  499. };
  500. return defaultExcludedFields.Distinct().ToList();
  501. }
  502. /// <summary>
  503. /// 手动创建步骤实体的副本
  504. /// </summary>
  505. /// <param name="source">源步骤实体</param>
  506. /// <returns>新的步骤实体副本</returns>
  507. private Grp_VisaProcessSteps ManualClone(Grp_VisaProcessSteps source)
  508. {
  509. if (source == null) return null;
  510. return new Grp_VisaProcessSteps
  511. {
  512. Id = source.Id,
  513. GroupId = source.GroupId,
  514. Step = source.Step,
  515. DataType = source.DataType,
  516. StoreVal = source.StoreVal,
  517. AttachUrl = source.AttachUrl,
  518. IsCompleted = source.IsCompleted,
  519. LastUpdateUserId = source.LastUpdateUserId,
  520. LastUpdateTime = source.LastUpdateTime,
  521. CreateUserId = source.CreateUserId,
  522. CreateTime = source.CreateTime
  523. // 注意:这里不拷贝计算属性(TypedValue 等),因为它们会被重新计算
  524. };
  525. }
  526. /// <summary>
  527. /// 根据步骤记录ID获取该步骤的所有操作日志
  528. /// </summary>
  529. /// <param name="stepId">步骤记录的主键ID</param>
  530. /// <returns>步骤操作日志列表,按操作时间倒序排列</returns>
  531. public async Task<List<VisaProcessStepsLogView>> GetStepLogsAsync(int stepId)
  532. {
  533. return await _sqlSugar.Queryable<Grp_VisaProcessSteps_Log>()
  534. .LeftJoin<Sys_Users>((x, y) => x.CreateUserId == y.Id)
  535. .Where((x, y) => x.StepId == stepId)
  536. .Select((x, y) => new VisaProcessStepsLogView
  537. {
  538. StepId = x.StepId,
  539. GroupId = x.GroupId,
  540. Step = x.Step,
  541. OperationType = x.OperationType,
  542. OperationDescription = x.OperationDescription,
  543. Operator = y.CnName,
  544. OperationTime = x.CreateTime,
  545. })
  546. .OrderByDescending(x => x.OperationTime)
  547. .ToListAsync();
  548. }
  549. /// <summary>
  550. /// 根据团组ID获取该团组下所有步骤的操作日志
  551. /// </summary>
  552. /// <param name="groupId">团组ID</param>
  553. /// <returns>团组步骤操作日志列表,按操作时间倒序排列</returns>
  554. public async Task<List<VisaProcessStepsLogView>> GetGroupStepLogsAsync(int groupId)
  555. {
  556. return await _sqlSugar.Queryable<Grp_VisaProcessSteps_Log>()
  557. .LeftJoin<Sys_Users>((x, y) => x.CreateUserId == y.Id)
  558. .Where((x, y) => x.GroupId == groupId)
  559. .Select((x, y) => new VisaProcessStepsLogView
  560. {
  561. StepId = x.StepId,
  562. GroupId = x.GroupId,
  563. Step = x.Step,
  564. OperationType = x.OperationType,
  565. OperationDescription = x.OperationDescription,
  566. Operator = y.CnName,
  567. OperationTime = x.CreateTime,
  568. })
  569. .OrderByDescending(x => x.OperationTime)
  570. .ToListAsync();
  571. }
  572. #endregion
  573. }
  574. }