## 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 排除规则
19 KiB
设计文档:小程序数据库基础设施层(miniapp-db-foundation)
概述
本设计实现 P1 基础设施层的三大核心能力:
- 业务库 Schema 划分:在
test_zqyy_app中创建auth(认证)和biz(业务)两个 Schema,配合权限管理 - ETL 库 RLS 视图层:在
test_etl_feiqiu.appSchema 中为 35 张 DWD/DWS 表创建行级安全视图,通过site_id隔离多门店数据 - 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 过滤) │ │ (外部表,只读) │
└─────────────────────┘ └─────────────────────┘
架构
分层架构
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
执行顺序
迁移脚本必须按以下顺序执行:
- ETL 库(通过
PG_DSN连接):创建appSchema → 创建app_reader角色 → 创建 RLS 视图 → 授权 - 业务库(通过
APP_DB_DSN连接):创建auth/bizSchema → 安装postgres_fdw→ 创建外部服务器 → 用户映射 → 导入外部表 → 授权
组件与接口
组件 1:Schema 管理(业务库)
职责:在业务库(APP_DB_DSN)中创建 auth 和 biz Schema,配置权限。
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;
组件 2:RLS 视图层(ETL 库)
职责:在 ETL 库(PG_DSN)的 app Schema 中为每张源表创建带 site_id 过滤的视图。
视图命名规则:app.v_<源表名>,例如 app.v_dim_member、app.v_dws_member_consumption_summary。
视图模板:
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 * 不加过滤条件。
权限配置:
-- 创建只读角色(如不存在)
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;
组件 3:FDW 跨库映射(业务库)
职责:通过 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(生产环境)的分环境模式,本次迁移脚本遵循相同模式——提供测试环境和生产环境两个版本。
-- 安装扩展
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;
设计决策:
- 使用
IMPORT FOREIGN SCHEMA而非逐表CREATE FOREIGN TABLE——自动匹配列定义,避免手动维护列类型不一致的风险 - 新增 RLS 视图后只需重新执行
IMPORT即可同步 - 与现有
db/fdw/setup_fdw_test.sql保持一致 - 服务器名使用通用名
etl_feiqiu_server(不含环境前缀),通过连接参数区分环境
组件 4:验证脚本
职责:自动化检查所有数据库对象是否正确创建。
文件位置:scripts/ops/validate_p1_db_foundation.py
接口:
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 拓扑
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 2:public Schema 不变量
For any 迁移脚本执行前后,test_zqyy_app.public Schema 中的表集合应保持不变——迁移不应删除、重命名或修改 public 中的现有表。
Validates: Requirements 1.6
Property 3:RLS 视图定义包含 site_id 过滤
For any test_etl_feiqiu.app Schema 中的 RLS 视图(含 site_id 列的源表对应的视图),其视图定义 SQL 中都应包含 current_setting('app.current_site_id') 过滤条件。
Validates: Requirements 2.4
Property 4:RLS 过滤正确性
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 6:FDW 外部表完整性与数据一致性
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 原生报错,因为:
- 更安全——不可能绕过
- 后端代码必须显式设置
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 生成缺失组合 |
注意:P2(public schema 不变量)和 P6(FDW 数据一致性)需要真实数据库环境,作为集成测试在验证脚本中实现,而非 hypothesis 属性测试。
单元测试
单元测试聚焦于验证脚本的逻辑正确性:
- 验证脚本在 Schema 缺失时正确报告失败
- 验证脚本在权限不足时正确报告
- 验证脚本的输出格式正确(JSON 结构)
- 环境变量缺失时的错误消息包含变量名
集成测试
集成测试通过验证脚本 scripts/ops/validate_p1_db_foundation.py 实现,覆盖:
- 全部 Schema 存在性检查
- 全部 RLS 视图存在性和过滤正确性
- 全部 FDW 外部表存在性和可查询性
- 权限配置完整性
- FDW 数据与 RLS 视图数据一致性