AlibabaNotify.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <?php
  2. /**
  3. * 1688订阅消息通知处理
  4. *
  5. * 处理1688开放平台推送的订阅消息,包括订单发货通知等
  6. *
  7. * @author: yourname
  8. * @day: 2026/05/08
  9. */
  10. namespace app\controller\api;
  11. use app\common\repositories\store\order\StoreOrderStatusRepository;
  12. use app\services\ThirdParty\AlibabaAgent\OrderService;
  13. use think\facade\Db;
  14. use think\facade\Log;
  15. class AlibabaNotify
  16. {
  17. /**
  18. * 1688订阅消息入口
  19. *
  20. * 接收1688开放平台推送的HTTP POST通知
  21. * 请求格式: application/x-www-form-urlencoded
  22. * 参数:
  23. * - message: JSON字符串,包含订阅消息内容
  24. * - _aop_signature: 签名
  25. *
  26. * @return \think\Response
  27. */
  28. public function notify()
  29. {
  30. // 获取原始POST数据
  31. $postData = request()->post();
  32. Log::channel('alibaba')->info('[订阅消息通知] 收到请求, data:' . json_encode($postData, JSON_UNESCAPED_UNICODE));
  33. if (empty($postData['message'])) {
  34. Log::channel('alibaba')->error('[订阅消息通知] message参数为空');
  35. return response('fail: message is empty');
  36. }
  37. // 解析message
  38. $message = json_decode($postData['message'], true);
  39. if (json_last_error() !== JSON_ERROR_NONE) {
  40. Log::channel('alibaba')->error('[订阅消息通知] message JSON解析失败, message:' . $postData['message']);
  41. return response('fail: message json decode error');
  42. }
  43. $type = $message['type'] ?? '';
  44. Log::channel('alibaba')->info('[订阅消息通知] 消息类型:' . $type . ', message:' . json_encode($message, JSON_UNESCAPED_UNICODE));
  45. try {
  46. switch ($type) {
  47. case 'ORDER_BUYER_VIEW_ANNOUNCE_SENDGOODS':
  48. return $this->handleOrderSendGoods($message);
  49. case 'ORDER_BUYER_VIEW_ORDER_BUYER_REFUND_IN_SALES':
  50. case 'ORDER_BUYER_VIEW_ORDER_REFUND_AFTER_SALES':
  51. return $this->handleRefundInSales($message);
  52. default:
  53. Log::channel('alibaba')->warning('[订阅消息通知] 未知消息类型, type:' . $type);
  54. return response('success: unknown type');
  55. }
  56. } catch (\Throwable $e) {
  57. $errorMsg = $e->getMessage();
  58. $traceStr = $e->getTraceAsString();
  59. Log::channel('alibaba')->error('[订阅消息通知] 处理异常, type:' . $type . ', error:' . $errorMsg . ', file:' . $e->getFile() . ':' . $e->getLine() . ', trace:' . $traceStr);
  60. // 同时记录到默认日志,防止渠道日志配置问题
  61. Log::error('[AlibabaNotify] 处理异常: ' . $errorMsg . ' | file: ' . $e->getFile() . ':' . $e->getLine());
  62. return response('fail: exception - ' . $errorMsg);
  63. }
  64. }
  65. /**
  66. * 处理订单发货通知
  67. *
  68. * 订阅消息类型: ORDER_BUYER_VIEW_ANNOUNCE_SENDGOODS
  69. *
  70. * 消息内容示例:
  71. * {
  72. * "bizKey": "3300284931845138963",
  73. * "data": {
  74. * "buyerMemberId": "b2b-665170100",
  75. * "orderId": 3300284931845138963,
  76. * "currentStatus": "waitbuyerreceive",
  77. * "sellerMemberId": "b2b-1676547900b7bb3",
  78. * "msgSendTime": "2018-05-30 19:34:27"
  79. * },
  80. * "gmtBorn": 1778221635003,
  81. * "msgId": 189049289670,
  82. * "type": "ORDER_BUYER_VIEW_ANNOUNCE_SENDGOODS",
  83. * "userInfo": "b2b-22210711363891dff5"
  84. * }
  85. *
  86. * 处理流程:
  87. * 1. 记录日志
  88. * 2. 根据 data.orderId 查询本地 store_order 表(alibaba_order_id)
  89. * 3. 请求1688获取物流信息接口,获取物流公司和物流单号
  90. * 4. 更新 store_order 表的 delivery_name(快递名称)和 delivery_id(快递单号)
  91. *
  92. * @param array $message 消息内容
  93. * @return \think\Response
  94. */
  95. protected function handleOrderSendGoods(array $message)
  96. {
  97. $data = $message['data'] ?? [];
  98. $orderId = $data['orderId'] ?? 0;
  99. if (empty($orderId)) {
  100. Log::channel('alibaba')->error('[发货通知] data.orderId为空, message:' . json_encode($message, JSON_UNESCAPED_UNICODE));
  101. return response('fail: orderId is empty');
  102. }
  103. Log::channel('alibaba')->info('[发货通知] 开始处理, alibaba_order_id:' . $orderId . ', currentStatus:' . ($data['currentStatus'] ?? '') . ', msgSendTime:' . ($data['msgSendTime'] ?? ''));
  104. // 1. 根据1688订单号查询本地订单
  105. $storeOrder = Db::name('store_order')
  106. ->where('alibaba_order_id', (string)$orderId)
  107. ->find();
  108. if (empty($storeOrder)) {
  109. Log::channel('alibaba')->warning('[发货通知] 未找到匹配的本地订单, alibaba_order_id:' . $orderId);
  110. return response('success: order not found locally');
  111. }
  112. Log::channel('alibaba')->info('[发货通知] 找到本地订单, order_id:' . $storeOrder['order_id'] . ', order_sn:' . $storeOrder['order_sn']);
  113. // 2. 如果已有物流信息,跳过
  114. if (!empty($storeOrder['delivery_name']) && !empty($storeOrder['delivery_id'])) {
  115. Log::channel('alibaba')->info('[发货通知] 订单已有物流信息,跳过更新, order_id:' . $storeOrder['order_id'] . ', delivery_name:' . $storeOrder['delivery_name'] . ', delivery_id:' . $storeOrder['delivery_id']);
  116. return response('success: already has logistics info');
  117. }
  118. // 3. 请求1688获取物流信息
  119. $orderService = new OrderService();
  120. $logisticsResult = $orderService->getLogisticsInfosBuyerView([
  121. 'orderId' => $orderId,
  122. ]);
  123. if ($logisticsResult === false || empty($logisticsResult['logisticsItems'])) {
  124. Log::channel('alibaba')->warning('[发货通知] 获取物流信息失败或物流信息为空, alibaba_order_id:' . $orderId . ', result:' . json_encode($logisticsResult, JSON_UNESCAPED_UNICODE));
  125. return response('success: no logistics info yet');
  126. }
  127. // 4. 取第一条物流信息
  128. $firstLogistics = $logisticsResult['logisticsItems'][0];
  129. $deliveryName = $firstLogistics['logisticsName'] ?? ''; // 物流公司名称
  130. $deliveryId = $firstLogistics['logisticsBillNo'] ?? ''; // 物流单号
  131. if (empty($deliveryName) || empty($deliveryId)) {
  132. Log::channel('alibaba')->warning('[发货通知] 物流信息不完整, logistics:' . json_encode($firstLogistics, JSON_UNESCAPED_UNICODE));
  133. return response('success: logistics info incomplete');
  134. }
  135. // 5. 更新 store_order 表
  136. $updateData = [
  137. 'delivery_type' => 1,
  138. 'delivery_name' => $deliveryName,
  139. 'delivery_id' => $deliveryId,
  140. 'status' => 1, // 待收货
  141. ];
  142. Db::name('store_order')
  143. ->where('order_id', $storeOrder['order_id'])
  144. ->update($updateData);
  145. app()->make(StoreOrderStatusRepository::class)->status($storeOrder['order_id'], 'delivery_0', '订单以配送【快递名称】:' . $deliveryName . '; 【快递单号】:' . $deliveryId);
  146. Log::channel('alibaba')->info('[发货通知] 物流信息更新成功, order_id:' . $storeOrder['order_id'] . ', order_sn:' . $storeOrder['order_sn'] . ', delivery_name:' . $deliveryName . ', delivery_id:' . $deliveryId);
  147. return response('success');
  148. }
  149. /**
  150. * 处理售中退款状态变更通知
  151. *
  152. * 订阅消息类型: ORDER_BUYER_VIEW_ORDER_BUYER_REFUND_IN_SALES
  153. *
  154. * 消息内容示例:
  155. * {
  156. * "data": {
  157. * "buyerMemberId": "b2b-...",
  158. * "orderId": 111,
  159. * "currentStatus": "refundsuccess",
  160. * "refundAction": "SYSTEM_AGREE_REFUND_PROTOCOL",
  161. * "sellerMemberId": "b2b-...",
  162. * "msgSendTime": "2018-05-30 19:30:13",
  163. * "operator": "system",
  164. * "refundId": "1234556"
  165. * },
  166. * "gmtBorn": 1778296872365,
  167. * "msgId": 189206085626,
  168. * "type": "ORDER_BUYER_VIEW_ORDER_BUYER_REFUND_IN_SALES",
  169. * "userInfo": "b2b-..."
  170. * }
  171. *
  172. * 处理流程:
  173. * 1. 根据 data.refundId 查询 rrx_store_refund_alibaba 表
  174. * 2. 更新 alibaba_status 为当前状态
  175. *
  176. * @param array $message 消息内容
  177. * @return \think\Response
  178. */
  179. protected function handleRefundInSales(array $message)
  180. {
  181. $data = $message['data'] ?? [];
  182. $refundId = $data['refundId'] ?? '';
  183. $currentStatus = $data['currentStatus'] ?? '';
  184. $orderId = $data['orderId'] ?? 0;
  185. if (empty($refundId)) {
  186. Log::channel('alibaba')->error('[退款通知] data.refundId为空, message:' . json_encode($message, JSON_UNESCAPED_UNICODE));
  187. return response('fail: refundId is empty');
  188. }
  189. Log::channel('alibaba')->info('[退款通知] 开始处理, refundId:' . $refundId . ', orderId:' . $orderId . ', currentStatus:' . $currentStatus);
  190. // 查询 rrx_store_refund_alibaba 表
  191. $refundRecord = Db::name('store_refund_alibaba')
  192. ->where('alibaba_refund_id', (string)$refundId)
  193. ->find();
  194. if (empty($refundRecord)) {
  195. Log::channel('alibaba')->warning('[退款通知] 未找到匹配的退款记录, refundId:' . $refundId);
  196. return response('success: refund record not found locally');
  197. }
  198. // 更新 alibaba_status
  199. Db::name('store_refund_alibaba')
  200. ->where('id', $refundRecord['id'])
  201. ->update([
  202. 'alibaba_status' => $currentStatus,
  203. ]);
  204. Log::channel('alibaba')->info('[退款通知] 退款状态更新成功, id:' . $refundRecord['id'] . ', refundId:' . $refundId . ', status:' . $currentStatus);
  205. return response('success');
  206. }
  207. }