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>
27 KiB
doctor/Submissions 端到端技术审查
审查日期: 2026-08-18
目标页面: ry-vue3/src/views/doctor/Submissions.vue (我的项目设计投稿)
审查方法: 前端 → 后端 → 数据库 → 原型 四层穿透
审查范围: AdminLayout 菜单 → router 路由 → Vue 组件 → BizProjectPlanController → BizProjectPlanMapper.xml → biz_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 发现 (筛选失效): planName 和 projectForm 两个筛选项前端都有,但 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=1x 1,status=2x 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=1x1,status=2x1 (没有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实体Stringvs 表bigint—— MyBatis 自动转换目前没报错 (实测 list 接口 OK),但 SnowflakeId 返回Long,Entity setPlanId(String) 隐式 toString 后插入,所以 DB 现在实际存的是bigint但表达式是String。建议统一为Long。 - ⚠️
auditTime实体Stringvs 表datetime—— 实际 PHP/MyBatis 转换可读出,但前端拿到的时间字符串可能不是yyyy-MM-dd HH:mm:ss而是 ISO。建议统一Date + @JsonFormat。 - ❌
isFinishedEntity 字段无对应 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 实现新增 (原型没有)
- "项目类别"筛选 + 列 —— 原型 columns 只有 7 列 (复选框/名称/学科方向/形式/设计文件/状态/备注/操作 = 8 列含复选框),实现加了"项目类别"列 + 筛选下拉,使用字典
biz_project_category填充。 ➕ 增强。 - "形式"升级为下拉 —— 原型是 input "形式",实现是 el-select 4 项。原 HTML 注释已写"原型无,用户确认保留"。 ✅
- 设计文件列简化 —— 原型是"查看"+"下载"双链,实现合并为一个 el-link "下载"。 ➕ 简化合理。
- 状态列使用 Tag 组件 —— 原型纯文本"未结题/已结题",实现用
<audit-status-tag>渲染彩色徽章。 ➕ 升级。 - submitter_id 数据隔离 —— 原型无,实现给 doctor 角色强制过滤自己投的稿。 ➕ 安全。
- 备注列扩展到所有行 —— 原型 8 行样例中有几行备注为空,实现统一展示
prop="remark"。 ✅ - 分页器 —— 原型无 el-pagination,实现加了 (10/20/50,prev/next/jumper)。 ➕ 工程化补全。
- 响应式 @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-54 的 selectList 只支持 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 | 筛选项 planName 和 projectForm 在前端 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_category 用 value-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.yml 读 spring.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)