后端目录 - db/:mysql / hydrogen / heatmap 连接与氢能只读 SQL 守卫收拢到一处。 - middleware/:auth(JWT → 注入 user)与 read-only 归位。 - 相关测试随文件移动(middleware/read-only.test.ts、db/hydrogen-read-only.test.ts)。 import 改写由一次性 codemod 完成,未改变任何逻辑。 架构守护 - 新增 src/architecture.test.ts,断言 6 条分层铁律: server 不依赖 modules、前端不依赖 server、shared 为叶子层、 无 vendor 引用、model.ts 不依赖 react、@ts-nocheck 仅限已登记的原型快照。 豁免清单只减不增。 测试基建 - npm test 的 glob 同时匹配 .test.ts 与 .test.tsx(此前 .tsx 测试会被静默漏掉)。 文档 - 新增根 README.md:入口、快速开始、命令、目录、部署注意事项 (JWT_SECRET 必须注入且 >=32 字符,否则拒绝启动)。 - 新增 docs/ARCHITECTURE.md:依赖方向、6 条硬规则、业务域形状、 中间件顺序、新增模块步骤,以及"已知未尽事项"的诚实清单。 lint / test(134) / build 全绿。
94 lines
3.5 KiB
TypeScript
94 lines
3.5 KiB
TypeScript
import pool from '../../db/mysql.js';
|
|
import type { VehicleInfoRow } from './types.js';
|
|
|
|
/** 车辆关联信息 SQL(客户名、部门、经理、租赁状态、主体、项目、品牌) */
|
|
export const VEHICLE_INFO_SQL = `SELECT
|
|
vi.plate_number AS plate,
|
|
vi.vin AS vin,
|
|
COALESCE(c.customer_name, vor.customer_name, ci.customer_name) AS customer,
|
|
COALESCE(c.business_department_name, vor.business_dept) AS department,
|
|
COALESCE(c.business_manager_name, vor.business_manager) AS manager,
|
|
CAST(COALESCE(c.business_manager_id, vi.business_id) AS CHAR) AS manager_id,
|
|
CASE vs.operation_status
|
|
WHEN '1' THEN '租赁'
|
|
WHEN '2' THEN '自营'
|
|
WHEN '3' THEN '可运营'
|
|
WHEN '4' THEN '待运营'
|
|
WHEN '5' THEN '退出运营'
|
|
ELSE vs.operation_status
|
|
END AS rent_status,
|
|
NULLIF(vi.registered_ownership, '') AS entity,
|
|
COALESCE(c.project_name, vor.project_name) AS project,
|
|
COALESCE(vi_operation_city.NAME, NULLIF(vi.operation_city, '')) AS inventory_location,
|
|
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 brand_label
|
|
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 common_district vi_operation_city
|
|
ON vi_operation_city.CODE = vi.operation_city COLLATE utf8mb4_unicode_ci
|
|
AND vi_operation_city.STATUS = 'VALID'
|
|
LEFT JOIN vehicle_lease_order_record vor
|
|
ON vor.vehicle_id = vi.id
|
|
AND vor.del_flag = '0'
|
|
AND vor.id = (
|
|
SELECT MAX(vor2.id)
|
|
FROM vehicle_lease_order_record vor2
|
|
WHERE vor2.vehicle_id = vi.id
|
|
AND vor2.del_flag = '0'
|
|
)
|
|
LEFT JOIN vehicle_lease_contract_info c
|
|
ON c.order_id = vor.contract_id
|
|
AND c.del_flag = '0'
|
|
LEFT JOIN customer_info ci
|
|
ON ci.id = vi.customer_id
|
|
AND ci.del_flag = '0'
|
|
WHERE vi.del_flag = '0'
|
|
AND COALESCE(vs.operation_status, '') <> '5'`;
|
|
|
|
/** 查询所有车辆关联信息,返回 plate→info 的 Map */
|
|
export async function fetchVehicleInfoMap(): Promise<Map<string, VehicleInfoRow>> {
|
|
const [rows] = await pool.execute(VEHICLE_INFO_SQL) as [VehicleInfoRow[], unknown];
|
|
const map = new Map<string, VehicleInfoRow>();
|
|
for (const row of rows) {
|
|
map.set(row.plate, row);
|
|
}
|
|
return map;
|
|
}
|
|
|
|
/** 查询指定车牌的关联信息 */
|
|
export async function fetchVehicleInfoByPlates(plates: string[]): Promise<Map<string, VehicleInfoRow>> {
|
|
if (plates.length === 0) return new Map();
|
|
const [rows] = await pool.execute(
|
|
`${VEHICLE_INFO_SQL} AND vi.plate_number IN (${plates.map(() => '?').join(',')})`,
|
|
plates
|
|
) as [VehicleInfoRow[], unknown];
|
|
const map = new Map<string, VehicleInfoRow>();
|
|
for (const row of rows) {
|
|
map.set(row.plate, row);
|
|
}
|
|
return map;
|
|
}
|