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:
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user