feat: 资产汇总表按品牌型号展示交车/还车/替换/待交车数据
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -267,7 +267,56 @@ function getRegionCounts(vehicles: Vehicle[], regions: readonly string[]): Recor
|
|||||||
}, {} as Record<string, number>);
|
}, {} as Record<string, number>);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStats(list: Vehicle[]) {
|
// Weekly truck ID sets, cached
|
||||||
|
interface WeeklyTruckIds {
|
||||||
|
pending: Set<number>;
|
||||||
|
delivered: Set<number>;
|
||||||
|
returned: Set<number>;
|
||||||
|
replaced: Set<number>;
|
||||||
|
}
|
||||||
|
let cachedWeeklyTruckIds: WeeklyTruckIds | null = null;
|
||||||
|
let weeklyTruckIdsLastFetch = 0;
|
||||||
|
|
||||||
|
async function getWeeklyTruckIds(): Promise<WeeklyTruckIds> {
|
||||||
|
const now = Date.now();
|
||||||
|
if (cachedWeeklyTruckIds && now - weeklyTruckIdsLastFetch < CACHE_TTL) {
|
||||||
|
return cachedWeeklyTruckIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [[pendingRows], [deliveredRows], [returnedRows], [replacedRows]] = await Promise.all([
|
||||||
|
pool.query<any[]>(`SELECT id AS truck_id FROM tab_truck WHERE is_deleted=0 AND is_operation=1 AND truck_rent_status=7`),
|
||||||
|
pool.query<any[]>(`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<any[]>(`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<any[]>(`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 {
|
return {
|
||||||
total: list.length,
|
total: list.length,
|
||||||
inventory: list.filter((v) => v.status === 'Inventory').length,
|
inventory: list.filter((v) => v.status === 'Inventory').length,
|
||||||
@@ -275,11 +324,11 @@ function getStats(list: Vehicle[]) {
|
|||||||
list.filter((v) => v.status === 'Inventory'),
|
list.filter((v) => v.status === 'Inventory'),
|
||||||
REGIONS,
|
REGIONS,
|
||||||
),
|
),
|
||||||
pending: 0,
|
pending: weeklyIds ? ids.filter((id) => weeklyIds.pending.has(id)).length : 0,
|
||||||
operating: list.filter((v) => v.status === 'Operating').length,
|
operating: list.filter((v) => v.status === 'Operating').length,
|
||||||
weeklyDelivered: 0,
|
weeklyDelivered: weeklyIds ? ids.filter((id) => weeklyIds.delivered.has(id)).length : 0,
|
||||||
weeklyReturned: 0,
|
weeklyReturned: weeklyIds ? ids.filter((id) => weeklyIds.returned.has(id)).length : 0,
|
||||||
weeklyReplaced: 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
|
// GET /api/vehicles/by-type
|
||||||
app.get('/by-type', async (c) => {
|
app.get('/by-type', async (c) => {
|
||||||
const vehicles = await getVehicles();
|
const [vehicles, weeklyIds] = await Promise.all([getVehicles(), getWeeklyTruckIds()]);
|
||||||
|
|
||||||
const typeFilters = [
|
const typeFilters = [
|
||||||
{ name: '4.5T普货', filter: (v: Vehicle) => v.type === '4.5T' && !v.model.includes('冷链') },
|
{ 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 modelSummaries: ModelSummary[] = models.map((model) => {
|
||||||
const modelVehicles = typeVehicles.filter((v) => v.model === 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);
|
const batches = Array.from(new Set(modelVehicles.map((v) => v.contractNo || '未知'))).filter(Boolean);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
model,
|
model,
|
||||||
...getStats(modelVehicles),
|
...getStats(modelVehicles, weeklyIds),
|
||||||
batches: batches.map((batch) => ({
|
batches: batches.map((batch) => ({
|
||||||
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 {
|
return {
|
||||||
type: t.name,
|
type: t.name,
|
||||||
totalAssets: typeVehicles.length,
|
totalAssets: typeVehicles.length,
|
||||||
|
|||||||
Reference in New Issue
Block a user