Skip to content

Latest commit

 

History

History
1122 lines (922 loc) · 29.5 KB

backend-structure.md

File metadata and controls

1122 lines (922 loc) · 29.5 KB

🚀 后端架构设计

项目概述

基于 Node.js + Express.js 构建的「闪电准备包」后端服务,采用 MVC 架构模式,支持微信小程序认证、支付、内容管理等核心功能。


目录结构

server/
├── package.json                # 项目依赖配置
├── package-lock.json          # 依赖锁定文件
├── .env                       # 环境变量配置
├── .env.example              # 环境变量示例
├── .gitignore                # Git忽略文件
├── README.md                 # 项目说明文档
├── app.js                    # 应用入口文件
├── server.js                 # 服务器启动文件
│
├── src/                      # 源代码目录
│   ├── controllers/          # 控制器层
│   │   ├── auth.js           # 认证控制器
│   │   ├── user.js           # 用户控制器
│   │   ├── company.js        # 公司控制器
│   │   ├── job.js            # 职位控制器
│   │   ├── package.js        # 准备包控制器
│   │   ├── content.js        # 内容控制器
│   │   ├── order.js          # 订单控制器
│   │   ├── progress.js       # 进度控制器
│   │   └── admin.js          # 管理后台控制器
│   │
│   ├── models/               # 数据模型层
│   │   ├── User.js           # 用户模型
│   │   ├── Company.js        # 公司模型
│   │   ├── Job.js            # 职位模型
│   │   ├── Package.js        # 准备包模型
│   │   ├── Content.js        # 内容模型
│   │   ├── Order.js          # 订单模型
│   │   ├── Progress.js       # 进度模型
│   │   ├── Coupon.js         # 优惠券模型
│   │   └── Review.js         # 评价模型
│   │
│   ├── services/             # 业务服务层
│   │   ├── auth.js           # 认证服务
│   │   ├── wechat.js         # 微信服务
│   │   ├── payment.js        # 支付服务
│   │   ├── content.js        # 内容服务
│   │   ├── notification.js   # 通知服务
│   │   ├── email.js          # 邮件服务
│   │   ├── sms.js            # 短信服务
│   │   └── analytics.js      # 数据分析服务
│   │
│   ├── middleware/           # 中间件
│   │   ├── auth.js           # 认证中间件
│   │   ├── validation.js     # 参数验证中间件
│   │   ├── rateLimit.js      # 限流中间件
│   │   ├── error.js          # 错误处理中间件
│   │   ├── cors.js           # 跨域中间件
│   │   ├── logger.js         # 日志中间件
│   │   └── upload.js         # 文件上传中间件
│   │
│   ├── routes/               # 路由定义
│   │   ├── index.js          # 路由入口
│   │   ├── auth.js           # 认证路由
│   │   ├── user.js           # 用户路由
│   │   ├── company.js        # 公司路由
│   │   ├── package.js        # 准备包路由
│   │   ├── content.js        # 内容路由
│   │   ├── order.js          # 订单路由
│   │   ├── progress.js       # 进度路由
│   │   └── admin.js          # 管理后台路由
│   │
│   ├── utils/                # 工具类
│   │   ├── crypto.js         # 加密工具
│   │   ├── validator.js      # 验证工具
│   │   ├── logger.js         # 日志工具
│   │   ├── redis.js          # Redis工具
│   │   ├── response.js       # 响应格式化工具
│   │   ├── constants.js      # 常量定义
│   │   ├── helpers.js        # 辅助函数
│   │   └── queue.js          # 队列工具
│   │
│   └── validators/           # 数据验证器
│       ├── auth.js           # 认证验证器
│       ├── user.js           # 用户验证器
│       ├── package.js        # 准备包验证器
│       ├── order.js          # 订单验证器
│       └── content.js        # 内容验证器
│
├── config/                   # 配置文件目录
│   ├── index.js              # 配置入口
│   ├── database.js           # 数据库配置
│   ├── wechat.js             # 微信配置
│   ├── payment.js            # 支付配置
│   ├── redis.js              # Redis配置
│   ├── email.js              # 邮件配置
│   ├── upload.js             # 文件上传配置
│   └── app.js                # 应用配置
│
├── scripts/                  # 脚本文件
│   ├── seed.js               # 数据初始化脚本
│   ├── migrate.js            # 数据迁移脚本
│   ├── backup.js             # 数据备份脚本
│   ├── cleanup.js            # 数据清理脚本
│   └── deploy.js             # 部署脚本
│
├── tests/                    # 测试文件
│   ├── unit/                 # 单元测试
│   │   ├── controllers/      # 控制器测试
│   │   ├── services/         # 服务测试
│   │   ├── models/           # 模型测试
│   │   └── utils/            # 工具测试
│   ├── integration/          # 集成测试
│   │   ├── auth.test.js      # 认证集成测试
│   │   ├── order.test.js     # 订单集成测试
│   │   └── payment.test.js   # 支付集成测试
│   ├── fixtures/             # 测试数据
│   │   ├── users.json        # 用户测试数据
│   │   ├── companies.json    # 公司测试数据
│   │   └── packages.json     # 准备包测试数据
│   └── helpers/              # 测试辅助函数
│       ├── setup.js          # 测试环境设置
│       └── teardown.js       # 测试环境清理
│
├── docs/                     # 文档目录
│   ├── api.md                # API文档
│   ├── deployment.md         # 部署文档
│   └── development.md        # 开发文档
│
├── logs/                     # 日志文件目录
│   ├── access.log            # 访问日志
│   ├── error.log             # 错误日志
│   └── app.log               # 应用日志
│
└── uploads/                  # 文件上传目录
    ├── images/               # 图片文件
    ├── documents/            # 文档文件
    └── temp/                 # 临时文件

技术栈

核心框架

  • Node.js: v18.x LTS
  • Express.js: v4.18.x
  • MongoDB: v6.x
  • Mongoose: v7.x
  • Redis: v7.x

认证与安全

  • jsonwebtoken: JWT认证
  • bcryptjs: 密码加密
  • helmet: 安全头设置
  • cors: 跨域处理
  • express-rate-limit: 请求限流

微信集成

  • wechat-api: 微信API封装
  • wechat-pay: 微信支付
  • crypto: 加密解密

工具库

  • joi: 数据验证
  • winston: 日志管理
  • nodemailer: 邮件发送
  • multer: 文件上传
  • moment: 日期处理

核心模块设计

1. 认证模块 (Auth)

控制器 (controllers/auth.js)

const authService = require('../services/auth')
const wechatService = require('../services/wechat')
const { successResponse, errorResponse } = require('../utils/response')

class AuthController {
  // 微信登录
  async wechatLogin(req, res) {
    try {
      const { code, userInfo } = req.body
      
      // 获取微信用户信息
      const wechatUser = await wechatService.getUser(code)
      
      // 创建或更新用户
      const user = await authService.createOrUpdateUser(wechatUser, userInfo)
      
      // 生成JWT token
      const token = authService.generateToken(user._id)
      
      return successResponse(res, {
        token,
        userInfo: user
      }, '登录成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
  
  // 绑定手机号
  async bindPhone(req, res) {
    try {
      const { encryptedData, iv } = req.body
      const userId = req.user.id
      
      // 解密手机号
      const phoneData = await wechatService.decryptData(encryptedData, iv)
      
      // 更新用户手机号
      await authService.bindPhone(userId, phoneData.phoneNumber)
      
      return successResponse(res, { phone: phoneData.phoneNumber }, '绑定成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
  
  // 刷新token
  async refreshToken(req, res) {
    try {
      const { refreshToken } = req.body
      const newToken = await authService.refreshToken(refreshToken)
      
      return successResponse(res, { token: newToken }, 'Token刷新成功')
    } catch (error) {
      return errorResponse(res, error.message, 401)
    }
  }
}

module.exports = new AuthController()

服务层 (services/auth.js)

const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const User = require('../models/User')
const config = require('../config')

class AuthService {
  // 生成JWT token
  generateToken(userId) {
    return jwt.sign(
      { userId },
      config.jwt.secret,
      { expiresIn: config.jwt.expiresIn }
    )
  }
  
  // 验证token
  verifyToken(token) {
    return jwt.verify(token, config.jwt.secret)
  }
  
  // 创建或更新用户
  async createOrUpdateUser(wechatUser, userInfo) {
    const { openid, unionid } = wechatUser
    
    let user = await User.findOne({ openid })
    
    if (user) {
      // 更新用户信息
      user.nickName = userInfo.nickName
      user.avatarUrl = userInfo.avatarUrl
      user.lastLoginAt = new Date()
      await user.save()
    } else {
      // 创建新用户
      user = new User({
        openid,
        unionid,
        nickName: userInfo.nickName,
        avatarUrl: userInfo.avatarUrl,
        isVip: false,
        points: 0,
        level: 1,
        createdAt: new Date(),
        lastLoginAt: new Date()
      })
      await user.save()
    }
    
    return user
  }
  
  // 绑定手机号
  async bindPhone(userId, phone) {
    const user = await User.findById(userId)
    if (!user) {
      throw new Error('用户不存在')
    }
    
    user.phone = phone
    await user.save()
    
    return user
  }
}

module.exports = new AuthService()

2. 订单模块 (Order)

控制器 (controllers/order.js)

const orderService = require('../services/order')
const paymentService = require('../services/payment')
const { successResponse, errorResponse } = require('../utils/response')

class OrderController {
  // 创建订单
  async createOrder(req, res) {
    try {
      const { packageId, paymentMethod } = req.body
      const userId = req.user.id
      
      // 创建订单
      const order = await orderService.createOrder(userId, packageId)
      
      // 生成支付信息
      const paymentInfo = await paymentService.createPayment(order, paymentMethod)
      
      return successResponse(res, {
        orderId: order._id,
        paymentInfo
      }, '订单创建成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
  
  // 支付回调
  async paymentCallback(req, res) {
    try {
      const paymentData = req.body
      
      // 验证支付结果
      const isValid = await paymentService.verifyPayment(paymentData)
      if (!isValid) {
        throw new Error('支付验证失败')
      }
      
      // 更新订单状态
      await orderService.updateOrderStatus(paymentData.orderId, 'paid')
      
      return successResponse(res, null, '支付成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
  
  // 获取订单列表
  async getOrders(req, res) {
    try {
      const userId = req.user.id
      const { page = 1, limit = 10, status } = req.query
      
      const orders = await orderService.getUserOrders(userId, {
        page: parseInt(page),
        limit: parseInt(limit),
        status
      })
      
      return successResponse(res, orders, '获取成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
}

module.exports = new OrderController()

3. 内容模块 (Content)

控制器 (controllers/content.js)

const contentService = require('../services/content')
const { successResponse, errorResponse } = require('../utils/response')

class ContentController {
  // 获取模块内容
  async getModuleContent(req, res) {
    try {
      const { moduleId } = req.params
      const { orderId } = req.query
      const userId = req.user.id
      
      // 验证用户权限
      const hasAccess = await contentService.checkAccess(userId, orderId, moduleId)
      if (!hasAccess) {
        return errorResponse(res, '无访问权限', 403)
      }
      
      // 获取内容
      const content = await contentService.getContent(moduleId, orderId)
      
      return successResponse(res, content, '获取成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
  
  // 更新学习进度
  async updateProgress(req, res) {
    try {
      const { orderId, moduleId, sectionId, action, timeSpent } = req.body
      const userId = req.user.id
      
      await contentService.updateProgress(userId, orderId, moduleId, sectionId, action, timeSpent)
      
      return successResponse(res, null, '进度更新成功')
    } catch (error) {
      return errorResponse(res, error.message, 400)
    }
  }
}

module.exports = new ContentController()

数据库连接配置

MongoDB连接 (config/database.js)

const mongoose = require('mongoose')
const config = require('./index')

class Database {
  constructor() {
    this.connect()
  }
  
  connect() {
    mongoose.connect(config.database.mongodb.uri, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      maxPoolSize: 10,
      serverSelectionTimeoutMS: 5000,
      socketTimeoutMS: 45000,
    })
    
    mongoose.connection.on('connected', () => {
      console.log('MongoDB connected successfully')
    })
    
    mongoose.connection.on('error', (err) => {
      console.error('MongoDB connection error:', err)
    })
    
    mongoose.connection.on('disconnected', () => {
      console.log('MongoDB disconnected')
    })
    
    // 优雅关闭
    process.on('SIGINT', async () => {
      await mongoose.connection.close()
      console.log('MongoDB connection closed through app termination')
      process.exit(0)
    })
  }
}

module.exports = new Database()

Redis连接 (utils/redis.js)

const redis = require('redis')
const config = require('../config')

class RedisClient {
  constructor() {
    this.client = redis.createClient({
      host: config.redis.host,
      port: config.redis.port,
      password: config.redis.password,
      db: config.redis.db
    })
    
    this.client.on('connect', () => {
      console.log('Redis connected successfully')
    })
    
    this.client.on('error', (err) => {
      console.error('Redis connection error:', err)
    })
  }
  
  // 设置缓存
  async set(key, value, expire = 3600) {
    const data = JSON.stringify(value)
    if (expire) {
      return await this.client.setex(key, expire, data)
    }
    return await this.client.set(key, data)
  }
  
  // 获取缓存
  async get(key) {
    const data = await this.client.get(key)
    return data ? JSON.parse(data) : null
  }
  
  // 删除缓存
  async del(key) {
    return await this.client.del(key)
  }
  
  // 检查键是否存在
  async exists(key) {
    return await this.client.exists(key)
  }
}

module.exports = new RedisClient()

中间件设计

认证中间件 (middleware/auth.js)

const jwt = require('jsonwebtoken')
const User = require('../models/User')
const config = require('../config')
const { errorResponse } = require('../utils/response')

const authMiddleware = async (req, res, next) => {
  try {
    const token = req.header('Authorization')?.replace('Bearer ', '')
    
    if (!token) {
      return errorResponse(res, '未提供认证token', 401)
    }
    
    const decoded = jwt.verify(token, config.jwt.secret)
    const user = await User.findById(decoded.userId)
    
    if (!user) {
      return errorResponse(res, '用户不存在', 401)
    }
    
    req.user = user
    next()
  } catch (error) {
    return errorResponse(res, '认证失败', 401)
  }
}

// 可选认证中间件
const optionalAuth = async (req, res, next) => {
  try {
    const token = req.header('Authorization')?.replace('Bearer ', '')
    
    if (token) {
      const decoded = jwt.verify(token, config.jwt.secret)
      const user = await User.findById(decoded.userId)
      req.user = user
    }
    
    next()
  } catch (error) {
    next()
  }
}

module.exports = {
  authMiddleware,
  optionalAuth
}

限流中间件 (middleware/rateLimit.js)

const rateLimit = require('express-rate-limit')
const RedisStore = require('rate-limit-redis')
const redis = require('../utils/redis')

// 通用限流
const generalLimiter = rateLimit({
  store: new RedisStore({
    client: redis.client
  }),
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 最多100次请求
  message: {
    code: 429,
    message: '请求过于频繁,请稍后再试'
  }
})

// 登录限流
const loginLimiter = rateLimit({
  store: new RedisStore({
    client: redis.client
  }),
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 5, // 最多5次登录尝试
  skipSuccessfulRequests: true,
  message: {
    code: 429,
    message: '登录尝试过于频繁,请15分钟后再试'
  }
})

// 支付限流
const paymentLimiter = rateLimit({
  store: new RedisStore({
    client: redis.client
  }),
  windowMs: 60 * 1000, // 1分钟
  max: 3, // 最多3次支付请求
  message: {
    code: 429,
    message: '支付请求过于频繁,请稍后再试'
  }
})

module.exports = {
  generalLimiter,
  loginLimiter,
  paymentLimiter
}

错误处理中间件 (middleware/error.js)

const logger = require('../utils/logger')

const errorHandler = (err, req, res, next) => {
  let error = { ...err }
  error.message = err.message
  
  // 记录错误日志
  logger.error(err)
  
  // Mongoose错误处理
  if (err.name === 'CastError') {
    const message = '资源未找到'
    error = { message, statusCode: 404 }
  }
  
  // Mongoose重复字段错误
  if (err.code === 11000) {
    const message = '数据已存在'
    error = { message, statusCode: 400 }
  }
  
  // Mongoose验证错误
  if (err.name === 'ValidationError') {
    const message = Object.values(err.errors).map(val => val.message).join(', ')
    error = { message, statusCode: 400 }
  }
  
  // JWT错误
  if (err.name === 'JsonWebTokenError') {
    const message = '无效的token'
    error = { message, statusCode: 401 }
  }
  
  // JWT过期错误
  if (err.name === 'TokenExpiredError') {
    const message = 'Token已过期'
    error = { message, statusCode: 401 }
  }
  
  res.status(error.statusCode || 500).json({
    code: error.statusCode || 500,
    message: error.message || '服务器内部错误',
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
  })
}

module.exports = errorHandler

服务层设计

微信服务 (services/wechat.js)

const crypto = require('crypto')
const axios = require('axios')
const config = require('../config')

class WechatService {
  constructor() {
    this.appId = config.wechat.appId
    this.appSecret = config.wechat.appSecret
  }
  
  // 获取access_token
  async getAccessToken() {
    const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.appId}&secret=${this.appSecret}`
    
    const response = await axios.get(url)
    
    if (response.data.errcode) {
      throw new Error(`获取access_token失败: ${response.data.errmsg}`)
    }
    
    return response.data.access_token
  }
  
  // 通过code获取用户信息
  async getUser(code) {
    const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${this.appId}&secret=${this.appSecret}&js_code=${code}&grant_type=authorization_code`
    
    const response = await axios.get(url)
    
    if (response.data.errcode) {
      throw new Error(`获取用户信息失败: ${response.data.errmsg}`)
    }
    
    return {
      openid: response.data.openid,
      unionid: response.data.unionid,
      sessionKey: response.data.session_key
    }
  }
  
  // 解密数据
  decryptData(encryptedData, iv, sessionKey) {
    const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(sessionKey, 'base64'), Buffer.from(iv, 'base64'))
    decipher.setAutoPadding(true)
    
    let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
    decrypted += decipher.final('utf8')
    
    return JSON.parse(decrypted)
  }
  
  // 发送模板消息
  async sendTemplateMessage(openid, templateId, data, page) {
    const accessToken = await this.getAccessToken()
    const url = `https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=${accessToken}`
    
    const payload = {
      touser: openid,
      template_id: templateId,
      page,
      data
    }
    
    const response = await axios.post(url, payload)
    
    if (response.data.errcode !== 0) {
      throw new Error(`发送模板消息失败: ${response.data.errmsg}`)
    }
    
    return response.data
  }
}

module.exports = new WechatService()

支付服务 (services/payment.js)

const crypto = require('crypto')
const axios = require('axios')
const config = require('../config')

class PaymentService {
  constructor() {
    this.appId = config.wechat.appId
    this.mchId = config.payment.mchId
    this.key = config.payment.key
    this.notifyUrl = config.payment.notifyUrl
  }
  
  // 创建支付订单
  async createPayment(order, paymentMethod = 'wechat') {
    if (paymentMethod === 'wechat') {
      return await this.createWechatPayment(order)
    }
    
    throw new Error('不支持的支付方式')
  }
  
  // 创建微信支付订单
  async createWechatPayment(order) {
    const params = {
      appid: this.appId,
      mch_id: this.mchId,
      nonce_str: this.generateNonceStr(),
      body: `闪电准备包-${order.packageId}`,
      out_trade_no: order._id.toString(),
      total_fee: Math.round(order.price * 100), // 转换为分
      spbill_create_ip: '127.0.0.1',
      notify_url: this.notifyUrl,
      trade_type: 'JSAPI',
      openid: order.userId.openid
    }
    
    // 生成签名
    params.sign = this.generateSign(params)
    
    // 转换为XML
    const xml = this.objectToXml(params)
    
    // 发送请求
    const response = await axios.post('https://api.mch.weixin.qq.com/pay/unifiedorder', xml, {
      headers: { 'Content-Type': 'application/xml' }
    })
    
    const result = this.xmlToObject(response.data)
    
    if (result.return_code !== 'SUCCESS' || result.result_code !== 'SUCCESS') {
      throw new Error(`微信支付创建失败: ${result.err_code_des || result.return_msg}`)
    }
    
    // 生成小程序支付参数
    const paymentInfo = {
      appId: this.appId,
      timeStamp: Math.floor(Date.now() / 1000).toString(),
      nonceStr: this.generateNonceStr(),
      package: `prepay_id=${result.prepay_id}`,
      signType: 'MD5'
    }
    
    paymentInfo.paySign = this.generateSign(paymentInfo)
    
    return paymentInfo
  }
  
  // 验证支付结果
  async verifyPayment(paymentData) {
    const sign = paymentData.sign
    delete paymentData.sign
    
    const generatedSign = this.generateSign(paymentData)
    
    return sign === generatedSign && paymentData.result_code === 'SUCCESS'
  }
  
  // 生成随机字符串
  generateNonceStr() {
    return crypto.randomBytes(16).toString('hex')
  }
  
  // 生成签名
  generateSign(params) {
    const sortedKeys = Object.keys(params).sort()
    const stringA = sortedKeys.map(key => `${key}=${params[key]}`).join('&')
    const stringSignTemp = `${stringA}&key=${this.key}`
    
    return crypto.createHash('md5').update(stringSignTemp).digest('hex').toUpperCase()
  }
  
  // 对象转XML
  objectToXml(obj) {
    let xml = '<xml>'
    for (const key in obj) {
      xml += `<${key}>${obj[key]}</${key}>`
    }
    xml += '</xml>'
    return xml
  }
  
  // XML转对象
  xmlToObject(xml) {
    const result = {}
    const regex = /<(\w+)>([^<]*)<\/\1>/g
    let match
    
    while ((match = regex.exec(xml)) !== null) {
      result[match[1]] = match[2]
    }
    
    return result
  }
}

module.exports = new PaymentService()

环境配置

环境变量 (.env)

# 应用配置
NODE_ENV=development
PORT=3005
APP_NAME=闪电准备包

# 数据库配置
MONGODB_URI=mongodb://localhost:27017/lightning-prep
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

# JWT配置
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d

# 微信配置
WECHAT_APP_ID=your_wechat_app_id
WECHAT_APP_SECRET=your_wechat_app_secret

# 微信支付配置
WECHAT_MCH_ID=your_mch_id
WECHAT_PAY_KEY=your_pay_key
WECHAT_NOTIFY_URL=https://your-domain.com/api/v1/orders/payment-callback

# 邮件配置
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_email_password

# 文件上传配置
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=5242880

# 日志配置
LOG_LEVEL=info
LOG_FILE=./logs/app.log

配置入口 (config/index.js)

require('dotenv').config()

module.exports = {
  app: {
    name: process.env.APP_NAME || '闪电准备包',
    port: process.env.PORT || 3000,
    env: process.env.NODE_ENV || 'development'
  },
  
  database: {
    mongodb: {
      uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/lightning-prep'
    }
  },
  
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: process.env.REDIS_PORT || 6379,
    password: process.env.REDIS_PASSWORD || '',
    db: process.env.REDIS_DB || 0
  },
  
  jwt: {
    secret: process.env.JWT_SECRET || 'your_jwt_secret',
    expiresIn: process.env.JWT_EXPIRES_IN || '7d'
  },
  
  wechat: {
    appId: process.env.WECHAT_APP_ID,
    appSecret: process.env.WECHAT_APP_SECRET
  },
  
  payment: {
    mchId: process.env.WECHAT_MCH_ID,
    key: process.env.WECHAT_PAY_KEY,
    notifyUrl: process.env.WECHAT_NOTIFY_URL
  },
  
  email: {
    host: process.env.EMAIL_HOST,
    port: process.env.EMAIL_PORT,
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASS
  },
  
  upload: {
    path: process.env.UPLOAD_PATH || './uploads',
    maxSize: process.env.MAX_FILE_SIZE || 5242880
  },
  
  log: {
    level: process.env.LOG_LEVEL || 'info',
    file: process.env.LOG_FILE || './logs/app.log'
  }
}

启动脚本

应用入口 (app.js)

const express = require('express')
const cors = require('cors')
const helmet = require('helmet')
const morgan = require('morgan')
const path = require('path')

// 导入配置和工具
const config = require('./config')
const logger = require('./src/utils/logger')
const errorHandler = require('./src/middleware/error')
const { generalLimiter } = require('./src/middleware/rateLimit')

// 导入路由
const routes = require('./src/routes')

// 导入数据库连接
require('./config/database')

const app = express()

// 基础中间件
app.use(helmet())
app.use(cors())
app.use(express.json({ limit: '10mb' }))
app.use(express.urlencoded({ extended: true }))
app.use(morgan('combined', { stream: { write: message => logger.info(message.trim()) } }))

// 限流中间件
app.use(generalLimiter)

// 静态文件服务
app.use('/uploads', express.static(path.join(__dirname, 'uploads')))

// API路由
app.use('/api/v1', routes)

// 健康检查
app.get('/health', (req, res) => {
  res.json({
    status: 'OK',
    timestamp: new Date().toISOString(),
    uptime: process.uptime()
  })
})

// 404处理
app.use('*', (req, res) => {
  res.status(404).json({
    code: 404,
    message: '接口不存在'
  })
})

// 错误处理中间件
app.use(errorHandler)

module.exports = app

服务器启动 (server.js)

const app = require('./app')
const config = require('./config')
const logger = require('./src/utils/logger')

const PORT = config.app.port

const server = app.listen(PORT, () => {
  logger.info(`🚀 服务器启动成功,端口: ${PORT}`)
  logger.info(`🌍 环境: ${config.app.env}`)
  logger.info(`📱 应用: ${config.app.name}`)
})

// 优雅关闭
process.on('SIGTERM', () => {
  logger.info('收到SIGTERM信号,开始优雅关闭...')
  server.close(() => {
    logger.info('服务器已关闭')
    process.exit(0)
  })
})

process.on('SIGINT', () => {
  logger.info('收到SIGINT信号,开始优雅关闭...')
  server.close(() => {
    logger.info('服务器已关闭')
    process.exit(0)
  })
})

// 未捕获异常处理
process.on('uncaughtException', (err) => {
  logger.error('未捕获异常:', err)
  process.exit(1)
})

process.on('unhandledRejection', (err) => {
  logger.error('未处理的Promise拒绝:', err)
  server.close(() => {
    process.exit(1)
  })
})

总结

本后端架构设计具有以下特点:

  1. 分层架构:控制器、服务、模型分离,职责清晰
  2. 模块化设计:功能模块独立,便于维护和扩展
  3. 安全性:JWT认证、限流、数据验证、错误处理
  4. 可扩展性:支持微信支付、消息推送、文件上传等功能
  5. 高性能:Redis缓存、数据库优化、连接池管理
  6. 可监控:完善的日志系统、健康检查、错误追踪

通过这个架构,可以确保系统的稳定性、安全性和可维护性。