feat: OCR 服务 + 会议材料模块
ry-ocr/ (新)
本地发票识别微服务 (PaddleOCR 3.x + FastAPI, 8801)
- QR 优先: 扫到二维码即取开票时间/发票号/金额; 没扫到/格式不合法直接判非发票, 不跑 OCR
- 配置 QR_FULL_OCR 控制快路径(false, 0.2s)还是全字段(true, 4.5s)
- /recognize/invoice (multipart) + /recognize/invoice/by-path (本地路径, 白名单) + /recognize/text
- is_invoice / from_qr / qr_raw / qr_error / error_code 字段
- 12 字段发票抽取 (regex + 启发式, 左右主体识别)
- 超时保护 (15s 单页 / 60s 总流程) + PaddleOCR 单例 + ThreadPoolExecutor
ry-api/ruoyi-business/
- pom.xml: 加 hutool-http/json/core 5.8.27, lombok 1.18.30 (OcrClient @Slf4j 所需)
- ocr/: OcrClient + InvoiceResult/Fields/Line + ZipExtractor + InvoiceOcrScheduler
- oss/: OssUploader + OssConfMeta (OCR 识别后重传 OSS)
- config/: OcrConfig + OcrExecutorConfig (后台线程池)
- service/impl/InvoiceOcrService: 后台提交 OCR, ZIP 路径解压识别, 替换场景先清旧
- 会议材料 CRUD 全套 (BizMeetingAuditLog/Executor/Invoice/Material/Supervisor):
controller + service + mapper + domain + xml
ry-vue3/
- MeetingDetail.vue (新建): 会议详情页 (含评分维度章节, 改只读)
- Meetings.vue / OssFileUploader.vue / router / Login.vue: 适配新字段
ry-api/ruoyi-admin/
- RuoYiApplication.java + application.yml: 启用 @Async 异步支持
_self/
- manager_meetings.md / manager_meeting_detail.md: 文档
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
# manager/meetings/detail — 端到端技术审查 (v2 修订)
|
||||
|
||||
> **审查时间**: 2026-08-21
|
||||
> **v1 修订**: v1 报告 §9 #1 标错 mapper 漏字段范围 (说 invitation_url/schedule_url/address 全漏, 实际只有 address 漏), 由 v2 实测 mapper 后修正
|
||||
> **v2 触发**: 实际访问 `/manager/meetings/detail` 发现"会议地址"等字段不显示, 重新按 SKILL 4 跳 + DB 实测 + 三层穿透
|
||||
> **审查范围**: `ry-vue3/src/views/manager/MeetingDetail.vue` (240 行) + `router/index.js:66` 路由 + 后端 `GET /business/meeting/{meetingId}` + `biz_meeting` DB 24 列实测 + `BizMeetingMapper.xml` 5 处 SELECT/UPDATE/INSERT 实测
|
||||
> **对比原型**: `proto/html/components/meeting-detail.html` (300 行, manager 端 10 字段 + 2 张邀请函 + 2 张日程)
|
||||
> **依赖项**: 完整列表审查见 `_self/manager_meetings.md` v3 (DB/Entity/mapper 共享)
|
||||
|
||||
---
|
||||
|
||||
## 0. 4 跳定位
|
||||
|
||||
| 跳 | 文件:行 | 内容 |
|
||||
|---|---|---|
|
||||
| ① 角色菜单 | `ry-vue3/src/layout/AdminLayout.vue:97` | manager → 会议管理 (无此详情页菜单项, 是从列表"查看"按钮跳转的次级页) |
|
||||
| ② 路由 | `ry-vue3/src/router/index.js:66` | `name: 'manager-meetings-detail', path: 'meetings/detail/:meetingId'` ✓ |
|
||||
| ③ 组件 | `ry-vue3/src/views/manager/MeetingDetail.vue` (240 行) | 4 段: 基本信息 + 邀请函 + 日程 + 返回 |
|
||||
| ④ 原型 | `proto/html/components/meeting-detail.html` (300 行) | 3 段: 基本信息 (10 字段) + 邀请函 (2 张图+下载) + 日程 (2 张图+下载) + 返回 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 主要功能 + 可见性
|
||||
|
||||
### 1.1 主要功能
|
||||
|
||||
| 功能 | 前端入口 | 后端接口 | 数据表 |
|
||||
|---|---|---|---|
|
||||
| 加载会议详情 | `MeetingDetail.vue:138-152 load()` | `GET /business/meeting/{meetingId}` (`BizMeetingController.java:38-42`) | `biz_meeting` |
|
||||
| 返回列表 | `goBack()` → `router.push('/manager/meetings')` | — | — |
|
||||
| 邀请函预览/下载 | `<a :href="row.invitationUrl" target="_blank">` (line 60-70) | (无后端, 直链) | `biz_meeting.invitation_url` |
|
||||
| 日程预览/下载 | `<a :href="row.scheduleUrl" target="_blank">` (line 82-92) | (无后端, 直链) | `biz_meeting.schedule_url` |
|
||||
|
||||
### 1.2 可见性 (三层过滤)
|
||||
|
||||
| 层 | 来源 | 校验字段 |
|
||||
|---|---|---|
|
||||
| 前端菜单 | 无菜单项, 从列表"查看"按钮 router.push 进入 |
|
||||
| 路由守卫 | `router/index.js:66` | `meta.title` (无 role 字段, 实际靠 `meta.role='manager'` 父路由限制) |
|
||||
| 后端 SQL | `BizMeetingMapper.xml:33-36 selectByPrimaryKey` | ❌ 无任何过滤 (manager 端全可见, 见 manager_meetings.md §10 #1) |
|
||||
|
||||
**数据隔离**: ❌ 缺失 (manager 端无 `lead_user_id` 过滤, 详情接口也未校验 project 归属)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 筛选项 (filter-form)
|
||||
|
||||
**无** — 详情页只读, 无筛选。
|
||||
|
||||
---
|
||||
|
||||
## 3. 工具栏 (toolbar)
|
||||
|
||||
**无** — 详情页只有底部"返回"按钮 (对应原型 u11772):
|
||||
|
||||
| 按钮 | 功能 | 接口 | 涉及表 | 原型对照 |
|
||||
|---|---|---|---|---|
|
||||
| 返回 | `goBack()` → `router.push('/manager/meetings')` | — | — | ✅ u11772 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 表格 (el-table)
|
||||
|
||||
**无** — 详情页只展示单条记录的字段, 不列表。
|
||||
|
||||
---
|
||||
|
||||
## 5. 行内操作按钮
|
||||
|
||||
**无** — 详情页是只读视图, 无行内操作。
|
||||
|
||||
---
|
||||
|
||||
## 6. Java Entity ↔ 数据库表 一致性
|
||||
|
||||
### 6.1 biz_meeting DDL (DB 实测, 2026-08-21)
|
||||
|
||||
`SHOW CREATE TABLE biz_meeting` 结果, 24 列 + 3 索引:
|
||||
|
||||
| # | 列 | 类型 | 中文 COMMENT | 索引 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | meeting_id | bigint NOT NULL AUTO_INCREMENT | 会议ID | PRIMARY |
|
||||
| 2 | business_id | varchar(50) NOT NULL | 业务流水ID 5643145673 | UNIQUE uk_business_id |
|
||||
| 3 | project_id | bigint NULL | 所属项目ID | idx_meeting_project |
|
||||
| 4 | project_no | varchar(50) NULL | 项目编号 | — |
|
||||
| 5 | project_name | varchar(200) NULL | 项目名称 | — |
|
||||
| 6 | meeting_name | varchar(200) NULL | 会议名称 | — |
|
||||
| 7 | period_no | int DEFAULT '1' | 期数 | — |
|
||||
| 8 | total_periods | int DEFAULT '1' | 总期数 | — |
|
||||
| 9 | project_form | varchar(20) NULL | 项目形式 | — |
|
||||
| 10 | start_time | datetime NULL | 会议开始时间 | — |
|
||||
| 11 | end_time | datetime NULL | 会议结束时间 | — |
|
||||
| 12 | org_name | varchar(200) NULL | 公司名称(冗余) | — |
|
||||
| **13** | **address** | **varchar(500) NULL** | **会议地址** | **—** |
|
||||
| 14 | current_stage | varchar(20) DEFAULT '0' | 当前阶段 未执行/待监管/监管通过/待整改/待结算/已结算/已结题 | idx_meeting_stage |
|
||||
| 15 | supervision_opinion | varchar(500) NULL | 监察意见 | — |
|
||||
| 16 | supervision_by | varchar(64) NULL | 监察人 | — |
|
||||
| 17 | supervision_time | datetime NULL | 监察时间 | — |
|
||||
| 18 | invitation_url | varchar(500) NULL | 邀请函URL | — |
|
||||
| 19 | schedule_url | varchar(500) NULL | 日程海报URL | — |
|
||||
| 20 | labor_signed | char(1) DEFAULT '0' | 签署劳务 0未签 1已签 | — |
|
||||
| 21 | create_by | varchar(64) DEFAULT '' | 创建者 | — |
|
||||
| 22 | create_time | datetime NULL | 创建时间 | — |
|
||||
| 23 | update_by | varchar(64) DEFAULT '' | 更新者 | — |
|
||||
| 24 | update_time | datetime NULL | 更新时间 | — |
|
||||
|
||||
### 6.2 4 层穿透字段对照矩阵 (本详情页相关)
|
||||
|
||||
| # | DB 列 | Entity 有 | Mapper SELECT | Vue 渲染 | 原型 | 状态 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | meeting_id | ✅ | ✅ | ✅ 会议ID | — | ✅ 富余 |
|
||||
| 2 | business_id | ✅ | ✅ | — | — | ⚠️ 内部审计字段 |
|
||||
| 3 | project_id | ✅ | ✅ | — | — | ⚠️ 内部 ID, 详情不显示 |
|
||||
| 4 | project_no | ✅ | ✅ | ✅ 项目编号 | ✅ | ✅ |
|
||||
| 5 | project_name | ✅ | ✅ | ✅ 项目名称 | ✅ | ✅ |
|
||||
| 6 | meeting_name | ✅ | ✅ | ✅ 会议名称 | ✅ | ✅ |
|
||||
| 7 | period_no | ✅ | ✅ | ✅ 期数 | ✅ | ✅ |
|
||||
| 8 | total_periods | ✅ | ✅ | ✅ 总期数 | ✅ | ✅ |
|
||||
| 9 | project_form | ✅ | ✅ | ✅ 项目形式 | — | ✅ 富余 (manager 端用) |
|
||||
| 10 | start_time | ✅ | ✅ | ✅ 会议开始时间 | ✅ | ✅ |
|
||||
| 11 | end_time | ✅ | ✅ | ✅ 会议结束时间 | ✅ | ✅ |
|
||||
| 12 | org_name | ✅ | ✅ | ✅ 支持单位 | ✅ | ✅ |
|
||||
| **13** | **address** | ✅ | **✅ v2 已修** | **✅ v2 已加** | — | ✅ **v2 修复完成** |
|
||||
| 14 | current_stage | ✅ | ✅ | ✅ 当前阶段 | — | ✅ 富余 |
|
||||
| 15-17 | supervision_* | ✅ | ✅ | ✅ 监察段 (条件) | — | ✅ 富余 |
|
||||
| 18 | invitation_url | ✅ | ✅ | ✅ 邀请函 (2 张) | ✅ | ✅ |
|
||||
| 19 | schedule_url | ✅ | ✅ | ✅ 日程 (2 张) | ✅ | ✅ |
|
||||
| 20 | labor_signed | ✅ | ✅ | ✅ 签署劳务 | — | ✅ 富余 |
|
||||
| 21 | create_by | ✅ | ✅ | ✅ 创建人员 | ✅ | ✅ |
|
||||
| 22 | create_time | ✅ | ✅ | ✅ 创建时间 | ✅ | ✅ |
|
||||
| 23-24 | update_by / update_time | ✅ | ✅ | — | — | ⚠️ 内部审计字段 |
|
||||
| — | (无) | ❌ | ❌ | (v2 已删) | — | ✓ 备注 dead field 已移除 |
|
||||
|
||||
**v2 修复**:
|
||||
- `BizMeetingMapper.xml:17, 31, 69, 91, 115` 加 `address` (5 处)
|
||||
- `MeetingDetail.vue:42` 加 `row.address` 字段, 替换原 `row.remark` (dead field)
|
||||
|
||||
**v2 验证**: 4 层穿透矩阵中 **24 列 DB 全部可达** (除内部审计字段), 详情页 14 字段渲染 + 邀请函/日程 2 张缩略图全部对得上数据源。
|
||||
|
||||
---
|
||||
|
||||
## 7. 索引检查
|
||||
|
||||
详情接口走 `SELECT ... FROM biz_meeting WHERE meeting_id = #{meetingId}` (精确匹配 PK)。索引:
|
||||
|
||||
| Key | 列 | 用途覆盖 |
|
||||
|---|---|---|
|
||||
| PRIMARY | meeting_id | ✅ 详情接口 WHERE 走 PK |
|
||||
| uk_business_id | business_id | (外部系统对接, 详情接口不用) |
|
||||
| idx_meeting_project | project_id | (列表 join 用, 详情接口不用) |
|
||||
| idx_meeting_stage | current_stage | (阶段筛选用, 详情接口不用) |
|
||||
|
||||
无需额外索引。
|
||||
|
||||
---
|
||||
|
||||
## 8. 与原型差异 (proto/html/components/meeting-detail.html)
|
||||
|
||||
### 8.1 实现新增 (原型没有) — **改进**
|
||||
|
||||
| 项 | 原型 | 实现 | 评估 |
|
||||
|---|---|---|---|
|
||||
| 当前阶段渲染带颜色 tag | 无 (纯文本) | ✅ stage-tag | 项目统一风格 |
|
||||
| 监察字段条件渲染 | 无 (静态展示) | ✅ 任一 supervision_* 有值才显示整段 | 避免空占位 |
|
||||
| 字段为空显示 "-" | 无 | ✅ | 数据完整性提示 |
|
||||
| 会议ID / 项目形式 / 当前阶段 / 签署劳务 | 无 | ✅ 4 项 | manager 端管理需要 |
|
||||
| 会议地址 | 无 | ✅ (v2 新增) | DB 有, 详情不显示会丢数据 |
|
||||
|
||||
### 8.2 原型有但实现缺失 — **v2 全部修复, 仅剩非 P0 项**
|
||||
|
||||
| 原型 | 实现 v2 | 实现 v1 | 状态 |
|
||||
|---|---|---|---|
|
||||
| **邀请函 2 张缩略图** (原型 line 246-255, 2 个 `.preview-card` in `.preview-grid`) | ✅ 2 张 (line 60-69) | ❌ 1 张 | ✅ v2 修复 |
|
||||
| **日程 2 张缩略图** (原型 line 264-273) | ✅ 2 张 (line 82-91) | ❌ 1 张 | ✅ v2 修复 |
|
||||
| **会议地址** (DB + entity 都有, v1 mapper 漏 SELECT) | ✅ 渲染 (line 42) | ❌ 永远 - | ✅ v2 修复 |
|
||||
| 总场次/总期数 (原型 1 行) | 拆"总期数"+"期数"两列 | 同 | ✅ 更细 |
|
||||
|
||||
### 8.3 文字 / 标签差异
|
||||
|
||||
| 项 | 原型 | 实现 | 评估 |
|
||||
|---|---|---|---|
|
||||
| 面包屑 | "我参与的会议 > 会议详情" | "首页 / 会议管理 / 会议详情" | ✅ 角色匹配 (doctor → manager) |
|
||||
| 章节标题 h1 22px vs 项目色 | 16px 原型小字 | 14px 章节标题样式 | ✅ 跟项目内 ProjectsNew/ManagerProjectDetail 一致 |
|
||||
| 颜色 | `#1890ff` (浅蓝) | `var(--brand-primary)` (深海军蓝) | ✅ 项目色 |
|
||||
| 红字提示 *点击缩略图 | `#ff4d4f` 红字 | `#909399` 灰字 | ✅ 跟项目灰字风格一致 (ProjectsNew 调整时同步) |
|
||||
|
||||
### 8.4 总结
|
||||
|
||||
**v2 整体方向**: **完全 1:1 移植 + manager 端扩展** — 14 字段 (10 原型 + 4 manager 扩展: 会议ID/项目形式/当前阶段/签署劳务) + 监察段 + 邀请函 2 张 + 日程 2 张 + 返回, 跟原型 3 段结构完全对应。
|
||||
|
||||
---
|
||||
|
||||
## 9. 设计问题
|
||||
|
||||
### 9.1 ✅ v2 已修: address mapper 漏 (v1 报告 §9 #1 误标范围)
|
||||
|
||||
**v1 错**: 报告说"invitation_url / schedule_url / address mapper 全套漏", 实际**只漏 address**。
|
||||
**v2 重测**:
|
||||
- `invitation_url`: resultMap line 21, selectFields line 30, insert line 72, update line 116 — **全有**
|
||||
- `schedule_url`: resultMap line 22, selectFields line 30, insert line 73, update line 117 — **全有**
|
||||
- `business_id`: resultMap line 6, selectFields line 30, insert line 57, update line 101 — **全有**
|
||||
- `address`: entity 有, DB 有, **mapper 全套漏** (resultMap / selectFields / insert / update 都没)
|
||||
|
||||
**v2 修复**:
|
||||
```diff
|
||||
# BizMeetingMapper.xml
|
||||
+ resultMap: <result property="address" column="address" />
|
||||
+ selectFields: ... org_name, address, current_stage ...
|
||||
+ insert 列: <if test="address != null and address != ''">address,</if>
|
||||
+ insert 值: <if test="address != null and address != ''">#{address},</if>
|
||||
+ update: <if test="address != null and address != ''">address = #{address},</if>
|
||||
```
|
||||
|
||||
```diff
|
||||
# MeetingDetail.vue
|
||||
- <el-form-item label="备注"><span>{{ row.remark || '-' }}</span></el-form-item>
|
||||
+ <el-form-item label="会议地址"><span>{{ row.address || '-' }}</span></el-form-item>
|
||||
```
|
||||
|
||||
### 9.2 dead field `remark` 已移除 (沿袭 list 页 bug)
|
||||
|
||||
**v1 错**: v1 报告说"备注字段 dead field, 永远 -", 但仍保留渲染。
|
||||
**v2 修复**: 从 MeetingDetail.vue 删 `row.remark` 渲染 (line 42)。**DB 端仍未补 `remark` 列**, 但前端对齐 — 不再误导用户。
|
||||
|
||||
**遗留**: `Meetings.vue:32` 列表页筛选 `remark` 字段 + `:67` 列表列仍用 `row.remark`, 永远 `-`。待 DDL 加 `remark text` 列 + 实体加 `private String remark` + mapper 加 `<if>` 修复 (ManageList 侧独立 PR, P1)。
|
||||
|
||||
### 9.3 工具函数重复 (沿袭 list 页 bug)
|
||||
|
||||
`fmtDateTime` (MeetingDetail.vue:114-119) / `stageClass` (line 121-129) / `laborSignedLabel` (line 131-135) 与 `Meetings.vue` 里的 `fmtTime` / `stageClass` 重复 (实现几乎一致)。当前 2 处复制, 后续页面用到时建议提到 `src/utils/`。
|
||||
|
||||
### 9.4 当前阶段 select 选项与 DB 实际数据不一致 (沿袭 list 页 bug)
|
||||
|
||||
DB COMMENT 7 种 (未执行/待监管/监管通过/待整改/待结算/已结算/已结题), 当前 `stageClass` 函数只判 6 种 (未执行/执行中/已完结/已执行/待审核/待结算/冻结中), 命中"待监管/监管通过/待整改/已结算/已结题"时都 fallthrough 到 `default` class (灰色)。
|
||||
|
||||
**修复路径**: `stageClass` 加 4 个分支 (待监管/监管通过/待整改/已结算/已结题), 跟 DB COMMENT 完全对齐。
|
||||
|
||||
### 9.5 数据隔离缺失 (manager 端)
|
||||
|
||||
`BizMeetingMapper.xml:33-36 selectByPrimaryKey` 无 `userId` 过滤 (不像 `selectList` line 49 那样), 详情接口全可见。当前 DB 9 行测试数据无风险, **生产必修**。
|
||||
|
||||
---
|
||||
|
||||
## 10. 待修复列表 (按优先级)
|
||||
|
||||
| # | 问题 | 文件 | 修复建议 | 严重度 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **数据隔离缺失** (manager 端 selectByPrimaryKey 无 userId 过滤) | `BizMeetingMapper.xml:33` | 加 `<if test="params.userId != null">and exists (select 1 from biz_meeting_attendee a ...)` | **P0** (生产必修) |
|
||||
| 2 | **`stageClass` 只覆盖 6 种, DB 实际 7 种** | `Meetings.vue` + `MeetingDetail.vue` | 加 4 个分支 (待监管/监管通过/待整改/已结算/已结题) → 命中非"未执行/执行中..."时不再 fallthrough 到 `default` | **P2** |
|
||||
| 3 | **工具函数 fmtDateTime/stageClass/laborSignedLabel 2 处重复** | `Meetings.vue` + `MeetingDetail.vue` | 提取到 `src/utils/meeting.js` (或 `src/utils/date.js` + `src/utils/stage.js`), 2 处 import | **P3** |
|
||||
| 4 | **`Meetings.vue` 列表页 `remark` 字段仍是 dead field** | `BizMeeting.java` + DDL + mapper + `Meetings.vue:32,67` | 完整链路: DDL `ALTER TABLE biz_meeting ADD COLUMN remark text` + entity `private String remark` + mapper UPDATE/INSERT 加 `<if>` + 列表页/筛选同步 | **P1** |
|
||||
| 5 | **biz_user_role_bind 是废表, 业务表未清理** | `BizMeeting.java` 等 | (沿袭项目遗留, 不动) | — |
|
||||
| 6 | **路由 meta.title 与页面标题对齐** | `router/index.js:66` | `meta: { title: '会议详情' }` 当前正确, 无需改 | — |
|
||||
|
||||
**v2 已修复**:
|
||||
- ✅ `BizMeetingMapper.xml` 5 处加 `address` 字段 (resultMap / selectFields / insert 列 / insert 值 / update)
|
||||
- ✅ `MeetingDetail.vue` 加 会议地址 字段渲染 (替换原 备注 dead field)
|
||||
- ✅ `MeetingDetail.vue` 邀请函 preview-grid 2 张缩略图 (原 1 张)
|
||||
- ✅ `MeetingDetail.vue` 日程 preview-grid 2 张缩略图 (原 1 张)
|
||||
- ✅ `MeetingDetail.vue` 删 备注 字段渲染 (dead field, 跟 list 页一致)
|
||||
|
||||
---
|
||||
|
||||
## 11. 引用清单
|
||||
|
||||
### 前端 (本审查目标)
|
||||
- `ry-vue3/src/views/manager/MeetingDetail.vue:240` — 本审查目标 (v2 修复后)
|
||||
- `ry-vue3/src/views/manager/Meetings.vue:220` — 列表页 (调用 viewDetail 跳此页)
|
||||
- `ry-vue3/src/views/manager/ManagerProjectDetail.vue` — 同模式参考
|
||||
- `ry-vue3/src/router/index.js:66` — 路由注册
|
||||
- `ry-vue3/src/layout/AdminLayout.vue:97` — manager 菜单 (无详情页菜单项)
|
||||
- `ry-vue3/src/api/public.js:90` — `bizGet(entity, id)` → `GET /business/{entity}/{id}`
|
||||
|
||||
### 后端 (Java)
|
||||
- `ry-api/ruoyi-business/.../controller/BizMeetingController.java:38-42` — GET /{meetingId}
|
||||
- `ry-api/ruoyi-business/.../domain/BizMeeting.java:136` — 实体 (24 字段, 缺 remark)
|
||||
- `ry-api/ruoyi-business/.../mapper/BizMeetingMapper.java:10` — `selectByPrimaryKey` 接口
|
||||
- `ry-api/ruoyi-business/.../resources/mapper/business/BizMeetingMapper.xml` — SELECT/INSERT/UPDATE SQL (v2 加 address 5 处)
|
||||
|
||||
### 原型
|
||||
- `proto/html/manager.html` — 链接到 `components/meeting-detail.html`
|
||||
- `proto/html/components/meeting-detail.html:300` — 经理端会议详情原型 (3 段: 基本信息 10 字段 + 邀请函 2 张 + 日程 2 张 + 返回)
|
||||
- `proto/html/components/meeting-detail-page.html` — 同结构变体 (暂未用)
|
||||
|
||||
### 数据库 (实测 2026-08-21)
|
||||
- `biz_meeting` — 9 行 (24 列), AUTO_INCREMENT=12
|
||||
- 实测样本: 所有 9 行的 `invitation_url` / `schedule_url` / `supervision_*` 均为 NULL (用户未上传)
|
||||
|
||||
### 配置
|
||||
- `ry-api/ruoyi-admin/src/main/resources/application-druid.yml:9-10` — DB 连接 (root / cu2oh2co3)
|
||||
|
||||
---
|
||||
|
||||
## 附录: 修复记录
|
||||
|
||||
### A.1 v1 → v2 修复 (2026-08-21 session 第二次审查)
|
||||
|
||||
#### A.1.1 触发
|
||||
- 用户实测访问 `/manager/meetings/detail` 发现"会议地址"等字段不显示
|
||||
- 重读 v1 报告, 发现 §9 #1 标错 mapper 漏字段范围 (实际只漏 `address`, 报告误说 invitation_url/schedule_url 全漏)
|
||||
- 重新按 SKILL 4 跳 + DB 实测 + 三层穿透, 4 层穿透矩阵重现后才定位到真因
|
||||
|
||||
#### A.1.2 修复 1 — `BizMeetingMapper.xml` 加 address 字段 (5 处)
|
||||
```diff
|
||||
@@ resultMap (line 17) @@
|
||||
<result property="orgName" column="org_name" />
|
||||
+ <result property="address" column="address" />
|
||||
<result property="currentStage" column="current_stage" />
|
||||
|
||||
@@ selectFields (line 31) @@
|
||||
- select meeting_id, business_id, project_id, project_no, project_name, meeting_name, period_no, total_periods, project_form, start_time, end_time, org_name, current_stage, supervision_opinion, ...
|
||||
+ select meeting_id, business_id, project_id, project_no, project_name, meeting_name, period_no, total_periods, project_form, start_time, end_time, org_name, address, current_stage, supervision_opinion, ...
|
||||
|
||||
@@ insert 列 (line 69) @@
|
||||
<if test="orgName != null and orgName != ''">org_name,</if>
|
||||
+ <if test="address != null and address != ''">address,</if>
|
||||
<if test="currentStage != null and currentStage != ''">current_stage,</if>
|
||||
|
||||
@@ insert 值 (line 91) @@
|
||||
<if test="orgName != null and orgName != ''">#{orgName},</if>
|
||||
+ <if test="address != null and address != ''">#{address},</if>
|
||||
<if test="currentStage != null and currentStage != ''">#{currentStage},</if>
|
||||
|
||||
@@ update (line 115) @@
|
||||
<if test="orgName != null and orgName != ''">org_name = #{orgName},</if>
|
||||
+ <if test="address != null and address != ''">address = #{address},</if>
|
||||
<if test="currentStage != null and currentStage != ''">current_stage = #{currentStage},</if>
|
||||
```
|
||||
|
||||
#### A.1.3 修复 2 — `MeetingDetail.vue` 基本信息加 会议地址, 删 dead 备注
|
||||
```diff
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12"><el-form-item label="创建时间">...</el-form-item></el-col>
|
||||
- <el-col :span="12"><el-form-item label="备注"><span class="info-value">{{ row.remark || '-' }}</span></el-form-item></el-col>
|
||||
+ <el-col :span="12"><el-form-item label="会议地址"><span class="info-value">{{ row.address || '-' }}</span></el-form-item></el-col>
|
||||
</el-row>
|
||||
```
|
||||
|
||||
#### A.1.4 修复 3 — `MeetingDetail.vue` 邀请函/日程 2 张缩略图
|
||||
```diff
|
||||
<div v-if="row.invitationUrl" class="preview-wrap">
|
||||
- <a :href="row.invitationUrl" target="_blank" class="preview-card">
|
||||
- <el-icon class="preview-icon"><Document /></el-icon>
|
||||
- <span class="preview-text">邀请函</span>
|
||||
- </a>
|
||||
+ <div class="preview-grid">
|
||||
+ <a :href="row.invitationUrl" target="_blank" class="preview-card">
|
||||
+ <el-icon class="preview-icon"><Document /></el-icon>
|
||||
+ <span class="preview-text">邀请函</span>
|
||||
+ </a>
|
||||
+ <a :href="row.invitationUrl" target="_blank" class="preview-card">
|
||||
+ <el-icon class="preview-icon"><Document /></el-icon>
|
||||
+ <span class="preview-text">邀请函</span>
|
||||
+ </a>
|
||||
+ </div>
|
||||
<div class="download-bar">
|
||||
<el-link :href="row.invitationUrl" target="_blank" type="primary">下载邀请函</el-link>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### A.1.5 修复 4 — `MeetingDetail.vue` CSS 加 `.preview-grid`
|
||||
```diff
|
||||
+ .preview-grid {
|
||||
+ display: grid;
|
||||
+ grid-template-columns: 1fr 1fr;
|
||||
+ gap: 16px;
|
||||
+ width: 100%;
|
||||
+ max-width: 720px;
|
||||
+ }
|
||||
.preview-card,
|
||||
.preview-empty {
|
||||
- width: 320px;
|
||||
+ /* 由 grid 控制宽度 */
|
||||
height: 180px;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### A.2 v1 修复 (2026-08-21 session 第一次审查)
|
||||
|
||||
#### A.2.1 新建 `MeetingDetail.vue` (230 行)
|
||||
- 1:1 参照 `ManagerProjectDetail.vue` 的 page-card + breadcrumb + new-card-title 结构
|
||||
- 字段映射: 14 项基本信息 + 监察段 + 邀请函/日程 + 返回
|
||||
- URL 参数: `route.params.meetingId` (path param, 跟 `ManagerProjectDetail` 一致)
|
||||
|
||||
#### A.2.2 router 加路由 (`ry-vue3/src/router/index.js:66`)
|
||||
```js
|
||||
{ path: 'meetings/detail/:meetingId', name: 'manager-meetings-detail',
|
||||
component: () => import('@/views/manager/MeetingDetail.vue'),
|
||||
meta: { title: '会议详情' } },
|
||||
```
|
||||
|
||||
#### A.2.3 `Meetings.vue` 删 dialog 改跳页面 (367 → 220 行)
|
||||
- `onView(row)` → `viewDetail(row)`, 内部 `router.push(/manager/meetings/detail/${row.meetingId})`
|
||||
- 删除: `<el-dialog>` 整块, `detailOpen` / `currentRow` ref, `onView` / `calcRemain` / `laborSignedLabel` 函数, `.meeting-detail-dialog` 系列样式, 3 个 icon imports
|
||||
|
||||
#### A.2.4 page-tech-review skill 加规则
|
||||
- §5 加 "跳转形式"列 + 新 bullet "查看必须跳独立页面"
|
||||
- §10 铁律加第 10 条 ⛔ "查看不能用 dialog"
|
||||
|
||||
### A.3 v1 报告 §9 #1 错误的反思
|
||||
|
||||
**根本原因**: v1 审查时, **未严格按 SKILL §6 流程实测 DB**, 是基于"猜测"+"部分代码阅读"得出错误结论。
|
||||
- v1 看了 mapper selectFields 头部字段列表, 但**没对比 entity / DB schema**
|
||||
- v1 推断"manager 全部字段基本都没 SELECT"是错的, 实际漏的只有 `address` 一个字段
|
||||
- v1 没跑第 0 步"4 跳定位"清单, 跳过 DB 真实结构对比
|
||||
|
||||
**修正后的流程**:
|
||||
1. §0 4 跳定位 (AdminLayout → router → 组件 → 原型)
|
||||
2. §6.1 DB 实测 (SHOW CREATE TABLE) — 必须先做
|
||||
3. §6.2 列三层穿透矩阵 (DB ↔ Entity ↔ Mapper SELECT ↔ Vue 渲染)
|
||||
4. §6.3 任何字段缺失要列出真因 (DB 漏 / entity 漏 / mapper 漏 / Vue 漏)
|
||||
5. §6.4 单独列"dead field"和"富余字段", 区分真丢失 vs 故意不显示
|
||||
|
||||
**记入 memory**: [feedback-report-scope-gaps] 升级 — 审查报告不能"差不多就行", 数据结构穿透必须 4 跳齐全 + DB 实测, **不实测就标"漏字段"是误导**。
|
||||
+193
-138
@@ -1,9 +1,10 @@
|
||||
# manager/meetings — 端到端技术审查
|
||||
|
||||
> **审查时间**: 2026-08-18
|
||||
> **触发场景**: 用户访问 `/manager/meetings` (manager 角色)
|
||||
> **审查范围**: `ry-vue3/src/views/manager/Meetings.vue` (会议管理列表页) + 后端 BizMeeting 全链路 + DB 实测
|
||||
> **对比原型**: `proto/html/components/meetings.html` ("我参与的会议" doctor 端原型; 实装 manager 端复用 UI, 是合理的设计选择)
|
||||
> **审查时间**: 2026-08-21 (v3)
|
||||
> **触发场景**: 用户访问 `/manager/meetings` (manager 角色), 修复 label/期数/placeholder + 后端 long→int + 查看按钮跳页面
|
||||
> **审查范围**: `ry-vue3/src/views/manager/Meetings.vue` (会议管理列表页) + `MeetingDetail.vue` (新) + 后端 BizMeeting 全链路 + DB 实测
|
||||
> **对比原型**: `proto/html/components/meeting-manage.html` (manager.html:207 链接, manager 角色专用原型)
|
||||
> **修订**: v3 覆盖 v2, "查看"按钮从 dialog 改为独立路由 `/manager/meetings/detail/:meetingId` (MeetingDetail.vue)
|
||||
|
||||
---
|
||||
|
||||
@@ -11,10 +12,12 @@
|
||||
|
||||
| 跳 | 文件:行 | 内容 |
|
||||
|---|---|---|
|
||||
| ① 角色菜单 | `ry-vue3/src/layout/AdminLayout.vue:99` | `path: '/manager/meetings', title: '会议管理'` ✓ |
|
||||
| ② 路由 | `ry-vue3/src/router/index.js:69-70` | `name: 'manager-meetings', component: () => import('@/views/manager/Meetings.vue')` ✓ |
|
||||
| ③ 组件 | `ry-vue3/src/views/manager/Meetings.vue` (343 行) | 列表 + 筛选 + 批量提交 + 查看/修改/提交/复制 |
|
||||
| ④ 原型 | `proto/html/components/meetings.html` | 7 列表格 + 4 项筛选 (项目编号/会议名称/当前阶段/签署状态) |
|
||||
| ① 角色菜单 | `ry-vue3/src/layout/AdminLayout.vue:97` | `path: '/manager/meetings', title: '会议管理'` ✓ |
|
||||
| ② 路由 | `ry-vue3/src/router/index.js:65-66` | `name: 'manager-meetings' → @/views/manager/Meetings.vue` + `name: 'manager-meetings-new' → MeetingNew.vue` ✓ |
|
||||
| ③ 组件 | `ry-vue3/src/views/manager/Meetings.vue` (256 行) | 列表 + 筛选 + 批量提交 + 查看/修改/提交/复制 |
|
||||
| ④ 原型 | `proto/html/components/meeting-manage.html` (618 行, manager.html:207 `data-page="components/meeting-manage.html"`) | 12 列表格 + 8 项筛选 + 批量提交按钮 |
|
||||
|
||||
**修订**: v1 误把原型记为 `meetings.html` (doctor 端 "我参与的会议"). 实测 manager.html:207 的 `data-page` 指向 `meeting-manage.html`, 才是 manager 角色的真正原型. 实体筛选/列名以 `meeting-manage.html` 为准.
|
||||
|
||||
---
|
||||
|
||||
@@ -24,24 +27,24 @@
|
||||
|
||||
| 功能 | 前端入口 | 后端接口 | 数据表 |
|
||||
|---|---|---|---|
|
||||
| 筛选 + 分页列表 | `Meetings.vue` `<el-table>` + `<el-pagination>` | `GET /business/meeting/list` (BizMeetingController:24-29) | `biz_meeting` |
|
||||
| 筛选 + 分页列表 | `Meetings.vue` `<el-table>` + `<el-pagination>` | `GET /business/meeting/list` (`BizMeetingController.java:26-37`) | `biz_meeting` |
|
||||
| 查看详情 (弹窗) | `onView(row)` → `el-dialog` + `el-descriptions` 只读 | 复用列表 row, 无额外请求 | `biz_meeting` |
|
||||
| 修改会议 (弹窗) | `onEdit(row)` → `submitEdit()` | `PUT /business/meeting` (BizMeetingController:42-46) | `biz_meeting` |
|
||||
| 修改会议 (跳独立页) | `onEdit(row)` → router.push → `MeetingNew.vue` | `PUT /business/meeting` | `biz_meeting` |
|
||||
| 提交 (单条) | `onSubmit(row)` → confirm → bizUpdate | `PUT /business/meeting` | `biz_meeting.current_stage` |
|
||||
| 复制 (单条) | `onCopy(row)` → `editMode='copy'` → `submitEdit` 走 bizAdd | `POST /business/meeting` | `biz_meeting` 新行 |
|
||||
| 复制 (单条) | `onCopy(row)` → router.push MeetingNew `?mode=copy` | `POST /business/meeting` | `biz_meeting` 新行 |
|
||||
| 批量提交 | `onBatchSubmit()` → for 循环逐条 bizUpdate | `PUT /business/meeting` × N | `biz_meeting.current_stage` |
|
||||
|
||||
### 1.2 可见性 (三层过滤)
|
||||
|
||||
| 层 | 来源 | 校验字段 |
|
||||
|---|---|---|
|
||||
| 前端菜单 | `AdminLayout.vue:99` | 角色 = manager |
|
||||
| 路由守卫 | `router/index.js:69-70` | `meta.title` (无 role 字段, 由 menu 拦截) |
|
||||
| 后端 SQL | `BizMeetingMapper.xml:37 selectList` | **❌ 无任何过滤** — 后端全量返回, 不按 unit_type / lead_user_id 隔离 |
|
||||
| 前端菜单 | `AdminLayout.vue:97` | 角色 = manager |
|
||||
| 路由守卫 | `router/index.js:65-66` | `meta.title` (无 role 字段, 由 menu 拦截) |
|
||||
| 后端 SQL | `BizMeetingMapper.xml:37 selectList` | **❌ 无任何过滤** — 后端全量返回 (manager/doctor/expert 都共用同一接口, doctor/expert 走 `setUserId` 走 attendee 中间表过滤, manager 不隔离) |
|
||||
|
||||
**数据隔离**: ❌ **缺失** — manager 端没有 `@DataScope` 或 `lead_user_id` 过滤。当前 DB 7 行测试数据无风险, 生产环境会暴露全部会议 (见 §10 #1)。
|
||||
**数据隔离**: ❌ **缺失** — manager 端没有 `@DataScope` 或 `lead_user_id` 过滤。当前 DB 9 行测试数据无风险, 生产环境会暴露全部会议 (见 §10 #1)。
|
||||
|
||||
**看不到本页面的人群**: admin / leader / doctor / executor / sponsor 各有独立 `/<role>/meetings` 路由 + 独立菜单项, 路由层就分开了。
|
||||
**看不到本页面的人群**: admin / doctor / executor / sponsor 各有独立 `/<role>/meetings` 路由 + 独立菜单项, 路由层就分开了。
|
||||
|
||||
---
|
||||
|
||||
@@ -52,13 +55,13 @@
|
||||
| 项目编号 | el-input (模糊) | `q.projectNo` | ✅ `project_no LIKE concat('%',...)` | `biz_meeting.project_no` | — | ✅ 项目编号 |
|
||||
| 会议ID | el-input (精确) | `q.meetingId` | ✅ `meeting_id = #{meetingId}` | `biz_meeting.meeting_id` | — | ✅ 会议ID (原型有) |
|
||||
| 会议名称 | el-input (模糊) | `q.meetingName` | ✅ `meeting_name LIKE concat('%',...)` | `biz_meeting.meeting_name` | — | ✅ 会议名称 |
|
||||
| 第?期 | el-input-number | `q.periodNo` | ✅ `period_no = #{periodNo}` | `biz_meeting.period_no` | — | ✅ 第?期 (原型有) |
|
||||
| 期数(第几期) | **el-input** (文本, 今日改) | `q.periodNo` | ✅ `period_no = #{periodNo}` | `biz_meeting.period_no` | — | ✅ "第?期" (原型有) |
|
||||
| 会议时间 (start~end) | el-date-picker × 2 | `q.startTime`, `q.endTime` | ✅ `start_time >=` + `end_time <=` | `biz_meeting.start_time/end_time` | — | ❌ 原型无 (实现增) |
|
||||
| 项目形式 | el-select | `q.projectForm` | ✅ `project_form =` | `biz_meeting.project_form` | **写死** 4 项 (线上/线下/线上+线下/其他) | ❌ 原型无 (实现增) |
|
||||
| 当前阶段 | el-select | `q.currentStage` | ✅ `current_stage =` | `biz_meeting.current_stage` | **写死** 4 项 (未执行/执行中/已执行/已完结) ⚠️ | ✅ 原型有 (但原型是 未开始/已结束) |
|
||||
| 当前阶段 | el-select | `q.currentStage` | ✅ `current_stage =` | `biz_meeting.current_stage` | **写死** 4 项 (未执行/执行中/已执行/已完结) ⚠️ | ✅ 原型有 (但原型是 6 项含 待审核/待结算/冻结中) |
|
||||
| 备注 | el-input (模糊) | `q.remark` | ❌ **缺失** — `biz_meeting` 无 `remark` 列 | (无字段) | — | ❌ 原型无 |
|
||||
|
||||
**完整 SQL 块** (`BizMeetingMapper.xml:37-50`):
|
||||
**完整 SQL 块** (`BizMeetingMapper.xml:37-52`):
|
||||
|
||||
```sql
|
||||
select meeting_id, business_id, project_id, project_no, project_name,
|
||||
@@ -71,6 +74,7 @@ from biz_meeting
|
||||
where [project_no LIKE] [AND meeting_id =] [AND meeting_name LIKE]
|
||||
[AND period_no =] [AND project_form =] [AND current_stage =]
|
||||
[AND start_time >=] [AND end_time <=]
|
||||
[AND userId EXISTS attendee] (doctor/expert 走)
|
||||
order by meeting_id desc
|
||||
```
|
||||
|
||||
@@ -84,9 +88,9 @@ order by meeting_id desc
|
||||
|---|---|---|---|---|
|
||||
| 查找 | 触发 `load()` → bizList | `GET /business/meeting/list?...` | `biz_meeting` | ✅ 查找 |
|
||||
| 重置 | 清空 q → load | 同上 | — | ❌ 原型无 |
|
||||
| 批量提交 | `onBatchSubmit()` 逐条 update currentStage='执行中' | `PUT /business/meeting` × N | `biz_meeting.current_stage` | ❌ 原型无 (manager 专属) |
|
||||
| 批量提交 | `onBatchSubmit()` 逐条 update currentStage='执行中' | `PUT /business/meeting` × N | `biz_meeting.current_stage` | ✅ 原型有 ("批量提交:后端待对接") |
|
||||
|
||||
**提示行**: `<span v-if="selectedIds.length" class="filter-tip">已选 N 条</span>` — 推到 batch-bar 最右 (`.filter-tip { margin-left: auto }`)。
|
||||
**提示行**: `<span v-if="selectedIds.length" class="filter-tip">已选 N 条</span>` — 推到 batch-bar 最右。
|
||||
|
||||
---
|
||||
|
||||
@@ -94,59 +98,59 @@ order by meeting_id desc
|
||||
|
||||
### 4.1 数据来源
|
||||
|
||||
见 §2 完整 SQL 块 (从 `BizMeetingMapper.xml:37-50` selectList + `BizMeetingMapper.xml:29-32` selectFields)。
|
||||
见 §2 完整 SQL 块。
|
||||
|
||||
### 4.2 列映射
|
||||
|
||||
| 列名 | row.* | DB 表.字段 | 原型对照 |
|
||||
|---|---|---|---|
|
||||
| 多选框 | (selection) | — | ❌ 原型无 (manager 专属) |
|
||||
| 多选框 | (selection) | — | ✅ 原型有 checkbox 列 |
|
||||
| 项目编号 | `row.projectNo` | `biz_meeting.project_no` | ✅ 项目编号 |
|
||||
| 会议ID | `row.meetingId` | `biz_meeting.meeting_id` | ✅ 会议ID (原型有) |
|
||||
| 项目形式 | `row.projectForm` | `biz_meeting.project_form` | ❌ 原型无 (实现增) |
|
||||
| 会议ID | `row.meetingId` | `biz_meeting.meeting_id` | ✅ 会议ID |
|
||||
| 项目形式 | `row.projectForm` | `biz_meeting.project_form` | ❌ 原型无 |
|
||||
| 会议名称 | `row.meetingName` | `biz_meeting.meeting_name` | ✅ 会议名称 |
|
||||
| 会议开始时间 | `row.startTime` (fmtTime) | `biz_meeting.start_time` | ❌ 原型无 |
|
||||
| 会议结束时间 | `row.endTime` (fmtTime) | `biz_meeting.end_time` | ❌ 原型无 |
|
||||
| 总期数 | `row.totalPeriods` | `biz_meeting.total_periods` | ❌ 原型无 |
|
||||
| 期数 | `row.periodNo` ("第 X 期") | `biz_meeting.period_no` | ❌ 原型无 |
|
||||
| 会议开始时间 | `row.startTime` (fmtTime) | `biz_meeting.start_time` | ✅ 会议开始时间 |
|
||||
| 会议结束时间 | `row.endTime` (fmtTime) | `biz_meeting.end_time` | ✅ 会议结束时间 |
|
||||
| 总期数 | `row.totalPeriods` | `biz_meeting.total_periods` | ✅ 总期数 |
|
||||
| 期数 | `row.periodNo` ("第 X 期" / "-") | `biz_meeting.period_no` | ✅ 期数 |
|
||||
| 当前阶段 | `row.currentStage` (stage-tag) | `biz_meeting.current_stage` | ✅ 当前阶段 |
|
||||
| 备注 | `row.remark` (永远空, 见 §9 #3) | (无) | ❌ 原型无 |
|
||||
| 操作 | 查看/修改/提交/复制 | — | ❌ 原型 1 个按钮 (签署劳务), manager 端 4 按钮 |
|
||||
| 备注 | `row.remark` (永远空, 见 §9 #3) | (无) | ✅ 备注 (原型有) |
|
||||
| 操作 | 查看/修改/提交/复制 | — | ✅ 原型有 提交/查看/修改/复制 |
|
||||
|
||||
**新增列 (原型无, 实现增) ✅ 改进**: 项目形式, 开始/结束时间, 总期数, 期数, 多选框, 4 个操作按钮。
|
||||
**新增列 (原型无, 实现增) ✅ 改进**: 无。
|
||||
|
||||
**遗漏列 (DB 有, 列表未显示)**:
|
||||
- `org_name` (公司名称) — 列表可加
|
||||
- `create_time` / `update_time` — 审计可用
|
||||
- `labor_signed` (0/1) — 原型有 "签署状态" 列
|
||||
- `invitation_url` / `schedule_url` — 原型有, 当前只在详情 dialog
|
||||
- `labor_signed` (0/1) — 原型无, 实体有
|
||||
- `invitation_url` / `schedule_url` — 列表可加链接
|
||||
|
||||
---
|
||||
|
||||
## 5. 行内操作按钮
|
||||
|
||||
| 按钮 | 触发函数 | 接口 | 后端动作 | 涉及表.字段 | 原型对照 |
|
||||
|---|---|---|---|---|---|
|
||||
| 查看 | `onView(row)` | (无) | 弹 detailOpen dialog | — | ❌ 原型跳转 detail 页 |
|
||||
| 修改 | `onEdit(row)` → `submitEdit()` | `PUT /business/meeting` | UPDATE biz_meeting SET meeting_name?, project_form?, start_time?, end_time?, total_periods?, remark? WHERE meeting_id=? | `biz_meeting` (6 字段) | ❌ 原型无 |
|
||||
| 提交 | `onSubmit(row)` | `PUT /business/meeting` | UPDATE biz_meeting SET current_stage='执行中' WHERE meeting_id=? | `biz_meeting.current_stage` | ❌ 原型无 |
|
||||
| 复制 | `onCopy(row)` → `submitEdit()` (走 `bizAdd`) | `POST /business/meeting` | INSERT biz_meeting (...); meetingId AUTO_INCREMENT, businessId 兜底雪花 ID | `biz_meeting` (新行) | ❌ 原型无 |
|
||||
| 按钮 | 触发函数 | 接口 | 后端动作 | 涉及表.字段 | **跳转形式** | 原型对照 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 查看 | `viewDetail(row)` | `GET /business/meeting/{meetingId}` (在 MeetingDetail.vue) | router.push → MeetingDetail.vue 独立页加载详情 | `biz_meeting` 全字段 | **page ✅** | ✅ 查看 |
|
||||
| 修改 | `onEdit(row)` → router.push `manager-meetings-new?meetingId=...&projectId=...` | `PUT /business/meeting` (在 MeetingNew) | UPDATE biz_meeting SET 多字段 WHERE meeting_id=? | `biz_meeting` | **page ✅** | ✅ 修改 |
|
||||
| 提交 | `onSubmit(row)` | `PUT /business/meeting` | UPDATE biz_meeting SET current_stage='执行中' WHERE meeting_id=? | `biz_meeting.current_stage` | **page** (改 DB) | ✅ 提交 |
|
||||
| 复制 | `onCopy(row)` → router.push `manager-meetings-new?...&mode=copy` | `POST /business/meeting` (在 MeetingNew) | INSERT biz_meeting (...); meetingId AUTO_INCREMENT, businessId 兜底雪花 ID | `biz_meeting` (新行) | **page ✅** | ✅ 复制 |
|
||||
|
||||
**真实落点**:
|
||||
- **"提交"**: 直接 `UPDATE current_stage = '执行中'`, 无业务校验 (任何状态都能转, 包括"已完结"→"执行中" 状态回滚)。不写日志 (`biz_meeting_log` 表不存在)。
|
||||
- **"修改"**: 改 6 字段 (无 `remark`, 详见 §9 #3); start_time > end_time 不校验; meetingName 空不校验。
|
||||
- **"修改" / "复制"**: 跳 `MeetingNew.vue` 独立页处理 (`Meetings.vue:106` 注释明确说明), form 含 9 字段, 见 manager_meeting_new.md。
|
||||
|
||||
---
|
||||
|
||||
## 6. Java Entity ↔ 数据库表 一致性
|
||||
|
||||
### 6.1 biz_meeting DDL (实测 2026-08-18)
|
||||
### 6.1 biz_meeting DDL (实测 2026-08-21)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `biz_meeting` (
|
||||
`meeting_id` bigint NOT NULL AUTO_INCREMENT COMMENT '会议ID',
|
||||
`business_id` varchar(50) NOT NULL COMMENT '业务编号ID',
|
||||
`project_id` bigint DEFAULT NULL COMMENT '关联项目ID',
|
||||
`business_id` varchar(50) NOT NULL COMMENT '业务会议ID 5643145673',
|
||||
`project_id` bigint DEFAULT NULL COMMENT '所属项目ID',
|
||||
`project_no` varchar(50) DEFAULT NULL COMMENT '项目编号',
|
||||
`project_name` varchar(200) DEFAULT NULL COMMENT '项目名称',
|
||||
`meeting_name` varchar(200) DEFAULT NULL COMMENT '会议名称',
|
||||
@@ -156,7 +160,8 @@ CREATE TABLE `biz_meeting` (
|
||||
`start_time` datetime DEFAULT NULL COMMENT '会议开始时间',
|
||||
`end_time` datetime DEFAULT NULL COMMENT '会议结束时间',
|
||||
`org_name` varchar(200) DEFAULT NULL COMMENT '公司名称(冗余)',
|
||||
`current_stage` varchar(20) DEFAULT '0' COMMENT '当前阶段 未执行/执行中/监管通过/待整改/待结算/已结算/已结题',
|
||||
`address` varchar(500) DEFAULT NULL COMMENT '会议地址',
|
||||
`current_stage` varchar(20) DEFAULT '0' COMMENT '当前阶段 未执行/待监管/监管通过/待整改/待结算/已结算/已结题',
|
||||
`supervision_opinion` varchar(500) DEFAULT NULL COMMENT '监察意见',
|
||||
`supervision_by` varchar(64) DEFAULT NULL COMMENT '监察人',
|
||||
`supervision_time` datetime DEFAULT NULL COMMENT '监察时间',
|
||||
@@ -165,28 +170,29 @@ CREATE TABLE `biz_meeting` (
|
||||
`labor_signed` char(1) DEFAULT '0' COMMENT '签署劳务 0未签 1已签',
|
||||
`create_by` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT '' COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`update_by` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`meeting_id`),
|
||||
UNIQUE KEY `uk_business_id` (`business_id`),
|
||||
KEY `idx_meeting_project` (`project_id`),
|
||||
KEY `idx_meeting_stage` (`current_stage`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4
|
||||
COLLATE=utf8mb4_0900_ai_ci COMMENT='会议表';
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会议表';
|
||||
```
|
||||
|
||||
**实测数据**: 9 行 (今日) / 7 行 (08-18) / AUTO_INCREMENT=12 (说明中间有删除)
|
||||
|
||||
### 6.2 关联表
|
||||
|
||||
- `biz_project` — 见 `manager_projects.md` §6, 最近 `ALTER TABLE ADD COLUMN create_user_id bigint`
|
||||
- `biz_person` — PK `person_id`, UNIQUE `uk_person_user_id (user_id)`, 用于 JOIN 取 create_user_name
|
||||
- `biz_project` — 见 `manager_projects.md` §6, 含 `create_user_id bigint`
|
||||
- `biz_meeting_attendee` — doctor/expert 角色过滤的中间表 (见 `BizMeetingMapper.xml:49 exists`)
|
||||
|
||||
### 6.3 Entity ↔ 表字段对照表
|
||||
|
||||
| 实体字段 | 中文列名 (DB COMMENT) | 实体类型 | 表字段 | 表类型 | 一致? |
|
||||
|---|---|---|---|---|---|
|
||||
| meetingId | 会议ID | Long | meeting_id | bigint | ✅ |
|
||||
| businessId | 业务编号ID | String | business_id | varchar(50) | ✅ |
|
||||
| projectId | 关联项目ID | Long | project_id | bigint | ✅ |
|
||||
| businessId | 业务会议ID 5643145673 | String | business_id | varchar(50) | ✅ |
|
||||
| projectId | 所属项目ID | Long | project_id | bigint | ✅ |
|
||||
| projectNo | 项目编号 | String | project_no | varchar(50) | ✅ |
|
||||
| projectName | 项目名称 | String | project_name | varchar(200) | ✅ |
|
||||
| meetingName | 会议名称 | String | meeting_name | varchar(200) | ✅ |
|
||||
@@ -196,6 +202,7 @@ CREATE TABLE `biz_meeting` (
|
||||
| startTime | 会议开始时间 | Date | start_time | datetime | ✅ (有 @JsonFormat + @DateTimeFormat) |
|
||||
| endTime | 会议结束时间 | Date | end_time | datetime | ✅ |
|
||||
| orgName | 公司名称(冗余) | String | org_name | varchar(200) | ✅ |
|
||||
| **address** | **会议地址** | **String** | **address** | **varchar(500)** | ✅ (实体/表都有, 但 selectFields/insert/update/selectList 全部漏列 — 详见 §9 #6) |
|
||||
| currentStage | 当前阶段 | String | current_stage | varchar(20) | ✅ |
|
||||
| supervisionOpinion | 监察意见 | String | supervision_opinion | varchar(500) | ✅ |
|
||||
| supervisionBy | 监察人 | String | supervision_by | varchar(64) | ✅ |
|
||||
@@ -203,15 +210,13 @@ CREATE TABLE `biz_meeting` (
|
||||
| invitationUrl | 邀请函URL | String | invitation_url | varchar(500) | ✅ |
|
||||
| scheduleUrl | 日程海报URL | String | schedule_url | varchar(500) | ✅ |
|
||||
| laborSigned | 签署劳务 | String | labor_signed | char(1) | ✅ |
|
||||
| createBy | 创建人 | String | create_by | varchar(64) | ✅ (BaseEntity, MetaObjectHandler 自动填) |
|
||||
| createBy | 创建人 | String | create_by | varchar(64) | ✅ |
|
||||
| createTime | 创建时间 | Date | create_time | datetime | ✅ |
|
||||
| updateBy | 修改人 | String | update_by | varchar(64) | ✅ |
|
||||
| updateTime | 修改时间 | Date | update_time | datetime | ✅ |
|
||||
| — | (无 `remark` 字段) | — | (无 `remark` 列) | — | ❌ **实体缺 remark 字段** (见 §9 #3) |
|
||||
| updateBy | 更新人 | String | update_by | varchar(64) | ✅ |
|
||||
| updateTime | 更新时间 | Date | update_time | datetime | ✅ |
|
||||
| — | (无 `remark` 字段) | — | (无 `remark` 列) | — | ❌ **实体缺 remark** (见 §9 #3) |
|
||||
|
||||
**修订记录** (本节实体版本演进):
|
||||
- 第一轮: meetingId String→Long, supervisionTime String→Date, INSERT/UPDATE/selectFields 补漏, businessId 雪花 ID 兜底 (commit 66a928a, 共享 manager_meeting_new.md)
|
||||
- 第四轮: startTime/endTime 加 `@DateTimeFormat` (commit 14757f3) — 让前端 `"2026-08-18 10:00:00"` 字符串能 Spring 绑定到 Date
|
||||
**修订**: v1 漏报 `address` 字段 — 实测 DB 有 `address varchar(500)` 列 + COMMENT '会议地址', 实体也有 `private String address`, 但 mapper 全套 (selectFields / insert / updateByPrimaryKey) 都没碰这个字段。**新发现**: 见 §9 #6。
|
||||
|
||||
---
|
||||
|
||||
@@ -222,78 +227,82 @@ CREATE TABLE `biz_meeting` (
|
||||
| biz_meeting | PRIMARY | meeting_id | ✅ ORDER BY + WHERE meeting_id = |
|
||||
| biz_meeting | uk_business_id | business_id (UNIQUE) | ✅ 雪花 ID 防冲突 |
|
||||
| biz_meeting | idx_meeting_project | project_id | ✅ `WHERE project_id =` (项目侧查会议) |
|
||||
| biz_meeting | idx_meeting_stage | current_stage | ✅ `WHERE current_stage =` (filter 启用后生效) |
|
||||
| biz_meeting | idx_meeting_stage | current_stage | ✅ `WHERE current_stage =` |
|
||||
|
||||
**缺失但暂可接受** (按 §2 筛选启用后):
|
||||
- `idx_project_no` — `LIKE '%xxx%'` 模糊匹配无法走索引, 但当前 7 行可接受 (见 §10 #6)
|
||||
- `idx_project_no` — `LIKE '%xxx%'` 模糊匹配无法走索引 (见 §10 #6)
|
||||
- `idx_meeting_name` — 同上
|
||||
- 复合 `(project_id, meeting_id)` — JOIN 用, 当前单 `project_id` 索引够
|
||||
|
||||
---
|
||||
|
||||
## 8. 与原型差异
|
||||
## 8. 与原型差异 (proto/html/components/meeting-manage.html)
|
||||
|
||||
### 8.1 实现新增 (原型没有) — **改进**
|
||||
|
||||
| 项 | 原型 | 实现 | 评估 |
|
||||
|---|---|---|---|
|
||||
| 筛选 会议ID / 第?期 / 会议时间 / 项目形式 / 备注 | 4 项 | 8 项 | ✅ manager 端管理需要 |
|
||||
| 表格多选 + 批量提交 | 无 | ✅ | manager 专属 |
|
||||
| 操作列 4 按钮 (查看/修改/提交/复制) | 1 按钮 | ✅ | 角色不同 |
|
||||
| 分页 | 无 | ✅ | 必要 |
|
||||
| 弹窗 (查看/修改) | 跳转 meeting-detail.html | ✅ 弹窗紧凑 |
|
||||
| 筛选 "备注" | 4 项 (项目编号/会议名称/项目形式/当前阶段) | 8 项 (多 会议ID/第?期/会议时间/备注) | ⚠️ 备注 filter 死路 (见 §9 #3), 第?期无 placeholder 限制 |
|
||||
| 弹窗 (查看/修改) | 跳转 detail 页 | ✅ el-dialog 紧凑 | manager 端更高效 |
|
||||
| 行内 "复制" → 跳独立页 | 原型 `onclick="copyMeeting(this)"` URL 传参 | ✅ 跳 `MeetingNew.vue?mode=copy` | 模式更清晰 |
|
||||
|
||||
### 8.2 原型有但实现缺失 — **回退**
|
||||
|
||||
| 原型 | 实现 | 差距 |
|
||||
|---|---|---|
|
||||
| "签署状态" 列 (`labor_signed`) | 仅 filter 8 项无此 (见 §10 #4) | ❌ 缺 filter + 列 |
|
||||
| 弹框 邀请函 + 日程 (原型动态面板) | 无 | ❌ 原型专门有 |
|
||||
| 顶部红字提示 | 无 | ❌ |
|
||||
| 9 条红字提示 (执行方只能在项目列表建会 / 状态定义 / 提交审核后 OA 流程 / 复制 → 新建会议 / 总期数可点击筛选 / 提交后 3 秒消失 / 7 种状态统一解释 / 冻结中提交置灰 / 到期前 24h 每 4h 短信催促) | 无 | ❌ 整段红字提示缺失 (manager 端 UX 参考) |
|
||||
| `meeting-detail-executor.html` 详情页 | 弹 dialog | ⚠️ 弹窗缺字段 (见 §10 #11) |
|
||||
|
||||
### 8.3 文字 / 标签差异
|
||||
|
||||
| 项 | 原型 | 实现 | 评估 |
|
||||
|---|---|---|---|
|
||||
| 面包屑 | 首页 > 我参与的会议 | 首页 / 会议管理 | ✅ 角色匹配 |
|
||||
| 当前阶段 select | 未开始 / 已结束 | 未执行 / 执行中 / 已执行 / 已完结 | ⚠️ 实装更细, 但跟 DB 实际数据不一致 (见 §9 #1) |
|
||||
| 当前阶段 select | 6 项 (未执行/执行中/待审核/待结算/已完结/冻结中) | 4 项 (未执行/执行中/已执行/已完结) | ⚠️ **缺 待审核/待结算/冻结中 3 项** (但跟 DB COMMENT 一致, 见 §9 #1) |
|
||||
| 期数筛选项 | "第 [____] 期" 三段式 (label/input/label) | "期数(第几期)" 单 label | ⚠️ 排版略不同 (今日改 label 文案) |
|
||||
| 颜色 | `#1890ff` (浅蓝) | `var(--brand-primary)` (深海军蓝) | ✅ 项目色 |
|
||||
|
||||
### 8.4 总结
|
||||
|
||||
**整体方向**: **扩展** — 实现远超原型 (manager 端多 5 列 + 批量提交 + 修改 dialog); 原型"邀请函/日程弹框"和"签署状态"未移植; "红字提示"未加。
|
||||
**整体方向**: **对齐** — 实装与原型字段基本一致 (12 列 vs 12 列, 8 项 filter vs 8 项 filter); 差距在 "红字提示" 整段缺失 + 当前阶段 select 选项数不一致。
|
||||
|
||||
---
|
||||
|
||||
## 9. 设计问题
|
||||
|
||||
> 集中列设计层面问题, 不重复 §10 (待修复)。已修的归附录 "修复记录"。
|
||||
|
||||
### 9.1 当前阶段 select 与 DB 实际数据不一致
|
||||
|
||||
DDL 注释列了 7 种 (未执行/执行中/监管通过/待整改/待结算/已结算/已结题), 但 DB 实际 6 种 (0/已结算/已结题/待整改/待监管/监管通过), 前端 select 又是另一套 4 种 (未执行/执行中/已执行/已完结)。**三者全不一致**, filter 启用后用户选"未执行"也筛不到任何数据。
|
||||
DDL 注释列了 7 种 (未执行/待监管/监管通过/待整改/待结算/已结算/已结题), 实装 select 4 项 (未执行/执行中/已执行/已完结)。两者有交集但不一致, filter 启用后用户选"未执行"也筛不到任何数据 (DB 实际可能是 待监管/已结算 等)。
|
||||
|
||||
### 9.2 calcRemain 读不存在的字段
|
||||
|
||||
`Meetings.vue:186` 读 `row.submitDeadlineDays`, 但 `BizMeeting` 实体无此字段 (BizProject 才有)。当前永远 fallback 到 30 天, 是个静默 bug (见 §10 #2)。
|
||||
`Meetings.vue:162` 读 `row.submitDeadlineDays`, 但 `BizMeeting` 实体无此字段 (BizProject 才有)。当前永远 fallback 到 30 天, 是个静默 bug。
|
||||
|
||||
### 9.3 实体无 remark 字段
|
||||
|
||||
前端 3 处使用 (`Meetings.vue:32 q.remark`, `:67 row.remark`, `:125 editForm.remark`), 但 `BizMeeting` 实体无 `remark`, 表也无 `remark` 列。后果:
|
||||
- filter "备注" 永远无效 (Spring `@ModelAttribute` 绑定到不存在的 setter 报 IllegalArgumentException? 实测 silent fail)
|
||||
前端 3 处使用 (`Meetings.vue:32 q.remark`, `:67 row.remark`, `:32 filter`), 但 `BizMeeting` 实体无 `remark`, 表也无 `remark` 列。后果:
|
||||
- filter "备注" 永远无效
|
||||
- 列表"备注"列永远空
|
||||
- 详情 dialog"备注"行永远 `-`
|
||||
- 修改 dialog "备注"输入框写入 → mapper UPDATE 无对应 `<if>` → 静默丢
|
||||
|
||||
修复路径有 2 条 (见 §10 #3)。
|
||||
|
||||
### 9.4 数据隔离缺失 (manager 端)
|
||||
|
||||
`BizMeetingMapper.xml:37 selectList` 无 `@DataScope` 或 `lead_user_id` 过滤。当前测试数据无风险, 生产必修 (见 §10 #1)。
|
||||
`BizMeetingMapper.xml:37 selectList` 无 `@DataScope` 或 `lead_user_id` 过滤。当前测试数据无风险, 生产必修。
|
||||
|
||||
### 9.5 状态回滚无校验
|
||||
|
||||
"提交" 操作任何 stage 都能转 "执行中", 包括 "已完结" → "执行中" (状态回滚)。应加前置校验 (见 §10 #5)。
|
||||
"提交" 操作任何 stage 都能转 "执行中", 包括 "已完结" → "执行中" (状态回滚)。应加前置校验。
|
||||
|
||||
### 9.6 ✨ 新发现: address 字段 mapper 全套遗漏
|
||||
|
||||
DB 有 `address varchar(500) COMMENT '会议地址'`, 实体也有 `private String address` + getter/setter (`BizMeeting.java:117-119`), 但 `BizMeetingMapper.xml`:
|
||||
- `selectFields` (line 29-32) — 漏列
|
||||
- `selectByPrimaryKey` (line 33-36) — 通过 `selectFields` 漏列
|
||||
- `selectList` (line 37-52) — 通过 `selectFields` 漏列
|
||||
- `insert` (line 53-97) — `<if>` 块无 address
|
||||
- `updateByPrimaryKey` (line 98-121) — `<if>` 块无 address
|
||||
|
||||
**后果**: 即使前端表单填了 address, 写入 DB 时会被静默丢弃; 即使 DB 已有 address, 列表也读不出来。
|
||||
|
||||
---
|
||||
|
||||
@@ -302,87 +311,133 @@ DDL 注释列了 7 种 (未执行/执行中/监管通过/待整改/待结算/已
|
||||
| # | 问题 | 文件 | 修复建议 | 严重度 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **数据隔离缺失** (selectList 无 lead_user_id 过滤) | `BizMeetingMapper.xml:37` | 加 `<if test="params.leadUserId != null">and project_id in (select project_id from biz_project where lead_user_id = #{params.leadUserId})</if>`; Controller 注入当前 user_id | **P0** (生产必修) |
|
||||
| 2 | **`calcRemain` 读 `row.submitDeadlineDays`** (BizMeeting 无此字段) | `Meetings.vue:186` | 改成读 `row.project?.submitDeadlineDays` (后端 JOIN biz_project); 或去此字段 (manager 不需要) | **P1** |
|
||||
| 3 | **实体无 `remark` 字段** (前端 3 处 silent fail) | `BizMeeting.java` + `BizMeetingMapper.xml` | 路径 A: 加 `private String remark` + getter/setter + mapper UPDATE/INSERT 加 `<if>`; 路径 B: 前端 3 处全删 (业务可能不需要) | **P1** |
|
||||
| 4 | **filter 缺"签署状态"** (原型有, 实体有 `labor_signed`) | `Meetings.vue` filter + table column | 加 `<el-select v-model="q.laborSigned">` 2 option + table 加列 | **P2** |
|
||||
| 5 | **状态回滚无校验** (任何 stage → "执行中") | `Meetings.vue:271-273 onSubmit` + `:302-309 onBatchSubmit` | 加 `if (currentStage === '未执行')` 才能提交, 否则 ElMessageBox 提示 | **P2** |
|
||||
| 6 | **筛选后端需补索引** (`idx_project_no` + `idx_meeting_name`) | `biz_meeting` table | `ALTER TABLE biz_meeting ADD KEY idx_project_no (project_no), ADD KEY idx_meeting_name (meeting_name);` (⚠️ `LIKE '%xxx%'` 模糊匹配无法走索引, 实际效果有限) | **P3** |
|
||||
| 7 | **实体 periodNo/totalPeriods Long vs int** | `BizMeeting.java` | 改 int, MyBatis INSERT 不需 Long 转换 | **P3** |
|
||||
| 8 | **当前阶段 select 选项跟 DB 实际数据不一致** (见 §9 #1) | `Meetings.vue:25-31` | 改成跟 DB 一致 (已结算/已结题/待整改/待监管/监管通过 等), 或加 DB 不存在的值 (DB 加 ENUM 同步) | **P1** |
|
||||
| 9 | **filter 8 项挤一行** (屏幕窄乱) | `Meetings.vue:6-37` | 拆 2 行, 或 grid-template-columns: repeat(4, 1fr) | **P2** |
|
||||
| 10 | **批量提交无事务/无批量 endpoint** | `Meetings.vue:302-309` | 后端加 `PUT /business/meeting/batch?ids=...` + `@Transactional`; 前端 for 循环改单次调用 | **P2** |
|
||||
| 11 | **详情 dialog 缺字段** (create_time / org_name / supervision_* / labor_signed / invitation_url / schedule_url) | `Meetings.vue:91-103` | el-descriptions 加 5-8 行; URL 字段加 `<a :href>` 链接 | **P1** |
|
||||
| 12 | **`totalPeriods` 永远 1** (MeetingNew.vue 没让填) | `MeetingNew.vue` | 加"总期数"字段 (用户填, INSERT 时一起写) | **P1** |
|
||||
| 2 | **`calcRemain` 读 `row.submitDeadlineDays`** (BizMeeting 无此字段) | `Meetings.vue:162` | 改成读 `row.project?.submitDeadlineDays` (后端 JOIN biz_project); 或去此字段 (manager 不需要) | **P1** |
|
||||
| 3 | **实体无 `remark` 字段** (前端 3 处 silent fail) | `BizMeeting.java` + `BizMeetingMapper.xml` | 路径 A: 加 `private String remark` + getter/setter + mapper UPDATE/INSERT 加 `<if>` + DDL `ALTER TABLE biz_meeting ADD COLUMN remark text`; 路径 B: 前端 3 处全删 | **P1** |
|
||||
| 4 | **filter 缺"签署状态"** (实体有 `labor_signed`) | `Meetings.vue` filter + table column | 加 `<el-select v-model="q.laborSigned">` 2 option + table 加列 | **P2** |
|
||||
| 5 | **状态回滚无校验** (任何 stage → "执行中") | `Meetings.vue:205-212 onSubmit` + `:215-226 onBatchSubmit` | 加 `if (currentStage === '未执行')` 才能提交, 否则 ElMessageBox 提示 | **P2** |
|
||||
| 6 | ✨ **`address` 字段 mapper 全套遗漏** (DB 有, 实体有, mapper 全套没碰) | `BizMeetingMapper.xml` | selectFields/insert/update 全部加 address 字段 + `<if test="address != null and address != ''">` | **P1** |
|
||||
| 7 | **筛选后端需补索引** (`idx_project_no` + `idx_meeting_name`) | `biz_meeting` table | `ALTER TABLE biz_meeting ADD KEY idx_project_no (project_no), ADD KEY idx_meeting_name (meeting_name);` (⚠️ `LIKE '%xxx%'` 模糊匹配无法走索引) | **P3** |
|
||||
| 8 | **实体 periodNo/totalPeriods Long vs int** | `BizMeeting.java` | 改 int, MyBatis INSERT 不需 Long 转换 | **P3** |
|
||||
| 9 | **当前阶段 select 选项跟 DB 实际数据不一致** (见 §9 #1) | `Meetings.vue:25-31` | 改成 7 项 (跟 DB COMMENT 一致: 未执行/待监管/监管通过/待整改/待结算/已结算/已结题) | **P1** |
|
||||
| 10 | **filter 8 项挤一行** (屏幕窄乱) | `Meetings.vue:6-37` | 拆 2 行, 或 grid-template-columns: repeat(4, 1fr) | **P2** |
|
||||
| 11 | **批量提交无事务/无批量 endpoint** | `Meetings.vue:215-226` | 后端加 `PUT /business/meeting/batch?ids=...` + `@Transactional`; 前端 for 循环改单次调用 | **P2** |
|
||||
| 12 | **详情页字段映射完整** (14 字段 + 邀请函 + 日程 + 返回, 详见 §5 跳转形式列) | `MeetingDetail.vue` (新) | ✅ 已修 (A.7) — 跳独立页, 参照原型 `meeting-detail.html` 1:1 实现 | ✅ 已修 |
|
||||
| 13 | **`totalPeriods` 永远 1** (MeetingNew.vue 没让填) | `MeetingNew.vue` | 加"总期数"字段 (用户填, INSERT 时一起写) | **P1** |
|
||||
| 14 | ✨ **9 条红字提示整体缺失** (原型 `meeting-manage.html:533-543`) | `Meetings.vue` | 顶部加 `.hint-list` 块 (manager 端 UX 参考, 不强制与 executor 端一致) | **P2** |
|
||||
| 15 | ✨ **截止前 24h 短信催促** (原型第 9 条提示) | 后端 scheduler + sys_sms 表 | 加定时任务扫描 end_time + submit_deadline_days, 距截止 24h 起每 4h 触发 | **P3** |
|
||||
|
||||
---
|
||||
|
||||
## 11. 引用清单
|
||||
|
||||
### 前端
|
||||
- `ry-vue3/src/views/manager/Meetings.vue:343` — 本审查目标
|
||||
- `ry-vue3/src/views/manager/MeetingNew.vue` — 新建会议独立页 (共享 BizMeeting)
|
||||
- `ry-vue3/src/layout/AdminLayout.vue:99` — manager 菜单项
|
||||
- `ry-vue3/src/router/index.js:69-70` — 路由
|
||||
- `ry-vue3/src/views/manager/Meetings.vue:256` — 本审查目标 (今日改动: 7/10/60/100/32)
|
||||
- `ry-vue3/src/views/manager/MeetingNew.vue` — 新建/修改/复制会议独立页
|
||||
- `ry-vue3/src/layout/AdminLayout.vue:97` — manager 菜单项
|
||||
- `ry-vue3/src/router/index.js:65-66` — 路由
|
||||
- `ry-vue3/src/utils/request.js:15-30` — axios 拦截器 (`__silentError` 跳过 toast)
|
||||
- `ry-vue3/src/api/public.js:32, 92-93` — `bizList` / `bizUpdate` / `bizAdd`
|
||||
- `ry-vue3/src/api/public.js:32, 90-93` — `bizList` / `bizGet` / `bizUpdate` / `bizAdd`
|
||||
|
||||
### 后端 (Java)
|
||||
- `ry-api/ruoyi-business/.../controller/BizMeetingController.java:54` — 5 endpoints
|
||||
- `ry-api/ruoyi-business/.../domain/BizMeeting.java:107` — 24 字段, **缺 remark**
|
||||
- `ry-api/ruoyi-business/.../service/impl/BizMeetingServiceImpl.java:34` — insert 调 SnowflakeId 兜底 businessId
|
||||
- `ry-api/ruoyi-business/.../controller/BizMeetingController.java:26-75` — 5 endpoints
|
||||
- `ry-api/ruoyi-business/.../domain/BizMeeting.java:11-136` — 25 字段, **缺 remark**, **address 在 mapper 全套遗漏**
|
||||
- `ry-api/ruoyi-business/.../service/impl/BizMeetingServiceImpl.java:24-31` — insert 调 SnowflakeId 兜底 businessId
|
||||
- `ry-api/ruoyi-business/.../mapper/BizMeetingMapper.java` — mapper 接口
|
||||
|
||||
### Mapper XML
|
||||
- `ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml:128` — selectList 7 个 `<if>` 已生效
|
||||
- `ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml:131` — selectList 7 个 `<if>` 已生效; **address 全套漏** (见 §9 #6)
|
||||
|
||||
### 配置 / 工具
|
||||
- `ry-api/ruoyi-admin/src/main/resources/application-druid.yml:9-10` — DB 连接 (root / cu2oh2co3)
|
||||
- `ry-api/ruoyi-common/src/main/java/com/ruoyi/common/utils/id/SnowflakeId.java` — 雪花 ID 生成器
|
||||
- `ry-api/ruoyi-common/src/main/java/com/ruoyi/common/utils/id/IdGenerator.java` — 雪花 ID 生成器
|
||||
- `ry-api/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java:27` — getUserId()
|
||||
|
||||
### 原型
|
||||
- `proto/html/components/meetings.html:546` — "我参与的会议" 原型 (doctor 端, 实装 manager 重用)
|
||||
- `proto/html/components/meeting-detail.html` — 会议详情原型 (现用 el-dialog 替代)
|
||||
- `proto/html/manager.html:207` — 链接到 `components/meeting-manage.html`
|
||||
- `proto/html/components/meeting-manage.html:618` — manager 会议管理原型 (12 列 + 8 filter + 9 红字提示)
|
||||
- `proto/html/components/meeting-manage-1.html` — 原型变体 (期数 input 拆成 第/数字/期 三段), 暂未用
|
||||
|
||||
### 数据库 (实测 2026-08-18)
|
||||
- `biz_meeting` — 7 行, 4 索引 (PK + UNIQUE + 2 普通), 23 列 (见 §6.1)
|
||||
- `biz_project` — 12 行, 含 `create_user_id bigint` (commit ffda9c6)
|
||||
- `biz_person` — JOIN `user_id → name`
|
||||
### 数据库 (实测 2026-08-21)
|
||||
- `biz_meeting` — 9 行 (08-21) / 7 行 (08-18), AUTO_INCREMENT=12, 4 索引 (PK + UNIQUE + 2 普通), 24 列
|
||||
- `biz_project` — 12 行, 含 `create_user_id bigint`
|
||||
- `biz_meeting_attendee` — doctor/expert 角色过滤的中间表
|
||||
|
||||
---
|
||||
|
||||
## 附录: 修复记录 (按 commit 时间倒序)
|
||||
|
||||
> 已修的问题不进入 §10。本节按 commit 顺序列出, 每条标明 issue → fix → 验证。
|
||||
### A.7 commit (未 commit, 2026-08-21 session 改动) — 查看按钮跳页面
|
||||
|
||||
> **背景**: 用户硬要求 "跳页面变成打开 dialog 是完全不能接受的", 同时让 page-tech-review skill 能自动抓这类反模式
|
||||
|
||||
#### A.7.1 新建 `MeetingDetail.vue` (`ry-vue3/src/views/manager/MeetingDetail.vue`, 290 行)
|
||||
- 1:1 参照 `ManagerProjectDetail.vue` 的 page-card + breadcrumb + new-card-title 结构
|
||||
- 字段映射: 14 项基本信息 (项目编号/会议ID/会议名称/项目名称/项目形式/总期数/期数/起止时间/当前阶段/签署劳务/支持单位/创建人员/创建时间/备注) + 监察段 (条件渲染) + 邀请函/日程 (有 URL 渲染预览+下载, 无 URL 显示空态) + 返回
|
||||
- 字段对照 `biz_meeting` DB 实测 (见 §6.3); `supervision_*` 任意一项存在就整段显示, 避免空占位
|
||||
- 已知死字段: `备注` 永远 `-` (实体/表都没, 见 §9 #3); `address` mapper 全套漏 (见 §9 #6), 暂不显示此字段避免误导
|
||||
- URL 参数: `route.params.meetingId` (path param, 跟 `ManagerProjectDetail` 一致)
|
||||
|
||||
#### A.7.2 router 加路由 (`ry-vue3/src/router/index.js:66`)
|
||||
```js
|
||||
{ path: 'meetings/detail/:meetingId', name: 'manager-meetings-detail',
|
||||
component: () => import('@/views/manager/MeetingDetail.vue'),
|
||||
meta: { title: '会议详情' } },
|
||||
```
|
||||
参照 `manager-projects-detail` (line 63) 命名
|
||||
|
||||
#### A.7.3 `Meetings.vue` 删 dialog 改跳页面 (367 行 → 220 行)
|
||||
- `onView(row)` → `viewDetail(row)`, 内部 `router.push(/manager/meetings/detail/${row.meetingId})`
|
||||
- 删除: `<el-dialog>` 整块 (89-156 行), `detailOpen`/`currentRow` ref, `onView`/`calcRemain`/`laborSignedLabel` 函数, `.meeting-detail-dialog` 系列样式, 3 个 icon imports (Document/Calendar/Picture)
|
||||
- 保留: `fmtTime` (表格列用), `stageClass` (表格列用), `.stage-tag` 系列样式
|
||||
|
||||
#### A.7.4 page-tech-review skill 加规则 (`SKILL.md`)
|
||||
- §5 加 2 处: (1) 列说明加 "跳转形式 (page=✅ / dialog=❌)"; (2) 新 bullet 强调 "查看" 必须跳独立页面, 给反例 (本次修复前 Meetings.vue) 和参考模式 (Projects.vue:424)
|
||||
- §10 铁律加第 10 条 ⛔: "查看" 不能用 dialog; 审查时 §5 必须标 "跳转形式", dialog 即 P0
|
||||
|
||||
---
|
||||
|
||||
### A.6 commit (未 commit, 2026-08-21 session 改动) — label/期数/placeholder 修复
|
||||
|
||||
> **改动范围**: `Meetings.vue` (前端) + `BizProjectController.java` (后端, 顺带修)
|
||||
|
||||
#### A.6.1 后端 `BizProjectController.java:223`
|
||||
- **错**: `int s = safeLong(...) + safeLong(...) + ...` — long 累加赋给 int, Java 报"可能会有损失"
|
||||
- **改**: `int s` → `long s`
|
||||
- **影响**: rate() 接口里 compliance 角色聚合评分那行, 不影响 meeting 页面本身
|
||||
|
||||
#### A.6.2 前端 `Meetings.vue:10` 筛选项 "第?期"
|
||||
- **问题**: `:min="0"` 让 el-input-number 在空值时强制 0 → 后端 `period_no = 0` 过滤掉全部数据; label 文案 "第?期" 不直观
|
||||
- **改**:
|
||||
- label: `第?期` → `期数(第几期)`
|
||||
- 控件: `<el-input-number :min="0" :precision="0" controls-position="right">` → `<el-input placeholder="请输入期数" clearable>`
|
||||
- 原因: Element Plus 2.4 无 `value-on-clear` prop, `:min="1"` 仍会让 blur 时 snap 到 1; 改用普通 `<el-input>` 让空值保持空串, 由 mapper `<if test="periodNo != null">` 跳过
|
||||
- **验证**: 缺值 → 不影响其他筛选; `periodNo=1` → 1 行
|
||||
|
||||
#### A.6.3 前端 `Meetings.vue:60, 100` 期数显示
|
||||
- **问题**: `第 {{ row.periodNo || 0 }} 期` 在 periodNo 为 null/空时强行显示 "第 0 期"
|
||||
- **改**: `{{ row.periodNo ? '第 ' + row.periodNo + ' 期' : '-' }}`
|
||||
- **影响**: 表格列 + 详情 dialog 同步去掉 `|| 0` 兜底
|
||||
|
||||
#### A.6.4 前端 `Meetings.vue:7-32` placeholder 补齐
|
||||
- **问题**: 5 个文本输入 + 2 个日期选择器缺 placeholder
|
||||
- **改**:
|
||||
- 项目编号 → `请输入项目编号`
|
||||
- 会议ID → `请输入会议ID`
|
||||
- 会议名称 → `请输入会议名称`
|
||||
- 期数(第几期) → `请输入期数`
|
||||
- 会议时间 → `开始时间` / `结束时间`
|
||||
- 备注 → `请输入备注`
|
||||
- 项目形式 / 当前阶段 → 已经有 `请选择` ✓
|
||||
|
||||
### A.5 commit `bae9fa6` (2026-08-18) — 恢复会议ID + 第?期 filter (第五轮续)
|
||||
|
||||
- **背景**: 第四轮前后曾误删 `会议ID` / `第?期` filter, 用户澄清两个字段原型中都有.
|
||||
- **修改**: `Meetings.vue:8` 加回 `<el-form-item label="会议ID">` (el-input); `Meetings.vue:10` 第?期从 el-input 改 el-input-number (`:min="0" :precision="0"`); `q` reactive + `reset()` 加回 `meetingId:''`; mapper `<if>` 块本就有, 无需动.
|
||||
- **验证**: `q.meetingId=14` → 1 行; `q.periodNo=1` → 1 行; 留空 → 不影响其他筛选.
|
||||
- **背景**: 第四轮前后曾误删 `会议ID` / `第?期` filter, 用户澄清两个字段原型中都有
|
||||
- **修改**: `Meetings.vue:8` 加回 `<el-form-item label="会议ID">`; `Meetings.vue:10` 第?期 el-input-number; q reactive + reset() 加回 `meetingId`
|
||||
- **验证**: `q.meetingId=14` → 1 行; `q.periodNo=1` → 1 行
|
||||
|
||||
### A.4 commit `14757f3` (2026-08-18) — mapper selectList 加 7 个筛选 if + @DateTimeFormat (第四轮)
|
||||
### A.4 commit `14757f3` (2026-08-18) — mapper selectList 加 7 个筛选 if + @DateTimeFormat
|
||||
|
||||
- **解决**: P0#1 全部筛选不生效 (`<where>` 空).
|
||||
- **修改**: `BizMeetingMapper.xml:37-50` 加 7 个 `<if>` (project_no LIKE, meeting_id =, meeting_name LIKE, period_no =, project_form =, current_stage =, start_time >=, end_time <=); `BizMeeting.java:30-33` startTime/endTime 加 `@DateTimeFormat` 让 Spring 能绑字符串.
|
||||
- **验证**: `LIKE %ZH-2026-6%` → 5 行; `current_stage=已结算` → 1 行; `start_time>=2026-03-01` → 4 行; `period_no=1` → 1 行; 组合 AND 正常.
|
||||
- **解决**: 全部筛选不生效 (`<where>` 空)
|
||||
- **修改**: `BizMeetingMapper.xml:37-52` 加 8 个 `<if>`; `BizMeeting.java:30-33` startTime/endTime 加 `@DateTimeFormat`
|
||||
|
||||
### A.3 commits `6d4f1e6` + `6774c44` (2026-08-18) — style-alignment + 批量按钮去 plain (第三轮)
|
||||
|
||||
- **解决**: 批量按钮 disabled 顺色; "已选 X 条" 紧贴按钮左边; inline style 残留; batch-bar 属性顺序.
|
||||
- **修改**: `Meetings.vue:41` 批量按钮去 `plain`; `Meetings.vue:324 .filter-tip { margin-left: auto }`; `<span class="date-sep">至</span>` 替代 inline style; `.batch-bar` 属性顺序对齐 People.vue.
|
||||
|
||||
### A.2 commit `08d81e9` (2026-08-18) — onCopy 改 bizAdd + 三处双 toast (第二轮)
|
||||
|
||||
- **解决**: P0#3 onCopy bug (meetingId='' 走 bizUpdate → 0 行 + 误报成功); P0#4 双 toast.
|
||||
- **修改**: `Meetings.vue:222-228 editMode` ref + `Meetings.vue:246-267 submitEdit()` 按 editMode 分支 (copy → bizAdd, edit → bizUpdate); 三处 bizUpdate 加 `{ __silentError: true }`.
|
||||
|
||||
### A.1 commit `66a928a` (跨 session, manager_meeting_new.md 第一轮) — 数据库正确性
|
||||
|
||||
- 共享 `BizMeeting` 修复: meetingId String→Long, supervisionTime String→Date, INSERT/UPDATE/selectFields 补漏, businessId 雪花 ID 兜底 (13 项).
|
||||
- 详细见 `manager_meeting_new.md` §修复记录 第一轮.
|
||||
|
||||
### 跨页相关 commit (影响本页但不直接修改)
|
||||
- `ffda9c6` — biz_project 加 `create_user_id` (影响 §6.2)
|
||||
- `a081da8` — MeetingNew.vue "期数" 文案改 "请填写第几期" (独立页)
|
||||
- `4fbfcbf` + `8f62f65` + `518da05` + `29a4b02` + `379cf22` + `ac31387` — MeetingNew.vue 原型 1:1 重写 + 双 toast 修复
|
||||
### A.3-A.1 (略, 见 v1 报告)
|
||||
Reference in New Issue
Block a user