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:
yml2213
2026-10-10 18:05:46 +08:00
parent 2d727a6797
commit 412d219cca
21 changed files with 788 additions and 50 deletions
+24
View File
@@ -39,6 +39,30 @@ export function isClaimPath(pathname: unknown): boolean {
return resolveClaimToken(pathname) !== ''
}
/**
* 取 H5 订单号领取链接(`/h5/<订单号>`)里的订单号。
*
* 只认 `/h5/<一段 orderNo>`:
* - `/h5/2623001922675146` 命中,结尾多余斜杠容忍(`/h5/2623001922675146/`);
* - `/h5`、`/h5/`、`/h5/a/b` 都不命中;
* - 查询串 / hash 片段(如 `/h5/123?x=1`)会被丢弃后取第一段。
*/
export function resolveH5ClaimOrderNo(pathname: unknown): string {
const normalized = String(pathname ?? '').trim()
if (!normalized.startsWith('/')) return ''
const segments = normalized.split(/[?#]/)[0].replace(/\/+$/, '').split('/').filter(Boolean)
if (segments.length !== 2 || segments[0] !== 'h5') return ''
return decodeTokenSegment(segments[1])
}
/** 是否为 H5 订单号领取链接(`/h5/<orderNo>`) */
export function isH5ClaimPath(pathname: unknown): boolean {
return resolveH5ClaimOrderNo(pathname) !== ''
}
function decodeTokenSegment(segment: string): string {
try {
return decodeURIComponent(segment)