diff --git a/.gitignore b/.gitignore index 42eae94a..c121daee 100644 --- a/.gitignore +++ b/.gitignore @@ -202,4 +202,5 @@ cython_debug/ 江西城市生命线-可交互原型/frontend/node_modules/* THAI-Platform/* urbanLifelineWeb/packages/wechat_demo/* -urbanLifelineWeb/packages/workcase_wechat/unpackage/* \ No newline at end of file +urbanLifelineWeb/packages/workcase_wechat/unpackage/* +docs/AI训练资料 \ No newline at end of file diff --git a/difyPlugin/.vscode/launch.json b/difyPlugin/.vscode/launch.json new file mode 100644 index 00000000..3ef0a12c --- /dev/null +++ b/difyPlugin/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "DifyPlugin: FastAPI", + "type": "debugpy", + "request": "launch", + "program": "run.py", + "cwd": "${workspaceFolder}", + "python": "F:\\Environment\\conda\\envs\\difyPlugin\\python.exe", + "env": { + "PYTHONPATH": "${workspaceFolder}/difyPlugin" + }, + "jinja": true + } + ] +} \ No newline at end of file diff --git a/difyPlugin/.vscode/settings.json b/difyPlugin/.vscode/settings.json new file mode 100644 index 00000000..e69de29b diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql index 376e2842..3752bd0b 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql @@ -169,3 +169,20 @@ COMMENT ON COLUMN ai.tb_knowledge_file.update_time IS '更新时间'; COMMENT ON COLUMN ai.tb_knowledge_file.delete_time IS '删除时间'; COMMENT ON COLUMN ai.tb_knowledge_file.deleted IS '是否删除'; +DROP TABLE IF EXISTS ai.tb_knowledge_file_log CASCADE; +CREATE TABLE ai.tb_knowledge_file_log( + optsn VARCHAR(50) NOT NULL, -- 流水号 + log_id VARCHAR(50) NOT NULL, -- 日志ID + knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID + file_root_id VARCHAR(50) NOT NULL, -- 文件根ID + file_id VARCHAR(50) NOT NULL, -- 文件ID + file_name VARCHAR(100) NOT NULL, -- 文件名 + service VARCHAR(50) NOT NULL, -- 所属服务 workcase、bidding + version INTEGER NOT NULL DEFAULT 1, -- 文件版本 + action VARCHAR(50) NOT NULL, -- 操作类型 upload、update、delete + creator VARCHAR(50) NOT NULL, -- 创建者(用户ID) + creator_name VARCHAR(100) NOT NULL, -- 创建者姓名 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + PRIMARY KEY (optsn), + UNIQUE (knowledge_id, file_id) +); diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql index 82198517..825f42ba 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql @@ -30,7 +30,7 @@ DROP TABLE IF EXISTS workcase.tb_chat_room CASCADE; CREATE TABLE workcase.tb_chat_room( optsn VARCHAR(50) NOT NULL, -- 流水号 room_id VARCHAR(50) NOT NULL, -- 聊天室ID - workcase_id VARCHAR(50) DEFAULT NULL, -- 关联工单ID + workcase_id VARCHAR(50) DEFAULT NULL, -- 关联工单ID room_name VARCHAR(200) NOT NULL, -- 聊天室名称(如:工单#12345的客服支持) room_type VARCHAR(20) NOT NULL DEFAULT 'workcase', -- 聊天室类型:workcase-工单客服 status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active-活跃 closed-已关闭 archived-已归档 @@ -38,8 +38,10 @@ CREATE TABLE workcase.tb_chat_room( guest_name VARCHAR(100) NOT NULL, -- 来客姓名 ai_session_id VARCHAR(50) DEFAULT NULL, -- AI对话会话ID(从ai.tb_chat同步) message_count INTEGER NOT NULL DEFAULT 0, -- 消息总数 + device_code VARCHAR(50) NOT NULL, -- 设备代码 last_message_time TIMESTAMPTZ DEFAULT NULL, -- 最后消息时间 last_message TEXT DEFAULT NULL, -- 最后一条消息内容(用于列表展示) + comment_level INTEGER DEFAULT 0, -- 服务评分(1-5) closed_by VARCHAR(50) DEFAULT NULL, -- 关闭人 closed_time TIMESTAMPTZ DEFAULT NULL, -- 关闭时间 creator VARCHAR(50) NOT NULL, -- 创建人(系统自动创建) diff --git a/urbanLifelineServ/.vscode/launch.json b/urbanLifelineServ/.vscode/launch.json index 2fee27d0..0fc9038e 100644 --- a/urbanLifelineServ/.vscode/launch.json +++ b/urbanLifelineServ/.vscode/launch.json @@ -1,20 +1,6 @@ { "version": "0.2.0", "configurations": [ - { - "type": "java", - "name": "URLQRCodeParseTest", - "request": "launch", - "mainClass": "org.xyzh.workcase.test.URLQRCodeParseTest", - "projectName": "workcase" - }, - { - "type": "java", - "name": "QRCodeTest", - "request": "launch", - "mainClass": "org.xyzh.workcase.test.QRCodeTest", - "projectName": "workcase" - }, { "type": "java", "name": "AesEncryptUtil", diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/DifyApiClient.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/DifyApiClient.java index c0cb735f..53c84a69 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/DifyApiClient.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/DifyApiClient.java @@ -284,9 +284,8 @@ public class DifyApiClient { dataMap.put("process_rule", defaultProcessRule); } - // 默认设置文档形式和语言 - dataMap.put("doc_form", "text_model"); - dataMap.put("doc_language", "Chinese"); + // 只保留官方支持的参数 + // doc_form 和 doc_language 不是请求参数,移除 String dataJson = JSON.toJSONString(dataMap); logger.info("上传文档到知识库: datasetId={}, file={}, data={}", datasetId, originalFilename, dataJson); @@ -698,10 +697,10 @@ public class DifyApiClient { String lastId, Integer limit, String apiKey) { - + StringBuilder urlBuilder = new StringBuilder(difyConfig.getFullApiUrl("/conversations")); urlBuilder.append("?user=").append(userId); - + if (lastId != null && !lastId.isEmpty()) { urlBuilder.append("&last_id=").append(lastId); } @@ -718,7 +717,7 @@ public class DifyApiClient { try (Response response = httpClient.newCall(httpRequest).execute()) { String responseBody = response.body() != null ? response.body().string() : ""; - + if (!response.isSuccessful()) { logger.error("获取对话列表失败: {} - {}", response.code(), responseBody); throw new DifyException("获取对话列表失败: " + responseBody); @@ -732,6 +731,56 @@ public class DifyApiClient { } } + /** + * 获取对话变量 + * @param conversationId 会话ID + * @param userId 用户标识 + * @param lastId 当前页最后面一条记录的ID,默认null + * @param limit 一次请求返回多少条记录,默认20条,最大100条,最小1条 + * @param apiKey API密钥 + * @return 对话变量响应 + */ + public ConversationVariablesResponse getConversationVariables( + String conversationId, + String userId, + String lastId, + Integer limit, + String apiKey) { + + StringBuilder urlBuilder = new StringBuilder( + difyConfig.getFullApiUrl("/conversations/" + conversationId + "/variables")); + urlBuilder.append("?user=").append(userId); + + if (lastId != null && !lastId.isEmpty()) { + urlBuilder.append("&last_id=").append(lastId); + } + if (limit != null) { + urlBuilder.append("&limit=").append(limit); + } + + try { + Request httpRequest = new Request.Builder() + .url(urlBuilder.toString()) + .header("Authorization", "Bearer " + getApiKey(apiKey)) + .get() + .build(); + + try (Response response = httpClient.newCall(httpRequest).execute()) { + String responseBody = response.body() != null ? response.body().string() : ""; + + if (!response.isSuccessful()) { + logger.error("获取对话变量失败: {} - {}", response.code(), responseBody); + throw new DifyException("获取对话变量失败: " + responseBody); + } + + return JSON.parseObject(responseBody, ConversationVariablesResponse.class); + } + } catch (IOException e) { + logger.error("获取对话变量异常", e); + throw new DifyException("获取对话变量异常: " + e.getMessage(), e); + } + } + // ===================== 通用 HTTP 方法(用于代理转发)===================== /** diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/dto/ConversationVariablesResponse.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/dto/ConversationVariablesResponse.java new file mode 100644 index 00000000..292a8018 --- /dev/null +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/client/dto/ConversationVariablesResponse.java @@ -0,0 +1,69 @@ +package org.xyzh.ai.client.dto; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.Data; + +import java.util.List; + +/** + * @description 对话变量响应 + * @filename ConversationVariablesResponse.java + * @author AI Assistant + * @copyright xyzh + * @since 2025-12-29 + */ +@Data +public class ConversationVariablesResponse { + + private Integer limit; + + @JSONField(name = "has_more") + private Boolean hasMore; + + private List data; + + /** + * 对话中的变量项 + */ + @Data + public static class ConversationVariableItem { + + /** + * 变量ID + */ + private String id; + + /** + * 变量名称 + */ + private String name; + + /** + * 变量类型 (string, number, boolean 等) + */ + @JSONField(name = "value_type") + private String valueType; + + /** + * 变量值 + */ + private String value; + + /** + * 变量描述 + */ + private String description; + + /** + * 创建时间戳 + */ + @JSONField(name = "created_at") + private Long createdAt; + + /** + * 最后更新时间戳 + */ + @JSONField(name = "updated_at") + private Long updatedAt; + } +} diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/ChatController.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/ChatController.java index 8d66188a..a7d0c012 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/ChatController.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/ChatController.java @@ -8,6 +8,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import org.xyzh.ai.client.dto.ConversationVariablesResponse; import org.xyzh.api.ai.dto.ChatPrepareData; import org.xyzh.api.ai.dto.TbChat; import org.xyzh.api.ai.dto.TbChatMessage; @@ -46,6 +47,9 @@ public class ChatController { @Autowired private AIFileUploadService fileUploadService; + @Autowired + private org.xyzh.ai.client.DifyApiClient difyApiClient; + // ====================== 会话管理 ====================== /** @@ -151,7 +155,7 @@ public class ChatController { * @since 2025-12-17 */ @PostMapping("/conversation/page") - public ResultDomain> getChatPage(@RequestBody PageRequest pageRequest, @RequestHeader("Authorization") String token) { + public ResultDomain getChatPage(@RequestBody PageRequest pageRequest, @RequestHeader("Authorization") String token) { log.info("分页获取会话列表: agentId={}", pageRequest.getFilter().getAgentId()); pageRequest.getFilter().setUserType(false); @@ -164,6 +168,53 @@ public class ChatController { return chatService.getChatPage(pageRequest); } + /** + * @description 获取对话变量 + * @param params 请求参数(包含agentId, conversationId, userId, lastId, limit) + * @author yslg + * @since 2025-12-29 + */ + @PostMapping("/conversation/variables") + public ResultDomain getConversationVariables( + @RequestBody Map params, + @RequestHeader("Authorization") String token) { + + // 参数验证 + ValidationResult result = ValidationUtils.validateMap(params, Arrays.asList( + ValidationUtils.requiredString("agentId", "智能体ID", 1, 100), + ValidationUtils.requiredString("conversationId", "会话ID", 1, 100), + ValidationUtils.requiredString("userId", "用户ID", 1, 100) + )); + if (!result.isValid()) { + return ResultDomain.failure(result.getAllErrors()); + } + + String agentId = (String) params.get("agentId"); + String conversationId = (String) params.get("conversationId"); + String userId = (String) params.get("userId"); + String lastId = params.containsKey("lastId") ? (String) params.get("lastId") : null; + Integer limit = params.containsKey("limit") ? + Integer.parseInt(params.get("limit").toString()) : 20; + + log.info("获取对话变量: agentId={}, conversationId={}, userId={}", agentId, conversationId, userId); + + try { + // 获取智能体信息以获取 API Key + // 这里需要根据 agentId 获取对应的 API Key + // 暂时先使用一个占位符,实际使用时需要从数据库或配置中获取 + // 或者通过 chatService 获取智能体配置 + + // 调用 Dify API 获取会话变量 + ConversationVariablesResponse response = + difyApiClient.getConversationVariables(conversationId, userId, lastId, limit, agentId); + + return ResultDomain.success("获取对话变量成功",response); + } catch (Exception e) { + log.error("获取对话变量失败", e); + return ResultDomain.failure("获取对话变量失败: " + e.getMessage()); + } + } + // ====================== 消息管理 ====================== /** diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java index a65ebb79..509a4a36 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java @@ -7,7 +7,6 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -16,9 +15,11 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.xyzh.api.ai.service.DifyProxyService; +import org.xyzh.api.ai.service.KnowledgeFileLogService; import org.xyzh.api.ai.service.KnowledgeService; import org.xyzh.api.ai.dto.TbKnowledge; import org.xyzh.api.ai.dto.TbKnowledgeFile; +import org.xyzh.api.ai.dto.TbKnowledgeFileLog; import org.xyzh.api.ai.vo.KnowledgeFileVO; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.page.PageRequest; @@ -46,6 +47,9 @@ public class KnowledgeController { @Autowired private KnowledgeService knowledgeService; + @Autowired + private KnowledgeFileLogService knowledgeFileLogService; + @Autowired private DifyProxyService difyProxyService; @@ -235,10 +239,10 @@ public class KnowledgeController { * @since 2025-12-18 */ @PreAuthorize("hasAuthority('ai:knowledge:file:delete')") - @DeleteMapping("/file/{fileId}") - public ResultDomain deleteFile(@PathVariable("fileId") @NotBlank String fileId) { - logger.info("删除知识库文件: fileId={}", fileId); - return knowledgeService.deleteKnowledgeFileById(fileId); + @DeleteMapping("/file/{fileRootId}") + public ResultDomain deleteFile(@PathVariable("fileRootId") @NotBlank String fileRootId) { + logger.info("删除知识库文件: fileId={}", fileRootId); + return knowledgeService.deleteKnowledgeFileById(fileRootId); } /** @@ -347,4 +351,29 @@ public class KnowledgeController { logger.info("更新文档状态: datasetId={}, action={}", datasetId, action); return difyProxyService.updateDocumentStatus(datasetId, action, requestBody); } + + // ================================ 知识库文件操作日志 ======================= + /** + * @description 查询知识库操作日志列表 + * @param fileLog + * @author yslg + * @since 2025-12-18 + */ + @PreAuthorize("hasAuthority('ai:knowledge:file:view')") + @PostMapping("/datasets/log/list") + public ResultDomain getKnowledgeFileLogList(@RequestBody TbKnowledgeFileLog fileLog){ + return knowledgeFileLogService.getKnowledgeFileLogList(fileLog); + } + + /** + * @description 查询知识库操作日志分页 + * @param pageRequest + * @author yslg + * @since 2025-12-18 + */ + @PreAuthorize("hasAuthority('ai:knowledge:file:view')") + @PostMapping("/datasets/log/page") + public ResultDomain getKnowledgeFileLogPage(@RequestBody PageRequest pageRequest){ + return knowledgeFileLogService.getKnowledgeFileLogPage(pageRequest); + } } diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileLogMapper.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileLogMapper.java new file mode 100644 index 00000000..e290c570 --- /dev/null +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileLogMapper.java @@ -0,0 +1,31 @@ +package org.xyzh.ai.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.api.ai.dto.TbKnowledgeFile; +import org.xyzh.api.ai.dto.TbKnowledgeFileLog; +import org.xyzh.api.ai.vo.KnowledgeFileVO; +import org.xyzh.common.core.page.PageParam; + +import java.util.List; + +/** + * @description 知识库文件数据访问层 + * @filename KnowledgeFileMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-12-17 + */ +@Mapper +public interface TbKnowledgeFileLogMapper { + + int addKnowledgeFileLog(TbKnowledgeFileLog tbKnowledgeFileLog); + + List getKnowledgeFileLogList(@Param("filter") TbKnowledgeFileLog filter); + + List getKnowledgeFileLogPage(@Param("pageParam") PageParam pageParam,@Param("filter") TbKnowledgeFileLog filter); + + + int countKnowledgeFileLog(@Param("filter") TbKnowledgeFileLog filter); + +} diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeMapper.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeMapper.java index 01592252..561b8412 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeMapper.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeMapper.java @@ -32,6 +32,8 @@ public interface TbKnowledgeMapper { */ int deleteKnowledge(TbKnowledge knowledge); + int updateKnowledgeFileCount(@Param("knowledgeId") String knowledgeId, @Param("num") Integer num); + /** * 根据ID查询知识库 */ diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AIFileUploadServiceImpl.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AIFileUploadServiceImpl.java index ffa8c874..a1c7d21a 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AIFileUploadServiceImpl.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AIFileUploadServiceImpl.java @@ -1,5 +1,6 @@ package org.xyzh.ai.service.impl; +import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,15 +14,18 @@ import org.xyzh.ai.client.dto.DocumentUploadResponse; import org.xyzh.api.ai.dto.TbAgent; import org.xyzh.api.ai.service.AIFileUploadService; import org.xyzh.api.ai.service.AgentService; +import org.xyzh.api.file.dto.TbSysFileDTO; +import org.xyzh.api.file.service.FileService; import org.xyzh.common.auth.utils.LoginUtil; import org.xyzh.common.core.domain.ResultDomain; import java.io.File; +import java.util.HashMap; import java.util.List; import java.util.Map; /** - * @description AI文件上传服务实现(只负责与Dify交互,不处理minio和数据库) + * @description AI文件上传服务实现(同时上传到MinIO和Dify) * @filename AIFileUploadServiceImpl.java * @author yslg * @copyright xyzh @@ -37,6 +41,9 @@ public class AIFileUploadServiceImpl implements AIFileUploadService { @Autowired private AgentService agentService; + @DubboReference(version = "1.0.0", group = "file", timeout = 30000, retries = 0) + private FileService fileService; + // ============================ 对话文件管理 ============================ @Override @@ -56,31 +63,58 @@ public class AIFileUploadServiceImpl implements AIFileUploadService { } TbAgent agent = agentResult.getData(); + // 3. 获取当前用户 + String userId = LoginUtil.getCurrentUserId(); + if (!StringUtils.hasText(userId)) { + userId = "anonymous"; + } + File tempFile = null; + String sysFileId = null; + String sysFileUrl = null; + try { - // 3. 将MultipartFile转换为临时File + // 4. 上传到MinIO(通过FileService,使用字节数组方式) + byte[] fileBytes = file.getBytes(); + String fileName = file.getOriginalFilename(); + String contentType = file.getContentType(); + ResultDomain fileResult = fileService.uploadFileBytes(fileBytes, fileName, contentType, "ai-chat", agentId); + if (fileResult.getSuccess() && fileResult.getData() != null) { + TbSysFileDTO sysFile = fileResult.getData(); + sysFileId = sysFile.getFileId(); + sysFileUrl = sysFile.getUrl(); + logger.info("上传文件到MinIO成功: fileId={}, url={}", sysFileId, sysFileUrl); + } else { + logger.warn("上传文件到MinIO失败: {}", fileResult.getMessage()); + // MinIO上传失败不阻断流程,继续上传到Dify + } + + // 5. 将MultipartFile转换为临时File用于Dify上传 tempFile = File.createTempFile("upload_", "_" + file.getOriginalFilename()); file.transferTo(tempFile); - // 4. 获取当前用户 - String userId = LoginUtil.getCurrentUserId(); - if (!StringUtils.hasText(userId)) { - userId = "anonymous"; - } - - // 5. 上传到Dify + // 6. 上传到Dify DifyFileInfo difyFile = difyApiClient.uploadFileForChat(tempFile, file.getOriginalFilename(), userId, agent.getApiKey()); if (difyFile != null && StringUtils.hasText(difyFile.getId())) { - logger.info("上传对话文件成功: agentId={}, fileId={}", agentId, difyFile.getId()); - Map result = new java.util.HashMap<>(); + logger.info("上传对话文件到Dify成功: agentId={}, difyFileId={}", agentId, difyFile.getId()); + + Map result = new HashMap<>(); + // Dify返回的信息 result.put("id", difyFile.getId()); result.put("name", difyFile.getName()); result.put("size", difyFile.getSize()); result.put("type", difyFile.getType()); + result.put("extension", difyFile.getExtension()); + result.put("mime_type", difyFile.getMimeType()); result.put("upload_file_id", difyFile.getUploadFileId()); + // 系统文件信息(用于前端展示和数据库存储) + result.put("sys_file_id", sysFileId); + result.put("preview_url", sysFileUrl); + result.put("source_url", sysFileUrl); + return ResultDomain.success("上传成功", result); } - return ResultDomain.failure("上传文件失败"); + return ResultDomain.failure("上传文件到Dify失败"); } catch (Exception e) { logger.error("上传对话文件异常: {}", e.getMessage(), e); return ResultDomain.failure("上传文件异常: " + e.getMessage()); diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AgentChatServiceImpl.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AgentChatServiceImpl.java index 4fdd10ef..2e0e3880 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AgentChatServiceImpl.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/AgentChatServiceImpl.java @@ -1,5 +1,6 @@ package org.xyzh.ai.service.impl; +import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import org.apache.dubbo.config.annotation.DubboService; @@ -12,6 +13,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.xyzh.ai.client.DifyApiClient; import org.xyzh.ai.client.callback.StreamCallback; import org.xyzh.ai.client.dto.ChatRequest; +import org.xyzh.ai.config.DifyConfig; import org.xyzh.ai.mapper.TbChatMapper; import org.xyzh.ai.mapper.TbChatMessageMapper; import org.xyzh.api.ai.dto.ChatPrepareData; @@ -19,17 +21,23 @@ import org.xyzh.api.ai.dto.DifyFileInfo; import org.xyzh.api.ai.dto.TbAgent; import org.xyzh.api.ai.dto.TbChat; import org.xyzh.api.ai.dto.TbChatMessage; +import org.xyzh.api.ai.dto.TbKnowledge; import org.xyzh.api.ai.service.AgentChatService; import org.xyzh.api.ai.service.AgentService; +import org.xyzh.api.ai.service.KnowledgeService; +import org.xyzh.api.system.service.GuestService; +import org.xyzh.common.core.domain.LoginDomain; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.page.PageDomain; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.core.page.PageRequest; import org.xyzh.common.redis.service.RedisService; +import org.xyzh.common.utils.NonUtils; import org.xyzh.common.utils.id.IdUtil; import org.xyzh.common.auth.utils.LoginUtil; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -69,20 +77,11 @@ public class AgentChatServiceImpl implements AgentChatService { @Autowired private RedisService redisService; - /** - * @description 根据 userType 获取用户ID - * @param chat 会话信息(包含 userId 和 userType) - * @return 真实的系统用户ID - */ - private String getUserIdByType(TbChat chat) { - if (!chat.getUserType()) { - // 来客(userType=false):直接返回传入的 userId(已经是真正的系统 userId) - return chat.getUserId(); - } else { - // 员工(userType=true):从登录信息获取 userId - return LoginUtil.getCurrentUserId(); - } - } + @Autowired + private KnowledgeService knowledgeService; + + @Autowired + private DifyConfig difyConfig; /** * @description 判断智能体是否是outer @@ -130,7 +129,8 @@ public class AgentChatServiceImpl implements AgentChatService { } // 2. 获取用户ID并校验权限 - String userId = getUserIdByType(chat); + LoginDomain loginDomain = LoginUtil.getCurrentLogin(); + String userId = loginDomain.getUser().getUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } @@ -164,7 +164,8 @@ public class AgentChatServiceImpl implements AgentChatService { return ResultDomain.failure("智能体不可用"); } // 2. 获取用户ID并校验权限 - String userId = getUserIdByType(filter); + LoginDomain loginDomain = LoginUtil.getCurrentLogin(); + String userId = loginDomain.getUser().getUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } @@ -189,12 +190,9 @@ public class AgentChatServiceImpl implements AgentChatService { @Override public ResultDomain getChatList(TbChat filter) { - // 判断agent是否是outer - if(!isOuterAgent(filter.getAgentId())){ - return ResultDomain.failure("智能体不可用"); - } + // 获取用户ID - String userId = getUserIdByType(filter); + String userId = LoginUtil.getCurrentUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } @@ -204,16 +202,16 @@ public class AgentChatServiceImpl implements AgentChatService { } @Override - public ResultDomain> getChatPage(PageRequest pageRequest) { + public ResultDomain getChatPage(PageRequest pageRequest) { TbChat filter = pageRequest.getFilter(); // 判断agent是否是outer(来客才需要校验) if (!filter.getUserType() && !isOuterAgent(filter.getAgentId())) { - return ResultDomain.>failure("智能体不可用"); + return ResultDomain.failure("智能体不可用"); } // 获取用户ID - String userId = getUserIdByType(filter); + String userId = LoginUtil.getCurrentUserId(); if (userId == null) { - return ResultDomain.>failure("用户信息获取失败"); + return ResultDomain.failure("用户信息获取失败"); } filter.setUserId(userId); @@ -224,7 +222,7 @@ public class AgentChatServiceImpl implements AgentChatService { pageParam.setTotal((int) total); PageDomain pageDomain = new PageDomain<>(pageParam, chatList); - return ResultDomain.>success("查询成功", pageDomain); + return ResultDomain.success("查询成功", pageDomain); } // ====================== 智能体聊天管理 ====================== @@ -241,7 +239,7 @@ public class AgentChatServiceImpl implements AgentChatService { return ResultDomain.failure("智能体不可用"); } // 2. 获取用户ID并校验权限 - String userId = getUserIdByType(filter); + String userId = LoginUtil.getCurrentUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } @@ -272,7 +270,8 @@ public class AgentChatServiceImpl implements AgentChatService { chatFilter.setUserId(prepareData.getUserId()); chatFilter.setUserType(prepareData.getUserType()); - String userId = getUserIdByType(chatFilter); + LoginDomain loginDomain = LoginUtil.getCurrentLogin(); + String userId = loginDomain.getUser().getUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } @@ -299,6 +298,9 @@ public class AgentChatServiceImpl implements AgentChatService { sessionData.put("userId", userId); sessionData.put("filesData", prepareData.getFiles()); sessionData.put("apiKey", agent.getApiKey()); + sessionData.put("outer", agent.getIsOuter()); + sessionData.put("service", prepareData.getService()); + sessionData.put("isGuest", "guest".equals(loginDomain.getUser().getStatus())); String cacheKey = CHAT_SESSION_PREFIX + sessionId; redisService.set(cacheKey, sessionData, SESSION_TTL, TimeUnit.SECONDS); @@ -332,6 +334,10 @@ public class AgentChatServiceImpl implements AgentChatService { String query = (String) sessionData.get("query"); String userId = (String) sessionData.get("userId"); String apiKey = (String) sessionData.get("apiKey"); + String service = (String) sessionData.get("service"); + Boolean outer = (Boolean) sessionData.get("outer"); + Boolean isGuest = (Boolean) sessionData.get("isGuest"); + @SuppressWarnings("unchecked") List filesData = (List) sessionData.get("filesData"); @@ -346,6 +352,18 @@ public class AgentChatServiceImpl implements AgentChatService { userMessage.setChatId(chatId); userMessage.setRole("user"); userMessage.setContent(query); + + // 提取系统文件ID列表保存到消息中 + if (filesData != null && !filesData.isEmpty()) { + List sysFileIds = filesData.stream() + .map(DifyFileInfo::getSysFileId) + .filter(StringUtils::hasText) + .collect(java.util.stream.Collectors.toList()); + if (!sysFileIds.isEmpty()) { + userMessage.setFiles(sysFileIds); + } + } + chatMessageMapper.insertChatMessage(userMessage); // 5. 构建Dify请求 @@ -353,7 +371,23 @@ public class AgentChatServiceImpl implements AgentChatService { chatRequest.setQuery(query); chatRequest.setUser(userId); chatRequest.setResponseMode("streaming"); - chatRequest.setInputs(new HashMap<>()); // Dify API 要求 inputs 必传 + Map inputsMap = new HashMap<>(); + chatRequest.setInputs(inputsMap); // Dify API 要求 inputs 必传 + // 处理动态知识库的问题 + if(outer && NonUtils.isNotEmpty(service)){ + TbKnowledge filter = new TbKnowledge(); + filter.setService(service); + filter.setCategory(isGuest?"external":"internal"); + ResultDomain knowledgeRD = knowledgeService.listKnowledges(filter); + List datasets = new ArrayList<>(); + if(knowledgeRD.getSuccess()){ + datasets = knowledgeRD.getDataList().stream().map(TbKnowledge::getDifyDatasetId).toList(); + } + inputsMap.put("datasets", JSON.toJSONString(datasets)); + inputsMap.put("dataset_apikey", difyConfig.getKnowledgeApiKey()); + + } + if (filesData != null && !filesData.isEmpty()) { chatRequest.setFiles(filesData); @@ -442,7 +476,7 @@ public class AgentChatServiceImpl implements AgentChatService { TbAgent agent = agentResult.getData(); // 2. 获取用户ID - String userId = getUserIdByType(filter); + String userId = LoginUtil.getCurrentUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } @@ -468,7 +502,7 @@ public class AgentChatServiceImpl implements AgentChatService { } // 2. 获取用户ID - String userId = getUserIdByType(filter); + String userId = LoginUtil.getCurrentUserId(); if (userId == null) { return ResultDomain.failure("用户信息获取失败"); } diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeFileLogServiceImpl.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeFileLogServiceImpl.java new file mode 100644 index 00000000..bd765cba --- /dev/null +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeFileLogServiceImpl.java @@ -0,0 +1,88 @@ +package org.xyzh.ai.service.impl; + +import java.util.Arrays; +import java.util.List; + +import org.apache.dubbo.config.annotation.DubboService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.xyzh.ai.mapper.TbKnowledgeFileLogMapper; +import org.xyzh.api.ai.dto.TbKnowledgeFileLog; +import org.xyzh.api.ai.service.KnowledgeFileLogService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageDomain; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.utils.NonUtils; +import org.xyzh.common.utils.id.IdUtil; +import org.xyzh.common.utils.validation.ValidationResult; +import org.xyzh.common.utils.validation.ValidationUtils; + +@DubboService(version="1.0.0", group="ai", timeout=3000, retries=0) +public class KnowledgeFileLogServiceImpl implements KnowledgeFileLogService{ + + private static final Logger logger = LoggerFactory.getLogger(KnowledgeServiceImpl.class); + + @Autowired + private TbKnowledgeFileLogMapper knowledgeFileLogMapper; + + /** + * @description 新增知识库文件操作日志 + * @param knowledgeFileLog + * @return 日志 + * @author yslg + * @since 2025-12-31 + */ + @Override + public ResultDomain addKnowledgeFileLog(TbKnowledgeFileLog knowledgeFileLog){ + knowledgeFileLog.setOptsn(IdUtil.getOptsn()); + knowledgeFileLog.setLogId(IdUtil.generateID()); + ValidationResult rt = ValidationUtils.validate(knowledgeFileLog, Arrays.asList( + + )); + if(!rt.isValid()){ + return ResultDomain.failure("日志参数校验失败"); + } + int result = knowledgeFileLogMapper.addKnowledgeFileLog(knowledgeFileLog); + if(result >0){ + return ResultDomain.success("添加知识库文件日志成功", knowledgeFileLog); + }else { + return ResultDomain.failure("添加知识库文件日志失败"); + } + } + + /** + * @description 查询知识库日志操作列表 + * @param filter + * @return 日志列表 + * @author yslg + * @since 2025-12-31 + */ + @Override + public ResultDomain getKnowledgeFileLogList(TbKnowledgeFileLog filter){ + List logs = knowledgeFileLogMapper.getKnowledgeFileLogList(filter); + return ResultDomain.success("查询知识库日志成功",logs); + } + + /** + * @description 查询知识库日志操作分页 + * @param pageRequest + * @return 日志分页数据 + * @author yslg + * @since 2025-12-31 + */ + @Override + public ResultDomain getKnowledgeFileLogPage(PageRequest pageRequest){ + List logs = knowledgeFileLogMapper.getKnowledgeFileLogPage(pageRequest.getPageParam(), pageRequest.getFilter()); + int total = knowledgeFileLogMapper.countKnowledgeFileLog(pageRequest.getFilter()); + PageDomain pageDomain = new PageDomain<>(); + pageDomain.setDataList(logs); + PageParam pageParam = pageRequest.getPageParam(); + pageParam.setTotal(total); + pageDomain.setPageParam(pageParam); + return ResultDomain.success("查询知识库日志成功", pageDomain); + } + + +} diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeServiceImpl.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeServiceImpl.java index 43de2d3f..68a3029e 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeServiceImpl.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/service/impl/KnowledgeServiceImpl.java @@ -16,7 +16,10 @@ import org.xyzh.ai.mapper.TbKnowledgeFileMapper; import org.xyzh.ai.mapper.TbKnowledgeMapper; import org.xyzh.api.ai.dto.TbKnowledge; import org.xyzh.api.ai.dto.TbKnowledgeFile; +import org.xyzh.api.ai.dto.TbKnowledgeFileLog; +import org.xyzh.api.ai.constance.KnowledgeFileLogAction; import org.xyzh.api.ai.service.AIFileUploadService; +import org.xyzh.api.ai.service.KnowledgeFileLogService; import org.xyzh.api.ai.service.KnowledgeService; import org.xyzh.api.ai.vo.KnowledgeFileVO; import org.xyzh.api.file.dto.TbSysFileDTO; @@ -58,6 +61,9 @@ public class KnowledgeServiceImpl implements KnowledgeService { @DubboReference(version = "1.0.0", group = "file", timeout = 30000, retries = 0) private FileService fileService; + @Autowired + private KnowledgeFileLogService knowledgeFileLogService; + @Autowired private AIFileUploadService aiFileUploadService; @@ -533,9 +539,22 @@ public class KnowledgeServiceImpl implements KnowledgeService { knowledgeFile.setDifyFileId(difyFileId); knowledgeFile.setVersion(1); + knowledgeMapper.updateKnowledgeFileCount(knowledgeId, 1); int rows = knowledgeFileMapper.insertKnowledgeFile(knowledgeFile); if (rows > 0) { logger.info("保存知识库文件记录成功: knowledgeId={}, fileId={}, difyFileId={}", knowledgeId, fileId, difyFileId); + // 记录日志 + TbKnowledgeFileLog log = new TbKnowledgeFileLog(); + log.setKnowledgeId(knowledgeId); + log.setFileRootId(fileId); + log.setFileId(fileId); + log.setFileName(file.getOriginalFilename()); + log.setVersion(1); + log.setAction(KnowledgeFileLogAction.UPLOAD.getAction()); + log.setService("workcase"); + log.setCreator(LoginUtil.getCurrentUserId()); + log.setCreatorName(LoginUtil.getCurrentUserName()); + knowledgeFileLogService.addKnowledgeFileLog(log); return ResultDomain.success("上传成功", knowledgeFile); } @@ -682,6 +701,18 @@ public class KnowledgeServiceImpl implements KnowledgeService { int rows = knowledgeFileMapper.insertKnowledgeFile(newKnowledgeFile); if (rows > 0) { logger.info("保存新版本记录成功: knowledgeId={}, fileRootId={}, newVersion={}", knowledgeId, fileRootId, newVersion); + // 记录日志 + TbKnowledgeFileLog log = new TbKnowledgeFileLog(); + log.setKnowledgeId(knowledgeId); + log.setFileRootId(fileRootId); + log.setFileId(newFileId); + log.setFileName(file.getOriginalFilename()); + log.setVersion(newVersion); + log.setAction(KnowledgeFileLogAction.UPDATE.getAction()); + log.setService("workcase"); + log.setCreator(LoginUtil.getCurrentUserId()); + log.setCreatorName(LoginUtil.getCurrentUserName()); + knowledgeFileLogService.addKnowledgeFileLog(log); return ResultDomain.success("更新成功", newKnowledgeFile); } @@ -722,15 +753,27 @@ public class KnowledgeServiceImpl implements KnowledgeService { if (!difyDocIds.isEmpty()) { aiFileUploadService.batchDeleteFilesFromDify(knowledge.getDifyDatasetId(), difyDocIds); } + }else{ + return ResultDomain.failure("知识库未关联Dify"); } // 3. 软删除本地记录和minio文件 int rows = knowledgeFileMapper.deleteFilesByRootId(fileRootId); + knowledgeMapper.updateKnowledgeFileCount(knowledge.getKnowledgeId(), -1); if (rows > 0) { logger.info("删除知识库文件成功: fileRootId={}", fileRootId); for (TbKnowledgeFile file : versions) { fileService.deleteFile(file.getFileId()); } + // 记录日志 + TbKnowledgeFileLog log = new TbKnowledgeFileLog(); + log.setKnowledgeId(knowledge.getKnowledgeId()); + log.setFileRootId(fileRootId); + log.setAction(KnowledgeFileLogAction.DELETE.getAction()); + log.setService("workcase"); + log.setCreator(LoginUtil.getCurrentUserId()); + log.setCreatorName(LoginUtil.getCurrentUserName()); + knowledgeFileLogService.addKnowledgeFileLog(log); return ResultDomain.success("删除成功", true); } diff --git a/urbanLifelineServ/ai/src/main/resources/application-dev.yml b/urbanLifelineServ/ai/src/main/resources/application-dev.yml index da48f901..310d49ba 100644 --- a/urbanLifelineServ/ai/src/main/resources/application-dev.yml +++ b/urbanLifelineServ/ai/src/main/resources/application-dev.yml @@ -29,7 +29,12 @@ security: spring: application: name: ai-service - + # 文件上传配置 + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB # ================== Spring Cloud Nacos ================== cloud: nacos: @@ -72,6 +77,7 @@ dubbo: name: urban-lifeline-agent qos-enable: false protocol: + payload: 110100480 name: dubbo port: -1 registry: diff --git a/urbanLifelineServ/ai/src/main/resources/application.yml b/urbanLifelineServ/ai/src/main/resources/application.yml index dc0f506e..35253da8 100644 --- a/urbanLifelineServ/ai/src/main/resources/application.yml +++ b/urbanLifelineServ/ai/src/main/resources/application.yml @@ -72,6 +72,7 @@ dubbo: name: urban-lifeline-agent qos-enable: false protocol: + payload: 110100480 name: dubbo port: -1 registry: diff --git a/urbanLifelineServ/ai/src/main/resources/log4j2.xml b/urbanLifelineServ/ai/src/main/resources/log4j2.xml index 6370e274..d99f9f0c 100644 --- a/urbanLifelineServ/ai/src/main/resources/log4j2.xml +++ b/urbanLifelineServ/ai/src/main/resources/log4j2.xml @@ -47,7 +47,7 @@ - + diff --git a/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileLogMapper.xml b/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileLogMapper.xml new file mode 100644 index 00000000..d63322d6 --- /dev/null +++ b/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileLogMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + optsn, log_id, knowledge_id, file_root_id, file_id, file_name, version, + action, creator, creator_name, create_time + + + + INSERT INTO ai.tb_knowledge_file_log ( + optsn, log_id, knowledge_id, file_root_id, file_id, file_name, version, + action, service, creator, creator_name, create_time + ) VALUES ( + #{optsn}, #{logId}, #{knowledgeId}, #{fileRootId}, #{fileId}, #{fileName}, #{version}, + #{action}, #{service}, #{creator}, #{creatorName}, NOW() + ) + + + + + + + + diff --git a/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeMapper.xml b/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeMapper.xml index 1fb778ad..7c27ab65 100644 --- a/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeMapper.xml +++ b/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeMapper.xml @@ -112,6 +112,12 @@ WHERE knowledge_id = #{knowledgeId} AND deleted = false + + UPDATE ai.tb_knowledge + SET document_count = document_count + #{num} + WHERE knowledge_id = #{knowledgeId} AND deleted = false + + - + SELECT p.process_id, p.optsn, p.workcase_id, p.action, p.message, p.files, + p.processor, p.remark, p.creator, p.create_time, + COALESCE(u1.username, g1.name) as creator_name, + COALESCE(u2.username, g2.name) as processor_name + FROM workcase.tb_workcase_process p + LEFT JOIN sys.tb_sys_user_info u1 ON p.creator = u1.user_id + LEFT JOIN sys.tb_guest g1 ON p.creator = g1.user_id + LEFT JOIN sys.tb_sys_user_info u2 ON p.processor = u2.user_id + LEFT JOIN sys.tb_guest g2 ON p.processor = g2.user_id - AND process_id = #{filter.processId} + AND p.process_id = #{filter.processId} - AND workcase_id = #{filter.workcaseId} + AND p.workcase_id = #{filter.workcaseId} - AND action = #{filter.action} + AND p.action = #{filter.action} - AND processor = #{filter.processor} + AND p.processor = #{filter.processor} - AND creator = #{filter.creator} + AND p.creator = #{filter.creator} - ORDER BY create_time ASC + ORDER BY p.create_time ASC - + SELECT p.process_id, p.optsn, p.workcase_id, p.action, p.message, p.files, + p.processor, p.remark, p.creator, p.create_time, + COALESCE(u1.username, g1.name) as creator_name, + COALESCE(u2.username, g2.name) as processor_name + FROM workcase.tb_workcase_process p + LEFT JOIN sys.tb_sys_user_info u1 ON p.creator = u1.user_id + LEFT JOIN sys.tb_guest g1 ON p.creator = g1.user_id + LEFT JOIN sys.tb_sys_user_info u2 ON p.processor = u2.user_id + LEFT JOIN sys.tb_guest g2 ON p.processor = g2.user_id - AND process_id = #{filter.processId} + AND p.process_id = #{filter.processId} - AND workcase_id = #{filter.workcaseId} + AND p.workcase_id = #{filter.workcaseId} - AND action = #{filter.action} + AND p.action = #{filter.action} - AND processor = #{filter.processor} + AND p.processor = #{filter.processor} - AND creator = #{filter.creator} + AND p.creator = #{filter.creator} - ORDER BY create_time ASC + ORDER BY p.create_time ASC LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset} diff --git a/urbanLifelineWeb/packages/platform/src/views/public/Login/Login.vue b/urbanLifelineWeb/packages/platform/src/views/public/Login/Login.vue index 5be1119c..f32aebb2 100644 --- a/urbanLifelineWeb/packages/platform/src/views/public/Login/Login.vue +++ b/urbanLifelineWeb/packages/platform/src/views/public/Login/Login.vue @@ -134,10 +134,9 @@ async function handleLogin() { if (response.success && response.data) { const loginData = response.data - // 8. 保存 Token + // 8. 保存 Token(只用 TokenManager,避免格式不一致) if (loginData.token) { TokenManager.setToken(loginData.token, loginForm.rememberMe) - localStorage.setItem('token', loginData.token) } // 9. 保存 LoginDomain 到 LocalStorage diff --git a/urbanLifelineWeb/packages/shared/src/api/ai/aiKnowledge.ts b/urbanLifelineWeb/packages/shared/src/api/ai/aiKnowledge.ts index 61e6986e..791e10c4 100644 --- a/urbanLifelineWeb/packages/shared/src/api/ai/aiKnowledge.ts +++ b/urbanLifelineWeb/packages/shared/src/api/ai/aiKnowledge.ts @@ -1,6 +1,6 @@ import { api } from '@/api/index' import type { ResultDomain, PageRequest } from '@/types' -import type { TbKnowledge, TbKnowledgeFile, KnowledgeFileVO, SegmentRequestBody, DocumentStatusRequestBody } from '@/types/ai' +import type { TbKnowledge, TbKnowledgeFile, KnowledgeFileVO, SegmentRequestBody, DocumentStatusRequestBody, TbKnowledgeFileLog } from '@/types/ai' /** * @description AI知识库相关接口 @@ -162,8 +162,8 @@ export const aiKnowledgeAPI = { * 删除知识库文件 * @param fileId 文件ID */ - async deleteFile(fileId: string): Promise> { - const response = await api.delete(`${this.baseUrl}/file/${fileId}`) + async deleteFile(fileRootId: string): Promise> { + const response = await api.delete(`${this.baseUrl}/file/${fileRootId}`) return response.data }, @@ -263,5 +263,25 @@ export const aiKnowledgeAPI = { requestBody ) return response.data + }, + + // ====================== 日志管理 ====================== + + /** + * 查询知识库操作日志列表 + * @param fileLog 查询条件 + */ + async getFileLogList(fileLog: TbKnowledgeFileLog): Promise> { + const response = await api.post(`${this.baseUrl}/datasets/log/list`, fileLog) + return response.data + }, + + /** + * 分页查询知识库操作日志 + * @param pageRequest 分页请求 + */ + async getFileLogPage(pageRequest: PageRequest): Promise> { + const response = await api.post(`${this.baseUrl}/datasets/log/page`, pageRequest) + return response.data } } \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/api/ai/aichat.ts b/urbanLifelineWeb/packages/shared/src/api/ai/aichat.ts index 104eb959..c289ce7b 100644 --- a/urbanLifelineWeb/packages/shared/src/api/ai/aichat.ts +++ b/urbanLifelineWeb/packages/shared/src/api/ai/aichat.ts @@ -119,7 +119,7 @@ export const aiChatAPI = { const formData = new FormData() formData.append('file', file) formData.append('agentId', agentId) - const response = await api.uploadPut(`${this.baseUrl}/file/upload`, formData) + const response = await api.upload(`${this.baseUrl}/file/upload`, formData) return response.data } } \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/api/file/file.ts b/urbanLifelineWeb/packages/shared/src/api/file/file.ts index 0bb77b37..cc71e7ff 100644 --- a/urbanLifelineWeb/packages/shared/src/api/file/file.ts +++ b/urbanLifelineWeb/packages/shared/src/api/file/file.ts @@ -48,7 +48,7 @@ export const fileAPI = { if (param.uploader) { formData.append('uploader', param.uploader); } - const response = await api.upload(`${this.baseUrl}/batch-upload`, formData); + const response = await api.upload(`${this.baseUrl}/upload/batch`, formData); return response.data; }, diff --git a/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.vue b/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.vue index 8ca9daf0..98cecf24 100644 --- a/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.vue +++ b/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.vue @@ -130,7 +130,7 @@
-
{{ file.name || file.fileName || '未知文件' }}
+
{{ file.name || '未知文件' }}
{{ file.size ? formatFileSize(file.size) : '' }}
@@ -148,7 +148,7 @@ \ No newline at end of file + diff --git a/urbanLifelineWeb/packages/workcase/src/views/admin/log/workcaseLog/WorkcaseLogView.vue b/urbanLifelineWeb/packages/workcase/src/views/admin/log/workcaseLog/WorkcaseLogView.vue index 3b3977e2..3b0d1cdd 100644 --- a/urbanLifelineWeb/packages/workcase/src/views/admin/log/workcaseLog/WorkcaseLogView.vue +++ b/urbanLifelineWeb/packages/workcase/src/views/admin/log/workcaseLog/WorkcaseLogView.vue @@ -1,5 +1,5 @@