每日加氢量
@@ -184,8 +164,7 @@ export default function HydrogenDaily() {
)}
- {/* 表格(外部车辆 + 全 0 时不渲染,由上方友好空状态替代) */}
- {!(customer === 'external' && rows !== null && totalKg === 0) && (
+ {/* 日明细:加载失败、无订单与正常数据分别展示。 */}
{/* 表头 */}
@@ -206,8 +185,8 @@ export default function HydrogenDaily() {
) : rows === null ? (
- ) : rows.length === 0 ? (
-
+ ) : rows.length === 0 || totalKg === 0 ? (
+
) : rows.map(r => {
const open = expanded.has(r.date);
const isAbnormal = Math.abs(r.chainPct) >= 0.3;
@@ -270,7 +249,6 @@ export default function HydrogenDaily() {
);
})}
- )}
);
diff --git a/src/server/routes/energy/hydrogen-daily.ts b/src/server/routes/energy/hydrogen-daily.ts
index 57a7a65..0ee5210 100644
--- a/src/server/routes/energy/hydrogen-daily.ts
+++ b/src/server/routes/energy/hydrogen-daily.ts
@@ -39,7 +39,7 @@ export function registerHydrogenDailyRoute(
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'),
+ customerClause(customer),
].join(' AND ');
// 站点级聚合(每日 × 每站)。前端组装成 day → stations
diff --git a/src/server/routes/energy/query-model.test.ts b/src/server/routes/energy/query-model.test.ts
index 02aa74e..a046814 100644
--- a/src/server/routes/energy/query-model.test.ts
+++ b/src/server/routes/energy/query-model.test.ts
@@ -1,5 +1,6 @@
import assert from 'node:assert/strict';
import test from 'node:test';
+import { DatabaseSync } from 'node:sqlite';
import {
customerClause,
dateRangeClause,
@@ -7,10 +8,31 @@ import {
resolveDateRange,
} from './query-model.js';
-test('客户范围继续让外部车辆返回空数据', () => {
- assert.equal(customerClause('external'), '1=0');
- assert.equal(customerClause('lingniu'), '1=1');
- assert.equal(customerClause('all'), '1=1');
+test('外部车辆只取佛山羚牛站未关联车辆的订单,车牌文字和收费不影响分类', () => {
+ const db = new DatabaseSync(':memory:');
+ try {
+ db.exec(`CREATE TABLE orders (
+ id INTEGER, station_id INTEGER, vehicle_id INTEGER,
+ license_plate TEXT, customer_price REAL, del_flag TEXT
+ );
+ INSERT INTO orders VALUES
+ (1, 341, NULL, '粤A外部', 38, '0'),
+ (2, 341, NULL, '', 0, '0'),
+ (3, 341, 123, '粤A羚牛', 38, '0'),
+ (4, 52, NULL, '粤A其他站', 38, '0'),
+ (5, NULL, NULL, '', 0, '0'),
+ (6, NULL, 456, '粤A未关联站点', 0, '0'),
+ (7, 341, NULL, '粤A已删除', 38, '1');`);
+ const ids = (kind: 'external' | 'lingniu' | 'all') => db
+ .prepare(`SELECT b.id FROM orders b WHERE b.del_flag = '0' AND ${customerClause(kind)} ORDER BY b.id`)
+ .all().map(row => row.id);
+ assert.deepEqual(ids('external'), [1, 2]);
+ assert.deepEqual(ids('lingniu'), [3, 4, 5, 6]);
+ assert.deepEqual(ids('all'), [1, 2, 3, 4, 5, 6]);
+ assert.deepEqual([...ids('external'), ...ids('lingniu')].sort(), ids('all'));
+ } finally {
+ db.close();
+ }
});
test('能源日期快捷区间继续按本地自然日计算', () => {
diff --git a/src/server/routes/energy/query-model.ts b/src/server/routes/energy/query-model.ts
index eb2b07a..71fb540 100644
--- a/src/server/routes/energy/query-model.ts
+++ b/src/server/routes/energy/query-model.ts
@@ -8,10 +8,14 @@ export interface EnergyDateRange {
const YMD_RE = /^\d{4}-\d{2}-\d{2}$/;
-// 新账本目前只承载羚牛车辆订单;外部车辆仍需返回空集合而不是混用羚牛数据。
+// 主数据 ID 341:佛山南海羚牛加氢站。车牌文字存在不代表已关联车辆。
+const FOSHAN_LINGNIU_STATION_ID = 341;
+
+// 日明细账本使用别名 b;两类互斥,其他订单继续保留在原羚牛范围内。
export function customerClause(customer: CustomerKind): string {
- if (customer === 'external') return '1=0';
- if (customer === 'lingniu') return '1=1';
+ const external = `(COALESCE(b.station_id, 0) = ${FOSHAN_LINGNIU_STATION_ID} AND b.vehicle_id IS NULL)`;
+ if (customer === 'external') return external;
+ if (customer === 'lingniu') return `NOT ${external}`;
return '1=1';
}
diff --git a/src/server/routes/energy/routes.test.ts b/src/server/routes/energy/routes.test.ts
index 8e3fdf8..86fddf3 100644
--- a/src/server/routes/energy/routes.test.ts
+++ b/src/server/routes/energy/routes.test.ts
@@ -105,6 +105,11 @@ test('四个处理器保持缓存和参数契约,加氢站点使用新主数
'/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, chainPct: 0, customerType: 'external',
+ stations: [{ name: '测试站', pricePerKg: 35, kg: 10, chainPct: 0 }] },
+ { date: '2025-01-01', totalKg: 0, chainPct: 0, customerType: 'external', stations: [] },
+ ]);
const electricApp = new Hono();
const electricDependencies = {
@@ -127,7 +132,7 @@ test('四个处理器保持缓存和参数契约,加氢站点使用新主数
{ key: 'electric/monthly?customer=external&start=2025-01-01&end=2025-01-02', force: true },
]);
- // 指纹来自拆分前的 13 条 SQL,连空白符变化也会使测试失败。
+ // 保留查询契约指纹;日明细已更新为佛山羚牛站未关联车辆的外部订单筛选。
assert.deepEqual(
[...hydrogenCalls, ...electricCalls].map(call => sqlHash(call.sql)),
[
@@ -138,7 +143,7 @@ test('四个处理器保持缓存和参数契约,加氢站点使用新主数
'256ac8309d5a',
'dac9e86fc527',
'a71da8f05765',
- '24b23780a259',
+ 'd1620280f520',
'226fd8f950a1',
'6e4031b10926',
'4fc77463294c',