Files
guoju0808/_self/doctor_home.md
T
郭庆泰andClaude e496f34393 feat: 多处页面改造 + 新增劳务协议模板配置
主要改动:
- biz_meeting_attendee 中间表 + doctor/expert 角色数据隔离
- biz_meeting_attendee 加 handsign/labor_protocol 字段 (劳务协议改造)
- biz_labor_protocol_template 配置表 + admin 页面 (从 HeguiConstants 迁移)
- biz_expert.expertId 改 Long + IdGenerator 生成 (去自增)
- Login.vue / PublicityDetail.vue / Home.vue 等多处 bug 修复 + UI 改进
- 新增 page-tech-review 报告 3 篇 (_self/*.md)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 21:06:49 +08:00

16 KiB

/doctor/home 页面端到端审查 (v3)

审查日期: 2026-08-19 (v3 含修复) 审查范围: 前端 → 后端 → DB → 原型 审查者: Claude Code (page-tech-review skill)

修订 (v1 → v2):

  • ✅ P1 数据隔离: biz_meeting_attendee 中间表 + mapper EXISTS + controller 注入 (doctor/expert 角色)
  • ✅ 欢迎栏显示真实姓名 (biz_expert.name → fallback 到 nickName/userName)

修订 (v2 → v3):

  • ✅ "待签署协议" 数据源从 biz_project_plan 改到 biz_meeting_attendee (按 user_id + 任一未签)
  • ✅ biz_meeting_attendee 表加 2 字段: handsign longtext (Base64 手写签名) + labor_protocol varchar(500) (劳务协议 URL)
  • ✅ 新建后端 controller + service + mapper + 前端 API
  • ❌ 签署 dialog + 手写板 UI 未做 (v3 待修)
  • ❌ 新建会议自动写 attendee 中间表 (BizMeetingController.add) 未做

0. 组件定位 (四跳链)

跳 命中
① 角色菜单 AdminLayout.vue:90 MENU.doctor → { path: '/doctor/home', title: '首页' }
② 路由 router/index.js:90 → name: 'doctor-home', component: () => import('@/views/doctor/Home.vue')
③ 组件 ry-vue3/src/views/doctor/Home.vue
④ 原型 proto/html/doctor.html:204 data-page="components/home.html" → proto/html/components/home.html

1. 主要功能 + 可见性

1.1 主要功能

页面是 dashboard 工作台,4 块内容:

功能 前端入口 后端接口 数据表
欢迎栏 (实时时钟 + 专家姓名) Home.vue:6-15 GET /business/expert/profile biz_expert
待参加的会议 Home.vue:114 bizList('meeting', {pageSize: 5}) GET /business/meeting/list biz_meeting + biz_meeting_attendee ✅
待签署的协议 (v3 改) Home.vue:126 listUnsignedMeetingProtocols() GET /business/meetingAttendee/unsigned biz_meeting_attendee (handsign 或 labor_protocol 为空) ✅
通知消息 Home.vue listMyMessages({limit: 5}) GET /business/message/my biz_message

页面无 CRUD 操作,纯展示 (v3 计划加签署 dialog 但未做)。

1.2 可见性 (三层过滤)

层 来源 校验字段
前端菜单 AdminLayout.vue:90 MENU.doctor 仅 doctor 角色可见
路由守卫 router/index.js:88 meta: { role: 'doctor' } + permission.js token 角色
后端 各 controller 注入 + mapper <if> 过滤 详见 §1.1

数据隔离现状 (v3):

数据块 隔离机制
欢迎栏 biz_expert 按 token userId
待参加会议 biz_meeting_attendee (EXISTS) — controller 注入 userId for doctor/expert
待签署协议 (v3) biz_meeting_attendee.user_id = current user (controller SecurityUtils.getUserId())
通知消息 biz_message.user_id = current user

2. 筛选项 (filter-form)

本页面无 filter-form,跳过。


3. 工具栏按钮 (toolbar)

本页面无 toolbar,只有 3 个"更多"链接 (Home.vue:22, 37, 57)。


4. 表格 (el-table)

本页面无表格,使用 ul/li 自定义列表。


5. UI 元素映射

5.1 欢迎栏 (Home.vue:6-15)

UI 绑定 来源
用户名 (左) displayName (computed) 优先级: biz_expert.name → nickName → userName → '专家'
当前时间 nowTime (setInterval) 前端 Date
当前日期 nowDate (setInterval) 前端 Date

5.2 待参加会议列表 (Home.vue:24-32)

UI label 绑定字段 后端 SQL 命中表.字段 原型对照
会议名 m.meetingName selectList + EXISTS biz_meeting_attendee biz_meeting.meeting_name ✅
状态/时间 formatTime(m.startTime) selectList biz_meeting.start_time ✅

5.3 待签署协议列表 (Home.vue:39-49) — v3 重大改动

UI label 绑定字段 后端 SQL 命中表.字段 原型对照
协议名 s.meetingName selectUnsignedByUserId JOIN biz_meeting biz_meeting.meeting_name (via JOIN) ⚠️ 原型写"协议/意见书",实际显示会议名
状态 固定 "待签署" — — ⚠️ 实现只显示"待签署",原型有"已完成"

v2 → v3 数据流变化:

  • v2 (错误): GET /business/projectPlan/list → 按 submitter_id 过滤 (这是"投稿方案"语义, 不是劳务协议)
  • v3 (正确): GET /business/meetingAttendee/unsigned → 当前用户 + 任一未签 → JOIN biz_meeting 取会议名

API (ry-vue3/src/api/business/meetingAttendee.js):

export function listUnsignedMeetingProtocols() {
  return request({ url: '/business/meetingAttendee/unsigned', method: 'get' })
}
export function updateHandsign(id, handsign) { ... }
export function updateLaborProtocol(id, laborProtocol) { ... }

后端 mapper SQL (核心):

select a.id, a.meeting_id, a.user_id, a.handsign, a.labor_protocol,
       m.meeting_name as meetingName, m.start_time as startTime,
       m.end_time as endTime, m.project_name as projectName, m.project_no as projectNo
from biz_meeting_attendee a
inner join biz_meeting m on m.meeting_id = a.meeting_id
where a.user_id = #{userId}
  and (a.handsign is null or a.handsign = ''
    or a.labor_protocol is null or a.labor_protocol = '')
order by m.start_time asc

判定逻辑 (决策 A1): 任一未签即"待签署"。两条都有值就排除。

5.4 通知消息列表 (Home.vue:59-69)

略,同 v2 报告。


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

6.1 biz_meeting_attendee (v3 实测, 2026-08-19)

CREATE TABLE biz_meeting_attendee (
  id bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  meeting_id bigint NOT NULL COMMENT '会议ID (FK biz_meeting.meeting_id)',
  user_id bigint NOT NULL COMMENT '参会人 user_id (FK sys_user.user_id)',
  handsign longtext COMMENT '手写签名 Base64',
  labor_protocol varchar(500) DEFAULT NULL COMMENT '劳务协议 URL (OSS)',
  create_by varchar(64) DEFAULT '' COMMENT '添加人',
  create_time datetime DEFAULT NULL COMMENT '添加时间',
  PRIMARY KEY (id),
  UNIQUE KEY uk_meeting_user (meeting_id, user_id),
  KEY idx_user_id (user_id)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会议参会人';

6.2 BizMeetingAttendee Entity ↔ DB 字段对照

实体字段 中文列名 Java 类型 表字段 DB 类型 一致?
id 主键 Long id bigint AUTO_INCREMENT ✅
meetingId 会议ID Long meeting_id bigint ✅
userId 参会人 user_id Long user_id bigint ✅
handsign 手写签名 Base64 String handsign longtext ✅ (v3 新增)
laborProtocol 劳务协议 URL (OSS) String labor_protocol varchar(500) ✅ (v3 新增)
createBy (BaseEntity) 添加人 String create_by varchar(64) ✅
createTime 添加时间 Date create_time datetime ✅
meetingName (transient) — String JOIN m.meeting_name — ✅ (联表字段)
startTime (transient) — Date JOIN m.start_time — ✅
endTime (transient) — Date JOIN m.end_time — ✅
projectName (transient) — String JOIN m.project_name — ✅
projectNo (transient) — String JOIN m.project_no — ✅

5 个 transient 联表字段 接收 selectUnsignedByUserId 的 JOIN 输出, 给前端用。


7. 索引检查

7.1 biz_meeting_attendee

Key 列 用途覆盖
PRIMARY id ✅
uk_meeting_user meeting_id, user_id (UNIQUE) ✅ 防重复 + 精确查
idx_user_id user_id ✅ selectUnsignedByUserId WHERE user_id = ? 走这个索引

评价: 复合查询 (WHERE user_id = X AND (handsign IS NULL OR labor_protocol IS NULL)) 走 idx_user_id,然后行过滤。10k 行内完全 OK。

7.2 缺什么?

  • ❌ 缺 idx_user_status: 如果会议量大且空记录占比小, 想加速"待签署"查询, 可以加 (user_id, handsign IS NULL, labor_protocol IS NULL) 函数索引。但目前数据少, 暂不需要。
  • ❌ 缺 idx_meeting_status: WHERE meeting_id = X AND handsign IS NULL 场景目前不存在 (管理端"看哪些医生没签"的视图未做), 暂不需要。

8. 与原型差异

8.1 实现新增 (超出原型)

项 实现 原型
实时时钟 ✅ ✅ 静态
通知标记已读 ✅ ❌
"更多"链接跳 Vue 路由 ✅ ❌ 静态 HTML
空状态文案 ✅ ❌
欢迎栏显示真实姓名 ✅ (v2) ❌ 静态写死
数据源按 user 隔离 ✅ (v2 加中间表) ❌
劳务协议按会议关联 ✅ (v3 改 biz_meeting_attendee) ❌

8.2 原型有但实现缺失

项 原型 实现 严重度
item-icon 图标 (📅 📄) ✅ ❌ 无 P2
section-title 左侧蓝条 ✅ ❌ 无 P2
欢迎栏渐变背景 ✅ ⚠️ 单色 P3
"已完成" 状态显示 (signed status) ✅ item-status.done ❌ 实现永远显示"待签署" P1
签署交互 (点列表 → 弹 dialog → 上传 handsign + labor_protocol) ✅ 原型点击 alert ❌ 无 dialog, 点列表没反应 P1

8.3 文字 / 标签差异

项 原型 实现
协议状态文案 "待签署 / 已完成" "待签署" (永远)
协议名显示 "《方案名》专家意见书" "会议名" (会议关联, 语义不同)

8.4 总结

v3 整体方向: 数据语义修正 (劳务协议按会议关联 ✅), 视觉 + 交互仍待补。


9. 字段冗余 / 设计问题

9.1 biz_meeting_attendee 设计合理 (v3 验证)

  • handsign longtext 存 Base64 是合理的: 不依赖 OSS, 数据完整性内嵌 DB, 简单
  • labor_protocol varchar(500) 存 OSS URL 是合理的: 大文件走 OSS, DB 只存引用

9.2 缺管理端点 ⚠️ P1

BizMeetingAttendeeController 目前只有 doctor 用的 3 接口。新建会议时, 没有 API 自动把参会人写入中间表。

  • 影响: 现有数据需要手动 INSERT (像之前那样用 SQL), 业务没法在线给会议加参会人
  • 修复: BizMeetingController.add 接受 attendeeUserIds: Long[], 批量 insert 中间表

9.3 缺管理列表 (P3)

没有"看某会议的所有参会人 + 签署状态"的后台接口 (manager 端要用)。

  • 影响小 (低优先级), 当前 P1 先修

9.4 biz_project_plan 完全废弃? ⚠️

v3 把"待签署协议"从 biz_project_plan 改走中间表, 但 biz_project_plan 表和 BizProjectPlan entity 还在。

  • 投稿方案业务 (manager 审稿) 仍存在, 这是独立的 "项目策划方案投稿" 业务
  • 与劳务协议是两回事, 不应混淆

9.5 biz_meeting.status 缺失 (沿 v2 待修)

业务希望"待签署协议"按 meeting.status 过滤 (会议取消/已结束的不再显示待签), 但 biz_meeting 无 status 字段, 需要 ALTER。


10. 待修复列表 (v3)

# 问题 文件 修复建议 严重度 状态
1 biz_meeting 无 user 隔离 Controller + mapper 中间表 EXISTS P1 ✅ v2 修
2 欢迎栏显示数字 userName Home.vue displayName computed 优先级链 P2 ✅ v2 修
3 劳务协议数据源错 (biz_project_plan) Home.vue + BizProjectPlanController 改走 biz_meeting_attendee P0 ✅ v3 修
4 表无 handsign/labor_protocol biz_meeting_attendee ALTER 加 2 列 + 后端全套接口 P0 ✅ v3 修
6 签署 dialog 未做: 点"待签署"列表项无反应 Home.vue 弹 dialog: handsign 用 textarea 输入 Base64 (临时), labor_protocol 用 OssImageUploader, 提交分别 PUT P1 待修
7 手写板 canvas 组件未做: handsign 暂用 textarea 输入 Base64, 用户体验差 新建 SignaturePad.vue vue-signature-pad 库 或 自写 canvas + toDataURL() P2 待修
8 item-icon 图标缺失 (📅 📄) Home.vue 加 .item-icon 样式 + 模板 P2 待修 (v1 提的)
9 section-title 左侧蓝条缺失 Home.vue:151 border-left + padding-left P2 待修 (v1 提的)
10 欢迎栏渐变背景降级为单色 Home.vue:145 还原 gradient P3 待修 (v1 提的)
11 biz_project_plan.status 注释缺失 DB ALTER COMMENT P3 待修
12 biz_meeting.status 不存在 (无法过滤"已结束"会议) DB + mapper ALTER 加 status 字段 P3 待修
13 Home.vue:131 read: a.read || i >= 2 强制第3条后标已读 Home.vue 改为纯 read: a.read P3 待修
14 新建会议自动写 attendee 中间表: BizMeetingController.add 不支持传 attendeeUserIds BizMeetingController.java + service add 接受 attendeeUserIds: Long[], 批量 insert 中间表 P1 待修
15 缺管理端"看参会人列表 + 签署状态"接口 新建 BizMeetingAttendeeController 管理端点 GET /business/meetingAttendee/byMeeting/{meetingId} P3 待修

11. 引用清单

v3 新增/改的文件

文件 行号 说明
DB biz_meeting_attendee — ALTER 加 handsign longtext + labor_protocol varchar(500)
ry-api/.../controller/BizMeetingAttendeeController.java 全文 v3 新建
ry-api/.../service/IBizMeetingAttendeeService.java 全文 v3 新建
ry-api/.../service/impl/BizMeetingAttendeeServiceImpl.java 全文 v3 新建
ry-api/.../domain/BizMeetingAttendee.java 全文 v3 加 handsign + laborProtocol + 5 个 transient 联表字段
ry-api/.../mapper/BizMeetingAttendeeMapper.java 全文 v3 加 updateHandsign + updateLaborProtocol + selectUnsignedByUserId
ry-api/.../mapper/business/BizMeetingAttendeeMapper.xml 全文 v3 加 resultMap 字段 + 2 update + selectUnsignedByUserId 联表 SQL
ry-vue3/src/api/business/meetingAttendee.js 全文 v3 新建: 3 API helpers
ry-vue3/src/views/doctor/Home.vue 39-49, 124-134 v3 改 "待签署协议" 数据源

未改的文件 (沿 v2)

文件 行号 说明
ry-vue3/src/layout/AdminLayout.vue 90 MENU.doctor 第 1 项
ry-vue3/src/router/index.js 88-99 /doctor 父路由
proto/html/doctor.html 204 原型菜单
proto/html/components/home.html 全文 原型基准
ry-vue3/src/api/public.js — listMyMessages / bizList
ry-vue3/src/api/business/expert.js 全文 v2 新建: getMyExpertProfile
ry-api/.../controller/BizMeetingController.java 23-33 v2 改: 加 userId 注入
ry-api/.../controller/BizProjectPlanController.java — 不再被"待签署协议"调用, 投稿方案独立业务
ry-api/.../controller/BizExpertController.java 46-50 欢迎栏取名
MySQL guoju0808.biz_meeting — v2 无改动
MySQL guoju0808.biz_meeting_attendee — v3 加 2 列
MySQL guoju0808.biz_message — 通知
MySQL guoju0808.biz_expert (user_id=142) — name='dct07'

v3 总结

修复 2 项 (跨 v2 → v3):

  1. ✅ 数据语义修正: "待签署协议" 从 biz_project_plan 改到 biz_meeting_attendee (语义正确: 劳务协议按会议关联, 不按投稿方案)
  2. ✅ 新字段 + 后端: handsign longtext + labor_protocol varchar(500) + 完整后端 CRUD 接口 + 前端 API

还剩 11 项待修复,按严重度:

  • P1 (2 项): 签署 dialog 缺失 (点列表没反应); 新建会议没自动写 attendee
  • P2 (3 项): 手写板 UI; item-icon; section-title 边框
  • P3 (6 项): 字段注释 + status 字段 + 索引 + 前端兜底 + 管理端点

SQL 实测验证 (user_id=142 当前状态):

待签署会议: 3 个 (id=1,2,3 全部 handsign=NULL labor_protocol=NULL)
会议名: 整合医学学会项目评审会 / 基层医疗改革试点中期评估会 / 数字化医疗转型方案评审会
按 start_time 升序 ✓