feat(claim): 支持固定H5订单号领取与订单兑换有效性后台控制
- 数据库新增 099_order_claim_status.sql 迁移,扩展 claim_status、claim_status_reason 等字段 - 后端新增 /claims/h5/:orderNo、UID提交、分销提交接口,保留原有 token 接口双轨兼容 - 增加兑换有效性强拦截:暂停/关闭/退款/取消订单精准阻断并返回友好提示 - 后台新增 POST /admin/orders/:orderId/claim-status 接口与审计日志记录 - 前端管理后台(订单列表、详情页)支持 H5 兑换状态展示、一键复制链接及暂停/关闭/启用弹窗 - 前端领取页面适配 /h5/:orderNo 路由及暂停/关闭等状态的异常展示 - 验证方式:运行前后端类型检查、全量单元测试(后端 674、前端 132)及全量构建编译均通过
This commit is contained in:
@@ -96,6 +96,9 @@ export function mapAdminOrderListItem(
|
||||
customerStatus: fulfillmentProgress.customerStatus,
|
||||
preparedTaskCount: fulfillmentProgress.preparedTaskCount,
|
||||
completedTaskCount: fulfillmentProgress.completedTaskCount,
|
||||
claimStatus: item.claim_status || 'active',
|
||||
claimStatusReason: item.claim_status_reason || '',
|
||||
claimStatusUpdatedAt: item.claim_status_updated_at || null,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -200,6 +200,9 @@ export async function getAdminOrderDetail(
|
||||
? order.raw_payload_json
|
||||
: JSON.parse(String(order.raw_payload_json || '{}')),
|
||||
fulfillmentProgress: buildOrderFulfillmentProgress(tasks),
|
||||
claimStatus: order.claim_status || 'active',
|
||||
claimStatusReason: order.claim_status_reason || '',
|
||||
claimStatusUpdatedAt: order.claim_status_updated_at || null,
|
||||
},
|
||||
items: items.map((item) => ({
|
||||
orderItemId: item.id,
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { getOrderById, updateOrderClaimStatus } from '../../../repositories/order-repo.js'
|
||||
import { createHttpError } from '../../../utils/http.js'
|
||||
import { nowIso } from '../../../utils/time.js'
|
||||
import type { AdminViewerSessionInput } from '../../../types/admin/read-inputs.js'
|
||||
|
||||
export type UpdateOrderClaimStatusPayload = {
|
||||
status: 'active' | 'paused' | 'closed'
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export async function updateAdminOrderClaimStatus(
|
||||
orderId: number | string,
|
||||
payload: UpdateOrderClaimStatusPayload,
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
) {
|
||||
const normalizedOrderId = Number(orderId)
|
||||
if (!Number.isFinite(normalizedOrderId) || normalizedOrderId <= 0) {
|
||||
throw createHttpError('无效的订单 ID', {
|
||||
statusCode: 400,
|
||||
errorCode: 'invalid_order_id',
|
||||
})
|
||||
}
|
||||
|
||||
const validStatuses = new Set(['active', 'paused', 'closed'])
|
||||
if (!payload || !validStatuses.has(payload.status)) {
|
||||
throw createHttpError('无效的兑换有效性状态,仅支持 active, paused, closed', {
|
||||
statusCode: 400,
|
||||
errorCode: 'invalid_claim_status',
|
||||
})
|
||||
}
|
||||
|
||||
const order = await getOrderById(normalizedOrderId)
|
||||
if (!order) {
|
||||
throw createHttpError('订单不存在', {
|
||||
statusCode: 404,
|
||||
errorCode: 'admin_order_not_found',
|
||||
})
|
||||
}
|
||||
|
||||
const reason = String(payload.reason || '').trim()
|
||||
const now = nowIso()
|
||||
|
||||
const updated = await updateOrderClaimStatus(order.id, {
|
||||
claimStatus: payload.status,
|
||||
reason,
|
||||
updatedAt: now,
|
||||
})
|
||||
|
||||
if (!updated) {
|
||||
throw createHttpError('更新订单兑换状态失败', {
|
||||
statusCode: 500,
|
||||
errorCode: 'update_claim_status_failed',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
orderId: updated.id,
|
||||
platformOrderId: updated.platform_order_id,
|
||||
claimStatus: updated.claim_status || payload.status,
|
||||
claimStatusReason: updated.claim_status_reason || reason,
|
||||
claimStatusUpdatedAt: updated.claim_status_updated_at || now,
|
||||
operator: session ? { userId: session.userId, username: session.username } : null,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user