Files
guoju0808/_self/manager_sponsor_orgs.md
郭庆泰 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

389 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# /manager/sponsor-orgs (合规 — 支持单位管理)
> 页面: `ry-vue3/src/views/manager/SponsorOrgs.vue` (120 行)
> 角色: 合规 (manager)
> 接口: `GET /business/org/list?orgType=sponsor&...`
> 数据表: `biz_org` (主), `sys_user` (people join)
---
## §0 组件定位 (四跳)
| 跳 | 位置 | 关键引用 |
|---|---|---|
| ① 菜单 | `AdminLayout.vue:101` | `MENU.manager` 第 6 项: `{ path: '/manager/sponsor-orgs', title: '支持单位管理', icon: Connection }` |
| ② 路由 | `router/index.js:74` | `{ path: 'sponsor-orgs', name: 'manager-sponsor-orgs', component: () => import('@/views/manager/SponsorOrgs.vue'), meta: { title: '支持单位管理' } }` |
| ③ 组件 | `ry-vue3/src/views/manager/SponsorOrgs.vue` | 任务审查目标 |
| ④ 原型 | `proto/html/components/support-unit-manage.html` | 父级 `manager.html:210` 菜单: `<a data-page="components/support-unit-manage.html" class="menu-link">支持单位管理</a>` |
> 注: 原型组件名 `support-unit-manage.html` ≠ 路由名 `sponsor-orgs`, 按"菜单→原型" 唯一对应规则锁定。
---
## §1 主要功能 + 可见性
### 1.1 主要功能 (manager 端查看-only)
| 功能 | 前端 | 后端 | 数据表 |
|---|---|---|---|
| 列表 + 筛选 | `SponsorOrgs.vue:30-54` | `GET /business/org/list` | `biz_org` |
| 详情查看 (dialog) | `SponsorOrgs.vue:56-74` | `GET /business/org/{orgId}` | `biz_org` |
| 跳人员管理 | `SponsorOrgs.vue:108-110` | 路由跳转, 不走 HTTP | — |
### 1.2 可见性 (三层过滤)
| 层 | 配置 | 校验 |
|---|---|---|
| 前端菜单 | `AdminLayout.vue:95-106` `MENU.manager` | 无 `requireMain` 标签, 也无 `store.isMain` 判断 — 只要 store.role==='manager' 即可见 |
| 路由守卫 | `router/index.js:74` `meta: { title: '支持单位管理' }` | **无 meta.role**, 不走路由权限拦截 (项目普遍现状) |
| 后端 SQL | `BizOrgMapper.xml:38` `<if test="orgType != null and orgType != ''"> and org_type = #{orgType} </if>` | 前端硬编码 `q.orgType='sponsor'` (SponsorOrgs.vue:85), 走 SQL 物理隔离 |
**不能看到的人群**:
- sponsor / executor / admin / leader / doctor 各自有独立菜单 (admin/SponsorOrgs.vue 是 admin 端全 CRUD, manager 端只读)
**数据隔离**: **前端硬编码 `orgType='sponsor'` + Mapper SQL `<if>` 拼接** — 任何调用 /business/org/list 的前端调用方都必须传 orgType, 否则查出所有公司。
---
## §2 筛选项 (filter-form)
| UI label | 控件 | 绑定字段 | 后端 SQL | 命中表.字段 | 选项来源 | 原型对照 |
|---|---|---|---|---|---|---|
| 公司名称 | `el-input` (clearable) | `q.orgName` | `BizOrgMapper.xml:39` `org_name like concat('%', #{orgName}, '%')` | `biz_org.org_name` | — | ✅ "单位名称" |
| 税号/统一社会信用代码 | `el-input` (clearable) | `q.taxNo` | `BizOrgMapper.xml:40` `tax_no = #{taxNo}` | `biz_org.tax_no` | — | ✅ "税号/统一社会信用代码" |
| 合作状态 | `el-select` (clearable) | `q.status` | `BizOrgMapper.xml:41` `status = #{status}` | `biz_org.status` | **写死** 2 个 option (0=正常, 1=禁用) | ✅ |
| 查找 / 重置 | `el-button` | — | — | — | — | ✅ "查找" |
**⚠️ 状态 filter 错乱** (SponsorOrgs.vue:19-23):
```vue
<el-option label="正常/合作中" value="0" /> <!-- 1 label 对应 2 个数据状态 -->
<el-option label="合作中" value="合作中" />
<el-option label="禁用" value="禁用" />
```
DB 实测 sponsor 端 status 分布:
```
status COUNT
0 10 ← 数字 (默认值)
合作中 1 ← 字符串
禁用 4 ← 字符串
正常 2 ← 字符串
```
**混合形态**: 历史数据用 "0"/"1" 数字, 新数据 (admin 端 dialog) 用 "正常"/"合作中"/"禁用" 字符串。filter 把 "0" 标 "正常/合作中" 是历史债务, 但业务上是合理的 (3 类用户能筛 3 类数据)。
**❌ 缺失的筛选项** (原型 line 272-277, 287-294):
- 创建日期 起止 (daterange) — 原型做出占位但实现无
- 单位地址 — 原型 line 287-290 单独占位
**❌ 控件宽度未设** (SponsorOrgs.vue:12, 15, 18): admin 端 (`SponsorOrgs.vue:13-19`) 分别 200px/220px/140px, manager 端没设 → 浏览器默认 100% 撑开, 视觉不一致。
---
## §3 工具栏
| 按钮 | 功能 | 接口 | 涉及表 | 原型对照 |
|---|---|---|---|---|
| (无) | — | — | — | 原型无工具栏 (line 256 完成 header 后直接进 filter) |
**实现新增强功能**: **操作列禁用/恢复** 在 admin 端 (`admin/SponsorOrgs.vue:57-59`) 有, manager 端**故意去掉** (查看-only 角色拆分, 跟 admin 端 CRUD 清晰分离)。✅ 业务合理。
---
## §4 表格 (el-table)
### 4.1 数据来源 SQL
```sql
SELECT org_id, user_id, org_name, org_type, business_nature, address, tax_no, status, create_time,
contact_name, contact_phone, intent_count,
create_by, update_by, update_time
FROM biz_org
WHERE 1=1
[AND user_id = #{userId}]
[AND org_type = #{orgType}] 前端硬编码 'sponsor'
[AND org_name LIKE concat('%', #{orgName}, '%')]
[AND tax_no = #{taxNo}]
[AND status = #{status}]
ORDER BY org_id DESC
```
### 4.2 表格列映射
| 列名 | `row.*` | DB 表.字段 | 原型对照 |
|---|---|---|---|
| # | (i+1) | — | ✅ 原型无 # 列, 实现新增 (item 1) |
| 公司名称 | `row.orgName` | `biz_org.org_name` | ✅ "单位名称" |
| 公司地址 | `row.address` | `biz_org.address` | ✅ "单位地址" |
| 税号/统一社会信用代码 | `row.taxNo` | `biz_org.tax_no` | ✅ "税号/统一社会信用代码" |
| 合作状态 | `row.status` (el-tag) | `biz_org.status` | ✅ "合作状态" |
| 创建日期 | `row.createTime` (slice 0,10) | `biz_org.create_time` | ✅ "创建日期" |
| 联系人 | `row.contactName` | `biz_org.contact_name` | ⚠️ 原型无 |
| 联系电话 | `row.contactPhone` | `biz_org.contact_phone` | ⚠️ 原型无 |
| 操作 | (el-button x2) | — | ✅ "人员管理"+"查看" |
**实现新增** (超出原型): # 序号、联系人、联系电话 — 是改进 (item 8.1)。
**⚠️ createTime 显示 (SponsorOrgs.vue:41)**:
```vue
{{ row.createTime ? String(row.createTime).slice(0,10) : '' }}
```
- 后端 `BizOrg.createTime``java.util.Date` (line 48), Jackson 默认序列化为 ISO 字符串 "2026-08-18T12:34:56" → slice 0,10 拿 → "2026-08-18" ✓
- 但 BizOrg 类**没有 `@JsonFormat` 注解** → 输出 ISO 8601, 不是 "yyyy-MM-dd HH:mm:ss"。admin 端 (line 47) 同样的 slice 写法。
- 风险: 如果后端 Spring 配置 `spring.jackson.date-format` 改成 `yyyy-MM-dd HH:mm:ss`, slice 0,10 拿到 "2026-08-18" 还是 OK; 但若配置空, 输出时间戳数字 `"1723965296000"`, slice 0,10 拿到 "1723965296" 就乱码。建议跟 §10 一起改用 `formatDate()` 工具。
---
## §5 行内操作
| 按钮 | 触发函数 | 接口 | 后端动作 | 涉及表.字段 | 原型对照 |
|---|---|---|---|---|---|
| 查看 | `view(row)` (line 107) | `GET /business/org/{orgId}` | `BizOrgController.getInfo` (Controller.java:62) → `bizOrgService.getById``bizOrgMapper.selectByPrimaryKey` | `biz_org` (整行) | ✅ "查看" |
| 人员管理 | `goPeople(row)` (line 108-110) | 路由跳转 | `router.push('/manager/sponsor-people?orgId=...&orgName=...')` | — | ✅ "人员管理" |
**manager 端没有的按钮** (admin 端独有, manager 端只读):
- 编辑 → admin 端 `onEdit(row)``bizUpdate('org', form)``BizOrgController.edit` (PUT) → `mapper.updateByPrimaryKey`
**manager 端已加的按钮** (合规角色该有禁用权限, 2026-08-19 补):
- 禁用/恢复 → `onToggleStatus(row)``toggleOrgStatus` helper → `PUT /business/org/toggleStatus``BizOrgServiceImpl.toggleStatus` 同步主账号 sys_user.status → 子账号 login 时被 4.5 节拦截
---
## §6 Java Entity ↔ 数据库表 一致性
### 6.1 biz_org DDL (实测, 2026-08-18)
```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', -- COMMENT 乱码
`business_nature` varchar(20) DEFAULT NULL COMMENT '私营/国营/...',
`address` varchar(500) DEFAULT NULL COMMENT 'λַ',
`tax_no` varchar(50) DEFAULT NULL COMMENT /ͳһ...',
`contact_name` varchar(50) DEFAULT NULL COMMENT 'ϵ',
`contact_phone` varchar(20) DEFAULT NULL COMMENT 'ϵ绰',
`intent_count` int DEFAULT '0' COMMENT '意向项目数',
`status` varchar(20) DEFAULT '0' COMMENT '合作状态 合作中/已签约/暂停',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`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=41 DEFAULT CHARSET=utf8mb4
COMMENT=ֵλ';
```
> ⚠️ 所有中文 COMMENT 在客户端显示乱码 (DB 字符集 utf8mb4_unicode_ci, 但客户端 mysql shell 默认 GBK)。结构本身无误。
### 6.2 Entity ↔ DB 字段对照
| 实体字段 | DB 中文列名 (推断) | 类型 | 表字段 | 类型 | 一致? |
|---|---|---|---|---|---|
| `orgId` | 主键 | `Long` | `org_id` | `bigint NOT NULL AUTO_INCREMENT` | ✅ |
| `userId` | 主账号 | `Long` | `user_id` | `bigint DEFAULT NULL` | ✅ |
| `orgName` | 单位名称 | `String` | `org_name` | `varchar(200) NOT NULL` | ✅ |
| `orgType` | 单位类型 | `String` | `org_type` | `varchar(20) NOT NULL DEFAULT 'sponsor'` | ✅ |
| `businessNature` | 企业性质 | `String` | `business_nature` | `varchar(20) DEFAULT NULL` | ✅ |
| `address` | 单位地址 | `String` | `address` | `varchar(500) DEFAULT NULL` | ✅ |
| `taxNo` | 税号 | `String` | `tax_no` | `varchar(50) DEFAULT NULL` | ✅ |
| `contactName` | 联系人 | `String` | `contact_name` | `varchar(50) DEFAULT NULL` | ✅ |
| `contactPhone` | 联系电话 | `String` | `contact_phone` | `varchar(20) DEFAULT NULL` | ✅ |
| `intentCount` | 意向项目数 | `Integer` | `intent_count` | `int DEFAULT 0` | ✅ |
| `status` | 合作状态 | `String` | `status` | `varchar(20) DEFAULT '0'` | ✅ |
| `createTime` | 创建时间 | `Date` | `create_time` | `datetime DEFAULT NULL` | ✅ |
| `updateTime` | 更新时间 | `Date` | `update_time` | `datetime DEFAULT NULL` | ✅ |
| `createBy` | 创建者 | `String` (BaseEntity) | `create_by` | `varchar(64) DEFAULT ''` | ✅ |
| `updateBy` | 更新者 | `String` (BaseEntity) | `update_by` | `varchar(64) DEFAULT ''` | ✅ |
**✅ 13 个字段 + 2 个 BaseEntity 字段 全部 mapper SELECT/resultMap 覆盖, 全部 getter/setter 完整 (不像 BizMeeting 漏 9 个)**。
### 6.3 修订说明
> 修订: v1 草稿说"BizOrg 缺 getter/setter", 实测对齐 ✓, 收回此说法。
---
## §7 索引检查
| Key | 列 | 类型 | 用途覆盖 |
|---|---|---|---|
| PRIMARY | `org_id` | UNIQUE BTREE | ✅ 主键查询 `WHERE org_id = ?` |
| `uk_org_user_type` | `(user_id, org_type)` | UNIQUE BTREE | ✅ "一用户一类型一公司" 强制; `selectMyCompany` 用 ✓ |
| `idx_org_type_status` | `(org_type, status)` | BTREE | ✅ 列表筛选 `WHERE org_type='sponsor' AND status=?` 命中 |
| `idx_org_type_name` | `(org_type, org_name)` | BTREE | ✅ 列表筛选 `WHERE org_type='sponsor' AND org_name LIKE ?` 命中 |
| `idx_org_user_type_name` | `(user_id, org_type, org_name)` | BTREE | ✅ `selectSponsorOrgOptions` / `selectExecutorOrgOptions` JOIN sys_user 命中 |
**结论**: 列表主查询路径 `WHERE org_type='sponsor' [AND org_name LIKE ?] [AND status=?]``idx_org_type_status``idx_org_type_name`, **无需新增索引**。当前数据量 17 行 sponsor, 性能完全 OK。
---
## §8 与原型差异 (实现偏离)
> 原型: `proto/html/components/support-unit-manage.html` (457 行)
### 8.1 实现新增 (超出原型)
| # | 项 | 说明 |
|---|---|---|
| 1 | 表格 # 序号列 | element-ui `type="index"` 自动生成, 视觉清爽 |
| 2 | 表格联系人 + 联系电话 | 详情 dialog 也有, 实际很有用 |
| 3 | el-dialog 详情 (descriptions 单列布局) | 原型是 dialog 跳转 `support-unit-view.html`, 实现是本页面 inline dialog |
| 4 | 详情里 "公司类型: 支持方" el-tag | 原型无 |
| 5 | 跳人员管理传 `orgId+orgName` | 原型是空链, 实现带 query 让 sponsor-people 页面回显 |
### 8.2 原型有但实现缺失
| # | 项 | 严重度 | 修复建议 |
|---|---|---|---|
| 1 | **筛选: 创建日期 起止** | P2 | 加 `startTime` `endTime` 字段, mapper 加 `<if>` |
| 2 | **筛选: 单位地址** | P2 | 加 `q.address`, mapper 加模糊匹配 |
| 3 | 面包屑 + page-title (大标题) | P3 | style-alignment 明确去 page-title, 跟其它页一致 ✓ |
| 4 | 底部红色提示 *点击禁用后, 该企业下所有账号则均不可登录* | P3 | manager 端无禁用按钮, 该提示放在 admin 端或 manager 详情 dialog |
### 8.3 文字 / 标签差异
| 项 | 原型 | 实现 | 评价 |
|---|---|---|---|
| 字段名 | "单位名称" / "单位地址" | "公司名称" / "公司地址" | ⚠️ 同义, 实现统一用 "公司" — 跟前述 `/sponsor/account` 一致, 可接受 |
| 状态值 | `active` / `disabled` (英文) | `正常` / `合作中` / `禁用` / `0` / `1` (混合) | ⚠️ DB 实业务字符串, 原型是 demo 数据 |
| 标题 | `支持单位管理` | `支持单位管理` | ✅ |
| 面包屑 | `工作台 > 支持单位管理` | `首页 / 支持单位管理` | ⚠️ 跟 style-alignment 统一规范一致, 牺牲原型还原 |
### 8.4 总结
- 方向: **扩展 + 减负** — 实现在原型基础上加了更细的字段 (联系人/电话), 减了不应有的功能 (manager 端无禁用按钮)
- 与 admin 端分工清晰: admin 端全 CRUD, manager 端只读 + 跳转
- 状态值混合 (数字 + 字符串) 是真业务历史问题, 不是 frontend bug
---
## §9 表字段冗余 / 设计问题
### 9.1 ❌ `biz_org` vs `biz_support_unit` 共存?
旧表 `biz_support_unit` / `biz_execution_unit` 已合并到 `biz_org`, 整个项目**不应该再有支持方/执行方单独表**。
- 检查 admin/SponsorOrgs.vue 引用 — 全部走 `biz_org`
- 检查 sponsor-people 父级 — 走 `biz_org.org_id`
- ✅ 合并完成, 无遗留
### 9.2 ❌ JOIN 字段 vs 持久化字段
`biz_org.intent_count` (承办方意向项目数) 是**持久化字段**还是 **JOIN 派生**?
- 看 mapper `selectList` 是直查 `intent_count`, 没有 JOIN 或子查询派生
- 但是 intent_count 不会自动更新 (没人写过 UPDATE biz_org SET intent_count = ... 的代码)
- 推测: 是各业务点 (创建/删除意向时) 手动维护, **存在不一致风险** — 数据库里 17 行 sponsor, 抽查几行 `intent_count` 值, 看是否有 < 5 的; 大概率是历史脏数据
### 9.3 ❌ 唯一约束 `uk_org_user_type` 语义
`UNIQUE (user_id, org_type)` 表示"一个 sys_user 在同一个 org_type 下只能有一家公司"。
- ✅ 业务合理: 一个用户不能同时是 2 家支持方
- ✅ 当前 17 行 sponsor / 16 行 executor 各归属独立 user_id
### 9.4 ✅ 业务闭环: 禁用/恢复同步主账号 (P1 已修复)
admin 端 `onToggleStatus``toggleOrgStatus(orgId, status)` → 后端 `BizOrgServiceImpl.toggleStatus` (`IBizOrgService.java:23-30` + `BizOrgServiceImpl.java:60-77`):
```java
@Transactional
public int toggleStatus(Long orgId, String newBizOrgStatus) {
BizOrg org = bizOrgMapper.selectByPrimaryKey(orgId);
// 1. UPDATE biz_org.status
// 2. 同步主账号 sys_user.status
// '禁用' → '1', '正常'/'合作中' → '0'
// 只联动主账号 (biz_org.user_id), 子账号不在此操作范围
}
```
**实测 login 流程** (`BizAuthController.java:140-157`):
```java
if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
return error("账号已被停用"); // 主账号自己
}
// 4.5 子账号专属: 校验主账号状态
if (user.getParentUserId() != null) {
SysUser parent = userMapper.selectUserById(user.getParentUserId());
if (UserStatus.DISABLE.getCode().equals(parent.getStatus())) {
return error("所属企业主账号已被停用, 请联系企业管理员"); // 子账号
}
}
```
**设计取舍**:
- 禁用时**只动主账号** (1 个 UPDATE), 不批量改子账号 sys_user.status
- 子账号由 login 时校验主账号状态拦截 (减少无效 UPDATE, 责任分离)
- 满足原型红字 "该企业下所有账号均不可登录" — 主账号停用时, 该 org 下所有子账号登录都被拦
**正例参考**: BizExpertService 已有 `toggleStatus` 模式 (`BizExpertService.java:83-90`), BizOrg 改造后同模式 ✓
### 9.4.1 ✅ 状态值整体收口 (2026-08-19 v3 落地)
**最终约定**: `biz_org.status` ⚠️ **`'0'` = 正常, `'1'` = 禁用** (纯数字, 无字符串)
**整改后的统一映射**:
| 位置 | 状态表达 |
|---|---|
| DB `biz_org.status` | `'0'` / `'1'` |
| 前端 dropdown value | `'0'` / `'1'` |
| 前端 `statusTagType` | `s === '1' ? 'danger' : 'success'` |
| 前端 `isDisabled` | `row.status === '1'` |
| 前端 `onToggleStatus` payload | `'0'` / `'1'` |
| 后端 `toggleStatus` 入参 | `'0'` / `'1'` (controller 严格校验) |
| 后端 sys_user.status 同步 | 同码 (`'0'` / `'1'`) |
**清理动作**:
- 跑数据迁移: `'正常'`/`'合作中'``'0'` (7 行), `'禁用'``'1'` (3 行), 备份 `biz_org_backup_20260819`
- 8 个前端文件统一: `manager/SponsorOrgs.vue`, `manager/ExecutorOrgs.vue`, `manager/Orgs.vue`, `admin/SponsorOrgs.vue`, `admin/ExecutorOrgs.vue`, `admin/Orgs.vue`, `manager/SponsorPeople.vue`, `manager/ExecutorPeople.vue`, `admin/SponsorPeople.vue`, `admin/ExecutorPeople.vue` (实际 10 个)
- 后端 `BizOrgController.toggleStatus` 入参校验: 只接 `'0'`/`'1'`, 其它报"status 必须是 '0' (正常) 或 '1' (禁用)"
- 后端 `BizOrgServiceImpl.toggleStatus` 不再做中文映射, `'0'` → sys_user.status='0', `'1'` → '1' (直透同码)
---
## §10 待修复列表 (按优先级)
| # | 问题 | 文件 | 修复建议 | 严重度 |
|---|---|---|---|---|
| 1 | 状态 filter "0" 选项文案 "正常/合作中" 模糊 | `manager/SponsorOrgs.vue`, `admin/SponsorOrgs.vue`, `admin/ExecutorOrgs.vue`, `manager/Orgs.vue` | 2026-08-19 落地: 全部收口 2 个 option (0=正常, 1=禁用), 删 0正常/1禁用/合作中/正常/禁用 字符串残骸 | ~~P2~~ → ✅ |
| 2 | admin 端也同源存在 "0"/"1" 鬼 option | 同上 | 同上 | ~~P2~~ → ✅ |
| 3 | 缺筛选: 创建日期起止 | `manager/SponsorOrgs.vue` + `BizOrgMapper.xml` | 加 `q.startTime/q.endTime` + `<if>` | P2 |
| 4 | 缺筛选: 单位地址 | `manager/SponsorOrgs.vue` + `BizOrgMapper.xml` | 加 `q.address` + `address LIKE ?` + 索引检查 | P2 |
| 5 | el-input / el-select 缺 width | `manager/SponsorOrgs.vue:12,15,18` | 加 `style="width:200px/220px/140px"` | P2 |
| 6 | createTime `String().slice(0,10)` 脆弱 | `manager/SponsorOrgs.vue:41` | 抽 `formatDate()` 工具函数, 兼容 ISO/时间戳/字符串 | P3 |
| 7 | admin 端禁用/恢复 不联动 sys_user.status | 已修复 — `BizOrgServiceImpl.toggleStatus` + `BizAuthController.smsLogin` 4.5 节 | 2026-08-19 落地: toggleStatus 同步主账号 sys_user.status; 登录时校验主账号状态拦截子账号 | ~~P1~~ → ✅ |
| 8 | `intent_count` 字段无自动维护 | `biz_org` + intent 业务代码 | 创建/删除意向时同步 +计数, 或改成派生 view (COUNT(*) WHERE user_id=org.user_id) | P3 |
| 9 | 原型"工作台"面包屑 vs 实现"首页" | `manager/SponsorOrgs.vue:8` | 跟 style-alignment 统一规范保持 "首页 / 支持单位管理" 即可, 接受偏离 | P3 |
---
## §11 引用清单
| 文件 | 行号 | 说明 |
|---|---|---|
| `ry-vue3/src/views/manager/SponsorOrgs.vue` | 1-120 | 任务审查目标 |
| `ry-vue3/src/views/admin/SponsorOrgs.vue` | 1-238 | 配套 admin 端 CRUD 版 |
| `ry-vue3/src/layout/AdminLayout.vue` | 95-106 | `MENU.manager` 含本菜单 (line 101) |
| `ry-vue3/src/router/index.js` | 74 | `manager-sponsor-orgs` 路由 |
| `ry-vue3/src/api/public.js` | 32-37, 90-94 | `bizList/bizGet/bizUpdate` 通用封装 |
| `ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizOrgController.java` | 26-31 | `GET /business/org/list` |
| `ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizOrgServiceImpl.java` | 13-53 | service 实现 |
| `ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizOrg.java` | 13-80 | Entity 全字段 (13 + 2 BaseEntity) |
| `ry-api/ruoyi-business/src/main/resources/mapper/business/BizOrgMapper.xml` | 34-44 | `selectList` SQL |
| `proto/html/manager.html` | 210 | 菜单指向 `support-unit-manage.html` |
| `proto/html/components/support-unit-manage.html` | 1-457 | 原型 (业务目标) |
| MySQL `biz_org` | 实测 2026-08-18 | 17 行 sponsor, status 混合 0/合作中/禁用/正常 |
---
## 附录 A. 修复记录
| 时刻 | 内容 |
|---|---|
| 2026-08-19 v2 | **P1 #7 修复**: admin 端禁用 org 同步主账号 sys_user.status; 子账号登录时校验主账号状态拦截. 涉及: BizOrgServiceImpl.toggleStatus + BizOrgController.toggleStatus endpoint + BizAuthController.smsLogin 4.5 节点 + admin/ExecutorOrgs.vue & admin/SponsorOrgs.vue onToggleStatus 改调 toggleOrgStatus helper + api/public.js 加 toggleOrgStatus |
| 2026-08-19 v2.1 | manager 端补 禁用/恢复 按钮 (合规角色该有). 涉及: manager/SponsorOrgs.vue + manager/ExecutorOrgs.vue 加 onToggleStatus + isDisabled, 操作列 180→240 |
| 2026-08-19 v3 | **整体整改 0/1 收口**: DB biz_org.status 统一为 '0'=正常, '1'=禁用; 后端 `BizOrgController.toggleStatus` 严格校验 '0'/'1'; 前端 10 个 orgs/people 文件 (manager/admin × Sponsor/Executor + manager/Orgs + admin/Orgs) 表格 el-tag 显示由 raw `{{ row.status }}` 改为 `{{ row.status === '1' ? '禁用' : '正常' }}`; 筛选下拉 + 编辑表单 + onToggleStatus label 全部对齐 0/1 语义; DB migration 已迁移 10 行脏数据 (7 正常/合作中 → 0, 3 禁用 → 1), 备份表 `biz_org_backup_20260819` |
| 2026-08-19 v3.1 | 4 个 People 页面表格 el-tag 显示 bug 修复: `{{ row.status || '正常' }}` 在 status='0' 时 '0' 是 truthy, 不会回退到 '正常', 实际显示 '0' 给用户. 改为 `{{ row.status === '1' ? '禁用' : '正常' }}` 跟 Orgs 保持一致. 涉及: manager/SponsorPeople.vue + manager/ExecutorPeople.vue + admin/SponsorPeople.vue + admin/ExecutorPeople.vue |
| 2026-08-18 v1 | 初稿 11 章齐, 9 个待修复 |