ShopGoodsService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. <?php
  2. namespace app\common\service;
  3. use app\common\model\GoodsAttensionModel;
  4. use app\common\model\ShopGoodsModel;
  5. use think\Exception;
  6. use think\facade\Db;
  7. use utils\RedisCache;
  8. /**
  9. * 商品服务 by wes
  10. * Class ShopGoodsService
  11. * @package app\common\service
  12. */
  13. class ShopGoodsService
  14. {
  15. protected static $instance = null;
  16. protected $model = null;
  17. public function __construct()
  18. {
  19. $this->model = new ShopGoodsModel();
  20. }
  21. /**
  22. * 静态化入口
  23. * @return static|null
  24. */
  25. public static function make()
  26. {
  27. if(!self::$instance){
  28. self::$instance = new static();
  29. }
  30. return self::$instance;
  31. }
  32. /**
  33. * 获取列表
  34. * @param $map 分组
  35. * @param $pageSize 分页大小
  36. * @param $field 返回字段
  37. * @param $cache 是否缓存数据,默认是
  38. * @return array|mixed
  39. * @throws \think\db\exception\DataNotFoundException
  40. * @throws \think\db\exception\DbException
  41. * @throws \think\db\exception\ModelNotFoundException
  42. */
  43. public function getList($map, $pageSize=10, $field='', $cache=true)
  44. {
  45. $page = request()->post('page', 1);
  46. $cacheKey = "caches:goods:list_{$page}_{$pageSize}_".md5(json_encode($map, 256).$field);
  47. $list = RedisCache::get($cacheKey);
  48. if($list && $cache){
  49. return $list;
  50. }
  51. $where = ['on_sale'=> 1];
  52. $menuId = isset($map['menu_id'])? intval($map['menu_id']) : 0;
  53. if ($menuId>0){
  54. $where['menu_id'] = $menuId;
  55. }
  56. $goodsType = isset($map['goods_type'])? intval($map['goods_type']) : 0;
  57. if ($goodsType>0){
  58. $where['goods_type'] = $goodsType;
  59. }
  60. $keywords = isset($map['keywords'])? trim($map['keywords']) : '';
  61. if (!empty($keywords)) {
  62. $where['goods_name|hot_keywords'] = "%{$map['keywords']}%";
  63. }
  64. $field = $field? $field : 'sort,category,goods_sn,goods_name,goods_type,goods_img,min_original_price as original_price,min_price as price,rebate_score,sales_volume,inventory,attension_count,restrictions_num';
  65. $order = isset($map['sort']) && $map['sort']? $map['sort'] : 'sort desc,goods_id desc';
  66. $list = $this->model->where($where)
  67. ->where(function($query) use($map){
  68. $cls = isset($map['cls'])? trim($map['cls']) : 0;
  69. if (!empty($cls)) {
  70. $query->where('give_vip','>', 0);
  71. }
  72. })
  73. ->withAttr('goods_img',function($value){
  74. return getPreviewUrl($value);
  75. })
  76. ->field($field)
  77. ->order($order)
  78. ->paginate($pageSize);
  79. $list = $list? $list->toArray():[];
  80. if($list){
  81. RedisCache::set($cacheKey, $list, rand(10,20));
  82. }
  83. return $list;
  84. }
  85. /**
  86. * 获取随机商品
  87. * @param int $num
  88. * @return array|mixed
  89. */
  90. public function getRandomGoods($num=12)
  91. {
  92. $cacheKey = "caches:goods:random:{$num}";
  93. $data = RedisCache::get($cacheKey);
  94. if($data){
  95. return $data;
  96. }
  97. $data = $this->model->where('on_sale', 1)
  98. ->withAttr('box_pic',function ($value){
  99. return getPreviewUrl($value);
  100. })
  101. ->orderRaw("rand() , goods_id DESC")
  102. ->field('goods_id,box_pic,goods_sn')
  103. ->limit($num)
  104. ->select()
  105. ->toArray();
  106. if($data){
  107. RedisCache::set($cacheKey, $data, rand(5, 10));
  108. }
  109. return $data?$data : [];
  110. }
  111. /**
  112. * 获取开奖福袋商品
  113. * @param int $num
  114. * @return array|mixed
  115. * @throws \think\db\exception\DataNotFoundException
  116. * @throws \think\db\exception\DbException
  117. * @throws \think\db\exception\ModelNotFoundException
  118. */
  119. public function getBoxMatchGoods($boxType, $canOpen=1, $num = 30)
  120. {
  121. $cacheKey = "caches:temp:boxGoods:{$boxType}_{$num}_{$canOpen}";
  122. $data = RedisCache::get($cacheKey);
  123. if($data){
  124. return $data;
  125. }
  126. $data = $this->model->where('box_type', $boxType)
  127. ->where('goods_type', 2)
  128. ->where('can_open', $canOpen)
  129. ->withAttr('goods_img', function($value){
  130. return getPreviewUrl($value);
  131. })
  132. ->field('goods_id,box_type,goods_img,price,goods_name')
  133. ->limit($num)
  134. ->select();
  135. $data = $data? $data->toArray() : [];
  136. if($data){
  137. RedisCache::set($cacheKey, $data, 86400);
  138. }
  139. return $data;
  140. }
  141. /**
  142. * 获取商品详情
  143. * @param $map
  144. * @return array|mixed
  145. * @throws \think\db\exception\DataNotFoundException
  146. * @throws \think\db\exception\DbException
  147. * @throws \think\db\exception\ModelNotFoundException
  148. */
  149. public function getDetail($map)
  150. {
  151. $where = [];
  152. $goodsSn = isset($map['goods_sn'])? trim($map['goods_sn']) : 0;
  153. $goodsId = isset($map['goods_id'])? intval($map['goods_id']) : 0;
  154. if($goodsSn){
  155. $where['goods_sn'] = $goodsSn;
  156. }
  157. if($goodsId){
  158. $where['goods_id'] = $goodsId;
  159. }
  160. $isCache = false;
  161. $cacheKey = "caches:goods:detail_sn{$goodsSn}_g{$goodsId}";
  162. $info = RedisCache::get($cacheKey);
  163. if(empty($info)){
  164. $field = 'menu_id,spec_name,goods_id,category,goods_sn,goods_name,goods_img,goods_img_banner,goods_remark,min_original_price as original_price,min_price as price,note,inventory,rebate_score,sales_volume,restrictions,rush_buy,buynote_template as buyNote,post_template_id,restrictions_num';
  165. $info = $this->model->where($where)->field($field)
  166. ->withAttr('goods_img_banner', function ($value) {
  167. $banners = $value? json_decode($value, true) : [];
  168. if($banners){
  169. foreach ($banners as &$v){
  170. $v = getPreviewUrl($v);
  171. }
  172. }
  173. return $banners;
  174. })->withAttr('goods_img', function ($value) {
  175. return getPreviewUrl($value);
  176. })->withAttr('goods_remark', function ($value) {
  177. return getContent(htmlspecialchars_decode($value));
  178. })->withAttr('buyNote', function ($value) {
  179. return htmlspecialchars_decode($value);
  180. })->findOrEmpty()->toArray();
  181. }else{
  182. $isCache = true;
  183. }
  184. if($info){
  185. $specRelation = ShopGoodsSpecRelationService::make()->getDataByGoods($info['goods_id']);
  186. $goodsExpress = ExpressDeliveryService::make()->getDataByTemplate($info['post_template_id']);
  187. $goodsSpec = ShopGoodsSpecService::make()->getListByGoods($info['goods_id']);
  188. $info['expressNote'] = $goodsExpress['remake'] ?: '';
  189. $info['spec_relation'] = $specRelation;
  190. $info['goods_spec'] = $goodsSpec;
  191. $attensionCount = RedisCache::get("caches:goods:attension:{$info['goods_id']}");
  192. if($isCache && $attensionCount>0){
  193. $info['attension_count'] = $attensionCount;
  194. }
  195. RedisCache::set($cacheKey, $info, rand(3,5));
  196. }
  197. return $info;
  198. }
  199. /**
  200. * 更新商品浏览量
  201. * @param $goodsId
  202. * @return false
  203. */
  204. public function updateScanCount($goodsId)
  205. {
  206. if($goodsId && $this->model->where(['goods_id'=> $goodsId])->inc('scan_count',1)->update()){
  207. RedisCache::keyDel('caches:goods:list_*');
  208. return true;
  209. }
  210. return false;
  211. }
  212. /**
  213. * 商品收藏
  214. * @param $uid 用户
  215. * @param $goodsId
  216. * @return bool
  217. * @throws Exception
  218. * @throws \think\db\exception\DataNotFoundException
  219. * @throws \think\db\exception\DbException
  220. * @throws \think\db\exception\ModelNotFoundException
  221. */
  222. public function goodsAttension($uid, $goodsId)
  223. {
  224. $info = $this->model->where(['goods_id'=> $goodsId])->field('goods_id,attension_count')->find();
  225. if(!$info){
  226. throw new Exception('商品不存在');
  227. }
  228. // 是否收藏过
  229. if(GoodsAttensionModel::where(['uid'=> $uid,'goods_id'=> $goodsId])->value('id')){
  230. return true;
  231. }else{
  232. $data = [
  233. 'uid'=> $uid,
  234. 'goods_id'=> $goodsId,
  235. 'create_time'=>sr_getcurtime(time())
  236. ];
  237. Db::startTrans();
  238. if(!GoodsAttensionModel::insertGetId($data)){
  239. Db::rollback();
  240. throw new Exception('收藏失败');
  241. }
  242. if(!$this->model->where(['goods_id'=> $goodsId])->inc('attension_count')->update()){
  243. Db::rollback();
  244. throw new Exception('收藏失败');
  245. }
  246. $attenSionCount = isset($info['attension_count'])? $info['attension_count'] : 0;
  247. RedisCache::set("caches:goods:attension:{$goodsId}", $attenSionCount+1, rand(5, 10));
  248. RedisCache::keyDel('caches:goods:list*');
  249. Db::commit();
  250. return true;
  251. }
  252. }
  253. /**
  254. * 取消收藏
  255. * @param $uid 用户
  256. * @param $goodsId 商品ID
  257. * @return bool
  258. */
  259. public function cancelAttension($uid, $goodsId)
  260. {
  261. if(!GoodsAttensionModel::where(['uid'=> $uid,'goods_id'=> $goodsId])->value('id')){
  262. return true;
  263. }
  264. Db::startTrans();
  265. if(GoodsAttensionModel::where(['uid'=> $uid,'goods_id'=> $goodsId])->delete()){
  266. RedisCache::keyDel('caches:goods:list_*');
  267. if(!$this->model->where(['goods_id'=> $goodsId])->dec('attension_count')->update()){
  268. Db::rollback();
  269. return false;
  270. }
  271. Db::commit();
  272. RedisCache::keyDel("caches:goods:attension:{$goodsId}");
  273. return true;
  274. }else{
  275. Db::rollback();
  276. return false;
  277. }
  278. }
  279. /**
  280. * 是否收藏过该商品
  281. * @param $uid 用户
  282. * @param $goodsId 商品
  283. * @return bool|mixed
  284. */
  285. public function checkAttension($uid, $goodsId)
  286. {
  287. $cacheKey = "caches:goods:attensionUser:u_{$uid}_g{$goodsId}";
  288. if(RedisCache::get($cacheKey)){
  289. return true;
  290. }
  291. $data = GoodsAttensionModel::where(['uid'=> $uid,'goods_id'=> $goodsId])->value('id');
  292. if($data){
  293. RedisCache::set($cacheKey, $data, rand(5, 10));
  294. }
  295. return $data? true : false;
  296. }
  297. /**
  298. * 获取商品缓存信息
  299. * @param $goodsSn
  300. * @param string $field
  301. * @return array|mixed
  302. * @throws \think\db\exception\DataNotFoundException
  303. * @throws \think\db\exception\DbException
  304. * @throws \think\db\exception\ModelNotFoundException
  305. */
  306. public function getCacheInfo($goodsSn, $field='', $cache=true)
  307. {
  308. $cacheKey = "caches:goods:info_sn{$goodsSn}".($field? '_'.md5($field) : '');
  309. $info = RedisCache::get($cacheKey);
  310. if($info && $cache){
  311. return $info;
  312. }
  313. $field = $field? $field : 'a.goods_id,a.goods_sn,a.post_template_id,a.give_vip,a.goods_type,a.restrictions_num,a.rebate_score,a.goods_name,a.category,a.goods_img,a.supplier,s.name as supplier_name';
  314. $info = $this->model->alias('a')
  315. ->leftJoin('shop_supplier s','s.id = a.supplier')
  316. ->where(['a.goods_sn'=> $goodsSn])
  317. ->withAttr('goods_img', function ($value) {
  318. return getPreviewUrl($value);
  319. })
  320. ->field($field)
  321. ->find();
  322. $info = $info? $info->toArray() : [];
  323. if($info && $cache){
  324. RedisCache::set($cacheKey, $info, rand(50, 100));
  325. }
  326. return $info;
  327. }
  328. /**
  329. * 获取商品缓存信息
  330. * @param $goodsId
  331. * @param string $field
  332. * @return array|mixed
  333. * @throws \think\db\exception\DataNotFoundException
  334. * @throws \think\db\exception\DbException
  335. * @throws \think\db\exception\ModelNotFoundException
  336. */
  337. public function getCacheInfoById($goodsId, $field='', $cache=true)
  338. {
  339. $cacheKey = "caches:goods:info_id{$goodsId}".($field? '_'.md5($field) : '');
  340. $info = RedisCache::get($cacheKey);
  341. if($info && $cache){
  342. return $info;
  343. }
  344. $field = $field? $field : 'a.goods_id,a.goods_sn,a.price,a.spec_name,a.post_template_id,a.give_vip,a.goods_type,a.restrictions_num,a.rebate_score,a.goods_name,a.category,a.goods_img,a.supplier,s.name as supplier_name';
  345. $info = $this->model->alias('a')
  346. ->leftJoin('shop_supplier s','s.id = a.supplier')
  347. ->where(['a.goods_id'=> $goodsId])
  348. ->withAttr('goods_img', function ($value) {
  349. return getPreviewUrl($value);
  350. })
  351. ->field($field)
  352. ->find();
  353. $info = $info? $info->toArray() : [];
  354. if($info && $cache){
  355. RedisCache::set($cacheKey, $info, rand(50, 100));
  356. }
  357. return $info;
  358. }
  359. /**
  360. * 按编号获取商品列表
  361. * @param $snArr 编号数组
  362. * @param string $field
  363. * @return ShopGoodsModel|array|mixed|\think\Model
  364. */
  365. public function getListBySn($snArr, $field='')
  366. {
  367. $cacheKey = "caches:goods:listSn:".md5(json_encode($snArr, 256).$field);
  368. $data = RedisCache::get($cacheKey);
  369. if($data){
  370. return $data;
  371. }
  372. $field = $field? $field : 'goods_type';
  373. $data = $this->model->whereIn('goods_sn', $snArr)->field($field)
  374. ->withAttr('goods_img', function ($value) {
  375. return getPreviewUrl($value);
  376. })->findOrEmpty();
  377. if($data){
  378. $data = $data->toArray();
  379. RedisCache::set($cacheKey, $data, rand(5, 10));
  380. }
  381. return $data;
  382. }
  383. /**
  384. * 福袋商品列表
  385. * @param $boxType 福袋类型,多个类型数组
  386. * @param int $pageSize 分页数
  387. * @param bool $cache
  388. * @return array|mixed
  389. * @throws \think\db\exception\DbException
  390. */
  391. public function getBoxGoodsListByType($boxType, $pageSize=10, $cache=true)
  392. {
  393. $key = is_array($boxType)? implode('_', $boxType) : $boxType;
  394. $cacheKey = "caches:boxGoods:box_{$key}";
  395. $data = RedisCache::get($cacheKey);
  396. if($data && $cache){
  397. return $data;
  398. }
  399. $data = $this->model->where(function($query) use($boxType){
  400. if($boxType){
  401. $boxType = is_array($boxType)? $boxType : [$boxType];
  402. $query->whereIn('box_type', $boxType);
  403. }else{
  404. $query->whereIn('box_type', [10,20,30,40]);
  405. }
  406. })
  407. ->withAttr('box_pic', function ($value) {
  408. return getPreviewUrl($value);
  409. })
  410. ->withAttr('goods_img', function ($value) {
  411. return getPreviewUrl($value);
  412. })
  413. ->field('goods_id,box_type,box_pic,goods_img,price,goods_name,goods_sn')
  414. ->order('box_type', 'desc')
  415. ->paginate($pageSize)
  416. ->toArray();
  417. if($data && $cache){
  418. RedisCache::set($cacheKey, $data, rand(5,10));
  419. }
  420. return $data;
  421. }
  422. }