Commit Graph

80 Commits

Author SHA1 Message Date
kkfluous
3ef0d4edfa feat(scheduling): persist notifications, batch notify flow, dedup protection
- Add tab_scheduling_notifications table with bootstrap via ensureSchedulingTables()
- Notify endpoint rewritten: dedup by (suggestion_id, candidate_plate), history list, PATCH /:id for execute/cancel lifecycle
- Batch notify endpoint returns success/skipped/failed counts
- Suggestions response now carries notificationId + notificationStatus per candidate (joined from active-notification map)
- UI: select mode with checkboxes, floating action bar, confirmation modal listing each swap; already-notified items are dimmed and skipped
- Detail view badges show sent/executed state, preventing duplicate notify

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 23:43:21 +08:00
kkfluous
31716c6547 refactor(scheduling): shared types, structured reason, cross-region candidates
- Extract shared types to src/shared/scheduling/types.ts (client/server both re-export)
- Convert SchedulingSuggestion.reason from string to structured { lines, conclusion }
- Remove hard region filter; algorithm keeps cross-region candidates with isSameRegion flag
- SuggestionDetail renders same-region vs cross-region sections with a divider
- Close detail modal when selected suggestion no longer exists in data
- Unify estimatedGain definition (strict canQualifyAfterSwap) between algorithm and API layers

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 23:36:38 +08:00
kkfluous
aa9a29fed8 fix(scheduling): use each candidate's own daysLeft for prediction
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Different assessment targets have different end dates. Previously all
candidates used the current vehicle's daysLeft, causing wrong predictions.

Now each inventory vehicle computes its own daysLeft from its assessment
target's current_year_assessment_end_date. predictedAfterSwap uses the
candidate's own daysLeft instead of the current vehicle's.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:58:47 +08:00
kkfluous
7aa0d961ce feat(scheduling): show all candidates instead of top 5, update section title
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:44:51 +08:00
kkfluous
d0984a430b refactor(scheduling): improve reason text, fix classification, polish detail view
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Classification: qualified requires actual completionRate >= 100% (not just predicted)
- Reason text: structured two-column layout (客户日均 | 考核周期剩余)
- Conclusion line in red bold (预估无法达标,需替换 / 已达标,建议换上未达标车辆)
- Remove verbose subtitle from candidate section
- Remove redundant middle line (预估考核期里程 vs 考核里程)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:32:50 +08:00
kkfluous
0a7a9a096d refactor(scheduling): condense reason text to data-only one-liner
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:11:37 +08:00
kkfluous
694e9a207a feat(scheduling): enable department/personal permission filtering
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Disable BYPASS_AUTH (was true, now false) — backend enforces JWT auth
- Scheduling suggestions filtered by department/manager permissions:
  - full: see all suggestions
  - department: see only own department's vehicles
  - personal: see only own managed vehicles
- Candidate vehicles (inventory) remain fully visible to all
- Summary recalculated after permission filtering
- Consistent with mileage module permission model

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:04:52 +08:00
kkfluous
0785c78382 fix(scheduling): only show candidates that can actually qualify after swap
replace_qualified (换下):
- Exclude already-qualified inventory (totalMileage >= yearTarget, gap=0)
- Only keep candidates where canQualifyAfterSwap=true
- Skip suggestions with no qualifiable candidates (e.g., too few days left)
- Reason text now shows customer's remaining capacity: "日均 318km × 53天 ≈ 1.7万km"

Before: showed 粤AGP9738 (缺口 0, already at target) — pointless
After: shows 粤AGQ5808 (缺口 1.7万, 换后 3.0万, 可达标) — meaningful

All replace_qualified candidates now guaranteed canQualifyAfterSwap=true.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:41:08 +08:00
kkfluous
afec75a1cc fix(scheduling): rescue candidates should be close-to-qualifying, not zero-mileage
For rescue_hopeless (换走) scenario, completely rethought candidate logic:

Before: showed biggest-gap candidates (0 mileage) → pointless, customer can't
  drive them to target
After: prioritize candidates where customer's remaining driving can push them
  over the target line (canQualifyAfterSwap), sorted by smallest gap first

Example: customer drives 178km/day × 57 days = ~1万km remaining.
- 粤AGR6869 (缺口 1990km) → 换后 3.8万, 可达标  (shown first)
- 浙FF58720 (缺口 6万km) → 换后 1万, 远不达标 (no longer shown first)

Also updated reason text to explain the math:
"该客户剩余57天还能跑约1万km,足以帮缺口小的车冲线"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:36:53 +08:00
kkfluous
1d1f8901aa fix(scheduling): exclude near-qualified vehicles from rescue candidates
For rescue_hopeless (换走) scenario, filter out inventory candidates
where totalMileage/yearTarget >= 80%. These are already near target
and swapping them in adds no value.

Instead, prioritize candidates with biggest mileage gaps — they benefit
most from accumulating any mileage, even at a low-mileage customer.

Before: showed 粤AGR6869 (93% done, 缺口 1990) as "可达标" — pointless
After:  shows 浙FF58720 (0% done, 缺口 60000) — genuinely needs mileage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:34:05 +08:00
kkfluous
9398688829 feat(scheduling): add department/manager filters, refine color palette
- Add department and manager fields to backend types and suggestions API
- Add department/manager to advanced filter panel
- Refine card colors: orange (换下) / blue (换走) / dark slate (全部)
- Selected card uses solid bg color, inactive uses gradient
- Batch pills use dark slate, confirm button uses dark slate
- Background changed to #F0F4F8 for subtle cool tone

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:28:04 +08:00
kkfluous
48fa3bc73f refactor(scheduling): rewrite terminology to match core business logic
Core story: 里程高的车换下来,里程少的车换上去。

- Summary cards: "里程高·需换下" / "里程低·需换走" / "替换建议"
- List tags: "换下" (amber) / "换走" (blue) with matching color bars
- Detail modal title: "里程高·换下此车" / "里程低·换走此车"
- Candidate section: explains WHY these vehicles are recommended
  - 换下: "以下车辆里程缺口大,换到该高里程客户处可加速达标"
  - 换走: "以下车辆里程已充足,可调给当前客户,将此车换走给高里程客户冲刺"
- Reason text: states current situation + clear action recommendation
  with specific numbers (已跑, 缺口, 日均, 完成率)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:23:35 +08:00
kkfluous
bcbeb64e28 fix(scheduling): use current year mileage for consistent data display
- Add currentYearMileage to SchedulingVehicleInfo (backend + frontend)
- Compute completionRate as currentYearMileage/yearTarget (year-based)
  instead of using overall completion_rate from DB
- Display "本年已跑" instead of "累计" in detail modal
- Fix reason text to show year completion rate

Before: 累计 4.6万, 考核 3.0万, 完成率 12.1% (mismatched periods)
After:  本年已跑 8.3万, 考核 3.0万, 完成率 275% (consistent year-based)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:08:29 +08:00
kkfluous
ec3b079311 fix(scheduling): only suggest replacements for rented/operated vehicles
Filter enriched vehicles to only include rent_status = '租赁' or '自营'.
Inventory candidates already filtered by truck_rent_status = 0 (在库).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:52:16 +08:00
kkfluous
033af15814 fix(scheduling): include soft-deleted trucks and infer type from target name
411 of 451 assessment vehicles had is_deleted=1 in tab_truck, causing type
classification to fall back to "其他" and miss all inventory matches. Fix:
- Remove is_deleted=0 filter from truck type query (assessment vehicles need type info regardless)
- Add inferTypeFromTargetName() fallback deriving type from target name when truck record is missing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:47:00 +08:00
kkfluous
253cc2f2c0 fix(scheduling): fix vehicle type classification and algorithm candidate matching
- classifyVehicleType now parses dic_type.dic_name (e.g. "4.5吨冷链车") instead of raw model code
- Remove overly strict completionRate >= 0.8 filter for hopeless candidates
- Use vehicle's yearTarget as fallback when inventory has no assessment target
- Filter out suggestions with no candidates (not actionable)
- estimatedGain counts rescue_hopeless suggestions as potential gains

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:31:44 +08:00
kkfluous
4169e04a9c feat(scheduling): add suggestions route with data aggregation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:23:56 +08:00
kkfluous
86d5bc8738 feat(scheduling): add notify route and wire up scheduling router
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 20:23:29 +08:00
kkfluous
460c9906e1 feat(scheduling): add algorithm pure functions and export mapRegion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:21:50 +08:00
kkfluous
ebe46c6f73 feat(scheduling): add backend type definitions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 20:20:18 +08:00
kkfluous
820fde5547 feat: 资产总览新增所属公司筛选,支持按归属主体过滤全页数据
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- 后端:新增 /api/vehicles/subjects 端点返回公司列表+台数预览;所有聚合端点接受 ?subject= 参数按 tab_truck.org_id 对应的主体公司过滤
- 前端:标题下方新增 Scope Chip 单选下拉,支持搜索+台数预览,选中后全页 KPI/汇总/库存统计按公司联动刷新

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:50:25 +08:00
kkfluous
d6c31dd2b6 fix: 实时监控累计总里程少算,G7S 数据源 total_km 为 NULL 时用业务库 vehicle_total_mileage 兜底
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:22:19 +08:00
kkfluous
8660c0d999 fix: 部门列表隐藏非业务员账号(超级用户/刘思宇/潘舒/黄卓华/许铮杰)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:48:07 +08:00
kkfluous
b4c4929dbb feat: 部门/业务负责人列表补齐无车辆业务员
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
/dept-stats 在按车辆聚合后,查询 tab_user 把业务部门内所有在职用户补进 managers 列表,无车辆显示为 0 辆。跳过公务车部门。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:44:01 +08:00
kkfluous
9c9d7a3805 feat: 全局客户名称脱敏(首尾保留+中间三个*)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 2-3字: 首字+*** (徐***)
- 4-6字: 首2字+***+末1字 (嘉兴***司)
- 7字+: 首4字+***+末2字 (嘉兴市乍***公司)
- 覆盖所有接口: monitoring, targets, vehicles, weekly-detail

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:23:24 +08:00
kkfluous
39f89c30a2 fix: 库存统计(inventory-stats/inventory-analysis)不设数据权限,对所有人开放
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:15:15 +08:00
kkfluous
840bf30517 fix: 本周动态也基于权限过滤后的车辆计算
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:57:32 +08:00
kkfluous
441f574238 fix: /summary 和 /by-type 接口补上权限过滤(之前遗漏)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:54:29 +08:00
kkfluous
143c1a57bb debug: 添加权限过滤日志定位问题
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 16:48:29 +08:00
kkfluous
2575778293 feat: 后端用户认证和权限过滤
- 新增 auth 模块:jumpToken 代理交换、用户信息获取、JWT 签发
- 三级权限:full(所有权限/数智中心/BI-Leader)、department(BI-Leader-Dep)、personal
- 添加 managerId 到车辆数据模型,支持个人级别按 userId 精确过滤
- auth 中间件保护所有 /api/* 端点(跳过 /api/health 和 /api/auth/*)
- 所有路由集成 filterByPermission 权限过滤

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:35:29 +08:00
kkfluous
6dbd36dcd3 refactor: replace mileage monolith with modular route files
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:35:58 +08:00
kkfluous
7ec422c13a refactor: create targets route handler 2026-04-02 13:33:14 +08:00
kkfluous
890050ed55 refactor: create trend route handler 2026-04-02 13:33:05 +08:00
kkfluous
ac2a16e7b7 refactor: create monitoring route handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:32:32 +08:00
kkfluous
459b0400b4 refactor: extract monitoring cache module 2026-04-02 13:31:07 +08:00
kkfluous
1a169feaa6 refactor: extract vehicle-info query module 2026-04-02 13:30:19 +08:00
kkfluous
bc1e0ea32e refactor: extract mileage shared types 2026-04-02 13:30:02 +08:00
kkfluous
997374cf25 feat: 客户多选筛选、统计报表里程与监控看板数据一致
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 资产管理按客户筛选改为多选(支持同时选多个客户)
- 新增 MultiSearchSelect 组件(搜索+标签+复选框)
- 统计报表 todayTotal 改用监控缓存数据,与里程看板一致

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:31:54 +08:00
kkfluous
8822ddf8ae feat: 车牌区域筛选、型号批次筛选、回到顶部修复、删除涨跌幅
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 新增车牌区域筛选(粤/沪/浙+数量),替代旧地区代码
- 新增型号批次筛选(从考核目标名称筛选车辆)
- 客户/部门增加"无值"选项筛选空值
- 修复回到顶部按钮在iOS上失效
- 删除KPI卡片涨跌幅百分比显示
- 全屏刷新按钮实际触发数据重新加载+加载动画
- 统计报表全屏刷新按钮修复

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:21:38 +08:00
kkfluous
adc9c3a9db feat: 多项优化 - 全屏加载全部数据、无值筛选、刷新按钮、加载动画、负值显示为0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 全屏监控一次加载全部车辆数据,支持完整滚动和筛选
- 客户/部门筛选增加"无值"选项筛选空数据
- 全屏刷新按钮实际触发数据重新加载,带旋转动画
- 全屏筛选时显示加载遮罩
- 负值里程前端显示为0
- 未对接车机显示"未对接"替代"-"
- 删除"未同步"标签
- 统计报表配色统一为白色主题、KPI联动选中项目
- 统计报表全屏表格列合并优化
- 车辆明细面板增加日期选择、租赁状态/部门/客户信息、里程合计
- 每分钟自动刷新数据
- 清除按钮修复租赁状态重置

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:52:45 +08:00
kkfluous
06a2edc470 feat: 租赁状态与部门分列筛选,未同步车辆显示-,卡片增加今/总标签,全屏监控压缩优化
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:01:17 +08:00
kkfluous
50eaeb05ae fix: 统计报表用年度完成率替代总完成率
- 完成率改用 current_year_completion_rate 平均值
- 50%达标数改用 current_year_completion_rate >= 0.5
- 修复后数据:40台普货 完成率51.6% 50%达标15台

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 00:08:46 +08:00
kkfluous
82dac759be fix: 环比统计跟随筛选条件正确计算
每辆车缓存其昨日里程(yesterdayKm),筛选后的环比基于
相同筛选条件下的车辆计算,而非全局对比。
例如筛选"业务一部"后,今日和昨日都只统计一部的车辆。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:50:07 +08:00
kkfluous
0b8bbbb063 feat: 支持查询指定日期里程+删除搜索关键词和车牌号筛选
- 后端支持 date 参数,指定日期时实时查询数据库(不用缓存)
- 同时查询前一天数据计算环比
- 高级筛选添加"查询日期"日期选择器
- 删除高级筛选中的"搜索关键词"和"车牌号"(已有快捷筛选)
- 筛选标签支持显示日期条件

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:41:03 +08:00
kkfluous
cbf0e18634 fix: 里程环比改为真实值(与前一天对比)
- 后端缓存刷新时查询前一天总里程(yesterdayTotal)
- 前端计算真实环比:(今日-昨日)/昨日*100%
- 上涨显示蓝色↑,下跌显示红色↓
- 昨日无数据时不显示环比

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:33:52 +08:00
kkfluous
66de41d50b fix: KPI统计跟随筛选条件变化+客户筛选修正+部门排序
- KPI统计(总里程/平均单车/监控台数)改为基于筛选后数据计算
- 移除不需要的 onlineCount 字段
- 快捷筛选"按客户"和全屏表格"客户"列改为真正的客户筛选
- 删除混乱的 projects 变量映射
- 部门列表按 一部→二部→三部 顺序排序

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:29:31 +08:00
kkfluous
8fffa141f4 fix: 修复车牌搜索失效,确保所有筛选条件正常
- 后端新增 plate 查询参数支持
- 前端将 filterPlate 传给 API 并加入依赖数组
- 所有筛选条件(部门/项目/主体/车牌/搜索/里程范围)
  均正确传递到后端并触发数据刷新

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:22:12 +08:00
kkfluous
2469da310d fix: 里程范围筛选接入后端
- 后端支持 mileageMin/mileageMax 查询参数
- 前端点击"完成筛选"时将里程范围提交到后端
- "重置所有"同时清除已应用的里程范围

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:14:56 +08:00
kkfluous
863ab17b58 fix: 筛选器和显示优化
- 删除年份筛选
- 项目筛选改用真实项目数据(ln_vehicle_contract.project_name)
- 主体查询改用 tab_truck → tab_org 的 org_name
- 里程区间改为两个独立条件(里程≥ / 里程≤)
- 未分配客户显示为 -
- 统计报表日期格式改为 M.D(如 3.25)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:06:56 +08:00
kkfluous
d8f25448d0 fix: 实时监控显示优化
1. KPI 总里程不保留小数
2. 车辆卡片先展示部门再展示客户名称,客户名称不截断
3. 无部门时展示租赁状态(自营/租赁/挂靠等)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 22:45:46 +08:00