This commit is contained in:
2025-09-10 18:10:40 +08:00
parent 8530e97ab6
commit d50290e8fe
27 changed files with 2025 additions and 3913 deletions

View File

@@ -26,7 +26,17 @@ class MatchingService {
// 检查用户审核状态、必要信息和余额
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 = ?',
`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]
);
@@ -79,8 +89,8 @@ class MatchingService {
maxCycles = 1;
} else if (matchingType === 'large') {
// 大额匹配用户自定义金额最高5万
if (!customAmount || customAmount < 5000 || customAmount > 50000) {
throw new Error('大额匹配金额必须在5000-50000之间');
if (!customAmount || customAmount < 3000 || customAmount > 50000) {
throw new Error('大额匹配金额必须在3000-50000之间');
}
totalAmount = customAmount;
maxCycles = 1;
@@ -688,19 +698,41 @@ class MatchingService {
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.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'
ORDER BY u.balance ASC`,
[excludeUserId]
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]
);
// 处理查询到的负余额用户
@@ -765,7 +797,7 @@ class MatchingService {
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 (maxTransfers > availableUsers.length + 1) {
if (minTransfers === 3 && availableUsers.length < 3) {
availableUsers.push(user);
}
@@ -820,21 +852,26 @@ class MatchingService {
if (range <= 0) {
maxUserAllocation = minAmount;
} else {
// 使用更均匀的分配策略
const randomFactor = Math.random(); // 使用均匀分布
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 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;
// 进一步减少额外增量的影响
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);
}
@@ -852,7 +889,7 @@ class MatchingService {
totalPendingInflow: user.total_pending_inflow,
availableForAllocation: user.available_for_allocation,
todayOutflow: user.today_outflow,
has_active_allocations:user.has_active_allocations
has_active_allocations: user.has_active_allocations
});
remainingAmount -= maxUserAllocation;
}
@@ -864,7 +901,7 @@ class MatchingService {
// 如果有剩余金额,优先检查现有非虚拟用户是否还能消化
if (remainingAmount > 0) {
// 筛选出非虚拟用户分配记录
if (allocations.length > 0) {
let totalAvailableCapacity = 0;
const userCapacities = [];
@@ -874,7 +911,7 @@ class MatchingService {
// 获取用户当前的实际余额状态使用has_active_allocations作为实际可分配余额
const maxSafeAmount = Math.abs(allocation.has_active_allocations);
const remainingCapacity = maxSafeAmount - allocation.amount;
if (remainingCapacity > 0) {
userCapacities.push({
allocation,
@@ -887,7 +924,7 @@ class MatchingService {
console.log(`现有用户剩余容量: ${totalAvailableCapacity}, 待分配金额: ${remainingAmount}`);
// 如果现有用户能够消化剩余金额
if (totalAvailableCapacity >= remainingAmount && userCapacities.length > 0) {
if (totalAvailableCapacity >= remainingAmount && userCapacities.length > 0 && allocations.length >= 3) {
// 按平均分配给这些用户,但需要检查每个用户的分配上限
const averageAmount = Math.floor(remainingAmount / userCapacities.length);
let distributedAmount = 0;
@@ -895,10 +932,10 @@ class MatchingService {
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);
@@ -914,15 +951,15 @@ class MatchingService {
console.log(`为用户${allocation.userId}追加分配${amountToAdd}元,总分配${allocation.amount}元,剩余容量${capacity - amountToAdd}`);
}
}
// 更新实际分配的剩余金额
remainingAmount = remainingToDistribute;
if (remainingAmount === 0) {
console.log('剩余金额已全部分配给现有用户');
} else {
console.log(`部分剩余金额已分配给现有用户,仍有${remainingAmount}元未分配`);
}
remainingAmount = remainingToDistribute;
if (remainingAmount === 0) {
console.log('剩余金额已全部分配给现有用户');
} else {
console.log(`部分剩余金额已分配给现有用户,仍有${remainingAmount}元未分配`);
}
}
}
}
@@ -931,17 +968,17 @@ class MatchingService {
if (remainingAmount > 0) {
// 获取已分配的用户ID列表
const allocatedUserIds = new Set(allocations.map(a => a.userId));
// 从原始用户列表中找到未分配的用户
const unallocatedUsers = priorityUsers.filter(user => !allocatedUserIds.has(user.user_id));
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({
@@ -952,7 +989,7 @@ class MatchingService {
currentBalance: user.current_balance,
availableForAllocation: user.has_active_allocations
});
console.log(`为未分配用户${user.user_id}分配剩余金额${remainingAmount}`);
remainingAmount = 0;
break;
@@ -1045,17 +1082,17 @@ class MatchingService {
// 使用更均匀的分配策略
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
@@ -1063,20 +1100,20 @@ class MatchingService {
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 = [];
@@ -1085,45 +1122,45 @@ class MatchingService {
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;
}