shc 4 сар өмнө
parent
commit
264baccad7

+ 200 - 13
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/task/WmTaskControllerV2.java

@@ -2,6 +2,7 @@ package com.qunzhixinxi.hnqz.admin.controller.task;
 
 import static com.qunzhixinxi.hnqz.common.core.util.R.ok;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.text.StrPool;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -18,15 +19,23 @@ import com.qunzhixinxi.hnqz.common.core.util.R;
 import com.qunzhixinxi.hnqz.common.log.annotation.SysLog;
 import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
 import com.qunzhixinxi.hnqz.common.security.util.SecurityUtils;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import javax.annotation.Resource;
 import javax.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisOperations;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -49,6 +58,9 @@ public class WmTaskControllerV2 {
   @Resource private RedisUtils redisUtils;
   @Resource private TaskManager taskManager;
 
+  private static final ConcurrentHashMap<String, Object> PRETREATMENT_LOCK =
+      new ConcurrentHashMap<>();
+
   /**
    * 获取当前操作人审核过的任务
    *
@@ -171,6 +183,117 @@ public class WmTaskControllerV2 {
     return ok(Boolean.TRUE);
   }
 
+  /**
+   * 清除批量审批预处理
+   *
+   * <p>用于筛选出正在审核任务,并对筛选结果锁定
+   *
+   * @param pretreatmentClear 预处理内容
+   * @return 预处理结果
+   */
+  @SysLog(value = "清除批量审批预处理")
+  @PostMapping(value = "/check/pretreatment/clear")
+  public R<Long> clearPretreatmentBatchCheck(
+      @Valid @RequestBody TaskReqVO.OnPretreatmentClear pretreatmentClear) {
+
+    synchronized (this) {
+
+      // 删除幂等token
+      String taskIdStr =
+          pretreatmentClear.getTaskIds().stream()
+              .sorted()
+              .collect(Collectors.joining(StrPool.COMMA));
+
+      boolean valid = redisUtils.validToken(pretreatmentClear.getToken(), taskIdStr);
+
+      if (!valid) {
+        throw new BizException("审核令牌无效,请刷新数据后重试");
+      }
+
+      List<String> taskIds =
+          pretreatmentClear.getTaskIds().stream()
+              .map(tid -> "task_opt:check:" + tid)
+              .collect(Collectors.toList());
+
+      return R.ok(redisTemplate.delete(taskIds));
+    }
+  }
+
+  /**
+   * 批量审批预处理
+   *
+   * <p>用于筛选出正在审核任务,并对筛选结果锁定
+   *
+   * @param pretreatment 预处理内容
+   * @return 预处理结果
+   */
+  @SysLog(value = "批量审核预处理")
+  @PostMapping(value = "/check/pretreatment")
+  public R<Map<String, Object>> pretreatmentBatchCheck(
+      @Valid @RequestBody TaskReqVO.OnPretreatment pretreatment) {
+
+    synchronized (this) {
+      HnqzUser user = SecurityUtils.getUser();
+
+      String lockInfo = String.format("【%s】已预留该服务", user.getRealName());
+
+      List<String> taskIds = pretreatment.getTaskIds();
+
+      ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
+      RedisOperations<String, Object> redisOperations = valueOperations.getOperations();
+
+      List<Map<String, String>> locked = new LinkedList<>();
+      List<Map<String, String>> available = new LinkedList<>();
+      List<String> availTaskIds = new LinkedList<>();
+
+      LocalDateTime now = LocalDateTime.now();
+      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+      for (String taskId : taskIds) {
+
+        String lockKey = "task_opt:check:" + taskId;
+
+        // 不存在历史执行锁
+        if (Boolean.TRUE.equals(
+            valueOperations.setIfAbsent(lockKey, lockInfo, 3, TimeUnit.MINUTES))) {
+          Map<String, String> map = new HashMap<>(4);
+          map.put("status", "成功");
+          map.put("taskId", taskId);
+          map.put("lockInfo", lockInfo);
+          map.put("expireAt", formatter.format(now.plusMinutes(3)));
+          available.add(map);
+          availTaskIds.add(taskId);
+        }
+        // 存在历史执行锁
+        else {
+          Object object = valueOperations.get(lockKey);
+          Map<String, String> map = new HashMap<>(4);
+          map.put("status", "失败");
+          map.put("taskId", taskId);
+          map.put("lockInfo", object.toString());
+          Long expire = redisOperations.getExpire(lockKey);
+          map.put("expireAt", formatter.format(now.plusSeconds(expire)));
+          locked.add(map);
+        }
+      }
+
+      List<Map<String, String>> total = new LinkedList<>();
+      total.addAll(available);
+      total.addAll(locked);
+      Map<String, Object> result = new HashMap<>(4);
+      result.put("total", total);
+      result.put("locked", locked);
+      result.put("available", available);
+      result.put(
+          "token",
+          CollUtil.isEmpty(available)
+              ? null
+              : redisUtils.generateToken(
+                  availTaskIds.stream().sorted().collect(Collectors.joining(StrPool.COMMA))));
+
+      return R.ok(result);
+    }
+  }
+
   /**
    * 批量任务审核
    *
@@ -215,18 +338,14 @@ public class WmTaskControllerV2 {
       lockKeys.put("task_opt:check:" + taskId, lockInfo);
     }
 
-    Boolean locked = redisTemplate.opsForValue().multiSetIfAbsent(lockKeys);
-
-    if (Boolean.FALSE.equals(locked)) {
-
-      Object object =
-          Objects.requireNonNull(redisTemplate.opsForValue().multiGet(lockKeys.keySet())).stream()
-              .filter(Objects::nonNull)
-              .findFirst()
-              .get();
-
-      throw new BizException("当前批量操作中包含至少一条在途审批(" + object + ",...),请刷新后重试");
-    }
+    // 设置执行锁
+    redisTemplate.opsForValue().multiSet(lockKeys);
+    redisTemplate.executePipelined(
+        (RedisCallback<Object>)
+            connection -> {
+              lockKeys.forEach((k, v) -> connection.expire(k.getBytes(), 600));
+              return null;
+            });
 
     // 发起审核
     try {
@@ -237,4 +356,72 @@ public class WmTaskControllerV2 {
 
     return ok(Boolean.TRUE);
   }
+
+  //  /**
+  //   * 批量任务审核
+  //   *
+  //   * @param checkReq 审核信息
+  //   * @return 审核结果
+  //   */
+  //  @SysLog(value = "批量任务审核")
+  //  @PostMapping(value = "/check/batch")
+  //  public R<Boolean> doBatchCheck(@Valid @RequestBody TaskReqVO.OnBatchCheck checkReq) {
+  //
+  //    // 删除幂等token
+  //    String taskIdStr =
+  //        checkReq.getTaskIds().stream().sorted().collect(Collectors.joining(StrPool.COMMA));
+  //
+  //    boolean valid = redisUtils.validToken(checkReq.getToken(), taskIdStr);
+  //
+  //    if (!valid) {
+  //      throw new BizException("审核令牌无效,请刷新数据后重试");
+  //    }
+  //
+  //    // 校验参数
+  //    if (!checkReq.getCheckResult() && StrUtil.isBlank(checkReq.getCheckMessage())) {
+  //      throw new BizException("拒绝时,【审核意见】必填");
+  //    }
+  //
+  //    List<String> taskIds =
+  //        checkReq.getTaskIds().stream().distinct().sorted().collect(Collectors.toList());
+  //    if (taskIds.size() != checkReq.getTaskIds().size()) {
+  //      throw new BizException("批量请求中存在重复的服务ID");
+  //    }
+  //
+  //    // 添加执行锁
+  //    HnqzUser user = SecurityUtils.getUser();
+  //
+  //    String lockInfo =
+  //        String.format(
+  //            "【%s】正在进行服务【%s】", user.getRealName(), checkReq.getCheckResult() ? "通过" : "拒绝");
+  //
+  //    Map<String, String> lockKeys = new LinkedHashMap<>(taskIds.size());
+  //
+  //    for (String taskId : taskIds) {
+  //      lockKeys.put("task_opt:check:" + taskId, lockInfo);
+  //    }
+  //
+  //    Boolean locked = redisTemplate.opsForValue().multiSetIfAbsent(lockKeys);
+  //
+  //    if (Boolean.FALSE.equals(locked)) {
+  //
+  //      Object object =
+  //
+  // Objects.requireNonNull(redisTemplate.opsForValue().multiGet(lockKeys.keySet())).stream()
+  //              .filter(Objects::nonNull)
+  //              .findFirst()
+  //              .get();
+  //
+  //      throw new BizException("当前批量操作中包含至少一条在途审批(" + object + ",...),请刷新后重试");
+  //    }
+  //
+  //    // 发起审核
+  //    try {
+  //      taskManager.doBatchCheck(checkReq, user);
+  //    } finally {
+  //      redisTemplate.delete(lockKeys.keySet());
+  //    }
+  //
+  //    return ok(Boolean.TRUE);
+  //  }
 }

+ 21 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/task/vo/TaskReqVO.java

@@ -105,4 +105,25 @@ public final class TaskReqVO {
     @NotEmpty(message = "任务ID不能为空")
     private List<String> taskIds;
   }
+
+  @Data
+  public static class OnPretreatment {
+
+    @NotEmpty(message = "批量任务ID必填")
+    @Size(min = 1, max = 500, message = "批量审核任务数量范围为{min}-{max}")
+    private List<String> taskIds;
+
+  }
+
+  @Data
+  public static class OnPretreatmentClear {
+
+    @NotEmpty(message = "批量任务ID必填")
+    @Size(min = 1, max = 500, message = "批量审核任务数量范围为{min}-{max}")
+    private List<String> taskIds;
+
+    @NotBlank(message = "请求令牌不能为空")
+    private String token;
+
+  }
 }