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.
yanzong/app/api/service/coupon/GoodsDeduct.php

216 lines
7.3 KiB

11 months ago
<?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--;
}
}
}