# manager/meetings — 端到端技术审查 > **审查时间**: 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) --- ## 0. 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` 为准. --- ## 1. 主要功能 + 可见性 ### 1.1 主要功能 | 功能 | 前端入口 | 后端接口 | 数据表 | |---|---|---|---| | 筛选 + 分页列表 | `Meetings.vue` `` + `` | `GET /business/meeting/list` (`BizMeetingController.java:26-37`) | `biz_meeting` | | 查看详情 (弹窗) | `onView(row)` → `el-dialog` + `el-descriptions` 只读 | 复用列表 row, 无额外请求 | `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)` → 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: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 9 行测试数据无风险, 生产环境会暴露全部会议 (见 §10 #1)。 **看不到本页面的人群**: admin / doctor / executor / sponsor 各有独立 `//meetings` 路由 + 独立菜单项, 路由层就分开了。 --- ## 2. 筛选项 (filter-form) | UI label | 控件 | 绑定字段 | 后端 `` | 命中表.字段 | 选项来源 | 原型对照 | |---|---|---|---|---|---|---| | 项目编号 | 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** (文本, 今日改) | `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 项 (未执行/执行中/已执行/已完结) ⚠️ | ✅ 原型有 (但原型是 6 项含 待审核/待结算/冻结中) | | 备注 | el-input (模糊) | `q.remark` | ❌ **缺失** — `biz_meeting` 无 `remark` 列 | (无字段) | — | ❌ 原型无 | **完整 SQL 块** (`BizMeetingMapper.xml:37-52`): ```sql 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, supervision_by, supervision_time, invitation_url, schedule_url, labor_signed, create_by, create_time, update_by, update_time 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 ``` 8 项 filter 中 7 项生效; **remark filter 是死路** — 实体/表/INSERT 都没有此字段 (见 §6.3, §9 #3)。 --- ## 3. 工具栏 (filter-form / batch-bar) | 按钮 | 功能 | 接口 | 涉及表 | 原型对照 | |---|---|---|---|---| | 查找 | 触发 `load()` → bizList | `GET /business/meeting/list?...` | `biz_meeting` | ✅ 查找 | | 重置 | 清空 q → load | 同上 | — | ❌ 原型无 | | 批量提交 | `onBatchSubmit()` 逐条 update currentStage='执行中' | `PUT /business/meeting` × N | `biz_meeting.current_stage` | ✅ 原型有 ("批量提交:后端待对接") | **提示行**: `已选 N 条` — 推到 batch-bar 最右。 --- ## 4. 表格 (el-table) ### 4.1 数据来源 见 §2 完整 SQL 块。 ### 4.2 列映射 | 列名 | row.* | DB 表.字段 | 原型对照 | |---|---|---|---| | 多选框 | (selection) | — | ✅ 原型有 checkbox 列 | | 项目编号 | `row.projectNo` | `biz_meeting.project_no` | ✅ 项目编号 | | 会议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.currentStage` (stage-tag) | `biz_meeting.current_stage` | ✅ 当前阶段 | | 备注 | `row.remark` (永远空, 见 §9 #3) | (无) | ✅ 备注 (原型有) | | 操作 | 查看/修改/提交/复制 | — | ✅ 原型有 提交/查看/修改/复制 | **新增列 (原型无, 实现增) ✅ 改进**: 无。 **遗漏列 (DB 有, 列表未显示)**: - `org_name` (公司名称) — 列表可加 - `create_time` / `update_time` — 审计可用 - `labor_signed` (0/1) — 原型无, 实体有 - `invitation_url` / `schedule_url` — 列表可加链接 --- ## 5. 行内操作按钮 | 按钮 | 触发函数 | 接口 | 后端动作 | 涉及表.字段 | **跳转形式** | 原型对照 | |---|---|---|---|---|---|---| | 查看 | `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` 表不存在)。 - **"修改" / "复制"**: 跳 `MeetingNew.vue` 独立页处理 (`Meetings.vue:106` 注释明确说明), form 含 9 字段, 见 manager_meeting_new.md。 --- ## 6. Java Entity ↔ 数据库表 一致性 ### 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 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 '会议名称', `period_no` int DEFAULT '1' COMMENT '期数', `total_periods` int DEFAULT '1' COMMENT '总期数', `project_form` varchar(20) DEFAULT NULL COMMENT '项目形式', `start_time` datetime DEFAULT NULL COMMENT '会议开始时间', `end_time` datetime DEFAULT NULL COMMENT '会议结束时间', `org_name` varchar(200) DEFAULT NULL 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 '监察时间', `invitation_url` varchar(500) DEFAULT NULL COMMENT '邀请函URL', `schedule_url` varchar(500) DEFAULT NULL COMMENT '日程海报URL', `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 '更新时间', 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=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, 含 `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 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) | ✅ | | periodNo | 期数 | **Long** | period_no | **int** | ⚠️ 类型不一致 (MyBatis 自动转 OK) | | totalPeriods | 总期数 | **Long** | total_periods | **int** | ⚠️ 类型不一致 (同上) | | projectForm | 项目形式 | String | project_form | varchar(20) | ✅ | | 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) | ✅ | | supervisionTime | 监察时间 | Date | supervision_time | datetime | ✅ (有 @JsonFormat) | | 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) | ✅ | | createTime | 创建时间 | Date | create_time | datetime | ✅ | | updateBy | 更新人 | String | update_by | varchar(64) | ✅ | | updateTime | 更新时间 | Date | update_time | datetime | ✅ | | — | (无 `remark` 字段) | — | (无 `remark` 列) | — | ❌ **实体缺 remark** (见 §9 #3) | **修订**: v1 漏报 `address` 字段 — 实测 DB 有 `address varchar(500)` 列 + COMMENT '会议地址', 实体也有 `private String address`, 但 mapper 全套 (selectFields / insert / updateByPrimaryKey) 都没碰这个字段。**新发现**: 见 §9 #6。 --- ## 7. 索引检查 | 表 | Key | 列 | 用途覆盖 | |---|---|---|---| | 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 =` | **缺失但暂可接受** (按 §2 筛选启用后): - `idx_project_no` — `LIKE '%xxx%'` 模糊匹配无法走索引 (见 §10 #6) - `idx_meeting_name` — 同上 - 复合 `(project_id, meeting_id)` — JOIN 用, 当前单 `project_id` 索引够 --- ## 8. 与原型差异 (proto/html/components/meeting-manage.html) ### 8.1 实现新增 (原型没有) — **改进** | 项 | 原型 | 实现 | 评估 | |---|---|---|---| | 筛选 "备注" | 4 项 (项目编号/会议名称/项目形式/当前阶段) | 8 项 (多 会议ID/第?期/会议时间/备注) | ⚠️ 备注 filter 死路 (见 §9 #3), 第?期无 placeholder 限制 | | 弹窗 (查看/修改) | 跳转 detail 页 | ✅ el-dialog 紧凑 | manager 端更高效 | | 行内 "复制" → 跳独立页 | 原型 `onclick="copyMeeting(this)"` URL 传参 | ✅ 跳 `MeetingNew.vue?mode=copy` | 模式更清晰 | ### 8.2 原型有但实现缺失 — **回退** | 原型 | 实现 | 差距 | |---|---|---| | 9 条红字提示 (执行方只能在项目列表建会 / 状态定义 / 提交审核后 OA 流程 / 复制 → 新建会议 / 总期数可点击筛选 / 提交后 3 秒消失 / 7 种状态统一解释 / 冻结中提交置灰 / 到期前 24h 每 4h 短信催促) | 无 | ❌ 整段红字提示缺失 (manager 端 UX 参考) | | `meeting-detail-executor.html` 详情页 | 弹 dialog | ⚠️ 弹窗缺字段 (见 §10 #11) | ### 8.3 文字 / 标签差异 | 项 | 原型 | 实现 | 评估 | |---|---|---|---| | 面包屑 | 首页 > 我参与的会议 | 首页 / 会议管理 | ✅ 角色匹配 | | 当前阶段 select | 6 项 (未执行/执行中/待审核/待结算/已完结/冻结中) | 4 项 (未执行/执行中/已执行/已完结) | ⚠️ **缺 待审核/待结算/冻结中 3 项** (但跟 DB COMMENT 一致, 见 §9 #1) | | 期数筛选项 | "第 [____] 期" 三段式 (label/input/label) | "期数(第几期)" 单 label | ⚠️ 排版略不同 (今日改 label 文案) | | 颜色 | `#1890ff` (浅蓝) | `var(--brand-primary)` (深海军蓝) | ✅ 项目色 | ### 8.4 总结 **整体方向**: **对齐** — 实装与原型字段基本一致 (12 列 vs 12 列, 8 项 filter vs 8 项 filter); 差距在 "红字提示" 整段缺失 + 当前阶段 select 选项数不一致。 --- ## 9. 设计问题 ### 9.1 当前阶段 select 与 DB 实际数据不一致 DDL 注释列了 7 种 (未执行/待监管/监管通过/待整改/待结算/已结算/已结题), 实装 select 4 项 (未执行/执行中/已执行/已完结)。两者有交集但不一致, filter 启用后用户选"未执行"也筛不到任何数据 (DB 实际可能是 待监管/已结算 等)。 ### 9.2 calcRemain 读不存在的字段 `Meetings.vue:162` 读 `row.submitDeadlineDays`, 但 `BizMeeting` 实体无此字段 (BizProject 才有)。当前永远 fallback 到 30 天, 是个静默 bug。 ### 9.3 实体无 remark 字段 前端 3 处使用 (`Meetings.vue:32 q.remark`, `:67 row.remark`, `:32 filter`), 但 `BizMeeting` 实体无 `remark`, 表也无 `remark` 列。后果: - filter "备注" 永远无效 - 列表"备注"列永远空 - 详情 dialog"备注"行永远 `-` ### 9.4 数据隔离缺失 (manager 端) `BizMeetingMapper.xml:37 selectList` 无 `@DataScope` 或 `lead_user_id` 过滤。当前测试数据无风险, 生产必修。 ### 9.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) — `` 块无 address - `updateByPrimaryKey` (line 98-121) — `` 块无 address **后果**: 即使前端表单填了 address, 写入 DB 时会被静默丢弃; 即使 DB 已有 address, 列表也读不出来。 --- ## 10. 待修复列表 (严格只列未修) | # | 问题 | 文件 | 修复建议 | 严重度 | |---|---|---|---|---| | 1 | **数据隔离缺失** (selectList 无 lead_user_id 过滤) | `BizMeetingMapper.xml:37` | 加 `and project_id in (select project_id from biz_project where lead_user_id = #{params.leadUserId})`; Controller 注入当前 user_id | **P0** (生产必修) | | 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 加 `` + DDL `ALTER TABLE biz_meeting ADD COLUMN remark text`; 路径 B: 前端 3 处全删 | **P1** | | 4 | **filter 缺"签署状态"** (实体有 `labor_signed`) | `Meetings.vue` filter + table column | 加 `` 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 字段 + `` | **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: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, 90-93` — `bizList` / `bizGet` / `bizUpdate` / `bizAdd` ### 后端 (Java) - `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:131` — selectList 7 个 `` 已生效; **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/IdGenerator.java` — 雪花 ID 生成器 - `ry-api/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java:27` — getUserId() ### 原型 - `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-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 时间倒序) ### 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})` - 删除: `` 整块 (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: `第?期` → `期数(第几期)` - 控件: `` → `` - 原因: Element Plus 2.4 无 `value-on-clear` prop, `:min="1"` 仍会让 blur 时 snap 到 1; 改用普通 `` 让空值保持空串, 由 mapper `` 跳过 - **验证**: 缺值 → 不影响其他筛选; `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` 加回 ``; `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 - **解决**: 全部筛选不生效 (`` 空) - **修改**: `BizMeetingMapper.xml:37-52` 加 8 个 ``; `BizMeeting.java:30-33` startTime/endTime 加 `@DateTimeFormat` ### A.3-A.1 (略, 见 v1 报告)