9shadow article banner
企业AI落地 · 企业级AI智能体 | AI智能设备软件开发 | 数字孪生 · 智慧园区 · 数字大屏 | App · 微信 · 小程序 | XR云展厅 · 元宇宙空间 | 虚拟仿真实训 | 品牌互动营销

知识付费小程序后端该怎么设计?

在知识付费行业产品形态日益趋同的当下,小程序凭借其轻量化、场景化优势成为创业者的首选载体。本文从后端架构设计视角出发,系统解析如何通过Node.js技术栈构建完整的知识付费小程序解决方案,涵盖核心模块设计、关键代码实现及工程化实践建议。

一、系统架构全景图


 

mermaid

  graph TD
  A[客户端小程序] --> B[API网关层<br>(Node.js/Express)]
  B --> C[业务服务层<br>(内容/用户/支付服务)]
  C --> D[数据存储层<br>(MongoDB+对象存储)]

1. 技术选型组合

  • 开发框架:Express.js(轻量级RESTful API开发)
  • 数据库:MongoDB(文档型数据库适配内容管理场景)
  • 对象存储:阿里云OSS/腾讯云COS(音视频文件存储)
  • 支付集成:微信支付商户平台SDK

2. 核心功能模块矩阵

 

模块 关键能力 典型场景
用户系统 多角色权限管理、JWT鉴权 游客浏览/付费用户解锁
内容系统 多媒体内容发布、分类标签体系 课程/专栏/直播回放管理
支付系统 预支付订单生成、异步通知处理 微信支付/企业分账
运营系统 优惠券核销、分销佣金计算 营销活动支撑

二、内容分发子系统设计

1. 核心数据模型(MongoDB)


 

javascript

  // models/content.schema.js
  const mongoose = require('mongoose');
  const { Schema } = mongoose;
   
  const ContentSchema = new Schema({
  title: { type: String, required: true },
  type: {
  type: String,
  enum: ['video', 'audio', 'article', 'live'],
  default: 'article'
  },
  body: {
  type: String,
  validate: {
  validator: v => ['markdown', 'html'].includes(v),
  message: 'Invalid content format'
  }
  },
  mediaUrl: { type: String, get: v => process.env.CDN_PREFIX + v },
  isFree: { type: Boolean, default: false },
  price: { type: Number, min: 0, default: 0 },
  authorId: { type: Schema.Types.ObjectId, ref: 'User' },
  tags: [{ type: String }],
  viewCount: { type: Number, default: 0 },
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now }
  }, {
  toJSON: { getters: true },
  timestamps: true
  });
   
  module.exports = mongoose.model('Content', ContentSchema);

2. 关键API实现


 

javascript

  // routes/content.js
  const express = require('express');
  const router = express.Router();
  const Content = require('../models/content');
  const { checkAccess } = require('../middlewares/permission');
   
  // 1. 公开内容查询(无需鉴权)
  router.get('/free', async (req, res) => {
  const contents = await Content.find({ isFree: true })
  .populate('authorId', 'nickname avatar')
  .sort({ createdAt: -1 })
  .limit(20);
  res.json({ success: true, data: contents });
  });
   
  // 2. 付费内容访问(鉴权中间件)
  router.get('/:id', checkAccess, async (req, res) => {
  const content = await Content.findById(req.params.id)
  .populate({
  path: 'authorId',
  select: 'nickname bio avatar'
  });
   
  if (!content) return res.status(404).json({ error: '内容不存在' });
   
  // 增加浏览计数(乐观锁)
  await Content.updateOne(
  { _id: content._id },
  { $inc: { viewCount: 1 } }
  );
   
  res.json({ success: true, data: content });
  });

三、权限控制中间件设计

1. 鉴权流程图


 

mermaid

  sequenceDiagram
  participant Client as 客户端
  participant API as API网关
  participant Auth as 鉴权服务
  participant DB as 数据库
   
  Client->>API: 访问付费内容
  API->>Auth: 调用权限校验中间件
  Auth->>DB: 查询内容是否免费
  alt 内容免费
  Auth-->>API: 放行请求
  else 内容收费
  Auth->>DB: 查询用户购买记录
  alt 已购买
  Auth-->>API: 放行请求
  else 未购买
  Auth-->>API: 返回403错误
  end
  end

2. 核心鉴权中间件


 

javascript

  // middlewares/permission.js
  const Order = require('../models/order');
  const Content = require('../models/content');
   
  module.exports = {
  checkAccess: async (req, res, next) => {
  try {
  const { id } = req.params;
  const userId = req.user._id; // JWT解析出的用户ID
   
  // 1. 查询内容信息
  const content = await Content.findById(id).select('isFree price');
  if (!content) return res.status(404).json({ error: '内容不存在' });
   
  // 2. 免费内容直接放行
  if (content.isFree) return next();
   
  // 3. 付费内容校验订单
  const order = await Order.findOne({
  userId,
  contentId: id,
  status: 'paid',
  paidAt: { $exists: true }
  });
   
  if (!order) {
  return res.status(403).json({
  error: '无访问权限',
  code: 'PAYMENT_REQUIRED',
  data: {
  contentId: id,
  price: content.price
  }
  });
  }
   
  next();
  } catch (err) {
  console.error('权限校验失败:', err);
  res.status(500).json({ error: '服务器错误' });
  }
  }
  };

四、支付系统深度实现

1. 微信支付流程时序图


 

mermaid

  sequenceDiagram
  participant Client as 客户端
  participant Server as 后端服务
  participant Wechat as 微信支付
   
  Client->>Server: 创建预支付订单
  Server->>Wechat: 调用统一下单API
  Wechat-->>Server: 返回prepay_id
  Server-->>Client: 返回支付参数
  Client->>Wechat: 调起支付
  Wechat-->>Client: 支付结果页
  Wechat->>Server: 异步通知支付结果
  Server->>DB: 更新订单状态
  Server-->>Wechat: 返回处理结果
  Server->>Client: 支付结果轮询接口

2. 核心支付接口实现


 

javascript

  // routes/payment.js
  const express = require('express');
  const router = express.Router();
  const { createUnifiedOrder, parseWxCallback } = require('../utils/wechat');
  const Order = require('../models/order');
  const Content = require('../models/content');
   
  // 1. 创建预支付订单
  router.post('/prepay', async (req, res) => {
  const { contentId } = req.body;
  const userId = req.user._id;
   
  // 参数校验
  if (!mongoose.Types.ObjectId.isValid(contentId)) {
  return res.status(400).json({ error: '无效的内容ID' });
  }
   
  // 查询内容信息
  const content = await Content.findById(contentId).select('price title');
  if (!content) return res.status(404).json({ error: '内容不存在' });
   
  // 创建订单
  const order = new Order({
  userId,
  contentId,
  price: content.price,
  status: 'pending',
  outTradeNo: `ORD${Date.now()}${Math.floor(Math.random() * 1000)}`
  });
   
  try {
  // 调用微信支付API
  const wxRes = await createUnifiedOrder({
  out_trade_no: order.outTradeNo,
  body: content.title,
  total_fee: Math.round(content.price * 100), // 转换为分
  openid: req.user.openid,
  trade_type: 'JSAPI',
  notify_url: `${process.env.APP_DOMAIN}/api/payment/notify`
  });
   
  // 保存订单
  await order.save();
   
  // 返回支付参数
  res.json({
  success: true,
  data: {
  appId: process.env.WECHAT_APPID,
  timeStamp: String(Date.now()),
  nonceStr: wxRes.nonce_str,
  package: `prepay_id=${wxRes.prepay_id}`,
  signType: 'MD5',
  paySign: generatePaySign(wxRes.prepay_id, wxRes.nonce_str)
  }
  });
  } catch (err) {
  console.error('支付下单失败:', err);
  await order.remove(); // 回滚订单
  res.status(500).json({ error: '支付系统异常' });
  }
  });
   
  // 2. 支付结果异步通知
  router.post('/notify', express.raw({ type: 'application/xml' }), async (req, res) => {
  try {
  // 解析XML数据
  const callbackData = await parseWxCallback(req.body.toString());
  const { out_trade_no, result_code, transaction_id } = callbackData;
   
  // 校验签名
  if (!verifyWxSign(callbackData)) {
  return res.status(200).send('<xml><return_code><![CDATA[FAIL]]></return_code></xml>');
  }
   
  // 查询订单
  const order = await Order.findOne({ outTradeNo: out_trade_no });
  if (!order || order.status !== 'pending') {
  return res.status(200).send('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>');
  }
   
  // 更新订单状态
  await Order.updateOne(
  { _id: order._id },
  {
  $set: {
  status: 'paid',
  paidAt: new Date(),
  transactionId,
  totalFee: callbackData.total_fee / 100 // 转换为元
  }
  }
  );
   
  // 触发后续业务(如分账、通知等)
  await handlePaymentSuccess(order);
   
  res.status(200).send('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>');
  } catch (err) {
  console.error('支付回调处理失败:', err);
  res.status(200).send('<xml><return_code><![CDATA[FAIL]]></return_code></xml>');
  }
  });

五、知识付费小程序工程化实践建议

1. 性能优化方案

  • CDN加速:音视频文件使用对象存储+CDN分发,配置7天缓存策略
  • 数据库索引:为content.isFreeorder.userId+contentId等查询字段建立复合索引
  • 读写分离:主库处理订单写入,从库处理内容查询

2. 安全防护体系

  • 防盗链机制:音视频文件URL添加有效期参数(如?expires=1625097600&signature=xxx
  • 敏感数据:openid/unionid使用AES-256加密存储,JWT令牌设置短期有效期
  • 支付安全:实现订单幂等处理,防止重复扣款

3. 运维监控方案

  • 日志系统:使用Winston记录关键操作日志,按天切割存储
  • 告警机制:对支付失败率、接口响应时间设置阈值告警
  • 容灾方案:数据库主从同步,支付回调实现重试队列