lty/qy_lty/.planning/PROJECT.md

166 lines
12 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洛天依统一后端服务
## What This Is
QY LTY Backend 是「洛天依Luotianyi智能陪伴产品」生态的统一 Django 后端,同时服务于 **3 个客户端**Unity 设备端(`LTY_Project`、Unity 移动 App`LTY_App_Project_URP`、Web 管理后台(`qy-lty-admin`。提供用户认证、AI 对话含语音、设备实时通信WebSocket + RTC、卡片/二维码、成就、订阅等综合能力。
## Core Value
**设备端与手机端通过同一个用户身份实时互通**——`device_{user_id}` 分组模型必须始终成立。一旦绑定/控制权解析、WebSocket 路由、RTC 房间号三者偏离同一 user_id整个产品的"陪伴"价值就坍塌了。其他能力(卡片、成就、订阅)可以暂时降级,这条不行。
## Requirements
### Validated
<!-- 已交付且在生产链路上跑通的能力,从 .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
### Active
<!-- 当前正在建设的目标。GSD 通过 phase 推动这一段;移到 Validated 才算完成 -->
(暂无 — 本次 `/gsd-new-project` 仅做 brownfield 文档化。下次新增功能 / 子系统时使用 `/gsd-new-milestone` 启动新 milestone把当时要交付的能力加到这一段然后 `/gsd-plan-phase` 拆 phase。
### Out of Scope
<!-- 明确排除项 + 理由。防止后续被无意识地拉回来 -->
- **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 系统性修。
## Context
**生态位**本服务是「设备—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需要规划升级
## Constraints
- **Tech stack**: Django 4.2.13 + DRF + Channels + Daphne — 已锁定,迁移成本极高
- **Tech stack**: Python 3.8EOL— 当前限制,但近期需升级
- **Tech stack**: PostgreSQL+ Redis缓存 + Channel Layer— 不可替换
- **Tech stack**: ASGI 必须,因为 WebSocket 是核心 — WSGI 路径 (`wsgi.py`) 仅作历史保留
- **Compatibility**: WebSocket 消息协议11 种 message type被 2 个 Unity 客户端依赖 — 任何新增字段必须向前兼容,删除字段需协调 3 方排期
- **Compatibility**: REST API 响应包装格式(`StandardResponseMiddleware` 输出的 `{success, code, message, data}`)被 `qy-lty-admin` 依赖 — 整体结构不可改
- **Compatibility**: `device_{user_id}` 分组命名 + `room_{user_id}` 房间命名是端到端的隐式契约 — 改名要同步 Unity / Volcengine RTC 配置
- **Documentation**: 每次代码改动**必须**追加到 `docs/修改记录.md` 顶部CLAUDE.md 强制规则,结构性文档变更同样适用)
- **Communication**: 与用户沟通使用中文CLAUDE.md「沟通语言」章节强制
- **Independence**: `qy_lty``qy-lty-admin` 独立维护,**修改记录、planning 工件不混合**
- **Security**: `.env` 不入库,所有 SDK secret 必须通过环境变量加载CONCERNS.md 已标 SECRET_KEY / DEBUG / CORS 多处需收紧)
## Key Decisions
| Decision | Rationale | Outcome |
|----------|-----------|---------|
| 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 通过预创建空目录强制锚定生效 |
## Evolution
This document evolves at phase transitions and milestone boundaries.
**After each phase transition** (via `/gsd-transition`):
1. Requirements invalidated? → Move to Out of Scope with reason
2. Requirements validated? → Move to Validated with phase reference
3. New requirements emerged? → Add to Active
4. Decisions to log? → Add to Key Decisions
5. "What This Is" still accurate? → Update if drifted
**After each milestone** (via `/gsd-complete-milestone`):
1. Full review of all sections
2. Core Value check — still the right priority?
3. Audit Out of Scope — reasons still valid?
4. Update Context with current state
---
*Last updated: 2026-05-07 after brownfield documentation initialization (existing system mapped, no Active milestone yet — use /gsd-new-milestone to start the next cycle)*