Commit f3db296ca46698c3171b15d6b739976e4b9d9837
1 parent
2449a3c0
完善订单Webhook事件通知:按订单应用定向回调,新增签名与失败重试机制,并补充后台指派/转单通知触发
Showing
8 changed files
with
127 additions
and
26 deletions
src/main/java/com/diligrp/rider/service/WebhookService.java
| 1 | package com.diligrp.rider.service; | 1 | package com.diligrp.rider.service; |
| 2 | 2 | ||
| 3 | +import com.diligrp.rider.entity.Orders; | ||
| 4 | + | ||
| 3 | /** | 5 | /** |
| 4 | * Webhook 推送服务 | 6 | * Webhook 推送服务 |
| 5 | * 订单状态变更时调用此服务通知接入方 | 7 | * 订单状态变更时调用此服务通知接入方 |
| @@ -13,6 +15,8 @@ public interface WebhookService { | @@ -13,6 +15,8 @@ public interface WebhookService { | ||
| 13 | */ | 15 | */ |
| 14 | void send(String event, Long bizId, String payload); | 16 | void send(String event, Long bizId, String payload); |
| 15 | 17 | ||
| 18 | + void sendOrderEvent(Orders order, String event, String payload); | ||
| 19 | + | ||
| 16 | /** 重试失败的 Webhook */ | 20 | /** 重试失败的 Webhook */ |
| 17 | void retry(Long logId); | 21 | void retry(Long logId); |
| 18 | } | 22 | } |
src/main/java/com/diligrp/rider/service/impl/AdminRiderServiceImpl.java
| @@ -17,8 +17,11 @@ import com.diligrp.rider.mapper.*; | @@ -17,8 +17,11 @@ import com.diligrp.rider.mapper.*; | ||
| 17 | import com.diligrp.rider.service.AdminRiderService; | 17 | import com.diligrp.rider.service.AdminRiderService; |
| 18 | import com.diligrp.rider.service.DeliveryOrderService; | 18 | import com.diligrp.rider.service.DeliveryOrderService; |
| 19 | import com.diligrp.rider.service.RiderHoldLimitService; | 19 | import com.diligrp.rider.service.RiderHoldLimitService; |
| 20 | +import com.diligrp.rider.service.WebhookService; | ||
| 20 | import com.diligrp.rider.vo.DeliveryOrderCreateVO; | 21 | import com.diligrp.rider.vo.DeliveryOrderCreateVO; |
| 22 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 21 | import lombok.RequiredArgsConstructor; | 23 | import lombok.RequiredArgsConstructor; |
| 24 | +import lombok.extern.slf4j.Slf4j; | ||
| 22 | import org.springframework.stereotype.Service; | 25 | import org.springframework.stereotype.Service; |
| 23 | import org.springframework.transaction.annotation.Transactional; | 26 | import org.springframework.transaction.annotation.Transactional; |
| 24 | import org.springframework.util.DigestUtils; | 27 | import org.springframework.util.DigestUtils; |
| @@ -31,6 +34,7 @@ import java.util.Map; | @@ -31,6 +34,7 @@ import java.util.Map; | ||
| 31 | import java.util.Set; | 34 | import java.util.Set; |
| 32 | import java.util.stream.Collectors; | 35 | import java.util.stream.Collectors; |
| 33 | 36 | ||
| 37 | +@Slf4j | ||
| 34 | @Service | 38 | @Service |
| 35 | @RequiredArgsConstructor | 39 | @RequiredArgsConstructor |
| 36 | public class AdminRiderServiceImpl implements AdminRiderService { | 40 | public class AdminRiderServiceImpl implements AdminRiderService { |
| @@ -42,6 +46,8 @@ public class AdminRiderServiceImpl implements AdminRiderService { | @@ -42,6 +46,8 @@ public class AdminRiderServiceImpl implements AdminRiderService { | ||
| 42 | private final AdminScopeGuard adminScopeGuard; | 46 | private final AdminScopeGuard adminScopeGuard; |
| 43 | private final DeliveryOrderService deliveryOrderService; | 47 | private final DeliveryOrderService deliveryOrderService; |
| 44 | private final RiderHoldLimitService riderHoldLimitService; | 48 | private final RiderHoldLimitService riderHoldLimitService; |
| 49 | + private final WebhookService webhookService; | ||
| 50 | + private final ObjectMapper objectMapper; | ||
| 45 | 51 | ||
| 46 | @Override | 52 | @Override |
| 47 | public void add(AdminRiderAddDTO dto, Long cityId) { | 53 | public void add(AdminRiderAddDTO dto, Long cityId) { |
| @@ -221,6 +227,7 @@ public class AdminRiderServiceImpl implements AdminRiderService { | @@ -221,6 +227,7 @@ public class AdminRiderServiceImpl implements AdminRiderService { | ||
| 221 | } | 227 | } |
| 222 | int updated = ordersMapper.update(null, wrapper); | 228 | int updated = ordersMapper.update(null, wrapper); |
| 223 | if (updated == 0) throw new BizException("指派失败,请重试"); | 229 | if (updated == 0) throw new BizException("指派失败,请重试"); |
| 230 | + notifyOrderEvent(orderId, "order.dispatched"); | ||
| 224 | } | 231 | } |
| 225 | 232 | ||
| 226 | @Override | 233 | @Override |
| @@ -248,6 +255,9 @@ public class AdminRiderServiceImpl implements AdminRiderService { | @@ -248,6 +255,9 @@ public class AdminRiderServiceImpl implements AdminRiderService { | ||
| 248 | } | 255 | } |
| 249 | int updated = ordersMapper.update(null, wrapper); | 256 | int updated = ordersMapper.update(null, wrapper); |
| 250 | if (updated == 0) throw new BizException("操作失败,请重试"); | 257 | if (updated == 0) throw new BizException("操作失败,请重试"); |
| 258 | + if (trans == 1) { | ||
| 259 | + notifyOrderEvent(orderId, "order.transferred"); | ||
| 260 | + } | ||
| 251 | } | 261 | } |
| 252 | 262 | ||
| 253 | @Override | 263 | @Override |
| @@ -266,6 +276,24 @@ public class AdminRiderServiceImpl implements AdminRiderService { | @@ -266,6 +276,24 @@ public class AdminRiderServiceImpl implements AdminRiderService { | ||
| 266 | deliveryOrderService.cancelByAdmin(order); | 276 | deliveryOrderService.cancelByAdmin(order); |
| 267 | } | 277 | } |
| 268 | 278 | ||
| 279 | + private void notifyOrderEvent(Long orderId, String event) { | ||
| 280 | + try { | ||
| 281 | + Orders order = ordersMapper.selectById(orderId); | ||
| 282 | + if (order == null || order.getAppKey() == null || order.getAppKey().isBlank()) return; | ||
| 283 | + Map<String, Object> payload = new HashMap<>(); | ||
| 284 | + payload.put("event", event); | ||
| 285 | + payload.put("outOrderNo", order.getOutOrderNo()); | ||
| 286 | + payload.put("deliveryOrderId", order.getId()); | ||
| 287 | + payload.put("orderNo", order.getOrderNo()); | ||
| 288 | + payload.put("status", order.getStatus()); | ||
| 289 | + payload.put("riderId", order.getRiderId()); | ||
| 290 | + payload.put("timestamp", System.currentTimeMillis() / 1000); | ||
| 291 | + webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); | ||
| 292 | + } catch (Exception e) { | ||
| 293 | + log.warn("后台订单事件通知失败 orderId={} event={}", orderId, event, e); | ||
| 294 | + } | ||
| 295 | + } | ||
| 296 | + | ||
| 269 | private String encryptPass(String pass) { | 297 | private String encryptPass(String pass) { |
| 270 | return DigestUtils.md5DigestAsHex(pass.getBytes(StandardCharsets.UTF_8)); | 298 | return DigestUtils.md5DigestAsHex(pass.getBytes(StandardCharsets.UTF_8)); |
| 271 | } | 299 | } |
src/main/java/com/diligrp/rider/service/impl/DeliveryOrderServiceImpl.java
| @@ -303,7 +303,7 @@ public class DeliveryOrderServiceImpl implements DeliveryOrderService { | @@ -303,7 +303,7 @@ public class DeliveryOrderServiceImpl implements DeliveryOrderService { | ||
| 303 | payload.put("status", order.getStatus()); | 303 | payload.put("status", order.getStatus()); |
| 304 | payload.put("timestamp", System.currentTimeMillis() / 1000); | 304 | payload.put("timestamp", System.currentTimeMillis() / 1000); |
| 305 | String json = objectMapper.writeValueAsString(payload); | 305 | String json = objectMapper.writeValueAsString(payload); |
| 306 | - webhookService.send(event, order.getId(), json); | 306 | + webhookService.sendOrderEvent(order, event, json); |
| 307 | } catch (Exception e) { | 307 | } catch (Exception e) { |
| 308 | log.warn("通知回调失败 orderId={}", order.getId(), e); | 308 | log.warn("通知回调失败 orderId={}", order.getId(), e); |
| 309 | } | 309 | } |
src/main/java/com/diligrp/rider/service/impl/DispatchServiceImpl.java
| @@ -346,7 +346,7 @@ public class DispatchServiceImpl implements DispatchService { | @@ -346,7 +346,7 @@ public class DispatchServiceImpl implements DispatchService { | ||
| 346 | payload.put("riderId", order.getRiderId()); | 346 | payload.put("riderId", order.getRiderId()); |
| 347 | payload.put("dispatchRiderId", order.getDispatchRiderId()); | 347 | payload.put("dispatchRiderId", order.getDispatchRiderId()); |
| 348 | payload.put("timestamp", System.currentTimeMillis() / 1000); | 348 | payload.put("timestamp", System.currentTimeMillis() / 1000); |
| 349 | - webhookService.send(event, orderId, objectMapper.writeValueAsString(payload)); | 349 | + webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); |
| 350 | } catch (Exception e) { | 350 | } catch (Exception e) { |
| 351 | log.warn("派单事件通知失败 orderId={} event={}", orderId, event, e); | 351 | log.warn("派单事件通知失败 orderId={} event={}", orderId, event, e); |
| 352 | } | 352 | } |
src/main/java/com/diligrp/rider/service/impl/RefundServiceImpl.java
| @@ -70,6 +70,7 @@ public class RefundServiceImpl implements RefundService { | @@ -70,6 +70,7 @@ public class RefundServiceImpl implements RefundService { | ||
| 70 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() | 70 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() |
| 71 | .eq(Orders::getId, orderId) | 71 | .eq(Orders::getId, orderId) |
| 72 | .set(Orders::getStatus, 7)); | 72 | .set(Orders::getStatus, 7)); |
| 73 | + order.setStatus(7); | ||
| 73 | 74 | ||
| 74 | // 写退款记录 | 75 | // 写退款记录 |
| 75 | OrderRefundRecord record = new OrderRefundRecord(); | 76 | OrderRefundRecord record = new OrderRefundRecord(); |
| @@ -115,12 +116,14 @@ public class RefundServiceImpl implements RefundService { | @@ -115,12 +116,14 @@ public class RefundServiceImpl implements RefundService { | ||
| 115 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() | 116 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() |
| 116 | .eq(Orders::getId, record.getOid()) | 117 | .eq(Orders::getId, record.getOid()) |
| 117 | .set(Orders::getStatus, 8)); | 118 | .set(Orders::getStatus, 8)); |
| 119 | + order.setStatus(8); | ||
| 118 | notifyRefundEvent(order, "order.refund_success"); | 120 | notifyRefundEvent(order, "order.refund_success"); |
| 119 | } else { | 121 | } else { |
| 120 | // 退款拒绝 → 订单状态改为退款拒绝 | 122 | // 退款拒绝 → 订单状态改为退款拒绝 |
| 121 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() | 123 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() |
| 122 | .eq(Orders::getId, record.getOid()) | 124 | .eq(Orders::getId, record.getOid()) |
| 123 | .set(Orders::getStatus, 9)); | 125 | .set(Orders::getStatus, 9)); |
| 126 | + order.setStatus(9); | ||
| 124 | notifyRefundEvent(order, "order.refund_reject"); | 127 | notifyRefundEvent(order, "order.refund_reject"); |
| 125 | } | 128 | } |
| 126 | } | 129 | } |
| @@ -148,7 +151,7 @@ public class RefundServiceImpl implements RefundService { | @@ -148,7 +151,7 @@ public class RefundServiceImpl implements RefundService { | ||
| 148 | payload.put("orderNo", order.getOrderNo()); | 151 | payload.put("orderNo", order.getOrderNo()); |
| 149 | payload.put("status", order.getStatus()); | 152 | payload.put("status", order.getStatus()); |
| 150 | payload.put("timestamp", System.currentTimeMillis() / 1000); | 153 | payload.put("timestamp", System.currentTimeMillis() / 1000); |
| 151 | - webhookService.send(event, order.getId(), objectMapper.writeValueAsString(payload)); | 154 | + webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); |
| 152 | } catch (Exception e) { | 155 | } catch (Exception e) { |
| 153 | log.warn("退款事件通知失败 orderId={}", order.getId(), e); | 156 | log.warn("退款事件通知失败 orderId={}", order.getId(), e); |
| 154 | } | 157 | } |
src/main/java/com/diligrp/rider/service/impl/RiderOrderServiceImpl.java
| @@ -256,7 +256,7 @@ public class RiderOrderServiceImpl implements RiderOrderService { | @@ -256,7 +256,7 @@ public class RiderOrderServiceImpl implements RiderOrderService { | ||
| 256 | payload.put("status", order.getStatus()); | 256 | payload.put("status", order.getStatus()); |
| 257 | payload.put("riderId", order.getRiderId()); | 257 | payload.put("riderId", order.getRiderId()); |
| 258 | payload.put("timestamp", System.currentTimeMillis() / 1000); | 258 | payload.put("timestamp", System.currentTimeMillis() / 1000); |
| 259 | - webhookService.send(event, orderId, objectMapper.writeValueAsString(payload)); | 259 | + webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); |
| 260 | } catch (Exception e) { | 260 | } catch (Exception e) { |
| 261 | log.warn("订单事件通知失败 orderId={} event={}", orderId, event, e); | 261 | log.warn("订单事件通知失败 orderId={} event={}", orderId, event, e); |
| 262 | } | 262 | } |
| @@ -414,7 +414,7 @@ public class RiderOrderServiceImpl implements RiderOrderService { | @@ -414,7 +414,7 @@ public class RiderOrderServiceImpl implements RiderOrderService { | ||
| 414 | 414 | ||
| 415 | if (order.getStatus() == 3) { | 415 | if (order.getStatus() == 3) { |
| 416 | // status=3(已接单,未取货):直接回抢单池,无需审批 | 416 | // status=3(已接单,未取货):直接回抢单池,无需审批 |
| 417 | - ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() | 417 | + int updated = ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() |
| 418 | .eq(Orders::getId, orderId) | 418 | .eq(Orders::getId, orderId) |
| 419 | .eq(Orders::getRiderId, riderId) | 419 | .eq(Orders::getRiderId, riderId) |
| 420 | .eq(Orders::getStatus, 3) | 420 | .eq(Orders::getStatus, 3) |
| @@ -426,6 +426,8 @@ public class RiderOrderServiceImpl implements RiderOrderService { | @@ -426,6 +426,8 @@ public class RiderOrderServiceImpl implements RiderOrderService { | ||
| 426 | .set(Orders::getRiderIncome, BigDecimal.ZERO) | 426 | .set(Orders::getRiderIncome, BigDecimal.ZERO) |
| 427 | .set(Orders::getSubstationIncome, BigDecimal.ZERO) | 427 | .set(Orders::getSubstationIncome, BigDecimal.ZERO) |
| 428 | .set(Orders::getTransTime, now)); | 428 | .set(Orders::getTransTime, now)); |
| 429 | + if (updated == 0) throw new BizException(980, "操作失败"); | ||
| 430 | + notifyOrderEvent(orderId, "order.transferred"); | ||
| 429 | } else { | 431 | } else { |
| 430 | // status=4(取货后配送中):需分站审批,设为申请中 | 432 | // status=4(取货后配送中):需分站审批,设为申请中 |
| 431 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() | 433 | ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() |
src/main/java/com/diligrp/rider/service/impl/WebhookServiceImpl.java
| @@ -3,13 +3,17 @@ package com.diligrp.rider.service.impl; | @@ -3,13 +3,17 @@ package com.diligrp.rider.service.impl; | ||
| 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | import com.fasterxml.jackson.databind.ObjectMapper; | 4 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 5 | import com.diligrp.rider.entity.OpenApp; | 5 | import com.diligrp.rider.entity.OpenApp; |
| 6 | +import com.diligrp.rider.entity.Orders; | ||
| 6 | import com.diligrp.rider.entity.WebhookLog; | 7 | import com.diligrp.rider.entity.WebhookLog; |
| 7 | import com.diligrp.rider.mapper.OpenAppMapper; | 8 | import com.diligrp.rider.mapper.OpenAppMapper; |
| 9 | +import com.diligrp.rider.mapper.OrdersMapper; | ||
| 8 | import com.diligrp.rider.mapper.WebhookLogMapper; | 10 | import com.diligrp.rider.mapper.WebhookLogMapper; |
| 9 | import com.diligrp.rider.service.WebhookService; | 11 | import com.diligrp.rider.service.WebhookService; |
| 12 | +import com.diligrp.rider.util.SignUtil; | ||
| 10 | import lombok.RequiredArgsConstructor; | 13 | import lombok.RequiredArgsConstructor; |
| 11 | import lombok.extern.slf4j.Slf4j; | 14 | import lombok.extern.slf4j.Slf4j; |
| 12 | import org.springframework.scheduling.annotation.Async; | 15 | import org.springframework.scheduling.annotation.Async; |
| 16 | +import org.springframework.scheduling.annotation.Scheduled; | ||
| 13 | import org.springframework.stereotype.Service; | 17 | import org.springframework.stereotype.Service; |
| 14 | 18 | ||
| 15 | import java.net.URI; | 19 | import java.net.URI; |
| @@ -18,6 +22,7 @@ import java.net.http.HttpRequest; | @@ -18,6 +22,7 @@ import java.net.http.HttpRequest; | ||
| 18 | import java.net.http.HttpResponse; | 22 | import java.net.http.HttpResponse; |
| 19 | import java.time.Duration; | 23 | import java.time.Duration; |
| 20 | import java.util.List; | 24 | import java.util.List; |
| 25 | +import java.util.UUID; | ||
| 21 | 26 | ||
| 22 | @Slf4j | 27 | @Slf4j |
| 23 | @Service | 28 | @Service |
| @@ -25,6 +30,7 @@ import java.util.List; | @@ -25,6 +30,7 @@ import java.util.List; | ||
| 25 | public class WebhookServiceImpl implements WebhookService { | 30 | public class WebhookServiceImpl implements WebhookService { |
| 26 | 31 | ||
| 27 | private final OpenAppMapper openAppMapper; | 32 | private final OpenAppMapper openAppMapper; |
| 33 | + private final OrdersMapper ordersMapper; | ||
| 28 | private final WebhookLogMapper webhookLogMapper; | 34 | private final WebhookLogMapper webhookLogMapper; |
| 29 | private final ObjectMapper objectMapper; | 35 | private final ObjectMapper objectMapper; |
| 30 | 36 | ||
| @@ -35,47 +41,102 @@ public class WebhookServiceImpl implements WebhookService { | @@ -35,47 +41,102 @@ public class WebhookServiceImpl implements WebhookService { | ||
| 35 | @Override | 41 | @Override |
| 36 | @Async | 42 | @Async |
| 37 | public void send(String event, Long bizId, String payload) { | 43 | public void send(String event, Long bizId, String payload) { |
| 38 | - // 查找订阅该事件的所有应用 | ||
| 39 | - List<OpenApp> apps = openAppMapper.selectList( | ||
| 40 | - new LambdaQueryWrapper<OpenApp>().eq(OpenApp::getStatus, 1)); | ||
| 41 | - | ||
| 42 | - for (OpenApp app : apps) { | ||
| 43 | - if (app.getWebhookUrl() == null || app.getWebhookUrl().isBlank()) continue; | ||
| 44 | - if (!isSubscribed(app.getWebhookEvents(), event)) continue; | ||
| 45 | - doSend(app, event, bizId, payload, 0); | 44 | + Orders order = ordersMapper.selectById(bizId); |
| 45 | + if (order == null) { | ||
| 46 | + log.warn("Webhook 发送跳过,订单不存在 bizId={} event={}", bizId, event); | ||
| 47 | + return; | ||
| 46 | } | 48 | } |
| 49 | + sendOrderEvent(order, event, payload); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Override | ||
| 53 | + @Async | ||
| 54 | + public void sendOrderEvent(Orders order, String event, String payload) { | ||
| 55 | + if (order == null || order.getAppKey() == null || order.getAppKey().isBlank()) return; | ||
| 56 | + OpenApp app = openAppMapper.selectOne(new LambdaQueryWrapper<OpenApp>() | ||
| 57 | + .eq(OpenApp::getAppKey, order.getAppKey()) | ||
| 58 | + .eq(OpenApp::getStatus, 1) | ||
| 59 | + .last("LIMIT 1")); | ||
| 60 | + if (app == null) return; | ||
| 61 | + if (!isSubscribed(app.getWebhookEvents(), event)) return; | ||
| 62 | + | ||
| 63 | + String url = order.getCallbackUrl(); | ||
| 64 | + if (url == null || url.isBlank()) { | ||
| 65 | + url = app.getWebhookUrl(); | ||
| 66 | + } | ||
| 67 | + if (url == null || url.isBlank()) return; | ||
| 68 | + | ||
| 69 | + doSend(app, url, event, order.getId(), payload, 0); | ||
| 47 | } | 70 | } |
| 48 | 71 | ||
| 49 | @Override | 72 | @Override |
| 50 | public void retry(Long logId) { | 73 | public void retry(Long logId) { |
| 51 | WebhookLog log = webhookLogMapper.selectById(logId); | 74 | WebhookLog log = webhookLogMapper.selectById(logId); |
| 52 | if (log == null || log.getStatus() == 1) return; | 75 | if (log == null || log.getStatus() == 1) return; |
| 53 | - OpenApp app = openAppMapper.selectById(log.getAppId()); | 76 | + retry(log); |
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Scheduled(fixedDelay = 300_000) | ||
| 80 | + public void retryFailedWebhooks() { | ||
| 81 | + List<WebhookLog> logs = webhookLogMapper.selectList(new LambdaQueryWrapper<WebhookLog>() | ||
| 82 | + .eq(WebhookLog::getStatus, 0) | ||
| 83 | + .lt(WebhookLog::getRetryCount, 5) | ||
| 84 | + .orderByAsc(WebhookLog::getCreateTime) | ||
| 85 | + .last("LIMIT 20")); | ||
| 86 | + for (WebhookLog log : logs) { | ||
| 87 | + retry(log); | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + private void retry(WebhookLog webhookLog) { | ||
| 92 | + OpenApp app = openAppMapper.selectById(webhookLog.getAppId()); | ||
| 54 | if (app == null) return; | 93 | if (app == null) return; |
| 55 | - doSend(app, log.getEvent(), log.getBizId(), log.getPayload(), log.getRetryCount() + 1); | 94 | + String url = webhookLog.getUrl(); |
| 95 | + if (url == null || url.isBlank()) { | ||
| 96 | + url = app.getWebhookUrl(); | ||
| 97 | + } | ||
| 98 | + if (url == null || url.isBlank()) return; | ||
| 99 | + SendResult result = sendHttp(app, url, webhookLog.getEvent(), webhookLog.getPayload()); | ||
| 100 | + webhookLog.setUrl(url); | ||
| 101 | + webhookLog.setResponseCode(result.responseCode()); | ||
| 102 | + webhookLog.setResponseBody(trimResponseBody(result.responseBody())); | ||
| 103 | + webhookLog.setStatus(result.status()); | ||
| 104 | + webhookLog.setRetryCount((webhookLog.getRetryCount() == null ? 0 : webhookLog.getRetryCount()) + 1); | ||
| 105 | + webhookLogMapper.updateById(webhookLog); | ||
| 56 | } | 106 | } |
| 57 | 107 | ||
| 58 | - private void doSend(OpenApp app, String event, Long bizId, String payload, int retryCount) { | 108 | + private void doSend(OpenApp app, String url, String event, Long bizId, String payload, int retryCount) { |
| 109 | + SendResult result = sendHttp(app, url, event, payload); | ||
| 59 | WebhookLog webhookLog = new WebhookLog(); | 110 | WebhookLog webhookLog = new WebhookLog(); |
| 60 | webhookLog.setAppId(app.getId()); | 111 | webhookLog.setAppId(app.getId()); |
| 61 | webhookLog.setEvent(event); | 112 | webhookLog.setEvent(event); |
| 62 | webhookLog.setBizId(bizId); | 113 | webhookLog.setBizId(bizId); |
| 63 | - webhookLog.setUrl(app.getWebhookUrl()); | 114 | + webhookLog.setUrl(url); |
| 64 | webhookLog.setPayload(payload); | 115 | webhookLog.setPayload(payload); |
| 65 | webhookLog.setRetryCount(retryCount); | 116 | webhookLog.setRetryCount(retryCount); |
| 66 | webhookLog.setCreateTime(System.currentTimeMillis() / 1000); | 117 | webhookLog.setCreateTime(System.currentTimeMillis() / 1000); |
| 118 | + webhookLog.setResponseCode(result.responseCode()); | ||
| 119 | + webhookLog.setResponseBody(trimResponseBody(result.responseBody())); | ||
| 120 | + webhookLog.setStatus(result.status()); | ||
| 121 | + webhookLogMapper.insert(webhookLog); | ||
| 122 | + } | ||
| 67 | 123 | ||
| 124 | + private SendResult sendHttp(OpenApp app, String url, String event, String payload) { | ||
| 68 | int responseCode = 0; | 125 | int responseCode = 0; |
| 69 | String responseBody = ""; | 126 | String responseBody = ""; |
| 70 | int status = 0; | 127 | int status = 0; |
| 71 | 128 | ||
| 72 | try { | 129 | try { |
| 130 | + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); | ||
| 131 | + String nonce = UUID.randomUUID().toString().replace("-", ""); | ||
| 73 | HttpRequest request = HttpRequest.newBuilder() | 132 | HttpRequest request = HttpRequest.newBuilder() |
| 74 | - .uri(URI.create(app.getWebhookUrl())) | 133 | + .uri(URI.create(url)) |
| 75 | .header("Content-Type", "application/json") | 134 | .header("Content-Type", "application/json") |
| 76 | .header("X-App-Key", app.getAppKey()) | 135 | .header("X-App-Key", app.getAppKey()) |
| 77 | .header("X-Event", event) | 136 | .header("X-Event", event) |
| 78 | - .header("X-Timestamp", String.valueOf(System.currentTimeMillis() / 1000)) | 137 | + .header("X-Timestamp", timestamp) |
| 138 | + .header("X-Nonce", nonce) | ||
| 139 | + .header("X-Sign", SignUtil.sign(app.getAppKey(), timestamp, nonce, app.getAppSecret())) | ||
| 79 | .POST(HttpRequest.BodyPublishers.ofString(payload)) | 140 | .POST(HttpRequest.BodyPublishers.ofString(payload)) |
| 80 | .timeout(Duration.ofSeconds(10)) | 141 | .timeout(Duration.ofSeconds(10)) |
| 81 | .build(); | 142 | .build(); |
| @@ -83,19 +144,22 @@ public class WebhookServiceImpl implements WebhookService { | @@ -83,19 +144,22 @@ public class WebhookServiceImpl implements WebhookService { | ||
| 83 | HttpResponse<String> response = HTTP_CLIENT.send(request, | 144 | HttpResponse<String> response = HTTP_CLIENT.send(request, |
| 84 | HttpResponse.BodyHandlers.ofString()); | 145 | HttpResponse.BodyHandlers.ofString()); |
| 85 | responseCode = response.statusCode(); | 146 | responseCode = response.statusCode(); |
| 86 | - responseBody = response.body(); | 147 | + responseBody = response.body() == null ? "" : response.body(); |
| 87 | if (responseCode == 200) status = 1; | 148 | if (responseCode == 200) status = 1; |
| 88 | } catch (Exception e) { | 149 | } catch (Exception e) { |
| 89 | log.warn("Webhook 发送失败 appId={} event={} err={}", app.getId(), event, e.getMessage()); | 150 | log.warn("Webhook 发送失败 appId={} event={} err={}", app.getId(), event, e.getMessage()); |
| 90 | - responseBody = e.getMessage(); | 151 | + responseBody = e.getMessage() == null ? "" : e.getMessage(); |
| 91 | } | 152 | } |
| 153 | + return new SendResult(responseCode, responseBody, status); | ||
| 154 | + } | ||
| 92 | 155 | ||
| 93 | - webhookLog.setResponseCode(responseCode); | ||
| 94 | - webhookLog.setResponseBody(responseBody.length() > 500 ? responseBody.substring(0, 500) : responseBody); | ||
| 95 | - webhookLog.setStatus(status); | ||
| 96 | - webhookLogMapper.insert(webhookLog); | 156 | + private String trimResponseBody(String responseBody) { |
| 157 | + if (responseBody == null) return ""; | ||
| 158 | + return responseBody.length() > 500 ? responseBody.substring(0, 500) : responseBody; | ||
| 97 | } | 159 | } |
| 98 | 160 | ||
| 161 | + private record SendResult(int responseCode, String responseBody, int status) {} | ||
| 162 | + | ||
| 99 | /** 检查应用是否订阅了某事件 */ | 163 | /** 检查应用是否订阅了某事件 */ |
| 100 | private boolean isSubscribed(String webhookEvents, String event) { | 164 | private boolean isSubscribed(String webhookEvents, String event) { |
| 101 | if (webhookEvents == null || webhookEvents.isBlank()) return false; | 165 | if (webhookEvents == null || webhookEvents.isBlank()) return false; |
src/main/java/com/diligrp/rider/task/OrderScheduleTask.java
| @@ -123,7 +123,7 @@ public class OrderScheduleTask { | @@ -123,7 +123,7 @@ public class OrderScheduleTask { | ||
| 123 | payload.put("status", 10); | 123 | payload.put("status", 10); |
| 124 | payload.put("reason", "超时无人接单,系统自动取消"); | 124 | payload.put("reason", "超时无人接单,系统自动取消"); |
| 125 | payload.put("timestamp", System.currentTimeMillis() / 1000); | 125 | payload.put("timestamp", System.currentTimeMillis() / 1000); |
| 126 | - webhookService.send("order.cancelled", order.getId(), | 126 | + webhookService.sendOrderEvent(order, "order.cancelled", |
| 127 | objectMapper.writeValueAsString(payload)); | 127 | objectMapper.writeValueAsString(payload)); |
| 128 | } catch (Exception e) { | 128 | } catch (Exception e) { |
| 129 | log.warn("取消通知失败 orderId={}", order.getId(), e); | 129 | log.warn("取消通知失败 orderId={}", order.getId(), e); |