# 后端需求与核对文档：钱包登录 + 收藏

> 面向后端（`Arkie-Library-Backend`）。
> 本文是在前端审计「登录 + 收藏」bug 与 UI 重设计时，对后端现有实现的逐条核对，以及由此产生的后端待办。
>
> **核心结论：后端目前几乎已经满足前端全部需求。本次重设计与 bug 修复基本是纯前端工作。后端真正需要新增的只有少量「可选项」，外加几处需要确认的契约。请不要把已完成的功能再派一遍。**

日期：2026-06-02（2026-06-02 更新：新增 §0.5 部署阻塞）
相关分支：`terry-wallet-login`

---

## 0.5 🔴 关键阻塞：线上后端是旧版本，收藏与 TokenPocket 端点未部署

> 源码（`Arkie-Library-Backend`）里这些端点都有；但**当前线上 / dev 代理指向的后端**（`https://ark-library.com/apnew`）是**旧构建**，下列路由全部 404。
> **前端已就绪，但收藏与 TokenPocket 扫码登录在生产上无法工作，直到后端把含这些路由的版本部署上线。** 这正是用户实测「收藏用不了、扫码登录用不了」的根因。

curl 实证（经 vite dev 代理打到线上后端，2026-06-02）：

| 端点 | 方法 | 实测状态 | 结论 |
|---|---|---|---|
| `/api/auth/wallet/nonce` | POST | **200** | ✅ 已部署 |
| `/api/auth/wallet/verify` | POST | **400**（入参错误）| ✅ 已部署 |
| `/api/auth/wallet/me` | GET | **401**（需鉴权）| ✅ 已部署 |
| `/api/auth/wallet/tp-login-request` | POST | **404** | ❌ 未部署 |
| `/api/auth/wallet/tp-result` | GET | **404** | ❌ 未部署 |
| `/api/auth/wallet/tp-callback` | POST | **404** | ❌ 未部署 |
| `/api/me/favorites` | GET | **404**（应为 401）| ❌ 未部署 |
| `/api/me/favorites/ids` | GET | **404** | ❌ 未部署 |
| `/api/me/favorites/{id}` | POST | **404** | ❌ 未部署 |

**后端动作（必做，按优先级最高）：**
1. 部署包含 favorites（`internal/handlers/favorites.go`）+ TokenPocket（`internal/handlers/wallet_tp.go`）路由的后端版本（`cmd/server/main.go` 已注册这些路由）。
2. 为 TokenPocket 登录设置 `PUBLIC_BASE_URL`（`buildTokenPocketSignURL` 需要它生成回调 URL，否则 tp-login-request 会 500）。
3. 确保 `wallet_tp_login_requests` / `user_favorites` 等表已迁移（`EnsureWalletAuthSchema` 会建表）。

> 复测命令：`curl -s -o /dev/null -w "%{http_code}" -X POST https://ark-library.com/apnew/api/auth/wallet/tp-login-request -d '{}'` 应返回 200（且 body 的 `qrUrl` 形如 `tpoutside://pull.activity?param=...`）。

---

## 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 全部为前端工作，无需后端介入。
