Files
guoju0808/_self/manager_meetings.md
T

24 KiB
Raw Blame History

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, 是合理的设计选择)


0. 4 跳定位 (铁律: 不按名字猜)

文件:行 内容
① 角色菜单 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 项筛选 (项目编号/会议名称/当前阶段/签署状态)

1. 主要功能 + 可见性

1.1 主要功能

功能 前端入口 后端接口 数据表
筛选 + 分页列表 Meetings.vue <el-table> + <el-pagination> GET /business/meeting/list (BizMeetingController:24-29) biz_meeting
查看详情 (弹窗) onView(row)el-dialog + el-descriptions 只读 复用列表 row, 无额外请求 biz_meeting
修改会议 (弹窗) onEdit(row)submitEdit() PUT /business/meeting (BizMeetingController:42-46) biz_meeting
提交 (单条) onSubmit(row) → confirm → bizUpdate PUT /business/meeting biz_meeting.current_stage
复制 (单条) onCopy(row)editMode='copy'submitEdit 走 bizAdd 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 隔离

数据隔离: 缺失 — manager 端没有 @DataScopelead_user_id 过滤。当前 DB 7 行测试数据无风险, 生产环境会暴露全部会议 (见 §10 #1)。

看不到本页面的人群: admin / leader / doctor / executor / sponsor 各有独立 /<role>/meetings 路由 + 独立菜单项, 路由层就分开了。


2. 筛选项 (filter-form)

UI label 控件 绑定字段 后端 <if> 命中表.字段 选项来源 原型对照
项目编号 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 第?期 (原型有)
会议时间 (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-input (模糊) q.remark 缺失biz_meetingremark (无字段) 原型无

完整 SQL 块 (BizMeetingMapper.xml:37-50):

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 <=]
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 原型无 (manager 专属)

提示行: <span v-if="selectedIds.length" class="filter-tip">已选 N 条</span> — 推到 batch-bar 最右 (.filter-tip { margin-left: auto })。


4. 表格 (el-table)

4.1 数据来源

见 §2 完整 SQL 块 (从 BizMeetingMapper.xml:37-50 selectList + BizMeetingMapper.xml:29-32 selectFields)。

4.2 列映射

列名 row.* DB 表.字段 原型对照
多选框 (selection) 原型无 (manager 专属)
项目编号 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) (无) 原型无
操作 查看/修改/提交/复制 原型 1 个按钮 (签署劳务), manager 端 4 按钮

新增列 (原型无, 实现增) 改进: 项目形式, 开始/结束时间, 总期数, 期数, 多选框, 4 个操作按钮。

遗漏列 (DB 有, 列表未显示):

  • org_name (公司名称) — 列表可加
  • create_time / update_time — 审计可用
  • labor_signed (0/1) — 原型有 "签署状态" 列
  • invitation_url / schedule_url — 原型有, 当前只在详情 dialog

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 (新行) 原型无

真实落点:

  • "提交": 直接 UPDATE current_stage = '执行中', 无业务校验 (任何状态都能转, 包括"已完结"→"执行中" 状态回滚)。不写日志 (biz_meeting_log 表不存在)。
  • "修改": 改 6 字段 (无 remark, 详见 §9 #3); start_time > end_time 不校验; meetingName 空不校验。

6. Java Entity ↔ 数据库表 一致性

6.1 biz_meeting DDL (实测 2026-08-18)

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',
  `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=9 DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_0900_ai_ci COMMENT='会议表';

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

6.3 Entity ↔ 表字段对照表

实体字段 中文列名 (DB COMMENT) 实体类型 表字段 表类型 一致?
meetingId 会议ID Long meeting_id bigint
businessId 业务编号ID 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)
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) (BaseEntity, MetaObjectHandler 自动填)
createTime 创建时间 Date create_time datetime
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

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 = (filter 启用后生效)

缺失但暂可接受 (按 §2 筛选启用后):

  • idx_project_noLIKE '%xxx%' 模糊匹配无法走索引, 但当前 7 行可接受 (见 §10 #6)
  • idx_meeting_name — 同上
  • 复合 (project_id, meeting_id) — JOIN 用, 当前单 project_id 索引够

8. 与原型差异

8.1 实现新增 (原型没有) — 改进

原型 实现 评估
筛选 会议ID / 第?期 / 会议时间 / 项目形式 / 备注 4 项 8 项 manager 端管理需要
表格多选 + 批量提交 manager 专属
操作列 4 按钮 (查看/修改/提交/复制) 1 按钮 角色不同
分页 必要
弹窗 (查看/修改) 跳转 meeting-detail.html 弹窗紧凑

8.2 原型有但实现缺失 — 回退

原型 实现 差距
"签署状态" 列 (labor_signed) 仅 filter 8 项无此 (见 §10 #4) 缺 filter + 列
弹框 邀请函 + 日程 (原型动态面板) 原型专门有
顶部红字提示

8.3 文字 / 标签差异

原型 实现 评估
面包屑 首页 > 我参与的会议 首页 / 会议管理 角色匹配
当前阶段 select 未开始 / 已结束 未执行 / 执行中 / 已执行 / 已完结 ⚠️ 实装更细, 但跟 DB 实际数据不一致 (见 §9 #1)
颜色 #1890ff (浅蓝) var(--brand-primary) (深海军蓝) 项目色

8.4 总结

整体方向: 扩展 — 实现远超原型 (manager 端多 5 列 + 批量提交 + 修改 dialog); 原型"邀请函/日程弹框"和"签署状态"未移植; "红字提示"未加。


9. 设计问题

集中列设计层面问题, 不重复 §10 (待修复)。已修的归附录 "修复记录"。

9.1 当前阶段 select 与 DB 实际数据不一致

DDL 注释列了 7 种 (未执行/执行中/监管通过/待整改/待结算/已结算/已结题), 但 DB 实际 6 种 (0/已结算/已结题/待整改/待监管/监管通过), 前端 select 又是另一套 4 种 (未执行/执行中/已执行/已完结)。三者全不一致, filter 启用后用户选"未执行"也筛不到任何数据。

9.2 calcRemain 读不存在的字段

Meetings.vue:186row.submitDeadlineDays, 但 BizMeeting 实体无此字段 (BizProject 才有)。当前永远 fallback 到 30 天, 是个静默 bug (见 §10 #2)。

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)
  • 列表"备注"列永远空
  • 详情 dialog"备注"行永远 -
  • 修改 dialog "备注"输入框写入 → mapper UPDATE 无对应 <if> → 静默丢

修复路径有 2 条 (见 §10 #3)。

9.4 数据隔离缺失 (manager 端)

BizMeetingMapper.xml:37 selectList@DataScopelead_user_id 过滤。当前测试数据无风险, 生产必修 (见 §10 #1)。

9.5 状态回滚无校验

"提交" 操作任何 stage 都能转 "执行中", 包括 "已完结" → "执行中" (状态回滚)。应加前置校验 (见 §10 #5)。


10. 待修复列表 (严格只列未修)

# 问题 文件 修复建议 严重度
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 calcRemainrow.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

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/utils/request.js:15-30 — axios 拦截器 (__silentError 跳过 toast)
  • ry-vue3/src/api/public.js:32, 92-93bizList / 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/.../mapper/BizMeetingMapper.java — mapper 接口

Mapper XML

  • ry-api/ruoyi-business/src/main/resources/mapper/business/BizMeetingMapper.xml:128 — selectList 7 个 <if> 已生效

配置 / 工具

  • 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/SecurityUtils.java:27 — getUserId()

原型

  • proto/html/components/meetings.html:546 — "我参与的会议" 原型 (doctor 端, 实装 manager 重用)
  • proto/html/components/meeting-detail.html — 会议详情原型 (现用 el-dialog 替代)

数据库 (实测 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

附录: 修复记录 (按 commit 时间倒序)

已修的问题不进入 §10。本节按 commit 顺序列出, 每条标明 issue → fix → 验证。

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 行; 留空 → 不影响其他筛选.

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 正常.

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 修复