refactor: add hydrogen customer drilldown

This commit is contained in:
kkfluous
2026-08-07 14:45:36 +08:00
parent 71d0091457
commit 757ed1ebd6
8 changed files with 134 additions and 12 deletions
+17 -3
View File
@@ -5,6 +5,7 @@ import hydrogenPool from '../../hydrogen-db.js';
import { cached } from './cache.js';
import {
parseHydrogenCustomerKind,
parseHydrogenCustomerName,
parseHydrogenStationId,
type HydrogenCustomerKind,
} from './query.js';
@@ -370,16 +371,25 @@ 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 = parseHydrogenCustomerKind(c.req.query('customer'));
const stationId = parseHydrogenStationId(c.req.query('stationId'));
const stationIdParam = c.req.query('stationId');
const customerNameParam = c.req.query('customerName');
const stationId = parseHydrogenStationId(stationIdParam);
const customerName = parseHydrogenCustomerName(customerNameParam);
if (stationIdParam !== undefined && stationId === null) {
return c.json({ error: 'stationId 必须是非负整数' }, 400);
}
if (customerNameParam !== undefined && customerName === null) {
return c.json({ error: 'customerName 必须是 1-128 个字符' }, 400);
}
const force = c.req.query('force') === '1';
const data = await cached(`hydrogen/daily?start=${dateRange.start}&end=${dateRange.end}&customer=${customer}&station=${stationId ?? 'all'}`, async () => {
const data = await cached(`hydrogen/daily?start=${dateRange.start}&end=${dateRange.end}&customer=${customer}&station=${stationId ?? 'all'}&customerName=${encodeURIComponent(customerName ?? 'all')}`, async () => {
const whereParts = [
HYDROGEN_BASE_WHERE_B,
@@ -392,6 +402,10 @@ app.get('/hydrogen/daily', async (c) => {
whereParts.push('COALESCE(b.station_id, 0) = ?');
whereParams.push(stationId);
}
if (customerName !== null) {
whereParts.push("COALESCE(NULLIF(TRIM(b.customer_name), ''), '未指定客户') = ?");
whereParams.push(customerName);
}
const where = whereParts.join(' AND ');
// 站点级聚合(每日 × 每站)。前端组装成 day → stations
+20
View File
@@ -1,7 +1,9 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import app from './index.js';
import {
parseHydrogenCustomerKind,
parseHydrogenCustomerName,
parseHydrogenStationId,
} from './query.js';
@@ -19,3 +21,21 @@ test('defaults unknown customer scopes to lingniu', () => {
assert.equal(parseHydrogenCustomerKind('invalid'), 'lingniu');
assert.equal(parseHydrogenCustomerKind(undefined), 'lingniu');
});
test('normalizes bounded customer names for exact matching', () => {
assert.equal(parseHydrogenCustomerName(' 武汉客户 '), '武汉客户');
assert.equal(parseHydrogenCustomerName('未指定客户'), '未指定客户');
assert.equal(parseHydrogenCustomerName(' '), null);
assert.equal(parseHydrogenCustomerName('x'.repeat(129)), null);
assert.equal(parseHydrogenCustomerName(undefined), null);
});
test('rejects invalid entity filters before querying hydrogen data', async () => {
const invalidStation = await app.request('/hydrogen/daily?stationId=-1');
assert.equal(invalidStation.status, 400);
assert.deepEqual(await invalidStation.json(), { error: 'stationId 必须是非负整数' });
const invalidCustomer = await app.request(`/hydrogen/daily?customerName=${'x'.repeat(129)}`);
assert.equal(invalidCustomer.status, 400);
assert.deepEqual(await invalidCustomer.json(), { error: 'customerName 必须是 1-128 个字符' });
});
+6
View File
@@ -10,3 +10,9 @@ export function parseHydrogenStationId(value: string | undefined): number | null
const parsed = Number(value);
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
}
export function parseHydrogenCustomerName(value: string | undefined): string | null {
if (value == null) return null;
const normalized = value.trim();
return normalized.length > 0 && normalized.length <= 128 ? normalized : null;
}