增加接单分类和大厅分类配额

This commit is contained in:
yml2213
2026-08-23 17:37:32 +08:00
parent 844f071aab
commit 1cebdd0487
18 changed files with 297 additions and 61 deletions
@@ -6,6 +6,7 @@ import {
import {
createWorkOrder,
createWorkOrderEvent,
getWorkCategoryById,
listWorkOrdersByPlatformOrderId,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
@@ -47,6 +48,18 @@ export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
: 0
: normalizeAmountFen(payload.requiredDepositAmount, 0)
const paymentFen = input.paymentAmount
const requestedCategoryId = Number(payload.categoryId || payload.category_id || 0)
let categoryId = defaults.category?.id || null
if (requestedCategoryId > 0) {
const category = await getWorkCategoryById(requestedCategoryId)
if (!category || category.status !== 'active') {
throw createHttpError('所选接单分类不存在或已停用', {
statusCode: 409,
errorCode: 'mock_work_order_category_invalid',
})
}
categoryId = Number(category.id)
}
const materialComplete = payload.materialComplete === true || payload.materialComplete === 'true'
const fields = normalizeRequirementFields(payload.fields)
const { platformOrderId, productName, shop } = input
@@ -92,7 +105,7 @@ export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
workOrderNo: randomId('WO'),
platformOrderId,
productName,
categoryId: defaults.category?.id || null,
categoryId,
status: materialComplete ? WORK_ORDER_STATUS.UNASSIGNED : WORK_ORDER_STATUS.PENDING_MATERIAL,
rewardAmount,
requiredDepositAmount,
@@ -250,6 +250,7 @@ export async function publishAdminWorkOrder(workOrderId: number | string, actorN
workOrderId: Number(workOrder.id),
statuses: [WORK_ORDER_STATUS.OPEN],
hallCandidateLimit: hallConfig.maxVisibleOrders,
hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
excludeFilledSharing: true,
})
return {
@@ -30,6 +30,7 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
const page = normalizePage(query.page)
const pageSize = normalizePageSize(query.pageSize)
const workOrderId = normalizeOptionalId(query.workOrderId ?? query.work_order_id) || 0
const categoryId = normalizeOptionalId(query.categoryId ?? query.category_id) || 0
const workerId = normalizeOptionalId(query.workerId ?? query.worker_id) || 0
const workerLevelIds = normalizeStatuses(query.workerLevelIds ?? query.worker_level_ids)
.map(normalizeOptionalId)
@@ -43,6 +44,7 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
const filters = {
statuses,
workOrderId,
categoryId,
keyword: String(query.keyword || '').trim(),
workerSharingId: workerId,
workerLevelIds,
@@ -5,9 +5,44 @@ import { getWorkerHallConfig, saveWorkerHallConfig } from './worker-hall-config-
export async function getAdminWorkerPlatformSummary() {
const hallConfig = getWorkerHallConfig()
const [pendingWorkers, pendingMaterial, openOrders, inProgressOrders] = await Promise.all([
listWorkerUsers({ page: 1, pageSize: 1, status: 'pending_review' }),
listWorkOrders({ page: 1, pageSize: 1, statuses: [WORK_ORDER_STATUS.PENDING_MATERIAL] }),
const [pendingWorkers, pendingMaterial, openOrders, visibleOpenOrders, inProgressOrders] =
await Promise.all([
listWorkerUsers({ page: 1, pageSize: 1, status: 'pending_review' }),
listWorkOrders({ page: 1, pageSize: 1, statuses: [WORK_ORDER_STATUS.PENDING_MATERIAL] }),
listWorkOrders({
page: 1,
pageSize: 1,
statuses: [WORK_ORDER_STATUS.OPEN],
excludeFilledSharing: true,
}),
listWorkOrders({
page: 1,
pageSize: 1,
statuses: [WORK_ORDER_STATUS.OPEN],
hallCandidateLimit: hallConfig.maxVisibleOrders,
hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
excludeFilledSharing: true,
}),
listWorkOrders({
page: 1,
pageSize: 1,
statuses: [WORK_ORDER_STATUS.IN_PROGRESS],
useOperationalStatus: true,
}),
])
return {
pendingWorkerCount: pendingWorkers.total,
pendingMaterialCount: pendingMaterial.total,
openOrderCount: visibleOpenOrders.total,
queuedHallOrderCount: Math.max(0, openOrders.total - visibleOpenOrders.total),
hallMaxVisibleOrders: hallConfig.maxVisibleOrders,
inProgressOrderCount: inProgressOrders.total,
}
}
export async function getAdminWorkerHallConfig() {
const config = getWorkerHallConfig()
const [openOrders, visibleOpenOrders] = await Promise.all([
listWorkOrders({
page: 1,
pageSize: 1,
@@ -17,32 +52,16 @@ export async function getAdminWorkerPlatformSummary() {
listWorkOrders({
page: 1,
pageSize: 1,
statuses: [WORK_ORDER_STATUS.IN_PROGRESS],
useOperationalStatus: true,
statuses: [WORK_ORDER_STATUS.OPEN],
hallCandidateLimit: config.maxVisibleOrders,
hallCategoryLimits: config.categoryMaxVisibleOrders,
excludeFilledSharing: true,
}),
])
return {
pendingWorkerCount: pendingWorkers.total,
pendingMaterialCount: pendingMaterial.total,
openOrderCount: Math.min(openOrders.total, hallConfig.maxVisibleOrders),
queuedHallOrderCount: Math.max(0, openOrders.total - hallConfig.maxVisibleOrders),
hallMaxVisibleOrders: hallConfig.maxVisibleOrders,
inProgressOrderCount: inProgressOrders.total,
}
}
export async function getAdminWorkerHallConfig() {
const config = getWorkerHallConfig()
const openOrders = await listWorkOrders({
page: 1,
pageSize: 1,
statuses: [WORK_ORDER_STATUS.OPEN],
excludeFilledSharing: true,
})
return {
...config,
visibleOrderCount: Math.min(openOrders.total, config.maxVisibleOrders),
queuedOrderCount: Math.max(0, openOrders.total - config.maxVisibleOrders),
visibleOrderCount: visibleOpenOrders.total,
queuedOrderCount: Math.max(0, openOrders.total - visibleOpenOrders.total),
}
}
@@ -55,6 +55,7 @@ export async function grabWorkerHallOrder(workOrderId: number | string, session:
takeRemaining: true,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
visibleAfterIso,
now,
})
@@ -91,6 +92,7 @@ export async function grabWorkerHallOrder(workOrderId: number | string, session:
depositAmount: freezeAmount,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
visibleAfterIso,
deadlineAt: resolveWorkOrderDeadlineAt(workOrder, now),
now,
@@ -143,6 +145,7 @@ export async function joinWorkerSharingOrder(
quantity,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
visibleAfterIso,
now,
})
@@ -7,17 +7,36 @@ import {
} from './worker-hall-config-service.js'
test('大厅配置默认展示 50 个订单', () => {
assert.deepEqual(createDefaultWorkerHallConfig(), { maxVisibleOrders: 50 })
assert.deepEqual(normalizeWorkerHallConfig({}), { maxVisibleOrders: 50 })
assert.deepEqual(createDefaultWorkerHallConfig(), {
maxVisibleOrders: 50,
categoryMaxVisibleOrders: {},
})
assert.deepEqual(normalizeWorkerHallConfig({}), {
maxVisibleOrders: 50,
categoryMaxVisibleOrders: {},
})
})
test('大厅配置只接受 1 到 500 的整数', () => {
assert.deepEqual(normalizeWorkerHallConfig({ maxVisibleOrders: 88 }), { maxVisibleOrders: 88 })
assert.deepEqual(normalizeWorkerHallConfig({ maxVisibleOrders: 88 }), {
maxVisibleOrders: 88,
categoryMaxVisibleOrders: {},
})
assert.deepEqual(normalizeWorkerHallConfig({ max_visible_orders: 999 }), {
maxVisibleOrders: 500,
categoryMaxVisibleOrders: {},
})
assert.deepEqual(normalizeWorkerHallConfig({ maxVisibleOrders: 0 }), {
maxVisibleOrders: 1,
categoryMaxVisibleOrders: {},
})
assert.deepEqual(normalizeWorkerHallConfig({ maxVisibleOrders: 0 }), { maxVisibleOrders: 1 })
assert.deepEqual(normalizeWorkerHallConfig({ maxVisibleOrders: 1.5 }), {
maxVisibleOrders: 50,
categoryMaxVisibleOrders: {},
})
assert.deepEqual(
normalizeWorkerHallConfig({ categoryMaxVisibleOrders: { '1': 12, '2': 0, '3': 501 } })
.categoryMaxVisibleOrders,
{ '1': 12 },
)
})
@@ -4,6 +4,7 @@ import { readAppConfigEntry, saveAppConfigEntry } from '../config/app-config-sto
export type WorkerHallConfig = {
maxVisibleOrders: number
categoryMaxVisibleOrders: Record<string, number>
}
export function getWorkerHallConfig(): WorkerHallConfig {
@@ -23,7 +24,7 @@ export async function saveWorkerHallConfig(rawValue: unknown): Promise<WorkerHal
}
export function createDefaultWorkerHallConfig(): WorkerHallConfig {
return { maxVisibleOrders: 50 }
return { maxVisibleOrders: 50, categoryMaxVisibleOrders: {} }
}
export function normalizeWorkerHallConfig(rawValue: unknown): WorkerHallConfig {
@@ -31,9 +32,24 @@ export function normalizeWorkerHallConfig(rawValue: unknown): WorkerHallConfig {
const value = Number(source.maxVisibleOrders ?? source.max_visible_orders)
return {
maxVisibleOrders: Number.isInteger(value) ? Math.min(500, Math.max(1, value)) : 50,
categoryMaxVisibleOrders: normalizeCategoryMaxVisibleOrders(
source.categoryMaxVisibleOrders ?? source.category_max_visible_orders,
),
}
}
function normalizeCategoryMaxVisibleOrders(value: unknown): Record<string, number> {
if (!isPlainObject(value)) return {}
return Object.entries(value).reduce<Record<string, number>>((result, [categoryId, raw]) => {
const id = Number(categoryId)
const limit = Number(raw)
if (Number.isInteger(id) && id > 0 && Number.isInteger(limit) && limit >= 1 && limit <= 500) {
result[String(id)] = limit
}
return result
}, {})
}
function isPlainObject(value: unknown): value is JsonObject {
return Object.prototype.toString.call(value) === '[object Object]'
}
@@ -39,6 +39,7 @@ export async function listWorkerHallOrders(query: JsonObject = {}, session: Work
keyword: String(query.keyword || '').trim(),
categoryId: categoryId || 0,
hallCandidateLimit: hallConfig.maxVisibleOrders,
hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
visibleAfterIso,
excludeFilledSharing: true,
pinnedFirst: true,