WeChatChannel.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php
  2. namespace App\Channels;
  3. use App\Channels\Library\WeChat;
  4. use Cache;
  5. use Helpers;
  6. use Http;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Notifications\Notification;
  9. use Log;
  10. use Str;
  11. class WeChatChannel
  12. {
  13. public function send($notifiable, Notification $notification): false|array
  14. { // route('message.show', ['type' => 'markdownMsg', 'msgId' => ''])
  15. $message = $notification->toCustom($notifiable);
  16. $url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token='.$this->getAccessToken();
  17. if (isset($message['button'])) { // 按钮交互型
  18. // https://work.weixin.qq.com/api/doc/90000/90135/90236#%E6%8C%89%E9%92%AE%E4%BA%A4%E4%BA%92%E5%9E%8B
  19. $body = [
  20. 'touser' => '@all',
  21. 'msgtype' => 'template_card',
  22. 'agentid' => sysConfig('wechat_aid'),
  23. 'template_card' => [
  24. 'card_type' => 'button_interaction',
  25. 'main_title' => ['title' => $message['title']],
  26. 'horizontal_content_list' => $message['body'],
  27. 'task_id' => time().Str::random(),
  28. 'button_list' => [
  29. [
  30. 'type' => 1,
  31. 'text' => trans('common.status.reject'),
  32. 'style' => 3,
  33. 'url' => $message['button'][0],
  34. ],
  35. [
  36. 'type' => 1,
  37. 'text' => trans('common.confirm'),
  38. 'style' => 1,
  39. 'url' => $message['button'][1],
  40. ],
  41. ],
  42. ],
  43. ];
  44. } elseif (isset($message['url_type'])) { // 文本卡片
  45. $msgId = Str::uuid(); // 生成对公消息查询URL
  46. $body = [
  47. 'touser' => '@all',
  48. 'agentid' => sysConfig('wechat_aid'),
  49. 'msgtype' => 'textcard',
  50. 'textcard' => [
  51. 'title' => $message['title'],
  52. 'description' => trans('notification.details_btn'),
  53. 'url' => route('message.show', ['type' => $message['url_type'], $msgId]),
  54. 'btntxt' => trans('notification.details'),
  55. ],
  56. ];
  57. } else { // 文本消息
  58. $body = [
  59. 'touser' => '@all',
  60. 'agentid' => sysConfig('wechat_aid'),
  61. 'msgtype' => 'text',
  62. 'text' => [
  63. 'content' => $message['content'],
  64. ],
  65. 'duplicate_check_interval' => 600,
  66. ];
  67. }
  68. $response = Http::timeout(15)->withBody(json_encode($body, JSON_UNESCAPED_UNICODE), 'application/json;charset=utf-8')->post($url);
  69. // 发送成功
  70. if ($response->ok()) {
  71. $ret = $response->json();
  72. if (! $ret['errcode'] && $ret['errmsg'] === 'ok') {
  73. Helpers::addNotificationLog($message['title'], $message['content'] ?? var_export($message['body'], true), 5, 1, null, $msgId ?? null);
  74. return $ret;
  75. }
  76. // 发送失败
  77. Helpers::addNotificationLog($message['title'], $message['content'] ?? var_export($message['body'], true), 5, -1, $ret ? $ret['errmsg'] : trans('common.status.unknown'));
  78. } else {
  79. Log::critical(trans('notification.error', ['channel' => trans('admin.system.notification.channel.wechat'), 'reason' => var_export($response, true)]));
  80. }
  81. return false;
  82. }
  83. private function getAccessToken(): ?string
  84. {
  85. if (Cache::has('wechat_access_token')) {
  86. $access_token = Cache::get('wechat_access_token');
  87. } else {
  88. // https://work.weixin.qq.com/api/doc/90000/90135/91039
  89. $response = Http::get('https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid='.sysConfig('wechat_cid').'&corpsecret='.sysConfig('wechat_secret'));
  90. if ($response->ok() && isset($response->json()['access_token'])) {
  91. $access_token = $response->json()['access_token'];
  92. Cache::put('wechat_access_token', $access_token, 7189); // 2小时
  93. } else {
  94. Log::critical(trans('notification.error', ['channel' => trans('admin.system.notification.channel.wechat'), 'reason' => trans('notification.get_access_token_failed', ['body' => $response->body()])]));
  95. abort(400);
  96. }
  97. }
  98. return $access_token ?? null;
  99. }
  100. public function verify(Request $request): void
  101. {
  102. $errCode = (new WeChat)->verifySignature($request->input('msg_signature'), $request->input('timestamp'), $request->input('nonce'), $request->input('echostr'), $sEchoStr);
  103. if ($errCode === 0) {
  104. exit($sEchoStr);
  105. }
  106. Log::critical(trans('notification.error', ['channel' => trans('admin.system.notification.channel.wechat'), 'reason' => trans('notification.sign_failed')]).var_export($errCode, true));
  107. }
  108. }