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>
This commit is contained in:
kkfluous
2026-04-02 15:35:29 +08:00
parent 6dbd36dcd3
commit 2575778293
14 changed files with 395 additions and 14 deletions

View File

@@ -77,6 +77,7 @@ function mergeVehicles(
customer: info?.customer || null,
department: info?.department || null,
manager: info?.manager || null,
managerId: info?.manager_id || null,
rentStatus: info?.rent_status || null,
entity: info?.entity || null,
project: info?.project || null,

View File

@@ -1,5 +1,7 @@
import { Hono } from 'hono';
import { getCache, queryDateMileage, buildDateFilters } from './cache.js';
import { filterByPermission } from '../../auth/permissions.js';
import type { AuthUser } from '../../auth/types.js';
import type { CachedVehicle, MonitoringFilters, MonitoringResponse } from './types.js';
const app = new Hono();
@@ -86,6 +88,13 @@ app.get('/', async (c) => {
filters = cache.filters;
}
// 权限过滤
const user = (c as any).get('user') as AuthUser | undefined;
if (user) {
allVehicles = filterByPermission(allVehicles, user);
filters = buildDateFilters(allVehicles); // 重算筛选选项以匹配权限范围
}
const filtered = applyFilters(allVehicles, filterParams);
const stats = {

View File

@@ -3,6 +3,7 @@ import pool from '../../db.js';
import mileagePool from '../../mileage-db.js';
import { getCache } from './cache.js';
import { fetchVehicleInfoByPlates } from './vehicle-info.js';
import { filterByPermission } from '../../auth/permissions.js';
const app = new Hono();
@@ -170,7 +171,9 @@ app.get('/:id/vehicles', async (c) => {
};
});
return c.json(result);
const user = (c as any).get('user') as import('../../auth/types.js').AuthUser | undefined;
const filtered = user ? filterByPermission(result, user) : result;
return c.json(filtered);
} catch (e: unknown) {
console.error('target vehicles error:', e);
return c.json([], 500);

View File

@@ -10,6 +10,7 @@ export interface CachedVehicle {
customer: string | null;
department: string | null;
manager: string | null;
managerId: string | null;
rentStatus: string | null;
entity: string | null;
project: string | null;
@@ -68,6 +69,7 @@ export interface VehicleInfoRow {
customer: string | null;
department: string | null;
manager: string | null;
manager_id: string | null;
rent_status: string | null;
entity: string | null;
project: string | null;

View File

@@ -7,6 +7,7 @@ export const VEHICLE_INFO_SQL = `SELECT
cus.customer_name AS customer,
dep.dep_name AS department,
u.user_name AS manager,
CAST(c.bd AS CHAR) AS manager_id,
dic_status.dic_name AS rent_status,
org_truck.org_name AS entity,
c.project_name AS project