Commit 27a753722437d951242ce15ba1ec3754d2c61387

Authored by “wangming”
1 parent 25c153f3

Enhance attendance record detail view by adding related workflows display for le…

…ave and sick statuses. Refactor logic to determine related workflows and update UI components for better clarity. Introduce new methods for handling workflow data in the backend.
antis-ncc-admin/src/views/attendance-record/components/record-detail-dialog.vue
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 <div class="detail-hero__title-row"> 8 <div class="detail-hero__title-row">
9 <div class="detail-hero__title">{{ detail.employeeName || '无' }}</div> 9 <div class="detail-hero__title">{{ detail.employeeName || '无' }}</div>
10 <el-tag class="detail-hero__status-tag" :type="statusTagType" effect="dark">{{ detail.statusText || '无' 10 <el-tag class="detail-hero__status-tag" :type="statusTagType" effect="dark">{{ detail.statusText || '无'
11 - }}</el-tag> 11 + }}</el-tag>
12 </div> 12 </div>
13 <div class="detail-hero__meta"> 13 <div class="detail-hero__meta">
14 <span>{{ detail.attendanceDate || '无' }}</span> 14 <span>{{ detail.attendanceDate || '无' }}</span>
@@ -144,33 +144,27 @@ @@ -144,33 +144,27 @@
144 </div> 144 </div>
145 </el-card> 145 </el-card>
146 146
147 - <el-card v-if="detail && detail.leaveApply" shadow="never" class="leave-apply-card"> 147 + <el-card v-if="hasRelatedWorkflows || isLeaveOrSickStatus" shadow="never" class="supplement-card workflow-card">
148 <div slot="header" class="supplement-card__header"> 148 <div slot="header" class="supplement-card__header">
149 - <span>请假/病假流程</span> 149 + <span>关联流程</span>
  150 + <el-button v-if="isLeaveOrSickStatus" type="danger" size="mini" :loading="cancelLoading"
  151 + :disabled="cancelLoading" style="float: right; margin-top: -4px;" @click="cancelLeave">确认销假</el-button>
150 </div> 152 </div>
151 - <div class="supplement-grid">  
152 - <div class="field-item">  
153 - <label>请假类型</label>  
154 - <span>{{ detail.leaveApply.leaveType || '无' }}</span>  
155 - </div>  
156 - <div class="field-item">  
157 - <label>流程单号</label>  
158 - <span>{{ detail.leaveApply.billNo || '无' }}</span>  
159 - </div>  
160 - <div class="field-item field-item--full">  
161 - <label>流程操作</label>  
162 - <span class="action-link">  
163 - <el-button type="text" size="mini" icon="el-icon-view" :disabled="!detail.leaveApply.id"  
164 - @click="goToLeaveApply">查看申请信息</el-button>  
165 - </span>  
166 - </div>  
167 - <div class="field-item field-item--full">  
168 - <label>销假</label>  
169 - <el-button type="danger" size="mini" :loading="cancelLoading"  
170 - :disabled="cancelLoading || !(detail.status === 4 || detail.status === 5)"  
171 - @click="cancelLeave">确认销假</el-button> 153 + <div v-if="hasRelatedWorkflows" class="workflow-list">
  154 + <div v-for="(wf, idx) in detail.relatedWorkflows" :key="idx" class="workflow-item">
  155 + <el-tag size="mini" :type="workflowTagType(wf.type)" effect="plain" class="workflow-item__tag">
  156 + {{ wf.type }}
  157 + </el-tag>
  158 + <span v-if="wf.leaveType" class="workflow-item__leave-type">{{ wf.leaveType }}</span>
  159 + <span v-if="wf.billNo" class="workflow-item__bill">{{ wf.billNo }}</span>
  160 + <span v-if="wf.time" class="workflow-item__time">{{ wf.time }}</span>
  161 + <el-button v-if="wf.id && wf.type === '请假'" type="text" size="mini" icon="el-icon-view"
  162 + @click="goToLeaveApply(wf)">查看申请</el-button>
172 </div> 163 </div>
173 </div> 164 </div>
  165 + <div v-else class="workflow-empty">
  166 + <span>当前为{{ detail.statusText }}状态,暂无关联流程记录</span>
  167 + </div>
174 </el-card> 168 </el-card>
175 </template> 169 </template>
176 170
@@ -219,6 +213,12 @@ export default { @@ -219,6 +213,12 @@ export default {
219 default: 213 default:
220 return 'danger' 214 return 'danger'
221 } 215 }
  216 + },
  217 + hasRelatedWorkflows() {
  218 + return this.detail && Array.isArray(this.detail.relatedWorkflows) && this.detail.relatedWorkflows.length > 0
  219 + },
  220 + isLeaveOrSickStatus() {
  221 + return this.detail && (this.detail.status === 4 || this.detail.status === 5)
222 } 222 }
223 }, 223 },
224 methods: { 224 methods: {
@@ -265,33 +265,33 @@ export default { @@ -265,33 +265,33 @@ export default {
265 await this.loadDetail() 265 await this.loadDetail()
266 this.$emit('refresh') 266 this.$emit('refresh')
267 }, 267 },
268 - goToLeaveApply() {  
269 - if (!this.detail || !this.detail.leaveApply || !this.detail.leaveApply.id) return  
270 - const leaveType = this.detail.leaveApply.leaveType || ''  
271 - const isPersonal = leaveType === '事假' || leaveType === '病假' 268 + workflowTagType(type) {
  269 + if (type === '请假') return 'danger'
  270 + if (type === '补卡') return 'warning'
  271 + return 'info'
  272 + },
  273 + goToLeaveApply(wf) {
  274 + if (!wf || !wf.id) return
  275 + const lt = wf.leaveType || ''
  276 + const isPersonal = lt === '事假' || lt === '病假'
272 const path = isPersonal ? '/workFlow/personal-leave-apply' : '/workFlow/paid-leave-apply' 277 const path = isPersonal ? '/workFlow/personal-leave-apply' : '/workFlow/paid-leave-apply'
273 - this.$router.push({  
274 - path,  
275 - query: { id: this.detail.leaveApply.id }  
276 - }) 278 + this.$router.push({ path, query: { id: wf.id } })
277 }, 279 },
278 async cancelLeave() { 280 async cancelLeave() {
279 if (!this.detail || !this.detail.id) return 281 if (!this.detail || !this.detail.id) return
280 - if (!(this.detail.status === 4 || this.detail.status === 5)) { 282 + if (!this.isLeaveOrSickStatus) {
281 this.$message.warning('当前考勤记录不是请假/病假状态,无法销假') 283 this.$message.warning('当前考勤记录不是请假/病假状态,无法销假')
282 return 284 return
283 } 285 }
284 this.cancelLoading = true 286 this.cancelLoading = true
285 try { 287 try {
286 - await this.$confirm('确定要对该考勤记录销假吗?', '提示', { type: 'warning' })  
287 - await cancelAttendanceLeave({  
288 - id: this.detail.id  
289 - }) 288 + await this.$confirm('确定要对该考勤记录销假吗?销假后将根据打卡记录重新判定考勤状态。', '确认销假', { type: 'warning' })
  289 + await cancelAttendanceLeave({ id: this.detail.id })
290 this.$message.success('销假成功') 290 this.$message.success('销假成功')
291 await this.loadDetail() 291 await this.loadDetail()
292 this.$emit('refresh') 292 this.$emit('refresh')
293 } catch (e) { 293 } catch (e) {
294 - // eslint-disable-next-line no-empty 294 + // user cancelled or api error
295 } finally { 295 } finally {
296 this.cancelLoading = false 296 this.cancelLoading = false
297 } 297 }
@@ -496,6 +496,53 @@ export default { @@ -496,6 +496,53 @@ export default {
496 grid-column: 1 / -1; 496 grid-column: 1 / -1;
497 } 497 }
498 498
  499 +.workflow-card {
  500 + margin-top: 16px;
  501 +}
  502 +
  503 +.workflow-list {
  504 + display: flex;
  505 + flex-direction: column;
  506 + gap: 10px;
  507 +}
  508 +
  509 +.workflow-item {
  510 + display: flex;
  511 + align-items: center;
  512 + gap: 10px;
  513 + padding: 8px 12px;
  514 + border-radius: 8px;
  515 + background: #f8f9fb;
  516 + font-size: 13px;
  517 +}
  518 +
  519 +.workflow-item__tag {
  520 + flex-shrink: 0;
  521 +}
  522 +
  523 +.workflow-item__leave-type {
  524 + color: #606266;
  525 + font-weight: 500;
  526 +}
  527 +
  528 +.workflow-item__bill {
  529 + color: #409eff;
  530 + font-family: monospace;
  531 +}
  532 +
  533 +.workflow-item__time {
  534 + color: #909399;
  535 + font-size: 12px;
  536 + margin-left: auto;
  537 +}
  538 +
  539 +.workflow-empty {
  540 + color: #909399;
  541 + font-size: 13px;
  542 + text-align: center;
  543 + padding: 12px 0;
  544 +}
  545 +
499 @media (max-width: 960px) { 546 @media (max-width: 960px) {
500 547
501 .overview-grid, 548 .overview-grid,
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqAttendanceRecord/RelatedWorkflowItem.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqAttendanceRecord
  2 +{
  3 + /// <summary>
  4 + /// 考勤记录关联流程项(存储在 F_RelatedWorkflowsJson 字段中的数组元素)
  5 + /// </summary>
  6 + public class RelatedWorkflowItem
  7 + {
  8 + /// <summary>
  9 + /// 流程类型:请假 | 补卡
  10 + /// </summary>
  11 + public string type { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 流程/表单主键(WFORM_LEAVEAPPLY.F_Id 等)
  15 + /// </summary>
  16 + public string id { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 单号(如 YGQJ-202604020008)
  20 + /// </summary>
  21 + public string billNo { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 假种(仅请假类型有值,如"年假"/"事假"/"病假")
  25 + /// </summary>
  26 + public string leaveType { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 备注
  30 + /// </summary>
  31 + public string remark { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 写入时间
  35 + /// </summary>
  36 + public string time { get; set; }
  37 + }
  38 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_attendance_record/LqAttendanceRecordEntity.cs
@@ -240,6 +240,12 @@ namespace NCC.Extend.Entitys.lq_attendance_record @@ -240,6 +240,12 @@ namespace NCC.Extend.Entitys.lq_attendance_record
240 public DateTime? UpdateTime { get; set; } 240 public DateTime? UpdateTime { get; set; }
241 241
242 /// <summary> 242 /// <summary>
  243 + /// 关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)
  244 + /// </summary>
  245 + [SugarColumn(ColumnName = "F_RelatedWorkflowsJson", ColumnDataType = "longtext")]
  246 + public string RelatedWorkflowsJson { get; set; }
  247 +
  248 + /// <summary>
243 /// 是否有效(1-有效,0-无效) 249 /// 是否有效(1-有效,0-无效)
244 /// </summary> 250 /// </summary>
245 [SugarColumn(ColumnName = "F_IsEffective")] 251 [SugarColumn(ColumnName = "F_IsEffective")]
netcore/src/Modularity/Extend/NCC.Extend/LqAttendanceRecordService.cs
@@ -25,6 +25,7 @@ using NCC.Extend.Entitys.lq_attendance_missing_card_rule; @@ -25,6 +25,7 @@ using NCC.Extend.Entitys.lq_attendance_missing_card_rule;
25 using NCC.Extend.Entitys.lq_attendance_record; 25 using NCC.Extend.Entitys.lq_attendance_record;
26 using NCC.Extend.Entitys.lq_attendance_setting; 26 using NCC.Extend.Entitys.lq_attendance_setting;
27 using NCC.Extend.Entitys.lq_mdxx; 27 using NCC.Extend.Entitys.lq_mdxx;
  28 +using NCC.Extend.Entitys.wform_leaveapply;
28 using NCC.Extend.Interfaces.LqAttendanceRecord; 29 using NCC.Extend.Interfaces.LqAttendanceRecord;
29 using NCC.FriendlyException; 30 using NCC.FriendlyException;
30 using Newtonsoft.Json; 31 using Newtonsoft.Json;
@@ -361,24 +362,41 @@ namespace NCC.Extend @@ -361,24 +362,41 @@ namespace NCC.Extend
361 record = recalculated.FirstOrDefault() ?? record; 362 record = recalculated.FirstOrDefault() ?? record;
362 var group = await GetAttendanceGroupAsync(record.AttendanceGroupId, false); 363 var group = await GetAttendanceGroupAsync(record.AttendanceGroupId, false);
363 364
364 - // 如果当前状态来自“请假/病假同步写入”,则尝试从 Remark 中解析单号并反查请假单信息,便于在考勤详情中跳转流程/操作销假  
365 - string leaveApplyId = null;  
366 - string leaveApplyBillNo = null;  
367 - string leaveApplyType = null; 365 + var relatedWorkflows = ParseRelatedWorkflows(record.RelatedWorkflowsJson);
368 366
369 - if (TryParseLeaveApplyFromSyncRemark(record.Remark, out var parsedLeaveType, out var parsedBillNo) 367 + var isLeaveStatus = record.Status == (int)AttendanceRecordStatusEnum.请假
  368 + || record.Status == (int)AttendanceRecordStatusEnum.病假;
  369 + if (!relatedWorkflows.Any(x => x.type == "请假") && isLeaveStatus
  370 + && TryParseLeaveApplyFromSyncRemark(record.Remark, out var parsedLeaveType, out var parsedBillNo)
370 && !string.IsNullOrWhiteSpace(parsedBillNo)) 371 && !string.IsNullOrWhiteSpace(parsedBillNo))
371 { 372 {
372 - leaveApplyBillNo = parsedBillNo;  
373 - leaveApplyType = parsedLeaveType;  
374 - var leaveApply = await _db.Queryable<NCC.Extend.Entitys.wform_leaveapply.WformLeaveApplyEntityLite>() 373 + var leaveApply = await _db.Queryable<WformLeaveApplyEntityLite>()
375 .Where(x => x.BillNo == parsedBillNo) 374 .Where(x => x.BillNo == parsedBillNo)
376 .WhereIF(!string.IsNullOrWhiteSpace(parsedLeaveType), x => x.LeaveType == parsedLeaveType) 375 .WhereIF(!string.IsNullOrWhiteSpace(parsedLeaveType), x => x.LeaveType == parsedLeaveType)
377 .FirstAsync(); 376 .FirstAsync();
378 - leaveApplyId = leaveApply?.Id; 377 +
  378 + relatedWorkflows.Add(new RelatedWorkflowItem
  379 + {
  380 + type = "请假",
  381 + id = leaveApply?.Id,
  382 + billNo = parsedBillNo,
  383 + leaveType = parsedLeaveType,
  384 + remark = record.Remark
  385 + });
  386 + }
  387 +
  388 + if (!string.IsNullOrWhiteSpace(record.SupplementWorkflowId) && !relatedWorkflows.Any(x => x.type == "补卡"))
  389 + {
  390 + relatedWorkflows.Add(new RelatedWorkflowItem
  391 + {
  392 + type = "补卡",
  393 + id = record.SupplementWorkflowId,
  394 + remark = record.SupplementRemark,
  395 + time = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss")
  396 + });
379 } 397 }
380 398
381 - return BuildDetailResult(record, group, leaveApplyId, leaveApplyBillNo, leaveApplyType); 399 + return BuildDetailResult(record, group, relatedWorkflows);
382 } 400 }
383 401
384 /// <summary> 402 /// <summary>
@@ -411,13 +429,8 @@ namespace NCC.Extend @@ -411,13 +429,8 @@ namespace NCC.Extend
411 throw NCCException.Oh("当前考勤记录不是请假/病假状态,无法销假"); 429 throw NCCException.Oh("当前考勤记录不是请假/病假状态,无法销假");
412 } 430 }
413 431
414 - // 通过 Remark 解析请假单信息(用于展示与记录销假备注)  
415 - if (!TryParseLeaveApplyFromSyncRemark(record.Remark, out var leaveType, out var billNo))  
416 - {  
417 - throw NCCException.Oh("请假备注格式不正确,无法解析销假所需信息");  
418 - } 432 + TryParseLeaveApplyFromSyncRemark(record.Remark, out var leaveType, out var billNo);
419 433
420 - // 撤销对该日期的请假影响:根据假期/免考勤/打卡情况,把该日期恢复为“休息/正常/缺卡”等对应考勤状态  
421 var attendanceDate = record.AttendanceDate.Date; 434 var attendanceDate = record.AttendanceDate.Date;
422 var isHoliday = await IsHolidayAsync(attendanceDate); 435 var isHoliday = await IsHolidayAsync(attendanceDate);
423 var isExempt = await IsExemptAsync(record.UserId, attendanceDate); 436 var isExempt = await IsExemptAsync(record.UserId, attendanceDate);
@@ -445,13 +458,12 @@ namespace NCC.Extend @@ -445,13 +458,12 @@ namespace NCC.Extend
445 record.LateMinutes = 0; 458 record.LateMinutes = 0;
446 record.EarlyLeaveMinutes = 0; 459 record.EarlyLeaveMinutes = 0;
447 record.IsManual = 1; 460 record.IsManual = 1;
448 - var cancelText = string.IsNullOrWhiteSpace(input.OperatorOpinion)  
449 - ? "已销假"  
450 - : input.OperatorOpinion.Trim();  
451 - // 保留原 Remark 前缀,便于在考勤详情中仍能反查到对应请假单信息  
452 - record.Remark = $"请假审批通过:{leaveType}(单号 {billNo});销假:{cancelText}";  
453 -  
454 - // 置空快照,确保 RecalculateRecordStatusesAsync 会重新生成规则快照 461 + var cancelText = string.IsNullOrWhiteSpace(input.OperatorOpinion) ? "已销假" : input.OperatorOpinion.Trim();
  462 + var hasLeaveInfo = !string.IsNullOrWhiteSpace(leaveType) && !string.IsNullOrWhiteSpace(billNo);
  463 + record.Remark = hasLeaveInfo
  464 + ? $"请假审批通过:{leaveType}(单号 {billNo});销假:{cancelText}"
  465 + : $"销假:{cancelText}";
  466 + record.RuleSnapshotJson = null;
455 record.RuleSnapshotJson = null; 467 record.RuleSnapshotJson = null;
456 record.AllRuleSnapshotJson = null; 468 record.AllRuleSnapshotJson = null;
457 469
@@ -616,6 +628,17 @@ namespace NCC.Extend @@ -616,6 +628,17 @@ namespace NCC.Extend
616 record.UpdateUserId = operatorUserId; 628 record.UpdateUserId = operatorUserId;
617 record.UpdateTime = record.SupplementTime; 629 record.UpdateTime = record.SupplementTime;
618 630
  631 + if (!string.IsNullOrWhiteSpace(input.WorkflowId))
  632 + {
  633 + record.RelatedWorkflowsJson = AppendRelatedWorkflow(record.RelatedWorkflowsJson, new RelatedWorkflowItem
  634 + {
  635 + type = "补卡",
  636 + id = input.WorkflowId.Trim(),
  637 + remark = input.Remark?.Trim(),
  638 + time = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss")
  639 + });
  640 + }
  641 +
619 await _db.Updateable(record).ExecuteCommandAsync(); 642 await _db.Updateable(record).ExecuteCommandAsync();
620 643
621 return new 644 return new
@@ -1418,10 +1441,9 @@ namespace NCC.Extend @@ -1418,10 +1441,9 @@ namespace NCC.Extend
1418 private static dynamic BuildDetailResult( 1441 private static dynamic BuildDetailResult(
1419 LqAttendanceRecordEntity record, 1442 LqAttendanceRecordEntity record,
1420 LqAttendanceGroupEntity group, 1443 LqAttendanceGroupEntity group,
1421 - string leaveApplyId = null,  
1422 - string leaveApplyBillNo = null,  
1423 - string leaveApplyType = null) 1444 + List<RelatedWorkflowItem> relatedWorkflows = null)
1424 { 1445 {
  1446 + var workflows = relatedWorkflows ?? new List<RelatedWorkflowItem>();
1425 return new 1447 return new
1426 { 1448 {
1427 id = record.Id, 1449 id = record.Id,
@@ -1472,14 +1494,15 @@ namespace NCC.Extend @@ -1472,14 +1494,15 @@ namespace NCC.Extend
1472 operateTime = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss"), 1494 operateTime = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss"),
1473 workflowId = record.SupplementWorkflowId 1495 workflowId = record.SupplementWorkflowId
1474 }, 1496 },
1475 - leaveApply = string.IsNullOrWhiteSpace(leaveApplyId)  
1476 - ? null  
1477 - : new  
1478 - {  
1479 - id = leaveApplyId,  
1480 - billNo = leaveApplyBillNo,  
1481 - leaveType = leaveApplyType  
1482 - }, 1497 + relatedWorkflows = workflows.Select(w => new
  1498 + {
  1499 + w.type,
  1500 + w.id,
  1501 + w.billNo,
  1502 + w.leaveType,
  1503 + w.remark,
  1504 + w.time
  1505 + }).ToList(),
1483 ruleSnapshotJson = record.RuleSnapshotJson, 1506 ruleSnapshotJson = record.RuleSnapshotJson,
1484 allRuleSnapshotJson = record.AllRuleSnapshotJson 1507 allRuleSnapshotJson = record.AllRuleSnapshotJson
1485 }; 1508 };
@@ -2263,6 +2286,35 @@ namespace NCC.Extend @@ -2263,6 +2286,35 @@ namespace NCC.Extend
2263 return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); 2286 return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
2264 } 2287 }
2265 2288
  2289 + private static List<RelatedWorkflowItem> ParseRelatedWorkflows(string json)
  2290 + {
  2291 + if (string.IsNullOrWhiteSpace(json))
  2292 + {
  2293 + return new List<RelatedWorkflowItem>();
  2294 + }
  2295 +
  2296 + try
  2297 + {
  2298 + return JsonConvert.DeserializeObject<List<RelatedWorkflowItem>>(json) ?? new List<RelatedWorkflowItem>();
  2299 + }
  2300 + catch
  2301 + {
  2302 + return new List<RelatedWorkflowItem>();
  2303 + }
  2304 + }
  2305 +
  2306 + private static string AppendRelatedWorkflow(string existingJson, RelatedWorkflowItem item)
  2307 + {
  2308 + var list = ParseRelatedWorkflows(existingJson);
  2309 + if (!string.IsNullOrWhiteSpace(item.id))
  2310 + {
  2311 + list.RemoveAll(x => x.id == item.id && x.type == item.type);
  2312 + }
  2313 +
  2314 + list.Add(item);
  2315 + return JsonConvert.SerializeObject(list);
  2316 + }
  2317 +
2266 private sealed class AttendanceStatusContext 2318 private sealed class AttendanceStatusContext
2267 { 2319 {
2268 public int Status { get; set; } 2320 public int Status { get; set; }
netcore/src/Modularity/WorkFlow/NCC.WorkFlow/WorkFlowForm/LeaveApplyService.cs
@@ -16,6 +16,7 @@ using NCC.Extend.Entitys.lq_mdxx; @@ -16,6 +16,7 @@ using NCC.Extend.Entitys.lq_mdxx;
16 using NCC.FriendlyException; 16 using NCC.FriendlyException;
17 using NCC.JsonSerialization; 17 using NCC.JsonSerialization;
18 using NCC.System.Entitys.Permission; 18 using NCC.System.Entitys.Permission;
  19 +using Newtonsoft.Json;
19 using NCC.System.Interfaces.System; 20 using NCC.System.Interfaces.System;
20 using NCC.WorkFlow.Entitys; 21 using NCC.WorkFlow.Entitys;
21 using NCC.WorkFlow.Entitys.Dto.WorkFlowForm.LeaveApply; 22 using NCC.WorkFlow.Entitys.Dto.WorkFlowForm.LeaveApply;
@@ -883,6 +884,36 @@ LIMIT 1&quot;; @@ -883,6 +884,36 @@ LIMIT 1&quot;;
883 } 884 }
884 return count; 885 return count;
885 } 886 }
  887 +
  888 + /// <summary>
  889 + /// 将新的请假流程项追加到已有的 RelatedWorkflowsJson(去重后返回新 JSON)
  890 + /// </summary>
  891 + private static string AppendLeaveWorkflow(string existingJson, string newItemArrayJson)
  892 + {
  893 + List<object> list;
  894 + try
  895 + {
  896 + list = string.IsNullOrWhiteSpace(existingJson)
  897 + ? new List<object>()
  898 + : JsonConvert.DeserializeObject<List<object>>(existingJson) ?? new List<object>();
  899 + }
  900 + catch
  901 + {
  902 + list = new List<object>();
  903 + }
  904 +
  905 + try
  906 + {
  907 + var newItems = JsonConvert.DeserializeObject<List<object>>(newItemArrayJson) ?? new List<object>();
  908 + list.AddRange(newItems);
  909 + }
  910 + catch
  911 + {
  912 + // ignore
  913 + }
  914 +
  915 + return JsonConvert.SerializeObject(list);
  916 + }
886 #endregion 917 #endregion
887 918
888 #region PublicMethod 919 #region PublicMethod
@@ -987,6 +1018,11 @@ LIMIT 1&quot;; @@ -987,6 +1018,11 @@ LIMIT 1&quot;;
987 .ToListAsync(); 1018 .ToListAsync();
988 var existing = existingList.FirstOrDefault(); 1019 var existing = existingList.FirstOrDefault();
989 1020
  1021 + var workflowItemJson = JsonConvert.SerializeObject(new[]
  1022 + {
  1023 + new { type = "请假", id = leave.Id, billNo = leave.BillNo ?? "", leaveType, remark, time = now.ToString("yyyy-MM-dd HH:mm:ss") }
  1024 + });
  1025 +
990 if (existing != null) 1026 if (existing != null)
991 { 1027 {
992 existing.Status = status; 1028 existing.Status = status;
@@ -1001,6 +1037,7 @@ LIMIT 1&quot;; @@ -1001,6 +1037,7 @@ LIMIT 1&quot;;
1001 existing.AttendanceGroupName = group?.GroupName; 1037 existing.AttendanceGroupName = group?.GroupName;
1002 existing.RuleSnapshotJson = "{}"; 1038 existing.RuleSnapshotJson = "{}";
1003 existing.AllRuleSnapshotJson = "{}"; 1039 existing.AllRuleSnapshotJson = "{}";
  1040 + existing.RelatedWorkflowsJson = AppendLeaveWorkflow(existing.RelatedWorkflowsJson, workflowItemJson);
1004 existing.UpdateUserId = operatorId; 1041 existing.UpdateUserId = operatorId;
1005 existing.UpdateTime = now; 1042 existing.UpdateTime = now;
1006 await _sqlSugarRepository.Context.Updateable(existing).ExecuteCommandAsync(); 1043 await _sqlSugarRepository.Context.Updateable(existing).ExecuteCommandAsync();
@@ -1024,6 +1061,7 @@ LIMIT 1&quot;; @@ -1024,6 +1061,7 @@ LIMIT 1&quot;;
1024 Remark = remark, 1061 Remark = remark,
1025 RuleSnapshotJson = "{}", 1062 RuleSnapshotJson = "{}",
1026 AllRuleSnapshotJson = "{}", 1063 AllRuleSnapshotJson = "{}",
  1064 + RelatedWorkflowsJson = workflowItemJson,
1027 CreateUserId = operatorId, 1065 CreateUserId = operatorId,
1028 CreateTime = now, 1066 CreateTime = now,
1029 UpdateUserId = operatorId, 1067 UpdateUserId = operatorId,
项目文档相关/docs/数据库说明.md
@@ -168,6 +168,7 @@ @@ -168,6 +168,7 @@
168 - `F_SupplementRemark`: 补卡备注 168 - `F_SupplementRemark`: 补卡备注
169 - `F_SupplementOperatorId` / `F_SupplementOperatorName` / `F_SupplementTime`: 补卡操作信息 169 - `F_SupplementOperatorId` / `F_SupplementOperatorName` / `F_SupplementTime`: 补卡操作信息
170 - `F_SupplementWorkflowId`: 关联补卡流程ID(可空) 170 - `F_SupplementWorkflowId`: 关联补卡流程ID(可空)
  171 + - `F_RelatedWorkflowsJson`: 关联流程 JSON(数组,记录与该日考勤相关的请假/补卡等流程,含类型、表单主键、单号、假种等,便于详情展示与追溯)
171 - **索引说明**: 172 - **索引说明**:
172 - `uk_attendance_record_user_date`: 保证同一员工同一天只有一条有效记录 173 - `uk_attendance_record_user_date`: 保证同一员工同一天只有一条有效记录
173 - `idx_attendance_record_month_group`: 支持按月份、分组快速查询月度矩阵 174 - `idx_attendance_record_month_group`: 支持按月份、分组快速查询月度矩阵
项目文档相关/sql/2026-3-23/创建考勤设置相关表.sql
@@ -225,6 +225,7 @@ CREATE TABLE IF NOT EXISTS `lq_attendance_record` ( @@ -225,6 +225,7 @@ CREATE TABLE IF NOT EXISTS `lq_attendance_record` (
225 `F_SupplementOperatorName` varchar(100) DEFAULT NULL COMMENT '补卡操作人姓名', 225 `F_SupplementOperatorName` varchar(100) DEFAULT NULL COMMENT '补卡操作人姓名',
226 `F_SupplementTime` datetime DEFAULT NULL COMMENT '补卡操作时间', 226 `F_SupplementTime` datetime DEFAULT NULL COMMENT '补卡操作时间',
227 `F_SupplementWorkflowId` varchar(50) DEFAULT NULL COMMENT '关联补卡流程ID', 227 `F_SupplementWorkflowId` varchar(50) DEFAULT NULL COMMENT '关联补卡流程ID',
  228 + `F_RelatedWorkflowsJson` longtext COMMENT '关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)',
228 `F_CreateUserId` varchar(50) DEFAULT NULL COMMENT '创建人ID', 229 `F_CreateUserId` varchar(50) DEFAULT NULL COMMENT '创建人ID',
229 `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', 230 `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
230 `F_UpdateUserId` varchar(50) DEFAULT NULL COMMENT '更新人ID', 231 `F_UpdateUserId` varchar(50) DEFAULT NULL COMMENT '更新人ID',
项目文档相关/sql/2026-3-25/考勤相关表全量重建.sql
@@ -325,6 +325,7 @@ CREATE TABLE `lq_attendance_record` ( @@ -325,6 +325,7 @@ CREATE TABLE `lq_attendance_record` (
325 `F_SupplementOperatorName` varchar(100) DEFAULT NULL COMMENT '补卡操作人姓名', 325 `F_SupplementOperatorName` varchar(100) DEFAULT NULL COMMENT '补卡操作人姓名',
326 `F_SupplementTime` datetime DEFAULT NULL COMMENT '补卡操作时间', 326 `F_SupplementTime` datetime DEFAULT NULL COMMENT '补卡操作时间',
327 `F_SupplementWorkflowId` varchar(50) DEFAULT NULL COMMENT '关联补卡流程ID', 327 `F_SupplementWorkflowId` varchar(50) DEFAULT NULL COMMENT '关联补卡流程ID',
  328 + `F_RelatedWorkflowsJson` longtext COMMENT '关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)',
328 `F_CreateUserId` varchar(50) DEFAULT NULL COMMENT '创建人ID', 329 `F_CreateUserId` varchar(50) DEFAULT NULL COMMENT '创建人ID',
329 `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', 330 `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
330 `F_UpdateUserId` varchar(50) DEFAULT NULL COMMENT '更新人ID', 331 `F_UpdateUserId` varchar(50) DEFAULT NULL COMMENT '更新人ID',
项目文档相关/sql/2026-4-2/考勤记录_新增关联流程JSON字段.sql 0 → 100644
  1 +-- 考勤记录新增关联流程JSON字段:存储所有与该考勤记录关联的流程信息(请假/补卡等)
  2 +-- 执行时间:2026-04-02
  3 +-- 影响表:lq_attendance_record
  4 +
  5 +ALTER TABLE lq_attendance_record
  6 +ADD COLUMN F_RelatedWorkflowsJson LONGTEXT NULL COMMENT '关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)';