Commit 8f018e3b9956a2574e0c746c4e2caa0ec4e382b0
1 parent
50f0b9fb
feature:运费模块 计算运费 保存新增模板 查询模板
Showing
2 changed files
with
330 additions
and
0 deletions
sl-express-ms-carriage-service/src/main/java/com/sl/ms/carriage/service/impl/CarriageServiceImpl.java
0 → 100644
| 1 | +package com.sl.ms.carriage.service.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ArrayUtil; | |
| 5 | +import cn.hutool.core.util.EnumUtil; | |
| 6 | +import cn.hutool.core.util.NumberUtil; | |
| 7 | +import cn.hutool.core.util.StrUtil; | |
| 8 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 9 | +import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; | |
| 10 | +import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |
| 11 | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |
| 12 | +import com.sl.ms.base.api.common.AreaFeign; | |
| 13 | +import com.sl.ms.carriage.domain.constant.CarriageConstant; | |
| 14 | +import com.sl.ms.carriage.domain.dto.CarriageDTO; | |
| 15 | +import com.sl.ms.carriage.domain.dto.WaybillDTO; | |
| 16 | +import com.sl.ms.carriage.domain.enums.EconomicRegionEnum; | |
| 17 | +import com.sl.ms.carriage.entity.CarriageEntity; | |
| 18 | +import com.sl.ms.carriage.enums.CarriageExceptionEnum; | |
| 19 | +import com.sl.ms.carriage.mapper.CarriageMapper; | |
| 20 | +import com.sl.ms.carriage.service.CarriageService; | |
| 21 | +import com.sl.ms.carriage.utils.CarriageUtils; | |
| 22 | +import com.sl.transport.common.exception.SLException; | |
| 23 | +import com.sl.transport.common.util.ObjectUtil; | |
| 24 | +import org.jetbrains.annotations.NotNull; | |
| 25 | +import org.springframework.stereotype.Service; | |
| 26 | + | |
| 27 | +import javax.annotation.Resource; | |
| 28 | +import java.math.BigDecimal; | |
| 29 | +import java.math.RoundingMode; | |
| 30 | +import java.util.Arrays; | |
| 31 | +import java.util.Collection; | |
| 32 | +import java.util.LinkedHashMap; | |
| 33 | +import java.util.List; | |
| 34 | +import java.util.stream.Collectors; | |
| 35 | + | |
| 36 | +@Service | |
| 37 | +public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService { | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private AreaFeign areaFeign; | |
| 41 | + | |
| 42 | + private CarriageEntity carriageEntity; | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 新增/修改运费模板 | |
| 46 | + * | |
| 47 | + * @param carriageDto 新增/修改运费对象 | |
| 48 | + * 必填字段:templateType、transportType | |
| 49 | + * 更新时传入id字段 | |
| 50 | + */ | |
| 51 | + @Override | |
| 52 | + public CarriageDTO saveOrUpdate(CarriageDTO carriageDto) { | |
| 53 | +// 校验运费模板是否存在,如果不存在直接插入(查询条件:模板类型 运输类型 如果是修改排除当前id) | |
| 54 | + LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(); | |
| 55 | + queryWrapper.eq(CarriageEntity::getTemplateType, carriageDto.getTemplateType()); | |
| 56 | + queryWrapper.eq(CarriageEntity::getTransportType, carriageDto.getTransportType()); | |
| 57 | + queryWrapper.ne(ObjectUtils.isNotEmpty(carriageDto.getId()), CarriageEntity::getId, carriageDto.getId()); | |
| 58 | + | |
| 59 | + List<CarriageEntity> carriageEntityList = super.list(queryWrapper); | |
| 60 | + | |
| 61 | +// 如果没有重复的模板,可以直接插入或更新操作(DTo转entity 保存成功 entity转DTO) | |
| 62 | + if (CollUtil.isEmpty(carriageEntityList)) { | |
| 63 | + return saveOrUpdateCarriage(carriageDto); | |
| 64 | + } | |
| 65 | + | |
| 66 | +// 如果存在重复模板,需要判断此次插入的是否为经济区互寄,非经济区互寄不可以重复 | |
| 67 | + if (ObjectUtil.notEqual(carriageDto.getTemplateType(), CarriageConstant.ECONOMIC_ZONE)) { | |
| 68 | + throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT); | |
| 69 | + } | |
| 70 | + | |
| 71 | +// 如果是京经济区互寄类型,需要进一步判断关联城市是否重复,通过集合取交集判断是否重复 | |
| 72 | + List<String> associatedCityList = carriageEntityList.stream().map(CarriageEntity::getAssociatedCity) | |
| 73 | + .map(associatedCity -> StrUtil.splitToArray(associatedCity, ",")) | |
| 74 | + .flatMap(Arrays::stream) | |
| 75 | + .collect(Collectors.toList()); | |
| 76 | + Collection<String> intersection = CollUtil.intersection(associatedCityList, carriageDto.getAssociatedCityList()); | |
| 77 | + if (CollUtil.isNotEmpty(intersection)) { | |
| 78 | + throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT); | |
| 79 | + } | |
| 80 | + | |
| 81 | +// 如果没有重复,可以新增或更新(DTO转Entity 保存成功 entity转DTO) | |
| 82 | + return saveOrUpdateCarriage(carriageDto); | |
| 83 | + } | |
| 84 | + | |
| 85 | + private CarriageDTO saveOrUpdateCarriage(CarriageDTO carriageDto) { | |
| 86 | + CarriageEntity carriageEntity = CarriageUtils.toEntity(carriageDto); | |
| 87 | + super.saveOrUpdate(carriageEntity); | |
| 88 | + return CarriageUtils.toDTO(carriageEntity); | |
| 89 | + } | |
| 90 | + | |
| 91 | + /** | |
| 92 | + * 获取全部运费模板 | |
| 93 | + * | |
| 94 | + * @return 运费模板对象列表 | |
| 95 | + */ | |
| 96 | + @Override | |
| 97 | + public List<CarriageDTO> findAll() { | |
| 98 | +// 构造查询条件,按创建时间倒叙 | |
| 99 | + LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(); | |
| 100 | + queryWrapper.orderByDesc(CarriageEntity::getCreated); | |
| 101 | + | |
| 102 | +// 查询数据库 | |
| 103 | + List<CarriageEntity> list = super.list(queryWrapper); | |
| 104 | + | |
| 105 | +// 将结果转换为DTO类型,使用CarriageUtils工具类 | |
| 106 | + return list.stream().map(CarriageUtils::toDTO).collect(Collectors.toList()); | |
| 107 | + } | |
| 108 | + | |
| 109 | + /** | |
| 110 | + * 运费计算 | |
| 111 | + * | |
| 112 | + * @param waybillDTO 运费计算对象 | |
| 113 | + * @return 运费模板对象,不仅包含模板数据还包含:computeWeight、expense 字段 | |
| 114 | + */ | |
| 115 | + @Override | |
| 116 | + public CarriageDTO compute(WaybillDTO waybillDTO) { | |
| 117 | +// 根据参数查找运费模板 调用findCarriage方法 | |
| 118 | + CarriageEntity carriage = findCarriage(waybillDTO); | |
| 119 | + | |
| 120 | +// 计算重量,最小重量为1KG,调用getComputedWeight方法 | |
| 121 | + double computeWeight = getComputeWeight(waybillDTO, carriage); | |
| 122 | + | |
| 123 | +// 计算运费 运费=首重价格 + (实际重量 - 1) * 续重加格 | |
| 124 | + double price = carriage.getFirstWeight() + (computeWeight - 1) * carriage.getContinuousWeight(); | |
| 125 | + | |
| 126 | +// 结果保留一位小数 | |
| 127 | + BigDecimal expense = NumberUtil.round(price, 1); | |
| 128 | + | |
| 129 | +// 封装运费和计算重量到CarriageDTO,并返回 | |
| 130 | + CarriageDTO carriageDTO = CarriageUtils.toDTO(carriage); | |
| 131 | + carriageDTO.setExpense(expense.doubleValue()); | |
| 132 | + carriageDTO.setComputeWeight(computeWeight); | |
| 133 | + | |
| 134 | + return carriageDTO; | |
| 135 | + } | |
| 136 | + | |
| 137 | + /** | |
| 138 | + * 计算重量 | |
| 139 | + * @param waybillDTO | |
| 140 | + * @param carriage | |
| 141 | + * @return | |
| 142 | + */ | |
| 143 | + private double getComputeWeight(WaybillDTO waybillDTO, CarriageEntity carriage) { | |
| 144 | +// 计算体积,如果传入体积则不需要计算 | |
| 145 | + Integer volume = waybillDTO.getVolume(); | |
| 146 | + if (ObjectUtil.isEmpty(volume)) { | |
| 147 | + try { | |
| 148 | + volume = waybillDTO.getMeasureHigh() * waybillDTO.getMeasureLong() * waybillDTO.getMeasureWidth(); | |
| 149 | + } catch (Exception e) { | |
| 150 | + volume = 0; | |
| 151 | + } | |
| 152 | + } | |
| 153 | + | |
| 154 | +// 计算体积重量 = 体积 / 轻抛系数 tips:使用NumberUtil工具类计算 保留一位小数 | |
| 155 | + BigDecimal volumeWeight = NumberUtil.div(volume, carriage.getLightThrowingCoefficient(), 1); | |
| 156 | + | |
| 157 | +// 重量取最大值 = 体积重量和实际重量 tips:使用NumberUtil工具类计算 保留一位小数 | |
| 158 | + double computeWeight = NumberUtil.max(volumeWeight, NumberUtil.round(waybillDTO.getWeight(), 1)).doubleValue(); | |
| 159 | + | |
| 160 | +// 计算续重,规则:不满1kg,按1kg计费 | |
| 161 | + if (computeWeight <= 1) { | |
| 162 | + return 1; | |
| 163 | + } | |
| 164 | + | |
| 165 | +// 10KG一下续重以0.1kg计量保留1位小数 | |
| 166 | + if (computeWeight <= 10) { | |
| 167 | + return computeWeight; | |
| 168 | + } | |
| 169 | + | |
| 170 | +// 100KG 以上四舍五入取整,举例108.4kg按照108收费 108.5kg 按照109KG收费 | |
| 171 | +// tips:使用NumberUtil工具类计算 | |
| 172 | + if (computeWeight >= 100) { | |
| 173 | + return NumberUtil.round(computeWeight, 0).doubleValue(); | |
| 174 | + } | |
| 175 | + | |
| 176 | +// 10-100kg续重以0.5kg计量保留1位小数 | |
| 177 | + int intValue = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).intValue(); | |
| 178 | + | |
| 179 | +// 0.5为一个计量单位,举例:18.8kg按照19收费,18.4kg按照18.5收费,18.1kg按照18.5kg收费 | |
| 180 | + double sub = NumberUtil.sub(computeWeight, intValue); | |
| 181 | + if (sub == 0) { | |
| 182 | + return intValue; | |
| 183 | + } | |
| 184 | + if (sub < 0.5) { | |
| 185 | + return NumberUtil.add(intValue, 0.5); | |
| 186 | + } | |
| 187 | + return NumberUtil.add(intValue, 0.5); | |
| 188 | + } | |
| 189 | + | |
| 190 | + /** | |
| 191 | + * 根据参数查找运费模板 | |
| 192 | + * @param waybillDTO | |
| 193 | + * @return | |
| 194 | + */ | |
| 195 | + private CarriageEntity findCarriage(WaybillDTO waybillDTO) { | |
| 196 | + Long senderCityId = waybillDTO.getSenderCityId(); | |
| 197 | + Long receiverCityId = waybillDTO.getReceiverCityId(); | |
| 198 | + | |
| 199 | +// 如果 发件的城市id 和 收件的城市id相同,查询同城模板 调用findByTemplateType方法 | |
| 200 | + if (ObjectUtil.equal(senderCityId, receiverCityId)) { | |
| 201 | + CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_CITY); | |
| 202 | + if (ObjectUtil.isNotEmpty(carriageEntity)) { | |
| 203 | + return carriageEntity; | |
| 204 | + } | |
| 205 | + } | |
| 206 | + | |
| 207 | +// 如果没有查到或不是同城,则获取收寄地址同省id,使用AreaFeign结构查询 | |
| 208 | + Long senderProvinceId = areaFeign.get(senderCityId).getParentId(); | |
| 209 | + Long receiverProvinceId = areaFeign.get(receiverCityId).getParentId(); | |
| 210 | + | |
| 211 | +// 如果 收发件的省份id相同,查询同省的模板,调用findByTemplate方法 | |
| 212 | + if (ObjectUtil.equal(senderProvinceId, receiverProvinceId)) { | |
| 213 | + CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_PROVINCE); | |
| 214 | + if (ObjectUtil.isNotEmpty(carriageEntity)) { | |
| 215 | + return carriageEntity; | |
| 216 | + } | |
| 217 | + } | |
| 218 | + | |
| 219 | +// 如果没有查到或不是同省,则查询是否为经济区互寄 调用findEconomicCarriage方法查询 | |
| 220 | + CarriageEntity carriageEntity = findEconomicCarriage(senderProvinceId, receiverProvinceId); | |
| 221 | + if (ObjectUtil.isNotEmpty(carriageEntity)) { | |
| 222 | + return carriageEntity; | |
| 223 | + } | |
| 224 | + | |
| 225 | +// 如果没有查到或不是经济区互寄,直接查跨省运费模板 | |
| 226 | + carriageEntity = findByTemplateType(CarriageConstant.TRANS_PROVINCE); | |
| 227 | + if (ObjectUtil.isNotEmpty(carriageEntity)) { | |
| 228 | + return carriageEntity; | |
| 229 | + } | |
| 230 | + | |
| 231 | +// 如果最后没有查到,直接抛自定义异常,提示模板未找到 | |
| 232 | + throw new SLException(CarriageExceptionEnum.NOT_FOUND); | |
| 233 | + } | |
| 234 | + | |
| 235 | + /** | |
| 236 | + * 查询是否为经济区互寄 | |
| 237 | + * @param senderProvinceId | |
| 238 | + * @param receiverProvinceId | |
| 239 | + * @return | |
| 240 | + */ | |
| 241 | + private CarriageEntity findEconomicCarriage(Long senderProvinceId, Long receiverProvinceId) { | |
| 242 | +// 通过工具类EnumUtil 获取经济区城市配置枚举 | |
| 243 | + LinkedHashMap<String, EconomicRegionEnum> enumMap = EnumUtil.getEnumMap(EconomicRegionEnum.class); | |
| 244 | + | |
| 245 | +// 遍历所有经济区枚举值 | |
| 246 | + EconomicRegionEnum economicRegionEnum = null; | |
| 247 | + for (EconomicRegionEnum regionEnum : enumMap.values()) { | |
| 248 | +// 通过ArrayUtil工具类 判断发件网点 和 收件网点是否在同一经济区 | |
| 249 | + boolean containsAll = ArrayUtil.containsAll(regionEnum.getValue(), receiverProvinceId, senderProvinceId); | |
| 250 | + | |
| 251 | +// 如果在得到对应经济区枚举 | |
| 252 | + if (containsAll) { | |
| 253 | + economicRegionEnum = regionEnum; | |
| 254 | + break; | |
| 255 | + } | |
| 256 | + } | |
| 257 | +// 循环遍历未发现所属经济区,方法直接返回null | |
| 258 | + if (ObjectUtil.isNull(economicRegionEnum)) { | |
| 259 | + return null; | |
| 260 | + } | |
| 261 | + | |
| 262 | +// 如果有经济区 根据 模板类型=经济区,运输类型=普快 关联城市=枚举的code值 查询 | |
| 263 | + LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(); | |
| 264 | + queryWrapper.eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE); | |
| 265 | + queryWrapper.eq(CarriageEntity::getTransportType, economicRegionEnum.getCode()); | |
| 266 | + return super.getOne(queryWrapper); | |
| 267 | + } | |
| 268 | + | |
| 269 | + | |
| 270 | + /** | |
| 271 | + * 根据模板类型查询模板,经济区互寄不通过该方法查询模板 | |
| 272 | + * | |
| 273 | + * @param templateType 模板类型:1-同城寄,2-省内寄,4-跨省 | |
| 274 | + * @return 运费模板 | |
| 275 | + */ | |
| 276 | + @Override | |
| 277 | + public CarriageEntity findByTemplateType(Integer templateType) { | |
| 278 | +// 根据模板类型,及运输类型 = CarriageConst.REGULAR_FAST查询模板 | |
| 279 | + LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(); | |
| 280 | + queryWrapper.eq(CarriageEntity::getTemplateType, templateType); | |
| 281 | + queryWrapper.eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST); | |
| 282 | + return super.getOne(queryWrapper); | |
| 283 | + } | |
| 284 | +} | ... | ... |
sl-express-ms-carriage-service/src/test/java/com/sl/ms/carriage/service/impl/CarriageServiceImplTest.java
0 → 100644
| 1 | +package com.sl.ms.carriage.service.impl; | |
| 2 | + | |
| 3 | +import com.sl.ms.carriage.domain.dto.CarriageDTO; | |
| 4 | +import com.sl.ms.carriage.domain.dto.WaybillDTO; | |
| 5 | +import com.sl.ms.carriage.service.CarriageService; | |
| 6 | +import org.junit.jupiter.api.Test; | |
| 7 | +import org.springframework.boot.test.context.SpringBootTest; | |
| 8 | + | |
| 9 | +import javax.annotation.Resource; | |
| 10 | + | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +import static org.junit.jupiter.api.Assertions.*; | |
| 14 | + | |
| 15 | +@SpringBootTest | |
| 16 | +class CarriageServiceImplTest { | |
| 17 | + | |
| 18 | + @Resource | |
| 19 | + private CarriageService carriageService; | |
| 20 | + | |
| 21 | + @Test | |
| 22 | + void saveOrUpdate() { | |
| 23 | + } | |
| 24 | + | |
| 25 | + @Test | |
| 26 | + void findAll() { | |
| 27 | + List<CarriageDTO> all = carriageService.findAll(); | |
| 28 | + all.forEach(System.out::println); | |
| 29 | + } | |
| 30 | + | |
| 31 | + @Test | |
| 32 | + void compute() { | |
| 33 | + WaybillDTO waybillDTO = new WaybillDTO(); | |
| 34 | + waybillDTO.setReceiverCityId(161793L); //上海 | |
| 35 | + waybillDTO.setSenderCityId(2L); //北京 | |
| 36 | + waybillDTO.setWeight(0.8); //重量 | |
| 37 | + waybillDTO.setVolume(1); //体积 | |
| 38 | + | |
| 39 | + CarriageDTO compute = this.carriageService.compute(waybillDTO); | |
| 40 | + System.out.println(compute); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Test | |
| 44 | + void findByTemplateType() { | |
| 45 | + } | |
| 46 | +} | |
| 0 | 47 | \ No newline at end of file | ... | ... |