diff --git a/api/assets.js b/api/assets.js index d447c09..355304b 100644 --- a/api/assets.js +++ b/api/assets.js @@ -305,26 +305,289 @@ function buildTransferTips(feePercent, bmtrFeePercent) { }; } -function buildWalletList(address) { - const normalizedAddress = String(address || "").trim(); +function resolveWalletModifyCount(source) { + const target = source && typeof source === "object" ? source : {}; + let rawCount = pickFirstValue(target, [ + "edit_times", + "editTimes", + "remaining_modify_count", + "remainingModifyCount", + "modify_num", + "modifyNum", + "modify_count", + "modifyCount", + "change_num", + "changeNum", + "change_count", + "changeCount", + "edit_num", + "editNum", + "edit_count", + "editCount", + "update_num", + "updateNum", + "update_count", + "updateCount", + "surplus_num", + "surplusNum", + "remain_num", + "remainNum", + "remaining_times", + "remainingTimes", + "modify_times", + "modifyTimes", + "change_times", + "changeTimes", + "address_modify_num", + "addressModifyNum", + "address_change_num", + "addressChangeNum", + "wallet_modify_num", + "walletModifyNum", + "remaining_count", + "remainingCount", + ]); - if (!normalizedAddress) { + if (rawCount === "") { + const countKey = Object.keys(target).find(function (key) { + const normalizedKey = String(key || "").toLowerCase(); + return ( + /(modify|change|edit|update|alter|replace).*(num|number|count|times|limit|remain|surplus)/.test( + normalizedKey, + ) || + /(remain|surplus).*(modify|change|edit|update|alter|replace)/.test( + normalizedKey, + ) + ); + }); + rawCount = countKey ? target[countKey] : ""; + } + + if (rawCount === "") { + const canModify = pickFirstValue(target, ["can_modify", "canModify"]); + if (canModify === "") { + return null; + } + return canModify === true || Number(canModify) > 0 ? 1 : 0; + } + + const count = Number(rawCount); + return Number.isFinite(count) ? Math.max(0, Math.floor(count)) : null; +} + +function normalizeWalletSource(data) { + if (Array.isArray(data)) { + return data; + } + + const source = data && typeof data === "object" ? data : {}; + const hasExplicitWalletFields = + Object.prototype.hasOwnProperty.call(source, "address") || + Object.prototype.hasOwnProperty.call(source, "more_address") || + Object.prototype.hasOwnProperty.call(source, "moreAddress"); + + if (hasExplicitWalletFields) { + const walletSource = []; + const defaultAddress = String(source.address || "").trim(); + const moreAddress = pickFirstValue(source, ["more_address", "moreAddress"]); + + if (defaultAddress) { + walletSource.push({ + address: defaultAddress, + is_default: 1, + type: 0, + name: "默认钱包", + }); + } + + normalizeWalletSource(moreAddress).forEach(function (item, index) { + const address = String( + item && typeof item === "object" + ? pickFirstValue(item, [ + "address", + "wallet_address", + "walletAddress", + "value", + ]) + : item || "", + ).trim(); + + if (address && address !== defaultAddress) { + walletSource.push({ + address: address, + is_default: 0, + type: 1, + name: + item && typeof item === "object" + ? pickFirstValue(item, ["name", "wallet_name", "walletName"]) + : "", + }); + } + }); + + return walletSource; + } + + let walletSource = pickFirstValue(source, [ + "wallets", + "wallet_list", + "walletList", + "address_list", + "addressList", + "addresses", + "list", + "address", + ]); + + if (!walletSource && typeof data === "string") { + walletSource = data; + } + + if (Array.isArray(walletSource)) { + return walletSource; + } + + if (walletSource && typeof walletSource === "object") { + return [walletSource]; + } + + const text = String(walletSource || "").trim(); + if (!text) { return []; } - return [ - { - id: "default-wallet", - name: serviceConfig.WALLET_NAME, - address: normalizedAddress, - isDefault: true, - }, - ]; + if (text.charAt(0) === "[") { + try { + const parsed = JSON.parse(text); + if (Array.isArray(parsed)) { + return parsed; + } + } catch (error) { + // Continue with delimiter parsing for legacy responses. + } + } + + return text.split(/[\n,;]+/).filter(function (item) { + return String(item || "").trim(); + }); } -function buildWalletPayload(address) { +function buildWalletList(data) { + const source = data && typeof data === "object" ? data : {}; + const sharedModifyCount = resolveWalletModifyCount(source); + const wallets = normalizeWalletSource(data) + .map(function (item, index) { + const wallet = item && typeof item === "object" ? item : {}; + const address = String( + typeof item === "string" + ? item + : pickFirstValue(wallet, [ + "address", + "wallet_address", + "walletAddress", + "value", + ]), + ).trim(); + + if (!address) { + return null; + } + + const walletModifyCount = resolveWalletModifyCount(wallet); + const remainingModifyCount = + walletModifyCount === null ? sharedModifyCount : walletModifyCount; + + const requestId = pickFirstValue(wallet, ["id", "wallet_id", "walletId"]); + const defaultValue = pickFirstValue(wallet, [ + "is_default", + "isDefault", + "default", + ]); + const isDefault = + defaultValue === true || + defaultValue === 1 || + String(defaultValue).toLowerCase() === "true" || + String(defaultValue) === "1"; + const walletType = Number(pickFirstValue(wallet, ["type"])); + const walletName = pickFirstValue(wallet, [ + "name", + "wallet_name", + "walletName", + ]); + + return { + id: String(requestId || "wallet-" + index), + requestId: requestId === "" ? "" : String(requestId), + name: String(walletName || (isDefault ? serviceConfig.WALLET_NAME : "")), + address: address, + isDefault: isDefault, + type: walletType === 1 ? 1 : 0, + editTimes: remainingModifyCount, + remainingModifyCount: remainingModifyCount, + canModify: + remainingModifyCount !== null && remainingModifyCount === 0, + }; + }) + .filter(Boolean) + .filter(function (wallet, index, list) { + return list.findIndex(function (item) { + return item.address === wallet.address; + }) === index; + }); + + const hasExplicitDefaultField = + Object.prototype.hasOwnProperty.call(source, "address") && + (Object.prototype.hasOwnProperty.call(source, "more_address") || + Object.prototype.hasOwnProperty.call(source, "moreAddress")); + if ( + wallets.length && + !hasExplicitDefaultField && + !wallets.some(function (wallet) { + return wallet.isDefault; + }) + ) { + wallets[0].isDefault = true; + if (!wallets[0].name) { + wallets[0].name = serviceConfig.WALLET_NAME; + } + } + + return wallets; +} + +function buildWalletPayload(data) { + const wallets = buildWalletList(data); + const editTimes = resolveWalletModifyCount(data); + const source = data && typeof data === "object" ? data : {}; + const rawMoreAddressNum = pickFirstValue(source, [ + "more_address_num", + "moreAddressNum", + ]); + const moreWallets = wallets.filter(function (wallet) { + return !wallet.isDefault; + }); + const parsedMoreAddressNum = Number(rawMoreAddressNum); + const moreAddressNum = + rawMoreAddressNum !== "" && Number.isFinite(parsedMoreAddressNum) + ? Math.max(0, Math.floor(parsedMoreAddressNum)) + : moreWallets.length; + return { - wallets: buildWalletList(address), + wallets: wallets, + defaultWallet: + wallets.find(function (wallet) { + return wallet.isDefault; + }) || null, + moreWallets: moreWallets, + moreAddressNum: moreAddressNum, + editTimes: editTimes, + remainingModifyCount: editTimes, + canModify: + editTimes !== null + ? editTimes === 0 + : wallets.some(function (wallet) { + return wallet.canModify; + }), instructions: [ "点击交易所 App 底部“资产”进入钱包页", "搜索或输入大写字母 BMT", @@ -1767,15 +2030,16 @@ async function fetchExtractBankData(requestOptions) { ); } -async function fetchBmtPowerRateData(requestOptions) { +async function fetchBmtRedeemConfigData(requestOptions) { return fetchPayload( createRequestOptions( { - url: serviceConfig.ENDPOINTS.bmtRedeemPowerRate, + url: serviceConfig.ENDPOINTS.bmtRedeemConfig, + method: "POST", }, requestOptions, ), - "兑换比例加载失败", + "兑换配置加载失败", ); } @@ -1919,6 +2183,7 @@ async function fetchWalletAddressData(requestOptions) { createRequestOptions( { url: serviceConfig.ENDPOINTS.walletDetail, + method: "GET", }, requestOptions, ), @@ -2607,28 +2872,36 @@ export async function submitAssetBmtFlashExchange(payload, requestOptions) { export async function fetchBmtExchangeDetail(requestOptions) { const result = await Promise.all([ fetchHomeBalanceData(requestOptions), - fetchBmtPowerRateData(requestOptions), + fetchBmtRedeemConfigData(requestOptions), + fetchUserData(requestOptions), ]); const ticker = getHomeTickerCache(); const balances = normalizeBalances(result[0]); - const powerRate = resolveRedeemPowerRate(result[1]); + const exchangeConfig = { + point: toNumber(result[1] && result[1].point), + cPower: toNumber(result[1] && result[1].c_power), + bmtr: toNumber(result[1] && result[1].bmtr), + redeemNum: toNumber(result[1] && result[1].redeem_num), + }; + const bmtr = pickFirstValue(result[2], ["bmtr"]); return { ticker: ticker, - powerRate: powerRate, + exchangeConfig: exchangeConfig, balances: { points: balances.points, power: balances.power, bmt: balances.bmt, + bmtr: toNumber(bmtr), voucher: balances.voucher, coupon: balances.coupon, }, tips: [ - "预估 BMT数= 输入可用积分数÷BMT 实时价格。", - `兑换会同步消耗算力, 1 点可用积分,需同步消耗${powerRate}点算力。`, - "仅支持 100 的正整数倍, 不足 100的可用积分无法发起兑换。", - "凌晨00:00至02:00为积分维护时段,期间暂停兑换服务,请避开该时间段操作。", + `预估BMT数=输入可用积分数 ÷ ${exchangeConfig.point} × ${exchangeConfig.redeemNum}BMT个数。`, + `兑换会同步消耗算力、BMTR、${exchangeConfig.point}点可用积分,需同步消耗${exchangeConfig.cPower}点算力和${exchangeConfig.bmtr}个BMTR`, + `仅支持100的正整数倍,不足100的可用积分无法发起兑换`, + `凌晨00:00至02:00为积分维护时段,期间暂停兑换服务,请避开该时间段操作。`, ], }; } @@ -2668,8 +2941,12 @@ export async function fetchWithdrawDetail(requestOptions) { ]); const ticker = normalizeTicker(result[0]); const balances = normalizeBalances(result[1]); - const walletPayload = buildWalletPayload(result[2] && result[2].address); + const walletPayload = buildWalletPayload(result[2]); const withdrawRate = resolvePercentRate(result[3]); + const defaultWallet = + walletPayload.wallets.find(function (wallet) { + return wallet.isDefault; + }) || walletPayload.wallets[0] || null; setHomeTickerCache(ticker); @@ -2683,7 +2960,7 @@ export async function fetchWithdrawDetail(requestOptions) { brokerage_price: toFixedNumber(balances.balance, 2), }, wallets: walletPayload.wallets, - defaultWallet: walletPayload.wallets[0] || null, + defaultWallet: defaultWallet, }; } @@ -2997,26 +3274,47 @@ export async function fetchVoucherCenterPage( export async function fetchWalletDetail(requestOptions) { const data = await fetchWalletAddressData(requestOptions); - return buildWalletPayload(data && data.address); + return buildWalletPayload(data); } export async function saveAssetWallet(payload, requestOptions) { - const address = String( + const rawAddress = String( payload && payload.address ? payload.address : "", ).trim(); + const name = String(payload && payload.name ? payload.name : "").trim(); + const type = Number(payload && payload.type) === 1 ? 1 : 0; + const addressList = rawAddress + .split(/[\n,;]+/) + .map(function (address) { + return String(address || "").trim(); + }) + .filter(Boolean) + .filter(function (address, index, list) { + return list.indexOf(address) === index; + }); + const address = type === 1 ? addressList.join(",") : addressList[0] || ""; if (!address) { throw createError("请输入钱包地址"); } + if (type === 0 && addressList.length > 1) { + throw createError("默认钱包只能填写一个地址"); + } + + const requestData = { + address: address, + type: type, + }; + if (name) { + requestData.name = name; + } await fetchPayload( createRequestOptions( { url: serviceConfig.ENDPOINTS.walletSave, method: "POST", - data: { - address: address, - }, + data: requestData, }, requestOptions, ), @@ -3028,26 +3326,6 @@ export async function saveAssetWallet(payload, requestOptions) { }; } -export async function deleteAssetWallet(id, requestOptions) { - await fetchPayload( - createRequestOptions( - { - url: serviceConfig.ENDPOINTS.walletSave, - method: "POST", - data: { - address: "", - }, - }, - requestOptions, - ), - "删除失败", - ); - - return { - success: true, - }; -} - function padTimeUnit(value) { return value < 10 ? "0" + value : String(value); } diff --git a/components/wallet-action-popup.vue b/components/wallet-action-popup.vue index 6e229bb..cf9da4b 100644 --- a/components/wallet-action-popup.vue +++ b/components/wallet-action-popup.vue @@ -75,9 +75,14 @@ export default {