Files
starRiverProperty/docs/visitor-floor-policy-user-guide.md
hpd840321 db2cb1966c docs: add visitor floor policy user guide and implementation log
- 功能清单与使用手册:23项功能清单、策略配置说明、部署运维、22项验收检查表

- 实施操作日志:9阶段操作流水、commit清单、关键文件索引、103服务器全库备份记录

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-26 09:27:57 +08:00

16 KiB
Raw Permalink Blame History

租户访客楼层策略 — 功能清单 & 使用手册

版本: v2.0.17 增量发布 分支: feature/guangfa-28f-hardcoded 日期: 2026-05-10 适用对象: 运维人员、业务管理人员(业主)


一、功能概述

1.1 背景

星中心(星河湾)电梯门禁系统中,不同组织的访客应被限制在特定楼层活动。例如:

  • 广发基金28-38F)的访客 → 仅可到达 28F
  • 物业管理公司的访客 → 可到达 28F / 6F

本功能通过租户访客楼层策略实现:当人员属于某个组织且有访客身份时,系统自动将其可到达楼层替换为策略指定的楼层集合。

1.2 策略语义变更

版本 语义 说明
旧 (V1) INTERSECT_ALLOWLIST 策略楼层与人员楼层求交集
新 (V2) REPLACE_ALLOWLIST 策略楼层整表替换人员楼层

变更原因:原求交语义会导致 40F 人员即使有策略也因求交结果为空而无法到达任意楼层;替换语义直接赋予目标楼层,逻辑清晰且满足业务需求。

1.3 架构变化

V1(旧):
  ┌─ elevator-app ─────────────────────┐
  │  TenantVisitorFloorPolicyService   │ ← 电梯侧自身查策略表
  │  (查询同库 tenant_visitor_floor_policy) │
  └────────────────────────────────────┘

V2(新):
  ┌─ elevator-app ──┐     Feign HTTP     ┌─ component-organization ────┐
  │  detail()       │ ─────────────────→ │  TenantVisitorFloorPolicy  │
  │  listByPage()   │ ←───────────────── │  .replacementZoneIds...    │
  └─────────────────┘    返回替换楼层     │  (唯一策略维护点)          │
                                          └────────────────────────────┘

V2 唯一策略维护点: 组织服务库 component-organization,电梯侧策略表已废弃(保留仅作历史参考)。


二、功能清单

2.1 策略数据管理

# 功能项 说明 验收标准
F1 策略表创建 component-organization 库建表 tenant_visitor_floor_policy 表结构正确,主键/唯一键/索引齐全
F2 广发基金 28F 策略 广发基金管理有限公司 org 配置:访客默认 28F 广发员工邀约访客 → 访客楼层为 28F
F3 物业管理 28F+6F 策略 7 个物业相关组织节点配置:访客默认 28F+6F 物业员工邀约访客 → 访客楼层为 28F+6F
F4 策略类型归一化 所有策略 policy_type = REPLACE_ALLOWLIST 无 INTERSECT_ALLOWLIST 残留
F5 幂等初始化 种子 SQL 支持 ON DUPLICATE KEY UPDATE 重复执行不报错,数据一致

2.2 策略调用(服务端)

# 功能项 说明 验收标准
F6 detail() 策略替换 人员详情接口按 org_id 逐级命中策略,替换 floorList 有策略的组织 → floorList 被替换;无策略 → 保留原始
F7 listByPage() 策略替换 分页查询接口同步执行策略替换 分页结果中访客记录楼层被替换
F8 addVisitor 策略应用 添加访客时校验并应用策略楼层 访客创建后楼层为策略指定值
F9 按 org_id 顺序匹配 多 org_id 时按传入顺序遍历,首个命中即返回 命中顺序与预期一致

2.3 日志与可观测性

# 功能项 说明 验收标准
F10 POLICY-HIT 日志 策略命中时记录 orgId、policyId、allowZones INFO 级别,可追溯
F11 POLICY-MISS 日志 无策略时 DEBUG 记录 不产生 WARN/ERROR
F12 POLICY-EMPTY 日志 策略 allow_zone_ids 为空时 WARN 记录 触发时明确标记
F13 POLICY-FALLBACK 日志 查询异常时 WARN 记录,不影响 detail 异常不回抛,fallback 到原始 floorList
F14 DETAIL 日志增强 detail() 入口/出口日志含 personId、businessId 可追踪每次 detail 调用
F15 日志级别优化 [POLICY] 入口从 INFO→DEBUG,减少冗馀 正常运行不产生 POLICY 入口日志

2.4 发布与部署

# 功能项 说明 验收标准
F16 component-org 发布脚本 自动构建、打包 JAR + DDL + 配置 + start.sh 产物完整可部署
F17 电梯应用发布脚本 自动构建 V2 fat JAR + 含策略 DDL 的完整发布包 产物完整可部署
F18 DDL 执行说明 发布包内含 DDL README,标明执行顺序和目标库 运维按说明执行无歧义
F19 版本升级说明书 含升级步骤、回滚方案、配置变更清单 运维可独立执行升级

2.5 代码健壮性

# 功能项 说明 验收标准
F20 Graceful Shutdown 30s 排空等待,在途请求完成后关闭 发布期无请求中断
F21 Bean 冲突修复 TypeFilter 排除 GroupPersonSyn* 避免重复 bean 服务启动正常,无 bean 冲突
F22 Logback 配置内嵌 recognition-logbox.xml 打包至 fat JAR classpath 无需外部 --logging.config 参数
F23 消息源修复 去掉 messages basename 的 _zh_CN 后缀 无 ResourceBundle WARN

三、策略配置说明

3.1 策略表结构(component-organization 库)

tenant_visitor_floor_policy (
  id              VARCHAR(32)  PRIMARY KEY,          -- 策略ID(业务唯一)
  org_id          VARCHAR(32),                       -- 组织节点ID(策略匹配键)
  policy_type     VARCHAR(32) DEFAULT 'REPLACE_ALLOWLIST',  -- 策略类型
  allow_zone_ids  TEXT,                              -- JSON 数组,允许的 zoneId 列表
  building_id     VARCHAR(64) NULL,                  -- 楼栋(预留)
  enabled         TINYINT(1) DEFAULT 1,              -- 启用标志
  policy_version  BIGINT DEFAULT 1,                  -- 版本号
  remark          VARCHAR(256),                      -- 备注
  ...
  UNIQUE KEY uk_org_building (org_id, building_id)   -- 每组织每楼栋一条策略
)

关键约束:

  • 每组织节点(org_id)最多一条启用策略
  • 策略匹配按 org_id 精确匹配(不向上回溯组织树)
  • allow_zone_ids 为标准 JSON 字符串数组:["zoneId1","zoneId2"]

3.2 楼层编码对照

Zone ID 楼层 说明
605560540432957440 1F 首层(默认所有人员可达)
605560541473144832 6F 物业管理办公层
605560545117995008 28F 广发基金办公层

3.3 策略执行逻辑

detail(personId):
  1. 通过 personId 查询人员详情(含 floorList、organizationIds
  2. 遍历 organizationIds 列表:
     a. 按 org_id 查 tenant_visitor_floor_policyenabled=1
     b. 找到 → 解析 allow_zone_ids → 替换 floorList → 返回
     c. 未找到 → 继续下一个 org_id
  3. 全部未命中 → 保留 listByImageId 原始 floorList

3.4 策略配置方法

新增策略:

INSERT INTO tenant_visitor_floor_policy (
  id, org_id, policy_type, allow_zone_ids, 
  enabled, policy_version, remark, created_at, updated_at
) VALUES (
  'custom_policy_001',                    -- 策略ID(唯一)
  'org_id_value',                          -- 组织节点ID
  'REPLACE_ALLOWLIST',                     -- 策略类型
  '["605560545117995008"]',               -- 目标楼层列表
  1, 1, '备注说明',
  UNIX_TIMESTAMP(NOW()) * 1000,
  UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE ...;

停用策略: 设置 enabled = 0

修改策略: 更新 allow_zone_ids + policy_version = policy_version + 1

删除策略: 物理删除行(不建议,建议停用)

注意: 所有策略变更需在 component-organization 库执行,电梯侧策略表已废弃。


四、部署运维说明

4.1 服务架构

                         Nginx :8090
                            │
               ┌────────────┼────────────┐
               ▼            ▼            ▼
        elevator-app   cwos-portal    frontend SPAs
        :18081         :18080
               │            │
               ▼            ▼
        component-org    CRK 人脸识别
        :17016           (GPU 后端)

策略查询路径: elevator-app → Feign HTTP → component-org → MySQL component-organization

4.2 数据库变更

执行目标库: component-organization

执行顺序(2 步):

  1. 建表:organization_tenant_visitor_floor_policy.sql
  2. 种子数据:organization_tenant_visitor_floor_policy_init_tenants.sql(幂等,可重复执行)

验证:

-- 检查策略表已创建
SHOW TABLES LIKE 'tenant_visitor_floor_policy';

-- 检查种子数据已写入
SELECT id, org_id, policy_type, allow_zone_ids, remark 
FROM tenant_visitor_floor_policy WHERE enabled=1;

-- 检查索引
SHOW INDEX FROM tenant_visitor_floor_policy;

4.3 发布步骤

component-organization 服务:

# 1. 构建发布包
./source/scripts/build/release-component-organization.sh 2.9.5

# 2. 产物位置
#    source/backend/ninca-common-component-organization/releases/
#    ninca-common-component-organization-2.9.5-xinghewan-YYYYMMDD.zip

# 3. 部署 JAR + 配置 + DDL → 目标服务器
# 4. 执行 DDL(见 4.2
# 5. 启动服务
cd /path/to/deploy && bash start.sh

电梯应用:

# 1. 构建发布包
./source/scripts/build/release-cw-elevator-application.sh 2.0.x

# 2. 产物位置
#    source/backend/cw-elevator-application/releases/
#    cw-elevator-application-V2.0.x.YYYYMMDD.zip

# 3. 部署 JAR + 配置 + DDL → 目标服务器
# 4. 执行 DDL(见 4.2
# 5. 启动服务
cd /path/to/deploy && bash start.sh

4.4 配置变更

component-organizationbootstrap.properties:

# 无新增配置项,复用现有 datasource 连接 component-organization 库
spring.datasource.url=jdbc:mysql://.../component-organization

电梯应用bootstrap.properties:

# 无新增配置项,策略查询通过 Feign 调用 component-org 完成
# logging 配置(策略追踪日志已编码在代码中)
logging.level.cn.cloudwalk.service.organization.policy=INFO

4.5 日志说明

日志标识 级别 含义
[POLICY-HIT] INFO 策略命中,记录 orgId、policyId、替换楼层列表
[POLICY-MISS] DEBUG 未找到组织策略(正常运行)
[POLICY-EMPTY] WARN 策略存在但 allow_zone_ids 解析为空
[POLICY-FALLBACK] WARN 策略查询异常,已回退到原始 floorList
[POLICY-RESULT] INFO batch 接口命中结果
[POLICY-ERR] WARN allow_zone_ids JSON 解析异常
[DETAIL] INFO detail() 调用入口/出口日志
[DETAIL-POLICY] DEBUG detail() 内策略替换前后 floorList 对比
[LIST-PAGE-POLICY] DEBUG listByPage() 策略命中详情
[ADDV-DETAIL] DEBUG addVisitor 中 detail floorList 和 orgIds

4.6 回滚方案

如果发布后出现问题:

方案 A:停用策略(快速回滚,推荐)

UPDATE tenant_visitor_floor_policy SET enabled=0;
-- 恢复方式:SET enabled=1

方案 B:回退 JAR 版本

  1. 停止服务
  2. 替换为上一版本 JAR
  3. 重启服务
  4. DDL 无需回滚(建表语句幂等)

五、验收检查表

5.1 功能验收

# 验收项 验收方法 结果
1 广发组织访客 → 楼层被替换为 28F 创建广发员工访客 → 查访客楼层是否为 [28F] □ 通过 □ 不通过
2 物业组织访客 → 楼层被替换为 28F+6F 创建物业员工访客 → 查访客楼层是否为 [28F, 6F] □ 通过 □ 不通过
3 无策略组织 → 楼层保持原始值 创建无策略组织访客 → 楼层与 listByImageId 一致 □ 通过 □ 不通过
4 策略停用 (enabled=0) → 不生效 停用策略后创建访客 → 楼层不被替换 □ 通过 □ 不通过
5 多 org_id 按顺序匹配 人员归属多个 org,首个有政策的应命中 □ 通过 □ 不通过
6 detail() 接口正常 调用 detail() → 返回符合策略的 floorList □ 通过 □ 不通过
7 listByPage() 分页正常 翻页查询 → 访客记录楼层正确 □ 通过 □ 不通过
8 策略查询失败 → 不阻断业务 模拟策略表不可用 → detail() 仍正常返回原始数据 □ 通过 □ 不通过

5.2 数据验收

# 验收项 验收方法 结果
9 策略表已创建 SHOW TABLES LIKE 'tenant_visitor_floor_policy' □ 通过 □ 不通过
10 广发基金策略已配置 SELECT * FROM tenant_visitor_floor_policy WHERE id LIKE 'gf_%' □ 通过 □ 不通过
11 物业 7 条策略已配置 SELECT COUNT(*) FROM tenant_visitor_floor_policy WHERE id LIKE 'pm_%' □ 通过 □ 不通过
12 所有策略类型为 REPLACE_ALLOWLIST SELECT DISTINCT policy_type FROM tenant_visitor_floor_policy □ 通过 □ 不通过
13 索引完整 SHOW INDEX FROM tenant_visitor_floor_policy → idx_org_enabled 存在 □ 通过 □ 不通过

5.3 日志验收

# 验收项 验收方法 结果
14 策略命中有 INFO 日志 grep 'POLICY-HIT' info.log □ 通过 □ 不通过
15 策略未命中仅有 DEBUG grep 'POLICY-MISS' info.log → 无输出 □ 通过 □ 不通过
16 无 ResourceBundle WARN grep 'ResourceBundle' info.log → 无 (messages basename 修复) □ 通过 □ 不通过
17 detail() 可追踪 grep 'DETAIL' info.log → 含 personId 上下文 □ 通过 □ 不通过

5.4 部署验收

# 验收项 验收方法 结果
18 component-org 启动正常 bash start.sh → 端口监听 17016 □ 通过 □ 不通过
19 电梯应用启动正常 bash run.sh → 端口监听 18081 □ 通过 □ 不通过
20 服务间 Feign 调用正常 curl detail() → 返回数据含策略楼层 □ 通过 □ 不通过
21 Graceful Shutdown 生效 停止服务 → 日志显示 drain 30s □ 通过 □ 不通过
22 无 Bean 冲突 启动日志无 GroupPersonSyn 相关冲突 □ 通过 □ 不通过

六、常见问题

Q: 为什么新建了策略但访客楼层没变? A: 检查 enabled=1;确认人员所属 org_id 与策略 org_id 精确匹配;检查策略 policy_type 是否为 REPLACE_ALLOWLIST

Q: 电梯侧策略表还起作用吗? A: V2 不再查询电梯侧 tenant_visitor_floor_policy 表。该表仅保留作历史参考。所有策略需在 component-organization 库维护。

Q: 一个组织可以配置多条策略吗? A: 唯一键 uk_org_building 限制每组织每楼栋一条策略。如需多策略,需通过不同的 org_id(子组织节点)实现。

Q: 如何确认当前生效的策略? A: 查询 component-organizationtenant_visitor_floor_policy WHERE enabled=1,并查看日志中的 [POLICY-HIT] 记录。

Q: 发布时需要停服吗? A: 组件服务(component-org / elevator-app)需要重启;使用 Graceful Shutdown30s 排空),在途请求不会中断。