积分与余额账务系统硬切重构
This commit is contained in:
+71
-21
@@ -16,6 +16,28 @@
|
||||
}
|
||||
var activePoll = null;
|
||||
|
||||
function operationKey(owner) {
|
||||
if (!owner.dataset.aksrtOperationKey) {
|
||||
owner.dataset.aksrtOperationKey = window.AksrtRequest.createIdempotencyKey();
|
||||
}
|
||||
return owner.dataset.aksrtOperationKey;
|
||||
}
|
||||
|
||||
function resetOperationKey(owner) {
|
||||
delete owner.dataset.aksrtOperationKey;
|
||||
}
|
||||
|
||||
function resetOperationKeyOnChange(owner) {
|
||||
owner.addEventListener('input', function () { resetOperationKey(owner); });
|
||||
owner.addEventListener('change', function () { resetOperationKey(owner); });
|
||||
}
|
||||
|
||||
function balanceMinor(value) {
|
||||
var match = /^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/.exec(String(value || '').trim());
|
||||
if (!match) return null;
|
||||
return (BigInt(match[1]) * 100n) + BigInt((match[2] || '').padEnd(2, '0') || '0');
|
||||
}
|
||||
|
||||
function api(path, options) {
|
||||
return window.AksrtRequest.request(path, options || {}).then(function (response) {
|
||||
return {
|
||||
@@ -218,6 +240,7 @@
|
||||
Array.prototype.forEach.call(roots, function (root) {
|
||||
var tip = root.querySelector('[data-aksrt-card-tip]');
|
||||
var busy = false;
|
||||
resetOperationKeyOnChange(root);
|
||||
|
||||
function setTip(text, isError) {
|
||||
if (!tip) return;
|
||||
@@ -255,6 +278,7 @@
|
||||
|
||||
api('/pay/card-pass/redeem', {
|
||||
method: 'POST',
|
||||
headers: { 'Idempotency-Key': operationKey(root) },
|
||||
body: JSON.stringify(body)
|
||||
}).then(function (r) {
|
||||
busy = false;
|
||||
@@ -264,6 +288,7 @@
|
||||
return;
|
||||
}
|
||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||
resetOperationKey(root);
|
||||
if (type === 'vip' && window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === 'function') {
|
||||
window.AksrtModalRuntime.invalidate(['vip', 'orders']);
|
||||
}
|
||||
@@ -278,7 +303,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 余额充值 / 转账 / 积分兑余额(legacy 资产中心) =====
|
||||
// ===== 余额充值 / 转账 / 余额兑积分 =====
|
||||
function openModal(el) {
|
||||
if (!el) return;
|
||||
el.hidden = false;
|
||||
@@ -344,21 +369,23 @@
|
||||
});
|
||||
});
|
||||
if (!form) return;
|
||||
resetOperationKeyOnChange(form);
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
if (busy) return;
|
||||
var pkgInput = form.querySelector('input[name="balance_package"]:checked') || form.querySelector('input[name="balance_package"]');
|
||||
var pkg = pkgInput ? pkgInput.value : 'custom';
|
||||
var customEl = form.querySelector('input[name="custom_price"]');
|
||||
var custom = customEl ? parseFloat(customEl.value || '0') : 0;
|
||||
if (custom > 0) {
|
||||
var custom = customEl ? String(customEl.value || '').trim() : '';
|
||||
var validCustom = /^(0|[1-9]\d*)(?:\.\d{1,2})?$/.test(custom) && custom !== '0' && custom !== '0.0' && custom !== '0.00';
|
||||
if (validCustom) {
|
||||
pkg = 'custom';
|
||||
}
|
||||
if ((!pkg || pkg === 'custom') && !(custom > 0) && !pkgInput) {
|
||||
if ((!pkg || pkg === 'custom') && !validCustom && !pkgInput) {
|
||||
notyf('请选择或输入充值金额', 'danger');
|
||||
return;
|
||||
}
|
||||
if (pkg === 'custom' && !(custom > 0)) {
|
||||
if (pkg === 'custom' && !validCustom) {
|
||||
notyf('请输入有效充值金额', 'danger');
|
||||
return;
|
||||
}
|
||||
@@ -368,6 +395,7 @@
|
||||
notyf('正在创建充值订单…', 'load', '', 'aksrt-balance-charge-toast');
|
||||
api('/pay/balance/charge', {
|
||||
method: 'POST',
|
||||
headers: { 'Idempotency-Key': operationKey(form) },
|
||||
body: JSON.stringify({
|
||||
package: pkg,
|
||||
custom_price: custom,
|
||||
@@ -380,6 +408,7 @@
|
||||
return;
|
||||
}
|
||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||
resetOperationKey(form);
|
||||
if (data && data.redirect && (data.payment_url || data.qr_content)) {
|
||||
if (data.payment_url) window.open(data.payment_url, '_blank');
|
||||
notyf(data.message || '请完成支付,支付成功后将自动刷新。', 'info', 5, 'aksrt-balance-charge-toast');
|
||||
@@ -478,6 +507,7 @@
|
||||
});
|
||||
}
|
||||
if (!form) return;
|
||||
resetOperationKeyOnChange(form);
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
if (busy) return;
|
||||
@@ -489,7 +519,12 @@
|
||||
notyf('请选择收款用户', 'danger');
|
||||
return;
|
||||
}
|
||||
if (!(parseFloat(amount) > 0) || (type === 'points' && (!Number.isInteger(Number(amount)) || Number(amount) < pointsTransferMin))) {
|
||||
var pointsAmount = /^[1-9]\d*$/.test(String(amount)) ? BigInt(String(amount)) : 0n;
|
||||
var cashAmount = balanceMinor(amount);
|
||||
var validAmount = type === 'points'
|
||||
? pointsAmount >= BigInt(pointsTransferMin) && pointsAmount <= BigInt(Number.MAX_SAFE_INTEGER)
|
||||
: cashAmount !== null && cashAmount > 0n;
|
||||
if (!validAmount) {
|
||||
notyf('请输入有效金额', 'danger');
|
||||
return;
|
||||
}
|
||||
@@ -497,7 +532,12 @@
|
||||
notyf('正在转账…', 'load', '', 'aksrt-balance-transfer-toast');
|
||||
api('/pay/transfer', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ type: type, recipient: recipient, amount: amount })
|
||||
headers: { 'Idempotency-Key': operationKey(form) },
|
||||
body: JSON.stringify({
|
||||
type: type,
|
||||
recipient: recipient,
|
||||
amount: type === 'points' ? Number(pointsAmount) : String(amount)
|
||||
})
|
||||
}).then(function (r) {
|
||||
busy = false;
|
||||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||||
@@ -505,6 +545,7 @@
|
||||
return;
|
||||
}
|
||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||
resetOperationKey(form);
|
||||
notyf((data && data.message) || '转账成功,正在刷新…', 'success', 5, 'aksrt-balance-transfer-toast');
|
||||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||||
}).catch(function () {
|
||||
@@ -529,12 +570,15 @@
|
||||
btn.addEventListener('click', function () { closeModal(modal); });
|
||||
});
|
||||
if (!form) return;
|
||||
resetOperationKeyOnChange(form);
|
||||
function updatePreview() {
|
||||
if (!preview || !amountEl) return;
|
||||
var amount = Number(amountEl.value || '0');
|
||||
var rate = Number(amountEl.getAttribute('data-aksrt-points-exchange-rate') || '0');
|
||||
preview.textContent = Number.isInteger(amount) && amount > 0 && rate > 0
|
||||
? '预计获得 ' + (amount * rate) + ' 积分'
|
||||
var amount = balanceMinor(amountEl.value);
|
||||
var rateRaw = amountEl.getAttribute('data-aksrt-points-exchange-rate') || '';
|
||||
var rate = /^[1-9]\d*$/.test(rateRaw) ? BigInt(rateRaw) : 0n;
|
||||
var scaled = amount !== null ? amount * rate : 0n;
|
||||
preview.textContent = amount !== null && amount > 0n && rate > 0n && scaled % 100n === 0n
|
||||
? '预计获得 ' + String(scaled / 100n) + ' 积分'
|
||||
: '';
|
||||
}
|
||||
amountEl.addEventListener('input', updatePreview);
|
||||
@@ -542,15 +586,16 @@
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
if (busy) return;
|
||||
var amount = amountEl ? Number(amountEl.value || '0') : 0;
|
||||
if (!Number.isInteger(amount) || !(amount > 0)) {
|
||||
notyf('请输入正整数余额', 'danger');
|
||||
var amount = amountEl ? String(amountEl.value || '').trim() : '';
|
||||
if (!/^(0|[1-9]\d*)(?:\.\d{1,2})?$/.test(amount) || /^0(?:\.0{1,2})?$/.test(amount)) {
|
||||
notyf('请输入最多两位小数的正数余额', 'danger');
|
||||
return;
|
||||
}
|
||||
busy = true;
|
||||
notyf('正在购买积分…', 'load', '', 'aksrt-balance-points-toast');
|
||||
api('/pay/balance/points-exchange', {
|
||||
method: 'POST',
|
||||
headers: { 'Idempotency-Key': operationKey(form) },
|
||||
body: JSON.stringify({ amount: amount })
|
||||
}).then(function (r) {
|
||||
busy = false;
|
||||
@@ -559,6 +604,7 @@
|
||||
return;
|
||||
}
|
||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||
resetOperationKey(form);
|
||||
notyf((data && data.message) || '购买成功,正在刷新…', 'success', 5, 'aksrt-balance-points-toast');
|
||||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||||
}).catch(function () {
|
||||
@@ -581,6 +627,7 @@
|
||||
btn.addEventListener('click', function () { closeModal(modal); });
|
||||
});
|
||||
if (!form) return;
|
||||
resetOperationKeyOnChange(form);
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
if (busy) return;
|
||||
@@ -588,17 +635,18 @@
|
||||
var methodEl = form.querySelector('[data-aksrt-withdraw-method]');
|
||||
var accountEl = form.querySelector('[data-aksrt-withdraw-account]');
|
||||
var rawAmount = amountEl ? String(amountEl.value || '').trim() : '';
|
||||
var amount = rawAmount ? Number(rawAmount) : 0;
|
||||
var minAmount = amountEl ? Number(amountEl.getAttribute('data-withdraw-min') || '0') : 0;
|
||||
var remainingAmount = amountEl ? Number(amountEl.getAttribute('data-withdraw-remaining-amount') || '0') : 0;
|
||||
var amount = rawAmount;
|
||||
var amountMinor = balanceMinor(amount);
|
||||
var minAmount = amountEl ? balanceMinor(amountEl.getAttribute('data-withdraw-min') || '0') : 0n;
|
||||
var remainingAmount = amountEl ? balanceMinor(amountEl.getAttribute('data-withdraw-remaining-amount') || '0') : 0n;
|
||||
var remainingCount = amountEl ? Number(amountEl.getAttribute('data-withdraw-remaining-count') || '0') : 0;
|
||||
var method = methodEl ? methodEl.value : 'alipay';
|
||||
var account = accountEl ? String(accountEl.value || '').trim() : '';
|
||||
if (!Number.isInteger(amount) || !(amount > 0)) {
|
||||
notyf('提现金额必须为正整数', 'danger');
|
||||
if (amountMinor === null || amountMinor <= 0n) {
|
||||
notyf('提现金额必须为最多两位小数的正数', 'danger');
|
||||
return;
|
||||
}
|
||||
if (amount < minAmount) {
|
||||
if (minAmount === null || amountMinor < minAmount) {
|
||||
notyf('提现金额低于最低提现门槛', 'danger');
|
||||
return;
|
||||
}
|
||||
@@ -606,7 +654,7 @@
|
||||
notyf('当日提现次数已用尽', 'danger');
|
||||
return;
|
||||
}
|
||||
if (amount > remainingAmount) {
|
||||
if (remainingAmount === null || amountMinor > remainingAmount) {
|
||||
notyf('本次提现后将超出单日提现总额上限', 'danger');
|
||||
return;
|
||||
}
|
||||
@@ -618,6 +666,7 @@
|
||||
notyf('正在提交提现申请…', 'load', '', 'aksrt-balance-withdraw-toast');
|
||||
api('/earnings/withdrawals', {
|
||||
method: 'POST',
|
||||
headers: { 'Idempotency-Key': operationKey(form) },
|
||||
body: JSON.stringify({ amount: amount, method: method, account: account })
|
||||
}).then(function (r) {
|
||||
busy = false;
|
||||
@@ -626,6 +675,7 @@
|
||||
return;
|
||||
}
|
||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||
resetOperationKey(form);
|
||||
notyf((data && data.message) || '申请已提交', 'success', 5, 'aksrt-balance-withdraw-toast');
|
||||
closeModal(modal);
|
||||
var panel = modal.closest ? modal.closest('.dashboard-panel') : null;
|
||||
|
||||
Reference in New Issue
Block a user