feat: Excel 导入预检与动态问答,AI 聊天/文件解析体验修复

- imports: 新增 preflight_import 预检报告(判定/分阶段统计/阻断归因/问题/下一步/错误示例),导入任务 settings 落库(映射/校区/更新/重复/未匹配策略),预览应用策略,向导提交写操作日志
- ai-chat: 新增 excel_analyze(ExcelJS)工具,移除附件/上下文截断,start_import_wizard 支持确认参数,ui.import_preflight SSE,预览确认写操作日志
- admin: ImportPreflightCard 渲染与持久化,聊天抽屉布局/侧边栏修复,考勤页 CSS 引入,费用/学生页接口 schema 校验修复
This commit is contained in:
2026-08-05 21:12:50 +08:00
parent b70f45fb04
commit ab4765cee5
44 changed files with 2449 additions and 166 deletions

View File

@@ -1,11 +1,8 @@
export const MAX_HISTORY_MESSAGES = 30;
export const MAX_CONTEXT_CHARS = 64 * 1024;
export const MAX_TOOL_CALLS_PER_ROUND = 50;
export const MAX_TOOL_ROUNDS = 90;
export const MAX_SUMMARY_CHARS = 2000;
export const MAX_GENERATED_CHARS = 256 * 1024;
export const MAX_ATTACHMENT_TEXT_CHARS = 20000;
export const MAX_FOCUS_CONTENT_CHARS = 40000;
export const DEFAULT_TITLE = '新对话';
const CELL_VALUE_ANY_OF = [
@@ -16,12 +13,75 @@ const CELL_VALUE_ANY_OF = [
];
export const A2UI_TOOL_SCHEMAS = [
{
type: 'function' as const,
function: {
name: 'preflight_import',
description:
'对上传的 Excel 进行导入预检并生成“可插入性报告”:分阶段行数(新建/更新/错误/跳过)、阻断原因(缺列、未找到学生/宿舍、文件内重复、已有在住、格式错误、未知校区)、需要确认的问题与导入后的下一步建议。当用户上传 Excel 并疑似需要批量导入学生、宿舍、换宿或入住数据时先调用;根据报告向用户确认后,再调用 start_import_wizard。',
parameters: {
type: 'object',
properties: {
attachmentId: {
type: 'integer',
description: '上传的 Excel 附件 ID。系统直接从文件读取行数据无需也不要在参数里抄录数据。',
},
},
required: ['attachmentId'],
additionalProperties: false,
},
},
},
{
type: 'function' as const,
function: {
name: 'excel_analyze',
description:
'用 ExcelJS 直接解析上传的 Excel.xlsx/.csvoverview 查看工作表概览表名、行数、前几行样本rows 按工作表/行范围读取具体行。适合核对表头、抽查数据行、确认预检报告里的错误原因Word/PPT 请用 office_analyze。',
parameters: {
type: 'object',
properties: {
attachmentId: {
type: 'integer',
description: '上传的 Excel 附件 ID',
},
action: {
type: 'string',
description: 'overview 概览 / rows 读取行',
enum: ['overview', 'rows'],
},
sheet: {
type: 'string',
description: '工作表名称rows 时可选,默认第一个表)',
maxLength: 200,
},
startRow: {
type: 'integer',
description: '起始行(含表头,从 1 开始,默认 1',
minimum: 1,
},
maxRows: {
type: 'integer',
description: '读取行数(默认 20传大值可读取更多/全部行)',
minimum: 1,
},
maxColumns: {
type: 'integer',
description: '读取列数(默认 30传大值可读取更多/全部列)',
minimum: 1,
},
},
required: ['attachmentId', 'action'],
additionalProperties: false,
},
},
},
{
type: 'function' as const,
function: {
name: 'start_import_wizard',
description:
'生成一个“批量导入向导”显示给用户。当用户上传 Excel 并需要批量导入学生、宿舍、换宿或入住数据时调用;传入 attachmentId 与 stages业务类型 + 工作表名),系统直接解析文件、自动识别列映射并按依赖顺序分阶段预览,用户确认后才会入库。每个回答回合最多调用一次,生成成功后提示用户打开向导逐阶段确认,不要重复调用,也不要代替用户调用任何写工具直接插入。',
'生成一个“批量导入向导”显示给用户。当用户上传 Excel 并需要批量导入学生、宿舍、换宿或入住数据时调用;传入 attachmentId 与 stages业务类型 + 工作表名),并把用户确认的 mapping/organization/updateExisting/duplicatePolicy/skipUnmatched 一并传入。系统直接解析文件、应用确认策略并按依赖顺序分阶段预览,用户确认后才会入库。每个回答回合最多调用一次,生成成功后提示用户打开向导逐阶段确认,不要重复调用,也不要代替用户调用任何写工具直接插入。',
parameters: {
type: 'object',
properties: {
@@ -46,6 +106,34 @@ export const A2UI_TOOL_SCHEMAS = [
additionalProperties: false,
},
},
mapping: {
type: 'object',
description:
'列映射确认结果:阶段 stepKey -> { 字段名: 工作表表头 }(字段名如 students.name/rooms.roomNumber/checkins.checkInDate/transfers.newRoom。来自 preflight_import 报告的映射确认;未确认时省略,系统自动识别。',
additionalProperties: {
type: 'object',
description: '字段名 -> 工作表表头',
additionalProperties: { type: 'string', maxLength: 200 },
},
},
organization: {
type: 'string',
description: '确认后的校区名称(预检报告出现未知校区时由用户确认)',
maxLength: 100,
},
updateExisting: {
type: 'boolean',
description: '是否更新已匹配的现有记录;默认 truefalse 时已匹配行跳过',
},
duplicatePolicy: {
type: 'string',
description: '文件内重复行策略error 标记错误 / skip 跳过重复行;默认 error',
enum: ['error', 'skip'],
},
skipUnmatched: {
type: 'boolean',
description: '关系表(入住/换宿)找不到学生或宿舍时是否跳过该行;默认 false',
},
},
required: ['attachmentId', 'stages'],
additionalProperties: false,
@@ -214,13 +302,19 @@ export const SYSTEM_PROMPT = `你是恭学系统的业务助理。回答必须
当用户需要录入或修改业务数据时,先调用 render_form 生成确认表单,提示用户填写并提交;只有在用户通过表单提交确认后,才能执行写操作工具(如 create_student、update_students
新增学生示例render_form 的 fields 使用 name/phone/gender/studentNo。
修改学生示例:批量修改姓名/档案时render_form 的字段可用 name_<学生ID> 等命名展示待修改内容,用户提交后再调用 update_students每条更新必须带学生 id。
当用户上传 Excel 并需要批量导入(学生、宿舍、换宿、入住记录)时,先调用 start_import_wizard 生成“导入向导”:必须传入 attachmentId上传附件的 ID和 stages声明业务类型 stepKeystudents 学生 / rooms 宿舍 / checkins 入住 / transfers 换宿,以及对应工作表 sheet 名),系统直接从文件解析行数据,禁止把整表数据抄进工具参数或凭空补全;生成向导后提示用户打开,按“基础档案(学生/宿舍)→ 关系(入住/换宿)”的顺序逐阶段预览,人工确认后系统才会入库;不要代替用户调用任何写工具直接插入。每个回答回合最多调用一次 start_import_wizard。
当用户上传 Excel 并需要批量导入(学生、宿舍、换宿、入住记录)时,按以下固定流程执行:
1. 先调用 preflight_import传入 attachmentId生成“可插入性预检报告”报告给出分阶段行数新建/更新/错误/跳过)、阻断原因(缺列、未找到学生/宿舍、文件内重复、已有在住、格式错误、未知校区)、需要确认的问题和导入后的下一步建议。
2. 报告为 blocked 时,向用户说明阻断原因并建议修正文件后重传,不要生成向导;报告为 needs_input 时,按报告中的 questions 向用户确认:选项型问题用 render_form 生成表单(如更新策略、重复策略、校区、未匹配行处理),列映射类问题用聊天文本确认;报告为 ready 时可直接进入下一步,如需列映射确认也可先问。不要替用户默认做出影响数据的决定。
报告只给汇总统计时,可用 excel_analyze 读取报告 errorSamples 对应的工作表与行号,向用户解释具体错误原因(如某行缺少手机号、姓名带日期后缀、宿舍未建档等)。
3. 用户确认后调用 start_import_wizard必须传入 attachmentId 和 stages业务类型 stepKeystudents 学生 / rooms 宿舍 / checkins 入住 / transfers 换宿,以及对应工作表 sheet 名并把确认结果一并传入mapping 列映射、organization 校区、updateExisting 是否更新已有记录、duplicatePolicy 重复行策略 error/skip、skipUnmatched 是否跳过未匹配行)。系统直接从文件解析行数据,禁止把整表数据抄进工具参数或凭空补全。
4. 生成向导后提示用户打开,按“基础档案(学生/宿舍)→ 关系(入住/换宿)”的顺序逐阶段预览,人工确认后系统才会入库;不要代替用户调用任何写工具直接插入。
每个回答回合最多调用一次 preflight_import 和一次 start_import_wizard报告与导入完成后由你给出下一步建议不要自动执行后续写操作。
当用户需要可视化数据(趋势、占比、对比、多维、完成率等)时,调用 render_chart 生成图表卡片chartType 支持 line 折线/bar 柱状/pie 饼图/area 面积/scatter 散点/radar 雷达/gauge 仪表盘/funnel 漏斗columns+rows 表格数据)。
上传的 Office 附件Excel/Word/PPT)可用 office_analyze 查看结构stats/outline确认表名与表头;批量导入前如不确定列名,可用 get/query 只读少量单元格核对,不要读取整表。
上传的 Office 附件Excel.xlsx/.csv优先用 excel_analyze 查看概览overview或按行读取rows核对表头与数据Word/PPT 用 office_analyze 查看结构stats/outline批量导入前如不确定列名,可先预检preflight_import再用 excel_analyze 抽查具体行,不要读取整表。
业务工作流引导(重要):
- 系统业务按“基础档案 → 业务关系 → 运行数据 → 结算”组织。常见闭环:学生、宿舍、教室、组织等基础档案先行;再建立分班、入住、租赁等关系;之后才有考勤、费用等运行数据;最后生成账单、押金等结算。
- 执行任何写入或导入前,先判断该操作依赖的前置数据是否已存在(可用查询工具核实):入住依赖学生和宿舍,换宿依赖学生和宿舍,账单依赖入住记录和费用,考勤依赖班级和排课。前置缺失时,先向用户说明缺什么、建议先完成哪一步,再继续,不要机械地跳过依赖直接入库。
- 导入或录入完成后,主动给出下一步建议(例如:入住导入完成 → 建议录入本月公共费用 → 生成并确认账单;学生导入完成 → 建议分班或排课)。
- 用户上传 Excel 但未说明用途时,先根据表头判断包含哪些业务,向用户说明将导入什么、依赖什么,再生成预览卡;多业务分表合并到同一张预览卡,并按依赖顺序执行。
- 用户上传 Excel 但未说明用途时,先根据表头判断包含哪些业务,向用户说明将导入什么、依赖什么;疑似导入时先调用 preflight_import 生成预检报告,再按报告提问并生成导入向导,按依赖顺序执行。
- 只引导当前角色权限范围内可执行的下一步,不得建议或执行用户无权操作。
不得扩大用户权限或猜测不可见数据。回答使用简洁中文 Markdown。`;