bztang-admin/app/frontend/modules/cart/models/CartGoods.php

699 lines
18 KiB
PHP

<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2021/4/6
* Time: 17:12
*/
namespace app\frontend\modules\cart\models;
use app\backend\modules\goods\models\GoodsTradeSet;
use app\common\exceptions\AppException;
use app\common\models\BaseModel;
use app\common\models\GoodsOption;
use app\common\models\Member;
use app\frontend\models\Goods;
use app\frontend\modules\cart\deduction\BaseCartDeduction;
use app\frontend\modules\cart\deduction\models\PreCartGoodsDeduction;
use app\frontend\modules\cart\discount\BaseCartDiscount;
use app\frontend\modules\cart\discount\EnoughReduceDiscount;
use app\frontend\modules\cart\discount\MemberLevelDiscount;
use app\frontend\modules\cart\discount\models\PreCartGoodsDiscount;
use app\frontend\modules\cart\discount\SingleEnoughReduceDiscount;
use app\frontend\modules\cart\extra\BaseCartExtraCharges;
use app\frontend\modules\cart\manager\GoodsAdapter;
use app\frontend\modules\cart\manager\GoodsOptionAdapter;
use app\frontend\modules\cart\node\CartGoodsBaseCartExtraChargesPriceNode;
use app\frontend\modules\cart\node\CartGoodsDeductionsPriceNode;
use app\frontend\modules\cart\node\CartGoodsDiscountPriceNode;
use app\frontend\modules\cart\node\CartGoodsPriceNodeBase;
use app\frontend\modules\cart\services\CartGoodsInterface;
use app\frontend\modules\order\PriceNode;
use app\frontend\modules\order\PriceNodeTrait;
use Illuminate\Support\Carbon;
/**
* Class CartGoods
* @package app\frontend\modules\cart\models
* @property memberCart memberCart
* @property Goods goods
* @property GoodsOption goodsOption
*
*/
class CartGoods extends BaseModel implements CartGoodsInterface
{
use PriceNodeTrait;
protected $appends = ['checked', 'disable'];
protected $hidden = ['memberCart', 'goods','goodsOption'];
public $shop;
protected $goodsAdapter;
protected $isChecked; //是否勾选
protected $isInvalid; //是否失效
protected $disable; //是否禁止选中
protected $estimated_price;
protected $price;
//节点法
public function _getPriceNodes()
{
// 商品价格节点
$nodes = collect([
new CartGoodsPriceNodeBase($this, 1000)
]);
//优惠的节点
$discountNodes = $this->getDiscounts()->map(function (BaseCartDiscount $discount) {
return new CartGoodsDiscountPriceNode($this, $discount, 2000);
});
//抵扣的节点
$deductionsNodes = $this->getDeductions()->map(function (BaseCartDeduction $deduction) {
return new CartGoodsDeductionsPriceNode($this, $deduction, 3000);
});
//附加费用节点
$extraChargesNodes = $this->getExtraCharges()->map(function (BaseCartExtraCharges $extraCharges) {
return new CartGoodsBaseCartExtraChargesPriceNode($this, $extraCharges, 4000);
});
// 按照weight排序
return $nodes->merge($discountNodes)
->merge($deductionsNodes)
->merge($extraChargesNodes)
->sortBy(function (PriceNode $priceNode) {
return $priceNode->getWeight();
})->values();
}
/**
* 优惠集合
* @return \Illuminate\Support\Collection
*/
public function getDiscounts()
{
$default = collect([
new MemberLevelDiscount($this), new SingleEnoughReduceDiscount($this), new EnoughReduceDiscount($this),
]);
// $default = collect([
// new SingleEnoughReduceDiscount($this), new EnoughReduceDiscount($this),
// ]);
$aggregate = $default->merge($this->setDiscounts());
return $aggregate;
}
public function setDiscounts()
{
return collect([]);
}
/**
* 抵扣集合
* @return \Illuminate\Support\Collection
*/
public function getDeductions()
{
$default = collect([]);
$aggregate = $default->merge($this->setDeductions());
return $aggregate;
}
public function setDeductions()
{
return collect([]);
}
/**
* 附加费用
* @return \Illuminate\Support\Collection
*/
public function getExtraCharges()
{
$default = collect([]);
$aggregate = $default->merge($this->setExtraCharges());
return $aggregate;
}
public function setExtraCharges()
{
return collect([]);
}
/*
* 加载购物车参数
*/
public function initialAttributes($data)
{
$this->setRawAttributes($data);
$this->beforeCreating();
}
public function beforeCreating()
{
}
public function invalidGoods()
{
$stock = $this->isOption() ? $this->goodsOption->stock : $this->goods->stock;
//商品下架 || 已删除 || 商品库存不足
$invalid = (empty($this->goods()->status) || $this->goods()->deleted_at || ($stock <= 0) || ($this->isOption() && !$this->goods()->has_option) || (!$this->isOption() && $this->goods()->has_option));
//权限判断
try {
if ($this->goods()->hasOnePrivilege) {
$this->goods()->hasOnePrivilege->validateMemberLevelLimit(Member::current());
$this->goods()->hasOnePrivilege->validateMemberGroupLimit(Member::current());
}
} catch (AppException $e) {
$invalid = true;
}
return $invalid;
}
/**
* 购物车商品是否失效
* @return bool
*/
public function isInvalid()
{
if (!isset($this->isInvalid)) {
$this->isInvalid = $this->invalidGoods();
}
return $this->isInvalid;
}
/**
* 验证商品
* @throws AppException
*/
public function goodsValidate()
{
//未勾选不验证
if (!$this->isChecked()) {
return true;
}
if (!isset($this->goods)) {
throw new AppException('(ID:' . $this->goods_id . ')未找到商品或已经删除');
}
//todo 验证商品是否启用规格
$this->goods->verifyOption($this->goods_option_id);
if (empty($this->goods->status)) {
throw new AppException($this->goods->title.'(ID:' . $this->goods->id . ')商品已下架');
}
if ($this->isOption()) {
$this->goodsOptionValidate();
}
}
/**
* 规格验证
* @throws AppException
*/
public function goodsOptionValidate()
{
if (!$this->goods->has_option) {
throw new AppException($this->goods->title.'(ID:' . $this->goods_id . ')商品未启用规格');
}
if (!isset($this->goodsOption)) {
throw new AppException($this->goods->title.'(ID:' . $this->goods_id . ')未找到规格或已经删除');
}
if ($this->goods_id != $this->goodsOption->goods_id) {
throw new AppException('规格('.$this->option_id.')'.$this->goodsOption->title.'不属于商品('.$this->goods_id.')'.$this->goods->title);
}
}
/**
* 注入分组类
* @param $shop
*/
public function setShop($shop)
{
$this->shop = $shop;
}
/**
* @return ShopCart
* @throws AppException
*/
final public function getShop()
{
if (!isset($this->shop)) {
throw new AppException('调用顺序错误,店铺对象还没有载入');
}
return $this->shop;
}
public function setDisable($bool)
{
$this->disable = $bool;
}
/**
* @return bool
*/
public function getDisableAttribute()
{
return $this->disable;
}
/**
* @return bool
* @throws AppException
*/
public function getCheckedAttribute()
{
return $this->isChecked();
}
/**
* 选择了此购物车
* @return bool
* @throws AppException
*/
public function isChecked()
{
if (!isset($this->isChecked)) {
if ($this->noBeChecked()) {
// 不能选中
$this->isChecked = false;
} else {
// 用户选中
$cart_ids = $this->getShop()->getRequest()->input('cart_ids');
if (!is_array($cart_ids)) {
//strpos($cart_ids, ',') !== false
//$cart_ids = json_decode($cart_ids, true);
$cart_ids = explode(',', $cart_ids);
}
$this->isChecked = in_array($this->cart_id, $cart_ids);
}
}
return $this->isChecked;
}
/**
* todo 这里暂时没用,因 isChecked 在 disable 设置之前调用
* 不能选中
* @return bool
*/
protected function noBeChecked()
{
return false;
}
/**
* 重构购物车参数
* 获取生成前的模型属性
* @return array
* @throws AppException
*/
public function getExtraField()
{
$attributes = array(
// 'cart_id' => $this->cart_id,
// 'goods_id' => $this->goods_id,
// 'total' => $this->total,
// 'goods_option_id' => $this->goods_option_id,
'is_alone' => $this->getShop()->isAlone(),
'shop_id' => $this->getShop()->getShopId(), //分组标识
'unit' => $this->getUnit(), //单位
'style_type' => $this->getStyleType(), //样式
'goods_title' => $this->goods()->title,
'vip_price' => $this->getVipPrice(),
'goods_thumb' => yz_tomedia($this->goods()->thumb),
'discount_activity' => $this->getDiscountActivity(),
'goods_price' => sprintf('%.2f', $this->getGoodsPrice()),
'price' => sprintf('%.2f', $this->getPrice()),
'estimated_price' => sprintf('%.2f', $this->getEstimatedPrice()), //预估价格
'month_buy_limit' => $this->getMonthBuyLimit(), //分类限购
'show_time_word' => $this->getArrivedTime(),
);
if ($this->goodsOption) {
$attributes += [
'goods_option_title' => $this->goodsOption->title,
];
if ($this->goodsOption['thumb']) {
$attributes['goods_thumb'] = yz_tomedia($this->goodsOption['thumb']);
}
}
$attributes = array_merge($this->getAttributes(), $attributes);
return $attributes;
}
private function getArrivedTime()
{
$goods_trade_set = GoodsTradeSet::where('goods_id', $this->goods_id)->first();
if (!$goods_trade_set || !$goods_trade_set->arrived_day || !app('plugins')->isEnabled('address-code')) {
return '';
}
$arrived_day = $goods_trade_set->arrived_day;
$arrived_word = $goods_trade_set->arrived_word;
if ($arrived_day > 1) {
$arrived_day -= 1;
$time_format = Carbon::createFromTimestamp(time())->addDays($arrived_day)->format('Y-m-d');
} else {
$time_format = Carbon::createFromTimestamp(time())->format('Y-m-d');
}
$time_format .= " {$goods_trade_set->arrived_time}:00";
$timestamp = strtotime($time_format);
if ($timestamp < time()) {
$timestamp += 86400;
}
$show_time = ltrim(date('m', $timestamp), '0').'月';
$show_time .= ltrim(date('d', $timestamp), '0').'日';
$show_time .= $goods_trade_set->arrived_time;
return str_replace('[送达时间]', $show_time, $arrived_word);
}
public function getUnit()
{
return '元';
}
public function getStyleType()
{
return 'shop';
}
/**
* 商品金额
* @return int
*/
public function getGoodsPrice()
{
return $this->getAdapter()->getPrice();
}
/**
* 商品支付金额
* @return mixed
*/
public function getPrice()
{
if (isset($this->price)) {
return $this->price;
}
//未选中不计算金额
if (!$this->isChecked()) {
return 0;
}
//商品单价 * 库存
$this->price = $this->getGoodsPrice() * $this->total;
return $this->price;
}
/**
* 商品预估金额
* @return mixed
* @throws AppException
*/
public function getEstimatedPrice()
{
if (isset($this->estimated_price)) {
return $this->estimated_price;
}
// //未选中不计算金额
if (!$this->isChecked()) {
return 0;
}
///商品支付金额 - 等级优惠金额 - 单品满减 - 全场满减
$this->estimated_price = $this->getPriceAfter($this->getPriceNodes()->last()->getKey());
//优惠劵
return $value = sprintf('%.2f', $this->estimated_price);
}
/**
* 商品的会员等级折扣金额
* @return float
* @throws AppException
*/
public function getVipDiscountAmount()
{
// $amount = $this->getAdapter()->_getVipDiscountAmount();
// $preCartGoodsDiscount = new PreCartGoodsDiscount([
// 'code' => 'memberLevel',
// 'amount' => $amount ?: 0,
// 'name' => '会员等级优惠',
// ]);
// $preCartGoodsDiscount->setCartGoods($this);
//
// return $amount;
return $this->getAdapter()->_getVipDiscountAmount() * $this->total;
}
/**
* 关联优惠抵扣
* @return mixed
*/
public function getCartGoodsDiscounts()
{
if (!$this->getRelation('cartGoodsDiscounts')) {
$this->setRelation('cartGoodsDiscounts', $this->newCollection());
}
return $this->cartGoodsDiscounts;
}
/**
* 抵扣
* @return mixed
*/
public function getCartGoodsDeductions()
{
if (!$this->getRelation('cartGoodsDeductions')) {
$this->setRelation('cartGoodsDeductions', $this->newCollection());
}
return $this->cartGoodsDeductions;
}
/**
* 额外费用
* @return mixed
*/
public function getCartGoodsExtraCharges()
{
if (!$this->getRelation('cartGoodsExtraCharges')) {
$this->setRelation('cartGoodsExtraCharges', $this->newCollection());
}
return $this->cartGoodsExtraCharges;
}
protected $isCoinExchange;
/**
* 是否满足全额抵扣判断
* @return bool
*/
protected function isCoinExchange()
{
//获取商城设置: 判断 积分、余额 是否有自定义名称
$shopSet = \Setting::get('shop.shop');
if (!isset($this->isCoinExchange)) {
if (!$this->goods()->hasOneSale->has_all_point_deduct) {
$this->isCoinExchange = false;
} else {
$this->isCoinExchange = true;
// 全额抵扣记录
$preModel = new PreCartGoodsDeduction([
'code' => 'pointAll',
'amount' => ($this->getGoodsPrice() * $this->total) ?: 0,
'name' => $shopSet['credit1'] ? $shopSet['credit1'] . '全额抵扣' : '积分全额抵扣',
]);
$preModel->setCartGoods($this);
}
}
return $this->isCoinExchange;
}
/**
* @return array
* @throws AppException
*/
public function toArray()
{
$this->setRawAttributes($this->getExtraField());
return parent::toArray();
}
//店铺商品活动优惠满减
public function getDiscountActivity()
{
//todo 这样可能要做成动态的
$data = [];
$sale = $this->goods()->hasOneSale;
if ($sale->ed_num || $sale->ed_money) {
$str = '';
if ($sale->ed_money) {
$str .= '满' . $sale->ed_money . '包邮';
}
if ($sale->ed_num) {
if (!empty($str)) {
$str .= ',';
}
$str .= '满' . $sale->ed_num . '件包邮';
}
$data[] = [
'name' => '包邮',
'code'=> 'freeSend',
'type'=> 'string',
'desc'=> $str,
];
}
if ((bccomp($sale->ed_full, 0.00, 2) == 1) && (bccomp($sale->ed_reduction, 0.00, 2) == 1)) {
$data[] = [
'name'=> '满减',
'code'=> 'goodsReduce',
'type'=> 'string',
'desc'=> '满'.$sale->ed_full.'减'.$sale->ed_reduction,
];
}
return $data;
}
/**
* 设置价格计算者
*/
public function _getAdapter()
{
if ($this->isOption()) {
$adapter = new GoodsOptionAdapter($this);
} else {
$adapter = new GoodsAdapter($this);
}
return $adapter;
}
/**
* 获取价格计算者
*/
public function getAdapter()
{
if (!isset($this->goodsAdapter)) {
$this->goodsAdapter = $this->_getAdapter();
}
return $this->goodsAdapter;
}
/**
* 是否为规格商品
* @return bool
*/
public function isOption()
{
return !empty($this->goods_option_id);
}
/**
* 商品模型
* @return Goods
*/
public function goods()
{
return $this->goods;
}
/**
* 插件类判断
* @param \app\common\models\Goods $goods
* @return bool
*/
public function verify()
{
return false;
}
public function getVipPrice()
{
if($this->isOption()){
return $this->goodsOption->vip_price;
}else{
return $this->goods()->vip_price;
}
}
private function getMonthBuyLimit()
{
if (!app('plugins')->isEnabled('month-buy-limit')) {
return [];
}
return \Yunshop\MonthBuyLimit\models\MonthLimitMember::getMemberLimit($this->goods()->id, \YunShop::app()->getMemberId());
}
}