terry-wallet-login #15
133
docs/backend-requirements-wallet-favorites.md
Normal file
133
docs/backend-requirements-wallet-favorites.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# 后端需求与核对文档:钱包登录 + 收藏
|
||||
|
||||
> 面向后端(`Arkie-Library-Backend`)。
|
||||
> 本文是在前端审计「登录 + 收藏」bug 与 UI 重设计时,对后端现有实现的逐条核对,以及由此产生的后端待办。
|
||||
>
|
||||
> **核心结论:后端目前几乎已经满足前端全部需求。本次重设计与 bug 修复基本是纯前端工作。后端真正需要新增的只有少量「可选项」,外加几处需要确认的契约。请不要把已完成的功能再派一遍。**
|
||||
|
||||
日期:2026-06-02
|
||||
相关分支:`terry-wallet-login`
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句话给后端
|
||||
|
||||
> 钱包认证、TokenPocket 扫码、收藏列表/筛选/分页/可用性,**都已实现且符合前端契约**。
|
||||
> 下面 §1 是「已完成、勿动」的核对;§2 是「真正可能要后端做的事」;§3 是「前端会改但与后端无关,别误接」。
|
||||
|
||||
---
|
||||
|
||||
## 1. 已实现并符合前端契约(✅ 无需改动)
|
||||
|
||||
逐条核对自后端源码(`internal/handlers/wallet_auth.go`、`wallet_tp.go`、`favorites.go`、`public.go`、`cmd/server/main.go`)。
|
||||
|
||||
### 1.1 钱包认证
|
||||
|
||||
| 端点 | 状态 | 说明 |
|
||||
|---|---|---|
|
||||
| `POST /api/auth/wallet/nonce` | ✅ | 返回 `{nonce, message}`,message 含一次性码,写入 `wallet_auth_nonces`,TTL 15 分钟 |
|
||||
| `POST /api/auth/wallet/verify` | ✅ | EIP-191 `personal_sign` 验签恢复地址,签发 JWT |
|
||||
| `GET /api/auth/wallet/me` | ✅ | Bearer JWT → `{wallet, role:"user"}` |
|
||||
|
||||
关键事实(对前端 bug 很重要):
|
||||
|
||||
- **验签完全链无关。** `recoverPersonalSign` 只做 EIP-191 文本哈希恢复,不校验任何 chainId。签名消息文案是
|
||||
`"ARK Database — wallet sign-in … Sign this message to log in. No transaction or gas fee."`,**不引用任何链**。
|
||||
→ 因此前端登录时强制切到 BNB 链(`ensureBnbChain`)是**多余的**,删除它**不影响后端**。这是一项纯前端修复。
|
||||
- JWT:HS256,**有效期 30 天**(`SignUserWallet(..., 30*24h)`),无状态。
|
||||
- nonce 用后即删,过期自动清理。
|
||||
|
||||
### 1.2 TokenPocket 扫码登录
|
||||
|
||||
| 端点 | 状态 |
|
||||
|---|---|
|
||||
| `POST /api/auth/wallet/tp-login-request` | ✅ 生成 actionId/nonce/message/qrUrl,写 `wallet_tp_login_requests` |
|
||||
| `POST /api/auth/wallet/tp-callback` | ✅ 钱包回调写入签名,校验 `callbackToken` |
|
||||
| `GET /api/auth/wallet/tp-result?actionId=` | ✅ 轮询返回 `pending/completed/expired/failed` |
|
||||
|
||||
→ 前端把扫码从「手机端」挪到「桌面端」只是 UI 位置调整,**后端无需改动**。
|
||||
|
||||
### 1.3 收藏
|
||||
|
||||
| 端点 | 状态 | 支持的能力 |
|
||||
|---|---|---|
|
||||
| `GET /api/me/favorites` | ✅ | `q`(title/description/body_text/tag ILIKE)、`category`(slug)、`sort`(`favorited_at`/`published_at`/`hot`)、`includeUnavailable`(默认 true)、`page`/`limit`(≤100)、返回 `total`、tags、`favoriteCount`、`availability` |
|
||||
| `GET /api/me/favorites/ids?resourceIds=` | ✅ | 批量查询收藏状态 |
|
||||
| `POST /api/me/favorites/{id}` | ✅ | 加收藏,返回 `{ok,resourceId,favorited,favoriteCount}` |
|
||||
| `DELETE /api/me/favorites/{id}` | ✅ | 取消收藏,`favorite_count` 不低于 `favorite_base_count` |
|
||||
|
||||
关键事实:
|
||||
|
||||
- **下架资源可用性已支持。** `scanFavoriteItem` 会把 `status!='published' 或 is_public=false` 的资源标为
|
||||
`availability:"unavailable"`,且默认 `includeUnavailable=true` 仍返回。→ 前端「不可用资源卡片」逻辑后端已就绪。
|
||||
- `sort=hot` 定义 = `download_count + favorite_count + share_count` 降序。
|
||||
- 鉴权失败统一返回 **401**。
|
||||
|
||||
---
|
||||
|
||||
## 2. 真正可能需要后端做的事
|
||||
|
||||
按优先级。除 2.1 外多为**可选/按产品决定**。
|
||||
|
||||
### 2.1 【需确认】CORS 允许前端源 + Authorization 头
|
||||
|
||||
前端通过 `apiBase` 调 `/api/me/favorites`,并带 `Authorization: Bearer <jwt>`。
|
||||
若前端与 API 不同源,需确认 CORS 允许:
|
||||
|
||||
- 来源:前端正式域名(及预览/本地开发源)
|
||||
- 方法:`GET, POST, DELETE`
|
||||
- 请求头:`Authorization, Content-Type`
|
||||
|
||||
**动作**:确认现有 CORS 配置覆盖以上;若 `apiBase` 同源则可忽略。
|
||||
|
||||
### 2.2 【可选】服务端登出 / Token 失效
|
||||
|
||||
现状:JWT 无状态,前端「断开连接」只清本地 localStorage,旧 token 在 30 天内仍有效。
|
||||
|
||||
若产品需要「真正的远程登出 / 失效被盗 token」,后端需引入二选一:
|
||||
|
||||
- token 版本号(用户级 `token_version`,签发与校验时比对);或
|
||||
- token 黑名单(jti 撤销表)
|
||||
|
||||
**默认建议**:第一版**不做**,保持无状态。仅在有安全需求时再做。
|
||||
|
||||
### 2.3 【可选】缩短或可配置 JWT 有效期
|
||||
|
||||
现为固定 30 天。若希望更安全或可配置,可将 TTL 提为环境变量(如 `USER_JWT_TTL`)。
|
||||
|
||||
**默认建议**:30 天对「只验证地址、无资产操作」的场景可接受,可暂不动。
|
||||
|
||||
### 2.4 【按产品决定】MetaMask / imToken 扫码兜底(WalletConnect/Reown)
|
||||
|
||||
如果前端最终保留 WalletConnect 扫码路径:**后端无需任何改动**——`/verify` 接受任何 `personal_sign` 签名,与连接方式无关。
|
||||
此项列出只为说明「即便前端接了 WalletConnect,也不产生后端工作」。
|
||||
|
||||
### 2.5 【可选打磨】收藏列表 `q` 搜索性能
|
||||
|
||||
当前 `q` 用多列 `ILIKE '%..%'`,数据量大时无法走索引。量级变大后可考虑 `pg_trgm` 或全文索引。
|
||||
|
||||
**默认建议**:当前数据量下不必做,记录备查。
|
||||
|
||||
---
|
||||
|
||||
## 3. 前端会改、但与后端无关(请勿误派给后端)
|
||||
|
||||
这些是本次 bug/重设计的主体,**全部在前端完成,不涉及后端**:
|
||||
|
||||
1. 删除登录时强制切 BNB 链(`ensureBnbChain`)—— 验签链无关(见 §1.1)。
|
||||
2. 桌面登录弹窗简化为「使用浏览器钱包登录」单一主操作;扫码降级为「其他方式」。
|
||||
3. 扫码从手机端挪到桌面端。
|
||||
4. 手机端「打开钱包 App」死路修复(反馈、未安装兜底)。
|
||||
5. 移除/收敛未被登录流程使用的 RainbowKit/WalletConnect 装配(纯前端依赖与体积问题)。
|
||||
6. 全站「我的收藏」入口缺失(导航 / 手机菜单 / 钱包下拉加入口)。
|
||||
7. 收藏按钮状态视觉、收藏页筛选区移动端密度、空/错误状态打磨。
|
||||
8. token 过期时前端自动登出并引导重新登录(消费后端已返回的 401,无需后端改)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 给后端的「确认清单」
|
||||
|
||||
- [ ] §2.1 CORS 是否已允许前端源 + `Authorization` 头?(唯一可能的必做项)
|
||||
- [ ] 是否需要 §2.2 服务端登出/撤销?(默认否)
|
||||
- [ ] 是否需要 §2.3 可配置 JWT TTL?(默认否,维持 30 天)
|
||||
- [ ] 知悉:§3 全部为前端工作,无需后端介入。
|
||||
@@ -0,0 +1,183 @@
|
||||
# 钱包登录 + 收藏:重设计与 Bug 修复 设计文档
|
||||
|
||||
日期:2026-06-02
|
||||
分支:`terry-wallet-login`
|
||||
范围:登录弹窗、Header/菜单钱包入口、收藏按钮、我的收藏页面、收藏入口
|
||||
关联:
|
||||
- 需求简报 `.unipi/docs/generated/2026-06-01-wallet-favorites-ui-redesign-requirements.md`
|
||||
- 后端核对 `docs/backend-requirements-wallet-favorites.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把已上线的「钱包登录 + 收藏」从「功能能跑但设计未完善、桌面/手机均有 bug」提升到完成度合格:
|
||||
|
||||
1. 修复登录流程中的真实功能 bug(强制切链、桌面误导、手机死路)。
|
||||
2. 按已批准的极简原则重做登录弹窗。
|
||||
3. 补齐完全缺失的「我的收藏」入口。
|
||||
4. 打磨收藏按钮状态与收藏页(移动端筛选、空/错误/不可用状态)。
|
||||
|
||||
**关键事实**:经核对后端,钱包认证、TokenPocket 扫码、收藏接口(筛选/排序/分页/可用性/计数)均已实现并符合契约。**本次为纯前端工作**,后端仅需确认 CORS(见后端文档 §2.1)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 登录架构(决策已定)
|
||||
|
||||
三条路径共存,但 UI 上分主次:
|
||||
|
||||
| 路径 | 用途 | UI 位置 |
|
||||
|---|---|---|
|
||||
| `window.ethereum` 注入登录 | 桌面插件 / 钱包内置浏览器 | **主路径** |
|
||||
| TokenPocket 自写扫码(deep link + 轮询) | 中国稳定扫码 | 「其他方式」折叠区 |
|
||||
| RainbowKit / WalletConnect | MetaMask / imToken 扫码兜底 | 「其他方式」折叠区 |
|
||||
|
||||
**决策**:保留并**真正接上** RainbowKit(当前为未被调用的死代码)。
|
||||
**前置项**:需在环境变量配置有效的 `VITE_WALLETCONNECT_PROJECT_ID`(当前默认 `ark-library-dev-only` 无效)。WalletConnect 兜底在部分中国网络不稳定,UI 需提示。
|
||||
|
||||
签名验证链无关(后端 EIP-191 personal_sign recover,消息不引用任何链)。
|
||||
|
||||
---
|
||||
|
||||
## 3. Bug 修复清单(前端)
|
||||
|
||||
| # | 严重度 | 问题 | 修复 |
|
||||
|---|---|---|---|
|
||||
| B1 | 🟠 | 每次登录强制切 BNB 链(`ensureBnbChain`),多一个换网络弹窗,常见失败点 | 删除强制切链;`personal_sign` 不需要链 |
|
||||
| B2 | 🟠 | 桌面弹窗摆 3 个钱包按钮,点 TP/imToken 误弹「请安装」 | 桌面只留 1 个主操作「使用浏览器钱包登录」 |
|
||||
| B3 | 🟠 | 桌面无扫码(TP 扫码被包在仅手机分支) | 扫码移入桌面「其他方式」 |
|
||||
| B4 | 🟠 | 手机「打开钱包 App」是死路:无反馈、App 未装无兜底 | 加跳转反馈 + 未安装兜底(提示去下载) |
|
||||
| B5 | 🔴 | RainbowKit 整套加载但从未被登录流程调用 | 真正接成「其他方式」扫码兜底 |
|
||||
| B6 | 🔴 | 全站无「我的收藏」入口,页面只能手敲 URL | 加 3 处入口(见 §5) |
|
||||
| B7 | 🔴 | 钱包下拉只有地址 + 断开 | 下拉加「我的收藏」 |
|
||||
| B8 | 🟡 | 收藏 token 过期只弹失败 toast | 401 时自动登出并引导重新登录 |
|
||||
| B9 | 🟡 | `isMobileDevice` 把触屏 Mac/iPad 判为手机 | 收紧检测,避免桌面被推进 App 跳转流 |
|
||||
| B10 | 🟡 | 收藏页加载失败无重试 | 错误态加重试按钮 |
|
||||
| B11 | 🟡 | WalletConnect projectId 默认无效值 | 用 env,缺失时禁用扫码兜底并提示 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 登录弹窗设计
|
||||
|
||||
### 4.1 桌面版
|
||||
|
||||
结构(自上而下):
|
||||
1. 标题「连接钱包」
|
||||
2. 说明「签名仅用于验证钱包地址,不会发起交易,也不需要 Gas」
|
||||
3. **主按钮**「使用浏览器钱包登录」(金色)→ `window.ethereum` 注入流程
|
||||
4. 辅助说明「请使用已安装钱包插件的浏览器,例如 MetaMask」
|
||||
5. 折叠「其他登录方式」(**默认折叠**),展开后:
|
||||
- TokenPocket 扫码(第一项,中国常用)
|
||||
- MetaMask / imToken 扫码(WalletConnect,附不稳定提示)
|
||||
6. 关闭按钮
|
||||
7. 错误区(红色)
|
||||
|
||||
### 4.2 手机版
|
||||
|
||||
结构:
|
||||
1. 标题「连接钱包」+ 说明「请在钱包 App 中打开本站并签名登录,无交易、无 Gas」
|
||||
2. 若检测到注入钱包:**「使用当前钱包登录」**主按钮
|
||||
3. 否则:分组「打开钱包 App」+ 三个按钮(TokenPocket / MetaMask / imToken),带品牌图标
|
||||
- 点按尝试 deep link;未跳转/未安装 → 提示去下载(**不再死路**,修 B4)
|
||||
4. 折叠「其他方式(扫码)」默认折叠
|
||||
5. 关闭按钮 + 错误区
|
||||
|
||||
### 4.3 通用
|
||||
- 钱包按钮配品牌彩色图标。
|
||||
- 多语言预留文字长度(en/zh-CN/zh-TW/ko/ja/vi/id/ms),按钮不溢出。
|
||||
- 弹窗在手机上可滚动、不被遮挡。
|
||||
|
||||
---
|
||||
|
||||
## 5. 钱包入口与收藏入口
|
||||
|
||||
### 5.1 Header 钱包入口
|
||||
- 未登录:`Connect Wallet / 连接钱包` 主按钮(桌面右侧 / 手机菜单内)。
|
||||
- 已登录:短地址 `0x12…ab34` + 绿点;点击展开下拉。
|
||||
|
||||
### 5.2 钱包下拉(已登录,修 B7)
|
||||
顺序:完整地址 → **♥ 我的收藏**(新增)→ 断开连接。
|
||||
|
||||
### 5.3 「我的收藏」入口策略:**始终显示(方案 B)**
|
||||
- 桌面钱包下拉、手机菜单中**始终**显示「我的收藏」入口。
|
||||
- 未登录点击 → 落到 `/favorites` 的「连接钱包查看收藏」引导页(现状已有,保留)。
|
||||
- 手机菜单导航项中加「♥ 我的收藏」。
|
||||
|
||||
### 5.4 收藏按钮触发登录(保留现状逻辑)
|
||||
未登录点 ♥ → 打开登录弹窗 → 登录成功后自动补上本次收藏(`pendingAfterLogin` 已实现)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 收藏按钮(`FavoriteButton`)
|
||||
|
||||
四态,一眼可分:
|
||||
- 未收藏:空心 ♡,低对比底。
|
||||
- 已收藏:实心 ♥,品牌金填充。
|
||||
- 加载中:转圈(`LoaderCircle`)。
|
||||
- 请求中:禁用 + 降透明。
|
||||
|
||||
行为:
|
||||
- 点击 `preventDefault + stopPropagation`,不误触进详情(已实现,保留)。
|
||||
- 增加点击微动效(`active:scale`)。
|
||||
- 乐观更新,失败回滚 + 错误 toast(已实现,保留)。
|
||||
|
||||
摆放:推荐卡 / 最新 / 热门 / 内容流 / 收藏页卡片右上角,不挡主内容、不与下载/预览混淆。
|
||||
|
||||
---
|
||||
|
||||
## 7. 我的收藏页面 `/favorites`
|
||||
|
||||
### 7.1 未登录
|
||||
图标 + 标题 + 说明 + 「连接钱包」CTA(现状已有,保留视觉打磨)。
|
||||
|
||||
### 7.2 已登录
|
||||
- **桌面**筛选一行:搜索 + 排序 + 分类 + 搜索按钮(现状保留)。
|
||||
- **移动端**:搜索框单独一行;排序/分类收进**「筛选抽屉」**,解决现状 4 控件挤压(新增)。
|
||||
- 列表:收藏资源卡(封面/标题/描述/分类/类型/更新时间/收藏数/收藏按钮)。
|
||||
- 分页:上一页/下一页 + 页码(现状保留)。
|
||||
- 「清除筛选」当存在筛选时显示。
|
||||
|
||||
### 7.3 状态
|
||||
- **不可用/下架**:黄边 + 「不可用」标 + 不可点进详情 + 保留移除按钮(后端 `availability` 已支持)。
|
||||
- **空状态**:区分「还没有收藏」与「筛选无结果」,后者给清除筛选入口。
|
||||
- **错误**:加载失败提示 +(新增)**重试按钮**(修 B10)。
|
||||
- **加载**:4 张 skeleton,布局不跳。
|
||||
|
||||
排序选项:最近收藏 / 最近发布 / 热门(后端 `favorited_at`/`published_at`/`hot` 已支持)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 多语言
|
||||
所有新增/改动文案覆盖 8 语言(en、zh-CN、zh-TW、ko、ja、vi、id、ms),key 写入 `src/locales/*`。移动端按钮预留长文本。
|
||||
|
||||
---
|
||||
|
||||
## 9. 验收清单
|
||||
|
||||
登录:
|
||||
- [ ] 桌面弹窗只有 1 个主操作;扫码在折叠区。
|
||||
- [ ] 手机可打开 TP/MetaMask/imToken;未安装有兜底。
|
||||
- [ ] 登录不再强制切链。
|
||||
- [ ] RainbowKit 真正接通(projectId 有效时);无效时扫码兜底禁用并提示。
|
||||
- [ ] 已登录显示短地址,可断开。
|
||||
|
||||
收藏:
|
||||
- [ ] 钱包下拉、手机菜单均有「我的收藏」入口(始终显示)。
|
||||
- [ ] 收藏按钮四态清楚,不与卡片点击冲突。
|
||||
- [ ] 未登录点收藏 → 引导登录 → 自动补收藏。
|
||||
- [ ] token 过期自动登出并引导重登。
|
||||
|
||||
收藏页:
|
||||
- [ ] 桌面一行筛选;移动端筛选抽屉。
|
||||
- [ ] 不可用/空/错误(含重试)/骨架屏 完整。
|
||||
- [ ] Desktop 与 mobile 均验证。
|
||||
|
||||
质量门槛(实现后):`npx tsc --noEmit`、`npm run format:check`、`npm test` 全绿。
|
||||
|
||||
---
|
||||
|
||||
## 10. 不做(YAGNI)
|
||||
- 服务端登出 / token 撤销(保持无状态 JWT)。
|
||||
- ENS、链上读取、交易。
|
||||
- 收藏分组/文件夹、批量操作(本期不做)。
|
||||
- 收藏 `q` 全文索引优化(数据量小,暂不做)。
|
||||
Reference in New Issue
Block a user