SystemStorageServices.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\services\system\config;
  12. use app\dao\system\config\SystemStorageDao;
  13. use app\services\BaseServices;
  14. use crmeb\exceptions\AdminException;
  15. use crmeb\services\CacheService;
  16. use crmeb\services\FormBuilder;
  17. use app\services\other\UploadService;
  18. /**
  19. * Class SystemStorageServices
  20. * @package app\services\system\config
  21. */
  22. class SystemStorageServices extends BaseServices
  23. {
  24. /**
  25. * SystemStorageServices constructor.
  26. * @param SystemStorageDao $dao
  27. */
  28. public function __construct(SystemStorageDao $dao)
  29. {
  30. $this->dao = $dao;
  31. }
  32. /**
  33. * @param array $where
  34. * @return array
  35. */
  36. public function getList(array $where)
  37. {
  38. [$page, $limit] = $this->getPageValue();
  39. $config = $this->getStorageConfig((int)$where['type']);
  40. $where['access_key'] = $config['accessKey'];
  41. $list = $this->dao->getList($where, ['*'], $page, $limit, 'add_time');
  42. foreach ($list as &$item) {
  43. $item['cname'] = str_replace('https://', '', $item['domain']);
  44. $item['_add_time'] = date('Y-m-d H:i:s', $item['add_time']);
  45. $item['_update_time'] = date('Y-m-d H:i:s', $item['update_time']);
  46. $service = UploadService::init($item['type']);
  47. $region = $service->getRegion();
  48. foreach ($region as $value) {
  49. if (strstr($item['region'], $value['value'])) {
  50. $item['_region'] = $value['label'];
  51. }
  52. }
  53. }
  54. $count = $this->dao->count($where);
  55. return compact('list', 'count');
  56. }
  57. /**
  58. * @param int $type
  59. * @return array
  60. * @throws \FormBuilder\Exception\FormBuilderException
  61. */
  62. public function getFormStorage(int $type)
  63. {
  64. $upload = UploadService::init($type);
  65. $config = $this->getStorageConfig($type);
  66. $ruleConfig = [];
  67. if (!$config['accessKey']) {
  68. $ruleConfig = [
  69. FormBuilder::input('accessKey', 'AccessKeyId', $config['accessKey'] ?? '')->required(),
  70. FormBuilder::input('secretKey', 'AccessKeySecret', $config['secretKey'] ?? '')->required(),
  71. ];
  72. }
  73. if ($type === 4 && isset($config['appid']) && !$config['appid']) {
  74. $ruleConfig[] = FormBuilder::input('appid', 'APPID', $config['appid'] ?? '')->required();
  75. }
  76. $rule = [
  77. FormBuilder::input('name', '空间名称')->required(),
  78. FormBuilder::select('region', '空间区域')->options($upload->getRegion())->required(),
  79. FormBuilder::radio('acl', '读写权限', 'public-read')->options([
  80. ['label' => '公共读(推荐)', 'value' => 'public-read'],
  81. ['label' => '公共读写', 'value' => 'public-read-write'],
  82. ])->required(),
  83. ];
  84. $rule = array_merge($ruleConfig, $rule);
  85. return create_form('添加云空间', $rule, '/system/config/storage/' . $type);
  86. }
  87. /**
  88. * @param int $type
  89. * @return array
  90. */
  91. public function getStorageConfig(int $type)
  92. {
  93. $config = [
  94. 'accessKey' => '',
  95. 'secretKey' => ''
  96. ];
  97. switch ($type) {
  98. case 2://七牛
  99. $config = [
  100. 'accessKey' => sys_config('qiniu_accessKey', ''),
  101. 'secretKey' => sys_config('qiniu_secretKey', ''),
  102. ];
  103. break;
  104. case 3:// oss 阿里云
  105. $config = [
  106. 'accessKey' => sys_config('accessKey', ''),
  107. 'secretKey' => sys_config('secretKey', ''),
  108. ];
  109. break;
  110. case 4:// cos 腾讯云
  111. $config = [
  112. 'accessKey' => sys_config('tengxun_accessKey', ''),
  113. 'secretKey' => sys_config('tengxun_secretKey', ''),
  114. 'appid' => sys_config('tengxun_appid', ''),
  115. ];
  116. break;
  117. case 5:// cos 京东云
  118. $config = [
  119. 'accessKey' => sys_config('jd_accessKey', ''),
  120. 'secretKey' => sys_config('jd_secretKey', ''),
  121. 'storageRegion' => sys_config('jd_storageRegion', ''),
  122. ];
  123. break;
  124. case 6:// cos 华为云
  125. $config = [
  126. 'accessKey' => sys_config('hw_accessKey', ''),
  127. 'secretKey' => sys_config('hw_secretKey', ''),
  128. ];
  129. break;
  130. case 7:// cos 天翼云
  131. $config = [
  132. 'accessKey' => sys_config('ty_accessKey', ''),
  133. 'secretKey' => sys_config('ty_secretKey', ''),
  134. ];
  135. break;
  136. }
  137. return $config;
  138. }
  139. /**
  140. * @param int $type
  141. * @return array
  142. * @throws \FormBuilder\Exception\FormBuilderException
  143. */
  144. public function getFormStorageConfig(int $type)
  145. {
  146. $config = $this->getStorageConfig($type);
  147. $rule = [
  148. FormBuilder::hidden('type', $type),
  149. FormBuilder::input('accessKey', 'AccessKeyId', $config['accessKey'] ?? '')->required(),
  150. FormBuilder::input('secretKey', 'AccessKeySecret', $config['secretKey'] ?? '')->required(),
  151. ];
  152. if ($type === 4) {
  153. $rule[] = FormBuilder::input('appid', 'APPID', $config['appid'] ?? '')->required();
  154. }
  155. if ($type === 5) {
  156. $rule[] = FormBuilder::input('storageRegion', 'storageRegion', $config['storageRegion'] ?? '')->required();
  157. }
  158. return create_form('配置信息', $rule, '/system/config/storage/config');
  159. }
  160. /**
  161. * 删除空间
  162. * @param int $id
  163. * @return bool
  164. * @throws \think\db\exception\DataNotFoundException
  165. * @throws \think\db\exception\DbException
  166. * @throws \think\db\exception\ModelNotFoundException
  167. */
  168. public function deleteStorage(int $id)
  169. {
  170. $storageInfo = $this->dao->get(['is_delete' => 0, 'id' => $id]);
  171. if (!$storageInfo) {
  172. throw new AdminException(400608);
  173. }
  174. if ($storageInfo->status) {
  175. throw new AdminException(400609);
  176. }
  177. try {
  178. $upload = UploadService::init($storageInfo->type);
  179. $res = $upload->deleteBucket($storageInfo->name, $storageInfo->region);
  180. if (false === $res) {
  181. throw new AdminException($upload->getError());
  182. }
  183. } catch (\Throwable $e) {
  184. throw new AdminException($e->getMessage());
  185. }
  186. $storageInfo->is_delete = 1;
  187. $storageInfo->save();
  188. CacheService::clear();
  189. return true;
  190. }
  191. public function saveConfig(int $type, array $data)
  192. {
  193. //保存配置信息
  194. if (1 !== $type) {
  195. $accessKey = $secretKey = $appid = $storageRegion = '';
  196. if (isset($data['accessKey']) && isset($data['secretKey']) && $data['accessKey'] && $data['secretKey']) {
  197. $accessKey = $data['accessKey'];
  198. $secretKey = $data['secretKey'];
  199. unset($data['accessKey'], $data['secretKey']);
  200. }
  201. if (isset($data['appid']) && $data['appid']) {
  202. $appid = $data['appid'];
  203. unset($data['appid']);
  204. }
  205. if (isset($data['storageRegion']) && $data['storageRegion']) {
  206. $storageRegion = $data['storageRegion'];
  207. unset($data['storageRegion']);
  208. }
  209. if (!$accessKey || !$secretKey) {
  210. return true;
  211. }
  212. /** @var SystemConfigServices $make */
  213. $make = app()->make(SystemConfigServices::class);
  214. switch ($type) {
  215. case 2://七牛
  216. $make->update('qiniu_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
  217. $make->update('qiniu_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
  218. break;
  219. case 3:// oss 阿里云
  220. $make->update('accessKey', ['value' => json_encode($accessKey)], 'menu_name');
  221. $make->update('secretKey', ['value' => json_encode($secretKey)], 'menu_name');
  222. break;
  223. case 4:// cos 腾讯云
  224. $make->update('tengxun_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
  225. $make->update('tengxun_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
  226. $make->update('tengxun_appid', ['value' => json_encode($appid)], 'menu_name');
  227. break;
  228. case 5:// oss 京东云
  229. $make->update('jd_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
  230. $make->update('jd_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
  231. $make->update('jd_storageRegion', ['value' => json_encode($storageRegion)], 'menu_name');
  232. break;
  233. case 6:// oss 华为云
  234. $make->update('hw_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
  235. $make->update('hw_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
  236. break;
  237. case 7:// oss 天翼云
  238. $make->update('ty_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
  239. $make->update('ty_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
  240. break;
  241. }
  242. CacheService::clear();
  243. }
  244. }
  245. /**
  246. * 保存云存储
  247. * @param int $type
  248. * @param array $data
  249. * @return mixed
  250. */
  251. public function saveStorage(int $type, array $data)
  252. {
  253. //保存配置信息
  254. $this->saveConfig($type, $data);
  255. if ($this->dao->count(['name' => $data['name']])) {
  256. throw new AdminException(400610);
  257. }
  258. //保存云存储
  259. $data['type'] = $type;
  260. $upload = UploadService::init($type);
  261. $res = $upload->createBucket($data['name'], $data['region'], $data['acl']);
  262. if (false === $res) {
  263. throw new AdminException($upload->getError());
  264. }
  265. if (3 === $type) {
  266. $data['region'] = $this->getReagionHost($type, $data['region']);
  267. }
  268. $data['domain'] = $this->getDomain($type, $data['name'], $data['region'], sys_config('tengxun_appid'));
  269. if (2 === $type) {
  270. $domianList = $upload->getDomian($data['name']);
  271. $data['domain'] = $domianList[count($domianList) - 1];
  272. } else {
  273. $data['cname'] = $data['domain'];
  274. }
  275. $data['add_time'] = time();
  276. $data['update_time'] = time();
  277. $config = $this->getStorageConfig($type);
  278. $data['access_key'] = $config['accessKey'];
  279. CacheService::clear();
  280. return $this->dao->save($data);
  281. }
  282. /**
  283. * 同步云储存桶
  284. * @param int $type
  285. * @return bool
  286. */
  287. public function synchronization(int $type)
  288. {
  289. $data = [];
  290. switch ($type) {
  291. case 2://七牛
  292. $config = $this->getStorageConfig($type);
  293. $upload = UploadService::init($type);
  294. $list = $upload->listbuckets();
  295. foreach ($list as $item) {
  296. if (!$this->dao->count(['name' => $item['id'], 'access_key' => $config['accessKey']])) {
  297. $data[] = [
  298. 'type' => $type,
  299. 'access_key' => $config['accessKey'],
  300. 'name' => $item['id'],
  301. 'region' => $item['region'],
  302. 'acl' => $item['private'] == 0 ? 'public-read' : 'private',
  303. 'status' => 0,
  304. 'is_delete' => 0,
  305. 'add_time' => time(),
  306. 'update_time' => time()
  307. ];
  308. }
  309. }
  310. break;
  311. case 3:// oss 阿里云
  312. $upload = UploadService::init($type);
  313. $list = $upload->listbuckets();
  314. $config = $this->getStorageConfig($type);
  315. foreach ($list as $item) {
  316. if (!$this->dao->count(['name' => $item['name'], 'access_key' => $config['accessKey']])) {
  317. $region = $this->getReagionHost($type, $item['location']);
  318. $data[] = [
  319. 'type' => $type,
  320. 'access_key' => $config['accessKey'],
  321. 'name' => $item['name'],
  322. 'region' => $region,
  323. 'acl' => 'public-read',
  324. 'domain' => $this->getDomain($type, $item['name'], $region),
  325. 'status' => 0,
  326. 'is_delete' => 0,
  327. 'add_time' => strtotime($item['createTime']),
  328. 'update_time' => time()
  329. ];
  330. }
  331. }
  332. break;
  333. case 4:// cos 腾讯云
  334. $upload = UploadService::init($type);
  335. $list = $upload->listbuckets();
  336. if (!empty($list['Name'])) {
  337. $newList = $list;
  338. $list = [];
  339. $list[] = $newList;
  340. }
  341. $config = $this->getStorageConfig($type);
  342. foreach ($list as $item) {
  343. if (!$this->dao->count(['name' => $item['Name'], 'access_key' => $config['accessKey']])) {
  344. $data[] = [
  345. 'type' => $type,
  346. 'access_key' => $config['accessKey'],
  347. 'name' => $item['Name'],
  348. 'region' => $item['Location'],
  349. 'acl' => 'public-read',
  350. 'status' => 0,
  351. 'domain' => sys_config('tengxun_appid') ? $this->getDomain($type, $item['Name'], $item['Location']) : '',
  352. 'is_delete' => 0,
  353. 'add_time' => strtotime($item['CreationDate']),
  354. 'update_time' => time()
  355. ];
  356. }
  357. }
  358. break;
  359. case 5:// cos 京东云
  360. $upload = UploadService::init($type);
  361. $res = $upload->listbuckets(sys_config('jd_storageRegion'));
  362. $list = $res['Buckets'];
  363. $location = explode('.', $res['@metadata']['effectiveUri'])[1] ?? 'cn-north-1';
  364. $config = $this->getStorageConfig($type);
  365. foreach ($list as $item) {
  366. if (!$this->dao->count(['name' => $item['Name'], 'access_key' => $config['accessKey']])) {
  367. $data[] = [
  368. 'type' => $type,
  369. 'access_key' => $config['accessKey'],
  370. 'name' => $item['Name'],
  371. 'region' => $location,
  372. 'acl' => 'public-read',
  373. 'status' => 0,
  374. 'domain' => $this->getDomain($type, $item['Name'], $location),
  375. 'is_delete' => 0,
  376. 'add_time' => time(),
  377. 'update_time' => time()
  378. ];
  379. }
  380. }
  381. break;
  382. case 6:// cos 华为云
  383. case 7:// cos 天翼云
  384. $upload = UploadService::init($type);
  385. $list = $upload->listbuckets();
  386. if (!empty($list['Name'])) {
  387. $newList = $list;
  388. $list = [];
  389. $list[] = $newList;
  390. }
  391. $config = $this->getStorageConfig($type);
  392. foreach ($list as $item) {
  393. if (!$this->dao->count(['name' => $item['Name'], 'access_key' => $config['accessKey']])) {
  394. $data[] = [
  395. 'type' => $type,
  396. 'access_key' => $config['accessKey'],
  397. 'name' => $item['Name'],
  398. 'region' => $item['Location'],
  399. 'acl' => 'public-read',
  400. 'status' => 0,
  401. 'domain' => $this->getDomain($type, $item['Name'], $item['Location']),
  402. 'is_delete' => 0,
  403. 'add_time' => strtotime($item['CreationDate']),
  404. 'update_time' => time()
  405. ];
  406. }
  407. }
  408. break;
  409. }
  410. if ($data) {
  411. $this->dao->saveAll($data);
  412. }
  413. CacheService::clear();
  414. return true;
  415. }
  416. /**
  417. * @param int $type
  418. * @param string $reagion
  419. * @return mixed|string
  420. */
  421. public function getReagionHost(int $type, string $reagion)
  422. {
  423. $upload = UploadService::init($type);
  424. $reagionList = $upload->getRegion();
  425. foreach ($reagionList as $item) {
  426. if (strstr($item['value'], $reagion) !== false) {
  427. return $item['value'];
  428. }
  429. }
  430. return '';
  431. }
  432. /**
  433. * 获取域名
  434. * @param int $type
  435. * @param string $name
  436. * @param string $reagion
  437. * @param string $appid
  438. * @return string
  439. */
  440. public function getDomain(int $type, string $name, string $reagion, string $appid = '')
  441. {
  442. $domainName = '';
  443. switch ($type) {
  444. case 3:// oss 阿里云
  445. $domainName = 'https://' . $name . '.' . $reagion;
  446. break;
  447. case 4:// cos 腾讯云
  448. $domainName = 'https://' . $name . ($appid ? '-' . $appid : '') . '.cos.' . $reagion . '.myqcloud.com';
  449. break;
  450. case 5:// cos 京东云
  451. $domainName = 'https://' . $name . '.s3.' . $reagion . '.jdcloud-oss.com';
  452. break;
  453. case 6:// cos 华为云
  454. $domainName = 'https://' . $name . '.obs.' . $reagion . '.myhuaweicloud.com';
  455. break;
  456. case 7:// cos 天翼云
  457. $domainName = 'https://' . $name . '.obs.' . $reagion . '.ctyun.cn';
  458. break;
  459. }
  460. return $domainName;
  461. }
  462. /**
  463. * 获取云存储配置
  464. * @param int $type
  465. * @return array|string[]
  466. */
  467. public function getConfig(int $type)
  468. {
  469. $res = ['name' => '', 'region' => '', 'domain' => '', 'cdn' => ''];
  470. try {
  471. $config = $this->dao->get(['type' => $type, 'status' => 1, 'is_delete' => 0]);
  472. if ($config) {
  473. return ['name' => $config->name, 'region' => $config->region, 'domain' => $config->domain, 'cdn' => $config->cdn];
  474. }
  475. } catch (\Throwable $e) {
  476. }
  477. return $res;
  478. }
  479. /**
  480. * 获取修改域名表单
  481. * @param int $id
  482. * @return array
  483. * @throws \FormBuilder\Exception\FormBuilderException
  484. */
  485. public function getUpdateDomainForm(int $id)
  486. {
  487. $storage = $this->dao->get(['id' => $id], ['domain', 'cdn']);
  488. $rule = [
  489. FormBuilder::input('domain', '空间域名', $storage['domain']),
  490. FormBuilder::input('cdn', 'cdn域名', $storage['cdn']),
  491. ];
  492. return create_form('修改空间域名', $rule, '/system/config/storage/domain/' . $id);
  493. }
  494. /**
  495. * 修改域名并绑定
  496. * @param int $id
  497. * @param string $domain
  498. * @return bool
  499. * @throws \think\db\exception\DataNotFoundException
  500. * @throws \think\db\exception\DbException
  501. * @throws \think\db\exception\ModelNotFoundException
  502. */
  503. public function updateDomain(int $id, string $domain, array $data = [])
  504. {
  505. $info = $this->dao->get($id);
  506. if (!$info) {
  507. throw new AdminException(100026);
  508. }
  509. if ($info->domain != $domain) {
  510. $info->domain = $domain;
  511. $upload = UploadService::init($info->type);
  512. //是否添加过域名不存在需要绑定域名
  513. $domainList = $upload->getDomian($info->name, $info->region);
  514. $domainParse = parse_url($domain);
  515. if (false === $domainParse) {
  516. throw new AdminException('域名输入有误');
  517. }
  518. if (!in_array($domainParse['host'], $domainList)) {
  519. //绑定域名到云储存桶
  520. $res = $upload->bindDomian($info->name, $domain, $info->region);
  521. if (false === $res) {
  522. throw new AdminException($upload->getError());
  523. }
  524. }
  525. //七牛云需要通过接口获取cname
  526. if (2 === ((int)$info->type)) {
  527. $resDomain = $upload->getDomianInfo($domain);
  528. $info->cname = $resDomain['cname'] ?? '';
  529. }
  530. $info->save();
  531. }
  532. if ($info->cdn != $data['cdn']) {
  533. $info->cdn = $data['cdn'];
  534. $info->save();
  535. }
  536. CacheService::clear();
  537. return true;
  538. }
  539. }