拆分打手大厅服务

This commit is contained in:
yml2213
2026-08-21 16:49:58 +08:00
parent 8388d6e875
commit 1076addead
3 changed files with 121 additions and 96 deletions
@@ -9,6 +9,7 @@ export * from './worker-sms-service.js'
export * from './worker-registration-service.js'
export * from './worker-login-service.js'
export * from './worker-profile-service.js'
export * from './worker-hall-service.js'
export * from './worker-platform-defaults.js'
export * from './work-order-timeout-policy.js'
export * from './after-sales-service.js'
@@ -0,0 +1,115 @@
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import {
listWorkCategories,
listWorkerCompletionLeaderboard,
listWorkOrderSharesByOrderIds,
listWorkOrders,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { normalizePage, normalizePageSize } from '../admin/admin-query-utils.js'
import {
mapWorkCategory,
mapWorkOrderForWorker,
mapWorkOrderShare,
normalizeOptionalId,
resolveWorkerPermissions,
} from './mappers.js'
import { getWorkerHallConfig } from './worker-hall-config-service.js'
import {
getRequiredWorker,
requireActiveWorkerSession,
type WorkerSession,
} from './worker-session-context-service.js'
export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
requireActiveWorkerSession(session)
const page = normalizePage(query.page)
const pageSize = normalizePageSize(query.pageSize)
const categoryId = normalizeOptionalId(query.categoryId)
const worker = await getRequiredWorker(session.workerId)
const permissions = resolveWorkerPermissions(worker)
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
const visibleAfterIso =
visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
const hallConfig = getWorkerHallConfig()
const { items, total } = await listWorkOrders({
page,
pageSize,
statuses: [WORK_ORDER_STATUS.OPEN],
keyword: String(query.keyword || '').trim(),
categoryId: categoryId || 0,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
excludeFilledSharing: true,
pinnedFirst: true,
})
const categories = (await listWorkCategories()).map(mapWorkCategory)
const orderIds = items.map((item) => Number(item.id))
const shares = await listWorkOrderSharesByOrderIds(orderIds)
const myShareByOrderId = new Map(
shares
.filter(
(share) => share.status !== 'cancelled' && Number(share.worker_id) === Number(worker.id),
)
.map((share) => [Number(share.work_order_id), share]),
)
const sharingProgressByOrderId = new Map<
number,
{ joinedQuantity: number; pendingSubmissionCount: number }
>()
for (const share of shares) {
if (share.status === 'cancelled') continue
const orderId = Number(share.work_order_id)
const progress = sharingProgressByOrderId.get(orderId) || {
joinedQuantity: 0,
pendingSubmissionCount: 0,
}
progress.joinedQuantity += Number(share.quantity || 0)
if (share.status === 'joined') progress.pendingSubmissionCount += 1
sharingProgressByOrderId.set(orderId, progress)
}
return {
items: items.map((item) => {
const myShare = myShareByOrderId.get(Number(item.id))
return {
...mapWorkOrderForWorker(item, permissions),
myShare: myShare ? mapWorkOrderShare(myShare) : null,
sharingProgress: sharingProgressByOrderId.get(Number(item.id)) || {
joinedQuantity: 0,
pendingSubmissionCount: 0,
},
}
}),
pagination: { page, pageSize, total },
categories,
visibleDelaySeconds,
}
}
export async function getWorkerHallLeaderboard(session: WorkerSession, period: unknown = 'all') {
requireActiveWorkerSession(session)
const leaderboardPeriod = period === 'daily' ? 'daily' : 'all'
const entries = await listWorkerCompletionLeaderboard({
limit: 10,
currentWorkerId: session.workerId,
period: leaderboardPeriod,
})
const current = entries.find((entry) => entry.workerId === session.workerId)
return {
items: entries
.filter((entry) => entry.rank <= 10)
.map((entry) => ({
...entry,
displayName: maskWorkerLeaderboardDisplayName(entry.displayName),
})),
current: {
rank: current?.rank || null,
acceptedOrderCount: current?.acceptedOrderCount || 0,
},
}
}
export function maskWorkerLeaderboardDisplayName(value: unknown): string {
const firstCharacter = Array.from(String(value || '').trim())[0]
return firstCharacter ? `${firstCharacter}****` : '****'
}
@@ -26,17 +26,14 @@ import {
touchWorkerSession,
joinWorkOrderShare,
listPendingMaterialWorkOrdersByPlatformOrderId,
listWorkerCompletionLeaderboard,
listWorkerCancelRequests,
listWorkerOrderFeedbacks,
listWorkerSharesByWorker,
listWorkerWorkOrderNotes,
listWorkerWorkOrderViews,
listWorkCategories,
listWorkOrders,
listWorkOrderEventsByOrderId,
listWorkOrderShares,
listWorkOrderSharesByOrderIds,
getWorkerCancelRequestRisk,
listOverdueWorkOrders,
settleOverdueWorkOrder,
@@ -79,7 +76,6 @@ import {
import {
mapCollectLookupOrder,
mapWorkCategory,
mapWorkOrderForWorker,
mapWorkOrderPublic,
mapWorkOrderEvents,
@@ -175,98 +171,11 @@ export {
saveWorkerAlipayWithdrawalQrCode,
} from './worker-profile-service.js'
export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
requireActiveWorkerSession(session)
const page = normalizePage(query.page)
const pageSize = normalizePageSize(query.pageSize)
const categoryId = normalizeOptionalId(query.categoryId)
const worker = await getRequiredWorker(session.workerId)
const permissions = resolveWorkerPermissions(worker)
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
const visibleAfterIso =
visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
const hallConfig = getWorkerHallConfig()
const { items, total } = await listWorkOrders({
page,
pageSize,
statuses: [WORK_ORDER_STATUS.OPEN],
keyword: String(query.keyword || '').trim(),
categoryId: categoryId || 0,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
excludeFilledSharing: true,
pinnedFirst: true,
})
const categories = (await listWorkCategories()).map(mapWorkCategory)
const orderIds = items.map((item) => Number(item.id))
const shares = await listWorkOrderSharesByOrderIds(orderIds)
const myShareByOrderId = new Map(
shares
.filter(
(share) => share.status !== 'cancelled' && Number(share.worker_id) === Number(worker.id),
)
.map((share) => [Number(share.work_order_id), share]),
)
const sharingProgressByOrderId = new Map<
number,
{ joinedQuantity: number; pendingSubmissionCount: number }
>()
for (const share of shares) {
if (share.status === 'cancelled') continue
const orderId = Number(share.work_order_id)
const progress = sharingProgressByOrderId.get(orderId) || {
joinedQuantity: 0,
pendingSubmissionCount: 0,
}
progress.joinedQuantity += Number(share.quantity || 0)
if (share.status === 'joined') progress.pendingSubmissionCount += 1
sharingProgressByOrderId.set(orderId, progress)
}
return {
items: items.map((item) => {
const myShare = myShareByOrderId.get(Number(item.id))
return {
...mapWorkOrderForWorker(item, permissions),
myShare: myShare ? mapWorkOrderShare(myShare) : null,
sharingProgress: sharingProgressByOrderId.get(Number(item.id)) || {
joinedQuantity: 0,
pendingSubmissionCount: 0,
},
}
}),
pagination: { page, pageSize, total },
categories,
visibleDelaySeconds,
}
}
export async function getWorkerHallLeaderboard(session: WorkerSession, period: unknown = 'all') {
requireActiveWorkerSession(session)
const leaderboardPeriod = period === 'daily' ? 'daily' : 'all'
const entries = await listWorkerCompletionLeaderboard({
limit: 10,
currentWorkerId: session.workerId,
period: leaderboardPeriod,
})
const current = entries.find((entry) => entry.workerId === session.workerId)
return {
items: entries
.filter((entry) => entry.rank <= 10)
.map((entry) => ({
...entry,
displayName: maskWorkerLeaderboardDisplayName(entry.displayName),
})),
current: {
rank: current?.rank || null,
acceptedOrderCount: current?.acceptedOrderCount || 0,
},
}
}
export function maskWorkerLeaderboardDisplayName(value: unknown): string {
const firstCharacter = Array.from(String(value || '').trim())[0]
return firstCharacter ? `${firstCharacter}****` : '****'
}
export {
getWorkerHallLeaderboard,
listWorkerHallOrders,
maskWorkerLeaderboardDisplayName,
} from './worker-hall-service.js'
export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
requireActiveWorkerSession(session)