知识库历史文件

This commit is contained in:
2025-12-20 17:12:42 +08:00
parent dfd9bb8b95
commit 62850717eb
59 changed files with 2351 additions and 276 deletions

View File

@@ -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<TbKnowledgeFile> getFileHistory(@PathVariable("fileId") @NotBlank String fileRootId) {
@GetMapping("/file/{fileRootId}/history")
public ResultDomain<KnowledgeFileVO> getFileHistory(@PathVariable("fileRootId") @NotBlank String fileRootId) {
logger.info("获取文件历史: fileRootId={}", fileRootId);
return knowledgeService.getKnowledgeFileHistory(fileRootId);
}

View File

@@ -76,4 +76,14 @@ public interface TbKnowledgeFileMapper {
@Param("knowledgeId") String knowledgeId,
@Param("fileRootId") String fileRootId
);
/**
* 根据文件根ID查询最大版本的文件
*/
TbKnowledgeFile selectLatestVersionFile(@Param("fileRootId") String fileRootId);
/**
* 根据文件根ID查询所有版本包含文件详细信息
*/
List<KnowledgeFileVO> selectFileVersionsWithDetail(@Param("fileRootId") String fileRootId);
}

View File

@@ -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<TbKnowledgeFile> oldVersions = knowledgeFileMapper.selectFileVersions(fileRootId);
if (oldVersions == null || oldVersions.isEmpty()) {
// 3. 获取最大版本的旧文件
TbKnowledgeFile latestOldFile = knowledgeFileMapper.selectLatestVersionFile(fileRootId);
if (latestOldFile == null) {
return ResultDomain.failure("原文件不存在");
}
// 4. 上传新版本到minio
ResultDomain<TbSysFileDTO> 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<TbSysFileDTO> 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<TbKnowledgeFile> 文件历史版本列表
* @return ResultDomain<KnowledgeFileVO> 文件历史版本列表dataList
* @author yslg
* @since 2025-12-18
*/
@Override
public ResultDomain<TbKnowledgeFile> getKnowledgeFileHistory(String fileRootId) {
public ResultDomain<KnowledgeFileVO> getKnowledgeFileHistory(String fileRootId) {
if (!StringUtils.hasText(fileRootId)) {
return ResultDomain.failure("文件根ID不能为空");
}
List<TbKnowledgeFile> versions = knowledgeFileMapper.selectFileVersions(fileRootId);
return ResultDomain.success("查询成功", versions);
List<KnowledgeFileVO> versions = knowledgeFileMapper.selectFileVersionsWithDetail(fileRootId);
ResultDomain<KnowledgeFileVO> result = ResultDomain.success("查询成功");
result.setDataList(versions);
return result;
}
}

View File

@@ -33,6 +33,7 @@
<result column="file_url" property="fileUrl" jdbcType="VARCHAR"/>
<result column="file_extension" property="fileExtension" jdbcType="VARCHAR"/>
<result column="file_md5_hash" property="fileMd5Hash" jdbcType="VARCHAR"/>
<result column="uploader_name" property="uploaderName" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
@@ -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
</select>
@@ -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}
</select>
<select id="countFiles" resultType="long">
SELECT COUNT(*)
SELECT COUNT(DISTINCT file_root_id)
FROM ai.tb_knowledge_file
WHERE knowledge_id = #{knowledgeId} AND deleted = false
</select>
@@ -133,4 +150,31 @@
FROM ai.tb_knowledge_file
WHERE knowledge_id = #{knowledgeId} AND file_root_id = #{fileRootId}
</select>
<select id="selectLatestVersionFile" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM ai.tb_knowledge_file
WHERE file_root_id = #{fileRootId} AND deleted = false
ORDER BY version DESC
LIMIT 1
</select>
<select id="selectFileVersionsWithDetail" resultMap="KnowledgeFileVOResultMap">
SELECT
kf.optsn, kf.knowledge_id, kf.file_root_id, kf.file_id, kf.dify_file_id, kf.version,
kf.create_time, kf.update_time, kf.delete_time, kf.deleted,
f.name as file_name,
f.path as file_path,
f.size as file_size,
f.mime_type as file_mime_type,
f.url as file_url,
f.extension as file_extension,
f.md5_hash as file_md5_hash,
ui.username as uploader_name
FROM ai.tb_knowledge_file kf
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.file_root_id = #{fileRootId} AND kf.deleted = false
ORDER BY kf.version DESC
</select>
</mapper>

View File

@@ -162,10 +162,10 @@ public interface KnowledgeService {
/**
* @description 获取文件历史版本
* @param fileRootId 文件根ID
* @return ResultDomain<TbKnowledgeFile> 文件历史版本列表
* @return ResultDomain<KnowledgeFileVO> 文件历史版本列表dataList
* @author yslg
* @since 2025-12-18
*/
ResultDomain<TbKnowledgeFile> getKnowledgeFileHistory(String fileRootId);
ResultDomain<KnowledgeFileVO> getKnowledgeFileHistory(String fileRootId);
}

View File

@@ -57,4 +57,7 @@ public class KnowledgeFileVO extends BaseVO {
@Schema(description = "文件MD5值")
private String fileMd5Hash;
@Schema(description = "上传人员名称")
private String uploaderName;
}

View File

@@ -108,4 +108,47 @@ public interface FileService {
*/
ResultDomain<TbSysFileDTO> 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<TbSysFileDTO> 上传结果
* @author yslg
* @since 2025-12-20
*/
ResultDomain<TbSysFileDTO> 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<TbSysFileDTO> 上传结果,包含新版本文件信息
* @author yslg
* @since 2025-12-20
*/
ResultDomain<TbSysFileDTO> 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<TbSysFileDTO> 上传结果,包含新版本文件信息
* @author yslg
* @since 2025-12-20
*/
ResultDomain<TbSysFileDTO> uploadFileBytesVersionWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId, String uploaderUserId);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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<String> tokenHolder = (ThreadLocal<String>) filterClass.getField("TOKEN_HOLDER").get(null);
String token = tokenHolder.get();
if (StringUtils.hasText(token)) {
return token;
}
} catch (Exception e) {
// Dubbo Filter不存在或未加载忽略
}
return null;

View File

@@ -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<OrderField> orderFields;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<TbSysFileDTO> 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());
}

View File

@@ -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<TbSysFileDTO> 上传结果,包含新版本文件信息
* @author yslg
* @since 2025-12-18
*/
@Override
public ResultDomain<TbSysFileDTO> 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<TbSysFileDTO> uploadFileBytes(byte[] fileBytes, String fileName, String contentType, String module, String businessId) {
return uploadFileBytesWithUser(fileBytes, fileName, contentType, module, businessId, getCurrentUserId());
}
@Override
public ResultDomain<TbSysFileDTO> 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<TbSysFileDTO> 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<TbSysFileDTO> 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<TbSysFileDTO> 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;
}
}

View File

@@ -62,6 +62,8 @@
<if test="bucketName != null">, bucket_name</if>
<if test="md5Hash != null">, md5_hash</if>
<if test="extension != null">, extension</if>
<if test="fileRootId != null">, file_root_id</if>
<if test="version != null">, version</if>
) VALUES (
<!-- 必填字段值 -->
#{optsn}, #{fileId}, #{name}, #{path}, #{size}
@@ -86,6 +88,8 @@
<if test="bucketName != null">, #{bucketName}</if>
<if test="md5Hash != null">, #{md5Hash}</if>
<if test="extension != null">, #{extension}</if>
<if test="fileRootId != null">, #{fileRootId}</if>
<if test="version != null">, #{version}</if>
)
</insert>

View File

@@ -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)

View File

@@ -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

View File

@@ -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