在前后端开发联调前 的提交20260223
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# BD_Manual:assistant_cancellation_records(助教废除记录)
|
||||
|
||||
> ODS 表:`ods.assistant_cancellation_records`
|
||||
> DWD 表:`dwd.dwd_assistant_trash_event`(主表)、`dwd.dwd_assistant_trash_event_ex`(扩展表)
|
||||
> API 接口:助教废除记录列表
|
||||
> JSON 路径:`assistant_cancellation_records.json → data.orderAssistantTrashLedgers`
|
||||
> 装载方式:事实表增量插入(`DwdLoadTask`)
|
||||
> 代码位置:`apps/etl/connectors/feiqiu/tasks/dwd/dwd_load_task.py`
|
||||
|
||||
---
|
||||
|
||||
## 1. dwd_assistant_trash_event(主表)
|
||||
|
||||
| DWD 列名 | 类型 | ODS 源列 | 映射方式 | 业务含义 | 取值范围/示例 |
|
||||
|----------|------|---------|---------|---------|-------------|
|
||||
| `assistant_trash_event_id` | BIGINT | `id` | FACT_MAPPINGS | 废除记录唯一标识(PK) | 飞球雪花 ID |
|
||||
| `site_id` | BIGINT | `siteid` | FACT_MAPPINGS | 门店 ID | 飞球门店 ID |
|
||||
| `tenant_id` | BIGINT | `tenant_id` | FACT_MAPPINGS | 租户 ID | 飞球租户 ID |
|
||||
| `assistant_no` | TEXT | `assistanton` | FACT_MAPPINGS | 助教编号(工号/序号)。ODS 列名 `assistantOn` 在 PG 中小写化为 `assistanton`,语义为助教的门店内编号 | 如 `31`、`1` |
|
||||
| `abolish_amount` | NUMERIC | `assistantabolishamount` | FACT_MAPPINGS | 废除金额(元),被废除的服务应收金额 | `0.00` ~ 金额值 |
|
||||
| `charge_minutes_raw` | INTEGER | `pdchargeminutes` | FACT_MAPPINGS | 陪打计费时长(分钟),被废除的服务时长 | 正整数 |
|
||||
| `table_id` | BIGINT | `tableid` | FACT_MAPPINGS | 台桌 ID | 飞球台桌 ID |
|
||||
| `table_area_id` | BIGINT | `tableareaid` | FACT_MAPPINGS | 台桌区域 ID | 飞球区域 ID |
|
||||
| `assistant_name` | TEXT | `assistantname` | FACT_MAPPINGS | 助教姓名快照 | 如 `张静然` |
|
||||
| `trash_reason` | TEXT | `trashreason` | FACT_MAPPINGS | 废除原因 | 自由文本 |
|
||||
| `create_time` | TIMESTAMPTZ | `createtime` | FACT_MAPPINGS | 废除操作时间 | ISO 时间戳 |
|
||||
|
||||
---
|
||||
|
||||
## 2. dwd_assistant_trash_event_ex(扩展表)
|
||||
|
||||
| DWD 列名 | 类型 | ODS 源列 | 映射方式 | 业务含义 | 取值范围/示例 |
|
||||
|----------|------|---------|---------|---------|-------------|
|
||||
| `assistant_trash_event_id` | BIGINT | `id` | FACT_MAPPINGS | 废除记录唯一标识(PK) | 同主表 |
|
||||
| `table_area_name` | TEXT | `tablearea` | FACT_MAPPINGS | 台桌区域名称快照 | 如 `大厅`、`VIP` |
|
||||
| `table_name` | VARCHAR(64) | `tablename` | FACT_MAPPINGS | 台桌名称快照 | 如 `1号台` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 跳过字段说明
|
||||
|
||||
| ODS 字段 | 跳过原因 |
|
||||
|---------|---------|
|
||||
| `siteprofile` | JSONB 嵌套列,已由 `dim_site` / `dim_site_ex` 通过 JSONB 提取映射 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 代码引用
|
||||
|
||||
- FACT_MAPPINGS:`dwd_load_task.py` → `FACT_MAPPINGS["dwd.dwd_assistant_trash_event"]` / `FACT_MAPPINGS["dwd.dwd_assistant_trash_event_ex"]`
|
||||
- TABLE_MAP:`"dwd.dwd_assistant_trash_event" → "ods.assistant_cancellation_records"`
|
||||
- DWS 下游:`dws_assistant_daily_task.py`(助教日业绩汇总,废除金额扣减)
|
||||
68
docs/database/_archived/BD_Manual_dim_member_add_birthday.md
Normal file
68
docs/database/_archived/BD_Manual_dim_member_add_birthday.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# BD 手册:会员生日字段 ETL 链路补齐(C1)
|
||||
|
||||
## 概述
|
||||
|
||||
为支持会员生日信息从上游 API 完整传递到 DWS 层,在 ODS 和 DWD 两层的会员表中新增 `birthday DATE` 列。ODS 层从 API payload 提取生日值落地,DWD 层通过自动列映射和 SCD2 机制同步。
|
||||
|
||||
## 变更说明
|
||||
|
||||
| Schema | 表 | 变更类型 | 字段 | 类型 | 说明 |
|
||||
|--------|---|---------|------|------|------|
|
||||
| ods | member_profiles | 加列 | birthday | DATE | 会员生日,从上游 API payload 提取 |
|
||||
| dwd | dim_member | 加列 | birthday | DATE | 会员生日,ODS → DWD 自动列映射 |
|
||||
|
||||
## 兼容性
|
||||
|
||||
- ETL Connector:DwdLoadTask 通过 `_get_columns()` 自动读取 DWD 表列名,新增列会被自动包含在列映射中;SCD2 变化检测自动覆盖所有非元数据列,`birthday` 无需额外配置
|
||||
- 后端 API / 小程序:无影响(当前未直接读取 `dim_member`)
|
||||
- DWS 层:`member_visit_task` 和 `member_consumption_task` 的 `_extract_member_info()` SQL 中已引用 `birthday` 字段,通过 `COALESCE(fdw_app.member_birthday_manual.birthday_value, dim_member.birthday)` 实现手动补录优先于 API 来源的合并逻辑(详见 `BD_Manual_fdw_reverse_member_birthday.md`)
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
ALTER TABLE ods.member_profiles DROP COLUMN IF EXISTS birthday;
|
||||
ALTER TABLE dwd.dim_member DROP COLUMN IF EXISTS birthday;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
回滚无数据丢失风险:`birthday` 为新增列,删除前所有值均为 NULL 或新写入的生日数据(可从 ODS payload JSONB 重新提取)。
|
||||
|
||||
## 验证步骤
|
||||
|
||||
```sql
|
||||
-- 1. 确认 ods.member_profiles.birthday 列存在且类型正确
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'ods'
|
||||
AND table_name = 'member_profiles'
|
||||
AND column_name = 'birthday';
|
||||
-- 预期:1 行,data_type = 'date'
|
||||
|
||||
-- 2. 确认 dwd.dim_member.birthday 列存在且类型正确
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'dwd'
|
||||
AND table_name = 'dim_member'
|
||||
AND column_name = 'birthday';
|
||||
-- 预期:1 行,data_type = 'date'
|
||||
|
||||
-- 3. 确认 dwd.dim_member.birthday 列注释已设置
|
||||
SELECT col_description(c.oid, a.attnum) AS column_comment
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
WHERE n.nspname = 'dwd'
|
||||
AND c.relname = 'dim_member'
|
||||
AND a.attname = 'birthday';
|
||||
-- 预期:'会员生日,来源:ODS member_profiles payload 中的 birthday 字段'
|
||||
```
|
||||
|
||||
## 关联文件
|
||||
|
||||
- 迁移脚本:`db/etl_feiqiu/migrations/2026-02-22__C1_dim_member_add_birthday.sql`
|
||||
- 主 DDL:`db/etl_feiqiu/schemas/ods.sql`(`member_profiles` 表)、`db/etl_feiqiu/schemas/dwd.sql`(`dim_member` 表)
|
||||
- 需求文档:`.kiro/specs/etl-aggregation-fix/requirements.md` — 需求 4.1, 4.2, 4.3, 4.4
|
||||
- ODS 入库逻辑:`apps/etl/connectors/feiqiu/tasks/ods/ods_tasks.py` — `member_profiles` 字段列表包含 `birthday`
|
||||
- DWS 生日合并:`BD_Manual_fdw_reverse_member_birthday.md` — FDW 反向映射供 DWS 任务 COALESCE 读取
|
||||
- 手动补录表:`BD_Manual_member_birthday_manual.md` — `zqyy_app.member_birthday_manual` 表文档
|
||||
@@ -0,0 +1,65 @@
|
||||
# BD 手册:删除助教废除(Abolish)独立链路表
|
||||
|
||||
## 迁移脚本
|
||||
|
||||
`db/etl_feiqiu/migrations/2026-02-22__drop_assistant_abolish_tables.sql`
|
||||
|
||||
## 变更说明
|
||||
|
||||
### 删除的表
|
||||
|
||||
| Schema | 表名 | 说明 |
|
||||
|--------|------|------|
|
||||
| `ods` | `assistant_cancellation_records` | 上游废除 API 原始数据(仅 78 条),不再抓取 |
|
||||
| `dwd` | `dwd_assistant_trash_event` | 废除事件主表,无消费者 |
|
||||
| `dwd` | `dwd_assistant_trash_event_ex` | 废除事件扩展表,无消费者 |
|
||||
|
||||
### 删除的索引
|
||||
|
||||
| Schema | 索引名 | 所属表 |
|
||||
|--------|--------|--------|
|
||||
| `ods` | `idx_ods_assistant_cancellation_records_latest` | `assistant_cancellation_records` |
|
||||
|
||||
### 清理的元数据
|
||||
|
||||
- `meta.etl_task` 中 `task_code = 'ODS_ASSISTANT_ABOLISH'` 的注册行
|
||||
|
||||
## 兼容性影响
|
||||
|
||||
- **ETL**:ODS 抓取任务 `ODS_ASSISTANT_ABOLISH` 已在代码层移除(Task 1–3),迁移脚本清理数据库残留
|
||||
- **DWD 加载**:`FACT_MAPPINGS` 和 `TABLE_MAP` 中的废除表映射已在代码层移除
|
||||
- **DWS 聚合**:`assistant_daily_task.py` 已改用 `dwd_assistant_service_log_ex.is_trash` 字段,不受影响
|
||||
- **后端 API**:无直接引用这些表
|
||||
- **小程序**:无直接引用这些表
|
||||
|
||||
## 回滚策略
|
||||
|
||||
1. 从 `db/etl_feiqiu/schemas/ods.sql` 恢复 `assistant_cancellation_records` 的 `CREATE TABLE`
|
||||
2. 从 `db/etl_feiqiu/schemas/dwd.sql` 恢复 `dwd_assistant_trash_event` / `_ex` 的 `CREATE TABLE`
|
||||
3. 重建索引:`CREATE INDEX idx_ods_assistant_cancellation_records_latest ON ods.assistant_cancellation_records (id, fetched_at DESC);`
|
||||
4. 重新注册任务:`INSERT INTO meta.etl_task (task_code, store_id, enabled) VALUES ('ODS_ASSISTANT_ABOLISH', <store_id>, TRUE);`
|
||||
5. ODS 数据可从上游 API 重新抓取(仅 78 条)
|
||||
|
||||
## 验证 SQL
|
||||
|
||||
```sql
|
||||
-- 1. 确认 3 张表已不存在
|
||||
SELECT tablename FROM pg_tables
|
||||
WHERE schemaname IN ('ods', 'dwd')
|
||||
AND tablename IN (
|
||||
'assistant_cancellation_records',
|
||||
'dwd_assistant_trash_event',
|
||||
'dwd_assistant_trash_event_ex'
|
||||
);
|
||||
-- 预期:0 行
|
||||
|
||||
-- 2. 确认索引已不存在
|
||||
SELECT indexname FROM pg_indexes
|
||||
WHERE schemaname = 'ods'
|
||||
AND indexname = 'idx_ods_assistant_cancellation_records_latest';
|
||||
-- 预期:0 行
|
||||
|
||||
-- 3. 确认 meta.etl_task 中无 ODS_ASSISTANT_ABOLISH 注册
|
||||
SELECT * FROM meta.etl_task WHERE task_code = 'ODS_ASSISTANT_ABOLISH';
|
||||
-- 预期:0 行
|
||||
```
|
||||
@@ -0,0 +1,93 @@
|
||||
# BD 手册:助教月度汇总表唯一约束变更(需求 A)
|
||||
|
||||
## 概述
|
||||
|
||||
`dws.dws_assistant_monthly_summary` 的唯一约束从 `(site_id, assistant_id, stat_month)` 变更为 `(site_id, assistant_id, stat_month, assistant_level_code)`,以支持助教月内多档位分段统计。
|
||||
|
||||
## 变更说明
|
||||
|
||||
| Schema | 表 | 变更类型 | 约束名 | 旧定义 | 新定义 |
|
||||
|--------|---|---------|--------|--------|--------|
|
||||
| dws | dws_assistant_monthly_summary | 改约束 | uk_dws_assistant_monthly | `(site_id, assistant_id, stat_month)` | `(site_id, assistant_id, stat_month, assistant_level_code)` |
|
||||
|
||||
### 变更原因
|
||||
|
||||
助教在同一月内可能因升级/降级而存在多个 `assistant_level_code`。旧约束只允许每个助教每月一行记录,导致:
|
||||
- `AssistantMonthlyTask` 按档位分组聚合时,INSERT 违反唯一约束
|
||||
- 临时修复(`MAX()` 聚合)丢失了档位维度信息,无法按档位分段计算工资
|
||||
|
||||
新约束允许同一助教同月按不同档位生成多行记录,支持精确的分段业绩统计和工资计算。
|
||||
|
||||
## 兼容性
|
||||
|
||||
- **ETL Connector**:`AssistantMonthlyTask` 的 `_extract_daily_aggregates()` 将同步修改 GROUP BY 加入 `assistant_level_code`(任务 7.2),INSERT 逻辑适配多行输出
|
||||
- **AssistantSalaryTask**:需适配多行月度汇总结构,按档位分段计算工资(任务 7.4)
|
||||
- **后端 API / 小程序**:无直接影响(当前未直接读取 `dws_assistant_monthly_summary`)
|
||||
- **DWS 下游任务**:`AssistantFinanceTask`、`AssistantCustomerTask` 不依赖此表的唯一约束,无影响
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
-- ⚠️ 回滚前须先清理同一 (site_id, assistant_id, stat_month) 的多行数据,
|
||||
-- 否则恢复旧约束会因重复键失败。
|
||||
|
||||
-- 步骤 1:清理多档位数据(保留每组最新一条)
|
||||
DELETE FROM dws.dws_assistant_monthly_summary a
|
||||
USING (
|
||||
SELECT site_id, assistant_id, stat_month,
|
||||
MAX(updated_at) AS keep_updated_at
|
||||
FROM dws.dws_assistant_monthly_summary
|
||||
GROUP BY site_id, assistant_id, stat_month
|
||||
) b
|
||||
WHERE a.site_id = b.site_id
|
||||
AND a.assistant_id = b.assistant_id
|
||||
AND a.stat_month = b.stat_month
|
||||
AND a.updated_at <> b.keep_updated_at;
|
||||
|
||||
-- 步骤 2:恢复旧约束
|
||||
BEGIN;
|
||||
ALTER TABLE dws.dws_assistant_monthly_summary
|
||||
DROP CONSTRAINT IF EXISTS uk_dws_assistant_monthly;
|
||||
ALTER TABLE dws.dws_assistant_monthly_summary
|
||||
ADD CONSTRAINT uk_dws_assistant_monthly
|
||||
UNIQUE (site_id, assistant_id, stat_month);
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
回滚后需重新执行 `DWS_ASSISTANT_MONTHLY` 任务以 MAX() 模式重新聚合数据。
|
||||
|
||||
## 验证步骤
|
||||
|
||||
```sql
|
||||
-- 1. 确认新约束存在且列组合正确
|
||||
SELECT conname, pg_get_constraintdef(oid) AS constraint_def
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'dws.dws_assistant_monthly_summary'::regclass
|
||||
AND conname = 'uk_dws_assistant_monthly';
|
||||
-- 预期:1 行,constraint_def 包含 (site_id, assistant_id, stat_month, assistant_level_code)
|
||||
|
||||
-- 2. 确认约束列数为 4
|
||||
SELECT COUNT(*) AS col_count
|
||||
FROM pg_constraint c
|
||||
JOIN LATERAL unnest(c.conkey) AS col_num ON TRUE
|
||||
WHERE c.conrelid = 'dws.dws_assistant_monthly_summary'::regclass
|
||||
AND c.conname = 'uk_dws_assistant_monthly';
|
||||
-- 预期:4
|
||||
|
||||
-- 3. 确认旧的 3 列约束不存在
|
||||
SELECT conname, array_length(conkey, 1) AS col_count
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'dws.dws_assistant_monthly_summary'::regclass
|
||||
AND contype = 'u';
|
||||
-- 预期:uk_dws_assistant_monthly 的 col_count = 4(非 3)
|
||||
```
|
||||
|
||||
## 关联文件
|
||||
|
||||
- 迁移脚本:`db/etl_feiqiu/migrations/2026-02-22__A_monthly_summary_uk_change.sql`
|
||||
- 主 DDL:`db/etl_feiqiu/schemas/dws.sql`(已合并)
|
||||
- 工资表约束变更:`BD_Manual_dws_assistant_salary_uk_change.md`
|
||||
- 需求文档:`.kiro/specs/etl-aggregation-fix/requirements.md` — 需求 2.1, 2.2, 2.3
|
||||
- 代码变更:
|
||||
- `apps/etl/connectors/feiqiu/tasks/dws/assistant_monthly_task.py` — GROUP BY 加入 `assistant_level_code`
|
||||
- `apps/etl/connectors/feiqiu/tasks/dws/assistant_salary_task.py` — 按档位分段计算工资
|
||||
@@ -0,0 +1,94 @@
|
||||
# BD 手册:助教工资计算表唯一约束变更(需求 A — 任务 7.4)
|
||||
|
||||
## 概述
|
||||
|
||||
`dws.dws_assistant_salary_calc` 的唯一约束从 `(site_id, assistant_id, salary_month)` 变更为 `(site_id, assistant_id, salary_month, assistant_level_code)`,以支持同一助教同月按不同档位分段计算工资。
|
||||
|
||||
## 变更说明
|
||||
|
||||
| Schema | 表 | 变更类型 | 约束名 | 旧定义 | 新定义 |
|
||||
|--------|---|---------|--------|--------|--------|
|
||||
| dws | dws_assistant_salary_calc | 改约束 | uk_dws_assistant_salary | `(site_id, assistant_id, salary_month)` | `(site_id, assistant_id, salary_month, assistant_level_code)` |
|
||||
|
||||
### 变更原因
|
||||
|
||||
上游 `dws_assistant_monthly_summary` 已按 `(site_id, assistant_id, stat_month, assistant_level_code)` 分行(任务 7.2/7.3)。`AssistantSalaryTask` 逐行读取月度汇总并计算工资,同一助教同月可能产生多条工资记录(对应不同档位)。旧约束 `(site_id, assistant_id, salary_month)` 会导致 INSERT 冲突。
|
||||
|
||||
### 代码变更
|
||||
|
||||
- `assistant_salary_task.py` 的 `get_primary_keys()` 返回值加入 `assistant_level_code`
|
||||
- `_extract_monthly_summary()`、`transform()`、`_calculate_salary()` 无需修改——已天然按行处理
|
||||
|
||||
## 兼容性
|
||||
|
||||
- **ETL Connector**:`AssistantSalaryTask` 的 `get_primary_keys()` 已同步更新,`upsert()` 方法使用该键做 ON CONFLICT
|
||||
- **`_delete_by_month()`**:按 `site_id + salary_month` 整月删除后重新插入,不受约束变更影响
|
||||
- **后端 API / 小程序**:无直接影响(当前未直接读取 `dws_assistant_salary_calc`)
|
||||
- **DWS 下游任务**:`AssistantFinanceDailyTask` 读取 salary_calc 做日度成本分摊,需确认按 assistant_id 聚合时能正确处理多档位行(现有逻辑按 assistant_id + stat_date 聚合,不受影响)
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
-- ⚠️ 回滚前须先清理同一 (site_id, assistant_id, salary_month) 的多行数据,
|
||||
-- 否则恢复旧约束会因重复键失败。
|
||||
|
||||
-- 步骤 1:清理多档位数据(保留每组最新一条)
|
||||
DELETE FROM dws.dws_assistant_salary_calc a
|
||||
USING (
|
||||
SELECT site_id, assistant_id, salary_month,
|
||||
MAX(updated_at) AS keep_updated_at
|
||||
FROM dws.dws_assistant_salary_calc
|
||||
GROUP BY site_id, assistant_id, salary_month
|
||||
) b
|
||||
WHERE a.site_id = b.site_id
|
||||
AND a.assistant_id = b.assistant_id
|
||||
AND a.salary_month = b.salary_month
|
||||
AND a.updated_at <> b.keep_updated_at;
|
||||
|
||||
-- 步骤 2:恢复旧约束
|
||||
BEGIN;
|
||||
ALTER TABLE dws.dws_assistant_salary_calc
|
||||
DROP CONSTRAINT IF EXISTS uk_dws_assistant_salary;
|
||||
ALTER TABLE dws.dws_assistant_salary_calc
|
||||
ADD CONSTRAINT uk_dws_assistant_salary
|
||||
UNIQUE (site_id, assistant_id, salary_month);
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
回滚后需重新执行 `DWS_ASSISTANT_SALARY` 任务,此时月度汇总表也应已回滚为单行模式。
|
||||
|
||||
## 验证步骤
|
||||
|
||||
```sql
|
||||
-- 1. 确认新约束存在且列组合正确
|
||||
SELECT conname, pg_get_constraintdef(oid) AS constraint_def
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'dws.dws_assistant_salary_calc'::regclass
|
||||
AND conname = 'uk_dws_assistant_salary';
|
||||
-- 预期:1 行,constraint_def 包含 (site_id, assistant_id, salary_month, assistant_level_code)
|
||||
|
||||
-- 2. 确认约束列数为 4
|
||||
SELECT COUNT(*) AS col_count
|
||||
FROM pg_constraint c
|
||||
JOIN LATERAL unnest(c.conkey) AS col_num ON TRUE
|
||||
WHERE c.conrelid = 'dws.dws_assistant_salary_calc'::regclass
|
||||
AND c.conname = 'uk_dws_assistant_salary';
|
||||
-- 预期:4
|
||||
|
||||
-- 3. 测试同一助教同月不同档位可共存
|
||||
INSERT INTO dws.dws_assistant_salary_calc
|
||||
(site_id, tenant_id, assistant_id, salary_month, assistant_level_code)
|
||||
VALUES (99999, 99999, 99999, '2099-01-01', 10),
|
||||
(99999, 99999, 99999, '2099-01-01', 20);
|
||||
-- 预期:成功插入 2 行
|
||||
DELETE FROM dws.dws_assistant_salary_calc
|
||||
WHERE site_id = 99999 AND assistant_id = 99999 AND salary_month = '2099-01-01';
|
||||
```
|
||||
|
||||
## 关联文件
|
||||
|
||||
- 迁移脚本:`db/etl_feiqiu/migrations/2026-02-22__A_salary_calc_uk_change.sql`
|
||||
- 主 DDL:`db/etl_feiqiu/schemas/dws.sql`、`db/etl_feiqiu/schemas/schema_dws.sql`
|
||||
- 代码:`apps/etl/connectors/feiqiu/tasks/dws/assistant_salary_task.py`
|
||||
- 需求文档:`.kiro/specs/etl-aggregation-fix/requirements.md` — 需求 2.4
|
||||
- 前置任务:7.1(monthly_summary UK 变更)、7.2/7.3(月度聚合按档位分行)
|
||||
105
docs/database/_archived/BD_Manual_fdw_reverse_member_birthday.md
Normal file
105
docs/database/_archived/BD_Manual_fdw_reverse_member_birthday.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# BD 手册:FDW 反向映射 — ETL 库读取业务库会员生日
|
||||
|
||||
## 概述
|
||||
|
||||
在 `etl_feiqiu`(生产)/ `test_etl_feiqiu`(测试)数据库中,通过 `postgres_fdw` 创建指向 `zqyy_app` / `test_zqyy_app` 的外部表 `fdw_app.member_birthday_manual`,使 ETL DWS 任务可只读访问助教手动补录的会员生日数据。
|
||||
|
||||
方向:`etl_feiqiu → zqyy_app`(与现有 `setup_fdw.sql` 的 `zqyy_app → etl_feiqiu` 方向相反)。
|
||||
|
||||
## 变更说明
|
||||
|
||||
| 库 | Schema | 对象 | 变更类型 | 说明 |
|
||||
|----|--------|------|---------|------|
|
||||
| etl_feiqiu / test_etl_feiqiu | — | zqyy_app_server / test_zqyy_app_server | 新建外部服务器 | 指向业务库 |
|
||||
| etl_feiqiu / test_etl_feiqiu | — | etl_user → app_reader 映射 | 新建用户映射 | 只读角色映射 |
|
||||
| etl_feiqiu / test_etl_feiqiu | fdw_app | — | 新建 schema | 存放来自业务库的外部表 |
|
||||
| etl_feiqiu / test_etl_feiqiu | fdw_app | member_birthday_manual | 新建外部表 | 映射业务库 public.member_birthday_manual |
|
||||
|
||||
### 外部表列定义
|
||||
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | BIGINT | 自增主键(源表 BIGSERIAL) |
|
||||
| member_id | BIGINT | 会员 ID |
|
||||
| birthday_value | DATE | 补录的生日值 |
|
||||
| recorded_by_assistant_id | BIGINT | 提交助教 ID |
|
||||
| recorded_by_name | VARCHAR(50) | 提交助教姓名 |
|
||||
| recorded_at | TIMESTAMPTZ | 提交时间 |
|
||||
| source | VARCHAR(20) | 数据来源标识 |
|
||||
| site_id | BIGINT | 门店 ID(多门店隔离,Requirements 13.1) |
|
||||
|
||||
### 角色说明
|
||||
|
||||
| 角色 | 所在库 | 用途 |
|
||||
|------|--------|------|
|
||||
| etl_user | etl_feiqiu | ETL 连接角色,通过 FDW 读取业务库数据 |
|
||||
| app_reader | zqyy_app | 业务库只读角色,供 FDW 用户映射使用 |
|
||||
|
||||
## 兼容性
|
||||
|
||||
- **ETL Connector**:DWS 任务(`member_visit_task`、`member_consumption_task`)通过 `fdw_app.member_birthday_manual` 读取手动补录生日,使用 `COALESCE(手动补录值, dim_member.birthday)` 合并
|
||||
- **后端 API**:无影响,后端直接写入 `zqyy_app.member_birthday_manual`
|
||||
- **小程序**:无影响
|
||||
- **现有 FDW**:与现有 `setup_fdw.sql`(zqyy_app → etl_feiqiu 方向)互不干扰,使用不同的 server 名和 schema 名
|
||||
|
||||
## 回滚策略
|
||||
|
||||
在 ETL 库(`etl_feiqiu` 或 `test_etl_feiqiu`)中按逆序执行:
|
||||
|
||||
```sql
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA fdw_app REVOKE SELECT ON TABLES FROM etl_user;
|
||||
REVOKE SELECT ON ALL TABLES IN SCHEMA fdw_app FROM etl_user;
|
||||
REVOKE USAGE ON SCHEMA fdw_app FROM etl_user;
|
||||
DROP FOREIGN TABLE IF EXISTS fdw_app.member_birthday_manual;
|
||||
DROP SCHEMA IF EXISTS fdw_app CASCADE;
|
||||
DROP USER MAPPING IF EXISTS FOR etl_user SERVER zqyy_app_server; -- 生产
|
||||
-- DROP USER MAPPING IF EXISTS FOR etl_user SERVER test_zqyy_app_server; -- 测试
|
||||
DROP SERVER IF EXISTS zqyy_app_server CASCADE; -- 生产
|
||||
-- DROP SERVER IF EXISTS test_zqyy_app_server CASCADE; -- 测试
|
||||
```
|
||||
|
||||
回滚后 DWS 任务的生日读取将降级为仅使用 `dim_member.birthday`(API 来源),需确保降级逻辑已实现(任务 9.3)。
|
||||
|
||||
## 验证步骤
|
||||
|
||||
```sql
|
||||
-- 1. 确认外部服务器存在
|
||||
SELECT srvname, srvoptions
|
||||
FROM pg_foreign_server
|
||||
WHERE srvname IN ('zqyy_app_server', 'test_zqyy_app_server');
|
||||
-- 预期:1 行(生产或测试环境对应的 server)
|
||||
|
||||
-- 2. 确认用户映射存在
|
||||
SELECT s.srvname, um.umoptions
|
||||
FROM pg_user_mapping um
|
||||
JOIN pg_foreign_server s ON um.umserver = s.oid
|
||||
WHERE s.srvname IN ('zqyy_app_server', 'test_zqyy_app_server');
|
||||
-- 预期:1 行
|
||||
|
||||
-- 3. 确认 fdw_app schema 存在
|
||||
SELECT schema_name
|
||||
FROM information_schema.schemata
|
||||
WHERE schema_name = 'fdw_app';
|
||||
-- 预期:1 行
|
||||
|
||||
-- 4. 确认外部表列结构完整
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'fdw_app'
|
||||
AND table_name = 'member_birthday_manual'
|
||||
ORDER BY ordinal_position;
|
||||
-- 预期:8 列
|
||||
|
||||
-- 5. 确认外部表可读取(需业务库侧表已存在且网络连通)
|
||||
SELECT COUNT(*) FROM fdw_app.member_birthday_manual;
|
||||
-- 预期:返回行数(可能为 0)
|
||||
```
|
||||
|
||||
## 关联文件
|
||||
|
||||
- 生产环境脚本:`db/fdw/setup_fdw_reverse.sql`
|
||||
- 测试环境脚本:`db/fdw/setup_fdw_reverse_test.sql`
|
||||
- 源表迁移脚本:`db/zqyy_app/migrations/2026-02-22__C2_member_birthday_manual.sql`
|
||||
- 源表文档:`docs/database/BD_Manual_member_birthday_manual.md`
|
||||
- 现有正向 FDW:`db/fdw/setup_fdw.sql`(zqyy_app → etl_feiqiu 方向)
|
||||
- 需求文档:`.kiro/specs/etl-aggregation-fix/requirements.md` — 需求 5.3
|
||||
56
docs/database/_archived/BD_Manual_fix_bc_sentinel_dates.md
Normal file
56
docs/database/_archived/BD_Manual_fix_bc_sentinel_dates.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# BD 手册:DWD 层 BC 哨兵日期修复
|
||||
|
||||
## 概述
|
||||
|
||||
上游飞球 API 对"未设置"的日期字段返回哨兵值 `0001-01-01T00:00:00`。该值在 ODS 层以 `timestamp without time zone` 存储,转入 DWD 层 `timestamp with time zone` 列时,PostgreSQL 在 `Asia/Shanghai` 时区下将其转换为 `0001-12-31 23:59:43 BC`(公元前日期)。psycopg2 无法解析 BC 日期,导致 `ValueError: year -1 is out of range`。
|
||||
|
||||
## 受影响对象
|
||||
|
||||
| Schema | 表 | 列 | ODS 源列类型 | DWD 列类型 | 受影响行数 |
|
||||
|--------|---|---|---|---|---|
|
||||
| dwd | dim_assistant_ex | birth_date | timestamp | timestamptz | ~1,107 |
|
||||
| dwd | dim_member_card_account_ex | disable_start_time | timestamp | timestamptz | ~18,172 |
|
||||
| dwd | dim_member_card_account_ex | disable_end_time | timestamp | timestamptz | ~18,172 |
|
||||
| dwd | dwd_assistant_service_log_ex | composite_grade_time | timestamp | timestamptz | ~5,297 |
|
||||
| dwd | dwd_recharge_order_ex | revoke_time | timestamp | timestamptz | ~485 |
|
||||
| dwd | dwd_settlement_head_ex | revoke_time | timestamp | timestamptz | ~26,435 |
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 存量数据
|
||||
|
||||
迁移脚本 `2026-02-22__fix_bc_sentinel_dates_to_null.sql`:将所有 `EXTRACT(year) < 1` 的 BC 日期置为 NULL。
|
||||
|
||||
### 增量防御(代码层)
|
||||
|
||||
在 `dwd_load_task.py` 的 SQL 表达式构造中加入哨兵值过滤(阈值 `0002-01-01`):
|
||||
|
||||
1. `_cast_expr`:对 `timestamptz` CAST 包裹 `CASE WHEN (base)::timestamp >= '0002-01-01'::timestamp THEN ... ELSE NULL END`
|
||||
2. `_build_fact_select_exprs`:事实表 `timestamp → timestamptz` 同类型列加过滤
|
||||
3. `_merge_dim_scd2`:维度表 ODS→DWD SELECT 和 DWD 现有数据读取均加过滤
|
||||
|
||||
### 设计决策
|
||||
|
||||
- 阈值选 `0002-01-01` 而非 `0001-01-02`:留出安全余量,公元 1 年的日期在业务上不可能出现
|
||||
- BC 日期 → NULL 而非保留原值:哨兵值本身无业务含义("未设置"),NULL 语义更准确
|
||||
- `(base)::timestamp` 显式 CAST:因为 base 可能是 JSONB `->>'key'` 提取的 text 类型,不能直接与 timestamp 比较
|
||||
|
||||
## 兼容性
|
||||
|
||||
- ETL Connector:v10 验证 19/19 任务全部成功,0 错误
|
||||
- 后端 API / 小程序:无影响
|
||||
- DWS 层:无影响(不引用这些时间列)
|
||||
|
||||
## 回滚
|
||||
|
||||
存量数据回滚不可行(BC 日期是错误数据)。代码回滚需还原 `_cast_expr`、`_build_fact_select_exprs`、`_merge_dim_scd2` 中的哨兵过滤逻辑。
|
||||
|
||||
## 验证
|
||||
|
||||
见 `docs/database/etl_feiqiu_schema_migration.md` 迁移 11 的验证 SQL。
|
||||
|
||||
## 关联文件
|
||||
|
||||
- 迁移脚本:`db/etl_feiqiu/migrations/2026-02-22__fix_bc_sentinel_dates_to_null.sql`
|
||||
- 代码修改:`apps/etl/connectors/feiqiu/tasks/dwd/dwd_load_task.py`
|
||||
- 存量修复脚本(已废弃):`scripts/ops/fix_bc_dates.py`
|
||||
104
docs/database/_archived/BD_Manual_member_birthday_manual.md
Normal file
104
docs/database/_archived/BD_Manual_member_birthday_manual.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# BD 手册:助教手动补录会员生日表(C2)
|
||||
|
||||
## 概述
|
||||
|
||||
为支持助教手动提交客户生日信息(上游 API 未提供时的补充渠道),在 `zqyy_app` / `test_zqyy_app` 业务库中新建 `member_birthday_manual` 表。该表通过 FDW 只读映射供 ETL DWS 任务读取,与 `dim_member.birthday`(API 来源)配合实现生日数据的双源合并。
|
||||
|
||||
## 变更说明
|
||||
|
||||
| 库 | Schema | 表 | 变更类型 | 说明 |
|
||||
|----|--------|---|---------|------|
|
||||
| zqyy_app / test_zqyy_app | public | member_birthday_manual | 新建表 | 助教手动补录的会员生日信息 |
|
||||
|
||||
### 表结构
|
||||
|
||||
| 列名 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | BIGSERIAL | PRIMARY KEY | 自增主键 |
|
||||
| member_id | BIGINT | NOT NULL | 会员 ID |
|
||||
| birthday_value | DATE | NOT NULL | 补录的生日值 |
|
||||
| recorded_by_assistant_id | BIGINT | — | 提交助教 ID |
|
||||
| recorded_by_name | VARCHAR(50) | — | 提交助教姓名 |
|
||||
| recorded_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 提交时间 |
|
||||
| source | VARCHAR(20) | DEFAULT 'assistant' | 数据来源标识 |
|
||||
| site_id | BIGINT | NOT NULL | 门店 ID(多门店隔离,Requirements 13.1) |
|
||||
|
||||
### 约束与索引
|
||||
|
||||
| 名称 | 类型 | 列 | 说明 |
|
||||
|------|------|---|------|
|
||||
| member_birthday_manual_pkey | PRIMARY KEY | id | 主键 |
|
||||
| uk_member_birthday_manual | UNIQUE | (member_id, recorded_by_assistant_id) | 同一助教对同一会员只保留一条记录,支持 UPSERT |
|
||||
| idx_mbd_member | INDEX (btree) | member_id | 加速按会员 ID 查询 |
|
||||
| idx_mbd_site_id | INDEX (btree) | site_id | 加速按门店 ID 查询 |
|
||||
|
||||
## 兼容性
|
||||
|
||||
- **ETL Connector**:后续通过 FDW 反向映射(`fdw_app.member_birthday_manual`)在 ETL 库中只读访问,DWS 任务使用 `COALESCE(手动补录值, API 值)` 合并生日数据
|
||||
- **后端 API**:新增 `POST /member-birthday` 接口执行 UPSERT 写入(任务 9.4)
|
||||
- **小程序**:助教端调用后端 API 提交生日,无直接数据库访问
|
||||
- **现有数据**:新建表,无历史数据影响
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
DROP TABLE IF EXISTS member_birthday_manual CASCADE;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
回滚无数据丢失风险:该表为新建表,回滚前的数据均为手动补录的生日信息,可由助教重新提交。若已建立 FDW 映射,需先在 ETL 库中删除外部表 `fdw_app.member_birthday_manual`。
|
||||
|
||||
## 验证步骤
|
||||
|
||||
```sql
|
||||
-- 1. 确认表存在
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'member_birthday_manual';
|
||||
-- 预期:1 行
|
||||
|
||||
-- 2. 确认唯一约束 uk_member_birthday_manual 存在
|
||||
SELECT conname, contype
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'member_birthday_manual'::regclass
|
||||
AND conname = 'uk_member_birthday_manual';
|
||||
-- 预期:1 行,contype = 'u'
|
||||
|
||||
-- 3. 确认索引 idx_mbd_member 存在
|
||||
SELECT indexname
|
||||
FROM pg_indexes
|
||||
WHERE tablename = 'member_birthday_manual'
|
||||
AND indexname = 'idx_mbd_member';
|
||||
-- 预期:1 行
|
||||
|
||||
-- 4. 确认表注释已设置
|
||||
SELECT obj_description('member_birthday_manual'::regclass, 'pg_class');
|
||||
-- 预期:'助教手动补录的会员生日信息'
|
||||
|
||||
-- 5. 确认列结构完整
|
||||
SELECT column_name, data_type, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'member_birthday_manual'
|
||||
ORDER BY ordinal_position;
|
||||
-- 预期:8 列(id, member_id, birthday_value, recorded_by_assistant_id,
|
||||
-- recorded_by_name, recorded_at, source, site_id)
|
||||
|
||||
-- 6. 确认 site_id 索引存在
|
||||
SELECT indexname
|
||||
FROM pg_indexes
|
||||
WHERE tablename = 'member_birthday_manual'
|
||||
AND indexname = 'idx_mbd_site_id';
|
||||
-- 预期:1 行
|
||||
```
|
||||
|
||||
## 关联文件
|
||||
|
||||
- 迁移脚本:`db/zqyy_app/migrations/2026-02-22__C2_member_birthday_manual.sql`
|
||||
- FDW 反向映射(生产):`db/fdw/setup_fdw_reverse.sql` — 在 etl_feiqiu 中创建 `fdw_app.member_birthday_manual` 外部表
|
||||
- FDW 反向映射(测试):`db/fdw/setup_fdw_reverse_test.sql` — 在 test_etl_feiqiu 中创建外部表
|
||||
- FDW 反向映射文档:`docs/database/BD_Manual_fdw_reverse_member_birthday.md`
|
||||
- 需求文档:`.kiro/specs/etl-aggregation-fix/requirements.md` — 需求 5.1, 5.3
|
||||
- 后续任务:9.3(DWS 任务生日读取优先级)、9.4(后端 API 接口)
|
||||
1478
docs/database/_archived/etl_feiqiu_schema_migration.md
Normal file
1478
docs/database/_archived/etl_feiqiu_schema_migration.md
Normal file
File diff suppressed because it is too large
Load Diff
144
docs/database/_archived/zqyy_app_admin_web_tables.md
Normal file
144
docs/database/_archived/zqyy_app_admin_web_tables.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# zqyy_app — Web 管理后台表结构文档
|
||||
|
||||
## 变更说明
|
||||
|
||||
在 `zqyy_app` 数据库中新增 4 张表,支撑 Web 管理后台的用户认证、任务队列、执行日志和定时调度功能。
|
||||
|
||||
| 操作 | 表名 | 说明 |
|
||||
|------|------|------|
|
||||
| 新增 | admin_users | 管理后台操作员账户,绑定门店 site_id |
|
||||
| 新增 | task_queue | ETL 任务执行队列,支持排序和状态流转 |
|
||||
| 新增 | task_execution_log | 任务执行历史记录,含日志和摘要 |
|
||||
| 新增 | scheduled_tasks | 定时调度任务,支持 5 种调度类型 |
|
||||
|
||||
### 新增字段概览
|
||||
|
||||
#### admin_users
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | SERIAL | PK | 自增主键 |
|
||||
| username | VARCHAR(64) | UNIQUE NOT NULL | 登录用户名 |
|
||||
| password_hash | VARCHAR(256) | NOT NULL | bcrypt 密码哈希 |
|
||||
| display_name | VARCHAR(128) | | 显示名称 |
|
||||
| site_id | BIGINT | NOT NULL | 绑定的门店 ID |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | 是否启用 |
|
||||
| created_at | TIMESTAMPTZ | DEFAULT NOW() | 创建时间 |
|
||||
| updated_at | TIMESTAMPTZ | DEFAULT NOW() | 更新时间 |
|
||||
|
||||
#### task_queue
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | PK, DEFAULT gen_random_uuid() | 队列任务 ID |
|
||||
| site_id | BIGINT | NOT NULL | 门店隔离 |
|
||||
| config | JSONB | NOT NULL | 序列化的 TaskConfig |
|
||||
| status | VARCHAR(20) | NOT NULL, DEFAULT 'pending' | pending/running/success/failed/cancelled |
|
||||
| position | INTEGER | NOT NULL, DEFAULT 0 | 队列排序位置 |
|
||||
| created_at | TIMESTAMPTZ | DEFAULT NOW() | 创建时间 |
|
||||
| started_at | TIMESTAMPTZ | | 开始执行时间 |
|
||||
| finished_at | TIMESTAMPTZ | | 执行完成时间 |
|
||||
| exit_code | INTEGER | | 子进程退出码 |
|
||||
| error_message | TEXT | | 错误信息 |
|
||||
|
||||
#### task_execution_log
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | PK, DEFAULT gen_random_uuid() | 日志 ID |
|
||||
| queue_id | UUID | FK → task_queue(id) | 关联的队列任务(可空) |
|
||||
| site_id | BIGINT | NOT NULL | 门店隔离 |
|
||||
| task_codes | TEXT[] | NOT NULL | 执行的任务编码列表 |
|
||||
| status | VARCHAR(20) | NOT NULL | 执行状态 |
|
||||
| started_at | TIMESTAMPTZ | NOT NULL | 开始时间 |
|
||||
| finished_at | TIMESTAMPTZ | | 结束时间 |
|
||||
| exit_code | INTEGER | | 退出码 |
|
||||
| duration_ms | INTEGER | | 执行时长(毫秒) |
|
||||
| command | TEXT | | 实际执行的 CLI 命令 |
|
||||
| output_log | TEXT | | stdout 完整日志 |
|
||||
| error_log | TEXT | | stderr 日志 |
|
||||
| summary | JSONB | | 执行摘要 |
|
||||
| created_at | TIMESTAMPTZ | DEFAULT NOW() | 记录创建时间 |
|
||||
|
||||
#### scheduled_tasks
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | UUID | PK, DEFAULT gen_random_uuid() | 调度任务 ID |
|
||||
| site_id | BIGINT | NOT NULL | 门店隔离 |
|
||||
| name | VARCHAR(256) | NOT NULL | 调度任务名称 |
|
||||
| task_codes | TEXT[] | NOT NULL | 任务编码列表 |
|
||||
| task_config | JSONB | NOT NULL | 序列化的 TaskConfig |
|
||||
| schedule_config | JSONB | NOT NULL | 序列化的 ScheduleConfig |
|
||||
| enabled | BOOLEAN | DEFAULT TRUE | 是否启用 |
|
||||
| last_run_at | TIMESTAMPTZ | | 上次执行时间 |
|
||||
| next_run_at | TIMESTAMPTZ | | 下次执行时间 |
|
||||
| run_count | INTEGER | DEFAULT 0 | 累计执行次数 |
|
||||
| last_status | VARCHAR(20) | | 上次执行状态 |
|
||||
| created_at | TIMESTAMPTZ | DEFAULT NOW() | 创建时间 |
|
||||
| updated_at | TIMESTAMPTZ | DEFAULT NOW() | 更新时间 |
|
||||
|
||||
### 索引
|
||||
|
||||
| 索引名 | 表 | 列 | 类型 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| idx_admin_users_site | admin_users | site_id | B-tree | 按门店查询用户 |
|
||||
| idx_task_queue_status | task_queue | status | B-tree | 按状态查询队列 |
|
||||
| idx_task_queue_site_position | task_queue | site_id, position | 部分索引 (WHERE status='pending') | 取出待执行任务 |
|
||||
| idx_execution_log_site_started | task_execution_log | site_id, started_at DESC | B-tree | 执行历史列表 |
|
||||
| idx_scheduled_tasks_site | scheduled_tasks | site_id | B-tree | 按门店查询调度 |
|
||||
| idx_scheduled_tasks_next_run | scheduled_tasks | next_run_at | 部分索引 (WHERE enabled=TRUE) | 查询到期调度 |
|
||||
|
||||
### 种子数据
|
||||
|
||||
- 默认管理员:`admin` / `admin123`,site_id=1
|
||||
- 种子脚本:`db/zqyy_app/seeds/admin_web_seed.sql`
|
||||
|
||||
## 兼容性
|
||||
|
||||
- **ETL Connector**:无影响。新表位于 `zqyy_app` 库,ETL 数据仍在 `etl_feiqiu` 库
|
||||
- **后端 API**:新增的 FastAPI 路由将读写这 4 张表,需配置 `zqyy_app` 数据库连接
|
||||
- **小程序**:无影响。小程序通过 FDW 访问 ETL 数据,不涉及管理后台表
|
||||
- **现有 zqyy_app 表**:无影响。新表与现有 users/roles/tasks 等表无外键关联
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
-- 按依赖顺序删除(task_execution_log 引用 task_queue)
|
||||
BEGIN;
|
||||
DROP TABLE IF EXISTS task_execution_log CASCADE;
|
||||
DROP TABLE IF EXISTS task_queue CASCADE;
|
||||
DROP TABLE IF EXISTS scheduled_tasks CASCADE;
|
||||
DROP TABLE IF EXISTS admin_users CASCADE;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
## 验证 SQL
|
||||
|
||||
```sql
|
||||
-- 1. 验证 4 张表均已创建
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name IN ('admin_users', 'task_queue', 'task_execution_log', 'scheduled_tasks')
|
||||
ORDER BY table_name;
|
||||
|
||||
-- 2. 验证索引已创建(应返回 6 条)
|
||||
SELECT indexname, tablename
|
||||
FROM pg_indexes
|
||||
WHERE tablename IN ('admin_users', 'task_queue', 'task_execution_log', 'scheduled_tasks')
|
||||
AND indexname LIKE 'idx_%'
|
||||
ORDER BY tablename, indexname;
|
||||
|
||||
-- 3. 验证默认管理员已插入
|
||||
SELECT id, username, display_name, site_id, is_active
|
||||
FROM admin_users
|
||||
WHERE username = 'admin';
|
||||
|
||||
-- 4. 验证 task_queue 的部分索引条件
|
||||
SELECT indexname, indexdef
|
||||
FROM pg_indexes
|
||||
WHERE indexname = 'idx_task_queue_site_position';
|
||||
|
||||
-- 5. 验证 task_execution_log 的外键约束
|
||||
SELECT conname, conrelid::regclass, confrelid::regclass
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'task_execution_log'::regclass
|
||||
AND contype = 'f';
|
||||
```
|
||||
Reference in New Issue
Block a user