增加兑换重试链路
This commit is contained in:
@@ -8,7 +8,7 @@ const props = defineProps<{
|
||||
function resolveTone(status: string) {
|
||||
const normalized = String(status || '').trim().toLowerCase()
|
||||
|
||||
if (['paid', 'link_generated', 'redeemed', 'available', 'delivered', 'consumed', 'success', 'active', 'system_bound', 'binding_completed'].includes(normalized)) {
|
||||
if (['paid', 'link_generated', 'redeemed', 'available', 'delivered', 'success', 'active', 'system_bound', 'binding_completed'].includes(normalized)) {
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ function resolveTone(status: string) {
|
||||
return 'primary'
|
||||
}
|
||||
|
||||
if (['waiting_inventory', 'retry_pending', 'manual_review', 'invalid', 'expired', 'operator', 'pending_binding', 'binding_exception', 'not_started'].includes(normalized)) {
|
||||
if (['waiting_inventory', 'retry_pending', 'manual_review', 'invalid', 'expired', 'operator', 'pending_binding', 'binding_exception', 'not_started', 'consumed'].includes(normalized)) {
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ function resolveLabel(status: string) {
|
||||
available: '可用',
|
||||
reserved: '已预占',
|
||||
delivered: '已发放',
|
||||
consumed: '已发放',
|
||||
consumed: '已消耗',
|
||||
invalid: '已作废',
|
||||
active: '生效中',
|
||||
admin: '管理员',
|
||||
|
||||
@@ -373,6 +373,21 @@ export interface AdminTaskDetail {
|
||||
roleId: string
|
||||
roleName: string
|
||||
}
|
||||
redeemResolution: null | {
|
||||
status: string
|
||||
taskStatus: string
|
||||
replacementCount: number
|
||||
finishedAt: string | null
|
||||
attempts: Array<{
|
||||
attempt: number
|
||||
inventoryItemId: number | null
|
||||
codeMasked: string
|
||||
credentialType: string
|
||||
outcome: string
|
||||
resultCode: string
|
||||
resultMessage: string
|
||||
}>
|
||||
}
|
||||
manualDispatch: null | {
|
||||
outcome: string
|
||||
deliveryReference: string
|
||||
|
||||
@@ -54,6 +54,10 @@ export function formatTaskEventType(eventType: string) {
|
||||
const labelMap: Record<string, string> = {
|
||||
manual_dispatch_completed: '人工履约已回写',
|
||||
inventory_binding_released: '库存绑定已释放',
|
||||
claim_redeem_code_used: '兑换码已使用',
|
||||
claim_redeem_code_invalid: '兑换码错误',
|
||||
claim_redeem_inventory_replaced: '已切换新兑换码',
|
||||
claim_redeem_completed: '兑换链路完成',
|
||||
agiso_auto_delivery_success: '咸鱼自动发货成功',
|
||||
agiso_auto_delivery_failed: '咸鱼自动发货失败',
|
||||
agiso_auto_delivery_skipped: '咸鱼自动发货已跳过',
|
||||
|
||||
@@ -28,6 +28,7 @@ export const adminInventoryStatusOptions = [
|
||||
{ label: '可用', value: 'available' },
|
||||
{ label: '已预占', value: 'reserved' },
|
||||
{ label: '已发放', value: 'delivered' },
|
||||
{ label: '已消耗', value: 'consumed' },
|
||||
{ label: '已作废', value: 'invalid' },
|
||||
]
|
||||
|
||||
|
||||
@@ -146,6 +146,13 @@ function formatTaskEventPayload(payload: Record<string, unknown>) {
|
||||
payload.outcome ? `结果 ${payload.outcome}` : '',
|
||||
payload.resultCode ? `代码 ${payload.resultCode}` : '',
|
||||
payload.resultMessage ? `说明 ${payload.resultMessage}` : '',
|
||||
payload.codeMasked ? `凭据 ${payload.codeMasked}` : '',
|
||||
payload.previousCodeMasked ? `旧码 ${payload.previousCodeMasked}` : '',
|
||||
payload.nextCodeMasked ? `新码 ${payload.nextCodeMasked}` : '',
|
||||
payload.inventoryItemId ? `库存项 #${payload.inventoryItemId}` : '',
|
||||
payload.previousInventoryItemId ? `旧库存 #${payload.previousInventoryItemId}` : '',
|
||||
payload.nextInventoryItemId ? `新库存 #${payload.nextInventoryItemId}` : '',
|
||||
payload.previousOutcome ? `切换原因 ${formatRedeemOutcomeLabel(String(payload.previousOutcome || ''))}` : '',
|
||||
payload.deliveryReference ? `单号 ${payload.deliveryReference}` : '',
|
||||
payload.platformOrderId ? `平台单 ${payload.platformOrderId}` : '',
|
||||
payload.reason ? `原因 ${payload.reason}` : '',
|
||||
@@ -161,6 +168,29 @@ function formatTaskEventPayload(payload: Record<string, unknown>) {
|
||||
return JSON.stringify(payload || {})
|
||||
}
|
||||
|
||||
function formatRedeemOutcomeLabel(value: string) {
|
||||
const normalized = String(value || '').trim()
|
||||
|
||||
switch (normalized) {
|
||||
case 'success':
|
||||
return '兑换成功'
|
||||
case 'code_used':
|
||||
return '兑换码已使用'
|
||||
case 'code_invalid':
|
||||
return '兑换码错误'
|
||||
case 'captcha_rejected':
|
||||
return '验证码错误'
|
||||
case 'failed':
|
||||
return '兑换失败'
|
||||
default:
|
||||
return normalized || '-'
|
||||
}
|
||||
}
|
||||
|
||||
function formatRedeemResolutionStatus(value: string) {
|
||||
return value === 'success' ? '已完成' : value === 'failed' ? '失败收口' : (value || '-')
|
||||
}
|
||||
|
||||
onMounted(loadDetail)
|
||||
onBeforeUnmount(clearScreenshotPreview)
|
||||
</script>
|
||||
@@ -309,6 +339,62 @@ onBeforeUnmount(clearScreenshotPreview)
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section v-if="detail.redeemResolution" class="table-card">
|
||||
<h3>兑换重试链路</h3>
|
||||
<p class="section-copy">这里记录自动兑换时每次使用过的 CDK、判定结果,以及是否切换到了新的同类型凭据。</p>
|
||||
|
||||
<div class="redeem-summary-grid">
|
||||
<article class="redeem-summary-card">
|
||||
<span>处理结果</span>
|
||||
<strong>{{ formatRedeemResolutionStatus(detail.redeemResolution.status) }}</strong>
|
||||
</article>
|
||||
<article class="redeem-summary-card">
|
||||
<span>任务收口状态</span>
|
||||
<strong>{{ detail.redeemResolution.taskStatus || detail.task.status || '-' }}</strong>
|
||||
</article>
|
||||
<article class="redeem-summary-card">
|
||||
<span>更换新码次数</span>
|
||||
<strong>{{ detail.redeemResolution.replacementCount }}</strong>
|
||||
</article>
|
||||
<article class="redeem-summary-card">
|
||||
<span>完成时间</span>
|
||||
<strong>{{ formatAdminDateTime(detail.redeemResolution.finishedAt) }}</strong>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div v-if="detail.redeemResolution.attempts.length > 0" class="redeem-attempts">
|
||||
<article
|
||||
v-for="attempt in detail.redeemResolution.attempts"
|
||||
:key="`${attempt.attempt}-${attempt.inventoryItemId || 'na'}`"
|
||||
class="redeem-attempt-card"
|
||||
>
|
||||
<div class="redeem-attempt-head">
|
||||
<strong>第 {{ attempt.attempt }} 次尝试</strong>
|
||||
<span class="redeem-outcome">{{ formatRedeemOutcomeLabel(attempt.outcome) }}</span>
|
||||
</div>
|
||||
<div class="redeem-attempt-grid">
|
||||
<div>
|
||||
<span>库存项</span>
|
||||
<strong>{{ attempt.inventoryItemId ? `#${attempt.inventoryItemId}` : '-' }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>凭据</span>
|
||||
<strong>{{ attempt.codeMasked || '-' }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>类型</span>
|
||||
<strong>{{ attempt.credentialType || '-' }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>结果码</span>
|
||||
<strong>{{ attempt.resultCode || '-' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<p class="redeem-attempt-copy">{{ attempt.resultMessage || '-' }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<h3>库存绑定</h3>
|
||||
<p class="section-copy">这里展示任务当前和历史绑定过的库存项,便于核对多库存任务的真实履约轨迹。</p>
|
||||
@@ -347,7 +433,9 @@ onBeforeUnmount(clearScreenshotPreview)
|
||||
<AdminStatusTag :status="binding.bindingStatus" />
|
||||
<AdminStatusTag :status="binding.inventoryStatus" />
|
||||
</div>
|
||||
<div v-if="binding.invalidReason" class="binding-meta">作废原因:{{ binding.invalidReason }}</div>
|
||||
<div v-if="binding.invalidReason" class="binding-meta">
|
||||
{{ binding.inventoryStatus === 'invalid' ? '作废原因' : binding.inventoryStatus === 'consumed' ? '消耗原因' : '处理原因' }}:{{ binding.invalidReason }}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ canViewSensitiveTaskData ? (binding.displayValue || '-') : '客服不可见' }}</td>
|
||||
<td>
|
||||
@@ -461,6 +549,56 @@ onBeforeUnmount(clearScreenshotPreview)
|
||||
.data-table th,.data-table td { padding: 12px 10px; text-align: left; border-bottom: 1px solid rgba(86,108,138,.08); }
|
||||
.data-table th { width: 160px; color: #64748b; }
|
||||
.status-stack { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.redeem-summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.redeem-summary-card,
|
||||
.redeem-attempt-card {
|
||||
padding: 14px 16px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(86,108,138,.1);
|
||||
background: rgba(248,250,252,.92);
|
||||
}
|
||||
.redeem-summary-card span,
|
||||
.redeem-attempt-grid span {
|
||||
display: block;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.redeem-summary-card strong,
|
||||
.redeem-attempt-grid strong {
|
||||
color: #1d3555;
|
||||
}
|
||||
.redeem-attempts {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.redeem-attempt-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
.redeem-outcome {
|
||||
color: #175cd3;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.redeem-attempt-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.redeem-attempt-copy {
|
||||
margin: 12px 0 0;
|
||||
color: #334155;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.table-empty { text-align: center; color: #64748b; }
|
||||
.binding-primary { margin-top: 4px; color: #0f766e; font-size: 12px; font-weight: 600; }
|
||||
.binding-meta { margin-top: 4px; color: #64748b; font-size: 12px; }
|
||||
|
||||
Reference in New Issue
Block a user