截流自动化的商城平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GoodsLogic.php 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop开源商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | gitee下载:https://gitee.com/likeshop_gitee
  7. // | github下载:https://github.com/likeshop-github
  8. // | 访问官网:https://www.likeshop.cn
  9. // | 访问社区:https://home.likeshop.cn
  10. // | 访问手册:http://doc.likeshop.cn
  11. // | 微信公众号:likeshop技术社区
  12. // | likeshop系列产品在gitee、github等公开渠道开源版本可免费商用,未经许可不能去除前后端官方版权标识
  13. // | likeshop系列产品收费版本务必购买商业授权,购买去版权授权后,方可去除前后端官方版权标识
  14. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  15. // | likeshop团队版权所有并拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshop.cn.team
  18. // +----------------------------------------------------------------------
  19. namespace app\shop\logic\goods;
  20. use app\common\basics\Logic;
  21. use app\common\enum\GoodsEnum;
  22. use app\common\model\distribution\DistributionGoods;
  23. use app\common\model\goods\Goods;
  24. use app\common\model\goods\GoodsImage;
  25. use app\common\model\goods\GoodsItem;
  26. use app\common\model\goods\GoodsSpec;
  27. use app\common\model\goods\GoodsSpecValue;
  28. use app\common\server\JsonServer;
  29. use app\common\server\UrlServer;
  30. use think\facade\Db;
  31. use app\common\model\shop\Shop;
  32. use think\facade\Validate;
  33. /**
  34. * 商品管理-逻辑
  35. * Class GoodsLogic
  36. * @package app\shop\logic\goods
  37. */
  38. class GoodsLogic extends Logic
  39. {
  40. /**
  41. * @notes 商品统计
  42. * @param $shop_id
  43. * @return int[]
  44. * @throws \think\db\exception\DataNotFoundException
  45. * @throws \think\db\exception\DbException
  46. * @throws \think\db\exception\ModelNotFoundException
  47. * @author 段誉
  48. * @date 2022/4/7 11:57
  49. */
  50. public static function statistics($shop_id){
  51. $where = [
  52. ['del', '<>', GoodsEnum::DEL_TRUE],
  53. ['shop_id', '=', $shop_id]
  54. ];
  55. return [
  56. // 销售中商品
  57. // 销售状态:上架中;删除状态:正常; 审核状态: 审核通过
  58. 'sell' => Goods::where($where)
  59. ->where('del', GoodsEnum::DEL_NORMAL)
  60. ->where('status', GoodsEnum::STATUS_SHELVES)
  61. ->where('audit_status', GoodsEnum::AUDIT_STATUS_OK)
  62. ->where('stock', '>=', Db::raw('stock_warn'))
  63. ->count(),
  64. // 库存预警商品
  65. // 销售状态:上架中;删除状态:正常; 审核状态: 审核通过;总库存 < 库存预警
  66. 'warn' => Goods::where($where)
  67. ->where('del', GoodsEnum::DEL_NORMAL)
  68. ->where('status', GoodsEnum::STATUS_SHELVES)
  69. ->where('audit_status', GoodsEnum::AUDIT_STATUS_OK)
  70. ->where('stock', '<', Db::raw('stock_warn'))
  71. ->count(),
  72. // 仓库中商品
  73. // 销售状态:仓库中;删除状态:正常; 审核状态: 审核通过
  74. 'warehouse' => Goods::where($where)
  75. ->where('del', GoodsEnum::DEL_NORMAL)
  76. ->where('status', GoodsEnum::STATUS_SOLD_OUT)
  77. ->where('audit_status', GoodsEnum::AUDIT_STATUS_OK)
  78. ->count(),
  79. // 回收站商品
  80. // 销售状态:任意;删除状态:回收站; 审核状态: 审核通过
  81. 'recycle' => Goods::where($where)
  82. ->where('del', GoodsEnum::DEL_RECYCLE)
  83. ->where('audit_status', GoodsEnum::AUDIT_STATUS_OK)
  84. ->count(),
  85. // 待审核商品
  86. // 销售状态:任意;删除状态:排除已删除; 审核状态: 待审核
  87. 'audit_stay' => Goods::where($where)
  88. ->where('del', '<>', GoodsEnum::DEL_TRUE)
  89. ->where('audit_status', GoodsEnum::AUDIT_STATUS_STAY)
  90. ->count(),
  91. // 审核未通过商品
  92. // 销售状态:任意;删除状态:排除已删除; 审核状态: 审核未通过
  93. 'audit_refuse'=> Goods::where($where)
  94. ->where('del', '<>', GoodsEnum::DEL_TRUE)
  95. ->where('audit_status', GoodsEnum::AUDIT_STATUS_REFUSE)
  96. ->count(),
  97. ];
  98. }
  99. /**
  100. * Notes: 列表
  101. * @param $get
  102. * @author 段誉(2021/4/15 10:53)
  103. * @return array
  104. * @throws \think\db\exception\DataNotFoundException
  105. * @throws \think\db\exception\DbException
  106. * @throws \think\db\exception\ModelNotFoundException
  107. */
  108. public static function lists($get)
  109. {
  110. $where = [
  111. ['shop_id', '=', $get['shop_id']]
  112. ];
  113. if(isset($get['goods_name']) && $get['goods_name']) {
  114. $where[] = ['name','like','%'.$get['goods_name'].'%'];
  115. }
  116. if(!empty($get['platform_cate_id'])) {
  117. $where[] = ['first_cate_id|second_cate_id|third_cate_id','=', $get['platform_cate_id']];
  118. }
  119. if(!empty($get['shop_cate_id'])) {
  120. $where[] = ['shop_cate_id','=', $get['shop_cate_id']];
  121. }
  122. if(isset($get['goods_type']) && $get['goods_type'] != '') {
  123. $where[] = ['type','=', $get['goods_type']];
  124. }
  125. if (Validate::must($get['is_distribution'] ?? '')) {
  126. $where[] = [ 'is_distribution', '=', $get['is_distribution'] ];
  127. }
  128. if (Validate::must($get['is_member'] ?? '')) {
  129. $where[] = [ 'is_member', '=', $get['is_member'] ];
  130. }
  131. $type = $get['type'] ?? 0;
  132. switch ($type) {
  133. case 1: //销售中
  134. $where[] = ['status', '=', GoodsEnum::STATUS_SHELVES];//上架
  135. $where[] = ['del', '=', GoodsEnum::DEL_NORMAL];
  136. $where[] = ['audit_status', '=', GoodsEnum::AUDIT_STATUS_OK];//审核通过
  137. $where[] = ['stock','exp', Db::raw('>=stock_warn')];
  138. break;
  139. case 2: //库存预警
  140. $where[] = ['status', '=', GoodsEnum::STATUS_SHELVES];//上架
  141. $where[] = ['del', '=', GoodsEnum::DEL_NORMAL];
  142. $where[] = ['audit_status', '=', GoodsEnum::AUDIT_STATUS_OK];//审核通过
  143. $where[] = ['stock','exp', Db::raw('<stock_warn')];
  144. break;
  145. case 3: //仓库中
  146. $where[] = ['status', '=', GoodsEnum::STATUS_SOLD_OUT];//下架
  147. $where[] = ['del', '=', GoodsEnum::DEL_NORMAL];
  148. $where[] = ['audit_status', '=', GoodsEnum::AUDIT_STATUS_OK];//审核通过
  149. break;
  150. case 4: //回收站
  151. $where[] = ['del', '=', GoodsEnum::DEL_RECYCLE];
  152. $where[] = ['audit_status', '=', GoodsEnum::AUDIT_STATUS_OK];//审核通过
  153. break;
  154. case 5: //待审核
  155. $where[] = ['del', '<>', GoodsEnum::DEL_TRUE];
  156. $where[] = ['audit_status', '=', GoodsEnum::AUDIT_STATUS_STAY];
  157. break;
  158. case 6: //审核未通过
  159. $where[] = ['del', '<>', GoodsEnum::DEL_TRUE];
  160. $where[] = ['audit_status', '=', GoodsEnum::AUDIT_STATUS_REFUSE];
  161. break;
  162. default:
  163. $where[] = ['del', '=', GoodsEnum::DEL_NORMAL];
  164. }
  165. $order = [
  166. 'sort' => 'asc',
  167. 'id' => 'desc'
  168. ];
  169. $lists = Goods::where($where)
  170. ->append([ 'is_distribution_desc', 'is_member_desc' ])
  171. ->page($get['page'], $get['limit'])
  172. ->order($order)
  173. ->select();
  174. $count = Goods::where($where)->count();
  175. if($count) {
  176. $lists = $lists->toArray();
  177. }else{
  178. $lists = [];
  179. }
  180. foreach ($lists as &$item) {
  181. // 处理价格格式
  182. $item['price'] = $item['spec_type'] == 1 ? $item["min_price"] : $item["min_price"] . " ~ " . $item["max_price"];
  183. }
  184. return ['count' => $count, 'lists' => $lists];
  185. }
  186. /**
  187. * Notes: 添加商品
  188. * @param $shop_id
  189. * @param $post
  190. * @param $spec_lists
  191. * @author 段誉(2021/4/20 15:14)
  192. * @return bool
  193. */
  194. public static function add($shop_id, $post, $spec_lists)
  195. {
  196. Db::startTrans();
  197. try {
  198. // 图片去除域名
  199. $post['image'] = UrlServer::setFileUrl($post['image']);
  200. $post['goods_image'] = array_map(function($value) {
  201. return UrlServer::setFileUrl($value);
  202. }, $post['goods_image']);
  203. //添加商品主表
  204. $goods_id = self::addGoods($shop_id, $post);
  205. //添加商品轮播图
  206. self::addGoodsImage($goods_id,$post);
  207. //添加规格项、规格值、SKU
  208. if ($post['spec_type'] == 1) {
  209. self::addOneSpec($goods_id, $post);
  210. } else {
  211. self::addMoreSpec($goods_id, $post, $spec_lists);
  212. }
  213. Db::commit();
  214. return true;
  215. } catch (\Exception $e) {
  216. Db::rollback();
  217. self::$error = $e->getMessage();
  218. return false;
  219. }
  220. }
  221. /**
  222. * @notes 删除
  223. * @param $shop_id
  224. * @param $id
  225. * @return Goods
  226. * @author 段誉
  227. * @date 2022/2/14 18:51
  228. */
  229. public static function del($shop_id, $id)
  230. {
  231. $result = Goods::update(['del' => 1], ['id' => $id, 'shop_id' => $shop_id]);
  232. event('UpdateCollect', ['goods_id' => $id]);
  233. return $result;
  234. }
  235. /**
  236. * 获取商品信息
  237. * @param $goods_id
  238. * @return array
  239. */
  240. public static function info($goods_id)
  241. {
  242. // 商品主表
  243. $info['base'] = Goods::where(['id' => $goods_id])
  244. ->withAttr('abs_image', function ($value, $data) {
  245. return UrlServer::getFileUrl($data['image']);
  246. })
  247. ->withAttr('content', function ($value){
  248. $preg = '/(<img .*?src=")[^https|^http](.*?)(".*?>)/is';
  249. $local_url = UrlServer::getFileUrl('/');
  250. return preg_replace($preg, "\${1}$local_url\${2}\${3}",$value);
  251. })
  252. ->withAttr('poster', function ($value){
  253. return empty($value) ? '' : UrlServer::getFileUrl($value);
  254. })
  255. ->withAttr('abs_video',function ($value,$data){
  256. if($data['video']){
  257. return UrlServer::getFileUrl($data['video']);
  258. }
  259. return '';
  260. })->append(['abs_image','abs_video'])->find();
  261. // 商品轮播图
  262. $info['base']['goods_image'] = GoodsImage::where(['goods_id' => $goods_id])
  263. ->withAttr('abs_image', function ($value, $data) {
  264. return UrlServer::getFileUrl($data['uri']);})
  265. ->append(['abs_image'])
  266. ->select();
  267. // 商品SKU
  268. $info['item'] =GoodsItem::where(['goods_id' => $goods_id])
  269. ->withAttr('abs_image', function ($value, $data) {
  270. return $data['image'] ? UrlServer::getFileUrl($data['image']) : '';
  271. })->append(['abs_image'])
  272. ->select();
  273. // 商品规格项
  274. $info['spec'] = GoodsSpec::where(['goods_id' => $goods_id])->select();
  275. // 商品规格值
  276. $spec_value = GoodsSpecValue::where(['goods_id' => $goods_id])->select();
  277. $data = [];
  278. foreach ($spec_value as $k => $v) {
  279. $data[$v['spec_id']][] = $v;
  280. }
  281. foreach ($info['spec'] as $k => $v) {
  282. $info['spec'][$k]['values'] = isset($data[$v['id']]) ? $data[$v['id']] : [];
  283. }
  284. return $info;
  285. }
  286. /**
  287. * 编辑
  288. */
  289. public static function edit($post, $spec_lists)
  290. {
  291. Db::startTrans();
  292. try {
  293. $oldItemIds = GoodsItem::where('goods_id', $post['id'])->column('id');
  294. //计算最大最小价格
  295. if ($post['spec_type'] == 1) {
  296. $max_price = $post['one_price'];
  297. $min_price = $post['one_price'];
  298. $market_price = $post['one_market_price'];
  299. $total_stock = $post['one_stock'];
  300. } else {
  301. $max_price = max($post['price']);
  302. $min_price = min($post['price']);
  303. $min_price_key = array_search($min_price,$post['price']);
  304. $market_price = $post['market_price'][$min_price_key];
  305. $total_stock = array_sum($post['stock']);
  306. }
  307. // 库存校验
  308. if($post['status'] == GoodsEnum::STATUS_SHELVES && $total_stock == 0) {
  309. throw new \Exception('库存为0不允许上架');
  310. }
  311. $old_spec_type = Goods::where('id', $post['goods_id'])->value('spec_type');
  312. // 格式化数据
  313. $post = self::formatGoodsData($post);
  314. //更新主表
  315. $data = [
  316. 'name' => $post['name'],
  317. 'code' => $post['code'],
  318. 'shop_cate_id' => $post['shop_cate_id'],
  319. 'first_cate_id' => $post['first_cate_id'],
  320. 'second_cate_id' => $post['second_cate_id'],
  321. 'third_cate_id' => $post['third_cate_id'],
  322. 'brand_id' => $post['brand_id'],
  323. 'unit_id' => $post['unit_id'],
  324. 'supplier_id' => $post['supplier_id'],
  325. 'status' => $post['status'],
  326. 'image' => clearDomain($post['image']),
  327. 'video' => $post['video'] ?? '',
  328. 'remark' => $post['remark'],
  329. 'content' => $post['content'],
  330. 'sort' => $post['sort'],
  331. 'spec_type' => $post['spec_type'],
  332. 'max_price' => $max_price,
  333. 'min_price' => $min_price,
  334. 'market_price' => $market_price,
  335. 'stock' => $total_stock,
  336. 'express_type' => $post['express_type'],
  337. 'express_money' => $post['express_money'],
  338. 'express_template_id' => $post['express_template_id'],
  339. 'is_recommend' => $post['is_recommend'],
  340. 'update_time' => time(),
  341. 'stock_warn' => $post['stock_warn'],
  342. 'poster' => isset($post['poster']) ? clearDomain($post['poster']) : '',
  343. 'is_show_stock' => $post['is_show_stock'],
  344. 'is_member' => $post['is_member'],
  345. 'delivery_type' => implode(',', $post['delivery_type']),
  346. 'after_pay' => $post['after_pay'] ?? 0,
  347. 'after_delivery' => $post['after_delivery'] ?? 0,
  348. 'delivery_content' => $post['delivery_content'] ?? '',
  349. ];
  350. // 判断是否为未审核通过的商品
  351. $audit_status = Goods::where(['id' => $post['goods_id']])->value('audit_status');
  352. if($audit_status == GoodsEnum::AUDIT_STATUS_REFUSE) {
  353. $data['audit_status'] = GoodsEnum::AUDIT_STATUS_STAY; // 编辑后未审核通过的商品状态置为待审核
  354. }
  355. Goods::where(['id' => $post['goods_id']])->update($data);
  356. if ($data['status'] != GoodsEnum::STATUS_SHELVES) {
  357. event('UpdateCollect', ['goods_id' => $post['goods_id']]);
  358. }
  359. //先删除再重新添加轮播图
  360. GoodsImage::where(['goods_id' => $post['goods_id']])->delete();
  361. $data = [];
  362. foreach ($post['goods_image'] as $k => $v) {
  363. $data[] = [
  364. 'goods_id' => $post['goods_id'],
  365. 'uri' => clearDomain($v),
  366. ];
  367. }
  368. (new GoodsImage())->saveAll($data);
  369. //写入规格表
  370. if ($post['spec_type'] == 1) {
  371. //单规格写入
  372. if ($old_spec_type == 1) {
  373. //原来是单规格
  374. $data = [
  375. 'image' => isset($post['one_spec_image']) ? clearDomain($post['one_spec_image']) : '',
  376. 'market_price' => $post['one_market_price'],
  377. 'price' => $post['one_price'],
  378. 'chengben_price' => $post['one_chengben_price'],
  379. 'stock' => $post['one_stock'],
  380. 'weight' => $post['one_weight'],
  381. 'volume' => $post['one_volume'],
  382. 'bar_code' => $post['one_bar_code'],
  383. ];
  384. GoodsItem::where(['goods_id' => $post['goods_id']])->update($data);
  385. } else {
  386. //原来多规格
  387. //删除多规格
  388. GoodsSpec::where('goods_id', $post['goods_id'])->delete();
  389. GoodsSpecValue::where('goods_id', $post['goods_id'])->delete();
  390. GoodsItem::where('goods_id', $post['goods_id'])->delete();
  391. $goodsSpec = GoodsSpec::create(['goods_id' => $post['goods_id'], 'name' => '默认']);
  392. $goods_spec_id = $goodsSpec->id;
  393. $goodsSpecValue = GoodsSpecValue::create(['spec_id' => $goods_spec_id, 'goods_id' => $post['goods_id'], 'value' => '默认']);
  394. $goods_spec_value_id = $goodsSpecValue->id;
  395. $data = [
  396. 'image' => isset($post['one_spec_image']) ? clearDomain($post['one_spec_image']) : '',
  397. 'goods_id' => $post['goods_id'],
  398. 'spec_value_ids' => $goods_spec_value_id,
  399. 'spec_value_str' => '默认',
  400. 'market_price' => $post['one_market_price'],
  401. 'price' => $post['one_price'],
  402. 'chengben_price' => $post['one_chengben_price'],
  403. 'stock' => $post['one_stock'],
  404. 'volume' => $post['one_volume'],
  405. 'weight' => $post['one_weight'],
  406. 'bar_code' => $post['one_bar_code'],
  407. ];
  408. GoodsItem::create($data);
  409. }
  410. } else {
  411. // 多规格写入
  412. $goods_specs = [];
  413. foreach ($post['spec_name'] as $k => $v) {
  414. $temp = ['goods_id' => $post['goods_id'], 'name' => $v, 'spec_id' => $post['spec_id'][$k]];
  415. $goods_specs[] = $temp;
  416. }
  417. $new_spec_name_ids = [];
  418. foreach ($goods_specs as $k => $v) {
  419. if ($v['spec_id']) {
  420. //更新规格名
  421. GoodsSpec::where(['goods_id' => $post['goods_id'], 'id' => $v['spec_id']])
  422. ->update(['name' => $v['name']]);
  423. $new_spec_name_ids[] = $v['spec_id'];
  424. } else {
  425. //添加规格名
  426. $goodsSpec = GoodsSpec::create(['goods_id' => $post['goods_id'], 'name' => $v['name']]);
  427. $new_spec_name_ids[] = $goodsSpec->id;
  428. }
  429. }
  430. //删除规格项
  431. $all_spec_ids = GoodsSpec::where('goods_id', $post['goods_id'])->column('id');
  432. $del_spec_name_ids = array_diff($all_spec_ids, $new_spec_name_ids);
  433. if (!empty($del_spec_name_ids)) {
  434. GoodsSpec::where('goods_id', $post['goods_id'])
  435. ->where('id', 'in', $del_spec_name_ids)
  436. ->delete();
  437. }
  438. $new_spec_value_ids = [];
  439. $goods_spec_name_key_id = Db::name('goods_spec')
  440. ->where(['goods_id' => $post['goods_id']])
  441. ->where('name', 'in', $post['spec_name'])
  442. ->column('id', 'name');
  443. foreach ($post['spec_values'] as $k => $v) {
  444. $value_id_row = explode(',', $post['spec_value_ids'][$k]);
  445. $value_row = explode(',', $v);
  446. foreach ($value_row as $k2 => $v2) {
  447. $temp = [
  448. 'goods_id' => $post['goods_id'],
  449. 'spec_id' => $goods_spec_name_key_id[$post['spec_name'][$k]],
  450. 'value' => $v2,
  451. ];
  452. if ($value_id_row[$k2]) {
  453. //更新规格值
  454. Db::name('goods_spec_value')
  455. ->where(['id' => $value_id_row[$k2]])
  456. ->update($temp);
  457. $new_spec_value_ids[] = $value_id_row[$k2];
  458. } else {
  459. //添加规格值
  460. $new_spec_value_ids[] = Db::name('goods_spec_value')
  461. ->insertGetId($temp);
  462. }
  463. }
  464. }
  465. $all_spec_value_ids = Db::name('goods_spec_value')
  466. ->where('goods_id', $post['goods_id'])
  467. ->column('id');
  468. $del_spec_value_ids = array_diff($all_spec_value_ids, $new_spec_value_ids);
  469. if (!empty($del_spec_value_ids)) {
  470. //删除规格值
  471. Db::name('goods_spec_value')
  472. ->where('goods_id', $post['goods_id'])
  473. ->where('id', 'in', $del_spec_value_ids)
  474. ->delete();
  475. }
  476. $new_item_id = [];
  477. $goods_spec_name_value_id = Db::name('goods_spec_value')
  478. ->where(['goods_id' => $post['goods_id']])
  479. ->column('id', 'value');
  480. foreach ($spec_lists as $k => $v) {
  481. $spec_lists[$k]['spec_value_ids'] = '';
  482. $temp = explode(',', $v['spec_value_str']);
  483. foreach ($temp as $k2 => $v2) {
  484. $spec_lists[$k]['spec_value_ids'] .= $goods_spec_name_value_id[$v2] . ',';
  485. }
  486. $spec_lists[$k]['spec_value_ids'] = trim($spec_lists[$k]['spec_value_ids'], ',');
  487. if(isset($spec_lists[$k]['spec_image'])) {
  488. $spec_lists[$k]['image'] = clearDomain($spec_lists[$k]['spec_image']);
  489. }
  490. unset($spec_lists[$k]['spec_image']);
  491. $spec_lists[$k]['goods_id'] = $post['goods_id'];
  492. unset($spec_lists[$k]['spec_id']);
  493. $item_id = $spec_lists[$k]['item_id'];
  494. unset($spec_lists[$k]['item_id']);
  495. if ($item_id) {
  496. Db::name('goods_item')
  497. ->where(['id' => $item_id])
  498. ->update($spec_lists[$k]);
  499. $new_item_id[] = $item_id;
  500. } else {
  501. $new_item_id[] = Db::name('goods_item')
  502. ->insertGetId($spec_lists[$k]);
  503. }
  504. }
  505. $all_item_id = Db::name('goods_item')
  506. ->where('goods_id', $post['goods_id'])
  507. ->column('id');
  508. $del_item_ids = array_diff($all_item_id, $new_item_id);
  509. if (!empty($del_item_ids)) {
  510. //删除规格值
  511. Db::name('goods_item')
  512. ->where('goods_id', $post['goods_id'])
  513. ->where('id', 'in', $del_item_ids)
  514. ->delete();
  515. }
  516. }
  517. $newItemIds = GoodsItem::where('goods_id', $post['id'])->column('id');
  518. $destroyIds = DistributionGoods::where('goods_id', $post['goods_id'])->column('id');
  519. // 删除原来的分销设置
  520. if ($oldItemIds != $newItemIds && $destroyIds) {
  521. DistributionGoods::destroy($destroyIds);
  522. self::$error = '商品信息修改成功,该商品属于分销商品,请重新设置分销信息';
  523. }
  524. Db::commit();
  525. return true;
  526. } catch (\Exception $e) {
  527. Db::rollback();
  528. self::$error = $e->getMessage();
  529. return false;
  530. }
  531. }
  532. /**
  533. * 放回仓库
  534. */
  535. public static function backToWarehouse($id)
  536. {
  537. $updateData = [
  538. 'id' => $id,
  539. 'status' => GoodsEnum::STATUS_SOLD_OUT,
  540. 'del' => GoodsEnum::DEL_NORMAL
  541. ];
  542. return Goods::update($updateData);
  543. }
  544. /**
  545. * @notes 批量更新商品状态
  546. * @param $ids
  547. * @param $status
  548. * @return Goods|false
  549. * @author 段誉
  550. * @date 2022/3/17 11:51
  551. */
  552. public static function setStatus($ids, $status)
  553. {
  554. try {
  555. $result = Goods::whereIn('id', $ids)->update([
  556. 'status' => $status,
  557. 'update_time' => time()
  558. ]);
  559. if (!$status) {
  560. // 下架商品,更新商品收藏
  561. event('UpdateCollect', ['goods_id' => $ids]);
  562. }
  563. return $result;
  564. } catch (\Exception $e) {
  565. self::$error = $e->getMessage();
  566. return false;
  567. }
  568. }
  569. /**
  570. * @notes 添加基础商品信息
  571. * @param $shop_id
  572. * @param $post
  573. * @return mixed
  574. * @throws \Exception
  575. * @author 段誉
  576. * @date 2022/4/7 11:55
  577. */
  578. public static function addGoods($shop_id, $post)
  579. {
  580. //算出最大最小价格
  581. if ($post['spec_type'] == 1) {
  582. $max_price = $post['one_price'];
  583. $min_price = $post['one_price'];
  584. $market_price = $post['one_market_price'];
  585. $total_stock = $post['one_stock'];
  586. } else { // 多规格
  587. $max_price = max($post['price']);
  588. $min_price = min($post['price']);
  589. $min_price_key = array_search($min_price,$post['price']);
  590. $market_price = $post['market_price'][$min_price_key];
  591. $total_stock = array_sum($post['stock']);
  592. }
  593. // 总库存为0的商品不允许上架
  594. if($post['status'] == GoodsEnum::STATUS_SHELVES && $total_stock == 0) {
  595. throw new \Exception('库存为0不允许上架');
  596. }
  597. // 判断商品是否需要审核
  598. $is_product_audit = Shop::where('id', $shop_id)->value('is_product_audit'); // 0-无需审核 1-需审核
  599. $audit_status = $is_product_audit ? 0 : 1;
  600. // 处理商品数据
  601. $post = self::formatGoodsData($post);
  602. //写入主表
  603. $data = [
  604. 'type' => $post['type'],
  605. 'name' => trim($post['name']),
  606. 'code' => trim($post['code']) ? trim($post['code']) : create_goods_code($shop_id),
  607. 'shop_id' => $shop_id,
  608. 'shop_cate_id' => $post['shop_cate_id'],
  609. 'first_cate_id' => $post['first_cate_id'],
  610. 'second_cate_id' => $post['second_cate_id'],
  611. 'third_cate_id' => $post['third_cate_id'],
  612. 'unit_id' => $post['unit_id'],
  613. 'brand_id' => $post['brand_id'],
  614. 'supplier_id' => $post['supplier_id'],
  615. 'status' => $post['status'],
  616. 'image' => $post['image'],
  617. 'video' => $post['video'] ?? '',
  618. 'remark' => $post['remark'],
  619. 'content' => $post['content'],
  620. 'sort' => $post['sort'],
  621. 'spec_type' => $post['spec_type'],
  622. 'max_price' => $max_price,
  623. 'min_price' => $min_price,
  624. 'market_price' => $market_price,
  625. 'stock' => $total_stock, // 总库存
  626. 'express_type' => $post['express_type'] ?? 0,
  627. 'express_money' => $post['express_money'],
  628. 'express_template_id' => $post['express_template_id'],
  629. 'is_recommend' => $post['is_recommend'],
  630. 'create_time' => time(),
  631. 'update_time' => time(),
  632. 'stock_warn' => $post['stock_warn'],
  633. 'poster' => isset($post['poster']) ? UrlServer::setFileUrl($post['poster']) : '',
  634. 'is_show_stock' => $post['is_show_stock'],
  635. 'is_member' => $post['is_member'],
  636. 'audit_status' => $audit_status,
  637. 'delivery_type' => implode(',', $post['delivery_type']),
  638. 'after_pay' => $post['after_pay'] ?? 0,
  639. 'after_delivery' => $post['after_delivery'] ?? 0,
  640. 'delivery_content' => $post['delivery_content'] ?? '',
  641. ];
  642. $goods = Goods::create($data);
  643. return $goods->id;
  644. }
  645. /**
  646. * @notes 添加商品图片
  647. * @param $goods_id
  648. * @param $post
  649. * @throws \Exception
  650. * @author 段誉
  651. * @date 2022/4/7 11:56
  652. */
  653. public static function addGoodsImage($goods_id, $post)
  654. {
  655. $data = [];
  656. foreach ($post['goods_image'] as $k => $v) {
  657. $data[] = [
  658. 'goods_id' => $goods_id,
  659. 'uri' => $v,
  660. ];
  661. }
  662. (new GoodsImage())->saveAll($data);
  663. }
  664. /**
  665. * @notes 添加单个规格
  666. * @param $goods_id
  667. * @param $post
  668. * @author 段誉
  669. * @date 2022/4/7 11:56
  670. */
  671. public static function addOneSpec($goods_id, $post)
  672. {
  673. //添加商品规格
  674. $goods_spec_id = (new GoodsSpec())->insertGetId([
  675. 'goods_id' => $goods_id,
  676. 'name' => '默认'
  677. ]);
  678. //添加商品规格值
  679. $goods_spec_value_id = (new GoodsSpecValue())->insertGetId([
  680. 'spec_id' => $goods_spec_id,
  681. 'goods_id' => $goods_id,
  682. 'value' => '默认'
  683. ]);
  684. if(isset($post['one_spec_image'])) {
  685. $post['one_spec_image'] = str_replace(request()->domain(), '', $post['one_spec_image']);
  686. }
  687. //商品sku
  688. GoodsItem::create([
  689. 'image' => $post['one_spec_image'] ?? $post['image'],
  690. 'goods_id' => $goods_id,
  691. 'spec_value_ids' => $goods_spec_value_id,
  692. 'spec_value_str' => '默认',
  693. 'market_price' => $post['one_market_price'],
  694. 'price' => $post['one_price'],
  695. 'stock' => $post['one_stock'],
  696. 'volume' => $post['one_volume'],
  697. 'weight' => $post['one_weight'],
  698. 'bar_code' => $post['one_bar_code'],
  699. 'chengben_price' => $post['one_chengben_price'],
  700. ]);
  701. }
  702. /**
  703. * @notes 添加多个规格
  704. * @param $goods_id
  705. * @param $post
  706. * @param $spec_lists
  707. * @author 段誉
  708. * @date 2022/4/7 11:56
  709. */
  710. public static function addMoreSpec($goods_id, $post, $spec_lists)
  711. {
  712. // 添加规格项
  713. $goods_specs = [];
  714. foreach ($post['spec_name'] as $k => $v) {
  715. $temp = ['goods_id' => $goods_id, 'name' => $v];
  716. $goods_specs[] = $temp;
  717. }
  718. (new GoodsSpec())->insertAll($goods_specs);
  719. // 规格项id及名称 例:['颜色'=>1, '尺码'=>2]
  720. $goods_spec_name_key_id = GoodsSpec::where(['goods_id' => $goods_id])
  721. ->where('name', 'in', $post['spec_name'])
  722. ->column('id', 'name');
  723. // 添加规格值
  724. $data = [];
  725. foreach ($post['spec_values'] as $k => $v) {
  726. $row = explode(',', $v);
  727. foreach ($row as $k2 => $v2) {
  728. $temp = [
  729. 'goods_id' => $goods_id,
  730. 'spec_id' => $goods_spec_name_key_id[$post['spec_name'][$k]],
  731. 'value' => $v2,
  732. ];
  733. $data[] = $temp;
  734. }
  735. }
  736. (new GoodsSpecValue())->insertAll($data);
  737. // 规格值id及名称 例:['红色'=>1,'蓝色'=>2,'S码'=>3,'M码'=>4]
  738. $goods_spec_name_value_id = GoodsSpecValue::where(['goods_id' => $goods_id])->column('id', 'value');
  739. // 添加SKU
  740. foreach ($spec_lists as $k => $v) {
  741. $spec_lists[$k]['spec_value_ids'] = '';
  742. $temp = explode(',', $v['spec_value_str']); // 例:"红色,S码" => ["红色", "S码"]
  743. // 组装SKU的spec_value_ids 例:"红色,S码" => ["红色", "S码"] => "1,3"
  744. foreach ($temp as $k2 => $v2) {
  745. $spec_lists[$k]['spec_value_ids'] .= $goods_spec_name_value_id[$v2] . ',';
  746. }
  747. $spec_lists[$k]['spec_value_ids'] = trim($spec_lists[$k]['spec_value_ids'], ',');
  748. if(isset($spec_lists[$k]['spec_image'])) {
  749. $spec_lists[$k]['spec_image'] = str_replace(request()->domain(), '', $spec_lists[$k]['spec_image']);
  750. }
  751. $spec_lists[$k]['image'] = $spec_lists[$k]['spec_image'] ?? $post['image'];
  752. $spec_lists[$k]['goods_id'] = $goods_id;
  753. if(isset($spec_lists[$k]['spec_image'])) {
  754. unset($spec_lists[$k]['spec_image']);
  755. }
  756. unset($spec_lists[$k]['spec_id']);
  757. unset($spec_lists[$k]['item_id']);
  758. }
  759. (new GoodsItem())->insertAll($spec_lists);
  760. }
  761. /**
  762. * @notes 格式化商品数据
  763. * @param array $data
  764. * @return array
  765. * @author 段誉
  766. * @date 2022/4/7 10:12
  767. */
  768. public static function formatGoodsData(array $data) : array
  769. {
  770. // 虚拟商品类型数据
  771. if ($data['type'] == GoodsEnum::TYPE_VIRTUAL) {
  772. $data['after_pay'] = !empty($data['after_pay']) ? $data['after_pay'] : 0;
  773. $data['after_delivery'] = !empty($data['after_delivery']) ? $data['after_delivery'] : 0;
  774. $data['delivery_content'] = !empty($data['delivery_content']) ? $data['delivery_content'] : '';
  775. }
  776. // 替换内容中图片地址
  777. $domain = UrlServer::getFileUrl('/');
  778. $data['content'] = str_replace($domain, '/', $data['content']);
  779. // 运费处理
  780. $data['express_money'] = $data['express_type'] == GoodsEnum::EXPRESS_TYPE_UNIFIED ? $data['express_money'] : 0;
  781. $data['express_template_id'] = $data['express_type'] == GoodsEnum::EXPRESS_TYPE_TEMPLATE ? $data['express_template_id'] : 0;
  782. return $data;
  783. }
  784. }