Product.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\common\model\store\product;
  12. use app\common\dao\store\StoreSeckillActiveDao;
  13. use app\common\model\BaseModel;
  14. use app\common\model\store\coupon\StoreCouponProduct;
  15. use app\common\model\store\shipping\ShippingTemplate;
  16. use app\common\model\store\StoreBrand;
  17. use app\common\model\store\StoreCategory;
  18. use app\common\model\store\StoreSeckillActive;
  19. use app\common\model\system\merchant\Merchant;
  20. use app\common\repositories\store\StoreCategoryRepository;
  21. use crmeb\services\VicWordService;
  22. use think\db\BaseQuery;
  23. use think\facade\Db;
  24. use think\model\concern\SoftDelete;
  25. class Product extends BaseModel
  26. {
  27. use SoftDelete;
  28. protected $deleteTime = 'is_del';
  29. protected $defaultSoftDelete = 0;
  30. /**
  31. * @Author:Qinii
  32. * @Date: 2020/5/8
  33. * @return string
  34. */
  35. public static function tablePk(): string
  36. {
  37. return 'product_id';
  38. }
  39. /**
  40. * @Author:Qinii
  41. * @Date: 2020/5/8
  42. * @return string
  43. */
  44. public static function tableName(): string
  45. {
  46. return 'store_product';
  47. }
  48. /*
  49. * -----------------------------------------------------------------------------------------------------------------
  50. * 属性
  51. * -----------------------------------------------------------------------------------------------------------------
  52. */
  53. public function getSliderImageAttr($value)
  54. {
  55. return $value ? explode(',',$value) : [];
  56. }
  57. public function getGiveCouponIdsAttr($value)
  58. {
  59. return $value ? explode(',',$value) : [];
  60. }
  61. public function getMaxExtensionAttr($value)
  62. {
  63. if($this->extension_type){
  64. return ($this->attrValue()->order('extension_two DESC')->value('extension_one'));
  65. } else {
  66. return bcmul(($this->attrValue()->order('price DESC')->value('price')) , systemConfig('extension_one_rate'),2);
  67. }
  68. }
  69. public function getMinExtensionAttr($value)
  70. {
  71. if($this->extension_type){
  72. return ($this->attrValue()->order('extension_two ASC')->value('extension_two'));
  73. } else {
  74. return bcmul(($this->attrValue()->order('price ASC')->value('price')) , systemConfig('extension_one_rate'),2);
  75. }
  76. }
  77. public function check()
  78. {
  79. if(!$this || !$this->is_show || !$this->is_used || !$this->status || $this->is_del || !$this->mer_status) return false;
  80. return true;
  81. }
  82. /**
  83. * TODO 秒杀商品结束时间
  84. * @return false|int
  85. * @author Qinii
  86. * @day 2020-08-15
  87. */
  88. public function getEndTimeAttr()
  89. {
  90. if($this->product_type !== 1) return true;
  91. $day = date('Y-m-d',time());
  92. $_day = strtotime($day);
  93. $end_day = strtotime($this->seckillActive['end_day']);
  94. if($end_day >= $_day)
  95. return strtotime($day.$this->seckillActive['end_time'].':00:00');
  96. if($end_day < strtotime($day))
  97. return strtotime(date('Y-m-d',$end_day).$this->seckillActive['end_time'].':00:00');
  98. }
  99. /**
  100. * TODO 秒杀商品状态
  101. * @return array|int
  102. * @author Qinii
  103. * @day 2020-08-19
  104. */
  105. public function getSeckillStatusAttr()
  106. {
  107. if($this->product_type !== 1) return true;
  108. $day = strtotime(date('Y-m-d',time()));
  109. $_h = date('H',time());
  110. $start_day = strtotime($this->seckillActive['start_day']);
  111. $end_day = strtotime($this->seckillActive['end_day']);
  112. if(!$this->seckillActive) return '';
  113. if($this->seckillActive['status'] !== -1){
  114. //还未开始
  115. if($start_day > time() || $this->is_show !== 1)return 0;
  116. //已结束
  117. if($end_day < $day) return -1;
  118. //开始 - 结束
  119. if($start_day <= $day && $day <= $end_day){
  120. //未开始
  121. if($this->seckillActive['start_time'] > $_h) return 0;
  122. //已结束
  123. if($this->seckillActive['end_time'] <= $_h) return -1;
  124. //进行中
  125. if($this->seckillActive['start_time'] <= $_h && $this->seckillActive['end_time'] > $_h) return 1;
  126. }
  127. }
  128. //已结束
  129. return -1;
  130. }
  131. public function getImageAttr($value)
  132. {
  133. if (is_int(strpos($value, 'http'))){
  134. return $value;
  135. }else{
  136. return systemConfig('site_url') .$value;
  137. }
  138. }
  139. public function getTopReplyAttr()
  140. {
  141. $res = ProductReply::where('product_id',$this->product_id)->where('is_del',0)->with(['orderProduct'])->field('reply_id,uid,nickname,merchant_reply_content,avatar,order_product_id,product_id,product_score,service_score,postage_score,comment,pics,rate,create_time')
  142. ->order('create_time DESC')->limit(1)->find();
  143. if(!$res) return null;
  144. if ($res['orderProduct'])
  145. $res['sku'] = $res['orderProduct']['cart_info']['productAttr']['sku'];
  146. unset($res['orderProduct']);
  147. return $res;
  148. }
  149. public function getUsStatusAttr()
  150. {
  151. return ($this->status == 1) ? ($this->is_used == 1 ? ( $this->is_show ? 1 : 0 ) : -1) : -1;
  152. }
  153. // public function getStarAttr()
  154. // {
  155. // return Spu::where('product_id',$this->product_id)->value('star');
  156. // }
  157. /*
  158. * -----------------------------------------------------------------------------------------------------------------
  159. * 关联模型
  160. * -----------------------------------------------------------------------------------------------------------------
  161. */
  162. public function merCateId()
  163. {
  164. return $this->hasMany(ProductCate::class,'product_id','product_id')->field('product_id,mer_cate_id');
  165. }
  166. public function attr()
  167. {
  168. return $this->hasMany(ProductAttr::class,'product_id','product_id');
  169. }
  170. public function attrValue()
  171. {
  172. return $this->hasMany(ProductAttrValue::class,'product_id','product_id');
  173. }
  174. public function oldAttrValue()
  175. {
  176. return $this->hasMany(ProductAttrValue::class,'product_id','old_product_id');
  177. }
  178. public function content()
  179. {
  180. return $this->hasOne(ProductContent::class,'product_id','product_id');
  181. }
  182. protected function temp()
  183. {
  184. return $this->hasOne(ShippingTemplate::class,'shipping_template_id','temp_id');
  185. }
  186. public function storeCategory()
  187. {
  188. return $this->hasOne(StoreCategory::class,'store_category_id','cate_id')->field('store_category_id,cate_name');
  189. }
  190. public function merchant()
  191. {
  192. return $this->hasOne(Merchant::class,'mer_id','mer_id')->field('is_trader,mer_id,mer_name,mer_avatar,product_score,service_score,postage_score,service_phone,care_count');
  193. }
  194. public function reply()
  195. {
  196. return $this->hasMany(ProductReply::class,'product_id','product_id')->order('create_time DESC');
  197. }
  198. public function brand()
  199. {
  200. return $this->hasOne(StoreBrand::class,'brand_id','brand_id')->field('brand_id,brand_name');
  201. }
  202. public function seckillActive()
  203. {
  204. return $this->hasOne(StoreSeckillActive::class,'product_id','product_id');
  205. }
  206. public function issetCoupon()
  207. {
  208. return $this->hasOne(StoreCouponProduct::class, 'product_id', 'product_id')->alias('A')
  209. ->rightJoin('StoreCoupon B', 'A.coupon_id = B.coupon_id')->where(function (BaseQuery $query) {
  210. $query->where('B.is_limited', 0)->whereOr(function (BaseQuery $query) {
  211. $query->where('B.is_limited', 1)->where('B.remain_count', '>', 0);
  212. });
  213. })->where(function (BaseQuery $query) {
  214. $query->where('B.is_timeout', 0)->whereOr(function (BaseQuery $query) {
  215. $time = date('Y-m-d H:i:s');
  216. $query->where('B.is_timeout', 1)->where('B.start_time', '<', $time)->where('B.end_time', '>', $time);
  217. });
  218. })->field('A.product_id,B.*')->where('status', 1)->where('type', 1)->where('send_type', 0)->where('is_del', 0)
  219. ->order('sort DESC,coupon_id DESC')->hidden(['is_del', 'status']);
  220. }
  221. public function assist()
  222. {
  223. return $this->hasOne(ProductAssist::class,'product_id','product_id');
  224. }
  225. public function productGroup()
  226. {
  227. return $this->hasOne(ProductGroup::class,'product_id','product_id');
  228. }
  229. /*
  230. * -----------------------------------------------------------------------------------------------------------------
  231. * 搜索器
  232. * -----------------------------------------------------------------------------------------------------------------
  233. */
  234. public function searchMerCateIdAttr($query, $value)
  235. {
  236. $cate_ids = (StoreCategory::where('path','like','%/'.$value.'/%'))->column('store_category_id');
  237. $cate_ids[] = intval($value);
  238. $product_id = ProductCate::whereIn('mer_cate_id',$cate_ids)->column('product_id');
  239. $query->whereIn('Product.product_id',$product_id);
  240. }
  241. public function searchKeywordAttr($query, $value)
  242. {
  243. if (!$value) return;
  244. if (is_numeric($value)) {
  245. $query->whereLike("Product.store_name|Product.keyword|bar_code|Product.product_id", "%{$value}%");
  246. } else {
  247. $word = app()->make(VicWordService::class)->getWord($value);
  248. $query->where(function ($query) use ($word, $value) {
  249. foreach ($word as $item) {
  250. $query->whereOr('Product.store_name|Product.keyword', 'LIKE', "%$item%");
  251. }
  252. $query->order(Db::raw('REPLACE(Product.store_name,\'' . $value . '\',\'\')'));
  253. });
  254. }
  255. }
  256. public function searchStatusAttr($query, $value)
  257. {
  258. if($value === -1){
  259. $query->where('Product.status', 'in',[-1,-2]);
  260. }else {
  261. $query->where('Product.status',$value);
  262. }
  263. }
  264. public function searchCateIdAttr($query, $value)
  265. {
  266. $query->where('cate_id',$value);
  267. }
  268. public function searchCateIdsAttr($query, $value)
  269. {
  270. $query->whereIn('cate_id',$value);
  271. }
  272. public function searchIsShowAttr($query, $value)
  273. {
  274. $query->where('is_show',$value);
  275. }
  276. public function searchPidAttr($query, $value)
  277. {
  278. $cateId = app()->make(StoreCategoryRepository::class)->allChildren(intval($value));
  279. $query->whereIn('cate_id', $cateId);
  280. }
  281. public function searchStockAttr($query, $value)
  282. {
  283. $value ? $query->where('stock','<=', $value) : $query->where('stock', $value);
  284. }
  285. public function searchIsNewAttr($query, $value)
  286. {
  287. $query->where('is_new',$value);
  288. }
  289. public function searchPriceAttr($query, $value)
  290. {
  291. if(empty($value[0]) && !empty($value[1]))
  292. $query->where('price','<',$value[1]);
  293. if(!empty($value[0]) && empty($value[1]))
  294. $query->where('price','>',$value[0]);
  295. if(!empty($value[0]) && !empty($value[1]))
  296. $query->whereBetween('price',[$value[0],$value[1]]);
  297. }
  298. public function searchBrandIdAttr($query, $value)
  299. {
  300. $query->whereIn('brand_id',$value);
  301. }
  302. public function searchIsGiftBagAttr($query, $value)
  303. {
  304. $query->where('is_gift_bag',$value);
  305. }
  306. public function searchIsGoodAttr($query, $value)
  307. {
  308. $query->where('is_good',$value);
  309. }
  310. public function searchIsUsedAttr($query, $value)
  311. {
  312. $query->where('is_used',$value);
  313. }
  314. public function searchProductTypeAttr($query, $value)
  315. {
  316. $query->where('Product.product_type',$value);
  317. }
  318. public function searchSeckillStatusAttr($query, $value)
  319. {
  320. $product_id = (new StoreSeckillActiveDao())->getStatus($value)->column('product_id');
  321. $query->whereIn('Product.product_id',$product_id);
  322. }
  323. public function searchStoreNameAttr($query, $value)
  324. {
  325. $query->where('Product.store_name','like','%'.$value.'%');
  326. }
  327. public function searchMerStatusAttr($query, $value)
  328. {
  329. $query->where('mer_status',$value);
  330. }
  331. public function searchProductIdAttr($query, $value)
  332. {
  333. $query->where('Product.product_id',$value);
  334. }
  335. public function searchPriceOnAttr($query, $value)
  336. {
  337. $query->where('price','>=',$value);
  338. }
  339. public function searchPriceOffAttr($query, $value)
  340. {
  341. $query->where('price','<=',$value);
  342. }
  343. }