1
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移脚本:创建 auth/biz Schema 与 app_user 权限配置
|
||||
-- 日期:2026-02-24
|
||||
-- 目标库:test_zqyy_app(通过 APP_DB_DSN 连接)
|
||||
-- 说明:在业务库中创建 auth(用户认证)和 biz(业务数据)两个 Schema,
|
||||
-- 并授予 app_user 角色完整的 CRUD 权限(含未来新表自动授权)。
|
||||
-- 不操作 public Schema,保留其中现有系统管理表不受影响。
|
||||
-- 前提:app_user 角色已由 DBA 预创建
|
||||
-- 需求:1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 4.2, 4.3, 4.4, 4.5, 4.6
|
||||
-- =============================================================================
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. 创建 auth Schema(用户认证、权限、映射)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE SCHEMA IF NOT EXISTS auth;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. 创建 biz Schema(业务数据)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE SCHEMA IF NOT EXISTS biz;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. 授予 app_user 对 auth Schema 的 USAGE + CRUD 权限
|
||||
-- ---------------------------------------------------------------------------
|
||||
GRANT USAGE ON SCHEMA auth TO app_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA auth TO app_user;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. 授予 app_user 对 biz Schema 的 USAGE + CRUD 权限
|
||||
-- ---------------------------------------------------------------------------
|
||||
GRANT USAGE ON SCHEMA biz TO app_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA biz TO app_user;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5. 设置 ALTER DEFAULT PRIVILEGES:未来新表自动授权
|
||||
-- ---------------------------------------------------------------------------
|
||||
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;
|
||||
|
||||
-- =============================================================================
|
||||
-- 回滚脚本(按逆序执行)
|
||||
-- =============================================================================
|
||||
-- ALTER DEFAULT PRIVILEGES IN SCHEMA biz REVOKE SELECT, INSERT, UPDATE, DELETE ON TABLES FROM app_user;
|
||||
-- ALTER DEFAULT PRIVILEGES IN SCHEMA auth REVOKE SELECT, INSERT, UPDATE, DELETE ON TABLES FROM app_user;
|
||||
-- REVOKE SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA biz FROM app_user;
|
||||
-- REVOKE USAGE ON SCHEMA biz FROM app_user;
|
||||
-- REVOKE SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA auth FROM app_user;
|
||||
-- REVOKE USAGE ON SCHEMA auth FROM app_user;
|
||||
-- DROP SCHEMA IF EXISTS biz CASCADE;
|
||||
-- DROP SCHEMA IF EXISTS auth CASCADE;
|
||||
@@ -1,71 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移脚本:配置 FDW 跨库映射(ETL 库 → 业务库)
|
||||
-- 日期:2026-02-24
|
||||
-- 目标库:test_zqyy_app(通过 APP_DB_DSN 连接)
|
||||
-- 说明:通过 postgres_fdw 将 ETL 库 app Schema 的 RLS 视图映射为业务库
|
||||
-- fdw_etl Schema 的只读外部表,使后端无需直连 ETL 库即可读取汇总/维度数据。
|
||||
-- 前提:
|
||||
-- 1. ETL 库已部署 app Schema 及 RLS 视图(2026-02-24__p1_create_app_schema_rls_views.sql)
|
||||
-- 2. ETL 库已创建 app_reader 只读角色
|
||||
-- 3. 业务库已创建 app_user 角色
|
||||
-- 需求:3.1, 3.2, 3.3, 3.4, 3.7, 4.2, 4.3, 4.4, 4.5, 4.6
|
||||
-- =============================================================================
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. 安装 postgres_fdw 扩展
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. 创建外部服务器(指向 ETL 库)
|
||||
-- host / dbname / port 使用占位符 '***',部署时按环境替换
|
||||
-- 服务器名使用通用名(不含环境前缀),通过连接参数区分环境
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE SERVER IF NOT EXISTS etl_feiqiu_server
|
||||
FOREIGN DATA WRAPPER postgres_fdw
|
||||
OPTIONS (host '***', dbname '***', port '***');
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. 创建用户映射(只读角色)
|
||||
-- app_user = test_zqyy_app 侧的应用连接角色
|
||||
-- app_reader = ETL 库侧的只读角色
|
||||
-- 密码占位符 '***',部署时替换为真实凭据
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE USER MAPPING IF NOT EXISTS FOR app_user
|
||||
SERVER etl_feiqiu_server
|
||||
OPTIONS (user 'app_reader', password '***');
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. 创建 fdw_etl Schema(幂等处理:先 DROP 再重建)
|
||||
-- IMPORT FOREIGN SCHEMA 不是幂等的(外部表已存在会报错),
|
||||
-- 因此采用 DROP CASCADE + 重建的方式确保可重复执行。
|
||||
-- ---------------------------------------------------------------------------
|
||||
DROP SCHEMA IF EXISTS fdw_etl CASCADE;
|
||||
CREATE SCHEMA fdw_etl;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5. 批量导入 ETL 库 app Schema 的所有外部表到 fdw_etl
|
||||
-- ---------------------------------------------------------------------------
|
||||
IMPORT FOREIGN SCHEMA app
|
||||
FROM SERVER etl_feiqiu_server
|
||||
INTO fdw_etl;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 6. 授权:允许 app_user 访问 fdw_etl Schema 及其外部表
|
||||
-- ---------------------------------------------------------------------------
|
||||
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;
|
||||
|
||||
-- =============================================================================
|
||||
-- 回滚脚本(按逆序执行)
|
||||
-- =============================================================================
|
||||
-- ALTER DEFAULT PRIVILEGES IN SCHEMA fdw_etl REVOKE SELECT ON TABLES FROM app_user;
|
||||
-- REVOKE SELECT ON ALL TABLES IN SCHEMA fdw_etl FROM app_user;
|
||||
-- REVOKE USAGE ON SCHEMA fdw_etl FROM app_user;
|
||||
-- DROP SCHEMA IF EXISTS fdw_etl CASCADE;
|
||||
-- DROP USER MAPPING IF EXISTS FOR app_user SERVER etl_feiqiu_server;
|
||||
-- DROP SERVER IF EXISTS etl_feiqiu_server CASCADE;
|
||||
-- DROP EXTENSION IF EXISTS postgres_fdw;
|
||||
@@ -1,280 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移脚本:创建 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;
|
||||
@@ -1,75 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 种子数据脚本:预置权限列表、默认角色、角色-权限映射
|
||||
-- 日期:2026-02-25
|
||||
-- 目标库:test_zqyy_app(通过 APP_DB_DSN 连接)
|
||||
-- 说明:在 auth Schema 的 permissions、roles、role_permissions 表中插入种子数据。
|
||||
-- 使用 ON CONFLICT DO NOTHING 幂等语法,重复执行不会产生重复数据。
|
||||
-- 前提:auth.roles、auth.permissions、auth.role_permissions 表已由
|
||||
-- 2026-02-25__p3_create_auth_tables.sql 创建
|
||||
-- 需求:2.1, 2.2, 2.3, 2.4
|
||||
-- =============================================================================
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. 插入固定权限(5 条)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO auth.permissions (code, name, description) VALUES
|
||||
('view_tasks', '查看任务', '允许查看任务列表和任务详情'),
|
||||
('view_board', '查看看板', '允许查看数据看板概览'),
|
||||
('view_board_finance', '查看财务看板', '允许查看财务相关的数据看板'),
|
||||
('view_board_customer', '查看客户看板', '允许查看客户相关的数据看板'),
|
||||
('view_board_coach', '查看助教看板', '允许查看助教相关的数据看板')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. 插入默认角色(4 条)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO auth.roles (code, name, description) VALUES
|
||||
('coach', '助教', '球房助教,可查看任务和助教看板'),
|
||||
('staff', '员工', '球房员工,可查看任务和数据看板'),
|
||||
('site_admin', '店铺管理员', '单店管理员,可查看所有看板'),
|
||||
('tenant_admin', '租户管理员', '租户级管理员,拥有全部权限')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. 插入角色-权限映射
|
||||
-- coach: view_tasks, view_board_coach
|
||||
-- staff: view_tasks, view_board
|
||||
-- site_admin: view_tasks, view_board, view_board_finance, view_board_customer, view_board_coach
|
||||
-- tenant_admin: 全部 5 个权限
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO auth.role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM auth.roles r
|
||||
CROSS JOIN auth.permissions p
|
||||
WHERE (r.code, p.code) IN (
|
||||
-- coach: 2 个权限
|
||||
('coach', 'view_tasks'),
|
||||
('coach', 'view_board_coach'),
|
||||
-- staff: 2 个权限
|
||||
('staff', 'view_tasks'),
|
||||
('staff', 'view_board'),
|
||||
-- site_admin: 5 个权限
|
||||
('site_admin', 'view_tasks'),
|
||||
('site_admin', 'view_board'),
|
||||
('site_admin', 'view_board_finance'),
|
||||
('site_admin', 'view_board_customer'),
|
||||
('site_admin', 'view_board_coach'),
|
||||
-- tenant_admin: 5 个权限
|
||||
('tenant_admin', 'view_tasks'),
|
||||
('tenant_admin', 'view_board'),
|
||||
('tenant_admin', 'view_board_finance'),
|
||||
('tenant_admin', 'view_board_customer'),
|
||||
('tenant_admin', 'view_board_coach')
|
||||
)
|
||||
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- 回滚脚本(按逆序执行)
|
||||
-- =============================================================================
|
||||
-- DELETE FROM auth.role_permissions
|
||||
-- WHERE role_id IN (SELECT id FROM auth.roles WHERE code IN ('coach', 'staff', 'site_admin', 'tenant_admin'))
|
||||
-- AND permission_id IN (SELECT id FROM auth.permissions WHERE code IN ('view_tasks', 'view_board', 'view_board_finance', 'view_board_customer', 'view_board_coach'));
|
||||
--
|
||||
-- DELETE FROM auth.roles WHERE code IN ('coach', 'staff', 'site_admin', 'tenant_admin');
|
||||
--
|
||||
-- DELETE FROM auth.permissions WHERE code IN ('view_tasks', 'view_board', 'view_board_finance', 'view_board_customer', 'view_board_coach');
|
||||
@@ -1,99 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移:member_birthday_manual → member_retention_clue(维客线索重构)
|
||||
-- 日期:2026-02-26
|
||||
-- 目标库:zqyy_app / test_zqyy_app
|
||||
-- 说明:
|
||||
-- 1. 删除 member_birthday_manual 表(生日不再单独记录,归入维客线索"客户基础信息"大类)
|
||||
-- 2. 新建 member_retention_clue 表(维客线索:大类 + 摘要 + 详情)
|
||||
-- 影响:
|
||||
-- - 后端 API:/api/member-birthday 废弃,替换为 /api/retention-clue
|
||||
-- - ETL DWS:移除 FDW 读取 member_birthday_manual 的逻辑
|
||||
-- - FDW:fdw_app.member_birthday_manual 外部表需在 ETL 库侧同步删除
|
||||
-- 幂等:DROP IF EXISTS / CREATE IF NOT EXISTS,可重复执行
|
||||
-- =============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================
|
||||
-- 1. 删除旧表 member_birthday_manual
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS member_birthday_manual CASCADE;
|
||||
|
||||
-- ============================================================
|
||||
-- 2. 创建 member_retention_clue 表
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS member_retention_clue (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
member_id BIGINT NOT NULL,
|
||||
category VARCHAR(20) NOT NULL
|
||||
CONSTRAINT chk_retention_clue_category CHECK (
|
||||
category IN ('客户基础信息', '消费习惯', '玩法偏好', '促销偏好', '社交关系', '重要反馈')
|
||||
),
|
||||
summary VARCHAR(200) NOT NULL,
|
||||
detail TEXT,
|
||||
recorded_by_assistant_id BIGINT,
|
||||
recorded_by_name VARCHAR(50),
|
||||
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
site_id BIGINT NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE member_retention_clue
|
||||
IS '维客线索:助教为会员记录的销售/维护线索(大类 + 摘要 + 详情)';
|
||||
COMMENT ON COLUMN member_retention_clue.category
|
||||
IS '线索大类枚举:客户基础信息、消费习惯、玩法偏好、促销偏好、社交关系、重要反馈';
|
||||
COMMENT ON COLUMN member_retention_clue.summary
|
||||
IS '摘要:重点信息';
|
||||
COMMENT ON COLUMN member_retention_clue.detail
|
||||
IS '详情:分析及扩展说明,可为空';
|
||||
|
||||
-- ============================================================
|
||||
-- 3. 创建索引
|
||||
-- ============================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_retention_clue_member
|
||||
ON member_retention_clue (member_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_retention_clue_site
|
||||
ON member_retention_clue (site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_retention_clue_category
|
||||
ON member_retention_clue (member_id, category);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 回滚(需手动执行,不在事务内)
|
||||
-- ============================================================
|
||||
-- BEGIN;
|
||||
-- DROP TABLE IF EXISTS member_retention_clue CASCADE;
|
||||
-- -- 如需恢复旧表,执行 2026-02-22__C2_member_birthday_manual.sql
|
||||
-- COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 验证 SQL
|
||||
-- ============================================================
|
||||
-- 1. 确认旧表已删除
|
||||
-- SELECT table_name FROM information_schema.tables
|
||||
-- WHERE table_schema = 'public' AND table_name = 'member_birthday_manual';
|
||||
-- 预期:0 行
|
||||
|
||||
-- 2. 确认新表存在
|
||||
-- SELECT table_name FROM information_schema.tables
|
||||
-- WHERE table_schema = 'public' AND table_name = 'member_retention_clue';
|
||||
-- 预期:1 行
|
||||
|
||||
-- 3. 确认列结构完整(9 列)
|
||||
-- SELECT column_name, data_type, is_nullable
|
||||
-- FROM information_schema.columns
|
||||
-- WHERE table_schema = 'public' AND table_name = 'member_retention_clue'
|
||||
-- ORDER BY ordinal_position;
|
||||
-- 预期:id, member_id, category, summary, detail,
|
||||
-- recorded_by_assistant_id, recorded_by_name, recorded_at, site_id
|
||||
|
||||
-- 4. 确认 CHECK 约束存在
|
||||
-- SELECT conname, consrc FROM pg_constraint
|
||||
-- WHERE conrelid = 'member_retention_clue'::regclass AND contype = 'c';
|
||||
-- 预期:1 行,chk_retention_clue_category
|
||||
|
||||
-- 5. 确认索引存在
|
||||
-- SELECT indexname FROM pg_indexes
|
||||
-- WHERE tablename = 'member_retention_clue';
|
||||
-- 预期:3 行(idx_retention_clue_member, idx_retention_clue_site, idx_retention_clue_category)
|
||||
-- + 1 行主键索引
|
||||
@@ -1,21 +0,0 @@
|
||||
-- 迁移:为 auth.users.status 的 CHECK 约束添加 'new' 值
|
||||
-- 原因:新用户注册后初始状态为 'new'(尚未提交申请),提交申请后变为 'pending'
|
||||
-- 影响:后端 wx_login 新用户创建、dev_login mock 登录
|
||||
-- 回滚:见文件末尾
|
||||
|
||||
-- 1. 删除旧约束
|
||||
ALTER TABLE auth.users DROP CONSTRAINT IF EXISTS users_status_check;
|
||||
|
||||
-- 2. 添加包含 'new' 的新约束
|
||||
ALTER TABLE auth.users ADD CONSTRAINT users_status_check
|
||||
CHECK (status IN ('new', 'pending', 'approved', 'rejected', 'disabled'));
|
||||
|
||||
-- 3. 修改默认值:新用户初始状态为 'new'(尚未提交申请)
|
||||
ALTER TABLE auth.users ALTER COLUMN status SET DEFAULT 'new';
|
||||
|
||||
-- ── 回滚 SQL ──
|
||||
-- ALTER TABLE auth.users ALTER COLUMN status SET DEFAULT 'pending';
|
||||
-- ALTER TABLE auth.users DROP CONSTRAINT IF EXISTS users_status_check;
|
||||
-- ALTER TABLE auth.users ADD CONSTRAINT users_status_check
|
||||
-- CHECK (status IN ('pending', 'approved', 'rejected', 'disabled'));
|
||||
-- UPDATE auth.users SET status = 'pending' WHERE status = 'new';
|
||||
@@ -1,52 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移:member_retention_clue 新增 source 字段
|
||||
-- 日期:2026-02-27
|
||||
-- 目标库:zqyy_app / test_zqyy_app
|
||||
-- 说明:
|
||||
-- 区分维客线索来源:manual(助教手动)/ ai_consumption(应用3)/ ai_note(应用6)
|
||||
-- 影响:
|
||||
-- - 后端 API:POST /api/retention-clue 已引用 source 列,本次补齐 DDL
|
||||
-- - 已有数据自动填充 DEFAULT 'manual'
|
||||
-- 幂等:IF NOT EXISTS 检查列是否存在,可重复执行
|
||||
-- =============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 仅在列不存在时添加(PostgreSQL 11+ 支持 IF NOT EXISTS)
|
||||
ALTER TABLE member_retention_clue
|
||||
ADD COLUMN IF NOT EXISTS source VARCHAR(20) NOT NULL DEFAULT 'manual';
|
||||
|
||||
COMMENT ON COLUMN member_retention_clue.source
|
||||
IS '线索来源:manual(助教手动)/ ai_consumption(应用3 消费分析)/ ai_note(应用6 备注分析)';
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 回滚(需手动执行)
|
||||
-- ============================================================
|
||||
-- BEGIN;
|
||||
-- ALTER TABLE member_retention_clue DROP COLUMN IF EXISTS source;
|
||||
-- COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 验证 SQL
|
||||
-- ============================================================
|
||||
-- 1. 确认 source 列存在
|
||||
-- SELECT column_name, data_type, column_default, is_nullable
|
||||
-- FROM information_schema.columns
|
||||
-- WHERE table_schema = 'public'
|
||||
-- AND table_name = 'member_retention_clue'
|
||||
-- AND column_name = 'source';
|
||||
-- 预期:1 行,varchar, 'manual'::character varying, NO
|
||||
|
||||
-- 2. 确认已有数据的 source 值
|
||||
-- SELECT source, COUNT(*) FROM member_retention_clue GROUP BY source;
|
||||
-- 预期:全部为 'manual'(或空表)
|
||||
|
||||
-- 3. 确认列注释
|
||||
-- SELECT col_description(
|
||||
-- (SELECT oid FROM pg_class WHERE relname = 'member_retention_clue'),
|
||||
-- (SELECT ordinal_position FROM information_schema.columns
|
||||
-- WHERE table_name = 'member_retention_clue' AND column_name = 'source')
|
||||
-- );
|
||||
-- 预期:包含 'manual' / 'ai_consumption' / 'ai_note'
|
||||
@@ -1,107 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移脚本:创建 biz Schema 业务表(coach_tasks / coach_task_history / notes / trigger_jobs)
|
||||
-- 日期:2026-02-27
|
||||
-- 目标库:test_zqyy_app(通过 APP_DB_DSN 连接)
|
||||
-- 说明:在 biz Schema 下创建助教任务系统和备注系统所需的 4 张业务表,
|
||||
-- 包含字段定义、约束、CHECK 约束、外键、部分唯一索引和查询索引。
|
||||
-- 前提:biz Schema 已由 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
|
||||
-- =============================================================================
|
||||
|
||||
-- 确保 biz Schema 存在(幂等)
|
||||
CREATE SCHEMA IF NOT EXISTS biz;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. biz.coach_tasks — 助教任务表
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS biz.coach_tasks (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
assistant_id BIGINT NOT NULL,
|
||||
member_id BIGINT NOT NULL,
|
||||
task_type VARCHAR(50) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||
priority_score NUMERIC(5,2),
|
||||
expires_at TIMESTAMPTZ,
|
||||
is_pinned BOOLEAN DEFAULT FALSE,
|
||||
abandon_reason TEXT,
|
||||
completed_at TIMESTAMPTZ,
|
||||
completed_task_type VARCHAR(50),
|
||||
parent_task_id BIGINT REFERENCES biz.coach_tasks(id),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 部分唯一索引:同一 (site_id, assistant_id, member_id, task_type) 下 active 任务最多一条
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_coach_tasks_site_assistant_member_type
|
||||
ON biz.coach_tasks (site_id, assistant_id, member_id, task_type)
|
||||
WHERE status = 'active';
|
||||
|
||||
-- 助教任务列表查询索引
|
||||
CREATE INDEX IF NOT EXISTS idx_coach_tasks_assistant_status
|
||||
ON biz.coach_tasks (site_id, assistant_id, status);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. biz.coach_task_history — 任务变更历史表
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS biz.coach_task_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
task_id BIGINT NOT NULL REFERENCES biz.coach_tasks(id),
|
||||
action VARCHAR(50) NOT NULL,
|
||||
old_status VARCHAR(20),
|
||||
new_status VARCHAR(20),
|
||||
old_task_type VARCHAR(50),
|
||||
new_task_type VARCHAR(50),
|
||||
detail JSONB,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. biz.notes — 统一备注表
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS biz.notes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
target_type VARCHAR(50) NOT NULL,
|
||||
target_id BIGINT NOT NULL,
|
||||
type VARCHAR(20) NOT NULL DEFAULT 'normal',
|
||||
content TEXT NOT NULL,
|
||||
rating_service_willingness SMALLINT CHECK (rating_service_willingness BETWEEN 1 AND 5),
|
||||
rating_revisit_likelihood SMALLINT CHECK (rating_revisit_likelihood BETWEEN 1 AND 5),
|
||||
task_id BIGINT REFERENCES biz.coach_tasks(id),
|
||||
ai_score SMALLINT,
|
||||
ai_analysis TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 按目标查询备注索引
|
||||
CREATE INDEX IF NOT EXISTS idx_notes_target
|
||||
ON biz.notes (site_id, target_type, target_id);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. biz.trigger_jobs — 触发器配置表
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS biz.trigger_jobs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
job_type VARCHAR(100) NOT NULL,
|
||||
job_name VARCHAR(100) NOT NULL UNIQUE,
|
||||
trigger_condition VARCHAR(20) NOT NULL,
|
||||
trigger_config JSONB NOT NULL,
|
||||
last_run_at TIMESTAMPTZ,
|
||||
next_run_at TIMESTAMPTZ,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'enabled',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- 回滚脚本(按逆序执行)
|
||||
-- =============================================================================
|
||||
-- DROP INDEX IF EXISTS biz.idx_notes_target;
|
||||
-- DROP INDEX IF EXISTS biz.idx_coach_tasks_assistant_status;
|
||||
-- DROP INDEX IF EXISTS biz.idx_coach_tasks_site_assistant_member_type;
|
||||
-- DROP TABLE IF EXISTS biz.trigger_jobs;
|
||||
-- DROP TABLE IF EXISTS biz.notes;
|
||||
-- DROP TABLE IF EXISTS biz.coach_task_history;
|
||||
-- DROP TABLE IF EXISTS biz.coach_tasks;
|
||||
@@ -1,43 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移脚本:预置触发器配置种子数据
|
||||
-- 日期:2026-02-27
|
||||
-- 目标库:test_zqyy_app(通过 APP_DB_DSN 连接)
|
||||
-- 说明:在 biz.trigger_jobs 表中插入 4 条核心触发器配置,
|
||||
-- 驱动任务生成、有效期检查、召回完成检测、备注回溯重分类。
|
||||
-- 前提:biz.trigger_jobs 表已由 2026-02-27__p4_create_biz_tables.sql 创建
|
||||
-- 需求:2.1, 2.2, 2.3, 2.4, 2.5
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO biz.trigger_jobs (job_type, job_name, trigger_condition, trigger_config, next_run_at)
|
||||
VALUES
|
||||
-- 1. task_generator:每日凌晨 4:00 运行,基于 WBI/NCI/RS 指数为助教生成任务
|
||||
('task_generator', 'task_generator', 'cron',
|
||||
'{"cron_expression": "0 4 * * *"}',
|
||||
(CURRENT_DATE + 1) + INTERVAL '4 hours'),
|
||||
|
||||
-- 2. task_expiry_check:每小时运行,检查 expires_at 到期的任务并标记 inactive
|
||||
('task_expiry_check', 'task_expiry_check', 'interval',
|
||||
'{"interval_seconds": 3600}',
|
||||
NOW() + INTERVAL '1 hour'),
|
||||
|
||||
-- 3. recall_completion_check:ETL 数据更新后触发,检测助教服务记录匹配活跃任务
|
||||
('recall_completion_check', 'recall_completion_check', 'event',
|
||||
'{"event_name": "etl_data_updated"}',
|
||||
NULL),
|
||||
|
||||
-- 4. note_reclassify_backfill:召回完成后触发,回溯重分类普通备注为回访备注
|
||||
('note_reclassify_backfill', 'note_reclassify_backfill', 'event',
|
||||
'{"event_name": "recall_completed"}',
|
||||
NULL)
|
||||
ON CONFLICT (job_name) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- 回滚脚本
|
||||
-- =============================================================================
|
||||
-- DELETE FROM biz.trigger_jobs
|
||||
-- WHERE job_name IN (
|
||||
-- 'task_generator',
|
||||
-- 'task_expiry_check',
|
||||
-- 'recall_completion_check',
|
||||
-- 'note_reclassify_backfill'
|
||||
-- );
|
||||
@@ -1,50 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移:auth.site_code_mapping.tenant_id INTEGER → BIGINT
|
||||
-- 原因:飞球 tenant_id 值域(如 2790683160709957)远超 int4 上限(~21 亿)
|
||||
-- 日期:2026-03-03
|
||||
-- 目标库:zqyy_app / test_zqyy_app
|
||||
-- 注意:该列无外键、无索引依赖,可直接 ALTER
|
||||
-- =============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. ALTER 列类型
|
||||
ALTER TABLE auth.site_code_mapping
|
||||
ALTER COLUMN tenant_id TYPE bigint;
|
||||
|
||||
-- 2. 刷新 FDW 外部表(ETL 库 tenant_id 变更后需重新导入)
|
||||
-- 注意:此步骤需在 ETL 库迁移完成后执行
|
||||
-- 如果 FDW 外部表类型不匹配,需要重新 IMPORT FOREIGN SCHEMA
|
||||
-- DROP FOREIGN TABLE IF EXISTS fdw_etl.v_dws_assistant_order_contribution;
|
||||
-- IMPORT FOREIGN SCHEMA app
|
||||
-- LIMIT TO (v_dws_assistant_order_contribution)
|
||||
-- FROM SERVER test_etl_feiqiu_server
|
||||
-- INTO fdw_etl;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- =============================================================================
|
||||
-- 回滚脚本
|
||||
-- =============================================================================
|
||||
-- BEGIN;
|
||||
-- ALTER TABLE auth.site_code_mapping ALTER COLUMN tenant_id TYPE integer;
|
||||
-- COMMIT;
|
||||
|
||||
-- =============================================================================
|
||||
-- 验证 SQL
|
||||
-- =============================================================================
|
||||
-- 1. 确认列类型已变更
|
||||
-- SELECT column_name, data_type, udt_name
|
||||
-- FROM information_schema.columns
|
||||
-- WHERE table_schema = 'auth'
|
||||
-- AND table_name = 'site_code_mapping'
|
||||
-- AND column_name = 'tenant_id';
|
||||
-- 预期:data_type = 'bigint', udt_name = 'int8'
|
||||
|
||||
-- 2. 确认现有数据完整
|
||||
-- SELECT count(*) FROM auth.site_code_mapping;
|
||||
-- 预期:行数与迁移前一致
|
||||
|
||||
-- 3. 确认可插入大值 tenant_id
|
||||
-- SELECT 2790683160709957::bigint;
|
||||
-- 预期:返回 2790683160709957(不报溢出错误)
|
||||
@@ -1,11 +0,0 @@
|
||||
-- 2026-03-07 | 多实例任务隔离:task_queue 增加 enqueued_by 列
|
||||
-- 背景:发现有另一台机器(宿主机 D 盘部署)的后端也在消费同一个 task_queue,
|
||||
-- 导致任务被错误实例执行(路径不匹配 → UTF-8 解码失败)。
|
||||
-- 通过 enqueued_by 列记录入队实例的 hostname,process_loop 只消费自己入队的任务。
|
||||
|
||||
ALTER TABLE task_queue ADD COLUMN IF NOT EXISTS enqueued_by VARCHAR(255) DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN task_queue.enqueued_by IS '入队实例的 hostname(platform.node()),用于多实例任务隔离';
|
||||
|
||||
-- 回滚:
|
||||
-- ALTER TABLE task_queue DROP COLUMN IF EXISTS enqueued_by;
|
||||
@@ -1,82 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 迁移:member_retention_clue.category CHECK 约束枚举对齐
|
||||
-- 日期:2026-03-08
|
||||
-- 目标库:zqyy_app / test_zqyy_app
|
||||
-- 说明:
|
||||
-- 将 category CHECK 约束中 '客户基础信息' 改为 '客户基础',
|
||||
-- 与 AI 应用 Prompt 枚举值统一(P5 spec 2026-03-08 评审决定)。
|
||||
-- '促销偏好' 保持不变(原 Prompt 中的 '促销接受' 已在 spec 侧对齐为 '促销偏好')。
|
||||
-- 统一后 6 个枚举值:客户基础、消费习惯、玩法偏好、促销偏好、社交关系、重要反馈
|
||||
-- 影响:
|
||||
-- - 已有数据中 category='客户基础信息' 的行需更新为 '客户基础'
|
||||
-- - 后端 API:POST /api/retention-clue 的 category 校验需同步
|
||||
-- - AI 应用 3/6/8 的 Prompt 枚举已在 P5 spec 中统一
|
||||
-- 幂等:先检查约束是否存在再操作,可重复执行
|
||||
-- =============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================
|
||||
-- 1. 更新已有数据(先于约束变更)
|
||||
-- ============================================================
|
||||
UPDATE member_retention_clue
|
||||
SET category = '客户基础'
|
||||
WHERE category = '客户基础信息';
|
||||
|
||||
-- ============================================================
|
||||
-- 2. 删除旧 CHECK 约束
|
||||
-- ============================================================
|
||||
ALTER TABLE member_retention_clue
|
||||
DROP CONSTRAINT IF EXISTS chk_retention_clue_category;
|
||||
|
||||
-- ============================================================
|
||||
-- 3. 创建新 CHECK 约束('客户基础信息' → '客户基础')
|
||||
-- ============================================================
|
||||
ALTER TABLE member_retention_clue
|
||||
ADD CONSTRAINT chk_retention_clue_category CHECK (
|
||||
category IN ('客户基础', '消费习惯', '玩法偏好', '促销偏好', '社交关系', '重要反馈')
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 4. 更新列注释
|
||||
-- ============================================================
|
||||
COMMENT ON COLUMN member_retention_clue.category
|
||||
IS '线索大类枚举:客户基础、消费习惯、玩法偏好、促销偏好、社交关系、重要反馈';
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 回滚(需手动执行)
|
||||
-- ============================================================
|
||||
-- BEGIN;
|
||||
-- UPDATE member_retention_clue SET category = '客户基础信息' WHERE category = '客户基础';
|
||||
-- ALTER TABLE member_retention_clue DROP CONSTRAINT IF EXISTS chk_retention_clue_category;
|
||||
-- ALTER TABLE member_retention_clue ADD CONSTRAINT chk_retention_clue_category CHECK (
|
||||
-- category IN ('客户基础信息', '消费习惯', '玩法偏好', '促销偏好', '社交关系', '重要反馈')
|
||||
-- );
|
||||
-- COMMENT ON COLUMN member_retention_clue.category
|
||||
-- IS '线索大类枚举:客户基础信息、消费习惯、玩法偏好、促销偏好、社交关系、重要反馈';
|
||||
-- COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 验证 SQL
|
||||
-- ============================================================
|
||||
-- 1. 确认无 '客户基础信息' 残留
|
||||
-- SELECT COUNT(*) FROM member_retention_clue WHERE category = '客户基础信息';
|
||||
-- 预期:0
|
||||
|
||||
-- 2. 确认 CHECK 约束内容
|
||||
-- SELECT conname, pg_get_constraintdef(oid) FROM pg_constraint
|
||||
-- WHERE conrelid = 'member_retention_clue'::regclass AND contype = 'c';
|
||||
-- 预期:chk_retention_clue_category,包含 '客户基础'(非 '客户基础信息')
|
||||
|
||||
-- 3. 测试新枚举值可写入
|
||||
-- INSERT INTO member_retention_clue (member_id, category, summary, site_id)
|
||||
-- VALUES (0, '客户基础', '测试', 0);
|
||||
-- 预期:成功
|
||||
-- DELETE FROM member_retention_clue WHERE member_id = 0 AND summary = '测试';
|
||||
|
||||
-- 4. 测试旧枚举值被拒绝
|
||||
-- INSERT INTO member_retention_clue (member_id, category, summary, site_id)
|
||||
-- VALUES (0, '客户基础信息', '测试', 0);
|
||||
-- 预期:CHECK 约束违反错误
|
||||
@@ -1,65 +0,0 @@
|
||||
-- ============================================================
|
||||
-- P5 AI 集成层:创建 AI 对话、消息、缓存三张表
|
||||
-- 需求: 1.1, 1.2, 1.3, 1.4, 1.5
|
||||
-- ============================================================
|
||||
|
||||
-- 1. biz.ai_conversations —— AI 对话记录
|
||||
CREATE TABLE IF NOT EXISTS biz.ai_conversations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
nickname VARCHAR(100) NOT NULL DEFAULT '',
|
||||
app_id VARCHAR(30) NOT NULL,
|
||||
site_id BIGINT NOT NULL,
|
||||
source_page VARCHAR(100),
|
||||
source_context JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE biz.ai_conversations IS 'AI 对话记录:每次 AI 调用(用户主动或系统自动)创建一条';
|
||||
COMMENT ON COLUMN biz.ai_conversations.app_id IS '应用标识:app1_chat / app2_finance / app3_clue / app4_analysis / app5_tactics / app6_note / app7_customer / app8_consolidation';
|
||||
COMMENT ON COLUMN biz.ai_conversations.user_id IS '用户 ID,系统自动调用时为 system';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_conv_user_site ON biz.ai_conversations (user_id, site_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_conv_app_site ON biz.ai_conversations (app_id, site_id, created_at DESC);
|
||||
|
||||
-- 2. biz.ai_messages —— AI 消息记录
|
||||
CREATE TABLE IF NOT EXISTS biz.ai_messages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
conversation_id BIGINT NOT NULL REFERENCES biz.ai_conversations(id) ON DELETE CASCADE,
|
||||
role VARCHAR(10) NOT NULL
|
||||
CONSTRAINT chk_ai_msg_role CHECK (role IN ('user', 'assistant', 'system')),
|
||||
content TEXT NOT NULL,
|
||||
tokens_used INTEGER,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE biz.ai_messages IS 'AI 消息记录:对话中的每条消息(输入/输出/系统)';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_msg_conv ON biz.ai_messages (conversation_id, created_at);
|
||||
|
||||
-- 3. biz.ai_cache —— AI 应用缓存
|
||||
CREATE TABLE IF NOT EXISTS biz.ai_cache (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
cache_type VARCHAR(30) NOT NULL
|
||||
CONSTRAINT chk_ai_cache_type CHECK (
|
||||
cache_type IN (
|
||||
'app2_finance', 'app3_clue', 'app4_analysis',
|
||||
'app5_tactics', 'app6_note_analysis',
|
||||
'app7_customer_analysis', 'app8_clue_consolidated'
|
||||
)
|
||||
),
|
||||
site_id BIGINT NOT NULL,
|
||||
target_id VARCHAR(100) NOT NULL,
|
||||
result_json JSONB NOT NULL,
|
||||
score INTEGER,
|
||||
triggered_by VARCHAR(100),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
COMMENT ON TABLE biz.ai_cache IS 'AI 应用缓存:各应用的结构化输出结果';
|
||||
COMMENT ON COLUMN biz.ai_cache.target_id IS '目标 ID:App2=时间维度编码 / App3,6,7,8=member_id / App4,5={assistant_id}_{member_id}';
|
||||
COMMENT ON COLUMN biz.ai_cache.score IS '评分:仅应用 6 使用(1-10 分)';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_cache_lookup ON biz.ai_cache (cache_type, site_id, target_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_cache_cleanup ON biz.ai_cache (cache_type, site_id, target_id, created_at);
|
||||
@@ -1,13 +0,0 @@
|
||||
-- 给 task_execution_log 添加 schedule_id 字段,关联调度任务
|
||||
-- 用于查询某个调度任务的执行历史
|
||||
ALTER TABLE task_execution_log
|
||||
ADD COLUMN IF NOT EXISTS schedule_id UUID REFERENCES scheduled_tasks(id) ON DELETE SET NULL;
|
||||
|
||||
-- 按 schedule_id 查询执行历史的部分索引
|
||||
CREATE INDEX IF NOT EXISTS idx_execution_log_schedule_id
|
||||
ON task_execution_log(schedule_id)
|
||||
WHERE schedule_id IS NOT NULL;
|
||||
|
||||
-- 给 task_queue 添加 schedule_id 字段,追溯调度来源
|
||||
ALTER TABLE task_queue
|
||||
ADD COLUMN IF NOT EXISTS schedule_id UUID REFERENCES scheduled_tasks(id) ON DELETE SET NULL;
|
||||
@@ -1,29 +0,0 @@
|
||||
-- 触发器:INSERT task_execution_log 时自动从 task_queue 回填 schedule_id
|
||||
-- 背景:队列处理循环中 schedule_id 可能未通过代码传递,用触发器兜底
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_backfill_schedule_id()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.schedule_id IS NULL AND NEW.queue_id IS NOT NULL THEN
|
||||
SELECT schedule_id INTO NEW.schedule_id
|
||||
FROM task_queue
|
||||
WHERE id = NEW.queue_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_backfill_schedule_id ON task_execution_log;
|
||||
|
||||
CREATE TRIGGER trg_backfill_schedule_id
|
||||
BEFORE INSERT ON task_execution_log
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION fn_backfill_schedule_id();
|
||||
|
||||
-- 回填历史数据
|
||||
UPDATE task_execution_log el
|
||||
SET schedule_id = tq.schedule_id
|
||||
FROM task_queue tq
|
||||
WHERE el.queue_id = tq.id
|
||||
AND tq.schedule_id IS NOT NULL
|
||||
AND el.schedule_id IS NULL;
|
||||
Reference in New Issue
Block a user