feat: 支持FAQ Excel导入导出并优化管理端交互

- 新增 FAQ Excel 导入、导出、模板下载接口及导入结果 VO,支持按分类路径+问题 upsert 与逐行容错

- 模板增加填写说明与更宽列宽,导出列与模板保持一致

- 管理端新增导入弹窗与结果展示,FAQ 列表操作栏精简为"添加 + 更多操作"并去除多余外层框

- 修复导出前校验顺序,避免非 FAQ 知识库触发默认分类写入
This commit is contained in:
2026-03-03 17:18:49 +08:00
parent 80409259c3
commit 30e1145ee7
9 changed files with 1195 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
@@ -16,6 +17,7 @@ import tech.easyflow.ai.entity.FaqItem;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.FaqCategoryService;
import tech.easyflow.ai.service.FaqItemService;
import tech.easyflow.ai.vo.FaqImportResultVo;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.filestorage.FileStorageService;
@@ -27,7 +29,10 @@ import tech.easyflow.common.web.jsonbody.JsonBody;
import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -173,6 +178,46 @@ public class FaqItemController extends BaseCurdController<FaqItemService, FaqIte
return Result.ok(resVo);
}
@PostMapping(value = "importExcel", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@SaCheckPermission("/api/v1/documentCollection/save")
public Result<FaqImportResultVo> importExcel(MultipartFile file, BigInteger collectionId) {
return Result.ok(service.importFromExcel(collectionId, file));
}
@GetMapping("downloadImportTemplate")
@SaCheckPermission("/api/v1/documentCollection/query")
public void downloadImportTemplate(BigInteger collectionId, HttpServletResponse response) throws Exception {
if (collectionId == null) {
throw new BusinessException("知识库ID不能为空");
}
DocumentCollection collection = documentCollectionService.getById(collectionId);
if (collection == null) {
throw new BusinessException("知识库不存在");
}
if (!collection.isFaqCollection()) {
throw new BusinessException("当前知识库不是FAQ类型");
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("faq_import_template", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
service.writeImportTemplate(response.getOutputStream());
}
@GetMapping("exportExcel")
@SaCheckPermission("/api/v1/documentCollection/query")
public void exportExcel(BigInteger collectionId, HttpServletResponse response) throws Exception {
if (collectionId == null) {
throw new BusinessException("知识库ID不能为空");
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String fileName = URLEncoder.encode("faq_export_" + timestamp, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
service.exportToExcel(collectionId, response.getOutputStream());
}
@Override
protected String getDefaultOrderBy() {
return "order_no asc";