diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/CommentUtil.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/CommentUtil.java new file mode 100644 index 0000000..0ebe210 --- /dev/null +++ b/common/service-utils/src/main/java/cn/bunny/common/service/utils/CommentUtil.java @@ -0,0 +1,51 @@ +package cn.bunny.common.service.utils; + +import cn.bunny.vo.user.comment.CommentVo; + +import java.util.ArrayList; +import java.util.List; + +public class CommentUtil { + /** + * 构建树型结构 + * + * @param commentList 评论列表 + * @return 结构列表 + */ + public static List buildTree(List commentList) { + // 构建树形结构 + List tree = new ArrayList<>(); + // 遍历评论列表 + for (CommentVo comment : commentList) { + // 找到顶级评论(没有父评论) + if (comment.getPCommentId() == 0) { + // 递归构建子评论 + comment.setChildren(getChildren(comment.getId(), commentList)); + tree.add(comment); + } + } + return tree; + } + + /** + * 递归获取子评论 + * + * @param commentId 当前评论ID + * @param commentList 评论列表 + * @return 子评论列表 + */ + private static List getChildren(Long commentId, List commentList) { + List children = new ArrayList<>(); + + // 遍历评论列表 + for (CommentVo comment : commentList) { + // 找到当前评论的子评论 + if (Long.valueOf(comment.getPCommentId()).equals(commentId)) { + // 递归构建子评论的子评论 + comment.setChildren(getChildren(comment.getId(), commentList)); + children.add(comment); + } + } + return children; + } +} diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/FileUtil.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/FileUtil.java index aa60bc2..3daf5cd 100644 --- a/common/service-utils/src/main/java/cn/bunny/common/service/utils/FileUtil.java +++ b/common/service-utils/src/main/java/cn/bunny/common/service/utils/FileUtil.java @@ -1,9 +1,12 @@ package cn.bunny.common.service.utils; -/** - * 计算 kb mb gb - */ +import org.springframework.stereotype.Component; + +@Component public class FileUtil { + /** + * * 获取文件大小字符串 + */ public static String getSize(Long fileSize) { double fileSizeInKB = fileSize / 1024.00; double fileSizeInMB = fileSizeInKB / 1024; diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java new file mode 100644 index 0000000..c1d83f6 --- /dev/null +++ b/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java @@ -0,0 +1,77 @@ +package cn.bunny.common.service.utils; + +import jakarta.annotation.PostConstruct; +import org.lionsoul.ip2region.xdb.Searcher; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.FileCopyUtils; + +import java.io.InputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class IpUtil { + private static Searcher searcher; + + /** + * 判断是否为合法 IP + */ + public static boolean checkIp(String ipAddress) { + String ip = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"; + Pattern pattern = Pattern.compile(ip); + Matcher matcher = pattern.matcher(ipAddress); + return matcher.matches(); + } + + /** + * 在服务启动时,将 ip2region 加载到内存中 + */ + @PostConstruct + private static void initIp2Region() { + try { + InputStream inputStream = new ClassPathResource("/ipdb/ip2region.xdb").getInputStream(); + byte[] bytes = FileCopyUtils.copyToByteArray(inputStream); + searcher = Searcher.newWithBuffer(bytes); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + /** + * 获取 ip 所属地址 + * + * @param ip ip + */ + public static String getIpRegion(String ip) { + if (ip.equals("0:0:0:0:0:0:0:1")) ip = "127.0.0.1"; + boolean isIp = checkIp(ip); + if (isIp) { + initIp2Region(); + try { + // searchIpInfo 的数据格式: 国家|区域|省份|城市|ISP + String searchIpInfo = searcher.search(ip); + String[] splitIpInfo = searchIpInfo.split("\\|"); + if (splitIpInfo.length > 0) { + if ("中国".equals(splitIpInfo[0])) { + // 国内属地返回省份 + return splitIpInfo[2]; + } else if ("0".equals(splitIpInfo[0])) { + if ("内网IP".equals(splitIpInfo[4])) { + // 内网 IP + return splitIpInfo[4]; + } else { + return ""; + } + } else { + // 国外属地返回国家 + return splitIpInfo[0]; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } else { + throw new IllegalArgumentException("非法的IP地址"); + } + } +} diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseHandlerUtil.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseHandlerUtil.java index 624a91a..2376798 100644 --- a/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseHandlerUtil.java +++ b/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseHandlerUtil.java @@ -9,4 +9,4 @@ public class ResponseHandlerUtil { ResponseUtil.out(response, Result.error(loginAuth)); return false; } -} \ No newline at end of file +} diff --git a/dao/src/main/java/cn/bunny/pojo/file/MinioFIlePath.java b/dao/src/main/java/cn/bunny/pojo/file/MinioFIlePath.java new file mode 100644 index 0000000..f283278 --- /dev/null +++ b/dao/src/main/java/cn/bunny/pojo/file/MinioFIlePath.java @@ -0,0 +1,18 @@ +package cn.bunny.pojo.file; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MinioFIlePath { + private String filename; + private String uuidFilename; + private String timeUuidFilename; + private String filepath; + private String bucketNameFilepath; +} diff --git a/dao/src/main/java/cn/bunny/pojo/result/constant/MinioConstant.java b/dao/src/main/java/cn/bunny/pojo/result/constant/MinioConstant.java new file mode 100644 index 0000000..c080b1f --- /dev/null +++ b/dao/src/main/java/cn/bunny/pojo/result/constant/MinioConstant.java @@ -0,0 +1,39 @@ +package cn.bunny.pojo.result.constant; + +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class MinioConstant { + public static final String favicon = "favicon"; + public static final String avatar = "avatar"; + public static final String article = "article"; + public static final String carousel = "carousel"; + public static final String feedback = "feedback"; + public static final String articleCovers = "articleCovers"; + public static final String articleAttachment = "articleAttachment"; + private static final Map typeMap = new HashMap<>(); + + static { + typeMap.put(favicon, "/favicon/"); + typeMap.put(avatar, "/avatar/"); + typeMap.put(article, "/article/"); + typeMap.put(carousel, "/carousel/"); + typeMap.put(feedback, "/feedback/"); + typeMap.put("articleImages", "/articleImages/"); + typeMap.put("articleVideo", "/articleVideo/"); + typeMap.put(articleCovers, "/articleCovers/"); + typeMap.put(articleAttachment, "/articleAttachment/"); + typeMap.put("images", "/images/"); + typeMap.put("video", "/video/"); + typeMap.put("default", "/default/"); + } + + public static String getType(String type) { + String value = typeMap.get(type); + if (value != null) return value; + throw new RuntimeException(FileMessageConstant.COMPOSE_OBJECT_EXCEPTION); + } +} diff --git a/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java b/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java index 5f717fd..a83d2f9 100644 --- a/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java +++ b/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java @@ -2,16 +2,23 @@ package cn.bunny.module.minio.utils; import cn.bunny.common.service.exception.BunnyException; import cn.bunny.module.minio.properties.MinioProperties; +import cn.bunny.pojo.file.MinioFIlePath; import cn.bunny.pojo.result.constant.FileMessageConstant; +import cn.bunny.pojo.result.constant.MinioConstant; import io.minio.*; import io.minio.messages.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.io.InputStream; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; +import java.util.UUID; /** * Minio操作工具类 简化操作步骤 @@ -26,16 +33,121 @@ public class MinioUtil { private MinioClient minioClient; /** - * 获取Minio全路径名 - * - * @param objectName 对象名称 - * @return 全路径 + * 获取Minio文件路径 */ - public String getFullPath(String objectName) { - String url = properties.getEndpointUrl(); + public static MinioFIlePath getMinioFilePath(String buckName, String minioPreType, MultipartFile file) { + String uuid = UUID.randomUUID().toString(); + // 定义日期时间格式 + LocalDateTime currentDateTime = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM-dd"); + String extension = ""; + + // 原始文件名 + String filename = file.getOriginalFilename(); + if (filename.contains(".")) { + extension = "." + filename.substring(filename.lastIndexOf(".") + 1); + } + + // UUID防止重名 + String uuidFilename = uuid + extension; + + // 拼接时间+UUID文件名 + String timeUuidFilename = currentDateTime.format(formatter) + "/" + uuidFilename;// 加上时间路径 + + // 上传根文件夹+拼接时间+UUID文件名 + String filepath = MinioConstant.getType(minioPreType) + timeUuidFilename; + + // 桶名称+上传根文件夹+拼接时间+UUID文件名 + String buckNameFilepath = "/" + buckName + MinioConstant.getType(minioPreType) + timeUuidFilename; + + // 设置及Minio基础信息 + MinioFIlePath minioFIlePath = new MinioFIlePath(); + minioFIlePath.setFilename(filename); + minioFIlePath.setUuidFilename(uuidFilename); + minioFIlePath.setTimeUuidFilename(timeUuidFilename); + minioFIlePath.setFilepath(filepath); + minioFIlePath.setBucketNameFilepath(buckNameFilepath); + + return minioFIlePath; + } + + /** + * 上传文件 + * + * @return 返回上传路径 + */ + public String uploadFile(MultipartFile file, String bucketName, String minioPreType) throws IOException { + return getUploadFilePath(file, bucketName, minioPreType); + } + + /** + * 上传文件 + * + * @return 返回上传路径 + */ + public String uploadFile(MultipartFile file, String minioPreType) throws IOException { + // 如果buckName为空,设置为默认的桶 String bucketName = properties.getBucketName(); - return url + "/" + bucketName + objectName; + return getUploadFilePath(file, bucketName, minioPreType); + } + + /** + * * 上传文件 + * + * @return 上传路径 + */ + private String getUploadFilePath(MultipartFile file, String bucketName, String minioPreType) throws IOException { + if (file != null) { + MinioFIlePath minioFile = getMinioFilePath(bucketName, minioPreType, file); + String bucketNameFilepath = minioFile.getBucketNameFilepath(); + String filepath = minioFile.getFilepath(); + + // 上传对象 + putObject(bucketName, filepath, file.getInputStream(), file.getSize()); + + // 设置图片地址 + return bucketNameFilepath; + } + return null; + } + + /** + * * 上传文件 + * + * @return 上传路径 + */ + public MinioFIlePath getUploadMinioObjectFilePath(MultipartFile file, String bucketName, String minioPreType) throws IOException { + if (file != null) { + MinioFIlePath minioFile = getMinioFilePath(bucketName, minioPreType, file); + String filepath = minioFile.getFilepath(); + + // 上传对象 + putObject(bucketName, filepath, file.getInputStream(), file.getSize()); + + // 设置图片地址 + return minioFile; + } + return null; + } + + /** + * * 上传文件并返回处理信息 + */ + public MinioFIlePath getUploadMinioObjectFilePath(MultipartFile file, String minioPreType) throws IOException { + // 如果buckName为空,设置为默认的桶 + String bucketName = properties.getBucketName(); + if (file != null) { + MinioFIlePath minioFile = getMinioFilePath(bucketName, minioPreType, file); + String filepath = minioFile.getFilepath(); + + // 上传对象 + putObject(bucketName, filepath, file.getInputStream(), file.getSize()); + + // 设置图片地址 + return minioFile; + } + return null; } /** @@ -56,7 +168,65 @@ public class MinioUtil { } throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); } - + + /** + * 获取默认bucket文件,并返回字节数组 + * + * @param objectName 对象名称 + * @return 文件流对象 + */ + public byte[] getBucketObjectByte(String objectName) { + // 如果buckName为空,设置为默认的桶 + String bucketName = properties.getBucketName(); + + try { + objectName = objectName.replace("/" + bucketName, ""); + GetObjectResponse getObjectResponse = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); + + return getObjectResponse.readAllBytes(); + } catch (Exception exception) { + exception.getStackTrace(); + } + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + /** + * 获取Minio全路径名,Object带有桶名称 + * + * @param objectName 对象名称 + * @return 全路径 + */ + public String getObjectNameFullPath(String objectName) { + String url = properties.getEndpointUrl(); + + return url + objectName; + } + + /** + * 获取Minio全路径名-默认桶名称 + * + * @param objectName 对象名称 + * @return 全路径 + */ + public String getDefaultBuckNameFullPath(String objectName) { + String url = properties.getEndpointUrl(); + String bucketName = properties.getBucketName(); + + return url + "/" + bucketName + objectName; + } + + /** + * 获取Minio全路径名,自定义桶名称 + * + * @param objectName 对象名称 + * @return 全路径 + */ + public String getBuckNameFullPath(String bucketName, String objectName) { + String url = properties.getEndpointUrl(); + + return url + "/" + bucketName + objectName; + } + /** * 判断桶是否存在 *