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.

389 lines
13 KiB

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