P0 数据正确性: - BizMeetingMapper.xml INSERT 扩 20 列 (原只插 meeting_id) - BizMeetingServiceImpl businessId 兜底生成 (DDL NOT NULL, 前端无字段) - MeetingNew.vue onMounted 回填 projectId/projectName (原只 projectNo) P1 核心功能: - 删 会议ID 输入框 (PK 让用户填错) - 加 6 个项目只读快照 (el-descriptions, 跟原型对齐) - 会议名称默认 = 项目名称, 不可修改 (加红字提示 + disabled) - meetingId String → Long (6 文件: BizMeeting/Mapper/ServiceImpl/Controller/IBizMeetingService/Mapper.xml) - supervisionTime String → Date + @JsonFormat P2 体验: - 面包屑改 项目列表 / 新建会议 - page-sub 显示项目名而非 ID 13 - 当前阶段 el-select 4 → 10 选项 - resultMap/selectFields 补 9 个漏字段 (projectId/projectName/orgName/supervision*/invitation*/schedule*/labor*) Co-Authored-By: Claude <noreply@anthropic.com>
20 KiB
manager/meetings/new — 端到端技术审查
审查时间: 2026-08-18 触发场景: 用户访问
/manager/meetings/new?projectId=13审查范围:ry-vue3/src/views/manager/MeetingNew.vue(新建会议独立页) + 后端 BizMeeting 全链路 + DB 实测 对比原型:proto/html/components/new-meeting.html
修复记录 (2026-08-18 第二次审查时落实)
| # | 问题 | 修复 | 状态 |
|---|---|---|---|
| P0#1 | mapper INSERT 只插 meeting_id | BizMeetingMapper.xml:43-87 扩 20 列 <if> |
✅ |
| P0#2 | businessId DDL NOT NULL 前端无字段 | BizMeetingServiceImpl:24-26 兜底 IdGenerator.generateId() |
✅ |
| P0#3 | form 没 projectId (只 projectNo) | MeetingNew.vue:88-105 onMounted 回填 projectId/projectName |
✅ |
| P1#4 | "会议ID" 输入框让用户填 PK | 删 field + 删 rule | ✅ |
| P1#5 | 6 个项目只读快照全缺失 | 加 el-descriptions (项目编号/名称/形式/开始时间/创建人/创建时间) | ✅ |
| P1#6 | 会议名称默认 = 项目名称 | onMounted 回填 + 字段 disabled + 红字提示 | ✅ |
| P1#7 | meetingId String → Long | 6 文件: BizMeeting.java + Mapper.java + ServiceImpl.java + Controller.java + IBizMeetingService.java + Mapper.xml | ✅ |
| P1#8 | supervisionTime String → Date | BizMeeting.java 加 @JsonFormat + Date |
✅ |
| P2#9 | 面包屑不一致 | 改 "项目列表 / 新建会议" | ✅ |
| P2#10 | page-sub 显示原始 ID "13" | 改成 "{projectName} ({projectNo})" | ✅ |
| P2#11 | 当前阶段 el-select 4 → 10 选项 | 补 待监管/监管通过/待整改/待结算/已结算/已结题 | ✅ |
| P2#12 | resultMap 漏字段 | BizMeetingMapper.xml:4-28 补 9 个 result | ✅ |
| P2#13 | selectFields SQL 漏字段 | BizMeetingMapper.xml:30 补 9 列 | ✅ |
0. 4 跳定位 (铁律: 不按名字猜)
| 跳 | 文件:行 | 结果 |
|---|---|---|
| ① 角色菜单 | ry-vue3/src/layout/AdminLayout.vue:99 |
path: '/manager/meetings', title: '会议管理' ✓ (新建是子菜单/独立路由) |
| ② 路由 | ry-vue3/src/router/index.js:70 |
{ path: 'meetings/new', name: 'manager-meetings-new', component: () => import('@/views/manager/MeetingNew.vue') } ✓ |
| ③ 组件 | ry-vue3/src/views/manager/MeetingNew.vue |
115 行, 本审查对象 |
| ④ 原型 | proto/html/manager.html 间接入口 → components/new-meeting.html |
注: 原型标 "角色: 执行方, 父级: executor.html" — 角色归属不一致! |
⚠️ 原型角色归属错位:
new-meeting.html头部注释角色: 执行方 / 父级: executor.html,但本组件在 manager 路由下。原型本身可能是早期版本,实际 Vue 实现给 manager 用 (合规管理员新建会议)。
1. 主要功能 + 可见性
1.1 主要功能
| 功能 | 前端入口 | 后端接口 | 涉及表 |
|---|---|---|---|
| 新建会议表单 | manager/MeetingNew.vue 表单 |
POST /business/meeting → BizMeetingController.add → bizMeetingService.insert |
biz_meeting |
| 预选项目回填 | onMounted → bizGet('project', preselectProjectId) |
GET /business/project/{projectId} |
biz_project |
| 取消 | onCancel |
(无) | — |
⚠️ 页面无编辑功能: 即便 URL 是
/new,MeetingNew.vue 没route.params.id分支,只能新建不能编辑。如果想编辑会议,得另开 MeetingEdit.vue 或复用本组件加 id 分支。
1.2 可见性 (三层隔离)
| 层 | 来源 | 校验字段 |
|---|---|---|
| 前端菜单 | AdminLayout.vue:99 |
manager 角色菜单 |
| 路由守卫 | router/index.js:70 |
name: 'manager-meetings-new', 路由 meta.role='manager' (来自父路由) |
| 后端 | BizMeetingController.list → selectList 无 user_id 过滤(manager 看所有会议) |
无 |
⚠️ manager 是全局视图角色,跟 manager/Projects 一致,不过滤 — 设计正确
2. 筛选项 (filter-form)
本页面无筛选区 — 是纯表单页,直接进入。
3. 工具栏按钮 (toolbar)
| 按钮 | 功能 | 接口 | 涉及表 | 原型对照 |
|---|---|---|---|---|
| 保存 | onSave |
POST /business/meeting {...form} |
biz_meeting | ✅ 原型 #6 |
| 取消 | onCancel |
router.back() |
— | ✅ 原型 #7 |
4. 表格 (el-table)
本页面无表格 — 是纯表单页。
5. 行内操作按钮
本页面无行内操作 — 是纯表单页。
6. Java Entity ↔ 数据库表 一致性
6.1 biz_meeting 主表 DDL (实测)
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 '公司名称(冗余)',
`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=8
6.2 关联表 DDL (引用)
biz_project(11 行, 7 索引 — 见manager_projects.md§7.1)biz_project_assign(11 行, 5 索引 — 见manager_projects.md§7.2)
6.3 Entity ↔ 表字段对照表
| 实体字段 | 中文列名 (DB COMMENT) | 类型 | 表字段 | 类型 | 一致? |
|---|---|---|---|---|---|
| meetingId | 会议ID | String | meeting_id | bigint | ⚠️ 类型不一致 (Java String vs SQL bigint), 应改 Long |
| businessId | 业务会议ID 5643145673 | String | business_id | varchar(50) NOT NULL | ✅ (但 NOT NULL, 前端必填, 当前 form 没校验!) |
| 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 | ✅ |
| totalPeriods | 总期数 | Long | total_periods | int | ✅ |
| projectForm | 项目形式 | String | project_form | varchar(20) | ✅ |
| startTime | 会议开始时间 | Date | start_time | datetime | ✅ |
| endTime | 会议结束时间 | Date | end_time | datetime | ✅ |
| orgName | 公司名称(冗余) | String | org_name | varchar(200) | ✅ |
| currentStage | 当前阶段 | String | current_stage | varchar(20) | ✅ |
| supervisionOpinion | 监察意见 | String | supervision_opinion | varchar(500) | ✅ |
| supervisionBy | 监察人 | String | supervision_by | varchar(64) | ✅ |
| supervisionTime | 监察时间 | String | supervision_time | datetime | ⚠️ Java String vs SQL datetime — Java 实体应当用 Date |
| invitationUrl | 邀请函URL | String | invitation_url | varchar(500) | ✅ |
| scheduleUrl | 日程海报URL | String | schedule_url | varchar(500) | ✅ |
| laborSigned | 签署劳务 0未签 1已签 | String | labor_signed | char(1) | ✅ |
| createBy/createTime/updateBy/updateTime | 创建人/时间 等 | (BaseEntity) | ✅ |
修订标记
⚠️ meetingId 同 §6.3 之前项目 (类似 BizProject.projectId): Java 用 String, SQL 用 bigint, 跟同表其他 bigint 列 (projectId) 风格不一致。biz_meeting 当前 7 行实测全有数据 (
meeting_id1-7,business_id5643145673-77), 说明数据通过别的方式 (seed 脚本) 写入, 不是走当前 mapper.insert — 见 §10 #1。⚠️ supervisionTime: 同 §6.3 之前 BizProject.startTime/endTime 模式, Java String vs SQL datetime, 未修复。
⚠️ businessId NOT NULL: DDL 是 NOT NULL, 但前端 form 没有
businessId字段, 提交后 INSERT 会失败 (DDL 约束) — 见 §10 #2。
7. 索引检查
7.1 biz_meeting 索引
| Key | 列 | 用途覆盖 |
|---|---|---|
| PRIMARY | meeting_id | ✅ 主键, ORDER BY |
| uk_business_id | business_id (UNIQUE) | ✅ 业务会议ID 精确匹配 (业务上应唯一,合理) |
| idx_meeting_project | project_id | ✅ WHERE project_id = ? JOIN 用 |
| idx_meeting_stage | current_stage | ✅ 按阶段过滤 |
| ❌ 缺 | project_no | ⚠️ MeetingNew.vue:103 走 form.projectNo 反查 (但实际是 form 字段, 后端无 project_no 过滤查询), 可接受 |
| ❌ 缺 | start_time / end_time | ⚠️ 时间范围查询 (如 /manager/meetings 列表筛选), 当前 manager/Meetings.vue 是按 ID 排序, 可接受 |
结论: 索引设计够用, 4 个索引覆盖主键 + 2 个常见 WHERE 列 + 1 个 UNIQUE 业务约束。
8. 与原型差异 (实现偏离)
8.1 实现新增 (原型没有)
| 项 | 位置 | 说明 |
|---|---|---|
| 项目形式下拉 | MeetingNew.vue:16-23 |
原型无, 实装 el-select |
| 当前阶段下拉 (4 选项) | MeetingNew.vue:36-43 |
原型无, 实装 (DB current_stage 注释是 7 个状态, 但 el-select 只给 4 个, 缺 "待监管/监管通过/待整改/待结算/已结算" 5 个) |
| 总期数 input-number | MeetingNew.vue:30-31 |
原型只在右列有"期数", 实装拆成"总期数 + 期数"两个 |
8.2 原型有但实现缺失
| 原型项 | 实现状态 |
|---|---|
| 6 个只读值 (左列) | 🐛 完全缺失 — 原型左列显示项目编号/项目名称/总场次/支持单位/创建时间/创建人员 6 个只读快照, 实装全部不显示, 只用 page-sub 提示"已选择项目: 13" |
| 面包屑 "项目列表 > 新建会议" | 🐛 实装用"首页 / 会议管理 / 新建会议", 跟原型"项目列表 > 新建会议"不一致 |
| 红字提示 "*会议名称默认为项目名称,不可修改" | 🐛 完全缺失 |
| 会议名称只读 | 🐛 实装让用户手填会议名称, 跟原型"默认为项目名称,不可修改"相反 |
| 期数文本框 | ✅ (实装 input-number, 比原型 input text 更好) |
| 会议开始/结束时间 | ✅ |
| 备注 | ✅ |
8.3 文字 / 标签差异
| 原型 | 实现 | 差异 |
|---|---|---|
| "新建会议" h1 | "首页 / 会议管理 / 新建会议" breadcrumb | 实现无 h1 标题, 只用 breadcrumb (轻微) |
| "项目编号" | "项目编号" | ✅ |
| "会议名称" | "会议名称" | ✅ |
| "会议ID" | — | 原型无, 实现新增 (P3 — 该删) |
| "项目形式" | "项目形式" | ✅ |
| "会议开始时间" | "会议开始时间" | ✅ |
| "会议结束时间" | "会议结束时间" | ✅ |
| "总期数" | "总期数" | ✅ |
| "期数" | "期数" | ✅ |
| "当前阶段" | "当前阶段" | ✅ |
| "备注" | "备注" | ✅ |
8.4 总结
- 整体方向: 实现严重偏离原型, 丢了 6 个项目只读快照 + 会议名称自动填充 + 红字提示, 改成全手填表单。
- 业务偏离: 原型"会议名称默认为项目名称,不可修改" — 设计意图是减少用户输入, 实装让用户自己填, 既不符合原型, 也增加出错概率 (用户可能填错/漏填)。
- 设计偏离: 实装多了"会议ID"输入框让用户填 PK, 完全错误 (meeting_id 是 AUTO_INCREMENT)。
9. 表字段冗余 / 设计问题
9.1 实体 vs 表不一致 (详见 §6.3)
- ⚠️ meetingId String → Long (跟 §10 #1 关联)
- ⚠️ supervisionTime String → Date (跟 BizProject.startTime/endTime 同模式, 未修复)
- ✅ 其他字段一致
9.2 Mapper INSERT 严重残缺 🐛
BizMeetingMapper.xml:34-42:
<insert id="insert" parameterType="BizMeeting">
insert into biz_meeting
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="meetingId != null and meetingId != ''">meeting_id,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="meetingId != null and meetingId != ''">#{meetingId},</if>
</trim>
</insert>
🛑 只插入 meeting_id 一列, 其他所有字段 (business_id, project_no, meeting_name, start_time, end_time, current_stage 等) 完全不写。
实测验证: 当前 7 行数据齐全 (business_id 5643145673-77), 但 meeting_id 是 1-7 顺序自增, create_time 全是 2026-08-16 19:18:32, create_by='admin'。明显是 seed 脚本直插, 不是从当前 mapper.insert 走的。
如果用户从本组件提交:
- INSERT SQL 实际是
INSERT INTO biz_meeting (meeting_id) VALUES (1234567890123456789)— SnowflakeId 生成的 Long - DB 默认值: business_id NOT NULL 违反约束 → INSERT 报错
- 其他字段全部 NULL, 数据无效
9.3 business_id NOT NULL 约束缺失前端校验
DDL: business_id varchar(50) NOT NULL, 但前端 form 没有 businessId 输入框 (line 7-15 也没列)。rules 里也没 businessId 必填校验。即便 mapper 修好 INSERT, 提交也会因缺 business_id 报错。
9.4 project_id 缺失
form 只提交 projectNo, 不提交 projectId。即使 mapper INSERT 修好, 写库的 meeting 也只有 project_no, 没有 project_id — 跟 biz_project 的 JOIN 查询会漏掉这条记录。
9.5 JOIN 字段 vs 持久化字段混淆
projectName / orgName 是冗余字段 (DB COMMENT 写"冗余"), 但前端 form 没填这两个字段, mapper INSERT 也不会写。后续 JOIN 派生需要靠 service 层回查。
10. 待修复列表 (按优先级)
| # | 问题 | 文件:行 | 修复建议 | 严重度 |
|---|---|---|---|---|
| 1 | mapper INSERT 只插 meeting_id, 其他字段全 NULL 🐛 | BizMeetingMapper.xml:34-42 |
跟 updateByPrimaryKey 一样, 给所有非空字段加 <if test="x != null and x != ''">x,</if> |
P0 |
| 2 | business_id NOT NULL, 前端 form 无字段无校验 🐛 | MeetingNew.vue (整个表单) + rules |
加 businessId 输入框 + 必填校验, 或 mapper 给 businessId 兜底生成 | P0 |
| 3 | form 没 projectId, 写库 meeting 只关联 projectNo 不关联 projectId | MeetingNew.vue:67-77 + :99-105 onMounted |
onMounted 查项目时同时回填 form.projectId = r.data.projectId (后端已 Long) |
P0 |
| 4 | meetingId 输入框让用户填 PK 🐛 | MeetingNew.vue:13-15 |
删 (meeting_id 是 AUTO_INCREMENT), 删 rules.meetingId | P1 |
| 5 | 6 个项目只读快照全缺失 (原型 §#4) | MeetingNew.vue:6-51 (表单整体) |
在 form 上方加 el-descriptions 区域, 显示 projectNo/projectName/totalSessions/sponsorOrgName/createTime/createBy | P1 |
| 6 | 会议名称应默认 = 项目名称, 不可修改 (原型 §#3 红字提示 + §#5 只读) | MeetingNew.vue:10-12 + :80 rules |
onMounted 查项目后, form.meetingName = r.data.projectName, 字段加 :disabled="true", rules.meetingName 改 trigger 或保留必填 |
P1 |
| 7 | 面包屑跟原型不一致 | MeetingNew.vue:3 |
改 "项目列表 > 新建会议" 或 "首页 > 会议管理 > 新建会议" 加一个返回项目列表链接 | P2 |
| 8 | page-sub 显示原始 ID "13", 不是项目名 | MeetingNew.vue:4 |
onMounted 查项目后, page-sub 改成 "已选择项目: {projectName} ({projectNo})" | P2 |
| 9 | 当前阶段 el-select 只 4 选项, DB 注释 7 个状态 | MeetingNew.vue:37-42 |
加 "待监管/监管通过/待整改/待结算/已结算" 5 个 option | P2 |
| 10 | meetingId String → Long (跟 BizProject.projectId 同模式) | BizMeeting.java:13 + getter/setter + BizMeetingMapper.java:10 + BizMeetingServiceImpl.java:17,23 + BizMeetingController.java:31 |
跟之前 9 文件同模式改 | P1 |
| 11 | supervisionTime String → Date (跟 BizProject.startTime/endTime 同模式) | BizMeeting.java:68 |
加 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
P1 |
| 12 | preselectProjectId 只在 setup 读一次, 路由变化不响应 | MeetingNew.vue:63-64 |
用 computed(() => route.query.projectId) 让其响应式 (但本场景路由不变, 影响小) |
P3 |
| 13 | supervisionOpinion/By/Time 表单缺失 (DB 有, 当前 stage 是 "未执行" 后续阶段需填) | (后续 MeetingEdit 阶段) | 不在本页范围, 在 MeetingEdit.vue 加 | P3 |
11. 引用清单
前端
ry-vue3/src/views/manager/MeetingNew.vue— 新建会议独立页 (115 行)ry-vue3/src/views/manager/Meetings.vue— 会议管理列表 (保存后跳转目标)ry-vue3/src/views/manager/Projects.vue:97— 操作列 dropdown "建会" 跳转入口router.push('/manager/meetings/new?projectId=${row.projectId}')ry-vue3/src/layout/AdminLayout.vue:99— 菜单项 "会议管理"ry-vue3/src/router/index.js:70—/manager/meetings/new路由ry-vue3/src/utils/request.js:15-30— 响应拦截器 (code=200 直接放行 data)
后端 (Java)
ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizMeetingController.java— 会议主 controller (54 行, 5 个 endpoint)ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizMeeting.java— 实体 (103 行, 字段含 2 个类型不一致: meetingId String vs meeting_id bigint + supervisionTime String vs supervision_time datetime)ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizMeetingServiceImpl.java— service 实现 (34 行, insert 调 SnowflakeId.injectIfEmpty)ry-api/ruoyi-business/src/main/java/com/ruoyi/business/mapper/BizMeetingMapper.java— mapper 接口 (16 行, 全部 String)
Mapper XML
ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml— 会议 mapper (76 行, 🛑 INSERT 严重残缺: 只插 meeting_id)
关联文件
ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizProjectController.java:139-143—GET /business/project/{projectId}(依赖此接口回填项目名, Long @PathVariable 已修)ry-vue3/src/api/public.js:90—bizGet(entity, id)→GET /business/${entity}/${id}
配置 / 工具
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 注入 (BizMeetingServiceImpl:23 调 injectIfEmpty)
原型
proto/html/components/new-meeting.html— 新建会议原型 (248 行, 标"角色: 执行方" 实装给 manager 用, 角色归属错位)proto/html/components/project-manage.html:529— 建会入口navigateTo('new-meeting.html?project_code=' + code)(实装改用 projectId 参数)proto/html/manager.html(间接) — manager 端菜单入口
数据库 (实测)
biz_meeting— 7 行, 4 索引 (含 1 唯一)biz_project— 11 行, 7 索引 (见manager_projects.md§7.1)biz_project_assign— 11 行, 5 索引 (见manager_projects.md§7.2)