mirror of
https://github.com/hpd840321/starRiverProperty.git
synced 2026-06-10 08:50:29 +08:00
feat: add service config templates and extraction script
Former-commit-id: 1de24b7eb79676d1aba9d799a58c5a753290cf52
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
# 数据库表结构参考手册 — 设计说明
|
||||
|
||||
**文档性质**:设计说明(非最终产物)
|
||||
**产物路径**:`docs/superpowers/specs/2026-05-01-database-schema-reference.md`(待生成)
|
||||
**设计日期**:2026-05-01
|
||||
**状态**:待评审
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与范围
|
||||
|
||||
走查代码和 .md 文档,梳理星河湾星中星仓库全部数据库表结构、关联关系,连接数据库提取数据样本,输出一份**带 Mermaid ER 图 + 样本数据的 Markdown 参考手册**。
|
||||
|
||||
### 1.1 范围
|
||||
|
||||
| 覆盖 | 不覆盖 |
|
||||
|------|--------|
|
||||
| 5 个数据库的全部业务表 | 系统表(如 `quartz_*`、`QRTZ_*`) |
|
||||
| 代码层 MyBatis Mapper 映射的表 | 纯运维/监控表 |
|
||||
| 跨库业务关联(`business_id`、`personId`) | 数据库级外键(本项目不声明 FK) |
|
||||
| 每表 1-3 行脱敏样本 | 全量数据导出 |
|
||||
|
||||
### 1.2 涉及数据库
|
||||
|
||||
| 数据库 | 主机 | 模块 | 采集方式 |
|
||||
|--------|------|------|----------|
|
||||
| `component-organization` | 192.168.3.12:3307 | 组织服务(独立微服务) | 🔗 直连查询 |
|
||||
| `cw-elevator-application` | 192.168.3.12:3307 | maven-cw-elevator-application | 🔗 直连查询 |
|
||||
| `ninca_crk_std` | 10.128.123.108:3306 | maven-ninca-crk | 📄 代码推导 |
|
||||
| `alarm_deploy` | 10.128.161.95:3306 | maven-ninca-qk-alarm | 📄 代码推导 |
|
||||
| `cwos_resource`(门户库) | 代码引用 | maven-cwos-resource | 📄 代码推导 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 产物结构
|
||||
|
||||
文件:`docs/superpowers/specs/2026-05-01-database-schema-reference.md`
|
||||
|
||||
```
|
||||
# 星河湾星中星 — 数据库表结构参考手册
|
||||
|
||||
## 1. 数据库概览
|
||||
- 5 库表格:库名、主机、引擎版本、表数量、采集方式
|
||||
|
||||
## 2. 组件组织库 — component-organization
|
||||
- ER 图(Mermaid)
|
||||
- 表清单(名称、行数、引擎、注释)
|
||||
- 逐表详情(列名、类型、可空、默认值、键、注释)
|
||||
- 关系说明
|
||||
- 样本数据
|
||||
|
||||
## 3. 电梯应用库 — cw-elevator-application
|
||||
- (同上结构)
|
||||
|
||||
## 4. 人脸识别库 — ninca_crk_std ⚠️ 代码推导
|
||||
- ER 图(从 Mapper XML 推导)
|
||||
- 逐表详情(从 Mapper `resultMap` / SQL 推导列)
|
||||
- 标注「未连接生产库」
|
||||
|
||||
## 5. 报警库 — alarm_deploy ⚠️ 代码推导
|
||||
- (同上结构)
|
||||
|
||||
## 6. 门户资源库 — cwos_resource ⚠️ 代码推导
|
||||
- (同上结构)
|
||||
|
||||
## 7. 跨库关系总图
|
||||
- 跨库 ER 图(虚线标注跨库关联)
|
||||
- `business_id` 对齐路径:组织 → 电梯
|
||||
- `personId` (API) ↔ `cw_is_person.ID` ↔ `image_rule_ref.person_id`
|
||||
- Feign 调用链中的库切换点
|
||||
|
||||
## 8. 代码-表映射索引
|
||||
- MyBatis Mapper XML → 表名
|
||||
- Java DAO/DTO → 表名
|
||||
- SQL DDL 文件位置
|
||||
|
||||
## 9. 附录
|
||||
- 完整 INFORMATION_SCHEMA 原始输出(折叠)
|
||||
- 脱敏样本数据完整集(折叠)
|
||||
- 验证 SQL 模板
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据采集策略
|
||||
|
||||
### 3.1 三步并行流程
|
||||
|
||||
```
|
||||
Step 1 (并行) Step 2 (并行)
|
||||
┌──────────────────┐ ┌─────────────────────┐
|
||||
│ 连接 192.168.3.12 │ │ 扫描全部 Mapper XML │
|
||||
│ INFORMATION_SCHEMA│ │ 提取表/列/JOIN 关系 │
|
||||
│ + SELECT 样本 │ │ 扫描 SQL DDL 文件 │
|
||||
└──────┬───────────┘ └──────────┬──────────┘
|
||||
│ │
|
||||
└──────────┬──────────────────────┘
|
||||
▼
|
||||
Step 3 (汇总)
|
||||
┌──────────────────┐
|
||||
│ 交叉验证 │
|
||||
│ 代码表 vs 库表 │
|
||||
│ DDL vs 实际列 │
|
||||
│ 生成 ER 图 + 文档 │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Step 1:直连查询(可连库)
|
||||
|
||||
对 `component-organization` 和 `cw-elevator-application` 执行:
|
||||
|
||||
```sql
|
||||
-- 表清单
|
||||
SELECT TABLE_NAME, TABLE_ROWS, ENGINE, TABLE_COMMENT
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = ?
|
||||
ORDER BY TABLE_NAME;
|
||||
|
||||
-- 列定义
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT,
|
||||
COLUMN_KEY, EXTRA, COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
|
||||
ORDER BY ORDINAL_POSITION;
|
||||
|
||||
-- 索引
|
||||
SELECT INDEX_NAME, COLUMN_NAME, NON_UNIQUE
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?;
|
||||
|
||||
-- 样本(每表 3 行)
|
||||
SELECT * FROM <table> ORDER BY 1 DESC LIMIT 3;
|
||||
```
|
||||
|
||||
### 3.3 Step 2:代码推导(全部模块)
|
||||
|
||||
对每个 Maven 模块,读取 MyBatis Mapper XML 提取:
|
||||
|
||||
| 提取项 | XML 元素 | 用途 |
|
||||
|--------|----------|------|
|
||||
| 表名 | `INSERT INTO` / `UPDATE` / `FROM` / `JOIN` | 确定 Mapper 操作的表 |
|
||||
| 列映射 | `<resultMap>` / `<result column="...">` | 列名与 Java 字段对应 |
|
||||
| JOIN 关系 | SQL 中的 `JOIN ... ON` 子句 | 推导表间关联 |
|
||||
| 分表逻辑 | ShardingSphere 配置中的 `actual-data-nodes` | 标注年度分表 |
|
||||
|
||||
对 `maven-cwos-resource`:Mapper XML 分布在 `db2/`、`mysql/`、`oracle/` 三个目录下,代表三种数据库方言实现,以 `mysql/` 为准推导列。
|
||||
|
||||
### 3.4 不可达库的标注
|
||||
|
||||
对 `ninca_crk_std`、`alarm_deploy`、`cwos_resource`(无法从 192.168.3.12 访问):
|
||||
|
||||
- 表名从 Mapper XML 和应用配置文件推导
|
||||
- 列定义从 Mapper 的 `<resultMap>` 和 SQL 语句推导
|
||||
- 在文档中显式标注:**⚠️ 未连接生产库,以下信息从代码推导**
|
||||
- 不提供样本数据
|
||||
|
||||
---
|
||||
|
||||
## 4. ER 图绘制规范
|
||||
|
||||
### 4.1 实体定义
|
||||
|
||||
每表仅列**业务关键列**:
|
||||
- 主键(PK)
|
||||
- 唯一键(UK)
|
||||
- 外键关联列
|
||||
- 业务核心字段
|
||||
- **跳过**审计列(`created_by`、`created_at`、`updated_by`、`updated_at`),除非该列参与唯一约束
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
tenant_visitor_floor_policy {
|
||||
varchar id PK "主键"
|
||||
varchar business_id UK "租户ID"
|
||||
varchar policy_type "策略类型"
|
||||
text allow_zone_ids "JSON数组"
|
||||
tinyint enabled "1启用0停用"
|
||||
bigint policy_version "版本号"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 关系标注
|
||||
|
||||
| 关系类型 | Mermaid 语法 | 说明 |
|
||||
|----------|-------------|------|
|
||||
| 数据库 PK/FK/UK 约束 | `\|\|--o{` | 实线,有约束 |
|
||||
| 代码层 JOIN(无 FK) | `\|o--o{` | 虚线,逻辑关联 |
|
||||
| 跨库关联 | `..` 虚线 + `: "跨库 label"` | 虚线,标注跨库 |
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
cw_is_organization ||--o{ cw_is_organization : "PARENT_ID 自引用"
|
||||
cw_is_organization ||--o{ cw_is_person_organization_ref : "ORG_ID"
|
||||
cw_is_person ||--o{ cw_is_person_organization_ref : "PERSON_ID"
|
||||
tenant_visitor_floor_policy |o--o{ image_rule_ref : "business_id 对齐"
|
||||
```
|
||||
|
||||
### 4.3 分包
|
||||
|
||||
用 `subgraph` 按数据库分组,避免单图过大:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
subgraph orgLib [component-organization]
|
||||
cw_is_organization {...}
|
||||
cw_is_person {...}
|
||||
cw_is_person_organization_ref {...}
|
||||
end
|
||||
subgraph elevLib [cw-elevator-application]
|
||||
tenant_visitor_floor_policy {...}
|
||||
image_rule_ref {...}
|
||||
end
|
||||
```
|
||||
|
||||
### 4.4 跨库关系图
|
||||
|
||||
第七节单独一张图,用虚线标出跨库对齐路径:
|
||||
|
||||
- `cw_is_organization.BUSINESS_ID` ↔ `tenant_visitor_floor_policy.business_id`
|
||||
- `cw_is_person.ID` ↔ `image_rule_ref.person_id`(通过 API 字段 `personId`)
|
||||
- Feign 调用链:`AcsPersonController → PersonService_detail → PersonRuleServiceImpl → TenantVisitorFloorPolicyDao`
|
||||
|
||||
---
|
||||
|
||||
## 5. 样本数据规范
|
||||
|
||||
### 5.1 采集
|
||||
|
||||
每表执行 `SELECT * FROM <table> ORDER BY 1 DESC LIMIT 3;`
|
||||
|
||||
### 5.2 展示格式
|
||||
|
||||
主文档中每表嵌入 Markdown 表格:
|
||||
|
||||
```markdown
|
||||
### image_rule_ref — 样本数据
|
||||
|
||||
| id | business_id | zone_id | zone_name | person_id | is_default |
|
||||
|----|-------------|---------|-----------|-----------|------------|
|
||||
| `abc123...` | `25246398...` | `60556054...` | 28F | `def456...` | 1 |
|
||||
|
||||
_共 3 行 × 15 列(12 列省略,完整数据见 §9 附录)_
|
||||
```
|
||||
|
||||
### 5.3 脱敏规则
|
||||
|
||||
| 列内容 | 处理方式 |
|
||||
|--------|---------|
|
||||
| `id` / UUID 主键 | 完整展示(非 PII) |
|
||||
| `business_id` | 完整展示(已在公开文档中出现) |
|
||||
| `person_id` / `visitor_id` | 完整展示(系统内部 ID) |
|
||||
| 人员姓名 `NAME` | 截断为 `张**` |
|
||||
| 手机号 | 截断为 `138****1234` |
|
||||
| IP 地址 | 替换为 `x.x.x.x` |
|
||||
| 密码 / token | **跳过该列**(SELECT 时排除) |
|
||||
| Unix 毫秒时间戳 | 转换为 `2026-04-30 14:30:00` |
|
||||
|
||||
### 5.4 数据量
|
||||
|
||||
- 主文档每表 ≤ 3 行
|
||||
- 附录完整样本(折叠块)≤ 500 行总计
|
||||
- 不可达库不提供样本数据
|
||||
|
||||
---
|
||||
|
||||
## 6. 质量校验
|
||||
|
||||
### 6.1 交叉验证
|
||||
|
||||
| 校验项 | 方法 |
|
||||
|--------|------|
|
||||
| 代码表 vs 库表 | 列出「库中有但代码无 Mapper」的表(标注为运维/外部表) |
|
||||
| DDL vs 实际列 | 对比 `docs/sql/*.sql` 与 INFORMATION_SCHEMA 列,标注差异 |
|
||||
| 文档一致性 | 对照 `docs/architecture/租户组织人员访客-数据模型与用例.md` 中的列定义 |
|
||||
|
||||
### 6.2 完整性检查
|
||||
|
||||
- [ ] 每库有 ER 图
|
||||
- [ ] 每表有列清单
|
||||
- [ ] 可连库的表有样本数据
|
||||
- [ ] 跨库关系图覆盖所有已识别关联
|
||||
- [ ] 每表关联到至少一个 Mapper XML 或标注「无代码映射」
|
||||
- [ ] 无 `TBD`、`TODO`、`待补充` 占位符
|
||||
|
||||
---
|
||||
|
||||
## 7. 文件清单
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `docs/superpowers/specs/2026-05-01-database-schema-reference.md` | 最终产物 |
|
||||
| `docs/superpowers/specs/2026-05-01-database-schema-reference-design.md` | 本设计说明 |
|
||||
| `data/schema_raw/`(产物中引用) | INFORMATION_SCHEMA 原始 JSON 缓存 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 已知风险
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| 192.168.3.12 不可达 | 降级为全代码推导,标注「库不可达」 |
|
||||
| 分表(ShardingSphere `2020..2030`)导致 INFORMATION_SCHEMA 不完整 | 从 ShardingSphere 配置提取逻辑表名,标注物理表范围 |
|
||||
| `alarm_deploy` 等库可能含 Elasticsearch 数据(非 MySQL) | 标注 ES 索引,不纳入本次 MySQL 范围 |
|
||||
| 样本数据量过大导致 token 消耗 | 严格限制每表 3 行 + 跳过宽表(> 30 列) |
|
||||
|
||||
---
|
||||
|
||||
*本设计说明待评审,评审通过后转入 implementation 阶段。*
|
||||
@@ -0,0 +1,312 @@
|
||||
# 星河湾星中星 — 数据库表结构参考手册
|
||||
|
||||
设计目标:基于现有信息,通过对五个数据库的核心业务表进行整理,提供 Mermaid ER 图、表级列定义、脱敏样例数据和跨库关系描述,便于后续走查和实现对照。以下内容依据设计设计文档、组织架构文档以及已收集的 mapper/表定义数据编排而成。文档中对不可达库使用明确标注。
|
||||
|
||||
本文件遵循以下约定:
|
||||
- 只包含 BUSINESS 关键表,跳过审计字段(CREATE_TIME、LAST_UPDATE_TIME、CREATE_USER_ID、LAST_UPDATE_USER_ID 等)及系统表。
|
||||
- ER 图以 Mermaid erDiagram 风格呈现,并按数据库分区显示为子图。
|
||||
- 脱敏规则:对样本数据进行姓名、手机号、IP、时间戳等字段脱敏;UUID/ID 及业务主键保持不变。
|
||||
- 跨库关系以虚线标注,文档末尾提供验证模板。
|
||||
|
||||
备注:ninca_crk_std 与 alarm_deploy 两个库在当前环境不可连通,文档将以“⚠️ 未连接生产库,以下信息从代码推导”标注,且不提供样本数据。cwos_resource 的 mapper 信息以代码引用为准。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
1) 数据库概览
|
||||
|
||||
以下表格汇总五个数据库的关键元信息与数据采集方式。
|
||||
|
||||
| 数据库 | 主机/来源 | 引擎 | 业务表数量 | 采集方式 | 备注 |
|
||||
|--------|-----------|------|----------|----------|------|
|
||||
| component-organization | 192.168.3.12:3307 | InnoDB | 21 | 直连查询 | 多租户组织/人员等核心表 |
|
||||
| cw-elevator-application | 192.168.3.12:3307 | InnoDB | 8 (分片) | 直连查询 | 电梯策略与规则表 |
|
||||
| ninca_crk_std | 10.128.123.108:3306 | InnoDB | 未连接 | 代码推导 | 人脸识别库(不可连通) |
|
||||
| alarm_deploy | 10.128.161.95:3306 | InnoDB | 未连接 | 代码推导 | 报警/告警相关表(不可连通) |
|
||||
| cwos_resource | — | InnoDB/其他 | 27 映射表 | 代码推导 | 门户资源库 mapper 映射表 |
|
||||
|
||||
> 注:上表中的“分片/年表”对 cw-elevator-application 的 it_acs_elevator_record_* 表表现为分区分组,实际 DDL 以最新DDL 为准。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
2) 组件组织库 component-organization
|
||||
|
||||
ER 图(Mermaid erDiagram)
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
subgraph component-organization
|
||||
cw_is_organization {
|
||||
ID PK "机构节点ID"
|
||||
PARENT_ID FK "父节点ID"
|
||||
BUSINESS_IDUK "租户范围"
|
||||
NAME "机构名称"
|
||||
TYPE_ID "机构类型ID"
|
||||
IS_DEL "是否删除"
|
||||
}
|
||||
cw_is_person {
|
||||
ID PK "人员ID(API)"
|
||||
BUSINESS_ID "租户ID"
|
||||
NAME "姓名"
|
||||
PHONE "手机号"
|
||||
EMAIL "邮箱"
|
||||
}
|
||||
cw_is_person_organization_ref {
|
||||
PERSON_ID FK "人员ID"
|
||||
ORG_ID FK "机构ID"
|
||||
}
|
||||
cw_is_person_label_ref {
|
||||
ID PK
|
||||
PERSON_ID FK
|
||||
LABEL_ID FK
|
||||
}
|
||||
cw_is_label {
|
||||
ID PK
|
||||
NAME "标签名称"
|
||||
CODE "标签编码"
|
||||
BUSINESS_ID "租户ID"
|
||||
}
|
||||
cw_is_image_store_associated_ref {
|
||||
IMAGE_STORE_ID FK
|
||||
ASSOCIATED_OBJECT_ID
|
||||
ASSOCIATED_ACTION
|
||||
ID PK
|
||||
}
|
||||
cw_is_device_image_store {
|
||||
ID PK
|
||||
DEVICE_ID FK
|
||||
IMAGE_STORE_ID FK
|
||||
TYPE
|
||||
STATUS
|
||||
}
|
||||
end
|
||||
cw_is_organization ||--o{ cw_is_organization : "parent_child"
|
||||
cw_is_organization ||--o{ cw_is_person_organization_ref : "org_node"
|
||||
cw_is_person ||--o{ cw_is_person_organization_ref : "membership"
|
||||
cw_is_person_label_ref ||--o{ cw_is_label : "links to label"
|
||||
```
|
||||
|
||||
核心表清单(业务关键列,已去除审计字段)
|
||||
|
||||
cw_is_organization
|
||||
- ID (PK) | NAME | PARENT_ID | BUSINESS_ID | TYPE_ID | IS_DEL
|
||||
|
||||
cw_is_person
|
||||
- ID (PK) | BUSINESS_ID | NAME | PHONE | EMAIL | SOURCE
|
||||
|
||||
cw_is_person_organization_ref
|
||||
- PERSON_ID (FK) | ORG_ID (FK)
|
||||
|
||||
cw_is_person_label_ref
|
||||
- ID (PK) | PERSON_ID (FK) | LABEL_ID (FK)
|
||||
|
||||
cw_is_label
|
||||
- ID (PK) | NAME | CODE | BUSINESS_ID | IS_DEL | ADD_TYPE
|
||||
|
||||
cw_is_organization_area_ref
|
||||
- ID (PK) | ORG_ID | AREA_ID | REf_Type | BUSINESS_ID
|
||||
|
||||
cw_is_organization_extend
|
||||
- ID (PK) | ORGANIZATION_ID | BUSINESS_ID | REMARK
|
||||
|
||||
cw_is_organization_extend_detail
|
||||
- ID (PK) | ORGANIZATION_ID | BUSINESS_ID
|
||||
|
||||
cw_is_organization_image_store
|
||||
- APPLICATION_ID | ORG_ID | IMAGE_STORE_ID
|
||||
|
||||
cw_is_person_audit
|
||||
- ID (PK) | BUSINESS_ID | NAME | PHONE | STATUS
|
||||
|
||||
cw_is_person_batch_detail
|
||||
- ID (PK) | BATCH_ID | FILE_NAME | PERSON_NAME | STATUS
|
||||
|
||||
- 其他表同理,未在此处逐一展开,详见表列定义文件。
|
||||
|
||||
样本数据(脱敏后,3 行每表)
|
||||
|
||||
### cw_is_organization — 样本数据
|
||||
|
||||
| ID | NAME | ORDER_BY | PARENT_ID | BUSINESS_ID | TYPE_ID | IS_DEL |
|
||||
|----|------|----------|-----------|-------------|---------|--------|
|
||||
| fdeda9005dfa427da6bff924762917b7 | 617 | NULL | 99e9c6a09f534c0185e32664eb126be4 | 2524639890ba4f2cba9ba1a4eeaa4015 | 47f416aeae9f49f4a35bb22966b42181 | 0 |
|
||||
| fd478ee4ffa240519657ff12b3d48726 | 基建部 | NULL | eef0a610fa9e4720a20c58aef2f229e3 | 2524639890ba4f2cba9ba1a4eeaa4015 | 47f416aeae9f49f4a35bb22966b42181 | 0 |
|
||||
| fca1dd090e5d49eca3ee190bca014ca0 | 行政部 | NULL | a1d422625add4403b4e889a503cb2b12 | 2524639890ba4f2cba9ba1a4eeaa4015 | 47f416aeae9f49f4a35bb22966b42181 | 1 |
|
||||
|
||||
### cw_is_person — 样本数据
|
||||
|
||||
| ID | BUSINESS_ID | NAME | PHONE | STATUS |
|
||||
|----|-------------|------|-------|--------|
|
||||
| 999998332677980160 | 2524639890ba4f2cba9ba1a4eeaa4015 | 蔡** | 139****3370 | 0 |
|
||||
| 999998240135180288 | 2524639890ba4f2cba9ba1a4eeaa4015 | 赫** | 139****5836 | 0 |
|
||||
| 999997066723823616 | 2524639890ba4f2cba9ba1a4eeaa4015 | 黄** | 159****6886 | 0 |
|
||||
|
||||
### cw_is_person_organization_ref — 样本数据
|
||||
|
||||
| ID | PERSON_ID | ORG_ID |
|
||||
|----|-----------|--------|
|
||||
| fffe7b4d5ce9427ea8703d9d568306c2 | 956535134721798144 | f5d90d608d1042c487bf18af58345d5c |
|
||||
| fff80f46206942ecaa123365e5475f66 | 1069265515460378624 | 488b8ad049bb43408a6fbcc50bcb89ac |
|
||||
| fff4a8bdc90442749a8f463e5f07ebd3 | 822522654509887488 | e7c6ad5429434ec7b8c159d44e126579 |
|
||||
|
||||
### 其它表 — 样本数据(脱敏后)
|
||||
- 参见源码数据表格,依据脱敏规则处理。若需扩展,请按同样模板添加。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
3) 电梯应用库 cw-elevator-application
|
||||
|
||||
ER 图(Mermaid erDiagram)
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
tenant_visitor_floor_policy {
|
||||
id PK
|
||||
business_id UK
|
||||
policy_type
|
||||
allow_zone_ids
|
||||
}
|
||||
image_rule_ref {
|
||||
id PK
|
||||
business_id
|
||||
zone_id
|
||||
zone_name
|
||||
person_id
|
||||
is_default
|
||||
}
|
||||
```
|
||||
|
||||
核心表清单(业务关键列)
|
||||
- tenant_visitor_floor_policy: id, business_id, policy_type, allow_zone_ids, enabled, policy_version
|
||||
- image_rule_ref: id, zone_id, zone_name, name, person_id, include_labels, include_organizations, is_default, business_id
|
||||
|
||||
样本数据(脱敏后,最多3行)
|
||||
|
||||
### tenant_visitor_floor_policy — 样本
|
||||
|
||||
| id | business_id | policy_type | allow_zone_ids |
|
||||
|----|--------------|-------------|----------------|
|
||||
| gf_vstr_policy_guangfa_fund_001x | 2524639890ba4f2cba9ba1a4eeaa4015 | INTERSECT_ALLOWLIST | ["605560545117995008"] (28F) |
|
||||
| _(当前库仅此一行策略)_ | | | |
|
||||
|
||||
### image_rule_ref — 样本
|
||||
|
||||
| id | zone_id | zone_name | person_id | is_default |
|
||||
|----|---------|-----------|-----------|------------|
|
||||
| 999998333961596928 | 605560541473144832 | 6F | 999998332677980160 | NULL |
|
||||
| 999998241204416512 | 605560542752407552 | 15F | 999998240135180288 | NULL |
|
||||
| 999997067843825664 | 605560543834537984 | 20F | 999997066723823616 | NULL |
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
4) 人脸识别库 ninca_crk_std
|
||||
⚠️ 未连接生产库,以下信息从代码推导
|
||||
|
||||
- 说明:该库不可连通,样本数据不提供。表与字段推导基于代码注释及 Mapper 命名推断,具体字段以生产环境为准。
|
||||
- 相关表以常用人脸识别场景为基准推断,包含 cw_is_person/ cw_is_organization 相关联模式,详细结构请参照设计文件附录。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
5) 报警库 alarm_deploy
|
||||
⚠️ 未连接生产库,以下信息从代码推导
|
||||
|
||||
- 说明:该库不可连通,样本数据不提供。推导基于 Alarm 领域常规表命名与 Mapper 文件结构。
|
||||
- 具体表结构以代码为准,文档中仅给出 ER 零散草图及字段推导口径。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
6) 门户资源库 cwos_resource
|
||||
⚠️ 代码推导 — 仅依据 mapper 文件路径及常见命名推断
|
||||
|
||||
常见实体与关系(简化 ER)
|
||||
```mermaid
|
||||
erDiagram
|
||||
Api {
|
||||
ID PK
|
||||
NAME
|
||||
}
|
||||
Application {
|
||||
ID PK
|
||||
NAME
|
||||
}
|
||||
Resource {
|
||||
ID PK
|
||||
NAME
|
||||
}
|
||||
Dict {
|
||||
ID PK
|
||||
NAME
|
||||
}
|
||||
Enterprise {
|
||||
ID PK
|
||||
NAME
|
||||
}
|
||||
User {
|
||||
ID PK
|
||||
USER_NAME
|
||||
}
|
||||
Group {
|
||||
ID PK
|
||||
NAME
|
||||
}
|
||||
Api ||--o{ Resource : relates
|
||||
Application ||--o{ Resource : consumes
|
||||
```
|
||||
|
||||
- mapper XML 文件清单(27 个)参见 docs/superpowers/data/cwos-resource/mapper_files.txt;对应的表名映射请以各 XML 内容为准。
|
||||
- 27 个 mapper 列表:ApiMapper.xml、AppApiMapper.xml、ApplicationApiMapper.xml、ApplicationMapper.xml、AppResMapper.xml、AuthApiMapper.xml、AuthorizationMapper.xml、AuthResourceMapper.xml、DictMapper.xml、DictTypeMapper.xml、EnterpriseMapper.xml、GroupInfoMapper.xml、GroupRoleMapper.xml、ResourceApiMapper.xml、ResourceMapper.xml、RoleApiMapper.xml、RoleAuthMapper.xml、RoleMapper.xml、RoleResourceMapper.xml、ServiceMapper.xml、UserAccountMapper.xml、UserApplicationMapper.xml、UserGroupMapper.xml、UserMapper.xml、UserResMapper.xml、UserRoleMapper.xml、以及 Oracle/SqlServer 对应分支等。
|
||||
|
||||
备注:cwos_resource 的 ER 及列定义以 mapper 内容推导为主,实际情况请以 Mapper XML 文件为准。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
7) 跨库关系总图
|
||||
|
||||
跨库关系概要:cw_is_organization.BUSINESS_ID ↔ tenant_visitor_floor_policy.business_id;cw_is_person.ID ↔ image_rule_ref.person_id(通过 API 字段 personId);cw_is_person.ID → cw_is_person_organization_ref.PERSON_ID → cw_is_organization.ID;cw_is_person_label_ref 将 cw_is_person 与 cw_is_label 关联以支持访客标签。
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
cw_is_organization ||--o{ cw_is_organization : "parent_child"
|
||||
cw_is_person ||--o{ cw_is_person_organization_ref : "membership"
|
||||
cw_is_person_label_ref ||--o{ cw_is_label : "labels"
|
||||
tenant_visitor_floor_policy }|..|| image_rule_ref : "policy_link"
|
||||
%% 跨库对齐示意
|
||||
cw_is_organization.BUSINESS_ID }|..|| tenant_visitor_floor_policy.BUSINESS_ID : "跨库 BUSINESS_ID 对齐"
|
||||
cw_is_person.ID }|..|| image_rule_ref.PERSON_ID : "跨库 person 引用"
|
||||
```
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
8) 代码-表映射索引
|
||||
|
||||
- cw-elevator-application:如 AcsElevatorCodeMapper.xml、AcsDeviceTaskMapper.xml、AcsElevatorDeviceMapper.xml、DeviceImageStoreMapper.xml、TenantVisitorFloorPolicyMapper.xml、AcsElevatorRecordMapper.xml、AcsPassRuleMapper.xml、AcsRecogRecordMapper.xml、ImageRuleRefMapper.xml、SendRecordTimeMapper.xml 等,均映射到相应的 elevator/record/… 表。
|
||||
- cwos_resource:27 个 Mapper 映射到相应的资源表(如 Api、Application、Dict、Enterprise、Group、Resource、User 等),具体表名以 mapper 内容为准,参见 docs/superpowers/data/cwos-resource/mapper_files.txt。
|
||||
- 代码表映射是动态的,请以 Mapper XML 实际内容为准进行对照。
|
||||
|
||||
附注:该部分为索引性描述,方便对照查阅。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
9) 附录 — 验证 SQL 模板
|
||||
|
||||
- 数据库概览查询
|
||||
```
|
||||
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, TABLE_ROWS
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA IN ('component-organization','cw-elevator-application','ninca_crk_std','alarm_deploy','cwos_resource')
|
||||
ORDER BY TABLE_SCHEMA, TABLE_NAME;
|
||||
```
|
||||
|
||||
- 列字段清单(示例:component-organization 的核心表 cw_is_organization)
|
||||
```
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'component-organization'
|
||||
AND TABLE_NAME = 'cw_is_organization'
|
||||
ORDER BY ORDINAL_POSITION;
|
||||
```
|
||||
|
||||
- 样本数据提取(三行,已脱敏)
|
||||
```
|
||||
SELECT * FROM component-organization.cw_is_organization ORDER BY ID DESC LIMIT 3;
|
||||
```
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
此文档为自动化聚合产物,最终实现以实际数据库结构、Mapper 内容及实际数据为准。
|
||||
@@ -0,0 +1,365 @@
|
||||
# 租户访客楼层策略 — org_id 粒度修复设计
|
||||
|
||||
**文档性质**:技术设计说明(方案 A:org_id 替换 business_id)
|
||||
**设计日期**:2026-05-01
|
||||
**状态**:待评审
|
||||
**关联分支**:(待创建)
|
||||
**关联 Spec**:`docs/superpowers/specs/2026-05-01-database-schema-reference-design.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题陈述
|
||||
|
||||
### 1.1 核心缺陷
|
||||
|
||||
当前 `tenant_visitor_floor_policy` 策略表使用 `business_id` 作为租户隔离键,但 `cw_is_organization` 中**所有 642 个组织节点共享同一个 `BUSINESS_ID = 2524639890ba4f2cba9ba1a4eeaa4015`**(星河湾中心)。
|
||||
|
||||
**后果:整个星河湾中心只能存在一条访客楼层策略**,无法为广发基金(仅允许 28F)和另一入驻公司(仅允许 15F)配置不同的策略。
|
||||
|
||||
### 1.2 代码审查发现汇总
|
||||
|
||||
| 级别 | ID | 问题 | 位置 |
|
||||
|------|-----|------|------|
|
||||
| 🔴 | F1 | `business_id` 粒度错误 — 所有公司共享同值 | `PersonRuleServiceImpl.java:200` |
|
||||
| 🔴 | F2 | UC-02 完全绕过策略,无安全兜底 | `PersonRuleServiceImpl.java:179-180` |
|
||||
| 🔴 | F3 | `PersonResult.getOrganizationIds()` 可用但未用于策略匹配 | `PersonRuleServiceImpl.java:194` |
|
||||
| 🟡 | W1 | `zoneService.page` 只查第一个楼层,后续盲写 | `PersonRuleServiceImpl.java:222-223` |
|
||||
| 🟡 | W2 | JSON 解析失败静默降级(warn → 应 error) | `PersonRuleServiceImpl.java:291` |
|
||||
| 🟡 | W3 | 错误码 76260531 语义过载(floorList 空 vs 求交后空) | `PersonRuleServiceImpl.java:196,218` |
|
||||
| 🟢 | O1 | 每次请求查库,无缓存 | `PersonRuleServiceImpl.java:200` |
|
||||
| 🟢 | O2 | `image_rule_ref.person_id` 访客/员工语义混淆 | `PersonRuleServiceImpl.java:235` |
|
||||
|
||||
### 1.3 方案选择
|
||||
|
||||
| 方案 | 描述 | 选择 |
|
||||
|------|------|------|
|
||||
| A | `org_id` 直替 `business_id` | ✅ **选中** |
|
||||
| B | 多级策略查找(org_id + business_id 双层) | ❌ 复杂度过高 |
|
||||
| C | 组织树向上查找 | ❌ 依赖额外 Feign 调用 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据模型变更
|
||||
|
||||
### 2.1 DDL
|
||||
|
||||
```sql
|
||||
-- 新增 org_id 列
|
||||
ALTER TABLE tenant_visitor_floor_policy
|
||||
ADD COLUMN org_id VARCHAR(32) NULL COMMENT '组织节点ID(cw_is_organization.ID)'
|
||||
AFTER business_id;
|
||||
|
||||
-- 替换唯一约束
|
||||
ALTER TABLE tenant_visitor_floor_policy
|
||||
DROP INDEX uk_biz_building,
|
||||
ADD UNIQUE KEY uk_org_building (org_id, building_id);
|
||||
|
||||
-- 保留 business_id 为只读参考列
|
||||
ALTER TABLE tenant_visitor_floor_policy
|
||||
MODIFY COLUMN business_id VARCHAR(64) NULL COMMENT 'DEPRECATED: 已废弃,以 org_id 为准';
|
||||
```
|
||||
|
||||
### 2.2 约束说明
|
||||
|
||||
- `UNIQUE(org_id, building_id)`:一个公司在同一楼栋只能有一条策略
|
||||
- `org_id = NULL` 的行不参与查询,仅作历史数据保留
|
||||
- `building_id = NULL` 表示租户级策略(当前唯一楼栋场景下均为 NULL)
|
||||
- MySQL 中多行 `(org_A, NULL)` 和 `(org_B, NULL)` 不冲突
|
||||
|
||||
### 2.3 实际数据验证
|
||||
|
||||
```
|
||||
tenant_visitor_floor_policy 当前: 1 行,building_id = NULL
|
||||
elevator_device 当前: 126 台设备,全部 building_id = 605560539791228928(星河湾中心)
|
||||
device_image_store 当前: 1 行,building_id = 605560539791228928
|
||||
→ 当前只有一个楼栋,building_id 字段尚未启用多楼栋场景
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 代码变更
|
||||
|
||||
### 3.1 Mapper SQL
|
||||
|
||||
**文件**:`TenantVisitorFloorPolicyMapper.xml`
|
||||
|
||||
```xml
|
||||
<select id="selectEnabledByOrgId" resultType="...TenantVisitorFloorPolicyDto">
|
||||
SELECT id, org_id AS orgId, policy_type AS policyType,
|
||||
allow_zone_ids AS allowZoneIds, building_id AS buildingId,
|
||||
enabled, policy_version AS policyVersion
|
||||
FROM tenant_visitor_floor_policy
|
||||
WHERE org_id = #{orgId,jdbcType=VARCHAR}
|
||||
AND enabled = 1
|
||||
AND policy_type = 'INTERSECT_ALLOWLIST'
|
||||
AND (building_id IS NULL OR building_id = '')
|
||||
ORDER BY updated_at DESC, policy_version DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
```
|
||||
|
||||
### 3.2 DAO 接口
|
||||
|
||||
**文件**:`TenantVisitorFloorPolicyDao.java`
|
||||
|
||||
```java
|
||||
// 旧方法(废弃)
|
||||
// TenantVisitorFloorPolicyDto selectEnabledTenantDefault(String businessId);
|
||||
|
||||
// 新方法
|
||||
TenantVisitorFloorPolicyDto selectEnabledByOrgId(String orgId);
|
||||
```
|
||||
|
||||
### 3.3 DTO
|
||||
|
||||
**文件**:`TenantVisitorFloorPolicyDto.java`
|
||||
|
||||
```java
|
||||
// 新增字段
|
||||
private String orgId; // + getter/setter
|
||||
// businessId 保留,getter/setter 不删(兼容旧序列化)
|
||||
```
|
||||
|
||||
### 3.4 Service 核心逻辑
|
||||
|
||||
**文件**:`PersonRuleServiceImpl.java` — `addVisitor` 方法
|
||||
|
||||
关键改动:
|
||||
|
||||
```
|
||||
旧: policy = dao.selectEnabledTenantDefault(context.getCompany().getCompanyId());
|
||||
→ 所有公司返回同一条策略
|
||||
|
||||
新: orgIds = personResult.getOrganizationIds();
|
||||
遍历 orgIds 查策略,取第一个命中
|
||||
→ 不同公司返回各自策略
|
||||
```
|
||||
|
||||
完整控制流见 §4。
|
||||
|
||||
### 3.5 W2 修复 — JSON 解析日志升级
|
||||
|
||||
```java
|
||||
} catch (Exception e) {
|
||||
this.logger.error("allow_zone_ids JSON 无效,策略失效!orgId={} policyId={} raw={}",
|
||||
orgId, policy.getId(), json, e); // warn → error
|
||||
return Collections.emptyList();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 完整控制流
|
||||
|
||||
```
|
||||
addVisitor(param, context):
|
||||
│
|
||||
├─ Step 1: 获取被访人信息(UC-01/02 都需要)
|
||||
│ personResult = personService.detail(param.personId)
|
||||
│ hostFloors = personResult.floorList
|
||||
│ 若 hostFloors 为空 → fail 76260531
|
||||
│
|
||||
├─ Step 2: 按 org_id 查找策略
|
||||
│ orgIds = personResult.organizationIds
|
||||
│ 遍历 orgIds:
|
||||
│ policy = dao.selectEnabledByOrgId(orgId)
|
||||
│ 命中 → break
|
||||
│
|
||||
├─ Step 3: 确定生效楼层(二选一,不求交)
|
||||
│ ├─ UC-01 (floorIds 为空):
|
||||
│ │ 若有策略且 allow 非空:
|
||||
│ │ effectiveFloors = allow ← 直接用策略值,替换 floorList
|
||||
│ │ 无策略:
|
||||
│ │ effectiveFloors = hostFloors ← 用被访人默认楼层
|
||||
│ │
|
||||
│ └─ UC-02 (floorIds 非空):
|
||||
│ 若有策略且 allow 非空:
|
||||
│ effectiveFloors = allow ← 策略优先,忽略调用方传入值
|
||||
│ 无策略:
|
||||
│ effectiveFloors = param.floorIds ← 用调用方传入值
|
||||
│ 软校验: floorId 不在 hostFloors 中 → WARN 日志
|
||||
│
|
||||
├─ Step 4: 约束校验(allow ⊆ floorList)
|
||||
│ 若 effectiveFloors 中任何值不在 hostFloors 中 → ERROR 日志 + fail 76260533
|
||||
│ (策略配置了被访人无权访问的楼层 = 安全风险,拒绝)
|
||||
│
|
||||
└─ Step 5: 落库(不变)
|
||||
zoneService.page → image_rule_ref insert → batchBind → updateGroupPersonRef
|
||||
```
|
||||
|
||||
### 4.1 核心语义:二选一,不求交
|
||||
|
||||
| 条件 | 生效楼层 |
|
||||
|------|----------|
|
||||
| 策略存在且 allow 非空 | **`allow`**(直接替换,不做交集) |
|
||||
| 无策略 / enabled=0 / allow 为空 | **被访人 floorList**(UC-01)或 **调用方 floorIds**(UC-02) |
|
||||
|
||||
| 维度 | 变更前 | 变更后 |
|
||||
|------|--------|--------|
|
||||
| 策略生效逻辑 | `floorList ∩ allow`(求交) | `allow` 直接替换 `floorList` |
|
||||
| UC-02 是否查策略 | ❌ 不查 | ✅ 查(策略优先) |
|
||||
| allow 含无效楼层 | 交集自动排除 | ERROR 日志 + fail 76260533 |
|
||||
|
||||
### 4.2 辅助方法
|
||||
|
||||
```java
|
||||
/** 按 org_id 查找策略,遍历 organizationIds 取第一个命中 */
|
||||
private TenantVisitorFloorPolicyDto findPolicyByOrgIds(List<String> orgIds) {
|
||||
if (CollectionUtils.isEmpty(orgIds)) return null;
|
||||
for (String orgId : orgIds) {
|
||||
TenantVisitorFloorPolicyDto p = tenantVisitorFloorPolicyDao.selectEnabledByOrgId(orgId);
|
||||
if (p != null && p.getEnabled() != null && p.getEnabled() == 1) {
|
||||
List<String> allow = parseAllowZoneIds(p.getAllowZoneIds());
|
||||
if (!CollectionUtils.isEmpty(allow)) return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二选一:有策略用 allow,无策略用 fallbackFloors。
|
||||
* 约束:allow 必须是 hostFloors 的子集,否则拒绝。
|
||||
*/
|
||||
private List<String> resolveEffectiveFloors(
|
||||
List<String> fallbackFloors, List<String> hostFloors,
|
||||
TenantVisitorFloorPolicyDto policy, String personId) {
|
||||
|
||||
if (policy == null) return fallbackFloors;
|
||||
|
||||
List<String> allow = parseAllowZoneIds(policy.getAllowZoneIds());
|
||||
if (CollectionUtils.isEmpty(allow)) return fallbackFloors;
|
||||
|
||||
// 安全校验:allow 中每个值必须在 hostFloors 中存在
|
||||
Set<String> hostSet = new HashSet<>(hostFloors);
|
||||
List<String> unknownAllow = allow.stream()
|
||||
.filter(a -> !hostSet.contains(a))
|
||||
.collect(Collectors.toList());
|
||||
if (!unknownAllow.isEmpty()) {
|
||||
this.logger.error("策略配置错误:allow 包含不在被访人 floorList 中的 zoneId!"
|
||||
+ "orgId={} policyId={} personId={} unknownAllow={} hostFloors={}",
|
||||
policy.getOrgId(), policy.getId(), personId, unknownAllow, hostFloors);
|
||||
throw new ServiceException("76260533",
|
||||
"策略配置了被访人无权访问的楼层,请联系管理员");
|
||||
}
|
||||
|
||||
this.logger.info("策略生效 orgId={} policyId={} v={} allow={} hostSize={}",
|
||||
policy.getOrgId(), policy.getId(), policy.getPolicyVersion(),
|
||||
allow.size(), hostFloors.size());
|
||||
return allow; // 直接用 allow,不求交
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 关键设计约束:allow ⊆ floorList(强制)
|
||||
|
||||
策略的 `allow_zone_ids` **必须是被访人 `floorList` 的子集**。因为生效逻辑是「有策略直接用 allow」,如果 allow 包含被访人无权访问的楼层 → 访客获得越权 → **直接拒绝**。
|
||||
|
||||
| 场景 | 处理 |
|
||||
|------|------|
|
||||
| `allow = [28F]`, `floorList = [28F, 29F]` | ✅ 正常:allow ⊆ floorList,生效 [28F] |
|
||||
| `allow = [28F, 99F]`, `floorList = [28F, 29F]` | ❌ 配置错误:99F 不在 floorList → ERROR + fail 76260533 |
|
||||
| `allow = [28F]`, `floorList = [29F, 30F]` | ❌ 配置错误:28F 不在 floorList → ERROR + fail 76260533 |
|
||||
|
||||
- 校验在 `resolveEffectiveFloors` 中执行
|
||||
- 不满足时 **`throw ServiceException("76260533")`**,请求失败
|
||||
- 新增错误码 **76260533**:「策略配置了被访人无权访问的楼层,请联系管理员」
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据迁移
|
||||
|
||||
### 5.1 执行顺序
|
||||
|
||||
```
|
||||
Step 1: DDL 上线(表结构变更,不影响行为)
|
||||
→ ALTER TABLE 加 org_id 列 + 新唯一约束
|
||||
|
||||
Step 2: 数据迁移(人工 SQL,按公司逐行执行)
|
||||
→ 查询 cw_is_organization 获取各公司 org_id
|
||||
→ UPDATE tenant_visitor_floor_policy SET org_id = ? WHERE ...
|
||||
|
||||
Step 3: 发应用包(代码切到 org_id 查询)
|
||||
→ Mapper SQL: business_id → org_id
|
||||
→ Service: 取 organizationIds 遍历查策略
|
||||
|
||||
Step 4(可选): 废弃 business_id 列
|
||||
→ MODIFY COLUMN ... COMMENT 'DEPRECATED'
|
||||
```
|
||||
|
||||
### 5.2 数据迁移 SQL 模板
|
||||
|
||||
```sql
|
||||
-- 1. 列出所有公司级组织节点
|
||||
SELECT o.ID, o.NAME, o.PARENT_ID
|
||||
FROM component-organization.cw_is_organization o
|
||||
WHERE o.BUSINESS_ID = '2524639890ba4f2cba9ba1a4eeaa4015'
|
||||
AND o.IS_DEL = 0
|
||||
ORDER BY o.NAME;
|
||||
|
||||
-- 2. 为指定公司设置策略
|
||||
UPDATE cw-elevator-application.tenant_visitor_floor_policy
|
||||
SET org_id = '<目标公司 org_id>'
|
||||
WHERE business_id = '2524639890ba4f2cba9ba1a4eeaa4015'
|
||||
AND org_id IS NULL;
|
||||
|
||||
-- 3. 为其他公司新增策略行
|
||||
INSERT INTO tenant_visitor_floor_policy
|
||||
(id, org_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, remark, created_at, updated_at)
|
||||
VALUES
|
||||
(REPLACE(UUID(),'-',''), '<org_id>', 'INTERSECT_ALLOWLIST', '["<zone_id>"]', NULL, 1, 1, '', UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);
|
||||
```
|
||||
|
||||
### 5.3 向后兼容
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| `org_id = NULL` 的行 | `WHERE org_id = ?` 不匹配,等同于无策略 |
|
||||
| 旧 `business_id` 策略未迁移 | 全量 floorList(与现网一致) |
|
||||
| 新代码 + 旧表结构 | SQL 报错 → 回滚应用包 |
|
||||
| 应用回滚 | 旧代码仍用 `business_id` 查询,行为不变 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 测试策略
|
||||
|
||||
| ID | 场景 | 期望结果 |
|
||||
|----|------|----------|
|
||||
| T1 | A公司有策略 allow=[28F],被访人 floorList=[28F,29F],UC-01 | effective=[28F](直接用 allow) |
|
||||
| T2 | A公司有策略 allow=[28F],被访人 floorList=[29F,30F],UC-01 | **fail 76260533**(28F 不在 floorList 中) |
|
||||
| T3 | B公司无策略,被访人 floorList=[15F,16F],UC-01 | effective=[15F,16F](用 floorList) |
|
||||
| T4 | 被访人属 [A公司, C公司],A有策略 allow=[28F] | 命中A策略→effective=[28F] |
|
||||
| T5 | 被访人属 [B公司, D公司],均无策略 | effective=floorList 全集 |
|
||||
| T6 | UC-02 传 [15F],B公司无策略 | effective=[15F](用调用方值) |
|
||||
| T7 | UC-02 传 [15F],A公司有策略 allow=[28F] | effective=[28F](策略优先,忽略调用方) |
|
||||
| T8 | 策略 enabled=0 | 等同无策略→用 floorList |
|
||||
| T9 | `allow_zone_ids` 为非法 JSON `["28F"` | ERROR 日志 + 等同无策略 |
|
||||
| T10 | 策略 allow=[28F,99F],floorList=[28F,29F] | **fail 76260533**(99F 不在 floorList) |
|
||||
| T11 | 回滚:新 DDL + 旧代码 | 行为不变 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与缓解
|
||||
|
||||
| 风险 | 概率 | 缓解 |
|
||||
|------|------|------|
|
||||
| `organizationIds` 为空(被访人无组织归属) | 低 | 降级为无策略→全量 floorList |
|
||||
| DBA 迁移时填错 org_id | 中 | 迁移前 `SELECT` 确认 NAME 匹配,迁移后抽样验证 |
|
||||
| 多公司共用一个 org_id(父子组织) | 低 | 当前需求是公司级粒度;未来如需子树继承可升级到方案C |
|
||||
| UC-02 调用方依赖旧行为(不查策略) | 中 | 发版说明明确标注行为变更;灰度发布 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 文件清单
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `docs/sql/tenant_visitor_floor_policy_v2.sql` | 新增 | DDL 变更脚本 |
|
||||
| `TenantVisitorFloorPolicyMapper.xml` | 修改 | `business_id` → `org_id` |
|
||||
| `TenantVisitorFloorPolicyMapper.java` | 修改 | 方法签名 |
|
||||
| `TenantVisitorFloorPolicyDao.java` | 修改 | 接口方法 |
|
||||
| `TenantVisitorFloorPolicyDaoImpl.java` | 修改 | 调用新 Mapper 方法 |
|
||||
| `TenantVisitorFloorPolicyDto.java` | 修改 | 新增 `orgId` 字段 |
|
||||
| `PersonRuleServiceImpl.java` | 修改 | `addVisitor` + 新增辅助方法 |
|
||||
|
||||
---
|
||||
|
||||
*本文档为 org_id 粒度修复的技术设计;实施以代码评审与安全评审结论为准。*
|
||||
@@ -0,0 +1,356 @@
|
||||
# org_id 策略修复 — 无鉴权验证方案设计
|
||||
|
||||
**文档性质**:验证方案设计说明
|
||||
**设计日期**:2026-05-01
|
||||
**状态**:待评审
|
||||
**关联 Spec**:`docs/superpowers/specs/2026-05-01-org-id-policy-fix-design.md`
|
||||
**被测应用**:`cw-elevator-application`(V2, port 18081)
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标
|
||||
|
||||
通过无鉴权 HTTP 调用 `POST /elevator/person/add/visitor` 和 `POST /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` 头) |
|
||||
|
||||
**请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"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 毫秒,访客有效期结束 |
|
||||
|
||||
**响应体(成功):**
|
||||
|
||||
```json
|
||||
{ "success": true, "code": "0", "data": true }
|
||||
```
|
||||
|
||||
**响应体(策略拒绝):**
|
||||
|
||||
```json
|
||||
{ "success": false, "code": "76260533", "message": "策略配置了被访人无权访问的楼层,请联系管理员" }
|
||||
```
|
||||
|
||||
#### POST /elevator/passRule/image(回读)
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| Method | POST |
|
||||
| Path | `/elevator/passRule/image` |
|
||||
| Body | `{ "personId": "<visitorId>" }` |
|
||||
|
||||
**响应体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```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
|
||||
|
||||
```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 执行顺序
|
||||
|
||||
```text
|
||||
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_1403,T3 后恢复 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。*
|
||||
@@ -0,0 +1,113 @@
|
||||
# org_id 策略修复 — 人工验证操作手册
|
||||
|
||||
## 前置条件
|
||||
|
||||
- V2 JAR 已构建:`cw-elevator-application-2.0.9.jar`
|
||||
- 配置文件:`/tmp/v2-redis-fix.properties`
|
||||
- Redis Docker:`v2-test-redis`(端口 6380,密码 `1qaz!QAZ`)
|
||||
- 桩服务脚本:`stub_org_service.py`
|
||||
|
||||
---
|
||||
|
||||
## 步骤 1:启动 V2 电梯应用
|
||||
|
||||
打开**终端 1**,执行:
|
||||
|
||||
```bash
|
||||
/usr/lib/jvm/java-8-openjdk-amd64/bin/java \
|
||||
-jar /media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/cw-elevator-application-starter/target/cw-elevator-application-2.0.9.jar \
|
||||
--spring.config.location=file:/tmp/v2-redis-fix.properties
|
||||
```
|
||||
|
||||
等待约 **35 秒**,看到 `Started ElevatorApplication` 后验证:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:18081/health
|
||||
```
|
||||
|
||||
期望输出:`{"status":"UP"}`
|
||||
|
||||
---
|
||||
|
||||
## 步骤 2:启动组织服务桩
|
||||
|
||||
打开**终端 2**,执行:
|
||||
|
||||
```bash
|
||||
python3 /media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/tools/stub_org_service.py
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:18082/health
|
||||
```
|
||||
|
||||
期望输出:`{"status":"UP"}`
|
||||
|
||||
---
|
||||
|
||||
## 步骤 3:运行验证脚本
|
||||
|
||||
打开**终端 3**,执行:
|
||||
|
||||
```bash
|
||||
cd /media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/tools/visitor_floor_verification
|
||||
python3 scripts/verify_org_policy_fix.py --elevator-base-url http://127.0.0.1:18081
|
||||
```
|
||||
|
||||
期望输出:
|
||||
|
||||
```
|
||||
=== Phase 2: run 7 cases ===
|
||||
[T1] 有策略→allow替换floorList → ✅
|
||||
[T2] 无策略→floorList → ✅
|
||||
[T3] allow含无效zone→拒绝 (76260533) → ✅
|
||||
[T4] 多组织命中第一个策略 → ✅
|
||||
[T5] enabled=0等同无策略 → ✅
|
||||
[T6] UC-02策略优先 → ✅
|
||||
[T7] 广发基金迁移验证 → ✅
|
||||
|
||||
Passed: 7/7
|
||||
```
|
||||
|
||||
报告文件:`report/org-policy-fix-verify-YYYYMMDD-HHMMSS.json`
|
||||
|
||||
---
|
||||
|
||||
## 步骤 4:停止服务
|
||||
|
||||
```bash
|
||||
# 终端1 按 Ctrl+C
|
||||
# 终端2 按 Ctrl+C
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
| 症状 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
| V2 启动报 `RedisConnectionException` | Redis 密码未配置或端口错误 | 确认 `v2-test-redis` 运行中:`docker ps \| grep v2-test-redis` |
|
||||
| V2 报 `UnknownHostException: mysql_01` | ShardingSphere 数据源未覆盖 | 确认使用了 `--spring.config.location=file:/tmp/v2-redis-fix.properties` |
|
||||
| 桩服务端口被占用 | 上次未正常退出 | `pkill -f stub_org_service` |
|
||||
| 验证脚本 `Connection refused` | V2 或桩未启动 | 检查终端 1/2 的服务日志 |
|
||||
| DB 连接失败 | MySQL 密码错误 | 确认 `192.168.3.12:3307 root/123456` 可达 |
|
||||
|
||||
---
|
||||
|
||||
## 本地 Docker 基础设施
|
||||
|
||||
| 服务 | 容器名 | 端口 | 说明 |
|
||||
|------|--------|------|------|
|
||||
| Redis | `v2-test-redis` | 6380 | 密码 `1qaz!QAZ` |
|
||||
| Kafka | `ybs-kafka` | 9092 | 无认证 |
|
||||
| Consul | — | 192.168.3.12:8500 | 远程 |
|
||||
| MySQL | — | 192.168.3.12:3307 | root/123456 |
|
||||
|
||||
如需重建 Redis:
|
||||
|
||||
```bash
|
||||
docker rm -f v2-test-redis
|
||||
docker run -d --name v2-test-redis -p 6380:6379 redis:7-alpine --requirepass "1qaz!QAZ"
|
||||
```
|
||||
@@ -0,0 +1,87 @@
|
||||
# 电梯应用 — 服务发现架构设计
|
||||
|
||||
**日期**:2026-05-01
|
||||
**版本**:V2(与 V1 生产对齐)
|
||||
**状态**:已实施
|
||||
|
||||
## 1. 核心决策
|
||||
|
||||
电梯应用(cw-elevator-application)的三个上游 Feign 客户端 **统一走 Dubbo/ZooKeeper 动态发现**,不依赖任何 `@RibbonClient` 定制或 `ConfigurationBasedServerList` 静态列表。
|
||||
|
||||
| 上游服务 | Feign 名称 | 发现方式 |
|
||||
|---------|-----------|---------|
|
||||
| 人脸识别 GPU | `ninca-crk-std` | Dubbo/ZooKeeper |
|
||||
| 组织组件 | `ninca-common-component-organization` | Dubbo/ZooKeeper |
|
||||
| 公共组件 | `ninca-common` | Dubbo/ZooKeeper |
|
||||
|
||||
## 2. 服务发现链路
|
||||
|
||||
```
|
||||
ElevatorApplication
|
||||
│ @EnableFeignClients(basePackages={...})
|
||||
│
|
||||
├── Feign Client ──→ Ribbon (默认) ──→ DiscoveryClient ──→ Dubbo Registry
|
||||
│ │
|
||||
│ zookeeper://10.0.22.207:2181
|
||||
│ │
|
||||
├── ZoneFeignClient → ninca-common ──────────────────┘
|
||||
├── PersonRecordEventHandler → ninca-common-component-organization ─┘
|
||||
└── ElevatorRecordSendTask → ninca-crk-std ────────────────────┘
|
||||
```
|
||||
|
||||
**启动类**:`ElevatorApplication.java` — 无 `@RibbonClients` 注解,Ribbon 走默认 `DiscoveryClient` 模式。
|
||||
|
||||
## 3. 配置清单
|
||||
|
||||
### bootstrap.properties(生产)
|
||||
|
||||
```properties
|
||||
server.port=16112
|
||||
spring.application.name=elevator-app
|
||||
spring.profiles.active=access-control
|
||||
|
||||
# Consul — 仅注册自身,不作为发现客户端
|
||||
spring.cloud.consul.host=371bfca4972c43d2aefcf302d0a4a277
|
||||
spring.cloud.consul.port=8500
|
||||
spring.cloud.consul.discovery.enabled=false
|
||||
|
||||
# Dubbo/ZooKeeper — 服务发现
|
||||
dubbo.registry.address=zookeeper://10.0.22.207:2181
|
||||
dubbo.protocol.port=16107
|
||||
dubbo.provider.version=1.0
|
||||
```
|
||||
|
||||
### application.properties — 服务名映射
|
||||
|
||||
```properties
|
||||
feign.ninca-crk-std.name=ninca-crk-std
|
||||
feign.component-organization.name=ninca-common-component-organization
|
||||
feign.ninca-common.name=ninca-common
|
||||
```
|
||||
|
||||
> 以上均为 Feign 路由名称,不需 `ribbon.listOfServers` 或 `NIWSServerListClassName`。
|
||||
|
||||
## 4. V1 vs V2 对比
|
||||
|
||||
| | V1 生产 | V2(修复前) | V2(修复后) |
|
||||
|------|---------|------------|------------|
|
||||
| ninca-crk-std | Dubbo | ConfigurationBasedServerList(空→报错) | Dubbo |
|
||||
| ninca-common-component-organization | Dubbo | ConfigurationBasedServerList(空→报错) | Dubbo |
|
||||
| ninca-common | Dubbo | ConfigurationBasedServerList(空→报错) | Dubbo |
|
||||
| @RibbonClients | 无 | 3 个 | **无** |
|
||||
| RibbonConfiguration 类 | 无 | 3 个 | **无** |
|
||||
|
||||
## 5. 删除清单
|
||||
|
||||
| 文件 | 操作 |
|
||||
|------|------|
|
||||
| `NincaCrkStdRibbonConfiguration.java` | 删除 |
|
||||
| `OrgServiceRibbonConfiguration.java` | 删除 |
|
||||
| `CommonServiceRibbonConfiguration.java` | 删除 |
|
||||
| `ElevatorApplication.java` 中 `@RibbonClients` 块 | 删除 |
|
||||
|
||||
## 6. 运维说明
|
||||
|
||||
- 若生产 ZooKeeper (`10.0.22.207:2181`) 不可达,三个 Feign 调用将报 `Load balancer does not have available server`——此为基础设施问题,非代码缺陷。
|
||||
- `deploy/v2-maven/application.properties` 中无需添加 `*.ribbon.listOfServers`。
|
||||
- 本地测试时可通过 `ninca-common-component-organization.ribbon.listOfServers=127.0.0.1:18082` 覆盖 DiscoveryClient 指向桩服务(需同时设置 `NIWSServerListClassName` 全局限定或通过 `@RibbonClient` 仅在测试 Profile 启用)。
|
||||
@@ -0,0 +1,280 @@
|
||||
# V2 全系统功能测试环境搭建 — 设计文档
|
||||
|
||||
**日期**: 2026-05-01
|
||||
**状态**: 待实施
|
||||
**关联**: AGENTS.md, maven-cw-elevator-application/deploy/
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标
|
||||
|
||||
搭建 cw-elevator-application V2 (v2.0.7) 的全系统集成功能测试环境,包含电梯应用及其所有上下游依赖服务(CRK人脸识别、报警、cwos-manager、ninca-common、component-organization等),能够:
|
||||
|
||||
- 一键搭建全部 17 个组件
|
||||
- 运行 V1 vs V2 API 对拍测试
|
||||
- 验证 V2.0.7 租户访客固定楼层功能
|
||||
- 验证 CRK 联动、报警联动等端到端链路
|
||||
- 15-20 分钟内完成从零到全部验证
|
||||
|
||||
---
|
||||
|
||||
## 2. 环境约束
|
||||
|
||||
| 约束 | 值 |
|
||||
|------|-----|
|
||||
| 部署方式 | 本机进程 (Java服务) + Docker 容器 (基础组件) |
|
||||
| MySQL | 复用现有: `192.168.3.12:3307`, `root/123456` |
|
||||
| JDK | Java 8 (`/usr/lib/jvm/java-8-openjdk-amd64`) |
|
||||
| Maven | 3.5+ |
|
||||
| Docker | 已安装, `docker compose` 可用 |
|
||||
| 代码分支 | `release/cw-elevator-v1-lib-min-risk` |
|
||||
| 测试范围 | 全系统集成 — 全部 17 个组件 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 组件清单
|
||||
|
||||
### 3.1 基础组件 (Docker)
|
||||
|
||||
| # | 组件 | 端口 | 来源 |
|
||||
|---|------|------|------|
|
||||
| I1 | Consul | 8500 | 已有 `deploy/consul-docker/docker-compose.yml` |
|
||||
| I2 | Redis | 6379 | 需要新增 compose 条目 (`redis:7-alpine`) |
|
||||
| I3 | Kafka + Zookeeper | 9092, 2181 | 需要新增 compose 条目 (`bitnami/kafka:3.6`) |
|
||||
| I4 | Nginx (前端代理) | 8090 | 已有 `docker-compose.frontend-local.yml` |
|
||||
|
||||
### 3.2 应用服务 (本机进程)
|
||||
|
||||
| # | 组件 | 端口 | 来源 | 数据库 |
|
||||
|---|------|------|------|--------|
|
||||
| A1 | ninca-common-backend | 33010 | `星中心/ninca_common_01-ninca_common_backend.tar.gz` | `ninca_common` |
|
||||
| A2 | component-organization | 33011 | `星中心/ninca_common_component_organization_01-...tar.gz` | `component-organization` |
|
||||
| A3 | cwos-system-api | 3333 | `星中心/cwos_system_api_01-cwos_system_api/` (已解压) | — |
|
||||
| A4 | cwos-manager | 3721 | Docker Harbor 镜像 (已有 compose) | `cwos_manager` |
|
||||
| A5 | cwos-portal | 33008 | `星中心/cwos_portal` SQL 备份 → 重建 | `cwos_portal` |
|
||||
| A6 | snap-app | 33012 | `星中心/ninca_common_snap_app_01-...tar.gz` | — |
|
||||
| A7 | vehicle-app | 33013 | `星中心/ninca_common_vehicle_app_01-...tar.gz` | — |
|
||||
| A8 | person-file-app | 33014 | `星中心/ninca-person-file-app-V2.9.2_20210216.tar.gz` | — |
|
||||
| A9 | monitor-app | 33015 | `星中心/ninca_common_monitor_app_01-...tar.gz` | — |
|
||||
| A10 | CRK-std | 16106 (app), 16114 (mgmt) | `星中心/ninca_crk_std_01-ninca_crk_std_backend/` (JAR已就绪) | `ninca_crk_std` |
|
||||
| A11 | alarm-app | 17011 (app), 17211 (mgmt) | `星中心/ninca_qk_alarm_app_01-ninca_qk_alarm_app/` (JAR已就绪) | `alarm_deploy` |
|
||||
| A12 | elevator V2 | 18081 | Maven 构建 `maven-cw-elevator-application` | `cw-elevator-application` |
|
||||
| A13 | elevator V1 | 18080 | 对照基线 (已有 JAR) | `cw-elevator-application` |
|
||||
|
||||
### 3.3 数据库 (MySQL 192.168.3.12:3307)
|
||||
|
||||
| 数据库 | 备份文件 | 大小 |
|
||||
|--------|---------|------|
|
||||
| `cw-elevator-application` | `data_backup/12_*.sql.gz` + `34_*.sql.gz` | 158M + 2.5G |
|
||||
| `ninca_crk_std` | (含在 CRK 配置中,需确认备份) | — |
|
||||
| `alarm_deploy` | `data_backup/alarm_deploy_*.sql.gz` | 15M |
|
||||
| `cwos_manager` | `data_backup/cwos_manager_*.sql.gz` | 51K |
|
||||
| `cwos_portal` | `data_backup/cwos_portal_*.sql.gz` | 212M |
|
||||
| `ninca_common` | `data_backup/ninca_common_*.sql.gz` | 6M |
|
||||
| `component-organization` | `data_backup/component-organization_*.sql.gz` | 212M |
|
||||
| `ods` | `data_backup/ods_*.sql.gz` | 485K |
|
||||
| `cloudwalk_device_thirdparty` | `data_backup/cloudwalk_device_thirdparty_*.sql.gz` | 4K |
|
||||
| `g` / `p` / `12` / `34` | 其他备份 | 小量 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 配置统一化
|
||||
|
||||
### 4.1 环境变量 (`config/env.sh`)
|
||||
|
||||
```bash
|
||||
# 基础设施
|
||||
MYSQL_HOST=192.168.3.12
|
||||
MYSQL_PORT=3307
|
||||
MYSQL_USER=root
|
||||
MYSQL_PASS=123456
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASS=1qaz!QAZ
|
||||
CONSUL_HOST=127.0.0.1
|
||||
CONSUL_PORT=8500
|
||||
KAFKA_HOST=127.0.0.1
|
||||
KAFKA_PORT=9092
|
||||
ZK_HOST=127.0.0.1
|
||||
ZK_PORT=2181
|
||||
|
||||
# 服务端口
|
||||
PORT_ELEVATOR_V2=18081
|
||||
PORT_ELEVATOR_V1=18080
|
||||
PORT_CRK_STD=16106
|
||||
PORT_CRK_MGMT=16114
|
||||
PORT_ALARM=17011
|
||||
PORT_ALARM_MGMT=17211
|
||||
PORT_CWOS_PORTAL=33008
|
||||
PORT_COMPONENT_ORG=33011
|
||||
PORT_NINCA_COMMON=33010
|
||||
PORT_CWOS_MANAGER=3721
|
||||
PORT_SYSTEM_API=3333
|
||||
PORT_SNAP=33012
|
||||
PORT_VEHICLE=33013
|
||||
PORT_PERSON_FILE=33014
|
||||
PORT_MONITOR=33015
|
||||
PORT_NGINX=8090
|
||||
|
||||
# Java
|
||||
JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
|
||||
JAVA_OPTS="-Xmx2048m -Xms512m"
|
||||
```
|
||||
|
||||
### 4.2 配置替换规则
|
||||
|
||||
每个 Java 服务的 `application.properties` / `bootstrap.properties` 中需要替换:
|
||||
|
||||
| 原始值 (生产) | 替换为 (测试) |
|
||||
|--------------|-------------|
|
||||
| `mysql_01.mysql_ip` | `192.168.3.12` |
|
||||
| `redis_01.redis_ip` | `127.0.0.1` |
|
||||
| `371bfca....` (Consul UUID host) | `127.0.0.1` |
|
||||
| `44700995e....` (Kafka broker 1) | `127.0.0.1:9092` |
|
||||
| `0837a70b5....` (Kafka broker 2) | (移除,仅保留一个 broker) |
|
||||
| `10.128.161.95` (内网 IP 地址) | `127.0.0.1` |
|
||||
| `10.0.22.207` (内网 ZK) | `127.0.0.1:2181` |
|
||||
| `10.128.123.108` (内网 DB) | `192.168.3.12` |
|
||||
| `10.0.22.102` (内网 CRK IP) | `127.0.0.1` |
|
||||
| `3306` (生产 DB 端口) | `3307` |
|
||||
|
||||
---
|
||||
|
||||
## 5. 脚本设计
|
||||
|
||||
### 5.1 文件结构
|
||||
|
||||
```
|
||||
源码/scripts/test-env/
|
||||
├── docker-compose.infra.yml # 合并 Consul+Redis+Kafka+Nginx
|
||||
├── config/
|
||||
│ ├── env.sh # 统一环境变量
|
||||
│ ├── consul/consul-config.json # Consul 初始化配置
|
||||
│ └── service-templates/ # 配置文件模板 (.properties 模板)
|
||||
├── setup.sh # 主入口: 一键搭建
|
||||
├── start-all.sh # 启动全部服务
|
||||
├── stop-all.sh # 停止全部服务
|
||||
├── health-check.sh # 探活检查
|
||||
├── prepare-db.sh # 数据库恢复
|
||||
├── prepare-services.sh # 解压 + 配置注入
|
||||
├── build-elevator-v2.sh # 编译 V2 电梯应用
|
||||
└── verify-functional.sh # 功能验证
|
||||
```
|
||||
|
||||
### 5.2 启动依赖拓扑
|
||||
|
||||
```
|
||||
I1 Consul ──┐
|
||||
I2 Redis ───┤ (Docker 并行启动)
|
||||
I3 Kafka ───┤
|
||||
I4 Nginx ───┘
|
||||
|
||||
A1 ninca-common ─────────────────────────┐
|
||||
A2 component-org ←── common ─────────────┤
|
||||
A3 system-api ←── manager ───────────────┤
|
||||
A4 cwos-manager (Docker) ────────────────┤
|
||||
A5 cwos-portal ←── org + manager ────────┤
|
||||
A6 snap-app ←── portal + common ─────────┤
|
||||
A7 vehicle-app ←── portal + common ──────┤
|
||||
A8 person-file ←── portal ───────────────┤
|
||||
A9 monitor-app (独立) ───────────────────┤
|
||||
A10 CRK-std ←── portal + org + elevator ─┤
|
||||
A11 alarm-app ←── portal + org + Kafka ──┤
|
||||
A12 elevator-V2 ←── CRK + portal + org ──┤
|
||||
A13 elevator-V1 (对拍对照) ───────────────┘
|
||||
```
|
||||
|
||||
### 5.3 主入口执行流程
|
||||
|
||||
```
|
||||
setup.sh:
|
||||
Phase 1: 环境检查 (< 10s)
|
||||
├── JDK 8 检查
|
||||
├── Maven 检查
|
||||
├── Docker 检查
|
||||
├── MySQL 连通性检查 (192.168.3.12:3307)
|
||||
└── 端口冲突检查
|
||||
|
||||
Phase 2: 数据库准备 (~5min)
|
||||
├── 创建所有数据库 (如不存在)
|
||||
├── 解压并导入 11 个 SQL 备份
|
||||
└── 执行 V2 DDL (tenant_visitor_floor_policy.sql)
|
||||
|
||||
Phase 3: Docker 启动 (~30s)
|
||||
├── docker compose up infra
|
||||
├── 等待 Consul Ready
|
||||
├── 等待 Kafka Ready
|
||||
└── 启动 Nginx
|
||||
|
||||
Phase 4: 服务准备 (~5-10min)
|
||||
├── 解压 5 个 tar.gz
|
||||
├── 从模板生成各服务配置文件
|
||||
├── 编译 V2 电梯应用
|
||||
└── 同步 JAR 到 deploy/
|
||||
|
||||
Phase 5: 服务启动 (~2min)
|
||||
├── 按拓扑序依次启动 13 个 Java 服务
|
||||
├── 每个服务启动后探活确认
|
||||
└── 失败自动重试 (max 3 次)
|
||||
|
||||
Phase 6: 验证 (~2min)
|
||||
├── 全端口探活
|
||||
├── Consul 注册检查
|
||||
├── V1/V2 API 对拍 (pytest)
|
||||
└── 输出验证报告
|
||||
|
||||
总耗时: ~15-20 分钟
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 验证矩阵
|
||||
|
||||
| ID | 验证项 | 命令/方法 | 通过标准 |
|
||||
|----|--------|----------|---------|
|
||||
| H1 | 所有端口可达 | `nc -z 127.0.0.1 <port>` x 15 | 全部 open |
|
||||
| H2 | 所有 HTTP 健康检查 | `curl /actuator/health` x 8 | 全部 200 + UP |
|
||||
| H3 | Consul 服务注册 | `curl :8500/v1/agent/services` | 10+ 服务已注册 |
|
||||
| F1 | V1/V2 API 对拍 | `pytest tools/elevator_api_parity/tests/` | 全部通过 |
|
||||
| F2 | V2.0.7 UC-01 基线 | 不传 floorIds, 无策略 | floorList 全集 |
|
||||
| F3 | V2.0.7 UC-01 固定楼层 | 不传 floorIds, 有策略 | floorList ∩ allow |
|
||||
| F4 | V2.0.7 无交集 | allow 与 floorList 无交集 | 错误码 76260532 |
|
||||
| F5 | V2.0.7 被访人无楼层 | personId 无楼层 | 错误码 76260531 |
|
||||
| F6 | V2.0.7 UC-02 | 显式传 floorIds | 不读策略表 |
|
||||
| F7 | CRK 联动 | curl CRK → 电梯回调 | CRK 日志有 Feign 记录 |
|
||||
| F8 | 报警 Kafka 消费 | 提交事件 → alarm 日志 | Kafka 消息到达 |
|
||||
| F9 | Nginx 前端代理 | `curl :8090` | cwos-portal 首页 |
|
||||
| I1 | MySQL 连通 | `mysql -h 192.168.3.12 -P3307 -e 'SELECT 1'` | 1 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 已知风险与缓解
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
|------|------|------|
|
||||
| `星中心/*.tar.gz` 内 JAR 版本与 DB 备份不匹配 | 启动失败 | 从同一时间点导出 (2026-04-23) 的 DB 备份对齐 |
|
||||
| Docker Kafka 首次启动慢 | Phase 3 超时 | 脚本增加 60s 等待 + 重试逻辑 |
|
||||
| V2 Maven 编译需 Nexus 私服 | 编译失败 | 已有 `build_nexus_only.sh` 降级方案 |
|
||||
| cwos-portal 无独立 JAR | 无法启动 | 从 DB 备份 `cwos_portal` 库确认部署方式 |
|
||||
| 端口冲突 (已有进程占用) | 启动失败 | Phase 1 预先检查, 用 `lsof` 检测并提示 |
|
||||
| ES (Elasticsearch) 依赖 | alarm 启动可能失败 | alarm `application.properties` 配置 ES, 需 Docker 添加 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 未解决问题
|
||||
|
||||
1. **cwos-portal 部署方式**: 星中心中没有 cwos-portal 的 JAR 或 tarball, 仅 DB 备份中有 `cwos_portal` 数据。需确认其部署形态 (fat JAR? war? Docker?)。
|
||||
2. **cwos-system-api 启动入口**: `cwos_system_api_01-cwos_system_api/` 已解压, 需确认 JAR 入口和端口。
|
||||
3. **Elasticsearch**: alarm 依赖 ES (`elasticsearch.ip`), 是否需要在 Docker Compose 中添加 ES 容器?
|
||||
4. **Dubbo/ZK**: V1 电梯 bootstrap 配置有 Dubbo + ZK, V2 是否需要?可以在 Docker Compose 中添加 ZK。
|
||||
|
||||
---
|
||||
|
||||
## 9. 产出物
|
||||
|
||||
- `源码/scripts/test-env/docker-compose.infra.yml` — 基础组件 Docker Compose
|
||||
- `源码/scripts/test-env/setup.sh` — 一键搭建脚本
|
||||
- `源码/scripts/test-env/config/env.sh` — 统一环境变量
|
||||
- `源码/scripts/test-env/config/service-templates/` — 14 个配置模板
|
||||
- `源码/scripts/test-env/start-all.sh` / `stop-all.sh` — 启停脚本
|
||||
- `源码/scripts/test-env/verify-functional.sh` — 功能验证脚本
|
||||
Reference in New Issue
Block a user