Goods.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\common\model;
  13. use cores\BaseModel;
  14. use think\Paginator;
  15. use think\model\Collection;
  16. use app\common\library\helper;
  17. use app\store\model\GoodsCategoryRel as GoodsCategoryRelModel;
  18. use app\common\enum\goods\Status as GoodsStatusEnum;
  19. /**
  20. * 商品模型
  21. * Class Goods
  22. * @package app\common\model
  23. */
  24. class Goods extends BaseModel
  25. {
  26. // 定义表名
  27. protected $name = 'goods';
  28. // 定义主键
  29. protected $pk = 'goods_id';
  30. // 追加字段
  31. protected $append = ['goods_sales'];
  32. /**
  33. * 计算显示销量 (初始销量 + 实际销量)
  34. * @param $value
  35. * @param $data
  36. * @return mixed
  37. */
  38. public function getGoodsSalesAttr($value, $data)
  39. {
  40. return $data['sales_initial'] + $data['sales_actual'];
  41. }
  42. /**
  43. * 商品详情:HTML实体转换回普通字符
  44. * @param $value
  45. * @return string
  46. */
  47. public function getContentAttr($value)
  48. {
  49. return htmlspecialchars_decode($value);
  50. }
  51. /**
  52. * 获取器:单独设置折扣的配置
  53. * @param $json
  54. * @return mixed
  55. */
  56. public function getAloneGradeEquityAttr($json)
  57. {
  58. return helper::jsonDecode($json);
  59. }
  60. /**
  61. * 修改器:单独设置折扣的配置
  62. * @param $data
  63. * @return mixed
  64. */
  65. public function setAloneGradeEquityAttr($data)
  66. {
  67. return helper::jsonEncode($data);
  68. }
  69. /**
  70. * 关联商品规格表
  71. * @return \think\model\relation\HasMany
  72. */
  73. public function skuList()
  74. {
  75. return $this->hasMany('GoodsSku')->order(['id' => 'asc']);
  76. }
  77. /**
  78. * 关联商品规格关系表
  79. * @return \think\model\relation\HasMany
  80. */
  81. public function specRel()
  82. {
  83. return $this->hasMany('GoodsSpecRel');
  84. }
  85. /**
  86. * 关联商品图片表
  87. * @return \think\model\relation\HasMany
  88. */
  89. public function images()
  90. {
  91. return $this->hasMany('GoodsImage')->order(['id']);
  92. }
  93. /**
  94. * 关联运费模板表
  95. * @return \think\model\relation\BelongsTo
  96. */
  97. public function delivery()
  98. {
  99. return $this->BelongsTo('Delivery');
  100. }
  101. /**
  102. * 关联订单评价表
  103. * @return \think\model\relation\HasMany
  104. */
  105. public function commentData()
  106. {
  107. return $this->hasMany('Comment');
  108. }
  109. /**
  110. * 获取商品列表
  111. * @param array $param 查询条件
  112. * @param int $listRows 分页数量
  113. * @return mixed
  114. * @throws \think\db\exception\DbException
  115. */
  116. public function getList(array $param = [], int $listRows = 15)
  117. {
  118. // 筛选条件
  119. $query = $this->getQueryFilter($param);
  120. // 设置显示的销量 goods_sales
  121. $query->field(['(sales_initial + sales_actual) as goods_sales']);
  122. // 排序条件
  123. $sort = $this->setQuerySort($param);
  124. // 执行查询
  125. $list = $query->with(['images.file'])
  126. ->alias($this->name)
  127. ->field($this->getAliasFields($this->name, ['content']))
  128. ->where('is_delete', '=', 0)
  129. ->order($sort)
  130. ->paginate($listRows);
  131. // 整理列表数据并返回
  132. return $this->setGoodsListData($list);
  133. }
  134. /**
  135. * 检索排序条件
  136. * @param array $param
  137. * @return array|string[]
  138. */
  139. private function setQuerySort(array $param = [])
  140. {
  141. $params = $this->setQueryDefaultValue($param, [
  142. 'sortType' => 'all', // 排序类型
  143. 'sortPrice' => false, // 价格排序 (true高到低 false低到高)
  144. ]);
  145. // 排序规则
  146. $sort = [];
  147. if ($params['sortType'] === 'all') {
  148. $sort = ['sort' => 'asc'];
  149. } elseif ($params['sortType'] === 'sales') {
  150. $sort = ['goods_sales' => 'desc'];
  151. } elseif ($params['sortType'] === 'price') {
  152. $sort = $params['sortPrice'] ? ['goods_price_max' => 'desc'] : ['goods_price_min' => 'asc'];
  153. }
  154. return array_merge($sort, [$this->getPk() => 'desc']);
  155. }
  156. /**
  157. * 检索查询条件
  158. * @param array $param
  159. * @return \think\db\BaseQuery
  160. */
  161. private function getQueryFilter(array $param)
  162. {
  163. // 商品列表获取条件
  164. $params = $this->setQueryDefaultValue($param, [
  165. 'listType' => 'all', // 列表模式 (全部:all 出售中:on_sale 已下架:off_sale 已售罄:sold_out)
  166. 'categoryId' => null, // 分类ID集
  167. 'goodsName' => null, // 商品名称
  168. 'goodsNo' => null, // 商品编码
  169. 'status' => 0, // 商品状态(0全部 10上架 20下架)
  170. ]);
  171. // 实例化新查询对象
  172. $query = $this->getNewQuery();
  173. // 筛选条件
  174. $filter = [];
  175. // 列表模式
  176. if ($params['listType'] === 'on_sale') {
  177. $filter[] = ['status', '=', GoodsStatusEnum::ON_SALE]; // 出售中
  178. } elseif ($params['listType'] === 'off_sale') {
  179. $filter[] = ['status', '=', GoodsStatusEnum::OFF_SALE]; // 已下架
  180. } elseif ($params['listType'] === 'sold_out') {
  181. $filter[] = ['stock_total', '=', 0]; // 已售罄
  182. }
  183. // 商品状态
  184. $params['status'] > 0 && $filter[] = ['status', '=', (int)$params['status']];
  185. // 商品分类
  186. if ($params['categoryId'] > 0) {
  187. // 关联商品与分类关系记录表
  188. $GoodsCategoryRelName = (new GoodsCategoryRelModel)->getName();
  189. $query->join($GoodsCategoryRelName, "{$GoodsCategoryRelName}.goods_id = {$this->name}.goods_id");
  190. // 设置分类ID条件
  191. $query->where('goods_category_rel.category_id', '=', (int)$params['categoryId']);
  192. }
  193. // 商品名称
  194. !empty($params['goodsName']) && $filter[] = ['goods_name', 'like', "%{$params['goodsName']}%"];
  195. // 商品编码
  196. !empty($params['goodsNo']) && $filter[] = ['goods_no', 'like', "%{$params['goodsNo']}%"];
  197. // 实例化新查询对象
  198. return $query->where($filter);
  199. }
  200. /**
  201. * 设置商品展示的数据
  202. * @param Collection|Paginator $list 商品列表
  203. * @param callable|null $callback 回调函数
  204. * @return mixed
  205. */
  206. protected function setGoodsListData($list, callable $callback = null)
  207. {
  208. if ($list->isEmpty()) return $list;
  209. // 遍历商品列表整理数据
  210. foreach ($list as &$goods) {
  211. $goods = $this->setGoodsData($goods, $callback);
  212. }
  213. return $list;
  214. }
  215. /**
  216. * 整理商品数据
  217. * @param Collection|static $goodsInfo
  218. * @param callable|null $callback
  219. * @return mixed
  220. */
  221. protected function setGoodsData($goodsInfo, callable $callback = null)
  222. {
  223. // 商品图片列表
  224. $goodsInfo['goods_images'] = helper::getArrayColumn($goodsInfo['images'], 'file');
  225. // 商品主图
  226. $goodsInfo['goods_image'] = current($goodsInfo['goods_images'])['preview_url'];
  227. // 商品销量(实际显示=初始虚拟销量+实际销量)
  228. $goodsInfo['goods_sales'] = $goodsInfo['sales_initial'] + $goodsInfo['sales_actual'];
  229. // 回调函数
  230. is_callable($callback) && call_user_func($callback, $goodsInfo);
  231. return $goodsInfo->hidden(array_merge($this->hidden, ['specRel', 'images']));
  232. }
  233. /**
  234. * 根据商品id集获取商品列表
  235. * @param array $goodsIds
  236. * @param null $status
  237. * @return array|mixed
  238. */
  239. public function getListByIds(array $goodsIds, $status = null)
  240. {
  241. // 筛选条件
  242. $filter = [['goods_id', 'in', $goodsIds]];
  243. // 商品状态
  244. $status > 0 && $filter[] = ['status', '=', $status];
  245. // 获取商品列表数据
  246. $data = $this
  247. // ->withoutField(['content'])
  248. ->with(['images.file'])
  249. ->where($filter)
  250. ->where('is_delete', '=', 0)
  251. ->orderRaw('field(goods_id, ' . implode(',', $goodsIds) . ')')
  252. ->select();
  253. // 整理列表数据并返回
  254. return $this->setGoodsListData($data);
  255. }
  256. /**
  257. * 商品多规格信息
  258. * @param \think\Collection $specRel
  259. * @param \think\Collection $skuData
  260. * @return array
  261. */
  262. public function getManySpecData($specRel, $skuData)
  263. {
  264. // spec_attr
  265. $specAttrData = [];
  266. foreach ($specRel as $item) {
  267. if (!isset($specAttrData[$item['spec_id']])) {
  268. $specAttrData[$item['spec_id']] = [
  269. 'group_id' => $item['spec']['spec_id'],
  270. 'name' => $item['spec']['spec_name'],
  271. 'spec_items' => [],
  272. ];
  273. }
  274. $specAttrData[$item['spec_id']]['spec_items'][] = [
  275. 'item_id' => $item['spec_value_id'],
  276. 'spec_value' => $item['spec_value'],
  277. ];
  278. }
  279. // spec_list
  280. $specListData = [];
  281. foreach ($skuData as $item) {
  282. $image = (isset($item['images']) && !empty($item['images'])) ? $item['images'] : ['file_id' => 0, 'external_url' => ''];
  283. $specListData[] = [
  284. 'id' => $item['id'],
  285. 'goods_sku_id' => $item['goods_sku_id'],
  286. 'rows' => [],
  287. 'form' => [
  288. 'image_id' => $image['file_id'],
  289. 'image_path' => $image['external_url'],
  290. 'goods_sku_no' => $item['goods_sku_no'],
  291. 'goods_price' => $item['goods_price'],
  292. 'goods_weight' => $item['goods_weight'],
  293. 'line_price' => $item['line_price'],
  294. 'stock_num' => $item['stock_num'],
  295. ],
  296. ];
  297. }
  298. return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData];
  299. }
  300. /**
  301. * 获取商品记录
  302. * @param int $goodsId
  303. * @param array $with
  304. * @return static
  305. */
  306. public static function detail(int $goodsId, array $with = [])
  307. {
  308. return static::get($goodsId, $with);
  309. }
  310. // /**
  311. // * 指定的商品规格信息
  312. // * @param static $goods 商品详情
  313. // * @param string $goodsSkuId
  314. // * @return array|bool
  315. // */
  316. // public static function getGoodsSku(self $goods, string $goodsSkuId)
  317. // {
  318. // // 获取指定的sku
  319. // $goodsSku = [];
  320. // foreach ($goods['skuList'] as $item) {
  321. // if ($item['goods_sku_id'] == $goodsSkuId) {
  322. // $goodsSku = $item;
  323. // break;
  324. // }
  325. // }
  326. // if (empty($goodsSku)) {
  327. // return false;
  328. // }
  329. // // 多规格文字内容
  330. // $goodsSku['goods_attr'] = '';
  331. // if ($goods['spec_type'] == 20) {
  332. // $specRelData = helper::arrayColumn2Key($goods['spec_rel'], 'spec_value_id');
  333. // $attrs = explode('_', $goodsSku['goods_sku_id']);
  334. // foreach ($attrs as $specValueId) {
  335. // $goodsSku['goods_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':'
  336. // . $specRelData[$specValueId]['spec_value'] . '; ';
  337. // }
  338. // }
  339. // return $goodsSku;
  340. // }
  341. }