| | |
| | | import java.util.Map; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | |
| | | "BX49OBHvRcoYiCw0ZLqx04BiWQ9KaEQwuVth_Lh_M2A"; |
| | | private static final String APPROVAL_TODO_PAGE = "pages/reviewList/index"; |
| | | private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | private static final DateTimeFormatter TEMPLATE_TIME_FORMATTER = |
| | | DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); |
| | | |
| | | private final WechatMiniGateway wechatMiniGateway; |
| | | private final MpUserMapper mpUserMapper; |
| | | private final ApprovalTaskMapper approvalTaskMapper; |
| | | private final BizVisitReasonMapper bizVisitReasonMapper; |
| | | |
| | | @Async |
| | | public void notifyApprovalFinalResult(AppointmentOrder order, boolean approved) { |
| | | if (order == null || order.getSubmitterId() == null) { |
| | | return; |
| | | } |
| | | MpUser submitter = |
| | | mpUserMapper.selectOne( |
| | | new LambdaQueryWrapper<MpUser>() |
| | | .eq(MpUser::getDelFlag, 0) |
| | | .eq(MpUser::getId, order.getSubmitterId()) |
| | | .last("limit 1")); |
| | | if (submitter == null || !StringUtils.hasText(submitter.getOpenId())) { |
| | | log.info( |
| | | "Skip mini subscribe notify, submitter not found or openId empty. submitterId={} orderNo={}", |
| | | order.getSubmitterId(), |
| | | order.getOrderNo()); |
| | | return; |
| | | } |
| | | Map<String, String> templateData = new LinkedHashMap<>(); |
| | | templateData.put("thing1", approved ? "审核通过" : "审核未通过"); |
| | | templateData.put("thing2", abbreviate(resolveSubmitterName(submitter), 20)); |
| | | templateData.put("time3", buildVisitTimeText(order)); |
| | | templateData.put("thing4", abbreviate(buildVisitReasonText(order), 20)); |
| | | wechatMiniGateway.sendSubscribeMessage( |
| | | submitter.getOpenId(), APPROVAL_RESULT_TEMPLATE_ID, APPROVAL_RESULT_PAGE, templateData); |
| | | log.info( |
| | | "Mini subscribe notify sent submitterId={} orderNo={} result={}", |
| | | order.getSubmitterId(), |
| | | order.getOrderNo(), |
| | | approved ? AppointmentOrderStatus.APPROVED : AppointmentOrderStatus.REJECTED); |
| | | } |
| | | |
| | | public void notifyApprovalNoticeUsers( |
| | | AppointmentOrder order, boolean approved, java.util.Collection<MpUser> noticeUsers, int visitorCount) { |
| | | if (order == null || noticeUsers == null || noticeUsers.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<String, String> templateData = new LinkedHashMap<>(); |
| | | templateData.put("thing6", buildVisitorCountText(visitorCount)); |
| | | templateData.put("time18", buildVisitTimeText(order)); |
| | | templateData.put("thing20", abbreviate(buildNoticeBusinessTypeText(order, approved), 20)); |
| | | for (MpUser noticeUser : noticeUsers) { |
| | | if (noticeUser == null || !StringUtils.hasText(noticeUser.getOpenId())) { |
| | | continue; |
| | | try { |
| | | if (order == null || order.getSubmitterId() == null) { |
| | | log.info("Mini subscribe final result skipped, order or submitter empty. orderId={}", |
| | | order == null ? null : order.getId()); |
| | | return; |
| | | } |
| | | wechatMiniGateway.sendSubscribeMessage( |
| | | noticeUser.getOpenId(), NOTICE_RESULT_TEMPLATE_ID, APPROVAL_RESULT_PAGE, templateData); |
| | | log.info( |
| | | "Mini subscribe notice sent noticeUserId={} orderNo={} result={}", |
| | | noticeUser.getId(), |
| | | order.getOrderNo(), |
| | | approved ? AppointmentOrderStatus.APPROVED : AppointmentOrderStatus.REJECTED); |
| | | } |
| | | } |
| | | |
| | | public void notifyCurrentApprovers(AppointmentOrder order, Long instanceId) { |
| | | if (order == null || instanceId == null) { |
| | | return; |
| | | } |
| | | java.util.List<ApprovalTask> pendingTasks = |
| | | approvalTaskMapper.selectList( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getDelFlag, 0) |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getTaskStatus, ApprovalTaskStatus.PENDING) |
| | | .orderByAsc(ApprovalTask::getId)); |
| | | if (pendingTasks.isEmpty()) { |
| | | return; |
| | | } |
| | | java.util.LinkedHashMap<Long, java.util.LinkedHashSet<String>> userNodeNames = new java.util.LinkedHashMap<>(); |
| | | for (ApprovalTask task : pendingTasks) { |
| | | if (task.getAssigneeUserId() == null) { |
| | | continue; |
| | | } |
| | | userNodeNames.computeIfAbsent(task.getAssigneeUserId(), key -> new java.util.LinkedHashSet<>()); |
| | | userNodeNames.get(task.getAssigneeUserId()).add(resolveNodeName(task.getNodeCode())); |
| | | } |
| | | if (userNodeNames.isEmpty()) { |
| | | return; |
| | | } |
| | | java.util.List<MpUser> users = |
| | | mpUserMapper.selectList( |
| | | new LambdaQueryWrapper<MpUser>() |
| | | .eq(MpUser::getDelFlag, 0) |
| | | .in(MpUser::getId, userNodeNames.keySet())); |
| | | java.util.Map<Long, MpUser> userMap = new java.util.LinkedHashMap<>(); |
| | | for (MpUser user : users) { |
| | | userMap.putIfAbsent(user.getId(), user); |
| | | } |
| | | for (java.util.Map.Entry<Long, java.util.LinkedHashSet<String>> entry : userNodeNames.entrySet()) { |
| | | MpUser user = userMap.get(entry.getKey()); |
| | | if (user == null || !StringUtils.hasText(user.getOpenId())) { |
| | | continue; |
| | | MpUser submitter = |
| | | mpUserMapper.selectOne( |
| | | new LambdaQueryWrapper<MpUser>() |
| | | .eq(MpUser::getDelFlag, 0) |
| | | .eq(MpUser::getId, order.getSubmitterId()) |
| | | .last("limit 1")); |
| | | if (submitter == null || !StringUtils.hasText(submitter.getOpenId())) { |
| | | log.info( |
| | | "Mini subscribe final result skipped, submitter not found or openId empty. submitterId={} orderNo={}", |
| | | order.getSubmitterId(), |
| | | order.getOrderNo()); |
| | | return; |
| | | } |
| | | Map<String, String> templateData = new LinkedHashMap<>(); |
| | | templateData.put("thing1", abbreviate(buildTodoTaskName(order), 20)); |
| | | templateData.put("thing3", abbreviate(String.join("、", entry.getValue()), 20)); |
| | | templateData.put("thing1", approved ? "审核通过" : "审核未通过"); |
| | | templateData.put("thing2", abbreviate(resolveSubmitterName(submitter), 20)); |
| | | templateData.put("time3", buildVisitTemplateTimeValue(order)); |
| | | templateData.put("thing4", abbreviate(buildVisitReasonText(order), 20)); |
| | | wechatMiniGateway.sendSubscribeMessage( |
| | | user.getOpenId(), APPROVAL_TODO_TEMPLATE_ID, APPROVAL_TODO_PAGE, templateData); |
| | | submitter.getOpenId(), APPROVAL_RESULT_TEMPLATE_ID, APPROVAL_RESULT_PAGE, templateData); |
| | | log.info( |
| | | "Mini subscribe todo notify sent approverId={} orderNo={} stages={}", |
| | | user.getId(), |
| | | "Mini subscribe final result success. submitterId={} orderNo={} result={}", |
| | | order.getSubmitterId(), |
| | | order.getOrderNo(), |
| | | entry.getValue()); |
| | | approved ? AppointmentOrderStatus.APPROVED : AppointmentOrderStatus.REJECTED); |
| | | } catch (Exception ex) { |
| | | log.warn("Mini subscribe final result failed. orderId={} orderNo={}", |
| | | order == null ? null : order.getId(), order == null ? null : order.getOrderNo(), ex); |
| | | } |
| | | } |
| | | |
| | | @Async |
| | | public void notifyApprovalNoticeUsers( |
| | | AppointmentOrder order, boolean approved, java.util.Collection<MpUser> noticeUsers, int visitorCount) { |
| | | try { |
| | | if (order == null || noticeUsers == null || noticeUsers.isEmpty()) { |
| | | log.info("Mini subscribe notice skipped, order or notice users empty. orderId={}", |
| | | order == null ? null : order.getId()); |
| | | return; |
| | | } |
| | | Map<String, String> templateData = new LinkedHashMap<>(); |
| | | templateData.put("thing6", buildVisitorCountText(visitorCount)); |
| | | templateData.put("time18", buildVisitTemplateTimeValue(order)); |
| | | templateData.put("thing20", abbreviate(buildNoticeBusinessTypeText(order, approved), 20)); |
| | | int successCount = 0; |
| | | int skippedCount = 0; |
| | | int failedCount = 0; |
| | | for (MpUser noticeUser : noticeUsers) { |
| | | if (noticeUser == null || !StringUtils.hasText(noticeUser.getOpenId())) { |
| | | skippedCount++; |
| | | continue; |
| | | } |
| | | try { |
| | | wechatMiniGateway.sendSubscribeMessage( |
| | | noticeUser.getOpenId(), NOTICE_RESULT_TEMPLATE_ID, APPROVAL_RESULT_PAGE, templateData); |
| | | successCount++; |
| | | log.info( |
| | | "Mini subscribe notice success. noticeUserId={} orderNo={} result={}", |
| | | noticeUser.getId(), |
| | | order.getOrderNo(), |
| | | approved ? AppointmentOrderStatus.APPROVED : AppointmentOrderStatus.REJECTED); |
| | | } catch (Exception ex) { |
| | | failedCount++; |
| | | log.warn("Mini subscribe notice failed. noticeUserId={} orderId={} orderNo={}", |
| | | noticeUser.getId(), order.getId(), order.getOrderNo(), ex); |
| | | } |
| | | } |
| | | log.info("Mini subscribe notice completed. orderId={} orderNo={} success={} skipped={} failed={}", |
| | | order.getId(), order.getOrderNo(), successCount, skippedCount, failedCount); |
| | | } catch (Exception ex) { |
| | | log.warn("Mini subscribe notice batch failed. orderId={} orderNo={}", |
| | | order == null ? null : order.getId(), order == null ? null : order.getOrderNo(), ex); |
| | | } |
| | | } |
| | | |
| | | @Async |
| | | public void notifyCurrentApprovers(AppointmentOrder order, Long instanceId) { |
| | | try { |
| | | if (order == null || instanceId == null) { |
| | | log.info("Mini subscribe todo skipped, order or instance empty. orderId={} instanceId={}", |
| | | order == null ? null : order.getId(), instanceId); |
| | | return; |
| | | } |
| | | java.util.List<ApprovalTask> pendingTasks = |
| | | approvalTaskMapper.selectList( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getDelFlag, 0) |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getTaskStatus, ApprovalTaskStatus.PENDING) |
| | | .orderByAsc(ApprovalTask::getId)); |
| | | if (pendingTasks.isEmpty()) { |
| | | log.info("Mini subscribe todo skipped, no pending tasks. orderId={} instanceId={}", |
| | | order.getId(), instanceId); |
| | | return; |
| | | } |
| | | java.util.LinkedHashMap<Long, java.util.LinkedHashSet<String>> userNodeNames = |
| | | new java.util.LinkedHashMap<>(); |
| | | for (ApprovalTask task : pendingTasks) { |
| | | if (task.getAssigneeUserId() == null) { |
| | | continue; |
| | | } |
| | | userNodeNames.computeIfAbsent(task.getAssigneeUserId(), key -> new java.util.LinkedHashSet<>()); |
| | | userNodeNames.get(task.getAssigneeUserId()).add(resolveNodeName(task.getNodeCode())); |
| | | } |
| | | if (userNodeNames.isEmpty()) { |
| | | log.info("Mini subscribe todo skipped, no assignee users. orderId={} instanceId={}", |
| | | order.getId(), instanceId); |
| | | return; |
| | | } |
| | | java.util.List<MpUser> users = |
| | | mpUserMapper.selectList( |
| | | new LambdaQueryWrapper<MpUser>() |
| | | .eq(MpUser::getDelFlag, 0) |
| | | .in(MpUser::getId, userNodeNames.keySet())); |
| | | java.util.Map<Long, MpUser> userMap = new java.util.LinkedHashMap<>(); |
| | | for (MpUser user : users) { |
| | | userMap.putIfAbsent(user.getId(), user); |
| | | } |
| | | int successCount = 0; |
| | | int skippedCount = 0; |
| | | int failedCount = 0; |
| | | for (java.util.Map.Entry<Long, java.util.LinkedHashSet<String>> entry : userNodeNames.entrySet()) { |
| | | MpUser user = userMap.get(entry.getKey()); |
| | | if (user == null || !StringUtils.hasText(user.getOpenId())) { |
| | | skippedCount++; |
| | | continue; |
| | | } |
| | | Map<String, String> templateData = new LinkedHashMap<>(); |
| | | templateData.put("thing1", abbreviate(buildTodoTaskName(order), 20)); |
| | | templateData.put("thing3", abbreviate(String.join("、", entry.getValue()), 20)); |
| | | try { |
| | | wechatMiniGateway.sendSubscribeMessage( |
| | | user.getOpenId(), APPROVAL_TODO_TEMPLATE_ID, APPROVAL_TODO_PAGE, templateData); |
| | | successCount++; |
| | | log.info( |
| | | "Mini subscribe todo success. approverId={} orderNo={} stages={}", |
| | | user.getId(), |
| | | order.getOrderNo(), |
| | | entry.getValue()); |
| | | } catch (Exception ex) { |
| | | failedCount++; |
| | | log.warn("Mini subscribe todo failed. approverId={} orderId={} orderNo={} stages={}", |
| | | user.getId(), order.getId(), order.getOrderNo(), entry.getValue(), ex); |
| | | } |
| | | } |
| | | log.info("Mini subscribe todo completed. orderId={} orderNo={} success={} skipped={} failed={}", |
| | | order.getId(), order.getOrderNo(), successCount, skippedCount, failedCount); |
| | | } catch (Exception ex) { |
| | | log.warn("Mini subscribe todo batch failed. orderId={} instanceId={}", |
| | | order == null ? null : order.getId(), instanceId, ex); |
| | | } |
| | | } |
| | | |
| | |
| | | }; |
| | | } |
| | | |
| | | private String buildVisitTemplateTimeValue(AppointmentOrder order) { |
| | | LocalDate start = order.getVisitStartDate(); |
| | | LocalDate end = order.getVisitEndDate(); |
| | | if (start == null && end == null) { |
| | | return "2099-12-31 00:00"; |
| | | } |
| | | if (start == null) { |
| | | start = end; |
| | | } |
| | | if (end == null) { |
| | | end = start; |
| | | } |
| | | return switch (safeTrim(order.getVisitTimeMode())) { |
| | | case "MULTI" -> start.atTime(0, 0).format(TEMPLATE_TIME_FORMATTER); |
| | | default -> start.atTime(resolveTemplateHour(order.getVisitTimeSlot()), 0) |
| | | .format(TEMPLATE_TIME_FORMATTER); |
| | | }; |
| | | } |
| | | |
| | | private String buildVisitReasonText(AppointmentOrder order) { |
| | | String reasonCode = safeTrim(order.getVisitReasonCode()); |
| | | if (!StringUtils.hasText(reasonCode)) { |
| | |
| | | } |
| | | if (AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(reasonCode)) { |
| | | String other = safeTrim(order.getVisitReasonOther()); |
| | | return StringUtils.hasText(other) ? other : "其他"; |
| | | return StringUtils.hasText(other) ? "其他:" + other : "其他"; |
| | | } |
| | | BizVisitReason reason = |
| | | bizVisitReasonMapper.selectOne( |
| | |
| | | }; |
| | | } |
| | | |
| | | private int resolveTemplateHour(String slot) { |
| | | return switch (safeTrim(slot)) { |
| | | case "AM" -> 9; |
| | | case "PM" -> 14; |
| | | case "ALL_DAY" -> 9; |
| | | default -> 9; |
| | | }; |
| | | } |
| | | |
| | | private String formatDate(LocalDate date) { |
| | | return date.format(DATE_FORMATTER); |
| | | } |