# 访客楼层策略完整走查 & 广发基金 20 层扩展评估 **日期**: 2026-05-09 **状态**: 走查评估完成 **作者**: OpenCode Agent --- ## 目录 1. [架构总览](#1-架构总览) 2. [星河湾 40F/6F 硬编码逻辑走查](#2-星河湾-40f6f-硬编码逻辑走查) 3. [租户访客策略完整链路走查](#3-租户访客策略完整链路走查) 4. [广发基金当前 28F 策略分析](#4-广发基金当前-28f-策略分析) 5. [广发基金扩展至 20 层评估](#5-广发基金扩展至-20-层评估) 6. [变更点清单与风险矩阵](#6-变更点清单与风险矩阵) 7. [测试验证策略](#7-测试验证策略) 8. [附录: 关键文件索引](#8-附录-关键文件索引) --- ## 1. 架构总览 ### 1.1 服务拓扑 ``` 访客登记页 (BFF/第三方) │ │ POST /elevator/person/add/visitor ▼ ┌──────────────────────────────┐ │ cw-elevator-application │ PersonRuleServiceImpl#addVisitor() │ (电梯应用) │ ┌─ 阶段1: personService.detail() │ │ ├─ 阶段2: UC-01/UC-02 分支 │ │ ├─ 阶段3: 空集校验 │ │ └─ 阶段4: 写入 image_rule_ref ├──────────────────────────────┤ │ PersonService.detail() │ ──Feign──▶ └──────────────────────────────┘ │ ▼ ┌──────────────────────────────┐ │ ninca-common-component-org │ ImgPersonServiceImpl#detail() │ (组织组件) │ ┌─ listByImageId() → 电梯 Feign │ │ ├─ ★ 策略替代 (replacementZoneIds) │ │ └─ 返回 PersonResult.floorList │ │ │ TenantVisitorFloorPolicyService │ TenantVisitorFloorPolicyMapper │ tenant_visitor_floor_policy 表 └──────────────────────────────┘ ``` ### 1.2 核心原则(已确立) | 原则 | 说明 | |------|------| | **策略只在组织组件** | `tenant_visitor_floor_policy` 表及策略逻辑仅在 `ninca-common-component-organization` | | **电梯侧只透传** | `PersonRuleServiceImpl#addVisitor()` 不再读策略表,不做求交 | | **替代(Replacement)语义** | 策略命中时 `allow_zone_ids` **整表替换** `floorList`,禁止与原始楼层求交 | | **对外接口不变** | `PersonService.detail` → `PersonResult.floorList` 契约保持兼容 | | **隔离键 = org_id** | 策略按组织节点 (`org_id`) 隔离,非 `business_id` | ### 1.3 楼层数据流 ``` detail() 路径 (UC-01 权威来源): ImgPersonServiceImpl#detail() → elevatorFeignClient.listByImageId() # 获取被访人所有楼层 → tenantVisitorFloorPolicyService.replacementZoneIds() # 策略替代 → PersonResult.floorList = allow_zone_ids # 返回替代后的楼层 listByPage 路径 (列表展示): ImgPersonServiceImpl#listByPage(isVisitor) → P1: 策略命中 → 使用 allow_zone_ids (跳过 XHW 块) → P2: 无策略 → listByImageId + XHW 40F/6F 硬编码逻辑 addVisitor() 路径 (电梯派梯): PersonRuleServiceImpl#addVisitor() → UC-01 (无 floorIds): effective = personResult.floorList → UC-02 (有 floorIds): effective = param.floorIds → 写入 image_rule_ref (每条 floorId 一行) ``` --- ## 2. 星河湾 40F/6F 硬编码逻辑走查 ### 2.1 配置来源 **文件**: `backend/ninca-common-component-organization/cwos-component-organization-starter/deploy/run-verify/application.properties` ```properties # 第 170-172 行 xhwId=21474e012cd14e26bc62771873b22562 xhwDefaultFloorId=605560547135455232 # = 40F xhwSixFloorId=605560541473144832 # = 6F ``` ### 2.2 Java 实现 **文件**: `backend/ninca-common-component-organization/cwos-component-organization-service/src/main/java/cn/cloudwalk/service/organization/service/ImgPersonServiceImpl.java` 注入点 (第 154-158 行): ```java @Value("${xhwId}") private String xhwId; @Value("${xhwDefaultFloorId}") private String xhwDefaultFloorId; @Value("${xhwSixFloorId}") private String xhwSixFloorId; ``` ### 2.3 生效路径: `listByPage` 访客列表分支 **关键事实**: 40F/6F 硬编码逻辑 **只存在于 `listByPage`**,不存在于 `detail()`。这意味着: - **访客邀约页 / UC-01 派梯**: 走 `detail()` → 不会触发 40F/6F 逻辑 - **访客列表页**: 走 `listByPage(isVisitor)` → 无策略命中时触发 40F/6F ```java // listByPage 第 326-370 行 if (policyZones.isPresent()) { // P1: 策略命中 → 使用 allow_zone_ids (跳过 XHW 块) imgStorePersonResult.setFloorInfoList(policyFloorInfo); imgStorePersonResult.setDefaultChooseFloor(policyZones.get().get(0)); } else { // P2: 无策略 → 走到 XHW 40F/6F List orgFloorList = orgFloorMapper.listByOrgIds(organizationIds); if (CollectionUtils.isEmpty(orgFloorList)) { imgStorePersonResult.setIsAcrossDay(1); // 跨天 } else { if (imgStorePersonResult.getOrganizationIds().contains(this.xhwId)) { // 物业公司 → 默认 40F imgStorePersonResult.setDefaultChooseFloor(this.xhwDefaultFloorId); // zoneName = "40F" } else { // 其他组织 → 默认 6F imgStorePersonResult.setDefaultChooseFloor(this.xhwSixFloorId); // zoneName = "6F" } } } ``` ### 2.4 40F/6F 判定逻辑总结 ``` 是否命中租户策略? ├── YES → 策略 allow_zone_ids 替代 (跳过 XHW 块) └── NO → 查询 OrgFloor 表 ├── 无 OrgFloor 记录 → isAcrossDay=1 (跨天通行) └── 有 OrgFloor 记录 ├── 组织 ID 含 xhwId → 默认 40F └── 其他组织 → 默认 6F ``` ### 2.5 影响范围 | 接口 | 是否涉及 40F/6F | 说明 | |------|----------------|------| | `PersonService.detail` | ❌ 不涉及 | detail 只做 listByImageId + 策略替代 | | `listByPage(isVisitor)` | ✅ 涉及 | 无策略命中时展示 40F/6F | | `addVisitor` | ❌ 不涉及 | 只透传 floorList/floorIds | --- ## 3. 租户访客策略完整链路走查 ### 3.1 数据库表结构 **表**: `tenant_visitor_floor_policy` (组织库 `component-organization`) ```sql -- DDL 定义于: docs/sql/tenant_visitor_floor_policy_v2.sql id VARCHAR(64) PRIMARY KEY org_id VARCHAR(64) -- 组织节点 ID (隔离键) business_id VARCHAR(64) -- 业务 ID policy_type VARCHAR(64) -- 'INTERSECT_ALLOWLIST' allow_zone_ids TEXT -- JSON 数组: ["zoneId1","zoneId2"] building_id VARCHAR(64) -- NULL (全局) enabled TINYINT(1) -- 0/1 policy_version INT -- 版本号 (ON UPDATE 自增) remark VARCHAR(512) created_at BIGINT updated_at BIGINT UNIQUE KEY: uk_org_building (org_id, building_id) ``` ### 3.2 策略查询链路 ``` TenantVisitorFloorPolicyMapper.selectEnabledByOrgId(orgId) ↓ @Select SELECT * FROM tenant_visitor_floor_policy WHERE org_id = #{orgId} AND enabled = 1 AND (building_id IS NULL OR building_id = '') LIMIT 1 TenantVisitorFloorPolicyService.findEnabledPolicyByOrgId(orgId) ↓ 返回 Optional TenantVisitorFloorPolicyService.replacementZoneIdsIfPolicyActive(orgIds) ↓ 遍历 orgIds, 找到第一个命中策略的 ↓ 解析 allow_zone_ids JSON → List ↓ 返回 Optional> (非空列表) ImgPersonServiceImpl#detail() ↓ replacementFloors.isPresent() ? ↓ YES: floorList = replacementFloors.get() // 替代 ↓ NO: 保留 listByImageId 原始结果 ``` ### 3.3 多组织匹配优先级 `replacementZoneIdsIfPolicyActive(List orgIds)` 按 `orgIds` 顺序依次尝试: ```java // TenantVisitorFloorPolicyService.java 第 70-80 行 for (String id : orgIds) { Optional> zones = replacementZoneIdsIfPolicyActive(id); if (zones.isPresent()) { return zones; // 第一个命中的策略作为结果 } } ``` 意味着: **人员所属的第一个有策略的组织决定最终楼层**。 ### 3.4 电梯侧 addVisitor() 流程 ```java // PersonRuleServiceImpl.java 第 165-270 行 addVisitor() { // 阶段 1: 获取被访人 detail PersonResult personResult = personService.detail(detailParam); // 阶段 2: 确定生效楼层 if (callerProvidedFloors) { effective = param.getFloorIds(); // UC-02: 调用方指定 } else { effective = personResult.getFloorList(); // UC-01: 使用 detail 返回 } // 阶段 3: 空集校验 → 76260531 if (CollectionUtils.isEmpty(effective)) { return fail("76260531"); } // 阶段 4: 写入 image_rule_ref for (String floorId : effective) { ImageRuleRefResultDto defaultRule = imageRuleRefDao.getDefaultByZoneId(floorId); // 插入 image_rule_ref 行 } } ``` ### 3.5 错误码 | 错误码 | 含义 | 触发点 | |--------|------|--------| | `76260530` | addVisitor 未捕获异常 | PersonRuleServiceImpl:263 | | `76260531` | 无可用楼层 | PersonRuleServiceImpl:184, 197, 208 | | `76260532` | 历史: 求交为空 (已废弃) | V1 解编译版:207 | --- ## 4. 广发基金当前 28F 策略分析 ### 4.1 当前策略配置 **组织库种子数据** (`docs/sql/organization_tenant_visitor_floor_policy_init_tenants.sql`): ```sql INSERT INTO tenant_visitor_floor_policy (...) VALUES ( 'gf_vstr_policy_guangfa_fund_001x', -- id '488b8ad049bb43408a6fbcc50bcb89ac', -- org_id (广发基金组织节点) '2524639890ba4f2cba9ba1a4eeaa4015', -- business_id 'INTERSECT_ALLOWLIST', -- policy_type '["605560545117995008"]', -- allow_zone_ids: 仅 28F NULL, 1, 1, -- building_id, enabled, version '广发基金:访客楼层策略(组织库);默认 28F。' ); ``` **允许楼层**: 1 个 zone — `605560545117995008` (28F) ### 4.2 当前生效流程 ``` 广发基金访客邀约: 1. 前端/POST /elevator/person/add/visitor 2. addVisitor() → personService.detail(personId) 3. ImgPersonServiceImpl#detail() → listByImageId() 返回被访人全部楼层 (可能多 zone) → replacementZoneIds(["488b8ad..."]) 命中策略 → floorList = ["605560545117995008"] # 仅 28F 4. addVisitor() UC-01: effective = ["605560545117995008"] 5. 写入 image_rule_ref (仅 28F 一条) ``` ### 4.3 当前配置的局限 | 局限 | 描述 | |------|------| | 仅 1 层 | 广发基金租用 28-38F (11层),但策略仅开放 28F 给访客 | | 组织粒度单一 | `org_id` 绑定到 `[28-38F]广发基金管理有限公司` 一个节点 | | 无部门区分 | 广发基金内部所有部门的访客均只能到达 28F | ### 4.4 相关文件清单 | 文件 | 内容 | |------|------| | `docs/sql/tenant_visitor_floor_policy_init_guangfa_fund.sql` | 广发 28F 种子 (电梯库侧) | | `docs/sql/organization_tenant_visitor_floor_policy_init_tenants.sql` | 广发 28F + 物业 28F+6F 种子 (组织库侧) | | `docs/testing/广发基金访客被访楼层接口生产验证.md` | 广发生产验证文档 | | `docs/testing/tenant-visitor-default-floor-isolation.md` | 租户隔离边界说明 | | `scripts/test-env/stub-person-service.py` | 测试桩 (organizationNames: ["广发基金"]) | | `backend/cw-elevator-application/tools/visitor_floor_verification/...` | 策略验证脚本 | | `docs/testing/release-visitor-noauth-verify-v20260430/...` | 批量验证套件 | --- ## 5. 广发基金扩展至 20 层评估 ### 5.1 需求理解 将广发基金访客可到达楼层从 **1 层 (28F)** 扩展至 **20 层** (推测: 28-38F 共 11 层 + 扩展至 20 层范围 = 可能包含更广范围的楼层如 19-38F 或 28-47F 等)。 **待确认**: 具体哪 20 层的 zone_id 列表需与业务方确认。 ### 5.2 变更方案 #### 方案 A: 纯数据变更 (推荐 ★★★★★) **操作**: 仅更新 `tenant_visitor_floor_policy` 表的 `allow_zone_ids` 字段, 从 `["605560545117995008"]` 扩展为 20 个 zone_id 的 JSON 数组。 **变更范围**: | 层级 | 变更内容 | |------|---------| | 数据库 | UPDATE `tenant_visitor_floor_policy` SET `allow_zone_ids` = '["zone1",...,"zone20"]' WHERE `id` = 'gf_vstr_policy_guangfa_fund_001x' | | Java 代码 | **无需修改** — `parseAllowZoneIds()` 已支持任意数量 zone | | SQL 种子 | 更新 `organization_tenant_visitor_floor_policy_init_tenants.sql` 和 `tenant_visitor_floor_policy_init_guangfa_fund.sql` | | 接口契约 | **不变** — `PersonResult.floorList` 自动包含 20 个 zone | **优点**: - 零代码变更 - 现有策略引擎自动支持多 zone 替代 - `detail()` 返回 `floorList` 自动包含 20 个 zone - `addVisitor()` 透传, 为每个 zone 写一条 `image_rule_ref` - 立即生效 (无需重启, 下次 `detail()` 调用即可) **风险**: 无 — `parseAllowZoneIds()` 和 `buildFloorInfoListFromOrderedZoneIds()` 都已正确处理多 zone 列表。 **验证**: ```sql -- 执行变更后验证 SELECT id, org_id, allow_zone_ids, enabled, policy_version FROM tenant_visitor_floor_policy WHERE id = 'gf_vstr_policy_guangfa_fund_001x'; -- 预期: allow_zone_ids 包含 20 个 zone_id 的 JSON 数组 ``` #### 方案 B: 多组织拆分策略 **思路**: 为广发基金的不同部门 / 组织节点配置不同的楼层范围。 **变更范围**: - 新增多个 `tenant_visitor_floor_policy` 行, 每条绑定不同的 `org_id` - 例如: `[28-30F]广发基金IT部` → allow 28-30F, `[31-33F]广发基金财务部` → allow 31-33F **适用场景**: 广发基金内部需要按部门细粒度控制访客楼层时启用。当前需求为整体 20 层开放, 暂不需要此方案。 #### 方案 C: 前端增强 (可选) **思路**: 前端访客邀约页根据 `floorList` (20 个 zone) 展示多选列表, 允许 BFF 调用方选择特定楼层子集传入 `addVisitor`。 **注意**: 当前 `addVisitor` 已支持 UC-02 调用方传 `floorIds` 子集, 无需后端变更。 ### 5.3 推荐实施步骤 ``` Step 1 ─ 确认 20 层 zone_id 列表 查询 code_elevator_area 表获取对应 zone_id SELECT zone_id, zone_name, code FROM code_elevator_area WHERE building_id = '605560539791228928' -- floor.building.id Step 2 ─ 更新生产库策略数据 UPDATE tenant_visitor_floor_policy SET allow_zone_ids = '["zone1","zone2",...,"zone20"]', policy_version = policy_version + 1, remark = '广发基金:访客默认 20 层(扩展自 v1 28F 单层)', updated_at = UNIX_TIMESTAMP(NOW()) * 1000 WHERE id = 'gf_vstr_policy_guangfa_fund_001x' Step 3 ─ 更新组织库种子 SQL 修改 organization_tenant_visitor_floor_policy_init_tenants.sql Step 4 ─ 验证 curl POST /elevator/person/add/visitor (广发基金被访人) 检查 PersonResult.floorList 包含 20 个 zone 检查 image_rule_ref 写入 20 行 ``` ### 5.4 性能影响 | 影响项 | 评估 | |--------|------| | `detail()` 响应 | 增加 19 个 zone_id, 响应体微增 (~1-2KB) | | `addVisitor()` 写入 | 从 1 行 image_rule_ref 变为 20 行, 批量插入一次性完成 | | `listByPage` 列表 | `buildFloorInfoListFromOrderedZoneIds()` 构建 20 条而不是 1 条 | | zone 查询 | 结果列表变大但无额外 DB 查询 | **结论**: 性能影响可忽略。核心开销在 `addVisitor` 的 `image_rule_ref` 批量插入 (20 行), 仍为单次 DB 操作。 ### 5.5 何时需要代码变更 当前架构中, **以下场景需要代码变更**: | 场景 | 是否需要代码变更 | |------|-----------------| | 修改 allow_zone_ids (数量变化) | ❌ 不需要 | | 新增策略行 (新租户) | ❌ 不需要 | | 修改策略类型 (policy_type) | ⚠️ 可能需要 — 当前仅实现 INTERSECT_ALLOWLIST | | 新增策略字段 (如按 building_id 隔离) | ✅ 需要 — Mapper/TDO 需添加字段 | | 前端默认选中逻辑需要映射 zone_name | ❌ 不需要 — `buildFloorInfoListFromOrderedZoneIds` 已处理 | --- ## 6. 变更点清单与风险矩阵 ### 6.1 变更范围最小化方案 | # | 步骤 | 类型 | 风险 | |---|------|------|------| | 1 | 确认 20 层 zone_id 列表 | 调研 | 🟢 | | 2 | UPDATE tenant_visitor_floor_policy (组织库) | DML | 🟢 | | 3 | UPDATE tenant_visitor_floor_policy (电梯库, 如存在) | DML | 🟢 | | 4 | 更新种子 SQL 文件 | 文档 | 🟢 | | 5 | 功能验证 (见 §7) | 测试 | 🟢 | ### 6.2 关键检查点 | 检查点 | 预期结果 | |--------|---------| | 策略查询 | `selectEnabledByOrgId('488b8ad...')` 返回 `enabled=1` 行 | | allow_zone_ids 解析 | `parseAllowZoneIds()` 返回 20 个 zone_id | | detail() floorList | `PersonResult.floorList` 含 20 个 zone_id | | addVisitor UC-01 | `effective` = 20 个 zone_id, 写入 20 行 image_rule_ref | | addVisitor UC-02 | 调用方传 5 个 floorIds → 只写 5 行 (策略不覆盖 UC-02) | | listByPage 访客列表 | 策略命中 → 展示 20 层, defaultChooseFloor = 第一个 zone | | 其他租户不受影响 | 非广发基金组织 → detail() 走 listByImageId 原路径 | ### 6.3 回滚方案 ```sql -- 回滚到 28F 单层 UPDATE tenant_visitor_floor_policy SET allow_zone_ids = '["605560545117995008"]', policy_version = policy_version + 1, remark = '广发基金:访客默认 28F(回滚)', updated_at = UNIX_TIMESTAMP(NOW()) * 1000 WHERE id = 'gf_vstr_policy_guangfa_fund_001x'; ``` 回滚立即生效, 无需重启服务。 --- ## 7. 测试验证策略 ### 7.1 快速验证 (curl) ```bash # 广发基金被访人访客邀约 curl -X POST http://127.0.0.1:18081/elevator/person/add/visitor \ -H 'Content-Type: application/json' \ -d '{ "personId": "1072908835884208128", "businessId": "2524639890ba4f2cba9ba1a4eeaa4015", "visitorName": "test_20f", "begVisitorTime": "2026-05-09 00:00:00", "endVisitorTime": "2026-12-31 23:59:59" }' # 预期: 返回成功, image_rule_ref 写入 20 行 ``` ### 7.2 数据库验证 ```sql -- 1. 验证策略配置 SELECT id, org_id, allow_zone_ids, enabled, policy_version FROM tenant_visitor_floor_policy WHERE id = 'gf_vstr_policy_guangfa_fund_001x'; -- 2. 验证写入的规则行数 SELECT COUNT(*) AS rule_count FROM image_rule_ref WHERE person_id = ''; -- 预期: rule_count = 20 -- 3. 验证 zone 覆盖 SELECT DISTINCT zone_id, zone_name FROM image_rule_ref WHERE person_id = '' ORDER BY zone_name; ``` ### 7.3 验证脚本 (现有工具) ```bash # 使用现有组织策略验证脚本 cd backend/cw-elevator-application/tools/visitor_floor_verification python3 scripts/verify_org_policy_fix.py # 期望: T1 (有策略→allow 替代) 返回 20 个 zone # T2-T7 不受影响 ``` ### 7.4 回归检查点 | 测试项 | 期望 | |--------|------| | 广发基金访客获 20 层权限 | ✅ | | 物业公司访客仍为 28F+6F | ✅ | | 其他租户访客不受影响 | ✅ | | UC-02 调用方指定 3 层 → 只写 3 行 | ✅ | | listByPage 访客列表策略命中展示 | ✅ | | 策略禁用 (enabled=0) → 回退到原逻辑 | ✅ | --- ## 8. 附录: 关键文件索引 ### 8.1 Java 源码 | 文件 | 行数参考 | 功能 | |------|---------|------| | `backend/ninca-common-component-organization/.../policy/TenantVisitorFloorPolicyService.java` | 全文 103 行 | 策略服务: 查询、解析、替代 | | `backend/ninca-common-component-organization/.../service/ImgPersonServiceImpl.java` | 154-158 (注入), 326-370 (listByPage), 643-648 (detail) | 策略应用点 | | `backend/cw-elevator-application/.../impl/PersonRuleServiceImpl.java` | 165-270 (addVisitor) | 电梯侧透传 | | `backend/ninca-common-component-organization/.../entity/TenantVisitorFloorPolicy.java` | 全文 | 策略实体 | | `backend/ninca-common-component-organization/.../mapper/TenantVisitorFloorPolicyMapper.java` | 18-26 (查询) | 策略 DAO | ### 8.2 SQL 脚本 | 文件 | 功能 | |------|------| | `docs/sql/tenant_visitor_floor_policy.sql` | 表 DDL | | `docs/sql/tenant_visitor_floor_policy_v2.sql` | 增加 org_id 迁移 | | `docs/sql/tenant_visitor_floor_policy_init_guangfa_fund.sql` | 广发基金种子 (电梯库) | | `docs/sql/organization_tenant_visitor_floor_policy_init_tenants.sql` | 广发 + 物业种子 (组织库) | | `docs/sql/tenant_visitor_floor_policy_migrate_org_id.sql` | org_id 数据迁移 | ### 8.3 设计文档 | 文件 | 内容 | |------|------| | `docs/superpowers/specs/2026-05-06-tenant-visitor-policy-organization-implementation.md` | 策略迁入组织组件规范 (559 行) | | `docs/superpowers/specs/2026-05-01-org-id-policy-fix-design.md` | org_id 粒度修复设计 | | `docs/business/租户访客默认楼层技术产品方案.md` | 产品方案 (427 行) | | `docs/testing/tenant-visitor-default-floor-isolation.md` | 租户隔离边界 (105 行) | | `docs/testing/visitor-registration-floor-validation.md` | 测试方案 (151 行) | | `docs/testing/广发基金访客被访楼层接口生产验证.md` | 广发生产验证 | ### 8.4 测试与验证 | 文件 | 功能 | |------|------| | `scripts/test-env/verify-functional.sh` | 功能验证 (F3/F4 租户策略测试) | | `scripts/test-env/stub-person-service.py` | 广发基金测试桩 | | `backend/cw-elevator-application/tools/visitor_floor_verification/...` | 策略验证脚本集 | | `docs/testing/release-visitor-noauth-verify-v20260430/...` | 批量验证套件 | --- ## 总结 1. **40F/6F 逻辑**是星河湾物业管理的历史遗留硬编码, 仅影响 `listByPage` 访客列表展示, 不影响 `detail()` 和 `addVisitor()`。 2. **租户策略 (tenant_visitor_floor_policy)** 走 **替代** 语义: 命中后 `allow_zone_ids` 完全替代 `floorList`, 不做求交。 3. **广发基金当前仅开放 28F** (1 层), 通过 `tenant_visitor_floor_policy` 单行配置实现。 4. **扩展至 20 层是纯数据变更**: 只需 UPDATE `allow_zone_ids` 字段为 20 个 zone_id 的 JSON 数组。**零代码变更, 零接口变更, 零重启**。 5. **next step**: 确认 20 层 zone_id 列表 → UPDATE 生产库 → 更新种子 SQL → 功能验证。