From 1876601ba74a235a9f182c0a4e856952a9e24bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=AD=E5=BA=86=E6=B3=B0?= <12369755+htcloud1@user.noreply.gitee.com> Date: Sat, 29 Aug 2026 20:02:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=9A=E8=AE=AE=E9=98=B6=E6=AE=B5?= =?UTF-8?q?=E5=8A=B3=E5=8A=A1/=E4=BC=9A=E5=8A=A1=E5=89=8D=E7=BC=80?= =?UTF-8?q?=E6=8A=95=E5=BD=B1=20+=20=E7=BB=93=E7=AE=97=E5=89=8D=E6=9C=AA?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=8B=A6=E6=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 阶段投影: 待审核/审核通过/待整改/待结算 按劳务/会务前缀区分 (前后端镜像) - 劳务材料提交门槛改为参会人员列表≥1人 (非上传文件) - sponsor/manager 操作列「审核」「结算」提为直链 - 会议结算时间轴节点: 待结算态显示「补充凭证并结算」 - 结算前检测未保存改动, 弹「保存并结算」拦截 Co-Authored-By: Claude --- .../controller/BizMeetingController.java | 20 ++++--- .../com/ruoyi/business/domain/BizMeeting.java | 10 ++++ .../ruoyi/business/service/StageDeriver.java | 40 ++++++++++++-- .../mapper/business/BizMeetingMapper.xml | 6 +++ ry-vue3/src/utils/meetingStage.js | 51 +++++++++++++++--- ry-vue3/src/views/meetings/MeetingDetail.vue | 54 ++++++++++++++++++- ry-vue3/src/views/meetings/Meetings.vue | 20 +++---- 7 files changed, 171 insertions(+), 30 deletions(-) diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizMeetingController.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizMeetingController.java index 9ad2156..5274a00 100644 --- a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizMeetingController.java +++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizMeetingController.java @@ -285,7 +285,7 @@ public class BizMeetingController extends BaseController { *
  • 校验 1: 当前用户是该会议执行方 (项目级归属, 强校验)
  • *
  • 校验 2: 已执行 (is_executed=1) 且未冻结
  • *
  • 校验 3: 所选轨 audit_stage ∈ {NOT_SUBMITTED, REJECTED}
  • - *
  • 校验 4: 所选轨在 biz_meeting_material 各至少 1 条 (劳务 L_* / 会务 M_*)
  • + *
  • 校验 4: 会务轨在 biz_meeting_material 至少 1 条 (M_*); 劳务轨 = 参会人员列表至少 1 人 (非上传文件)
  • * * body: { "types": ["LABOR","SERVICE"] } * 通过后该轨 → SUBMITTED (compliance_approved=0), 每轨记一条 audit_log. @@ -310,8 +310,9 @@ public class BizMeetingController extends BaseController { } List mats = bizMeetingMaterialService.selectByMeetingId(meetingId); - boolean hasLabor = mats.stream().anyMatch(x -> x.getSubType() != null && x.getSubType().startsWith("L_")); boolean hasService = mats.stream().anyMatch(x -> x.getSubType() != null && x.getSubType().startsWith("M_")); + // 劳务材料 = 参会人员列表 (不是上传的文件), 提交门槛 = 至少 1 名参会人 + boolean hasLabor = !attendeeService.selectByMeetingId(meetingId).isEmpty(); for (String type : types) { String label = "LABOR".equals(type) ? "劳务材料" : "会务材料"; @@ -319,8 +320,11 @@ public class BizMeetingController extends BaseController { if (!"NOT_SUBMITTED".equals(stage) && !"REJECTED".equals(stage)) { throw new ServiceException(label + "当前阶段 (" + stage + ") 不允许提交"); } - boolean has = "LABOR".equals(type) ? hasLabor : hasService; - if (!has) throw new ServiceException(label + "至少上传一条"); + if ("LABOR".equals(type)) { + if (!hasLabor) throw new ServiceException("请先添加参会人员 (劳务材料)"); + } else if (!hasService) { + throw new ServiceException("会务材料至少上传一条"); + } } for (String type : types) { @@ -685,8 +689,12 @@ public class BizMeetingController extends BaseController { log.setAuditResult(result); log.setOpinion(opinion); log.setMaterialType(materialType); - log.setCreateTime(new Date()); - log.setAuditTime(new Date()); + Date now = new Date(); + log.setCreateTime(now); + log.setAuditTime(now); + // 本次动作所在轨「最近时间」记为 now, 供 deriveDisplay 按最近时间合并两轨 (4 列阶段快照) + if ("LABOR".equals(materialType)) m.setLaborAuditTime(now); + else if ("SERVICE".equals(materialType)) m.setServiceAuditTime(now); log.setExecutorStage(stageDeriver.deriveDisplay("executor", m)); log.setSponsorStage(stageDeriver.deriveDisplay("sponsor", m)); log.setManagerStage(stageDeriver.deriveDisplay("manager", m)); diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizMeeting.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizMeeting.java index 90aaec7..6e461fa 100644 --- a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizMeeting.java +++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizMeeting.java @@ -99,6 +99,12 @@ public class BizMeeting extends BaseEntity { /** 材料最近一次审核动作时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date materialAuditTime; + /** 劳务材料最后一次动作时间 (派生字段, 标量子查询 max(audit_time) where material_type='LABOR', 不落库) */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date laborAuditTime; + /** 会务材料最后一次动作时间 (派生字段, 标量子查询 max(audit_time) where material_type='SERVICE', 不落库) */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date serviceAuditTime; /** 劳务材料合规是否已通过 0否1是 (区分 SUBMITTED 内合规审/支持方审) */ private Integer laborComplianceApproved; /** 会务材料合规是否已通过 0否1是 (区分 SUBMITTED 内合规审/支持方审) */ @@ -219,6 +225,10 @@ public class BizMeeting extends BaseEntity { public void setFreezeTime(Date freezeTime) { this.freezeTime = freezeTime; } public Date getMaterialAuditTime() { return materialAuditTime; } public void setMaterialAuditTime(Date materialAuditTime) { this.materialAuditTime = materialAuditTime; } + public Date getLaborAuditTime() { return laborAuditTime; } + public void setLaborAuditTime(Date laborAuditTime) { this.laborAuditTime = laborAuditTime; } + public Date getServiceAuditTime() { return serviceAuditTime; } + public void setServiceAuditTime(Date serviceAuditTime) { this.serviceAuditTime = serviceAuditTime; } public Integer getLaborComplianceApproved() { return laborComplianceApproved; } public void setLaborComplianceApproved(Integer laborComplianceApproved) { this.laborComplianceApproved = laborComplianceApproved; } public Integer getServiceComplianceApproved() { return serviceComplianceApproved; } diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/StageDeriver.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/StageDeriver.java index 1d6eb95..205f792 100644 --- a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/StageDeriver.java +++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/StageDeriver.java @@ -88,8 +88,42 @@ public class StageDeriver if (t(m.getIsFinished())) return "已完结"; if (t(m.getIsSettled())) return "已结算"; - int chosen = chooseState(role, laborState(m), serviceState(m)); - return render(role, chosen, executionPhase(m)); + int phase = executionPhase(m); + int labor = laborState(m); + int service = serviceState(m); + + // 中性角色 (admin/doctor/expert): 维持原逻辑 (最小进度 + 单轨措辞), 不加前缀 + if (!"executor".equals(role) && !"sponsor".equals(role) && !"manager".equals(role)) + { + return render(role, Math.min(labor, service), phase); + } + + // 三流程角色: 两轨合并 + 劳务/会务前缀 + String laborLabel = render(role, labor, phase); + String serviceLabel = render(role, service, phase); + if (laborLabel.equals(serviceLabel)) return laborLabel; + + // 待办态最高优先: manager=C0(2), sponsor=C1(3), executor=R(0) + int urgent = "manager".equals(role) ? 2 : "sponsor".equals(role) ? 3 : 0; + String track; + String label; + if (labor == urgent) { track = "labor"; label = laborLabel; } + else if (service == urgent) { track = "service"; label = serviceLabel; } + else + { + Date lt = m.getLaborAuditTime(); + Date st = m.getServiceAuditTime(); + boolean laborNewer = lt != null && (st == null || !lt.before(st)); + if (laborNewer) { track = "labor"; label = laborLabel; } + else { track = "service"; label = serviceLabel; } + } + + // 仅 待审核/审核通过/待整改/待结算 四类加前缀 + if ("待审核".equals(label) || "审核通过".equals(label) || "待整改".equals(label) || "待结算".equals(label)) + { + return ("labor".equals(track) ? "劳务" : "会务") + label; + } + return label; } /** 按角色优先级选代表轨 (两轨中优先级更高的那轨). */ @@ -139,7 +173,7 @@ public class StageDeriver switch (s) { case 0: // R 退回 - return "executor".equals(role) ? "已退回" : "待整改"; + return "待整改"; case 1: // N 未提交 (时间驱动三态: 未执行 → 执行中 → 已执行, 所有角色统一) if (phase == 0) return "未执行"; if (phase == 1) return "执行中"; diff --git a/ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml b/ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml index a4c7640..ac2a4c1 100644 --- a/ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml +++ b/ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml @@ -33,6 +33,8 @@ + + @@ -59,6 +61,8 @@