|
@@ -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;
|
|
|
+ }
|
|
|
+}
|