YunxinSmsService.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace crmeb\services;
  12. use app\common\dao\system\sms\SmsRecordDao;
  13. use app\common\repositories\store\broadcast\BroadcastRoomRepository;
  14. use app\common\repositories\store\order\StoreGroupOrderRepository;
  15. use app\common\repositories\store\order\StoreOrderRepository;
  16. use app\common\repositories\store\order\StoreRefundOrderRepository;
  17. use app\common\repositories\store\service\StoreServiceRepository;
  18. use app\common\repositories\system\config\ConfigValueRepository;
  19. use crmeb\exceptions\SmsException;
  20. use FormBuilder\Exception\FormBuilderException;
  21. use FormBuilder\Factory\Elm;
  22. use FormBuilder\Form;
  23. use think\exception\ValidateException;
  24. use think\facade\Cache;
  25. use think\facade\Config;
  26. use think\facade\Route;
  27. /**
  28. * Class YunxinSmsService
  29. * @package crmeb\services
  30. * @author xaboy
  31. * @day 2020-05-18
  32. */
  33. class YunxinSmsService
  34. {
  35. /**
  36. * api
  37. */
  38. const API = 'https://sms.crmeb.net/api/';
  39. // const API = 'http://plat.crmeb.net/api/';
  40. /**
  41. * @var array
  42. */
  43. protected $config;
  44. /**
  45. * YunxinSmsService constructor.
  46. * @param array $config
  47. */
  48. public function __construct(array $config = [])
  49. {
  50. $this->config = $config;
  51. if (isset($this->config['sms_token'])) {
  52. $this->config['sms_token'] = $this->getToken();
  53. }
  54. }
  55. public static function sendMessage($tempId, $id)
  56. {
  57. if ($tempId == 'DELIVER_GOODS_CODE') {
  58. if (!systemConfig('sms_fahuo_status')) return;
  59. $order = app()->make(StoreOrderRepository::class)->get($id);
  60. if (!$order || !$order->user_phone) return;
  61. $nickname = $order->user->nickname;
  62. $store_name = $order->orderProduct[0]['cart_info']['product']['store_name'] . (count($order->orderProduct) ? '等' : '');
  63. $order_id = $order->order_sn;
  64. self::create()->send($order->user_phone, $tempId, compact('nickname', 'store_name', 'order_id'));
  65. } else if ($tempId == 'TAKE_DELIVERY_CODE') {
  66. if (!systemConfig('sms_take_status')) return;
  67. $order = app()->make(StoreOrderRepository::class)->get($id);
  68. if (!$order || !$order->user_phone) return;
  69. $order_id = $order->order_sn;
  70. $store_name = $order->orderProduct[0]['cart_info']['product']['store_name'] . (count($order->orderProduct) ? '等' : '');
  71. self::create()->send($order->user_phone, $tempId, compact('store_name', 'order_id'));
  72. } else if ($tempId == 'PAY_SUCCESS_CODE') {
  73. if (!systemConfig('sms_pay_status')) return;
  74. $order = app()->make(StoreGroupOrderRepository::class)->get($id);
  75. if (!$order || !$order->user_phone) return;
  76. $pay_price = $order->pay_price;
  77. $order_id = $order->group_order_sn;
  78. self::create()->send($order->user_phone, $tempId, compact('pay_price', 'order_id'));
  79. } else if ($tempId == 'PRICE_REVISION_CODE') {
  80. if (!systemConfig('sms_revision_status')) return;
  81. $order = app()->make(StoreOrderRepository::class)->get($id);
  82. if (!$order || !$order->user_phone) return;
  83. $pay_price = $order->pay_price;
  84. $order_id = $order->order_sn;
  85. self::create()->send($order->user_phone, $tempId, compact('pay_price', 'order_id'));
  86. } else if ($tempId == 'ORDER_PAY_FALSE') {
  87. if (!systemConfig('sms_pay_false_status')) return;
  88. $order = app()->make(StoreGroupOrderRepository::class)->get($id);
  89. if (!$order || !$order->user_phone) return;
  90. $order_id = $order->group_order_sn;
  91. self::create()->send($order->user_phone, $tempId, compact('order_id'));
  92. } else if ($tempId == 'REFUND_FAIL_CODE') {
  93. if (!systemConfig('sms_refund_fail_status')) return;
  94. $order = app()->make(StoreRefundOrderRepository::class)->get($id);
  95. if (!$order || !$order->order->user_phone) return;
  96. $order_id = $order->order->order_sn;
  97. $store_name = $order->refundProduct[0]->product['cart_info']['product']['store_name'] . (count($order->refundProduct) ? '等' : '');
  98. self::create()->send($order->order->user_phone, $tempId, compact('order_id', 'store_name'));
  99. } else if ($tempId == 'REFUND_SUCCESS_CODE') {
  100. if (!systemConfig('sms_refund_success_status')) return;
  101. $order = app()->make(StoreRefundOrderRepository::class)->get($id);
  102. if (!$order || !$order->order->user_phone) return;
  103. $order_id = $order->order->order_sn;
  104. $store_name = $order->refundProduct[0]->product['cart_info']['product']['store_name'] . (count($order->refundProduct) ? '等' : '');
  105. self::create()->send($order->order->user_phone, $tempId, compact('order_id', 'store_name'));
  106. } else if ($tempId == 'REFUND_CONFORM_CODE') {
  107. if (!systemConfig('sms_refund_confirm_status')) return;
  108. $order = app()->make(StoreRefundOrderRepository::class)->get($id);
  109. if (!$order || !$order->order->user_phone) return;
  110. $order_id = $order->order->order_sn;
  111. $store_name = $order->refundProduct[0]->product['cart_info']['product']['store_name'] . (count($order->refundProduct) ? '等' : '');
  112. self::create()->send($order->order->user_phone, $tempId, compact('order_id', 'store_name'));
  113. } else if ($tempId == 'ADMIN_PAY_SUCCESS_CODE') {
  114. if (!systemConfig('sms_admin_pay_status')) return;
  115. $order = app()->make(StoreGroupOrderRepository::class)->get($id);
  116. if (!$order) return;
  117. foreach ($order->orderList as $_order) {
  118. self::sendMerMessage($_order->mer_id, $tempId, [
  119. 'order_id' => $_order->order_sn
  120. ]);
  121. }
  122. } else if ($tempId == 'ADMIN_RETURN_GOODS_CODE') {
  123. if (!systemConfig('sms_admin_return_status')) return;
  124. $order = app()->make(StoreRefundOrderRepository::class)->get($id);
  125. if (!$order) return;
  126. self::sendMerMessage($order->mer_id, $tempId, [
  127. 'order_id' => $order->refund_order_sn
  128. ]);
  129. } else if ($tempId == 'ADMIN_TAKE_DELIVERY_CODE') {
  130. if (!systemConfig('sms_admin_take_status')) return;
  131. $order = app()->make(StoreOrderRepository::class)->get($id);
  132. if (!$order) return;
  133. self::sendMerMessage($order->mer_id, $tempId, [
  134. 'order_id' => $order->order_sn
  135. ]);
  136. } else if ($tempId == 'ADMIN_DELIVERY_CODE') {
  137. if (!systemConfig('sms_admin_postage_status')) return;
  138. $order = app()->make(StoreOrderRepository::class)->get($id);
  139. if (!$order) return;
  140. self::sendMerMessage($order->mer_id, $tempId, [
  141. 'order_id' => $order->order_sn
  142. ]);
  143. } else if ($tempId == 'BROADCAST_ROOM_CODE') {
  144. if (!systemConfig('sms_broadcast_room_status')) return;
  145. $room = app()->make(BroadcastRoomRepository::class)->get($id);
  146. if (!$room) return;
  147. self::create()->send($room->phone, $tempId, [
  148. 'wechat' => $room->anchor_wechat
  149. ]);
  150. } else if ($tempId == 'PAY_PRESELL_CODE') {
  151. if (!systemConfig('sms_pay_presell_status')) return;
  152. $order = app()->make(StoreOrderRepository::class)->get($id);
  153. if (!$order || !$order->user_phone) return;
  154. self::create()->send($order->user_phone, $tempId, [
  155. 'date' => date('Y-m-d', strtotime($order->pay_time)),
  156. 'product_name' => $order->orderProduct[0]['cart_info']['product']['store_name'] ?? ''
  157. ]);
  158. } else if ($tempId == 'APPLY_MER_SUCCESS') {
  159. if (systemConfig('sms_apply_mer_succ_status') !== '' && !systemConfig('sms_apply_mer_succ_status')) return;
  160. self::create()->send($id['phone'], $tempId, [
  161. 'date' => date('m月d日', strtotime($id['date'])),
  162. 'mer' => $id['mer'],
  163. 'phone' => $id['phone'],
  164. 'pwd' => $id['pwd'],
  165. 'site_name' => systemConfig('site_name'),
  166. ]);
  167. } else if ($tempId == 'APPLY_MER_FAIL') {
  168. if (systemConfig('sms_apply_mer_fail_status') !== '' && !systemConfig('sms_apply_mer_fail_status')) return;
  169. self::create()->send($id['phone'], $tempId, [
  170. 'date' => date('m月d日', strtotime($id['date'])),
  171. 'mer' => $id['mer'],
  172. 'site' => systemConfig('site_name'),
  173. ]);
  174. }
  175. }
  176. public static function sendMerMessage($merId, string $tempId, array $data)
  177. {
  178. $noticeServiceInfo = app()->make(StoreServiceRepository::class)->getNoticeServiceInfo($merId);
  179. $yunxinSmsService = self::create();
  180. foreach ($noticeServiceInfo as $service) {
  181. if (!$service['phone']) continue;
  182. $yunxinSmsService->send($service['phone'], $tempId, array_merge(['admin_name' => $service['nickname']], $data));
  183. }
  184. }
  185. /**
  186. * @return string
  187. * @author xaboy
  188. * @day 2020-05-18
  189. */
  190. protected function getToken()
  191. {
  192. return md5($this->config['sms_account'] . $this->config['sms_token']);
  193. }
  194. /**
  195. * @author xaboy
  196. * @day 2020-05-18
  197. */
  198. public function checkConfig()
  199. {
  200. if (!isset($this->config['sms_account']) || !$this->config['sms_account']) {
  201. throw new ValidateException('请登录短信账户');
  202. }
  203. if (!isset($this->config['sms_token']) || !$this->config['sms_token']) {
  204. throw new ValidateException('请登录短信账户');
  205. }
  206. }
  207. /**
  208. * 发送注册验证码
  209. * @param $phone
  210. * @return mixed
  211. */
  212. public function captcha($phone)
  213. {
  214. return json_decode(HttpService::getRequest(self::API . 'sms/captcha', compact('phone')), true);
  215. }
  216. /**
  217. * 短信注册
  218. * @param $account
  219. * @param $password
  220. * @param $url
  221. * @param $phone
  222. * @param $code
  223. * @param $sign
  224. * @return mixed
  225. */
  226. public function register($account, $password, $url, $phone, $code, $sign)
  227. {
  228. return $this->registerData(compact('account', 'password', 'url', 'phone', 'code', 'sign'));
  229. }
  230. /**
  231. * @param array $data
  232. * @return mixed
  233. * @author xaboy
  234. * @day 2020-05-18
  235. */
  236. public function registerData(array $data)
  237. {
  238. return json_decode(HttpService::postRequest(self::API . 'sms/register', $data), true);
  239. }
  240. /**
  241. * 公共短信模板列表
  242. * @param array $data
  243. * @return mixed
  244. */
  245. public function publictemp(array $data = [])
  246. {
  247. $this->checkConfig();
  248. $data['account'] = $this->config['sms_account'];
  249. $data['token'] = $this->config['sms_token'];
  250. $data['source'] = 'crmeb_merchant';
  251. return json_decode(HttpService::postRequest(self::API . 'sms/publictemp', $data), true);
  252. }
  253. /**
  254. * 公共短信模板添加
  255. * @param $id
  256. * @param $tempId
  257. * @return mixed
  258. */
  259. public function use($id, $tempId)
  260. {
  261. $this->checkConfig();
  262. $data = [
  263. 'account' => $this->config['sms_account'],
  264. 'token' => $this->config['sms_token'],
  265. 'id' => $id,
  266. 'tempId' => $tempId,
  267. ];
  268. return json_decode(HttpService::postRequest(self::API . 'sms/use', $data), true);
  269. }
  270. /**
  271. * @param string $templateId
  272. * @return mixed
  273. * @author xaboy
  274. * @day 2020-05-18
  275. */
  276. public function getTemplateCode(string $templateId)
  277. {
  278. return Config::get('sms.template_id.' . $templateId);
  279. }
  280. /**
  281. * 发送短信
  282. * @param string $phone
  283. * @param string $templateId
  284. * @param array $data
  285. * @return bool|string
  286. * @throws SmsException
  287. */
  288. public function send(string $phone, string $templateId, array $data = [])
  289. {
  290. if (!$phone) {
  291. throw new SmsException('Mobile number cannot be empty');
  292. }
  293. $this->checkConfig();
  294. $formData['uid'] = $this->config['sms_account'];
  295. $formData['token'] = $this->config['sms_token'];
  296. $formData['mobile'] = $phone;
  297. $formData['template'] = $this->getTemplateCode($templateId);
  298. if (is_null($formData['template']))
  299. throw new SmsException('Missing template number');
  300. $formData['param'] = json_encode($data);
  301. $resource = json_decode(HttpService::postRequest(self::API . 'sms/send', $formData), true);
  302. if ($resource['status'] === 400) {
  303. throw new SmsException($resource['msg']);
  304. } else {
  305. app()->make(SmsRecordDao::class)->create([
  306. 'uid' => $formData['uid'],
  307. 'phone' => $phone,
  308. 'content' => $resource['data']['content'],
  309. 'template' => $resource['data']['template'],
  310. 'record_id' => $resource['data']['id']
  311. ]);
  312. }
  313. return $resource;
  314. }
  315. /**
  316. * 账号信息
  317. * @return mixed
  318. */
  319. public function count()
  320. {
  321. $this->checkConfig();
  322. return json_decode(HttpService::postRequest(self::API . 'sms/userinfo', [
  323. 'account' => $this->config['sms_account'],
  324. 'token' => $this->config['sms_token']
  325. ]), true);
  326. }
  327. /**
  328. * 支付套餐
  329. * @param $page
  330. * @param $limit
  331. * @return mixed
  332. */
  333. public function meal($page, $limit)
  334. {
  335. return json_decode(HttpService::getRequest(self::API . 'sms/meal', [
  336. 'page' => $page,
  337. 'limit' => $limit
  338. ]), true);
  339. }
  340. /**
  341. * 支付码
  342. * @param $payType
  343. * @param $mealId
  344. * @param $price
  345. * @param $attach
  346. * @param $notify
  347. * @return mixed
  348. */
  349. public function pay($payType, $mealId, $price, $attach, $notify = null)
  350. {
  351. $this->checkConfig();
  352. $data['uid'] = $this->config['sms_account'];
  353. $data['token'] = $this->config['sms_token'];
  354. $data['payType'] = $payType;
  355. $data['mealId'] = $mealId;
  356. $data['notify'] = $notify ?? Route::buildUrl('SmsNotify')->build();
  357. $data['price'] = $price;
  358. $data['attach'] = $attach;
  359. return json_decode(HttpService::postRequest(self::API . 'sms/mealpay', $data), true);
  360. }
  361. /**
  362. * 申请模板消息
  363. * @param $title
  364. * @param $content
  365. * @param $type
  366. * @return mixed
  367. */
  368. public function apply($title, $content, $type)
  369. {
  370. $this->checkConfig();
  371. $data['account'] = $this->config['sms_account'];
  372. $data['token'] = $this->config['sms_token'];
  373. $data['title'] = $title;
  374. $data['content'] = $content;
  375. $data['type'] = $type;
  376. return json_decode(HttpService::postRequest(self::API . 'sms/apply', $data), true);
  377. }
  378. /**
  379. * 短信模板列表
  380. * @param $data
  381. * @return mixed
  382. */
  383. public function template(array $data)
  384. {
  385. $this->checkConfig();
  386. return json_decode(HttpService::postRequest(self::API . 'sms/template', $data + [
  387. 'account' => $this->config['sms_account'], 'token' => $this->config['sms_token']
  388. ]), true);
  389. }
  390. /**
  391. * 获取短息记录状态
  392. * @param $record_id
  393. * @return mixed
  394. */
  395. public function getStatus(array $record_id)
  396. {
  397. return json_decode(HttpService::postRequest(self::API . 'sms/status', [
  398. 'record_id' => json_encode($record_id)
  399. ]), true);
  400. }
  401. /**
  402. * @return YunxinSmsService
  403. * @author xaboy
  404. * @day 2020-05-18
  405. */
  406. public static function create()
  407. {
  408. /** @var ConfigValueRepository $make */
  409. $make = app()->make(ConfigValueRepository::class);
  410. $config = $make->more(['sms_account', 'sms_token'], 0);
  411. return new static($config);
  412. }
  413. /**
  414. * @param string $sms_account
  415. * @param string $sms_token
  416. * @return $this
  417. * @author xaboy
  418. * @day 2020-05-18
  419. */
  420. public function setConfig(string $sms_account, string $sms_token)
  421. {
  422. $this->config = compact('sms_token', 'sms_account');
  423. $this->config['sms_token'] = $this->getToken();
  424. return $this;
  425. }
  426. /**
  427. * @return Form
  428. * @throws FormBuilderException
  429. * @author xaboy
  430. * @day 2020-05-18
  431. */
  432. public function form()
  433. {
  434. return Elm::createForm(Route::buildUrl('smsCreate')->build(), [
  435. Elm::input('title', '模板名称'),
  436. Elm::input('content', '模板内容')->type('textarea'),
  437. Elm::radio('type', '模板类型', 1)->options([['label' => '验证码', 'value' => 1], ['label' => '通知', 'value' => 2], ['label' => '推广', 'value' => 3]])
  438. ])->setTitle('申请短信模板');
  439. }
  440. /**
  441. * @return mixed
  442. * @author xaboy
  443. * @day 2020-05-18
  444. */
  445. public function account()
  446. {
  447. $this->checkConfig();
  448. return $this->config['sms_account'];
  449. }
  450. /**
  451. * @Author:Qinii
  452. * @Date: 2020/9/19
  453. * @param $data
  454. * @return mixed
  455. */
  456. public function smsChange($data)
  457. {
  458. $this->checkConfig();
  459. $data['account'] = $this->config['sms_account'];
  460. $data['token'] = $this->config['sms_token'];
  461. return json_decode(HttpService::postRequest(self::API . 'sms/modify', $data), true);
  462. }
  463. /**
  464. * @Author:Qinii
  465. * @Date: 2020/9/19
  466. * @param $phone
  467. * @param $code
  468. * @param $type
  469. * @return bool
  470. */
  471. public function checkSmsCode($phone, $code, $type)
  472. {
  473. $sms_key = $this->sendSmsKey($phone, $type);
  474. if (!$cache_code = Cache::get($sms_key)) return false;
  475. if ($code != $cache_code) return false;
  476. Cache::delete($sms_key);
  477. return true;
  478. }
  479. /**
  480. * @Author:Qinii
  481. * @Date: 2020/9/19
  482. * @param $phone
  483. * @param string $type
  484. * @return string
  485. */
  486. public function sendSmsKey($phone, $type = 'login')
  487. {
  488. switch ($type) {
  489. case 'login': //登录
  490. return 'api_login_' . $phone;
  491. break;
  492. case 'binding': //绑定手机号
  493. return 'api_binding_' . $phone;
  494. break;
  495. case 'intention': //申请入住
  496. return 'merchant_intention_' . $phone;
  497. break;
  498. case 'change_pwd': //修改密码
  499. return 'pwd_' . $phone;
  500. break;
  501. default:
  502. return 'crmeb_' . $phone;
  503. break;
  504. }
  505. }
  506. }