Commit Graph
100 Commits
Author SHA1 Message Date
kkfluous 3f0edfaaf5 fix(mileage): 区分年度累计里程和计入完成
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-03 11:18:48 +08:00
kkfluous feb950dd59 fix(mileage): 已到期年度按期末里程统计
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-03 11:13:40 +08:00
kkfluous 5e1c12eba2 fix(mileage): 减少车型展开时页面抖动
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-02 16:01:47 +08:00
kkfluous ae24bc7647 fix(mileage): 已到期年度显示考核期末日期
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-02 15:50:35 +08:00
kkfluous 0a372e4290 fix(mileage): 消除年度完成日期歧义
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-02 15:48:01 +08:00
kkfluous 1e08d1ea62 fix(mileage): 标注年度已进入车辆数
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-02 15:34:25 +08:00
kkfluous 2d82918d73 fix(mileage): 年度考核区间按批次展示
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-02 15:33:08 +08:00
kkfluous 482243e052 feat(mileage): 支持车型按考核年度查看
ci/woodpecker/push/woodpecker Pipeline was successful
2026-06-02 15:30:13 +08:00
kkfluous f1a69c8271 Correct hydrogen daily vehicle split
ci/woodpecker/push/woodpecker Pipeline was successful
2026-05-28 22:19:08 +08:00
kkfluous 1d2c3a0cd5 Switch hydrogen BI to ledger data source
ci/woodpecker/push/woodpecker Pipeline was successful
2026-05-28 16:47:43 +08:00
kkfluousandClaude Opus 4.7 69168abdf8 chore(mileage): 粤AGP5681 运营区域改为华东区域
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:36:54 +08:00
kkfluousandClaude Opus 4.7 7e6fd491b0 fix(mileage): 当日未对接但有历史 totalKm 的车今日里程显示 0 不是「未对接」
ci/woodpecker/push/woodpecker Pipeline was successful
承接上一笔修复:兜底拿到 totalKm 后,今日里程也应该理解为 0(这车
在系统里有数据,只是今天没增量),而不是再贴「未对接」标签。

涉及:
- MonitoringView 表格 + 卡片:dailyKm 显示与对应颜色 / amber 点
- xlsx-export「今日里程」列

判定改成 (isDataSynced || totalKm != null);
isOnline / 在线-离线 标签不变(基于 dailyKm > 0,与本次语义无关)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:27:23 +08:00
kkfluousandClaude Opus 4.7 1c57eb4a58 fix(mileage): 累计里程展示不再被「未对接」状态盖掉
ci/woodpecker/push/woodpecker Pipeline was successful
后端 cache 已用 fetchLatestPgTotalMileageMap 在当日 source=NONE 时
回填该车 ln_vehicle_day_total_pg 最近一条 total_mileage,但前端
表格 / 卡片 / Excel 都用 isDataSynced && totalKm != null 判定,
未对接车被锁回「未对接」文案。

调整为:
- 今日里程仍按 isDataSynced 显示「未对接」(今天确实没增量)
- 累计里程只看 totalKm 是否为 null,未对接但有历史值的车现在能显示

实测 粤A03423F:source=NONE / dailyKm=0 / totalKm=9205.6
原来「总: 未对接」→ 现在显示 9205.6 km

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:20:33 +08:00
kkfluousandClaude Opus 4.7 331ad1a1da fix(mileage): totalKm 当日为空时回填该车 ln_vehicle_day_total_pg 最近一条
ci/woodpecker/push/woodpecker Pipeline was successful
视图 v_vehicle_daily_stats.total_km 仅当日有 TBOX 记录才有值,
否则原样为 NULL。新增 fetchLatestPgTotalMileageMap 在应用层按车牌
取最近一条非空 total_mileage(queryDateMileage 限定 dates <= 查询日,
保证历史日不取未来值),插入 gpsTotal → latestPgTotal → bizTotal 的
回退链,让 totalKm 显示连续。

MySQL 5.7 无窗口函数,用 INNER JOIN + GROUP BY MAX(dates) 取每车最近一条;
本地 dev 实测 1004 辆车 cache 刷新 ~16s,不再有 SQL 解析错。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:17:16 +08:00
kkfluousandClaude Opus 4.7 1357296f28 fix(mileage): totalKm 当日为空时回填该车 ln_vehicle_day_total_pg 最近一条
ci/woodpecker/push/woodpecker Pipeline was successful
视图 v_vehicle_daily_stats.total_km 仅当日有 TBOX 记录才有值,
否则原样为 NULL。新增 fetchLatestPgTotalMileageMap 在应用层按车牌
取最近一条非空 total_mileage(queryDateMileage 限定 dates <= 查询日,
保证历史日不取未来值),插入 gpsTotal → latestPgTotal → bizTotal 的
回退链,让 totalKm 显示连续。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:07:39 +08:00
kkfluousandClaude Opus 4.7 433a75f9d1 fix(mileage): 7天里程趋势忽略负值脏数据
ci/woodpecker/push/woodpecker Pipeline was successful
v_vehicle_daily_stats.daily_km 偶发负值(粤A00828F 在 5.1 录得 -82061km),
源于里程表回滚 / 换 GPS 设备。SQL 聚合时把负值置 0,避免一辆脏数据车
拖垮整组的当日趋势柱。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:33:25 +08:00
kkfluousandClaude Opus 4.7 0193e78f18 fix(auth): 能源管理仅 BI-LEADER-ENERGY 与「所有权限」可访问
ci/woodpecker/push/woodpecker Pipeline was successful
收紧准入:之前 FULL_ACCESS_ROLES(含 数智中心 / BI-Leader)会自动通过。
现在只接受 BI-LEADER-ENERGY 或「所有权限」两类角色。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:16:42 +08:00
kkfluousandClaude Opus 4.7 2a851fc243 feat(auth): 能源管理放开全量权限角色访问
ci/woodpecker/push/woodpecker Pipeline was successful
BI-LEADER-ENERGY 之外,FULL_ACCESS_ROLES(所有权限/数智中心/BI-Leader)
也可访问能源管理模块。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:03:05 +08:00
kkfluousandClaude Opus 4.7 6142af7617 fix(auth): 能源管理仅 BI-LEADER-ENERGY 可访问,移除全量权限旁路
ci/woodpecker/push/woodpecker Pipeline was successful
与智能调度的口径一致:模块访问需要专属角色,全量权限角色不再自动通过。
本地开发 dev mock 用户已含 BI-LEADER-ENERGY,调试不受影响。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:02:21 +08:00
kkfluousandClaude Opus 4.7 26f7d7ab3f feat(auth): 能源管理模块需要 BI-LEADER-ENERGY 角色
ci/woodpecker/push/woodpecker Pipeline was successful
- 新增 ENERGY_ACCESS_ROLES 与 canAccessEnergy(roles) 守卫(全量权限角色亦可访问)
- 后端 /api/energy/* 加模块级守卫:无角色返回 403
- 前端 App.tsx 按角色动态注入 EnergyModule,无权限时主导航不显示
- dev mock 用户(前端 + 后端)追加 BI-LEADER-ENERGY 便于本地调试

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 17:55:29 +08:00
kkfluousandClaude Opus 4.7 f06b0d21eb perf(energy): SWR 缓存 + 自调度刷新,氢能总览 6s → 13ms
ci/woodpecker/push/woodpecker Pipeline was successful
接口侧:
- cache.ts 改为 stale-while-revalidate:每个 key 自调度,TTL 到期前 5s 后台刷新,用户永远命中热缓存
- 闲置 10 分钟后停止调度,避免空跑
- loader 失败保留旧值 + 10s 后退避重试
- 所有 4 个端点支持 ?force=1 强制绕过缓存

前端 HydrogenOverview:
- 顶部加 RefreshCw 按钮(强刷绕过缓存),带旋转动画
- 显示"更新于 X 秒前"相对时间
- 刷新中:顶部 0.5px 流光进度条,不替换内容、不闪烁
- 60s 静默自动刷新(命中后端热缓存)

实测:cold 6.1s → 命中 13ms(470× 提速)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 17:43:24 +08:00
kkfluousandClaude Opus 4.7 6ad4b5e2a4 feat(energy): 氢能总览补全维度(5KPI+收支+客户/加氢站全量+年份切换)
ci/woodpecker/push/woodpecker Pipeline was successful
按 BI 页面 (https://bi.lnh2e.com/lingniu/decision/link/0iqP) 完整还原:
- 5 张 KPI:累计加氢量 / 累计加氢费 / 时享加氢获利 / 本月加氢 / 本日加氢
- 月度收支对比柱图:成本支出 vs 客户收入双柱
- 加氢站加氢汇总(全量 55 站):加氢量+占比+氢费收入+收入占比,进度条
- 客户账单 Top 30:承担方 / 加氢量 / 成本支出 / 应收
- 年份切换(2025/2026),全量数据按选定年份重算
- 关键修正:用 cost_type 区分客户单/我司单(cost_type=2 客户单,cost_type=3 我司单),获利口径与 BI 对齐

后端 /hydrogen/overview 重写:
- 增加 customers/stations/availableYears/year 字段
- KPI 含 yearProfit/monthProfit/todayProfit
- monthly 含 fee/revenue/profit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:43:05 +08:00
kkfluousandClaude Opus 4.7 ad8ec50038 refactor(energy): 氢能总览参照 BI 重构 + 月度趋势 + 高密度 KPI
ci/woodpecker/push/woodpecker Pipeline was successful
参考 https://bi.lnh2e.com/lingniu/decision/link/0iqP 重新设计:
- 4 张高密度 KPI 卡:累计加氢量 / 累计加氢费 / 本月加氢 / 本日加氢
  每张含主指标 + 2 行明细(我司/客户、加氢费/占比)
- 新增年内月度加氢量柱图(缺失月份补 0)
- 数字格式化:万元/亿元/T 单位自动切换,tabular-nums 对齐
- 后端 /hydrogen/overview 增加 monthly 字段
- 骨架屏同步更新

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:54:35 +08:00
kkfluousandClaude Opus 4.7 dc6f541c8b fix(energy): 桌面 sticky 失效 —— overflow:hidden 限定到移动端横屏
ci/woodpecker/push/woodpecker Pipeline was successful
原因:父容器一直挂着 landscape:overflow-hidden,意图是手机横屏全屏
体验。但 Tailwind 的 landscape: 是纯方向匹配(含桌面横屏显示器),
所以桌面也命中 overflow:hidden,sticky 完全失效,滚动时头部 tab
全部消失,看起来像「半截被遮挡」。

修复:把 landscape: 修饰符改为 max-md:landscape: ,仅在移动端
(< 768px)+ 横屏时生效。桌面恢复正常 overflow:visible,sticky
头部能稳稳停在顶部。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:42:32 +08:00
kkfluousandClaude Opus 4.7 034654265c style(energy): sticky 头部底部缓冲 pb-2 → pb-4
ci/woodpecker/push/woodpecker Pipeline was successful
不再尝试把日期速选并入 sticky 头部(之前的方案撤回)。
仅增加一点底部 padding 当作缓冲,保持简洁。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:37:49 +08:00
kkfluous 5958bb581e Revert "fix(energy): 日期速选并入 sticky 头部,避免滚动时被遮挡"
This reverts commit 4153f329b8.
2026-04-30 15:37:17 +08:00
kkfluousandClaude Opus 4.7 4153f329b8 fix(energy): 日期速选并入 sticky 头部,避免滚动时被遮挡
ci/woodpecker/push/woodpecker Pipeline was successful
之前「本周/本月/近 15 天」放在 HydrogenDaily/ElectricDaily 的
内容区第一行,sticky 头部独占顶部。滚动后这一行会从 sticky
头部下方钻过去,露出半截,看起来像被切。

修复:把日期速选行也放进 sticky 头部白卡里:
  - EnergyModule 持有 hydroPick / electricPick state
  - 头部第三行(border-t 分割)渲染速选按钮,仅 daily 模式显示
  - HydrogenView/ElectricView/ElectricDaily/HydrogenDaily 改为
    通过 pick prop 接收,组件内不再 useState

现在头部「Top Tab + Sub Tab + 日期速选」是同一张白卡,
滚动时整体一起 sticky,不再有半截遮挡。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:34:41 +08:00
kkfluousandClaude Opus 4.7 ee981639eb fix(energy): sticky 头部不再半透明,避免快捷选按钮"半截露脸"+ 文案改「每日加氢量」
ci/woodpecker/push/woodpecker Pipeline was successful
问题 1:sticky 头部 bg-[#F8F9FB]/85 backdrop-blur-md 半透明,
下方「本周/本月/近15天」快捷选按钮在滚动时透过 sticky 条隐约可见,
看起来像被遮罩切掉一半,效果差。

修复:把头部背景改为不透明页面色 bg-[#F8F9FB],去掉 backdrop-blur,
加上一条很淡的下投影 shadow(仅在头部下边缘)作为分割线,
既不再透出后面的内容,也保留了一点层次感。

问题 2:氢能 daily 图表标题「时段每日加氢量」→「每日加氢量」更简洁。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:28:23 +08:00
kkfluousandClaude Opus 4.7 fe70ec389b refactor(energy): 电能整体页面对齐氢能:每日 / 总览 子 tab 切换
ci/woodpecker/push/woodpecker Pipeline was successful
- ElectricView 改为受控组件接收 sub prop(与 HydrogenView 对齐)
- EnergyModule sticky 头部统一显示 sub-tabs:氢能、电能都给 每日 / 总览
  ETC 仍不显示子 tab(建设中页)
- 共享 sub state 抽 helper:activeTab 切换时自动用对应的 sub
- 龙王路停车场充电站信息条移入 ElectricOverview 顶部(同氢能"数据自...")

进入电能默认显示「每日」(与氢能一致),切换「总览」看 KPI + 柱图

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:22:02 +08:00
kkfluousandClaude Opus 4.7 c3b463d9ca feat(energy): 外部车辆 tab 暂时显示「数据未就绪」占位
ci/woodpecker/push/woodpecker Pipeline was successful
新系统的外部车辆数据尚未接通,按零填充会显示一长串 0,体验差。
当 customer=external 且全期总量为 0 时:
  - 隐藏柱图与明细表
  - 显示「外部车辆 · 数据未就绪」友好占位
    (Plug 图标 + 蓝色脉冲点 + 文案「新系统的外部车辆 X 数据还在准备中」)

氢能/电能 daily 都加。一旦后端接通真实外部数据,totalKg/totalKwh > 0,
占位自动消失,恢复正常表格视图。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:18:14 +08:00
kkfluousandClaude Opus 4.7 f2acb73033 style(energy): 加氢站单价固定显示在站名下一行
ci/woodpecker/push/woodpecker Pipeline was successful
之前 mobile 用 inline-flex flex-wrap 让单价跟在站名后,空间不够才换行。
统一改为站名独占第一行(whitespace-nowrap),单价 amber 徽章固定在
第二行(mt-1),结构更稳定一眼能看到。

行整体改 items-start 顶部对齐。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:15:07 +08:00
kkfluousandClaude Opus 4.7 015ff9bc7e fix(energy): 站名长名称不再换行 + 「羚牛/外部」改为「羚牛车辆/外部车辆」
ci/woodpecker/push/woodpecker Pipeline was successful
问题 1:广州新锋交通联新加氢站 等长名称在右侧空间足够时仍折行
- mobile 列宽过宽:120px (kg) + 88px (chainPct) 占去 232px
- 子行 pl-9 缩进 (36px) 也吃掉空间
修复:
  - mobile 列宽收紧 84/80,gap 缩到 2,子行缩进 pl-6
  - 名字 + 单价徽章改为 inline-flex(whitespace-nowrap)
    名字一行写完,单价徽章紧跟其后;空间不够时单价徽章自动换到下一行
  - desktop 列宽 140/120/104(之前 140/140/120)

问题 2:氢/电 daily 客户类型 segmented control
  「羚牛」→「羚牛车辆」,「外部」→「外部车辆」更明确

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:11:19 +08:00
kkfluousandClaude Opus 4.7 d24ce55a59 fix(energy): 顶部双 sticky 间隙泄露 + 加氢站名单价完整显示
ci/woodpecker/push/woodpecker Pipeline was successful
问题 1:原本「氢能/电能/ETC」与「每日/总览」是两个独立的 sticky
元素,分别 top:0 / top:[58px],中间 gap-3 +位置不精确导致滚动时
图表内容从 14px 缝隙里穿过。

修复:
- 把 HydrogenView 内部的 sub-tab 状态提到 EnergyModule
- top tab + 子 tab 合并到「同一张白色 rounded-2xl 卡片」里,无内部间隙
- 外层 sticky 容器 frosted glass:bg-[#F8F9FB]/85 + backdrop-blur-md
  -mx -mt 扩到页面边,消除左右上的微缝
- HydrogenView 改为受控组件(接收 sub prop)

问题 2:站点行 mobile 上 name + ' · 价格' 共用一格还 truncate,
导致长名称(广州新锋交通联新…/上海浦江加氢站…)截断、单价不可见。

修复:
- 行改为「站名换行 + 下方单价 chip」纵向排列
- 单价用 amber 小徽章「单价 X 元/Kg」,不再 inline 跟着名字
- name 用 break-words 允许折行,items-start 顶部对齐
- 单价为 0(免费/赠送)时不显示徽章,desktop 列里显示「—」

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:08:22 +08:00
kkfluousandClaude Opus 4.7 e0183986ee feat(energy): 氢/电统一时间速选为「本周/本月/近15天」+ 缺失日补 0
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端 Range 类型精简到 thisWeek / thisMonth / last15
  rangeClause 同步精简;删除 today / thisQuarter / last7 / last30 分支
- 新增 enumerateDates(range):列出 range 内全部日期,用于补零
- /hydrogen/daily:用 enumerateDates 补齐缺失日期 totalKg=0、stations=[]
  补零后基于完整日期序列重算环比(0→上一日有值时显示 -100%)
- /electric/monthly:增加 range 参数,扁平日聚合 + 月份分组
  缺失日期同样补零;环比基于补零序列重算
- 默认 range 改 last15

前端
- HydrogenDaily QUICK_PICK_OPTIONS 收紧到 3 项,默认 last15
- ElectricDaily 之前没有日期速选,现按氢能样式加上同样 3 项

类型 DateQuickPick 改 'thisWeek' | 'thisMonth' | 'last15'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:56:13 +08:00
kkfluousandClaude Opus 4.7 234b44ea03 feat(energy): 能源管理新增 ETC tab,建设中占位页
ci/woodpecker/push/woodpecker Pipeline was successful
- EnergyModule TopTab 加 'etc',用 lucide Receipt 图标
- TABS 数据驱动渲染,加新 tab 不用复制粘贴
- 新增 ETCView:可爱的「建设中」占位
    - 大号 Construction 图标 + 顶上的 Hammer 摆动动画
    - 文案「ETC 模块建设中」+ 副标说明
    - 4 步进度(需求评审/数据对接/页面开发/正式上线)
      已完成项绿点、进行中项黄点 + 脉冲,未来项灰点
    - 底部专属 RotatingFooterHint,5 条 ETC 上下文文案

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:42:00 +08:00
kkfluousandClaude Opus 4.7 8d861538af fix(feedback): 第一步选完类型后点「下一步」无反应
ci/woodpecker/push/woodpecker Pipeline was successful
next() 漏写了 step===1 且 type 已选的分支,按钮只在「未选」时
拦截,「已选」时进入空函数体直接返回,没有 setStep(2)。

补上 step===1 已选时 setStep(2),行为:
- 直接点卡片:保持原有自动下一步(onClick 里 setStep)
- 选中后用底部「下一步」按钮:现在也能正常推进

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:37:46 +08:00
kkfluousandClaude Opus 4.7 bdefb878a5 style(feedback): 选类型副标题换成轮转动态提示
ci/woodpecker/push/woodpecker Pipeline was successful
step 1 标题下的「选一个最贴近的类型」改成 RotatingFooterHint,
6 条文案 4 秒一轮(含「数字背后还有故事,等下一次上线揭晓」等)。

RotatingFooterHint 兼容自定义对齐:传 className 就完全覆盖
默认 mt-1 + justify-center;不传则保持底部居中(其他模块的用法)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:34:21 +08:00
kkfluousandClaude Opus 4.7 2aeff0c2f4 fix(feedback): 隐藏页加返回按钮 + 入库时间用东八区
ci/woodpecker/push/woodpecker Pipeline was successful
- FeedbackAdminPage / EleImportPage 头部加 ← 返回按钮:
  优先 history.back(来自 SPA 内跳转),否则 hash=#mileage 兜底回主页
- 反馈入库(created_at / reply_at)改为 DATE_ADD(UTC_TIMESTAMP, INTERVAL 8 HOUR)
  不再依赖 MySQL/容器的本地时区设置,固定 CST

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:27:41 +08:00
kkfluousandClaude Opus 4.7 9bbd11cc86 fix(feedback): 反馈管理跳转无效 + 本地调试角色补齐
ci/woodpecker/push/woodpecker Pipeline is pending
问题 1:菜单点「反馈管理」跳到 #/admin/feedback,URL 变了但
AuthGate 只在初始 render 读 location,hashchange 不会重渲染。
修复:AuthGate 用 useState/useEffect 监听 hashchange/popstate,
URL 变化即时切换页面。

问题 2:本地 DEV_BYPASS_AUTH 模式下 roles 没有 BI-ADMIN-FEEDBACK,
菜单看不到入口。前后端 dev bypass 的 roles 都补上:
  ['所有权限', 'BI-SCHEDULE-OPT', 'BI-ADMIN-FEEDBACK']

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:25:30 +08:00
kkfluousandClaude Opus 4.7 1a3d48b2d1 feat(feedback): 反馈 FAB 菜单加「反馈管理」入口,BI-ADMIN-FEEDBACK 角色可见
ci/woodpecker/push/woodpecker Pipeline was successful
- shared/auth/roles 新增 FEEDBACK_ADMIN_ROLES = ['BI-ADMIN-FEEDBACK']
  + canManageFeedback() helper(含 FULL_ACCESS_ROLES 兜底)
- FeedbackFab 菜单:在「我的反馈」下方加分割线 + 紫色 ⚙ 图标的「反馈管理」
  仅 canManageFeedback 为 true 时渲染,跳到 #/admin/feedback
- 后端守卫:GET /api/feedback/list 与 PATCH /api/feedback/:id 加角色判断
  无权限返回 403。/mine /submit /upload 仍对全部登录用户开放。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:20:45 +08:00
kkfluousandClaude Opus 4.7 c5541fbbf5 style(feedback): 选类型步骤改用 Lucide 图标 + 克制白卡
ci/woodpecker/push/woodpecker Pipeline was successful
之前用 emoji + 整卡渐变背景,颜色饱和度高、和系统其他模块的视觉
语言不一致,看起来有点像玩具。

新视觉:
  - 替换 emoji 为 Lucide 图标:Lightbulb / Bug / Palette / NotebookPen
    与项目其他模块(Truck/Route/Zap)保持一致
  - 卡片白底 + 1px 浅边框,hover 阴影;选中态用 ring 替代填色
  - 图标放在彩色圆角小容器里(amber/rose/violet/blue),强度更克制
  - 标题升级到 13px,副标题统一 11px slate-400 medium
  - 入场级联动画 + 微交互(hover y=-1,→ 按钮位移)

文案微调:「想反馈点什么?」+ 副标题「选一个最贴近的类型」

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:16:26 +08:00
kkfluousandClaude Opus 4.7 90b34b681e feat(feedback): 移除联系方式步骤,登录态用户身份已知
ci/woodpecker/push/woodpecker Pipeline was successful
之前第三步要求填写微信/钉钉等联系方式,但用户已登录,后端已经
记录 user_id / user_name(与水印取的同一份),可以直接通过内部
渠道触达,无需再问。

流程从 4 步收紧为 3 步:
  1) 选类型
  2) 写内容 + 截图 + 板块
  3) 成功页

提交概览页一并删除(信息不变就直接提交,少一次点击)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:14:54 +08:00
kkfluousandClaude Opus 4.7 90b1266fe5 fix(feedback): 反馈弹窗禁止背景点击关闭,只能用 X 按钮
ci/woodpecker/push/woodpecker Pipeline was successful
之前点击外部遮罩会关闭弹窗,用户填到一半误触会丢失全部已输入内容。
去掉 backdrop 的 onClick={close},只保留右上角 X 按钮关闭路径。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:08:22 +08:00
kkfluousandClaude Opus 4.7 20ebb16e08 feat(feedback): 截图上传 + 我的反馈历史 + 后台管理页
ci/woodpecker/push/woodpecker Pipeline was successful
后端
- 接入阿里云 OSS(ali-oss SDK),bucket=lnh2etest,目录 /dos/feedback/YYYY-MM-DD/
- POST /api/feedback/upload:单图 multipart 上传,限制 5MB,仅 png/jpeg/webp/gif
- bi_user_feedback 增加 screenshots(JSON)、reply_content/reply_user/reply_at、user_id 索引
  老表通过 try-catch 自动 ALTER 兼容
- POST /api/feedback/submit:增加 screenshots[] 字段
- GET /api/feedback/mine:当前用户自己的反馈历史
- PATCH /api/feedback/:id:更新状态 + 回复
- GET /api/feedback/list:增加 status 过滤

前端
- FeedbackFab 改为悬浮按钮 + 弹出菜单:「提个建议」/「我的反馈」
- 弹窗 Step 2 增加截图区:点击选择 / 多张 / 直接 Ctrl+V 粘贴
  缩略图预览 + 单张移除,最多 6 张,上传中转圈
- FeedbackHistoryDrawer 新组件:底部抽屉展示自己的反馈
  含状态徽章(待处理/处理中/已完成/已忽略)、截图缩略图、产品同学回复区
- 新增隐藏后台管理页 /admin/feedback(或 #/admin/feedback)
  状态分类计数 + 列表 + 详情弹窗(改状态 + 写回复,状态选项含徽章色)
  待处理项有快捷按钮(标记处理中 / 忽略)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:06:21 +08:00
kkfluousandClaude Opus 4.7 e8f1604c11 feat: 全局反馈系统 + 各模块底部统一动态提示
ci/woodpecker/push/woodpecker Pipeline was successful
- 新增 components/RotatingFooterHint:统一文案+蓝色脉冲,4 秒轮换
- 新增 components/FeedbackFab:右下角悬浮按钮(渐变 + 心形信封 + 黄色脉冲点),
  点击打开 4 步引导式弹窗
    Step 1 选类型(💡新维度 / 🐛bug / 🎨界面 / 📝其他)
    Step 2 描述需求 + 选当前板块(chip)
    Step 3 留联系方式(可选)+ 提交概览
    Step 4 ❤️ 成功页(弹簧 √ 动画)
  顶部 spring 进度条,底部上一步/下一步,下拉手柄,背景点击或 X 关闭
- 后端 routes/feedback:bi_user_feedback 表(自动建表,含 status 字段)
  POST /api/feedback/submit + GET /api/feedback/list
- Shell 全局挂载 FeedbackFab,自动从 hash 检测当前模块
- 各模块底部追加 RotatingFooterHint:
  AssetsModule / MileageModule / SchedulingModule / EleImportPage
  HydrogenOverview / HydrogenDaily / ElectricOverview / ElectricDaily
  (HydrogenOverview 旧的内嵌实现已替换为共享组件)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 13:50:39 +08:00
kkfluousandClaude Opus 4.7 08f21b7e24 chore(energy): 电能总览删除「今日」KPI 卡
ci/woodpecker/push/woodpecker Pipeline was successful
KPI 头从 3 列改为 2 列,仅保留累计与本月。
今日卡常被遮罩(手工导入的数据滞后),且与本月信息重复,先去掉。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:57:24 +08:00
kkfluousandClaude Opus 4.7 57207debfb fix(energy): hydrogen_time 已是 CST 字面值,去掉多余的 +8 HOUR 转换
ci/woodpecker/push/woodpecker Pipeline was successful
之前认为 hydrogen_time / charging_start_time 字段存的是 UTC,
所有氢能查询都加了 DATE_ADD(..., INTERVAL 8 HOUR) 转 CST。
但实际上字段存的是 CST 字面值,转换反而把日期边界提前了 8 小时,
导致诸如「04-28 嘉兴嘉燃经开站」的统计少算了部分晚间订单。

实测:
  - 之前:04-28 嘉燃经开 = 144.36 Kg(CST 转换错位)
  - 现在:04-28 嘉燃经开 = 153.81 Kg(与业务方直接 DATE(hydrogen_time) 口径一致)

电能 charging_start_time 同样去掉转换。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:54:22 +08:00
kkfluousandClaude Opus 4.7 d3fa2fd4d6 revert(energy): 取消 GF_HECRI_BILL 过滤,全部数据展示
ci/woodpecker/push/woodpecker Pipeline was successful
按用户要求恢复全量统计:移除 4 处 GF 过滤子句和相关常量。
现在 GF_HECRI_BILL 历史订单会与 JQ 新订单一同计入。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:28:52 +08:00
kkfluousandClaude Opus 4.7 8b4fb6563f refactor(energy): 简化为始终过滤 GF_HECRI_BILL,移除条件判断
ci/woodpecker/push/woodpecker Pipeline was successful
去掉 shouldFilterGfBills 探测、5 分钟缓存、JQ 存在性判定。
4 处氢能查询无条件追加 GF_HECRI_BILL 过滤,逻辑更直接。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:26:52 +08:00
kkfluousandClaude Opus 4.7 e187c0d02e feat(energy): 嘉燃经开站存在 JQ 单时全局过滤 GF_HECRI_BILL 历史订单
ci/woodpecker/push/woodpecker Pipeline was successful
业务背景:旧系统加氢账单使用 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) <noreply@anthropic.com>
2026-04-29 20:23:03 +08:00
kkfluousandClaude Opus 4.7 3d4d862d73 feat(energy): 氢能总览删除 4 张 KPI 卡,底部加动态幽默提示
ci/woodpecker/push/woodpecker Pipeline was successful
- 删除:年加氢量 / 年加氢费 / 累计羚牛承担 / 本月-今日 四张顶部 KPI 卡
  (及对应的 Skeleton 占位、未用的 Fuel/Wallet/Coins/CalendarClock import 与 fmt 工具函数)
- 新增 RotatingFooterHint:底部居中蓝色脉冲点 + 6 条幽默文案 4s 轮换淡入
  例如「更多统计维度接入中,欢迎您的建议 ~」「数据科学家正在深夜挖掘新维度…」

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:10:27 +08:00
kkfluousandClaude Opus 4.7 23a7722583 chore(energy): 氢能总览删除「我方/客户产生」副统计
ci/woodpecker/push/woodpecker Pipeline was successful
年加氢量、年加氢费 卡片移除以下副信息:
  - 我方 XXX
  - 客户产生 XXX

只保留主标题与大数字。其他卡片(累计羚牛承担、本月/今日)保持不变。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:04:56 +08:00
kkfluousandClaude Opus 4.7 213037c2ac fix(energy): 氢能 Top5 排名圆点左侧被 SVG 视窗切掉
ci/woodpecker/push/woodpecker Pipeline was successful
之前 cx=-178 + YAxis.width=190 + margin.left=0:
  Y 轴线 pivot 在 chart x=190;圆点 abs x=12,半径 9 → 左缘 abs x=3
  紧贴 SVG viewBox 左边界,渲染时被切掉一半。

新值:margin.left=12,YAxis.width=188,circle cx=-172,text x=-154
  pivot=200,圆点 abs x=28,左缘=19,离左边界 19px 缓冲,圆点完整可见。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:03:24 +08:00
kkfluousandClaude Opus 4.7 3efa701395 feat(energy): 氢能总览加载骨架屏,缓解 1-2s 初始等待
ci/woodpecker/push/woodpecker Pipeline was successful
之前数据回来前只显示一行「加载中…」纯文本,1-2 秒等待体感差。
新增 HydrogenOverviewSkeleton:
  - 4 张 KPI 卡占位(含标题/数值/副信息行)
  - Top5 横向条形图占位(5 行 圆点 + 站名 + 渐变条 + 数值)
  - 区域占比环 + 图例占位
  - 底部蓝色脉冲点 +「正在加载氢能总览…」
全部用 animate-pulse,结构与真实页面保持一致,避免回填时跳动。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 20:00:48 +08:00
kkfluousandClaude Opus 4.7 e775acb8fe fix(energy): 氢能 Top5 排名圆点与站名重叠修复
ci/woodpecker/push/woodpecker Pipeline was successful
YAxis 宽度 170→190,圆点 cx 从 -158 → -178,文字 x 从 -144 → -160。
间距从 14px 拉到 18px+,避免在窄屏上圆点压住站名首字。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:59:37 +08:00
kkfluousandClaude Opus 4.7 c788dd4577 fix(energy): 单价直接取 MAX(cost_price),不重算不返 null
ci/woodpecker/push/woodpecker Pipeline was successful
之前用 MIN=MAX...ELSE NULL 判定,再 NULLIF 排零,遇到「1 笔 0 元免费单 + 多笔 35 元正价单」
仍可能误判混合,最终页面显示「—」(如佛山豪汇石油加氢站)。

按业务约定:单价就是订单上记录的成本价,不做"统一性"判定,也不返 null。
改用 MAX(b.cost_price):
  - 自然忽略 0 元免费/赠送单(被正价 max 掉)
  - 同价组等于原价
  - 极少数真正混合价组也展示该日付出过的最高单价(仍是订单上的真实数字)

回退类型:HydrogenStationRow.pricePerKg 重新固定为 number。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:56:26 +08:00
kkfluousandClaude Opus 4.7 3851335843 fix(energy): 氢能单价不再加权,混合价组显示「—」并修复 hydrogen_time 歧义
ci/woodpecker/push/woodpecker Pipeline was successful
- pricePerKg 改为 CASE WHEN MIN=MAX THEN MIN ELSE NULL,
  同价组返回原价(无小数误读),混合价组返回 null
- 类型 HydrogenStationRow.pricePerKg: number | null
- 前端 mobile/desktop 两处展示在 null 时显示「—」
- 修复 ER_NON_UNIQ_ERROR:tab_import_hydrogen_order 也有 hydrogen_time 字段,
  把 SELECT/ORDER BY 中 ${HYDROGEN_LOCAL} 替换为显式 b.hydrogen_time 限定
- 实测:712 个站点-日组中 682 个同价直接显示原价,30 个混合显示「—」

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:51:41 +08:00
kkfluousandClaude Opus 4.7 d0a644cf18 fix(energy): 氢能站点名补全 tab_import_hydrogen_order,单价改为按量加权
ci/woodpecker/push/woodpecker Pipeline was successful
站点名 fallback 链新增第三档:
  内部站表 → 外部站表 → tab_import_hydrogen_order(by bill_code) → 「未关联/未知 #ID」
经此关联,原来 140 条「未知站点」补全为 9 个真实站名(洛阳新红山、佛山新城等)

单价之前用 AVG(cost_price) 简单平均,混合价组会算出 34.0334... 这种
意外小数(看起来像被「重新计算」)。改为按量加权效率价:
  ROUND(SUM(cost_expense) / NULLIF(SUM(hydrogen_quantity), 0), 2)
单一价格组自动等于原价,混合价组得到真实付款单价。

Top5 与每日聚合两处 SQL 同步修改。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:46:56 +08:00
kkfluousandClaude Opus 4.7 0d30ee2df5 fix(energy): 氢能站点 fallback 区分「未关联」与「未知 #ID」
ci/woodpecker/push/woodpecker Pipeline was successful
之前两张 site 表都查不到的账单,UI 一律显示「未知站点」。
其中 137 条 station_id 为 NULL(账单未填站点),3 条 station_id
有值但站点表没收录(站点删除/字典漂移)。账单上的 cost_price 是真实的,
所以会出现「未知站点 25 元/Kg」这种可追溯但难定位的情况。

现在 SQL fallback 改为:
  - station_id IS NULL → 「未关联站点」
  - station_id 不为空但 join 不上 → 「未知站点 #ID」
方便定位具体是哪条字典记录缺失。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:41:57 +08:00
kkfluousandClaude Opus 4.7 9a20a7cb79 refactor(energy): 电能仅展示充电量,电/氢 daily 表格统一列宽
ci/woodpecker/push/woodpecker Pipeline was successful
- 电能去掉「充电费用(元)」列,只保留 月份/日期 | 充电量(度) | 环比
  原列宽 auto+auto+auto 在窄屏会挤压重叠
- 改用固定 minmax(0,1fr)/120/88(移动)和 1fr/160/120(桌面)
- 氢能 daily 同步统一列宽:1fr/120/88(移动)和 1fr/140/140/120(桌面)
- 单价表头改为「单价 (元/Kg)」全展示,移动端站点行价格作为副信息缩小展示

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:37:26 +08:00
kkfluousandClaude Opus 4.7 d1d79f1c7c feat(energy): 电能统计切到 bi_ele_charge_record,外部数据接通
ci/woodpecker/push/woodpecker Pipeline was successful
- /api/energy/electric/overview & /electric/monthly 不再读 tab_energy_electricity_bill
- 改读 bi_ele_charge_record:kwh/fee/start_time
- 外部/我司用 vehicle_kind 区分(external/internal)
- 电能默认 customer 由 'external' 改 'lingniu',与导入页约定一致
- ElectricDaily 移除「数据对接中…」友好空状态(外部已有数据)

ele 导入页同步收紧:
- 命中系统车辆=internal,未命中(含车牌为空)一律 external
- 移除 unknown 分类、KPI 卡、批次列、过滤按钮、UploadResult 字段
- 历史 unknown 行已 UPDATE 为 external

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:11:52 +08:00
kkfluousandClaude Opus 4.7 5217e19b25 fix(ele): /ele/import 同时支持 hash 路由
ci/woodpecker/push/woodpecker Pipeline was successful
主应用模块切换走 hash(#mileage 等),用户也会用 #/ele/import 访问。
之前只判 pathname='/ele/import',hash 形式直接落到 Shell 默认模块。
现在 path / hash 两种形式(/ele/import、#/ele/import、#ele/import)都识别。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:04:48 +08:00
kkfluousandClaude Opus 4.7 57fdd346cf feat(ele): 充电记录后台导入页面 /ele/import(隐藏入口)
ci/woodpecker/push/woodpecker Pipeline was successful
后端
- 新建 bi_ele_charge_record 表(首次访问自动 CREATE TABLE IF NOT EXISTS)
  字段含订单编号(UNIQUE)、电站、时段、电量/费用、车牌/判定车牌、内外部分类、原始 JSON、批次号
- POST /api/ele/import:multipart 上传 xlsx,识别表头自动定位,
  文件内 + 数据库双重去重(INSERT IGNORE on UNIQUE order_no)
  上传时按 plate/judged_plate 在 tab_truck 中匹配,命中=internal、未命中但有牌=external、无牌=unknown
- GET /api/ele/list 分页 + kind/batch/search 过滤
- GET /api/ele/batches 批次汇总(数量、内/外/未知拆分、电量/费用合计)
- GET /api/ele/aggregate 全量与近 30 日按日 × 分类聚合

前端
- /ele/import 路径直接渲染 EleImportPage,主导航不显示,需手动输入 URL
- 拖拽/点击上传,结果卡展示解析/新增/重复/分类
- KPI 8 卡:总数、内/外/未知记录、累计电量与费用、内/外电量
- 批次列表(点击筛选)+ 最新记录表(kind 切换 + 关键字搜索)
- 上传后自动 reload 全部数据

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:02:38 +08:00
kkfluousandClaude Opus 4.7 d8189329ac chore(energy): 羚牛 tab 置前并默认选中(氢能/电能一致)
ci/woodpecker/push/woodpecker Pipeline was successful
- HydrogenDaily/ElectricDaily 默认 customer 改为 'lingniu'
- segmented control 顺序改为 ['lingniu', 'external']
- 进入页面立刻看到我司数据,避免空状态首屏

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:53:22 +08:00
kkfluousandClaude Opus 4.7 e2d04db06d chore(energy): 重新放开电能 tab
ci/woodpecker/push/woodpecker Pipeline was successful
- 取消之前的临时屏蔽,恢复氢能/电能两个顶部 tab
- 电能内部已和氢能同时支持 truck_id 区分外部/我司,外部空数据会展示「数据对接中…」

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:52:11 +08:00
kkfluousandClaude Opus 4.7 5493e27e49 feat(energy): 用 truck_id 区分外部/我司,外部数据空时给友好提示
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端 customerClause 改为基于 truck_id:外部=IS NULL,我司=IS NOT NULL
- KPI 内联条件(ourYearKg/Fee、customerYearKg、lingniuBornKg/Fee)同步切换为 truck_id
- 调用方 /hydrogen/daily 与 /electric/monthly 改传 b.truck_id / truck_id
- 当前外部账单 truck_id 尚未对接,HydrogenDaily/ElectricDaily 在 customer=external 且无数据时
  改展示「数据对接中…」友好状态(插头图标 + 蓝色脉冲),替代「暂无数据」

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:52:03 +08:00
kkfluousandClaude Opus 4.7 355c45a2e4 fix(assets): 区域车型分解新增「待交车」字段,并合入「其他」车型
ci/woodpecker/push/woodpecker Pipeline was successful
- typeBreakdown 之前只产 4.5T/18T/49T 且仅含 inventory 字段,
  导致区域级 待交车 与车型级 待:N 不一致、操作中合计 != 区域合计
- 后端 getTypeBreakdown 计算 pending(status==='Pending'),
  并把不属 4.5T/18T/49T 的车辆聚合为「其他」类型
- 前端区域 mobile/desktop 视图把「待:」从 inventory 改读 pending
- 点击穿透的 category 也由 'Inventory' 改 'Pending'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:21:37 +08:00
kkfluousandClaude Opus 4.7 66779a98e3 fix(mileage): 弹窗关闭改为向下滑出 + 淡出
ci/woodpecker/push/woodpecker Pipeline was successful
- exit 从 y:60 改为 y:'100%',整张表自顶向下滑出屏幕
- y/opacity 拆分 transition:y 走 spring,opacity 走 0.18s 淡出
- initial 也改为 y:'100%' 让出现/消失对称

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:16:57 +08:00
kkfluousandClaude Opus 4.7 97ac92a0da feat(mileage): 车辆明细弹窗新增时间范围切换、骨架加载与下滑关闭
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端 vehicle/:plate/recent 支持 start/end 任意区间,最长 366 天
- 前端弹窗加 segmented control: 近 15 天 / 本月 / 本季度,切换重新加载
- 加载时柱状图与每日明细均显示骨架,区间合计/日均/有数据天 KPI 同步骨架
- 数据回来后柱条与每行进度条带渐入动画
- 顶部加 iOS 风格 drag handle(小白条),按住下滑超过 100px 或大速度触发关闭
- 保留点击背景与 X 按钮两种关闭方式

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:14:47 +08:00
kkfluousandClaude Opus 4.7 7ca8ef24dc feat(mileage): 点击车辆卡片展示近 15 日行驶里程明细
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端新增 GET /api/mileage/vehicle/:plate/recent,返回近 N 天 + 今日的每日里程
- 缺失日补全为 dailyKm=0 + isDataSynced=false
- 前端新增 VehicleDetailModal:头部信息、合计/日均/有数据天 KPI、近 N 日柱状图、每日明细列表
- 移动端从底部弹起;缺失日柱条置灰,明细行标注「未对接」
- 卡片点击改为打开弹窗(不再复制车牌)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:16:54 +08:00
kkfluousandClaude Opus 4.7 f9c6155ea7 fix(mileage): 修复车牌多选弹窗手机端右侧溢出与粘贴行为
ci/woodpecker/push/woodpecker Pipeline was successful
- 弹窗改为 right-0 锚右,宽度 min(280px, vw-24px),避免在窄列触发器下右侧溢出
- 移除 onPaste 自动 apply(避免与已输入文本拼接出非预期 token),改为粘贴入框 + 点击「添加」或 Cmd/Ctrl+Enter 确认
- placeholder 文案补充提示

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:12:19 +08:00
kkfluousandClaude Opus 4.7 cab86556f3 feat(mileage): 区域与车牌列表级联,并自动剔除越界车牌
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端: 选中 region 时基于该区域车辆重算 filters,车牌列表只展示该区域
- 前端: filterOptions.plates 收窄后自动从已选车牌中剔除不属于新区域的项

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:31:12 +08:00
kkfluousandClaude Opus 4.7 e0c609168e chore(energy): 恢复能源管理入口,仅隐藏电能 tab
ci/woodpecker/push/woodpecker Pipeline was successful
- 重新启用 EnergyModule 作为侧边栏入口
- EnergyModule 内部隐藏「电能」tab,只保留「氢能」(保留 Electric* 代码)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:25:37 +08:00
kkfluousandClaude Opus 4.7 ebd82893bc chore(energy): 暂时隐藏能源管理入口(发版前临时屏蔽,保留模块代码)
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:24:19 +08:00
kkfluousandClaude Opus 4.7 3809e785c1 feat(mileage): 外部三选筛选 + 车牌多选粘贴 + 运营区域 + xlsx 下载
ci/woodpecker/push/woodpecker Pipeline was successful
- 外部行改为 批次型号 / 运营区域 / 车牌多选;按部门、按客户移入详情面板
- 车牌多选支持从 Excel 粘贴(换行/逗号/空格分隔),未匹配项显示警告
- 新增运营区域筛选:基于 136 批次区域映射(华东/华南/西南/西北)
- 新增 xlsx 数据下载,导出当前筛选结果(带表头样式与列宽)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:19:00 +08:00
kkfluousandClaude Opus 4.7 d1acdafa7e refactor(energy): merge electric overview into a single page
Drop the 每日/总览 sub-tabs on 电能 — only 龙王路充电站 in scope, so
the overview is light (3 KPI cards + 1 bar chart) and combining
saves a click for daily ops. ElectricView now renders ElectricOverview
+ ElectricDaily back-to-back below the hint card.

氢能 keeps its sub-tabs (richer overview with Top5 + region chart).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:04:26 +08:00
kkfluousandClaude Opus 4.7 c3b43837fb fix(energy): update hint text to match real 1m cache TTL
The "每 5 分钟更新" copy was inherited from the BI dashboard mock and
no longer matches reality — server cache is 60s TTL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:02:41 +08:00
kkfluousandClaude Opus 4.7 c02c1aa62c perf(energy): add 60s TTL cache for all 4 endpoints
Hydrogen overview was 1.8-2.0s (3 full-table aggregations on 66K rows).
With cache: cold 1 user/min eats the full query, all subsequent within
60s window return in ~10ms.

Implementation:
- New cache.ts with cached(key, loader) helper
- Per-key in-flight de-duplication: concurrent requests share one loader
- Each handler wrapped, cache key includes query params
  (e.g. "hydrogen/daily?range=last30&customer=external")
- TTL 60s as requested

Measured speedups:
- hydrogen/overview: 1.96s → 12ms (165x)
- hydrogen/daily:    270ms → 11ms (24x)
- electric/overview:  93ms →  9ms (10x)
- electric/monthly:   36ms →  9ms (4x)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:50:48 +08:00
kkfluousandClaude Opus 4.7 9a4f1945d9 feat(energy): connect to real DB (lingniu_prod)
Replace front-end mock data with live API backed by:
- tab_energy_hydrogen_bill (66.5K rows) joined with
  tab_hydrogen_site (internal stations) and tab_outside_hydrogen_site
  (external stations, joined via inner_site_id)
- tab_energy_electricity_bill (4.4K rows, all 龙王路充电站)

New server routes (src/server/routes/energy/):
- GET /api/energy/hydrogen/overview  → KPI + Top5 站点 + 区域占比
- GET /api/energy/hydrogen/daily?range=&customer=  → 日级 + 站点级下钻
- GET /api/energy/electric/overview  → KPI + 本月柱图 (fallback to last
  available month if current month has no data)
- GET /api/energy/electric/monthly?customer=  → 6 个月分组日级表

Business rules encoded server-side:
- 客户类型: customer_id IS NULL = 羚牛承担, NOT NULL = 外部
- 时区: DATETIME 列字面值是 UTC,分组前 +8h 转成 CST
- 数据清理: hydrogen_time >= 2024-01-01 (排除 1900 年脏数据)
- 站点名 fallback: short_name → name → fixed_station_name → station_name → '未知站点'
- 区域归一化: SUBSTRING_INDEX(city, '-', -1) 取最后一段,去掉 '省'/'市'
  让 '四川省-成都市' 和 '成都市' 合并为 '成都'

Component changes:
- All 4 components (HydrogenOverview, HydrogenDaily, ElectricOverview,
  ElectricDaily) now use useEffect + fetch with loading/error states
- HydrogenDaily filtering moved to server (range + customer params)
  → drops client-side TODAY constant + isInPick switch
- ElectricOverview chart title is dynamic: shows 'YYYY-MM 每日充电'
  when fallback kicks in (current month has no data)
- mock.ts deleted

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:42:37 +08:00
kkfluousandClaude Opus 4.7 7de2d1ecd5 refactor(energy): split electric view into 总览/每日 sub-tabs
- Symmetry with hydrogen — both sides now have a 每日/总览 sub-tab pair
- New ElectricOverview (KPI + bar chart) and ElectricDaily (table)
- Sub-tab styling: pill fill (active = blue-50/blue-600) instead of the
  underline-style used by parent — clearer visual hierarchy
- Tab order swapped to 每日 → 总览 with 每日 as default (daily ops focus)
- Today KPI: pill moves to absolute top-right corner so today's kwh
  reading regains full row width (was getting truncated to "510...")

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:39:05 +08:00
kkfluousandClaude Opus 4.7 42ec6e1c01 refactor(energy): drop anomaly coloring on hydrogen daily bars
All bars now use the cyan→blue gradient consistently, matching the
electric daily chart. Anomaly information stays available via the
table row tinting and the trend pills below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:33:43 +08:00
kkfluousandClaude Opus 4.7 313325553d feat(energy): hydrogen daily — period bar chart with anomaly coloring
Mirrors the electric-view treatment: a 时段每日加氢量 bar chart sits
between the customer toggle and the table. Bars use the cyan→blue
gradient by default; days where |chainPct| >= 30% render in solid
emerald (positive) or red (negative), giving an at-a-glance view of
anomalous days that's reinforced by the table's row tinting below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:32:40 +08:00
kkfluousandClaude Opus 4.7 d9b9ff495e feat(energy): electric view — daily bar chart + anomaly tint + mobile 环比
- New 本月每日充电 bar chart (蓝青 gradient) sits between KPI row and
  table, fixing the previous "wall of numbers" feel
- Day rows now tint emerald/red when |chainPct| >= 30% (matches hydrogen)
- 环比 pill column now also shows on mobile (was desktop-only)
- Today KPI: pill moves to second line alongside kwh via justify-between
  so it no longer gets clipped on narrow viewports
- Day labels in table trimmed to MM-DD (parent month row already shows year)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:05:11 +08:00
kkfluousandClaude Opus 4.7 bdd039a2c4 refactor(energy): visual polish + KPI/table self-consistency
- mock: derive ELECTRIC_KPI month/today from APR_DAYS so card and table
  totals always agree (previously ¥8,437 vs ¥9,151 mismatch)
- overview: Top5 bar chart now shows rank badges (1-5) and inline value
  labels at bar ends — readable without hover
- overview: donut "年合计 362.43T" moves into the chart center
  (previously below as a separate line, defeating the donut hole)
- daily: rows with |chainPct| ≥ 30% get a tinted background
  (green for spikes, red for drops) for at-a-glance abnormal-day spotting

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:01:13 +08:00
kkfluousandClaude Opus 4.7 2a92d991b0 fix(energy): bump KPI text-3xl to lg breakpoint to avoid landscape wrap
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:46:15 +08:00
kkfluousandClaude Opus 4.7 ccf76cba79 feat(energy): electric view with mini KPI + month grouping
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:39:55 +08:00
kkfluousandClaude Opus 4.7 a40fd2be34 feat(energy): hydrogen daily table with station drilldown
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:33:52 +08:00
kkfluousandClaude Opus 4.7 c8a1e8506e fix(energy): widen Top5 YAxis to prevent station name truncation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:29:45 +08:00
kkfluousandClaude Opus 4.7 dc1f0326fc feat(energy): hydrogen overview Top5 bar + region donut
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:27:38 +08:00
kkfluousandClaude Opus 4.7 e6880cba17 feat(energy): hydrogen overview KPI cards (4-card grid)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:20:32 +08:00
kkfluousandClaude Opus 4.7 09b9862f1f feat(energy): add module shell, register in nav
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:15:55 +08:00
kkfluousandClaude Opus 4.7 deb2f2d5da feat(energy): add types and mock data for new module
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:07:04 +08:00
kkfluousandClaude Opus 4.7 ccd97d3aae docs(energy): add 7-task implementation plan
Mobile + desktop responsive front-end module with mocked data.
Each task = lint pass + chrome-devtools visual verification (no
test framework in this project).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:04:08 +08:00
kkfluousandClaude Opus 4.7 61db692980 docs(energy): add design spec for new 能源管理 module
Mobile-first responsive entry under bottom nav. Phase 1: front-end
prototype with mocked data — backend deferred. Mirrors three FineBI
dashboards (TPqB / GBSp / 0iqP) restructured into 氢能/电能 tabs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:57:12 +08:00
kkfluousandClaude Opus 4.7 cfe79cace2 fix(assets): correct modal filtering for 待交车/库存-其他/本周X
ci/woodpecker/push/woodpecker Pipeline was successful
三个弹窗筛选问题一起修:

1. 待交车 drill-in:Pending 原本错归入 weekly-detail(该接口不支持
   model/batch/location 过滤),改走 /list 并给 /list 的 category 分支
   补上 'Pending' 状态匹配。

2. 库存-其他:'其他' 同时存在于两个体系——资产表的"库存-其他"
   (mapRegion 结果) vs 区域统计的"其他"(mapMacroRegion 结果),
   过滤语义完全不同。引入 source 参数由前端传递,source==='asset'
   时按 v.location 匹配(库存语义),否则按 mapMacroRegion(宏观区域)。
   抽取 filterByLocation 辅助函数供 /list 与 /weekly-detail 共用。

3. 本周交车/还车/替换:/weekly-detail 接口新增 model/batch/location/source
   过滤;前端 fetchWeeklyDetail 签名扩容。实现方式:SQL 结果与缓存
   车辆集(按过滤条件筛)按 truck_id 取交集。

4. BIGINT 精度丢失:DELIVERED_SQL / RETURNED_SQL / REPLACED_SQL 及
   pending/new 子查询原本使用裸 truck.id,mysql2 驱动把 BIGINT 当
   JS Number 返回,大 id (>2^53) 尾部被截,导致 truck_id 交集永远
   为空。全部改为 CAST(truck.id AS CHAR),与 MAIN_SQL 保持一致。

5. fetchVehicleList 类型补上 source,避免前端传的 source 被 URLSearchParams
   构造时静默丢弃。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:00:45 +08:00
kkfluousandClaude Opus 4.7 9ea2f306c4 feat(dev): env-gated local auth bypass for development
.env 里设 DEV_BYPASS_AUTH=1 + VITE_DEV_BYPASS_AUTH=1 即可本地免登录调试。
前端判定强制要求 import.meta.env.DEV,避免生产构建误启用。
后端塞入 dev 身份(含 所有权限 / BI-SCHEDULE-OPT 角色),保证 c.get('user')
下游依赖不会 crash。

新增 src/vite-env.d.ts 引入 vite/client 类型以访问 import.meta.env。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:00:30 +08:00
kkfluousandClaude Opus 4.7 a472e543ce refactor(scheduling): gate access strictly on BI-SCHEDULE-OPT role
ci/woodpecker/push/woodpecker Pipeline was successful
Remove the implicit fallback that granted scheduling access to any
FULL_ACCESS role (所有权限 / 数智中心 / BI-Leader). Access now requires
an explicit BI-SCHEDULE-OPT assignment, so the module scope is managed
purely via role assignment rather than piggy-backing on admin roles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:50:48 +08:00
kkfluousandClaude Opus 4.7 0c258dd1a2 fix(docker): copy src/shared into runtime image for server imports
ci/woodpecker/push/woodpecker Pipeline was successful
src/server/auth/types.ts imports runtime values (role constants,
canAccessScheduling helper) from src/shared/auth/roles.ts — without
the shared folder in the final stage the server crashes with
ERR_MODULE_NOT_FOUND. Existing shared/scheduling imports survived
only because they were type-only and elided at runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:44:49 +08:00
kkfluousandClaude Opus 4.7 200172f0af feat(scheduling): role-based access + align list count with qualifiedCount
ci/woodpecker/push/woodpecker Pipeline was successful
- Gate 智能调度 module on BI-SCHEDULE-OPT role (or full-access roles)
  via shared canAccessScheduling helper, replacing hardcoded userId allowlist
- Thread roles[] through JWT payload → middleware → frontend nav
- Add router guard that 403s non-authorized users on /api/scheduling/*
- Emit replace_qualified suggestion for every qualified vehicle so list
  count matches the 已完成考核目标 card; recalc qualifiedCount /
  hopelessCount post-permission-filter for card↔list consistency

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:42:21 +08:00
kkfluousandClaude Opus 4.7 a954fb90f6 refactor(scheduling): 考核剩余 → 年度考核剩余
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 11:04:08 +08:00