diff --git a/src/server/routes/vehicles.ts b/src/server/routes/vehicles.ts index 66a6d96..79340e1 100644 --- a/src/server/routes/vehicles.ts +++ b/src/server/routes/vehicles.ts @@ -267,7 +267,56 @@ function getRegionCounts(vehicles: Vehicle[], regions: readonly string[]): Recor }, {} as Record); } -function getStats(list: Vehicle[]) { +// Weekly truck ID sets, cached +interface WeeklyTruckIds { + pending: Set; + delivered: Set; + returned: Set; + replaced: Set; +} +let cachedWeeklyTruckIds: WeeklyTruckIds | null = null; +let weeklyTruckIdsLastFetch = 0; + +async function getWeeklyTruckIds(): Promise { + const now = Date.now(); + if (cachedWeeklyTruckIds && now - weeklyTruckIdsLastFetch < CACHE_TTL) { + return cachedWeeklyTruckIds; + } + + const [[pendingRows], [deliveredRows], [returnedRows], [replacedRows]] = await Promise.all([ + pool.query(`SELECT id AS truck_id FROM tab_truck WHERE is_deleted=0 AND is_operation=1 AND truck_rent_status=7`), + pool.query(`SELECT rent_truck.truck_id FROM tab_truck_rent_take take + LEFT JOIN tab_truck_rent_task task ON task.id = take.truck_rent_task_id + LEFT JOIN tab_contract_rent_truck rent_truck ON rent_truck.id = task.contract_rent_truck_id + WHERE take.is_deleted=0 AND take.take_name IS NOT NULL + AND task.task_type=1 AND task.task_status=1 AND take.update_time IS NOT NULL + AND take.handover_date >= ${WEEK_START_SQL} AND take.handover_date < ${WEEK_END_SQL}`), + pool.query(`SELECT rent_truck.truck_id FROM tab_truck_rent_return r + LEFT JOIN tab_truck_rent_task task ON task.id = r.truck_rent_task_id + LEFT JOIN tab_contract_rent_truck rent_truck ON rent_truck.id = task.contract_rent_truck_id + WHERE r.is_deleted=0 AND r.return_date IS NOT NULL + AND r.return_date >= ${WEEK_START_SQL} AND r.return_date < ${WEEK_END_SQL}`), + pool.query(`SELECT rent_truck.truck_id FROM tab_truck_rent_take take + LEFT JOIN tab_truck_rent_task task ON task.id = take.truck_rent_task_id + LEFT JOIN tab_contract_rent_truck rent_truck ON rent_truck.id = task.contract_rent_truck_id + WHERE take.is_deleted=0 AND take.take_name IS NOT NULL + AND task.task_type=3 AND task.task_status=1 AND take.update_time IS NOT NULL + AND take.handover_date >= ${WEEK_START_SQL} AND take.handover_date < ${WEEK_END_SQL}`), + ]); + + const toSet = (rows: any[]) => new Set((rows as any[]).map((r) => Number(r.truck_id)).filter(Boolean)); + cachedWeeklyTruckIds = { + pending: toSet(pendingRows as any[]), + delivered: toSet(deliveredRows as any[]), + returned: toSet(returnedRows as any[]), + replaced: toSet(replacedRows as any[]), + }; + weeklyTruckIdsLastFetch = now; + return cachedWeeklyTruckIds; +} + +function getStats(list: Vehicle[], weeklyIds?: WeeklyTruckIds) { + const ids = list.map((v) => v.id); return { total: list.length, inventory: list.filter((v) => v.status === 'Inventory').length, @@ -275,11 +324,11 @@ function getStats(list: Vehicle[]) { list.filter((v) => v.status === 'Inventory'), REGIONS, ), - pending: 0, + pending: weeklyIds ? ids.filter((id) => weeklyIds.pending.has(id)).length : 0, operating: list.filter((v) => v.status === 'Operating').length, - weeklyDelivered: 0, - weeklyReturned: 0, - weeklyReplaced: 0, + weeklyDelivered: weeklyIds ? ids.filter((id) => weeklyIds.delivered.has(id)).length : 0, + weeklyReturned: weeklyIds ? ids.filter((id) => weeklyIds.returned.has(id)).length : 0, + weeklyReplaced: weeklyIds ? ids.filter((id) => weeklyIds.replaced.has(id)).length : 0, }; } @@ -419,7 +468,7 @@ app.get('/summary', async (c) => { // GET /api/vehicles/by-type app.get('/by-type', async (c) => { - const vehicles = await getVehicles(); + const [vehicles, weeklyIds] = await Promise.all([getVehicles(), getWeeklyTruckIds()]); const typeFilters = [ { name: '4.5T普货', filter: (v: Vehicle) => v.type === '4.5T' && !v.model.includes('冷链') }, @@ -435,20 +484,19 @@ app.get('/by-type', async (c) => { const modelSummaries: ModelSummary[] = models.map((model) => { const modelVehicles = typeVehicles.filter((v) => v.model === model); - // Use contractNo as batch identifier const batches = Array.from(new Set(modelVehicles.map((v) => v.contractNo || '未知'))).filter(Boolean); return { model, - ...getStats(modelVehicles), + ...getStats(modelVehicles, weeklyIds), batches: batches.map((batch) => ({ batch, - ...getStats(modelVehicles.filter((v) => (v.contractNo || '未知') === batch)), + ...getStats(modelVehicles.filter((v) => (v.contractNo || '未知') === batch), weeklyIds), })), }; }); - const typeStats = getStats(typeVehicles); + const typeStats = getStats(typeVehicles, weeklyIds); return { type: t.name, totalAssets: typeVehicles.length,