docs: sync genarrative shared skills
Some checks failed
CI / verify (pull_request) Has been cancelled
Some checks failed
CI / verify (pull_request) Has been cancelled
This commit is contained in:
149
.hermes/skills/genarrative-profile-invite-flow/SKILL.md
Normal file
149
.hermes/skills/genarrative-profile-invite-flow/SKILL.md
Normal file
@@ -0,0 +1,149 @@
|
||||
---
|
||||
name: genarrative-profile-invite-flow
|
||||
description: 在 Genarrative 中排查或修改邀请码、邀请好友、首次登录后填写邀请码、我的页签邀请码兑换链路时使用。
|
||||
version: 1.0.0
|
||||
author: Hermes Agent
|
||||
license: MIT
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [Genarrative, 邀请码, referral, auth, profile, query-params, 前端]
|
||||
related_skills: []
|
||||
---
|
||||
|
||||
# Genarrative 邀请码与邀请好友流程
|
||||
|
||||
用于排查或修改 Genarrative 的邀请码读取、填写、兑换、邀请中心与“我的”页签相关能力。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 判断 URL query 参数中的邀请码是否被读取。
|
||||
- 修改邀请码填写入口、首次登录后引导或“我的”页签兑换入口。
|
||||
- 排查邀请码预填、兑换、已填写状态、邀请好友复制链接。
|
||||
- 修改邀请中心 API client 或前端 referral UI。
|
||||
- 回答用户关于“邀请码在哪里填 / 从哪里配置 / query 是否支持”的问题。
|
||||
|
||||
## 先做代码核对,不要只凭旧记忆回答
|
||||
|
||||
邀请码流程近期发生过迁移:不要默认认为登录窗口可填写邀请码。回答前优先搜索并核对当前代码,尤其是:
|
||||
|
||||
```bash
|
||||
cd <repo-root>
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
root=Path('src')
|
||||
terms=['RegistrationInviteModal','readInviteCodeFromLocation','referralRedeemCode','redeemRpgProfileReferralInviteCode','邀请码','inviteCode']
|
||||
for term in terms:
|
||||
print('\n---', term)
|
||||
for p in root.rglob('*'):
|
||||
if p.is_file() and p.suffix in ['.ts', '.tsx']:
|
||||
try:
|
||||
txt=p.read_text('utf-8')
|
||||
except Exception:
|
||||
continue
|
||||
if term in txt:
|
||||
for i, line in enumerate(txt.splitlines(), 1):
|
||||
if term in line:
|
||||
print(f'{p}:{i}:{line.strip()[:180]}')
|
||||
```
|
||||
|
||||
## 当前前端链路口径
|
||||
|
||||
### 1. AuthGate 中仍有旧 query 读取逻辑
|
||||
|
||||
文件:
|
||||
- `src/components/auth/AuthGate.tsx`
|
||||
|
||||
重点函数 / 状态:
|
||||
- `readInviteCodeFromLocation()`
|
||||
- `pendingInviteCode`
|
||||
- `showRegistrationInviteModal`
|
||||
- `RegistrationInviteModal`
|
||||
|
||||
当前旧逻辑会读取:
|
||||
- `?inviteCode=...`
|
||||
- `?invite_code=...`
|
||||
|
||||
并把值清洗为大写字母数字形式。
|
||||
|
||||
### 2. 登录窗口本身不再填写邀请码
|
||||
|
||||
不要回答“登录窗口可填写邀请码”。当前登录弹窗 `LoginScreen` 只负责登录 / 注册账号;邀请码填写已迁移到登录后的流程。
|
||||
|
||||
### 3. 新版“我的”页签兑换入口在 RpgEntryHomeView
|
||||
|
||||
文件:
|
||||
- `src/components/rpg-entry/RpgEntryHomeView.tsx`
|
||||
|
||||
重点常量 / 函数 / 状态:
|
||||
- `PROFILE_INVITE_QUERY_KEYS`:新版 query 支持 `inviteCode` / `invite_code`。
|
||||
- `normalizeProfileInviteQueryCode()`:去掉非字母数字并转大写。
|
||||
- `readProfileInviteCodeFromLocationSearch()`:从 `window.location.search` 读取并 normalize。
|
||||
- `pendingProfileInviteCode`:组件初始化时读取 query 邀请码。
|
||||
- `referralCenter`
|
||||
- `referralRedeemCode`
|
||||
- `setReferralRedeemCode`
|
||||
- `openProfilePopupPanel('redeem')`
|
||||
- `submitReferralRedeemCode()`
|
||||
- `canShowReferralRedeemShortcut`
|
||||
- `isWithinProfileInviteRedeemWindow(authUi?.user?.createdAt)`
|
||||
|
||||
UI 中“填邀请码”面板会使用 `referralRedeemCode` 作为输入值,并通过 `submitReferralRedeemCode()` 提交。当前新版实现会在首次打开“填邀请码”面板时用 `pendingProfileInviteCode` 预填输入框;例如 `/?inviteCode=spring-2026` 会预填为 `SPRING2026`。
|
||||
|
||||
### 4. 新版兑换 API client
|
||||
|
||||
文件:
|
||||
- `src/services/rpg-entry/rpgProfileClient.ts`
|
||||
|
||||
函数:
|
||||
- `getRpgProfileReferralInviteCenter()` -> `GET /profile/referrals/invite-center`
|
||||
- `redeemRpgProfileReferralInviteCode(inviteCode)` -> `POST /profile/referrals/redeem-code`
|
||||
|
||||
## 判断 query 参数是否真正接入新版流程
|
||||
|
||||
回答这类问题时要区分两层:
|
||||
|
||||
1. “是否存在旧 query 读取代码”:看 `AuthGate.tsx` 的 `readInviteCodeFromLocation()`。
|
||||
2. “query 是否接到新版填写入口”:看 `RpgEntryHomeView.tsx` 是否存在 `pendingProfileInviteCode` / `readProfileInviteCodeFromLocationSearch()`,以及打开 `openProfilePopupPanel('redeem')` 时是否把该值写回 `referralRedeemCode`。
|
||||
|
||||
当前新版流程已经支持 `inviteCode` / `invite_code` query 预填“我的”页签的“填邀请码”弹窗;登录窗口仍不填写邀请码。
|
||||
|
||||
如果未来代码只看到 AuthGate 读 query,但没有看到 `RpgEntryHomeView` 的 `referralRedeemCode` 从 query 初始化,就应回答:
|
||||
|
||||
> 代码里仍支持读取 `inviteCode` / `invite_code`,但新版“第一次登录后 / 我的页签”的填写入口未必已经完整接入该 query 值;需要继续把 query 值传入新版 profile referral redeem 流程。
|
||||
|
||||
## 修改建议顺序
|
||||
|
||||
如果要把 query 邀请码完整接入新版流程,建议按这个顺序做:
|
||||
|
||||
1. 先确定 query 参数规范:继续支持 `inviteCode` / `invite_code`,并统一 normalize。
|
||||
2. 在 `RpgEntryHomeView.tsx` 内用 `readProfileInviteCodeFromLocationSearch(window.location.search)` 初始化 `pendingProfileInviteCode`。
|
||||
3. 用 `pendingProfileInviteCode` 初始化 `referralRedeemCode`,并在 `openProfilePopupPanel('redeem')` 时重新写回,避免关闭后再次打开被清空。
|
||||
4. 如产品要求自动弹出:
|
||||
- 有 `pendingProfileInviteCode` 且未登录时,自动调用 `authUi?.openLoginModal()` 打开登录窗口;登录窗口仍不承接邀请码输入。
|
||||
- 有 `pendingProfileInviteCode` 且已登录时,自动将 `referralRedeemCode` 设为该 query 邀请码,并 `setProfilePopupPanel('redeem')` 直接打开“填邀请码”面板。
|
||||
- 用 `useRef` 记录是否已处理过当前 query,避免组件重渲染或 `authUi` 对象变化导致重复弹窗。
|
||||
- 当前项目实现已从“只预填、不自动弹”调整为上述行为。
|
||||
5. 兑换成功后清理输入态;是否清理 URL query 需由产品决定,避免破坏分享链接归因。
|
||||
6. 补测试覆盖:未登录访问带 query、已登录访问带 query 自动打开填写面板、我的页签手动打开、已填写邀请码、过期窗口、空/非法 query。当前已有 `RpgEntryHomeView.recharge.test.tsx` 覆盖:
|
||||
- `invite query opens login modal for logged out users`
|
||||
- `invite query opens redeem modal directly for logged in users`
|
||||
- `profile redeem invite modal reads query invite code after login`
|
||||
|
||||
## 常见坑
|
||||
|
||||
1. 不要把旧 `RegistrationInviteModal` 误认为当前唯一入口。
|
||||
2. 不要说“登录窗口可以填写邀请码”,除非当前代码重新把邀请码输入放回 `LoginScreen`。
|
||||
3. `AuthGate` 读到 query 不等于新版 `RpgEntryHomeView` 已经预填。
|
||||
4. “第一次登录后”与“我的页签”可能是两个入口;修改时要同时检查自动引导和手动入口。
|
||||
5. `canShowReferralRedeemShortcut` 受登录态、创建时间窗口、邀请中心初始化、已兑换状态共同影响。
|
||||
6. 邀请码 URL 通常由 `inviteLinkPath` 生成,复制逻辑在 `copyInviteInfo()`,不要只改兑换入口而忘记分享链接格式。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- `references/query-invite-code-flow-2026-05-07.md`:本次会话确认的邀请码 query 与新版 profile referral 入口关系。
|
||||
|
||||
## 验证标准
|
||||
|
||||
- 能明确回答当前 query 参数读取位置与参数名。
|
||||
- 能区分旧 AuthGate 邀请弹窗与新版“我的”页签 referral redeem。
|
||||
- 若实现改动,测试覆盖带 query 的登录后预填/弹窗行为,以及已填写邀请码时不再提示。
|
||||
@@ -0,0 +1,101 @@
|
||||
# Query 邀请码与新版 profile referral 入口关系(2026-05-07)
|
||||
|
||||
## 会话结论
|
||||
|
||||
用户指出:邀请码填写流程已经修改,登录窗口目前填写不了邀请码;邀请码填写被挪到了第一次登录后以及“我的”页签中。
|
||||
|
||||
因此后续回答或修改时不能只根据 `AuthGate` 里的旧逻辑判断“已支持”。
|
||||
|
||||
## 当前代码观察
|
||||
|
||||
### 旧 AuthGate 逻辑
|
||||
|
||||
文件:`src/components/auth/AuthGate.tsx`
|
||||
|
||||
- `readInviteCodeFromLocation()` 读取 `window.location.search`。
|
||||
- 支持 `inviteCode` / `invite_code`。
|
||||
- 会 normalize 为大写字母数字。
|
||||
- 写入 `pendingInviteCode`,传给 `RegistrationInviteModal`。
|
||||
|
||||
这只能说明“旧层仍有 query 读取”。
|
||||
|
||||
### 新版 profile referral 入口
|
||||
|
||||
文件:`src/components/rpg-entry/RpgEntryHomeView.tsx`
|
||||
|
||||
- “我的”页签标签为 `profile`。
|
||||
- 兑换输入状态:`referralRedeemCode`。
|
||||
- 打开填邀请码面板:`openProfilePopupPanel('redeem')`。
|
||||
- 提交兑换:`submitReferralRedeemCode()`。
|
||||
- 可显示快捷入口受 `canShowReferralRedeemShortcut` 控制。
|
||||
|
||||
文件:`src/services/rpg-entry/rpgProfileClient.ts`
|
||||
|
||||
- `getRpgProfileReferralInviteCenter()` -> `GET /profile/referrals/invite-center`
|
||||
- `redeemRpgProfileReferralInviteCode(inviteCode)` -> `POST /profile/referrals/redeem-code`
|
||||
|
||||
## 回答口径
|
||||
|
||||
如果被问“是否支持 query 参数读取邀请码”,应回答:
|
||||
|
||||
- 代码里仍有 query 读取,支持 `inviteCode` / `invite_code`。
|
||||
- 但登录窗口不再填写邀请码。
|
||||
- 新版入口在第一次登录后 / 我的页签;需要检查 `referralRedeemCode` 是否从 query 初始化。
|
||||
- 若没有该连接,就不能说新版流程完整支持 query 预填。
|
||||
|
||||
## 本次实现后的状态
|
||||
|
||||
已将 query 邀请码读取接入新版 `RpgEntryHomeView`:
|
||||
|
||||
- `PROFILE_INVITE_QUERY_KEYS = ['inviteCode', 'invite_code']`
|
||||
- `normalizeProfileInviteQueryCode()`:去除非字母数字并转大写。
|
||||
- `readProfileInviteCodeFromLocationSearch(window.location.search)`:读取 query 邀请码。
|
||||
- `pendingProfileInviteCode`:组件初始化时读取 query。
|
||||
- `referralRedeemCode`:用 `pendingProfileInviteCode` 初始化。
|
||||
- `openProfilePopupPanel('redeem')`:打开“填邀请码”时重新写入 `pendingProfileInviteCode`,避免首次打开或重新打开时丢失 query 预填。
|
||||
|
||||
当前行为:只预填,不自动弹出“填邀请码”面板;用户仍需在“我的”页签点击“填邀请码”。
|
||||
|
||||
验证测试:
|
||||
|
||||
```bash
|
||||
cd <repo-root>
|
||||
npm test -- --run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx
|
||||
npx eslint src/components/rpg-entry/RpgEntryHomeView.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx --max-warnings=0
|
||||
node scripts/check-encoding.mjs
|
||||
```
|
||||
|
||||
测试用例:`profile redeem invite modal reads query invite code after login` 覆盖 `/?inviteCode=spring-2026` 预填为 `SPRING2026`。
|
||||
|
||||
## 后续调整:带邀请码链接自动开窗
|
||||
|
||||
用户进一步明确期望:
|
||||
|
||||
- 如果用户未登录,直接打开登录窗口。
|
||||
- 如果用户已登录,直接打开邀请码填写窗口。
|
||||
|
||||
实现要点:
|
||||
|
||||
- 在 `RpgEntryHomeView.tsx` 中保留 `pendingProfileInviteCode` 作为 query 邀请码来源。
|
||||
- 新增 `autoOpenedInviteQueryRef = useRef(false)`,防止 effect 重复触发弹窗。
|
||||
- 新增 `useEffect`:
|
||||
- 无 query 邀请码或已处理过则 return。
|
||||
- 未登录:调用 `authUi?.openLoginModal()`。
|
||||
- 已登录:设置 `referralRedeemCode`、清空 referral 错误/成功提示、`setProfilePopupPanel('redeem')`。
|
||||
- 登录窗口仍不接收邀请码;邀请码只在登录后的 profile referral redeem 面板显示。
|
||||
- 仍然只自动打开和预填,不自动提交兑换。
|
||||
|
||||
补充测试:
|
||||
|
||||
- `invite query opens login modal for logged out users`
|
||||
- `invite query opens redeem modal directly for logged in users`
|
||||
- 原 `profile redeem invite modal reads query invite code after login` 同步调整为直接断言自动打开后的输入值。
|
||||
|
||||
验证命令仍为:
|
||||
|
||||
```bash
|
||||
cd <repo-root>
|
||||
npm test -- --run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx
|
||||
npx eslint src/components/rpg-entry/RpgEntryHomeView.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx --max-warnings=0
|
||||
node scripts/check-encoding.mjs
|
||||
```
|
||||
Reference in New Issue
Block a user