Files
starRiverProperty/docs/superpowers/specs/2026-05-01-org-policy-verify-design.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

13 KiB
Raw Permalink Blame History

org_id 策略修复 — 无鉴权验证方案设计

文档性质:验证方案设计说明
设计日期2026-05-01
状态:待评审
关联 Specdocs/superpowers/specs/2026-05-01-org-id-policy-fix-design.md
被测应用cw-elevator-applicationV2, port 18081


1. 目标

通过无鉴权 HTTP 调用 POST /elevator/person/add/visitorPOST /elevator/passRule/image,验证 org_id 策略修复的 7 个核心场景,确保业务逻辑满足需求。

不验证:鉴权正确性、Feign 调用链、图库绑定、Consul 服务发现。


2. 完整业务流程与接口调用链

2.1 业务全景

Step 0: 访客邀约(上游,非电梯侧)
  ┌─────────────────────────────────────────────────┐
  │ 第三方业务系统(BFF / intelligent / 登记页)      │
  │   ├── 创建访客人员档案(cw_is_person + 访客标签) │
  │   └── 确定被访人(host personId                │
  └─────────────────────────────────────────────────┘
  → 访客已存在于组织库中,personId 可用
  → 本方案使用预置测试访客(919900... 号段),跳过此步

Step 1: 设置访客可访问楼层(电梯侧,核心验证目标)
  POST /elevator/person/add/visitor
  ┌──────────────────────────────────────────────────────┐
  │ ① Feign → /component/person/detail                  │
  │    取被访人 floorList + organizationIds              │
  │                                                      │
  │ ② 查 tenant_visitor_floor_policy                     │
  │    WHERE org_id = ?  (从 organizationIds 取)         │
  │                                                      │
  │ ③ 二选一:                                          │
  │    有策略 → effectiveFloors = allow(直接替换)      │
  │    无策略 → effectiveFloors = floorList              │
  │    allow 含无效值 → fail 76260533                    │
  │                                                      │
  │ ④ Feign → /sysetting/zone/page                      │
  │    取楼栋 + 图库 imageStoreId                        │
  │                                                      │
  │ ⑤ 写 image_rule_ref(访客 personId ← visitorId    │
  │                                                      │
  │ ⑥ Feign → /component/imagestore/person/batchBind    │
  │    访客绑定到楼栋图库                                │
  └──────────────────────────────────────────────────────┘

Step 2: 回读验证(确认楼层写入正确)
  POST /elevator/passRule/image
  Body: { "personId": "<visitorId>" }
  → 返回该访客可访问的 zoneId 列表
  → 与期望楼层比对

2.2 接口调用细节

POST /elevator/person/add/visitor

项目
Method POST
Path /elevator/person/add/visitor
Content-Type application/json
鉴权模式 noauth-probe(仅 businessid 头)

请求体:

{
    "personId": "1060601019894960128",
    "visitorId": "9199000100000000001",
    "floorIds": [],
    "begVisitorTime": 1751319780000,
    "endVisitorTime": 1751406180000
}
字段 必填 说明
personId 被访人 ID(决定 floorList 和 org_id 来源)
visitorId 访客 ID(落库 image_rule_ref 的 person_id
floorIds 空数组=UC-01 由电梯补全;非空=UC-02 调用方指定
begVisitorTime Unix 毫秒,访客有效期开始
endVisitorTime Unix 毫秒,访客有效期结束

响应体(成功):

{ "success": true, "code": "0", "data": true }

响应体(策略拒绝):

{ "success": false, "code": "76260533", "message": "策略配置了被访人无权访问的楼层,请联系管理员" }

POST /elevator/passRule/image(回读)

项目
Method POST
Path /elevator/passRule/image
Body { "personId": "<visitorId>" }

响应体:

{
    "success": true,
    "data": {
        "datas": [
            { "zoneId": "605560545117995008", "zoneName": "28F" }
        ]
    }
}

2.3 策略在流程中的位置

add/visitor 请求进入
  │
  ├─ personId → Feign detail() → { floorList, organizationIds }
  │
  ├─ organizationIds[0] → SELECT ... FROM tenant_visitor_floor_policy
  │                        WHERE org_id = ?   ← 新:org_id 粒度
  │                        AND enabled = 1
  │
  ├─ 有策略行 → effectiveFloors = allow          ← 二选一,不求交
  │             allow ⊆ floorList 校验
  │             └─ 不通过 → 76260533
  │
  └─ 无策略行 → effectiveFloors = floorList      ← 默认行为

3. 测试环境

项目
电梯服务 http://127.0.0.1:18081
MySQL 组织库 192.168.3.12:3307 / component-organization
MySQL 电梯库 192.168.3.12:3307 / cw-elevator-application
租户 businessId = 2524639890ba4f2cba9ba1a4eeaa4015
调用方式 noauth-probe(仅 businessid 头,无鉴权 token

4. 测试矩阵(7 个用例)

4.1 测试数据

实体 ID
1403艾斯 org 72fb65ec5de94201b909a98b8bae1892
1405一博环保 org 2095de3d541f44eba686c78fda68336f
星中心物业 org f216235e54ca42bfa0379e69b3754aff
广发基金 org 488b8ad049bb43408a6fbcc50bcb89ac
陈国辉(1403+星中心) 1060601019894960128
王姣(1405 1090779433129840640
秦夏(广发基金) 1072908835884208128
28F zone 605560545117995008
6F zone 605560541473144832

4.1.1 访客数据

测试使用开发库中已预置的专用测试访客(号段 9199000100000000001~9199000100000000020,均已标注"访客"标签 LABEL_ID = ed2dbab6d6234a7287106b80857c819e)。这些是测试专用人员,非真实业务访客。

add/visitor 写入 image_rule_ref 时以 visitorId 为键,为避免同一访客的多次写入相互干扰,每个用例轮换使用不同测试访客。

用例 visitorId 访客标识
T1 9199000100000000001 测试访客AUTO-01
T2 9199000100000000002 测试访客AUTO-02
T3 9199000100000000003 测试访客AUTO-03
T4 9199000100000000004 测试访客AUTO-04
T5 9199000100000000005 测试访客AUTO-05
T6 9199000100000000006 测试访客AUTO-06
T7 9199000100000000007 测试访客AUTO-07

4.2 用例

ID 场景 被访人 策略 org allow enabled 期望
T1 有策略→allow 替换 floorList 陈国辉 1403 [28F] 1 passRule 返回仅 [28F]
T2 无策略→floorList 王姣 1405 passRule 返回 floorList 全集
T3 allow 含无效zone→拒绝 陈国辉 1403 [28F, 99F] 1 code=76260533
T4 多组织命中第一个策略 陈国辉 1403+星中心 1403有,星中心无 1 命中1403→[28F]
T5 enabled=0 等同无策略 陈国辉 1403 [28F] 0 passRule 返回 floorList 全集
T6 UC-02 策略优先 陈国辉 1403 [28F] 1 传floorIds被忽略→[28F]
T7 广发基金迁移验证 秦夏 广发基金 [28F] 1 迁移后→仅 [28F]

5. 脚本结构

文件maven-cw-elevator-application/tools/visitor_floor_verification/scripts/verify_org_policy_fix.py

verify_org_policy_fix.py
  │
  ├── Phase 0: health_check()
  │   GET /actuator/health
  │
  ├── Phase 1: prepare_test_data()
  │   ① INSERT policy_t1_1403 (org=1403, allow=[28F], enabled=1)
  │   ② INSERT policy_t3_invalid (org=1403, allow=[28F,99F], enabled=1)
  │   ③ INSERT policy_t5_disabled (org=1403, allow=[28F], enabled=0)
  │   ④ UPDATE 广发基金 SET org_id='488b8adb...' WHERE id='gf_vstr_...'
  │
  ├── Phase 2: run_all_cases()
  │   每个用例:
  │     POST add/visitor → 检查 HTTP 200 + code + success
  │     成功 → POST passRule/image → 比对 zoneIds
  │     失败 → 检查 code == 期望错误码
  │
  ├── Phase 3: cleanup_test_data()
  │   ① DELETE 测试策略行
  │   ② UPDATE 广发基金 SET org_id=NULL
  │
  └── Phase 4: print_report()
      输出 JSON 报告到 report/org-policy-fix-verify-{timestamp}.json

5.1 HTTP 请求格式(noauth-probe

headers = {
    "Content-Type": "application/json",
    "businessid": "2524639890ba4f2cba9ba1a4eeaa4015"
}

# add/visitor
POST /elevator/person/add/visitor
{
    "personId": "1060601019894960128",
    "visitorId": "<轮换访客ID>",
    "floorIds": [],
    "begVisitorTime": <now_ms>,
    "endVisitorTime": <now_ms + 86400000>
}

# passRule/image(回读)
POST /elevator/passRule/image
{
    "personId": "<visitorId>"
}

6. 数据准备与清理

6.1 准备 SQL

-- T1/T4/T6 共用
INSERT INTO tenant_visitor_floor_policy
(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at)
VALUES ('policy_t1_1403', '72fb65ec5de94201b909a98b8bae1892', NULL, 'INTERSECT_ALLOWLIST',
 '["605560545117995008"]', NULL, 1, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);

-- T3
INSERT INTO tenant_visitor_floor_policy
(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at)
VALUES ('policy_t3_invalid', '72fb65ec5de94201b909a98b8bae1892', NULL, 'INTERSECT_ALLOWLIST',
 '["605560545117995008","605560540000000000"]', NULL, 1, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);

-- T5
INSERT INTO tenant_visitor_floor_policy
(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at)
VALUES ('policy_t5_disabled', '72fb65ec5de94201b909a98b8bae1892', NULL, 'INTERSECT_ALLOWLIST',
 '["605560545117995008"]', NULL, 0, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);

-- T7: 广发基金迁移
UPDATE tenant_visitor_floor_policy
SET org_id = '488b8ad049bb43408a6fbcc50bcb89ac'
WHERE id = 'gf_vstr_policy_guangfa_fund_001x';

6.2 清理 SQL

DELETE FROM tenant_visitor_floor_policy WHERE id IN ('policy_t1_1403','policy_t3_invalid','policy_t5_disabled');
UPDATE tenant_visitor_floor_policy SET org_id = NULL WHERE id = 'gf_vstr_policy_guangfa_fund_001x';

6.3 执行顺序

prepare:
  INSERT policy_t1_1403           (T1/T4/T6 使用)
  INSERT policy_t3_invalid        (T3 使用)
  INSERT policy_t5_disabled       (T5 使用)
  UPDATE 广发基金 SET org_id      (T7 使用)

execute:
  T1: 陈国辉 + policy_t1_1403     → allow=[28F]
  T3: 陈国辉 + policy_t3_invalid  → 76260533(需先改1403的org_id或先DELETE T1再切换)
  T4: 陈国辉(多组织) + policy_t1  → allow=[28F]
  T5: 陈国辉 + policy_t5_disabled → floorList全集
  T2: 王姣(无策略)                → floorList全集
  T6: 陈国辉(UC-02) + policy_t1   → allow=[28F]
  T7: 秦夏 + 广发基金策略          → allow=[28F]

cleanup:
  DELETE 测试行
  UPDATE 广发基金 SET org_id=NULL

T3 执行前需确保 policy_t1_1403 不生效(相同 org_id 只能有一条启用策略)。方案:T3 前 DELETE policy_t1_1403T3 后恢复 INSERT。


7. 验证判定规则

判定项 通过条件
HTTP 状态 add/visitor 返回 200
业务成功 response.success == true
业务失败 response.code == "76260533"
楼层匹配 passRule/image 返回的 zoneId 集合 == 期望集合(顺序无关)
脱敏 报告中不出现 PII(姓名/手机号)

8. 输出

report/org-policy-fix-verify-{timestamp}.json

包含:每个用例的请求/响应摘要、passRule 结果、期望 vs 实际对比、通过/失败汇总。


9. 文件清单

文件 操作 说明
tools/visitor_floor_verification/scripts/verify_org_policy_fix.py 新增 主验证脚本
tools/visitor_floor_verification/report/ 报告输出目录(已有)

本设计说明待评审,通过后转入 implementation。