shc 9 месяцев назад
Родитель
Сommit
c8d2a572a9

+ 23 - 1
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/dto/SysImplementPlanDetailsDTO.java

@@ -1,5 +1,6 @@
 package com.qunzhixinxi.hnqz.admin.api.dto;
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.qunzhixinxi.hnqz.admin.api.constant.UpmsState;
 import lombok.AccessLevel;
 import lombok.Data;
@@ -86,10 +87,31 @@ public final class SysImplementPlanDetailsDTO {
         @NotNull(message = "审核结果必填")
         private Boolean result;
 
-        @NotBlank(message = "审核意见必填")
+        // @NotBlank(message = "审核意见必填")
         private String msg;
 
         @NotNull(message = "当前状态必填")
         private UpmsState.ImplPlanDetailsState currentState;
     }
+
+    @Data
+    public static class OnBatchCheck {
+
+        @ExcelProperty(value = "详情ID")
+        private Integer detailsId;
+
+        @ExcelProperty(value = "审核结果")
+        private String result;
+
+        @ExcelProperty(value = "审核意见")
+        private String msg;
+
+        @ExcelProperty(value = "当前审核节点状态(系统用)")
+        private String currentState;
+    }
+
+    @Data
+    public static class OnExport{
+
+    }
 }

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

@@ -0,0 +1,58 @@
+package com.qunzhixinxi.hnqz.admin.api.model.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import lombok.Data;
+
+/**
+ * 计划详情
+ *
+ * @author jimmy
+ * @date 2024-10-12 16:30
+ */
+@Data
+public class PlanDetailsExportModel {
+
+
+    @ExcelProperty(value = "详情ID")
+    @ColumnWidth(16)
+    private String detailsId;
+
+    @ExcelProperty(value = "所属区域")
+    @ColumnWidth(32)
+    private String consignor;
+
+    @ExcelProperty(value = "承接对象")
+    @ColumnWidth(48)
+    private String consignee;
+
+    @ExcelProperty(value = "计划名称")
+    @ColumnWidth(48)
+    private String planName;
+
+    @ExcelProperty(value = "所属时间")
+    @ColumnWidth(48)
+    private String period;
+
+    @ExcelProperty(value = "计划总值")
+    @ColumnWidth(16)
+    private String total;
+
+    @ExcelProperty(value = "服务内容明细")
+    @ColumnWidth(128)
+    private String summary;
+
+    @ExcelProperty(value = "审核结果")
+    @ColumnWidth(16)
+    private String result;
+
+    @ExcelProperty(value = "审核意见")
+    @ColumnWidth(48)
+    private String msg;
+
+    @ExcelProperty(value = "当前审核节点状态(系统用)")
+    @ColumnWidth(16)
+    private String currentState;
+
+
+}

+ 16 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/SysImplementPlanController.java

@@ -10,6 +10,7 @@ import com.qunzhixinxi.hnqz.admin.api.constant.UpmsState;
 import com.qunzhixinxi.hnqz.admin.api.constant.UpmsType;
 import com.qunzhixinxi.hnqz.admin.api.dto.SysImplementPlanDTO;
 import com.qunzhixinxi.hnqz.admin.api.dto.SysImplementPlanDetailsDTO;
+import com.qunzhixinxi.hnqz.admin.api.model.excel.PlanDetailsExportModel;
 import com.qunzhixinxi.hnqz.admin.api.model.excel.PlanInfoExportModel;
 import com.qunzhixinxi.hnqz.admin.api.vo.SysImplementPlanVO;
 import com.qunzhixinxi.hnqz.admin.manager.SysImplementPlanManager;
@@ -152,10 +153,25 @@ public class SysImplementPlanController {
         return R.ok(implementPlanManager.checkPlanDetails(resource, SecurityUtils.getUser()));
     }
 
+
+    @SysLog(value = "审核计划详情(批量)")
+    @PostMapping(value = "/plan/details/check/batch")
+    public R<List<Map<String, String>>> batchCheckPlanDetails(@RequestExcel(ignoreEmptyRow = true) List<SysImplementPlanDetailsDTO.OnBatchCheck> resource) {
+
+
+        return R.ok(implementPlanManager.batchCheckPlanDetails(resource, SecurityUtils.getUser()));
+    }
+
     @GetMapping(value = "/plan/details/{details_id}")
     public R<Map<String, Object>> getPlanDetails(@PathVariable(value = "details_id") Integer detailsId) {
         return R.ok(implementPlanManager.getPlanDetails(detailsId));
     }
 
+    @GetMapping(value = "/plan/details/export")
+    @ResponseExcel(name = "exportPlanDetails", sheets = {@Sheet(sheetName = "计划详情")})
+    public List<PlanDetailsExportModel> exportPlanDetails(@Validated SysImplementPlanDetailsDTO.OnExport query){
+
+        return implementPlanManager.exportPlanDetails(query, SecurityUtils.getUser());
+    }
 
 }

+ 282 - 1
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/manager/SysImplementPlanManager.java

@@ -3,6 +3,7 @@ package com.qunzhixinxi.hnqz.admin.manager;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.copier.CopyOptions;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.lang.tree.Tree;
 import cn.hutool.core.lang.tree.TreeNode;
 import cn.hutool.core.lang.tree.TreeUtil;
@@ -27,6 +28,7 @@ import com.qunzhixinxi.hnqz.admin.api.entity.SysImplementPlan;
 import com.qunzhixinxi.hnqz.admin.api.entity.SysPlanPkg;
 import com.qunzhixinxi.hnqz.admin.api.entity.WmScorePackage;
 import com.qunzhixinxi.hnqz.admin.api.entity.WmTaskType;
+import com.qunzhixinxi.hnqz.admin.api.model.excel.PlanDetailsExportModel;
 import com.qunzhixinxi.hnqz.admin.api.model.excel.PlanInfoExportModel;
 import com.qunzhixinxi.hnqz.admin.api.vo.SysImplementPlanVO;
 import com.qunzhixinxi.hnqz.admin.service.SysDeptService;
@@ -43,6 +45,7 @@ import com.qunzhixinxi.hnqz.admin.service.WmTaskTypeService;
 import com.qunzhixinxi.hnqz.common.core.constant.CommonConstants;
 import com.qunzhixinxi.hnqz.common.core.exception.BizException;
 import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import com.qunzhixinxi.hnqz.common.security.util.SecurityUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -51,6 +54,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -347,7 +351,8 @@ public class SysImplementPlanManager {
                         wrapper.eq(SysImplementPlan::getPlanState, UpmsState.ImplementPlanState.PASSED).apply("plan_score != avail_score");
                         break;
                     case PASSED_TO_GO:
-                        wrapper.eq(SysImplementPlan::getPlanState, UpmsState.ImplementPlanState.PASSED).apply("plan_score = avail_score");;
+                        wrapper.eq(SysImplementPlan::getPlanState, UpmsState.ImplementPlanState.PASSED).apply("plan_score = avail_score");
+                        ;
                         break;
                     case REJECTED:
                         wrapper.eq(SysImplementPlan::getPlanState, UpmsState.ImplementPlanState.REJECTED);
@@ -985,4 +990,280 @@ public class SysImplementPlanManager {
 
         return collect;
     }
+
+    /**
+     * 导出计划详情
+     *
+     * @param query 查询条件
+     * @param user  操作人
+     * @return 详情信息
+     */
+    public List<PlanDetailsExportModel> exportPlanDetails(SysImplementPlanDetailsDTO.OnExport query, HnqzUser user) {
+
+
+        List<Integer> roles = SecurityUtils.getRoles();
+
+
+        UpmsState.ImplPlanDetailsState detailsState;
+        // 区域管理员
+        if (roles.contains(4)) {
+            detailsState = UpmsState.ImplPlanDetailsState.INIT;
+        }
+        // 商务管理员
+        else if (roles.contains(40)){
+            detailsState = UpmsState.ImplPlanDetailsState.CKT1;
+        }
+        // 事业部分管领导
+        else if(roles.contains(41)) {
+            detailsState = UpmsState.ImplPlanDetailsState.CKT2;
+        } else {
+            throw new BizException("当前角色没有导出权限");
+        }
+
+        // 获取当前人操作的所有可见企业
+        List<Long> entIds = listVisibleEntIds(user);
+
+        if (CollUtil.isEmpty(entIds)) {
+            throw new BizException("操作人未分配管理区域");
+        }
+
+        // 获取所见区域下的所有服务上
+        List<SysDept> depts = deptService.listByIds(entIds);
+        // 过滤出服务
+        Set<Integer> serviceIds = depts.stream().filter(d -> d.getLevel() == 4).mapToInt(SysDept::getDeptId).boxed().collect(Collectors.toSet());
+        if (CollUtil.isEmpty(serviceIds)) {
+            throw new BizException("操作人所在区域不存在服务商");
+        }
+
+        List<SysImplementPlan> plans = implementPlanService.list(Wrappers.<SysImplementPlan>lambdaQuery()
+                .eq(SysImplementPlan::getPlanState, UpmsState.ImplementPlanState.PASSED)
+                .in(SysImplementPlan::getConsigneeId, serviceIds));
+
+        if (CollUtil.isEmpty(plans)) {
+            throw new BizException("没有查询到服务计划");
+        }
+
+        Set<Integer> planIds = plans.stream().mapToInt(SysImplementPlan::getPlanId).boxed().collect(Collectors.toSet());
+
+
+        List<SysImplPlanDetails> details = implPlanDetailsService.list(Wrappers.<SysImplPlanDetails>lambdaQuery()
+                .eq(SysImplPlanDetails::getDetailsState, detailsState)
+                .in(SysImplPlanDetails::getPlanId, planIds));
+
+        if (CollUtil.isEmpty(details)) {
+            throw new BizException("计划尚未拆解");
+        }
+
+        Set<Integer> detailsIds = details.stream().mapToInt(SysImplPlanDetails::getDetailsId).boxed().collect(Collectors.toSet());
+
+        List<SysImplPlanDetailsItem> detailsItems = implPlanDetailsItemService.list(Wrappers.<SysImplPlanDetailsItem>lambdaQuery().in(SysImplPlanDetailsItem::getDetailsId, detailsIds));
+
+        if (CollUtil.isEmpty(detailsItems)) {
+            throw new BizException("计划详情拆解信息不存在");
+        }
+
+
+        Map<Integer, SysImplementPlan> planId2PlanMap = plans.stream().collect(Collectors.toMap(SysImplementPlan::getPlanId, Function.identity()));
+
+        List<PlanDetailsExportModel> collect = details.stream().map(d -> {
+            PlanDetailsExportModel model = new PlanDetailsExportModel();
+            model.setDetailsId(d.getDetailsId().toString());
+            // model.setConsignee();
+            SysImplementPlan plan = planId2PlanMap.get(d.getPlanId());
+
+            SysDept dept = deptService.getById(plan.getConsignorId());
+            model.setConsignor(dept == null ? "" : dept.getName());
+            model.setPlanName(plan.getPlanName());
+
+            SysDept dept1 = deptService.getById(plan.getConsigneeId());
+            model.setConsignee(dept1 == null ? "" : dept1.getName());
+
+            String issue = DateTimeFormatter.ofPattern(DatePattern.CHINESE_DATE_PATTERN).format(plan.getPlanIssue());
+            String expiry = DateTimeFormatter.ofPattern(DatePattern.CHINESE_DATE_PATTERN).format(plan.getPlanExpiry());
+            model.setPeriod(issue + "~" + expiry);
+
+
+            List<SysImplPlanDetailsItem> items = implPlanDetailsItemService.list(Wrappers.<SysImplPlanDetailsItem>lambdaQuery().eq(SysImplPlanDetailsItem::getDetailsId, d.getDetailsId()));
+
+            StringBuilder summary = new StringBuilder();
+
+            items.forEach(i -> {
+                summary.append("[");
+                summary.append("服务名称:【").append(i.getTaskTypeName()).append("】");
+                summary.append("服务次数:【").append(i.getQty()).append("】");
+                summary.append("单项积分:【").append(i.getScore()).append("】");
+                summary.append("服务小记:【").append(i.getSubtotal()).append("】");
+                summary.append("]\n");
+            });
+
+            model.setSummary(summary.toString());
+            model.setCurrentState(d.getDetailsState().getState());
+            model.setTotal(plan.getPlanScore().toString());
+
+            return model;
+
+
+        }).collect(Collectors.toList());
+
+
+        return collect;
+
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public List<Map<String, String>> batchCheckPlanDetails(List<SysImplementPlanDetailsDTO.OnBatchCheck> resource, HnqzUser user) {
+
+
+        // 上锁
+        Set<String> cacheKeySet = resource.stream().map(res -> "lock:plan:details:check:batch:" + res.getDetailsId()).collect(Collectors.toSet());
+
+        List<String> lockKey = new ArrayList<>(resource.size());
+
+        try {
+
+            cacheKeySet.forEach(key -> {
+                boolean locked = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, IdUtil.fastUUID(), 10, TimeUnit.MINUTES));
+
+                if (!locked) {
+                    throw new BizException("有在途的操作,请稍后");
+                }
+                log.info("批量操作加锁:{} =====================================", key);
+                lockKey.add(key);
+
+            });
+
+            // 校验
+            List<Map<String, String>> errorList = this.checkDataOnBatchCheckDetails(resource);
+
+            if (CollUtil.isNotEmpty(errorList)) {
+                return errorList;
+            }
+
+            LocalDateTime now = LocalDateTime.now();
+            String username = user.getUsername();
+            List<SysImplPlanDetails> det = resource.stream().map(res -> {
+
+
+                boolean result = "通过".equals(res.getResult());
+                String msg = res.getMsg();
+                Integer detailsId = res.getDetailsId();
+
+                SysImplPlanDetails details = new SysImplPlanDetails();
+                details.setDetailsId(detailsId);
+
+                UpmsState.ImplPlanDetailsState implPlanDetailsState = Enum.valueOf(UpmsState.ImplPlanDetailsState.class, res.getCurrentState());
+                switch (implPlanDetailsState) {
+                    case INIT:
+                        details.setDetailsState(result ? UpmsState.ImplPlanDetailsState.CKT1 : UpmsState.ImplPlanDetailsState.REJECTED);
+                        details.setCktRes1(result);
+                        details.setCktMsg1(msg);
+                        details.setCktTime1(now);
+                        details.setCktName1(username);
+                        break;
+
+                    case CKT1:
+                        details.setDetailsState(result ? UpmsState.ImplPlanDetailsState.CKT2 : UpmsState.ImplPlanDetailsState.REJECTED);
+                        details.setCktRes2(result);
+                        details.setCktMsg2(msg);
+                        details.setCktTime2(now);
+                        details.setCktName2(username);
+                        break;
+                    case CKT2:
+                        details.setDetailsState(result ? UpmsState.ImplPlanDetailsState.PASSED : UpmsState.ImplPlanDetailsState.REJECTED);
+                        details.setCktRes3(result);
+                        details.setCktMsg3(msg);
+                        details.setCktTime3(now);
+                        details.setCktName3(username);
+                        break;
+                    default:
+                        throw new BizException("当前状态不支持审核");
+
+                }
+                details.setUpdateBy(username);
+                details.setUpdateTime(now);
+
+                return details;
+
+
+            }).collect(Collectors.toList());
+
+            implPlanDetailsService.updateBatchById(det);
+
+
+            return Collections.emptyList();
+
+
+        } finally {
+            if (CollUtil.isNotEmpty(lockKey)) {
+                redisTemplate.delete(lockKey);
+            }
+        }
+    }
+
+    private List<Map<String, String>> checkDataOnBatchCheckDetails(List<SysImplementPlanDetailsDTO.OnBatchCheck> resource) {
+
+        List<Map<String, String>> errorList = new LinkedList<>();
+
+        int size = resource.size();
+
+        if (size == 0) {
+            Map<String, String> errorMap = new HashMap<>(2);
+            errorMap.put("idx", "1");
+            errorMap.put("errorMsg", "空表单不支持审核");
+            errorList.add(errorMap);
+            return errorList;
+        }
+
+
+        Map<Integer, List<SysImplementPlanDetailsDTO.OnBatchCheck>> detailsId2batchCheck = resource.stream().collect(Collectors.groupingBy(SysImplementPlanDetailsDTO.OnBatchCheck::getDetailsId));
+
+
+        for (int i = 0; i < size; i++) {
+
+
+            StringBuilder errorMsg = new StringBuilder();
+
+            SysImplementPlanDetailsDTO.OnBatchCheck onBatchCheck = resource.get(i);
+
+            String result = onBatchCheck.getResult();
+            String msg = onBatchCheck.getMsg();
+            String currentState = onBatchCheck.getCurrentState();
+            Integer detailsId = onBatchCheck.getDetailsId();
+
+            // 校验空值
+            if (StrUtil.hasBlank(result, currentState) || detailsId == null) {
+                errorMsg.append("存在未填写的内容;");
+            }
+
+            if (!StrUtil.equalsAny(currentState, "CKT2", "CKT1", "INIT")) {
+                errorMsg.append("当前审核节点(系统用)值错误,只能是INIT/CKT1/CKT2;");
+            }
+
+            List<SysImplementPlanDetailsDTO.OnBatchCheck> onBatchChecks = detailsId2batchCheck.get(onBatchCheck.getDetailsId());
+            if (CollUtil.isNotEmpty(onBatchChecks) && onBatchChecks.size() > 1) {
+                errorMsg.append("表单中存在重复的详情ID;");
+            }
+
+            if (!StrUtil.equalsAny(result, "通过", "驳回")) {
+                errorMsg.append("审核结果只能是通过/驳回;");
+            }
+
+            if ("驳回".equals(result) && StrUtil.isBlank(msg)) {
+                errorMsg.append("驳回信息必填;");
+            }
+
+
+            if (StrUtil.isNotBlank(errorMsg)) {
+                Map<String, String> errorMap = new HashMap<>(2);
+                String idx = String.valueOf(i + 1);
+                errorMap.put("idx", idx);
+                errorMap.put("errorMsg", errorMsg.toString());
+                errorList.add(errorMap);
+            }
+
+        }
+
+
+        return errorList;
+    }
 }