Sms.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <?php
  2. namespace modules\sms;
  3. use ba\module\moduleException;
  4. use ba\module\Server;
  5. use ba\Version;
  6. use FilesystemIterator;
  7. use RecursiveDirectoryIterator;
  8. use RecursiveIteratorIterator;
  9. use think\Exception;
  10. use think\facade\Cache;
  11. use think\facade\Db;
  12. use think\facade\Event;
  13. use think\facade\Validate;
  14. use app\common\model\Config;
  15. use app\common\library\Menu;
  16. use Overtrue\EasySms\EasySms;
  17. use modules\sms\library\Helper;
  18. use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
  19. class Sms
  20. {
  21. private $uid = 'sms';
  22. public static function send($templateCode, $mobile, $tplVar = []): bool
  23. {
  24. // 环境检查
  25. if (!extension_loaded('curl')) {
  26. throw new Exception('Please install curl extension');
  27. }
  28. if (!ini_get('curl.cainfo')) {
  29. throw new Exception('Please configure curl.cainfo in the php.ini file');
  30. }
  31. // 数据检查
  32. $validate = Validate::rule(['mobile' => 'require|mobile'])->message(['mobile' => 'Mobile format error']);
  33. if (!$validate->check(['mobile' => $mobile])) {
  34. throw new Exception($validate->getError());
  35. }
  36. $templateData = Db::name('sms_template')
  37. ->where('code', $templateCode)
  38. ->where('status', 1)
  39. ->find();
  40. if (!$templateData) {
  41. throw new Exception('SMS template does not exist');
  42. }
  43. if (!$templateData['template'] && !$templateData['content']) {
  44. throw new Exception('SMS template error');
  45. }
  46. // 配置检查
  47. $config = config('sms');
  48. if (!is_array($config['default']['gateways']) || count($config['default']['gateways']) <= 0) {
  49. throw new Exception('Please configure available service providers for SMS sending');
  50. }
  51. // 解析模板
  52. $template = Helper::analysisVariable($templateData['content'], $templateData['variables'], $tplVar);
  53. Event::trigger('TemplateAnalysisAfter', $template);
  54. try {
  55. $easySms = new EasySms($config);
  56. $res = $easySms->send($mobile, [
  57. 'template' => $templateData['template'],
  58. 'content' => $template['content'],
  59. 'data' => $template['variables'],
  60. ]);
  61. } catch (NoGatewayAvailableException $e) {
  62. throw new Exception($e->getLastException()->getMessage());
  63. }
  64. return true;
  65. }
  66. public function install()
  67. {
  68. $menu = [
  69. [
  70. 'type' => 'menu_dir',
  71. 'title' => '短信管理',
  72. 'name' => 'sms',
  73. 'path' => 'sms',
  74. 'icon' => 'el-icon-ChatLineRound',
  75. 'menu_type' => 'tab',
  76. 'children' => [
  77. [
  78. 'type' => 'menu',
  79. 'title' => '短信配置',
  80. 'name' => 'sms/config',
  81. 'path' => 'sms/config',
  82. 'icon' => 'el-icon-Setting',
  83. 'menu_type' => 'tab',
  84. 'component' => '/src/views/backend/sms/config.vue',
  85. 'keepalive' => '1',
  86. 'children' => [
  87. ['type' => 'button', 'title' => '查看', 'name' => 'sms/config/getConfigKey'],
  88. ['type' => 'button', 'title' => '修改配置', 'name' => 'sms/config/saveConfig'],
  89. ]
  90. ],
  91. [
  92. 'type' => 'menu',
  93. 'title' => '模板变量管理',
  94. 'name' => 'sms/variable',
  95. 'path' => 'sms/variable',
  96. 'icon' => 'fa fa-asterisk',
  97. 'menu_type' => 'tab',
  98. 'component' => '/src/views/backend/sms/variable/index.vue',
  99. 'keepalive' => '1',
  100. 'children' => [
  101. ['type' => 'button', 'title' => '查看', 'name' => 'sms/variable/index'],
  102. ['type' => 'button', 'title' => '添加', 'name' => 'sms/variable/add'],
  103. ['type' => 'button', 'title' => '编辑', 'name' => 'sms/variable/edit'],
  104. ['type' => 'button', 'title' => '删除', 'name' => 'sms/variable/del'],
  105. ]
  106. ],
  107. [
  108. 'type' => 'menu',
  109. 'title' => '短信模板管理',
  110. 'name' => 'sms/template',
  111. 'path' => 'sms/template',
  112. 'icon' => 'el-icon-Document',
  113. 'menu_type' => 'tab',
  114. 'component' => '/src/views/backend/sms/template/index.vue',
  115. 'keepalive' => '1',
  116. 'remark' => '不同服务商可能需要不同的模板ID,所以单个模板并不一定适用于所有服务商,若有轮询服务商发送的需求,多数情况需自行通过代码实现',
  117. 'children' => [
  118. ['type' => 'button', 'title' => '查看', 'name' => 'sms/template/index'],
  119. ['type' => 'button', 'title' => '添加', 'name' => 'sms/template/add'],
  120. ['type' => 'button', 'title' => '编辑', 'name' => 'sms/template/edit'],
  121. ['type' => 'button', 'title' => '删除', 'name' => 'sms/template/del'],
  122. ]
  123. ]
  124. ]
  125. ]
  126. ];
  127. Menu::create($menu);
  128. }
  129. public function uninstall()
  130. {
  131. Menu::delete('sms', true);
  132. }
  133. public function enable()
  134. {
  135. Menu::enable('sms');
  136. Config::addQuickEntrance('短信配置', '/admin/sms/config');
  137. // 恢复短信配置
  138. if (Version::compare('v1.1.0', \think\facade\Config::get('buildadmin.version'))) {
  139. $config = Cache::pull('sms-module-config');
  140. if ($config) {
  141. @file_put_contents(config_path() . 'sms.php', $config);
  142. }
  143. }
  144. }
  145. public function disable()
  146. {
  147. Menu::disable('sms');
  148. Config::removeQuickEntrance('短信配置');
  149. // 备份短信配置
  150. if (Version::compare('v1.1.0', \think\facade\Config::get('buildadmin.version'))) {
  151. $config = @file_get_contents(config_path() . 'sms.php');
  152. if ($config) {
  153. Cache::set('sms-module-config', $config, 3600);
  154. }
  155. }
  156. }
  157. public function update()
  158. {
  159. // 兼容系统v1.1.2的语言包按需加载
  160. // 寻找安装时备份中的lang/pages文件,如果有,还原到lang/backend内而不是原位置
  161. $ebakDir = root_path() . 'modules' . DIRECTORY_SEPARATOR . 'ebak' . DIRECTORY_SEPARATOR;
  162. $zipFile = $ebakDir . $this->uid . '-install.zip';
  163. $zipDir = false;
  164. if (is_file($zipFile)) {
  165. try {
  166. $zipDir = Server::unzip($zipFile);
  167. } catch (moduleException|Exception $e) {
  168. // skip
  169. }
  170. }
  171. if ($zipDir) {
  172. $oldBaInputs = [
  173. path_transform('web\src\lang\pages\zh-cn\sms\config.ts'),
  174. path_transform('web\src\lang\pages\en\sms\config.ts'),
  175. path_transform('web\src\lang\pages\zh-cn\sms\template.ts'),
  176. path_transform('web\src\lang\pages\en\sms\template.ts'),
  177. path_transform('web\src\lang\pages\zh-cn\sms\variable.ts'),
  178. path_transform('web\src\lang\pages\en\sms\variable.ts'),
  179. ];
  180. foreach ($oldBaInputs as $oldBaInput) {
  181. @unlink(root_path() . $oldBaInput);
  182. }
  183. foreach (
  184. $iterator = new RecursiveIteratorIterator(
  185. new RecursiveDirectoryIterator($zipDir, FilesystemIterator::SKIP_DOTS),
  186. RecursiveIteratorIterator::SELF_FIRST
  187. ) as $item
  188. ) {
  189. $ebakFile = path_transform($iterator->getSubPathName());
  190. if (!$item->isDir() && in_array($ebakFile, $oldBaInputs)) {
  191. $newFileName = str_replace(DIRECTORY_SEPARATOR . 'pages' . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR . 'backend' . DIRECTORY_SEPARATOR, $ebakFile);
  192. copy($item, root_path() . $newFileName);
  193. }
  194. }
  195. }
  196. deldir($zipDir);
  197. del_empty_dir(root_path() . 'web/src/lang/pages/en');
  198. del_empty_dir(root_path() . 'web/src/lang/pages/zh-cn');
  199. }
  200. }