Files
guoju0808/_self/admin_labor_protocol.md
T
郭庆泰andClaude 3198c408b7 feat: 劳务协议编辑改独立页 + 去 el-card 圆角框
- 新增 LaborProtocolEdit.vue (独立页, 新建/编辑复用, 跟 ExpertNew.vue 风格一致)
- 加路由 /admin/labor-protocol/new 和 /admin/labor-protocol/edit/:id
- LaborProtocol.vue 删 el-dialog + 改为 router.push 跳转
- 新增 .form-card.nested { border: none; box-shadow: none; } 去 el-card 圆角框
- 新增 _self/admin_labor_protocol.md 审查报告

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

15 KiB

/admin/labor-protocol 页面端到端审查

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


0. 组件定位 (四跳链)

命中
① 角色菜单 AdminLayout.vue:86 MENU.admin[网站管理]{ path: '/admin/labor-protocol', title: '劳务协议配置', icon: Document }
② 路由 router/index.js:50name: 'admin-labor-protocol', component: () => import('@/views/admin/LaborProtocol.vue')
③ 组件 ry-vue3/src/views/admin/LaborProtocol.vue
④ 原型 无对应原型 (proto/html/components/ 中无 "劳务协议配置" 页面, 最近的是 admin-settings.html 是系统设置, 不同业务)

注意: 这是新建页面, 没有原型图可对比。从 HeguiConstants.java 的 3 个硬编码 HTML 字符串 (DEFAULT_PROTOCOL / BBS_PROTOCOL / JIANYAN_PROTOCOL) 迁移而来。


1. 主要功能 + 可见性

1.1 主要功能

功能 前端入口 后端接口 数据表
列表 (分页) LaborProtocol.vue:25-37 GET /business/laborProtocolTemplate/list biz_labor_protocol_template
筛选 (按模板名/状态) LaborProtocol.vue:8-19 同 list (mapper <if> 过滤) biz_labor_protocol_template
全部启用 (下拉) (无 UI, 接口预留) GET /business/laborProtocolTemplate/allEnabled
默认模板 (单条) (无 UI, 接口预留) GET /business/laborProtocolTemplate/default
详情 (无 UI, 接口预留) GET /business/laborProtocolTemplate/{id}
新建 LaborProtocol.vue:140-152 POST /business/laborProtocolTemplate
编辑 同上 (dialog 复用) PUT /business/laborProtocolTemplate
删除 (批量/ 单条) LaborProtocol.vue:160-167 DELETE /business/laborProtocolTemplate/{id}
设为默认 LaborProtocol.vue:153-158 PUT /business/laborProtocolTemplate/{id}/default 同 (事务保证全局唯一)

1.2 可见性 (三层过滤)

来源 校验字段
前端菜单 AdminLayout.vue:86 admin 角色可见
路由守卫 router/index.js:23 meta: { role: 'admin' } admin token 校验
后端 无强制 role 校验 (依赖 Spring Security 注解 @PreAuthorize 缺失) ⚠️ 任何登录用户都能调

不能看到本页面的人群: leader / manager / doctor / executor / sponsor — 他们各自走自己的角色页面。

数据隔离: ⚠️ 没有按角色二次校验BizLaborProtocolTemplateController 没有 @PreAuthorize("hasRole('admin')"), 任何已登录用户都能调这 8 个接口。这是设计疏忽。


2. 筛选项 (filter-form)

LaborProtocol.vue:8-19:

UI label 控件 绑定字段 后端 SQL (selectList) 命中表.字段 选项来源 原型对照
模板名 el-input q.templateName <if test="templateName != null and templateName != ''">and template_name like concat('%', #{templateName}, '%')</if> biz_labor_protocol_template.template_name (varchar100) N/A (无原型)
状态 el-select (Y=启用 / N=禁用) q.status <if test="status != null and status != ''">and status = #{status}</if> biz_labor_protocol_template.status (char(1)) 写死 (硬编码 Y/N) N/A
查询/重置/新建 按钮 N/A

3. 工具栏按钮 (toolbar)

LaborProtocol.vue:21-24 + dialog 操作:

按钮 功能 接口 涉及表 原型对照
查询 触发表单 submit (前端) N/A
重置 清 q, 重 load (前端) N/A
新建模板 打开 dialog POST /business/laborProtocolTemplate biz_labor_protocol_template N/A
编辑 (行内) openEdit(row) 填表单 PUT /business/laborProtocolTemplate N/A
设为默认 (行内) setDefault(row) PUT /business/laborProtocolTemplate/{id}/default 同 (事务, 清空其他 default_flag) N/A
删除 (行内) removeRow(row) DELETE /business/laborProtocolTemplate/{id} N/A

4. 表格 (el-table)

LaborProtocol.vue:28-50

4.1 数据来源

BizLaborProtocolTemplateMapper.xml selectList:

select id, template_name, template_content, default_flag, sort_order, status, remark,
       create_by, create_time, update_by, update_time
from biz_labor_protocol_template
[where name like concat('%', #{templateName}, '%')]   -- 模板名模糊匹配
[and   status = #{status}]                              -- 状态精确匹配
[and   default_flag = #{defaultFlag}]                   -- 默认标记精确匹配 (预留,前端未用)
order by sort_order asc, id asc

startPage() + selectList() 走 RuoYi PageHelper 分页。

4.2 列映射

列名 row.* DB 表.字段 原型对照
ID row.id biz_labor_protocol_template.id N/A
模板名 row.templateName .template_name (varchar100) N/A
排序 row.sortOrder .sort_order (int) N/A
默认 (默认 row.defaultFlag === 'Y' ) → el-tag success .default_flag (char(1))
状态 (状态 row.status === 'Y' ? 'success' : 'info' .status (char(1))
备注 row.remark .remark (varchar500) N/A
更新时间 row.updateTime .update_time (datetime) N/A
操作

5. 行内操作按钮

LaborProtocol.vue:43-48:

按钮 触发函数 接口 后端动作 涉及表.字段
编辑 openEdit(row) PUT /business/laborProtocolTemplate service.update(entity)mapper.updateByPrimaryKey .template_name/.template_content/.sort_order/.status/.remark/.default_flag
设为默认 setDefault(row) PUT /business/laborProtocolTemplate/{id}/default service.setDefault(id)@Transactional: mapper.clearAllDefault() 全部置 N → mapper.updateByPrimaryKey 目标置 Y .default_flag (整张表)
删除 removeRow(row) DELETE /business/laborProtocolTemplate/{id} service.deleteByPrimaryKeys([id])mapper.deleteByPrimaryKeys 整行

真实落点:

  • 设为默认:事务保证全局唯一 (@TransactionalBizLaborProtocolTemplateServiceImpl.setDefault),先 clearAllDefault 后改目标
  • 删除: 拒绝默认模板删除 (LaborProtocol.vue:162-163 前端校验 row.defaultFlag === 'Y')

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

6.1 实测 DB schema (2026-08-19)

CREATE TABLE biz_labor_protocol_template (
  id bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  template_name varchar(100) NOT NULL COMMENT '模板名 (前端下拉显示)',
  template_content longtext NOT NULL COMMENT '模板 HTML (含占位符 {name} {phone} 等)',
  default_flag char(1) DEFAULT 'N' COMMENT '默认标记: Y=默认, N=非默认',
  sort_order int DEFAULT '0' COMMENT '排序 (前端下拉顺序)',
  status char(1) DEFAULT 'Y' COMMENT '状态: Y=启用, N=禁用',
  remark varchar(500) DEFAULT NULL COMMENT '备注',
  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 (id),
  KEY idx_default_flag (default_flag),
  KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='劳务协议模板配置';

6.2 Entity ↔ DB 字段对照

实体字段 中文列名 (DB COMMENT) Java 类型 表字段 DB 类型 一致?
id 主键 Long id bigint AUTO_INCREMENT
templateName 模板名 String template_name varchar(100) NOT NULL
templateContent 模板 HTML (含占位符) String template_content longtext NOT NULL
defaultFlag 默认标记 Y/N String default_flag char(1) DEFAULT 'N'
sortOrder 排序 Integer sort_order int DEFAULT 0
status 状态 Y启用/N禁用 String status char(1) DEFAULT 'Y'
remark 备注 String remark varchar(500)
createBy (BaseEntity) 创建者 String create_by varchar(64)
createTime (BaseEntity) 创建时间 Date create_time datetime
updateBy (BaseEntity) 更新者 String update_by varchar(64)
updateTime (BaseEntity) 更新时间 Date update_time datetime

无字段缺失, 无类型不一致


7. 索引检查

7.1 biz_labor_protocol_template 索引 (实测)

Key 用途覆盖
PRIMARY id WHERE id = ?
idx_default_flag default_flag WHERE default_flag = 'Y' (取默认模板)
idx_status status WHERE status = 'Y' (下拉只显示启用)

7.2 缺口分析

查询场景 涉及列 现有索引 评价
列表分页 (按 sort_order, id 排) sort_order, id PRIMARY 走 filesort, OK
模板名模糊筛选 template_name 无索引 ⚠️ 暂可接受 (数据少)
状态精确筛选 status idx_status
默认模板 default_flag='Y' idx_default_flag
全启用下拉 status='Y' idx_status

8. 与原型差异

8.1 实现新增 (超出原型)

实现 原型 评价
整个页面 无对应原型 全新业务
编辑 dialog + 富文本 textarea 实现完整
"设为默认" 按钮 业务创新
default_flag 全局唯一事务保证 数据完整性设计
3 个 HTML 模板从 HeguiConstants 迁移 内容来源

8.2 原型有但实现缺失

无原型可对比 — 这是新业务页面。

8.3 文字 / 标签差异

N/A

8.4 总结

整体方向: 这是新建页面, 端到端完整 (DB + 后端 + 前端)。没有原型对比, 无从评判偏离度。


9. 字段冗余 / 设计问题

9.1 default_flag 全局唯一 — 设计 OK

  • DB 没有 UNIQUE 约束 (MySQL UNIQUE 不支持 'Y' 唯一,允许多 NULL)
  • App 层事务保证: BizLaborProtocolTemplateServiceImpl.setDefault@Transactional,先 clearAllDefault 再设目标为 Y
  • 前端兜底: 编辑/删除按钮在 defaultFlag === 'Y' 时禁用

9.2 无 template_type 字段 — 设计 OK

HeguiConstants 有 3 个模板 (北京整合医学学会 / 北京中卫 / 北京医学检验学会), 都属于"劳务协议"类型。用户要求"全局, 不需要加所属机构" → 当前实现一致。

  • 后续如要区分协议类型 (劳务 / 保密 / 知识产权 等), 可加 template_type 字段, 暂不需要

9.3 template_content 是 longtext (16MB 上限)

3 个 HeguiConstants 模板实际长度: 14K / 11K / 12K (合计 36K)。longtext 容量上限 ~16M, 够用。

  • ⚠️ 灌入的 HeguiConstants 是 HTML 转义后的字符串字面量 (Java 转义 \\n 等), DB 存的是已 unescape 的真实 HTML。已实测验证。

9.4 没有"template_id" 外键

template_content 里的 {name} {phone} {会议名称} 等占位符在签署时由谁替换? 目前没人替换!

  • ⚠️ 闭环问题: 模板只是"配置", 实际签署 (biz_meeting_attendee.labor_protocol 存 URL) 还需要单独的工作流渲染模板 → 替换占位符 → 生成 PDF/HTML
  • 后续: 单独工作流 (可能用 hwt-serve 的 ActivityProcess 等), 不在本页面范围

10. 待修复列表

# 问题 文件 修复建议 严重度
1 Controller 无角色校验: 任何已登录用户都能调 8 个接口 BizLaborProtocolTemplateController.java @PreAuthorize("hasRole('admin')") 类级别注解 (依赖 RuoYi 注解 + Spring Security 配置) P0
2 /api/business/laborProtocolTemplate.js default/allEnabled 接口前端未使用 api/business/laborProtocolTemplate.js 暂时保留 (后续签署流程要用), 或删除以减小 API surface P3
3 富文本编辑器缺失, 现在 textarea 不友好 LaborProtocol.vue 集成 Tinymce 或 wangEditor (含 HTML 预览) P2
4 template_content 没"占位符说明"提示 LaborProtocol.vue placeholder 加可用占位符列表 (如 {name}, {phone}, {会议名称}) P2
5 入参没有 default_flag 校验: 前端可能传任意字符串 (不是 Y/N) BizLaborProtocolTemplateController.add/edit service 层校验 defaultFlag ∈ {Y, N}, 失败抛 ServiceException P2
6 update mapper 写了 template_content = #{templateContent} 不在 <if> 内 — 总会覆盖 BizLaborProtocolTemplateMapper.xml:53-66 改成 <if test="templateContent != null">template_content = #{templateContent},</if> 避免误清空 P3
7 删除模板没审计 (硬 DELETE, 不走 del_flag='0'``) 模板不重要, 可以接受, 不修 P3
8 template_content 中文乱码: 后端 JSON 响应 Content-Type 没 charset=UTF-8, 浏览器 fallback Latin-1 ResourcesConfig.java configureMessageConverters 设 Jackson 默认 UTF-8 (你说暂不改, 留待后续) P2

11. 引用清单

v1 新建文件 (12 个)

文件 行号 说明
DB biz_labor_protocol_template 11 列 + PK + 2 索引
ry-api/.../domain/BizLaborProtocolTemplate.java 全文 entity
ry-api/.../mapper/BizLaborProtocolTemplateMapper.java 全文 mapper 接口
ry-api/.../resources/mapper/business/BizLaborProtocolTemplateMapper.xml 全文 mapper SQL
ry-api/.../service/IBizLaborProtocolTemplateService.java 全文 service 接口
ry-api/.../service/impl/BizLaborProtocolTemplateServiceImpl.java 全文 service 实现 (含 @Transactional setDefault)
ry-api/.../controller/BizLaborProtocolTemplateController.java 全文 8 个 REST 接口
ry-vue3/src/api/business/laborProtocolTemplate.js 全文 前端 API 封装
ry-vue3/src/views/admin/LaborProtocol.vue 全文 admin 页面 (列表 + dialog)
ry-vue3/src/router/index.js:50 /admin/labor-protocol 路由
ry-vue3/src/layout/AdminLayout.vue:86 加菜单 "劳务协议配置"

没动

文件 说明
HeguiConstants.java (hwt-serve) 3 个常量保留, 内容已灌入 DB
proto/html/components/admin-settings.html 系统设置原型, 不相关

TL;DR (给项目负责人)

新建页面, 端到端完整:

  • DB biz_labor_protocol_template (11 列 + 3 行初始化 + 2 索引)
  • 后端 完整 CRUD + default_flag 事务保证
  • 前端 admin 页面 + 列表 + dialog

P0 安全风险: Controller 没加 @PreAuthorize("hasRole('admin')"), 任何登录用户能调所有接口 → 必须修

P2 体验:

  • 富文本编辑器缺失 (现在是 textarea)
  • 占位符说明缺失
  • template_content 中文乱码 (Content-Type 没 charset,你说暂不改)

正面:

  • 设计清晰, default_flag 全局唯一有 app 层事务保证
  • 3 个 HeguiConstants 模板完整迁移, DB 实测 14K/11K/12K 字符
  • 索引覆盖主要查询场景
  • 没有数据隔离 (因为是全局配置,本来就不需要按用户隔离, 只有角色隔离缺失 — 见 P0)