feat: 足额免配送费
This commit is contained in:
1 parent
be9601ff91
commit
2aa950e5f4
14 files changed
+72
-9
No files matched your search
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `yshop_store_shop`
|
||||
ADD COLUMN `free_delivery_amount` decimal(10,2) NOT NULL DEFAULT '0.00'
|
||||
COMMENT '满额免配送费门槛,0表示不启用' AFTER `delivery_rules`;
|
||||
@@ -13702,6 +13702,7 @@ CREATE TABLE `yshop_store_shop` (
|
||||
`min_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '起送价钱',
|
||||
`delivery_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '配送价格',
|
||||
`delivery_rules` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '配送规则JSON:[{minKm,maxKm,price}]',
|
||||
`free_delivery_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '满额免配送费门槛,0表示不启用',
|
||||
`notice` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '公告',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否营业:0=否,1=是',
|
||||
`admin_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '管理员id',
|
||||
@@ -13718,8 +13719,8 @@ CREATE TABLE `yshop_store_shop` (
|
||||
-- Records of yshop_store_shop
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `yshop_store_shop` VALUES (2, '意向餐饮店', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '源汇区人民东路', '河南省漯河市源汇区团结路50号-20', '2023-08-19 09:00:00', '2023-08-19 23:00:00', '114.04877877439665', '33.57093831826829', 10, 10.00, 20.00, '[{"minKm":0,"maxKm":3,"price":8},{"minKm":3,"maxKm":5,"price":12},{"minKm":5,"maxKm":10,"price":18}]', '新店开张', 1, '126', '1', '2023-08-06 11:48:43', '2023-09-14 17:30:59', '1', '1', b'0');
|
||||
INSERT INTO `yshop_store_shop` VALUES (3, 'yshop', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '67号', '北京市东城区', '2023-08-06 13:35:02', '2023-08-06 15:35:02', '116.40403593222032', '39.91643892263258', 0, 1.00, 2.00, NULL, '2222', 1, '100', '2', '2023-08-06 13:14:53', '2024-03-26 15:42:20', '1', '1', b'0');
|
||||
INSERT INTO `yshop_store_shop` VALUES (2, '意向餐饮店', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '源汇区人民东路', '河南省漯河市源汇区团结路50号-20', '2023-08-19 09:00:00', '2023-08-19 23:00:00', '114.04877877439665', '33.57093831826829', 10, 10.00, 20.00, '[{"minKm":0,"maxKm":3,"price":8},{"minKm":3,"maxKm":5,"price":12},{"minKm":5,"maxKm":10,"price":18}]', 0.00, '新店开张', 1, '126', '1', '2023-08-06 11:48:43', '2023-09-14 17:30:59', '1', '1', b'0');
|
||||
INSERT INTO `yshop_store_shop` VALUES (3, 'yshop', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '67号', '北京市东城区', '2023-08-06 13:35:02', '2023-08-06 15:35:02', '116.40403593222032', '39.91643892263258', 0, 1.00, 2.00, NULL, 0.00, '2222', 1, '100', '2', '2023-08-06 13:14:53', '2024-03-26 15:42:20', '1', '1', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
+3
@@ -234,6 +234,9 @@ public class AppStoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper,Store
|
||||
&& sumPrice.compareTo(storeShopDO.getMinPrice()) < 0) {
|
||||
throw exception(STORE_ORDER_MIN_PRICE_NOT_MET);
|
||||
}
|
||||
if (OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(param.getOrderType())) {
|
||||
postagePrice = DeliveryCalculator.applyFreeDelivery(storeShopDO, sumPrice, postagePrice);
|
||||
}
|
||||
|
||||
//计算优惠券价格
|
||||
if(StrUtil.isNotBlank(param.getCouponId())){
|
||||
|
||||
+4
@@ -67,6 +67,10 @@ public class StoreShopBaseVO {
|
||||
@Schema(description = "配送规则(按距离区间阶梯计费)", example = "[{\"minKm\":1,\"maxKm\":3,\"price\":8}]")
|
||||
private List<DeliveryRule> deliveryRules;
|
||||
|
||||
@Schema(description = "满额免配送费门槛,0或空表示不启用", example = "50")
|
||||
@DecimalMin(value = "0", message = "免配送费门槛不能小于0")
|
||||
private BigDecimal freeDeliveryAmount;
|
||||
|
||||
@Schema(description = "公告", required = true)
|
||||
@NotNull(message = "公告不能为空")
|
||||
private String notice;
|
||||
|
||||
+4
-2
@@ -24,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import static co.yixiang.yshop.framework.common.pojo.CommonResult.success;
|
||||
@@ -80,8 +81,9 @@ public class AppStoreController {
|
||||
@Operation(summary = "预估配送费")
|
||||
public CommonResult<DeliveryFeeRespVO> getDeliveryFee(@RequestParam("shopId") Long shopId,
|
||||
@RequestParam("lng") double lng,
|
||||
@RequestParam("lat") double lat) {
|
||||
return success(appStoreShopService.getDeliveryFee(shopId, lng, lat));
|
||||
@RequestParam("lat") double lat,
|
||||
@RequestParam(value = "amount", required = false) BigDecimal amount) {
|
||||
return success(appStoreShopService.getDeliveryFee(shopId, lng, lat, amount));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
@@ -57,6 +57,9 @@ public class AppStoreShopVO {
|
||||
@Schema(description = "配送规则JSON文本", example = "[{\"minKm\":1,\"maxKm\":3,\"price\":8}]")
|
||||
private String deliveryRules;
|
||||
|
||||
@Schema(description = "满额免配送费门槛,0或空表示不启用", example = "50")
|
||||
private BigDecimal freeDeliveryAmount;
|
||||
|
||||
@Schema(description = "公告", required = true)
|
||||
private String notice;
|
||||
|
||||
|
||||
+4
@@ -84,6 +84,10 @@ public class StoreShopDO extends BaseDO {
|
||||
*/
|
||||
@TableField(typeHandler = DeliveryRuleListTypeHandler.class)
|
||||
private List<DeliveryRule> deliveryRules;
|
||||
/**
|
||||
* 满额免配送费门槛,0或空表示不启用
|
||||
*/
|
||||
private BigDecimal freeDeliveryAmount;
|
||||
/**
|
||||
* 公告
|
||||
*/
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import co.yixiang.yshop.module.store.controller.app.storeshop.vo.DeliveryFeeResp
|
||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,6 @@ public interface AppStoreShopService extends IService<StoreShopDO> {
|
||||
* @param lat 收货地址纬度
|
||||
* @return 配送费及是否超出配送范围
|
||||
*/
|
||||
DeliveryFeeRespVO getDeliveryFee(Long shopId, double lng, double lat);
|
||||
DeliveryFeeRespVO getDeliveryFee(Long shopId, double lng, double lat, BigDecimal amount);
|
||||
|
||||
}
|
||||
+3
-2
@@ -38,7 +38,7 @@ public class AppStoreShopServiceImpl extends ServiceImpl<StoreShopMapper, StoreS
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeliveryFeeRespVO getDeliveryFee(Long shopId, double lng, double lat) {
|
||||
public DeliveryFeeRespVO getDeliveryFee(Long shopId, double lng, double lat, BigDecimal amount) {
|
||||
StoreShopDO shop = getById(shopId);
|
||||
DeliveryFeeRespVO vo = new DeliveryFeeRespVO();
|
||||
vo.setOutOfRange(true);
|
||||
@@ -49,7 +49,8 @@ public class AppStoreShopServiceImpl extends ServiceImpl<StoreShopMapper, StoreS
|
||||
vo.setMaxDistanceKm(shop.getDistance());
|
||||
double distanceKm = DeliveryCalculator.calcDistanceKm(
|
||||
shop.getLng(), shop.getLat(), String.valueOf(lng), String.valueOf(lat));
|
||||
BigDecimal price = DeliveryCalculator.calcDeliveryPrice(shop, distanceKm);
|
||||
BigDecimal price = DeliveryCalculator.applyFreeDelivery(shop, amount,
|
||||
DeliveryCalculator.calcDeliveryPrice(shop, distanceKm));
|
||||
vo.setDistanceKm(distanceKm);
|
||||
if (price != null) {
|
||||
vo.setOutOfRange(false);
|
||||
|
||||
+8
@@ -83,4 +83,12 @@ public class DeliveryCalculator {
|
||||
return shop.getDeliveryPrice();
|
||||
}
|
||||
|
||||
public static BigDecimal applyFreeDelivery(StoreShopDO shop, BigDecimal orderAmount,
|
||||
BigDecimal deliveryPrice) {
|
||||
BigDecimal threshold = shop.getFreeDeliveryAmount();
|
||||
return deliveryPrice != null && orderAmount != null && threshold != null
|
||||
&& threshold.signum() > 0 && orderAmount.compareTo(threshold) >= 0
|
||||
? BigDecimal.ZERO : deliveryPrice;
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package co.yixiang.yshop.module.store.service.storeshop;
|
||||
|
||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class DeliveryCalculatorTest {
|
||||
|
||||
@Test
|
||||
void applyFreeDeliveryUsesConfiguredThreshold() {
|
||||
StoreShopDO shop = StoreShopDO.builder()
|
||||
.freeDeliveryAmount(new BigDecimal("30"))
|
||||
.build();
|
||||
BigDecimal fee = new BigDecimal("8");
|
||||
|
||||
assertEquals(fee, DeliveryCalculator.applyFreeDelivery(shop, new BigDecimal("29.99"), fee));
|
||||
assertEquals(BigDecimal.ZERO,
|
||||
DeliveryCalculator.applyFreeDelivery(shop, new BigDecimal("30"), fee));
|
||||
assertNull(DeliveryCalculator.applyFreeDelivery(shop, new BigDecimal("30"), null));
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ const amount = computed(() =>{
|
||||
})
|
||||
|
||||
// 选地址/切换外卖后预估配送费(基于收货地址距离匹配配送规则)
|
||||
watch([address, store, orderType], async () => {
|
||||
watch([address, store, orderType, total], async () => {
|
||||
if (orderType.value !== 'takeout' || !store.value.id) {
|
||||
deliveryFee.value = 0
|
||||
outOfRange.value = false
|
||||
@@ -183,7 +183,8 @@ watch([address, store, orderType], async () => {
|
||||
const res = await getDeliveryFee({
|
||||
shopId: store.value.id,
|
||||
lng: address.value.longitude,
|
||||
lat: address.value.latitude
|
||||
lat: address.value.latitude,
|
||||
amount: total.value.toFixed(2)
|
||||
})
|
||||
outOfRange.value = !!res.outOfRange
|
||||
deliveryFee.value = parseFloat(res.deliveryPrice) || 0
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface ShopVO {
|
||||
minPrice: number
|
||||
deliveryPrice: number
|
||||
deliveryRules: DeliveryRule[]
|
||||
freeDeliveryAmount?: number
|
||||
notice: string
|
||||
status: boolean
|
||||
adminId: string
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
<el-form-item label="默认配送费" prop="deliveryPrice">
|
||||
<el-input v-model="formData.deliveryPrice" placeholder="未配置规则或无匹配时的兜底配送费" />
|
||||
</el-form-item>
|
||||
<el-form-item label="满额免配送费">
|
||||
<el-input-number v-model="formData.freeDeliveryAmount" :min="0" :precision="2" />
|
||||
<span class="delivery-rule-tip">元,0或不填表示不启用</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="配送规则">
|
||||
<div style="width: 100%;">
|
||||
<div v-for="(rule, index) in formData.deliveryRules" :key="index" class="delivery-rule-row">
|
||||
@@ -127,6 +131,7 @@ const formData = ref<any>({
|
||||
minPrice: undefined,
|
||||
deliveryPrice: undefined,
|
||||
deliveryRules: [],
|
||||
freeDeliveryAmount: undefined,
|
||||
notice: undefined,
|
||||
status: undefined,
|
||||
adminId: undefined,
|
||||
@@ -278,6 +283,7 @@ const resetForm = () => {
|
||||
minPrice: undefined,
|
||||
deliveryPrice: undefined,
|
||||
deliveryRules: [],
|
||||
freeDeliveryAmount: undefined,
|
||||
notice: undefined,
|
||||
status: 1,
|
||||
adminId: undefined,
|
||||
|
||||
Reference in new issue
Block a user