SmsService.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <?php
  2. namespace services\extend;
  3. use Yii;
  4. use yii\helpers\Json;
  5. use yii\web\NotFoundHttpException;
  6. use yii\web\UnprocessableEntityHttpException;
  7. use common\models\extend\sms\TencentSms;
  8. use common\enums\StatusEnum;
  9. use common\helpers\ArrayHelper;
  10. use common\helpers\EchantsHelper;
  11. use common\models\extend\SmsLog;
  12. use common\queues\SmsJob;
  13. use Overtrue\EasySms\EasySms;
  14. /**
  15. * 短信
  16. *
  17. * Class SmsService
  18. * @package services\extend
  19. * @author jianyan74 <751393839@qq.com>
  20. */
  21. class SmsService
  22. {
  23. /**
  24. * @var bool
  25. */
  26. public $queueSwitch = false;
  27. /**
  28. * @var
  29. */
  30. protected $config;
  31. /**
  32. * 发送短信
  33. *
  34. * ```php
  35. * Yii::$app->services->extendSms->send($mobile, $code, $usage, $member_id)
  36. * ```
  37. *
  38. * @param int $mobile 手机号码
  39. * @param int $code 验证码
  40. * @param string $usage 用途
  41. * @param int $member_id 用户ID
  42. * @return SmsLog|string|null
  43. * @throws UnprocessableEntityHttpException
  44. */
  45. public function send($mobile, $code, $usage, $member_id = 0)
  46. {
  47. $this->initSms();
  48. $ip = Yii::$app->services->base->getUserIp();
  49. if ($this->queueSwitch == true) {
  50. $messageId = Yii::$app->queue->push(new SmsJob([
  51. 'mobile' => $mobile,
  52. 'code' => $code,
  53. 'usage' => $usage,
  54. 'member_id' => $member_id,
  55. 'ip' => $ip
  56. ]));
  57. return $messageId;
  58. }
  59. return $this->realSend($mobile, $code, $usage, $member_id, $ip);
  60. }
  61. /**
  62. * 真实发送短信
  63. *
  64. * @param $mobile
  65. * @param $code
  66. * @param $usage
  67. * @param int $member_id
  68. * @param string $ip
  69. * @return SmsLog
  70. * @throws UnprocessableEntityHttpException
  71. */
  72. public function realSend($mobile, $code, $usage, $member_id = 0, $ip = '')
  73. {
  74. !empty($this->template) && $this->template = ArrayHelper::map(Json::decode($this->template), 'group', 'template');
  75. is_array($this->template) && $templateID = $this->template[$usage] ?? '';
  76. try {
  77. // 校验发送是否频繁
  78. if (($smsLog = $this->findByMobile($mobile)) && $smsLog['created_at'] + 60 > time()) {
  79. throw new NotFoundHttpException('请不要频繁发送短信');
  80. }
  81. $easySms = new EasySms($this->config);
  82. // Tencent SMS
  83. $easySms->extend('tencent', function($gatewayConfig){
  84. return new TencentSms($gatewayConfig);
  85. });
  86. $result = $easySms->send($mobile, [
  87. 'template' => $templateID,
  88. 'data' => [
  89. 'code' => $code,
  90. ],
  91. ]);
  92. $this->saveLog([
  93. 'mobile' => $mobile,
  94. 'code' => $code,
  95. 'member_id' => $member_id,
  96. 'usage' => $usage,
  97. 'ip' => $ip,
  98. 'error_code' => 200,
  99. 'error_msg' => 'ok',
  100. 'error_data' => Json::encode($result),
  101. ]);
  102. } catch (NotFoundHttpException $e) {
  103. throw new UnprocessableEntityHttpException($e->getMessage());
  104. } catch (\Exception $e) {
  105. $errorMessage = [];
  106. $exceptions = $e->getExceptions();
  107. $gateways = $this->config['default']['gateways'];
  108. foreach ($gateways as $gateway) {
  109. if (isset($exceptions[$gateway])) {
  110. $errorMessage[$gateway] = $exceptions[$gateway]->getMessage();
  111. }
  112. }
  113. $this->saveLog([
  114. 'mobile' => $mobile,
  115. 'code' => $code,
  116. 'member_id' => $member_id,
  117. 'usage' => $usage,
  118. 'ip' => $ip,
  119. 'error_code' => 422,
  120. 'error_msg' => '发送失败',
  121. 'error_data' => Json::encode($errorMessage),
  122. ]);
  123. throw new UnprocessableEntityHttpException('短信发送失败');
  124. }
  125. }
  126. /**
  127. * @param $type
  128. * @return array
  129. */
  130. public function stat($type)
  131. {
  132. $fields = [
  133. 'count' => '异常发送数量'
  134. ];
  135. // 获取时间和格式化
  136. list($time, $format) = EchantsHelper::getFormatTime($type);
  137. // 获取数据
  138. return EchantsHelper::lineOrBarInTime(function ($start_time, $end_time, $formatting) {
  139. return SmsLog::find()
  140. ->select(["from_unixtime(created_at, '$formatting') as time", 'count(id) as count'])
  141. ->andWhere(['between', 'created_at', $start_time, $end_time])
  142. ->andWhere(['status' => StatusEnum::ENABLED])
  143. ->andWhere(['>', 'error_code', 399])
  144. ->andFilterWhere(['merchant_id' => Yii::$app->services->merchant->getId()])
  145. ->groupBy(['time'])
  146. ->asArray()
  147. ->all();
  148. }, $fields, $time, $format);
  149. }
  150. /**
  151. * @param $mobile
  152. * @return array|\yii\db\ActiveRecord|null
  153. */
  154. public function findByMobile($mobile)
  155. {
  156. return SmsLog::find()
  157. ->where(['mobile' => $mobile])
  158. ->orderBy('id desc')
  159. ->asArray()
  160. ->one();
  161. }
  162. /**
  163. * @param array $data
  164. * @return SmsLog
  165. */
  166. protected function saveLog($data = [])
  167. {
  168. $log = new SmsLog();
  169. $log = $log->loadDefaultValues();
  170. $log->attributes = $data;
  171. $log->save();
  172. return $log;
  173. }
  174. /**
  175. * 初始化
  176. */
  177. protected function initSms()
  178. {
  179. $config = Yii::$app->services->config->backendConfigAll();
  180. $gateway = $config['sms_default'] ?? 'aliyun';
  181. // 模板
  182. if ($gateway == 'aliyun') {
  183. $this->template = $config['sms_aliyun_template'] ?? '';
  184. } else {
  185. $this->template = $config['sms_tencent_template'] ?? '';
  186. }
  187. $this->config = [
  188. // HTTP 请求的超时时间(秒)
  189. 'timeout' => 5.0,
  190. // 默认发送配置
  191. 'default' => [
  192. // 网关调用策略,默认:顺序调用
  193. 'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,
  194. // 默认可用的发送网关
  195. 'gateways' => [$gateway],
  196. ],
  197. // 可用的网关配置
  198. 'gateways' => [
  199. 'errorlog' => [
  200. 'file' => Yii::getAlias('runtime') . '/easy-sms.log',
  201. ],
  202. 'aliyun' => [
  203. 'access_key_id' => $config['sms_aliyun_accesskeyid'] ?? '',
  204. 'access_key_secret' => $config['sms_aliyun_accesskeysecret'] ?? '',
  205. 'sign_name' => $config['sms_aliyun_sign_name'] ?? '',
  206. ],
  207. 'tencent' => [
  208. 'access_app_id' => $config['sms_tencent_accessappid'] ?? '',
  209. 'access_key_id' => $config['sms_tencent_accesskeyid'] ?? '',
  210. 'access_key_secret' => $config['sms_tencent_accesskeysecret'] ?? '',
  211. 'sign_name' => $config['sms_tencent_sign_name'] ?? '',
  212. ],
  213. ],
  214. ];
  215. }
  216. }
粤ICP备19079148号