- Prisma 7 + adapter-pg;prisma.config 与 users 初始迁移\n- AppModule 挂载 PrismaModule;PrismaService 仅依赖 DATABASE_URL\n- main 入口 dotenv/config,避免 Prisma 早于 Config 读 env\n- 短信登录 upsert User;默认昵称 Chat+手机号后四位\n- README / project-solution:目录、迁移规范、用户 avatar_url 说明\n- 依赖:dotenv、@prisma/adapter-pg、pg Made-with: Cursor
320 lines
9.0 KiB
Markdown
320 lines
9.0 KiB
Markdown
# chat-one-service
|
||
|
||
## TODO(当前迭代)
|
||
|
||
- [ ] client-app:短信登录 + JWT 鉴权(接入真实短信服务前先 mock)
|
||
- [ ] client-app:Chat 会话与消息持久化(接入 Prisma + 数据库)
|
||
- [ ] client-app:按用户限流与基础风控(基于 Redis 后续补上)
|
||
- [ ] admin-app:邮箱登录与 RBAC(角色/权限点基础能力)
|
||
- [ ] admin-app:平台管理接口(增删改查 + 开关 + 权重)
|
||
- [ ] admin-app:用量统计与简单报表(按平台 / 用户聚合)
|
||
|
||
---
|
||
|
||
ChatOne 服务端:基于 **NestJS 11** + **Fastify**,整合多厂商大模型对话能力,对外提供统一的 **SSE(Server-Sent Events)** 流式输出协议,便于 Web / App 接入。
|
||
|
||
更完整的架构说明见:`docs/project-solution.md`。JWT 最小示例见:`docs/jwt-minimal-nestjs.md`。
|
||
|
||
## 目录
|
||
|
||
- [TODO(当前迭代)](#todo当前迭代)
|
||
- [技术栈](#技术栈)
|
||
- [目录结构(概要)](#目录结构概要)
|
||
- [环境要求](#环境要求)
|
||
- [安装依赖](#安装依赖)
|
||
- [环境变量](#环境变量)
|
||
- [常用脚本](#常用脚本)
|
||
- [数据库迁移规范(Prisma)](#数据库迁移规范prisma)
|
||
- [启动](#启动)
|
||
- [API 说明](#api-说明)
|
||
- [数据库与 Prisma](#数据库与-prisma)
|
||
- [安全提示](#安全提示)
|
||
- [许可证](#许可证)
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
- **框架**:NestJS 11,`@nestjs/platform-fastify`
|
||
- **HTTP 客户端**:`undici`(调用各厂商 OpenAI 兼容接口)
|
||
- **配置**:`@nestjs/config`(可选环境校验见 `src/config/validation.ts`,默认可在 `AppModule` 中关闭)
|
||
- **日志**:`nestjs-pino` + `pino-pretty`(开发环境美化输出)
|
||
- **文档**:Swagger,默认路径 `/docs`
|
||
- **安全头**:`helmet`
|
||
|
||
---
|
||
|
||
## 目录结构(概要)
|
||
|
||
```text
|
||
src/
|
||
├── main.ts # 入口:Fastify + 全局前缀 api + Swagger
|
||
├── app.module.ts # 根模块
|
||
├── config/ # 配置加载与环境校验
|
||
├── apps/
|
||
│ ├── client-app/ # 用户端(当前含 Chat SSE)
|
||
│ ├── admin-app/ # 管理端(占位模块,后续扩展)
|
||
│ └── shared-domain/
|
||
│ └── ai-gateway/ # 多平台 Provider 与路由
|
||
├── prisma/ # Prisma 封装(按需接入数据库时再挂回 AppModule)
|
||
docs/
|
||
├── project-solution.md
|
||
└── jwt-minimal-nestjs.md
|
||
```
|
||
|
||
---
|
||
|
||
## 环境要求
|
||
|
||
- **Node.js**:建议 20 LTS 及以上
|
||
- **包管理**:本项目使用 **Yarn**(存在 `yarn.lock`);亦可用 npm
|
||
|
||
---
|
||
|
||
## 安装依赖
|
||
|
||
```bash
|
||
yarn install
|
||
```
|
||
|
||
---
|
||
|
||
## 环境变量
|
||
|
||
在项目根目录创建 `.env`(该文件已在 `.gitignore` 中,**不要提交到 Git**)。
|
||
|
||
### 服务
|
||
|
||
| 变量 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| `PORT` | 监听端口 | `3000` |
|
||
| `NODE_ENV` | 运行环境 | `development` / `production` |
|
||
|
||
### 千问(DashScope OpenAI 兼容)
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `QWEN_API_KEY` | 千问 API Key |
|
||
| `QWEN_BASE_URL` | 可选,默认 `https://dashscope.aliyuncs.com/compatible-mode/v1` |
|
||
|
||
### DeepSeek
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `DEEPSEEK_API_KEY` | DeepSeek API Key |
|
||
| `DEEPSEEK_BASE_URL` | 可选,默认 `https://api.deepseek.com/v1` |
|
||
|
||
### 火山引擎(方舟 OpenAI 兼容形态,具体以你控制台为准)
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `VOLC_API_KEY` | 火山引擎 API Key |
|
||
| `VOLC_BASE_URL` | 可选,默认 `https://ark.cn-beijing.volces.com/api/v3` |
|
||
|
||
### Spug 推送助手(短信验证码)
|
||
|
||
在 [Spug 推送助手](https://push.spug.cc) 创建「短信验证码」类消息模板,复制模板编号(URL 路径中的 ID)。模板中可使用变量 **`number`**(有效期,单位:**分钟**);服务端会根据 `SMS_CODE_TTL_SECONDS` 换算后传入(向上取整,至少为 1),与 Redis 中验证码 TTL 一致。
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `SPUG_PUSH_SMS_TEMPLATE_ID` | 消息模板编号(必填,非 mock 时) |
|
||
| `SPUG_PUSH_BASE_URL` | 可选,默认 `https://push.spug.cc` |
|
||
| `SPUG_SMS_NAME` | 可选,模板若要求 `name` 变量则配置(与官方示例一致) |
|
||
| `SMS_MOCK` | 可选,`true` 时跳过真实短信发送(本地联调用) |
|
||
| `SMS_CODE_TTL_SECONDS` | 可选,短信验证码 Redis TTL(秒),默认 `300` |
|
||
|
||
> 说明:当前各 Provider 对上游采用 **非流式** `chat/completions` 调用,由网关将完整回复 **切片** 后以 SSE 推给客户端。后续可升级为上游真流式。
|
||
|
||
---
|
||
|
||
## 常用脚本
|
||
|
||
| 命令 | 说明 |
|
||
|------|------|
|
||
| `yarn start:dev` | 开发模式(`tsx watch`,改代码自动重启) |
|
||
| `yarn build` | TypeScript 编译到 `dist/` |
|
||
| `yarn start` | 运行已编译产物 `dist/main.js` |
|
||
| `yarn lint` | ESLint(需本地配置规则时生效) |
|
||
|
||
## 数据库迁移规范(Prisma)
|
||
|
||
### 开发环境变更流程
|
||
|
||
1. 修改 `prisma/schema.prisma`(新增表/字段/索引等)
|
||
1. 生成并执行迁移:
|
||
|
||
```bash
|
||
npx prisma migrate dev --name <migration_name>
|
||
```
|
||
|
||
1. 如需手动更新 Prisma Client:
|
||
|
||
```bash
|
||
npx prisma generate
|
||
```
|
||
|
||
1. 本地验证接口与核心流程
|
||
1. 提交代码时必须包含:
|
||
- `prisma/schema.prisma`
|
||
- `prisma/migrations/**`
|
||
|
||
### 生产环境发布流程
|
||
|
||
生产环境禁止使用 `migrate dev`,仅执行:
|
||
|
||
```bash
|
||
npx prisma migrate deploy
|
||
```
|
||
|
||
### 迁移命名规范
|
||
|
||
迁移名统一使用:`动词_对象_说明`
|
||
|
||
例如:
|
||
|
||
- `init_users`
|
||
- `add_users_avatar_url`
|
||
- `add_chat_sessions`
|
||
- `alter_chat_messages_token_count_type`
|
||
|
||
### 风险控制建议
|
||
|
||
- 涉及删列、改类型、重命名时,先在测试库验证
|
||
- 可能影响历史数据的迁移,先做备份
|
||
- 大改动拆分为多次小迁移,避免一次性高风险变更
|
||
|
||
### 常用命令速查
|
||
|
||
```bash
|
||
# 生成并执行开发迁移
|
||
npx prisma migrate dev --name <name>
|
||
|
||
# 仅更新 Prisma Client
|
||
npx prisma generate
|
||
|
||
# 查看数据库状态
|
||
npx prisma migrate status
|
||
|
||
# 生产环境应用迁移
|
||
npx prisma migrate deploy
|
||
```
|
||
|
||
---
|
||
|
||
## 启动
|
||
|
||
开发:
|
||
|
||
```bash
|
||
yarn start:dev
|
||
```
|
||
|
||
默认监听:`http://localhost:3000`(可通过 `PORT` 修改)。
|
||
|
||
生产(先编译再启动):
|
||
|
||
```bash
|
||
yarn build
|
||
yarn start
|
||
```
|
||
|
||
---
|
||
|
||
## API 说明
|
||
|
||
### Swagger
|
||
|
||
浏览器打开:`http://localhost:3000/docs`
|
||
|
||
### 客户端认证(MVP)
|
||
|
||
- `POST /api/client/v1/auth/sms/send`
|
||
- 入参:`{ "phone": "13800000000", "scene": "login" }`
|
||
- 返回:`requestId`、`expireIn`、`provider`
|
||
- 说明:已接入 Spug 推送助手;非生产环境会额外返回 `testCode` 便于联调
|
||
- `POST /api/client/v1/auth/sms/login`
|
||
- 入参:`{ "phone": "13800000000", "code": "xxxxxx" }`
|
||
- 返回:`accessToken`、`refreshToken`、`user`
|
||
- `POST /api/client/v1/auth/refresh`
|
||
- 入参:`{ "refreshToken": "..." }`
|
||
- 返回:新的 `accessToken`、`refreshToken`
|
||
|
||
> 验证码当前已接入 Redis 存储(key: `chatone:client:sms:code:{phone}:{scene}`,TTL 300 秒)。
|
||
|
||
### 统一 SSE Chat(用户端)
|
||
|
||
- **路径**:`POST /api/client/v1/chat/completions/stream`
|
||
- **Content-Type**:`application/json`
|
||
- **响应**:`text/event-stream`(SSE)
|
||
|
||
#### 请求体(示例)
|
||
|
||
```json
|
||
{
|
||
"model": "qwen-plus",
|
||
"platform": "qwen",
|
||
"messages": [
|
||
{ "role": "system", "content": "你是一个乐于助人的助手" },
|
||
{ "role": "user", "content": "你好" }
|
||
]
|
||
}
|
||
```
|
||
|
||
- `platform`:`qwen` | `deepseek` | `volc` | `auto`(或不传,由路由按 `model` 简单匹配)
|
||
- `model`:各平台实际模型名或 Endpoint ID(火山侧请填你在控制台配置的模型标识)
|
||
|
||
#### SSE 事件约定
|
||
|
||
| 事件名 | data 字段含义 |
|
||
|--------|----------------|
|
||
| `meta` | 请求元信息:`requestId`、`platform`、`model` |
|
||
| `delta` | 文本增量:`{"delta":"..."}` |
|
||
| `usage` | Token 用量:`promptTokens` / `completionTokens` / `totalTokens` |
|
||
| `done` | 结束:`finishReason` |
|
||
|
||
#### curl 示例(流式)
|
||
|
||
先获取 token:
|
||
|
||
```bash
|
||
curl -s -H "Content-Type: application/json" -X POST \
|
||
-d '{"phone":"13800000000","code":"123456"}' \
|
||
http://localhost:3000/api/client/v1/auth/sms/login
|
||
```
|
||
|
||
再带 `Authorization` 调用 chat:
|
||
|
||
```bash
|
||
curl -N \
|
||
-H "Authorization: Bearer 你的accessToken" \
|
||
-H "Content-Type: application/json" \
|
||
-X POST \
|
||
-d '{"platform":"qwen","model":"qwen-plus","messages":[{"role":"user","content":"你是谁"}]}' \
|
||
http://localhost:3000/api/client/v1/chat/completions/stream
|
||
```
|
||
|
||
---
|
||
|
||
## 数据库与 Prisma
|
||
|
||
仓库内已包含 `src/prisma/` 模块骨架。若尚未准备好 PostgreSQL 或未执行 `prisma generate`,请勿在 `AppModule` 中导入 `PrismaModule`,避免启动时报缺少生成客户端的错误。
|
||
|
||
接入数据库时的典型步骤:
|
||
|
||
1. 编写 `prisma/schema.prisma` 并配置 `DATABASE_URL`
|
||
2. 执行 `npx prisma generate` / `yarn prisma generate`
|
||
3. 在 `src/app.module.ts` 中按需 `imports: [PrismaModule]`
|
||
|
||
---
|
||
|
||
## 安全提示
|
||
|
||
- **切勿**将含真实 Key 的 `.env` 提交到版本库。
|
||
- 若密钥曾在公开场合暴露,请在对应云厂商控制台 **轮换 API Key**。
|
||
|
||
---
|
||
|
||
## 许可证
|
||
|
||
MIT(见仓库根目录 `LICENSE`)。
|