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.
216 lines
7.3 KiB
216 lines
7.3 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2017~2023 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--;
|
|
}
|
|
}
|
|
} |