This commit is contained in:
2026-10-08 18:18:21 +08:00
parent 9cc1ff546c
commit 1e3095c05e
1911 changed files with 349551 additions and 0 deletions
@@ -0,0 +1,171 @@
'use strict';
/**
* 余额记录改为读取订单付款单(jjx-order-pay-list)。
* 只允许查询当前登录用户作为卖家的付款单,避免客户端伪造 uid 查询其他用户数据。
*/
module.exports = {
/**
* @url client/userBalanceLog/kh/getList 前端调用的url参数地址
* @param {Object} data
* @param {String} data.queryDate 查询日期,格式 yyyyMMdd
*/
main: async (event) => {
const {data = {}, userInfo = {}, util} = event;
const {vk, _} = util;
const sellerUid = userInfo._id || userInfo.uid;
const pageIndex = Math.max(parseInt(data.pageIndex, 10) || 1, 1);
const pageSize = Math.min(Math.max(parseInt(data.pageSize, 10) || 10, 1), 1000);
if (!sellerUid) {
return {code: 0, msg: '', rows: [], total: 0, pageIndex, pageSize};
}
const requestedDate = String(data.queryDate || '').trim();
let dateText = /^\d{8}$/.test(requestedDate) ? requestedDate : formatDate(new Date());
let dateRange = getDateRange(dateText);
if (!dateRange) {
dateText = formatDate(new Date());
dateRange = getDateRange(dateText);
}
const whereJsonArr = [{sellerUid}];
// _add_time 是付款单的创建时间,按所选日期过滤;保留 _add_time_str 兼容历史字符串数据。
if (dateRange) {
whereJsonArr.push(_.or([
_.and([
{_add_time: _.gte(dateRange.start)},
{_add_time: _.lt(dateRange.end)}
]),
_.and([
{_add_time: _.gte(dateRange.start.getTime())},
{_add_time: _.lt(dateRange.end.getTime())}
]),
_.and([
{_add_time_str: _.gte(dateRange.startText)},
{_add_time_str: _.lt(dateRange.endText)}
])
]));
}
const result = await vk.baseDao.select({
dbName: 'jjx-order-pay-list',
getCount: true,
whereJson: _.and(whereJsonArr),
sortArr: [{name: '_add_time', type: 'desc'}],
pageIndex,
pageSize
});
const rows = Array.isArray(result.rows) ? result.rows : [];
let merNo = String(userInfo.merNo || '').trim();
if (!merNo) {
const currentUser = await vk.baseDao.findById({dbName: 'uni-id-users', id: sellerUid});
merNo = String((currentUser && currentUser.merNo) || '').trim();
}
// 银联交易明细查询(202007)按 seqId 查询。单条查询失败不影响其他记录返回。
await Promise.all(rows.map(async (row) => {
const notifyData = parseObject(row.payNotifyData);
const seqId = notifyData && notifyData.seqId;
if (seqId && merNo) {
try {
row.flowData = await vk.request({
url: 'http://jjx-union-pay.gxwebsoft.com/api/union-pay-jjx/getFlow',
method: 'POST',
dataType: 'json',
header: {'content-type': 'application/json; charset=utf-8'},
data: {
accNo: merNo,
transDate: dateText,
// 202007 查询项约定:A-请求流水号(seqId)。
queryItem: 'A',
queryValue: String(seqId)
},
timeout: 60000
});
row.flowStatus = pickFlowValue(row.flowData, ['orderStatus', 'transStatus', 'status', 'respCode']);
row.flowErrMsg = pickFlowValue(row.flowData, ['errMsg', 'respMsg', 'msg', 'message']);
} catch (error) {
row.flowError = error && error.message ? error.message : '流水状态查询失败';
}
}
// 给原余额记录表保留一组兼容字段,同时返回付款单原始字段供页面使用。
row.reqJournalNo = row.reqJournalNo || row.orderNo || row.orderId || row._id;
const amountYuan = row.amount !== undefined && row.amount !== null
? Number(row.amount)
: Number(row.payAmount || 0) / 100;
row.orderAmt = (Number.isFinite(amountYuan) ? amountYuan : 0) * 100;
row.orderStatus = normalizeOrderStatus(row.flowStatus, row.hasPaid, row.status);
row.errMsg = row.errMsg || row.flowErrMsg || row.refuseReason || '';
row.tradeTime = row.paidAt || row._add_time_str || row._add_time || '';
row.payerName = row.payerName || row.buyerName || '';
row.payerAcctNo = row.payerAcctNo || '';
}));
return {
...result,
rows,
pageIndex,
pageSize
};
}
};
function parseObject(value) {
if (!value) return null;
if (typeof value === 'object') return value;
if (typeof value !== 'string') return null;
try {
const parsed = JSON.parse(value);
return parsed && typeof parsed === 'object' ? parsed : null;
} catch (e) {
return null;
}
}
function pickFlowValue(value, keys) {
const objects = [value, value && value.data, value && value.info, value && value.content]
.filter(item => item && typeof item === 'object' && !Array.isArray(item));
for (const object of objects) {
for (const key of keys) {
if (object[key] !== undefined && object[key] !== null && object[key] !== '') return object[key];
}
}
return '';
}
function normalizeOrderStatus(flowStatus, hasPaid, localStatus) {
if (flowStatus !== undefined && flowStatus !== null && flowStatus !== '') {
const status = String(flowStatus);
if (/^[0-4]$/.test(status)) return Number(status);
if (['SUCCESS', 'TRADE_SUCCESS', 'PAID', '00'].includes(status.toUpperCase())) return 1;
if (['FAIL', 'FAILED', 'TRADE_FAIL', 'CLOSED'].includes(status.toUpperCase())) return 2;
}
// 付款单 status 0~4 表示支付流程阶段,并非银联失败码;只要已进入已支付流程即展示成功。
if (hasPaid === true || Number(localStatus) > 0) return 1;
return 0;
}
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}${month}${day}`;
}
function getDateRange(dateText) {
const year = Number(dateText.slice(0, 4));
const month = Number(dateText.slice(4, 6));
const day = Number(dateText.slice(6, 8));
const start = new Date(year, month - 1, day);
if (Number.isNaN(start.getTime()) || start.getFullYear() !== year || start.getMonth() !== month - 1 || start.getDate() !== day) {
return null;
}
const end = new Date(start.getTime());
end.setDate(end.getDate() + 1);
const startText = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} 00:00:00`;
const endText = `${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, '0')}-${String(end.getDate()).padStart(2, '0')} 00:00:00`;
return {start, end, startText, endText};
}
@@ -0,0 +1,55 @@
'use strict';
const balanceUtil = require('../../../admin/finance/balance/sys/util.js');
module.exports = {
/**
* 查询当前用户商户的实时余额。
* @url client/userBalanceLog/kh/getMerchantBalance 前端调用的url参数地址
* @param {String} accNo 企业用户号/商户号
*/
main: async (event) => {
const { data = {}, userInfo = {}, util } = event;
const { vk } = util;
const requestedAccNo = String(data.accNo || '').trim();
const userAccNo = String(userInfo.merNo || '').trim();
// 商户号来自当前登录用户,避免通过客户端参数查询其他企业的余额。
if (requestedAccNo && userAccNo && requestedAccNo !== userAccNo) {
return {
code: 403,
msg: '无权查询该商户余额'
};
}
const accNo = userAccNo || requestedAccNo;
if (!accNo) {
return {
code: -1,
msg: 'accNo不能为空'
};
}
const remoteResponse = await balanceUtil.requestMerchantBalance({
vk,
accNo
});
const merchantBalance = balanceUtil.getRemoteMerchantBalance(remoteResponse);
if (merchantBalance === null) {
return {
code: -1,
msg: (remoteResponse && (remoteResponse.msg || remoteResponse.message)) || '未获取到余额'
};
}
return {
code: 0,
msg: '',
data: {
accNo,
balance: merchantBalance
}
};
}
};