lty/qy_lty/.planning/PROJECT.md
pmc ab3d728a08 docs(qy_lty): 启动 Milestone v1.0 通用凭据槽位
PROJECT.md 加入「本期 Milestone」段:全局单例 APP ID + Access Token 凭据存储,
管理端读写 + 客户端读取 + 日志脱敏;前端联动 milestone 在 qy-lty-admin 另起。
STATE.md 切换到 v1.0 状态:当前位置 = 需求定义中(roadmap 待生成)。
2026-05-07 16:29:10 +08:00

190 lines
14 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.

# QY LTY Backend洛天依统一后端服务
## 项目简介
QY LTY Backend 是「洛天依Luotianyi智能陪伴产品」生态的统一 Django 后端,同时服务于 **3 个客户端**Unity 设备端(`LTY_Project`、Unity 移动 App`LTY_App_Project_URP`、Web 管理后台(`qy-lty-admin`。提供用户认证、AI 对话含语音、设备实时通信WebSocket + RTC、卡片/二维码、成就、订阅等综合能力。
## 核心价值
**设备端与手机端通过同一个用户身份实时互通**——`device_{user_id}` 分组模型必须始终成立。一旦绑定/控制权解析、WebSocket 路由、RTC 房间号三者偏离同一 user_id整个产品的"陪伴"价值就坍塌了。其他能力(卡片、成就、订阅)可以暂时降级,这条不行。
## 本期 Milestonev1.0 通用凭据槽位APP ID + Access Token
**启动日期**2026-05-07
**目标**在后端提供一组全局单例的通用凭据存储槽位APP ID + Access Token管理后台可写入手机端 + 设备端可读取。不绑定特定服务商,运营自由填值;前端联动 milestone 在 `qy-lty-admin` 仓库另行启动。
**目标能力**
- 后端单例配置模型(保存 APP ID + Access Token强制全局唯一记录
- 管理端读写接口admin token 鉴权,`/api/v1/admin/` 命名空间GET 时 Access Token 脱敏)
- 客户端读取接口user token 鉴权,复用 `RedisTokenAuthentication`,明文返回供 LTY_App_Project_URP / LTY_Project 实际调用第三方服务)
- 敏感字段脱敏(响应壳层 + 阿里云日志),避免 Access Token 落入生产日志
**关键约束**
- 客户端实际使用 Access Token 调用第三方服务,所以**客户端 GET 接口必须返回明文**;只有管理端 GET 与日志做脱敏
- 单例语义DB 中保证最多一条记录(建议 `pk=1` 固定主键 + `get_or_create` 模式,或单字段唯一约束)
- 与现有 `StandardResponseMiddleware` 响应壳层兼容(`{ success, code, message, data }`
- 不引入新的鉴权体系:管理端走 `admin_token:{token}`,客户端走 `token:{token}`,与现有所有接口对齐
## 需求清单
### 已交付
<!-- 已交付且在生产链路上跑通的能力,从 .planning/codebase/ 推断 -->
**用户与认证userapp**
-**AUTH-01** 自定义用户模型 `ParadiseUser`(含 gender、MBTI、interests 等画像字段)— existing
-**AUTH-02** Redis 后端 token 认证(`RedisTokenAuthentication`30 天 TTLkey `token:{token}` / `admin_token:{token}`)— existing
-**AUTH-03** 手机号 + 阿里云 SMS 验证码登录 — existing
-**AUTH-04** WeChat 社交登录django-allauth + Weixin provider— existing
-**AUTH-05** 设备端通过 MAC 换 user-token`POST /api/user/mac-login/`,按 `-bound_at` 取最新绑定者)— existing
**AI 对话与语音aiapp**
-**AI-01** 单轮 / 多轮文本对话(接 KimiOpenAI 兼容协议)— existing
-**AI-02** 多服务商语音抽象 `AudioService`(火山引擎 / 阿里云 NLS / 腾讯via `AUDIO_SERVICE_PROVIDER`)— existing
-**AI-03** 语音合成TTS+ 语音识别ASR通用接口 — existing
-**AI-04** 字幕落库(`ConversationSubtitle`+ Bot 配置管理 — existing
**设备交互device_interaction**
-**DEV-01** WebSocket 双通道:`/ws/device/`Header 鉴权)和 `/ws/device/token/{token}/`URL 鉴权)— existing
-**DEV-02** Channel Layer 分组模型 `device_{user_id}`,端到端唯一身份 — existing
-**DEV-03** 设备绑定 / 解绑 / 设主 / 状态查询 REST 接口 — existing
-**DEV-04** 设备心跳:消息触发刷新 Redis key `device:last_seen:{mac}`5 min TTL— existing
-**DEV-05** 设备状态写库(`Device.status` connected/disconnected— existing
-**DEV-06** "后绑挤先绑"控制权语义(`UserDevice.Meta.ordering = ['-bound_at']`)— existing
-**DEV-07** 火山引擎 RTC token 签发(`/api/device/rtc-token/get_by_mac/``room_id = room_{user_id}`)— existing
-**DEV-08** WebSocket 消息类型完整chat/weather/sing/dance/touch/flow_light/device_info/device_state/conversation_status/conversation_subtitle/factory_reset — existing
**卡片系统card**
-**CARD-01** 卡片分类管理 + 属性配置 — existing
-**CARD-02** 卡片批量生成 + 二维码 — existing
-**CARD-03** 卡片使用追踪 + 数据分析 — existing
**成就achievement_app**
-**ACH-01** 成就定义 + 稀有度等级 + 用户进度追踪 — existing
- ⚠️ **ACH-02** 成就解锁条件校验 — **TODO 占位**`achievement_app/views.py:139` 标记未实现,当前 endpoint 任意客户端可主张任意成就,详见 CONCERNS.md
**订阅subscription_app**
-**SUB-01** 用户订阅模型 + 订阅状态管理 — existing
-**SUB-02** APScheduler 定时任务调度(生产环境)— existing
**好感度系统userapp / device_interaction2026-04 完成 P1**
-**AFF-01** 设备级好感度计数(`UserDevice.favorability`,从 `ParadiseUser.favorability` 迁移)— existing
-**AFF-02** AffinityRule / AffinityLevel / AffinityLog / AffinityCounter / AffinitySetting / AffinityRewardClaim 6 张表 — existing
-**AFF-03** 配置驱动的规则 / 等级 / 奖励 / 上限 / 冷却 / 区间随机 — existing
-**AFF-04** Service 层P2+ 接口层P3+ 客户端集成P4**未交付**,待后续 milestone
**视觉智能ali_vi_app**
-**VI-01** 阿里云人脸检测 / 识别 SDK 集成 — existing
**通用基础设施common**
-**INF-01** `StandardResponseMiddleware` 统一响应包装(`success` / `code` / `message` / `data`)— existing
-**INF-02** `CustomPageNumberPagination` 自定义分页(`page_size` 可调)— existing
-**INF-03** Aliyun OSS 文件上传抽象(`common/oss.py`)— existing
-**INF-04** Aliyun Log Service 生产日志集成 — existing
-**INF-05** Swagger / ReDoc API 文档自动生成drf-yasg— existing
**管理后台admin**
-**ADM-01** Django Admin 深度定制SimpleUI 主题 + 中英双语)— existing
-**ADM-02** `/api/v1/admin/` 命名空间为 Web 管理后台提供 REST 接口 — existing
**部署deployment**
-**DEP-01** Docker + docker-compose 容器化(端口 12012— existing
-**DEP-02** Daphne ASGI同时承载 HTTP + WebSocket— existing
-**DEP-03** PostgreSQL主库+ Redis缓存 + Channel Layer— existing
-**DEP-04** i18n 双语zh_HAns / envia django-rosetta — existing
### 进行中
<!-- 当前正在建设的目标。GSD 通过 phase 推动这一段;移到 Validated 才算完成 -->
**Milestone v1.0 通用凭据槽位**(启动 2026-05-07
- [ ] **CRED-01** 单例 `CredentialSlot` 模型 + 迁移(强制 DB 中最多一条)
- [ ] **CRED-02** Django Admin 注册(`Access Token` 字段写入态可见,列表/查看态脱敏)
- [ ] **CRED-03** 管理端 GET `/api/v1/admin/credential-slot/`admin token 鉴权Access Token 字段脱敏掩码返回)
- [ ] **CRED-04** 管理端 PUT `/api/v1/admin/credential-slot/`admin token 鉴权,全字段覆写更新)
- [ ] **CRED-05** 客户端 GET `/api/credential-slot/`user token 鉴权 via `RedisTokenAuthentication`,明文返回 APP ID + Access Token
- [ ] **CRED-06** Access Token 在阿里云日志中过滤(日志格式化器 / 中间件层避免 secret 落生产日志)
### 范围外
<!-- 明确排除项 + 理由。防止后续被无意识地拉回来 -->
- **Web 管理后台前端实现** — 在独立项目 `../qy-lty-admin/` 中维护Next.js 15 + React 19。本仓库**只**提供 `/api/v1/admin/` REST 接口,不写任何前端代码。
- **Unity 客户端业务逻辑** — 在 `C:\Unity2022project\LTY_App_Project_URP`(手机 App`C:\Unity2022project\LTY_Project`(设备端)独立维护。本仓库提供 HTTP / WebSocket / RTC 协议接口,不耦合客户端实现。
- **跨项目的混合修改记录** — 服务端 / 管理后台改动**各自记录**到本仓库 `docs/修改记录.md``qy-lty-admin/docs/修改记录.md`,跨项目联动**两端各写一条互相引用**不混在同一条目里CLAUDE.md 显式规定)。
- **会话完整管控(如 Sentry / APM 全链路追踪)** — 当前用阿里云日志服务,没有上 APM。如需要再开 milestone 评估。
- **多 Redis 实例 / Sentinel 集群** — 当前是单 RedisCONCERNS.md 标记为 HA 风险,但不在本次范畴内;待性能/可用性 milestone 决议。
- **真正意义的测试套件** — 当前 ≈10 个 test 文件中只有 1 个真正在测,无 mock 基础设施。这是 CONCERNS.md 标的 HIGH-severity 工程债,但需要独立 milestone 系统性修。
## 背景上下文
**生态位**本服务是「设备—App—管理端」三角的中心节点对所有客户端来说**它是唯一的真理来源**。
**对接客户端清单**(以 CLAUDE.md 为准):
| 客户端 | 路径 | 通讯方式 |
|--------|------|---------|
| LTY_App_Project_URP手机端 Unity URP | `C:\Unity2022project\LTY_App_Project_URP` | HTTP REST + WebSocket `/ws/device/` |
| LTY_Project设备端 Unity | `C:\Unity2022project\LTY_Project` | HTTP REST + WebSocket + RTC火山引擎 |
| qy-lty-adminWeb 管理后台) | `../qy-lty-admin/` | HTTP REST `/api/v1/admin/` |
**最近活跃工作**git log + 修改记录):
- 2026-04-24 ~ 至今好感度系统从「用户级单值」演进到「设备级独立计数」P1 数据层已上线P2/P3/P4 待后续 milestone
- 设备交互视图持续更新(最近 commit `a13a081`
- CI 触发性 commit 多次(`6c1cfde` / `ba16766` / `df85773`)—— 暗示 CI/CD 配置仍在调试
**已知工程现状**(详见 `.planning/codebase/`
- 整体规模约 168 个 Python 文件
- 测试覆盖**极低**(实质 ≈ 0是最大的工程债
- `device_interaction/views.py` 单文件 1867 行,承载 30+ action method —— 需要拆分但风险高(无测试保护)
- `requirements.txt` 不锁版本(无 lockfile—— 部署一致性靠 Docker 镜像保证
- Python 3.8 已 EOL2024-10需要规划升级
## 约束
- **技术栈**Django 4.2.13 + DRF + Channels + Daphne — 已锁定,迁移成本极高
- **技术栈**Python 3.8EOL— 当前限制,但近期需升级
- **技术栈**PostgreSQL+ Redis缓存 + Channel Layer— 不可替换
- **技术栈**ASGI 必须,因为 WebSocket 是核心 — WSGI 路径 (`wsgi.py`) 仅作历史保留
- **兼容性**WebSocket 消息协议11 种 message type被 2 个 Unity 客户端依赖 — 任何新增字段必须向前兼容,删除字段需协调 3 方排期
- **兼容性**REST API 响应包装格式(`StandardResponseMiddleware` 输出的 `{success, code, message, data}`)被 `qy-lty-admin` 依赖 — 整体结构不可改
- **兼容性**`device_{user_id}` 分组命名 + `room_{user_id}` 房间命名是端到端的隐式契约 — 改名要同步 Unity / Volcengine RTC 配置
- **文档规范**:每次代码改动**必须**追加到 `docs/修改记录.md` 顶部CLAUDE.md 强制规则,结构性文档变更同样适用)
- **沟通语言**与用户沟通使用中文CLAUDE.md「沟通语言」章节强制
- **项目独立**`qy_lty``qy-lty-admin` 独立维护,**修改记录、planning 工件不混合**
- **安全**`.env` 不入库,所有 SDK secret 必须通过环境变量加载CONCERNS.md 已标 SECRET_KEY / DEBUG / CORS 多处需收紧)
## 关键决策
| 决策 | 理由 | 结果 |
|------|------|------|
| WebSocket 分组用 `device_{user_id}`(不是 `device_{mac}` | 让"同一用户"的多个端点(手机 + 设备)天然共享同一通信空间;同时一个设备同一时刻只能被最新绑定的用户控制,符合"陪伴"产品语义 | ✓ Good — 已支撑生产 |
| `UserDevice.Meta.ordering = ['-bound_at']` 隐式驱动控制权解析 | 实现"后绑挤先绑"语义无需显式状态机,依赖 ORM 默认排序 | ⚠️ Revisit — 语义正确但隐式,新人容易误删旧记录或绕过 ordering 直接 query建议改写为显式 `current_owner` 字段 |
| 30 天 token TTLRedis 后端) | 设备 + 手机端长期在线,短 TTL 体验差 | ✓ Good — 与产品形态匹配 |
| 多服务商音频抽象(`AudioService` 工厂) | Aliyun/腾讯/火山随时切换、降本调优 | ✓ Good — 实测有效,火山为当前默认 |
| `StandardResponseMiddleware` 统一包装响应 | 客户端只需处理一种响应壳层 | ✓ Good — 已被 3 个客户端依赖 |
| 设备级好感度vs 用户级)— 2026-04 P1 | 同一用户多设备时不互相污染好感度 | — Pending — 需 P2 service 层落地后才能完整验证 |
| 测试 MAC `AA:BB:CC:DD:EE:FF` 硬编码绕过绑定校验 | 早期开发便利 | ⚠️ Revisit — CONCERNS.md 标 HIGH上规模前必须替换为环境变量开关 |
| `.planning/` 锚定在 `qy_lty\`(不是 `Lila-Server\` | `qy_lty``qy-lty-admin` 是独立项目CLAUDE.md 规定各自维护 | ✓ Good — 2026-05-07 通过预创建空目录强制锚定生效 |
## 演进规则
本文档在 phase 切换与 milestone 边界处更新。
**每次 phase 切换后**(通过 `/gsd-transition`
1. 需求被推翻?→ 移到「范围外」并说明理由
2. 需求已交付?→ 移到「已交付」并标注 phase 引用
3. 出现新需求?→ 加到「进行中」
4. 有决策需要记录?→ 加到「关键决策」
5. 「项目简介」是否仍然准确?→ 如有偏移则更新
**每个 milestone 完成后**(通过 `/gsd-complete-milestone`
1. 全面回顾所有章节
2. 复核「核心价值」—— 是否仍是当前最高优先级?
3. 审视「范围外」—— 排除理由是否仍然成立?
4. 用当前状态更新「背景上下文」
---
*最后更新2026-05-07启动 Milestone v1.0「通用凭据槽位APP ID + Access Token」*