fix:小组件- PUSH
This commit is contained in:
122
spec_kit/Policy Links/plan.md
Normal file
122
spec_kit/Policy Links/plan.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# 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 不崩溃,且同意流程可继续
|
||||
|
||||
127
spec_kit/Policy Links/spec.md
Normal file
127
spec_kit/Policy Links/spec.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Policy Links(高层规范)
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
当前客户端首次进入的同意页(Splash Consent)需要在底部提供「隐私协议」「用户使用协议」的查看入口。为避免协议链接写死在客户端、并支持多语言分发,本需求将**协议链接放到后端统一管理与下发**,客户端按设备语言展示对应链接。
|
||||
|
||||
### 目标
|
||||
|
||||
- **首进页补充协议链接**:在客户端首次进入的同意页底部展示「隐私协议」「用户使用协议」链接入口
|
||||
- **后端托管与可配置**:协议链接由后端配置与下发,支持后续更新而无需发版
|
||||
- **按设备语言下发**:后端根据客户端设备语言返回对应语言的链接
|
||||
- **默认 EN**:当语言缺失/不支持/无法识别时,回退为英文(EN)链接
|
||||
|
||||
### 非目标(本阶段不做)
|
||||
|
||||
- 不做复杂的协议版本强制重同意机制(例如协议更新后要求重新同意)
|
||||
- 不规定协议内容必须以何种格式(Markdown/HTML/PDF)存储;本需求仅要求“链接由后端提供并可访问”
|
||||
|
||||
## 2. 需求范围与交付物
|
||||
|
||||
### 范围
|
||||
|
||||
- 客户端:
|
||||
- 首次进入同意页底部展示 2 个协议链接入口,并在点击时打开对应链接
|
||||
- **个人主页弹窗(ProfileModal)**中「隐私协议」「用户使用协议」入口也必须使用同一套后端下发链接(不再写死/不再 TODO)
|
||||
- 后端:提供可被客户端请求的协议链接下发能力,并支持按语言返回不同链接
|
||||
|
||||
### 交付物
|
||||
|
||||
- 后端:新增/扩展一个“协议链接配置与下发”的接口(或纳入现有配置接口),并可按语言返回
|
||||
- 客户端:同意页 UI 补充协议链接入口,且链接来源改为后端下发(含兜底策略)
|
||||
- 文档:本 spec
|
||||
|
||||
## 3. 用户体验与交互(高层)
|
||||
|
||||
### 3.1 展示位置
|
||||
|
||||
- 位置:客户端**首次进入同意页**底部,「同意并继续」按钮下方(或同等位置)展示:
|
||||
- **隐私协议**
|
||||
- **用户使用协议**
|
||||
|
||||
### 3.2 点击行为
|
||||
|
||||
- 点击后打开对应链接内容:
|
||||
- 推荐使用 App 内 WebView(不离开 App),也允许外部浏览器打开(实现阶段在客户端方案中确定)
|
||||
- 若链接无法获取或打开失败:
|
||||
- 仍允许用户继续使用“同意并继续”(不阻塞主流程)
|
||||
- 入口可隐藏或展示为不可点击状态(实现阶段确定,但需保持体验一致且不崩溃)
|
||||
|
||||
## 4. 语言策略
|
||||
|
||||
### 4.1 语言来源
|
||||
|
||||
- 客户端以**设备语言/应用当前语言**作为请求依据(优先级由客户端既有 i18n 策略决定)
|
||||
- 客户端请求应携带语言信息(例如 `Accept-Language` 头或显式参数)
|
||||
|
||||
### 4.2 默认与回退
|
||||
|
||||
- 默认语言:**EN**
|
||||
- 回退策略(必须满足):
|
||||
- 语言缺失/空值 → EN
|
||||
- 语言不支持 → EN
|
||||
- 后端配置缺失对应语言 → EN
|
||||
|
||||
### 4.3 支持语言(建议)
|
||||
|
||||
当前多语言仅支持 **EN / TC**,建议最少覆盖:
|
||||
|
||||
- `en`
|
||||
- `tc`
|
||||
|
||||
> 说明:当前协议文档可能包含英文与繁体版本;当 tc 版本缺失时,系统会回退到 EN。
|
||||
|
||||
## 5. 后端职责(高层)
|
||||
|
||||
### 5.1 配置管理
|
||||
|
||||
- 后端存储并维护协议链接配置,至少包含:
|
||||
- `privacy_policy_url`
|
||||
- `terms_of_use_url`
|
||||
- 配置需支持按语言维度区分(例如每个语言各一套 URL)
|
||||
- **兜底要求**:当外部托管链接未配置或不可用时,后端应提供可访问的内置协议页面(保证客户端点击“必有内容”)
|
||||
|
||||
### 5.2 下发接口(建议形态)
|
||||
|
||||
- 提供一个可被客户端调用的接口,用于获取协议链接(示例,仅表达形态,不强制路径):
|
||||
- `GET /v1/legal/links`
|
||||
- 输入(建议):
|
||||
- 语言:通过 `Accept-Language` 或 `lang` 参数(例如 `en`/`tc`)
|
||||
- 可选:平台、App 版本(用于灰度/差异化配置)
|
||||
- 输出(建议):
|
||||
- `privacyPolicyUrl: string`
|
||||
- `termsOfUseUrl: string`
|
||||
- `resolvedLang: string`(后端最终命中的语言,便于排查)
|
||||
|
||||
### 5.3 可用性与安全
|
||||
|
||||
- 链接必须可被公网访问(或在 App 可访问的网络环境可达)
|
||||
- 推荐使用 HTTPS
|
||||
- 若后端短时不可用,不应导致客户端首进流程崩溃
|
||||
|
||||
## 6. 客户端职责(高层)
|
||||
|
||||
- 在首次进入同意页渲染时,向后端请求协议链接
|
||||
- 根据返回结果渲染「隐私协议」「用户使用协议」入口
|
||||
- 若请求失败或返回缺失:
|
||||
- 按本 spec 的默认回退策略处理(最终保证 EN)
|
||||
- 不阻塞“同意并继续”主流程
|
||||
- (建议)对获取到的链接做轻量校验(非空字符串、看起来是 URL),避免渲染异常
|
||||
|
||||
## 7. 验收标准
|
||||
|
||||
- **首进展示**:首次进入同意页时,底部出现「隐私协议」「用户使用协议」入口
|
||||
- **语言分发**:
|
||||
- 设备语言为 EN 时,打开英文链接
|
||||
- 设备语言为已支持的其他语言(如 `zh-CN`/`zh-TW`)时,打开对应语言链接
|
||||
- 设备语言为不支持语言时,默认回退打开 EN 链接
|
||||
- **稳定性**:后端不可达/返回异常时:
|
||||
- App 不崩溃
|
||||
- “同意并继续”仍可正常进入后续流程
|
||||
|
||||
## 8. 风险与注意事项
|
||||
|
||||
- **法务一致性**:各语言协议内容差异需要法务确认;若部分语言缺失,需明确 `zh-CN` 的回退策略(到 EN 还是 `zh-TW`)
|
||||
- **链接长期可用**:后端配置的 URL 需保证稳定与可访问性(避免过期链接导致合规风险)
|
||||
- **缓存与更新**:若客户端做本地缓存,需要确保更新后能及时刷新(本期不强制,后续可扩展)
|
||||
|
||||
68
spec_kit/Policy Links/tasks.md
Normal file
68
spec_kit/Policy Links/tasks.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Policy Links(任务清单)
|
||||
|
||||
> 说明:本清单按 `plan.md` 拆分为可执行任务。
|
||||
> 状态含义:`[ ]` 未完成,`[x]` 已完成。
|
||||
|
||||
## 0. 前置检查
|
||||
|
||||
- [x] 确认客户端存在至少两处协议入口:首次同意页、个人主页弹窗(ProfileModal)
|
||||
- [x] 确认后端路由前缀体系为 `/v1/*`
|
||||
|
||||
## 1. 后端(FastAPI)——协议链接下发
|
||||
|
||||
### 1.1 配置项(环境变量)
|
||||
|
||||
- [x] 在后端配置类中新增协议链接配置(EN 兜底 + 可选 TC)
|
||||
- [x] 在 `server/env.example` 补充 `LEGAL_*` 配置示例与注释
|
||||
|
||||
### 1.2 API 路由与返回结构
|
||||
|
||||
- [x] 新增 `GET /v1/legal/links` 接口
|
||||
- [x] 解析 `Accept-Language`(轻量:只关心 `en/tc`,其他回退 `en`)
|
||||
- [x] 支持语言缺失/未配置时回退 EN
|
||||
- [x] 返回 `resolvedLang` 便于排查
|
||||
- [x] 在 `app/main.py` 注册 legal router
|
||||
|
||||
### 1.3 代码质量与最小校验
|
||||
|
||||
- [x] 确保后端代码可被 `python3 -m compileall` 编译通过
|
||||
- [x] (可选)补充单测:覆盖缺省、tc、zh-hant/zh-hk/zh-tw 等输入解析与回退(unittest)
|
||||
|
||||
### 1.4 内置协议内容页(兜底,保证有内容)
|
||||
|
||||
- [x] 新增 `GET /v1/legal/privacy`、`GET /v1/legal/terms` 两个 HTML 页面接口
|
||||
- [x] 未配置 `LEGAL_*` 时,`GET /v1/legal/links` 自动回退到内置页面绝对 URL
|
||||
- [x] 更新后端配置默认值:`LEGAL_*` 未配置时使用内置页面(`__internal__` 哨兵)
|
||||
|
||||
## 2. 客户端(React Native + Expo)——统一获取并打开链接
|
||||
|
||||
### 2.1 新增统一请求封装
|
||||
|
||||
- [x] 新增 `client/src/services/legalApi.ts`
|
||||
- [x] 统一设置 `Accept-Language`(将客户端当前语言映射到 `en/tc`)
|
||||
- [x] 统一超时与错误处理(抛错给调用方兜底)
|
||||
|
||||
### 2.2 首次同意页接入
|
||||
|
||||
- [x] 将 `client/app/(splash)/splash.tsx` 的写死 URL 改为调用 `fetchLegalLinks()`
|
||||
- [x] 拉取失败不崩溃、不阻塞「同意并继续」
|
||||
- [x] 点击协议入口用 `expo-web-browser` 打开后端下发链接
|
||||
|
||||
### 2.3 个人主页弹窗接入
|
||||
|
||||
- [x] 将 `client/components/home/ProfileModal.tsx` 的 `toastTodo` 替换为打开后端下发链接
|
||||
- [x] 弹窗打开时拉取链接(失败不影响其他功能)
|
||||
- [x] 修复 TypeScript 定时器类型(RN 环境兼容)
|
||||
|
||||
### 2.4 客户端最小验证
|
||||
|
||||
- [x] 运行现有 `vitest` 测试通过(`npm test`)
|
||||
- [x] (可选)补充一个轻量测试:`buildAcceptLanguage()` 映射规则(en/tc)
|
||||
|
||||
## 3. 文档与追踪
|
||||
|
||||
- [x] `spec_kit/Policy Links/spec.md` 增补:个人主页弹窗也必须使用同一 API;接口路径统一为 `GET /v1/legal/links`
|
||||
- [x] 生成 `spec_kit/Policy Links/plan.md`
|
||||
- [x] 生成 `spec_kit/Policy Links/tasks.md`
|
||||
- [x] 在 `spec_kit/overview.md` 的 `Policy Links` 条目下标记“任务清单已执行完毕(已完成编码)”
|
||||
|
||||
Reference in New Issue
Block a user