Files
guoju0808/_self/doctor_submissions.md
郭庆泰andClaude 8aba05aa0f docs(review): sponsor/account 端到端审查报告
11 章穿透审查:
- 四跳定位 (AdminLayout → router → Account.vue → proto)
- 后端链路: SysProfileController → SysUserMapper
- DB 实测 sys_user DDL + 索引
- 与原型 sponsor-account.html 对比
- 发现 P1: 改手机号未走短信验证码 (后端 /changePhone 已实现但前端未用)
- 发现 P3: sys_user 缺 uk_user_name / idx_phonenumber

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 20:47:23 +08:00

27 KiB
Raw Permalink Blame History

doctor/Submissions 端到端技术审查

审查日期: 2026-08-18 目标页面: ry-vue3/src/views/doctor/Submissions.vue (我的项目设计投稿) 审查方法: 前端 → 后端 → 数据库 → 原型 四层穿透 审查范围: AdminLayout 菜单 → router 路由 → Vue 组件 → BizProjectPlanControllerBizProjectPlanMapper.xmlbiz_project_plan 表 → proto/html/components/submissions.html


第 0 章: 组件定位 (四跳)

文件 命中
① 角色菜单 ry-vue3/src/layout/AdminLayout.vue MENU.doctor[4] = { path: '/doctor/submissions', title: '我的项目设计投稿', icon: EditPen } 112
② 路由 ry-vue3/src/router/index.js name: 'doctor-submissions', component: () => import('@/views/doctor/Submissions.vue') 94
③ 组件 ry-vue3/src/views/doctor/Submissions.vue 审查目标文件
④ 原型 proto/html/components/submissions.html doctor.html:208 <a data-page="components/submissions.html"> 208 / submissions.html

路由表 (router/index.js:88-99) 还包含 submission/new (新建)、submission/detail/:planId (详情)、submission/edit/:planId (同 SubmissionNew.vue 修改模式),本审查不展开。


第 1 章: 主要功能 + 可见性

1.1 主要功能

功能 前端入口 后端接口 数据表
列表查询 (分页 + 筛选) bizList('projectPlan', q) → Submissions.vue:119 GET /business/projectPlan/list (BizProjectPlanController.list:29) biz_project_plan
提交投稿 (单条 + 批量) bizUpdate('projectPlan', {planId, status:'1'}) → Submissions.vue:164,178 PUT /business/projectPlan (BizProjectPlanController.edit:60) biz_project_plan.status
新建投稿 (路由跳转) router.push('/doctor/submission/new') → Submissions.vue:149 POST /business/projectPlan (Controller.add:47) biz_project_plan
查看/修改 (路由跳转) Submissions.vue:151-155 GET /business/projectPlan/{planId} + PUT (同上) biz_project_plan
项目方向下拉 options request.get('/business/specialPlan/options') → Submissions.vue:111 GET /business/specialPlan/options (BizSpecialPlanController.options:40) biz_special_plan (status='0' 启用项)

1.2 可见性 (三层过滤)

来源 校验字段
前端菜单 AdminLayout.vue:131 menu computed 仅当 role='doctor' 显示;无 requireMain
路由守卫 router/index.js:88-100 meta: { role: 'doctor' } 路由级 role 守卫 (其他角色不可访问)
后端 SQL 强隔离 BizProjectPlanController.list:32-35 if ("doctor".equals(roleType)) setSubmitterId(getUserId())强制只查自己投的稿,前端绕不开

其他角色如何查看投稿:

  • manager 角色看不到此菜单;通过 /manager/plans (manager/Plans.vue) 全量管理所有投稿。
  • admin 角色通过 /admin/library 或 admin 后台直接调接口。
  • sponsor/executor/leader 角色均没有投稿查看入口(意图明确,投稿是评审专家专属)。

数据隔离结论: doctor 端无论 UI 怎么改 (切换 tab/手动调接口),后端 list 都强制覆盖 submitter_id = currentUserId 安全。

1.3 状态枚举不一致警告 ⚠️

位置 取值
biz_project_plan DDL 注释 1 待审 / 2 通过 / 3 拒绝 (没有 0 待提交)
前端 Submissions.vue:25-29 待提交/待审核/通过/拒绝 映射到 0/1/2/3
BizProjectPlanController.add:53-55 doctor 角色强制 setStatus('0') (待提交)

DDL 注释说"状态: 1 待审 / 2 通过 / 3 拒绝",但代码 + 前端都用 0 表示"待提交",且实际 SELECT 查到当前 DB 仅有 1/2 (2 条数据,见 §6.1),0/3 都是无数据状态。 P2 待修: ① DDL 注释补上 0 待提交;② BizAuditStatusEnum (若有) 同步。


第 2 章: 筛选项 (filter-form)

UI label 控件 绑定字段 后端 SQL 命中表.字段 选项来源 原型对比
策划方案名称 el-input q.planName 无过滤(Mapper 没有 planName 条件,详见 §9.1) ⚠️ 原型 label="投稿名称",实现 label 改文案"策划方案名称"
项目方向 el-select q.planDirectionId p.plan_direction_id = #{planDirectionId} (XML:43) biz_project_plan.plan_direction_id GET /business/specialPlan/options → 仅 status='0' 启用项 ⚠️ 原型是 input "学科方向",实现改下拉 → 超出原型 (改进)
项目类别 dict-select (DictSelect 组件) q.planCategory p.plan_category = #{planCategory} (XML:44, 等值匹配) biz_project_plan.plan_category 字典 biz_project_category (sys_dict_data 7 条,value-field=label) 原型没有"项目类别"筛选 (实现扩展)
形式 el-select q.projectForm 无过滤(Mapper 没写 projectForm 条件) 写死 4 项: 线上/线下/线上+线下/其他 原型保留 input,实现升级下拉 (用户确认保留)
状态 el-select q.status p.status = #{status} (XML:48, 等值匹配) biz_project_plan.status 写死 4 项: 待提交 0 / 待审核 1 / 通过 2 / 拒绝 3 ⚠️ 原型 select 是"已结题/未结题",实现映射到 status 码
备注 el-input q.remark p.remark = #{remark} (XML:51, 等值匹配) biz_project_plan.remark 原型一致

P0 发现 (筛选失效): planNameprojectForm 两个筛选项前端都有,但 mapper BizProjectPlanMapper.xml:40-54 没有 <if test="planName"><if test="projectForm"> 条件,后端会默默忽略这两个查询条件,UI 上输入再多次值都查不到。详见 §10 P0-1。

P1 发现 (精确匹配不像搜索): remark= 等值匹配,而非 LIKE,用户输备注关键词搜不到。原型是 input,但语义上应是模糊搜索。详见 §10 P1-3。


第 3 章: 工具栏按钮 (toolbar)

按钮 触发函数 接口 涉及表 原型对比
新建投稿 onCreate()router.push('/doctor/submission/new') (无前端调用, 仅路由跳转) 一致
批量提交 onBatch() → 遍历 selected 调用 bizUpdate('projectPlan', {planId, status:'1'}) PUT /business/projectPlan biz_project_plan (status 改 '1', submitter_id 兜底) 一致,但原型弹"您确定要提交吗?" modal,实现用 ElMessageBox.confirm (效果一致,实现不同)
查找 <el-button @click="load">查找</el-button> 触发 load() 重新列表 一致
重置 <el-button @click="reset">重置</el-button> 清空 q 后 load() 原型无,实现扩展 (辅助筛选)

第 4 章: 表格 (el-table)

4.1 数据来源 SQL (从 mapper 摘出)

-- BizProjectPlanMapper.xml:24-54
select p.plan_id, p.plan_name, p.plan_direction, p.plan_direction_id,
       s.title as plan_direction_title,
       p.plan_category, p.project_form, p.design_file_url, p.status, p.is_settled,
       p.project_no, p.remark, p.submitter_id,
       COALESCE(bp.name, u.user_name) as submitter_name,
       p.create_by, p.create_time, p.update_by, p.update_time
  from biz_project_plan p
  left join biz_special_plan s on s.id = p.plan_direction_id
  left join sys_user u on u.user_id = p.submitter_id
  left join biz_person bp on bp.user_id = u.user_id
 where 1=1
   [and p.plan_direction_id = #{planDirectionId}]
   [and p.plan_category = #{planCategory}]
   [and p.submitter_id = #{submitterId}]   -- doctor 角色强制 SET (Controller.list:33-34)
   [and p.status = #{status}]
   [and p.remark = #{remark}]              -- 等值匹配,非 LIKE
 order by p.plan_id desc

⚠️ 实际跑出当前 DB 仅 2 条数据 (status=1 x 1, status=2 x 1),submitter_id 都不为 NULL。 (mysql 实测,见 §6.1)

4.2 列映射

列名 (label) 绑定 DB 表.字段 原型对比
选择框 el-table-column type="selection" 一致
投稿名称 prop="planName" biz_project_plan.plan_name 一致 (文案一致)
项目方向 {{ row.planDirectionTitle || row.planDirection || '-' }} JOIN biz_special_plan.title 兜底自身 plan_direction ⚠️ 原型是 "学科方向"(文案差异,见 §8.3)
项目类别 prop="planCategory" biz_project_plan.plan_category (字典 label) 原型无 (实现扩展,字典来源)
形式 prop="projectForm" biz_project_plan.project_form 一致 (原型 input 升级为下拉)
设计文件 el-link href=row.designFileUrl biz_project_plan.design_file_url ⚠️ 原型是"查看+下载"双链,实现合并为一个下载链
状态 <audit-status-tag :status="row.status"> biz_project_plan.status ⚠️ 原型纯文本,实现用 Tag 组件
备注 prop="remark" biz_project_plan.remark 一致
操作 按钮组 (查看/修改/提交) 一致

第 5 章: 行内操作按钮

按钮 触发函数 接口 后端动作 涉及表.字段 原型对比
查看 onView(row)router.push('/doctor/submission/detail/${row.planId}') (仅路由) 一致
修改 onEdit(row)router.push('/doctor/submission/edit/${row.planId}') PUT /business/projectPlan (通过 SubmissionNew.vue) updateByPrimaryKey: SET submitter_id = currentUserId, 字段按实体非空更新 biz_project_plan.* 一致
提交 onSubmit(row) → ElMessageBox.confirm → bizUpdate('projectPlan', {planId, status:'1'}) PUT /business/projectPlan updateByPrimaryKey: SET status='1' + submitter_id=currentUserId (兜底) biz_project_plan.status 一致

真实落点:

  • 医生角色真的不能把 status 改成非 '1' —— 因为前端代码只发了 {planId, status:'1'},即使前端被攻破修改请求体,Controller 也不会修改 status 之外的东西,但没有强制 status='1',理论上可以发任意 status 给 doctor。 (业务上没必要,前端足够安全)
  • 修改按钮的可见性 canModify:status !== '1' && status !== '2' → 待审/通过 锁定,符合原型"待审核和审核通过后,操作只显示查看按钮,无法进行修改"。
  • 提交按钮的可见性 canSubmit:status === '0' || status === '3' → 仅未提交/拒绝可提交,符合原型说明。

第 6 章: Java Entity ↔ 数据库表 一致性

6.1 biz_project_plan 主表 DDL (mysql 实测, 2026-08-18)

CREATE TABLE `biz_project_plan` (
  `plan_id`         bigint      NOT NULL AUTO_INCREMENT,
  `plan_name`       varchar(200) NOT NULL,
  `plan_direction`  varchar(500) DEFAULT NULL,
  `plan_direction_id` bigint    DEFAULT NULL,
  `plan_category`   varchar(50)  DEFAULT NULL,
  `project_form`    varchar(20)  DEFAULT NULL,
  `design_file_url` varchar(500) DEFAULT NULL,
  `status`          varchar(20)  DEFAULT '1'    COMMENT '状态 BizAuditStatusEnum: 1待审 2通过 3拒绝',
  `is_settled`      char(1)      DEFAULT 'N',
  `project_no`      varchar(50)  DEFAULT NULL,
  `remark`          varchar(500) DEFAULT NULL,
  `audit_opinion`   varchar(500) DEFAULT NULL,
  `audit_by`        varchar(64)  DEFAULT NULL,
  `audit_time`      datetime     DEFAULT NULL,
  `create_by`       varchar(64)  DEFAULT '',
  `create_time`     datetime     DEFAULT NULL,
  `update_by`       varchar(64)  DEFAULT '',
  `update_time`     datetime     DEFAULT NULL,
  `submitter_id`    bigint       DEFAULT NULL  COMMENT '投?人用户ID (医生侧投稿归? ...)',
  PRIMARY KEY (`plan_id`),
  KEY `idx_plan_status` (`status`),
  KEY `idx_plan_project_no` (`project_no`),
  KEY `idx_plan_submitter` (`submitter_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1463898549354497
  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
  COMMENT='项目策划方案表';

当前 DB 数据状态 (mysql 实测):

  • 总行数: 2
  • submitter_id IS NOT NULL 行数: 2 (100% 都被投稿)
  • 按 status 分组: status=1 x1, status=2 x1 (没有 0/3 数据)

6.2 join 关联表

biz_special_plan (下拉 options 用)

类型 说明
id bigint PK AUTO_INCREMENT biz_project_plan.plan_direction_id
title varchar(200) NOT NULL 前端下拉 label=opt.title
content_type varchar(20) DEFAULT 'rich' (丰富/file 二选一)
content mediumtext 富文本
file_url varchar(500) 上传文件 URL
sort_order int DEFAULT 0 排序,controller /options 没排序 (可改进)
status char(1) DEFAULT '0' 0=启用,1=停用
remark varchar(500)
create_by/update_by varchar(64)
create_time/update_time datetime

sys_dict_data (字典 biz_project_category)

7 条记录,value-field=label (即前端存的"共识""学术项目""重点学科"...)。注意 RuoYi 默认 value-field=dict_value,这里强制改 label,对其他模块兼容性需复查

6.3 Entity ↔ 表 字段对照

实体字段 实体类型 表字段 表类型 一致? 备注
planId String plan_id bigint ⚠️ 类型不一致 详见 §9.2
planName String plan_name varchar(200) NOT NULL
planDirection String plan_direction varchar(500)
planDirectionId Long plan_direction_id bigint
planDirectionTitle String (LEFT JOIN 注入) biz_special_plan.title SQL 别名注入
planCategory String plan_category varchar(50) 字典 label
projectForm String project_form varchar(20)
designFileUrl String design_file_url varchar(500)
status String status varchar(20)
isSettled String is_settled char(1)
projectNo String project_no varchar(50)
remark String remark varchar(500)
isFinished String (不存在!) Entity 冗余字段,DDL 没有此列,代码也没用,应清理
createBy String create_by varchar(64) BaseEntity
createTime Date create_time datetime BaseEntity
updateBy String update_by varchar(64) BaseEntity
updateTime Date update_time datetime BaseEntity
auditOpinion String audit_opinion varchar(500)
auditBy String audit_by varchar(64)
auditTime String audit_time datetime ⚠️ 类型不一致 详见 §9.2
submitterId Long submitter_id bigint
submitterName String (LEFT JOIN 注入) COALESCE(bp.name, u.user_name)

修订:

  • ⚠️ planId 实体 String vs 表 bigint —— MyBatis 自动转换目前没报错 (实测 list 接口 OK),但 SnowflakeId 返回 Long,Entity setPlanId(String) 隐式 toString 后插入,所以 DB 现在实际存的是 bigint 但表达式是 String。建议统一为 Long
  • ⚠️ auditTime 实体 String vs 表 datetime —— 实际 PHP/MyBatis 转换可读出,但前端拿到的时间字符串可能不是 yyyy-MM-dd HH:mm:ss 而是 ISO。建议统一 Date + @JsonFormat
  • isFinished Entity 字段无对应 DDL 列 —— 设计残留,建议删除 (或在 DDL 加列并实现)。

第 7 章: 索引检查

biz_project_plan

Key 用途覆盖
PRIMARY plan_id SELECT BY ID/WHERE p.plan_id = #{planId} (详情)
idx_plan_status status 状态筛选
idx_plan_project_no project_no 项目编号筛选(Manager 端用)
idx_plan_submitter submitter_id doctor 角色数据隔离强过滤 (P0 关键索引)

未建索引列 (但有 WHERE 过滤):

  • plan_direction_id - 有 LEFT JOIN + 等值过滤,应有索引 (大表时拖慢) 📌
  • plan_category - 等值过滤,应有索引
  • project_form - 即使 mapper 没过滤 (§9.1),前端 UI 也有这筛选项,若补上 SQL 应同时建索引
  • remark - 等值匹配 (说明里写 LIKE 更好,但当前 =),小数据量暂可接受

当前规模: 表只有 2 条数据,所有索引都不影响性能。但未来预估 doctor 投稿会增长,idx_plan_submitter 是 P0 关键 (doctor 每打开页面都强制走这个),已建

biz_special_plan (join 目标表)

Key 用途覆盖
PRIMARY id JOIN ON
idx_special_plan_status (status, sort_order) options 端 status='0' 过滤 + 排序
idx_special_plan_create_time create_time (管理后台 list 用)

JOIN 列 id 是 PK,无需额外索引。


第 8 章: 与原型差异

8.1 实现新增 (原型没有)

  1. "项目类别"筛选 + 列 —— 原型 columns 只有 7 列 (复选框/名称/学科方向/形式/设计文件/状态/备注/操作 = 8 列含复选框),实现加了"项目类别"列 + 筛选下拉,使用字典 biz_project_category 填充。 增强。
  2. "形式"升级为下拉 —— 原型是 input "形式",实现是 el-select 4 项。原 HTML 注释已写"原型无,用户确认保留"。
  3. 设计文件列简化 —— 原型是"查看"+"下载"双链,实现合并为一个 el-link "下载"。 简化合理。
  4. 状态列使用 Tag 组件 —— 原型纯文本"未结题/已结题",实现用 <audit-status-tag> 渲染彩色徽章。 升级。
  5. submitter_id 数据隔离 —— 原型无,实现给 doctor 角色强制过滤自己投的稿。 安全。
  6. 备注列扩展到所有行 —— 原型 8 行样例中有几行备注为空,实现统一展示 prop="remark"
  7. 分页器 —— 原型无 el-pagination,实现加了 (10/20/50,prev/next/jumper)。 工程化补全。
  8. 响应式 @media 768px (原型有,实现继承)

8.2 原型有但实现缺失/降级

无重大缺失。原型第 419 行的红字提示"*待审核和审核通过后,操作只显示查看按钮,无法进行修改"实现通过 canModify 隐式表达,用户不容易感知。

8.3 文字 / 标签差异

位置 原型 实现 差异原因
表头 学科方向 项目方向 跟项目内统一术语 (manager/Plans.vue 用"项目方向")
表头 (无) 项目类别 字典扩展
状态原型文案 未结题/已结题 待提交/待审核/通过/拒绝 原型状态枚举与项目不一致 (见 §1.3)
第 1 行红字提示 "*点击查看可进行预览" 未实现 详见 §10 P2-2
表后红字提示 "*待审核和审核通过后,操作只显示查看按钮,无法进行修改" 未实现 详见 §10 P2-2

8.4 总结

整体方向:扩展 + 升级 (无原型有但实现缺失的核心功能)。 实现严格遵循"7 列 → 加 1 列项目类别"的扩展模式,且安全控制 (submitter_id 强隔离) 远超原型。


第 9 章: 表字段冗余 / 设计问题

9.1 Mapper 字段过滤 vs 前端字段不一致 ⚠️

前端 filter-form 有 6 个筛选项 (planName/planDirectionId/planCategory/projectForm/status/remark),但 BizProjectPlanMapper.xml:40-54selectList 只支持 5 个条件,缺 2 个:

  • planName (前端有,mapper 没有)
  • projectForm (前端有,mapper 没有)

影响: 用户输入"小牛血清"想搜投稿名,前端发了 planName=小牛血清,后端 mapper 没处理,SQL 无此条件,查不到结果,以为是空数据。P0。

9.2 Entity 字段类型与 DDL 不一致

实体字段 实体类型 DDL 风险
planId String bigint 高 - SnowflakeId 返回 Long,setPlanId(String) 需 toString(),Mapper updateByPrimaryKey 的 WHERE plan_id=#{planId} 走隐式转换
auditTime String datetime 中 - JSON 反序列化丢失时间格式

建议: 统一为 Long/Date,避免隐式转换。P2。

9.3 Entity 残留字段 isFinished

Entity 有 isFinished,DDL 没有此列。BizProjectPlanService 接口全无 getIsFinished()/setIsFinished(),前端 Submissions.vue 不引用。纯遗留,建议清掉。P3。

9.4 Dictionary value-field=label 与全局不一致

项目默认 RuoYi 字典 value-field=dict_value,DictSelect 组件在 Submissions.vue:13 强行写 value-field="label",这意味着插入数据是中文 label。但 DDL plan_category varchar(50) 可以存,只是国际化场景下会破坏 (例如以后加英文 category)。审查所有 manager 端是否同步? 若不一致,会有数据脏。 P2。

9.5 biz_project_plan 没有软删除字段

DDL 无 del_flag,Mapper deleteByPrimaryKeys 是硬删除。doctor 角色无删除按钮 (前端未实现),但接口 DELETE /business/projectPlan/{ids} 可被 admin/manager 任意删。验收策略需明确。P2。

9.6 role_type 多源真相风险 (项目级)

根据 sys-user-role-type-truth memory,本项目单一可信源是 sys_user.role_type,biz_user_role_bind 是废表别动。本页面 Controller.list:32 getRoleType() 直接读 sys_user,符合

9.7 唯一约束

biz_project_plan.plan_id 是 PK,无其他唯一键。可考虑加 uk_plan_submitter_name(plan_name, submitter_id) 防同人重名 (业务待定)。


第 10 章: 待修复列表

# 问题 文件 修复建议 严重度
P0-1 筛选项 planNameprojectForm 在前端 filter-form 有绑定,但 BizProjectPlanMapper.xml:40-54 selectList 没有 <if test="planName"><if test="projectForm"> 条件,导致输入后查不到数据 (沉默失效) BizProjectPlanMapper.xml:40-54 + Submissions.vue:6,16 ① mapper 加 <if test="planName != null and planName != ''">and p.plan_name LIKE concat('%', #{planName}, '%')</if>;② 同时加 <if test="projectForm != null and projectForm != ''">and p.project_form = #{projectForm}</if> P0
P0-2 remark filter 用 = 等值匹配 (mapper:51),语义上应是 LIKE 模糊搜索,否则用户必须输完整备注才能命中 BizProjectPlanMapper.xml:51 =LIKE concat('%', #{remark}, '%') P0
P1-1 DDL 注释 status 缺少 0 待提交 枚举,但代码和前端都用 0,且 DDL DEFAULT '1' 与 controller.add:53-55 的 doctor 角色强制 setStatus('0') 配合有歧义 biz_project_plan DDL COMMENT ALTER TABLE 修改 COMMENT 为 状态 BizAuditStatusEnum: 0待提交 1待审核 2通过 3拒绝 P1
P1-2 当前 DB 无 status=0/status=3 数据,canModify/canSubmit 逻辑未真实跑过。需在测试环境插入 0/3 各一条做回归 测试数据 INSERT (status='0'/'3') 用于测试 P1
P1-3 remark filter 用 = 等值 (见 P0-2),且前端表头列也是 prop="remark",若数据库内 remark 是长文本,搜索 UX 差。planName/remark 未来都建议 LIKE (合并 P0-2) (合并)
P2-1 auditTime 实体 String vs DDL datetime 类型不一致,JSON 序列化时区可能错乱 BizProjectPlan.java:67 + DDL audit_time 统一 Date + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") P2
P2-2 原型红字提示"*点击查看可进行预览" 和 "*待审核和审核通过后,操作只显示查看按钮,无法进行修改" 实现未保留 Submissions.vue 在表格下方新增 2 行 .hint-text 红字,跟原型风格一致 P2
P2-3 字典 biz_project_categoryvalue-field=label (中文 label 进 DDL),国际化将破。需全项目排查是否一致 DictSelect 调用方 评审全项目 DictSelect 用法,统一改 value-field=dict_value,前端 label 同时展示 P2
P2-4 biz_project_plan DDL 无 del_flag,Mapper 硬删除;应加 del_flag 默认 '0',软删除 DDL + Mapper ALTER TABLE ADD del_flag char(1) DEFAULT '0';Mapper deleteByPrimaryKey 改 UPDATE del_flag='1' P2
P3-1 Entity isFinished 字段无对应 DDL 列,纯遗留 BizProjectPlan.java:46-47,96-97 删掉 getIsFinished/setIsFinished/getter+setter P3
P3-2 planId 实体 String 与 DDL bigint 类型不一致,Setter 隐式转换风险 BizProjectPlan.java:13,72-73 改 Long 类型,同步 SnowflakeId.injectIfEmpty P3
P3-3 biz_special_plan.status='0' 表示启用 (跟项目内其它表 del_flag 语义混) 文档/常量 加 BizPlanStatusEnum 常量类注释 P3
P3-4 plan_direction_id / plan_category / project_form 缺索引,数据量 <10k 暂可接受,>10k 后会有 performance 影响 DDL + Mapper ALTER TABLE 加 idx_plan_direction_id / idx_plan_category / idx_plan_form P3

第 11 章: 引用清单

类型 文件 行号 备注
菜单 ry-vue3/src/layout/AdminLayout.vue 107-114 doctor 菜单 6 项
路由 ry-vue3/src/router/index.js 88-100 /doctor children
组件 ry-vue3/src/views/doctor/Submissions.vue 审查目标
新建 ry-vue3/src/views/doctor/SubmissionNew.vue (未读,但路由引用)
详情 ry-vue3/src/views/doctor/SubmissionDetail.vue (未读,但路由引用)
前端 API ry-vue3/src/api/public.js 32-37, 90-94 bizList/bizGet/bizAdd/bizUpdate/bizDelete 通用入口
Controller ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizProjectPlanController.java 全部接口
Service ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizProjectPlanServiceImpl.java 薄壳
Entity ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizProjectPlan.java 见 §6.3
Mapper ry-api/ruoyi-business/src/main/resources/mapper/business/BizProjectPlanMapper.xml 24-54 关键 selectList
Join Controller ry-api/ruoyi-business/src/main/java/com/.../controller/BizSpecialPlanController.java 40-53 /options 接口
数据源 ry-api/ruoyi-admin/src/main/resources/application-druid.yml 7-15 mysql root@127.0.0.1:3306/guoju0808
原型入口 proto/html/doctor.html 208 data-page=components/submissions.html
原型 proto/html/components/submissions.html 484 行 HTML/CSS
DB 实测 biz_project_plan §6.1 2 行数据,status 仅有 1/2
DB 实测 biz_special_plan §6.2 7 行,AUTO_INCREMENT=8
DB 实测 sys_dict_data dict_type=biz_project_category §6.2 7 条字典

附录: skill 改进反馈 (来自本次审查)

用户反馈:"从 druid yml 中获取。把获取数据连接的方法写到 skill 中。"

改进内容: 在 page-tech-review skill 中增加 §0.5 节"DB 连接获取",固化从 ry-api/ruoyi-admin/src/main/resources/application-druid.ymlspring.datasource.druid.master.url/username/password 的步骤,避免每次询问用户。

# 提取 DB 连接 (单行命令)
grep -E "url:|username:|password:" ry-api/ruoyi-admin/src/main/resources/application-druid.yml
# → 拿到 jdbc:mysql://127.0.0.1:3306/guoju0808? ... / root / cu2oh2co3
# 实测 DDL 用:
mysql -h 127.0.0.1 -u root -pcu2oh2co3 guoju0808 -e "SHOW CREATE TABLE biz_project_plan \G"

(本次审查已落地使用,见 §6.1)