Files
Neo-ZQYY/apps/etl/connectors/feiqiu/docs/business-rules/scd2_rules.md
Neo b25308c3f4 feat: P1-P3 全栈集成 — 数据库基础 + DWS 扩展 + 小程序鉴权 + 工程化体系
## 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 排除规则
2026-02-26 08:03:53 +08:00

11 KiB
Raw Blame History

SCD2 缓慢变化维处理规则

本文档定义 dwd 模式下维度表的 SCD2Slowly Changing Dimension Type 2处理策略、 生效区间管理和版本控制规则。


1. 概述

1.1 什么是 SCD2

SCD2 通过保留维度记录的历史版本来追踪属性变化。当被跟踪字段发生变更时:

  1. 关闭当前版本(设置结束时间、标记为非当前)
  2. 插入新版本(设置开始时间、标记为当前)

1.2 实现模块

  • 处理器:tasks/dwd/dwd_load_task.py_merge_dim_scd2() 方法
  • 变更检测:_is_row_changed() — 比较所有非 SCD2 控制列,任一列值不同即视为变更
  • 批量关闭:_close_current_dim_bulk() — 批量设置旧版本的 scd2_end_timescd2_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

  • Schemadwd
  • ODS 来源:ods.table_fee_transactions(从台费流水中的 siteProfile 快照提取)
  • 自然键:site_id
  • dim_site 跟踪字段:org_idtenant_idshop_namesite_labelfull_addressaddresslongitudelatitudetenant_site_region_idbusiness_telsite_typeshop_status
  • dim_site_ex 跟踪字段:avataraddresslongitudelatitudetenant_site_region_idauto_lightlight_statuslight_typelight_tokensite_typesite_labelattendance_enabledattendance_distancecustomer_service_qrcodecustomer_service_wechatfixed_pay_qrcodeprod_envshop_statuscreate_timeupdate_time
  • 变更触发场景:门店名称/地址/状态/经纬度等基础信息变更

4.2 台桌维度dim_table / dim_table_ex

  • Schemadwd
  • ODS 来源:ods.site_tables_master
  • 自然键:table_id
  • dim_table 跟踪字段:site_idtable_namesite_table_area_idsite_table_area_nametenant_table_area_idtable_priceorder_id
  • dim_table_ex 跟踪字段:show_statusis_online_reservationtable_cloth_use_timetable_cloth_use_cycletable_statuscreate_timelight_statustablestatusnamesitenameapplet_qr_code_urlaudit_statuscharge_freedelay_lights_timeis_rest_areaonly_allow_grouponorder_delay_timeself_tabletemporary_light_secondvirtual_table
  • 变更触发场景:台桌名称/区域/价格/状态变更

4.3 助教维度dim_assistant / dim_assistant_ex

  • Schemadwd
  • ODS 来源:ods.assistant_accounts_master
  • 自然键:assistant_id
  • dim_assistant 跟踪字段:user_idassistant_noreal_namenicknamemobiletenant_idsite_idteam_idteam_namelevelentry_timeresign_timeleave_statusassistant_status
  • dim_assistant_ex 跟踪字段:genderbirth_dateavatarintroducevideo_introduction_urlheightweightshop_namegroup_idgroup_nameperson_org_idstaff_idstaff_profile_idassistant_gradesum_gradeget_grade_timescharge_wayallow_cxis_guaranteedsalary_grant_enabledentry_typeentry_sign_statusresign_sign_statuswork_statusshow_statusshow_sortonline_statusis_deletecriticism_statuscreate_timeupdate_timestart_timeend_timelast_table_idlast_table_namelast_update_nameorder_trade_noding_talk_syncedsite_light_cfg_idlight_equipment_idlight_statusis_team_leaderserial_numbersystem_role_idjob_numcx_unit_pricepd_unit_price
  • 变更触发场景:助教等级/团队/状态/入职离职/评分等变更

4.4 会员维度dim_member / dim_member_ex

  • Schemadwd
  • ODS 来源:ods.member_profiles
  • 自然键:member_id
  • dim_member 跟踪字段:system_member_idtenant_idregister_site_idmobilenicknamemember_card_grade_codemember_card_grade_namecreate_timeupdate_timepay_money_sumrecharge_money_sumbirthday
  • dim_member_ex 跟踪字段:referrer_member_idpointregister_site_namegrowth_valueuser_statusstatusperson_tenant_org_idperson_tenant_org_nameregister_source
  • 变更触发场景:会员昵称/手机号/卡等级/累计消费充值/状态等变更

4.5 会员卡账户维度dim_member_card_account / dim_member_card_account_ex

  • Schemadwd
  • ODS 来源:ods.member_stored_value_cards
  • 自然键:member_card_id
  • dim_member_card_account 跟踪字段:tenant_idregister_site_idtenant_member_idsystem_member_idcard_type_idmember_card_grade_codemember_card_grade_code_namemember_card_type_namemember_namemember_mobilebalancestart_timeend_timelast_consume_timestatusis_deleteprincipal_balancemember_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_idsupplier_idcategory_namegoods_category_idgoods_second_category_idgoods_namegoods_numberunitmarket_pricegoods_statecreate_timeupdate_timeis_deletenot_sale

门店商品dim_store_goods / dim_store_goods_ex

  • ODS 来源:ods.store_goods_master
  • 自然键:site_goods_id
  • dim_store_goods 跟踪字段:tenant_idsite_idtenant_goods_idgoods_namegoods_category_idgoods_second_category_idcategory_level1_namecategory_level2_namebatch_stock_qtysale_qtytotal_sales_qtysale_pricecreated_atupdated_atavg_monthly_salesgoods_stateenable_statussend_stateis_deletecommodity_codenot_sale

4.7 商品分类维度dim_goods_category

  • ODS 来源:ods.stock_goods_category_tree
  • 自然键:category_id
  • 跟踪字段:tenant_idcategory_namealias_nameparent_category_idbusiness_nametenant_goods_business_idcategory_levelis_leafopen_salesmansort_orderis_warehousing
  • 变更触发场景:分类名称/层级/排序/启用状态变更

4.8 团购套餐维度dim_groupbuy_package / dim_groupbuy_package_ex

  • ODS 来源:ods.group_buy_packages
  • 自然键:groupbuy_package_id
  • dim_groupbuy_package 跟踪字段:tenant_idsite_idpackage_namepackage_template_idselling_pricecoupon_face_valueduration_secondsstart_timeend_timetable_area_nameis_enabledis_deletecreate_timetenant_table_area_id_listcard_type_idssortis_first_limit
  • 变更触发场景:套餐名称/价格/面值/有效期/启用状态变更

4.9 员工维度dim_staff / dim_staff_ex

  • ODS 来源:ods.staff_info_master
  • 自然键:staff_id
  • dim_staff 跟踪字段:staff_namealias_namemobilegenderjobtenant_idsite_idsystem_role_idstaff_identitystatusleave_statusentry_timeresign_timeis_delete
  • dim_staff_ex 跟踪字段:avatarjob_numaccount_statusrank_idrank_namenew_rank_idnew_staff_identityis_reserveshop_namesite_labeltenant_org_idsystem_user_idcashier_point_idcashier_point_namegroup_idgroup_namestaff_profile_idauth_codeauth_code_createding_talk_syncedsalary_grant_enabledentry_typeentry_sign_statusresign_sign_statuscriticism_statuscreate_timeuser_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 编码,中文撰写