补齐客户端会话生命周期接口(创建、列表、消息分页、改名、删除),并在流式 chat 中强制绑定 sessionId 与落库消息,确保会话标题和历史可追踪,同时统一 Swagger 文档为 DTO 驱动以减少重复维护。 Made-with: Cursor
22 KiB
ChatOne Service 项目方案(NestJS)
本文给出一个主流且简洁、可直接落地的服务端方案,满足以下目标:
- 基于 NestJS 构建;
- 同时提供客户端接口与后台管理接口;
- 支持短信登录、邮箱登录;
- 支持千问、火山引擎、DeepSeek 等多平台 AI Chat 流式输出;
- 对外提供统一调用接口(自动路由)和指定平台调用接口;
- 提供用户管理、平台管理、用量统计。
目录
- 1. 技术选型(主流且简洁)
- 2. 项目结构(推荐:按用户端/管理端强隔离)
- 3. 模块职责
- 4. API 设计(示例)
- 5. 自动路由与统一格式方案
- 6. 依赖清单(核心)
- 7. 配置文件示例
- 8. 数据库设计(PostgreSQL)
- 9. 缓存方案(Redis)
- 10. 登录与鉴权方案
- 11. 拆分演进路线(低成本)
- 12. 最小可用开发流程(MVP)
- 13. 非功能建议(上线前)
- 14. 一句话总结
1. 技术选型(主流且简洁)
- 运行时:Node.js 20 LTS
- 框架:NestJS 10(Fastify 适配器,性能更优)
- ORM:Prisma(类型安全、迁移友好)
- 数据库:PostgreSQL 15
- 缓存/队列:Redis 7(缓存、限流、验证码、会话黑名单)
- 鉴权:JWT(Access + Refresh)+ RBAC
- 文档:Swagger(
@nestjs/swagger) - 校验:
class-validator+class-transformer - 日志:Pino(
nestjs-pino) - 任务调度:
@nestjs/schedule(统计聚合作业) - 流式输出:SSE(标准 EventSource,前端接入简单)
2. 项目结构(推荐:按用户端/管理端强隔离)
chat-one-service/
├─ src/
│ ├─ main.ts # 单体双端统一启动入口(MVP)
│ ├─ app.module.ts # 根模块:聚合 client/admin/shared
│ ├─ common/
│ │ ├─ constants/ # 全局常量(错误码前缀、header key 等)
│ │ ├─ decorators/ # 自定义装饰器(当前用户、角色、公开路由)
│ │ ├─ filters/ # 全局异常过滤(统一错误响应格式)
│ │ ├─ guards/ # 通用 guard(按需被 client/admin 复用)
│ │ ├─ interceptors/ # 请求日志、traceId、响应耗时统计
│ │ ├─ pipes/ # DTO 校验与类型转换
│ │ └─ utils/ # 通用工具函数(时间、脱敏、签名等)
│ ├─ config/
│ │ ├─ configuration.ts # 读取并组织配置(按模块导出)
│ │ ├─ validation.ts # 启动前环境变量校验(fail fast)
│ │ └─ swagger.ts # Swagger 初始化与分组(client/admin)
│ ├─ apps/
│ │ ├─ client-app/ # 用户端边界:面向 C 端业务能力
│ │ │ ├─ client-app.module.ts # 用户端聚合模块(auth/chat/sessions/profile)
│ │ │ ├─ auth/ # 用户端认证:短信验证码、登录、refresh
│ │ │ ├─ chat/
│ │ │ │ ├─ chat.module.ts # chat 子模块聚合入口
│ │ │ │ ├─ controllers/ # 对外接口层(统一/指定平台 SSE 入口)
│ │ │ │ ├─ application/ # 用例编排层(配额检查、路由、日志)
│ │ │ │ ├─ domain/ # 领域规则层(会话/消息/策略)
│ │ │ │ ├─ infrastructure/ # 基础设施层(Prisma/Redis/SSE/事件)
│ │ │ │ ├─ dto/ # 请求/响应 DTO(仅用户端可见字段)
│ │ │ │ └─ constants/ # chat 常量(事件类型、错误码、模型别名)
│ │ │ ├─ sessions/ # 会话与历史查询(可逐步并入 chat)
│ │ │ └─ profile/ # 用户资料、偏好设置、个人配置
│ │ ├─ admin-app/ # 管理端边界:面向运营/管理能力
│ │ │ ├─ admin-app.module.ts # 管理端聚合模块(auth/users/platforms/stats)
│ │ │ ├─ auth/ # 管理端认证:邮箱登录、RBAC、审计
│ │ │ ├─ users/ # 用户管理:查询、禁用、角色与额度治理
│ │ │ ├─ platforms/ # 平台管理:密钥、权重、模型映射、健康检查
│ │ │ ├─ stats/ # 统计报表:请求量、token、成本、错误率
│ │ │ └─ audits/ # 审计日志:关键管理操作留痕
│ │ └─ shared-domain/ # 双端共享能力(无业务端特有语义)
│ │ ├─ ai-gateway/
│ │ │ ├─ providers/ # 第三方平台适配实现(qwen/volc/deepseek)
│ │ │ │ ├─ provider.interface.ts# provider 抽象协议,统一调用方式
│ │ │ │ ├─ qwen.provider.ts
│ │ │ │ ├─ volc.provider.ts
│ │ │ │ └─ deepseek.provider.ts
│ │ │ ├─ router/
│ │ │ │ └─ provider-router.service.ts # 自动路由与降级策略
│ │ │ └─ formatter/
│ │ │ └─ stream-normalizer.service.ts # 统一 SSE chunk 输出协议
│ │ ├─ identity/ # 身份能力:JWT 签发、策略、guard、token 工具
│ │ ├─ sms/ # 短信能力:验证码发送、频控、模板封装
│ │ ├─ mail/ # 邮件能力:登录通知、告警通知、模板发送
│ │ └─ stats-core/ # 统计核心:聚合计算、指标定义、通用查询
│ ├─ prisma/
│ │ ├─ prisma.module.ts # Prisma 注入模块
│ │ └─ prisma.service.ts # PrismaClient 生命周期与扩展
│ └─ types/ # 全局类型声明(枚举、共享接口)
├─ prisma/
│ ├─ schema.prisma # 数据模型定义
│ └─ migrations/ # 数据库迁移历史
├─ docs/
│ └─ project-solution.md # 架构与实现方案文档
├─ .env.example # 环境变量示例
├─ package.json # 依赖与脚本
└─ tsconfig.json # TypeScript 编译配置
3. 模块职责
3.1 用户端(client-app)
- client-auth
- 仅负责短信验证码发送/校验、客户端 token 签发与刷新
- chat
- 提供统一 chat 接口与指定平台 chat 接口
- 对外只暴露用户侧可见字段,不暴露平台敏感信息
- 按
controller -> application -> domain -> infrastructure分层,便于平滑拆分
- sessions/profile(可选)
- 会话记录、消息历史、个人资料
chat 子目录建议(简化版)
- controllers
- 仅处理 HTTP/SSE 协议、参数校验、响应头,不写业务逻辑
- application
- 编排用例流程(鉴权通过后,做配额检查、路由 provider、写 usage)
- domain
- 放会话、消息、配额策略等核心业务规则与仓储抽象接口
- infrastructure
- 对接 Prisma、Redis、SSE writer、消息队列等外部依赖实现
- dto
- 负责 API 入参与出参定义,避免直接暴露内部模型
- constants
- 统一维护 chat 错误码、模型别名、事件类型常量
建议保持这个粒度即可,后续按开发规模再向下细分,不必一开始拆到文件级别。
3.2 管理端(admin-app)
- admin-auth
- 仅负责邮箱密码登录(可选二次验证码)
- users
- 用户管理、封禁/解禁、角色治理
- platforms
- AI 平台开关、密钥、权重、模型映射、健康检查
- stats/audits
- 用量统计、成本分析、审计日志
3.3 共享域(shared-domain)
- ai-gateway
- 第三方平台适配、自动路由、流式格式统一
- identity
- JWT strategy、通用 guard、token 工具
- sms/mail/stats-core
- 可复用基础设施能力,避免双端重复实现
设计原则:控制器和应用服务严格按端隔离,后续拆分时优先迁移
client-app或admin-app整个目录,shared 部分按需复制或抽公共包。
4. API 设计(示例)
统一前缀建议:
- 客户端:
/api/client/v1 - 管理端:
/api/admin/v1
为避免后期冲突,建议从现在开始执行以下规范:
- DTO 分离:
client-app/dto与admin-app/dto禁止互相引用; - Guard 分离:
ClientJwtGuard与AdminJwtGuard不混用; - Swagger 分离:客户端文档与管理端文档分组输出(如
/docs-client、/docs-admin); - 错误码分离:客户端错误码与管理端错误码使用不同前缀(如
C_/A_)。
4.1 客户端认证接口(短信)
-
POST /api/client/v1/auth/sms/send- 入参:
{ phone, scene } - 出参:
{ requestId, expireIn }
- 入参:
-
POST /api/client/v1/auth/sms/login- 入参:
{ phone, code } - 出参:
{ accessToken, refreshToken, user }(user与users表对齐,建议含id、phone、nickname、avatarUrl等;未落库前可为占位结构)
- 入参:
-
POST /api/client/v1/auth/refresh- 入参:
{ refreshToken } - 出参:
{ accessToken, refreshToken }
- 入参:
4.2 客户端 AI Chat 接口(流式)
POST /api/client/v1/chat/completions/stream- 说明:统一接口,后台自动路由平台;支持会话续聊
- 入参示例:
{ "model": "qwen-plus", "messages": [ { "role": "system", "content": "你是助手" }, { "role": "user", "content": "介绍一下NestJS" } ], "platform": "auto", "sessionId": "1" } - 备注:
sessionId必填,需先调用创建会话接口;meta会返回sessionId(字符串),前端可缓存用于后续续聊。done前会先落库用户消息与会话标题,便于列表即时刷新。
- 返回:
text/event-stream(SSE)
4.3 客户端会话接口
-
POST /api/client/v1/chat/sessions- 说明:创建空会话(推荐在首次发送前调用)
- 入参:
{ title? } - 出参:
{ id, userId, title, createdAt, updatedAt }
-
GET /api/client/v1/chat/sessions?limit=&offset=- 说明:会话列表(按
updatedAt desc) - 出参:
{ items, total, limit, offset }
- 说明:会话列表(按
-
GET /api/client/v1/chat/sessions/:sessionId/messages?limit=&offset=- 说明:分页查询会话消息
- 出参:
{ sessionId, items, total, limit, offset }
-
DELETE /api/client/v1/chat/sessions/:sessionId- 说明:删除会话并级联删除该会话下全部消息
- 出参:删除成功后返回被删除会话信息
-
PATCH /api/client/v1/chat/sessions/:sessionId/title- 说明:修改会话标题(可传空字符串清空)
- 入参:
{ title } - 出参:更新后的会话信息
4.4 建议统一 SSE 事件格式
event: meta
data: {"requestId":"xxx","platform":"qwen","model":"qwen-plus","sessionId":"1"}
event: delta
data: {"delta":"你好"}
event: usage
data: {"promptTokens":120,"completionTokens":80,"totalTokens":200}
event: done
data: {"finishReason":"stop"}
event: error
data: {"code":"PLATFORM_TIMEOUT","message":"upstream timeout"}
4.5 管理端认证接口(邮箱)
-
POST /api/admin/v1/auth/login- 入参:
{ email, password } - 出参:
{ accessToken, refreshToken, admin }
- 入参:
-
POST /api/admin/v1/auth/refresh -
POST /api/admin/v1/auth/logout
4.6 管理端用户管理
GET /api/admin/v1/usersGET /api/admin/v1/users/:idPATCH /api/admin/v1/users/:id/status(启用/禁用)PATCH /api/admin/v1/users/:id/role
4.7 管理端平台管理
GET /api/admin/v1/platformsPOST /api/admin/v1/platformsPATCH /api/admin/v1/platforms/:idPATCH /api/admin/v1/platforms/:id/health-check
4.8 管理端统计
GET /api/admin/v1/stats/overview?startDate=&endDate=GET /api/admin/v1/stats/platformsGET /api/admin/v1/stats/users/top
5. 自动路由与统一格式方案
5.1 自动路由策略(简版)
按优先级执行:
- 过滤禁用平台;
- 过滤健康检查失败平台;
- 根据模型支持能力过滤;
- 根据权重 + 当前错误率 + 当前限流剩余综合打分;
- 选择得分最高平台;
- 失败时自动降级重试下一个平台(最多 1~2 次)。
5.2 统一消息格式(内部 DTO)
ChatMessageDto:{ role, content, name?, toolCall? }ChatRequestDto:{ model, messages, temperature, topP, stream, userId }ChatChunkDto:{ type: 'meta'|'delta'|'usage'|'done'|'error', payload }
第三方差异全部在 provider 内部适配,Controller 对外始终输出统一 SSE 事件。
6. 依赖清单(核心)
# Nest 基础
npm i @nestjs/common @nestjs/core @nestjs/platform-fastify @nestjs/config @nestjs/jwt @nestjs/passport @nestjs/swagger
npm i class-validator class-transformer passport passport-jwt
# 数据库/缓存
npm i @prisma/client ioredis
npm i -D prisma
# 日志/安全/限流
npm i nestjs-pino pino-http helmet @nestjs/throttler
# 任务/工具
npm i @nestjs/schedule dayjs
# 第三方请求
npm i undici
# 开发依赖
npm i -D typescript ts-node tsx @types/node eslint prettier
可选增强:
argon2:密码哈希zod:配置或响应结构额外校验@opentelemetry/api:链路追踪
7. 配置文件示例
7.1 .env.example
# App
NODE_ENV=development
PORT=3000
APP_NAME=chat-one-service
APP_BASE_URL=http://localhost:3000
# JWT
JWT_ACCESS_SECRET=replace_me_access
JWT_REFRESH_SECRET=replace_me_refresh
JWT_ACCESS_EXPIRES_IN=2h
JWT_REFRESH_EXPIRES_IN=30d
# PostgreSQL
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/chat_one?schema=public
# Redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# SMS(Spug 推送助手:https://push.spug.cc)
SPUG_PUSH_SMS_TEMPLATE_ID=你的消息模板编号
# SPUG_PUSH_BASE_URL=https://push.spug.cc
# SPUG_SMS_NAME=模板要求的 name 变量(可选)
# SMS_MOCK=true
SMS_CODE_TTL_SECONDS=300
# Mail (admin login / notice)
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
MAIL_SECURE=true
MAIL_USER=xxx@qq.com
MAIL_PASS=app_password
MAIL_FROM=ChatOne <xxx@qq.com>
# AI Providers
QWEN_API_KEY=your_qwen_key
QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
VOLC_API_KEY=your_volc_key
VOLC_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
DEEPSEEK_API_KEY=your_deepseek_key
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
# Route Strategy
AI_ROUTE_RETRY_TIMES=1
AI_ROUTE_TIMEOUT_MS=45000
# Split-ready(拆分预留)
CLIENT_PORT=3000
ADMIN_PORT=3001
CLIENT_JWT_ACCESS_SECRET=replace_me_client_access
ADMIN_JWT_ACCESS_SECRET=replace_me_admin_access
REDIS_KEY_PREFIX_CLIENT=chatone:client
REDIS_KEY_PREFIX_ADMIN=chatone:admin
7.2 src/config/validation.ts(建议)
使用 class-validator 或 zod 校验环境变量,应用启动即失败(fail fast),避免线上缺配置。
8. 数据库设计(PostgreSQL)
以下为简化且够用的核心表:
8.1 用户与管理员
-
usersid(bigserial pk)phone(varchar unique)nickname(varchar)avatar_url(varchar, nullable) — 头像地址(HTTPS 对象存储或 CDN URL);为空时客户端展示默认头像status(smallint, 1正常 0禁用)created_at,updated_at
-
adminsidemail(varchar unique)password_hash(varchar)role(varchar: super_admin/admin/ops)statuslast_login_atcreated_at,updated_at
8.2 平台配置与模型映射
-
ai_platformsidcode(varchar unique: qwen/volc/deepseek)nameenabled(bool)weight(int default 100)priority(int default 100)base_urlapi_key_encryptedtimeout_msrpm_limithealth_status(varchar: healthy/unhealthy/unknown)created_at,updated_at
-
ai_platform_modelsidplatform_id(fk)biz_model(varchar) # 业务统一模型名provider_model(varchar) # 平台真实模型名enabled(bool)- unique(platform_id, biz_model)
8.3 会话与消息(已落库)
-
chat_sessionsiduser_id(fk)titlecreated_at,updated_at- index:
user_id
-
chat_messagesidsession_id(fk)role(user/assistant/system)content(text)token_count(int)provider(varchar)created_at- index:
session_id session_id外键onDelete: Cascade
8.4 请求审计与统计
-
ai_request_logsidrequest_id(varchar unique)user_id(fk nullable)platform_codebiz_modelprovider_modelstatus(success/fail)error_codelatency_msprompt_tokenscompletion_tokenstotal_tokensestimated_costcreated_at
-
usage_daily_statsidstat_date(date)dimension(platform/user/total)dimension_key(varchar)request_countsuccess_countfail_counttotal_tokensestimated_cost- unique(stat_date, dimension, dimension_key)
9. 缓存方案(Redis)
建议键设计:
- 短信验证码:
sms:code:{phone}:{scene}(TTL 5 分钟) - 短信发送频控:
sms:send:limit:{phone}(TTL 60 秒) - 刷新令牌黑名单:
auth:rt:blacklist:{jti}(TTL 到 token 过期) - 用户会话缓存:
auth:user:{userId}(TTL 30 分钟,可选) - 平台健康状态:
ai:platform:health:{code}(TTL 30 秒) - 平台动态权重:
ai:platform:weight:{code}(TTL 60 秒) - 统计临时聚合:
stats:daily:{date}:{dimension}(TTL 1 天)
为便于拆分,建议从第一天就加命名空间前缀:
- 用户端:
chatone:client:* - 管理端:
chatone:admin:* - 共享:
chatone:shared:*
同时启用 @nestjs/throttler 做接口限流:
- 客户端 chat 接口:按用户 + IP 双维度限流;
- 登录接口:按手机号/邮箱严格限流,防刷。
10. 登录与鉴权方案
10.1 客户端(短信登录)
- 用户请求发送验证码;
- 服务端生成验证码写入 Redis(哈希存储,避免明文);
- 用户提交验证码登录,校验通过后签发 JWT;
- AccessToken 短期(2h),RefreshToken 长期(30d);
- 刷新时校验 RefreshToken 的
jti与黑名单状态; - 注销时将 RefreshToken
jti拉黑至过期。
10.2 管理端(邮箱登录)
- 邮箱 + 密码登录(密码
argon2哈希); - 可选开启邮箱二次验证码;
- JWT + RBAC(
super_admin/admin/ops); - 管理端接口统一加
JwtAuthGuard + RolesGuard; - 关键操作(平台密钥修改、用户封禁)记录审计日志。
10.3 Token 建议
- AccessToken:只放必要字段(
sub,role,type) - RefreshToken:包含
jti,便于撤销 - 密钥轮转:支持双密钥平滑切换(
kid) - 传输:优先
Authorization: Bearer;管理端也可改 HttpOnly Cookie - 强隔离建议:客户端与管理端使用不同
issuer、audience、secret,token 绝不互认
11. 拆分演进路线(低成本)
阶段 A:单体双端(当前推荐)
- 一个 Nest 进程,两个 AppModule(
client-app/admin-app); - 共用数据库与 Redis,但 key 前缀、token 体系已隔离;
- 发布快,开发成本最低。
阶段 B:双进程同仓
- 启动两个入口:
main.client.ts、main.admin.ts; - 用户端和管理端可独立扩容、独立发布;
- 共享逻辑仍来自
shared-domain。
阶段 C:双服务拆仓(可选)
client-service:保留短信、chat、会话;admin-service:保留邮箱登录、平台管理、统计审计;shared-domain抽为内部 npm 包或 Git 子模块。
12. 最小可用开发流程(MVP)
建议按下面顺序实现,2~3 周可交付可用版本:
- 初始化 NestJS + Prisma + PostgreSQL + Redis;
- 完成客户端短信登录与 JWT 刷新;
- 完成 Qwen 单平台流式 chat;
- 抽象 provider 接口,接入 Volc、DeepSeek;
- 实现统一流式格式 + 自动路由;
- 完成管理端邮箱登录;
- 完成平台管理、用户管理;
- 完成请求日志与基础统计报表;
- 完成 Swagger、限流、异常处理与日志。
13. 非功能建议(上线前)
- 安全:API Key 加密存储;生产环境开启 Helmet/CORS 白名单;
- 稳定性:上游超时、熔断、重试与降级;
- 可观测性:请求链路 ID、结构化日志、错误告警;
- 成本控制:平台权重 + 单用户日额度 + 模型白名单;
- 测试:核心流程至少有 e2e(登录、chat、管理端鉴权)。
14. 一句话总结
该方案以 NestJS + Prisma + PostgreSQL + Redis 为核心,采用“用户端/管理端强隔离 + shared-domain 复用”组织代码,在保持开发效率的同时确保后续可平滑拆分为两个独立服务,并持续支持多平台 AI 路由与统一流式协议。