Files
cursor/plans/转盘与agent分享归因_d2e94766.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

179 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: 转盘与Agent分享归因
overview: 实现「转盘分享双计agent + 转盘、agent 分享仅计 agent」wheel 与 user_agent 各一条 share_url 短码MQ 的 share_origin 直接来自 invite_code 查表console 仅 share_origin=wheel 时发转盘助力。
todos:
- id: console-origin
content: "slot_console: wheel 独立落库 origin=wheelLogic 仅 wheel 计助力invite-link/createShareUrl 走 wheel 短码"
status: completed
- id: user-mq
content: "slot_user: shouldNotifyAgentByInviteOrigin 含 wheelMQ share_origin 来自 share_url.origin"
status: completed
- id: agent-filter
content: "slot_agent建议: console 回调粗筛 share_origin=wheel"
status: completed
- id: tests-docs
content: 单测/集成测 + 需求文档/YApi/deploy 对齐(撤销 wheel→user_agent 映射说明)
status: completed
isProject: false
---
# 转盘分享 vs Agent 分享单向归因方案share_origin 双短码)
## 产品规则SSOT
| 分享入口 | share_url.origin | Agent 邀请 | Lucky Rewards 助力 |
| --- | --- | --- | --- |
| 转盘分享(`from=wheel` / invite-link | `wheel` | 算 | 算 |
| Agent 分享(`from=user_agent` | `user_agent` | 算 | **不算** |
## 方案选型(已确认)
**方案 A两条短码**`wheel``user_agent``share_url` 各存一行,同一邀请人可有 **两个** `short_code`
- MQ `share_origin` 由注册时 `invite_code``share_url.origin` 得到,**无需** `landing_scene`、**无需** FE 注册透传额外字段。
- 撤销当前 [`ShareService::resolvePersistOrigin()`](slot_console/app/service/ShareService.php) 把 `wheel → user_agent` 的映射。
```mermaid
sequenceDiagram
participant Inviter
participant Console as slot_console
participant User as slot_user
participant Agent as slot_agent
alt WheelShare
Inviter->>Console: createShareUrl from=wheel
Console->>Console: share_url origin=wheel 短码W
Note over Inviter: 分享短码W
User->>User: register invite_code=W
User->>Agent: MQ share_origin=wheel
Agent->>Console: callback share_origin=wheel
Console->>Console: success发Spin
else AgentShare
Inviter->>Console: createShareUrl from=user_agent
Console->>Console: share_url origin=user_agent 短码A
User->>User: register invite_code=A
User->>Agent: MQ share_origin=user_agent
Agent->>Console: callback share_origin=user_agent
Console->>Console: skipped_not_wheel
Note over Agent: agent关系照常
end
```
---
## 为何之前不能「只改 MQ 不改落库」
当前实现把 `from=wheel` **映射成** `user_agent` 落库(复用一条短码)。此时代码查表得到的 `origin` 永远是 `user_agent`MQ 无法出现 `share_origin=wheel`,除非 FE 在注册时强行覆盖 —— 那与 `landing_scene` 同类,且语义与 DB 不一致。
**双短码**后:`invite_code` 本身即归因,链路最简。
---
## 实现步骤
### 1. slot_consolewheel 独立短码 + 助力判定
**[`ShareService`](slot_console/app/service/ShareService.php)**
- **删除或停用** `resolvePersistOrigin()` 对 wheel 的 `user_agent` 映射;`SHARE_FROM_WHEEL` 直接作为 `createUrl()``$from` / `ShareUrlModel.origin`
- `isAgentShareFrom()` 保持 `user_agent` + `wheel`agent 文案接口仍适用)。
- 更新/替换 [`ShareServiceWheelFromTest`](slot_console/tests/Unit/ShareServiceWheelFromTest.php):断言 wheel 创建后 `origin=wheel`,且与 `user_agent` 为**不同** `short_code`(同一 uid 两行)。
**[`GiftController::createShareUrl()`](slot_console/app/api/controller/GiftController.php)**
- `from=wheel``createUrl(ShareService::SHARE_FROM_WHEEL)`,不再经 `resolvePersistOrigin`
**[`LuckyRewardInviteLogic::buildInviteLink()`](slot_console/app/api/logic/LuckyRewardInviteLogic.php)**
- 同上,直接 `createUrl(SHARE_FROM_WHEEL)`
- PHPDoc 更新:不再写「落库复用 user_agent 短码」。
**[`LuckyRewardInviteLogic::handleInviteBind()`](slot_console/app/api/logic/LuckyRewardInviteLogic.php)**
- 将现有「必须 `share_origin=user_agent`」改为 **必须 `share_origin=wheel`**
```php
if ($callbackDto->shareOrigin !== ShareService::SHARE_FROM_WHEEL) {
return $this->buildInviteBindResult(
InviteBindResultEntity::STATUS_SKIPPED_NOT_WHEEL,
'Invite code origin is not wheel'
);
}
```
**[`InviteBindResultEntity`](slot_console/app/entity/luckyReward/InviteBindResultEntity.php)**
- 新增 `STATUS_SKIPPED_NOT_WHEEL = 'skipped_not_wheel'`(保留旧 `skipped_not_user_agent` 常量亦可,新逻辑用新 status
- **不需要** `landing_scene` / `skipped_no_landing`(除非后续另有用途)。
**[`InviteBindCallbackDTO`](slot_console/app/dto/luckyReward/InviteBindCallbackDTO.php)**
- 已有 `shareOrigin`,无需新增 `landingScene`agent/SDK 侧 landing_scene 字段可留空,不读)。
### 2. slot_user发 MQ + agent 资格
**[`AbstractRegisterService::shouldNotifyAgentByInviteOrigin()`](slot_user/app/service/register/AbstractRegisterService.php)**
```php
return in_array($origin, ['user_agent', 'wheel'], true);
```
**[`notifyAgentInviteBind()`](slot_user/app/service/register/AbstractRegisterService.php)**
- `share_origin` 继续写 `$this->invite_share_origin`(来自 `share_url.origin`wheel 注册自然为 `wheel`
- **无需**新增 `landing_scene` 属性或 MQ 字段(与 agent 侧已有 `landing_scene` 字段兼容即可,留空)。
**[`resolveInviteUidByInviteCode()`](slot_user/app/service/register/AbstractRegisterService.php)** — 已读 `shareRow->origin`,双短码下无需改逻辑。
### 3. slot_agentconsole 回调粗筛(建议)
[`LuckyRewardInviteCallbackGatewayService::canNotifyInviteBind()`](slot_agent/app/service/LuckyRewardInviteCallbackGatewayService.php) 增加:
-`share_origin === 'wheel'` 时 HTTP 调 consoleagent 绑定、奖励、关系同步**不受影响**,仍处理全部 invite_bind MQ
- `user_agent` 绑定不再打 console减少无效请求。
### 4. 前端 / 产品说明
- 转盘页 Copy Link / 社媒分享:使用 **invite-link****create-share-url `from=wheel`** 返回的 `code`wheel 短码)。
- Agent 中心分享:继续使用 **`from=user_agent`** 返回的 `code`(另一条短码)。
- **不要混用**:用 agent 短码进转盘页,只计 agent、不计转盘 —— 符合产品规则。
### 5. 测试
| 仓库 | 用例 |
| --- | --- |
| slot_console | `ShareServiceWheelFromTest`:同 uid 存在 wheel + user_agent 两行、短码不同 |
| slot_console | `LuckyRewardInviteLogicUnitTest``share_origin=user_agent``skipped_not_wheel``wheel` + 新注册 → success |
| slot_console | `LuckyRewardInviteBindIntegrationTest``buildValidInviteBindPayload()``share_origin``wheel` |
| slot_user | `shouldNotifyAgentByInviteOrigin`wheel / user_agent 为 true其它为 false |
### 6. 文档与 YApi
- 更新 [`05_slot_console_邀请助力与弹窗方案.md`](docs/requirements/lucky_rewards/05_slot_console_邀请助力与弹窗方案.md) §2有效助力 = **新注册** + **`share_origin=wheel`**(双短码,非 landing_scene
- 更新 [`00_整体技术方案.md`](docs/requirements/lucky_rewards/00_整体技术方案.md) 归因段落。
- [`lucky_reward_deploy.md`](slot_console/doc/lucky_reward_deploy.md)、YApi **81** / invite-link说明 wheel 与 user_agent **不同 code**;撤销「复用 user_agent 短码」描述。
- innerapi 回调文档:`skipped_not_wheel``landing_scene` 标注为可选/未使用。
---
## 判定优先级console 更新后)
1. invalid_params
2. skipped_self_invite
3. skipped_old_user
4. **skipped_not_wheel**(原 skipped_not_user_agent 逻辑替换)
5. activity_closed
6. already_processed
7. success
---
## 风险与边界
- **历史已发出的 wheel 链接**:若当时用的是映射后的 `user_agent` 短码,升级后只计 agent、不计转盘新 wheel 链接需走 invite-link / `from=wheel` 拿新短码。可在发版说明中写清。
- **同一用户两条码**:运营/客服需知 wheel 与 agent 链接不可互换。
- **手工绑码**`share_url.origin` 决定 MQ `share_origin`;绑 agent 码不计转盘。
- **slot_sdk / agent** `landing_scene` 字段保留兼容,本期不参与判定。
---
## 涉及仓库
- **必改**`slot_console``slot_user`
- **建议改**`slot_agent`HTTP 粗筛 `share_origin=wheel`
- **不改**`slot_sdk`(已有 share_origin 转发)
- **FE**:使用正确短码即可,无 register 额外字段