Files
36-character-flower/src/api/finance-api.ts
JiaJun 8721a47a61 feat(finance): 添加财务记录状态显示功能
- 在 API 类型定义中添加订单状态字段及其枚举值
- 更新桌面端财务记录表格界面,增加状态列并调整列宽布局
- 实现移动端财务记录表格的状态列显示和样式适配
- 添加财务记录状态的颜色标识和标签显示逻辑
- 在多语言文件中添加状态相关的翻译内容
- 修复充值组件中的金额字段引用错误
- 重构提现功能中的汇率计算逻辑,提高准确性
2026-07-07 16:29:05 +08:00

456 lines
13 KiB
TypeScript

import {
API_SUCCESS_CODE,
DEFAULT_LIST_PAGE_SIZE,
FINANCE_API_ENDPOINTS,
} from '@/constants'
import { api } from '@/lib/api/api-client'
import { ApiError } from '@/lib/api/api-error'
import type {
ApiResponse,
DepositCreateRequestDto,
DepositCreateResponseDto,
DepositTierItem,
DepositTierItemDto,
DepositWithdrawConfig,
DepositWithdrawConfigDto,
FinanceCurrencyConfigDto,
FinanceDepositConfigDto,
FinanceDepositFieldsDto,
FinanceDepositMethodDto,
FinanceOrderItemDto,
FinanceOrderList,
FinanceOrderListDto,
FinancePayChannelDto,
FinanceRateConfigDto,
FinanceWithdrawBankDto,
FinanceWithdrawConfigDto,
WalletRecordItemDto,
WalletRecordList,
WalletRecordListDto,
WalletRecordType,
WithdrawCreateRequestDto,
WithdrawCreateResponseDto,
} from '@/type'
function unwrapFinanceEnvelope<T>(
response: ApiResponse<T>,
fallbackMessage = 'Finance request failed',
) {
if (response.code === API_SUCCESS_CODE) {
return response.data
}
const responseMessage =
typeof response.message === 'string' && response.message.length > 0
? response.message
: typeof response.msg === 'string' && response.msg.length > 0
? response.msg
: fallbackMessage
throw new ApiError({
data: response,
message: responseMessage,
})
}
function toFiniteNumber(value: string | number | null | undefined) {
const numericValue = Number(value)
return Number.isFinite(numericValue) ? numericValue : 0
}
function normalizeCurrency(dto: FinanceCurrencyConfigDto) {
return {
code: dto.code,
depositCoinsPerFiat: dto.deposit_coins_per_fiat,
depositCoinsPerFiatValue: toFiniteNumber(dto.deposit_coins_per_fiat),
label: dto.label,
withdrawCoinsPerFiat: dto.withdraw_coins_per_fiat,
withdrawCoinsPerFiatValue: toFiniteNumber(dto.withdraw_coins_per_fiat),
}
}
function normalizeRate(dto: FinanceRateConfigDto) {
return {
currency: dto.currency,
diamondsPerFiatUnit: dto.diamonds_per_fiat_unit,
diamondsPerFiatUnitValue: toFiniteNumber(dto.diamonds_per_fiat_unit),
}
}
function normalizePayChannel(dto: FinancePayChannelDto) {
return {
code: dto.code,
currencyCodes: Array.isArray(dto.currency_codes) ? dto.currency_codes : [],
depositBanks: (dto.deposit_banks ?? []).map(normalizeWithdrawBank),
name: dto.name,
sort: Number.isFinite(dto.sort) ? dto.sort : 0,
status: typeof dto.status === 'number' ? dto.status : 1,
tierIds: Array.isArray(dto.tier_ids) ? dto.tier_ids : [],
}
}
function normalizeWithdrawBank(dto: FinanceWithdrawBankDto, index: number) {
const code = dto.code ?? dto.id ?? dto.name ?? `bank-${index + 1}`
const label = dto.label ?? dto.name ?? code
return {
code,
currencyCode: dto.currency_code ?? null,
gatewayCode: dto.gateway_code ?? null,
label,
sort:
typeof dto.sort === 'number' && Number.isFinite(dto.sort)
? dto.sort
: index,
status: typeof dto.status === 'number' ? dto.status : 1,
}
}
function normalizeDepositMethod(dto: FinanceDepositMethodDto) {
return {
code: dto.code,
currencyCode: dto.currency_code,
gatewayCode: dto.gateway_code,
name: dto.name,
requiresBank: dto.requires_bank,
requiresBankAccount: dto.requires_bank_account,
requiresDepositorName: dto.requires_depositor_name,
requiresFromAddress: dto.requires_from_address,
}
}
function normalizeDepositFields(dto: FinanceDepositFieldsDto | undefined = {}) {
return {
depositBankAccountParam:
dto.deposit_bank_account_param ?? 'deposit_bank_account',
depositBankParam: dto.deposit_bank_param ?? 'deposit_bank',
depositFromAddressParam:
dto.deposit_from_address_param ?? 'deposit_from_address',
depositNameServerSide: dto.deposit_name_server_side ?? false,
paymentTypeParam: dto.payment_type_param ?? 'payment_type',
requireChannelCode: dto.require_channel_code ?? true,
requireIdempotencyKey: dto.require_idempotency_key ?? true,
requirePaymentType: dto.require_payment_type ?? false,
}
}
function normalizeDepositConfig(
dto: FinanceDepositConfigDto | undefined,
fallbackPayChannels: FinancePayChannelDto[] = [],
) {
const payChannels =
dto?.pay_channels && dto.pay_channels.length > 0
? dto.pay_channels
: fallbackPayChannels
return {
banks: (dto?.banks ?? []).map(normalizeWithdrawBank),
defaultChannelCode: dto?.default_channel_code ?? '',
defaultChannelByCurrency: dto?.default_channel_by_currency ?? {},
fields: normalizeDepositFields(dto?.fields),
methods: (dto?.methods ?? []).map(normalizeDepositMethod),
payChannels: payChannels.map(normalizePayChannel),
}
}
function normalizeWithdrawFields(dto: FinanceWithdrawConfigDto['fields'] = {}) {
return {
receiveTypeBankOnly: dto.receive_type_bank_only ?? true,
requireBankBranch: dto.require_bank_branch ?? false,
requireBankCode: dto.require_bank_code ?? true,
requireChannelCode: dto.require_channel_code ?? true,
requireReceiveAccount: dto.require_receive_account ?? true,
requireReceiverEmail: dto.require_receiver_email ?? true,
requireReceiverMobile: dto.require_receiver_mobile ?? true,
requireReceiverName: dto.require_receiver_name ?? true,
}
}
function normalizeWithdrawConfig(dto: FinanceWithdrawConfigDto) {
return {
banks: (dto.banks ?? []).map(normalizeWithdrawBank),
feeNote: dto.fee_note,
fields: normalizeWithdrawFields(dto.fields),
minBank: dto.min_bank,
minEwallet: dto.min_ewallet,
payChannels: (dto.pay_channels ?? []).map(normalizePayChannel),
processingNote: dto.processing_note,
rateHint: dto.rate_hint,
rateMode: dto.rate_mode,
reviewThresholdCoin: dto.review_threshold_coin ?? '0',
}
}
function normalizeDepositWithdrawConfig(
dto: DepositWithdrawConfigDto,
): DepositWithdrawConfig {
return {
currencies: (dto.currencies ?? []).map(normalizeCurrency),
defaultDepositChannelCode: dto.default_deposit_channel_code ?? '',
deposit: normalizeDepositConfig(dto.deposit, dto.pay_channels ?? []),
payChannels: (dto.pay_channels ?? []).map(normalizePayChannel),
platformCoinLabel: dto.platform_coin_label,
rates: (dto.rates ?? []).map(normalizeRate),
withdraw: normalizeWithdrawConfig(dto.withdraw),
}
}
function normalizeDepositTierItem(
dto: DepositTierItemDto,
index: number,
): DepositTierItem {
const id = String(dto.id ?? dto.tier_id ?? `tier-${index + 1}`)
const amount = toFiniteNumber(dto.amount)
const bonusAmount = toFiniteNumber(dto.bonus_amount ?? dto.bonus_coins)
const payAmount = toFiniteNumber(dto.pay_amount ?? dto.amount)
const totalAmount = toFiniteNumber(
dto.total_amount ?? dto.coins ?? amount + bonusAmount,
)
const coins = totalAmount
const currency =
typeof dto.currency === 'string' && dto.currency.length > 0
? dto.currency
: null
const payChannelCode =
typeof dto.pay_channel_code === 'string' && dto.pay_channel_code.length > 0
? dto.pay_channel_code
: null
const channelCode =
typeof dto.channel_code === 'string' && dto.channel_code.length > 0
? dto.channel_code
: (payChannelCode ?? null)
const sort = toFiniteNumber(dto.sort ?? dto.pay_amount ?? dto.amount)
const status = toFiniteNumber(dto.status ?? 1)
const channels = (dto.channels ?? [])
.map((channel, channelIndex) => ({
code: channel.code ?? `channel-${channelIndex + 1}`,
name: channel.name ?? channel.code ?? '--',
sort: toFiniteNumber(channel.sort ?? channelIndex),
}))
.sort((left, right) => left.sort - right.sort)
return {
amount,
bonusAmount,
channelCode,
channels,
coins,
currency,
desc: dto.desc ?? '',
id,
name:
typeof dto.name === 'string' && dto.name.length > 0
? dto.name
: `${amount}`,
payAmount,
payChannelCode,
sort,
status,
tierKey:
typeof dto.tier_key === 'string' && dto.tier_key.length > 0
? dto.tier_key
: null,
totalAmount,
title:
typeof dto.title === 'string' && dto.title.length > 0
? dto.title
: typeof dto.name === 'string' && dto.name.length > 0
? dto.name
: `${amount}`,
}
}
function normalizeFinanceOrderItem(dto: FinanceOrderItemDto) {
return {
amount: String(dto.amount ?? ''),
bonusAmount: String(dto.bonus_amount ?? ''),
orderNo: dto.order_no,
status: String(dto.status ?? ''),
}
}
function normalizeFinanceOrderList(dto: FinanceOrderListDto): FinanceOrderList {
return {
list: (dto.list ?? []).map(normalizeFinanceOrderItem),
pagination: {
page: dto.pagination?.page ?? 1,
page_size: dto.pagination?.page_size ?? DEFAULT_LIST_PAGE_SIZE,
total: dto.pagination?.total ?? 0,
},
}
}
function stringifyNullableValue(value: unknown) {
return value === null || value === undefined ? '' : String(value)
}
function normalizeWalletRecordItem(dto: WalletRecordItemDto, index: number) {
const createdAt = dto.created_at ?? dto.create_time ?? dto.time ?? null
const id =
dto.id ??
dto.record_id ??
dto.wallet_record_id ??
dto.order_no ??
`${createdAt ?? 'wallet-record'}-${index + 1}`
return {
amount: stringifyNullableValue(
dto.amount ?? dto.change_amount ?? dto.coin ?? '',
),
balanceAfter: stringifyNullableValue(
dto.balance_after ?? dto.after_balance ?? dto.balance ?? '',
),
balanceBefore: stringifyNullableValue(
dto.balance_before ?? dto.before_balance ?? '',
),
createdAt,
id: String(id),
remark: stringifyNullableValue(dto.remark ?? dto.description ?? dto.memo),
type: stringifyNullableValue(
dto.type ?? dto.change_type ?? dto.biz_type ?? dto.scene,
),
}
}
function normalizeWalletRecordList(dto: WalletRecordListDto): WalletRecordList {
return {
list: (dto.list ?? []).map(normalizeWalletRecordItem),
pagination: {
page: dto.pagination?.page ?? dto.page ?? 1,
page_size:
dto.pagination?.page_size ?? dto.page_size ?? DEFAULT_LIST_PAGE_SIZE,
total: dto.pagination?.total ?? dto.total ?? 0,
},
}
}
export async function getDepositWithdrawConfig() {
const response = await api.post<DepositWithdrawConfigDto>(
FINANCE_API_ENDPOINTS.depositWithdrawConfig,
)
const dto = unwrapFinanceEnvelope(
response as ApiResponse<DepositWithdrawConfigDto>,
'Failed to load deposit and withdrawal config',
)
return normalizeDepositWithdrawConfig(dto)
}
export async function getDepositTierList() {
const response = await api.post<
| DepositTierItemDto[]
| { items?: DepositTierItemDto[]; list?: DepositTierItemDto[] },
undefined
>(FINANCE_API_ENDPOINTS.depositTierList)
const dto = unwrapFinanceEnvelope(
response as ApiResponse<
| DepositTierItemDto[]
| { items?: DepositTierItemDto[]; list?: DepositTierItemDto[] }
>,
'Failed to load deposit tier list',
)
const tierItems = Array.isArray(dto) ? dto : (dto?.list ?? dto?.items ?? [])
return tierItems
.map(normalizeDepositTierItem)
.filter((item) => item.status === 1)
.sort((left, right) => left.sort - right.sort)
}
export async function createDeposit(payload: DepositCreateRequestDto) {
const response = await api.post<
DepositCreateResponseDto,
DepositCreateRequestDto
>(FINANCE_API_ENDPOINTS.depositCreate, {
json: payload,
})
const dto = unwrapFinanceEnvelope(
response as ApiResponse<DepositCreateResponseDto>,
'Failed to create deposit',
)
return dto
}
export async function getDepositOrderList(params?: {
page?: number
pageSize?: number
}) {
const response = await api.get<FinanceOrderListDto>(
FINANCE_API_ENDPOINTS.depositList,
{
searchParams: {
page: String(params?.page ?? 1),
page_size: String(params?.pageSize ?? DEFAULT_LIST_PAGE_SIZE),
},
},
)
const dto = unwrapFinanceEnvelope(
response as ApiResponse<FinanceOrderListDto>,
'Failed to load deposit order list',
)
return normalizeFinanceOrderList(dto)
}
export async function getWithdrawOrderList(params?: {
page?: number
pageSize?: number
}) {
const response = await api.get<FinanceOrderListDto>(
FINANCE_API_ENDPOINTS.withdrawList,
{
searchParams: {
page: String(params?.page ?? 1),
page_size: String(params?.pageSize ?? DEFAULT_LIST_PAGE_SIZE),
},
},
)
const dto = unwrapFinanceEnvelope(
response as ApiResponse<FinanceOrderListDto>,
'Failed to load withdraw order list',
)
return normalizeFinanceOrderList(dto)
}
export async function getWalletRecordList(params?: {
page?: number
pageSize?: number
type?: WalletRecordType
}) {
const response = await api.get<WalletRecordListDto>(
FINANCE_API_ENDPOINTS.walletRecordList,
{
searchParams: {
page: String(params?.page ?? 1),
page_size: String(params?.pageSize ?? DEFAULT_LIST_PAGE_SIZE),
type: params?.type ?? 'payout',
},
},
)
const dto = unwrapFinanceEnvelope(
response as ApiResponse<WalletRecordListDto>,
'Failed to load wallet record list',
)
return normalizeWalletRecordList(dto)
}
export async function createWithdraw(payload: WithdrawCreateRequestDto) {
const response = await api.post<
WithdrawCreateResponseDto,
WithdrawCreateRequestDto
>(FINANCE_API_ENDPOINTS.withdrawCreate, {
json: payload,
})
const dto = unwrapFinanceEnvelope(
response as ApiResponse<WithdrawCreateResponseDto>,
'Failed to create withdraw',
)
return dto
}