mirror of
https://github.com/hpd840321/starRiverProperty.git
synced 2026-06-09 16:30:29 +08:00
42c4a9fd6b
- Deprecate elevator-side tenant_visitor_floor_policy SQL files (V2 queries only component-organization library) - Add guangfa 28F visitor floor design spec (table-driven approach A) - Add complete database ER diagram (14 DBs, 537 tables) - Add implementation plan for guangfa visitor floor policy - Code walkthrough docs for visitor floor policy analysis
9.5 KiB
9.5 KiB
广发基金 28F 策略 — 参照 40F/6F 硬编码模式重实现可行性评估
日期: 2026-05-09 状态: 评估完成 — 不可行 (推荐保留表驱动方案)
1. 两种模式的本质差异
1.1 40F/6F 硬编码模式
┌─ @Value("${xhwId}") 注入配置
├─ @Value("${xhwDefaultFloorId}") 注入配置
├─ @Value("${xhwSixFloorId}") 注入配置
│
└─ ImgPersonServiceImpl#listByPage (仅此一处)
└─ if (orgIds.contains(xhwId))
└─ defaultChooseFloor = xhwDefaultFloorId, zoneName = "40F"
else
└─ defaultChooseFloor = xhwSixFloorId, zoneName = "6F"
核心特征: 只影响 listByPage 访客列表展示层, 不涉及 detail() / addVisitor()。
1.2 当前广发基金表驱动模式
┌─ tenant_visitor_floor_policy 表 数据源
├─ TenantVisitorFloorPolicyMapper DAO
├─ TenantVisitorFloorPolicyService 策略引擎
│
├─ ImgPersonServiceImpl#listByPage P1 分支 (列表展示)
└─ ImgPersonServiceImpl#detail() 核心路径 (UC-01 派梯)
└─ replacementZoneIdsIfPolicyActive → floorList 替代
└─ PersonRuleServiceImpl#addVisitor() → 写入 image_rule_ref
核心特征: 同时影响 listByPage (列表) 和 detail() (派梯)。
1.3 关键差异对比
| 维度 | 40F/6F 硬编码 | 广发表驱动 | 差异 |
|---|---|---|---|
| 数据存储 | application.properties |
tenant_visitor_floor_policy 表 |
文件 vs DB |
| 变更方式 | 改配置文件 + 重启 | UPDATE SQL (立即生效) | 重启 vs 实时 |
| 生效范围 | listByPage 仅列表 |
listByPage + detail() |
单路径 vs 双路径 |
| 多 zone 支持 | 否 (单 zone) | 是 (JSON 数组) | 1 vs N |
| zone 名称 | 硬编码字符串 "40F" |
动态解析 (zone 表) | 硬编码 vs 动态 |
| 组织匹配 | contains(xhwId) | 按 org_id 精确匹配 | 包含 vs 精确 |
| 多组织策略 | 无概念 | 按 orgIds 顺序依次命中 | N/A |
| 启用/禁用 | 无开关 | enabled 字段 |
需删配置 vs 一行 SQL |
| 策略版本 | 无 | policy_version 自增 |
无追踪 vs 有追踪 |
| addVisitor 影响 | ❌ 无 (不走 detail) | ✅ 核心路径 | 致命差异 |
2. 致命问题: detail() 路径缺失
2.1 addVisitor() 的楼层来源
// PersonRuleServiceImpl#addVisitor() 第 187-194 行
boolean callerProvidedFloors = !CollectionUtils.isEmpty(param.getFloorIds());
if (callerProvidedFloors) {
effective = param.getFloorIds(); // UC-02
} else {
effective = personResult.getFloorList(); // UC-01 ← 来自 detail()
}
detail() 返回的 floorList 是 UC-01 派梯的唯一数据源。
2.2 detail() 的当前楼层组装流程
// ImgPersonServiceImpl#detail() 第 630-651 行
// Step A: listByImageId 获取被访人全部楼层
CloudwalkResult<List<AcsPassRuleImageResultDto>> images = elevatorFeignClient.listByImageId(...);
// Step B: 遍历组装 floorList (原始全量)
for (AcsPassRuleImageResultDto dto : acsPassRuleImageResultDtoList) {
floorList.add(dto.getZoneId());
}
// Step C: ★ 策略替代 — 这是 40F/6F 模式完全没有的
Optional<List<String>> replacementFloors =
tenantVisitorFloorPolicyService.replacementZoneIdsIfPolicyActive(organizationIds);
if (replacementFloors.isPresent()) {
floorList = new ArrayList<>(replacementFloors.get()); // ← 替代
}
// Step D: 写入 PersonResult
result.setFloorList(floorList);
2.3 如果搬到 40F/6F 模式会发生什么
广发基金访客 addVisitor 的 UC-01 路径:
// 改造后 detail() 不再查策略表
detail() → listByImageId() → floorList = [28F, 29F, ..., 38F] 全部楼层
→ 无策略替代 → floorList = 11 个 zone (被访人的全部楼层)
→ addVisitor UC-01: effective = 11 个 zone
→ 写入 11 行 image_rule_ref
→ ❌ 访客获得了不应该有的 29-38F 权限
结论: 40F/6F 模式的核心缺陷是不接入 detail() 路径。广发基金策略如果只套用 listByPage 硬编码, 将导致 addVisitor 派梯完全绕过策略限制, 访客获得被访人的全部楼层权限而非仅限 20 层。
3. 如果强行修补 (在 detail() 中也加硬编码)
3.1 代码膨胀预估
// 需要在 ImgPersonServiceImpl 中新增:
@Value("${gfOrgId}") private String gfOrgId;
@Value("${gfDefaultFloorList}") private String gfDefaultFloorList; // 逗号分隔 20 个 zoneId
// detail() 中新增 (第 646 行附近):
if (result.getOrganizationIds().contains(this.gfOrgId)) {
floorList = Arrays.asList(gfDefaultFloorList.split(","));
zoneNames = buildCommaSeparatedFloorNames(businessId, floorList);
}
// listByPage 中新增 (第 354 行附近):
if (imgStorePersonResult.getOrganizationIds().contains(this.gfOrgId)) {
List<String> floorIds = Arrays.asList(gfDefaultFloorList.split(","));
buildFloorInfoListFromOrderedZoneIds(businessId, floorIds);
imgStorePersonResult.setDefaultChooseFloor(floorIds.get(0));
}
3.2 问题清单
| # | 问题 | 严重度 |
|---|---|---|
| 1 | 每新增一个租户需要加 2 个 @Value + 2 处 if/else |
🔴 |
| 2 | application.properties 中 gfDefaultFloorList 需要维护 20 个 zone_id (长字符串) |
🔴 |
| 3 | 20 个 zone_id 在配置文件中不可读,容易出错 | 🟡 |
| 4 | 修改策略需改配置文件 + 重启服务 | 🟡 |
| 5 | 无法动态启用/禁用 (需注释掉 @Value 或删配置) | 🟡 |
| 6 | 无策略版本追踪 | 🟢 |
| 7 | 与现有 tenant_visitor_floor_policy 表形成双重逻辑源 |
🔴 |
| 8 | detail() 中硬编码逻辑与表驱动逻辑并存,维护混乱 |
🔴 |
3.3 架构退化示意
改造前 (表驱动, 单一逻辑源):
detail() ─→ TenantVisitorFloorPolicyService ─→ DB
listByPage ─→ TenantVisitorFloorPolicyService ─→ DB
改造后 (混合模式, 双逻辑源):
detail() ─┬→ TenantVisitorFloorPolicyService ─→ DB (其他租户)
└→ if (contains(gfOrgId)) { split(config) } (广发特例)
listByPage ─┬→ TenantVisitorFloorPolicyService ─→ DB (其他租户)
├→ if (contains(xhwId)) { "40F" } (物业)
├→ else { "6F" } (非物业)
└→ if (contains(gfOrgId)) { split(config) } (广发特例 ← 新增)
每增加一个租户策略, 代码分支呈线性增长, 最终成为无法维护的 if/else 链。
4. 对比结论
| 评估维度 | 40F/6F 模式 | 表驱动模式 | 胜出 |
|---|---|---|---|
| 代码简洁性 | 简单 (3 个 @Value + 1 个 if/else) | 中等 (5 个类) | 40F |
| 扩展性 (新增租户) | 差 (每次加代码) | 优 (INSERT 一行) | 表驱动 |
| 多 zone 支持 | 无 (单 zone) | 优 (JSON 数组) | 表驱动 |
| 变更生效 | 需重启 | 实时 | 表驱动 |
| 启用/禁用 | 改配置重启 | UPDATE SQL | 表驱动 |
| 版本追踪 | 无 | policy_version | 表驱动 |
| addVisitor 覆盖 | ❌ 不覆盖 | ✅ 覆盖 | 表驱动 |
| 双重逻辑源风险 | 无 (仅 listByPage) | 无 (单一策略引擎) | 平 |
| 安全审计 | 无 | DB 行可审计 | 表驱动 |
4.1 40F/6F 模式为什么可以存在
40F/6F 逻辑是星河湾物业管理的历史遗留, 且:
- 其职责仅限于
listByPage访客列表的展示层默认值 - 不影响
detail()/addVisitor()的派梯权限 - 是"全物业" vs "非物业"的二分类, 不需要细粒度组织匹配
- 只有 2 个 zone, 不需要多 zone 支持
因此 40F/6F 的正确理解是: 这不是一个"楼层策略", 而是列表展示的 fallback 默认值。
4.2 广发基金不能套用的根本原因
广发基金 20 层是真正的访问控制策略 — 它必须限制 addVisitor 写入的 image_rule_ref 行。40F/6F 模式完全不接入这条路径, 强行接入会导致代码膨胀和双重逻辑源。
5. 推荐方案
方案 A: 保持表驱动不改 (★★★★★ 强烈推荐)
当前架构已经正确:
tenant_visitor_floor_policy 表 ← 数据
TenantVisitorFloorPolicyService ← 引擎
ImgPersonServiceImpl#detail() ← 替代 floorList
ImgPersonServiceImpl#listByPage ← P1 展示
PersonRuleServiceImpl#addVisitor ← UC-01 透传
唯一需要的变更: UPDATE allow_zone_ids 从 1 个 zone 到 20 个 zone。
方案 B: 重构 40F/6F 到表驱动 (★★★ 锦上添花, 非必须)
将 40F/6F 的硬编码逻辑也迁移到 tenant_visitor_floor_policy 表:
INSERT INTO tenant_visitor_floor_policy (...) VALUES (
'pm_40f_hardcoded_migration',
'21474e012cd14e26bc62771873b22562', -- xhwId
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560547135455232"]', -- 40F
NULL, 1, 1,
'物业公司:列表默认展示 40F (迁移自硬编码)'
);
然后在 listByPage 中删除 40F/6F 硬编码分支, 统一走策略引擎。此项非当前必须, 可作为技术债务清理。
6. 总结
| 问题 | 答案 |
|---|---|
| 能否参照 40F/6F 模式重实现广发 28F? | 技术上可以, 产出上不可行 |
| 根本原因 | 40F/6F 模式不接入 detail(), 导致 addVisitor 绕过策略 |
| 如果强行修补 | 双逻辑源、代码膨胀、维护灾难 |
| 正确做法 | 保持表驱动, 扩展 allow_zone_ids 即可 (零代码变更) |
| 额外收益 | 后续可将 40F/6F 也迁移到表驱动 (统一架构) |