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 } }