diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml
index 31f6ee5..9ded1f3 100644
--- a/.idea/data_source_mapping.xml
+++ b/.idea/data_source_mapping.xml
@@ -2,7 +2,10 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
deleted file mode 100644
index 56782ca..0000000
--- a/.idea/sqldialects.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/routes/matching.js b/routes/matching.js
index d9e1db5..e34b2d8 100644
--- a/routes/matching.js
+++ b/routes/matching.js
@@ -310,12 +310,12 @@ router.post('/confirm-allocation/:allocationId', auth, async (req, res) => {
}
// 调用服务层方法,传递完整的转账信息
- const transferId = await matchingService.confirmAllocation(
- allocationId,
- userId,
- transferAmount,
- description,
- voucher
+ const transferId = matchingService.confirmAllocation(
+ allocationId,
+ userId,
+ transferAmount,
+ description,
+ voucher
);
res.json({
@@ -324,11 +324,11 @@ router.post('/confirm-allocation/:allocationId', auth, async (req, res) => {
data: { transferId }
});
- axios.post('http://localhost:5000/ocr',{
- id: allocationId
- }).then(res => {
- console.log(res.data)
- })
+ // axios.post('http://localhost:5000/ocr',{
+ // id: allocationId
+ // }).then(res => {
+ // console.log(res.data)
+ // })
} catch (error) {
console.error('确认分配失败:', error);
diff --git a/routes/sms.js b/routes/sms.js
index 787bd56..d326fa0 100644
--- a/routes/sms.js
+++ b/routes/sms.js
@@ -141,11 +141,6 @@ router.post('/send', async (req, res) => {
// 生产环境发送真实短信
try {
console.log(code);
- res.json({
- success: true,
- message: '验证码发送成功'
- })
- return
const sendSmsRequest = new Dysmsapi20170525.SendSmsRequest({
phoneNumbers: phone,
signName: SMS_CONFIG.signName,
diff --git a/routes/transfers.js b/routes/transfers.js
index 91ff2fd..d8b6494 100644
--- a/routes/transfers.js
+++ b/routes/transfers.js
@@ -12,6 +12,48 @@ const dayjs = require('dayjs');
const router = express.Router();
+// router.get('/tmp', async (req, res) => {
+// const db = getDB();
+// // 1. 查询所有转账记录,按 id 升序
+// let [transfers] = await db.execute(`
+// SELECT *
+// FROM transfers
+// WHERE status='received' or status='confirmed'
+// ORDER BY id ASC`);
+//
+// // 2. 用对象维护每个用户的最新余额
+// const userBalances = {};
+//
+// // 3. 遍历每条转账记录,计算余额
+// for (const trx of transfers) {
+// const fromId = trx.from_user_id;
+// const toId = trx.to_user_id;
+// const amount = Number(trx.amount);
+//
+// if (!(fromId in userBalances)) userBalances[fromId] = 0;
+// if (!(toId in userBalances)) userBalances[toId] = 0;
+//
+// const fromBalance = userBalances[fromId] - amount;
+// const toBalance = userBalances[toId] + amount;
+//
+// userBalances[fromId] = fromBalance;
+// userBalances[toId] = toBalance;
+//
+// const balanceDiff = toBalance - fromBalance;
+//
+// // 4. 更新当前行的字段
+// await db.execute(
+// `UPDATE transfers
+// SET from_user_balance = ?,
+// to_user_balance = ?,
+// balance_diff = ?
+// WHERE id = ?`,
+// [fromBalance, toBalance, amount, trx.id]
+// );
+//
+// }
+// res.json('更新成功')
+// })
router.get('/',
authenticateToken,
validateQuery(transferSchemas.query),
@@ -342,7 +384,6 @@ router.post('/confirm-not-received',
);
-
// 获取用户转账记录
router.get('/user/:userId', authenticateToken, async (req, res) => {
try {
@@ -386,8 +427,9 @@ router.get('/user/:userId', authenticateToken, async (req, res) => {
LEFT JOIN users to_user ON t.to_user_id = to_user.id
${whereClause}
ORDER BY t.created_at
- DESC
- LIMIT ${limitNum} OFFSET ${offset}
+ DESC
+ LIMIT ${limitNum}
+ OFFSET ${offset}
`, countParams);
const [countResult] = await db.execute(`
@@ -425,37 +467,37 @@ router.get('/stats', authenticateToken, async (req, res) => {
if (isAdmin) {
// 管理员可以查看全局统计
const [totalStats] = await db.execute(`
- SELECT COUNT(*) as total_transfers,
- SUM(amount) as total_flow_amount,
- SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
- SUM(CASE WHEN status = 'confirmed' THEN 1 ELSE 0 END) as confirmed_count,
- SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received_count,
- SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count,
- SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_count,
- SUM(CASE WHEN status = 'not_received' THEN 1 ELSE 0 END) as not_received_count,
- SUM(CASE WHEN is_overdue = 1 THEN 1 ELSE 0 END) as overdue_count,
- SUM(CASE WHEN is_bad_debt = 1 THEN 1 ELSE 0 END) as bad_debt_count,
- SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as total_amount,
- SUM(CASE WHEN is_bad_debt = 1 THEN amount ELSE 0 END) as bad_debt_amount,
+ SELECT COUNT(*) as total_transfers,
+ SUM(amount) as total_flow_amount,
+ SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
+ SUM(CASE WHEN status = 'confirmed' THEN 1 ELSE 0 END) as confirmed_count,
+ SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received_count,
+ SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count,
+ SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_count,
+ SUM(CASE WHEN status = 'not_received' THEN 1 ELSE 0 END) as not_received_count,
+ SUM(CASE WHEN is_overdue = 1 THEN 1 ELSE 0 END) as overdue_count,
+ SUM(CASE WHEN is_bad_debt = 1 THEN 1 ELSE 0 END) as bad_debt_count,
+ SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as total_amount,
+ SUM(CASE WHEN is_bad_debt = 1 THEN amount ELSE 0 END) as bad_debt_amount,
SUM(CASE
WHEN transfer_type = 'initial' AND status = 'confirmed' THEN amount
- ELSE 0 END) as initial_amount,
+ ELSE 0 END) as initial_amount,
SUM(CASE
WHEN transfer_type = 'return' AND status = 'confirmed' THEN amount
- ELSE 0 END) as return_amount,
+ ELSE 0 END) as return_amount,
SUM(CASE
WHEN transfer_type = 'user_to_user' AND status = 'confirmed' THEN amount
- ELSE 0 END) as user_to_user_amount,
+ ELSE 0 END) as user_to_user_amount,
(SELECT SUM(balance)
FROM users
WHERE role = 'user'
- AND is_system_account = 1) as total_merchant_balance,
+ AND is_system_account = 1) as total_merchant_balance,
(SELECT SUM(balance)
FROM users
WHERE role = 'user'
AND is_system_account != 1) as total_user_balance,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as participated_transfers,
- SUM(CASE WHEN source_type IN ('agent') THEN amount END) as agent_total,
+ SUM(CASE WHEN source_type IN ('system') THEN amount END) as participated_transfers,
+ SUM(CASE WHEN source_type IN ('agent') THEN amount END) as agent_total,
SUM(CASE WHEN source_type IN ('operated_agent') THEN amount END) as operated_agent_total
FROM transfers
`);
@@ -470,26 +512,26 @@ router.get('/stats', authenticateToken, async (req, res) => {
(
COALESCE((SELECT SUM(amount)
FROM transfers
- WHERE DATE(created_at) = ?
- AND to_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
- AND status = 'received'), 0) -
+ WHERE DATE (created_at) = ?
+ AND to_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
+ AND status = 'received'), 0) -
COALESCE((SELECT SUM(amount)
FROM transfers
- WHERE DATE(created_at) = ?
- AND from_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
- AND status = 'received'), 0)
+ WHERE DATE (created_at) = ?
+ AND from_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
+ AND status = 'received'), 0)
) as today_amount
FROM transfers
- WHERE DATE(created_at) = ?
+ WHERE DATE (created_at) = ?
`, [todayStr, todayStr, todayStr]);
const [monthlyStats] = await db.execute(`
- SELECT COUNT(*) as monthly_transfers,
- SUM(CASE WHEN status = 'received' THEN amount ELSE 0 END) as monthly_amount,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as monthly_participated_transfers
+ SELECT COUNT(*) as monthly_transfers,
+ SUM(CASE WHEN status = 'received' THEN amount ELSE 0 END) as monthly_amount,
+ SUM(CASE WHEN source_type IN ('system') THEN amount END) as monthly_participated_transfers
FROM transfers
- WHERE YEAR(created_at) = ?
- AND MONTH(created_at) = ?
+ WHERE YEAR (created_at) = ?
+ AND MONTH (created_at) = ?
`, [currentYear, currentMonth]);
// 获取上月统计数据用于对比
@@ -497,12 +539,12 @@ router.get('/stats', authenticateToken, async (req, res) => {
const lastMonthYear = currentMonth === 1 ? currentYear - 1 : currentYear;
const [lastMonthStats] = await db.execute(`
- SELECT COUNT(*) as last_monthly_transfers,
- SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as last_monthly_amount,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as last_monthly_participated_transfers
+ SELECT COUNT(*) as last_monthly_transfers,
+ SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as last_monthly_amount,
+ SUM(CASE WHEN source_type IN ('system') THEN amount END) as last_monthly_participated_transfers
FROM transfers
- WHERE YEAR(created_at) = ?
- AND MONTH(created_at) = ?
+ WHERE YEAR (created_at) = ?
+ AND MONTH (created_at) = ?
`, [lastMonthYear, lastMonth]);
stats = {
@@ -524,9 +566,9 @@ router.get('/stats', authenticateToken, async (req, res) => {
return_amount: parseFloat(totalStats[0].return_amount || 0),
user_to_user_amount: parseFloat(totalStats[0].user_to_user_amount || 0),
participated_transfers: totalStats[0].participated_transfers || 0,
- total_user_balance:totalStats[0].total_user_balance || 0,
- agent_total:totalStats[0].agent_total || 0,//代理收入
- operated_agent_total:totalStats[0].operated_agent_total || 0,//直营代理收入
+ total_user_balance: totalStats[0].total_user_balance || 0,
+ agent_total: totalStats[0].agent_total || 0,//代理收入
+ operated_agent_total: totalStats[0].operated_agent_total || 0,//直营代理收入
},
today: {
transfers: todayStats[0].today_transfers || 0,
@@ -565,7 +607,7 @@ router.get('/stats', authenticateToken, async (req, res) => {
SUM(CASE WHEN status = 'confirmed' AND to_user_id = ? THEN amount ELSE 0 END) as today_received
FROM transfers
WHERE (from_user_id = ? OR to_user_id = ?)
- AND DATE(created_at) = ?
+ AND DATE (created_at) = ?
`, [userId, userId, userId, userId, todayStr]);
stats = {
@@ -706,9 +748,9 @@ router.get('/trend', authenticateToken, async (req, res) => {
// 首先获取数据库中最早和最晚的转账日期
const [dateRange] = await db.execute(`
- SELECT MIN(DATE(created_at)) as min_date,
- MAX(DATE(created_at)) as max_date,
- COUNT(*) as total_count
+ SELECT MIN(DATE (created_at)) as min_date,
+ MAX(DATE (created_at)) as max_date,
+ COUNT(*) as total_count
FROM transfers
`);
@@ -737,13 +779,13 @@ router.get('/trend', authenticateToken, async (req, res) => {
// 获取指定天数内的转账趋势(从最大日期往前推)
const [trendData] = await db.execute(`
- SELECT DATE(created_at) as date,
- COUNT(*) as count,
- SUM(amount) as amount
+ SELECT DATE (created_at) as date, COUNT (*) as count, SUM (amount) as amount
FROM transfers
- WHERE DATE(created_at) >= DATE_SUB(?, INTERVAL ? DAY)
- AND status IN ('confirmed', 'received')
- GROUP BY DATE(created_at)
+ WHERE DATE (created_at) >= DATE_SUB(?
+ , INTERVAL ? DAY)
+ AND status IN ('confirmed'
+ , 'received')
+ GROUP BY DATE (created_at)
ORDER BY date ASC
`, [dateRange[0].max_date, daysNum - 1]);
@@ -1024,7 +1066,8 @@ router.get('/pending-allocations',
JOIN matching_orders mo ON oa.id = mo.id
${whereClause}
ORDER BY oa.${sortField} ${sortOrder}
- LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}
+ LIMIT ${parseInt(limit)}
+ OFFSET ${parseInt(offset)}
`;
const [allocations] = queryParams.length > 0
@@ -1140,19 +1183,16 @@ router.get('/daily-stats',
// 获取所有用户的昨日转出和今日入账统计
let [userStats] = await db.execute(`
- SELECT u.id as user_id,
+ SELECT u.id as user_id,
u.username,
u.real_name,
u.phone,
u.balance,
- COALESCE(yesterday_out.amount, 0) as yesterday_out_amount,
- COALESCE(today_in.amount, 0) as today_in_amount,
- COALESCE(confirmed_from.confirmed_amount, 0) as confirmed_from_amount,
- CASE
- WHEN (COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0)) > ABS(u.balance)
- THEN ABS(u.balance)
- ELSE (COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0))
- END as balance_needed
+ COALESCE(yesterday_out.amount, 0) -
+ COALESCE(yesterday_system_num.amount, 0) as yesterday_out_amount,
+ COALESCE(today_in.amount, 0) as today_in_amount,
+ COALESCE(confirmed_from.confirmed_amount, 0) as confirmed_from_amount,
+ COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0) as balance_needed
FROM users u
LEFT JOIN (SELECT from_user_id,
SUM(amount) as amount
@@ -1161,6 +1201,15 @@ router.get('/daily-stats',
AND created_at <= ?
AND status IN ('confirmed', 'received')
GROUP BY from_user_id) yesterday_out ON u.id = yesterday_out.from_user_id
+ LEFT JOIN (SELECT to_user_id,
+ SUM(amount) as amount
+ FROM transfers
+ WHERE created_at >= ?
+ AND created_at <= ?
+ AND transfer_type != 'user_to_user'
+ AND status IN ('received')
+ GROUP BY to_user_id) yesterday_system_num
+ ON u.id = yesterday_system_num.to_user_id
LEFT JOIN (SELECT to_user_id,
SUM(amount) as amount
FROM transfers
@@ -1171,17 +1220,17 @@ router.get('/daily-stats',
left join (select from_user_id,
sum(amount) as confirmed_amount
from transfers
- where status = 'received'
- and created_at >= ?
- and created_at <= ?
- group by from_user_id) as confirmed_from on u.id = confirmed_from.from_user_id
- WHERE u.role != 'admin'
+ WHERE status IN ('confirmed', 'received')
+ AND created_at >= ?
+ AND created_at <= ?
+ GROUP BY from_user_id) as confirmed_from on u.id = confirmed_from.from_user_id
+ WHERE u.role != 'admin' AND u.user_type !='directly_operated'
AND u.is_system_account != 1
AND yesterday_out.amount > 0
AND u.balance < 0
ORDER BY balance_needed DESC, yesterday_out_amount DESC
- `, [yesterdayStartStr, yesterdayEndStr, todayStartStr, todayEndStr, todayStartStr, todayEndStr]);
- // userStats = userStats.filter(item=>item.balance_needed >= 100)
+ `, [yesterdayStartStr, yesterdayEndStr, yesterdayStartStr, yesterdayEndStr,todayStartStr, todayEndStr, todayStartStr, todayEndStr]);
+ // userStats = userStats.filter(item=>item.balance_needed > 100)
userStats.forEach(item => {
item.balance_needed = Math.abs(item.balance_needed)
})
diff --git a/routes/upload.js b/routes/upload.js
index 2222e0b..d3894ef 100644
--- a/routes/upload.js
+++ b/routes/upload.js
@@ -51,54 +51,6 @@ const multiUpload = multer({
}
});
-/**
- * @swagger
- * /upload/image:
- * post:
- * summary: 上传图片
- * tags: [Upload]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * multipart/form-data:
- * schema:
- * type: object
- * properties:
- * file:
- * type: string
- * format: binary
- * description: 要上传的图片文件
- * type:
- * type: string
- * enum: [avatar, product, document]
- * default: document
- * description: 上传文件类型
- * responses:
- * 200:
- * description: 图片上传成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * url:
- * type: string
- * description: 上传后的文件URL
- * filename:
- * type: string
- * description: 上传后的文件名
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
router.post('/image', authenticateToken, (req, res) => {
upload.single('file')(req, res, async (err) => {
if (err instanceof multer.MulterError) {
diff --git a/routes/users.js b/routes/users.js
index 5b0afbc..dbecd93 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -215,7 +215,7 @@ router.get('/', auth, adminAuth, async (req, res) => {
// 确保参数为有效数字
const pageNum = Math.max(1, parseInt(page) || 1);
- const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10));
+ const limitNum = Math.max(1, parseInt(limit) || 10);
const offset = Math.max(0, (pageNum - 1) * limitNum);
let whereConditions = [];
@@ -504,7 +504,10 @@ router.get('/stats', auth, async (req, res) => {
// 活跃用户数(有订单的用户)
const [activeUsers] = await db.execute(
- 'SELECT COUNT(DISTINCT from_user_id) as count FROM transfers'
+ `SELECT
+ COUNT(DISTINCT from_user_id) AS count
+ FROM transfers
+ WHERE YEARWEEK(created_at, 1) = YEARWEEK(CURDATE() - INTERVAL 1 WEEK, 1)`
);
const [weekUsers] = await db.execute(`
SELECT COUNT(DISTINCT from_user_id) as count
@@ -1546,20 +1549,52 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
[distribute.inviter]
);
distributeUser = distributeUser[0]
+ //分销是代理
if (distributeUser.user_type == 'agent') {
- //给代理添加2980融豆的70% 增加区域保护
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.7, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.7, '用户服务费返现', 'agent']
- );
+ let [agentUser] = await db.execute(`
+ SELECT r.*
+ FROM regional_agents as r
+ LEFT JOIN users au on r.user_id = au.id
+ WHERE au.user_type = 'agent'
+ AND r.status = 'active'
+ AND r.region_id = ${user.district_id}
+ `)
+ if (agentUser.length > 0 && agentUser[0].user_id !== distributeUser.id) {
+ //增加区域保护
+ await db.execute(`
+ UPDATE users
+ SET balance = balance - ?
+ WHERE id = ?
+ `, [serviceFee * 0.05, agentUser[0].user_id])
+ await db.execute(
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ [userId, agentUser[0].user_id, 'user_to_agent', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
+ );
+ await db.execute(
+ 'UPDATE users SET balance = balance - ? WHERE id = ?',
+ [serviceFee * 0.65, distributeUser.id]
+ );
+ //记录转账记录
+ await db.execute(
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ [userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.65, '用户服务费返现', 'agent']
+ );
+ } else {
+ //给代理添加2980融豆的70% 增加区域保护
+ await db.execute(
+ 'UPDATE users SET balance = balance - ? WHERE id = ?',
+ [serviceFee * 0.7, distributeUser.id]
+ );
+ //记录转账记录
+ await db.execute(
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ [userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.7, '用户服务费返现', 'agent']
+ );
+ }
+
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
);
//记录服务费
@@ -1577,12 +1612,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent_operated']
);
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
//记录服务费
@@ -1615,17 +1650,17 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'operated_agent']
);
//记录直营利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id, from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.3, '用户服务费返现', 'directly_operated']
);
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
}
@@ -1642,17 +1677,17 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.15, '用户服务费返现', 'agent_operated']
);
//记录直营利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.35, '用户服务费返现', 'directly_operated']
);
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
}
@@ -1669,7 +1704,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.1, '用户服务费返现', 'agent_operated']
);
//记录直营利润
@@ -1679,7 +1714,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
}
@@ -1701,29 +1736,61 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
userUpInfo = userUpInfo[0]
//判断用户上级是否是代理
if (userUpInfo && userUpInfo.user_type === 'agent') {
+ let [agentUser] = await db.execute(`
+ SELECT r.*
+ FROM regional_agents as r
+ LEFT JOIN users au on r.user_id = au.id
+ WHERE au.user_type = 'agent'
+ AND r.status = 'active'
+ AND r.region_id = ${user.district_id}
+ `)
+ if (agentUser.length > 0 && agentUser[0].user_id !== distributeUser.id) {
+ //增加区域保护
+ await db.execute(`
+ UPDATE users
+ SET balance = balance - ?
+ WHERE id = ?
+ `, [serviceFee * 0.05, agentUser[0].user_id])
+ await db.execute(
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ [userId, agentUser[0].user_id, 'user_to_agent', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
+ );
+ //给代理分配
+ await db.execute(
+ 'UPDATE users SET balance = balance - ? WHERE id = ?',
+ [serviceFee * 0.45, userUpInfo.id]
+ );
+ //记录代理转账信息
+ await db.execute(
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ [userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.45, '用户服务费返现', 'agent']
+ );
+ }else {
+ //给代理分配
+ await db.execute(
+ 'UPDATE users SET balance = balance - ? WHERE id = ?',
+ [serviceFee * 0.5, userUpInfo.id]
+ );
+ //记录代理转账信息
+ await db.execute(
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ [userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent']
+ );
+ }
//给用户分配
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.2, distributeUser.id]
);
- //给代理分配
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.5, userUpInfo.id]
- );
//记录用户转账信息
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
);
- //记录代理转账信息
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent']
- );
+
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
);
} else {
@@ -1734,12 +1801,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
);
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.8, '用户服务费返现', 'system']
);
@@ -1754,25 +1821,34 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
} else {
//判断用户此区域是否有代理
let [agentUser] = await db.execute(`
- SELECT user_id
- FROM regional_agents
- WHERE region_id = ?
- `, [users[0].district_id])
+ SELECT rg.user_id
+ FROM regional_agents as rg
+ LEFT JOIN users ag ON ag.id = rg.user_id
+ WHERE rg.region_id = ?
+ AND rg.status = 'active'
+ AND ag.user_type = 'agent'
+ `, [user.district_id])
if (agentUser.length > 0) {
+ //给区域代理分区域保护
+ await db.execute(`
+ UPDATE users
+ SET balance = balance - ?
+ WHERE id = ?
+ `, [serviceFee * 0.05, agentUser[0].user_id])
//给代理分成5%
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, agentUser[0].user_id, 'user_to_regional', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
)
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.95, '用户服务费返现', 'system']
);
} else {
//记录平台利润
await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
+ 'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee, '用户服务费返现', 'system']
);
diff --git a/services/matchingService.js b/services/matchingService.js
index 380696e..5910bfe 100644
--- a/services/matchingService.js
+++ b/services/matchingService.js
@@ -1,4 +1,4 @@
-const { getDB } = require('../database');
+const {getDB} = require('../database');
const timeoutService = require('./timeoutService');
const dayjs = require('dayjs');
@@ -9,1425 +9,1441 @@ const dayjs = require('dayjs');
* @returns {string} 格式化的日期字符串
*/
function getLocalDateString(date = new Date()) {
- return dayjs(date).format('YYYY-MM-DD');
+ return dayjs(date).format('YYYY-MM-DD');
}
class MatchingService {
- // 创建匹配订单(支持两种模式)
- async createMatchingOrder(userId, matchingType = 'small', customAmount = null) {
- const db = getDB();
-
- try {
- // 检查用户是否被拉黑
- const isBlacklisted = await timeoutService.isUserBlacklisted(userId);
- if (isBlacklisted) {
- throw new Error('您已被拉黑,无法参与匹配。如有疑问请联系管理员。');
- }
-
- // 检查用户审核状态、必要信息和余额
- const [userResult] = await db.execute(
- `SELECT audit_status,
- balance,
- wechat_qr,
- alipay_qr,
- unionpay_qr,
- bank_card,
- business_license,
- id_card_front,
- id_card_back
- FROM users
- WHERE id = ?`,
- [userId]
- );
-
- if (userResult.length === 0) {
- throw new Error('用户不存在');
- }
-
- const user = userResult[0];
-
- // 检查用户余额:只有负余额用户才能发起匹配
- // if (user.balance > 0) {
- // throw new Error('只有余额为负数的用户才能发起匹配,这是为了防止公司资金损失的重要规则');
- // }
-
- // 检查用户审核状态
- if (user.audit_status !== 'approved') {
- if (user.audit_status === 'pending') {
- throw new Error('您的账户正在审核中,审核通过后才能参与匹配');
- } else if (user.audit_status === 'rejected') {
- throw new Error('您的账户审核未通过,请联系管理员');
- }
- }
-
- // 检查必要的收款信息是否已上传
- const missingItems = [];
- if (!user.wechat_qr && !user.alipay_qr && !user.unionpay_qr) {
- missingItems.push('收款码(微信/支付宝/云闪付至少一种)');
- }
- if (!user.bank_card) {
- missingItems.push('银行卡号');
- }
- if (!user.business_license) {
- missingItems.push('营业执照');
- }
- if (!user.id_card_front || !user.id_card_back) {
- missingItems.push('身份证正反面');
- }
-
- if (missingItems.length > 0) {
- throw new Error(`请先上传以下信息:${missingItems.join('、')}`);
- }
-
- await db.query('START TRANSACTION');
-
- let totalAmount, maxCycles;
-
- if (matchingType === 'small') {
- // 小额匹配:固定5000元金额
- totalAmount = 5000;
- maxCycles = 1;
- } else if (matchingType === 'large') {
- // 大额匹配:用户自定义金额(最高5万)
- if (!customAmount || customAmount < 3000 || customAmount > 50000) {
- throw new Error('大额匹配金额必须在3000-50000之间');
- }
- totalAmount = customAmount;
- maxCycles = 1;
- } else {
- throw new Error('不支持的匹配类型');
- }
-
- // 创建匹配订单
- const [result] = await db.execute(
- 'INSERT INTO matching_orders (initiator_id, amount, status, max_cycles, matching_type) VALUES (?, ?, "matching", ?, ?)',
- [userId, totalAmount, maxCycles, matchingType]
- );
-
- const orderId = result.insertId;
-
- // 记录用户参与
- await db.execute(
- 'INSERT INTO matching_records (matching_order_id, user_id, action, amount) VALUES (?, ?, "join", ?)',
- [orderId, userId, totalAmount]
- );
-
- await db.query('COMMIT');
-
- // 立即生成智能分配
- const allocations = await this.generateSmartAllocationsWithDB(orderId, userId);
-
- // 检查并触发系统账户反向匹配
- // await this.checkAndTriggerSystemMatching();
-
- return {
- orderId,
- matchingType,
- totalAmount,
- allocations: allocations || [],
- allocationCount: allocations ? allocations.length : 0
- };
- } catch (error) {
- await db.query('ROLLBACK');
- console.error('创建匹配订单失败:', error);
- throw error;
- }
- }
-
- /**
- * 检查资金平衡并触发系统账户反向匹配
- * 当收款需求大于打款资金时,系统账户主动发起匹配
- */
- async checkAndTriggerSystemMatching() {
- const db = getDB();
-
- try {
- // 计算当前待收款总额(负余额用户的资金缺口)
- const [negativeBalanceResult] = await db.execute(`
- SELECT SUM(ABS(balance)) as total_deficit
- FROM users
- WHERE is_system_account = FALSE AND balance < 0
- `);
-
- const totalDeficit = negativeBalanceResult[0].total_deficit || 0;
-
- // 计算当前待打款总额(pending状态的分配)
- const [pendingPaymentsResult] = await db.execute(`
- SELECT SUM(oa.amount) as total_pending
- FROM transfers oa
- JOIN users u ON oa.from_user_id = u.id
- WHERE oa.status = 'pending' AND u.is_system_account = FALSE
- `);
-
- const totalPendingPayments = pendingPaymentsResult[0].total_pending || 0;
-
- console.log(`资金平衡检查: 总资金缺口=${totalDeficit}, 待打款总额=${totalPendingPayments}`);
-
- // 如果收款需求大于打款资金,触发系统账户反向匹配
- if (totalDeficit > totalPendingPayments) {
- const shortfall = totalDeficit - totalPendingPayments;
- console.log(`检测到资金缺口: ${shortfall}元,触发系统账户反向匹配`);
-
- await this.createSystemReverseMatching(shortfall);
- }
- } catch (error) {
- console.error('检查资金平衡失败:', error);
- // 不抛出错误,避免影响主流程
- }
- }
-
- /**
- * 创建系统账户反向匹配
- * 系统账户作为付款方,向有资金缺口的用户打款
- * @param {number} targetAmount - 目标匹配金额
- */
- async createSystemReverseMatching(targetAmount) {
- const db = getDB();
-
- try {
- // 获取可用的系统账户
- const [systemAccounts] = await db.execute(`
- SELECT id, balance FROM users
- WHERE is_system_account = TRUE AND balance > 1000
- ORDER BY balance DESC
- LIMIT 1
- `);
-
- if (systemAccounts.length === 0) {
- console.log('没有可用的系统账户进行反向匹配');
- return;
- }
-
- const systemAccount = systemAccounts[0];
-
- // 确定实际匹配金额(不超过系统账户余额的80%)
- const maxMatchAmount = Math.min(targetAmount, systemAccount.balance * 0.8);
-
- if (maxMatchAmount < 1000) {
- console.log('系统账户余额不足,无法进行反向匹配');
- return;
- }
-
- // 创建系统反向匹配订单
- const [result] = await db.execute(
- 'INSERT INTO matching_orders (initiator_id, amount, status, max_cycles, matching_type, is_system_reverse) VALUES (?, ?, "matching", 1, "system_reverse", TRUE)',
- [systemAccount.id, maxMatchAmount]
- );
-
- const orderId = result.insertId;
-
- // 生成分配给负余额用户
- await this.generateSystemReverseAllocations(orderId, maxMatchAmount, systemAccount.id);
-
- console.log(`系统反向匹配创建成功: 订单ID=${orderId}, 金额=${maxMatchAmount}`);
-
- } catch (error) {
- console.error('创建系统反向匹配失败:', error);
- }
- }
-
- /**
- * 为系统反向匹配生成分配
- * @param {number} orderId - 匹配订单ID
- * @param {number} totalAmount - 总金额
- * @param {number} systemUserId - 系统账户ID
- */
- async generateSystemReverseAllocations(orderId, totalAmount, systemUserId) {
- const db = getDB();
-
- try {
- // 获取负余额用户,按缺口大小排序
- const [negativeUsers] = await db.execute(`
- SELECT id, balance, ABS(balance) as deficit
- FROM users
- WHERE is_system_account = FALSE AND balance < 0
- ORDER BY deficit DESC
- LIMIT 10
- `);
-
- if (negativeUsers.length === 0) {
- console.log('没有负余额用户需要反向匹配');
- return;
- }
-
- // 按比例分配金额给负余额用户
- const totalDeficit = negativeUsers.reduce((sum, user) => sum + user.deficit, 0);
- let remainingAmount = totalAmount;
-
- for (let i = 0; i < negativeUsers.length && remainingAmount > 0; i++) {
- const user = negativeUsers[i];
- let allocationAmount;
-
- if (i === negativeUsers.length - 1) {
- // 最后一个用户分配剩余金额
- allocationAmount = remainingAmount;
- } else {
- // 按比例分配
- const proportion = user.deficit / totalDeficit;
- allocationAmount = Math.min(
- Math.floor(totalAmount * proportion),
- user.deficit,
- remainingAmount
- );
- }
-
- if (allocationAmount > 0) {
- // 创建分配记录(系统账户向用户转账)
- await db.execute(
- 'INSERT INTO transfers (matching_order_id, from_user_id, to_user_id, amount, cycle_number, status) VALUES (?, ?, ?, ?, 1, "pending")',
- [orderId, systemUserId, user.id, allocationAmount]
- );
-
- remainingAmount -= allocationAmount;
- console.log(`系统反向分配: ${allocationAmount}元 从系统账户${systemUserId} 到用户${user.id}`);
- }
- }
-
- } catch (error) {
- console.error('生成系统反向分配失败:', error);
- throw error;
- }
- }
-
- /**
- * 生成大额匹配的随机金额分拆(15000以上)
- * @param {number} totalAmount - 总金额
- * @returns {Array} 分拆后的金额数组
- */
- generateRandomLargeAmounts(totalAmount) {
- const amounts = [];
- let remaining = totalAmount;
- const minAmount = 1000; // 最小单笔金额
- const maxAmount = 8000; // 最大单笔金额
-
- while (remaining > maxAmount) {
- // 生成随机金额,确保剩余金额至少还能分一笔
- const maxThisAmount = Math.min(maxAmount, remaining - minAmount);
- const amount = Math.floor(Math.random() * (maxThisAmount - minAmount + 1)) + minAmount;
- amounts.push(amount);
- remaining -= amount;
- }
-
- // 最后一笔是剩余金额
- if (remaining > 0) {
- amounts.push(remaining);
- }
-
- return amounts;
- }
-
- /**
- * 生成3笔分配(兼容旧版本接口)
- * 确保不会分配给同一个用户
- * @param {number} orderId - 订单ID
- * @param {Array} amounts - 金额数组
- * @param {number} initiatorId - 发起人ID
- */
- /**
- * 验证匹配金额是否符合业务规则
- * @param {number} userId - 用户ID
- * @param {number} totalAmount - 总匹配金额
- * @param {Array} amounts - 分配金额数组
- * @returns {Object} 验证结果和建议金额
- */
- async validateMatchingAmount(userId, totalAmount, amounts) {
- const db = getDB();
-
- try {
- // 获取昨天的日期(本地时区)
- const yesterdayStr = dayjs().subtract(1, 'day').format('YYYY-MM-DD');
-
- // 获取前一天所有用户的出款总数(系统总出款)
- const [systemOutboundResult] = await db.execute(
- `SELECT SUM(oa.amount) as total_outbound
- FROM transfers oa
- JOIN users u ON oa.from_user_id = u.id
- WHERE DATE(oa.outbound_date) = ? AND oa.status = 'confirmed' AND u.is_system_account = FALSE`,
- [yesterdayStr]
- );
-
- const systemOutbound = systemOutboundResult[0].total_outbound || 0;
-
- // 获取前一天所有用户的具体出款金额(用于检查重复)
- const [yesterdayAmountsResult] = await db.execute(
- `SELECT DISTINCT oa.amount
- FROM transfers oa
- JOIN users u ON oa.from_user_id = u.id
- WHERE DATE(oa.outbound_date) = ? AND oa.status = 'confirmed' AND u.is_system_account = FALSE`,
- [yesterdayStr]
- );
-
- const yesterdayAmounts = yesterdayAmountsResult.map(row => row.amount);
-
- // 检查每笔金额是否与前一天的金额不同
- const duplicateAmounts = [];
- for (const amount of amounts) {
- if (yesterdayAmounts.includes(amount)) {
- duplicateAmounts.push(amount);
- }
- }
-
- return {
- isValid: duplicateAmounts.length === 0,
- systemOutbound,
- duplicateAmounts,
- suggestedAmount: systemOutbound,
- message: duplicateAmounts.length > 0
- ? `以下金额与前一天重复: ${duplicateAmounts.join(', ')}元`
- : '匹配金额验证通过'
- };
- } catch (error) {
- console.error('验证匹配金额失败:', error);
- return {
- isValid: false,
- systemOutbound: 0,
- duplicateAmounts: [],
- suggestedAmount: 0,
- message: '验证匹配金额时发生错误'
- };
- }
- }
-
- /**
- * 生成智能分配并创建数据库记录
- * @param {number} orderId - 订单ID
- * @param {number} initiatorId - 发起人ID
- * @returns {Promise} 返回分配结果数组
- */
- async generateSmartAllocationsWithDB(orderId, initiatorId) {
- const db = getDB();
-
- try {
- // 获取订单总金额
- const [orderResult] = await db.execute(
- 'SELECT amount FROM matching_orders WHERE id = ?',
- [orderId]
- );
-
- if (orderResult.length === 0) {
- throw new Error('匹配订单不存在');
- }
-
- const totalAmount = orderResult[0].amount;
-
- // 使用智能分配算法生成分配方案
- const allocations = await this.generateSmartAllocations(totalAmount, initiatorId);
-
- if (allocations.length === 0) {
- throw new Error('无法生成有效的分配方案');
- }
-
- // 验证总金额(简化版验证)
- const totalAllocated = allocations.reduce((sum, allocation) => sum + allocation.amount, 0);
- if (Math.abs(totalAllocated - totalAmount) > 0.01) {
- throw new Error(`分配金额不匹配:期望${totalAmount}元,实际分配${totalAllocated}元`);
- }
-
- console.log(`智能分配验证通过: 用户${initiatorId}, 匹配金额${totalAmount}元, 分配${allocations.length}笔`);
-
- // 创建分配记录
- const createdAllocations = [];
- for (let i = 0; i < allocations.length; i++) {
- const allocation = allocations[i];
-
- // 设置出款日期为今天,可回款时间为明天的00:00:00
- const today = dayjs();
- const tomorrow = dayjs().add(1, 'day').startOf('day');
-
- const [result] = await db.execute(
- 'INSERT INTO transfers (matching_order_id, from_user_id, to_user_id, amount, cycle_number, status, outbound_date, can_return_after) VALUES (?, ?, ?, ?, 1, "pending", CURDATE(), ?)',
- [orderId, initiatorId, allocation.userId, allocation.amount, tomorrow.format('YYYY-MM-DD HH:mm:ss')]
- );
-
- // 添加分配ID到结果中
- const createdAllocation = {
- ...allocation,
- allocationId: result.insertId,
- status: 'pending',
- outboundDate: today.format('YYYY-MM-DD'),
- canReturnAfter: tomorrow.toISOString()
- };
-
- createdAllocations.push(createdAllocation);
-
- console.log(`创建智能分配: ${allocation.amount}元 从用户${initiatorId} 到用户${allocation.userId}(${allocation.username}) [${allocation.userType}]`);
- }
-
- return createdAllocations;
- } catch (error) {
- console.error('生成智能分配失败:', error);
- throw error;
- }
- }
-
- /**
- * 生成传统三笔分配(保留原方法用于兼容性)
- * @param {number} orderId - 订单ID
- * @param {Array} amounts - 分配金额数组
- * @param {number} initiatorId - 发起人ID
- * @returns {Promise}
- */
- async generateThreeAllocations(orderId, amounts, initiatorId) {
- const db = getDB();
-
- try {
- // 获取订单总金额
- const [orderResult] = await db.execute(
- 'SELECT amount FROM matching_orders WHERE id = ?',
- [orderId]
- );
-
- if (orderResult.length === 0) {
- throw new Error('匹配订单不存在');
- }
-
- const totalAmount = orderResult[0].amount;
-
- // 验证匹配金额是否符合业务规则
- const validation = await this.validateMatchingAmount(initiatorId, totalAmount, amounts);
- if (!validation.isValid) {
- throw new Error(`匹配金额不符合业务规则:${validation.message}。建议匹配金额:${validation.suggestedAmount}元`);
- }
-
- // 记录验证信息
- console.log(`匹配金额验证通过: 用户${initiatorId}, 匹配金额${totalAmount}元, 前一天系统出款${validation.systemOutbound}元`);
-
- const usedTargetUsers = new Set(); // 记录已使用的目标用户
-
- for (let i = 0; i < amounts.length; i++) {
- const amount = amounts[i];
-
- // 获取匹配目标,排除已使用的用户
- const targetUser = await this.getMatchingTargetExcluding(initiatorId, usedTargetUsers);
-
- if (!targetUser) {
- throw new Error(`无法为第${i + 1}笔分配找到匹配目标`);
- }
-
- // 记录已使用的目标用户
- usedTargetUsers.add(targetUser);
-
- // 创建分配记录,默认为第1轮
- // 设置出款日期为今天,可回款时间为明天的00:00:00
- const today = new Date();
- const tomorrow = new Date(today);
- tomorrow.setDate(tomorrow.getDate() + 1);
- tomorrow.setHours(0, 0, 0, 0);
-
- // 将Date对象转换为MySQL兼容的字符串格式
- const tomorrowStr = tomorrow.toISOString().slice(0, 19).replace('T', ' ');
-
- await db.execute(
- 'INSERT INTO transfers (matching_order_id, from_user_id, to_user_id, amount, cycle_number, status, outbound_date, can_return_after) VALUES (?, ?, ?, ?, 1, "pending", CURDATE(), ?)',
- [orderId, initiatorId, targetUser, amount, tomorrowStr]
- );
-
- console.log(`创建分配: ${amount}元 从用户${initiatorId} 到用户${targetUser}`);
- }
- } catch (error) {
- console.error('生成分配失败:', error);
- throw error;
- }
- }
-
- /**
- * 获取匹配目标用户
- * @param {number} excludeUserId - 要排除的用户ID
- * @returns {number} 目标用户ID
- */
-
-
- /**
- * 获取匹配目标用户(排除指定用户集合)
- * @param {number} excludeUserId - 要排除的发起人用户ID
- * @param {Set} excludeUserIds - 要排除的用户ID集合
- * @returns {number} 目标用户ID
- */
- async getMatchingTargetExcluding(excludeUserId, excludeUserIds = new Set()) {
- const db = getDB();
-
- try {
- // 获取今天和昨天的日期(本地时区)
- const today = new Date();
- const todayStr = getLocalDateString(today);
- const yesterday = new Date();
- yesterday.setDate(yesterday.getDate() - 1);
- const yesterdayStr = getLocalDateString(yesterday);
-
- // 获取前一天打款的用户ID列表(需要排除)
- const [yesterdayPayersResult] = await db.execute(
- `SELECT DISTINCT oa.from_user_id
- FROM transfers oa
- WHERE DATE(oa.outbound_date) = ? AND oa.status = 'confirmed'`,
- [yesterdayStr]
- );
- const yesterdayPayers = yesterdayPayersResult.map(row => row.from_user_id);
-
- // 获取待确认/待处理/即将生成匹配金额总和超过0的普通用户(需要排除)
- const [pendingUsersResult] = await db.execute(
- `SELECT oa.to_user_id, SUM(oa.amount) as pending_amount
- FROM transfers oa
- JOIN users u ON oa.to_user_id = u.id
- WHERE oa.status IN ('pending', 'processing', 'generating')
- AND u.is_system_account = FALSE
- GROUP BY oa.to_user_id
- HAVING pending_amount > 0`
- );
- const pendingUsers = pendingUsersResult.map(row => row.to_user_id);
-
- // 获取当天有转出订单的普通用户(需要排除)
- const [todayPayersResult] = await db.execute(
- `SELECT DISTINCT oa.from_user_id
- FROM transfers oa
- JOIN users u ON oa.from_user_id = u.id
- WHERE DATE(oa.created_at) = ?
- AND oa.status IN ('confirmed', 'pending', 'processing')
- AND u.is_system_account = FALSE`,
- [todayStr]
- );
- const todayPayers = todayPayersResult.map(row => row.from_user_id);
-
- // 构建排除用户的条件(包括发起人、已使用的用户、昨天打款的用户、待处理用户、当天转出用户)
- const excludeList = [
- excludeUserId,
- ...Array.from(excludeUserIds),
- ...yesterdayPayers,
- ...pendingUsers,
- ...todayPayers
- ];
- const placeholders = excludeList.map(() => '?').join(',');
-
- // 第一优先级:最早成为负数且通过前面检查的普通用户
- // 使用最早的转出记录时间作为成为负数的参考时间
- const [earliestNegativeUsers] = await db.execute(
- `SELECT u.id, u.balance,
- (SELECT MIN(t.created_at) FROM transfers t
- WHERE t.from_user_id = u.id AND t.status IN ('confirmed', 'received')) as first_transfer_time
- FROM users u
- WHERE u.id NOT IN (${placeholders})
- AND u.is_system_account = FALSE
- AND u.balance < 0
- AND (SELECT COUNT(*) FROM transfers t
- WHERE t.from_user_id = u.id AND t.status IN ('confirmed', 'received')) > 0
- ORDER BY first_transfer_time ASC, u.balance ASC, RAND()
- LIMIT 1`,
- excludeList
- );
-
- if (earliestNegativeUsers.length > 0) {
- return earliestNegativeUsers[0].id;
- }
-
- // 第二优先级:有可回款分配的普通用户(昨天或更早出款,今天可以回款)
- // 但必须是负余额用户,且通过前面的检查
- const [returnableUsers] = await db.execute(
- `SELECT DISTINCT oa.from_user_id as id, u.balance
- FROM transfers oa
- JOIN matching_orders mo ON oa.id = mo.id
- JOIN users u ON oa.from_user_id = u.id
- WHERE oa.from_user_id NOT IN (${placeholders})
- AND oa.status = 'confirmed'
- AND oa.can_return_after <= NOW()
- AND oa.return_date IS NULL
- AND mo.status = 'matching'
- AND u.balance < 0
- AND u.is_system_account = FALSE
- ORDER BY oa.can_return_after ASC, u.balance ASC, RAND()
- LIMIT 1`,
- excludeList
- );
-
- if (returnableUsers.length > 0) {
- return returnableUsers[0].id;
- }
-
- // 第三优先级:其他负余额普通用户(余额为负数说明他们给其他用户转过钱,钱还没收回来)
- const [negativeBalanceUsers] = await db.execute(
- `SELECT id FROM users
- WHERE id NOT IN (${placeholders})
- AND is_system_account = FALSE
- AND balance < 0
- ORDER BY balance ASC, RAND()
- LIMIT 1`,
- excludeList
- );
-
- if (negativeBalanceUsers.length > 0) {
- return negativeBalanceUsers[0].id;
- }
-
- // 最后优先级:虚拟用户(系统账户)
- const [systemUsers] = await db.execute(
- `SELECT id FROM users
- WHERE is_system_account = TRUE AND id NOT IN (${placeholders})
- ORDER BY balance DESC, RAND()
- LIMIT 1`,
- excludeList
- );
-
- if (systemUsers.length > 0) {
- return systemUsers[0].id;
- }
-
- // 如果连系统账户都没有,抛出错误
- throw new Error('没有可用的匹配目标:所有符合条件的用户都被排除');
- } catch (error) {
- console.error('获取匹配目标失败:', error);
- throw error;
- }
- }
-
- // 获取可用用户
-
-
- /**
- * 生成智能分配金额
- * 1. 排除今天打款的用户
- * 2. 优先分配给负余额用户(余额+待确认收款为负数)
- * 3. 每笔最高5000,不够再分配给虚拟用户
- * 4. 笔数3-10笔
- * @param {number} totalAmount - 总金额
- * @param {number} excludeUserId - 排除的用户ID(发起人)
- * @returns {Promise} 分配金额数组
- */
- async generateSmartAllocations(totalAmount, excludeUserId) {
- const db = getDB();
- const minAmount = 100;
- const maxAmountPerTransfer = totalAmount;
- const minTransfers = totalAmount > 5000 ? 4 : 3;
- const maxTransfers = 10;
-
- try {
- // 首先获取当前用户的城市、省份和区域信息
- const [currentUserResult] = await db.execute(
- `SELECT city, province, district_id FROM users WHERE id = ?`,
- [excludeUserId]
- );
-
- const currentUserCity = currentUserResult[0]?.city;
- const currentUserProvince = currentUserResult[0]?.province;
- const currentUserDistrictId = currentUserResult[0]?.district_id;
-
- // 获取负余额用户,按区县、城市、省份优先级排序
- let [userBalanceResult] = await db.execute(
- `SELECT
- u.id as user_id,
- u.balance as current_balance,
- u.city,
- u.province,
- u.district_id
- FROM users u
- WHERE u.is_system_account = FALSE
- AND u.is_distribute = TRUE
- AND u.id != ?
- AND u.balance < -100
- AND u.audit_status = 'approved'
- AND u.user_type != 'directly_operated'
- AND u.payment_status = 'paid'
- AND u.province = ?
- ORDER BY
- CASE
- WHEN u.city = ? AND u.district_id = ? THEN 1 -- 相同城市且相同区县排第一
- WHEN u.city = ? THEN 2 -- 相同城市但不同区县排第二
- WHEN u.province = ? THEN 3 -- 相同省份但不同城市排第三
- ELSE 4 -- 其他省份排第四
- END,
- u.balance ASC`,
- [excludeUserId,currentUserProvince, currentUserCity, currentUserDistrictId, currentUserCity, currentUserProvince]
- );
-
- // 处理查询到的负余额用户
- const availableUsers = [];
-
- for (const user of userBalanceResult) {
- // 确保余额是数字类型
- const currentBalance = parseFloat(user.current_balance) || 0;
-
- // 更新用户对象
- user.current_balance = currentBalance;
-
- // 查询用户的分配订单金额统计
- const [orderStatusResult] = await db.execute(
- `SELECT
- SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount
- FROM transfers
- WHERE to_user_id = ?`,
- [user.user_id]
- );
-
- // 查询用户的分配订单金待确认金额统计
- const [orderStatusConfirmedResult] = await db.execute(
- `SELECT
- SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as confirmed_amount
- FROM transfers
- WHERE to_user_id = ?`,
- [user.user_id]
- );
- //查询用户给其他用户已确认的金额统计(要减去,因为款项还没回来)
- const [orderStatusConfirmedResultFrom] = await db.execute(
- `SELECT
- SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as confirmed_amount
- FROM transfers
- WHERE from_user_id = ?`,
- [user.user_id]
- );
- // 查询用户当天在matching_orders表中打出去的款项
- const today = getLocalDateString();
- const [todayOutflowResult] = await db.execute(
- `SELECT
- SUM(amount) as today_outflow
- FROM matching_orders
- WHERE initiator_id = ? AND DATE(updated_at) = ?`,
- [user.user_id, today]
- );
-
- // 添加分配金额信息到用户对象
- const orderStatus = orderStatusResult[0] || { pending_amount: 0 };
- const todayOutflow = todayOutflowResult[0] || { today_outflow: 0 };
- const orderStatusConfirmedFrom = orderStatusConfirmedResultFrom[0] || { confirmed_amount: 0 };
- const orderStatusConfirmed = orderStatusConfirmedResult[0] || { confirmed_amount: 0 };
- user.today_outflow = parseFloat(todayOutflow.today_outflow) || 0;
- user.pending_amount = parseFloat(orderStatus.pending_amount) || 0;
- user.confirmed_amount = parseFloat(orderStatusConfirmed.confirmed_amount) || 0;
- user.has_active_allocations = user.current_balance + user.pending_amount + user.confirmed_amount + user.today_outflow - orderStatusConfirmedFrom.confirmed_amount;
-
-
-
- // 所有查询到的用户都是负余额用户,直接添加到可用列表
- }
- userBalanceResult = userBalanceResult.filter(user => user.has_active_allocations < -100);
- userBalanceResult = userBalanceResult.sort((a, b) => {
- const getPriority = (user) => {
- if (user.district_id === currentUserDistrictId) return 1; // 同区县
- if (user.city === currentUserCity) return 2; // 同城市
- return 3; // 其他
- };
-
- const priorityA = getPriority(a);
- const priorityB = getPriority(b);
-
- if (priorityA !== priorityB) {
- return priorityA - priorityB; // 优先级小的排前
- }
-
- // 同优先级里:越接近0(数值越大)排前 -> 使用降序
- return b.has_active_allocations - a.has_active_allocations;
- });
- for (const user of userBalanceResult) {
- if (maxTransfers > availableUsers.length + 1) {
- if (minTransfers === 3 && availableUsers.length < 3) {
- availableUsers.push(user);
- }
- if (minTransfers === 4) {
- availableUsers.push(user);
- }
- }
- console.log(user, '普通用户');
- }
-
-
- console.log(`可参与分配的负余额用户数量: ${availableUsers.length}`);
-
- // 第二步:由于第一步已经筛选了余额小于0的用户,这里直接使用可用用户作为优先分配用户
- // 用户已按余额升序排列(最负的优先),然后按可分配金额降序排列
- const priorityUsers = availableUsers; // 所有可用用户都是负余额用户,无需再次筛选
-
- // 第三步:获取虚拟用户作为备选
- const [virtualUsersResult] = await db.execute(
- `SELECT id, username, balance FROM users
- WHERE is_system_account = TRUE
- ORDER BY balance DESC, RAND()`
- );
-
- // 计算分配方案
- const allocations = [];
- let remainingAmount = totalAmount;
-
- // 优先分配给当前余额为负的用户
- for (const user of priorityUsers) {
- if (remainingAmount <= 0 || allocations.length >= maxTransfers) break;
-
- // 计算该用户可接受的最大分配金额
- // 确保分配后用户余额不会变成正数
- const currentBalance = Math.abs(user.has_active_allocations);
- // 使用随机分配而不是平均分配
- const remainingTransfers = minTransfers - allocations.length;
- const minRequiredForRemaining = Math.max(0, (remainingTransfers - 1) * minAmount); // 为剩余转账预留的最小金额,确保不为负数
-
- console.log(`用户${user.user_id}分配计算: remainingAmount=${remainingAmount}, remainingTransfers=${remainingTransfers}, minRequiredForRemaining=${minRequiredForRemaining}`);
-
- const maxRandomAllocation = Math.min(
- currentBalance, // 不能超过安全分配额度,确保接收后余额不会变成正数
- maxAmountPerTransfer, // 单笔最大金额限制
- remainingAmount - minRequiredForRemaining // 确保剩余金额足够分配给后续转账
- );
-
- // 生成随机分配金额,使用极度偏向大值的算法
- let maxUserAllocation = 0;
- if (maxRandomAllocation >= minAmount) {
- const range = maxRandomAllocation - minAmount;
- if (range <= 0) {
- maxUserAllocation = minAmount;
- } else {
- if (maxRandomAllocation > 1000) {
- // 使用更均匀的分配策略
- const randomFactor = Math.random(); // 使用均匀分布
-
- // 基础分配:在整个范围内更均匀分布,减少偏向性
- const baseOffset = Math.floor(range * 0.15); // 降低到15%的基础偏移
- const adjustedRange = range - baseOffset;
- maxUserAllocation = Math.floor(randomFactor * adjustedRange) + minAmount + baseOffset;
-
- // 进一步减少额外增量的影响
- const bonusRange = Math.min(range * 0.1, maxRandomAllocation - maxUserAllocation); // 降低到10%
- if (bonusRange > 0 && Math.random() > 0.7) { // 30%概率获得额外增量,进一步降低
- const bonus = Math.floor(Math.random() * bonusRange * 0.3); // 使用30%的bonus范围
- maxUserAllocation += bonus;
- }
- }else{
- maxUserAllocation = maxRandomAllocation
+ // 创建匹配订单(支持两种模式)
+ async createMatchingOrder(userId, matchingType = 'small', customAmount = null) {
+ const db = getDB();
+
+ try {
+ // 检查用户是否被拉黑
+ const isBlacklisted = await timeoutService.isUserBlacklisted(userId);
+ if (isBlacklisted) {
+ throw new Error('您已被拉黑,无法参与匹配。如有疑问请联系管理员。');
}
+ // 检查用户审核状态、必要信息和余额
+ const [userResult] = await db.execute(
+ `SELECT audit_status,
+ balance,
+ wechat_qr,
+ alipay_qr,
+ unionpay_qr,
+ bank_card,
+ business_license,
+ id_card_front,
+ id_card_back
+ FROM users
+ WHERE id = ?`,
+ [userId]
+ );
- // 确保不超过最大限制
- maxUserAllocation = Math.min(maxUserAllocation, maxRandomAllocation);
- }
- }
- console.log(maxUserAllocation, minAmount, '+++++++++++++++');
-
- if (maxUserAllocation >= minAmount) {
- allocations.push({
- userId: user.user_id,
- username: user.username || `用户${user.user_id}`,
- amount: maxUserAllocation,
- userType: 'priority_user',
- currentBalance: user.current_balance,
- historicalNetBalance: user.historical_net_balance,
- totalPendingInflow: user.total_pending_inflow,
- availableForAllocation: user.available_for_allocation,
- todayOutflow: user.today_outflow,
- has_active_allocations: user.has_active_allocations
- });
- remainingAmount -= maxUserAllocation;
- }
- }
-
- // 如果还有剩余金额且分配数量不足最小笔数,最后分配给虚拟用户
- const availableVirtualUsers = virtualUsersResult
-
- // 如果有剩余金额,优先检查现有非虚拟用户是否还能消化
- if (remainingAmount > 0) {
- // 筛选出非虚拟用户分配记录
-
- if (allocations.length > 0) {
- let totalAvailableCapacity = 0;
- const userCapacities = [];
-
- // 计算每个用户的剩余可分配容量
- for (const allocation of allocations) {
- // 获取用户当前的实际余额状态(使用has_active_allocations作为实际可分配余额)
- const maxSafeAmount = Math.abs(allocation.has_active_allocations);
- const remainingCapacity = maxSafeAmount - allocation.amount;
-
- if (remainingCapacity > 0) {
- userCapacities.push({
- allocation,
- capacity: remainingCapacity
- });
- totalAvailableCapacity += remainingCapacity;
- }
- }
-
- console.log(`现有用户剩余容量: ${totalAvailableCapacity}, 待分配金额: ${remainingAmount}`);
-
- // 如果现有用户能够消化剩余金额
- if (totalAvailableCapacity >= remainingAmount && userCapacities.length > 0 && allocations.length >= 3) {
- // 按平均分配给这些用户,但需要检查每个用户的分配上限
- const averageAmount = Math.floor(remainingAmount / userCapacities.length);
- let distributedAmount = 0;
- let remainingToDistribute = remainingAmount;
-
- for (let i = 0; i < userCapacities.length; i++) {
- const { allocation, capacity } = userCapacities[i];
-
- // 计算本次可分配的金额
- let amountToAdd = 0;
-
- if (i === userCapacities.length - 1) {
- // 最后一个用户分配剩余的所有金额,但不能超过其容量
- amountToAdd = Math.min(remainingToDistribute, capacity);
- } else {
- // 其他用户按平均分配,但不能超过其容量
- amountToAdd = Math.min(averageAmount, capacity);
- }
-
- if (amountToAdd > 0) {
- allocation.amount += amountToAdd;
- distributedAmount += amountToAdd;
- remainingToDistribute -= amountToAdd;
- console.log(`为用户${allocation.userId}追加分配${amountToAdd}元,总分配${allocation.amount}元,剩余容量${capacity - amountToAdd}元`);
- }
+ if (userResult.length === 0) {
+ throw new Error('用户不存在');
}
- // 更新实际分配的剩余金额
- remainingAmount = remainingToDistribute;
+ const user = userResult[0];
- if (remainingAmount === 0) {
- console.log('剩余金额已全部分配给现有用户');
+ // 检查用户余额:只有负余额用户才能发起匹配
+ // if (user.balance > 0) {
+ // throw new Error('只有余额为负数的用户才能发起匹配,这是为了防止公司资金损失的重要规则');
+ // }
+
+ // 检查用户审核状态
+ if (user.audit_status !== 'approved') {
+ if (user.audit_status === 'pending') {
+ throw new Error('您的账户正在审核中,审核通过后才能参与匹配');
+ } else if (user.audit_status === 'rejected') {
+ throw new Error('您的账户审核未通过,请联系管理员');
+ }
+ }
+
+ // 检查必要的收款信息是否已上传
+ const missingItems = [];
+ if (!user.wechat_qr && !user.alipay_qr && !user.unionpay_qr) {
+ missingItems.push('收款码(微信/支付宝/云闪付至少一种)');
+ }
+ if (!user.bank_card) {
+ missingItems.push('银行卡号');
+ }
+ if (!user.business_license) {
+ missingItems.push('营业执照');
+ }
+ if (!user.id_card_front || !user.id_card_back) {
+ missingItems.push('身份证正反面');
+ }
+
+ if (missingItems.length > 0) {
+ throw new Error(`请先上传以下信息:${missingItems.join('、')}`);
+ }
+
+ await db.query('START TRANSACTION');
+
+ let totalAmount, maxCycles;
+
+ if (matchingType === 'small') {
+ // 小额匹配:固定5000元金额
+ totalAmount = 5000;
+ maxCycles = 1;
+ } else if (matchingType === 'large') {
+ // 大额匹配:用户自定义金额(最高5万)
+ if (!customAmount || customAmount < 3000 || customAmount > 50000) {
+ throw new Error('大额匹配金额必须在3000-50000之间');
+ }
+ totalAmount = customAmount;
+ maxCycles = 1;
} else {
- console.log(`部分剩余金额已分配给现有用户,仍有${remainingAmount}元未分配`);
+ throw new Error('不支持的匹配类型');
}
- }
+
+ // 创建匹配订单
+ const [result] = await db.execute(
+ 'INSERT INTO matching_orders (initiator_id, amount, status, max_cycles, matching_type) VALUES (?, ?, "matching", ?, ?)',
+ [userId, totalAmount, maxCycles, matchingType]
+ );
+
+ const orderId = result.insertId;
+
+ // 记录用户参与
+ await db.execute(
+ 'INSERT INTO matching_records (matching_order_id, user_id, action, amount) VALUES (?, ?, "join", ?)',
+ [orderId, userId, totalAmount]
+ );
+
+ await db.query('COMMIT');
+
+ // 立即生成智能分配
+ const allocations = await this.generateSmartAllocationsWithDB(orderId, userId);
+
+ return {
+ orderId,
+ matchingType,
+ totalAmount,
+ allocations: allocations || [],
+ allocationCount: allocations ? allocations.length : 0
+ };
+ } catch (error) {
+ await db.query('ROLLBACK');
+ console.error('创建匹配订单失败:', error);
+ throw error;
}
- }
+ }
- // 如果仍有剩余金额,检查是否有未分配的用户可以消化剩余金额
- if (remainingAmount > 0) {
- // 获取已分配的用户ID列表
- const allocatedUserIds = new Set(allocations.map(a => a.userId));
+ /**
+ * 检查资金平衡并触发系统账户反向匹配
+ * 当收款需求大于打款资金时,系统账户主动发起匹配
+ */
+ async checkAndTriggerSystemMatching() {
+ const db = getDB();
- // 从原始用户列表中找到未分配的用户
- const unallocatedUsers = userBalanceResult.filter(user => !allocatedUserIds.has(user.user_id));
+ try {
+ // 计算当前待收款总额(负余额用户的资金缺口)
+ const [negativeBalanceResult] = await db.execute(`
+ SELECT SUM(ABS(balance)) as total_deficit
+ FROM users
+ WHERE is_system_account = FALSE
+ AND balance < 0
+ `);
- if (unallocatedUsers.length > 0) {
- console.log(`发现${unallocatedUsers.length}个未分配的用户,剩余金额: ${remainingAmount}`);
+ const totalDeficit = negativeBalanceResult[0].total_deficit || 0;
- // 查找可分配金额大于剩余金额的用户
- for (const user of unallocatedUsers) {
- const maxSafeAmount = Math.abs(user.has_active_allocations);
+ // 计算当前待打款总额(pending状态的分配)
+ const [pendingPaymentsResult] = await db.execute(`
+ SELECT SUM(oa.amount) as total_pending
+ FROM transfers oa
+ JOIN users u ON oa.from_user_id = u.id
+ WHERE oa.status = 'pending'
+ AND u.is_system_account = FALSE
+ `);
- if (maxSafeAmount >= remainingAmount) {
- // 找到合适的用户,分配剩余金额
- allocations.push({
- userId: user.user_id,
- username: user.username || `User${user.user_id}`,
- amount: remainingAmount,
- userType: 'priority_user',
- currentBalance: user.current_balance,
- availableForAllocation: user.has_active_allocations
- });
+ const totalPendingPayments = pendingPaymentsResult[0].total_pending || 0;
- console.log(`为未分配用户${user.user_id}分配剩余金额${remainingAmount}元`);
- remainingAmount = 0;
- break;
+ console.log(`资金平衡检查: 总资金缺口=${totalDeficit}, 待打款总额=${totalPendingPayments}`);
+
+ // 如果收款需求大于打款资金,触发系统账户反向匹配
+ if (totalDeficit > totalPendingPayments) {
+ const shortfall = totalDeficit - totalPendingPayments;
+ console.log(`检测到资金缺口: ${shortfall}元,触发系统账户反向匹配`);
+
+ await this.createSystemReverseMatching(shortfall);
}
- }
+ } catch (error) {
+ console.error('检查资金平衡失败:', error);
+ // 不抛出错误,避免影响主流程
}
- }
+ }
- // 如果仍有剩余金额,分配给虚拟用户
- if (remainingAmount > 0 && availableVirtualUsers.length > 0) {
- const maxPossibleTransfers = Math.min((minTransfers - allocations.length) <= 0 ? 1 : minTransfers - allocations.length, availableVirtualUsers.length);
+ /**
+ * 创建系统账户反向匹配
+ * 系统账户作为付款方,向有资金缺口的用户打款
+ * @param {number} targetAmount - 目标匹配金额
+ */
+ async createSystemReverseMatching(targetAmount) {
+ const db = getDB();
- // 生成随机分配金额数组
- const randomAmounts = this.generateRandomAmounts(remainingAmount, maxPossibleTransfers, minAmount, maxAmountPerTransfer);
+ try {
+ // 获取可用的系统账户
+ const [systemAccounts] = await db.execute(`
+ SELECT id, balance
+ FROM users
+ WHERE is_system_account = TRUE
+ AND balance > 1000
+ ORDER BY balance DESC
+ LIMIT 1
+ `);
- // 为每个随机金额分配虚拟用户
- for (let i = 0; i < randomAmounts.length && availableVirtualUsers.length > 0; i++) {
- const randomIndex = Math.floor(Math.random() * availableVirtualUsers.length);
- const virtualUser = availableVirtualUsers[randomIndex];
+ if (systemAccounts.length === 0) {
+ console.log('没有可用的系统账户进行反向匹配');
+ return;
+ }
- allocations.push({
- userId: virtualUser.id,
- username: virtualUser.username,
- amount: randomAmounts[i],
- userType: 'virtual',
- balance: virtualUser.balance
- });
+ const systemAccount = systemAccounts[0];
- remainingAmount -= randomAmounts[i];
- availableVirtualUsers.splice(randomIndex, 1);
+ // 确定实际匹配金额(不超过系统账户余额的80%)
+ const maxMatchAmount = Math.min(targetAmount, systemAccount.balance * 0.8);
+
+ if (maxMatchAmount < 1000) {
+ console.log('系统账户余额不足,无法进行反向匹配');
+ return;
+ }
+
+ // 创建系统反向匹配订单
+ const [result] = await db.execute(
+ 'INSERT INTO matching_orders (initiator_id, amount, status, max_cycles, matching_type, is_system_reverse) VALUES (?, ?, "matching", 1, "system_reverse", TRUE)',
+ [systemAccount.id, maxMatchAmount]
+ );
+
+ const orderId = result.insertId;
+
+ // 生成分配给负余额用户
+ await this.generateSystemReverseAllocations(orderId, maxMatchAmount, systemAccount.id);
+
+ console.log(`系统反向匹配创建成功: 订单ID=${orderId}, 金额=${maxMatchAmount}`);
+
+ } catch (error) {
+ console.error('创建系统反向匹配失败:', error);
}
- }
+ }
- // 检查是否有足够的用户来完成分配
- if (remainingAmount > 0 && allocations.length < minTransfers && availableVirtualUsers.length === 0) {
- throw new Error('没有足够的可用用户来完成分配(避免重复分配给同一用户)');
- }
+ /**
+ * 为系统反向匹配生成分配
+ * @param {number} orderId - 匹配订单ID
+ * @param {number} totalAmount - 总金额
+ * @param {number} systemUserId - 系统账户ID
+ */
+ async generateSystemReverseAllocations(orderId, totalAmount, systemUserId) {
+ const db = getDB();
- // 确保至少有最小笔数
- // if (allocations.length < minTransfers) {
- // throw new Error(`无法生成足够的分配:需要至少${minTransfers}笔,但只能生成${allocations.length}笔`);
- // }
+ try {
+ // 获取负余额用户,按缺口大小排序
+ const [negativeUsers] = await db.execute(`
+ SELECT id, balance, ABS(balance) as deficit
+ FROM users
+ WHERE is_system_account = FALSE
+ AND balance < 0
+ ORDER BY deficit DESC
+ LIMIT 10
+ `);
- // 精确控制总金额,避免超出预期
- const currentTotal = allocations.reduce((sum, a) => sum + a.amount, 0);
- console.log('剩余金额处理前:', remainingAmount, '当前总分配金额:', currentTotal, '期望总金额:', totalAmount);
+ if (negativeUsers.length === 0) {
+ console.log('没有负余额用户需要反向匹配');
+ return;
+ }
- if (remainingAmount > 0 && allocations.length > 0) {
- // 检查是否会超出总金额
- if (currentTotal + remainingAmount <= totalAmount) {
- console.log('将剩余金额', remainingAmount, '加到最后一笔分配');
- allocations[allocations.length - 1].amount += remainingAmount;
- } else {
- // 如果会超出,只加到刚好等于总金额的部分
- const allowedAmount = totalAmount - currentTotal;
- if (allowedAmount > 0) {
+ // 按比例分配金额给负余额用户
+ const totalDeficit = negativeUsers.reduce((sum, user) => sum + user.deficit, 0);
+ let remainingAmount = totalAmount;
- console.log('调整最后一笔分配,增加', allowedAmount, '元以达到精确总金额');
- allocations[allocations.length - 1].amount += allowedAmount;
- }
+ for (let i = 0; i < negativeUsers.length && remainingAmount > 0; i++) {
+ const user = negativeUsers[i];
+ let allocationAmount;
+
+ if (i === negativeUsers.length - 1) {
+ // 最后一个用户分配剩余金额
+ allocationAmount = remainingAmount;
+ } else {
+ // 按比例分配
+ const proportion = user.deficit / totalDeficit;
+ allocationAmount = Math.min(
+ Math.floor(totalAmount * proportion),
+ user.deficit,
+ remainingAmount
+ );
+ }
+
+ if (allocationAmount > 0) {
+ // 创建分配记录(系统账户向用户转账)
+ await db.execute(
+ 'INSERT INTO transfers (matching_order_id, from_user_id, to_user_id, amount, cycle_number, status) VALUES (?, ?, ?, ?, 1, "pending")',
+ [orderId, systemUserId, user.id, allocationAmount]
+ );
+
+ remainingAmount -= allocationAmount;
+ console.log(`系统反向分配: ${allocationAmount}元 从系统账户${systemUserId} 到用户${user.id}`);
+ }
+ }
+
+ } catch (error) {
+ console.error('生成系统反向分配失败:', error);
+ throw error;
}
- remainingAmount = 0; // 重置剩余金额
- }
-
- console.log(`智能分配完成: 总金额${totalAmount}元,分配${allocations.length}笔`);
- console.log('分配详情:', allocations.map(a =>
- `${a.amount}元 -> 用户${a.userId}(${a.username}) [${a.userType}]`
- ).join(', '));
-
- return allocations;
-
- } catch (error) {
- console.error('智能分配失败:', error);
- throw error;
- }
- }
-
- /**
- * 生成随机分配金额数组(更均匀的分配策略)
- * @param {number} totalAmount - 总金额
- * @param {number} transferCount - 分配笔数
- * @param {number} minAmount - 最小金额
- * @param {number} maxAmount - 最大金额
- * @returns {number[]} 随机金额数组
- */
- generateRandomAmounts(totalAmount, transferCount, minAmount, maxAmount) {
- if (transferCount <= 0 || totalAmount < minAmount * transferCount) {
- return [];
}
- // 使用更均匀的分配策略
- const amounts = [];
+ /**
+ * 生成大额匹配的随机金额分拆(15000以上)
+ * @param {number} totalAmount - 总金额
+ * @returns {Array} 分拆后的金额数组
+ */
+ generateRandomLargeAmounts(totalAmount) {
+ const amounts = [];
+ let remaining = totalAmount;
+ const minAmount = 1000; // 最小单笔金额
+ const maxAmount = 8000; // 最大单笔金额
- // 首先为每笔分配最小金额
- for (let i = 0; i < transferCount; i++) {
- amounts.push(minAmount);
- }
-
- let remainingToDistribute = totalAmount - (minAmount * transferCount);
-
- // 计算平均每笔应该额外分配的金额
- const averageExtra = Math.floor(remainingToDistribute / transferCount);
-
- // 为每笔添加平均额外金额,但加入一些随机性
- for (let i = 0; i < transferCount && remainingToDistribute > 0; i++) {
- // 计算这笔最多还能增加多少(不超过maxAmount)
- const maxPossibleIncrease = Math.min(
- maxAmount - amounts[i],
- remainingToDistribute
- );
-
- if (maxPossibleIncrease > 0) {
- // 在平均值附近随机分配,但控制在更小的范围内以保证更均匀
- const baseIncrease = Math.min(averageExtra, maxPossibleIncrease);
- const randomVariation = Math.floor(baseIncrease * 0.15); // 减少到15%的随机变化
- const minIncrease = Math.max(0, baseIncrease - randomVariation);
- const maxIncrease = Math.min(maxPossibleIncrease, baseIncrease + randomVariation);
-
- const increase = Math.floor(Math.random() * (maxIncrease - minIncrease + 1)) + minIncrease;
- amounts[i] += increase;
- remainingToDistribute -= increase;
- }
- }
-
- // 如果还有剩余金额,尽量均匀分配给还能接受的笔数
- while (remainingToDistribute > 0) {
- const availableIndices = [];
- for (let i = 0; i < transferCount; i++) {
- if (amounts[i] < maxAmount) {
- availableIndices.push(i);
+ while (remaining > maxAmount) {
+ // 生成随机金额,确保剩余金额至少还能分一笔
+ const maxThisAmount = Math.min(maxAmount, remaining - minAmount);
+ const amount = Math.floor(Math.random() * (maxThisAmount - minAmount + 1)) + minAmount;
+ amounts.push(amount);
+ remaining -= amount;
}
- }
- if (availableIndices.length === 0) {
- break; // 无法继续分配
- }
-
- // 计算每个可用位置应该分配多少
- const perIndexAmount = Math.floor(remainingToDistribute / availableIndices.length);
- const remainder = remainingToDistribute % availableIndices.length;
-
- // 为每个可用位置分配相等的金额
- for (let i = 0; i < availableIndices.length && remainingToDistribute > 0; i++) {
- const index = availableIndices[i];
- const maxIncrease = Math.min(maxAmount - amounts[index], remainingToDistribute);
-
- if (maxIncrease > 0) {
- // 基础分配金额
- let increase = Math.min(perIndexAmount, maxIncrease);
-
- // 如果是前几个位置,额外分配余数
- if (i < remainder) {
- increase = Math.min(increase + 1, maxIncrease);
- }
-
- amounts[index] += increase;
- remainingToDistribute -= increase;
+ // 最后一笔是剩余金额
+ if (remaining > 0) {
+ amounts.push(remaining);
}
- }
- // 如果所有位置都已达到最大值,退出循环
- if (perIndexAmount === 0 && remainder === 0) {
- break;
- }
+ return amounts;
}
- // 如果还有剩余金额无法分配,返回空数组表示失败
- if (remainingToDistribute > 0) {
- return [];
- }
+ /**
+ * 生成3笔分配(兼容旧版本接口)
+ * 确保不会分配给同一个用户
+ * @param {number} orderId - 订单ID
+ * @param {Array} amounts - 金额数组
+ * @param {number} initiatorId - 发起人ID
+ */
+ /**
+ * 验证匹配金额是否符合业务规则
+ * @param {number} userId - 用户ID
+ * @param {number} totalAmount - 总匹配金额
+ * @param {Array} amounts - 分配金额数组
+ * @returns {Object} 验证结果和建议金额
+ */
+ async validateMatchingAmount(userId, totalAmount, amounts) {
+ const db = getDB();
- return amounts;
- }
+ try {
+ // 获取昨天的日期(本地时区)
+ const yesterdayStr = dayjs().subtract(1, 'day').format('YYYY-MM-DD');
- // 生成3笔随机金额,总计指定金额(保留原方法用于兼容性)
- generateThreeRandomAmounts(totalAmount) {
- // 确保总金额足够分配三笔最小金额
- const minAmount = 500;
- const maxAmount = Math.min(5000, totalAmount - 2 * minAmount);
+ // 获取前一天所有用户的出款总数(系统总出款)
+ const [systemOutboundResult] = await db.execute(
+ `SELECT SUM(oa.amount) as total_outbound
+ FROM transfers oa
+ JOIN users u ON oa.from_user_id = u.id
+ WHERE DATE(oa.outbound_date) = ?
+ AND oa.status = 'confirmed'
+ AND u.is_system_account = FALSE`,
+ [yesterdayStr]
+ );
- // 生成第一笔金额 (500-5000)
- const amount1 = Math.floor(Math.random() * (maxAmount - minAmount + 1)) + minAmount;
+ const systemOutbound = systemOutboundResult[0].total_outbound || 0;
- // 生成第二笔金额 (500-剩余金额-500)
- const remaining1 = totalAmount - amount1;
- const maxAmount2 = Math.min(5000, remaining1 - minAmount);
- const amount2 = Math.floor(Math.random() * (maxAmount2 - minAmount + 1)) + minAmount;
+ // 获取前一天所有用户的具体出款金额(用于检查重复)
+ const [yesterdayAmountsResult] = await db.execute(
+ `SELECT DISTINCT oa.amount
+ FROM transfers oa
+ JOIN users u ON oa.from_user_id = u.id
+ WHERE DATE(oa.outbound_date) = ?
+ AND oa.status = 'confirmed'
+ AND u.is_system_account = FALSE`,
+ [yesterdayStr]
+ );
- // 第三笔是剩余金额
- const amount3 = totalAmount - amount1 - amount2;
+ const yesterdayAmounts = yesterdayAmountsResult.map(row => row.amount);
- return [amount1, amount2, amount3];
- }
+ // 检查每笔金额是否与前一天的金额不同
+ const duplicateAmounts = [];
+ for (const amount of amounts) {
+ if (yesterdayAmounts.includes(amount)) {
+ duplicateAmounts.push(amount);
+ }
+ }
- // 生成随机金额(保留原方法用于其他地方)
-
-
- /**
- * 确认分配并创建转账记录
- * @param {number} allocationId - 分配ID
- * @param {number} userId - 用户ID
- * @param {number} transferAmount - 实际转账金额(用于校验)
- * @param {string} description - 转账描述
- * @param {string} voucher - 转账凭证URL
- * @returns {number} 转账记录ID
- */
- async confirmAllocation(allocationId, userId, transferAmount = null, description = null, voucher = null) {
- const db = getDB();
-
- try {
- await db.query('START TRANSACTION');
-
- // 获取分配信息
- const [allocations] = await db.execute(
- 'SELECT * FROM transfers WHERE id = ? AND from_user_id = ?',
- [allocationId, userId]
- );
-
- if (allocations.length === 0) {
- throw new Error('分配不存在或无权限');
- }
-
- const allocation = allocations[0];
-
- // 校验转账金额(如果提供了转账金额)
- if (transferAmount !== null) {
- const expectedAmount = parseFloat(allocation.amount);
- const actualAmount = parseFloat(transferAmount);
-
- if (Math.abs(expectedAmount - actualAmount) > 0.01) {
- throw new Error(`转账金额不匹配!应转账 ${expectedAmount} 元,实际转账 ${actualAmount} 元`);
+ return {
+ isValid: duplicateAmounts.length === 0,
+ systemOutbound,
+ duplicateAmounts,
+ suggestedAmount: systemOutbound,
+ message: duplicateAmounts.length > 0
+ ? `以下金额与前一天重复: ${duplicateAmounts.join(', ')}元`
+ : '匹配金额验证通过'
+ };
+ } catch (error) {
+ console.error('验证匹配金额失败:', error);
+ return {
+ isValid: false,
+ systemOutbound: 0,
+ duplicateAmounts: [],
+ suggestedAmount: 0,
+ message: '验证匹配金额时发生错误'
+ };
}
- }
-
- // 检查分配状态
- if (allocation.status !== 'pending') {
- throw new Error('该分配已处理,无法重复确认');
- }
-
- // 检查匹配订单是否已超时
- const [matchingOrder] = await db.execute(
- 'SELECT * FROM matching_orders WHERE id = ?',
- [allocation.matching_order_id]
- );
-
- if (matchingOrder.length === 0) {
- throw new Error('匹配订单不存在');
- }
-
- // 检查订单是否已被取消(超时会导致订单被取消)
- if (matchingOrder[0].status === 'cancelled') {
- throw new Error('该匹配订单已超时取消,无法进行转账');
- }
-
- // 检查是否存在相关的超时转账记录
- const [timeoutTransfers] = await db.execute(
- `SELECT COUNT(*) as count FROM transfers
- WHERE (from_user_id = ? OR to_user_id = ?)
- AND is_overdue = 1
- AND description LIKE ?`,
- [userId, userId, `%匹配订单 ${allocation.matching_order_id}%`]
- );
-
- if (timeoutTransfers[0].count > 0) {
- throw new Error('该匹配订单存在超时记录,无法继续转账。请联系管理员处理');
- }
-
- // 计算3小时后的截止时间
- const deadline = dayjs().add(3, 'hour').toDate();
-
- // 更新转账记录状态为confirmed,跳过待确认环节
- const transferDescription = description || `匹配订单 ${allocation.matching_order_id} 第 ${allocation.cycle_number} 轮转账`;
- const [transferResult] = await db.execute(
- `UPDATE transfers
- SET status = "confirmed", description = ?, deadline_at = ?, confirmed_at = NOW(), voucher_url = ?
- WHERE id = ?`,
- [
- transferDescription,
- deadline,
- voucher,
- allocationId
- ]
- );
-
- // 注意:发送方余额将在接收方确认收款时扣除,而不是在确认转账时扣除
- // 这样可以避免资金被锁定但收款方未确认的情况
-
- // 记录确认动作
- await db.execute(
- 'INSERT INTO matching_records (matching_order_id, user_id, action, amount, note) VALUES (?, ?, "confirm", ?, ?)',
- [
- allocation.matching_order_id,
- userId,
- allocation.amount,
- transferAmount ? `实际转账金额: ${transferAmount}` : null
- ]
- );
-
- await db.query('COMMIT');
-
- // 检查是否需要进入下一轮
- await this.checkCycleCompletion(allocation.matching_order_id, allocation.cycle_number);
-
- return transferResult.insertId;
- } catch (error) {
- await db.query('ROLLBACK');
- throw error;
}
- }
- // 检查轮次完成情况
- async checkCycleCompletion(matchingOrderId, cycleNumber) {
- const db = getDB();
+ /**
+ * 生成智能分配并创建数据库记录
+ * @param {number} orderId - 订单ID
+ * @param {number} initiatorId - 发起人ID
+ * @returns {Promise} 返回分配结果数组
+ */
+ async generateSmartAllocationsWithDB(orderId, initiatorId) {
+ const db = getDB();
- try {
- // 检查当前轮次是否全部确认
- const [pending] = await db.execute(
- 'SELECT COUNT(*) as count FROM transfers WHERE matching_order_id = ? AND cycle_number = ? AND status = "pending"',
- [matchingOrderId, cycleNumber]
- );
+ try {
+ await db.query('START TRANSACTION');
+ // 获取订单总金额
+ const [orderResult] = await db.execute(
+ 'SELECT amount FROM matching_orders WHERE id = ? FOR UPDATE',
+ [orderId]
+ );
+ //获取用户余额
+ const [user] = await db.execute(`
+ SELECT balance
+ FROM users
+ WHERE id = ${initiatorId}
+ `)
- if (pending[0].count === 0) {
- // 当前轮次完成,检查是否需要下一轮
- const [order] = await db.execute(
- 'SELECT * FROM matching_orders WHERE id = ?',
- [matchingOrderId]
- );
+ if (orderResult.length === 0) {
+ await db.query('ROLLBACK');
+ throw new Error('匹配订单不存在');
+ }
- const currentOrder = order[0];
+ const totalAmount = orderResult[0].amount;
- if (currentOrder.cycle_count + 1 < currentOrder.max_cycles) {
- // 开始下一轮
- await db.execute(
- 'UPDATE matching_orders SET cycle_count = cycle_count + 1 WHERE id = ?',
- [matchingOrderId]
- );
+ // 使用智能分配算法生成分配方案
+ const allocations = await this.generateSmartAllocations(totalAmount, initiatorId);
- // 生成下一轮分配
- const amounts = this.generateThreeRandomAmounts(currentOrder.amount);
- await this.generateThreeAllocations(matchingOrderId, amounts, currentOrder.initiator_id);
- } else {
- // 完成所有轮次
- await db.execute(
- 'UPDATE matching_orders SET status = "completed" WHERE id = ?',
- [matchingOrderId]
- );
+ if (allocations.length === 0) {
+ await db.query('ROLLBACK');
+ throw new Error('无法生成有效的分配方案');
+ }
- console.log(`匹配订单 ${matchingOrderId} 已完成所有轮次`);
+ // 验证总金额(简化版验证)
+ const totalAllocated = allocations.reduce((sum, allocation) => sum + allocation.amount, 0);
+ if (Math.abs(totalAllocated - totalAmount) > 0.01) {
+ await db.query('ROLLBACK');
+ throw new Error(`分配金额不匹配:期望${totalAmount}元,实际分配${totalAllocated}元`);
+ }
- // 检查用户是否完成第三次匹配,如果是则给代理分佣
- await this.checkAndProcessAgentCommission(currentOrder.initiator_id);
+ console.log(`智能分配验证通过: 用户${initiatorId}, 匹配金额${totalAmount}元, 分配${allocations.length}笔`);
+
+ // 创建分配记录
+ const createdAllocations = [];
+ let from_user_balance = user[0].balance
+ for (let i = 0; i < allocations.length; i++) {
+ const allocation = allocations[i];
+ from_user_balance -= allocation.amount
+ // 设置出款日期为今天,可回款时间为明天的00:00:00
+ const today = dayjs();
+ const tomorrow = dayjs().add(1, 'day').startOf('day');
+ const [result] = await db.execute(
+ 'INSERT INTO transfers (matching_order_id, from_user_id, to_user_id, amount, cycle_number, status, outbound_date, can_return_after,from_user_balance,to_user_balance) VALUES (?, ?, ?, ?, 1, "pending", CURDATE(), ?,?,?)',
+ [orderId, initiatorId, allocation.userId, allocation.amount, tomorrow.format('YYYY-MM-DD HH:mm:ss'), from_user_balance, Number(allocation.currentBalance) + Number(allocation.amount)]
+ );
+
+ // 添加分配ID到结果中
+ const createdAllocation = {
+ ...allocation,
+ allocationId: result.insertId,
+ status: 'pending',
+ outboundDate: today.format('YYYY-MM-DD'),
+ canReturnAfter: tomorrow.toISOString()
+ };
+
+ createdAllocations.push(createdAllocation);
+
+ console.log(`创建智能分配: ${allocation.amount}元 从用户${initiatorId} 到用户${allocation.userId}(${allocation.username}) [${allocation.userType}]`);
+ }
+ await db.query('COMMIT');
+
+ return createdAllocations;
+ } catch (error) {
+ await db.query('ROLLBACK');
+ console.error('生成智能分配失败:', error);
+ throw error;
}
- }
- } catch (error) {
- console.error('检查轮次完成情况失败:', error);
- throw error;
}
- }
- // 获取用户的匹配订单
- async getUserMatchingOrders(userId, page = 1, limit = 10) {
- const db = getDB();
- const offset = (parseInt(page) - 1) * parseInt(limit);
+ /**
+ * 生成传统三笔分配(保留原方法用于兼容性)
+ * @param {number} orderId - 订单ID
+ * @param {Array} amounts - 分配金额数组
+ * @param {number} initiatorId - 发起人ID
+ * @returns {Promise}
+ */
+ async generateThreeAllocations(orderId, amounts, initiatorId) {
+ const db = getDB();
+ try {
+ // 获取订单总金额
+ const [orderResult] = await db.execute(
+ 'SELECT amount FROM matching_orders WHERE id = ?',
+ [orderId]
+ );
+ if (orderResult.length === 0) {
+ throw new Error('匹配订单不存在');
+ }
- try {
- // 获取用户发起的订单
- 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.initiator_id = ?
- ORDER BY mo.created_at DESC
- LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`,
- [userId]
- );
+ const totalAmount = orderResult[0].amount;
- // 同时获取系统反向匹配订单(如果用户参与了分配)
- const [systemOrders] = await db.execute(
- `SELECT DISTINCT mo.*, u.username as initiator_name
- FROM matching_orders mo
- JOIN users u ON mo.initiator_id = u.id
- JOIN transfers oa ON mo.id = oa.id
- WHERE mo.is_system_reverse = TRUE AND oa.to_user_id = ?
- ORDER BY mo.created_at DESC
- LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`,
- [userId]
- );
+ // 验证匹配金额是否符合业务规则
+ const validation = await this.validateMatchingAmount(initiatorId, totalAmount, amounts);
+ if (!validation.isValid) {
+ throw new Error(`匹配金额不符合业务规则:${validation.message}。建议匹配金额:${validation.suggestedAmount}元`);
+ }
- // 合并订单列表
- const allOrders = [...orders, ...systemOrders];
+ // 记录验证信息
+ console.log(`匹配金额验证通过: 用户${initiatorId}, 匹配金额${totalAmount}元, 前一天系统出款${validation.systemOutbound}元`);
- // 按创建时间排序
- allOrders.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
+ const usedTargetUsers = new Set(); // 记录已使用的目标用户
- // 为每个订单获取分配信息
- for (let order of allOrders) {
- const [allocations] = await db.execute(
- `SELECT * FROM transfers WHERE matching_order_id = ? ORDER BY cycle_number, created_at`,
- [order.id]
- );
- order.allocations = allocations;
- }
+ for (let i = 0; i < amounts.length; i++) {
+ const amount = amounts[i];
- return allOrders;
- } catch (error) {
- console.error('获取用户匹配订单失败:', error);
- throw error;
+ // 获取匹配目标,排除已使用的用户
+ const targetUser = await this.getMatchingTargetExcluding(initiatorId, usedTargetUsers);
+
+ if (!targetUser) {
+ throw new Error(`无法为第${i + 1}笔分配找到匹配目标`);
+ }
+
+ // 记录已使用的目标用户
+ usedTargetUsers.add(targetUser);
+
+ // 创建分配记录,默认为第1轮
+ // 设置出款日期为今天,可回款时间为明天的00:00:00
+ const today = new Date();
+ const tomorrow = new Date(today);
+ tomorrow.setDate(tomorrow.getDate() + 1);
+ tomorrow.setHours(0, 0, 0, 0);
+
+ // 将Date对象转换为MySQL兼容的字符串格式
+ const tomorrowStr = tomorrow.toISOString().slice(0, 19).replace('T', ' ');
+
+ await db.execute(
+ 'INSERT INTO transfers (matching_order_id, from_user_id, to_user_id, amount, cycle_number, status, outbound_date, can_return_after) VALUES (?, ?, ?, ?, 1, "pending", CURDATE(), ?)',
+ [orderId, initiatorId, targetUser, amount, tomorrowStr]
+ );
+
+ console.log(`创建分配: ${amount}元 从用户${initiatorId} 到用户${targetUser}`);
+ }
+ } catch (error) {
+ console.error('生成分配失败:', error);
+ throw error;
+ }
}
- }
+
+ /**
+ * 获取匹配目标用户
+ * @param {number} excludeUserId - 要排除的用户ID
+ * @returns {number} 目标用户ID
+ */
+ /**
+ * 获取匹配目标用户(排除指定用户集合)
+ * @param {number} excludeUserId - 要排除的发起人用户ID
+ * @param {Set} excludeUserIds - 要排除的用户ID集合
+ * @returns {number} 目标用户ID
+ */
+ async getMatchingTargetExcluding(excludeUserId, excludeUserIds = new Set()) {
+ const db = getDB();
- // 获取用户待处理的分配
- async getUserPendingAllocations(userId) {
- const db = getDB();
+ try {
+ // 获取今天和昨天的日期(本地时区)
+ const today = new Date();
+ const todayStr = getLocalDateString(today);
+ const yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
+ const yesterdayStr = getLocalDateString(yesterday);
- try {
- const [allocations] = await db.execute(
- `(SELECT oa.*, mo.amount as total_amount, mo.status as order_status, u.username as to_user_name,
+ // 获取前一天打款的用户ID列表(需要排除)
+ const [yesterdayPayersResult] = await db.execute(
+ `SELECT DISTINCT oa.from_user_id
+ FROM transfers oa
+ WHERE DATE(oa.outbound_date) = ?
+ AND oa.status = 'confirmed'`,
+ [yesterdayStr]
+ );
+ const yesterdayPayers = yesterdayPayersResult.map(row => row.from_user_id);
+
+ // 获取待确认/待处理/即将生成匹配金额总和超过0的普通用户(需要排除)
+ const [pendingUsersResult] = await db.execute(
+ `SELECT oa.to_user_id, SUM(oa.amount) as pending_amount
+ FROM transfers oa
+ JOIN users u ON oa.to_user_id = u.id
+ WHERE oa.status IN ('pending', 'processing', 'generating')
+ AND u.is_system_account = FALSE
+ GROUP BY oa.to_user_id
+ HAVING pending_amount > 0`
+ );
+ const pendingUsers = pendingUsersResult.map(row => row.to_user_id);
+
+ // 获取当天有转出订单的普通用户(需要排除)
+ const [todayPayersResult] = await db.execute(
+ `SELECT DISTINCT oa.from_user_id
+ FROM transfers oa
+ JOIN users u ON oa.from_user_id = u.id
+ WHERE DATE(oa.created_at) = ?
+ AND oa.status IN ('confirmed', 'pending', 'processing')
+ AND u.is_system_account = FALSE`,
+ [todayStr]
+ );
+ const todayPayers = todayPayersResult.map(row => row.from_user_id);
+
+ // 构建排除用户的条件(包括发起人、已使用的用户、昨天打款的用户、待处理用户、当天转出用户)
+ const excludeList = [
+ excludeUserId,
+ ...Array.from(excludeUserIds),
+ ...yesterdayPayers,
+ ...pendingUsers,
+ ...todayPayers
+ ];
+ const placeholders = excludeList.map(() => '?').join(',');
+
+ // 第一优先级:最早成为负数且通过前面检查的普通用户
+ // 使用最早的转出记录时间作为成为负数的参考时间
+ const [earliestNegativeUsers] = await db.execute(
+ `SELECT u.id,
+ u.balance,
+ (SELECT MIN(t.created_at)
+ FROM transfers t
+ WHERE t.from_user_id = u.id
+ AND t.status IN ('confirmed', 'received')) as first_transfer_time
+ FROM users u
+ WHERE u.id NOT IN (${placeholders})
+ AND u.is_system_account = FALSE
+ AND u.balance < 0
+ AND (SELECT COUNT(*)
+ FROM transfers t
+ WHERE t.from_user_id = u.id
+ AND t.status IN ('confirmed', 'received')) > 0
+ ORDER BY first_transfer_time ASC, u.balance ASC, RAND()
+ LIMIT 1`,
+ excludeList
+ );
+
+ if (earliestNegativeUsers.length > 0) {
+ return earliestNegativeUsers[0].id;
+ }
+
+ // 第二优先级:有可回款分配的普通用户(昨天或更早出款,今天可以回款)
+ // 但必须是负余额用户,且通过前面的检查
+ const [returnableUsers] = await db.execute(
+ `SELECT DISTINCT oa.from_user_id as id, u.balance
+ FROM transfers oa
+ JOIN matching_orders mo ON oa.id = mo.id
+ JOIN users u ON oa.from_user_id = u.id
+ WHERE oa.from_user_id NOT IN (${placeholders})
+ AND oa.status = 'confirmed'
+ AND oa.can_return_after <= NOW()
+ AND oa.return_date IS NULL
+ AND mo.status = 'matching'
+ AND u.balance < 0
+ AND u.is_system_account = FALSE
+ ORDER BY oa.can_return_after ASC, u.balance ASC, RAND()
+ LIMIT 1`,
+ excludeList
+ );
+
+ if (returnableUsers.length > 0) {
+ return returnableUsers[0].id;
+ }
+
+ // 第三优先级:其他负余额普通用户(余额为负数说明他们给其他用户转过钱,钱还没收回来)
+ const [negativeBalanceUsers] = await db.execute(
+ `SELECT id
+ FROM users
+ WHERE id NOT IN (${placeholders})
+ AND is_system_account = FALSE
+ AND balance < 0
+ ORDER BY balance ASC, RAND()
+ LIMIT 1`,
+ excludeList
+ );
+
+ if (negativeBalanceUsers.length > 0) {
+ return negativeBalanceUsers[0].id;
+ }
+
+ // 最后优先级:虚拟用户(系统账户)
+ const [systemUsers] = await db.execute(
+ `SELECT id
+ FROM users
+ WHERE is_system_account = TRUE
+ AND id NOT IN (${placeholders})
+ ORDER BY balance DESC, RAND()
+ LIMIT 1`,
+ excludeList
+ );
+
+ if (systemUsers.length > 0) {
+ return systemUsers[0].id;
+ }
+
+ // 如果连系统账户都没有,抛出错误
+ throw new Error('没有可用的匹配目标:所有符合条件的用户都被排除');
+ } catch (error) {
+ console.error('获取匹配目标失败:', error);
+ throw error;
+ }
+ }
+
+ // 获取可用用户
+
+
+ /**
+ * 生成智能分配金额
+ * 1. 排除今天打款的用户
+ * 2. 优先分配给负余额用户(余额+待确认收款为负数)
+ * 3. 每笔最高5000,不够再分配给虚拟用户
+ * 4. 笔数3-10笔
+ * @param {number} totalAmount - 总金额
+ * @param {number} excludeUserId - 排除的用户ID(发起人)
+ * @returns {Promise} 分配金额数组
+ */
+ async generateSmartAllocations(totalAmount, excludeUserId) {
+ const db = getDB();
+ const minAmount = 100;
+ const maxAmountPerTransfer = totalAmount;
+ const minTransfers = totalAmount > 5000 ? 4 : 3;
+ const maxTransfers = 10;
+
+ try {
+ // 首先获取当前用户的城市、省份和区域信息
+ const [currentUserResult] = await db.execute(
+ `SELECT city, province, district_id
+ FROM users
+ WHERE id = ?`,
+ [excludeUserId]
+ );
+
+ const currentUserCity = currentUserResult[0]?.city;
+ const currentUserProvince = currentUserResult[0]?.province;
+ const currentUserDistrictId = currentUserResult[0]?.district_id;
+
+ // 获取负余额用户,按区县、城市、省份优先级排序
+ let [userBalanceResult] = await db.execute(
+ `SELECT u.id as user_id,
+ u.balance as current_balance,
+ u.city,
+ u.province,
+ u.district_id
+ FROM users u
+ WHERE u.is_system_account = FALSE
+ AND u.is_distribute = TRUE
+ AND u.id != ?
+ AND u.balance < -100
+ AND u.audit_status = 'approved'
+ AND u.user_type != 'directly_operated'
+ AND u.payment_status = 'paid'
+ AND u.province = ?
+ ORDER BY CASE
+ WHEN u.city = ? AND u.district_id = ? THEN 1 -- 相同城市且相同区县排第一
+ WHEN u.city = ? THEN 2 -- 相同城市但不同区县排第二
+ WHEN u.province = ? THEN 3 -- 相同省份但不同城市排第三
+ ELSE 4 -- 其他省份排第四
+ END,
+ u.balance ASC`,
+ [excludeUserId, currentUserProvince, currentUserCity, currentUserDistrictId, currentUserCity, currentUserProvince]
+ );
+
+ // 处理查询到的负余额用户
+ const availableUsers = [];
+
+ for (const user of userBalanceResult) {
+ // 确保余额是数字类型
+ const currentBalance = parseFloat(user.current_balance) || 0;
+
+ // 更新用户对象
+ user.current_balance = currentBalance;
+
+ // 查询用户的分配订单金额统计
+ const [orderStatusResult] = await db.execute(
+ `SELECT SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount
+ FROM transfers
+ WHERE to_user_id = ?`,
+ [user.user_id]
+ );
+
+ // 查询用户的分配订单金待确认金额统计
+ const [orderStatusConfirmedResult] = await db.execute(
+ `SELECT SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as confirmed_amount
+ FROM transfers
+ WHERE to_user_id = ?`,
+ [user.user_id]
+ );
+ // 查询用户当天在matching_orders表中打出去的款项
+ const today = getLocalDateString();
+ const [todayOutflowResult] = await db.execute(
+ `SELECT SUM(amount) as today_outflow
+ FROM transfers
+ WHERE from_user_id = ?
+ AND DATE(created_at) = ?
+ AND status != 'cancelled' `,
+ [user.user_id, today]
+ );
+
+ // 添加分配金额信息到用户对象
+ const orderStatus = orderStatusResult[0] || {pending_amount: 0};
+ const todayOutflow = todayOutflowResult[0] || {today_outflow: 0};
+ const orderStatusConfirmed = orderStatusConfirmedResult[0] || {confirmed_amount: 0};
+ user.today_outflow = parseFloat(todayOutflow.today_outflow) || 0;
+ user.pending_amount = parseFloat(orderStatus.pending_amount) || 0;
+ user.confirmed_amount = parseFloat(orderStatusConfirmed.confirmed_amount) || 0;
+ user.has_active_allocations = user.current_balance + user.pending_amount + user.confirmed_amount + user.today_outflow;
+
+
+ // 所有查询到的用户都是负余额用户,直接添加到可用列表
+ }
+ userBalanceResult = userBalanceResult.filter(user => user.has_active_allocations < -100);
+ userBalanceResult = userBalanceResult.sort((a, b) => {
+ const getPriority = (user) => {
+ if (user.district_id === currentUserDistrictId) return 1; // 同区县
+ if (user.city === currentUserCity) return 2; // 同城市
+ return 3; // 其他
+ };
+
+ const priorityA = getPriority(a);
+ const priorityB = getPriority(b);
+
+ if (priorityA !== priorityB) {
+ return priorityA - priorityB; // 优先级小的排前
+ }
+
+ // 同优先级里:越接近0(数值越大)排前 -> 使用降序
+ return b.has_active_allocations - a.has_active_allocations;
+ });
+ for (const user of userBalanceResult) {
+ if (maxTransfers > availableUsers.length + 1) {
+ if (minTransfers === 3 && availableUsers.length < 3) {
+ availableUsers.push(user);
+ }
+ if (minTransfers === 4) {
+ availableUsers.push(user);
+ }
+ }
+ console.log(user, '普通用户');
+ }
+
+
+ console.log(`可参与分配的负余额用户数量: ${availableUsers.length}`);
+
+ // 第二步:由于第一步已经筛选了余额小于0的用户,这里直接使用可用用户作为优先分配用户
+ // 用户已按余额升序排列(最负的优先),然后按可分配金额降序排列
+ const priorityUsers = availableUsers; // 所有可用用户都是负余额用户,无需再次筛选
+
+ // 第三步:获取虚拟用户作为备选
+ const [virtualUsersResult] = await db.execute(
+ `SELECT id, username, balance
+ FROM users
+ WHERE is_system_account = TRUE
+ ORDER BY balance DESC, RAND()`
+ );
+
+ // 计算分配方案
+ const allocations = [];
+ let remainingAmount = totalAmount;
+
+ // 优先分配给当前余额为负的用户
+ for (const user of priorityUsers) {
+ if (remainingAmount <= 0 || allocations.length >= maxTransfers) break;
+
+ // 计算该用户可接受的最大分配金额
+ // 确保分配后用户余额不会变成正数
+ const currentBalance = Math.abs(user.has_active_allocations);
+ // 使用随机分配而不是平均分配
+ const remainingTransfers = minTransfers - allocations.length;
+ const minRequiredForRemaining = Math.max(0, (remainingTransfers - 1) * minAmount); // 为剩余转账预留的最小金额,确保不为负数
+
+ console.log(`用户${user.user_id}分配计算: remainingAmount=${remainingAmount}, remainingTransfers=${remainingTransfers}, minRequiredForRemaining=${minRequiredForRemaining}`);
+
+ const maxRandomAllocation = Math.min(
+ currentBalance, // 不能超过安全分配额度,确保接收后余额不会变成正数
+ maxAmountPerTransfer, // 单笔最大金额限制
+ remainingAmount - minRequiredForRemaining // 确保剩余金额足够分配给后续转账
+ );
+
+ // 生成随机分配金额,使用极度偏向大值的算法
+ let maxUserAllocation = 0;
+ if (maxRandomAllocation >= minAmount) {
+ const range = maxRandomAllocation - minAmount;
+ if (range <= 0) {
+ maxUserAllocation = minAmount;
+ } else {
+ if (maxRandomAllocation > 1000) {
+ // 使用更均匀的分配策略
+ const randomFactor = Math.random(); // 使用均匀分布
+
+ // 基础分配:在整个范围内更均匀分布,减少偏向性
+ const baseOffset = Math.floor(range * 0.15); // 降低到15%的基础偏移
+ const adjustedRange = range - baseOffset;
+ maxUserAllocation = Math.floor(randomFactor * adjustedRange) + minAmount + baseOffset;
+
+ // 进一步减少额外增量的影响
+ const bonusRange = Math.min(range * 0.1, maxRandomAllocation - maxUserAllocation); // 降低到10%
+ if (bonusRange > 0 && Math.random() > 0.7) { // 30%概率获得额外增量,进一步降低
+ const bonus = Math.floor(Math.random() * bonusRange * 0.3); // 使用30%的bonus范围
+ maxUserAllocation += bonus;
+ }
+ } else {
+ maxUserAllocation = maxRandomAllocation
+ }
+
+
+ // 确保不超过最大限制
+ maxUserAllocation = Math.min(maxUserAllocation, maxRandomAllocation);
+ }
+ }
+ console.log(maxUserAllocation, minAmount, '+++++++++++++++');
+
+ if (maxUserAllocation >= minAmount) {
+ allocations.push({
+ userId: user.user_id,
+ username: user.username || `用户${user.user_id}`,
+ amount: maxUserAllocation,
+ userType: 'priority_user',
+ currentBalance: user.current_balance,
+ todayOutflow: user.today_outflow,
+ has_active_allocations: user.has_active_allocations
+ });
+ remainingAmount -= maxUserAllocation;
+ }
+ }
+
+ // 如果还有剩余金额且分配数量不足最小笔数,最后分配给虚拟用户
+ const availableVirtualUsers = virtualUsersResult
+
+ // 如果有剩余金额,优先检查现有非虚拟用户是否还能消化
+ if (remainingAmount > 0) {
+ // 筛选出非虚拟用户分配记录
+
+ if (allocations.length > 0) {
+ let totalAvailableCapacity = 0;
+ const userCapacities = [];
+
+ // 计算每个用户的剩余可分配容量
+ for (const allocation of allocations) {
+ // 获取用户当前的实际余额状态(使用has_active_allocations作为实际可分配余额)
+ const maxSafeAmount = Math.abs(allocation.has_active_allocations);
+ const remainingCapacity = maxSafeAmount - allocation.amount;
+
+ if (remainingCapacity > 0) {
+ userCapacities.push({
+ allocation,
+ capacity: remainingCapacity
+ });
+ totalAvailableCapacity += remainingCapacity;
+ }
+ }
+
+ console.log(`现有用户剩余容量: ${totalAvailableCapacity}, 待分配金额: ${remainingAmount}`);
+
+ // 如果现有用户能够消化剩余金额
+ if (totalAvailableCapacity >= remainingAmount && userCapacities.length > 0 && allocations.length >= 3) {
+ // 按平均分配给这些用户,但需要检查每个用户的分配上限
+ const averageAmount = Math.floor(remainingAmount / userCapacities.length);
+ let distributedAmount = 0;
+ let remainingToDistribute = remainingAmount;
+
+ for (let i = 0; i < userCapacities.length; i++) {
+ const {allocation, capacity} = userCapacities[i];
+
+ // 计算本次可分配的金额
+ let amountToAdd = 0;
+
+ if (i === userCapacities.length - 1) {
+ // 最后一个用户分配剩余的所有金额,但不能超过其容量
+ amountToAdd = Math.min(remainingToDistribute, capacity);
+ } else {
+ // 其他用户按平均分配,但不能超过其容量
+ amountToAdd = Math.min(averageAmount, capacity);
+ }
+
+ if (amountToAdd > 0) {
+ allocation.amount += amountToAdd;
+ distributedAmount += amountToAdd;
+ remainingToDistribute -= amountToAdd;
+ console.log(`为用户${allocation.userId}追加分配${amountToAdd}元,总分配${allocation.amount}元,剩余容量${capacity - amountToAdd}元`);
+ }
+ }
+
+ // 更新实际分配的剩余金额
+ remainingAmount = remainingToDistribute;
+
+ if (remainingAmount === 0) {
+ console.log('剩余金额已全部分配给现有用户');
+ } else {
+ console.log(`部分剩余金额已分配给现有用户,仍有${remainingAmount}元未分配`);
+ }
+ }
+ }
+ }
+
+ // 如果仍有剩余金额,检查是否有未分配的用户可以消化剩余金额
+ if (remainingAmount > 0) {
+ // 获取已分配的用户ID列表
+ const allocatedUserIds = new Set(allocations.map(a => a.userId));
+
+ // 从原始用户列表中找到未分配的用户
+ const unallocatedUsers = userBalanceResult.filter(user => !allocatedUserIds.has(user.user_id));
+
+ if (unallocatedUsers.length > 0) {
+ console.log(`发现${unallocatedUsers.length}个未分配的用户,剩余金额: ${remainingAmount}`);
+
+ // 查找可分配金额大于剩余金额的用户
+ for (const user of unallocatedUsers) {
+ const maxSafeAmount = Math.abs(user.has_active_allocations);
+
+ if (maxSafeAmount >= remainingAmount) {
+ // 找到合适的用户,分配剩余金额
+ allocations.push({
+ userId: user.user_id,
+ username: user.username || `User${user.user_id}`,
+ amount: remainingAmount,
+ userType: 'priority_user',
+ currentBalance: user.current_balance,
+ availableForAllocation: user.has_active_allocations
+ });
+
+ console.log(`为未分配用户${user.user_id}分配剩余金额${remainingAmount}元`);
+ remainingAmount = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ // 如果仍有剩余金额,分配给虚拟用户
+ if (remainingAmount > 0 && availableVirtualUsers.length > 0) {
+ const maxPossibleTransfers = Math.min((minTransfers - allocations.length) <= 0 ? 1 : minTransfers - allocations.length, availableVirtualUsers.length);
+
+ // 生成随机分配金额数组
+ const randomAmounts = this.generateRandomAmounts(remainingAmount, maxPossibleTransfers, minAmount, maxAmountPerTransfer);
+
+ // 为每个随机金额分配虚拟用户
+ for (let i = 0; i < randomAmounts.length && availableVirtualUsers.length > 0; i++) {
+ const randomIndex = Math.floor(Math.random() * availableVirtualUsers.length);
+ const virtualUser = availableVirtualUsers[randomIndex];
+
+ allocations.push({
+ userId: virtualUser.id,
+ username: virtualUser.username,
+ amount: randomAmounts[i],
+ userType: 'virtual',
+ balance: virtualUser.balance,
+ currentBalance: virtualUser.balance,
+ });
+
+ remainingAmount -= randomAmounts[i];
+ availableVirtualUsers.splice(randomIndex, 1);
+ }
+ }
+
+ // 检查是否有足够的用户来完成分配
+ if (remainingAmount > 0 && allocations.length < minTransfers && availableVirtualUsers.length === 0) {
+ throw new Error('没有足够的可用用户来完成分配(避免重复分配给同一用户)');
+ }
+
+ // 确保至少有最小笔数
+ // if (allocations.length < minTransfers) {
+ // throw new Error(`无法生成足够的分配:需要至少${minTransfers}笔,但只能生成${allocations.length}笔`);
+ // }
+
+ // 精确控制总金额,避免超出预期
+ const currentTotal = allocations.reduce((sum, a) => sum + a.amount, 0);
+ console.log('剩余金额处理前:', remainingAmount, '当前总分配金额:', currentTotal, '期望总金额:', totalAmount);
+ console.log(`智能分配完成: 总金额${totalAmount}元,分配${allocations.length}笔`);
+ console.log('分配详情:', allocations.map(a =>
+ `${a.amount}元 -> 用户${a.userId}(${a.username}) [${a.userType}]`
+ ).join(', '));
+ //检查每个用户的匹配到的数量不能超过自身
+ let checking = allocations.filter(item => item.userType !== 'virtual')
+ let is_checking = true
+ for (const user of checking) {
+ if (Math.abs(user.has_active_allocations) < user.amount) {
+ is_checking = false;
+ break;
+ }
+ }
+ if (is_checking) {
+ return allocations;
+ }
+ return []
+
+
+ } catch (error) {
+ console.error('智能分配失败:', error);
+ throw error;
+ }
+ }
+
+ /**
+ * 生成随机分配金额数组(更均匀的分配策略)
+ * @param {number} totalAmount - 总金额
+ * @param {number} transferCount - 分配笔数
+ * @param {number} minAmount - 最小金额
+ * @param {number} maxAmount - 最大金额
+ * @returns {number[]} 随机金额数组
+ */
+ generateRandomAmounts(totalAmount, transferCount, minAmount, maxAmount) {
+ if (transferCount <= 0 || totalAmount < minAmount * transferCount) {
+ return [];
+ }
+
+ // 使用更均匀的分配策略
+ const amounts = [];
+
+ // 首先为每笔分配最小金额
+ for (let i = 0; i < transferCount; i++) {
+ amounts.push(minAmount);
+ }
+
+ let remainingToDistribute = totalAmount - (minAmount * transferCount);
+
+ // 计算平均每笔应该额外分配的金额
+ const averageExtra = Math.floor(remainingToDistribute / transferCount);
+
+ // 为每笔添加平均额外金额,但加入一些随机性
+ for (let i = 0; i < transferCount && remainingToDistribute > 0; i++) {
+ // 计算这笔最多还能增加多少(不超过maxAmount)
+ const maxPossibleIncrease = Math.min(
+ maxAmount - amounts[i],
+ remainingToDistribute
+ );
+
+ if (maxPossibleIncrease > 0) {
+ // 在平均值附近随机分配,但控制在更小的范围内以保证更均匀
+ const baseIncrease = Math.min(averageExtra, maxPossibleIncrease);
+ const randomVariation = Math.floor(baseIncrease * 0.15); // 减少到15%的随机变化
+ const minIncrease = Math.max(0, baseIncrease - randomVariation);
+ const maxIncrease = Math.min(maxPossibleIncrease, baseIncrease + randomVariation);
+
+ const increase = Math.floor(Math.random() * (maxIncrease - minIncrease + 1)) + minIncrease;
+ amounts[i] += increase;
+ remainingToDistribute -= increase;
+ }
+ }
+
+ // 如果还有剩余金额,尽量均匀分配给还能接受的笔数
+ while (remainingToDistribute > 0) {
+ const availableIndices = [];
+ for (let i = 0; i < transferCount; i++) {
+ if (amounts[i] < maxAmount) {
+ availableIndices.push(i);
+ }
+ }
+
+ if (availableIndices.length === 0) {
+ break; // 无法继续分配
+ }
+
+ // 计算每个可用位置应该分配多少
+ const perIndexAmount = Math.floor(remainingToDistribute / availableIndices.length);
+ const remainder = remainingToDistribute % availableIndices.length;
+
+ // 为每个可用位置分配相等的金额
+ for (let i = 0; i < availableIndices.length && remainingToDistribute > 0; i++) {
+ const index = availableIndices[i];
+ const maxIncrease = Math.min(maxAmount - amounts[index], remainingToDistribute);
+
+ if (maxIncrease > 0) {
+ // 基础分配金额
+ let increase = Math.min(perIndexAmount, maxIncrease);
+
+ // 如果是前几个位置,额外分配余数
+ if (i < remainder) {
+ increase = Math.min(increase + 1, maxIncrease);
+ }
+
+ amounts[index] += increase;
+ remainingToDistribute -= increase;
+ }
+ }
+
+ // 如果所有位置都已达到最大值,退出循环
+ if (perIndexAmount === 0 && remainder === 0) {
+ break;
+ }
+ }
+
+ // 如果还有剩余金额无法分配,返回空数组表示失败
+ if (remainingToDistribute > 0) {
+ return [];
+ }
+
+ return amounts;
+ }
+
+ // 生成3笔随机金额,总计指定金额(保留原方法用于兼容性)
+ generateThreeRandomAmounts(totalAmount) {
+ // 确保总金额足够分配三笔最小金额
+ const minAmount = 500;
+ const maxAmount = Math.min(5000, totalAmount - 2 * minAmount);
+
+ // 生成第一笔金额 (500-5000)
+ const amount1 = Math.floor(Math.random() * (maxAmount - minAmount + 1)) + minAmount;
+
+ // 生成第二笔金额 (500-剩余金额-500)
+ const remaining1 = totalAmount - amount1;
+ const maxAmount2 = Math.min(5000, remaining1 - minAmount);
+ const amount2 = Math.floor(Math.random() * (maxAmount2 - minAmount + 1)) + minAmount;
+
+ // 第三笔是剩余金额
+ const amount3 = totalAmount - amount1 - amount2;
+
+ return [amount1, amount2, amount3];
+ }
+
+ // 生成随机金额(保留原方法用于其他地方)
+
+
+ /**
+ * 确认分配并创建转账记录
+ * @param {number} allocationId - 分配ID
+ * @param {number} userId - 用户ID
+ * @param {number} transferAmount - 实际转账金额(用于校验)
+ * @param {string} description - 转账描述
+ * @param {string} voucher - 转账凭证URL
+ * @returns {number} 转账记录ID
+ */
+ async confirmAllocation(allocationId, userId, transferAmount = null, description = null, voucher = null) {
+ const db = getDB();
+
+ try {
+ await db.query('START TRANSACTION');
+
+ // 获取分配信息
+ const [allocations] = await db.execute(
+ 'SELECT * FROM transfers WHERE id = ? AND from_user_id = ?',
+ [allocationId, userId]
+ );
+
+ if (allocations.length === 0) {
+ throw new Error('分配不存在或无权限');
+ }
+
+ const allocation = allocations[0];
+
+ // 校验转账金额(如果提供了转账金额)
+ if (transferAmount !== null) {
+ const expectedAmount = parseFloat(allocation.amount);
+ const actualAmount = parseFloat(transferAmount);
+
+ if (Math.abs(expectedAmount - actualAmount) > 0.01) {
+ throw new Error(`转账金额不匹配!应转账 ${expectedAmount} 元,实际转账 ${actualAmount} 元`);
+ }
+ }
+
+ // 检查分配状态
+ if (allocation.status !== 'pending') {
+ throw new Error('该分配已处理,无法重复确认');
+ }
+
+ // 检查匹配订单是否已超时
+ const [matchingOrder] = await db.execute(
+ 'SELECT * FROM matching_orders WHERE id = ?',
+ [allocation.matching_order_id]
+ );
+
+ if (matchingOrder.length === 0) {
+ throw new Error('匹配订单不存在');
+ }
+
+ // 检查订单是否已被取消(超时会导致订单被取消)
+ if (matchingOrder[0].status === 'cancelled') {
+ throw new Error('该匹配订单已超时取消,无法进行转账');
+ }
+
+ // 检查是否存在相关的超时转账记录
+ const [timeoutTransfers] = await db.execute(
+ `SELECT COUNT(*) as count
+ FROM transfers
+ WHERE (from_user_id = ? OR to_user_id = ?)
+ AND is_overdue = 1
+ AND description LIKE ?`,
+ [userId, userId, `%匹配订单 ${allocation.matching_order_id}%`]
+ );
+
+ if (timeoutTransfers[0].count > 0) {
+ throw new Error('该匹配订单存在超时记录,无法继续转账。请联系管理员处理');
+ }
+
+ // 计算3小时后的截止时间
+ const deadline = dayjs().add(3, 'hour').toDate();
+
+ // 更新转账记录状态为confirmed,跳过待确认环节
+ const transferDescription = description || `匹配订单 ${allocation.matching_order_id} 第 ${allocation.cycle_number} 轮转账`;
+ const [transferResult] = await db.execute(
+ `UPDATE transfers
+ SET status = "confirmed",
+ description = ?,
+ deadline_at = ?,
+ confirmed_at = NOW(),
+ voucher_url = ?
+ WHERE id = ?`,
+ [
+ transferDescription,
+ deadline,
+ voucher,
+ allocationId
+ ]
+ );
+
+ // 注意:发送方余额将在接收方确认收款时扣除,而不是在确认转账时扣除
+ // 这样可以避免资金被锁定但收款方未确认的情况
+
+ // 记录确认动作
+ await db.execute(
+ 'INSERT INTO matching_records (matching_order_id, user_id, action, amount, note) VALUES (?, ?, "confirm", ?, ?)',
+ [
+ allocation.matching_order_id,
+ userId,
+ allocation.amount,
+ transferAmount ? `实际转账金额: ${transferAmount}` : null
+ ]
+ );
+
+ await db.query('COMMIT');
+
+ // 检查是否需要进入下一轮
+ await this.checkCycleCompletion(allocation.matching_order_id, allocation.cycle_number);
+
+ return transferResult.insertId;
+ } catch (error) {
+ await db.query('ROLLBACK');
+ throw error;
+ }
+ }
+
+ // 检查轮次完成情况
+ async checkCycleCompletion(matchingOrderId, cycleNumber) {
+ const db = getDB();
+
+ try {
+ // 检查当前轮次是否全部确认
+ const [pending] = await db.execute(
+ 'SELECT COUNT(*) as count FROM transfers WHERE matching_order_id = ? AND cycle_number = ? AND status = "pending"',
+ [matchingOrderId, cycleNumber]
+ );
+
+ if (pending[0].count === 0) {
+ // 当前轮次完成,检查是否需要下一轮
+ const [order] = await db.execute(
+ 'SELECT * FROM matching_orders WHERE id = ?',
+ [matchingOrderId]
+ );
+
+ const currentOrder = order[0];
+
+ if (currentOrder.cycle_count + 1 < currentOrder.max_cycles) {
+ // 开始下一轮
+ await db.execute(
+ 'UPDATE matching_orders SET cycle_count = cycle_count + 1 WHERE id = ?',
+ [matchingOrderId]
+ );
+
+ // 生成下一轮分配
+ const amounts = this.generateThreeRandomAmounts(currentOrder.amount);
+ await this.generateThreeAllocations(matchingOrderId, amounts, currentOrder.initiator_id);
+ } else {
+ // 完成所有轮次
+ await db.execute(
+ 'UPDATE matching_orders SET status = "completed" WHERE id = ?',
+ [matchingOrderId]
+ );
+
+ console.log(`匹配订单 ${matchingOrderId} 已完成所有轮次`);
+
+ // 检查用户是否完成第三次匹配,如果是则给代理分佣
+ await this.checkAndProcessAgentCommission(currentOrder.initiator_id);
+ }
+ }
+ } catch (error) {
+ console.error('检查轮次完成情况失败:', error);
+ throw error;
+ }
+ }
+
+ // 获取用户的匹配订单
+ async getUserMatchingOrders(userId, page = 1, limit = 10) {
+ const db = getDB();
+ const offset = (parseInt(page) - 1) * parseInt(limit);
+
+
+ try {
+ // 获取用户发起的订单
+ 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.initiator_id = ?
+ ORDER BY mo.created_at DESC
+ LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`,
+ [userId]
+ );
+
+ // 同时获取系统反向匹配订单(如果用户参与了分配)
+ const [systemOrders] = await db.execute(
+ `SELECT DISTINCT mo.*, u.username as initiator_name
+ FROM matching_orders mo
+ JOIN users u ON mo.initiator_id = u.id
+ JOIN transfers oa ON mo.id = oa.id
+ WHERE mo.is_system_reverse = TRUE
+ AND oa.to_user_id = ?
+ ORDER BY mo.created_at DESC
+ LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`,
+ [userId]
+ );
+
+ // 合并订单列表
+ const allOrders = [...orders, ...systemOrders];
+
+ // 按创建时间排序
+ allOrders.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
+
+ // 为每个订单获取分配信息
+ for (let order of allOrders) {
+ const [allocations] = await db.execute(
+ `SELECT *
+ FROM transfers
+ WHERE matching_order_id = ?
+ ORDER BY cycle_number, created_at`,
+ [order.id]
+ );
+ order.allocations = allocations;
+ }
+
+ return allOrders;
+ } catch (error) {
+ console.error('获取用户匹配订单失败:', error);
+ throw error;
+ }
+ }
+
+
+ // 获取用户待处理的分配
+ async getUserPendingAllocations(userId) {
+ const db = getDB();
+
+ try {
+ const [allocations] = await db.execute(
+ `(SELECT oa.*, mo.amount as total_amount, mo.status as order_status, u.username as to_user_name,
u.real_name as to_user_real_name,
DATE_ADD(oa.created_at, INTERVAL 150 MINUTE) as expected_deadline,
oa.outbound_date,
@@ -1451,122 +1467,123 @@ class MatchingService {
JOIN users u ON oa.to_user_id = u.id
WHERE oa.from_user_id = ? AND oa.status = "pending")
ORDER BY created_at ASC`,
- [userId, userId]
- );
+ [userId, userId]
+ );
- // 检查每个分配的超时状态,但不过滤掉
- const allocationsWithTimeoutStatus = [];
- for (const allocation of allocations) {
- // 检查是否存在相关的超时转账记录
- const [timeoutTransfers] = await db.execute(
- `SELECT COUNT(*) as count FROM transfers
- WHERE (from_user_id = ? OR to_user_id = ?)
- AND is_overdue = 1
- AND description LIKE ?`,
- [userId, userId, `%匹配订单 ${allocation.matching_order_id}%`]
- );
+ // 检查每个分配的超时状态,但不过滤掉
+ const allocationsWithTimeoutStatus = [];
+ for (const allocation of allocations) {
+ // 检查是否存在相关的超时转账记录
+ const [timeoutTransfers] = await db.execute(
+ `SELECT COUNT(*) as count
+ FROM transfers
+ WHERE (from_user_id = ? OR to_user_id = ?)
+ AND is_overdue = 1
+ AND description LIKE ?`,
+ [userId, userId, `%匹配订单 ${allocation.matching_order_id}%`]
+ );
- // 添加超时状态标记
- allocation.has_timeout_record = timeoutTransfers[0].count > 0;
- allocationsWithTimeoutStatus.push(allocation);
- }
- // 检查并处理超时订单
- const now = new Date();
- // 隐藏系统账户身份并添加时效状态
- const processedAllocations = allocationsWithTimeoutStatus.map(allocation => {
- const deadline = allocation.transfer_deadline || allocation.expected_deadline;
- const deadlineDate = new Date(deadline);
- const timeLeft = deadlineDate - now;
+ // 添加超时状态标记
+ allocation.has_timeout_record = timeoutTransfers[0].count > 0;
+ allocationsWithTimeoutStatus.push(allocation);
+ }
+ // 检查并处理超时订单
+ const now = new Date();
+ // 隐藏系统账户身份并添加时效状态
+ const processedAllocations = allocationsWithTimeoutStatus.map(allocation => {
+ const deadline = allocation.transfer_deadline || allocation.expected_deadline;
+ const deadlineDate = new Date(deadline);
+ const timeLeft = deadlineDate - now;
- // 计算剩余时间
- let timeStatus = 'normal';
- let timeLeftText = '';
+ // 计算剩余时间
+ let timeStatus = 'normal';
+ let timeLeftText = '';
- if (timeLeft <= 0) {
- timeStatus = 'expired';
- timeLeftText = '已超时';
- } else if (timeLeft <= 2.5 * 60 * 60 * 1000) { // 2.5小时内
- timeStatus = 'urgent';
- const hours = Math.floor(timeLeft / (60 * 60 * 1000));
- const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
- timeLeftText = hours > 0 ? `${hours}小时${minutes}分钟` : `${minutes}分钟`;
- } else {
- const hours = Math.floor(timeLeft / (60 * 60 * 1000));
- const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
- timeLeftText = `${hours}小时${minutes}分钟`;
+ if (timeLeft <= 0) {
+ timeStatus = 'expired';
+ timeLeftText = '已超时';
+ } else if (timeLeft <= 2.5 * 60 * 60 * 1000) { // 2.5小时内
+ timeStatus = 'urgent';
+ const hours = Math.floor(timeLeft / (60 * 60 * 1000));
+ const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
+ timeLeftText = hours > 0 ? `${hours}小时${minutes}分钟` : `${minutes}分钟`;
+ } else {
+ const hours = Math.floor(timeLeft / (60 * 60 * 1000));
+ const minutes = Math.floor((timeLeft % (60 * 60 * 1000)) / (60 * 1000));
+ timeLeftText = `${hours}小时${minutes}分钟`;
+ }
+
+ return {
+ ...allocation,
+ to_user_name: allocation.to_user_name || '匿名用户',
+ is_system_account: undefined, // 移除系统账户标识
+ deadline: deadline,
+ time_status: timeStatus,
+ time_left: timeLeftText,
+ can_transfer: !allocation.has_timeout_record, // 是否可以转账
+ timeout_reason: allocation.has_timeout_record ? '该匹配订单存在超时记录,无法继续转账' : null
+ };
+ });
+
+ return processedAllocations;
+ } catch (error) {
+ console.error('获取用户待处理分配失败:', error);
+ throw error;
}
-
- return {
- ...allocation,
- to_user_name: allocation.to_user_name || '匿名用户',
- is_system_account: undefined, // 移除系统账户标识
- deadline: deadline,
- time_status: timeStatus,
- time_left: timeLeftText,
- can_transfer: !allocation.has_timeout_record, // 是否可以转账
- timeout_reason: allocation.has_timeout_record ? '该匹配订单存在超时记录,无法继续转账' : null
- };
- });
-
- return processedAllocations;
- } catch (error) {
- console.error('获取用户待处理分配失败:', error);
- throw error;
}
- }
- // 检查并处理代理佣金
- async checkAndProcessAgentCommission(userId) {
- const db = getDB();
+ // 检查并处理代理佣金
+ async checkAndProcessAgentCommission(userId) {
+ const db = getDB();
- try {
- // 检查用户是否有代理关系
- const [agentRelation] = await db.execute(
- 'SELECT agent_id, created_at FROM agent_merchants WHERE merchant_id = ?',
- [userId]
- );
+ try {
+ // 检查用户是否有代理关系
+ const [agentRelation] = await db.execute(
+ 'SELECT agent_id, created_at FROM agent_merchants WHERE merchant_id = ?',
+ [userId]
+ );
- if (agentRelation.length === 0) {
- return; // 用户没有代理,无需处理
- }
+ if (agentRelation.length === 0) {
+ return; // 用户没有代理,无需处理
+ }
- const agentId = agentRelation[0].agent_id;
- const agentJoinTime = agentRelation[0].created_at;
+ const agentId = agentRelation[0].agent_id;
+ const agentJoinTime = agentRelation[0].created_at;
- // 检查用户给他人转账的次数(状态为已收款,且转账时间在代理商入驻之后)
- const [completedTransfers] = await db.execute(
- 'SELECT COUNT(*) as count FROM transfers WHERE from_user_id = ? AND status = "received" AND created_at >= ?',
- [userId, agentJoinTime]
- );
+ // 检查用户给他人转账的次数(状态为已收款,且转账时间在代理商入驻之后)
+ const [completedTransfers] = await db.execute(
+ 'SELECT COUNT(*) as count FROM transfers WHERE from_user_id = ? AND status = "received" AND created_at >= ?',
+ [userId, agentJoinTime]
+ );
- const transferCount = completedTransfers[0].count;
+ const transferCount = completedTransfers[0].count;
- // 如果完成至少三次转账,给代理分佣
- if (transferCount >= 3) {
- // 检查是否已经给过佣金(防止重复分佣)
- const [existingCommission] = await db.execute(
- 'SELECT id FROM agent_commission_records WHERE agent_id = ? AND merchant_id = ? AND description LIKE "%第三次转账%"',
- [agentId, userId]
- );
+ // 如果完成至少三次转账,给代理分佣
+ if (transferCount >= 3) {
+ // 检查是否已经给过佣金(防止重复分佣)
+ const [existingCommission] = await db.execute(
+ 'SELECT id FROM agent_commission_records WHERE agent_id = ? AND merchant_id = ? AND description LIKE "%第三次转账%"',
+ [agentId, userId]
+ );
- if (existingCommission.length === 0) {
- // 计算佣金:399元的10% = 39.9元
- const commissionAmount = 399 * 0.10;
+ if (existingCommission.length === 0) {
+ // 计算佣金:399元的10% = 39.9元
+ const commissionAmount = 399 * 0.10;
- // 记录佣金
- await db.execute(
- 'INSERT INTO agent_commission_records (agent_id, merchant_id, commission_amount, commission_type, description, created_at) VALUES (?, ?, ?, "matching", "用户完成第三次转账获得的代理佣金", NOW())',
- [agentId, userId, commissionAmount]
- );
+ // 记录佣金
+ await db.execute(
+ 'INSERT INTO agent_commission_records (agent_id, merchant_id, commission_amount, commission_type, description, created_at) VALUES (?, ?, ?, "matching", "用户完成第三次转账获得的代理佣金", NOW())',
+ [agentId, userId, commissionAmount]
+ );
- console.log(`用户 ${userId} 完成第三次转账,为代理 ${agentId} 分佣 ${commissionAmount} 元`);
+ console.log(`用户 ${userId} 完成第三次转账,为代理 ${agentId} 分佣 ${commissionAmount} 元`);
+ }
+ }
+ } catch (error) {
+ console.error('处理代理佣金失败:', error);
+ // 不抛出错误,避免影响主流程
}
- }
- } catch (error) {
- console.error('处理代理佣金失败:', error);
- // 不抛出错误,避免影响主流程
}
- }
}
module.exports = new MatchingService();
\ No newline at end of file
diff --git a/services/transferService.js b/services/transferService.js
index c4e5fb4..5d0ea0b 100644
--- a/services/transferService.js
+++ b/services/transferService.js
@@ -241,7 +241,7 @@ class TransferService {
// 获取转账列表
async getTransfers(filters = {}, pagination = {}, user_type = 'user_to_user') {
const db = getDB();
- const {page = 1, limit = 10, sort = 'created_at', order = 'desc'} = pagination;
+ const {page = 1, limit = 10, sort = 'id', order = 'desc'} = pagination;
const pageNum = parseInt(page, 10) || 1;
const limitNum = parseInt(limit, 10) || 10;
const offset = (pageNum - 1) * limitNum;
@@ -283,7 +283,7 @@ class TransferService {
// 构建排序子句
const validSortFields = ['id', 'amount', 'created_at', 'updated_at', 'status'];
- const sortField = validSortFields.includes(sort) ? sort : 'created_at';
+ const sortField = validSortFields.includes(sort) ? sort : 'id';
const sortOrder = order && order.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
const orderClause = `ORDER BY t.${sortField} ${sortOrder}`;
@@ -304,6 +304,8 @@ class TransferService {
`SELECT t.*,
fu.username as from_username,
fu.real_name as from_real_name,
+ fu.balance as from_balance,
+ tu.balance as to_balance,
tu.username as to_username,
tu.real_name as to_real_name,
f_p.name as from_province,
@@ -322,7 +324,7 @@ class TransferService {
LEFT JOIN china_regions t_c ON t_c.code = tu.city
LEFT JOIN china_regions t_d ON t_d.code = tu.district_id
${whereClause} ${orderClause}
- LIMIT ${limitNum}`,
+ LIMIT ${limitNum} OFFSET ${offset}`,
params
);