Coupon.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * Author: 芸众商城 www.yunzshop.com
  5. * Date: 2017/3/28
  6. * Time: 下午1:48
  7. */
  8. namespace app\frontend\modules\coupon\services\models;
  9. use app\common\helpers\ArrayHelper;
  10. use app\common\helpers\Serializer;
  11. use app\common\models\MemberCoupon;
  12. use app\common\models\Coupon as DbCoupon;
  13. use app\frontend\models\order\PreOrderCoupon;
  14. use app\frontend\modules\coupon\services\MemberCouponService;
  15. use app\frontend\modules\coupon\services\models\Price\CouponPrice;
  16. use app\frontend\modules\coupon\services\models\Price\DiscountCouponPrice;
  17. use app\frontend\modules\coupon\services\models\Price\ExchangeCouponPrice;
  18. use app\frontend\modules\coupon\services\models\Price\MoneyOffCouponPrice;
  19. use app\frontend\modules\coupon\services\models\TimeLimit\DateTimeRange;
  20. use app\frontend\modules\coupon\services\models\TimeLimit\SinceReceive;
  21. use app\frontend\modules\coupon\services\models\TimeLimit\TimeLimit;
  22. use app\frontend\modules\coupon\services\models\UseScope\CategoryScope;
  23. use app\frontend\modules\coupon\services\models\UseScope\CouponUseScope;
  24. use app\frontend\modules\coupon\services\models\UseScope\GoodsScope;
  25. use app\frontend\modules\coupon\services\models\UseScope\ShopScope;
  26. use app\frontend\modules\order\models\PreOrder;
  27. use phpDocumentor\Reflection\Types\Null_;
  28. class Coupon
  29. {
  30. /**
  31. * @var CouponPrice
  32. */
  33. private $price;
  34. /**
  35. * @var CouponUseScope
  36. */
  37. private $useScope;
  38. /**
  39. * @var TimeLimit
  40. */
  41. private $timeLimit;
  42. /**
  43. * @var PreOrder
  44. */
  45. private $order;
  46. /**
  47. * @var \app\common\models\MemberCoupon
  48. */
  49. private $memberCoupon;
  50. public function __construct(MemberCoupon $memberCoupon, PreOrder $order)
  51. {
  52. $this->memberCoupon = $memberCoupon;
  53. $this->order = $order;
  54. $this->price = $this->getPriceInstance(); //价格计算类
  55. $this->useScope = $this->getUseScopeInstance(); //订单商品集合类
  56. $this->timeLimit = $this->getTimeLimitInstance(); //有效期计算类
  57. }
  58. public function getPreOrder()
  59. {
  60. return $this->order;
  61. }
  62. /**
  63. * @return MemberCoupon
  64. */
  65. public function getMemberCoupon()
  66. {
  67. return $this->memberCoupon;
  68. }
  69. /**
  70. * 金额类的实例
  71. */
  72. private function getPriceInstance()
  73. {
  74. switch ($this->memberCoupon->belongsToCoupon->coupon_method) {
  75. case DbCoupon::COUPON_MONEY_OFF:
  76. return new MoneyOffCouponPrice($this);
  77. break;
  78. case DbCoupon::COUPON_DISCOUNT:
  79. return new DiscountCouponPrice($this);
  80. break;
  81. default:
  82. // if (config('app.debug')) {
  83. // dd($this->memberCoupon->belongsToCoupon->coupon_method);
  84. // dd($this->memberCoupon);
  85. // throw new AppException('优惠券优惠类型不存在');
  86. // }
  87. return null;
  88. break;
  89. }
  90. }
  91. /**
  92. * 使用范围类的实例,从config中读取值与类的对应关系,以便插件扩展
  93. */
  94. private function getUseScopeInstance()
  95. {
  96. $item = array_first(\app\common\modules\shop\ShopConfig::current()->get('shop-foundation.coupon.OrderCoupon.scope'), function ($item) {
  97. if($this->memberCoupon->belongsToCoupon->use_type == 8){
  98. return $item['key'] == 2;
  99. }
  100. return $item['key'] == $this->memberCoupon->belongsToCoupon->use_type;
  101. }, null);
  102. if (isset($item) && $item['class'] instanceof \Closure) {
  103. return call_user_func($item['class'], $this);
  104. }
  105. return null;
  106. }
  107. /**
  108. * 时间限制类实例
  109. */
  110. private function getTimeLimitInstance()
  111. {
  112. switch ($this->memberCoupon->belongsToCoupon->time_limit) {
  113. case DbCoupon::COUPON_DATE_TIME_RANGE:
  114. return new DateTimeRange($this);
  115. break;
  116. case DbCoupon::COUPON_SINCE_RECEIVE:
  117. return new SinceReceive($this);
  118. break;
  119. default:
  120. // if (config('app.debug')) {
  121. // dd($this->memberCoupon->belongsToCoupon);
  122. // throw new AppException('时限类型不存在');
  123. // }
  124. return null;
  125. break;
  126. }
  127. }
  128. public function getExpiredAt(){
  129. return $this->getTimeLimitInstance()->expiredTime();
  130. }
  131. /**
  132. * 获取订单优惠价格
  133. */
  134. public function getDiscountAmount()
  135. {
  136. $this->setOrderGoodsDiscountPrice();
  137. return $this->price->getPrice();
  138. }
  139. /**
  140. * 激活优惠券
  141. */
  142. public function activate()
  143. {
  144. if ($this->getMemberCoupon()->selected) {
  145. return;
  146. }
  147. //记录优惠券被选中了
  148. $this->getMemberCoupon()->selected = 1;
  149. $this->getMemberCoupon()->used = 1;
  150. $this->getMemberCoupon()->use_time = time();
  151. // todo 订单优惠券使用记录暂时加在这里,优惠券部分需要重构
  152. $preOrderCoupon = new PreOrderCoupon([
  153. 'coupon_id' => $this->memberCoupon->coupon_id,
  154. 'member_coupon_id' => $this->memberCoupon->id,
  155. 'name' => $this->memberCoupon->belongsToCoupon->name,
  156. 'amount' => $this->getDiscountAmount()
  157. ]);
  158. $preOrderCoupon->setRelation('memberCoupon', $this->memberCoupon);
  159. $preOrderCoupon->coupon = $this;
  160. $preOrderCoupon->setOrder($this->order);
  161. }
  162. /**
  163. * 分配优惠金额 todo 需理清与订单商品类之间的调用关系
  164. */
  165. private function setOrderGoodsDiscountPrice()
  166. {
  167. $this->price->setOrderGoodsDiscountPrice();
  168. }
  169. /**
  170. * 获取范围内的订单商品
  171. */
  172. public function getOrderGoodsInScope()
  173. {
  174. return $this->useScope->getOrderGoodsInScope();
  175. }
  176. /**
  177. * 优惠券可使用
  178. * @return bool
  179. */
  180. public function valid()
  181. {
  182. if (!$this->isOptional()) {
  183. return false;
  184. }
  185. if (!$this->unique()) {
  186. return false;
  187. }
  188. if (!$this->price->valid()) {
  189. return false;
  190. }
  191. return true;
  192. }
  193. /**
  194. * 用户优惠券所属的优惠券未被选中过
  195. * @return bool
  196. */
  197. public function unique()
  198. {
  199. //允许多张使用
  200. if ($this->getMemberCoupon()->belongsToCoupon->is_complex) {
  201. return true;
  202. }
  203. $memberCoupons = MemberCouponService::getCurrentMemberCouponCache($this->getPreOrder()->belongsToMember);
  204. //本优惠券与某个选中的优惠券是一张 就返回false
  205. return !$memberCoupons->contains(function ($memberCoupon) {
  206. if ($memberCoupon->selected == true) {
  207. //本优惠券与选中的优惠券是一张
  208. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '同一单不能使用多张此类型优惠券');
  209. return $memberCoupon->coupon_id == $this->getMemberCoupon()->coupon_id;
  210. }
  211. return false;
  212. });
  213. }
  214. /**
  215. * 优惠券已选中
  216. * @return bool
  217. */
  218. public function isChecked()
  219. {
  220. if ($this->getMemberCoupon()->selected == 1) {
  221. return true;
  222. }
  223. return false;
  224. }
  225. /**
  226. * 优惠券可选
  227. * @return bool
  228. */
  229. public function isOptional()
  230. {
  231. //检测优惠券是否被删除
  232. if($this->memberCoupon->is_member_deleted == 1){
  233. return false;
  234. }
  235. if (!isset($this->useScope)) {
  236. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '范围设置无效');
  237. return false;
  238. }
  239. if (!isset($this->price)) {
  240. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '满减类型设置无效');
  241. return false;
  242. }
  243. if (!isset($this->timeLimit)) {
  244. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '时限类型设置无效');
  245. return false;
  246. }
  247. //满足范围
  248. if (!$this->useScope->valid()) {
  249. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '不满足范围');
  250. return false;
  251. }
  252. //满足额度
  253. if (!$this->price->isOptional()) {
  254. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '不满足额度');
  255. return false;
  256. }
  257. //满足时限
  258. if (!$this->timeLimit->valid()) {
  259. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '不满足时限');
  260. return false;
  261. }
  262. //未使用
  263. if ($this->getMemberCoupon()->used) {
  264. trace_log()->coupon("优惠券{$this->getMemberCoupon()->id}", '已使用');
  265. return false;
  266. }
  267. return true;
  268. }
  269. }