Преглед на файлове

feat: 1.导出异步机制实现;2.人员导出逻辑编写

lixuesong преди 1 седмица
родител
ревизия
4909188055
променени са 12 файла, в които са добавени 722 реда и са изтрити 0 реда
  1. 5 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/constant/CacheConstants.java
  2. 27 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/constant/enums/ExportType.java
  3. 17 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/dto/SysUserDTO.java
  4. 60 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/model/excel/SysUserExcelModel.java
  5. 2 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/user/SysUserController.java
  6. 42 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/user/SysUserExportController.java
  7. 34 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/event/UserExportEvent.java
  8. 36 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/listener/UserExportEventListener.java
  9. 12 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/SysFileService.java
  10. 44 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/SysUserExportService.java
  11. 59 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/SysFileServiceImpl.java
  12. 384 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/SysUserExportServiceImpl.java

+ 5 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/constant/CacheConstants.java

@@ -99,6 +99,11 @@ public interface CacheConstants {
 
     String NEW_EXCEL_COMMON_REPORT_CACHE = "new_excel_export:common_report:%s";
 
+    /**
+     * 异步导出缓存key
+     */
+    String ASYNC_EXPORT_CACHE = "user_export:%s:%s";
+
 
     Long DEF_REPORT_CREATING_TTL = 24L * 60 * 60 * 1000;
 

+ 27 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/constant/enums/ExportType.java

@@ -0,0 +1,27 @@
+package com.qunzhixinxi.hnqz.admin.api.constant.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 导出类型
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+@Getter
+@AllArgsConstructor
+public enum ExportType {
+
+	USER("USER", "人员信息");
+
+	/**
+	 * 类型
+	 */
+	private final String type;
+
+	/**
+	 * 描述
+	 */
+	private final String description;
+}

+ 17 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/dto/SysUserDTO.java

@@ -121,4 +121,21 @@ public final class SysUserDTO {
         private Integer current;
 
     }
+
+    @Data
+    public static class OnList {
+
+        private String username;
+
+        private String realname;
+
+        private List<Integer> role;
+
+        private Integer deptId;
+
+        private List<Long> areaCodes;
+
+        private String lockFlag;
+
+    }
 }

+ 60 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/model/excel/SysUserExcelModel.java

@@ -0,0 +1,60 @@
+package com.qunzhixinxi.hnqz.admin.api.model.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 用户 Excel 模型
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+@Data
+public class SysUserExcelModel implements Serializable {
+	private static final long serialVersionUID = 6204125117898666322L;
+
+	/**
+	 * 人员名称
+	 */
+	@ExcelProperty(value = "人员名称", index = 1)
+	private String realName;
+
+	/**
+	 * 手机号
+	 */
+	@ExcelProperty(value = "手机号", index = 2)
+	private String phoneNumber;
+
+	/**
+	 * 人员角色
+	 */
+	@ExcelProperty(value = "人员角色", index = 3)
+	private String roleList;
+
+	/**
+	 * 派工方
+	 */
+	@ExcelProperty(value = "派工方", index = 4)
+	private String deptName;
+
+	/**
+	 * 状态
+	 */
+	@ExcelProperty(value = "状态", index = 5)
+	private String lockFlag;
+
+	/**
+	 * 创建时间
+	 */
+	@ExcelProperty(value = "创建时间", index = 6)
+	private String createTime;
+
+	/**
+	 * 证书状态
+	 */
+	@ExcelProperty(value = "是/否备案", index = 7)
+	private String certificateFlag;
+
+}

+ 2 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/user/SysUserController.java

@@ -36,6 +36,7 @@ import com.qunzhixinxi.hnqz.admin.service.SysRoleService;
 import com.qunzhixinxi.hnqz.admin.service.SysUserAreaService;
 import com.qunzhixinxi.hnqz.admin.service.SysUserService;
 import com.qunzhixinxi.hnqz.admin.service.WmPlatformQuizTestResultService;
+import com.qunzhixinxi.hnqz.admin.service.impl.SysUserExportServiceImpl;
 import com.qunzhixinxi.hnqz.common.core.constant.CommonConstants;
 import com.qunzhixinxi.hnqz.common.core.entity.BaseEntity;
 import com.qunzhixinxi.hnqz.common.core.exception.BizException;
@@ -1095,6 +1096,7 @@ public class SysUserController {
      *
      * @param query 查询参数列表
      * @return 用户集合
+     * @see SysUserExportServiceImpl#listUsers(HnqzUser, List, SysUserDTO.OnList) 查询条件如有修改,需要同步代码逻辑
      */
     @PostMapping(value = "/selectUserList")
     public R<?> pageUsers(@Validated @RequestBody SysUserDTO.OnPage query) {

+ 42 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/user/SysUserExportController.java

@@ -0,0 +1,42 @@
+package com.qunzhixinxi.hnqz.admin.controller.user;
+
+import com.qunzhixinxi.hnqz.admin.api.dto.SysUserDTO;
+import com.qunzhixinxi.hnqz.admin.api.model.excel.SysUserExcelModel;
+import com.qunzhixinxi.hnqz.admin.service.SysUserExportService;
+import com.qunzhixinxi.hnqz.common.core.util.R;
+import com.qunzhixinxi.hnqz.common.log.annotation.SysLog;
+import com.qunzhixinxi.hnqz.common.security.util.SecurityUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 用户导出控制器
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping(value = "/user/export")
+public class SysUserExportController {
+
+	private final SysUserExportService userExportService;
+
+	/**
+	 * 导出用户列表
+	 *
+	 * @return {@link List }<{@link SysUserExcelModel }> 用户信息
+	 */
+	@SysLog("导出用户信息")
+	@PostMapping("/export-user-info")
+	public R<Boolean> exportUser(@RequestBody SysUserDTO.OnList query) {
+		return R.ok(userExportService.asyncExport(SecurityUtils.getUser(), SecurityUtils.getRoles(), query));
+	}
+}

+ 34 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/event/UserExportEvent.java

@@ -0,0 +1,34 @@
+package com.qunzhixinxi.hnqz.admin.event;
+
+import com.qunzhixinxi.hnqz.admin.api.dto.SysUserDTO;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.List;
+
+/**
+ * 用户导出事件
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+@Getter
+@AllArgsConstructor
+public class UserExportEvent {
+
+	/**
+	 * 用户
+	 */
+	private HnqzUser user;
+
+	/**
+	 * 角色
+	 */
+	private List<Integer> roles;
+
+	/**
+	 * 查询参数
+	 */
+	private SysUserDTO.OnList query;
+}

+ 36 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/listener/UserExportEventListener.java

@@ -0,0 +1,36 @@
+package com.qunzhixinxi.hnqz.admin.listener;
+
+import com.qunzhixinxi.hnqz.admin.event.UserExportEvent;
+import com.qunzhixinxi.hnqz.admin.service.SysUserExportService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * 用户导出事件监听器
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Configuration
+public class UserExportEventListener {
+
+	private final SysUserExportService userExportService;
+
+	/**
+	 * 推送事件
+	 *
+	 * @param event 事件
+	 */
+	@Async
+	@Order
+	@EventListener(UserExportEvent.class)
+	public void pushEvent(UserExportEvent event) {
+		userExportService.export(event.getUser(), event.getRoles(), event.getQuery());
+	}
+}

+ 12 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/SysFileService.java

@@ -7,6 +7,7 @@ import com.qunzhixinxi.hnqz.common.core.util.R;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.util.Map;
@@ -36,6 +37,17 @@ public interface SysFileService extends IService<SysFile> {
      */
     Map<String, String> upload(MultipartFile file, String username);
 
+    /**
+     * 上传
+     *
+     * @param inputStream      输入流
+     * @param originalFileName 原始文件名
+     * @param fileName         文件名
+     * @param username         用户名
+     * @return {@link Map }<{@link String }, {@link String }> 结果
+     */
+    Map<String, String> upload(InputStream inputStream, String originalFileName, String fileName, String username);
+
     /**
      * 上传文件
      *

+ 44 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/SysUserExportService.java

@@ -0,0 +1,44 @@
+package com.qunzhixinxi.hnqz.admin.service;
+
+import com.qunzhixinxi.hnqz.admin.api.dto.SysUserDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmReportOpt;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+
+import java.util.List;
+
+/**
+ * 用户导出服务
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+public interface SysUserExportService {
+
+	/**
+	 * 异步导出
+	 *
+	 * @param user  用户
+	 * @param roles 角色
+	 * @param query 查询
+	 * @return {@link Boolean } 是否成功
+	 */
+	Boolean asyncExport(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query);
+
+	/**
+	 * 导出用户
+	 *
+	 * @param user  用户
+	 * @param roles 角色
+	 * @param query 查询
+	 * @return {@link Boolean } 是否成功
+	 */
+	Boolean export(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query);
+
+	/**
+	 * 导出用户信息的结果
+	 *
+	 * @param user 用户
+	 * @return {@link WmReportOpt } 状态和结果
+	 */
+	WmReportOpt exportResult(HnqzUser user);
+}

+ 59 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/SysFileServiceImpl.java

@@ -124,6 +124,41 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile>
     return resultMap;
   }
 
+  /**
+   * 上传
+   *
+   * @param inputStream 输入流
+   * @param originalFileName 原始文件名
+   * @param fileName    文件名
+   * @param username    用户名
+   * @return {@link Map }<{@link String }, {@link String }> 结果
+   */
+  @Override
+  public Map<String, String> upload(InputStream inputStream, String originalFileName, String fileName, String username) {
+
+    // 判断是否有重名的操作
+    Map<String, String> resultMap = new HashMap<>(5);
+    resultMap.put("bucketName", ossProperties.getBucketName());
+    resultMap.put("orgFileName", fileName);
+    resultMap.put("fileName", fileName);
+
+    String url = String.format("/admin/sys-file/%s/%s", ossProperties.getBucketName(), fileName);
+    resultMap.put("url", url);
+
+    try {
+      minioTemplate.putObject(ossProperties.getBucketName(), fileName, inputStream);
+      // 文件管理数据记录,收集管理追踪文件(注意文件大小不保证准确性)
+      SysFile sysFile = this.fileLog(fileName, fileName, inputStream.available(), url, username);
+      resultMap.put("fileId", String.valueOf(sysFile.getId()));
+
+    } catch (Exception e) {
+      log.error("上传失败", e);
+      return null;
+    }
+
+    return resultMap;
+  }
+
   private MultipartFile heic2jpg(InputStream inputStream, String fileName) throws IOException {
 
     String tempFileName =
@@ -293,6 +328,30 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile>
     return sysFile;
   }
 
+  /**
+   * 文件管理数据记录,收集管理追踪文件
+   *
+   * @param originalFileName 原始文件名
+   * @param fileName         文件名
+   * @param size             大小
+   * @param url              文件url
+   * @param username         用户名
+   * @return {@link SysFile }
+   */
+  private SysFile fileLog(String originalFileName, String fileName, Integer size, String url, String username) {
+    SysFile sysFile = new SysFile();
+    // 原文件名
+    sysFile.setFileName(fileName);
+    sysFile.setOriginal(originalFileName);
+    sysFile.setFileSize(Long.valueOf(size));
+    sysFile.setType(FileUtil.extName(originalFileName));
+    sysFile.setBucketName(ossProperties.getBucketName());
+    sysFile.setPath(url);
+    sysFile.setCreateUser(username);
+    this.save(sysFile);
+    return sysFile;
+  }
+
   @Override
   public String uploadImgByByte(InputStream inputStream) {
     String fileName = IdUtil.simpleUUID() + StrUtil.DOT + ".jpg";

+ 384 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/SysUserExportServiceImpl.java

@@ -0,0 +1,384 @@
+package com.qunzhixinxi.hnqz.admin.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.qunzhixinxi.hnqz.admin.api.constant.CacheConstants;
+import com.qunzhixinxi.hnqz.admin.api.constant.UpmsType;
+import com.qunzhixinxi.hnqz.admin.api.constant.enums.ExportType;
+import com.qunzhixinxi.hnqz.admin.api.dto.SysUserDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysAreaEntity;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysDept;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysRole;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysUser;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysUserCertificate;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysUserRole;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmReportOpt;
+import com.qunzhixinxi.hnqz.admin.api.model.excel.SysUserExcelModel;
+import com.qunzhixinxi.hnqz.admin.controller.user.SysUserController;
+import com.qunzhixinxi.hnqz.admin.event.UserExportEvent;
+import com.qunzhixinxi.hnqz.admin.mapper.SysAreaEntityMapper;
+import com.qunzhixinxi.hnqz.admin.mapper.SysDeptMapper;
+import com.qunzhixinxi.hnqz.admin.mapper.SysRoleMapper;
+import com.qunzhixinxi.hnqz.admin.mapper.SysUserCertificateMapper;
+import com.qunzhixinxi.hnqz.admin.mapper.SysUserMapper;
+import com.qunzhixinxi.hnqz.admin.mapper.SysUserRoleMapper;
+import com.qunzhixinxi.hnqz.admin.service.SysFileService;
+import com.qunzhixinxi.hnqz.admin.service.SysUserAreaService;
+import com.qunzhixinxi.hnqz.admin.service.SysUserExportService;
+import com.qunzhixinxi.hnqz.admin.util.OsEnvUtils;
+import com.qunzhixinxi.hnqz.common.core.util.SpringContextHolder;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.io.FileInputStream;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 用户导出服务 impl
+ *
+ * @author snows
+ * @date 2025/07/30
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SysUserExportServiceImpl implements SysUserExportService {
+	private final RedisTemplate<String, Object> redisTemplate;
+	private final SysAreaEntityMapper areaEntityMapper;
+	private final SysUserMapper userMapper;
+	private final SysRoleMapper roleMapper;
+	private final SysUserRoleMapper userRoleMapper;
+	private final SysUserAreaService userAreaService;
+	private final SysDeptMapper deptMapper;
+	private final SysUserCertificateMapper userCertificateMapper;
+	private final SysFileService fileService;
+
+	private static final long DEF_REPORT_TTL = 7L * 24 * 60 * 60 * 1000;
+	private static final String ERROR_MSG_UNKNOWN = "ERROR_未知错误,请联系管理员";
+	private static final String ERROR_MSG_NO_DATA = "ERROR_没有数据";
+	private static final String ERROR_MSG_UPLOAD_FAIL = "ERROR_上传OSS失败";
+
+
+	/**
+	 * 异步导出
+	 *
+	 * @param user  用户
+	 * @param roles 角色
+	 * @param query 查询
+	 * @return {@link Boolean } 是否成功
+	 */
+	@Override
+	public Boolean asyncExport(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query) {
+		SpringContextHolder.getApplicationContext().publishEvent(new UserExportEvent(user, roles, query));
+		return Boolean.TRUE;
+	}
+
+	/**
+	 * 导出用户
+	 *
+	 * @param user  用户
+	 * @param roles 角色
+	 * @param query 查询
+	 * @return {@link WmReportOpt } 状态和结果
+	 */
+	@Override
+	public Boolean export(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query) {
+		String key = String.format(CacheConstants.ASYNC_EXPORT_CACHE, ExportType.USER.getType(), user.getId());
+
+		// 查询用户列表
+		List<SysUser> users = this.listUsers(user, roles, query);
+
+		if (CollUtil.isNotEmpty(users)) {
+			redisTemplate.opsForValue().set(key, ERROR_MSG_NO_DATA, DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
+			return Boolean.FALSE;
+		}
+
+		String resultValue;
+		try {
+			// 查询企业信息
+			Set<Integer> deptIds = users.stream().map(SysUser::getDeptId).collect(Collectors.toSet());
+			Map<Integer, SysDept> deptMap = new HashMap<>();
+			if (CollUtil.isNotEmpty(deptIds)) {
+				List<SysDept> sysDepts = deptMapper.selectBatchIds(deptIds);
+				deptMap.putAll(sysDepts.stream().collect(Collectors.toMap(SysDept::getDeptId, Function.identity())));
+			}
+			// 查询角色
+			Set<Integer> userIds = users.stream().map(SysUser::getUserId).collect(Collectors.toSet());
+			List<SysUserRole> userRoles = userRoleMapper.selectList(Wrappers.<SysUserRole>lambdaQuery().in(SysUserRole::getUserId, userIds));
+
+			Map<Integer, List<SysUserRole>> userRolesMap = userRoles.stream().collect(Collectors.groupingBy(SysUserRole::getUserId));
+
+			Map<Integer, String> roleMap = new HashMap<>();
+			if (CollUtil.isNotEmpty(userRoles)) {
+				Set<Integer> roleIds = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
+				List<SysRole> resultRoles = roleMapper.selectBatchIds(roleIds);
+				if (CollUtil.isNotEmpty(resultRoles)) {
+					roleMap.putAll(resultRoles.stream().collect(Collectors.toMap(SysRole::getRoleId, SysRole::getRoleName)));
+				}
+			}
+
+			// 查询备案信息
+			List<SysUserCertificate> userCertificates =
+					userCertificateMapper.selectList(Wrappers.<SysUserCertificate>lambdaQuery()
+							.in(SysUserCertificate::getUserId, userIds)
+							.eq(SysUserCertificate::getType, "REG")
+							.select(SysUserCertificate::getCertificateId));
+
+			Map<Integer, Long> userCertMap = userCertificates.stream()
+					.collect(Collectors.groupingBy(SysUserCertificate::getUserId, Collectors.counting()));
+
+			// 转为excel列表
+			List<SysUserExcelModel> excelModels = users.stream().map(u -> {
+				SysUserExcelModel excelModel = BeanUtil.copyProperties(u, SysUserExcelModel.class, "lockFlag");
+				// 派工方
+				SysDept dept = deptMap.get(u.getDeptId());
+				if (dept != null) {
+					excelModel.setDeptName(dept.getName());
+				}
+
+				// 角色
+				List<SysUserRole> userRoleList = userRolesMap.get(u.getUserId());
+				if (CollUtil.isNotEmpty(userRoleList)) {
+					String roleNames = userRoleList.stream()
+							.map(userRole -> roleMap.getOrDefault(userRole.getRoleId(), ""))
+							.collect(Collectors.joining(","));
+					excelModel.setRoleList(roleNames);
+				}
+
+				// 备案状态
+				long certCount = userCertMap.getOrDefault(u.getUserId(), 0L);
+				excelModel.setCertificateFlag(certCount > 0 ? "是" : "否");
+
+				// 锁定状态
+				excelModel.setLockFlag("0".equals(u.getLockFlag()) ? "活跃" : "休眠");
+
+
+				return excelModel;
+			}).collect(Collectors.toList());
+
+			// 写入excel文件
+			String tempPath = OsEnvUtils.getEachEnvPaths().get(OsEnvUtils.TargetFile.TEMP.getName());
+			String fileName = tempPath + "人员_" + DateTimeFormatter.ofPattern(DatePattern.PURE_TIME_PATTERN)
+					.format(LocalDateTime.now()) + RandomStringUtils.randomNumeric(6) + ".xlsx";
+			EasyExcel.write(fileName, SysUserExcelModel.class).sheet("人员")
+					.doWrite(excelModels);
+			log.info("人员导出生成缓存文件:{}", fileName);
+
+			FileInputStream inputStream = new FileInputStream(fileName);
+			// 上传oss
+			Map<String, String> uploadResult = fileService.upload(inputStream, fileName, fileName, user.getUsername());
+			inputStream.close();
+			log.info("人员导出生成oss文件:{}", uploadResult);
+
+			if (CollUtil.isNotEmpty(uploadResult)) {
+				resultValue = uploadResult.get("url");
+			} else {
+				resultValue = ERROR_MSG_UPLOAD_FAIL;
+			}
+		} catch (Exception e) {
+			log.error("人员导出写入文件失败", e);
+			resultValue = ERROR_MSG_UNKNOWN;
+		}
+
+		redisTemplate.opsForValue().set(key, resultValue, DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
+
+		return Boolean.FALSE;
+	}
+
+	/**
+	 * 列出用户
+	 *
+	 * @param user  用户
+	 * @param roles 角色
+	 * @param query 查询
+	 * @return {@link List<SysUser> } 用户列表
+	 * @see SysUserController#pageUsers(SysUserDTO.OnPage) 如有修改,需要同步代码逻辑
+	 */
+	private List<SysUser> listUsers(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query) {
+		//  获取全部的可用角色
+		List<SysRole> queryRoles = roleMapper.selectList(Wrappers.emptyWrapper());
+		Map<Integer, SysRole> roleId2RoleMap = queryRoles
+				.stream().collect(Collectors.toMap(SysRole::getRoleId, Function.identity()));
+
+		// 获取操作人的角色
+		Set<Integer> operatorRoleIds = new HashSet<>(roles);
+
+		Set<Integer> targetRoleIds = new HashSet<>();
+
+
+		SysRole role = null;
+		boolean needArea = false;
+
+		// 系统管理员
+		if (operatorRoleIds.contains(1)) {
+			role = roleId2RoleMap.get(1);
+		}
+		// 中生平台管理员
+		else if (operatorRoleIds.contains(2)) {
+			role = roleId2RoleMap.get(2);
+		}
+		// 事业部系统管理员
+		else if (operatorRoleIds.contains(50)) {
+			role = roleId2RoleMap.get(50);
+		}
+		// 运营管理员
+		else if (operatorRoleIds.contains(3)) {
+			role = roleId2RoleMap.get(3);
+		}
+		// 区域管理员
+		else if (operatorRoleIds.contains(4)) {
+			role = roleId2RoleMap.get(4);
+			query.setDeptId(user.getDeptId());
+			needArea = true;
+		}
+		// 服务商管理员
+		else if (operatorRoleIds.contains(37)) {
+			role = roleId2RoleMap.get(37);
+			query.setDeptId(user.getDeptId());
+			needArea = true;
+		}
+		// 备案管理员
+		else if (operatorRoleIds.contains(47)) {
+			role = roleId2RoleMap.get(47);
+		}
+
+
+		if (role == null) {
+			log.error("当前用户角色禁止查看其他角色");
+			return Collections.emptyList();
+		} else {
+			String visible = role.getVisible();
+			if (StrUtil.isBlank(visible)) {
+				log.error("当前角色未配置查询角色");
+				return Collections.emptyList();
+			}
+
+			Set<Integer> visibles = Arrays.stream(visible.split(StrUtil.COMMA)).mapToInt(Integer::valueOf).boxed().collect(Collectors.toSet());
+
+			Integer targetRoleId = CollUtil.isEmpty(query.getRole()) ? null : query.getRole().get(0);
+
+			if (targetRoleId == null) {
+				targetRoleIds.addAll(visibles);
+			} else if (visibles.contains(targetRoleId)) {
+				targetRoleIds.add(targetRoleId);
+			} else {
+				log.error("当前角色禁止查看其他角色");
+				return Collections.emptyList();
+			}
+		}
+
+		// 获取所有三级区域
+		String areaCacheKey = "sys:area:lv3";
+		Set<Long> areaEntities;
+		if (Boolean.TRUE.equals(redisTemplate.hasKey(areaCacheKey))) {
+			areaEntities = (Set<Long>) redisTemplate.opsForValue().get(areaCacheKey);
+		} else {
+			areaEntities = areaEntityMapper.selectList(Wrappers.<SysAreaEntity>lambdaQuery().eq(SysAreaEntity::getAreaType, UpmsType.AreaType.DISTRICT))
+					.stream().map(SysAreaEntity::getAreaId).collect(Collectors.toSet());
+			redisTemplate.opsForValue().set(areaCacheKey, areaEntities, 12, TimeUnit.HOURS);
+		}
+
+		List<Long> areas = query.getAreaCodes();
+
+		if (CollUtil.isEmpty(areas) && needArea) {
+			areas = userAreaService.listUserAreas(Long.valueOf(user.getId()));
+		}
+
+		// 如果实际三级区域于查询三级区域相等,也就是全国的时候,默认直接查询全国
+		query.setAreaCodes((areaEntities.size() == (CollUtil.isNotEmpty(areas) ? areas.size() : 0)) ? Collections.emptyList() : new LinkedList<>(CollUtil.intersectionDistinct(areaEntities, areas)));
+		query.setRole(new LinkedList<>(targetRoleIds));
+
+		Set<Integer> areaUserIds = null;
+		if (CollUtil.isNotEmpty(query.getAreaCodes())) {
+			areaUserIds = userAreaService.listAreaUser(query.getAreaCodes()).stream().map(Long::intValue).collect(Collectors.toSet());
+		}
+
+		List<SysUserRole> userRoles = userRoleMapper.selectList(Wrappers.<SysUserRole>lambdaQuery()
+				.in(CollUtil.isNotEmpty(areaUserIds), SysUserRole::getUserId, areaUserIds)
+				.in(SysUserRole::getRoleId, query.getRole()));
+
+
+		Map<Integer, List<Integer>> collect = userRoles.stream()
+				.collect(Collectors.groupingBy(SysUserRole::getUserId, Collectors.mapping(SysUserRole::getRoleId, Collectors.toList())));
+
+
+		if (CollUtil.isEmpty(collect)) {
+			return Collections.emptyList();
+		}
+
+		LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.<SysUser>lambdaQuery()
+				.like(StrUtil.isNotBlank(query.getUsername()), SysUser::getUsername, query.getUsername())
+				.like(StrUtil.isNotBlank(query.getRealname()), SysUser::getRealname, query.getRealname())
+				.eq(StrUtil.isNotBlank(query.getLockFlag()), SysUser::getLockFlag, query.getLockFlag())
+				.eq(query.getDeptId() != null, SysUser::getDeptId, query.getDeptId())
+				.in(SysUser::getUserId, collect.keySet())
+				.orderByDesc(SysUser::getCreateTime);
+
+		return userMapper.selectList(queryWrapper);
+	}
+
+	/**
+	 * 导出用户信息的结果
+	 *
+	 * @param user 用户
+	 * @return {@link WmReportOpt } 状态和结果
+	 */
+	@Override
+	public WmReportOpt exportResult(HnqzUser user) {
+		String key = String.format(CacheConstants.ASYNC_EXPORT_CACHE, ExportType.USER.getType(), user.getId());
+
+		String o = (String) redisTemplate.opsForValue().get(key);
+		WmReportOpt opt = new WmReportOpt();
+
+		if (StringUtils.isNotEmpty(o)) {
+			// 生成中的
+			if ("GENERATING".equals(o)) {
+				opt.setStatus(WmReportOpt.WmReportOptStatus.GENERATING);
+			}
+			// 生成失败的
+			else if (o.startsWith("ERROR")) {
+				opt.setStatus(WmReportOpt.WmReportOptStatus.ERROR);
+				opt.setErrorMsg(o.split(StrUtil.UNDERLINE)[1]);
+			}
+			// 生成失败的2
+			else if (!o.startsWith("http")) {
+				opt.setStatus(WmReportOpt.WmReportOptStatus.ERROR);
+				opt.setErrorMsg(o);
+			}
+			// 成功的
+			else {
+				Long expire = redisTemplate.opsForValue().getOperations().getExpire(key, TimeUnit.SECONDS);
+				opt.setStatus(WmReportOpt.WmReportOptStatus.GENERATED);
+				opt.setTtl(LocalDateTime.now().plusSeconds(expire));
+				opt.setLatestUrl(o);
+			}
+
+		} else {
+			opt.setStatus(WmReportOpt.WmReportOptStatus.NOT_GENERATE);
+		}
+
+		return opt;
+	}
+}