2174 lines
54 KiB
JavaScript
2174 lines
54 KiB
JavaScript
import serviceConfig from "../config/service";
|
||
import request from "../utils/request";
|
||
import { getCurrentWebviewToken } from "../utils/webview-token";
|
||
|
||
const HOME_TICKER_STORAGE_KEY = "asset_home_ticker_cache";
|
||
let runtimeHomeTickerCache = null;
|
||
|
||
function createError(message, raw) {
|
||
return {
|
||
message: message || "接口请求失败",
|
||
raw: raw,
|
||
};
|
||
}
|
||
|
||
function toNumber(value) {
|
||
const number = Number(value || 0);
|
||
return Number.isFinite(number) ? number : 0;
|
||
}
|
||
|
||
function toFixedNumber(value, digits) {
|
||
return toNumber(value).toFixed(digits);
|
||
}
|
||
|
||
function formatHomeNumber(value, digits) {
|
||
const number = toNumber(value);
|
||
|
||
if (digits > 0) {
|
||
const fixed = number.toFixed(digits);
|
||
const parts = fixed.split(".");
|
||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||
return parts.join(".");
|
||
}
|
||
|
||
return Math.round(number)
|
||
.toString()
|
||
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||
}
|
||
|
||
function isSuccessPayload(payload) {
|
||
if (!payload || typeof payload !== "object") {
|
||
return false;
|
||
}
|
||
|
||
const statusCode =
|
||
payload.code !== undefined ? Number(payload.code) : Number(payload.status);
|
||
|
||
return statusCode === 200;
|
||
}
|
||
|
||
function unwrapPayload(payload, fallbackMessage) {
|
||
if (isSuccessPayload(payload)) {
|
||
return payload.data;
|
||
}
|
||
|
||
if (payload && typeof payload === "object") {
|
||
throw createError(
|
||
payload.msg || payload.message || fallbackMessage || "接口请求失败",
|
||
payload,
|
||
);
|
||
}
|
||
|
||
throw createError(fallbackMessage || "接口返回异常", payload);
|
||
}
|
||
|
||
async function fetchPayload(options, fallbackMessage) {
|
||
try {
|
||
const payload = await request(options);
|
||
return unwrapPayload(payload, fallbackMessage);
|
||
} catch (error) {
|
||
if (error && error.message) {
|
||
throw error;
|
||
}
|
||
|
||
throw createError(fallbackMessage || "接口请求失败", error);
|
||
}
|
||
}
|
||
|
||
function createRequestOptions(baseOptions, requestOptions) {
|
||
return Object.assign({}, baseOptions, requestOptions || {});
|
||
}
|
||
|
||
function normalizeTicker(data) {
|
||
const close = toNumber(data && (data.close || data.cnyPrice));
|
||
const lastDayClose = toNumber(data && data.lastDayClose);
|
||
const rawChange = data && data.change;
|
||
let change = typeof rawChange === "string" ? rawChange : "";
|
||
|
||
if (!change) {
|
||
if (close && lastDayClose) {
|
||
const percent = ((close - lastDayClose) / lastDayClose) * 100;
|
||
const prefix = percent >= 0 ? "+" : "";
|
||
change = prefix + percent.toFixed(2) + "%";
|
||
} else {
|
||
change = "0.00%";
|
||
}
|
||
}
|
||
return {
|
||
symbol: (data && data.symbol) || "BMT/CNY",
|
||
close: close,
|
||
cnyPrice:
|
||
(data && data.cnyPrice) || (close ? close.toFixed(2) : "0.00"),
|
||
lastDayClose: lastDayClose,
|
||
change: change,
|
||
};
|
||
}
|
||
|
||
function setHomeTickerCache(ticker) {
|
||
const normalizedTicker = normalizeTicker(ticker);
|
||
runtimeHomeTickerCache = normalizedTicker;
|
||
|
||
try {
|
||
if (typeof uni !== "undefined" && uni.setStorageSync) {
|
||
uni.setStorageSync(HOME_TICKER_STORAGE_KEY, normalizedTicker);
|
||
}
|
||
} catch (error) {
|
||
// Ignore storage failures and keep runtime cache fallback.
|
||
}
|
||
|
||
return normalizedTicker;
|
||
}
|
||
|
||
function getHomeTickerCache() {
|
||
if (runtimeHomeTickerCache) {
|
||
return runtimeHomeTickerCache;
|
||
}
|
||
|
||
try {
|
||
if (typeof uni !== "undefined" && uni.getStorageSync) {
|
||
const storedTicker = uni.getStorageSync(HOME_TICKER_STORAGE_KEY);
|
||
if (storedTicker && typeof storedTicker === "object") {
|
||
runtimeHomeTickerCache = normalizeTicker(storedTicker);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
// Ignore storage failures.
|
||
}
|
||
|
||
return runtimeHomeTickerCache || normalizeTicker(null);
|
||
}
|
||
|
||
function normalizeBalances(data) {
|
||
return {
|
||
points: toNumber(data && data.point),
|
||
power: toNumber(data && data.c_power),
|
||
bmt: toNumber(data && data.bmt_num),
|
||
withdrawableBmt: toNumber(data && data.bmt_num),
|
||
voucher: toNumber(data && data.coin),
|
||
coupon: toNumber(data && data.diamond_balance),
|
||
};
|
||
}
|
||
|
||
function toRawDisplayValue(value) {
|
||
if (value === undefined || value === null || value === "") {
|
||
return "0";
|
||
}
|
||
|
||
return String(value);
|
||
}
|
||
|
||
function buildHomeOverview(balanceData, tickerData) {
|
||
const balances = normalizeBalances(balanceData);
|
||
const ticker = normalizeTicker(tickerData);
|
||
const rawQuickAssets = {
|
||
points: toRawDisplayValue(balanceData && balanceData.point),
|
||
voucher: toRawDisplayValue(balanceData && balanceData.coin),
|
||
coupon: toRawDisplayValue(balanceData && balanceData.diamond_balance),
|
||
power: toRawDisplayValue(balanceData && balanceData.c_power),
|
||
};
|
||
|
||
return {
|
||
title: "数字资产",
|
||
ticker: ticker,
|
||
topStats: [
|
||
{
|
||
key: "wallet-bmt",
|
||
title: "可提取BMT",
|
||
value: toFixedNumber(balances.withdrawableBmt, 2),
|
||
unit: "BMT",
|
||
accent: "gold",
|
||
},
|
||
{
|
||
key: "ticker",
|
||
title: "BMT实时价格",
|
||
value: toFixedNumber(ticker.close || ticker.cnyPrice, 3),
|
||
unit: "CNY/BMT",
|
||
accent: "green",
|
||
},
|
||
],
|
||
quickAssets: [
|
||
{
|
||
key: "points",
|
||
title: "可用积分",
|
||
value: rawQuickAssets.points,
|
||
accent: "gold",
|
||
},
|
||
{
|
||
key: "voucher",
|
||
title: "抵用券",
|
||
value: rawQuickAssets.voucher,
|
||
accent: "rose",
|
||
},
|
||
{
|
||
key: "coupon",
|
||
title: "消费券",
|
||
value: rawQuickAssets.coupon,
|
||
accent: "teal",
|
||
},
|
||
{
|
||
key: "power",
|
||
title: "算力",
|
||
value: rawQuickAssets.power,
|
||
accent: "violet",
|
||
},
|
||
],
|
||
features: [
|
||
{
|
||
key: "bmt-exchange",
|
||
title: "BMT兑换",
|
||
desc: "积分与算力兑换 BMT",
|
||
accent: "mint",
|
||
},
|
||
{
|
||
key: "power-exchange",
|
||
title: "算力兑换",
|
||
desc: "抵用券与消费券兑换算力",
|
||
accent: "amber",
|
||
},
|
||
{
|
||
key: "transfer",
|
||
title: "转赠中心",
|
||
desc: "积分或算力转赠好友",
|
||
accent: "indigo",
|
||
},
|
||
{
|
||
key: "withdraw",
|
||
title: "BMT提取",
|
||
desc: "钱包中的 BMT 可提取到交易所进行交易",
|
||
accent: "pink",
|
||
},
|
||
{
|
||
key: "points-convert",
|
||
title: "积分转换",
|
||
desc: "释放中的积分转换为可用积分",
|
||
accent: "pink",
|
||
},
|
||
],
|
||
notice:
|
||
"数字资产是您在平台上的虚拟资产,请谨慎管理;BMT可在交易所中进行交易。",
|
||
};
|
||
}
|
||
|
||
function buildTransferTips(feePercent) {
|
||
const percentText = toNumber(feePercent) || 10;
|
||
|
||
return {
|
||
points: [
|
||
"只能转赠100的整数倍",
|
||
"凌晨00:00至02:00为系统维护时段不可赠送",
|
||
"转赠系统会扣除" + percentText + "%的手续费",
|
||
],
|
||
power: [
|
||
"只能转赠1的整数倍",
|
||
"转赠系统会扣除" + percentText + "%的手续费",
|
||
],
|
||
};
|
||
}
|
||
|
||
function buildWalletList(address) {
|
||
const normalizedAddress = String(address || "").trim();
|
||
|
||
if (!normalizedAddress) {
|
||
return [];
|
||
}
|
||
|
||
return [
|
||
{
|
||
id: "default-wallet",
|
||
name: serviceConfig.WALLET_NAME,
|
||
address: normalizedAddress,
|
||
isDefault: true,
|
||
},
|
||
];
|
||
}
|
||
|
||
function buildWalletPayload(address) {
|
||
return {
|
||
wallets: buildWalletList(address),
|
||
instructions: [
|
||
"点击交易所 App 底部“资产”进入钱包页",
|
||
"搜索或输入大写字母 BMT",
|
||
"点击“充币 / 充值”进入收款地址页面",
|
||
"复制钱包地址后回填到当前页面",
|
||
],
|
||
};
|
||
}
|
||
|
||
function pickFirstValue(source, keys) {
|
||
const target = source && typeof source === "object" ? source : {};
|
||
|
||
for (let index = 0; index < keys.length; index += 1) {
|
||
const key = keys[index];
|
||
const value = target[key];
|
||
|
||
if (
|
||
value !== undefined &&
|
||
value !== null &&
|
||
!(typeof value === "string" && value.trim() === "")
|
||
) {
|
||
return value;
|
||
}
|
||
}
|
||
|
||
return "";
|
||
}
|
||
|
||
function normalizeListData(data) {
|
||
if (Array.isArray(data)) {
|
||
return data;
|
||
}
|
||
|
||
if (data && Array.isArray(data.list)) {
|
||
return data.list;
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
const DEFAULT_LIST_PAGE = 1;
|
||
const DEFAULT_LIST_PAGE_SIZE = 10;
|
||
|
||
function hasPaginationKey(value) {
|
||
if (!value || typeof value !== "object") {
|
||
return false;
|
||
}
|
||
|
||
return (
|
||
value.page !== undefined ||
|
||
value.pageSize !== undefined ||
|
||
value.page_size !== undefined ||
|
||
value.pagesize !== undefined ||
|
||
value.size !== undefined ||
|
||
value.limit !== undefined
|
||
);
|
||
}
|
||
|
||
function normalizePaginationOptions(pagination) {
|
||
const source = pagination && typeof pagination === "object" ? pagination : {};
|
||
const page = Math.max(
|
||
1,
|
||
Math.floor(
|
||
toNumber(
|
||
pickFirstValue(source, ["page", "current_page", "currentPage", "p"]) ||
|
||
DEFAULT_LIST_PAGE,
|
||
),
|
||
),
|
||
);
|
||
const pageSize = Math.max(
|
||
1,
|
||
Math.floor(
|
||
toNumber(
|
||
pickFirstValue(source, [
|
||
"pageSize",
|
||
"page_size",
|
||
"pagesize",
|
||
"size",
|
||
"limit",
|
||
"per_page",
|
||
]) || DEFAULT_LIST_PAGE_SIZE,
|
||
),
|
||
),
|
||
);
|
||
|
||
return {
|
||
page: page,
|
||
pageSize: pageSize,
|
||
};
|
||
}
|
||
|
||
function resolvePagingArguments(paginationOrRequestOptions, requestOptions) {
|
||
if (requestOptions || hasPaginationKey(paginationOrRequestOptions)) {
|
||
return {
|
||
pagination: normalizePaginationOptions(paginationOrRequestOptions),
|
||
requestOptions: requestOptions || null,
|
||
};
|
||
}
|
||
|
||
return {
|
||
pagination: normalizePaginationOptions(null),
|
||
requestOptions: paginationOrRequestOptions || null,
|
||
};
|
||
}
|
||
|
||
function buildPagingRequestData(baseData, pagination) {
|
||
const normalizedPagination = normalizePaginationOptions(pagination);
|
||
|
||
return Object.assign({}, baseData || {}, {
|
||
page: normalizedPagination.page,
|
||
limit: normalizedPagination.pageSize,
|
||
});
|
||
}
|
||
|
||
function parseBooleanLike(value) {
|
||
if (typeof value === "boolean") {
|
||
return value;
|
||
}
|
||
|
||
if (typeof value === "number") {
|
||
return value > 0;
|
||
}
|
||
|
||
if (typeof value === "string") {
|
||
const normalized = value.trim().toLowerCase();
|
||
if (!normalized) {
|
||
return null;
|
||
}
|
||
|
||
if (
|
||
normalized === "1" ||
|
||
normalized === "true" ||
|
||
normalized === "yes" ||
|
||
normalized === "y"
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
if (
|
||
normalized === "0" ||
|
||
normalized === "false" ||
|
||
normalized === "no" ||
|
||
normalized === "n"
|
||
) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function buildPaginationMeta(data, requestPagination) {
|
||
const normalizedData = data && typeof data === "object" ? data : {};
|
||
const normalizedRequestPagination = normalizePaginationOptions(requestPagination);
|
||
const list = normalizeListData(normalizedData);
|
||
const page =
|
||
Math.floor(
|
||
toNumber(
|
||
pickFirstValue(normalizedData, [
|
||
"page",
|
||
"current_page",
|
||
"currentPage",
|
||
"page_num",
|
||
]),
|
||
),
|
||
) || normalizedRequestPagination.page;
|
||
const pageSize =
|
||
Math.floor(
|
||
toNumber(
|
||
pickFirstValue(normalizedData, [
|
||
"page_size",
|
||
"pagesize",
|
||
"size",
|
||
"limit",
|
||
"per_page",
|
||
]),
|
||
),
|
||
) || normalizedRequestPagination.pageSize;
|
||
const total = Math.floor(
|
||
toNumber(
|
||
pickFirstValue(normalizedData, [
|
||
"total",
|
||
"count",
|
||
"total_count",
|
||
"totalCount",
|
||
"record_count",
|
||
"all_count",
|
||
]),
|
||
),
|
||
);
|
||
const lastPage = Math.floor(
|
||
toNumber(
|
||
pickFirstValue(normalizedData, [
|
||
"last_page",
|
||
"lastPage",
|
||
"page_total",
|
||
"total_page",
|
||
"pages",
|
||
"pageCount",
|
||
]),
|
||
),
|
||
);
|
||
|
||
const hasMoreRaw = pickFirstValue(normalizedData, [
|
||
"has_more",
|
||
"hasMore",
|
||
"more",
|
||
"is_more",
|
||
"next_page",
|
||
"nextPage",
|
||
]);
|
||
const hasMoreByRawValue = parseBooleanLike(hasMoreRaw);
|
||
let hasMore = hasMoreByRawValue;
|
||
|
||
if (hasMore === null && lastPage > 0) {
|
||
hasMore = page < lastPage;
|
||
}
|
||
|
||
if (hasMore === null && total > 0 && pageSize > 0) {
|
||
hasMore = page * pageSize < total;
|
||
}
|
||
|
||
if (hasMore === null) {
|
||
hasMore = list.length >= pageSize && list.length > 0;
|
||
}
|
||
|
||
if (!list.length) {
|
||
hasMore = false;
|
||
}
|
||
|
||
return {
|
||
page: page,
|
||
pageSize: pageSize,
|
||
total: total,
|
||
lastPage: lastPage,
|
||
hasMore: Boolean(hasMore),
|
||
};
|
||
}
|
||
|
||
function formatTransferRecordNumber(value) {
|
||
const number = toNumber(value);
|
||
return formatHomeNumber(number, Number.isInteger(number) ? 0 : 2);
|
||
}
|
||
|
||
function formatLedgerRecordNumber(value) {
|
||
return formatTransferRecordNumber(Math.abs(toNumber(value)));
|
||
}
|
||
|
||
function resolveRecordDirection(item, amountValue, positiveLabel, negativeLabel) {
|
||
const rawDirection = pickFirstValue(item, [
|
||
"io_type",
|
||
"in_out",
|
||
"direction",
|
||
"income_type",
|
||
"is_add",
|
||
"flow_type",
|
||
]);
|
||
|
||
if (rawDirection !== "") {
|
||
const directionText = String(rawDirection).toLowerCase();
|
||
|
||
if (
|
||
directionText === "1" ||
|
||
directionText === "in" ||
|
||
directionText === "income" ||
|
||
directionText === "add" ||
|
||
directionText === "+"
|
||
) {
|
||
return positiveLabel;
|
||
}
|
||
|
||
if (
|
||
directionText === "0" ||
|
||
directionText === "-1" ||
|
||
directionText === "out" ||
|
||
directionText === "expense" ||
|
||
directionText === "sub" ||
|
||
directionText === "-"
|
||
) {
|
||
return negativeLabel;
|
||
}
|
||
}
|
||
|
||
return toNumber(amountValue) >= 0 ? positiveLabel : negativeLabel;
|
||
}
|
||
|
||
function buildDirectionPresentation(direction) {
|
||
return {
|
||
income:
|
||
direction === "收入" ||
|
||
direction === "转入" ||
|
||
direction === "获得" ||
|
||
direction === "到账",
|
||
actionSymbol:
|
||
direction === "收入" ||
|
||
direction === "转入" ||
|
||
direction === "获得" ||
|
||
direction === "到账"
|
||
? "+"
|
||
: "-",
|
||
};
|
||
}
|
||
|
||
function buildRecordId(item, fallbackPrefix) {
|
||
return String(
|
||
pickFirstValue(item, [
|
||
"order_sn",
|
||
"trade_no",
|
||
"bill_no",
|
||
"sn",
|
||
"id",
|
||
"log_id",
|
||
"uid",
|
||
]) || fallbackPrefix + "-" + Math.random(),
|
||
);
|
||
}
|
||
|
||
function normalizeAvailablePointsText(text) {
|
||
const safeText = String(text || "");
|
||
if (!safeText) {
|
||
return "";
|
||
}
|
||
|
||
return safeText
|
||
.replace(/可用积分/g, "__AVAILABLE_POINTS__")
|
||
.replace(/积分/g, "可用积分")
|
||
.replace(/__AVAILABLE_POINTS__/g, "可用积分");
|
||
}
|
||
|
||
function mapWalletFlowRecords(list, meta) {
|
||
return normalizeListData(list).map(function (item) {
|
||
const amountValue = pickFirstValue(item, [
|
||
"number",
|
||
"num",
|
||
"amount",
|
||
"change_num",
|
||
"value",
|
||
"money",
|
||
"bmt_num",
|
||
"power_num",
|
||
"gold_num",
|
||
"point_num",
|
||
]);
|
||
const direction = resolveRecordDirection(item, amountValue, "收入", "支出");
|
||
const directionPresentation = buildDirectionPresentation(direction);
|
||
const orderSn = String(
|
||
pickFirstValue(item, ["order_sn", "trade_no", "bill_no", "sn"]) || "",
|
||
);
|
||
const noteText = String(
|
||
pickFirstValue(item, [
|
||
"remark",
|
||
"desc",
|
||
"memo",
|
||
"note",
|
||
"type_name",
|
||
"status_text",
|
||
]) || "",
|
||
);
|
||
const balanceValue = pickFirstValue(item, [
|
||
"balance",
|
||
"after_balance",
|
||
"surplus",
|
||
"remain",
|
||
"wallet_balance",
|
||
"usable_balance",
|
||
]);
|
||
const title =
|
||
pickFirstValue(item, ["title", "name", "type_name"]) ||
|
||
meta.title ||
|
||
meta.unit + "记录";
|
||
const isPointsLedger = String(meta && meta.key ? meta.key : "") === "points";
|
||
const displayTitle = isPointsLedger
|
||
? normalizeAvailablePointsText(title)
|
||
: title;
|
||
const displayNoteText = isPointsLedger
|
||
? normalizeAvailablePointsText(noteText)
|
||
: noteText;
|
||
const displayDirection = isPointsLedger
|
||
? normalizeAvailablePointsText(direction)
|
||
: direction;
|
||
const displaySubtitle = orderSn
|
||
? "单号 " + orderSn
|
||
: displayNoteText || displayDirection;
|
||
const displayFeeText =
|
||
displayNoteText || (orderSn ? "单号 " + orderSn : meta.subtitle);
|
||
|
||
return {
|
||
id: buildRecordId(item, meta.key || "flow"),
|
||
sourceId: String(pickFirstValue(item, ["id", "log_id", "uid"]) || ""),
|
||
title: displayTitle,
|
||
subtitle: displaySubtitle,
|
||
time: pickFirstValue(item, [
|
||
"add_time",
|
||
"create_time",
|
||
"created_at",
|
||
"time",
|
||
"update_time",
|
||
]),
|
||
amount:
|
||
directionPresentation.actionSymbol +
|
||
formatLedgerRecordNumber(amountValue),
|
||
balance:
|
||
balanceValue !== ""
|
||
? "结余 " + formatTransferRecordNumber(balanceValue) + " " + meta.unit
|
||
: "",
|
||
balanceLabel:
|
||
balanceValue !== ""
|
||
? "剩余" + meta.unit + ":" + formatTransferRecordNumber(balanceValue)
|
||
: "当前" + meta.unit + "流水",
|
||
assetLabel: meta.unit,
|
||
feeText: displayFeeText,
|
||
directionLabel: displayDirection,
|
||
actionSymbol: directionPresentation.actionSymbol,
|
||
orderSn: orderSn,
|
||
noteText: displayNoteText,
|
||
tag: directionPresentation.income ? "收" : "支",
|
||
tone: directionPresentation.income ? "success" : "danger",
|
||
cardTone: directionPresentation.income ? "success" : "danger",
|
||
};
|
||
});
|
||
}
|
||
|
||
function buildRedeemConsumeText(item, meta) {
|
||
const fragments = [];
|
||
|
||
if (meta.type === 0) {
|
||
const voucherValue = pickFirstValue(item, [
|
||
"voucher_num",
|
||
"coin",
|
||
"deduct_coin",
|
||
"use_coin",
|
||
]);
|
||
const couponValue = pickFirstValue(item, [
|
||
"coupon_num",
|
||
"diamond",
|
||
"deduct_diamond",
|
||
"use_diamond",
|
||
]);
|
||
|
||
if (voucherValue !== "") {
|
||
fragments.push("抵用券 " + formatTransferRecordNumber(voucherValue));
|
||
}
|
||
|
||
if (couponValue !== "") {
|
||
fragments.push("消费券 " + formatTransferRecordNumber(couponValue));
|
||
}
|
||
} else {
|
||
const pointValue = pickFirstValue(item, [
|
||
"point",
|
||
"points",
|
||
"point_num",
|
||
"use_point",
|
||
"integral",
|
||
]);
|
||
const powerValue = pickFirstValue(item, [
|
||
"power",
|
||
"power_num",
|
||
"c_power",
|
||
"use_power",
|
||
]);
|
||
|
||
if (pointValue !== "") {
|
||
const pointLabel = meta.type === 1 ? "可用积分" : "积分";
|
||
fragments.push(pointLabel + " " + formatTransferRecordNumber(pointValue));
|
||
}
|
||
|
||
if (powerValue !== "") {
|
||
fragments.push("算力 " + formatTransferRecordNumber(powerValue));
|
||
}
|
||
}
|
||
|
||
if (fragments.length) {
|
||
return "消耗" + fragments.join(" / ");
|
||
}
|
||
|
||
return String(
|
||
pickFirstValue(item, ["remark", "desc", "memo", "note", "status_text"]) ||
|
||
meta.subtitle ||
|
||
"",
|
||
);
|
||
}
|
||
|
||
function mapRedeemRecords(list, meta) {
|
||
return normalizeListData(list).map(function (item) {
|
||
const primaryValue = pickFirstValue(item, meta.amountKeys);
|
||
const orderSn = String(
|
||
pickFirstValue(item, ["order_sn", "trade_no", "bill_no", "sn"]) || "",
|
||
);
|
||
const balanceValue = pickFirstValue(item, [
|
||
"balance",
|
||
"after_balance",
|
||
"surplus",
|
||
"remain",
|
||
]);
|
||
const noteText = buildRedeemConsumeText(item, meta);
|
||
|
||
return {
|
||
id: buildRecordId(item, meta.key || "redeem"),
|
||
title:
|
||
pickFirstValue(item, ["title", "name", "type_name"]) ||
|
||
meta.title ||
|
||
"兑换记录",
|
||
subtitle: orderSn ? "单号 " + orderSn : noteText,
|
||
time: pickFirstValue(item, [
|
||
"add_time",
|
||
"create_time",
|
||
"created_at",
|
||
"time",
|
||
"update_time",
|
||
]),
|
||
amount: "+" + formatLedgerRecordNumber(primaryValue),
|
||
balance:
|
||
balanceValue !== ""
|
||
? "结余 " + formatTransferRecordNumber(balanceValue) + " " + meta.unit
|
||
: "",
|
||
balanceLabel:
|
||
balanceValue !== ""
|
||
? "剩余" + meta.unit + ":" + formatTransferRecordNumber(balanceValue)
|
||
: "本次到账" + meta.unit,
|
||
assetLabel: meta.unit,
|
||
feeText: noteText,
|
||
directionLabel: "兑换",
|
||
actionSymbol: "+",
|
||
orderSn: orderSn,
|
||
tag: "兑",
|
||
tone: "success",
|
||
cardTone: "success",
|
||
};
|
||
});
|
||
}
|
||
|
||
function getTransferRecordUnit(item) {
|
||
return Number(item && item.type) === 0 ? "算力" : "可用积分";
|
||
}
|
||
|
||
function normalizeTransferPointsText(text, item) {
|
||
if (Number(item && item.type) === 0) {
|
||
return String(text || "");
|
||
}
|
||
|
||
const safeText = String(text || "");
|
||
return safeText
|
||
.replace(/可用积分/g, "__POINTS_LABEL__")
|
||
.replace(/积分/g, "可用积分")
|
||
.replace(/__POINTS_LABEL__/g, "可用积分");
|
||
}
|
||
|
||
function getTransferRecordTone(item) {
|
||
return Number(item && item.io_type) === 1 ? "success" : "danger";
|
||
}
|
||
|
||
function getTransferRecordTag(item) {
|
||
return Number(item && item.io_type) === 1 ? "收" : "赠";
|
||
}
|
||
|
||
function getTransferRecordDirection(item) {
|
||
return Number(item && item.io_type) === 1 ? "转入" : "转出";
|
||
}
|
||
|
||
function getTransferRecordTitle(item) {
|
||
const unit = getTransferRecordUnit(item);
|
||
return Number(item && item.io_type) === 1 ? unit + "获赠" : unit + "转赠";
|
||
}
|
||
|
||
function getTransferRecordSymbol(item) {
|
||
return Number(item && item.io_type) === 1 ? "+" : "-";
|
||
}
|
||
|
||
function getTransferRecordAmount(item) {
|
||
const numberText = formatTransferRecordNumber(item && item.num);
|
||
return getTransferRecordSymbol(item) + numberText;
|
||
}
|
||
|
||
function extractTransferTitleFeeValue(item) {
|
||
const titleText = String(pickFirstValue(item, ["title", "name"]) || "").trim();
|
||
|
||
if (!titleText) {
|
||
return "";
|
||
}
|
||
|
||
if (/^-?\d+(?:\.\d+)?$/.test(titleText)) {
|
||
return titleText;
|
||
}
|
||
|
||
const titleFeeMatch = titleText.match(
|
||
/手续费(?:\s*[::]?\s*|\s*[((]\s*)(-?\d+(?:\.\d+)?)\s*[))]?/,
|
||
);
|
||
|
||
return titleFeeMatch && titleFeeMatch[1] !== undefined
|
||
? titleFeeMatch[1]
|
||
: "";
|
||
}
|
||
|
||
function getTransferRecordFee(item) {
|
||
const titleFeeValue = extractTransferTitleFeeValue(item);
|
||
if (titleFeeValue !== "") {
|
||
return toNumber(titleFeeValue);
|
||
}
|
||
|
||
if (
|
||
item &&
|
||
item.fee !== undefined &&
|
||
item.fee !== null &&
|
||
String(item.fee).trim() !== ""
|
||
) {
|
||
return toNumber(item.fee);
|
||
}
|
||
|
||
return (toNumber(item && item.num) * toNumber(item && item.fee_percent)) / 100;
|
||
}
|
||
|
||
function getTransferRecordFeeText(item) {
|
||
const percent = toNumber(item && item.fee_percent);
|
||
const unit = getTransferRecordUnit(item);
|
||
const feeText = formatHomeNumber(getTransferRecordFee(item), 2);
|
||
|
||
if (percent > 0) {
|
||
return "手续费: " + feeText;
|
||
}
|
||
|
||
return "手续费: " + feeText + " " + unit;
|
||
}
|
||
|
||
function getTransferRecordBalance(item) {
|
||
const unit = getTransferRecordUnit(item);
|
||
return "结余 " + toFixedNumber(item && item.balance, 2) + " " + unit;
|
||
}
|
||
|
||
function getTransferRecordBalanceLabel(item) {
|
||
const unit = getTransferRecordUnit(item);
|
||
return "剩余" + unit + ":" + formatTransferRecordNumber(item && item.balance);
|
||
}
|
||
|
||
function mapTransferRecords(list) {
|
||
return (Array.isArray(list) ? list : []).map(function (item) {
|
||
const titleText = item.title || getTransferRecordTitle(item);
|
||
return {
|
||
id: item.order_sn || item.id || String(Math.random()),
|
||
title: normalizeTransferPointsText(titleText, item),
|
||
subtitle: item.order_sn ? "单号 " + item.order_sn : getTransferRecordDirection(item),
|
||
time: item.add_time || "",
|
||
amount: getTransferRecordAmount(item),
|
||
balance: getTransferRecordBalance(item),
|
||
balanceLabel: getTransferRecordBalanceLabel(item),
|
||
assetLabel: getTransferRecordUnit(item),
|
||
feeText: getTransferRecordFeeText(item),
|
||
directionLabel: getTransferRecordDirection(item),
|
||
actionSymbol: getTransferRecordSymbol(item),
|
||
orderSn: item.order_sn || "",
|
||
tag: getTransferRecordTag(item),
|
||
tone: getTransferRecordTone(item),
|
||
cardTone: getTransferRecordTone(item),
|
||
};
|
||
});
|
||
}
|
||
|
||
function mapPointsConvertRecords(list) {
|
||
return normalizeListData(list).map(function (item) {
|
||
const rawTransferPointValue = pickFirstValue(item, [
|
||
"transfer_point_num",
|
||
"transfer_coin_num",
|
||
"point_num",
|
||
"coin_num",
|
||
"value",
|
||
]);
|
||
const hasTransferPointValue = rawTransferPointValue !== "";
|
||
const amountValue = pickFirstValue(item, [
|
||
"gold_num",
|
||
"number",
|
||
"transfer_point_num",
|
||
"transfer_coin_num",
|
||
"point_num",
|
||
"num",
|
||
"amount",
|
||
"value",
|
||
]);
|
||
const rawStatusText = String(
|
||
pickFirstValue(item, [
|
||
"status_text",
|
||
"status_name",
|
||
"status_desc",
|
||
"transfer_status_text",
|
||
]) || "",
|
||
);
|
||
const rawStatusCode = String(
|
||
pickFirstValue(item, ["status", "state", "transfer_status"]) || "",
|
||
);
|
||
let statusText = rawStatusText;
|
||
let disabled = false;
|
||
|
||
if (!statusText) {
|
||
if (rawStatusCode === "1") {
|
||
statusText = "兑换中";
|
||
} else if (rawStatusCode === "2") {
|
||
statusText = "已转换";
|
||
} else if (rawStatusCode === "3") {
|
||
statusText = "转换失败";
|
||
}
|
||
}
|
||
|
||
if (
|
||
/中/.test(statusText) ||
|
||
/完成/.test(statusText) ||
|
||
/成功/.test(statusText) ||
|
||
/失败/.test(statusText)
|
||
) {
|
||
disabled = true;
|
||
}
|
||
|
||
if (hasTransferPointValue && toNumber(rawTransferPointValue) <= 0) {
|
||
disabled = true;
|
||
}
|
||
|
||
return {
|
||
id: String(
|
||
pickFirstValue(item, ["id", "bill_id", "log_id", "sn"]) ||
|
||
Math.random(),
|
||
),
|
||
sourceId: String(pickFirstValue(item, ["id", "bill_id", "log_id"]) || ""),
|
||
orderSn: String(
|
||
pickFirstValue(item, ["order_sn", "trade_no", "bill_no", "sn"]) || "",
|
||
),
|
||
title:
|
||
pickFirstValue(item, ["title", "name", "type_name"]) ||
|
||
"系统赠送积分",
|
||
subtitle:
|
||
"可转数量 " +
|
||
formatTransferRecordNumber(
|
||
rawTransferPointValue || 0,
|
||
),
|
||
time: pickFirstValue(item, [
|
||
"add_time",
|
||
"create_time",
|
||
"created_at",
|
||
"time",
|
||
"update_time",
|
||
]),
|
||
amount: "+" + formatLedgerRecordNumber(amountValue),
|
||
amountValue: toNumber(amountValue),
|
||
transferPointValue: toNumber(rawTransferPointValue),
|
||
transfer_point_num: toNumber(rawTransferPointValue),
|
||
statusText: statusText,
|
||
disabled: disabled,
|
||
balance:
|
||
item &&
|
||
item.userBillRelease &&
|
||
item.userBillRelease.total !== undefined &&
|
||
item.userBillRelease.total !== null
|
||
? "释放总量 " + formatTransferRecordNumber(item.userBillRelease.total)
|
||
: "",
|
||
tag: "积",
|
||
tone: disabled ? "warning" : "success",
|
||
};
|
||
});
|
||
}
|
||
|
||
function extractMonthLabel(value) {
|
||
const raw = String(value || "").trim();
|
||
|
||
if (!raw) {
|
||
const currentDate = new Date();
|
||
const year = currentDate.getFullYear();
|
||
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
|
||
return year + "-" + month;
|
||
}
|
||
|
||
const matcher = raw.match(/(\d{4})[-/.](\d{1,2})/);
|
||
if (matcher) {
|
||
return matcher[1] + "-" + String(matcher[2]).padStart(2, "0");
|
||
}
|
||
|
||
const date = new Date(raw.replace(/-/g, "/"));
|
||
if (Number.isNaN(date.getTime())) {
|
||
return raw.slice(0, 7);
|
||
}
|
||
|
||
return (
|
||
date.getFullYear() +
|
||
"-" +
|
||
String(date.getMonth() + 1).padStart(2, "0")
|
||
);
|
||
}
|
||
|
||
function buildPointsHistoryRecords(list) {
|
||
return mapWalletFlowRecords(list, {
|
||
key: "points-convert-history",
|
||
unit: "积分",
|
||
title: "积分记录",
|
||
subtitle: "可用积分流水",
|
||
}).map(function (item) {
|
||
return Object.assign({}, item, {
|
||
month: extractMonthLabel(item.time),
|
||
detailAvailable: canOpenPointsConvertRecordDetail(item),
|
||
amountTone: item.actionSymbol === "+" ? "increase" : "decrease",
|
||
});
|
||
});
|
||
}
|
||
|
||
function buildMonthOptions(list) {
|
||
const months = (Array.isArray(list) ? list : []).reduce(function (
|
||
result,
|
||
item,
|
||
) {
|
||
const month = item && item.month ? item.month : extractMonthLabel(item && item.time);
|
||
if (month && result.indexOf(month) === -1) {
|
||
result.push(month);
|
||
}
|
||
return result;
|
||
}, []);
|
||
|
||
months.sort(function (left, right) {
|
||
return right.localeCompare(left);
|
||
});
|
||
|
||
if (!months.length) {
|
||
months.push(extractMonthLabel(""));
|
||
}
|
||
|
||
return months;
|
||
}
|
||
|
||
function canOpenPointsConvertRecordDetail(item) {
|
||
const target = item && typeof item === "object" ? item : {};
|
||
const contentText = [
|
||
target.title,
|
||
target.subtitle,
|
||
target.noteText,
|
||
target.feeText,
|
||
target.directionLabel,
|
||
]
|
||
.join(" ")
|
||
.toLowerCase();
|
||
|
||
return (
|
||
Boolean(target.orderSn || target.sourceId) &&
|
||
/转换/.test(contentText) &&
|
||
!/bmt/.test(contentText)
|
||
);
|
||
}
|
||
|
||
function extractPointsConvertDetailItems(data) {
|
||
if (Array.isArray(data)) {
|
||
return data;
|
||
}
|
||
|
||
if (!data || typeof data !== "object") {
|
||
return [];
|
||
}
|
||
|
||
if (Array.isArray(data.list)) {
|
||
return data.list;
|
||
}
|
||
|
||
if (Array.isArray(data.items)) {
|
||
return data.items;
|
||
}
|
||
|
||
if (Array.isArray(data.details)) {
|
||
return data.details;
|
||
}
|
||
|
||
if (Array.isArray(data.records)) {
|
||
return data.records;
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
function buildPointsConvertDetailFallback(record) {
|
||
const fallback = record || {};
|
||
const amountValue = String(fallback.amount || "").replace(/[^\d.-]/g, "");
|
||
|
||
return {
|
||
orderSn: fallback.orderSn || "",
|
||
totalAmount:
|
||
fallback.amount && String(fallback.amount).trim()
|
||
? String(fallback.amount)
|
||
: "+" + formatLedgerRecordNumber(amountValue),
|
||
time: fallback.time || "",
|
||
details: [
|
||
{
|
||
id: fallback.id || fallback.orderSn || "fallback-record",
|
||
title: fallback.title || "积分转换",
|
||
amount:
|
||
fallback.amount && String(fallback.amount).trim()
|
||
? String(fallback.amount)
|
||
: "+" + formatLedgerRecordNumber(amountValue),
|
||
time: fallback.time || "",
|
||
},
|
||
],
|
||
};
|
||
}
|
||
|
||
function buildPointsConvertDetailPayload(data, fallbackRecord) {
|
||
const detailItems = extractPointsConvertDetailItems(data);
|
||
const summarySource =
|
||
data && typeof data === "object" && !Array.isArray(data) ? data : {};
|
||
const fallback = fallbackRecord || {};
|
||
const summaryTotalValue = pickFirstValue(summarySource, [
|
||
"gold_num",
|
||
"number",
|
||
"num",
|
||
"amount",
|
||
"point_num",
|
||
"transfer_coin_num",
|
||
"total",
|
||
]);
|
||
const detailFallbackTotal = detailItems.reduce(function (total, item) {
|
||
const value = pickFirstValue(item, [
|
||
"gold_num",
|
||
"number",
|
||
"num",
|
||
"amount",
|
||
"point_num",
|
||
"transfer_coin_num",
|
||
"value",
|
||
]);
|
||
return total + toNumber(value);
|
||
}, 0);
|
||
const totalValue =
|
||
summaryTotalValue !== "" ? summaryTotalValue : detailFallbackTotal;
|
||
const totalAmountText =
|
||
totalValue !== ""
|
||
? "+" + formatLedgerRecordNumber(totalValue)
|
||
: fallback.amount || "+0";
|
||
const detailList = detailItems.map(function (item, index) {
|
||
const amountValue = pickFirstValue(item, [
|
||
"gold_num",
|
||
"number",
|
||
"num",
|
||
"amount",
|
||
"point_num",
|
||
"transfer_coin_num",
|
||
"value",
|
||
]);
|
||
|
||
return {
|
||
id: String(
|
||
pickFirstValue(item, ["id", "bill_id", "log_id", "sn"]) ||
|
||
"detail-" + index,
|
||
),
|
||
title:
|
||
pickFirstValue(item, ["title", "name", "type_name"]) ||
|
||
fallback.title ||
|
||
"系统赠送积分",
|
||
amount: "+" + formatLedgerRecordNumber(amountValue),
|
||
time:
|
||
pickFirstValue(item, [
|
||
"add_time",
|
||
"create_time",
|
||
"created_at",
|
||
"time",
|
||
"update_time",
|
||
]) || fallback.time || "",
|
||
};
|
||
});
|
||
|
||
if (!detailList.length) {
|
||
return buildPointsConvertDetailFallback({
|
||
id: fallback.id,
|
||
orderSn:
|
||
String(
|
||
pickFirstValue(summarySource, [
|
||
"order_sn",
|
||
"trade_no",
|
||
"bill_no",
|
||
"sn",
|
||
]) || fallback.orderSn || "",
|
||
),
|
||
title:
|
||
pickFirstValue(summarySource, ["title", "name", "type_name"]) ||
|
||
fallback.title,
|
||
amount: totalAmountText,
|
||
time:
|
||
pickFirstValue(summarySource, [
|
||
"add_time",
|
||
"create_time",
|
||
"created_at",
|
||
"time",
|
||
"update_time",
|
||
]) || fallback.time,
|
||
});
|
||
}
|
||
|
||
return {
|
||
orderSn: String(
|
||
pickFirstValue(summarySource, ["order_sn", "trade_no", "bill_no", "sn"]) ||
|
||
fallback.orderSn ||
|
||
"",
|
||
),
|
||
totalAmount: totalAmountText,
|
||
time:
|
||
pickFirstValue(summarySource, [
|
||
"add_time",
|
||
"create_time",
|
||
"created_at",
|
||
"time",
|
||
"update_time",
|
||
]) || fallback.time || "",
|
||
details: detailList,
|
||
};
|
||
}
|
||
|
||
function buildDefaultLedger(type, balances) {
|
||
const map = {
|
||
power: {
|
||
title: "兑换记录",
|
||
subtitle: "算力兑换记录",
|
||
},
|
||
bmt: {
|
||
title: "兑换记录",
|
||
subtitle: "BMT兑换记录",
|
||
},
|
||
withdraw: {
|
||
title: "提取记录",
|
||
subtitle: "BMT提取流水",
|
||
},
|
||
coupon: {
|
||
title: "消费券",
|
||
subtitle: "当前消费券 " + toFixedNumber(balances.coupon, 2),
|
||
},
|
||
voucher: {
|
||
title: "抵用券记录",
|
||
subtitle: "可用抵用券 " + toFixedNumber(balances.voucher, 2),
|
||
},
|
||
};
|
||
|
||
return {
|
||
type: type,
|
||
title: map[type].title,
|
||
subtitle: map[type].subtitle,
|
||
records: [],
|
||
};
|
||
}
|
||
|
||
function normalizeTransferTarget(data, fallbackId) {
|
||
if (data && typeof data === "object" && !Array.isArray(data)) {
|
||
const id = String(data.uid || data.id || fallbackId || "").trim();
|
||
if (!id) {
|
||
throw createError("未查询到好友", data);
|
||
}
|
||
|
||
return {
|
||
id: id,
|
||
nickname:
|
||
data.nickname || data.nick_name || data.username || "用户" + id,
|
||
phone: data.mobile || data.phone || "ID已通过校验",
|
||
avatar: data.avatar || data.headimg || "",
|
||
};
|
||
}
|
||
|
||
if (data === true) {
|
||
const id = String(fallbackId || "").trim();
|
||
if (!id) {
|
||
throw createError("未查询到好友", data);
|
||
}
|
||
|
||
return {
|
||
id: id,
|
||
nickname: "用户" + id,
|
||
phone: "ID已通过校验",
|
||
avatar: "",
|
||
};
|
||
}
|
||
|
||
throw createError("未查询到好友", data);
|
||
}
|
||
|
||
function sumBy(list, key) {
|
||
return (Array.isArray(list) ? list : []).reduce(function (total, item) {
|
||
return total + toNumber(item && item[key]);
|
||
}, 0);
|
||
}
|
||
|
||
async function fetchPriceData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.price,
|
||
}, requestOptions),
|
||
"实时价格加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchHomeBalanceData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.homeBalance,
|
||
}, requestOptions),
|
||
"首页资产加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchBmtPowerRateData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.bmtRedeemPowerRate,
|
||
}, requestOptions),
|
||
"兑换比例加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchPowerExchangeMuitData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.powerExchangeMuit,
|
||
}, requestOptions),
|
||
"兑换倍率加载失败",
|
||
);
|
||
}
|
||
|
||
function resolveRedeemPowerRate(data) {
|
||
const sourceValue =
|
||
data && typeof data === "object"
|
||
? pickFirstValue(data, [
|
||
"r",
|
||
"rate",
|
||
"power_rate",
|
||
"powerRate",
|
||
"redeem_power_rate",
|
||
"redeemRate",
|
||
"value",
|
||
"num",
|
||
"number",
|
||
])
|
||
: data;
|
||
|
||
const normalizedRate = toNumber(sourceValue);
|
||
return normalizedRate > 0 ? normalizedRate : 0;
|
||
}
|
||
|
||
async function fetchTransferFeeData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.transferFee,
|
||
}, requestOptions),
|
||
"手续费比例加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchWithdrawRateData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.withdrawRate,
|
||
}, requestOptions),
|
||
"提现费率加载失败",
|
||
);
|
||
}
|
||
|
||
function resolvePercentRate(data) {
|
||
let sourceValue =
|
||
data && typeof data === "object"
|
||
? pickFirstValue(data, [
|
||
"r",
|
||
"rate",
|
||
"fee_rate",
|
||
"feeRate",
|
||
"withdraw_rate",
|
||
"withdrawRate",
|
||
"prop",
|
||
"value",
|
||
"num",
|
||
"number",
|
||
])
|
||
: data;
|
||
|
||
if (typeof sourceValue === "string") {
|
||
sourceValue = sourceValue.replace(/%/g, "").trim();
|
||
}
|
||
|
||
const rate = toNumber(sourceValue);
|
||
|
||
if (rate > 0 && rate < 1) {
|
||
return rate * 100;
|
||
}
|
||
|
||
return rate;
|
||
}
|
||
|
||
async function fetchWalletAddressData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.walletDetail,
|
||
}, requestOptions),
|
||
"钱包加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchCoinIndexData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.coinIndex,
|
||
}, requestOptions),
|
||
"抵用券信息加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchUserProfileData(requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.userProfile,
|
||
}, requestOptions),
|
||
"用户信息加载失败",
|
||
);
|
||
}
|
||
|
||
function normalizePointsConvertInterval(value) {
|
||
const rawInterval = String(
|
||
value === undefined || value === null ? "" : value,
|
||
).trim();
|
||
|
||
if (!rawInterval) {
|
||
return serviceConfig.POINTS_CONVERT_INTERVAL;
|
||
}
|
||
|
||
const matcher = rawInterval.match(
|
||
/^(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)$/,
|
||
);
|
||
if (!matcher) {
|
||
return serviceConfig.POINTS_CONVERT_INTERVAL;
|
||
}
|
||
|
||
const start = toNumber(matcher[1]);
|
||
const end = toNumber(matcher[2]);
|
||
|
||
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
||
return serviceConfig.POINTS_CONVERT_INTERVAL;
|
||
}
|
||
|
||
return String(start) + "," + String(end);
|
||
}
|
||
|
||
async function fetchPointsConvertList(pagination, requestOptions, interval) {
|
||
const normalizedInterval = normalizePointsConvertInterval(interval);
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.pointsConvertAvailableList,
|
||
data: buildPagingRequestData({
|
||
interval: normalizedInterval,
|
||
}, pagination),
|
||
}, requestOptions),
|
||
"积分转换列表加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchPointsConvertHistoryListData(
|
||
month,
|
||
pagination,
|
||
requestOptions,
|
||
) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.pointsConvertHistoryList,
|
||
data: buildPagingRequestData({
|
||
month: month,
|
||
}, pagination),
|
||
}, requestOptions),
|
||
"积分兑换列表加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchTransferLedgerData(pagination, requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.transferLedger,
|
||
data: buildPagingRequestData({}, pagination),
|
||
}, requestOptions),
|
||
"转赠记录加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchWalletFlowListData(flowType, pagination, requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.walletFlowList,
|
||
data: buildPagingRequestData({
|
||
type: flowType,
|
||
}, pagination),
|
||
}, requestOptions),
|
||
"资产流水加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchRedeemRecordListData(redeemType, pagination, requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.redeemRecordList,
|
||
data: buildPagingRequestData({
|
||
type: redeemType,
|
||
}, pagination),
|
||
}, requestOptions),
|
||
"兑换记录加载失败",
|
||
);
|
||
}
|
||
|
||
async function fetchPointsConvertInfoData(query, requestOptions) {
|
||
return fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.pointsConvertRecordDetail,
|
||
data: query || {},
|
||
}, requestOptions),
|
||
"积分转换详情加载失败",
|
||
);
|
||
}
|
||
|
||
export async function fetchAssetHome(requestOptions) {
|
||
const result = await Promise.all([
|
||
fetchPriceData(requestOptions),
|
||
fetchHomeBalanceData(requestOptions),
|
||
]);
|
||
const overview = buildHomeOverview(result[1], result[0]);
|
||
setHomeTickerCache(overview.ticker);
|
||
return overview;
|
||
}
|
||
|
||
export async function fetchVoucherBrokerLinkData(requestOptions) {
|
||
const data = await fetchCoinIndexData(requestOptions);
|
||
const userId = pickFirstValue(data, ["user_id", "uid", "id"]);
|
||
const balance = pickFirstValue(data, ["balance", "coin", "total"]);
|
||
|
||
if (userId === "") {
|
||
throw createError("未获取到用户信息", data);
|
||
}
|
||
|
||
return {
|
||
userId: String(userId),
|
||
balance: balance === "" ? "0" : String(balance),
|
||
};
|
||
}
|
||
|
||
export async function fetchCouponRedeemLinkData(requestOptions) {
|
||
const userInfo = await fetchUserProfileData(requestOptions);
|
||
const canRedeem = pickFirstValue(userInfo, [
|
||
"is_show_merchant_button",
|
||
"canRedeem",
|
||
"can_redeem",
|
||
]);
|
||
const token = String(getCurrentWebviewToken() || "").trim();
|
||
let targetUrl =
|
||
serviceConfig.HTTP_REQUEST_URL +
|
||
"/MD/pages/redeemVoucher/index?canRedeem=" +
|
||
encodeURIComponent(canRedeem === "" ? "0" : String(canRedeem));
|
||
|
||
if (token) {
|
||
targetUrl +=
|
||
(targetUrl.indexOf("?") > -1 ? "&" : "?") +
|
||
"token=" +
|
||
encodeURIComponent(token);
|
||
}
|
||
|
||
return {
|
||
canRedeem: canRedeem === "" ? "0" : String(canRedeem),
|
||
token: token,
|
||
targetUrl: targetUrl,
|
||
encodedTargetUrl: encodeURIComponent(targetUrl),
|
||
};
|
||
}
|
||
|
||
export async function fetchPointsConvertHome(
|
||
month,
|
||
paginationOrRequestOptions,
|
||
requestOptions,
|
||
) {
|
||
const params = resolvePagingArguments(
|
||
paginationOrRequestOptions,
|
||
requestOptions,
|
||
);
|
||
const pagination = params.pagination;
|
||
const mergedRequestOptions = params.requestOptions;
|
||
const result = await Promise.all([
|
||
fetchHomeBalanceData(mergedRequestOptions),
|
||
fetchPointsConvertHistoryListData(
|
||
month,
|
||
pagination,
|
||
mergedRequestOptions,
|
||
),
|
||
]);
|
||
const balances = normalizeBalances(result[0]);
|
||
const records = buildPointsHistoryRecords(result[1]).map(function (item) {
|
||
return Object.assign({}, item, {
|
||
month: extractMonthLabel(item.time) || month,
|
||
detailAvailable: canOpenPointsConvertRecordDetail(item),
|
||
});
|
||
});
|
||
|
||
return {
|
||
availablePoints: formatHomeNumber(balances.points, 0),
|
||
records: records,
|
||
pagination: buildPaginationMeta(result[1], pagination),
|
||
};
|
||
}
|
||
|
||
export async function fetchPointsConvertSelection(
|
||
paginationOrRequestOptions,
|
||
requestOptions,
|
||
) {
|
||
const interval =
|
||
paginationOrRequestOptions &&
|
||
typeof paginationOrRequestOptions === "object"
|
||
? paginationOrRequestOptions.interval
|
||
: "";
|
||
const params = resolvePagingArguments(
|
||
paginationOrRequestOptions,
|
||
requestOptions,
|
||
);
|
||
const data = await fetchPointsConvertList(
|
||
params.pagination,
|
||
params.requestOptions,
|
||
interval,
|
||
);
|
||
const items = mapPointsConvertRecords(data);
|
||
const pagination = buildPaginationMeta(data, params.pagination);
|
||
|
||
return {
|
||
items: items,
|
||
pagination: pagination,
|
||
};
|
||
}
|
||
|
||
export async function fetchPointsConvertRecordDetail(record, requestOptions) {
|
||
const fallbackRecord = record || {};
|
||
const query = {};
|
||
|
||
if (fallbackRecord.sourceId) {
|
||
query.id = fallbackRecord.sourceId;
|
||
} else if (
|
||
fallbackRecord.id &&
|
||
fallbackRecord.orderSn &&
|
||
fallbackRecord.id !== fallbackRecord.orderSn
|
||
) {
|
||
query.id = fallbackRecord.id;
|
||
}
|
||
|
||
if (fallbackRecord.orderSn) {
|
||
query.order_sn = fallbackRecord.orderSn;
|
||
query.sn = fallbackRecord.orderSn;
|
||
}
|
||
|
||
if (!Object.keys(query).length) {
|
||
return buildPointsConvertDetailFallback(fallbackRecord);
|
||
}
|
||
|
||
try {
|
||
const data = await fetchPointsConvertInfoData(query, requestOptions);
|
||
return buildPointsConvertDetailPayload(data, fallbackRecord);
|
||
} catch (error) {
|
||
return buildPointsConvertDetailFallback(fallbackRecord);
|
||
}
|
||
}
|
||
|
||
export async function submitAssetPointsConvert(payload, requestOptions) {
|
||
const ids = Array.isArray(payload && payload.ids) ? payload.ids : [];
|
||
|
||
if (!ids.length) {
|
||
throw createError("暂无可转换积分");
|
||
}
|
||
|
||
await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.pointsConvertSubmit,
|
||
method: "POST",
|
||
data: {
|
||
ids: ids.join(","),
|
||
type: 1,
|
||
},
|
||
}, requestOptions),
|
||
"积分转换失败",
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
message: "转换成功",
|
||
};
|
||
}
|
||
|
||
export async function fetchTransferDetail(requestOptions) {
|
||
const result = await Promise.all([
|
||
fetchHomeBalanceData(requestOptions),
|
||
fetchTransferFeeData(requestOptions),
|
||
]);
|
||
const balances = normalizeBalances(result[0]);
|
||
const feePercent = toNumber(result[1] && result[1].r) || 10;
|
||
|
||
return {
|
||
balances: {
|
||
points: toFixedNumber(balances.points, 0),
|
||
power: toFixedNumber(balances.power, 0),
|
||
},
|
||
feePercent: feePercent,
|
||
tips: buildTransferTips(feePercent),
|
||
};
|
||
}
|
||
|
||
export async function searchTransferUser(uid, requestOptions) {
|
||
const keyword = String(uid || "").trim();
|
||
if (!keyword) {
|
||
throw createError("请输入被赠人ID");
|
||
}
|
||
|
||
const data = await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.transferUser,
|
||
method: "POST",
|
||
data: {
|
||
uid: keyword,
|
||
},
|
||
}, requestOptions),
|
||
"查询好友失败",
|
||
);
|
||
|
||
return normalizeTransferTarget(data, keyword);
|
||
}
|
||
|
||
export async function submitAssetTransfer(payload, requestOptions) {
|
||
const transferType = payload && payload.type === "power" ? "power" : "points";
|
||
const targetId = String(payload && payload.targetId ? payload.targetId : "").trim();
|
||
const amount = toNumber(payload && payload.amount);
|
||
|
||
if (!targetId) {
|
||
throw createError("请选择被赠送人");
|
||
}
|
||
|
||
if (!amount) {
|
||
throw createError("请输入转赠数量");
|
||
}
|
||
|
||
const result = await Promise.all([
|
||
fetchTransferFeeData(requestOptions),
|
||
fetchPayload(
|
||
createRequestOptions({
|
||
url:
|
||
transferType === "power"
|
||
? serviceConfig.ENDPOINTS.transferPowerSubmit
|
||
: serviceConfig.ENDPOINTS.transferPointsSubmit,
|
||
method: "POST",
|
||
data: {
|
||
uid: targetId,
|
||
number: String(amount),
|
||
},
|
||
}, requestOptions),
|
||
"转赠失败",
|
||
),
|
||
]);
|
||
const feePercent = toNumber(result[0] && result[0].r) || 10;
|
||
const fee = (amount * feePercent) / 100;
|
||
const received = amount - fee;
|
||
|
||
return {
|
||
success: true,
|
||
fee: toFixedNumber(fee, 2),
|
||
received: toFixedNumber(received, 2),
|
||
};
|
||
}
|
||
|
||
export async function fetchPowerExchangeDetail(requestOptions) {
|
||
const result = await Promise.all([
|
||
fetchPriceData(requestOptions),
|
||
fetchHomeBalanceData(requestOptions),
|
||
fetchPowerExchangeMuitData(requestOptions),
|
||
]);
|
||
const ticker = normalizeTicker(result[0]);
|
||
const balances = normalizeBalances(result[1]);
|
||
const exchangeMultiplier = resolveRedeemPowerRate(result[2]) || 1;
|
||
return {
|
||
ticker: ticker,
|
||
exchangeMultiplier: exchangeMultiplier,
|
||
balances: {
|
||
coupon: toFixedNumber(balances.coupon, 2),
|
||
voucher: toFixedNumber(balances.voucher, 2),
|
||
power: toFixedNumber(balances.power, 0),
|
||
},
|
||
tips: [
|
||
"算力 = 抵用券或消费券 × BMT实时价格 × 兑换倍率;",
|
||
"抵用券和消费券总数小于100券的不可兑换;",
|
||
"算力用于兑换BMT使用。",
|
||
],
|
||
};
|
||
}
|
||
|
||
export async function submitAssetPowerExchange(payload, requestOptions) {
|
||
const mode = payload && payload.mode === "coupon" ? "coupon" : "voucher";
|
||
const amount = toNumber(payload && payload.amount);
|
||
|
||
if (!amount) {
|
||
throw createError("请输入兑换数量");
|
||
}
|
||
|
||
await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.powerExchangeSubmit,
|
||
method: "POST",
|
||
data: {
|
||
type: mode === "coupon" ? 1 : 0,
|
||
number: String(amount),
|
||
},
|
||
}, requestOptions),
|
||
"算力兑换失败",
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
};
|
||
}
|
||
|
||
|
||
// BMT兑换
|
||
export async function fetchBmtExchangeDetail(requestOptions) {
|
||
const result = await Promise.all([
|
||
fetchHomeBalanceData(requestOptions),
|
||
fetchBmtPowerRateData(requestOptions),
|
||
]);
|
||
|
||
const ticker = getHomeTickerCache();
|
||
const balances = normalizeBalances(result[0]);
|
||
const powerRate = resolveRedeemPowerRate(result[1]);
|
||
|
||
return {
|
||
ticker: ticker,
|
||
powerRate: powerRate,
|
||
balances: {
|
||
points: balances.points,
|
||
power: balances.power,
|
||
bmt: balances.bmt,
|
||
voucher: balances.voucher,
|
||
coupon: balances.coupon,
|
||
},
|
||
tips: [
|
||
"预估 BMT数= 输入可用积分数÷BMT 实时价格。",
|
||
`兑换会同步消耗算力, 1 点可用积分,需同步消耗${powerRate}点算力。`,
|
||
"仅支持 100 的正整数倍, 不足 100的可用积分无法发起兑换。",
|
||
"凌晨00:00至02:00为积分维护时段,期间暂停兑换服务,请避开该时间段操作。",
|
||
],
|
||
};
|
||
}
|
||
|
||
export async function submitAssetBmtExchange(payload, requestOptions) {
|
||
const amount = toNumber(payload && payload.amount);
|
||
|
||
if (!amount) {
|
||
throw createError("请输入积分数量");
|
||
}
|
||
|
||
await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.bmtExchangeSubmit,
|
||
method: "POST",
|
||
data: {
|
||
number: String(amount),
|
||
},
|
||
}, requestOptions),
|
||
"BMT兑换失败",
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
};
|
||
}
|
||
|
||
export async function fetchWithdrawDetail(requestOptions) {
|
||
const result = await Promise.all([
|
||
fetchPriceData(requestOptions),
|
||
fetchHomeBalanceData(requestOptions),
|
||
fetchWalletAddressData(requestOptions),
|
||
fetchWithdrawRateData(requestOptions),
|
||
]);
|
||
const ticker = normalizeTicker(result[0]);
|
||
const balances = normalizeBalances(result[1]);
|
||
const walletPayload = buildWalletPayload(result[2] && result[2].address);
|
||
const withdrawRate = resolvePercentRate(result[3]);
|
||
|
||
setHomeTickerCache(ticker);
|
||
|
||
return {
|
||
ticker: ticker,
|
||
withdrawableBmt: toFixedNumber(balances.withdrawableBmt, 2),
|
||
withdrawRate: withdrawRate,
|
||
balances: {
|
||
voucher: balances.voucher,
|
||
coupon: balances.coupon,
|
||
},
|
||
wallets: walletPayload.wallets,
|
||
defaultWallet: walletPayload.wallets[0] || null,
|
||
};
|
||
}
|
||
|
||
export async function submitAssetWithdraw(payload, requestOptions) {
|
||
const address = String(
|
||
payload && payload.address ? payload.address : "",
|
||
).trim();
|
||
const amount = toNumber(payload && payload.amount);
|
||
|
||
if (!address) {
|
||
throw createError("请先添加钱包地址");
|
||
}
|
||
|
||
if (!amount) {
|
||
throw createError("请输入提取数量");
|
||
}
|
||
|
||
await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.withdrawSubmit,
|
||
method: "POST",
|
||
data: {
|
||
address: address,
|
||
number: String(amount),
|
||
},
|
||
}, requestOptions),
|
||
"提取失败",
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
};
|
||
}
|
||
|
||
export async function fetchLedgerDetail(
|
||
type,
|
||
paginationOrRequestOptions,
|
||
requestOptions,
|
||
) {
|
||
const params = resolvePagingArguments(
|
||
paginationOrRequestOptions,
|
||
requestOptions,
|
||
);
|
||
const pagination = params.pagination;
|
||
const mergedRequestOptions = params.requestOptions;
|
||
|
||
if (type === "transfer") {
|
||
const data = await fetchTransferLedgerData(pagination, mergedRequestOptions);
|
||
return {
|
||
type: type,
|
||
title: "转赠记录",
|
||
subtitle: "可用积分与算力转赠流水",
|
||
records: mapTransferRecords(normalizeListData(data)),
|
||
pagination: buildPaginationMeta(data, pagination),
|
||
};
|
||
}
|
||
|
||
if (type === "points") {
|
||
const data = await fetchWalletFlowListData(0, pagination, mergedRequestOptions);
|
||
return {
|
||
type: type,
|
||
title: "可用积分记录",
|
||
subtitle: "可用积分收支记录",
|
||
records: mapWalletFlowRecords(data, {
|
||
key: "points",
|
||
unit: "可用积分",
|
||
title: "可用积分记录",
|
||
subtitle: "可用积分流水",
|
||
}),
|
||
pagination: buildPaginationMeta(data, pagination),
|
||
};
|
||
}
|
||
|
||
if (type === "power-flow") {
|
||
const data = await fetchWalletFlowListData(1, pagination, mergedRequestOptions);
|
||
return {
|
||
type: type,
|
||
title: "算力记录",
|
||
subtitle: "算力收支记录",
|
||
records: mapWalletFlowRecords(data, {
|
||
key: "power-flow",
|
||
unit: "算力",
|
||
title: "算力记录",
|
||
subtitle: "算力流水",
|
||
}),
|
||
pagination: buildPaginationMeta(data, pagination),
|
||
};
|
||
}
|
||
|
||
if (type === "power") {
|
||
const data = await fetchRedeemRecordListData(0, pagination, mergedRequestOptions);
|
||
return {
|
||
type: type,
|
||
title: "兑换记录",
|
||
subtitle: "算力兑换记录",
|
||
records: mapRedeemRecords(data, {
|
||
key: "power",
|
||
type: 0,
|
||
unit: "算力",
|
||
title: "兑换算力",
|
||
subtitle: "算力兑换",
|
||
amountKeys: [
|
||
"num",
|
||
"number",
|
||
"power",
|
||
"power_num",
|
||
"c_power",
|
||
"redeem_power",
|
||
],
|
||
}),
|
||
pagination: buildPaginationMeta(data, pagination),
|
||
};
|
||
}
|
||
|
||
if (type === "bmt") {
|
||
const data = await fetchRedeemRecordListData(1, pagination, mergedRequestOptions);
|
||
return {
|
||
type: type,
|
||
title: "兑换记录",
|
||
subtitle: "BMT兑换记录",
|
||
records: mapRedeemRecords(data, {
|
||
key: "bmt",
|
||
type: 1,
|
||
unit: "BMT",
|
||
title: "兑换BMT",
|
||
subtitle: "BMT兑换",
|
||
amountKeys: ["bmt", "bmt_num", "redeem_bmt", "num", "number"],
|
||
}),
|
||
pagination: buildPaginationMeta(data, pagination),
|
||
};
|
||
}
|
||
|
||
if (type === "withdraw") {
|
||
const data = await fetchWalletFlowListData(2, pagination, mergedRequestOptions);
|
||
return {
|
||
type: type,
|
||
title: "提取记录",
|
||
subtitle: "BMT收支记录",
|
||
records: mapWalletFlowRecords(data, {
|
||
key: "withdraw",
|
||
unit: "BMT",
|
||
title: "BMT记录",
|
||
subtitle: "BMT流水",
|
||
}),
|
||
pagination: buildPaginationMeta(data, pagination),
|
||
};
|
||
}
|
||
|
||
const homeData = await fetchHomeBalanceData(mergedRequestOptions);
|
||
const balances = normalizeBalances(homeData);
|
||
return Object.assign({}, buildDefaultLedger(type, balances), {
|
||
pagination: {
|
||
page: pagination.page,
|
||
pageSize: pagination.pageSize,
|
||
total: 0,
|
||
lastPage: 0,
|
||
hasMore: false,
|
||
},
|
||
});
|
||
}
|
||
|
||
export async function fetchWalletDetail(requestOptions) {
|
||
const data = await fetchWalletAddressData(requestOptions);
|
||
return buildWalletPayload(data && data.address);
|
||
}
|
||
|
||
export async function saveAssetWallet(payload, requestOptions) {
|
||
const address = String(payload && payload.address ? payload.address : "").trim();
|
||
|
||
if (!address) {
|
||
throw createError("请输入钱包地址");
|
||
}
|
||
|
||
await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.walletSave,
|
||
method: "POST",
|
||
data: {
|
||
address: address,
|
||
},
|
||
}, requestOptions),
|
||
"保存失败",
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
};
|
||
}
|
||
|
||
export async function deleteAssetWallet(id, requestOptions) {
|
||
await fetchPayload(
|
||
createRequestOptions({
|
||
url: serviceConfig.ENDPOINTS.walletSave,
|
||
method: "POST",
|
||
data: {
|
||
address: "",
|
||
},
|
||
}, requestOptions),
|
||
"删除失败",
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
};
|
||
}
|