feat: 全局客户名称脱敏(首尾保留+中间三个*)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 2-3字: 首字+*** (徐***) - 4-6字: 首2字+***+末1字 (嘉兴***司) - 7字+: 首4字+***+末2字 (嘉兴市乍***公司) - 覆盖所有接口: monitoring, targets, vehicles, weekly-detail Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<T>(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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Vehicle[]> {
|
||||
|
||||
async function getVehiclesForUser(c: Context): Promise<Vehicle[]> {
|
||||
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<string, number> {
|
||||
@@ -1009,7 +1006,8 @@ app.get('/weekly-detail', async (c) => {
|
||||
return c.json([]);
|
||||
}
|
||||
const [rows] = await pool.query<any[]>(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
|
||||
|
||||
Reference in New Issue
Block a user