Browse Source

1. 完成用户导出的功能
2. 完成前后端的导出的封装

YunaiV 4 years ago
parent
commit
f942b34d02

+ 2 - 1
ruoyi-ui/src/main.js

@@ -16,7 +16,7 @@ import './assets/icons' // icon
 import './permission' // permission control
 import { getDicts } from "@/api/system/dict/data";
 import { getConfigKey } from "@/api/system/config";
-import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi";
+import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree, downloadExcel } from "@/utils/ruoyi";
 import Pagination from "@/components/Pagination";
 // 自定义表格工具扩展
 import RightToolbar from "@/components/RightToolbar"
@@ -33,6 +33,7 @@ Vue.prototype.addDateRange = addDateRange
 Vue.prototype.selectDictLabel = selectDictLabel
 Vue.prototype.selectDictLabels = selectDictLabels
 Vue.prototype.download = download
+Vue.prototype.downloadExcel = downloadExcel
 Vue.prototype.handleTree = handleTree
 
 Vue.prototype.msgSuccess = function (msg) {

+ 15 - 0
ruoyi-ui/src/utils/ruoyi.js

@@ -101,6 +101,21 @@ export function download(fileName) {
 	window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true;
 }
 
+// 下载 Excel 方法
+export function downloadExcel(data, fileName) {
+  // 创建 blob
+  let blob = new Blob([data], {type: 'application/vnd.ms-excel'});
+  // 创建 href 超链接,点击进行下载
+  window.URL = window.URL || window.webkitURL;
+  let href = URL.createObjectURL(blob);
+  let downA = document.createElement("a");
+  downA.href =  href;
+  downA.download = fileName;
+  downA.click();
+  // 销毁超连接
+  window.URL.revokeObjectURL(href);
+}
+
 // 字符串格式化(%s )
 export function sprintf(str) {
 	var args = arguments, flag = true, i = 1;

+ 6 - 11
ruoyi-ui/src/views/system/user/index.vue

@@ -510,7 +510,7 @@ export default {
       this.reset();
     },
     // 取消按钮(角色权限)
-    cancelMenu() {
+    cancelRole() {
       this.openRole = false;
       this.reset();
     },
@@ -647,7 +647,10 @@ export default {
     },
     /** 导出按钮操作 */
     handleExport() {
-      const queryParams = this.queryParams;
+      const queryParams = this.addDateRange(this.queryParams, [
+        this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined,
+        this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined,
+      ]);
       this.$confirm('是否确认导出所有用户数据项?', "警告", {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
@@ -655,15 +658,7 @@ export default {
         }).then(function() {
           return exportUser(queryParams);
         }).then(response => {
-          // this.download(response.msg);
-          let blob = new Blob([response], {type: 'application/vnd.ms-excel'});
-          window.URL = window.URL || window.webkitURL;
-          let href = URL.createObjectURL(blob);
-          let downA = document.createElement("a");
-          downA.href =  href;//
-          downA.download = '123321.xls';
-          downA.click();
-          window.URL.revokeObjectURL(href);
+          this.downloadExcel(response, '用户数据.xls');
         })
     },
     /** 导入按钮操作 */

+ 1 - 1
src/main/java/cn/iocoder/dashboard/framework/excel/core/convert/DictConvert.java

@@ -54,7 +54,7 @@ public class DictConvert implements Converter<Object> {
             return null;
         }
         // 生成 Excel 小表格
-        return new CellData<>(dictData.getValue());
+        return new CellData<>(dictData.getLabel());
     }
 
     private static DictTypeEnum getType(ExcelContentProperty contentProperty) {

+ 27 - 0
src/main/java/cn/iocoder/dashboard/framework/excel/core/util/ExcelUtils.java

@@ -0,0 +1,27 @@
+package cn.iocoder.dashboard.framework.excel.core.util;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.List;
+
+/**
+ * Excel 工具类
+ */
+public class ExcelUtils {
+
+    public static void write(HttpServletResponse response, String filename, String sheetName,
+                                 Class<?> head, List<?> data) throws IOException {
+        // 设置 header 和 contentType
+        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
+        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
+        // 输出 Excel
+        EasyExcel.write(response.getOutputStream(), head)
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
+                .sheet(sheetName).doWrite(data);
+    }
+
+}

+ 31 - 14
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java

@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.controller.user;
 import cn.hutool.core.collection.CollUtil;
 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.user.vo.user.*;
 import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
@@ -10,7 +11,7 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService;
 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
 import cn.iocoder.dashboard.util.collection.CollectionUtils;
-import com.alibaba.excel.EasyExcel;
+import cn.iocoder.dashboard.util.collection.MapUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;
@@ -20,7 +21,10 @@ import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
 
@@ -37,7 +41,7 @@ public class SysUserController {
     @ApiOperation("获得用户分页列表")
     @GetMapping("/page")
 //    @PreAuthorize("@ss.hasPermi('system:user:list')")
-    public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(SysUserPageReqVO reqVO) {
+    public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(@Validated SysUserPageReqVO reqVO) {
         // 获得用户分页列表
         PageResult<SysUserDO> pageResult = userService.pageUsers(reqVO);
         if (CollUtil.isEmpty(pageResult.getList())) {
@@ -45,13 +49,8 @@ public class SysUserController {
         }
 
         // 获得拼接需要的数据
-        Map<Long, SysDeptDO> deptMap;
         Collection<Long> deptIds = CollectionUtils.convertList(pageResult.getList(), SysUserDO::getDeptId);
-        if (CollUtil.isNotEmpty(deptIds)) {
-            deptMap = CollectionUtils.convertMap(deptService.listDepts(deptIds), SysDeptDO::getId);
-        } else {
-            deptMap = Collections.emptyMap();
-        }
+        Map<Long, SysDeptDO> deptMap = deptService.getDeptMap(deptIds);
         // 拼接结果返回
         List<SysUserPageItemRespVO> userList = new ArrayList<>(pageResult.getList().size());
         pageResult.getList().forEach(user -> {
@@ -121,11 +120,29 @@ public class SysUserController {
 
     @ApiOperation("导出用户")
     @GetMapping("/export")
-    public void exportUsers(HttpServletResponse response) throws IOException {
-        String fileName = "测试文件.xls";
-        response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
-        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
-        EasyExcel.write(response.getOutputStream(), SysUserExcelVO.class).sheet().doWrite(new ArrayList<>());
+//    @PreAuthorize("@ss.hasPermi('system:user:export')") , @Validated SysUserExportReqVO reqVO
+//    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
+    public void exportUsers(HttpServletResponse response, @Validated SysUserExportReqVO reqVO) throws IOException {
+        // 获得用户列表
+        List<SysUserDO> users = userService.listUsers(reqVO);
+
+        // 获得拼接需要的数据
+        Collection<Long> deptIds = CollectionUtils.convertList(users, SysUserDO::getDeptId);
+        Map<Long, SysDeptDO> deptMap = deptService.getDeptMap(deptIds);
+        // 拼接数据
+        List<SysUserExcelVO> excelUsers = new ArrayList<>(users.size());
+        users.forEach(user -> {
+            SysUserExcelVO excelVO = SysUserConvert.INSTANCE.convert02(user);
+            MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> {
+                excelVO.setDeptName(dept.getName());
+                excelVO.setDeptLeader(dept.getLeader());
+            });
+            excelUsers.add(excelVO);
+        });
+
+        // 输出
+        ExcelUtils.write(response, "用户数据.xls", "用户列表",
+                SysUserExcelVO.class, excelUsers);
     }
 
 //    @Log(title = "用户管理", businessType = BusinessType.EXPORT)

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserExcelVO.java

@@ -22,7 +22,7 @@ public class SysUserExcelVO {
     @ExcelProperty("用户名称")
     private String username;
 
-    @ExcelProperty("用户称")
+    @ExcelProperty("用户称")
     private String nickname;
 
     @ExcelProperty("用户邮箱")

+ 40 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserExportReqVO.java

@@ -0,0 +1,40 @@
+package cn.iocoder.dashboard.modules.system.controller.user.vo.user;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel(value = "用户导出 Request VO", description = "参数和 SysUserPageReqVO 是一致的")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SysUserExportReqVO {
+
+    @ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配")
+    private String username;
+
+    @ApiModelProperty(value = "手机号码", example = "yudao", notes = "模糊匹配")
+    private String mobile;
+
+    @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 SysCommonStatusEnum 枚举类")
+    private Integer status;
+
+    @ApiModelProperty(value = "开始时间", example = "2020-10-24")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date beginTime;
+
+    @ApiModelProperty(value = "结束时间", example = "2020-10-24")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date endTime;
+
+    @ApiModelProperty(value = "部门编号", example = "1024", notes = "同时筛选子部门")
+    private Long deptId;
+
+}

+ 3 - 0
src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java

@@ -2,6 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.user;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExcelVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageItemRespVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
@@ -27,4 +28,6 @@ public interface SysUserConvert {
 
     SysUserDO convert(SysUserUpdateReqVO bean);
 
+    SysUserExcelVO convert02(SysUserDO bean);
+
 }

+ 11 - 1
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/user/SysUserMapper.java

@@ -2,6 +2,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dao.user;
 
 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.user.vo.user.SysUserExportReqVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -9,6 +10,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Collection;
 import java.util.List;
 
 @Mapper
@@ -26,7 +28,7 @@ public interface SysUserMapper extends BaseMapper<SysUserDO> {
         return selectOne(new QueryWrapper<SysUserDO>().eq("email", email));
     }
 
-    default IPage<SysUserDO> selectList(SysUserPageReqVO reqVO, List<Long> deptIds) {
+    default IPage<SysUserDO> selectList(SysUserPageReqVO reqVO, Collection<Long> deptIds) {
         return selectPage(MyBatisUtils.buildPage(reqVO),
                 new QueryWrapperX<SysUserDO>().likeIfPresent("username", reqVO.getUsername())
                         .likeIfPresent("mobile", reqVO.getMobile())
@@ -35,5 +37,13 @@ public interface SysUserMapper extends BaseMapper<SysUserDO> {
                         .inIfPresent("dept_id", deptIds));
     }
 
+    default List<SysUserDO> selectList(SysUserExportReqVO reqVO, Collection<Long> deptIds) {
+        return selectList(new QueryWrapperX<SysUserDO>().likeIfPresent("username", reqVO.getUsername())
+                .likeIfPresent("mobile", reqVO.getMobile())
+                .eqIfPresent("status", reqVO.getStatus())
+                .betweenIfPresent("create_time", reqVO.getBeginTime(), reqVO.getEndTime())
+                .inIfPresent("dept_id", deptIds));
+    }
+
 }
 

+ 18 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysDeptService.java

@@ -1,12 +1,16 @@
 package cn.iocoder.dashboard.modules.system.service.dept;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.dept.SysDeptCreateReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.dept.SysDeptListReqVO;
 import cn.iocoder.dashboard.modules.system.controller.dept.vo.dept.SysDeptUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 部门 Service 接口
@@ -35,6 +39,20 @@ public interface SysDeptService {
      */
     List<SysDeptDO> listDepts(Collection<Long> ids);
 
+    /**
+     * 获得指定编号的部门 Map
+     *
+     * @param ids 部门编号数组
+     * @return 部门 Map
+     */
+    default Map<Long, SysDeptDO> getDeptMap(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyMap();
+        }
+        List<SysDeptDO> list = listDepts(ids);
+        return CollectionUtils.convertMap(list, SysDeptDO::getId);
+    }
+
     /**
      * 筛选部门列表
      *

+ 2 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java

@@ -15,6 +15,7 @@ import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService;
 import com.google.common.collect.ImmutableTable;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -56,6 +57,7 @@ public class SysDictDataServiceImpl implements SysDictDataService {
     private SysDictDataMapper dictDataMapper;
 
     @Override
+    @PostConstruct
     public void init() {
         // 获得字典数据
         List<SysDictDataDO> list = this.listDictDatas();

+ 12 - 94
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java

@@ -2,23 +2,19 @@ package cn.iocoder.dashboard.modules.system.service.user;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExportReqVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 
+import java.util.List;
+
 /**
  * 用户 Service 接口
  *
  * @author 芋道源码
  */
 public interface SysUserService {
-//    /**
-//     * 根据条件分页查询用户列表
-//     *
-//     * @param user 用户信息
-//     * @return 用户信息集合信息
-//     */
-//    public List<SysUser> selectUserList(SysUser user);
 
     /**
      * 通过用户名查询用户
@@ -44,6 +40,14 @@ public interface SysUserService {
      */
     PageResult<SysUserDO> pageUsers(SysUserPageReqVO reqVO);
 
+    /**
+     * 获得用户列表
+     *
+     * @param reqVO 列表请求
+     * @return 用户列表
+     */
+    List<SysUserDO> listUsers(SysUserExportReqVO reqVO);
+
     /**
      * 创建用户
      *
@@ -82,77 +86,6 @@ public interface SysUserService {
      */
     void updateUserStatus(Long id, Integer status);
 
-//
-//    /**
-//     * 根据用户ID查询用户所属角色组
-//     *
-//     * @param userName 用户名
-//     * @return 结果
-//     */
-//    public String selectUserRoleGroup(String userName);
-//
-//    /**
-//     * 根据用户ID查询用户所属岗位组
-//     *
-//     * @param userName 用户名
-//     * @return 结果
-//     */
-//    public String selectUserPostGroup(String userName);
-//
-//    /**
-//     * 校验用户名称是否唯一
-//     *
-//     * @param userName 用户名称
-//     * @return 结果
-//     */
-//    public String checkUserNameUnique(String userName);
-//
-//    /**
-//     * 校验手机号码是否唯一
-//     *
-//     * @param user 用户信息
-//     * @return 结果
-//     */
-//    public String checkPhoneUnique(SysUser user);
-//
-//    /**
-//     * 校验email是否唯一
-//     *
-//     * @param user 用户信息
-//     * @return 结果
-//     */
-//    public String checkEmailUnique(SysUser user);
-//
-//    /**
-//     * 校验用户是否允许操作
-//     *
-//     * @param user 用户信息
-//     */
-//    public void checkUserAllowed(SysUser user);
-//
-//    /**
-//     * 新增用户信息
-//     *
-//     * @param user 用户信息
-//     * @return 结果
-//     */
-//    public int insertUser(SysUser user);
-//
-//    /**
-//     * 修改用户信息
-//     *
-//     * @param user 用户信息
-//     * @return 结果
-//     */
-//    public int updateUser(SysUser user);
-//
-//    /**
-//     * 修改用户状态
-//     *
-//     * @param user 用户信息
-//     * @return 结果
-//     */
-//    public int updateUserStatus(SysUser user);
 //
 //    /**
 //     * 修改用户基本信息
@@ -187,22 +120,7 @@ public interface SysUserService {
 //     * @return 结果
 //     */
 //    public int resetUserPwd(String userName, String password);
-//
-//    /**
-//     * 通过用户ID删除用户
-//     *
-//     * @param userId 用户ID
-//     * @return 结果
-//     */
-//    public int deleteUserById(Long userId);
-//
-//    /**
-//     * 批量删除用户信息
-//     *
-//     * @param userIds 需要删除的用户ID
-//     * @return 结果
-//     */
-//    public int deleteUserByIds(Long[] userIds);
+
 //
 //    /**
 //     * 导入用户数据

+ 23 - 8
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java

@@ -6,6 +6,7 @@ import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
 import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExportReqVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert;
@@ -77,15 +78,29 @@ public class SysUserServiceImpl implements SysUserService {
 
     @Override
     public PageResult<SysUserDO> pageUsers(SysUserPageReqVO reqVO) {
-        // 处理部门查询条件
-        List<Long> deptIds = Collections.emptyList();
-        if (reqVO.getDeptId() != null) {
-            deptIds = CollectionUtils.convertList(deptService.listDeptsByParentIdFromCache(
-                    reqVO.getDeptId(), true), SysDeptDO::getId);
-            deptIds.add(reqVO.getDeptId());
+        return SysUserConvert.INSTANCE.convertPage(userMapper.selectList(reqVO,
+                this.getDeptCondition(reqVO.getDeptId())));
+    }
+
+    @Override
+    public List<SysUserDO> listUsers(SysUserExportReqVO reqVO) {
+        return userMapper.selectList(reqVO, this.getDeptCondition(reqVO.getDeptId()));
+    }
+
+    /**
+     * 获得部门条件:查询指定部门的子部门编号们,包括自身
+     *
+     * @param deptId 部门编号
+     * @return 部门编号集合
+     */
+    private Set<Long> getDeptCondition(Long deptId) {
+        if (deptId == null) {
+            return Collections.emptySet();
         }
-        // 执行查询
-        return SysUserConvert.INSTANCE.convertPage(userMapper.selectList(reqVO, deptIds));
+        Set<Long> deptIds = CollectionUtils.convertSet(deptService.listDeptsByParentIdFromCache(
+                deptId, true), SysDeptDO::getId);
+        deptIds.add(deptId); // 包括自身
+        return deptIds;
     }
 
     @Override

+ 25 - 0
src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java

@@ -1,5 +1,6 @@
 package cn.iocoder.dashboard.util.collection;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 
 import java.util.*;
@@ -19,37 +20,61 @@ public class CollectionUtils {
     }
 
     public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
+        if (CollUtil.isEmpty(from)) {
+            return new ArrayList<>();
+        }
         return from.stream().filter(predicate).collect(Collectors.toList());
     }
 
     public static <T, U> List<U> convertList(List<T> from, Function<T, U> func) {
+        if (CollUtil.isEmpty(from)) {
+            return new ArrayList<>();
+        }
         return from.stream().map(func).collect(Collectors.toList());
     }
 
     public static <T, U> Set<U> convertSet(List<T> from, Function<T, U> func) {
+        if (CollUtil.isEmpty(from)) {
+            return new HashSet<>();
+        }
         return from.stream().map(func).collect(Collectors.toSet());
     }
 
     public static <T, K> Map<K, T> convertMap(List<T> from, Function<T, K> keyFunc) {
+        if (CollUtil.isEmpty(from)) {
+            return new HashMap<>();
+        }
         return from.stream().collect(Collectors.toMap(keyFunc, item -> item));
     }
 
     public static <T, K, V> Map<K, V> convertMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
+        if (CollUtil.isEmpty(from)) {
+            return new HashMap<>();
+        }
         return from.stream().collect(Collectors.toMap(keyFunc, valueFunc));
     }
 
     public static <T, K> Map<K, List<T>> convertMultiMap(List<T> from, Function<T, K> keyFunc) {
+        if (CollUtil.isEmpty(from)) {
+            return new HashMap<>();
+        }
         return from.stream().collect(Collectors.groupingBy(keyFunc,
                 Collectors.mapping(t -> t, Collectors.toList())));
     }
 
     public static <T, K, V> Map<K, List<V>> convertMultiMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
+        if (CollUtil.isEmpty(from)) {
+            return new HashMap<>();
+        }
         return from.stream().collect(Collectors.groupingBy(keyFunc,
                 Collectors.mapping(valueFunc, Collectors.toList())));
     }
 
     // 暂时没想好名字,先以 2 结尾噶
     public static <T, K, V> Map<K, Set<V>> convertMultiMap2(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
+        if (CollUtil.isEmpty(from)) {
+            return new HashMap<>();
+        }
         return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
     }
 

+ 29 - 0
src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java

@@ -1,11 +1,14 @@
 package cn.iocoder.dashboard.util.collection;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import com.google.common.collect.Multimap;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
 
 /**
  * Map 工具类
@@ -14,6 +17,13 @@ import java.util.List;
  */
 public class MapUtils {
 
+    /**
+     * 从哈希表表中,获得 keys 对应的所有 value 数组
+     *
+     * @param multimap 哈希表
+     * @param keys keys
+     * @return value 数组
+     */
     public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {
         List<V> result = new ArrayList<>();
         keys.forEach(k -> {
@@ -26,4 +36,23 @@ public class MapUtils {
         return result;
     }
 
+    /**
+     * 从哈希表查找到 key 对应的 value,然后进一步处理
+     * 注意,如果查找到的 value 为 null 时,不进行处理
+     *
+     * @param map 哈希表
+     * @param key key
+     * @param consumer 进一步处理的逻辑
+     */
+    public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
+        if (CollUtil.isEmpty(map)) {
+            return;
+        }
+        V value = map.get(key);
+        if (value == null) {
+            return;
+        }
+        consumer.accept(value);
+    }
+
 }