// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\api\model; use app\api\model\card\RiceCard; use app\api\model\card\UserRiceCard; use app\api\model\Coupon as CouponModel; use app\api\model\ActivityDiscountGoods as ActivityDiscountGoodsModel; use app\api\model\ActivityDiscount as ActivityDiscountModel; use app\api\model\fullsend\FullSendActivity; use app\api\model\member\MemberGoods; use app\api\model\mj\MjSendActivity; use app\api\model\qc\QcMjSendActivity; use app\api\service\User as UserService; use app\api\model\GoodsSpecRel as GoodsSpecRelModel; use app\common\model\Goods as GoodsModel; use app\common\enum\goods\Status as GoodsStatusEnum; use app\common\library\helper; use app\common\exception\BaseException; use app\common\model\UserFavorite as UserFavoriteModel; use app\common\service\commission\RecordWaitCommission; use app\common\service\delivery\Express as ExpressService; use app\api\model\Order as OrderModel; use app\common\model\GoodsSku as GoodsSkuModel; use app\api\model\Setting as SettingModel; use app\common\enum\Setting as SettingEnum; use app\api\model\member\MemberGoods as MemerGoodsModel; /** * 商品模型 * Class Goods * @package app\api\model */ class Goods extends GoodsModel { /** * 隐藏字段 * @var array */ public $hidden = [ // 'images', 'deduct_stock_type', 'sales_initial', 'sales_actual', 'sort', 'is_delete', 'store_id', 'create_time', 'update_time', 'provider', // ]; /** * 商品详情:HTML实体转换回普通字符 * @param $value * @return string */ public function getContentAttr($value) { return htmlspecialchars_decode((string)$value); } /** * 获取商品列表 * @param array $param 查询条件 * @param int $listRows 分页数量 * @param bool $showVip * @return mixed|\think\model\Collection|\think\Paginator * @throws \think\db\exception\DbException */ public function getList(array $param = [], int $listRows = 15 , bool $showVip = false,bool $enableExchange = false) { // 整理查询参数 $params = array_merge($param, ['status' => GoodsStatusEnum::ON_SALE]); // 获取商品列表 $list = parent::getListFront($params, $listRows ,$showVip,$enableExchange); if ($list->isEmpty()) { return $list; } // 隐藏冗余的字段 $list->hidden(array_merge($this->hidden, ['content', 'goods_images', 'images'])); // 整理列表数据并返回 return $this->setGoodsListDataFromApi($list); } //抄首页的代码 lrh public function getRiceList($param = []){ $allGoods = []; $goodsList = GoodsModel::alias('goods')->with(['images.file']) ->field('goods.*,(goods.sales_initial+goods.sales_actual + goods.sales_shops) as goods_sales,(goods.collect_num*0.8+goods.cart_num+(goods.sales_initial+goods.sales_actual)*1.5) as common_sort') ->where('goods.status', 10) ->where('goods.stock_total', '>', 0) ->where('goods.is_delete', 0) ->where('goods.is_show', 1) ->where("goods.goods_id",'in',$param['goods_id_arr']) ->where("goods_type",10) ->order(['create_time'=>'desc'])->select(); if (!empty($goodsList)) { foreach ($goodsList as $vv) { $goodsInfo = $vv; $goodsInfo['goods_images'] = helper::getArrayColumn($vv['images'], 'file'); // 商品主图 $goodsInfo['goods_image'] = current($goodsInfo['goods_images'])['preview_url']; $allGoods[] = [ 'goods_id' => $goodsInfo['goods_id'], 'goods_name' => $goodsInfo['goods_name'], 'spec_type' => $goodsInfo['spec_type'], 'goods_price_min' => $goodsInfo['goods_price_min'], 'goods_price_max' => $goodsInfo['goods_price_max'], 'line_price_min' => $goodsInfo['line_price_min'], 'line_price_max' => $goodsInfo['line_price_max'], 'goods_images' => $goodsInfo['goods_images'], 'goods_image' => $goodsInfo['goods_image'], 'goods_sales' => $goodsInfo['goods_sales'], 'stock_total' => $goodsInfo['stock_total'] ]; } } return $allGoods; } /** * 获取商品详情 (详细数据用于页面展示) * @param int $goodsId 商品id * @param array|bool $userInfo 用户信息 * @return mixed * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function getDetails(int $goodsId, $userInfo = false, $addressId = 0, $staffUserId = 0) { // 关联查询 $with = ['images' => ['file'], 'skuList' => ['image']]; // 获取商品记录 $goodsInfo = static::detail($goodsId, $with); // 判断商品的状态 if (empty($goodsInfo) || $goodsInfo['is_delete'] || $goodsInfo['status'] != GoodsStatusEnum::ON_SALE) { throwError('很抱歉,商品信息不存在或已下架'); } // 设置商品展示的数据 $goodsInfo = $this->setGoodsDataFromApi($goodsInfo); // 商品规格列表 $goodsInfo['specList'] = GoodsSpecRelModel::getSpecList($goodsInfo['goods_id']); $userFavoriteModel = new UserFavoriteModel; $goodsInfo['collect'] = $userFavoriteModel->getGoodCollect($userInfo->user_id ?? 0, $goodsId); // 预计佣金 $o = new RecordWaitCommission(); $goodsInfo['fc_yj_amount'] = $o->calcFcYjAmount($goodsInfo, $userInfo); // 判断配送范围 $goodSkuId = GoodsSkuModel::where('goods_id',$goodsId)->order('id', 'asc')->value('goods_sku_id'); $goodsList = $this->getOrderGoodsListForDelivery( (int)$goodsId, (string)$goodSkuId, 1 ); // 获取收货地址 $address_info = []; if (!empty($userInfo) && $addressId) { $address_info = UserAddress::where('user_id', $userInfo->user_id)->where('address_id', $addressId)->find(); } if (empty($address_info)) { $address_info = $userInfo['address_default']??null; } $goodsInfo['address_info'] = $address_info; $cityId = $address_info ? (int)$address_info['city_id'] : 0; // 初始化配送服务类 $ExpressService = new ExpressService($cityId, $goodsList); $goodsInfo['isIntraRegion'] = true; // 验证商品是否在限购配送范围 $isDeliveryLimit = $ExpressService->isDeliveryLimit(); if ($isDeliveryLimit == false) { $goodsInfo['isIntraRegion'] = false; } else { // 验证商品是否在配送范围 $isIntraRegion = $ExpressService->isIntraRegion(); if ($cityId > 0 && $isIntraRegion == false) { $goodsInfo['isIntraRegion'] = false; } } // 可使用现金卡数据 $goodsInfo['valid_cash_rice_card'] = (new RiceCard())->validCashRiceCard($goodsInfo); $CouponModel = new CouponModel; $couponList = $CouponModel->getList(50, true,0,null,2,1);//获取可领取的优惠券 // 验证是否可使用优惠券 $goodsInfo['valid_cash_coupon'] = false; $newCouponList = []; //剔除当前商品不可用的优惠券 foreach ($couponList as $item){ $exceptGoodsId = helper::getArrayColumn($item['goodsExcept'],'goods_id'); $excludedGoodsIds = array_intersect($exceptGoodsId, [$goodsId]); if(count($excludedGoodsIds)>0) continue; if($item['is_receive']){ $goodsInfo['valid_cash_coupon'] = true; } //最低限额 if($item['min_price']>$goodsInfo['goods_price_min']) continue; // 计算打折金额 if ($item['coupon_type'] == 50) { $reducePrice = helper::bcmul($goodsInfo['goods_price_min'], 1-($item['discount'] / 100)); //如果设置了最大的优惠金额并且当前优惠的金额大于设置的金额 if($item['max_discount_price']>0 && $item['max_discount_price']<$reducePrice){ $reducePrice = $item['max_discount_price']; } $item['reduced_price'] = $reducePrice; } else $item['reduced_price'] = $item['reduce_price']; $newCouponList[] = $item->toArray(); } //按照reduced_price倒叙,tmp_expire_time升序 $tempCs = array_values(array_sort($newCouponList, 'reduced_price', true)); $reducePrices = array_values(array_unique(array_column($tempCs,'reduced_price'))); $fc = []; foreach ($reducePrices as $k=>$i){ $tt = []; foreach ($newCouponList as $j=>$c){ if ($i == $c['reduced_price']){ $tt[] = $c; unset($newCouponList[$j]); } } if (count($tt)){ $ttt = array_sort($tt, 'fmt_expire_time', false); $fc = array_merge($fc,$ttt); } } $goodsInfo['coupon_list'] = $fc; //$goodsInfo['coupon_list'] = array_values(array_sort($newCouponList, 'reduced_price', true)); // 顾客购物优惠金额--start--- $distributorRadio = SettingModel::getItem(SettingEnum::DISTRIBUTOR)['shopping_discount']; $shopping_discount_amount = 0; // 获取分享链接的用户信息 $userStaff = User::detail($staffUserId); if ($distributorRadio > 0) { if ((!empty($userInfo) && $userInfo->role == User::COMMISSION_USER) || (!empty($userStaff) && $userStaff->role == User::COMMISSION_USER)) { $shopping_discount_amount = helper::bcmul($goodsInfo['goods_price_min'], $distributorRadio / 100, 2); } } $goodsInfo['shopping_discount_amount'] = $shopping_discount_amount; // 顾客购物优惠金额--end--- // 顾客购物优惠金额--start--- $member_good_discount = MemberGoods::where('goods_id',$goodsInfo['goods_id'])->where('status',0)->value('discount'); if($member_good_discount){ $goodsInfo['is_member_goods'] = 1; if($userInfo['member_expire_time']>time()){ $goodsInfo['is_member'] = 1; }else{ $goodsInfo['is_member'] = 0; } $goodsInfo['member_discount_amount'] = helper::bcmul($goodsInfo['goods_price_min'], (100-$member_good_discount)/100);//换算成百分比,会员优惠券 }else{ $goodsInfo['member_discount_amount'] = 0; $goodsInfo['is_member'] = 0; $goodsInfo['is_member_goods'] = 0; } // 顾客购物优惠金额--end--- //满就送、满件送、全场满件送活动显示规则 self::getSendActivityOrder($goodsInfo); //添加判断是否有第N件打折的活动 self::getActivityDiscount($goodsInfo); return $goodsInfo; } //获取排名显示顺序 private function getSendActivityOrder(&$goodsInfo){ //满就送对象 $goodsInfo['full_send_act'] = FullSendActivity::validActivity($goodsInfo['goods_id']); //满件送对象详情 $goodsInfo['mj_send_act'] = MjSendActivity::validActivity($goodsInfo['goods_id']); //全场满件赠对象 $goodsInfo['qc_mj_send_act'] = QcMjSendActivity::validActivity($goodsInfo['goods_id']); $goodsInfo['is_send_act'] = 0;//是否显示活动 0不显示 1显示满就送 2显示满件送 3全场满件送 $time1 = $goodsInfo['full_send_act']['remain_secs']??0; $time2 = $goodsInfo['mj_send_act']['remain_secs']??0; $time3 = $goodsInfo['qc_mj_send_act']['remain_secs']??0; if($goodsInfo['full_send_act'] && $goodsInfo['mj_send_act'] && $goodsInfo['qc_mj_send_act']){ $time1 = $goodsInfo['full_send_act']['remain_secs']; $time2 = $goodsInfo['mj_send_act']['remain_secs']; $time3 = $goodsInfo['qc_mj_send_act']['remain_secs']; if($time1>$time2 && $time2>$time3){ $goodsInfo['is_send_act'] = 3; } if($time1>$time2 && $time3>$time2){ $goodsInfo['is_send_act'] = 2; } if($time2>$time1 && $time1>$time3){ $goodsInfo['is_send_act'] = 3; } if($time2>$time3 && $time3>$time1){ $goodsInfo['is_send_act'] = 1; } if($time3>$time1 && $time1>$time2){ $goodsInfo['is_send_act'] = 2; } if($time3>$time2 && $time2>$time1){ $goodsInfo['is_send_act'] = 1; } } if($goodsInfo['full_send_act'] && $goodsInfo['mj_send_act'] && !$goodsInfo['qc_mj_send_act']){ if($time1>$time2){ $goodsInfo['is_send_act'] = 2; }else{ $goodsInfo['is_send_act'] = 1; } } if($goodsInfo['full_send_act'] && !$goodsInfo['mj_send_act'] && $goodsInfo['qc_mj_send_act']){ if($time1>$time3){ $goodsInfo['is_send_act'] = 3; }else{ $goodsInfo['is_send_act'] = 1; } } if($goodsInfo['full_send_act'] && !$goodsInfo['mj_send_act'] && !$goodsInfo['qc_mj_send_act']){ $goodsInfo['is_send_act'] = 1; } if(!$goodsInfo['full_send_act'] && $goodsInfo['mj_send_act'] && $goodsInfo['qc_mj_send_act']){ if($time3>$time2){ $goodsInfo['is_send_act'] = 2; }else{ $goodsInfo['is_send_act'] = 3; } } if(!$goodsInfo['full_send_act'] && $goodsInfo['mj_send_act'] && !$goodsInfo['qc_mj_send_act']){ $goodsInfo['is_send_act'] = 2; } if(!$goodsInfo['full_send_act'] && !$goodsInfo['mj_send_act'] && $goodsInfo['qc_mj_send_act']){ $goodsInfo['is_send_act'] = 3; } $goodsInfo['is_show_line_price'] = 0; if($goodsInfo['line_price_min']<=$goodsInfo['goods_price_min']){ $goodsInfo['is_show_line_price'] = 1; } } /** * 获取当前商品是否存在N件X折活动 * @param $goodsInfo 商品详情 * @param $type 商品详情 * @return mixed * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @author: zjwhust * @Time: 2022/3/10 14:47 */ public function getActivityDiscount(&$goodsInfo){ $goodsInfo['is_activity_discount'] = 0; //是否有折扣活动 0没有 1有 $activity_ids = ActivityDiscountGoodsModel::where('goods_id',$goodsInfo['goods_id'])->column('activity_id');//获取所有有该商品的活动ID if($activity_ids){ $ActivityDiscount = ActivityDiscountModel::where('id','in',$activity_ids) ->where(['is_up'=>1,'audit_status_zg'=>10]) ->where('start_time','<',date('Y-m-d H:i:s')) ->where('end_time','>',date('Y-m-d H:i:s')) ->find(); if($ActivityDiscount){ $discount_props = array_values(array_sort($ActivityDiscount['discount_props'], 'value')); $goodsInfo['is_activity_discount'] = 1; $goodsInfo['activity_discount_time'] = (strtotime($ActivityDiscount['end_time'])-time());//折扣活动倒计时时间戳 if($ActivityDiscount['type']==1){ //1:第N件起打折,2:满N件起打折 $goodsInfo['activity_discount_desc'] = '第'.$discount_props[0]['value'].'件起'.floatval($discount_props[0]['count']).'折'; $goodsInfo['discount_type'] = 1; //是否有折扣活动 0没有 1有 }else{ $goodsInfo['activity_discount_desc'] = '满'.$discount_props[0]['value'].'件打'.floatval($discount_props[0]['count']).'折'; $goodsInfo['discount_goods_price'] = helper::number2(helper::bcmul($goodsInfo['goods_price_min'],$discount_props[0]['count']/10),true); $goodsInfo['discount_type'] = 2; //是否有折扣活动 0没有 1有 $goodsInfo['discount_num'] = $discount_props[0]['value']; } $goodsInfo['activity_discount'] = $ActivityDiscount; } } return $goodsInfo; } public function getOrderGoodsListForDelivery(int $goodsId, string $goodsSkuId, int $goodsNum) { // 获取商品列表 $model = new GoodsModel; $goodsList = $this->getListByIdsFromApi([$goodsId]); if ($goodsList->isEmpty()) { throwError('未找到商品信息1'); } // 隐藏冗余的属性 $goodsList->hidden(array_merge($model->hidden, ['content', 'goods_images', 'images'])); foreach ($goodsList as &$item) { // 商品sku信息 $item['skuInfo'] = GoodsSkuModel::detail($item['goods_id'], $goodsSkuId); // 商品单价 $item['goods_price'] = $item['skuInfo']['goods_price']; // 商品购买数量 $item['total_num'] = $goodsNum; // 商品SKU索引 $item['goods_sku_id'] = $item['skuInfo']['goods_sku_id']; // 商品购买总金额 $item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum); } return $goodsList; } /** * 根据商品id集获取商品列表 * @param array $goodsIds * @return mixed */ public function getListByIdsFromApi(array $goodsIds) { // 获取商品列表 // $data = $this->getListByIds($goodsIds, GoodsStatusEnum::ON_SALE); $data = $this->getListByIds($goodsIds, null);//不隐藏下架的商品 // 整理列表数据并返回 return $this->setGoodsListDataFromApi($data); } /** * 设置商品展示的数据 api模块 * @param $data * @return mixed */ private function setGoodsListDataFromApi($data) { return $this->setGoodsListData($data, function ($goods) { // 计算并设置商品会员价 $this->setGoodsDataFromApi($goods); }); } /** * 整理商品数据 api模块 * @param $goodsInfo * @return mixed */ private function setGoodsDataFromApi($goodsInfo) { return $this->setGoodsData($goodsInfo, function ($goods) { // 计算并设置商品会员价 $this->setGoodsGradeMoney($goods); }); } /** * 设置商品的会员价 * @param Goods $goods * @throws BaseException */ private function setGoodsGradeMoney(self $goods) { // 获取当前登录的用户信息 $userInfo = UserService::getCurrentLoginUser(); $goods['member_price'] = (new MemerGoodsModel)->getGoodsMemberPrice($goods['goods_id'],$goods['goods_price_min']); // 会员等级状态 $gradeStatus = (!empty($userInfo) && $userInfo['grade_id'] > 0 && !empty($userInfo['grade'])) && (!$userInfo['grade']['is_delete'] && $userInfo['grade']['status']); // 判断商品是否参与会员折扣 if (!$gradeStatus || !$goods['is_enable_grade']) { $goods['is_user_grade'] = false; return; } // 商品单独设置了会员折扣 if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$userInfo['grade_id']])) { // 折扣比例 $discountRatio = helper::bcdiv($goods['alone_grade_equity'][$userInfo['grade_id']], 10); } else { // 折扣比例 $discountRatio = helper::bcdiv($userInfo['grade']['equity']['discount'], 10); } if ($discountRatio > 0) { // 标记参与会员折扣 $goods['is_user_grade'] = true; // 会员折扣价 foreach ($goods['skuList'] as &$skuItem) { $skuItem['goods_price'] = helper::number2(helper::bcmul($skuItem['goods_price'], $discountRatio), true); } } } /** * 获取当前商品是否存在N件X折活动 * @param $goodsInfo 商品详情 * @param $type 类型 1购物车 2确认订单 * @return mixed * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @author: zjwhust * @Time: 2022/3/10 14:47 */ public function getActivityDiscountDetail(&$goodsInfo,$type=1){ $goodsInfo['is_activity_discount'] = 0; //是否参与折扣活动 0没有 1有 $goodsInfo['activity_discount_total_price'] = 0; //优惠的金额 $goodsInfo['activity_discount_overlay'] = ''; //叠加的优惠 $goodsInfo['activity_discount_count'] = ''; //数量 $activity_ids = ActivityDiscountGoodsModel::where('goods_id',$goodsInfo['goods_id'])->column('activity_id');//获取所有有该商品的活动ID if($activity_ids){ $ActivityDiscount = ActivityDiscountModel::with(['goods']) ->where('id','in',$activity_ids) ->where(['is_up'=>1,'audit_status_zg'=>10]) ->where('start_time','<',date('Y-m-d H:i:s')) ->where('end_time','>',date('Y-m-d H:i:s')) ->find(); if($ActivityDiscount){ $residue_num = 0; foreach ($ActivityDiscount['goods'] as $goods){ if($goods['goods_id']==$goodsInfo['goods_id']){ $residue_num = $goods['limit_stock']-$goods['purchase_stock']; } } if($residue_num<1){//活动剩余数量为0 return $goodsInfo; } if($type == 1 ){ $good_num = $goodsInfo['goods_num']; $good_price = $goodsInfo['goods']['skuInfo']['goods_price']; } if($type == 2 ){ $good_num = $goodsInfo['total_num']; $good_price = $goodsInfo['goods_price']; } $good_num2 = $good_num; if($good_num>$residue_num){ $good_num2 = $residue_num; } // $goodsInfo['activity_discount_time'] = (strtotime($ActivityDiscount['end_time'])-time());//折扣活动倒计时时间戳 $discount_props = array_values(array_sort($ActivityDiscount['discount_props'], 'value', true)); $prop = []; foreach ($discount_props as $arr){ if($good_num2>=$arr['value']){ $prop = $arr; break; } } if(!empty($prop)){//当商品数量达到了商品活动的起始数量时 $goodsInfo['is_activity_discount'] = 1; $goodsInfo['activity_discount_id'] = $ActivityDiscount['id']; //N件X折活动ID $goodsInfo['activity_discount_overlay'] = $ActivityDiscount['overlay_discount']; //叠加活动状态 $goodsInfo['activity_discount_count'] = $good_num2; //叠加活动数量 if($ActivityDiscount['type']==1){ //1:第N件起打折,2:满N件起打折 $num = $good_num-$prop['value']+1; $goodsInfo['activity_discount_total_price'] = helper::bcmul((10-$prop['count'])/10,$good_price*$num,2); $goodsInfo['activity_discount_desc'] = '第'.$prop['value'].'件起打'.floatval($prop['count']).'折'; }else{ $goodsInfo['activity_discount_total_price'] = helper::bcmul((10-$prop['count'])/10,$good_price*$good_num,2); $goodsInfo['activity_discount_desc'] = '满'.$prop['value'].'件打'.floatval($prop['count']).'折'; } } } } return $goodsInfo; } }