const express = require('express'); const bodyParser = require('body-parser'); const cors = require('cors'); const mysql = require('mysql2/promise'); const path = require('path'); const OssUploader = require('./oss-uploader'); const app = express(); const PORT = 3000; // 配置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))); // 数据库配置 const dbConfig = { host: '1.95.162.61', user: 'root', password: 'schl@2025', // 请替换为实际的数据库密码 database: 'wechat_app', // 连接到wechat_app数据库 waitForConnections: true, connectionLimit: 10, queueLimit: 0 }; // userlogin数据库配置 const userLoginDbConfig = { host: '1.95.162.61', user: 'root', password: 'schl@2025', // 请替换为实际的数据库密码 database: 'userlogin', // 连接到userlogin数据库 waitForConnections: true, connectionLimit: 10, queueLimit: 0 }; // 创建数据库连接池 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 = []; // 添加搜索条件 if (actualSearch) { whereClause += ` WHERE (p.id LIKE ? OR p.productId LIKE ? OR p.productName LIKE ?)`; params.push(`%${actualSearch}%`, `%${actualSearch}%`, `%${actualSearch}%`); } // 添加手机号搜索 if (phoneNumber) { whereClause += actualSearch ? ' AND' : ' WHERE'; whereClause += ` u.phoneNumber LIKE ?`; params.push(`%${phoneNumber}%`); } // 添加状态筛选 if (status) { whereClause += (actualSearch || phoneNumber) ? ' AND' : ' WHERE'; whereClause += ` status = ?`; params.push(status); } // 添加sellerId筛选,只返回指定用户的货源 const { sellerId } = req.query; if (sellerId) { whereClause += (actualSearch || phoneNumber || status) ? ' AND' : ' WHERE'; whereClause += ` 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. 在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 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相同的返回格式 const contacts = personnelData.map((person, index) => ({ id: index + 1, 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 app.put('/api/supplies/:id/contact', async (req, res) => { try { const { id } = req.params; const { productContact, contactPhone } = req.body; // 移除"联系人"前缀和"销售员 - "前缀 const processedProductContact = productContact.replace(/^(联系人|销售员\s*-\s*)/g, '').trim(); const connection = await pool.getConnection(); // 更新产品的联系人信息 const [result] = await connection.query( 'UPDATE products SET product_contact = ?, contact_phone = ? WHERE id = ?', [processedProductContact, contactPhone, id] ); connection.release(); if (result.affectedRows === 0) { sendResponse(res, false, null, '未找到指定的产品'); return; } sendResponse(res, true, null, '产品联系人信息更新成功'); } 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, price, quantity, grossWeight, yolk, specification, quality, region, imageUrls, sellerId, supplyStatus, description, sourceType, contactId, category, producting } = req.body; // 开始事务 await connection.beginTransaction(); // 验证必填字段 if (!productName || !price || !quantity || !supplyStatus || !sourceType) { connection.release(); return sendResponse(res, false, null, '商品名称、价格、最小起订量、货源状态和货源类型不能为空'); } // 如果sellerId为空,设置一个默认值 if (!sellerId) { sellerId = 'default_seller'; } // 处理联系人信息 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.toString(), // 确保是varchar类型 quantity: parseInt(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 // 添加联系人电话 }; // 插入商品数据 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, price, quantity, grossWeight, yolk, specification, producting, region, status, supplyStatus, sourceType, description, rejectReason, imageUrls, created_at, audit_time, product_contact, contact_phone) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; insertParams = [ productId, productData.sellerId, productName, category || '', price.toString(), parseInt(quantity), grossWeight, yolk, specification, producting, region, productData.status, productData.supplyStatus, productData.sourceType, productData.description, productData.rejectReason, productData.imageUrls, new Date(), new Date(), productContact, contactPhone // 添加联系人信息 ]; } else { // 有quality字段,包含quality字段的插入 insertQuery = 'INSERT INTO products (productId, sellerId, productName, category, price, quantity, grossWeight, yolk, specification, quality, producting, region, status, supplyStatus, sourceType, description, rejectReason, imageUrls, created_at, audit_time, product_contact, contact_phone) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; insertParams = [ productId, productData.sellerId, productName, category || '', price.toString(), parseInt(quantity), grossWeight, yolk, specification, quality, producting, region, productData.status, productData.supplyStatus, productData.sourceType, productData.description, productData.rejectReason, productData.imageUrls, new Date(), new Date(), productContact, contactPhone // 添加联系人信息 ]; } 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(); 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 = ? WHERE id = ?', ['hidden', productId] ); // 提交事务 await connection.commit(); connection.release(); 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(); // 检查当前状态 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 = ? WHERE id = ?', ['pending_review', productId] ); // 提交事务 await connection.commit(); connection.release(); 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, '货源不存在'); } // 删除货源 await connection.query( 'DELETE FROM products WHERE id = ?', [productId] ); // 提交事务 await connection.commit(); connection.release(); 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, req.body); try { const connection = await pool.getConnection(); const productId = req.params.id; const { productName, price, quantity, grossWeight, yolk, specification, supplyStatus, description, region, contactId, producting } = req.body; // 开始事务 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, '货源不存在'); } // 处理联系人信息 let productContact = ''; let contactPhone = ''; if (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(); if (personnelData && personnelData.length > 0) { productContact = personnelData[0].alias || ''; contactPhone = personnelData[0].phoneNumber || ''; } } // 处理媒体文件上传(图片和视频) let uploadedImageUrls = []; const { imageUrls } = req.body; 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, '个文件'); } // 如果有新上传的图片,更新imageUrls字段 if (uploadedImageUrls.length > 0) { await connection.query( 'UPDATE products SET imageUrls = ? WHERE id = ?', [JSON.stringify(uploadedImageUrls), productId] ); } // 更新货源信息 const updateQuery = ` UPDATE products SET productName = ?, price = ?, quantity = ?, grossWeight = ?, yolk = ?, specification = ?, supplyStatus = ?, description = ?, region = ?, producting = ?, product_contact = ?, contact_phone = ? WHERE id = ? `; await connection.query(updateQuery, [ productName, price.toString(), parseInt(quantity), grossWeight, yolk, specification, supplyStatus, description, region, producting, productContact, contactPhone, productId ]); // 提交事务 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 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(); app.listen(PORT, () => { console.log(`服务器已启动,监听端口 ${PORT}`); console.log(`访问地址: http://localhost:${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字段添加成功'); } // 检查表是否有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;