# 活牛采购智能数字化系统 - 技术实施方案 ## 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 小程序服务接口 ```typescript // 统一小程序API接口设计 export interface MiniProgramApi { // 用户认证接口 login(phone: string, code: string): Promise; logout(): Promise; // 订单相关接口 createOrder(orderData: OrderCreateDto): Promise; getOrderList(params: OrderQueryDto): Promise; getOrderDetail(orderId: string): Promise; // 运输跟踪接口 reportLocation(location: LocationDto): Promise; getTransportTrack(orderId: string): Promise; // 文件上传接口 uploadFile(file: File, type: FileType): Promise; // 消息通知接口 getNotifications(): Promise; markAsRead(notificationId: string): Promise; } ``` ## 3. 数据库设计 ### 3.1 Sequelize ORM 模型定义 ```javascript // 统一用户模型 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 认证路由示例 ```javascript // 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 订单路由示例 ```javascript // 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 运输跟踪路由示例 ```javascript // 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 文件上传路由示例 ```javascript // 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 支付路由示例 ```javascript // 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小程序开发规范 ```javascript // 统一页面结构规范 { "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组件库规范 ```vue ``` ### 5.3 统一交互体验规范 ```javascript // 统一加载状态管理 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) ```javascript // 主要功能模块 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) ```javascript // 主要功能模块 const supplierModules = { // 订单管理 order: { orderReceive: '接单管理', orderProcessing: '订单处理', orderStatus: '订单状态更新' }, // 牛只管理 cattle: { inventory: '牛只库存', qualityCheck: '质量检验', certificateUpload: '证件上传' }, // 装车管理 loading: { loadingPlan: '装车计划', loadingProcess: '装车过程记录', videoRecording: '装车视频录制' }, // 结算管理 settlement: { settlementQuery: '结算查询', invoiceManagement: '发票管理', paymentRecords: '收款记录' } }; // 技术特色 - 视频录制和上传功能 - 证件扫描和OCR识别 - 库存管理系统集成 - 财务结算对接 ``` ### 7.3 司机小程序 (Driver Mini-Program) ```javascript // 主要功能模块 const driverModules = { // 运输任务 transport: { taskReceive: '任务接收', taskList: '任务列表', taskDetail: '任务详情' }, // 位置跟踪 tracking: { autoTracking: '自动位置上报', manualReport: '手动状态报告', routePlanning: '路线规划' }, // 牛只监控 monitoring: { cattleStatus: '牛只状态监测', environment: '环境参数监测', emergency: '紧急情况处理' }, // 单据管理 documents: { deliveryNote: '交货单管理', receiptConfirmation: '回执确认', expenseReporting: '费用报销' } }; // 技术特色 - 后台位置持续跟踪 - 离线数据同步机制 - 紧急求助功能 - 多媒体数据采集 ``` ### 7.4 内部员工小程序 (Staff Mini-Program) ```javascript // 主要功能模块 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 开发环境 ```yaml # 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 生产环境 ```yaml # 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 备份恢复策略 ```yaml # 数据库备份 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 代码规范 ```javascript // 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工作流 ```bash # 功能开发流程 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 测试规范 ```javascript // 单元测试示例 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文档自动化 - 数据库文档维护 - 部署文档更新