Files
jurong_circle_black/routes/matching.js
2025-09-17 16:59:34 +08:00

626 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;