FlashDeliveryNotificationService.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package com.ruoyi.app.flashdelivery.service;
  2. import com.ruoyi.app.order.dto.OrderPushBodyDto;
  3. import com.ruoyi.app.push.BusinessPushProperties;
  4. import com.ruoyi.app.push.NativeRiderBatchPushService;
  5. import com.ruoyi.app.push.UserNotificationScene;
  6. import com.ruoyi.app.utils.PayPush;
  7. import com.ruoyi.app.utils.event.PushEventService;
  8. import com.ruoyi.common.core.domain.entity.SysDictData;
  9. import com.ruoyi.common.utils.DictUtils;
  10. import com.ruoyi.framework.manager.AsyncManager;
  11. import com.ruoyi.system.domain.InfoUser;
  12. import com.ruoyi.system.domain.RiderPosition;
  13. import com.ruoyi.system.domain.flash.FlashDeliveryOrder;
  14. import com.ruoyi.system.mapper.InfoUserMapper;
  15. import com.ruoyi.system.mapper.RiderPositionMapper;
  16. import org.slf4j.Logger;
  17. import org.slf4j.LoggerFactory;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.stereotype.Service;
  20. import org.springframework.transaction.support.TransactionSynchronization;
  21. import org.springframework.transaction.support.TransactionSynchronizationManager;
  22. import java.util.List;
  23. import java.util.TimerTask;
  24. import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.ACCEPTED;
  25. import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.CANCELLED;
  26. import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.DELIVERED;
  27. import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.PICKED_UP;
  28. import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.WAITING_ACCEPTANCE;
  29. /**
  30. * 闪送订单消息推送:可抢新单推符合条件的骑手,履约事件推寄件用户,取消事件推接单骑手。
  31. * 推送统一在事务提交后异步执行,失败仅记日志,不影响订单状态流转本身。
  32. */
  33. @Service
  34. public class FlashDeliveryNotificationService {
  35. private static final Logger log = LoggerFactory.getLogger(FlashDeliveryNotificationService.class);
  36. private static final String TITLE_KEY = "no.message.push.message";
  37. private static final String CONTENT_ACCEPTED = "no.message.push.delivery.personnel.receiving.order";
  38. private static final String CONTENT_PICKED_UP = "no.message.push.delivery.personnel.qspsz.order";
  39. private static final String CONTENT_DELIVERED = "no.message.push.delivery.personnel.qsysd.order";
  40. private static final String CONTENT_CANCELLED = "no.message.push.order.cancelled";
  41. private static final String CONTENT_AVAILABLE = "no.message.push.new.flash.order";
  42. private static final int RIDER_NOTIFICATION_LIMIT = 20;
  43. /** 推送 payload 的业务类型:1=外卖、2=充值,3=闪送。 */
  44. private static final int PUSH_TYPE_FLASH = 3;
  45. /** -1 表示非语音提醒;0 表示骑手新单提醒。 */
  46. private static final int PUSH_PAYLOAD_TYPE_NONE = -1;
  47. private final InfoUserMapper userMapper;
  48. private final PushEventService pushEventService;
  49. private final RiderPositionMapper riderPositionMapper;
  50. private final BusinessPushProperties pushProperties;
  51. private final NativeRiderBatchPushService nativeRiderBatch;
  52. public FlashDeliveryNotificationService(InfoUserMapper userMapper, PushEventService pushEventService) {
  53. this(userMapper, pushEventService, null);
  54. }
  55. public FlashDeliveryNotificationService(InfoUserMapper userMapper, PushEventService pushEventService,
  56. RiderPositionMapper riderPositionMapper) {
  57. this(userMapper, pushEventService, riderPositionMapper, null, null);
  58. }
  59. @Autowired
  60. public FlashDeliveryNotificationService(InfoUserMapper userMapper, PushEventService pushEventService,
  61. RiderPositionMapper riderPositionMapper,
  62. BusinessPushProperties pushProperties,
  63. NativeRiderBatchPushService nativeRiderBatch) {
  64. this.userMapper = userMapper;
  65. this.pushEventService = pushEventService;
  66. this.riderPositionMapper = riderPositionMapper;
  67. this.pushProperties = pushProperties;
  68. this.nativeRiderBatch = nativeRiderBatch;
  69. }
  70. /** 按订单取寄件用户;订单或用户缺失时静默跳过。 */
  71. InfoUser senderOf(FlashDeliveryOrder order) {
  72. if (order == null || order.getUserId() == null) return null;
  73. return userMapper.selectById(order.getUserId());
  74. }
  75. /** 按订单取接单骑手;未接单(riderId 为空)或骑手缺失时静默跳过。 */
  76. InfoUser riderOf(FlashDeliveryOrder order) {
  77. if (order == null || order.getRiderId() == null) return null;
  78. return userMapper.selectById(order.getRiderId());
  79. }
  80. /** 向寄件用户推送指定状态文案;消息入库由通道内 PublisherEvent 完成,cid 为空时仅入库。 */
  81. void sendToUser(InfoUser recipient, FlashDeliveryOrder order, String status, String contentKey,
  82. UserNotificationScene scene) {
  83. if (recipient == null || recipient.getUserId() == null) return;
  84. String orderNo = order.getOrderNo();
  85. // 仅用户履约消息带场景;保持闪送 pushType=3 与原有 type/state。
  86. String body = OrderPushBodyDto.getUserJson(orderNo, status, PUSH_PAYLOAD_TYPE_NONE, PUSH_TYPE_FLASH, scene);
  87. AsyncManager.me().execute(new TimerTask() {
  88. @Override
  89. public void run() {
  90. try {
  91. PayPush.userPushHandleLocal(new PayPush(), pushEventService, recipient.getUserId(),
  92. recipient.getCid(), TITLE_KEY, contentKey, body, "", orderNo);
  93. } catch (Exception e) {
  94. log.error("闪送用户推送失败,userId:{}, orderNo:{}", recipient.getUserId(), orderNo, e);
  95. }
  96. }
  97. });
  98. }
  99. /** 向接单骑手推送指定状态文案;与用户端一致,失败仅记日志。 */
  100. void sendToRider(InfoUser recipient, FlashDeliveryOrder order, String status, String contentKey) {
  101. sendToRider(recipient, order, status, contentKey, PUSH_PAYLOAD_TYPE_NONE);
  102. }
  103. private void sendToRider(InfoUser recipient, FlashDeliveryOrder order, String status,
  104. String contentKey, int payloadType) {
  105. if (recipient == null || recipient.getUserId() == null) return;
  106. String orderNo = order.getOrderNo();
  107. String body = OrderPushBodyDto.getJson(orderNo, status, payloadType, PUSH_TYPE_FLASH);
  108. AsyncManager.me().execute(new TimerTask() {
  109. @Override
  110. public void run() {
  111. try {
  112. PayPush.qsPushHandleLocal(new PayPush(), pushEventService, recipient.getUserId(),
  113. recipient.getCid(), TITLE_KEY, contentKey, body, "", orderNo);
  114. } catch (Exception e) {
  115. log.error("闪送骑手推送失败,riderId:{}, orderNo:{}", recipient.getUserId(), orderNo, e);
  116. }
  117. }
  118. });
  119. }
  120. /** 向一名可接单骑手发送闪送新单提醒;type=0 保留骑手端新单语音语义。 */
  121. void sendAvailableOrderToRider(RiderPosition rider, FlashDeliveryOrder order) {
  122. if (rider == null || rider.getRiderId() == null) return;
  123. InfoUser recipient = new InfoUser();
  124. recipient.setUserId(rider.getRiderId());
  125. recipient.setCid(rider.getCid());
  126. sendToRider(recipient, order, WAITING_ACCEPTANCE, CONTENT_AVAILABLE, 0);
  127. }
  128. /** 订单已向骑手开放后,通知附近最多 20 名符合闪送接单条件的骑手。 */
  129. public void notifyAvailable(FlashDeliveryOrder order) {
  130. if (order == null || riderPositionMapper == null || !WAITING_ACCEPTANCE.equals(order.getStatus())
  131. || !Boolean.TRUE.equals(order.getIsDisplay()) || order.getRiderId() != null
  132. || order.getPickupLongitude() == null || order.getPickupLatitude() == null) {
  133. return;
  134. }
  135. runAfterCommit(() -> {
  136. int vehicleType = Integer.valueOf(2).equals(order.getVehicleType()) ? 2 : 1;
  137. List<RiderPosition> riders = riderPositionMapper.getFlashRiderNotificationList(
  138. order.getPickupLongitude(), order.getPickupLatitude(), vehicleType,
  139. order.getDeliveryType(), newTaskDistanceLimit(), RIDER_NOTIFICATION_LIMIT);
  140. if (riders != null) {
  141. if (pushProperties != null && pushProperties.getMode() == BusinessPushProperties.Mode.NATIVE
  142. && nativeRiderBatch != null) {
  143. String orderNo = order.getOrderNo();
  144. String body = OrderPushBodyDto.getJson(orderNo, WAITING_ACCEPTANCE, 0, PUSH_TYPE_FLASH);
  145. nativeRiderBatch.sendNewOrder(riders, TITLE_KEY, CONTENT_AVAILABLE, body, orderNo,
  146. rider -> sendAvailableOrderToRider(rider, order));
  147. } else {
  148. riders.forEach(rider -> sendAvailableOrderToRider(rider, order));
  149. }
  150. }
  151. });
  152. }
  153. /** 骑手抢单成功后通知寄件用户;未解析到寄件用户时跳过。 */
  154. public void notifyAccepted(FlashDeliveryOrder order) {
  155. runAfterCommit(() -> {
  156. InfoUser sender = senderOf(order);
  157. if (sender != null) sendToUser(sender, order, ACCEPTED, CONTENT_ACCEPTED, UserNotificationScene.RIDER_ACCEPTED);
  158. });
  159. }
  160. /** 骑手取件成功后通知寄件用户;未解析到寄件用户时跳过。 */
  161. public void notifyPickedUp(FlashDeliveryOrder order) {
  162. runAfterCommit(() -> {
  163. InfoUser sender = senderOf(order);
  164. if (sender != null) sendToUser(sender, order, PICKED_UP, CONTENT_PICKED_UP, UserNotificationScene.ORDER_DELIVERING);
  165. });
  166. }
  167. /** 骑手送达后通知寄件用户;未解析到寄件用户时跳过。 */
  168. public void notifyDelivered(FlashDeliveryOrder order) {
  169. runAfterCommit(() -> {
  170. InfoUser sender = senderOf(order);
  171. if (sender != null) sendToUser(sender, order, DELIVERED, CONTENT_DELIVERED, UserNotificationScene.ORDER_DELIVERED);
  172. });
  173. }
  174. /** 订单取消后通知接单骑手;未接单(无骑手归属)或骑手缺失时跳过,寄件用户不推。 */
  175. public void notifyCancelled(FlashDeliveryOrder order) {
  176. runAfterCommit(() -> {
  177. InfoUser rider = riderOf(order);
  178. if (rider != null) sendToRider(rider, order, CANCELLED, CONTENT_CANCELLED);
  179. });
  180. }
  181. /** 仅事务提交后执行推送准备;无事务同步时(单测)直接执行。 */
  182. static void runAfterCommit(Runnable action) {
  183. // 查询收件人或投递异步任务失败同样属于通知失败,不改变已成功的履约结果。
  184. Runnable safeAction = () -> {
  185. try {
  186. action.run();
  187. } catch (Exception exception) {
  188. log.error("闪送通知准备失败", exception);
  189. }
  190. };
  191. if (!TransactionSynchronizationManager.isSynchronizationActive()) {
  192. safeAction.run();
  193. return;
  194. }
  195. TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  196. @Override
  197. public void afterCommit() {
  198. safeAction.run();
  199. }
  200. });
  201. }
  202. private Integer newTaskDistanceLimit() {
  203. try {
  204. List<SysDictData> values = DictUtils.getDictCache("sys_qs_newtask_distance");
  205. if (values != null && !values.isEmpty()) {
  206. int limit = Integer.parseInt(values.get(0).getDictValue());
  207. return limit > 0 ? limit : null;
  208. }
  209. } catch (Exception ignored) {
  210. // 与骑手新任务列表保持一致:缺失或非法配置时不限制距离。
  211. }
  212. return null;
  213. }
  214. }