From 65d83e255e4e1bdfb0336bea920d6c884c510711 Mon Sep 17 00:00:00 2001 From: Administrator <15274802129@163.com> Date: Tue, 30 Sep 2025 09:43:55 +0800 Subject: [PATCH] feat(ai): 新增知识库文件管理功能 --- src/main/resources/templates/febs/views/modules/ai/knowledge/add.html | 165 +++++++++++ src/main/resources/templates/febs/views/modules/ai/knowledge/list.html | 162 +++++++++++ src/main/java/cc/mrbird/febs/common/utils/FileUtil.java | 10 src/main/java/cc/mrbird/febs/mall/controller/goods/AdminMallGoodsController.java | 22 + src/main/resources/application-test.yml | 3 src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java | 8 src/main/java/cc/mrbird/febs/ai/service/impl/AiCompanyServiceImpl.java | 8 src/main/resources/templates/febs/views/modules/ai/knowledge/info.html | 98 +++++++ src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java | 8 src/main/java/cc/mrbird/febs/ai/controller/knowledge/AiKnowledgeController.java | 54 +++ src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java | 80 +++++ src/main/java/cc/mrbird/febs/ai/controller/knowledge/ViewController.java | 49 +++ src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java | 167 ++--------- 13 files changed, 692 insertions(+), 142 deletions(-) diff --git a/src/main/java/cc/mrbird/febs/ai/controller/knowledge/AiKnowledgeController.java b/src/main/java/cc/mrbird/febs/ai/controller/knowledge/AiKnowledgeController.java new file mode 100644 index 0000000..6a41772 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/controller/knowledge/AiKnowledgeController.java @@ -0,0 +1,54 @@ +package cc.mrbird.febs.ai.controller.knowledge; + +import cc.mrbird.febs.ai.entity.AiCompany; +import cc.mrbird.febs.ai.entity.AiKnowledgeFile; +import cc.mrbird.febs.ai.service.AiCompanyService; +import cc.mrbird.febs.ai.service.AiKnowledgeFileService; +import cc.mrbird.febs.common.annotation.ControllerEndpoint; +import cc.mrbird.febs.common.controller.BaseController; +import cc.mrbird.febs.common.entity.FebsResponse; +import cc.mrbird.febs.common.entity.QueryRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Map; + +/** + * @author Administrator + */ +@Slf4j +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/admin/aiKnowledgeFile") +public class AiKnowledgeController extends BaseController { + + private final AiKnowledgeFileService aiKnowledgeFileService; + + @GetMapping("list") + public FebsResponse list(AiKnowledgeFile dto, QueryRequest request) { + String companyId = getCurrentUserCompanyId(); + dto.setCompanyId(companyId); + Map<String, Object> data = getDataTable(aiKnowledgeFileService.listInPage(dto, request)); + return new FebsResponse().success().data(data); + } + + @PostMapping("add") + @ControllerEndpoint(operation = "新增", exceptionMessage = "操作失败") + public FebsResponse add(@RequestBody @Valid AiKnowledgeFile dto) { + + String companyId = getCurrentUserCompanyId(); + dto.setCompanyId(companyId); + return aiKnowledgeFileService.add(dto); + } + + @PostMapping("update") + @ControllerEndpoint(operation = "更新", exceptionMessage = "操作失败") + public FebsResponse update(@RequestBody @Valid AiKnowledgeFile dto) { + + return aiKnowledgeFileService.update(dto); + } +} diff --git a/src/main/java/cc/mrbird/febs/ai/controller/knowledge/ViewController.java b/src/main/java/cc/mrbird/febs/ai/controller/knowledge/ViewController.java new file mode 100644 index 0000000..5101055 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/controller/knowledge/ViewController.java @@ -0,0 +1,49 @@ +package cc.mrbird.febs.ai.controller.knowledge; + +import cc.mrbird.febs.ai.entity.AiCompany; +import cc.mrbird.febs.ai.entity.AiKnowledgeFile; +import cc.mrbird.febs.ai.service.AiCompanyService; +import cc.mrbird.febs.ai.service.AiKnowledgeFileService; +import cc.mrbird.febs.common.entity.FebsConstant; +import cc.mrbird.febs.common.utils.FebsUtil; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Administrator + */ +@Controller("knowledge") +@RequestMapping(FebsConstant.VIEW_PREFIX + "modules/ai/knowledge") +@RequiredArgsConstructor +public class ViewController { + + private final AiKnowledgeFileService aiKnowledgeFileService; + + @GetMapping("list") + @RequiresPermissions("knowledgeList:view") + public String list() { + + return FebsUtil.view("modules/ai/knowledge/list"); + } + + @GetMapping(value = "/add") + @RequiresPermissions("knowledgeList:add") + public String add() { + + return FebsUtil.view("modules/ai/knowledge/add"); + } + + @GetMapping("info/{id}") + @RequiresPermissions("knowledgeList:info") + public String info(@PathVariable String id, Model model) { + AiKnowledgeFile entity = aiKnowledgeFileService.getById(id); + model.addAttribute("aiKnowledgeFile", entity); + return FebsUtil.view("modules/ai/knowledge/info"); + } + +} diff --git a/src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java b/src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java index ff2757b..3ba3990 100644 --- a/src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java +++ b/src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java @@ -8,6 +8,14 @@ @TableName("ai_knowledge_file") public class AiKnowledgeFile extends AiBaseEntity { /** + * 状态:0-上传服务器,1-应用数据,2-知识库 + */ + private Integer state; + /** + * 文件ID + */ + private String jobId; + /** * 文件ID */ private String fileId; diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java b/src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java index f4e7230..d501086 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java +++ b/src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java @@ -1,7 +1,15 @@ package cc.mrbird.febs.ai.service; import cc.mrbird.febs.ai.entity.AiKnowledgeFile; +import cc.mrbird.febs.common.entity.FebsResponse; +import cc.mrbird.febs.common.entity.QueryRequest; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; public interface AiKnowledgeFileService extends IService<AiKnowledgeFile> { + IPage<AiKnowledgeFile> listInPage(AiKnowledgeFile dto, QueryRequest request); + + FebsResponse add(AiKnowledgeFile dto); + + FebsResponse update(AiKnowledgeFile dto); } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiCompanyServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiCompanyServiceImpl.java index 37f0506..1bae742 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiCompanyServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiCompanyServiceImpl.java @@ -78,15 +78,9 @@ String knowledgeId = ""; try { - Client client = KnowledgeBaseUtil.createClient(); - String workspaceId = KnowledgeBaseUtil.WORKSPACE_ID; String name = entity.getName(); - String sourceType = "DATA_CENTER_FILE"; - String structureType = "unstructured"; - String sinkType = "DEFAULT"; String fileId = "file_b7d0a5b2df1745b9bfb7402f5fe1054f_12629554"; - CreateIndexResponse indexResponse = KnowledgeBaseUtil.createIndex(client, workspaceId, fileId, name, structureType, sourceType, sinkType); - knowledgeId = indexResponse.getBody().getData().getId(); + knowledgeId = KnowledgeBaseUtil.createKnowledgeBase(fileId, name); } catch (Exception e) { throw new FebsException("初始化知识库失败"); } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java index afc19e3..ba6b536 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java @@ -1,12 +1,35 @@ package cc.mrbird.febs.ai.service.impl; +import cc.mrbird.febs.ai.entity.AiCompany; import cc.mrbird.febs.ai.entity.AiKnowledgeFile; +import cc.mrbird.febs.ai.entity.AiProduct; +import cc.mrbird.febs.ai.entity.AiProductCategory; import cc.mrbird.febs.ai.mapper.AiKnowledgeFileMapper; +import cc.mrbird.febs.ai.service.AiCompanyService; import cc.mrbird.febs.ai.service.AiKnowledgeFileService; +import cc.mrbird.febs.ai.util.KnowledgeBaseUtil; +import cc.mrbird.febs.ai.util.UUID; +import cc.mrbird.febs.common.entity.FebsResponse; +import cc.mrbird.febs.common.entity.QueryRequest; +import cc.mrbird.febs.common.exception.FebsException; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; @Slf4j @Service @@ -14,4 +37,61 @@ public class AiKnowledgeFileServiceImpl extends ServiceImpl<AiKnowledgeFileMapper, AiKnowledgeFile> implements AiKnowledgeFileService { private final AiKnowledgeFileMapper aiKnowledgeFileMapper; + private final AiCompanyService aiCompanyService; + + @Override + public IPage<AiKnowledgeFile> listInPage(AiKnowledgeFile dto, QueryRequest request) { + Page<AiKnowledgeFile> page = new Page<>(request.getPageNum(), request.getPageSize()); + LambdaQueryWrapper<AiKnowledgeFile> query = Wrappers.lambdaQuery(AiKnowledgeFile.class); + if (StrUtil.isNotEmpty(dto.getCompanyId())){ + query.eq(AiKnowledgeFile::getCompanyId, dto.getCompanyId()); + } + query.orderByDesc(AiKnowledgeFile::getCompanyId); + Page<AiKnowledgeFile> pages = aiKnowledgeFileMapper.selectPage(page, query); + return pages; + } + + @Override + @Transactional + public FebsResponse add(AiKnowledgeFile dto) { + AiKnowledgeFile entity = new AiKnowledgeFile(); + entity.setId(UUID.getSimpleUUIDString()); + entity.setCompanyId(dto.getCompanyId()); + entity.setName(dto.getName()); + entity.setSavePath(dto.getSavePath()); + entity.setCreatedTime(new Date()); + this.save(entity); + + String categoryId = null; + String knowledgeId = null; + if (StrUtil.isNotEmpty(entity.getCompanyId())){ + AiCompany aiCompany = aiCompanyService.getById(entity.getCompanyId()); + if (StrUtil.isNotEmpty(aiCompany.getCategoryId())){ + categoryId = aiCompany.getCategoryId(); + knowledgeId = aiCompany.getKnowledgeId(); + } + }else{ + categoryId = KnowledgeBaseUtil.DEFAULT_CATEGORY_ID; + knowledgeId = KnowledgeBaseUtil.DEFAULT_KNOWLEDGE_ID; + } + + String fileId = KnowledgeBaseUtil.uploadFileToAppData(entity.getSavePath(), categoryId); + if (StrUtil.isBlank(fileId)){ + throw new FebsException("初始化应用数据失败"); + } + + String jobId = KnowledgeBaseUtil.updateKnowledgeBase(fileId, knowledgeId, null); + aiKnowledgeFileMapper.update(null, + Wrappers.lambdaUpdate(AiKnowledgeFile.class) + .set(AiKnowledgeFile::getFileId, fileId) + .set(AiKnowledgeFile::getJobId, jobId) + .eq(AiKnowledgeFile::getId, entity.getId()) + ); + return new FebsResponse().success().message("操作成功"); + } + + @Override + public FebsResponse update(AiKnowledgeFile dto) { + return null; + } } diff --git a/src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java b/src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java index 076ce8c..9811090 100644 --- a/src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java +++ b/src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java @@ -20,6 +20,8 @@ public static final String ACCESS_KEY_ID = "LTAI5tCyQRwhZ2eimxCFKbdq"; public static final String ACCESS_KEY_SECRET = "fs1mEwLXg2j9XuKJsFoW8ThQbJFqHl"; public static final String WORKSPACE_ID = "llm-4bcr09yfxlgz0b0t"; + public static final String DEFAULT_CATEGORY_ID = "cate_e6a47dd4d7cd4062a452eddccac76ad6_12629554"; + public static final String DEFAULT_KNOWLEDGE_ID = "9rxd16p56z"; public static final String ENDPOINT = "bailian.cn-beijing.aliyuncs.com"; /** @@ -270,22 +272,18 @@ } /** - * 使用阿里云百炼服务创建知识库。 + * 使用阿里云百炼服务上传应用数据。 * * @param filePath 文件本地路径 - * @param workspaceId 业务空间ID - * @param name 知识库名称 + * @param categoryId 应用数据目录ID * @return 如果成功,返回知识库ID;否则返回 null */ - public static String createKnowledgeBase(String filePath, String workspaceId, String name,String categoryId) { + public static String uploadFileToAppData(String filePath, String categoryId) { // 设置默认值 if (StrUtil.isBlank(categoryId)){ - categoryId = "default"; + return null; } String parser = "DASHSCOPE_DOCMIND"; - String sourceType = "DATA_CENTER_FILE"; - String structureType = "unstructured"; - String sinkType = "DEFAULT"; try { // 步骤1:初始化客户端(Client) System.out.println("步骤1:初始化Client"); @@ -299,7 +297,7 @@ // 步骤3:申请上传租约 System.out.println("步骤3:向阿里云百炼申请上传租约"); - ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize, workspaceId); + ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize, WORKSPACE_ID); String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId(); String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl(); Object uploadHeaders = leaseResponse.getBody().getData().getParam().getHeaders(); @@ -314,53 +312,36 @@ // 步骤5:将文件添加到服务器 System.out.println("步骤5:将文件添加到阿里云百炼服务器"); - AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, workspaceId); - String fileId = addResponse.getBody().getData().getFileId(); + AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, WORKSPACE_ID); - // 步骤6:检查文件状态 - System.out.println("步骤6:检查阿里云百炼中的文件状态"); - while (true) { - DescribeFileResponse describeResponse = describeFile(client, workspaceId, fileId); - String status = describeResponse.getBody().getData().getStatus(); - System.out.println("当前文件状态:" + status); + return addResponse.getBody().getData().getFileId(); + } catch (Exception e) { + System.out.println("发生错误:" + e.getMessage()); + e.printStackTrace(); + return null; + } + } - if (status.equals("INIT")) { - System.out.println("文件待解析,请稍候..."); - } else if (status.equals("PARSING")) { - System.out.println("文件解析中,请稍候..."); - } else if (status.equals("PARSE_SUCCESS")) { - System.out.println("文件解析完成!"); - break; - } else { - System.out.println("未知的文件状态:" + status + ",请联系技术支持。"); - return null; - } - TimeUnit.SECONDS.sleep(5); - } + /** + * 使用阿里云百炼服务创建知识库。 + * + * @param fileId 应用数据ID + * @param name 知识库名称 + * @return 如果成功,返回知识库ID;否则返回 null + */ + public static String createKnowledgeBase(String fileId, String name) { + String sourceType = "DATA_CENTER_FILE"; + String structureType = "unstructured"; + String sinkType = "DEFAULT"; + try { + // 步骤1:初始化客户端(Client) + System.out.println("步骤1:初始化Client"); + com.aliyun.bailian20231229.Client client = KnowledgeBaseUtil.createClient(); // 步骤7:初始化知识库 System.out.println("步骤7:在阿里云百炼中创建知识库"); - CreateIndexResponse indexResponse = createIndex(client, workspaceId, fileId, name, structureType, sourceType, sinkType); + CreateIndexResponse indexResponse = createIndex(client, WORKSPACE_ID, fileId, name, structureType, sourceType, sinkType); String indexId = indexResponse.getBody().getData().getId(); - - // 步骤8:提交索引任务 - System.out.println("步骤8:向阿里云百炼提交索引任务"); - SubmitIndexJobResponse submitResponse = submitIndex(client, workspaceId, indexId); - String jobId = submitResponse.getBody().getData().getId(); - - // 步骤9:获取索引任务状态 - System.out.println("步骤9:获取阿里云百炼索引任务状态"); - while (true) { - GetIndexJobStatusResponse getStatusResponse = getIndexJobStatus(client, workspaceId, jobId, indexId); - String status = getStatusResponse.getBody().getData().getStatus(); - System.out.println("当前索引任务状态:" + status); - - if (status.equals("COMPLETED")) { - break; - } - TimeUnit.SECONDS.sleep(5); - } - System.out.println("阿里云百炼知识库创建成功!"); return indexId; @@ -376,95 +357,24 @@ /** * 使用阿里云百炼服务更新知识库 * - * @param filePath 文件(更新后的)的实际本地路径 - * @param workspaceId 业务空间ID + * @param fileId 文件(更新后的)的实际本地路径 * @param indexId 需要更新的知识库ID * @param oldFileId 需要更新的文件的FileID * @return 如果成功,返回知识库ID;否则返回 null */ - public static String updateKnowledgeBase(String filePath, String workspaceId, String indexId, String oldFileId, String categoryId) { - // 设置默认值 - if (StrUtil.isBlank(categoryId)){ - categoryId = "default"; - } - String parser = "DASHSCOPE_DOCMIND"; + public static String updateKnowledgeBase(String fileId, String indexId, String oldFileId) { String sourceType = "DATA_CENTER_FILE"; try { // 步骤1:初始化客户端(Client) System.out.println("步骤1:创建Client"); com.aliyun.bailian20231229.Client client = createClient(); - // 步骤2:准备文件信息(更新后的文件) - System.out.println("步骤2:准备文件信息"); - String fileName = Paths.get(filePath).getFileName().toString(); - String fileMd5 = calculateMD5(filePath); - String fileSize = getFileSize(filePath); - - // 步骤3:申请上传租约 - System.out.println("步骤3:向阿里云百炼申请上传租约"); - ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize, workspaceId); - String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId(); - String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl(); - Object uploadHeaders = leaseResponse.getBody().getData().getParam().getHeaders(); - - // 步骤4:上传文件到临时存储 - System.out.println("步骤4:上传文件到临时存储"); - // 请自行安装jackson-databind - // 将上一步的uploadHeaders转换为Map(Key-Value形式) - ObjectMapper mapper = new ObjectMapper(); - Map<String, String> uploadHeadersMap = (Map<String, String>) mapper.readValue(mapper.writeValueAsString(uploadHeaders), Map.class); - uploadFile(uploadUrl, uploadHeadersMap, filePath); - - // 步骤5:添加文件到类目中 - System.out.println("步骤5:添加文件到类目中"); - AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, workspaceId); - String fileId = addResponse.getBody().getData().getFileId(); - - // 步骤6:检查更新后的文件状态 - System.out.println("步骤6:检查阿里云百炼中的文件状态"); - while (true) { - DescribeFileResponse describeResponse = describeFile(client, workspaceId, fileId); - String status = describeResponse.getBody().getData().getStatus(); - System.out.println("当前文件状态:" + status); - if ("INIT".equals(status)) { - System.out.println("文件待解析,请稍候..."); - } else if ("PARSING".equals(status)) { - System.out.println("文件解析中,请稍候..."); - } else if ("PARSE_SUCCESS".equals(status)) { - System.out.println("文件解析完成!"); - break; - } else { - System.out.println("未知的文件状态:" + status + ",请联系技术支持。"); - return null; - } - Thread.sleep(5000); - } - // 步骤7:提交追加文件任务 - System.out.println("步骤7:提交追加文件任务"); - SubmitIndexAddDocumentsJobResponse indexAddResponse = submitIndexAddDocumentsJob(client, workspaceId, indexId, fileId, sourceType); + System.out.println("步骤7:提交文件任务"); + SubmitIndexAddDocumentsJobResponse indexAddResponse = submitIndexAddDocumentsJob(client, WORKSPACE_ID, indexId, fileId, sourceType); String jobId = indexAddResponse.getBody().getData().getId(); - - // 步骤8:等待追加任务完成 - System.out.println("步骤8:等待追加任务完成"); - while (true) { - GetIndexJobStatusResponse jobStatusResponse = getIndexJobStatus(client, workspaceId, jobId, indexId); - String status = jobStatusResponse.getBody().getData().getStatus(); - System.out.println("当前索引任务状态:" + status); - if ("COMPLETED".equals(status)) { - break; - } - Thread.sleep(5000); - } - - // 步骤9:删除旧文件 - if(StrUtil.isNotBlank(oldFileId)){ - System.out.println("步骤9:删除旧文件"); - deleteIndexDocument(client, workspaceId, indexId, oldFileId); - } - System.out.println("阿里云百炼知识库更新成功!"); - return indexId; + return jobId; } catch (Exception e) { System.out.println("发生错误:" + e.getMessage()); return null; @@ -516,12 +426,11 @@ */ public static void main(String[] args) { - String filePath = "D:\\项目\\大模型\\阿里云百炼\\知识库\\薪资谈判常见100问与答.md"; + String filePath = "updateKnowledgeBase"; String indexId = "xlmj6e7ix1"; String oldFileId = "file_e943bb6d305a49a5acb7781ca00d70dd_12629554"; - String workspaceId = WORKSPACE_ID; - String result = updateKnowledgeBase(filePath, workspaceId, indexId, oldFileId,null); + String result = updateKnowledgeBase(filePath, indexId, oldFileId); if (result != null) { System.out.println("知识库更新成功,返回知识库ID: " + result); } else { diff --git a/src/main/java/cc/mrbird/febs/common/utils/FileUtil.java b/src/main/java/cc/mrbird/febs/common/utils/FileUtil.java index 2e3138d..cd14559 100644 --- a/src/main/java/cc/mrbird/febs/common/utils/FileUtil.java +++ b/src/main/java/cc/mrbird/febs/common/utils/FileUtil.java @@ -39,8 +39,6 @@ public static Map<String,Object> fileUploadEsc(MultipartFile file,String resourceUrl,String resourcePath) throws IOException { // 文件保存目录路径 String savePath = resourcePath; - // 文件保存目录URL - String saveUrl = resourceUrl; // 检查目录 File uploadDir = new File(savePath); if (!uploadDir.isDirectory()) { @@ -71,12 +69,12 @@ } } - String visitPath = (saveUrl + newFileName); + String visitPath = (savePath + fileName); Map<String,Object> map = new HashMap<String,Object>(); Map<String,Object> map2 = new HashMap<String,Object>(); - map2.put("src",visitPath);//图片url - map2.put("title",newFileName);//图片名称,这个会显示在输入框里 - map.put("code",0);//0表示成功,1失败 + map2.put("path",visitPath);//图片url + map2.put("title",fileName);//图片名称,这个会显示在输入框里 + map.put("code",200);//0表示成功,1失败 map.put("msg","上传成功");//提示消息 map.put("data",map2); return map; diff --git a/src/main/java/cc/mrbird/febs/mall/controller/goods/AdminMallGoodsController.java b/src/main/java/cc/mrbird/febs/mall/controller/goods/AdminMallGoodsController.java index 1c5d1c2..4b49aa4 100644 --- a/src/main/java/cc/mrbird/febs/mall/controller/goods/AdminMallGoodsController.java +++ b/src/main/java/cc/mrbird/febs/mall/controller/goods/AdminMallGoodsController.java @@ -6,6 +6,7 @@ import cc.mrbird.febs.common.entity.FebsResponse; import cc.mrbird.febs.common.entity.QueryRequest; import cc.mrbird.febs.common.enumerates.DataDictionaryEnum; +import cc.mrbird.febs.common.exception.FebsException; import cc.mrbird.febs.common.utils.FileUtil; import cc.mrbird.febs.common.utils.OssUtils; import cc.mrbird.febs.mall.dto.*; @@ -45,6 +46,27 @@ private final IAdminMallGoodsService adminMallGoodsService; private final DataDictionaryCustomMapper dataDictionaryCustomMapper; + // 文件保存目录URL + @Value("${static.resource.ai.common}") + private String commonPath; + // 文件保存目录URL + @Value("${static.resource.ai.company}") + private String companyPath; + @ControllerEndpoint(operation = "文件上传服务器", exceptionMessage = "上传失败") + @PostMapping(value = "/fileUploadEsc") + public Map<String,Object> fileUploadEsc(@RequestBody @Validated MultipartFile file) throws IOException { + if (file == null) { + throw new FebsException("上传文件不能为空"); + } + + String path = commonPath; + String companyId = getCurrentUserCompanyId(); + if (StrUtil.isNotEmpty(companyId)){ + path = companyPath + companyId; + } + return FileUtil.fileUploadEsc(file,null,path); + } + // /** // * 图片上传 // * @return diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 0d6fbb2..7daa8e4 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -85,6 +85,9 @@ static: resource: + ai: + common: /mnt/sdc/webresource/ai/common/ + company: /mnt/sdc/webresource/ai/company/ url: http://blnka.csxuncong.com/file/ path: /mnt/sdc/webresource/file/ diff --git a/src/main/resources/templates/febs/views/modules/ai/knowledge/add.html b/src/main/resources/templates/febs/views/modules/ai/knowledge/add.html new file mode 100644 index 0000000..df0bf9e --- /dev/null +++ b/src/main/resources/templates/febs/views/modules/ai/knowledge/add.html @@ -0,0 +1,165 @@ +<div class="layui-fluid layui-anim febs-anim" id="febs-aiKnowledgeFile-add" lay-title="新增"> + <div class="layui-row febs-container"> + <div class="layui-col-md12"> + <div class="layui-fluid" id="aiKnowledgeFile-add"> + <form class="layui-form" action="" lay-filter="aiKnowledgeFile-add-form"> + <div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief"> + <ul class="layui-tab-title"> + <li class="layui-this">基础信息</li> + </ul> + <div class="layui-tab-content"> + <div class="layui-tab-item layui-show"> + <div class="layui-row layui-col-space10 layui-form-item"> + <div class="layui-col-lg12"> + <label class="layui-form-label febs-form-item-require">文件上传:</label> + <div class="layui-input-block"> + <div class="layui-upload"> + <button type="button" class="layui-btn layui-btn-normal" id="test1">选择文件</button> + <div class="layui-upload-list"> + <input type="hidden" name="savePath" id="savePath"> + <input type="hidden" name="name" id="name"> + <div class="layui-progress layui-progress-big" lay-filter="demo" id="uploadProgress" style="display: none;"> + <div class="layui-progress-bar layui-bg-green" lay-percent="0%"></div> + </div> + <table class="layui-table"> + <thead> + <tr><th>文件名</th><th>大小</th><th>状态</th></tr> + </thead> + <tbody id="fileList"> + <!-- 上传文件列表将在这里显示 --> + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class="layui-form-item febs-hide"> + <button class="layui-btn" lay-submit="" lay-filter="aiKnowledgeFile-add-form-submit" id="submit">保存</button> + </div> + </form> + </div> + </div> + </div> +</div> + +<!-- 表格操作栏 end --> +<script data-th-inline="javascript"> + layui.use(['febs', 'form', 'formSelects', 'validate', 'treeSelect', 'eleTree','dropdown', 'laydate', 'layedit', 'upload', 'element', 'table', 'xmSelect','jquery'], function () { + var $ = layui.jquery, + febs = layui.febs, + layer = layui.layer, + table = layui.table, + formSelects = layui.formSelects, + treeSelect = layui.treeSelect, + form = layui.form, + laydate = layui.laydate, + eleTree = layui.eleTree, + $view = $('#aiKnowledgeFile-add'), + layedit = layui.layedit, + upload = layui.upload, + validate = layui.validate, + element = layui.element; + + form.render(); + element.render('progress'); + + // 文件上传配置 + var uploadInst = upload.render({ + elem: '#test1', + url: ctx + 'admin/goods/fileUploadEsc', // 上传接口 + accept: 'file', + size: 50 * 1024, // 50MB + exts: '', // 不限制文件类型 + auto: true, // 选择文件后自动上传 + before: function(obj) { + // 显示进度条 + $('#uploadProgress').show(); + // 预读本地文件示例,不支持ie8 + obj.preview(function(index, file, result) { + // 显示文件信息 + var fileSize = (file.size / 1024 / 1024).toFixed(2) + 'MB'; + var tr = $(['<tr id="upload-'+ index +'">', + '<td>'+ file.name +'</td>', + '<td>'+ fileSize +'</td>', + '<td><div class="layui-progress layui-progress-big" lay-filter="progress-'+ index +'">', + '<div class="layui-progress-bar layui-bg-green" lay-percent="0%"></div>', + '</div></td>', + '</tr>'].join('')); + + $('#fileList').html(tr); + element.render('progress'); + }); + }, + done: function(res, index, upload) { + if (res.code == 200) { // 上传成功 + // 保存文件信息到隐藏字段 + $('#savePath').val(res.data.path); + $('#name').val(res.data.title); + + // 更新进度条为100% + element.progress('progress-'+index, '100%'); + + // 显示上传成功状态 + $('#upload-'+index).find('td:eq(2)').html('<span style="color: #52c41a;">上传成功</span>'); + febs.alert.success('文件上传成功'); + + return false; + } else { + this.error(index, upload); + } + }, + error: function(index, upload) { + $('#upload-'+index).find('td:eq(2)').html('<span style="color: #ff5722;">上传失败</span>'); + febs.alert.warn('文件上传失败'); + }, + progress: function(n, elem, res, index) { + element.progress('progress-'+index, n + '%'); // n为上传进度 + element.progress('demo', n + '%'); // 总进度 + } + }); + + // 表单数据提交函数 + function submitFormData() { + var data = form.val('aiKnowledgeFile-add-form'); + + // 验证文件是否已上传 + if (!data.name) { + febs.alert.warn('请先上传文件'); + return; + } + + $.ajax({ + 'url':ctx + 'admin/aiKnowledgeFile/add', + 'type':'post', + 'dataType':'json', + 'headers' : {'Content-Type' : 'application/json;charset=utf-8'}, + 'traditional': true, + 'data':JSON.stringify(data), + 'success':function (res) { + if(res.code==200){ + layer.closeAll(); + febs.alert.success(res.message); + $('#febs-aiKnowledgeFile').find('#query').click(); + }else{ + febs.alert.warn(res.message); + } + }, + 'error':function () { + febs.alert.warn('服务器繁忙'); + } + }) + } + + // 修改保存按钮行为,点击时直接提交表单数据 + form.on('submit(aiKnowledgeFile-add-form-submit)', function () { + submitFormData(); + return false; + }); + + }); +</script> diff --git a/src/main/resources/templates/febs/views/modules/ai/knowledge/info.html b/src/main/resources/templates/febs/views/modules/ai/knowledge/info.html new file mode 100644 index 0000000..ae2f5e8 --- /dev/null +++ b/src/main/resources/templates/febs/views/modules/ai/knowledge/info.html @@ -0,0 +1,98 @@ +<div class="layui-fluid layui-anim febs-anim" id="febs-aiCompany-Info" lay-title="编辑"> + <div class="layui-row febs-container"> + <div class="layui-col-md12"> + <div class="layui-fluid" id="aiCompany-info"> + <form class="layui-form" action="" lay-filter="aiCompany-info-form"> + <div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief"> + <ul class="layui-tab-title"> + <li class="layui-this">基础信息</li> + </ul> + <div class="layui-tab-content"> + <input type="text" name="id" + placeholder="" autoComplete="off" class="layui-input febs-hide"> + <div class="layui-tab-item layui-show"> + <div class="layui-row layui-col-space10 layui-form-item"> + <div class="layui-col-lg6"> + <label class="layui-form-label febs-form-item-require">名称:</label> + <div class="layui-input-block"> + <input type="text" name="name" lay-verify="required" + placeholder="" autocomplete="off" class="layui-input"> + </div> + </div> + </div> + </div> + </div> + </div> + <div class="layui-form-item febs-hide"> + <button class="layui-btn" lay-submit="" lay-filter="aiCompany-info-form-submit" id="submit">保存</button> + </div> + </form> + </div> + </div> + </div> +</div> +<style> + .blue-border { + border-left-color: #2db7f5; + font-size: 18px; + } + .layui-table-cell { + height:auto; + } + .layui-upload-list { + margin: 0 !important; + } + .multi-images { + margin: 0 5px !important; + } +</style> +<!-- 表格操作栏 end --> +<script data-th-inline="javascript"> + layui.use(['febs', 'form', 'validate','formSelects', 'table', 'upload'], function () { + var $ = layui.jquery, + febs = layui.febs, + layer = layui.layer, + table = layui.table, + form = layui.form, + $view = $('#aiCompany-info'), + aiCompany = [[${aiCompany}]], + upload = layui.upload, + validate = layui.validate; + + form.render(); + + + initAiCompanyInfo(); + function initAiCompanyInfo() { + form.val("aiCompany-info-form", { + "id": aiCompany.id, + "name": aiCompany.name, + }); + } + + form.on('submit(aiCompany-info-form-submit)', function (data) { + $.ajax({ + 'url':ctx + 'admin/aiCompany/update', + 'type':'post', + 'dataType':'json', + 'headers' : {'Content-Type' : 'application/json;charset=utf-8'}, //接口json格式 + 'traditional': true,//ajax传递数组必须添加属性 + 'data':JSON.stringify(data.field), + 'success':function (data) { + if(data.code==200){ + layer.closeAll(); + febs.alert.success(data.message); + $('#febs-aiCompany').find('#query').click(); + }else{ + febs.alert.warn(data.message); + } + }, + 'error':function () { + febs.alert.warn('服务器繁忙'); + } + }) + return false; + }); + + }); +</script> \ No newline at end of file diff --git a/src/main/resources/templates/febs/views/modules/ai/knowledge/list.html b/src/main/resources/templates/febs/views/modules/ai/knowledge/list.html new file mode 100644 index 0000000..71aa3da --- /dev/null +++ b/src/main/resources/templates/febs/views/modules/ai/knowledge/list.html @@ -0,0 +1,162 @@ +<div class="layui-fluid layui-anim febs-anim" id="febs-aiKnowledgeFile" lay-title="知识库管理"> + <div class="layui-row febs-container"> + <div class="layui-col-md12"> + <div class="layui-card"> + <div class="layui-card-body febs-table-full"> + <form class="layui-form layui-table-form" lay-filter="aiKnowledgeFile-table-form"> + <div class="layui-row"> + <div class="layui-col-md10"> + <div class="layui-form-item"> + </div> + </div> + <div class="layui-col-md2 layui-col-sm12 layui-col-xs12 table-action-area"> + <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain table-action" id="query"> + <i class="layui-icon"></i> + </div> + <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain table-action" id="reset"> + <i class="layui-icon"></i> + </div> + </div> + </div> + </form> + <table lay-filter="aiKnowledgeFileTable" lay-data="{id: 'aiKnowledgeFileTable'}"></table> + + <style type="text/css"> + .layui-table-cell{ + text-align:center; + height: auto; + white-space: nowrap; /*文本不会换行,在同一行显示*/ + overflow: hidden; /*超出隐藏*/ + text-overflow: ellipsis; /*省略号显示*/ + } + .layui-table img{ + max-width:100px + } + ::-webkit-scrollbar { + height: 20px !important; + background-color: #f4f4f4; + } + </style> + </div> + </div> + </div> + </div> +</div> + +<script type="text/html" id="aiKnowledgeFileToolbar"> + <div class="layui-btn-container"> + <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="knowledgeList:add" lay-event="aiKnowledgeFileAdd">新增</button> + </div> +</script> + +<script type="text/html" id="aiKnowledgeFileOption"> + <button class="layui-btn layui-btn-normal layui-btn-sm" type="button" shiro:hasPermission="knowledgeList:info" lay-event="aiKnowledgeFileInfoEvent">编辑</button> +</script> + + +<style> + .layui-form-onswitch { + background-color: #5FB878 !important; + } +</style> +<!-- 表格操作栏 end --> +<script data-th-inline="none" type="text/javascript"> + // 引入组件并初始化 + layui.use([ 'jquery', 'form', 'table', 'febs'], function () { + var $ = layui.jquery, + febs = layui.febs, + form = layui.form, + table = layui.table, + $view = $('#febs-aiKnowledgeFile'), + $query = $view.find('#query'), + $reset = $view.find('#reset'), + $searchForm = $view.find('form'), + sortObject = {field: 'orderNum', type: null}, + tableIns; + + form.render(); + + // 表格初始化 + initaiKnowledgeFileTable(); + + // 初始化表格操作栏各个按钮功能 + table.on('tool(aiKnowledgeFileTable)', function (obj) { + console.log("触发事件:", obj.event); // 调试信息 + var data = obj.data, + layEvent = obj.event; + + if (layEvent === 'aiKnowledgeFileInfoEvent') { + if (data.state == 1){ + febs.alert.warn('请先禁用这行数据'); + return; + } + febs.modal.open('编辑','modules/ai/aiKnowledgeFile/info/' + data.id, { + btn: ['提交', '取消'], + area: ['100%', '100%'], + yes: function (index, layero) { + $('#febs-aiKnowledgeFile-Info').find('#submit').trigger('click'); + }, + btn2: function () { + layer.closeAll(); + } + }); + } + + }); + + // 初始化表格操作栏各个按钮功能 + table.on('toolbar(aiKnowledgeFileTable)', function (obj) { + let data = obj.data, + layEvent = obj.event; + if(layEvent === 'aiKnowledgeFileAdd'){ + febs.modal.open('新增', 'modules/ai/knowledge/add/', { + btn: ['提交', '取消'], + area:['100%','100%'], + yes: function (index, layero) { + $('#febs-aiKnowledgeFile-add').find('#submit').trigger('click'); + }, + btn2: function () { + layer.closeAll(); + } + }); + } + }); + + function initaiKnowledgeFileTable() { + tableIns = febs.table.init({ + elem: $view.find('table'), + id: 'aiKnowledgeFileTable', + url: ctx + 'admin/aiKnowledgeFile/list', + toolbar:"#aiKnowledgeFileToolbar", + defaultToolbar:[], + cols: [[ + {type: 'checkbox'}, + {type: 'numbers', title: '', width: 80}, + {title: '操作', toolbar: '#aiKnowledgeFileOption', minWidth: 200, align: 'center'}, + {field: 'id', title: 'ID', minWidth: 100,align:'center'}, + {field: 'name', title: '名称', minWidth: 100,align:'center'}, + ]] + }); + } + + + // 查询按钮 + $query.on('click', function () { + var params = $.extend(getQueryParams(), {field: sortObject.field, order: sortObject.type}); + tableIns.reload({where: params, page: {curr: 1}}); + }); + + // 刷新按钮 + $reset.on('click', function () { + $searchForm[0].reset(); + sortObject.type = 'null'; + tableIns.reload({where: getQueryParams(), page: {curr: 1}, initSort: sortObject}); + }); + // 获取查询参数 + function getQueryParams() { + return { + }; + } + + }) +</script> -- Gitblit v1.9.1