StoreProduct.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\controller\store\product;
  12. use app\jobs\BatchHandleJob;
  13. use app\jobs\store\SynchStocksJob;
  14. use app\services\other\queue\QueueServices;
  15. use app\services\product\branch\StoreBranchProductAttrValueServices;
  16. use app\services\product\branch\StoreBranchProductServices;
  17. use app\services\product\category\StoreProductCategoryServices;
  18. use app\services\product\product\StoreProductBatchProcessServices;
  19. use app\services\product\product\StoreProductServices;
  20. use app\services\product\sku\StoreProductAttrServices;
  21. use app\services\product\sku\StoreProductAttrValueServices;
  22. use app\services\store\SystemStoreServices;
  23. use app\services\user\label\UserLabelCateServices;
  24. use app\services\user\label\UserLabelServices;
  25. use crmeb\services\UploadService;
  26. use think\facade\App;
  27. use app\controller\store\AuthController;
  28. /**
  29. * Class StoreProduct
  30. * @package app\controller\store\product
  31. */
  32. class StoreProduct extends AuthController
  33. {
  34. protected $services;
  35. protected $branchServices;
  36. /**
  37. * @param App $app
  38. * @param StoreProductServices $service
  39. * @param StoreBranchProductServices $branchServices
  40. */
  41. public function __construct(App $app, StoreProductServices $service, StoreBranchProductServices $branchServices)
  42. {
  43. parent::__construct($app);
  44. $this->services = $service;
  45. $this->branchServices = $branchServices;
  46. }
  47. /**
  48. * 显示资源列表头部
  49. * @return mixed
  50. */
  51. public function type_header(StoreProductCategoryServices $storeProductCategoryServices)
  52. {
  53. $where = $this->request->getMore([
  54. ['store_name', ''],
  55. ['cate_id', ''],
  56. ['type', 1, '', 'status'],
  57. ['show_type', ''],
  58. ['sales', ''],
  59. ['pid', ''],
  60. ['data', '', '', 'time'],
  61. ['store_label_id', ''],
  62. ['brand_id', '']
  63. ]);
  64. $cateId = $where['cate_id'];
  65. if ($cateId) {
  66. $cateId = is_string($cateId) ? [$cateId] : $cateId;
  67. $cateId = array_merge($cateId, $storeProductCategoryServices->getColumn(['pid' => $cateId], 'id'));
  68. $cateId = array_unique(array_diff($cateId, [0]));
  69. }
  70. $where['cate_id'] = $cateId;
  71. $list = $this->services->getHeader((int)$this->storeId, $where);
  72. return app('json')->success(compact('list'));
  73. }
  74. /**
  75. * 显示资源列表
  76. * @return mixed
  77. */
  78. public function index(StoreProductCategoryServices $storeProductCategoryServices)
  79. {
  80. $where = $this->request->getMore([
  81. ['store_name', ''],
  82. ['cate_id', ''],
  83. ['type', 1, '', 'status'],
  84. ['show_type', ''],
  85. ['sales', 'normal'],
  86. ['pid', ''],
  87. ['data', '', '', 'time'],
  88. ['store_label_id', ''],
  89. ['brand_id', '']
  90. ]);
  91. $cateId = $where['cate_id'];
  92. if ($cateId) {
  93. $cateId = is_string($cateId) ? [$cateId] : $cateId;
  94. $cateId = array_merge($cateId, $storeProductCategoryServices->getColumn(['pid' => $cateId], 'id'));
  95. $cateId = array_unique(array_diff($cateId, [0]));
  96. }
  97. $where['cate_id'] = $cateId;
  98. $where['relation_id'] = $this->storeId;
  99. $where['type'] = 1;
  100. $data = $this->services->getList($where);
  101. return app('json')->success($data);
  102. }
  103. /**
  104. * 获取选择的商品列表
  105. * @return mixed
  106. */
  107. public function search_list()
  108. {
  109. $where = $this->request->getMore([
  110. ['cate_id', ''],
  111. ['store_name', ''],
  112. ['type', 1, '', 'status'],
  113. ['is_live', 0],
  114. ['is_new', ''],
  115. ['is_vip_product', ''],
  116. ['is_presale_product', ''],
  117. ['data', '', '', 'time'],
  118. ['store_label_id', ''],
  119. ['brand_id', '']
  120. ]);
  121. $where['is_show'] = 1;
  122. $where['is_del'] = 0;
  123. $where['type'] = 1;
  124. $where['relation_id'] = $this->storeId;
  125. /** @var StoreProductCategoryServices $storeCategoryServices */
  126. $storeCategoryServices = app()->make(StoreProductCategoryServices::class);
  127. if ($where['cate_id'] !== '') {
  128. if ($storeCategoryServices->value(['id' => $where['cate_id']], 'pid')) {
  129. $where['sid'] = $where['cate_id'];
  130. } else {
  131. $where['cid'] = $where['cate_id'];
  132. }
  133. }
  134. unset($where['cate_id']);
  135. $list = $this->services->searchList($where);
  136. return $this->success($list);
  137. }
  138. /**
  139. * 获取分类cascader格式数据
  140. * @param $type
  141. * @return mixed
  142. * @throws \think\db\exception\DataNotFoundException
  143. * @throws \think\db\exception\DbException
  144. * @throws \think\db\exception\ModelNotFoundException
  145. */
  146. public function cascader_list(StoreProductCategoryServices $services)
  147. {
  148. return app('json')->success($services->cascaderList(1, (int)$this->storeId));
  149. }
  150. /**
  151. * 获取商品详细信息
  152. * @param int $id
  153. * @throws \think\db\exception\DataNotFoundException
  154. * @throws \think\db\exception\DbException
  155. * @throws \think\db\exception\ModelNotFoundException
  156. */
  157. public function read($id = 0)
  158. {
  159. return app('json')->success($this->services->getInfo((int)$id));
  160. }
  161. /**
  162. * 保存新建或编辑
  163. * @param SystemStoreServices $storeServices
  164. * @param StoreProductAttrServices $attrServices
  165. * @param $id
  166. * @return mixed
  167. * @throws \think\db\exception\DataNotFoundException
  168. * @throws \think\db\exception\DbException
  169. * @throws \think\db\exception\ModelNotFoundException
  170. */
  171. public function save(SystemStoreServices $storeServices, StoreProductAttrServices $attrServices, $id)
  172. {
  173. $data = $this->request->postMore([
  174. ['product_type', 0],//商品类型
  175. ['supplier_id', 0],//供应商ID
  176. ['cate_id', []],
  177. ['store_name', ''],
  178. ['store_info', ''],
  179. ['keyword', ''],
  180. ['unit_name', '件'],
  181. ['recommend_image', ''],
  182. ['slider_image', []],
  183. ['is_sub', []],//佣金是单独还是默认
  184. ['sort', 0],
  185. // ['sales', 0],
  186. ['ficti', 100],
  187. ['give_integral', 0],
  188. ['is_show', 0],
  189. ['is_hot', 0],
  190. ['is_benefit', 0],
  191. ['is_best', 0],
  192. ['is_new', 0],
  193. ['mer_use', 0],
  194. ['is_postage', 0],
  195. ['is_good', 0],
  196. ['description', ''],
  197. ['spec_type', 0],
  198. ['video_open', 0],
  199. ['video_link', ''],
  200. ['items', []],
  201. ['attrs', []],
  202. ['recommend', []],//商品推荐
  203. ['activity', []],
  204. ['coupon_ids', []],
  205. ['label_id', []],
  206. ['command_word', ''],
  207. ['tao_words', ''],
  208. ['type', 0, '', 'is_copy'],
  209. ['delivery_type', []],//物流设置
  210. ['freight', 1],//运费设置
  211. ['postage', 0],//邮费
  212. ['temp_id', 0],//运费模版
  213. ['recommend_list', []],
  214. ['brand_id', []],
  215. ['soure_link', ''],
  216. ['bar_code', ''],
  217. ['code', ''],
  218. ['is_support_refund', 1],//是否支持退款
  219. ['is_presale_product', 0],//预售商品开关
  220. ['presale_time', []],//预售时间
  221. ['presale_day', 0],//预售发货日
  222. ['is_vip_product', 0],//是否付费会员商品
  223. ['auto_on_time', 0],//自动上架时间
  224. ['auto_off_time', 0],//自动下架时间
  225. ['custom_form', []],//自定义表单
  226. ['system_form_id', 0],//系统表单ID
  227. ['store_label_id', []],//商品标签
  228. ['ensure_id', []],//商品保障服务区
  229. ['specs', []],//商品参数
  230. ['specs_id', 0],//商品参数ID
  231. ['is_limit', 0],//是否限购
  232. ['limit_type', 0],//限购类型
  233. ['limit_num', 0],//限购数量
  234. ['show_type', 0],//商品展示
  235. ]);
  236. //门店商品编辑 需要再次审核
  237. $storeId = (int)$this->storeId;
  238. $storeInfo = [];
  239. if ($storeId) {
  240. $storeInfo = $storeServices->getStoreInfo($storeId);
  241. }
  242. if (!$storeInfo) {
  243. return $this->fail('门店不存在或者已下架');
  244. }
  245. if (!$storeInfo['product_status']) {
  246. return $this->fail('暂不支持添加商品,请联系平台管理员');
  247. }
  248. $data['is_verify'] = 0;
  249. //门店开启免审
  250. if (isset($storeInfo['product_verify_status']) && $storeInfo['product_verify_status']) {
  251. $data['is_verify'] = 1;
  252. }
  253. //门店商品不支持平台配送
  254. $data['delivery_type'] = array_diff($data['delivery_type'], [1]);
  255. $this->services->save((int)$id, $data, 1, (int)$this->storeId);
  256. $this->services->cacheTag()->clear();
  257. $attrServices->cacheTag()->clear();
  258. return $this->success($id ? '保存商品信息成功' : '添加商品成功!');
  259. }
  260. /**
  261. * 保存编辑
  262. * @param int $id
  263. * @param StoreBranchProductServices $services
  264. * @return mixed
  265. */
  266. public function update($id = 0, StoreBranchProductAttrValueServices $services)
  267. {
  268. $data = $this->request->postMore([
  269. ['attrs', []],
  270. ['label_id', []],
  271. ['is_show', 1]
  272. ]);
  273. $storeId = $this->storeId;
  274. $services->updataAll((int)$id, (array)$data, (int)$storeId);
  275. return app('json')->success('保存商品信息成功');
  276. }
  277. /**
  278. * 门店同步库存
  279. * @return mixed
  280. */
  281. public function synchStocks()
  282. {
  283. [$ids] = $this->request->postMore([
  284. ['ids', []]
  285. ], true);
  286. if (!count($ids)) return $this->fail('请选择商品');
  287. $storeId = $this->storeId;
  288. //拆分大数组
  289. $idsArr = array_chunk($ids, 5);
  290. foreach ($idsArr as $syncIds) {
  291. //加入同步
  292. SynchStocksJob::dispatch([$syncIds, $storeId]);
  293. }
  294. return app('json')->success('库存同步已加入队列执行,请稍后查看');
  295. }
  296. /**
  297. * 获取关联用户标签列表
  298. * @param UserLabelServices $service
  299. * @return mixed
  300. */
  301. public function getUserLabel(UserLabelCateServices $userLabelCateServices, UserLabelServices $service)
  302. {
  303. $cate = $userLabelCateServices->getLabelCateAll(1, (int)$this->storeId);
  304. $data = [];
  305. $label = [];
  306. if ($cate) {
  307. foreach ($cate as $value) {
  308. $data[] = [
  309. 'id' => $value['id'] ?? 0,
  310. 'value' => $value['id'] ?? 0,
  311. 'label_cate' => 0,
  312. 'label_name' => $value['name'] ?? '',
  313. 'label' => $value['name'] ?? '',
  314. 'relation_id' => $value['store_id'] ?? 0,
  315. 'type' => $value['type'] ?? 1,
  316. ];
  317. }
  318. $label = $service->getColumn(['type' => 1, 'relation_id' => $this->storeId], '*');
  319. if ($label) {
  320. foreach ($label as &$item) {
  321. $item['label'] = $item['label_name'];
  322. $item['value'] = $item['id'];
  323. }
  324. }
  325. }
  326. return app('json')->success($service->get_tree_children($data, $label));
  327. }
  328. /**
  329. * 修改状态
  330. * @param string $is_show
  331. * @param string $id
  332. * @return mixed
  333. */
  334. public function set_show($is_show = '', $id = '', StoreBranchProductServices $services)
  335. {
  336. if (!$id) return $this->fail('缺少商品ID');
  337. $services->setShow($this->storeId, $id, $is_show);
  338. return $this->success($is_show == 1 ? '上架成功' : '下架成功');
  339. }
  340. /**
  341. * 获取规格模板
  342. * @return mixed
  343. */
  344. public function get_rule()
  345. {
  346. return $this->success($this->services->getRule(1, (int)$this->storeId));
  347. }
  348. /**
  349. * 获取商品详细信息
  350. * @param int $id
  351. * @throws \think\db\exception\DataNotFoundException
  352. * @throws \think\db\exception\DbException
  353. * @throws \think\db\exception\ModelNotFoundException
  354. */
  355. public function get_product_info($id = 0)
  356. {
  357. return $this->success($this->services->getInfo((int)$id));
  358. }
  359. /**
  360. * 获取运费模板列表
  361. * @return mixed
  362. */
  363. public function get_template()
  364. {
  365. return $this->success($this->services->getTemp(1, (int)$this->storeId));
  366. }
  367. /**
  368. * 获取视频上传token
  369. * @return mixed
  370. * @throws \Exception
  371. */
  372. public function getTempKeys()
  373. {
  374. $upload = UploadService::init();
  375. $type = (int)sys_config('upload_type', 1);
  376. $key = $this->request->get('key', '');
  377. $path = $this->request->get('path', '');
  378. $contentType = $this->request->get('contentType', '');
  379. if ($type === 5) {
  380. if (!$key || !$contentType) {
  381. return app('json')->fail('缺少参数');
  382. }
  383. $re = $upload->getTempKeys($key, $path, $contentType);
  384. } else {
  385. $re = $upload->getTempKeys();
  386. }
  387. return $re ? $this->success($re) : $this->fail($upload->getError());
  388. }
  389. /**
  390. * 获取商品所有规格数据
  391. * @param StoreBranchProductAttrValueServices $services
  392. * @param $id
  393. * @return mixed
  394. */
  395. public function getAttrs(StoreBranchProductAttrValueServices $services, $id)
  396. {
  397. if (!$id) {
  398. return $this->fail('缺少商品ID');
  399. }
  400. return $this->success($services->getStoreProductAttr((int)$id));
  401. }
  402. /**
  403. * 删除指定资源
  404. *
  405. * @param int $id
  406. * @return \think\Response
  407. */
  408. public function delete($id)
  409. {
  410. //删除商品检测是否有参与活动
  411. $this->services->checkActivity($id);
  412. $res = $this->services->del($id);
  413. event('product.delete', [$id]);
  414. return $this->success($res);
  415. }
  416. /**
  417. * 生成规格列表
  418. * @param int $id
  419. * @param int $type
  420. * @return mixed
  421. */
  422. public function is_format_attr($id = 0, $type = 0)
  423. {
  424. $data = $this->request->postMore([
  425. ['attrs', []],
  426. ['items', []],
  427. ['product_type', 0]
  428. ]);
  429. if ($id > 0 && $type == 1) $this->services->checkActivity($id);
  430. $info = $this->services->getAttr($data, $id, $type, 1);
  431. return $this->success(compact('info'));
  432. }
  433. /**
  434. * 快速修改商品规格库存
  435. * @param StoreProductAttrValueServices $services
  436. * @param $id
  437. * @return mixed
  438. */
  439. public function saveProductAttrsStock(StoreProductAttrValueServices $services, $id)
  440. {
  441. if (!$id) {
  442. return $this->fail('缺少商品ID');
  443. }
  444. [$attrs] = $this->request->getMore([
  445. ['attrs', []]
  446. ], true);
  447. if (!$attrs) {
  448. return $this->fail('请重新修改规格库存');
  449. }
  450. foreach ($attrs as $attr) {
  451. if (!isset($attr['unique']) || !isset($attr['pm']) || !isset($attr['stock'])) {
  452. return $this->fail('请重新修改规格库存');
  453. }
  454. }
  455. return $this->success(['stock' => $services->saveProductAttrsStock((int)$id, $attrs)]);
  456. }
  457. /**
  458. * 设置批量商品上架
  459. * @return mixed
  460. */
  461. public function product_show()
  462. {
  463. [$ids, $all, $where] = $this->request->postMore([
  464. ['ids', []],
  465. ['all', 0],
  466. ['where', []],
  467. ], true);
  468. if ($all == 0) {//单页不走队列
  469. if (empty($ids)) return $this->fail('请选择需要上架的商品');
  470. $this->services->setShow($ids, 1);
  471. return $this->success('上架成功');
  472. }
  473. if ($all == 1) {
  474. $ids = [];
  475. if (isset($where['type'])) {
  476. $where['status'] = $where['type'];
  477. unset($where['type']);
  478. }
  479. $where['type'] = 1;
  480. $where['relation_id'] = $this->storeId;
  481. }
  482. $type = 4;//商品上架
  483. /** @var QueueServices $queueService */
  484. $queueService = app()->make(QueueServices::class);
  485. $queueService->setQueueData($where, 'id', $ids, $type);
  486. //加入队列
  487. BatchHandleJob::dispatch(['up', $type]);
  488. return $this->success('后台程序已执商品上架任务!');
  489. }
  490. /**
  491. * 设置批量商品下架
  492. * @return mixed
  493. */
  494. public function product_unshow()
  495. {
  496. [$ids, $all, $where] = $this->request->postMore([
  497. ['ids', []],
  498. ['all', 0],
  499. ['where', []],
  500. ], true);
  501. if ($all == 0) {//单页不走队列
  502. if (empty($ids)) return $this->fail('请选择需要下架的商品');
  503. $this->services->setShow($ids, 0);
  504. return $this->success('下架成功');
  505. }
  506. if ($all == 1) {
  507. $all_ids = $this->services->getColumn(['is_show' => 1, 'is_del' => 0, 'type' => 1, 'relation_id' => $this->storeId], 'id');
  508. $this->services->checkActivity($all_ids);
  509. $ids = [];
  510. if (isset($where['type'])) {
  511. $where['status'] = $where['type'];
  512. unset($where['type']);
  513. }
  514. $where['type'] = 1;
  515. $where['relation_id'] = $this->storeId;
  516. }
  517. $type = 4;//商品下架
  518. /** @var QueueServices $queueService */
  519. $queueService = app()->make(QueueServices::class);
  520. $queueService->setQueueData($where, 'id', $ids, $type);
  521. //加入队列
  522. BatchHandleJob::dispatch(['down', $type]);
  523. return $this->success('后台程序已执商品下架任务!');
  524. }
  525. /**
  526. * 商品批量操作
  527. * @param StoreProductBatchProcessServices $batchProcessServices
  528. * @return mixed
  529. */
  530. public function batchProcess(StoreProductBatchProcessServices $batchProcessServices)
  531. {
  532. [$type, $ids, $all, $where, $data] = $this->request->postMore([
  533. ['type', 1],
  534. ['ids', ''],
  535. ['all', 0],
  536. ['where', ""],
  537. ['data', []]
  538. ], true);
  539. if (!$ids && $all == 0) return $this->fail('请选择批处理商品');
  540. if (!$data) {
  541. return $this->fail('请选择批处理数据');
  542. }
  543. if (isset($where['type'])) {
  544. $where['status'] = $where['type'];
  545. unset($where['type']);
  546. }
  547. $where['type'] = 1;
  548. $where['relation_id'] = $this->storeId;
  549. //批量操作
  550. $batchProcessServices->batchProcess((int)$type, $ids, $data, !!$all, $where);
  551. return app('json')->success('已加入消息队列,请稍后查看');
  552. }
  553. }