diff --git a/addons/qcloudsms/.addonrc b/addons/qcloudsms/.addonrc new file mode 100644 index 0000000..c8d7f60 --- /dev/null +++ b/addons/qcloudsms/.addonrc @@ -0,0 +1 @@ +{"files":[],"license":"regular","licenseto":"10789","licensekey":"o6mJaMsK4NpyHf9q psldq2tLvR9vsAcBNF\/P7cl4cmp82g9FH9Xpm96xhYw=","domains":[],"licensecodes":[],"validations":[]} \ No newline at end of file diff --git a/addons/qcloudsms/Qcloudsms.php b/addons/qcloudsms/Qcloudsms.php new file mode 100644 index 0000000..13cd038 --- /dev/null +++ b/addons/qcloudsms/Qcloudsms.php @@ -0,0 +1,197 @@ +<?php + +namespace addons\qcloudsms; + +use addons\qcloudsms\library\SmsSingleSender; +use addons\qcloudsms\library\SmsVoicePromptSender; +use addons\qcloudsms\library\SmsVoiceverifyCodeSender; +use addons\qcloudsms\library\TtsVoiceSender; +use think\Addons; +use think\Config; + +/** + * 插件 + */ +class Qcloudsms extends Addons +{ + private $appid = null; + private $appkey = null; + private $config = null; + private $sender = null; + private $sendError = ''; + + public function ConfigInit() + { + $this->config = $this->getConfig(); + //如果使用语音短信 更换成语音短信模板 + if ($this->config['isVoice'] == 1) { + $this->config['template'] = $this->config['voiceTemplate']; + //语音短信 需要另行设置Aappid 与Appkey + $this->appid = $this->config['voiceAppid']; + $this->appkey = $this->config['voiceAppkey']; + } else { + $this->appid = $this->config['appid']; + $this->appkey = $this->config['appkey']; + } + } + + /** + * 短信发送行为 + * @param Sms $params + * @return boolean + */ + public function smsSend(&$params) + { + $this->ConfigInit(); + try { + if ($this->config['isTemplateSender'] == 1) { + $templateID = $this->config['template'][$params->event]; + if ($this->config['isVoice'] != 1) { + //普通短信发送 + $this->sender = new SmsSingleSender($this->appid, $this->appkey); + $result = $this->sender->sendWithParam("86", $params['mobile'], $templateID, ["{$params->code}"], $this->config['sign'], "", ""); + } else { + //语音短信发送 + $this->sender = new TtsVoiceSender($this->appid, $this->appkey); + //参数: 国家码,手机号、模板ID、模板参数、播放次数(可选字段)、用户的session内容,服务器端原样返回(可选字段) + $result = $this->sender->send("86", $params['mobile'], $templateID, [$params->code]); + } + } else { + //判断是否是语音短信 + if ($this->config['isVoice'] != 1) { + $this->sender = new SmsSingleSender($this->appid, $this->appkey); + //参数:短信类型{1营销短信,0普通短信 }、国家码、手机号、短信内容、扩展码(可留空)、服务的原样返回的参数 + $result = $this->sender->send($params['type'], '86', $params['mobile'], $params['msg'], "", ""); + } else { + $this->sender = new SmsVoiceVerifyCodeSender($this->appid, $this->appkey); + //参数:国家码、手机号、短信内容、播放次数(默认2次)、服务的原样返回的参数 + $result = $this->sender->send('86', $params['mobile'], $params['msg']); + } + } + + $rsp = json_decode($result, true); + if ($rsp['result'] == 0 && $rsp['errmsg'] == 'OK') { + return true; + } else { + //记录错误信息 + $this->setError($rsp); + return false; + } + } catch (\Exception $e) { + $this->setError($e->getMessage()); + } + return false; + } + + /** + * 短信发送通知 + * @param array $params + * @return boolean + */ + public function smsNotice(&$params) + { + $this->ConfigInit(); + try { + if ($this->config['isTemplateSender'] == 1) { + $templateID = $this->config['template'][$params['template']]; + + if ($this->config['isVoice'] != 1) { + //普通短信发送 + $this->sender = new SmsSingleSender($this->appid, $this->appkey); + $result = $this->sender->sendWithParam("86", $params['mobile'], $templateID, ["{$params['msg']}"], $this->config['sign'], "", ""); + } else { + //语音短信发送 + $this->sender = new TtsVoiceSender($this->appid, $this->appkey); + //参数: 国家码,手机号、模板ID、模板参数、播放次数(可选字段)、用户的session内容,服务器端原样返回(可选字段) + $result = $this->sender->send("86", $params['mobile'], $templateID, [$params['msg']]); + } + } else { + //判断是否是语音短信 + if ($this->config['isVoice'] != 1) { + $this->sender = new SmsSingleSender($this->appid, $this->appkey); + //参数:短信类型{1营销短信,0普通短信 }、国家码、手机号、短信内容、扩展码(可留空)、服务的原样返回的参数 + $result = $this->sender->send($params['type'], '86', $params['mobile'], $params['msg'], "", ""); + } else { + $this->sender = new SmsVoicePromptSender($this->appid, $this->appkey); + //参数:国家码、手机号、语音类型(目前固定为2)、短信内容、播放次数(默认2次)、服务的原样返回的参数 + $result = $this->sender->send('86', $params['mobile'], 2, $params['msg']); + } + } + $rsp = (array)json_decode($result, true); + if ($rsp['result'] == 0 && $rsp['errmsg'] == 'OK') { + return true; + } else { + //记录错误信息 + $this->setError($rsp); + return false; + } + } catch (\Exception $e) { + var_dump($e); + exit(); + } + } + + /** + * 记录失败信息 + * @param [type] $err [description] + */ + private function setError($err) + { + $this->sendError = $err; + } + + /** + * 获取失败信息 + * @return [type] [description] + */ + public function getError() + { + return $this->sendError; + } + + /** + * 检测验证是否正确 + * @param Sms $params + * @return boolean + */ + public function smsCheck(&$params) + { + return true; + } + + /** + * 插件安装方法 + * @return bool + */ + public function install() + { + return true; + } + + /** + * 插件卸载方法 + * @return bool + */ + public function uninstall() + { + return true; + } + + /** + * 插件启用方法 + * @return bool + */ + public function enable() + { + return true; + } + + /** + * 插件禁用方法 + * @return bool + */ + public function disable() + { + return true; + } +} diff --git a/addons/qcloudsms/config.php b/addons/qcloudsms/config.php new file mode 100644 index 0000000..aa03ed7 --- /dev/null +++ b/addons/qcloudsms/config.php @@ -0,0 +1,139 @@ +<?php + +return array( + array( + 'name' => 'appid', + 'title' => '应用AppID', + 'type' => 'string', + 'content' => + array(), + 'value' => '', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'appkey', + 'title' => '应用AppKEY', + 'type' => 'string', + 'content' => + array(), + 'value' => '', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'voiceAppid', + 'title' => '语音短信AppID', + 'type' => 'string', + 'content' => + array(), + 'value' => '', + 'rule' => 'required', + 'msg' => '使用语音短信必须设置', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'voiceAppkey', + 'title' => '语音短信AppKEY', + 'type' => 'string', + 'content' => + array(), + 'value' => '', + 'rule' => 'required', + 'msg' => '使用语音短信必须设置', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'sign', + 'title' => '签名', + 'type' => 'string', + 'content' => + array(), + 'value' => 'your sign', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'isVoice', + 'title' => '是否使用语音短信', + 'type' => 'radio', + 'content' => + array( + 0 => '否', + 1 => '是', + ), + 'value' => '0', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'isTemplateSender', + 'title' => '是否使用短信模板发送', + 'type' => 'radio', + 'content' => + array( + 0 => '否', + 1 => '是', + ), + 'value' => '1', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'template', + 'title' => '短信模板', + 'type' => 'array', + 'content' => + array(), + 'value' => + array( + 'register' => '', + 'resetpwd' => '', + 'changepwd' => '', + 'profile' => '', + ), + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), + array( + 'name' => 'voiceTemplate', + 'title' => '语音短信模板', + 'type' => 'array', + 'content' => + array(), + 'value' => + array( + 'register' => '', + 'resetpwd' => '', + 'changepwd' => '', + 'profile' => '', + ), + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '', + ), +); diff --git a/addons/qcloudsms/controller/Index.php b/addons/qcloudsms/controller/Index.php new file mode 100644 index 0000000..eb595df --- /dev/null +++ b/addons/qcloudsms/controller/Index.php @@ -0,0 +1,15 @@ +<?php + +namespace addons\qcloudsms\controller; + +use think\addons\Controller; + +class Index extends Controller +{ + + public function index() + { + $this->error("当前插件暂无前台页面"); + } + +} diff --git a/addons/qcloudsms/info.ini b/addons/qcloudsms/info.ini new file mode 100644 index 0000000..ef35e76 --- /dev/null +++ b/addons/qcloudsms/info.ini @@ -0,0 +1,10 @@ +name = qcloudsms +title = 腾讯云短信发送插件 +intro = 腾讯云短信发送插件 +author = Seacent +website = https://www.seacent.com +version = 1.0.3 +state = 1 +url = /addons/qcloudsms +license = regular +licenseto = 10789 diff --git a/addons/qcloudsms/library/FileVoiceSender.php b/addons/qcloudsms/library/FileVoiceSender.php new file mode 100644 index 0000000..4ed9f07 --- /dev/null +++ b/addons/qcloudsms/library/FileVoiceSender.php @@ -0,0 +1,69 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + + +/** + * 按语音文件fid发送语音通知类 + * + */ +class FileVoiceSender +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendfvoice"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * + * 按语音文件fid发送语音通知 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $phoneNumber 不带国家码的手机号 + * @param string $fid 语音文件fid + * @param string $playtimes 播放次数,可选,最多3次,默认2次 + * @param string $ext 用户的session内容,服务端原样返回,可选字段,不需要可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function send($nationCode, $phoneNumber, $fid, $playtimes = 2, $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + // 按照协议组织 post 包体 + $data = new \stdClass(); + $tel = new \stdClass(); + $tel->nationcode = "".$nationCode; + $tel->mobile = "".$phoneNumber; + $data->tel = $tel; + $data->fid = $fid; + $data->playtimes = $playtimes; + + // app凭证 + $data->sig = $this->util->calculateSig($this->appkey, $random, + $curTime, array($phoneNumber)); + + // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败 + $data->time = $curTime; + $data->ext = $ext; + + return $this->util->sendCurlPost($wholeUrl, $data); + } +} diff --git a/addons/qcloudsms/library/SmsMobileStatusPuller.php b/addons/qcloudsms/library/SmsMobileStatusPuller.php new file mode 100644 index 0000000..40976fb --- /dev/null +++ b/addons/qcloudsms/library/SmsMobileStatusPuller.php @@ -0,0 +1,91 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + +/** + * 拉取单个手机短信状态类 + * + */ +class SmsMobileStatusPuller +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/pullstatus4mobile"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * 拉取回执结果 + * + * @param int $type 拉取类型,0表示回执结果,1表示回复信息 + * @param string $nationCode 国家码,如 86 为中国 + * @param string $mobile 不带国家码的手机号 + * @param int $beginTime 开始时间(unix timestamp) + * @param int $endTime 结束时间(unix timestamp) + * @param int $max 拉取最大条数,最多100 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + private function pull($type, $nationCode, $mobile, $beginTime, $endTime, $max) + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + $data = new \stdClass(); + $data->sig = $this->util->calculateSigForPuller($this->appkey, $random, $curTime); + $data->time = $curTime; + $data->type = $type; + $data->max = $max; + $data->begin_time = $beginTime; + $data->end_time = $endTime; + $data->nationcode = $nationCode; + $data->mobile = $mobile; + + return $this->util->sendCurlPost($wholeUrl, $data); + } + + /** + * 拉取回执结果 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $mobile 不带国家码的手机号 + * @param int $beginTime 开始时间(unix timestamp) + * @param int $endTime 结束时间(unix timestamp) + * @param int $max 拉取最大条数,最多100 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function pullCallback($nationCode, $mobile, $beginTime, $endTime, $max) + { + return $this->pull(0, $nationCode, $mobile, $beginTime, $endTime, $max); + } + + /** + * 拉取回复信息 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $mobile 不带国家码的手机号 + * @param int $beginTime 开始时间(unix timestamp) + * @param int $endTime 结束时间(unix timestamp) + * @param int $max 拉取最大条数,最多100 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function pullReply($nationCode, $mobile, $beginTime, $endTime, $max) + { + return $this->pull(1, $nationCode, $mobile, $beginTime, $endTime, $max); + } +} \ No newline at end of file diff --git a/addons/qcloudsms/library/SmsMultiSender.php b/addons/qcloudsms/library/SmsMultiSender.php new file mode 100644 index 0000000..18ef526 --- /dev/null +++ b/addons/qcloudsms/library/SmsMultiSender.php @@ -0,0 +1,99 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + +/** + * 群发短信类 + * + */ +class SmsMultiSender +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/sendmultisms2"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * 普通群发 + * + * 普通群发需明确指定内容,如果有多个签名,请在内容中以【】的方式添加到信息内容中, + * 否则系统将使用默认签名。 + * + * + * @param int $type 短信类型,0 为普通短信,1 营销短信 + * @param string $nationCode 国家码,如 86 为中国 + * @param array $phoneNumbers 不带国家码的手机号列表 + * @param string $msg 信息内容,必须与申请的模板格式一致,否则将返回错误 + * @param string $extend 扩展码,可填空串 + * @param string $ext 服务端原样返回的参数,可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function send($type, $nationCode, $phoneNumbers, $msg, $extend = "", $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + $data = new \stdClass(); + $data->tel = $this->util->phoneNumbersToArray($nationCode, $phoneNumbers); + $data->type = $type; + $data->msg = $msg; + $data->sig = $this->util->calculateSig($this->appkey, $random, + $curTime, $phoneNumbers); + $data->time = $curTime; + $data->extend = $extend; + $data->ext = $ext; + + return $this->util->sendCurlPost($wholeUrl, $data); + } + + /** + * 指定模板群发 + * + * + * @param string $nationCode 国家码,如 86 为中国 + * @param array $phoneNumbers 不带国家码的手机号列表 + * @param int $templId 模板id + * @param array $params 模板参数列表,如模板 {1}...{2}...{3},那么需要带三个参数 + * @param string $sign 签名,如果填空串,系统会使用默认签名 + * @param string $extend 扩展码,可填空串 + * @param string $ext 服务端原样返回的参数,可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function sendWithParam($nationCode, $phoneNumbers, $templId, $params, + $sign = "", $extend = "", $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + $data = new \stdClass(); + $data->tel = $this->util->phoneNumbersToArray($nationCode, $phoneNumbers); + $data->sign = $sign; + $data->tpl_id = $templId; + $data->params = $params; + $data->sig = $this->util->calculateSigForTemplAndPhoneNumbers( + $this->appkey, $random, $curTime, $phoneNumbers); + $data->time = $curTime; + $data->extend = $extend; + $data->ext = $ext; + + return $this->util->sendCurlPost($wholeUrl, $data); + } +} diff --git a/addons/qcloudsms/library/SmsSenderUtil.php b/addons/qcloudsms/library/SmsSenderUtil.php new file mode 100644 index 0000000..8e4d9db --- /dev/null +++ b/addons/qcloudsms/library/SmsSenderUtil.php @@ -0,0 +1,211 @@ +<?php + +namespace addons\qcloudsms\library; + +/** + * 发送Util类 + * + */ +class SmsSenderUtil +{ + /** + * 生成随机数 + * + * @return int 随机数结果 + */ + public function getRandom() + { + return rand(100000, 999999); + } + + /** + * 生成签名 + * + * @param string $appkey sdkappid对应的appkey + * @param string $random 随机正整数 + * @param string $curTime 当前时间 + * @param array $phoneNumbers 手机号码 + * @return string 签名结果 + */ + public function calculateSig($appkey, $random, $curTime, $phoneNumbers) + { + $phoneNumbersString = $phoneNumbers[0]; + for ($i = 1; $i < count($phoneNumbers); $i++) { + $phoneNumbersString .= ("," . $phoneNumbers[$i]); + } + + return hash("sha256", "appkey=".$appkey."&random=".$random + ."&time=".$curTime."&mobile=".$phoneNumbersString); + } + + /** + * 生成签名 + * + * @param string $appkey sdkappid对应的appkey + * @param string $random 随机正整数 + * @param string $curTime 当前时间 + * @param array $phoneNumbers 手机号码 + * @return string 签名结果 + */ + public function calculateSigForTemplAndPhoneNumbers($appkey, $random, + $curTime, $phoneNumbers) + { + $phoneNumbersString = $phoneNumbers[0]; + for ($i = 1; $i < count($phoneNumbers); $i++) { + $phoneNumbersString .= ("," . $phoneNumbers[$i]); + } + + return hash("sha256", "appkey=".$appkey."&random=".$random + ."&time=".$curTime."&mobile=".$phoneNumbersString); + } + + public function phoneNumbersToArray($nationCode, $phoneNumbers) + { + $i = 0; + $tel = array(); + do { + $telElement = new \stdClass(); + $telElement->nationcode = $nationCode; + $telElement->mobile = $phoneNumbers[$i]; + array_push($tel, $telElement); + } while (++$i < count($phoneNumbers)); + + return $tel; + } + + /** + * 生成签名 + * + * @param string $appkey sdkappid对应的appkey + * @param string $random 随机正整数 + * @param string $curTime 当前时间 + * @param array $phoneNumber 手机号码 + * @return string 签名结果 + */ + public function calculateSigForTempl($appkey, $random, $curTime, $phoneNumber) + { + $phoneNumbers = array($phoneNumber); + + return $this->calculateSigForTemplAndPhoneNumbers($appkey, $random, + $curTime, $phoneNumbers); + } + + /** + * 生成签名 + * + * @param string $appkey sdkappid对应的appkey + * @param string $random 随机正整数 + * @param string $curTime 当前时间 + * @return string 签名结果 + */ + public function calculateSigForPuller($appkey, $random, $curTime) + { + return hash("sha256", "appkey=".$appkey."&random=".$random + ."&time=".$curTime); + } + + /** + * 生成上传文件授权 + * + * @param string $appkey sdkappid对应的appkey + * @param string $random 随机正整数 + * @param string $curTime 当前时间 + * @param array $fileSha1Sum 文件sha1sum + * @return string 授权结果 + */ + public function calculateAuth($appkey, $random, $curTime, $fileSha1Sum) + { + return hash("sha256", "appkey=".$appkey."&random=".$random + ."&time=".$curTime."&content-sha1=".$fileSha1Sum); + } + + /** + * 生成sha1sum + * + * @param string $content 内容 + * @return string 内容sha1散列值 + */ + public function sha1sum($content) + { + return hash("sha1", $content); + } + + /** + * 发送请求 + * + * @param string $url 请求地址 + * @param array $dataObj 请求内容 + * @return string 应答json字符串 + */ + public function sendCurlPost($url, $dataObj) + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60); + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($dataObj)); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json; charset=utf-8', + 'Content-Length: ' . strlen(json_encode($dataObj))) + ); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); + + $ret = curl_exec($curl); + if (false == $ret) { + // curl_exec failed + $result = "{ \"result\":" . -2 . ",\"errmsg\":\"" . curl_error($curl) . "\"}"; + } else { + $rsp = curl_getinfo($curl, CURLINFO_HTTP_CODE); + if (200 != $rsp) { + $result = "{ \"result\":" . -1 . ",\"errmsg\":\"". $rsp + . " " . curl_error($curl) ."\"}"; + } else { + $result = $ret; + } + } + + curl_close($curl); + + return $result; + } + + /** + * 发送请求 + * + * @param string $req 请求对象 + * @return string 应答json字符串 + */ + public function fetch($req) + { + $curl = curl_init(); + + curl_setopt($curl, CURLOPT_URL, $req->url); + curl_setopt($curl, CURLOPT_HTTPHEADER, $req->headers); + curl_setopt($curl, CURLOPT_POSTFIELDS, $req->body); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); + + $result = curl_exec($curl); + + if (false == $result) { + // curl_exec failed + $result = "{ \"result\":" . -2 . ",\"errmsg\":\"" . curl_error($curl) . "\"}"; + } else { + $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + if (200 != $code) { + $result = "{ \"result\":" . -1 . ",\"errmsg\":\"". $rsp + . " " . curl_error($curl) ."\"}"; + } + } + curl_close($curl); + + return $result; + } +} diff --git a/addons/qcloudsms/library/SmsSingleSender.php b/addons/qcloudsms/library/SmsSingleSender.php new file mode 100644 index 0000000..dc0f104 --- /dev/null +++ b/addons/qcloudsms/library/SmsSingleSender.php @@ -0,0 +1,107 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + +/** + * 单发短信类 + * + */ +class SmsSingleSender +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/sendsms"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * 普通单发 + * + * 普通单发需明确指定内容,如果有多个签名,请在内容中以【】的方式添加到信息内容中,否则系统将使用默认签名。 + * + * @param int $type 短信类型,0 为普通短信,1 营销短信 + * @param string $nationCode 国家码,如 86 为中国 + * @param string $phoneNumber 不带国家码的手机号 + * @param string $msg 信息内容,必须与申请的模板格式一致,否则将返回错误 + * @param string $extend 扩展码,可填空串 + * @param string $ext 服务端原样返回的参数,可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function send($type, $nationCode, $phoneNumber, $msg, $extend = "", $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + // 按照协议组织 post 包体 + $data = new \stdClass(); + $tel = new \stdClass(); + $tel->nationcode = "".$nationCode; + $tel->mobile = "".$phoneNumber; + + $data->tel = $tel; + $data->type = (int)$type; + $data->msg = $msg; + $data->sig = hash("sha256", + "appkey=".$this->appkey."&random=".$random."&time=" + .$curTime."&mobile=".$phoneNumber, FALSE); + $data->time = $curTime; + $data->extend = $extend; + $data->ext = $ext; + + return $this->util->sendCurlPost($wholeUrl, $data); + } + + /** + * 指定模板单发 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $phoneNumber 不带国家码的手机号 + * @param int $templId 模板 id + * @param array $params 模板参数列表,如模板 {1}...{2}...{3},那么需要带三个参数 + * @param string $sign 签名,如果填空串,系统会使用默认签名 + * @param string $extend 扩展码,可填空串 + * @param string $ext 服务端原样返回的参数,可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function sendWithParam($nationCode, $phoneNumber, $templId = 0, $params, + $sign = "", $extend = "", $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + // 按照协议组织 post 包体 + $data = new \stdClass(); + $tel = new \stdClass(); + $tel->nationcode = "".$nationCode; + $tel->mobile = "".$phoneNumber; + + $data->tel = $tel; + $data->sig = $this->util->calculateSigForTempl($this->appkey, $random, + $curTime, $phoneNumber); + $data->tpl_id = $templId; + $data->params = $params; + $data->sign = $sign; + $data->time = $curTime; + $data->extend = $extend; + $data->ext = $ext; + + return $this->util->sendCurlPost($wholeUrl, $data); + } +} diff --git a/addons/qcloudsms/library/SmsStatusPuller.php b/addons/qcloudsms/library/SmsStatusPuller.php new file mode 100644 index 0000000..26ce88f --- /dev/null +++ b/addons/qcloudsms/library/SmsStatusPuller.php @@ -0,0 +1,75 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + +/** + * 拉取短信状态类 + * + */ +class SmsStatusPuller +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://yun.tim.qq.com/v5/tlssmssvr/pullstatus"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * 拉取回执结果 + * + * @param int $type 拉取类型,0表示回执结果,1表示回复信息 + * @param int $max 最大条数,最多100 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + private function pull($type, $max) + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + $data = new \stdClass(); + $data->sig = $this->util->calculateSigForPuller($this->appkey, $random, $curTime); + $data->time = $curTime; + $data->type = $type; + $data->max = $max; + + return $this->util->sendCurlPost($wholeUrl, $data); + } + + /** + * 拉取回执结果 + * + * @param int $max 拉取最大条数,最多100 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function pullCallback($max) + { + return $this->pull(0, $max); + } + + /** + * 拉取回复信息 + * + * @param int $max 拉取最大条数,最多100 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function pullReply($max) + { + return $this->pull(1, $max); + } +} \ No newline at end of file diff --git a/addons/qcloudsms/library/SmsVoicePromptSender.php b/addons/qcloudsms/library/SmsVoicePromptSender.php new file mode 100644 index 0000000..4ee87b5 --- /dev/null +++ b/addons/qcloudsms/library/SmsVoicePromptSender.php @@ -0,0 +1,71 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + +/** + * 发送语音通知类 + * + */ +class SmsVoicePromptSender +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://yun.tim.qq.com/v5/tlsvoicesvr/sendvoiceprompt"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * + * 发送语音通知 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $phoneNumber 不带国家码的手机号 + * @param string $prompttype 语音类型,目前固定为2 + * @param string $msg 信息内容,必须与申请的模板格式一致,否则将返回错误 + * @param string $playtimes 播放次数,可选,最多3次,默认2次 + * @param string $ext 用户的session内容,服务端原样返回,可选字段,不需要可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function send($nationCode, $phoneNumber, $prompttype, $msg, $playtimes = 2, $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + // 按照协议组织 post 包体 + $data = new \stdClass(); + $tel = new \stdClass(); + $tel->nationcode = "".$nationCode; + $tel->mobile = "".$phoneNumber; + + $data->tel = $tel; + // 通知内容,utf8编码,支持中文英文、数字及组合,需要和语音内容模版相匹配 + $data->promptfile = $msg; + // 固定值 2 + $data->prompttype = $prompttype; + $data->playtimes = $playtimes; + // app凭证 + $data->sig = hash("sha256", + "appkey=".$this->appkey."&random=".$random."&time=" + .$curTime."&mobile=".$phoneNumber, FALSE); + // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败 + $data->time = $curTime; + $data->ext = $ext; + return $this->util->sendCurlPost($wholeUrl, $data); + } +} diff --git a/addons/qcloudsms/library/SmsVoiceVerifyCodeSender.php b/addons/qcloudsms/library/SmsVoiceVerifyCodeSender.php new file mode 100644 index 0000000..f59ff2b --- /dev/null +++ b/addons/qcloudsms/library/SmsVoiceVerifyCodeSender.php @@ -0,0 +1,67 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + +/** + * 发送语音验证码类 + * + */ +class SmsVoiceVerifyCodeSender +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://yun.tim.qq.com/v5/tlsvoicesvr/sendvoice"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * 发送语音验证码 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $phoneNumber 不带国家码的手机号 + * @param string $msg 信息内容,必须与申请的模板格式一致,否则将返回错误 + * @param int $playtimes 播放次数,可选,最多3次,默认2次 + * @param string $ext 用户的session内容,服务端原样返回,可选字段,不需要可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function send($nationCode, $phoneNumber, $msg, $playtimes = 2, $ext = "") + { + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + // 按照协议组织 post 包体 + $data = new \stdClass(); + $tel = new \stdClass(); + $tel->nationcode = "".$nationCode; + $tel->mobile = "".$phoneNumber; + + $data->tel = $tel; + $data->msg = $msg; + $data->playtimes = $playtimes; + // app凭证 + $data->sig = hash("sha256", + "appkey=".$this->appkey."&random=".$random."&time=" + .$curTime."&mobile=".$phoneNumber, FALSE); + // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败 + $data->time = $curTime; + $data->ext = $ext; + + return $this->util->sendCurlPost($wholeUrl, $data); + } +} diff --git a/addons/qcloudsms/library/TtsVoiceSender.php b/addons/qcloudsms/library/TtsVoiceSender.php new file mode 100644 index 0000000..b45f55e --- /dev/null +++ b/addons/qcloudsms/library/TtsVoiceSender.php @@ -0,0 +1,77 @@ +<?php + +namespace addons\qcloudsms\library; + +use addons\qcloudsms\library\SmsSenderUtil; + + +/** + * 指定模板发送语音通知类 + * + */ +class TtsVoiceSender +{ + private $url; + private $appid; + private $appkey; + private $util; + + /** + * 构造函数 + * + * @param string $appid sdkappid + * @param string $appkey sdkappid对应的appkey + */ + public function __construct($appid, $appkey) + { + $this->url = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendtvoice"; + $this->appid = $appid; + $this->appkey = $appkey; + $this->util = new SmsSenderUtil(); + } + + /** + * + * 指定模板发送语音短信 + * + * @param string $nationCode 国家码,如 86 为中国 + * @param string $phoneNumber 不带国家码的手机号 + * @param int $templId 模板 id + * @param array $params 模板参数列表,如模板 {1}...{2}...{3},需要带三个参数 + * @param string $playtimes 播放次数,可选,最多3次,默认2次 + * @param string $ext 用户的session内容,服务端原样返回,可选字段,不需要可填空串 + * @return string 应答json字符串,详细内容参见腾讯云协议文档 + */ + public function send($nationCode, $phoneNumber, $templId, $params, $playtimes = 2, $ext = "") + { + /*var_dump($nationCode); + var_dump($phoneNumber); + var_dump($templId); + var_dump($params); + var_dump($playtimes); + exit();*/ + $random = $this->util->getRandom(); + $curTime = time(); + $wholeUrl = $this->url . "?sdkappid=" . $this->appid . "&random=" . $random; + + // 按照协议组织 post 包体 + $data = new \stdClass(); + $tel = new \stdClass(); + $tel->nationcode = "".$nationCode; + $tel->mobile = "".$phoneNumber; + $data->tel = $tel; + $data->tpl_id = $templId; + $data->params = $params; + $data->playtimes = $playtimes; + + // app凭证 + $data->sig = $this->util->calculateSig($this->appkey, $random, + $curTime, array($phoneNumber)); + + // unix时间戳,请求发起时间,如果和系统时间相差超过10分钟则会返回失败 + $data->time = $curTime; + $data->ext = $ext; + //var_dump($data);exit(); + return $this->util->sendCurlPost($wholeUrl, $data); + } +} diff --git a/application/admin/controller/litestore/Litestoregoods.php b/application/admin/controller/litestore/Litestoregoods.php index 2fb34e5..da99668 100644 --- a/application/admin/controller/litestore/Litestoregoods.php +++ b/application/admin/controller/litestore/Litestoregoods.php @@ -2,24 +2,18 @@ namespace app\admin\controller\litestore; -use addons\fastexport\library\ExportLib; use app\admin\model\litestore\Litestoregoodsspec; -use app\api\model\Goods; use app\common\controller\Backend; use app\admin\model\litestore\Litestorespec as SpecModel; use app\admin\model\litestore\Litestorespecvalue as SpecValueModel; -use PHPExcel; -use PHPExcel_Style; -use PHPExcel_Style_Alignment; -use PHPExcel_Style_Border; -use PHPExcel_Style_Fill; -use PhpOffice\PhpSpreadsheet\Cell\DataType; -use PhpOffice\PhpSpreadsheet\Spreadsheet; -use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Reader\Csv; +use PhpOffice\PhpSpreadsheet\Reader\Xls; +use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use think\Db; -use think\Exception; use think\exception\PDOException; + /** * * @@ -34,6 +28,7 @@ class Litestoregoods extends Backend * @var \app\admin\model\litestore\Litestoregoods */ protected $model = null; + protected $spec_model = null; public function _initialize() { @@ -41,7 +36,8 @@ class Litestoregoods extends Backend $this->SpecModel = new SpecModel; $this->SpecValueModel = new SpecValueModel; - $this->model = new \app\admin\model\litestore\Litestoregoods; + $this->model = new \app\admin\model\litestore\Litestoregoods; + $this->spec_model = new \app\admin\model\litestore\Litestoregoodsspec(); $this->view->assign("specTypeList", $this->model->getSpecTypeList()); $this->view->assign("deductStockTypeList", $this->model->getDeductStockTypeList()); $this->view->assign("goodsStatusList", $this->model->getGoodsStatusList()); @@ -100,6 +96,8 @@ class Litestoregoods extends Backend return json($result); } + $this->assign('template', $this->request->domain() . '/static/template/商品导入模板.xlsx'); + return $this->view->fetch(); } @@ -292,6 +290,7 @@ class Litestoregoods extends Backend ->select(); $list = collection($list)->toArray(); foreach ($list as &$item) { + $item['category_name'] = Db::name('litestore_category')->where('id', $item['category_id'])->value('name'); if (!empty($item['spec_sku_id'])) { $sku_value = explode('_', $item['spec_sku_id']); if (count($sku_value) > 1) { @@ -320,35 +319,57 @@ class Litestoregoods extends Backend $objPHPExcel->setActiveSheetIndex(0) ->setCellValue('A1', '商品ID') ->setCellValue('B1', '商品名称') - ->setCellValue('C1', '商品规格') - ->setCellValue('D1', '商品原价') - ->setCellValue('E1', '折扣') - ->setCellValue('F1', '商品现价') - ->setCellValue('G1', '已销数量') - ->setCellValue('H1', '导出时间'); + ->setCellValue('C1', '搜索关键词') + ->setCellValue('D1', '分类名称') + ->setCellValue('E1', '活动名称(新人用户=1,限时秒杀=2,进口商品=4)') + ->setCellValue('F1', '商品规格(10=单规格,20=多规格)') + ->setCellValue('G1', '库存') + ->setCellValue('H1', '商品规格') + ->setCellValue('I1', '商品原价') + ->setCellValue('J1', '折扣') + ->setCellValue('K1', '商品现价') + ->setCellValue('L1', '已销数量') + ->setCellValue('M1', '商品状态(10=上架,20=下架)') + ->setCellValue('N1', '首页展示(0=未展示,1=已展示)') + ->setCellValue('O1', '导出时间'); //设置A列水平居中 $objPHPExcel->setActiveSheetIndex(0)->getStyle('A1')->getAlignment() ->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER); //设置单元格宽度 //6.循环刚取出来的数组,将数据逐一添加到excel表格。 - $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('B')->setWidth(10); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('A')->setWidth(10); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('B')->setWidth(20); $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('C')->setWidth(20); $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('D')->setWidth(20); $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('E')->setWidth(20); $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('F')->setWidth(20); $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('G')->setWidth(20); $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('H')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('I')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('J')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('K')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('L')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('M')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('N')->setWidth(20); + $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension('O')->setWidth(20); for ($i = 0; $i < count($list); $i++) { $objPHPExcel->getActiveSheet()->setCellValue('A' . ($i + 2), $list[$i]['goods_id']);//ID $objPHPExcel->getActiveSheet()->setCellValue('B' . ($i + 2), $list[$i]['goods_name']);//商品名称 - $objPHPExcel->getActiveSheet()->setCellValue('C' . ($i + 2), $list[$i]['sku_value']);//商品规格 - $objPHPExcel->getActiveSheet()->setCellValue('D' . ($i + 2), $list[$i]['line_price']);//原价 - $objPHPExcel->getActiveSheet()->setCellValue('E' . ($i + 2), $list[$i]['discount']);//ID - $objPHPExcel->getActiveSheet()->setCellValue('F' . ($i + 2), $list[$i]['goods_price']);//标签码 - $objPHPExcel->getActiveSheet()->setCellValue('G' . ($i + 2), $list[$i]['goods_sales']);//ID - $objPHPExcel->getActiveSheet()->setCellValue('H' . ($i + 2), datetime(time()));//标签码 + $objPHPExcel->getActiveSheet()->setCellValue('C' . ($i + 2), $list[$i]['keywords']);//商品规格 + $objPHPExcel->getActiveSheet()->setCellValue('D' . ($i + 2), $list[$i]['category_name']);//原价 + $objPHPExcel->getActiveSheet()->setCellValue('E' . ($i + 2), $list[$i]['activity_id']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('F' . ($i + 2), $list[$i]['spec_type']);//标签码 + $objPHPExcel->getActiveSheet()->setCellValue('G' . ($i + 2), $list[$i]['stock_num']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('H' . ($i + 2), $list[$i]['sku_value']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('I' . ($i + 2), $list[$i]['line_price']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('J' . ($i + 2), $list[$i]['discount']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('K' . ($i + 2), $list[$i]['goods_price']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('L' . ($i + 2), $list[$i]['goods_sales']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('M' . ($i + 2), $list[$i]['goods_status']);//ID + $objPHPExcel->getActiveSheet()->setCellValue('N' . ($i + 2), $list[$i]['is_index']);//标签码 + $objPHPExcel->getActiveSheet()->setCellValue('O' . ($i + 2), datetime(time()));//标签码 } ob_end_clean(); ob_start(); @@ -366,6 +387,100 @@ class Litestoregoods extends Backend exit(); } + /** + *导入 + * + * */ + public function import() + { + $file = $this->request->request('file'); + if (!$file) { + $this->error(__('Parameter %s can not be empty', 'file')); + } + $filePath = ROOT_PATH . DS . 'public' . DS . $file; + if (!is_file($filePath)) { + $this->error(__('No results were found')); + } + //实例化reader + $ext = pathinfo($filePath, PATHINFO_EXTENSION); + if (!in_array($ext, ['csv', 'xls', 'xlsx'])) { + $this->error(__('Unknown data format')); + } + if ($ext === 'csv') { + $file = fopen($filePath, 'r'); + $filePath = tempnam(sys_get_temp_dir(), 'import_csv'); + $fp = fopen($filePath, "w"); + $n = 0; + while ($line = fgets($file)) { + $line = rtrim($line, "\n\r\0"); + $encoding = mb_detect_encoding($line, ['utf-8', 'gbk', 'latin1', 'big5']); + if ($encoding != 'utf-8') { + $line = mb_convert_encoding($line, 'utf-8', $encoding); + } + if ($n == 0 || preg_match('/^".*"$/', $line)) { + fwrite($fp, $line . "\n"); + } else { + fwrite($fp, '"' . str_replace(['"', ','], ['""', '","'], $line) . "\"\n"); + } + $n++; + } + fclose($file) || fclose($fp); + $reader = new Csv(); + } elseif ($ext === 'xls') { + $reader = new Xls(); + } else { + $reader = new Xlsx(); + } + set_time_limit(0); + //加载文件 + $insert = []; + try { + if (!$PHPExcel = $reader->load($filePath)) { + $this->error(__('Unknown data format')); + } + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allColumn = $currentSheet->getHighestDataColumn(); //取得最大的列号 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + $maxColumnNumber = Coordinate::columnIndexFromString($allColumn); // 一共多少列 + for ($i = 2; $i <= $allRow; $i++) { + if (!empty($PHPExcel->getActiveSheet()->getCell("A" . $i)->getValue())) { + $insert[$i]['goods_name'] = $PHPExcel->getActiveSheet()->getCell("A" . $i)->getValue();//商品名称 + $insert[$i]['keywords'] = $PHPExcel->getActiveSheet()->getCell("B" . $i)->getValue();//关键词 + $category = $PHPExcel->getActiveSheet()->getCell("C" . $i)->getValue() ?? '';//类别 + $insert[$i]['category_id'] = Db::name('litestore_category')->where('name', $category)->value('id') ?? ''; + $insert[$i]['activity_id'] = $PHPExcel->getActiveSheet()->getCell("D" . $i)->getValue() ?? '';//活动类型 + $insert[$i]['is_index'] = $PHPExcel->getActiveSheet()->getCell("I" . $i)->getValue() ?? '';//是否首页 + $insert[$i]['goods_status'] = $PHPExcel->getActiveSheet()->getCell("H" . $i)->getValue() ?? '';//商品状态 + $spec_insert[$i]['stock_num'] = $PHPExcel->getActiveSheet()->getCell("E" . $i)->getValue() ?? '';//库存 + $spec_insert[$i]['goods_price'] = $PHPExcel->getActiveSheet()->getCell("G" . $i)->getValue() ?? '';//现价 + $spec_insert[$i]['line_price'] = $PHPExcel->getActiveSheet()->getCell("F" . $i)->getValue() ?? '';//原价 + } + } + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + if (!$insert) { + $this->error(__('No rows were updated')); + } + try { + + $res_goods = $this->model->saveAll($insert); + foreach ($spec_insert as $key => &$v) { + $v['goods_id'] = $res_goods[$key]['goods_id']; + } + unset($v); + $this->spec_model->saveAll($spec_insert); + } catch (PDOException $exception) { + $msg = $exception->getMessage(); + if (preg_match("/.+Integrity constraint violation: 1062 Duplicate entry '(.+)' for key '(.+)'/is", $msg, $matches)) { + $msg = "导入失败,包含【{$matches[1]}】的记录已存在"; + }; + $this->error($msg); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } } diff --git a/application/admin/view/litestore/litestoregoods/index.html b/application/admin/view/litestore/litestoregoods/index.html index 71a9491..4acdba0 100644 --- a/application/admin/view/litestore/litestoregoods/index.html +++ b/application/admin/view/litestore/litestoregoods/index.html @@ -17,18 +17,19 @@ <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('litestore/litestoregoods/del')?'':'hide'}" title="{:__('Delete')}"><i class="fa fa-trash"></i> {:__('Delete')}</a> - <!-- <a href="javascript:;"--> - <!-- class="btn btn-danger btn-import {:$auth->check('litestore/litestoregoods/import')?'':'hide'}"--> - <!-- title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload"--> - <!-- data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i>--> - <!-- {:__('Import')}</a>--> - <!-- <a href="javascript:;"--> + <a href="javascript:;" + class="btn btn-danger btn-import {:$auth->check('litestore/litestoregoods/import')?'':'hide'}" + title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" + data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> + {:__('Import')}</a> + <!-- <a href="javascript:;"--> <!-- class="btn btn-info btn-change btn-export btn-disable" data-params="action=export"--> <!-- data-url="litestore/litestoregoods/export"--> <!-- title="{:__('批量导出')}"><i class="fa fa-download"></i>{:__('批量导出')}</a>--> <a href="javascript:;" class="btn btn-info btn-export {:$auth->check('litestore/litestoregoods/export')?'':'hide'}" title="{:__('Export')}" id="btn-export-file"><i class="fa fa-download"></i> 批量导出</a> + <a href="{$template}" class="btn btn-info"><i class="fa fa-folder-open"></i> {:__('导入模板下载')}</a> <!-- <div class="dropdown btn-group {:$auth->check('litestore/litestoregoods/multi')?'':'hide'}">--> <!-- <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>--> diff --git a/application/api/controller/Notify.php b/application/api/controller/Notify.php index e843b16..7de45ef 100644 --- a/application/api/controller/Notify.php +++ b/application/api/controller/Notify.php @@ -24,59 +24,61 @@ class Notify extends Api public function orderNotify() { $paytype = $this->request->param('type'); - $pay = Service::checkNotify($paytype); + $pay = Service::checkNotify($paytype); if (!$pay) { return '签名错误'; } $data = $pay->verify(); - if (!is_array($data)) $data = json_decode($data,true); - $model = new \app\api\model\Order(); - $goodsmodel = new \app\api\model\OrderGoods(); - $skumodel = new \app\api\model\GoodsSpec(); + if (!is_array($data)) $data = json_decode($data, true); + $model = new \app\api\model\Order(); + $goodsmodel = new \app\api\model\OrderGoods(); + $skumodel = new \app\api\model\GoodsSpec(); $goods_model = new \app\api\model\Goods(); - $buymodel = new \app\api\model\UserBuylist(); + $buymodel = new \app\api\model\UserBuylist(); try { $out_trade_no = $data['out_trade_no']; - $order = $model - ->where('order_no',$out_trade_no) - ->where('pay_status','10') - ->where('order_status','10') + $order = $model + ->where('order_no', $out_trade_no) + ->where('pay_status', '10') + ->where('order_status', '10') ->find(); if (!$order) $this->error('订单不存在'); - $order->pay_status = '20'; + $order->pay_status = '20'; $order->transaction_id = $data['transaction_id']; - $order->pay_time = time(); + $order->pay_time = time(); $order->isUpdate()->save(); // 减少库存 $sales_actual = 0; - $goods_ids = []; - $list = $goodsmodel->where('order_id',$order['id'])->select(); - foreach ($list as $key => $value){ - $goods_ids[] = $value['goods_id']; - $sales_actual+=$value['total_num']; - if ($value['deduct_stock_type'] == 20){ - $skumodel->where('goods_spec_id',$value['goods_spec_id'])->setDec('stock_num',$value['total_num']); + $goods_ids = []; + $list = $goodsmodel->where('order_id', $order['id'])->select(); + foreach ($list as $key => $value) { + $goods_ids[] = $value['goods_id']; + $sales_actual += $value['total_num']; + if ($value['deduct_stock_type'] == 20) { + $skumodel->where('goods_spec_id', $value['goods_spec_id'])->setDec('stock_num', $value['total_num']); + //增加规格销量 + $skumodel->where('goods_spec_id', $value['goods_spec_id'])->setInc('goods_sales', $value['total_num']); } } // 增加销量 - $goods_model->whereIn('goods_id',$goods_ids)->setInc('sales_actual',$sales_actual); + $goods_model->whereIn('goods_id', $goods_ids)->setInc('sales_actual', $sales_actual); // 判断是否邀请成功 $user = \app\api\model\User::get($order['user_id']); if ($user['invite_user_id'] > 0 && $user['invite_status'] != 1) { $user->invite_status = '1'; $user->isUpdate()->save(); - $coupon = Db::name('coupon')->where('id',1)->find(); - if ($coupon['endtime'] > time()){ + $coupon = Db::name('coupon')->where('id', 1)->find(); + if ($coupon['endtime'] > time()) { $data = []; $time = time(); - for ($i=1;$i<=$coupon['send_number'];$i++){ + for ($i = 1; $i <= $coupon['send_number']; $i++) { $data[] = [ - 'user_id' => $user->invite_user_id, - 'coupon_id' => $coupon['id'], - 'name' => $coupon['name'], - 'price' => $coupon['price'], + 'user_id' => $user->invite_user_id, + 'coupon_id' => $coupon['id'], + 'name' => $coupon['name'], + 'price' => $coupon['price'], 'full_price' => $coupon['full_price'], - 'endtime' => $time+$coupon['days']*86400, + 'endtime' => $time + $coupon['days'] * 86400, 'createtime' => $time, ]; } @@ -85,24 +87,24 @@ class Notify extends Api } // 加入我常买 $data = []; - foreach ($list as $key => $value){ + foreach ($list as $key => $value) { $data[] = [ - 'user_id' => $order['user_id'], + 'user_id' => $order['user_id'], 'goods_id' => $value['goods_id'], - 'sku_id' => $value['goods_spec_id'], + 'sku_id' => $value['goods_spec_id'], ]; } $buymodel->isUpdate(false)->saveAll($data); // 给后台发送新订单提醒 - $client = new Client(); - $domain = config('socketio.domain'); + $client = new Client(); + $domain = config('socketio.domain'); $http_port = config('socketio.http_port'); - $client->request('POST', $domain.":{$http_port}/", [ + $client->request('POST', $domain . ":{$http_port}/", [ 'form_params' => [ - 'type' => 'publish', + 'type' => 'publish', 'content' => '您有新的订单,请注意查收', - 'to' => '', - ] + 'to' => '', + ], ]); } catch (Exception $e) { } @@ -116,40 +118,40 @@ class Notify extends Api public function refund() { $paytype = $this->request->param('type'); - $pay = Service::checkNotify($paytype,'refund'); + $pay = Service::checkNotify($paytype, 'refund'); if (!$pay) { echo '签名错误'; return; } - $data = $pay->verify(); - $data = json_decode($data,true); - $model = new \app\api\model\Order(); - $goodsmodel = new \app\api\model\OrderGoods(); - $skumodel = new \app\api\model\GoodsSpec(); + $data = $pay->verify(); + $data = json_decode($data, true); + $model = new \app\api\model\Order(); + $goodsmodel = new \app\api\model\OrderGoods(); + $skumodel = new \app\api\model\GoodsSpec(); $goods_model = new \app\api\model\Goods(); try { $out_trade_no = $data['out_refund_no']; - $order = $model - ->where('refund_no',$out_trade_no) - ->where('pay_status','20') - ->where('order_status','10') + $order = $model + ->where('refund_no', $out_trade_no) + ->where('pay_status', '20') + ->where('order_status', '10') ->find(); if (!$order) $this->error('订单不存在'); - $order->refund_id = $data['refund_id']; - $order->refund_time = time(); + $order->refund_id = $data['refund_id']; + $order->refund_time = time(); $order->order_status = '20'; $order->isUpdate()->save(); // 增加库存 $sales_actual = 0; - $list = $goodsmodel->where('order_id',$order['id'])->select(); - foreach ($list as $key => $value){ - $sales_actual+=$value['total_num']; - if ($value['deduct_stock_type'] == 20){ - $skumodel->where('goods_spec_id',$value['goods_spec_id'])->setInc('stock_num',$value['total_num']); + $list = $goodsmodel->where('order_id', $order['id'])->select(); + foreach ($list as $key => $value) { + $sales_actual += $value['total_num']; + if ($value['deduct_stock_type'] == 20) { + $skumodel->where('goods_spec_id', $value['goods_spec_id'])->setInc('stock_num', $value['total_num']); } } // 减少销量 - $goods_model->setDec('sales_actual',$sales_actual); + $goods_model->setDec('sales_actual', $sales_actual); //你可以在此编写订单逻辑 } catch (Exception $e) { } diff --git a/application/config.php b/application/config.php index bf82e30..5be330b 100644 --- a/application/config.php +++ b/application/config.php @@ -18,7 +18,7 @@ return [ // 应用命名空间 'app_namespace' => 'app', // 应用调试模式 - 'app_debug' => Env::get('app.debug', true), + 'app_debug' => Env::get('app.debug', false), // 应用Trace 'app_trace' => Env::get('app.trace', false), // 应用模式状态 diff --git a/application/extra/site.php b/application/extra/site.php index c22afe2..986d257 100644 --- a/application/extra/site.php +++ b/application/extra/site.php @@ -4,7 +4,7 @@ return array ( 'name' => '橙子优选', 'beian' => '', 'cdnurl' => '', - 'version' => '1.0.1', + 'version' => '1.0.2', 'timezone' => 'Asia/Shanghai', 'forbiddenip' => '', 'languages' => @@ -41,6 +41,7 @@ return array ( 'category2' => '分类二', 'custom' => '自定义', ), +<<<<<<< HEAD 'user_agreement' => '<p><div><p><strong>一、序言</strong> <br /> 1、您使用简书软件和/或服务,即视为您签署了本协议,表明您自愿接受本协议全部条款的约束,本协议将构成您与上海佰集科技有限公司(以下称我公司)就“简书”软件及服务(以下统称“简书”)之间具有约束力的法律文件。无论您是进入简书浏览网页,还是在简书上发布任何内容,或者是直接或通过各类方式(如站外API引用等)间接使用简书的行为,都将被视作已无条件接受本声明所涉全部内容。</p> <p>2、我公司有权利对本协议进行修改,我公司将在简书相关页面公告或发送通知等方式公布修改的内容,修改后的协议一经公布即有效的代替原协议。如果您不同意本协议的修改,请立即停止访问或使用简书或取消已经获得的服务;如果您选择继续访问或使用简书,则视为您已接受本协议的修改。</p> <p>3、本协议所列明的条款,并不能完全涵盖您与我公司之间所有的权利和义务。因此,我公司不定期公布的其他声明、规则、子协议等均视为本协议之补充协议,为本协议不可分割的组成部分,与本协议具有同等法律效力。</p> @@ -48,6 +49,15 @@ return array ( 'privacy_agreement' => '<p><br /></p><div><p>1、帐号的取得 (1)您确认,在您开始使用简书时,已经具备中华人民共和国法律规定的与您行为相适应的民事行为能力。若您不具备前述与您行为相适应的民事行为能力,则您及您的监护人应依照法律规定承担因此而导致的一切后果。</p> <p>(2)您注册成功后,即成为简书注册用户,用户须对在简书的注册信息的真实性、合法性、有效性承担全部责任。您可自行创建、修改昵称,但用户名和昵称的命名及使用应遵守相关法律法规并符合网络道德,不得冒充他人或恶意注册使人误认;不得利用他人的名义发布任何信息;不得恶意使用注册帐号导致其他用户误认;用户名和昵称中不能含有任何侮辱、诽谤、淫秽或暴力等侵害他人合法权益或违反公序良俗的词语。如您违反前述规定,简书有权随时限制或拒绝您使用该账号,甚至注销该账号。 (3)用户名的管理 1)请勿以党和国家领导人或其他名人的真实姓名、字、号、艺名、笔名、头衔等注册和使用昵称(如确为本人,需要提交相关证据并通过审核方可允许使用); 2)请勿以国家组织机构或其他组织机构的名称等注册和使用昵称(如确为该机构,需要提交相关证据并通过审核方可允许使用); 3)请勿注册和使用与其他网友相同、相仿的用户名或昵称; 4)请勿注册和使用不文明、不健康的用户名和昵称; 5)请勿注册和使用易产生歧义、引起他人误解或带有各种奇形怪状符号的用户名和昵称。 6)用户以虚假信息骗取账号名称注册,或账号头像、简介等注册信息存在违法和不良信息的,简书将暂停或注销。</p> <p>2、请您妥善保管您注册时填写的用户帐号和密码,不要将帐号密码告知他人,因您原因导致帐号或密码泄露而造成的法律后果由用户负责。同时,您还应当对以此帐号登录进行的所有活动和事件承担全部后果与法律责任。</p> +======= + 'user_agreement' => '<p><div><p><strong>一、序言</strong> <br /> 1、您使用简书软件和/或服务,即视为您签署了本协议,表明您自愿接受本协议全部条款的约束,本协议将构成您与上海佰集科技有限公司(以下称我公司)就“简书”软件及服务(以下统称“简书”)之间具有约束力的法律文件。无论您是进入简书浏览网页,还是在简书上发布任何内容,或者是直接或通过各类方式(如站外API引用等)间接使用简书的行为,都将被视作已无条件接受本声明所涉全部内容。</p> +<p>2、我公司有权利对本协议进行修改,我公司将在简书相关页面公告或发送通知等方式公布修改的内容,修改后的协议一经公布即有效的代替原协议。如果您不同意本协议的修改,请立即停止访问或使用简书或取消已经获得的服务;如果您选择继续访问或使用简书,则视为您已接受本协议的修改。</p> +<p>3、本协议所列明的条款,并不能完全涵盖您与我公司之间所有的权利和义务。因此,我公司不定期公布的其他声明、规则、子协议等均视为本协议之补充协议,为本协议不可分割的组成部分,与本协议具有同等法律效力。</p> +<p>4、如本协议与简书的补充协议不一致,以补充协议内容为准。</p></div></p><p><br /></p>', + 'privacy_agreement' => '<p><br /></p><div><p>1、帐号的取得 (1)您确认,在您开始使用简书时,已经具备中华人民共和国法律规定的与您行为相适应的民事行为能力。若您不具备前述与您行为相适应的民事行为能力,则您及您的监护人应依照法律规定承担因此而导致的一切后果。</p> +<p>(2)您注册成功后,即成为简书注册用户,用户须对在简书的注册信息的真实性、合法性、有效性承担全部责任。您可自行创建、修改昵称,但用户名和昵称的命名及使用应遵守相关法律法规并符合网络道德,不得冒充他人或恶意注册使人误认;不得利用他人的名义发布任何信息;不得恶意使用注册帐号导致其他用户误认;用户名和昵称中不能含有任何侮辱、诽谤、淫秽或暴力等侵害他人合法权益或违反公序良俗的词语。如您违反前述规定,简书有权随时限制或拒绝您使用该账号,甚至注销该账号。 (3)用户名的管理 1)请勿以党和国家领导人或其他名人的真实姓名、字、号、艺名、笔名、头衔等注册和使用昵称(如确为本人,需要提交相关证据并通过审核方可允许使用); 2)请勿以国家组织机构或其他组织机构的名称等注册和使用昵称(如确为该机构,需要提交相关证据并通过审核方可允许使用); 3)请勿注册和使用与其他网友相同、相仿的用户名或昵称; 4)请勿注册和使用不文明、不健康的用户名和昵称; 5)请勿注册和使用易产生歧义、引起他人误解或带有各种奇形怪状符号的用户名和昵称。 6)用户以虚假信息骗取账号名称注册,或账号头像、简介等注册信息存在违法和不良信息的,简书将暂停或注销。</p> +<p>2、请您妥善保管您注册时填写的用户帐号和密码,不要将帐号密码告知他人,因您原因导致帐号或密码泄露而造成的法律后果由用户负责。同时,您还应当对以此帐号登录进行的所有活动和事件承担全部后果与法律责任。</p> +>>>>>>> 7daf6f7b9eb6b739e54a4f431ec20b925f200636 <p>3、若您的帐号或密码遗失,可以通过注册信息按照相关步骤找回密码,若您发现账号遭到未授权的使用或存在其他安全漏洞的情况,应立即告知我公司。</p></div><p><br /></p><p><br /></p>', 'notice' => '于10月6日 全场半价全场半价 于10月6日 全场半价全场半价 于10月6日 全场半价全场半价', 'advert' => '/uploads/20220126/fc7a7c9c0c0ea799bafe33a82c67d71b.png', diff --git a/public/assets/js/backend/litestore/litestoregoods.js b/public/assets/js/backend/litestore/litestoregoods.js index 8c76c06..8b286e7 100644 --- a/public/assets/js/backend/litestore/litestoregoods.js +++ b/public/assets/js/backend/litestore/litestoregoods.js @@ -11,6 +11,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'litestor edit_url: 'litestore/litestoregoods/edit', del_url: 'litestore/litestoregoods/del', multi_url: 'litestore/litestoregoods/multi', + import_url: 'litestore/litestoregoods/import', table: 'litestore_goods', } }); diff --git a/public/static/template/商品导入模板.xlsx b/public/static/template/商品导入模板.xlsx new file mode 100644 index 0000000..e9b9584 Binary files /dev/null and b/public/static/template/商品导入模板.xlsx differ diff --git a/thinkphp/base.php b/thinkphp/base.php index 92c4fa5..bf2dbbb 100644 --- a/thinkphp/base.php +++ b/thinkphp/base.php @@ -29,6 +29,8 @@ defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS); defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录 defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀 defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀 +// PUBLIC_PATH +define('PUBLIC_PATH', ROOT_PATH . 'public'); // 环境常量 define('IS_CLI', PHP_SAPI == 'cli' ? true : false);