fix: policy always checked regardless of caller-provided floors

Redesign addVisitor four-phase flow:
- Phase1: ALWAYS query person detail (orgIds for policy lookup)
- Phase2: candidate = caller floors or org floorList
- Phase3: ALWAYS check policy; intersect candidate with allow
- Phase4: empty set validation
Fixes UC-02 bypass: policy was entirely skipped when caller
provided floorIds. Now policy always constrains.
Bump v2.0.19
This commit is contained in:
反编译工作区
2026-05-05 19:47:01 +08:00
parent c5febc9905
commit f7da04caea
42 changed files with 2584 additions and 43 deletions
@@ -0,0 +1,79 @@
# 租户访客默认楼层策略 — 逻辑修正设计
**日期**2026-05-05
**状态**:待实施
**基线版本**v2.0.18 → v2.0.19
---
## 1. 问题
现有实现在 `PersonRuleServiceImpl.addVisitor` 中将策略查询包裹在 `if (!callerProvidedFloors)` 条件内,导致:
- **UC-02**(调用方传了 `floorIds`):策略被完全跳过,即使租户配置了生效策略也不会被执行
- 违反用户预期:**任何时候都应当查询策略**,有策略且生效就走策略路径
## 2. 修正后控制流
```
addVisitor(param, context):
// 阶段1: 确定候选楼层列表 candidate
if (param.floorIds 非空): ← UC-02
candidate = param.floorIds
else: ← UC-01
candidate = personService.detail() → floorList
if candidate 为空: 返回 76260531
// 阶段2: 查询策略(ALWAYS
policy = DAO.selectEnabledByOrgId(orgId) ← enabled=1
// 阶段3: 策略决定最终楼层
if (policy 存在):
effective = candidate ∩ policy.allow_zone_ids
if effective 为空: 返回 76260532(策略约束下无可用楼层)
else:
effective = candidate ← 无策略约束,原样使用
param.floorIds = effective
// 继续 zoneService.page → image_rule_ref → batchBind ...
```
## 3. 核心变更
| 变更点 | 改前 | 改后 |
|--------|------|------|
| 策略查询条件 | 仅在 `!callerProvidedFloors` 时查 | **ALWAYS 查** |
| UC-02 策略行为 | 完全跳过策略 | 策略生效时对 `floorIds` 求交 |
| UC-01 策略行为 | 查组织 `floorList` → 查策略 | 查组织 floorList → 查策略(不变) |
## 4. UC 对照矩阵
| 场景 | floorIds | 策略 | 改前结果 | 改后结果 |
|------|----------|------|---------|---------|
| UC-01 无策略 | 空 | 无 | `floorList` 全集 | `floorList` 全集 ✅ |
| UC-01 + 策略 | 空 | 有且生效 | `allow ∩ floorList` | `allow ∩ floorList` ✅ |
| UC-01 无交集 | 空 | allow 与 floorList 无交集 | 失败 76260532 | 失败 76260532 ✅ |
| UC-02 无策略 | 非空 | 无 | 按请求楼层 | 按请求楼层 ✅ |
| UC-02 + 策略 | 非空 | 有且生效 | **按请求楼层(绕过策略)❌** | **`请求楼层 ∩ allow` ✅** |
| UC-02 策略不包含 | 非空 | allow 不含请求楼层 | 成功开通(绕过)❌ | **失败 76260532 ✅** |
## 5. 错误码
| 错误码 | 场景 | 说明 |
|--------|------|------|
| 76260531 | 无可用楼层 | `floorList` 为空 或 求交后为空 |
| 76260532 | 策略交集为空 | `candidate ∩ allow` 无交集 |
| 76260533 | 策略配置错误 | allow 包含被访人无权访问的 zoneId |
## 6. 日志完善
| 关键路径 | 日志级别 | 内容 |
|---------|---------|------|
| UC-02 分支 | INFO | 调用方已指定楼层,候选楼层为 xxx |
| UC-01 分支 | INFO | 未传楼层,查组织 floorList 得到 xxx |
| 策略查询 | INFO | 查询 orgId=xxx 的策略 |
| 策略存在 | INFO | 找到启用策略 policyId=xxx allow=xxx |
| 无策略 | INFO | 未找到启用策略,使用候选楼层原值 |
| 求交成功 | INFO | 策略生效,最终楼层为 xxx |
| 求交为空 | WARN | 候选楼层与策略无交集,返回 76260532 |
| 空楼层 | WARN | 无可用楼层,返回 76260531 |