src/main/java/cc/mrbird/febs/ai/controller/knowledge/AiKnowledgeController.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/cc/mrbird/febs/ai/quartz/KnowledgeJob.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java | ●●●●● patch | view | raw | blame | history | |
src/main/resources/templates/febs/views/modules/ai/knowledge/list.html | ●●●●● patch | view | raw | blame | history |
src/main/java/cc/mrbird/febs/ai/controller/knowledge/AiKnowledgeController.java
@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotNull; import java.util.Map; /** @@ -51,4 +52,22 @@ return aiKnowledgeFileService.update(dto); } @GetMapping("refresh/{id}") @ControllerEndpoint(operation = "刷新", exceptionMessage = "操作失败") public FebsResponse refresh( @NotNull(message = "{required}") @PathVariable String id ) { return aiKnowledgeFileService.refresh(id); } @GetMapping("delete/{id}") @ControllerEndpoint(operation = "删除", exceptionMessage = "操作失败") public FebsResponse delete( @NotNull(message = "{required}") @PathVariable String id ) { return aiKnowledgeFileService.delete(id); } } src/main/java/cc/mrbird/febs/ai/entity/AiKnowledgeFile.java
@@ -1,6 +1,7 @@ package cc.mrbird.febs.ai.entity; import cc.mrbird.febs.common.entity.AiBaseEntity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -8,7 +9,7 @@ @TableName("ai_knowledge_file") public class AiKnowledgeFile extends AiBaseEntity { /** * 状态:0-上传服务器,1-应用数据,2-知识库 * 状态:0-上传服务器,1-应用数据,2-知识库 3-成功 */ private Integer state; /** @@ -34,4 +35,7 @@ * 公司ID */ private String companyId; @TableField(exist = false) private String companyName; } src/main/java/cc/mrbird/febs/ai/quartz/KnowledgeJob.java
New file @@ -0,0 +1,24 @@ package cc.mrbird.febs.ai.quartz; import cc.mrbird.febs.ai.service.AiKnowledgeFileService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Slf4j @Component public class KnowledgeJob { @Autowired private AiKnowledgeFileService aiKnowledgeFileService; /** * 订单失效 * 五分钟运行一次 */ @Scheduled(cron = "0 0/5 * * * ? ") public void refresh() { aiKnowledgeFileService.refreshEvent(); } } src/main/java/cc/mrbird/febs/ai/service/AiKnowledgeFileService.java
@@ -14,4 +14,10 @@ FebsResponse update(AiKnowledgeFile dto); void getAddKnowledge(String id); FebsResponse refresh(String id); FebsResponse delete(String id); void refreshEvent(); } src/main/java/cc/mrbird/febs/ai/service/impl/AiKnowledgeFileServiceImpl.java
@@ -2,8 +2,6 @@ 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; @@ -26,10 +24,10 @@ 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.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; @Slf4j @@ -50,6 +48,25 @@ } query.orderByDesc(AiKnowledgeFile::getCompanyId); Page<AiKnowledgeFile> pages = aiKnowledgeFileMapper.selectPage(page, query); List<AiKnowledgeFile> records = pages.getRecords(); if (CollUtil.isNotEmpty(records)){ //stream流获取全部的公司ID Set<String> companyIds = records.stream().map(AiKnowledgeFile::getCompanyId).collect(Collectors.toSet()); List<String> companyIdList = new ArrayList<>(companyIds); List<AiCompany> listById = aiCompanyService.getListById((companyIdList)); Map<String, AiCompany> collect = listById.stream().collect(Collectors.toMap(AiCompany::getId, aiCompany -> aiCompany)); for (AiKnowledgeFile aiKnowledgeFile : records){ if (StrUtil.isEmpty(aiKnowledgeFile.getCompanyId())){ aiKnowledgeFile.setCompanyName("公共"); }else{ if (StrUtil.isEmpty(aiKnowledgeFile.getCompanyId())){ aiKnowledgeFile.setCompanyName("公共"); }else{ aiKnowledgeFile.setCompanyName(collect.get(aiKnowledgeFile.getCompanyId()).getName()); } } } } return pages; } @@ -60,6 +77,7 @@ entity.setId(UUID.getSimpleUUIDString()); entity.setCompanyId(dto.getCompanyId()); entity.setName(dto.getName()); entity.setState(1); entity.setSavePath(dto.getSavePath()); entity.setCreatedTime(new Date()); this.save(entity); @@ -108,4 +126,98 @@ ); } @Override public FebsResponse refresh(String id) { AiKnowledgeFile aiKnowledgeFile = this.getById(id); if (ObjectUtil.isNull(aiKnowledgeFile)){ throw new FebsException("文件不存在"); } Integer state = aiKnowledgeFile.getState(); if (state == 1){ state = KnowledgeBaseUtil.getFileJobStatus(aiKnowledgeFile.getFileId()); if ( state == 1) { aiKnowledgeFileMapper.update(null, Wrappers.lambdaUpdate(AiKnowledgeFile.class) .set(AiKnowledgeFile::getState, 2) .eq(AiKnowledgeFile::getId, aiKnowledgeFile.getId()) ); } } if (state == 2){ String knowledgeId = null; if (StrUtil.isNotEmpty(aiKnowledgeFile.getCompanyId())){ AiCompany aiCompany = aiCompanyService.getById(aiKnowledgeFile.getCompanyId()); if (StrUtil.isNotEmpty(aiCompany.getCategoryId())){ knowledgeId = aiCompany.getKnowledgeId(); } }else{ knowledgeId = KnowledgeBaseUtil.DEFAULT_KNOWLEDGE_ID; } state = KnowledgeBaseUtil.getIndexKnowledgeJobStatus(aiKnowledgeFile.getFileId(),knowledgeId); if ( state == 1) { aiKnowledgeFileMapper.update(null, Wrappers.lambdaUpdate(AiKnowledgeFile.class) .set(AiKnowledgeFile::getState, 3) .eq(AiKnowledgeFile::getId, aiKnowledgeFile.getId()) ); } } return new FebsResponse().success().message("操作成功"); } @Override public FebsResponse delete(String id) { //如何从服务器上的位置删除对应的文件 AiKnowledgeFile aiKnowledgeFile = this.getById(id); if (ObjectUtil.isNull(aiKnowledgeFile)){ throw new FebsException("文件不存在"); } //服务器删除 try { Path filePath = Paths.get(aiKnowledgeFile.getSavePath()); boolean deleted = Files.deleteIfExists(filePath); if (!deleted) { throw new FebsException("文件删除成功"); } } catch (Exception e) { throw new FebsException("删除文件时发生错误: " + e.getMessage()); } //知识库删除 String knowledgeId = null; if (StrUtil.isNotEmpty(aiKnowledgeFile.getCompanyId())){ AiCompany aiCompany = aiCompanyService.getById(aiKnowledgeFile.getCompanyId()); if (StrUtil.isNotEmpty(aiCompany.getCategoryId())){ knowledgeId = aiCompany.getKnowledgeId(); } }else{ knowledgeId = KnowledgeBaseUtil.DEFAULT_KNOWLEDGE_ID; } KnowledgeBaseUtil.knowledgeFileDelete(aiKnowledgeFile.getFileId(), knowledgeId); //应用数据删除 try { KnowledgeBaseUtil.deleteFile(aiKnowledgeFile.getFileId()); } catch (Exception e) { throw new FebsException("应用数据删除时发生错误: " + e.getMessage()); } return new FebsResponse().success().message("操作成功"); } @Override public void refreshEvent() { LambdaQueryWrapper<AiKnowledgeFile> queryWrapper = Wrappers.lambdaQuery(AiKnowledgeFile.class); queryWrapper.ne(AiKnowledgeFile::getState, 3); List<AiKnowledgeFile> list = aiKnowledgeFileMapper.selectList(queryWrapper); if (CollUtil.isEmpty( list)){ for (AiKnowledgeFile aiKnowledgeFile : list){ refresh(aiKnowledgeFile.getId()); } } } } src/main/java/cc/mrbird/febs/ai/util/KnowledgeBaseUtil.java
@@ -9,12 +9,10 @@ import java.io.FileInputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Paths; import java.security.MessageDigest; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; public class KnowledgeBaseUtil { public static final String ACCESS_KEY_ID = "LTAI5tCyQRwhZ2eimxCFKbdq"; @@ -61,6 +59,20 @@ // 调用添加分类API接口 AddCategoryResponse addCategoryResponse = client.addCategoryWithOptions(WORKSPACE_ID, addCategoryRequest, headers, runtime); return addCategoryResponse.getBody().getData().getCategoryId(); } /** * 永久删除应用数据中的指定文件 * @param fileId 文件 ID * @throws Exception 当API调用失败或其他异常情况时抛出 */ public static void deleteFile(String fileId) throws Exception { com.aliyun.bailian20231229.Client client = KnowledgeBaseUtil.createClient(); com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions(); java.util.Map<String, String> headers = new java.util.HashMap<>(); client.deleteFileWithOptions(fileId, WORKSPACE_ID, headers, runtime); return; } /** @@ -242,7 +254,7 @@ * @param indexId 知识库ID * @return 阿里云百炼服务的响应对象 */ public static GetIndexJobStatusResponse getIndexJobStatus(com.aliyun.bailian20231229.Client client, String workspaceId, String jobId, String indexId) throws Exception { public static GetIndexJobStatusResponse getFileJobStatus(com.aliyun.bailian20231229.Client client, String workspaceId, String jobId, String indexId) throws Exception { Map<String, String> headers = new HashMap<>(); com.aliyun.bailian20231229.models.GetIndexJobStatusRequest getIndexJobStatusRequest = new com.aliyun.bailian20231229.models.GetIndexJobStatusRequest(); getIndexJobStatusRequest.setIndexId(indexId); @@ -381,6 +393,61 @@ } } public static int getFileJobStatus(String fileId) { int state = 0; // 步骤1:初始化客户端(Client) System.out.println("步骤1:创建Client"); com.aliyun.bailian20231229.Client client = null; try { client = createClient(); // 步骤6:检查更新后的文件状态 System.out.println("步骤6:检查阿里云百炼中的文件状态"); DescribeFileResponse describeResponse = describeFile(client, WORKSPACE_ID, fileId); String status = describeResponse.getBody().getData().getStatus(); System.out.println("当前文件状态:" + status); if ("PARSE_SUCCESS".equals(status)) { state = 1; } } catch (Exception e) { e.printStackTrace(); } return state; } public static int getIndexKnowledgeJobStatus(String jobId, String indexId) { int state = 0; // 步骤1:初始化客户端(Client) System.out.println("步骤1:创建Client"); com.aliyun.bailian20231229.Client client = null; try { client = createClient(); System.out.println("步骤8:等待追加任务完成"); GetIndexJobStatusResponse jobStatusResponse = getFileJobStatus(client, WORKSPACE_ID, jobId, indexId); String status = jobStatusResponse.getBody().getData().getStatus(); System.out.println("当前索引任务状态:" + status); if ("COMPLETED".equals(status)) { state = 1; } } catch (Exception e) { e.printStackTrace(); } return state; } public static void knowledgeFileDelete(String fileId, String indexId) { com.aliyun.bailian20231229.Client client = null; try { System.out.println("步骤1:创建Client"); client = createClient(); // 步骤9:删除旧文件 System.out.println("步骤9:删除旧文件"); deleteIndexDocument(client, WORKSPACE_ID, indexId, fileId); } catch (Exception e) { e.printStackTrace(); } } /** * 向一个文档类知识库追加导入已解析的文件 * src/main/resources/templates/febs/views/modules/ai/knowledge/list.html
@@ -50,7 +50,8 @@ </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> <button class="layui-btn layui-btn-normal layui-btn-sm" type="button" shiro:hasPermission="knowledgeList:add" lay-event="aiKnowledgeFileRefreshEvent">刷新</button> <button class="layui-btn layui-btn-danger layui-btn-sm" type="button" shiro:hasPermission="knowledgeList:add" lay-event="aiKnowledgeFileDeleteEvent">删除</button> </script> @@ -102,7 +103,41 @@ }); } if (layEvent === 'aiKnowledgeFileRefreshEvent') { if (data.state == 3){ febs.alert.success('文件已成功解析'); return; } febs.modal.confirm('刷新', '确认刷新?', function () { aiKnowledgeFileRefreshEvent(data.id); }); } if (layEvent === 'aiKnowledgeFileDeleteEvent') { if (data.state != 3){ febs.alert.error('文件解析中,不能中断操作'); return; } febs.modal.confirm('删除', '确认删除?', function () { aiKnowledgeFileDeleteEvent(data.id); }); } }); function aiKnowledgeFileDeleteEvent(id) { febs.get(ctx + 'admin/aiKnowledgeFile/delete/' + id, null, function (data) { febs.alert.success(data.message); $query.click(); }); } function aiKnowledgeFileRefreshEvent(id) { febs.get(ctx + 'admin/aiKnowledgeFile/refresh/' + id, null, function (data) { febs.alert.success(data.message); $query.click(); }); } // 初始化表格操作栏各个按钮功能 table.on('toolbar(aiKnowledgeFileTable)', function (obj) { @@ -134,7 +169,13 @@ {type: 'numbers', title: '', width: 80}, {title: '操作', toolbar: '#aiKnowledgeFileOption', minWidth: 200, align: 'center'}, {field: 'id', title: 'ID', minWidth: 100,align:'center'}, {field: 'companyName', title: '公司', minWidth: 100,align:'center'}, {field: 'name', title: '名称', minWidth: 100,align:'center'}, {field: 'state', title: '状态', minWidth: 100,align:'center', templet: function(d) { var stateMap = {'0': '上传服务器', '1': '应用数据应用中', '2': '知识库应用中', '3': '成功'}; return stateMap[d.state] || '未知'; }} ]] }); }