push.php 12 KB

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