From 62850717ebf45ee154ecdb32de4c30333b530452 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Sat, 20 Dec 2025 17:12:42 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/controller/KnowledgeController.java | 6 +- .../xyzh/ai/mapper/TbKnowledgeFileMapper.java | 10 + .../ai/service/impl/KnowledgeServiceImpl.java | 38 +- .../mapper/TbKnowledgeFileMapper.xml | 50 +- .../org/xyzh/api/ai/dto/KnowledgeFileVO.java | 0 .../xyzh/api/ai/service/KnowledgeService.java | 4 +- .../org/xyzh/api/ai/vo/KnowledgeFileVO.java | 3 + .../xyzh/api/file/service/FileService.java | 43 + .../xyzh/auth/controller/AuthController.java | 4 +- .../auth/service/impl/AuthServiceImpl.java | 4 +- .../org/xyzh/common/auth/utils/LoginUtil.java | 23 +- .../main/java/org/xyzh/common/vo/BaseVO.java | 16 + .../common/wechat/config/WeChatKefuInit.java | 2 +- .../kefu/core/KefuAccessTokenManager.java | 2 +- .../xyzh/file/controller/FileController.java | 6 +- .../file/service/impl/FileServiceImpl.java | 280 +++--- .../src/main/resources/mapper/FileMapper.xml | 4 + .../message/config/DynamicConfigLoader.java | 2 +- .../xyzh/workcase/config/KnowledgeInit.java | 4 +- .../service/WorkcaseChatServiceImpl.java | 2 +- .../example/DocumentSegmentDialog.vue | 855 ++++++++++++++++++ .../packages/bidding/src/types/shared.d.ts | 18 +- .../AdminSidebarLayout/AdminSidebarLayout.vue | 27 +- .../packages/platform/src/types/shared.d.ts | 19 +- .../views/public/Agents/AgentPlatformView.vue | 2 +- .../Agents/components/AgentEdit/AgentEdit.vue | 4 +- .../src/views/public/Chat/AIChatView.vue | 22 +- .../components/ChatDefault/ChatDefault.vue | 2 +- .../packages/platform/vite.config.ts | 5 + urbanLifelineWeb/packages/shared/EXPOSES.md | 8 +- .../packages/shared/src/api/ai/aiKnowledge.ts | 6 +- .../shared/src/components/ai/index.ts | 2 +- .../documentDetail/DocumentDetail.scss | 0 .../documentDetail}/DocumentDetail.vue | 0 .../documentSegment/DocumentSegment.scss | 202 +++++ .../documentSegment/DocumentSegment.vue | 501 ++++++++++ .../src/components/ai/knowledge/index.ts | 2 + .../file/fileHistory/FileHistory.scss | 33 + .../file/fileHistory/FileHistory.vue | 155 ++++ .../{ => file}/fileupload/FileUpload.scss | 0 .../{ => file}/fileupload/FileUpload.vue | 0 .../fileupload/FileUploadExample.vue | 0 .../shared/src/components/file/index.ts | 2 + .../shared/src/components/fileupload/index.ts | 1 - .../packages/shared/src/components/index.ts | 3 +- .../shared/src/types/ai/aiKnowledge.ts | 37 +- .../packages/shared/src/types/base/index.ts | 25 +- .../packages/shared/vite.config.ts | 7 +- .../packages/workcase/src/types/shared.d.ts | 20 +- .../admin/customerChat/CustomerChatView.vue | 2 +- .../views/admin/knowledge/KnowLedgeView.scss | 14 + .../views/admin/knowledge/KnowLedgeView.vue | 134 ++- .../log/knowledgeLog/KnowledgeLogView.vue | 2 +- .../admin/log/systemLog/SystemLogView.vue | 2 +- .../admin/log/workcaseLog/WorkcaseLogView.vue | 2 +- .../views/admin/overview/OverviewView.scss | 1 + .../src/views/admin/overview/OverviewView.vue | 2 +- .../src/views/admin/workcase/WorkcaseView.vue | 2 +- .../packages/workcase/vite.config.ts | 5 + 59 files changed, 2351 insertions(+), 276 deletions(-) rename urbanLifelineWeb/packages/shared/src/components/ai/DocumentDetail/DocumentDetail.scss => urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/dto/KnowledgeFileVO.java (100%) create mode 100644 urbanLifelineWeb/example/DocumentSegmentDialog.vue create mode 100644 urbanLifelineWeb/packages/shared/src/components/ai/knowledge/documentDetail/DocumentDetail.scss rename urbanLifelineWeb/packages/shared/src/components/ai/{DocumentDetail => knowledge/documentDetail}/DocumentDetail.vue (100%) create mode 100644 urbanLifelineWeb/packages/shared/src/components/ai/knowledge/documentSegment/DocumentSegment.scss create mode 100644 urbanLifelineWeb/packages/shared/src/components/ai/knowledge/documentSegment/DocumentSegment.vue create mode 100644 urbanLifelineWeb/packages/shared/src/components/ai/knowledge/index.ts create mode 100644 urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.scss create mode 100644 urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.vue rename urbanLifelineWeb/packages/shared/src/components/{ => file}/fileupload/FileUpload.scss (100%) rename urbanLifelineWeb/packages/shared/src/components/{ => file}/fileupload/FileUpload.vue (100%) rename urbanLifelineWeb/packages/shared/src/components/{ => file}/fileupload/FileUploadExample.vue (100%) create mode 100644 urbanLifelineWeb/packages/shared/src/components/file/index.ts delete mode 100644 urbanLifelineWeb/packages/shared/src/components/fileupload/index.ts 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 2b4a6d9b..a65ebb79 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 @@ -243,13 +243,13 @@ public class KnowledgeController { /** * 获取文件历史版本,获取fileRootId下所有version - * @param fileRootId 文件id + * @param fileRootId 文件根ID * @author yslg * @since 2025-12-18 */ @PreAuthorize("hasAuthority('ai:knowledge:file:view')") - @GetMapping("/file/{fileId}/history") - public ResultDomain getFileHistory(@PathVariable("fileId") @NotBlank String fileRootId) { + @GetMapping("/file/{fileRootId}/history") + public ResultDomain getFileHistory(@PathVariable("fileRootId") @NotBlank String fileRootId) { logger.info("获取文件历史: fileRootId={}", fileRootId); return knowledgeService.getKnowledgeFileHistory(fileRootId); } diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileMapper.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileMapper.java index ab5b60fc..ddcdc5e9 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileMapper.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/mapper/TbKnowledgeFileMapper.java @@ -76,4 +76,14 @@ public interface TbKnowledgeFileMapper { @Param("knowledgeId") String knowledgeId, @Param("fileRootId") String fileRootId ); + + /** + * 根据文件根ID查询最大版本的文件 + */ + TbKnowledgeFile selectLatestVersionFile(@Param("fileRootId") String fileRootId); + + /** + * 根据文件根ID查询所有版本(包含文件详细信息) + */ + List selectFileVersionsWithDetail(@Param("fileRootId") String fileRootId); } 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 51e4203d..43de2d3f 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 @@ -55,7 +55,7 @@ public class KnowledgeServiceImpl implements KnowledgeService { @Autowired private DifyApiClient difyApiClient; - @DubboReference(version = "1.0.0", group = "file", timeout = 30000) + @DubboReference(version = "1.0.0", group = "file", timeout = 30000, retries = 0) private FileService fileService; @Autowired @@ -615,14 +615,22 @@ public class KnowledgeServiceImpl implements KnowledgeService { return ResultDomain.failure("知识库未关联Dify"); } - // 3. 获取旧版本 - List oldVersions = knowledgeFileMapper.selectFileVersions(fileRootId); - if (oldVersions == null || oldVersions.isEmpty()) { + // 3. 获取最大版本的旧文件 + TbKnowledgeFile latestOldFile = knowledgeFileMapper.selectLatestVersionFile(fileRootId); + if (latestOldFile == null) { return ResultDomain.failure("原文件不存在"); } - // 4. 上传新版本到minio - ResultDomain fileResult = fileService.uploadFileVersion(file, "knowledge", knowledgeId, fileRootId); + // 4. 上传新版本到minio(使用字节数组避免 Dubbo 序列化问题) + byte[] fileBytes; + try { + fileBytes = file.getBytes(); + } catch (java.io.IOException e) { + logger.error("读取文件字节失败", e); + return ResultDomain.failure("读取文件字节失败: " + e.getMessage()); + } + ResultDomain fileResult = fileService.uploadFileBytesVersion( + fileBytes, file.getOriginalFilename(), file.getContentType(), "knowledge", knowledgeId, fileRootId); if (!fileResult.getSuccess() || fileResult.getData() == null) { return ResultDomain.failure("上传新版本文件失败: " + fileResult.getMessage()); } @@ -631,11 +639,9 @@ public class KnowledgeServiceImpl implements KnowledgeService { int newVersion = sysFile.getVersion(); logger.info("上传新版本到minio成功: fileId={}, version={}", newFileId, newVersion); - // 5. 删除Dify旧文档 - for (TbKnowledgeFile oldFile : oldVersions) { - if (StringUtils.hasText(oldFile.getDifyFileId())) { - aiFileUploadService.deleteFileFromDify(knowledge.getDifyDatasetId(), oldFile.getDifyFileId()); - } + // 5. 删除Dify最大版本的旧文档 + if (StringUtils.hasText(latestOldFile.getDifyFileId())) { + aiFileUploadService.deleteFileFromDify(knowledge.getDifyDatasetId(), latestOldFile.getDifyFileId()); } // 6. 上传新文件到Dify @@ -734,17 +740,19 @@ public class KnowledgeServiceImpl implements KnowledgeService { /** * @description 获取文件历史版本 * @param fileRootId 文件根ID - * @return ResultDomain 文件历史版本列表 + * @return ResultDomain 文件历史版本列表(dataList) * @author yslg * @since 2025-12-18 */ @Override - public ResultDomain getKnowledgeFileHistory(String fileRootId) { + public ResultDomain getKnowledgeFileHistory(String fileRootId) { if (!StringUtils.hasText(fileRootId)) { return ResultDomain.failure("文件根ID不能为空"); } - List versions = knowledgeFileMapper.selectFileVersions(fileRootId); - return ResultDomain.success("查询成功", versions); + List versions = knowledgeFileMapper.selectFileVersionsWithDetail(fileRootId); + ResultDomain result = ResultDomain.success("查询成功"); + result.setDataList(versions); + return result; } } diff --git a/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileMapper.xml b/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileMapper.xml index 85cfc5bf..cf988eed 100644 --- a/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileMapper.xml +++ b/urbanLifelineServ/ai/src/main/resources/mapper/TbKnowledgeFileMapper.xml @@ -33,6 +33,7 @@ + @@ -83,9 +84,17 @@ f.mime_type as file_mime_type, f.url as file_url, f.extension as file_extension, - f.md5_hash as file_md5_hash + f.md5_hash as file_md5_hash, + ui.username as uploader_name FROM ai.tb_knowledge_file kf + INNER JOIN ( + SELECT file_root_id, MAX(version) as max_version + FROM ai.tb_knowledge_file + WHERE knowledge_id = #{knowledgeId} AND deleted = false + GROUP BY file_root_id + ) latest ON kf.file_root_id = latest.file_root_id AND kf.version = latest.max_version LEFT JOIN file.tb_sys_file f ON kf.file_id = f.file_id AND f.deleted = false + LEFT JOIN sys.tb_sys_user_info ui ON f.uploader = ui.user_id AND ui.deleted = false WHERE kf.knowledge_id = #{knowledgeId} AND kf.deleted = false ORDER BY kf.create_time DESC @@ -107,16 +116,24 @@ f.mime_type as file_mime_type, f.url as file_url, f.extension as file_extension, - f.md5_hash as file_md5_hash + f.md5_hash as file_md5_hash, + ui.username as uploader_name FROM ai.tb_knowledge_file kf + INNER JOIN ( + SELECT file_root_id, MAX(version) as max_version + FROM ai.tb_knowledge_file + WHERE knowledge_id = #{knowledgeId} AND deleted = false + GROUP BY file_root_id + ) latest ON kf.file_root_id = latest.file_root_id AND kf.version = latest.max_version LEFT JOIN file.tb_sys_file f ON kf.file_id = f.file_id AND f.deleted = false + LEFT JOIN sys.tb_sys_user_info ui ON f.uploader = ui.user_id AND ui.deleted = false WHERE kf.knowledge_id = #{knowledgeId} AND kf.deleted = false ORDER BY kf.create_time DESC LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset} @@ -133,4 +150,31 @@ FROM ai.tb_knowledge_file WHERE knowledge_id = #{knowledgeId} AND file_root_id = #{fileRootId} + + + + diff --git a/urbanLifelineWeb/packages/shared/src/components/ai/DocumentDetail/DocumentDetail.scss b/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/dto/KnowledgeFileVO.java similarity index 100% rename from urbanLifelineWeb/packages/shared/src/components/ai/DocumentDetail/DocumentDetail.scss rename to urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/dto/KnowledgeFileVO.java diff --git a/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/service/KnowledgeService.java b/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/service/KnowledgeService.java index 6dd975dc..51bd7858 100644 --- a/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/service/KnowledgeService.java +++ b/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/service/KnowledgeService.java @@ -162,10 +162,10 @@ public interface KnowledgeService { /** * @description 获取文件历史版本 * @param fileRootId 文件根ID - * @return ResultDomain 文件历史版本列表 + * @return ResultDomain 文件历史版本列表(dataList) * @author yslg * @since 2025-12-18 */ - ResultDomain getKnowledgeFileHistory(String fileRootId); + ResultDomain getKnowledgeFileHistory(String fileRootId); } diff --git a/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/vo/KnowledgeFileVO.java b/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/vo/KnowledgeFileVO.java index e6286a78..648755ca 100644 --- a/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/vo/KnowledgeFileVO.java +++ b/urbanLifelineServ/apis/api-ai/src/main/java/org/xyzh/api/ai/vo/KnowledgeFileVO.java @@ -57,4 +57,7 @@ public class KnowledgeFileVO extends BaseVO { @Schema(description = "文件MD5值") private String fileMd5Hash; + + @Schema(description = "上传人员名称") + private String uploaderName; } diff --git a/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java b/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java index b7d613bd..792ee8f0 100644 --- a/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java +++ b/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java @@ -108,4 +108,47 @@ public interface FileService { */ ResultDomain uploadFileBytes(byte[] fileBytes, String fileName, String contentType, String module, String businessId); + /** + * @description 通过字节数组上传文件(支持直接指定上传者,用于未登录用户场景如AIChat) + * @param fileBytes 文件字节数组 + * @param fileName 文件名 + * @param contentType 文件类型 + * @param module 所属模块 + * @param businessId 业务ID + * @param uploaderUserId 上传者用户ID(可为null) + * @return ResultDomain 上传结果 + * @author yslg + * @since 2025-12-20 + */ + ResultDomain uploadFileBytesWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String uploaderUserId); + + /** + * @description 通过字节数组上传新版本文件(用于跨模块 Dubbo 调用的文件版本更新) + * @param fileBytes 文件字节数组 + * @param fileName 文件名 + * @param contentType 文件类型 + * @param module 所属模块 + * @param businessId 业务ID + * @param fileRootId 文件根ID(多版本一致) + * @return ResultDomain 上传结果,包含新版本文件信息 + * @author yslg + * @since 2025-12-20 + */ + ResultDomain uploadFileBytesVersion(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId); + + /** + * @description 通过字节数组上传新版本文件(支持直接指定上传者) + * @param fileBytes 文件字节数组 + * @param fileName 文件名 + * @param contentType 文件类型 + * @param module 所属模块 + * @param businessId 业务ID + * @param fileRootId 文件根ID(多版本一致) + * @param uploaderUserId 上传者用户ID(可为null) + * @return ResultDomain 上传结果,包含新版本文件信息 + * @author yslg + * @since 2025-12-20 + */ + ResultDomain uploadFileBytesVersionWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId, String uploaderUserId); + } diff --git a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java index 728fba32..e0751bf7 100644 --- a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java +++ b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java @@ -34,10 +34,10 @@ public class AuthController { @Autowired private AuthService authService; - @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false) + @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0) private SysUserService userService; - @DubboReference(version = "1.0.0", group = "message", timeout = 5000, check = false) + @DubboReference(version = "1.0.0", group = "message", timeout = 5000, check = false, retries = 0) private MessageService messageService; @Autowired diff --git a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java index 8c2c8f2c..83c37682 100644 --- a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java +++ b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java @@ -59,10 +59,10 @@ public class AuthServiceImpl implements AuthService{ private RedisService redisService; - @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false) + @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0) private SysUserService userService; - @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false) + @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0) private ModulePermissionService modulePermissionService; @Autowired diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java index 7b10a5dc..e42425df 100644 --- a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java +++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java @@ -84,17 +84,28 @@ public class LoginUtil { } /** - * 从请求头获取Token + * 从请求头或Dubbo RpcContext获取Token */ public static String getToken() { + // 1. 优先从HTTP请求头获取(正常Web请求) HttpServletRequest request = getRequest(); - if (request == null) { - return null; + if (request != null) { + String authHeader = request.getHeader(AUTHORIZATION_HEADER); + if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) { + return authHeader.substring(BEARER_PREFIX.length()); + } } - String authHeader = request.getHeader(AUTHORIZATION_HEADER); - if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) { - return authHeader.substring(BEARER_PREFIX.length()); + // 2. 从Dubbo Provider ThreadLocal获取(跨服务调用) + try { + Class filterClass = Class.forName("org.xyzh.common.auth.filter.DubboProviderContextFilter"); + ThreadLocal tokenHolder = (ThreadLocal) filterClass.getField("TOKEN_HOLDER").get(null); + String token = tokenHolder.get(); + if (StringUtils.hasText(token)) { + return token; + } + } catch (Exception e) { + // Dubbo Filter不存在或未加载,忽略 } return null; diff --git a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/vo/BaseVO.java b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/vo/BaseVO.java index 5c41d88a..88f51697 100644 --- a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/vo/BaseVO.java +++ b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/vo/BaseVO.java @@ -2,6 +2,10 @@ package org.xyzh.common.vo; import java.io.Serializable; import java.util.Date; +import java.util.List; + +import org.xyzh.common.dto.OrderField; + import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.v3.oas.annotations.media.Schema; @@ -54,5 +58,17 @@ public class BaseVO implements Serializable { @Schema(description = "是否已删除", defaultValue = "false") private Boolean deleted = false; + + @Schema(description = "数量限制") + private Integer limit; + + @Schema(description = "开始时间") + private Date startTime; + + @Schema(description = "结束时间") + private Date endTime; + + @Schema(description = "排序字段") + private List orderFields; } diff --git a/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/config/WeChatKefuInit.java b/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/config/WeChatKefuInit.java index fa1208e3..8ad88eb0 100644 --- a/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/config/WeChatKefuInit.java +++ b/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/config/WeChatKefuInit.java @@ -21,7 +21,7 @@ public class WeChatKefuInit { private static final Logger logger = LoggerFactory.getLogger(WeChatKefuInit.class); - @DubboReference(version = "1.0.0", group = "system", check = false) + @DubboReference(version = "1.0.0", group = "system", check = false, retries = 0) private SysConfigService sysConfigService; private static WeChatKefuConfig weChatConfig; diff --git a/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/kefu/core/KefuAccessTokenManager.java b/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/kefu/core/KefuAccessTokenManager.java index 19fb4d7c..0c999ffc 100644 --- a/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/kefu/core/KefuAccessTokenManager.java +++ b/urbanLifelineServ/common/common-wechat/src/main/java/org/xyzh/common/wechat/kefu/core/KefuAccessTokenManager.java @@ -29,7 +29,7 @@ public class KefuAccessTokenManager { private final RestTemplate restTemplate = new RestTemplate(); - @DubboReference(version = "1.0.0", group = "system", check = false) + @DubboReference(version = "1.0.0", group = "system", check = false, retries = 0) private SysConfigService sysConfigService; private String corpId; diff --git a/urbanLifelineServ/file/src/main/java/org/xyzh/file/controller/FileController.java b/urbanLifelineServ/file/src/main/java/org/xyzh/file/controller/FileController.java index fece2621..b3107973 100644 --- a/urbanLifelineServ/file/src/main/java/org/xyzh/file/controller/FileController.java +++ b/urbanLifelineServ/file/src/main/java/org/xyzh/file/controller/FileController.java @@ -1,5 +1,8 @@ package org.xyzh.file.controller; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -94,9 +97,10 @@ public class FileController { ResultDomain fileInfo = fileService.getFileById(fileId); String filename = fileInfo.getData() != null ? fileInfo.getData().getName() : "download"; + String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8).replace("+", "%20"); return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"") + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(result.getData()); } diff --git a/urbanLifelineServ/file/src/main/java/org/xyzh/file/service/impl/FileServiceImpl.java b/urbanLifelineServ/file/src/main/java/org/xyzh/file/service/impl/FileServiceImpl.java index fc56e728..0bec0c52 100644 --- a/urbanLifelineServ/file/src/main/java/org/xyzh/file/service/impl/FileServiceImpl.java +++ b/urbanLifelineServ/file/src/main/java/org/xyzh/file/service/impl/FileServiceImpl.java @@ -9,6 +9,8 @@ import org.springframework.util.DigestUtils; import org.springframework.web.multipart.MultipartFile; 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.LoginDomain; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.file.config.MinioConfig; import org.xyzh.file.mapper.FileMapper; @@ -53,65 +55,7 @@ public class FileServiceImpl implements FileService { if (file == null || file.isEmpty()) { return ResultDomain.failure("文件不能为空"); } - - // 生成文件信息 - String originalFilename = file.getOriginalFilename(); - String extension = getFileExtension(originalFilename); - String contentType = file.getContentType(); - long size = file.getSize(); - - // 生成唯一的对象名称 - String objectName = generateObjectName(originalFilename, module); - - // 计算文件MD5 - String md5Hash = calculateMD5(file.getBytes()); - - // 上传到MinIO - String bucketName = minioConfig.getBucketName(); - boolean uploadSuccess = minioUtil.uploadFile( - bucketName, - objectName, - file.getInputStream(), - size, - contentType - ); - - if (!uploadSuccess) { - return ResultDomain.failure("文件上传到MinIO失败"); - } - - // 保存到数据库 - TbSysFileDTO fileDTO = new TbSysFileDTO(); - String fileId = UUID.randomUUID().toString(); - fileDTO.setOptsn(UUID.randomUUID().toString()); - fileDTO.setFileId(fileId); - fileDTO.setName(originalFilename); - fileDTO.setPath(objectName); - fileDTO.setSize(size); - fileDTO.setType(extension); - fileDTO.setStorageType("MINIO"); - fileDTO.setMimeType(contentType); - // URL 设为 NULL,前端通过后端接口 /api/file/download/{fileId} 下载 - fileDTO.setUrl(null); - fileDTO.setStatus("NORMAL"); - fileDTO.setModule(module); - fileDTO.setBusinessId(businessId); - fileDTO.setObjectName(objectName); - fileDTO.setBucketName(bucketName); - fileDTO.setMd5Hash(md5Hash); - fileDTO.setExtension(extension); - fileDTO.setCreateTime(new java.util.Date()); - - int result = fileMapper.insertFile(fileDTO); - if (result <= 0) { - // 如果数据库保存失败,删除MinIO中的文件 - minioUtil.deleteFile(bucketName, objectName); - return ResultDomain.failure("文件信息保存失败"); - } - - logger.info("文件上传成功: {}, 大小: {} bytes", originalFilename, size); - return ResultDomain.success("文件上传成功", fileDTO); - + return uploadFileBytesWithUser(file.getBytes(), file.getOriginalFilename(), file.getContentType(), module, businessId, getCurrentUserId()); } catch (Exception e) { logger.error("文件上传失败", e); return ResultDomain.failure("文件上传失败: " + e.getMessage()); @@ -283,16 +227,6 @@ public class FileServiceImpl implements FileService { } } - /** - * @description 上传新版本文件(用于文件更新,fileRootId保持一致,version递增) - * @param file 文件对象 - * @param module 所属模块 - * @param businessId 业务ID - * @param fileRootId 文件根ID(多版本一致) - * @return ResultDomain 上传结果,包含新版本文件信息 - * @author yslg - * @since 2025-12-18 - */ @Override public ResultDomain uploadFileVersion(MultipartFile file, String module, String businessId, String fileRootId) { try { @@ -302,96 +236,105 @@ public class FileServiceImpl implements FileService { if (fileRootId == null || fileRootId.isEmpty()) { return ResultDomain.failure("文件根ID不能为空"); } + return uploadFileBytesVersionWithUser(file.getBytes(), file.getOriginalFilename(), file.getContentType(), module, businessId, fileRootId, getCurrentUserId()); + } catch (Exception e) { + logger.error("新版本文件上传失败", e); + return ResultDomain.failure("文件上传失败: " + e.getMessage()); + } + } + + @Override + public ResultDomain uploadFileBytes(byte[] fileBytes, String fileName, String contentType, String module, String businessId) { + return uploadFileBytesWithUser(fileBytes, fileName, contentType, module, businessId, getCurrentUserId()); + } + + @Override + public ResultDomain uploadFileBytesWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String uploaderUserId) { + try { + if (fileBytes == null || fileBytes.length == 0) { + return ResultDomain.failure("文件不能为空"); + } + + String extension = getFileExtension(fileName); + long size = fileBytes.length; + String objectName = generateObjectName(fileName, module); + String md5Hash = calculateMD5(fileBytes); + + String bucketName = minioConfig.getBucketName(); + java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(fileBytes); + boolean uploadSuccess = minioUtil.uploadFile(bucketName, objectName, inputStream, size, contentType); + + if (!uploadSuccess) { + return ResultDomain.failure("文件上传到MinIO失败"); + } + + TbSysFileDTO fileDTO = new TbSysFileDTO(); + String fileId = UUID.randomUUID().toString().replace("-", ""); + fileDTO.setOptsn(UUID.randomUUID().toString()); + fileDTO.setFileId(fileId); + fileDTO.setName(fileName); + fileDTO.setPath(objectName); + fileDTO.setUrl(null); + fileDTO.setSize(size); + fileDTO.setMimeType(contentType); + fileDTO.setExtension(extension); + fileDTO.setMd5Hash(md5Hash); + fileDTO.setModule(module); + fileDTO.setBusinessId(businessId); + fileDTO.setStorageType("MINIO"); + fileDTO.setObjectName(objectName); + fileDTO.setBucketName(bucketName); + fileDTO.setVersion(1); + fileDTO.setFileRootId(fileId); + fileDTO.setCreator(uploaderUserId); + fileDTO.setUploader(uploaderUserId); + fileDTO.setCreateTime(new java.util.Date()); + + int result = fileMapper.insertFile(fileDTO); + if (result <= 0) { + minioUtil.deleteFile(bucketName, objectName); + return ResultDomain.failure("文件信息保存失败"); + } + + logger.info("字节数组文件上传成功: {}, uploader: {}", fileName, uploaderUserId); + return ResultDomain.success("文件上传成功", fileDTO); + + } catch (Exception e) { + logger.error("字节数组文件上传失败", e); + return ResultDomain.failure("文件上传失败: " + e.getMessage()); + } + } + + @Override + public ResultDomain uploadFileBytesVersion(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId) { + return uploadFileBytesVersionWithUser(fileBytes, fileName, contentType, module, businessId, fileRootId, getCurrentUserId()); + } + + @Override + public ResultDomain uploadFileBytesVersionWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId, String uploaderUserId) { + try { + if (fileBytes == null || fileBytes.length == 0) { + return ResultDomain.failure("文件不能为空"); + } + if (fileRootId == null || fileRootId.isEmpty()) { + return ResultDomain.failure("文件根ID不能为空"); + } // 1. 获取当前最大版本号 Integer maxVersion = fileMapper.selectMaxVersionByFileRootId(fileRootId); int newVersion = (maxVersion != null ? maxVersion : 0) + 1; // 2. 生成文件信息 - String originalFilename = file.getOriginalFilename(); - String extension = getFileExtension(originalFilename); - String contentType = file.getContentType(); - long size = file.getSize(); - - // 3. 生成唯一的对象名称 - String objectName = generateObjectName(originalFilename, module); - - // 4. 计算文件MD5 - String md5Hash = calculateMD5(file.getBytes()); - - // 5. 上传到MinIO - String bucketName = minioConfig.getBucketName(); - boolean uploadSuccess = minioUtil.uploadFile( - bucketName, - objectName, - file.getInputStream(), - size, - contentType - ); - - if (!uploadSuccess) { - return ResultDomain.failure("文件上传到MinIO失败"); - } - - // 6. 构建文件访问URL - String fileUrl = minioConfig.buildFileUrl(objectName); - - // 7. 保存到数据库(新版本记录) - TbSysFileDTO fileDTO = new TbSysFileDTO(); - fileDTO.setOptsn(UUID.randomUUID().toString()); - fileDTO.setFileId(UUID.randomUUID().toString()); - fileDTO.setFileRootId(fileRootId); - fileDTO.setVersion(newVersion); - fileDTO.setName(originalFilename); - fileDTO.setPath(objectName); - fileDTO.setSize(size); - fileDTO.setType(extension); - fileDTO.setStorageType("MINIO"); - fileDTO.setMimeType(contentType); - fileDTO.setUrl(fileUrl); - fileDTO.setStatus("NORMAL"); - fileDTO.setModule(module); - fileDTO.setBusinessId(businessId); - fileDTO.setObjectName(objectName); - fileDTO.setBucketName(bucketName); - fileDTO.setMd5Hash(md5Hash); - fileDTO.setExtension(extension); - fileDTO.setCreateTime(new java.util.Date()); - - int result = fileMapper.insertFile(fileDTO); - if (result <= 0) { - // 如果数据库保存失败,删除MinIO中的文件 - minioUtil.deleteFile(bucketName, objectName); - return ResultDomain.failure("文件信息保存失败"); - } - - logger.info("新版本文件上传成功: {}, version: {}, fileRootId: {}", originalFilename, newVersion, fileRootId); - return ResultDomain.success("文件上传成功", fileDTO); - - } catch (Exception e) { - logger.error("新版本文件上传失败", e); - return ResultDomain.failure("文件上传失败: " + e.getMessage()); - } - } - - @Override - public ResultDomain uploadFileBytes(byte[] fileBytes, String fileName, String contentType, String module, String businessId) { - try { - if (fileBytes == null || fileBytes.length == 0) { - return ResultDomain.failure("文件不能为空"); - } - - // 生成文件信息 String extension = getFileExtension(fileName); long size = fileBytes.length; - // 生成唯一的对象名称 + // 3. 生成唯一的对象名称 String objectName = generateObjectName(fileName, module); - // 计算文件MD5 + // 4. 计算文件MD5 String md5Hash = calculateMD5(fileBytes); - // 上传到MinIO + // 5. 上传到MinIO String bucketName = minioConfig.getBucketName(); java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(fileBytes); boolean uploadSuccess = minioUtil.uploadFile( @@ -405,41 +348,45 @@ public class FileServiceImpl implements FileService { if (!uploadSuccess) { return ResultDomain.failure("文件上传到MinIO失败"); } + + // 6. 构建文件访问URL + String fileUrl = minioConfig.buildFileUrl(objectName); - // 创建文件DTO + // 7. 保存到数据库(新版本记录) TbSysFileDTO fileDTO = new TbSysFileDTO(); - String fileId = UUID.randomUUID().toString().replace("-", ""); fileDTO.setOptsn(UUID.randomUUID().toString()); - fileDTO.setFileId(fileId); + fileDTO.setFileId(UUID.randomUUID().toString()); + fileDTO.setFileRootId(fileRootId); + fileDTO.setVersion(newVersion); fileDTO.setName(fileName); fileDTO.setPath(objectName); - // URL 设为 NULL,前端通过后端接口 /api/file/download/{fileId} 下载 - fileDTO.setUrl(null); fileDTO.setSize(size); + fileDTO.setType(extension); + fileDTO.setStorageType("MINIO"); fileDTO.setMimeType(contentType); - fileDTO.setExtension(extension); - fileDTO.setMd5Hash(md5Hash); + fileDTO.setUrl(fileUrl); + fileDTO.setStatus("NORMAL"); fileDTO.setModule(module); fileDTO.setBusinessId(businessId); - fileDTO.setStorageType("MINIO"); fileDTO.setObjectName(objectName); fileDTO.setBucketName(bucketName); - fileDTO.setVersion(1); - fileDTO.setFileRootId(fileId); + fileDTO.setMd5Hash(md5Hash); + fileDTO.setExtension(extension); + fileDTO.setCreator(uploaderUserId); + fileDTO.setUploader(uploaderUserId); fileDTO.setCreateTime(new java.util.Date()); - // 保存到数据库 int result = fileMapper.insertFile(fileDTO); if (result <= 0) { minioUtil.deleteFile(bucketName, objectName); return ResultDomain.failure("文件信息保存失败"); } - logger.info("字节数组文件上传成功: {}", fileName); + logger.info("新版本文件上传成功(bytes): {}, version: {}, fileRootId: {}, uploader: {}", fileName, newVersion, fileRootId, uploaderUserId); return ResultDomain.success("文件上传成功", fileDTO); } catch (Exception e) { - logger.error("字节数组文件上传失败", e); + logger.error("新版本文件上传失败(bytes)", e); return ResultDomain.failure("文件上传失败: " + e.getMessage()); } } @@ -471,4 +418,19 @@ public class FileServiceImpl implements FileService { private String calculateMD5(byte[] data) { return DigestUtils.md5DigestAsHex(data); } + + /** + * 获取当前登录用户ID(支持未登录场景) + */ + private String getCurrentUserId() { + try { + LoginDomain loginDomain = LoginUtil.getCurrentLogin(); + if (loginDomain != null && loginDomain.getUser() != null) { + return loginDomain.getUser().getUserId(); + } + } catch (Exception e) { + logger.debug("获取当前登录用户失败: {}", e.getMessage()); + } + return null; + } } diff --git a/urbanLifelineServ/file/src/main/resources/mapper/FileMapper.xml b/urbanLifelineServ/file/src/main/resources/mapper/FileMapper.xml index 166cfd8c..b60a4115 100644 --- a/urbanLifelineServ/file/src/main/resources/mapper/FileMapper.xml +++ b/urbanLifelineServ/file/src/main/resources/mapper/FileMapper.xml @@ -62,6 +62,8 @@ , bucket_name , md5_hash , extension + , file_root_id + , version ) VALUES ( #{optsn}, #{fileId}, #{name}, #{path}, #{size} @@ -86,6 +88,8 @@ , #{bucketName} , #{md5Hash} , #{extension} + , #{fileRootId} + , #{version} ) diff --git a/urbanLifelineServ/message/src/main/java/org/xyzh/message/config/DynamicConfigLoader.java b/urbanLifelineServ/message/src/main/java/org/xyzh/message/config/DynamicConfigLoader.java index 497f6a15..7aae33a3 100644 --- a/urbanLifelineServ/message/src/main/java/org/xyzh/message/config/DynamicConfigLoader.java +++ b/urbanLifelineServ/message/src/main/java/org/xyzh/message/config/DynamicConfigLoader.java @@ -27,7 +27,7 @@ public class DynamicConfigLoader implements ApplicationRunner { private static final Logger logger = LoggerFactory.getLogger(DynamicConfigLoader.class); - @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false) + @DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0) private SysConfigService sysConfigService; @Autowired(required = false) diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/KnowledgeInit.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/KnowledgeInit.java index 38c67e89..8025c61f 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/KnowledgeInit.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/KnowledgeInit.java @@ -30,10 +30,10 @@ public class KnowledgeInit { private static final String CATEGORY_INTERNAL = "internal"; private static final String CATEGORY_EXTERNAL = "external"; - @DubboReference(version = "1.0.0", group = "ai", timeout = 30000) + @DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0) private KnowledgeService knowledgeService; - @DubboReference(version = "1.0.0", group = "system", timeout = 30000) + @DubboReference(version = "1.0.0", group = "system", timeout = 30000, retries = 0) private SysConfigService sysConfigService; @Bean diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseChatServiceImpl.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseChatServiceImpl.java index 1d17146e..71fe9f3e 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseChatServiceImpl.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseChatServiceImpl.java @@ -33,7 +33,7 @@ public class WorkcaseChatServiceImpl implements WorkcaseChatService{ private static final String CHAT_COUNT_KEY_PREFIX = "workcase:chat:count:"; private static final int TRANSFER_HUMAN_THRESHOLD = 3; - @DubboReference(version = "1.0.0", group = "ai", check = false) + @DubboReference(version = "1.0.0", group = "ai", check = false, retries = 0) private AgentChatService agentChatService; @Autowired diff --git a/urbanLifelineWeb/example/DocumentSegmentDialog.vue b/urbanLifelineWeb/example/DocumentSegmentDialog.vue new file mode 100644 index 00000000..906005e1 --- /dev/null +++ b/urbanLifelineWeb/example/DocumentSegmentDialog.vue @@ -0,0 +1,855 @@ + + + + + + diff --git a/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts b/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts index 379edaf5..aaa06ee5 100644 --- a/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts +++ b/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts @@ -6,15 +6,21 @@ // ========== 组件模块 ========== declare module 'shared/components' { export const FileUpload: any + export const FileHistory: any export const DynamicFormItem: any export const IframeView: any } -declare module 'shared/components/FileUpload' { +declare module 'shared/components/file/FileUpload' { import { DefineComponent } from 'vue' const FileUpload: DefineComponent<{}, {}, any> export default FileUpload } +declare module 'shared/components/file/FileHistory' { + import { DefineComponent } from 'vue' + const FileHistory: DefineComponent<{}, {}, any> + export default FileHistory +} declare module 'shared/components/DynamicFormItem' { import { DefineComponent } from 'vue' @@ -27,7 +33,17 @@ declare module 'shared/components/iframe/IframeView.vue' { const IframeView: DefineComponent<{}, {}, any> export default IframeView } +declare module 'shared/components/ai/knowledge/DocumentSegment.vue' { + import { DefineComponent } from 'vue' + const DocumentSegment: DefineComponent<{}, {}, any> + export default DocumentSegment +} +declare module 'shared/components/ai/knowledge/DocumentDetail.vue' { + import { DefineComponent } from 'vue' + const DocumentDetail: DefineComponent<{}, {}, any> + export default DocumentDetail +} // ========== API 模块 ========== declare module 'shared/api' { export const api: any diff --git a/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue b/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue index f1258a72..4ea8e16f 100644 --- a/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue +++ b/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue @@ -68,21 +68,22 @@ import { ref, computed, watch } from 'vue' import { useRouter, useRoute } from 'vue-router' import { - ChatDotRound, - Grid, - Connection, - Document, - Service, - DArrowLeft, - DArrowRight, + MessageCircle as ChatDotRound, + LayoutGrid as Grid, + Link as Connection, + FileText as Document, + Headphones as Service, + ChevronsLeft as DArrowLeft, + ChevronsRight as DArrowRight, User, - Setting, - SwitchButton, - Refresh, - Back -} from '@element-plus/icons-vue' + Settings as Setting, + Power as SwitchButton, + RefreshCw as Refresh, + ArrowLeft as Back, + PanelLeftClose, + PanelLeftOpen +} from 'lucide-vue-next' import { IframeView } from 'shared/components' -import { PanelLeftClose, PanelLeftOpen } from 'lucide-vue-next' import { ElMessage } from 'element-plus' import type { MenuItem } from 'shared/types' diff --git a/urbanLifelineWeb/packages/platform/src/types/shared.d.ts b/urbanLifelineWeb/packages/platform/src/types/shared.d.ts index 379edaf5..71c8011c 100644 --- a/urbanLifelineWeb/packages/platform/src/types/shared.d.ts +++ b/urbanLifelineWeb/packages/platform/src/types/shared.d.ts @@ -6,15 +6,21 @@ // ========== 组件模块 ========== declare module 'shared/components' { export const FileUpload: any + export const FileHistory: any export const DynamicFormItem: any export const IframeView: any } -declare module 'shared/components/FileUpload' { +declare module 'shared/components/file/FileUpload' { import { DefineComponent } from 'vue' const FileUpload: DefineComponent<{}, {}, any> export default FileUpload } +declare module 'shared/components/file/FileHistory' { + import { DefineComponent } from 'vue' + const FileHistory: DefineComponent<{}, {}, any> + export default FileHistory +} declare module 'shared/components/DynamicFormItem' { import { DefineComponent } from 'vue' @@ -28,6 +34,17 @@ declare module 'shared/components/iframe/IframeView.vue' { export default IframeView } +declare module 'shared/components/ai/knowledge/DocumentSegment.vue' { + import { DefineComponent } from 'vue' + const DocumentSegment: DefineComponent<{}, {}, any> + export default DocumentSegment +} + +declare module 'shared/components/ai/knowledge/DocumentDetail.vue' { + import { DefineComponent } from 'vue' + const DocumentDetail: DefineComponent<{}, {}, any> + export default DocumentDetail +} // ========== API 模块 ========== declare module 'shared/api' { export const api: any diff --git a/urbanLifelineWeb/packages/platform/src/views/public/Agents/AgentPlatformView.vue b/urbanLifelineWeb/packages/platform/src/views/public/Agents/AgentPlatformView.vue index 5e56e3fc..8ef8a202 100644 --- a/urbanLifelineWeb/packages/platform/src/views/public/Agents/AgentPlatformView.vue +++ b/urbanLifelineWeb/packages/platform/src/views/public/Agents/AgentPlatformView.vue @@ -53,7 +53,7 @@ + + diff --git a/urbanLifelineWeb/packages/shared/src/components/ai/knowledge/index.ts b/urbanLifelineWeb/packages/shared/src/components/ai/knowledge/index.ts new file mode 100644 index 00000000..782bc94a --- /dev/null +++ b/urbanLifelineWeb/packages/shared/src/components/ai/knowledge/index.ts @@ -0,0 +1,2 @@ +export { default as DocumentSegment } from './documentSegment/DocumentSegment.vue' +export { default as DocumentDetail } from './documentDetail/DocumentDetail.vue' \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.scss b/urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.scss new file mode 100644 index 00000000..c27cb160 --- /dev/null +++ b/urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.scss @@ -0,0 +1,33 @@ +.file-history-dialog { + .el-dialog__body { + padding: 16px 20px; + } + + .file-history-table { + width: 100%; + + .file-name-cell { + display: flex; + align-items: center; + gap: 8px; + + .file-icon { + color: #409eff; + font-size: 16px; + } + } + } + + .action-buttons { + display: flex !important; + flex-direction: column !important; + align-items: center !important; + justify-content: center !important; + // gap: 4px; + // width: 100%; + + .el-button { + margin: 0 !important; + } + } +} diff --git a/urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.vue b/urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.vue new file mode 100644 index 00000000..c22c2eab --- /dev/null +++ b/urbanLifelineWeb/packages/shared/src/components/file/fileHistory/FileHistory.vue @@ -0,0 +1,155 @@ + + + + + \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/components/fileupload/FileUpload.scss b/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.scss similarity index 100% rename from urbanLifelineWeb/packages/shared/src/components/fileupload/FileUpload.scss rename to urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.scss diff --git a/urbanLifelineWeb/packages/shared/src/components/fileupload/FileUpload.vue b/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.vue similarity index 100% rename from urbanLifelineWeb/packages/shared/src/components/fileupload/FileUpload.vue rename to urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUpload.vue diff --git a/urbanLifelineWeb/packages/shared/src/components/fileupload/FileUploadExample.vue b/urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUploadExample.vue similarity index 100% rename from urbanLifelineWeb/packages/shared/src/components/fileupload/FileUploadExample.vue rename to urbanLifelineWeb/packages/shared/src/components/file/fileupload/FileUploadExample.vue diff --git a/urbanLifelineWeb/packages/shared/src/components/file/index.ts b/urbanLifelineWeb/packages/shared/src/components/file/index.ts new file mode 100644 index 00000000..d1f2c430 --- /dev/null +++ b/urbanLifelineWeb/packages/shared/src/components/file/index.ts @@ -0,0 +1,2 @@ +export { default as FileUpload } from './fileupload/FileUpload.vue' +export { default as FileHistory } from './fileHistory/FileHistory.vue' \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/components/fileupload/index.ts b/urbanLifelineWeb/packages/shared/src/components/fileupload/index.ts deleted file mode 100644 index 7c158ff5..00000000 --- a/urbanLifelineWeb/packages/shared/src/components/fileupload/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as FileUpload } from './FileUpload.vue' \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/components/index.ts b/urbanLifelineWeb/packages/shared/src/components/index.ts index bcedc31d..f1fecff7 100644 --- a/urbanLifelineWeb/packages/shared/src/components/index.ts +++ b/urbanLifelineWeb/packages/shared/src/components/index.ts @@ -1,7 +1,6 @@ -export * from './fileupload' export * from './base' export * from './dynamicFormItem' export * from './ai' - +export * from './file' // 通用视图组件 export { default as IframeView } from './iframe/IframeView.vue' \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/src/types/ai/aiKnowledge.ts b/urbanLifelineWeb/packages/shared/src/types/ai/aiKnowledge.ts index 16984b9d..958c1350 100644 --- a/urbanLifelineWeb/packages/shared/src/types/ai/aiKnowledge.ts +++ b/urbanLifelineWeb/packages/shared/src/types/ai/aiKnowledge.ts @@ -1,4 +1,4 @@ -import type { BaseDTO } from '../base' +import type { BaseDTO, BaseVO } from '../base' /** * 知识库配置 @@ -58,6 +58,41 @@ export interface TbKnowledgeFile extends BaseDTO { version?: number } +/** + * 知识库文件视图对象(关联文件信息) + */ +export interface KnowledgeFileVO extends BaseVO { + // TbKnowledgeFile 的字段 + /** 知识库ID */ + knowledgeId?: string + /** 文件ID */ + fileId?: string + /** 文件根ID */ + fileRootId?: string + /** Dify文件ID */ + difyFileId?: string + /** 文件版本 */ + version?: number + + // TbSysFile 的字段 + /** 文件名 */ + fileName?: string + /** 文件路径 */ + filePath?: string + /** 文件大小(字节) */ + fileSize?: number + /** 文件MIME类型 */ + fileMimeType?: string + /** 文件访问URL */ + fileUrl?: string + /** 文件扩展名 */ + fileExtension?: string + /** 文件MD5值 */ + fileMd5Hash?: string + /** 上传人员名称 */ + uploaderName?: string +} + /** * 文档分段请求体 */ diff --git a/urbanLifelineWeb/packages/shared/src/types/base/index.ts b/urbanLifelineWeb/packages/shared/src/types/base/index.ts index dd58a573..3f1791f3 100644 --- a/urbanLifelineWeb/packages/shared/src/types/base/index.ts +++ b/urbanLifelineWeb/packages/shared/src/types/base/index.ts @@ -1,3 +1,13 @@ +/** + * 排序字段 + */ +export interface OrderField { + /** 排序字段 */ + field: string + /** 排序方式 */ + order: 'ASC' | 'DESC' +} + /** * 基础DTO - 包含所有数据传输对象的公共字段 */ @@ -20,6 +30,14 @@ export interface BaseDTO { deleteTime?: string /** 是否已删除 */ deleted?: boolean + /** 数量限制 */ + limit?: number + /** 开始时间 */ + startTime?: string + /** 结束时间 */ + endTime?: string + /** 排序字段列表 */ + orderFields?: OrderField[] } /** @@ -28,11 +46,4 @@ export interface BaseDTO { export interface BaseVO extends BaseDTO { /** 主键ID */ id?: string - - orderTypes?: OrderType[] -} - -export interface OrderType { - field: string - order: 'ASC' | 'DESC' } \ No newline at end of file diff --git a/urbanLifelineWeb/packages/shared/vite.config.ts b/urbanLifelineWeb/packages/shared/vite.config.ts index 25b90299..adeba1a2 100644 --- a/urbanLifelineWeb/packages/shared/vite.config.ts +++ b/urbanLifelineWeb/packages/shared/vite.config.ts @@ -34,9 +34,12 @@ export default defineConfig({ exposes: { // ========== 组件模块 ========== './components': './src/components/index.ts', - './components/FileUpload': './src/components/fileupload/FileUpload.vue', + './components/file/FileUpload': './src/components/file/fileupload/FileUpload.vue', + './components/file/FileHistory': './src/components/file/fileHistory/FileHistory.vue', './components/DynamicFormItem': './src/components/dynamicFormItem/DynamicFormItem.vue', './components/iframe/IframeView.vue': './src/components/iframe/IframeView.vue', + './components/ai/knowledge/DocumentSegment.vue': './src/components/ai/knowledge/documentSegment/DocumentSegment.vue', + './components/ai/knowledge/DocumentDetail.vue': './src/components/ai/knowledge/documentDetail/DocumentDetail.vue', // ========== API 模块 ========== './api': './src/api/index.ts', @@ -72,7 +75,7 @@ export default defineConfig({ vue: {}, 'vue-router': {}, 'element-plus': {}, - '@element-plus/icons-vue': {}, + 'lucide-vue-next': {}, axios: {} } }) diff --git a/urbanLifelineWeb/packages/workcase/src/types/shared.d.ts b/urbanLifelineWeb/packages/workcase/src/types/shared.d.ts index 379edaf5..3c298ef8 100644 --- a/urbanLifelineWeb/packages/workcase/src/types/shared.d.ts +++ b/urbanLifelineWeb/packages/workcase/src/types/shared.d.ts @@ -6,15 +6,21 @@ // ========== 组件模块 ========== declare module 'shared/components' { export const FileUpload: any + export const FileHistory: any export const DynamicFormItem: any export const IframeView: any } -declare module 'shared/components/FileUpload' { +declare module 'shared/components/file/FileUpload' { import { DefineComponent } from 'vue' const FileUpload: DefineComponent<{}, {}, any> export default FileUpload } +declare module 'shared/components/file/FileHistory' { + import { DefineComponent } from 'vue' + const FileHistory: DefineComponent<{}, {}, any> + export default FileHistory +} declare module 'shared/components/DynamicFormItem' { import { DefineComponent } from 'vue' @@ -28,6 +34,18 @@ declare module 'shared/components/iframe/IframeView.vue' { export default IframeView } +declare module 'shared/components/ai/knowledge/DocumentSegment.vue' { + import { DefineComponent } from 'vue' + const DocumentSegment: DefineComponent<{}, {}, any> + export default DocumentSegment +} + +declare module 'shared/components/ai/knowledge/DocumentDetail.vue' { + import { DefineComponent } from 'vue' + const DocumentDetail: DefineComponent<{}, {}, any> + export default DocumentDetail +} + // ========== API 模块 ========== declare module 'shared/api' { export const api: any diff --git a/urbanLifelineWeb/packages/workcase/src/views/admin/customerChat/CustomerChatView.vue b/urbanLifelineWeb/packages/workcase/src/views/admin/customerChat/CustomerChatView.vue index 639915e7..6a319055 100644 --- a/urbanLifelineWeb/packages/workcase/src/views/admin/customerChat/CustomerChatView.vue +++ b/urbanLifelineWeb/packages/workcase/src/views/admin/customerChat/CustomerChatView.vue @@ -94,7 +94,7 @@