NativeRiderBatchPushService.java 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package com.ruoyi.app.push;
  2. import com.ruoyi.app.utils.event.PushEventService;
  3. import com.ruoyi.common.utils.LocaleUtils;
  4. import com.ruoyi.common.utils.MessageUtils;
  5. import com.ruoyi.system.domain.InfoUser;
  6. import com.ruoyi.system.domain.RiderPosition;
  7. import com.ruoyi.system.service.IInfoUserService;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.scheduling.annotation.Async;
  11. import org.springframework.stereotype.Service;
  12. import java.util.ArrayList;
  13. import java.util.LinkedHashMap;
  14. import java.util.List;
  15. import java.util.Map;
  16. import java.util.function.Consumer;
  17. /** Batches identical Android rider alerts without changing the existing uni or iOS paths. */
  18. @Service
  19. public class NativeRiderBatchPushService {
  20. private static final Logger log = LoggerFactory.getLogger(NativeRiderBatchPushService.class);
  21. private final BusinessPushProperties properties;
  22. private final IInfoUserService users;
  23. private final FcmPushSender fcm;
  24. private final PushEventService events;
  25. public NativeRiderBatchPushService(BusinessPushProperties properties, IInfoUserService users,
  26. FcmPushSender fcm, PushEventService events) {
  27. this.properties = properties;
  28. this.users = users;
  29. this.fcm = fcm;
  30. this.events = events;
  31. }
  32. @Async("threadPoolTaskExecutor")
  33. public void sendNewOrder(List<RiderPosition> riders, String titleKey, String contentKey,
  34. String payload, String orderNo, Consumer<RiderPosition> perRiderFallback) {
  35. if (riders == null || riders.isEmpty()) {
  36. return;
  37. }
  38. Map<Content, List<RiderTarget>> groups = new LinkedHashMap<>();
  39. for (RiderPosition rider : riders) {
  40. if (rider == null || rider.getRiderId() == null) {
  41. continue;
  42. }
  43. InfoUser current;
  44. try {
  45. current = users.getById(rider.getRiderId());
  46. } catch (RuntimeException exception) {
  47. perRiderFallback.accept(rider);
  48. continue;
  49. }
  50. if (properties.getMode() != BusinessPushProperties.Mode.NATIVE || current == null
  51. || current.getCidType() == null || !"android".equalsIgnoreCase(current.getCidType().trim())
  52. || current.getDeviceToken() == null || current.getDeviceToken().isBlank()) {
  53. perRiderFallback.accept(rider);
  54. continue;
  55. }
  56. try {
  57. Long userId = rider.getRiderId();
  58. java.util.Locale locale = LocaleUtils.getUserLocale(userId);
  59. String title = MessageUtils.message(titleKey, locale);
  60. String content = MessageUtils.message(contentKey, locale) + ",NO:" + orderNo;
  61. events.PublisherEvent(userId, title, content, payload);
  62. groups.computeIfAbsent(new Content(title, content, payload), ignored -> new ArrayList<>())
  63. .add(new RiderTarget(userId, current.getDeviceToken()));
  64. } catch (RuntimeException exception) {
  65. log.warn("Native rider alert preparation failed, userId={}, category={}",
  66. rider.getRiderId(), exception.getClass().getSimpleName());
  67. }
  68. }
  69. for (Map.Entry<Content, List<RiderTarget>> entry : groups.entrySet()) {
  70. List<RiderTarget> targets = entry.getValue();
  71. Content content = entry.getKey();
  72. try {
  73. List<FcmPushSender.DeliveryResult> results = fcm.sendBatch(RecipientApp.RIDER,
  74. targets.stream().map(RiderTarget::token).toList(),
  75. content.title(), content.body(), content.payload());
  76. for (int index = 0; index < targets.size(); index++) {
  77. if (index >= results.size() || !results.get(index).successful()) {
  78. String category = index >= results.size() ? "RESULT_MISSING" : results.get(index).category();
  79. log.warn("Native rider alert failed, userId={}, category={}",
  80. targets.get(index).userId(), category);
  81. }
  82. }
  83. } catch (RuntimeException exception) {
  84. String message = exception.getMessage();
  85. String category = exception instanceof IllegalStateException && message != null
  86. && message.matches("FCM_[A-Z0-9_]{1,64}")
  87. ? message : exception.getClass().getSimpleName();
  88. for (RiderTarget target : targets) {
  89. log.warn("Native rider alert failed, userId={}, category={}", target.userId(), category);
  90. }
  91. }
  92. }
  93. }
  94. private record Content(String title, String body, String payload) { }
  95. private record RiderTarget(Long userId, String token) { }
  96. }