refactor(docs): 更新API接口文档结构,优化环境配置说明并移除冗余文件

This commit is contained in:
2025-09-22 15:29:23 +08:00
parent cc2a351f84
commit 00cf840e6f
74 changed files with 620 additions and 5203 deletions

View File

@@ -0,0 +1,107 @@
// 创建测试订单数据
const { Order } = require('./models');
const createTestOrders = async () => {
try {
// 测试数据库连接
const { testConnection } = require('./models');
const connected = await testConnection();
if (!connected) {
console.error('数据库连接失败,无法创建测试数据');
return;
}
// 同步模型
await Order.sync({ force: false });
console.log('✅ 订单表同步成功');
// 检查是否已有数据
const existingCount = await Order.count();
if (existingCount > 0) {
console.log(`已存在 ${existingCount} 条订单记录`);
return;
}
// 创建测试订单
const testOrders = [
{
orderNo: 'ORD20240101001',
buyerId: 1,
buyerName: '测试采购商A',
supplierId: 1,
supplierName: '山东畜牧合作社',
cattleBreed: '西门塔尔牛',
cattleCount: 50,
expectedWeight: 25000.00,
unitPrice: 28.50,
totalAmount: 712500.00,
paidAmount: 200000.00,
remainingAmount: 512500.00,
status: 'pending',
deliveryAddress: '山东省济南市历下区测试地址1号',
expectedDeliveryDate: new Date('2024-02-01'),
notes: '优质西门塔尔牛,要求健康证明齐全'
},
{
orderNo: 'ORD20240101002',
buyerId: 2,
buyerName: '测试采购商B',
supplierId: 2,
supplierName: '河北养殖基地',
cattleBreed: '安格斯牛',
cattleCount: 30,
expectedWeight: 18000.00,
unitPrice: 32.00,
totalAmount: 576000.00,
paidAmount: 576000.00,
remainingAmount: 0.00,
status: 'confirmed',
deliveryAddress: '河北省石家庄市长安区测试地址2号',
expectedDeliveryDate: new Date('2024-01-25'),
actualDeliveryDate: new Date('2024-01-24'),
notes: '已完成支付,请按时交付'
},
{
orderNo: 'ORD20240101003',
buyerId: 1,
buyerName: '测试采购商A',
supplierId: 3,
supplierName: '内蒙古牧场',
cattleBreed: '蒙古牛',
cattleCount: 80,
expectedWeight: 32000.00,
unitPrice: 26.00,
totalAmount: 832000.00,
paidAmount: 300000.00,
remainingAmount: 532000.00,
status: 'shipping',
deliveryAddress: '内蒙古呼和浩特市赛罕区测试地址3号',
expectedDeliveryDate: new Date('2024-02-10'),
notes: '大批量订单,分批交付'
}
];
// 批量创建订单
const createdOrders = await Order.bulkCreate(testOrders);
console.log(`✅ 成功创建 ${createdOrders.length} 条测试订单`);
// 显示创建的订单
createdOrders.forEach(order => {
console.log(`- ${order.orderNo}: ${order.cattleCount}${order.cattleBreed} (${order.status})`);
});
} catch (error) {
console.error('❌ 创建测试订单失败:', error);
}
};
// 如果直接运行此文件
if (require.main === module) {
createTestOrders().then(() => {
console.log('测试数据创建完成');
process.exit(0);
});
}
module.exports = createTestOrders;

1254
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -31,40 +31,42 @@
"author": "NiuMall Team",
"license": "MIT",
"dependencies": {
"express": "^4.18.2",
"sequelize": "^6.35.2",
"mysql2": "^3.6.5",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"joi": "^17.11.0",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"express-rate-limit": "^7.1.5",
"bcryptjs": "^3.0.2",
"compression": "^1.7.4",
"morgan": "^1.10.0",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-rate-limit": "^7.1.5",
"express-validator": "^7.0.1",
"helmet": "^7.1.0",
"joi": "^17.11.0",
"jsonwebtoken": "^9.0.2",
"moment": "^2.29.4",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"mysql2": "^3.6.5",
"sequelize": "^6.35.2",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"express-validator": "^7.0.1",
"moment": "^2.29.4",
"uuid": "^9.0.1"
"uuid": "^9.0.1",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1",
"yamljs": "^0.3.0"
},
"devDependencies": {
"nodemon": "^3.0.2",
"jest": "^29.7.0",
"supertest": "^6.3.3",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"prettier": "^3.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"sequelize-cli": "^6.6.2"
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.7.0",
"nodemon": "^3.0.2",
"prettier": "^3.1.0",
"sequelize-cli": "^6.6.2",
"supertest": "^6.3.3"
},
"jest": {
"testEnvironment": "node",

View File

@@ -1,5 +1,5 @@
const { successResponse, errorResponse, paginatedResponse } = require('../utils/response');
const Order = require('../models/Order');
const { Order } = require('../../models');
// 创建订单
const createOrder = async (req, res) => {
@@ -51,13 +51,13 @@ const getOrderList = async (req, res) => {
// 根据用户类型过滤
if (req.user.userType === 'client') {
whereConditions.buyer_id = req.user.id;
whereConditions.buyerId = req.user.id;
} else if (req.user.userType === 'trader') {
whereConditions.trader_id = req.user.id;
whereConditions.traderId = req.user.id;
} else if (req.user.userType === 'supplier') {
whereConditions.supplier_id = req.user.id;
whereConditions.supplierId = req.user.id;
} else if (req.user.userType === 'driver') {
whereConditions.driver_id = req.user.id;
whereConditions.driverId = req.user.id;
}
if (status) whereConditions.status = status;
@@ -71,7 +71,18 @@ const getOrderList = async (req, res) => {
order: [['created_at', 'DESC']]
});
res.json(paginatedResponse(rows, count, parseInt(page), parseInt(pageSize)));
// 格式化返回数据以匹配前端期望的格式
res.json({
success: true,
data: {
items: rows,
total: count,
page: parseInt(page),
pageSize: parseInt(pageSize),
totalPages: Math.ceil(count / parseInt(pageSize))
},
message: '获取订单列表成功'
});
} catch (error) {
console.error('获取订单列表错误:', error);
res.status(500).json(errorResponse('服务器内部错误', 500));
@@ -90,19 +101,19 @@ const getOrderDetail = async (req, res) => {
}
// 权限检查
if (req.user.userType === 'client' && order.buyer_id !== req.user.id) {
if (req.user.userType === 'client' && order.buyerId !== req.user.id) {
return res.status(403).json(errorResponse('无权限访问该订单', 403));
}
if (req.user.userType === 'trader' && order.trader_id !== req.user.id) {
if (req.user.userType === 'trader' && order.traderId !== req.user.id) {
return res.status(403).json(errorResponse('无权限访问该订单', 403));
}
if (req.user.userType === 'supplier' && order.supplier_id !== req.user.id) {
if (req.user.userType === 'supplier' && order.supplierId !== req.user.id) {
return res.status(403).json(errorResponse('无权限访问该订单', 403));
}
if (req.user.userType === 'driver' && order.driver_id !== req.user.id) {
if (req.user.userType === 'driver' && order.driverId !== req.user.id) {
return res.status(403).json(errorResponse('无权限访问该订单', 403));
}

View File

@@ -29,7 +29,7 @@ const getUserList = async (req, res) => {
email: user.email,
userType: user.user_type,
status: user.status,
avatar: user.avatar_url,
avatar: user.avatar,
createdAt: user.created_at
}));
@@ -60,12 +60,8 @@ const getUserDetail = async (req, res) => {
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,
avatar: user.avatar,
businessLicense: user.business_license,
createdAt: user.created_at,
updatedAt: user.updated_at
};
@@ -84,7 +80,7 @@ const updateUser = async (req, res) => {
const updateData = req.body;
// 过滤不允许更新的字段
const allowedFields = ['real_name', 'email', 'avatar_url'];
const allowedFields = ['username', 'real_name', 'email', 'avatar'];
const filteredData = {};
Object.keys(updateData).forEach(key => {
if (allowedFields.includes(key)) {

View File

@@ -65,6 +65,7 @@ app.use(cors({
origin: [
'http://localhost:3000',
'http://localhost:3001',
'http://localhost:3002',
'http://localhost:5173',
'https://wapi.nanniwan.com',
'https://ad.nanniwan.com',

View File

@@ -24,7 +24,11 @@ const authenticateToken = (req, res, next) => {
// 从请求头获取token
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return errorResponse(res, '未提供认证token', 401);
return res.status(401).json({
success: false,
message: '未提供认证token',
code: 'AUTH_TOKEN_MISSING'
});
}
const token = authHeader.split(' ')[1];
@@ -35,11 +39,23 @@ const authenticateToken = (req, res, next) => {
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return errorResponse(res, 'Token已过期', 401);
return res.status(401).json({
success: false,
message: 'Token已过期',
code: 'AUTH_TOKEN_EXPIRED'
});
} else if (error.name === 'JsonWebTokenError') {
return errorResponse(res, '无效的Token', 401);
return res.status(401).json({
success: false,
message: '无效的Token',
code: 'AUTH_INVALID_TOKEN'
});
}
return errorResponse(res, '认证失败', 401);
return res.status(401).json({
success: false,
message: '认证失败',
code: 'AUTH_FAILED'
});
}
};

View File

@@ -11,6 +11,7 @@ router.post('/mini-program/login', AuthController.miniProgramLogin);
// 获取当前用户信息
router.get('/current', authenticate, AuthController.getCurrentUser);
router.get('/me', authenticate, AuthController.getCurrentUser);
// 用户登出
router.post('/logout', authenticate, async (req, res) => {

View File

@@ -3,6 +3,15 @@ const router = express.Router();
const OrderController = require('../controllers/OrderController');
const { authenticate } = require('../middleware/auth');
// 测试接口 - 不需要认证
router.get('/test', (req, res) => {
res.json({
success: true,
message: '订单API测试成功',
timestamp: new Date().toISOString()
});
});
// 创建订单
router.post('/', authenticate, OrderController.createOrder);

View File

@@ -67,7 +67,7 @@ const getUserDetail = async (id) => {
// 更新用户信息服务
const updateUser = async (id, updateData) => {
// 过滤不允许更新的字段
const allowedFields = ['real_name', 'email', 'avatar_url'];
const allowedFields = ['real_name', 'avatar_url'];
const filteredData = {};
Object.keys(updateData).forEach(key => {
if (allowedFields.includes(key)) {

View File

@@ -0,0 +1,65 @@
const sequelize = require('./src/config/database');
const { User } = require('./src/models');
// 使用环境变量中的数据库配置
const config = {
host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
port: process.env.DB_PORT || 20784,
username: process.env.DB_USERNAME || 'jiebanke',
password: process.env.DB_PASSWORD || 'aiot741$12346',
database: process.env.DB_NAME || 'niumall'
};
async function testUserUpdate() {
try {
console.log('测试用户编辑功能...');
// 连接到腾讯云数据库
await sequelize.authenticate();
console.log('数据库连接成功');
// 获取一个测试用户
const testUser = await User.findOne();
if (!testUser) {
console.log('没有找到测试用户,请先创建用户数据');
return;
}
console.log('原始用户数据:', {
id: testUser.id,
username: testUser.username,
email: testUser.email
});
// 更新用户信息
const updateData = {
username: 'test_updated_' + Date.now(),
email: 'updated_' + Date.now() + '@example.com'
};
const [updatedRows] = await User.update(updateData, {
where: { id: testUser.id }
});
if (updatedRows > 0) {
console.log('用户更新成功,更新行数:', updatedRows);
// 验证更新结果
const updatedUser = await User.findByPk(testUser.id);
console.log('更新后的用户数据:', {
id: updatedUser.id,
username: updatedUser.username,
email: updatedUser.email
});
console.log('用户编辑功能测试通过!');
} else {
console.log('用户更新失败,没有行被更新');
}
} catch (error) {
console.error('测试过程中出错:', error.message);
}
}
testUserUpdate();

View File

@@ -0,0 +1,217 @@
// 使用腾讯云数据库配置进行测试
const { Sequelize, DataTypes } = require('sequelize');
const config = {
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
port: 20784,
username: 'jiebanke',
password: 'aiot741$12346',
database: 'niumall',
dialect: 'mysql',
dialectOptions: {
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci'
},
logging: console.log
};
const sequelize = new Sequelize(config);
// 定义用户模型(与数据库表结构一致)
const User = sequelize.define('User', {
id: {
type: DataTypes.BIGINT,
primaryKey: true,
autoIncrement: true,
comment: '用户ID'
},
uuid: {
type: DataTypes.STRING(36),
allowNull: true,
unique: true,
comment: '用户唯一标识符'
},
username: {
type: DataTypes.STRING(50),
allowNull: true,
unique: true,
comment: '用户名'
},
password_hash: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '密码哈希值'
},
openid: {
type: DataTypes.STRING(64),
allowNull: true,
comment: '微信小程序OpenID'
},
unionid: {
type: DataTypes.STRING(64),
allowNull: true,
comment: '微信UnionID'
},
nickname: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '用户昵称'
},
real_name: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '真实姓名'
},
avatar: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '头像URL'
},
gender: {
type: DataTypes.ENUM('male', 'female', 'other'),
allowNull: true,
comment: '性别'
},
birthday: {
type: DataTypes.DATE,
allowNull: true,
comment: '生日'
},
phone: {
type: DataTypes.STRING(20),
allowNull: true,
unique: true,
comment: '手机号码'
},
email: {
type: DataTypes.STRING(100),
allowNull: true,
unique: true,
comment: '邮箱地址'
},
user_type: {
type: DataTypes.ENUM('buyer', 'trader', 'supplier', 'driver', 'staff', 'admin'),
allowNull: false,
defaultValue: 'buyer',
comment: '用户类型'
},
company_name: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '公司名称'
},
company_address: {
type: DataTypes.STRING(200),
allowNull: true,
comment: '公司地址'
},
business_license: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '营业执照文件路径'
},
id_card: {
type: DataTypes.STRING(18),
allowNull: true,
comment: '身份证号'
},
status: {
type: DataTypes.ENUM('active', 'inactive', 'suspended', 'pending_approval'),
allowNull: false,
defaultValue: 'pending_approval',
comment: '用户状态'
},
last_login_at: {
type: DataTypes.DATE,
allowNull: true,
comment: '最后登录时间'
},
login_count: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '登录次数'
},
registration_source: {
type: DataTypes.ENUM('miniprogram', 'web', 'admin_create'),
allowNull: false,
defaultValue: 'miniprogram',
comment: '注册来源'
},
approval_notes: {
type: DataTypes.TEXT,
allowNull: true,
comment: '审核备注'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '创建时间'
},
updated_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '更新时间'
}
}, {
tableName: 'users',
timestamps: false
});
async function testUserUpdate() {
try {
console.log('测试用户编辑功能...');
// 连接到腾讯云数据库
await sequelize.authenticate();
console.log('数据库连接成功');
// 获取一个测试用户
const testUser = await User.findOne();
if (!testUser) {
console.log('没有找到测试用户,请先创建用户数据');
return;
}
console.log('原始用户数据:', {
id: testUser.id,
username: testUser.username,
email: testUser.email
});
// 更新用户信息
const updateData = {
username: 'test_updated_' + Date.now(),
email: 'updated_' + Date.now() + '@example.com'
};
const [updatedRows] = await User.update(updateData, {
where: { id: testUser.id }
});
if (updatedRows > 0) {
console.log('用户更新成功,更新行数:', updatedRows);
// 验证更新结果
const updatedUser = await User.findByPk(testUser.id);
console.log('更新后的用户数据:', {
id: updatedUser.id,
username: updatedUser.username,
email: updatedUser.email
});
console.log('用户编辑功能测试通过!');
} else {
console.log('用户更新失败,没有行被更新');
}
} catch (error) {
console.error('测试过程中出错:', error.message);
} finally {
await sequelize.close();
}
}
testUserUpdate();