# 用户权限校验实现计划

## 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. 全屏监控筛选选项 → 仅显示用户权限范围内的部门/客户
