Files
niumalll/docs/技术实施方案.md
2025-09-02 01:23:16 +08:00

50 KiB
Raw Blame History

活牛采购智能数字化系统 - 技术实施方案

1. 技术架构

1.1 系统架构

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Mini-Program  │    │   Admin System   │    │    Website      │
│  (uni-app)      │    │  (Vue 3)         │    │  (HTML5 + Bootstrap)  │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                      │                      │
         └──────────┬───────────┴──────────┬───────────┘
                    │                      │
           ┌────────┴─────────┐    ┌──────┴───────┐
           │   API Gateway     │    │ 统一用户中心  │
           │  (Authentication)│    │ (Single Sign-On)
           └────────┬─────────┘    └──────┬───────┘
                    │                      │
                    └──────────┬───────────┘
                               │
                    ┌──────────┴──────────┐
                    │   微服务层           │
                    │  (NestJS Services)  │
                    └──────────┬──────────┘
                               │
                    ┌──────────┴──────────┐
                    │   统一数据库         │
                    │  (MySQL + Redis)    │
                    └─────────────────────┘

1.2 技术选型

层级 技术栈 说明
官网前端 HTML5 + Bootstrap 企业官网展示
管理后台 Vue 3 + TypeScript + Element Plus + Vite + Pinia Web管理后台
小程序端 Uni-app + Vue 3 + TypeScript 跨平台小程序矩阵
后端 Node.js + Express.js 微服务架构
数据库 MySQL 5.7 + Redis 统一业务数据 + 缓存
文件存储 MinIO/阿里云OSS 视频文件存储
消息队列 RabbitMQ 异步任务处理
实时通信 WebSocket 实时数据传输

数据库连接信息:

  • 主机: 129.211.213.226
  • 端口: 9527
  • 用户名: root
  • 密码: aiotAiot123!
  • 数据库名: jiebandata

管理员默认账号: admin/admin123

1.3 小程序矩阵技术架构

┌─────────────────────────────────────────────────┐
│                Uni-app 跨端框架                  │
├─────────────────────────────────────────────────┤
│  Client MP    Supplier MP   Driver MP   Staff MP │
│  (客户端)      (供应商)      (司机)     (内部员工) │
└─────────────────────────────────────────────────┘
                     │
           ┌─────────┴─────────┐
           │  统一API接口调用    │
           │  统一用户认证       │
           │  统一数据格式       │
           └─────────┬─────────┘
                     │
           ┌─────────┴─────────┐
           │  后端微服务集群     │
           └───────────────────┘

2. 微服务划分

2.1 服务模块

服务名称 职责 技术栈
user-service 统一用户管理、权限控制 Express.js + Sequelize + JWT + RBAC
auth-service 统一认证中心、单点登录 Express.js + Sequelize + OAuth2.0
order-service 订单管理、流程控制 Express.js + Sequelize + MySQL
transport-service 运输跟踪、状态管理 Express.js + Sequelize + WebSocket
payment-service 支付结算、财务处理 Express.js + Sequelize + 支付接口
file-service 文件管理、视频处理 Express.js + Sequelize + MinIO
notification-service 消息通知、提醒 Express.js + Sequelize + RabbitMQ
mini-program-service 小程序接口统一管理 Express.js + Sequelize + 接口网关

2.2 小程序服务接口

// 统一小程序API接口设计
export interface MiniProgramApi {
  // 用户认证接口
  login(phone: string, code: string): Promise<UserInfo>;
  logout(): Promise<void>;
  
  // 订单相关接口
  createOrder(orderData: OrderCreateDto): Promise<Order>;
  getOrderList(params: OrderQueryDto): Promise<Order[]>;
  getOrderDetail(orderId: string): Promise<OrderDetail>;
  
  // 运输跟踪接口
  reportLocation(location: LocationDto): Promise<void>;
  getTransportTrack(orderId: string): Promise<TransportTrack[]>;
  
  // 文件上传接口
  uploadFile(file: File, type: FileType): Promise<FileResponse>;
  
  // 消息通知接口
  getNotifications(): Promise<Notification[]>;
  markAsRead(notificationId: string): Promise<void>;
}

3. 数据库设计

3.1 Sequelize ORM 模型定义

// 统一用户模型
const User = sequelize.define('User', {
  id: {
    type: DataTypes.BIGINT,
    primaryKey: true,
    autoIncrement: true
  },
  uuid: {
    type: DataTypes.STRING(36),
    allowNull: false,
    unique: true
  },
  username: {
    type: DataTypes.STRING(50),
    allowNull: false,
    unique: true
  },
  password_hash: {
    type: DataTypes.STRING(255),
    allowNull: false
  },
  phone: {
    type: DataTypes.STRING(20),
    allowNull: false,
    unique: true
  },
  email: DataTypes.STRING(100),
  real_name: DataTypes.STRING(50),
  avatar_url: DataTypes.STRING(255),
  user_type: {
    type: DataTypes.ENUM('client','supplier','driver','staff','admin'),
    allowNull: false
  },
  status: {
    type: DataTypes.ENUM('active','inactive','locked'),
    defaultValue: 'active'
  }
}, {
  tableName: 'users',
  timestamps: true,
  indexes: [
    { fields: ['phone'] },
    { fields: ['user_type'] },
    { fields: ['status'] }
  ]
});

// 用户角色模型
const UserRole = sequelize.define('UserRole', {
  id: {
    type: DataTypes.BIGINT,
    primaryKey: true,
    autoIncrement: true
  },
  role_code: {
    type: DataTypes.STRING(50),
    allowNull: false
  }
}, {
  tableName: 'user_roles',
  timestamps: true
});

// 用户会话模型
const UserSession = sequelize.define('UserSession', {
  id: {
    type: DataTypes.BIGINT,
    primaryKey: true,
    autoIncrement: true
  },
  session_token: {
    type: DataTypes.STRING(255),
    allowNull: false,
    unique: true
  },
  device_type: {
    type: DataTypes.ENUM('web','mini_program','app'),
    allowNull: false
  },
  device_info: DataTypes.STRING(500),
  login_ip: DataTypes.STRING(45),
  expires_at: {
    type: DataTypes.DATE,
    allowNull: false
  }
}, {
  tableName: 'user_sessions',
  timestamps: true,
  indexes: [
    { fields: ['session_token'] },
    { fields: ['user_id', 'device_type'] }
  ]
});

// 订单模型
const Order = sequelize.define('Order', {
  id: {
    type: DataTypes.BIGINT,
    primaryKey: true,
    autoIncrement: true
  },
  order_no: {
    type: DataTypes.STRING(50),
    allowNull: false,
    unique: true
  },
  breed_type: {
    type: DataTypes.STRING(20),
    allowNull: false
  },
  min_weight: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false
  },
  max_weight: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false
  },
  total_count: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  total_weight: DataTypes.DECIMAL(10, 2),
  unit_price: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false
  },
  total_amount: {
    type: DataTypes.DECIMAL(15, 2),
    allowNull: false
  },
  status: {
    type: DataTypes.ENUM('pending','confirmed','loading','shipping','delivered','completed','cancelled'),
    defaultValue: 'pending'
  }
}, {
  tableName: 'orders',
  timestamps: true,
  indexes: [
    { fields: ['order_no'] },
    { fields: ['buyer_id'] },
    { fields: ['status'] },
    { fields: ['created_at'] }
  ]
});

// 定义模型关联关系
User.hasMany(UserRole, { foreignKey: 'user_id' });
UserRole.belongsTo(User, { foreignKey: 'user_id' });

User.hasMany(UserSession, { foreignKey: 'user_id' });
UserSession.belongsTo(User, { foreignKey: 'user_id' });

User.hasMany(Order, { foreignKey: 'buyer_id', as: 'BuyerOrders' });
User.hasMany(Order, { foreignKey: 'trader_id', as: 'TraderOrders' });
User.hasMany(Order, { foreignKey: 'supplier_id', as: 'SupplierOrders' });
Order.belongsTo(User, { foreignKey: 'buyer_id', as: 'Buyer' });
Order.belongsTo(User, { foreignKey: 'trader_id', as: 'Trader' });
Order.belongsTo(User, { foreignKey: 'supplier_id', as: 'Supplier' });

// 运输跟踪模型 const TransportTrack = sequelize.define('TransportTrack', { id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true }, latitude: { type: DataTypes.DECIMAL(10, 8), allowNull: true }, longitude: { type: DataTypes.DECIMAL(11, 8), allowNull: true }, speed: { type: DataTypes.DECIMAL(5, 2), allowNull: true }, direction: { type: DataTypes.DECIMAL(5, 2), allowNull: true }, cattle_status: { type: DataTypes.STRING(20), allowNull: true }, video_url: { type: DataTypes.STRING(255), allowNull: true } }, { tableName: 'transport_tracks', timestamps: true });

// 结算模型 const Settlement = sequelize.define('Settlement', { id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true }, prepayment_amount: { type: DataTypes.DECIMAL(15, 2), allowNull: true }, final_amount: { type: DataTypes.DECIMAL(15, 2), allowNull: true }, total_amount: { type: DataTypes.DECIMAL(15, 2), allowNull: false }, payment_status: { type: DataTypes.ENUM('pending', 'paid', 'refunded'), defaultValue: 'pending' }, payment_time: { type: DataTypes.DATE, allowNull: true } }, { tableName: 'settlements', timestamps: true });

// 完善模型关联关系 Order.hasMany(TransportTrack, { foreignKey: 'order_id' }); TransportTrack.belongsTo(Order, { foreignKey: 'order_id' });

Order.hasMany(Settlement, { foreignKey: 'order_id' }); Settlement.belongsTo(Order, { foreignKey: 'order_id' });

User.hasMany(TransportTrack, { foreignKey: 'driver_id', as: 'DriverTracks' }); TransportTrack.belongsTo(User, { foreignKey: 'driver_id', as: 'Driver' });

// Sequelize 连接配置 const sequelize = new Sequelize('jiebandata', 'root', 'aiotAiot123!', { host: '129.211.213.226', port: 9527, dialect: 'mysql', dialectOptions: { charset: 'utf8mb4', collate: 'utf8mb4_unicode_ci' }, pool: { max: 10, min: 0, acquire: 30000, idle: 10000 }, logging: process.env.NODE_ENV === 'development' ? console.log : false, timezone: '+08:00' // 东八区 });


## 4. 统一API接口设计

### 4.1 Express.js + Swagger 配置
```javascript
// app.js - Express.js 应用配置
const express = require('express');
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const app = express();

// 中间件配置
app.use(helmet());
app.use(cors());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

// 速率限制
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100 // 每个IP限制100个请求
});
app.use(limiter);

// Swagger 配置
const swaggerOptions = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: '活牛采购智能数字化系统 API',
      version: '1.0.0',
      description: '活牛采购标准化操作流程系统接口文档',
      contact: {
        name: 'API支持',
        email: 'support@niumall.com'
      }
    },
    servers: [
      {
        url: 'http://localhost:3000/api',
        description: '开发环境'
      },
      {
        url: 'https://api.niumall.com/api',
        description: '生产环境'
      }
    ],
    components: {
      securitySchemes: {
        BearerAuth: {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT'
        }
      },
      schemas: {
        ApiResponse: {
          type: 'object',
          properties: {
            code: { type: 'integer', description: '状态码200成功400客户端错误500服务端错误' },
            message: { type: 'string', description: '提示信息' },
            data: { type: 'object', description: '响应数据' },
            timestamp: { type: 'integer', description: '时间戳' }
          }
        },
        PaginationParams: {
          type: 'object',
          properties: {
            page: { type: 'integer', description: '当前页码' },
            limit: { type: 'integer', description: '每页数量' },
            sort: { type: 'string', description: '排序字段' },
            order: { type: 'string', enum: ['asc', 'desc'], description: '排序方向' }
          }
        },
        PaginatedResponse: {
          type: 'object',
          properties: {
            items: { type: 'array', description: '数据列表' },
            total: { type: 'integer', description: '总记录数' },
            page: { type: 'integer', description: '当前页码' },
            limit: { type: 'integer', description: '每页数量' },
            totalPages: { type: 'integer', description: '总页数' }
          }
        }
      }
    }
  },
  apis: ['./routes/*.js', './models/*.js'] // API路由文件路径
};

const swaggerSpec = swaggerJsdoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));

// 统一响应中间件
app.use((req, res, next) => {
  res.apiSuccess = (data, message = '成功') => {
    res.json({
      code: 200,
      message,
      data,
      timestamp: Date.now()
    });
  };
  
  res.apiError = (message = '服务器错误', code = 500) => {
    res.status(code).json({
      code,
      message,
      data: null,
      timestamp: Date.now()
    });
  };
  next();
});

// JWT认证中间件
const jwt = require('jsonwebtoken');
app.use((req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  if (token) {
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
      req.user = decoded;
    } catch (error) {
      // Token验证失败但不阻止请求
    }
  }
  next();
});

module.exports = app;

4.2 Express.js 认证路由示例

// routes/auth.js - 认证路由
const express = require('express');
const jwt = require('jsonwebtoken');
const { User } = require('../models');
const router = express.Router();

/**
 * @swagger
 * /api/auth/mini-program/login:
 *   post:
 *     summary: 小程序用户登录
 *     tags: [认证]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - phone
 *               - code
 *               - miniProgramType
 *             properties:
 *               phone:
 *                 type: string
 *                 description: 手机号
 *                 example: "13800138000"
 *               code:
 *                 type: string
 *                 description: 验证码
 *                 example: "123456"
 *               miniProgramType:
 *                 type: string
 *                 enum: [client, supplier, driver, staff]
 *                 description: 小程序类型
 *                 example: "client"
 *     responses:
 *       200:
 *         description: 登录成功
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ApiResponse'
 *               example:
 *                 code: 200
 *                 message: "登录成功"
 *                 data:
 *                   token: "jwt_token_string"
 *                   userInfo:
 *                     id: 1
 *                     username: "user123"
 *                     realName: "张三"
 *                     avatar: "https://example.com/avatar.jpg"
 *                     userType: "client"
 *                     roles: ["purchaser"]
 *                 timestamp: 1643097600000
 */
router.post('/mini-program/login', async (req, res) => {
  try {
    const { phone, code, miniProgramType } = req.body;
    
    // 验证验证码逻辑
    // ...
    
    // 查找用户
    const user = await User.findOne({ 
      where: { phone, user_type: miniProgramType } 
    });
    
    if (!user) {
      return res.apiError('用户不存在', 404);
    }
    
    // 生成JWT token
    const token = jwt.sign(
      { 
        id: user.id, 
        username: user.username,
        userType: user.user_type 
      },
      process.env.JWT_SECRET || 'your-secret-key',
      { expiresIn: '7d' }
    );
    
    res.apiSuccess({
      token,
      userInfo: {
        id: user.id,
        username: user.username,
        realName: user.real_name,
        avatar: user.avatar_url,
        userType: user.user_type,
        roles: [] // 根据实际业务获取角色
      }
    }, '登录成功');
    
  } catch (error) {
    console.error('Login error:', error);
    res.apiError('登录失败');
  }
});

/**
 * @swagger
 * /api/auth/user-info:
 *   get:
 *     summary: 获取当前用户信息
 *     tags: [认证]
 *     security:
 *       - BearerAuth: []
 *     responses:
 *       200:
 *         description: 获取成功
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ApiResponse'
 */
router.get('/user-info', (req, res) => {
  if (!req.user) {
    return res.apiError('未认证', 401);
  }
  
  // 从数据库获取完整的用户信息
  User.findByPk(req.user.id)
    .then(user => {
      res.apiSuccess({
        id: user.id,
        username: user.username,
        realName: user.real_name,
        avatar: user.avatar_url,
        userType: user.user_type,
        phone: user.phone,
        email: user.email
      });
    })
    .catch(error => {
      res.apiError('获取用户信息失败');
    });
});

module.exports = router;

4.3 Express.js 订单路由示例

// routes/orders.js - 订单路由
const express = require('express');
const { Order, User } = require('../models');
const router = express.Router();

/**
 * @swagger
 * /api/orders:
 *   post:
 *     summary: 创建采购订单
 *     tags: [订单]
 *     security:
 *       - BearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - breedType
 *               - minWeight
 *               - maxWeight
 *               - totalCount
 *               - unitPrice
 *             properties:
 *               breedType:
 *                 type: string
 *                 description: 牛品种
 *                 example: "simmental"
 *               minWeight:
 *                 type: number
 *                 format: float
 *                 description: 最低重量(kg)
 *                 example: 500
 *               maxWeight:
 *                 type: number
 *                 format: float
 *                 description: 最高重量(kg)
 *                 example: 600
 *               totalCount:
 *                 type: integer
 *                 description: 总数量
 *                 example: 100
 *               unitPrice:
 *                 type: number
 *                 format: float
 *                 description: 单价(元/kg)
 *                 example: 35.5
 *               deliveryAddress:
 *                 type: string
 *                 description: 配送地址
 *                 example: "xxx养殖场"
 *               deliveryDate:
 *                 type: string
 *                 format: date
 *                 description: 配送日期
 *                 example: "2024-01-25"
 *               specialRequirements:
 *                 type: string
 *                 description: 特殊要求
 *                 example: "要求健康无病"
 *     responses:
 *       201:
 *         description: 订单创建成功
 */
router.post('/', async (req, res) => {
  try {
    const {
      breedType,
      minWeight,
      maxWeight,
      totalCount,
      unitPrice,
      deliveryAddress,
      deliveryDate,
      specialRequirements
    } = req.body;
    
    // 计算总金额
    const avgWeight = (minWeight + maxWeight) / 2;
    const totalAmount = avgWeight * totalCount * unitPrice;
    
    // 生成订单号
    const orderNo = 'ORD' + Date.now() + Math.random().toString(36).substr(2, 6);
    
    const order = await Order.create({
      order_no: orderNo,
      buyer_id: req.user.id,
      trader_id: 1, // 默认贸易商ID
      supplier_id: 1, // 默认供应商ID
      breed_type: breedType,
      min_weight: minWeight,
      max_weight: maxWeight,
      total_count: totalCount,
      total_weight: avgWeight * totalCount,
      unit_price: unitPrice,
      total_amount: totalAmount,
      status: 'pending'
    });
    
    res.apiSuccess(order, '订单创建成功');
    
  } catch (error) {
    console.error('Create order error:', error);
    res.apiError('创建订单失败');
  }
});

/**
 * @swagger
 * /api/orders:
 *   get:
 *     summary: 获取订单列表
 *     tags: [订单]
 *     security:
 *       - BearerAuth: []
 *     parameters:
 *       - in: query
 *         name: status
 *         schema:
 *           type: string
 *         description: 订单状态过滤
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *           default: 1
 *         description: 页码
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 10
 *         description: 每页数量
 *     responses:
 *       200:
 *         description: 获取成功
 */
router.get('/', async (req, res) => {
  try {
    const { status, page = 1, limit = 10 } = req.query;
    const offset = (page - 1) * limit;
    
    const whereClause = {};
    if (status) {
      whereClause.status = status;
    }
    
    // 根据用户类型过滤订单
    if (req.user.userType === 'client') {
      whereClause.buyer_id = req.user.id;
    } else if (req.user.userType === 'supplier') {
      whereClause.supplier_id = req.user.id;
    }
    
    const { count, rows: orders } = await Order.findAndCountAll({
      where: whereClause,
      limit: parseInt(limit),
      offset: parseInt(offset),
      order: [['created_at', 'DESC']],
      include: [
        { model: User, as: 'Buyer', attributes: ['id', 'real_name', 'phone'] },
        { model: User, as: 'Supplier', attributes: ['id', 'real_name', 'phone'] }
      ]
    });
    
    res.apiSuccess({
      items: orders,
      total: count,
      page: parseInt(page),
      limit: parseInt(limit),
      totalPages: Math.ceil(count / limit)
    });
    
  } catch (error) {
    console.error('Get orders error:', error);
    res.apiError('获取订单列表失败');
  }
});

module.exports = router;

4.4 Express.js 运输跟踪路由示例

// routes/transport.js - 运输跟踪路由
const express = require('express');
const { TransportTrack, Order } = require('../models');
const router = express.Router();

/**
 * @swagger
 * /api/transport/tracks:
 *   post:
 *     summary: 司机上报位置信息
 *     tags: [运输跟踪]
 *     security:
 *       - BearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - orderId
 *               - latitude
 *               - longitude
 *             properties:
 *               orderId:
 *                 type: integer
 *                 description: 订单ID
 *                 example: 123
 *               latitude:
 *                 type: number
 *                 format: float
 *                 description: 纬度
 *                 example: 39.9042
 *               longitude:
 *                 type: number
 *                 format: float
 *                 description: 经度
 *                 example: 116.4074
 *               speed:
 *                 type: number
 *                 format: float
 *                 description: 速度(km/h)
 *                 example: 80.5
 *               direction:
 *                 type: number
 *                 format: float
 *                 description: 方向(度)
 *                 example: 45.2
 *               cattleStatus:
 *                 type: string
 *                 description: 牛只状态
 *                 example: "normal"
 *               temperature:
 *                 type: number
 *                 format: float
 *                 description: 车内温度(℃)
 *                 example: 25.5
 *               humidity:
 *                 type: number
 *                 format: float
 *                 description: 湿度(%)
 *                 example: 60.2
 *               videoUrl:
 *                 type: string
 *                 description: 状态视频URL
 *                 example: "https://example.com/status.mp4"
 *     responses:
 *       200:
 *         description: 位置上报成功
 */
router.post('/tracks', async (req, res) => {
  try {
    const {
      orderId,
      latitude,
      longitude,
      speed,
      direction,
      cattleStatus,
      temperature,
      humidity,
      videoUrl
    } = req.body;
    
    // 验证司机权限
    const order = await Order.findByPk(orderId);
    if (!order || order.driver_id !== req.user.id) {
      return res.apiError('无权限操作此订单', 403);
    }
    
    const track = await TransportTrack.create({
      order_id: orderId,
      driver_id: req.user.id,
      latitude,
      longitude,
      speed,
      direction,
      cattle_status: cattleStatus,
      temperature,
      humidity,
      video_url: videoUrl
    });
    
    // 实时推送位置信息给相关用户
    // WebSocket推送逻辑...
    
    res.apiSuccess(track, '位置上报成功');
    
  } catch (error) {
    console.error('Report location error:', error);
    res.apiError('位置上报失败');
  }
});

/**
 * @swagger
 * /api/transport/orders/{orderId}/tracks:
 *   get:
 *     summary: 获取订单运输轨迹
 *     tags: [运输跟踪]
 *     security:
 *       - BearerAuth: []
 *     parameters:
 *       - in: path
 *         name: orderId
 *         required: true
 *         schema:
 *           type: integer
 *         description: 订单ID
 *     responses:
 *       200:
 *         description: 获取成功
 */
router.get('/orders/:orderId/tracks', async (req, res) => {
  try {
    const { orderId } = req.params;
    
    // 验证订单访问权限
    const order = await Order.findByPk(orderId);
    if (!order) {
      return res.apiError('订单不存在', 404);
    }
    
    // 权限验证:采购人只能查看自己的订单
    if (req.user.userType === 'client' && order.buyer_id !== req.user.id) {
      return res.apiError('无权限查看此订单', 403);
    }
    
    const tracks = await TransportTrack.findAll({
      where: { order_id: orderId },
      order: [['created_at', 'ASC']],
      attributes: [
        'id',
        'latitude',
        'longitude',
        'speed',
        'direction',
        'cattle_status',
        'temperature',
        'humidity',
        'video_url',
        'created_at'
      ]
    });
    
    res.apiSuccess(tracks);
    
  } catch (error) {
    console.error('Get tracks error:', error);
    res.apiError('获取运输轨迹失败');
  }
});

module.exports = router;

4.5 Express.js 文件上传路由示例

// routes/files.js - 文件上传路由
const express = require('express');
const multer = require('multer');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const router = express.Router();

// 配置multer文件上传
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname);
    cb(null, `${uuidv4()}${ext}`);
  }
});

const upload = multer({
  storage,
  limits: {
    fileSize: 100 * 1024 * 1024 // 100MB限制
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = [
      'image/jpeg',
      'image/png',
      'image/gif',
      'video/mp4',
      'video/quicktime',
      'application/pdf'
    ];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('不支持的文件类型'));
    }
  }
});

/**
 * @swagger
 * /api/files/upload:
 *   post:
 *     summary: 统一文件上传接口
 *     tags: [文件管理]
 *     security:
 *       - BearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         multipart/form-data:
 *           schema:
 *             type: object
 *             required:
 *               - file
 *             properties:
 *               file:
 *                 type: string
 *                 format: binary
 *                 description: 上传的文件
 *               type:
 *                 type: string
 *                 description: 文件类型
 *                 example: "cattle_video"
 *               businessId:
 *                 type: string
 *                 description: 业务ID
 *                 example: "order_123"
 *               description:
 *                 type: string
 *                 description: 文件描述
 *                 example: "装车过程视频"
 *     responses:
 *       200:
 *         description: 上传成功
 */
router.post('/upload', upload.single('file'), async (req, res) => {
  try {
    const { type, businessId, description } = req.body;
    
    if (!req.file) {
      return res.apiError('请选择要上传的文件', 400);
    }
    
    // 这里应该集成到云存储如阿里云OSS、腾讯云COS
    // 实际项目中应该将文件上传到云存储并返回云存储URL
    
    const fileInfo = {
      fileId: `file_${uuidv4()}`,
      url: `/uploads/${req.file.filename}`,
      thumbnail: `/uploads/thumbnails/${req.file.filename}.jpg`,
      size: req.file.size,
      mimeType: req.file.mimetype,
      originalName: req.file.originalname,
      type,
      businessId,
      description
    };
    
    // 保存文件信息到数据库
    // await File.create(fileInfo);
    
    res.apiSuccess(fileInfo, '上传成功');
    
  } catch (error) {
    console.error('File upload error:', error);
    res.apiError(error.message || '文件上传失败');
  }
});

module.exports = router;

4.6 Express.js 支付路由示例

// routes/payments.js - 支付路由
const express = require('express');
const { Payment, Order } = require('../models');
const router = express.Router();

/**
 * @swagger
 * /api/payments:
 *   post:
 *     summary: 创建支付订单
 *     tags: [支付管理]
 *     security:
 *       - BearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - orderId
 *               - amount
 *               - paymentType
 *             properties:
 *               orderId:
 *                 type: integer
 *                 description: 订单ID
 *                 example: 123
 *               amount:
 *                 type: number
 *                 format: float
 *                 description: 支付金额(元)
 *                 example: 355000
 *               paymentType:
 *                 type: string
 *                 enum: [wechat, alipay, bank]
 *                 description: 支付类型
 *                 example: "wechat"
 *               paymentMethod:
 *                 type: string
 *                 enum: [mini_program, app, web]
 *                 description: 支付方式
 *                 example: "mini_program"
 *     responses:
 *       200:
 *         description: 创建成功
 */
router.post('/', async (req, res) => {
  try {
    const { orderId, amount, paymentType, paymentMethod = 'mini_program' } = req.body;
    
    // 验证订单
    const order = await Order.findByPk(orderId);
    if (!order) {
      return res.apiError('订单不存在', 404);
    }
    
    // 验证支付权限
    if (order.buyer_id !== req.user.id) {
      return res.apiError('无权限支付此订单', 403);
    }
    
    // 创建支付订单
    const payment = await Payment.create({
      order_id: orderId,
      user_id: req.user.id,
      amount,
      payment_type: paymentType,
      payment_method: paymentMethod,
      status: 'pending',
      payment_no: `pay_${Date.now()}${Math.random().toString(36).substr(2, 9)}`
    });
    
    // 调用第三方支付接口(微信支付、支付宝等)
    // const paymentData = await callPaymentGateway(paymentType, paymentMethod, amount, payment.payment_no);
    
    res.apiSuccess({
      paymentId: payment.id,
      paymentNo: payment.payment_no,
      amount: payment.amount,
      // ...paymentData // 第三方支付返回的数据
    }, '支付订单创建成功');
    
  } catch (error) {
    console.error('Create payment error:', error);
    res.apiError('创建支付订单失败');
  }
});

/**
 * @swagger
 * /api/payments/{id}/callback:
 *   post:
 *     summary: 支付回调接口
 *     tags: [支付管理]
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: 支付订单ID
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - paymentId
 *               - status
 *             properties:
 *               paymentId:
 *                 type: string
 *                 description: 第三方支付ID
 *                 example: "pay_123456"
 *               status:
 *                 type: string
 *                 enum: [success, failed, canceled]
 *                 description: 支付状态
 *                 example: "success"
 *               paidAmount:
 *                 type: number
 *                 format: float
 *                 description: 实际支付金额
 *                 example: 355000
 *               paidTime:
 *                 type: string
 *                 format: date-time
 *                 description: 支付时间
 *                 example: "2024-01-25 15:30:00"
 *     responses:
 *       200:
 *         description: 回调处理成功
 */
router.post('/:id/callback', async (req, res) => {
  try {
    const { id } = req.params;
    const { paymentId, status, paidAmount, paidTime } = req.body;
    
    const payment = await Payment.findByPk(id);
    if (!payment) {
      return res.apiError('支付订单不存在', 404);
    }
    
    // 更新支付状态
    await payment.update({
      third_party_id: paymentId,
      status: status === 'success' ? 'paid' : 'failed',
      paid_amount: paidAmount,
      paid_time: paidTime ? new Date(paidTime) : new Date()
    });
    
    // 如果支付成功,更新订单状态
    if (status === 'success') {
      const order = await Order.findByPk(payment.order_id);
      if (order) {
        await order.update({ status: 'paid' });
      }
    }
    
    res.apiSuccess(null, '支付回调处理成功');
    
  } catch (error) {
    console.error('Payment callback error:', error);
    res.apiError('支付回调处理失败');
  }
});

/**
 * @swagger
 * /api/payments/{id}/status:
 *   get:
 *     summary: 查询支付状态
 *     tags: [支付管理]
 *     security:
 *       - BearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: 支付订单ID
 *     responses:
 *       200:
 *         description: 查询成功
 */
router.get('/:id/status', async (req, res) => {
  try {
    const { id } = req.params;
    
    const payment = await Payment.findByPk(id);
    if (!payment) {
      return res.apiError('支付订单不存在', 404);
    }
    
    // 验证查询权限
    if (payment.user_id !== req.user.id) {
      return res.apiError('无权限查询此支付订单', 403);
    }
    
    res.apiSuccess({
      paymentId: payment.id,
      paymentNo: payment.payment_no,
      amount: payment.amount,
      status: payment.status,
      paidAmount: payment.paid_amount,
      paidTime: payment.paid_time,
      createdAt: payment.created_at
    });
    
  } catch (error) {
    console.error('Get payment status error:', error);
    res.apiError('查询支付状态失败');
  }
});

module.exports = router;

5. 统一交互设计与开发规范

5.1 Uni-app小程序开发规范

// 统一页面结构规范
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页",
        "enablePullDownRefresh": true,
        "backgroundColor": "#f5f5f5"
      }
    }
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "uni-app",
    "navigationBarBackgroundColor": "#FFFFFF",
    "backgroundColor": "#FFFFFF",
    "app-plus": {
      "background": "#efeff4"
    }
  },
  "tabBar": {
    "color": "#7A7E83",
    "selectedColor": "#007AFF",
    "borderStyle": "black",
    "backgroundColor": "#FFFFFF",
    "list": []
  }
}

// 统一组件命名规范
- 组件目录/components/
- 页面目录/pages/
- 工具函数/utils/
- API接口/api/
- 状态管理/store/
- 类型定义/types/

// 统一状态管理Vuex
const store = new Vuex.Store({
  state: {
    userInfo: null,
    token: null,
    currentOrder: null
  },
  mutations: {
    SET_USER_INFO(state, userInfo) {
      state.userInfo = userInfo;
    },
    SET_TOKEN(state, token) {
      state.token = token;
    }
  },
  actions: {
    async login({ commit }, { phone, code }) {
      const response = await uni.request({
        url: '/api/auth/mini-program/login',
        method: 'POST',
        data: { phone, code, miniProgramType: 'client' }
      });
      commit('SET_USER_INFO', response.data.data.userInfo);
      commit('SET_TOKEN', response.data.data.token);
      uni.setStorageSync('token', response.data.data.token);
    }
  }
});

5.2 统一UI组件库规范

<!-- 统一按钮组件 -->
<template>
  <button 
    :class="['btn', type, size, { disabled: disabled }]"
    :disabled="disabled"
    @click="handleClick"
  >
    <slot></slot>
  </button>
</template>

<script>
export default {
  name: 'UniButton',
  props: {
    type: {
      type: String,
      default: 'default', // primary/success/warning/danger
      validator: value => ['default', 'primary', 'success', 'warning', 'danger'].includes(value)
    },
    size: {
      type: String,
      default: 'medium', // small/medium/large
      validator: value => ['small', 'medium', 'large'].includes(value)
    },
    disabled: Boolean
  },
  methods: {
    handleClick(event) {
      if (!this.disabled) {
        this.$emit('click', event);
      }
    }
  }
};
</script>

<style scoped>
.btn {
  border: none;
  border-radius: 8rpx;
  padding: 16rpx 32rpx;
  font-size: 28rpx;
  transition: all 0.3s;
}

.btn.primary {
  background-color: #007AFF;
  color: #FFFFFF;
}

.btn.small {
  padding: 12rpx 24rpx;
  font-size: 24rpx;
}

.btn.disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
</style>

5.3 统一交互体验规范

// 统一加载状态管理
const useLoading = () => {
  const isLoading = ref(false);
  
  const showLoading = (title = '加载中') => {
    isLoading.value = true;
    uni.showLoading({ title, mask: true });
  };
  
  const hideLoading = () => {
    isLoading.value = false;
    uni.hideLoading();
  };
  
  return { isLoading, showLoading, hideLoading };
};

// 统一错误处理
const handleApiError = (error) => {
  console.error('API Error:', error);
  
  if (error.response?.status === 401) {
    // token过期跳转到登录页
    uni.showModal({
      title: '提示',
      content: '登录已过期,请重新登录',
      showCancel: false,
      success: () => {
        uni.navigateTo({ url: '/pages/login/login' });
      }
    });
  } else if (error.response?.status === 403) {
    uni.showToast({ title: '无权限访问', icon: 'none' });
  } else {
    uni.showToast({ 
      title: error.response?.data?.message || '网络异常,请重试', 
      icon: 'none' 
    });
  }
};

// 统一页面跳转
const navigateTo = (url, params = {}) => {
  if (params) {
    const query = Object.keys(params)
      .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
      .join('&');
    url += `?${query}`;
  }
  uni.navigateTo({ url });
};

6. 安全设计

6.1 统一认证授权

  • JWT Token认证 + Refresh Token机制
  • 基于角色的访问控制RBAC
  • API访问频率限制和防刷机制
  • 多因素认证支持(短信验证码)

6.2 数据安全与隐私保护

  • 敏感数据加密存储AES-256
  • HTTPS传输加密TLS 1.3
  • 视频文件访问权限控制和签名URL
  • 用户隐私数据脱敏处理
  • GDPR/个人信息保护法合规

6.3 业务安全防护

  • 订单状态机验证和防篡改
  • 支付金额校验和防重放攻击
  • 操作日志审计和追溯
  • 敏感操作二次确认机制
  • 业务数据完整性校验

6. 性能优化

6.1 数据库优化

  • 读写分离
  • 索引优化
  • 分表分库策略

6.2 缓存策略

  • Redis缓存热点数据
  • 本地缓存减少IO
  • 缓存失效策略

6.3 文件处理

  • 视频文件分片上传
  • CDN加速访问
  • 压缩和转码处理

7. 小程序矩阵详细实现

7.1 客户端小程序 (Client Mini-Program)

// 主要功能模块
const clientModules = {
  // 用户认证
  auth: {
    login: '用户登录注册',
    profile: '个人信息管理',
    certification: '企业认证'
  },
  
  // 采购管理
  procurement: {
    orderCreate: '创建采购订单',
    orderList: '订单列表查看',
    orderDetail: '订单详情',
    orderTracking: '订单跟踪'
  },
  
  // 支付结算
  payment: {
    prepayment: '预付款支付',
    balancePayment: '尾款支付',
    paymentRecords: '支付记录'
  },
  
  // 消息通知
  notification: {
    systemMsg: '系统消息',
    orderUpdates: '订单状态更新',
    paymentReminders: '付款提醒'
  }
};

// 技术特色
- 基于uni-app的跨端开发
- 集成微信支付SDK
- 实时订单状态推送
- 地图集成和位置服务

7.2 供应商小程序 (Supplier Mini-Program)

// 主要功能模块
const supplierModules = {
  // 订单管理
  order: {
    orderReceive: '接单管理',
    orderProcessing: '订单处理',
    orderStatus: '订单状态更新'
  },
  
  // 牛只管理
  cattle: {
    inventory: '牛只库存',
    qualityCheck: '质量检验',
    certificateUpload: '证件上传'
  },
  
  // 装车管理
  loading: {
    loadingPlan: '装车计划',
    loadingProcess: '装车过程记录',
    videoRecording: '装车视频录制'
  },
  
  // 结算管理
  settlement: {
    settlementQuery: '结算查询',
    invoiceManagement: '发票管理',
    paymentRecords: '收款记录'
  }
};

// 技术特色
- 视频录制和上传功能
- 证件扫描和OCR识别
- 库存管理系统集成
- 财务结算对接

7.3 司机小程序 (Driver Mini-Program)

// 主要功能模块
const driverModules = {
  // 运输任务
  transport: {
    taskReceive: '任务接收',
    taskList: '任务列表',
    taskDetail: '任务详情'
  },
  
  // 位置跟踪
  tracking: {
    autoTracking: '自动位置上报',
    manualReport: '手动状态报告',
    routePlanning: '路线规划'
  },
  
  // 牛只监控
  monitoring: {
    cattleStatus: '牛只状态监测',
    environment: '环境参数监测',
    emergency: '紧急情况处理'
  },
  
  // 单据管理
  documents: {
    deliveryNote: '交货单管理',
    receiptConfirmation: '回执确认',
    expenseReporting: '费用报销'
  }
};

// 技术特色
- 后台位置持续跟踪
- 离线数据同步机制
- 紧急求助功能
- 多媒体数据采集

7.4 内部员工小程序 (Staff Mini-Program)

// 主要功能模块
const staffModules = {
  // 运营监控
  operation: {
    dashboard: '运营看板',
    orderMonitor: '订单监控',
    exceptionHandling: '异常处理'
  },
  
  // 客户服务
  customerService: {
    customerInfo: '客户信息',
    serviceRecords: '服务记录',
    complaintHandling: '投诉处理'
  },
  
  // 数据统计
  statistics: {
    businessData: '业务数据',
    financialReports: '财务报表',
    performanceAnalysis: '绩效分析'
  },
  
  // 系统管理
  system: {
    userManagement: '用户管理',
    rolePermission: '权限管理',
    systemSettings: '系统设置'
  }
};

// 技术特色
- 管理后台功能移动化
- 实时数据可视化
- 移动审批流程
- 多维度数据分析

8. 监控告警

8.1 系统监控

  • 应用性能监控(APM):监控各微服务性能指标
  • 数据库监控MySQL和Redis性能监控
  • 服务器资源监控CPU、内存、磁盘、网络
  • 小程序性能监控加载时间、渲染性能、API响应

8.2 业务监控

  • 订单流程监控:各状态订单数量和耗时
  • 运输异常检测:偏离路线、长时间停留预警
  • 支付成功率监控:各渠道支付成功率和失败原因
  • 用户行为分析:各小程序用户活跃度和功能使用情况

8.3 日志管理

  • 操作日志记录:用户关键操作审计日志
  • 错误日志收集:系统异常和错误信息收集
  • 日志分析和查询ELK日志分析平台
  • 实时日志追踪:分布式请求追踪

9. 部署方案

9.1 开发环境

# docker-compose-dev.yml
version: '3.8'
services:
  # 后端服务
  user-service:
    build: ./services/user-service
    ports:
      - "3001:3000"
    environment:
      - NODE_ENV=development
      - DB_HOST=mysql
      - REDIS_HOST=redis
  
  # 数据库
  mysql:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=niu_mall
    ports:
      - "3306:3306"
  
  # 缓存
  redis:
    image: redis:6.2
    ports:
      - "6379:6379"

  # 小程序开发环境
  mini-program-dev:
    image: node:16
    working_dir: /app
    volumes:
      - ./mini_program:/app
    ports:
      - "8080:8080"
    command: npm run dev:mp-weixin

9.2 生产环境

# kubernetes部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: niu-mall-api
  labels:
    app: niu-mall
spec:
  replicas: 3
  selector:
    matchLabels:
      app: niu-mall-api
  template:
    metadata:
      labels:
        app: niu-mall-api
    spec:
      containers:
      - name: api-gateway
        image: niu-mall/api-gateway:latest
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: production
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"

# 小程序生产部署
- 微信小程序平台审核发布
- 阿里云OSS静态资源托管
- CDN加速和域名配置
- SSL证书和HTTPS强制

9.3 备份恢复策略

# 数据库备份
backup:
  schedule: "0 2 * * *"  # 每天凌晨2点
  retention: 30          # 保留30天
  storage: 
    type: oss            # 阿里云OSS存储
    bucket: niu-mall-backup

# 文件备份
file_backup:
  enabled: true
  schedule: "0 3 * * *"  # 每天凌晨3点
  include:
    - /data/uploads     # 用户上传文件
    - /data/logs        # 日志文件
  exclude:
    - /data/temp        # 临时文件

# 灾难恢复
recovery:
  rto: "4h"             # 恢复时间目标4小时
  rpo: "1h"             # 恢复点目标1小时
  procedures:
    - database_restore
    - service_restart
    - data_validation

10. 开发规范

10.1 代码规范

// ESLint配置
module.exports = {
  env: {
    node: true,
    browser: true,
    es2021: true
  },
  extends: [
    'eslint:recommended',
    '@vue/typescript/recommended',
    'prettier'
  ],
  rules: {
    '@typescript-eslint/no-explicit-any': 'warn',
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
  }
};

// Prettier配置
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false
}

10.2 Git工作流

# 功能开发流程
git checkout -b feature/order-management
git add .
git commit -m "feat: 添加订单管理功能"
git push origin feature/order-management

# 代码审查
- 至少需要2个reviewer批准
- 所有测试必须通过
- 代码覆盖率要求85%以上

# 发布流程
- 开发 → 测试 → 预发布 → 生产
- 蓝绿部署或金丝雀发布
- 自动化CI/CD流水线

10.3 测试规范

// 单元测试示例
describe('OrderService', () => {
  it('should create order successfully', async () => {
    const orderData = {
      breedType: 'simmental',
      minWeight: 500,
      maxWeight: 600,
      totalCount: 100,
      unitPrice: 35.5
    };
    
    const result = await orderService.createOrder(orderData);
    expect(result).toHaveProperty('id');
    expect(result.status).toBe('pending');
  });

  it('should validate order data', async () => {
    const invalidData = { totalCount: -1 };
    await expect(orderService.createOrder(invalidData))
      .rejects
      .toThrow('数量必须大于0');
  });
});

// 测试覆盖率要求
- 单元测试: 80%
- 集成测试: 70% 
- E2E测试: 核心业务流程100%覆盖
  • 灾难恢复预案

9. 开发规范

9.1 代码规范

  • TypeScript严格模式
  • ESLint代码检查
  • Prettier代码格式化

9.2 Git规范

  • 分支管理策略
  • Commit message规范
  • Code Review流程

9.3 文档规范

  • API文档自动化
  • 数据库文档维护
  • 部署文档更新