优化 affiliate_dash 核销时机:建单成功即核销(参考 feifei 给用户链接即核销)
- prepareAffiliateDashTask 建单成功(91 卡信息到手、链接可发放)立即核销行业电子凭证 - 核销失败不阻塞下单与链接发放(consumeStatus=failed, delivered 回调兜底补核销) - sync delivered 分支 alreadyConsumed 短路,避免重复核销 - typecheck + 212 测试通过
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
|||||||
} from '../../platforms/affiliate-dash/order-service.js'
|
} from '../../platforms/affiliate-dash/order-service.js'
|
||||||
import { getAffiliateDashConfig } from '../../platforms/affiliate-dash/config.js'
|
import { getAffiliateDashConfig } from '../../platforms/affiliate-dash/config.js'
|
||||||
import { consumeKuaishouIndustryVouchersForTask } from '../../platforms/kuaishou-industry/voucher-service.js'
|
import { consumeKuaishouIndustryVouchersForTask } from '../../platforms/kuaishou-industry/voucher-service.js'
|
||||||
|
import { buildKuaishouIndustryVoucherContext } from '../../platforms/kuaishou-industry/voucher-binding-service.js'
|
||||||
import type { TaskRow } from '../../../types/repository/rows.js'
|
import type { TaskRow } from '../../../types/repository/rows.js'
|
||||||
|
|
||||||
export function isAffiliateDashTask(task: Partial<TaskRow> | null | undefined) {
|
export function isAffiliateDashTask(task: Partial<TaskRow> | null | undefined) {
|
||||||
@@ -75,6 +76,38 @@ export async function prepareAffiliateDashTask(task: TaskRow) {
|
|||||||
clientOrderNo,
|
clientOrderNo,
|
||||||
syncedAt: now,
|
syncedAt: now,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 核销时机优化:建单成功(91 卡信息到手、领取链接可发放)即核销行业电子凭证,
|
||||||
|
// 不再等 delivered 回调(参考 kuaishou-feifei「给用户链接即核销」)。
|
||||||
|
// 核销失败不阻塞下单与链接发放:consumeStatus=failed,delivered 回调兜底补核销。
|
||||||
|
const consumeResult = await consumeKuaishouIndustryVouchersForTask(task, {
|
||||||
|
source: 'affiliate_dash_order_created',
|
||||||
|
consumeTime: Date.now(),
|
||||||
|
})
|
||||||
|
const consumedVoucher = consumeResult.consumed[0] || null
|
||||||
|
let nextIndustryVoucher = taskContext.kuaishouIndustryVoucher
|
||||||
|
if (consumeResult.vouchers.length > 0) {
|
||||||
|
nextFlow.consumeStatus = consumeResult.ok ? 'success' : 'failed'
|
||||||
|
if (consumedVoucher) {
|
||||||
|
nextIndustryVoucher = buildKuaishouIndustryVoucherContext(
|
||||||
|
consumedVoucher,
|
||||||
|
taskContext.kuaishouIndustryVoucher,
|
||||||
|
now,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!consumeResult.ok) {
|
||||||
|
logIntegration('[affiliate-dash]', '建单成功但电子凭证核销失败,delivered 回调将兜底重试', {
|
||||||
|
taskId: task.id,
|
||||||
|
orderNo: order.orderNo,
|
||||||
|
voucherCount: consumeResult.vouchers.length,
|
||||||
|
failedCount: consumeResult.failed.length,
|
||||||
|
errorMessage: consumeResult.failed[0]?.errorMessage || '',
|
||||||
|
}, { level: 'warn' })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextFlow.consumeStatus = 'not_required'
|
||||||
|
}
|
||||||
|
|
||||||
const updatedTask = await updateTask(task.id, {
|
const updatedTask = await updateTask(task.id, {
|
||||||
task_status: TASK_STATUS.LINK_GENERATED,
|
task_status: TASK_STATUS.LINK_GENERATED,
|
||||||
user_action_status: 'pending_claim',
|
user_action_status: 'pending_claim',
|
||||||
@@ -83,6 +116,7 @@ export async function prepareAffiliateDashTask(task: TaskRow) {
|
|||||||
context_json: JSON.stringify({
|
context_json: JSON.stringify({
|
||||||
...taskContext,
|
...taskContext,
|
||||||
affiliateDash: nextFlow,
|
affiliateDash: nextFlow,
|
||||||
|
kuaishouIndustryVoucher: nextIndustryVoucher,
|
||||||
}),
|
}),
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
})
|
})
|
||||||
@@ -153,10 +187,14 @@ export async function syncAffiliateDashTaskStatus(task: TaskRow) {
|
|||||||
lastError = ''
|
lastError = ''
|
||||||
break
|
break
|
||||||
case 'delivered': {
|
case 'delivered': {
|
||||||
const consumeResult = await consumeKuaishouIndustryVouchersForTask(task, {
|
const alreadyConsumed =
|
||||||
source: 'affiliate_dash_delivered',
|
nextFlow.consumeStatus === 'success' || nextFlow.consumeStatus === 'not_required'
|
||||||
consumeTime: Date.now(),
|
const consumeResult = alreadyConsumed
|
||||||
})
|
? { ok: true, vouchers: [], consumed: [], failed: [] }
|
||||||
|
: await consumeKuaishouIndustryVouchersForTask(task, {
|
||||||
|
source: 'affiliate_dash_delivered',
|
||||||
|
consumeTime: Date.now(),
|
||||||
|
})
|
||||||
if (consumeResult.ok || consumeResult.vouchers.length === 0) {
|
if (consumeResult.ok || consumeResult.vouchers.length === 0) {
|
||||||
nextTaskStatus = TASK_STATUS.REDEEMED
|
nextTaskStatus = TASK_STATUS.REDEEMED
|
||||||
deliveryStatus = 'delivered'
|
deliveryStatus = 'delivered'
|
||||||
|
|||||||
@@ -523,3 +523,20 @@ order_site 处理要求:
|
|||||||
**验证**:前后端 typecheck 通过;后端全量 212 测试通过;claim 详情分流脚本验证(flowType=affiliate_dash、affiliateDash 块完整、product 正确、其他 flow 为 null)。
|
**验证**:前后端 typecheck 通过;后端全量 212 测试通过;claim 详情分流脚本验证(flowType=affiliate_dash、affiliateDash 块完整、product 正确、其他 flow 为 null)。
|
||||||
|
|
||||||
**领取闭环状态迁移(与 §8 状态机一致)**:输 UID → submitClaimUid(bind 触发,task `link_generated → waiting_binding`)→ 轮询详情(bind-result 刷新 bound)→ 提交发货(submitAffiliateDashClaim,task `waiting_binding → redeeming`)→ 回调 delivered → redeemed + 核销。
|
**领取闭环状态迁移(与 §8 状态机一致)**:输 UID → submitClaimUid(bind 触发,task `link_generated → waiting_binding`)→ 轮询详情(bind-result 刷新 bound)→ 提交发货(submitAffiliateDashClaim,task `waiting_binding → redeeming`)→ 回调 delivered → redeemed + 核销。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19. 核销时机优化(v2.4.1 · 已完成)
|
||||||
|
|
||||||
|
**需求**:核销时机从「delivered 回调」提前到「给用户链接(91 卡信息到手、建单成功)」——参考 kuaishou-feifei 的「给用户链接即核销」模式。
|
||||||
|
|
||||||
|
**改动**(`apps/backend/src/services/fulfillment/affiliate-dash/index.ts`):
|
||||||
|
|
||||||
|
| 位置 | 行为 |
|
||||||
|
| --- | --- |
|
||||||
|
| `prepareAffiliateDashTask` | 建单成功(orderNo 拿到、task → link_generated、91 侧即可取 claimUrl)后**立即**调用 `consumeKuaishouIndustryVouchersForTask`:有凭证 → 核销并写回 `kuaishouIndustryVoucher` 上下文(`consumeStatus=success`);无凭证 → `not_required`;**核销失败不阻塞**下单与链接发放(`consumeStatus=failed` + warn 日志,delivered 兜底) |
|
||||||
|
| `syncAffiliateDashTaskStatus`(delivered 分支) | `alreadyConsumed` 短路:`consumeStatus ∈ {success, not_required}` 时不再重复调核销,直接 redeemed;`failed/空` 才补核销(原逻辑保留为兜底) |
|
||||||
|
|
||||||
|
**效果**:核销由「发货完成后(分钟~小时级)」提前到「建单成功(秒级)」;91 卡信息一经消耗即核销,delivered 回调只做状态收敛。已核销成功的凭证在 delivered 时 `resolveTaskVouchers` 返回空,自然走 redeemed。
|
||||||
|
|
||||||
|
**验证**:typecheck 通过;后端全量 212 测试通过。
|
||||||
|
|||||||
Reference in New Issue
Block a user