refactor(stage9): 后端业务域目录形状统一

- 每个域现在都是 src/server/routes/<domain>/ 目录:
  vehicles/routes.ts(原 routes/vehicles.ts)、vehicle-heatmap/{routes,model}.ts、
  hydrogen-heatmap/{routes,model}.ts、ele/routes.ts、feedback/routes.ts。
- 命名约定:index.ts 是聚合器(挂子路由),routes.ts 是叶子路由。mileage/energy/
  scheduling 保持 index.ts 聚合,其余统一为 routes.ts。
- 纯移动与改名,未改动任何 SQL、参数顺序、路由挂载前缀或处理器逻辑。
  import 改写由一次性 codemod 完成;vehicles/repository.test.ts 读取的源码路径同步更新。
- docs/ARCHITECTURE.md 补充"后端业务域形状"表,并如实标注 ele / feedback 尚未拆出
  repository(这两个域缺 SQL 指纹测试,建议先补测试再拆)。

lint / test(139) / build 全绿,可达性 0 未引用文件。
This commit is contained in:
dsh-agent
2026-09-11 10:30:54 +08:00
parent 954d626afc
commit 795fb207cb
14 changed files with 48 additions and 33 deletions
@@ -1,152 +0,0 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildVehicleGrid,
buildVehicleRanking,
daysInclusive,
filterVehicleRecordsByRadius,
haversineKm,
parseNearbyQuery,
parseVehicleHeatmapQuery,
summarizeVehicleRecords,
vehicleGridPrecision,
type VehicleHeatmapRecord,
} from './vehicle-heatmap-model.js';
import { recentDayRange } from '../../shared/date-range.js';
// 默认区间是"近 30 天"滚动窗口,注入固定时钟让断言可复现。
const NOW = new Date('2026-07-13T12:00:00');
const FALLBACK = recentDayRange(30, NOW);
function record(overrides: Partial<VehicleHeatmapRecord> = {}): VehicleHeatmapRecord {
return {
date: '2026-07-01',
vin: 'VIN-A',
plate: '粤A00001',
time: '2026-07-01 08:00:00',
longitude: 120,
latitude: 30,
source: 'test',
sourceRecordId: '1',
...overrides,
};
}
test('车辆热力图查询默认使用近 30 天滚动窗口,并保留原有字符串处理规则', () => {
assert.deepEqual(parseVehicleHeatmapQuery({}, NOW), {
startDate: FALLBACK.start,
endDate: FALLBACK.end,
query: '',
batchModel: '',
metric: 'locations',
});
assert.deepEqual(parseVehicleHeatmapQuery({
startDate: '2026-06-01',
endDate: 'invalid',
query: ' 粤A ',
batchModel: ' 190台冷链 ',
metric: 'vehicles',
}, NOW), {
startDate: '2026-06-01',
endDate: FALLBACK.end,
query: ' 粤A ',
batchModel: '190台冷链',
metric: 'vehicles',
});
});
test('附近查询继续沿用经纬度 Number 语义和 5 至 200 公里半径限制', () => {
assert.deepEqual(parseNearbyQuery({ lng: '', lat: '0', radiusKm: '0' }), {
center: { lng: 0, lat: 0 },
radiusKm: 50,
});
assert.deepEqual(parseNearbyQuery({ lng: '120', lat: '30', radiusKm: '-1' }), {
center: { lng: 120, lat: 30 },
radiusKm: 5,
});
assert.equal(parseNearbyQuery({ lng: '120', lat: '30', radiusKm: '500' }).radiusKm, 200);
assert.equal(parseNearbyQuery({ lng: 'bad', lat: '30' }).center, null);
});
test('网格精度继续按含首尾天数和筛选条件决定', () => {
assert.equal(daysInclusive('2026-07-01', '2026-07-14'), 14);
assert.equal(daysInclusive('2026-07-14', '2026-07-01'), 0);
assert.equal(vehicleGridPrecision('2026-07-01', '2026-07-14', '', ''), 3);
assert.equal(vehicleGridPrecision('2026-07-01', '2026-07-15', '', ''), 2);
assert.equal(vehicleGridPrecision('2026-07-01', '2026-07-15', ' ', ''), 3);
});
test('车辆网格继续区分位置次数和去重车辆数并保留插入顺序', () => {
const records = [
record({ vin: 'VIN-A', longitude: 120.1231, latitude: 30.9878 }),
record({ vin: 'VIN-A', longitude: 120.1232, latitude: 30.9879 }),
record({ vin: 'VIN-B', longitude: 120.1233, latitude: 30.9877 }),
record({ vin: 'VIN-C', longitude: 121, latitude: 31 }),
];
assert.deepEqual(buildVehicleGrid(records, 'locations', 3), {
points: [
{ lng: 120.123, lat: 30.988, count: 3 },
{ lng: 121, lat: 31, count: 1 },
],
max: 3,
});
assert.deepEqual(buildVehicleGrid(records, 'vehicles', 3), {
points: [
{ lng: 120.123, lat: 30.988, count: 2 },
{ lng: 121, lat: 31, count: 1 },
],
max: 2,
});
assert.deepEqual(buildVehicleGrid([], 'locations', 2), { points: [], max: 1 });
});
test('车辆排名继续按记录数、车牌排序并保留首末出现时间', () => {
const records = [
record({ vin: 'VIN-B', plate: '', time: '2026-07-02 10:00:00' }),
record({ vin: 'VIN-A', plate: '粤A00001', time: '2026-07-02 09:00:00' }),
record({ vin: 'VIN-B', plate: '粤B00001', time: '2026-07-01 08:00:00' }),
record({ vin: 'VIN-A', plate: '粤A00001', time: '2026-07-03 11:00:00' }),
];
assert.deepEqual(buildVehicleRanking(records), [
{
vin: 'VIN-A',
plateNumber: '粤A00001',
locationCount: 2,
firstSeen: '2026-07-02 09:00:00',
lastSeen: '2026-07-03 11:00:00',
},
{
vin: 'VIN-B',
plateNumber: '粤B00001',
locationCount: 2,
firstSeen: '2026-07-01 08:00:00',
lastSeen: '2026-07-02 10:00:00',
},
]);
assert.equal(buildVehicleRanking(records, 1).length, 1);
});
test('车辆统计和附近筛选继续使用 VIN、自然日去重及 Haversine 距离', () => {
const records = [
record({ vin: 'VIN-A', date: '2026-07-01' }),
record({ vin: 'VIN-A', date: '2026-07-02', longitude: 120.01 }),
record({ vin: 'VIN-B', date: '2026-07-02', longitude: 121 }),
];
assert.deepEqual(summarizeVehicleRecords(records), {
locationCount: 3,
vehicleCount: 2,
dayCount: 2,
});
assert.equal(haversineKm(120, 30, 120, 30), 0);
assert.ok(Math.abs(haversineKm(0, 0, 0, 1) - 111.195) < 0.01);
assert.deepEqual(
filterVehicleRecordsByRadius(records, { lng: 120, lat: 30 }, 5).map(({ vin, date }) => ({ vin, date })),
[
{ vin: 'VIN-A', date: '2026-07-01' },
{ vin: 'VIN-A', date: '2026-07-02' },
],
);
});