PHP订单模块梳理(乐观锁)
购物车功能点
1.0 购物车列表
2.0 添加到购物车
3.0 更新、删除购物车商品
4.0 选择或取消购物车商品
1.0下单
2.0订单支付、超时取消
3.0 确认收货
4.0订单评价
5.0订单列表、详情
er图设计
添加购物车的思路:
如果已经存在购物车货品,则增加数量;
否则添加新的购物车货品项.
逻辑思路
1.0 校验参数
$goodsId = $this->verifyId('goodsId', 0);$productId = $this->verifyId('productId', 0);$number = $this->verifyPositiveInteger('number', 0);//2.0判断商品是否存在(主要判断商品是否可以购买)
if($number <= 0){return $this->badArgument();
}
$goods = GoodsService::getInstance()->getGoods($goodsId);if (is_null($goods) || !$goods->is_on_sale) {return $this->fail(CodeResponse::GOODS_UNSHELVE);}//判断购物车中是否存在此规格商品$product = GoodsServices::getInstance()->getGoodsProductById($productId);if (is_null($product) || $product->number < $number) {return $this->fail(CodeResponse::GOODS_NO_STOCK);}//获取购物车商品CartServices::getInstance()->add($this->userId(), $goodsId, $productId, $number);//如果没有添加购物车就添加一个新的,如果有就添加数量
更新:逻辑梳理
//1.0接收参数
$id = $this->verifyId('id', 0);$goodsId = $this->verifyId('goodsId', 0);$productId = $this->verifyId('productId', 0);$number = $this->verifyPositiveInteger('number', 0);//2.0获取当前购物车$cart = CartServices::getInstance()->getCartById($this->userId(), $id);//3.0如果购物车是null返回错误if (is_null($cart)) {return $this->badArgumentValue();}//4.0//判断当前购物车是否等于当前购物车以及其他参数if ($cart->goods_id != $goodsId || $cart->product_id != $productId) {return $this->badArgumentValue();}//5.0判断当前货品是否下架$goods = GoodsServices::getInstance()->getGoods($goodsId);if (is_null($goods) || !$goods->is_on_sale) {return $this->fail(CodeResponse::GOODS_UNSHELVE);}//6.0判断商品的库存是否足够$product = GoodsServices::getInstance()->getGoodsProductById($productId);if (is_null($product) || $product->number < $number) {return $this->fail(CodeResponse::GOODS_NO_STOCK);}//7.0修改购物车$cart->number = $number;$ret = $cart->save();return $this->failOrSuccess($ret);
购物车:删除
逻辑:删除id
//验证是否数组是否为空$productIds = $this->verifyArrayNotEmpty('productIds', []);CartServices::getInstance()->delete($this->userId(), $productIds);return $this->index();
立即购买
逻辑梳理:立即购买与加入购物车的逻辑很相识
区别
1.如果购物车内已经存在购物车货品,前者的逻辑是添加,这里的逻辑是数量覆盖。
2.0添加成功以后,前者的逻辑是返回当前购物车数量,这里的逻辑是返回对应购物车像的ID
/*** 立即购买* @return JsonResponse* @throws BusinessException*/public function fastadd(){$goodsId = $this->verifyId('goodsId', 0);$productId = $this->verifyId('productId', 0);$number = $this->verifyPositiveInteger('number', 0);$cart = CartServices::getInstance()->fastadd($this->userId(), $goodsId, $productId, $number);return $this->success($cart->id);}
public function fastadd($userId, $goodsId, $productId, $number){//获取商品信息list($goods, $product) = $this->getGoodsInfo($goodsId, $productId);$cartProduct = $this->getCartProduct($userId, $goodsId, $productId);if (is_null($cartProduct)) {return $this->newCart($userId, $goods, $product, $number);} else {return $this->editCart($cartProduct, $product, $number);}}
public function getGoodsInfo($goodsId, $productId){$goods = GoodsServices::getInstance()->getGoods($goodsId);if (is_null($goods) || !$goods->is_on_sale) {$this->throwBusinessException(CodeResponse::GOODS_UNSHELVE);}$product = GoodsServices::getInstance()->getGoodsProductById($productId);if (is_null($product)) {$this->throwBusinessException(CodeResponse::GOODS_NO_STOCK);}return [$goods, $product];}
public function getCartProduct($userId, $goodsId, $productId){return Cart::query()->where('user_id', $userId)->where('goods_id', $goodsId)->where('product_id', $productId)->first();}
public function newCart($userId, Goods $goods, GoodsProduct $product, $number){if ($number > $product->number) {$this->throwBusinessException(CodeResponse::GOODS_NO_STOCK);}$cart = Cart::new();$cart->goods_sn = $goods->goods_sn;$cart->goods_name = $goods->name;$cart->pic_url = $product->url ?: $goods->pic_url;$cart->price = $product->price;$cart->specifications = $product->specifications;$cart->user_id = $userId;$cart->checked = true;$cart->number = $number;$cart->goods_id = $goods->id;$cart->product_id = $product->id;$cart->save();return $cart;}
public function editCart($existCart, $product, $num){if ($num > $product->number) {$this->throwBusinessException(CodeResponse::GOODS_NO_STOCK);}$existCart->number = $num;$existCart->save();return $existCart;}
购物车列表接口
//逻辑梳理
//1.0购物车列表是需要登录
//2.0根据用户获取相关的数据记录
//3.0循环一下购物车获取一下还没有失效的商品
//4.0统计购物车商品的总件数金额等
//5.0最后拼装返回
//获取购物车列表public function getCartList($userId){return Cart::query()->where('user_id', $userId)->get();}
public function getValidCartList($userId){//获取有效的商品列表$list = $this->getCartList($userId);$goodsIds = $list->pluck('goods_id')->toArray();//获取商品信息$goodsList = GoodsServices::getInstance()->getGoodsListByIds($goodsIds)->keyBy('id');//记录无效的商品id$invalidCartIds = [];//对数组进行过滤$list = $list->filter(function (Cart $cart) use ($goodsList, &$invalidCartIds) {/** @var Goods $goods *///拿到商品信息$goods = $goodsList->get($cart->goods_id);//过滤$isValid = !empty($goods) && $goods->is_on_sale;if (!$isValid) {$invalidCartIds[] = $cart->id;}return $isValid;});//删除无效的商品id$this->deleteCartList($invalidCartIds);return $list;}
public function deleteCartList($ids){if (empty($ids)) {return 0;}return Cart::query()->whereIn('id', $ids)->delete();}
/*** 购物车列表信息* @return JsonResponse* @throws Exception*/public function index(){$list = CartServices::getInstance()->getValidCartList($this->userId());//总数$goodsCount = 0;//总金额$goodsAmount = 0;//选中商品的总数$checkedGoodsCount = 0;//选中的总金额$checkedGoodsAmount = 0;foreach ($list as $item) {$goodsCount += $item->number;$amount = bcmul($item->price, $item->number, 2);$goodsAmount = bcadd($goodsAmount, $amount, 2);if ($item->checked) {$checkedGoodsCount += $item->number;$checkedGoodsAmount = bcadd($checkedGoodsAmount, $amount, 2);}}return $this->success(['cartList' => $list->toArray(),'cartTotal' => ['goodsCount' => $goodsCount,'goodsAmount' => (double) $goodsAmount,'checkedGoodsCount' => $checkedGoodsCount,'checkedGoodsAmount' => (double) $checkedGoodsAmount,]]);}
下单前逻辑确认
public function checkout(){//验证id$cartId = $this->verifyInteger('cartId');$addressId = $this->verifyInteger('addressId');$couponId = $this->verifyInteger('couponId');$grouponRulesId = $this->verifyInteger('grouponRulesId');// 获取地址$address = AddressServices::getInstance()->getAddressOrDefault($this->userId(), $addressId);$addressId = $address->id ?? 0;// 获取购物车的商品列表$checkedGoodsList = CartServices::getInstance()->getCheckedCartList($this->userId(), $cartId);// 计算商品总金额$grouponPrice = 0;$checkedGoodsPrice = CartServices::getInstance()->getCartPriceCutGroupon($checkedGoodsList, $grouponRulesId,$grouponPrice);// 获取优惠券信息$availableCouponLength = 0;$couponUser = CouponServices::getInstance()->getMostMeetPriceCoupon($this->userId(), $couponId,$checkedGoodsPrice, $availableCouponLength);// 兼容litemall的返回if ($couponId == -1 || is_null($couponId)) {// if (is_null($couponUser)) {$couponId = -1;$userCouponId = -1;$couponPrice = 0;} else {$couponId = $couponUser->coupon_id ?? 0;$userCouponId = $couponUser->id ?? 0;$couponPrice = CouponServices::getInstance()->getCoupon($couponId)->discount ?? 0;}// 运费$freightPrice = OrderServices::getInstance()->getFreight($checkedGoodsPrice);// 计算订单金额$orderPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderPrice = bcsub($orderPrice, $couponPrice, 2);$orderPrice = max(0, $orderPrice);return $this->success(["addressId" => $addressId,"couponId" => $couponId,"userCouponId" => $userCouponId,"cartId" => $cartId,"grouponRulesId" => $grouponRulesId,"grouponPrice" => $grouponPrice,"checkedAddress" => $address,"availableCouponLength" => $availableCouponLength,"goodsTotalPrice" => $checkedGoodsPrice,"freightPrice" => $freightPrice,"couponPrice" => $couponPrice,"orderTotalPrice" => $orderPrice,"actualPrice" => $orderPrice,"checkedGoodsList" => $checkedGoodsList->toArray(),]);}
/*** 获取地址或者返回默认地址* @param $userId* @param null $addressId* @return Address|Builder|Model|object|null* @throws BusinessException*/public function getAddressOrDefault($userId, $addressId = null){// 获取地址if (empty($addressId)) {$address = AddressServices::getInstance()->getDefaultAddress($userId);} else {$address = AddressServices::getInstance()->getAddress($userId, $addressId);if (empty($address)) {$this->throwBadArgumentValue();}}return $address;}
public function getDefaultAddress($userId){//返回默认地址return Address::query()->where('user_id', $userId)->where('is_default', 1)->first();}
/*** 获取用户地址* @param $userId* @param $addressId* @return Address|Model|null*/public function getAddress($userId, $addressId){return Address::query()->where('user_id', $userId)->where('id', $addressId)->first();}
/*** 获取已选择购物车商品列表* @param $userId* @param $cartId* @return Cart[]|Collection* @throws BusinessException*/public function getCheckedCartList($userId, $cartId = null){if (empty($cartId)) {$checkedGoodsList = $this->getCheckedCarts($userId);} else {$cart = $this->getCartById($userId, $cartId);if (empty($cart)) {$this->throwBadArgumentValue();}$checkedGoodsList = collect([$cart]);}return $checkedGoodsList;}
public function getCheckedCarts($userId){return Cart::query()->where('user_id', $userId)->where('checked', 1)->get();}
public function getCartById($userId, $id){return Cart::query()->where('user_id', $userId)->where('id', $id)->first();}
public function getCartPriceCutGroupon($checkedGoodsList, $grouponRulesId, &$grouponPrice = 0){//获取一下团购规则$grouponRules = GrouponServices::getInstance()->getGrouponRulesById($grouponRulesId);$checkedGoodsPrice = 0;//循环一下获取到购物车的列表foreach ($checkedGoodsList as $cart) {//(如果有团购商品就用团购规则来计算,如果没有就用原价)//所以需要判断goods_id == 购物车->goods_idif ($grouponRules && $grouponRules->goods_id == $cart->goods_id) {//计算团购优惠的总金额$grouponPrice = bcmul($grouponRules->discount, $cart->number, 2);//如果都满足减折扣$price = bcsub($cart->price, $grouponRules->discount, 2);} else {$price = $cart->price;}//计算单个商品的总金额$price = bcmul($price, $cart->number, 2);//计算所有商品的总金额$checkedGoodsPrice = bcadd($checkedGoodsPrice, $price, 2);}return $checkedGoodsPrice;}
先创建一个枚举用来区别不同的优惠卷
<?phpnamespace App\Enums;class CouponUserEnums
{const STATUS_USABLE = 0;const STATUS_USED = 1;const STATUS_EXPIRED = 2;const STATUS_OUT = 3;
}
/*** 验证当前价格是否可以使用这张优惠券* @param Coupon $coupon* @param CouponUser $couponUser* @param double $price* @return bool* @throws Exception*/public function checkCouponAndPrice($coupon, $couponUser, $price){//如果优惠用户为空返回错误if (empty($couponUser)) {return false;}//如果优惠券为空返回错误if (empty($coupon)) {return false;}//如果用户与优惠卷 id不一样说明 数据不一致.if ($couponUser->coupon_id != $coupon->id) {return false;}//如果优惠券的状态不是0的话,那也是不能使用的if ($coupon->status != CouponEnums::STATUS_NORMAL) {return false;}//验证优惠卷支持的商品类型if ($coupon->goods_type != CouponEnums::GOODS_TYPE_ALL) {return false;}//需要满足最低消费金额if (bccomp($coupon->min, $price) == 1) {return false;}$now = now();switch ($coupon->time_type) {case CouponEnums::TIME_TYPE_TIME:$start = Carbon::parse($coupon->start_time);$end = Carbon::parse($coupon->end_time);//如果当前时间在开始时间或者结束时间之后if ($now->isBefore($start) || $now->isAfter($end)) {return false;}break;//算相对时间:从优惠卷领娶的时间开始算case CouponEnums::TIME_TYPE_DAYS://当前时间加上有效时间$expired = Carbon::parse($couponUser->add_time)->addDays($coupon->days);//如果超过了有效时间if ($now->isAfter($expired)) {return false;}break;default:return false;}return true;}
public function getMeetPriceCouponAndSort($userId, $price){//获娶可以用的优惠券$couponUsers = CouponServices::getInstance()->getUsableCoupons($userId);//获取优惠卷的id列表$couponIds = $couponUsers->pluck('coupon_id')->toArray();//批量获取优惠券$coupons = CouponServices::getInstance()->getCoupons($couponIds)->keyBy('id');//过滤不需要的内容filterreturn $couponUsers->filter(function (CouponUser $couponUser) use ($coupons, $price) {/** @var Coupon $coupon */$coupon = $coupons->get($couponUser->coupon_id);return CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $price);//默认选取优惠力度最大的})->sortByDesc(function (CouponUser $couponUser) use ($coupons) {/** @var Coupon $coupon */$coupon = $coupons->get($couponUser->coupon_id);return $coupon->discount;//discount优惠金额});}
一下代码还会进行封装切勿复制粘贴
public function getMeetPriceCouponAndSort($userId, $price){//获娶可以用的优惠券$couponUsers = CouponServices::getInstance()->getUsableCoupons($userId);//获取优惠卷的id列表$couponIds = $couponUsers->pluck('coupon_id')->toArray();//批量获取优惠券$coupons = CouponServices::getInstance()->getCoupons($couponIds)->keyBy('id');//过滤不需要的内容filterreturn $couponUsers->filter(function (CouponUser $couponUser) use ($coupons, $price) {/** @var Coupon $coupon */$coupon = $coupons->get($couponUser->coupon_id);return CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $price);//默认选取优惠力度最大的})->sortByDesc(function (CouponUser $couponUser) use ($coupons) {/** @var Coupon $coupon */$coupon = $coupons->get($couponUser->coupon_id);return $coupon->discount;//discount优惠金额});//初始化优惠卷价格$couponFirst = 0;//如果couponId是null或者couponId == -1 不使用优惠卷if(is_null($couponId) || $couponId == -1;){$userCouponId = -1;}elseif ($couponId == 0){/** @var CouponUser $couponUser */$couponUser = $couponUsers->first();$couponId = $couponUser->coupon_id ?? 0;//获取优惠信息$couponPrice = CouponServices::getInstance()->getCoupon($couponId)->discount ?? 0;} else {//如果传了$couponId//获取$coupon对象信息$coupon = CouponServices::getInstance()->getCoupon($couponId);//获取$coupon 对象信息$couponUser = CouponServices::getInstance()->getCouponUser($userCouponId);//验证这个优惠卷是否可用$is = CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $price);if ($is) {$couponPrice = $coupon->disscount ?? 0 ;//下面两句可以省略不写 因为 验证通过了肯定可以//$couponId = $couponUser -> coupon_id ?? 0;// $userCouponId = $couponUser -> id ?? 0;}}}}
public function getCouponUser($id, $columns = ['*']){return CouponUser::query()->find($id, $columns);}
到此优惠卷相关内容已经完成 接下来 算运费
在这里插入代码片
<?phpnamespace App\Services;use App\Models\System;class SystemServices extends BaseServices
{// 小程序相关配置const LITEMALL_WX_INDEX_NEW = "litemall_wx_index_new";const LITEMALL_WX_INDEX_HOT = "litemall_wx_index_hot";const LITEMALL_WX_INDEX_BRAND = "litemall_wx_index_brand";const LITEMALL_WX_INDEX_TOPIC = "litemall_wx_index_topic";const LITEMALL_WX_INDEX_CATLOG_LIST = "litemall_wx_catlog_list";const LITEMALL_WX_INDEX_CATLOG_GOODS = "litemall_wx_catlog_goods";const LITEMALL_WX_SHARE = "litemall_wx_share";// 运费相关配置const LITEMALL_EXPRESS_FREIGHT_VALUE = "litemall_express_freight_value";const LITEMALL_EXPRESS_FREIGHT_MIN = "litemall_express_freight_min";// 订单相关配置const LITEMALL_ORDER_UNPAID = "litemall_order_unpaid";const LITEMALL_ORDER_UNCONFIRM = "litemall_order_unconfirm";const LITEMALL_ORDER_COMMENT = "litemall_order_comment";// 商场相关配置const LITEMALL_MALL_NAME = "litemall_mall_name";const LITEMALL_MALL_ADDRESS = "litemall_mall_address";const LITEMALL_MALL_PHONE = "litemall_mall_phone";const LITEMALL_MALL_QQ = "litemall_mall_qq";const LITEMALL_MALL_LONGITUDE = "litemall_mall_longitude";const LITEMALL_MALL_Latitude = "litemall_mall_latitude";public function getOrderUnConfirmDays(){return (int) $this->get(self::LITEMALL_ORDER_UNCONFIRM);}public function getOrderUnpaidDelayMinutes(){return (int) $this->get(self::LITEMALL_ORDER_UNPAID);}public function getFreightValue(){return (double) $this->get(self::LITEMALL_EXPRESS_FREIGHT_VALUE);}public function getFreightMin(){return (double) $this->get(self::LITEMALL_EXPRESS_FREIGHT_MIN);}//获取 系统配置方法public function get($key){$value = System::query()->where('key_name', $key)->first(['key_value']);$value = $value['key_value'] ?? null;if ($value == 'false' || $value == 'FALSE') {return false;}if ($value == 'true' || $value == 'TRUE') {return true;}return $value;}
}
public function getMeetPriceCouponAndSort($userId, $price){//获娶可以用的优惠券$couponUsers = CouponServices::getInstance()->getUsableCoupons($userId);//获取优惠卷的id列表$couponIds = $couponUsers->pluck('coupon_id')->toArray();//批量获取优惠券$coupons = CouponServices::getInstance()->getCoupons($couponIds)->keyBy('id');//过滤不需要的内容filterreturn $couponUsers->filter(function (CouponUser $couponUser) use ($coupons, $price) {/** @var Coupon $coupon */$coupon = $coupons->get($couponUser->coupon_id);return CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $price);//默认选取优惠力度最大的})->sortByDesc(function (CouponUser $couponUser) use ($coupons) {/** @var Coupon $coupon */$coupon = $coupons->get($couponUser->coupon_id);return $coupon->discount;//discount优惠金额});//初始化优惠卷价格$couponFirst = 0;//如果couponId是null或者couponId == -1 不使用优惠卷if(is_null($couponId) || $couponId == -1;){ $couponId = -1;$userCouponId = -1;}elseif ($couponId == 0){/** @var CouponUser $couponUser */$couponUser = $couponUsers->first();$couponId = $couponUser->coupon_id ?? 0;//获取优惠信息$couponPrice = CouponServices::getInstance()->getCoupon($couponId)->discount ?? 0;} else {//如果传了$couponId//获取$coupon对象信息$coupon = CouponServices::getInstance()->getCoupon($couponId);//获取$coupon 对象信息$couponUser = CouponServices::getInstance()->getCouponUser($userCouponId);//验证这个优惠卷是否可用$is = CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $price);if ($is) {$couponPrice = $coupon->disscount ?? 0 ;//下面两句可以省略不写 因为 验证通过了肯定可以//$couponId = $couponUser -> coupon_id ?? 0;// $userCouponId = $couponUser -> id ?? 0;}}//运费$freightPrice = 0;//获取最低运费$freightMin = SYstemServices::getInstance()->getFreightMin();if (bccomp($freightMin, $price) == 1) {$freightPrice = SystemServices::getInstance()->getFreightValue();}// 计算订单金额 +运费-优惠卷$orderPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderPrice = bcsub($orderPrice, $couponPrice, 2);return $this->success(["addressId" => $addressId,"couponId" => $couponId,"userCouponId" => $userCouponId,"cartId" => $cartId,"grouponRulesId" => $grouponRulesId,"grouponPrice" => $grouponPrice,"checkedAddress" => $address,"availableCouponLength" => $availableCouponLength,"goodsTotalPrice" => $checkedGoodsPrice,"freightPrice" => $freightPrice,"couponPrice" => $couponPrice,"orderTotalPrice" => $orderPrice,"actualPrice" => $orderPrice,"checkedGoodsList" => $checkedGoodsList->toArray(),]);}}
public function getFreightValue(){return (double) $this->get(self::LITEMALL_EXPRESS_FREIGHT_VALUE);}
优化下单前信息确认
/*** 下单前信息确认* @return JsonResponse* @throws BusinessException* @throws Exception*/public function checkout(){//验证id$cartId = $this->verifyInteger('cartId');$addressId = $this->verifyInteger('addressId');$couponId = $this->verifyInteger('couponId');$grouponRulesId = $this->verifyInteger('grouponRulesId');// 获取地址$address = AddressServices::getInstance()->getAddressOrDefault($this->userId(), $addressId);$addressId = $address->id ?? 0;// 获取购物车的商品列表$checkedGoodsList = CartServices::getInstance()->getCheckedCartList($this->userId(), $cartId);// 计算商品总金额$grouponPrice = 0;$checkedGoodsPrice = CartServices::getInstance()->getCartPriceCutGroupon($checkedGoodsList, $grouponRulesId,$grouponPrice);// 获取优惠券信息$availableCouponLength = 0;$couponUser = CouponServices::getInstance()->getMostMeetPriceCoupon($this->userId(), $couponId,$checkedGoodsPrice, $availableCouponLength);// 兼容litemall的返回if ($couponId == -1 || is_null($couponId)) {// if (is_null($couponUser)) {$couponId = -1;$userCouponId = -1;$couponPrice = 0;} else {$couponId = $couponUser->coupon_id ?? 0;$userCouponId = $couponUser->id ?? 0;$couponPrice = CouponServices::getInstance()->getCoupon($couponId)->discount ?? 0;}// 运费$freightPrice = OrderServices::getInstance()->getFreight($checkedGoodsPrice);// 计算订单金额$orderPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderPrice = bcsub($orderPrice, $couponPrice, 2);$orderPrice = max(0, $orderPrice);return $this->success(["addressId" => $addressId,"couponId" => $couponId,"userCouponId" => $userCouponId,"cartId" => $cartId,"grouponRulesId" => $grouponRulesId,"grouponPrice" => $grouponPrice,"checkedAddress" => $address,"availableCouponLength" => $availableCouponLength,"goodsTotalPrice" => $checkedGoodsPrice,"freightPrice" => $freightPrice,"couponPrice" => $couponPrice,"orderTotalPrice" => $orderPrice,"actualPrice" => $orderPrice,"checkedGoodsList" => $checkedGoodsList->toArray(),]);}
}
/*** 获取地址或者返回默认地址* @param $userId* @param null $addressId* @return Address|Builder|Model|object|null* @throws BusinessException*/public function getAddressOrDefault($userId, $addressId = null){// 获取地址if (empty($addressId)) {$address = AddressServices::getInstance()->getDefaultAddress($userId);} else {$address = AddressServices::getInstance()->getAddress($userId, $addressId);if (empty($address)) {$this->throwBadArgumentValue();}}return $address;}
public function getDefaultAddress($userId){return Address::query()->where('user_id', $userId)->where('is_default', 1)->first();}
/*** 获取用户地址* @param $userId* @param $addressId* @return Address|Model|null*/public function getAddress($userId, $addressId){return Address::query()->where('user_id', $userId)->where('id', $addressId)->first();}
/*** @throws BusinessException*/public function throwBadArgumentValue(){$this->throwBusinessException(CodeResponse::PARAM_VALUE_ILLEGAL);}
// 获取购物车的商品列表
checkedGoodsList=CartServices::getInstance()−>getCheckedCartList(checkedGoodsList = CartServices::getInstance()->getCheckedCartList(checkedGoodsList=CartServices::getInstance()−>getCheckedCartList(this->userId(), $cartId);
/*** 获取已选择购物车商品列表* @param $userId* @param $cartId* @return Cart[]|Collection* @throws BusinessException*/public function getCheckedCartList($userId, $cartId = null){if (empty($cartId)) {$checkedGoodsList = $this->getCheckedCarts($userId);} else {$cart = $this->getCartById($userId, $cartId);if (empty($cart)) {$this->throwBadArgumentValue();}$checkedGoodsList = collect([$cart]);}return $checkedGoodsList;}
public function getCheckedCarts($userId){return Cart::query()->where('user_id', $userId)->where('checked', 1)->get();}
public function getCartById($userId, $id){return Cart::query()->where('user_id', $userId)->where('id', $id)->first();}
/*** @throws BusinessException*/public function throwBadArgumentValue(){$this->throwBusinessException(CodeResponse::PARAM_VALUE_ILLEGAL);}
这里以引用的方式传进来会getCartPriceCutGroupon方法执行后重新赋值
// 计算商品总金额$grouponPrice = 0;$checkedGoodsPrice = CartServices::getInstance()->getCartPriceCutGroupon($checkedGoodsList, $grouponRulesId,$grouponPrice);
public function getCartPriceCutGroupon($checkedGoodsList, $grouponRulesId, &$grouponPrice = 0){//获取一下团购规则$grouponRules = GrouponServices::getInstance()->getGrouponRulesById($grouponRulesId);$checkedGoodsPrice = 0;//循环一下获取到购物车的列表foreach ($checkedGoodsList as $cart) {//(如果有团购商品就用团购规则来计算,如果没有就用原价)//所以需要判断goods_id == 购物车->goods_idif ($grouponRules && $grouponRules->goods_id == $cart->goods_id) {//计算团购优惠的总金额$grouponPrice = bcmul($grouponRules->discount, $cart->number, 2);//如果都满足减折扣$price = bcsub($cart->price, $grouponRules->discount, 2);} else {$price = $cart->price;}//计算单个商品的总金额$price = bcmul($price, $cart->number, 2);//计算所有商品的总金额$checkedGoodsPrice = bcadd($checkedGoodsPrice, $price, 2);}return $checkedGoodsPrice;}
// 获取优惠券信息$availableCouponLength = 0;$couponUser = CouponServices::getInstance()->getMostMeetPriceCoupon($this->userId(), $couponId,$checkedGoodsPrice, $availableCouponLength);// 兼容litemall的返回 如果是空就返回 -1 如果不是非空就返回正常信息if ($couponId == -1 || is_null($couponId)) {// if (is_null($couponUser)) {$couponId = -1;$userCouponId = -1;$couponPrice = 0;} else {$couponId = $couponUser->coupon_id ?? 0;$userCouponId = $couponUser->id ?? 0;$couponPrice = CouponServices::getInstance()->getCoupon($couponId)->discount ?? 0;}
/*** 获取适合当前价格的优惠券* @param $userId* @param $couponId* @param $price* @param int $availableCouponLength* @return CouponUser|null* @throws Exception*/public function getMostMeetPriceCoupon($userId, $couponId, $price, &$availableCouponLength = 0){$couponUsers = $this->getMeetPriceCouponAndSort($userId, $price);$availableCouponLength = $couponUsers->count();if (is_null($couponId) || $couponId == -1) {return null;}if (!empty($couponId)) {$coupon = $this->getCoupon($couponId);$couponUser = $this->getCouponUserByCouponId($userId, $couponId);$is = $this->checkCouponAndPrice($coupon, $couponUser, $price);if ($is) {return $couponUser;}}return $couponUsers->first();}
最后重构
// 运费$freightPrice = OrderServices::getInstance()->getFreight($checkedGoodsPrice);// 计算订单金额$orderPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderPrice = bcsub($orderPrice, $couponPrice, 2);$orderPrice = max(0, $orderPrice);return $this->success(["addressId" => $addressId,"couponId" => $couponId,"userCouponId" => $userCouponId,"cartId" => $cartId,"grouponRulesId" => $grouponRulesId,"grouponPrice" => $grouponPrice,"checkedAddress" => $address,"availableCouponLength" => $availableCouponLength,"goodsTotalPrice" => $checkedGoodsPrice,"freightPrice" => $freightPrice,"couponPrice" => $couponPrice,"orderTotalPrice" => $orderPrice,"actualPrice" => $orderPrice,"checkedGoodsList" => $checkedGoodsList->toArray(),]);
/*** 获取运费* @param $price* @return float|int*/public function getFreight($price){$freightPrice = 0;$freightMin = SystemServices::getInstance()->getFreightMin();if (bccomp($freightMin, $price) == 1) {$freightPrice = SystemServices::getInstance()->getFreightValue();}return $freightPrice;}public function getOrderByUserIdAndId($userId, $orderId){return Order::query()->where('user_id', $userId)->find($orderId);}
下单逻辑实在是有点恶心.后期还会整理 有些地方还可以整理
由于参数过长这里我们可以封装一下
/*** 提交订单* @return JsonResponse* @throws BusinessException* @throws Throwable*/public function submit(){//入参$input = OrderSubmitInput::new();//调用提交订单方法 transaction是开启事务$order = DB::transaction(function () use ($input) {return OrderServices::getInstance()->submit($this->userId(), $input);});return $this->success(['orderId' => $order->id,'grouponLikeId' => $input->grouponLinkId ?? 0]);}
/*** 提交订单* @param $userId* @param OrderSubmitInput $input* @return Order|void* @throws BusinessException*/public function submit($userId, OrderSubmitInput $input){// 验证团购规则的有效性if (!empty($input->grouponRulesId)) {GrouponServices::getInstance()->checkGrouponValid($userId, $input->grouponRulesId);}$address = AddressServices::getInstance()->getAddress($userId, $input->addressId);if (empty($address)) {return $this->throwBadArgumentValue();}// 获取购物车的商品列表$checkedGoodsList = CartServices::getInstance()->getCheckedCartList($userId, $input->cartId);// 计算商品总金额$grouponPrice = 0;$checkedGoodsPrice = CartServices::getInstance()->getCartPriceCutGroupon($checkedGoodsList,$input->grouponRulesId,$grouponPrice);// 获取优惠券面额$couponPrice = 0;if ($input->couponId > 0) {$coupon = CouponServices::getInstance()->getCoupon($input->couponId);$couponUser = CouponServices::getInstance()->getCouponUser($input->userCouponId);$is = CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $checkedGoodsPrice);if ($is) {$couponPrice = $coupon->discount;}}// 运费$freightPrice = $this->getFreight($checkedGoodsPrice);// 计算订单金额$orderTotalPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderTotalPrice = bcsub($orderTotalPrice, $couponPrice, 2);$orderTotalPrice = max(0, $orderTotalPrice);$order = Order::new();$order->user_id = $userId;$order->order_sn = $this->generateOrderSn();$order->order_status = OrderEnums::STATUS_CREATE;$order->consignee = $address->name;$order->mobile = $address->tel;$order->address = $address->province.$address->city.$address->county." ".$address->address_detail;$order->message = $input->message;$order->goods_price = $checkedGoodsPrice;$order->freight_price = $freightPrice;$order->integral_price = 0;$order->coupon_price = $couponPrice;$order->order_price = $orderTotalPrice;$order->actual_price = $orderTotalPrice;$order->groupon_price = $grouponPrice;$order->save();// 写入订单商品记录$this->saveOrderGoods($checkedGoodsList, $order->id);// 清理购物车记录CartServices::getInstance()->clearCartGoods($userId, $input->cartId);// 减库存$this->reduceProductsStock($checkedGoodsList);// 添加团购记录GrouponServices::getInstance()->openOrJoinGroupon($userId, $order->id, $input->grouponRulesId,$input->grouponLinkId);// 设置超时任务dispatch(new OrderUnpaidTimeEndJob($userId, $order->id));return $order;}
//此时暂时控制器的代码
// 计算商品总金额$grouponPrice = 0;$checkedGoodsPrice = CartServices::getInstance()->getCartPriceCutGroupon($checkedGoodsList,$input->grouponRulesId,$grouponPrice);// 获取优惠券面额$couponPrice = 0;if ($input->couponId > 0) {$coupon = CouponServices::getInstance()->getCoupon($input->couponId);$couponUser = CouponServices::getInstance()->getCouponUser($input->userCouponId);$is = CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $checkedGoodsPrice);if ($is) {$couponPrice = $coupon->discount;}}// 运费$freightPrice = $this->getFreight($checkedGoodsPrice);// 计算订单金额$orderTotalPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderTotalPrice = bcsub($orderTotalPrice, $couponPrice, 2);$orderTotalPrice = max(0, $orderTotalPrice);
优化后
/*** 提交订单* @param $userId* @param OrderSubmitInput $input* @return Order|void* @throws BusinessException*/public function submit($userId, OrderSubmitInput $input){// 验证团购规则的有效性if (!empty($input->grouponRulesId)) {//校验用户是否可以开启或参与某个团购活动GrouponServices::getInstance()->checkGrouponValid($userId, $input->grouponRulesId);}//获取收获地址$address = AddressServices::getInstance()->getAddress($userId, $input->addressId);//如果获取不到的话抛出异常if (empty($address)) {return $this->throwBadArgumentValue();}// 获取购物车的商品列表 计算总金额需要先获取商品列表$checkedGoodsList = CartServices::getInstance()->getCheckedCartList($userId, $input->cartId);// 计算商品总金额$grouponPrice = 0;$checkedGoodsPrice = CartServices::getInstance()->getCartPriceCutGroupon($checkedGoodsList,$input->grouponRulesId,$grouponPrice);// 获取优惠券面额 逻辑:这里只要用户选中了就代表是最终的选择$couponPrice = 0;if ($input->couponId > 0) {//获取优惠卷信息$coupon = CouponServices::getInstance()->getCoupon($input->couponId);//获取优惠用户$couponUser = CouponServices::getInstance()->getCouponUser($input->userCouponId);$is = CouponServices::getInstance()->checkCouponAndPrice($coupon, $couponUser, $checkedGoodsPrice);if ($is) {$couponPrice = $coupon->discount;}}// 运费$freightPrice = $this->getFreight($checkedGoodsPrice);// 计算订单金额$orderTotalPrice = bcadd($checkedGoodsPrice, $freightPrice, 2);$orderTotalPrice = bcsub($orderTotalPrice, $couponPrice, 2);$orderTotalPrice = max(0, $orderTotalPrice);//保存订单$order = Order::new();$order->user_id = $userId;$order->order_sn = $this->generateOrderSn();$order->order_status = OrderEnums::STATUS_CREATE;$order->consignee = $address->name;$order->mobile = $address->tel;$order->address = $address->province.$address->city.$address->county." ".$address->address_detail;$order->message = $input->message;$order->goods_price = $checkedGoodsPrice;$order->freight_price = $freightPrice;$order->integral_price = 0;$order->coupon_price = $couponPrice;$order->order_price = $orderTotalPrice;$order->actual_price = $orderTotalPrice;$order->groupon_price = $grouponPrice;$order->save();// 写入订单商品记录$this->saveOrderGoods($checkedGoodsList, $order->id);// 清理购物车记录CartServices::getInstance()->clearCartGoods($userId, $input->cartId);// 减库存 这里的减库存的逻辑可以先跳过$this->reduceProductsStock($checkedGoodsList);// 添加团购记录GrouponServices::getInstance()->openOrJoinGroupon($userId, $order->id, $input->grouponRulesId,$input->grouponLinkId);// 设置超时任务dispatch(new OrderUnpaidTimeEndJob($userId, $order->id));return $order;}
public function reduceProductsStock($goodsList){foreach ($goodsList as $cart) {//获取商品的信息$products = GoodsServices::getInstance()->getGoodsProductsByIds($productIds)->keyBy('id');//判断货品信息是否获取到了if (empty($product)) {$this->throwBadArgumentValue();}//判断库存是否充足if ($product->number < $cart->number) {$this->throwBusinessException(CodeResponse::GOODS_NO_STOCK);}//减库存 decrement方法可以自动减库存GoodsProduct::query()->where('id',$product->id)->where('number','>',$cart->number)->decrement('number',$cart->number);}}
PHP订单模块梳理(乐观锁)相关推荐
- 基于Django的乐观锁与悲观锁解决订单并发问题的一点浅见
订单并发这个问题我想大家都是有一定认识的,这里我说一下我的一些浅见,我会尽可能的让大家了解如何解决这类问题. 在解释如何解决订单并发问题之前,需要先了解一下什么是数据库的事务.(我用的是mysql数据 ...
- Django电商项目(八)订单生成、悲观锁、乐观锁
Django电商项目 订单生成 mysql事务 django使用事务 提交订单页面 创建订单后台view 订单生成 mysql事务 事务概念 一组mysql语句,要么执行,要么全不不执行. 事务的特点 ...
- MySQL建表添加乐观锁字段_Java秒杀系统优化-Redis缓存-分布式session-RabbitMQ异步下单-页面静态化...
Java秒杀系统优化-Redis缓存-分布式session-RabbitMQ异步下单-页面静态化 项目介绍 基于SpringBoot+Mybatis搭建的秒杀系统,并且针对高并发场景进行了优化,保证线 ...
- Django电商网站项目(6)--订单模块
设计的订单相关的表如下所示: 由于每一个订单中的商品种类与数量都不定,因此单独将订单商品提出为一个表,为一对多的关系. 订单的提交 从购物车页面提交是通过form形式提交的,在checkbox元素中定 ...
- java乐观锁实现案例
简单说说乐观锁.乐观锁是相对于悲观锁而言.悲观锁认为,这个线程,发生并发的可能性极大,线程冲突几率大,比较悲观.一般用synchronized实现,保证每次操作数据不会冲突.乐观锁认为,线程冲突可能性 ...
- python乐观锁代码实现_Elasticsearch系列—并发控制及乐观锁实现原理
概要 本篇主要介绍一下Elasticsearch的并发控制和乐观锁的实现原理,列举常见的电商场景,关系型数据库的并发控制.ES的并发控制实践. 并发场景 不论是关系型数据库的应用,还是使用Elasti ...
- mysql乐观锁处理超卖_通过乐观锁解决库存超卖的问题
前言 在通过多线程来解决高并发的问题上,线程安全往往是最先需要考虑的问题,其次才是性能.库存超卖问题是有很多种技术解决方案的,比如悲观锁,分布式锁,乐观锁,队列串行化,Redis原子操作等.本篇通过M ...
- [初级]深入理解乐观锁与悲观锁
2019独角兽企业重金招聘Python工程师标准>>> 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔 ...
- mysql乐观锁总结和实践
2019独角兽企业重金招聘Python工程师标准>>> 上一篇文章<MySQL悲观锁总结和实践>谈到了MySQL悲观锁,但是悲观锁并不是适用于任何场景,它也有它存在的一些 ...
最新文章
- [WCF REST] 解决资源并发修改的一个有效的手段:条件更新(Conditional Update)
- android7.1+msm8937双MIC改为单MIC(晓龙相机录像声音小)
- 实战:遇到HTM的文件图标丢失的问题
- apache.camel_Apache Camel 2.9发布–十大变化
- webservice 基本要点
- 记录关于vs2008 和vs2015 的报错问题
- 运用循环判断语句和列表的购物车程序
- python随机生成两个一维数组_如何用python随机产生一个一维数组
- java8 时间类型相关类
- URL重写,asp.net URL重写,URLRewriter.dll下载
- 针式打印机无电脑测试软件,针式打印机断针测试软件合集
- 计算机技术转让增值税,技术转让收入是否交增值税
- 一筐鸡蛋c语言编程,求答案-求答案?一筐鸡蛋?求答案?一筐鸡蛋:1个1个拿,正好拿完2个2 爱问知识人...
- 英语12种记忆单词的方法
- 英国发现巨型失落海底世界:曾生活数万居民
- 利用Eigen库实现最小二乘拟合平面
- DataV-数据-csv文件
- 开源利器分享:BitBar 坐看今天你的项目涨了多少star
- 最炙手可热的行业——大数据就业方向和学习路线图详解
- 信用卡nbsp;nbsp;知识