优化任务状态投影和部署文档
This commit is contained in:
@@ -1,10 +1,26 @@
|
||||
import { query } from '../../db/client.js'
|
||||
import { TASK_STATUS } from '../../domain/task-status.js'
|
||||
import { nowIso } from '../../utils/time.js'
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
export async function getAdminDashboardSummary() {
|
||||
const todayPrefix = nowIso().slice(0, 10)
|
||||
const paidPendingClaimStatuses = [
|
||||
TASK_STATUS.PAID,
|
||||
TASK_STATUS.LINK_GENERATED,
|
||||
TASK_STATUS.PENDING_BINDING_PREPARE,
|
||||
TASK_STATUS.WAITING_BINDING,
|
||||
]
|
||||
const claimingStatuses = [
|
||||
TASK_STATUS.CLAIMED,
|
||||
TASK_STATUS.ROLE_CONFIRMED,
|
||||
TASK_STATUS.REDEEMING,
|
||||
]
|
||||
const abnormalStatuses = [
|
||||
TASK_STATUS.RETRY_PENDING,
|
||||
TASK_STATUS.MANUAL_REVIEW,
|
||||
]
|
||||
const result = await query(
|
||||
`
|
||||
SELECT
|
||||
@@ -12,26 +28,32 @@ export async function getAdminDashboardSummary() {
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM fulfillment_tasks
|
||||
WHERE task_status IN ('paid', 'link_generated', 'pending_binding_prepare', 'waiting_binding')
|
||||
WHERE task_status = ANY($2::text[])
|
||||
) AS paid_pending_claim,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM fulfillment_tasks
|
||||
WHERE task_status IN ('claimed', 'role_confirmed', 'redeeming')
|
||||
WHERE task_status = ANY($3::text[])
|
||||
) AS claiming_tasks,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM fulfillment_tasks
|
||||
WHERE task_status = 'redeemed'
|
||||
WHERE task_status = $4
|
||||
AND to_char(updated_at AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD') = $1
|
||||
) AS redeemed_today,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM fulfillment_tasks
|
||||
WHERE task_status IN ('retry_pending', 'manual_review')
|
||||
WHERE task_status = ANY($5::text[])
|
||||
) AS abnormal_tasks
|
||||
`,
|
||||
[todayPrefix],
|
||||
[
|
||||
todayPrefix,
|
||||
paidPendingClaimStatuses,
|
||||
claimingStatuses,
|
||||
TASK_STATUS.REDEEMED,
|
||||
abnormalStatuses,
|
||||
],
|
||||
)
|
||||
const summary: JsonObject = result.rows[0] || {}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
getTaskPrimaryClaimTokenId,
|
||||
isManualDispatchTask,
|
||||
mapKuaishouCloudFulfillmentContext,
|
||||
mapKuaishouCloudTaskStateProjection,
|
||||
mapManualDispatchContext,
|
||||
mapRedeemResolutionContext,
|
||||
parseTaskContext,
|
||||
@@ -180,10 +181,11 @@ export async function getAdminTaskDetail(
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
const claimUrl = claimToken ? buildClaimUrl(claimToken.token) : ''
|
||||
const screenshotUrl = await resolveAdminTaskScreenshotUrl(task, viewerContext)
|
||||
const cloudSourceLabelMap = buildCloudSourceLabelMap()
|
||||
const kuaishouCloudFulfillment = mapKuaishouCloudFulfillmentContext(
|
||||
taskContext.kuaishouCloudFulfillment,
|
||||
{ cloudSourceLabelMap: buildCloudSourceLabelMap() },
|
||||
)
|
||||
{ cloudSourceLabelMap },
|
||||
) || mapKuaishouCloudTaskStateProjection(task, { cloudSourceLabelMap })
|
||||
|
||||
return {
|
||||
task: mapAdminTaskListItem({
|
||||
|
||||
@@ -160,6 +160,105 @@ export function mapKuaishouCloudFulfillmentContext(
|
||||
}
|
||||
}
|
||||
|
||||
export function mapKuaishouCloudTaskStateProjection(
|
||||
task: TaskLike | null | undefined,
|
||||
options: KuaishouCloudFulfillmentMapOptions = {},
|
||||
): JsonRecord | null {
|
||||
if (!task || String(task.executor_key || '').trim() !== 'kuaishou_ct_assisted') {
|
||||
return null
|
||||
}
|
||||
|
||||
const hasProjection = [
|
||||
task.kuaishou_ticket_status,
|
||||
task.kuaishou_ticket_code_masked,
|
||||
task.kuaishou_bind_url,
|
||||
task.kuaishou_vn_phone,
|
||||
task.kuaishou_role_name,
|
||||
task.kuaishou_role_id,
|
||||
task.kuaishou_dispatch_status,
|
||||
task.kuaishou_consume_status,
|
||||
task.kuaishou_source_key,
|
||||
].some((value) => String(value || '').trim())
|
||||
|
||||
if (!hasProjection) {
|
||||
return null
|
||||
}
|
||||
|
||||
const sourceKey = String(task.kuaishou_source_key || '').trim()
|
||||
const roleName = String(task.kuaishou_role_name || '').trim()
|
||||
const roleId = String(task.kuaishou_role_id || '').trim()
|
||||
|
||||
return {
|
||||
flowType: 'kuaishou_cloud_fulfillment',
|
||||
configId: '',
|
||||
internalSkuCode: '',
|
||||
internalSkuName: '',
|
||||
ticket: {
|
||||
code: String(task.kuaishou_ticket_code_masked || '').trim(),
|
||||
capturedAt: null,
|
||||
status: String(task.kuaishou_ticket_status || 'pending').trim() || 'pending',
|
||||
verifiedAt: null,
|
||||
goodsTitle: '',
|
||||
leftCount: 0,
|
||||
},
|
||||
binding: {
|
||||
prepareStatus: task.kuaishou_bind_url ? 'ready' : 'pending',
|
||||
cloudSourceKeys: sourceKey ? [sourceKey] : [],
|
||||
cloudSourceLabels: sourceKey ? [resolveCloudSourceLabel(sourceKey, options.cloudSourceLabelMap)] : [],
|
||||
resolvedSourceKey: sourceKey,
|
||||
resolvedSourceLabel: sourceKey ? resolveCloudSourceLabel(sourceKey, options.cloudSourceLabelMap) : '',
|
||||
skuId: 0,
|
||||
skuName: '',
|
||||
vnKey: '',
|
||||
vnId: 0,
|
||||
vnPhone: String(task.kuaishou_vn_phone || '').trim(),
|
||||
bindUrl: String(task.kuaishou_bind_url || '').trim(),
|
||||
bindPreparedAt: null,
|
||||
bindExpiresAt: null,
|
||||
bindProbeAt: null,
|
||||
bindProbeStatus: '',
|
||||
bindProbeMessage: '',
|
||||
},
|
||||
role: {
|
||||
status: roleName || roleId ? 'ready' : 'pending',
|
||||
name: roleName,
|
||||
rid: roleId,
|
||||
refreshedAt: null,
|
||||
errorMessage: '',
|
||||
rawInfo: null,
|
||||
},
|
||||
purchase: {
|
||||
autoBuyEnabled: true,
|
||||
minAssetReserve: 0,
|
||||
usedKnapsack: false,
|
||||
purchaseTriggered: false,
|
||||
assetBefore: 0,
|
||||
assetAfter: 0,
|
||||
purchaseAt: null,
|
||||
},
|
||||
dispatch: {
|
||||
status: String(task.kuaishou_dispatch_status || 'pending').trim() || 'pending',
|
||||
dispatchAt: null,
|
||||
sendType: 0,
|
||||
note: '',
|
||||
},
|
||||
returnNumber: {
|
||||
status: 'pending',
|
||||
returnedAt: null,
|
||||
autoReturnEnabled: false,
|
||||
},
|
||||
consume: {
|
||||
status: String(task.kuaishou_consume_status || 'pending').trim() || 'pending',
|
||||
shopId: '',
|
||||
shopName: '',
|
||||
autoConsumeEnabled: false,
|
||||
consumedAt: null,
|
||||
errorMessage: '',
|
||||
},
|
||||
notes: '',
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCloudSourceLabel(sourceKey: string, labelMap: CloudSourceLabelMap | undefined) {
|
||||
const key = String(sourceKey || '').trim()
|
||||
if (!key) return ''
|
||||
|
||||
@@ -102,8 +102,8 @@ export function mapAdminTaskListItem(
|
||||
resourceStatus: fulfillment.resourceStatus,
|
||||
customerStatus: fulfillment.customerStatus,
|
||||
loginType: task.login_type,
|
||||
roleName: task.role_name,
|
||||
roleId: task.role_id,
|
||||
roleName: resolveTaskDisplayRoleName(task),
|
||||
roleId: resolveTaskDisplayRoleId(task),
|
||||
runtimeSessionId: task.runtime_session_id,
|
||||
claimedAt: task.claimed_at,
|
||||
roleConfirmedAt: task.role_confirmed_at,
|
||||
@@ -269,3 +269,11 @@ function normalizeRecord(value: unknown): JsonRecord {
|
||||
function getTaskRetryCount(task: TaskRow | null | undefined): number {
|
||||
return Number(task?.attempt_count || 0)
|
||||
}
|
||||
|
||||
function resolveTaskDisplayRoleName(task: TaskRow): string {
|
||||
return String(task.kuaishou_role_name || task.role_name || '').trim()
|
||||
}
|
||||
|
||||
function resolveTaskDisplayRoleId(task: TaskRow): string {
|
||||
return String(task.kuaishou_role_id || task.role_id || '').trim()
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from '../../platforms/kuaishou-eticket/source-config-service.js'
|
||||
import { createHttpError } from '../../../utils/http.js'
|
||||
import { nowIso } from '../../../utils/time.js'
|
||||
import { TASK_STATUS } from '../../../domain/task-status.js'
|
||||
import {
|
||||
createAdminViewerContext,
|
||||
parseTaskContext,
|
||||
@@ -200,7 +201,7 @@ export async function prepareAdminTaskKuaishouCloudFulfillment(
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
task_status: 'waiting_binding',
|
||||
task_status: TASK_STATUS.WAITING_BINDING,
|
||||
user_action_status: 'pending',
|
||||
claim_token: claimLinkState.token || task.claim_token || '',
|
||||
claim_expires_at: claimLinkState.expiredAt || getTaskClaimExpiresAt(task),
|
||||
@@ -334,7 +335,7 @@ export async function dispatchAdminTaskKuaishouCloudFulfillment(
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
task_status: 'dispatched_pending_return',
|
||||
task_status: TASK_STATUS.DISPATCHED_PENDING_RETURN,
|
||||
delivery_status: 'delivered',
|
||||
result_code: 'kuaishou_cloud_dispatched',
|
||||
result_message: String(dispatchResult.responseMessage || dispatchResult.note || 'cloudtentacles 发货成功').trim(),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { updateTask } from '../../../repositories/task-repo.js'
|
||||
import { createTaskEvent } from '../../../repositories/task-event-repo.js'
|
||||
import { createHttpError } from '../../../utils/http.js'
|
||||
import { nowIso } from '../../../utils/time.js'
|
||||
import { TASK_STATUS } from '../../../domain/task-status.js'
|
||||
import {
|
||||
canViewerCloseTask,
|
||||
createAdminViewerContext,
|
||||
@@ -44,7 +45,7 @@ export async function closeAdminTask(
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
task_status: 'closed',
|
||||
task_status: TASK_STATUS.CLOSED,
|
||||
delivery_status: 'closed',
|
||||
result_code: 'closed_by_admin',
|
||||
result_message: '任务已关闭,领取链接已失效',
|
||||
|
||||
Reference in New Issue
Block a user