Goods.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. <?php
  2. namespace app\admin\model\shopro\goods;
  3. use app\admin\model\shopro\Common;
  4. use traits\model\SoftDelete;
  5. use addons\shopro\library\Tree;
  6. use app\admin\model\shopro\Category;
  7. use app\admin\model\shopro\activity\Activity as ActivityModel;
  8. use app\admin\model\shopro\activity\SkuPrice as ActivitySkuPriceModel;
  9. use app\admin\model\shopro\app\ScoreSkuPrice;
  10. use app\admin\model\shopro\user\GoodsLog;
  11. use app\admin\model\shopro\order\Order;
  12. use app\admin\model\shopro\order\OrderItem;
  13. use addons\shopro\facade\Activity as ActivityFacade;
  14. class Goods extends Common
  15. {
  16. use SoftDelete;
  17. protected $deleteTime = 'deletetime';
  18. // 表名
  19. protected $name = 'shopro_goods';
  20. protected $type = [
  21. 'images' => 'json',
  22. 'image_wh' => 'json',
  23. 'params' => 'json',
  24. ];
  25. // 追加属性
  26. protected $append = [
  27. 'status_text',
  28. 'type_text',
  29. 'dispatch_type_text'
  30. ];
  31. protected $hidden = [
  32. 'content',
  33. 'max_sku_price',
  34. 'score_sku_prices',
  35. 'activity_sku_prices',
  36. 'total_sales' // 商品列表,销量排序会用到
  37. ];
  38. /**
  39. * type 中文
  40. */
  41. public function typeList()
  42. {
  43. return [
  44. 'normal' => '实体商品',
  45. 'virtual' => '虚拟商品',
  46. 'card' => '电子卡密',
  47. ];
  48. }
  49. /**
  50. * status 中文
  51. */
  52. public function statusList()
  53. {
  54. return [
  55. 'up' => '上架中',
  56. 'down' => '已下架',
  57. 'hidden' => '已隐藏',
  58. ];
  59. }
  60. /**
  61. * status 中文
  62. */
  63. public function dispatchTypeList()
  64. {
  65. return [
  66. 'express' => '快递物流',
  67. 'autosend' => '自动发货',
  68. 'custom' => '商家发货'
  69. ];
  70. }
  71. /**
  72. * 修改器 service_ids
  73. *
  74. * @param array|string $value
  75. * @param array $data
  76. * @return string
  77. */
  78. public function setServiceIdsAttr($value, $data)
  79. {
  80. $service_ids = is_array($value) ? join(',', $value) : $value;
  81. return $service_ids;
  82. }
  83. public function setIsOfflineAttr($value, $data)
  84. {
  85. // 除了实体商品,其他都是 0
  86. return $data['type'] == 'normal' ? $value : 0;
  87. }
  88. public function scopeShow($query)
  89. {
  90. return $query->whereIn('status', ['up', 'hidden']);
  91. }
  92. public function getDispatchTypeTextAttr($value, $data)
  93. {
  94. $value = $value ?: ($data['dispatch_type'] ?? null);
  95. $list = $this->dispatchTypeList();
  96. return isset($list[$value]) ? $list[$value] : '';
  97. }
  98. //最低价格,金钱格式,而不是数组格式
  99. public function getPriceminAttr($value, $data)
  100. {
  101. // 前端传入的 session_id
  102. $activity_id = session('goods-activity_id:' . $data['id']);
  103. if ($activity_id && $this->activity) {
  104. // 活动商品的价格
  105. $skuPrices = $data['new_sku_prices'] ?? [];
  106. $prices = $skuPrices instanceof \think\Collection ? $skuPrices->column('min_price') : array_column($skuPrices, 'min_price');
  107. $maxPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column('max_price') : array_column($skuPrices, 'max_price');
  108. $min_price = $prices ? min($prices) : $data['price'];
  109. $max_price = $maxPrices ? max($maxPrices) : $data['price'];
  110. //原版
  111. /*$priceArr[] = $min_price;
  112. if ($min_price < $max_price) {
  113. $priceArr[] = $max_price;
  114. }*/
  115. //新的
  116. $priceArr = $min_price;
  117. } else if (isset($data['show_score_shop'])) {
  118. // 积分商品价格
  119. $skuPrices = $data['new_sku_prices'] ?? [];
  120. $skuPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column(null, 'score') : array_column($skuPrices, null, 'score');
  121. ksort($skuPrices);
  122. $skuPrice = current($skuPrices); // 需要积分最少的规格
  123. if ($skuPrice) {
  124. // 不自动拼接积分
  125. // $price = $skuPrice['score'] . '积分';
  126. // if ($skuPrice['price'] > 0) {
  127. // $price .= '+¥' . $skuPrice['price'];
  128. // }
  129. // $priceArr[] = $price;
  130. $priceArr[] = $skuPrice['price'];
  131. } else {
  132. // 防止没有规格
  133. $priceArr[] = $data['price'];
  134. }
  135. } else {
  136. // 普通商品的价格区间
  137. $price = $value ? $value : ($data['price'] ?? 0);
  138. //原版
  139. /*$priceArr = [$price];
  140. if ($price && isset($data['is_sku']) && $data['is_sku']) {
  141. $max_price = $this->max_sku_price->price;
  142. if ($price < $max_price) {
  143. $priceArr[] = $max_price;
  144. }
  145. }*/
  146. $priceArr = $price;
  147. }
  148. return $priceArr;
  149. }
  150. public function getPriceAttr($value, $data)
  151. {
  152. // 前端传入的 session_id
  153. $activity_id = session('goods-activity_id:' . $data['id']);
  154. if ($activity_id && $this->activity) {
  155. // 活动商品的价格
  156. $skuPrices = $data['new_sku_prices'] ?? [];
  157. $prices = $skuPrices instanceof \think\Collection ? $skuPrices->column('min_price') : array_column($skuPrices, 'min_price');
  158. $maxPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column('max_price') : array_column($skuPrices, 'max_price');
  159. $min_price = $prices ? min($prices) : $data['price'];
  160. $max_price = $maxPrices ? max($maxPrices) : $data['price'];
  161. $priceArr[] = $min_price;
  162. if ($min_price < $max_price) {
  163. $priceArr[] = $max_price;
  164. }
  165. } else if (isset($data['show_score_shop'])) {
  166. // 积分商品价格
  167. $skuPrices = $data['new_sku_prices'] ?? [];
  168. $skuPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column(null, 'score') : array_column($skuPrices, null, 'score');
  169. ksort($skuPrices);
  170. $skuPrice = current($skuPrices); // 需要积分最少的规格
  171. if ($skuPrice) {
  172. // 不自动拼接积分
  173. // $price = $skuPrice['score'] . '积分';
  174. // if ($skuPrice['price'] > 0) {
  175. // $price .= '+¥' . $skuPrice['price'];
  176. // }
  177. // $priceArr[] = $price;
  178. $priceArr[] = $skuPrice['price'];
  179. } else {
  180. // 防止没有规格
  181. $priceArr[] = $data['price'];
  182. }
  183. } else {
  184. // 普通商品的价格区间
  185. $price = $value ? $value : ($data['price'] ?? 0);
  186. $priceArr = [$price];
  187. if ($price && isset($data['is_sku']) && $data['is_sku']) {
  188. $max_price = $this->max_sku_price->price;
  189. if ($price < $max_price) {
  190. $priceArr[] = $max_price;
  191. }
  192. }
  193. }
  194. return $priceArr;
  195. }
  196. /**
  197. * 这个目前只有拼团单独购买要使用
  198. *
  199. * @return void
  200. */
  201. public function getOriginalGoodsPriceAttr($value, $data)
  202. {
  203. $activity_id = session('goods-activity_id:' . $data['id']);
  204. $priceArr = [];
  205. if ($activity_id && $this->activity) {
  206. // 活动商品的价格
  207. $skuPrices = $data['new_sku_prices'] ?? [];
  208. $prices = $skuPrices instanceof \think\Collection ? $skuPrices->column('old_price') : array_column($skuPrices, 'old_price');
  209. $min_price = $prices ? min($prices) : $data['price'];
  210. $max_price = $prices ? max($prices) : $data['price'];
  211. $priceArr[] = $min_price;
  212. if ($min_price < $max_price) {
  213. $priceArr[] = $max_price;
  214. }
  215. }
  216. return $priceArr;
  217. }
  218. /**
  219. * 前端积分商城列表,获取默认所需积分(价格不自动拼接积分了,所以这里单独设置一个属性)
  220. */
  221. public function getScoreAttr($value, $data)
  222. {
  223. // 积分商品价格
  224. $skuPrices = $data['new_sku_prices'] ?? [];
  225. $skuPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column(null, 'score') : array_column($skuPrices, null, 'score');
  226. ksort($skuPrices);
  227. $skuPrice = current($skuPrices); // 需要积分最少的规格
  228. if ($skuPrice) {
  229. $scoreAmount = $skuPrice['score'] ?? 0;
  230. } else {
  231. // 防止没有规格
  232. $scoreAmount = 0;
  233. }
  234. return $scoreAmount;
  235. }
  236. public function getSalesAttr($value, $data)
  237. {
  238. // 前端传入的 session_id
  239. $activity_id = session('goods-activity_id:' . $data['id']);
  240. $sales = $data['sales'] ?? 0;
  241. $sales += ($data['show_sales'] ?? 0);
  242. if ($activity_id && $this->activity) {
  243. if ($this->activity['rules'] && isset($this->activity['rules']['sales_show_type']) && $this->activity['rules']['sales_show_type'] == 'real') {
  244. // 活动设置显示真实销量
  245. $skuPrices = $data['new_sku_prices'] ?? [];
  246. $sales = array_sum($skuPrices instanceof \think\Collection ? $skuPrices->column('sales') : array_column($skuPrices, 'sales'));
  247. }
  248. } else if (isset($data['show_score_shop'])) {
  249. // 积分商城显示真实销量
  250. $skuPrices = $data['new_sku_prices'] ?? [];
  251. $sales = array_sum($skuPrices instanceof \think\Collection ? $skuPrices->column('sales') : array_column($skuPrices, 'sales'));
  252. }
  253. return $sales;
  254. }
  255. /**
  256. * 真实销量
  257. */
  258. public function getRealSalesAttr($value, $data)
  259. {
  260. $sales = $data['sales'] ?? 0;
  261. return $sales;
  262. }
  263. /**
  264. * 相关商品(包含活动)购买用户,只查三个
  265. *
  266. * @param [type] $value
  267. * @param [type] $data
  268. * @return void
  269. */
  270. public function getBuyersAttr($value, $data)
  271. {
  272. // 查询活动正在购买的人Goods
  273. $activity_id = session('goods-activity_id:' . $data['id']);
  274. $orderItems = OrderItem::with(['user' => function ($query) {
  275. return $query->field('id,nickname,avatar');
  276. }])->whereExists(function ($query) {
  277. $order_name = (new Order())->getQuery()->getTable();
  278. $order_item_name = (new OrderItem())->getQuery()->getTable();
  279. $query->table($order_name)->where($order_item_name . '.order_id=' . $order_name . '.id')->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED, Order::STATUS_PENDING]);
  280. });
  281. if ($activity_id) {
  282. $orderItems = $orderItems->where('activity_id', $activity_id);
  283. }
  284. $orderItems = $orderItems->fieldRaw('max(id),user_id')->where('goods_id', $data['id'])->group('user_id')->limit(3)->select();
  285. $user = [];
  286. foreach ($orderItems as $item) {
  287. if ($item['user']) {
  288. $user[] = $item['user'];
  289. }
  290. }
  291. return $user;
  292. }
  293. public function getIsScoreShopAttr($value, $data)
  294. {
  295. $scoreGoodsIds = ScoreSkuPrice::group('goods_id')->cache(20)->column('goods_id');
  296. return in_array($data['id'], $scoreGoodsIds) ? 1 : 0;
  297. }
  298. /**
  299. * 获取当前商品所属分类的所有上级
  300. *
  301. * @param string $value
  302. * @param array $data
  303. * @return array
  304. */
  305. public function getCategoryIdsArrAttr($value, $data)
  306. {
  307. $categoryIds = $data['category_ids'] ? explode(',', $data['category_ids']) : [];
  308. $categoryIdsArr = [];
  309. $category = new Category();
  310. foreach ($categoryIds as $key => $category_id) {
  311. $currentCategoryIds = (new Tree($category))->getParentFields($category_id);
  312. if ($currentCategoryIds) {
  313. $categoryIdsArr[] = $currentCategoryIds;
  314. }
  315. }
  316. return $categoryIdsArr;
  317. }
  318. /**
  319. * 获取服务ids数组
  320. *
  321. * @param [type] $value
  322. * @param [type] $data
  323. * @return void
  324. */
  325. public function getServiceIdsAttr($value, $data)
  326. {
  327. $serviceIds = $this->attrFormatComma($value, $data, 'service_ids', true);
  328. return $serviceIds ? array_values(array_filter(array_map("intval", $serviceIds))) : $serviceIds;
  329. }
  330. /**
  331. * 获取所有服务列表
  332. *
  333. * @param string $value
  334. * @param array $data
  335. * @return array
  336. */
  337. public function getServiceAttr($value, $data)
  338. {
  339. $serviceIds = $this->attrFormatComma($value, $data, 'service_ids');
  340. $serviceData = [];
  341. if ($serviceIds) {
  342. $serviceData = Service::whereIn('id', $serviceIds)->select();
  343. }
  344. return $serviceData;
  345. }
  346. /**
  347. * 获取规格列表
  348. *
  349. * @param string $value
  350. * @param array $data
  351. * @return array
  352. */
  353. public function getSkusAttr($value, $data)
  354. {
  355. $sku = Sku::with('children')->where('goods_id', $data['id'])->where('parent_id', 0)->select();
  356. return $sku;
  357. }
  358. /**
  359. * 获取规格项列表
  360. *
  361. * @param string $value
  362. * @param array $data
  363. * @return array
  364. */
  365. public function getSkuPricesAttr($value, $data)
  366. {
  367. $skuPrices = collection(SkuPrice::where('goods_id', $data['id'])->select());
  368. return $skuPrices;
  369. }
  370. /**
  371. * 获取器获取所有活动
  372. *
  373. * @param string $value
  374. * @param array $data
  375. * @return array
  376. */
  377. public function getActivitiesAttr($value, $data)
  378. {
  379. $activities = ActivityFacade::getGoodsActivitys($data['id']);
  380. return $activities;
  381. }
  382. /**
  383. * 获取器获取指定活动
  384. *
  385. * @param string $value
  386. * @param array $data
  387. * @return array
  388. */
  389. public function getActivityAttr($value, $data)
  390. {
  391. $activity_id = session('goods-activity_id:' . $data['id']);//获取商品活动id的session
  392. $activities = ActivityFacade::getGoodsActivityByActivity($data['id'], $activity_id); //new \addons\shopro\library\activity\ActivityRedis; getGoodsActivityByActivity
  393. return $activities;
  394. }
  395. public function getPromosAttr($value, $data)
  396. {
  397. $promos = ActivityFacade::getGoodsPromos($data['id']);
  398. foreach ($promos as $key => $promo) {
  399. $rules = $promo['rules'];
  400. $rules['simple'] = true;
  401. $tags = ActivityFacade::formatRuleTags($rules, $promo['type']);
  402. $promo['tag'] = $tags[0] ?? '';
  403. $promo['tags'] = $tags;
  404. $texts = ActivityFacade::formatRuleTexts($rules, $promo['type']);
  405. $promo['texts'] = $texts;
  406. $promos[$key] = $promo;
  407. }
  408. return $promos ?? [];
  409. }
  410. /**
  411. * 积分商城价格,积分商城的属性
  412. *
  413. * @param string $value
  414. * @param array $data
  415. * @return string
  416. */
  417. public function getScorePriceAttr($value, $data)
  418. {
  419. $scoreSkuPrices = collection($this->score_sku_prices)->column(null, 'score');
  420. ksort($scoreSkuPrices);
  421. $scoreSkuPrice = current($scoreSkuPrices); // 需要积分最少的规格
  422. // print_r($scoreSkuPrices);exit;
  423. if ($scoreSkuPrice) {
  424. $price['score'] = $scoreSkuPrice['score'] ?? 0;
  425. $price['price'] = $scoreSkuPrice['price'] ?? 0;
  426. return $price;
  427. } else {
  428. return null;
  429. }
  430. // return $score . '积分' . ($price > 0 ? '+¥' . $price : '');
  431. }
  432. /**
  433. * 积分商城销量
  434. *
  435. * @param string $value
  436. * @param array $data
  437. * @return string
  438. */
  439. public function getScoreSalesAttr($value, $data)
  440. {
  441. $scoreSkuPrices = $this->score_sku_prices;
  442. return array_sum(collection($scoreSkuPrices)->column('sales'));
  443. }
  444. /**
  445. * 积分商城库存
  446. *
  447. * @param string $value
  448. * @param array $data
  449. * @return string
  450. */
  451. public function getScoreStockAttr($value, $data)
  452. {
  453. $scoreSkuPrices = $this->score_sku_prices;
  454. return array_sum(collection($scoreSkuPrices)->column('stock'));
  455. }
  456. public function maxSkuPrice()
  457. {
  458. return $this->hasOne(SkuPrice::class, 'goods_id')->order('price', 'desc');
  459. }
  460. public function favorite()
  461. {
  462. $user = auth_user();
  463. $user_id = empty($user) ? 0 : $user->id;
  464. return $this->hasOne(GoodsLog::class, 'goods_id', 'id')->where('user_id', $user_id)->favorite();
  465. }
  466. public function activitySkuPrices()
  467. {
  468. return $this->hasMany(ActivitySkuPriceModel::class, 'goods_id');
  469. }
  470. public function scoreSkuPrices()
  471. {
  472. return $this->hasMany(ScoreSkuPrice::class, 'goods_id')->up();
  473. }
  474. /**
  475. * 包含下架的积分规格
  476. */
  477. public function allScoreSkuPrices()
  478. {
  479. return $this->hasMany(ScoreSkuPrice::class, 'goods_id');
  480. }
  481. public function delScoreSkuPrices()
  482. {
  483. return $this->hasMany(ScoreSkuPrice::class, 'goods_id')->removeOption('soft_delete')->whereNotNull('deletetime'); // 只查被删除的记录,这里使用 onlyTrashed 报错
  484. }
  485. // -- commission code start --
  486. public function commissionGoods()
  487. {
  488. return $this->hasOne(\app\admin\model\shopro\commission\CommissionGoods::class, 'goods_id');
  489. }
  490. // -- commission code end --
  491. }