const express = require('express'); const router = express.Router(); const { getDB } = require('../database'); const matchingService = require('../services/matchingService'); const { auth } = require('../middleware/auth'); const { default: axios } = require('axios'); router.post('/create', auth, async (req, res) => { try { console.log('匹配订单创建请求 - 用户ID:', req.user.id); console.log('请求体:', req.body); const userId = req.user.id; const { matchingType = 'small', customAmount } = req.body; const [user_type] = await getDB().query(`SELECT count(*) as total FROM users WHERE id=${userId} and user_type='directly_operated'`); if(user_type[0].total > 0){ return res.status(400).json({message: '平台暂不支持直营用户获得融豆'}) } // 验证匹配类型 if (!['small', 'large'].includes(matchingType)) { return res.status(400).json({ message: '无效的匹配类型' }); } // 验证大额匹配的金额 if (matchingType === 'large') { if (!customAmount || typeof customAmount !== 'number') { return res.status(400).json({ message: '大额匹配需要指定金额' }); } if (customAmount < 3000 || customAmount > 50000) { return res.status(400).json({ message: '大额匹配金额必须在3000-50000之间' }); } } // 检查用户是否有未完成的匹配订单(排除已失败的订单) const [existingOrders] = await getDB().execute( 'SELECT COUNT(*) as count FROM matching_orders WHERE initiator_id = ? AND status IN ("pending", "matching")', [userId] ); if (existingOrders[0].count > 0) { return res.status(400).json({ message: '您有未完成的匹配订单,请等待完成后再创建新订单' }); } // 校验用户是否已上传必要的证件和收款码 const [userInfo] = await getDB().execute( 'SELECT business_license, id_card_front, id_card_back, wechat_qr, alipay_qr, bank_card, unionpay_qr FROM users WHERE id = ?', [userId] ); if (userInfo.length === 0) { return res.status(404).json({ message: '用户不存在' }); } const user = userInfo[0]; // 检查证件是否已上传 if (!user.business_license || !user.id_card_front || !user.id_card_back) { return res.status(400).json({ message: '开始匹配前,请先在个人中心上传营业执照和身份证正反面', code: 'MISSING_DOCUMENTS' }); } // 检查收款码是否已上传(至少需要一种收款方式) if (!user.wechat_qr && !user.alipay_qr && !user.bank_card && !user.unionpay_qr) { return res.status(400).json({ message: '开始匹配前,请先在个人中心设置至少一种收款方式(微信、支付宝、银行卡或云闪付)', code: 'MISSING_PAYMENT_METHODS' }); } // 创建匹配订单 const result = await matchingService.createMatchingOrder(userId, matchingType, customAmount); const message = matchingType === 'small' ? '小额匹配成功!已为您生成3笔转账分配' : `大额匹配成功!已为您生成${result.totalAmount}笔转账分配`; res.json({ success: true, message, data: { matchingOrderId: result.orderId, amounts: result.amounts, matchingType: result.matchingType, totalAmount: result.totalAmount } }); } catch (error) { console.error('创建匹配订单失败:', error); res.status(500).json({ message: error.message || '匹配失败,请稍后重试' }); } }); router.get('/my-orders', auth, async (req, res) => { try { const userId = req.user.id; const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 10; const orders = await matchingService.getUserMatchingOrders(userId, page, limit); res.json({ success: true, data: orders }); } catch (error) { console.error('获取匹配订单失败:', error); res.status(500).json({ message: '获取匹配订单失败' }); } }); /** * @swagger * /api/matching/pending-allocations: * get: * summary: 获取用户待处理的分配 * tags: [Matching] * security: * - bearerAuth: [] * responses: * 200: * description: 成功获取待处理分配 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: array * items: * $ref: '#/components/schemas/Allocation' * 401: * description: 未授权 * 500: * description: 服务器错误 */ router.get('/pending-allocations', auth, async (req, res) => { try { const userId = req.user.id; const allocations = await matchingService.getUserPendingAllocations(userId); res.json({ success: true, data: allocations }); } catch (error) { console.error('获取待处理分配失败:', error); res.status(500).json({ message: '获取待处理分配失败' }); } }); /** * @swagger * /api/matching/allocation/{id}: * get: * summary: 获取分配详情 * tags: [Matching] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: 分配ID * responses: * 200: * description: 成功获取分配详情 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * $ref: '#/components/schemas/Allocation' * 401: * description: 未授权 * 403: * description: 无权限访问 * 404: * description: 分配不存在 * 500: * description: 服务器错误 */ router.get('/allocation/:id', auth, async (req, res) => { try { const db = getDB(); const allocationId = req.params.id; const userId = req.user.id; // 首先获取分配信息 const [allocations] = await db.execute(` SELECT oa.id, oa.from_user_id, oa.to_user_id, oa.cycle_number, oa.amount, oa.status, oa.created_at, from_user.username as from_user_name, to_user.username as to_user_name FROM transfers oa JOIN users from_user ON oa.from_user_id = from_user.id JOIN users to_user ON oa.to_user_id = to_user.id WHERE oa.id = ? `, [allocationId]); if (allocations.length === 0) { return res.status(404).json({ success: false, message: '分配不存在' }); } const allocation = allocations[0]; // 检查权限:只有分配的发起人或接收人可以查看 if (allocation.from_user_id !== userId && allocation.to_user_id !== userId) { return res.status(403).json({ success: false, message: '无权限访问此分配' }); } res.json({ success: true, data: allocation }); } catch (error) { console.error('获取分配详情错误:', error); res.status(500).json({ success: false, message: '获取分配详情失败' }); } }); /** * @swagger * /api/matching/confirm-allocation/{allocationId}: * post: * summary: 确认分配(创建转账) * tags: [Matching] * security: * - bearerAuth: [] * parameters: * - in: path * name: allocationId * required: true * schema: * type: integer * description: 分配ID * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * transferAmount: * type: number * description: 转账金额 * description: * type: string * description: 转账描述 * voucher: * type: string * description: 转账凭证(图片URL) * required: * - voucher * responses: * 200: * description: 转账凭证提交成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * message: * type: string * data: * type: object * properties: * transferId: * type: integer * 400: * description: 参数错误 * 401: * description: 未授权 * 500: * description: 服务器错误 */ router.post('/confirm-allocation/:allocationId', auth, async (req, res) => { try { const { allocationId } = req.params; const userId = req.user.id; const { transferAmount, description, voucher } = req.body; // 获取转账信息 // 校验转账凭证是否存在 if (!voucher) { return res.status(400).json({ success: false, message: '请上传转账凭证' }); } // 调用服务层方法,传递完整的转账信息 const transferId = await matchingService.confirmAllocation( allocationId, userId, transferAmount, description, voucher ); res.json({ success: true, message: '转账凭证已提交,转账记录已创建', data: { transferId } }); axios.post('http://192.168.1.43:5000',{ allocationId: allocationId }).then(res => { console.log(res.data) }) } catch (error) { console.error('确认分配失败:', error); res.status(500).json({ message: error.message || '确认分配失败' }); } }); /** * @swagger * /api/matching/reject-allocation/{allocationId}: * post: * summary: 拒绝分配 * tags: [Matching] * security: * - bearerAuth: [] * parameters: * - in: path * name: allocationId * required: true * schema: * type: integer * description: 分配ID * requestBody: * content: * application/json: * schema: * type: object * properties: * reason: * type: string * description: 拒绝原因 * responses: * 200: * description: 拒绝分配成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * message: * type: string * 401: * description: 未授权 * 404: * description: 分配不存在或无权限 * 500: * description: 服务器错误 */ router.post('/reject-allocation/:allocationId', auth, async (req, res) => { try { const { allocationId } = req.params; const userId = req.user.id; const { reason } = req.body; const db = getDB(); // 获取分配信息 const [allocations] = await db.execute( 'SELECT * FROM transfers WHERE id = ? AND from_user_id = ?', [allocationId, userId] ); if (allocations.length === 0) { return res.status(404).json({ message: '分配不存在或无权限' }); } const allocation = allocations[0]; // 更新分配状态 await db.execute( 'UPDATE transfers SET status = "rejected" WHERE id = ?', [allocationId] ); // 记录拒绝动作 await db.execute( 'INSERT INTO matching_records (matching_order_id, user_id, action, note) VALUES (?, ?, "reject", ?)', [allocation.matching_order_id, userId, reason || '用户拒绝'] ); // 检查订单状态是否需要更新 const statusResult = await matchingService.checkOrderStatusAfterRejection( allocation.matching_order_id, allocation.cycle_number ); let message = '已拒绝分配'; if (statusResult === 'failed') { message = '已拒绝分配,该轮次所有分配均被拒绝,匹配订单已失败'; } res.json({ success: true, message }); } catch (error) { console.error('拒绝分配失败:', error); res.status(500).json({ message: '拒绝分配失败' }); } }); /** * @swagger * /api/matching/order/{orderId}: * get: * summary: 获取匹配订单详情 * tags: [Matching] * security: * - bearerAuth: [] * parameters: * - in: path * name: orderId * required: true * schema: * type: integer * description: 订单ID * responses: * 200: * description: 成功获取订单详情 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * order: * $ref: '#/components/schemas/MatchingOrder' * allocations: * type: array * items: * $ref: '#/components/schemas/Allocation' * records: * type: array * items: * type: object * 401: * description: 未授权 * 403: * description: 无权限查看 * 404: * description: 订单不存在 * 500: * description: 服务器错误 */ router.get('/order/:orderId', auth, async (req, res) => { try { const { orderId } = req.params; const userId = req.user.id; const db = getDB(); // 获取订单基本信息 const [orders] = await db.execute( `SELECT mo.*, u.username as initiator_name,u.real_name as initiator_real_name FROM matching_orders mo JOIN users u ON mo.initiator_id = u.id WHERE mo.id = ?`, [orderId] ); if (orders.length === 0) { return res.status(404).json({ message: '匹配订单不存在' }); } const order = orders[0]; // 检查权限(订单发起人或参与者) const [userCheck] = await db.execute( `SELECT COUNT(*) as count FROM ( SELECT initiator_id as user_id FROM matching_orders WHERE id = ? UNION SELECT from_user_id as user_id FROM transfers WHERE id = ? ) as participants WHERE user_id = ?`, [orderId, orderId, userId] ); if (userCheck[0].count === 0) { return res.status(403).json({ message: '无权限查看此订单' }); } // 获取分配信息 const [allocations] = await db.execute( `SELECT oa.*, uf.username as from_user_name, ut.username as to_user_name FROM transfers oa JOIN users uf ON oa.from_user_id = uf.id JOIN users ut ON oa.to_user_id = ut.id WHERE oa.id = ? ORDER BY oa.cycle_number, oa.created_at`, [orderId] ); // 获取匹配记录 const [records] = await db.execute( `SELECT mr.*, u.username FROM matching_records mr JOIN users u ON mr.user_id = u.id WHERE mr.matching_order_id = ? ORDER BY mr.created_at`, [orderId] ); res.json({ success: true, data: { order, allocations, records } }); } catch (error) { console.error('获取匹配订单详情失败:', error); res.status(500).json({ message: '获取匹配订单详情失败' }); } }); /** * @swagger * /api/matching/stats: * get: * summary: 获取匹配统计信息 * tags: [Matching] * security: * - bearerAuth: [] * responses: * 200: * description: 成功获取统计信息 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * userStats: * type: object * properties: * initiated_orders: * type: integer * participated_allocations: * type: integer * total_initiated_amount: * type: number * total_participated_amount: * type: number * 401: * description: 未授权 * 500: * description: 服务器错误 */ router.get('/stats', auth, async (req, res) => { try { const userId = req.user.id; const db = getDB(); // 获取用户统计 const [userStats] = await db.execute( `SELECT COUNT(CASE WHEN mo.initiator_id = ? THEN 1 END) as initiated_orders, COUNT(CASE WHEN oa.from_user_id = ? THEN 1 END) as participated_allocations, SUM(CASE WHEN mo.initiator_id = ? AND mo.status = 'completed' THEN mo.amount ELSE 0 END) as total_initiated_amount, SUM(CASE WHEN oa.from_user_id = ? AND oa.status = 'completed' THEN oa.amount ELSE 0 END) as total_participated_amount FROM matching_orders mo LEFT JOIN transfers oa ON mo.id = oa.id`, [userId, userId, userId, userId] ); res.json({ success: true, data: { userStats: userStats[0] } }); } catch (error) { console.error('获取匹配统计失败:', error); res.status(500).json({ message: '获取匹配统计失败' }); } }); module.exports = router;