Commit Graph
100 Commits
Author SHA1 Message Date
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
kkfluousandClaude Opus 4.7 2ea00a5383 refactor(scheduling): 拆分 reason 区为 客户/车辆 两栏
ci/woodpecker/push/woodpecker Pipeline was successful
详情页的指标区从单列两格网格改为 左:客户 / 右:车辆 两栏。客户日均归
左侧,考核剩余、日均需、年度完成率、可为新车贡献归右侧,便于一眼
识别数据归属。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 10:01:59 +08:00
kkfluousandClaude Opus 4.7 cf138f67c0 refactor(scheduling): remove 7日 客户日均 趋势徽章
详情页和列表里的 ↗ 7日 +X% / ↘ 7日 -X% 徽章移除,客户日均只保留
30 日均这一项。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 10:00:10 +08:00
kkfluousandClaude Opus 4.7 e32b0b58b3 fix(scheduling): 近7天 filter should be time-only, not exclude cancelled
Previously the toggle hid cancelled records, so users who clicked a
record timestamped within 7 days but later cancelled would see nothing.
Now 近7天 filters purely by createdAt; combine with status tabs to
narrow further.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:56:30 +08:00
kkfluousandClaude Opus 4.7 9d1e8c4d30 feat(scheduling): enrich history records with customer/dept/manager + drill-in to swap plan
Each row in 调度记录 now shows 业务部门(简)/业务负责人/客户 beneath
the plate line, and is clickable to open the reusable SwapPreview
showing the full replacement plan (current mileage, 考核目标, 替换后预测).
Drill-in is only enabled when the suggestion is still in the active
scheduling view; the user can still 取消干预 from the preview.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:30:07 +08:00
kkfluousandClaude Opus 4.7 ba1e0e9f16 feat(scheduling): add 近期已干预 summary card (last 7 days)
Restore 替换建议 card and add a new emerald 近期已干预 card. Clicking
opens the history modal pre-filtered to the last 7 days (excluding
cancelled) via a toggle chip users can switch off.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:25:59 +08:00
kkfluousandClaude Opus 4.7 1b2ad68743 fix(scheduling): enforce one active intervention per current vehicle
Business rule: a running vehicle can hold AT MOST ONE active (sent|executed)
intervention. Switching to a different candidate requires cancelling the
prior one first.

- Server: insertNotification dedup key changes from (suggestion_id,
  candidate_plate) to just suggestion_id; 409 response includes the blocking
  candidate plate
- Detail modal: shows a banner naming the locked candidate; non-active
  candidates render a disabled "该车已有其他干预,请先解除" hint instead
  of the action button
- Batch: pickBestCandidate returns null for any suggestion already holding
  an active intervention — the whole suggestion is excluded

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:19:43 +08:00
kkfluousandClaude Opus 4.7 210db7f8ff feat(scheduling): rename 通知→干预, allow drill-in on intervened items
- Globally rename user-facing 通知 → 干预 (list badge, detail button, batch
  modal, CSV header, server response messages, db table comment)
- 已干预 row in detail is now clickable — opens SwapPreview which shows
  a read-only summary plus a 取消干预 action (PATCH notify /:id with
  status=cancelled). Sending is blocked while already intervened.
- Selected suggestion now follows the latest data snapshot so status
  changes from within the detail flow propagate immediately.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:14:53 +08:00
kkfluousandClaude Opus 4.7 1d9f4cb43d feat(scheduling): history view, execute/cancel lifecycle, CSV export, 7d trend
- Add 调度记录 modal: lists notifications by status, supports 标记已执行 (with
  after-mileage + notes) and 取消 for open records
- Add CSV export of filtered suggestions (UTF-8 BOM for Excel); top candidate
  per row picked by same-region > can-qualify preference
- Compute customer 7-day average alongside 30-day baseline in a single query;
  show trend indicator (up/down/flat) next to 客户日均 in list and detail card

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 23:47:31 +08:00
kkfluousandClaude Opus 4.7 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
kkfluousandClaude Opus 4.7 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
kkfluousandClaude Opus 4.7 335282a2c3 feat(scheduling): add km unit to 客户日均; move 年度考核 to top-right of list item
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 23:24:25 +08:00
kkfluous dfc32c4485 fix(scheduling): rename 日均→客户日均 to avoid ambiguity
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 23:07:49 +08:00
kkfluous ceed067807 refactor(scheduling): re-layout list items — left group (dept/manager/customer) + right group (daily/rate)
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 23:06:49 +08:00
kkfluous 2f11afc25f fix(scheduling): shorten department name by removing 业务 prefix
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 23:05:40 +08:00
kkfluous 9f781c766a feat(scheduling): rename 完成→年度达标, add sort by 客户日均/年度达标 to list
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 23:03:59 +08:00
kkfluous 8664317852 feat(scheduling): show department and manager in list items and detail card
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 23:02:03 +08:00
kkfluous c3de4ebaf5 refactor(scheduling): redesign current vehicle card to match candidate card style
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 23:00:22 +08:00
kkfluousandClaude Opus 4.6 aa9a29fed8 fix(scheduling): use each candidate's own daysLeft for prediction
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 a52a77f3a2 feat(scheduling): batch filter as multi-select pills instead of dropdown
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:54:01 +08:00
kkfluous 9012a955b8 feat(scheduling): add 剩余xx天 to candidate header
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:52:50 +08:00
kkfluous caff78c5f3 fix(scheduling): rename 预估年终 to 考核期结束预估
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:51:37 +08:00
kkfluous f6f872d2ce fix(scheduling): remove duplicate mileage text, keep only metrics row
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:50:42 +08:00
kkfluous dbefb90089 feat(scheduling): add metrics row (当前/预估年终/考核) for current vehicle
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:49:43 +08:00
kkfluous db568c1ebb fix(scheduling): rename 全部批次→全部, reorder metrics to 当前/替换后预计/考核
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:48:26 +08:00
kkfluousandClaude Opus 4.6 ba6a38973d feat(scheduling): add batch filter and sort controls for candidate list
ci/woodpecker/push/woodpecker Pipeline was successful
- Batch dropdown filter (全部批次 / per target name)
- Sort by 替换后预计 (asc/desc toggle)
- Sort by 当前里程 (asc/desc toggle)
- Active sort button highlighted in blue
- Display count shows filtered/total (e.g. "3/12 辆")

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:46:42 +08:00
kkfluous 7aa0d961ce feat(scheduling): show all candidates instead of top 5, update section title
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:44:51 +08:00
kkfluous 4f02a54d38 fix(scheduling): move region to header as pin badge, remove from metrics grid
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:39:31 +08:00
kkfluous 073496cd44 fix(scheduling): reorder candidate metrics to 区域/考核/当前/替换后预计, remove 缺口
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:38:25 +08:00
kkfluous bea67b6710 fix(scheduling): remove decimals from km display (1,990.2 → 1,990)
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:37:14 +08:00
kkfluousandClaude Opus 4.6 d0984a430b refactor(scheduling): improve reason text, fix classification, polish detail view
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 b3a6beb26b fix(scheduling): update card titles per user request
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:12:19 +08:00
kkfluous 0a7a9a096d refactor(scheduling): condense reason text to data-only one-liner
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:11:37 +08:00
kkfluous dd03157804 refactor(scheduling): simplify card labels for clarity
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 22:09:27 +08:00
kkfluousandClaude Opus 4.6 694e9a207a feat(scheduling): enable department/personal permission filtering
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 fb89c9beed fix(scheduling): change 辆 to 干预 in suggestion list
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-16 21:57:35 +08:00
kkfluousandClaude Opus 4.6 75f0aca5d1 fix(auth): require jumpToken for access, remove temporary bypass
ci/woodpecker/push/woodpecker Pipeline was successful
Previously: no jumpToken → direct access allowed (临时放行)
Now: no jumpToken → show "请从业务系统跳转访问" unauthorized page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:48:29 +08:00
kkfluousandClaude Opus 4.6 8598aea445 feat(scheduling): restrict scheduling module to allowed users only
Only userId 1105261382487539712 and 1116631120763437056 can see the
scheduling tab. Other users see only assets + mileage modules.

- Add userId to frontend AuthState.user type
- App.tsx conditionally includes scheduling module based on user ID
- Backend already returns userId in auth exchange response

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:47:57 +08:00
kkfluousandClaude Opus 4.6 25199b507c refactor(scheduling): simplify SwapPreview layout, remove verbose reason
- Remove type badge, reason section — too verbose
- Two clean white cards connected by arrow (swap diagram)
- Result section: predicted mileage, target, conclusion badge
- Tighter spacing, no redundant labels
- Professional tone, no childish wording

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:45:01 +08:00
kkfluousandClaude Opus 4.6 6a3a5ba319 feat(scheduling): add full-screen SwapPreview for screenshot sharing
New SwapPreview component replaces direct "发送通知" button:
- Full-screen white background for clean screenshots
- Swap diagram: current vehicle → arrow → replacement vehicle
- Replacement reason section
- Post-swap prediction: predicted mileage, target, conclusion
- "发送替换通知" button at bottom
- Candidate button in detail modal changed to "查看替换方案 →"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:43:18 +08:00
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 81305be2df feat(scheduling): replace spinner with skeleton loading placeholders
- Full-page skeleton on initial load: card placeholders + list row placeholders
- List skeleton on refresh: 6 rows with pulse animation
- Skeleton blocks match actual layout (color bar, plate, badges, info line)
- Uses Tailwind animate-pulse for smooth loading effect

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:30:23 +08:00
kkfluousandClaude Opus 4.6 64f47d5ad6 fix(scheduling): truncate long customer names, prevent list item wrap
- Customer name in list items: truncate with max-w-[40%]
- Daily km and completion rate: flex-shrink-0 to stay on same line

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:28:30 +08:00
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 1a5a1c1514 feat(scheduling): add advanced filter panel matching prototype
- Filter icon in list header with active count badge
- Expandable filter panel: plate search, region select, vehicle type select, customer select
- FilterSelect component with search for long option lists
- Active filter tags shown as removable pills below header
- Temp/confirmed filter pattern (edit → confirm/cancel)
- Result count displayed when filters active

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:18:02 +08:00
kkfluousandClaude Opus 4.6 73080a605d refactor(scheduling): polish overall color scheme and UX
- Modal header: unified dark slate-800 with directional icon (↓ rescue, ↑ release)
- Modal click-outside to close
- Candidate metrics: table-style with bg-slate-50 + dividers, more scannable
- Send button: dark slate instead of blue (avoids color overload)
- Reason section: warm amber accent
- Consistent font sizing and spacing throughout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:13:12 +08:00
kkfluousandClaude Opus 4.6 6f7555a407 feat(scheduling): make summary cards clickable filters + refine color scheme
- Cards filter suggestions by type (已达标/无望达标/全部)
- Toggle: click active card again to reset to all
- Default: white bg + gray border; active: colored bg + ring
- Batch selector: dark pills instead of blue
- Refresh button moved into list header
- Reset type filter when switching batch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:12:02 +08:00
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 6ee811c937 refactor(scheduling): optimize UI for clarity and information density
- Summary cards: white bg + color border, remove icons, more compact
- SuggestionList: replace badge stacking with compact 2-line layout,
  use color bars for priority, fix completion rate format (0.16 → 16.4%)
- SuggestionDetail: bottom-sheet on mobile, compact inline metrics
  instead of grid cards, reduce vertical space per candidate
- Follows ui-ux-pro-max Data-Dense Dashboard guidelines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:04:26 +08:00
kkfluousandClaude Opus 4.6 495f4bf44f feat(scheduling): add 本年考核 field to candidate cards and rename 年度目标 to 本年考核
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:01:01 +08:00
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 db5ca2e686 feat(scheduling): wire up scheduling module in app navigation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:26:53 +08:00
kkfluousandClaude Sonnet 4.6 2e82a30893 feat(scheduling): add SuggestionDetail modal with candidate comparison
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 20:25:58 +08:00
kkfluousandClaude Sonnet 4.6 9c005bebc8 feat(scheduling): add SuggestionList component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 20:25:21 +08:00
kkfluousandClaude Sonnet 4.6 82ee7f5480 feat(scheduling): add SchedulingModule main entry component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 20:25:13 +08:00
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Sonnet 4.6 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
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Sonnet 4.6 569b5ea349 feat(scheduling): add frontend types and API client
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 20:20:23 +08:00
kkfluousandClaude Sonnet 4.6 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
kkfluousandClaude Opus 4.6 32b297c731 docs: 智能调度模块实现计划
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:17:08 +08:00
kkfluousandClaude Opus 4.6 9bf9bdd8ff docs: 智能调度模块设计规格
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:11:51 +08:00
kkfluousandClaude Opus 4.6 188d2b105f fix: 关闭全局演示模糊模式,恢复车牌等敏感字段正常显示,版本号 1.1.5
ci/woodpecker/push/woodpecker Pipeline was successful
Shell.tsx 的 DemoModeProvider 之前设为 enabled={true},Blur 组件包裹的
车牌/客户名/经理名全部被 blur(5px) 盖住。改为 false 恢复真实显示。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:59:58 +08:00
kkfluousandClaude Opus 4.6 26d59190c9 feat: 小程序 webview 内点全屏监控自动 CSS 横屏,版本号 1.1.4
ci/woodpecker/push/woodpecker Pipeline was successful
小程序 webview 无法调用系统旋转 API,竖屏全屏体验很差。检测到微信/抖音/
支付宝小程序 UA 且当前为竖屏时,全屏覆盖层用 transform: rotate(90deg)
配合 100vh × 100vw 的尺寸模拟真横屏,用户用横屏姿势看设备即可获得横屏
监控面板。浏览器会自动把触摸坐标映射回旋转前坐标系,交互不受影响。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:42:43 +08:00
kkfluousandClaude Opus 4.6 f3b795e8a9 fix: 小程序/webview 内需点两次返回才能退出的问题,版本号 1.1.3
ci/woodpecker/push/woodpecker Pipeline was successful
Shell 首次挂载时用 location.hash= 同步模块到 URL hash,会 push 一条多余
的 history 记录;webview 里第一次返回只是回到 hash 为空的同一页面没有
视觉变化,得再按一次才能真正退出。

改为 history.replaceState 更新 hash,切换 tab 也走 replace,整个应用
只占用一个 history 记录,一次返回即可退出。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:13:01 +08:00
kkfluousandClaude Opus 4.6 71ff459111 fix: 补提交缺失的 Blur 组件,修复 CI 打包找不到模块,版本号 1.1.2
ci/woodpecker/push/woodpecker Pipeline was successful
AssetsModule 等引用 src/components/Blur 但该文件未纳入版本库导致 CI
rollup 报 Could not resolve "../../components/Blur"。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:53:57 +08:00
kkfluousandClaude Opus 4.6 4acf10ef79 fix: 修复打包 lint 报错,版本号 1.1.1
ci/woodpecker/push/woodpecker Pipeline failed
middleware.ts 临时跳过认证的早 return 导致后续代码 unreachable,
TS 在不可达分支里不做类型 narrowing 触发 TS18048;
改为 BYPASS_AUTH 常量分支保留完整鉴权逻辑便于恢复。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:53:01 +08:00
kkfluousandClaude Opus 4.6 820fde5547 feat: 资产总览新增所属公司筛选,支持按归属主体过滤全页数据
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
kkfluousandClaude Opus 4.6 d6c31dd2b6 fix: 实时监控累计总里程少算,G7S 数据源 total_km 为 NULL 时用业务库 vehicle_total_mileage 兜底
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
kkfluousandClaude Opus 4.6 8660c0d999 fix: 部门列表隐藏非业务员账号(超级用户/刘思宇/潘舒/黄卓华/许铮杰)
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
kkfluousandClaude Opus 4.6 b4c4929dbb feat: 部门/业务负责人列表补齐无车辆业务员
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
kkfluousandClaude Opus 4.6 e4f682dff5 fix: 按部门下钻车辆数与列表不一致,补充 category=Operating 过滤
ci/woodpecker/push/woodpecker Pipeline was successful
dept-stats 只统计 Operating 车辆,但下钻弹窗调用 /list 时未传 category
参数,导致返回了非运营状态车辆(如业务二部显示233但下钻显示237)。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:32:43 +08:00
kkfluousandClaude Opus 4.6 cbbdb7bb5f fix: 2-3字客户名脱敏改为 首字+* (如 徐*)
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:24:43 +08:00
kkfluousandClaude Opus 4.6 9c9d7a3805 feat: 全局客户名称脱敏(首尾保留+中间三个*)
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
kkfluousandClaude Opus 4.6 e7efe179b7 fix: 未授权页面增加两种进入方式说明
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:19:18 +08:00
kkfluousandClaude Opus 4.6 39f89c30a2 fix: 库存统计(inventory-stats/inventory-analysis)不设数据权限,对所有人开放
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
kkfluousandClaude Opus 4.6 8ed2031c45 feat: 水印移到全局Shell,资产和里程页面都有水印
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:12:48 +08:00
kkfluousandClaude Opus 4.6 c2d227059c feat: 实时监控加载动画 - KPI骨架屏+车辆列表skeleton
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:10:18 +08:00
kkfluousandClaude Opus 4.6 1680c53279 feat: 支持 URL path 路由进入不同模块
ci/woodpecker/push/woodpecker Pipeline was successful
/vehicle 或 /assets → 资产管理
/mileage → 里程管理

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:05:24 +08:00
kkfluousandClaude Opus 4.6 b5806b3148 feat: 生产环境配置 EXTERNAL_API_BASE=lnh2e.com 和 JWT_SECRET
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:01:49 +08:00
kkfluousandClaude Opus 4.6 840bf30517 fix: 本周动态也基于权限过滤后的车辆计算
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
kkfluousandClaude Opus 4.6 441f574238 fix: /summary 和 /by-type 接口补上权限过滤(之前遗漏)
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: 添加权限过滤日志定位问题
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 16:48:29 +08:00
kkfluous 4cd76b6a30 feat: 添加 /api/auth/me 调试端点查看当前用户权限
ci/woodpecker/push/woodpecker Pipeline was successful
2026-04-02 16:44:41 +08:00
kkfluousandClaude Opus 4.6 09719f3cd6 fix: 水印改为 用户名+时间 替代 羚牛氢能+时间
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:38:20 +08:00
kkfluousandClaude Opus 4.6 bf1f1946e4 fix: 合并 exchange+login 为一步,直接从 jumpToken 响应提取用户信息签发JWT
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:04:09 +08:00
kkfluousandClaude Opus 4.6 a7ec5ba7b0 fix: 防止 StrictMode 双重调用导致 jumpToken 重复消费
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:59:11 +08:00
kkfluousandClaude Opus 4.6 f66049dcbc feat: 前端认证网关 + API 自动附加 JWT
ci/woodpecker/push/woodpecker Pipeline was successful
- AuthProvider 管理 jumpToken 交换和 JWT 生命周期
- 未授权页面(ShieldX 图标 + 提示文字)
- 加载中旋转动画
- fetchJson 全局客户端自动附加 Authorization header
- 401 响应触发重新认证
- JWT 存 sessionStorage,刷新不丢失

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:38:22 +08:00
kkfluousandClaude Opus 4.6 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
kkfluousandClaude Opus 4.6 6dbd36dcd3 refactor: replace mileage monolith with modular route files
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