## P1 数据库基础 - zqyy_app: 创建 auth/biz schema、FDW 连接 etl_feiqiu - etl_feiqiu: 创建 app schema RLS 视图、商品库存预警表 - 清理 assistant_abolish 残留数据 ## P2 ETL/DWS 扩展 - 新增 DWS 助教订单贡献度表 (dws.assistant_order_contribution) - 新增 assistant_order_contribution_task 任务及 RLS 视图 - member_consumption 增加充值字段、assistant_daily 增加处罚字段 - 更新 ODS/DWD/DWS 任务文档及业务规则文档 - 更新 consistency_checker、flow_runner、task_registry 等核心模块 ## P3 小程序鉴权系统 - 新增 xcx_auth 路由/schema(微信登录 + JWT) - 新增 wechat/role/matching/application 服务层 - zqyy_app 鉴权表迁移 + 角色权限种子数据 - auth/dependencies.py 支持小程序 JWT 鉴权 ## 文档与审计 - 新增 DOCUMENTATION-MAP 文档导航 - 新增 7 份 BD_Manual 数据库变更文档 - 更新 DDL 基线快照(etl_feiqiu 6 schema + zqyy_app auth) - 新增全栈集成审计记录、部署检查清单更新 - 新增 BACKLOG 路线图、FDW→Core 迁移计划 ## Kiro 工程化 - 新增 5 个 Spec(P1/P2/P3/全栈集成/核心业务) - 新增审计自动化脚本(agent_on_stop/build_audit_context/compliance_prescan) - 新增 6 个 Hook(合规检查/会话日志/提交审计等) - 新增 doc-map steering 文件 ## 运维与测试 - 新增 ops 脚本:迁移验证/API 健康检查/ETL 监控/集成报告 - 新增属性测试:test_dws_contribution / test_auth_system - 清理过期 export 报告文件 - 更新 .gitignore 排除规则
11 KiB
11 KiB
SCD2 缓慢变化维处理规则
本文档定义 dwd 模式下维度表的 SCD2(Slowly Changing Dimension Type 2)处理策略、
生效区间管理和版本控制规则。
1. 概述
1.1 什么是 SCD2
SCD2 通过保留维度记录的历史版本来追踪属性变化。当被跟踪字段发生变更时:
- 关闭当前版本(设置结束时间、标记为非当前)
- 插入新版本(设置开始时间、标记为当前)
1.2 实现模块
- 处理器:
tasks/dwd/dwd_load_task.py—_merge_dim_scd2()方法 - 变更检测:
_is_row_changed()— 比较所有非 SCD2 控制列,任一列值不同即视为变更 - 批量关闭:
_close_current_dim_bulk()— 批量设置旧版本的scd2_end_time和scd2_is_current = 0 - 批量插入:
_insert_dim_rows_bulk()— 批量插入新版本行
1.3 变更检测逻辑
_is_row_changed(current, incoming, dwd_cols) 遍历目标表的所有列(排除 SCD2 控制列),逐列比较当前版本与新数据。比较时会进行类型归一化处理:
- 空值归一化:
None、空字符串、"null"视为等价 - 数值归一化:字符串形式的数字与
Decimal/int比较前先转换 - 布尔归一化:
"true"/"1"/"yes"等与True视为等价 - 日期归一化:字符串形式的日期与
datetime比较前先解析
2. SCD2 元数据字段
所有维度表统一包含以下 SCD2 控制字段:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
scd2_start_time |
TIMESTAMPTZ |
now() |
版本生效起始时间 |
scd2_end_time |
TIMESTAMPTZ |
'9999-12-31' |
版本失效时间(9999-12-31 表示当前有效) |
scd2_is_current |
INT |
1 |
当前版本标记(1 = 当前,0 = 历史) |
scd2_version |
INT |
1 |
版本号(自增) |
约束
- 主键:
(natural_key, scd2_start_time)— 同一自然键的不同版本通过生效时间区分 - 唯一索引:
WHERE scd2_is_current = 1— 保证每个自然键只有一条当前记录
3. 处理流程
_merge_dim_scd2(cur, dwd_table, ods_table, dwd_cols, ods_cols, now)
│
├── 1. 从 ODS 取最新有效版本(DISTINCT ON + is_delete IS DISTINCT FROM 1)
│
├── 2. 从 DWD 取当前版本(scd2_is_current = 1)
│
├── 3. 按自然键逐条比较:
│ │
│ ├── DWD 中不存在 → 收集为待插入(INSERT)
│ │
│ ├── 存在但 _is_row_changed() 返回 True → 收集为待更新
│ │ ├── 关闭旧版本(scd2_end_time = now, scd2_is_current = 0)
│ │ └── 插入新版本(scd2_start_time = now, scd2_is_current = 1, version + 1)
│ │
│ └── 存在且无变化 → 跳过(UNCHANGED)
│
├── 4. _close_current_dim_bulk() — 批量关闭旧版本
│
└── 5. _insert_dim_rows_bulk() — 批量插入新版本
4. 维度表 SCD2 配置
跟踪字段 = 表中除自然键和 SCD2 控制列(
scd2_start_time/scd2_end_time/scd2_is_current/scd2_version)之外的所有列。任一跟踪字段值变化即触发新版本。
4.1 门店维度(dim_site / dim_site_ex)
- Schema:
dwd - ODS 来源:
ods.table_fee_transactions(从台费流水中的siteProfile快照提取) - 自然键:
site_id - dim_site 跟踪字段:
org_id、tenant_id、shop_name、site_label、full_address、address、longitude、latitude、tenant_site_region_id、business_tel、site_type、shop_status - dim_site_ex 跟踪字段:
avatar、address、longitude、latitude、tenant_site_region_id、auto_light、light_status、light_type、light_token、site_type、site_label、attendance_enabled、attendance_distance、customer_service_qrcode、customer_service_wechat、fixed_pay_qrcode、prod_env、shop_status、create_time、update_time - 变更触发场景:门店名称/地址/状态/经纬度等基础信息变更
4.2 台桌维度(dim_table / dim_table_ex)
- Schema:
dwd - ODS 来源:
ods.site_tables_master - 自然键:
table_id - dim_table 跟踪字段:
site_id、table_name、site_table_area_id、site_table_area_name、tenant_table_area_id、table_price、order_id - dim_table_ex 跟踪字段:
show_status、is_online_reservation、table_cloth_use_time、table_cloth_use_cycle、table_status、create_time、light_status、tablestatusname、sitename、applet_qr_code_url、audit_status、charge_free、delay_lights_time、is_rest_area、only_allow_groupon、order_delay_time、self_table、temporary_light_second、virtual_table - 变更触发场景:台桌名称/区域/价格/状态变更
4.3 助教维度(dim_assistant / dim_assistant_ex)
- Schema:
dwd - ODS 来源:
ods.assistant_accounts_master - 自然键:
assistant_id - dim_assistant 跟踪字段:
user_id、assistant_no、real_name、nickname、mobile、tenant_id、site_id、team_id、team_name、level、entry_time、resign_time、leave_status、assistant_status - dim_assistant_ex 跟踪字段:
gender、birth_date、avatar、introduce、video_introduction_url、height、weight、shop_name、group_id、group_name、person_org_id、staff_id、staff_profile_id、assistant_grade、sum_grade、get_grade_times、charge_way、allow_cx、is_guaranteed、salary_grant_enabled、entry_type、entry_sign_status、resign_sign_status、work_status、show_status、show_sort、online_status、is_delete、criticism_status、create_time、update_time、start_time、end_time、last_table_id、last_table_name、last_update_name、order_trade_no、ding_talk_synced、site_light_cfg_id、light_equipment_id、light_status、is_team_leader、serial_number、system_role_id、job_num、cx_unit_price、pd_unit_price - 变更触发场景:助教等级/团队/状态/入职离职/评分等变更
4.4 会员维度(dim_member / dim_member_ex)
- Schema:
dwd - ODS 来源:
ods.member_profiles - 自然键:
member_id - dim_member 跟踪字段:
system_member_id、tenant_id、register_site_id、mobile、nickname、member_card_grade_code、member_card_grade_name、create_time、update_time、pay_money_sum、recharge_money_sum、birthday - dim_member_ex 跟踪字段:
referrer_member_id、point、register_site_name、growth_value、user_status、status、person_tenant_org_id、person_tenant_org_name、register_source - 变更触发场景:会员昵称/手机号/卡等级/累计消费充值/状态等变更
4.5 会员卡账户维度(dim_member_card_account / dim_member_card_account_ex)
- Schema:
dwd - ODS 来源:
ods.member_stored_value_cards - 自然键:
member_card_id - dim_member_card_account 跟踪字段:
tenant_id、register_site_id、tenant_member_id、system_member_id、card_type_id、member_card_grade_code、member_card_grade_code_name、member_card_type_name、member_name、member_mobile、balance、start_time、end_time、last_consume_time、status、is_delete、principal_balance、member_grade - dim_member_card_account_ex 跟踪字段:(60+ 列,含各类折扣比例、抵扣开关等,详见 DDL)
- 变更触发场景:卡余额/状态/折扣配置/有效期等变更
4.6 商品维度
租户商品(dim_tenant_goods / dim_tenant_goods_ex)
- ODS 来源:
ods.tenant_goods_master - 自然键:
tenant_goods_id - dim_tenant_goods 跟踪字段:
tenant_id、supplier_id、category_name、goods_category_id、goods_second_category_id、goods_name、goods_number、unit、market_price、goods_state、create_time、update_time、is_delete、not_sale
门店商品(dim_store_goods / dim_store_goods_ex)
- ODS 来源:
ods.store_goods_master - 自然键:
site_goods_id - dim_store_goods 跟踪字段:
tenant_id、site_id、tenant_goods_id、goods_name、goods_category_id、goods_second_category_id、category_level1_name、category_level2_name、batch_stock_qty、sale_qty、total_sales_qty、sale_price、created_at、updated_at、avg_monthly_sales、goods_state、enable_status、send_state、is_delete、commodity_code、not_sale
4.7 商品分类维度(dim_goods_category)
- ODS 来源:
ods.stock_goods_category_tree - 自然键:
category_id - 跟踪字段:
tenant_id、category_name、alias_name、parent_category_id、business_name、tenant_goods_business_id、category_level、is_leaf、open_salesman、sort_order、is_warehousing - 变更触发场景:分类名称/层级/排序/启用状态变更
4.8 团购套餐维度(dim_groupbuy_package / dim_groupbuy_package_ex)
- ODS 来源:
ods.group_buy_packages - 自然键:
groupbuy_package_id - dim_groupbuy_package 跟踪字段:
tenant_id、site_id、package_name、package_template_id、selling_price、coupon_face_value、duration_seconds、start_time、end_time、table_area_name、is_enabled、is_delete、create_time、tenant_table_area_id_list、card_type_ids、sort、is_first_limit - 变更触发场景:套餐名称/价格/面值/有效期/启用状态变更
4.9 员工维度(dim_staff / dim_staff_ex)
- ODS 来源:
ods.staff_info_master - 自然键:
staff_id - dim_staff 跟踪字段:
staff_name、alias_name、mobile、gender、job、tenant_id、site_id、system_role_id、staff_identity、status、leave_status、entry_time、resign_time、is_delete - dim_staff_ex 跟踪字段:
avatar、job_num、account_status、rank_id、rank_name、new_rank_id、new_staff_identity、is_reserve、shop_name、site_label、tenant_org_id、system_user_id、cashier_point_id、cashier_point_name、group_id、group_name、staff_profile_id、auth_code、auth_code_create、ding_talk_synced、salary_grant_enabled、entry_type、entry_sign_status、resign_sign_status、criticism_status、create_time、user_roles - 变更触发场景:员工姓名/岗位/角色/状态/入职离职等变更
5. 查询约定
获取当前有效记录
SELECT * FROM dwd.dim_member
WHERE scd2_is_current = 1;
获取某时间点的历史快照
SELECT * FROM dwd.dim_member
WHERE scd2_start_time <= '2025-06-01'
AND scd2_end_time > '2025-06-01';
获取某记录的完整变更历史
SELECT * FROM dwd.dim_member
WHERE member_id = 12345
ORDER BY scd2_start_time;
6. 注意事项
- 时区:
scd2_start_time/scd2_end_time使用TIMESTAMPTZ,统一以Asia/Shanghai时区存储 - 并发安全:当前实现在单次 ETL 运行内串行处理,未做行级锁;并发写入需额外保护
- 删除策略:维度记录不做物理删除,仅通过关闭版本(
scd2_is_current = 0)标记失效 - ODS 来源过滤:从 ODS 取数时统一使用
DISTINCT ON (natural_key) ... WHERE is_delete IS DISTINCT FROM 1 ORDER BY natural_key, fetched_at DESC,确保取最新有效版本
维护约定
- 新增维度表时,须在本文档添加对应章节
- 跟踪字段变更时,须同步更新文档并评估历史数据影响
- 文档统一 UTF-8 编码,中文撰写