Browse Source

!89 update 0.9.8.5
Merge pull request !89 from Foming/dev

Foming 3 years ago
parent
commit
4d9fa1ab02
26 changed files with 429 additions and 299 deletions
  1. 7 8
      doc/docs/guide/quicklyDistribution.md
  2. 32 33
      doc/docs/guide/quicklySeparate.md
  3. 9 6
      doc/docs/guide/quicklySource.md
  4. BIN
      doc/docs/picture/quickly/img.png
  5. BIN
      doc/docs/picture/quickly/img_15.png
  6. 40 0
      report-core/README.md
  7. 15 2
      report-core/pom.xml
  8. 2 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java
  9. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java
  10. 2 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/service/impl/AccessUserServiceImpl.java
  11. 7 11
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/service/impl/ReportDashboardServiceImpl.java
  12. 2 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/controller/param/GaeaFileParam.java
  13. 8 12
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/service/GaeaFileService.java
  14. 122 128
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/service/impl/GaeaFileServiceImpl.java
  15. 35 26
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/service/impl/ReportExcelServiceImpl.java
  16. 68 24
      report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java
  17. 13 4
      report-core/src/main/resources/bootstrap-dev.yml
  18. 0 4
      report-core/src/main/resources/bootstrap-prod.yml
  19. 36 10
      report-core/src/main/resources/bootstrap.yml
  20. 9 8
      report-core/src/main/resources/i18n/messages_en_US.properties
  21. 9 8
      report-core/src/main/resources/i18n/messages_zh_CN.properties
  22. 2 2
      report-ui/src/utils/request.js
  23. 1 1
      report-ui/src/views/excelreport/designer/index.vue
  24. 4 4
      report-ui/src/views/layout/components/Navbar.vue
  25. 1 1
      report-ui/src/views/layout/components/Sidebar/index.vue
  26. 4 4
      report-ui/src/views/login.vue

+ 7 - 8
doc/docs/guide/quicklyDistribution.md

@@ -1,8 +1,9 @@
 ```
  第一步,下载zip包,解压
- 第二步,conf->bootstrap.yml,修改mysql连接
+ 第二步,conf->bootstrap.yml,修改mysql连接等信息
  第三步,启动bin目录下start.sh
- 第四步,访问 http://localhost:9095
+ 第四步,访问 http://localhost:9095  admin 123456
+ 第五步,修改"数据源->mysql数据源"用户名密码
 ```
 
 ## 下载发行版
@@ -21,10 +22,10 @@
 ![bootstrap.png](../picture/quickly/img_2.png) <br>
 **注**:请确认你的Mysql是否支持远程连接,登陆用户是否有DDL权限 <br>
 
-## 上传功能
+## OSS配置
 
-使用上传功能,必须修改此内容,注意路径格式,比如Win是 \ ,linux是 / <br>
-![file.png](../picture/quickly/img_15.png) <br>
+OSS底层已支持minio、amazonS3、dfs,都配置的情况下优先级minio->amazonS3->nfs <br>
+![file.png](../picture/quickly/img.png) <br>
 
 ## 启动
 
@@ -38,9 +39,7 @@ aj-report-XXX --> bin --> start.bat <br>
 
 如果start.bat启动有问题的话,可以尝试以下方法解决。<br>
 修改第4行的JAVA_HOME,改成你自己的JAVA_HOME,并去掉**rem**注释,双击启动<br>
-![java.png](../picture/quickly/img_7.png) <br>
-**注**:如果你JAVA_HOME目录存在空格,将bat文件最下面的JAVA_HOME添加""号
-![img.png](../picture/quickly/img_8.png) <br>
+![java.png](../picture/quickly/img_7.png) 
 
 ## 访问
 

+ 32 - 33
doc/docs/guide/quicklySeparate.md

@@ -15,6 +15,8 @@ java -jar
 BASE_API: '"./"',改成自己后端的api
 npm install
 npm run build
+
+使用nginx转发
 ```
 
 ## linux部署后端
@@ -24,8 +26,8 @@ npm run build
 - [Apache Maven] 3.5 <br>
 - [Node.js] v14.16.0 <br>
 - [Jdk] 1.8 <br>
-  请在你的Windows上先准备好maven、node.js、jdk <br>
-  **注**:已知 **Jdk11** (部分小版本)存在兼容性问题,请不要使用openJdk,环境问题请看 **常见问题** 大类 <br>
+
+**注**:已知 **Jdk11** (部分小版本)存在兼容性问题,请不要使用openJdk,环境问题请看 **常见问题** 大类 <br>
 
 ### 克隆源码
 
@@ -36,46 +38,39 @@ git clone https://gitee.com/anji-plus/report.git <br>
 ### 修改mysql连接
 
 report-core --> src --> main --> resources --> bootstrap.yml <br>
-![bootstrap.png](../picture/quickly/img_2.png) <br>
 将图中关于mysql的连接配置信息换成你使用的IP <br>
-**注**:aj_report库是存放底层基础信息的库,flyway启动时会自动建立,如果你在这里修改了库,将会出错<br>
-**注**:请确认你的Mysql是否支持远程连接,登陆用户是否有DDL权限 <br>
-
-### 上传功能
 
-使用上传功能,必须修改此内容,注意路径格式,比如Win是 \ ,linux是 / <br>
-![file.png](../picture/quickly/img_15.png) <br>
+![bootstrap.png](../picture/quickly/img_2.png) <br>
 
-### maven打包
+**注 :**
 
-**打包之前如果系统用的不止mysql数据源,需要自己在pom文件中加入对应的数据库的驱动,登陆系统之后,数据源提示无驱动,则选择通用JDBC数据源,这里不做演示了** <br>
-使用 maven package <br>
-**注**:不要使用maven install <br>
-**注**:此方式不会打包 lib目录下的驱动,详情可查看 **数据源 扩展** <br>
-![img10](../picture/quickly/img_10.png) <br>
-
-### linux启动jar包
+```
+1、aj_report库是存放底层基础信息的库,flyway启动时会自动建立,如果你在这里修改了库,将会出错
+2、请确认你的Mysql是否支持远程连接,登陆用户是否有DDL权限
+```
 
-将上步生成的jar包上传至linux,使用java -jar命令启动 <br>
-**注**:请确保你的linux有jdk <br>
+### OSS配置
 
-## 本地启动前端
+OSS底层已支持minio、amazonS3、dfs,都配置的情况下优先级minio->amazonS3->nfs <br>
+![file.png](../picture/quickly/img.png) <br>
 
-### 前端编译
+### maven打包
 
-进入前端目录:report-ui <br>
-![img11](../picture/quickly/img_11.png) <br>
-执行 npm install <br>
+直接使用 maven package 打包,打包完成如图所示<br>
 
-### 修改config
+![img10](../picture/quickly/img_10.png) <br>
+**注 :**
 
-目录地址:report-ui --> config --> dev.env.js <br>
-修改你的BASE_API地址 <br>
+```
+1、打包之前如果系统用的不止mysql数据源,需要自己在pom文件中加入对应的数据库的驱动,登陆系统之后,数据源提示无驱动,则选择通用JDBC数据源,这里不做演示了
+2、不要使用 maven install
+3、此方式不会打包 lib目录下的驱动,详情可查看 "数据源->扩展"
+```
 
-### 启动前端
+### linux启动jar包
 
-report-ui目录: <br>
-执行 npm run dev <br>
+将上步生成的jar包上传至linux,使用java -jar命令启动 <br>
+**注**:请确保你的linux有jdk1.8 <br>
 
 ## 前端build
 
@@ -88,14 +83,18 @@ report-ui目录: <br>
 ### 修改config
 
 目录地址:report-ui --> config --> prod.env.js <br>
-修改你的BASE_API地址,改成自己后端的api <br>
+将BASE_API地址,改成你后端的api地址 <br>
 
-### build
+### 打包
 
-reoprt-ui目录: <br>
 执行 npm run build <br>
 
 生成的前端dist目录文件在report-ui下面 <br>
+
 ![img12](../picture/quickly/img_12.png) <br>
 
+### 前端部署
+
+使用nginx做转发
+
 

+ 9 - 6
doc/docs/guide/quicklySource.md

@@ -9,8 +9,10 @@ cd aj-report-xxxx
 vim conf/bootstrap.yml 修改数据库连接、上传文件的路径以及地址
 sh bin/start.sh
 
-访问
-http://serverip:9095
+访问:http://serverip:9095
+admin 123456
+
+修改:"数据源->mysql数据源"用户名密码
 ```
 
 ## 编译环境
@@ -37,7 +39,8 @@ git clone https://gitee.com/anji-plus/report.git <br>
 编译完成后是放在当前目录下的build文件夹中:aj-report-xxxx.zip <br>
 
 **注:** 如果Win10部署的话,如图用git执行sh build.sh就行了。Linux就直接去report目录下执行sh build.sh就行。 <br>
-**特别注意:** 如果是Win10编译,那么几个启动脚本的格式则是win的格式,放linux上执行会报错的,反之放linux编译在win10启动也会报错,需要转格式。 <br>
+**特别注意:**
+如果是Win10编译,那么几个启动脚本的格式则是win的格式,放linux上执行会报错的,反之放linux编译在win10启动也会报错,需要转格式。 <br>
 
 ## 修改mysql连接
 
@@ -50,10 +53,10 @@ git clone https://gitee.com/anji-plus/report.git <br>
 ![bootstrap.png](../picture/quickly/img_2.png) <br>
 **注**:请确认你的Mysql是否支持远程连接,登陆用户是否有DDL权限 <br>
 
-## 上传功能
+## OSS配置
 
-使用上传功能,必须修改此内容,注意路径格式,比如Win是 \ ,linux是 / <br>
-![file.png](../picture/quickly/img_15.png)
+OSS底层已支持minio、amazonS3、dfs,都配置的情况下优先级minio->amazonS3->nfs <br>
+![file.png](../picture/quickly/img.png) <br>
 
 ## 启动
 

BIN
doc/docs/picture/quickly/img.png


BIN
doc/docs/picture/quickly/img_15.png


+ 40 - 0
report-core/README.md

@@ -1 +1,41 @@
 ## 后端springboot
+
+### 采用redis缓存
+
+#### 1.pom文件
+```java
+<dependency>
+    <groupId>com.anji-plus</groupId>
+    <artifactId>spring-boot-gaea</artifactId>
+    <version>2.0.5.RELEASE</version>
+    <exclusions>
+        <exclusion>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </exclusion>
+    </exclusions>
+</dependency>
+```
+删除exclusions内容,因为底层默认支持redis
+删除ehcache相关依赖
+
+#### 2.删除 package com.anjiplus.template.gaea.business.cache
+CacheHelper底层默认实现为RedisCacheHelper。
+@ConditionalOnMissingBean 注解起到的作用
+```java
+package com.anji.plus.gaea;
+
+@Configuration
+@EnableConfigurationProperties({GaeaProperties.class})
+public class GaeaAutoConfiguration {
+    @Bean
+    @ConditionalOnClass({RedisAutoConfiguration.class})
+    @ConditionalOnMissingBean
+    public CacheHelper cacheHelper() {
+        return new RedisCacheHelper();
+    }
+}
+```
+
+#### 3.bootstrap.yml加上对应的redis配置即可
+

+ 15 - 2
report-core/pom.xml

@@ -61,6 +61,12 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-mock</artifactId>
+            <version>2.0.8</version>
+        </dependency>
+
         <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-context</artifactId>
@@ -76,7 +82,7 @@
         <dependency>
             <groupId>com.anji-plus</groupId>
             <artifactId>spring-boot-gaea</artifactId>
-            <version>2.0.3.RELEASE</version>
+            <version>2.0.5.RELEASE</version>
             <exclusions>
                 <exclusion>
                     <groupId>org.springframework.boot</groupId>
@@ -85,6 +91,12 @@
             </exclusions>
         </dependency>
 
+        <dependency>
+            <groupId>com.anji-plus</groupId>
+            <artifactId>spring-boot-starter-gaea-oss</artifactId>
+            <version>2.0.5.RELEASE</version>
+        </dependency>
+
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
@@ -94,6 +106,7 @@
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.28</version>
         </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
@@ -115,7 +128,7 @@
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient</artifactId>
-            <version>4.5.10</version>
+            <version>4.5.13</version>
         </dependency>
 
         <dependency>

+ 2 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java

@@ -118,7 +118,8 @@ public interface ResponseCode {
     String FILE_SUFFIX_UNSUPPORTED = "2002";
     String FILE_UPLOAD_ERROR = "2003";
     String FILE_ONT_EXSIT = "2004";
-    String LIST_IS_EMPTY = "2005";
+    String FILE_OPERATION_FAILED = "file.operation.failed";
+
     String PUSHCODE_NEED_UNIQUE = "3001";
     String RECEIVER_IS_EMPTY = "3002";
     String DATA_SOURCE_CONNECTION_FAILED = "4001";

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java

@@ -231,7 +231,7 @@ public class TokenFilter implements Filter {
     }
 
     private void error(HttpServletResponse response) throws IOException {
-        ResponseBean responseBean = ResponseBean.builder().code("50008").message("The Token has expired").build();
+        ResponseBean responseBean = ResponseBean.builder().code("User.credentials.expired").message("The Token has expired").build();
         response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
         response.getWriter().print(JSONObject.toJSONString(responseBean));
     }

+ 2 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/service/impl/AccessUserServiceImpl.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.anji.plus.gaea.bean.TreeNode;
 import com.anji.plus.gaea.cache.CacheHelper;
 import com.anji.plus.gaea.constant.BaseOperationEnum;
+import com.anji.plus.gaea.constant.GaeaConstant;
 import com.anji.plus.gaea.exception.BusinessException;
 import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
 import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
@@ -163,7 +164,7 @@ public class AccessUserServiceImpl implements AccessUserService {
         } else {
             // 生成用户token
             String uuid = GaeaUtils.UUID();
-            token = jwtBean.createToken(loginName, uuid);
+            token = jwtBean.createToken(loginName, uuid, 0, GaeaConstant.TENANT_CODE);
             cacheHelper.stringSetExpire(tokenKey, token, 3600);
         }
 

+ 7 - 11
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/service/impl/ReportDashboardServiceImpl.java

@@ -80,13 +80,13 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
     @Autowired
     private ReportService reportService;
 
-    @Value("${customer.file.downloadPath:''}")
+    @Value("${spring.gaea.subscribes.oss.downloadPath:}")
     private String fileDownloadPath;
 
-    @Value("${customer.file.dist-path:''}")
+    @Value("${customer.file.tmp-path:.}")
     private String dictPath;
 
-    private final static String ZIP_PATH = "/zip/";
+    private final static String ZIP_PATH = "/tmp_zip/";
     private final static String JSON_PATH = "dashboard.json";
 
     private Map<String, ChartStrategy> queryServiceImplMap = new HashMap<>();
@@ -331,7 +331,7 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
                     GaeaFile gaeaFile = gaeaFileService.selectOne(queryWrapper);
                     String uploadPath;
                     if (null == gaeaFile) {
-                        GaeaFile upload = gaeaFileService.upload(imageFile, fileName);
+                        GaeaFile upload = gaeaFileService.upload(imageFile);
                         log.info("存入图片: {}", upload.getFilePath());
                         uploadPath = upload.getUrlPath();
                     }else {
@@ -395,13 +395,9 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
             queryWrapper.eq(GaeaFile::getFileId, fileName);
             GaeaFile gaeaFile = gaeaFileService.selectOne(queryWrapper);
             if (null != gaeaFile) {
-                String fileType = gaeaFile.getFileType();
-                path = path + "/image/" + fileName + "." + fileType;
-                //path = /app/disk/upload/zip/UUID/image
-
-                //原始文件的路径
-                String filePath = gaeaFile.getFilePath();
-                FileUtil.copyFileUsingFileChannels(filePath, path);
+                byte[] file = gaeaFileService.getFile(gaeaFile.getFileId());
+                path = path + "/image/";
+                FileUtil.byte2File(file, path, gaeaFile.getFileId().concat(".").concat(gaeaFile.getFileType()));
             }
         }
 

+ 2 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/controller/param/GaeaFileParam.java

@@ -4,6 +4,7 @@ package com.anjiplus.template.gaea.business.modules.file.controller.param;
 import com.anji.plus.gaea.annotation.Query;
 import com.anji.plus.gaea.constant.QueryEnum;
 import com.anji.plus.gaea.curd.params.PageParam;
+import lombok.Data;
 
 import java.io.Serializable;
 
@@ -13,6 +14,7 @@ import java.io.Serializable;
  * @author peiyanni
  * @since 2021-02-18 14:48:29
  */
+@Data
 public class GaeaFileParam extends PageParam implements Serializable {
 
     /** 模糊查询 */

+ 8 - 12
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/service/GaeaFileService.java

@@ -18,16 +18,6 @@ import java.io.File;
  */
 public interface GaeaFileService extends GaeaBaseService<GaeaFileParam, GaeaFile> {
 
-    /**
-     * 文件上传
-     *
-     * @param multipartFile  文件
-     * @param file 文件
-     * @param customFileName 自定义文件名,默认给null
-     * @return
-     */
-    GaeaFile upload(MultipartFile multipartFile, File file, String customFileName);
-
     /**
      * 文件上传
      *
@@ -41,10 +31,9 @@ public interface GaeaFileService extends GaeaBaseService<GaeaFileParam, GaeaFile
      * 文件上传
      *
      * @param file 二选一
-     * @param customFileName 自定义文件名
      * @return
      */
-    GaeaFile upload(File file, String customFileName);
+    GaeaFile upload(File file);
     /**
      * 根据fileId显示图片或者下载文件
      *
@@ -54,4 +43,11 @@ public interface GaeaFileService extends GaeaBaseService<GaeaFileParam, GaeaFile
      * @return
      */
     ResponseEntity<byte[]> download(HttpServletRequest request, HttpServletResponse response, String fileId);
+
+    /**
+     * 获取文件
+     * @param fileId
+     * @return
+     */
+    byte[] getFile(String fileId);
 }

+ 122 - 128
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/service/impl/GaeaFileServiceImpl.java

@@ -4,59 +4,51 @@ import com.anji.plus.gaea.constant.BaseOperationEnum;
 import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
 import com.anji.plus.gaea.exception.BusinessException;
 import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
+import com.anji.plus.gaea.oss.exceptions.GaeaOSSException;
+import com.anji.plus.gaea.oss.exceptions.GaeaOSSTypeLimitedException;
+import com.anji.plus.gaea.oss.ossbuilder.GaeaOSSTemplate;
+import com.anji.plus.gaea.oss.utils.ResponseUtil;
 import com.anjiplus.template.gaea.business.code.ResponseCode;
 import com.anjiplus.template.gaea.business.modules.file.dao.GaeaFileMapper;
 import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
 import com.anjiplus.template.gaea.business.modules.file.service.GaeaFileService;
-import com.anjiplus.template.gaea.business.modules.file.util.FileUtils;
-import com.anjiplus.template.gaea.business.modules.file.util.StringPatternUtil;
-import com.anjiplus.template.gaea.business.util.FileUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.entity.ContentType;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.CacheControl;
-import org.springframework.http.MediaType;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.interceptor.TransactionAspectSupport;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.io.FileInputStream;
 import java.util.List;
 import java.util.UUID;
-import java.util.stream.Collectors;
 
 /**
- * (GaeaFile)ServiceImpl
- *
- * @author peiyanni
- * @since 2021-02-18 14:48:26
+ * 文件管理服务实现
+ * @author: Raod
+ * @since: 2022-08-31
  */
 @Service
 @Slf4j
+@RefreshScope
 public class GaeaFileServiceImpl implements GaeaFileService {
 
-    @Value("${customer.file.dist-path:''}")
-    private String dictPath;
-
-    @Value("${customer.file.white-list:''}")
-    private String whiteList;
-
-    @Value("${customer.file.excelSuffix:''}")
-    private String excelSuffix;
-
-    @Value("${customer.file.downloadPath:''}")
+    @Value("${spring.gaea.subscribes.oss.downloadPath:''}")
     private String fileDownloadPath;
 
+    @Autowired
+    private GaeaOSSTemplate gaeaOSSTemplate;
+
     @Autowired
     private GaeaFileMapper gaeaFileMapper;
 
@@ -65,70 +57,6 @@ public class GaeaFileServiceImpl implements GaeaFileService {
         return gaeaFileMapper;
     }
 
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public GaeaFile upload(MultipartFile multipartFile, File file, String customFileName) {
-        try {
-            String fileName = "";
-            if (null != multipartFile) {
-                fileName = multipartFile.getOriginalFilename();
-            }else {
-                fileName = file.getName();
-            }
-
-            if (StringUtils.isBlank(fileName)) {
-                throw BusinessExceptionBuilder.build(ResponseCode.FILE_EMPTY_FILENAME);
-            }
-
-            String suffixName = fileName.substring(fileName.lastIndexOf("."));
-            String fileInstruction = fileName.substring(0, fileName.lastIndexOf("."));
-            //白名单校验(不区分大小写)
-            List<String> list = new ArrayList<>(Arrays.asList(whiteList.split("\\|")));
-            list.addAll(list.stream().map(String::toUpperCase).collect(Collectors.toList()));
-            if (!list.contains(suffixName)) {
-                throw BusinessExceptionBuilder.build(ResponseCode.FILE_SUFFIX_UNSUPPORTED);
-            }
-            // 生成文件唯一性标识
-            String fileId;
-            if (StringUtils.isBlank(customFileName)) {
-                fileId = UUID.randomUUID().toString();
-            } else {
-                fileId = customFileName;
-            }
-            String newFileName = fileId + suffixName;
-            // 本地文件保存路径
-            String filePath = dictPath + newFileName;
-            String urlPath = fileDownloadPath + "/" + fileId;
-
-            GaeaFile gaeaFile = new GaeaFile();
-            gaeaFile.setFilePath(filePath);
-            gaeaFile.setFileId(fileId);
-            gaeaFile.setUrlPath(urlPath);
-            gaeaFile.setFileType(suffixName.replace(".", ""));
-            gaeaFile.setFileInstruction(fileInstruction);
-            gaeaFileMapper.insert(gaeaFile);
-
-            //写文件 将文件保存/app/dictPath/upload/下
-            java.io.File dest = new java.io.File(dictPath + newFileName);
-            java.io.File parentFile = dest.getParentFile();
-            if (!parentFile.exists()) {
-                parentFile.mkdirs();
-            }
-            if (null != multipartFile) {
-                multipartFile.transferTo(dest);
-            }else {
-                FileUtil.copyFileUsingFileChannels(file, dest);
-            }
-            // 将完整的http访问路径返回
-            return gaeaFile;
-        } catch (Exception e) {
-            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-            log.error("file upload error: {}", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FILE_UPLOAD_ERROR);
-        }
-    }
-
     /**
      * 文件上传
      *
@@ -137,66 +65,138 @@ public class GaeaFileServiceImpl implements GaeaFileService {
      */
     @Override
     public GaeaFile upload(MultipartFile multipartFile) {
-        return upload(multipartFile, null, null);
+        String originalFilename =  multipartFile.getOriginalFilename();
+
+        if (StringUtils.isBlank(originalFilename)) {
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_EMPTY_FILENAME);
+        }
+        // 文件后缀 .png
+        String suffixName = originalFilename.substring(originalFilename.lastIndexOf("."));
+        // 生成文件唯一性标识
+        String fileId = UUID.randomUUID().toString();
+
+        // 生成在oss中存储的文件名 402b6193e70e40a9bf5b73a78ea1e8ab.png
+        String fileObjectName = fileId + suffixName;
+        // 生成链接通过fileId http访问路径 http://10.108.3.121:9089/meta/file/download/402b6193e70e40a9bf5b73a78ea1e8ab
+        String urlPath = fileDownloadPath + "/" + fileId;
+
+        // 上传文件
+        try{
+            gaeaOSSTemplate.uploadFileByInputStream(multipartFile, fileObjectName);
+        }catch (GaeaOSSTypeLimitedException e){
+            log.error("上传失败GaeaOSSTypeLimitedException", e);
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_SUFFIX_UNSUPPORTED, e.getMessage());
+        }catch (GaeaOSSException e){
+            log.error("上传失败GaeaOSSException", e);
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_UPLOAD_ERROR, e.getMessage());
+        }
+
+        // 保存到文件管理中
+        GaeaFile gaeaFile = new GaeaFile();
+        gaeaFile.setFileId(fileId);
+        gaeaFile.setFilePath(fileObjectName);
+        gaeaFile.setUrlPath(urlPath);
+        gaeaFile.setFileType(suffixName.replace(".", ""));
+        gaeaFile.setFileInstruction(originalFilename);
+        insert(gaeaFile);
+
+        return gaeaFile;
+    }
+
+    private MultipartFile getMultipartFile(File file){
+        FileInputStream fileInputStream;
+        MultipartFile multipartFile;
+        try {
+            fileInputStream = new FileInputStream(file);
+            multipartFile = new MockMultipartFile(file.getName(),file.getName(),
+                    ContentType.APPLICATION_OCTET_STREAM.toString(),fileInputStream);
+        } catch (Exception e) {
+            log.error("file转MultipartFile失败", e);
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
+        }
+        return multipartFile;
     }
 
     /**
      * 文件上传
      *
      * @param file           文件
-     * @param customFileName 自定义文件名
      * @return
      */
     @Override
-    public GaeaFile upload(File file, String customFileName) {
-        return upload(null, file, customFileName);
+    public GaeaFile upload(File file) {
+        return upload(getMultipartFile(file));
     }
 
     @Override
     public ResponseEntity<byte[]> download(HttpServletRequest request, HttpServletResponse response, String fileId) {
         try {
-            String userAgent = request.getHeader("User-Agent");
-            boolean isIeBrowser = userAgent.indexOf("MSIE") > 0;
-            //根据fileId,从gaea_file中读出filePath
+            // fileId必填
+            if(StringUtils.isBlank(fileId)){
+                throw BusinessExceptionBuilder.build(ResponseCode.FILE_ONT_EXSIT);
+            }
+            // 根据fileId,从gaea_file中读出filePath
             LambdaQueryWrapper<GaeaFile> queryWrapper = Wrappers.lambdaQuery();
             queryWrapper.eq(GaeaFile::getFileId, fileId);
             GaeaFile gaeaFile = gaeaFileMapper.selectOne(queryWrapper);
             if (null == gaeaFile) {
                 throw BusinessExceptionBuilder.build(ResponseCode.FILE_ONT_EXSIT);
             }
-            //解析文件路径、文件名和后缀
-            String filePath = gaeaFile.getFilePath();
-            if (StringUtils.isBlank(filePath)) {
+
+            String userAgent = request.getHeader("User-Agent");
+            boolean isIEBrowser = userAgent.indexOf("MSIE") > 0;
+            // 在oss中存储的文件名 402b6193e70e40a9bf5b73a78ea1e8ab.png
+            String fileObjectName = gaeaFile.getFileId().concat(".").concat(gaeaFile.getFileType());
+            String originalFilename = gaeaFile.getFileInstruction();
+            if (StringUtils.isBlank(fileObjectName) || StringUtils.isBlank(originalFilename)) {
                 throw BusinessExceptionBuilder.build(ResponseCode.FILE_ONT_EXSIT);
             }
-            String filename = filePath.substring(filePath.lastIndexOf(File.separator));
-            String fileSuffix = filename.substring(filename.lastIndexOf("."));
-
-            //根据文件后缀来判断,是显示图片\视频\音频,还是下载文件
-            File file = new File(filePath);
-            ResponseEntity.BodyBuilder builder = ResponseEntity.ok();
-            builder.contentLength(file.length());
-            if (StringPatternUtil.stringMatchIgnoreCase(fileSuffix, "(.png|.jpg|.jpeg|.bmp|.gif|.icon)")) {
-                builder.cacheControl(CacheControl.noCache()).contentType(MediaType.IMAGE_PNG);
-            } else if (StringPatternUtil.stringMatchIgnoreCase(fileSuffix, "(.flv|.swf|.mkv|.avi|.rm|.rmvb|.mpeg|.mpg|.ogg|.ogv|.mov|.wmv|.mp4|.webm|.wav|.mid|.mp3|.aac)")) {
-                builder.header("Content-Type", "video/mp4; charset=UTF-8");
-            } else {
-                //application/octet-stream 二进制数据流(最常见的文件下载)
-                builder.contentType(MediaType.APPLICATION_OCTET_STREAM);
-                filename = URLEncoder.encode(filename, "UTF-8");
-                if (isIeBrowser) {
-                    builder.header("Content-Disposition", "attachment; filename=" + filename);
-                } else {
-                    builder.header("Content-Disposition", "attacher; filename*=UTF-8''" + filename);
-                }
+            if (!originalFilename.endsWith(".".concat(gaeaFile.getFileType()))) {
+                originalFilename = originalFilename.concat(".").concat(gaeaFile.getFileType());
             }
-            return builder.body(FileUtils.readFileToByteArray(file));
+
+            // 调用文件存储工厂,读取文件,返回字节数组
+            byte[] fileBytes = gaeaOSSTemplate.downloadFile(fileObjectName);
+
+            // 根据文件后缀来判断,是显示图片\视频\音频,还是下载文件
+            return ResponseUtil.writeBody(originalFilename, fileBytes, isIEBrowser);
         } catch (Exception e) {
-            log.error("file download error: {}", e);
-            return null;
+            log.error("file download error", e);
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
+    /**
+     * 获取文件
+     *
+     * @param fileId
+     * @return
+     */
+    @Override
+    public byte[] getFile(String fileId) {
+        // fileId必填
+        if(StringUtils.isBlank(fileId)){
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_ONT_EXSIT);
+        }
+        // 根据fileId,从gaea_file中读出filePath
+        LambdaQueryWrapper<GaeaFile> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(GaeaFile::getFileId, fileId);
+        GaeaFile gaeaFile = gaeaFileMapper.selectOne(queryWrapper);
+        if (null == gaeaFile) {
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_ONT_EXSIT);
+        }
+
+        // 在oss中存储的文件名 402b6193e70e40a9bf5b73a78ea1e8ab.png
+        String fileObjectName = gaeaFile.getFileId().concat(".").concat(gaeaFile.getFileType());
+        String originalFilename = gaeaFile.getFileInstruction();
+        if (StringUtils.isBlank(fileObjectName) || StringUtils.isBlank(originalFilename)) {
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_ONT_EXSIT);
+        }
+
+        // 调用文件存储工厂,读取文件,返回字节数组
+        return gaeaOSSTemplate.downloadFile(fileObjectName);
+    }
+
     /**
      * 批处理操作后续处理
      * 删除本地已经存在的文件
@@ -209,13 +209,7 @@ public class GaeaFileServiceImpl implements GaeaFileService {
     public void processBatchAfterOperation(List<GaeaFile> entities, BaseOperationEnum operationEnum) throws BusinessException {
         if (operationEnum.equals(BaseOperationEnum.DELETE_BATCH)) {
             // 删除本地文件
-            entities.forEach(gaeaFile -> {
-                String filePath = gaeaFile.getFilePath();
-                File file = new File(filePath);
-                if (file.exists()) {
-                    file.delete();
-                }
-            });
+            entities.forEach(gaeaFile -> gaeaOSSTemplate.deleteFile(gaeaFile.getFileId().concat(".").concat(gaeaFile.getFileType())));
         }
 
     }

+ 35 - 26
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/service/impl/ReportExcelServiceImpl.java

@@ -7,6 +7,7 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.anji.plus.gaea.constant.BaseOperationEnum;
 import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
 import com.anji.plus.gaea.exception.BusinessException;
+import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
 import com.anji.plus.gaea.utils.GaeaAssert;
 import com.anji.plus.gaea.utils.GaeaBeanUtils;
 import com.anjiplus.template.gaea.business.code.ResponseCode;
@@ -14,8 +15,7 @@ import com.anjiplus.template.gaea.business.enums.ExportTypeEnum;
 import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.DataSetDto;
 import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.OriginalDataDto;
 import com.anjiplus.template.gaea.business.modules.dataset.service.DataSetService;
-import com.anjiplus.template.gaea.business.modules.file.dao.GaeaFileMapper;
-import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
+import com.anjiplus.template.gaea.business.modules.file.service.GaeaFileService;
 import com.anjiplus.template.gaea.business.modules.report.dao.ReportMapper;
 import com.anjiplus.template.gaea.business.modules.report.dao.entity.Report;
 import com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto.ReportExcelDto;
@@ -23,7 +23,6 @@ import com.anjiplus.template.gaea.business.modules.reportexcel.dao.ReportExcelMa
 import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel;
 import com.anjiplus.template.gaea.business.modules.reportexcel.service.ReportExcelService;
 import com.anjiplus.template.gaea.business.modules.reportexcel.util.CellType;
-import com.anjiplus.template.gaea.business.modules.reportexcel.util.XlsSheetUtil;
 import com.anjiplus.template.gaea.business.modules.reportexcel.util.XlsUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.commons.lang3.StringUtils;
@@ -34,8 +33,15 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.io.*;
-import java.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * TODO
@@ -54,18 +60,17 @@ public class ReportExcelServiceImpl implements ReportExcelService {
     @Autowired
     private DataSetService dataSetService;
 
+    @Autowired
+    private GaeaFileService gaeaFileService;
+
 
     @Autowired
     private ReportMapper reportMapper;
 
-    @Value("${customer.file.dist-path:''}")
+    @Value("${customer.file.tmp-path:.}")
     private String dictPath;
 
-    @Value("${customer.file.downloadPath:''}")
-    private String fileDownloadPath;
-
-    @Autowired
-    private GaeaFileMapper gaeaFileMapper;
+    private final static String ZIP_PATH = "/tmp_zip/";
 
 
     @Override
@@ -139,26 +144,30 @@ public class ReportExcelServiceImpl implements ReportExcelService {
             reportExcelDto.setJsonStr(report.getJsonStr());
             String jsonStr = analysisReportData(reportExcelDto);
             List<JSONObject> lists=(List<JSONObject> ) JSON.parse(jsonStr);
-            OutputStream out;
+            OutputStream out = null;
+            File file = null;
             try {
-                String fileId = UUID.randomUUID().toString();
-                String filePath = dictPath + File.separator + fileId + ".xlsx";
-                String urlPath = fileDownloadPath + java.io.File.separator + fileId;
-
-                GaeaFile gaeaFile = new GaeaFile();
-                gaeaFile.setFilePath(filePath);
-                gaeaFile.setFileId(fileId);
-                gaeaFile.setUrlPath(urlPath);
-                gaeaFile.setFileType("xlsx");
-                gaeaFile.setFileInstruction(reportCode + ".xlsx");
-
-                out = new FileOutputStream(filePath);
+                String fileName = report.getReportCode();
+                File dir = new File(dictPath + ZIP_PATH);
+                if (!dir.exists()){
+                    dir.mkdirs();
+                }
+                String filePath = dir.getAbsolutePath() + File.separator + fileName + ".xlsx";
+                file = new File(filePath);
+                out = Files.newOutputStream(Paths.get(filePath));
                 XlsUtil.exportXlsFile(out, true, lists);
+                gaeaFileService.upload(file);
 
-                gaeaFileMapper.insert(gaeaFile);
-                logger.info("导出成功:{}", gaeaFile);
             } catch (IOException e) {
                 logger.error("导出失败", e);
+            }finally {
+                try {
+                    out.close();
+                    file.delete();
+                } catch (IOException e) {
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
+                }
+
             }
         }
         return true;

+ 68 - 24
report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java

@@ -1,7 +1,7 @@
 package com.anjiplus.template.gaea.business.util;
 
-import com.anji.plus.gaea.code.ResponseCode;
 import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
+import com.anjiplus.template.gaea.business.code.ResponseCode;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -42,7 +42,7 @@ public class FileUtil {
             log.info("链接下载图片:{},临时路径:{}", urlPath, path);
         } catch (IOException e) {
             log.error("根据链接下载失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
@@ -66,14 +66,14 @@ public class FileUtil {
             outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
         } catch (IOException e) {
             log.error("复制文件失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         } finally {
             try {
                 inputChannel.close();
                 outputChannel.close();
             } catch (IOException e) {
                 log.error("", e);
-                throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
             }
         }
     }
@@ -106,7 +106,7 @@ public class FileUtil {
             outputStream.close();
         } catch (Exception e) {
             log.error("写入文件失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
@@ -132,20 +132,20 @@ public class FileUtil {
             return sbf.toString();
         } catch (IOException e) {
             log.error("读文件失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         } finally {
             if (null != isr) {
                 try {
                     isr.close();
                 } catch (IOException e) {
-                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                 }
             }
             if (reader != null) {
                 try {
                     reader.close();
                 } catch (IOException e1) {
-                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e1.getMessage());
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e1.getMessage());
                 }
             }
         }
@@ -185,14 +185,14 @@ public class FileUtil {
             compress(srcFile, zipOut, baseDir);
         } catch (IOException e) {
             log.error("压缩文件夹失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         } finally {
             if (null != zipOut) {
                 try {
                     zipOut.close();
                 } catch (IOException e) {
                     log.error("", e);
-                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                 }
                 out = null;
             }
@@ -201,7 +201,7 @@ public class FileUtil {
                     out.close();
                 } catch (IOException e) {
                     log.error("", e);
-                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                 }
             }
         }
@@ -246,14 +246,14 @@ public class FileUtil {
 
         } catch (IOException e) {
             log.error("压缩文件夹失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         } finally {
             if (null != bis) {
                 try {
                     bis.close();
                 } catch (IOException e) {
                     log.error("", e);
-                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                 }
             }
         }
@@ -264,23 +264,26 @@ public class FileUtil {
             decompress(new ZipFile(zipFile), dstPath);
         } catch (IOException e) {
             log.error("", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
     public static void decompress(MultipartFile zipFile, String dstPath) {
         try {
-            File file = new File(dstPath + File.separator + zipFile.getOriginalFilename());
-            if (!file.getParentFile().exists()) {
-                file.getParentFile().mkdirs();
+            File dir = new File(dstPath);
+            if (!dir.exists()){
+                dir.mkdirs();
             }
+            String path = dir.getPath();
+            String absolutePath = dir.getAbsolutePath();
+            File file = new File(dir.getAbsolutePath() + File.separator + zipFile.getOriginalFilename());
             zipFile.transferTo(file);
             decompress(new ZipFile(file), dstPath);
             //解压完删除
             file.delete();
         } catch (IOException e) {
             log.error("", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
@@ -326,14 +329,14 @@ public class FileUtil {
                     }
                 } catch (IOException e) {
                     log.error("解压失败", e);
-                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                 } finally {
                     if (null != in) {
                         try {
                             in.close();
                         } catch (IOException e) {
                             log.error("", e);
-                            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                         }
                     }
 
@@ -342,7 +345,7 @@ public class FileUtil {
                             out.close();
                         } catch (IOException e) {
                             log.error("", e);
-                            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
                         }
                     }
                 }
@@ -350,7 +353,7 @@ public class FileUtil {
             zip.close();
         } catch (IOException e) {
             log.error("解压失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
@@ -371,7 +374,7 @@ public class FileUtil {
             ins.close();
         } catch (Exception e) {
             log.error("获取流文件失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
         }
     }
 
@@ -399,8 +402,49 @@ public class FileUtil {
             });
         } catch (IOException e) {
             log.error("删除文件失败", e);
-            throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
+        }
+    }
+
+    /**
+     * byte 转file
+     */
+    public static File byte2File(byte[] buf, String filePath, String fileName){
+        BufferedOutputStream bos = null;
+        FileOutputStream fos = null;
+        File file = null;
+        try{
+            File dir = new File(filePath);
+            if (!dir.exists()){
+                dir.mkdirs();
+            }
+            file = new File(filePath + File.separator + fileName);
+            fos = new FileOutputStream(file);
+            bos = new BufferedOutputStream(fos);
+            bos.write(buf);
+        }catch (Exception e){
+            log.error("", e);
+            throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
+        }
+        finally{
+            if (bos != null){
+                try{
+                    bos.close();
+                }catch (IOException e){
+                    log.error("", e);
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
+                }
+            }
+            if (fos != null){
+                try{
+                    fos.close();
+                }catch (IOException e){
+                    log.error("", e);
+                    throw BusinessExceptionBuilder.build(ResponseCode.FILE_OPERATION_FAILED, e.getMessage());
+                }
+            }
         }
+        return file;
     }
 
 

+ 13 - 4
report-core/src/main/resources/bootstrap-dev.yml

@@ -4,8 +4,17 @@ spring:
     url: jdbc:mysql://10.108.26.197:3306/aj_report?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
     username: root
     password: appuser@anji
+  gaea:
+    subscribes:
+      oss: #文件存储
+        enabled: true
+        ##允许上传的文件后缀
+        file-type-while-list: .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi|.jpeg
+        # 用于文件上传成功后,生成文件的下载公网完整URL
+        downloadPath: http://127.0.0.1:9095/file/download
+        nfs:
+          path: D:\\aaa\\
+
+
+
 
-customer:
-  file:
-    dist-path: D:\Workspace\AJ-Report\report-core\upload
-    downloadPath: http://127.0.0.1:9095/file/download

+ 0 - 4
report-core/src/main/resources/bootstrap-prod.yml

@@ -4,7 +4,3 @@ spring:
     url: jdbc:mysql://10.108.26.197:3306/aj_report?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
     username: root
     password: appuser@anji
-
-customer:
-  file:
-    dist-path: /app/disk/upload/

+ 36 - 10
report-core/src/main/resources/bootstrap.yml

@@ -22,6 +22,14 @@ spring:
     url: jdbc:mysql://10.108.26.197:3306/aj_report?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
     username: root
     password: appuser@anji
+
+  #如果要使用redis,请参考report-core目录下的README.md文件中写的进行调整
+#  redis:
+#    host: 127.0.0.1
+#    port: 6379
+#    password: root
+#    database: 1
+
   #数据源连接池配置
   druid:
     initial-size: 10 # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
@@ -46,6 +54,31 @@ spring:
     placeholder-replacement: false
     init-sqls:
       - CREATE DATABASE IF NOT EXISTS `aj_report` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
+  gaea:
+    subscribes:
+      oss: #文件存储 都配置的情况下优先级minio->amazonS3->nfs
+        enabled: true
+        ##允许上传的文件后缀
+        file-type-while-list: .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi|.jpeg|.aaa
+        # 用于文件上传成功后,生成文件的下载公网完整URL
+        downloadPath: http://10.108.26.197:9095/file/download
+        nfs:
+          #上传对应本地全路径,目录必须是真实存在的,注意 Win是 \ 且有盘符,linux是 / 无盘符
+          path: /app/disk/upload/
+        #若要使用minio文件存储,请启用以下配置
+        #minio:
+        #  url: http://127.0.0.1
+        #  port: 9000
+        #  access-key: minioreport
+        #  secret-key: minioreport
+        #  bucket-name: aj-report
+        #若要使用amazonS3文件存储,请启用以下配置
+        #amazonS3:
+        #  url: http://127.0.0.1
+        #  access-key: access-key
+        #  secret-key: secret-key
+        #  bucket-name: AJ-Report
+        #若minio和amazonS3都没有,使用服务器高可用的nfs共享盘
 
 mybatis-plus:
   configuration:
@@ -64,18 +97,11 @@ logging:
 
 # 本应用自定义参数
 customer:
-  # 开发测试用本地文件,如果是生产,请考虑使用对象存储
-  file:
-    #上传对应本地全路径,目录必须是真实存在的,注意 Win是 \ 且有盘符,linux是 / 无盘符
-    dist-path: /app/disk/upload/
-    #dist-path: D:\Download
-    white-list: .png|.jpg|.jpeg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi
-    excelSuffix: .xlsx|.xls|.csv
-    #上传对应下载的下载链接路径 http://serverip:9095/file/download
-    downloadPath: http://10.108.26.197:9095/file/download
-
   # 跳过token验证和权限验证的url清单
   skip-authenticate-urls: /gaeaDict/all, /login, /static, /file/download/, /index.html, /favicon.ico, /reportShare/detailByCode
+  file:
+    #导入导出临时文件夹 默认.代表当前目录,拼接/tmp_zip/目录
+    tmpPath: .
   user:
     ##新增用户默认密码
     default:

+ 9 - 8
report-core/src/main/resources/i18n/messages_en_US.properties

@@ -9,8 +9,9 @@ user.old.password.error=user old password error
 1013=The code does not allow duplication
 2001=File names are not allowed to be empty
 2002=Unsupported suffix type
-2003=File upload failed
+2003=File upload failed:{0}
 2004=File does not exist
+file.operation.failed=File operation failed\uFF1A{0}
 
 field.not.null={} can not be null
 field.not.empty={} can not be empty
@@ -37,16 +38,16 @@ Rule.field.value.is.required=Rule field value is required
 Rule.field.value.type.error=Rule field value type error
 Rule.fields.check.error=Rule fields check error
 Component.load.check.error={0} Component not load
-4001=Data source connection failed
+4001=Data source connection failed {0}
 4002=Data source type is not currently supported
-4003=execute sql error
-4004=Incomplete parameter replacement values
-4005=execute js error
-4006=analysis data error
+4003=execute sql error, {0}
+4004=Incomplete parameter replacement values {0}
+4005=execute js error {0}
+4006=analysis data error {0}
 4007=The report code does not allow duplication
 4008=The set code does not allow duplication
-4009=The source code does not allow duplication
+4009=The source code does not allow duplication {0}
 4010=Can't auto find match driver class
-4011=execute javaBean error
+4011=execute javaBean error {0}
 
 report.share.link.invalid=report share link invalid

+ 9 - 8
report-core/src/main/resources/i18n/messages_zh_CN.properties

@@ -8,8 +8,9 @@ user.old.password.error=\u65E7\u5BC6\u7801\u4E0D\u6B63\u786E
 1013=\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D
 2001=\u6587\u4EF6\u540D\u4E0D\u5141\u8BB8\u4E3A\u7A7A
 2002=\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301
-2003=\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25
+2003=\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25\uFF1A{0}
 2004=\u6587\u4EF6\u4E0D\u5B58\u5728
+file.operation.failed=\u6587\u4EF6\u64CD\u4F5C\u5931\u8D25\uFF1A{0}
 
 field.not.null={}\u4E0D\u80FD\u4E3Anull
 field.not.empty={}\u4E0D\u80FD\u4E3A\u7A7A\u5B57\u7B26\u4E32
@@ -38,17 +39,17 @@ Rule.field.value.type.error=\u89C4\u5219\u5B57\u6BB5\u503C\u7C7B\u578B\u9519\u8B
 Rule.fields.check.error=\u89C4\u5219\u53C2\u6570\u6821\u9A8C\u4E0D\u901A\u8FC7
 Component.load.check.error={0}\u7EC4\u4EF6\u672A\u52A0\u8F7D
 
-4001=\u6570\u636E\u6E90\u8FDE\u63A5\u5931\u8D25
+4001=\u6570\u636E\u6E90\u8FDE\u63A5\u5931\u8D25\uFF0C{0}
 4002=\u6570\u636E\u6E90\u7C7B\u578B\u6682\u4E0D\u652F\u6301
-4003=\u6267\u884Csql\u5931\u8D25
-4004=\u53C2\u6570\u66FF\u6362\u503C\u4E0D\u5168
-4005=\u6267\u884Cjs\u5931\u8D25
-4006=\u89E3\u6790\u6570\u636E\u5931\u8D25
+4003=\u6267\u884Csql\u5931\u8D25\uFF0C{0}
+4004=\u53C2\u6570\u66FF\u6362\u503C\u4E0D\u5168\uFF0C{0}
+4005=\u6267\u884Cjs\u5931\u8D25\uFF0C{0}
+4006=\u89E3\u6790\u6570\u636E\u5931\u8D25\uFF0C{0}
 4007=\u62A5\u8868\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D
 4008=\u6570\u636E\u96C6\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D
 4009=\u6570\u636E\u6E90\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D
-4010=\u9A71\u52A8\u5305\u4E0D\u5B58\u5728
-4011=\u6267\u884CjavaBean\u5931\u8D25
+4010=\u9A71\u52A8\u5305\u4E0D\u5B58\u5728\uFF0C{0}
+4011=\u6267\u884CjavaBean\u5931\u8D25\uFF0C{0}
 6001={0}
 
 7001=\u89E3\u6790\u5931\u8D25

+ 2 - 2
report-ui/src/utils/request.js

@@ -27,8 +27,8 @@ service.interceptors.response.use(
      * code为非20000是抛错 可结合自己业务进行修改
      */
     const res = response.data
-    // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
-    if (res.code == '50008' || res.code == '50012' || res.code == '50014') {
+    // User.credentials.expired:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
+    if (res.code == 'User.credentials.expired' || res.code == '50012' || res.code == '50014') {
       MessageBox.confirm(
         '你已被登出,可以取消继续留在该页面,或者重新登录',
         '重新登录',

+ 1 - 1
report-ui/src/views/excelreport/designer/index.vue

@@ -25,7 +25,7 @@
           <el-collapse-item :title="item.setName" :name="item.id">
             <el-popconfirm
               :title="'确定删除' + item.setName + '吗?'"
-              @onConfirm="del(item)"
+              @confirm="del(item)"
             >
               <el-button
                 slot="reference"

+ 4 - 4
report-ui/src/views/layout/components/Navbar.vue

@@ -90,14 +90,14 @@
       width="34%"
       center
     >
-      <div style="font-size: 30px; line-height: 50px; margin-bottom: 50px">
+      <div style="font-size: 20px; line-height: 50px; margin-bottom: 50px">
         AJ-Report由<a href="http://www.anji-plus.com/" target="_blank" style="text-decoration: underline"><b>安吉加加信息技术有限公司</b></a
-      >遵循 <a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank" style="word-wrap: break-word"><strong style="color: orangered">Apache2.0开源协议</strong></a
+      >遵循 <a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank" style="text-decoration: underline; word-wrap: break-word"><strong style="color: orangered" >Apache2.0开源协议</strong></a
       >在<a href="https://gitee.com/explore" target="_blank" style="text-decoration: underline; word-wrap: break-word"><b>Gitee平台</b></a
       >进行开源。
       </div>
-      <div style="font-size: 30px; line-height: 50px">
-        <strong style="color: orangered">个人/商业使用须遵循Apache2.0开源协议。</strong>
+      <div style="font-size: 20px; line-height: 50px">
+        <strong> 个人/商业使用须遵循Apache2.0开源协议。</strong>
         <strong style="color: orangered">禁止将AJ-Report产品用于违法违规业务。</strong>
       </div>
       <span slot="footer" class="dialog-footer">

+ 1 - 1
report-ui/src/views/layout/components/Sidebar/index.vue

@@ -3,7 +3,7 @@
     <div class="admin-title" @click="goBigScreen">
       <div class="con">
         <img src="../../../../../static/logo-dp.png" width="50" />
-        <span class="version">V0.9.8.1</span>
+        <span class="version">V0.9.8.5</span>
       </div>
     </div>
     <el-menu

+ 4 - 4
report-ui/src/views/login.vue

@@ -133,14 +133,14 @@
       width="34%"
       center
     >
-      <div style="font-size: 30px; line-height: 50px; margin-bottom: 50px">
+      <div style="font-size: 20px; line-height: 50px; margin-bottom: 50px">
         AJ-Report由<a href="http://www.anji-plus.com/" target="_blank" style="text-decoration: underline"><b>安吉加加信息技术有限公司</b></a
-      >遵循 <a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank" style="word-wrap: break-word"><strong style="color: orangered">Apache2.0开源协议</strong></a
+      >遵循 <a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank" style="text-decoration: underline; word-wrap: break-word"><strong style="color: orangered" >Apache2.0开源协议</strong></a
       >在<a href="https://gitee.com/explore" target="_blank" style="text-decoration: underline; word-wrap: break-word"><b>Gitee平台</b></a
       >进行开源。
       </div>
-      <div style="font-size: 30px; line-height: 50px">
-        <strong style="color: orangered">个人/商业使用须遵循Apache2.0开源协议。</strong>
+      <div style="font-size: 20px; line-height: 50px">
+        <strong> 个人/商业使用须遵循Apache2.0开源协议。</strong>
         <strong style="color: orangered">禁止将AJ-Report产品用于违法违规业务。</strong>
       </div>
       <span slot="footer" class="dialog-footer">