467 lines
20 KiB
TypeScript
467 lines
20 KiB
TypeScript
import pool from '../../db.js';
|
||
import { LATEST_VEHICLE_LEASE_JOIN_SQL } from '../../vehicle-lease-sql.js';
|
||
import type { Vehicle, VehicleRow } from '../../types.js';
|
||
import { transformRow } from './model.js';
|
||
import type { WeeklyTruckIds } from './types.js';
|
||
|
||
interface Database {
|
||
query<T = any[]>(sql: string, values?: any[]): Promise<[T, ...any[]]>;
|
||
}
|
||
|
||
type FlowType = 'delivered' | 'returned' | 'replaced';
|
||
|
||
export type WeeklyDetailType = FlowType | 'pending' | 'new';
|
||
|
||
export interface FlowDetailRow {
|
||
id: string;
|
||
type: FlowType;
|
||
type_label: string;
|
||
stat_date: string;
|
||
truck_id: string;
|
||
plate_number: string;
|
||
event_time: string | null;
|
||
submit_time: string | null;
|
||
department: string | null;
|
||
manager: string | null;
|
||
customer_name: string | null;
|
||
contract_no: string | null;
|
||
project_name: string | null;
|
||
}
|
||
|
||
interface WeeklyStats {
|
||
pendingDelivery: number;
|
||
weeklyNew: number;
|
||
weeklyRemoved: number;
|
||
weeklyDelivered: number;
|
||
weeklyReturned: number;
|
||
weeklyReplaced: number;
|
||
}
|
||
|
||
export const MAIN_SQL = `SELECT
|
||
CAST(vi.id AS CHAR) AS id,
|
||
CAST(am.id AS CHAR) AS abnormal_move_id,
|
||
am.move_type AS abnormal_move_type,
|
||
NULLIF(TRIM(am_applicant.nick_name), '') AS abnormal_move_applicant,
|
||
CAST(pdv.id AS CHAR) AS pending_delivery_id,
|
||
pdts.contract_code AS pending_contract_no,
|
||
pdts.customer_name AS pending_customer_name,
|
||
am.move_status AS abnormal_move_status,
|
||
CONCAT_WS(';', NULLIF(TRIM(am.remark), ''), NULLIF(TRIM(amr.remark), '')) AS abnormal_move_work,
|
||
DATE_FORMAT(am.move_plan_end_time, '%Y-%m-%d %H:%i') AS abnormal_move_target_time,
|
||
DATE_FORMAT(am.move_end_time, '%Y-%m-%d %H:%i') AS abnormal_move_actual_time,
|
||
vi.plate_number AS 车牌号,
|
||
vi.vin AS vin,
|
||
vm.brand AS 车辆品牌,
|
||
vm.model AS 车辆型号,
|
||
vi.body_color AS 车辆颜色,
|
||
vi.rental_company AS 租赁公司,
|
||
CASE vi.vehicle_source
|
||
WHEN '0' THEN '自有'
|
||
WHEN '1' THEN '挂靠'
|
||
WHEN '2' THEN '外租'
|
||
WHEN '3' THEN '自有'
|
||
ELSE vi.actual_ownership
|
||
END AS 车辆归属状态Label,
|
||
vm.model AS 车辆型号Label,
|
||
vm.vehicle_type AS 车辆类型参数,
|
||
vi.operation_city AS 库存区域,
|
||
vs.vehicle_status AS 车辆租赁状态,
|
||
vs.operation_status AS 车辆租赁状态Label,
|
||
CASE WHEN COALESCE(vs.operation_status, '') = '5' THEN 0 ELSE 1 END AS 是否营运,
|
||
COALESCE(info_province.NAME, NULLIF(info.province, ''), vi.province_name, vi_province.NAME, NULLIF(vi.province, '')) AS 省,
|
||
COALESCE(info_city.NAME, NULLIF(info.city, ''), vi.city_name, vi_city.NAME, vi_operation_city.NAME, NULLIF(vi.city, ''), NULLIF(vi.operation_city, '')) AS 市,
|
||
info.lat AS 纬度,
|
||
info.lng AS 经度,
|
||
CASE vm.brand
|
||
WHEN 'hyundai' THEN CASE WHEN vm.model LIKE '%帕力安%' OR vm.model LIKE '%冷链%' OR vm.model LIKE '%双飞翼%' THEN '帕力安牌' ELSE '现代' END
|
||
WHEN 'yuejin' THEN '跃进'
|
||
WHEN 'feichi' THEN '飞驰'
|
||
WHEN 'sulong' THEN '苏龙'
|
||
WHEN 'higer' THEN '海格'
|
||
WHEN 'dongfeng' THEN '东风'
|
||
WHEN 'yutong' THEN '宇通'
|
||
WHEN 'chufeng' THEN '楚风'
|
||
WHEN 'tonghua' THEN '通华'
|
||
WHEN 'maxus' THEN '大通'
|
||
WHEN 'mingwei' THEN '明威'
|
||
WHEN 'wanfeng' THEN '万风'
|
||
WHEN 'shujie' THEN '舒捷'
|
||
WHEN 'denza' THEN '腾势'
|
||
WHEN 'hongyan' THEN '红岩'
|
||
WHEN 'yuanchang brand' THEN '远程牌'
|
||
WHEN 'others' THEN '其他'
|
||
ELSE vm.brand
|
||
END AS 车辆品牌Label,
|
||
c.id AS 合同ID,
|
||
vor.contract_code AS 合同编码,
|
||
vor.customer_name AS 客户名称,
|
||
c.signing_company AS 合同归属公司,
|
||
vor.business_dept AS 合同归属部门,
|
||
NULLIF(vi.registered_ownership, '') AS 主体,
|
||
vor.project_name AS 项目名称,
|
||
vor.business_manager AS 客户经理,
|
||
CAST(COALESCE(c.business_manager_id, vi.business_id) AS CHAR) AS 经理ID
|
||
FROM vehicle_info vi
|
||
LEFT JOIN vehicle_status vs
|
||
ON vs.vehicle_id = vi.id
|
||
AND vs.del_flag = 0
|
||
LEFT JOIN vehicle_model vm
|
||
ON vm.id = vi.vehicle_model_id
|
||
AND vm.del_flag = '0'
|
||
LEFT JOIN tab_truck_remote_sync_realtime_info info
|
||
ON info.plate_number = vi.plate_number
|
||
AND info.is_deleted = 0
|
||
LEFT JOIN common_district info_province
|
||
ON info_province.CODE = info.province COLLATE utf8mb4_unicode_ci
|
||
AND info_province.STATUS = 'VALID'
|
||
LEFT JOIN common_district info_city
|
||
ON info_city.CODE = info.city COLLATE utf8mb4_unicode_ci
|
||
AND info_city.STATUS = 'VALID'
|
||
LEFT JOIN common_district vi_province
|
||
ON vi_province.CODE = vi.province COLLATE utf8mb4_unicode_ci
|
||
AND vi_province.STATUS = 'VALID'
|
||
LEFT JOIN common_district vi_city
|
||
ON vi_city.CODE = vi.city COLLATE utf8mb4_unicode_ci
|
||
AND vi_city.STATUS = 'VALID'
|
||
LEFT JOIN common_district vi_operation_city
|
||
ON vi_operation_city.CODE = vi.operation_city COLLATE utf8mb4_unicode_ci
|
||
AND vi_operation_city.STATUS = 'VALID'
|
||
-- Pending delivery uses the current active task, never a completed historical lease.
|
||
LEFT JOIN delivery_vehicle pdv ON pdv.id = (
|
||
SELECT dv.id
|
||
FROM delivery_vehicle dv
|
||
JOIN delivery_task_subject dts ON dts.id = dv.delivery_task_subject_id
|
||
AND dts.del_flag = '0' AND dts.status = 0
|
||
WHERE dv.vehicle_id = vi.id AND dv.del_flag = '0' AND dv.status = 0
|
||
AND dv.delivery_status IN (0, 1) AND vs.vehicle_status = '4'
|
||
ORDER BY dv.create_time DESC, dv.id DESC
|
||
LIMIT 1
|
||
)
|
||
LEFT JOIN delivery_task_subject pdts ON pdts.id = pdv.delivery_task_subject_id
|
||
-- Select one effective movement per vehicle; drafts and withdrawals are not operational work.
|
||
-- An ongoing movement takes precedence over completed history.
|
||
LEFT JOIN vehicle_abnormal_move_record amr ON amr.id = (
|
||
SELECT mr.id
|
||
FROM vehicle_abnormal_move_record mr
|
||
JOIN vehicle_abnormal_move m ON m.id = mr.move_id AND m.del_flag = '0'
|
||
WHERE mr.vehicle_id = vi.id AND mr.del_flag = '0' AND m.move_status IN (3, 5)
|
||
AND vs.vehicle_status = '14'
|
||
ORDER BY (m.move_status = 3) DESC, COALESCE(m.move_start_time, m.create_time) DESC, m.id DESC, mr.id DESC
|
||
LIMIT 1
|
||
)
|
||
LEFT JOIN vehicle_abnormal_move am ON am.id = amr.move_id
|
||
LEFT JOIN \`ry-cloud\`.sys_user am_applicant ON am_applicant.user_id = am.create_by
|
||
${LATEST_VEHICLE_LEASE_JOIN_SQL}
|
||
WHERE vi.del_flag = '0'
|
||
AND COALESCE(vs.operation_status, '') <> '5'`;
|
||
|
||
// Week range: last Saturday 00:00 to this Friday 23:59
|
||
// MySQL: WEEKDAY() returns 0=Monday..6=Sunday, Saturday=5
|
||
// "上周六-本周五": offset from today to last Saturday
|
||
export const WEEK_START_SQL = `DATE_SUB(CURDATE(), INTERVAL (WEEKDAY(CURDATE()) + 2) % 7 DAY)`;
|
||
export const WEEK_END_SQL = `DATE_ADD(${WEEK_START_SQL}, INTERVAL 7 DAY)`;
|
||
|
||
export const WEEKLY_TRUCK_ID_SQLS = [
|
||
`SELECT CAST(vehicle_id AS CHAR) AS truck_id
|
||
FROM vehicle_status
|
||
WHERE del_flag=0 AND vehicle_status='4' AND COALESCE(operation_status, '') <> '5'`,
|
||
`SELECT CAST(vehicle_id AS CHAR) AS truck_id
|
||
FROM delivery_vehicle
|
||
WHERE del_flag='0'
|
||
AND vehicle_id IS NOT NULL
|
||
AND delivery_time >= ${WEEK_START_SQL} AND delivery_time < ${WEEK_END_SQL}
|
||
AND delivery_status IN (2,3,5)`,
|
||
`SELECT CAST(vehicle_id AS CHAR) AS truck_id
|
||
FROM return_vehicle_task
|
||
WHERE del_flag='0'
|
||
AND vehicle_id IS NOT NULL
|
||
AND arrival_time >= ${WEEK_START_SQL} AND arrival_time < ${WEEK_END_SQL}
|
||
AND status IN (2,3,5)`,
|
||
`SELECT CAST(new_vehicle_id AS CHAR) AS truck_id
|
||
FROM vehicle_replacement
|
||
WHERE del_flag='0'
|
||
AND new_vehicle_id IS NOT NULL
|
||
AND replace_time >= ${WEEK_START_SQL} AND replace_time < ${WEEK_END_SQL}
|
||
AND status=20`,
|
||
] as const;
|
||
|
||
// 交车单 SQL
|
||
const DELIVERED_SQL = `SELECT
|
||
dv.id, DATE(dv.delivery_time) AS handover_date,
|
||
CAST(dv.vehicle_id AS CHAR) AS truck_id, dv.plate_number,
|
||
c.contract_type,
|
||
COALESCE(dts.customer_name, c.customer_name) AS customer_name
|
||
FROM delivery_vehicle dv
|
||
LEFT JOIN delivery_task_subject dts
|
||
ON dts.id = dv.delivery_task_subject_id
|
||
AND dts.del_flag = '0'
|
||
LEFT JOIN vehicle_lease_contract_info c
|
||
ON c.order_id = dv.contract_id
|
||
AND c.del_flag = '0'
|
||
WHERE dv.del_flag = '0'
|
||
AND dv.vehicle_id IS NOT NULL
|
||
AND dv.delivery_time IS NOT NULL
|
||
AND dv.delivery_status IN (2,3,5)`;
|
||
|
||
// 还车单 SQL
|
||
const RETURNED_SQL = `SELECT
|
||
r.id, DATE(r.arrival_time) AS handover_date,
|
||
CAST(r.vehicle_id AS CHAR) AS truck_id, r.plate_number,
|
||
c.contract_type,
|
||
COALESCE(dts.customer_name, c.customer_name) AS customer_name
|
||
FROM return_vehicle_task r
|
||
LEFT JOIN delivery_task_subject dts
|
||
ON dts.id = r.delivery_task_subject_id
|
||
AND dts.del_flag = '0'
|
||
LEFT JOIN vehicle_lease_contract_info c
|
||
ON c.order_id = r.contract_id
|
||
AND c.del_flag = '0'
|
||
WHERE r.del_flag = '0'
|
||
AND r.vehicle_id IS NOT NULL
|
||
AND r.arrival_time IS NOT NULL
|
||
AND r.status IN (2,3,5)`;
|
||
|
||
// 替换车单 SQL
|
||
const REPLACED_SQL = `SELECT
|
||
vr.id, DATE(vr.replace_time) AS handover_date,
|
||
CAST(vr.new_vehicle_id AS CHAR) AS truck_id, vr.new_vehicle_plate AS plate_number,
|
||
c.contract_type,
|
||
c.customer_name
|
||
FROM vehicle_replacement vr
|
||
LEFT JOIN vehicle_lease_contract_info c
|
||
ON c.id = vr.contract_id
|
||
AND c.del_flag = '0'
|
||
WHERE vr.del_flag = '0'
|
||
AND vr.new_vehicle_id IS NOT NULL
|
||
AND vr.replace_time IS NOT NULL
|
||
AND vr.status = 20`;
|
||
|
||
export const WEEKLY_DETAIL_SQL: Record<WeeklyDetailType, string> = {
|
||
delivered: `${DELIVERED_SQL} AND dv.delivery_time >= ${WEEK_START_SQL} AND dv.delivery_time < ${WEEK_END_SQL} ORDER BY dv.delivery_time DESC`,
|
||
returned: `${RETURNED_SQL} AND r.arrival_time >= ${WEEK_START_SQL} AND r.arrival_time < ${WEEK_END_SQL} ORDER BY r.arrival_time DESC`,
|
||
replaced: `${REPLACED_SQL} AND vr.replace_time >= ${WEEK_START_SQL} AND vr.replace_time < ${WEEK_END_SQL} ORDER BY vr.replace_time DESC`,
|
||
pending: `SELECT CAST(vi.id AS CHAR) AS truck_id, vi.plate_number, NULL AS handover_date, NULL AS contract_type, NULL AS customer_name
|
||
FROM vehicle_info vi
|
||
LEFT JOIN vehicle_status vs ON vs.vehicle_id=vi.id AND vs.del_flag=0
|
||
WHERE vi.del_flag='0' AND vs.vehicle_status='4' AND COALESCE(vs.operation_status, '') <> '5'`,
|
||
new: `SELECT CAST(vi.id AS CHAR) AS truck_id, vi.plate_number, vi.create_time AS handover_date, NULL AS contract_type, NULL AS customer_name
|
||
FROM vehicle_info vi
|
||
LEFT JOIN vehicle_status vs ON vs.vehicle_id=vi.id AND vs.del_flag=0
|
||
WHERE vi.del_flag='0' AND COALESCE(vs.operation_status, '') <> '5'
|
||
AND vi.create_time >= ${WEEK_START_SQL} AND vi.create_time < ${WEEK_END_SQL} ORDER BY vi.create_time DESC`,
|
||
};
|
||
|
||
export const FLOW_STATS_SQL = `
|
||
SELECT *
|
||
FROM (
|
||
SELECT
|
||
CONCAT(CASE WHEN dv.vehicle_replacement_id IS NOT NULL THEN 'replaced-' ELSE 'delivered-' END, dv.id) AS id,
|
||
CASE WHEN dv.vehicle_replacement_id IS NOT NULL THEN 'replaced' ELSE 'delivered' END AS type,
|
||
CASE WHEN dv.vehicle_replacement_id IS NOT NULL THEN '替换交车' ELSE '交车' END AS type_label,
|
||
DATE_FORMAT(dv.delivery_completion_processed_time, '%Y-%m-%d') AS stat_date,
|
||
CAST(dv.vehicle_id AS CHAR) AS truck_id,
|
||
dv.plate_number,
|
||
DATE_FORMAT(dv.delivery_completion_processed_time, '%Y-%m-%d %H:%i:%s') AS event_time,
|
||
DATE_FORMAT(dv.create_time, '%Y-%m-%d %H:%i:%s') AS submit_time,
|
||
c.business_department_name AS department,
|
||
c.business_manager_name AS manager,
|
||
COALESCE(dts.customer_name, c.customer_name) AS customer_name,
|
||
COALESCE(NULLIF(dts.contract_code, ''), c.contract_code) AS contract_no,
|
||
COALESCE(NULLIF(dts.project_name, ''), c.project_name) AS project_name
|
||
FROM delivery_vehicle dv
|
||
LEFT JOIN delivery_task_subject dts
|
||
ON dts.id = dv.delivery_task_subject_id
|
||
AND dts.del_flag = '0'
|
||
LEFT JOIN vehicle_lease_contract_info c
|
||
ON c.order_id = dv.contract_id
|
||
AND c.del_flag = '0'
|
||
WHERE dv.del_flag = '0'
|
||
AND dv.vehicle_id IS NOT NULL
|
||
AND dv.delivery_completion_processed_time IS NOT NULL
|
||
-- 待客户签章也计入交车闭环,日期仍按运维签章时间归属。
|
||
AND dv.delivery_status IN (2,3,5,20)
|
||
AND (dts.status IS NULL OR dts.status <> 10)
|
||
AND dv.delivery_completion_processed_time >= ?
|
||
AND dv.delivery_completion_processed_time < DATE_ADD(?, INTERVAL 1 DAY)
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
CONCAT('returned-', r.id) AS id,
|
||
'returned' AS type,
|
||
'还车' AS type_label,
|
||
DATE_FORMAT(r.return_completion_processed_time, '%Y-%m-%d') AS stat_date,
|
||
CAST(r.vehicle_id AS CHAR) AS truck_id,
|
||
r.plate_number,
|
||
DATE_FORMAT(r.return_completion_processed_time, '%Y-%m-%d %H:%i:%s') AS event_time,
|
||
DATE_FORMAT(r.create_time, '%Y-%m-%d %H:%i:%s') AS submit_time,
|
||
c.business_department_name AS department,
|
||
c.business_manager_name AS manager,
|
||
COALESCE(dts.customer_name, c.customer_name) AS customer_name,
|
||
COALESCE(NULLIF(dts.contract_code, ''), c.contract_code) AS contract_no,
|
||
COALESCE(NULLIF(dts.project_name, ''), c.project_name) AS project_name
|
||
FROM return_vehicle_task r
|
||
LEFT JOIN delivery_task_subject dts
|
||
ON dts.id = r.delivery_task_subject_id
|
||
AND dts.del_flag = '0'
|
||
LEFT JOIN vehicle_lease_contract_info c
|
||
ON c.order_id = r.contract_id
|
||
AND c.del_flag = '0'
|
||
WHERE r.del_flag = '0'
|
||
AND r.vehicle_id IS NOT NULL
|
||
AND r.return_completion_processed_time IS NOT NULL
|
||
-- 与还车管理历史记录一致:完成、已签章、待签署;日期按运维签章时间归属。
|
||
AND r.status IN (2,3,5)
|
||
AND r.return_completion_processed_time >= ?
|
||
AND r.return_completion_processed_time < DATE_ADD(?, INTERVAL 1 DAY)
|
||
|
||
) flow
|
||
ORDER BY flow.event_time DESC, flow.id DESC
|
||
`;
|
||
|
||
const CACHE_TTL = 5 * 1000;
|
||
|
||
export class VehicleRepository {
|
||
private cachedVehicles: Vehicle[] = [];
|
||
private lastFetchTime = 0;
|
||
private cachedWeeklyTruckIds: WeeklyTruckIds | null = null;
|
||
private weeklyTruckIdsLastFetch = 0;
|
||
private cachedWeeklyStats: WeeklyStats | null = null;
|
||
private weeklyStatsLastFetch = 0;
|
||
|
||
constructor(
|
||
private readonly db: Database,
|
||
private readonly now: () => number = Date.now,
|
||
private readonly mapVehicleRow: (row: VehicleRow) => Vehicle = transformRow,
|
||
) {}
|
||
|
||
async getVehicles(): Promise<Vehicle[]> {
|
||
const now = this.now();
|
||
if (this.cachedVehicles.length > 0 && now - this.lastFetchTime < CACHE_TTL) {
|
||
return this.cachedVehicles;
|
||
}
|
||
const [rows] = await this.db.query<any[]>(MAIN_SQL);
|
||
this.cachedVehicles = (rows as VehicleRow[]).map(this.mapVehicleRow);
|
||
this.lastFetchTime = now;
|
||
return this.cachedVehicles;
|
||
}
|
||
|
||
async getWeeklyTruckIds(): Promise<WeeklyTruckIds> {
|
||
const now = this.now();
|
||
if (this.cachedWeeklyTruckIds && now - this.weeklyTruckIdsLastFetch < CACHE_TTL) {
|
||
return this.cachedWeeklyTruckIds;
|
||
}
|
||
|
||
const [[pendingRows], [deliveredRows], [returnedRows], [replacedRows]] = await Promise.all(
|
||
WEEKLY_TRUCK_ID_SQLS.map((sql) => this.db.query<any[]>(sql)),
|
||
);
|
||
const toSet = (rows: any[]) => new Set(rows.map((r) => String(r.truck_id)).filter((s) => s && s !== 'null'));
|
||
this.cachedWeeklyTruckIds = {
|
||
pending: toSet(pendingRows as any[]),
|
||
delivered: toSet(deliveredRows as any[]),
|
||
returned: toSet(returnedRows as any[]),
|
||
replaced: toSet(replacedRows as any[]),
|
||
};
|
||
this.weeklyTruckIdsLastFetch = now;
|
||
return this.cachedWeeklyTruckIds;
|
||
}
|
||
|
||
async getWeeklyStats(): Promise<WeeklyStats> {
|
||
const now = this.now();
|
||
if (this.cachedWeeklyStats && now - this.weeklyStatsLastFetch < CACHE_TTL) {
|
||
return this.cachedWeeklyStats;
|
||
}
|
||
|
||
const [[pendingRows], [newRows], [removedRows], [deliveredRows], [returnedRows], [replacedRows]] = await Promise.all([
|
||
this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM vehicle_status WHERE del_flag=0 AND vehicle_status='4' AND COALESCE(operation_status, '') <> '5'`),
|
||
this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM vehicle_info WHERE del_flag='0' AND create_time >= ${WEEK_START_SQL} AND create_time < ${WEEK_END_SQL}`),
|
||
this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM vehicle_info vi LEFT JOIN vehicle_status vs ON vs.vehicle_id=vi.id AND vs.del_flag=0 WHERE (vi.del_flag='1' OR vs.operation_status='5') AND vi.update_time >= ${WEEK_START_SQL} AND vi.update_time < ${WEEK_END_SQL}`),
|
||
this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM delivery_vehicle
|
||
WHERE del_flag='0' AND delivery_time IS NOT NULL AND delivery_status IN (2,3,5)
|
||
AND delivery_time >= ${WEEK_START_SQL} AND delivery_time < ${WEEK_END_SQL}`),
|
||
this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM return_vehicle_task
|
||
WHERE del_flag='0' AND arrival_time IS NOT NULL AND status IN (2,3,5)
|
||
AND arrival_time >= ${WEEK_START_SQL} AND arrival_time < ${WEEK_END_SQL}`),
|
||
this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM vehicle_replacement
|
||
WHERE del_flag='0' AND replace_time IS NOT NULL AND status=20
|
||
AND replace_time >= ${WEEK_START_SQL} AND replace_time < ${WEEK_END_SQL}`),
|
||
]);
|
||
|
||
this.cachedWeeklyStats = {
|
||
pendingDelivery: (pendingRows as any[])[0]?.cnt || 0,
|
||
weeklyNew: (newRows as any[])[0]?.cnt || 0,
|
||
weeklyRemoved: (removedRows as any[])[0]?.cnt || 0,
|
||
weeklyDelivered: (deliveredRows as any[])[0]?.cnt || 0,
|
||
weeklyReturned: (returnedRows as any[])[0]?.cnt || 0,
|
||
weeklyReplaced: (replacedRows as any[])[0]?.cnt || 0,
|
||
};
|
||
this.weeklyStatsLastFetch = now;
|
||
return this.cachedWeeklyStats;
|
||
}
|
||
|
||
invalidateRefreshCaches(): void {
|
||
this.lastFetchTime = 0;
|
||
this.weeklyStatsLastFetch = 0;
|
||
}
|
||
|
||
async getRealtimeMileageRows(): Promise<any[]> {
|
||
const [rows] = await this.db.query<any[]>(`
|
||
SELECT plate_number, day_mileage
|
||
FROM tab_truck_remote_sync_realtime_info
|
||
WHERE is_deleted = 0 AND plate_number IS NOT NULL
|
||
`);
|
||
return rows;
|
||
}
|
||
|
||
async getAttendanceMileageRows(): Promise<any[]> {
|
||
const [rows] = await this.db.query<any[]>(`SELECT plate_number, day_mileage FROM tab_truck_remote_sync_realtime_info WHERE is_deleted = 0 AND plate_number IS NOT NULL`);
|
||
return rows;
|
||
}
|
||
|
||
async getWeeklyDetailRows(type: WeeklyDetailType): Promise<any[]> {
|
||
const [rows] = await this.db.query<any[]>(WEEKLY_DETAIL_SQL[type]);
|
||
return rows;
|
||
}
|
||
|
||
async getFlowDetailRows(start: string, end: string): Promise<FlowDetailRow[]> {
|
||
const [rows] = await this.db.query<any[]>(FLOW_STATS_SQL, [start, end, start, end]);
|
||
return rows as FlowDetailRow[];
|
||
}
|
||
|
||
async getWeeklyDebugData(): Promise<{
|
||
dateRange: any;
|
||
deliveredAll: any;
|
||
deliveredRecent: any;
|
||
latestTake: any;
|
||
returnedRecent: any;
|
||
latestReturn: any;
|
||
}> {
|
||
const [[dateRange]] = await this.db.query<any[]>(`SELECT
|
||
${WEEK_START_SQL} AS week_start,
|
||
${WEEK_END_SQL} AS week_end,
|
||
CURDATE() AS today,
|
||
WEEKDAY(CURDATE()) AS weekday`);
|
||
const [[deliveredAll]] = await this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM delivery_vehicle
|
||
WHERE del_flag='0' AND delivery_time IS NOT NULL AND delivery_status IN (2,3,5)`);
|
||
const [[deliveredRecent]] = await this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM delivery_vehicle
|
||
WHERE del_flag='0' AND delivery_time IS NOT NULL AND delivery_status IN (2,3,5)
|
||
AND delivery_time >= ${WEEK_START_SQL} AND delivery_time < ${WEEK_END_SQL}`);
|
||
const [[latestTake]] = await this.db.query<any[]>(`SELECT MAX(delivery_time) AS latest FROM delivery_vehicle
|
||
WHERE del_flag='0' AND delivery_time IS NOT NULL AND delivery_status IN (2,3,5)`);
|
||
const [[returnedRecent]] = await this.db.query<any[]>(`SELECT COUNT(*) AS cnt FROM return_vehicle_task
|
||
WHERE del_flag='0' AND arrival_time IS NOT NULL AND status IN (2,3,5)
|
||
AND arrival_time >= ${WEEK_START_SQL} AND arrival_time < ${WEEK_END_SQL}`);
|
||
const [[latestReturn]] = await this.db.query<any[]>(`SELECT MAX(arrival_time) AS latest FROM return_vehicle_task
|
||
WHERE del_flag='0' AND arrival_time IS NOT NULL AND status IN (2,3,5)`);
|
||
|
||
return { dateRange, deliveredAll, deliveredRecent, latestTake, returnedRecent, latestReturn };
|
||
}
|
||
|
||
async getRealtimeProvinceRows(): Promise<any[]> {
|
||
const [rows] = await this.db.query<any[]>(`SELECT plate_number, province FROM tab_truck_remote_sync_realtime_info WHERE is_deleted = 0 AND plate_number IS NOT NULL`);
|
||
return rows;
|
||
}
|
||
}
|
||
|
||
export const vehicleRepository = new VehicleRepository(pool as unknown as Database);
|