| | |
| | | import com.sinata.appointment.constants.ApprovalNodeCode; |
| | | import com.sinata.appointment.constants.ApprovalTaskStatus; |
| | | import com.sinata.appointment.entity.ApprovalTask; |
| | | import com.sinata.appointment.entity.AppointmentVisitor; |
| | | import com.sinata.appointment.entity.AppointmentOrder; |
| | | import com.sinata.appointment.entity.BizVisitReason; |
| | | import com.sinata.appointment.entity.MpUser; |
| | | import com.sinata.appointment.integration.WechatMiniGateway; |
| | | import com.sinata.appointment.mapper.ApprovalTaskMapper; |
| | | import com.sinata.appointment.mapper.AppointmentVisitorMapper; |
| | | import com.sinata.appointment.mapper.BizVisitReasonMapper; |
| | | import com.sinata.appointment.mapper.MpUserMapper; |
| | | import java.time.LocalDate; |
| | |
| | | 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; |
| | | |
| | |
| | | public class IntegrationWechatService { |
| | | |
| | | private static final String APPROVAL_RESULT_TEMPLATE_ID = |
| | | "4FQovCBNFM7rth42AWHTusHajO788eT9VpplSN4ZG2s"; |
| | | "rIJpfN51_q818RTpJAqTe5ssTwWCHnxDM5rpq64uuKM"; |
| | | private static final String NOTICE_RESULT_TEMPLATE_ID = |
| | | "GC1S3T2qHCbz8ow130u7elRczuqW6Z8q54mKRCYE-xo"; |
| | | "Wip0ODJgybZpeFpcvev_0GK1wQXEqZw-LHOjS-UyDLw"; |
| | | private static final String APPROVAL_RESULT_PAGE = "pages/record/index"; |
| | | private static final String APPROVAL_TODO_TEMPLATE_ID = |
| | | "BX49OBHvRcoYiCw0ZLqx04BiWQ9KaEQwuVth_Lh_M2A"; |
| | | "QlNwG99WXWztRDgtlLcE0fHxfY2VwaX8PRMrwGpEync"; |
| | | 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 = |
| | |
| | | private final WechatMiniGateway wechatMiniGateway; |
| | | private final MpUserMapper mpUserMapper; |
| | | private final ApprovalTaskMapper approvalTaskMapper; |
| | | private final AppointmentVisitorMapper appointmentVisitorMapper; |
| | | 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", buildVisitTemplateTimeValue(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", buildVisitTemplateTimeValue(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("phrase4", approved ? "通过" : "未通过"); |
| | | templateData.put("time2", buildVisitTemplateTimeValue(order)); |
| | | templateData.put("thing5", approved ? "预约审核通过" : "预约审核未通过"); |
| | | 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<>(); |
| | | MpUser submitter = loadSubmitter(order); |
| | | templateData.put("name1", abbreviate(resolveSubmitterName(submitter), 10)); |
| | | templateData.put("number36", buildVisitorCountNumber(visitorCount)); |
| | | templateData.put("date3", buildVisitTemplateTimeValue(order)); |
| | | 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<>(); |
| | | MpUser submitter = loadSubmitter(order); |
| | | templateData.put("name1", abbreviate(resolveSubmitterName(submitter), 10)); |
| | | templateData.put("number15", buildVisitorCountNumber(countVisitors(order))); |
| | | templateData.put("time16", buildVisitTemplateTimeValue(order)); |
| | | templateData.put("thing5", abbreviate(buildVisitReasonText(order), 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 resolveSubmitterName(MpUser submitter) { |
| | | if (submitter == null) { |
| | | return "预约人"; |
| | | } |
| | | if (StringUtils.hasText(submitter.getNickName())) { |
| | | return submitter.getNickName().trim(); |
| | | } |
| | |
| | | return submitter.getMobile().trim(); |
| | | } |
| | | return "预约人"; |
| | | } |
| | | |
| | | private MpUser loadSubmitter(AppointmentOrder order) { |
| | | if (order == null || order.getSubmitterId() == null) { |
| | | return null; |
| | | } |
| | | return mpUserMapper.selectOne( |
| | | new LambdaQueryWrapper<MpUser>() |
| | | .eq(MpUser::getDelFlag, 0) |
| | | .eq(MpUser::getId, order.getSubmitterId()) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | private String buildVisitTimeText(AppointmentOrder order) { |
| | |
| | | return "拜访"; |
| | | } |
| | | if (AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(reasonCode)) { |
| | | String other = safeTrim(order.getVisitReasonOther()); |
| | | return StringUtils.hasText(other) ? "其他:" + other : "其他"; |
| | | return "其他"; |
| | | } |
| | | BizVisitReason reason = |
| | | bizVisitReasonMapper.selectOne( |
| | |
| | | return reasonCode; |
| | | } |
| | | |
| | | private String buildVisitorCountText(int visitorCount) { |
| | | return Math.max(visitorCount, 0) + "人"; |
| | | private String buildVisitorCountNumber(int visitorCount) { |
| | | return String.valueOf(Math.max(visitorCount, 0)); |
| | | } |
| | | |
| | | private String buildNoticeBusinessTypeText(AppointmentOrder order, boolean approved) { |
| | | String reason = buildVisitReasonText(order); |
| | | if (!StringUtils.hasText(reason)) { |
| | | return approved ? "预约通过" : "预约驳回"; |
| | | private int countVisitors(AppointmentOrder order) { |
| | | if (order == null || order.getId() == null) { |
| | | return 0; |
| | | } |
| | | return reason; |
| | | Long count = |
| | | appointmentVisitorMapper.selectCount( |
| | | new LambdaQueryWrapper<AppointmentVisitor>() |
| | | .eq(AppointmentVisitor::getDelFlag, 0) |
| | | .eq(AppointmentVisitor::getOrderId, order.getId())); |
| | | return count == null ? 0 : count.intValue(); |
| | | } |
| | | |
| | | private String buildTodoTaskName(AppointmentOrder order) { |