← 返回宏观 DAG

Retention & Repurchase Sub-DAG

留存复购子图 | Points → Membership | Referral | Gift Card | Marketing Automation
2026-03-22 | Spec 098-business-dag-audit

健康度总览

13
总边数
9
已连通
3
部分连通
1
断裂

留存复购 DAG

已连通 (端到端代码链路验证)
部分连通 (后端存在但链路有缺口)
断裂 (未实现)

Path A: Points / Membership

graph LR
  PAY["Payment Complete (支付完成)"]
  EARN["Points Earned (积分累计)"]
  TIER["Tier Evaluation (会员等级评估)"]
  DISC["Checkout Discount (积分抵扣)"]

  PAY -->|"R1 积分累计"| EARN
  EARN -->|"R2 等级评估"| TIER
  TIER -->|"R3 等级折扣"| DISC

  style PAY fill:#1a2332,stroke:#3b82f6,color:#e0e0e0
  style EARN fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style TIER fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style DISC fill:#1a2332,stroke:#22c55e,color:#e0e0e0

  linkStyle 0 stroke:#22c55e,stroke-width:2px
  linkStyle 1 stroke:#22c55e,stroke-width:2px
  linkStyle 2 stroke:#22c55e,stroke-width:2px
    

Path B: Review Solicitation

graph LR
  SVCCOMP["Service Complete (服务完成)"]
  REVEVAL["Review Evaluate (评价规则评估)"]
  REVSMS["Review SMS (评价短信发送)"]

  SVCCOMP -->|"R4 触发评价请求"| REVEVAL
  REVEVAL -->|"R5 发送评价短信"| REVSMS

  style SVCCOMP fill:#1a2332,stroke:#3b82f6,color:#e0e0e0
  style REVEVAL fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style REVSMS fill:#1a2332,stroke:#22c55e,color:#e0e0e0

  linkStyle 0 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
  linkStyle 1 stroke:#22c55e,stroke-width:2px
    

Path C: Referral

graph LR
  REFCODE["Referral Code (推荐码生成)"]
  TRACK["Referral Tracking (推荐跟踪)"]
  REWARD["Referral Reward (推荐奖励)"]
  ACQ["Acquisition (获客入口)"]

  REFCODE -->|"R6 推荐码追踪"| TRACK
  TRACK -->|"R7 推荐奖励"| REWARD
  REFCODE -.->|"R13 跨图: 获客"| ACQ

  style REFCODE fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style TRACK fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style REWARD fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style ACQ fill:#0d2818,stroke:#8b5cf6,color:#8b5cf6,stroke-dasharray:5

  linkStyle 0 stroke:#22c55e,stroke-width:2px
  linkStyle 1 stroke:#22c55e,stroke-width:2px
  linkStyle 2 stroke:#ef4444,stroke-width:2px,stroke-dasharray:5
    

Path D: Gift Card Lifecycle

graph LR
  GCPURCH["Gift Card Purchase (礼品卡购买)"]
  GCGEN["Card Generation (卡号生成)"]
  GCDLVR["Delivery (邮件/短信发送)"]
  GCCLAIM["Claim (领取)"]
  GCUSE["Checkout Deduction (结账抵扣)"]

  GCPURCH -->|"R8 生成卡号"| GCGEN
  GCGEN -->|"R9 发送通知"| GCDLVR
  GCDLVR -->|"R10 领取"| GCCLAIM
  GCCLAIM -->|"R11 结账抵扣"| GCUSE

  style GCPURCH fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style GCGEN fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style GCDLVR fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style GCCLAIM fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style GCUSE fill:#1a2332,stroke:#22c55e,color:#e0e0e0

  linkStyle 0 stroke:#22c55e,stroke-width:2px
  linkStyle 1 stroke:#22c55e,stroke-width:2px
  linkStyle 2 stroke:#22c55e,stroke-width:2px
  linkStyle 3 stroke:#22c55e,stroke-width:2px
    

Path E: Marketing Automation

graph LR
  TEVAL["Tag Evaluation (标签评估)"]
  TRIGGER["Trigger Engine (触发引擎)"]
  SEND["Send Message (消息发送)"]
  BOOK["Booking Page (预约入口)"]

  TEVAL -->|"R12a 标签触发"| TRIGGER
  TRIGGER -->|"R12b 消息发送"| SEND
  SEND -.->|"R12c 跨图: 预约"| BOOK

  style TEVAL fill:#1a2332,stroke:#f59e0b,color:#e0e0e0
  style TRIGGER fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style SEND fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style BOOK fill:#0d2818,stroke:#8b5cf6,color:#8b5cf6,stroke-dasharray:5

  linkStyle 0 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
  linkStyle 1 stroke:#22c55e,stroke-width:2px
  linkStyle 2 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
    

边审计表

状态 涉及模块 代码路径 备注
R1 Payment → Points 累计 Loyalty api/loyalty.js:1892 checkout apply 调用 loyaltyService.earnPoints()
services/loyalty/loyaltyService.js:520 onPaymentCompleted() 调用 this.earnPoints()
services/loyalty/pointsAccountService.js:132 earnPoints() 写入 points_transactions
双路径:checkout-apply 直接调用 + onPaymentCompleted facade。支持 tier multiplier 加成。
R2 Points → Tier 评估 Loyalty services/loyalty/membershipService.js:405 recordSpend() 累加 period_spend + lifetime_spend
services/loyalty/membershipService.js:426 异步调用 checkAndUpgrade()
services/loyalty/loyaltyService.js:553 onPaymentCompleted() 调用 recordSpend()
支付完成后 recordSpend 累加消费额,非阻塞触发 checkAndUpgrade 比较 period_spend vs upgrade_threshold 决定升降级。
R3 Tier → Checkout 折扣 Loyalty services/loyalty/membershipService.js:305 calculateMembershipDiscount()
services/loyalty/loyaltyService.js:414 processCheckout() 步骤 1 调用此方法
processCheckout 在计算最终金额时查询 tier discount_percent 并减免。
R4 Service Complete → Review 请求 Automation, Agent api/appointments.js:2211 status='pending_payment' 时调用 automationEvents.serviceCompleted()
services/automation/event-emitter.js:85 serviceCompleted() 触发 trigger engine
agents/acquisition/review.agent.js Review Agent 评估 + 发送
service_completed 事件通过 automation trigger engine 调度后续消息。Review Agent 是独立的 Inngest agent,通过 checkout 事件触发,不直接挂接在 automation trigger engine 的 service_completed 事件上。两套并行路径,automation rule 和 Review Agent 各自独立。缺乏统一入口编排。
R5 Review Evaluate → Send SMS Agent agents/acquisition/tools/review.tools.js:23 evaluateReviewRequest 规则引擎
agents/acquisition/tools/review.tools.js:79 sendReviewRequest SMS 发送
services/acquisition/review-evaluator.js 规则引擎:tip 阈值、cooldown、weekly cap
Review Agent 内部完整:evaluate 判断 shouldSend,通过后 sendReviewRequest 发短信含 Google Review URL。
R6 Referral Code → 追踪 Loyalty services/loyalty/referralService.js:35 generateCode() 生成唯一 6 字符码
services/loyalty/referralService.js:156 applyCode() 创建 pending referral + 增加 usage_count
code 生成 → 新客注册时 applyCode 创建 pending referral 记录。防自引用、防重复引用。
R7 Referral → Reward Loyalty services/loyalty/loyaltyService.js:564 onPaymentCompleted() 调用 referral.completeReferral()
services/loyalty/referralService.js:234 completeReferral() 判断 multi_reward_enabled
services/loyalty/referralRewardService.js:126 issueRewards() 支持 points / cash_voucher / discount 三种奖励
services/loyalty/referralRewardService.js:470 retryPendingRewards() 失败重试 (5min/15min/60min)
084-referral-reward-enhancement: 首次支付后自动完成 referral,多类型奖励(积分/现金券/折扣券),含重试机制和通知。
R8 Gift Card Purchase → 生成 Gift Card services/loyalty/giftCardService.js:63 purchaseGiftCard()
utils/giftCardGenerator.js 生成唯一卡号 + PIN
services/giftCard/giftCardService.js 另一套 GiftCardService (023-gift-cards),支持 claim_token / JWT
两套 GiftCardService 并存:loyalty/ 下的原始版 (017) 和 giftCard/ 下的增强版 (023)。purchaseGiftCard 原子性写入 gift_cards + gift_card_transactions。
R9 Gift Card → 发送通知 Gift Card, Messaging services/loyalty/giftCardDeliveryService.js:20 sendGiftCardEmail() 旧版邮件发送
services/giftCard/giftCardNotificationService.js:90 sendGiftCardEmail() 新版含 claim URL
services/giftCard/giftCardNotificationService.js:169 sendGiftCardSMS() 短信含 claim URL
services/giftCard/giftCardDeliveryStateService.js delivery 状态追踪
新版 NotificationService 生成 claim URL 并通过 email/SMS/both 发送,记录 delivery events 便于审计。旧版 DeliveryService 直接发卡号+PIN。
R10 Gift Card → 领取 Gift Card, Frontend api/gift-cards.js GET /api/gift-cards/claim/verify/:token 验证 token
api/gift-cards.js POST /api/gift-cards/claim/:token 执行领取
frontend/.../gift-cards/claim/[token]/page.tsx 前端 claim 页面
services/giftCard/giftCardService.js JWT claim_token 验证 + 绑定 guest
完整 claim 流程:收到通知 → 点击 claim URL → 前端 claim 页面 → verify token → 登录/注册 → POST claim → 绑定到 guest 账户。
R11 Gift Card → 结账抵扣 Loyalty, Payment services/loyalty/loyaltyService.js:391 processCheckout() 步骤 3 调用 gift card 验证 + 抵扣
services/loyalty/loyaltyService.js:542 onPaymentCompleted() 步骤 3 调用 redeemGiftCard()
services/loyalty/giftCardService.js:290 redeemGiftCard() FOR UPDATE 锁卡 + 扣余额
api/payment/request-routes.js + api/payment/split-payment-routes.js 支付路由含 gift card 抵扣
双路径:processCheckout 预计算抵扣 + onPaymentCompleted 实际执行扣款。redeemGiftCard 使用 FOR UPDATE 行锁防并发。
R12 Tag 评估 → Trigger → 发送 Tagging, Automation, Messaging services/tagging/evaluationService.js:29 evaluateTagDefinition() SQL 规则评估 + 增量更新 guest_tags
services/automation/trigger-engine.js:29 EVENT_TYPES.TAG_ADDED 事件类型已注册
services/automation/event-emitter.js:211 tagAdded() helper 已就绪
services/automation/execution-processor.js 调用 MessagingService 发送 email/SMS
Tag 评估服务和 automation trigger engine 各自完整,但 evaluationService 在标签变更后未调用 automationEvents.tagAdded()。tag_added 事件触发路径断裂:无代码在标签写入后 emit 事件到 trigger engine。需要在 evaluationService 的 step 5 (添加标签循环) 中加入 automationEvents.tagAdded() 调用。
R12c Recall → Booking Page (跨图) Automation, Conversion services/automation/execution-processor.js 发送 email/SMS 含模板变量
模板可包含 booking link 变量 (appointmentInfoUrl 等)
模板引擎支持注入 booking URL,但具体 recall 类 automation rule 的模板是否配置了正确的预约链接取决于运营配置。代码层面具备能力,但无强制保证。
R13 Referral → Acquisition Agent (跨图) Loyalty, Agent services/loyalty/referralService.js 推荐系统独立运作
agents/acquisition/ 获客 Agent 组 (SEO, Review, Content) 无 referral 集成
Referral 系统和 Acquisition Agent 组完全独立,无交叉引用。Referral 走的是 guest-to-guest 口碑路径(生成码 → 分享 → 新客注册 → 首购奖励),Acquisition Agent 走的是 Local SEO / Review / Content 路径。两者未连接是有意设计还是缺口待确认。

礼品卡生命周期完整性检查

状态 生命周期阶段 代码实现 备注
1. Purchase (购买) loyalty/giftCardService.js:63 purchaseGiftCard + giftCard/giftCardService.js 生成唯一卡号 + PIN + claim_token,验证金额范围,原子写入
2. Delivery (发送通知) giftCard/giftCardNotificationService.js email + SMS + delivery state tracking 含 claim URL,记录 delivery_events 审计
3. Claim (领取) api/gift-cards.js verify + claim endpoints
frontend/.../gift-cards/claim/[token]/page.tsx
JWT token 验证,可过期 (claim_expire_days 配置),绑定 guest 账户
4. Redeem (使用) loyalty/giftCardService.js:290 redeemGiftCard + processCheckout FOR UPDATE 行锁,支持部分使用,余额为 0 自动标记 USED
5. Reload (充值) loyalty/giftCardService.js:358 reloadGiftCard 已用完的卡充值后自动恢复 active 状态
6. Transfer (转让) loyalty/giftCardService.js:428 transferGiftCard 更新 recipient + 记录 TRANSFER_OUT 交易
7. Cancel (取消) loyalty/giftCardService.js:495 cancelGiftCard 记录 CANCEL 交易,余额归零
8. Expiry 通知 services/automation/event-emitter.js:194 giftCardExpiring() automation trigger engine 支持 GIFT_CARD_EXPIRY 事件触发通知

礼品卡生命周期全 8 个阶段均有代码实现,是留存模块中最完整的子系统。

散点问题汇总

LP-1: Tag 评估 → Automation 事件断裂

services/tagging/evaluationService.js 在标签变更后(step 5 添加 / step 6 移除)没有调用 automationEvents.tagAdded()

event-emitter 中 tagAdded() helper 已就绪 (line 211),trigger-engine 中 TAG_ADDED 事件类型已注册 (line 29),但无代码在标签实际变更时触发此事件。

修复方案:在 evaluationService.js 的 step 5 循环中,每次成功添加标签后调用 automationEvents.tagAdded(tenantId, { customerId: guestId, tagId: definitionId })

LP-2: 两套 GiftCardService 并存

services/loyalty/giftCardService.js (017-loyalty-program 原始版) 和 services/giftCard/giftCardService.js (023-gift-cards 增强版) 同时存在。

loyalty 版用于 checkout 抵扣 (redeemGiftCard),giftCard 版用于 claim token / delivery state。referralRewardService 中发放 cash_voucher 奖励调用的是 loyalty 版的 purchaseGiftCard。

风险:两套服务操作同一张 gift_cards 表但逻辑不完全同步 (例如 claim_token 仅 giftCard 版生成)。

LP-3: Review Agent vs Automation Trigger 双路径

service_completed 事件通过 automation trigger engine 调度通用营销消息 (birthday recall, follow-up 等)。Review Agent 是独立的 Inngest agent,通过 checkout 事件单独触发评价短信。

两套系统各自运作,无统一编排层。运营人员配置 automation rule 时不知道 Review Agent 也会发短信,存在重复发送风险。

跨图引用

→ Conversion (转化子图)
→ Acquisition (获客子图)
→ Delivery (交付子图)