Files
jurong_circle_black/services/matchingService.js
2025-09-10 18:10:40 +08:00

1555 lines
57 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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

const { getDB } = require('../database');
const timeoutService = require('./timeoutService');
const dayjs = require('dayjs');
/**
* 获取本地时区的日期字符串YYYY-MM-DD格式
* 确保在晚上12点正确切换到第二天
* @param {Date} date - 可选的日期对象,默认为当前时间
* @returns {string} 格式化的日期字符串
*/
function getLocalDateString(date = new Date()) {
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<Array>} 返回分配结果数组
*/
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<void>}
*/
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<Array>} 分配金额数组
*/
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'
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, 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) => a.has_active_allocations - b.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,
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}`);
}
}
// 更新实际分配的剩余金额
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
});
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);
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) {
console.log('调整最后一笔分配,增加', allowedAmount, '元以达到精确总金额');
allocations[allocations.length - 1].amount += allowedAmount;
}
}
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 = [];
// 首先为每笔分配最小金额
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,
oa.return_date,
oa.can_return_after,
oa.confirmed_at
FROM transfers oa
JOIN matching_orders mo ON oa.matching_order_id = mo.id
JOIN users u ON oa.to_user_id = u.id
WHERE oa.from_user_id = ? AND oa.status = "pending" AND mo.status != "cancelled"
AND (oa.source_type IS NULL))
UNION ALL
(SELECT oa.*, oa.amount as total_amount, 'active' 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,
oa.return_date,
oa.can_return_after,
oa.confirmed_at
FROM transfers oa
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]
);
// 检查每个分配的超时状态,但不过滤掉
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;
// 计算剩余时间
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}分钟`;
}
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();
try {
// 检查用户是否有代理关系
const [agentRelation] = await db.execute(
'SELECT agent_id, created_at FROM agent_merchants WHERE merchant_id = ?',
[userId]
);
if (agentRelation.length === 0) {
return; // 用户没有代理,无需处理
}
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 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 (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]
);
console.log(`用户 ${userId} 完成第三次转账,为代理 ${agentId} 分佣 ${commissionAmount}`);
}
}
} catch (error) {
console.error('处理代理佣金失败:', error);
// 不抛出错误,避免影响主流程
}
}
}
module.exports = new MatchingService();