把 task 的 helper独立出来了

This commit is contained in:
yml2213
2026-04-14 10:12:11 +08:00
parent 335eb44104
commit 9ca15fc503
21 changed files with 1202 additions and 824 deletions
@@ -0,0 +1,246 @@
// @ts-check
import { getAgisoShopConfig } from '../platforms/agiso/shop-config-service.js'
import { safeParseJson } from './admin-query-utils.js'
import { normalizeAdminRole } from './admin-auth-service.js'
export function mapManualDispatchContext(value, viewerContext = createAdminViewerContext()) {
if (!value || typeof value !== 'object') {
return null
}
return {
outcome: String(value.outcome || '').trim(),
deliveryReference: String(value.deliveryReference || '').trim(),
deliveredCredential: viewerContext.canViewSensitiveTaskData ? String(value.deliveredCredential || '').trim() : '',
resultMessage: String(value.resultMessage || '').trim(),
completedAt: value.completedAt || null,
completedBy: value.completedBy && typeof value.completedBy === 'object'
? {
userId: Number(value.completedBy.userId || 0) || 0,
username: String(value.completedBy.username || '').trim(),
role: String(value.completedBy.role || '').trim(),
}
: null,
}
}
export function mapRedeemResolutionContext(value) {
if (!value || typeof value !== 'object') {
return null
}
const attempts = Array.isArray(value.attempts)
? value.attempts
.map((attempt) => mapRedeemResolutionAttempt(attempt))
.filter(Boolean)
: []
return {
status: String(value.status || '').trim(),
taskStatus: String(value.taskStatus || '').trim(),
replacementCount: Math.max(0, Number(value.replacementCount || 0)),
finishedAt: value.finishedAt || null,
attempts,
}
}
export function getTaskPrimaryInventoryItemId(task) {
const value = Number(task?.primary_inventory_item_id || 0)
return Number.isFinite(value) && value > 0 ? value : null
}
export function getTaskPrimaryClaimTokenId(task) {
const value = Number(task?.primary_claim_token_id || 0)
return Number.isFinite(value) && value > 0 ? value : null
}
export function createAdminViewerContext(session = null) {
const role = normalizeAdminRole(session?.role)
return {
role,
canViewSensitiveTaskData: role === 'admin' || role === 'operator',
canManageSensitiveInventory: role === 'admin',
canManageTaskLifecycle: role === 'admin' || role === 'operator',
canOperateAssistedTask: role === 'admin' || role === 'operator' || role === 'support',
}
}
export async function resolveAdminTaskScreenshotUrl(task, viewerContext) {
if (viewerContext.role === 'support') {
return task.browser_session_id ? `/api/v1/admin/tasks/${task.id}/screenshot` : ''
}
if (task.screenshot_path || task.browser_session_id) {
return `/api/v1/admin/tasks/${task.id}/screenshot`
}
return ''
}
export function resolveDisplayShopName(provider, shopId, shopName) {
const normalizedShopName = String(shopName || '').trim()
if (normalizedShopName) {
return normalizedShopName
}
const normalizedShopId = String(shopId || '').trim()
if (!normalizedShopId) {
return ''
}
if (String(provider || '').trim().toLowerCase() === 'agiso') {
const configuredName = String(getAgisoShopConfig(normalizedShopId)?.shopName || '').trim()
if (configuredName) {
return configuredName
}
}
return normalizedShopId
}
export function parseTaskContext(task) {
const value = task?.context_json
if (!value) {
return {}
}
if (typeof value === 'object') {
return value
}
try {
return JSON.parse(String(value || '{}'))
} catch {
return {}
}
}
export function parseTaskState(task) {
const value = task?.state_json
if (!value) {
return {}
}
if (typeof value === 'object') {
return value
}
try {
return JSON.parse(String(value || '{}'))
} catch {
return {}
}
}
export function resolveOrderItemTitle(item) {
if (!item) {
return ''
}
const spec = safeParseJson(item.spec_json)
return pickFirstNonEmpty([
spec.title,
spec.Title,
spec.itemTitle,
spec.item_title,
spec.goods_name,
spec.goodsName,
item.sku_name,
item.sku_code,
])
}
export function resolveOrderItemDeliveryMode(tasks, orderItemId) {
const task = (Array.isArray(tasks) ? tasks : []).find((item) => item.order_item_id === orderItemId)
if (!task) {
return ''
}
if (String(task.executor_key || '').trim() === 'manual_dispatch') {
return 'manual_dispatch'
}
if (task.requires_claim || getTaskPrimaryClaimTokenId(task) || task.primary_claim_token || task.claim_token) {
return 'claim_link'
}
return String(task.executor_key || '').trim()
}
export function isManualDispatchTask(task) {
return String(task?.executor_key || '').trim() === 'manual_dispatch'
}
export function isAssistedClaimTask(task) {
return String(task?.executor_key || '').trim() === 'tencent_claim_assisted'
}
export function canRegenerateClaimLinkForViewer(task, viewerContext) {
if (isManualDispatchTask(task)) {
return false
}
const baseAllowed = ['link_generated', 'claimed', 'role_confirmed', 'retry_pending', 'manual_review'].includes(task.task_status)
if (!baseAllowed) {
return false
}
if (viewerContext.canManageTaskLifecycle) {
return true
}
return viewerContext.canOperateAssistedTask && isAssistedClaimTask(task)
}
export function canViewerConfirmAssistedRole(task, viewerContext) {
if (!viewerContext.canOperateAssistedTask || !isAssistedClaimTask(task)) {
return false
}
return String(task?.task_status || '').trim() === 'claimed'
}
export function canViewerRedeemAssistedTask(task, viewerContext) {
if (!viewerContext.canOperateAssistedTask || !isAssistedClaimTask(task)) {
return false
}
return ['role_confirmed', 'redeeming'].includes(String(task?.task_status || '').trim())
}
function mapRedeemResolutionAttempt(value) {
if (!value || typeof value !== 'object') {
return null
}
return {
attempt: Math.max(1, Number(value.attempt || 1)),
inventoryItemId: Number(value.inventoryItemId || 0) || null,
codeMasked: String(value.codeMasked || '').trim(),
credentialType: String(value.credentialType || '').trim(),
outcome: String(value.outcome || '').trim(),
resultCode: String(value.resultCode || '').trim(),
resultMessage: String(value.resultMessage || '').trim(),
}
}
function pickFirstNonEmpty(values) {
for (const value of values) {
if (typeof value === 'string' && value.trim()) {
return value.trim()
}
if (typeof value === 'number' && Number.isFinite(value)) {
return String(value)
}
}
return ''
}