130 lines
6.1 KiB
JavaScript
130 lines
6.1 KiB
JavaScript
import { createRequestError } from './request-client.js'
|
|
import { normalizeOptionalResponseText } from './response-normalizers.js'
|
|
import {
|
|
assertPlainPayload,
|
|
normalizeOssIdString,
|
|
normalizeOptionalText,
|
|
normalizeResourcePathId
|
|
} from './request-normalizers.js'
|
|
|
|
export const EARNING_WITHDRAWAL_STATUS_LABELS = Object.freeze({
|
|
PENDING: '等待审核',
|
|
APPROVED: '审核通过',
|
|
PAYING: '正在转账',
|
|
PAID: '已到账',
|
|
REJECTED: '未通过',
|
|
FAILED: '转账失败',
|
|
CANCELLED: '已取消'
|
|
})
|
|
|
|
const earningWithdrawalStatuses = new Set(
|
|
Object.keys(EARNING_WITHDRAWAL_STATUS_LABELS)
|
|
)
|
|
|
|
const normalizeEarningText = (value, field) =>
|
|
normalizeOptionalResponseText(value, field, '收益响应', 'EARNING_RESPONSE_INVALID')
|
|
|
|
const normalizeMoneyText = (value, label, { nullable = false, allowNegative = false } = {}) => {
|
|
if (nullable && (value === undefined || value === null || value === '')) return null
|
|
if (typeof value !== 'string') throw createRequestError(`${label}必须是金额字符串`, 'EARNING_RESPONSE_INVALID')
|
|
const text = value.trim()
|
|
const pattern = allowNegative ? /^-?(?:0|[1-9]\d{0,16})(?:\.\d{1,2})?$/ : /^(?:0|[1-9]\d{0,16})(?:\.\d{1,2})?$/
|
|
if (!pattern.test(text)) throw createRequestError(`${label}格式无效`, 'EARNING_RESPONSE_INVALID')
|
|
return Number(text).toFixed(2)
|
|
}
|
|
|
|
export const normalizeEarningSummary = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('收益汇总响应无效', 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
if (value.currency !== 'CNY' || typeof value.withdrawalEnabled !== 'boolean') {
|
|
throw createRequestError('收益汇总缺少稳定字段', 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
const rewardRateBps = value.rewardRateBps === undefined || value.rewardRateBps === null
|
|
? null
|
|
: value.rewardRateBps
|
|
if (rewardRateBps !== null && (!Number.isSafeInteger(rewardRateBps) || rewardRateBps < 0 || rewardRateBps > 10000)) {
|
|
throw createRequestError('收益比例无效', 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
availableAmount: normalizeMoneyText(value.availableAmount, '可用收益'),
|
|
frozenAmount: normalizeMoneyText(value.frozenAmount, '冻结收益'),
|
|
minimumWithdrawal: normalizeMoneyText(value.minimumWithdrawal, '最低提现金额', { nullable: true }),
|
|
rewardRateBps,
|
|
currency: value.currency,
|
|
withdrawalEnabled: value.withdrawalEnabled
|
|
}
|
|
}
|
|
|
|
export const normalizeEarningLedger = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('收益明细响应无效', 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
if (value.currency !== 'CNY') throw createRequestError('收益币种无效', 'EARNING_RESPONSE_INVALID')
|
|
return {
|
|
ledgerId: normalizeResourcePathId(value.ledgerId, '收益明细标识'),
|
|
entryType: normalizeEarningText(value.entryType, 'entryType'),
|
|
availableDelta: normalizeMoneyText(value.availableDelta, '可用收益变动', { allowNegative: true }),
|
|
frozenDelta: normalizeMoneyText(value.frozenDelta, '冻结收益变动', { allowNegative: true }),
|
|
availableAfter: normalizeMoneyText(value.availableAfter, '变动后可用收益'),
|
|
frozenAfter: normalizeMoneyText(value.frozenAfter, '变动后冻结收益'),
|
|
currency: value.currency,
|
|
remark: normalizeEarningText(value.remark, 'remark'),
|
|
createTime: normalizeEarningText(value.createTime, 'createTime')
|
|
}
|
|
}
|
|
|
|
export const normalizeEarningWithdrawal = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('提现记录响应无效', 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
const withdrawalStatus = normalizeEarningText(value.withdrawalStatus, 'withdrawalStatus')
|
|
if (!earningWithdrawalStatuses.has(withdrawalStatus) || value.currency !== 'CNY') {
|
|
throw createRequestError('提现记录状态或币种无效', 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
withdrawalId: normalizeResourcePathId(value.withdrawalId, '提现记录标识'),
|
|
withdrawalNo: normalizeEarningText(value.withdrawalNo, 'withdrawalNo'),
|
|
amount: normalizeMoneyText(value.amount, '提现金额'),
|
|
currency: value.currency,
|
|
payoutAccountName: normalizeEarningText(value.payoutAccountName, 'payoutAccountName'),
|
|
withdrawalStatus,
|
|
auditRemark: normalizeEarningText(value.auditRemark, 'auditRemark'),
|
|
payoutReference: normalizeEarningText(value.payoutReference, 'payoutReference'),
|
|
paidAt: normalizeEarningText(value.paidAt, 'paidAt'),
|
|
failureReason: normalizeEarningText(value.failureReason, 'failureReason'),
|
|
createTime: normalizeEarningText(value.createTime, 'createTime')
|
|
}
|
|
}
|
|
|
|
export const normalizeEarningPage = (value, rowNormalizer, label) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value) || !Array.isArray(value.rows) || !Number.isSafeInteger(value.total) || value.total < 0) {
|
|
throw createRequestError(`${label}分页响应无效`, 'EARNING_RESPONSE_INVALID')
|
|
}
|
|
return { rows: value.rows.map(rowNormalizer), total: value.total }
|
|
}
|
|
|
|
export const normalizeEarningPageQuery = ({ pageNum = 1, pageSize = 20 } = {}) => {
|
|
if (!Number.isSafeInteger(pageNum) || pageNum < 1 || !Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 100) {
|
|
throw new TypeError('收益分页参数无效')
|
|
}
|
|
return { pageNum, pageSize }
|
|
}
|
|
|
|
export const normalizeEarningWithdrawalPayload = (payload) => {
|
|
assertPlainPayload(payload, new Set(['requestId', 'amount', 'payoutQrOssId', 'payoutAccountName']), '提现申请')
|
|
const requestId = normalizeOptionalText(payload.requestId, 'requestId')
|
|
const payoutAccountName = normalizeOptionalText(payload.payoutAccountName, 'payoutAccountName')
|
|
if (!requestId || requestId.length > 64) throw new TypeError('提现请求号无效')
|
|
if (!payoutAccountName || payoutAccountName.length > 64) throw new TypeError('请填写不超过64个字符的收款人姓名')
|
|
const amount = normalizeMoneyText(payload.amount, '提现金额')
|
|
if (Number(amount) <= 0) throw new TypeError('提现金额必须大于0元')
|
|
const payoutQrOssId = normalizeOssIdString(payload.payoutQrOssId, 'payoutQrOssId')
|
|
return {
|
|
requestId,
|
|
amount: Number(amount),
|
|
payoutQrOssId,
|
|
payoutAccountName
|
|
}
|
|
}
|