diff --git a/server-example/server-mysql.js b/server-example/server-mysql.js index 5954461..122a1e9 100644 --- a/server-example/server-mysql.js +++ b/server-example/server-mysql.js @@ -381,6 +381,331 @@ app.get('/api/cover', async (req, res) => { } }); +// 订单列表API端点 - 根据电话号码查询订单 +app.post('/api/orders/list', async (req, res) => { + try { + const { phoneNumber, startDate, endDate, paymentStatus, page = 1, pageSize = 10 } = req.body; + console.log('获取订单列表请求:', { phoneNumber, startDate, endDate, paymentStatus, page, pageSize }); + console.log('请求体完整内容:', req.body); + + if (!phoneNumber) { + return res.status(400).json({ + success: false, + code: 400, + message: '用户电话号码不能为空' + }); + } + + // 构建查询条件 + const whereCondition = { + phone: phoneNumber + }; + + // 添加时间范围过滤 + if (startDate) { + whereCondition.order_date = { + ...whereCondition.order_date, + [Op.gte]: startDate + }; + console.log('添加开始日期过滤:', startDate); + } + + if (endDate) { + whereCondition.order_date = { + ...whereCondition.order_date, + [Op.lte]: endDate + }; + console.log('添加结束日期过滤:', endDate); + } + + // 添加付款状态过滤 + if (paymentStatus) { + whereCondition.payment_status = paymentStatus; + console.log('添加付款状态过滤:', paymentStatus); + } + + console.log('最终查询条件:', whereCondition); + + // 计算分页偏移量 + const offset = (page - 1) * pageSize; + console.log('分页参数:', { page, pageSize, offset }); + + // 查询trade_library数据库中的订单主表,根据电话号码和时间范围过滤 + const orders = await JdSalesMain.findAll({ + where: whereCondition, + include: [ + { + model: JdSalesSub, + as: 'subItems', + attributes: [ + 'sub_id', + 'product_name', + 'batch_no', + 'unit', + 'yolk', + 'specification', + 'sales_pieces', + 'sales_weight', + 'unit_price', + 'sales_amount', + 'discount_amount', + 'rebate_amount', + 'discount_reason' + ] + } + ], + order: [['order_date', 'DESC']], + limit: pageSize, + offset: offset + }); + + console.log('查询到的订单数量:', orders.length); + + // 格式化响应数据 + const formattedOrders = orders.map(order => { + const orderData = order.toJSON(); + + // 确保order_date是字符串格式 + if (orderData.order_date instanceof Date) { + orderData.order_date = orderData.order_date.toISOString().split('T')[0]; + } + + // 确保数值字段正确格式化 + orderData.total_pieces = parseInt(orderData.total_pieces) || 0; + orderData.total_weight = parseFloat(orderData.total_weight) || 0; + orderData.total_amount = parseFloat(orderData.total_amount) || 0; + + // 格式化子项数据 + if (orderData.subItems) { + orderData.subItems = orderData.subItems.map(subItem => ({ + ...subItem, + sales_pieces: parseInt(subItem.sales_pieces) || 0, + sales_weight: parseFloat(subItem.sales_weight) || 0, + unit_price: parseFloat(subItem.unit_price) || 0, + sales_amount: parseFloat(subItem.sales_amount) || 0, + discount_amount: parseFloat(subItem.discount_amount) || 0, + rebate_amount: parseFloat(subItem.rebate_amount) || 0 + })); + } + + return orderData; + }); + + res.status(200).json({ + success: true, + code: 200, + message: '获取订单列表成功', + data: { + orders: formattedOrders, + totalCount: formattedOrders.length + } + }); + + } catch (error) { + console.error('获取订单列表失败:', error); + res.status(500).json({ + success: false, + code: 500, + message: '获取订单列表失败: ' + error.message + }); + } +}); + +// 订单详情API端点 - 根据订单ID查询订单详情 +app.get('/api/orders/detail/:dataid', async (req, res) => { + try { + const { dataid } = req.params; + console.log('获取订单详情请求:', { dataid }); + + if (!dataid) { + return res.status(400).json({ + success: false, + code: 400, + message: '订单ID不能为空' + }); + } + + // 查询订单详情 + const order = await JdSalesMain.findOne({ + where: { + dataid + }, + include: [ + { + model: JdSalesSub, + as: 'subItems', + attributes: [ + 'sub_id', + 'product_name', + 'batch_no', + 'unit', + 'yolk', + 'specification', + 'sales_pieces', + 'sales_weight', + 'unit_price', + 'sales_amount', + 'discount_amount', + 'rebate_amount', + 'discount_reason' + ] + } + ] + }); + + if (!order) { + return res.status(404).json({ + success: false, + code: 404, + message: '订单不存在' + }); + } + + // 格式化响应数据 + const orderData = order.toJSON(); + + // 确保order_date是字符串格式 + if (orderData.order_date instanceof Date) { + orderData.order_date = orderData.order_date.toISOString().split('T')[0]; + } + + // 确保数值字段正确格式化 + orderData.total_pieces = parseInt(orderData.total_pieces) || 0; + orderData.total_weight = parseFloat(orderData.total_weight) || 0; + orderData.total_amount = parseFloat(orderData.total_amount) || 0; + + // 格式化子项数据 + if (orderData.subItems) { + orderData.subItems = orderData.subItems.map(subItem => ({ + ...subItem, + sales_pieces: parseInt(subItem.sales_pieces) || 0, + sales_weight: parseFloat(subItem.sales_weight) || 0, + unit_price: parseFloat(subItem.unit_price) || 0, + sales_amount: parseFloat(subItem.sales_amount) || 0, + discount_amount: parseFloat(subItem.discount_amount) || 0, + rebate_amount: parseFloat(subItem.rebate_amount) || 0 + })); + } + + res.status(200).json({ + success: true, + code: 200, + message: '获取订单详情成功', + data: orderData + }); + + } catch (error) { + console.error('获取订单详情失败:', error); + res.status(500).json({ + success: false, + code: 500, + message: '获取订单详情失败: ' + error.message + }); + } +}); + +// 订单统计API端点 - 获取完整的订单统计数据(不分页) +app.post('/api/orders/statistics', async (req, res) => { + try { + const { phoneNumber, startDate, endDate, paymentStatus } = req.body; + console.log('获取订单统计请求:', { phoneNumber, startDate, endDate, paymentStatus }); + + if (!phoneNumber) { + return res.status(400).json({ + success: false, + code: 400, + message: '用户电话号码不能为空' + }); + } + + // 构建查询条件 + const whereCondition = { + phone: phoneNumber + }; + + // 添加时间范围过滤 + if (startDate) { + whereCondition.order_date = { + ...whereCondition.order_date, + [Op.gte]: startDate + }; + } + + if (endDate) { + whereCondition.order_date = { + ...whereCondition.order_date, + [Op.lte]: endDate + }; + } + + // 添加付款状态过滤 + if (paymentStatus) { + whereCondition.payment_status = paymentStatus; + } + + console.log('统计查询条件:', whereCondition); + + // 查询所有符合条件的订单(不分页) + const allOrders = await JdSalesMain.findAll({ + where: whereCondition, + order: [['order_date', 'DESC']] + }); + + console.log('统计查询到的订单数量:', allOrders.length); + + // 计算统计信息 + let totalOrders = allOrders.length; + let totalAmount = 0; + let totalPieces = 0; + let totalWeight = 0; + let unpaidAmount = 0; + let paidAmount = 0; + + allOrders.forEach(order => { + const orderData = order.toJSON(); + const amount = parseFloat(orderData.total_amount) || 0; + const pieces = parseInt(orderData.total_pieces) || 0; + const weight = parseFloat(orderData.total_weight) || 0; + + totalAmount += amount; + totalPieces += pieces; + totalWeight += weight; + + if (orderData.payment_status === '未收款') { + unpaidAmount += amount; + } else if (orderData.payment_status === '全款') { + paidAmount += amount; + } + }); + + const statistics = { + totalOrders, + totalAmount: Math.round(totalAmount * 100) / 100, + totalPieces, + totalWeight: Math.round(totalWeight * 1000) / 1000, + unpaidAmount: Math.round(unpaidAmount * 100) / 100, + paidAmount: Math.round(paidAmount * 100) / 100 + }; + + console.log('计算的统计信息:', statistics); + + res.status(200).json({ + success: true, + code: 200, + message: '获取订单统计成功', + data: statistics + }); + + } catch (error) { + console.error('获取订单统计失败:', error); + res.status(500).json({ + success: false, + code: 500, + message: '获取订单统计失败: ' + error.message + }); + } +}); + // 创建动态接口(已废弃 - 使用上面的实际实现) app.post('/api/eggbar/posts/deprecated', async (req, res) => { try { @@ -832,10 +1157,31 @@ const eggbarSequelize = new Sequelize( } ); +// 4. trade_library数据源连接 - 用于订单查询 +const tradeLibrarySequelize = new Sequelize( + 'trade_library', + dbConfig.user, + dbConfig.password, + { + host: dbConfig.host, + port: dbConfig.port, + dialect: 'mysql', + pool: { + max: 10, + min: 0, + acquire: 30000, + idle: 10000 + }, + logging: console.log, + define: { + timestamps: false + }, + timezone: '+08:00' // 设置时区为UTC+8 + } +); + // 为保持兼容性,保留默认sequelize引用(指向wechat_app) const sequelize = wechatAppSequelize; - -// 定义会话模型 const ChatConversation = sequelize.define('ChatConversation', { id: { type: DataTypes.INTEGER, @@ -1753,6 +2099,222 @@ Cover.init({ timestamps: false }); +// 简道云销售订单主表模型(新添加) +class JdSalesMain extends Model { } +JdSalesMain.init({ + dataid: { + type: DataTypes.STRING(50), + primaryKey: true, + allowNull: false, + comment: '主表自增主键(简道云插件关联用)' + }, + sales_no: { + type: DataTypes.STRING(50), + allowNull: false, + unique: true, + comment: '销售单号(业务唯一编号)' + }, + order_date: { + type: DataTypes.DATEONLY, + allowNull: false, + comment: '下单日期' + }, + customer_company: { + type: DataTypes.STRING(200), + allowNull: false, + comment: '客户公司' + }, + contact_person: { + type: DataTypes.STRING(100), + allowNull: false, + comment: '联系人' + }, + phone: { + type: DataTypes.STRING(20), + allowNull: true, + comment: '联系电话(允许带区号等特殊字符,用字符串存储)' + }, + salesman: { + type: DataTypes.STRING(100), + allowNull: true, + comment: '销售员' + }, + merchandiser: { + type: DataTypes.STRING(100), + allowNull: true, + comment: '跟单员' + }, + address: { + type: DataTypes.STRING(500), + allowNull: true, + comment: '地址' + }, + total_pieces: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '总件数' + }, + total_weight: { + type: DataTypes.DECIMAL(10, 3), + allowNull: false, + defaultValue: 0.000, + comment: '总斤数(保留3位小数)' + }, + total_amount: { + type: DataTypes.DECIMAL(12, 2), + allowNull: false, + defaultValue: 0.00, + comment: '总金额(保留2位小数)' + }, + vehicle_scale: { + type: DataTypes.STRING(100), + allowNull: true, + comment: '车辆规模' + }, + payment_voucher: { + type: DataTypes.STRING(255), + allowNull: true, + comment: '支付凭证(存储照片路径)' + }, + invoice: { + type: DataTypes.STRING(255), + allowNull: true, + comment: '发票(存储照片路径)' + }, + payment_status: { + type: DataTypes.STRING(50), + allowNull: true, + comment: '付款状态(如:未付款/部分付款/已付清)' + }, + order_status: { + type: DataTypes.STRING(50), + allowNull: true + }, + chepai: { + type: DataTypes.STRING(255), + allowNull: true, + comment: '车牌' + } +}, { + sequelize: tradeLibrarySequelize, + modelName: 'JdSalesMain', + tableName: 'jd_sales_main', + timestamps: false, + indexes: [ + { + unique: true, + fields: ['sales_no'], + name: 'idx_sales_no', + comment: '销售单号唯一索引(避免重复)' + } + ] +}); + +// 简道云销售订单明细表模型(新添加) +class JdSalesSub extends Model { } +JdSalesSub.init({ + sub_id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + comment: '子表自增主键' + }, + dataid: { + type: DataTypes.STRING(50), + allowNull: false, + comment: '关联主表dataid' + }, + product_name: { + type: DataTypes.STRING(200), + allowNull: false, + comment: '产品名称' + }, + batch_no: { + type: DataTypes.STRING(100), + allowNull: true, + comment: '批次号' + }, + unit: { + type: DataTypes.STRING(50), + allowNull: true, + comment: '单位(件,斤)' + }, + yolk: { + type: DataTypes.STRING(50), + allowNull: true, + comment: '蛋黄(描述信息)' + }, + specification: { + type: DataTypes.STRING(100), + allowNull: true, + comment: '规格' + }, + sales_pieces: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '销售件数' + }, + sales_weight: { + type: DataTypes.DECIMAL(10, 3), + allowNull: false, + defaultValue: 0.000, + comment: '销售斤数(保留3位小数)' + }, + unit_price: { + type: DataTypes.DECIMAL(10, 2), + allowNull: false, + defaultValue: 0.00, + comment: '单价(保留2位小数)' + }, + purchase_price: { + type: DataTypes.DECIMAL(10, 2), + allowNull: true, + comment: '采购价(保留2位小数)' + }, + sales_amount: { + type: DataTypes.DECIMAL(12, 2), + allowNull: false, + defaultValue: 0.00, + comment: '销售金额' + }, + discount_amount: { + type: DataTypes.DECIMAL(10, 2), + allowNull: true, + defaultValue: 0.00, + comment: '优惠金额' + }, + rebate_amount: { + type: DataTypes.DECIMAL(10, 2), + allowNull: true, + defaultValue: 0.00, + comment: '折扣金额' + }, + purchase_amount: { + type: DataTypes.DECIMAL(12, 2), + allowNull: true, + comment: '采购金额' + }, + discount_reason: { + type: DataTypes.STRING(500), + allowNull: true, + comment: '优惠原因' + } +}, { + sequelize: tradeLibrarySequelize, + modelName: 'JdSalesSub', + tableName: 'jd_sales_sub', + timestamps: false, + indexes: [ + { + fields: ['dataid'], + name: 'idx_main_id', + comment: '关联主表索引(加速查询)' + } + ] +}); + // Eggbar 帖子模型 - 用于存储用户发布的动态 class EggbarPost extends Model { } EggbarPost.init({ @@ -1956,6 +2518,21 @@ UserManagement.belongsTo(User, { as: 'user' // 管理信息所属用户 }); +// 简道云销售订单主表和明细表的关联关系 +JdSalesMain.hasMany(JdSalesSub, { + foreignKey: 'dataid', + sourceKey: 'dataid', + as: 'subItems', + onDelete: 'CASCADE', + onUpdate: 'CASCADE' +}); + +JdSalesSub.belongsTo(JdSalesMain, { + foreignKey: 'dataid', + targetKey: 'dataid', + as: 'mainOrder' +}); + // 同步数据库模型到MySQL async function syncDatabase() { try { @@ -10435,6 +11012,68 @@ app.post('/api/evaluate/price', async (req, res) => { } }); +// 订单查询接口 - 根据用户电话号码查询订单 +app.get('/api/orders', async (req, res) => { + try { + const { phone } = req.query; + + if (!phone) { + return res.status(400).json({ + success: false, + code: 400, + message: '缺少phone参数' + }); + } + + console.log('查询订单:', { phone }); + + // 查询主表订单信息 + const orders = await tradeLibrarySequelize.query( + `SELECT * FROM jd_sales_main WHERE phone = ? ORDER BY order_date DESC`, + { + replacements: [phone], + type: tradeLibrarySequelize.QueryTypes.SELECT + } + ); + + // 查询每个订单的子表信息 + const ordersWithDetails = await Promise.all(orders.map(async (order) => { + const details = await tradeLibrarySequelize.query( + `SELECT * FROM jd_sales_sub WHERE dataid = ?`, + { + replacements: [order.dataid], + type: tradeLibrarySequelize.QueryTypes.SELECT + } + ); + + return { + ...order, + details + }; + })); + + console.log('订单查询结果:', { count: ordersWithDetails.length }); + + res.json({ + success: true, + code: 200, + message: '订单查询成功', + data: { + orders: ordersWithDetails, + total: ordersWithDetails.length + } + }); + } catch (error) { + console.error('订单查询失败:', error); + res.status(500).json({ + success: false, + code: 500, + message: '订单查询失败', + error: error.message + }); + } +}); + // 在服务器启动前执行商品联系人更新 updateProductContacts().then(() => { console.log('\n📦 商品联系人信息更新完成!');