Files
Neo-ZQYY/docs/specs/board-finance-dws-area-refactor/tasks.md
Neo 70324d8542 chore: 文档与 IDE 配置整理
- .kiro/specs/ → docs/specs/(41 个历史需求 spec 迁移,移除 .config.kiro)
- CLAUDE.md 三层拆分:根文件精简 + apps/backend/CLAUDE.md + .claude/commands/
- 新增 /spec-close、/pre-change 两个工作流命令
- DDL 基线刷新(从测试库重新导出 11 个文件,dws 35→38 表,biz 18→21 表)
- BD_Manual → BD_manual 命名统一(48 个文件)
- 修复 3 处文档与数据库不一致(auth.users.status 默认值、scheduled_tasks 字段、RLS 视图数)
- 新增 BD_manual_public_rbac_tables.md(public schema 8 张 RBAC/工作流表)
- 合并 biz.trigger_jobs 文档(10→12 字段,归档独立文档)
- docs/database/README.md 索引更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:02:37 +08:00

290 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation Plan: 财务看板 DWS 区域维度重构
## Overview
将财务看板的优惠数据从全局 DWS 取数改为按区域日粒度预计算,分 4 个阶段实施:基础设施层(共享映射 + DDL→ ETL 层(两个新任务)→ 后端层(查询改造 + 缓存逻辑)→ 收尾联调、DDL 合并、文档、审计)。每个阶段末尾设检查点,确保增量验证。
## Tasks
- [x] 1. 共享区域映射配置与属性测试
- [x] 1.1 创建 `packages/shared/src/neozqyy_shared/area_mapping.py`
- 定义 `AREA_LABEL_MAP` 字典7 个具体区域 → 物理名称列表)
- 定义 `SPECIFIC_AREA_CODES``ALL_AREA_CODES` 常量
- 构建 `_REVERSE_MAP` 反向映射
- 实现 `resolve_area_code(area_name)``get_area_labels(area_code)` 函数
- _Requirements: 1.1, 1.2, 1.3, 1.5_
- [x] 1.2 编写属性测试:区域映射 round-trip
- **Property 1: 区域映射 round-trip**
- 生成器:从 `AREA_LABEL_MAP` 所有值列表中随机选取 `area_name`
- 验证:`resolve_area_code(area_name)` 返回正确 `area_code`,且 `area_name in get_area_labels(result)`
- **验证: 需求 1.1, 1.5**
- [x] 1.3 编写属性测试:未知区域名称返回 None
- **Property 2: 未知区域名称返回 None**
- 生成器:`st.text()` 生成随机字符串,过滤掉已知 area_name
- 验证:`resolve_area_code(unknown_name)` 返回 `None`
- **验证: 需求 1.4**
- [x] 1.4 编写单元测试area_mapping 边界条件
- 测试文件:`tests/test_area_mapping_unit.py`
- 覆盖空字符串、None、大小写敏感、特殊字符、hall/all 的 get_area_labels 返回 None
- _Requirements: 1.4, 1.5_
- [x] 2. DDL创建 dws_finance_area_daily 表与 RLS 视图
- [x] 2.1 编写 DDL 迁移脚本
- 创建 `dws.dws_finance_area_daily` 表(含收入 5 字段、优惠 7 字段、confirmed_income、现金流 8 字段、卡消费 3 字段、充值 3 字段、order_count
- 添加 UNIQUE 约束 `(site_id, stat_date, area_code)`
- 创建 RLS 视图 `v_dws_finance_area_daily``WHERE site_id = current_setting('app.current_site_id')::bigint`
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_
- [x] 3. 检查点 — 基础设施层验证
- 确保 area_mapping 属性测试和单元测试通过:`cd C:\NeoZQYY && pytest tests/test_area_mapping_props.py tests/test_area_mapping_unit.py -v`
- 确保 DDL 迁移脚本语法正确
- ask the user if questions arise.
- [x] 4. ETLDWS_FINANCE_AREA_DAILY 任务与属性测试
- [x] 4.1 创建 `apps/etl/connectors/feiqiu/tasks/dws/finance_area_daily.py`
- 继承 `FinanceBaseTask`,实现 `get_task_code`/`get_target_table`/`get_primary_keys`
- `extract`:从 `dwd_settlement_head` + `dim_table``scd2_is_current=1`)提取当天结算单(`settle_type IN (1,3)`,按 `BUSINESS_DAY_START_HOUR` 切点),同时从 `dws_finance_daily_summary` 提取全局现金流/充值/卡消费
- `transform`:使用 `resolve_area_code` 映射区域,按区域聚合收入和优惠字段,构建 9 行hallA~ktv + hall + all非 all 行现金流/卡消费/充值字段为 0
- `load`delete-before-insert`site_id + stat_date` 删除后插入 9 行,单事务)
- `discount_gift_card` 使用赠送卡消费金额口径(与现有 ETL 一致)
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 8.1, 8.2_
- [x] 4.2 编写属性测试:日粒度行数学恒等式
- **Property 3: 日粒度行数学恒等式**
- 生成器:随机生成结算单列表(金额用 `st.decimals`area_name 从已知+未知混合)
- 验证transform 输出的每行满足 gross_amount = 四项之和、discount_total = 六项之和、confirmed_income = gross - discount
- **验证: 需求 2.1, 2.2, 2.3, 8.3**
- [x] 4.3 编写属性测试:非 all 区域现金流为零
- **Property 4: 非 all 区域现金流/卡消费/充值为零**
- 生成器:随机结算单列表 + 全局现金流数据
- 验证transform 输出中 `area_code ≠ 'all'` 的行,所有现金流/卡消费/充值字段 = 0
- **验证: 需求 2.5**
- [x] 4.4 编写属性测试ETL 输出完整性与聚合正确性
- **Property 5: ETL 输出完整性与聚合正确性**
- 生成器:随机结算单列表
- 验证:输出恰好 9 行all 行收入/优惠 = hallA~ktv 之和hall 行 = hallA~ktv 之和
- **验证: 需求 2.7, 2.8, 8.4**
- [x] 4.5 编写属性测试ETL 幂等性
- **Property 6: ETL 幂等性delete-before-insert**
- 生成器:随机结算单列表
- 验证:对同一输入运行两次 transform两次输出完全相同
- **验证: 需求 3.4**
- [x] 4.6 编写属性测试settle_type 过滤
- **Property 7: settle_type 过滤**
- 生成器:包含不同 settle_type 值的结算单列表
- 验证:仅 `settle_type IN (1, 3)` 的记录影响输出金额
- **验证: 需求 3.6**
- [x] 4.7 编写单元测试ETL transform 边界条件
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_finance_area_daily.py`
- 覆盖discount_gift_card 口径验证、营业日切点边界、未知区域名称处理、空结算单输入
- _Requirements: 3.1, 3.2, 3.9_
- [x] 5. DDL创建 dws_finance_board_cache 表与 RLS 视图
- [x] 5.1 编写 DDL 迁移脚本
- 创建 `dws.dws_finance_board_cache`overview 8 项 + 日期范围 + 指纹 + 元数据)
- 添加 UNIQUE 约束 `(site_id, time_range, area_code)`
- 创建 RLS 视图 `v_dws_finance_board_cache`
- _Requirements: 4.1, 4.2, 4.3, 4.4_
- [x] 6. ETLDWS_FINANCE_BOARD_CACHE 任务与属性测试
- [x] 6.1 创建 `apps/etl/connectors/feiqiu/tasks/dws/finance_board_cache.py`
- 继承 `BaseDwsTask`
- `extract`:遍历 5 个已完成周期 × 9 个区域 = 45 组合,从 `dws_finance_area_daily` 读取日粒度行
- `transform`:实现 `compute_fingerprint`MD5与缓存表对比标记需重算的组合
- `load`:对需重算的组合从日粒度表 SUM 后 upsert 到缓存表(`ON CONFLICT DO UPDATE`
- _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7_
- [x] 6.2 编写属性测试:数据指纹确定性与缓存失效
- **Property 8: 数据指纹确定性与缓存失效**
- 生成器:随机日粒度行列表
- 验证:相同输入产生相同指纹;修改任意行的 gross_amount 或 discount_total 后指纹变化
- **验证: 需求 5.2, 5.3, 5.4**
- [x] 6.3 编写属性测试:当期周期不写入缓存
- **Property 9: 当期周期不写入缓存**
- 生成器:随机 time_range 从 {month, week, quarter}
- 验证ETL 缓存任务不为当期 time_range 写入缓存记录
- **验证: 需求 5.7**
- [x] 6.4 编写单元测试:缓存任务边界条件
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_finance_board_cache.py`
- 覆盖指纹变化检测、空数据处理、upsert 幂等性
- _Requirements: 5.2, 5.3, 5.4_
- [x] 7. 检查点 — ETL 层验证
- 运行 ETL 属性测试:`cd C:\NeoZQYY && pytest tests/test_finance_area_daily_props.py tests/test_finance_board_cache_props.py -v`
- 运行 ETL 单元测试:`cd apps/etl/connectors/feiqiu && pytest tests/unit/test_finance_area_daily.py tests/unit/test_finance_board_cache.py -v`
- 确保 Property 3-9 全部通过
- ask the user if questions arise.
- [x] 8. 后端:改造 fdw_queries.py 查询函数与属性测试
- [x] 8.1 新增/改造 `apps/backend/app/fdw_queries.py` 中的查询函数
- 新增 `get_finance_overview_area(conn, site_id, start_date, end_date, area_code)` — 从 `v_dws_finance_area_daily` 按 area_code 聚合 overview 8 项指标
- 新增 `get_finance_revenue_area(conn, site_id, start_date, end_date, area_code)` — 从 `v_dws_finance_area_daily` 按 area_code 聚合 revenue 板块数据
- 新增 `get_finance_board_cache(conn, site_id, time_range, area_code)` — 查询 `v_dws_finance_board_cache` 缓存
- 新增 `set_finance_board_cache(conn, site_id, time_range, area_code, data)` — 写入/更新缓存
- 使用 `SET LOCAL app.current_site_id` 保证 RLS 隔离
- _Requirements: 6.1, 6.2, 6.3, 6.5, 6.6_
- [x] 8.2 编写属性测试:查询路由正确性
- **Property 10: 查询路由正确性**
- 生成器随机查询参数time_range、area_code+ mock 数据库返回
- 验证:已完成周期+缓存存在→返回缓存缓存不存在→SUM+写缓存;当期→直接 SUM 不查缓存
- **验证: 需求 6.1, 6.2, 6.3, 9.4**
- [x] 8.3 编写属性测试:区域过滤行为
- **Property 11: 区域过滤行为**
- 生成器:随机 area_code ≠ 'all' + mock 数据
- 验证recharge 返回 nullcashflow/expense/coach_analysis 使用全局数据
- **验证: 需求 6.7, 6.8**
- [x] 8.4 编写属性测试revenue 固定项数
- **Property 12: revenue 固定项数**
- 生成器:随机查询参数 + mock 数据
- 验证discount_items 恰好 5 项channel_items 恰好 3 项
- **验证: 需求 7.3, 7.4**
- [x] 8.5 编写属性测试area≠all 时 overview 覆盖逻辑
- **Property 13: area≠all 时 overview 覆盖逻辑**
- 生成器:随机 area_code ≠ 'all' + mock revenue 数据
- 验证overview.occurrence = revenue.total_occurrenceoverview.discount = revenue.discount_totaloverview.confirmed_revenue = revenue.confirmed_total
- **验证: 需求 7.6**
- [x] 8.6 编写单元测试fdw_queries 查询正确性
- 测试文件:`apps/backend/tests/unit/test_fdw_queries_area.py`
- 覆盖SQL 正确性、area_code 过滤、缓存命中/未命中、RLS 隔离
- _Requirements: 6.1, 6.2, 6.5, 6.6_
- [x] 9. 后端:改造 board_service.py 缓存查询逻辑
- [x] 9.1 改造 `apps/backend/app/board_service.py``get_finance_board` 函数
- 新增缓存查询逻辑:已完成周期先查 `get_finance_board_cache`,命中直接返回
- 缓存未命中:从日粒度表 SUM 计算,写入缓存后返回
- 当期周期:直接从日粒度表 SUM不查缓存
- `_build_overview` 改为调用 `get_finance_overview_area`(传入 area_code
- `_build_revenue` 改为调用 `get_finance_revenue_area`(传入 area_code
- `_build_cashflow` 不变(始终用全局数据)
- `area≠all` 时 overview 覆盖逻辑保留occurrence/discount/confirmedRevenue = revenue 对应值)
- `area≠all` 时 recharge 返回 null
- compare=1 时对上期执行同样缓存/日粒度逻辑
- _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 7.1, 7.2, 7.5, 7.6_
- [x] 9.2 编写属性测试area=all 回归一致性
- **Property 14: area=all 回归一致性**
- 生成器:随机日期范围 + mock 新旧逻辑数据
- 验证area=all 时新逻辑的 overview 8 项指标与旧逻辑完全一致
- **验证: 需求 9.1**
- [x] 9.3 编写单元测试board_service 改造
- 测试文件:`apps/backend/tests/unit/test_board_service_area.py`
- 覆盖:缓存命中/未命中路径、覆盖逻辑、环比计算、降级行为(无数据返回全零)
- _Requirements: 6.1, 6.2, 6.3, 7.6, 9.1_
- [x] 10. 检查点 — 后端层验证
- 运行后端属性测试:`cd C:\NeoZQYY && pytest tests/test_board_service_props.py -v`
- 运行后端单元测试:`cd apps/backend && pytest tests/unit/test_fdw_queries_area.py tests/unit/test_board_service_area.py -v`
- 确保 Property 10-14 全部通过
- ask the user if questions arise.
- [x] 11. 历史数据回填脚本
- [x] 11.1 编写回填脚本 `scripts/ops/backfill_finance_area_daily.py`
- 对已有日期范围批量调用 `FinanceAreaDailyTask.transform` 逻辑
- 支持指定 site_id 和日期范围参数
- 回填完成后触发 `DWS_FINANCE_BOARD_CACHE` 重算所有已完成周期缓存
- _Requirements: 2.7, 3.4, 5.1_
- [x] 12. 前后端联调与集成验证
- [x] 12.1 启动后端服务,使用测试库验证各端点完整请求-响应链路
- 使用真实 FDW 连接验证 SQL 查询正确性
- 验证 JSON 响应结构与 Schema 定义一致camelCase 序列化)
- 验证数据隔离(`SET LOCAL app.current_site_id`)在真实请求中生效
- _Requirements: 7.1, 7.2, 9.1_
- [x] 12.2 运行 144 组合全量验证脚本
- 执行 `scripts/ops/validate_board_finance.py`8 time_range × 9 area_code × 2 compare
- 确认 area=all 时所有板块数据与重构前完全一致(回归测试)
- 确认 area≠all 时 discountRate 不出现 400%+ 异常值
- 确认已完成周期第二次请求命中缓存
- _Requirements: 9.1, 9.2, 9.3, 9.4_
- [x] 12.3 前端联调验证
- 确认小程序财务看板页能正确调用 API 并渲染数据(前端零改动)
- 验证空数据/降级场景下前端不崩溃
- 如前端页面尚未开发,记录待联调清单供后续任务使用
- _Requirements: 7.1, 7.2_
- [x] 13. 数据库变更审计与 DDL 合并
- [x] 13.1 审计本次实现中对数据库的所有改动
- 检查新建表dws_finance_area_daily、dws_finance_board_cache、RLS 视图、FDW 映射变更
- _Requirements: 2.6, 4.1_
- [x] 13.2 执行迁移脚本到测试库
- 验证新表和索引已正确创建(使用 BD 手册中的验证 SQL
- _Requirements: 2.6, 4.1_
- [x] 13.3 合并到主 DDL 基线文件
- ETL 库 → `docs/database/ddl/etl_feiqiu__dws.sql`
- FDW → `db/fdw/` 对应文件
- _Requirements: 2.6, 4.1_
- [x] 13.4 编写回滚脚本(逆序 DROP TABLE/VIEW
- _Requirements: 2.6, 4.1_
- [x] 14. BD 手册更新
- [x] 14.1 创建 BD 手册
- ETL 库 → `apps/etl/connectors/feiqiu/docs/database/dws/main/BD_manual_dws_finance_area_daily.md`
- ETL 库 → `apps/etl/connectors/feiqiu/docs/database/dws/main/BD_manual_dws_finance_board_cache.md`
- FDW → `docs/database/BD_Manual_fdw_finance_area.md`
- 每份手册包含:字段明细、约束与索引、验证 SQL≥3 条)、兼容性影响、回滚策略
- 记录变更原因、影响范围
- _Requirements: 2.1, 2.6, 4.1, 4.4_
- [x] 15. 文档同步更新
- [x] 15.1 更新 ETL 任务文档
-`docs/etl_tasks/` 新增 DWS_FINANCE_AREA_DAILY 和 DWS_FINANCE_BOARD_CACHE 任务文档
- _Requirements: 3.7, 3.8, 5.5, 5.6_
- [x] 15.2 更新后端 README
-`apps/backend/README.md` 更新 board_service 和 fdw_queries 模块摘要
- _Requirements: 6.5, 6.6_
- [x] 15.3 更新文档地图
-`docs/DOCUMENTATION-MAP.md` 新增本次模块条目BD 手册、ETL 任务文档)
- _Requirements: 2.6, 4.1_
- [x] 16. 变更审计收口
- [x] 16.1 触发审计子代理audit-writer执行完整审计流程
- 确认 `.kiro/state/.audit_state.json``audit_required` 已标记
- 审计子代理自动完成变更审计记录docs/audit/changes/、AI_CHANGELOG、CHANGE 标记注释
- 高风险路径:`tasks/`ETL`apps/backend/app/`(后端)、`packages/shared/`(共享包)、`db/`(数据库)
- _Requirements: 全部_
- [x] 16.2 验证审计产物完整性
- 确认 `docs/audit/changes/<YYYY-MM-DD>__board-finance-dws-area-refactor.md` 已生成
- 确认涉及的高风险文件均有 AI_CHANGELOG 条目
- 确认逻辑变更处有 CHANGE 标记注释含日期、Prompt、直接原因
- _Requirements: 全部_
- [x] 16.3 刷新审计一览表
- 执行 `python scripts/audit/gen_audit_dashboard.py`
- 确认新记录出现在 `docs/audit/audit_dashboard.md`
- _Requirements: 全部_
- [x] 17. 最终检查点 — 全量验证
- 运行 Monorepo 属性测试:`cd C:\NeoZQYY && pytest tests/ -v`
- 运行 ETL 单元测试:`cd apps/etl/connectors/feiqiu && pytest tests/unit -v`
- 运行后端单元测试:`cd apps/backend && pytest tests/ -v`
- 确保所有属性测试Property 1-14和单元测试全部通过
- 确保 DDL 迁移已合并到主基线
- 确保 BD 手册已同步更新
- 确保后端 README、文档地图均已更新
- 确保变更审计记录已生成、AI_CHANGELOG 已写入、审计一览表已刷新
- ask the user if questions arise.
## 备注
- 标记 `*` 的子任务为可选(属性测试/单元测试),可跳过以加速 MVP
- 每个任务引用了具体的需求编号以确保可追溯性
- 属性测试验证通用正确性属性Property 1-14单元测试验证具体边界条件
- 检查点任务确保增量验证,避免问题累积
- 本 spec 为跨系统类ETL + 后端 + DB + 共享包),收尾阶段覆盖步骤 1-6
- 设计文档使用 Python所有实现和测试均使用 Python