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,374 +0,0 @@
|
||||
import type { Hono } from 'hono';
|
||||
import type { RowDataPacket } from 'mysql2';
|
||||
import type hydrogenPool from '../../hydrogen-db.js';
|
||||
import type { cached } from './cache.js';
|
||||
import {
|
||||
HYDROGEN_FUEL_ONLY_WHERE,
|
||||
HYDROGEN_FUEL_ONLY_WHERE_B,
|
||||
HYDROGEN_LOCAL,
|
||||
HYDROGEN_MIN_DATE,
|
||||
HYDROGEN_TABLE,
|
||||
} from './constants.js';
|
||||
|
||||
export interface HydrogenOverviewDependencies {
|
||||
hydrogenPool: Pick<typeof hydrogenPool, 'query'>;
|
||||
cached: typeof cached;
|
||||
}
|
||||
|
||||
type VehicleScope = 'all' | 'lingniu' | 'external';
|
||||
type VerifyScope = 'all' | 'verified';
|
||||
|
||||
interface OverviewFilter {
|
||||
cacheKey: string;
|
||||
ledgerSql: string;
|
||||
billSql: string;
|
||||
params: number[];
|
||||
stationId: number | null;
|
||||
vehicleScope: VehicleScope;
|
||||
verifyScope: VerifyScope;
|
||||
}
|
||||
|
||||
function overviewFilter(
|
||||
stationIdParam: string | undefined,
|
||||
vehicleScopeParam: string | undefined,
|
||||
verifyScopeParam: string | undefined,
|
||||
): OverviewFilter {
|
||||
const stationId = Number(stationIdParam);
|
||||
const safeStationId = Number.isInteger(stationId) && stationId > 0 ? stationId : null;
|
||||
const vehicleScope: VehicleScope = vehicleScopeParam === 'lingniu' || vehicleScopeParam === 'external'
|
||||
? vehicleScopeParam
|
||||
: 'all';
|
||||
const verifyScope: VerifyScope = verifyScopeParam === 'verified' ? 'verified' : 'all';
|
||||
const clauses: string[] = [];
|
||||
const params: number[] = [];
|
||||
|
||||
if (safeStationId) {
|
||||
clauses.push('station_id = ?');
|
||||
params.push(safeStationId);
|
||||
}
|
||||
if (vehicleScope === 'lingniu') clauses.push('vehicle_id IS NOT NULL');
|
||||
if (vehicleScope === 'external') clauses.push('vehicle_id IS NULL');
|
||||
// Only use the ledger's persisted verification state; never infer verification
|
||||
// from import source or payment status.
|
||||
if (verifyScope === 'verified') {
|
||||
clauses.push("LOWER(COALESCE(NULLIF(TRIM(verify_status), ''), 'unverified')) = 'verified'");
|
||||
}
|
||||
|
||||
const suffix = clauses.length ? ` AND ${clauses.join(' AND ')}` : '';
|
||||
const billSuffix = suffix
|
||||
.replaceAll('station_id', 'b.station_id')
|
||||
.replaceAll('vehicle_id', 'b.vehicle_id')
|
||||
.replaceAll('verify_status', 'b.verify_status');
|
||||
const cacheParts = [
|
||||
safeStationId ? `station=${safeStationId}` : '',
|
||||
vehicleScope !== 'all' ? `vehicle=${vehicleScope}` : '',
|
||||
verifyScope !== 'all' ? `verify=${verifyScope}` : '',
|
||||
].filter(Boolean);
|
||||
|
||||
return {
|
||||
cacheKey: cacheParts.length ? `&${cacheParts.join('&')}` : '',
|
||||
ledgerSql: suffix,
|
||||
billSql: billSuffix,
|
||||
params,
|
||||
stationId: safeStationId,
|
||||
vehicleScope,
|
||||
verifyScope,
|
||||
};
|
||||
}
|
||||
|
||||
// 氢能总览:KPI + 趋势 + 站点/区域/客户汇总。筛选只使用账本中可验证的站点和车辆归属字段。
|
||||
export function registerHydrogenOverviewRoute(
|
||||
app: Hono,
|
||||
{ hydrogenPool, cached }: HydrogenOverviewDependencies,
|
||||
) {
|
||||
app.get('/hydrogen/overview', async (c) => {
|
||||
const yearParam = c.req.query('year');
|
||||
const force = c.req.query('force') === '1';
|
||||
const filter = overviewFilter(c.req.query('stationId'), c.req.query('vehicleScope'), c.req.query('verifyScope'));
|
||||
const today = new Date();
|
||||
const todayYear = today.getFullYear();
|
||||
const requestedYear = yearParam ? Number(yearParam) || todayYear : todayYear;
|
||||
|
||||
const data = await cached(`hydrogen/overview?year=${requestedYear}${filter.cacheKey}`, async () => {
|
||||
// 可选年份(数据自 HYDROGEN_MIN_DATE 起)
|
||||
const [yearListRows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT YEAR(${HYDROGEN_LOCAL}) AS y,
|
||||
DATE_FORMAT(MAX(${HYDROGEN_LOCAL}), '%Y-%m-%d %H:%i:%s') AS latestLedgerTime
|
||||
FROM ${HYDROGEN_TABLE}
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE} AND ${HYDROGEN_LOCAL} >= ?
|
||||
GROUP BY YEAR(${HYDROGEN_LOCAL})
|
||||
ORDER BY y DESC`,
|
||||
[HYDROGEN_MIN_DATE],
|
||||
);
|
||||
const availableYears = yearListRows.map(r => Number(r.y)).filter(y => y > 0);
|
||||
// 数据源时间来自账本,不等同于前端请求完成时间。
|
||||
const year = availableYears.includes(requestedYear) ? requestedYear : (availableYears[0] ?? todayYear);
|
||||
// The watermark is calculated with the active scope below. The year list
|
||||
// remains global metadata so users can still select a year with no matching
|
||||
// verified/vehicle/station records.
|
||||
let latestLedgerTime: string | null = null;
|
||||
const isCurrentYear = year === todayYear;
|
||||
|
||||
// KPI(按 year 分桶;月/日仅在 isCurrentYear 时取本月/今日)。
|
||||
// yearProfit 是客户单毛利:仅以存在对客价/对客收入的订单成本抵扣对客收入。
|
||||
const [kpiRows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT DATE_FORMAT(MAX(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ? THEN ${HYDROGEN_LOCAL} END), '%Y-%m-%d %H:%i:%s') AS latestLedgerTime,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ?
|
||||
THEN amount_kg ELSE 0 END) AS yearKg,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ?
|
||||
THEN cost_total ELSE 0 END) AS yearFee,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ? AND (COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0)
|
||||
THEN cost_total ELSE 0 END) AS yearCustomerCost,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ?
|
||||
THEN fee_total ELSE 0 END) AS yearRevenue,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ? AND COALESCE(customer_price, 0) <= 0 AND COALESCE(fee_total, 0) <= 0
|
||||
THEN amount_kg ELSE 0 END) AS ourYearKg,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ? AND COALESCE(customer_price, 0) <= 0 AND COALESCE(fee_total, 0) <= 0
|
||||
THEN cost_total ELSE 0 END) AS ourYearFee,
|
||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = ? AND (COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0)
|
||||
THEN amount_kg ELSE 0 END) AS customerYearKg,
|
||||
SUM(CASE WHEN ? = 1 AND DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
|
||||
THEN amount_kg ELSE 0 END) AS monthKg,
|
||||
SUM(CASE WHEN ? = 1 AND DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
|
||||
THEN cost_total ELSE 0 END) AS monthFee,
|
||||
SUM(CASE WHEN ? = 1 AND DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
|
||||
AND (COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0)
|
||||
THEN cost_total ELSE 0 END) AS monthCustomerCost,
|
||||
SUM(CASE WHEN ? = 1 AND DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
|
||||
THEN fee_total ELSE 0 END) AS monthRevenue,
|
||||
SUM(CASE WHEN ? = 1 AND DATE(${HYDROGEN_LOCAL}) = CURDATE()
|
||||
THEN amount_kg ELSE 0 END) AS todayKg,
|
||||
SUM(CASE WHEN ? = 1 AND DATE(${HYDROGEN_LOCAL}) = CURDATE()
|
||||
THEN cost_total ELSE 0 END) AS todayFee,
|
||||
SUM(CASE WHEN ? = 1 AND DATE(${HYDROGEN_LOCAL}) = CURDATE()
|
||||
AND (COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0)
|
||||
THEN cost_total ELSE 0 END) AS todayCustomerCost,
|
||||
SUM(CASE WHEN ? = 1 AND DATE(${HYDROGEN_LOCAL}) = CURDATE()
|
||||
THEN fee_total ELSE 0 END) AS todayRevenue,
|
||||
SUM(CASE WHEN vehicle_id IS NOT NULL
|
||||
THEN amount_kg ELSE 0 END) AS lingniuBornKg,
|
||||
SUM(CASE WHEN vehicle_id IS NOT NULL
|
||||
THEN cost_total ELSE 0 END) AS lingniuBornFee
|
||||
FROM ${HYDROGEN_TABLE}
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE} AND ${HYDROGEN_LOCAL} >= ?${filter.ledgerSql}`,
|
||||
[year, year, year, year, year, year, year, year,
|
||||
isCurrentYear ? 1 : 0, isCurrentYear ? 1 : 0, isCurrentYear ? 1 : 0, isCurrentYear ? 1 : 0,
|
||||
isCurrentYear ? 1 : 0, isCurrentYear ? 1 : 0, isCurrentYear ? 1 : 0, isCurrentYear ? 1 : 0,
|
||||
HYDROGEN_MIN_DATE, ...filter.params],
|
||||
);
|
||||
const k = kpiRows[0] ?? {};
|
||||
latestLedgerTime = typeof k.latestLedgerTime === 'string' ? k.latestLedgerTime : null;
|
||||
const yearFee = Number(k.yearFee) || 0;
|
||||
const yearCustomerCost = Number(k.yearCustomerCost) || 0;
|
||||
const yearRevenue = Number(k.yearRevenue) || 0;
|
||||
const monthFee = Number(k.monthFee) || 0;
|
||||
const monthCustomerCost = Number(k.monthCustomerCost) || 0;
|
||||
const monthRevenue = Number(k.monthRevenue) || 0;
|
||||
const todayFee = Number(k.todayFee) || 0;
|
||||
const todayCustomerCost = Number(k.todayCustomerCost) || 0;
|
||||
const todayRevenue = Number(k.todayRevenue) || 0;
|
||||
const kpi = {
|
||||
yearKg: Number(k.yearKg) || 0,
|
||||
yearFee,
|
||||
yearRevenue,
|
||||
yearProfit: yearRevenue - yearCustomerCost,
|
||||
ourYearKg: Number(k.ourYearKg) || 0,
|
||||
ourYearFee: Number(k.ourYearFee) || 0,
|
||||
customerYearKg: Number(k.customerYearKg) || 0,
|
||||
monthKg: Number(k.monthKg) || 0,
|
||||
monthFee,
|
||||
monthRevenue,
|
||||
monthProfit: monthRevenue - monthCustomerCost,
|
||||
todayKg: Number(k.todayKg) || 0,
|
||||
todayFee,
|
||||
todayRevenue,
|
||||
todayProfit: todayRevenue - todayCustomerCost,
|
||||
lingniuBornKg: Number(k.lingniuBornKg) || 0,
|
||||
lingniuBornFee: Number(k.lingniuBornFee) || 0,
|
||||
};
|
||||
|
||||
// Top5 加氢站(指定年份)
|
||||
const [top5Rows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT b.station_id AS id,
|
||||
COALESCE(MAX(s.station_short_name), MAX(s.station_name), MAX(b.station_name),
|
||||
CASE WHEN b.station_id IS NULL THEN '未关联站点'
|
||||
ELSE CONCAT('未知站点 #', b.station_id) END) AS name,
|
||||
SUM(b.amount_kg) AS kg,
|
||||
SUM(b.cost_total) AS fee
|
||||
FROM ${HYDROGEN_TABLE} b
|
||||
LEFT JOIN hydrogen_station s ON s.id = b.station_id AND s.del_flag = '0'
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE_B}
|
||||
AND b.${HYDROGEN_LOCAL} >= ?
|
||||
AND YEAR(b.${HYDROGEN_LOCAL}) = ?${filter.billSql}
|
||||
GROUP BY b.station_id
|
||||
ORDER BY kg DESC
|
||||
LIMIT 5`,
|
||||
[HYDROGEN_MIN_DATE, year, ...filter.params],
|
||||
);
|
||||
const top5KgSum = kpi.yearKg || 1;
|
||||
const top5 = top5Rows.map((r, i) => ({
|
||||
rank: i + 1,
|
||||
id: Number(r.id) || 0,
|
||||
name: r.name as string,
|
||||
kg: Number(r.kg) || 0,
|
||||
fee: Number(r.fee) || 0,
|
||||
share: (Number(r.kg) || 0) / top5KgSum,
|
||||
}));
|
||||
|
||||
// 加氢站全量汇总(同年所有站,按加氢量降序)
|
||||
const [stationFullRows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT b.station_id AS id,
|
||||
COALESCE(MAX(s.station_short_name), MAX(s.station_name), MAX(b.station_name),
|
||||
CASE WHEN b.station_id IS NULL THEN '未关联站点'
|
||||
ELSE CONCAT('未知站点 #', b.station_id) END) AS name,
|
||||
COALESCE(MAX(d.NAME), '未归属') AS province,
|
||||
SUM(b.amount_kg) AS kg,
|
||||
SUM(b.fee_total) AS revenue
|
||||
FROM ${HYDROGEN_TABLE} b
|
||||
LEFT JOIN hydrogen_station s ON s.id = b.station_id AND s.del_flag = '0'
|
||||
LEFT JOIN common_district d
|
||||
ON CONVERT(d.CODE USING utf8mb4) COLLATE utf8mb4_unicode_ci
|
||||
= CONVERT(s.province USING utf8mb4) COLLATE utf8mb4_unicode_ci
|
||||
AND d.STATUS = 'VALID'
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE_B}
|
||||
AND b.${HYDROGEN_LOCAL} >= ?
|
||||
AND YEAR(b.${HYDROGEN_LOCAL}) = ?${filter.billSql}
|
||||
GROUP BY b.station_id
|
||||
ORDER BY kg DESC`,
|
||||
[HYDROGEN_MIN_DATE, year, ...filter.params],
|
||||
);
|
||||
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,
|
||||
province: r.province as string,
|
||||
kg: Number(r.kg) || 0,
|
||||
revenue: Number(r.revenue) || 0,
|
||||
share: (Number(r.kg) || 0) / stationKgSum,
|
||||
revenueShare: (Number(r.revenue) || 0) / stationRevSum,
|
||||
}));
|
||||
|
||||
// 区域占比(按城市,指定年份)— 取前 8,其余合并为"其他"
|
||||
const [regionRows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT region, SUM(kg) AS kg FROM (
|
||||
SELECT CASE
|
||||
WHEN NULLIF(TRIM(d.NAME), '') IS NOT NULL THEN d.NAME
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%嘉兴%' OR COALESCE(s.station_name, b.station_name, '') LIKE '%平湖%' THEN '嘉兴'
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%广州%' THEN '广州'
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%佛山%' THEN '佛山'
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%成都%' THEN '成都'
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%重庆%' THEN '重庆'
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%乌鲁木齐%' THEN '乌鲁木齐'
|
||||
WHEN COALESCE(s.station_name, b.station_name, '') LIKE '%昆山%' THEN '昆山'
|
||||
ELSE '未归属区域'
|
||||
END AS region,
|
||||
b.amount_kg AS kg
|
||||
FROM ${HYDROGEN_TABLE} b
|
||||
LEFT JOIN hydrogen_station s ON s.id = b.station_id AND s.del_flag = '0'
|
||||
-- 历史站点城市编码与行政区字典的排序规则不同,显式统一避免线上连接报错。
|
||||
LEFT JOIN common_district d
|
||||
ON CONVERT(d.CODE USING utf8mb4) COLLATE utf8mb4_unicode_ci
|
||||
= CONVERT(s.city USING utf8mb4) COLLATE utf8mb4_unicode_ci
|
||||
AND d.STATUS = 'VALID'
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE_B}
|
||||
AND b.${HYDROGEN_LOCAL} >= ?
|
||||
AND YEAR(b.${HYDROGEN_LOCAL}) = ?${filter.billSql}
|
||||
) r
|
||||
GROUP BY region
|
||||
ORDER BY kg DESC`,
|
||||
[HYDROGEN_MIN_DATE, year, ...filter.params],
|
||||
);
|
||||
const totalKg = regionRows.reduce((sum, r) => sum + (Number(r.kg) || 0), 0) || 1;
|
||||
const TOP_REGIONS = 8;
|
||||
const top = regionRows.slice(0, TOP_REGIONS);
|
||||
const restKg = regionRows.slice(TOP_REGIONS).reduce((s, r) => s + (Number(r.kg) || 0), 0);
|
||||
const regions = [
|
||||
...top.map(r => ({
|
||||
region: r.region as string,
|
||||
kg: Number(r.kg) || 0,
|
||||
share: (Number(r.kg) || 0) / totalKg,
|
||||
})),
|
||||
...(restKg > 0 ? [{ region: '其他', kg: restKg, share: restKg / totalKg }] : []),
|
||||
];
|
||||
|
||||
// 月度趋势(指定年份内 12 个月,缺失月补 0)含成本/收入/利润
|
||||
// 利润 = 客户单收入 - 客户单成本(按 customer_price/fee_total 判断客户承担)
|
||||
const [monthRows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') AS m,
|
||||
ROUND(SUM(amount_kg), 2) AS kg,
|
||||
ROUND(SUM(CASE WHEN vehicle_id IS NOT NULL THEN amount_kg ELSE 0 END), 2) AS lingniuKg,
|
||||
ROUND(SUM(CASE WHEN vehicle_id IS NULL THEN amount_kg ELSE 0 END), 2) AS externalKg,
|
||||
ROUND(SUM(cost_total), 2) AS fee,
|
||||
ROUND(SUM(CASE WHEN COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0 THEN cost_total ELSE 0 END), 2) AS customerCost,
|
||||
ROUND(SUM(fee_total), 2) AS revenue
|
||||
FROM ${HYDROGEN_TABLE}
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE}
|
||||
AND ${HYDROGEN_LOCAL} >= ?
|
||||
AND YEAR(${HYDROGEN_LOCAL}) = ?${filter.ledgerSql}
|
||||
GROUP BY m
|
||||
ORDER BY m`,
|
||||
[HYDROGEN_MIN_DATE, year, ...filter.params],
|
||||
);
|
||||
const monthMap = new Map<string, { kg: number; lingniuKg: number; externalKg: number; fee: number; revenue: number; customerCost: number }>();
|
||||
for (const r of monthRows) {
|
||||
monthMap.set(r.m as string, {
|
||||
kg: Number(r.kg) || 0,
|
||||
lingniuKg: Number(r.lingniuKg) || 0,
|
||||
externalKg: Number(r.externalKg) || 0,
|
||||
fee: Number(r.fee) || 0,
|
||||
revenue: Number(r.revenue) || 0,
|
||||
customerCost: Number(r.customerCost) || 0,
|
||||
});
|
||||
}
|
||||
const lastMonth = isCurrentYear ? today.getMonth() + 1 : 12;
|
||||
const monthly: { month: string; kg: number; lingniuKg: number; externalKg: number; fee: number; revenue: number; profit: number }[] = [];
|
||||
for (let mi = 1; mi <= lastMonth; mi++) {
|
||||
const key = `${year}-${String(mi).padStart(2, '0')}`;
|
||||
const v = monthMap.get(key) || { kg: 0, lingniuKg: 0, externalKg: 0, fee: 0, revenue: 0, customerCost: 0 };
|
||||
monthly.push({ month: key, kg: v.kg, lingniuKg: v.lingniuKg, externalKg: v.externalKg, fee: v.fee, revenue: v.revenue, profit: v.revenue - v.customerCost });
|
||||
}
|
||||
|
||||
// 客户账单 Top(指定年份;按加氢量降序,前 30)。
|
||||
// 同一客户可能同时存在客户承担与羚牛承担订单,必须显示为混合,不能按任一条记录覆盖。
|
||||
const [customerRows] = await hydrogenPool.query<RowDataPacket[]>(
|
||||
`SELECT COALESCE(NULLIF(TRIM(customer_name), ''), '未指定客户') AS name,
|
||||
CASE
|
||||
WHEN SUM(CASE WHEN COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0 THEN 1 ELSE 0 END) > 0
|
||||
AND SUM(CASE WHEN COALESCE(customer_price, 0) <= 0 AND COALESCE(fee_total, 0) <= 0 THEN 1 ELSE 0 END) > 0 THEN 'mixed'
|
||||
WHEN SUM(CASE WHEN COALESCE(customer_price, 0) > 0 OR COALESCE(fee_total, 0) > 0 THEN 1 ELSE 0 END) > 0 THEN 'customer'
|
||||
ELSE 'lingniu'
|
||||
END AS payer,
|
||||
SUM(amount_kg) AS kg,
|
||||
SUM(cost_total) AS cost,
|
||||
SUM(fee_total) AS revenue
|
||||
FROM ${HYDROGEN_TABLE}
|
||||
WHERE ${HYDROGEN_FUEL_ONLY_WHERE}
|
||||
AND ${HYDROGEN_LOCAL} >= ?
|
||||
AND YEAR(${HYDROGEN_LOCAL}) = ?${filter.ledgerSql}
|
||||
GROUP BY name
|
||||
ORDER BY kg DESC
|
||||
LIMIT 30`,
|
||||
[HYDROGEN_MIN_DATE, year, ...filter.params],
|
||||
);
|
||||
const customers = customerRows.map(r => ({
|
||||
name: r.name as string,
|
||||
payer: (r.payer as string) === 'lingniu'
|
||||
? 'lingniu' as const
|
||||
: (r.payer as string) === 'mixed'
|
||||
? 'mixed' as const
|
||||
: 'customer' as const,
|
||||
kg: Number(r.kg) || 0,
|
||||
cost: Number(r.cost) || 0,
|
||||
revenue: Number(r.revenue) || 0,
|
||||
}));
|
||||
|
||||
return { kpi, top5, regions, monthly, customers, stations, availableYears, year, latestLedgerTime, filter: {
|
||||
stationId: filter.stationId,
|
||||
vehicleScope: filter.vehicleScope,
|
||||
verifyScope: filter.verifyScope,
|
||||
} };
|
||||
}, { force });
|
||||
return c.json(data);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user