feat: P1-P3 全栈集成 — 数据库基础 + DWS 扩展 + 小程序鉴权 + 工程化体系

## P1 数据库基础
- zqyy_app: 创建 auth/biz schema、FDW 连接 etl_feiqiu
- etl_feiqiu: 创建 app schema RLS 视图、商品库存预警表
- 清理 assistant_abolish 残留数据

## P2 ETL/DWS 扩展
- 新增 DWS 助教订单贡献度表 (dws.assistant_order_contribution)
- 新增 assistant_order_contribution_task 任务及 RLS 视图
- member_consumption 增加充值字段、assistant_daily 增加处罚字段
- 更新 ODS/DWD/DWS 任务文档及业务规则文档
- 更新 consistency_checker、flow_runner、task_registry 等核心模块

## P3 小程序鉴权系统
- 新增 xcx_auth 路由/schema(微信登录 + JWT)
- 新增 wechat/role/matching/application 服务层
- zqyy_app 鉴权表迁移 + 角色权限种子数据
- auth/dependencies.py 支持小程序 JWT 鉴权

## 文档与审计
- 新增 DOCUMENTATION-MAP 文档导航
- 新增 7 份 BD_Manual 数据库变更文档
- 更新 DDL 基线快照(etl_feiqiu 6 schema + zqyy_app auth)
- 新增全栈集成审计记录、部署检查清单更新
- 新增 BACKLOG 路线图、FDW→Core 迁移计划

## Kiro 工程化
- 新增 5 个 Spec(P1/P2/P3/全栈集成/核心业务)
- 新增审计自动化脚本(agent_on_stop/build_audit_context/compliance_prescan)
- 新增 6 个 Hook(合规检查/会话日志/提交审计等)
- 新增 doc-map steering 文件

## 运维与测试
- 新增 ops 脚本:迁移验证/API 健康检查/ETL 监控/集成报告
- 新增属性测试:test_dws_contribution / test_auth_system
- 清理过期 export 报告文件
- 更新 .gitignore 排除规则
This commit is contained in:
Neo
2026-02-26 08:03:53 +08:00
parent fafc95e64c
commit b25308c3f4
224 changed files with 17660 additions and 32198 deletions

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,431 @@
# 设计文档小程序数据库基础设施层miniapp-db-foundation
## 概述
本设计实现 P1 基础设施层的三大核心能力:
1. **业务库 Schema 划分**:在 `test_zqyy_app` 中创建 `auth`(认证)和 `biz`(业务)两个 Schema配合权限管理
2. **ETL 库 RLS 视图层**:在 `test_etl_feiqiu.app` Schema 中为 35 张 DWD/DWS 表创建行级安全视图,通过 `site_id` 隔离多门店数据
3. **FDW 跨库映射**:通过 `postgres_fdw` 将 ETL 库的 RLS 视图映射为业务库的只读外部表
**环境变量驱动**:所有数据库名称通过 `.env` 环境变量引用,不硬编码。迁移脚本中使用占位符,验证脚本从 `PG_DSN` / `APP_DB_DSN` 解析连接信息。
| 环境变量 | 用途 | 示例值 |
|---------|------|--------|
| `PG_DSN` | ETL 库连接字符串 | `postgresql://user:pass@host:5432/test_etl_feiqiu` |
| `APP_DB_DSN` | 业务库连接字符串 | `postgresql://user:pass@host:5432/test_zqyy_app` |
整体数据流向:
```
ETL 库PG_DSN 业务库APP_DB_DSN
┌─────────────────────┐ ┌─────────────────────┐
│ dwd.dim_member │ │ auth (用户认证) │
│ dwd.dim_assistant │ │ biz (业务数据) │
│ dws.dws_* │ │ public (系统管理) │
│ dws.cfg_* │ │ │
│ │ │ │ fdw_etl │
│ ▼ │ postgres_fdw │ ├ v_dim_member │
│ app.v_dim_member │ ◄──────────────► │ ├ v_dim_assistant │
│ app.v_dws_* │ IMPORT SCHEMA │ └ v_dws_* │
│ (RLS: site_id 过滤) │ │ (外部表,只读) │
└─────────────────────┘ └─────────────────────┘
```
## 架构
### 分层架构
```mermaid
graph TB
subgraph "业务库APP_DB_DSN"
AUTH["auth Schema<br/>用户认证、权限、映射"]
BIZ["biz Schema<br/>业务数据"]
PUBLIC["public Schema<br/>系统管理表(保留)"]
FDW["fdw_etl Schema<br/>FDW 外部表(只读)"]
end
subgraph "ETL 库PG_DSN"
APP["app Schema<br/>RLS 视图层"]
DWD["dwd Schema<br/>明细层11 张表)"]
DWS["dws Schema<br/>汇总层24 张表)"]
end
FDW -->|"postgres_fdw<br/>IMPORT FOREIGN SCHEMA"| APP
APP -->|"WHERE site_id = current_setting(...)"| DWD
APP -->|"WHERE site_id = current_setting(...)"| DWS
```
### 执行顺序
迁移脚本必须按以下顺序执行:
1. **ETL 库**(通过 `PG_DSN` 连接):创建 `app` Schema → 创建 `app_reader` 角色 → 创建 RLS 视图 → 授权
2. **业务库**(通过 `APP_DB_DSN` 连接):创建 `auth`/`biz` Schema → 安装 `postgres_fdw` → 创建外部服务器 → 用户映射 → 导入外部表 → 授权
## 组件与接口
### 组件 1Schema 管理(业务库)
**职责**:在业务库(`APP_DB_DSN`)中创建 `auth``biz` Schema配置权限。
**SQL 接口**
```sql
-- 创建 Schema
CREATE SCHEMA IF NOT EXISTS auth;
CREATE SCHEMA IF NOT EXISTS biz;
-- 授权 app_user
GRANT USAGE ON SCHEMA auth TO app_user;
GRANT USAGE ON SCHEMA biz TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA auth TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA biz TO app_user;
-- 未来新表自动授权
ALTER DEFAULT PRIVILEGES IN SCHEMA auth
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA biz
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
```
### 组件 2RLS 视图层ETL 库)
**职责**:在 ETL 库(`PG_DSN`)的 `app` Schema 中为每张源表创建带 `site_id` 过滤的视图。
**视图命名规则**`app.v_<源表名>`,例如 `app.v_dim_member``app.v_dws_member_consumption_summary`
**视图模板**
```sql
CREATE OR REPLACE VIEW app.v_<> AS
SELECT * FROM <schema>.<>
WHERE site_id = current_setting('app.current_site_id')::bigint;
```
**DWD 层视图清单11 张)**
| 视图名 | 源表 |
|--------|------|
| `app.v_dim_member` | `dwd.dim_member` |
| `app.v_dim_assistant` | `dwd.dim_assistant` |
| `app.v_dim_member_card_account` | `dwd.dim_member_card_account` |
| `app.v_dim_table` | `dwd.dim_table` |
| `app.v_dwd_settlement_head` | `dwd.dwd_settlement_head` |
| `app.v_dwd_table_fee_log` | `dwd.dwd_table_fee_log` |
| `app.v_dwd_assistant_service_log` | `dwd.dwd_assistant_service_log` |
| `app.v_dwd_recharge_order` | `dwd.dwd_recharge_order` |
| `app.v_dwd_store_goods_sale` | `dwd.dwd_store_goods_sale` |
| `app.v_dim_staff` | `dwd.dim_staff` |
| `app.v_dim_staff_ex` | `dwd.dim_staff_ex` |
**DWS 层视图清单24 张)**
| 视图名 | 源表 |
|--------|------|
| `app.v_dws_member_consumption_summary` | `dws.dws_member_consumption_summary` |
| `app.v_dws_member_visit_detail` | `dws.dws_member_visit_detail` |
| `app.v_dws_member_winback_index` | `dws.dws_member_winback_index` |
| `app.v_dws_member_newconv_index` | `dws.dws_member_newconv_index` |
| `app.v_dws_member_recall_index` | `dws.dws_member_recall_index` |
| `app.v_dws_member_assistant_relation_index` | `dws.dws_member_assistant_relation_index` |
| `app.v_dws_member_assistant_intimacy` | `dws.dws_member_assistant_intimacy` |
| `app.v_dws_assistant_daily_detail` | `dws.dws_assistant_daily_detail` |
| `app.v_dws_assistant_monthly_summary` | `dws.dws_assistant_monthly_summary` |
| `app.v_dws_assistant_salary_calc` | `dws.dws_assistant_salary_calc` |
| `app.v_dws_assistant_customer_stats` | `dws.dws_assistant_customer_stats` |
| `app.v_dws_assistant_finance_analysis` | `dws.dws_assistant_finance_analysis` |
| `app.v_dws_finance_daily_summary` | `dws.dws_finance_daily_summary` |
| `app.v_dws_finance_income_structure` | `dws.dws_finance_income_structure` |
| `app.v_dws_finance_recharge_summary` | `dws.dws_finance_recharge_summary` |
| `app.v_dws_finance_discount_detail` | `dws.dws_finance_discount_detail` |
| `app.v_dws_finance_expense_summary` | `dws.dws_finance_expense_summary` |
| `app.v_dws_platform_settlement` | `dws.dws_platform_settlement` |
| `app.v_dws_assistant_recharge_commission` | `dws.dws_assistant_recharge_commission` |
| `app.v_cfg_performance_tier` | `dws.cfg_performance_tier` |
| `app.v_cfg_assistant_level_price` | `dws.cfg_assistant_level_price` |
| `app.v_cfg_bonus_rules` | `dws.cfg_bonus_rules` |
| `app.v_cfg_index_parameters` | `dws.cfg_index_parameters` |
| `app.v_dws_order_summary` | `dws.dws_order_summary` |
**P2 预留(注释标记,暂不创建)**
- `dws.dws_member_spending_power_index` → 待 P2 完成后补充
- `dws.dws_assistant_order_contribution` → 待 P2 完成后补充
**`cfg_*` 表特殊处理**:配置表(`cfg_performance_tier``cfg_assistant_level_price``cfg_bonus_rules``cfg_index_parameters`)可能不含 `site_id` 列。对于不含 `site_id` 的配置表,视图直接 `SELECT *` 不加过滤条件。
**权限配置**
```sql
-- 创建只读角色(如不存在)
DO $$ BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'app_reader') THEN
CREATE ROLE app_reader LOGIN;
END IF;
END $$;
GRANT USAGE ON SCHEMA app TO app_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT ON TABLES TO app_reader;
```
### 组件 3FDW 跨库映射(业务库)
**职责**:通过 `postgres_fdw` 将 ETL 库 `app` Schema 的视图映射为业务库 `fdw_etl` Schema 的外部表。
**实现方式**:使用 `IMPORT FOREIGN SCHEMA` 批量导入,而非逐表定义外部表。这与现有 `db/fdw/setup_fdw_test.sql` 的模式一致。
**环境感知**:迁移脚本中的 `host``dbname``port``password` 等连接参数使用占位符 `'***'`,部署时根据环境替换。项目已有 `db/fdw/setup_fdw_test.sql`(测试环境)和 `db/fdw/setup_fdw.sql`(生产环境)的分环境模式,本次迁移脚本遵循相同模式——提供测试环境和生产环境两个版本。
```sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
-- 创建外部服务器
-- host / dbname / port 按环境替换,从 PG_DSN 解析 ETL 库连接信息
CREATE SERVER IF NOT EXISTS etl_feiqiu_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host '***', dbname '***', port '***');
-- 用户映射(密码按环境替换)
CREATE USER MAPPING IF NOT EXISTS FOR app_user
SERVER etl_feiqiu_server
OPTIONS (user 'app_reader', password '***');
-- 创建目标 Schema
CREATE SCHEMA IF NOT EXISTS fdw_etl;
-- 批量导入
IMPORT FOREIGN SCHEMA app
FROM SERVER etl_feiqiu_server
INTO fdw_etl;
-- 授权
GRANT USAGE ON SCHEMA fdw_etl TO app_user;
GRANT SELECT ON ALL TABLES IN SCHEMA fdw_etl TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA fdw_etl GRANT SELECT ON TABLES TO app_user;
```
**设计决策**
1. 使用 `IMPORT FOREIGN SCHEMA` 而非逐表 `CREATE FOREIGN TABLE`——自动匹配列定义,避免手动维护列类型不一致的风险
2. 新增 RLS 视图后只需重新执行 `IMPORT` 即可同步
3. 与现有 `db/fdw/setup_fdw_test.sql` 保持一致
4. 服务器名使用通用名 `etl_feiqiu_server`(不含环境前缀),通过连接参数区分环境
### 组件 4验证脚本
**职责**:自动化检查所有数据库对象是否正确创建。
**文件位置**`scripts/ops/validate_p1_db_foundation.py`
**接口**
```python
def validate_p1_db_foundation() -> dict:
"""
返回验证结果字典:
{
"schemas": {"auth": bool, "biz": bool, "app": bool, "fdw_etl": bool},
"rls_views": {"app.v_dim_member": bool, ...},
"fdw_tables": {"fdw_etl.v_dim_member": bool, ...},
"rls_filtering": bool,
"permissions": {"app_user": bool, "app_reader": bool},
"errors": [str, ...]
}
"""
```
**环境变量依赖**(强制从 `.env` 加载,缺失时 `RuntimeError` 终止):
- `PG_DSN`ETL 库连接字符串(从中解析 host、port、dbname
- `APP_DB_DSN`:业务库连接字符串(从中解析 host、port、dbname
- 脚本通过 `load_dotenv()` 加载根 `.env`,禁止硬编码任何数据库名称或连接参数
## 数据模型
### Schema 拓扑
```mermaid
erDiagram
test_zqyy_app_auth {
string schema_name "auth"
string purpose "用户认证、权限、映射"
}
test_zqyy_app_biz {
string schema_name "biz"
string purpose "业务数据任务、备注、AI、Excel"
}
test_zqyy_app_fdw_etl {
string schema_name "fdw_etl"
string purpose "FDW 外部表(只读)"
}
test_zqyy_app_public {
string schema_name "public"
string purpose "系统管理表(保留)"
}
test_etl_feiqiu_app {
string schema_name "app"
string purpose "RLS 视图层"
}
test_etl_feiqiu_dwd {
string schema_name "dwd"
string purpose "明细层11 张表)"
}
test_etl_feiqiu_dws {
string schema_name "dws"
string purpose "汇总层24 张表)"
}
test_etl_feiqiu_app ||--o{ test_etl_feiqiu_dwd : "视图引用"
test_etl_feiqiu_app ||--o{ test_etl_feiqiu_dws : "视图引用"
test_zqyy_app_fdw_etl ||--|| test_etl_feiqiu_app : "postgres_fdw 映射"
```
### RLS 视图数据流
对于含 `site_id` 的表:
```
源表数据(全量)→ RLS 视图site_id 过滤)→ FDW 外部表(只读访问)
```
对于不含 `site_id` 的配置表(`cfg_*`
```
源表数据(全量)→ 直通视图(无过滤)→ FDW 外部表(只读访问)
```
### 迁移脚本清单
| 序号 | 目标库 | 文件名 | 内容 |
|------|--------|--------|------|
| 1 | ETL 库(`PG_DSN` | `YYYY-MM-DD__p1_create_app_schema_rls_views.sql` | 创建 app Schema + 全部 RLS 视图 + app_reader 权限 |
| 2 | 业务库(`APP_DB_DSN` | `YYYY-MM-DD__p1_create_auth_biz_schemas.sql` | 创建 auth/biz Schema + app_user 权限 |
| 3 | 业务库(`APP_DB_DSN` | `YYYY-MM-DD__p1_setup_fdw_etl.sql` | FDW 扩展 + 外部服务器 + 用户映射 + 导入外部表 |
## 正确性属性Correctness Properties
*属性是系统在所有有效执行中都应保持为真的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性是人类可读规格与机器可验证正确性保证之间的桥梁。*
### Property 1默认权限自动授予
*For any*`auth``biz` Schema 中新创建的表,`app_user` 角色都应自动获得 SELECT、INSERT、UPDATE、DELETE 权限,无需额外手动授权。
**Validates: Requirements 1.5**
### Property 2public Schema 不变量
*For any* 迁移脚本执行前后,`test_zqyy_app.public` Schema 中的表集合应保持不变——迁移不应删除、重命名或修改 `public` 中的现有表。
**Validates: Requirements 1.6**
### Property 3RLS 视图定义包含 site_id 过滤
*For any* `test_etl_feiqiu.app` Schema 中的 RLS 视图(含 `site_id` 列的源表对应的视图),其视图定义 SQL 中都应包含 `current_setting('app.current_site_id')` 过滤条件。
**Validates: Requirements 2.4**
### Property 4RLS 过滤正确性
*For any*`site_id` 列的 RLS 视图和任意有效的 `site_id` 值,设置 `app.current_site_id` 后查询该视图,返回结果中所有行的 `site_id` 都应等于设置的值。
**Validates: Requirements 2.5**
### Property 5未设置 site_id 时 RLS 视图拒绝访问
*For any*`site_id` 过滤的 RLS 视图,在未设置 `app.current_site_id` 的会话中执行查询,应抛出错误而非返回数据。
**Validates: Requirements 2.6**
### Property 6FDW 外部表完整性与数据一致性
*For any* `test_etl_feiqiu.app` Schema 中的视图,`test_zqyy_app.fdw_etl` Schema 中都应存在对应的可查询外部表,且在相同 `site_id` 条件下,外部表返回的数据与 RLS 视图返回的数据一致。
**Validates: Requirements 3.4, 3.5, 3.6**
### Property 7迁移脚本结构合规性
*For any* `db/etl_feiqiu/migrations/``db/zqyy_app/migrations/` 中本次新增的迁移脚本文件,文件名应匹配 `YYYY-MM-DD__*.sql` 模式,且文件内容中应包含回滚语句(以注释形式)。
**Validates: Requirements 4.3, 4.4**
### Property 8迁移脚本幂等性
*For any* 本次新增的迁移脚本,连续执行两次的结果应与执行一次相同——第二次执行不应产生错误。
**Validates: Requirements 4.5**
### Property 9环境变量缺失时验证脚本报错
*For any* 必需环境变量(`PG_DSN``APP_DB_DSN`)的缺失组合,验证脚本应立即抛出错误终止,而非静默使用默认值或空字符串。
**Validates: Requirements 5.8**
## 错误处理
### 迁移脚本错误处理
| 场景 | 处理方式 |
|------|---------|
| Schema 已存在 | `CREATE SCHEMA IF NOT EXISTS` 幂等跳过 |
| 视图已存在 | `CREATE OR REPLACE VIEW` 覆盖更新 |
| 角色不存在 | `DO $$ ... IF NOT EXISTS ... END $$` 条件创建 |
| 源表不存在P2 待建表) | 以注释形式预留,不创建视图 |
| FDW 服务器已存在 | `CREATE SERVER IF NOT EXISTS` 幂等跳过 |
| 用户映射已存在 | `CREATE USER MAPPING IF NOT EXISTS` 幂等跳过 |
| `IMPORT FOREIGN SCHEMA` 表已存在 | 先 `DROP SCHEMA fdw_etl CASCADE` 再重新导入(脚本中提供选项) |
### 验证脚本错误处理
| 场景 | 处理方式 |
|------|---------|
| 环境变量缺失 | `RuntimeError` 立即终止,输出缺失变量名 |
| 数据库连接失败 | 捕获 `psycopg2.OperationalError`,输出连接参数(脱敏)和错误信息 |
| Schema/视图/外部表不存在 | 记录为失败项,继续检查其余项目 |
| RLS 过滤验证无数据 | 标记为 SKIP无法验证不标记为失败 |
| 权限查询失败 | 记录具体错误,继续检查 |
### `current_setting` 未设置时的行为
PostgreSQL 中 `current_setting('app.current_site_id')` 在未设置时会抛出 `ERROR: unrecognized configuration parameter "app.current_site_id"`。这是期望行为(需求 2.6),确保不会意外返回全量数据。
如果需要更友好的错误信息,可以使用 `current_setting('app.current_site_id', true)` 返回 NULL然后在视图中用 `CASE` 处理。但当前设计选择让 PostgreSQL 原生报错,因为:
1. 更安全——不可能绕过
2. 后端代码必须显式设置 `SET app.current_site_id = ...`,这是一个强制约束
## 测试策略
### 属性测试Property-Based Testing
使用 Python `hypothesis` 框架,测试目录:`tests/`Monorepo 级属性测试目录)。
每个属性测试至少运行 100 次迭代。每个测试用注释标注对应的设计属性编号。
标注格式:`# Feature: miniapp-db-foundation, Property N: <属性标题>`
**属性测试清单**
| 属性 | 测试方法 | 生成器 |
|------|---------|--------|
| P1 默认权限 | 生成随机表名,在 auth/biz 中创建表,验证 app_user 权限 | `hypothesis.strategies.text` 生成合法 SQL 标识符 |
| P3 视图定义过滤 | 遍历所有 app schema 视图,检查定义 SQL | 无需生成器,遍历所有视图 |
| P4 RLS 过滤正确性 | 生成随机 site_id设置后查询视图验证结果 | `hypothesis.strategies.integers` 生成 site_id |
| P5 未设置 site_id 报错 | 遍历所有 RLS 视图,在新会话中查询 | 无需生成器,遍历所有视图 |
| P7 脚本结构合规 | 遍历所有新增迁移脚本,验证命名和内容 | 无需生成器,遍历文件 |
| P8 幂等性 | 对每个迁移脚本执行两次,验证无错误 | 无需生成器 |
| P9 环境变量缺失 | 生成环境变量缺失组合,验证报错 | `hypothesis.strategies.sampled_from` 生成缺失组合 |
**注意**P2public schema 不变量)和 P6FDW 数据一致性)需要真实数据库环境,作为集成测试在验证脚本中实现,而非 hypothesis 属性测试。
### 单元测试
单元测试聚焦于验证脚本的逻辑正确性:
- 验证脚本在 Schema 缺失时正确报告失败
- 验证脚本在权限不足时正确报告
- 验证脚本的输出格式正确JSON 结构)
- 环境变量缺失时的错误消息包含变量名
### 集成测试
集成测试通过验证脚本 `scripts/ops/validate_p1_db_foundation.py` 实现,覆盖:
- 全部 Schema 存在性检查
- 全部 RLS 视图存在性和过滤正确性
- 全部 FDW 外部表存在性和可查询性
- 权限配置完整性
- FDW 数据与 RLS 视图数据一致性

View File

@@ -0,0 +1,88 @@
# 需求文档小程序数据库基础设施层miniapp-db-foundation
## 简介
P1 基础设施层是整个小程序系统的第一个 SPEC无前置依赖是所有后续 SPEC 的硬依赖。本 SPEC 负责在业务库 `test_zqyy_app` 中建立清晰的 Schema 划分(`auth` + `biz`),在 ETL 库 `test_etl_feiqiu` 中为数据依赖矩阵列出的所有 DWD/DWS 表创建 RLS 视图(按 `site_id` 隔离),并通过 `postgres_fdw` 将 RLS 视图映射为业务库的外部表,使后端无需直连 ETL 库即可读取汇总/维度数据。
## 术语表
- **Schema_Manager**:负责在 PostgreSQL 数据库中创建和管理 Schema、权限配置的迁移脚本系统
- **RLS_View_Layer**:在 `test_etl_feiqiu.app` Schema 中创建的一组视图,通过 `current_setting('app.current_site_id')``site_id` 过滤数据,实现行级安全隔离
- **FDW_Bridge**:通过 `postgres_fdw` 扩展在 `test_zqyy_app.fdw_etl` Schema 中创建的外部表集合,只读映射 ETL 库 `app` Schema 的 RLS 视图
- **Migration_Script**:存放在 `db/zqyy_app/migrations/``db/etl_feiqiu/migrations/` 中的纯 SQL 迁移脚本,以日期前缀命名
- **Validation_Script**:用于验证数据库对象是否正确创建、权限是否配置正确、数据是否可查询的 Python 脚本
- **site_id**:门店标识符,类型为 `BIGINT`,用于多门店数据隔离
- **app_reader**ETL 库侧的只读角色,供 FDW 用户映射使用
- **app_user**:业务库侧的应用连接角色,通过 FDW 读取 ETL 数据
## 需求
### 需求 1业务库 Schema 划分与权限配置
**用户故事:** 作为后端开发者,我需要 `test_zqyy_app` 中有清晰的 Schema 划分(`auth` + `biz`),以便按功能组织业务表。
#### 验收标准
1. WHEN Migration_Script 执行完成, THE Schema_Manager SHALL 在 `test_zqyy_app` 中创建 `auth` Schema
2. WHEN Migration_Script 执行完成, THE Schema_Manager SHALL 在 `test_zqyy_app` 中创建 `biz` Schema
3. WHEN `auth` Schema 创建完成, THE Schema_Manager SHALL 授予 `app_user` 角色对 `auth` Schema 的 USAGE 权限和对其中所有表的 SELECT、INSERT、UPDATE、DELETE 权限
4. WHEN `biz` Schema 创建完成, THE Schema_Manager SHALL 授予 `app_user` 角色对 `biz` Schema 的 USAGE 权限和对其中所有表的 SELECT、INSERT、UPDATE、DELETE 权限
5. WHEN 新表在 `auth``biz` Schema 中创建, THE Schema_Manager SHALL 通过 ALTER DEFAULT PRIVILEGES 自动授予 `app_user` 角色相应权限
6. THE Migration_Script SHALL 保留 `public` Schema 中现有的系统管理表(`admin_users``roles``permissions` 等)不受影响
### 需求 2ETL 库 RLS 视图层创建
**用户故事:** 作为系统管理员,我需要 RLS 视图按 `site_id` 隔离数据,以便多门店数据安全。
#### 验收标准
1. WHEN Migration_Script 执行完成, THE Schema_Manager SHALL 在 `test_etl_feiqiu` 中创建 `app` Schema如不存在
2. WHEN `app` Schema 创建完成, THE RLS_View_Layer SHALL 为数据依赖矩阵中列出的每张 DWD 表创建对应的 RLS 视图(共 11 张:`dim_member``dim_assistant``dim_member_card_account``dim_table``dwd_settlement_head``dwd_table_fee_log``dwd_assistant_service_log``dwd_recharge_order``dwd_store_goods_sale``dim_staff``dim_staff_ex`
3. WHEN `app` Schema 创建完成, THE RLS_View_Layer SHALL 为数据依赖矩阵中列出的每张 DWS 表创建对应的 RLS 视图(共 24 张,包含 `dws_*``cfg_*` 表)
4. THE RLS_View_Layer 中每个视图 SHALL 包含 `WHERE site_id = current_setting('app.current_site_id')::bigint` 过滤条件
5. WHEN 设置 `app.current_site_id` 为某门店 ID 后查询 RLS 视图, THE RLS_View_Layer SHALL 仅返回该门店的数据
6. WHEN 未设置 `app.current_site_id` 时查询 RLS 视图, THE RLS_View_Layer SHALL 抛出错误而非返回全部数据
7. THE Schema_Manager SHALL 授予 `app_reader` 角色对 `app` Schema 的 USAGE 权限和对其中所有视图的 SELECT 权限
8. THE RLS_View_Layer SHALL 为 P2 待建表(`dws_member_spending_power_index``dws_assistant_order_contribution`)在迁移脚本中以注释形式预留位置
### 需求 3FDW 外部表映射
**用户故事:** 作为后端开发者,我需要通过 FDW 从 `test_zqyy_app` 读取 ETL 库的 DWS/DWD 数据,以便小程序页面展示 ETL 计算结果。
#### 验收标准
1. WHEN Migration_Script 执行完成, THE FDW_Bridge SHALL 在 `test_zqyy_app` 中安装 `postgres_fdw` 扩展
2. WHEN `postgres_fdw` 扩展安装完成, THE FDW_Bridge SHALL 创建指向 `test_etl_feiqiu` 的外部服务器 `test_etl_feiqiu_server`
3. WHEN 外部服务器创建完成, THE FDW_Bridge SHALL 创建 `app_user``app_reader` 的用户映射
4. WHEN 用户映射创建完成, THE FDW_Bridge SHALL 在 `fdw_etl` Schema 中通过 `IMPORT FOREIGN SCHEMA app` 导入所有外部表
5. WHEN 外部表导入完成, THE FDW_Bridge SHALL 对 `fdw_etl` Schema 中的每张外部表执行 `SELECT` 查询验证可读性
6. WHEN 外部表查询成功, THE FDW_Bridge 返回的数据 SHALL 与 ETL 库 `app` Schema 中对应 RLS 视图的数据一致
7. THE FDW_Bridge SHALL 授予 `app_user` 角色对 `fdw_etl` Schema 的 USAGE 权限和对其中所有外部表的 SELECT 权限
### 需求 4迁移脚本管理
**用户故事:** 作为后端开发者,我需要所有数据库变更都有对应的迁移脚本,以便变更可追溯、可重放、可回滚。
#### 验收标准
1. THE Migration_Script SHALL 将 ETL 库变更RLS 视图创建、`app` Schema 权限)存放在 `db/etl_feiqiu/migrations/` 目录中
2. THE Migration_Script SHALL 将业务库变更Schema 创建、FDW 配置)存放在 `db/zqyy_app/migrations/` 目录中
3. THE Migration_Script SHALL 使用日期前缀命名(格式:`YYYY-MM-DD__<描述>.sql`
4. THE Migration_Script SHALL 在每个脚本中包含回滚语句(以注释形式)
5. THE Migration_Script SHALL 使用 `IF NOT EXISTS` / `OR REPLACE` 等幂等语法,确保重复执行不会报错
6. THE Migration_Script SHALL 使用 UTF-8 编码,纯 SQL非 ORM
### 需求 5端到端验证
**用户故事:** 作为后端开发者,我需要一个自动化验证脚本,确认所有数据库对象正确创建且数据可访问。
#### 验收标准
1. WHEN Validation_Script 执行时, THE Validation_Script SHALL 检查 `test_zqyy_app``auth``biz` Schema 是否存在
2. WHEN Validation_Script 执行时, THE Validation_Script SHALL 检查 `test_etl_feiqiu.app` Schema 中所有 RLS 视图是否存在
3. WHEN Validation_Script 执行时, THE Validation_Script SHALL 检查 `test_zqyy_app.fdw_etl` 中所有外部表是否存在
4. WHEN Validation_Script 执行时, THE Validation_Script SHALL 对每张外部表执行 `SELECT count(*)` 验证可查询性
5. WHEN Validation_Script 执行时, THE Validation_Script SHALL 设置 `app.current_site_id` 后验证 RLS 视图正确过滤数据
6. WHEN Validation_Script 执行时, THE Validation_Script SHALL 验证 `app_user``app_reader` 角色的权限配置正确
7. WHEN 验证发现异常, THE Validation_Script SHALL 输出具体的失败项和错误信息
8. THE Validation_Script SHALL 从 `.env` 加载数据库连接参数(`PG_DSN``APP_DB_DSN`),缺失时立即报错终止

View File

@@ -0,0 +1,108 @@
# 实现计划小程序数据库基础设施层miniapp-db-foundation
## 概述
按照"ETL 库先行 → 业务库跟进 → FDW 桥接 → 验证收尾"的顺序,将设计拆分为可增量执行的编码任务。每个迁移脚本使用幂等语法,所有连接参数通过环境变量驱动。
## 任务
- [x] 1. ETL 库:创建 app Schema 与 RLS 视图
- [x] 1.1 编写迁移脚本 `db/etl_feiqiu/migrations/YYYY-MM-DD__p1_create_app_schema_rls_views.sql`
- 创建 `app` Schema`CREATE SCHEMA IF NOT EXISTS app`
- 创建 `app_reader` 角色(条件创建,`DO $$ ... IF NOT EXISTS ... END $$`
- 为 11 张 DWD 表创建 RLS 视图(`CREATE OR REPLACE VIEW app.v_<表名> AS SELECT * FROM dwd.<表名> WHERE site_id = current_setting('app.current_site_id')::bigint`
- 为 24 张 DWS 表创建 RLS 视图(同上模式;`cfg_*` 配置表若无 `site_id` 列则直接 `SELECT *` 不加过滤)
- 在脚本末尾以注释形式预留 P2 待建表位置(`dws_member_spending_power_index``dws_assistant_order_contribution`
- 授予 `app_reader``app` Schema 的 USAGE + SELECT 权限 + ALTER DEFAULT PRIVILEGES
- 包含回滚语句(注释形式)
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.7, 2.8, 4.1, 4.3, 4.4, 4.5, 4.6_
- [x] 1.2 编写属性测试RLS 视图定义包含 site_id 过滤
- **Property 3: RLS 视图定义包含 site_id 过滤**
- 遍历 `app` Schema 所有视图,查询 `pg_views.definition`,验证含 `site_id` 列的源表对应视图包含 `current_setting` 过滤条件
- **Validates: Requirements 2.4**
- [x] 1.3 编写属性测试:未设置 site_id 时 RLS 视图拒绝访问
- **Property 5: 未设置 site_id 时 RLS 视图拒绝访问**
- 遍历所有含 `site_id` 过滤的 RLS 视图,在新会话(未设置 `app.current_site_id`)中执行 `SELECT`,验证抛出错误
- **Validates: Requirements 2.6**
- [x] 2. 业务库:创建 auth/biz Schema 与权限
- [x] 2.1 编写迁移脚本 `db/zqyy_app/migrations/YYYY-MM-DD__p1_create_auth_biz_schemas.sql`
- 创建 `auth` Schema`CREATE SCHEMA IF NOT EXISTS auth`
- 创建 `biz` Schema`CREATE SCHEMA IF NOT EXISTS biz`
- 授予 `app_user``auth`/`biz` 的 USAGE + CRUD 权限
- 设置 ALTER DEFAULT PRIVILEGES 自动授权未来新表
- 包含回滚语句(注释形式)
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 4.2, 4.3, 4.4, 4.5, 4.6_
- [x] 2.2 编写属性测试:默认权限自动授予
- **Property 1: 默认权限自动授予**
- 使用 hypothesis 生成随机合法表名,在 `auth`/`biz` 中创建临时表,验证 `app_user` 自动获得 SELECT/INSERT/UPDATE/DELETE 权限,测试后清理
- **Validates: Requirements 1.5**
- [x] 3. 业务库:配置 FDW 跨库映射
- [x] 3.1 编写迁移脚本 `db/zqyy_app/migrations/YYYY-MM-DD__p1_setup_fdw_etl.sql`
- 安装 `postgres_fdw` 扩展
- 创建外部服务器 `etl_feiqiu_server`host/dbname/port 使用占位符 `'***'`,按环境替换)
- 创建 `app_user``app_reader` 用户映射
- 创建 `fdw_etl` Schema
- 执行 `IMPORT FOREIGN SCHEMA app FROM SERVER etl_feiqiu_server INTO fdw_etl`
- 授予 `app_user``fdw_etl` 的 USAGE + SELECT 权限 + ALTER DEFAULT PRIVILEGES
- 包含回滚语句(注释形式)
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.7, 4.2, 4.3, 4.4, 4.5, 4.6_
- [x] 3.2 编写属性测试FDW 外部表完整性与数据一致性
- **Property 6: FDW 外部表完整性与数据一致性**
- 遍历 ETL 库 `app` Schema 所有视图,验证 `fdw_etl` 中存在对应外部表且可查询;在相同 `site_id` 下对比数据一致性
- **Validates: Requirements 3.4, 3.5, 3.6**
- [x] 4. Checkpoint — 迁移脚本验证
- 确保三个迁移脚本均可在测试环境中成功执行按顺序ETL 库 → 业务库 Schema → 业务库 FDW
- 验证重复执行不报错(幂等性)
- 如有问题请告知用户
- [x] 5. 编写端到端验证脚本
- [x] 5.1 创建 `scripts/ops/validate_p1_db_foundation.py`
- 通过 `load_dotenv()` 加载根 `.env`,读取 `PG_DSN``APP_DB_DSN`,缺失时 `RuntimeError` 终止
- 检查业务库中 `auth``biz` Schema 存在性
- 检查 ETL 库中 `app` Schema 及所有 RLS 视图存在性(对照设计文档中的 35 张视图清单)
- 检查业务库中 `fdw_etl` Schema 及所有外部表存在性
- 对每张外部表执行 `SELECT count(*)` 验证可查询性
- 设置 `app.current_site_id` 后验证 RLS 视图过滤正确性
- 验证 `app_user``app_reader` 角色权限配置
- 输出结构化验证结果(通过/失败/跳过),失败项附带错误信息
- _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8_
- [x] 5.2 编写属性测试RLS 过滤正确性
- **Property 4: RLS 过滤正确性**
- 使用 hypothesis 生成随机 `site_id`,设置 `app.current_site_id` 后查询含 `site_id` 的 RLS 视图,验证返回结果中所有行的 `site_id` 等于设置值
- **Validates: Requirements 2.5**
- [x] 5.3 编写属性测试:环境变量缺失报错
- **Property 9: 环境变量缺失时验证脚本报错**
- 使用 hypothesis 生成 `PG_DSN`/`APP_DB_DSN` 的缺失组合,验证脚本抛出 `RuntimeError`
- **Validates: Requirements 5.8**
- [x] 5.4 编写属性测试:迁移脚本结构合规性
- **Property 7: 迁移脚本结构合规性**
- 遍历本次新增的迁移脚本文件,验证文件名匹配 `YYYY-MM-DD__*.sql` 模式,且内容包含回滚语句注释
- **Validates: Requirements 4.3, 4.4**
- [x] 5.5 编写属性测试:迁移脚本幂等性
- **Property 8: 迁移脚本幂等性**
- 对每个迁移脚本连续执行两次,验证第二次执行无错误
- **Validates: Requirements 4.5**
- [x] 6. Final checkpoint — 全量验证
- 运行验证脚本 `python scripts/ops/validate_p1_db_foundation.py`,确认所有检查项通过
- 运行属性测试 `cd C:\NeoZQYY && pytest tests/ -v -k p1`,确认所有属性测试通过
- 如有问题请告知用户
## 说明
- 标记 `*` 的子任务为可选,可跳过以加速 MVP
- 每个任务引用具体的需求编号,确保可追溯
- Checkpoint 确保增量验证
- 属性测试使用 Python hypothesis 框架,测试文件放在根目录 `tests/`
- 迁移脚本中的数据库连接参数host/dbname/port/password均使用占位符按环境替换