Files
starRiverProperty/docs/superpowers/specs/2026-05-05-tenant-visitor-floor-policy-redesign.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

7.9 KiB
Raw Permalink Blame History

租户访客默认楼层策略 — 业务逻辑重设计

废止 / 仅作历史归档(2026-05-06
现行规范租户访客默认楼层策略 — 迁入组织组件 为准:租户策略 allow_zone_ids 只能在组织 PersonService.detail 以「替代」写入 floorList禁止将策略定义为与被访人楼层 「求交(∩)」。电梯 addVisitor 不再读取策略表、不再做 ∩。
下文保留 2026-05-05 草稿中的电梯侧求交流程图,仅供对照旧表述,勿作为实现或验收依据。

日期2026-05-05
状态:已废止(见上)
设计依据:产品方案 租户访客默认楼层技术产品方案
涉及代码PersonRuleServiceImpl.addVisitor(现行代码已按 2026-05-06 规范移除电梯侧策略 ∩)


1. 业务规则(核心不变量)—— 历史草案,已被「替代语义」取代

规则 说明(历史)
策略全时生效 废止:电梯侧不再全时查策略表;策略在组织 detail 替代 floorList
策略即安全边界 allow_zone_ids 仍表达租户允许的 zone 集合;落实方式为 替代写入 detail,而非 ∩。
无策略不禁锢 组织未命中策略时,floorList 仍为 listByImageId 遍历结果。
交集为空必须拒绝 废止:不再使用 candidate ∩ allow;空 floorList/effective 走 76260531

2. 总流程

2.1 UC-01:调用方未传 floorIds

sequenceDiagram
    participant Caller as 调用方/BFF
    participant Elevator as 电梯应用<br/>cw-elevator-application
    participant Org as 组织服务<br/>ninca-common-component-organization
    participant PolicyDB as 策略表<br/>tenant_visitor_floor_policy

    Caller->>Elevator: POST /elevator/person/add/visitor<br/>{personId, visitorId}<br/>(不传 floorIds)
    
    Note over Elevator: 阶段1:查被访人信息
    Elevator->>Org: POST /component/person/detail<br/>{personId, businessId}
    Org-->>Elevator: PersonResult<br/>{floorList, organizationIds}
    
    Note over Elevator: 阶段2:候选楼层 = floorList
    Note over Elevator: floorList 来自组织服务
    
    Note over Elevator: 阶段3:查策略
    Elevator->>PolicyDB: SELECT * FROM tenant_visitor_floor_policy<br/>WHERE org_id IN (organizationIds) AND enabled=1
    PolicyDB-->>Elevator: policy 行 / 空
    
    alt 策略存在且生效
        Note over Elevator: 最终楼层 = floorList ∩ allow_zone_ids
        alt 交集为空
            Elevator-->>Caller: 失败 76260532<br/>(租户策略与被访人授权无交集)
        else 交集非空
            Note over Elevator: 继续开通流程
            Elevator-->>Caller: 成功,仅开通交集内楼层
        end
    else 无策略或未启用
        Note over Elevator: 最终楼层 = floorList(原值)
        Elevator-->>Caller: 成功
    end

2.2 UC-02:调用方传入 floorIds

sequenceDiagram
    participant Caller as 调用方/BFF
    participant Elevator as 电梯应用<br/>cw-elevator-application
    participant Org as 组织服务<br/>ninca-common-component-organization
    participant PolicyDB as 策略表<br/>tenant_visitor_floor_policy

    Caller->>Elevator: POST /elevator/person/add/visitor<br/>{personId, visitorId, floorIds:[...]}
    
    Note over Elevator: 阶段1:查被访人信息(仅取 organizationIds
    Elevator->>Org: POST /component/person/detail<br/>{personId, businessId}
    Org-->>Elevator: PersonResult<br/>{organizationIds}
    
    Note over Elevator: 阶段2:候选楼层 = 调用方传入的 floorIds
    
    Note over Elevator: 阶段3:查策略(ALWAYS
    Elevator->>PolicyDB: SELECT * FROM tenant_visitor_floor_policy<br/>WHERE org_id IN (organizationIds) AND enabled=1
    PolicyDB-->>Elevator: policy 行 / 空
    
    alt 策略存在且生效
        Note over Elevator: 最终楼层 = callerFloorIds ∩ allow_zone_ids
        alt 交集为空
            Elevator-->>Caller: 失败 76260532<br/>(请求楼层不在策略允许范围内)
        else 交集非空
            Elevator-->>Caller: 成功,仅开通交集内楼层
        end
    else 无策略或未启用
        Note over Elevator: 最终楼层 = callerFloorIds(原值)
        Elevator-->>Caller: 成功
    end

3. 控制流伪代码

addVisitor(param, context):
  // === 阶段1:查被访人组织信息(ALWAYS===
  detailResult = personService.detail(personId, businessId)
  if failed: return detailResult.error
  person = detailResult.data

  // === 阶段2:确定候选楼层 ===
  if param.floorIds 非空:                ← UC-02
      candidate = param.floorIds
  else:                                  ← UC-01
      candidate = person.floorList
      if candidate 为空: return 76260531

  // === 阶段3ALWAYS 查策略 ===
  policy = findEnabledPolicy(person.organizationIds)
  if policy != null:
      effective = intersect(candidate, policy.allow_zone_ids)
      if effective 为空: return 76260532
  else:
      effective = candidate

  // === 阶段4:空集校验 ===
  if effective 为空: return 76260531
  param.floorIds = effective

  // === 阶段5:开通流程(不变)===
  zoneService.page → image_rule_ref → batchBind → ...

4. 场景对照矩阵

场景 调用方 floorIds 策略状态 候选楼层 最终结果
UC-01 无策略 无策略行 floorList floorList
UC-01 + 策略通过 有且生效 floorList floorList ∩ allow
UC-01 + 策略无交集 allow 与 floorList 无交集 floorList 失败 76260532
UC-01 被访人无楼层 任意 floorList=空 失败 76260531
UC-02 无策略 [A,B] 无策略行 [A,B] [A,B]
UC-02 + 策略包含 [A,B] allow=[A,C] [A,B] [A]
UC-02 + 策略不包含 [A,B] allow=[C,D] [A,B] 失败 76260532

与当前实现的差异

场景 当前实现 重设计后
UC-02 + 策略存在 绕过策略,按请求楼层开通 策略求交
UC-02 + 策略不包含请求楼层 成功开通(本应拒绝) 失败 76260532

5. 错误码

错误码 触发条件 说明
76260531 候选楼层为空 被访人 floorList 为空,或有效楼层为空
76260532 candidate ∩ allow 无交集 策略约束了可访楼层,但候选楼层全部不在允许范围内
76260533 策略配置错误 allow_zone_ids 包含被访人无权限的 zoneId

6. 日志规范

关键路径 日志内容
入口 businessId, personId, visitorId, requestFloorSize
UC-01 调用方未传楼层,取被访人默认楼层 floorList=xxx
UC-02 调用方已指定楼层,候选楼层=candidate
策略查询 查询组织 orgIds=xxx
策略命中 找到启用策略 policyId=xxx allow=xxx
策略未命中 未找到启用策略,使用候选楼层原值
求交成功 策略生效,最终楼层=effective
求交为空 候选楼层与策略无交集,返回 76260532
空楼层 无可用楼层,返回 76260531

7. 实施范围

变更点 影响 风险
UC-02 增加 personService.detail() 调用 多一次 RPC(数百微秒)
UC-02 增加策略求交逻辑 对传入楼层做过滤 中——调用方可能不符合预期
删除 callerProvidedFloors 分支 简化代码结构

兼容性提醒:UC-02 行为改变意味着:之前能开通的请求(传入策略允许范围外的楼层)现在会失败。需通知集成方。