Sfoglia il codice sorgente

1. 基于 db 实现文件的存储

YunaiV 4 anni fa
parent
commit
629fa9b407
26 ha cambiato i file con 351 aggiunte e 168 eliminazioni
  1. 0 118
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
  2. 1 1
      ruoyi-ui/src/api/login.js
  3. 2 1
      ruoyi-ui/src/api/system/post.js
  4. 1 1
      ruoyi-ui/src/views/system/post/index.vue
  5. 12 0
      src/main/java/cn/iocoder/dashboard/framework/file/config/FileConfiguration.java
  6. 23 0
      src/main/java/cn/iocoder/dashboard/framework/file/config/FileProperties.java
  7. 16 0
      src/main/java/cn/iocoder/dashboard/framework/file/package-info.java
  8. 4 6
      src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
  9. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java
  10. 47 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysFileController.java
  11. 13 6
      src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/SysPostController.java
  12. 0 27
      src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelRespVO.java
  13. 32 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java
  14. 17 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExportReqVO.java
  15. 3 4
      src/main/java/cn/iocoder/dashboard/modules/system/convert/dept/SysPostConvert.java
  16. 15 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/common/SysFileMapper.java
  17. 6 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/dept/SysPostMapper.java
  18. 30 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/common/SysFileDO.java
  19. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dept/SysDeptDO.java
  20. 2 0
      src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
  21. 29 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/common/SysFileService.java
  22. 47 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/common/impl/SysFileServiceImpl.java
  23. 10 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysPostService.java
  24. 6 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/dept/impl/SysPostServiceImpl.java
  25. 25 1
      src/main/java/cn/iocoder/dashboard/util/servlet/ServletUtils.java
  26. 8 0
      src/main/resources/application.yaml

+ 0 - 118
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java

@@ -1,118 +0,0 @@
-package com.ruoyi.web.controller.common;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.file.FileUploadUtils;
-import com.ruoyi.common.utils.file.FileUtils;
-import com.ruoyi.framework.config.ServerConfig;
-
-/**
- * 通用请求处理
- * 
- * @author ruoyi
- */
-@RestController
-public class CommonController
-{
-    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
-
-    @Autowired
-    private ServerConfig serverConfig;
-
-    /**
-     * 通用下载请求
-     * 
-     * @param fileName 文件名称
-     * @param delete 是否删除
-     */
-    @GetMapping("common/download")
-    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
-    {
-        try
-        {
-            if (!FileUtils.checkAllowDownload(fileName))
-            {
-                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
-            }
-            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
-            String filePath = RuoYiConfig.getDownloadPath() + fileName;
-
-            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
-            FileUtils.setAttachmentResponseHeader(response, realFileName);
-            FileUtils.writeBytes(filePath, response.getOutputStream());
-            if (delete)
-            {
-                FileUtils.deleteFile(filePath);
-            }
-        }
-        catch (Exception e)
-        {
-            log.error("下载文件失败", e);
-        }
-    }
-
-    /**
-     * 通用上传请求
-     */
-    @PostMapping("/common/upload")
-    public AjaxResult uploadFile(MultipartFile file) throws Exception
-    {
-        try
-        {
-            // 上传文件路径
-            String filePath = RuoYiConfig.getUploadPath();
-            // 上传并返回新文件名称
-            String fileName = FileUploadUtils.upload(filePath, file);
-            String url = serverConfig.getUrl() + fileName;
-            AjaxResult ajax = AjaxResult.success();
-            ajax.put("fileName", fileName);
-            ajax.put("url", url);
-            return ajax;
-        }
-        catch (Exception e)
-        {
-            return AjaxResult.error(e.getMessage());
-        }
-    }
-
-    /**
-     * 本地资源通用下载
-     */
-    @GetMapping("/common/download/resource")
-    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
-            throws Exception
-    {
-        try
-        {
-            if (!FileUtils.checkAllowDownload(resource))
-            {
-                throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
-            }
-            // 本地资源路径
-            String localPath = RuoYiConfig.getProfile();
-            // 数据库资源地址
-            String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
-            // 下载名称
-            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
-            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
-            FileUtils.setAttachmentResponseHeader(response, downloadName);
-            FileUtils.writeBytes(downloadPath, response.getOutputStream());
-        }
-        catch (Exception e)
-        {
-            log.error("下载文件失败", e);
-        }
-    }
-}

+ 1 - 1
ruoyi-ui/src/api/login.js

@@ -34,7 +34,7 @@ export function logout() {
 // 获取验证码
 export function getCodeImg() {
   return request({
-    url: '/captcha/get-image',
+    url: '/system/captcha/get-image',
     method: 'get'
   })
 }

+ 2 - 1
ruoyi-ui/src/api/system/post.js

@@ -56,6 +56,7 @@ export function exportPost(query) {
   return request({
     url: '/system/post/export',
     method: 'get',
-    params: query
+    params: query,
+    responseType: 'blob'
   })
 }

+ 1 - 1
ruoyi-ui/src/views/system/post/index.vue

@@ -286,7 +286,7 @@ export default {
         }).then(function() {
           return exportPost(queryParams);
         }).then(response => {
-          this.download(response.msg);
+        this.downloadExcel(response, '岗位数据.xls');
         })
     }
   }

+ 12 - 0
src/main/java/cn/iocoder/dashboard/framework/file/config/FileConfiguration.java

@@ -0,0 +1,12 @@
+package cn.iocoder.dashboard.framework.file.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 文件 配置类
+ */
+@Configuration
+@EnableConfigurationProperties(FileProperties.class)
+public class FileConfiguration {
+}

+ 23 - 0
src/main/java/cn/iocoder/dashboard/framework/file/config/FileProperties.java

@@ -0,0 +1,23 @@
+package cn.iocoder.dashboard.framework.file.config;
+
+import cn.iocoder.dashboard.modules.system.controller.common.SysFileController;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotNull;
+
+@ConfigurationProperties(prefix = "yudao.file")
+@Validated
+@Data
+public class FileProperties {
+
+    /**
+     * 对应 {@link SysFileController#}
+     */
+    @NotNull(message = "基础文件路径不能为空")
+    private String basePath;
+
+    // TODO 七牛、等等
+
+}

+ 16 - 0
src/main/java/cn/iocoder/dashboard/framework/file/package-info.java

@@ -0,0 +1,16 @@
+/**
+ * 文件的存储,推荐使用七牛、阿里云、华为云、腾讯云等文件服务
+ *
+ * 在不采用云服务的情况下,我们有几种技术选型:
+ * 方案 1. 使用自建的文件服务,例如说 minIO、FastDFS 等等
+ * 方案 2. 使用服务器的文件系统存储
+ * 方案 3. 使用数据库进行存储
+ *
+ * 如果考虑额外在搭建服务,推荐方案 1。
+ * 对于方案 2 来说,如果要实现文件存储的高可用,需要多台服务器之间做实时同步,可以基于 rsync + inotify 来做
+ * 对于方案 3 的话,实现起来最简单,但是数据库本身不适合存储海量的文件
+ *
+ * 综合考虑,暂时使用方案 3 的方式,比较适合这样一个 all in one 的项目。
+ * 随着文件的量级大了之后,还是推荐采用云服务。
+ */
+package cn.iocoder.dashboard.framework.file;

+ 4 - 6
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java

@@ -1,7 +1,6 @@
 package cn.iocoder.dashboard.framework.security.config;
 
 import cn.iocoder.dashboard.framework.security.core.filter.JwtAuthenticationTokenFilter;
-import cn.iocoder.dashboard.framework.security.core.handler.AuthenticationEntryPointImpl;
 import cn.iocoder.dashboard.framework.security.core.handler.LogoutSuccessHandlerImpl;
 import cn.iocoder.dashboard.framework.web.config.WebProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -19,8 +18,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.access.AccessDeniedHandler;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-import org.springframework.security.web.authentication.logout.LogoutFilter;
-import org.springframework.web.filter.CorsFilter;
 
 import javax.annotation.Resource;
 
@@ -126,12 +123,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
                 // 登陆的接口,可匿名访问
                 .antMatchers(webProperties.getApiPrefix() + "/login").anonymous()
                 // 通用的接口,可匿名访问
-                .antMatchers( webProperties.getApiPrefix() + "/captcha/**").anonymous()
+                .antMatchers( webProperties.getApiPrefix() + "/system/captcha/**").anonymous()
                 // TODO
                 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
                 .antMatchers("/profile/**").anonymous()
-                .antMatchers("/common/download**").anonymous()
-                .antMatchers("/common/download/resource**").anonymous()
+                // 文件的获取接口,可匿名访问
+                .antMatchers(webProperties.getApiPrefix() + "/system/file/get/**").anonymous()
+                // TODO
                 .antMatchers("/swagger-ui.html").anonymous()
                 .antMatchers("/swagger-resources/**").anonymous()
                 .antMatchers("/webjars/**").anonymous()

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java

@@ -15,7 +15,7 @@ import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
 
 @Api(tags = "验证码 API")
 @RestController
-@RequestMapping("/captcha")
+@RequestMapping("/system/captcha")
 public class SysCaptchaController {
 
     @Resource

+ 47 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysFileController.java

@@ -0,0 +1,47 @@
+package cn.iocoder.dashboard.modules.system.controller.common;
+
+import cn.hutool.core.io.IoUtil;
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.common.SysFileDO;
+import cn.iocoder.dashboard.modules.system.service.common.SysFileService;
+import cn.iocoder.dashboard.util.servlet.ServletUtils;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+
+@Api(tags = "文件 API")
+@RestController
+@RequestMapping("/system/file")
+@Slf4j
+public class SysFileController {
+
+    @Resource
+    private SysFileService fileService;
+
+    @PostMapping("/upload")
+    public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file,
+                                           @RequestParam("path") String path) throws IOException {
+        return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream())));
+    }
+
+    @GetMapping("/get/{path}")
+    public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException {
+        SysFileDO file = fileService.getFile(path);
+        if (file == null) {
+            log.warn("[getFile][path({}) 文件不存在]", path);
+            response.setStatus(HttpStatus.NOT_FOUND.value());
+            return;
+        }
+        ServletUtils.writeAttachment(response, path, file.getContent());
+    }
+
+}

+ 13 - 6
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/SysPostController.java

@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.controller.dept;
 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
 import cn.iocoder.dashboard.common.pojo.CommonResult;
 import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.*;
 import cn.iocoder.dashboard.modules.system.convert.dept.SysPostConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysPostDO;
@@ -14,6 +15,8 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -80,12 +83,16 @@ public class SysPostController {
         return success(SysPostConvert.INSTANCE.convert(postService.getPost(id)));
     }
 
+    @GetMapping("/export")
+    @ApiOperation("岗位管理")
 //    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
 //    @PreAuthorize("@ss.hasPermi('system:post:export')")
-//    @GetMapping("/export")
-//    public AjaxResult export(SysPost post) {
-//        List<SysPost> list = postService.selectPostList(post);
-//        ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
-//        return util.exportExcel(list, "岗位数据");
-//    }
+    public void export(HttpServletResponse response, @Validated SysPostExportReqVO reqVO) throws IOException {
+        List<SysPostDO> posts = postService.listPosts(reqVO);
+        List<SysPostExcelVO> excelPosts = SysPostConvert.INSTANCE.convertList03(posts);
+        // 输出
+        ExcelUtils.write(response, "岗位数据.xls", "岗位列表",
+                SysPostExcelVO.class, excelPosts);
+    }
+
 }

+ 0 - 27
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelRespVO.java

@@ -1,27 +0,0 @@
-package cn.iocoder.dashboard.modules.system.controller.dept.vo.post;
-
-import cn.iocoder.dashboard.framework.excel.Excel;
-import lombok.Data;
-
-/**
- * 岗位 Excel 导出响应 VO
- */
-@Data
-public class SysPostExcelRespVO {
-
-    @Excel(name = "岗位序号", cellType = Excel.ColumnType.NUMERIC)
-    private Long id;
-
-    @Excel(name = "岗位编码")
-    private String code;
-
-    @Excel(name = "岗位名称")
-    private String name;
-
-    @Excel(name = "岗位排序")
-    private String sort;
-
-    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
-    private String status;
-
-}

+ 32 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java

@@ -0,0 +1,32 @@
+package cn.iocoder.dashboard.modules.system.controller.dept.vo.post;
+
+import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import static cn.iocoder.dashboard.modules.system.enums.dict.DictTypeEnum.SYS_COMMON_STATUS;
+
+/**
+ * 岗位 Excel 导出响应 VO
+ */
+@Data
+public class SysPostExcelVO {
+
+    @ExcelProperty("岗位序号")
+    private Long id;
+
+    @ExcelProperty("岗位编码")
+    private String code;
+
+    @ExcelProperty("岗位名称")
+    private String name;
+
+    @ExcelProperty("岗位排序")
+    private String sort;
+
+    @ExcelProperty(value = "状态", converter = DictConvert.class)
+    @DictFormat(SYS_COMMON_STATUS)
+    private String status;
+
+}

+ 17 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExportReqVO.java

@@ -0,0 +1,17 @@
+package cn.iocoder.dashboard.modules.system.controller.dept.vo.post;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@ApiModel(value = "岗位导出 Request VO", description = "参数和 SysPostExcelVO 是一致的")
+@Data
+public class SysPostExportReqVO {
+
+    @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
+    private String name;
+
+    @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 SysCommonStatusEnum 枚举类")
+    private Integer status;
+
+}

+ 3 - 4
src/main/java/cn/iocoder/dashboard/modules/system/convert/dept/SysPostConvert.java

@@ -1,10 +1,7 @@
 package cn.iocoder.dashboard.modules.system.convert.dept;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostCreateReqVO;
-import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostRespVO;
-import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostSimpleRespVO;
-import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.*;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysPostDO;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.mapstruct.Mapper;
@@ -31,4 +28,6 @@ public interface SysPostConvert {
 
     SysPostDO convert(SysPostUpdateReqVO reqVO);
 
+    List<SysPostExcelVO> convertList03(List<SysPostDO> list);
+
 }

+ 15 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/common/SysFileMapper.java

@@ -0,0 +1,15 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dao.common;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.common.SysFileDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SysFileMapper extends BaseMapper<SysFileDO> {
+
+    default Integer selectCountById(String id) {
+        return selectCount(new QueryWrapper<SysFileDO>().eq("id", id));
+    }
+
+}

+ 6 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/dept/SysPostMapper.java

@@ -2,6 +2,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dao.dept;
 
 import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
 import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils;
+import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostExportReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostPageReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysPostDO;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -26,6 +27,11 @@ public interface SysPostMapper extends BaseMapper<SysPostDO> {
                         .eqIfPresent("status", reqVO.getStatus()));
     }
 
+    default List<SysPostDO> selectList(SysPostExportReqVO reqVO) {
+        return selectList(new QueryWrapperX<SysPostDO>().likeIfPresent("name", reqVO.getName())
+                .eqIfPresent("status", reqVO.getStatus()));
+    }
+
     default SysPostDO selectByName(String name) {
         return selectOne(new QueryWrapper<SysPostDO>().eq("name", name));
     }

+ 30 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/common/SysFileDO.java

@@ -0,0 +1,30 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.common;
+
+import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 文件表
+ *
+ * @author 芋道源码
+ */
+@Data
+@TableName("sys_file")
+@EqualsAndHashCode(callSuper = true)
+public class SysFileDO extends BaseDO {
+
+    /**
+     * 文件路径
+     */
+    @TableId(type = IdType.INPUT)
+    private String id;
+    /**
+     * 文件内容
+     */
+    private byte[] content;
+
+}

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/dept/SysDeptDO.java

@@ -8,7 +8,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 /**
- * 部门表 sys_dept
+ * 部门表
  *
  * @author ruoyi
  */

+ 2 - 0
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java

@@ -69,4 +69,6 @@ public interface SysErrorCodeConstants {
     // ========== 通知公告 1002008000 ==========
     ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1002008001, "当前通知公告不存在");
 
+    // ========== 文件 1002009000 ==========
+    ErrorCode FILE_PATH_EXISTS = new ErrorCode(1002009001, "文件路径已经存在");
 }

+ 29 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/common/SysFileService.java

@@ -0,0 +1,29 @@
+package cn.iocoder.dashboard.modules.system.service.common;
+
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.common.SysFileDO;
+
+/**
+ * 文件 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface SysFileService {
+
+    /**
+     * 保存文件,并返回文件的访问路径
+     *
+     * @param path 文件路径
+     * @param content 文件内容
+     * @return 文件路径
+     */
+    String createFile(String path, byte[] content);
+
+    /**
+     * 获得文件
+     *
+     * @param path 文件路径
+     * @return 文件
+     */
+    SysFileDO getFile(String path);
+
+}

+ 47 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/common/impl/SysFileServiceImpl.java

@@ -0,0 +1,47 @@
+package cn.iocoder.dashboard.modules.system.service.common.impl;
+
+import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
+import cn.iocoder.dashboard.framework.file.config.FileProperties;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.common.SysFileMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.common.SysFileDO;
+import cn.iocoder.dashboard.modules.system.service.common.SysFileService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
+
+/**
+ * 文件 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+public class SysFileServiceImpl implements SysFileService {
+
+    @Resource
+    private SysFileMapper fileMapper;
+
+    @Resource
+    private FileProperties fileProperties;
+
+    @Override
+    public String createFile(String path, byte[] content) {
+        if (fileMapper.selectCountById(path) > 0) {
+            throw ServiceExceptionUtil.exception(FILE_PATH_EXISTS);
+        }
+        // 保存到数据库
+        SysFileDO file = new SysFileDO();
+        file.setId(path);
+        file.setContent(content);
+        fileMapper.insert(file);
+        // 拼接路径返回
+        return fileProperties.getBasePath() + path;
+    }
+
+    @Override
+    public SysFileDO getFile(String path) {
+        return fileMapper.selectById(path);
+    }
+
+}

+ 10 - 1
src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysPostService.java

@@ -2,6 +2,7 @@ package cn.iocoder.dashboard.modules.system.service.dept;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostExportReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostPageReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysPostDO;
@@ -18,7 +19,7 @@ import java.util.List;
 public interface SysPostService {
 
     /**
-     * 获得所有岗位列表
+     * 获得符合条件的岗位列表
      *
      * @param ids 岗位编号数组。如果为空,不进行筛选
      * @param statuses 状态数组。如果为空,不进行筛选
@@ -34,6 +35,14 @@ public interface SysPostService {
      */
     PageResult<SysPostDO> pagePosts(SysPostPageReqVO reqVO);
 
+    /**
+     * 获得岗位列表
+     *
+     * @param reqVO 查询条件
+     * @return 部门列表
+     */
+    List<SysPostDO> listPosts(SysPostExportReqVO reqVO);
+
     /**
      * 获得岗位信息
      *

+ 6 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/dept/impl/SysPostServiceImpl.java

@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.service.dept.impl;
 import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostExportReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostPageReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.convert.dept.SysPostConvert;
@@ -38,6 +39,11 @@ public class SysPostServiceImpl implements SysPostService {
         return SysPostConvert.INSTANCE.convertPage02(postMapper.selectList(reqVO));
     }
 
+    @Override
+    public List<SysPostDO> listPosts(SysPostExportReqVO reqVO) {
+        return postMapper.selectList(reqVO);
+    }
+
     @Override
     public SysPostDO getPost(Long id) {
         return postMapper.selectById(id);

+ 25 - 1
src/main/java/cn/iocoder/dashboard/util/servlet/ServletUtils.java

@@ -1,11 +1,13 @@
 package cn.iocoder.dashboard.util.servlet;
 
+import cn.hutool.core.io.IoUtil;
 import cn.hutool.extra.servlet.ServletUtil;
 import com.alibaba.fastjson.JSON;
 import org.springframework.http.MediaType;
 
-
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
 
 /**
  * 客户端工具类
@@ -14,10 +16,32 @@ import javax.servlet.http.HttpServletResponse;
  */
 public class ServletUtils {
 
+    /**
+     * 返回 JSON 字符串
+     *
+     * @param response 响应
+     * @param object 对象,会序列化成 JSON 字符串
+     */
     @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
     public static void writeJSON(HttpServletResponse response, Object object) {
         String content = JSON.toJSONString(object);
         ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
     }
 
+    /**
+     * 返回附件
+     *
+     * @param response 响应
+     * @param filename 文件名
+     * @param content 附件内容
+     * @throws IOException
+     */
+    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
+        // 设置 header 和 contentType
+        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
+        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+        // 输出附件
+        IoUtil.write(response.getOutputStream(), false, content);
+    }
+
 }

+ 8 - 0
src/main/resources/application.yaml

@@ -17,6 +17,12 @@ spring:
     database: 0
     # 连接超时时间
     timeout: 10s
+  # Servlet 配置
+  servlet:
+    # 文件上传相关配置项
+    multipart:
+      max-file-size: 16MB # 单个文件大小
+      max-request-size: 32MB # 设置总上传的文件大小
 
 # 芋道配置项,设置当前项目所有自定义的配置
 yudao:
@@ -38,6 +44,8 @@ yudao:
     timeout: 5m
     width: 160
     height: 60
+  file:
+    base-path: http://127.0.0.1:1024/api/file/get/
 
 # MyBatis Plus 的配置项
 mybatis-plus: