feat: 完成前端业务闭环与后端联调

This commit is contained in:
2026-08-24 23:37:56 +08:00
parent c59e36f933
commit 9ad572907b
175 changed files with 10711 additions and 1740 deletions
+127 -1
View File
@@ -41,6 +41,26 @@ const normalizeVipOrderStatus = (value) => {
}
}
export const VIP_PAYMENT_METHOD = Object.freeze({
WECHAT: 'WECHAT',
ALIPAY: 'ALIPAY',
BALANCE: 'BALANCE'
})
const vipPaymentMethods = new Set(Object.values(VIP_PAYMENT_METHOD))
const vipPaymentMethodLabels = Object.freeze({
[VIP_PAYMENT_METHOD.WECHAT]: '微信支付',
[VIP_PAYMENT_METHOD.ALIPAY]: '支付宝',
[VIP_PAYMENT_METHOD.BALANCE]: '余额支付'
})
export const assertVipPaymentMethod = (value) => {
if (!vipPaymentMethods.has(value)) {
throw new TypeError('VIP 支付方式必须是 WECHAT、ALIPAY 或 BALANCE')
}
return value
}
export const normalizeVipPackages = (value) => {
if (!Array.isArray(value)) throw createRequestError('VIP 套餐响应不是列表', 'VIP_RESPONSE_INVALID')
const packages = value.map((item) => {
@@ -76,8 +96,10 @@ export const normalizeVipOrders = (value) => {
return {
key: `vip-order-${id}`,
id,
orderNo: normalizeVipText(item.orderNo, 'orderNo'),
packageName: normalizeVipText(item.packageName, 'packageName', { required: true }),
amount: normalizeVipAmount(item.payAmount ?? item.orderAmount, 'payAmount', { required: true }),
paymentMethod: normalizeVipText(item.payType, 'payType'),
status,
statusLabel,
paidAt: normalizeVipText(item.payTime, 'payTime'),
@@ -94,8 +116,112 @@ export const normalizeVipCapability = (value) => {
if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.enabled !== 'boolean') {
throw createRequestError('VIP 购买能力响应无效', 'VIP_RESPONSE_INVALID')
}
if (value.paymentMethods !== undefined && !Array.isArray(value.paymentMethods)) {
throw createRequestError('VIP 支付方式能力不是列表', 'VIP_RESPONSE_INVALID')
}
const paymentMethods = (value.paymentMethods || []).map((item) => {
if (!item || typeof item !== 'object' || Array.isArray(item)) {
throw createRequestError('VIP 支付方式能力包含无效条目', 'VIP_RESPONSE_INVALID')
}
const method = normalizeVipText(item.method, 'paymentMethods.method', { required: true })
if (!vipPaymentMethods.has(method) || typeof item.enabled !== 'boolean') {
throw createRequestError('VIP 支付方式能力字段无效', 'VIP_RESPONSE_INVALID')
}
return {
method,
label: vipPaymentMethodLabels[method],
enabled: item.enabled,
disabledReason: normalizeVipText(item.disabledReason, 'paymentMethods.disabledReason')
}
})
if (new Set(paymentMethods.map((item) => item.method)).size !== paymentMethods.length) {
throw createRequestError('VIP 支付方式能力包含重复方式', 'VIP_RESPONSE_INVALID')
}
if (value.enabled && !paymentMethods.some((item) => item.enabled)) {
throw createRequestError('VIP 已开放购买但没有可用支付方式', 'VIP_RESPONSE_INVALID')
}
return {
enabled: value.enabled,
disabledReason: normalizeVipText(value.disabledReason, 'disabledReason')
disabledReason: normalizeVipText(value.disabledReason, 'disabledReason'),
paymentMethods
}
}
const normalizeVipId = (value, field) => {
const id = value === undefined || value === null ? '' : String(value)
if (!/^[1-9]\d*$/.test(id)) {
throw createRequestError(`VIP 响应 ${field} 无效`, 'VIP_RESPONSE_INVALID')
}
return id
}
const vipPaymentStatuses = new Set([
'CREATED',
'PAYING',
'SUCCESS',
'CLOSED',
'REFUNDING',
'REFUNDED'
])
export const normalizeVipPaymentOrder = (value, expectedPaymentMethod) => {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw createRequestError('VIP 支付订单响应无效', 'VIP_RESPONSE_INVALID')
}
const paymentMethod = normalizeVipText(value.paymentMethod, 'paymentMethod', { required: true })
if (!vipPaymentMethods.has(paymentMethod) || paymentMethod !== expectedPaymentMethod) {
throw createRequestError('VIP 支付订单方式与请求不匹配', 'VIP_RESPONSE_INVALID')
}
const transactionId = normalizeVipId(value.transactionId, 'transactionId')
if (paymentMethod === VIP_PAYMENT_METHOD.WECHAT) {
if (value.tradeType !== 'APP') {
throw createRequestError('VIP 微信支付订单 tradeType 无效', 'VIP_RESPONSE_INVALID')
}
return {
paymentMethod,
transactionId,
nativePaymentRequired: true,
orderInfo: {
appid: normalizeVipText(value.appId, 'appId', { required: true }),
partnerid: normalizeVipText(value.partnerId, 'partnerId', { required: true }),
prepayid: normalizeVipText(value.prepayId, 'prepayId', { required: true }),
package: normalizeVipText(value.packageValue, 'packageValue', { required: true }),
noncestr: normalizeVipText(value.nonceStr, 'nonceStr', { required: true }),
timestamp: normalizeVipText(value.timestamp, 'timestamp', { required: true }),
sign: normalizeVipText(value.sign, 'sign', { required: true })
}
}
}
if (paymentMethod === VIP_PAYMENT_METHOD.ALIPAY) {
return {
paymentMethod,
transactionId,
nativePaymentRequired: true,
orderInfo: normalizeVipText(value.orderString, 'orderString', { required: true })
}
}
if (value.completed !== true) {
throw createRequestError('VIP 余额支付没有原子完成', 'VIP_RESPONSE_INVALID')
}
return {
paymentMethod,
transactionId: normalizeVipId(value.transactionId, 'transactionId'),
nativePaymentRequired: false,
orderInfo: null,
paymentStatus: 'SUCCESS'
}
}
export const normalizeVipPaymentStatus = (value) => {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw createRequestError('VIP 支付状态响应无效', 'VIP_RESPONSE_INVALID')
}
const status = normalizeVipText(value.status, 'status', { required: true })
if (!vipPaymentStatuses.has(status)) {
throw createRequestError('VIP 支付状态超出约定范围', 'VIP_RESPONSE_INVALID')
}
return {
transactionId: normalizeVipId(value.transactionId, 'transactionId'),
status
}
}