|
@@ -32,7 +32,10 @@ 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.ExportUtils;
|
|
|
import com.qunzhixinxi.hnqz.admin.util.OsEnvUtils;
|
|
|
+import com.qunzhixinxi.hnqz.admin.util.RedisUtils;
|
|
|
+import com.qunzhixinxi.hnqz.common.core.constant.CommonConstants;
|
|
|
import com.qunzhixinxi.hnqz.common.core.exception.BizException;
|
|
|
import com.qunzhixinxi.hnqz.common.core.util.SpringContextHolder;
|
|
|
import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
|
|
@@ -77,15 +80,10 @@ public class SysUserExportServiceImpl implements SysUserExportService {
|
|
|
private final SysUserAreaService userAreaService;
|
|
|
private final SysDeptMapper deptMapper;
|
|
|
private final SysUserCertificateMapper userCertificateMapper;
|
|
|
- private final SysFileService fileService;
|
|
|
private final UpmsConfig upmsConfig;
|
|
|
private final RetryTemplate retryTemplate;
|
|
|
-
|
|
|
- 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失败";
|
|
|
-
|
|
|
+ private final RedisUtils redisUtils;
|
|
|
+ private final ExportUtils exportUtils;
|
|
|
|
|
|
/**
|
|
|
* 异步导出
|
|
@@ -99,34 +97,10 @@ public class SysUserExportServiceImpl implements SysUserExportService {
|
|
|
@Override
|
|
|
@ExportGuard(type = "USER")
|
|
|
public Boolean asyncExport(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query) {
|
|
|
- return retryTemplate.execute(retryContext -> {
|
|
|
- log.info("人员异步导出第{}次重试", retryContext.getRetryCount());
|
|
|
-
|
|
|
- // 使用Redis原子操作实现限流
|
|
|
- String key = CacheConstants.ASYNC_EXPORT_LIMIT_KEY;
|
|
|
- Long increment = redisTemplate.opsForValue().increment(key);
|
|
|
-
|
|
|
- // 设置key的过期时间,避免计数器无限增长
|
|
|
- if (increment != null && increment.equals(1L)) {
|
|
|
- redisTemplate.expire(key, 1, TimeUnit.MINUTES);
|
|
|
- }
|
|
|
-
|
|
|
- // 检查是否超过限流阈值
|
|
|
- if (increment != null && increment > upmsConfig.getAsyncExportLimit()) {
|
|
|
- // 超过限流阈值时,减少计数器并抛出异常
|
|
|
- redisTemplate.opsForValue().decrement(key);
|
|
|
- throw new BizException("系统繁忙,请稍后再试");
|
|
|
- }
|
|
|
-
|
|
|
- // 缓存key
|
|
|
- String cacheKey = String.format(CacheConstants.ASYNC_EXPORT_CACHE, ExportType.USER.getType(), user.getId());
|
|
|
- // 更新状态为生成中
|
|
|
- redisTemplate.opsForValue().set(cacheKey, WmReportOpt.WmReportOptStatus.GENERATING.name(), DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
|
|
|
-
|
|
|
- SpringContextHolder.getApplicationContext().publishEvent(new UserExportEvent(user, roles, query));
|
|
|
- return Boolean.TRUE;
|
|
|
- });
|
|
|
|
|
|
+ redisUtils.setExportStarting(ExportType.USER, user.getId());
|
|
|
+ SpringContextHolder.getApplicationContext().publishEvent(new UserExportEvent(user, roles, query));
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -139,114 +113,76 @@ public class SysUserExportServiceImpl implements SysUserExportService {
|
|
|
*/
|
|
|
@Override
|
|
|
public Boolean export(HnqzUser user, List<Integer> roles, SysUserDTO.OnList query) {
|
|
|
- // 缓存key
|
|
|
- String key = String.format(CacheConstants.ASYNC_EXPORT_CACHE, ExportType.USER.getType(), user.getId());
|
|
|
-
|
|
|
- // 临时文件路径
|
|
|
- String tempPath = OsEnvUtils.getEachEnvPaths().get(OsEnvUtils.TargetFile.TEMP.getName());
|
|
|
- // 缓存文件名
|
|
|
- String fileName = "人员_" + DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)
|
|
|
- .format(LocalDateTime.now()) + RandomStringUtils.randomNumeric(6) + ".xlsx";
|
|
|
- String fullPath = tempPath + fileName;
|
|
|
-
|
|
|
try {
|
|
|
- // 查询用户列表
|
|
|
- List<SysUser> users = this.listUsers(user, roles, query);
|
|
|
+ return retryTemplate.execute(retryContext -> {
|
|
|
+ log.info("人员异步导出第{}次重试", retryContext.getRetryCount());
|
|
|
|
|
|
- if (CollUtil.isEmpty(users)) {
|
|
|
- log.info("用户列表为空");
|
|
|
- redisTemplate.opsForValue().set(key, ERROR_MSG_NO_DATA, DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
|
|
|
- return Boolean.FALSE;
|
|
|
- }
|
|
|
+ redisUtils.checkExportGlobalAllows(upmsConfig.getAsyncExportLimit());
|
|
|
|
|
|
- // 构建Excel数据并导出
|
|
|
- String resultValue = buildAndExportExcel(users, fullPath, fileName, user);
|
|
|
- redisTemplate.opsForValue().set(key, resultValue, DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
|
|
|
+ // 查询用户列表 构建Excel数据
|
|
|
+ List<SysUser> users = this.listUsers(user, roles, query);
|
|
|
+ List<SysUserExcelModel> data = buildExcelModel(users);
|
|
|
|
|
|
- return !StrUtil.startWith(resultValue, "ERROR");
|
|
|
- } finally {
|
|
|
- // 清理临时文件
|
|
|
- cleanupTempFile(fullPath);
|
|
|
+ // 导出
|
|
|
+ String resultValue = exportUtils.WriteExportExcel(ExportType.USER, data, SysUserExcelModel.class, user);
|
|
|
+ return !StrUtil.startWith(resultValue, "ERROR");
|
|
|
+ });
|
|
|
|
|
|
- // 删除限流key
|
|
|
- String limitKey = CacheConstants.ASYNC_EXPORT_LIMIT_KEY;
|
|
|
- redisTemplate.delete(limitKey);
|
|
|
+ } finally {
|
|
|
+ // 释放全局限流
|
|
|
+ redisTemplate.opsForValue().decrement(CacheConstants.ASYNC_EXPORT_LIMIT_KEY);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
|
|
|
- /**
|
|
|
- * 构建Excel数据并导出到文件
|
|
|
- *
|
|
|
- * @param users 用户列表
|
|
|
- * @param fullPath 完整文件路径
|
|
|
- * @param fileName 文件名
|
|
|
- * @param user 当前用户
|
|
|
- * @return 结果值(URL或错误信息)
|
|
|
- */
|
|
|
- private String buildAndExportExcel(List<SysUser> users, String fullPath, String fileName, HnqzUser user) {
|
|
|
- try {
|
|
|
- // 查询企业信息
|
|
|
- Map<Integer, SysDept> deptMap = queryDeptInfo(users);
|
|
|
-
|
|
|
- // 查询角色信息
|
|
|
- Map<Integer, String> roleMap = queryRoleInfo(users);
|
|
|
- Map<Integer, List<SysUserRole>> userRolesMap = queryUserRoles(users);
|
|
|
-
|
|
|
- // 查询备案信息
|
|
|
- Map<Integer, Long> userCertMap = queryCertificateInfo(users);
|
|
|
-
|
|
|
- // 转为excel列表
|
|
|
- List<SysUserExcelModel> excelModels = users.stream().map(u -> {
|
|
|
- SysUserExcelModel excelModel = BeanUtil.copyProperties(u, SysUserExcelModel.class, "lockFlag");
|
|
|
- excelModel.setRealName(u.getRealname());
|
|
|
- excelModel.setPhoneNumber(u.getPhone());
|
|
|
- // 锁定状态
|
|
|
- excelModel.setLockFlag("0".equals(u.getLockFlag()) ? "活跃" : "休眠");
|
|
|
- // 创建时间
|
|
|
- excelModel.setCreateTime(u.getCreateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
|
|
-
|
|
|
- // 派工方
|
|
|
- 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 ? "是" : "否");
|
|
|
-
|
|
|
- return excelModel;
|
|
|
- }).collect(Collectors.toList());
|
|
|
-
|
|
|
- // 写入excel文件
|
|
|
- EasyExcel.write(fullPath, SysUserExcelModel.class).sheet("人员")
|
|
|
- .doWrite(excelModels);
|
|
|
- log.info("人员导出生成缓存文件:{}", fullPath);
|
|
|
-
|
|
|
- // 上传oss
|
|
|
- try (FileInputStream inputStream = new FileInputStream(fullPath)) {
|
|
|
- Map<String, String> uploadResult = fileService.upload(inputStream, fileName, fileName, user.getUsername());
|
|
|
- log.info("人员导出生成oss文件:{}", uploadResult);
|
|
|
-
|
|
|
- if (CollUtil.isNotEmpty(uploadResult)) {
|
|
|
- return uploadResult.get("url");
|
|
|
- } else {
|
|
|
- return ERROR_MSG_UPLOAD_FAIL;
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("人员导出失败", e);
|
|
|
- return ERROR_MSG_UNKNOWN;
|
|
|
+ private List<SysUserExcelModel> buildExcelModel(List<SysUser> users) {
|
|
|
+ if (CollUtil.isEmpty(users)) {
|
|
|
+ log.info("用户列表为空");
|
|
|
+ return List.of();
|
|
|
}
|
|
|
+ // 查询企业信息
|
|
|
+ Map<Integer, SysDept> deptMap = queryDeptInfo(users);
|
|
|
+
|
|
|
+ // 查询角色信息
|
|
|
+ Map<Integer, String> roleMap = queryRoleInfo(users);
|
|
|
+ Map<Integer, List<SysUserRole>> userRolesMap = queryUserRoles(users);
|
|
|
+
|
|
|
+ // 查询备案信息
|
|
|
+ Map<Integer, Long> userCertMap = queryCertificateInfo(users);
|
|
|
+
|
|
|
+ // 转为excel列表
|
|
|
+ List<SysUserExcelModel> excelModels = users.stream().map(u -> {
|
|
|
+ SysUserExcelModel excelModel = BeanUtil.copyProperties(u, SysUserExcelModel.class, "lockFlag");
|
|
|
+ excelModel.setRealName(u.getRealname());
|
|
|
+ excelModel.setPhoneNumber(u.getPhone());
|
|
|
+ // 锁定状态
|
|
|
+ excelModel.setLockFlag("0".equals(u.getLockFlag()) ? "活跃" : "休眠");
|
|
|
+ // 创建时间
|
|
|
+ excelModel.setCreateTime(u.getCreateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
|
|
+
|
|
|
+ // 派工方
|
|
|
+ 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 ? "是" : "否");
|
|
|
+
|
|
|
+ return excelModel;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ return excelModels;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -304,19 +240,6 @@ public class SysUserExportServiceImpl implements SysUserExportService {
|
|
|
.collect(Collectors.groupingBy(SysUserCertificate::getUserId, Collectors.counting()));
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 清理临时文件
|
|
|
- */
|
|
|
- private void cleanupTempFile(String fullPath) {
|
|
|
- try {
|
|
|
- File file = new File(fullPath);
|
|
|
- if (file.exists()) {
|
|
|
- file.delete();
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("删除临时文件失败: {}", fullPath, e);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
|
|
|
/**
|