From e187c0d02e9c973474c2d8600d28b38196a54e36 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Wed, 29 Apr 2026 20:23:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(energy):=20=E5=98=89=E7=87=83=E7=BB=8F?= =?UTF-8?q?=E5=BC=80=E7=AB=99=E5=AD=98=E5=9C=A8=20JQ=20=E5=8D=95=E6=97=B6?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E8=BF=87=E6=BB=A4=20GF=5FHECRI=5FBILL=20?= =?UTF-8?q?=E5=8E=86=E5=8F=B2=E8=AE=A2=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务背景:旧系统加氢账单使用 GF_HECRI_BILL 前缀,新系统统一改用 JQ 前缀。 切换期间两套数据共存,会重复计入加氢量。约定:当嘉兴嘉燃经开站出现 JQ 订单(视为切到新单号体系),全局过滤掉 GF_HECRI_BILL 前缀的历史订单。 实现: - shouldFilterGfBills() 探测嘉兴嘉燃经开站是否有 JQ 单,结果缓存 5 分钟 - GF_EXCLUDE_CLAUSE = b.bill_code NOT LIKE 'GF\_HECRI\_BILL%' ESCAPE '\\' - 应用到 4 处氢能查询:KPI、Top5、区域占比、daily 站点聚合 实测:当前嘉燃经开 4508 JQ + 665 GF,嘉锦 11783 JQ + 1 GF,全部 GF 已隐藏。 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/server/routes/energy/index.ts | 36 +++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/server/routes/energy/index.ts b/src/server/routes/energy/index.ts index 2fb28d0..94ccb86 100644 --- a/src/server/routes/energy/index.ts +++ b/src/server/routes/energy/index.ts @@ -22,6 +22,31 @@ function customerClause(field: string, customer: CustomerKind): string { type Range = 'today' | 'thisWeek' | 'thisMonth' | 'thisQuarter' | 'last7' | 'last30'; +// 是否应过滤 GF_HECRI_BILL 前缀的加氢订单: +// 当嘉兴嘉燃经开站存在 JQ 前缀订单时(说明业务已切到新单号体系), +// 把 GF_HECRI_BILL 历史/重复订单过滤掉;否则不过滤。 +// 结果缓存 5 分钟,避免每次请求都跑探测查询。 +let gfFilterCache: { value: boolean; expiresAt: number } | null = null; +const GF_FILTER_TTL_MS = 5 * 60 * 1000; +async function shouldFilterGfBills(): Promise { + const now = Date.now(); + if (gfFilterCache && gfFilterCache.expiresAt > now) return gfFilterCache.value; + const [rows] = await pool.query( + `SELECT 1 FROM tab_energy_hydrogen_bill b + LEFT JOIN tab_hydrogen_site s ON s.id = b.hydrogen_station_id + LEFT JOIN tab_outside_hydrogen_site os ON os.inner_site_id = b.hydrogen_station_id + WHERE b.is_deleted = 0 + AND b.bill_code LIKE 'JQ%' + AND (s.short_name LIKE '%嘉燃经开%' OR s.name LIKE '%嘉燃经开%' + OR os.station_name LIKE '%嘉燃经开%' OR os.fixed_station_name LIKE '%嘉燃经开%') + LIMIT 1`, + ); + const value = rows.length > 0; + gfFilterCache = { value, expiresAt: now + GF_FILTER_TTL_MS }; + return value; +} +const GF_EXCLUDE_CLAUSE = `b.bill_code NOT LIKE 'GF\\_HECRI\\_BILL%' ESCAPE '\\\\'`; + function rangeClause(localExpr: string, range: Range): string { switch (range) { case 'today': return `DATE(${localExpr}) = CURDATE()`; @@ -38,6 +63,9 @@ function rangeClause(localExpr: string, range: Range): string { // ========================================================= app.get('/hydrogen/overview', async (c) => { const data = await cached('hydrogen/overview', async () => { + const filterGf = await shouldFilterGfBills(); + const gfClauseKpi = filterGf ? `AND bill_code NOT LIKE 'GF\\_HECRI\\_BILL%' ESCAPE '\\\\'` : ''; + const gfClause = filterGf ? `AND ${GF_EXCLUDE_CLAUSE}` : ''; // KPI(年/月/日 + 我方/客户分解 + 累计羚牛承担) const [kpiRows] = await pool.query( `SELECT @@ -64,7 +92,7 @@ app.get('/hydrogen/overview', async (c) => { SUM(CASE WHEN truck_id IS NOT NULL THEN cost_expense ELSE 0 END) AS lingniuBornFee FROM tab_energy_hydrogen_bill - WHERE is_deleted = 0 AND hydrogen_time >= ?`, + WHERE is_deleted = 0 AND hydrogen_time >= ? ${gfClauseKpi}`, [HYDROGEN_MIN_DATE], ); const k = kpiRows[0] ?? {}; @@ -99,6 +127,7 @@ app.get('/hydrogen/overview', async (c) => { WHERE b.is_deleted = 0 AND b.hydrogen_time >= ? AND YEAR(DATE_ADD(b.hydrogen_time, INTERVAL 8 HOUR)) = YEAR(CURDATE()) + ${gfClause} GROUP BY b.hydrogen_station_id ORDER BY kg DESC LIMIT 5`, @@ -123,7 +152,8 @@ app.get('/hydrogen/overview', async (c) => { LEFT JOIN tab_outside_hydrogen_site os ON os.inner_site_id = b.hydrogen_station_id WHERE b.is_deleted = 0 AND b.hydrogen_time >= ? - AND YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) + AND YEAR(DATE_ADD(b.hydrogen_time, INTERVAL 8 HOUR)) = YEAR(CURDATE()) + ${gfClause} ) r GROUP BY region ORDER BY kg DESC`, @@ -156,11 +186,13 @@ app.get('/hydrogen/daily', async (c) => { const data = await cached(`hydrogen/daily?range=${range}&customer=${customer}`, async () => { + const filterGf = await shouldFilterGfBills(); const where = [ 'b.is_deleted = 0', `b.hydrogen_time >= '${HYDROGEN_MIN_DATE}'`, rangeClause(`b.hydrogen_time + INTERVAL 8 HOUR`, range), customerClause('b.truck_id', customer), + filterGf ? GF_EXCLUDE_CLAUSE : '1=1', ].join(' AND '); // 站点级聚合(每日 × 每站)。前端组装成 day → stations