- imports: 新增 preflight_import 预检报告(判定/分阶段统计/阻断归因/问题/下一步/错误示例),导入任务 settings 落库(映射/校区/更新/重复/未匹配策略),预览应用策略,向导提交写操作日志 - ai-chat: 新增 excel_analyze(ExcelJS)工具,移除附件/上下文截断,start_import_wizard 支持确认参数,ui.import_preflight SSE,预览确认写操作日志 - admin: ImportPreflightCard 渲染与持久化,聊天抽屉布局/侧边栏修复,考勤页 CSS 引入,费用/学生页接口 schema 校验修复
73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
import type {
|
||
ImportRowAction,
|
||
ImportRowStatus,
|
||
ImportRunSettings,
|
||
} from './imports.types';
|
||
import type { ValidatedRow } from './imports.rows';
|
||
|
||
export interface PolicyPreviewResult {
|
||
errors: string[];
|
||
action: ImportRowAction | null;
|
||
status: ImportRowStatus;
|
||
}
|
||
|
||
function isDuplicateError(error: string): boolean {
|
||
return (
|
||
error.includes('请勿重复导入') ||
|
||
error.includes('请勿重复换宿') ||
|
||
error.includes('本次文件中已有')
|
||
);
|
||
}
|
||
|
||
function isReferenceError(error: string): boolean {
|
||
return (
|
||
error.includes('未找到匹配学生') ||
|
||
error.includes('缺少学生标识') ||
|
||
error.includes('未找到宿舍') ||
|
||
error.includes('未找到原宿舍') ||
|
||
error.includes('未找到新宿舍') ||
|
||
error.includes('未找到该学生在原宿舍的在住记录')
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 把 AI 预检确认的策略应用到预览行:
|
||
* - updateExisting=false:已匹配行改为跳过;
|
||
* - duplicatePolicy=skip:文件内重复行跳过并保留提示;
|
||
* - skipUnmatched=true:关系表找不到学生/宿舍的行跳过并保留提示。
|
||
*/
|
||
export function applyPreviewPolicies(
|
||
result: ValidatedRow,
|
||
settings: ImportRunSettings | null,
|
||
): PolicyPreviewResult {
|
||
const errors = [...result.errors];
|
||
let action = result.action;
|
||
let status: ImportRowStatus = errors.length > 0 ? 'error' : 'valid';
|
||
|
||
const duplicatePolicy = settings?.duplicatePolicy ?? 'error';
|
||
const updateExisting = settings?.updateExisting ?? true;
|
||
const skipUnmatched = settings?.skipUnmatched ?? false;
|
||
|
||
if (duplicatePolicy === 'skip' && errors.some(isDuplicateError)) {
|
||
action = 'skip';
|
||
status = 'valid';
|
||
const keptErrors = errors.filter((error) => !isDuplicateError(error));
|
||
keptErrors.push('文件内重复行,已按策略跳过');
|
||
errors.splice(0, errors.length, ...keptErrors);
|
||
}
|
||
|
||
if (!updateExisting && errors.length === 0 && action === 'update') {
|
||
action = 'skip';
|
||
status = 'valid';
|
||
errors.push('已匹配现有记录,按策略跳过更新');
|
||
}
|
||
|
||
if (skipUnmatched && status === 'error' && errors.every(isReferenceError)) {
|
||
action = 'skip';
|
||
status = 'valid';
|
||
errors.push('未匹配学生/宿舍,按策略跳过');
|
||
}
|
||
|
||
return { errors, action, status };
|
||
}
|