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
-14
View File
@@ -1,14 +0,0 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { buildHydrogenSettlementQuery } from './api.js';
test('现结自定义日期明确传递 custom 范围,避免服务端回退到最近有数据', () => {
assert.equal(
buildHydrogenSettlementQuery({
range: 'custom',
startDate: '2026-05-26',
endDate: '2026-05-26',
}),
'range=custom&startDate=2026-05-26&endDate=2026-05-26',
);
});
+21 -153
View File
@@ -1,117 +1,17 @@
import { fetchJson } from '../../auth/api-client';
import type {
HydrogenKpi, HydrogenStationTop, HydrogenRegionShare, HydrogenMonthlyPoint, HydrogenDailyRow,
HydrogenCustomerRow, HydrogenStationFull,
ElectricKpi, ElectricDailyRow, ElectricMonthGroup,
CustomerType, DateQuickPick,
HydrogenStationBoardResponse,
HydrogenDailyDetailResponse,
HydrogenOverviewDetailGroupBy,
HydrogenOverviewDetailResponse,
} from './types';
const BASE = '/api/energy';
export interface HydrogenOverviewResponse {
kpi: HydrogenKpi;
top5: HydrogenStationTop[];
regions: HydrogenRegionShare[];
monthly: HydrogenMonthlyPoint[];
customers: HydrogenCustomerRow[];
stations: HydrogenStationFull[];
availableYears: number[];
year: number;
latestLedgerTime: string | null;
filter: {
stationId: number | null;
vehicleScope: HydrogenVehicleScope;
verifyScope: HydrogenVerifyScope;
};
}
export type HydrogenVehicleScope = 'all' | 'lingniu' | 'external';
export type HydrogenVerifyScope = 'all' | 'verified';
export interface HydrogenOverviewQuery {
year?: number;
stationId?: number | null;
vehicleScope?: HydrogenVehicleScope;
verifyScope?: HydrogenVerifyScope;
force?: boolean;
}
export function fetchHydrogenOverview(query: HydrogenOverviewQuery = {}): Promise<HydrogenOverviewResponse> {
const params = new URLSearchParams();
if (query.year) params.set('year', String(query.year));
if (query.stationId) params.set('stationId', String(query.stationId));
if (query.vehicleScope && query.vehicleScope !== 'all') params.set('vehicleScope', query.vehicleScope);
if (query.verifyScope === 'verified') params.set('verifyScope', 'verified');
if (query.force) params.set('force', '1');
const q = params.toString();
return fetchJson<HydrogenOverviewResponse>(`${BASE}/hydrogen/overview${q ? `?${q}` : ''}`);
}
export interface HydrogenOverviewDetailQuery {
year: number;
vehicleScope?: HydrogenVehicleScope;
verifyScope?: HydrogenVerifyScope;
stationId?: number | null;
month?: string | null;
date?: string | null;
customerName?: string | null;
customerId?: number | null;
plateNo?: string | null;
region?: string | null;
regionGranularity?: 'province' | 'city';
groupBy?: HydrogenOverviewDetailGroupBy | null;
limit?: number;
}
export function fetchHydrogenOverviewDetail(query: HydrogenOverviewDetailQuery): Promise<HydrogenOverviewDetailResponse> {
const params = new URLSearchParams({ year: String(query.year) });
if (query.vehicleScope && query.vehicleScope !== 'all') params.set('vehicleScope', query.vehicleScope);
if (query.verifyScope === 'verified') params.set('verifyScope', 'verified');
if (query.stationId) params.set('stationId', String(query.stationId));
if (query.month) params.set('month', query.month);
if (query.date) params.set('date', query.date);
if (query.customerName) params.set('customerName', query.customerName);
if (query.customerId !== null && query.customerId !== undefined) params.set('customerId', String(query.customerId));
if (query.plateNo) params.set('plateNo', query.plateNo);
if (query.region) params.set('region', query.region);
if (query.regionGranularity) params.set('regionGranularity', query.regionGranularity);
if (query.groupBy) params.set('groupBy', query.groupBy);
if (query.limit) params.set('limit', String(query.limit));
return fetchJson<HydrogenOverviewDetailResponse>(`${BASE}/hydrogen/overview-detail?${params.toString()}`);
}
export interface HydrogenDailyQuery {
range?: DateQuickPick;
startDate?: string;
endDate?: string;
verifyScope?: HydrogenVerifyScope;
}
export function fetchHydrogenDaily(query: HydrogenDailyQuery, customer: CustomerType): Promise<HydrogenDailyRow[]> {
const q = new URLSearchParams({ customer });
if (query.range) q.set('range', query.range);
if (query.startDate) q.set('startDate', query.startDate);
if (query.endDate) q.set('endDate', query.endDate);
if (query.verifyScope === 'verified') q.set('verifyScope', 'verified');
return fetchJson<HydrogenDailyRow[]>(`${BASE}/hydrogen/daily?${q.toString()}`);
}
export function fetchHydrogenDailyDetail(
date: string,
customer: CustomerType,
stationId?: number | null,
verifyScope: HydrogenVerifyScope = 'all',
): Promise<HydrogenDailyDetailResponse> {
const params = new URLSearchParams({ date, customer });
if (stationId) params.set('stationId', String(stationId));
if (verifyScope === 'verified') params.set('verifyScope', 'verified');
return fetchJson<HydrogenDailyDetailResponse>(`${BASE}/hydrogen/daily-detail?${params.toString()}`);
}
/**
* 氢能单站日报数据源。
* 注意:该接口路径仍带 `/hydrogen` 前缀(历史命名),但为当前单站日报页面在用,
* 与已下线的 `/hydrogen/overview|daily|settlement` 等旧接口无关。
*/
export interface HydrogenStationBoardQuery {
startDate: string;
endDate: string;
@@ -126,6 +26,13 @@ export function fetchHydrogenStationBoard(query: HydrogenStationBoardQuery): Pro
return fetchJson<HydrogenStationBoardResponse>(`${BASE}/hydrogen/station-board?${params.toString()}`);
}
/** 电能与 ETC 共用的自然日区间查询参数。 */
export interface EnergyRangeQuery {
range?: DateQuickPick;
startDate?: string;
endDate?: string;
}
export interface ElectricOverviewResponse {
kpi: ElectricKpi;
trend: ElectricDailyRow[];
@@ -136,12 +43,15 @@ export function fetchElectricOverview(): Promise<ElectricOverviewResponse> {
return fetchJson<ElectricOverviewResponse>(`${BASE}/electric/overview`);
}
export function fetchElectricMonthly(customer: CustomerType, query: HydrogenDailyQuery = { range: 'last15' }): Promise<ElectricMonthGroup[]> {
const q = new URLSearchParams({ customer });
if (query.range) q.set('range', query.range);
if (query.startDate) q.set('startDate', query.startDate);
if (query.endDate) q.set('endDate', query.endDate);
return fetchJson<ElectricMonthGroup[]>(`${BASE}/electric/monthly?${q.toString()}`);
export function fetchElectricMonthly(
customer: CustomerType,
query: EnergyRangeQuery = { range: 'last15' },
): Promise<ElectricMonthGroup[]> {
const params = new URLSearchParams({ customer });
if (query.range) params.set('range', query.range);
if (query.startDate) params.set('startDate', query.startDate);
if (query.endDate) params.set('endDate', query.endDate);
return fetchJson<ElectricMonthGroup[]>(`${BASE}/electric/monthly?${params.toString()}`);
}
export interface EtcOverviewResponse {
@@ -158,45 +68,3 @@ export interface EtcOverviewResponse {
export function fetchEtcOverview(force = false): Promise<EtcOverviewResponse> {
return fetchJson<EtcOverviewResponse>(`${BASE}/etc/overview${force ? '?force=1' : ''}`);
}
export type HydrogenSettlementRange = DateQuickPick | 'latest';
export interface HydrogenSettlementRow {
date: string;
stationId: number | null;
stationName: string;
amount: number;
paymentCount: number;
matchMode: 'exact' | 'manual' | 'group' | 'unmatched';
}
export interface HydrogenSettlementResponse {
range: { start: string | null; end: string | null; mode: HydrogenSettlementRange | 'custom' };
summary: {
amount: number;
paymentCount: number;
stationDayCount: number;
stationCount: number;
latestPaymentDate: string | null;
};
rows: HydrogenSettlementRow[];
}
export interface HydrogenSettlementQuery {
range?: HydrogenSettlementRange | 'custom';
startDate?: string;
endDate?: string;
}
export function buildHydrogenSettlementQuery(query: HydrogenSettlementQuery = {}) {
const params = new URLSearchParams();
if (query.range) params.set('range', query.range);
if (query.startDate) params.set('startDate', query.startDate);
if (query.endDate) params.set('endDate', query.endDate);
return params.toString();
}
export function fetchHydrogenSettlement(query: HydrogenSettlementQuery = {}): Promise<HydrogenSettlementResponse> {
const q = buildHydrogenSettlementQuery(query);
return fetchJson<HydrogenSettlementResponse>(`${BASE}/hydrogen/settlement${q ? `?${q}` : ''}`);
}
+2 -192
View File
@@ -1,127 +1,8 @@
/** 能源模块(电能 / ETC / 单站日报)共用的 DTO 与筛选枚举。 */
export type CustomerType = 'all' | 'external' | 'lingniu';
export type DateQuickPick = 'thisWeek' | 'thisMonth' | 'last15';
export interface HydrogenKpi {
yearKg: number;
yearFee: number;
yearRevenue: number;
// 客户单毛利:对客收入减去客户承担订单的氢气成本,不代表全量经营利润。
yearProfit: number;
ourYearKg: number;
ourYearFee: number;
customerYearKg: number;
monthKg: number;
monthFee: number;
monthRevenue: number;
monthProfit: number;
todayKg: number;
todayFee: number;
todayRevenue: number;
todayProfit: number;
lingniuBornKg: number;
lingniuBornFee: number;
}
export interface HydrogenStationTop {
id: number;
rank: number;
name: string;
kg: number;
fee: number;
share: number;
}
export interface HydrogenRegionShare {
region: string;
kg: number;
share: number;
}
export interface HydrogenMonthlyPoint {
month: string; // YYYY-MM
kg: number;
lingniuKg?: number;
externalKg?: number;
fee: number;
revenue: number;
profit: number;
}
export interface HydrogenCustomerRow {
name: string;
payer: 'lingniu' | 'customer' | 'mixed';
kg: number;
cost: number;
revenue: number;
}
export interface HydrogenStationFull {
id: number;
name: string;
province?: string;
kg: number;
revenue: number;
share: number; // 加氢量占比
revenueShare: number;// 收入占比
}
export interface HydrogenStationRow {
id: number;
name: string;
pricePerKg: number;
kg: number;
fee: number;
chainPct: number;
}
export interface HydrogenDailyRow {
date: string;
totalKg: number;
totalFee: number;
chainPct: number;
customerType: CustomerType;
stations: HydrogenStationRow[];
}
export interface HydrogenDailyDetailVehicle {
id: string;
time: string;
plateNo: string;
vehicleScope: 'lingniu' | 'external';
source: string;
verifyStatus: string;
unitPrice: number;
kg: number;
fee: number;
}
export interface HydrogenDailyDetailCustomer {
id: number;
name: string;
kg: number;
fee: number;
vehicles: HydrogenDailyDetailVehicle[];
}
export interface HydrogenDailyDetailStation {
id: number;
name: string;
stationType: string;
kg: number;
fee: number;
// Null means the source system has no persisted station-balance record for
// this station on or before the selected date; it must not be treated as 0.
balance: number | null;
balanceEffectiveTime: string | null;
customers: HydrogenDailyDetailCustomer[];
}
export interface HydrogenDailyDetailResponse {
date: string;
recordCount: number;
stations: HydrogenDailyDetailStation[];
}
export interface HydrogenStationBoardStation {
id: number;
name: string;
@@ -195,77 +76,6 @@ export interface HydrogenStationBoardResponse {
} | null;
}
export interface HydrogenOverviewDetailRecord {
id: number;
orderNo: string;
refuelTime: string;
stationId: number;
stationName: string;
province: string;
city: string;
customerId: number;
customerName: string;
plateNo: string;
vehicleScope: 'lingniu' | 'external';
source: string;
verifyStatus: string;
kg: number;
costPrice: number;
cost: number;
customerPrice: number;
revenue: number;
paymentStatus: number | null;
}
export type HydrogenOverviewDetailGroupBy = 'station' | 'customer' | 'vehicle';
export interface HydrogenOverviewDetailGroup {
id: number | string;
name: string;
province: string | null;
city: string | null;
recordCount: number;
stationCount: number;
customerCount: number;
kg: number;
cost: number;
customerCost: number;
revenue: number;
vehicleScope?: 'lingniu' | 'external';
source?: string;
verifyStatus?: string;
}
export interface HydrogenOverviewDetailResponse {
filter: {
year: number;
vehicleScope: 'all' | 'lingniu' | 'external';
verifyScope: 'all' | 'verified';
stationId: number | null;
month: string | null;
date: string | null;
customerName: string | null;
customerId: number | null;
plateNo: string | null;
region: string | null;
regionGranularity: 'province' | 'city';
groupBy: HydrogenOverviewDetailGroupBy | null;
};
truncated: boolean;
summary: {
recordCount: number;
stationCount: number;
customerCount: number;
kg: number;
cost: number;
customerCost: number;
revenue: number;
};
groupBy: HydrogenOverviewDetailGroupBy | null;
groups: HydrogenOverviewDetailGroup[];
records: HydrogenOverviewDetailRecord[];
}
export interface ElectricKpi {
totalKwh: number;
totalFee: number;