From cc78cb8b2192a844688aad9646b65414fde915c2 Mon Sep 17 00:00:00 2001 From: Theo <971366405@qq.com> Date: Wed, 15 Jan 2025 16:34:21 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(WebMvcConfig):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=A0=BC=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 LocalDateTime 序列化支持,使用 NORM_DATETIME_PATTERN 格式 - 设置 GMT+8 时区 - 使用 DatePattern.NORM_DATETIME_PATTERN 替代硬编码的日期格式 --- .../java/com/youlai/boot/config/WebMvcConfig.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/youlai/boot/config/WebMvcConfig.java b/src/main/java/com/youlai/boot/config/WebMvcConfig.java index a865c163..ec938575 100644 --- a/src/main/java/com/youlai/boot/config/WebMvcConfig.java +++ b/src/main/java/com/youlai/boot/config/WebMvcConfig.java @@ -1,11 +1,14 @@ package com.youlai.boot.config; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import jakarta.validation.Validation; import jakarta.validation.Validator; import jakarta.validation.ValidatorFactory; @@ -21,6 +24,9 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.math.BigInteger; import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.List; /** @@ -50,8 +56,12 @@ public class WebMvcConfig implements WebMvcConfigurer { SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance); + simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer( + DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN).withZone(ZoneId.of( "GMT+8")) + )); objectMapper.registerModule(simpleModule); - objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + objectMapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN)); + jackson2HttpMessageConverter.setObjectMapper(objectMapper); converters.add(1, jackson2HttpMessageConverter); From 8bd38f630280b76b40aa95564b6e6dfc0d873699 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 21 Jan 2025 17:13:04 +0800 Subject: [PATCH 2/6] =?UTF-8?q?docs:=20=E6=96=87=E6=A1=A3=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E9=A1=B9=E7=9B=AE=E6=9B=B4=E6=96=B0=E7=9A=84=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 74 +++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 58315170..01a6f061 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,11 @@ ![](https://raw.gitmirror.com/youlaitech/image/main/docs/rainbow.png)
- 🔍 在线预览 | 📖 阅读文档 | 🌐English + 🔍 在线预览 | 📖 阅读文档
## 📢 项目简介 -**在线预览**: [https://vue3.youlai.tech](https://vue3.youlai.tech) - 基于 JDK 17、Spring Boot 3、Spring Security 6、JWT、Redis、Mybatis-Plus、Knife4j、Vue 3、Element-Plus 构建的前后端分离单体权限管理系统。 - **🚀 开发框架**: 使用 Spring Boot 3 和 Vue 3,以及 Element-Plus 等主流技术栈,实时更新。 @@ -37,7 +35,37 @@ - **🛠️ 功能模块**: 包括用户管理、角色管理、菜单管理、部门管理、字典管理等多个功能。 -- **📘 接口文档**: 自动生成接口文档,支持在线调试,提高开发效率。 + +## 🌈 项目地址 + +- **在线预览**:[https://vue.youlai.tech](https://vue.youlai.tech) +- **前端项目**:[vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) +- **接口文档**:[https://www.apifox.cn/apidoc](https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5) +- **项目文档**:[youlai-boot 企业级权限管理系统全功能详解](https://youlai.blog.csdn.net/article/details/145178880) +- **从0到1文档**:[从0到1搭建 youlai-boot 企业级权限管理系统](https://youlai.blog.csdn.net/article/details/145177011) + +## 🚀 项目启动 + +1. **克隆项目** + + ```bash + git clone https://gitee.com/youlaiorg/youlai-boot.git + ``` + +2. **数据库初始化** + + 执行 [youlai_boot.sql](sql/mysql8/youlai_boot.sql) 脚本完成数据库创建、表结构和基础数据的初始化。 + +3. **修改配置** + + [application-dev.yml](src/main/resources/application-dev.yml) 修改MySQL、Redis连接配置; + +4. **启动项目** + + 执行 [YoulaiBootApplication.java](src/main/java/com/youlai/boot/YoulaiBootApplication.java) 的 main 方法完成后端项目启动; + + 访问接口文档地址 [http://localhost:8989/doc.html](http://localhost:8989/doc.html) 验证项目启动是否成功。 + ## 📁 项目目录 ``` @@ -47,6 +75,7 @@ youlai-boot │ └── mysql8 # MySQL8 脚本 ├── src # 源码目录 │ ├── common # 公共模块 +│ │ ├── annotation # 注解定义 │ │ ├── base # 基础类 │ │ ├── constant # 常量 │ │ ├── enums # 枚举类型 @@ -71,7 +100,7 @@ youlai-boot │ │ ├── WebSocketConfig # WebSocket 自动装配配置 │ │ └── XxlJobConfig # XXL-JOB 自动装配配置 │ ├── core # 核心功能 -│ │ ├── annotation # 注解定义 + │ │ ├── aspect # 切面 │ │ │ ├── LogAspect # 日志切面 │ │ │ └── RepeatSubmitAspect # 防重提交切面 @@ -81,7 +110,7 @@ youlai-boot │ │ ├── handler # 处理器 │ │ │ ├── MyDataPermissionHandler # 数据权限处理器 │ │ │ └── MyMetaObjectHandler # 元对象字段填充处理器 -│ │ └── security # Security 安全中心 +│ │ └── security # Spring Security 安全模块 │ ├── modules # 业务模块 │ │ ├── member # 会员模块【业务模块演示】 │ │ ├── order # 订单模块【业务模块演示】 @@ -112,35 +141,8 @@ youlai-boot └── end ``` -## 🌺 前端工程 -| Gitee | Github | -|-------|------| -| [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | -## 🌈 接口文档 - -- `knife4j` 接口文档:[http://localhost:8989/doc.html](http://localhost:8989/doc.html) -- `swagger` 接口文档:[http://localhost:8989/swagger-ui/index.html](http://localhost:8989/swagger-ui/index.html) -- `apifox` 在线接口文档:[https://www.apifox.cn/apidoc](https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5) - - -## 🚀 项目启动 - -1. **数据库初始化** - - 执行 [youlai_boot.sql](sql/mysql8/youlai_boot.sql) 脚本完成数据库创建、表结构和基础数据的初始化。 - -2. **修改配置** - - [application-dev.yml](src/main/resources/application-dev.yml) 修改MySQL、Redis连接配置; - -3. **启动项目** - - 执行 [SystemApplication.java](src/main/java/com/youlai/boot/YouLaiApplication.java) 的 main 方法完成后端项目启动; - - 访问接口文档地址 [http://localhost:8989/doc.html](http://localhost:8989/doc.html) 验证项目启动是否成功。 - ## ✅ 项目统计 ![Alt](https://repobeats.axiom.co/api/embed/544c5c0b5b3611a6c4d5ef0faa243a9066b89659.svg "Repobeats analytics image") @@ -152,9 +154,7 @@ Thanks to all the contributors! ## 💖 加交流群 -> 关注公众号【有来技术】,获取交流群二维码,不想关注公众号或二维码过期欢迎加我微信(`haoxianrui`)备注【有来】即可,拉你进群。 - -| ![](https://s2.loli.net/2022/11/19/OGjum9wr8f6idLX.png) | -|---------------------------------------------------------| +> 关注公众号 有来技术 ,点击菜单 交流群 获取加群二维码。 +![](https://foruda.gitee.com/images/1737108820762592766/3390ed0d_716974.png) From c2ab755cf3cb4dad966a25ec8ceddbc2706693dd Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 22 Jan 2025 11:56:58 +0800 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20MinIO=20=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E8=BF=94=E5=9B=9E=E5=90=8D=E7=A7=B0=E4=B8=8D?= =?UTF-8?q?=E5=B8=A6=E6=96=87=E4=BB=B6=E5=A4=B9=E5=92=8C=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E4=BF=A1=E6=81=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/BusinessException.java | 7 +++ .../exception/GlobalExceptionHandler.java | 8 +-- .../boot/common/result/IResultCode.java | 4 +- .../youlai/boot/common/result/ResultCode.java | 16 ++--- .../boot/shared/file/model/FileInfo.java | 7 +++ .../file/service/impl/MinioFileService.java | 58 ++++++++++--------- 6 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/youlai/boot/common/exception/BusinessException.java b/src/main/java/com/youlai/boot/common/exception/BusinessException.java index c74ae972..06467091 100644 --- a/src/main/java/com/youlai/boot/common/exception/BusinessException.java +++ b/src/main/java/com/youlai/boot/common/exception/BusinessException.java @@ -20,6 +20,13 @@ public class BusinessException extends RuntimeException { this.resultCode = errorCode; } + + public BusinessException(IResultCode errorCode,String message) { + super(message); + this.resultCode = errorCode; + } + + public BusinessException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java index 2021dbd9..cf7722a8 100644 --- a/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/boot/common/exception/GlobalExceptionHandler.java @@ -30,7 +30,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; - /** * 全局系统异常处理器 *

@@ -219,9 +218,9 @@ public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleBizException(BusinessException e) { - log.error("biz exception: {}", e.getMessage()); + log.error("biz exception", e); if (e.getResultCode() != null) { - return Result.failed(e.getResultCode()); + return Result.failed(e.getResultCode(), e.getMessage()); } return Result.failed(e.getMessage()); } @@ -239,8 +238,7 @@ public class GlobalExceptionHandler { || e instanceof AuthenticationException) { throw e; } - log.error("unknown exception: {}", e.getMessage()); - e.printStackTrace(); + log.error("unknown exception", e); return Result.failed(e.getLocalizedMessage()); } diff --git a/src/main/java/com/youlai/boot/common/result/IResultCode.java b/src/main/java/com/youlai/boot/common/result/IResultCode.java index 479639e3..741d604a 100644 --- a/src/main/java/com/youlai/boot/common/result/IResultCode.java +++ b/src/main/java/com/youlai/boot/common/result/IResultCode.java @@ -3,8 +3,8 @@ package com.youlai.boot.common.result; /** * 响应码接口 * - * @author Ray - * @since 2022/2/18 + * @author Ray.Hao + * @since 1.0.0 **/ public interface IResultCode { diff --git a/src/main/java/com/youlai/boot/common/result/ResultCode.java b/src/main/java/com/youlai/boot/common/result/ResultCode.java index 73ab7752..026927ef 100644 --- a/src/main/java/com/youlai/boot/common/result/ResultCode.java +++ b/src/main/java/com/youlai/boot/common/result/ResultCode.java @@ -42,7 +42,7 @@ public enum ResultCode implements IResultCode, Serializable { VOICE_VERIFICATION_CODE_INPUT_ERROR("A0133", "语音校验码输入错误"), USER_CERTIFICATE_EXCEPTION("A0140", "用户证件异常"), - USER_CERTIFICATE_TYPE_NOT_SELECTED("A0141", "用户证��类型未选择"), + USER_CERTIFICATE_TYPE_NOT_SELECTED("A0141", "用户证件类型未选择"), MAINLAND_ID_NUMBER_VERIFICATION_ILLEGAL("A0142", "大陆身份证编号校验非法"), USER_BASIC_INFORMATION_VERIFICATION_FAILED("A0150", "用户基本信息校验失败"), @@ -127,12 +127,14 @@ public enum ResultCode implements IResultCode, Serializable { USER_RESOURCE_NOT_FOUND("A0606", "用户资源不存在"), /** 二级宏观错误码 */ - USER_UPLOAD_FILE_EXCEPTION("A0700", "用户上传文件异常"), - USER_UPLOAD_FILE_TYPE_MISMATCH("A0701", "用户上传文件类型不匹配"), - USER_UPLOAD_FILE_TOO_LARGE("A0702", "用户上传文件太大"), - USER_UPLOAD_IMAGE_TOO_LARGE("A0703", "用户上传图片太大"), - USER_UPLOAD_VIDEO_TOO_LARGE("A0704", "用户上传视频太大"), - USER_UPLOAD_COMPRESSED_FILE_TOO_LARGE("A0705", "用户上传压缩文件太大"), + UPLOAD_FILE_EXCEPTION("A0700", "上传文件异常"), + UPLOAD_FILE_TYPE_MISMATCH("A0701", "上传文件类型不匹配"), + UPLOAD_FILE_TOO_LARGE("A0702", "上传文件太大"), + UPLOAD_IMAGE_TOO_LARGE("A0703", "上传图片太大"), + UPLOAD_VIDEO_TOO_LARGE("A0704", "上传视频太大"), + UPLOAD_COMPRESSED_FILE_TOO_LARGE("A0705", "上传压缩文件太大"), + + DELETE_FILE_EXCEPTION("A0710", "删除文件异常"), /** 二级宏观错误码 */ USER_CURRENT_VERSION_EXCEPTION("A0800", "用户当前版本异常"), diff --git a/src/main/java/com/youlai/boot/shared/file/model/FileInfo.java b/src/main/java/com/youlai/boot/shared/file/model/FileInfo.java index f1251dc4..ec550a18 100644 --- a/src/main/java/com/youlai/boot/shared/file/model/FileInfo.java +++ b/src/main/java/com/youlai/boot/shared/file/model/FileInfo.java @@ -3,6 +3,13 @@ package com.youlai.boot.shared.file.model; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; + +/** + * 文件信息对象 + * + * @author Ray.Hao + * @since 1.0.0 + */ @Schema(description = "文件对象") @Data public class FileInfo { diff --git a/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java b/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java index db5fce55..59c7e576 100644 --- a/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java +++ b/src/main/java/com/youlai/boot/shared/file/service/impl/MinioFileService.java @@ -5,30 +5,30 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; +import com.youlai.boot.common.exception.BusinessException; +import com.youlai.boot.common.result.ResultCode; import com.youlai.boot.shared.file.service.FileService; import com.youlai.boot.shared.file.model.FileInfo; import io.minio.*; -import io.minio.errors.*; import io.minio.http.Method; import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; +import java.io.File; import java.io.InputStream; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; /** * MinIO 文件上传服务类 * - * @author haoxr + * @author Ray.Hao * @since 2023/6/2 */ @Component @@ -36,6 +36,7 @@ import java.time.LocalDateTime; @ConfigurationProperties(prefix = "oss.minio") @RequiredArgsConstructor @Data +@Slf4j public class MinioFileService implements FileService { /** @@ -77,7 +78,7 @@ public class MinioFileService implements FileService { * 上传文件 * * @param file 表单文件对象 - * @return + * @return 文件信息 */ @Override public FileInfo uploadFile(MultipartFile file) { @@ -85,16 +86,19 @@ public class MinioFileService implements FileService { // 创建存储桶(存储桶不存在),如果有搭建好的minio服务,建议放在init方法中 createBucketIfAbsent(bucketName); - // 生成文件名(日期文件夹) + // 文件后缀 String suffix = FileUtil.getSuffix(file.getOriginalFilename()); - String uuid = IdUtil.simpleUUID(); - String fileName = DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + "/" + uuid + "." + suffix; + // 文件夹名称 + String dateFolder = DateUtil.format(LocalDateTime.now(), "yyyyMMdd"); + // 文件名称 + String fileName = IdUtil.simpleUUID() + "." + suffix; + // try-with-resource 语法糖自动释放流 try (InputStream inputStream = file.getInputStream()) { // 文件上传 PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(bucketName) - .object(fileName) + .object(dateFolder + "/"+ fileName) .contentType(file.getContentType()) .stream(inputStream, inputStream.available(), -1) .build(); @@ -104,15 +108,18 @@ public class MinioFileService implements FileService { String fileUrl; // 未配置自定义域名 if (StrUtil.isBlank(customDomain)) { + // 获取文件URL GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder() - .bucket(bucketName).object(fileName) + .bucket(bucketName) + .object(dateFolder + "/"+ fileName) .method(Method.GET) .build(); fileUrl = minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs); fileUrl = fileUrl.substring(0, fileUrl.indexOf("?")); - } else { // 配置自定义文件路径域名 - fileUrl = customDomain + '/' + bucketName + "/" + fileName; + } else { + // 配置自定义文件路径域名 + fileUrl = customDomain + "/"+ bucketName + "/"+ dateFolder + "/"+ fileName; } FileInfo fileInfo = new FileInfo(); @@ -120,7 +127,8 @@ public class MinioFileService implements FileService { fileInfo.setUrl(fileUrl); return fileInfo; } catch (Exception e) { - throw new RuntimeException("文件上传失败"); + log.error("上传文件失败", e); + throw new BusinessException(ResultCode.UPLOAD_FILE_EXCEPTION, e.getMessage()); } } @@ -128,9 +136,8 @@ public class MinioFileService implements FileService { /** * 删除文件 * - * @param filePath 文件路径 http://localhost:9000/default/20221120/test.jpg - * - * @return + * @param filePath 文件完整路径 + * @return 是否删除成功 */ @Override public boolean deleteFile(String filePath) { @@ -152,7 +159,8 @@ public class MinioFileService implements FileService { minioClient.removeObject(removeObjectArgs); return true; } catch (Exception e) { - throw new RuntimeException("文件删除失败", e); + log.error("删除文件失败", e); + throw new BusinessException(ResultCode.DELETE_FILE_EXCEPTION, e.getMessage()); } } @@ -161,17 +169,11 @@ public class MinioFileService implements FileService { * PUBLIC桶策略 * 如果不配置,则新建的存储桶默认是PRIVATE,则存储桶文件会拒绝访问 Access Denied * - * @param bucketName - * @return + * @param bucketName 存储桶名称 + * @return 存储桶策略 */ private static String publicBucketPolicy(String bucketName) { - /** - * AWS的S3存储桶策略 - * Principal: 生效用户对象 - * Resource: 指定存储桶 - * Action: 操作行为 - */ - + // AWS的S3存储桶策略 JSON 格式 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/example-bucket-policies.html return "{\"Version\":\"2012-10-17\"," + "\"Statement\":[{\"Effect\":\"Allow\"," + "\"Principal\":{\"AWS\":[\"*\"]}," @@ -185,7 +187,7 @@ public class MinioFileService implements FileService { /** * 创建存储桶(存储桶不存在) * - * @param bucketName + * @param bucketName 存储桶名称 */ @SneakyThrows private void createBucketIfAbsent(String bucketName) { From f1f30712f7c1e21ff659aa8ee570e16c7b404362 Mon Sep 17 00:00:00 2001 From: Theo <971366405@qq.com> Date: Wed, 22 Jan 2025 16:14:43 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat(shared):=20=E4=BC=98=E5=8C=96=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=96=87=E4=BB=B6=E6=9C=8D=E5=8A=A1=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加文件上传和删除方法的代码注释 - 使用 Hutool 的 DatePattern 优化日期格式化 - 修复文件路径分隔符问题,提高代码兼容性 --- .../file/service/impl/LocalFileService.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java b/src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java index f6a217e4..46740dc4 100644 --- a/src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java +++ b/src/main/java/com/youlai/boot/shared/file/service/impl/LocalFileService.java @@ -1,5 +1,6 @@ package com.youlai.boot.shared.file.service.impl; +import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; @@ -7,6 +8,7 @@ import com.youlai.boot.shared.file.model.FileInfo; import com.youlai.boot.shared.file.service.FileService; import lombok.Data; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -23,40 +25,53 @@ import java.time.LocalDateTime; * @author Theo * @since 2024-12-09 17:11 */ +@Data +@Slf4j @Component @ConditionalOnProperty(value = "oss.type", havingValue = "local") @ConfigurationProperties(prefix = "oss.local") @RequiredArgsConstructor -@Data public class LocalFileService implements FileService { @Value("${oss.local.storage-path}") private String storagePath; + /** + * 上传文件方法 + * + * @param file 表单文件对象 + * @return 文件信息 + */ @Override public FileInfo uploadFile(MultipartFile file) { // 生成文件名(日期文件夹) String suffix = FileUtil.getSuffix(file.getOriginalFilename()); String uuid = IdUtil.simpleUUID(); - String folder = DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + File.separator; - String fileName = uuid + "." + suffix; + String folder = DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN); + String fileName = uuid + "." + suffix; String filePrefix = storagePath.endsWith(File.separator) ? storagePath : storagePath + File.separator; // try-with-resource 语法糖自动释放流 try (InputStream inputStream = file.getInputStream()) { // 上传文件 - FileUtil.writeFromStream(inputStream, filePrefix +folder+ fileName); + FileUtil.writeFromStream(inputStream, filePrefix + folder + File.separator + fileName); } catch (Exception e) { - e.printStackTrace(); + log.error("文件上传失败", e); throw new RuntimeException("文件上传失败"); } // 获取文件访问路径,因为这里是本地存储,所以直接返回文件的相对路径,需要前端自行处理访问前缀 - String fileUrl = File.separator +folder+File.separator + fileName; + String fileUrl = File.separator + folder + File.separator + fileName; FileInfo fileInfo = new FileInfo(); fileInfo.setName(fileName); fileInfo.setUrl(fileUrl); return fileInfo; } + + /** + * 删除文件 + * @param filePath 文件完整URL + * @return 是否删除成功 + */ @Override public boolean deleteFile(String filePath) { //判断文件是否为空 From 8936f61f43b31a8df6c93336367ce2964a5e6da8 Mon Sep 17 00:00:00 2001 From: Theo <971366405@qq.com> Date: Wed, 22 Jan 2025 16:25:13 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(User):=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=82=AE=E7=AE=B1=E5=AD=97=E6=AE=B5-=20=E5=9C=A8=20Us?= =?UTF-8?q?erMapper.xml=20=E6=96=87=E4=BB=B6=E4=B8=AD=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E7=94=A8=E6=88=B7=E9=82=AE=E7=AE=B1=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=20-=20=E5=9C=A8=E6=9F=A5=E8=AF=A2=E7=94=A8=E6=88=B7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E7=9B=B8=E5=85=B3=20SQL=20=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E4=B8=AD=E5=8C=85=E5=90=AB=E4=BA=86=E9=82=AE=E7=AE=B1=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/mapper/system/UserMapper.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/resources/mapper/system/UserMapper.xml b/src/main/resources/mapper/system/UserMapper.xml index 88e103af..1cfc5907 100644 --- a/src/main/resources/mapper/system/UserMapper.xml +++ b/src/main/resources/mapper/system/UserMapper.xml @@ -14,6 +14,7 @@ u.gender, u.avatar, u.STATUS, + u.email, d.NAME AS dept_name, GROUP_CONCAT( r.NAME ) AS roleNames, u.create_time @@ -182,6 +183,7 @@ u.username, u.nickname, u.mobile, + u.email, CASE u.gender WHEN 1 THEN '男' WHEN 2 THEN '女' @@ -219,6 +221,7 @@ u.gender, u.avatar, u.STATUS, + u.email, d.NAME AS deptName, GROUP_CONCAT(r.NAME) AS roleNames, u.create_time From d15af9f51e483f46191e791c0fede1ba0b6e9249 Mon Sep 17 00:00:00 2001 From: Theo <971366405@qq.com> Date: Wed, 22 Jan 2025 16:45:28 +0800 Subject: [PATCH 6/6] =?UTF-8?q?refactor(system):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 UserMapper 中的性别转换逻辑 - 在 UserServiceImpl 中添加字典数据获取和性别转换逻辑 --- .../system/service/impl/UserServiceImpl.java | 38 ++++++++++++------- .../resources/mapper/system/UserMapper.xml | 6 +-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java index e9beb707..c4f74448 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java @@ -10,29 +10,29 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SystemConstants; -import com.youlai.boot.core.security.manager.TokenManager; +import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.common.model.Option; +import com.youlai.boot.core.security.manager.TokenManager; +import com.youlai.boot.core.security.service.PermissionService; +import com.youlai.boot.core.security.util.SecurityUtils; import com.youlai.boot.shared.mail.service.MailService; import com.youlai.boot.shared.sms.enums.SmsTypeEnum; import com.youlai.boot.shared.sms.service.SmsService; +import com.youlai.boot.system.converter.UserConverter; +import com.youlai.boot.system.enums.DictCodeEnum; +import com.youlai.boot.system.mapper.UserMapper; +import com.youlai.boot.system.model.bo.UserBO; +import com.youlai.boot.system.model.dto.UserAuthInfo; +import com.youlai.boot.system.model.dto.UserExportDTO; +import com.youlai.boot.system.model.entity.DictData; import com.youlai.boot.system.model.entity.User; import com.youlai.boot.system.model.entity.UserRole; import com.youlai.boot.system.model.form.*; -import com.youlai.boot.system.converter.UserConverter; -import com.youlai.boot.common.exception.BusinessException; -import com.youlai.boot.system.model.vo.UserProfileVO; -import com.youlai.boot.core.security.util.SecurityUtils; -import com.youlai.boot.system.mapper.UserMapper; -import com.youlai.boot.system.model.dto.UserAuthInfo; -import com.youlai.boot.system.model.bo.UserBO; import com.youlai.boot.system.model.query.UserPageQuery; -import com.youlai.boot.system.model.dto.UserExportDTO; import com.youlai.boot.system.model.vo.UserInfoVO; import com.youlai.boot.system.model.vo.UserPageVO; -import com.youlai.boot.core.security.service.PermissionService; -import com.youlai.boot.system.service.RoleService; -import com.youlai.boot.system.service.UserRoleService; -import com.youlai.boot.system.service.UserService; +import com.youlai.boot.system.model.vo.UserProfileVO; +import com.youlai.boot.system.service.*; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.crypto.password.PasswordEncoder; @@ -69,6 +69,8 @@ public class UserServiceImpl extends ServiceImpl implements Us private final TokenManager tokenManager; + private final DictDataService dictDataService; + private final UserConverter userConverter; /** @@ -274,7 +276,15 @@ public class UserServiceImpl extends ServiceImpl implements Us */ @Override public List listExportUsers(UserPageQuery queryParams) { - return this.baseMapper.listExportUsers(queryParams); + List userExportDTOS = this.baseMapper.listExportUsers(queryParams); + //获取角色的字典数据 + List list = dictDataService.list(new LambdaQueryWrapper().eq(DictData::getDictCode, DictCodeEnum.GENDER.getValue())); + Map genderMap = list.stream().collect(Collectors.toMap(DictData::getValue, DictData::getLabel)); + userExportDTOS.forEach(userExportDTO -> { + String genderLabel = genderMap.get(userExportDTO.getGender()); + userExportDTO.setGender(genderLabel); + }); + return null; } /** diff --git a/src/main/resources/mapper/system/UserMapper.xml b/src/main/resources/mapper/system/UserMapper.xml index 1cfc5907..0940c488 100644 --- a/src/main/resources/mapper/system/UserMapper.xml +++ b/src/main/resources/mapper/system/UserMapper.xml @@ -184,11 +184,7 @@ u.nickname, u.mobile, u.email, - CASE u.gender - WHEN 1 THEN '男' - WHEN 2 THEN '女' - ELSE '保密' - END gender, + u.gender, d.NAME AS dept_name, u.create_time FROM