From db2cb1966cfdc362564baeecf672270e8e3713d2 Mon Sep 17 00:00:00 2001 From: hpd840321 Date: Tue, 26 May 2026 09:27:57 +0800 Subject: [PATCH] docs: add visitor floor policy user guide and implementation log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 功能清单与使用手册:23项功能清单、策略配置说明、部署运维、22项验收检查表 - 实施操作日志:9阶段操作流水、commit清单、关键文件索引、103服务器全库备份记录 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- ...visitor-floor-policy-implementation-log.md | 410 ++++++++++++++++++ docs/visitor-floor-policy-user-guide.md | 356 +++++++++++++++ 2 files changed, 766 insertions(+) create mode 100644 docs/visitor-floor-policy-implementation-log.md create mode 100644 docs/visitor-floor-policy-user-guide.md diff --git a/docs/visitor-floor-policy-implementation-log.md b/docs/visitor-floor-policy-implementation-log.md new file mode 100644 index 00000000..b3a80213 --- /dev/null +++ b/docs/visitor-floor-policy-implementation-log.md @@ -0,0 +1,410 @@ +# 租户访客楼层策略 — 实施操作日志 + +> **项目**: 星河湾星中星 (starRiverProperty) +> **功能**: 租户访客楼层策略(REPLACE_ALLOWLIST) +> **分支**: feature/guangfa-28f-hardcoded +> **实施人**: Huang Ping (hpd840321) +> **基线**: v2.0.17 + +--- + +## 操作日志总览 + +| 阶段 | 时间段 | 操作次数 | 涉及文件 | +|------|--------|---------|---------| +| **0. 生产环境全库备份** | **04-23 17:28** | **1** | **data-backups/** | +| Ⅰ. 需求分析与设计 | 04-30 ~ 05-05 | 4 | docs/ | +| Ⅱ. 代码重组 | 05-07 ~ 05-09 | 15 | 全局目录 | +| Ⅲ. 广发 28F 楼层策略设计 | 05-09 | 1 | docs/ | +| Ⅳ. 策略数据归一化 | 05-10 01:14 | 1 | SQL + 发布脚本 | +| Ⅴ. DDL 修正 | 05-10 06:43 | 1 | SQL DDL | +| Ⅵ. 发布脚本 | 05-10 06:54 | 1 | scripts/build/ | +| Ⅶ. 日志系统增强 | 05-10 09:37 ~ 13:22 | 5 | Java service | +| Ⅷ. 启动与 Bean 修复 | 05-10 11:44 ~ 12:32 | 4 | Java config | +| Ⅸ. 上线日志分析 | 05-10 15:32 | 1 | docs/ | + +--- + +## 阶段0:生产环境全库备份(04-23 17:28) + +### 操作 + +在实施任何数据库变更前,对 **103 生产服务器**(10.0.22.103)全部数据库执行完整 `mysqldump` 备份,共计 **11 个库 / 3.1 GB**。 + +### 备份清单 + +| # | 数据库文件 | 大小 | 说明 | +|---|-----------|------|------| +| 1 | `component-organization_2026_04_23_17_28_33.sql.gz` | 212 MB | **策略表目标库** — 组织服务库 | +| 2 | `cwos_portal_2026_04_23_17_28_33.sql.gz` | 212 MB | 物业管理门户 | +| 3 | `34_2026_04_23_17_28_33.sql.gz` | 2.5 GB | 电梯应用主库 | +| 4 | `12_2026_04_23_17_28_33.sql.gz` | 158 MB | 电梯应用辅助库 | +| 5 | `alarm_deploy_2026_04_23_17_28_33.sql.gz` | 15 MB | 报警服务 | +| 6 | `ninca_common_2026_04_23_17_28_33.sql.gz` | 6.0 MB | 公共服务 | +| 7 | `cwos_manager_2026_04_23_17_28_33.sql.gz` | 51 KB | CWOS 管理 | +| 8 | `cloudwalk_device_thirdparty_2026_04_23_17_28_33.sql.gz` | 4.0 KB | 三方设备 | +| 9 | `ods_2026_04_23_17_28_33.sql.gz` | 485 KB | ODS 数据 | +| 10 | `g_2026_04_23_17_28_33.sql.gz` | 3.9 KB | 通用库 G | +| 11 | `p_2026_04_23_17_28_33.sql.gz` | 1.1 KB | 通用库 P | + +### 备份信息 + +| 项目 | 内容 | +|------|------| +| **服务器** | 10.0.22.103 | +| **时间戳** | 2026-04-23 17:28:33 | +| **工具** | `mysqldump` + `gzip` | +| **总量** | 11 个数据库,合计 3.1 GB | +| **备份存储** | `data-backups/`(仓库根目录) | +| **备份目的** | 策略功能上线前全量快照,确保 DDL/DML 操作可回滚 | + +### 恢复方式 + +```bash +# 单库恢复示例(component-organization): +zcat data-backups/component-organization_2026_04_23_17_28_33.sql.gz \ + | mysql -h 10.0.22.103 -u root -p component-organization + +# 全库恢复: +cd data-backups +for f in *.sql.gz; do + dbname=$(echo "$f" | sed 's/_2026.*//') + echo "Restoring $dbname from $f ..." + zcat "$f" | mysql -h 10.0.22.103 -u root -p "$dbname" +done +``` + +--- + +## 阶段Ⅰ:需求分析与设计(04-30 ~ 05-05) + +### 2026-04-30 策略重设计文档 + +**操作**: 撰写策略重设计文档,含序列图 +**涉及文件**: +- `docs/superpowers/specs/*-policy-redesign.md` +- `docs/superpowers/specs/*-initialization-flow-analysis.md` + +**变更内容**: +- 确定了 REPLACE_ALLOWLIST 替换语义(VS INTERSECT_ALLOWLIST 求交) +- 分析了策略初始化流程 + +### 2026-05-01 功能验证脚本 + +**操作**: 新增 API 对拍 + 租户策略 + 集成测试脚本 +**涉及文件**: `scripts/test-env/verify-functional.sh` + +**变更内容**: +- 增加策略相关测试用例 +- tenant policy + integration 验证 + +### 2026-05-05 策略评估逻辑修正 + +**操作**: 修复策略始终参与评估的 bug +**涉及文件**: `backend/cw-elevator-application/.../PersonRuleServiceImpl.java` + +**变更内容**: +- 策略无论调用方是否传入楼层均参与评估 +- 补充 `INTERSECT_ALLOWLIST` 求交逻辑 + +--- + +## 阶段Ⅱ:代码仓库重组(05-07 ~ 05-09) + +### 2026-05-07 目录规范化 + +**操作**: 中文目录 → 英文目录重命名(15 次提交) + +| 操作 | 变更 | +|------|------| +| `星中心/` → `runtime/` | 服务目录命名规范化 | +| `部署包/` → `packages/` | 部署包目录重命名 | +| `data_backup/` → `data-backups/` | kebab-case 统一 | +| `源码/` → `source/` | 源码目录重命名 | +| 前端备份移至 `archive/` | 整理归档 | +| nginx/artifacts 从 submodule 迁出 | 顶层目录化 | + +**影响**: 所有策略相关路径更新为新目录结构 + +--- + +## 阶段Ⅲ:广发 28F 访客楼层策略设计(05-09) + +### 2026-05-09 23:56 设计文档 + 数据库 ER 图 + +**操作**: 新增广发 28F 访客楼层策略设计文档 +**涉及文件**: +- `docs/superpowers/specs/*-guangfa-visitor-floor-design.md` +- `docs/sql/tenant_visitor_floor_policy.sql`(标记 @deprecated) +- `docs/sql/organization_tenant_visitor_floor_policy.sql`(新增组织侧 DDL) +- `docs/sql/organization_tenant_visitor_floor_policy_init_tenants.sql`(种子数据) + +**变更内容**: +1. 设计文档:表驱动方案 A 作为实现路径 +2. 电梯侧策略表标记为 @deprecated +3. 组织侧 DDL 新建:`organization_tenant_visitor_floor_policy.sql` +4. 组织侧种子数据:广发基金 28F + 物业管理 28F+6F +5. 数据库 ER 图:14 个库、537 张表 + +**决策记录**: +``` +方案选择: 表驱动方案 A +理由: 配置化策略管理,无需代码修改即可新增/修改策略 +V2 策略维护点: component-organization 库(唯一) +电梯侧策略表: @deprecated(V2 不查询) +``` + +--- + +## 阶段Ⅳ:策略数据归一化(05-10 01:14) + +### 2026-05-10 01:14 归一化 REPLACE_ALLOWLIST + +**操作**: 统一所有策略数据为 REPLACE_ALLOWLIST +**涉及文件**: +- `docs/sql/` 下所有策略 SQL +- `scripts/build/release-component-organization.sh` +- `scripts/build/release-cw-elevator-application.sh` + +**变更内容**: +1. 种子 SQL 中 `policy_type` 全部改为 `REPLACE_ALLOWLIST` +2. 备注格式统一:`组织名 | 访客默认NF` +3. 发布脚本包含组织侧 DDL/seed + deprecation README +4. 发布版本命名规范文档 +5. 电梯侧 `tenant_visitor_floor_policy` 禁用(V2 不查询) + +**SQL 执行记录**: +```sql +-- 目标库: component-organization +-- 执行文件: +-- 1. organization_tenant_visitor_floor_policy.sql(建表) +-- 2. organization_tenant_visitor_floor_policy_init_tenants.sql(种子) +``` + +**策略数据清单**: +| org_id | 策略类型 | allow_zone_ids | 组织名称 | +|--------|---------|---------------|---------| +| `488b8ad0...` | REPLACE_ALLOWLIST | `["605560545117995008"]` | [28-38F]广发基金管理有限公司 | +| `64fdc8ea...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (1/7) | +| `8fc3f910...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (2/7) | +| `cc760fdf...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (3/7) | +| `f216235e...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (4/7) | +| `95818575...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (5/7) | +| `348328d7...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (6/7) | +| `dde6cc9a...` | REPLACE_ALLOWLIST | `["605560545117995008","605560541473144832"]` | 物业管理公司 (7/7) | + +--- + +## 阶段Ⅴ:DDL 修正(05-10 06:43) + +### 2026-05-10 06:43 DDL 默认值修正 + +**操作**: 修正 DDL 中 `policy_type` 默认值为 `REPLACE_ALLOWLIST` +**涉及文件**: +- `docs/sql/organization_tenant_visitor_floor_policy.sql` + +**变更内容**: +```sql +-- 修改前 +policy_type VARCHAR(32) NOT NULL DEFAULT 'INTERSECT_ALLOWLIST' + +-- 修改后 +policy_type VARCHAR(32) NOT NULL DEFAULT 'REPLACE_ALLOWLIST' +``` + +--- + +## 阶段Ⅵ:发布脚本编写(05-10 06:54) + +### 2026-05-10 06:54 component-org 发布脚本 + +**操作**: 新增 `release-component-organization.sh` +**涉及文件**: `scripts/build/release-component-organization.sh`(新增,139 行) + +**脚本流程**: +``` +1. Maven set version → package (fat JAR) +2. Copy JAR → releases/ 输出目录 +3. Copy 配置文件 (bootstrap/application/recognition-logback) +4. Copy DDL(组织侧:建表SQL + 种子SQL) +5. 生成 DDL README(执行顺序说明) +6. 生成 start.sh +7. 生成 RELEASE.txt + BUILD_MANIFEST.txt +8. Zip 压缩发布包 +9. 还原 Maven 版本号 +``` + +**发布包命名**: `ninca-common-component-organization-{ver}-xinghewan-{YYYYMMDD}` + +--- + +## 阶段Ⅶ:日志系统增强(05-10 09:37 ~ 13:22) + +### 2026-05-10 09:37 策略追踪日志 + +**操作**: 在 `TenantVisitorFloorPolicyService` 中添加策略追踪日志 +**涉及文件**: `source/backend/ninca-common-component-organization/.../TenantVisitorFloorPolicyService.java` + +**新增日志**: + +| 日志标签 | 级别 | 触发条件 | 记录内容 | +|---------|------|---------|---------| +| POLICY-HIT | INFO | 策略命中 | orgId, policyId, allowZones | +| POLICY-MISS | DEBUG | 未找到策略 | orgId | +| POLICY-EMPTY | WARN | allow_zone_ids 解析为空 | policyId | +| POLICY-FALLBACK | WARN | 查询异常 | orgId, exception | +| POLICY-RESULT | INFO | batch 命中结果 | orgId, zones | + +### 2026-05-10 09:50 ImgPersonServiceImpl 全链路日志 + +**操作**: 在 `ImgPersonServiceImpl` 中添加详细日志 +**涉及文件**: `source/backend/ninca-common-component-organization/.../ImgPersonServiceImpl.java` + +**新增日志**: + +| 日志标签 | 级别 | 触发条件 | +|---------|------|---------| +| DETAIL | INFO | detail() 入口/出口,含 personId | +| DETAIL-POLICY | DEBUG | 策略替换前后 floorList 对比 | +| LIST-PAGE-POLICY | DEBUG | listByPage() 策略命中 | +| LIST-PAGE-XHW | DEBUG | 40F/6F hardcoded 分支 | +| PAGE | INFO | page() 查询结果 | +| ADDV-DETAIL | DEBUG | addVisitor detail floorList/orgIds | + +### 2026-05-10 11:44 消息源 & Graceful Shutdown + +**操作**: 修复 ResourceBundle WARN + 添加优雅关闭 +**涉及文件**: +- `source/backend/ninca-common-component-organization/.../OrganizationServer.java` +- `bootstrap.properties` + +**变更内容**: +```properties +# 修改前 +spring.messages.basename=i18n/messages_zh_CN + +# 修改后(Spring 自动追加 locale) +spring.messages.basename=i18n/messages +``` + +**Graceful Shutdown**: +```java +@EventListener(ContextClosedEvent.class) +public void onShutdown() { + // 30s drain delay → 在途请求完成后销毁 Bean + Thread.sleep(30_000); +} +``` + +### 2026-05-10 12:01 Logback 配置打包 + +**操作**: 将 `recognition-logback.xml` 打包到 fat JAR classpath +**涉及文件**: `source/backend/ninca-common-component-organization/.../starter/pom.xml` + +**变更**: 不再依赖外部 `--logging.config` 参数 + +### 2026-05-10 12:32 Bean 冲突修复 + +**操作**: TypeFilter 排除 GroupPersonSyn* 重复 bean +**涉及文件**: `source/backend/ninca-common-component-organization/.../starter/.../GroupPersonSynExcludeFilter.java` + +**变更**: 自定义 TypeFilter,排除 starter 模块外的 `cn.cloudwalk.service.organization.config.GroupPersonSyn*` 类 + +### 2026-05-10 13:22 日志级别优化 + +**操作**: 将 [POLICY] 入口日志从 INFO→DEBUG +**涉及文件**: `TenantVisitorFloorPolicyService.java` + +**变更**: `log.info("[POLICY] entry orgIds={}")` → `log.debug("[POLICY] entry orgIds={}")` + 新增 `[DETAIL-POLICY-CHECK]` DEBUG 日志 + +--- + +## 阶段Ⅷ:修复与优化(05-10 11:44 ~ 12:32) + +### 2026-05-10 11:44 Graceful Shutdown + +详见阶段Ⅶ。 + +### 2026-05-10 12:01 Logback 配置内嵌 + +详见阶段Ⅶ。 + +### 2026-05-10 12:32 Bean 冲突修复 + +详见阶段Ⅶ。 + +### 2026-05-10 13:22 日志级别优化 + +详见阶段Ⅶ。 + +--- + +## 阶段Ⅸ:上线日志分析与优化方案(05-10 15:32) + +### 2026-05-10 15:32 日志分析报告 + +**操作**: 分析 component-org 上线 37 分钟日志 +**涉及文件**: `docs/superpowers/specs/2026-05-10-component-org-log-analysis.md` + +**分析结果**: + +| 指标 | 数据 | +|------|------| +| 日志总量 | 106,372 行 / 26MB | +| 有效 TraceId | 9,848 个 | +| 设备同步占比 | 84.2%(89,531 行) | +| WARN 占比 | 29.9%(31,800 行)— 主要是设备同步失败 | +| 用户业务调用 | 0.06%(64 行) | +| 策略命中 | 12 行 | +| 策略调用频率 | ~1 次/分钟 | +| 单次策略查询耗时 | < 5ms | + +**策略触发优化方案**: 详见 `docs/superpowers/specs/2026-05-10-floor-policy-trigger-optimization.md` + +--- + +## 附录 + +### A. Commit 清单 + +| 时间 | Hash | 类型 | 描述 | +|------|------|------|------| +| 05-09 23:56 | `42c4a9fd` | docs | 广发 28F 设计文档 + ER 图 | +| 05-10 01:14 | `03752480` | feat | 策略归一化 REPLACE_ALLOWLIST | +| 05-10 06:43 | `73dc34ec` | fix | DDL 默认值修正 | +| 05-10 06:54 | `368d14de` | feat | component-org 发布脚本 | +| 05-10 09:37 | `d8bcc145` | feat | 策略追踪日志 | +| 05-10 09:50 | `f350f318` | feat | ImgPersonService 全链路日志 | +| 05-10 11:44 | `3f1a23a1` | fix | 消息源 + Graceful Shutdown | +| 05-10 12:01 | `c7d4ac4a` | fix | Logback 内嵌 | +| 05-10 12:32 | `09b1bbe5` | fix | Bean 冲突修复 | +| 05-10 13:22 | `203005aa` | refactor | 日志级别优化 | +| 05-10 15:32 | `f7c042ca` | docs | 上线日志分析 | + +### B. 关键文件索引 + +| 文件 | 说明 | +|------|------| +| `source/docs/sql/organization_tenant_visitor_floor_policy.sql` | 组织库建表 DDL | +| `source/docs/sql/organization_tenant_visitor_floor_policy_init_tenants.sql` | 种子数据(广发+物业) | +| `source/scripts/build/release-component-organization.sh` | component-org 发布脚本 | +| `source/scripts/build/release-cw-elevator-application.sh` | 电梯应用发布脚本 | +| `source/backend/ninca-common-component-organization/.../policy/TenantVisitorFloorPolicyService.java` | 策略服务核心逻辑 | +| `source/backend/ninca-common-component-organization/.../service/ImgPersonServiceImpl.java` | detail/listByPage 策略调用入口 | +| `docs/superpowers/specs/2026-05-10-component-org-log-analysis.md` | 上线日志分析报告 | +| `docs/superpowers/specs/2026-05-10-floor-policy-trigger-optimization.md` | 策略触发优化方案 | + +### C. 数据字典 + +| 表 | 库 | 用途 | +|----|----|------| +| `tenant_visitor_floor_policy` | `component-organization` | **V2 唯一策略表**(活跃) | +| `tenant_visitor_floor_policy` | `cw-elevator-application` | ⛔ **废弃**(仅历史参考) | + +### D. 楼层编码 + +| zone_id | 楼层 | 用途 | +|---------|------|------| +| `605560540432957440` | 1F | 首层 | +| `605560541473144832` | 6F | 物业管理层 | +| `605560545117995008` | 28F | 广发基金层 | diff --git a/docs/visitor-floor-policy-user-guide.md b/docs/visitor-floor-policy-user-guide.md new file mode 100644 index 00000000..5ad1296a --- /dev/null +++ b/docs/visitor-floor-policy-user-guide.md @@ -0,0 +1,356 @@ +# 租户访客楼层策略 — 功能清单 & 使用手册 + +> **版本**: 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 库) + +```sql +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_policy(enabled=1) + b. 找到 → 解析 allow_zone_ids → 替换 floorList → 返回 + c. 未找到 → 继续下一个 org_id + 3. 全部未命中 → 保留 listByImageId 原始 floorList +``` + +### 3.4 策略配置方法 + +**新增策略**: +```sql +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`(幂等,可重复执行) + +**验证**: +```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 服务**: +```bash +# 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 +``` + +**电梯应用**: +```bash +# 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-organization**(`bootstrap.properties`): +```properties +# 无新增配置项,复用现有 datasource 连接 component-organization 库 +spring.datasource.url=jdbc:mysql://.../component-organization +``` + +**电梯应用**(`bootstrap.properties`): +```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:停用策略(快速回滚,推荐)** +```sql +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-organization` 库 `tenant_visitor_floor_policy WHERE enabled=1`,并查看日志中的 `[POLICY-HIT]` 记录。 + +**Q: 发布时需要停服吗?** +A: 组件服务(component-org / elevator-app)需要重启;使用 Graceful Shutdown(30s 排空),在途请求不会中断。