配送页面字段内容调整
This commit is contained in:
@@ -102,7 +102,7 @@ public class VehicleTypeController extends BaseController
|
|||||||
*/
|
*/
|
||||||
@PreAuthorize("@ss.hasPermi('document:type:remove')")
|
@PreAuthorize("@ss.hasPermi('document:type:remove')")
|
||||||
@Log(title = "车型定义", businessType = BusinessType.DELETE)
|
@Log(title = "车型定义", businessType = BusinessType.DELETE)
|
||||||
@DeleteMapping("/{ids}")
|
@DeleteMapping("/{ids:\\d+}")
|
||||||
public AjaxResult remove(@PathVariable Long[] ids)
|
public AjaxResult remove(@PathVariable Long[] ids)
|
||||||
{
|
{
|
||||||
return toAjax(vehicleTypeService.deleteVehicleTypeByIds(ids));
|
return toAjax(vehicleTypeService.deleteVehicleTypeByIds(ids));
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ public class DeliveryOrder extends BaseEntity {
|
|||||||
private String orderStatus;
|
private String orderStatus;
|
||||||
|
|
||||||
/** 车辆类型ID */
|
/** 车辆类型ID */
|
||||||
private Long vehicleTypeId;
|
private String vehicleTypeId;
|
||||||
|
|
||||||
/** 车辆类型名称 */
|
/** 车辆类型名称 */
|
||||||
@Excel(name = "车辆类型名称")
|
@Excel(name = "车辆类型名称")
|
||||||
@@ -312,8 +312,8 @@ public class DeliveryOrder extends BaseEntity {
|
|||||||
public String getOrderStatus() { return orderStatus; }
|
public String getOrderStatus() { return orderStatus; }
|
||||||
public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; }
|
public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; }
|
||||||
|
|
||||||
public Long getVehicleTypeId() { return vehicleTypeId; }
|
public String getVehicleTypeId() { return vehicleTypeId; }
|
||||||
public void setVehicleTypeId(Long vehicleTypeId) { this.vehicleTypeId = vehicleTypeId; }
|
public void setVehicleTypeId(String vehicleTypeId) { this.vehicleTypeId = vehicleTypeId; }
|
||||||
|
|
||||||
public String getVehicleTypeName() { return vehicleTypeName; }
|
public String getVehicleTypeName() { return vehicleTypeName; }
|
||||||
public void setVehicleTypeName(String vehicleTypeName) { this.vehicleTypeName = vehicleTypeName; }
|
public void setVehicleTypeName(String vehicleTypeName) { this.vehicleTypeName = vehicleTypeName; }
|
||||||
|
|||||||
@@ -26,5 +26,6 @@ public class CalcSuggestFeeDTO {
|
|||||||
private BigDecimal distanceKm;
|
private BigDecimal distanceKm;
|
||||||
|
|
||||||
/** 指定车型ID(可选;传入则按该车型计算建议费用) */
|
/** 指定车型ID(可选;传入则按该车型计算建议费用) */
|
||||||
private Long vehicleTypeId;
|
private String vehicleTypeId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DeliveryOrderCreateDTO {
|
|||||||
private String orderStatus;
|
private String orderStatus;
|
||||||
|
|
||||||
/** 车型 ID */
|
/** 车型 ID */
|
||||||
private Long vehicleTypeId;
|
private String vehicleTypeId;
|
||||||
|
|
||||||
/** 车型名称 */
|
/** 车型名称 */
|
||||||
private String vehicleTypeName;
|
private String vehicleTypeName;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// src/main/java/com/delivery/project/document/domain/vo/CalcSuggestFeeVO.java
|
|
||||||
package com.delivery.project.document.domain.vo;
|
package com.delivery.project.document.domain.vo;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -6,58 +5,93 @@ import java.math.BigDecimal;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算建议费用 - 响应体 VO
|
* 计算建议费用 返回VO
|
||||||
* 返回推荐/选定的车型、单价、建议费用,以及候选车型列表。
|
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class CalcSuggestFeeVO {
|
public class CalcSuggestFeeVO {
|
||||||
|
|
||||||
/** 选定/推荐车型ID */
|
/** 是否存在可用车型 */
|
||||||
private Long vehicleTypeId;
|
|
||||||
|
|
||||||
/** 选定/推荐车型名称 */
|
|
||||||
private String vehicleTypeName;
|
|
||||||
|
|
||||||
/** 每公里单价(单位:元/公里) */
|
|
||||||
private BigDecimal unitPricePerKm;
|
|
||||||
|
|
||||||
/** 建议费用(单位:元,四舍五入保留2位小数) */
|
|
||||||
private BigDecimal suggestFee;
|
|
||||||
|
|
||||||
/** 错误信息 */
|
|
||||||
private String errorMessage;
|
|
||||||
|
|
||||||
/** 是否有适配车型 */
|
|
||||||
private Boolean hasSuitableType;
|
private Boolean hasSuitableType;
|
||||||
|
|
||||||
/** 候选车型列表(按价格/容量排序) */
|
/** 错误信息(无车型时返回) */
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
/** 推荐总费用 */
|
||||||
|
private BigDecimal suggestFee;
|
||||||
|
|
||||||
|
/** 总车辆数 */
|
||||||
|
private Integer totalVehicleCount;
|
||||||
|
|
||||||
|
/** 货物重量 */
|
||||||
|
private BigDecimal totalWeightTon;
|
||||||
|
|
||||||
|
/** 货物体积 */
|
||||||
|
private BigDecimal totalVolumeM3;
|
||||||
|
|
||||||
|
/** 推荐车辆组合方案 */
|
||||||
|
private List<VehiclePlanVO> bestPlan;
|
||||||
|
|
||||||
|
/** 候选车型列表 */
|
||||||
private List<VehicleTypeOptionVO> candidates;
|
private List<VehicleTypeOptionVO> candidates;
|
||||||
|
/** 提示信息(不阻断) */
|
||||||
|
private String warningMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 候选车型项 VO
|
* 推荐车辆组合
|
||||||
* 表示一条可选车型的基本信息。
|
*/
|
||||||
|
@Data
|
||||||
|
public static class VehiclePlanVO {
|
||||||
|
|
||||||
|
/** 车型ID */
|
||||||
|
private String vehicleTypeId;
|
||||||
|
|
||||||
|
/** 车型名称 */
|
||||||
|
private String vehicleTypeName;
|
||||||
|
|
||||||
|
/** 车辆数量 */
|
||||||
|
private Integer vehicleCount;
|
||||||
|
|
||||||
|
/** 每公里单价 */
|
||||||
|
private BigDecimal unitPricePerKm;
|
||||||
|
|
||||||
|
/** 单车费用 */
|
||||||
|
private BigDecimal singleVehicleFee;
|
||||||
|
|
||||||
|
/** 该车型总费用 */
|
||||||
|
private BigDecimal totalFee;
|
||||||
|
|
||||||
|
/** 单车最大承重 */
|
||||||
|
private BigDecimal weightMaxTon;
|
||||||
|
|
||||||
|
/** 单车最大体积 */
|
||||||
|
private BigDecimal volumeMaxM3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 候选车型
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class VehicleTypeOptionVO {
|
public static class VehicleTypeOptionVO {
|
||||||
|
|
||||||
/** 车型ID */
|
/** 车型ID */
|
||||||
private Long id;
|
private String id;
|
||||||
|
|
||||||
/** 车型名称 */
|
/** 车型名称 */
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/** 每公里单价(单位:元/公里) */
|
/** 每公里单价 */
|
||||||
private BigDecimal unitPricePerKm;
|
private BigDecimal unitPricePerKm;
|
||||||
|
|
||||||
/** 承重下限(单位:吨,含) */
|
/** 最小承重 */
|
||||||
private BigDecimal weightMinTon;
|
private BigDecimal weightMinTon;
|
||||||
|
|
||||||
/** 承重上限(单位:吨,含) */
|
/** 最大承重 */
|
||||||
private BigDecimal weightMaxTon;
|
private BigDecimal weightMaxTon;
|
||||||
|
|
||||||
/** 载方下限(单位:立方米,含) */
|
/** 最小体积 */
|
||||||
private BigDecimal volumeMinM3;
|
private BigDecimal volumeMinM3;
|
||||||
|
|
||||||
/** 载方上限(单位:吨,含) */
|
/** 最大体积 */
|
||||||
private BigDecimal volumeMaxM3;
|
private BigDecimal volumeMaxM3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ public class DeliveryOrderVo {
|
|||||||
private String orderStatus;
|
private String orderStatus;
|
||||||
|
|
||||||
/** 车型 ID */
|
/** 车型 ID */
|
||||||
private Long vehicleTypeId;
|
private String vehicleTypeId;
|
||||||
|
|
||||||
/** 车型名称 */
|
/** 车型名称 */
|
||||||
private String vehicleTypeName;
|
private String vehicleTypeName;
|
||||||
|
|||||||
@@ -113,4 +113,6 @@ public interface DeliveryOrderMapper
|
|||||||
|
|
||||||
/** 配送单VO列表 */
|
/** 配送单VO列表 */
|
||||||
List<DeliveryOrderVo> selectDeliveryOrderVoList(DeliveryOrder deliveryOrder);
|
List<DeliveryOrderVo> selectDeliveryOrderVoList(DeliveryOrder deliveryOrder);
|
||||||
|
/** 防止重复创建配送单 */
|
||||||
|
List<Long> selectExistsRkRecordIds(@Param("rkRecordIds") List<Long> rkRecordIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,4 +77,8 @@ public interface VehicleTypeMapper
|
|||||||
* @return 批量结果
|
* @return 批量结果
|
||||||
*/
|
*/
|
||||||
List<VehicleType> selectFallbackTypes(BigDecimal w, BigDecimal v);
|
List<VehicleType> selectFallbackTypes(BigDecimal w, BigDecimal v);
|
||||||
|
/**
|
||||||
|
* 1 查询所有可用车型
|
||||||
|
* */
|
||||||
|
List<VehicleType> selectUsableTypes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,4 +67,6 @@ public interface IVehicleTypeService
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
CalcSuggestFeeVO calcSuggestFee(CalcSuggestFeeDTO dto);
|
CalcSuggestFeeVO calcSuggestFee(CalcSuggestFeeDTO dto);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,17 +186,37 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
|
|||||||
throw new ServiceException("物料明细不能为空");
|
throw new ServiceException("物料明细不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
String billNo = dto.getItems().get(0).getBillNo();
|
List<DeliveryOrderLineDTO> items = dto.getItems();
|
||||||
boolean fromWms = StringUtils.isNotBlank(billNo);
|
|
||||||
|
|
||||||
List<Long> rkRecordIds = dto.getItems().stream()
|
boolean fromWms = items.stream().anyMatch(it -> StringUtils.isNotBlank(it.getBillNo()));
|
||||||
|
|
||||||
|
List<Long> rkRecordIds = items.stream()
|
||||||
.map(DeliveryOrderLineDTO::getRkRecordId)
|
.map(DeliveryOrderLineDTO::getRkRecordId)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (fromWms && rkRecordIds.isEmpty()) {
|
if (fromWms) {
|
||||||
throw new ServiceException("明细行缺少 rk_record 主键 id");
|
for (DeliveryOrderLineDTO it : items) {
|
||||||
|
if (StringUtils.isBlank(it.getBillNo())) {
|
||||||
|
throw new ServiceException("WMS来源明细缺少 billNo");
|
||||||
|
}
|
||||||
|
if (it.getRkRecordId() == null) {
|
||||||
|
throw new ServiceException("WMS来源明细缺少 rk_record_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rkRecordIds.isEmpty()) {
|
||||||
|
throw new ServiceException("明细行缺少 rk_record 主键 id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 新增:防止重复创建配送单 =====
|
||||||
|
if (!rkRecordIds.isEmpty()) {
|
||||||
|
List<Long> existIds = deliveryOrderMapper.selectExistsRkRecordIds(rkRecordIds);
|
||||||
|
if (existIds != null && !existIds.isEmpty()) {
|
||||||
|
throw new ServiceException("存在已生成配送单的明细,不能重复创建,明细ID:" + existIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String orderNo = StringUtils.isBlank(dto.getOrderNo())
|
String orderNo = StringUtils.isBlank(dto.getOrderNo())
|
||||||
@@ -208,19 +228,9 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
|
|||||||
Long currentUserId = SecurityUtils.getUserId();
|
Long currentUserId = SecurityUtils.getUserId();
|
||||||
Long makerId = dto.getMakerId() != null ? dto.getMakerId() : currentUserId;
|
Long makerId = dto.getMakerId() != null ? dto.getMakerId() : currentUserId;
|
||||||
|
|
||||||
List<DeliveryOrder> rows = new ArrayList<>();
|
List<DeliveryOrder> rows = new ArrayList<>(items.size());
|
||||||
|
|
||||||
for (DeliveryOrderLineDTO it : dto.getItems()) {
|
|
||||||
|
|
||||||
if (fromWms) {
|
|
||||||
if (!billNo.equals(it.getBillNo())) {
|
|
||||||
throw new ServiceException("暂不支持多单合并配送");
|
|
||||||
}
|
|
||||||
if (it.getRkRecordId() == null) {
|
|
||||||
throw new ServiceException("明细缺少 rk_record_id");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (DeliveryOrderLineDTO it : items) {
|
||||||
DeliveryOrder row = new DeliveryOrder();
|
DeliveryOrder row = new DeliveryOrder();
|
||||||
|
|
||||||
row.setOrderNo(orderNo);
|
row.setOrderNo(orderNo);
|
||||||
@@ -244,7 +254,6 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
|
|||||||
row.setMakerId(makerId);
|
row.setMakerId(makerId);
|
||||||
row.setReceiveStatus(dto.getReceiveStatus());
|
row.setReceiveStatus(dto.getReceiveStatus());
|
||||||
row.setReceiveProblem(dto.getReceiveProblem());
|
row.setReceiveProblem(dto.getReceiveProblem());
|
||||||
|
|
||||||
row.setOrderStatus(StringUtils.defaultIfBlank(dto.getOrderStatus(), "1"));
|
row.setOrderStatus(StringUtils.defaultIfBlank(dto.getOrderStatus(), "1"));
|
||||||
|
|
||||||
row.setVehicleTypeId(dto.getVehicleTypeId());
|
row.setVehicleTypeId(dto.getVehicleTypeId());
|
||||||
@@ -255,10 +264,8 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
|
|||||||
row.setTotalKm(dto.getTotalKm());
|
row.setTotalKm(dto.getTotalKm());
|
||||||
row.setRemark(dto.getRemark());
|
row.setRemark(dto.getRemark());
|
||||||
|
|
||||||
// 🔥 关键改造点
|
|
||||||
row.setRkRecordId(it.getRkRecordId());
|
row.setRkRecordId(it.getRkRecordId());
|
||||||
row.setBillNo(it.getBillNo());
|
row.setBillNo(it.getBillNo());
|
||||||
|
|
||||||
row.setXmMs(it.getXmMs());
|
row.setXmMs(it.getXmMs());
|
||||||
row.setXmNo(it.getXmNo());
|
row.setXmNo(it.getXmNo());
|
||||||
row.setWlNo(it.getWlNo());
|
row.setWlNo(it.getWlNo());
|
||||||
@@ -276,7 +283,10 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!rows.isEmpty()) {
|
if (!rows.isEmpty()) {
|
||||||
deliveryOrderMapper.batchInsert(rows);
|
int insertRows = deliveryOrderMapper.batchInsert(rows);
|
||||||
|
if (insertRows <= 0) {
|
||||||
|
throw new ServiceException("配送单保存失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromWms) {
|
if (fromWms) {
|
||||||
|
|||||||
@@ -1,228 +1,473 @@
|
|||||||
package com.delivery.project.document.service.impl;
|
package com.delivery.project.document.service.impl;
|
||||||
|
|
||||||
|
import com.delivery.common.exception.ServiceException;
|
||||||
|
import com.delivery.common.utils.StringUtils;
|
||||||
|
import com.delivery.project.document.domain.VehicleType;
|
||||||
|
import com.delivery.project.document.domain.dto.CalcSuggestFeeDTO;
|
||||||
|
import com.delivery.project.document.domain.vo.CalcSuggestFeeVO;
|
||||||
|
import com.delivery.project.document.mapper.VehicleTypeMapper;
|
||||||
|
import com.delivery.project.document.service.IVehicleTypeService;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
import java.util.stream.Collectors;
|
||||||
import java.util.List;
|
|
||||||
import com.delivery.common.utils.DateUtils;
|
|
||||||
import com.delivery.project.document.domain.dto.CalcSuggestFeeDTO;
|
|
||||||
import com.delivery.project.document.domain.vo.CalcSuggestFeeVO;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import com.delivery.project.document.mapper.VehicleTypeMapper;
|
|
||||||
import com.delivery.project.document.domain.VehicleType;
|
|
||||||
import com.delivery.project.document.service.IVehicleTypeService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 车型定义Service业务层处理
|
|
||||||
*
|
|
||||||
* @author delivery
|
|
||||||
* @date 2025-10-23
|
|
||||||
*/
|
|
||||||
@Service
|
@Service
|
||||||
public class VehicleTypeServiceImpl implements IVehicleTypeService
|
public class VehicleTypeServiceImpl implements IVehicleTypeService {
|
||||||
{
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private VehicleTypeMapper vehicleTypeMapper;
|
private VehicleTypeMapper vehicleTypeMapper;
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询车型定义
|
|
||||||
*
|
|
||||||
* @param id 车型定义主键
|
|
||||||
* @return 车型定义
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public VehicleType selectVehicleTypeById(Long id)
|
public VehicleType selectVehicleTypeById(Long id) {
|
||||||
{
|
|
||||||
return vehicleTypeMapper.selectVehicleTypeById(id);
|
return vehicleTypeMapper.selectVehicleTypeById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询车型定义列表
|
|
||||||
*
|
|
||||||
* @param vehicleType 车型定义
|
|
||||||
* @return 车型定义
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public List<VehicleType> selectVehicleTypeList(VehicleType vehicleType)
|
public List<VehicleType> selectVehicleTypeList(VehicleType vehicleType) {
|
||||||
{
|
|
||||||
return vehicleTypeMapper.selectVehicleTypeList(vehicleType);
|
return vehicleTypeMapper.selectVehicleTypeList(vehicleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增车型定义
|
|
||||||
*
|
|
||||||
* @param vehicleType 车型定义
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int insertVehicleType(VehicleType vehicleType)
|
public int insertVehicleType(VehicleType vehicleType) {
|
||||||
{
|
|
||||||
vehicleType.setCreateTime(DateUtils.getNowDate());
|
|
||||||
return vehicleTypeMapper.insertVehicleType(vehicleType);
|
return vehicleTypeMapper.insertVehicleType(vehicleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改车型定义
|
|
||||||
*
|
|
||||||
* @param vehicleType 车型定义
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int updateVehicleType(VehicleType vehicleType)
|
public int updateVehicleType(VehicleType vehicleType) {
|
||||||
{
|
|
||||||
vehicleType.setUpdateTime(DateUtils.getNowDate());
|
|
||||||
return vehicleTypeMapper.updateVehicleType(vehicleType);
|
return vehicleTypeMapper.updateVehicleType(vehicleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除车型定义
|
|
||||||
*
|
|
||||||
* @param ids 需要删除的车型定义主键
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int deleteVehicleTypeByIds(Long[] ids)
|
public int deleteVehicleTypeByIds(Long[] ids) {
|
||||||
{
|
|
||||||
return vehicleTypeMapper.deleteVehicleTypeByIds(ids);
|
return vehicleTypeMapper.deleteVehicleTypeByIds(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除车型定义信息
|
|
||||||
*
|
|
||||||
* @param id 车型定义主键
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int deleteVehicleTypeById(Long id)
|
public int deleteVehicleTypeById(Long id) {
|
||||||
{
|
|
||||||
return vehicleTypeMapper.deleteVehicleTypeById(id);
|
return vehicleTypeMapper.deleteVehicleTypeById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算建议运费,推荐车型
|
* 计算建议费用
|
||||||
* @param dto
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CalcSuggestFeeVO calcSuggestFee(CalcSuggestFeeDTO dto)
|
public CalcSuggestFeeVO calcSuggestFee(CalcSuggestFeeDTO dto) {
|
||||||
{
|
|
||||||
// ========== 0) 入参兜底 ==========
|
|
||||||
BigDecimal w = nvl(dto.getWeightTon()); // 货物重量(吨)
|
|
||||||
BigDecimal v = nvl(dto.getVolumeM3()); // 货物体积(立方米)
|
|
||||||
BigDecimal km = nvl(dto.getDistanceKm()); // 行程公里数(公里)
|
|
||||||
|
|
||||||
// ========== 1) 候选车型:严格匹配,若无则兜底匹配 ==========
|
BigDecimal weight = nvl(dto.getWeightTon());
|
||||||
List<VehicleType> candidates = vehicleTypeMapper.selectMatchTypes(w, v);
|
BigDecimal volume = nvl(dto.getVolumeM3());
|
||||||
if (candidates == null || candidates.isEmpty()) {
|
BigDecimal km = nvl(dto.getDistanceKm());
|
||||||
candidates = vehicleTypeMapper.selectFallbackTypes(w, v);
|
|
||||||
|
if (km.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new ServiceException("公里数必须大于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 2) 检查是否存在适配车型 ==========
|
List<VehicleType> vehicleTypes = vehicleTypeMapper.selectUsableTypes();
|
||||||
if (candidates == null || candidates.isEmpty()) {
|
|
||||||
// 如果没有适配车型,检查系统中是否至少存在一个车型配置
|
if (vehicleTypes == null || vehicleTypes.isEmpty()) {
|
||||||
List<VehicleType> allVehicleTypes = vehicleTypeMapper.selectVehicleTypeList(new VehicleType());
|
throw new ServiceException("没有配置车型");
|
||||||
if (allVehicleTypes == null || allVehicleTypes.isEmpty()) {
|
}
|
||||||
CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
|
|
||||||
vo.setErrorMessage("系统中暂无车型配置,请先配置车型信息。");
|
// 构建算法节点
|
||||||
vo.setHasSuitableType(false);
|
List<VehicleNode> nodes = vehicleTypes.stream()
|
||||||
return vo;
|
.map(v -> buildNode(v, km))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 按费用排序
|
||||||
|
nodes.sort(Comparator.comparing(VehicleNode::getSingleFee));
|
||||||
|
|
||||||
|
SearchContext ctx = new SearchContext(weight, volume);
|
||||||
|
|
||||||
|
dfs(nodes,
|
||||||
|
0,
|
||||||
|
BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO,
|
||||||
|
0,
|
||||||
|
new ArrayList<>(),
|
||||||
|
ctx);
|
||||||
|
CalcSuggestFeeVO calcSuggestFeeVO = buildResult(ctx,dto);
|
||||||
|
|
||||||
|
return calcSuggestFeeVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DFS搜索最佳组合(最优费用 + 同价最少车)
|
||||||
|
*/
|
||||||
|
private void dfs(List<VehicleNode> nodes,
|
||||||
|
int index,
|
||||||
|
BigDecimal weight,
|
||||||
|
BigDecimal volume,
|
||||||
|
BigDecimal fee,
|
||||||
|
int vehicleCount,
|
||||||
|
List<VehiclePlan> plan,
|
||||||
|
SearchContext ctx) {
|
||||||
|
|
||||||
|
// 1. 剪枝:当前费用已经更差
|
||||||
|
if (ctx.bestFee != null && fee.compareTo(ctx.bestFee) > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 满足需求
|
||||||
|
if (weight.compareTo(ctx.targetWeight) >= 0 &&
|
||||||
|
volume.compareTo(ctx.targetVolume) >= 0) {
|
||||||
|
|
||||||
|
if (ctx.bestFee == null
|
||||||
|
|| fee.compareTo(ctx.bestFee) < 0
|
||||||
|
|| (fee.compareTo(ctx.bestFee) == 0
|
||||||
|
&& vehicleCount < ctx.bestVehicleCount)) {
|
||||||
|
|
||||||
|
ctx.bestFee = fee;
|
||||||
|
ctx.bestVehicleCount = vehicleCount;
|
||||||
|
ctx.bestPlan = new ArrayList<>(plan);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 递归结束
|
||||||
|
if (index >= nodes.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VehicleNode node = nodes.get(index);
|
||||||
|
|
||||||
|
int max = calculateMaxCount(node, ctx.targetWeight, ctx.targetVolume);
|
||||||
|
|
||||||
|
// 4. 从多到少尝试
|
||||||
|
for (int i = max; i >= 0; i--) {
|
||||||
|
|
||||||
|
BigDecimal addWeight = node.weightMaxTon.multiply(BigDecimal.valueOf(i));
|
||||||
|
BigDecimal addVolume = node.volumeMaxM3.multiply(BigDecimal.valueOf(i));
|
||||||
|
BigDecimal addFee = node.singleFee.multiply(BigDecimal.valueOf(i));
|
||||||
|
|
||||||
|
List<VehiclePlan> newPlan = new ArrayList<>(plan);
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
VehiclePlan p = new VehiclePlan();
|
||||||
|
p.vehicleTypeId = node.id;
|
||||||
|
p.vehicleTypeName = node.typeName;
|
||||||
|
p.count = i;
|
||||||
|
p.singleFee = node.singleFee;
|
||||||
|
p.totalFee = addFee;
|
||||||
|
p.weightMaxTon = node.weightMaxTon;
|
||||||
|
p.volumeMaxM3 = node.volumeMaxM3;
|
||||||
|
newPlan.add(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 找到最大承载能力的车型
|
dfs(nodes,
|
||||||
VehicleType maxCapacityType = allVehicleTypes.stream()
|
index + 1,
|
||||||
.filter(type -> "0".equals(type.getStatus()) && !"1".equals(type.getIsDelete()))
|
weight.add(addWeight),
|
||||||
.max(Comparator
|
volume.add(addVolume),
|
||||||
.comparing(VehicleType::getWeightMaxTon, Comparator.nullsLast(BigDecimal::compareTo))
|
fee.add(addFee),
|
||||||
.thenComparing(VehicleType::getVolumeMaxM3, Comparator.nullsLast(BigDecimal::compareTo)))
|
vehicleCount + i,
|
||||||
.orElse(null);
|
newPlan,
|
||||||
|
ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (maxCapacityType != null) {
|
/**
|
||||||
// 检查最大容量车型是否仍无法满足要求
|
* 计算最多车辆数
|
||||||
String errorMsg = buildNoMatchErrorMessage(w, v, maxCapacityType);
|
*/
|
||||||
CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
|
private int calculateMaxCount(VehicleNode node,
|
||||||
vo.setErrorMessage(errorMsg);
|
BigDecimal weight,
|
||||||
vo.setHasSuitableType(false);
|
BigDecimal volume) {
|
||||||
// 仍然返回候选车型列表供参考
|
|
||||||
List<CalcSuggestFeeVO.VehicleTypeOptionVO> list = new ArrayList<>(allVehicleTypes.size());
|
int w = 0;
|
||||||
|
int v = 0;
|
||||||
|
|
||||||
|
if (node.weightMaxTon.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
w = weight.divide(node.weightMaxTon, 0, RoundingMode.CEILING).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.volumeMaxM3.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
v = volume.divide(node.volumeMaxM3, 0, RoundingMode.CEILING).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(w, v) + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建节点
|
||||||
|
*/
|
||||||
|
private VehicleNode buildNode(VehicleType v, BigDecimal km) {
|
||||||
|
|
||||||
|
if (!"0".equals(v.getStatus()) || "1".equals(v.getIsDelete())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.getUnitPricePerKm() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
VehicleNode node = new VehicleNode();
|
||||||
|
|
||||||
|
node.id = v.getId();
|
||||||
|
node.typeName = v.getTypeName();
|
||||||
|
node.unitPricePerKm = v.getUnitPricePerKm();
|
||||||
|
node.singleFee = v.getUnitPricePerKm()
|
||||||
|
.multiply(km)
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
node.weightMaxTon = nvl(v.getWeightMaxTon());
|
||||||
|
node.volumeMaxM3 = nvl(v.getVolumeMaxM3());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建返回结果
|
||||||
|
*/
|
||||||
|
private CalcSuggestFeeVO buildResult(SearchContext ctx,CalcSuggestFeeDTO dto) {
|
||||||
|
|
||||||
|
CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
|
||||||
|
|
||||||
|
if (ctx.bestPlan == null) {
|
||||||
|
vo.setHasSuitableType(false);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
vo.setHasSuitableType(true);
|
||||||
|
vo.setSuggestFee(ctx.bestFee);
|
||||||
|
vo.setTotalVehicleCount(ctx.bestVehicleCount);
|
||||||
|
|
||||||
|
List<CalcSuggestFeeVO.VehiclePlanVO> bestPlanList =
|
||||||
|
ctx.bestPlan.stream()
|
||||||
|
.map(p -> {
|
||||||
|
|
||||||
|
CalcSuggestFeeVO.VehiclePlanVO item =
|
||||||
|
new CalcSuggestFeeVO.VehiclePlanVO();
|
||||||
|
|
||||||
|
item.setVehicleTypeId(String.valueOf(p.vehicleTypeId));
|
||||||
|
item.setVehicleTypeName(p.vehicleTypeName);
|
||||||
|
item.setVehicleCount(p.count);
|
||||||
|
item.setSingleVehicleFee(p.singleFee);
|
||||||
|
item.setTotalFee(p.totalFee);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
vo.setBestPlan(bestPlanList);
|
||||||
|
// ========== 1) 候选车型:严格匹配,若无则兜底匹配 ==========
|
||||||
|
// 候选车型:同时满足“体积 >= 推荐最大体积” 且 “承重 >= 推荐最大承重”;否则返回全部启用车型
|
||||||
|
// 2. 查询全部车型
|
||||||
|
List<VehicleType> allVehicleTypes = vehicleTypeMapper.selectVehicleTypeList(new VehicleType());
|
||||||
|
if (bestPlanList!=null&&bestPlanList.size()==1) {
|
||||||
|
// 1. 取推荐方案中的最大体积、最大承重
|
||||||
|
BigDecimal recommendMaxVolume = ctx.bestPlan.stream()
|
||||||
|
.map(p -> p.volumeMaxM3)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.max(BigDecimal::compareTo)
|
||||||
|
.orElse(BigDecimal.ZERO);
|
||||||
|
|
||||||
|
BigDecimal recommendMaxWeight = ctx.bestPlan.stream()
|
||||||
|
.map(p -> p.weightMaxTon)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.max(BigDecimal::compareTo)
|
||||||
|
.orElse(BigDecimal.ZERO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 3. 过滤启用且未删除的车型
|
||||||
|
List<VehicleType> enabledTypes = new ArrayList<>();
|
||||||
|
if (allVehicleTypes != null) {
|
||||||
for (VehicleType t : allVehicleTypes) {
|
for (VehicleType t : allVehicleTypes) {
|
||||||
if ("0".equals(t.getStatus()) && !"1".equals(t.getIsDelete())) {
|
if ("0".equals(t.getStatus()) && !"1".equals(t.getIsDelete())) {
|
||||||
CalcSuggestFeeVO.VehicleTypeOptionVO o = new CalcSuggestFeeVO.VehicleTypeOptionVO();
|
enabledTypes.add(t);
|
||||||
o.setId(t.getId());
|
|
||||||
o.setName(t.getTypeName());
|
|
||||||
o.setUnitPricePerKm(t.getUnitPricePerKm());
|
|
||||||
o.setWeightMinTon(t.getWeightMinTon());
|
|
||||||
o.setWeightMaxTon(t.getWeightMaxTon());
|
|
||||||
o.setVolumeMinM3(t.getVolumeMinM3());
|
|
||||||
o.setVolumeMaxM3(t.getVolumeMaxM3());
|
|
||||||
list.add(o);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vo.setCandidates(list);
|
|
||||||
return vo;
|
|
||||||
} else {
|
|
||||||
CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
|
|
||||||
vo.setErrorMessage("未找到适配车型,请检查车型配置或输入参数。");
|
|
||||||
vo.setHasSuitableType(false);
|
|
||||||
return vo;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 稳定排序:单价升序 -> 承重上限升序 -> 载方上限升序 -> id 升序(数据库已排序,这里再兜一层)
|
// 4. 筛选满足条件的车型
|
||||||
candidates.sort(Comparator
|
List<VehicleType> candidates = new ArrayList<>();
|
||||||
.comparing(VehicleType::getUnitPricePerKm, Comparator.nullsLast(BigDecimal::compareTo))
|
for (VehicleType t : enabledTypes) {
|
||||||
.thenComparing(VehicleType::getWeightMaxTon, Comparator.nullsLast(BigDecimal::compareTo))
|
if (t.getWeightMaxTon() != null
|
||||||
.thenComparing(VehicleType::getVolumeMaxM3, Comparator.nullsLast(BigDecimal::compareTo))
|
&& t.getVolumeMaxM3() != null
|
||||||
.thenComparing(VehicleType::getId, Comparator.nullsLast(Long::compareTo)));
|
&& t.getWeightMaxTon().compareTo(recommendMaxWeight) >= 0
|
||||||
|
&& t.getVolumeMaxM3().compareTo(recommendMaxVolume) >= 0) {
|
||||||
|
candidates.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 如果没有符合条件的,返回全部启用车型
|
||||||
|
if (candidates.isEmpty()) {
|
||||||
|
candidates = enabledTypes;
|
||||||
|
}
|
||||||
|
List<CalcSuggestFeeVO.VehicleTypeOptionVO> list = new ArrayList<>();
|
||||||
|
for (VehicleType t : candidates) {
|
||||||
|
CalcSuggestFeeVO.VehicleTypeOptionVO o = new CalcSuggestFeeVO.VehicleTypeOptionVO();
|
||||||
|
o.setId(String.valueOf(t.getId()));
|
||||||
|
o.setName(t.getTypeName());
|
||||||
|
o.setUnitPricePerKm(t.getUnitPricePerKm());
|
||||||
|
o.setWeightMinTon(t.getWeightMinTon());
|
||||||
|
o.setWeightMaxTon(t.getWeightMaxTon());
|
||||||
|
o.setVolumeMinM3(t.getVolumeMinM3());
|
||||||
|
o.setVolumeMaxM3(t.getVolumeMaxM3());
|
||||||
|
list.add(o);
|
||||||
|
}
|
||||||
|
vo.setCandidates(list);
|
||||||
|
}else {
|
||||||
|
List<CalcSuggestFeeVO.VehicleTypeOptionVO> list = new ArrayList<>();
|
||||||
|
for (VehicleType t : allVehicleTypes) {
|
||||||
|
CalcSuggestFeeVO.VehicleTypeOptionVO o = new CalcSuggestFeeVO.VehicleTypeOptionVO();
|
||||||
|
o.setId(String.valueOf(t.getId()));
|
||||||
|
o.setName(t.getTypeName());
|
||||||
|
o.setUnitPricePerKm(t.getUnitPricePerKm());
|
||||||
|
o.setWeightMinTon(t.getWeightMinTon());
|
||||||
|
o.setWeightMaxTon(t.getWeightMaxTon());
|
||||||
|
o.setVolumeMinM3(t.getVolumeMinM3());
|
||||||
|
o.setVolumeMaxM3(t.getVolumeMaxM3());
|
||||||
|
list.add(o);
|
||||||
|
}
|
||||||
|
vo.setCandidates(list);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 3) 选定车型:前端指定 or 系统推荐 ==========
|
// ========== 3) 选定车型:前端指定 or 系统推荐 ==========
|
||||||
VehicleType chosen;
|
List<VehicleType> chosenList;
|
||||||
if (dto.getVehicleTypeId() != null) {
|
|
||||||
// 前端点名车型:优先在候选中找,找不到则直接按主键查
|
if (StringUtils.isNotBlank(dto.getVehicleTypeId())) {
|
||||||
chosen = candidates.stream()
|
|
||||||
.filter(t -> dto.getVehicleTypeId().equals(t.getId()))
|
List<Long> idList = Arrays.stream(dto.getVehicleTypeId().split(","))
|
||||||
.findFirst()
|
.map(String::trim)
|
||||||
.orElseGet(() -> vehicleTypeMapper.selectVehicleTypeById(dto.getVehicleTypeId()));
|
.filter(s -> !s.isEmpty())
|
||||||
if (chosen == null) {
|
.map(Long::valueOf)
|
||||||
throw new RuntimeException("指定车型不存在或已被禁用:id=" + dto.getVehicleTypeId());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
chosenList = allVehicleTypes.stream()
|
||||||
|
.filter(t -> idList.contains(t.getId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// ========== 先判断:前端指定车型组合能否装下 ==========
|
||||||
|
BigDecimal totalWeightCapacity = BigDecimal.ZERO;
|
||||||
|
BigDecimal totalVolumeCapacity = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
for (VehicleType t : chosenList) {
|
||||||
|
totalWeightCapacity = totalWeightCapacity.add(
|
||||||
|
t.getWeightMaxTon() == null ? BigDecimal.ZERO : t.getWeightMaxTon()
|
||||||
|
);
|
||||||
|
totalVolumeCapacity = totalVolumeCapacity.add(
|
||||||
|
t.getVolumeMaxM3() == null ? BigDecimal.ZERO : t.getVolumeMaxM3()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
chosen = candidates.get(0);
|
boolean weightEnough = totalWeightCapacity.compareTo(dto.getWeightTon()) >= 0;
|
||||||
|
boolean volumeEnough = totalVolumeCapacity.compareTo(dto.getVolumeM3()) >= 0;
|
||||||
|
|
||||||
|
if (!weightEnough || !volumeEnough) {
|
||||||
|
StringBuilder sb = new StringBuilder("所选车型可能无法装下当前货物");
|
||||||
|
sb.append(",总承重=").append(totalWeightCapacity).append("吨");
|
||||||
|
sb.append(",总载方=").append(totalVolumeCapacity).append("方");
|
||||||
|
sb.append(",货物重量=").append(dto.getWeightTon()).append("吨");
|
||||||
|
sb.append(",货物体积=").append(dto.getVolumeM3()).append("方");
|
||||||
|
|
||||||
|
vo.setWarningMessage(sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 4) 多车型计算建议费用 ==========
|
||||||
|
BigDecimal totalFee = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
for (VehicleType t : chosenList) {
|
||||||
|
|
||||||
|
if (t.getUnitPricePerKm() == null) {
|
||||||
|
throw new ServiceException("车型【" + t.getTypeName() + "】未配置单价");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单车费用
|
||||||
|
BigDecimal singleFee = t.getUnitPricePerKm()
|
||||||
|
.multiply(dto.getDistanceKm())
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
// 这里默认每个指定车型 1 辆
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
BigDecimal subTotal = singleFee.multiply(BigDecimal.valueOf(count));
|
||||||
|
totalFee = totalFee.add(subTotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
vo.setSuggestFee(totalFee);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 4) 计算建议费用:单价 × 公里数,保留2位 ==========
|
|
||||||
BigDecimal price = chosen.getUnitPricePerKm();
|
|
||||||
if (price == null) {
|
|
||||||
throw new RuntimeException("车型【" + chosen.getTypeName() + "】未配置每公里单价(unit_price_per_km 为空)");
|
|
||||||
}
|
|
||||||
BigDecimal fee = price.multiply(km).setScale(2, RoundingMode.HALF_UP);
|
|
||||||
|
|
||||||
// ========== 5) 组装返回 ==========
|
|
||||||
CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
|
|
||||||
vo.setVehicleTypeId(chosen.getId());
|
|
||||||
vo.setVehicleTypeName(chosen.getTypeName());
|
|
||||||
vo.setUnitPricePerKm(price);
|
|
||||||
vo.setSuggestFee(fee);
|
|
||||||
vo.setHasSuitableType(true); // 有适配车型
|
|
||||||
|
|
||||||
// 候选项
|
// // ========== 5) 组装返回 ==========
|
||||||
List<CalcSuggestFeeVO.VehicleTypeOptionVO> list = new ArrayList<>(candidates.size());
|
// CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
|
||||||
for (VehicleType t : candidates) {
|
// vo.setVehicleTypeId(chosen.getId());
|
||||||
CalcSuggestFeeVO.VehicleTypeOptionVO o = new CalcSuggestFeeVO.VehicleTypeOptionVO();
|
// vo.setVehicleTypeName(chosen.getTypeName());
|
||||||
o.setId(t.getId());
|
// vo.setUnitPricePerKm(price);
|
||||||
o.setName(t.getTypeName());
|
// vo.setHasSuitableType(true); // 有适配车型
|
||||||
o.setUnitPricePerKm(t.getUnitPricePerKm());
|
|
||||||
o.setWeightMinTon(t.getWeightMinTon());
|
|
||||||
o.setWeightMaxTon(t.getWeightMaxTon());
|
|
||||||
o.setVolumeMinM3(t.getVolumeMinM3());
|
|
||||||
o.setVolumeMaxM3(t.getVolumeMaxM3());
|
|
||||||
list.add(o);
|
|
||||||
}
|
|
||||||
vo.setCandidates(list);
|
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空值转0
|
||||||
|
*/
|
||||||
|
private BigDecimal nvl(BigDecimal v) {
|
||||||
|
return v == null ? BigDecimal.ZERO : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 算法节点
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
private static class VehicleNode {
|
||||||
|
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
String typeName;
|
||||||
|
|
||||||
|
BigDecimal unitPricePerKm;
|
||||||
|
|
||||||
|
BigDecimal singleFee;
|
||||||
|
|
||||||
|
BigDecimal weightMaxTon;
|
||||||
|
|
||||||
|
BigDecimal volumeMaxM3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合方案
|
||||||
|
*/
|
||||||
|
private static class VehiclePlan {
|
||||||
|
|
||||||
|
|
||||||
|
Long vehicleTypeId;
|
||||||
|
|
||||||
|
String vehicleTypeName;
|
||||||
|
|
||||||
|
int count;
|
||||||
|
|
||||||
|
BigDecimal singleFee;
|
||||||
|
|
||||||
|
BigDecimal totalFee;
|
||||||
|
BigDecimal weightMaxTon;
|
||||||
|
BigDecimal volumeMaxM3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索上下文
|
||||||
|
*/
|
||||||
|
private static class SearchContext {
|
||||||
|
|
||||||
|
BigDecimal targetWeight;
|
||||||
|
|
||||||
|
BigDecimal targetVolume;
|
||||||
|
|
||||||
|
BigDecimal bestFee;
|
||||||
|
|
||||||
|
int bestVehicleCount;
|
||||||
|
|
||||||
|
List<VehiclePlan> bestPlan;
|
||||||
|
|
||||||
|
SearchContext(BigDecimal w, BigDecimal v) {
|
||||||
|
targetWeight = w;
|
||||||
|
targetVolume = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 构建无匹配车型的错误信息
|
* 构建无匹配车型的错误信息
|
||||||
*/
|
*/
|
||||||
@@ -235,23 +480,18 @@ public class VehicleTypeServiceImpl implements IVehicleTypeService
|
|||||||
|
|
||||||
if (weightExceeds && volumeExceeds) {
|
if (weightExceeds && volumeExceeds) {
|
||||||
sb.append("货物重量(").append(weight).append("吨)超过最大承重(")
|
sb.append("货物重量(").append(weight).append("吨)超过最大承重(")
|
||||||
.append(maxCapacityType.getWeightMaxTon()).append("吨),且货物体积(")
|
.append(maxCapacityType.getWeightMaxTon()).append("吨),且货物体积(")
|
||||||
.append(volume).append("立方米)超过最大载方(")
|
.append(volume).append("立方米)超过最大载方(")
|
||||||
.append(maxCapacityType.getVolumeMaxM3()).append("立方米)。");
|
.append(maxCapacityType.getVolumeMaxM3()).append("立方米)。");
|
||||||
} else if (weightExceeds) {
|
} else if (weightExceeds) {
|
||||||
sb.append("货物重量(").append(weight).append("吨)超过最大承重(")
|
sb.append("货物重量(").append(weight).append("吨)超过最大承重(")
|
||||||
.append(maxCapacityType.getWeightMaxTon()).append("吨)。");
|
.append(maxCapacityType.getWeightMaxTon()).append("吨)。");
|
||||||
} else if (volumeExceeds) {
|
} else if (volumeExceeds) {
|
||||||
sb.append("货物体积(").append(volume).append("立方米)超过最大载方(")
|
sb.append("货物体积(").append(volume).append("立方米)超过最大载方(")
|
||||||
.append(maxCapacityType.getVolumeMaxM3()).append("立方米)。");
|
.append(maxCapacityType.getVolumeMaxM3()).append("立方米)。");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("最大承载车型为:").append(maxCapacityType.getTypeName());
|
sb.append("最大承载车型为:").append(maxCapacityType.getTypeName());
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static BigDecimal nvl(BigDecimal x) {
|
|
||||||
return x == null ? BigDecimal.ZERO : x;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -553,7 +553,7 @@
|
|||||||
|
|
||||||
<!-- 车型 -->
|
<!-- 车型 -->
|
||||||
<if test="vehicleTypeId != null">
|
<if test="vehicleTypeId != null">
|
||||||
AND dor.vehicle_type_id = #{vehicleTypeId}
|
AND FIND_IN_SET(#{vehicleTypeId}, dor.vehicle_type_id)
|
||||||
</if>
|
</if>
|
||||||
<if test="vehicleTypeName != null and vehicleTypeName != ''">
|
<if test="vehicleTypeName != null and vehicleTypeName != ''">
|
||||||
AND dor.vehicle_type_name LIKE CONCAT('%', #{vehicleTypeName}, '%')
|
AND dor.vehicle_type_name LIKE CONCAT('%', #{vehicleTypeName}, '%')
|
||||||
@@ -945,6 +945,7 @@
|
|||||||
|
|
||||||
<!-- 车辆类型 -->
|
<!-- 车辆类型 -->
|
||||||
<if test="q.vehicleTypeId != null">
|
<if test="q.vehicleTypeId != null">
|
||||||
|
AND FIND_IN_SET(#{q.vehicleTypeId}, dor.vehicle_type_id)
|
||||||
AND dor.vehicle_type_id = #{q.vehicleTypeId}
|
AND dor.vehicle_type_id = #{q.vehicleTypeId}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
@@ -1054,4 +1055,16 @@
|
|||||||
GROUP BY DATE_FORMAT(dor.delivery_date, '%Y-%m')
|
GROUP BY DATE_FORMAT(dor.delivery_date, '%Y-%m')
|
||||||
ORDER BY statMonth ASC
|
ORDER BY statMonth ASC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectExistsRkRecordIds" resultType="java.lang.Long">
|
||||||
|
SELECT DISTINCT rk_record_id
|
||||||
|
FROM delivery_order
|
||||||
|
WHERE is_delete = '0'
|
||||||
|
AND rk_record_id IS NOT NULL
|
||||||
|
AND order_status IN ('1','2','3')
|
||||||
|
AND rk_record_id IN
|
||||||
|
<foreach collection="rkRecordIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -140,5 +140,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and volume_max_m3 >= #{v}
|
and volume_max_m3 >= #{v}
|
||||||
order by unit_price_per_km asc, weight_max_ton asc, volume_max_m3 asc, id asc
|
order by unit_price_per_km asc, weight_max_ton asc, volume_max_m3 asc, id asc
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectUsableTypes" resultMap="VehicleTypeResult">
|
||||||
|
SELECT *
|
||||||
|
FROM vehicle_type
|
||||||
|
WHERE status = '0'
|
||||||
|
AND is_delete = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
Reference in New Issue
Block a user