输入文件:
- 租赁任务考核_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>
91 lines
4.4 KiB
Markdown
91 lines
4.4 KiB
Markdown
# 用户权限校验实现计划
|
||
|
||
## 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. 数据模型添加 managerId(vehicle-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. 全屏监控筛选选项 → 仅显示用户权限范围内的部门/客户
|