Revert "fix(mileage): 已到期年度按期末里程统计"
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This reverts commit feb950dd59.
This commit is contained in:
@@ -135,17 +135,8 @@ app.get('/', async (c) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [targetVehicleRows] = await pool.execute(
|
const [targetVehicleRows] = await pool.execute(
|
||||||
`SELECT target_id, plate_number, current_mileage, current_year_number,
|
'SELECT target_id, plate_number FROM tab_mileage_assessment_vehicle WHERE is_deleted = 0'
|
||||||
DATE_FORMAT(assessment_start_date, '%Y-%m-%d') as assessment_start_date
|
) as [{ target_id: number; plate_number: string }[], unknown];
|
||||||
FROM tab_mileage_assessment_vehicle
|
|
||||||
WHERE is_deleted = 0`
|
|
||||||
) as [{
|
|
||||||
target_id: number;
|
|
||||||
plate_number: string;
|
|
||||||
current_mileage: string | number | null;
|
|
||||||
current_year_number: number;
|
|
||||||
assessment_start_date: string;
|
|
||||||
}[], unknown];
|
|
||||||
|
|
||||||
const targetIdPlatesMap = new Map<number, string[]>();
|
const targetIdPlatesMap = new Map<number, string[]>();
|
||||||
for (const r of targetVehicleRows) {
|
for (const r of targetVehicleRows) {
|
||||||
@@ -154,118 +145,6 @@ app.get('/', async (c) => {
|
|||||||
targetIdPlatesMap.set(r.target_id, list);
|
targetIdPlatesMap.set(r.target_id, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetRuleMap = new Map<number, any>();
|
|
||||||
for (const t of targets) targetRuleMap.set(t.id, t);
|
|
||||||
|
|
||||||
function formatUtcDate(date: Date): string {
|
|
||||||
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}-${String(date.getUTCDate()).padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addYearsMinusOneDay(dateStr: string, years: number): string {
|
|
||||||
const date = new Date(`${dateStr}T00:00:00Z`);
|
|
||||||
date.setUTCFullYear(date.getUTCFullYear() + years);
|
|
||||||
date.setUTCDate(date.getUTCDate() - 1);
|
|
||||||
return formatUtcDate(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
function round2(value: number): number {
|
|
||||||
return Math.round(value * 100) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
const todayStr = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai' }).format(new Date());
|
|
||||||
const endedPeriodDates: string[] = [];
|
|
||||||
for (const row of targetVehicleRows) {
|
|
||||||
const target = targetRuleMap.get(row.target_id);
|
|
||||||
const maxYear = Math.min(Number(target?.assessment_years) || 0, Number(row.current_year_number) || 0);
|
|
||||||
for (let year = 1; year <= maxYear; year++) {
|
|
||||||
const endDate = addYearsMinusOneDay(row.assessment_start_date, year);
|
|
||||||
if (endDate < todayStr) endedPeriodDates.push(endDate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const postPeriodDailyMap = new Map<string, { date: string; km: number }[]>();
|
|
||||||
const allTargetPlates = Array.from(new Set(targetVehicleRows.map(row => row.plate_number)));
|
|
||||||
const minEndedDate = endedPeriodDates.sort()[0];
|
|
||||||
if (minEndedDate && allTargetPlates.length > 0) {
|
|
||||||
const [postPeriodDailyRows] = await mileagePool.execute(
|
|
||||||
`SELECT plate,
|
|
||||||
DATE_FORMAT(stat_date, '%Y-%m-%d') as stat_date,
|
|
||||||
MAX(GREATEST(daily_km, 0)) as daily_km
|
|
||||||
FROM v_vehicle_daily_stats
|
|
||||||
WHERE stat_date > ? AND plate IN (${allTargetPlates.map(() => '?').join(',')})
|
|
||||||
GROUP BY plate, stat_date`,
|
|
||||||
[minEndedDate, ...allTargetPlates]
|
|
||||||
) as [{ plate: string; stat_date: string; daily_km: string | number | null }[], unknown];
|
|
||||||
|
|
||||||
for (const row of postPeriodDailyRows) {
|
|
||||||
const list = postPeriodDailyMap.get(row.plate) || [];
|
|
||||||
list.push({ date: row.stat_date, km: Number(row.daily_km) || 0 });
|
|
||||||
postPeriodDailyMap.set(row.plate, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const yearlyMetricMap = new Map<string, {
|
|
||||||
completed: number;
|
|
||||||
remaining: number;
|
|
||||||
completionRate: number;
|
|
||||||
qualifiedCount: number;
|
|
||||||
qualifiedRate: number;
|
|
||||||
halfQualifiedCount: number;
|
|
||||||
}>();
|
|
||||||
const yearlyMetricDraftMap = new Map<string, {
|
|
||||||
target: number;
|
|
||||||
completed: number;
|
|
||||||
remaining: number;
|
|
||||||
vehicleCount: number;
|
|
||||||
qualifiedCount: number;
|
|
||||||
halfQualifiedCount: number;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
for (const row of targetVehicleRows) {
|
|
||||||
const target = targetRuleMap.get(row.target_id);
|
|
||||||
const annualMileage = Number(target?.annual_mileage_per_vehicle) || 0;
|
|
||||||
const maxYear = Math.min(Number(target?.assessment_years) || 0, Number(row.current_year_number) || 0);
|
|
||||||
const postDailyRows = postPeriodDailyMap.get(row.plate_number) || [];
|
|
||||||
|
|
||||||
for (let year = 1; year <= maxYear; year++) {
|
|
||||||
const key = `${row.target_id}-${year}`;
|
|
||||||
const goal = annualMileage * year;
|
|
||||||
const endDate = addYearsMinusOneDay(row.assessment_start_date, year);
|
|
||||||
const postPeriodMileage = endDate < todayStr
|
|
||||||
? postDailyRows.reduce((sum, item) => item.date > endDate ? sum + item.km : sum, 0)
|
|
||||||
: 0;
|
|
||||||
const mileageAtCutoff = Math.max(0, (Number(row.current_mileage) || 0) - postPeriodMileage);
|
|
||||||
const completed = Math.min(mileageAtCutoff, goal);
|
|
||||||
const draft = yearlyMetricDraftMap.get(key) || {
|
|
||||||
target: 0,
|
|
||||||
completed: 0,
|
|
||||||
remaining: 0,
|
|
||||||
vehicleCount: 0,
|
|
||||||
qualifiedCount: 0,
|
|
||||||
halfQualifiedCount: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
draft.target += goal;
|
|
||||||
draft.completed += completed;
|
|
||||||
draft.remaining += Math.max(goal - mileageAtCutoff, 0);
|
|
||||||
draft.vehicleCount += 1;
|
|
||||||
if (mileageAtCutoff >= goal) draft.qualifiedCount += 1;
|
|
||||||
if (mileageAtCutoff >= goal * 0.5) draft.halfQualifiedCount += 1;
|
|
||||||
yearlyMetricDraftMap.set(key, draft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, draft] of yearlyMetricDraftMap) {
|
|
||||||
yearlyMetricMap.set(key, {
|
|
||||||
completed: round2(draft.completed),
|
|
||||||
remaining: round2(draft.remaining),
|
|
||||||
completionRate: round2(draft.target > 0 ? (draft.completed / draft.target) * 100 : 0),
|
|
||||||
qualifiedCount: draft.qualifiedCount,
|
|
||||||
qualifiedRate: round2(draft.vehicleCount > 0 ? (draft.qualifiedCount / draft.vehicleCount) * 100 : 0),
|
|
||||||
halfQualifiedCount: draft.halfQualifiedCount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const result = targets.map((t: any) => {
|
const result = targets.map((t: any) => {
|
||||||
const s = statsMap.get(t.id) || {};
|
const s = statsMap.get(t.id) || {};
|
||||||
@@ -288,21 +167,20 @@ app.get('/', async (c) => {
|
|||||||
const endDate = row.end_date ? new Date(row.end_date) : now;
|
const endDate = row.end_date ? new Date(row.end_date) : now;
|
||||||
const assessmentDaysLeft = Math.max(0, Math.ceil((endDate.getTime() - now.getTime()) / 86400000));
|
const assessmentDaysLeft = Math.max(0, Math.ceil((endDate.getTime() - now.getTime()) / 86400000));
|
||||||
const yearNumber = Number(row.year_number) || 0;
|
const yearNumber = Number(row.year_number) || 0;
|
||||||
const cutoffMetrics = yearlyMetricMap.get(`${row.target_id}-${row.year_number}`);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
yearNumber,
|
yearNumber,
|
||||||
label: `第${yearNumber}年`,
|
label: `第${yearNumber}年`,
|
||||||
vehicleCount,
|
vehicleCount,
|
||||||
target: Number(row.target_mileage) || 0,
|
target: Number(row.target_mileage) || 0,
|
||||||
completed: cutoffMetrics?.completed ?? (Number(row.completed_mileage) || 0),
|
completed: Number(row.completed_mileage) || 0,
|
||||||
remaining: cutoffMetrics?.remaining ?? remainingMileage,
|
remaining: remainingMileage,
|
||||||
completionRate: cutoffMetrics?.completionRate ?? ((Number(row.completion_rate) || 0) * 100),
|
completionRate: (Number(row.completion_rate) || 0) * 100,
|
||||||
qualifiedCount: cutoffMetrics?.qualifiedCount ?? qualifiedCount,
|
qualifiedCount,
|
||||||
qualifiedRate: cutoffMetrics?.qualifiedRate ?? (vehicleCount > 0 ? (qualifiedCount / vehicleCount) * 100 : 0),
|
qualifiedRate: vehicleCount > 0 ? (qualifiedCount / vehicleCount) * 100 : 0,
|
||||||
halfQualifiedCount: cutoffMetrics?.halfQualifiedCount ?? (Number(row.half_qualified_count) || 0),
|
halfQualifiedCount: Number(row.half_qualified_count) || 0,
|
||||||
daysLeft: assessmentDaysLeft,
|
daysLeft: assessmentDaysLeft,
|
||||||
dailyTarget: assessmentDaysLeft > 0 ? Math.round(((cutoffMetrics?.remaining ?? remainingMileage) / assessmentDaysLeft) * 10) / 10 : 0,
|
dailyTarget: assessmentDaysLeft > 0 ? Math.round((remainingMileage / assessmentDaysLeft) * 10) / 10 : 0,
|
||||||
startDate: row.start_date || null,
|
startDate: row.start_date || null,
|
||||||
endDate: row.end_date || null,
|
endDate: row.end_date || null,
|
||||||
periods: yearlyPeriodsMap.get(`${row.target_id}-${row.year_number}`) || [],
|
periods: yearlyPeriodsMap.get(`${row.target_id}-${row.year_number}`) || [],
|
||||||
|
|||||||
Reference in New Issue
Block a user