refactor: add hydrogen station drilldown

This commit is contained in:
kkfluous
2026-08-07 14:41:29 +08:00
parent d53257c65c
commit 71d0091457
9 changed files with 361 additions and 25 deletions
+22 -9
View File
@@ -3,6 +3,11 @@ import type { RowDataPacket } from 'mysql2';
import pool from '../../db.js';
import hydrogenPool from '../../hydrogen-db.js';
import { cached } from './cache.js';
import {
parseHydrogenCustomerKind,
parseHydrogenStationId,
type HydrogenCustomerKind,
} from './query.js';
import type { AuthUser } from '../../auth/types.js';
import { canAccessEnergy } from '../../auth/types.js';
@@ -27,10 +32,8 @@ const HYDROGEN_BASE_WHERE = `del_flag = '0'`;
const HYDROGEN_BASE_WHERE_B = `b.del_flag = '0'`;
const ELECTRIC_LOCAL = `charging_start_time`;
type CustomerKind = 'external' | 'lingniu' | 'all';
// 新账本 hydrogen_fuel_ledger 当前只承载羚牛车辆订单;外部车辆数据源待接入。
function customerClause(customer: CustomerKind): string {
function customerClause(customer: HydrogenCustomerKind): string {
if (customer === 'external') return '1=0';
if (customer === 'lingniu') return '1=1';
return '1=1';
@@ -229,6 +232,7 @@ app.get('/hydrogen/overview', async (c) => {
);
const top5KgSum = kpi.yearKg || 1;
const top5 = top5Rows.map((r, i) => ({
id: Number(r.id) || 0,
rank: i + 1,
name: r.name as string,
kg: Number(r.kg) || 0,
@@ -256,6 +260,7 @@ app.get('/hydrogen/overview', async (c) => {
const stationKgSum = stationFullRows.reduce((s, r) => s + (Number(r.kg) || 0), 0) || 1;
const stationRevSum = stationFullRows.reduce((s, r) => s + (Number(r.revenue) || 0), 0) || 1;
const stations = stationFullRows.map(r => ({
id: Number(r.id) || 0,
name: r.name as string,
kg: Number(r.kg) || 0,
revenue: Number(r.revenue) || 0,
@@ -370,17 +375,24 @@ app.get('/hydrogen/overview', async (c) => {
app.get('/hydrogen/daily', async (c) => {
const range = (c.req.query('range') || 'last15') as Range;
const dateRange = resolveDateRange(range, c.req.query('startDate'), c.req.query('endDate'));
const customer = (c.req.query('customer') || 'external') as CustomerKind;
const customer = parseHydrogenCustomerKind(c.req.query('customer'));
const stationId = parseHydrogenStationId(c.req.query('stationId'));
const force = c.req.query('force') === '1';
const data = await cached(`hydrogen/daily?start=${dateRange.start}&end=${dateRange.end}&customer=${customer}`, async () => {
const data = await cached(`hydrogen/daily?start=${dateRange.start}&end=${dateRange.end}&customer=${customer}&station=${stationId ?? 'all'}`, async () => {
const where = [
const whereParts = [
HYDROGEN_BASE_WHERE_B,
`b.${HYDROGEN_LOCAL} >= '${HYDROGEN_MIN_DATE}'`,
dateRangeClause(`b.${HYDROGEN_LOCAL}`),
customerClause(customer).replaceAll('customer_price', 'b.customer_price').replaceAll('fee_total', 'b.fee_total'),
].join(' AND ');
];
const whereParams: Array<string | number> = [dateRange.start, dateRange.end];
if (stationId !== null) {
whereParts.push('COALESCE(b.station_id, 0) = ?');
whereParams.push(stationId);
}
const where = whereParts.join(' AND ');
// 站点级聚合(每日 × 每站)。前端组装成 day → stations
// 站点名 fallback:站点主数据 → 账本冗余站点名 → 未关联站点
@@ -399,7 +411,7 @@ app.get('/hydrogen/daily', async (c) => {
WHERE ${where}
GROUP BY d, COALESCE(b.station_id, 0)
ORDER BY d DESC, kg DESC`,
[dateRange.start, dateRange.end],
whereParams,
);
// 站点环比:同站点上一条记录的 kg
@@ -459,6 +471,7 @@ app.get('/hydrogen/daily', async (c) => {
customerType: customer,
stations: info
? info.stations.slice().sort((a, b) => b.kg - a.kg).map(s => ({
stationId: s.stationId,
name: s.name,
pricePerKg: Math.round(s.pricePerKg * 100) / 100,
kg: Math.round(s.kg * 100) / 100,
@@ -571,7 +584,7 @@ app.get('/electric/overview', async (c) => {
// 缺失日期补零
// =========================================================
app.get('/electric/monthly', async (c) => {
const customer = (c.req.query('customer') || 'lingniu') as CustomerKind;
const customer = parseHydrogenCustomerKind(c.req.query('customer'));
const range = (c.req.query('range') || 'last15') as Range;
const dateRange = resolveDateRange(range, c.req.query('startDate'), c.req.query('endDate'));
const force = c.req.query('force') === '1';