src/main/java/cc/mrbird/febs/ai/controller/productQuestion/AiProductQuestionController.java
@@ -153,4 +153,19 @@ return aiProductQuestionService.updateLabel(dto.getIds(), dto.getLabel()); } @GetMapping("exportNewProductQuestion") @ControllerEndpoint(operation = "模板导出", exceptionMessage = "操作失败") public FebsResponse exportNewProductQuestion(AiProductQuestion dto, HttpServletResponse response) throws IOException { aiProductQuestionService.exportNewProductQuestion(dto, response); return null; } @PostMapping(value = "/importNewProductQuestion") @ControllerEndpoint(operation = "模板导入", exceptionMessage = "操作失败") public FebsResponse importNewProductQuestion(@RequestBody MultipartFile file, @RequestParam String categoryId){ String companyId = getCurrentUserCompanyId(); return aiProductQuestionService.importNewProductQuestion(file, categoryId, companyId); } } src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java
@@ -60,4 +60,8 @@ FebsResponse importDeliver(MultipartFile file); FebsResponse updateLabel(String ids, String label); void exportNewProductQuestion(AiProductQuestion dto, HttpServletResponse response); FebsResponse importNewProductQuestion(MultipartFile file, String categoryId, String companyId); } src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java
@@ -654,4 +654,184 @@ return new FebsResponse().success().message("操作成功"); } @Override public void exportNewProductQuestion(AiProductQuestion dto, HttpServletResponse response) { try { List<ExcelSheetPO> res = new ArrayList<>(); ExcelSheetPO orderSheet = new ExcelSheetPO(); String title = "单选题目列表"; orderSheet.setSheetName(title); orderSheet.setTitle(title); String[] header = {"题目", "答案", "正确答案", "解析"}; orderSheet.setHeaders(header); QueryRequest request = new QueryRequest(); request.setPageNum(1); request.setPageSize(9999); AiProductQuestion aiProductQuestion = aiProductQuestionMapper.selectOne( Wrappers.lambdaQuery(AiProductQuestion.class) .orderByAsc(AiProductQuestion::getCreatedTime) .last("limit 1") ); List<AiProductQuestionItem> aiProductQuestionItems = aiProductQuestionItemService.getListByQuery( Wrappers.lambdaQuery(AiProductQuestionItem.class) .eq(AiProductQuestionItem::getProductQuestionId, aiProductQuestion.getId()) .orderByAsc(AiProductQuestionItem::getProductQuestionId) ); List<List<Object>> list = new ArrayList<>(); if (CollUtil.isNotEmpty(aiProductQuestionItems)) { String label = "(示例,新增请删除整行内容)"; for (AiProductQuestionItem item : aiProductQuestionItems) { List<Object> temp = new ArrayList<>(); temp.add(label + item.getTitle()); temp.add(item.getAnswer()); temp.add(item.getCorrectAnswer() == 1 ? "是" : "否"); temp.add(item.getAnswerAnalysis()); list.add(temp); } } orderSheet.setDataList(list); res.add(orderSheet); // 设置响应头 response = ResponseHeadUtil.setExcelHead(response); String fileName = title + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx"; response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); // 写入 Excel 文件 try (OutputStream os = response.getOutputStream()) { ExcelUtil.createWorkbookAtOutStream(ExcelVersion.V2007, res, os, true); } } catch (Exception e) { log.error("导出模板产品失败", e); throw new RuntimeException("导出失败,请稍后重试"); } } @Override public FebsResponse importNewProductQuestion(MultipartFile file, String categoryId, String companyId) { try { if (file.isEmpty()) { return new FebsResponse().fail(); } String fileName = file.getOriginalFilename(); String dirPath = "/home/javaweb/webresource/ai/import/"; File saveFile = new File(new File(dirPath).getAbsolutePath() + File.separator + fileName); if (!saveFile.exists()) { if (!saveFile.getParentFile().exists()) { saveFile.getParentFile().mkdirs(); } } file.transferTo(saveFile); List<ExcelSheetPO> data = ExcelUtil.readExcel(saveFile, null, null); if (CollUtil.isEmpty(data)) { return new FebsResponse().fail(); } List<List<Object>> dataList = data.get(0).getDataList(); // String[] header = {"题目", "答案", "正确答案", "解析"}; int titleIndex = -1; int answerIndex = -1; int correctAnswerIndex = -1; int answerAnalysisIndex = -1; // 用于存储当前处理的题目信息 AiProductQuestion currentQuestion = null; List<AiProductQuestionItem> currentItems = new ArrayList<>(); for (int i = 2; i < dataList.size(); i++) { List<Object> objects = dataList.get(i); String title = ""; String answer = ""; Integer correctAnswer = 0; String answerAnalysis = ""; for (int j = 0; j < objects.size(); j++) { Object obj = objects.get(j); if ("题目".equals(obj)) { titleIndex = j; } if ("答案".equals(obj)) { answerIndex = j; } if ("正确答案".equals(obj)) { correctAnswerIndex = j; } if ("解析".equals(obj)) { answerAnalysisIndex = j; } if (j == titleIndex && obj != null) { title = obj.toString(); } if (j == answerIndex && obj != null) { answer = obj.toString(); } if (j == correctAnswerIndex && obj != null) { correctAnswer = "是".equals(obj.toString()) ? 1 : 0; } if (j == answerAnalysisIndex && obj != null) { answerAnalysis = obj.toString(); } } if (StrUtil.isNotBlank(title)) { // 如果有新的题目,先保存之前的题目 if (currentQuestion != null && CollUtil.isNotEmpty(currentItems)) { saveQuestionWithItems(currentQuestion, currentItems); } // 创建新的题目 currentQuestion = new AiProductQuestion(); currentQuestion.setId(UUID.getSimpleUUIDString()); currentQuestion.setCompanyId("1"); // 假设默认公司ID currentQuestion.setProductCategoryId(categoryId); currentQuestion.setCompanyId(companyId); currentQuestion.setTitle(title); currentQuestion.setDifficulty(2); // 默认中等难度 currentQuestion.setState(1); // 默认启用 currentQuestion.setCreatedTime(new Date()); currentItems = new ArrayList<>(); } if (currentQuestion != null && StrUtil.isNotBlank(answer)) { // 添加答案选项 AiProductQuestionItem item = new AiProductQuestionItem(); item.setId(UUID.getSimpleUUIDString()); item.setProductQuestionId(currentQuestion.getId()); item.setCompanyId(companyId); // 假设默认公司ID item.setTitle(currentQuestion.getTitle()); item.setAnswer(answer); item.setCorrectAnswer(correctAnswer); item.setAnswerAnalysis(answerAnalysis); item.setCreatedTime(new Date()); currentItems.add(item); } } // 保存最后一个题目 if (currentQuestion != null && CollUtil.isNotEmpty(currentItems)) { saveQuestionWithItems(currentQuestion, currentItems); } return new FebsResponse().success(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("导入失败,请稍后重试"); } } private void saveQuestionWithItems(AiProductQuestion question, List<AiProductQuestionItem> items) { this.save(question); for (AiProductQuestionItem item : items) { aiProductQuestionItemService.getBaseMapper().insert(item); } } } src/main/resources/templates/febs/views/modules/ai/productQuestion/list.html
@@ -59,6 +59,8 @@ <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="productQuestionList:aiAdd" lay-event="productQuestionAddLabel">打标签</button> <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain" shiro:hasPermission="productQuestionList:aiAdd" lay-event="exportProductQuestion">导出(审核)</button> <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain" shiro:hasPermission="productQuestionList:aiAdd" id="importProductQuestion" lay-event="importProductQuestion">导入(审核)</button> <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain" shiro:hasPermission="productQuestionList:aiAdd" lay-event="exportNewProductQuestion">模板导出(新增)</button> <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain" shiro:hasPermission="productQuestionList:aiAdd" id="importNewProductQuestion" lay-event="importNewProductQuestion">模板导入(新增)</button> </div> </script> @@ -290,6 +292,162 @@ } }); } if (layEvent == 'exportNewProductQuestion') { window.location.href = ctx + "admin/productQuestion/exportNewProductQuestion"; } if (layEvent == 'importNewProductQuestion') { layui.use(['layer', 'upload', 'form', 'xmSelect'], function(){ var layer = layui.layer; var upload = layui.upload; var form = layui.form; // 在外层定义变量,使其在多个函数中可访问 var selectedCategoryId = ''; var selectedFile = null; // 使用 layer.open 替代 febs.modal.open layer.open({ title: '模板导入(新增)', type: 1, area: ['500px', '350px'], btn: ['开始导入', '取消'], content: '<div style="padding: 20px;">' + '<div class="layui-form-item">' + '<label class="layui-form-label">选择分类</label>' + '<div class="layui-input-block">' + '<div id="importCategorySelect"></div>' + '</div>' + '</div>' + '<div class="layui-form-item">' + '<label class="layui-form-label">上传文件</label>' + '<div class="layui-input-block">' + '<button type="button" class="layui-btn" id="importFileBtn">' + '<i class="layui-icon"></i>选择文件' + '</button>' + '<div id="fileInfo" style="margin-top: 10px; color: #666;"></div>' + '</div>' + '</div>' + '</div>', success: function(layero, index) { // 确保 xmSelect 已加载 if (typeof xmSelect === 'undefined') { layer.msg('xmSelect 组件未加载'); return; } // 初始化分类选择器 var importCategory = xmSelect.render({ el: '#importCategorySelect', language: 'zn', prop: { name: 'name', value: 'id', children: 'child' }, tips: '请选择分类', filterable: true, radio: true, clickClose: true, tree: { show: true, strict: false, }, data: [], on: function(data) { console.log('xmSelect 选择数据:', data); // 调试用 // 监听分类选择变化 if (data.arr && data.arr.length > 0) { // 尝试不同的属性名 selectedCategoryId = data.arr[0].value || data.arr[0].id || data.arr[0].val; console.log('选择的分类ID:', selectedCategoryId); // 调试用 } else { selectedCategoryId = ''; } } }); // 获取分类列表 $.ajax({ url: ctx + 'admin/productCategory/categoryTree', type: 'GET', success: function(res) { console.log('分类数据:', res); // 调试用 if (res.code === 200) { importCategory.update({ data: res.data }); } } }); // 初始化文件选择(不上传) $('#importFileBtn').click(function() { // 创建隐藏的文件输入 var fileInput = $('<input type="file" style="display:none">'); $('body').append(fileInput); fileInput.click(); fileInput.on('change', function() { var file = this.files[0]; if (file) { selectedFile = file; $('#fileInfo').html('<span style="color:#666">已选择文件: ' + file.name + '</span>'); } fileInput.remove(); }); }); }, yes: function(index, layero) { console.log('开始导入,selectedCategoryId:', selectedCategoryId); // 调试用 console.log('开始导入,selectedFile:', selectedFile); // 调试用 // 点击"开始导入"按钮 if (!selectedCategoryId) { layer.msg('请先选择分类', { icon: 2 }); return false; } if (!selectedFile) { layer.msg('请先选择文件', { icon: 2 }); return false; } // 创建 FormData var formData = new FormData(); formData.append('file', selectedFile); // 显示加载中 var loadingIndex = layer.load(1); // 使用 AJAX 上传文件 $.ajax({ url: ctx + 'admin/productQuestion/importNewProductQuestion?categoryId=' + selectedCategoryId, type: 'POST', data: formData, contentType: false, processData: false, success: function(res) { layer.close(loadingIndex); if (res.code === 200) { layer.msg('导入成功', { icon: 1 }); layer.close(index); // 刷新表格 if (typeof $query !== 'undefined') { $query.click(); } } else { layer.msg(res.msg || '导入失败', { icon: 2 }); } }, error: function() { layer.close(loadingIndex); layer.msg('上传失败', { icon: 2 }); } }); } }); }); } }); upload.render({