From 1076addeada40c43caf33611a3497169427daf42 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 21 Aug 2026 16:49:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E6=89=93=E6=89=8B=E5=A4=A7?= =?UTF-8?q?=E5=8E=85=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/services/worker-platform/index.ts | 1 + .../worker-platform/worker-hall-service.ts | 115 ++++++++++++++++++ .../worker-platform/worker-service.ts | 101 +-------------- 3 files changed, 121 insertions(+), 96 deletions(-) create mode 100644 apps/backend/src/services/worker-platform/worker-hall-service.ts diff --git a/apps/backend/src/services/worker-platform/index.ts b/apps/backend/src/services/worker-platform/index.ts index 5f8358eb..49e59b07 100644 --- a/apps/backend/src/services/worker-platform/index.ts +++ b/apps/backend/src/services/worker-platform/index.ts @@ -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' diff --git a/apps/backend/src/services/worker-platform/worker-hall-service.ts b/apps/backend/src/services/worker-platform/worker-hall-service.ts new file mode 100644 index 00000000..2ff44a3d --- /dev/null +++ b/apps/backend/src/services/worker-platform/worker-hall-service.ts @@ -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}****` : '****' +} diff --git a/apps/backend/src/services/worker-platform/worker-service.ts b/apps/backend/src/services/worker-platform/worker-service.ts index c438a5dd..2be44834 100644 --- a/apps/backend/src/services/worker-platform/worker-service.ts +++ b/apps/backend/src/services/worker-platform/worker-service.ts @@ -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)