Files
chat-one-service/docs/project-solution.md
alboped 32303d099a feat(client): 新增会话管理与消息落库能力
补齐客户端会话生命周期接口(创建、列表、消息分页、改名、删除),并在流式 chat 中强制绑定 sessionId 与落库消息,确保会话标题和历史可追踪,同时统一 Swagger 文档为 DTO 驱动以减少重复维护。

Made-with: Cursor
2026-04-22 23:32:10 +08:00

22 KiB
Raw Permalink Blame History

ChatOne Service 项目方案NestJS

本文给出一个主流且简洁、可直接落地的服务端方案,满足以下目标:

  • 基于 NestJS 构建;
  • 同时提供客户端接口与后台管理接口;
  • 支持短信登录、邮箱登录;
  • 支持千问、火山引擎、DeepSeek 等多平台 AI Chat 流式输出;
  • 对外提供统一调用接口(自动路由)和指定平台调用接口;
  • 提供用户管理、平台管理、用量统计。

目录


1. 技术选型(主流且简洁)

  • 运行时Node.js 20 LTS
  • 框架NestJS 10Fastify 适配器,性能更优)
  • ORMPrisma类型安全、迁移友好
  • 数据库PostgreSQL 15
  • 缓存/队列Redis 7缓存、限流、验证码、会话黑名单
  • 鉴权JWTAccess + Refresh+ RBAC
  • 文档Swagger@nestjs/swagger
  • 校验class-validator + class-transformer
  • 日志Pinonestjs-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-appadmin-app 整个目录shared 部分按需复制或抽公共包。


4. API 设计(示例)

统一前缀建议:

  • 客户端:/api/client/v1
  • 管理端:/api/admin/v1

为避免后期冲突,建议从现在开始执行以下规范:

  • DTO 分离:client-app/dtoadmin-app/dto 禁止互相引用;
  • Guard 分离:ClientJwtGuardAdminJwtGuard 不混用;
  • 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 }userusers 表对齐,建议含 idphonenicknameavatarUrl 等;未落库前可为占位结构)
  • 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-streamSSE

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/users
  • GET /api/admin/v1/users/:id
  • PATCH /api/admin/v1/users/:id/status(启用/禁用)
  • PATCH /api/admin/v1/users/:id/role

4.7 管理端平台管理

  • GET /api/admin/v1/platforms
  • POST /api/admin/v1/platforms
  • PATCH /api/admin/v1/platforms/:id
  • PATCH /api/admin/v1/platforms/:id/health-check

4.8 管理端统计

  • GET /api/admin/v1/stats/overview?startDate=&endDate=
  • GET /api/admin/v1/stats/platforms
  • GET /api/admin/v1/stats/users/top

5. 自动路由与统一格式方案

5.1 自动路由策略(简版)

按优先级执行:

  1. 过滤禁用平台;
  2. 过滤健康检查失败平台;
  3. 根据模型支持能力过滤;
  4. 根据权重 + 当前错误率 + 当前限流剩余综合打分;
  5. 选择得分最高平台;
  6. 失败时自动降级重试下一个平台(最多 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

# SMSSpug 推送助手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-validatorzod 校验环境变量应用启动即失败fail fast避免线上缺配置。


8. 数据库设计PostgreSQL

以下为简化且够用的核心表:

8.1 用户与管理员

  • users

    • id (bigserial pk)
    • phone (varchar unique)
    • nickname (varchar)
    • avatar_url (varchar, nullable) — 头像地址HTTPS 对象存储或 CDN URL为空时客户端展示默认头像
    • status (smallint, 1正常 0禁用)
    • created_at, updated_at
  • admins

    • id
    • email (varchar unique)
    • password_hash (varchar)
    • role (varchar: super_admin/admin/ops)
    • status
    • last_login_at
    • created_at, updated_at

8.2 平台配置与模型映射

  • ai_platforms

    • id
    • code (varchar unique: qwen/volc/deepseek)
    • name
    • enabled (bool)
    • weight (int default 100)
    • priority (int default 100)
    • base_url
    • api_key_encrypted
    • timeout_ms
    • rpm_limit
    • health_status (varchar: healthy/unhealthy/unknown)
    • created_at, updated_at
  • ai_platform_models

    • id
    • platform_id (fk)
    • biz_model (varchar) # 业务统一模型名
    • provider_model (varchar) # 平台真实模型名
    • enabled (bool)
    • unique(platform_id, biz_model)

8.3 会话与消息(已落库)

  • chat_sessions

    • id
    • user_id (fk)
    • title
    • created_at, updated_at
    • index: user_id
  • chat_messages

    • id
    • session_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_logs

    • id
    • request_id (varchar unique)
    • user_id (fk nullable)
    • platform_code
    • biz_model
    • provider_model
    • status (success/fail)
    • error_code
    • latency_ms
    • prompt_tokens
    • completion_tokens
    • total_tokens
    • estimated_cost
    • created_at
  • usage_daily_stats

    • id
    • stat_date (date)
    • dimension (platform/user/total)
    • dimension_key (varchar)
    • request_count
    • success_count
    • fail_count
    • total_tokens
    • estimated_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 客户端(短信登录)

  1. 用户请求发送验证码;
  2. 服务端生成验证码写入 Redis哈希存储避免明文
  3. 用户提交验证码登录,校验通过后签发 JWT
  4. AccessToken 短期2hRefreshToken 长期30d
  5. 刷新时校验 RefreshToken 的 jti 与黑名单状态;
  6. 注销时将 RefreshToken jti 拉黑至过期。

10.2 管理端(邮箱登录)

  1. 邮箱 + 密码登录(密码 argon2 哈希);
  2. 可选开启邮箱二次验证码;
  3. JWT + RBACsuper_admin / admin / ops
  4. 管理端接口统一加 JwtAuthGuard + RolesGuard
  5. 关键操作(平台密钥修改、用户封禁)记录审计日志。

10.3 Token 建议

  • AccessToken只放必要字段sub, role, type
  • RefreshToken包含 jti,便于撤销
  • 密钥轮转:支持双密钥平滑切换(kid
  • 传输:优先 Authorization: Bearer;管理端也可改 HttpOnly Cookie
  • 强隔离建议:客户端与管理端使用不同 issueraudiencesecrettoken 绝不互认

11. 拆分演进路线(低成本)

阶段 A单体双端当前推荐

  • 一个 Nest 进程,两个 AppModuleclient-app / admin-app
  • 共用数据库与 Redis但 key 前缀、token 体系已隔离;
  • 发布快,开发成本最低。

阶段 B双进程同仓

  • 启动两个入口:main.client.tsmain.admin.ts
  • 用户端和管理端可独立扩容、独立发布;
  • 共享逻辑仍来自 shared-domain

阶段 C双服务拆仓可选

  • client-service保留短信、chat、会话
  • admin-service:保留邮箱登录、平台管理、统计审计;
  • shared-domain 抽为内部 npm 包或 Git 子模块。

12. 最小可用开发流程MVP

建议按下面顺序实现2~3 周可交付可用版本:

  1. 初始化 NestJS + Prisma + PostgreSQL + Redis
  2. 完成客户端短信登录与 JWT 刷新;
  3. 完成 Qwen 单平台流式 chat
  4. 抽象 provider 接口,接入 Volc、DeepSeek
  5. 实现统一流式格式 + 自动路由;
  6. 完成管理端邮箱登录;
  7. 完成平台管理、用户管理;
  8. 完成请求日志与基础统计报表;
  9. 完成 Swagger、限流、异常处理与日志。

13. 非功能建议(上线前)

  • 安全API Key 加密存储;生产环境开启 Helmet/CORS 白名单;
  • 稳定性:上游超时、熔断、重试与降级;
  • 可观测性:请求链路 ID、结构化日志、错误告警
  • 成本控制:平台权重 + 单用户日额度 + 模型白名单;
  • 测试:核心流程至少有 e2e登录、chat、管理端鉴权

14. 一句话总结

该方案以 NestJS + Prisma + PostgreSQL + Redis 为核心,采用“用户端/管理端强隔离 + shared-domain 复用”组织代码,在保持开发效率的同时确保后续可平滑拆分为两个独立服务,并持续支持多平台 AI 路由与统一流式协议。