|
@@ -0,0 +1,221 @@
|
|
|
+package org.jeecg.modules.app.wxNotification;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import lombok.extern.log4j.Log4j2;
|
|
|
+import org.jeecg.modules.app.vo.weixin.NotificationRequest;
|
|
|
+import org.jeecg.modules.app.weixinService.WechatNotificationService;
|
|
|
+import org.jeecg.modules.quartz.vo.JobClassNoticeVo;
|
|
|
+import org.jeecg.modules.quartz.vo.JobExtendedClassNoticeVo;
|
|
|
+import org.jeecg.modules.system.app.entity.AppOrder;
|
|
|
+import org.jeecg.modules.system.app.service.IAppOrderService;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Log4j2
|
|
|
+@Service
|
|
|
+public class WxNotificationService {
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ WechatNotificationService notificationService;
|
|
|
+ @Autowired
|
|
|
+ IAppOrderService iAppOrderService;
|
|
|
+ private final int maxRetry = 3;
|
|
|
+ private final List<Integer> notifyHoursBefore = Arrays.asList(8, 24);
|
|
|
+ @Autowired
|
|
|
+ private RedisTemplate<String, String> redisTemplate;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上课时间服务通知 8小时和24小时
|
|
|
+ */
|
|
|
+ public void classNoticeJob() {
|
|
|
+
|
|
|
+ String lockKey = "order:notification:lock";
|
|
|
+ String lockValue = String.valueOf(System.currentTimeMillis());
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取分布式锁,防止多实例重复执行
|
|
|
+ Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 4, TimeUnit.MINUTES);
|
|
|
+ if (locked == null || !locked) {
|
|
|
+ log.debug("Failed to acquire notification lock, skipping this execution");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Date now = new Date();
|
|
|
+ log.info("Start checking orders for notification at {}", now);
|
|
|
+
|
|
|
+ // 处理每个预设的通知时间点
|
|
|
+ for (Integer hours : notifyHoursBefore) {
|
|
|
+ processNotificationForTimeWindow(now, hours);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("Finished checking orders for notification");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Error occurred while checking orders for notification", e);
|
|
|
+ } finally {
|
|
|
+ // 释放锁
|
|
|
+ if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
|
|
|
+ redisTemplate.delete(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 预约过场地通知 每天7点执行
|
|
|
+ */
|
|
|
+ public void siteJob() {
|
|
|
+
|
|
|
+ String lockKey = "site:notification:lock";
|
|
|
+ String lockValue = String.valueOf(System.currentTimeMillis());
|
|
|
+ try {
|
|
|
+ // 获取分布式锁,防止多实例重复执行
|
|
|
+ Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 4, TimeUnit.MINUTES);
|
|
|
+ if (locked == null || !locked) {
|
|
|
+ log.debug("Failed to acquire notification lock, skipping this execution");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Date now = new Date();
|
|
|
+ log.info("Start checking orders for notification at {}", now);
|
|
|
+
|
|
|
+ //查询预约过学校场地的用户并通知他
|
|
|
+// iAppOrderService.findBySite();
|
|
|
+
|
|
|
+ log.info("Finished checking orders for notification");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Error occurred while checking orders for notification", e);
|
|
|
+ } finally {
|
|
|
+ // 释放锁
|
|
|
+ if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
|
|
|
+ redisTemplate.delete(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 延课通知
|
|
|
+ */
|
|
|
+ public void extendedClassesJob(List<JobExtendedClassNoticeVo> jobExtendedClassNoticeVos) {
|
|
|
+ String lockKey = "site:notification:lock";
|
|
|
+ String lockValue = String.valueOf(System.currentTimeMillis());
|
|
|
+ try {
|
|
|
+ // 获取分布式锁,防止多实例重复执行
|
|
|
+ Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 4, TimeUnit.MINUTES);
|
|
|
+ if (locked == null || !locked) {
|
|
|
+ log.debug("Failed to acquire notification lock, skipping this execution");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Date now = new Date();
|
|
|
+ log.info("Start checking orders for notification at {}", now);
|
|
|
+ ExecutorService executor = Executors.newFixedThreadPool(10);
|
|
|
+ for (JobExtendedClassNoticeVo jobExtendedClassNoticeVo : jobExtendedClassNoticeVos) {
|
|
|
+ NotificationRequest notificationRequest = new NotificationRequest();
|
|
|
+ notificationRequest.setOpenid(jobExtendedClassNoticeVo.getUserOpenId());
|
|
|
+ notificationRequest.setTemplateId("NPlIBEwBDPX23J3Jip0CwYUU4vF9ZlcK8U1d6Gs4yrM");
|
|
|
+ JSONObject data = new JSONObject();
|
|
|
+ // 根据模板内容设置数据项
|
|
|
+ data.put("thing2", new JSONObject().fluentPut("value", jobExtendedClassNoticeVo.getLassHourTime()));
|
|
|
+ data.put("thing1", new JSONObject().fluentPut("value", jobExtendedClassNoticeVo.getSiteName()));
|
|
|
+ data.put("thing3", new JSONObject().fluentPut("value", jobExtendedClassNoticeVo.getReasonClassExtension()));
|
|
|
+ executor.submit(() -> {
|
|
|
+ try {
|
|
|
+ notificationService.sendSubscribeMessage(notificationRequest);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("延课批量发送小程序订阅消息异常", e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+ //查询被延课的数据通知他
|
|
|
+// iAppOrderService.findBySite();
|
|
|
+
|
|
|
+ log.info("Finished checking orders for notification");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Error occurred while checking orders for notification", e);
|
|
|
+ } finally {
|
|
|
+ // 释放锁
|
|
|
+ if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
|
|
|
+ redisTemplate.delete(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private void processNotificationForTimeWindow(Date now, Integer hoursBefore) {
|
|
|
+ long windowStart = now.getTime();
|
|
|
+ long windowEnd = now.getTime() + hoursBefore * 3600 * 1000;
|
|
|
+ ;
|
|
|
+ String logPrefix = hoursBefore + "h notification";
|
|
|
+
|
|
|
+ log.info("Processing {} for time window {} - {}", logPrefix, new Date(windowStart), new Date(windowEnd));
|
|
|
+
|
|
|
+ // 查询需要通知的订单(未通知或重试次数未达上限)
|
|
|
+ List<JobClassNoticeVo> list = iAppOrderService.findByJob(hoursBefore,new Date(windowEnd),maxRetry);
|
|
|
+ if (list.isEmpty()) {
|
|
|
+ log.debug("No orders found for {}", logPrefix);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("Found {} orders for {}", list.size(), logPrefix);
|
|
|
+ ExecutorService executor = Executors.newFixedThreadPool(10);
|
|
|
+ for (JobClassNoticeVo jobClassNoticeVo : list) {
|
|
|
+ try {
|
|
|
+ // 发送到消息队列异步处理
|
|
|
+ NotificationRequest notificationRequest = new NotificationRequest();
|
|
|
+ notificationRequest.setOpenid(jobClassNoticeVo.getUserOpenId());
|
|
|
+ notificationRequest.setTemplateId("Yi1Z1IKRwgF6-mpiFcOUTvavc4TUAsfsLynK_3Yu350");//上课时间模板
|
|
|
+ JSONObject data = new JSONObject();
|
|
|
+ String date = getDate(jobClassNoticeVo.getStartTime());
|
|
|
+ String siteName = null;
|
|
|
+ if (jobClassNoticeVo.getSiteName()!=null){
|
|
|
+ siteName= jobClassNoticeVo.getSiteName().substring(0, Math.min(jobClassNoticeVo.getSiteName().length(), 20));
|
|
|
+ }
|
|
|
+ // 根据模板内容设置数据项
|
|
|
+ data.put("time3", new JSONObject().fluentPut("value", date));
|
|
|
+ data.put("thing4", new JSONObject().fluentPut("value", siteName));
|
|
|
+ data.put("thing11", new JSONObject().fluentPut("value", "如有特殊情况不能上课,记得沟通延课"));
|
|
|
+ executor.submit(() -> {
|
|
|
+ AppOrder appOrder = iAppOrderService.getById(jobClassNoticeVo.getOrderId());
|
|
|
+ if (appOrder==null){
|
|
|
+ log.error("上课消息通知未查询到该订单:"+jobClassNoticeVo.getOrderNo()+",订单id:"+jobClassNoticeVo.getOrderId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ notificationService.sendSubscribeMessage(notificationRequest);
|
|
|
+ if (hoursBefore==24){
|
|
|
+ appOrder.setNotified24h(1);
|
|
|
+ }else if (hoursBefore==8){
|
|
|
+ appOrder.setNotified24h(1);
|
|
|
+ }
|
|
|
+ iAppOrderService.updateById(appOrder);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 更新重试次数
|
|
|
+ appOrder.setNotifyRetryCount(appOrder.getNotifyRetryCount() + 1);
|
|
|
+ iAppOrderService.updateById(appOrder);
|
|
|
+ log.error("批量发送小程序订阅消息异常", e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ log.debug("Sent notification task for order {} to", jobClassNoticeVo.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Failed to send notification task for order {}", jobClassNoticeVo.getOrderNo(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private String getDate(Date date){
|
|
|
+ if (date==null){
|
|
|
+ return "未知时间";
|
|
|
+ }
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
|
|
|
+ return sdf.format(date);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|