- 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
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。
目录
技术栈
- 框架:NestJS 11,
@nestjs/platform-fastify - HTTP 客户端:
undici(调用各厂商 OpenAI 兼容接口) - 配置:
@nestjs/config(可选环境校验见src/config/validation.ts,默认可在AppModule中关闭) - 日志:
nestjs-pino+pino-pretty(开发环境美化输出) - 文档:Swagger,默认路径
/docs - 安全头:
helmet
目录结构(概要)
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
安装依赖
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 推送助手 创建「短信验证码」类消息模板,复制模板编号(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)
开发环境变更流程
- 修改
prisma/schema.prisma(新增表/字段/索引等) - 生成并执行迁移:
npx prisma migrate dev --name <migration_name>
- 如需手动更新 Prisma Client:
npx prisma generate
- 本地验证接口与核心流程
- 提交代码时必须包含:
prisma/schema.prismaprisma/migrations/**
生产环境发布流程
生产环境禁止使用 migrate dev,仅执行:
npx prisma migrate deploy
迁移命名规范
迁移名统一使用:动词_对象_说明
例如:
init_usersadd_users_avatar_urladd_chat_sessionsalter_chat_messages_token_count_type
风险控制建议
- 涉及删列、改类型、重命名时,先在测试库验证
- 可能影响历史数据的迁移,先做备份
- 大改动拆分为多次小迁移,避免一次性高风险变更
常用命令速查
# 生成并执行开发迁移
npx prisma migrate dev --name <name>
# 仅更新 Prisma Client
npx prisma generate
# 查看数据库状态
npx prisma migrate status
# 生产环境应用迁移
npx prisma migrate deploy
启动
开发:
yarn start:dev
默认监听:http://localhost:3000(可通过 PORT 修改)。
生产(先编译再启动):
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)
请求体(示例)
{
"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:
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:
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,避免启动时报缺少生成客户端的错误。
接入数据库时的典型步骤:
- 编写
prisma/schema.prisma并配置DATABASE_URL - 执行
npx prisma generate/yarn prisma generate - 在
src/app.module.ts中按需imports: [PrismaModule]
安全提示
- 切勿将含真实 Key 的
.env提交到版本库。 - 若密钥曾在公开场合暴露,请在对应云厂商控制台 轮换 API Key。
许可证
MIT(见仓库根目录 LICENSE)。