# Policy Links（技术计划）

## 1. 目标与原则

**目标**：让客户端所有「隐私协议 / 使用协议」入口（至少包含：首次同意页 + 个人主页弹窗）统一通过后端接口获取链接，并支持按设备语言分发，缺省回退 EN。

**原则**：

- 客户端不写死协议 URL（避免发版更新）
- 请求失败不阻塞主流程（尤其是首次同意页）
- 语言选择策略一致、可解释、可排查（返回 `resolvedLang`）

## 2. 接口与契约

### 2.1 HTTP 接口

- **方法与路径**：`GET /v1/legal/links`
- **请求头**：
  - `Accept-Language`: `en` / `tc`（当前仅支持 EN / TC；客户端可按 i18n/设备语言映射为其中之一）
- **响应**（JSON）：
  - `privacyPolicyUrl: string`
  - `termsOfUseUrl: string`
  - `resolvedLang: "en" | "tc"`

### 2.2 语言回退（服务端决策）

- 若 `Accept-Language` 缺失/无法解析：回退 `en`
- 若目标语言的链接未配置：回退 `en`
- `resolvedLang` 表示服务端最终命中的语言（用于排查）

## 3. 后端实现方案（FastAPI）

### 3.1 配置来源

采用环境变量（Pydantic Settings）承载各语言链接配置：

- 推荐配置（EN）：
  - `LEGAL_PRIVACY_URL_EN`
  - `LEGAL_TERMS_URL_EN`
- 可选：
  - `LEGAL_PRIVACY_URL_TC` / `LEGAL_TERMS_URL_TC`

> 补充：若未配置 `LEGAL_*`，后端将使用**内置协议内容页**作为兜底，保证客户端点击后“必有内容”。

### 3.2 路由与业务逻辑

- 新增 `v1/legal` 路由模块
- 读取 `Accept-Language` 做轻量解析（只关心 en/tc，其他回退 en）
- 根据解析结果选择 URL；如缺失则回退到 EN URL

### 3.3 内置协议内容页（兜底，保证有内容）

- **页面接口**：
  - `GET /v1/legal/privacy`：隐私协议（HTML）
  - `GET /v1/legal/terms`：使用协议（HTML）
- **内容来源**：
  - 直接使用仓库文档：`设计说明文档/隐私协议.md`、`设计说明文档/用户使用协议.md`
- **语言策略**：
  - 仍按 `Accept-Language` 解析 `en/tc`；若文档缺少 tc 段落则回退 en
- **链接下发回退**：
  - 当 `LEGAL_*` 未配置（或显式走内置）时，`GET /v1/legal/links` 返回当前服务域名下的上述内置页面绝对 URL

### 3.4 兼容性与安全

- 要求链接为 HTTPS（推荐）；返回前做基本 URL 校验（通过响应模型约束）
- 不引入数据库变更

### 3.5 后端测试（建议）

- 单测覆盖：
  - `Accept-Language` 缺失 → `resolvedLang=en`
  - `tc` 命中/缺失回退
  - `zh-hant/zh-hk/zh-tw` 等输入能解析为 `tc`

## 4. 客户端实现方案（React Native + Expo）

### 4.1 统一 service

- 新增 `src/services/legalApi.ts`：
  - 统一拼接 `API_BASE_URL + /v1/legal/links`
  - 统一设置 `Accept-Language`
  - 统一超时与错误处理（抛错由调用方兜底）

### 4.2 接入点

- **首次同意页**（`app/(splash)/splash.tsx`）：
  - 页面展示时拉取链接
  - 拉取失败：不崩溃、不阻塞「同意并继续」
  - 点击协议入口：使用 `expo-web-browser` 打开下发链接
- **个人主页弹窗**（`components/home/ProfileModal.tsx`）：
  - 弹窗打开时拉取链接
  - 点击「隐私协议/使用协议」时打开下发链接（不再 `toastTodo`）

### 4.3 语言映射策略（客户端）

- 将客户端当前语言映射到 `en / tc` 二选一：
  - 任意 `zh*` → `tc`
  - 其他语言 → `en`

## 5. 可观测与排障

- 开发环境打印：
  - 请求地址、请求头（含 `Accept-Language`）
  - 拉取失败原因（仅 dev）
- 线上环境：
  - 不打印用户相关敏感信息（本接口仅链接配置，风险较低，但仍应克制日志）

## 6. 发布与回滚策略

- **灰度**：dev 环境可先不配 `LEGAL_*`，直接验证内置页面能打开；再逐步切到外部托管链接
- **补齐多语言**：逐步补齐 `zh-CN` / `zh-TW` 链接配置
- **回滚**：
  - 后端：回滚到仅 EN 配置仍可运行（客户端自动回退）
  - 客户端：即使接口失败也不阻塞主流程（保持可用）

## 7. 验收映射（对应 spec）

- 首次同意页 + 个人主页弹窗均从 API 获取链接
- 设备语言为 EN/TC 时分别打开对应链接
- 不支持语言回退 EN
- 后端不可达时 App 不崩溃，且同意流程可继续

