Files
mileage-bonus/.claude_plans/tranquil-sniffing-babbage.md
kkfluous 573f8397a6 chore: 添加输入输出文件 + .claude记忆和计划
输入文件:
- 租赁任务考核_2026年{1,2,3}月.xlsx (考核源数据)
- {1,2}月.xlsx (客户盈亏表)
- 车辆里程考核与奖金发放规则(V.1.2).docx

输出文件:
- 里程任务考核_{1,2,3}月核算.xlsx (月度核算结果)
- 里程任务考核_Q1汇总.xlsx (含车辆台账)
- 3月客户盈亏表(待填写).xlsx (模版)

.claude_memory: 项目记忆(规则/偏好/架构/测试车辆)
.claude_plans: 历次计划文件

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:09:24 +08:00

91 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 用户权限校验实现计划
## Context
当前系统零认证,所有 API 完全开放。需要接入公司现有的 jumpToken 认证体系,并根据用户角色实现数据权限过滤。用户从资产管理平台跳转进入本系统,携带 jumpToken。
## 认证流程
```
用户带 jumpToken 访问 → 前端提取 jumpToken
→ 后端代理调用外部API换取 sessionToken
→ 后端调用外部API获取用户信息(roles, depCode)
→ 后端签发 JWT 返回前端
→ 前端所有请求带 JWT → 后端中间件验证
```
## 三级权限模型
| 级别 | 角色条件 | 数据范围 |
|------|---------|---------|
| **full** | roleName 含 "所有权限" / "数智中心" / "BI-Leader" | 全部数据 |
| **department** | roleName 含 "BI-Leader-Dep" | 自己部门的全部数据 |
| **personal** | 无以上角色 | 仅自己负责的车辆bd=userId |
部门匹配:用户 depCode → 查 tab_department 得 dep_name → 过滤车辆数据中的 department 字段
个人匹配:添加 managerId 字段c.bd按 userId 精确匹配(不用 userName 避免重名)
## 新增/修改文件
### 后端新增
- `src/server/auth/types.ts` — AuthUser、PermissionLevel、JwtPayload 类型
- `src/server/auth/login.ts``POST /api/auth/login`接收外部token调外部API获取用户信息签发JWT+ `GET /api/auth/exchange`(代理 jumpToken 换取 sessionToken避免前端 CORS 问题)
- `src/server/auth/middleware.ts` — Hono 中间件,验证 JWT跳过 /api/health 和 /api/auth/*
- `src/server/auth/permissions.ts``filterByPermission<T>(items, user)` 通用过滤函数
### 后端修改
- `src/server/index.ts` — 挂载 auth 路由和中间件
- `src/server/routes/mileage/vehicle-info.ts` — SQL 添加 `c.bd AS manager_id`
- `src/server/routes/mileage/types.ts` — CachedVehicle 添加 `managerId: number | null`
- `src/server/routes/mileage/cache.ts` — 传递 managerId
- `src/server/routes/mileage/monitoring.ts` — 请求时 filterByPermission + 重算筛选选项
- `src/server/routes/mileage/targets.ts` — 按权限过滤
- `src/server/routes/mileage/trend.ts` — 按权限限定车牌范围
- `src/server/routes/vehicles.ts` — 所有端点用 `getVehiclesForUser(c)` 替代 `getVehicles()`
- `src/server/types.ts` — Vehicle 类型添加 managerId
### 前端新增
- `src/auth/AuthProvider.tsx` — 认证上下文,管理 jumpToken 交换和 JWT 存储
- `src/auth/useAuth.ts` — 认证状态 hook
- `src/auth/api-client.ts` — 全局 fetchJson自动附加 Authorization header
- `src/auth/UnauthorizedPage.tsx` — 未授权页面(图标 + 提示文字)
### 前端修改
- `src/App.tsx` — 包裹 AuthProvider条件渲染 Shell / UnauthorizedPage
- `src/modules/mileage/api.ts` — fetchJson 改用 auth/api-client
- `src/modules/assets/api.ts` — fetchJson 改用 auth/api-client
### 环境配置
- `.env` 添加 `JWT_SECRET``EXTERNAL_API_BASE`
- `package.json` 添加 `jsonwebtoken``@types/jsonwebtoken`
## 关键设计决策
1. **缓存全局,请求时过滤** — 监控缓存保持全量数据,每次请求根据用户权限过滤,筛选选项也从过滤后数据重算
2. **jumpToken 交换走后端代理** — 避免前端 CORS 问题,外部 API 调用全部在服务端
3. **用 managerId数字ID匹配而非 userName** — 避免重名问题
4. **JWT 存 sessionStorage** — 刷新页面不丢失,关闭标签页自动清除
5. **filterByPermission 泛型函数** — 同时适配 Vehicle 和 CachedVehicle 类型
## 实施顺序
1. 安装依赖 (`jsonwebtoken`)
2. 后端 auth 模块types → login → middleware → permissions
3. 数据模型添加 managerIdvehicle-info → types → cache
4. 挂载中间件到 server/index.ts
5. 集成权限过滤到各路由vehicles.ts, monitoring, targets, trend
6. 前端 auth 模块AuthProvider, useAuth, api-client, UnauthorizedPage
7. 前端 API 模块切换到 auth fetch
8. App.tsx 添加认证网关
9. 端到端测试三个权限级别
## 验证方式
1. 无 jumpToken 访问 → 显示"未授权访问"页面
2. 带有效 jumpToken 访问 → 正常进入,检查 JWT 签发
3. full 角色用户 → 看到全部 1004 辆车数据
4. department 角色用户 → 仅看到自己部门的车辆
5. personal 用户 → 仅看到自己负责的车辆
6. JWT 过期后请求 → 返回 401前端显示未授权
7. 全屏监控筛选选项 → 仅显示用户权限范围内的部门/客户