Explorar o código

!102 少量 bug 快速 fix
Merge pull request !102 from 芋道源码/feature/1.6.1

芋道源码 %!s(int64=3) %!d(string=hai) anos
pai
achega
e923bc661d
Modificáronse 64 ficheiros con 259 adicións e 197 borrados
  1. 1 1
      pom.xml
  2. 2 2
      sql/ruoyi-vue-pro.sql
  3. 13 6
      yudao-dependencies/pom.xml
  4. 2 2
      yudao-framework/yudao-spring-boot-starter-file/pom.xml
  5. 5 3
      yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java
  6. 51 42
      yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java
  7. 9 18
      yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java
  8. 0 36
      yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3ModifyPathInterceptor.java
  9. 2 0
      yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java
  10. 2 0
      yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java
  11. 39 12
      yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java
  12. 2 0
      yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java
  13. 0 1
      yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java
  14. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
  15. 0 1
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  16. 2 2
      yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java
  17. 1 2
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java
  18. 1 1
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java
  19. 3 4
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
  20. 2 2
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java
  21. 5 2
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
  22. 1 1
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java
  23. 1 1
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
  24. 3 3
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java
  25. 3 3
      yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java
  26. 1 1
      yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java
  27. 3 3
      yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java
  28. 2 3
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java
  29. 1 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
  30. 1 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
  31. 1 1
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImplTest.java
  32. 1 2
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java
  33. 3 5
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java
  34. 3 7
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java
  35. 1 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java
  36. 1 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
  37. 1 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
  38. 2 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
  39. 1 1
      yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/user/UserServiceImplTest.java
  40. 6 0
      yudao-ui-admin/.env.demo1024
  41. 3 0
      yudao-ui-admin/.env.development
  42. 5 0
      yudao-ui-admin/.env.production
  43. 6 0
      yudao-ui-admin/.env.staging
  44. 1 1
      yudao-ui-admin/package.json
  45. 25 0
      yudao-ui-admin/src/components/DocAlert/index.vue
  46. 2 0
      yudao-ui-admin/src/main.js
  47. 14 1
      yudao-ui-admin/src/utils/ruoyi.js
  48. 2 0
      yudao-ui-admin/src/views/bpm/definition/index.vue
  49. 1 0
      yudao-ui-admin/src/views/bpm/form/index.vue
  50. 1 0
      yudao-ui-admin/src/views/bpm/group/index.vue
  51. 1 0
      yudao-ui-admin/src/views/bpm/model/index.vue
  52. 1 0
      yudao-ui-admin/src/views/bpm/oa/leave/index.vue
  53. 1 0
      yudao-ui-admin/src/views/bpm/processInstance/index.vue
  54. 2 1
      yudao-ui-admin/src/views/bpm/task/done.vue
  55. 1 0
      yudao-ui-admin/src/views/bpm/task/todo.vue
  56. 3 3
      yudao-ui-admin/src/views/infra/build/index.vue
  57. 1 0
      yudao-ui-admin/src/views/infra/codegen/index.vue
  58. 1 6
      yudao-ui-admin/src/views/infra/file/index.vue
  59. 2 6
      yudao-ui-admin/src/views/infra/fileConfig/index.vue
  60. 1 1
      yudao-ui-admin/src/views/login.vue
  61. 1 0
      yudao-ui-admin/src/views/system/menu/index.vue
  62. 2 0
      yudao-ui-admin/src/views/system/role/index.vue
  63. 1 1
      yudao-ui-admin/src/views/system/tenant/index.vue
  64. 1 1
      yudao-ui-admin/src/views/system/tenantPackage/index.vue

+ 1 - 1
pom.xml

@@ -25,7 +25,7 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <properties>
-        <revision>1.6.0-snapshot</revision>
+        <revision>1.6.1-snapshot</revision>
         <!-- Maven 相关 -->
         <java.version>1.8</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 2
sql/ruoyi-vue-pro.sql


+ 13 - 6
yudao-dependencies/pom.xml

@@ -14,7 +14,7 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <properties>
-        <revision>1.6.0-snapshot</revision>
+        <revision>1.6.1-snapshot</revision>
         <!-- 统一依赖管理 -->
         <spring.boot.version>2.5.10</spring.boot.version>
         <!-- Web 相关 -->
@@ -28,7 +28,7 @@
         <dynamic-datasource.version>3.5.0</dynamic-datasource.version>
         <redisson.version>3.16.6</redisson.version>
         <!-- Config 配置中心相关 -->
-        <apollo.version>1.7.0</apollo.version>
+        <apollo.version>1.9.2</apollo.version>
         <!-- Job 定时任务相关 -->
         <!-- 服务保障相关 -->
         <lock4j.version>2.2.0</lock4j.version>
@@ -52,11 +52,12 @@
         <velocity.version>2.2</velocity.version>
         <screw.version>1.0.5</screw.version>
         <guava.version>30.1.1-jre</guava.version>
+        <guice.version>5.1.0</guice.version>
         <transmittable-thread-local.version>2.12.2</transmittable-thread-local.version>
         <commons-net.version>3.8.0</commons-net.version>
         <jsch.version>0.1.55</jsch.version>
         <!-- 三方云服务相关 -->
-        <s3.version>2.17.147</s3.version>
+        <minio.version>8.2.2</minio.version>
         <aliyun-java-sdk-core.version>4.5.25</aliyun-java-sdk-core.version>
         <aliyun-java-sdk-dysmsapi.version>2.1.0</aliyun-java-sdk-dysmsapi.version>
         <yunpian-java-sdk.version>1.2.7</yunpian-java-sdk.version>
@@ -490,6 +491,12 @@
                 <version>${guava.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.google.inject</groupId>
+                <artifactId>guice</artifactId>
+                <version>${guice.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->
@@ -514,9 +521,9 @@
                 <version>${revision}</version>
             </dependency>
             <dependency>
-                <groupId>software.amazon.awssdk</groupId>
-                <artifactId>s3</artifactId>
-                <version>${s3.version}</version>
+                <groupId>io.minio</groupId>
+                <artifactId>minio</artifactId>
+                <version>${minio.version}</version>
             </dependency>
 
             <!-- SMS SDK begin -->

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-file/pom.xml

@@ -63,8 +63,8 @@
 
         <!-- 三方云服务相关 -->
         <dependency>
-            <groupId>software.amazon.awssdk</groupId>
-            <artifactId>s3</artifactId>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
         </dependency>
 
         <!-- Test 测试相关 -->

+ 5 - 3
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java

@@ -20,15 +20,17 @@ public interface FileClient {
      * @param content 文件流
      * @param path 相对路径
      * @return 完整路径,即 HTTP 访问地址
+     * @throws Exception 上传文件时,抛出 Exception 异常
      */
-    String upload(byte[] content, String path);
+    String upload(byte[] content, String path) throws  Exception;
 
     /**
      * 删除文件
      *
      * @param path 相对路径
+     * @throws Exception 删除文件时,抛出 Exception 异常
      */
-    void delete(String path);
+    void delete(String path) throws Exception;
 
     /**
      * 获得文件的内容
@@ -36,6 +38,6 @@ public interface FileClient {
      * @param path 相对路径
      * @return 文件的内容
      */
-    byte[] getContent(String path);
+    byte[] getContent(String path) throws Exception;
 
 }

+ 51 - 42
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java

@@ -1,19 +1,14 @@
 package cn.iocoder.yudao.framework.file.core.client.s3;
 
+import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
 import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient;
-import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
-import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
-import software.amazon.awssdk.core.sync.RequestBody;
-import software.amazon.awssdk.regions.Region;
-import software.amazon.awssdk.services.s3.S3Client;
-import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
-import software.amazon.awssdk.services.s3.model.GetObjectRequest;
-import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import io.minio.*;
 
-import java.net.URI;
+import java.io.ByteArrayInputStream;
 
-import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_QINIU;
+import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN;
 
 /**
  * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
@@ -24,7 +19,7 @@ import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.
  */
 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
 
-    private S3Client client;
+    private MinioClient client;
 
     public S3FileClient(Long id, S3FileClientConfig config) {
         super(id, config);
@@ -34,34 +29,27 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
     protected void doInit() {
         // 补全 domain
         if (StrUtil.isEmpty(config.getDomain())) {
-            config.setDomain(createDomain());
+            config.setDomain(buildDomain());
         }
         // 初始化客户端
-        client = S3Client.builder()
-                .serviceConfiguration(sb -> sb.pathStyleAccessEnabled(false) // 关闭路径风格
-                .chunkedEncodingEnabled(false)) // 禁用 chunk
-                .endpointOverride(createURI()) // 上传地址
-                .region(Region.of(config.getRegion())) // Region
-                .credentialsProvider(StaticCredentialsProvider.create( // 认证密钥
-                        AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())))
-                .overrideConfiguration(cb -> cb.addExecutionInterceptor(new S3ModifyPathInterceptor(config.getBucket())))
+        client = MinioClient.builder()
+                .endpoint(buildEndpointURL()) // Endpoint URL
+                .region(buildRegion()) // Region
+                .credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥
                 .build();
     }
 
     /**
-     * 基于 endpoint 构建调用云服务的 URI 地址
+     * 基于 endpoint 构建调用云服务的 URL 地址
      *
      * @return URI 地址
      */
-    private URI createURI() {
-        String uri;
-        // 如果是七牛,无需拼接 bucket
-        if (config.getEndpoint().contains(ENDPOINT_QINIU)) {
-            uri = StrUtil.format("https://{}", config.getEndpoint());
-        } else {
-            uri = StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
+    private String buildEndpointURL() {
+        // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO
+        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
+            return config.getEndpoint();
         }
-        return URI.create(uri);
+        return StrUtil.format("https://{}", config.getEndpoint());
     }
 
     /**
@@ -69,35 +57,56 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
      *
      * @return Domain 地址
      */
-    private String createDomain() {
+    private String buildDomain() {
+        // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO
+        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
+            return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket());
+        }
+        // 阿里云、腾讯云、华为云都适合。七牛云比较特殊,必须有自定义域名
         return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
     }
 
+    /**
+     * 基于 bucket 构建 region 地区
+     *
+     * @return region 地区
+     */
+    private String buildRegion() {
+        // 阿里云必须有 region,否则会报错
+        if (config.getEndpoint().contains(ENDPOINT_ALIYUN)) {
+            return StrUtil.subBefore(config.getEndpoint(), '.', false)
+                    .replaceAll("-internal", ""); // 去除内网 Endpoint 的后缀
+        }
+        return null;
+    }
+
     @Override
-    public String upload(byte[] content, String path) {
+    public String upload(byte[] content, String path) throws Exception {
         // 执行上传
-        PutObjectRequest.Builder request = PutObjectRequest.builder()
+        client.putObject(PutObjectArgs.builder()
                 .bucket(config.getBucket()) // bucket 必须传递
-                .key(path); // 相对路径作为 key
-        client.putObject(request.build(), RequestBody.fromBytes(content));
+                .object(path) // 相对路径作为 key
+                .stream(new ByteArrayInputStream(content), content.length, -1) // 文件内容
+                .build());
         // 拼接返回路径
         return config.getDomain() + "/" + path;
     }
 
     @Override
-    public void delete(String path) {
-        DeleteObjectRequest.Builder request = DeleteObjectRequest.builder()
+    public void delete(String path) throws Exception {
+        client.removeObject(RemoveObjectArgs.builder()
                 .bucket(config.getBucket()) // bucket 必须传递
-                .key(path); // 相对路径作为 key
-        client.deleteObject(request.build());
+                .object(path) // 相对路径作为 key
+                .build());
     }
 
     @Override
-    public byte[] getContent(String path) {
-        GetObjectRequest.Builder request = GetObjectRequest.builder()
+    public byte[] getContent(String path) throws Exception {
+        GetObjectResponse response = client.getObject(GetObjectArgs.builder()
                 .bucket(config.getBucket()) // bucket 必须传递
-                .key(path); // 相对路径作为 key
-        return client.getObjectAsBytes(request.build()).asByteArray();
+                .object(path) // 相对路径作为 key
+                .build());
+        return IoUtil.readBytes(response);
     }
 
 }

+ 9 - 18
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientConfig.java

@@ -18,37 +18,28 @@ import javax.validation.constraints.NotNull;
 public class S3FileClientConfig implements FileClientConfig {
 
     public static final String ENDPOINT_QINIU = "qiniucs.com";
+    public static final String ENDPOINT_ALIYUN = "aliyuncs.com";
 
     /**
      * 节点地址
-     * 1. MinIO:
+     * 1. MinIO:https://www.iocoder.cn/Spring-Boot/MinIO 。例如说,http://127.0.0.1:9000
      * 2. 阿里云:https://help.aliyun.com/document_detail/31837.html
-     * 3. 腾讯云:
+     * 3. 腾讯云:https://cloud.tencent.com/document/product/436/6224
      * 4. 七牛云:https://developer.qiniu.com/kodo/4088/s3-access-domainname
-     * 5. 华为云:
+     * 5. 华为云:https://developer.huaweicloud.com/endpoint?OBS
      */
     @NotNull(message = "endpoint 不能为空")
     private String endpoint;
     /**
      * 自定义域名
-     * 1. MinIO:
+     * 1. MinIO:通过 Nginx 配置
      * 2. 阿里云:https://help.aliyun.com/document_detail/31836.html
      * 3. 腾讯云:https://cloud.tencent.com/document/product/436/11142
      * 4. 七牛云:https://developer.qiniu.com/kodo/8556/set-the-custom-source-domain-name
-     * 5. 华为云:
+     * 5. 华为云:https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html
      */
     @URL(message = "domain 必须是 URL 格式")
     private String domain;
-    /**
-     * 区域
-     * 1. MinIO:
-     * 2. 阿里云:https://help.aliyun.com/document_detail/31837.html
-     * 3. 腾讯云:
-     * 4. 七牛云:https://developer.qiniu.com/kodo/4088/s3-access-domainname
-     * 5. 华为云:
-     */
-    @NotNull(message = "region 不能为空")
-    private String region;
     /**
      * 存储 Bucket
      */
@@ -57,11 +48,11 @@ public class S3FileClientConfig implements FileClientConfig {
 
     /**
      * 访问 Key
-     * 1. MinIO:
-     * 2. 阿里云:
+     * 1. MinIO:https://www.iocoder.cn/Spring-Boot/MinIO
+     * 2. 阿里云:https://ram.console.aliyun.com/manage/ak
      * 3. 腾讯云:https://console.cloud.tencent.com/cam/capi
      * 4. 七牛云:https://portal.qiniu.com/user/key
-     * 5. 华为云:
+     * 5. 华为云:https://support.huaweicloud.com/qs-obs/obs_qs_0005.html
      */
     @NotNull(message = "accessKey 不能为空")
     private String accessKey;

+ 0 - 36
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3ModifyPathInterceptor.java

@@ -1,36 +0,0 @@
-package cn.iocoder.yudao.framework.file.core.client.s3;
-
-import software.amazon.awssdk.core.interceptor.Context;
-import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
-import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
-import software.amazon.awssdk.http.SdkHttpRequest;
-
-/**
- * S3 修改路径的拦截器,移除多余的 Bucket 前缀。
- * 如果不使用该拦截器,希望上传的路径是 /tudou.jpg 时,会被添加成 /bucket/tudou.jpg
- *
- * @author 芋道源码
- */
-public class S3ModifyPathInterceptor implements ExecutionInterceptor {
-
-	private final String bucket;
-
-	public S3ModifyPathInterceptor(String bucket) {
-		this.bucket = "/" + bucket;
-	}
-
-	@Override
-	public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
-		SdkHttpRequest request = context.httpRequest();
-		SdkHttpRequest.Builder rb = SdkHttpRequest.builder().protocol(request.protocol()).host(request.host()).port(request.port())
-				.method(request.method()).headers(request.headers()).rawQueryParameters(request.rawQueryParameters());
-		// 移除 path 前的 bucket 路径
-		if (request.encodedPath().startsWith(bucket)) {
-			rb.encodedPath(request.encodedPath().substring(bucket.length()));
-		} else {
-			rb.encodedPath(request.encodedPath());
-		}
-		return rb.build();
-	}
-
-}

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClientTest.java

@@ -3,11 +3,13 @@ package cn.iocoder.yudao.framework.file.core.client.ftp;
 import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.extra.ftp.FtpMode;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 public class FtpFileClientTest {
 
     @Test
+    @Disabled
     public void test() {
         // 创建客户端
         FtpFileClientConfig config = new FtpFileClientConfig();

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/local/LocalFileClientTest.java

@@ -2,11 +2,13 @@ package cn.iocoder.yudao.framework.file.core.client.local;
 
 import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.util.IdUtil;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 public class LocalFileClientTest {
 
     @Test
+    @Disabled
     public void test() {
         // 创建客户端
         LocalFileClientConfig config = new LocalFileClientConfig();

+ 39 - 12
yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClientTest.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.file.core.client.s3;
 
 import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -11,9 +10,25 @@ import javax.validation.Validation;
 
 public class S3FileClientTest {
 
+    @Test
+    @Disabled // MinIO,如果要集成测试,可以注释本行
+    public void testMinIO() throws Exception {
+        S3FileClientConfig config = new S3FileClientConfig();
+        // 配置成你自己的
+        config.setAccessKey("admin");
+        config.setAccessSecret("password");
+        config.setBucket("yudaoyuanma");
+        config.setDomain(null);
+        // 默认 9000 endpoint
+        config.setEndpoint("http://127.0.0.1:9000");
+
+        // 执行上传
+        testExecuteUpload(config);
+    }
+
     @Test
     @Disabled // 阿里云 OSS,如果要集成测试,可以注释本行
-    public void testAliyun() {
+    public void testAliyun() throws Exception {
         S3FileClientConfig config = new S3FileClientConfig();
         // 配置成你自己的
         config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY"));
@@ -29,7 +44,7 @@ public class S3FileClientTest {
 
     @Test
     @Disabled // 腾讯云 COS,如果要集成测试,可以注释本行
-    public void testQCloud() {
+    public void testQCloud() throws Exception {
         S3FileClientConfig config = new S3FileClientConfig();
         // 配置成你自己的
         config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY"));
@@ -38,7 +53,6 @@ public class S3FileClientTest {
         config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn
         // 默认上海的 endpoint
         config.setEndpoint("cos.ap-shanghai.myqcloud.com");
-        config.setRegion("ap-shanghai");
 
         // 执行上传
         testExecuteUpload(config);
@@ -46,7 +60,7 @@ public class S3FileClientTest {
 
     @Test
     @Disabled // 七牛云存储,如果要集成测试,可以注释本行
-    public void testQiniu() {
+    public void testQiniu() throws Exception {
         S3FileClientConfig config = new S3FileClientConfig();
         // 配置成你自己的
 //        config.setAccessKey(System.getenv("QINIU_ACCESS_KEY"));
@@ -62,11 +76,24 @@ public class S3FileClientTest {
         testExecuteUpload(config);
     }
 
-    private void testExecuteUpload(S3FileClientConfig config) {
-        // 补全配置
-        if (config.getRegion() == null) {
-            config.setRegion(StrUtil.subBefore(config.getEndpoint(), '.', false));
-        }
+    @Test
+    @Disabled // 华为云存储,如果要集成测试,可以注释本行
+    public void testHuaweiCloud() throws Exception {
+        S3FileClientConfig config = new S3FileClientConfig();
+        // 配置成你自己的
+//        config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY"));
+//        config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY"));
+        config.setBucket("yudao");
+        config.setDomain(null); // 如果有自定义域名,则可以设置。
+        // 默认上海的 endpoint
+        config.setEndpoint("obs.cn-east-3.myhuaweicloud.com");
+
+        // 执行上传
+        testExecuteUpload(config);
+    }
+
+    private void testExecuteUpload(S3FileClientConfig config) throws Exception {
+        // 校验配置
         ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config);
         // 创建 Client
         S3FileClient client = new S3FileClient(0L, config);
@@ -77,9 +104,9 @@ public class S3FileClientTest {
         String fullPath = client.upload(content, path);
         System.out.println("访问地址:" + fullPath);
         // 读取文件
-        if (false) {
+        if (true) {
             byte[] bytes = client.getContent(path);
-            System.out.println("文件内容:" + bytes);
+            System.out.println("文件内容:" + bytes.length);
         }
         // 删除文件
         if (false) {

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-file/src/test/java/cn/iocoder/yudao/framework/file/core/client/sftp/SftpFileClientTest.java

@@ -2,11 +2,13 @@ package cn.iocoder.yudao.framework.file.core.client.sftp;
 
 import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.util.IdUtil;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 public class SftpFileClientTest {
 
     @Test
+    @Disabled
     public void test() {
         // 创建客户端
         SftpFileClientConfig config = new SftpFileClientConfig();

+ 0 - 1
yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java

@@ -22,7 +22,6 @@ public interface BpmMessageService {
      */
     void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO);
 
-
     /**
      * 发送流程实例被不通过的消息
      *

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java

@@ -5,7 +5,6 @@ import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
-import cn.iocoder.yudao.framework.datapermission.core.dept.rule.DeptDataPermissionRule;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
@@ -69,11 +68,13 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
     public BpmUserTaskActivityBehavior(UserTask userTask) {
         super(userTask);
     }
+
     public void setScripts(List<BpmTaskAssignScript> scripts) {
         this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
     }
 
     @Override
+    @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据
     protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
         // 第一步,获得任务的规则
         BpmTaskAssignRuleDO rule = getTaskRule(task);
@@ -98,7 +99,6 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
         return taskRules.get(0);
     }
 
-    @VisibleForTesting
     Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRuleDO rule) {
         Set<Long> assigneeUserIds = null;
         if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {

+ 0 - 1
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -15,7 +15,6 @@ import java.util.Set;
 @Component
 public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
 
-
     @Override
     @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据
     public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {

+ 2 - 2
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java

@@ -15,7 +15,7 @@ public interface FileApi {
      * @param content 文件内容
      * @return 文件路径
      */
-   default String createFile(byte[] content) {
+   default String createFile(byte[] content) throws Exception {
        return createFile(IdUtil.fastUUID(), content);
    }
 
@@ -26,6 +26,6 @@ public interface FileApi {
      * @param content 文件内容
      * @return 文件路径
      */
-    String createFile(String path, byte[] content);
+    String createFile(String path, byte[] content) throws Exception;
 
 }

+ 1 - 2
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.infra.api.file;
 
-import cn.iocoder.yudao.module.infra.api.file.FileApi;
 import cn.iocoder.yudao.module.infra.service.file.FileService;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -20,7 +19,7 @@ public class FileApiImpl implements FileApi {
     private FileService fileService;
 
     @Override
-    public String createFile(String path, byte[] content) {
+    public String createFile(String path, byte[] content) throws Exception {
         return fileService.createFile(path, content);
     }
 

+ 1 - 1
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java

@@ -82,7 +82,7 @@ public class FileConfigController {
     @GetMapping("/test")
     @ApiOperation("测试文件配置是否正确")
     @PreAuthorize("@ss.hasPermission('infra:file-config:query')")
-    public CommonResult<String> testFileConfig(@RequestParam("id") Long id) {
+    public CommonResult<String> testFileConfig(@RequestParam("id") Long id) throws Exception {
         String url = fileConfigService.testFileConfig(id);
         return success(url);
     }

+ 3 - 4
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java

@@ -23,7 +23,6 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
-import java.io.IOException;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
@@ -44,7 +43,7 @@ public class FileController {
             @ApiImplicitParam(name = "path", value = "文件路径", example = "yudaoyuanma.png", dataTypeClass = String.class)
     })
     public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file,
-                                           @RequestParam("path") String path) throws IOException {
+                                           @RequestParam("path") String path) throws Exception {
         return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream())));
     }
 
@@ -52,7 +51,7 @@ public class FileController {
     @ApiOperation("删除文件")
     @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
     @PreAuthorize("@ss.hasPermission('infra:file:delete')")
-    public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) {
+    public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) throws Exception {
         fileService.deleteFile(id);
         return success(true);
     }
@@ -65,7 +64,7 @@ public class FileController {
     })
     public void getFileContent(HttpServletResponse response,
                                @PathVariable("configId") Long configId,
-                               @PathVariable("path") String path) throws IOException {
+                               @PathVariable("path") String path) throws Exception {
         byte[] content = fileService.getFileContent(configId, path);
         if (content == null) {
             log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);

+ 2 - 2
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java

@@ -82,7 +82,7 @@ public class CodegenServiceImpl implements CodegenService {
         table.setAuthor(userApi.getUser(userId).getNickname());
         codegenTableMapper.insert(table);
         // 构建 CodegenColumnDO 数组,插入到 DB 中
-        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
+        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(table.getId(), schemaColumns);
         codegenColumnMapper.insertBatch(columns);
         return table.getId();
     }
@@ -196,7 +196,7 @@ public class CodegenServiceImpl implements CodegenService {
         }
 
         // 插入新增的字段
-        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
+        List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, schemaColumns);
         codegenColumnMapper.insertBatch(columns);
         // 删除不存在的字段
         if (CollUtil.isNotEmpty(deleteColumnIds)) {

+ 5 - 2
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java

@@ -133,9 +133,12 @@ public class CodegenBuilder {
         table.setTemplateType(CodegenTemplateTypeEnum.CRUD.getType());
     }
 
-    public List<CodegenColumnDO> buildColumns(List<SchemaColumnDO> schemaColumns) {
+    public List<CodegenColumnDO> buildColumns(Long tableId, List<SchemaColumnDO> schemaColumns) {
         List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(schemaColumns);
-        columns.forEach(this::initColumnDefault);
+        for (CodegenColumnDO column : columns) {
+            column.setTableId(tableId);
+            initColumnDefault(column);
+        }
         return columns;
     }
 

+ 1 - 1
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java

@@ -82,7 +82,7 @@ public interface FileConfigService {
      * @param id 编号
      * @return 文件 URL
      */
-    String testFileConfig(Long id);
+    String testFileConfig(Long id) throws Exception;
 
     /**
      * 获得指定编号的文件客户端

+ 1 - 1
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java

@@ -225,7 +225,7 @@ public class FileConfigServiceImpl implements FileConfigService {
     }
 
     @Override
-    public String testFileConfig(Long id) {
+    public String testFileConfig(Long id) throws Exception {
         // 校验存在
         this.validateFileConfigExists(id);
         // 上传文件

+ 3 - 3
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java

@@ -26,14 +26,14 @@ public interface FileService {
      * @param content 文件内容
      * @return 文件路径
      */
-    String createFile(String path, byte[] content);
+    String createFile(String path, byte[] content) throws Exception;
 
     /**
      * 删除文件
      *
      * @param id 编号
      */
-    void deleteFile(Long id);
+    void deleteFile(Long id) throws Exception;
 
     /**
      * 获得文件内容
@@ -42,6 +42,6 @@ public interface FileService {
      * @param path 文件路径
      * @return 文件内容
      */
-    byte[] getFileContent(Long configId, String path);
+    byte[] getFileContent(Long configId, String path) throws Exception;
 
 }

+ 3 - 3
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java

@@ -35,7 +35,7 @@ public class FileServiceImpl implements FileService {
     }
 
     @Override
-    public String createFile(String path, byte[] content) {
+    public String createFile(String path, byte[] content) throws Exception {
         // 上传到文件存储器
         FileClient client = fileConfigService.getMasterFileClient();
         Assert.notNull(client, "客户端(master) 不能为空");
@@ -53,7 +53,7 @@ public class FileServiceImpl implements FileService {
     }
 
     @Override
-    public void deleteFile(Long id) {
+    public void deleteFile(Long id) throws Exception {
         // 校验存在
         FileDO file = this.validateFileExists(id);
 
@@ -75,7 +75,7 @@ public class FileServiceImpl implements FileService {
     }
 
     @Override
-    public byte[] getFileContent(Long configId, String path) {
+    public byte[] getFileContent(Long configId, String path) throws Exception {
         FileClient client = fileConfigService.getFileClient(configId);
         Assert.notNull(client, "客户端({}) 不能为空", configId);
         return client.getContent(path);

+ 1 - 1
yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java

@@ -228,7 +228,7 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void testFileConfig() {
+    public void testFileConfig() throws Exception {
         // mock 数据
         FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false);
         fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据

+ 3 - 3
yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java

@@ -70,7 +70,7 @@ public class FileServiceTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void testCreateFile_success() {
+    public void testCreateFile_success() throws Exception {
         // 准备参数
         String path = randomString();
         byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
@@ -95,7 +95,7 @@ public class FileServiceTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void testDeleteFile_success() {
+    public void testDeleteFile_success() throws Exception {
         // mock 数据
         FileDO dbFile = randomPojo(FileDO.class, o -> o.setConfigId(10L).setPath("tudou.jpg"));
         fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据
@@ -123,7 +123,7 @@ public class FileServiceTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void testGetFileContent() {
+    public void testGetFileContent() throws Exception {
         // 准备参数
         Long configId = 10L;
         String path = "tudou.jpg";

+ 2 - 3
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java

@@ -16,9 +16,8 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.io.IOException;
 
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY;
@@ -44,7 +43,7 @@ public class AppUserController {
     @PutMapping("/update-avatar")
     @ApiOperation("修改用户头像")
     @PreAuthenticated
-    public CommonResult<String> updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException {
+    public CommonResult<String> updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws Exception {
         if (file.isEmpty()) {
             throw exception(FILE_IS_EMPTY);
         }

+ 1 - 1
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java

@@ -60,7 +60,7 @@ public interface MemberUserService {
      * @param inputStream 头像文件
      * @return 头像url
      */
-    String updateUserAvatar(Long userId, InputStream inputStream);
+    String updateUserAvatar(Long userId, InputStream inputStream) throws Exception;
 
     /**
      * 修改手机

+ 1 - 1
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java

@@ -100,7 +100,7 @@ public class MemberUserServiceImpl implements MemberUserService {
     }
 
     @Override
-    public String updateUserAvatar(Long userId, InputStream avatarFile) {
+    public String updateUserAvatar(Long userId, InputStream avatarFile) throws Exception {
         this.checkUserExists(userId);
         // 创建文件
         String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));

+ 1 - 1
yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImplTest.java

@@ -74,7 +74,7 @@ public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest {
     }
 
     @Test
-    public void testUpdateAvatar_success(){
+    public void testUpdateAvatar_success() throws Exception {
         // mock 数据
         MemberUserDO dbUser = randomUserDO();
         userMapper.insert(dbUser);

+ 1 - 2
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java

@@ -29,7 +29,6 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.io.IOException;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -99,7 +98,7 @@ public class UserProfileController {
 
     @PutMapping("/update-avatar")
     @ApiOperation("上传用户个人头像")
-    public CommonResult<String> updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException {
+    public CommonResult<String> updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws Exception {
         if (file.isEmpty()) {
             throw ServiceExceptionUtil.exception(FILE_IS_EMPTY);
         }

+ 3 - 5
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java

@@ -1,12 +1,12 @@
 package cn.iocoder.yudao.module.system.dal.mysql.permission;
 
-import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
 
 import java.util.Date;
 import java.util.List;
@@ -28,9 +28,7 @@ public interface MenuMapper extends BaseMapperX<MenuDO> {
                 .eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
     }
 
-    default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
-        return selectOne(new LambdaQueryWrapper<MenuDO>().select(MenuDO::getId)
-                .gt(MenuDO::getUpdateTime, maxUpdateTime).last(SqlConstants.LIMIT1)) != null;
-    }
+    @Select("SELECT id FROM system_menu WHERE update_time > #{maxUpdateTime} LIMIT 1")
+    MenuDO selectExistsByUpdateTimeAfter(Date maxUpdateTime);
 
 }

+ 3 - 7
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java

@@ -7,9 +7,8 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
-import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
 import org.springframework.lang.Nullable;
 
 import java.util.Collection;
@@ -45,10 +44,7 @@ public interface RoleMapper extends BaseMapperX<RoleDO> {
         return selectList(new LambdaQueryWrapperX<RoleDO>().inIfPresent(RoleDO::getStatus, statuses));
     }
 
-    @InterceptorIgnore(tenantLine = "true") // 该方法忽略多租户。原因:该方法被异步 task 调用,此时获取不到租户编号
-    default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
-        return selectOne(new QueryWrapper<RoleDO>().select("id")
-                .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
-    }
+    @Select("SELECT id FROM system_role WHERE update_time > #{maxUpdateTime} LIMIT 1")
+    RoleDO selectExistsByUpdateTimeAfter(Date maxUpdateTime);
 
 }

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java

@@ -120,7 +120,7 @@ public class MenuServiceImpl implements MenuService {
         if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
             log.info("[loadMenuIfUpdate][首次加载全量菜单]");
         } else { // 判断数据库中是否有更新的菜单
-            if (!menuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
+            if (menuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
                 return null;
             }
             log.info("[loadMenuIfUpdate][增量加载全量菜单]");

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java

@@ -116,7 +116,7 @@ public class RoleServiceImpl implements RoleService {
         if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
             log.info("[loadRoleIfUpdate][首次加载全量角色]");
         } else { // 判断数据库中是否有更新的角色
-            if (!roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
+            if (roleMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
                 return null;
             }
             log.info("[loadRoleIfUpdate][增量加载全量角色]");

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java

@@ -64,7 +64,7 @@ public interface AdminUserService {
      * @param id         用户 id
      * @param avatarFile 头像文件
      */
-    String updateUserAvatar(Long id, InputStream avatarFile);
+    String updateUserAvatar(Long id, InputStream avatarFile) throws Exception;
 
     /**
      * 修改密码

+ 2 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java

@@ -63,6 +63,7 @@ public class AdminUserServiceImpl implements AdminUserService {
     private FileApi fileApi;
 
     @Override
+
     public Long createUser(UserCreateReqVO reqVO) {
         // 校验账户配合
         tenantService.handleTenantInfo(tenant -> {
@@ -118,7 +119,7 @@ public class AdminUserServiceImpl implements AdminUserService {
     }
 
     @Override
-    public String updateUserAvatar(Long id, InputStream avatarFile) {
+    public String updateUserAvatar(Long id, InputStream avatarFile) throws Exception {
         this.checkUserExists(id);
         // 存储文件
         String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/user/UserServiceImplTest.java

@@ -196,7 +196,7 @@ public class UserServiceImplTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void testUpdateUserAvatar_success() {
+    public void testUpdateUserAvatar_success() throws Exception {
         // mock 数据
         AdminUserDO dbUser = randomAdminUserDO();
         userMapper.insert(dbUser);

+ 6 - 0
yudao-ui-admin/.env.demo1024

@@ -13,3 +13,9 @@ VUE_APP_BASE_API = 'http://127.0.0.1:48080'
 PUBLIC_PATH = '/admin-ui/'
 # 二级部署路径
 VUE_APP_APP_NAME ='/admin-ui/'
+
+# 多租户的开关
+VUE_APP_TENANT_ENABLE = true
+
+# 文档的开关
+VUE_APP_DOC_ENABLE = true

+ 3 - 0
yudao-ui-admin/.env.development

@@ -13,3 +13,6 @@ VUE_CLI_BABEL_TRANSPILE_MODULES = true
 
 # 多租户的开关
 VUE_APP_TENANT_ENABLE = true
+
+# 文档的开关
+VUE_APP_DOC_ENABLE = true

+ 5 - 0
yudao-ui-admin/.env.production

@@ -12,3 +12,8 @@ PUBLIC_PATH = 'http://my-pi.com:8888/yudao-admin/'
 # 二级部署路径
 VUE_APP_APP_NAME ='yudao-admin'
 
+# 多租户的开关
+VUE_APP_TENANT_ENABLE = true
+
+# 文档的开关
+VUE_APP_DOC_ENABLE = false

+ 6 - 0
yudao-ui-admin/.env.staging

@@ -11,3 +11,9 @@ VUE_APP_BASE_API = 'http://api-dashboard.yudao.iocoder.cn'
 
 # 静态资源地址
 PUBLIC_PATH = 'http://static.yudao.iocoder.cn/'
+
+# 多租户的开关
+VUE_APP_TENANT_ENABLE = true
+
+# 文档的开关
+VUE_APP_DOC_ENABLE = false

+ 1 - 1
yudao-ui-admin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "yudao-ui-admin",
-  "version": "1.6.0-snapshot",
+  "version": "1.6.1-snapshot",
   "description": "芋道管理系统",
   "author": "芋道",
   "license": "MIT",

+ 25 - 0
yudao-ui-admin/src/components/DocAlert/index.vue

@@ -0,0 +1,25 @@
+<template>
+  <el-alert v-if="enable()" :title="'【' + title + '】文档地址:' + url" type="success" show-icon />
+</template>
+
+<script>
+import {getDocEnable} from "@/utils/ruoyi";
+
+export default {
+  name: "DocAlert",
+  props: {
+    title: String,
+    url: String,
+  },
+  methods: {
+    enable: function () {
+      return getDocEnable();
+    }
+  }
+};
+</script>
+<style scoped>
+.el-alert--success.is-light {
+  margin-bottom: 10px;
+}
+</style>

+ 2 - 0
yudao-ui-admin/src/main.js

@@ -41,10 +41,12 @@ Vue.prototype.handleTree = handleTree
 
 // 全局组件挂载
 Vue.component('DictTag', DictTag)
+Vue.component('DocAlert', DocAlert)
 Vue.component('Pagination', Pagination)
 Vue.component('RightToolbar', RightToolbar)
 // 字典标签组件
 import DictTag from '@/components/DictTag'
+import DocAlert from '@/components/DocAlert'
 // 头部标签插件
 import VueMeta from 'vue-meta'
 

+ 14 - 1
yudao-ui-admin/src/utils/ruoyi.js

@@ -175,7 +175,7 @@ export function getNowDateTime(timeStr) {
  * 获得租户功能是否开启
  */
 export function getTenantEnable() {
-  console.log("enable: " + process.env.VUE_APP_TENANT_ENABLE)
+  // console.log("enable: " + process.env.VUE_APP_TENANT_ENABLE)
   if (process.env.VUE_APP_TENANT_ENABLE === "true") {
     return true;
   }
@@ -184,3 +184,16 @@ export function getTenantEnable() {
   }
   return process.env.VUE_APP_TENANT_ENABLE || true;
 }
+
+/**
+ * 获得文档是否开启
+ */
+export function getDocEnable() {
+  if (process.env.VUE_APP_DOC_ENABLE === "true") {
+    return true;
+  }
+  if (process.env.VUE_APP_DOC_ENABLE === "false") {
+    return false;
+  }
+  return process.env.VUE_APP_DOC_ENABLE || false;
+}

+ 2 - 0
yudao-ui-admin/src/views/bpm/definition/index.vue

@@ -1,5 +1,7 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
+
     <!-- 列表 -->
     <el-table v-loading="loading" :data="list">
       <el-table-column label="定义编号" align="center" prop="id" width="400" />

+ 1 - 0
yudao-ui-admin/src/views/bpm/form/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 1 - 0
yudao-ui-admin/src/views/bpm/group/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 1 - 0
yudao-ui-admin/src/views/bpm/model/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 1 - 0
yudao-ui-admin/src/views/bpm/oa/leave/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 1 - 0
yudao-ui-admin/src/views/bpm/processInstance/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 2 - 1
yudao-ui-admin/src/views/bpm/task/done.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
@@ -24,7 +25,7 @@
       <el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" width="120" />
       <el-table-column label="结果" align="center" prop="result">
         <template slot-scope="scope">
-          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.result"/>
+          <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
         </template>
       </el-table-column>
       <el-table-column label="审批意见" align="center" prop="comment" width="200" />

+ 1 - 0
yudao-ui-admin/src/views/bpm/task/todo.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
 
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">

+ 3 - 3
yudao-ui-admin/src/views/infra/build/index.vue

@@ -44,9 +44,9 @@
 
     <div class="center-board">
       <div class="action-bar">
-        <el-button icon="el-icon-video-play" type="text" @click="run">
-          运行
-        </el-button>
+<!--        <el-button icon="el-icon-video-play" type="text" @click="run">-->
+<!--          运行-->
+<!--        </el-button>-->
         <el-button icon="el-icon-view" type="text" @click="showJson">
           查看json
         </el-button>

+ 1 - 0
yudao-ui-admin/src/views/infra/codegen/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="代码生成" url="https://doc.iocoder.cn/new-feature/" />
     <!-- 操作工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="表名称" prop="tableName">

+ 1 - 6
yudao-ui-admin/src/views/infra/file/index.vue

@@ -1,16 +1,11 @@
 <template>
   <div class="app-container">
-
+    <doc-alert title="上传下载" url="https://doc.iocoder.cn/file/" />
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="文件路径" prop="path">
         <el-input v-model="queryParams.path" placeholder="请输入文件路径" clearable size="small" @keyup.enter.native="handleQuery"/>
       </el-form-item>
-      <el-form-item label="文件类型" prop="type">
-        <el-select v-model="queryParams.type" placeholder="请选择文件类型" clearable size="small">
-          <el-option label="请选择字典生成" value="" />
-        </el-select>
-      </el-form-item>
       <el-form-item label="创建时间">
         <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
                         type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />

+ 2 - 6
yudao-ui-admin/src/views/infra/fileConfig/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-
+    <doc-alert title="上传下载" url="https://doc.iocoder.cn/file/" />
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="配置名" prop="name">
@@ -51,7 +51,7 @@
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240">
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
                      v-hasPermi="['infra:file-config:update']">修改</el-button>
@@ -109,9 +109,6 @@
         <el-form-item v-if="form.storage === 20" label="节点地址" prop="config.endpoint">
           <el-input v-model="form.config.endpoint" placeholder="请输入节点地址" />
         </el-form-item>
-        <el-form-item v-if="form.storage === 20" label="区域" prop="config.region">
-          <el-input v-model="form.config.region" placeholder="请输入区域" />
-        </el-form-item>
         <el-form-item v-if="form.storage === 20" label="存储 bucket" prop="config.bucket">
           <el-input v-model="form.config.bucket" placeholder="请输入 bucket" />
         </el-form-item>
@@ -190,7 +187,6 @@ export default {
           password: [{ required: true, message: "密码不能为空", trigger: "blur" }],
           mode: [{ required: true, message: "连接模式不能为空", trigger: "change" }],
           endpoint: [{ required: true, message: "节点地址不能为空", trigger: "blur" }],
-          region: [{ required: true, message: "区域名不能为空", trigger: "blur" }],
           bucket: [{ required: true, message: "存储 bucket 不能为空", trigger: "blur" }],
           accessKey: [{ required: true, message: "accessKey 不能为空", trigger: "blur" }],
           accessSecret: [{ required: true, message: "accessSecret 不能为空", trigger: "blur" }],

+ 1 - 1
yudao-ui-admin/src/views/login.vue

@@ -87,7 +87,7 @@ export default {
               // debugger
               getTenantIdByName(value).then(res => {
                 const tenantId = res.data;
-                if (tenantId >= 0) {
+                if (tenantId && tenantId >= 0) {
                   // 设置租户
                   Cookies.set("tenantId", tenantId);
                   callback();

+ 1 - 0
yudao-ui-admin/src/views/system/menu/index.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="app-container">
+    <doc-alert title="功能权限" url="https://doc.iocoder.cn/resource-permission" />
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
       <el-form-item label="菜单名称" prop="name">
         <el-input v-model="queryParams.name" placeholder="请输入菜单名称" clearable size="small" @keyup.enter.native="handleQuery"/>

+ 2 - 0
yudao-ui-admin/src/views/system/role/index.vue

@@ -1,5 +1,7 @@
 <template>
   <div class="app-container">
+    <doc-alert title="功能权限" url="https://doc.iocoder.cn/resource-permission" />
+    <doc-alert title="数据权限" url="https://doc.iocoder.cn/data-permission" />
     <el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true">
       <el-form-item label="角色名称" prop="name">
         <el-input v-model="queryParams.name" placeholder="请输入角色名称" clearable size="small" style="width: 240px"

+ 1 - 1
yudao-ui-admin/src/views/system/tenant/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-
+    <doc-alert title="SaaS 多租户" url="https://doc.iocoder.cn/saas-tenant/" />
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="租户名" prop="name">

+ 1 - 1
yudao-ui-admin/src/views/system/tenantPackage/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-
+    <doc-alert title="SaaS 多租户" url="https://doc.iocoder.cn/saas-tenant/" />
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="套餐名" prop="name">

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio