6.6 KiB
6.6 KiB
需求文档
简介
v8 联调修复了 11 个 BUG,其中 4 个的当前修复方式是"临时止血",需要更完整的方案。本 Spec 覆盖以下四个需求:
- 需求 A:助教月度聚合按档位分段统计,替代
MAX()聚合 - 需求 B:多门店会员查询
register_site_id已知限制标记 + 预留扩展方案 - 需求 C:会员生日字段全链路补齐(C1: ETL 链路 / C2: 手动补录)
- 需求 D:
DwdLoadTask.load()返回值格式规范化
术语表
- DwdLoadTask:DWD 层装载任务,负责将 ODS 原始数据清洗装载至 DWD 明细层
- DWS 任务:数据汇总层任务,从 DWD 层聚合生成业务报表数据
- SCD2:缓慢变化维度类型 2,通过版本化记录维度属性的历史变化
- BaseTask:ETL 任务基类,提供 Extract/Transform/Load 模板方法和计数累加逻辑
- FlowRunner:ETL 流程编排器,按层级顺序执行任务并汇总计数
- dim_member:DWD 层会员维度表,使用 SCD2 管理历史版本
- dws_assistant_monthly_summary:DWS 层助教月度汇总表
- dws_assistant_salary_calc:DWS 层助教工资计算表
- register_site_id:会员注册门店 ID,当前
dim_member中唯一的门店标识 - assistant_level_code:助教档位代码,助教月内可能因升级/降级而变化
- dim_member_birthday_manual:手动补录生日表(位于
zqyy_app业务库),存储助教提交的会员生日信息,通过 FDW 只读映射供 ETL DWS 任务读取 - _safe_int():
flow_runner.py中的类型安全辅助函数,将int/list/None统一转为int - _accumulate_counts():
BaseTask中的计数累加方法,合并多段执行的统计结果
需求
需求 1:DwdLoadTask 返回值格式规范化(需求 D)
用户故事: 作为 ETL 开发者,我希望 DwdLoadTask.load() 的返回值格式与其他任务保持一致,以便 FlowRunner 能安全地汇总所有任务的计数。
验收标准
- WHEN DwdLoadTask.load() 执行完成, THE DwdLoadTask SHALL 返回包含
errors键(值为int类型,等于错误列表长度)和error_details键(值为list[dict]类型,包含错误详情)的字典 - WHEN BaseTask._accumulate_counts() 遇到值为
list类型的计数项, THE BaseTask SHALL 将该值转换为len(list)后再累加(防御层) - WHEN FlowRunner 汇总所有任务计数, THE FlowRunner SHALL 保留
_safe_int()作为最终防御层,确保sum()不会因类型不一致而崩溃
需求 2:助教月度聚合按档位分段统计(需求 A)
用户故事: 作为运营管理者,我希望助教月度汇总按档位分段统计业绩,以便准确反映助教在不同档位期间的表现和工资计算。
验收标准
- WHEN 助教在同一月内存在多个 assistant_level_code, THE AssistantMonthlyTask SHALL 按
(assistant_id, stat_month, assistant_level_code)分组生成多行记录,分别统计各档位的业绩指标 - THE dws_assistant_monthly_summary 表 SHALL 使用
(site_id, assistant_id, stat_month, assistant_level_code)作为唯一约束 - WHEN AssistantMonthlyTask 需要取 nickname 值, THE AssistantMonthlyTask SHALL 按时间倒序取最后一条记录的 nickname,而非使用
MAX()聚合 - WHEN AssistantSalaryTask 计算工资, THE AssistantSalaryTask SHALL 按档位分段计算抽成,适配新的多行月度汇总结构
- WHEN AssistantFinanceTask 提取日度收入需要 nickname, THE AssistantFinanceTask SHALL 按时间倒序取最后一条记录的 nickname,而非使用
MAX()聚合 - WHEN AssistantCustomerTask 提取服务对需要 nickname, THE AssistantCustomerTask SHALL 按时间倒序取最后一条记录的 nickname,而非使用
MAX()聚合
需求 3:多门店会员查询支持(需求 B)
用户故事: 作为运营管理者,我希望 DWS 任务能正确查询跨店消费的会员信息,以便 B 店能看到在 A 店注册但在 B 店消费的会员维度数据。
验收标准
- WHEN DWS 任务需要查询会员信息, THE DWS 任务 SHALL 通过事实表中的
member_id反查dim_member,而非使用WHERE register_site_id = %s预筛选 - WHEN 会员在 A 店注册并在 B 店消费, THE B 店的 DWS 任务 SHALL 能查询到该会员的昵称、手机号等维度信息
- WHEN DWS 任务执行会员信息提取, THE DWS 任务 SHALL 使用
WHERE member_id IN (SELECT DISTINCT member_id FROM dwd.事实表 WHERE site_id = %s)模式获取会员维度数据
需求 4:会员生日字段 ETL 链路补齐(需求 C1)
用户故事: 作为运营管理者,我希望会员生日信息能从上游 API 完整传递到 DWS 层,以便用于会员分析和销售线索。
验收标准
- THE dim_member 表 SHALL 包含
birthday DATE列 - WHEN DwdLoadTask 执行 ODS → DWD 装载, THE DwdLoadTask SHALL 在列映射中包含
birthday字段,将 ODS 中的生日数据装载到dim_member.birthday - WHEN SCD2 更新 dim_member 记录, THE DwdLoadTask SHALL 将
birthday作为变化检测字段之一,正常处理生日值的变化 - WHEN MemberVisitTask 等 DWS 任务提取会员信息, THE DWS 任务 SHALL 从
dim_member.birthday读取生日字段并写入 DWS 目标表
需求 5:助教手动补录会员生日(需求 C2)
用户故事: 作为助教,我希望能手动提交客户的生日信息,以便在上游 API 未提供生日数据时补充这一重要销售线索。
验收标准
- THE
zqyy_app业务库(开发/测试环境使用test_zqyy_app)SHALL 包含member_birthday_manual表,结构包含member_id、birthday_value、recorded_by_assistant_id、recorded_by_name、recorded_at、source字段,唯一约束为(member_id, recorded_by_assistant_id) - WHEN 同一助教对同一会员重复提交生日, THE 系统 SHALL 更新该助教的已有记录(UPSERT),保留所有其他助教的提交记录
- WHEN DWS 任务需要读取手动补录生日, THE DWS 任务 SHALL 通过 FDW 只读映射从
zqyy_app.member_birthday_manual读取数据 - WHEN dim_member.birthday(API 来源)和 member_birthday_manual(手动来源)同时存在, THE DWS 任务 SHALL 优先使用手动补录值
- WHEN 助教通过后端 API 提交生日, THE 后端 SHALL 提供 POST 接口接收
member_id、birthday_value、assistant_id、assistant_name参数,执行 UPSERT 写入zqyy_app.member_birthday_manual - WHEN SCD2 更新 dim_member.birthday, THE DwdLoadTask SHALL 正常更新 API 来源的生日值,不影响业务库中手动补录表的数据