GoodsLogic.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LikeShop有特色的全开源社交分销电商系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 商业用途务必购买系统授权,以免引起不必要的法律纠纷
  7. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  8. // | 微信公众号:好象科技
  9. // | 访问官网:http://www.likemarket.net
  10. // | 访问社区:http://bbs.likemarket.net
  11. // | 访问手册:http://doc.likemarket.net
  12. // | 好象科技开发团队 版权所有 拥有最终解释权
  13. // +----------------------------------------------------------------------
  14. // | Author: LikeShopTeam
  15. // +----------------------------------------------------------------------
  16. namespace app\adminapi\logic\goods;
  17. use think\facade\Db;
  18. use app\common\enum\{ActivityEnum, YesNoEnum, GoodsEnum, DefaultEnum};
  19. use app\common\model\{Cart,
  20. DistributionGoods,
  21. Goods,
  22. Freight,
  23. GoodsActivity,
  24. GoodsUnit,
  25. GoodsSpec,
  26. GoodsItem,
  27. GoodsImage,
  28. GoodsBrand,
  29. GoodsSupplier,
  30. GoodsCategory,
  31. GoodsSpecValue,
  32. GoodsCategoryIndex};
  33. /**
  34. * 商品逻辑层
  35. * Class GoodsLogic
  36. * @package app\adminapi\logic\goods
  37. */
  38. class GoodsLogic
  39. {
  40. /**
  41. * Notes:添加商品
  42. * @param array $postData 商品信息
  43. * @return bool|string
  44. * @author: cjhao 2021/7/9 18:04
  45. */
  46. public function add(array $params)
  47. {
  48. Db::startTrans();
  49. try {
  50. $goods = $this->setBase($params);
  51. $this->addGoodsItem($goods,$params);
  52. Db::commit();
  53. return true;
  54. } catch (\Exception $e) {
  55. Db::rollback();
  56. return $e->getMessage();
  57. }
  58. }
  59. /**
  60. * @notes 编辑商品
  61. * @param array $params
  62. * @return array|bool[]
  63. * @author cjhao
  64. * @date 2021/7/14 18:09
  65. */
  66. public function edit(array $params)
  67. {
  68. Db::startTrans();
  69. try {
  70. $oldItemIds = GoodsItem::where('goods_id', $params['id'])->column('id');
  71. $goods = $this->setBase($params);
  72. $this->editGoodsItem($goods,$params);
  73. $newItemIds = GoodsItem::where('goods_id', $params['id'])->column('id');
  74. //todo 其他业务逻辑
  75. $isDistributionGoods = false;
  76. $distributionGoods = DistributionGoods::where('goods_id', $params['id'])->findOrEmpty()->toArray();
  77. // 存在分销 且sku已经变更 则清除分销信息
  78. if ($distributionGoods && $oldItemIds != $newItemIds) {
  79. // 编辑商品时清除已设置好的分销比例,商家须重新设置
  80. $deleteIds = DistributionGoods::where('goods_id', $params['id'])->column('id');
  81. DistributionGoods::destroy($deleteIds);
  82. $isDistributionGoods = true;
  83. }
  84. Db::commit();
  85. return [
  86. 'status' => true,
  87. 'is_distribution_goods' => $isDistributionGoods,
  88. ];
  89. } catch (\Exception $e) {
  90. Db::rollback();
  91. return [
  92. 'status' => false,
  93. 'err' => $e->getMessage()
  94. ];
  95. }
  96. }
  97. /**
  98. * @notes 商品基础信息
  99. * @param array $params
  100. * @return Goods|array|\think\Model|null
  101. * @throws \think\db\exception\DataNotFoundException
  102. * @throws \think\db\exception\DbException
  103. * @throws \think\db\exception\ModelNotFoundException
  104. * @author cjhao
  105. * @date 2021/7/22 14:01
  106. */
  107. public function setBase(array $params){
  108. $goods = new Goods();
  109. $goodsImage = $params['goods_image'];
  110. $goodsCategory = $params['category_id'];
  111. //总库存
  112. $totalStock = array_sum(array_column($params['spec_value_list'],'stock'));
  113. //售价数组
  114. $specPriceArray = array_column($params['spec_value_list'],'sell_price');
  115. //划线价数组
  116. $specLineationPriceArray = array_column($params['spec_value_list'],'lineation_price');
  117. //编辑操作
  118. if(isset($params['id'])){
  119. $goods = $goods::find($params['id']);
  120. //删除轮播图
  121. GoodsImage::where(['goods_id'=>$goods->id])->delete();
  122. //删除商品分类
  123. GoodsCategoryIndex::where(['goods_id'=>$goods->id])->delete();
  124. //保存改变前的规格类型
  125. $goods->oldSpecType = $goods->spec_type;
  126. }
  127. $isExpress = GoodsEnum::GOODS_REALITY == $params['type'] ? $params['is_express'] : 0 ; //快递
  128. $isSelffetch = GoodsEnum::GOODS_REALITY == $params['type'] ? $params['is_selffetch'] : 0 ; //自提
  129. $isVirtualdelivery = GoodsEnum::GOODS_VIRTUAL == $params['type'] ? 1 : 0; //虚拟发货
  130. $afterPay = 0;
  131. $afterDelivery = 0;
  132. switch ($params['type']){
  133. case 1:
  134. $isExpress = $params['is_express'];
  135. $isSelffetch = $params['is_selffetch'];
  136. break;
  137. case 2:
  138. $isVirtualdelivery = 1;
  139. $afterPay = $params['after_pay'];
  140. $afterDelivery = $params['after_delivery'];
  141. break;
  142. }
  143. //更新商品基础信息
  144. $goods->name = $params['name'];
  145. $goods->code = $params['code'];
  146. $goods->type = $goods['type'] ?? $params['type']; //编辑不允许修改类型
  147. $goods->brand_id = $params['brand_id'];
  148. $goods->supplier_id = $params['supplier_id'];
  149. $goods->unit_id = $params['unit_id'];
  150. $goods->image = array_shift($goodsImage); //弹出第一张图片,设为商品主图;
  151. $goods->spec_type = $params['spec_type'];
  152. $goods->video_source = $params['video_source'];
  153. $goods->video_cover = $params['video_cover'];
  154. $goods->video = $params['video'];
  155. $goods->poster = $params['poster'];
  156. $goods->total_stock = $totalStock;
  157. $goods->min_price = min($specPriceArray);
  158. $goods->max_price = max($specPriceArray);
  159. $goods->min_lineation_price = min($specLineationPriceArray);
  160. $goods->max_lineation_price = max($specLineationPriceArray);
  161. $goods->express_type = $params['express_type'];
  162. $goods->express_money = $params['express_money'];
  163. $goods->express_template_id = $params['express_template_id'];
  164. $goods->stock_warning = $params['stock_warning'];
  165. $goods->virtual_sales_num = $params['virtual_sales_num'];
  166. $goods->virtual_click_num = $params['virtual_click_num'] ?? 0;
  167. $goods->sort = $params['sort'] ?? DefaultEnum::SORT;
  168. $goods->status = $params['status'];
  169. $goods->content = $params['content'];
  170. $goods->is_express = $isExpress; //快递配送
  171. $goods->is_selffetch = $isSelffetch; //上门自提
  172. $goods->is_virtualdelivery = $isVirtualdelivery; //虚拟发货
  173. $goods->after_pay = $afterPay; //买家付款后:1-自动发货,2-手动发货
  174. $goods->after_delivery = $afterDelivery; //发货后:1-自动完成订单,2-需要买家确认
  175. $goods->delivery_content = $params['delivery_content'] ?? ''; //发货内容
  176. $goods->delivery_type = $params['delivery_type'] ?? 0;// 发货类型
  177. $goods->delivery_template_id= $params['delivery_template_id'] ?? 0;// 发货模版
  178. $goods->is_address = $params['is_address'] ?? 0;
  179. $goods->limit_type = $params['limit_type'];
  180. $goods->limit_value = $params['limit_value'];
  181. $goods->service_guarantee_ids = $params['service_guarantee_ids'] ?? [];
  182. $goods->save();
  183. //添加商品轮播图
  184. if ($goodsImage) {
  185. array_walk($goodsImage, function (&$image){
  186. $image = ['uri' => $image];
  187. });
  188. $goods->imageList()->saveAll($goodsImage);
  189. }
  190. //添加商品分类关联表
  191. if($goodsCategory){
  192. array_walk($goodsCategory, function (&$category_id){
  193. $category_id = ['category_id' => $category_id];
  194. });
  195. $goods->goodsCategoryIndex()->saveAll($goodsCategory);
  196. }
  197. return $goods;
  198. }
  199. /**
  200. * @notes 添加商品规格信息
  201. * @param Goods $goods
  202. * @param array $params
  203. * @author cjhao
  204. * @date 2021/7/22 14:37
  205. */
  206. public function addGoodsItem(Goods $goods,array $params)
  207. {
  208. $specType = $params['spec_type'];
  209. //添加商品规格
  210. if (GoodsEnum::SEPC_TYPE_SIGNLE == $specType) {
  211. //单规格商品
  212. $specValueList = $params['spec_value_list'];
  213. foreach ($specValueList as $spec) {
  214. $goodsSpec = new GoodsSpec();
  215. $goodsSpec->goods_id = $goods->id;
  216. $goodsSpec->name = '默认';
  217. $goodsSpec->save();
  218. $goodsSpecValue = new GoodsSpecValue();
  219. $goodsSpecValue->save([
  220. 'goods_id' => $goodsSpec->goods_id,
  221. 'spec_id' => $goodsSpec->id,
  222. 'value' => '默认',
  223. ]);
  224. $specValueData = [
  225. 'goods_id' => $goods->id,
  226. 'spec_value_ids' => $goodsSpecValue->id,
  227. 'spec_value_str' => '默认',
  228. 'image' => $spec['image'],
  229. 'sell_price' => $spec['sell_price'],
  230. 'lineation_price' => $spec['lineation_price'],
  231. 'cost_price' => $spec['cost_price'],
  232. 'stock' => $spec['stock'],
  233. 'volume' => $spec['volume'],
  234. 'weight' => $spec['weight'],
  235. 'bar_code' => $spec['bar_code'],
  236. ];
  237. (new GoodsItem())->save($specValueData);
  238. }
  239. } else {
  240. //添加规格项
  241. $postSpecValue = $params['spec_value'];
  242. $specData = [];
  243. foreach ($postSpecValue as $sepcKey => $specVal) {
  244. $goodsSpec = new GoodsSpec();
  245. $goodsSpec->goods_id = $goods->id;
  246. $goodsSpec->name = $specVal['name'];
  247. $goodsSpec->save();
  248. //处理规格值
  249. array_walk($specVal['spec_list'], function ($spec) use ($goodsSpec,&$specData){
  250. $specData[] = ['spec_id'=>$goodsSpec->id,'value'=>$spec['value']];
  251. });
  252. }
  253. $goods->specValueSpec()->saveAll($specData);
  254. //添加规格信息
  255. $serverSpecValueList = $params['server_spec_value_list'];
  256. //改变数据结构,ids为索引
  257. $postSpecValueList = array_column($params['spec_value_list'], null, 'ids');
  258. // $goodsSpecValueList = GoodsSpecValue::where(['goods_id'=>$goods->id])
  259. // ->group('spec_id')
  260. // ->column(' GROUP_CONCAT(value Separator \',\') as spec_values,GROUP_CONCAT(id Separator \',\') as spec_ids');
  261. //GROUP_CONCAT函数有长度限制,替换成下面的方法
  262. $goodsSpecValue = GoodsSpecValue::where(['goods_id'=>$goods->id])
  263. ->column('id,spec_id,value');
  264. $goodsSpecValueList = [];
  265. foreach ($goodsSpecValue as $goodsSpecValue_val) {
  266. $goodsSpecValueList[$goodsSpecValue_val['spec_id']][] = $goodsSpecValue_val;
  267. }
  268. foreach ($goodsSpecValueList as $goodsSpecValueList_key=>$goodsSpecValueList_val) {
  269. $spec_values = array_column($goodsSpecValueList_val,'value');
  270. $spec_values = implode(',',$spec_values);
  271. $spec_ids = array_column($goodsSpecValueList_val,'id');
  272. $spec_ids = implode(',',$spec_ids);
  273. $goodsSpecValueList[$goodsSpecValueList_key] = [];
  274. $goodsSpecValueList[$goodsSpecValueList_key]['spec_values'] = $spec_values;
  275. $goodsSpecValueList[$goodsSpecValueList_key]['spec_ids'] = $spec_ids;
  276. }
  277. $goodsSpecValueList = array_values($goodsSpecValueList);
  278. $specValueData = [];
  279. foreach ($serverSpecValueList as $serverValue) {
  280. $specValueList = $postSpecValueList[$serverValue['ids']];
  281. $specValue = explode(GoodsEnum::SPEC_SEPARATOR, $serverValue['spec_value']);
  282. $specIds = [];
  283. //获取规格值对应的id
  284. foreach ($specValue as $specIndex => $specVal){
  285. $specListValues = explode(',',$goodsSpecValueList[$specIndex]['spec_values']);
  286. $specListIds = explode(',',$goodsSpecValueList[$specIndex]['spec_ids']);
  287. $specValueIds = array_combine($specListValues,$specListIds);
  288. $specIds[] = $specValueIds[$specVal];
  289. }
  290. $specValueData[] = [
  291. 'spec_value_ids' => implode(GoodsEnum::SPEC_SEPARATOR, $specIds),
  292. 'spec_value_str' => $serverValue['spec_value'],
  293. 'image' => $specValueList['image'],
  294. 'sell_price' => $specValueList['sell_price'],
  295. 'lineation_price' => $specValueList['lineation_price'],
  296. 'cost_price' => $specValueList['cost_price'],
  297. 'stock' => $specValueList['stock'],
  298. 'volume' => $specValueList['volume'],
  299. 'weight' => $specValueList['weight'],
  300. 'bar_code' => $specValueList['bar_code'],
  301. ];
  302. }
  303. $goods->specValueList()->saveAll($specValueData);
  304. }
  305. }
  306. /**
  307. * @notes 编辑商品规格信息
  308. * @param Goods $goods
  309. * @param array $params
  310. * @throws \think\db\exception\DataNotFoundException
  311. * @throws \think\db\exception\DbException
  312. * @throws \think\db\exception\ModelNotFoundException
  313. * @author cjhao
  314. * @date 2021/7/22 14:46
  315. */
  316. public function editGoodsItem(Goods $goods,array $params)
  317. {
  318. $specType = $params['spec_type']; //编辑后的规格类型
  319. $oldSpecType = $goods->oldSpecType; //原来的规格类型
  320. //编辑商品规格
  321. if(GoodsEnum::SEPC_TYPE_SIGNLE == $specType){
  322. //原来单规格,更新规格信息
  323. $specValueList = $params['spec_value_list'][0];
  324. if(GoodsEnum::SEPC_TYPE_SIGNLE == $oldSpecType){
  325. GoodsItem::update([
  326. 'id' => $specValueList['id'],
  327. 'sell_price' => $specValueList['sell_price'],
  328. 'lineation_price' => $specValueList['lineation_price'],
  329. 'cost_price' => $specValueList['cost_price'],
  330. 'stock' => $specValueList['stock'],
  331. 'volume' => $specValueList['volume'],
  332. 'weight' => $specValueList['weight'],
  333. 'bar_code' => $specValueList['bar_code'],
  334. ]);
  335. }else{
  336. //原来多规格,删除多规格数据
  337. GoodsSpec::where(['goods_id'=>$goods->id])->delete();
  338. GoodsSpecValue::where(['goods_id'=>$goods->id])->delete();
  339. GoodsItem::where(['goods_id'=>$goods->id])->delete();
  340. //写入单规格数据
  341. $goodsSpec = new GoodsSpec();
  342. $goodsSpec->goods_id = $goods->id;
  343. $goodsSpec->name = '默认';
  344. $goodsSpec->save();
  345. $goodsSpecValue = new GoodsSpecValue();
  346. $goodsSpecValue->save([
  347. 'goods_id' => $goodsSpec->goods_id,
  348. 'spec_id' => $goodsSpec->id,
  349. 'value' => '默认',
  350. ]);
  351. $specValueData = [
  352. 'goods_id' => $goods->id,
  353. 'spec_value_ids' => $goodsSpecValue->id,
  354. 'spec_value_str' => '默认',
  355. 'image' => $specValueList['image'],
  356. 'sell_price' => $specValueList['sell_price'],
  357. 'lineation_price' => $specValueList['lineation_price'],
  358. 'cost_price' => $specValueList['cost_price'],
  359. 'stock' => $specValueList['stock'],
  360. 'volume' => $specValueList['volume'],
  361. 'weight' => $specValueList['weight'],
  362. 'bar_code' => $specValueList['bar_code'],
  363. ];
  364. (new GoodsItem())->save($specValueData);
  365. }
  366. }else{
  367. //原单规格
  368. if(GoodsEnum::SEPC_TYPE_SIGNLE == $oldSpecType){
  369. //原来单规格,删除单规格数据
  370. GoodsSpec::where(['goods_id'=>$goods->id])->delete();
  371. GoodsSpecValue::where(['goods_id'=>$goods->id])->delete();
  372. GoodsItem::where(['goods_id'=>$goods->id])->delete();
  373. //添加规格项
  374. $postSpecValue = $params['spec_value'];
  375. $specData = [];
  376. foreach ($postSpecValue as $specVal) {
  377. $goodsSpec = new GoodsSpec();
  378. $goodsSpec->goods_id = $goods->id;
  379. $goodsSpec->name = $specVal['name'];
  380. $goodsSpec->save();
  381. //处理规格值
  382. array_walk($specVal['spec_list'], function ($spec) use ($goodsSpec,&$specData){
  383. $specData[] = ['spec_id'=>$goodsSpec->id,'value'=>$spec['value']];
  384. });
  385. }
  386. $goods->specValueSpec()->saveAll($specData);
  387. //添加规格信息
  388. $serverSpecValueList = $params['server_spec_value_list'];
  389. //改变数据结构,ids为索引
  390. $postSpecValueList = array_column($params['spec_value_list'], null, 'ids');
  391. // $goodsSpecValueList = GoodsSpecValue::where(['goods_id'=>$goods->id])
  392. // ->group('spec_id')
  393. // ->column(' GROUP_CONCAT(value Separator \',\') as spec_values,GROUP_CONCAT(id Separator \',\') as spec_ids');
  394. //GROUP_CONCAT函数有长度限制,替换成下面的方法
  395. $goodsSpecValue = GoodsSpecValue::where(['goods_id'=>$goods->id])
  396. ->column('id,spec_id,value');
  397. $goodsSpecValueList = [];
  398. foreach ($goodsSpecValue as $goodsSpecValue_val) {
  399. $goodsSpecValueList[$goodsSpecValue_val['spec_id']][] = $goodsSpecValue_val;
  400. }
  401. foreach ($goodsSpecValueList as $goodsSpecValueList_key=>$goodsSpecValueList_val) {
  402. $spec_values = array_column($goodsSpecValueList_val,'value');
  403. $spec_values = implode(',',$spec_values);
  404. $spec_ids = array_column($goodsSpecValueList_val,'id');
  405. $spec_ids = implode(',',$spec_ids);
  406. $goodsSpecValueList[$goodsSpecValueList_key] = [];
  407. $goodsSpecValueList[$goodsSpecValueList_key]['spec_values'] = $spec_values;
  408. $goodsSpecValueList[$goodsSpecValueList_key]['spec_ids'] = $spec_ids;
  409. }
  410. $goodsSpecValueList = array_values($goodsSpecValueList);
  411. $specValueData = [];
  412. foreach ($serverSpecValueList as $serverValue) {
  413. $specValueList = $postSpecValueList[$serverValue['ids']];
  414. $specValue = explode(GoodsEnum::SPEC_SEPARATOR, $serverValue['spec_value']);
  415. $specIds = [];
  416. //获取规格值对应的id
  417. foreach ($specValue as $specIndex => $specVal){
  418. $specListValues = explode(',',$goodsSpecValueList[$specIndex]['spec_values']);
  419. $specListIds = explode(',',$goodsSpecValueList[$specIndex]['spec_ids']);
  420. $specValueIds = array_combine($specListValues,$specListIds);
  421. $specIds[] = $specValueIds[$specVal];
  422. }
  423. $specValueData[] = [
  424. 'spec_value_ids' => implode(GoodsEnum::SPEC_SEPARATOR, $specIds),
  425. 'spec_value_str' => $serverValue['spec_value'],
  426. 'image' => $specValueList['image'],
  427. 'sell_price' => $specValueList['sell_price'],
  428. 'lineation_price' => $specValueList['lineation_price'],
  429. 'cost_price' => $specValueList['cost_price'],
  430. 'stock' => $specValueList['stock'],
  431. 'volume' => $specValueList['volume'],
  432. 'weight' => $specValueList['weight'],
  433. 'bar_code' => $specValueList['bar_code'],
  434. ];
  435. }
  436. $goods->specValueList()->saveAll($specValueData);
  437. }else{
  438. //原来多规格,改变后还是多规格
  439. $goodsSpecIds = GoodsSpec::where(['goods_id'=>$goods->id])->column('id');
  440. $goodsSpecValueIds = GoodsSpecValue::where(['goods_id'=>$goods->id])->column('id');
  441. $goodsItemIds = GoodsItem::where(['goods_id'=>$goods->id])->column('id');
  442. $postSpecValue = $params['spec_value'];
  443. $postGoodsSpecValueIds = [];
  444. foreach ($postSpecValue as $spec) {
  445. $goodsSpec = new GoodsSpec();
  446. //存在规格id,进行更新操作
  447. if($spec['id'] > 0){
  448. $goodsSpec = $goodsSpec->find($spec['id']);
  449. }
  450. $goodsSpec->goods_id = $goods->id;
  451. $goodsSpec->name = $spec['name'];
  452. $goodsSpec->save();
  453. //合并规格值id
  454. $postGoodsSpecValueIds = array_merge($postGoodsSpecValueIds,array_column($spec['spec_list'],'id'));
  455. //写入规格,存在则更新
  456. array_walk($spec['spec_list'], function ($specVal) use ($goodsSpec, &$specList) {
  457. $goodsSpecValue = new GoodsSpecValue();
  458. //存在规格值id,进行更新操作
  459. if($specVal['id'] > 0){
  460. $goodsSpecValue = $goodsSpecValue->find($specVal['id']);
  461. }
  462. $goodsSpecValue->save([
  463. 'goods_id' => $goodsSpec->goods_id,
  464. 'spec_id' => $goodsSpec->id,
  465. 'value' => $specVal['value'],
  466. ]);
  467. });
  468. }
  469. //添加规格信息
  470. $serverSpecValueList = $params['server_spec_value_list'];
  471. //改变数据结构,ids为索引
  472. $postSpecValueList = array_column($params['spec_value_list'], null, 'ids');
  473. $specValues = array_column($serverSpecValueList, 'spec_value');
  474. $specValues = implode(',',$specValues);
  475. // $goodsSpecValueList = GoodsSpecValue::where(['goods_id'=>$goods->id])
  476. // ->where('value','in',$specValues)
  477. // ->group('spec_id')
  478. // ->column(' GROUP_CONCAT(value Separator \',\') as spec_values,GROUP_CONCAT(id Separator \',\') as spec_ids');
  479. //GROUP_CONCAT函数有长度限制,替换成下面的方法
  480. $goodsSpecValue = GoodsSpecValue::where(['goods_id'=>$goods->id])
  481. ->where('value','in',$specValues)
  482. ->column('id,spec_id,value');
  483. $goodsSpecValueList = [];
  484. foreach ($goodsSpecValue as $goodsSpecValue_val) {
  485. $goodsSpecValueList[$goodsSpecValue_val['spec_id']][] = $goodsSpecValue_val;
  486. }
  487. foreach ($goodsSpecValueList as $goodsSpecValueList_key=>$goodsSpecValueList_val) {
  488. $spec_values = array_column($goodsSpecValueList_val,'value');
  489. $spec_values = implode(',',$spec_values);
  490. $spec_ids = array_column($goodsSpecValueList_val,'id');
  491. $spec_ids = implode(',',$spec_ids);
  492. $goodsSpecValueList[$goodsSpecValueList_key] = [];
  493. $goodsSpecValueList[$goodsSpecValueList_key]['spec_values'] = $spec_values;
  494. $goodsSpecValueList[$goodsSpecValueList_key]['spec_ids'] = $spec_ids;
  495. }
  496. $goodsSpecValueList = array_values($goodsSpecValueList);
  497. $specValueData = [];
  498. foreach ($serverSpecValueList as $serverValue) {
  499. $specValueList = $postSpecValueList[$serverValue['ids']];
  500. $specValue = explode(GoodsEnum::SPEC_SEPARATOR, $serverValue['spec_value']);
  501. $specIds = [];
  502. //获取规格值对应的id
  503. foreach ($specValue as $specIndex => $specVal){
  504. $specListValues = explode(',',$goodsSpecValueList[$specIndex]['spec_values']);
  505. $specListIds = explode(',',$goodsSpecValueList[$specIndex]['spec_ids']);
  506. $specValueIds = array_combine($specListValues,$specListIds);
  507. $specIds[] = $specValueIds[$specVal];
  508. }
  509. //添加的数据
  510. $itemData = [
  511. 'spec_value_ids' => implode(GoodsEnum::SPEC_SEPARATOR, $specIds),
  512. 'spec_value_str' => $serverValue['spec_value'],
  513. 'image' => $specValueList['image'],
  514. 'sell_price' => $specValueList['sell_price'],
  515. 'lineation_price' => $specValueList['lineation_price'],
  516. 'cost_price' => $specValueList['cost_price'],
  517. 'stock' => $specValueList['stock'],
  518. 'volume' => $specValueList['volume'],
  519. 'weight' => $specValueList['weight'],
  520. 'bar_code' => $specValueList['bar_code'],
  521. ];
  522. //更新规格
  523. if($specValueList['id'] > 0){
  524. $itemData['id'] = $specValueList['id'];
  525. }
  526. $specValueData[] = $itemData;
  527. }
  528. $goods->specValueList()->saveAll($specValueData);
  529. $postSpecIds = array_column($postSpecValue,'id');
  530. $postItemIds = array_column($params['spec_value_list'],'id');
  531. //对比规格是否需要删除
  532. $delSpecIds = array_diff($goodsSpecIds, $postSpecIds);
  533. $delSpecValyeIds = array_diff($goodsSpecValueIds, $postGoodsSpecValueIds);
  534. $delItemIds = array_diff($goodsItemIds, $postItemIds);
  535. //需要删除规格名
  536. if($delSpecIds){
  537. GoodsSpec::where(['id'=>array_values($delSpecIds)])->delete();
  538. }
  539. //删除规格值
  540. if($delSpecValyeIds){
  541. GoodsSpecValue::where(['id'=>array_values($delSpecValyeIds)])->delete();
  542. }
  543. //删除规格信息
  544. if($delItemIds){
  545. GoodsItem::where(['id'=>array_values($delItemIds)])->delete();
  546. }
  547. }
  548. }
  549. }
  550. /**
  551. * @notes 商品其他列表
  552. * @param string $type
  553. * @return array
  554. * @throws \think\db\exception\DataNotFoundException
  555. * @throws \think\db\exception\DbException
  556. * @throws \think\db\exception\ModelNotFoundException
  557. * @author cjhao
  558. * @date 2021/7/22 15:47
  559. */
  560. public function otherList(string $type)
  561. {
  562. $supplierList = GoodsSupplier::field('id,name')->order('sort','asc')->select();
  563. $categoryList = GoodsCategory::field('id,pid,name')->order('sort','asc')->select()->toArray();
  564. $categoryList = linear_to_tree($categoryList,'sons');
  565. if('all' !== $type){
  566. $activityLists = ActivityEnum::getActivityDesc();
  567. $activityLists = [0=>'未参与'] + $activityLists;
  568. return [
  569. 'supplier_list' => $supplierList,
  570. 'category_list' => $categoryList,
  571. 'type_list' => GoodsEnum::getGoodsTypeDesc(),
  572. 'activity_list' => $activityLists,
  573. ];
  574. }
  575. $unitList = GoodsUnit::field('id,name')->order('sort','asc')->select();
  576. $brandList = GoodsBrand::field('id,name')->order('sort','asc')->select();
  577. $freightList = Freight::field('id,name')->select();
  578. $list = [
  579. 'supplier_list' => $supplierList,
  580. 'category_list' => $categoryList,
  581. 'brand_list' => $brandList,
  582. 'unit_list' => $unitList,
  583. 'freight_list' => $freightList,
  584. ];
  585. return $list;
  586. }
  587. /**
  588. * @notes 商品详情
  589. * @param int $id 商品id
  590. * @return array|\think\Model|null
  591. * @throws \think\db\exception\DataNotFoundException
  592. * @throws \think\db\exception\DbException
  593. * @throws \think\db\exception\ModelNotFoundException
  594. * @author cjhao
  595. * @date 2021/7/15 15:49
  596. */
  597. public function detail(int $id)
  598. {
  599. $goods = Goods::with(['spec_value.spec_list','spec_value_list'])
  600. ->withoutField('create_time,update_time')
  601. ->append(['goods_image','category_id'])
  602. ->find($id)
  603. ->toArray();
  604. return $goods;
  605. }
  606. /**
  607. * @notes 商品上下架
  608. * @param array $params
  609. * @return Goods
  610. * @author cjhao
  611. * @date 2021/7/17 17:10
  612. */
  613. public function status(array $params)
  614. {
  615. $status = YesNoEnum::YES == $params['status'] ? 1 : 0;
  616. return Goods::update(['status'=>$status],['id'=>$params['ids']]);
  617. }
  618. /**
  619. * @notes 设置商品上下架
  620. * @param array $params
  621. * @return Goods
  622. * @author cjhao
  623. * @date 2021/7/22 9:51
  624. */
  625. public function sort(array $params)
  626. {
  627. return Goods::update(['id'=>$params['id'],'sort'=>(int)$params['sort']]);
  628. }
  629. /**
  630. * @notes 删除商品 todo 删除接口需要考虑商品的其他状态
  631. * @param array $ids
  632. * @return bool
  633. * @author cjhao
  634. * @date 2021/7/17 17:23
  635. */
  636. public function del(array $ids)
  637. {
  638. Db::startTrans();
  639. try {
  640. Goods::destroy(['id'=>$ids]);
  641. // 删除商品时清除已设置好的分销比例
  642. $deleteIds = DistributionGoods::where('goods_id', 'in', $ids)->column('id');
  643. DistributionGoods::destroy($deleteIds);
  644. // 删掉用户购物车商品
  645. Cart::where(['goods_id'=>$ids])->delete();
  646. Db::commit();
  647. return true;
  648. } catch (\Exception $e) {
  649. Db::rollback();
  650. return false;
  651. }
  652. }
  653. /**
  654. * @notes 修改商品名称
  655. * @param array $params
  656. * @return Goods
  657. * @author cjhao
  658. * @date 2021/7/29 16:11
  659. */
  660. public function rename(array $params)
  661. {
  662. return Goods::update(['id'=>$params['id'],'name'=>$params['name']]);
  663. }
  664. /**
  665. * @notes 移动分类
  666. * @param array $params
  667. * @throws \Exception
  668. * @author ljj
  669. * @date 2023/5/6 2:24 下午
  670. */
  671. public function changeCategory(array $params)
  672. {
  673. //删除旧的商品分类
  674. GoodsCategoryIndex::where(['goods_id'=>$params['ids']])->delete();
  675. //创建新的商品分类
  676. $data = [];
  677. foreach ($params['ids'] as $id) {
  678. foreach ($params['category_id'] as $category_id) {
  679. $data[] = ['goods_id'=>$id,'category_id'=>$category_id];
  680. }
  681. }
  682. (new GoodsCategoryIndex())->saveAll($data);
  683. return true;
  684. }
  685. /**
  686. * @notes 商品规格价格导入
  687. * @param array $params
  688. * @return bool|string
  689. * @author
  690. * @date 2024/01/01 00:00
  691. */
  692. public function importSpecPrice(array $params)
  693. {
  694. if (empty($params['file'])) {
  695. return '请上传导入文件';
  696. }
  697. Db::startTrans();
  698. try {
  699. // 处理上传的Excel文件
  700. $file = $params['file'];
  701. $data = $this->parseExcelFile($file);
  702. if (empty($data)) {
  703. return '导入文件为空或格式错误';
  704. }
  705. $successCount = 0;
  706. $errorMessages = [];
  707. foreach ($data as $row => $item) {
  708. try {
  709. // 验证必要字段
  710. if (empty($item['item_id']) || empty($item['goods_id'])) {
  711. $errorMessages[] = "第{$row}行:商品ID或规格ID不能为空";
  712. continue;
  713. }
  714. // 检查商品是否存在
  715. $goods = Goods::find($item['goods_id']);
  716. if (!$goods) {
  717. $errorMessages[] = "第{$row}行:商品ID {$item['goods_id']} 不存在";
  718. continue;
  719. }
  720. // 检查规格是否存在
  721. $goodsItem = GoodsItem::where('id', $item['item_id'])
  722. ->where('goods_id', $item['goods_id'])
  723. ->find();
  724. if (!$goodsItem) {
  725. $errorMessages[] = "第{$row}行:规格ID {$item['item_id']} 不存在或不属于该商品";
  726. continue;
  727. }
  728. // 更新规格价格信息
  729. $updateData = [];
  730. if (isset($item['sell_price']) && is_numeric($item['sell_price'])) {
  731. $updateData['sell_price'] = $item['sell_price'];
  732. }
  733. if (isset($item['lineation_price']) && is_numeric($item['lineation_price'])) {
  734. $updateData['lineation_price'] = $item['lineation_price'];
  735. }
  736. if (isset($item['cost_price']) && is_numeric($item['cost_price'])) {
  737. $updateData['cost_price'] = $item['cost_price'];
  738. }
  739. if (isset($item['stock']) && is_numeric($item['stock'])) {
  740. $updateData['stock'] = $item['stock'];
  741. }
  742. if (isset($item['weight']) && is_numeric($item['weight'])) {
  743. $updateData['weight'] = $item['weight'];
  744. }
  745. if (isset($item['volume']) && is_numeric($item['volume'])) {
  746. $updateData['volume'] = $item['volume'];
  747. }
  748. if (!empty($updateData)) {
  749. GoodsItem::where('id', $item['item_id'])->update($updateData);
  750. $successCount++;
  751. }
  752. } catch (\Exception $e) {
  753. $errorMessages[] = "第{$row}行:" . $e->getMessage();
  754. }
  755. }
  756. Db::commit();
  757. if (!empty($errorMessages)) {
  758. return "导入完成,成功{$successCount}条,失败" . count($errorMessages) . "条。错误信息:" . implode(';', array_slice($errorMessages, 0, 5));
  759. }
  760. return true;
  761. } catch (\Exception $e) {
  762. Db::rollback();
  763. return '导入失败:' . $e->getMessage();
  764. }
  765. }
  766. /**
  767. * @notes 解析Excel文件
  768. * @param string $file
  769. * @return array
  770. * @author
  771. * @date 2024/01/01 00:00
  772. */
  773. private function parseExcelFile($file)
  774. {
  775. try {
  776. // 使用PhpSpreadsheet处理Excel文件
  777. if (is_array($file)) {
  778. return $file;
  779. }
  780. // 检查文件是否存在
  781. if (!file_exists($file)) {
  782. throw new \Exception('文件不存在');
  783. }
  784. // 加载Excel文件
  785. $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file);
  786. $worksheet = $spreadsheet->getActiveSheet();
  787. $data = $worksheet->toArray();
  788. if (empty($data)) {
  789. throw new \Exception('Excel文件为空');
  790. }
  791. // 获取表头
  792. $headers = array_shift($data);
  793. $result = [];
  794. // 将表头映射为字段名
  795. $fieldMap = [
  796. '商品ID' => 'goods_id',
  797. '商品编码' => 'goods_code',
  798. '商品名称' => 'goods_name',
  799. '规格ID' => 'item_id',
  800. '规格名称' => 'spec_value_str',
  801. '销售价格' => 'sell_price',
  802. '划线价格' => 'lineation_price',
  803. '成本价格' => 'cost_price',
  804. '库存数量' => 'stock',
  805. '重量(kg)' => 'weight',
  806. '体积(m³)' => 'volume'
  807. ];
  808. // 创建字段索引映射
  809. $fieldIndexes = [];
  810. foreach ($headers as $index => $header) {
  811. if (isset($fieldMap[$header])) {
  812. $fieldIndexes[$fieldMap[$header]] = $index;
  813. }
  814. }
  815. // 处理数据行
  816. foreach ($data as $rowIndex => $row) {
  817. if (empty(array_filter($row))) {
  818. continue; // 跳过空行
  819. }
  820. $item = [];
  821. foreach ($fieldIndexes as $field => $index) {
  822. $item[$field] = isset($row[$index]) ? trim($row[$index]) : '';
  823. }
  824. if (!empty($item)) {
  825. $result[$rowIndex + 2] = $item; // +2 因为表头占一行,数组从0开始
  826. }
  827. }
  828. return $result;
  829. } catch (\Exception $e) {
  830. throw new \Exception('Excel文件解析失败:' . $e->getMessage());
  831. }
  832. }
  833. }