fix: show coin seller transfer details

This commit is contained in:
zhx 2026-07-01 15:17:30 +08:00
parent f7a3a68a1d
commit d2cc6a3dd4
2 changed files with 173 additions and 47 deletions

View File

@ -158,6 +158,11 @@
return Number.isFinite(number) ? number : 0;
}
function positiveNumber(value) {
var number = toNumber(value);
return number > 0 ? number : 0;
}
function firstValue(source, keys) {
if (!source) return '';
for (var i = 0; i < keys.length; i += 1) {
@ -210,9 +215,7 @@
function hasAPI() {
return (
window.HyAppAPI &&
window.HyAppAPI.user &&
window.HyAppAPI.wallet
window.HyAppAPI && window.HyAppAPI.user && window.HyAppAPI.wallet
);
}
@ -340,6 +343,67 @@
);
}
function normalizeTransferAmount(item) {
// 转账美金来自服务端账务 metadata不同入账来源汇率可能不同前端不能用金币数反推缺字段时保持不展示。
var minor = positiveNumber(
firstValue(item, [
'transfer_usd_minor',
'transferUsdMinor',
'salary_usd_minor',
'salaryUsdMinor',
'recharge_usd_minor',
'rechargeUsdMinor',
'usd_minor_amount',
'usdMinorAmount',
])
);
if (minor > 0) return minor / 100;
var micro = positiveNumber(
firstValue(item, [
'paid_amount_micro',
'paidAmountMicro',
'amount_usdt_micro',
'amountUsdtMicro',
])
);
if (micro > 0) return micro / 1000000;
return positiveNumber(
firstValue(item, [
'amount_usd',
'amountUsd',
'transfer_amount_usd',
'transferAmountUsd',
])
);
}
function normalizeTransferCurrency(item, amount) {
return amount > 0
? String(
firstValue(item, [
'transfer_currency_code',
'transferCurrencyCode',
'recharge_currency_code',
'rechargeCurrencyCode',
'paid_currency_code',
'paidCurrencyCode',
'currency_code',
'currencyCode',
]) || 'USD'
).toUpperCase()
: '';
}
function formatTransferAmount(amount, currency) {
if (!amount || amount <= 0) return '';
var code = String(currency || 'USD').toUpperCase();
var value = Number(amount).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
return code + ' ' + value;
}
function normalizeResolvedUser(payload, keyword) {
var source = unwrapData(payload) || {};
var identity = source.identity || source.user_identity || source;
@ -399,7 +463,8 @@
);
var type = delta >= 0 ? 'income' : 'expenditure';
var counterpartyUserID = String(
firstValue(item, ['counterparty_user_id', 'counterpartyUserId']) || ''
firstValue(item, ['counterparty_user_id', 'counterpartyUserId']) ||
''
);
var counterpartyDisplayID = String(
firstValue(item, [
@ -419,19 +484,21 @@
'counterpartyAvatar',
]);
var counterpartyUID = counterpartyDisplayID || counterpartyUserID;
// 入账流水也可能来自某个用户的工资转币商,标题必须优先展示对方资料;只有后台进货这类没有对方用户的流水才展示库存入账。
var name =
type === 'income'
? t('coinseller_center.stock_transfer', 'Stock transfer')
: counterpartyName ||
(counterpartyUID
? t(
'coinseller_center.user_name_template',
'User {id}'
).replace('{id}', counterpartyUID)
: t('coinseller_center.transfer_out', 'Transfer out'));
counterpartyName ||
(counterpartyUID
? t(
'coinseller_center.user_name_template',
'User {id}'
).replace('{id}', counterpartyUID)
: type === 'income'
? t('coinseller_center.stock_transfer', 'Stock transfer')
: t('coinseller_center.transfer_out', 'Transfer out'));
var uid =
counterpartyUID ||
(type === 'income' ? t('coinseller_center.system', 'System') : '-');
var transferAmount = normalizeTransferAmount(item);
return {
id:
firstValue(item, ['transaction_id', 'transactionId']) ||
@ -439,14 +506,16 @@
type: type,
name: name,
uid: uid,
avatar: counterpartyAvatar || avatarData(
name,
type === 'income' ? '#e7f7f3' : '#dbc8ff',
type === 'income' ? '#31a88f' : '#7d57c7'
),
avatar:
counterpartyAvatar ||
avatarData(
name,
type === 'income' ? '#e7f7f3' : '#dbc8ff',
type === 'income' ? '#31a88f' : '#7d57c7'
),
coins: Math.abs(delta),
amount: 0,
currency: '',
amount: transferAmount,
currency: normalizeTransferCurrency(item, transferAmount),
time: timeLabelFromMS(
firstValue(item, ['created_at_ms', 'createdAtMs'])
),
@ -485,7 +554,12 @@
function loadRealData() {
if (!hasAPI()) {
toast(t('coinseller_center.load_failed', 'Load failed. Try again later.'));
toast(
t(
'coinseller_center.load_failed',
'Load failed. Try again later.'
)
);
return Promise.resolve();
}
state.loading = true;
@ -518,7 +592,10 @@
})
.catch(function (error) {
if (window.console && window.console.warn) {
window.console.warn('[coinseller-center] api failed', error);
window.console.warn(
'[coinseller-center] api failed',
error
);
}
toast(
t(
@ -651,7 +728,8 @@
return data.records
.filter(function (record) {
return (
record.type === state.historyTab && recordMatchesQuery(record)
record.type === state.historyTab &&
recordMatchesQuery(record)
);
})
.slice(0, state.historyLimit);
@ -662,7 +740,9 @@
var avatar = document.createElement('div');
var main = document.createElement('div');
var title = document.createElement('div');
var metaGroup = document.createElement('div');
var meta = document.createElement('div');
var transfer = document.createElement('span');
var side = document.createElement('div');
var amount = document.createElement('strong');
var time = document.createElement('span');
@ -671,15 +751,21 @@
avatar.className = 'record-avatar';
main.className = 'record-main';
title.className = 'record-title';
metaGroup.className = 'record-meta-group';
meta.className = 'record-meta';
transfer.className = 'record-transfer';
side.className =
'record-side' + (record.type === 'income' ? ' is-income' : '');
renderAvatar(avatar, record);
title.textContent = record.name || '-';
meta.textContent = t('coinseller_center.uid_prefix', 'UID: {id}').replace(
'{id}',
record.uid || '-'
meta.textContent = t(
'coinseller_center.uid_prefix',
'UID: {id}'
).replace('{id}', record.uid || '-');
transfer.textContent = formatTransferAmount(
record.amount,
record.currency
);
amount.textContent =
(record.type === 'income' ? '+' : '-') +
@ -689,7 +775,9 @@
time.textContent = record.time || '';
main.appendChild(title);
main.appendChild(meta);
metaGroup.appendChild(meta);
if (transfer.textContent) metaGroup.appendChild(transfer);
main.appendChild(metaGroup);
side.appendChild(amount);
side.appendChild(time);
row.appendChild(avatar);
@ -709,13 +797,16 @@
function renderHistory() {
document.querySelectorAll('[data-history-tab]').forEach(function (tab) {
var active = tab.getAttribute('data-history-tab') === state.historyTab;
var active =
tab.getAttribute('data-history-tab') === state.historyTab;
tab.classList.toggle('is-active', active);
tab.setAttribute('aria-selected', String(active));
});
var all = data.records.filter(function (record) {
return record.type === state.historyTab && recordMatchesQuery(record);
return (
record.type === state.historyTab && recordMatchesQuery(record)
);
});
var records = visibleHistoryRecords();
$('loadMoreButton').hidden = records.length >= all.length;
@ -783,7 +874,10 @@
renderRechargeState();
setStatus(
$('searchStatus'),
t('coinseller_center.user_not_found', 'User not found.'),
t(
'coinseller_center.user_not_found',
'User not found.'
),
'warning'
);
return;
@ -795,7 +889,10 @@
})
.catch(function (error) {
if (window.console && window.console.warn) {
window.console.warn('[coinseller-center] user search failed', error);
window.console.warn(
'[coinseller-center] user search failed',
error
);
}
state.target = null;
renderTarget();
@ -832,12 +929,9 @@
function appendTransferRecord(amount, response) {
var payload = unwrapData(response) || {};
var coins = toNumber(
firstValue(payload, [
'amount',
'coin_amount',
'coinAmount',
])
firstValue(payload, ['amount', 'coin_amount', 'coinAmount'])
);
var transferAmount = normalizeTransferAmount(payload);
data.records.unshift({
id:
firstValue(payload, ['transaction_id', 'transactionId']) ||
@ -847,8 +941,8 @@
uid: state.target.uid,
avatar: state.target.avatar,
coins: coins || amount,
amount: 0,
currency: '',
amount: transferAmount,
currency: normalizeTransferCurrency(payload, transferAmount),
time: currentTimeLabel(),
});
}
@ -858,7 +952,10 @@
if (!state.target) {
setStatus(
$('rechargeStatus'),
t('coinseller_center.select_user_first', 'Select a user first.'),
t(
'coinseller_center.select_user_first',
'Select a user first.'
),
'warning'
);
return;
@ -877,7 +974,10 @@
if (amount > data.balance.coins) {
setStatus(
$('rechargeStatus'),
t('coinseller_center.amount_exceeds_stock', 'Amount exceeds stock.'),
t(
'coinseller_center.amount_exceeds_stock',
'Amount exceeds stock.'
),
'warning'
);
return;
@ -921,13 +1021,19 @@
})
.catch(function (error) {
if (window.console && window.console.warn) {
window.console.warn('[coinseller-center] transfer failed', error);
window.console.warn(
'[coinseller-center] transfer failed',
error
);
}
setStatus($('rechargeStatus'), '', '');
toast(
error && error.message
? error.message
: t('coinseller_center.transfer_failed', 'Transfer failed.')
: t(
'coinseller_center.transfer_failed',
'Transfer failed.'
)
);
})
.finally(function () {
@ -1006,11 +1112,13 @@
$('helpButton').addEventListener('click', function () {
openModal('helpModal');
});
document.querySelectorAll('[data-close-modal]').forEach(function (node) {
node.addEventListener('click', function () {
closeModal(node.getAttribute('data-close-modal'));
document
.querySelectorAll('[data-close-modal]')
.forEach(function (node) {
node.addEventListener('click', function () {
closeModal(node.getAttribute('data-close-modal'));
});
});
});
$('searchForm').addEventListener('submit', function (event) {
event.preventDefault();
var value = $('userSearchInput').value.trim();

View File

@ -405,9 +405,18 @@
white-space: nowrap;
}
.record-meta {
.record-meta-group {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px 8px;
margin-top: 5px;
min-width: 0;
}
.record-meta {
overflow: hidden;
min-width: 0;
color: var(--muted);
font-size: 12px;
font-weight: 800;
@ -416,6 +425,15 @@
white-space: nowrap;
}
.record-transfer {
flex: 0 0 auto;
color: var(--primary-deep);
font-size: 12px;
font-weight: 900;
line-height: 1.2;
white-space: nowrap;
}
.record-side {
display: grid;
justify-items: end;