ソースを参照

新版发送短信通知

Yahoo 4 年 前
親
コミット
b3a1ee381c

+ 61 - 0
app/common/library/sms/Config.php

@@ -0,0 +1,61 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+namespace app\common\library\sms;
+
+use Overtrue\EasySms\Strategies\OrderStrategy;
+
+/**
+ * EasySms配置类
+ * Class Config
+ * @package app\common\library\sms
+ */
+class Config
+{
+    /**
+     * 生成EasySms的配置项
+     * @param array $smsConfig
+     * @return array
+     */
+    public static function getEasySmsConfig(array $smsConfig): array
+    {
+        return [
+            // HTTP 请求的超时时间(秒)
+            'timeout' => 5.0,
+
+            // 默认发送配置
+            'default' => [
+                // 网关调用策略,默认:顺序调用
+                'strategy' => OrderStrategy::class,
+
+                // 默认可用的发送网关
+                'gateways' => [$smsConfig['default']],
+            ],
+            // 可用的网关配置
+            'gateways' => [
+                'aliyun' => [
+                    'access_key_id' => $smsConfig['engine']['aliyun']['AccessKeyId'],
+                    'access_key_secret' => $smsConfig['engine']['aliyun']['AccessKeySecret'],
+                    'sign_name' => $smsConfig['engine']['aliyun']['sign'],
+                ],
+                'qcloud' => [
+                    'sdk_app_id' => $smsConfig['engine']['qcloud']['SdkAppID'],
+                    'secret_id' => $smsConfig['engine']['qcloud']['AccessKeyId'],
+                    'secret_key' => $smsConfig['engine']['qcloud']['AccessKeySecret'],
+                    'sign_name' => $smsConfig['engine']['qcloud']['sign'],
+                ],
+                'qiniu' => [
+                    'access_key' => $smsConfig['engine']['qiniu']['AccessKey'],
+                    'secret_key' => $smsConfig['engine']['qiniu']['SecretKey'],
+                ],
+            ]
+        ];
+    }
+}

+ 97 - 42
app/common/library/sms/Driver.php

@@ -8,86 +8,141 @@
 // +----------------------------------------------------------------------
 // | Author: 萤火科技 <admin@yiovo.com>
 // +----------------------------------------------------------------------
-declare (strict_types = 1);
+declare (strict_types=1);
 
 namespace app\common\library\sms;
 
-use think\Exception;
+use app\common\library\helper;
+use cores\traits\ErrorTrait;
+use cores\exception\BaseException;
+use Overtrue\EasySms\EasySms;
+use Overtrue\EasySms\Exceptions\InvalidArgumentException;
+use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
 
 /**
  * 短信通知模块驱动
- * Class driver
+ * Class Driver
  * @package app\common\library\sms
  */
 class Driver
 {
-    private $config;    // 配置信息
-    private $engine;    // 当前短信服务引擎类
-    private $engineName;    // 当前短信引擎名称
+    use ErrorTrait;
 
-    private $error = '';    // 错误信息
+    // 后台短信配置信息
+    private $smsConfig;
+
+    // 当前短信平台 (后台设置)
+    private $gateway;
 
     /**
      * 构造方法
      * Driver constructor.
-     * @param array $config
-     * @throws Exception
+     * @param array $smsConfig
      */
-    public function __construct(array $config)
+    public function __construct(array $smsConfig)
     {
         // 配置信息
-        $this->config = $config;
-        // 当前引擎名称
-        $this->engineName = $this->config['default'];
-        // 实例化当前存储引擎
-        $this->engine = $this->getEngineClass();
+        $this->smsConfig = $smsConfig;
+        // 当前短信平台
+        $this->gateway = $this->smsConfig['default'];
     }
 
     /**
      * 发送短信通知
-     * @param string $sceneValue 场景值
-     * @param array $templateParams 短信模板变量
-     * @param array $sceneConfig 短信场景配置
-     * @param bool $isTest
+     * @param string $acceptPhone 接收的手机号
+     * @param string $templateCode 短信模板ID
+     * @param array $templateParams 短信模板参数
      * @return bool
+     * @throws BaseException
+     * @throws InvalidArgumentException
      */
-    public function sendSms(string $sceneValue, array $templateParams, array $sceneConfig = [], bool $isTest = false)
+    public function sendSms(string $acceptPhone, string $templateCode, array $templateParams): bool
     {
-        // 短信场景配置
-        $sceneConfig = array_merge($this->config['scene'][$sceneValue], $sceneConfig);
-        if ($isTest == false && !$sceneConfig['isEnable']) {
-            $this->error = '短信通知服务未开启,请在商户后台中设置';
-            return false;
+        // 实例化EasySms
+        $easySmsConfig = Config::getEasySmsConfig($this->smsConfig);
+        $easySms = new EasySms($easySmsConfig);
+        try {
+            // 执行发送短信
+            $result = $easySms->send($acceptPhone, [
+                'template' => $templateCode,
+                'data' => $this->getSmsTemplateData($templateParams),
+            ]);
+            // 短信发送成功的错误处理
+            $sendStatus = $this->resultHandle($result);
+        } catch (NoGatewayAvailableException $e) {
+            // 短信发送异常的错误处理
+            $sendStatus = false;
+            $this->exceptionHandle($e);
         }
-        // 执行发送短信
-        if (!$this->engine->sendSms($sceneConfig, $templateParams)) {
-            $this->error = $this->engine->getError();
-            return false;
+        // 记录日志
+        helper::logInfo('发送短信', [
+            'gateway' => $this->smsConfig['default'],
+            'acceptPhone' => $acceptPhone,
+            'templateCode' => $templateCode,
+            'templateParams' => $templateParams,
+            'sendStatus' => $sendStatus,
+        ]);
+        // 存在异常时抛错
+        $sendStatus === false && throwError($this->getError());
+        return $sendStatus;
+    }
+
+    /**
+     * 短信发送成功的错误处理
+     * @param array $result
+     * @return bool
+     */
+    private function resultHandle(array $result): bool
+    {
+        // 腾讯云短信错误: 模板ID不正确
+        if ($this->gateway === 'qcloud') {
+            $response = $result[$this->gateway]['result']['Response']['SendStatusSet'];
+            if (isset($response[0]) && isset($response[0]['Code']) && $response[0]['Code'] !== 'Ok') {
+                $this->error = '请检查后台短信平台参数和模板ID是否正确';
+                return false;
+            }
         }
         return true;
     }
 
     /**
-     * 获取错误信息
-     * @return mixed
+     * 短信发送异常的错误处理
+     * @param NoGatewayAvailableException $e
      */
-    public function getError()
+    private function exceptionHandle(NoGatewayAvailableException $e)
     {
-        return $this->error;
+        // 短信发送失败
+        $err = $e->getLastException();
+        if ($err instanceof \GuzzleHttp\Exception\ClientException) {
+            $body = (string)$err->getResponse()->getBody();
+            $result = helper::jsonDecode($body);
+            if (isset($result['Message'])) {
+                // $errMsg = $result['Message'];
+                $this->error = '请检查后台短信平台的参数设置';
+                return;
+            }
+            if (isset($result['message'])) {
+                $this->error = $result['message'];
+                return;
+            }
+        }
+//        if ($err instanceof \Overtrue\EasySms\Exceptions\GatewayErrorException) {
+//            $this->error = $err->getMessage();
+//            return;
+//        }
+        $this->error = $err->getMessage();
     }
 
     /**
-     * 获取当前的存储引擎
-     * @return mixed
-     * @throws Exception
+     * 生成短信模板数据
+     * @param array $templateParams
+     * @return array
      */
-    private function getEngineClass()
+    private function getSmsTemplateData(array $templateParams): array
     {
-        $classSpace = __NAMESPACE__ . '\\engine\\' . ucfirst($this->engineName);
-        if (!class_exists($classSpace)) {
-            throwError('未找到存储引擎类: ' . $this->engineName);
+        if ($this->gateway === 'qcloud') {
+            $templateParams = array_values($templateParams);
         }
-        return new $classSpace($this->config['engine'][$this->engineName]);
+        return $templateParams;
     }
-
 }

+ 20 - 1
app/common/model/store/Setting.php

@@ -194,10 +194,29 @@ class Setting extends BaseModel
                     'engine' => [
                         // 阿里云
                         'aliyun' => [
+                            'name' => '阿里云短信',
+                            'website' => 'https://dysms.console.aliyun.com/dysms.htm',
                             'AccessKeyId' => '',
                             'AccessKeySecret' => '',
                             'sign' => '萤火商城'   // 短信签名
-                        ]
+                        ],
+                        // 腾讯云
+                        'qcloud' => [
+                            'name' => '腾讯云短信',
+                            'website' => 'https://console.cloud.tencent.com/smsv2',
+                            'SdkAppID' => '',
+                            'AccessKeyId' => '',
+                            'AccessKeySecret' => '',
+                            'sign' => '萤火商城'   // 短信签名
+                        ],
+                        // 七牛云
+                        'qiniu' => [
+                            'name' => '七牛云短信',
+                            'website' => 'https://portal.qiniu.com/sms/dashboard',
+                            'AccessKey' => '',
+                            'SecretKey' => '',
+                            'sign' => '萤火商城'   // 短信签名
+                        ],
                     ],
                     // 短信通知场景
                     'scene' => [

+ 4 - 4
app/common/service/delivery/Express.php

@@ -56,7 +56,7 @@ class Express extends BaseService
      * 验证用户收货地址是否在配送范围
      * @return bool
      */
-    public function isIntraRegion()
+    public function isIntraRegion(): bool
     {
         if (!$this->cityId) return false;
         foreach ($this->data as $item) {
@@ -110,7 +110,7 @@ class Express extends BaseService
      * @return float|int|mixed
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\DbException
-     * @throws \think\db\exception\ModelNotFoundException\
+     * @throws \think\db\exception\ModelNotFoundException
      */
     private function getFinalFreight()
     {
@@ -140,7 +140,7 @@ class Express extends BaseService
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      */
-    private function freeshipping()
+    private function freeshipping(): bool
     {
         // 订单商品总金额
         $orderTotalPrice = helper::getArrayColumnSum($this->goodsList, 'total_price');
@@ -205,7 +205,7 @@ class Express extends BaseService
         foreach ($item['goodsList'] as $goodsItem) {
             // 如果商品为包邮,则不计算总量中
             if (!in_array($goodsItem['goods_id'], $item['free_goods_list'])) {
-                $goodsWeight = helper::bcmul($goodsItem['skuInfo']['goods_weight'], $goodsItem['total_num']);
+                $goodsWeight = helper::bcmul($goodsItem['skuInfo']['goods_weight'], $goodsItem['total_num']);
                 $totalWeight = helper::bcadd($totalWeight, $goodsWeight);
                 $totalNum = helper::bcadd($totalNum, $goodsItem['total_num']);
             }

+ 60 - 25
app/common/service/message/Basics.php

@@ -8,13 +8,15 @@
 // +----------------------------------------------------------------------
 // | Author: 萤火科技 <admin@yiovo.com>
 // +----------------------------------------------------------------------
-declare (strict_types = 1);
+declare (strict_types=1);
 
 namespace app\common\service\message;
 
-use app\common\model\store\Setting as SettingModel;
-use app\common\library\sms\Driver as SmsDriver;
+use cores\traits\ErrorTrait;
 use app\common\service\BaseService;
+use app\common\library\sms\Driver as SmsDriver;
+use app\common\model\store\Setting as SettingModel;
+use app\common\model\wxapp\Setting as WxappSettingModel;
 
 /**
  * 消息通知服务[基类]
@@ -23,12 +25,13 @@ use app\common\service\BaseService;
  */
 abstract class Basics extends BaseService
 {
+    use ErrorTrait;
+
     // 参数列表
     protected $param = [];
+
     // 商城ID
     protected $storeId;
-    // 错误信息
-    protected $error = '';
 
     /**
      * 构造方法
@@ -49,28 +52,70 @@ abstract class Basics extends BaseService
     abstract public function send(array $param);
 
     /**
-     * 发送短信提醒
-     * @param string $sceneValue
-     * @param array $templateParams
-     * @param array $sceneConfig
+     * 发送短信通知
+     * @param string $sceneValue 短信发送场景
+     * @param string $acceptPhone 接收的手机号
+     * @param array $templateParams 短信模板参数
      * @return bool
      * @throws \think\Exception
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      */
-    protected function sendSms(string $sceneValue, array $templateParams, array $sceneConfig = []): bool
+    protected function sendSms(string $sceneValue, string $acceptPhone, array $templateParams): bool
     {
         // 短信通知设置
-        $smsConfig = SettingModel::getItem('sms', $this->storeId);
+        $smsConfig = $this->getSmsConfig();
         // 短信服务
         $SmsDriver = new SmsDriver($smsConfig);
-        // 发送短信
-        if (!$SmsDriver->sendSms($sceneValue, $templateParams, $sceneConfig)) {
-            $this->error = $SmsDriver->getError();
+        try {
+            // 判断短信服务是否开启
+            $this->isEnableSms($smsConfig, $sceneValue);
+            // 获取短信模板ID
+            $templateCode = $this->getSmsTemplateCode($smsConfig, $sceneValue);
+            // 执行发送短信
+            $SmsDriver->sendSms($acceptPhone, $templateCode, $templateParams);
+            return true;
+        } catch (\Exception $e) {
+            $this->error = $e->getMessage();
             return false;
         }
-        return true;
+    }
+
+    /**
+     * 获取短信通知设置
+     * @return array|mixed
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getSmsConfig()
+    {
+        return SettingModel::getItem('sms', $this->storeId);
+    }
+
+    /**
+     * 判断短信服务是否开启
+     * @param array $smsConfig 短信设置
+     * @param string $sceneValue 短信发送场景
+     * @throws \cores\exception\BaseException
+     */
+    private function isEnableSms(array $smsConfig, string $sceneValue)
+    {
+        if (!$smsConfig['scene'][$sceneValue]['isEnable']) {
+            throwError('短信通知服务未开启,请在商户后台中设置');
+        }
+    }
+
+    /**
+     * 获取短信模板ID
+     * @param array $smsConfig 短信设置
+     * @param string $sceneValue 短信发送场景
+     * @return string
+     */
+    private function getSmsTemplateCode(array $smsConfig, string $sceneValue): string
+    {
+        return $smsConfig['scene'][$sceneValue]['templateCode'];
     }
 
     /**
@@ -84,14 +129,4 @@ abstract class Basics extends BaseService
     {
         return str_substr($content, $length);
     }
-
-    /**
-     * 获取错误信息
-     * @return mixed
-     */
-    public function getError()
-    {
-        return $this->error;
-    }
-
 }

+ 0 - 1
app/common/service/message/order/Payment.php

@@ -68,5 +68,4 @@ class Payment extends Basics
         $orderInfo = $this->param['order'];
         $this->sendSms(SettingSmsScene::ORDER_PAY, ['order_no' => $orderInfo['order_no']]);
     }
-
 }

+ 2 - 6
app/common/service/message/passport/Captcha.php

@@ -8,7 +8,7 @@
 // +----------------------------------------------------------------------
 // | Author: 萤火科技 <admin@yiovo.com>
 // +----------------------------------------------------------------------
-declare (strict_types = 1);
+declare (strict_types=1);
 
 namespace app\common\service\message\passport;
 
@@ -33,14 +33,10 @@ class Captcha extends Basics
      */
     public function send(array $param)
     {
-        // 短信api参数
-        $sceneConfig = ['acceptPhone' => $param['mobile']];
-        $templateParams = ['code' => $param['code']];
         // 发送短信
-        if (!$this->sendSms(SettingSmsScene::CAPTCHA, $templateParams, $sceneConfig)) {
+        if (!$this->sendSms(SettingSmsScene::CAPTCHA, $param['mobile'], ['code' => $param['code']])) {
             throwError('短信发送失败:' . $this->getError());
         }
         return true;
     }
-
 }