123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- <?php
- // +----------------------------------------------------------------------
- // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2017~2024 https://www.yiovo.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
- // +----------------------------------------------------------------------
- // | Author: 萤火科技 <admin@yiovo.com>
- // +----------------------------------------------------------------------
- declare (strict_types=1);
- namespace app\api\service\coupon;
- use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
- use app\common\enum\coupon\CouponType as CouponTypeEnum;
- use app\common\service\BaseService;
- use app\common\library\helper;
- use cores\exception\BaseException;
- /**
- * 订单优惠券折扣服务类
- * Class GoodsDeduct
- * @package app\api\service\coupon
- */
- class GoodsDeduct extends BaseService
- {
- // 实际抵扣金额
- private int $actualReducedMoney;
- // 订单商品列表
- private iterable $goodsList = [];
- // 优惠券信息
- private array $couponInfo = [];
- // 实际参与优惠券折扣的商品记录
- private array $rangeGoodsList = [];
- // 设置订单商品列表
- public function setGoodsList(iterable $goodsList): GoodsDeduct
- {
- $this->goodsList = $goodsList;
- return $this;
- }
- // 设置优惠券信息
- public function setCouponInfo(array $couponInfo): GoodsDeduct
- {
- $this->couponInfo = $couponInfo;
- return $this;
- }
- /**
- * 计算优惠券抵扣金额
- * @return GoodsDeduct
- * @throws BaseException
- */
- public function setGoodsCouponMoney(): GoodsDeduct
- {
- // 验证当前类属性
- $this->checkAttribute();
- // 设置实际参与优惠券折扣的商品记录
- $this->setRangeGoodsList();
- // 计算实际抵扣的金额
- $this->setActualReducedMoney();
- // 实际抵扣金额为0
- if ($this->actualReducedMoney > 0) {
- // 计算商品的价格权重
- $this->setGoodsListWeight();
- // 计算商品优惠券抵扣金额
- $this->setGoodsListCouponMoney();
- // 总抵扣金额 (已分配的)
- $assignedCouponMoney = (int)helper::getArrayColumnSum($this->rangeGoodsList, 'coupon_money');
- // 分配剩余的抵扣金额
- $this->setGoodsListCouponMoneyFill($assignedCouponMoney);
- $this->setGoodsListCouponMoneyDiff($assignedCouponMoney);
- }
- return $this;
- }
- // 获取实际参与优惠券折扣的商品记录
- public function getRangeGoodsList(): array
- {
- return $this->rangeGoodsList;
- }
- /**
- * 设置实际参与优惠券折扣的商品记录
- * @return void
- */
- private function setRangeGoodsList()
- {
- $this->rangeGoodsList = [];
- foreach ($this->goodsList as $goods) {
- $goods['total_price'] *= 100;
- $goodsKey = "{$goods['goods_id']}-{$goods['goods_sku_id']}";
- switch ($this->couponInfo['apply_range']) {
- case ApplyRangeEnum::ALL:
- $this->rangeGoodsList[$goodsKey] = $goods;
- break;
- case ApplyRangeEnum::SOME:
- if (in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['applyGoodsIds'])) {
- $this->rangeGoodsList[$goodsKey] = $goods;
- }
- break;
- case ApplyRangeEnum::EXCLUDE:
- if (!in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['excludedGoodsIds'])) {
- $this->rangeGoodsList[$goodsKey] = $goods;
- }
- break;
- default:
- break;
- }
- }
- }
- /**
- * 验证当前类属性
- * @throws BaseException
- */
- private function checkAttribute()
- {
- if (empty($this->goodsList) || empty($this->couponInfo)) {
- throwError('goodsList or couponInfo not found.');
- }
- }
- public function getActualReducedMoney(): int
- {
- return $this->actualReducedMoney;
- }
- /**
- * 计算实际抵扣的金额
- */
- private function setActualReducedMoney()
- {
- // 获取当前订单商品总额
- $orderTotalPrice = $this->getOrderTotalPrice();
- // 计算最大抵扣金额
- $reducedMoney = 0;
- foreach ($this->rangeGoodsList as $goods) {
- $reducedMoney += $goods['total_price'];
- }
- // 计算打折金额
- if ($this->couponInfo['coupon_type'] == CouponTypeEnum::DISCOUNT) {
- $reducePrice = $reducedMoney - ($reducedMoney * ($this->couponInfo['discount'] / 10));
- } else {
- $reducePrice = $this->couponInfo['reduce_price'] * 100;
- }
- // 优惠券最大允许抵扣到一分钱,所以此处判断抵扣金额大于等于订单金额时,减去一分钱
- $this->actualReducedMoney = ($reducePrice >= $orderTotalPrice) ? $orderTotalPrice - 1 : (int)$reducePrice;
- }
- /**
- * 获取当前订单商品总额
- * @return int
- */
- private function getOrderTotalPrice(): int
- {
- $orderTotalPrice = 0;
- foreach ($this->goodsList as $goods) {
- $orderTotalPrice += (int)($goods['total_price'] * 100);
- }
- return $orderTotalPrice;
- }
- /**
- * 计算商品抵扣的权重(占比)
- */
- private function setGoodsListWeight()
- {
- $orderTotalPrice = helper::getArrayColumnSum($this->rangeGoodsList, 'total_price');
- foreach ($this->rangeGoodsList as &$goods) {
- $weight = round($goods['total_price'] / $orderTotalPrice, 6);
- $goods['weight'] = helper::scToStr((string)$weight, 6);
- }
- array_sort($this->rangeGoodsList, 'weight', true);
- }
- /**
- * 计算商品抵扣的金额
- */
- private function setGoodsListCouponMoney(): void
- {
- foreach ($this->rangeGoodsList as &$goods) {
- $goods['coupon_money'] = helper::bcmul($this->actualReducedMoney, $goods['weight'], 0);
- }
- }
- private function setGoodsListCouponMoneyFill(int $assignedCouponMoney): void
- {
- if ($assignedCouponMoney === 0) {
- $temReducedMoney = $this->actualReducedMoney;
- foreach ($this->rangeGoodsList as &$goods) {
- if ($temReducedMoney == 0) break;
- $goods['coupon_money'] = 1;
- $temReducedMoney--;
- }
- }
- }
- private function setGoodsListCouponMoneyDiff(int $assignedCouponMoney): void
- {
- // 剩余未抵扣金额 = 实际抵扣金额 - 总抵扣金额 (已分配的)
- $tempDiff = $this->actualReducedMoney - $assignedCouponMoney;
- foreach ($this->rangeGoodsList as &$goods) {
- if ($tempDiff < 1) break;
- // 当抵扣金额大于商品总价时不再累积抵扣
- if ($goods['coupon_money'] >= $goods['total_price']) continue;
- $goods['coupon_money']++;
- $tempDiff--;
- }
- }
- }
|