You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1932 lines
75 KiB

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mysql = require('mysql2/promise');
const path = require('path');
const http = require('http');
const WebSocket = require('ws');
const OssUploader = require('./oss-uploader');
const app = express();
const PORT = 3000;
// 创建HTTP服务器
const server = http.createServer(app);
// 创建WebSocket服务器
const wss = new WebSocket.Server({ server });
// 客户端连接集合
const clients = new Set();
// WebSocket连接处理
wss.on('connection', (ws) => {
console.log('WebSocket客户端已连接');
// 将新连接的客户端添加到集合中
clients.add(ws);
// 接收消息处理
ws.on('message', (message) => {
try {
const data = JSON.parse(message);
console.log('收到WebSocket消息:', data);
// 处理不同类型的消息
switch (data.type) {
case 'login':
// 登录消息,存储用户信息
ws.userId = data.userId;
console.log(`用户 ${data.userId} 已登录`);
break;
case 'pong':
// 心跳响应,无需处理
break;
default:
console.log('未知消息类型:', data.type);
}
} catch (error) {
console.error('解析WebSocket消息失败:', error);
}
});
// 连接关闭处理
ws.on('close', () => {
console.log('WebSocket客户端已断开连接');
clients.delete(ws);
});
// 连接错误处理
ws.on('error', (error) => {
console.error('WebSocket连接错误:', error);
});
});
// 广播消息给所有客户端
function broadcastMessage(message) {
const messageStr = JSON.stringify(message);
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(messageStr);
}
});
}
// 广播消息给特定用户
function broadcastToUser(userId, message) {
const messageStr = JSON.stringify(message);
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN && client.userId === userId) {
client.send(messageStr);
}
});
}
// 启动心跳机制,每30秒发送一次ping消息
setInterval(() => {
broadcastMessage({ type: 'ping' });
// console.log('发送心跳消息: ping');
}, 30000);
// 自动下架任务 - 每分钟检查一次
setInterval(async () => {
try {
console.log('检查需要自动下架的商品...');
const connection = await pool.getConnection();
// 查找所有状态为published且autoOfflineTime已过的商品
const currentTime = new Date();
const [productsToOffline] = await connection.query(
'SELECT id FROM products WHERE status = ? AND autoOfflineTime <= ? AND autoOfflineTime IS NOT NULL',
['published', currentTime]
);
if (productsToOffline.length > 0) {
console.log(`发现${productsToOffline.length}个商品需要自动下架`);
// 批量更新商品状态为sold_out
for (const product of productsToOffline) {
await connection.query(
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
['sold_out', product.id]
);
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: product.id,
action: 'unpublish',
status: 'sold_out'
});
}
console.log('自动下架任务完成');
}
connection.release();
} catch (error) {
console.error('自动下架任务失败:', error.message);
}
}, 60000); // 每分钟检查一次
// 配置CORS
app.use(cors());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
next();
});
app.use(bodyParser.json({ limit: '10mb' }));
app.use(express.static(path.join(__dirname)));
// 数据库配置 - 从环境变量获取,与docker-compose.yml保持一致
const dbConfig = {
host: process.env.DB_HOST || '1.95.162.61',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'schl@2025',
database: process.env.DB_NAME || 'wechat_app',
waitForConnections: true,
connectionLimit: 20, // 增加连接池大小,提高并发处理能力
queueLimit: 0,
connectTimeout: 10000, // 增加连接超时时间(mysql2支持的选项)
timezone: '+08:00' // 设置为北京时间时区
};
// userlogin数据库配置 - 从环境变量获取
const userLoginDbConfig = {
host: process.env.DB_HOST || '1.95.162.61',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'schl@2025',
database: process.env.USER_LOGIN_DB_NAME || 'userlogin',
waitForConnections: true,
connectionLimit: 20, // 增加连接池大小
queueLimit: 0,
connectTimeout: 10000, // 增加连接超时时间(mysql2支持的选项)
timezone: '+08:00' // 设置为北京时间时区
};
// 创建数据库连接池
let pool;
let userLoginPool;
// 初始化数据库连接
async function initDatabase() {
try {
pool = mysql.createPool(dbConfig);
console.log('wechat_app数据库连接池创建成功');
// 初始化userlogin数据库连接池
userLoginPool = mysql.createPool(userLoginDbConfig);
console.log('userlogin数据库连接池创建成功');
// 测试wechat_app连接
const connection = await pool.getConnection();
console.log('wechat_app数据库连接测试成功');
connection.release();
// 测试userlogin连接
const userLoginConnection = await userLoginPool.getConnection();
console.log('userlogin数据库连接测试成功');
userLoginConnection.release();
// 确保数据库结构
await ensureDatabaseSchema();
} catch (error) {
console.error('数据库初始化失败:', error.message);
console.error('错误详情:', error);
// 如果初始化失败,尝试重新初始化
setTimeout(() => {
console.log('尝试重新初始化数据库连接...');
initDatabase();
}, 5000);
}
}
// 通用响应函数
function sendResponse(res, success, data = null, message = '') {
res.json({
success,
data,
message
});
}
// 导出函数供测试使用
module.exports.sendResponse = sendResponse;
// 获取货源列表API
app.get('/api/supplies', async (req, res) => {
console.log('收到获取货源列表请求:', req.query);
try {
const connection = await pool.getConnection();
// 支持search和keyword两种参数名,确保兼容性
const { page = 1, pageSize = 10, search = '', keyword = '', status = '', phoneNumber = '' } = req.query;
// 如果提供了keyword参数,优先使用keyword
const actualSearch = keyword || search;
const offset = (page - 1) * pageSize;
// 构建基础查询,添加LEFT JOIN获取用户信息
let query = 'SELECT p.*, u.phoneNumber, u.nickName FROM products p LEFT JOIN users u ON p.sellerId = u.userId';
let countQuery = 'SELECT COUNT(*) as total FROM products p LEFT JOIN users u ON p.sellerId = u.userId';
let whereClause = '';
let params = [];
// 默认过滤掉status为hidden的货源,实现软删除效果
whereClause += ` WHERE status != 'hidden'`;
// 添加搜索条件
if (actualSearch) {
whereClause += ` AND (p.id LIKE ? OR p.productId LIKE ? OR p.productName LIKE ?)`;
params.push(`%${actualSearch}%`, `%${actualSearch}%`, `%${actualSearch}%`);
}
// 添加手机号搜索
if (phoneNumber) {
whereClause += ` AND u.phoneNumber LIKE ?`;
params.push(`%${phoneNumber}%`);
}
// 添加状态筛选
if (status) {
whereClause += ` AND status = ?`;
params.push(status);
}
// 添加sellerId筛选,只返回指定用户的货源
const { sellerId } = req.query;
if (sellerId) {
whereClause += ` AND p.sellerId = ?`;
params.push(sellerId);
}
// 执行查询
const [results] = await connection.query(
`${query}${whereClause} ORDER BY p.id DESC LIMIT ? OFFSET ?`,
[...params, parseInt(pageSize), offset]
);
// 获取总数
const [countResults] = await connection.query(
`${countQuery}${whereClause}`,
params
);
connection.release();
// 处理返回结果中的imageUrls字段
const processedResults = results.map(product => {
// 处理imageUrls字段
let imageUrls = [];
if (product.imageUrls) {
if (typeof product.imageUrls === 'string') {
// 尝试解析为JSON数组
try {
let parsedImages = JSON.parse(product.imageUrls);
// 检查是否是JSON字符串的字符串表示(转义的JSON)
if (typeof parsedImages === 'string' &&
(parsedImages.startsWith('[') || parsedImages.startsWith('{'))) {
// 进行第二次解析
parsedImages = JSON.parse(parsedImages);
}
if (Array.isArray(parsedImages)) {
imageUrls = parsedImages;
} else if (typeof parsedImages === 'string') {
// 如果解析结果是字符串,可能是单个URL
imageUrls = [parsedImages];
}
} catch (e) {
// 解析失败,尝试按逗号分隔
if (product.imageUrls.includes(',')) {
imageUrls = product.imageUrls.split(',').map(url => url.trim());
} else {
// 作为单个URL处理
imageUrls = [product.imageUrls.trim()];
}
}
} else if (Array.isArray(product.imageUrls)) {
// 已经是数组,直接使用
imageUrls = product.imageUrls;
} else {
// 其他类型,转换为字符串数组
imageUrls = [String(product.imageUrls)];
}
}
// 过滤并处理无效的URL:移除反引号并验证
imageUrls = imageUrls
.filter(url => {
if (!url) return false;
const processedUrl = url.replace(/`/g, '').trim();
return processedUrl.startsWith('http://') || processedUrl.startsWith('https://');
})
// 对每个有效URL进行处理,移除反引号
.map(url => url.replace(/`/g, '').trim());
return {
...product,
imageUrls
};
});
// 返回结果
sendResponse(res, true, {
list: processedResults,
total: countResults[0].total,
page: parseInt(page),
pageSize: parseInt(pageSize)
}, '获取货源列表成功');
} catch (error) {
console.error('获取货源列表失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '获取货源列表失败');
}
});
// 审核通过API
app.post('/api/supplies/:id/approve', async (req, res) => {
console.log('收到审核通过请求:', req.params.id, req.body);
try {
const connection = await pool.getConnection();
const { remark = '' } = req.body;
const productId = req.params.id;
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 检查状态是否可审核,只允许审核中的状态进行审核操作
if (!['pending_review'].includes(currentProduct[0].status)) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '该货源已审核,无需重复操作');
}
// 更新状态为已审核
await connection.query(
'UPDATE products SET status = ?, audit_time = ? WHERE id = ?',
['published', new Date(), productId]
);
// 记录日志
await connection.query(
'INSERT INTO audit_logs (supply_id, action, user_id, remark, created_at) VALUES (?, ?, ?, ?, ?)',
[productId, 'approve', 'system', remark, new Date()]
);
// 提交事务
await connection.commit();
connection.release();
sendResponse(res, true, null, '审核通过成功');
} catch (error) {
console.error('审核通过失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '审核通过失败');
}
});
console.log('正在注册拒绝审核API路由: /api/supplies/:id/reject');
// 审核拒绝API
app.post('/api/supplies/:id/reject', async (req, res) => {
console.log('收到审核拒绝请求:', req.params.id, req.body);
try {
const connection = await pool.getConnection();
// 同时支持reason和rejectReason参数,保持向后兼容
const { reason, rejectReason = '', remark = '' } = req.body;
// 如果有reason参数,则使用reason,否则使用rejectReason
const actualRejectReason = reason || rejectReason;
const productId = req.params.id;
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 检查状态是否可审核,只允许审核中的状态进行审核操作
if (!['pending_review'].includes(currentProduct[0].status)) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '当前状态不允许审核拒绝');
}
// 更新状态和拒绝理由
await connection.query(
'UPDATE products SET status = ?, rejectReason = ?, audit_time = ? WHERE id = ?',
['rejected', actualRejectReason, new Date(), productId]
);
// 记录日志
await connection.query(
'INSERT INTO audit_logs (supply_id, action, user_id, remark, created_at) VALUES (?, ?, ?, ?, ?)',
[productId, 'reject', 'system', remark, new Date()]
);
// 提交事务
await connection.commit();
connection.release();
sendResponse(res, true, null, '审核拒绝成功');
} catch (error) {
console.error('审核拒绝失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '审核拒绝失败');
}
});
// 获取供应商列表API已删除
// 供应商审核通过API已删除
// 供应商审核拒绝API已删除
// 供应商开始合作API已删除
// 供应商终止合作API已删除
console.log('正在注册测试API路由: /api/test-db');
// 测试数据库连接API
app.get('/api/test-db', async (req, res) => {
console.log('收到数据库连接测试请求');
try {
const connection = await pool.getConnection();
const [results] = await connection.query('SELECT 1 + 1 as solution');
connection.release();
sendResponse(res, true, results[0], '数据库连接成功');
} catch (error) {
console.error('数据库连接测试失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '数据库连接测试失败');
}
});
// 登录API
app.post('/api/login', async (req, res) => {
try {
const { projectName, userName, password } = req.body;
// 验证参数
if (!projectName || !userName || !password) {
return sendResponse(res, false, null, '职位名称、用户名和密码不能为空');
}
// 1. 验证职位名称是否为允许的类型
const allowedProjectNames = ['采购员', '管理员'];
if (!allowedProjectNames.includes(projectName)) {
return sendResponse(res, false, null, '仅允许采购员和管理员登录');
}
// 2. 在login表中验证登录信息
const userLoginConnection = await userLoginPool.getConnection();
const [loginResult] = await userLoginConnection.query(
'SELECT id, projectName, userName, managerId FROM login WHERE projectName = ? AND userName = ? AND password = ?',
[projectName, userName, password]
);
if (loginResult.length === 0) {
userLoginConnection.release();
return sendResponse(res, false, null, '职位名称、用户名或密码错误');
}
const loginInfo = loginResult[0];
const { managerId } = loginInfo;
// 2. 在personnel表中查询手机号码
const [personnelResult] = await userLoginConnection.query(
'SELECT phoneNumber, name FROM personnel WHERE managerId = ?',
[managerId]
);
userLoginConnection.release();
if (personnelResult.length === 0) {
return sendResponse(res, false, null, '未找到对应的员工信息');
}
const { phoneNumber, name } = personnelResult[0];
// 3. 在users表中查询userId
const connection = await pool.getConnection();
const [userResult] = await connection.query(
'SELECT userId FROM users WHERE phoneNumber = ?',
[phoneNumber]
);
connection.release();
let userId = null;
if (userResult.length > 0) {
userId = userResult[0].userId;
}
// 4. 生成token(简单实现,实际项目中应使用JWT等安全机制)
const token = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// 5. 返回登录结果
const userInfo = {
id: loginInfo.id,
projectName: loginInfo.projectName,
userName: loginInfo.userName,
managerId: loginInfo.managerId,
name: name,
phoneNumber: phoneNumber,
userId: userId
};
sendResponse(res, true, { userInfo, token }, '登录成功');
} catch (error) {
console.error('登录失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '登录失败,请稍后重试');
}
});
// 获取联系人数据API
app.get('/api/contacts', async (req, res) => {
try {
// 直接从userlogin数据库的Personnel表查询联系人信息,只查询工位名为销售员的联系人
const userLoginConnection = await userLoginPool.getConnection();
const [personnelData] = await userLoginConnection.query(
'SELECT id, projectName, alias, phoneNumber FROM Personnel WHERE projectName = "销售员" AND phoneNumber IS NOT NULL AND phoneNumber != ""'
);
userLoginConnection.release();
if (personnelData.length === 0) {
sendResponse(res, true, [], '没有找到联系人信息');
return;
}
// 创建联系人数据数组,保持与原API相同的返回格式,使用实际的数据库ID
const contacts = personnelData.map((person) => ({
id: person.id, // 使用数据库中的实际ID
salesPerson: person.projectName, // 销售员
name: person.alias, // 联系人别名
phoneNumber: person.phoneNumber // 电话号码
}));
sendResponse(res, true, contacts, '联系人数据获取成功');
} catch (error) {
console.error('获取联系人数据失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '获取联系人数据失败');
}
});
// 供应商审核通过API - /api/suppliers/:id/approve
console.log('正在注册供应商审核通过API路由: /api/suppliers/:id/approve');
app.post('/api/suppliers/:id/approve', async (req, res) => {
console.log('收到供应商审核通过请求:', req.params);
try {
const connection = await pool.getConnection();
const userId = req.params.id;
if (!userId) {
connection.release();
return sendResponse(res, false, null, '用户ID不能为空');
}
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentUser] = await connection.query(
'SELECT partnerstatus FROM users WHERE userId = ?',
[userId]
);
if (currentUser.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '供应商不存在');
}
if (currentUser[0].partnerstatus !== 'underreview') {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '当前状态不允许审核通过');
}
// 更新状态和审核时间
await connection.query(
'UPDATE users SET partnerstatus = ?, audit_time = ? WHERE userId = ?',
['approved', new Date(), userId]
);
// 提交事务
await connection.commit();
connection.release();
sendResponse(res, true, null, '供应商审核通过成功');
} catch (error) {
console.error('供应商审核通过失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '供应商审核通过失败');
}
});
// 供应商审核拒绝API - /api/suppliers/:id/reject
console.log('正在注册供应商审核拒绝API路由: /api/suppliers/:id/reject');
app.post('/api/suppliers/:id/reject', async (req, res) => {
console.log('收到供应商审核拒绝请求:', req.params, req.body);
try {
const connection = await pool.getConnection();
const userId = req.params.id;
const { rejectReason } = req.body;
if (!userId) {
connection.release();
return sendResponse(res, false, null, '用户ID不能为空');
}
if (!rejectReason) {
connection.release();
return sendResponse(res, false, null, '审核失败原因不能为空');
}
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentUser] = await connection.query(
'SELECT partnerstatus FROM users WHERE userId = ?',
[userId]
);
if (currentUser.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '供应商不存在');
}
if (currentUser[0].partnerstatus !== 'underreview') {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '当前状态不允许审核拒绝');
}
// 更新状态、审核失败原因和审核时间
await connection.query(
'UPDATE users SET partnerstatus = ?, reasonforfailure = ?, audit_time = ? WHERE userId = ?',
['reviewfailed', rejectReason, new Date(), userId]
);
// 提交事务
await connection.commit();
connection.release();
sendResponse(res, true, null, '供应商审核拒绝成功');
} catch (error) {
console.error('供应商审核拒绝失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '供应商审核拒绝失败');
}
});
// 供应商开始合作API - /api/suppliers/:id/cooperate
console.log('正在注册供应商开始合作API路由: /api/suppliers/:id/cooperate');
app.post('/api/suppliers/:id/cooperate', async (req, res) => {
console.log('收到供应商开始合作请求:', req.params);
try {
const connection = await pool.getConnection();
const userId = req.params.id;
if (!userId) {
connection.release();
return sendResponse(res, false, null, '用户ID不能为空');
}
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentUser] = await connection.query(
'SELECT partnerstatus FROM users WHERE userId = ?',
[userId]
);
if (currentUser.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '供应商不存在');
}
if (currentUser[0].partnerstatus !== 'approved') {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '只有审核通过的供应商才能开始合作');
}
// 更新状态
await connection.query(
'UPDATE users SET partnerstatus = ? WHERE userId = ?',
['incooperation', userId]
);
// 提交事务
await connection.commit();
connection.release();
sendResponse(res, true, null, '供应商开始合作成功');
} catch (error) {
console.error('供应商开始合作失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '供应商开始合作失败');
}
});
// 供应商终止合作API - /api/suppliers/:id/terminate
console.log('正在注册供应商终止合作API路由: /api/suppliers/:id/terminate');
app.post('/api/suppliers/:id/terminate', async (req, res) => {
console.log('收到供应商终止合作请求:', req.params, req.body);
try {
const connection = await pool.getConnection();
const userId = req.params.id;
const { reason } = req.body;
if (!userId) {
connection.release();
return sendResponse(res, false, null, '用户ID不能为空');
}
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentUser] = await connection.query(
'SELECT partnerstatus FROM users WHERE userId = ?',
[userId]
);
if (currentUser.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '供应商不存在');
}
if (currentUser[0].partnerstatus !== 'approved' && currentUser[0].partnerstatus !== 'incooperation') {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '只有审核通过或合作中的供应商才能终止合作');
}
// 更新状态和终止原因
await connection.query(
'UPDATE users SET partnerstatus = ?, terminate_reason = ? WHERE userId = ?',
['notcooperative', reason, userId]
);
// 提交事务
await connection.commit();
connection.release();
sendResponse(res, true, null, '供应商终止合作成功');
} catch (error) {
console.error('供应商终止合作失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '供应商终止合作失败');
}
});
// 导入图片处理工具
const ImageProcessor = require('./image-processor');
// 创建货源API - /api/supplies/create
console.log('正在注册创建货源API路由: /api/supplies/create');
app.post('/api/supplies/create', async (req, res) => {
console.log('收到创建货源请求:', req.body);
let connection;
try {
connection = await pool.getConnection();
const { productName, costprice, quantity, grossWeight, yolk, specification, quality, region, imageUrls, sellerId, supplyStatus, description, sourceType, contactId, category, producting } = req.body;
// 开始事务
await connection.beginTransaction();
// 验证必填字段
if (!productName || !costprice || !quantity || !supplyStatus || !sourceType) {
connection.release();
return sendResponse(res, false, null, '商品名称、采购价、最小起订量、货源状态和货源类型不能为空');
}
// 如果sellerId为空,设置一个默认值
if (!sellerId) {
sellerId = 'default_seller';
}
// 移除重复货源检测限制,允许创建相同货源
console.log('移除重复货源检测限制,允许创建相同货源...');
// 处理联系人信息
let productContact = '';
let contactPhone = '';
if (contactId) {
console.log('开始处理联系人信息,contactId:', contactId);
// 从userlogin数据库获取联系人信息
const userLoginConnection = await userLoginPool.getConnection();
const [personnelData] = await userLoginConnection.query(
'SELECT alias, phoneNumber FROM Personnel WHERE projectName = "销售员" AND phoneNumber IS NOT NULL AND phoneNumber != "" AND id = ?',
[parseInt(contactId)] // 使用contactId直接查询对应的联系人
);
userLoginConnection.release();
console.log('查询到的联系人数据:', personnelData);
if (personnelData && personnelData.length > 0) {
productContact = personnelData[0].alias || '';
contactPhone = personnelData[0].phoneNumber || '';
console.log('获取到的联系人信息:', productContact, contactPhone);
}
}
console.log('准备插入的联系人信息:', productContact, contactPhone);
// 生成唯一的productId
const productId = `product_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
// 处理媒体文件上传(图片和视频)
let uploadedImageUrls = [];
if (Array.isArray(imageUrls) && imageUrls.length > 0) {
console.log('开始处理媒体文件上传,共', imageUrls.length, '个文件');
for (const mediaUrl of imageUrls) {
if (mediaUrl.startsWith('data:image/') || mediaUrl.startsWith('data:video/')) {
// 处理DataURL
let base64Data, ext, fileType;
if (mediaUrl.startsWith('data:image/')) {
// 图片类型
base64Data = mediaUrl.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, '');
ext = mediaUrl.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png';
fileType = 'image';
} else {
// 视频类型
base64Data = mediaUrl.replace(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/, '');
ext = mediaUrl.match(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/)?.[1] || 'mp4';
fileType = 'video';
}
let buffer = Buffer.from(base64Data, 'base64');
const filename = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}.${ext}`;
try {
// 不再添加水印,前端已处理
console.log('【水印处理】前端已添加水印,跳过后端水印处理');
// 使用OSS上传媒体文件
const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `products/${productName || 'general'}`, fileType);
uploadedImageUrls.push(ossUrl);
console.log(`${fileType}上传成功:`, ossUrl);
} catch (uploadError) {
console.error(`${fileType}上传失败:`, uploadError.message);
// 继续上传其他文件,不中断流程
}
} else {
// 已经是URL,直接使用
uploadedImageUrls.push(mediaUrl);
}
}
console.log('媒体文件处理完成,成功上传', uploadedImageUrls.length, '个文件');
}
// 创建商品数据
const productData = {
productId,
sellerId: sellerId, // 使用前端传入的sellerId
productName,
category: category || '', // 添加种类
// 不再使用price字段,移除price字段
costprice: costprice || '', // 添加采购价
quantity: quantity, // 保持为逗号分隔的字符串,不转换为整数
grossWeight,
yolk,
specification,
quality,
producting: producting || '', // 添加产品包装
region,
status: 'published', // 直接上架,而不是审核中
supplyStatus: supplyStatus || '', // 预售/现货
sourceType: sourceType || '', // 平台货源/三方认证/三方未认证
description: description || '',
rejectReason: '',
imageUrls: uploadedImageUrls.length > 0 ? JSON.stringify(uploadedImageUrls) : '[]',
created_at: new Date(),
product_contact: productContact, // 添加联系人名称
contact_phone: contactPhone, // 添加联系人电话
autoOfflineTime: req.body.autoOfflineTime, // 自动下架时间
autoOfflineDays: req.body.autoOfflineDays, // 自动下架天数
autoOfflineHours: req.body.autoOfflineHours // 自动下架小时数
};
// 插入商品数据
let result;
try {
// 检查products表是否有quality字段
const [columns] = await connection.query('SHOW COLUMNS FROM products LIKE ?', ['quality']);
let insertQuery;
let insertParams;
if (columns.length === 0) {
// 没有quality字段,不包含quality字段的插入
insertQuery = 'INSERT INTO products (productId, sellerId, productName, category, freshness, costprice, quantity, grossWeight, yolk, specification, producting, region, status, supplyStatus, sourceType, description, rejectReason, imageUrls, created_at, audit_time, product_contact, contact_phone, autoOfflineTime, autoOfflineDays, autoOfflineHours) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
insertParams = [
productId, productData.sellerId, productName, category || '', req.body.freshness || '', costprice || '', quantity, grossWeight,
yolk, specification, producting, region, productData.status, productData.supplyStatus, productData.sourceType,
productData.description, productData.rejectReason, productData.imageUrls, new Date(), new Date(),
productContact, contactPhone, req.body.autoOfflineTime, req.body.autoOfflineDays, req.body.autoOfflineHours // 添加联系人信息和自动下架时间、小时数
];
} else {
// 有quality字段,包含quality字段的插入
insertQuery = 'INSERT INTO products (productId, sellerId, productName, category, freshness, costprice, quantity, grossWeight, yolk, specification, producting, quality, region, status, supplyStatus, sourceType, description, rejectReason, imageUrls, created_at, audit_time, product_contact, contact_phone, autoOfflineTime, autoOfflineDays, autoOfflineHours) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
insertParams = [
productId, productData.sellerId, productName, category || '', req.body.freshness || '', costprice || '', quantity, grossWeight,
yolk, specification, producting, quality, region, productData.status, productData.supplyStatus,
productData.sourceType, productData.description, productData.rejectReason, productData.imageUrls,
new Date(), new Date(), productContact, contactPhone, req.body.autoOfflineTime, req.body.autoOfflineDays, req.body.autoOfflineHours // 添加联系人信息和自动下架时间、小时数
];
}
result = await connection.query(insertQuery, insertParams);
} catch (insertError) {
await connection.rollback();
connection.release();
console.error('插入商品数据失败:', insertError.message);
console.error('SQL错误:', insertError.sqlMessage);
return sendResponse(res, false, null, `创建货源失败: ${insertError.sqlMessage}`);
}
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_create',
supplyId: result.insertId,
action: 'create',
data: {
id: result.insertId,
productId: productId,
productName,
category: category || '',
// 不再使用price字段,移除price字段
costprice: costprice || '',
quantity,
grossWeight,
yolk,
specification,
producting: producting || '',
region,
status: 'published',
supplyStatus: supplyStatus || '',
sourceType: sourceType || '',
description: description || '',
sellerId,
imageUrls: uploadedImageUrls
}
});
sendResponse(res, true, { productId: result.insertId }, '货源创建成功');
} catch (error) {
if (connection) {
try {
await connection.rollback();
connection.release();
} catch (rollbackError) {
console.error('回滚失败:', rollbackError.message);
}
}
console.error('创建货源失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, `创建货源失败: ${error.message}`);
}
});
// 媒体文件上传API - /api/upload-media
console.log('正在注册媒体文件上传API路由: /api/upload-media');
app.post('/api/upload-media', async (req, res) => {
console.log('收到媒体文件上传请求:', req.body);
try {
const { fileData, fileName, folder = 'general' } = req.body;
// 验证参数
if (!fileData) {
return sendResponse(res, false, null, '文件数据不能为空');
}
let base64Data, ext, fileType;
if (fileData.startsWith('data:image/')) {
// 图片类型
base64Data = fileData.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, '');
ext = fileData.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png';
fileType = 'image';
} else if (fileData.startsWith('data:video/')) {
// 视频类型
base64Data = fileData.replace(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/, '');
ext = fileData.match(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/)?.[1] || 'mp4';
fileType = 'video';
} else {
return sendResponse(res, false, null, '不支持的文件类型');
}
let buffer = Buffer.from(base64Data, 'base64');
const filename = fileName || `${Date.now()}-${Math.random().toString(36).substr(2, 9)}.${ext}`;
try {
// 使用OSS上传媒体文件
const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `uploads/${folder}`, fileType);
console.log(`${fileType}上传成功:`, ossUrl);
sendResponse(res, true, {
url: ossUrl,
fileType: fileType,
message: `${fileType}上传成功`
}, `${fileType}上传成功`);
} catch (uploadError) {
console.error(`${fileType}上传失败:`, uploadError.message);
sendResponse(res, false, null, `${fileType}上传失败: ${uploadError.message}`);
}
} catch (error) {
console.error('媒体文件上传API错误:', error.message);
sendResponse(res, false, null, `媒体文件上传失败: ${error.message}`);
}
});
// 图片上传API - /api/upload-image(兼容旧接口)
console.log('正在注册图片上传API路由: /api/upload-image');
app.post('/api/upload-image', async (req, res) => {
console.log('收到图片上传请求,转发到媒体文件上传API');
// 将请求转发到媒体文件上传API
const uploadMediaHandler = app._router.stack.find(layer =>
layer.route && layer.route.path === '/api/upload-media' && layer.route.methods['post']
);
if (uploadMediaHandler && uploadMediaHandler.route && uploadMediaHandler.route.stack[0]) {
return uploadMediaHandler.route.stack[0].handle(req, res);
} else {
sendResponse(res, false, null, '图片上传API内部错误');
}
});
// 视频上传API - /api/upload-video(专门的视频上传接口)
console.log('正在注册视频上传API路由: /api/upload-video');
app.post('/api/upload-video', async (req, res) => {
console.log('收到视频上传请求,转发到媒体文件上传API');
// 将请求转发到媒体文件上传API
const uploadMediaHandler = app._router.stack.find(layer =>
layer.route && layer.route.path === '/api/upload-media' && layer.route.methods['post']
);
if (uploadMediaHandler && uploadMediaHandler.route && uploadMediaHandler.route.stack[0]) {
return uploadMediaHandler.route.stack[0].handle(req, res);
} else {
sendResponse(res, false, null, '视频上传API内部错误');
}
});
// 获取审核失败原因API - /api/supplies/:id/reject-reason
console.log('正在注册获取审核失败原因API路由: /api/supplies/:id/reject-reason');
app.get('/api/supplies/:id/reject-reason', async (req, res) => {
console.log('收到获取审核失败原因请求:', req.params.id);
try {
const connection = await pool.getConnection();
const supplyId = req.params.id;
// 查询该货源的拒绝原因
const [supply] = await connection.query(
'SELECT rejectReason FROM products WHERE id = ?',
[supplyId]
);
connection.release();
if (supply.length === 0) {
return sendResponse(res, false, null, '货源不存在');
}
sendResponse(res, true, {
rejectReason: supply[0].rejectReason
}, '获取审核失败原因成功');
} catch (error) {
console.error('获取审核失败原因失败:', error.message);
sendResponse(res, false, null, '获取审核失败原因失败');
}
});
// 下架货源API - /api/supplies/:id/unpublish
console.log('正在注册下架货源API路由: /api/supplies/:id/unpublish');
app.post('/api/supplies/:id/unpublish', async (req, res) => {
console.log('收到下架货源请求:', req.params.id);
try {
const connection = await pool.getConnection();
const productId = req.params.id;
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 更新状态为已下架,并将标签设置为已锁定
await connection.query(
'UPDATE products SET status = ?, label = 1, updated_at = NOW() WHERE id = ?',
['sold_out', productId]
);
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'unpublish',
status: 'sold_out'
});
sendResponse(res, true, null, '货源下架成功');
} catch (error) {
console.error('下架货源失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '货源下架失败');
}
});
// 上架货源API - /api/supplies/:id/publish
console.log('正在注册上架货源API路由: /api/supplies/:id/publish');
app.post('/api/supplies/:id/publish', async (req, res) => {
console.log('收到上架货源请求:', req.params.id);
try {
const connection = await pool.getConnection();
const productId = req.params.id;
// 开始事务
await connection.beginTransaction();
// 检查当前状态和label
const [currentProduct] = await connection.query(
'SELECT status, label FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 检查是否被锁定
if (currentProduct[0].label === 1) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源已被锁定,无法上架');
}
// 更新状态为已发布(直接上架,不需要审核),同时更新updated_at和autoOfflineHours
await connection.query(
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
['published', productId]
);
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'publish',
status: 'published'
});
sendResponse(res, true, null, '货源上架成功');
} catch (error) {
console.error('上架货源失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '货源上架失败');
}
});
// 删除货源API - /api/supplies/:id/delete
console.log('正在注册删除货源API路由: /api/supplies/:id/delete');
app.post('/api/supplies/:id/delete', async (req, res) => {
console.log('收到删除货源请求:', req.params.id);
try {
const connection = await pool.getConnection();
const productId = req.params.id;
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 将货源状态改为hidden,而不是删除
await connection.query(
'UPDATE products SET status = ? WHERE id = ?',
['hidden', productId]
);
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_update',
supplyId: productId,
action: 'update'
});
sendResponse(res, true, null, '货源已删除');
} catch (error) {
console.error('删除货源失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '货源删除失败');
}
});
// 更新货源状态API - /api/supplies/:id
console.log('正在注册更新货源状态API路由: /api/supplies/:id');
app.put('/api/supplies/:id', async (req, res) => {
console.log('收到更新货源状态请求:', req.params.id, req.body);
try {
const connection = await pool.getConnection();
const productId = req.params.id;
const { status } = req.body;
// 验证状态参数
if (!status) {
connection.release();
return sendResponse(res, false, null, '状态不能为空');
}
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 更新状态
await connection.query(
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
[status, productId]
);
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'update_status',
status: status
});
sendResponse(res, true, null, '状态更新成功');
} catch (error) {
console.error('更新货源状态失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '更新货源状态失败');
}
});
// 编辑货源API - /api/supplies/:id/edit
console.log('正在注册编辑货源API路由: /api/supplies/:id/edit');
app.put('/api/supplies/:id/edit', async (req, res) => {
console.log('收到编辑货源请求:', req.params.id);
console.log('请求体中的category:', req.body.category);
console.log('请求体中的sourceType:', req.body.sourceType);
console.log('请求体中的freshness:', req.body.freshness);
try {
const connection = await pool.getConnection();
const productId = req.params.id;
const { productName, costprice, quantity, grossWeight, yolk, specification, supplyStatus, description, region, contactId, producting, imageUrls, autoOfflineTime, category, sourceType, freshness, autoOfflineHours } = req.body;
// 开始事务
await connection.beginTransaction();
// 检查当前状态和label
const [currentProduct] = await connection.query(
'SELECT status, label FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 检查是否被锁定
if (currentProduct[0].label === 1) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源已被锁定,无法编辑');
}
// 处理联系人信息:只在有新联系人ID且能查询到时才更新,否则保持原有值
let productContact = null;
let contactPhone = null;
// 先查询原有联系人信息
const [existingProduct] = await connection.query(
'SELECT product_contact, contact_phone FROM products WHERE id = ?',
[productId]
);
// 如果没有提供contactId或contactId为空,使用原有联系人信息
if (!contactId || contactId === '') {
productContact = existingProduct[0].product_contact;
contactPhone = existingProduct[0].contact_phone;
} else {
// 从userlogin数据库获取联系人信息
const userLoginConnection = await userLoginPool.getConnection();
const [personnelData] = await userLoginConnection.query(
'SELECT alias, phoneNumber FROM Personnel WHERE projectName = "销售员" AND phoneNumber IS NOT NULL AND phoneNumber != "" AND id = ?',
[parseInt(contactId)] // 使用contactId直接查询对应的联系人
);
userLoginConnection.release();
if (personnelData && personnelData.length > 0) {
// 成功查询到联系人,使用新联系人信息
productContact = personnelData[0].alias || '';
contactPhone = personnelData[0].phoneNumber || '';
} else {
// 未查询到联系人,使用原有联系人信息
productContact = existingProduct[0].product_contact;
contactPhone = existingProduct[0].contact_phone;
}
}
// 处理媒体文件上传(图片和视频)
let uploadedImageUrls = [];
if (imageUrls && Array.isArray(imageUrls) && imageUrls.length > 0) {
console.log('开始处理编辑媒体文件上传,共', imageUrls.length, '个文件');
for (const mediaUrl of imageUrls) {
if (mediaUrl.startsWith('data:image/') || mediaUrl.startsWith('data:video/')) {
// 处理DataURL
let base64Data, ext, fileType;
if (mediaUrl.startsWith('data:image/')) {
// 图片类型
base64Data = mediaUrl.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, '');
ext = mediaUrl.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png';
fileType = 'image';
} else {
// 视频类型
base64Data = mediaUrl.replace(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/, '');
ext = mediaUrl.match(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/)?.[1] || 'mp4';
fileType = 'video';
}
let buffer = Buffer.from(base64Data, 'base64');
const filename = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}.${ext}`;
try {
// 不再添加水印,前端已处理
console.log('【水印处理】前端已添加水印,跳过后端水印处理');
// 使用OSS上传媒体文件
const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `products/${productName || 'general'}`, fileType);
uploadedImageUrls.push(ossUrl);
console.log(`${fileType}上传成功:`, ossUrl);
} catch (uploadError) {
console.error(`${fileType}上传失败:`, uploadError.message);
// 继续上传其他文件,不中断流程
}
} else {
// 已经是URL,直接使用
uploadedImageUrls.push(mediaUrl);
}
}
console.log('媒体文件处理完成,成功上传', uploadedImageUrls.length, '个文件');
}
// 处理数量:保存所有数量值,与规格保持一致
let quantityValue = quantity; // 直接保存前端提交的数量字符串
// 如果是数字,转换为字符串
if (typeof quantity === 'number') {
quantityValue = quantity.toString();
} else if (typeof quantity === 'string' && !isNaN(parseFloat(quantity)) && isFinite(quantity)) {
// 如果是单个数字字符串,直接使用
quantityValue = quantity;
}
// 只有当有新上传的图片时,才更新imageUrls,否则保留原有值
let imageUrlsToUpdate = null;
if (uploadedImageUrls.length > 0) {
imageUrlsToUpdate = JSON.stringify(uploadedImageUrls);
} else {
// 如果没有新上传的图片,查询原有图片URL
const [existingProduct] = await connection.query(
'SELECT imageUrls FROM products WHERE id = ?',
[productId]
);
imageUrlsToUpdate = existingProduct[0].imageUrls;
}
// 打印用于调试的更新参数
console.log('更新参数调试:');
console.log('productName:', productName);
// 不再使用price字段,移除price调试日志
console.log('costprice:', costprice || '');
console.log('quantityValue:', quantityValue);
console.log('grossWeight:', grossWeight);
console.log('yolk:', yolk);
console.log('specification:', specification);
console.log('producting:', producting);
console.log('supplyStatus:', supplyStatus);
console.log('description:', description);
console.log('region:', region);
console.log('category:', category);
console.log('sourceType:', sourceType);
console.log('freshness:', freshness);
console.log('productContact:', productContact);
console.log('contactPhone:', contactPhone);
console.log('imageUrlsToUpdate:', imageUrlsToUpdate);
console.log('autoOfflineTime:', autoOfflineTime || null);
console.log('autoOfflineHours:', autoOfflineHours || 24);
console.log('productId:', productId);
// 更新货源信息
const updateQuery = `
UPDATE products
SET productName = ?, costprice = ?, quantity = ?, grossWeight = ?,
yolk = ?, specification = ?, producting = ?, supplyStatus = ?, description = ?, region = ?,
category = ?, sourceType = ?, freshness = ?,
product_contact = ?, contact_phone = ?, imageUrls = ?,
autoOfflineTime = ?, autoOfflineHours = ?, updated_at = ?
WHERE id = ?
`;
const [result] = await connection.query(updateQuery, [
productName, costprice || '', quantityValue, grossWeight,
yolk, specification, producting, supplyStatus, description, region,
category, sourceType, freshness,
productContact, contactPhone, imageUrlsToUpdate,
autoOfflineTime || null, // 自动下架时间
autoOfflineHours || 24, // 默认24小时
new Date(), // 更新updated_at字段
productId
]);
console.log('更新结果:', result);
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_update',
supplyId: productId,
action: 'update',
data: {
id: productId,
productName,
// 不再使用price字段,移除price字段
costprice: costprice || '',
quantity,
grossWeight,
yolk,
specification,
category,
sourceType,
freshness,
producting,
supplyStatus,
description,
region,
imageUrls: uploadedImageUrls.length > 0 ? uploadedImageUrls : imageUrls
}
});
sendResponse(res, true, null, '货源编辑成功');
} catch (error) {
console.error('编辑货源失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '货源编辑失败');
}
});
// 供应商列表查询API - /api/suppliers
console.log('正在注册供应商列表查询API路由: /api/suppliers');
app.get('/api/suppliers', async (req, res) => {
console.log('收到供应商列表查询请求:', req.query);
try {
const connection = await pool.getConnection();
const { page = 1, pageSize = 10, status = '', keyword = '', phoneNumber = '' } = req.query;
// 构建查询条件
let whereClause = '';
let params = [];
// 添加状态筛选
if (status) {
whereClause += ` WHERE partnerstatus = ?`;
params.push(status);
}
// 添加关键词搜索
if (keyword) {
whereClause += status ? ' AND' : ' WHERE';
whereClause += ` (username LIKE ? OR company LIKE ? OR phoneNumber LIKE ?)`;
params.push(`%${keyword}%`, `%${keyword}%`, `%${keyword}%`);
}
// 添加手机号搜索(优先级高于keyword中的手机号搜索)
if (phoneNumber) {
whereClause += (status || keyword) ? ' AND' : ' WHERE';
whereClause += ` phoneNumber LIKE ?`;
params.push(`%${phoneNumber}%`);
}
// 获取总数
const [totalResult] = await connection.query(
`SELECT COUNT(*) as total FROM users${whereClause}`,
params
);
const total = totalResult[0].total;
// 计算分页
const offset = (page - 1) * pageSize;
params.push(parseInt(pageSize), offset);
// 查询供应商列表
const [suppliers] = await connection.query(
`SELECT userId, phoneNumber, province, city, district, detailedaddress, company, collaborationid, cooperation, businesslicenseurl, proofurl, brandurl, partnerstatus, reasonforfailure, reject_reason, terminate_reason, audit_time
FROM users${whereClause}
ORDER BY audit_time DESC LIMIT ? OFFSET ?`,
params
);
connection.release();
sendResponse(res, true, {
list: suppliers,
total,
page: parseInt(page),
pageSize: parseInt(pageSize)
}, '查询成功');
} catch (error) {
console.error('供应商列表查询失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '供应商列表查询失败');
}
});
// 首页路由
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'Reject.html'));
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error('服务器错误:', err.message);
console.error('错误详情:', err);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
});
// 启动服务器
async function startServer() {
try {
await initDatabase();
server.listen(PORT, () => {
console.log(`服务器已启动,监听端口 ${PORT}`);
console.log(`访问地址: http://localhost:${PORT}`);
console.log(`WebSocket服务已启动,端口: ${PORT}`);
});
} catch (error) {
console.error('服务器启动失败:', error.message);
console.error('错误详情:', error);
// 如果启动失败,尝试重新启动
setTimeout(() => {
console.log('尝试重新启动服务器...');
startServer();
}, 5000);
}
}
// 确保数据库结构
async function ensureDatabaseSchema() {
console.log('开始执行数据库结构检查...');
try {
const connection = await pool.getConnection();
console.log('获取数据库连接成功');
// 检查users表是否有必要的字段
console.log('检查users表是否有partnerstatus字段...');
const [partnerStatusColumns] = await connection.query(
'SHOW COLUMNS FROM `users` LIKE ?',
['partnerstatus']
);
console.log('检查表字段结果:', partnerStatusColumns.length > 0 ? '已存在' : '不存在');
if (partnerStatusColumns.length === 0) {
console.log('添加partnerstatus字段到users表...');
await connection.query(
'ALTER TABLE `users` ADD COLUMN partnerstatus VARCHAR(50) DEFAULT "underreview" COMMENT "合作商状态"'
);
console.log('partnerstatus字段添加成功');
}
console.log('检查users表是否有reject_reason字段...');
const [rejectReasonColumns] = await connection.query(
'SHOW COLUMNS FROM `users` LIKE ?',
['reject_reason']
);
console.log('检查表字段结果:', rejectReasonColumns.length > 0 ? '已存在' : '不存在');
if (rejectReasonColumns.length === 0) {
console.log('添加reject_reason字段到users表...');
await connection.query(
'ALTER TABLE `users` ADD COLUMN reject_reason TEXT COMMENT "拒绝理由"'
);
console.log('reject_reason字段添加成功');
}
console.log('检查users表是否有terminate_reason字段...');
const [terminateReasonColumns] = await connection.query(
'SHOW COLUMNS FROM `users` LIKE ?',
['terminate_reason']
);
console.log('检查表字段结果:', terminateReasonColumns.length > 0 ? '已存在' : '不存在');
if (terminateReasonColumns.length === 0) {
console.log('添加terminate_reason字段到users表...');
await connection.query(
'ALTER TABLE `users` ADD COLUMN terminate_reason TEXT COMMENT "终止合作理由"'
);
console.log('terminate_reason字段添加成功');
}
console.log('检查users表是否有audit_time字段...');
const [userAuditTimeColumns] = await connection.query(
'SHOW COLUMNS FROM `users` LIKE ?',
['audit_time']
);
console.log('检查表字段结果:', userAuditTimeColumns.length > 0 ? '已存在' : '不存在');
if (userAuditTimeColumns.length === 0) {
console.log('添加audit_time字段到users表...');
await connection.query(
'ALTER TABLE `users` ADD COLUMN audit_time DATETIME COMMENT "审核时间"'
);
console.log('audit_time字段添加成功');
}
// 检查表是否有rejectReason字段
console.log('检查表products是否有rejectReason字段...');
const [columns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['rejectReason']
);
console.log('检查表字段结果:', columns.length > 0 ? '已存在' : '不存在');
if (columns.length === 0) {
console.log('添加rejectReason字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN rejectReason TEXT COMMENT "拒绝理由"'
);
console.log('rejectReason字段添加成功');
}
// 检查表是否有autoOfflineTime字段
console.log('检查表products是否有autoOfflineTime字段...');
const [autoOfflineTimeColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['autoOfflineTime']
);
console.log('检查表字段结果:', autoOfflineTimeColumns.length > 0 ? '已存在' : '不存在');
if (autoOfflineTimeColumns.length === 0) {
console.log('添加autoOfflineTime字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN autoOfflineTime DATETIME COMMENT "自动下架时间"'
);
console.log('autoOfflineTime字段添加成功');
}
// 检查表是否有autoOfflineDays字段
console.log('检查表products是否有autoOfflineDays字段...');
const [autoOfflineDaysColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['autoOfflineDays']
);
console.log('检查表字段结果:', autoOfflineDaysColumns.length > 0 ? '已存在' : '不存在');
if (autoOfflineDaysColumns.length === 0) {
console.log('添加autoOfflineDays字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN autoOfflineDays INT COMMENT "自动下架天数"'
);
console.log('autoOfflineDays字段添加成功');
}
// 检查表是否有autoOfflineHours字段
console.log('检查表products是否有autoOfflineHours字段...');
const [autoOfflineHoursColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['autoOfflineHours']
);
console.log('检查表字段结果:', autoOfflineHoursColumns.length > 0 ? '已存在' : '不存在');
if (autoOfflineHoursColumns.length === 0) {
console.log('添加autoOfflineHours字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN autoOfflineHours FLOAT COMMENT "自动下架小时数"'
);
console.log('autoOfflineHours字段添加成功');
}
// 检查表是否有supplyStatus字段
console.log('检查表products是否有supplyStatus字段...');
const [supplyStatusColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['supplyStatus']
);
console.log('检查表字段结果:', supplyStatusColumns.length > 0 ? '已存在' : '不存在');
if (supplyStatusColumns.length === 0) {
console.log('添加supplyStatus字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN supplyStatus VARCHAR(50) COMMENT "货源状态"'
);
console.log('supplyStatus字段添加成功');
}
// 检查表是否有sourceType字段
console.log('检查表products是否有sourceType字段...');
const [sourceTypeColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['sourceType']
);
console.log('检查表字段结果:', sourceTypeColumns.length > 0 ? '已存在' : '不存在');
if (sourceTypeColumns.length === 0) {
console.log('添加sourceType字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN sourceType VARCHAR(50) COMMENT "货源类型"'
);
console.log('sourceType字段添加成功');
}
// 检查表是否有category字段
console.log('检查表products是否有category字段...');
const [categoryColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['category']
);
console.log('检查表字段结果:', categoryColumns.length > 0 ? '已存在' : '不存在');
if (categoryColumns.length === 0) {
console.log('添加category字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN category VARCHAR(50) COMMENT "种类"'
);
console.log('category字段添加成功');
}
// 检查表是否有audit_time字段
console.log('检查表products是否有audit_time字段...');
const [auditTimeColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['audit_time']
);
console.log('检查表字段结果:', auditTimeColumns.length > 0 ? '已存在' : '不存在');
if (auditTimeColumns.length === 0) {
console.log('添加audit_time字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN audit_time DATETIME COMMENT "审核时间"'
);
console.log('audit_time字段添加成功');
}
// 检查表是否有producting字段
console.log('检查表products是否有producting字段...');
const [productingColumns] = await connection.query(
'SHOW COLUMNS FROM `products` LIKE ?',
['producting']
);
console.log('检查表字段结果:', productingColumns.length > 0 ? '已存在' : '不存在');
if (productingColumns.length === 0) {
console.log('添加producting字段到products表...');
await connection.query(
'ALTER TABLE `products` ADD COLUMN producting VARCHAR(255) COMMENT "产品包装"'
);
console.log('producting字段添加成功');
}
// 检查audit_logs表是否存在,如果不存在则创建
console.log('检查audit_logs表是否存在...');
const [tables] = await connection.query(
"SHOW TABLES LIKE 'audit_logs'"
);
console.log('检查表存在性结果:', tables.length > 0 ? '已存在' : '不存在');
if (tables.length === 0) {
console.log('创建audit_logs表...');
await connection.query(`
CREATE TABLE audit_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
supply_id VARCHAR(50) NOT NULL,
action VARCHAR(20) NOT NULL COMMENT 'approve或reject',
user_id VARCHAR(50) NOT NULL COMMENT '操作人ID',
remark TEXT COMMENT '备注信息',
created_at DATETIME NOT NULL,
INDEX idx_supply_id (supply_id),
INDEX idx_created_at (created_at)
) COMMENT '审核操作日志表'
`);
console.log('audit_logs表创建成功');
}
connection.release();
console.log('数据库结构检查完成');
} catch (error) {
console.error('数据库结构检查失败:', error.message);
console.error('错误详情:', error);
}
}
// 启动服务器
startServer();
// 优雅关闭
process.on('SIGINT', async () => {
console.log('正在关闭服务器...');
if (pool) {
try {
await pool.end();
console.log('数据库连接池已关闭');
} catch (error) {
console.error('关闭数据库连接池失败:', error.message);
}
}
console.log('服务器已关闭');
process.exit(0);
});
module.exports = app;