SeckillLogic.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop100%开源免费商用商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | gitee下载:https://gitee.com/likeshop_gitee
  10. // | github下载:https://github.com/likeshop-github
  11. // | 访问官网:https://www.likeshop.cn
  12. // | 访问社区:https://home.likeshop.cn
  13. // | 访问手册:http://doc.likeshop.cn
  14. // | 微信公众号:likeshop技术社区
  15. // | likeshop团队 版权所有 拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshopTeam
  18. // +----------------------------------------------------------------------
  19. namespace app\shopapi\logic;
  20. use app\common\enum\OrderEnum;
  21. use app\common\enum\SeckillEnum;
  22. use app\common\model\Goods;
  23. use app\common\model\GoodsCollect;
  24. use app\common\model\GoodsItem;
  25. use app\common\model\GoodsServiceGuarantee;
  26. use app\common\model\Order;
  27. use app\common\model\OrderGoods;
  28. use app\common\model\SeckillGoods;
  29. use app\common\model\SeckillGoodsItem;
  30. use app\common\model\User;
  31. use app\common\model\UserAddress;
  32. use app\common\service\ConfigService;
  33. use app\common\service\FileService;
  34. use app\shopapi\service\CouponService;
  35. use Exception;
  36. use think\facade\Db;
  37. class SeckillLogic
  38. {
  39. /**
  40. * @notes 秒杀活动详细
  41. * @return array|string
  42. * @author 张无忌
  43. * @date 2021/7/27 18:27
  44. */
  45. public static function detail($params)
  46. {
  47. try {
  48. // 查询校验商品获取活动
  49. $seckillGoods = (new SeckillGoods())->alias('SG')
  50. ->field(['SG.*,SA.name,SA.min_buy,SA.max_buy',
  51. 'SA.is_coupon,SA.is_distribution,SA.start_time,SA.end_time,SG.browse_volume,SG.virtual_sales_num,SG.virtual_click_num'])
  52. ->join('seckill_activity SA', 'SA.id = SG.seckill_id')
  53. ->where('SA.start_time', '<', time())
  54. ->where('SA.end_time', '>=', time())
  55. ->where('SA.status', '>=', SeckillEnum::SECKILL_STATUS_CONDUCT)
  56. ->findOrEmpty($params['id'])->toArray();
  57. if (!$seckillGoods) {
  58. throw new Exception('当前秒杀活动已结束');
  59. }
  60. $seckillGoodsItem = (new SeckillGoodsItem())
  61. ->where(['seckill_gid' => $seckillGoods['id']])
  62. ->select()->toArray();
  63. $goods = (new Goods())
  64. ->field([
  65. 'id,name,code,image,video,video_cover,min_price',
  66. 'min_lineation_price,total_stock,sales_num,spec_type,content,unit_id,express_type,express_money,express_template_id,type,service_guarantee_ids'
  67. ])->with(['spec_value.spec_list', 'spec_value_list'])
  68. ->append(['goods_image'])
  69. ->findOrEmpty($seckillGoods['goods_id']);
  70. $goods['is_collect'] = 0;
  71. if($params['user_id']){
  72. $isCollect = GoodsCollect::where(['goods_id'=>$goods['id'],'user_id'=>$params['user_id']])->value('id');
  73. $goods['is_collect'] = $isCollect ? 1 : 0;
  74. }
  75. $goods['stock_show'] = ConfigService::get('goods_set', 'is_show', 0);
  76. foreach ($goods['spec_value_list'] as &$item) {
  77. $item['image'] = $item['image'] ? $item['image'] : $goods['image'];
  78. foreach ($seckillGoodsItem as $value) {
  79. if ($item['goods_id'] == $value['goods_id'] and $item['id'] == $value['item_id']) {
  80. $item['seckill_price'] = $value['seckill_price'];
  81. $item['sales_volume'] = $value['sales_volume'];
  82. unset($value);
  83. }
  84. }
  85. }
  86. $goods['unit_name'] = '';
  87. if($goods['unit_id']){
  88. $goods['unit_name'] = $goods->unit->name;
  89. }
  90. $goods['activity'] = [
  91. 'id' => $seckillGoods['seckill_id'],
  92. 'name' => $seckillGoods['name'],
  93. 'min_buy' => $seckillGoods['min_buy'],
  94. 'max_buy' => $seckillGoods['max_buy'],
  95. 'min_seckill_price' => $seckillGoods['min_seckill_price'],
  96. 'max_seckill_price' => $seckillGoods['max_seckill_price'],
  97. 'browse_volume' => $seckillGoods['browse_volume'] + $seckillGoods['virtual_click_num'],
  98. 'is_coupon' => $seckillGoods['is_coupon'],
  99. 'is_distribution' => $seckillGoods['is_distribution'],
  100. 'start_time' => $seckillGoods['start_time'],
  101. 'end_time' => $seckillGoods['end_time'],
  102. 'surplus_time' => $seckillGoods['end_time'] - time(),
  103. 'closing_order' => (new SeckillGoodsItem())->where(['seckill_gid'=>$seckillGoods['id']])->sum('closing_order'),
  104. 'sales_volume' => (new SeckillGoodsItem())->where(['seckill_gid'=>$seckillGoods['id']])->sum('sales_volume') + $seckillGoods['virtual_sales_num']
  105. ];
  106. //商品评价
  107. $goods['goods_comment'] = GoodsLogic::getComment($goods['id']);
  108. // 用户地址
  109. $address_id = input('address_id/d', 0);
  110. $address = $address_id ? UserAddress::getAddressById($params['user_id'], $address_id) : UserAddress::getDefaultAddress($params['user_id']);
  111. if ($address) {
  112. $goods->address = $address;
  113. }
  114. // 服务保障
  115. $goods->service_guarantee = GoodsServiceGuarantee::getApiList($goods->service_guarantee_ids);
  116. // 增加浏览记录
  117. SeckillGoods::update(['browse_volume'=> ['inc', 1]], ['id'=>$params['id']]);
  118. // 店铺推荐
  119. $goods['recommend'] = self::recommend();
  120. // 包邮信息
  121. if ($goods['express_type'] == 1) {
  122. $goods['free_shipping_tips'] = '免运费';
  123. } else {
  124. $goods['free_shipping_tips'] = GoodsLogic::getFreeShippingTips($params['user_id'], $goods);
  125. }
  126. return $goods->hidden(['unit','unit_id'])->toArray();
  127. } catch (Exception $e) {
  128. return $e->getMessage();
  129. }
  130. }
  131. /**
  132. * @notes 店铺推荐
  133. * @author Tab
  134. * @date 2021/12/10 14:41
  135. */
  136. public static function recommend()
  137. {
  138. $filed = [
  139. 'AG.id',
  140. 'AG.goods_id',
  141. 'AG.min_seckill_price',
  142. 'AG.goods_snap',
  143. ];
  144. $lists = SeckillGoods::alias('AG')
  145. ->leftJoin('seckill_activity SA', 'SA.id = AG.seckill_id')
  146. ->field($filed)
  147. ->where('SA.start_time', '<', time())
  148. ->where('SA.end_time', '>=', time())
  149. ->where('SA.status', '>=', SeckillEnum::SECKILL_STATUS_CONDUCT)
  150. ->order('AG.id', 'desc')
  151. ->limit(5)
  152. ->select()
  153. ->toArray();
  154. return $lists;
  155. }
  156. /**
  157. * @notes 结算信息
  158. * @param $params
  159. * @param $user_id
  160. * @return array|string
  161. * @author 张无忌
  162. * @date 2021/8/5 16:43
  163. */
  164. public static function settlement($params, $user_id)
  165. {
  166. try {
  167. // 获取用户收货地址
  168. $address = UserAddress::getOneAddress($user_id, $params['address_id'] ?? 0);
  169. // 获取下单拼团商品信息
  170. $seckillGoodsItem = (new SeckillGoodsItem())->alias('SGI')
  171. ->field('SGI.*,SG.goods_snap,SA.name,SA.min_buy,SA.max_buy,SA.is_coupon,SA.is_distribution')
  172. ->join('seckill_activity SA', 'SA.id = SGI.seckill_id')
  173. ->join('seckill_goods SG', 'SG.id = SGI.seckill_gid')
  174. ->where([
  175. ['SGI.seckill_id', '=', (int)$params['seckill_id']],
  176. ['SGI.goods_id', '=', (int)$params['goods']['goods_id']],
  177. ['SGI.item_id', '=', (int)$params['goods']['item_id']],
  178. ['SA.status', '=', SeckillEnum::SECKILL_STATUS_CONDUCT],
  179. ['SA.start_time', '<=', time()],
  180. ['SA.end_time', '>=', time()],
  181. ])->findOrEmpty()->toArray();
  182. if (!$seckillGoodsItem) {
  183. throw new Exception('该秒杀活动已结束了,下次再来吧');
  184. }
  185. if ($params['goods']['count'] < $seckillGoodsItem['min_buy']) {
  186. throw new Exception('下单数量不能少于' . $seckillGoodsItem['min_buy'] . '件');
  187. }
  188. if ($params['goods']['count'] > $seckillGoodsItem['min_buy']) {
  189. throw new Exception('下单数量不能大于' . $seckillGoodsItem['max_buy'] . '件');
  190. }
  191. // 获取用户信息
  192. $user = (new User())->findOrEmpty($user_id)->toArray();
  193. // 处理返回的数据
  194. $goodsSnap = json_decode($seckillGoodsItem['goods_snap'], true);
  195. $itemSnap = json_decode($seckillGoodsItem['item_snap'], true);
  196. $orderStatus = [
  197. 'found_id' => $params['found_id'] ?? null,
  198. 'order_type' => OrderEnum::TEAM_ORDER,
  199. 'pay_way' => intval($params['pay_way'] ?? 1),
  200. 'coupon_id' => intval($params['coupon_list_id'] ?? 0),
  201. 'remark' => $params['remark'] ?? '',
  202. 'express_price' => 0,
  203. 'discount_amount' => 0,
  204. 'total_count' => intval($params['goods']['count']),
  205. 'total_amount' => round($seckillGoodsItem['seckill_price'] * $params['goods']['count'], 2),
  206. 'order_amount' => round($seckillGoodsItem['seckill_price'] * $params['goods']['count'], 2),
  207. 'user_money' => $user['user_money'],
  208. 'user_integral' => $user['user_integral'],
  209. 'address' => $address,
  210. 'activity' => [
  211. 'id' => $seckillGoodsItem['seckill_id'],
  212. 'name' => $seckillGoodsItem['name'],
  213. 'min_buy' => $seckillGoodsItem['min_buy'],
  214. 'max_buy' => $seckillGoodsItem['max_buy'],
  215. 'sell_price' => $seckillGoodsItem['sell_price'],
  216. 'seckill_price' => $seckillGoodsItem['seckill_price'],
  217. 'is_coupon' => $seckillGoodsItem['is_coupon'],
  218. 'is_distribution' => $seckillGoodsItem['is_distribution']
  219. ],
  220. 'goods' => [
  221. 'id' => intval($params['goods']['goods_id']),
  222. 'item_id' => intval($params['goods']['item_id']),
  223. 'spec_value_ids' => $itemSnap['spec_value_ids'],
  224. 'name' => $goodsSnap['name'],
  225. 'image' => $itemSnap['image'] ? FileService::getFileUrl($itemSnap['image']) : FileService::getFileUrl($goodsSnap['image']),
  226. 'spec_value_str' => $itemSnap['spec_value_str'],
  227. 'cost_price' => $seckillGoodsItem['sell_price'],
  228. 'sell_price' => $seckillGoodsItem['seckill_price'],
  229. 'total_price' => round($seckillGoodsItem['seckill_price'] * $params['goods']['count'], 2),
  230. 'count' => intval($params['goods']['count']),
  231. 'goods_snap' => $goodsSnap,
  232. ]
  233. ];
  234. // 处理是否使用优惠券
  235. if ($orderStatus['coupon_id'] and $orderStatus['activity']['is_coupon']) {
  236. $couponStatus = CouponService::isUsable($user_id, $params['coupon_id'], $orderStatus);
  237. if ($couponStatus['isUsable']) {
  238. $orderStatus['order_amount'] -= $couponStatus['money'];
  239. $orderStatus['discount_amount'] += $couponStatus['money'];
  240. }
  241. }
  242. return $orderStatus;
  243. } catch (Exception $e) {
  244. return $e->getMessage();
  245. }
  246. }
  247. /**
  248. * @notes 下单
  249. * @param $params
  250. * @param $user_id
  251. * @return array|string
  252. * @throws Exception
  253. * @author 张无忌
  254. * @date 2021/8/5 16:43
  255. */
  256. public static function buy($params, $user_id)
  257. {
  258. Db::startTrans();
  259. try {
  260. // 验证收货地址
  261. if (empty($params['address']) || !$params['address']) {
  262. throw new Exception('请选择收货地址');
  263. }
  264. // 验证并扣减库存
  265. $goodsItem = (new GoodsItem())
  266. ->where(['id' => $params['goods']['item_id']])
  267. ->where(['goods_id' => $params['goods']['id']])
  268. ->findOrEmpty()->toArray();
  269. if ($goodsItem['stock'] - $params['total_count'] < 0) {
  270. throw new Exception('抱歉,库存不足了');
  271. }
  272. (new GoodsItem())
  273. ->where(['id' => $params['goods']['item_id']])
  274. ->where(['goods_id' => $params['goods']['id']])
  275. ->update(['stock' => ['dec', $params['total_count']]]);
  276. // 订单逻辑: 创建订单
  277. $order = self::addOrder($params, $user_id);
  278. // 提交记录
  279. Db::commit();
  280. return [
  281. 'type' => 'order',
  282. 'order_id' => $order['id']
  283. ];
  284. } catch (Exception $e) {
  285. Db::rollback();
  286. return $e->getMessage();
  287. }
  288. }
  289. /**
  290. * @notes 添加订单
  291. * @param $params
  292. * @param $user_id
  293. * @return Order|\think\Model
  294. * @author 张无忌
  295. * @date 2021/8/4 10:56
  296. */
  297. public static function addOrder($params, $user_id)
  298. {
  299. // 创建订单记录
  300. $order = Order::create([
  301. 'sn' => generate_sn((new Order()), 'sn'),
  302. 'order_type' => $params['order_type'],
  303. 'user_id' => $user_id,
  304. 'order_terminal' => 0,
  305. 'pay_way' => $params['pay_way'],
  306. 'total_num' => $params['total_count'],
  307. 'total_amount' => $params['total_amount'],
  308. 'goods_price' => $params['total_amount'],
  309. 'order_amount' => $params['order_amount'],
  310. 'express_price' => $params['express_price'],
  311. 'discount_amount' => $params['discount_amount'],
  312. 'user_remark' => $params['remark'],
  313. 'address' => [
  314. 'contact' => $params['address']['contact'],
  315. 'province' => $params['address']['province_id'],
  316. 'city' => $params['address']['city_id'],
  317. 'district' => $params['address']['district_id'],
  318. 'address' => $params['address']['address'],
  319. 'mobile' => $params['address']['mobile'],
  320. ],
  321. ]);
  322. // 创建订单商品记录
  323. OrderGoods::create([
  324. 'order_id' => $order['id'],
  325. 'goods_id' => $params['goods']['id'],
  326. 'item_id' => $params['goods']['item_id'],
  327. 'goods_name' => $params['goods']['name'],
  328. 'goods_num' => $params['goods']['count'],
  329. 'goods_price' => $params['goods']['sell_price'],
  330. 'member_price' => $params['goods']['sell_price'],
  331. 'original_price' => $params['goods']['sell_price'],
  332. 'total_price' => $params['goods']['total_price'],
  333. 'total_pay_price' => $params['order_amount'],
  334. 'discount_price' => $params['discount_amount'],
  335. 'integral_price' => 0,
  336. 'spec_value_ids' => $params['goods']['spec_value_ids'],
  337. 'goods_snap' => $params['goods']['goods_snap']
  338. ]);
  339. return $order;
  340. }
  341. }