Files
cursor/plans/invitebind_规范与弹窗_23c1cd10.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

8.0 KiB
Raw Permalink Blame History

name, overview, todos, isProject
name overview todos isProject
InviteBind 规范与弹窗 重构 `LuckyRewardInviteLogic::handleInviteBind` 以符合 php-clean-code 分层写法并在有效助力成功时可靠入队邀请人「Claim Now」弹窗在线 WS 即时推送 + 离线/游戏中 HomeEvent 补弹),对齐 PRD 4.2 与子需求 F/D 文档。
id content status
invite-bind-context-dto 新增 InviteBindProcessContextDTOfetchUserProfile 改为返回 LuckyRewardUserProfileEntity completed
id content status
refactor-handle-invite-bind 拆分 handleInviteBind 为编排 + private 步骤(校验/上下文/事务/弹窗/日志) completed
id content status
inviter-popup-prd-42 notifyInviterInviteSuccess成功必入队补全 PRD 4.2 payload去掉 spin_reward>0 早退 completed
id content status
tests-docs-verify 增强 InviteBind 集成测 payload 断言;更新 05 文档;跑 verify + phpunit completed
false

LuckyRewardInviteLogic handleInviteBind 重构与邀请人弹窗

现状与问题

handleInviteBind 约 160 行,单方法内堆叠:参数校验、活动/轮次解析、幂等判断、事务写 helper/grant/player、弹窗入队、日志。

编码规范偏差(对照 php-clean-code

问题 现状
方法过长 handleInviteBind 远超 50 行建议
编排不清晰 校验 / 发奖 / 弹窗混在一个方法
返回值不规范 fetchUserProfile() 返回 array{nickname,avatar},同模块已有 LuckyRewardUserProfileEntityLuckyRewardLogic 已使用)
弹窗条件偏窄 enqueueInviterSuccessPopupinviterSpinReward <= 0 时直接 return有效助力成功但配置为 0 时不入队

弹窗能力已存在但未在编排层显式表达:

PRD 4.2 邀请成功:被邀请人注册成功后,邀请人收到 LUCKY YOU / 1 Free Spin / Claim Now仅回大厅弹、游戏内禁弹由客户端忽略 WS + HomeEvent flush 保证)。

sequenceDiagram
    participant Agent as slot_agent_MQ
    participant Logic as handleInviteBind
    participant DB as s_common
    participant Popup as PendingPopupService
    participant WS as WsService
    participant Home as HomeEvent

    Agent->>Logic: invite_bind callback
    Logic->>DB: helper + grant + player
    Logic->>Popup: enqueue inviter popup
    Popup->>DB: user_pending_popup
    Popup->>WS: notifyClientPOP
    Note over WS: 在线大厅即时弹
    Home->>Popup: fetchPending on home
    Popup->>WS: 补弹离线/回大厅

目标结构

handleInviteBind 编排public读流程

public function handleInviteBind(InviteBindCallbackDTO $callbackDto): InviteBindResultEntity
{
    $earlyResult = $this->resolveInviteBindSkipResult($callbackDto);
    if ($earlyResult !== null) {
        return $earlyResult;
    }

    $processContext = $this->resolveInviteBindProcessContext($callbackDto);
    if ($processContext instanceof InviteBindResultEntity) {
        return $processContext; // activity closed 等
    }

    if ($this->isInviteHelperAlreadyProcessed($processContext->cycleId, $callbackDto->inviteeUid)) {
        return $this->buildInviteBindResult(STATUS_ALREADY_PROCESSED, ...);
    }

    $this->persistInviteHelperInTransaction($callbackDto, $processContext);
    $this->notifyInviterInviteSuccess($callbackDto, $processContext);
    $this->logInviteBindSuccess($callbackDto, $processContext);

    return $this->buildInviteBindResult(STATUS_SUCCESS, ...);
}

新增编排上下文 DTO

InviteBindProcessContextDTOreadonly承载本用例多步共享字段避免 private 方法重复挂标量:

  • resolvedSource, cycleId, inviterSpinReward, inviteeSpinReward
  • inviterBizId, inviteeBizId, popupBizId

拆分 private 方法(均有中文 PHPDoc

方法 职责
resolveInviteBindSkipResult 参数非法 / 自邀 / 老用户 / 非 wheel 来源 → early return Entity
resolveInviteBindProcessContext 解析活动配置与活跃轮次;失败返回 InviteBindResultEntity
isInviteHelperAlreadyProcessed 封装 LuckyRewardHelperModel::findByCycleAndInvitee
persistInviteHelperInTransaction 事务:写 helper、发 inviter/invitee grant、更新 player
createInviteHelperRecord 写 helper 行
grantInviterSpinForHelper inviter grant + player spin/valid_invite_count
grantInviteeSpinForHelper invitee grant + player spin
notifyInviterInviteSuccess PRD 4.2:有效助力成功后入队 + WS委托 PendingPopupService
logInviteBindSuccess 成功业务日志

弹窗行为调整PRD 4.2

  • 成功即入队notifyInviterInviteSuccessSTATUS_SUCCESS 路径始终调用(去掉 inviterSpinReward <= 0 早退);payload.spin_reward 仍传配置值(可为 0
  • payload 对齐前端(在现有字段基础上补全,不破坏已对接字段):
    • 保留:invitee_uid, invitee_nickname, invitee_avatar, spin_reward, cycle_id
    • 新增PRD 文案,便于客户端少写死):title=LUCKY YOU!, content=You've received 1 Free Spin, button_text=Claim Now, priority
  • 在线推送:继续复用 PendingPopupService::enqueue(内部已 WSLogic 不重复调 WsService
  • 离线/游戏中:依赖已有 user_pending_popup + HomeEvent::appendLuckyRewardHomePopups,无需 Logic 判断在线

用户资料返回 Entity

  • fetchUserProfile(int $uid): LuckyRewardUserProfileEntity 替换 array 返回
  • enqueueInviterSuccessPopup / buildSupportInvitePopup 同步改用 Entity 字段(后者可顺带改,范围小)

测试

用例 文件
现有 early-return 单测 LuckyRewardInviteLogicUnitTest 保持
集成:成功写 helper + pending popup 已有;增强断言 popup_typepayloadtitle/content/spin_reward
集成:第二次回调幂等不入队新 popup 已有 already_processed;确认 popup 仍仅 1 条

运行:RUN_DB_TESTS=1LuckyRewardInviteBindIntegrationTest + 现有 Unit 测。


文档


不在本次范围

  • buildSupportInvitePopup 改返回 Entity可选后续本次仅改其用到的 fetchUserProfile
  • 服务端精确判断「是否在大厅/是否在线」07 文档一期不做,依赖 WS + home flush
  • slot_hub / 客户端弹窗 UI 实现

自检

  • verify-slot-backend.sh + docker php -l
  • 对照 Logic §3/§8方法长度、中文 PHPDoc、无 Logic 返回 array、编排 DTO 不双份拷贝