8.0 KiB
8.0 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| InviteBind 规范与弹窗 | 重构 `LuckyRewardInviteLogic::handleInviteBind` 以符合 php-clean-code 分层写法,并在有效助力成功时可靠入队邀请人「Claim Now」弹窗(在线 WS 即时推送 + 离线/游戏中 HomeEvent 补弹),对齐 PRD 4.2 与子需求 F/D 文档。 |
|
false |
LuckyRewardInviteLogic handleInviteBind 重构与邀请人弹窗
现状与问题
handleInviteBind 约 160 行,单方法内堆叠:参数校验、活动/轮次解析、幂等判断、事务写 helper/grant/player、弹窗入队、日志。
编码规范偏差(对照 php-clean-code):
| 问题 | 现状 |
|---|---|
| 方法过长 | handleInviteBind 远超 50 行建议 |
| 编排不清晰 | 校验 / 发奖 / 弹窗混在一个方法 |
| 返回值不规范 | fetchUserProfile() 返回 array{nickname,avatar},同模块已有 LuckyRewardUserProfileEntity(LuckyRewardLogic 已使用) |
| 弹窗条件偏窄 | enqueueInviterSuccessPopup 在 inviterSpinReward <= 0 时直接 return,有效助力成功但配置为 0 时不入队 |
弹窗能力已存在但未在编排层显式表达:
- 成功路径已调用
enqueueInviterSuccessPopup→PendingPopupService::enqueue→ 写user_pending_popup+WsService::notifyClientPOP(在线即时推送) - 离线/游戏中:
HomeEvent::appendLuckyRewardHomePopups从待弹队列补弹(05 邀请方案 §5、07 待弹窗中心) - 集成测已断言 pending 记录存在(
LuckyRewardInviteBindIntegrationTest),但未覆盖「成功必入队 + payload 结构」
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
InviteBindProcessContextDTO(readonly),承载本用例多步共享字段,避免 private 方法重复挂标量:
resolvedSource,cycleId,inviterSpinReward,inviteeSpinRewardinviterBizId,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)
- 成功即入队:
notifyInviterInviteSuccess在STATUS_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(内部已 WS);Logic 不重复调WsService - 离线/游戏中:依赖已有
user_pending_popup+HomeEvent::appendLuckyRewardHomePopups,无需 Logic 判断在线
用户资料返回 Entity
fetchUserProfile(int $uid): LuckyRewardUserProfileEntity替换 array 返回enqueueInviterSuccessPopup/buildSupportInvitePopup同步改用 Entity 字段(后者可顺带改,范围小)
测试
| 用例 | 文件 |
|---|---|
| 现有 early-return 单测 | LuckyRewardInviteLogicUnitTest 保持 |
| 集成:成功写 helper + pending popup | 已有;增强断言 popup_type、payload 含 title/content/spin_reward |
| 集成:第二次回调幂等不入队新 popup | 已有 already_processed;确认 popup 仍仅 1 条 |
运行:RUN_DB_TESTS=1 跑 LuckyRewardInviteBindIntegrationTest + 现有 Unit 测。
文档
- 更新
docs/requirements/lucky_rewards/05_slot_console_邀请助力与弹窗方案.md§5 payload 示例(若新增 title/content 字段) - 无需改 plan 文件
不在本次范围
buildSupportInvitePopup改返回 Entity(可选后续;本次仅改其用到的fetchUserProfile)- 服务端精确判断「是否在大厅/是否在线」(07 文档一期不做,依赖 WS + home flush)
- slot_hub / 客户端弹窗 UI 实现
自检
verify-slot-backend.sh+ dockerphp -l- 对照 Logic §3/§8:方法长度、中文 PHPDoc、无 Logic 返回 array、编排 DTO 不双份拷贝