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.

387 lines
13 KiB

5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\Service;
  3. use Hyperf\Di\Annotation\Inject;
  4. use Hyperf\DbConnection\Db;
  5. use App\Model\CouponUserRecType;
  6. use App\Model\Coupon;
  7. use App\Model\CouponRec;
  8. use App\Model\CouponUserUse;
  9. use Hyperf\Utils\ApplicationContext;
  10. use App\TaskWorker\SSDBTask;
  11. use App\Constants\SsdbKeysPrefix;
  12. use App\Constants\LogLabel;
  13. use App\Commons\Log;
  14. use Exception;
  15. use App\Service\CommonService;
  16. use Hyperf\Redis\Redis;
  17. class CouponService implements CouponServiceInterface
  18. {
  19. /**
  20. * @Inject
  21. * @var Log
  22. */
  23. protected $log;
  24. /**
  25. * @Inject
  26. * @var CommonService
  27. */
  28. protected $commonService;
  29. /**
  30. * 获取用户可领取优惠卷接口
  31. */
  32. public function getSystemCouponUserList($userId,$receiveType)
  33. {
  34. /* 优惠券活动标志 2 */
  35. $ssdb = ApplicationContext::getContainer()->get(SSDBTask::class);
  36. $couponActivity = $ssdb->exec('hgetall', SsdbKeysPrefix::COUPON_REBATE_ACTIVITY);
  37. $activityType = $couponActivity === false ? 0 : $couponActivity['activity'];
  38. $result = [
  39. 'active_type' => 1,
  40. 'not_receive' => [],
  41. 'jump_data' => [
  42. 'src' => "/zh_cjdianc/pages/couponrebate/index?activity_type=".$activityType,
  43. 'src2' => "/zh_cjdianc/pages/couponrebate/index?activity_type=".$activityType,
  44. 'share_bg' => env('OSS_IMG_HOST').'/static/img/coupon_share.png',
  45. 'receive_bg' => env('OSS_IMG_HOST').'/static/img/coupon_bg.png',
  46. 'coupons' => []
  47. ]
  48. ];
  49. $nowTime = time();
  50. $c_ids = [];
  51. $whereC = [
  52. ['end_time','>',$nowTime],
  53. ['start_time','<=',$nowTime],
  54. ['status','=',1]
  55. ];
  56. // 渠道开启,查询该渠道可以领取的优惠券ID
  57. // 渠道未开启,查询所有优惠券
  58. if (env('SUB_CHANNEL') == 1) {
  59. $c_ids = CouponUserRecType::where('receive_type', $receiveType)->where($whereC)->pluck('system_coupon_user_id');
  60. } else {
  61. $c_ids = Coupon::where($whereC)->pluck('id');
  62. }
  63. $couponReceive = CouponRec::where('user_id',$userId);
  64. // 渠道开启,查询该用户在此渠道领过的优惠券ID
  65. if (env('SUB_CHANNEL') == 1) {
  66. $couponReceive->where('receive_type', $receiveType);
  67. }
  68. $cr_ids = $couponReceive->pluck('system_coupon_user_id');
  69. // 可领取的券ID
  70. $c_ids = $c_ids->toArray();
  71. // 已经领取的券ID
  72. $cr_ids = $cr_ids->toArray();
  73. // 当前用户可领的优惠券ID
  74. $couponIds = array_diff($c_ids, $cr_ids);
  75. // 转发型优惠券
  76. $couponReceiveIds = ($couponActivity === false || $this->commonService->empty($couponActivity['forward']) )? [] : explode(',',$couponActivity['forward']);
  77. // 所有优惠券
  78. $couponIds = array_merge($couponIds,$couponReceiveIds);
  79. $whereC = [
  80. ['u.end_time','>',$nowTime],
  81. ['u.start_time','<=',$nowTime],
  82. ['u.status','=',1]
  83. ];
  84. // 查询领取型1 和 转发型2
  85. $whereActiveType = [1,2];
  86. if (env('SUB_CHANNEL') == 1) {
  87. array_push($whereC, ['type.receive_type','=', $receiveType]);
  88. }
  89. $coupons = Db::table('ims_system_coupon_user as u')
  90. ->join('ims_system_coupon_user_receivetype as type', 'u.id', '=', 'type.system_coupon_user_id')
  91. ->whereIn('u.id', $couponIds)
  92. ->whereIn('u.active_type', $whereActiveType)
  93. ->where($whereC)
  94. ->whereRaw('u.inventory_use < u.inventory and u.inventory-u.inventory_use >= type.one_receive_number')
  95. ->select('u.*','type.one_receive_number')
  96. ->orderBy('u.weigh','desc')
  97. ->get();
  98. foreach ($coupons as $k => &$v){
  99. if($v->active_type == 1 && count($result['not_receive']) < 4){
  100. $result['not_receive'][] = $v;
  101. }else if($v->active_type == 2 && in_array($v->id,$couponReceiveIds)){
  102. $result['jump_data']['coupons'][] = $v->id;
  103. }
  104. if($v->discount_type == 2){
  105. $v->discounts = floatval($v->discounts);
  106. }
  107. }
  108. $result['active_type'] = count($result['jump_data']['coupons']) > 0 ? 2 : $result['active_type'] ;
  109. return $result;
  110. }
  111. //统计用户
  112. public function userCouponAccount()
  113. {
  114. }
  115. /**
  116. * 用户领取优惠卷
  117. */
  118. public function userReceiveCoupon()
  119. {
  120. }
  121. /**
  122. * 获取用户已经领取的优惠卷列表
  123. */
  124. public function getUserReceiveCouponList()
  125. {
  126. }
  127. /**
  128. * 获取用户当前订单可用的优惠券列表
  129. * 按分类(1订单 等优惠)分组返回
  130. */
  131. public function getUserAvailableCoupons($orderAmount,$userId,$marketId,$type,$storetypeId)
  132. {
  133. $storetypeIds = explode(',', str_replace(',', ',', $storetypeId));
  134. $available = [];
  135. $notAvailable = [];
  136. if ($this->empty($orderAmount) || $this->empty($userId)) {
  137. return $this->success([
  138. 'available' => $available,
  139. 'not_available' => array_values($notAvailable)
  140. ]);
  141. }
  142. // 获取用户优惠券
  143. $currentTime = time();
  144. $data = Db::table('ims_system_coupon_user_receive as receive')
  145. ->select([
  146. 'receive.id as receive_id',
  147. 'receive.user_id',
  148. 'receive.number_remain',
  149. 'coupon.id',
  150. 'coupon.title',
  151. 'coupon.full_amount',
  152. 'coupon.discounts',
  153. 'coupon.usable_start_time',
  154. 'coupon.usable_end_time',
  155. 'coupon.discount_type'
  156. ])
  157. ->join('ims_system_coupon_user as coupon', 'coupon.id', '=', 'receive.system_coupon_user_id')
  158. ->where(['receive.user_id' => $userId])
  159. ->whereIn('receive.status', [0,1])
  160. ->where('receive.number_remain', '>', 0)
  161. ->whereIn('coupon.type', [1,$type])
  162. ->where('coupon.full_amount', '<=', $orderAmount)
  163. ->where('coupon.usable_start_time', '<=', $currentTime)
  164. ->where('coupon.usable_end_time', '>=', $currentTime)
  165. ->where('coupon.usable_number', '<=', Db::raw('receive.number_remain'))
  166. ->where('coupon.market_id', 'in', [0, $marketId])
  167. ->whereIn('coupon.storetype_id', $storetypeIds)
  168. ->orderByRaw('coupon.discounts DESC, coupon.full_amount DESC')
  169. ->get();
  170. // 分离用户今天用过的优惠券种类
  171. $container = ApplicationContext::getContainer();
  172. $redis = $container->get(Redis::class);
  173. $couponIds = $redis->sMembers('coupon_'.date('Ymd').'_used_'.$userId);
  174. foreach ($data as $key => &$item) {
  175. if (in_array($item->id, $couponIds)) {
  176. $notAvailable[$item->id] = $item;
  177. } else {
  178. $available[] = $item;
  179. }
  180. }
  181. return [
  182. 'available' => $available,
  183. 'not_available' => array_values($notAvailable)
  184. ];
  185. }
  186. /**
  187. * @inheritDoc
  188. */
  189. public function getOrderCanUseCoupons($orderAmount, $marketId, $userId, $fields=[], $type = 1, $storeTypeIds = [0])
  190. {
  191. // 用户今日使用过的优惠券
  192. $redis = ApplicationContext::getContainer()->get(Redis::class);
  193. $couponTodayUsedIds = $redis->sMembers('coupon_'.date('Ymd').'_used_'.$userId);
  194. $currentTime = time();
  195. $builder = Db::table('ims_system_coupon_user_receive as receive')
  196. ->join('ims_system_coupon_user as coupon', 'coupon.id', '=', 'receive.system_coupon_user_id', 'inner');
  197. if (is_array($fields)&&!empty($fields)) {
  198. $builder->select($fields);
  199. }
  200. if (is_array($couponTodayUsedIds)&&!empty($couponTodayUsedIds)) {
  201. $builder->whereNotIn('coupon.id', $couponTodayUsedIds);
  202. }
  203. return $builder->where(['receive.user_id' => $userId])
  204. ->whereIn('receive.status', [0,1])
  205. ->where('receive.number_remain', '>', 0)
  206. ->whereIn('coupon.type', [1,$type])
  207. ->where('coupon.full_amount', '<=', $orderAmount)
  208. ->where('coupon.usable_start_time', '<=', $currentTime)
  209. ->where('coupon.usable_end_time', '>=', $currentTime)
  210. ->where('coupon.usable_number', '<=', Db::raw('receive.number_remain'))
  211. ->where('coupon.market_id', 'in', [0, $marketId])
  212. ->whereIn('coupon.storetype_id', $storeTypeIds)
  213. ->orderByRaw('coupon.discounts DESC, coupon.full_amount DESC')
  214. ->get()
  215. ->toArray();
  216. }
  217. /**
  218. * 缓存优惠券今日使用情况
  219. * @param $userId
  220. * @param $couponId
  221. * @param $couponRecId
  222. * @return bool
  223. */
  224. function cacheTodayCouponUsed($userId, $couponId, $couponRecId)
  225. {
  226. $redis = ApplicationContext::getContainer()->get(Redis::class);
  227. $setRes = $redis->sAdd(
  228. 'coupon_'.date('Ymd').'_used_'.$userId,
  229. $couponId
  230. );
  231. $expireRes = $redis->expire(
  232. 'coupon_'.date('Ymd').'_used_'.$userId,
  233. strtotime(date('Y-m-d').' 23:59:59')-time()
  234. );
  235. return $setRes&&$expireRes;
  236. }
  237. /**
  238. * 取消订单返券
  239. * @param $order_id
  240. * @return bool
  241. */
  242. public function refundOrderCoupons($order_id){
  243. $coupon = CouponUserUse::where([
  244. ['order_main_id','=',$order_id],
  245. ['status','=',CouponUserUse::COUPON_USE_STATE_USED],
  246. ])
  247. ->select('id','user_receive_id','number')
  248. ->first();
  249. if (empty($coupon)) {
  250. return '';
  251. }
  252. // 返回用户优惠券数量并更新状态
  253. $res = Db::update("UPDATE ims_system_coupon_user_receive SET number_remain=number_remain+{$coupon->number}, status=IF(number=number_remain,0,1), update_time=".time().""
  254. ." WHERE id={$coupon->user_receive_id} AND number>=(number_remain+{$coupon->number})");
  255. // 更新使用记录状态为已退回
  256. CouponUserUse::where([
  257. ['id','=',$coupon->id],
  258. ['status','=',CouponUserUse::COUPON_USE_STATE_USED],
  259. ])
  260. ->update([
  261. 'status' => CouponUserUse::COUPON_USE_STATE_CANCEL,
  262. 'return_time' => time(),
  263. 'update_time' => time(),
  264. ]);
  265. //删除当日 redis 使用记录缓存
  266. $order_main = OrderMain::where('id',$order_id)
  267. ->select('global_order_id','user_id')
  268. ->first();
  269. $redis = ApplicationContext::getContainer()->get(Redis::class);
  270. $remRes = $redis->sRem(
  271. 'coupon_'.date('Ymd').'_used_'.$order_main->user_id,
  272. $coupon->system_coupon_id
  273. );
  274. return $res;
  275. }
  276. /* 删除-优惠券今日使用的缓存
  277. * @param $userId
  278. * @param $couponId
  279. * @return bool
  280. */
  281. public function clearTodayCouponUsed($userId, $couponId)
  282. {
  283. $redis = ApplicationContext::getContainer()->get(Redis::class);
  284. $res = $redis->sRem(
  285. 'coupon_'.date('Ymd').'_used_'.$userId,
  286. $couponId
  287. );
  288. return $res;
  289. }
  290. /**
  291. * 退款返还优惠券
  292. * 先查询是否正常使用优惠券
  293. * 修改状态,退还领取记录库存,删除ssdb缓存
  294. */
  295. public function orderRefundCoupons($global_order_id)
  296. {
  297. $time = time();
  298. Db::beginTransaction();
  299. try {
  300. $couponUses = CouponUserUse::query()
  301. ->select('id','system_coupon_id','user_id','number','user_receive_id')
  302. ->where('global_order_id',$global_order_id)
  303. ->where('status',CouponUserUse::COUPON_USE_STATE_USED)
  304. ->select();
  305. if(!empty($couponUse)){
  306. foreach($couponUses as $use){
  307. $use->status = CouponUserUse::COUPON_USE_STATE_USED;
  308. $use->return_time = $time;
  309. $use->update_time = $time;
  310. $res = $use->save();
  311. $couponReceive = CouponRec::query()
  312. ->where('id',$use->user_receive_id)
  313. ->whereRaw('number >= number_remain+'.$use->number)
  314. ->update([
  315. 'number_remain' => Db::raw('number_remain+'.$use->number),
  316. 'status' => Db::raw('IF(number=number_remain,' . CouponRec::STATE_UNUSED . ',' . CouponRec::STATE_SOME . ')'),
  317. 'update_time' => $time
  318. ]);
  319. $clearUseRedis = $this->clearTodayCouponUsed($use->user_id,$use->system_coupon_id);
  320. }
  321. }
  322. Db::commit();
  323. return true;
  324. } catch (Exception $e) {
  325. $this->log->event(LogLabel::ORDER_LOG, ['msg'=> '订单退款','exception' => $e->getMessage()]);
  326. Db::rollBack();
  327. return false;
  328. }
  329. return true;
  330. }
  331. }