Files
NewPlatform/docs_old/architecture/technology-stack-details.md
2026-08-23 00:16:15 +08:00

10 KiB
Raw Permalink Blame History

AIwelink 技术栈选型细节

本文是 技术栈选型 的细节说明,记录当前边界、约束、实现规则和演进条件。

1. Web 与 API 的同域名反向代理

1.1 对外访问模型

第一阶段使用同一个公网域名:

https://aiwelink.com/                 -> Next.js
https://aiwelink.com/api/v1/...      -> REST API

入口反向代理负责路由:

Client
  │
  ▼
Nginx
  ├── /api/v1/*  -> api:3000
  └── /*         -> web:3000

NestJS 设置全局 API 前缀 /api/v1。反向代理必须统一是否保留路径前缀,避免产生 /api/v1/api/v1/... 的重复路径。

1.2 同域名的优点

  • 浏览器不需要跨域访问 API。
  • HttpOnly Cookie 的会话处理更直接。
  • 分享页、公开赛事页和登录后工作台使用同一域名。
  • 本地开发、测试和生产环境的请求路径一致。
  • 将来即使迁移到 api.aiwelink.com,外部资源路径仍可以保持不变。

1.3 注意事项

  • API 和 Web 要在入口层设置不同的超时、缓存和安全策略。
  • API 不应由 Next.js 页面逻辑重复实现。
  • Next.js 服务端调用 API 时优先使用 Docker 内部网络,而不是绕回公网域名。
  • 如果未来提供 App、第三方赛事系统或开放平台,再单独设计 CORS、Token 和 API 子域名策略。
  • 如果未来使用 SSE,反向代理要关闭响应缓冲并设置长连接超时。

2. NestJS + Fastify Adapter

2.1 选择原因

NestJS 提供模块、依赖注入、Guard、拦截器、异常过滤器和 OpenAPI 集成;Fastify Adapter 提供轻量 HTTP 层。该组合适合授权、项目、邀请、积分、订单等规则较多的业务。

2.2 代码约束

  • 统一使用 Fastify 兼容的 NestJS 插件。
  • 不在 Controller 中编写数据库查询和复杂业务规则。
  • 通过 Guard 和服务端资源归属检查完成权限控制。
  • 统一错误格式和 requestId。
  • 统一使用 DTO 校验输入,不直接把数据库实体作为 API 响应。
  • API 使用 /api/v1 版本前缀,JSON 字段使用 camelCase,URL 使用小写短横线。

2.3 推荐的模块布局

src/
  modules/
    identity/
    users/
    profiles/
    consents/
    projects/
    opportunities/
    invitations/
    points/
    commerce/
    recommendations/
    operations/
  common/
    guards/
    filters/
    interceptors/
    decorators/
  infrastructure/
    database/
    storage/
    cache/

3. RESTful API 约束

3.1 资源设计

使用名词和复数:

GET    /api/v1/projects
GET    /api/v1/projects/{projectId}
POST   /api/v1/projects
PATCH  /api/v1/projects/{projectId}
DELETE /api/v1/projects/{projectId}

嵌套资源表示明确的从属关系:

GET  /api/v1/projects/{projectId}/members
POST /api/v1/projects/{projectId}/applications

不要使用 getProjects、createProject、generateReport 这类动词路径。生成报告等动作应建模为创建报告资源或创建任务资源。

3.2 状态码

  • 200:读取或更新成功。
  • 201:创建资源成功。
  • 202:异步任务已接受。
  • 204:成功但无响应体。
  • 400:请求格式错误。
  • 401:未登录或会话无效。
  • 403:无权限。
  • 404:资源不存在。
  • 409:资源状态冲突。
  • 422:字段校验失败。
  • 429:请求过于频繁。
  • 500:服务端异常。

3.3 响应和错误

成功响应统一包含 data,列表响应在 meta 中返回游标信息。错误建议采用 application/problem+json,至少包含 type、title、status、code、detail 和 requestId。

3.4 幂等和安全

以下写操作需要支持 Idempotency-Key:

  • 订单创建。
  • 积分发放。
  • 邀请接受。
  • 付费权益创建。
  • 可能重复提交的报告任务。

权限检查必须在 API 服务端完成,不能依赖前端隐藏按钮。

4. Identity 模块细节

4.1 责任边界

Identity 模块只负责身份和会话,不负责个人能力画像、项目经历或心理测评结论。

建议的核心表:

users
user_identities
sessions
email_verifications
login_events

4.2 登录方式

  • 微信扫码或授权登录作为主入口。
  • 邮箱用于绑定、验证、通知和账号恢复。
  • 登录方式与用户主体分离,允许同一用户绑定多个身份。
  • 会话使用 HttpOnly、Secure Cookie。
  • 服务端保存会话状态和撤销记录。
  • 长期 Token 不存储在 localStorage。

4.3 后续可扩展

未来可增加手机号、学校身份验证或第三方身份,但不改变 users 作为统一用户主体的模型。

5. Drizzle 与原生 SQL

5.1 默认规则

场景 方式
普通增删改查 Drizzle
普通事务 Drizzle transaction
JSONB 复杂筛选 Drizzle + SQL
pgvector 相似度 SQL Repository
推荐排序 SQL Repository
全文搜索 SQL Repository
报表和聚合 SQL Repository

原生 SQL 必须参数化,禁止字符串拼接用户输入。

5.2 访问层边界

Controller
  ↓
Application Service
  ↓
Repository Interface
  ↓
Drizzle Repository / SQL Repository
  ↓
PostgreSQL

Controller 不直接访问数据库,也不直接依赖 Drizzle 实现。

5.3 迁移

  • 数据库结构必须版本化迁移。
  • 普通表结构可由 Drizzle Schema 和迁移工具维护。
  • pgvector、特殊索引、函数、分区等能力可以使用手写迁移 SQL。
  • 生产环境禁止手工修改表结构而不留下迁移记录。

6. PostgreSQL、JSONB 和 pgvector

6.1 PostgreSQL 作为主数据源

PostgreSQL 保存所有需要事务一致性的业务数据:用户、身份、授权、项目、成员、申请、积分、订单、报告元数据和推荐记录。

Redis 不保存这些数据的最终状态。

6.2 JSONB 使用边界

适合 JSONB:

  • 灵活的用户偏好。
  • Agent 输出的结构化报告内容。
  • 版本化的推荐特征快照。
  • 运营可配置字段。

不适合全部放入 JSONB:

  • 用户身份。
  • 权限和授权。
  • 订单金额和状态。
  • 积分流水。
  • 项目成员关系。

这些核心关系仍应使用结构化字段和约束。

6.3 pgvector 使用边界

pgvector 用于项目、课程、学科和公开内容的语义召回。向量记录至少保存:

  • 来源资源 ID。
  • Embedding 模型名称。
  • 模型版本。
  • 向量维度。
  • 生成时间。
  • 来源内容版本。

原始测评答案和敏感心理数据不应默认向量化,也不应直接作为不可解释的决策依据。

7. 分层存储与分表策略

7.1 逻辑分层

建议至少划分以下数据层:

身份层:users、user_identities、sessions
资料层:profile_basics、profile_preferences、public_profiles
认知层:assessment_sessions、assessment_answers、insight_snapshots
证据层:insight_evidence、project_reviews、behavior_events
授权层:consent_grants、consent_audits
项目层:opportunities、projects、project_members、applications
增长层:referral_events、point_ledger
审计层:audit_logs

7.2 分表原则

现在就按业务边界拆分表,便于权限、索引和生命周期管理。不要按“感觉可能很大”把一个简单实体过度拆成多个表。

大型追加型表应优先考虑:

  • 合理索引。
  • 时间范围查询。
  • 冷数据归档。
  • 单独的查询 Repository。
  • 后续按时间做 PostgreSQL 分区。

暂不做跨库分片和手工水平分片。

8. Redis 使用边界

Redis 用于:

  • 缓存热点数据。
  • 请求限流。
  • 短期验证码和临时状态。
  • 队列基础设施。
  • 幂等键状态。
  • 短期排行榜。

Redis 不用于保存:

  • 最终积分余额。
  • 订单状态。
  • 授权记录。
  • 用户画像原始数据。
  • 项目成员关系。

9. 阿里云 OSS 文件流转

推荐流程:

API 创建临时凭证
  ↓
浏览器直传 OSS
  ↓
上传完成通知 API
  ↓
PostgreSQL 写入文件元数据
  ↓
按需执行解析、审核或索引

数据库只保存:

fileId
ownerId
objectKey
mimeType
size
hash
visibility
createdAt

私有文件只能通过短期签名 URL 访问。上传必须限制类型、大小和归属,不能仅依赖文件扩展名。

10. ECS + Docker Compose

10.1 第一阶段服务

nginx
web
api
worker(具体 Agent 方案待定)
postgres
redis

应用容器需要配置健康检查和自动重启。镜像推送到镜像仓库,生产使用固定版本标签,不直接使用 latest。

10.2 对 Kubernetes 的兼容性

现在就遵守:

  • 应用服务无状态。
  • 配置通过环境变量或 Secret 注入。
  • OSS 保存文件,不使用容器本地磁盘。
  • 数据库迁移独立执行。
  • Web、API、Worker 独立容器。
  • 服务之间通过内部 DNS 或配置发现。

这些约束可以降低未来迁移 K8S 的改造量。

10.3 数据服务部署建议

如果 PostgreSQL 和 Redis 也运行在 ECS 上,应与应用容器至少分开主机或独立数据盘,并配置:

  • 自动备份。
  • 备份保留周期。
  • 恢复演练。
  • 磁盘空间监控。
  • 数据库升级和回滚方案。

11. Uptime Kuma 与测试

11.1 Uptime Kuma 的职责

Uptime Kuma 负责基础可用性:

  • 首页 HTTP 监控。
  • API 存活和就绪监控。
  • TCP/端口监控。
  • HTTPS 证书到期提醒。

建议提供:

GET /api/v1/health/live
GET /api/v1/health/ready

live 只判断进程是否存活;ready 检查 PostgreSQL、Redis 和必要配置是否可用。

11.2 测试不由 Uptime Kuma 替代

Uptime Kuma 不执行单元测试、接口测试或浏览器流程测试。最低测试基线建议为:

  • TypeScript 类型检查。
  • Lint。
  • API 单元和集成测试。
  • 关键流程端到端测试。
  • 发布前数据库迁移检查。

具体测试框架可以在开始实现前再确认,但必须覆盖:

微信登录
→ 邮箱绑定
→ 创建个人资料
→ 创建项目
→ 发送邀请
→ 授权共享
→ 项目申请
→ 积分流水

12. 暂不定型事项

以下内容当前不做最终技术承诺:

  • Agent 使用的模型供应商和模型路由。
  • Agent 对话编排框架。
  • 长报告是否使用队列以及具体队列库。
  • 推荐系统是否拆成独立服务。
  • 是否引入专用搜索引擎。
  • Kubernetes 的具体发行版和部署方式。
  • 完整日志、指标和链路追踪方案。

这些选项应在业务流程、数据量和实际故障模式出现后再定型。