引言:积分商城系统的商业价值与开发挑战

在当今的数字化营销时代,积分制兑换商城已成为企业提升用户粘性、促进消费转化的重要工具。无论是电商平台、银行系统还是移动应用,积分商城都扮演着核心角色。然而,从零开始开发一个可商用的积分商城系统并非易事,它涉及复杂的业务逻辑、安全机制、性能优化以及可扩展性设计。本指南将带您一步步从需求分析到源码实现,再到二次开发,构建一个完整的、可直接商用的积分商城系统。我们将使用Node.js作为后端技术栈(因其高效和生态丰富),MongoDB作为数据库(适合处理动态数据),并结合React作为前端框架,确保系统现代化且易于维护。

为什么选择这个技术栈?Node.js的非阻塞I/O模型适合高并发场景,MongoDB的文档结构能灵活存储积分规则和用户数据,而React则提供响应式UI。整个系统将遵循MVC(Model-View-Controller)架构,确保代码模块化,便于二次开发。指南中,我们会提供详尽的代码示例,包括API接口、数据库模型和前端组件,帮助您快速上手。如果您是开发者,这篇文章将节省您数周的开发时间;如果您是产品经理,它将帮助您理解系统架构以便决策。

在开始之前,确保您的开发环境已安装Node.js(v14+)、MongoDB(v5+)和npm/yarn。我们将从需求分析入手,逐步深入到源码实现和优化。

第一部分:需求分析与系统架构设计

1.1 核心业务需求梳理

一个可商用的积分商城系统必须覆盖以下核心功能:

  • 用户积分管理:用户注册、登录、积分获取(如签到、消费返积分)、积分扣除(兑换商品)。
  • 商品兑换:商品列表展示、库存管理、积分扣减、订单生成。
  • 积分规则引擎:灵活配置积分获取规则(如消费1元=1积分)和兑换规则(如100积分=1元商品)。
  • 后台管理:管理员管理商品、用户、积分流水、数据统计。
  • 安全与性能:防止积分刷取、API限流、数据加密。
  • 扩展性:支持第三方支付集成(如微信支付)、短信通知、日志审计。

非功能性需求:

  • 高可用:支持1000+并发用户。
  • 数据一致性:积分扣减需原子操作,避免超扣。
  • 易扩展:模块化设计,便于添加新积分来源(如任务系统)。

1.2 系统架构设计

我们采用前后端分离架构:

  • 后端:Node.js + Express.js框架,提供RESTful API。
  • 前端:React + Ant Design UI库,构建管理后台和用户端。
  • 数据库:MongoDB存储用户、商品、订单、积分流水。
  • 缓存:Redis用于积分实时查询和限流。
  • 部署:Docker容器化,Nginx反向代理。

架构图(文本描述):

用户端 (React App) --> API Gateway (Express) --> 业务层 (Service) --> 数据层 (MongoDB/Redis)
后台管理 (React Admin) --> 同上
第三方服务 (短信/支付) --> Webhook回调

这种设计确保了系统的解耦和可维护性。接下来,我们将逐步实现每个模块。

第二部分:环境搭建与项目初始化

2.1 后端项目初始化

首先,创建Node.js项目。打开终端,执行以下命令:

mkdir integral-mall-backend
cd integral-mall-backend
npm init -y
npm install express mongoose redis body-parser cors dotenv jsonwebtoken bcryptjs
npm install --save-dev nodemon
  • express: Web框架。
  • mongoose: MongoDB ODM。
  • redis: 缓存客户端。
  • jsonwebtoken & bcryptjs: JWT认证和密码加密。

创建项目结构:

integral-mall-backend/
├── src/
│   ├── config/      # 配置文件
│   ├── models/      # 数据库模型
│   ├── routes/      # API路由
│   ├── services/    # 业务逻辑
│   ├── middleware/  # 中间件(认证、限流)
│   └── app.js       # 入口文件
├── .env             # 环境变量
└── package.json

.env文件中配置:

PORT=3000
MONGO_URI=mongodb://localhost:27017/integral_mall
REDIS_URL=redis://localhost:6379
JWT_SECRET=your_super_secret_key

入口文件src/app.js

const express = require('express');
const mongoose = require('mongoose');
const redis = require('redis');
const cors = require('cors');
const bodyParser = require('body-parser');
require('dotenv').config();

const app = express();
app.use(cors());
app.use(bodyParser.json());

// 连接MongoDB
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('MongoDB connected'))
  .catch(err => console.error(err));

// 连接Redis
const redisClient = redis.createClient({ url: process.env.REDIS_URL });
redisClient.connect().then(() => console.log('Redis connected'));

// 路由导入
const authRoutes = require('./routes/auth');
const integralRoutes = require('./routes/integral');
const productRoutes = require('./routes/product');
app.use('/api/auth', authRoutes);
app.use('/api/integral', integralRoutes);
app.use('/api/product', productRoutes);

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

app.listen(process.env.PORT, () => {
  console.log(`Server running on port ${process.env.PORT}`);
});

package.json中添加启动脚本:

"scripts": {
  "start": "node src/app.js",
  "dev": "nodemon src/app.js"
}

运行npm run dev启动后端。

2.2 前端项目初始化

创建React项目(使用Create React App):

npx create-react-app integral-mall-frontend
cd integral-mall-frontend
npm install axios antd react-router-dom

项目结构:

integral-mall-frontend/
├── src/
│   ├── components/  # 组件
│   ├── pages/       # 页面
│   ├── services/    # API服务
│   └── App.js

src/services/api.js中配置Axios:

import axios from 'axios';

const API_BASE = 'http://localhost:3000/api';

export const api = axios.create({
  baseURL: API_BASE,
  timeout: 5000,
});

// 请求拦截器,添加JWT
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

运行npm start启动前端。现在,基础环境已就绪。

第三部分:核心模块源码实现

3.1 用户认证与积分管理模块

数据库模型(src/models/User.js)

用户模型包含积分字段:

const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  points: { type: Number, default: 0 },  // 当前积分
  createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('User', UserSchema);

积分流水模型(src/models/PointTransaction.js)

记录每笔积分变动,确保可追溯:

const mongoose = require('mongoose');

const PointTransactionSchema = new mongoose.Schema({
  userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  type: { type: String, enum: ['earn', 'spend', 'adjust'], required: true },  // earn: 获取, spend: 消耗, adjust: 调整
  points: { type: Number, required: true },
  description: { type: String, required: true },  // 如"消费返积分"
  orderId: { type: mongoose.Schema.Types.ObjectId, ref: 'Order' },  // 关联订单
  createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('PointTransaction', PointTransactionSchema);

认证路由(src/routes/auth.js)

注册和登录,使用JWT和密码加密:

const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const router = express.Router();

// 注册
router.post('/register', async (req, res) => {
  try {
    const { username, password, email } = req.body;
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = new User({ username, password: hashedPassword, email });
    await user.save();
    res.status(201).json({ message: 'User registered successfully' });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// 登录
router.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await User.findOne({ username });
    if (!user || !await bcrypt.compare(password, user.password)) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '24h' });
    res.json({ token, user: { id: user._id, username: user.username, points: user.points } });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;

积分服务(src/services/integralService.js)

处理积分获取和扣除,使用MongoDB事务确保原子性(需MongoDB 4.0+副本集):

const User = require('../models/User');
const PointTransaction = require('../models/PointTransaction');
const mongoose = require('mongoose');

// 获取积分(示例:签到奖励10积分)
async function earnPoints(userId, points, description) {
  const session = await mongoose.startSession();
  session.startTransaction();
  try {
    const user = await User.findById(userId).session(session);
    if (!user) throw new Error('User not found');
    
    user.points += points;
    await user.save({ session });
    
    const transaction = new PointTransaction({
      userId,
      type: 'earn',
      points,
      description
    });
    await transaction.save({ session });
    
    await session.commitTransaction();
    return { success: true, newPoints: user.points };
  } catch (error) {
    await session.abortTransaction();
    throw error;
  } finally {
    session.endSession();
  }
}

// 扣除积分(兑换商品)
async function spendPoints(userId, points, description, orderId) {
  const session = await mongoose.startSession();
  session.startTransaction();
  try {
    const user = await User.findById(userId).session(session);
    if (!user || user.points < points) throw new Error('Insufficient points');
    
    user.points -= points;
    await user.save({ session });
    
    const transaction = new PointTransaction({
      userId,
      type: 'spend',
      points: -points,  // 负值表示扣除
      description,
      orderId
    });
    await transaction.save({ session });
    
    await session.commitTransaction();
    return { success: true, newPoints: user.points };
  } catch (error) {
    await session.abortTransaction();
    throw error;
  } finally {
    session.endSession();
  }
}

module.exports = { earnPoints, spendPoints };

积分路由(src/routes/integral.js)

const express = require('express');
const { earnPoints, spendPoints } = require('../services/integralService');
const authMiddleware = require('../middleware/auth');  // JWT验证中间件
const router = express.Router();

// 签到赚积分(需认证)
router.post('/checkin', authMiddleware, async (req, res) => {
  try {
    const result = await earnPoints(req.user.userId, 10, 'Daily check-in');
    res.json(result);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// 扣除积分(兑换时调用)
router.post('/spend', authMiddleware, async (req, res) => {
  try {
    const { points, description, orderId } = req.body;
    const result = await spendPoints(req.user.userId, points, description, orderId);
    res.json(result);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

module.exports = router;

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

const jwt = require('jsonwebtoken');

module.exports = (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'No token, authorization denied' });

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Token is not valid' });
  }
};

3.2 商品与兑换模块

商品模型(src/models/Product.js)

const mongoose = require('mongoose');

const ProductSchema = new mongoose.Schema({
  name: { type: String, required: true },
  description: { type: String },
  price: { type: Number, required: true },  // 原价(元)
  pointsRequired: { type: Number, required: true },  // 所需积分
  stock: { type: Number, default: 0 },  // 库存
  imageUrl: { type: String },
  status: { type: String, enum: ['active', 'inactive'], default: 'active' },
  createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('Product', ProductSchema);

订单模型(src/models/Order.js)

const mongoose = require('mongoose');

const OrderSchema = new mongoose.Schema({
  userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  productId: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
  quantity: { type: Number, default: 1 },
  totalPoints: { type: Number, required: true },
  status: { type: String, enum: ['pending', 'completed', 'cancelled'], default: 'pending' },
  shippingAddress: { type: String },
  createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('Order', OrderSchema);

兑换服务(src/services/orderService.js)

处理兑换逻辑,包括库存检查和积分扣除:

const Product = require('../models/Product');
const Order = require('../models/Order');
const { spendPoints } = require('./integralService');
const mongoose = require('mongoose');

async function createOrder(userId, productId, quantity, shippingAddress) {
  const session = await mongoose.startSession();
  session.startTransaction();
  try {
    const product = await Product.findById(productId).session(session);
    if (!product || product.status !== 'active') throw new Error('Product not available');
    if (product.stock < quantity) throw new Error('Insufficient stock');

    const totalPoints = product.pointsRequired * quantity;
    const user = await User.findById(userId).session(session);
    if (user.points < totalPoints) throw new Error('Insufficient points');

    // 扣除积分
    await spendPoints(userId, totalPoints, `兑换 ${product.name}`, null);  // 订单ID暂未生成

    // 扣减库存
    product.stock -= quantity;
    await product.save({ session });

    // 创建订单
    const order = new Order({
      userId,
      productId,
      quantity,
      totalPoints,
      shippingAddress,
      status: 'completed'  // 简化,实际可添加支付确认
    });
    await order.save({ session });

    // 更新积分流水中的订单ID
    await PointTransaction.findOneAndUpdate(
      { userId, type: 'spend', description: new RegExp(product.name) },
      { orderId: order._id },
      { session }
    );

    await session.commitTransaction();
    return { success: true, orderId: order._id, remainingPoints: user.points - totalPoints };
  } catch (error) {
    await session.abortTransaction();
    throw error;
  } finally {
    session.endSession();
  }
}

module.exports = { createOrder };

商品与订单路由(src/routes/product.js)

const express = require('express');
const Product = require('../models/Product');
const Order = require('../models/Order');
const { createOrder } = require('../services/orderService');
const authMiddleware = require('../middleware/auth');
const router = express.Router();

// 获取商品列表
router.get('/', async (req, res) => {
  try {
    const products = await Product.find({ status: 'active' }).select('-__v');
    res.json(products);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 兑换商品
router.post('/exchange', authMiddleware, async (req, res) => {
  try {
    const { productId, quantity, shippingAddress } = req.body;
    const result = await createOrder(req.user.userId, productId, quantity, shippingAddress);
    res.json(result);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// 用户订单历史
router.get('/orders', authMiddleware, async (req, res) => {
  try {
    const orders = await Order.find({ userId: req.user.userId }).populate('productId', 'name pointsRequired');
    res.json(orders);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;

3.3 后台管理模块

为简化,我们使用一个简单的Admin路由(实际可扩展为独立应用)。添加管理员认证中间件(检查角色)。

管理员路由(src/routes/admin.js)

const express = require('express');
const Product = require('../models/Product');
const User = require('../models/User');
const PointTransaction = require('../models/PointTransaction');
const authMiddleware = require('../middleware/auth');
const adminMiddleware = require('../middleware/admin');  // 自定义:检查用户角色
const router = express.Router();

// 添加商品(管理员)
router.post('/products', authMiddleware, adminMiddleware, async (req, res) => {
  try {
    const { name, description, price, pointsRequired, stock, imageUrl } = req.body;
    const product = new Product({ name, description, price, pointsRequired, stock, imageUrl });
    await product.save();
    res.status(201).json(product);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// 获取积分流水(管理员)
router.get('/transactions', authMiddleware, adminMiddleware, async (req, res) => {
  try {
    const { userId, page = 1, limit = 10 } = req.query;
    const query = userId ? { userId } : {};
    const transactions = await PointTransaction.find(query)
      .populate('userId', 'username')
      .populate('orderId', '_id')
      .limit(limit * 1)
      .skip((page - 1) * limit)
      .sort({ createdAt: -1 });
    const total = await PointTransaction.countDocuments(query);
    res.json({ transactions, totalPages: Math.ceil(total / limit), currentPage: page });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 用户管理(查看/调整积分)
router.put('/users/:id/points', authMiddleware, adminMiddleware, async (req, res) => {
  try {
    const { points, description } = req.body;
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ error: 'User not found' });
    
    user.points += points;
    await user.save();
    
    const transaction = new PointTransaction({
      userId: user._id,
      type: 'adjust',
      points,
      description: `Admin adjust: ${description}`
    });
    await transaction.save();
    
    res.json({ success: true, newPoints: user.points });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

module.exports = router;

管理员中间件(src/middleware/admin.js):

const User = require('../models/User');

module.exports = async (req, res, next) => {
  try {
    const user = await User.findById(req.user.userId);
    if (user.role !== 'admin') return res.status(403).json({ error: 'Admin access required' });
    next();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
};

在User模型中添加role字段:role: { type: String, enum: ['user', 'admin'], default: 'user' }

3.4 前端实现示例(用户端)

登录页面(src/pages/Login.js)

import React, { useState } from 'react';
import { Form, Input, Button, message } from 'antd';
import { api } from '../services/api';

const Login = () => {
  const [loading, setLoading] = useState(false);

  const onFinish = async (values) => {
    setLoading(true);
    try {
      const res = await api.post('/auth/login', values);
      localStorage.setItem('token', res.data.token);
      message.success('登录成功');
      window.location.href = '/dashboard';
    } catch (error) {
      message.error(error.response?.data?.error || '登录失败');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ maxWidth: 400, margin: '50px auto', padding: 20 }}>
      <h2>登录积分商城</h2>
      <Form onFinish={onFinish}>
        <Form.Item name="username" rules={[{ required: true, message: '请输入用户名' }]}>
          <Input placeholder="用户名" />
        </Form.Item>
        <Form.Item name="password" rules={[{ required: true, message: '请输入密码' }]}>
          <Input.Password placeholder="密码" />
        </Form.Item>
        <Form.Item>
          <Button type="primary" htmlType="submit" loading={loading} block>登录</Button>
        </Form.Item>
      </Form>
    </div>
  );
};

export default Login;

积分兑换页面(src/pages/Exchange.js)

import React, { useState, useEffect } from 'react';
import { Card, Button, message, Row, Col } from 'antd';
import { api } from '../services/api';

const Exchange = () => {
  const [products, setProducts] = useState([]);
  const [userPoints, setUserPoints] = useState(0);

  useEffect(() => {
    fetchProducts();
    fetchUserPoints();
  }, []);

  const fetchProducts = async () => {
    try {
      const res = await api.get('/product');
      setProducts(res.data);
    } catch (error) {
      message.error('获取商品失败');
    }
  };

  const fetchUserPoints = async () => {
    try {
      const res = await api.get('/integral/balance');  // 需添加此API
      setUserPoints(res.data.points);
    } catch (error) {
      message.error('获取积分失败');
    }
  };

  const handleExchange = async (productId, pointsRequired) => {
    if (userPoints < pointsRequired) {
      message.error('积分不足');
      return;
    }
    try {
      const res = await api.post('/product/exchange', { productId, quantity: 1, shippingAddress: '示例地址' });
      message.success(`兑换成功!订单ID: ${res.data.orderId}`);
      fetchUserPoints();
    } catch (error) {
      message.error(error.response?.data?.error || '兑换失败');
    }
  };

  return (
    <div style={{ padding: 20 }}>
      <h2>积分兑换</h2>
      <p>当前积分: {userPoints}</p>
      <Row gutter={16}>
        {products.map(product => (
          <Col span={8} key={product._id}>
            <Card title={product.name} extra={`需 ${product.pointsRequired} 积分`}>
              <p>{product.description}</p>
              <p>库存: {product.stock}</p>
              <Button type="primary" onClick={() => handleExchange(product._id, product.pointsRequired)}>
                兑换
              </Button>
            </Card>
          </Col>
        ))}
      </Row>
    </div>
  );
};

export default Exchange;

对于后台管理,可使用Ant Design Pro或自定义Admin组件,类似地调用Admin API。

第四部分:安全、性能优化与测试

4.1 安全机制

  • 防刷积分:使用Redis限流。例如,添加中间件(src/middleware/rateLimiter.js):
const redis = require('redis');
const client = redis.createClient({ url: process.env.REDIS_URL });

module.exports = (key, limit = 5, window = 60) => async (req, res, next) => {
  const ip = req.ip;
  const redisKey = `${key}:${ip}`;
  const current = await client.incr(redisKey);
  if (current === 1) await client.expire(redisKey, window);
  if (current > limit) return res.status(429).json({ error: 'Too many requests' });
  next();
};

在积分路由使用:router.post('/checkin', rateLimiter('checkin', 3, 3600), authMiddleware, ...)

  • 输入验证:使用Joi库验证请求体。
  • HTTPS:生产环境强制HTTPS。

4.2 性能优化

  • 缓存:商品列表缓存到Redis,过期时间5分钟。
// 在productService.js
async function getProducts() {
  const cached = await redisClient.get('products');
  if (cached) return JSON.parse(cached);
  const products = await Product.find({ status: 'active' });
  await redisClient.setEx('products', 300, JSON.stringify(products));
  return products;
}
  • 分页与索引:MongoDB添加索引:UserSchema.index({ username: 1 });
  • 负载均衡:使用PM2集群模式运行Node.js:npm install -g pm2,然后pm2 start src/app.js -i 4

4.3 测试

使用Jest进行单元测试:

npm install --save-dev jest supertest

示例测试(tests/integral.test.js):

const request = require('supertest');
const app = require('../src/app');
const mongoose = require('mongoose');

describe('Integral API', () => {
  beforeAll(async () => {
    await mongoose.connect(process.env.MONGO_URI);
  });

  afterAll(async () => {
    await mongoose.connection.close();
  });

  it('should earn points on checkin', async () => {
    const loginRes = await request(app).post('/api/auth/login').send({ username: 'test', password: 'test' });
    const token = loginRes.body.token;

    const res = await request(app)
      .post('/api/integral/checkin')
      .set('Authorization', `Bearer ${token}`)
      .expect(200);

    expect(res.body.success).toBe(true);
    expect(res.body.newPoints).toBeGreaterThan(0);
  });
});

运行npm test。集成测试可使用Postman验证API。

第五部分:部署与二次开发教程

5.1 部署指南

  1. 本地测试:确保MongoDB和Redis运行,启动前后端。
  2. Docker化
    • 创建Dockerfile(后端):
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
  • docker-compose.yml
version: '3'
services:
  backend:
    build: .
    ports: ["3000:3000"]
    environment:
      - MONGO_URI=mongodb://mongo:27017/integral_mall
      - REDIS_URL=redis://redis:6379
    depends_on:
      - mongo
      - redis
  mongo:
    image: mongo:5
    ports: ["27017:27017"]
  redis:
    image: redis:6
    ports: ["6379:6379"]
  frontend:
    build: ../integral-mall-frontend
    ports: ["80:80"]
    depends_on: [backend]
  • 运行docker-compose up
  1. 生产部署:使用阿里云/腾讯云ECS,配置Nginx反向代理前端,后端用PM2。添加SSL证书。

5.2 二次开发教程

系统设计为模块化,便于扩展:

  • 添加新积分来源:如任务系统。在integralService.js添加earnPointsFromTask(userId, taskId),调用earnPoints
  • 集成支付:在订单服务中添加支付确认。使用微信支付SDK:
npm install wechatpay-axios-plugin

createOrder后添加支付步骤:

const { WechatPay } = require('wechatpay-axios-plugin');
const pay = new WechatPay({ mchid: '商户ID', privateKey: '私钥' });
// 调用pay.transactions.jsapi()生成支付链接
  • 自定义规则引擎:创建RuleEngine类,解析JSON规则(如{“earn”: {“消费”: 1}}),动态应用。
  • 前端扩展:使用React Hooks添加实时积分显示:useEffect轮询API。
  • API文档:使用Swagger:npm install swagger-jsdoc swagger-ui-express,在app.js添加:
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
  definition: { openapi: '3.0.0', info: { title: 'Integral Mall API', version: '1.0.0' } },
  apis: ['./routes/*.js'],
};
const specs = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));

通过这些步骤,您可以快速迭代系统,例如添加积分过期功能(在User模型添加pointsExpiry字段,定时任务清理)。

结论

本指南提供了从零搭建可商用积分商城系统的完整解决方案,包括详尽的源码、代码示例和优化建议。通过Node.js、MongoDB和React的组合,您能构建一个高效、安全的系统。实际开发中,根据业务调整规则和UI。如果遇到问题,建议参考官方文档或社区资源。开始您的开发之旅吧!如果需要特定模块的深入扩展,欢迎提供更多细节。