UserCoupon.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types = 1);
  12. namespace app\api\model;
  13. use app\api\service\User as UserService;
  14. use app\common\library\helper;
  15. use app\common\model\UserCoupon as UserCouponModel;
  16. use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
  17. /**
  18. * 用户优惠券模型
  19. * Class UserCoupon
  20. * @package app\api\model
  21. */
  22. class UserCoupon extends UserCouponModel
  23. {
  24. /**
  25. * 获取用户优惠券列表
  26. * @param int $userId
  27. * @param array $param
  28. * @return \think\Paginator
  29. * @throws \think\db\exception\DbException
  30. */
  31. public function getList(int $userId, array $param)
  32. {
  33. $filter = $this->getFilter($param);
  34. return $this->where($filter)
  35. ->where('user_id', '=', $userId)
  36. ->paginate();
  37. }
  38. /**
  39. * 检索查询条件
  40. * @param array $param
  41. * @return array|mixed
  42. */
  43. private function getFilter(array $param = [])
  44. {
  45. // 设置默认查询参数
  46. $params = $this->setQueryDefaultValue($param, [
  47. 'dataType' => 'all', // all:全部 isUsable:可用的 isExpire:已过期 isUse:已使用
  48. 'amount' => null, // 订单消费金额
  49. ]);
  50. // 检索列表类型
  51. $filter = [];
  52. // 可用的优惠券
  53. if ($params['dataType'] === 'isUsable') {
  54. $filter[] = ['is_use', '=', 0];
  55. $filter[] = ['is_expire', '=', 0];
  56. }
  57. // 已过期的优惠券
  58. if ($params['dataType'] === 'isExpire') {
  59. $filter[] = ['is_expire', '=', 1];
  60. }
  61. // 已使用的优惠券
  62. if ($params['dataType'] === 'isUse') {
  63. $filter[] = ['is_use', '=', 1];
  64. }
  65. // 订单消费金额
  66. $params['amount'] > 0 && $filter[] = ['min_price', '<=', $params['amount']];
  67. return $filter;
  68. }
  69. /**
  70. * 获取用户优惠券总数量(可用)
  71. * @param int $userId
  72. * @return int
  73. */
  74. public function getCount(int $userId)
  75. {
  76. return $this->where('user_id', '=', $userId)
  77. ->where('is_use', '=', 0)
  78. ->where('is_expire', '=', 0)
  79. ->count();
  80. }
  81. /**
  82. * 获取用户优惠券ID集
  83. * @param $userId
  84. * @return array
  85. */
  86. public function getUserCouponIds(int $userId)
  87. {
  88. return $this->where('user_id', '=', $userId)->column('coupon_id');
  89. }
  90. /**
  91. * 领取优惠券
  92. * @param int $couponId 优惠券ID
  93. * @return bool
  94. * @throws \app\common\exception\BaseException
  95. */
  96. public function receive(int $couponId)
  97. {
  98. // 当前用户信息
  99. $userInfo = UserService::getCurrentLoginUser(true);
  100. // 获取优惠券信息
  101. $coupon = Coupon::detail($couponId);
  102. // 验证优惠券是否可领取
  103. if (!$this->checkReceive($userInfo, $coupon)) {
  104. return false;
  105. }
  106. // 添加领取记录
  107. return $this->add($userInfo, $coupon);
  108. }
  109. /**
  110. * 添加领取记录
  111. * @param $user
  112. * @param Coupon $coupon
  113. * @return bool
  114. */
  115. private function add($user, $coupon)
  116. {
  117. // 计算有效期
  118. if ($coupon['expire_type'] == 10) {
  119. $start_time = time();
  120. $end_time = $start_time + ($coupon['expire_day'] * 86400);
  121. } else {
  122. $start_time = $coupon['start_time'];
  123. $end_time = $coupon['end_time'];
  124. }
  125. // 整理领取记录
  126. $data = [
  127. 'coupon_id' => $coupon['coupon_id'],
  128. 'name' => $coupon['name'],
  129. 'coupon_type' => $coupon['coupon_type'],
  130. 'reduce_price' => $coupon['reduce_price'],
  131. 'discount' => $coupon->getData('discount'),
  132. 'min_price' => $coupon['min_price'],
  133. 'expire_type' => $coupon['expire_type'],
  134. 'expire_day' => $coupon['expire_day'],
  135. 'start_time' => $start_time,
  136. 'end_time' => $end_time,
  137. 'apply_range' => $coupon['apply_range'],
  138. 'apply_range_config' => $coupon['apply_range_config'],
  139. 'user_id' => $user['user_id'],
  140. 'store_id' => self::$storeId
  141. ];
  142. return $this->transaction(function () use ($data, $coupon) {
  143. // 添加领取记录
  144. $status = $this->save($data);
  145. if ($status) {
  146. // 更新优惠券领取数量
  147. $coupon->setIncReceiveNum();
  148. }
  149. return $status;
  150. });
  151. }
  152. /**
  153. * 验证优惠券是否可领取
  154. * @param $user
  155. * @param Coupon $coupon
  156. * @return bool
  157. */
  158. private function checkReceive($user, Coupon $coupon)
  159. {
  160. if (!$coupon) {
  161. $this->error = '优惠券不存在';
  162. return false;
  163. }
  164. if (!$coupon->checkReceive()) {
  165. $this->error = $coupon->getError();
  166. return false;
  167. }
  168. // 验证是否已领取
  169. $userCouponIds = $this->getUserCouponIds($user['user_id']);
  170. if (in_array($coupon['coupon_id'], $userCouponIds)) {
  171. $this->error = '该优惠券已领取';
  172. return false;
  173. }
  174. return true;
  175. }
  176. /**
  177. * 订单结算优惠券列表
  178. * @param int $userId 用户id
  179. * @param float $orderPayPrice 订单商品总金额
  180. * @return array|mixed
  181. * @throws \think\db\exception\DbException
  182. */
  183. public static function getUserCouponList(int $userId, float $orderPayPrice)
  184. {
  185. // 获取用户可用的优惠券列表
  186. $list = (new static)->getList($userId, ['dataType' => 'isUsable', 'amount' => $orderPayPrice]);
  187. $data = [];
  188. foreach ($list as $coupon) {
  189. // 有效期范围内
  190. if ($coupon['start_time'] > time()) continue;
  191. $key = $coupon['user_coupon_id'];
  192. $data[$key] = [
  193. 'user_coupon_id' => $coupon['user_coupon_id'],
  194. 'name' => $coupon['name'],
  195. 'coupon_type' => $coupon['coupon_type'],
  196. 'reduce_price' => $coupon['reduce_price'],
  197. 'discount' => $coupon['discount'],
  198. 'min_price' => $coupon['min_price'],
  199. 'expire_type' => $coupon['expire_type'],
  200. 'start_time' => $coupon['start_time'],
  201. 'end_time' => $coupon['end_time'],
  202. 'apply_range' => $coupon['apply_range'],
  203. 'apply_range_config' => $coupon['apply_range_config']
  204. ];
  205. // 计算打折金额
  206. if ($coupon['coupon_type'] == 20) {
  207. $reducePrice = helper::bcmul($orderPayPrice, $coupon['discount'] / 10);
  208. $data[$key]['reduced_price'] = helper::bcsub($orderPayPrice, $reducePrice);
  209. } else
  210. $data[$key]['reduced_price'] = $coupon['reduce_price'];
  211. }
  212. // 根据折扣金额排序并返回
  213. return array_sort($data, 'reduced_price', true);
  214. }
  215. /**
  216. * 判断当前优惠券是否满足订单使用条件
  217. * @param $couponList
  218. * @param $orderGoodsIds
  219. * @return mixed
  220. */
  221. public static function couponListApplyRange(array $couponList, array $orderGoodsIds)
  222. {
  223. // 名词解释(is_apply):允许用于抵扣当前订单
  224. foreach ($couponList as &$item) {
  225. if ($item['apply_range'] == 10) {
  226. // 1. 全部商品
  227. $item['is_apply'] = true;
  228. } elseif ($item['apply_range'] == 20) {
  229. // 2. 指定商品, 判断订单商品是否存在可用
  230. $applyGoodsIds = array_intersect($item['apply_range_config']['applyGoodsIds'], $orderGoodsIds);
  231. $item['is_apply'] = !empty($applyGoodsIds);
  232. } elseif ($item['apply_range'] == 30) {
  233. // 2. 排除商品, 判断订单商品是否全部都在排除行列
  234. $excludedGoodsIds = array_intersect($item['apply_range_config']['excludedGoodsIds'], $orderGoodsIds);
  235. $item['is_apply'] = count($excludedGoodsIds) != count($orderGoodsIds);
  236. }
  237. !$item['is_apply'] && $item['not_apply_info'] = '该优惠券不支持当前商品';
  238. }
  239. return $couponList;
  240. }
  241. }