push.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. namespace common\components;
  3. use yii\db\ActiveRecord;
  4. use Yii;
  5. class push
  6. {
  7. // 消息类型
  8. const MSG_TYPE_ORDER = 'order'; // 订单消息
  9. const MSG_TYPE_LOGISTICS = 'logistics'; // 物流消息
  10. const MSG_TYPE_ACCOUNT = 'account'; // 账户消息
  11. const MSG_TYPE_WORK = 'work'; // 工作事项提醒
  12. const MSG_TYPE_SUBSCRIPTION = 'subscription'; // 订阅消息
  13. const SECRET = 'RIVATE_KEY_S^@VJDy7'; // 👈 与云函数中保持一致
  14. const GHS_URL = 'https://fc-mp-42d1a66f-2aec-4fc5-9d92-4d176bf3d337.next.bspapp.com/ghs/push';
  15. const HD_URL = 'https://fc-mp-027cce01-d39a-4ed2-8b09-1f4ff43ec945.next.bspapp.com/hd/push';
  16. // 私有属性
  17. private $url = '';
  18. private $client = '';
  19. private $msgType = '';
  20. public function __construct($client, $msgType) {
  21. $this->client = $client;
  22. if ($client == 'ghs') {
  23. $this->url = self::GHS_URL;
  24. } else if ($client == 'hd') {
  25. $this->url = self::HD_URL;
  26. } else {
  27. throw new \Exception('client not found');
  28. }
  29. $this->msgType = $msgType;
  30. }
  31. /**
  32. * 实现部分厂商特定功能,包括仅部分厂商支持、不常用或厂商临时新增的功能(不依赖 uni-push,厂商文档支持的参数可直接使用)。
  33. * 例如:推送渠道 ID、消息分类(部分厂商未配置时可能被限量推送或静默推送,即静音且需下拉系统通知栏才可见通知内容)、通知栏富文本
  34. */
  35. public function getOptions($client) {
  36. if ($client == 'ghs') {
  37. return $this->initGhsOptions();
  38. }
  39. return $this->initHdOptions();
  40. }
  41. /**
  42. * 花店的厂商options
  43. * @return array
  44. */
  45. public function initHdOptions() {
  46. $optionsArr = [];
  47. switch ($this->msgType) {
  48. case self::MSG_TYPE_ORDER:
  49. $optionsArr = [ "android" =>
  50. [
  51. "XM" => [
  52. "/extra.channel_id" => "142737" //小米后台申请的通知类别id -- https://admin.xmpush.xiaomi.com/zh_CN/channel/list?appId=2882303761520146676
  53. ],
  54. "HW" => [
  55. "/message/android/category" => "EXPRESS"
  56. ],
  57. "VV" => [
  58. "/category" => "ORDER"
  59. ],
  60. ]
  61. ];
  62. break;
  63. case self::MSG_TYPE_LOGISTICS:
  64. $optionsArr = [ "android" =>
  65. [
  66. "XM" => [
  67. "/extra.channel_id" => "142737"
  68. ],
  69. "HW" => [
  70. "/message/android/category" => "EXPRESS"
  71. ],
  72. "VV" => [
  73. "/category" => "ORDER"
  74. ],
  75. ]
  76. ];
  77. break;
  78. case self::MSG_TYPE_ACCOUNT:
  79. $optionsArr = [ "android" =>
  80. [
  81. "XM" => [
  82. "/extra.channel_id" => "143019"
  83. ],
  84. "HW" => [
  85. "/message/android/category" => "ACCOUNT"
  86. ],
  87. "VV" => [
  88. "/category" => "ACCOUNT"
  89. ],
  90. ]
  91. ];
  92. break;
  93. case self::MSG_TYPE_WORK:
  94. $optionsArr = [ "android" =>
  95. [
  96. "XM" => [
  97. "/extra.channel_id" => "143018"
  98. ],
  99. "HW" => [
  100. "/message/android/category" => "WORK"
  101. ],
  102. "VV" => [
  103. "/category" => "TODO"
  104. ],
  105. ]
  106. ];
  107. break;
  108. case self::MSG_TYPE_SUBSCRIPTION:
  109. $optionsArr = [ "android" =>
  110. [
  111. "XM" => [
  112. "/extra.channel_id" => "0" // TODO 暂无
  113. ],
  114. "HW" => [
  115. "/message/android/category" => "SUBSCRIPTION"
  116. ],
  117. "VV" => [
  118. "/category" => "SUBSCRIPTION"
  119. ],
  120. ]
  121. ];
  122. break;
  123. }
  124. // 【荣耀】
  125. $optionsArr["android"]["HO"] = [
  126. "/android/notification/importance" => "NORMAL"
  127. ];
  128. return $optionsArr;
  129. }
  130. /**
  131. * 批发商的厂商options
  132. * @return array
  133. */
  134. public function initGhsOptions() {
  135. $optionsArr = [];
  136. switch ($this->msgType) {
  137. case self::MSG_TYPE_ORDER:
  138. $optionsArr = [ "android" =>
  139. [
  140. "XM" => [
  141. "/extra.channel_id" => "142743" //小米后台申请的通知类别id -- https://admin.xmpush.xiaomi.com/zh_CN/channel/list?appId=2882303761520146676
  142. ],
  143. "HW" => [
  144. "/message/android/category" => "EXPRESS"
  145. ],
  146. "VV" => [
  147. "/category" => "ORDER"
  148. ],
  149. ]
  150. ];
  151. break;
  152. case self::MSG_TYPE_LOGISTICS:
  153. $optionsArr = [ "android" =>
  154. [
  155. "XM" => [
  156. "/extra.channel_id" => "142744"
  157. ],
  158. "HW" => [
  159. "/message/android/category" => "EXPRESS"
  160. ],
  161. "VV" => [
  162. "/category" => "ORDER"
  163. ],
  164. ]
  165. ];
  166. break;
  167. case self::MSG_TYPE_ACCOUNT:
  168. $optionsArr = [ "android" =>
  169. [
  170. "XM" => [
  171. "/extra.channel_id" => "142895"
  172. ],
  173. "HW" => [
  174. "/message/android/category" => "ACCOUNT"
  175. ],
  176. "VV" => [
  177. "/category" => "ACCOUNT"
  178. ],
  179. ]
  180. ];
  181. break;
  182. case self::MSG_TYPE_WORK:
  183. $optionsArr = [ "android" =>
  184. [
  185. "XM" => [
  186. "/extra.channel_id" => "142892"
  187. ],
  188. "HW" => [
  189. "/message/android/category" => "WORK"
  190. ],
  191. "VV" => [
  192. "/category" => "TODO"
  193. ],
  194. ]
  195. ];
  196. break;
  197. case self::MSG_TYPE_SUBSCRIPTION:
  198. $optionsArr = [ "android" =>
  199. [
  200. "XM" => [
  201. "/extra.channel_id" => "0" // TODO 暂无
  202. ],
  203. "HW" => [
  204. "/message/android/category" => "SUBSCRIPTION"
  205. ],
  206. "VV" => [
  207. "/category" => "SUBSCRIPTION"
  208. ],
  209. ]
  210. ];
  211. break;
  212. }
  213. return $optionsArr;
  214. }
  215. public function pushByCloud($cids, $title, $content, $payload = [])
  216. {
  217. $sign = md5($title . $content . self::SECRET);
  218. $optionsArr = $this->getOptions($this->client);
  219. // 测试环境的特殊处理
  220. if ( getenv('YII_ENV') == 'dev') {
  221. $optionsArr["android"]["HW"]["/message/android/target_user_type"] = 1; //华为 -- 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条,此target_user_type 参数请勿发布至线上。
  222. $optionsArr["android"]["VV"]["/pushMode"] = 1; //VIVO -- 值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。线上请勿使用测试推送
  223. $optionsArr["android"]["HO"]["/android/targetUserType"] = 1; //荣耀 -- 值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。
  224. }
  225. $data = [
  226. 'cid' => is_array($cids) ? $cids : [$cids],
  227. 'title' => $title,
  228. 'content' => $content,
  229. 'payload' => $payload,
  230. 'force_notification' => true, // true: 无论是离线推送还是在线推送,都自创建通知栏消息
  231. 'sign' => $sign,
  232. 'options' => $optionsArr
  233. ];
  234. $ch = curl_init($this->url);
  235. if ($ch === false) {
  236. throw new \Exception('Failed to initialize curl');
  237. }
  238. try {
  239. curl_setopt_array($ch, [
  240. CURLOPT_RETURNTRANSFER => true,
  241. CURLOPT_POST => true,
  242. CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
  243. CURLOPT_POSTFIELDS => json_encode($data),
  244. CURLOPT_TIMEOUT => 10,
  245. CURLOPT_CONNECTTIMEOUT => 10,
  246. ]);
  247. $response = curl_exec($ch);
  248. if ($response === false) {
  249. throw new \Exception('CURL Error: ' . curl_error($ch));
  250. }
  251. return $response;
  252. } finally {
  253. curl_close($ch);
  254. }
  255. }
  256. /**
  257. * 向供货商 APP 发送新订单通知
  258. * @param ActiveRecord $shop 供货商门店
  259. * @param ActiveRecord $cgShop 采购花店门店
  260. * @param ActiveRecord $order xhGhsOrder
  261. * @throws \Exception
  262. */
  263. public function ghsOrderMessage($shop, $cgShop, $order)
  264. {
  265. try {
  266. $allDevices = \bizGhs\device\classes\GhsDeviceClass::getAllByCondition(['shopId'=>$shop->id]);
  267. $cids = array_column($allDevices, 'clientId');
  268. $title = '你有新的订单';
  269. $shopName = $cgShop->merchantName . ($cgShop->shopName != '首店' ? '(' . $cgShop->shopName . ')' : '');
  270. $content = $shopName . ' ¥' . $order->actPrice;
  271. $payload = [
  272. "page" => "pagesOrder/detail",
  273. "params" => ["id" => $order->id]
  274. ];
  275. $this->pushByCloud($cids, $title, $content, $payload);
  276. } catch (\Exception $e) {
  277. Yii::error('向供货商 APP 发送新订单通知失败: ' . $e->getMessage());
  278. }
  279. }
  280. }