Files
guoju0808/_self/sponsor_people.md
T
郭庆泰 5a0a892574 feat: 邀请函邮件发送 + 首次设密/密码为空 + 姓名单一可信源
邀请发送 (菜单暂隐藏, 待确认参数见 发送邀请-待确认参数.md):
- 后端: BizInvite / BizInviteRecipient + Controller/Service/Mapper/XML
- 邮件: InviteMailSender (spring-boot-starter-mail SMTP) + application*.yml 邮件配置
- 上传进度: UploadProgressRegistry + UploadProgressController
- 前端: InviteList / InviteNew / InviteDetail / InviteView + api/business/invite.js
- 原型: proto/html/components/invite-detail / new-invitation / send-invitation

登录/账号:
- 首次设密: /getInfo 返回 isPasswordEmpty, SysProfileController 密码为空时跳过旧密码校验, ForcePasswordDialog 强制弹窗
- 姓名单一可信源 resolveDisplayName: doctor→biz_expert.name, sponsor/executor→biz_person.name, 其余回退 nick_name
- OA compliance 门禁改为按手机号查 ecology 视图 (不再限定 manager/leader)

其它:
- OSS zip 在线查看 (列清单+取单文件, 公开只读) + SecurityConfig permitAll
- doctor 项目详情 ProjectDetail.vue
- 数据库/测试/设计文档 (md) 入库
2026-09-10 20:40:49 +08:00

25 KiB

Sponsor 端 人员管理 (SponsorPeople.vue) 完整审查 (v2 修订)

入口: ry-vue3/src/views/sponsor/SponsorPeople.vue 数据源: guoju0808 (MySQL, 详见 application-druid.yml) 数据连接: jdbc:mysql://127.0.0.1:3306/guoju0808 (root / cu2oh2co3) 前端请求: GET /business/person/sponsorList (前端 → BizPersonController.sponsorList) 后端 mapper: BizPersonMapper.selectSponsorList (走 BizPersonMapper.xml)

路由定位 (由 AdminLayout 菜单而来, 非按名字猜):

  • sponsor 角色菜单 src/layout/AdminLayout.vue 第 123-129 行 MENU.sponsor: { path: '/sponsor/home', title: '首页' } → { path: '/sponsor/people', title: '人员管理' }
  • 路由表 src/router/index.js 第 119 行: { path: 'people', name: 'sponsor-people', component: () => import('@/views/sponsor/SponsorPeople.vue') }
  • 同菜单路径在原型 proto/html/sponsor.html 第 40 行 <li><a href="#" data-page="components/sponsor-people.html" class="menu-link">人员管理</a></li>
  • 原型图: proto/html/components/sponsor-people.html

1. 本页面的主要功能是什么? 哪些人能看到本页面?

1.1 主要功能

sponsor 端"人员管理"页面是支持方 (赞助方) 主账号对其团队子账号的 CRUD 管理台, 具体包括:

功能 描述 实现入口
查看团队成员 列出当前 sponsor 主账号下所有子账号人员 (含 MAIN 主账号 + SUB 子账号) 表格 el-table + 列表接口 GET /business/person/sponsorList
筛选查询 按姓名/手机号/工作单位/部门/状态 等条件过滤列表 顶部 filter-form
新建人员 创建新的子账号, 同步生成 sys_user 登录账号 (用户名=手机号, 默认密码 123456) 工具栏 "新建人员" → views/sponsor/NewPerson.vue
批量导入 通过 Excel 模板一次性导入多个人员, 每行自动创建 sys_user 子账号 + biz_person 业务记录 工具栏 "批量导入" → 导入 dialog → POST /business/person/sponsorImport
批量删除 勾选多行后批量逻辑删除 (联动软删 sys_user.del_flag='1') 工具栏 "批量删除"
编辑人员 修改业务字段 (姓名/手机号/部门/职务/角色/工作单位), 同步更新 sys_user.nick_name / phonenumber / status 行内 "编辑" 按钮 → NewPerson.vue 编辑模式 → PUT /business/person
查看人员详情 只读查看人员完整信息 行内 "查看" 按钮 → PersonDetail.vue
禁用/恢复账号 切换账号启停状态, 同步 sys_user.status ('0'/'1'), 禁用的账号无法登录 行内 "禁用/恢复" 按钮 → PUT /business/person
删除人员 单条逻辑删除, 联动 sys_user.del_flag='1' 行内 "删除" 按钮 → DELETE /business/person/{personId}

1.2 哪些人能看到本页面

仅"赞助方 (sponsor) 主账号"可见, 且需满足以下全部条件:

条件 来源 不满足时的行为
① 已登录用户角色为 sponsor 路由 meta: { role: 'sponsor' } (router/index.js:114); 路由守卫校验 role 字段 (见 permission.js) 跳到登录页或角色对应首页
② 是主账号 (非子账号) 菜单项 requireMain: true (AdminLayout.vue:127), 由 store.isMain 判定 子账号看不到此菜单项
③ sys_user.role_type = 'sponsor' 后端 /sponsorList 接口强制 p.unit_type='sponsor' AND u.parent_user_id=当前主账号 隔离 即使绕前端, 后端也会返回空

简而言之:

赞助方主账号 (sponsor + MAIN + role_type='sponsor')
  └─ 可以管理自己团队下的所有子账号人员 (SUB, parent_user_id=自己)
       └─ 创建/编辑/删除/启停

不能看到本页面的人群:

角色 看到的对应页面
管理员 (admin) /admin/sponsor-people (views/admin/SponsorPeople.vue, 走公共 /list 接口, 不限主账号)
合规人员 (manager) /manager/sponsor-people (views/manager/SponsorPeople.vue)
执行方主账号 (executor + MAIN) /executor/people (views/executor/People.vue, 走 /executorList 接口, unit_type='executor')
执行方子账号 (executor + SUB) 同主账号菜单, 但菜单项 requireMain: true 会隐藏
评审专家 (doctor) 看不到此菜单
项目负责人 (leader) 看不到此菜单
匿名未登录用户 看不到 (侧栏菜单不渲染)

数据隔离机制 (后端强制, 前端绕不开):

  1. BizPersonController.sponsorList() 第 73-81 行: bizPerson.getParams().put("sponsorOwnerUid", mainUid), 把当前登录主账号 user_id 注入 mapper 参数
  2. BizPersonMapper.xml selectSponsorList 第 152-153 行: p.unit_type='sponsor' AND u.parent_user_id = #{params.sponsorOwnerUid} — 三层过滤 (del_flag='0' + unit_type='sponsor' + parent_user_id 匹配)
  3. 即使前端篡改请求参数, 这两条硬编码 SQL 条件无法绕过, 只能看到自己团队的数据

2. 筛选项 (filter-form)

UI label 控件 绑定字段 q.* 后端 SQL (BizPersonMapper.xml) 命中表.字段 选项来源 (若为下拉) 原型对照
姓名 el-input q.name p.name like concat('%', #{name}, '%') (模糊匹配) biz_person.name 一致
手机号 el-input q.phone p.phone = #{phone} (精确匹配) biz_person.phone 一致
工作单位 el-input q.orgName o.org_name like concat('%', #{orgName}, '%') (模糊) biz_org.org_name (JOIN) 一致 (已修复)
部门 el-input q.department p.department = #{department} (精确匹配) biz_person.department 一致
状态 el-select q.status u.status = #{status} (0/1) sys_user.status 硬编码: label="正常" value="0" / label="禁用" value="1" ⚠️ 原型没有"状态"筛选
职务 — (无) ⚠️ 原型有"职务"筛选, 实现没有
查询 button (原型叫"查找")
重置 button ⚠️ 原型无重置按钮

2.1 工作单位筛选项 — 已修复 (2026-08-18)

修复: 在 mapper XML 的 3 个 SELECT 方法 (selectList / selectSponsorList / selectExecutorList) 的 <where> 块各加了一行:

<if test="orgName != null and orgName != ''"> and o.org_name like concat('%', #{orgName}, '%')</if>

原理:

  • biz_person.org_id 是 FK, JOIN biz_orgo.org_name
  • LIKE concat('%', ?, '%') 做模糊匹配, 适配用户输入部分公司名的场景
  • 索引命中: biz_org.idx_org_type_name (org_type, org_name) 复合索引 (前提是 unit_type 已固定)

实测验证 (mysql 直查):

查询 命中
无 orgName 过滤 (基线) 8 条 (含 4 条 org_id=NULL)
orgName LIKE '%20%' 1 条 (person_id=1463820510666752, org_name='20')
orgName LIKE '%sps%' 0 条 (没有 person 的 org_name 包含 sps)

2.2 历史数据 org_id=NULL 导致"工作单位"列空 — 已数据回填 (2026-08-18)

症状: 即使 orgName 筛选已生效, 部分 person 的"工作单位"列仍然为空 (例如 sponsor01 user_id=104 的 4 个子账号).

根因: 旧版导入/新建逻辑没有写 biz_person.org_id, 后来的 service 兜底 (BizPersonServiceImpl.java:56-70) 只对新数据生效, 历史 11 条 person 仍是 NULL, LEFT JOIN biz_org 拿不到 org_name.

修复: 通过 mysql inline 执行两步 UPDATE 回填 (按用户要求不保存 .sql 文件)

-- step 1: sub-account person 的 org_id = 主账号的 biz_org.org_id
UPDATE biz_person p
JOIN sys_user u ON u.user_id = p.user_id
JOIN biz_org o ON o.user_id = u.parent_user_id AND o.org_type = p.unit_type
SET p.org_id = o.org_id
WHERE p.org_id IS NULL AND u.parent_user_id IS NOT NULL;

-- step 2: 兜底, 孤儿数据 (user_id=NULL) 按 unit_type 匹配 biz_org 第一个
UPDATE biz_person p
JOIN (SELECT org_type, MIN(org_id) AS fallback_org_id FROM biz_org GROUP BY org_type) fo
  ON fo.org_type = p.unit_type
SET p.org_id = fo.fallback_org_id
WHERE p.org_id IS NULL AND p.unit_type IN ('sponsor', 'executor');

实际执行结果:

阶段 still_null
before 11
after step 1 8
after step 2 0

验证 (sponsor01 团队):

person_id name org_id org_name
1463464150351872 测试子账号 1 北京XXXXXXX公司
1463464238129152 张三 1 北京XXXXXXX公司
1463472990150656 张三 1 北京XXXXXXX公司
1463532487467008 张三 1 北京XXXXXXX公司

回滚: (无 backup 表, 因为不持久化) 如需回滚, 用 UPDATE biz_person SET org_id = NULL WHERE person_id IN (...) 单条执行.

2.3 姓名筛选项 — 已改为模糊匹配 (2026-08-18)

修复: 3 个 SELECT 的 p.name = #{name} 都改为 p.name like concat('%', #{name}, '%'). 支持用户输入部分姓名 (如"张"匹配"张三")。

实测验证 (mysql 直查):

查询 命中
name = '赵八' (旧精确) 2 条 (full match)
name LIKE '%赵%' (新模糊) 2 条 (赵八, 赵八)
name LIKE '%张三%' 2 条

3. 工具栏按钮 (toolbar)

按钮 功能 接口/Mapper 涉及表 原型对照
新建人员 路由跳转 /sponsor/people/newviews/sponsor/NewPerson.vue 一致
批量导入 打开 Excel 导入 dialog GET /business/person/sponsorImportTemplate (下载模板) / POST /business/person/sponsorImport (上传) sys_user + biz_person + biz_org (按 orgName 查) 一致
批量删除 按勾选 personIds 循环 bizDelete('person', r.personId) DELETE /business/person/{ids} sys_user.del_flag='1' (mapper deleteByPrimaryKey 联动软删) ⚠️ 原型无"批量删除"按钮

4. 表格 (el-table)

4.1 数据来源

后端 SQL (BizPersonMapper.selectSponsorList):

SELECT p.person_id, p.name, p.phone, p.org_id, o.org_name, o.org_type,
       p.department, p.position, p.role, p.unit_type, p.user_id,
       p.create_by, p.create_time, p.update_by, p.update_time,
       u.account_type, u.parent_user_id, u.status, u.del_flag
FROM biz_person p
LEFT JOIN biz_org o ON p.org_id = o.org_id
LEFT JOIN sys_user u ON p.user_id = u.user_id
WHERE u.del_flag = '0'
  AND p.unit_type = 'sponsor'                              -- SQL 硬编码
  AND u.parent_user_id = #{params.sponsorOwnerUid}         -- 当前登录主账号
  -- 以下均为可选条件 (前端 q.* 拼上来):
  [AND p.name = #{name}]
  [AND p.phone = #{phone}]
  [AND p.org_id = #{orgId}]                                -- 前端没传
  [AND p.department = #{department}]
  [AND p.position = #{position}]                           -- 前端没传 (无对应 input)
  [AND p.role = #{role}]                                   -- 前端没传 (无对应 input)
  [AND u.status = #{status}]
ORDER BY p.person_id DESC

强制隔离三层: u.del_flag='0' + p.unit_type='sponsor' + u.parent_user_id=当前主账号. INNER JOIN 天然剔除 user_id IS NULL 的游离 person.

4.2 每列对应的数据库字段

UI 列 row.* 取值 数据库表.字段 原型对照
复选框 (selection) row.personId biz_person.person_id
姓名 row.name biz_person.name
手机号 row.phone biz_person.phone
工作单位 row.orgName biz_org.org_name (LEFT JOIN)
部门 row.department biz_person.department
职务 row.position biz_person.position
角色 row.role biz_person.role (仅当 == 'supervisor' 显示"监察员") ⚠️ 原型无此列 (实现自行扩展)
账号类型 row.accountType sys_user.account_type (LEFT JOIN, MAIN=主账号 / SUB=子账号) ⚠️ 原型无此列 (实现自行扩展)
状态 row.status sys_user.status (LEFT JOIN, '0'=正常 / '1'=禁用) (原型显示"启用"/"禁用")
创建时间 row.createTime biz_person.create_time ⚠️ 原型无此列
操作 (4 按钮) 见 §5

5. 行内操作按钮 (每行)

按钮 触发函数 接口 后端动作 涉及表.字段 原型对照
查看 onView(row) /sponsor/people/detail/{personId} (PersonDetail.vue)
编辑 onEdit(row) /sponsor/people/edit/{personId} (NewPerson.vue 编辑模式) 后续走 PUT /business/person
禁用 / 恢复 onToggleStatus(row) PUT /business/person (bizUpdate('person', { personId, userId, status })) service updateByPrimaryKey: ① biz_personMapper.updateByPrimaryKey (XML 中 <update> 不含 status 子句, 故 biz_person 无 status 列可改) ② sysUserMapper.updateUser(u) 同步 status 到 sys_user sys_user.status
删除 onRemoveOne(row) DELETE /business/person/{personId} (bizDelete('person', personId)) bizPersonMapper.deleteByPrimaryKeyUPDATE sys_user u INNER JOIN biz_person p ... SET u.del_flag='1' WHERE p.person_id=... 联动软删 sys_user sys_user.del_flag ⚠️ 原型无"删除"操作

5.1 禁用/恢复的真实落点

  • BizPerson.status 在 Java 端是 transient (Java 注释: "非持久化字段")
  • mapper updateByPrimaryKey<trim prefix="SET"> 块不含 status 子句
  • 真正生效的是 service 里同步 sys_user.status (BizPersonServiceImpl.java:115-117)

6. Java Entity ↔ 数据库表 一致性 (实地校对 2026-08-18)

6.1 biz_org 真实 DDL (MySQL SHOW CREATE TABLE 实测)

CREATE TABLE `biz_org` (
  `org_id` bigint NOT NULL AUTO_INCREMENT,
  `org_name` varchar(200) NOT NULL,
  `org_type` varchar(20) NOT NULL DEFAULT 'sponsor' COMMENT 'sponsor支持方/execution执行方',
  `business_nature` varchar(20) DEFAULT NULL COMMENT '企业性质 私营/国营/中外合资/外资/其他',
  `address` varchar(500) DEFAULT NULL COMMENT '单位地址',
  `tax_no` varchar(50) DEFAULT NULL,
  `contact_name` varchar(50) DEFAULT NULL,
  `contact_phone` varchar(20) DEFAULT NULL,
  `intent_count` int DEFAULT '0' COMMENT '意向项目数',
  `status` varchar(20) DEFAULT '0' COMMENT '合作状态 合作中/待签约/已停用',
  `create_by` varchar(64) DEFAULT '',
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(64) DEFAULT '',
  `update_time` datetime DEFAULT NULL,
  `user_id` bigint DEFAULT NULL COMMENT '主账号sys_user.user_id',
  PRIMARY KEY (`org_id`),
  UNIQUE KEY `uk_org_user_type` (`user_id`,`org_type`),
  KEY `idx_org_type_status` (`org_type`,`status`),
  KEY `idx_org_type_name` (`org_type`,`org_name`),
  KEY `idx_org_user_type_name` (`user_id`,`org_type`,`org_name`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支持单位表'

修订: 上一版报告说"biz_org 不存在"是错的 — 实测 DB 中表存在, 索引齐全 (UK user_id+org_type, 三个复合索引都覆盖了 mapper 里的查询路径). 旧 dump 文件 ry0808_mysql_dump.sql 没包含此表是因为它是后来新增的 (dump 时间 2026-08-11, 表应是之后创建的).

6.2 biz_person 真实 DDL (MySQL SHOW CREATE TABLE 实测)

CREATE TABLE `biz_person` (
  `person_id` bigint NOT NULL DEFAULT '0' COMMENT '雪花ID',
  `user_id` bigint DEFAULT NULL COMMENT '关联系统用户ID',
  `name` varchar(50) NOT NULL,
  `phone` varchar(20) NOT NULL,
  `org_id` bigint DEFAULT NULL COMMENT '所属公司ID (FK: biz_org.org_id)',
  `department` varchar(100) DEFAULT NULL,
  `position` varchar(50) DEFAULT NULL,
  `role` varchar(20) DEFAULT NULL COMMENT '角色 项目负责人/会议执行/监察员/会务执行',
  `unit_type` varchar(20) DEFAULT 'execution' COMMENT 'execution执行/sponsor支持',
  `create_by` varchar(64) DEFAULT '',
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(64) DEFAULT '',
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`person_id`),
  UNIQUE KEY `uk_person_phone` (`phone`),
  UNIQUE KEY `uk_person_user_id` (`user_id`),
  KEY `idx_person_role` (`role`),
  KEY `idx_person_unit_type` (`unit_type`),
  KEY `idx_person_org_id` (`org_id`),
  KEY `idx_person_org_role` (`org_id`,`role`)
)

修订: 上一版"biz_person 有 work_unit / status 列"是错的 — DB 当前 没有 work_unit 列 (dump 旧, 后续删除了), 也没有 status 列 (status 唯一来源是 sys_user). Java 实体完全跟 DB 对得上, 没有列错位.

6.3 BizPerson.java ↔ biz_person 表 字段对照

实体字段 类型 表字段 类型 一致?
personId String person_id bigint
userId Long user_id bigint
name String name varchar(50)
phone String phone varchar(20)
orgId Long org_id bigint
orgName String (JOIN) biz_org.org_name (LEFT JOIN) 仅展示
orgType String (JOIN) biz_org.org_type (LEFT JOIN) 仅展示
department String department varchar(100)
position String position varchar(50)
role String role varchar(20)
status transient 表里无此列,真值在 sys_user.status 双源已修复 (DB 只剩 sys_user)
unitType String unit_type varchar(20)
createBy/Time, updateBy/Time 标准 同名
accountType String (JOIN) sys_user.account_type 仅展示
parentUserId Long (JOIN) sys_user.parent_user_id 仅展示
loginUsername / loginPassword transient 写到 sys_user.user_name / 加密 password 仅创建时用

7. 索引检查 (实测 SHOW INDEX FROM)

7.1 biz_person 现有索引 (7 个)

Key 用途覆盖
PRIMARY person_id
uk_person_phone phone (UNIQUE) 手机号精确查
uk_person_user_id user_id (UNIQUE) JOIN sys_user
idx_person_role role role 精确查
idx_person_unit_type unit_type sponsor/executor 隔离
idx_person_org_id org_id JOIN biz_org
idx_person_org_role org_id,role (复合) 复合过滤

结论: 没有缺失的关键索引. department / position / name 走精确匹配无索引但属于小数据, 暂可接受. 若以后按部门/职务建索引, 可新增 idx_person_department / idx_person_position.

7.2 biz_org 现有索引 (5 个)

Key 用途覆盖
PRIMARY org_id
uk_org_user_type user_id,org_type (UNIQUE) 一用户一类型一公司
idx_org_type_status org_type,status
idx_org_type_name org_type,org_name
idx_org_user_type_name user_id,org_type,org_name selectMyCompany

结论: 索引已覆盖 mapper 里所有查询路径, 无需新增.

7.3 sys_user 现有索引

  • PRIMARY KEY (user_id)
  • KEY idx_parent_user_id (parent_user_id)
  • ⚠️ status / account_type / role_type 仍无单独索引. 但 selectSponsorList 中的 u.status = ? 过滤在 LEFT JOIN 后命中行数本就少 (主账号的子账号不会很多), 暂可接受.

8. 与原型的差异 (实现偏离)

通过菜单定位到的原型 proto/html/components/sponsor-people.html, 跟 SponsorPeople.vue 实现相比:

8.1 实现新增 (原型没有)

# 实现 原型
1 "状态" 筛选下拉 (正常/禁用)
2 "重置" 按钮
3 "角色" 列 (只显示"监察员" tag)
4 "账号类型" 列 (主账号/子账号 tag)
5 "创建时间" 列
6 "删除" 行内按钮 无 (原型只有查看/编辑/禁用-恢复)
7 "批量删除" 工具栏按钮
8 分页 el-pagination (10/20/50, jumper) 原型不分页 (静态 5 行示例)
9 导入结果明细 dialog (table 展示每行 ok/fail) 原型只有 alert 提示

8.2 原型有但实现缺失

# 实现 原型
1 "职务" 筛选输入框 无 (后端 mapper 里有 p.position = ? 字段, 但前端无对应输入框) 有 (职务: [输入框])
2 红色提示 *新建时,管理员只能新建监察员
3 红色提示 *批量导入模板与列表表项一致 无 (工具栏)
4 红色提示 *点击禁用后,状态变为禁用,按钮变成恢复;禁用后,账号无法登录,提示请联系管理员 无 (表格下方)
5 导入 modal 内 取消 / 确定 按钮 只有"关闭"
6 导入 modal 内 红色提示 *批量导入模板xls与列表表项一致

8.3 文字 / 标签差异

位置 原型 实现
页面标题 (h1) 人员管理 breadcrumb 首页 / 人员管理 (无 h1)
状态 tag 文案 启用 / 禁用 正常 / 禁用
查询按钮 查找 查询

8.4 总结: 实现 vs 原型

  • 实现扩展性强 (新增角色/账号类型/创建时间/批量删除/分页), 超出原型范围, 是改进
  • 实现有 3 处原型有但缺失的红色提示和 1 个"职务"筛选 — 回退补漏
  • "工作单位" 筛选虽在原型有, 但实现里实际不生效 (后端未接 orgName), 这是 bug
  • 实现与原型在视觉布局上对齐 (page-card / breadcrumb / filter-form / toolbar / table 风格一致)

9. 表字段冗余 / 设计问题 (修订)

  1. biz_org.org_namebiz_support_unit.unit_name / biz_execution_unit.company_name: 三处并存表示"单位名". biz_org 重构后未清理旧表. 旧表 (biz_support_unit / biz_execution_unit / biz_service_org) 仍存在 DB, 应做一次审计决定是否 DROP.
  2. accountType / parentUserId / orgName / orgType 在 BizPerson 上 LEFT JOIN: 是只读展示字段, 但 mapper XML 把它们塞到 resultMap, 前端依赖. 若未来要分库 / 视图, 会很别扭. 可考虑抽出 BizPersonListVO 与实体分离.
  3. biz_person.user_id 唯一约束 (uk_person_user_id): 一个 sys_user 子账号最多对应一个 biz_person, 符合"一人一员"语义, 设计正确.
  4. biz_person.org_idbiz_org.user_id + org_type 的关系: 一个 biz_person 属于一个 org, org 通过 (user_id, org_type) 唯一确定其主账号. 主账号注册时一条 biz_org 记录, 子账号的 org_id 必须等于主账号的 org_id — 当前 service (BizPersonServiceImpl.java:54-70) 实现了兜底逻辑, 设计闭环.

10. 待修复列表 (按优先级, 修订版)

# 问题 文件 修复建议 严重度
1 "工作单位" 筛选不生效 (后端未接) BizPersonMapper.xml 3 处 SELECT 已修复 — 加 o.org_name like concat('%', #{orgName}, '%'), 实测通过 (见 §2.1) P0
1.5 历史数据 biz_person.org_id=NULL 导致"工作单位"列空 (inline mysql, 不落 .sql 文件) 已修复 — 数据回填 (11 → 0 NULL), 见 §2.2 P0
2 缺"职务"筛选输入框 SponsorPeople.vue filter-form <el-form-item label="职务"><el-input v-model="q.position"></el-input></el-form-item> P1
3 缺原型 3 处红色提示 SponsorPeople.vue toolbar / 表格下 / import modal 按原型补 *新建时,管理员只能新建监察员 P2
4 导入 modal 缺"取消/确定"按钮 SponsorPeople.vue import dialog 按原型补按钮 (当前只有"关闭") P2
5 状态文案与原型不一致 (启用 vs 正常) SponsorPeople.vue 与产品对齐: 用 正常 还是 启用 P3
6 旧表 (biz_support_unit / biz_execution_unit / biz_service_org) 与 biz_org 共存 DB DDL 审计后 DROP 旧表 (确认无引用) P3

11. 引用清单

  • 前端: ry-vue3/src/views/sponsor/SponsorPeople.vue
  • 前端 API: ry-vue3/src/api/business/person.js (listSponsorPerson)
  • 路由: ry-vue3/src/router/index.js 第 113-124 行 (sponsor 路由块)
  • 菜单: ry-vue3/src/layout/AdminLayout.vue 第 123-129 行 (sponsor MENU)
  • Controller: ry-api/ruoyi-business/.../controller/BizPersonController.java (/sponsorList, /sponsorImport, /sponsorImportTemplate)
  • Service: ry-api/ruoyi-business/.../service/impl/BizPersonServiceImpl.java
  • Mapper 接口: ry-api/ruoyi-business/.../mapper/BizPersonMapper.java
  • Mapper XML: ry-api/ruoyi-business/src/main/resources/mapper/business/BizPersonMapper.xml
  • 实体: BizPerson.java, BizOrg.java, BizPersonImportVO.java
  • 数据库配置: ry-api/ruoyi-admin/src/main/resources/application-druid.yml
  • 库结构 (旧,仅供参考): ry0808_mysql_dump.sql (与现行 DB 存在差异 — biz_org / biz_person 部分字段未在此 dump)
  • 原型菜单入口: proto/html/sponsor.html (sponsor 角色工作台布局)
  • 原型组件: proto/html/components/sponsor-people.html本次对比基准