Files
Neo-ZQYY/.kiro/specs/01-miniapp-db-foundation/design.md
Neo b25308c3f4 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 排除规则
2026-02-26 08:03:53 +08:00

19 KiB
Raw Blame History

设计文档小程序数据库基础设施层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 过滤)  │                  │   (外部表,只读)     │
└─────────────────────┘                  └─────────────────────┘

架构

分层架构

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)中创建 authbiz 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;

组件 2RLS 视图层ETL 库)

职责:在 ETL 库(PG_DSN)的 app Schema 中为每张源表创建带 site_id 过滤的视图。

视图命名规则app.v_<源表名>,例如 app.v_dim_memberapp.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_tiercfg_assistant_level_pricecfg_bonus_rulescfg_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;

组件 3FDW 跨库映射(业务库)

职责:通过 postgres_fdw 将 ETL 库 app Schema 的视图映射为业务库 fdw_etl Schema 的外部表。

实现方式:使用 IMPORT FOREIGN SCHEMA 批量导入,而非逐表定义外部表。这与现有 db/fdw/setup_fdw_test.sql 的模式一致。

环境感知:迁移脚本中的 hostdbnameportpassword 等连接参数使用占位符 '***',部署时根据环境替换。项目已有 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;

设计决策

  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

接口

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_DSNETL 库连接字符串(从中解析 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 anyauthbiz 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 anysite_id 列的 RLS 视图和任意有效的 site_id 值,设置 app.current_site_id 后查询该视图,返回结果中所有行的 site_id 都应等于设置的值。

Validates: Requirements 2.5

Property 5未设置 site_id 时 RLS 视图拒绝访问

For anysite_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_DSNAPP_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 视图数据一致性