Commit f3db296ca46698c3171b15d6b739976e4b9d9837

Authored by shaofan
1 parent 2449a3c0

完善订单Webhook事件通知:按订单应用定向回调,新增签名与失败重试机制,并补充后台指派/转单通知触发

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);