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,280 @@
-- =============================================================================
-- 迁移脚本:创建 auth Schema 认证业务表
-- 日期2026-02-25
-- 目标库test_zqyy_app通过 APP_DB_DSN 连接)
-- 说明:在 auth Schema 下创建用户认证系统所需的 8 张业务表:
-- users、user_applications、site_code_mapping、roles、permissions、
-- role_permissions、user_site_roles、user_assistant_binding
-- 包含字段定义、约束、索引、外键。使用 IF NOT EXISTS 幂等语法。
-- 前提auth Schema 已由 P1 迁移脚本创建
-- 2026-02-24__p1_create_auth_biz_schemas.sql
-- 需求1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10
-- =============================================================================
-- 确保 auth Schema 存在(幂等)
CREATE SCHEMA IF NOT EXISTS auth;
-- ---------------------------------------------------------------------------
-- 1. users — 微信用户主表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.users (
id SERIAL PRIMARY KEY,
wx_openid VARCHAR(128) NOT NULL,
wx_union_id VARCHAR(128),
wx_avatar_url VARCHAR(512),
nickname VARCHAR(50),
phone VARCHAR(20),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- wx_openid 唯一约束(幂等:先检查再添加)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uq_users_wx_openid' AND conrelid = 'auth.users'::regclass
) THEN
ALTER TABLE auth.users ADD CONSTRAINT uq_users_wx_openid UNIQUE (wx_openid);
END IF;
END $$;
-- 索引
CREATE INDEX IF NOT EXISTS ix_users_wx_openid ON auth.users (wx_openid);
-- ---------------------------------------------------------------------------
-- 2. site_code_mapping — 球房ID与门店映射表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.site_code_mapping (
id SERIAL PRIMARY KEY,
site_code VARCHAR(10) NOT NULL,
site_id BIGINT NOT NULL,
site_name VARCHAR(100),
tenant_id INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- site_code 唯一约束格式2字母+3数字如 AB123
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uq_site_code_mapping_site_code' AND conrelid = 'auth.site_code_mapping'::regclass
) THEN
ALTER TABLE auth.site_code_mapping ADD CONSTRAINT uq_site_code_mapping_site_code UNIQUE (site_code);
END IF;
END $$;
-- site_id 唯一约束
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uq_site_code_mapping_site_id' AND conrelid = 'auth.site_code_mapping'::regclass
) THEN
ALTER TABLE auth.site_code_mapping ADD CONSTRAINT uq_site_code_mapping_site_id UNIQUE (site_id);
END IF;
END $$;
-- 索引
CREATE INDEX IF NOT EXISTS ix_site_code_mapping_site_code ON auth.site_code_mapping (site_code);
-- ---------------------------------------------------------------------------
-- 3. roles — 角色定义表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.roles (
id SERIAL PRIMARY KEY,
code VARCHAR(50) NOT NULL,
name VARCHAR(100),
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uq_roles_code' AND conrelid = 'auth.roles'::regclass
) THEN
ALTER TABLE auth.roles ADD CONSTRAINT uq_roles_code UNIQUE (code);
END IF;
END $$;
-- ---------------------------------------------------------------------------
-- 4. permissions — 权限定义表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.permissions (
id SERIAL PRIMARY KEY,
code VARCHAR(50) NOT NULL,
name VARCHAR(100),
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uq_permissions_code' AND conrelid = 'auth.permissions'::regclass
) THEN
ALTER TABLE auth.permissions ADD CONSTRAINT uq_permissions_code UNIQUE (code);
END IF;
END $$;
-- ---------------------------------------------------------------------------
-- 5. role_permissions — 角色-权限关联表(联合主键)
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id)
);
-- 外键(幂等添加)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_role_permissions_role_id' AND conrelid = 'auth.role_permissions'::regclass
) THEN
ALTER TABLE auth.role_permissions
ADD CONSTRAINT fk_role_permissions_role_id
FOREIGN KEY (role_id) REFERENCES auth.roles (id) ON DELETE CASCADE;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_role_permissions_permission_id' AND conrelid = 'auth.role_permissions'::regclass
) THEN
ALTER TABLE auth.role_permissions
ADD CONSTRAINT fk_role_permissions_permission_id
FOREIGN KEY (permission_id) REFERENCES auth.permissions (id) ON DELETE CASCADE;
END IF;
END $$;
-- ---------------------------------------------------------------------------
-- 6. user_applications — 用户入驻申请表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.user_applications (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
site_code VARCHAR(10) NOT NULL,
site_id BIGINT,
applied_role_text VARCHAR(100) NOT NULL,
employee_number VARCHAR(50),
phone VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
reviewer_id INT,
review_note TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
reviewed_at TIMESTAMPTZ
);
-- 外键
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_user_applications_user_id' AND conrelid = 'auth.user_applications'::regclass
) THEN
ALTER TABLE auth.user_applications
ADD CONSTRAINT fk_user_applications_user_id
FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE;
END IF;
END $$;
-- 索引
CREATE INDEX IF NOT EXISTS ix_user_applications_user_id ON auth.user_applications (user_id);
CREATE INDEX IF NOT EXISTS ix_user_applications_status ON auth.user_applications (status);
-- ---------------------------------------------------------------------------
-- 7. user_site_roles — 用户-门店-角色关联表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.user_site_roles (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
site_id BIGINT NOT NULL,
role_id INT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 外键
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_user_site_roles_user_id' AND conrelid = 'auth.user_site_roles'::regclass
) THEN
ALTER TABLE auth.user_site_roles
ADD CONSTRAINT fk_user_site_roles_user_id
FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_user_site_roles_role_id' AND conrelid = 'auth.user_site_roles'::regclass
) THEN
ALTER TABLE auth.user_site_roles
ADD CONSTRAINT fk_user_site_roles_role_id
FOREIGN KEY (role_id) REFERENCES auth.roles (id) ON DELETE CASCADE;
END IF;
END $$;
-- 唯一约束:同一用户在同一门店下不能重复分配同一角色
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uq_user_site_roles_user_site_role' AND conrelid = 'auth.user_site_roles'::regclass
) THEN
ALTER TABLE auth.user_site_roles
ADD CONSTRAINT uq_user_site_roles_user_site_role UNIQUE (user_id, site_id, role_id);
END IF;
END $$;
-- 索引
CREATE INDEX IF NOT EXISTS ix_user_site_roles_user_site ON auth.user_site_roles (user_id, site_id);
-- ---------------------------------------------------------------------------
-- 8. user_assistant_binding — 用户-人员绑定表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth.user_assistant_binding (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
site_id BIGINT NOT NULL,
assistant_id BIGINT,
staff_id BIGINT,
binding_type VARCHAR(20) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 外键
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_user_assistant_binding_user_id' AND conrelid = 'auth.user_assistant_binding'::regclass
) THEN
ALTER TABLE auth.user_assistant_binding
ADD CONSTRAINT fk_user_assistant_binding_user_id
FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE;
END IF;
END $$;
-- ---------------------------------------------------------------------------
-- 授予 app_user 对新表的 SEQUENCE 使用权限SERIAL 字段需要)
-- ---------------------------------------------------------------------------
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA auth TO app_user;
-- =============================================================================
-- 回滚脚本(按逆序执行)
-- =============================================================================
-- DROP TABLE IF EXISTS auth.user_assistant_binding CASCADE;
-- DROP TABLE IF EXISTS auth.user_site_roles CASCADE;
-- DROP TABLE IF EXISTS auth.user_applications CASCADE;
-- DROP TABLE IF EXISTS auth.role_permissions CASCADE;
-- DROP TABLE IF EXISTS auth.permissions CASCADE;
-- DROP TABLE IF EXISTS auth.roles CASCADE;
-- DROP TABLE IF EXISTS auth.site_code_mapping CASCADE;
-- DROP TABLE IF EXISTS auth.users CASCADE;