feat: 会议提交剩余时间+解冻 + 会务/劳务材料批量下载 + 多角色人员/账号模块完善

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-25 06:53:18 +08:00
co-authored by Claude
parent 0c882ae846
commit 591a43fe61
126 changed files with 3789 additions and 1334 deletions
+38 -31
View File
@@ -1,7 +1,7 @@
# 会议状态流转 —— 设计与实现总结
> 本文档是对「会议状态机」从单一 `current_stage` 枚举重构为**事实驱动模型**的完整设计说明。
> 供后续维护、二次开发、排查问题时查阅。最后更新时间:2026-08-23
> 供后续维护、二次开发、排查问题时查阅。最后更新时间:2026-08-24
---
@@ -19,7 +19,7 @@
**事实与展示分离**
- `biz_meeting` 只存**事实**`is_executed`/`is_frozen`/`is_settled`/`is_finished` + 材料/凭证审核阶段 + 审核时间 + 两级审核标记)。
- `biz_meeting` 只存**事实**`is_executed`/`is_frozen`/`is_settled`/`is_finished` + 材料审核阶段 + 审核时间 + 两级审核标记)。
- 各角色看到的「阶段名称」由这些事实**实时推导**,不再是一个固定枚举投影。
> 类比:数据库不存「年龄」,只存「出生日期」,「年龄」是算出来的。这里 `biz_meeting` 只存「发生了什么」,各角色看到的「当前处于什么阶段」是算出来的。
@@ -43,17 +43,14 @@
| `material_audit_stage` | varchar | 材料审核阶段(4 值,见 §3) | 执行方提交 / 审核动作 |
| `material_audit_time` | datetime | 材料最近一次审核动作时间 | 同上 |
| `material_compliance_approved` | int(0/1) | 材料合规(一级)是否通过 | 合规审核通过时置 1 |
| `voucher_audit_stage` | varchar | 凭证审核阶段(4 值) | 执行方提交 / 审核动作 |
| `voucher_audit_time` | datetime | 凭证最近一次审核动作时间 | 同上 |
| `voucher_compliance_approved` | int(0/1) | 凭证合规(一级)是否通过 | 合规审核通过时置 1 |
| `current_stage` | varchar | **派生缓存**9 值,见 §4 | 仅由 `StageDeriver`/控制器/调度器写,**业务代码不得直接改** |
> `is_executed` 为数据库既有字段,本次直接复用,未删未重加。
> 新增字段默认值:`material_audit_stage`/`voucher_audit_stage` 默认 `'NOT_SUBMITTED'`(已 ALTER),其余 int 默认 0、时间默认 NULL。
> 新增字段默认值:`material_audit_stage` 默认 `'NOT_SUBMITTED'`(已 ALTER),其余 int 默认 0、时间默认 NULL。
### 2.2 审核阶段枚举(4 值)
`material_audit_stage` / `voucher_audit_stage` 共用,枚举 `MeetingAuditStageEnum`
`material_audit_stage` 使用枚举 `MeetingAuditStageEnum`
| 值 | 中文 | 含义 |
|----|------|------|
@@ -68,7 +65,7 @@
## 3. 两级审核(合规 → 支持方)
`SUBMITTED` 内部再拆两级,由 `material_compliance_approved` / `voucher_compliance_approved` 标记:
`SUBMITTED` 内部再拆两级,由 `material_compliance_approved` 标记:
```
执行方提交 (SUBMITTED, compliance_approved=0)
@@ -89,8 +86,8 @@
APPROVED
```
- 材料、凭证各自独立走这套两级链路,可**同时提交、同时审核**(`auditType = MATERIAL|VOUCHER|BOTH`)。
- 「材料合规通过 + 凭证也通过」后,若最晚审核时间已超 24h,则进入「待结算」。
- 材料单独走这套两级链路(凭证审核链路已移除,凭证改由合规人员在结算时上传)。
- 「材料审核通过」后,若审核时间已超 24h,则进入「待结算」。
---
@@ -117,11 +114,11 @@
**`settlementReady` 判据**(待结算,用户拍板口径):
```text
voucher_audit_stage = APPROVED
AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日)
material_audit_stage = APPROVED
AND material_audit_time 距今 >= 24 小时(自然日)
```
即:材料+凭证都通过,且**最晚通过的那个时间点**已超 24h。
即:材料通过,且**通过的那个时间点**已超 24h。
### 4.2 各角色展示名(`deriveDisplay`
@@ -144,7 +141,7 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
> - **退回**:只有执行方看「已退回」,其余方看「待整改」。
> - **合规审中**:支持方(只读旁观者)看「已执行未传材料」。
> - **支持方审中**:合规(manager)看「审核通过」,其余方看「待审核」。
> - 展示名**优先按材料阶段**驱动(用户规则:材料优先),凭证阶段只在 `settlementReady` 判断中起作用
> - 展示名按材料阶段驱动
---
@@ -159,9 +156,9 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
┌─────────────────────────────┐
│ RUNNING 执行中 │
└──────┬───────────────┬──────┘
执行方提交材料/凭证 │ │ end_time+submit_deadline_days 到
(submit-material/ │ │ 且 material 未提交(调度器 markFrozen
submit-voucher) │ ▼
执行方提交材料 │ │ end_time+submit_deadline_days 到
(submit-material) │ │ 且 material 未提交(调度器 markFrozen
│ ▼
▼ │ ┌─────────────────┐
┌──────────────────┐ │ │ FROZEN 冻结中 │(终止,不可提交)
│ SUBMITTED 已提交 │◄┘ └─────────────────┘
@@ -178,7 +175,7 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
├─ 拒绝 → REJECTED ─────────────┤
└─ 通过 → APPROVED │
│ │
material+voucher 都 APPROVED
material APPROVED
│ │
┌─────────────────┴──┐ │
│ 未满 24h: │ │
@@ -203,7 +200,7 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
└─────────────────────┘ │
REJECTED 退回执行方 → 执行方看「已退回」, 可重新提交 ◄──┘
(material/voucher 回到 NOT_SUBMITTED 语义, 重新走 SUBMITTED)
(material 回到 NOT_SUBMITTED 语义, 重新走 SUBMITTED)
```
---
@@ -213,11 +210,11 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
| 文件 | 改动 |
|------|------|
| `service/StageDeriver.java`**新增** | 单一可信源:`derivePhysicalStage` + `deriveDisplay` + `settlementReady` |
| `domain/BizMeeting.java` | 新增 12 个事实字段 + getter/setter |
| `mapper/BizMeetingMapper.java` + `.xml` | resultMap/selectFields/updateByPrimaryKey 补 12 字段;调度 SQL 重写为 3 个 `update` |
| `domain/BizMeeting.java` | 新增事实字段 + getter/setter(凭证 3 字段已删,见 §12 |
| `mapper/BizMeetingMapper.java` + `.xml` | resultMap/selectFields/updateByPrimaryKey 补事实字段;调度 SQL 重写为 3 个 `update` |
| `scheduler/MeetingStageScheduler.java`**重写** | 3 个 `@Scheduled` 每分钟任务:`markExecuted` / `markFrozen` / `markSettlementReady` |
| `controller/BizMeetingController.java` | 重写审核段(见 §7);新增 `StageDeriver` 注入与 `appendAuditLog` 4 列版本 |
| `service/impl/BizMeetingServiceImpl.java` | 新建会议默认 `material/voucher_audit_stage = 'NOT_SUBMITTED'`(原 `INIT` |
| `service/impl/BizMeetingServiceImpl.java` | 新建会议默认 `material_audit_stage = 'NOT_SUBMITTED'`(原 `INIT` |
| `mapper/BizMeetingAuditLogMapper.java` + `.xml` | `current_stage` 拆为 4 列:`executor_stage`/`sponsor_stage`/`manager_stage`/`admin_stage` |
| `controller/BizDashboardController.java` | 修复历史 bug:中文标签比对 → 枚举码比对 |
@@ -225,7 +222,7 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
- `markExecuted``start_time <= NOW()``material_audit_stage='NOT_SUBMITTED'``is_executed=0``is_frozen=0``is_executed=1, execute_time=NOW(), current_stage='RUNNING'`
- `markFrozen``material='NOT_SUBMITTED'``date_add(end_time, interval p.submit_deadline_days day) <= NOW()``is_frozen=1, freeze_time=NOW(), current_stage='FROZEN'``submit_deadline_days` 取自 `biz_project`join 软删过滤)。
- `markSettlementReady`:材料+凭证都 `APPROVED``greatest(material_audit_time, voucher_audit_time) <= NOW() - INTERVAL 1 DAY``current_stage='AWAITING_SETTLEMENT'`
- `markSettlementReady`:材料 `APPROVED``material_audit_time <= NOW() - INTERVAL 1 DAY``current_stage='AWAITING_SETTLEMENT'`
---
@@ -234,19 +231,18 @@ AND max(material_audit_time, voucher_audit_time) 距今 >= 24 小时(自然日
| 接口 | 角色 | 前置校验 | 通过后 | 拒绝后 |
|------|------|----------|--------|--------|
| `POST /{meetingId}/submit-material` | 执行方(`isExecutorOfProject`) | 已执行 + 未冻结 + stage∈{NOT_SUBMITTED,REJECTED} + 至少 1 条 `L_*` 材料 + 1 条 `M_*` 材料 | material→SUBMITTED, compliance=0 | — |
| `POST /{meetingId}/submit-voucher` | 执行方 | 已执行 + 未冻结 + stage∈{NOT_SUBMITTED,REJECTED} + 有 `LV_PAYMENT` + `SV_PAYMENT` | voucher→SUBMITTED, compliance=0 | — |
| `POST /{meetingId}/audit-compliance` | 合规(manager | stage=SUBMITTED 且 compliance=0;拒绝需意见 | compliance=1(转入支持方) | →REJECTED(退回) |
| `POST /{meetingId}/audit-supervision` | 监察员(`biz_meeting_supervisor` | stage=SUBMITTED 且 compliance=1;拒绝需意见 | →APPROVED(材料通过时一并写监管意见) | →REJECTED(退回并通知执行方) |
| `POST /{meetingId}/settle` | 合规/管理员 | 材料+凭证都 APPROVED | `is_settled=1, settle_time` | — |
| `POST /{meetingId}/settle` | 合规/管理员 | 材料 APPROVED + 费用汇总完成 + 上传劳务凭证/会务凭证 | 保存凭证 + `is_settled=1, settle_time` | — |
| `POST /{meetingId}/finish` | 合规/管理员 | `is_settled=1` | `is_finished=1, finish_time` | — |
- `auditType` 支持 `MATERIAL`/`VOUCHER`/`BOTH`(多选,`resolveTypes` 展开)
- 结算接口已二合一:合规/管理员在结算时上传劳务凭证(LV_PAYMENT+ 会务凭证(SV_PAYMENT),缺任一报错「请上传凭证」;结算后凭证只读可见
- 结算 → 完结是**两个独立的手动动作**(用户规则 1),不能跳步。
- `supervision-opinion` 旧接口已删除,支持方审批统一走 `audit-supervision`
### 7.1 审核日志(`biz_meeting_audit_log`
`appendAuditLog(m, auditType, result, opinion)` 不再写单一 `current_stage`,改为写 **4 个角色列**,每个记录流转那一刻各角色看到的展示态:
`appendAuditLog(m, auditType, result, opinion)` 不再写单一 `current_stage`,改为写 **4 个角色列**,每个记录流转那一刻各角色看到的展示态auditType 现仅 `MATERIAL` 审核链路与 `SETTLE` 结算两类)
```text
executor_stage / sponsor_stage / manager_stage / admin_stage
@@ -262,7 +258,7 @@ executor_stage / sponsor_stage / manager_stage / admin_stage
|------|------|
| `utils/meetingStage.js`**重写** | 镜像 `StageDeriver``derivePhysicalStage` / `deriveStage` / `stageLabel` / `stageClass` / `stageTag` / `STAGE_OPTIONS` |
| `views/meetings/Meetings.vue` | admin/manager/sponsor 共享列表:`stageLabel(roleSegment, row)` + `stageClass(row)`sponsor「审批」按钮条件 `derivePhysicalStage(row)==='AWAITING_SUPERVISION'`,点击 POST `audit-supervision` |
| `views/meetings/MeetingDetail.vue` | 审核 dialog 改多选 checkbox(材料/凭证);新增「结算」「完结」按钮 + `canSettle`/`canFinish` + `onSettle`/`onFinish``fmtAuditStage` 4 值标签 |
| `views/meetings/MeetingDetail.vue` | 审核 dialog 仅材料;新增「结算」「完结」按钮(结算 dialog 内含劳务/会务凭证上传,二合一)+ `canSettle`/`canFinish` + `onSettle`/`onFinish`凭证结算后只读展示 |
| `views/executor/Meetings.vue` | `stageLabel('executor', row)`(列表 + 监督意见 dialog |
| `views/doctor/Meetings.vue` | `stageTag(row)` + `stageLabel('doctor', row)`;筛选下拉改用 `STAGE_OPTIONS` |
@@ -273,17 +269,17 @@ executor_stage / sponsor_stage / manager_stage / admin_stage
## 9. 关键设计决策(拍板记录)
1. **结算 → 完结需手动点击**`settle``finish` 是两个独立动作,合规/管理员各点一次。
2. **待结算 24h 门槛**:材料+凭证都通过且「最晚通过时间」超 24h 才进「待结算」。
2. **待结算 24h 门槛**:材料通过且「通过时间」超 24h 才进「待结算」。
3. **冻结判据**`end_time + project.submit_deadline_days` 天;未提交判据 = `material_audit_stage == NOT_SUBMITTED`
4. **已提交 / 待审核合并**:审核阶段 4 值(`SUBMITTED` 内部靠 `compliance_approved` 分两级)。
5. **`is_executed(0,1)` 复用**:DB 已有,直接使用,未删未重加。
6. **材料优先**:展示名优先按材料阶段驱动,凭证阶段仅参与 `settlementReady` 判断
6. **凭证改由结算时上传**:执行方不再提交/审核凭证,凭证由合规/管理员在结算时一并上传(二合一),需同时有劳务凭证与会务凭证
---
## 10. 已知问题 / 待办 / 注意事项 ⚠️
1. **24h 是软门槛**`settle` 接口和前端「结算」按钮只校验「材料+凭证都 APPROVED」,**未强制校验 24h**。24h 目前只体现在展示名(「审核通过」→「待结算」)和调度器翻转 `current_stage`。若需硬校验,需给 `canSettle` + `settle``settlementReady` 判据。
1. **24h 是软门槛**`settle` 接口和前端「结算」按钮只校验「材料 APPROVED + 费用汇总完成 + 凭证齐全」,**未强制校验 24h**。24h 目前只体现在展示名(「审核通过」→「待结算」)和调度器翻转 `current_stage`。若需硬校验,需给 `canSettle` + `settle``settlementReady` 判据。
2. **执行方新增「审核通过」态**:材料支持方通过后、满 24h 前,执行方看到「审核通过」,此态不在原始需求清单中,需确认是否可接受。
3. **`views/sponsor/Meetings.vue` 是死代码**:路由未引用(sponsor 复用共享 `meetings/Meetings.vue`),内含非法枚举 `AWAITING_SPONSOR`、不存在的 `auditStatus`/`auditOpinion` 字段、直接写 `current_stage` 的「结算/解冻」逻辑。建议删除。
4. **`current_stage` 是派生缓存**:仅 `StageDeriver` + 控制器转换端点 + 调度器可写,**任何业务代码不得直接 `update current_stage`**(写了也会被下次推导覆盖)。
@@ -311,3 +307,14 @@ executor_stage / sponsor_stage / manager_stage / admin_stage
- `views/meetings/MeetingDetail.vue`
- `views/executor/Meetings.vue`
- `views/doctor/Meetings.vue`
---
## 12. 近期变更:凭证流程移除(2026-08-24)
**改动动机**:执行方不再保存/提交/审核凭证,凭证改为由合规人员在结算时一并上传。
1. **移除执行方凭证链路**:删除 `POST /{meetingId}/submit-voucher`;审核接口不再支持 `auditType=VOUCHER/BOTH` 多选,审核链路只剩材料一条(`auditType` 固定 `MATERIAL`)。
2. **结算二合一**`POST /{meetingId}/settle` 改为「上传凭证 + 结算」合并。合规/管理员点击结算时,必须同时上传劳务凭证(`LV_PAYMENT`)与会务凭证(`SV_PAYMENT`),缺任一报错「请上传凭证」;前置校验:材料 `APPROVED` + `fee_calc_status=1` + 未结算。
3. **凭证落库**:凭证作为 `biz_meeting_material``LABOR_VOUCHER`/`SERVICE_VOUCHER`)保存,不参与费用汇总;结算后凭证只读可见。
4. **DDL**`biz_meeting` 已 DROP 三个死列 `voucher_audit_stage` / `voucher_audit_time` / `voucher_compliance_approved`,对应 domain/mapper/service 字段与映射同步删除。