Files
starRiverProperty/docs/business/租户访客楼层策略-代码重构实施指南.md
hpd840321 7b2bd307f1 Initial commit: reorganized source tree
- backend/: 13 Maven modules (cw-elevator-application, cloudwalk-cloud, intelligent-cwoscomponent, ninca-crk, etc.)
- frontend/: 4 Vue projects (elevator-front, cwos-portal, alarm-front, front_acs) + decompiled + scripts
- scripts/: build, test-env, tools (Docker Compose, service templates, API parity)
- docs/: AGENTS.md, superpowers specs, architecture docs
- .gitignore: standard Java/Maven exclusions

Moved from legacy maven-*/ root layout to backend/ organized structure.
2026-05-09 09:56:45 +08:00

116 lines
7.8 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.
# 租户访客楼层策略 — 代码重构实施指南
> **目标**:在**不破坏对外 HTTP / Feign 契约**的前提下,实现规范中的 **「组织 `detail` 内以 `allow_zone_ids` 替代 `PersonResult.floorList`」**,使**邀约页、UC-01 派梯**与**租户策略**一致。
> **依据**:[访客邀约与派梯楼层一致性梳理](访客邀约与派梯楼层一致性梳理.md)、[迁入组织规格](../superpowers/specs/2026-05-06-tenant-visitor-policy-organization-implementation.md)(以下简称 **《组织规格》**)。
> **当前状态**:电梯 **`PersonRuleServiceImpl#addVisitor`** 已**不**读策略表、**不**做 ∩。组织侧已落地 **`TenantVisitorFloorPolicyService`** + **`ImgPersonServiceImpl#detail` / `page(isVisitor)`** 的 **`allow_zone_ids` 替代**;部署组织库需执行 **`docs/sql/organization_tenant_visitor_floor_policy.sql`**。电梯工程内已无 **`TenantVisitorFloorPolicyDao`**(死代码已删)。
---
## 1. 重构原则(必须遵守)
| 原则 | 说明 |
|------|------|
| **策略语义** | 仅 **「替代」**:命中策略时 **`floorList` = 配置中的 `allow_zone_ids` 解析结果****不要**与 `listByImageId` 结果做 **∩** 作为规范主路径。 |
| **唯一对外楼层权威** | 消费「被访人可派梯/可邀约楼层」**必须**走 **`PersonService.detail``floorList`**(经 Intelligent → 组织)。 |
| **电梯侧** | **不**新增「再算一遍策略」;**不**恢复 `TenantVisitorFloorPolicyDao` 参与 `addVisitor` 有效楼层计算。 |
| **接口兼容** | 《组织规格》约定:`cwos-component-organization-interface` **不**为策略新增公开 DTO/方法时,策略仅作为**服务层内部实现**;对外仍只通过现有 `detail` 返回里的 `floorList` / `floorNames` 体现。 |
| **UC-02** | 显式 `floorIds` 仍由调用方负责与业务单一致;若需 **⊆ 策略** 的硬约束,在 **BFF** 实现,或单独立项改电梯**且**经评审。 |
---
## 2. 分阶段实施任务
### 阶段 A — 数据与组织工程骨架
1. **策略表落库位置**(二选一,推荐 **组织库** 为唯一主库)
- **推荐**:在 **组织服务使用的 MySQL 库** 中创建与《组织规格》一致的表结构(字段含 `business_id``policy_type``allow_zone_ids` JSON、`enabled``building_id` 可空等)。
- **迁移期**:若生产数据仍在**电梯库** `tenant_visitor_floor_policy`,做 **一次性迁移脚本** + 运维切换窗口;迁移完成后电梯侧 Dao **仅删除调用**,不必双写。
2. **组织模块新增(均在 `maven-ninca-common-component-organization`**
- Entity / Mapper / XML`tenant_visitor_floor_policy` CRUD 或至少 **按 `business_id` + enabled** 查询租户默认行(`building_id IS NULL`)。
- **`TenantVisitorFloorPolicyService`**(命名可依项目惯例):
- `boolean isPolicyActive(String businessId)``Optional<PolicyRow> findTenantDefault(String businessId)`
- `List<String> parseAllowZoneIds(String json)`(健壮解析,非法 JSON → 视为未启用或记录告警)。
- **不做**:在 interface 模块暴露新 REST(除非产品明确要求管理 API)。
### 阶段 B — 在 `ImgPersonServiceImpl#detail` 接入替代逻辑(核心)
**文件**`cwos-component-organization-service/.../ImgPersonServiceImpl.java`,方法 **`detail`**。
**插入点**:在 **`elevatorFeignClient.listByImageId` 成功**、已根据 `images` 遍历得到 **`floorList` / `floorNames`(原始)** 之后;在 **`result.setFloorList` / `setFloorNames`** 之前增加分支:
```
伪代码:
原始列表 = 当前遍历 listByImageId 得到的 floorList, floorNames
若 TenantVisitorFloorPolicyService 对 businessId(及必要时 organizationIds)判定「启用且 allow 非空」:
floorList := allow_zone_ids 解析后的 zoneId 列表(顺序:与 JSON 数组顺序一致)
floorNames := 需与 floorList 对齐展示
选项 1:配置侧同时存 id→name 映射(扩展列或独立字典表)
选项 2:对 allow 中每个 zoneId 调现有 Zone Feign 批量查名称(注意批量与超时)
选项 3:若产品允许仅展示 zoneId,则 floorNames 可与 zoneId 同步占位(不推荐体验)
否则:
保持现有原始 listByImageId 语义
result.setFloorList(...)
result.setFloorNames(...)
```
**注意**
- **`defaultFloor` / `floorName`(单人默认层展示)** 与 **`floorList`(通行可达层)** 语义不同,勿混写;参见 [08 文档](../../maven-cw-elevator-application/cw-elevator-application-service/docs/08-visitor-registration-and-elevator-auth.md)。
- **启用判定键**:与《组织规格》一致,以 **`business_id`**Header `businessid` / `companyId`)为主;若后续要 **按机构细分**,再扩展查询条件,不在本阶段默认实现。
### 阶段 C — 访客列表 `page(isVisitor)` 对齐(强烈建议)
**文件**:同一 `ImgPersonServiceImpl`,方法 **`page`**,分支 **`param.getIsVisitor()` 非空**。
- 现状:该分支内有 **星河湾 40F/6F** 等与 **`detail`** 不一致的展示逻辑。
- **建议**:在策略**命中**时 **优先应用与 `detail` 相同的替代结果**(或抽 **私有方法** `applyTenantVisitorFloorPolicy(resultRow, businessId, …)``detail``page` 共用),并 **跳过**与租户策略冲突的硬编码默认层块(参见《组织规格》§4.3)。
- 避免:邀约走 `detail`、列表走 `page` 时出现两套楼层。
### 阶段 D — 电梯侧清理(收尾)
1. **确认** `PersonRuleServiceImpl` **无** `TenantVisitorFloorPolicyDao` 注入与 ∩ 逻辑(当前梳理版本已符合则仅做静态检查)。
2. **删除或标注废弃**`cw-elevator-application-data`**`TenantVisitorFloorPolicyDao` / Mapper / XML** 若已不再被任何 Bean 引用 — **删除**可减少歧义;若迁移脚本仍需参考表结构,可保留 DDL 文档到 `docs/sql`,代码删除前 grep 全仓引用。
3. **电梯库表**:迁移完成后由 DBA **删表或归档**(按运维规范)。
### 阶段 E — UC-02 与集成(可选增强)
- **BFF**:派梯前读取邀约单 `floorIds`,调用 **`detail`** 得到 **`floorList`**(已含替代),校验 **`floorIds ⊆ floorList`**(集合意义),失败则拒绝派梯并返回明确错误码。
- **幂等**:邀约单号 + 派梯状态机由业务系统保证,电梯接口本身不变。
### 阶段 F — 测试与验收
| 场景 | 期望 |
|------|------|
| 未配置策略 | `detail.floorList` 与改造前 **listByImageId** 一致;UC-01 行为不变。 |
| 配置启用且 allow 非空 | `detail.floorList` **等于** allow 列表(替代);UC-01 开通层与邀约页一致。 |
| 关闭策略 / JSON 无效 | 回退原始语义;无启动报错。 |
| `page(isVisitor)` | 与 `detail` 策略表现一致(验收抽样)。 |
---
## 3. 风险与缓解
| 风险 | 缓解 |
|------|------|
| **`floorNames` 与 id 不对齐** | 替代后必须统一用 Zone 服务补全名称或扩展配置。 |
| **性能**detail 每次多一次 DB + 可选批量 Zone | 策略行缓存(短 TTL)或本地缓存按 `business_id`。 |
| **跨楼栋首层**(§4.8 | 产品确认单次 `effective` 是否允许多楼栋;否则约束邀约/UC-02 单层栋或拆分调用。 |
---
## 4. 推荐提交顺序(便于 Code Review
1. 组织库 DDL + Mapper + **PolicyService**(单测解析 JSON)。
2. **`detail` 接入替代** + 单元/集成测试(可 Mock Feign)。
3. **`page` 分支对齐**(若有)。
4. 电梯侧删除死代码 + 文档更新(本文 + 《一致性梳理》§6)。
5. 迁移脚本在生产前演练。
---
**文档版本**:与仓库实施同步更新;重大契约变更需同步修改《组织规格》并评审。