拆分打手大厅服务
This commit is contained in:
@@ -9,6 +9,7 @@ export * from './worker-sms-service.js'
|
|||||||
export * from './worker-registration-service.js'
|
export * from './worker-registration-service.js'
|
||||||
export * from './worker-login-service.js'
|
export * from './worker-login-service.js'
|
||||||
export * from './worker-profile-service.js'
|
export * from './worker-profile-service.js'
|
||||||
|
export * from './worker-hall-service.js'
|
||||||
export * from './worker-platform-defaults.js'
|
export * from './worker-platform-defaults.js'
|
||||||
export * from './work-order-timeout-policy.js'
|
export * from './work-order-timeout-policy.js'
|
||||||
export * from './after-sales-service.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,
|
touchWorkerSession,
|
||||||
joinWorkOrderShare,
|
joinWorkOrderShare,
|
||||||
listPendingMaterialWorkOrdersByPlatformOrderId,
|
listPendingMaterialWorkOrdersByPlatformOrderId,
|
||||||
listWorkerCompletionLeaderboard,
|
|
||||||
listWorkerCancelRequests,
|
listWorkerCancelRequests,
|
||||||
listWorkerOrderFeedbacks,
|
listWorkerOrderFeedbacks,
|
||||||
listWorkerSharesByWorker,
|
listWorkerSharesByWorker,
|
||||||
listWorkerWorkOrderNotes,
|
listWorkerWorkOrderNotes,
|
||||||
listWorkerWorkOrderViews,
|
listWorkerWorkOrderViews,
|
||||||
listWorkCategories,
|
|
||||||
listWorkOrders,
|
listWorkOrders,
|
||||||
listWorkOrderEventsByOrderId,
|
listWorkOrderEventsByOrderId,
|
||||||
listWorkOrderShares,
|
listWorkOrderShares,
|
||||||
listWorkOrderSharesByOrderIds,
|
|
||||||
getWorkerCancelRequestRisk,
|
getWorkerCancelRequestRisk,
|
||||||
listOverdueWorkOrders,
|
listOverdueWorkOrders,
|
||||||
settleOverdueWorkOrder,
|
settleOverdueWorkOrder,
|
||||||
@@ -79,7 +76,6 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
mapCollectLookupOrder,
|
mapCollectLookupOrder,
|
||||||
mapWorkCategory,
|
|
||||||
mapWorkOrderForWorker,
|
mapWorkOrderForWorker,
|
||||||
mapWorkOrderPublic,
|
mapWorkOrderPublic,
|
||||||
mapWorkOrderEvents,
|
mapWorkOrderEvents,
|
||||||
@@ -175,98 +171,11 @@ export {
|
|||||||
saveWorkerAlipayWithdrawalQrCode,
|
saveWorkerAlipayWithdrawalQrCode,
|
||||||
} from './worker-profile-service.js'
|
} from './worker-profile-service.js'
|
||||||
|
|
||||||
export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
|
export {
|
||||||
requireActiveWorkerSession(session)
|
getWorkerHallLeaderboard,
|
||||||
const page = normalizePage(query.page)
|
listWorkerHallOrders,
|
||||||
const pageSize = normalizePageSize(query.pageSize)
|
maskWorkerLeaderboardDisplayName,
|
||||||
const categoryId = normalizeOptionalId(query.categoryId)
|
} from './worker-hall-service.js'
|
||||||
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 async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
|
export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
|
||||||
requireActiveWorkerSession(session)
|
requireActiveWorkerSession(session)
|
||||||
|
|||||||
Reference in New Issue
Block a user