refactor(stage3): 下线 5 个无调用方的 legacy 氢能接口

先确认调用方再删:/hydrogen/station-board 被单站日报页面(StationDailyApp /
StationDailyDetailView)实际使用,必须保留;其余 5 个确认零调用方。

- 删服务端路由 hydrogen-overview / -overview-detail / -daily / -daily-detail /
  -settlement(约 1,080 行),energy/index.ts 保留原有注册顺序语义。
- 删客户端对应请求函数与类型:api.ts 由 202 行精简为 74 行;
  types.ts 由 291 行精简为 112 行(只保留 StationBoard / Electric / ETC 闭包)。
- 顺带修正命名:HydrogenDailyQuery -> EnergyRangeQuery(它服务于电能按月接口)。
- routes.test.ts 同步收敛:删除 settlement / overview 两个已下线接口的测试,
  '四个处理器' 改为只覆盖电能;路由顺序断言更新为现存 9 条。
  电能 SQL 指纹沿用原值,证明取数口径未变。

测试 131 -> 128(删掉的 3 个用例覆盖的是已下线接口)。lint / test / build 全绿。
This commit is contained in:
dsh-agent
2026-09-11 10:16:43 +08:00
parent 7556dbcebc
commit 680c7e0662
10 changed files with 28 additions and 1738 deletions
+3 -290
View File
@@ -12,18 +12,6 @@ import {
registerEtcOverviewRoute,
type EtcOverviewDependencies,
} from "./etc.js";
import {
registerHydrogenDailyRoute,
type HydrogenDailyDependencies,
} from "./hydrogen-daily.js";
import {
registerHydrogenOverviewRoute,
type HydrogenOverviewDependencies,
} from "./hydrogen-overview.js";
import {
registerHydrogenSettlementRoute,
type HydrogenSettlementDependencies,
} from "./hydrogen-settlement.js";
import {
registerHydrogenStationBoardRoute,
type HydrogenStationBoardDependencies,
@@ -90,11 +78,6 @@ test("能源路由按既有顺序注册并增加只读单站看板接口", () =>
.filter((route) => route.method === "GET")
.map((route) => route.path),
[
"/hydrogen/overview",
"/hydrogen/overview-detail",
"/hydrogen/daily",
"/hydrogen/daily-detail",
"/hydrogen/settlement",
"/hydrogen/station-board",
"/h2/v2/meta",
"/h2/v2/overview",
@@ -704,117 +687,10 @@ test("单站外部客户按唯一标准化车牌聚合,进账保持租户客
assert.match(calls[9].sql, /refuel_time < DATE_ADD\(\?, INTERVAL 1 DAY\)/);
});
test("站日现结台账仅读取付款流水,并按最近有数据的 15 天聚合", async () => {
const calls: QueryCall[] = [];
const cacheCalls: CacheCall[] = [];
const app = new Hono();
registerHydrogenSettlementRoute(app, {
hydrogenPool: {
query: createQueryMock(
[
[{ latestPaymentDate: "2026-05-26" }],
[
{
date: "2026-05-26",
stationId: 44,
stationName: "联新加氢站",
amount: 30000,
paymentCount: 1,
matchMode: "exact",
},
{
date: "2026-05-25",
stationId: null,
stationName: "佛山中石化",
amount: 20000,
paymentCount: 2,
matchMode: "group",
},
],
],
calls,
),
},
cached: createCachedMock(cacheCalls),
} as unknown as HydrogenSettlementDependencies);
const response = await app.request(
"/hydrogen/settlement?range=latest&force=1",
);
assert.equal(response.status, 200);
assert.deepEqual(await response.json(), {
range: { start: "2026-05-12", end: "2026-05-26", mode: "latest" },
summary: {
amount: 50000,
paymentCount: 3,
stationDayCount: 2,
stationCount: 2,
latestPaymentDate: "2026-05-26",
},
rows: [
{
date: "2026-05-26",
stationId: 44,
stationName: "联新加氢站",
amount: 30000,
paymentCount: 1,
matchMode: "exact",
},
{
date: "2026-05-25",
stationId: null,
stationName: "佛山中石化",
amount: 20000,
paymentCount: 2,
matchMode: "group",
},
],
});
assert.deepEqual(cacheCalls, [
{ key: "hydrogen/settlement?start=2026-05-12&end=2026-05-26", force: true },
]);
assert.equal(calls.length, 2);
assert.match(calls[0].sql, /MAX\(payment_date\)/);
assert.match(calls[1].sql, /FROM hydrogen_station_payment p/);
assert.match(calls[1].sql, /LIMIT 500/);
assert.deepEqual(calls[1].params, ["2026-05-12", "2026-05-26"]);
});
test("四个处理器继续使用原缓存语义、数据库、SQL 与参数", async () => {
const hydrogenCalls: QueryCall[] = [];
test("电能处理器继续使用原缓存语义、数据库、SQL 与参数", async () => {
const electricCalls: QueryCall[] = [];
const cacheCalls: CacheCall[] = [];
const cached = createCachedMock(cacheCalls);
const hydrogenQuery = createQueryMock(
[
[{ y: 2025, latestLedgerTime: "2025-01-02 10:30:00" }],
[{}],
[],
[],
[],
[],
[
{
name: "混合承担客户",
payer: "mixed",
kg: 10,
cost: 350,
revenue: 360,
},
],
[
{
d: "2025-01-02",
stationId: 7,
stationName: "测试站",
kg: 10,
fee: 350,
pricePerKg: 35,
},
],
],
hydrogenCalls,
);
const electricQuery = createQueryMock(
[
[
@@ -835,65 +711,6 @@ test("四个处理器继续使用原缓存语义、数据库、SQL 与参数", a
electricCalls,
);
const hydrogenOverviewApp = new Hono();
registerHydrogenOverviewRoute(hydrogenOverviewApp, {
hydrogenPool: { query: hydrogenQuery },
cached,
} as unknown as HydrogenOverviewDependencies);
const overviewResponse = await hydrogenOverviewApp.request(
"/hydrogen/overview?year=2025&force=1",
);
assert.equal(overviewResponse.status, 200);
const overviewPayload = (await overviewResponse.json()) as {
customers: { payer: string; name: string }[];
};
assert.deepEqual(overviewPayload.customers, [
{
name: "混合承担客户",
payer: "mixed",
kg: 10,
cost: 350,
revenue: 360,
},
]);
const hydrogenDailyApp = new Hono();
registerHydrogenDailyRoute(hydrogenDailyApp, {
hydrogenPool: { query: hydrogenQuery },
cached,
} as unknown as HydrogenDailyDependencies);
const dailyResponse = await hydrogenDailyApp.request(
"/hydrogen/daily?startDate=2025-01-01&endDate=2025-01-02&customer=external&force=1",
);
assert.equal(dailyResponse.status, 200);
assert.deepEqual(await dailyResponse.json(), [
{
date: "2025-01-02",
totalKg: 10,
totalFee: 350,
chainPct: 0,
customerType: "external",
stations: [
{
id: 7,
name: "测试站",
pricePerKg: 35,
kg: 10,
fee: 350,
chainPct: 0,
},
],
},
{
date: "2025-01-01",
totalKg: 0,
totalFee: 0,
chainPct: 0,
customerType: "external",
stations: [],
},
]);
const electricApp = new Hono();
const electricDependencies = {
pool: { query: electricQuery },
@@ -911,11 +728,6 @@ test("四个处理器继续使用原缓存语义、数据库、SQL 与参数", a
assert.equal(electricMonthlyResponse.status, 200);
assert.deepEqual(cacheCalls, [
{ key: "hydrogen/overview?year=2025", force: true },
{
key: "hydrogen/daily?start=2025-01-01&end=2025-01-02&customer=external",
force: true,
},
{ key: "electric/overview", force: true },
{
key: "electric/monthly?customer=external&start=2025-01-01&end=2025-01-02",
@@ -923,18 +735,10 @@ test("四个处理器继续使用原缓存语义、数据库、SQL 与参数", a
},
]);
// 指纹覆盖加氢事实专用过滤与既有查询结构,避免手工充值重新混入统计。
// 指纹锁定既有查询结构,避免取数口径被无意改动。
assert.deepEqual(
[...hydrogenCalls, ...electricCalls].map((call) => sqlHash(call.sql)),
electricCalls.map((call) => sqlHash(call.sql)),
[
"4fa77c56a990",
"ac6329507bc2",
"e46a92ba851e",
"0295b2deee31",
"852b5b0b9fe1",
"abad6dcf0b2f",
"41cc73632c26",
"1224ee38f710",
"03fd669f8077",
"6e4031b10926",
"4fc77463294c",
@@ -943,103 +747,12 @@ test("四个处理器继续使用原缓存语义、数据库、SQL 与参数", a
],
);
assert.deepEqual(
hydrogenCalls.map((call) => call.params),
[
["2024-01-01"],
[
2025,
2025,
2025,
2025,
2025,
2025,
2025,
2025,
0,
0,
0,
0,
0,
0,
0,
0,
"2024-01-01",
],
["2024-01-01", 2025],
["2024-01-01", 2025],
["2024-01-01", 2025],
["2024-01-01", 2025],
["2024-01-01", 2025],
["2025-01-01", "2025-01-02"],
],
);
assert.match(hydrogenCalls[7].sql, /b\.vehicle_id IS NULL/);
assert.ok(hydrogenCalls.every((call) =>
call.sql.includes("external_recharge_manual"),
));
assert.deepEqual(
electricCalls.map((call) => call.params),
[undefined, undefined, undefined, undefined, ["2025-01-01", "2025-01-02"]],
);
});
test("氢能总览按车辆归属和单站筛选,并隔离缓存结果", async () => {
const calls: QueryCall[] = [];
const cacheCalls: CacheCall[] = [];
const app = new Hono();
registerHydrogenOverviewRoute(app, {
hydrogenPool: {
query: createQueryMock(
[
[{ y: 2026, latestLedgerTime: "2026-08-17 21:50:09" }],
[{ yearKg: 12, latestLedgerTime: "2026-08-17 21:50:09" }],
[],
[],
[],
[],
[],
],
calls,
),
},
cached: createCachedMock(cacheCalls),
} as unknown as HydrogenOverviewDependencies);
const response = await app.request(
"/hydrogen/overview?year=2026&stationId=7&vehicleScope=lingniu",
);
const payload = await response.json();
assert.equal(response.status, 200);
assert.equal(payload.latestLedgerTime, "2026-08-17 21:50:09");
assert.deepEqual(cacheCalls, [
{
key: "hydrogen/overview?year=2026&station=7&vehicle=lingniu",
force: false,
},
]);
assert.deepEqual(payload.filter, {
stationId: 7,
vehicleScope: "lingniu",
verifyScope: "all",
});
// 年份候选项是全局元数据;其余查询都必须带上单站筛选参数。
assert.ok(
calls
.slice(1)
.every(
(call) =>
Array.isArray(call.params) && (call.params as unknown[]).includes(7),
),
);
assert.match(calls[1].sql, /station_id = \? AND vehicle_id IS NOT NULL/);
assert.match(
calls[2].sql,
/b\.station_id = \? AND b\.vehicle_id IS NOT NULL/,
);
});
test("ETC 总览对空台账返回明确空状态", async () => {
const calls: QueryCall[] = [];
const cacheCalls: CacheCall[] = [];