拆分工单事件映射

This commit is contained in:
yml2213
2026-08-21 18:21:04 +08:00
parent 1e16b42c7d
commit 8c058d71b1
2 changed files with 111 additions and 114 deletions
@@ -1158,117 +1158,4 @@ export const IGNORABLE_WORKER_AUTH_ERROR_CODES = new Set([
'worker_auth_device_revoked',
])
export type WorkOrderEventView = {
id: number
time: string
actorType: string
actorName: string
eventType: string
fromStatus: string
toStatus: string
payload: JsonObject
/** 仅管理端流转记录返回,供查看事件涉及的打手信息。 */
worker?: {
workerId: number
displayName: string
username: string
workerType: string
levelName: string
status: string
}
}
/**
* 将工单事件原始行映射为带操作人显示名的视图。
* admin 事件的操作人来自 actor_id(管理员用户名);worker 事件按打手表解析显示名。
*/
export async function mapWorkOrderEvents(
events: Array<{
id: number
actor_type: string
actor_id: string
event_type: string
from_status: string
to_status: string
payload_json: string | Record<string, unknown>
created_at: string
}>,
options: { includeWorkerSummary?: boolean } = {},
): Promise<WorkOrderEventView[]> {
const workerIds = [
...new Set(
events.map(resolveWorkOrderEventWorkerId).filter((id) => Number.isFinite(id) && id > 0),
),
]
const workers = await Promise.all(workerIds.map((workerId) => getWorkerUserById(workerId)))
const workerById = new Map(
workers
.filter((worker): worker is WorkerUserRow => Boolean(worker))
.map((worker) => [Number(worker.id), worker]),
)
const workerNameById = new Map(
[...workerById.entries()].map(([workerId, worker]) => [
workerId,
worker.display_name || worker.username || `打手#${workerId}`,
]),
)
return events.map((event) => {
const workerId = resolveWorkOrderEventWorkerId(event)
const worker = workerById.get(workerId)
return {
id: Number(event.id),
time: new Date(event.created_at).toISOString(),
actorType: event.actor_type,
actorName: resolveWorkOrderEventActorName(event, workerNameById),
eventType: event.event_type,
fromStatus: event.from_status,
toStatus: event.to_status,
payload: safeParseJson(event.payload_json),
...(options.includeWorkerSummary && worker
? {
worker: {
workerId: Number(worker.id),
displayName: worker.display_name || worker.username || `打手#${worker.id}`,
username: worker.username,
workerType: normalizeWorkerType(worker.worker_type),
levelName: worker.level_name || '-',
status: worker.status,
},
}
: {}),
}
})
}
/** 操作人优先,其次读取事件载荷中的关联打手。 */
function resolveWorkOrderEventWorkerId(event: {
actor_type: string
actor_id: string
payload_json: string | Record<string, unknown>
}) {
const actorWorkerId = event.actor_type === 'worker' ? Number(event.actor_id) : 0
if (Number.isInteger(actorWorkerId) && actorWorkerId > 0) return actorWorkerId
const payloadWorkerId = Number(safeParseJson(event.payload_json).workerId || 0)
return Number.isInteger(payloadWorkerId) && payloadWorkerId > 0 ? payloadWorkerId : 0
}
function resolveWorkOrderEventActorName(
event: { actor_type: string; actor_id: string },
workerNameById: Map<number, string>,
) {
const actorId = String(event.actor_id || '').trim()
if (event.actor_type === 'worker') {
return workerNameById.get(Number(actorId)) || (actorId ? `打手#${actorId}` : '打手')
}
if (event.actor_type === 'admin') {
return actorId || '管理员'
}
if (event.actor_type === 'system') {
return '系统'
}
if (event.actor_type === 'collect') {
return '采集'
}
return actorId || '未知'
}
export { mapWorkOrderEvents, type WorkOrderEventView } from './work-order-event-mappers.js'
@@ -0,0 +1,110 @@
import { getWorkerUserById, type WorkerUserRow } from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { safeParseJson } from '../admin/admin-query-utils.js'
export type WorkOrderEventView = {
id: number
time: string
actorType: string
actorName: string
eventType: string
fromStatus: string
toStatus: string
payload: JsonObject
/** 仅管理端流转记录返回,供查看事件涉及的打手信息。 */
worker?: {
workerId: number
displayName: string
username: string
workerType: string
levelName: string
status: string
}
}
export async function mapWorkOrderEvents(
events: Array<{
id: number
actor_type: string
actor_id: string
event_type: string
from_status: string
to_status: string
payload_json: string | Record<string, unknown>
created_at: string
}>,
options: { includeWorkerSummary?: boolean } = {},
): Promise<WorkOrderEventView[]> {
const workerIds = [
...new Set(
events.map(resolveWorkOrderEventWorkerId).filter((id) => Number.isFinite(id) && id > 0),
),
]
const workers = await Promise.all(workerIds.map((workerId) => getWorkerUserById(workerId)))
const workerById = new Map(
workers
.filter((worker): worker is WorkerUserRow => Boolean(worker))
.map((worker) => [Number(worker.id), worker]),
)
const workerNameById = new Map(
[...workerById.entries()].map(([workerId, worker]) => [
workerId,
worker.display_name || worker.username || `打手#${workerId}`,
]),
)
return events.map((event) => {
const workerId = resolveWorkOrderEventWorkerId(event)
const worker = workerById.get(workerId)
return {
id: Number(event.id),
time: new Date(event.created_at).toISOString(),
actorType: event.actor_type,
actorName: resolveWorkOrderEventActorName(event, workerNameById),
eventType: event.event_type,
fromStatus: event.from_status,
toStatus: event.to_status,
payload: safeParseJson(event.payload_json),
...(options.includeWorkerSummary && worker
? {
worker: {
workerId: Number(worker.id),
displayName: worker.display_name || worker.username || `打手#${worker.id}`,
username: worker.username,
workerType: normalizeWorkerType(worker.worker_type),
levelName: worker.level_name || '-',
status: worker.status,
},
}
: {}),
}
})
}
function normalizeWorkerType(value: unknown): 'internal' | 'external' {
return String(value || '').trim() === 'internal' ? 'internal' : 'external'
}
function resolveWorkOrderEventWorkerId(event: {
actor_type: string
actor_id: string
payload_json: string | Record<string, unknown>
}) {
const actorWorkerId = event.actor_type === 'worker' ? Number(event.actor_id) : 0
if (Number.isInteger(actorWorkerId) && actorWorkerId > 0) return actorWorkerId
const payloadWorkerId = Number(safeParseJson(event.payload_json).workerId || 0)
return Number.isInteger(payloadWorkerId) && payloadWorkerId > 0 ? payloadWorkerId : 0
}
function resolveWorkOrderEventActorName(
event: { actor_type: string; actor_id: string },
workerNameById: Map<number, string>,
) {
const actorId = String(event.actor_id || '').trim()
if (event.actor_type === 'worker') {
return workerNameById.get(Number(actorId)) || (actorId ? `打手#${actorId}` : '打手')
}
if (event.actor_type === 'admin') return actorId || '管理员'
if (event.actor_type === 'system') return '系统'
if (event.actor_type === 'collect') return '采集'
return actorId || '未知'
}