Files
guoju0808/_self/executor_people.md
郭庆泰andClaude e59b512e4b fix(person): 子账号继承主账号 role_type + Login 不再硬编 leader
问题: sponsor 主账号新建的子账号 role_type 没写入,
      被 sys_user.role_type DEFAULT 'executor' 覆盖,
      导致子账号进入 executor 视角;另外 Login.vue
      autoRole/短信登录都硬编 'leader' 兜底, getInfo
      失败时静默跳 /leader/home。

修复:
- BizPersonServiceImpl.insert 读主账号 SysUser,
  把 mainUser.getRoleType() 写到新子账号
  (SysUserMapper.xml insertUser 已支持 role_type 列)
- Login.vue: 短信登录 fallbackRole 改 '',
  autoRole 不匹配返回 '', afterLogin 无 role 跳 /login
  (不再 fallback /leader/home)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 21:35:51 +08:00

378 lines
22 KiB
Markdown

# executor / People — 端到端页面技术审查
> 审查日期: 2026-08-18
> 审查范围: 前端 → 后端 → 数据库 → 原型
> 审查目标: `ry-vue3/src/views/executor/People.vue`
---
## 0. 四跳定位
| 跳 | 文件 / 位置 | 命中 |
|---|---|---|
| ① 角色菜单 | `ry-vue3/src/layout/AdminLayout.vue:119` | `{ path: '/executor/people', title: '人员管理', icon: User, requireMain: true }` |
| ② 路由 | `ry-vue3/src/router/index.js:101-110` | 父路由 `meta: { role: 'executor' }` → 子路由 `{ path: 'people', name: 'executor-people', component: () => import('@/views/executor/People.vue'), meta: { title: '人员管理' } }` |
| ③ 组件 | `ry-vue3/src/views/executor/People.vue` | ✅ 审查目标 (1-171 行) |
| ④ 原型 | `proto/html/components/people.html` | `proto/html/executor.html:206``data-page="components/people.html"` |
**角色定义**: `AdminLayout.vue:65``executor: '执行人'`
---
## 1. 主要功能与可见性
### 1.1 主要功能
| 功能 | 前端入口 | 后端接口 | 数据表 |
|---|---|---|---|
| 列表 + 筛选 + 分页 | `loadList()` (onMounted) | `GET /business/person/executorList` | `biz_person` + LEFT JOIN `biz_org`, `sys_user` |
| 新建人员 | `goNew()` → 跳 `/executor/people/new` | (另页 `NewPerson.vue`) | `biz_person` + `sys_user` (新建 SUB 子账号) |
| 编辑人员 | `goEdit(row)` → 跳 `/executor/people/edit/:id` | `PUT /business/person` | `biz_person` + 同步 `sys_user` |
| 查看详情 | `onView(row)` (本地 dialog) | `GET /business/person/{id}` | 同列表 |
| 启用 / 禁用 | `onToggleStatus(row, action)` | `PUT /business/person` (传 `status` + `userId`) | `biz_person` (无字段,status transient) + 同步 `sys_user.status` |
| 批量导入 | `submitImport()` (dialog) | `POST /business/person/executorImport` (本页面**未调用**,UI 占位) | `biz_person` + `sys_user` |
### 1.2 可见性三层过滤
| 层 | 校验 | 行为 |
|---|---|---|
| 前端菜单 | `AdminLayout.vue:119` `requireMain: true` | **仅主账号可见** (子账号在菜单里看不到该入口) |
| 路由守卫 | `router/index.js:101` `meta.role = 'executor'` | 仅 executor 角色可进入 |
| 后端 SQL | `BizPersonMapper.xml:170-187` `selectExecutorList` | ✅ 硬编码 `p.unit_type = 'executor'` + `(u.parent_user_id = #{executorOwnerUid} OR u.user_id = #{executorOwnerUid})`,前端绕不开 |
**不能看到本页面的人群**:
- 其他角色映射到自己的同名页: `admin` / `manager``router/index.js:41-43, 79-81``executor-people` (实际是"全公司视角",看所有执行方的人员);`sponsor` 有自己的 `sponsor-people` (router:119)
- executor 子账号 — 菜单项 `requireMain: true`,前端就不渲染
**数据隔离机制** (核心):
```
-- BizPersonMapper.xml:170-187 (selectExecutorList)
where u.del_flag = '0'
and p.unit_type = 'executor' -- SQL 硬编码,前端改 unit_type 也无效
and (u.parent_user_id = #{params.executorOwnerUid} -- 主账号视角: 看到我的子账号人员
or u.user_id = #{params.executorOwnerUid}) -- 主账号自己也是 person,避免漏自己
```
⚠️ 注意 `executorOwnerUid``BizPersonController.java:81-89``getUserId()` 注入 — 后端取的是**当前登录用户**,前端无法注入他人 UID。
---
## 2. 筛选项 (filter-form)
| UI label | 控件 | 绑定字段 | 后端 SQL | 命中表.字段 | 选项来源 | 原型对照 |
|---|---|---|---|---|---|---|
| 姓名 | `el-input` | `q.name` | mapper.xml:177 `<if test="name != null and name != ''"> and p.name like concat('%', #{name}, '%')</if>` | `biz_person.name` (模糊) | 用户输入 | ✅ 原型有 |
| 手机号 | `el-input` | `q.phone` | mapper.xml:178 `and p.phone = #{phone}` | `biz_person.phone` (精确) | 用户输入 | ✅ |
| 所属公司 | `el-input` | `q.orgName` | mapper.xml:180 `and o.org_name like concat('%', #{orgName}, '%')</if>` | `biz_org.org_name` (模糊) | 用户输入 | ⚠️ 原型 label "工作单位",实现改成 "所属公司" |
| 部门 | `el-input` | `q.department` | mapper.xml:181 `and p.department = #{department}` | `biz_person.department` | 用户输入 | ✅ |
| 职务 | `el-input` | `q.position` | mapper.xml:182 `and p.position = #{position}` | `biz_person.position` | 用户输入 | ✅ |
**附注 — 角色 / 状态列**:
- 前端不展示"角色" / "状态"作为筛选项 (但表格里作为列展示),后端 SQL mapper.xml:183-184 提供 `<if>` 支持,但前端没传参
---
## 3. 工具栏按钮 (toolbar)
| 按钮 | 功能 | 接口 | 涉及表 | 原型对照 |
|---|---|---|---|---|
| 查找 | 触发列表刷新 | 无 (本地) | — | ✅ |
| 重置 | 清空筛选 + 回 pageNum=1 | 无 (本地) | — | ⚠️ 原型无"重置"按钮 |
| 新建人员 | 跳独立页 `/executor/people/new` | (另页) | `biz_person` + `sys_user` | ✅ |
| 批量导入 | 弹 dialog (本页面 UI 占位,未实际上传) | (UI 占位) | — | ✅ (原型 alert,实现也 alert) |
⚠️ **P2**: `downloadTemplate` / `submitImport` 都只 `ElMessage.info` 占位,没有真实 Excel 下载 / 上传逻辑。BizPersonController.java:152-164 已实现 `/executorImportTemplate``/executorImport` 接口,但前端未对接。
---
## 4. 表格 (el-table)
### 4.1 数据来源
`BizPersonMapper.xml:170-187` (`selectExecutorList`):
```sql
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 as user_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 = 'executor'
and (u.parent_user_id = #{params.executorOwnerUid}
or u.user_id = #{params.executorOwnerUid})
[ and p.name like concat('%', #{name}, '%') ]
[ and p.phone = #{phone} ]
[ and p.org_id = #{orgId} ]
[ and o.org_name like concat('%', #{orgName}, '%') ]
[ and p.department = #{department} ]
[ and p.position = #{position} ]
[ and p.role = #{role} ]
[ and u.status = #{status} ]
order by p.person_id desc
```
### 4.2 列映射
| 列名 | `row.*` | DB 表.字段 | 原型对照 |
|---|---|---|---|
| 姓名 | `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` (`translatePersonRole` 转中文) | ✅ |
| 状态 | `row.status` | `sys_user.status` (LEFT JOIN) | ✅ |
| 操作 | — | — | ✅ |
**附注 — status 字段不在 biz_person**:
`BizPerson.java:51-53` 标注 `status` 字段为 `transient`:
```java
/** 启停状态 '0'/'1' (前端 toggle 用, BizPersonServiceImpl 同步到 sys_user.status) - 非持久化字段 */
@com.fasterxml.jackson.annotation.JsonProperty("status")
private transient String status;
```
所以表格里的 `row.status``sys_user.status` 通过 LEFT JOIN 进来,**不要尝试往 biz_person.status UPDATE**(不存在这列)。
---
## 5. 行内操作按钮
| 按钮 | 触发函数 | 接口 | 后端动作 | 涉及表.字段 | 原型对照 |
|---|---|---|---|---|---|
| 查看 | `onView(row)` | 无 (本地弹 dialog) | 无 | — | ✅ |
| 编辑 | `goEdit(row)` | 跳 `/executor/people/edit/${row.personId}` | `PUT /business/person` (另页) | `biz_person.*` + 同步 `sys_user.nick_name/phonenumber/status` | ✅ |
| 禁用 / 恢复 | `onToggleStatus(row, action)` | `PUT /business/person` (本页面直调) | `BizPersonServiceImpl.updateByPrimaryKey` (impl:106-118): 先 updateByPrimaryKey 改 biz_person 业务字段,**`if (entity.getUserId() != null)` 同步** sys_user.status | `sys_user.status` (NOT `biz_person.status`,后者不存在) | ✅ |
**保护逻辑** (People.vue:46-50):
```js
<template v-if="row.userId !== store.user?.userId">
<el-button v-if="(row.status || '正常') === '正常'" ... >禁用</el-button>
...
</template>
```
✅ 主账号的"本人"行不显示禁用按钮 — 防主账号把自己禁用造成锁定。但**没禁掉子账号主账号的(主账号不是 executor 自己,这里只针对本主账号自己)**。
**真实落点 — 同步到 sys_user**:
`BizPersonServiceImpl.java:106-118` (`updateByPrimaryKey`):
```java
int n = bizPersonMapper.updateByPrimaryKey(entity);
if (n > 0 && entity.getUserId() != null) {
SysUser u = new SysUser();
u.setUserId(entity.getUserId());
u.setNickName(entity.getName());
u.setPhonenumber(entity.getPhone());
u.setUpdateBy(SecurityUtils.getUsername());
if (entity.getStatus() != null) {
u.setStatus(entity.getStatus());
}
sysUserMapper.updateUser(u);
}
```
⚠️ **关键**: `entity.getUserId() == null` 时,**不会同步 sys_user.status**。前端 People.vue:144 已传 userId ✅。
---
## 6. Java Entity ↔ 数据库表 一致性
### 6.1 `biz_person` DDL (DB 实测 2026-08-18)
```sql
CREATE TABLE `biz_person` (
`person_id` bigint NOT NULL DEFAULT '0',
`user_id` bigint DEFAULT NULL,
`name` varchar(50) NOT NULL,
`phone` varchar(20) NOT NULL,
`org_id` bigint DEFAULT NULL,
`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`)
)
```
### 6.2 `biz_org` DDL (DB 实测)
```sql
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,
`address` varchar(500) DEFAULT NULL,
`tax_no` varchar(50) DEFAULT NULL,
`contact_name` varchar(50) DEFAULT NULL,
`contact_phone` varchar(20) DEFAULT NULL,
`intent_count` int DEFAULT '0',
`status` varchar(20) DEFAULT '0',
`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`)
)
```
### 6.3 `BizPerson` Entity ↔ `biz_person` 表
| 实体字段 | Java 类型 | 表字段 | 表类型 | 一致? | 备注 |
|---|---|---|---|---|---|
| personId | String | person_id | bigint | ✅ | **类型不一致**: Java String vs DB bigint — 但 MyBatis resultMap 兼容 (driver 自动转),⚠️ 性能上不建议 |
| name | String | name | varchar(50) NOT NULL | ✅ | |
| phone | String | phone | varchar(20) NOT NULL | ✅ | 有 uk_person_phone 唯一约束 |
| orgId | Long | org_id | bigint | ✅ | LEFT JOIN biz_org 时填 orgName/orgType |
| orgName | String | — (JOIN) | — | ✅ | 仅展示字段,resultMap 映射到 org_name |
| orgType | String | — (JOIN) | — | ✅ | 仅展示 |
| department | String | department | varchar(100) | ✅ | |
| position | String | position | varchar(50) | ✅ | |
| role | String | role | varchar(20) | ✅ | |
| unitType | String | unit_type | varchar(20) | ✅ | |
| userId | Long | user_id | bigint | ✅ | 有 uk_person_user_id 唯一约束 |
| status | String (transient) | — (不存在) | — | ✅ | 实际写 sys_user.status |
| accountType | String (JOIN) | — | — | ✅ | LEFT JOIN sys_user.account_type |
| parentUserId | Long (JOIN) | — | — | ✅ | LEFT JOIN sys_user.parent_user_id |
| loginUsername | String (transient) | — | — | ✅ | 创建 sys_user 子账号用 |
| loginPassword | String (transient) | — | — | ✅ | 创建后丢弃 |
| delFlag | String (return null) | — | — | ⚠️ | getter 永远返回 null — 软删走 sys_user.del_flag='1' |
### 6.4 修订:之前在 `_self/sponsor_people.md` 写过的"`work_unit` 已删除"假设
**实测 DB 后**: `biz_person` 表里**没有 `work_unit` 字段**,但**也没有 `biz_person.work_unit`** 这张废表;目前存"所属公司"的方式是 `biz_person.org_id` LEFT JOIN `biz_org`。所以现在的设计是**外键指向公司表**,比之前假设的"work_unit 字段"更规范 ✅。
---
## 7. 索引检查
### 7.1 `biz_person` (DB 实测)
| Key | 列 | 用途覆盖 | 评估 |
|---|---|---|---|
| PRIMARY | `person_id` | ✅ `updateByPrimaryKey WHERE person_id = ?`, `selectByPrimaryKey` | OK |
| uk_person_phone | `phone` (UNIQUE) | ✅ `insert` 唯一性,`SELECT WHERE phone = ?` 精确 | OK |
| uk_person_user_id | `user_id` (UNIQUE) | ✅ `insert` 唯一性,JOIN `sys_user` 关键 | OK |
| idx_person_role | `role` | ✅ `mapper:183 and p.role = #{role}`,`idx_person_org_role` 已覆盖 | OK (但有冗余) |
| idx_person_unit_type | `unit_type` | ✅ `selectList:49 and p.unit_type = #{unitType}`,`selectSponsorList`/`selectExecutorList` 硬编码 unit_type='sponsor'/'executor' | OK |
| idx_person_org_id | `org_id` | ✅ `selectByPrimaryKey` JOIN,`selectList:52 and p.org_id = #{orgId}` | OK |
| idx_person_org_role | `(org_id, role)` | ✅ `countAdminByOrgId` (impl 中可能) | OK |
### 7.2 `biz_org`
| Key | 列 | 用途覆盖 | 评估 |
|---|---|---|---|
| PRIMARY | `org_id` | ✅ JOIN | OK |
| uk_org_user_type | `(user_id, org_type)` | ✅ 主账号唯一公司 | OK |
| idx_org_type_status | `(org_type, status)` | OK | |
| idx_org_type_name | `(org_type, org_name)` | ✅ `selectList:53 LIKE '%org_name%'`**但 LIKE 前缀模糊会走全索引扫描**(若 % 开头,索引失效) | ⚠️ 暂可接受(org 数量小) |
| idx_org_user_type_name | `(user_id, org_type, org_name)` | OK | |
### 7.3 `sys_user` (已实测,见 `_self/sponsor_account.md` §7)
| Key | 列 | 评估 |
|---|---|---|
| PRIMARY | user_id | ✅ |
| idx_parent_user_id | parent_user_id | ✅ SELECT 子账号 |
| — | phonenumber | ❌ 缺(暂可接受,26 行) |
⚠️ `selectExecutorList` 的关键过滤 `(u.parent_user_id = X OR u.user_id = X)` 走 PRIMARY 主键索引 (`u.user_id = X`) + idx_parent_user_id,虽然 OR 走全索引扫描但小表 OK;破万后考虑重写为 UNION ALL。
---
## 8. 与原型差异 (`proto/html/components/people.html`)
### 8.1 实现新增 (原型没有)
- **分页** (`el-pagination`) — 原型 8 行静态,实现加分页支持
- **数据隔离** — 原型无账号概念,实现按主账号/子账号隔离
- **行内按钮"恢复"(启用)** — 原型叫"启用",实现叫"恢复";语义一致 ✅
- **本人禁用按钮隐藏** — 业务逻辑(防主账号自禁用),原型无
- **status 状态列颜色 tag** — 扩展
- **角色列 tag** — 原型是普通 span,实现是 el-tag + type
### 8.2 原型有但实现缺失
- **顶部 h1 "人员管理" + breadcrumb** — 实现只用 `class="page-sub"` 一行小字"管理执行方人员,分配会务执行角色",无 h1,无面包屑;**与项目其它页风格不一致**(其它页用 breadcrumb)
- **复选框列** — 原型表头第一列是 checkbox(全选/单选),实现完全删了。原型注释 (people.html:30) 自承"整合为整列",但实际实现把整列删了
- **红字提示** `*角色:执行方管理员新建人员时,默认只有会议执行的角色` — 原型 people.html:282-284 有提示卡片,实现无 ⚠️
### 8.3 文字 / 标签差异
| 项 | 原型 | 实现 | 评估 |
|---|---|---|---|
| 筛选项 label | "工作单位:" | "所属公司" | ⚠️ 改了语义,后端 SQL `LIKE %org_name%` 含义不变,但用户口径不一致 |
| 列表列名 | "工作单位" | "所属公司" | ⚠️ 同上 |
| 操作按钮文字 | "启用" | "恢复" | 轻微差异,语义等价 ✅ |
| 表头 | 无角色/状态标题 color | "角色"/"状态"独立列 | ✅ 改进 |
### 8.4 总结
- **方向**: 实现**做了减法 + 加法** — 删了复选框 / 标题 / 提示,加了分页 / 隔离 / 状态管理
- **缺失最严重的**: 顶部 h1 + breadcrumb(项目其它页都是这个风格);"工作单位"→"所属公司" 是有意的口径调整(后端表名 `biz_org.org_name`)
---
## 9. 表字段冗余 / 设计问题
| # | 问题 | 文件 | 说明 |
|---|---|---|---|
| 1 | `BizPerson.status` 字段是 `transient`,前端 toggle 后通过 service 同步到 `sys_user.status` | `BizPerson.java:51-53` + `BizPersonServiceImpl.java:106-118` | 字段不持久化,语义跨表,易出 bug — 但前端已规范传 `userId`,目前安全 ⚠️ |
| 2 | `BizPerson.delFlag` getter 永远返回 `null` | `BizPerson.java:84-86` | 设计意图是"软删走 sys_user",getter 留作兼容 — 但保留这个 getter 容易误导 |
| 3 | `BizPerson.personId` Java 类型是 `String`,DB 是 `bigint` | `BizPerson.java:13` vs DDL | MyBatis 自动转换 OK,但后续若有 `personId > 100` 这类比较会 ClassCastException ⚠️ |
| 4 | `biz_person.unit_type` 默认值 `'execution'`,但 executor 端 SQL 硬编码 `unit_type='executor'` | DDL vs mapper.xml:174 | DEFAULT 是历史值,新代码已用 'executor' 字符串;DEFAULT 未统一 |
| 5 | People.vue:144 调用 `bizUpdate('person', ...)``userId` 是**强依赖**,没传则 status 不更新 | `BizPersonServiceImpl.java:108` | 这是隐式契约,前端注释 (People.vue:142-143) 已标,但**没有任何后端校验**(传 null 会 silently noop) |
| 6 | `selectExecutorList` 包含 `u.status = ?` 过滤条件,前端未用,SQL 永远不过滤 | `BizPersonMapper.xml:184` | 是预留扩展 OK,但注意 status 列展示**不是用这个条件** — 展示来自 `select *`(全字段) |
| 7 | 删除走 `sys_user.del_flag='1'`,业务侧 biz_person 记录永远存在;若要"恢复"还得改 `del_flag='0'`,当前无"恢复已删人员" UI | `BizPersonMapper.xml:122-145` | 是设计选择,但产品流程上若有"误删恢复"需求会找不到入口 |
---
## 10. 待修复列表 (按优先级)
| # | 问题 | 文件 / 位置 | 修复建议 | 严重度 |
|---|---|---|---|---|
| 1 | 顶部缺 h1 + breadcrumb,与项目其它页风格不一致 | `People.vue:3` | 加 breadcrumb `首页 / 人员管理` + (可选) h1,跟 manager/executor/Overview 风格统一 | **P2** |
| 2 | 批量导入 dialog UI 占位,未对接 `/executorImport` 接口 | `People.vue:64-80, 154-158` | 调用 `POST /business/person/executorImport` + 模板下载 `GET /executorImportTemplate`;返回的 `errorRows` 渲染成明细 | **P2** |
| 3 | 缺原型红字提示 "*角色:执行方管理员新建人员时,默认只有会议执行的角色" | `People.vue:18-23` `batch-bar` | 在表格上方或表单区补一行提示 | **P2** |
| 4 | 原型有复选框列(全选/批量操作),实现完全删了 | `People.vue:26-53` | 如不需批量操作,可保留一行 checkbox 列+ disabled;若产品决定不做批量,需与原型对齐(在原型文件加 "调整说明" 备注) | **P2** |
| 5 | `biz_person.unit_type` 默认值 'execution' 与 executor SQL 'executor' 不一致 | DDL DEFAULT vs mapper.xml | 把 DDL DEFAULT 改 'executor' (或同步 sponsor SQL) | **P3** |
| 6 | `BizPerson.delFlag` getter 返回 null 是误导 | `BizPerson.java:84-86` | 移除 getter/setter 或改名 `getSysUserDelFlag()` | **P3** |
| 7 | `BizPerson.personId` 用 String 存 bigint | `BizPerson.java:13` | 改为 Long,前端 `personId` 转 Number | **P3** |
| 8 | serviceImpl updateByPrimaryKey 在 userId=null 时 silently noop status 同步 | `BizPersonServiceImpl.java:108` | 加日志 warn 或抛异常提示前端 | **P3** |
| 9 | "工作单位" → "所属公司" 改名是 SQL 含义,产品口径需统一 | 前端文案 | 与产品确认:对外叫"所属公司"还是"工作单位" | **P3** |
---
## 11. 引用清单
| 用途 | 文件 | 行号 |
|---|---|---|
| 角色菜单 | `ry-vue3/src/layout/AdminLayout.vue` | 65, 115-122 |
| 路由 | `ry-vue3/src/router/index.js` | 101-110 |
| 路由守卫 | `ry-vue3/src/router/index.js` | 134+ |
| 审查目标 | `ry-vue3/src/views/executor/People.vue` | 1-171 |
| 前端 API 封装 | `ry-vue3/src/api/business/person.js` | 24-31 (`listExecutorPerson`) |
| 后端 Controller | `ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizPersonController.java` | 39-62 (BUSINESS_MAIN_ROLES), 81-89 (executorList), 152-164 (executorImport) |
| 后端 Mapper XML | `ry-api/ruoyi-business/src/main/resources/mapper/business/BizPersonMapper.xml` | 4-22 (resultMap), 24-38 (selectFields), 105-120 (updateByPrimaryKey), 122-145 (deleteByPrimaryKey/Keys), 168-187 (selectExecutorList), 189-194 (countAdminByOrgId) |
| 后端 Service 实现 | `ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizPersonServiceImpl.java` | 96-98 (SysUser 构造), 106-118 (updateByPrimaryKey + sys_user 同步) |
| Entity | `ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizPerson.java` | 9-108 (字段定义 + setter/getter,行 51-53 status 是 transient) |
| DDL (DB 实测) | `mysql -h 127.0.0.1 -e 'USE guoju0808; SHOW CREATE TABLE biz_person\G'` | 2026-08-18 |
| DDL (DB 实测) | `mysql -h 127.0.0.1 -e 'USE guoju0808; SHOW CREATE TABLE biz_org\G'` | 2026-08-18 |
| 原型 | `proto/html/components/people.html` | 全文 434 行 (含详细注释) |
| 原型菜单链 | `proto/html/executor.html` | 206 (data-page="components/people.html") |
| 记忆 (角色真相) | memory/sys-user-role-type-truth.md | role_type 是单一可信源 |
| 记忆 (sponsor 同模式) | commit 50c2678 | executorList 加 executorList 兜底 (sponsor 同模式) |