Commit 27a753722437d951242ce15ba1ec3754d2c61387
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.
Showing
9 changed files
with
262 additions
and
72 deletions
antis-ncc-admin/src/views/attendance-record/components/record-detail-dialog.vue
| ... | ... | @@ -8,7 +8,7 @@ |
| 8 | 8 | <div class="detail-hero__title-row"> |
| 9 | 9 | <div class="detail-hero__title">{{ detail.employeeName || '无' }}</div> |
| 10 | 10 | <el-tag class="detail-hero__status-tag" :type="statusTagType" effect="dark">{{ detail.statusText || '无' |
| 11 | - }}</el-tag> | |
| 11 | + }}</el-tag> | |
| 12 | 12 | </div> |
| 13 | 13 | <div class="detail-hero__meta"> |
| 14 | 14 | <span>{{ detail.attendanceDate || '无' }}</span> |
| ... | ... | @@ -144,33 +144,27 @@ |
| 144 | 144 | </div> |
| 145 | 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 | 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 | 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 | 163 | </div> |
| 173 | 164 | </div> |
| 165 | + <div v-else class="workflow-empty"> | |
| 166 | + <span>当前为{{ detail.statusText }}状态,暂无关联流程记录</span> | |
| 167 | + </div> | |
| 174 | 168 | </el-card> |
| 175 | 169 | </template> |
| 176 | 170 | |
| ... | ... | @@ -219,6 +213,12 @@ export default { |
| 219 | 213 | default: |
| 220 | 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 | 224 | methods: { |
| ... | ... | @@ -265,33 +265,33 @@ export default { |
| 265 | 265 | await this.loadDetail() |
| 266 | 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 | 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 | 280 | async cancelLeave() { |
| 279 | 281 | if (!this.detail || !this.detail.id) return |
| 280 | - if (!(this.detail.status === 4 || this.detail.status === 5)) { | |
| 282 | + if (!this.isLeaveOrSickStatus) { | |
| 281 | 283 | this.$message.warning('当前考勤记录不是请假/病假状态,无法销假') |
| 282 | 284 | return |
| 283 | 285 | } |
| 284 | 286 | this.cancelLoading = true |
| 285 | 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 | 290 | this.$message.success('销假成功') |
| 291 | 291 | await this.loadDetail() |
| 292 | 292 | this.$emit('refresh') |
| 293 | 293 | } catch (e) { |
| 294 | - // eslint-disable-next-line no-empty | |
| 294 | + // user cancelled or api error | |
| 295 | 295 | } finally { |
| 296 | 296 | this.cancelLoading = false |
| 297 | 297 | } |
| ... | ... | @@ -496,6 +496,53 @@ export default { |
| 496 | 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 | 546 | @media (max-width: 960px) { |
| 500 | 547 | |
| 501 | 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 | 240 | public DateTime? UpdateTime { get; set; } |
| 241 | 241 | |
| 242 | 242 | /// <summary> |
| 243 | + /// 关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等) | |
| 244 | + /// </summary> | |
| 245 | + [SugarColumn(ColumnName = "F_RelatedWorkflowsJson", ColumnDataType = "longtext")] | |
| 246 | + public string RelatedWorkflowsJson { get; set; } | |
| 247 | + | |
| 248 | + /// <summary> | |
| 243 | 249 | /// 是否有效(1-有效,0-无效) |
| 244 | 250 | /// </summary> |
| 245 | 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 | 25 | using NCC.Extend.Entitys.lq_attendance_record; |
| 26 | 26 | using NCC.Extend.Entitys.lq_attendance_setting; |
| 27 | 27 | using NCC.Extend.Entitys.lq_mdxx; |
| 28 | +using NCC.Extend.Entitys.wform_leaveapply; | |
| 28 | 29 | using NCC.Extend.Interfaces.LqAttendanceRecord; |
| 29 | 30 | using NCC.FriendlyException; |
| 30 | 31 | using Newtonsoft.Json; |
| ... | ... | @@ -361,24 +362,41 @@ namespace NCC.Extend |
| 361 | 362 | record = recalculated.FirstOrDefault() ?? record; |
| 362 | 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 | 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 | 374 | .Where(x => x.BillNo == parsedBillNo) |
| 376 | 375 | .WhereIF(!string.IsNullOrWhiteSpace(parsedLeaveType), x => x.LeaveType == parsedLeaveType) |
| 377 | 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 | 402 | /// <summary> |
| ... | ... | @@ -411,13 +429,8 @@ namespace NCC.Extend |
| 411 | 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 | 434 | var attendanceDate = record.AttendanceDate.Date; |
| 422 | 435 | var isHoliday = await IsHolidayAsync(attendanceDate); |
| 423 | 436 | var isExempt = await IsExemptAsync(record.UserId, attendanceDate); |
| ... | ... | @@ -445,13 +458,12 @@ namespace NCC.Extend |
| 445 | 458 | record.LateMinutes = 0; |
| 446 | 459 | record.EarlyLeaveMinutes = 0; |
| 447 | 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 | 467 | record.RuleSnapshotJson = null; |
| 456 | 468 | record.AllRuleSnapshotJson = null; |
| 457 | 469 | |
| ... | ... | @@ -616,6 +628,17 @@ namespace NCC.Extend |
| 616 | 628 | record.UpdateUserId = operatorUserId; |
| 617 | 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 | 642 | await _db.Updateable(record).ExecuteCommandAsync(); |
| 620 | 643 | |
| 621 | 644 | return new |
| ... | ... | @@ -1418,10 +1441,9 @@ namespace NCC.Extend |
| 1418 | 1441 | private static dynamic BuildDetailResult( |
| 1419 | 1442 | LqAttendanceRecordEntity record, |
| 1420 | 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 | 1447 | return new |
| 1426 | 1448 | { |
| 1427 | 1449 | id = record.Id, |
| ... | ... | @@ -1472,14 +1494,15 @@ namespace NCC.Extend |
| 1472 | 1494 | operateTime = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss"), |
| 1473 | 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 | 1506 | ruleSnapshotJson = record.RuleSnapshotJson, |
| 1484 | 1507 | allRuleSnapshotJson = record.AllRuleSnapshotJson |
| 1485 | 1508 | }; |
| ... | ... | @@ -2263,6 +2286,35 @@ namespace NCC.Extend |
| 2263 | 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 | 2318 | private sealed class AttendanceStatusContext |
| 2267 | 2319 | { |
| 2268 | 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 | 16 | using NCC.FriendlyException; |
| 17 | 17 | using NCC.JsonSerialization; |
| 18 | 18 | using NCC.System.Entitys.Permission; |
| 19 | +using Newtonsoft.Json; | |
| 19 | 20 | using NCC.System.Interfaces.System; |
| 20 | 21 | using NCC.WorkFlow.Entitys; |
| 21 | 22 | using NCC.WorkFlow.Entitys.Dto.WorkFlowForm.LeaveApply; |
| ... | ... | @@ -883,6 +884,36 @@ LIMIT 1"; |
| 883 | 884 | } |
| 884 | 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 | 917 | #endregion |
| 887 | 918 | |
| 888 | 919 | #region PublicMethod |
| ... | ... | @@ -987,6 +1018,11 @@ LIMIT 1"; |
| 987 | 1018 | .ToListAsync(); |
| 988 | 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 | 1026 | if (existing != null) |
| 991 | 1027 | { |
| 992 | 1028 | existing.Status = status; |
| ... | ... | @@ -1001,6 +1037,7 @@ LIMIT 1"; |
| 1001 | 1037 | existing.AttendanceGroupName = group?.GroupName; |
| 1002 | 1038 | existing.RuleSnapshotJson = "{}"; |
| 1003 | 1039 | existing.AllRuleSnapshotJson = "{}"; |
| 1040 | + existing.RelatedWorkflowsJson = AppendLeaveWorkflow(existing.RelatedWorkflowsJson, workflowItemJson); | |
| 1004 | 1041 | existing.UpdateUserId = operatorId; |
| 1005 | 1042 | existing.UpdateTime = now; |
| 1006 | 1043 | await _sqlSugarRepository.Context.Updateable(existing).ExecuteCommandAsync(); |
| ... | ... | @@ -1024,6 +1061,7 @@ LIMIT 1"; |
| 1024 | 1061 | Remark = remark, |
| 1025 | 1062 | RuleSnapshotJson = "{}", |
| 1026 | 1063 | AllRuleSnapshotJson = "{}", |
| 1064 | + RelatedWorkflowsJson = workflowItemJson, | |
| 1027 | 1065 | CreateUserId = operatorId, |
| 1028 | 1066 | CreateTime = now, |
| 1029 | 1067 | UpdateUserId = operatorId, | ... | ... |
项目文档相关/docs/数据库说明.md
| ... | ... | @@ -168,6 +168,7 @@ |
| 168 | 168 | - `F_SupplementRemark`: 补卡备注 |
| 169 | 169 | - `F_SupplementOperatorId` / `F_SupplementOperatorName` / `F_SupplementTime`: 补卡操作信息 |
| 170 | 170 | - `F_SupplementWorkflowId`: 关联补卡流程ID(可空) |
| 171 | + - `F_RelatedWorkflowsJson`: 关联流程 JSON(数组,记录与该日考勤相关的请假/补卡等流程,含类型、表单主键、单号、假种等,便于详情展示与追溯) | |
| 171 | 172 | - **索引说明**: |
| 172 | 173 | - `uk_attendance_record_user_date`: 保证同一员工同一天只有一条有效记录 |
| 173 | 174 | - `idx_attendance_record_month_group`: 支持按月份、分组快速查询月度矩阵 | ... | ... |
项目文档相关/sql/2026-3-23/创建考勤设置相关表.sql
| ... | ... | @@ -225,6 +225,7 @@ CREATE TABLE IF NOT EXISTS `lq_attendance_record` ( |
| 225 | 225 | `F_SupplementOperatorName` varchar(100) DEFAULT NULL COMMENT '补卡操作人姓名', |
| 226 | 226 | `F_SupplementTime` datetime DEFAULT NULL COMMENT '补卡操作时间', |
| 227 | 227 | `F_SupplementWorkflowId` varchar(50) DEFAULT NULL COMMENT '关联补卡流程ID', |
| 228 | + `F_RelatedWorkflowsJson` longtext COMMENT '关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)', | |
| 228 | 229 | `F_CreateUserId` varchar(50) DEFAULT NULL COMMENT '创建人ID', |
| 229 | 230 | `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', |
| 230 | 231 | `F_UpdateUserId` varchar(50) DEFAULT NULL COMMENT '更新人ID', | ... | ... |
项目文档相关/sql/2026-3-25/考勤相关表全量重建.sql
| ... | ... | @@ -325,6 +325,7 @@ CREATE TABLE `lq_attendance_record` ( |
| 325 | 325 | `F_SupplementOperatorName` varchar(100) DEFAULT NULL COMMENT '补卡操作人姓名', |
| 326 | 326 | `F_SupplementTime` datetime DEFAULT NULL COMMENT '补卡操作时间', |
| 327 | 327 | `F_SupplementWorkflowId` varchar(50) DEFAULT NULL COMMENT '关联补卡流程ID', |
| 328 | + `F_RelatedWorkflowsJson` longtext COMMENT '关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)', | |
| 328 | 329 | `F_CreateUserId` varchar(50) DEFAULT NULL COMMENT '创建人ID', |
| 329 | 330 | `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', |
| 330 | 331 | `F_UpdateUserId` varchar(50) DEFAULT NULL COMMENT '更新人ID', | ... | ... |
项目文档相关/sql/2026-4-2/考勤记录_新增关联流程JSON字段.sql
0 → 100644