CouponMarketing.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. <?php
  2. namespace common\logic\order\marketing;
  3. use addons\Coupon\AddonConfig;
  4. use addons\Coupon\common\enums\AddonsCouponEnum;
  5. use addons\Coupon\common\models\AddonsCoupon;
  6. use addons\Coupon\common\models\AddonsCouponUserRelate;
  7. use common\enums\AddonsEnum;
  8. use common\enums\StatusEnum;
  9. use common\models\goods\GoodsCateRelate;
  10. use common\models\mall\MallAddons;
  11. use Exception;
  12. use yii\helpers\Json;
  13. /**
  14. * 优惠券
  15. * @Author bing
  16. * @DateTime 2021-09-27 19:07:32
  17. * @copyright: Copyright (c) 2020 广东七件事集团
  18. */
  19. class CouponMarketing extends BaseMarketing
  20. {
  21. public function execute($form)
  22. {
  23. }
  24. public static function compute($item, &$form, &$extra = [])
  25. {
  26. try {
  27. $group_order_goods = $form['group_order_goods'];
  28. $mall_id = $form['mall_id'];
  29. $data = is_array($form->data) ? $form->data : Json::decode($form->data, true);
  30. if (empty($data['user_coupon_ids'])) return $item;
  31. $time = $_SERVER['REQUEST_TIME'];
  32. if (empty($extra['coupon'])) {
  33. $user_coupon_id = $data['user_coupon_ids'];
  34. $params = ['where' => [['id' => $user_coupon_id]], 'with' => ['coupon' => function ($query) {
  35. $query->where(['status' => [StatusEnum::ENABLED,StatusEnum::DISABLED]])->with(['goodsRelate', 'cateRelate', 'memberRelate']);
  36. }]];
  37. $user_relate_list = AddonsCouponUserRelate::lists($params, true);
  38. // 优惠券叠加使用
  39. self::superpositionUse($user_relate_list);
  40. // 验证优惠券是否能使用
  41. AddonsCoupon::checkIsAllowUse($user_relate_list, $form);
  42. $is_install = MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_COUPON);
  43. $coupon_data['user_relate_list'] = $user_relate_list;
  44. $coupon_data['user_coupon_id'] = $user_coupon_id;
  45. $coupon_data['is_install'] = $is_install;
  46. $extra['coupon'] = $coupon_data;
  47. $goods_ids = $goods_arr = $cate_arr = [];
  48. foreach ($item['goods'] as $v) {
  49. $goods_ids[] = $v['goods_id'];
  50. $goods_arr[$v['goods_id']]['mch_id'] = $v['goods_details'][0]['mch_id'];
  51. }
  52. $gcrl = GoodsCateRelate::findAll(['goods_id' => $goods_ids]);
  53. $cate_id_arr = [];
  54. foreach ($gcrl as $v) {
  55. $goods_arr[$v['goods_id']]['cate_id'][]= $v['cate_id'];
  56. $cate_id_arr[] = $v['cate_id'];
  57. $cate_arr[$v['cate_id']]['mch_id'] = $goods_arr[$v['goods_id']]['mch_id'];
  58. }
  59. $extra['cate_arr'] = $cate_arr;
  60. $extra['goods_arr'] = $goods_arr;
  61. $extra['cate_id_arr'] = $cate_id_arr;
  62. foreach ($coupon_data['user_relate_list'] as &$info) {
  63. if (!empty($info['is_use'])) throw new Exception('优惠券【' . $info['coupon']['name'] . '】已被使用');
  64. if (empty($info['coupon'])) throw new Exception('优惠券id:【' . $info['coupon_id'] . '】不存在');
  65. $end_time = $info['end_time'];
  66. $start_time = $info['start_time'];
  67. if ($time < $start_time) throw new Exception('优惠券【' . $info['coupon']['name'] . '】还没生效');
  68. if ($time > $end_time) throw new Exception('优惠券【' . $info['coupon']['name'] . '】已过期');
  69. $coupon = $info['coupon'];
  70. if (!AddonsCoupon::checkCouponSourceIsFromSharing($info)) {
  71. throw new Exception('当前优惠券只能转赠之后才能使用');
  72. }
  73. $info['total_goods_price'] = 0;
  74. $info['have_sub_price'] = 0;
  75. $goods_id_arr = array_column($coupon['goodsRelate'], 'goods_id');//该优惠券关联的商品id
  76. $cate_id_arr = array_column($coupon['cateRelate'], 'cate_id');//该优惠券关联的商品分类id
  77. foreach ($item['goods'] as $row) {
  78. //指定分类
  79. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE1) {
  80. $cate_id = $goods_arr[$row['goods_id']]['cate_id'];
  81. if (!empty($info['mch_id'])) {
  82. $goods_arr = $extra['goods_arr'];
  83. if ($info['mch_id'] != $cate_arr[$cate_id]['mch_id']) continue;
  84. }
  85. if (!array_intersect($cate_id, $cate_id_arr)) continue;
  86. $info['total_goods_price'] = bcadd($info['total_goods_price'], $row['goods_price'], 2);
  87. }
  88. //指定商品
  89. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE2) {
  90. if (!empty($info['mch_id'])) {
  91. $goods_arr = $extra['goods_arr'];
  92. if ($info['mch_id'] != $goods_arr[$row['goods_id']]['mch_id']) continue;
  93. }
  94. if (!in_array($row['goods_id'], $goods_id_arr)) continue;
  95. $info['total_goods_price'] = bcadd($info['total_goods_price'], $row['goods_price'], 2);
  96. }
  97. //全场通用
  98. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE3) {
  99. $info['total_goods_price'] = bcadd($info['total_goods_price'], $row['goods_price'], 2);
  100. }
  101. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE5) {
  102. if (!empty($info['coupon']['mch_id'])) {
  103. $mch_ids = explode(',',$info['coupon']['mch_id']);
  104. $goods_arr = $extra['goods_arr'];
  105. if ( !in_array($goods_arr[$row['goods_id']]['mch_id'],$mch_ids)) continue;
  106. }
  107. $info['total_goods_price'] = bcadd($info['total_goods_price'], $row['goods_price'], 2);
  108. }
  109. }
  110. }
  111. } else {
  112. $coupon_data = $extra['coupon'];
  113. }
  114. if (empty($extra['total_deduct_money'])) {
  115. $total_deduct_money = 0;
  116. $extra['total_deduct_money'] = $total_deduct_money;
  117. } else {
  118. $total_deduct_money = $extra['total_deduct_money'];
  119. }
  120. $coupon_arr = [];
  121. $originalGoodsPriceArr = [];
  122. $useCouponNum = count($coupon_data['user_relate_list']);
  123. foreach ($coupon_data['user_relate_list'] as $k => &$v) {
  124. if (!empty($v['is_use'])) throw new Exception('优惠券【' . $v['coupon']['name'] . '】已被使用');
  125. if (empty($v['coupon'])) throw new Exception('优惠券id:【' . $v['coupon_id'] . '】不存在');
  126. $begin_at = $v['coupon']['begin_at'];
  127. if($v['coupon']['expire_type']==AddonsCouponEnum::EXPIRE_TYPE1){
  128. $begin_at = $v['created_at'];
  129. }
  130. $end_at = $v['end_time'];
  131. if ($time < $begin_at) throw new Exception('优惠券【' . $v['coupon']['name'] . '】还没生效');
  132. if ($time > $end_at) throw new Exception('优惠券【' . $v['coupon']['name'] . '】已过期');
  133. $coupon_arr[$v['coupon_id']] = $v['coupon'];
  134. $coupon = $v['coupon'];
  135. $cate_id_arr = array_column($coupon['cateRelate'], 'cate_id');
  136. $goods_id_arr = array_column($coupon['goodsRelate'], 'goods_id');
  137. $cate_arr = $extra['cate_arr'];
  138. $goods_arr = $extra['goods_arr'];
  139. $total_coupon_money = bcadd($total_coupon_money ?? 0, $v['coupon']['sub_price'], 2);
  140. $goodsKey = 0;
  141. foreach ($item['goods'] as &$row) {
  142. if (!isset($originalGoodsPriceArr[$row['goods_id']])) {
  143. $originalGoodsPriceArr[$row['goods_id']] = $row['goods_price'];
  144. }
  145. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE1) {
  146. $cate_id = $goods_arr[$row['goods_id']]['cate_id'];
  147. if (!empty($v['mch_id']) && $v['mch_id'] != $cate_arr[$cate_id]['mch_id']) continue;
  148. if (!($cate_id && $cate_id_arr)) continue;
  149. if (!array_intersect($cate_id, $cate_id_arr)) continue;
  150. }
  151. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE2) {
  152. if (!empty($v['mch_id']) && $v['mch_id'] != $goods_arr[$row['goods_id']]['mch_id']) continue;
  153. if (!in_array($row['goods_id'], $goods_id_arr)) continue;
  154. }
  155. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE3) {
  156. if (!empty($v['mch_id']) && $v['mch_id'] != $goods_arr[$row['goods_id']]['mch_id']) continue;
  157. }
  158. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE6) {
  159. continue;
  160. }
  161. $old_goods_price = $row['goods_price'];
  162. $goods_price = self::deduct(array_merge($row, ['original_goods_price_arr' => $originalGoodsPriceArr, 'goods_key' => $goodsKey, 'use_coupon_num' => $useCouponNum, 'use_coupon_index' => $k]), $v);
  163. $deduct_money = static::_floorNum($row['goods_price'] - $goods_price, 2);
  164. $total_deduct_money = bcadd($total_deduct_money, $deduct_money, 2);
  165. $row['goods_price'] = $goods_price;
  166. $sum = $temp_sum = 0;
  167. foreach ($row['goods_details'] as &$goods_detail) {
  168. if($row['goods_price']>0){
  169. $deduct_money_single = bcdiv(bcmul($goods_detail['goods_price'], $deduct_money, 2), $old_goods_price, 2); // 优惠按售价占比
  170. }else{
  171. $deduct_money_single = $goods_detail['goods_price'];
  172. }
  173. $temp_sum = bcadd($temp_sum, $deduct_money_single, 2);
  174. if ($temp_sum > $deduct_money) {
  175. $deduct_money_single = bcsub($deduct_money, $sum, 2);
  176. } else {
  177. $sum = bcadd($sum, $deduct_money_single, 2);
  178. }
  179. if (isset($goods_detail['marketing']['pay_money'])) {
  180. $pay_money = bcsub(($goods_detail['marketing']['pay_money'] ?? 0), $deduct_money_single, 2);
  181. } else {
  182. $pay_money = bcsub(($goods_detail['goods_price'] ?? 0), $deduct_money_single, 2);
  183. }
  184. $goods_detail['goods_price'] = bcsub(($goods_detail['goods_price'] ?? 0), $deduct_money_single, 2);
  185. $goods_detail['coupon'][] = ['user_coupon_id' => $v['id'], 'pay_money' => $pay_money, 'coupon_discount_money' => static::_floorNum($deduct_money_single, 2)];
  186. }
  187. $goodsKey++;
  188. }
  189. }
  190. foreach ($item['goods'] as &$row) {
  191. $coupons = [];
  192. $coupon_discount_money = 0;
  193. $pay_money = 0;
  194. foreach ($row['goods_details'] as &$goods_detail) {
  195. if (!empty($goods_detail['coupon'])) {
  196. array_map(function ($item) use (&$coupons, &$pay_money, &$coupon_discount_money) {
  197. $coupons[$item['user_coupon_id']] = $item['coupon_discount_money'];
  198. $pay_money = $item['pay_money'];
  199. $coupon_discount_money += $item['coupon_discount_money'];
  200. }, $goods_detail['coupon']);
  201. unset($goods_detail['coupon']);
  202. $goods_detail['marketing']['user_coupon_id'] = json_encode($coupons);
  203. $goods_detail['marketing']['pay_money'] = $pay_money;
  204. $goods_detail['marketing']['coupon_discount_money'] = $coupon_discount_money;
  205. }
  206. }
  207. }
  208. // 适应满减券
  209. if (self::_isFullMinus($user_relate_list ?? [])) {
  210. $total_deduct_money = ($total_coupon_money == $total_deduct_money) ? $total_deduct_money : $total_coupon_money;
  211. }
  212. $item['mch_order_info']['goods_price'] = bcsub(($item['mch_order_info']['goods_price'] ?? 0), $total_deduct_money, 2) ;
  213. $item['mch_order_info']['coupon_money'] = $total_deduct_money;
  214. $form['group_order_goods'] = $group_order_goods;
  215. $form['total_pay_money'] = bcsub(($form['total_pay_money'] ?? 0), $total_deduct_money, 2);
  216. if (!empty($total_deduct_money)) $form->marketing['deduct']['coupon'] = ['name' => '优惠券', 'money' => -static::_floorNum($total_deduct_money, 2)];
  217. return $item;
  218. } catch (Exception $e) {
  219. CouponMarketing::setStaticError($e->getMessage());
  220. return false;
  221. }
  222. }
  223. /**
  224. * 优惠后的金额
  225. * @Author: hua
  226. * @DateTime: 2021/10/15 16:46
  227. * @Copyright: copyright (c) 2021 广东七件事集团
  228. * @param $row
  229. * @param $v
  230. * @return float|int|string
  231. * @throws Exception
  232. */
  233. public static function deduct($row, &$v)
  234. {
  235. $goods_price = $row['goods_price'];
  236. $coupon = $v['coupon'];
  237. if(!empty($coupon['is_original_use'])){
  238. $price=0;
  239. foreach ($row['goods_details'] as $goods_detail) {
  240. $price+=$goods_detail['price']*$goods_detail['num'];
  241. }
  242. if($row['goods_price']!=$price) throw new Exception('原价购买['.$row['goods_name'].']才能使用优惠券:'.$coupon['name']);
  243. }
  244. //最低消费
  245. if (!empty($coupon['min_price']) && $v['total_goods_price'] < $coupon['min_price']) throw new Exception('没有达到最低消费');
  246. //折扣
  247. if ($coupon['type'] == AddonsCouponEnum::DISCOUNT) {
  248. $goods_price = sprintf('%.2f', $row['goods_price'] * $coupon['discount'] / 10); //折扣
  249. $deduct_money = $row['goods_price'] - $goods_price;
  250. if ($coupon['discount_limit'] != '0.00' && $deduct_money > $coupon['discount_limit']) {
  251. $goods_price = bcsub($row['goods_price'], $coupon['discount_limit'], 2);
  252. }
  253. }
  254. //满减
  255. if ($coupon['type'] == AddonsCouponEnum::FULL_MINUS) {
  256. $sub_price = $row['original_goods_price'] / $v['total_goods_price'] * $coupon['sub_price'];
  257. $v['coupon']['have_sub_price'] = isset($v['coupon']['have_sub_price']) ? $v['coupon']['have_sub_price'] : 0;
  258. if ($v['coupon']['have_sub_price'] + $sub_price > $coupon['sub_price']) {
  259. $sub_price = $coupon['sub_price'] - $v['coupon']['have_sub_price'];
  260. $v['coupon']['have_sub_price'] += $sub_price;
  261. } else {
  262. $v['coupon']['have_sub_price'] += $sub_price;
  263. }
  264. if (($row['goods_price'] - $sub_price) < 0) {
  265. $goods_price = 0;
  266. } else {
  267. $goods_price = bcsub($row['goods_price'], $sub_price, 2);
  268. }
  269. }
  270. // 兑换券
  271. if ($coupon['type'] == AddonsCouponEnum::EXCHANGE && isset($row['goods_key']) && $row['goods_key'] == 0) {
  272. $goods_price = bcsub($row['goods_price'], bcdiv($row['original_goods_price_arr'][$row['goods_id']], $row['num'], 2), 2);
  273. $goods_price = max($goods_price, 0);
  274. // 如果使用兑换券和下单的数量相等,则最终的优惠结果就应该是0,所以如果循环到最后一次优惠券计算但是金额仍不等于0,则直接等于0
  275. if ($row['num'] == $row['use_coupon_num'] && ($row['use_coupon_index'] + 1) == $row['use_coupon_num'] && $goods_price != 0) {
  276. $goods_price = 0;
  277. }
  278. }
  279. return static::_floorNum($goods_price, 2);
  280. }
  281. /**
  282. * 优惠券叠加使用
  283. *
  284. * @param array $user_coupon_list
  285. * @return void
  286. */
  287. public static function superpositionUse(array $user_coupon_list)
  288. {
  289. if (count($user_coupon_list) >= 2) {
  290. foreach ($user_coupon_list as $user_coupon) {
  291. // 优惠券属性
  292. $coupon = $user_coupon['coupon'];
  293. // 兑换券类型不需要检查
  294. if ($coupon['type'] == AddonsCouponEnum::EXCHANGE) {
  295. continue;
  296. }
  297. if ($coupon['is_superposition_use'] == 0) throw new Exception('选择优惠券中存在不支持叠加使用的优惠券');
  298. }
  299. }
  300. }
  301. /**
  302. * 获取
  303. *
  304. * @param string $number
  305. * @param integer $precision
  306. * @return string
  307. */
  308. private static function _floorNum(string $number, $precision = 2)
  309. {
  310. $pow = pow(10, 2);
  311. return bcdiv(bcmul($number, $pow), $pow, 2);
  312. }
  313. /**
  314. * 判断当前使用优惠券是否是满减券
  315. *
  316. * @param array $coupon_ids
  317. * @return boolean
  318. */
  319. private static function _isFullMinus(array $user_coupon_list)
  320. {
  321. $type = reset(array_column(array_column($user_coupon_list, 'coupon'), 'type'));
  322. return $type == AddonsCouponEnum::FULL_MINUS;
  323. }
  324. }