From 9c9d7a3805c8920800d133ecb060020df00dfba1 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Thu, 2 Apr 2026 20:23:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=A8=E5=B1=80=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E8=84=B1=E6=95=8F=EF=BC=88=E9=A6=96=E5=B0=BE?= =?UTF-8?q?=E4=BF=9D=E7=95=99+=E4=B8=AD=E9=97=B4=E4=B8=89=E4=B8=AA*?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 2-3字: 首字+*** (徐***) - 4-6字: 首2字+***+末1字 (嘉兴***司) - 7字+: 首4字+***+末2字 (嘉兴市乍***公司) - 覆盖所有接口: monitoring, targets, vehicles, weekly-detail Co-Authored-By: Claude Opus 4.6 (1M context) --- src/server/auth/permissions.ts | 21 +++++++++++++++++++++ src/server/routes/mileage/monitoring.ts | 4 ++-- src/server/routes/mileage/targets.ts | 4 ++-- src/server/routes/vehicles.ts | 12 +++++------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/server/auth/permissions.ts b/src/server/auth/permissions.ts index dc14642..76120e7 100644 --- a/src/server/auth/permissions.ts +++ b/src/server/auth/permissions.ts @@ -1,5 +1,26 @@ import type { AuthUser } from './types.js'; +/** 客户名称脱敏 */ +export function maskCustomerName(name: string | null): string | null { + if (!name) return name; + const len = name.length; + if (len <= 1) return '*'; + if (len <= 3) return name[0] + '***'; + if (len <= 6) return name.slice(0, 2) + '***' + name.slice(-1); + return name.slice(0, 4) + '***' + name.slice(-2); +} + +/** 对数据列表中的客户名称进行脱敏 */ +export function maskCustomerNames(items: T[]): T[] { + return items.map(v => { + const obj = v as any; + const copy = { ...obj }; + if ('customer' in copy && copy.customer) copy.customer = maskCustomerName(copy.customer); + if ('customerName' in copy && copy.customerName) copy.customerName = maskCustomerName(copy.customerName); + return copy as T; + }); +} + /** * 通用权限过滤函数 * 适配 CachedVehicle(department, manager, managerId)和 Vehicle(departmentName, customerManager, managerId) diff --git a/src/server/routes/mileage/monitoring.ts b/src/server/routes/mileage/monitoring.ts index f173b08..08c4ea1 100644 --- a/src/server/routes/mileage/monitoring.ts +++ b/src/server/routes/mileage/monitoring.ts @@ -1,6 +1,6 @@ import { Hono } from 'hono'; import { getCache, queryDateMileage, buildDateFilters } from './cache.js'; -import { filterByPermission } from '../../auth/permissions.js'; +import { filterByPermission, maskCustomerNames } from '../../auth/permissions.js'; import type { AuthUser } from '../../auth/types.js'; import type { CachedVehicle, MonitoringFilters, MonitoringResponse } from './types.js'; @@ -115,7 +115,7 @@ app.get('/', async (c) => { const total = filtered.length; return c.json({ - vehicles: paged, + vehicles: maskCustomerNames(paged), stats, filters, total, diff --git a/src/server/routes/mileage/targets.ts b/src/server/routes/mileage/targets.ts index 7301e32..f6bb3b3 100644 --- a/src/server/routes/mileage/targets.ts +++ b/src/server/routes/mileage/targets.ts @@ -3,7 +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'; +import { filterByPermission, maskCustomerNames } from '../../auth/permissions.js'; const app = new Hono(); @@ -173,7 +173,7 @@ app.get('/:id/vehicles', async (c) => { 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); + return c.json(maskCustomerNames(filtered)); } catch (e: unknown) { console.error('target vehicles error:', e); return c.json([], 500); diff --git a/src/server/routes/vehicles.ts b/src/server/routes/vehicles.ts index 30d02d9..56d8ca5 100644 --- a/src/server/routes/vehicles.ts +++ b/src/server/routes/vehicles.ts @@ -10,7 +10,7 @@ import type { BatchGroup, InventoryTypeSummary, } from '../types.js'; -import { filterByPermission } from '../auth/permissions.js'; +import { filterByPermission, maskCustomerNames, maskCustomerName } from '../auth/permissions.js'; import type { AuthUser } from '../auth/types.js'; import type { Context } from 'hono'; @@ -312,15 +312,12 @@ async function getVehicles(): Promise { async function getVehiclesForUser(c: Context): Promise { const all = await getVehicles(); - // Hono 子路由 context 可能丢失变量,尝试多种方式获取 const user = ((c as any).get?.('user') || (c as any).var?.user) as AuthUser | undefined; if (user) { const filtered = filterByPermission(all, user); - console.log(`[vehicles] permission: ${user.permissionLevel}, user: ${user.userName}, before: ${all.length}, after: ${filtered.length}`); - return filtered; + return maskCustomerNames(filtered); } - console.log('[vehicles] WARNING: no user in context, returning all'); - return all; + return maskCustomerNames(all); } function getRegionCounts(vehicles: Vehicle[], regions: readonly string[]): Record { @@ -1009,7 +1006,8 @@ app.get('/weekly-detail', async (c) => { return c.json([]); } const [rows] = await pool.query(sql); - return c.json(rows); + const masked = (rows as any[]).map(r => ({ ...r, customer_name: maskCustomerName(r.customer_name) })); + return c.json(masked); }); // GET /api/vehicles/refresh — force cache refresh