李旭 4 anos atrás
pai
commit
191d72f8ea

+ 71 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/controller/GaeaFileController.java

@@ -0,0 +1,71 @@
+package com.anjiplus.template.gaea.business.modules.file.controller;
+
+import com.anji.plus.gaea.bean.ResponseBean;
+import com.anji.plus.gaea.curd.service.GaeaBaseService;
+import com.anjiplus.template.gaea.business.base.BaseController;
+import com.anjiplus.template.gaea.business.modules.file.controller.dto.GaeaFileDTO;
+import com.anjiplus.template.gaea.business.modules.file.controller.param.GaeaFileParam;
+import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
+import com.anjiplus.template.gaea.business.modules.file.service.GaeaFileService;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * (GaeaFile)实体类
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:33
+ */
+@RestController
+@RequestMapping("/file")
+@Api(value = "/file", tags = "")
+public class GaeaFileController extends BaseController<GaeaFileParam, GaeaFile, GaeaFileDTO> {
+    @Autowired
+    private GaeaFileService gaeaFileService;
+
+    @PostMapping("/upload")
+    public ResponseBean upload(@RequestParam("file") MultipartFile file) {
+        return ResponseBean.builder().message("success").data((gaeaFileService.upload(file))).build();
+    }
+
+    @GetMapping(value = "/download/{fileId}")
+    public ResponseEntity<byte[]> download(HttpServletRequest request, HttpServletResponse response, @PathVariable("fileId") String fileId) {
+        return gaeaFileService.download(request, response, fileId);
+    }
+
+    /**
+     * 获取实际服务类
+     *
+     * @return
+     */
+    @Override
+    public GaeaBaseService<GaeaFileParam, GaeaFile> getService() {
+        return gaeaFileService;
+    }
+
+    /**
+     * 获取当前Controller数据库实体Entity
+     *
+     * @return
+     */
+    @Override
+    public GaeaFile getEntity() {
+        return new GaeaFile();
+    }
+
+    /**
+     * 获取当前Controller数据传输DTO
+     *
+     * @return
+     */
+    @Override
+    public GaeaFileDTO getDTO() {
+        return new GaeaFileDTO();
+    }
+}

+ 67 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/controller/dto/GaeaFileDTO.java

@@ -0,0 +1,67 @@
+package com.anjiplus.template.gaea.business.modules.file.controller.dto;
+
+import com.anji.plus.gaea.curd.dto.GaeaBaseDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * (GaeaFile)实体类
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:27
+ */
+@ApiModel(value = "")
+public class GaeaFileDTO extends GaeaBaseDTO {
+
+    /**
+     * 文件uuid
+     */
+    private String fileId;
+    /**
+     * 文件在linux中的完整目录,比如/app/dist/export/excel/${fileid}.xlsx
+     */
+    @ApiModelProperty(value = "文件在linux中的完整目录,比如/app/dist/export/excel/${fileid}.xlsx")
+    private String filePath;
+    /**
+     * 通过接口的下载完整http路径
+     */
+    @ApiModelProperty(value = "通过接口的下载完整http路径")
+    private String urlPath;
+    /**
+     * 文件内容说明,比如 对账单(202001~202012)
+     */
+    @ApiModelProperty(value = "文件内容说明,比如 对账单(202001~202012)")
+    private String fileInstruction;
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public String getUrlPath() {
+        return urlPath;
+    }
+
+    public void setUrlPath(String urlPath) {
+        this.urlPath = urlPath;
+    }
+
+    public String getFileInstruction() {
+        return fileInstruction;
+    }
+
+    public void setFileInstruction(String fileInstruction) {
+        this.fileInstruction = fileInstruction;
+    }
+
+    public String getFileId() {
+        return fileId;
+    }
+
+    public void setFileId(String fileId) {
+        this.fileId = fileId;
+    }
+}

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

@@ -0,0 +1,31 @@
+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 java.io.Serializable;
+
+/**
+ * (GaeaFile)param
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:29
+ */
+public class GaeaFileParam extends PageParam implements Serializable {
+
+    /**
+     * 文件在linux中的完整目录,比如/app/dist/export/excel/${fileid}.xlsx
+     */
+    @Query(QueryEnum.LIKE)
+    private String filePath;
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+}

+ 17 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/dao/GaeaFileMapper.java

@@ -0,0 +1,17 @@
+package com.anjiplus.template.gaea.business.modules.file.dao;
+
+import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
+import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * (GaeaFile)Mapper
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:24
+ */
+@Mapper
+public interface GaeaFileMapper extends GaeaBaseMapper<GaeaFile> {
+
+
+}

+ 65 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/entity/GaeaFile.java

@@ -0,0 +1,65 @@
+package com.anjiplus.template.gaea.business.modules.file.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.anji.plus.gaea.curd.entity.GaeaBaseEntity;
+
+import java.io.Serializable;
+
+/**
+ * (GaeaFile)实体类
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:20
+ */
+@TableName("gaea_file")
+public class GaeaFile extends GaeaBaseEntity implements Serializable {
+
+    /**
+     * 文件uuid
+     */
+    private String fileId;
+    /**
+     * 文件在linux中的完整目录,比如/app/dist/export/excel/${fileid}.xlsx
+     */
+    private String filePath;
+    /**
+     * 通过接口的下载完整http路径
+     */
+    private String urlPath;
+    /**
+     * 文件内容说明,比如 对账单(202001~202012)
+     */
+    private String fileInstruction;
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public String getUrlPath() {
+        return urlPath;
+    }
+
+    public void setUrlPath(String urlPath) {
+        this.urlPath = urlPath;
+    }
+
+    public String getFileInstruction() {
+        return fileInstruction;
+    }
+
+    public void setFileInstruction(String fileInstruction) {
+        this.fileInstruction = fileInstruction;
+    }
+
+    public String getFileId() {
+        return fileId;
+    }
+
+    public void setFileId(String fileId) {
+        this.fileId = fileId;
+    }
+}

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

@@ -0,0 +1,38 @@
+package com.anjiplus.template.gaea.business.modules.file.service;
+
+import com.anji.plus.gaea.curd.service.GaeaBaseService;
+import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
+import com.anjiplus.template.gaea.business.modules.file.controller.param.GaeaFileParam;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * (GaeaFile)Service
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:25
+ */
+public interface GaeaFileService extends GaeaBaseService<GaeaFileParam, GaeaFile> {
+
+
+    /**
+     * 文件上传
+     *
+     * @param file
+     * @return 文件访问路径
+     */
+    String upload(MultipartFile file);
+
+    /**
+     * 根据fileId显示图片或者下载文件
+     *
+     * @param request
+     * @param response
+     * @param fileId
+     * @return
+     */
+    ResponseEntity<byte[]> download(HttpServletRequest request, HttpServletResponse response, String fileId);
+}

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

@@ -0,0 +1,187 @@
+package com.anjiplus.template.gaea.business.modules.file.service.impl;
+
+import com.anji.plus.gaea.constant.BaseOperationEnum;
+import com.anji.plus.gaea.exception.BusinessException;
+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.export.dao.GaeaExportMapper;
+import com.anjiplus.template.gaea.business.modules.export.dao.entity.GaeaExport;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.anjiplus.template.gaea.common.RespCommonCode;
+import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
+import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
+import com.anjiplus.template.gaea.common.util.StringPatternUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+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.http.ResponseEntity;
+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.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * (GaeaFile)ServiceImpl
+ *
+ * @author peiyanni
+ * @since 2021-02-18 14:48:26
+ */
+@Service
+@Slf4j
+public class GaeaFileServiceImpl implements GaeaFileService {
+
+    @Value("${file.dist-path:''}")
+    private String dictPath;
+
+    @Value("${file.white-list:''}")
+    private String whiteList;
+
+    @Value("${file.excelSuffix:''}")
+    private String excelSuffix;
+
+    @Value("${file.downloadPath:''}")
+    private String fileDownloadPath;
+
+    @Autowired
+    private GaeaFileMapper gaeaFileMapper;
+    @Autowired
+    private GaeaExportMapper gaeaExportMapper;
+
+    @Override
+    public GaeaBaseMapper<GaeaFile> getMapper() {
+        return gaeaFileMapper;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String upload(MultipartFile file) {
+        try {
+            String fileName = file.getOriginalFilename();
+            if (StringUtils.isBlank(fileName)) {
+                throw BusinessExceptionBuilder.build(RespCommonCode.FILE_EMPTY_FILENAME);
+            }
+            String suffixName = fileName.substring(fileName.lastIndexOf("."));
+            //白名单校验(不区分大小写)
+            List<String> list = new ArrayList<String>(Arrays.asList(whiteList.split("\\|")));
+            list.addAll(list.stream().map(String::toUpperCase).collect(Collectors.toList()));
+            if (!list.contains(suffixName)) {
+                throw BusinessExceptionBuilder.build(RespCommonCode.FILE_SUFFIX_UNSUPPORTED);
+            }
+            // 生成文件唯一性标识
+            String fileId = UUID.randomUUID().toString();
+            String newFileName = fileId + suffixName;
+            // 本地文件保存路径
+            String filePath = dictPath + newFileName;
+            String urlPath = fileDownloadPath + File.separator + fileId;
+
+            GaeaFile gaeaFile = new GaeaFile();
+            gaeaFile.setFilePath(filePath);
+            gaeaFile.setFileId(fileId);
+            gaeaFile.setUrlPath(urlPath);
+            gaeaFileMapper.insert(gaeaFile);
+
+            //写文件 将文件保存/app/dictPath/upload/下
+            File dest = new File(dictPath + newFileName);
+            file.transferTo(dest);
+            // 将完整的http访问路径返回
+            return urlPath;
+        } catch (Exception e) {
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+            log.error("file upload error: {}", e);
+            throw BusinessExceptionBuilder.build(RespCommonCode.FILE_UPLOAD_ERROR);
+        }
+    }
+
+    @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
+            LambdaQueryWrapper<GaeaFile> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(GaeaFile::getFileId, fileId);
+            GaeaFile gaeaFile = gaeaFileMapper.selectOne(queryWrapper);
+            if (null == gaeaFile) {
+                throw BusinessExceptionBuilder.build(RespCommonCode.FILE_ONT_EXSIT);
+            }
+            //解析文件路径、文件名和后缀
+            String filePath = gaeaFile.getFilePath();
+            if (StringUtils.isBlank(filePath)) {
+                throw BusinessExceptionBuilder.build(RespCommonCode.FILE_ONT_EXSIT);
+            }
+            String filename = filePath.substring(filePath.lastIndexOf(File.separator));
+            String fileSuffix = filename.substring(filename.lastIndexOf("."));
+            //特殊处理:如果是excel文件,则从t_export表中查询文件名
+            List list = Arrays.asList(excelSuffix.split("\\|"));
+            if (list.contains(fileSuffix)) {
+                LambdaQueryWrapper<GaeaExport> exportWrapper = Wrappers.lambdaQuery();
+                exportWrapper.eq(GaeaExport::getFileId, fileId);
+                GaeaExport exportPO = gaeaExportMapper.selectOne(exportWrapper);
+                if (null != exportPO) {
+                    filename = exportPO.getFileTitle() + fileSuffix;
+                }
+            }
+            //根据文件后缀来判断,是显示图片\视频\音频,还是下载文件
+            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);
+                }
+            }
+            return builder.body(FileUtils.readFileToByteArray(file));
+        } catch (Exception e) {
+            log.error("file download error: {}", e);
+            return null;
+        }
+    }
+
+    /**
+     * 批处理操作后续处理
+     * 删除本地已经存在的文件
+     *
+     * @param entities
+     * @param operationEnum 操作类型
+     * @throws BusinessException 阻止程序继续执行或回滚事务
+     */
+    @Override
+    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();
+                }
+            });
+        }
+
+    }
+}