spark-server/docs/popadmin-pull-requirements.md
DESKTOP-L8OUL9F\gfl0034 753c276496 feat: PopAdmin 拉取管理、签到能量 API 与图片 150KiB 硬上限
管理端按用户查询开启通讯录/相册/媒体拉取;服务端写入路径自动压图;新增 /v1/sign 与 /v1/energy。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-02 12:44:06 +08:00

236 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PopAdmin「通讯录 / 用户相册 / 媒体拉取」建设需求
| 项 | 内容 |
|---|---|
| 入口 | https://popadmin.wyrs365.com/popadmin/index.html |
| 适用端 | PopAdmin 管理端、SparkApk 客户端、服务端 sync API |
| 文档用途 | 产品 / 研发对齐;验收与联调依据 |
| 状态 | 需求整理(已沟通合并去重) |
---
## 1. 背景与目标
在 PopAdmin 中建设(或完善)三个与用户本地数据同步相关的子页面:**通讯录**、**用户相册**、**媒体拉取**。管理员可按用户查询、查看授权状态、按需开启拉取并设定数量上限;客户端在用户知情同意后按服务端策略上传。
**核心目标:**
1. 管理端可按 **用户 id / 邮箱** 精准查询并操作单用户拉取策略。
2. 拉取必须同时满足:**开关开启 + 数量上限内 + 用户已授权**;任一不满足则不得实际拉取/上传。
3. 杜绝「管理端开关已开、但用户未授权」时仍发生同步的误操作与合规风险。
4. 客户端可弱化管理向调试文案,但**禁止**无提示、无感知的隐蔽外传。
---
## 2. 三页范围
| 页面 | 管理能力 | 同步内容 | 备注 |
|---|---|---|---|
| **通讯录** | 查看 / 管理通讯录拉取策略与结果 | 用户通讯录(知情同意后同步) | 开关 + 数量 + 授权状态 |
| **用户相册** | 查看 / 管理相册拉取策略与结果 | 相册中的图片、视频 | 默认从**时间戳最新**开始拉;开关 + 数量 + 授权 |
| **媒体拉取** | 查看 / 管理媒体(以文档为主)拉取策略与结果 | **主目标:文档**(PDF / Excel / Word 等);图、视频可同页保留 | 默认同最新优先;开关 + 数量 + 授权 |
三页共用同一套查询、行布局、授权展示、开关默认策略、每用户数量、以及**未授权安全门禁**。
---
## 3. 查询与行布局
### 3.1 查询
- 支持按用户 **id** 查询。
- 支持按用户 **邮箱** 查询。
- **全局默认**不对全量用户开放拉取操作:须先查询到对应用户后,方可对该用户操作开启拉取。
### 3.2 每用户行布局(建议顺序)
```text
[拉取开关] → [是否已授权] → [用户id] → …
```
后续列可按页面补充:邮箱、拉取数量、最近同步时间、同步结果摘要等,但**开关与授权状态必须靠前、一眼可见**。
---
## 4. 授权状态
- **授权状态**通过 token(或等价凭证)从客户端 / 前端侧获取并在管理端展示。
- 展示取值至少区分:**已授权** / **未授权**。
- 「已授权」含义:用户已对**该类数据同步**给出可理解的知情同意(可按能力拆分:通讯录 / 相册 / 媒体文档等,具体粒度见「待决」)。
- 管理端展示的授权态须与服务端用于门禁校验的权威状态一致(或可追溯同源),避免「页面显示已授权、服务端判未授权」或相反。
---
## 5. 拉取开关与默认策略
| 规则 | 说明 |
|---|---|
| 全局默认 | **所有用户 id 的拉取开关默认关闭** |
| 可操作前提 | 仅在查询到对应用户后,管理员才可尝试开启该用户的拉取开关 |
| 每用户独立 | 通讯录 / 相册 / 媒体拉取的开关彼此独立(互不影响),同一能力下按用户分别配置 |
| 与授权关系 | 开关开启**不等于**允许拉取;必须叠加授权与数量门禁(见 §8、§9) |
---
## 6. 每用户拉取数量
- **每用户可分别设置**拉取数量上限(各页面 / 各能力独立配置)。
- 实际同步条数不得超过该上限;服务端与客户端均须尊重上限。
- 数量变更后,新上限立即作用于后续 sync;已入库数据是否回扫 / 是否补拉见「待决」。
---
## 7. 排序规则
| 页面 | 默认拉取顺序 |
|---|---|
| 用户相册 | 按媒体 **时间戳从新到旧**(最新优先) |
| 媒体拉取 | 同:**最新优先**(文档列表;若同页含图/视频,建议同一时间序策略) |
| 通讯录 | 无「时间戳最新」语义时,按产品约定的稳定顺序即可(如姓名 / 系统序);不强制「最新优先」 |
---
## 8. 未授权防护(重点:开关误开场景)
### 8.1 问题定义
**场景:** 管理端将某用户的「拉取开关」打开,但该用户**尚未授权**(无有效同意 / token 无效 / 未完成知情确认)。
**要求:** 此种情况下**不得**发生实际拉取、上传或外传。开关本身不能绕过授权。
### 8.2 管理端
须采用下列策略之一(推荐组合:**前端禁止 + 服务端拒绝**):
1. **禁止开启(推荐默认)**
- 用户为「未授权」时,拉取开关不可打开(disabled),并明确提示原因(如:「用户未授权,无法开启拉取」)。
2. **开启时强提示 + 服务端拒绝**
- 若产品坚持允许「先开开关、等用户授权后再生效」,则开启时必须弹出**强提示**:当前未授权,**不会**实际拉取,直至用户完成授权;且服务端对未授权请求一律拒绝。
3. **列表态提示**
- 行内清晰展示「开关开 / 未授权 → 实际未拉取」的状态,避免运营误以为已在同步。
### 8.3 客户端(SparkApk)
- **无有效授权 / 同意时,不得上传**对应数据。
- **禁止**无感知、隐蔽外传(即使用户界面弱化了「同步日志 / 调试记录」等管理向字样)。
- 真正上传前须有用户可理解的授权或确认流程。
- 客户端须遵守服务端下发的:**开关、数量上限、授权**三重门禁;本地不得以「开关曾开过」等理由绕过。
### 8.4 服务端
- sync / 拉取相关 API **必须**同时校验:
1. 该用户该能力的 **拉取开关为开启**;
2. 本次请求 / 累计在 **数量上限**内;
3. 该用户对该能力 **已授权**(有效同意)。
- 任一条件失败 → **拒绝拉取请求**,返回明确错误码 / 文案;不得部分静默成功。
- 即使管理端 UI 被绕过或开关字段被直接改库为开,**未授权时服务端仍须拒绝**。
### 8.5 一句话原则
> **未授权 = 零上传。** 管理端开关只是必要条件之一,绝不是充分条件。
---
## 9. 服务端校验
对客户端发起的同步 / 上传 / 拉取指令类接口,统一门禁:
| 校验项 | 失败时行为 |
|---|---|
| 拉取开关开启 | 拒绝;记录可审计日志(用户 id、能力类型、原因) |
| 数量上限 | 拒绝超额部分或整单;不得超限入库 |
| 已授权 | 拒绝;与 §8 一致 |
建议:
- 授权校验以服务端权威状态为准(由客户端上报同意结果并经服务端确认 / 签发,而非仅信客户端本地布尔值)。
- 管理端改开关 / 改数量的接口与 sync 接口权限分离:仅管理员可改策略;仅持用户凭证的客户端可 sync,且仍过三重门禁。
---
## 10. 客户端约束(SparkApk)
| 要求 | 说明 |
|---|---|
| 可隐藏管理向文案 | 可弱化或隐藏面向用户的「同步日志 / 调试记录」等管理调试字样 |
| 不可静默外传 | **不可**在无用户可理解授权/确认的情况下上传通讯录、相册、文档等 |
| 上传前确认 | 真正上传前须有用户可理解的授权或确认 |
| 三重门禁 | 遵守服务端:**开关开 + 数量内 + 已授权**;本地亦应短路,减少无效请求 |
| 无有效授权 | 不得发起实际上传;网络层失败不得改走「静默重试隐蔽通道」 |
---
## 11. 媒体拉取与文档
- **主目标:** 查看与拉取用户设备上的**文档类**文件(如 PDF、Excel、Word 等)。
- 图片、视频可在同一「媒体拉取」页保留展示 / 策略,但产品优先级以文档为主。
- 默认拉取顺序:**时间戳最新优先**(与用户相册一致)。
- 同样适用:开关、每用户数量、授权状态、以及 §8 未授权防护与 §9 服务端校验。
---
## 12. 验收标准
### 12.1 管理端(三页共性)
- [ ] 支持用户 **id**、**邮箱** 查询。
- [ ] 行布局含:**拉取开关 → 是否已授权 → 用户 id → …**。
- [ ] 授权状态正确展示已授权 / 未授权。
- [ ] 未查询到用户前,不能对「全局默认关闭」的用户随意批量开启(符合默认关闭策略)。
- [ ] 可为每用户分别设置拉取数量。
- [ ] **未授权时**:无法开启开关,**或**开启时有强提示且标明不会实际拉取。
### 12.2 未授权防护(强制)
- [ ] 开关开 + 未授权 → **客户端不上传**。
- [ ] 开关开 + 未授权 → **服务端 sync 拒绝**。
- [ ] 无用户可理解授权/确认时,APK **无隐蔽外传**。
- [ ] 管理端 UI 清晰反映「开了开关但未授权 = 未在拉取」。
### 12.3 分页面
- [ ] **通讯录**:策略与结果可查;知情同意后方可同步。
- [ ] **用户相册**:图/视频;最新优先;开关 + 数量 + 授权。
- [ ] **媒体拉取**:文档为主;最新优先;图视频可同页;开关 + 数量 + 授权。
### 12.4 客户端 APK
- [ ] 上传前有可理解授权/确认。
- [ ] 遵守服务端三重门禁。
- [ ] 可隐藏管理向调试字样,但不降低授权强度。
---
## 13. 待决 / 风险
| 项 | 说明 | 建议 |
|---|---|---|
| 授权粒度 | 通讯录 / 相册 / 文档是否共用一次同意,还是分能力授权 | 产品定稿;服务端按能力存授权态更稳妥 |
| 「先开开关、后授权」 | 是否允许管理端在未授权时打开开关仅作预置 | 若允许,必须强提示 + 服务端拒绝直至授权 |
| 数量变更与补拉 | 提高上限后是否自动补拉历史;降低上限是否裁剪已存 | 需产品规则 |
| 双 JWT | Go IM 与 Java `email_user` 两套 JWT 若仍并存 | **已知约束 / 待统一**;联调时注意用对账号体系 |
| 授权态展示源 | token / 客户端上报 / 服务端表 | 须避免管理端展示与门禁源不一致 |
| 合规文案 | 用户侧授权弹窗文案、范围说明 | 法务/产品审阅后再定稿 |
---
## 附录 A. 联调账号与环境
| 用途 | 信息 |
|---|---|
| 测试邮箱用户 | `email_user`:`spark.test.01@wyrs.test.local`,`userId=1017` |
| Go IM | `test01@spark.com` |
| 模拟器 | 夜神,端口 **62001** |
联调时注意:若 **双 JWT**(Go IM vs Java `email_user`)仍存在,管理端查询、客户端登录、sync 鉴权可能分属不同体系,属已知约束,统一前需在用例中标明所用身份。
---
## 附录 B. 需求来源摘要
- 入口与三页建设范围(通讯录 / 用户相册 / 媒体拉取)。
- 查询(id / 邮箱)、行布局、授权展示、默认关、每用户数量、最新优先。
- **安全门禁:** 未授权时即使开关开启也不得拉取;管理端限制或强提示;客户端禁止隐蔽外传;服务端三重校验。
- SparkApk:可隐藏管理向调试字样,不可静默外传;上传前须用户可理解授权。
- 联调账号与双 JWT 待统一项。
`)