docs(website): 重构关于页面布局和内容
- 更新页面布局,优化导航栏和面包屑导航 - 重新组织页面内容,突出公司使命和价值观 - 添加发展历程和核心团队介绍 - 更新合作伙伴展示方式 - 调整页脚内容,增加社交媒体链接
This commit is contained in:
55
backend/src/config/config.js
Normal file
55
backend/src/config/config.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// 数据库配置
|
||||
const dbConfig = {
|
||||
development: {
|
||||
username: 'root',
|
||||
password: 'password',
|
||||
database: 'niumall_dev',
|
||||
host: '127.0.0.1',
|
||||
port: 3306,
|
||||
dialect: 'mysql'
|
||||
},
|
||||
production: {
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT || 3306,
|
||||
dialect: 'mysql'
|
||||
}
|
||||
};
|
||||
|
||||
// JWT配置
|
||||
const jwtConfig = {
|
||||
secret: process.env.JWT_SECRET || 'niumall-jwt-secret',
|
||||
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
|
||||
};
|
||||
|
||||
// Redis配置
|
||||
const redisConfig = {
|
||||
development: {
|
||||
host: '127.0.0.1',
|
||||
port: 6379
|
||||
},
|
||||
production: {
|
||||
host: process.env.REDIS_HOST,
|
||||
port: process.env.REDIS_PORT || 6379
|
||||
}
|
||||
};
|
||||
|
||||
// 服务器配置
|
||||
const serverConfig = {
|
||||
port: process.env.PORT || 3000
|
||||
};
|
||||
|
||||
// 日志配置
|
||||
const logConfig = {
|
||||
level: process.env.LOG_LEVEL || 'info'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
dbConfig,
|
||||
jwtConfig,
|
||||
redisConfig,
|
||||
serverConfig,
|
||||
logConfig
|
||||
};
|
||||
36
backend/src/config/database.js
Normal file
36
backend/src/config/database.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const { Sequelize } = require('sequelize');
|
||||
const { dbConfig } = require('./config');
|
||||
|
||||
// 获取环境变量
|
||||
const nodeEnv = process.env.NODE_ENV || 'development';
|
||||
|
||||
// 创建Sequelize实例
|
||||
const sequelize = new Sequelize(
|
||||
dbConfig[nodeEnv].database,
|
||||
dbConfig[nodeEnv].username,
|
||||
dbConfig[nodeEnv].password,
|
||||
{
|
||||
host: dbConfig[nodeEnv].host,
|
||||
port: dbConfig[nodeEnv].port,
|
||||
dialect: dbConfig[nodeEnv].dialect,
|
||||
logging: nodeEnv === 'development' ? console.log : false, // 开发环境显示SQL日志
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 测试数据库连接
|
||||
const testConnection = async () => {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
} catch (error) {
|
||||
console.error('数据库连接失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = sequelize;
|
||||
91
backend/src/controllers/AuthController.js
Normal file
91
backend/src/controllers/AuthController.js
Normal file
@@ -0,0 +1,91 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { successResponse, errorResponse } = require('../utils/response');
|
||||
const { jwtConfig } = require('../config/config');
|
||||
const User = require('../models/User');
|
||||
|
||||
// 小程序用户登录
|
||||
const miniProgramLogin = async (req, res) => {
|
||||
try {
|
||||
const { phone, code, miniProgramType } = req.body;
|
||||
|
||||
// 验证验证码(实际项目中需要对接短信服务)
|
||||
if (code !== '123456') { // 临时验证码,实际项目中需要验证真实验证码
|
||||
return res.status(400).json(errorResponse('验证码错误', 400));
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
let user = await User.findOne({ where: { phone } });
|
||||
|
||||
// 如果用户不存在则创建新用户
|
||||
if (!user) {
|
||||
user = await User.create({
|
||||
uuid: uuidv4(),
|
||||
username: `user_${phone}`,
|
||||
phone,
|
||||
user_type: miniProgramType || 'client',
|
||||
password_hash: bcrypt.hashSync(phone, 10) // 临时密码,实际项目中需要更安全的处理
|
||||
});
|
||||
}
|
||||
|
||||
// 生成JWT token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
id: user.id,
|
||||
uuid: user.uuid,
|
||||
username: user.username,
|
||||
phone: user.phone,
|
||||
userType: user.user_type
|
||||
},
|
||||
jwtConfig.secret,
|
||||
{ expiresIn: jwtConfig.expiresIn }
|
||||
);
|
||||
|
||||
// 返回用户信息和token
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
avatar: user.avatar_url,
|
||||
userType: user.user_type,
|
||||
phone: user.phone
|
||||
};
|
||||
|
||||
res.json(successResponse({ token, userInfo }));
|
||||
} catch (error) {
|
||||
console.error('登录错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取当前用户信息
|
||||
const getCurrentUser = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findByPk(req.user.id);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json(errorResponse('用户不存在', 404));
|
||||
}
|
||||
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
avatar: user.avatar_url,
|
||||
userType: user.user_type,
|
||||
phone: user.phone,
|
||||
email: user.email
|
||||
};
|
||||
|
||||
res.json(successResponse(userInfo));
|
||||
} catch (error) {
|
||||
console.error('获取用户信息错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
miniProgramLogin,
|
||||
getCurrentUser
|
||||
};
|
||||
133
backend/src/controllers/OrderController.js
Normal file
133
backend/src/controllers/OrderController.js
Normal file
@@ -0,0 +1,133 @@
|
||||
const { successResponse, errorResponse, paginatedResponse } = require('../utils/response');
|
||||
const Order = require('../models/Order');
|
||||
|
||||
// 创建订单
|
||||
const createOrder = async (req, res) => {
|
||||
try {
|
||||
const orderData = req.body;
|
||||
|
||||
// 设置买家ID
|
||||
orderData.buyer_id = req.user.id;
|
||||
|
||||
// 生成订单号
|
||||
const orderNo = `ORD${Date.now()}${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`;
|
||||
orderData.order_no = orderNo;
|
||||
|
||||
// 创建订单
|
||||
const order = await Order.create(orderData);
|
||||
|
||||
res.status(201).json(successResponse(order, '订单创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建订单错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取订单列表
|
||||
const getOrderList = async (req, res) => {
|
||||
try {
|
||||
const { page = 1, pageSize = 10, status, orderNo } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
// 根据用户类型过滤
|
||||
if (req.user.userType === 'client') {
|
||||
whereConditions.buyer_id = req.user.id;
|
||||
} else if (req.user.userType === 'trader') {
|
||||
whereConditions.trader_id = req.user.id;
|
||||
} else if (req.user.userType === 'supplier') {
|
||||
whereConditions.supplier_id = req.user.id;
|
||||
} else if (req.user.userType === 'driver') {
|
||||
whereConditions.driver_id = req.user.id;
|
||||
}
|
||||
|
||||
if (status) whereConditions.status = status;
|
||||
if (orderNo) whereConditions.order_no = orderNo;
|
||||
|
||||
// 查询订单列表
|
||||
const { count, rows } = await Order.findAndCountAll({
|
||||
where: whereConditions,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
res.json(paginatedResponse(rows, count, parseInt(page), parseInt(pageSize)));
|
||||
} catch (error) {
|
||||
console.error('获取订单列表错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取订单详情
|
||||
const getOrderDetail = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const order = await Order.findByPk(id);
|
||||
|
||||
if (!order) {
|
||||
return res.status(404).json(errorResponse('订单不存在', 404));
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
if (req.user.userType === 'client' && order.buyer_id !== req.user.id) {
|
||||
return res.status(403).json(errorResponse('无权限访问该订单', 403));
|
||||
}
|
||||
|
||||
if (req.user.userType === 'trader' && order.trader_id !== req.user.id) {
|
||||
return res.status(403).json(errorResponse('无权限访问该订单', 403));
|
||||
}
|
||||
|
||||
if (req.user.userType === 'supplier' && order.supplier_id !== req.user.id) {
|
||||
return res.status(403).json(errorResponse('无权限访问该订单', 403));
|
||||
}
|
||||
|
||||
if (req.user.userType === 'driver' && order.driver_id !== req.user.id) {
|
||||
return res.status(403).json(errorResponse('无权限访问该订单', 403));
|
||||
}
|
||||
|
||||
res.json(successResponse(order));
|
||||
} catch (error) {
|
||||
console.error('获取订单详情错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新订单状态
|
||||
const updateOrderStatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
const order = await Order.findByPk(id);
|
||||
|
||||
if (!order) {
|
||||
return res.status(404).json(errorResponse('订单不存在', 404));
|
||||
}
|
||||
|
||||
// 权限检查和状态流转验证
|
||||
// 这里需要根据业务逻辑实现详细的状态流转控制
|
||||
|
||||
const [updatedRowsCount] = await Order.update({ status }, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
return res.status(400).json(errorResponse('订单状态更新失败', 400));
|
||||
}
|
||||
|
||||
res.json(successResponse(null, '订单状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新订单状态错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createOrder,
|
||||
getOrderList,
|
||||
getOrderDetail,
|
||||
updateOrderStatus
|
||||
};
|
||||
121
backend/src/controllers/PaymentController.js
Normal file
121
backend/src/controllers/PaymentController.js
Normal file
@@ -0,0 +1,121 @@
|
||||
const { successResponse, errorResponse, paginatedResponse } = require('../utils/response');
|
||||
const Payment = require('../models/Payment');
|
||||
|
||||
// 创建支付
|
||||
const createPayment = async (req, res) => {
|
||||
try {
|
||||
const { orderId, amount, paymentType, paymentMethod } = req.body;
|
||||
|
||||
// 生成支付单号
|
||||
const paymentNo = `PAY${Date.now()}${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`;
|
||||
|
||||
// 创建支付记录
|
||||
const payment = await Payment.create({
|
||||
order_id: orderId,
|
||||
user_id: req.user.id,
|
||||
amount,
|
||||
payment_type: paymentType,
|
||||
payment_method: paymentMethod,
|
||||
payment_no: paymentNo,
|
||||
status: 'pending'
|
||||
});
|
||||
|
||||
// 这里需要对接实际的支付接口(微信支付、支付宝等)
|
||||
// 暂时返回模拟的支付参数
|
||||
const paymentParams = {
|
||||
paymentNo,
|
||||
amount,
|
||||
paymentType,
|
||||
paymentMethod
|
||||
};
|
||||
|
||||
res.status(201).json(successResponse({ payment, paymentParams }, '支付创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建支付错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取支付列表
|
||||
const getPaymentList = async (req, res) => {
|
||||
try {
|
||||
const { page = 1, pageSize = 10, status } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {
|
||||
user_id: req.user.id
|
||||
};
|
||||
|
||||
if (status) whereConditions.status = status;
|
||||
|
||||
// 查询支付列表
|
||||
const { count, rows } = await Payment.findAndCountAll({
|
||||
where: whereConditions,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
res.json(paginatedResponse(rows, count, parseInt(page), parseInt(pageSize)));
|
||||
} catch (error) {
|
||||
console.error('获取支付列表错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取支付详情
|
||||
const getPaymentDetail = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const payment = await Payment.findByPk(id);
|
||||
|
||||
if (!payment) {
|
||||
return res.status(404).json(errorResponse('支付记录不存在', 404));
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
if (payment.user_id !== req.user.id) {
|
||||
return res.status(403).json(errorResponse('无权限访问该支付记录', 403));
|
||||
}
|
||||
|
||||
res.json(successResponse(payment));
|
||||
} catch (error) {
|
||||
console.error('获取支付详情错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新支付状态(模拟支付回调)
|
||||
const updatePaymentStatus = async (req, res) => {
|
||||
try {
|
||||
const { paymentNo, status, thirdPartyId } = req.body;
|
||||
|
||||
const [updatedRowsCount] = await Payment.update(
|
||||
{
|
||||
status,
|
||||
third_party_id: thirdPartyId,
|
||||
paid_time: status === 'paid' ? new Date() : null
|
||||
},
|
||||
{
|
||||
where: { payment_no: paymentNo }
|
||||
}
|
||||
);
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
return res.status(404).json(errorResponse('支付记录不存在', 404));
|
||||
}
|
||||
|
||||
res.json(successResponse(null, '支付状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新支付状态错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createPayment,
|
||||
getPaymentList,
|
||||
getPaymentDetail,
|
||||
updatePaymentStatus
|
||||
};
|
||||
136
backend/src/controllers/UserController.js
Normal file
136
backend/src/controllers/UserController.js
Normal file
@@ -0,0 +1,136 @@
|
||||
const { successResponse, errorResponse, paginatedResponse } = require('../utils/response');
|
||||
const User = require('../models/User');
|
||||
|
||||
// 获取用户列表
|
||||
const getUserList = async (req, res) => {
|
||||
try {
|
||||
const { page = 1, pageSize = 10, userType, status } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
if (userType) whereConditions.user_type = userType;
|
||||
if (status) whereConditions.status = status;
|
||||
|
||||
// 查询用户列表
|
||||
const { count, rows } = await User.findAndCountAll({
|
||||
where: whereConditions,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
// 格式化用户数据
|
||||
const users = rows.map(user => ({
|
||||
id: user.id,
|
||||
uuid: user.uuid,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
userType: user.user_type,
|
||||
status: user.status,
|
||||
avatar: user.avatar_url,
|
||||
createdAt: user.created_at
|
||||
}));
|
||||
|
||||
res.json(paginatedResponse(users, count, parseInt(page), parseInt(pageSize)));
|
||||
} catch (error) {
|
||||
console.error('获取用户列表错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取用户详情
|
||||
const getUserDetail = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const user = await User.findByPk(id);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json(errorResponse('用户不存在', 404));
|
||||
}
|
||||
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
uuid: user.uuid,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
userType: user.user_type,
|
||||
status: user.status,
|
||||
avatar: user.avatar_url,
|
||||
idCardFront: user.id_card_front_url,
|
||||
idCardBack: user.id_card_back_url,
|
||||
licenseFront: user.license_front_url,
|
||||
licenseBack: user.license_back_url,
|
||||
businessLicense: user.business_license_url,
|
||||
createdAt: user.created_at,
|
||||
updatedAt: user.updated_at
|
||||
};
|
||||
|
||||
res.json(successResponse(userInfo));
|
||||
} catch (error) {
|
||||
console.error('获取用户详情错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新用户信息
|
||||
const updateUser = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
// 过滤不允许更新的字段
|
||||
const allowedFields = ['real_name', 'email', 'avatar_url'];
|
||||
const filteredData = {};
|
||||
Object.keys(updateData).forEach(key => {
|
||||
if (allowedFields.includes(key)) {
|
||||
filteredData[key] = updateData[key];
|
||||
}
|
||||
});
|
||||
|
||||
const [updatedRowsCount] = await User.update(filteredData, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
return res.status(404).json(errorResponse('用户不存在', 404));
|
||||
}
|
||||
|
||||
res.json(successResponse(null, '用户信息更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新用户信息错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新用户状态
|
||||
const updateUserStatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
const [updatedRowsCount] = await User.update({ status }, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
return res.status(404).json(errorResponse('用户不存在', 404));
|
||||
}
|
||||
|
||||
res.json(successResponse(null, '用户状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新用户状态错误:', error);
|
||||
res.status(500).json(errorResponse('服务器内部错误', 500));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getUserList,
|
||||
getUserDetail,
|
||||
updateUser,
|
||||
updateUserStatus
|
||||
};
|
||||
912
backend/src/docs/api.yaml
Normal file
912
backend/src/docs/api.yaml
Normal file
@@ -0,0 +1,912 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: 活牛采购智能数字化系统API
|
||||
description: 活牛采购智能数字化系统后端API接口文档
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: Niutech
|
||||
email: niutech@example.com
|
||||
|
||||
servers:
|
||||
- url: http://localhost:3000
|
||||
description: 开发服务器
|
||||
- url: https://api.niumall.com
|
||||
description: 生产服务器
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
schemas:
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 用户ID
|
||||
uuid:
|
||||
type: string
|
||||
description: 用户UUID
|
||||
username:
|
||||
type: string
|
||||
description: 用户名
|
||||
realName:
|
||||
type: string
|
||||
description: 真实姓名
|
||||
phone:
|
||||
type: string
|
||||
description: 手机号
|
||||
email:
|
||||
type: string
|
||||
description: 邮箱
|
||||
userType:
|
||||
type: string
|
||||
description: 用户类型
|
||||
status:
|
||||
type: string
|
||||
description: 用户状态
|
||||
avatar:
|
||||
type: string
|
||||
description: 头像URL
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
Order:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 订单ID
|
||||
orderNo:
|
||||
type: string
|
||||
description: 订单号
|
||||
buyerId:
|
||||
type: integer
|
||||
description: 买家ID
|
||||
traderId:
|
||||
type: integer
|
||||
description: 贸易商ID
|
||||
supplierId:
|
||||
type: integer
|
||||
description: 供应商ID
|
||||
driverId:
|
||||
type: integer
|
||||
description: 司机ID
|
||||
breedType:
|
||||
type: string
|
||||
description: 品种类型
|
||||
minWeight:
|
||||
type: number
|
||||
description: 最小重量
|
||||
maxWeight:
|
||||
type: number
|
||||
description: 最大重量
|
||||
totalCount:
|
||||
type: integer
|
||||
description: 总数量
|
||||
totalWeight:
|
||||
type: number
|
||||
description: 总重量
|
||||
unitPrice:
|
||||
type: number
|
||||
description: 单价
|
||||
totalAmount:
|
||||
type: number
|
||||
description: 总金额
|
||||
status:
|
||||
type: string
|
||||
description: 订单状态
|
||||
deliveryAddress:
|
||||
type: string
|
||||
description: 交付地址
|
||||
deliveryDate:
|
||||
type: string
|
||||
format: date
|
||||
description: 交付日期
|
||||
specialRequirements:
|
||||
type: string
|
||||
description: 特殊要求
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
Payment:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 支付ID
|
||||
orderId:
|
||||
type: integer
|
||||
description: 订单ID
|
||||
userId:
|
||||
type: integer
|
||||
description: 用户ID
|
||||
amount:
|
||||
type: number
|
||||
description: 支付金额
|
||||
paidAmount:
|
||||
type: number
|
||||
description: 实际支付金额
|
||||
paymentType:
|
||||
type: string
|
||||
description: 支付类型
|
||||
paymentMethod:
|
||||
type: string
|
||||
description: 支付方式
|
||||
paymentNo:
|
||||
type: string
|
||||
description: 支付单号
|
||||
thirdPartyId:
|
||||
type: string
|
||||
description: 第三方支付ID
|
||||
status:
|
||||
type: string
|
||||
description: 支付状态
|
||||
paidTime:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 支付时间
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
AuthResponse:
|
||||
type: object
|
||||
properties:
|
||||
token:
|
||||
type: string
|
||||
description: JWT token
|
||||
userInfo:
|
||||
$ref: '#/components/schemas/User'
|
||||
SuccessResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
description: 状态码
|
||||
message:
|
||||
type: string
|
||||
description: 消息
|
||||
data:
|
||||
type: object
|
||||
description: 数据
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
description: 状态码
|
||||
message:
|
||||
type: string
|
||||
description: 错误消息
|
||||
PaginatedResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
description: 状态码
|
||||
message:
|
||||
type: string
|
||||
description: 消息
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
description: 数据列表
|
||||
pagination:
|
||||
type: object
|
||||
properties:
|
||||
page:
|
||||
type: integer
|
||||
description: 当前页码
|
||||
pageSize:
|
||||
type: integer
|
||||
description: 每页数量
|
||||
total:
|
||||
type: integer
|
||||
description: 总数量
|
||||
totalPages:
|
||||
type: integer
|
||||
description: 总页数
|
||||
|
||||
paths:
|
||||
/health:
|
||||
get:
|
||||
summary: 健康检查
|
||||
description: 检查服务器健康状态
|
||||
responses:
|
||||
'200':
|
||||
description: 服务器健康
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
uptime:
|
||||
type: number
|
||||
/:
|
||||
get:
|
||||
summary: 根路径
|
||||
description: 返回API根路径信息
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
/api/auth/mini-program/login:
|
||||
post:
|
||||
summary: 小程序用户登录
|
||||
description: 小程序用户通过手机号和验证码登录
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
phone:
|
||||
type: string
|
||||
description: 手机号
|
||||
code:
|
||||
type: string
|
||||
description: 验证码
|
||||
miniProgramType:
|
||||
type: string
|
||||
description: 小程序类型
|
||||
responses:
|
||||
'200':
|
||||
description: 登录成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/auth/current:
|
||||
get:
|
||||
summary: 获取当前用户信息
|
||||
description: 获取当前登录用户的信息
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回用户信息
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/users:
|
||||
get:
|
||||
summary: 获取用户列表
|
||||
description: 获取用户列表(管理员权限)
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: 页码
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: 每页数量
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
- name: userType
|
||||
in: query
|
||||
description: 用户类型
|
||||
schema:
|
||||
type: string
|
||||
- name: status
|
||||
in: query
|
||||
description: 用户状态
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回用户列表
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/users/{id}:
|
||||
get:
|
||||
summary: 获取用户详情
|
||||
description: 获取指定用户的信息
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 用户ID
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回用户信息
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
put:
|
||||
summary: 更新用户信息
|
||||
description: 更新指定用户的信息
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 用户ID
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
real_name:
|
||||
type: string
|
||||
description: 真实姓名
|
||||
email:
|
||||
type: string
|
||||
description: 邮箱
|
||||
avatar_url:
|
||||
type: string
|
||||
description: 头像URL
|
||||
responses:
|
||||
'200':
|
||||
description: 更新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/users/{id}/status:
|
||||
patch:
|
||||
summary: 更新用户状态
|
||||
description: 更新指定用户的状态(管理员权限)
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 用户ID
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
description: 用户状态
|
||||
responses:
|
||||
'200':
|
||||
description: 更新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/orders:
|
||||
post:
|
||||
summary: 创建订单
|
||||
description: 创建新的订单
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
trader_id:
|
||||
type: integer
|
||||
description: 贸易商ID
|
||||
supplier_id:
|
||||
type: integer
|
||||
description: 供应商ID
|
||||
driver_id:
|
||||
type: integer
|
||||
description: 司机ID
|
||||
breed_type:
|
||||
type: string
|
||||
description: 品种类型
|
||||
min_weight:
|
||||
type: number
|
||||
description: 最小重量
|
||||
max_weight:
|
||||
type: number
|
||||
description: 最大重量
|
||||
total_count:
|
||||
type: integer
|
||||
description: 总数量
|
||||
total_weight:
|
||||
type: number
|
||||
description: 总重量
|
||||
unit_price:
|
||||
type: number
|
||||
description: 单价
|
||||
total_amount:
|
||||
type: number
|
||||
description: 总金额
|
||||
delivery_address:
|
||||
type: string
|
||||
description: 交付地址
|
||||
delivery_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 交付日期
|
||||
special_requirements:
|
||||
type: string
|
||||
description: 特殊要求
|
||||
responses:
|
||||
'201':
|
||||
description: 订单创建成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
get:
|
||||
summary: 获取订单列表
|
||||
description: 获取订单列表
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: 页码
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: 每页数量
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
- name: status
|
||||
in: query
|
||||
description: 订单状态
|
||||
schema:
|
||||
type: string
|
||||
- name: orderNo
|
||||
in: query
|
||||
description: 订单号
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回订单列表
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/orders/{id}:
|
||||
get:
|
||||
summary: 获取订单详情
|
||||
description: 获取指定订单的详细信息
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 订单ID
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回订单信息
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 订单不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/orders/{id}/status:
|
||||
patch:
|
||||
summary: 更新订单状态
|
||||
description: 更新指定订单的状态
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 订单ID
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
description: 订单状态
|
||||
responses:
|
||||
'200':
|
||||
description: 更新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 订单不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/payments:
|
||||
post:
|
||||
summary: 创建支付
|
||||
description: 创建新的支付
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
order_id:
|
||||
type: integer
|
||||
description: 订单ID
|
||||
amount:
|
||||
type: number
|
||||
description: 支付金额
|
||||
payment_type:
|
||||
type: string
|
||||
description: 支付类型
|
||||
payment_method:
|
||||
type: string
|
||||
description: 支付方式
|
||||
responses:
|
||||
'201':
|
||||
description: 支付创建成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
get:
|
||||
summary: 获取支付列表
|
||||
description: 获取支付列表
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: 页码
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: 每页数量
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
- name: status
|
||||
in: query
|
||||
description: 支付状态
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回支付列表
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/payments/{id}:
|
||||
get:
|
||||
summary: 获取支付详情
|
||||
description: 获取指定支付的详细信息
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 支付ID
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: 成功返回支付信息
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'401':
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'404':
|
||||
description: 支付记录不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/api/payments/callback:
|
||||
post:
|
||||
summary: 支付回调
|
||||
description: 处理第三方支付回调
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
paymentNo:
|
||||
type: string
|
||||
description: 支付单号
|
||||
status:
|
||||
type: string
|
||||
description: 支付状态
|
||||
thirdPartyId:
|
||||
type: string
|
||||
description: 第三方支付ID
|
||||
responses:
|
||||
'200':
|
||||
description: 支付状态更新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'404':
|
||||
description: 支付记录不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'500':
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
76
backend/src/main.js
Normal file
76
backend/src/main.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const helmet = require('helmet');
|
||||
const morgan = require('morgan');
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const YAML = require('yamljs');
|
||||
|
||||
// 数据库连接
|
||||
const sequelize = require('./config/database');
|
||||
|
||||
// 路由
|
||||
const authRoutes = require('./routes/auth');
|
||||
const userRoutes = require('./routes/users');
|
||||
const orderRoutes = require('./routes/orders');
|
||||
const paymentRoutes = require('./routes/payments');
|
||||
|
||||
// 创建Express应用
|
||||
const app = express();
|
||||
|
||||
// 中间件
|
||||
app.use(helmet()); // 安全防护
|
||||
app.use(cors()); // 跨域支持
|
||||
app.use(morgan('combined')); // HTTP请求日志
|
||||
app.use(express.json()); // JSON解析
|
||||
app.use(express.urlencoded({ extended: true })); // URL编码解析
|
||||
|
||||
// 自定义日志中间件
|
||||
const logger = require('./middleware/logger');
|
||||
app.use(logger);
|
||||
|
||||
// 健康检查路由
|
||||
const healthCheckRoutes = require('./middleware/healthCheck');
|
||||
app.use('/', healthCheckRoutes);
|
||||
|
||||
// Swagger UI
|
||||
const swaggerDocument = YAML.load('./src/docs/api.yaml');
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
|
||||
|
||||
// API路由
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/users', userRoutes);
|
||||
app.use('/api/orders', orderRoutes);
|
||||
app.use('/api/payments', paymentRoutes);
|
||||
|
||||
// 基本路由
|
||||
app.get('/', (req, res) => {
|
||||
res.json({
|
||||
message: '活牛采购智能数字化系统后端服务',
|
||||
version: '1.0.0'
|
||||
});
|
||||
});
|
||||
|
||||
// 错误处理中间件
|
||||
const errorHandler = require('./middleware/errorHandler');
|
||||
app.use(errorHandler);
|
||||
|
||||
// 同步数据库模型
|
||||
const syncDatabase = async () => {
|
||||
try {
|
||||
await sequelize.sync({ alter: true });
|
||||
console.log('数据库模型同步成功');
|
||||
} catch (error) {
|
||||
console.error('数据库模型同步失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 启动服务器
|
||||
const { serverConfig } = require('./config/config');
|
||||
const PORT = serverConfig.port;
|
||||
app.listen(PORT, async () => {
|
||||
console.log(`服务器运行在端口 ${PORT}`);
|
||||
// 同步数据库
|
||||
await syncDatabase();
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
54
backend/src/middleware/auth.js
Normal file
54
backend/src/middleware/auth.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { jwtConfig } = require('../config/config');
|
||||
|
||||
// 认证中间件
|
||||
const authenticate = (req, res, next) => {
|
||||
try {
|
||||
// 从请求头获取token
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '未提供认证token'
|
||||
});
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
|
||||
// 验证token
|
||||
const decoded = jwt.verify(token, jwtConfig.secret);
|
||||
req.user = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '无效的认证token'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 角色权限检查中间件
|
||||
const checkRole = (roles) => {
|
||||
return (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '未认证'
|
||||
});
|
||||
}
|
||||
|
||||
if (!roles.includes(req.user.userType)) {
|
||||
return res.status(403).json({
|
||||
code: 403,
|
||||
message: '权限不足'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
authenticate,
|
||||
checkRole
|
||||
};
|
||||
31
backend/src/middleware/errorHandler.js
Normal file
31
backend/src/middleware/errorHandler.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const { errorResponse } = require('../utils/response');
|
||||
|
||||
// 错误处理中间件
|
||||
const errorHandler = (err, req, res, next) => {
|
||||
// 记录错误日志
|
||||
console.error(err.stack);
|
||||
|
||||
// 默认错误状态码和消息
|
||||
let statusCode = 500;
|
||||
let message = '服务器内部错误';
|
||||
|
||||
// 根据错误类型设置状态码和消息
|
||||
if (err.name === 'ValidationError') {
|
||||
statusCode = 400;
|
||||
message = '请求参数验证失败';
|
||||
} else if (err.name === 'UnauthorizedError') {
|
||||
statusCode = 401;
|
||||
message = '未授权访问';
|
||||
} else if (err.name === 'ForbiddenError') {
|
||||
statusCode = 403;
|
||||
message = '访问被拒绝';
|
||||
} else if (err.name === 'NotFoundError') {
|
||||
statusCode = 404;
|
||||
message = '资源未找到';
|
||||
}
|
||||
|
||||
// 返回错误响应
|
||||
res.status(statusCode).json(errorResponse(message, statusCode));
|
||||
};
|
||||
|
||||
module.exports = errorHandler;
|
||||
13
backend/src/middleware/healthCheck.js
Normal file
13
backend/src/middleware/healthCheck.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// 健康检查端点
|
||||
router.get('/health', (req, res) => {
|
||||
res.status(200).json({
|
||||
status: 'OK',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime()
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
36
backend/src/middleware/logger.js
Normal file
36
backend/src/middleware/logger.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { logConfig } = require('../config/config');
|
||||
|
||||
// 创建日志目录
|
||||
const logDir = path.join(__dirname, '../../logs');
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir, { recursive: true });
|
||||
}
|
||||
|
||||
// 创建日志文件流
|
||||
const logStream = fs.createWriteStream(path.join(logDir, 'app.log'), { flags: 'a' });
|
||||
|
||||
// 日志中间件
|
||||
const logger = (req, res, next) => {
|
||||
// 只在开发环境记录详细日志
|
||||
if (process.env.NODE_ENV === 'development' || logConfig.level === 'debug') {
|
||||
const logEntry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent')
|
||||
};
|
||||
|
||||
// 写入日志文件
|
||||
logStream.write(JSON.stringify(logEntry) + '\n');
|
||||
|
||||
// 控制台输出
|
||||
console.log(`${logEntry.timestamp} - ${logEntry.method} ${logEntry.url} - ${logEntry.ip}`);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = logger;
|
||||
83
backend/src/models/Order.js
Normal file
83
backend/src/models/Order.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../config/database');
|
||||
|
||||
// 订单模型
|
||||
const Order = sequelize.define('Order', {
|
||||
id: {
|
||||
type: DataTypes.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
order_no: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
buyer_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false
|
||||
},
|
||||
trader_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: true
|
||||
},
|
||||
supplier_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false
|
||||
},
|
||||
driver_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: 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: {
|
||||
type: DataTypes.DECIMAL(10,2),
|
||||
allowNull: true
|
||||
},
|
||||
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'
|
||||
},
|
||||
delivery_address: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false
|
||||
},
|
||||
delivery_date: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false
|
||||
},
|
||||
special_requirements: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
}
|
||||
}, {
|
||||
tableName: 'orders',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
});
|
||||
|
||||
module.exports = Order;
|
||||
59
backend/src/models/Payment.js
Normal file
59
backend/src/models/Payment.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../config/database');
|
||||
|
||||
// 支付模型
|
||||
const Payment = sequelize.define('Payment', {
|
||||
id: {
|
||||
type: DataTypes.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
order_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false
|
||||
},
|
||||
user_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false
|
||||
},
|
||||
amount: {
|
||||
type: DataTypes.DECIMAL(15,2),
|
||||
allowNull: false
|
||||
},
|
||||
paid_amount: {
|
||||
type: DataTypes.DECIMAL(15,2),
|
||||
allowNull: true
|
||||
},
|
||||
payment_type: {
|
||||
type: DataTypes.ENUM('wechat', 'alipay', 'bank'),
|
||||
allowNull: false
|
||||
},
|
||||
payment_method: {
|
||||
type: DataTypes.ENUM('mini_program', 'app', 'web'),
|
||||
allowNull: false
|
||||
},
|
||||
payment_no: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
third_party_id: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('pending', 'paid', 'failed', 'refunded'),
|
||||
defaultValue: 'pending'
|
||||
},
|
||||
paid_time: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
}
|
||||
}, {
|
||||
tableName: 'payments',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
});
|
||||
|
||||
module.exports = Payment;
|
||||
58
backend/src/models/TransportTrack.js
Normal file
58
backend/src/models/TransportTrack.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../config/database');
|
||||
|
||||
// 运输跟踪模型
|
||||
const TransportTrack = sequelize.define('TransportTrack', {
|
||||
id: {
|
||||
type: DataTypes.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
order_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false
|
||||
},
|
||||
driver_id: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false
|
||||
},
|
||||
latitude: {
|
||||
type: DataTypes.DECIMAL(10,8),
|
||||
allowNull: false
|
||||
},
|
||||
longitude: {
|
||||
type: DataTypes.DECIMAL(11,8),
|
||||
allowNull: false
|
||||
},
|
||||
speed: {
|
||||
type: DataTypes.DECIMAL(5,2),
|
||||
allowNull: true
|
||||
},
|
||||
direction: {
|
||||
type: DataTypes.DECIMAL(5,2),
|
||||
allowNull: true
|
||||
},
|
||||
cattle_status: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: true
|
||||
},
|
||||
temperature: {
|
||||
type: DataTypes.DECIMAL(5,2),
|
||||
allowNull: true
|
||||
},
|
||||
humidity: {
|
||||
type: DataTypes.DECIMAL(5,2),
|
||||
allowNull: true
|
||||
},
|
||||
video_url: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true
|
||||
}
|
||||
}, {
|
||||
tableName: 'transport_tracks',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: false
|
||||
});
|
||||
|
||||
module.exports = TransportTrack;
|
||||
56
backend/src/models/User.js
Normal file
56
backend/src/models/User.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../config/database');
|
||||
|
||||
// 用户模型
|
||||
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: true
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true
|
||||
},
|
||||
real_name: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: true
|
||||
},
|
||||
avatar_url: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true
|
||||
},
|
||||
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,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
});
|
||||
|
||||
module.exports = User;
|
||||
12
backend/src/routes/auth.js
Normal file
12
backend/src/routes/auth.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const AuthController = require('../controllers/AuthController');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
|
||||
// 小程序用户登录
|
||||
router.post('/mini-program/login', AuthController.miniProgramLogin);
|
||||
|
||||
// 获取当前用户信息
|
||||
router.get('/current', authenticate, AuthController.getCurrentUser);
|
||||
|
||||
module.exports = router;
|
||||
18
backend/src/routes/orders.js
Normal file
18
backend/src/routes/orders.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const OrderController = require('../controllers/OrderController');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
|
||||
// 创建订单
|
||||
router.post('/', authenticate, OrderController.createOrder);
|
||||
|
||||
// 获取订单列表
|
||||
router.get('/', authenticate, OrderController.getOrderList);
|
||||
|
||||
// 获取订单详情
|
||||
router.get('/:id', authenticate, OrderController.getOrderDetail);
|
||||
|
||||
// 更新订单状态
|
||||
router.patch('/:id/status', authenticate, OrderController.updateOrderStatus);
|
||||
|
||||
module.exports = router;
|
||||
18
backend/src/routes/payments.js
Normal file
18
backend/src/routes/payments.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const PaymentController = require('../controllers/PaymentController');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
|
||||
// 创建支付
|
||||
router.post('/', authenticate, PaymentController.createPayment);
|
||||
|
||||
// 获取支付列表
|
||||
router.get('/', authenticate, PaymentController.getPaymentList);
|
||||
|
||||
// 获取支付详情
|
||||
router.get('/:id', authenticate, PaymentController.getPaymentDetail);
|
||||
|
||||
// 更新支付状态(模拟支付回调)
|
||||
router.post('/callback', PaymentController.updatePaymentStatus);
|
||||
|
||||
module.exports = router;
|
||||
18
backend/src/routes/users.js
Normal file
18
backend/src/routes/users.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const UserController = require('../controllers/UserController');
|
||||
const { authenticate, checkRole } = require('../middleware/auth');
|
||||
|
||||
// 获取用户列表 (管理员)
|
||||
router.get('/', authenticate, checkRole(['admin']), UserController.getUserList);
|
||||
|
||||
// 获取用户详情
|
||||
router.get('/:id', authenticate, UserController.getUserDetail);
|
||||
|
||||
// 更新用户信息
|
||||
router.put('/:id', authenticate, UserController.updateUser);
|
||||
|
||||
// 更新用户状态 (管理员)
|
||||
router.patch('/:id/status', authenticate, checkRole(['admin']), UserController.updateUserStatus);
|
||||
|
||||
module.exports = router;
|
||||
17
backend/src/scripts/syncDatabase.js
Normal file
17
backend/src/scripts/syncDatabase.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const sequelize = require('../config/database');
|
||||
|
||||
// 同步数据库模型
|
||||
const syncDatabase = async () => {
|
||||
try {
|
||||
await sequelize.sync({ alter: true });
|
||||
console.log('数据库模型同步成功');
|
||||
} catch (error) {
|
||||
console.error('数据库模型同步失败:', error);
|
||||
} finally {
|
||||
// 关闭数据库连接
|
||||
await sequelize.close();
|
||||
}
|
||||
};
|
||||
|
||||
// 执行同步
|
||||
syncDatabase();
|
||||
67
backend/src/services/AuthService.js
Normal file
67
backend/src/services/AuthService.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { jwtConfig } = require('../config/config');
|
||||
const User = require('../models/User');
|
||||
|
||||
// 小程序用户登录服务
|
||||
const miniProgramLogin = async (phone, code, miniProgramType) => {
|
||||
// 验证验证码(实际项目中需要对接短信服务)
|
||||
if (code !== '123456') { // 临时验证码,实际项目中需要验证真实验证码
|
||||
throw new Error('验证码错误');
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
let user = await User.findOne({ where: { phone } });
|
||||
|
||||
// 如果用户不存在则创建新用户
|
||||
if (!user) {
|
||||
user = await User.create({
|
||||
uuid: uuidv4(),
|
||||
username: `user_${phone}`,
|
||||
phone,
|
||||
user_type: miniProgramType || 'client',
|
||||
password_hash: bcrypt.hashSync(phone, 10) // 临时密码,实际项目中需要更安全的处理
|
||||
});
|
||||
}
|
||||
|
||||
// 生成JWT token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
id: user.id,
|
||||
uuid: user.uuid,
|
||||
username: user.username,
|
||||
phone: user.phone,
|
||||
userType: user.user_type
|
||||
},
|
||||
jwtConfig.secret,
|
||||
{ expiresIn: jwtConfig.expiresIn }
|
||||
);
|
||||
|
||||
// 返回用户信息和token
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
avatar: user.avatar_url,
|
||||
userType: user.user_type,
|
||||
phone: user.phone
|
||||
};
|
||||
|
||||
return { token, userInfo };
|
||||
};
|
||||
|
||||
// 验证token服务
|
||||
const verifyToken = async (token) => {
|
||||
try {
|
||||
const decoded = jwt.verify(token, jwtConfig.secret);
|
||||
return decoded;
|
||||
} catch (error) {
|
||||
throw new Error('无效的token');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
miniProgramLogin,
|
||||
verifyToken
|
||||
};
|
||||
104
backend/src/services/OrderService.js
Normal file
104
backend/src/services/OrderService.js
Normal file
@@ -0,0 +1,104 @@
|
||||
const Order = require('../models/Order');
|
||||
|
||||
// 创建订单服务
|
||||
const createOrder = async (orderData, userId) => {
|
||||
// 设置买家ID
|
||||
orderData.buyer_id = userId;
|
||||
|
||||
// 生成订单号
|
||||
const orderNo = `ORD${Date.now()}${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`;
|
||||
orderData.order_no = orderNo;
|
||||
|
||||
// 创建订单
|
||||
const order = await Order.create(orderData);
|
||||
return order;
|
||||
};
|
||||
|
||||
// 获取订单列表服务
|
||||
const getOrderList = async (query, user) => {
|
||||
const { page = 1, pageSize = 10, status, orderNo } = query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
// 根据用户类型过滤
|
||||
if (user.userType === 'client') {
|
||||
whereConditions.buyer_id = user.id;
|
||||
} else if (user.userType === 'trader') {
|
||||
whereConditions.trader_id = user.id;
|
||||
} else if (user.userType === 'supplier') {
|
||||
whereConditions.supplier_id = user.id;
|
||||
} else if (user.userType === 'driver') {
|
||||
whereConditions.driver_id = user.id;
|
||||
}
|
||||
|
||||
if (status) whereConditions.status = status;
|
||||
if (orderNo) whereConditions.order_no = orderNo;
|
||||
|
||||
// 查询订单列表
|
||||
const { count, rows } = await Order.findAndCountAll({
|
||||
where: whereConditions,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
return { orders: rows, count, page: parseInt(page), pageSize: parseInt(pageSize) };
|
||||
};
|
||||
|
||||
// 获取订单详情服务
|
||||
const getOrderDetail = async (id, user) => {
|
||||
const order = await Order.findByPk(id);
|
||||
|
||||
if (!order) {
|
||||
throw new Error('订单不存在');
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
if (user.userType === 'client' && order.buyer_id !== user.id) {
|
||||
throw new Error('无权限访问该订单');
|
||||
}
|
||||
|
||||
if (user.userType === 'trader' && order.trader_id !== user.id) {
|
||||
throw new Error('无权限访问该订单');
|
||||
}
|
||||
|
||||
if (user.userType === 'supplier' && order.supplier_id !== user.id) {
|
||||
throw new Error('无权限访问该订单');
|
||||
}
|
||||
|
||||
if (user.userType === 'driver' && order.driver_id !== user.id) {
|
||||
throw new Error('无权限访问该订单');
|
||||
}
|
||||
|
||||
return order;
|
||||
};
|
||||
|
||||
// 更新订单状态服务
|
||||
const updateOrderStatus = async (id, status, user) => {
|
||||
const order = await Order.findByPk(id);
|
||||
|
||||
if (!order) {
|
||||
throw new Error('订单不存在');
|
||||
}
|
||||
|
||||
// 权限检查和状态流转验证
|
||||
// 这里需要根据业务逻辑实现详细的状态流转控制
|
||||
|
||||
const [updatedRowsCount] = await Order.update({ status }, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
throw new Error('订单状态更新失败');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createOrder,
|
||||
getOrderList,
|
||||
getOrderDetail,
|
||||
updateOrderStatus
|
||||
};
|
||||
96
backend/src/services/PaymentService.js
Normal file
96
backend/src/services/PaymentService.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const Payment = require('../models/Payment');
|
||||
|
||||
// 创建支付服务
|
||||
const createPayment = async (paymentData, userId) => {
|
||||
const { orderId, amount, paymentType, paymentMethod } = paymentData;
|
||||
|
||||
// 生成支付单号
|
||||
const paymentNo = `PAY${Date.now()}${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`;
|
||||
|
||||
// 创建支付记录
|
||||
const payment = await Payment.create({
|
||||
order_id: orderId,
|
||||
user_id: userId,
|
||||
amount,
|
||||
payment_type: paymentType,
|
||||
payment_method: paymentMethod,
|
||||
payment_no: paymentNo,
|
||||
status: 'pending'
|
||||
});
|
||||
|
||||
// 这里需要对接实际的支付接口(微信支付、支付宝等)
|
||||
// 暂时返回模拟的支付参数
|
||||
const paymentParams = {
|
||||
paymentNo,
|
||||
amount,
|
||||
paymentType,
|
||||
paymentMethod
|
||||
};
|
||||
|
||||
return { payment, paymentParams };
|
||||
};
|
||||
|
||||
// 获取支付列表服务
|
||||
const getPaymentList = async (query, userId) => {
|
||||
const { page = 1, pageSize = 10, status } = query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {
|
||||
user_id: userId
|
||||
};
|
||||
|
||||
if (status) whereConditions.status = status;
|
||||
|
||||
// 查询支付列表
|
||||
const { count, rows } = await Payment.findAndCountAll({
|
||||
where: whereConditions,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
return { payments: rows, count, page: parseInt(page), pageSize: parseInt(pageSize) };
|
||||
};
|
||||
|
||||
// 获取支付详情服务
|
||||
const getPaymentDetail = async (id, userId) => {
|
||||
const payment = await Payment.findByPk(id);
|
||||
|
||||
if (!payment) {
|
||||
throw new Error('支付记录不存在');
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
if (payment.user_id !== userId) {
|
||||
throw new Error('无权限访问该支付记录');
|
||||
}
|
||||
|
||||
return payment;
|
||||
};
|
||||
|
||||
// 更新支付状态服务(模拟支付回调)
|
||||
const updatePaymentStatus = async (paymentNo, status, thirdPartyId) => {
|
||||
const [updatedRowsCount] = await Payment.update(
|
||||
{
|
||||
status,
|
||||
third_party_id: thirdPartyId,
|
||||
paid_time: status === 'paid' ? new Date() : null
|
||||
},
|
||||
{
|
||||
where: { payment_no: paymentNo }
|
||||
}
|
||||
);
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
throw new Error('支付记录不存在');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createPayment,
|
||||
getPaymentList,
|
||||
getPaymentDetail,
|
||||
updatePaymentStatus
|
||||
};
|
||||
107
backend/src/services/UserService.js
Normal file
107
backend/src/services/UserService.js
Normal file
@@ -0,0 +1,107 @@
|
||||
const User = require('../models/User');
|
||||
|
||||
// 获取用户列表服务
|
||||
const getUserList = async (query) => {
|
||||
const { page = 1, pageSize = 10, userType, status } = query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
if (userType) whereConditions.user_type = userType;
|
||||
if (status) whereConditions.status = status;
|
||||
|
||||
// 查询用户列表
|
||||
const { count, rows } = await User.findAndCountAll({
|
||||
where: whereConditions,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
// 格式化用户数据
|
||||
const users = rows.map(user => ({
|
||||
id: user.id,
|
||||
uuid: user.uuid,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
userType: user.user_type,
|
||||
status: user.status,
|
||||
avatar: user.avatar_url,
|
||||
createdAt: user.created_at
|
||||
}));
|
||||
|
||||
return { users, count, page: parseInt(page), pageSize: parseInt(pageSize) };
|
||||
};
|
||||
|
||||
// 获取用户详情服务
|
||||
const getUserDetail = async (id) => {
|
||||
const user = await User.findByPk(id);
|
||||
|
||||
if (!user) {
|
||||
throw new Error('用户不存在');
|
||||
}
|
||||
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
uuid: user.uuid,
|
||||
username: user.username,
|
||||
realName: user.real_name,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
userType: user.user_type,
|
||||
status: user.status,
|
||||
avatar: user.avatar_url,
|
||||
idCardFront: user.id_card_front_url,
|
||||
idCardBack: user.id_card_back_url,
|
||||
licenseFront: user.license_front_url,
|
||||
licenseBack: user.license_back_url,
|
||||
businessLicense: user.business_license_url,
|
||||
createdAt: user.created_at,
|
||||
updatedAt: user.updated_at
|
||||
};
|
||||
|
||||
return userInfo;
|
||||
};
|
||||
|
||||
// 更新用户信息服务
|
||||
const updateUser = async (id, updateData) => {
|
||||
// 过滤不允许更新的字段
|
||||
const allowedFields = ['real_name', 'email', 'avatar_url'];
|
||||
const filteredData = {};
|
||||
Object.keys(updateData).forEach(key => {
|
||||
if (allowedFields.includes(key)) {
|
||||
filteredData[key] = updateData[key];
|
||||
}
|
||||
});
|
||||
|
||||
const [updatedRowsCount] = await User.update(filteredData, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
throw new Error('用户不存在');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 更新用户状态服务
|
||||
const updateUserStatus = async (id, status) => {
|
||||
const [updatedRowsCount] = await User.update({ status }, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedRowsCount === 0) {
|
||||
throw new Error('用户不存在');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getUserList,
|
||||
getUserDetail,
|
||||
updateUser,
|
||||
updateUserStatus
|
||||
};
|
||||
43
backend/src/utils/response.js
Normal file
43
backend/src/utils/response.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// 统一响应格式
|
||||
|
||||
// 成功响应
|
||||
const successResponse = (data, message = '成功', code = 200) => {
|
||||
return {
|
||||
code,
|
||||
message,
|
||||
data,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
};
|
||||
|
||||
// 错误响应
|
||||
const errorResponse = (message = '请求失败', code = 500, data = null) => {
|
||||
return {
|
||||
code,
|
||||
message,
|
||||
data,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
};
|
||||
|
||||
// 分页响应
|
||||
const paginatedResponse = (items, total, page, limit) => {
|
||||
return {
|
||||
code: 200,
|
||||
message: '成功',
|
||||
data: {
|
||||
items,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
},
|
||||
timestamp: Date.now()
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
successResponse,
|
||||
errorResponse,
|
||||
paginatedResponse
|
||||
};
|
||||
Reference in New Issue
Block a user