From db2a401e33426166517ffff194bc4fd68b2f238c Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 21 Aug 2026 11:36:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=89=93=E6=89=8B=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E7=AD=9B=E9=80=89=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repositories/worker-platform-repo.test.ts | 27 ++++++++++ .../worker-platform/work-order-repo.ts | 51 ++++++++++++------- .../pages/admin/panels/WorkOrdersPanel.tsx | 8 +++ 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform-repo.test.ts b/apps/backend/src/repositories/worker-platform-repo.test.ts index 90c07db8..9fb00175 100644 --- a/apps/backend/src/repositories/worker-platform-repo.test.ts +++ b/apps/backend/src/repositories/worker-platform-repo.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert/strict' import test from 'node:test' import { + buildWorkOrderWhere, resolveOutstandingDepositAmount, shouldApplyAutomaticWorkerLevel, } from './worker-platform/index.js' @@ -58,3 +59,29 @@ test('automatic worker level changes only move to a higher threshold', () => { false, ) }) + +test('指定打手的运营状态统计保留可见范围和拼单汇总关联', () => { + const query = buildWorkOrderWhere({ + workerSharingId: 3, + useOperationalStatus: true, + }) + + assert.match(query.joins, /worker_visible/) + assert.match(query.joins, /operational_share_summary/) + assert.match(query.joins, /LEFT JOIN LATERAL/) + assert.ok( + query.joins.indexOf('worker_visible') < query.joins.indexOf('operational_share_summary'), + ) + assert.deepEqual(query.params, [3]) +}) + +test('未指定打手的运营状态统计使用一次全表拼单汇总', () => { + const query = buildWorkOrderWhere({ useOperationalStatus: true }) + + assert.match(query.joins, /GROUP BY wos\.work_order_id/) + assert.match( + query.joins, + /operational_share_summary ON operational_share_summary\.work_order_id = wo\.id/, + ) + assert.doesNotMatch(query.joins, /worker_visible/) +}) diff --git a/apps/backend/src/repositories/worker-platform/work-order-repo.ts b/apps/backend/src/repositories/worker-platform/work-order-repo.ts index 606fe5b6..d9d1c962 100644 --- a/apps/backend/src/repositories/worker-platform/work-order-repo.ts +++ b/apps/backend/src/repositories/worker-platform/work-order-repo.ts @@ -4371,7 +4371,7 @@ async function getWorkOrderByIdWithClient( return result.rows[0] || null } -function buildWorkOrderWhere({ +export function buildWorkOrderWhere({ statuses = [], workOrderId = 0, keyword = '', @@ -4386,30 +4386,18 @@ function buildWorkOrderWhere({ }: ListInput) { const filters: string[] = [] const params: unknown[] = [] - let joins = '' + const joins: string[] = [] let workerSharePlaceholder = '' - if (useOperationalStatus) { - joins += ` - LEFT JOIN ( - SELECT - wos.work_order_id, - SUM(wos.quantity) FILTER (WHERE wos.status != 'cancelled') AS active_share_quantity, - COUNT(*) FILTER (WHERE wos.status = 'joined') AS pending_share_count - FROM work_order_shares wos - GROUP BY wos.work_order_id - ) operational_share_summary ON operational_share_summary.work_order_id = wo.id` - } - if (excludeFilledSharing) { - joins += ` + joins.push(` LEFT JOIN ( SELECT wos.work_order_id, SUM(wos.quantity) FILTER (WHERE wos.status != 'cancelled') AS active_share_quantity FROM work_order_shares wos GROUP BY wos.work_order_id - ) filter_share_summary ON filter_share_summary.work_order_id = wo.id` + ) filter_share_summary ON filter_share_summary.work_order_id = wo.id`) } // 先从打手的整单、历史单和拼单记录组成可见工单集合,再关联工单详情。 @@ -4417,7 +4405,7 @@ function buildWorkOrderWhere({ if (workerSharingId) { params.push(workerSharingId) workerSharePlaceholder = `$${params.length}` - joins = ` + joins.push(` INNER JOIN ( SELECT assigned.id AS work_order_id FROM work_orders assigned @@ -4443,7 +4431,32 @@ function buildWorkOrderWhere({ ) worker_visible ON worker_visible.work_order_id = wo.id LEFT JOIN work_order_shares worker_share ON worker_share.work_order_id = wo.id - AND worker_share.worker_id = ${workerSharePlaceholder}` + AND worker_share.worker_id = ${workerSharePlaceholder}`) + } + + if (useOperationalStatus) { + if (workerSharingId) { + // 已限定打手时只汇总其候选工单,利用 work_order_id 索引避免扫描整张拼单表。 + joins.push(` + LEFT JOIN LATERAL ( + SELECT + SUM(wos.quantity) FILTER (WHERE wos.status != 'cancelled') AS active_share_quantity, + COUNT(*) FILTER (WHERE wos.status = 'joined') AS pending_share_count + FROM work_order_shares wos + WHERE wos.work_order_id = wo.id + ) operational_share_summary ON TRUE`) + } else { + // 无打手范围时一次聚合全部拼单,避免对每笔工单重复执行关联子查询。 + joins.push(` + LEFT JOIN ( + SELECT + wos.work_order_id, + SUM(wos.quantity) FILTER (WHERE wos.status != 'cancelled') AS active_share_quantity, + COUNT(*) FILTER (WHERE wos.status = 'joined') AS pending_share_count + FROM work_order_shares wos + GROUP BY wos.work_order_id + ) operational_share_summary ON operational_share_summary.work_order_id = wo.id`) + } } const normalizedStatuses = [ ...new Set(statuses.map((item) => String(item || '').trim()).filter(Boolean)), @@ -4529,7 +4542,7 @@ function buildWorkOrderWhere({ )`) } return { - joins, + joins: joins.join('\n'), whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '', params, } diff --git a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx index 9ca46fd9..b46bb6f7 100644 --- a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx @@ -1091,6 +1091,14 @@ export default function WorkOrdersPanel() { {canViewStatistics ? ( ) : null} + {ordersQuery.isError ? ( + + ) : null}