Skip to content

项目规划与设计

项目概述

为 Flutter 移动应用开发 API 服务端 + 管理后台,基于 Nuxt 4 全栈开发。

核心功能

  1. 用户认证:注册、登录、短信验证码(腾讯云/阿里云)
  2. 实时消息推送:WebSocket 长连接、HTTP API 触发推送、离线消息补发
  3. 会员订阅:微信支付、支付宝支付
  4. 管理后台:用户管理、内容管理、订单管理
  5. 安全防护:输入校验、速率限制、XSS/CSRF 防护

技术选型

层级技术说明
框架Nuxt 4全栈框架
数据库PostgreSQL主数据库
ORMDrizzle ORM类型安全的 ORM
缓存Redis会话缓存、速率限制、跨实例 PubSub
认证JWT无状态认证
实时通信WebSocket (Nitro 内置)消息推送、实时通知
支付微信支付/支付宝SDK 集成
短信腾讯云/阿里云 SMS验证码发送
UINuxt UI管理后台组件库
校验Zod输入验证
部署Docker + PM2容器化部署

为什么选这些技术?

为什么用 Nuxt 4 而不是 Express + Vue?

对比项Nuxt 4 全栈Express + Vue 分离
项目数量1 个项目2 个项目(前端 + 后端)
部署方式1 次 nuxt build,1 个 Node 进程分别构建、分别部署
类型共享shared/ 目录自动共享需要发布 npm 包或复制代码
开发体验热更新统一、自动导入两套配置,需要 CORS 联调
SSR 支持内置,routeRules 按需开启需要单独配置 SSR 服务器
代码重复API 路由 + 页面同项目接口定义两边各写一次

选择 Nuxt 的核心理由:本项目同时需要 API 服务和管理后台,Nuxt 一个项目即可同时实现两端,避免维护两个独立项目的开销。

为什么选 PostgreSQL 而不是 MySQL?

对比项PostgreSQLMySQL
数据类型丰富的 JSONB、数组、UUID 原生支持JSON 支持弱,无数组类型
扩展性PostGIS(地理)、pg_trgm(模糊搜索)扩展生态较弱
并发控制MVCC,读写不互锁写操作可能锁表
ORM 兼容性Drizzle 对 PG 支持最完善Drizzle 支持 PG 和 MySQL,但 PG 优先
金额精度numeric 类型精确计算无浮点误差DECIMAL 也可,但 PG 更可靠

选择 PostgreSQL 的核心理由:JSONB 支持灵活存储、Drizzle ORM 对 PG 支持最成熟、金额计算(订单系统)需要精确数值类型。

为什么选 Drizzle ORM 而不是 Prisma?

对比项Drizzle ORMPrisma
包体积~40KB~10MB(Rust 引擎)
构建速度快,纯 TypeScript慢,需生成 Prisma Client
SQL 控制接近原生 SQL,可写复杂查询抽象层厚,复杂查询需 raw SQL
类型安全基于表的类型推导基于 schema.prisma 生成
迁移方式SQL 文件可直接审查和修改迁移文件是自动生成的,难以审查
边缘运行完全兼容Prisma 加速引擎需特殊配置

选择 Drizzle 的核心理由:轻量、构建快、迁移文件可审查、更贴近 SQL 思维——适合需要完全掌控数据库的项目。

为什么用 JWT 而不是 Session?

对比项JWTSession
服务器存储无状态,不需要存储需要内存或 Redis 存储
水平扩展天然支持多实例需要 Redis 共享 Session
客户端适配移动端(Flutter)原生支持Cookie 在移动端不友好
即时失效不能即时撤销(需黑名单)删除即失效
安全性依赖签名验证 + 黑名单依赖服务器端存储

选择 JWT 的核心理由:本项目主要服务 Flutter 移动端,JWT 在移动端集成更自然;同时支持 Cookie 模式(管理后台 Web 端),实现"一套后端,双模式认证"。

注意

JWT 最大的缺点是"无法即时撤销"。本方案通过 Redis 黑名单补足——用户登出时将 Token 加入黑名单,认证中间件检查黑名单即可。

架构设计决策

认证双模式架构

本项目同时服务移动端和 Web 管理后台,两者认证方式不同:

text
┌─────────────────────────────────────────────────────────┐
│                     客户端请求                            │
├───────────────────────┬─────────────────────────────────┤
│  Flutter 移动端       │  Web 管理后台                     │
│  Authorization:       │  Cookie: auth-token              │
│  Bearer <token>       │  (httpOnly, secure, sameSite)    │
├───────────────────────┴─────────────────────────────────┤
│               服务端认证中间件                            │
│  1. 优先检查 Authorization header(Bearer Token)        │
│  2. 其次检查 Cookie(auth-token)                        │
│  3. 验证 JWT 签名 + 检查黑名单                            │
│  4. 写入 event.context.userId / userRole                 │
└─────────────────────────────────────────────────────────┘

为什么需要双模式

  • 移动端用 Authorization: Bearer 是业界标准,Flutter 的 HTTP 客户端原生支持
  • 管理后台用 Cookie 是因为浏览器自动携带,且 httpOnly 可防止 XSS 读取
  • 两种方式共用同一套 JWT 签发逻辑,后端零额外成本

API 设计原则

原则做法原因
RESTful 风格GET /api/usersPOST /api/orders语义清晰,前端开发者一目了然
版本化预留路由以 /api/ 为前缀未来如需 v2,可加 /api/v2/ 前缀
认证/公开路由分离公开路由白名单在中间件中维护避免逐个 API 判断,集中管理
管理后台路由隔离/api/admin/** 需要 admin 角色中间件级别拦截,不存在越权风险
支付回调无认证/api/payments/** 不经过 auth 中间件第三方支付平台无法携带 JWT,用签名验证替代

数据库设计

用户表 (users)

字段类型说明
idserial主键
phonevarchar(20)手机号
emailvarchar(100)邮箱
password_hashvarchar(255)密码哈希
nicknamevarchar(50)昵称
avatarvarchar(255)头像 URL
rolevarchar(20)角色:user/vip/admin
statussmallint状态:0禁用/1正常
vip_expires_attimestampVIP 过期时间
created_attimestamp创建时间
updated_attimestamp更新时间

订单表 (orders)

字段类型说明
idserial主键
user_idinteger用户 ID
order_novarchar(64)订单号
plan_idinteger套餐 ID
amountdecimal(10,2)金额
payment_methodvarchar(20)支付方式
payment_novarchar(64)支付流水号
statussmallint0待支付/1已支付/2已取消/3已退款
paid_attimestamp支付时间
created_attimestamp创建时间

会员套餐表 (plans)

字段类型说明
idserial主键
namevarchar(50)套餐名称
duration_daysinteger有效天数
pricedecimal(10,2)价格
original_pricedecimal(10,2)原价
sort_orderinteger排序
statussmallint0下架/1上架

短信验证码表 (sms_codes)

字段类型说明
idserial主键
phonevarchar(20)手机号
codevarchar(6)验证码
usedboolean是否已使用
expires_attimestamp过期时间
created_attimestamp创建时间

通知表 (notifications)

字段类型说明
idserial主键
user_idinteger目标用户 ID
titlevarchar(100)通知标题
contenttext通知内容
typevarchar(20)通知类型:system/order/promotion
is_readboolean是否已读
created_attimestamp创建时间

数据库设计要点

  • 金额用 decimal 而非 float:避免浮点精度丢失,订单金额必须精确到分
  • 手机号设 unique 约束:防止重复注册,比应用层检查更可靠
  • sms_codesusedexpires_at:验证码一次有效 + 有过期时间,防止重放攻击
  • 订单号 order_nounique:支付回调时通过订单号定位,必须唯一
  • notificationsuser_id 索引:按用户查询通知是最高频操作,必须加速
  • notifications.type 用枚举值:system/order/promotion 三种类型,便于分类筛选

API 设计

认证相关

http
POST /api/auth/register       # 注册
POST /api/auth/login          # 登录
POST /api/auth/logout         # 登出
POST /api/auth/refresh        # 刷新 Token
POST /api/auth/sms/send       # 发送验证码
POST /api/auth/sms/verify     # 验证验证码

用户相关

http
GET  /api/user/profile        # 获取个人信息
PUT  /api/user/profile        # 更新个人信息
PUT  /api/user/password       # 修改密码

会员相关

http
GET  /api/plans                # 获取套餐列表
POST /api/orders               # 创建订单
GET  /api/orders               # 获取订单列表
POST /api/orders/:id/pay       # 发起支付
POST /api/payments/wechat/notify  # 微信支付回调
POST /api/payments/alipay/notify  # 支付宝回调

管理后台

http
GET  /api/admin/users          # 用户列表
GET  /api/admin/users/:id      # 用户详情
PUT  /api/admin/users/:id      # 更新用户
GET  /api/admin/orders         # 订单列表
GET  /api/admin/stats          # 统计数据

消息推送

http
WebSocket /api/_ws             # WebSocket 连接端点(?token=JWT_TOKEN)
POST /api/push/send            # 推送消息给指定用户
POST /api/push/batch           # 批量推送消息
POST /api/push/broadcast       # 全站广播(仅管理员)
GET  /api/notifications        # 获取通知列表
PUT  /api/notifications/read   # 标记通知已读

项目结构

text
my-app/
├── app/                          # 【前端目录】所有客户端代码(页面、组件、布局、中间件等)
│   │                             # Nuxt 4 中 app/ 替代了原来的根目录,是前端代码的标准位置
│   ├── pages/                    # 【页面目录】基于文件的路由系统,每个 .vue 文件自动生成对应路由
│   │   ├── admin/                # 管理后台页面组(路由前缀 /admin/)
│   │   │   ├── index.vue         # 仪表盘首页 → /admin
│   │   │   ├── login.vue         # 后台登录页 → /admin/login
│   │   │   ├── users/            # 用户管理页面组
│   │   │   │   ├── index.vue     # 用户列表页 → /admin/users
│   │   │   │   └── [id].vue      # 用户详情页 → /admin/users/:id(动态路由)
│   │   │   ├── orders/           # 订单管理页面组
│   │   │   │   └── index.vue     # 订单列表页 → /admin/orders
│   │   │   ├── plans/            # 套餐管理页面组
│   │   │   │   └── index.vue     # 套餐列表页 → /admin/plans
│   │   │   └── content/          # 内容管理页面组
│   │   │       └── index.vue     # 内容列表页 → /admin/content
│   │   └── ...                   # 其他前端页面(如用户端页面)
│   │
│   ├── components/               # 【组件目录】可复用的 Vue 组件,支持自动导入
│   │   └── admin/                # 管理后台专用组件
│   │       └── ContentEditor.vue # 富文本编辑器组件(基于 Tiptap)
│   │
│   ├── layouts/                  # 【布局目录】页面布局模板,通过 definePageMeta 指定
│   │   ├── default.vue           # 默认布局(普通页面使用)
│   │   └── admin.vue             # 管理后台布局(侧边栏 + 主内容区)
│   │
│   ├── composables/              # 【组合式函数目录】可复用的响应式逻辑,支持自动导入
│   │   └── useAuth.ts            # 认证相关逻辑(获取用户状态、登出等)
│   │
│   ├── middleware/               # 【路由中间件目录】页面导航守卫,在路由跳转前执行
│   │   ├── auth.ts               # 前端认证中间件:检查用户是否已登录
│   │   └── admin-auth.ts         # 管理员中间件:检查用户是否为 admin 角色
│   │
│   └── plugins/                  # 【插件目录】Vue/Nuxt 插件,应用启动时自动注册
│       └── error-handler.ts      # 全局错误处理插件(捕获 Vue 错误、未处理的 Promise 拒绝)

├── server/                       # 【服务端目录】所有后端代码(API、中间件、数据库、工具函数等)
│   │                             # Nuxt 基于 Nitro 引擎,server/ 下的代码仅在服务端运行
│   ├── database/                 # 【数据库目录】数据库连接和 Schema 定义
│   │   ├── db.ts                 # 数据库连接实例(Drizzle ORM + postgres.js 连接池)
│   │   └── schema.ts             # 数据表 Schema 定义(users、orders、plans、sms_codes)
│   │
│   ├── api/                      # 【API 路由目录】基于文件的路由系统,自动生成 API 端点
│   │   │                         # 文件名决定 HTTP 方法和路径,如 login.post.ts → POST /api/auth/login
│   │   ├── auth/                 # 认证相关 API
│   │   │   ├── register.post.ts  # POST /api/auth/register - 用户注册(手机号+验证码+密码)
│   │   │   ├── login.post.ts     # POST /api/auth/login - 用户登录(密码或验证码)
│   │   │   ├── refresh.post.ts   # POST /api/auth/refresh - 刷新 JWT Token
│   │   │   └── sms/              # 短信验证码 API
│   │   │       └── send.post.ts  # POST /api/auth/sms/send - 发送短信验证码
│   │   │
│   │   ├── user/                 # 用户信息 API(需认证)
│   │   │   ├── profile.get.ts    # GET /api/user/profile - 获取当前用户信息
│   │   │   ├── profile.put.ts    # PUT /api/user/profile - 更新用户昵称/头像/邮箱
│   │   │   └── password.put.ts   # PUT /api/user/password - 修改密码
│   │   │
│   │   ├── plans.get.ts          # GET /api/plans - 获取上架的会员套餐列表
│   │   │
│   │   ├── orders/               # 订单 API(需认证)
│   │   │   ├── orders.post.ts   # POST /api/orders - 创建订单并发起支付
│   │   │   └── orders.get.ts    # GET /api/orders - 获取当前用户的订单列表
│   │   │
│   │   ├── payments/             # 支付回调 API(第三方支付平台调用,无需认证)
│   │   │   ├── wechat/          # 微信支付
│   │   │   │   └── notify.post.ts  # POST /api/payments/wechat/notify - 微信支付结果回调
│   │   │   └── alipay/          # 支付宝
│   │   │       └── notify.post.ts  # POST /api/payments/alipay/notify - 支付宝支付结果回调
│   │   │
│   │   ├── upload.post.ts        # POST /api/upload - 文件上传(管理后台富文本编辑器使用)
│   │   │
│   │   ├── _ws.ts                # WebSocket 端点 - 实时消息推送连接(?token=JWT)
│   │   │
│   │   ├── push/                 # 消息推送 API(外部系统调用,推送消息给 Flutter 客户端)
│   │   │   ├── send.post.ts     # POST /api/push/send - 推送消息给指定用户
│   │   │   ├── batch.post.ts    # POST /api/push/batch - 批量推送消息
│   │   │   └── broadcast.post.ts # POST /api/push/broadcast - 全站广播(仅管理员)
│   │   │
│   │   ├── notifications/        # 通知 API(需认证)
│   │   │   ├── notifications.get.ts  # GET /api/notifications - 获取通知列表
│   │   │   └── read.put.ts      # PUT /api/notifications/read - 标记通知已读
│   │   │
│   │   └── admin/                # 管理后台 API(需 admin 角色)
│   │       ├── stats.get.ts      # GET /api/admin/stats - 仪表盘统计数据(总用户/VIP/订单/收入)
│   │       ├── users.get.ts      # GET /api/admin/users - 用户列表(分页+搜索)
│   │       ├── users/            # 用户操作
│   │       │   └── [id].put.ts   # PUT /api/admin/users/:id - 更新用户状态/角色
│   │       ├── orders.get.ts     # GET /api/admin/orders - 订单列表(分页+状态筛选)
│   │       ├── plans.get.ts      # GET /api/admin/plans - 获取所有套餐(含已下架)
│   │       ├── plans.post.ts     # POST /api/admin/plans - 新增套餐
│   │       ├── plans/            # 套餐操作
│   │       │   └── [id].put.ts   # PUT /api/admin/plans/:id - 更新套餐信息/上下架
│   │       ├── content.get.ts    # GET /api/admin/content - 内容列表
│   │       └── content.post.ts   # POST /api/admin/content - 创建内容(文章/公告/FAQ)
│   │
│   ├── middleware/                # 【服务端中间件】每个请求都会经过,按文件名字母顺序执行
│   │   │                         # 用于认证、限流、日志、安全头等全局逻辑
│   │   ├── auth.ts               # 认证中间件:验证 JWT Token,将 userId/userRole 写入 event.context
│   │   ├── rate-limit.ts         # 速率限制:不同路由不同频率(如验证码 1次/分钟,登录 10次/15分钟)
│   │   ├── logger.ts             # 请求日志:记录请求方法、URL、状态码、耗时、IP 等
│   │   ├── performance.ts        # 性能监控:检测慢请求(>1秒)并记录警告
│   │   └── security.ts           # 安全头:设置 X-Content-Type-Options、X-Frame-Options、CSP 等
│   │
│   ├── plugins/                  # 【Nitro 插件】服务端启动时执行,用于注册全局钩子
│   │   └── error-tracking.ts     # 错误追踪插件:捕获服务端未处理错误,记录日志并可上报 Sentry
│   │
│   ├── utils/                    # 【工具函数目录】服务端通用工具,支持自动导入
│   │   ├── jwt.ts                # JWT 工具:signToken / signRefreshToken / verifyToken
│   │   ├── password.ts           # 密码工具:hashPassword / verifyPassword(基于 bcrypt)
│   │   ├── wechat-pay.ts         # 微信支付工具:创建支付订单、获取支付客户端
│   │   ├── alipay.ts             # 支付宝工具:创建支付订单、获取支付客户端
│   │   ├── sms-aliyun.ts         # 阿里云短信工具:替代腾讯云的短信发送方案
│   │   ├── cache.ts              # 缓存工具:getCached / setCache / getCachedOrFetch(基于 Redis)
│   │   ├── logger.ts             # 日志工具:info / error / warn(结构化 JSON 日志)
│   │   ├── alert.ts              # 告警工具:sendAlert(通过 Webhook 发送告警到企业微信等)
│   │   ├── ws-manager.ts         # WebSocket 管理器:连接注册/移除/按用户推送/广播
│   │   ├── notification.ts       # 通知工具:fetchUnreadNotifications 查询未读消息
│   │   └── ws-redis.ts           # WebSocket Redis:跨实例 PubSub 通信(多实例部署时使用)
│   │
│   └── routes/                   # 【路由目录】非 API 路由(如健康检查等非 /api 前缀的路由)
│       └── health.ts             # GET /health - 健康检查(检测数据库和 Redis 连接状态)

├── shared/                       # 【共享目录】前后端共享的代码,客户端和服务端都可使用
│   ├── types/                    # 共享 TypeScript 类型定义
│   │   └── index.ts              # 如 User、Order、Plan 等接口定义
│   └── utils/                    # 共享工具函数
│       └── validators.ts         # 如通用校验规则(手机号、邮箱等 Zod Schema)

├── public/                       # 【静态资源目录】直接通过根路径访问的文件,不经过构建处理
│   └── favicon.ico               # 网站图标

├── uploads/                      # 【上传目录】用户上传的文件存储目录(需在 .gitignore 中忽略)
│                                 # 生产环境中建议使用对象存储(如腾讯云 COS)替代本地存储

├── drizzle/                      # 【数据库迁移目录】Drizzle Kit 生成的 SQL 迁移文件

├── nuxt.config.ts                # 【Nuxt 配置文件】项目核心配置:模块、运行时变量、路由规则等

├── drizzle.config.ts             # 【Drizzle Kit 配置】数据库 Schema 路径、迁移输出目录、数据库连接

├── package.json                  # 【项目依赖】npm 包管理和脚本定义

├── .env                          # 【环境变量】服务端私有配置(JWT密钥、数据库URL、Redis等),不提交到 Git

├── .env.docker                   # Docker 环境变量模板(Docker Compose 使用)

├── Dockerfile                    # 【Docker 构建文件】多阶段构建:先编译,再运行,减小镜像体积

├── docker-compose.yml            # 【Docker Compose 编排】一键启动 App + PostgreSQL + Redis + Nginx

├── nginx.conf                    # 【Nginx 配置】反向代理 + SSL + 静态资源缓存 + 安全头

└── .github/workflows/deploy.yml  # 【CI/CD 配置】GitHub Actions 自动部署到服务器

完整文件清单

以下是项目需要的所有文件,按目录分类:

目录文件说明
根目录nuxt.config.tsNuxt 核心配置(模块、运行时变量、路由规则)
package.json项目依赖和脚本
.env环境变量(不提交 Git)
drizzle.config.tsDrizzle ORM 迁移配置
DockerfileDocker 多阶段构建
docker-compose.ymlDocker 编排(App+DB+Redis+Nginx)
nginx.confNginx 反向代理配置
.env.dockerDocker 环境变量
.github/workflows/deploy.ymlCI/CD 自动部署
app/pages/admin/index.vue管理后台仪表盘
login.vue管理后台登录页
users/index.vue用户管理列表
users/[id].vue用户详情
orders/index.vue订单管理
plans/index.vue套餐管理
content/index.vue内容管理
app/layouts/default.vue默认布局
admin.vue管理后台布局(侧边栏+主内容)
app/components/admin/ContentEditor.vue富文本编辑器(Tiptap)
app/composables/useAuth.ts认证相关组合式函数
app/middleware/auth.ts前端认证守卫
admin-auth.ts管理员权限守卫
app/plugins/error-handler.ts全局错误处理
server/database/db.ts数据库连接实例
schema.ts数据表 Schema(5张表:users、orders、plans、sms_codes、notifications)
server/api/auth/register.post.ts注册 API
login.post.ts登录 API
refresh.post.ts刷新 Token API
server/api/auth/sms/send.post.ts发送验证码 API
server/api/user/profile.get.ts获取个人信息
profile.put.ts更新个人信息
password.put.ts修改密码
server/api/plans.get.ts套餐列表 API
upload.post.ts文件上传 API
server/api/_ws.tsWebSocket 端点(实时消息推送)
server/api/push/send.post.ts推送消息给指定用户
batch.post.ts批量推送消息
broadcast.post.ts全站广播(仅管理员)
server/api/notifications/notifications.get.ts通知列表 API
read.put.ts标记通知已读 API
server/api/orders/orders.post.ts创建订单
orders.get.ts订单列表
server/api/payments/wechat/notify.post.ts微信支付回调
server/api/payments/alipay/notify.post.ts支付宝回调
server/api/admin/stats.get.ts统计数据
users.get.ts用户列表
users/[id].put.ts更新用户
orders.get.ts订单列表
plans.get.ts套餐列表(含下架)
plans.post.ts新增套餐
plans/[id].put.ts更新套餐
content.get.ts内容列表
content.post.ts创建内容
server/middleware/auth.tsJWT 认证中间件
rate-limit.ts速率限制中间件
logger.ts请求日志中间件
performance.ts慢请求检测中间件
security.ts安全响应头中间件
server/plugins/error-tracking.ts错误追踪插件
server/utils/jwt.tsJWT 签名/验证工具
password.ts密码哈希/验证工具
wechat-pay.ts微信支付工具
alipay.ts支付宝工具
sms-aliyun.ts阿里云短信工具
cache.tsRedis 缓存工具
logger.ts结构化日志工具
alert.ts告警通知工具
ws-manager.tsWebSocket 连接管理器
notification.ts通知查询工具
ws-redis.tsWebSocket Redis 跨实例 PubSub(可选)
server/routes/health.ts健康检查路由
shared/types/index.ts共享类型定义
shared/utils/validators.ts共享校验规则

基于 Nuxt 4 官方文档整理编写