You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
11 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\Service\v3\Implementations;
  3. use App\Model\v3\Coupon;
  4. use App\Model\v3\CouponRec;
  5. use App\Model\v3\GoodsActivity;
  6. use App\Service\v3\Interfaces\CouponRecServiceInterface;
  7. use App\Service\v3\Interfaces\CouponServiceInterface;
  8. use App\Service\v3\Interfaces\ShopCartServiceInterface;
  9. use Hyperf\DbConnection\Db;
  10. use Hyperf\Redis\Redis;
  11. use Hyperf\Utils\ApplicationContext;
  12. use Hyperf\Di\Annotation\Inject;
  13. class CouponRecService implements CouponRecServiceInterface
  14. {
  15. /**
  16. * @Inject
  17. * @var ShopCartServiceInterface
  18. */
  19. protected $shopCartService;
  20. /**
  21. * @Inject
  22. * @var CouponServiceInterface
  23. */
  24. protected $couponService;
  25. public function do()
  26. {
  27. // TODO: Implement do() method.
  28. }
  29. public function check()
  30. {
  31. // TODO: Implement check() method.
  32. }
  33. public function undo()
  34. {
  35. // TODO: Implement undo() method.
  36. }
  37. /**
  38. * 获取当前订单可使用的优惠券
  39. * @param $totalAmount
  40. * @param $userId
  41. * @param $marketId
  42. * @param $type
  43. * @param $storeTypeIds
  44. * @return array
  45. */
  46. public function allForOrderOlAvailable($totalAmount, $userId, $marketId, $type, $storeTypeIds = [])
  47. {
  48. // 用户今日使用过的优惠券
  49. $redis = ApplicationContext::getContainer()->get(Redis::class);
  50. $couponTodayUsedIds = $redis->sMembers('coupon_'.date('Ymd').'_used_'.$userId);
  51. $currentTime = time();
  52. $builder = Db::table('lanzu_coupon_receive as receive')
  53. ->join('lanzu_coupon as coupon', 'coupon.id', '=', 'receive.coupon_id', 'inner');
  54. if (is_array($couponTodayUsedIds)&&!empty($couponTodayUsedIds)) {
  55. $builder->whereNotIn('coupon.id', $couponTodayUsedIds);
  56. }
  57. foreach ($storeTypeIds as $key => &$item) {
  58. $item = (string)$item;
  59. }
  60. if (!empty($storeTypeIds)) {
  61. $builder->whereJsonContains('coupon.category_ids', $storeTypeIds);
  62. }
  63. $builder->where(['receive.user_id' => $userId])
  64. ->whereIn('receive.status', [0,1])
  65. ->where('receive.number_remain', '>', 0)
  66. ->whereIn('coupon.type', [1,$type])
  67. ->where('coupon.full_amount', '<=', $totalAmount)
  68. ->where('coupon.usable_start_time', '<=', $currentTime)
  69. ->where('coupon.usable_end_time', '>=', $currentTime)
  70. ->where('coupon.usable_number', '<=', Db::raw('receive.number_remain'));
  71. if ($marketId) {
  72. $builder->whereJsonContains('coupon.market_ids', [(string)$marketId]);
  73. }
  74. return $builder->orderByRaw('coupon.discounts DESC, coupon.full_amount DESC')
  75. ->get()
  76. ->toArray();
  77. }
  78. /**
  79. * 用户优惠券列表
  80. * @param $userId
  81. * @param $type
  82. * @param int $page
  83. * @param int $pagesize
  84. * @return array
  85. */
  86. public function getListByUser($userId,$type,$page = 1,$pagesize = 5)
  87. {
  88. //查询优惠券
  89. $builder = CouponRec::query()->join('lanzu_coupon', 'lanzu_coupon.id', '=', 'lanzu_coupon_receive.coupon_id')->with('coupon')
  90. ->where([
  91. ['lanzu_coupon_receive.user_id' ,'=', $userId],
  92. ]);
  93. /**
  94. * $type unused 未使用 used 已使用 expired 已失效
  95. */
  96. switch ($type){
  97. case 'unused':
  98. $builder = $builder->where([
  99. ['lanzu_coupon.usable_end_time' ,'>', time()],
  100. ['lanzu_coupon_receive.number_remain' ,'>', 0]
  101. ])
  102. ->whereIn('lanzu_coupon_receive.status',[0,1]);
  103. break;
  104. case 'used':
  105. $builder = $builder->whereIn('lanzu_coupon_receive.status',[1,2]);
  106. break;
  107. case 'expired':
  108. $builder = $builder->where(function ($query) {
  109. $query->where('lanzu_coupon.usable_end_time', '<', time());
  110. });
  111. break;
  112. }
  113. $builder = $builder->select(
  114. 'lanzu_coupon_receive.*'
  115. )
  116. ->orderBy('lanzu_coupon.weigh','desc');
  117. $paginate = $builder->paginate($pagesize);
  118. $couponList = $paginate->toArray();
  119. return ['has_more_pages' => $paginate->hasMorePages(), 'list' => $couponList['data']];
  120. }
  121. /**
  122. * 获取用户当前线上订单可以使用的优惠券
  123. * 1、所有未过期额优惠券,并且满足订单的金额要求
  124. * 2、筛选出其中当日使用过的优惠券
  125. * 3、筛选出其中活动商品不可用的优惠券(订单中有活动商品且活动商品不可使用优惠券)
  126. * 4、筛选出其中活动商品可用但商品的活动类型不符合优惠券活动使用类型的要求(订单中有活动商品可以用优惠券,但是活动类型type和优惠券中的available活动类型不可用)
  127. * @param $userId
  128. * @param $marketId
  129. * @param $shopcartIds
  130. * @return array
  131. */
  132. public function allForOnlineOrderAvailable($userId, $marketId, $shopcartIds = [])
  133. {
  134. // 获取购物车数据
  135. $carts = $this->shopCartService->allForUser($userId, $marketId, $shopcartIds);
  136. $totalAmount = $carts['total'];
  137. // 获取购物车中商品和店铺的类别
  138. $storeCategoryIds = []; // 商户类型
  139. $goodsCategoryIds = []; // 商品类型
  140. $hasActivityGoodsCannotUse = false; // 购物车中是否有不可使用优惠券的活动商品
  141. $goodsActivityTypes = []; // 活动商品的类型集合
  142. foreach ($carts['store_lists'] as $key => &$cart) {
  143. // 商户类型
  144. if (isset($cart['store']['category_id']) && $cart['store']['category_id']) {
  145. array_push($storeCategoryIds, $cart['store']['category_id']);
  146. }
  147. if (isset($cart['shopping_cart']) && is_array($cart['shopping_cart'])) {
  148. foreach ($cart['shopping_cart'] as $key2 => &$goods) {
  149. // 商品类型
  150. if (isset($goods['goods']['category_id']) && $goods['goods']['category_id']) {
  151. array_push($goodsCategoryIds, $goods['goods']['category_id']);
  152. }
  153. // 活动商品不可使用优惠券的情况
  154. if ($goods['activity_type'] == 2 && $goods['goods']['can_use_coupon'] != 1) {
  155. $hasActivityGoodsCannotUse = true;
  156. }
  157. // 活动商品类型集合
  158. if ($goods['activity_type'] == 2) {
  159. array_merge($goodsActivityTypes, [$goods['goods']['type']]);
  160. }
  161. }
  162. }
  163. }
  164. $categoryIds = array_merge($storeCategoryIds, $goodsCategoryIds);
  165. $coupon = ApplicationContext::getContainer()->get(Coupon::class);
  166. $couponRec = ApplicationContext::getContainer()->get(CouponRec::class);
  167. $couponTable = $coupon->getTable();
  168. $receiveTable = $couponRec->getTable();
  169. $currentTime = time();
  170. // 获取用户已领取的优惠券,同时是在使用期内的优惠券
  171. $builder = $couponRec->with('coupon')
  172. ->select("{$receiveTable}.*")
  173. ->join($couponTable, "{$couponTable}.id", "=", "{$receiveTable}.coupon_id")
  174. ->where("{$receiveTable}.user_id", $userId)
  175. ->where("{$receiveTable}.number_remain", ">", 0)
  176. ->whereIn("{$receiveTable}.status", [0,1])
  177. ->where("{$couponTable}.usable_start_time", "<=", $currentTime)
  178. ->where("{$couponTable}.usable_end_time", ">=", $currentTime);
  179. if (!empty($marketId)) { # 市场限制
  180. $builder->where(function ($query) use ($marketId, $couponTable) {
  181. return $query->whereJsonContains("{$couponTable}.market_ids", [(string)$marketId])
  182. ->orWhereJsonLength("{$couponTable}.market_ids", '=', 0);
  183. });
  184. }
  185. if (!empty($categoryIds)) { # 分类限制
  186. $builder->where(function ($query) use ($categoryIds, $couponTable) {
  187. return $query->whereJsonContains("{$couponTable}.category_ids", $categoryIds)
  188. ->orWhereJsonLength("{$couponTable}.category_ids", '=', 0);
  189. });
  190. }
  191. $couponReceives = $builder->orderByRaw("{$couponTable}.usable_end_time ASC")->get();
  192. $available = [];
  193. $notAvailable = [];
  194. // 有活动商品不可用优惠券,全部都不可用了
  195. if ($hasActivityGoodsCannotUse) {
  196. $notAvailable = $couponReceives;
  197. foreach ($notAvailable as $key => &$item) {
  198. $item['not_available_reason'] = '活动不可用';
  199. }
  200. $couponReceives = [];
  201. }
  202. // 循环摘出失效不可用的优惠券
  203. foreach ($couponReceives as $key => &$item) {
  204. // 不满订单金额
  205. if ($item['coupon']['full_amount'] > $totalAmount) {
  206. $item['not_available_reason'] = '差'.bcsub($item['coupon']['full_amount'], $totalAmount, 2).'元';
  207. array_push($notAvailable, $item);
  208. continue;
  209. }
  210. // 今日已使用
  211. $todayUsedCouponIds = $this->couponService->allTodayCouponUsed($userId);
  212. if (in_array($item['coupon']['id'], $todayUsedCouponIds)) {
  213. $item['not_available_reason'] = '今日已使用';
  214. array_push($notAvailable, $item);
  215. continue;
  216. }
  217. // 活动商品可用,校验对应的优惠券可使用活动类型
  218. $intersects = array_intersect($goodsActivityTypes, (array)$item['activity_available']); # 所有活动商品的活动类型和优惠券的可用活动类型求交集
  219. $canUseByTypes = array_diff($goodsActivityTypes, $intersects); # 所有活动商品的活动类型和上述交集求差集,如果为空则是可以用的,否则说明前者中有后者不能用的活动类型
  220. if (!empty($canUseByTypes)) {
  221. $item['not_available_reason'] = '活动不可用';
  222. array_push($notAvailable, $item);
  223. continue;
  224. }
  225. $item['not_available_reason'] = '';
  226. array_push($available, $item);
  227. }
  228. return ['available' => $available, 'not_available' => $notAvailable];
  229. }
  230. /**
  231. * @param $userId
  232. * @return mixed
  233. */
  234. public function statistics($userId)
  235. {
  236. //未使用
  237. $res['unused'] = 0;
  238. //已使用
  239. $res['used'] = 0;
  240. //已过期
  241. $res['expired'] = 0;
  242. $coupons = CouponRec::query()
  243. ->with('coupon')
  244. ->where('user_id',$userId)
  245. ->get();
  246. foreach ($coupons as $coupon){
  247. if($coupon->coupon->usable_end_time < time()){
  248. $res['expired'] += $coupon->number_remain;
  249. }else{
  250. $res['unused'] += $coupon->number_remain;
  251. }
  252. $res['used'] += $coupon->used_num;
  253. }
  254. return $res;
  255. }
  256. }