feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
@@ -26,7 +26,7 @@ SCHEMA_ETL=meta
|
||||
# API 配置(上游 SaaS API)
|
||||
# ------------------------------------------------------------------------------
|
||||
API_BASE=https://pc.ficoo.vip/apiprod/admin/v1/
|
||||
API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnQtdHlwZSI6IjQiLCJ1c2VyLXR5cGUiOiIxIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiMTIiLCJyb2xlLWlkIjoiMTIiLCJ0ZW5hbnQtaWQiOiIyNzkwNjgzMTYwNzA5OTU3Iiwibmlja25hbWUiOiLnp5_miLfnrqHnkIblkZjvvJrmganmgakxIiwic2l0ZS1pZCI6IjAiLCJtb2JpbGUiOiIxMzgxMDUwMjMwNCIsInNpZCI6IjI5NTA0ODk2NTgzOTU4NDUiLCJzdGFmZi1pZCI6IjMwMDk5MTg2OTE1NTkwNDUiLCJvcmctaWQiOiIwIiwicm9sZS10eXBlIjoiMyIsInJlZnJlc2hUb2tlbiI6IncxTkhzalRIeHU1b0Ric0hnQXp6SUgrU2Q2d2M3YndUTTU1ZTZnSXg0RTQ9IiwicmVmcmVzaEV4cGlyeVRpbWUiOiIyMDI2LzMvMTkg5LiK5Y2IMToxNzowMyIsIm5lZWRDaGVja1Rva2VuIjoiZmFsc2UiLCJleHAiOjE3NzM4NTQyMjMsImlzcyI6InRlc3QiLCJhdWQiOiJVc2VyIn0.E7oh3g_-3fyeC1-oHsJXZ-tTGqcvUCMDrd9TifJAs4U
|
||||
API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnQtdHlwZSI6IjQiLCJ1c2VyLXR5cGUiOiIxIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiMTIiLCJyb2xlLWlkIjoiMTIiLCJ0ZW5hbnQtaWQiOiIyNzkwNjgzMTYwNzA5OTU3Iiwibmlja25hbWUiOiLnp5_miLfnrqHnkIblkZjvvJrmganmgakxIiwic2l0ZS1pZCI6IjAiLCJtb2JpbGUiOiIxMzgxMDUwMjMwNCIsInNpZCI6IjI5NTA0ODk2NTgzOTU4NDUiLCJzdGFmZi1pZCI6IjMwMDk5MTg2OTE1NTkwNDUiLCJvcmctaWQiOiIwIiwicm9sZS10eXBlIjoiMyIsInJlZnJlc2hUb2tlbiI6InhZbFozZDN1ekR3cnpkaFNUeVpFYnJVQUc5ZEtrZDVrK1FUWEd0Ym9LQkU9IiwicmVmcmVzaEV4cGlyeVRpbWUiOiIyMDI2LzMvMjYg5LiK5Y2IMTozMTo1MCIsIm5lZWRDaGVja1Rva2VuIjoiZmFsc2UiLCJleHAiOjE3NzQ0NTk5MTAsImlzcyI6InRlc3QiLCJhdWQiOiJVc2VyIn0.yNtsQIIQPVoFkjPUHWVsi9nRGc_lSgFGerxOfJSDOcc
|
||||
API_TIMEOUT=20
|
||||
API_PAGE_SIZE=200
|
||||
API_RETRY_MAX=3
|
||||
|
||||
@@ -202,13 +202,17 @@ card_type_id
|
||||
|
||||
2791990152417157 → “台费卡”(7 条)
|
||||
|
||||
2791987095408517 → “年卡”(7 条)
|
||||
|
||||
2793306611533637 → “月卡”(12 条)
|
||||
|
||||
含义:卡种类型 ID,用于区分不同卡种。
|
||||
|
||||
memberCardTypeName
|
||||
|
||||
类型:string
|
||||
|
||||
值:"储值卡", "活动抵用券", "酒水卡", "台费卡"
|
||||
值:"储值卡", "活动抵用券", "酒水卡", "台费卡", "年卡", "月卡"
|
||||
|
||||
含义:卡种名称,与 card_type_id 一一对应,是一个 卡种枚举名称。
|
||||
|
||||
@@ -560,7 +564,7 @@ payment_method 决定是否有外部支付以及大致支付渠道;
|
||||
|
||||
卡种类型在本表中已经完全可识别
|
||||
|
||||
通过 card_type_id ↔ memberCardTypeName,本表已经给出了储值卡、酒水卡、台费卡、活动抵用券四种卡型及各自的 ID;
|
||||
通过 card_type_id ↔ memberCardTypeName,本表已经给出了储值卡、酒水卡、台费卡、活动抵用券、年卡、月卡六种卡型及各自的 ID;
|
||||
|
||||
与“会员档案”里 member_card_grade_code / member_card_grade_name 可以配套构成更完整的“卡种维度”。
|
||||
|
||||
@@ -587,3 +591,12 @@ remark 与 from_type 的配合使用
|
||||
|
||||
|
||||
整体来看,余额变更记录.json 是会员卡层面的“总账/明细账表”,与“充值记录”“消费结算记录”“会员档案”“卡类型、卡实例”之间,通过一整套 ID 和枚举字段建立了清晰的结构关系,而本次你给的这家门店只是该结构在一个门店上的数据切片。
|
||||
|
||||
<!--
|
||||
AI_CHANGELOG:
|
||||
- 日期: 2026-03-19
|
||||
- Prompt: card_type_id 年卡/月卡映射同步
|
||||
- 直接原因: 用户确认 2791987095408517=年卡、2793306611533637=月卡,同步到所有涉及 card_type_id 的文档
|
||||
- 变更摘要: card_type_id 枚举补充年卡/月卡(2 条),memberCardTypeName 枚举补充"年卡""月卡","四种卡型"改为"六种卡型"
|
||||
- 风险与验证: 纯文档变更,无运行时影响
|
||||
-->
|
||||
|
||||
@@ -133,17 +133,9 @@
|
||||
|
||||
有效期、最近消费时间等状态信息。
|
||||
|
||||
根据字段值,这一页数据中主要有五类卡:
|
||||
根据字段值,这一页数据中主要有六类卡:
|
||||
|
||||
储值卡
|
||||
|
||||
活动抵用券
|
||||
|
||||
台费卡
|
||||
|
||||
酒水卡
|
||||
|
||||
月卡
|
||||
储值卡、活动抵用券、台费卡、酒水卡、年卡、月卡
|
||||
|
||||
因此,这个 JSON 更准确地理解为:门店下所有储值/次卡/券类会员卡的列表视图。
|
||||
|
||||
@@ -163,15 +155,14 @@ card_type_id
|
||||
|
||||
枚举(按数据分布):
|
||||
|
||||
2793249295533893
|
||||
|
||||
2793266846533445
|
||||
|
||||
2791990152417157
|
||||
|
||||
2794699703437125
|
||||
|
||||
2793306611533637
|
||||
| card_type_id | 卡种名称 |
|
||||
|---|---|
|
||||
| 2793249295533893 | 储值卡 |
|
||||
| 2793266846533445 | 活动抵用券 |
|
||||
| 2791990152417157 | 台费卡 |
|
||||
| 2794699703437125 | 酒水卡 |
|
||||
| 2791987095408517 | 年卡 |
|
||||
| 2793306611533637 | 月卡 |
|
||||
|
||||
这些 ID 对应不同的卡种配置,具体含义在系统内部的“卡种配置表”中。
|
||||
|
||||
@@ -183,15 +174,15 @@ member_card_grade_code
|
||||
|
||||
枚举:
|
||||
|
||||
2790683528022853 → 储值卡
|
||||
| member_card_grade_code | 卡种名称 |
|
||||
|---|---|
|
||||
| 2790683528022853 | 储值卡 |
|
||||
| 2790683528022856 | 活动抵用券 |
|
||||
| 2790683528022855 | 台费卡 |
|
||||
| 2790683528022858 | 酒水卡 |
|
||||
| 2790683528022857 | 月卡 |
|
||||
|
||||
2790683528022856 → 活动抵用券
|
||||
|
||||
2790683528022855 → 台费卡
|
||||
|
||||
2790683528022858 → 酒水卡
|
||||
|
||||
2790683528022857 → 月卡
|
||||
> 注意:年卡(card_type_id `2791987095408517`)对应的 member_card_grade_code 尚未在样本数据中出现。
|
||||
|
||||
member_card_grade_code_name
|
||||
|
||||
@@ -201,15 +192,7 @@ member_card_grade_code_name
|
||||
|
||||
枚举值(与上面 code 一一对应):
|
||||
|
||||
"储值卡"
|
||||
|
||||
"活动抵用券"
|
||||
|
||||
"台费卡"
|
||||
|
||||
"酒水卡"
|
||||
|
||||
"月卡"
|
||||
"储值卡"、"活动抵用券"、"台费卡"、"酒水卡"、"月卡"、"年卡"
|
||||
|
||||
member_card_type_name
|
||||
|
||||
@@ -808,4 +791,10 @@ AI_CHANGELOG:
|
||||
- 直接原因: 合并 `electricityCardDeduct`/`rechargeFreezeBalance` 驼峰变体与已有小写字段去重,补充 5 个真正新字段描述
|
||||
- 变更摘要: 去除大小写重复行,补充 able_share_member_discount/electricity_deduct_radio/electricity_discount/member_grade/principal_balance 的中文描述
|
||||
- 风险与验证: 纯文档文案修正,无运行时影响;验证:grep "新发现字段" 返回 0 结果
|
||||
|
||||
- 日期: 2026-03-19
|
||||
- Prompt: card_type_id 年卡/月卡映射同步
|
||||
- 直接原因: 用户确认 2791987095408517=年卡、2793306611533637=月卡,同步到所有涉及 card_type_id 的文档
|
||||
- 变更摘要: 补充年卡/月卡的 card_type_id 映射,将枚举列表改为表格形式并标注中文名称
|
||||
- 风险与验证: 纯文档变更,无运行时影响
|
||||
-->
|
||||
|
||||
@@ -82,8 +82,8 @@
|
||||
| `tenant_member_id` | int | `2799212845565701` | 租户内会员主键 ID。对应会员档案表的 `id` |
|
||||
| `system_member_id` | int | `2799212844549893` | 系统级会员 ID(全局唯一) |
|
||||
| `tenant_member_card_id` | int | `2799219999295237` | 会员卡账户 ID,指明本次变更针对哪一张卡。对应储值卡列表的 `id` |
|
||||
| `card_type_id` | int | `2793249295533893` | 卡种类型 ID。枚举:`2793249295533893` = 储值卡,`2793266846533445` = 活动抵用券,`2794699703437125` = 酒水卡,`2791990152417157` = 台费卡 |
|
||||
| `memberCardTypeName` | string | `"储值卡"` | 卡种名称,与 `card_type_id` 一一对应。枚举值:`"储值卡"`、`"活动抵用券"`、`"酒水卡"`、`"台费卡"` |
|
||||
| `card_type_id` | int | `2793249295533893` | 卡种类型 ID。枚举:`2793249295533893` = 储值卡,`2793266846533445` = 活动抵用券,`2794699703437125` = 酒水卡,`2791990152417157` = 台费卡,`2791987095408517` = 年卡,`2793306611533637` = 月卡 |
|
||||
| `memberCardTypeName` | string | `"储值卡"` | 卡种名称,与 `card_type_id` 一一对应。枚举值:`"储值卡"`、`"活动抵用券"`、`"酒水卡"`、`"台费卡"`、`"年卡"`、`"月卡"` |
|
||||
| `memberName` | string | `"曾丹烨"` | 会员姓名/称呼 |
|
||||
| `memberMobile` | string | `"13922213242"` | 会员手机号 |
|
||||
|
||||
@@ -220,4 +220,10 @@ AI_CHANGELOG:
|
||||
- 直接原因: 4.8"时间"组混入 principal_after/principal_before/principal_data 三个本金字段
|
||||
- 变更摘要: 新增 4.8"本金明细"组(3 字段);原 4.8 时间组重编号为 4.9
|
||||
- 风险与验证: 纯文档分组调整,无运行时影响;验证:统计各组字段数总和 = 25+3 = 28(含本金字段)
|
||||
|
||||
- 日期: 2026-03-19
|
||||
- Prompt: card_type_id 年卡/月卡映射同步
|
||||
- 直接原因: 用户确认 2791987095408517=年卡、2793306611533637=月卡,同步到所有涉及 card_type_id 的文档
|
||||
- 变更摘要: card_type_id 枚举和 memberCardTypeName 枚举补充年卡/月卡
|
||||
- 风险与验证: 纯文档变更,无运行时影响
|
||||
-->
|
||||
|
||||
@@ -83,11 +83,17 @@ WHERE scd2_is_current = 1;
|
||||
|
||||
**储值卡ID**
|
||||
- 储值卡 card_type_id = 2793249295533893
|
||||
**赠送卡ID**
|
||||
**赠送卡ID**(归入 `gift_card_balance`)
|
||||
- 台费卡 2791990152417157
|
||||
- 酒水卡 2794699703437125
|
||||
- 活动抵用券 2793266846533445
|
||||
|
||||
**其他卡类型**(当前未计入 `cash_card_balance` 或 `gift_card_balance`)
|
||||
- 年卡 2791987095408517
|
||||
- 月卡 2793306611533637
|
||||
|
||||
> ⚠️ 年卡和月卡的余额目前未被 `_extract_card_balances()` 统计,不包含在 `total_card_balance` 中。详见 P12 PRD(`docs/prd/specs/P12-gift-card-breakdown.md`)。
|
||||
|
||||
## 可回溯性
|
||||
|
||||
| 项目 | 说明 |
|
||||
@@ -95,3 +101,12 @@ WHERE scd2_is_current = 1;
|
||||
| 可回溯 | ✅ 完全可回溯 |
|
||||
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||
| 依赖表 | dwd_recharge_order, dim_member_card_account |
|
||||
|
||||
<!--
|
||||
AI_CHANGELOG:
|
||||
- 日期: 2026-03-19
|
||||
- Prompt: card_type_id 年卡/月卡映射同步
|
||||
- 直接原因: 用户确认 2791987095408517=年卡、2793306611533637=月卡,同步到所有涉及 card_type_id 的文档
|
||||
- 变更摘要: 赠送卡ID 部分添加归入说明,新增"其他卡类型"段落标注年卡/月卡未被统计的遗漏
|
||||
- 风险与验证: 纯文档变更,无运行时影响
|
||||
-->
|
||||
|
||||
@@ -859,6 +859,8 @@ dim_table ────────────────────┘
|
||||
| 台费卡 | `2791990152417157` | `gift_card_balance` |
|
||||
| 活动抵用券 | `2793266846533445` | `gift_card_balance` |
|
||||
| 酒水卡 | `2794699703437125` | `gift_card_balance` |
|
||||
| 年卡 | `2791987095408517` | ⚠️ 未统计(遗漏) |
|
||||
| 月卡 | `2793306611533637` | ⚠️ 未统计(遗漏) |
|
||||
|
||||
`total_card_balance = cash_card_balance + gift_card_balance`
|
||||
|
||||
@@ -1209,6 +1211,8 @@ card_consume_total = recharge_card_consume + gift_card_consume
|
||||
| 台费卡 | `2791990152417157` | `gift_card_balance` |
|
||||
| 酒水卡 | `2794699703437125` | `gift_card_balance` |
|
||||
| 活动抵用券 | `2793266846533445` | `gift_card_balance` |
|
||||
| 年卡 | `2791987095408517` | ⚠️ 未统计(遗漏) |
|
||||
| 月卡 | `2793306611533637` | ⚠️ 未统计(遗漏) |
|
||||
|
||||
```
|
||||
total_card_balance = cash_card_balance + gift_card_balance
|
||||
@@ -1772,3 +1776,12 @@ dwd_table_fee_log (ledger_count) ────────────┘
|
||||
6. 计算占比:`percentage = duration_seconds / total_seconds`(四位小数)
|
||||
7. 占比 ≥ 0.25 标记 `is_tagged = TRUE`
|
||||
8. 过滤条件:`COALESCE(is_delete, 0) = 0`,营业日切点通过 `biz_date_sql_expr` 处理
|
||||
|
||||
<!--
|
||||
AI_CHANGELOG:
|
||||
- 日期: 2026-03-19
|
||||
- Prompt: card_type_id 年卡/月卡映射同步
|
||||
- 直接原因: 用户确认 2791987095408517=年卡、2793306611533637=月卡,同步到所有涉及 card_type_id 的文档
|
||||
- 变更摘要: 两处卡类型映射表(DWS_MEMBER_CONSUMPTION 和 DWS_FINANCE_RECHARGE)补充年卡/月卡行,标注"⚠️ 未统计(遗漏)"
|
||||
- 风险与验证: 纯文档变更,无运行时影响
|
||||
-->
|
||||
|
||||
@@ -139,6 +139,48 @@ class FinanceRechargeTask(FinanceBaseTask):
|
||||
rows = self.db.query(sql, (site_id, start_date, end_date))
|
||||
return [dict(row) for row in rows] if rows else []
|
||||
|
||||
# CHANGE 2026-07-17 | task 2.1: card_type_id → 细分余额字段名映射
|
||||
GIFT_TYPE_FIELD_MAP = {
|
||||
2794699703437125: 'gift_liquor_balance', # 酒水卡
|
||||
2791990152417157: 'gift_table_fee_balance', # 台费卡
|
||||
2793266846533445: 'gift_voucher_balance', # 抵用券
|
||||
}
|
||||
|
||||
# CHANGE 2026-07-18 | task 3.1: card_type_id → 细分充值字段名映射
|
||||
GIFT_RECHARGE_FIELD_MAP = {
|
||||
2794699703437125: 'gift_liquor_recharge', # 酒水卡
|
||||
2791990152417157: 'gift_table_fee_recharge', # 台费卡
|
||||
2793266846533445: 'gift_voucher_recharge', # 抵用券
|
||||
}
|
||||
|
||||
# CHANGE 2026-07-18 | task 3.1: 按 card_type_id 拆分赠送卡新增充值
|
||||
def _extract_gift_recharge_breakdown(self, site_id: int, start_date: date, end_date: date) -> Dict[str, Decimal]:
|
||||
"""按卡类型拆分赠送卡新增充值(JOIN 充值订单与会员卡账户维度表)"""
|
||||
cutoff = self.config.get("app.business_day_start_hour", 8)
|
||||
biz_expr = biz_date_sql_expr("ro.pay_time", cutoff)
|
||||
sql = f"""
|
||||
SELECT dca.card_type_id, SUM(ro.point_amount) AS gift_recharge
|
||||
FROM dwd.dwd_recharge_order ro
|
||||
JOIN dwd.dim_member_card_account dca
|
||||
ON ro.tenant_member_card_id = dca.tenant_member_id
|
||||
WHERE ro.site_id = %s
|
||||
AND {biz_expr} >= %s AND {biz_expr} <= %s
|
||||
AND dca.card_type_id IN (2794699703437125, 2791990152417157, 2793266846533445)
|
||||
AND dca.scd2_is_current = 1
|
||||
AND COALESCE(dca.is_delete, 0) = 0
|
||||
GROUP BY dca.card_type_id
|
||||
"""
|
||||
rows = self.db.query(sql, (site_id, start_date, end_date))
|
||||
|
||||
result: Dict[str, Decimal] = {
|
||||
field: Decimal('0') for field in self.GIFT_RECHARGE_FIELD_MAP.values()
|
||||
}
|
||||
for row in (rows or []):
|
||||
field_name = self.GIFT_RECHARGE_FIELD_MAP.get(row['card_type_id'])
|
||||
if field_name:
|
||||
result[field_name] = self.safe_decimal(row['gift_recharge'])
|
||||
return result
|
||||
|
||||
def _extract_card_balances(self, site_id: int, stat_date: date) -> Dict[str, Decimal]:
|
||||
CASH_CARD_TYPE_ID = 2793249295533893
|
||||
GIFT_CARD_TYPE_IDS = [2791990152417157, 2793266846533445, 2794699703437125]
|
||||
@@ -162,6 +204,10 @@ class FinanceRechargeTask(FinanceBaseTask):
|
||||
|
||||
cash_balance = Decimal('0')
|
||||
gift_balance = Decimal('0')
|
||||
# CHANGE 2026-07-17 | task 2.1: 按 card_type_id 拆分赠送卡余额
|
||||
gift_breakdown: Dict[str, Decimal] = {
|
||||
field: Decimal('0') for field in self.GIFT_TYPE_FIELD_MAP.values()
|
||||
}
|
||||
|
||||
for row in (rows or []):
|
||||
card_type_id = row['card_type_id']
|
||||
@@ -170,11 +216,16 @@ class FinanceRechargeTask(FinanceBaseTask):
|
||||
cash_balance += balance
|
||||
elif card_type_id in GIFT_CARD_TYPE_IDS:
|
||||
gift_balance += balance
|
||||
# 写入细分字段(未知 card_type_id 不会命中 GIFT_TYPE_FIELD_MAP)
|
||||
field_name = self.GIFT_TYPE_FIELD_MAP.get(card_type_id)
|
||||
if field_name:
|
||||
gift_breakdown[field_name] += balance
|
||||
|
||||
return {
|
||||
'cash_balance': cash_balance,
|
||||
'gift_balance': gift_balance,
|
||||
'total_balance': cash_balance + gift_balance
|
||||
'total_balance': cash_balance + gift_balance,
|
||||
**gift_breakdown,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user