Files
guoju0808/_self/manager_sponsor_orgs.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

22 KiB
Raw Blame History

/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):

<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

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):

{{ row.createTime ? String(row.createTime).slice(0,10) : '' }}
  • 后端 BizOrg.createTimejava.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.getByIdbizOrgMapper.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/toggleStatusBizOrgServiceImpl.toggleStatus 同步主账号 sys_user.status → 子账号 login 时被 4.5 节拦截

§6 Java Entity ↔ 数据库表 一致性

6.1 biz_org DDL (实测, 2026-08-18)

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_statusidx_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 端 onToggleStatustoggleOrgStatus(orgId, status) → 后端 BizOrgServiceImpl.toggleStatus (IBizOrgService.java:23-30 + BizOrgServiceImpl.java:60-77):

@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):

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
2026-08-18 v1 初稿 11 章齐, 9 个待修复