Goods.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. <?php
  2. /**
  3. *
  4. * @copyright ©2020 点小铺
  5. * @author hanj
  6. * @link: https://dyuit.com
  7. * Created by VSCode
  8. */
  9. namespace app\common\model\sharing;
  10. use app\common\model\BaseModel;
  11. use app\common\library\helper;
  12. /**
  13. * 拼团商品模型
  14. * Class Goods
  15. * @package app\common\model\sharing
  16. */
  17. class Goods extends BaseModel
  18. {
  19. protected $name = 'sharing_goods';
  20. protected $append = ['goods_sales'];
  21. /**
  22. * 计算显示销量 (初始销量 + 实际销量)
  23. * @param $value
  24. * @param $data
  25. * @return mixed
  26. */
  27. public function getGoodsSalesAttr($value, $data)
  28. {
  29. return $data['sales_initial'] + $data['sales_actual'];
  30. }
  31. /**
  32. * 获取器:单独设置折扣的配置
  33. * @param $json
  34. * @return mixed
  35. */
  36. public function getAloneGradeEquityAttr($json)
  37. {
  38. return json_decode($json, true);
  39. }
  40. /**
  41. * 修改器:单独设置折扣的配置
  42. * @param $data
  43. * @return mixed
  44. */
  45. public function setAloneGradeEquityAttr($data)
  46. {
  47. return json_encode($data);
  48. }
  49. /**
  50. * 关联商品分类表
  51. * @return \think\model\relation\BelongsTo
  52. */
  53. public function category()
  54. {
  55. return $this->belongsTo('Category');
  56. }
  57. /**
  58. * 关联商品规格表
  59. * @return \think\model\relation\HasMany
  60. */
  61. public function sku()
  62. {
  63. return $this->hasMany('GoodsSku', 'goods_id')->order(['goods_sku_id' => 'asc']);
  64. }
  65. /**
  66. * 关联商品规格关系表
  67. * @return \think\model\relation\BelongsToMany
  68. */
  69. public function specRel()
  70. {
  71. $module = self::getCalledModule() ?: 'common';
  72. return $this->belongsToMany(
  73. "app\\{$module}\\model\\SpecValue",
  74. 'SharingGoodsSpecRel',
  75. 'spec_value_id',
  76. 'goods_id'
  77. )->order(['id' => 'asc']);
  78. }
  79. /**
  80. * 关联商品图片表
  81. * @return \think\model\relation\HasMany
  82. */
  83. public function image()
  84. {
  85. $module = self::getCalledModule() ?: 'common';
  86. return $this->hasMany("app\\{$module}\\model\\sharing\\GoodsImage", 'goods_id')
  87. ->order(['id' => 'asc']);
  88. }
  89. /**
  90. * 关联运费模板表
  91. * @return \think\model\relation\BelongsTo
  92. */
  93. public function delivery()
  94. {
  95. $module = self::getCalledModule() ?: 'common';
  96. return $this->BelongsTo("app\\{$module}\\model\\Delivery");
  97. }
  98. /**
  99. * 关联订单评价表
  100. * @return \think\model\relation\HasMany
  101. */
  102. public function commentData()
  103. {
  104. return $this->hasMany('Comment', 'goods_id');
  105. }
  106. /**
  107. * 计费方式
  108. * @param $value
  109. * @return mixed
  110. */
  111. public function getGoodsStatusAttr($value)
  112. {
  113. $status = [10 => '上架', 20 => '下架'];
  114. return ['text' => $status[$value], 'value' => $value];
  115. }
  116. /**
  117. * 获取商品列表
  118. * @param $param
  119. * @return mixed
  120. * @throws \think\exception\DbException
  121. */
  122. public function getList($param)
  123. {
  124. // 商品列表获取条件
  125. $params = array_merge([
  126. 'status' => 10, // 商品状态
  127. 'category_id' => 0, // 分类id
  128. 'search' => '', // 搜索关键词
  129. 'sortType' => 'all', // 排序类型
  130. 'sortPrice' => false, // 价格排序 高低
  131. 'listRows' => 15, // 每页数量
  132. ], $param);
  133. // 筛选条件
  134. $filter = [];
  135. $params['category_id'] > 0 && $filter['category_id'] = ['IN', Category::getSubCategoryId($params['category_id'])];
  136. $params['status'] > 0 && $filter['goods_status'] = $params['status'];
  137. !empty($params['search']) && $filter['goods_name'] = ['like', '%' . trim($params['search']) . '%'];
  138. // 排序规则
  139. $sort = [];
  140. if ($params['sortType'] === 'all') {
  141. $sort = ['goods_sort', 'goods_id' => 'desc'];
  142. } elseif ($params['sortType'] === 'sales') {
  143. $sort = ['goods_sales' => 'desc'];
  144. } elseif ($params['sortType'] === 'price') {
  145. $sort = $params['sortPrice'] ? ['goods_max_price' => 'desc'] : ['goods_min_price'];
  146. }
  147. // 商品表名称
  148. $tableName = $this->getTable();
  149. // 多规格商品 最高价与最低价
  150. $GoodsSku = new GoodsSku;
  151. $minPriceSql = $GoodsSku->field(['MIN(sharing_price)'])
  152. ->where('goods_id', 'EXP', "= `$tableName`.`goods_id`")->buildSql();
  153. $maxPriceSql = $GoodsSku->field(['MAX(sharing_price)'])
  154. ->where('goods_id', 'EXP', "= `$tableName`.`goods_id`")->buildSql();
  155. // 执行查询
  156. $list = $this
  157. ->field(['*', '(sales_initial + sales_actual) as goods_sales',
  158. "$minPriceSql AS goods_min_price",
  159. "$maxPriceSql AS goods_max_price"
  160. ])
  161. ->with(['category', 'image.file', 'sku'])
  162. ->where('is_delete', '=', 0)
  163. ->where($filter)
  164. ->order($sort)
  165. ->paginate($params['listRows'], false, [
  166. 'query' => \request()->request()
  167. ]);
  168. // 整理列表数据并返回
  169. return $this->setGoodsListData($list, true);
  170. }
  171. /**
  172. * 设置商品展示的数据
  173. * @param $data
  174. * @param bool $isMultiple
  175. * @param callable $callback
  176. * @return mixed
  177. */
  178. protected function setGoodsListData(&$data, $isMultiple = true, callable $callback = null)
  179. {
  180. if (!$isMultiple) $dataSource = [&$data]; else $dataSource = &$data;
  181. // 整理商品列表数据
  182. foreach ($dataSource as &$goods) {
  183. // 商品默认规格
  184. $goodsSku = $goods['sku'][0];
  185. // 商品默认数据
  186. $goods['goods_image'] = $goods['image'][0]['file_path'];
  187. $goods['goods_sku'] = $goodsSku;
  188. // 回调函数
  189. is_callable($callback) && call_user_func($callback, $goods);
  190. }
  191. return $data;
  192. }
  193. /**
  194. * 根据商品id集获取商品列表
  195. * @param array $goodsIds
  196. * @param null $status
  197. * @return false|\PDOStatement|string|\think\Collection
  198. * @throws \think\db\exception\DataNotFoundException
  199. * @throws \think\db\exception\ModelNotFoundException
  200. * @throws \think\exception\DbException
  201. */
  202. public function getListByIds($goodsIds, $status = null)
  203. {
  204. // 筛选条件
  205. $filter = ['goods_id' => ['in', $goodsIds]];
  206. $status > 0 && $filter['goods_status'] = $status;
  207. if (!empty($goodsIds)) {
  208. $this->orderRaw('field(goods_id, ' . implode(',', $goodsIds) . ')');
  209. }
  210. // 获取商品列表数据
  211. $data = $this->with(['category', 'image.file', 'sku', 'spec_rel.spec', 'delivery.rule'])
  212. ->where($filter)
  213. ->select();
  214. if ($data->isEmpty()) return $data;
  215. // 格式化数据
  216. foreach ($data as &$item) {
  217. $item['goods_image'] = $item['image'][0]['file_path'];
  218. }
  219. return $data;
  220. }
  221. /**
  222. * 商品多规格信息
  223. * @param \think\Collection $spec_rel
  224. * @param \think\Collection $skuData
  225. * @return array
  226. */
  227. public function getManySpecData($spec_rel, $skuData)
  228. {
  229. // spec_attr
  230. $specAttrData = [];
  231. foreach ($spec_rel->toArray() as $item) {
  232. if (!isset($specAttrData[$item['spec_id']])) {
  233. $specAttrData[$item['spec_id']] = [
  234. 'group_id' => $item['spec']['spec_id'],
  235. 'group_name' => $item['spec']['spec_name'],
  236. 'spec_items' => [],
  237. ];
  238. }
  239. $specAttrData[$item['spec_id']]['spec_items'][] = [
  240. 'item_id' => $item['spec_value_id'],
  241. 'spec_value' => $item['spec_value'],
  242. ];
  243. }
  244. // spec_list
  245. $specListData = [];
  246. foreach ($skuData->toArray() as $item) {
  247. $image = (isset($item['image']) && !empty($item['image'])) ? $item['image'] : ['file_id' => 0, 'file_path' => ''];
  248. $specListData[] = [
  249. 'goods_sku_id' => $item['goods_sku_id'],
  250. 'spec_sku_id' => $item['spec_sku_id'],
  251. 'rows' => [],
  252. 'form' => [
  253. 'image_id' => $image['file_id'],
  254. 'image_path' => $image['file_path'],
  255. 'goods_no' => $item['goods_no'],
  256. 'goods_price' => $item['goods_price'],
  257. 'sharing_price' => $item['sharing_price'],
  258. 'goods_weight' => $item['goods_weight'],
  259. 'line_price' => $item['line_price'],
  260. 'stock_num' => $item['stock_num'],
  261. ],
  262. ];
  263. }
  264. return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData];
  265. }
  266. /**
  267. * 多规格表格数据
  268. * @param $goods
  269. * @return array
  270. */
  271. public function getManySpecTable(&$goods)
  272. {
  273. $specData = $this->getManySpecData($goods['spec_rel'], $goods['sku']);
  274. $totalRow = count($specData['spec_list']);
  275. foreach ($specData['spec_list'] as $i => &$sku) {
  276. $rowData = [];
  277. $rowCount = 1;
  278. foreach ($specData['spec_attr'] as $attr) {
  279. $skuValues = $attr['spec_items'];
  280. $rowCount *= count($skuValues);
  281. $anInterBankNum = ($totalRow / $rowCount);
  282. $point = (($i / $anInterBankNum) % count($skuValues));
  283. if (0 === ($i % $anInterBankNum)) {
  284. $rowData[] = [
  285. 'rowspan' => $anInterBankNum,
  286. 'item_id' => $skuValues[$point]['item_id'],
  287. 'spec_value' => $skuValues[$point]['spec_value']
  288. ];
  289. }
  290. }
  291. $sku['rows'] = $rowData;
  292. }
  293. return $specData;
  294. }
  295. /**
  296. * 获取商品详情
  297. * @param $goodsId
  298. * @return static
  299. */
  300. public static function detail($goodsId)
  301. {
  302. /* @var $model self */
  303. $model = (new static)->with([
  304. 'category',
  305. 'image.file',
  306. 'sku.image',
  307. 'spec_rel.spec',
  308. ])->where('goods_id', '=', $goodsId)
  309. ->find();
  310. // 整理列表数据并返回
  311. return $model->setGoodsListData($model, false);
  312. }
  313. /**
  314. * 指定的商品规格信息
  315. * @param static $goods 商品详情
  316. * @param int $specSkuId
  317. * @return array|bool
  318. */
  319. public static function getGoodsSku($goods, $specSkuId)
  320. {
  321. // 获取指定的sku
  322. $goodsSku = [];
  323. foreach ($goods['sku'] as $item) {
  324. if ($item['spec_sku_id'] == $specSkuId) {
  325. $goodsSku = $item;
  326. break;
  327. }
  328. }
  329. if (empty($goodsSku)) {
  330. return false;
  331. }
  332. // 多规格文字内容
  333. $goodsSku['goods_attr'] = '';
  334. if ($goods['spec_type'] == 20) {
  335. $specRelData = helper::arrayColumn2Key($goods['spec_rel'], 'spec_value_id');
  336. $attrs = explode('_', $goodsSku['spec_sku_id']);
  337. foreach ($attrs as $specValueId) {
  338. $goodsSku['goods_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':'
  339. . $specRelData[$specValueId]['spec_value'] . '; ';
  340. }
  341. }
  342. return $goodsSku;
  343. }
  344. /**
  345. * 更新商品库存销量
  346. * @param $goodsList
  347. * @throws \Exception
  348. */
  349. public function updateStockSales($goodsList)
  350. {
  351. // 整理批量更新商品销量
  352. $goodsSave = [];
  353. // 批量更新商品规格:sku销量、库存
  354. $goodsSpecSave = [];
  355. foreach ($goodsList as $goods) {
  356. $goodsSave[] = [
  357. 'goods_id' => $goods['goods_id'],
  358. 'sales_actual' => ['inc', $goods['total_num']]
  359. ];
  360. // 付款减库存
  361. if ($goods['deduct_stock_type'] == 20) {
  362. $goodsSpecSave[] = [
  363. 'data' => ['stock_num' => ['dec', $goods['total_num']]],
  364. 'where' => [
  365. 'goods_id' => $goods['goods_id'],
  366. 'spec_sku_id' => $goods['spec_sku_id'],
  367. ],
  368. ];
  369. }
  370. }
  371. // 更新商品总销量
  372. $this->allowField(true)->isUpdate()->saveAll($goodsSave);
  373. // 更新商品规格库存
  374. !empty($goodsSpecSave) && (new GoodsSku)->updateAll($goodsSpecSave);
  375. }
  376. }