lty/qy_lty/CLAUDE.md
pmc 33b302c773 fix(affinity-P1): CR-001 + IN-005 修复 UserDevice 软删语义 + is_bound 改名
UserDevice.is_active 改名为 is_bound(消除与 Device.is_active 的命名冲突),
新增 ActiveUserDeviceManager(active manager),4 处控制权解析调用点
(MAC 登录、bind_status、绑定校验、RTC token、绑定 endpoint)切换到
UserDevice.active.filter(...),避免 P2 软删后旧绑定者被签发 user-token、
WS 分组路由错误、RTC 房间归属错乱等安全 / 越权风险。

base_manager_name='objects' 保证 admin 默认 queryset 不受 active 过滤影响。

详见 docs/REVIEW-affinity-P1.md CR-001 / IN-005。
2026-05-13 10:10:14 +08:00

282 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.

# CLAUDE.md
本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指导。
## 沟通语言(重要 — 始终生效)
- 在本仓库工作时,**所有面向用户的回复**(思考后的最终回答、状态更新、错误说明、提问、计划摘要等)**统一使用中文**
- 内部思考thinking可使用任意语言以保证推理质量但**呈现给用户的输出必须是中文**
- 工具调用参数、Git 提交信息commit message、代码注释保持原项目约定中文为主必要的英文术语、变量名、API 名等可保留)
- 此规则覆盖默认的英文输出倾向;只有当用户明确要求改用其他语言时才切换
## 项目概述
QY LTY Backend 是一个基于 Django 的综合性后端服务,提供以下功能:
- 用户管理与认证系统
- AI 对话能力(支持语音的单轮/多轮聊天)
- 卡片管理系统,支持批量生成与二维码功能
- 通过 WebSocket 实时通信进行设备交互
- 成就系统
- 订阅管理
- 多种第三方集成(阿里云、火山引擎、腾讯等)
### 对接的客户端项目
本服务器作为统一后端,与以下客户端/管理端项目进行通讯和数据交互:
| 项目 | 路径 | 角色 | 通讯方式 |
|------|------|------|---------|
| **LTY_App_Project_URP** | `C:\Unity2022project\LTY_App_Project_URP` | 洛天依 APPUnity URP 客户端) | HTTP REST API + WebSocket (`/ws/device/`) |
| **LTY_Project** | `C:\Unity2022project\LTY_Project` | 洛天依 Unity 项目(设备端/终端) | HTTP REST API + WebSocket + RTC火山引擎 |
| **qy-lty-admin** | `../qy-lty-admin/``C:\Users\admin\Desktop\Lila-Server\qy-lty-admin` | Web 管理后台Next.js 15 + React 19 | HTTP REST API`NEXT_PUBLIC_API_BASE_URL` |
- 两个 Unity 客户端通过 `device_interaction` 模块共享同一套 WebSocket 通道(分组模型 `device_{user_id}`),设备端与 APP 端解析到同一 user_id 才能互通消息
- 管理后台通过 `/api/v1/admin/` 与各业务模块接口进行运营管理数据交互
## 开发命令
### 环境配置
```bash
# 安装依赖
pip install -r requirements.txt
# 复制环境配置文件
cp .env.bak .env
# 数据库迁移
python manage.py migrate
# 创建超级用户
python manage.py createsuperuser
```
### 运行应用
```bash
# 启动开发服务器(支持 WebSocket 的 ASGI
./run.sh
# 或直接使用 daphne
daphne -b 0.0.0.0 -p 8000 qy_lty.asgi:application
# 传统 Django 开发服务器(仅 HTTP
python manage.py runserver
```
### 数据库管理
```bash
# 创建新的迁移文件
python manage.py makemigrations
python manage.py makemigrations [app_name]
# 应用迁移
python manage.py migrate
# 数据库 shell
python manage.py dbshell
# Django shell
python manage.py shell
```
### 国际化 (i18n)
```bash
# 生成消息文件
django-admin makemessages -l en
django-admin makemessages -l zh_HAns
# 编译翻译
django-admin compilemessages
```
### Docker 部署
```bash
# 构建并启动容器
docker-compose up -d --build
# 通过 http://localhost:12012 访问应用
```
## 架构概览
### 核心 Django 应用结构
- **userapp/**:自定义用户模型 (ParadiseUser),包含认证、手机验证和用户管理
- **aiapp/**AI 对话系统,集成 Kimi通过多个服务商提供语音合成/识别
- **card/**:卡片管理系统,包含分类、批次、二维码和使用追踪
- **device_interaction/**:基于 WebSocket 的实时设备通信,带有认证机制
- **achievement_app/**:用户成就与进度追踪系统
- **subscription_app/**:用户订阅与计费管理
- **ali_vi_app/**:阿里云视觉智能集成
- **workflow_app/**:多租户工作流管理(开发中)
### 关键技术组件
#### ASGI/WebSocket 配置
- 使用 Django Channels 提供 WebSocket 支持
- 自定义基于 token 的 WebSocket 认证(`device_interaction.auth.TokenAuthMiddleware`,复用 `userapp.authentication.RedisTokenAuthentication`
- 基于 Redis 的通道层用于实时消息传递
- WebSocket 路由:`ws://domain/ws/device/`Header 鉴权)和 `ws://domain/ws/device/token/{token}/`URL 鉴权)
- **分组模型**`device_{user_id}` —— 设备端与手机端必须解析到 **同一个 user_id** 才能互通消息(详见下文"设备绑定与控制权"
#### 认证系统
- 自定义用户模型:`userapp.ParadiseUser`,继承自 AbstractUser
- 基于 token 的 API 认证token 存储在 Redis 中,普通用户 key 为 `token:{token}`,管理员为 `admin_token:{token}`TTL 30 天(见 `userapp/utils.py:generate_token`
- 通过短信进行手机验证(阿里云 SMS 服务)
- 通过 django-allauth 实现社交认证(微信)
- 设备端通过 `POST /api/user/mac-login/` 用 MAC 地址换取绑定用户的 user-token
#### 数据库配置
- 主库PostgreSQL可通过环境变量配置
- Redis 用于缓存和 WebSocket 通道层
- 使用 python-decouple 进行基于环境的配置
#### 音频/语音服务
- 多服务商音频支持:阿里云、腾讯、火山引擎
- 通过 `AUDIO_SERVICE_PROVIDER` 配置切换服务商
- 支持语音合成与识别能力
## API 文档
### 访问入口
- Swagger UI`http://localhost:8000/swagger/`
- ReDoc`http://localhost:8000/redoc/`
### 主要 API 模块
- `/api/user/` - 用户认证与管理
- `/api/ai/` - AI 对话接口
- `/api/device/` - 设备交互与 WebSocket 消息
- `/api/card/` - 卡片系统管理
- `/api/achievement/` - 成就系统
- `/api/v1/admin/` - 管理功能
### WebSocket 消息类型
```json
{
"type": "message_type",
"message": "content",
"user_id": "user_id"
}
```
支持的类型(见 `device_interaction/consumers.py:DeviceConsumer.receive`
- `chat_message` — 默认文本消息
- `weather` — 天气信息message 为 JSON 字符串)
- `sing` / `dance` / `touch` — 动作指令
- `flow_light` — 流水灯开关
- `device_info` — 设备上报 MAC、电量、固件、WiFi 等,会写库并刷新心跳
- `device_state` — 手机端设备状态
- `conversation_status` / `conversation_subtitle` — 火山引擎对话回调转发
- `factory_reset` — 恢复出厂设置
### RTC火山引擎
- 端点 `/api/device/rtc-token/get_by_mac/?mac_address=...` 不需鉴权,根据 MAC 返回该设备绑定用户对应的 RTC token
- `room_id = f"room_{user_id}"`、token 缓存 key 为 `rtc_room:{user_id}:{task_id}`
- `room_id` 与 WebSocket 分组绑定的是同一个 user_id保持端到端一致
## 环境配置
### 必需的环境变量 (.env)
- `SECRET_KEY` - Django 密钥
- `DEBUG` - 开发模式标志
- 数据库:`POSTGRESQL_DATABASE_*` 系列变量
- Redis`REDIS_LOCATION``REDIS_PASSWORD`
- Kimi AI`KIMI_API_KEY``KIMI_BASE_URL`
- 阿里云服务:各类 `ALIYUN_*` 密钥
- 音频服务:服务商相关配置
- 火山引擎:`VOLCENGINE_ACCESS_KEY``VOLCENGINE_SECRET_KEY`
## 关键依赖
### 核心框架
- Django 4.2.13 配合 Django REST Framework
- Django Channels 提供 WebSocket 支持
- Daphne ASGI 服务器
### 第三方集成
- 阿里云SMS、OSS、NLS语音、视觉智能
- 火山引擎字节跳动RTC 服务
- 腾讯:音频服务
- OpenAI 兼容 APIKimi
### 开发工具
- django-debug-toolbar 用于开发环境调试
- django-simpleui 用于增强后台管理界面
- drf-yasg 用于生成 API 文档
## 代码模式与约定
### 模型结构
- 自定义用户模型包含丰富的资料字段性别、MBTI、兴趣等
- 卡片系统采用基于批次的生成方式,配合分类管理
- 成就系统包含稀有度等级与进度追踪
### API 响应格式
- 通过 `common.middleware.StandardResponseMiddleware` 实现标准化响应
- 自定义分页:`common.pagination.CustomPageNumberPagination`
### 日志
- 集成阿里云日志服务用于生产环境日志记录
- 为 aiapp、userapp、common 模块配置专用日志记录器
### 文件存储
- 使用 OSS阿里云对象存储存储音频和媒体文件
- 开发环境使用本地存储,路径可配置
## 开发说明
### WebSocket 开发
- 设备连接需要基于 token 的认证
- 通道层使用 Redis 进行消息传递
- 自定义消费者位于 `device_interaction.consumers`
- 设备心跳:每次收消息时刷新 `device:last_seen:{mac}`TTL 5 分钟),断连时把 `Device.status` 标记为 `disconnected`
### 设备绑定与控制权(重要)
- `UserDevice` 关联表的 `Meta.ordering = ['-bound_at']`
- **"后绑的挤掉先绑的"语义**`userapp/views.py` 的 MAC 登录显式按 `order_by('-bound_at').first()` 取最新绑定者并签发 user-token`device_interaction/views.py` 中的 `bind_status` / `rtc-token/get_by_mac` 等使用 `.first()` 隐式依赖该 ordering结果一致
- 由于 WebSocket 分组是 `device_{user_id}`**同一台设备同一时刻只有一个用户能真正控制它**——即最近一次绑定的那个用户
- **必须过滤 `is_bound=True`(硬规则)**P1-08 引入 `UserDevice.is_bound`(原名 `is_active`P1 收尾因与 `Device.is_active` 命名冲突已改名)作为软删除标记。**所有控制权解析的查询MAC 登录、WS 分组、RTC 房间路由、绑定校验、`bind_status`)必须使用 `UserDevice.active.filter(...)`**`active``ActiveUserDeviceManager`,自动过滤 `is_bound=True`)。直接 `UserDevice.objects.filter(...)` 仅供管理后台 / 审计 / 数据迁移等需要历史记录的场景
- 旧的 `UserDevice` 记录**软删而非硬删**:解绑置 `is_bound=False`,重绑时可读取历史好感度值;硬删仅在运维清理场景下手工执行
- `is_primary` 是"用户视角的主设备"(每个用户最多一个),**不是**"设备视角的主控用户"——同一台设备可能出现多条 `is_primary=True` 的记录
- **测试 MAC `AA:BB:CC:DD:EE:FF`** 在 `device_interaction/serializers.py``views.py` 中被硬编码跳过"设备已被其他用户绑定"校验,仅供测试用
### 音频服务集成
- 通过 `aiapp.audio.AudioService` 提供与服务商无关的接口
- 支持语音转文本与文本转语音能力
- 集成文件存储用于音频资源管理
### 卡片系统功能
- 批量生成,支持配置数量与格式
- 二维码生成与扫描
- 使用追踪与数据分析
- 基于分类的组织管理,支持属性配置
### 后台管理界面
- 深度定制 SimpleUI 主题
- 支持多语言(中文/英文)
- 为不同模块配置自定义图标与组织结构
## 项目修改记录规则(重要 — 自动执行)
每次对本仓库代码做出改动后,**必须**在同一会话内把变更追加到 [docs/修改记录.md](docs/修改记录.md) **顶部**(最新在最前),不要等用户提醒。条目格式遵循该文件头部"修改格式说明"约定:
```
### [日期] 修改简述
- **文件路径**: 相对于项目根目录的文件路径
- **修改类型**: 新增 / 修改 / 删除 / 重构 / 修复Bug
- **修改内容**: 具体修改了什么
- **修改原因**: 为什么要做这个修改
```
### `qy_lty` 与 `qy-lty-admin` 是独立项目,各自维护
- `qy_lty/docs/修改记录.md` — **仅**记录服务端(本仓库)改动
- `qy-lty-admin/docs/修改记录.md` — **仅**记录管理后台前端改动;如该文件不存在则新建(基于 qy_lty 同名文件的格式骨架)
跨项目联动(例如新增服务端接口 + 管理后台调用):**两端各写一条,相互引用**对方的修改记录条目,不要把两端混进同一条记录里。
### 适用范围
- 业务代码、配置、迁移文件、CI / k8s / Dockerfile、文档结构性改动 → **必须**记录
- 单纯的 typo 修复、注释微调 → 可省略
- 临时调试脚本、`.gitignore` 中文件、本地实验文件 → 不记录