Product.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <?php
  2. namespace app\common\model\product;
  3. use app\api\model\supplier\Supplier as SupplierModel;
  4. use app\common\library\helper;
  5. use app\common\model\BaseModel;
  6. use think\facade\Db;
  7. /**
  8. * 商品模型
  9. */
  10. class Product extends BaseModel
  11. {
  12. protected $name = 'product';
  13. protected $pk = 'product_id';
  14. protected $append = ['product_sales'];
  15. /**
  16. * 计算显示销量 (初始销量 + 实际销量)
  17. */
  18. public function getProductSalesAttr($value, $data)
  19. {
  20. return $data['sales_initial'] + $data['sales_actual'];
  21. }
  22. /**
  23. * 获取器:单独设置折扣的配置
  24. */
  25. public function getAloneGradeEquityAttr($json)
  26. {
  27. return json_decode($json, true);
  28. }
  29. /**
  30. * 修改器:单独设置折扣的配置
  31. */
  32. public function setAloneGradeEquityAttr($data)
  33. {
  34. return json_encode($data);
  35. }
  36. /**
  37. * 关联商品分类表
  38. */
  39. public function category()
  40. {
  41. return $this->belongsTo('app\\common\\model\\product\\Category');
  42. }
  43. /**
  44. * 关联商品规格表
  45. */
  46. public function sku()
  47. {
  48. return $this->hasMany('ProductSku')->order(['product_sku_id' => 'asc']);
  49. }
  50. /**
  51. * 关联商品规格关系表
  52. */
  53. public function specRel()
  54. {
  55. return $this->belongsToMany('SpecValue', 'ProductSpecRel')->order(['id' => 'asc']);
  56. }
  57. /**
  58. * 关联商品图片表
  59. */
  60. public function image()
  61. {
  62. return $this->hasMany('app\\common\\model\\product\\ProductImage')->where('image_type', '=', 0)->order(['id' => 'asc']);
  63. }
  64. /**
  65. * 关联商品详情图片表
  66. */
  67. public function contentImage()
  68. {
  69. return $this->hasMany('app\\common\\model\\product\\ProductImage')->where('image_type', '=', 1)->order(['id' => 'asc']);
  70. }
  71. /**
  72. * 关联运费模板表
  73. */
  74. public function delivery()
  75. {
  76. return $this->BelongsTo('app\\common\\model\\settings\\Delivery');
  77. }
  78. /**
  79. * 关联订单评价表
  80. */
  81. public function commentData()
  82. {
  83. return $this->hasMany('app\\common\\model\\product\\Comment', 'product_id', 'product_id');
  84. }
  85. /**
  86. * 关联供应商表
  87. */
  88. public function supplier()
  89. {
  90. return $this->belongsTo('app\\common\\model\\supplier\\Supplier', 'shop_supplier_id', 'shop_supplier_id')
  91. ->field(['shop_supplier_id', 'name', 'address', 'logo_id']);
  92. }
  93. /**
  94. * 关联视频
  95. */
  96. public function video()
  97. {
  98. return $this->hasOne('app\\common\\model\\file\\UploadFile', 'file_id', 'video_id');
  99. }
  100. /**
  101. * 关联视频封面
  102. */
  103. public function poster()
  104. {
  105. return $this->hasOne('app\\common\\model\\file\\UploadFile', 'file_id', 'poster_id');
  106. }
  107. /**
  108. * 计费方式
  109. */
  110. public function getProductStatusAttr($value, $data)
  111. {
  112. $status = [10 => '已上架', 20 => '仓库中', 30 => '回收站'];
  113. return ['text' => $status[$value], 'value' => $value];
  114. }
  115. /**
  116. * 获取商品列表
  117. */
  118. public function getList($param)
  119. {
  120. // 商品列表获取条件
  121. $params = array_merge([
  122. 'type' => 'sell', // 商品状态
  123. 'category_id' => 0, // 分类id
  124. 'sortType' => 'all', // 排序类型
  125. 'sortPrice' => false, // 价格排序 高低
  126. 'list_rows' => 15, // 每页数量
  127. 'audit_status' => -1, //审核状态
  128. ], $param);
  129. // 筛选条件
  130. $filter = [];
  131. $model = $this;
  132. if ($params['category_id'] > 0) {
  133. $arr = Category::getSubCategoryId($params['category_id']);
  134. $model = $model->where('product.category_id', 'IN', $arr);
  135. }
  136. if (!empty($params['product_name'])) {
  137. $model = $model->where('product_name', 'like', '%' . trim($params['product_name']) . '%');
  138. }
  139. if (!empty($params['search'])) {
  140. $model = $model->where('product_name', 'like', '%' . trim($params['search']) . '%');
  141. }
  142. if ($params['audit_status'] > -1) {
  143. $model = $model->where('audit_status', '=', $params['audit_status']);
  144. }
  145. // 排序规则
  146. $sort = [];
  147. if ($params['sortType'] === 'all') {
  148. $sort = ['product_sort', 'product_id' => 'desc'];
  149. } else if ($params['sortType'] === 'sales') {
  150. $sort = ['product_sales' => 'desc'];
  151. } else if ($params['sortType'] === 'price') {
  152. $sort = $params['sortPrice'] ? ['product_max_price' => 'desc'] : ['product_min_price'];
  153. }
  154. if (isset($params['type'])) {
  155. $model = $this->buildProductType($model, $params['type']);
  156. }
  157. if(isset($params['shop_supplier_id'])&&$params['shop_supplier_id']){
  158. $model = $model->where('product.shop_supplier_id', '=', $params['shop_supplier_id']);
  159. }
  160. if(isset($params['product_id'])&&$params['product_id']){
  161. $model = $model->whereNotIn('product_id',$params['product_id']);
  162. }
  163. // 多规格商品 最高价与最低价
  164. $ProductSku = new ProductSku;
  165. $minPriceSql = $ProductSku->field(['MIN(product_price)'])
  166. ->where('product_id', 'EXP', "= `product`.`product_id`")->buildSql();
  167. $maxPriceSql = $ProductSku->field(['MAX(product_price)'])
  168. ->where('product_id', 'EXP', "= `product`.`product_id`")->buildSql();
  169. // 执行查询
  170. $list = $model->alias('product')
  171. ->field(['product.*', '(sales_initial + sales_actual) as product_sales',
  172. "$minPriceSql AS product_min_price",
  173. "$maxPriceSql AS product_max_price"
  174. ])
  175. ->with(['category', 'image.file', 'sku', 'supplier'])
  176. ->join('supplier supplier', 'product.shop_supplier_id = supplier.shop_supplier_id','left')
  177. ->where('product.is_delete', '=', 0)
  178. ->where('supplier.is_delete', '=', 0)
  179. ->where('supplier.status', '=', 0)
  180. ->where('supplier.is_recycle', '=', 0)
  181. ->where($filter)
  182. ->order($sort)
  183. ->paginate($params);
  184. // 整理列表数据并返回
  185. return $this->setProductListData($list, true);
  186. }
  187. public function buildProductType($model, $type){
  188. //出售中
  189. if ($type == 'sell') {
  190. $model = $model->where('product_status', '=', 10);
  191. $model = $model->where('audit_status', '=', 10);
  192. }
  193. //仓库中
  194. if ($type == 'lower') {
  195. $model = $model->where('product_status', '=', 20);
  196. $model = $model->where('audit_status', '=', 10);
  197. }
  198. //回收站
  199. if ($type == 'recovery') {
  200. $model = $model->where('product_status', '=', 30);
  201. }
  202. //待审核
  203. if ($type == 'audit') {
  204. $model = $model->where('audit_status', '=', 0);
  205. }
  206. //未通过
  207. if ($type == 'no_audit') {
  208. $model = $model->where('audit_status', '=', 20);
  209. }
  210. //库存紧张
  211. if ($type == 'stock') {
  212. $model = $model->where('product_stock', '<', 10);
  213. $model = $model->where('product_status', '=', 10);
  214. $model = $model->where('audit_status', '=', 10);
  215. }
  216. //草稿
  217. if ($type == 'draft') {
  218. $model = $model->where('audit_status', '=', 40);
  219. }
  220. return $model;
  221. }
  222. /**
  223. * 获取商品列表
  224. */
  225. public function getLists($param)
  226. {
  227. // 商品列表获取条件
  228. $params = array_merge([
  229. 'product_status' => 10, // 商品状态
  230. 'category_id' => 0, // 分类id
  231. ], $param);
  232. // 筛选条件
  233. $model = $this;
  234. if ($params['category_id'] > 0) {
  235. $arr = Category::getSubCategoryId($params['category_id']);
  236. $model = $model->where('category_id', 'IN', $arr);
  237. }
  238. if (!empty($params['product_name'])) {
  239. $model = $model->where('product_name', 'like', '%' . trim($params['product_name']) . '%');
  240. }
  241. if (!empty($params['search'])) {
  242. $model = $model->where('product_name', 'like', '%' . trim($params['search']) . '%');
  243. }
  244. $list = $model
  245. ->with(['category', 'image.file'])
  246. ->where('is_delete', '=', 0)
  247. ->where('product_status', '=', $params['product_status'])
  248. ->paginate($params);
  249. // 整理列表数据并返回
  250. return $this->setProductListData($list, true);
  251. }
  252. /**
  253. * 设置商品展示的数据
  254. */
  255. protected function setProductListData($data, $isMultiple = true, callable $callback = null)
  256. {
  257. if (!$isMultiple) $dataSource = [&$data]; else $dataSource = &$data;
  258. // 整理商品列表数据
  259. foreach ($dataSource as &$product) {
  260. // 商品主图
  261. $product['product_image'] = $product['image'][0]['file_path'];
  262. // 商品默认规格
  263. $product['product_sku'] = self::getShowSku($product);
  264. // 等级id转换成数组
  265. if(!is_array ($product['grade_ids'])){
  266. if($product['grade_ids'] != ''){
  267. $product['grade_ids'] = explode(',', $product['grade_ids']);
  268. } else {
  269. $product['grade_ids'] = [];
  270. }
  271. }
  272. // 回调函数
  273. is_callable($callback) && call_user_func($callback, $product);
  274. }
  275. return $data;
  276. }
  277. /**
  278. * 根据商品id集获取商品列表
  279. */
  280. public function getListByIds($productIds, $status = null)
  281. {
  282. $model = $this;
  283. $filter = [];
  284. // 筛选条件
  285. $status > 0 && $filter['product_status'] = $status;
  286. if (!empty($productIds)) {
  287. $model = $model->orderRaw('field(product_id, ' . implode(',', $productIds) . ')');
  288. }
  289. // 获取商品列表数据
  290. $data = $model->with(['category', 'image.file', 'sku'])
  291. ->where('audit_status', '=', 10)
  292. ->where($filter)
  293. ->where('product_id', 'in', $productIds)
  294. ->select();
  295. // 整理列表数据并返回
  296. return $this->setProductListData($data, true);
  297. }
  298. /**
  299. * 商品多规格信息
  300. */
  301. public function getManySpecData($specRel, $skuData)
  302. {
  303. // spec_attr
  304. $specAttrData = [];
  305. foreach ($specRel as $item) {
  306. if (!isset($specAttrData[$item['spec_id']])) {
  307. $specAttrData[$item['spec_id']] = [
  308. 'group_id' => $item['spec']['spec_id'],
  309. 'group_name' => $item['spec']['spec_name'],
  310. 'spec_items' => [],
  311. ];
  312. }
  313. $specAttrData[$item['spec_id']]['spec_items'][] = [
  314. 'item_id' => $item['spec_value_id'],
  315. 'spec_value' => $item['spec_value'],
  316. ];
  317. }
  318. // spec_list
  319. $specListData = [];
  320. foreach ($skuData as $item) {
  321. $image = (isset($item['image']) && !empty($item['image'])) ? $item['image'] : ['file_id' => 0, 'file_path' => ''];
  322. $specListData[] = [
  323. 'product_sku_id' => $item['product_sku_id'],
  324. 'spec_sku_id' => $item['spec_sku_id'],
  325. 'rows' => [],
  326. 'spec_form' => [
  327. 'image_id' => $image['file_id'],
  328. 'image_path' => $image['file_path'],
  329. 'product_no' => $item['product_no'],
  330. 'product_price' => $item['product_price'],
  331. 'product_weight' => $item['product_weight'],
  332. 'line_price' => $item['line_price'],
  333. 'stock_num' => $item['stock_num'],
  334. 'supplier_price' => $item['supplier_price'],
  335. ],
  336. ];
  337. }
  338. return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData];
  339. }
  340. /**
  341. * 多规格表格数据
  342. */
  343. public function getManySpecTable(&$product)
  344. {
  345. $specData = $this->getManySpecData($product['spec_rel'], $product['sku']);
  346. $totalRow = count($specData['spec_list']);
  347. foreach ($specData['spec_list'] as $i => &$sku) {
  348. $rowData = [];
  349. $rowCount = 1;
  350. foreach ($specData['spec_attr'] as $attr) {
  351. $skuValues = $attr['spec_items'];
  352. $rowCount *= count($skuValues);
  353. $anInterBankNum = ($totalRow / $rowCount);
  354. $point = (($i / $anInterBankNum) % count($skuValues));
  355. if (0 === ($i % $anInterBankNum)) {
  356. $rowData[] = [
  357. 'rowspan' => $anInterBankNum,
  358. 'item_id' => $skuValues[$point]['item_id'],
  359. 'spec_value' => $skuValues[$point]['spec_value']
  360. ];
  361. }
  362. }
  363. $sku['rows'] = $rowData;
  364. }
  365. return $specData;
  366. }
  367. /**
  368. * 获取商品详情
  369. */
  370. public static function detail($product_id)
  371. {
  372. $model = (new static())->with([
  373. 'category',
  374. 'image.file',
  375. 'sku.image',
  376. 'spec_rel.spec',
  377. 'supplier.logo',
  378. 'video',
  379. 'poster',
  380. 'contentImage.file',
  381. ])->where('product_id', '=', $product_id)
  382. ->find();
  383. if (empty($model)) {
  384. return $model;
  385. }
  386. // 整理商品数据并返回
  387. return $model->setProductListData($model, false);
  388. }
  389. /**
  390. * 指定的商品规格信息
  391. */
  392. public static function getProductSku($product, $specSkuId)
  393. {
  394. // 获取指定的sku
  395. $productSku = [];
  396. foreach ($product['sku'] as $item) {
  397. if ($item['spec_sku_id'] == $specSkuId) {
  398. $productSku = $item;
  399. break;
  400. }
  401. }
  402. if (empty($productSku)) {
  403. return false;
  404. }
  405. // 多规格文字内容
  406. $productSku['product_attr'] = '';
  407. if ($product['spec_type'] == 20) {
  408. $specRelData = helper::arrayColumn2Key($product['spec_rel'], 'spec_value_id');
  409. $attrs = explode('_', $productSku['spec_sku_id']);
  410. foreach ($attrs as $specValueId) {
  411. $productSku['product_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':'
  412. . $specRelData[$specValueId]['spec_value'] . '; ';
  413. }
  414. }
  415. return $productSku;
  416. }
  417. /**
  418. * 根据商品名称得到相关列表
  419. */
  420. public function getWhereData($product_name)
  421. {
  422. return $this->where('product_name', 'like', '%' . trim($product_name) . '%')->select();
  423. }
  424. /**
  425. * 显示的sku,目前取最低价
  426. */
  427. public static function getShowSku($product){
  428. //如果是单规格
  429. if($product['spec_type'] == 10){
  430. return $product['sku'][0];
  431. }else{
  432. //多规格返回最低价
  433. foreach ($product['sku'] as $sku){
  434. if($product['product_price'] == $sku['product_price']){
  435. return $sku;
  436. }
  437. }
  438. }
  439. // 兼容历史数据,如果找不到返回第一个
  440. return $product['sku'][0];
  441. }
  442. /**
  443. * 获取当前商品总数
  444. */
  445. public function getProductTotal($where = [])
  446. {
  447. return $this->where('is_delete', '=', 0)->where($where)->count();
  448. }
  449. /**
  450. * 供应商商品总销量
  451. */
  452. public function reSupplierTotalSales($shop_supplier_id){
  453. $total = $this->where('shop_supplier_id', '=', $shop_supplier_id)
  454. ->sum(Db::raw('sales_initial + sales_actual'));
  455. return (new SupplierModel())->where('shop_supplier_id', '=', $shop_supplier_id)
  456. ->save([
  457. 'product_sales' => $total
  458. ]);
  459. }
  460. }