优化任务账号名称展示

This commit is contained in:
yml2213
2026-05-26 13:14:31 +08:00
parent 569d6a6e3d
commit 677d7fab46
5 changed files with 101 additions and 10 deletions
@@ -4,6 +4,7 @@ import { listOrderItemsByOrderId } from '../../repositories/order-item-repo.js'
import { getOrderById, listOrders } from '../../repositories/order-repo.js' import { getOrderById, listOrders } from '../../repositories/order-repo.js'
import { getTaskById, listTasks, listTasksByOrderId } from '../../repositories/task-repo.js' import { getTaskById, listTasks, listTasksByOrderId } from '../../repositories/task-repo.js'
import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js' import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js'
import { listCloudtentaclesSources } from '../platforms/cloudtentacles/source-config-service.js'
import { createHttpError } from '../../utils/http.js' import { createHttpError } from '../../utils/http.js'
import { formatFenToAmount, normalizeFen } from '../../utils/money.js' import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
import { normalizeDateQuery, normalizePage, normalizePageSize, safeParseJson } from './admin-query-utils.js' import { normalizeDateQuery, normalizePage, normalizePageSize, safeParseJson } from './admin-query-utils.js'
@@ -171,7 +172,10 @@ export async function getAdminTaskDetail(
const viewerContext = createAdminViewerContext(session) const viewerContext = createAdminViewerContext(session)
const claimUrl = claimToken ? buildClaimUrl(claimToken.token) : '' const claimUrl = claimToken ? buildClaimUrl(claimToken.token) : ''
const screenshotUrl = await resolveAdminTaskScreenshotUrl(task, viewerContext) const screenshotUrl = await resolveAdminTaskScreenshotUrl(task, viewerContext)
const kuaishouCloudFulfillment = mapKuaishouCloudFulfillmentContext(taskContext.kuaishouCloudFulfillment) const kuaishouCloudFulfillment = mapKuaishouCloudFulfillmentContext(
taskContext.kuaishouCloudFulfillment,
{ cloudSourceLabelMap: buildCloudSourceLabelMap() },
)
return { return {
task: mapAdminTaskListItem({ task: mapAdminTaskListItem({
@@ -248,6 +252,20 @@ export async function getAdminTaskDetail(
} }
} }
function buildCloudSourceLabelMap() {
const labelMap = new Map<string, string>()
for (const source of listCloudtentaclesSources().sources) {
const key = String(source.key || '').trim()
if (!key) continue
const label = String(source.label || source.username || key).trim() || key
labelMap.set(key, label)
}
return labelMap
}
export async function getAdminTaskScreenshotPath( export async function getAdminTaskScreenshotPath(
taskId: AdminEntityIdInput, taskId: AdminEntityIdInput,
session: AdminViewerSessionInput | null = null, session: AdminViewerSessionInput | null = null,
@@ -4,6 +4,7 @@ import assert from 'node:assert/strict'
import { import {
canViewerCloseTask, canViewerCloseTask,
createAdminViewerContext, createAdminViewerContext,
mapKuaishouCloudFulfillmentContext,
resolveAdminTaskScreenshotUrl, resolveAdminTaskScreenshotUrl,
} from './admin-read-shared-helpers.js' } from './admin-read-shared-helpers.js'
@@ -37,3 +38,24 @@ test('canViewerCloseTask allows support to close active task but not redeemed ta
assert.equal(canViewerCloseTask({ task_status: 'link_generated' }, viewerContext), true) assert.equal(canViewerCloseTask({ task_status: 'link_generated' }, viewerContext), true)
assert.equal(canViewerCloseTask({ task_status: 'redeemed' }, viewerContext), false) assert.equal(canViewerCloseTask({ task_status: 'redeemed' }, viewerContext), false)
}) })
test('mapKuaishouCloudFulfillmentContext exposes cloud account display labels', () => {
const flow = mapKuaishouCloudFulfillmentContext(
{
binding: {
cloudSourceKeys: [' account_a ', 'account_b'],
resolvedSourceKey: 'account_b',
},
},
{
cloudSourceLabelMap: new Map([
['account_a', '白班账号'],
['account_b', '夜班账号'],
]),
},
)
assert.deepEqual(flow?.binding.cloudSourceKeys, ['account_a', 'account_b'])
assert.deepEqual(flow?.binding.cloudSourceLabels, ['白班账号', '夜班账号'])
assert.equal(flow?.binding.resolvedSourceLabel, '夜班账号')
})
@@ -9,6 +9,11 @@ import type { AdminViewerSessionInput } from '../../types/admin/read-inputs.js'
import type { OrderItemRow, TaskRow } from '../../types/repository/rows.js' import type { OrderItemRow, TaskRow } from '../../types/repository/rows.js'
type JsonRecord = Record<string, any> type JsonRecord = Record<string, any>
type CloudSourceLabelMap = Map<string, string> | Record<string, string>
type KuaishouCloudFulfillmentMapOptions = {
cloudSourceLabelMap?: CloudSourceLabelMap
}
type TaskLike = Partial<TaskRow> & { type TaskLike = Partial<TaskRow> & {
state_json?: string | JsonRecord state_json?: string | JsonRecord
@@ -55,7 +60,10 @@ export function mapManualDispatchContext(
} }
} }
export function mapKuaishouCloudFulfillmentContext(value: unknown): JsonRecord | null { export function mapKuaishouCloudFulfillmentContext(
value: unknown,
options: KuaishouCloudFulfillmentMapOptions = {},
): JsonRecord | null {
if (!value || typeof value !== 'object') { if (!value || typeof value !== 'object') {
return null return null
} }
@@ -68,6 +76,10 @@ export function mapKuaishouCloudFulfillmentContext(value: unknown): JsonRecord |
const dispatch = record.dispatch && typeof record.dispatch === 'object' ? record.dispatch : {} const dispatch = record.dispatch && typeof record.dispatch === 'object' ? record.dispatch : {}
const returnNumber = record.returnNumber && typeof record.returnNumber === 'object' ? record.returnNumber : {} const returnNumber = record.returnNumber && typeof record.returnNumber === 'object' ? record.returnNumber : {}
const consume = record.consume && typeof record.consume === 'object' ? record.consume : {} const consume = record.consume && typeof record.consume === 'object' ? record.consume : {}
const cloudSourceKeys = Array.isArray(binding.cloudSourceKeys)
? binding.cloudSourceKeys.map((value: unknown) => String(value || '').trim()).filter(Boolean)
: []
const resolvedSourceKey = String(binding.resolvedSourceKey || '').trim()
return { return {
flowType: String(record.flowType || '').trim(), flowType: String(record.flowType || '').trim(),
@@ -84,10 +96,14 @@ export function mapKuaishouCloudFulfillmentContext(value: unknown): JsonRecord |
}, },
binding: { binding: {
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending', prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
cloudSourceKeys: Array.isArray(binding.cloudSourceKeys) cloudSourceKeys,
? binding.cloudSourceKeys.map((value: unknown) => String(value || '').trim()).filter(Boolean) cloudSourceLabels: cloudSourceKeys.map((sourceKey: string) =>
: [], resolveCloudSourceLabel(sourceKey, options.cloudSourceLabelMap),
resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(), ),
resolvedSourceKey,
resolvedSourceLabel: resolvedSourceKey
? resolveCloudSourceLabel(resolvedSourceKey, options.cloudSourceLabelMap)
: '',
skuId: Number(binding.skuId || 0) || 0, skuId: Number(binding.skuId || 0) || 0,
skuName: String(binding.skuName || '').trim(), skuName: String(binding.skuName || '').trim(),
vnKey: String(binding.vnKey || '').trim(), vnKey: String(binding.vnKey || '').trim(),
@@ -140,6 +156,17 @@ export function mapKuaishouCloudFulfillmentContext(value: unknown): JsonRecord |
} }
} }
function resolveCloudSourceLabel(sourceKey: string, labelMap: CloudSourceLabelMap | undefined) {
const key = String(sourceKey || '').trim()
if (!key) return ''
const label = labelMap instanceof Map
? labelMap.get(key)
: labelMap?.[key]
return String(label || key).trim() || key
}
export function mapRedeemResolutionContext(value: unknown): JsonRecord | null { export function mapRedeemResolutionContext(value: unknown): JsonRecord | null {
if (!value || typeof value !== 'object') { if (!value || typeof value !== 'object') {
return null return null
+2
View File
@@ -127,7 +127,9 @@ export interface AdminTaskDetail {
binding: { binding: {
prepareStatus: string prepareStatus: string
cloudSourceKeys: string[] cloudSourceKeys: string[]
cloudSourceLabels: string[]
resolvedSourceKey: string resolvedSourceKey: string
resolvedSourceLabel: string
skuId: number skuId: number
skuName: string skuName: string
vnKey: string vnKey: string
@@ -34,6 +34,30 @@ const emit = defineEmits<{
copyBindUrl: [url: string] copyBindUrl: [url: string]
openBindUrl: [url: string] openBindUrl: [url: string]
}>() }>()
function formatCloudSourcePriority(flow: KuaishouCloudFlow) {
return formatAccountNames(flow.binding.cloudSourceLabels, flow.binding.cloudSourceKeys)
}
function formatResolvedCloudSource(flow: KuaishouCloudFlow) {
return flow.binding.resolvedSourceLabel || flow.binding.resolvedSourceKey || '-'
}
function formatAccountNames(labels: string[] | undefined, keys: string[] | undefined) {
const displayNames = Array.isArray(labels)
? labels.map((value) => String(value || '').trim()).filter(Boolean)
: []
if (displayNames.length > 0) {
return displayNames.join(' / ')
}
const fallbackKeys = Array.isArray(keys)
? keys.map((value) => String(value || '').trim()).filter(Boolean)
: []
return fallbackKeys.length > 0 ? fallbackKeys.join(' / ') : '-'
}
</script> </script>
<template> <template>
@@ -222,13 +246,11 @@ const emit = defineEmits<{
<div class="flow-meta-list flow-meta-list--two-column"> <div class="flow-meta-list flow-meta-list--two-column">
<div> <div>
<span>账号优先级</span> <span>账号优先级</span>
<strong>{{ <strong>{{ formatCloudSourcePriority(flow) }}</strong>
flow.binding.cloudSourceKeys?.length ? flow.binding.cloudSourceKeys.join(' / ') : '-'
}}</strong>
</div> </div>
<div> <div>
<span>实际使用账号</span> <span>实际使用账号</span>
<strong>{{ flow.binding.resolvedSourceKey || '-' }}</strong> <strong>{{ formatResolvedCloudSource(flow) }}</strong>
</div> </div>
<div> <div>
<span>cloud SKU</span> <span>cloud SKU</span>