| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- <?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\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));
- 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());
- }
- }
|