运费计算优化

This commit is contained in:
Jack 2024-12-05 00:43:53 +08:00
parent aa606ce880
commit 2179f2c09b
7 changed files with 125 additions and 71 deletions

View File

@ -9,30 +9,22 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "CheckoutDTO", description = "CheckoutDTO")
public class OrderCheckoutDTO {
@ApiModelProperty(value = "用户编号")
private Integer user_id;
@ApiModelProperty(value = "是否购物车下单")
private Integer ifcart;
@ApiModelProperty(value = "收货地址编号")
private Integer ud_id = 0;
@ApiModelProperty(value = "下单商品数据:商品编号|数量,商品编号|数量...")
String cart_id = "";
@ApiModelProperty(value = "门店编号")
private Integer chain_id = 0;
@ApiModelProperty(value = "活动编号")
private Integer activity_id = 0;
@ApiModelProperty(value = "团购编号")
private Integer pfgb_id = 0;
@ApiModelProperty(value = "购物车附件")
String cart_file = "";
@ApiModelProperty(value = "用户编号")
private Integer user_id;
@ApiModelProperty(value = "是否购物车下单")
private Integer ifcart;
@ApiModelProperty(value = "收货地址编号")
private Integer ud_id = 0;
@ApiModelProperty(value = "门店编号")
private Integer chain_id = 0;
@ApiModelProperty(value = "活动编号")
private Integer activity_id = 0;
@ApiModelProperty(value = "团购编号")
private Integer pfgb_id = 0;
@ApiModelProperty(value = "购物车附件标记")
private boolean check_flag = false;

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
* Vestibulum commodo. Ut rhoncus gravida arcu.
*/
package com.suisung.mall.common.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "同城配送配送费计算返回实体DTO", description = "同城配送配送费计算返回实体DTO")
public class SameCityDeliveryFeeRespDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "能否配送(在不在配送范围内)")
private Boolean canDelivery;
@ApiModelProperty(value = "是否免运费")
private Boolean isFree;
@ApiModelProperty(value = "原本基础运费")
private BigDecimal baseDeliveryFee;
@ApiModelProperty(value = "减免的运费")
private BigDecimal reduceDeliveryFee;
@ApiModelProperty(value = "最终的运费")
private BigDecimal deliveryFee;
@ApiModelProperty(value = "返回信息")
private String respMsg;
}

View File

@ -25,12 +25,10 @@ import java.util.List;
@ApiModel(value = "同城配送基础设置DTO", description = "同城配送基础设置DTO")
public class ShopStoreSameCityTransportBaseDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "店铺同城快递基础运费设置")
ShopStoreSameCityTransportBase transportBase;
@ApiModelProperty(value = "店铺同城快递运费设置(起送条件+优惠条件)")
public List<ShopStoreSameCityTransport> transportList;
@ApiModelProperty(value = "店铺同城快递基础运费设置")
ShopStoreSameCityTransportBase transportBase;
public void rebuildTransportList() {
if (this.transportBase == null || this.transportList.isEmpty()) {

View File

@ -33,7 +33,7 @@ public class ShopStorePrinterController {
@ApiOperation(value = "内部测试案例", notes = "内部测试案例")
@RequestMapping(value = "/testcase", method = {RequestMethod.GET})
public CommonResult TestCase() {
Object data = shopStoreSameCityTransportBaseService.computeSameCityTransportFee(2L, BigDecimal.valueOf(110.085), BigDecimal.valueOf(23.37), 20000, BigDecimal.valueOf(102), BigDecimal.valueOf(96), BigDecimal.valueOf(94));
Object data = shopStoreSameCityTransportBaseService.computeSameCityTransportFee(2L, BigDecimal.valueOf(120.085), BigDecimal.valueOf(23.37), 20000, BigDecimal.valueOf(102), BigDecimal.valueOf(96), BigDecimal.valueOf(94),true);
return CommonResult.success(data);
}

View File

@ -10,6 +10,7 @@ package com.suisung.mall.shop.store.service;
import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.modules.store.ShopStoreSameCityTransportBase;
import com.suisung.mall.common.pojo.dto.SameCityDeliveryFeeRespDTO;
import com.suisung.mall.common.pojo.dto.ShopStoreSameCityTransportBaseDTO;
import org.springframework.data.util.Pair;
@ -67,8 +68,9 @@ public interface ShopStoreSameCityTransportBaseService {
* @param orderProductAmount 订单商品原价金额
* @param orderDiscountAmount 订单商品折扣金额订单原价减去每个商品折扣费
* @param orderPayAmount 订单实际支付金额折扣金额-优惠券-积分扣-人工干预扣费不包含运费
* @return 是否能配送最终同城配送费
* @param canThrow 能否抛出异常
* @return
*/
Pair<Boolean, BigDecimal> computeSameCityTransportFee(Long storeId, BigDecimal orderLongitude, BigDecimal orderLatitude, Integer weightGram, BigDecimal orderProductAmount, BigDecimal orderDiscountAmount, BigDecimal orderPayAmount);
SameCityDeliveryFeeRespDTO computeSameCityTransportFee(Long storeId, BigDecimal orderLongitude, BigDecimal orderLatitude, Integer weightGram, BigDecimal orderProductAmount, BigDecimal orderDiscountAmount, BigDecimal orderPayAmount, Boolean canThrow);
}

View File

@ -15,11 +15,14 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.constant.CommonConstant;
import com.suisung.mall.common.domain.UserDto;
import com.suisung.mall.common.exception.ApiException;
import com.suisung.mall.common.modules.store.ShopStoreBase;
import com.suisung.mall.common.modules.store.ShopStoreSameCityTransport;
import com.suisung.mall.common.modules.store.ShopStoreSameCityTransportBase;
import com.suisung.mall.common.pojo.dto.SameCityDeliveryFeeRespDTO;
import com.suisung.mall.common.pojo.dto.ShopStoreSameCityTransportBaseDTO;
import com.suisung.mall.common.utils.CommonUtil;
import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.common.utils.PositionUtil;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.chain.controller.admin.ShopChainUserController;
@ -234,11 +237,7 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
}
// 计算订单同城配送费
// 1.先获取订单的经纬度计算配送距离获取配送范围确定是否能配送如果能计算基础运费
// 2.如果能配送再订单总重量订单原价金额订单折后金额订单实付金额
// 3.根据两点经纬度计算配送距离结合订单总重量计算基础运费
// 4.查看是否有运费优惠设置如果有就直接从基础运费中扣除优惠运费得出最终的订单配送费
/**
* 计算同城订单配送费
@ -249,24 +248,37 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
* @param weightGram 重量单位克
* @param orderProductAmount 订单商品原价金额
* @param orderDiscountAmount 订单商品折扣金额订单原价减去每个商品折扣费
* @param orderPayAmount 订单实际支付金额折扣金额-优惠券-积分扣费-人工干预扣费不包含运费
* @return 同城配送费 (是否能配送配送费)
* @param orderPayAmount 订单实际支付金额折扣金额-优惠券-积分扣-人工干预扣费不包含运费
* @param canThrow 不能配送是否抛出异常
* @return
*/
@Override
public Pair<Boolean, BigDecimal> computeSameCityTransportFee(Long storeId, BigDecimal orderLongitude, BigDecimal orderLatitude, Integer weightGram, BigDecimal orderProductAmount, BigDecimal orderDiscountAmount, BigDecimal orderPayAmount) {
public SameCityDeliveryFeeRespDTO computeSameCityTransportFee(Long storeId, BigDecimal orderLongitude, BigDecimal orderLatitude, Integer weightGram, BigDecimal orderProductAmount, BigDecimal orderDiscountAmount, BigDecimal orderPayAmount, Boolean canThrow) {
// 计算订单同城配送费
// 1.先获取订单的经纬度计算配送距离获取配送范围确定是否能配送如果能计算基础运费
// 2.如果能配送再订单总重量订单原价金额订单折后金额订单实付金额
// 3.根据两点经纬度计算配送距离结合订单总重量计算基础运费
// 4.查看是否有运费优惠设置如果有就直接从基础运费中扣除优惠运费得出最终的订单配送费
// 该订单能否配送
Boolean canDelivery = false;
if (CommonUtil.hasAnyBlank(storeId, orderLongitude, orderLatitude) || storeId <= 0) {
logger.error("同城配送费计算:缺少必要的参数");
return Pair.of(canDelivery, BigDecimal.ZERO);
logger.error("同城配送费计算时,缺少必要的参数。");
if (canThrow) {
throw new ApiException(I18nUtil._("同城配送费计算时,缺少必要的参数。"));
}
return new SameCityDeliveryFeeRespDTO(canDelivery, false, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, "同城配送费计算时,缺少必要的参数。");
}
// 获取基础运费设置记录
ShopStoreSameCityTransportBase transportBase = getShopStoreSameCityTransportBaseById(storeId);
if (CommonUtil.hasAnyBlank(transportBase, transportBase.getStore_longitude(), transportBase.getStore_latitude(), transportBase.getDistance_base(), transportBase.getDelivery_base_fee())) {
logger.error("同城配送费计算:无法获取基础运费设置记录,或店铺经纬度为空");
return Pair.of(canDelivery, BigDecimal.ZERO);
if (canThrow) {
throw new ApiException(I18nUtil._("商家未作同城配送设置。"));
}
return new SameCityDeliveryFeeRespDTO(canDelivery, false, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, "商家未作同城配送设置。");
}
// 通过高德或百度地图api计算两点的距离如果服务不可用或无法计算使用内部算法计算距离(单位米)
@ -277,7 +289,7 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
// ### 基础配送费计算
BigDecimal deliveryBaseFee = transportBase.getDelivery_base_fee();
// 每增加一个距离累加运费
if (CommonUtil.isAllNotBlank(transportBase.getDistance_increase_km(), transportBase.getDistance_increase_fee(),transportBase.getDistance_base()) && distance > transportBase.getDistance_base() * 1000) {
if (CommonUtil.isAllNotBlank(transportBase.getDistance_increase_km(), transportBase.getDistance_increase_fee(), transportBase.getDistance_base()) && distance > transportBase.getDistance_base() * 1000) {
// if (transportBase.getDistance_increase_km() != null && transportBase.getDistance_increase_fee() != null && transportBase.getDistance_base() != null && distance > transportBase.getDistance_base() * 1000) {
// 实际配送距离超出基础距离单位km
BigDecimal diffDistanceM = CommonUtil.DecimalRoundHalfUp(BigDecimal.valueOf(distance - transportBase.getDistance_base() * 1000).divide(BigDecimal.valueOf(1000)));
@ -296,7 +308,7 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
}
// 每增加一个重量累加运费重量暂时忽略,配置的时候设置0
if (CommonUtil.isAllNotBlank(transportBase.getWeight_increase_kg(), transportBase.getWeight_increase_fee(),transportBase.getWeight_base()) && weightGram > transportBase.getWeight_base() * 1000) {
if (CommonUtil.isAllNotBlank(transportBase.getWeight_increase_kg(), transportBase.getWeight_increase_fee(), transportBase.getWeight_base()) && weightGram > transportBase.getWeight_base() * 1000) {
// if (transportBase.getWeight_increase_kg() != null && transportBase.getWeight_increase_fee() != null && transportBase.getWeight_base() != null && weightGram > transportBase.getWeight_base() * 1000) {
// 实际配送重量超出基础重量单位kg
BigDecimal diffWeightKg = CommonUtil.DecimalRoundHalfUp(BigDecimal.valueOf(weightGram - transportBase.getWeight_base() * 1000).divide(BigDecimal.valueOf(1000)));
@ -315,8 +327,8 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
// #### 基础配送费计算完毕
// 优惠的配送费重要
BigDecimal deliveryDiscountFee = BigDecimal.ZERO;
// 优惠减免的配送费重要
BigDecimal reduceDeliveryFee = BigDecimal.ZERO;
// 通过配送范围和起送金额决定使用哪个配送费优惠规则
// 获取运费配送范围和优惠信息
@ -324,8 +336,7 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
if (CollUtil.isEmpty(transportList)) {
// 没有配送范围和起配金额规则的时候直接以基础配送费来配送
canDelivery = true;
return Pair.of(canDelivery, deliveryBaseFee);
return new SameCityDeliveryFeeRespDTO(true, false, deliveryBaseFee, BigDecimal.ZERO, deliveryBaseFee, "");
}
for (ShopStoreSameCityTransport transport : transportList) {
@ -339,7 +350,6 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
if ((CommonConstant.Delivery_Amount_Comput_Type_Original.equals(transport.getMin_delivery_amount_type()) && transport.getMin_delivery_amount().compareTo(orderProductAmount) > 0) ||
(CommonConstant.Delivery_Amount_Comput_Type_Discounted.equals(transport.getMin_delivery_amount_type()) && transport.getMin_delivery_amount().compareTo(orderDiscountAmount) > 0) ||
(CommonConstant.Delivery_Amount_Comput_Type_Payment.equals(transport.getMin_delivery_amount_type()) && transport.getMin_delivery_amount().compareTo(orderPayAmount) > 0)) {
// 订单原价金额小于起送金额返回订单不能送达了
canDelivery = false;
continue;
@ -353,20 +363,25 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl<S
if ((CommonConstant.Delivery_Amount_Comput_Type_Original.equals(transport.getDelivery_discount_type()) && transport.getMin_delivery_discount_amount().compareTo(orderProductAmount) <= 0) ||
(CommonConstant.Delivery_Amount_Comput_Type_Discounted.equals(transport.getDelivery_discount_type()) && transport.getMin_delivery_discount_amount().compareTo(orderDiscountAmount) <= 0) ||
(CommonConstant.Delivery_Amount_Comput_Type_Payment.equals(transport.getDelivery_discount_type()) && transport.getMin_delivery_discount_amount().compareTo(orderPayAmount) <= 0)) {
// 订单实付金额小于起送金额返回不送了
if (deliveryDiscountFee.compareTo(transport.getDelivery_discount()) < 0) {
if (reduceDeliveryFee.compareTo(transport.getDelivery_discount()) < 0) {
// 采用优惠最大的一个豁免设置
deliveryDiscountFee = transport.getDelivery_discount();
reduceDeliveryFee = transport.getDelivery_discount();
}
}
}
if(!canDelivery) {
if (canThrow) {
throw new ApiException(I18nUtil._("有订单不在配送范围内,请检查店铺的距离位置!"));
}
return new SameCityDeliveryFeeRespDTO(false, false, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, "有订单不在配送范围内,请检查订单详情!");
}
// 配送费不能负数就是说优惠运费不能高于基础运费
deliveryBaseFee = deliveryBaseFee.subtract(deliveryDiscountFee);
return Pair.of(canDelivery, deliveryBaseFee.compareTo(BigDecimal.ZERO) >= 0 ? deliveryBaseFee : BigDecimal.ZERO);
BigDecimal deliveryFee = deliveryBaseFee.subtract(reduceDeliveryFee);
boolean isFee = deliveryFee.compareTo(BigDecimal.ZERO) <= 0;
return new SameCityDeliveryFeeRespDTO(canDelivery, isFee, deliveryBaseFee, reduceDeliveryFee, isFee ? BigDecimal.ZERO : deliveryFee, "");
}
}

View File

@ -32,6 +32,7 @@ import com.suisung.mall.common.modules.user.ShopUserCart;
import com.suisung.mall.common.modules.user.ShopUserDeliveryAddress;
import com.suisung.mall.common.modules.user.ShopUserVoucher;
import com.suisung.mall.common.pojo.dto.OrderCheckoutDTO;
import com.suisung.mall.common.pojo.dto.SameCityDeliveryFeeRespDTO;
import com.suisung.mall.common.service.MessageService;
import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.common.utils.I18nUtil;
@ -2474,6 +2475,7 @@ public class ShopUserCartServiceImpl extends BaseServiceImpl<ShopUserCartMapper,
throw new ApiException(I18nUtil._("参数有误!"));
}
boolean can_delivery = true;
// 配送方式 5-门店自取10-普通快递16-同城配送
Integer delivery_type_id = Convert.toInt(cart_data.get("delivery_type_id"), StateCode.DELIVERY_TYPE_SAME_CITY);
// 同城配送运费计算分支
@ -2484,13 +2486,8 @@ public class ShopUserCartServiceImpl extends BaseServiceImpl<ShopUserCartMapper,
cart_data.put("delivery_item_none_row", new ArrayList());
//16-同城配送
BigDecimal orderSelProductAmount = Convert.toBigDecimal(cart_data.get("orderSelProductAmount"), BigDecimal.ZERO); //商品订单原价
BigDecimal orderSelFreightAmount = Convert.toBigDecimal(cart_data.get("orderSelFreightAmount"), BigDecimal.ZERO);//选中商品运费金额(仅运费)
BigDecimal orderSelMoneyAmount = Convert.toBigDecimal(cart_data.get("orderSelMoneyAmount"), BigDecimal.ZERO);//选中商品最终应付金额
// BigDecimal orderDiscountAmount = Convert.toBigDecimal(cart_data.get("orderDiscountAmount"), BigDecimal.ZERO);//商品订单折扣优惠金额
// BigDecimal orderSelPointsAmount = Convert.toBigDecimal(cart_data.get("orderSelPointsAmount"), BigDecimal.ZERO);//商品订单积分抵扣金额
// BigDecimal orderSelSpAmount = Convert.toBigDecimal(cart_data.get("orderSelSpAmount"), BigDecimal.ZERO);//可能是商品订单促销金额
// BigDecimal orderRebateAmount = Convert.toBigDecimal(cart_data.get("order_rebate_amount"), BigDecimal.ZERO);//可能是商品订单返利金额
// items一个或多个店铺的订单
List<Map> items = (List<Map>) cart_data.get("items");
@ -2506,29 +2503,40 @@ public class ShopUserCartServiceImpl extends BaseServiceImpl<ShopUserCartMapper,
// 订单最终应付金额
BigDecimal orderPaymentAmount = Convert.toBigDecimal(store_row.get("productMoneySelGoods"));
// 同城配送运费检查和计算
Pair<Boolean, BigDecimal> pair = shopStoreSameCityTransportBaseService.computeSameCityTransportFee(storeId,storeLng,storeLat,0,productMoneyOriginGoods,orderSelDiscountAmount,orderPaymentAmount);
// 同城配送运费检查和计算(只返回数据不能配送不抛异常)
SameCityDeliveryFeeRespDTO sameCityDeliveryFeeResp = shopStoreSameCityTransportBaseService.computeSameCityTransportFee(storeId,storeLng,storeLat,0,productMoneyOriginGoods,orderSelDiscountAmount,orderPaymentAmount,true);
Boolean postFree = pair.getFirst();
store_row.put("postFree", postFree); // 是否免运费
store_row.put("freight", pair.getSecond()); // 运费金额
store_row.put("postFreeBalance", 0); // 还需postFreeBalance元即可免邮费
// 是否能配送在配送范围内
Boolean canDelivery = sameCityDeliveryFeeResp.getCanDelivery();
// 关键订单金额数据
orderSelFreightAmount = orderSelFreightAmount.add(Convert.toBigDecimal(pair.getSecond())); // 重要订单运费金额
if(postFree) {
// 还差 postFreeBalance 元即可免运费
BigDecimal postFreeBalance = BigDecimal.ZERO;
if(!sameCityDeliveryFeeResp.getIsFree()){
postFreeBalance = sameCityDeliveryFeeResp.getBaseDeliveryFee().subtract(sameCityDeliveryFeeResp.getReduceDeliveryFee());
}
store_row.put("postFree", sameCityDeliveryFeeResp.getIsFree()); // 是否免运费
store_row.put("freight", sameCityDeliveryFeeResp.getDeliveryFee()); // 运费金额
store_row.put("postFreeBalance", postFreeBalance); // 还需postFreeBalance元即可免邮费
// 订单的总运费 + 本次订单的运费
orderSelFreightAmount = orderSelFreightAmount.add(Convert.toBigDecimal(sameCityDeliveryFeeResp.getDeliveryFee())); // 重要订单运费金额
if(canDelivery) {
// 如果能配送最终支付金额+本次订单的运费
orderSelMoneyAmount = orderSelMoneyAmount.add(Convert.toBigDecimal(store_row.get("order_money_select_items")));// 重要订单折后金额订单金额-折扣金额
}
}
// 订单总计运费
cart_data.put("orderSelFreightAmount", orderSelFreightAmount);
// 订单总计支付金额
cart_data.put("orderSelMoneyAmount", orderSelMoneyAmount);
// 这些订单是否能配送全局
cart_data.put("can_delivery", can_delivery);
} else {
// 普通快递运费计算和门店自取分支
} else { // 普通快递运费计算和门店自取分支
// 配送不到这个区域商品id
List transport_type_none_ids = new ArrayList();
// 配送不到这个区域商品记录