- .kiro/specs/ → docs/specs/(41 个历史需求 spec 迁移,移除 .config.kiro) - CLAUDE.md 三层拆分:根文件精简 + apps/backend/CLAUDE.md + .claude/commands/ - 新增 /spec-close、/pre-change 两个工作流命令 - DDL 基线刷新(从测试库重新导出 11 个文件,dws 35→38 表,biz 18→21 表) - BD_Manual → BD_manual 命名统一(48 个文件) - 修复 3 处文档与数据库不一致(auth.users.status 默认值、scheduled_tasks 字段、RLS 视图数) - 新增 BD_manual_public_rbac_tables.md(public schema 8 张 RBAC/工作流表) - 合并 biz.trigger_jobs 文档(10→12 字段,归档独立文档) - docs/database/README.md 索引更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
255 lines
9.9 KiB
Markdown
255 lines
9.9 KiB
Markdown
# BD 手册:biz 注册体系表(NS4.1 registry)
|
||
|
||
## 概述
|
||
|
||
NS4.1 注册体系新增 4 张表,全部位于 `biz` Schema,建立「连接器 → 租户 → 店铺」三级注册体系。合并原 `auth.site_code_mapping`,统一管理上游 SaaS 系统、租户和店铺的关系,并为简写ID 提供归属和变更历史。
|
||
|
||
所有表位于 `zqyy_app` / `test_zqyy_app` 数据库。
|
||
|
||
## 变更原因
|
||
|
||
- NS4.1 注册体系设计,建立项目级「连接器 → 租户 → 店铺」三级结构
|
||
- 合并原 `auth.site_code_mapping` 到 `biz.sites`,增加租户关联和简写ID 变更历史管理
|
||
- 数据来源:种子数据从 `auth.site_code_mapping` 迁移,ETL 增量同步从 `dwd.dim_site`
|
||
|
||
## 变更说明
|
||
|
||
| 库 | Schema | 表 | 变更类型 | 说明 |
|
||
|----|--------|---|---------|------|
|
||
| zqyy_app | biz | connectors | 新建 | 连接器注册表 |
|
||
| zqyy_app | biz | tenants | 新建 | 租户注册表 |
|
||
| zqyy_app | biz | sites | 新建 | 店铺注册表(合并原 auth.site_code_mapping) |
|
||
| zqyy_app | biz | site_code_history | 新建 | 简写ID 变更历史表 |
|
||
| zqyy_app | auth | site_code_mapping | 废弃重命名 | → `auth._archived_site_code_mapping` |
|
||
|
||
---
|
||
|
||
## 1. biz.connectors — 连接器注册表
|
||
|
||
记录本项目接入的上游 SaaS 系统。当前仅有飞球(feiqiu)一个连接器,预留多连接器扩展能力。
|
||
|
||
### 表结构
|
||
|
||
| 列名 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| id | SERIAL | PRIMARY KEY | 自增主键 |
|
||
| connector_key | VARCHAR(50) | UNIQUE NOT NULL | 连接器标识(如 `'feiqiu'`) |
|
||
| display_name | VARCHAR(100) | NOT NULL | 显示名称 |
|
||
| is_active | BOOLEAN | NOT NULL DEFAULT true | 是否启用 |
|
||
| created_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
|
||
|
||
### 约束与索引
|
||
|
||
| 名称 | 类型 | 列 | 说明 |
|
||
|------|------|---|------|
|
||
| connectors_pkey | PRIMARY KEY | id | 主键 |
|
||
| connectors_connector_key_key | UNIQUE | connector_key | 连接器标识唯一 |
|
||
|
||
### 种子数据
|
||
|
||
```sql
|
||
INSERT INTO biz.connectors (connector_key, display_name) VALUES ('feiqiu', '飞球');
|
||
```
|
||
|
||
---
|
||
|
||
## 2. biz.tenants — 租户注册表
|
||
|
||
连接器下的租户,`tenant_id` 来自上游系统。同一连接器下 `tenant_id` 唯一。
|
||
|
||
### 表结构
|
||
|
||
| 列名 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| id | SERIAL | PRIMARY KEY | 自增主键 |
|
||
| connector_id | INTEGER | NOT NULL FK → biz.connectors(id) | 所属连接器 |
|
||
| tenant_id | BIGINT | NOT NULL | 上游系统租户 ID |
|
||
| tenant_name | VARCHAR(200) | — | 租户名称 |
|
||
| is_active | BOOLEAN | NOT NULL DEFAULT true | 是否启用 |
|
||
| created_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
|
||
| updated_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 更新时间 |
|
||
|
||
### 约束与索引
|
||
|
||
| 名称 | 类型 | 列 | 说明 |
|
||
|------|------|---|------|
|
||
| tenants_pkey | PRIMARY KEY | id | 主键 |
|
||
| tenants_connector_id_tenant_id_key | UNIQUE | (connector_id, tenant_id) | 同一连接器下租户唯一 |
|
||
| tenants_connector_id_fkey | FOREIGN KEY | connector_id → biz.connectors(id) | 外键关联连接器 |
|
||
|
||
### 种子数据
|
||
|
||
```sql
|
||
INSERT INTO biz.tenants (connector_id, tenant_id, tenant_name)
|
||
VALUES (1, 2790683160709957, '朗朗桌球');
|
||
```
|
||
|
||
---
|
||
|
||
## 3. biz.sites — 店铺注册表
|
||
|
||
合并原 `auth.site_code_mapping`,增加租户关联和简写ID 管理。`site_id` 来自上游系统,全局唯一。`site_code` 为当前生效的简写ID(6 位字符,3+3 格式),全局唯一。
|
||
|
||
### 表结构
|
||
|
||
| 列名 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| id | SERIAL | PRIMARY KEY | 自增主键 |
|
||
| tenant_id | INTEGER | NOT NULL FK → biz.tenants(id) | 所属租户 |
|
||
| site_id | BIGINT | NOT NULL UNIQUE | 上游系统门店 ID |
|
||
| site_name | VARCHAR(200) | — | 门店名称 |
|
||
| site_code | VARCHAR(6) | UNIQUE | 当前生效的简写ID(3+3 格式,如 `LLQ001`) |
|
||
| site_label | VARCHAR(50) | — | 门店标签 |
|
||
| is_active | BOOLEAN | NOT NULL DEFAULT true | 是否启用 |
|
||
| created_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
|
||
| updated_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 更新时间 |
|
||
|
||
### 约束与索引
|
||
|
||
| 名称 | 类型 | 列 | 说明 |
|
||
|------|------|---|------|
|
||
| sites_pkey | PRIMARY KEY | id | 主键 |
|
||
| sites_site_id_key | UNIQUE | site_id | 上游门店 ID 唯一 |
|
||
| sites_site_code_key | UNIQUE | site_code | 简写ID 全局唯一 |
|
||
| sites_tenant_id_fkey | FOREIGN KEY | tenant_id → biz.tenants(id) | 外键关联租户 |
|
||
|
||
### 数据迁移
|
||
|
||
```sql
|
||
-- 从 auth.site_code_mapping 迁移真实数据
|
||
INSERT INTO biz.sites (tenant_id, site_id, site_name, site_code)
|
||
SELECT t.id, scm.site_id, scm.site_name, scm.site_code
|
||
FROM auth.site_code_mapping scm
|
||
JOIN biz.tenants t ON t.tenant_id = scm.tenant_id
|
||
WHERE scm.tenant_id IS NOT NULL;
|
||
|
||
-- ETL 增量同步补充:通过 FDW 读取 dwd.dim_site(scd2_is_current=1),
|
||
-- 补充 auth.site_code_mapping 中没有但 dwd.dim_site 中有的店铺
|
||
```
|
||
|
||
---
|
||
|
||
## 4. biz.site_code_history — 简写ID 变更历史表
|
||
|
||
增量记录所有使用过的简写ID。`site_code` 全局唯一(含历史),确保已退役的 code 不会被重新分配。每个 `site_id` 最多一条 `is_current=true` 记录。
|
||
|
||
### 表结构
|
||
|
||
| 列名 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| id | SERIAL | PRIMARY KEY | 自增主键 |
|
||
| site_id | BIGINT | NOT NULL | 关联 biz.sites.site_id |
|
||
| site_code | VARCHAR(6) | NOT NULL UNIQUE | 简写ID(全局唯一,含历史) |
|
||
| is_current | BOOLEAN | NOT NULL DEFAULT false | true=当前生效,每个 site_id 最多一条 |
|
||
| created_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
|
||
| retired_at | TIMESTAMPTZ | — | 退役时间(is_current=false 时设置) |
|
||
|
||
### 约束与索引
|
||
|
||
| 名称 | 类型 | 列 | 说明 |
|
||
|------|------|---|------|
|
||
| site_code_history_pkey | PRIMARY KEY | id | 主键 |
|
||
| site_code_history_site_code_key | UNIQUE | site_code | 简写ID 全局唯一(含历史) |
|
||
|
||
### 初始数据
|
||
|
||
```sql
|
||
-- 为已有 site_code 的店铺创建历史记录
|
||
INSERT INTO biz.site_code_history (site_id, site_code, is_current)
|
||
SELECT site_id, site_code, true
|
||
FROM biz.sites
|
||
WHERE site_code IS NOT NULL;
|
||
```
|
||
|
||
---
|
||
|
||
## 兼容性
|
||
|
||
| 组件 | 影响 |
|
||
|------|------|
|
||
| 后端 API | 全部切换到 `biz.sites` + `biz.site_code_history`。新增 `admin_registry` 路由模块(租户列表、店铺列表、简写ID 管理)。`admin_tenant_admins` 路由中 `tenant_id` 从 `biz.tenants` 选择。`tenant_users` 路由中 site_code 查询从 `auth.site_code_mapping` 切换到 `biz.sites` |
|
||
| ETL | 无直接影响。店铺同步通过 FDW 只读访问 ETL 库 `dwd.dim_site`,写入 `biz.sites`。ETL 流程本身不变 |
|
||
| 小程序 | 无需改动。用户申请时的 site_code 验证由后端 API 透明切换到 `biz.sites` + `biz.site_code_history` |
|
||
| 管理后台(admin-web) | 新增注册体系 API 调用(`src/api/registry.ts`),租户管理员创建流程从 `biz.tenants`/`biz.sites` 选择 |
|
||
| 原 auth.site_code_mapping | 迁移完成后重命名为 `auth._archived_site_code_mapping`,保留供回滚 |
|
||
|
||
## 回滚策略
|
||
|
||
### 完整回滚(逆序 DROP + 恢复原表)
|
||
|
||
```sql
|
||
BEGIN;
|
||
-- 1. 逆序删除注册体系表
|
||
DROP TABLE IF EXISTS biz.site_code_history CASCADE;
|
||
DROP TABLE IF EXISTS biz.sites CASCADE;
|
||
DROP TABLE IF EXISTS biz.tenants CASCADE;
|
||
DROP TABLE IF EXISTS biz.connectors CASCADE;
|
||
|
||
-- 2. 恢复原表(如已重命名)
|
||
ALTER TABLE IF EXISTS auth._archived_site_code_mapping
|
||
RENAME TO site_code_mapping;
|
||
COMMENT ON TABLE auth.site_code_mapping IS '店铺简写ID 映射表(已恢复)';
|
||
COMMIT;
|
||
```
|
||
|
||
注意:
|
||
- 回滚前需确认后端代码已切换回 `auth.site_code_mapping` 查询
|
||
- `CASCADE` 会级联删除依赖对象
|
||
- 如果 `biz.sites` 中已有新增店铺(ETL 同步补充的),回滚后这些数据将丢失
|
||
|
||
## 验证 SQL
|
||
|
||
```sql
|
||
-- 1. 验证 biz.sites 中已有 site_code 的店铺数量
|
||
SELECT COUNT(*) FROM biz.sites WHERE site_code IS NOT NULL;
|
||
-- 预期:与原 auth.site_code_mapping 中有 site_code 的行数一致
|
||
|
||
-- 2. 验证 sites 与 site_code_history 的一致性
|
||
SELECT s.site_id, s.site_code, h.is_current
|
||
FROM biz.sites s
|
||
LEFT JOIN biz.site_code_history h
|
||
ON h.site_id = s.site_id AND h.site_code = s.site_code
|
||
WHERE s.site_code IS NOT NULL;
|
||
-- 预期:所有行的 h.is_current = true(每个有 code 的店铺在历史表中有对应的当前记录)
|
||
|
||
-- 3. 验证三级注册体系关联完整性
|
||
SELECT c.connector_key, t.tenant_name, COUNT(s.id) AS site_count
|
||
FROM biz.connectors c
|
||
JOIN biz.tenants t ON t.connector_id = c.id
|
||
LEFT JOIN biz.sites s ON s.tenant_id = t.id
|
||
GROUP BY c.connector_key, t.tenant_name;
|
||
-- 预期:至少 1 行(feiqiu / 朗朗桌球 / N),site_count > 0
|
||
|
||
-- 4. 验证 4 张注册体系表全部存在
|
||
SELECT table_name
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'biz'
|
||
AND table_name IN ('connectors', 'tenants', 'sites', 'site_code_history')
|
||
ORDER BY table_name;
|
||
-- 预期:返回 4 行
|
||
|
||
-- 5. 验证 site_code 全局唯一性(sites + history 无冲突)
|
||
SELECT site_code, COUNT(*) AS cnt
|
||
FROM biz.site_code_history
|
||
GROUP BY site_code
|
||
HAVING COUNT(*) > 1;
|
||
-- 预期:返回 0 行(每个 site_code 在历史表中最多出现一次)
|
||
|
||
-- 6. 验证种子数据
|
||
SELECT connector_key, display_name FROM biz.connectors WHERE connector_key = 'feiqiu';
|
||
-- 预期:1 行(feiqiu / 飞球)
|
||
SELECT tenant_id, tenant_name FROM biz.tenants WHERE tenant_id = 2790683160709957;
|
||
-- 预期:1 行(2790683160709957 / 朗朗桌球)
|
||
```
|
||
|
||
## 关联文件
|
||
|
||
- DDL 基线(biz):`docs/database/ddl/zqyy_app__biz.sql`
|
||
- 迁移脚本:`db/zqyy_app/migrations/2026-03-22__ns41_registry_tables.sql`
|
||
- 后端路由:`apps/backend/app/routers/admin_registry.py`
|
||
- 后端 Schema:`apps/backend/app/schemas/admin_registry.py`
|
||
- 管理员路由:`apps/backend/app/routers/admin_tenant_admins.py`
|
||
- 前端 API:`apps/admin-web/src/api/registry.ts`
|
||
- Spec:`.kiro/specs/admin-web-enhancement/`
|
||
- PRD:`docs/prd/Neo_Specs/NS4.1-tenant-admin-redesign.md`
|