fix: 提现排除处理中金额,选择报价改为重点提示
可提现额度扣除处理中提现,避免重复提现超总额;收到报价选择供应商时展示重点提示而非服务费提示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+70
-28
@@ -8,15 +8,17 @@ module.exports = {
|
||||
* @param {String} accNo 账号
|
||||
* @param {String} reqDate 请求日期,格式:yyyyMMdd
|
||||
* @param {String} reqJournalNo 请求流水号
|
||||
* @param {String} logId 可选,流水ID,传入后会回写状态
|
||||
*/
|
||||
main: async (event) => {
|
||||
let { data = {}, util } = event;
|
||||
let { vk } = util;
|
||||
let { accNo, reqDate, reqJournalNo } = data;
|
||||
let { accNo, reqDate, reqJournalNo, logId } = data;
|
||||
|
||||
accNo = String(accNo || '').trim();
|
||||
reqDate = String(reqDate || '').trim();
|
||||
reqJournalNo = String(reqJournalNo || '').trim();
|
||||
logId = String(logId || '').trim();
|
||||
|
||||
if (!accNo) {
|
||||
return {
|
||||
@@ -46,43 +48,83 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
|
||||
let res = await vk.request({
|
||||
url: 'http://jjx-union-pay.gxwebsoft.com/api/union-pay-jjx/getActionLog',
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
header: {
|
||||
'content-type': 'application/json; charset=utf-8'
|
||||
},
|
||||
data: {
|
||||
accNo,
|
||||
reqDate,
|
||||
reqJournalNo
|
||||
},
|
||||
timeout: 60000
|
||||
let res = await balanceUtil.requestActionLog({
|
||||
vk,
|
||||
accNo,
|
||||
reqDate,
|
||||
reqJournalNo
|
||||
});
|
||||
|
||||
if (res && typeof res === 'object' && typeof res.code === 'undefined') {
|
||||
res.code = 0;
|
||||
}
|
||||
|
||||
const responseObjects = balanceUtil.getRemoteResponseObjects(res);
|
||||
const list = balanceUtil.pickFirstDefined([
|
||||
Array.isArray(res.list) ? res.list : undefined,
|
||||
Array.isArray(res.rows) ? res.rows : undefined,
|
||||
Array.isArray(res.data) ? res.data : undefined,
|
||||
Array.isArray(res.data && res.data.list) ? res.data.list : undefined,
|
||||
Array.isArray(res.data && res.data.rows) ? res.data.rows : undefined,
|
||||
Array.isArray(res.info && res.info.list) ? res.info.list : undefined,
|
||||
Array.isArray(res.info && res.info.rows) ? res.info.rows : undefined,
|
||||
Array.isArray(res.content && res.content.list) ? res.content.list : undefined,
|
||||
Array.isArray(res.content && res.content.rows) ? res.content.rows : undefined
|
||||
]) || [];
|
||||
const status = balanceUtil.getActionStatus(res);
|
||||
const statusText = balanceUtil.getActionStatusTextByStatus(status);
|
||||
|
||||
if (status !== null) {
|
||||
let logInfo = null;
|
||||
if (logId) {
|
||||
logInfo = await vk.baseDao.findByWhereJson({
|
||||
dbName: 'jjx-balance-log',
|
||||
whereJson: {
|
||||
_id: logId
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!logInfo) {
|
||||
logInfo = await vk.baseDao.findByWhereJson({
|
||||
dbName: 'jjx-balance-log',
|
||||
whereJson: {
|
||||
srcReqId: reqJournalNo,
|
||||
merchantId: accNo
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (logInfo && logInfo._id) {
|
||||
logId = logInfo._id;
|
||||
const prevStatus = Number(logInfo.status);
|
||||
const nextStatus = Number(status);
|
||||
const updateData = {
|
||||
status: nextStatus
|
||||
};
|
||||
|
||||
// 新提现单(balanceDeducted === false):成功时再扣减余额缓存
|
||||
// 旧提现单无该字段:创建时已扣减,这里只更新状态
|
||||
const needDeductOnSuccess = logInfo.balanceDeducted === false
|
||||
&& prevStatus === 1
|
||||
&& nextStatus === 2;
|
||||
|
||||
if (needDeductOnSuccess) {
|
||||
const amount = balanceUtil.normalizeAmount(logInfo.amount);
|
||||
await balanceUtil.applyMerchantBalanceDelta({
|
||||
vk,
|
||||
merchantId: accNo,
|
||||
delta: -amount,
|
||||
lastLogId: logId
|
||||
});
|
||||
updateData.balanceDeducted = true;
|
||||
}
|
||||
|
||||
await vk.baseDao.updateById({
|
||||
dbName: 'jjx-balance-log',
|
||||
id: logId,
|
||||
dataJson: updateData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const responseObjects = balanceUtil.getRemoteResponseObjects(res);
|
||||
if (responseObjects.data && typeof responseObjects.data === 'object' && !Array.isArray(responseObjects.data)) {
|
||||
responseObjects.data.list = list;
|
||||
responseObjects.data.status = status;
|
||||
responseObjects.data.statusText = statusText;
|
||||
responseObjects.data.logId = logId;
|
||||
} else if (res && typeof res === 'object') {
|
||||
res.data = {
|
||||
list
|
||||
status,
|
||||
statusText,
|
||||
logId
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
const { BALANCE_DOC_ID, getBalanceInfoList } = require('./util.js');
|
||||
const { BALANCE_DOC_ID, getBalanceInfoList, getAvailableWithdrawBalance, normalizeAmount } = require('./util.js');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@@ -25,6 +25,24 @@ module.exports = {
|
||||
}
|
||||
|
||||
const balanceInfoList = getBalanceInfoList(info.balanceInfo || {});
|
||||
const enrichedList = [];
|
||||
for (let i = 0; i < balanceInfoList.length; i++) {
|
||||
const item = balanceInfoList[i];
|
||||
const {
|
||||
pendingAmount,
|
||||
availableBalance
|
||||
} = await getAvailableWithdrawBalance({
|
||||
vk,
|
||||
merchantId: item.accNo,
|
||||
merchantBalance: item.balance
|
||||
});
|
||||
enrichedList.push({
|
||||
...item,
|
||||
balance: normalizeAmount(item.balance),
|
||||
pendingAmount,
|
||||
availableBalance
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
@@ -32,7 +50,7 @@ module.exports = {
|
||||
data: {
|
||||
balance: Number(Number(info.balance || 0).toFixed(2)),
|
||||
updateTime: info.updateTime || info._update_time_str || info._add_time_str || '',
|
||||
balanceInfoList
|
||||
balanceInfoList: enrichedList
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -213,6 +213,64 @@ function getActionStatusTextByStatus(status) {
|
||||
return statusMap[Number(status)] || '';
|
||||
}
|
||||
|
||||
async function applyMerchantBalanceDelta({
|
||||
vk,
|
||||
transaction,
|
||||
merchantId,
|
||||
delta,
|
||||
lastLogId = '',
|
||||
updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
}) {
|
||||
const accNo = String(merchantId || '').trim();
|
||||
if (!accNo) {
|
||||
throw new Error('提现账号不能为空');
|
||||
}
|
||||
const changeAmount = normalizeAmount(delta);
|
||||
const balanceRecord = await ensureBalanceRecord({ transaction, vk });
|
||||
const balanceInfoMap = buildBalanceInfoMap(balanceRecord.balanceInfo || {});
|
||||
const currentMerchantInfo = balanceInfoMap[accNo] || {
|
||||
accNo,
|
||||
merchantName: getMerchantLabel(accNo),
|
||||
balance: 0,
|
||||
updateTime: ''
|
||||
};
|
||||
const currentMerchantBalance = normalizeAmount(currentMerchantInfo.balance || 0);
|
||||
const nextMerchantBalance = normalizeAmount(currentMerchantBalance + changeAmount);
|
||||
if (nextMerchantBalance < 0) {
|
||||
throw new Error('余额不足');
|
||||
}
|
||||
const nextBalanceInfoMap = {
|
||||
...balanceInfoMap,
|
||||
[accNo]: {
|
||||
accNo,
|
||||
merchantName: currentMerchantInfo.merchantName || getMerchantLabel(accNo),
|
||||
balance: nextMerchantBalance,
|
||||
updateTime
|
||||
}
|
||||
};
|
||||
const nextTotalBalance = normalizeAmount(Number(balanceRecord.balance || 0) + changeAmount);
|
||||
const dataJson = {
|
||||
balance: nextTotalBalance,
|
||||
balanceInfo: nextBalanceInfoMap,
|
||||
updateTime
|
||||
};
|
||||
if (lastLogId) {
|
||||
dataJson.lastLogId = lastLogId;
|
||||
}
|
||||
await vk.baseDao.updateById({
|
||||
db: transaction,
|
||||
dbName: 'jjx-balance',
|
||||
id: balanceRecord._id,
|
||||
dataJson
|
||||
});
|
||||
return {
|
||||
balance: nextTotalBalance,
|
||||
merchantBalance: nextMerchantBalance,
|
||||
updateTime,
|
||||
balanceInfoMap: nextBalanceInfoMap
|
||||
};
|
||||
}
|
||||
|
||||
async function syncMerchantBalance({
|
||||
vk,
|
||||
accNo,
|
||||
@@ -323,6 +381,49 @@ async function checkBalanceAvailable({ transaction, vk, changeAmount, excludeLog
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定商户处理中的提现金额(status=1)
|
||||
*/
|
||||
async function getPendingWithdrawAmount({ transaction, vk, merchantId }) {
|
||||
const accNo = String(merchantId || '').trim();
|
||||
if (!accNo) {
|
||||
return 0;
|
||||
}
|
||||
const res = await vk.baseDao.select({
|
||||
db: transaction,
|
||||
dbName: 'jjx-balance-log',
|
||||
whereJson: {
|
||||
merchantId: accNo,
|
||||
type: 'expense',
|
||||
source: 'withdraw',
|
||||
status: 1
|
||||
},
|
||||
pageIndex: 1,
|
||||
pageSize: 1000,
|
||||
getCount: false
|
||||
});
|
||||
const rows = (res && res.rows) || [];
|
||||
return normalizeAmount(rows.reduce((sum, item) => sum + Number(item.amount || 0), 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 可提现余额 = 当前余额 - 处理中提现金额
|
||||
*/
|
||||
async function getAvailableWithdrawBalance({ transaction, vk, merchantId, merchantBalance }) {
|
||||
const pendingAmount = await getPendingWithdrawAmount({
|
||||
transaction,
|
||||
vk,
|
||||
merchantId
|
||||
});
|
||||
const balance = normalizeAmount(merchantBalance);
|
||||
const availableBalance = normalizeAmount(Math.max(0, balance - pendingAmount));
|
||||
return {
|
||||
balance,
|
||||
pendingAmount,
|
||||
availableBalance
|
||||
};
|
||||
}
|
||||
|
||||
async function updateBalanceWithDelta({ transaction, vk, delta, lastLogId = '' }) {
|
||||
const balanceInfo = await ensureBalanceRecord({ transaction, vk });
|
||||
const nextBalance = Number((Number(balanceInfo.balance || 0) + Number(delta || 0)).toFixed(2));
|
||||
@@ -372,10 +473,13 @@ module.exports = {
|
||||
parseOrderSet,
|
||||
getActionStatus,
|
||||
getActionStatusTextByStatus,
|
||||
applyMerchantBalanceDelta,
|
||||
syncMerchantBalance,
|
||||
ensureBalanceRecord,
|
||||
recalculateBalance,
|
||||
checkBalanceAvailable,
|
||||
getPendingWithdrawAmount,
|
||||
getAvailableWithdrawBalance,
|
||||
updateBalanceWithDelta,
|
||||
rollbackTransaction
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
const dayjs = require('dayjs');
|
||||
const balanceUtil = require('./util.js');
|
||||
const merchantMap = balanceUtil.MERCHANT_MAP;
|
||||
|
||||
@@ -34,11 +33,28 @@ module.exports = {
|
||||
updateTime: ''
|
||||
};
|
||||
const merchantBalance = balanceUtil.normalizeAmount(merchantBalanceInfo.balance || 0);
|
||||
if (merchantBalance <= 0) {
|
||||
throw new Error('当前企业余额小于等于0,无法提现');
|
||||
const {
|
||||
pendingAmount,
|
||||
availableBalance
|
||||
} = await balanceUtil.getAvailableWithdrawBalance({
|
||||
transaction,
|
||||
vk,
|
||||
merchantId,
|
||||
merchantBalance
|
||||
});
|
||||
if (availableBalance <= 0) {
|
||||
throw new Error(
|
||||
pendingAmount > 0
|
||||
? `可提现余额不足(处理中提现¥${pendingAmount.toFixed(2)}已占用)`
|
||||
: '当前企业余额小于等于0,无法提现'
|
||||
);
|
||||
}
|
||||
if (amount > merchantBalance) {
|
||||
throw new Error('提现金额不能大于当前企业余额');
|
||||
if (amount > availableBalance) {
|
||||
throw new Error(
|
||||
pendingAmount > 0
|
||||
? `提现金额不能大于可提现余额¥${availableBalance.toFixed(2)}(已排除处理中提现¥${pendingAmount.toFixed(2)})`
|
||||
: '提现金额不能大于当前企业余额'
|
||||
);
|
||||
}
|
||||
const description = String(data.description || '').trim() || `余额提现-${merchantMap[merchantId]}`;
|
||||
|
||||
@@ -80,52 +96,28 @@ module.exports = {
|
||||
merchantId,
|
||||
merchantName: merchantMap[merchantId],
|
||||
srcReqId: requestRes.srcReqId,
|
||||
status: 1
|
||||
status: 1,
|
||||
// 新逻辑:处理中不扣余额,成功后再扣;用于兼容旧数据(旧单默认已扣)
|
||||
balanceDeducted: false
|
||||
};
|
||||
const changeAmount = balanceUtil.buildBalanceChange(logData);
|
||||
|
||||
await balanceUtil.checkBalanceAvailable({
|
||||
transaction,
|
||||
vk,
|
||||
changeAmount
|
||||
});
|
||||
|
||||
const addRes = await vk.baseDao.add({
|
||||
await vk.baseDao.add({
|
||||
db: transaction,
|
||||
dbName: 'jjx-balance-log',
|
||||
dataJson: logData
|
||||
});
|
||||
|
||||
const updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
const nextMerchantBalance = Number((merchantBalance + changeAmount).toFixed(2));
|
||||
const nextBalance = Number((Number(balanceRecord.balance || 0) + changeAmount).toFixed(2));
|
||||
await vk.baseDao.updateById({
|
||||
db: transaction,
|
||||
dbName: 'jjx-balance',
|
||||
id: balanceRecord._id,
|
||||
dataJson: {
|
||||
balance: nextBalance,
|
||||
lastLogId: addRes.id,
|
||||
updateTime,
|
||||
balanceInfo: {
|
||||
...balanceInfoMap,
|
||||
[merchantId]: {
|
||||
accNo: merchantId,
|
||||
merchantName: merchantMap[merchantId],
|
||||
balance: nextMerchantBalance,
|
||||
updateTime
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 处理中提现不立刻扣减余额缓存,避免与远端余额同步互相覆盖;
|
||||
// 可提现额度通过「余额 - 处理中提现」计算,防止重复提现超总额。
|
||||
await transaction.commit();
|
||||
return {
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {
|
||||
balance: nextBalance,
|
||||
merchantBalance: nextMerchantBalance
|
||||
balance: merchantBalance,
|
||||
pendingAmount: balanceUtil.normalizeAmount(pendingAmount + amount),
|
||||
availableBalance: balanceUtil.normalizeAmount(Math.max(0, availableBalance - amount)),
|
||||
merchantBalance
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user