feat(scheduling): enable department/personal permission filtering
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

- Disable BYPASS_AUTH (was true, now false) — backend enforces JWT auth
- Scheduling suggestions filtered by department/manager permissions:
  - full: see all suggestions
  - department: see only own department's vehicles
  - personal: see only own managed vehicles
- Candidate vehicles (inventory) remain fully visible to all
- Summary recalculated after permission filtering
- Consistent with mileage module permission model

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-04-16 22:04:52 +08:00
parent fb89c9beed
commit 694e9a207a
2 changed files with 15 additions and 3 deletions

View File

@@ -5,7 +5,7 @@ import type { JwtPayload, AuthUser } from './types.js';
const JWT_SECRET = process.env.JWT_SECRET || 'ln-bi-default-secret'; const JWT_SECRET = process.env.JWT_SECRET || 'ln-bi-default-secret';
// 临时:跳过所有认证(保留完整逻辑便于快速恢复) // 临时:跳过所有认证(保留完整逻辑便于快速恢复)
const BYPASS_AUTH = true; const BYPASS_AUTH = false;
export async function authMiddleware(c: Context, next: Next) { export async function authMiddleware(c: Context, next: Next) {
const path = c.req.path; const path = c.req.path;

View File

@@ -5,7 +5,7 @@ import { fetchVehicleInfoMap } from '../mileage/vehicle-info.js';
import { mapRegion } from '../vehicles.js'; import { mapRegion } from '../vehicles.js';
import { filterByPermission, maskCustomerNames } from '../../auth/permissions.js'; import { filterByPermission, maskCustomerNames } from '../../auth/permissions.js';
import { classifyVehicle, generateSuggestions } from './algorithm.js'; import { classifyVehicle, generateSuggestions } from './algorithm.js';
import type { EnrichedVehicle, InventoryVehicle, SchedulingResponse } from './types.js'; import type { EnrichedVehicle, InventoryVehicle, SchedulingResponse, SchedulingSummary } from './types.js';
import type { AuthUser } from '../../auth/types.js'; import type { AuthUser } from '../../auth/types.js';
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -305,8 +305,20 @@ app.get('/', async (c) => {
vehicleCount: targetVehicleCounts.get(t.id) || 0, vehicleCount: targetVehicleCounts.get(t.id) || 0,
})); }));
// Recalculate summary based on permission-filtered results
const filteredQualified = masked.filter((s: any) => s.type === 'replace_qualified').length;
const filteredHopeless = masked.filter((s: any) => s.type === 'rescue_hopeless').length;
const filteredSummary: SchedulingSummary = {
qualifiedCount: summary.qualifiedCount,
hopelessCount: summary.hopelessCount,
suggestionCount: masked.length,
estimatedGain: masked.filter((s: any) =>
s.candidates?.some((c: any) => c.canQualifyAfterSwap)
).length,
};
const response: SchedulingResponse = { const response: SchedulingResponse = {
summary, summary: filteredSummary,
suggestions: masked, suggestions: masked,
targets: targetOptions, targets: targetOptions,
}; };