feat: 出勤率数据源切换为tab_truck_remote_sync_realtime_info
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- dept-stats和/list的attendance过滤改为查询realtime表的day_mileage - day_mileage>0为出勤,=0为闲置 - 移除旧的ln_vehicle_day_mileage依赖 - 前端恢复出勤率/出勤/闲置的数据显示和下钻功能 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -652,35 +652,16 @@ app.get('/dept-stats', async (c) => {
|
||||
const vehicles = await getVehicles();
|
||||
const withManager = vehicles.filter((v) => v.status === 'Operating');
|
||||
|
||||
// Query mileage data: last 30 days attendance & avg mileage per plate
|
||||
// + today's mileage for idle detection
|
||||
const [[mileageRows], [todayRows]] = await Promise.all([
|
||||
pool.query<any[]>(`
|
||||
SELECT plateNumber,
|
||||
COUNT(CASE WHEN dayMileage > 0 THEN 1 END) AS activeDays,
|
||||
COUNT(*) AS totalDays,
|
||||
AVG(dayMileage) AS avgMileage
|
||||
FROM ln_vehicle_day_mileage
|
||||
WHERE dates >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY plateNumber
|
||||
`),
|
||||
pool.query<any[]>(`
|
||||
SELECT plateNumber, dayMileage
|
||||
FROM ln_vehicle_day_mileage
|
||||
WHERE dates = CURDATE()
|
||||
`),
|
||||
]);
|
||||
const mileageMap = new Map<string, { activeDays: number; totalDays: number; avgMileage: number }>();
|
||||
for (const row of mileageRows as any[]) {
|
||||
mileageMap.set(row.plateNumber, {
|
||||
activeDays: Number(row.activeDays),
|
||||
totalDays: Number(row.totalDays),
|
||||
avgMileage: Number(row.avgMileage),
|
||||
});
|
||||
}
|
||||
// Query realtime day_mileage from tab_truck_remote_sync_realtime_info
|
||||
const [realtimeRows] = await pool.query<any[]>(`
|
||||
SELECT plate_number, day_mileage
|
||||
FROM tab_truck_remote_sync_realtime_info
|
||||
WHERE is_deleted = 0 AND plate_number IS NOT NULL
|
||||
`);
|
||||
const todayMileageMap = new Map<string, number>();
|
||||
for (const row of todayRows as any[]) {
|
||||
todayMileageMap.set(row.plateNumber, Number(row.dayMileage));
|
||||
for (const row of realtimeRows as any[]) {
|
||||
const plate = (row.plate_number || '').trim();
|
||||
if (plate) todayMileageMap.set(plate, Number(row.day_mileage) || 0);
|
||||
}
|
||||
|
||||
const deptMap = new Map<string, Map<string, Vehicle[]>>();
|
||||
@@ -693,15 +674,15 @@ app.get('/dept-stats', async (c) => {
|
||||
mgrMap.get(mgr)!.push(v);
|
||||
}
|
||||
|
||||
// Compute attendance (today) & avg mileage (30d) for a set of vehicles
|
||||
// Compute attendance & avg mileage from realtime data
|
||||
const getMileageStats = (vList: Vehicle[]) => {
|
||||
const todayActive = vList.filter((v) => (todayMileageMap.get(v.plateNumber) || 0) > 0).length;
|
||||
let totalMileage = 0;
|
||||
let count = 0;
|
||||
for (const v of vList) {
|
||||
const m = mileageMap.get(v.plateNumber);
|
||||
if (m) {
|
||||
totalMileage += m.avgMileage;
|
||||
const m = todayMileageMap.get(v.plateNumber);
|
||||
if (m !== undefined && m > 0) {
|
||||
totalMileage += m;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -859,11 +840,11 @@ app.get('/list', async (c) => {
|
||||
|
||||
let filtered = vehicles;
|
||||
|
||||
// attendance filter: active = today mileage > 0, idle = today mileage = 0 (only for Operating vehicles)
|
||||
// attendance filter: active = day_mileage > 0, idle = day_mileage = 0 (only for Operating vehicles)
|
||||
if (attendance === 'active' || attendance === 'idle') {
|
||||
const [todayRows] = await pool.query<any[]>(`SELECT plateNumber, dayMileage FROM ln_vehicle_day_mileage WHERE dates = CURDATE()`);
|
||||
const [realtimeRows] = await pool.query<any[]>(`SELECT plate_number, day_mileage FROM tab_truck_remote_sync_realtime_info WHERE is_deleted = 0 AND plate_number IS NOT NULL`);
|
||||
const todayMap = new Map<string, number>();
|
||||
for (const row of todayRows as any[]) todayMap.set(row.plateNumber, Number(row.dayMileage));
|
||||
for (const row of realtimeRows as any[]) todayMap.set((row.plate_number || '').trim(), Number(row.day_mileage) || 0);
|
||||
filtered = filtered.filter((v) => v.status === 'Operating');
|
||||
if (attendance === 'active') {
|
||||
filtered = filtered.filter((v) => (todayMap.get(v.plateNumber) || 0) > 0);
|
||||
|
||||
Reference in New Issue
Block a user