From 22c080f77a9e1af843c1675ce4c7df3aa20ebd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=B8=85?= <3115919733@qq.com> Date: Wed, 23 Apr 2025 10:08:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E8=BD=A6=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 12 ++ .../src/main/resources/application-prod.yml | 41 +++-- .../entity/TmsIogisticsPrice.java | 9 +- .../mapper/TmsIogisticsPriceMapper.java | 8 + .../mapper/xml/TmsIogisticsPriceMapper.xml | 7 + .../vehicledemand/entity/Solution.java | 19 ++ .../impl/VehicleDemandServiceImpl.java | 163 +++++++++++++++--- 7 files changed, 215 insertions(+), 44 deletions(-) create mode 100644 .gitignore create mode 100644 jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/entity/Solution.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec1211d --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +## ide +**/.idea +*.iml +rebel.xml + +## backend +**/target +**/logs +*/logs + +## front +**/*.lock diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml index 9280101..16b6e32 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml @@ -1,5 +1,5 @@ server: - port: 8080 + port: 9001 tomcat: max-swallow-size: -1 error: @@ -110,7 +110,7 @@ spring: # 初始化大小,最小,最大 initial-size: 5 min-idle: 5 - maxActive: 1000 + maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 @@ -130,10 +130,14 @@ spring: connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 datasource: master: - url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai - username: root - password: root - driver-class-name: com.mysql.cj.jdbc.Driver + # url: jdbc:mysql://114.215.188.164:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + # username: root + # password: 123456 + # driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:oracle:thin:@123.57.206.181:1521/ORCLPDB1 + username: SDKTMS + password: SDKTMS + driver-class-name: oracle.jdbc.OracleDriver # 多数据源配置 #multi-datasource1: #url: jdbc:mysql://localhost:3306/jeecg-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai @@ -143,7 +147,7 @@ spring: #redis 配置 redis: database: 0 - host: 127.0.0.1 + host: 123.57.206.181 port: 6379 password: '' #mybatis plus 设置 @@ -172,33 +176,32 @@ jeecg: signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a # 签名拦截接口 signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys - #local\minio\alioss - uploadType: alioss + #local、minio、alioss + uploadType: local # 前端访问地址 domainUrl: pc: http://localhost:3100 app: http://localhost:8051 path: #文件上传根目录 设置 - upload: /opt/jeecg-boot/upload + upload: /opt/upFiles #webapp文件路径 - webapp: /opt/jeecg-boot/webapp + webapp: /opt/webapp shiro: - excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**,/api/getUserInfo + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/** #阿里云oss存储和大鱼短信秘钥配置 oss: accessKey: ?? secretKey: ?? endpoint: oss-cn-beijing.aliyuncs.com bucketName: jeecgdev - staticDomain: https://static.jeecg.com - # ElasticSearch 设置 + # ElasticSearch 6设置 elasticsearch: cluster-name: jeecg-ES cluster-nodes: 127.0.0.1:9200 - check-enabled: true + check-enabled: false # 在线预览文件服务器地址配置 - file-view-domain: http://fileview.jeecg.com + file-view-domain: 127.0.0.1:8012 # minio文件上传 minio: minio_url: http://minio.jeecg.com @@ -207,7 +210,7 @@ jeecg: bucketName: otatest #大屏报表参数设置 jmreport: - mode: prod + mode: dev #数据字典是否进行saas数据隔离,自己看自己的字典 saas: false #是否需要校验token @@ -245,7 +248,7 @@ knife4j: #开启生产环境屏蔽 production: false basic: - enable: true + enable: false username: jeecg password: jeecg1314 #第三方登录 @@ -295,4 +298,4 @@ third-app: client-id: ?? # appSecret client-secret: ?? - agent-id: ?? \ No newline at end of file + agent-id: ?? diff --git a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/entity/TmsIogisticsPrice.java b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/entity/TmsIogisticsPrice.java index 46b14cf..c9765d7 100644 --- a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/entity/TmsIogisticsPrice.java +++ b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/entity/TmsIogisticsPrice.java @@ -4,10 +4,8 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.Date; import java.math.BigDecimal; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.annotation.TableLogic; + +import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import com.fasterxml.jackson.annotation.JsonFormat; import org.springframework.format.annotation.DateTimeFormat; @@ -125,4 +123,7 @@ public class TmsIogisticsPrice implements Serializable { @Excel(name = "时效", width = 15) @ApiModelProperty(value = "时效") private java.lang.String validity; + /**托数*/ + @TableField(exist = false) + private java.lang.Integer maxPallets; } diff --git a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/TmsIogisticsPriceMapper.java b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/TmsIogisticsPriceMapper.java index e9cec2a..6681fbb 100644 --- a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/TmsIogisticsPriceMapper.java +++ b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/TmsIogisticsPriceMapper.java @@ -14,4 +14,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; */ public interface TmsIogisticsPriceMapper extends BaseMapper { + /** + * 查询物流价格信息 + * @param pickUpHub 起始城市 + * @param deliveryAddress 目标城市 + * @return + */ + List getIogisticsPrice(@Param("pickUpHub") String pickUpHub, + @Param("deliveryAddress") String deliveryAddress); } diff --git a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/xml/TmsIogisticsPriceMapper.xml b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/xml/TmsIogisticsPriceMapper.xml index fa46770..30e4129 100644 --- a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/xml/TmsIogisticsPriceMapper.xml +++ b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/basicdata/iogisticsprice/mapper/xml/TmsIogisticsPriceMapper.xml @@ -2,4 +2,11 @@ + diff --git a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/entity/Solution.java b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/entity/Solution.java new file mode 100644 index 0000000..5c4be5f --- /dev/null +++ b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/entity/Solution.java @@ -0,0 +1,19 @@ +package org.jeecg.modules.tms.outbound.vehicledemand.entity; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * @Description + * @Author admin + * @Date 2025/4/23 9:12 + */ +@Data +public class Solution { + /**方案*/ + Map vehicleCounts; + /**成本*/ + BigDecimal totalCost; +} diff --git a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/service/impl/VehicleDemandServiceImpl.java b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/service/impl/VehicleDemandServiceImpl.java index d781ce9..f999330 100644 --- a/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/service/impl/VehicleDemandServiceImpl.java +++ b/jeecg-boot/jeecg-module-tms/src/main/java/org/jeecg/modules/tms/outbound/vehicledemand/service/impl/VehicleDemandServiceImpl.java @@ -3,9 +3,13 @@ package org.jeecg.modules.tms.outbound.vehicledemand.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.tms.basicdata.iogisticsprice.entity.TmsIogisticsPrice; +import org.jeecg.modules.tms.basicdata.iogisticsprice.mapper.TmsIogisticsPriceMapper; import org.jeecg.modules.tms.basicdata.vehiclepallet.entity.VehiclePallet; import org.jeecg.modules.tms.basicdata.vehiclepallet.mapper.VehiclePalletMapper; +import org.jeecg.modules.tms.outbound.vehicledemand.entity.Solution; import org.jeecg.modules.tms.outbound.vehicledemand.entity.VehicleDemand; import org.jeecg.modules.tms.outbound.vehicledemand.mapper.VehicleDemandMapper; import org.jeecg.modules.tms.outbound.vehicledemand.service.IVehicleDemandService; @@ -15,8 +19,8 @@ import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import java.util.ArrayList; -import java.util.List; +import java.math.BigDecimal; +import java.util.*; /** * @Description: 用车需求 @@ -24,14 +28,149 @@ import java.util.List; * @Date: 2025-04-11 * @Version: V1.0 */ +@Slf4j @Service @RequiredArgsConstructor public class VehicleDemandServiceImpl extends ServiceImpl implements IVehicleDemandService { private final VehiclePalletMapper vehiclePalletMapper; private final IVehicleDemandCountService vehicleDemandCountService; + private final TmsIogisticsPriceMapper iogisticsPriceMapper; @Override + public Result carCount(VehicleDemand vehicleDemand) { + // 1.判断是否进行过用车计算 + LambdaQueryWrapper countLambdaQueryWrapper = new LambdaQueryWrapper<>(); + countLambdaQueryWrapper.eq(VehicleDemandCount::getVdNo, vehicleDemand.getVdNo()); + List list = vehicleDemandCountService.list(countLambdaQueryWrapper); + // 1.1.进行过 + if (list != null && list.size() > 0) { + return Result.error("已经进行过用车计算了!"); + } + // 2.查询物流价格信息(从起始城市到目标城市各种车型的价格) + List iogisticsPriceList = iogisticsPriceMapper.getIogisticsPrice( + vehicleDemand.getPickUpHub(), + vehicleDemand.getDeliveryAddress()); + if (iogisticsPriceList == null || iogisticsPriceList.size() <= 0) { + return Result.error("没有符合的物流价格基础信息,请维护!"); + } + // 3.查找费用最低的方案 + // Map vehicleCounts; + // String: 由车型与车长组成,以逗号分隔 + // Integer: 该种车型与车长选择的数量 + Solution solution = findCheapestSolution(iogisticsPriceList, vehicleDemand.getTotalPallets()); + // 打印,方便查看,开发时可以打开 + /*for (Map.Entry entry : solution.getVehicleCounts().entrySet()) { + System.out.println(entry.getKey() + " 车: " + entry.getValue() + " 辆"); + }*/ + // 4.转化集合 + List vehicleDemandCounts = convertToVehicleDemandCountList(solution.getVehicleCounts(), vehicleDemand); + // 5.新增 + vehicleDemandCountService.saveBatch(vehicleDemandCounts); + log.info("用车需求计算,用车需求编号:{},方案:{},费用:{}",vehicleDemand.getVdNo(), + solution.getVehicleCounts(),solution.getTotalCost()); + return Result.OK("用车计算成功!"); + } + + /** + * 查找费用最低的方案 + */ + private Solution findCheapestSolution(List iogisticsPriceList, Integer totalPallets) { + // dp[i] 表示运送 i 托盘时的最小成本 + BigDecimal[] dp = new BigDecimal[totalPallets + 1]; + Arrays.fill(dp, BigDecimal.valueOf(Integer.MAX_VALUE)); + dp[0] = BigDecimal.ZERO; + + // 记录每种状态下的车辆选择 + Map> choices = new HashMap<>(); + for (int i = 0; i <= totalPallets; i++) { + choices.put(i, new HashMap<>()); + } + + // 动态规划填表 + for (int i = 0; i <= totalPallets; i++) { + if (dp[i].compareTo(BigDecimal.valueOf(Integer.MAX_VALUE)) == 0) continue; // 如果当前状态不可达,跳过 + + for (TmsIogisticsPrice iogisticsPrice : iogisticsPriceList) { + int next = i + iogisticsPrice.getMaxPallets(); + if (next > totalPallets) next = totalPallets; // 不超过总托盘数 + + BigDecimal newCost = dp[i].add(iogisticsPrice.getTandardPrice()); + if (newCost.compareTo(dp[next]) < 0 ) { // 如果新成本更低,则更新 + dp[next] = newCost; + + // 更新车辆选择 + Map newChoices = new HashMap<>(choices.get(i)); + newChoices.put(iogisticsPrice.getCarType() + "," +iogisticsPrice.getCarLong(), newChoices.getOrDefault(iogisticsPrice.getCarType() + "," +iogisticsPrice.getCarLong(), 0) + 1); + choices.put(next, newChoices); + } + } + } + // choices.get(totalPallets):选择的方案, dp[totalPallets]费用 + Solution solution = new Solution(); + solution.setVehicleCounts(choices.get(totalPallets)); + solution.setTotalCost(dp[totalPallets]); + return solution; + } + + /** + * 转化集合 + * @param vehicleCounts + * @return + */ + private List convertToVehicleDemandCountList(Map vehicleCounts, + VehicleDemand vehicleDemand) { + List resultList = new ArrayList<>(); + + // 遍历 solution.getVehicleCounts() + for (Map.Entry entry : vehicleCounts.entrySet()) { + String key = entry.getKey(); // 键格式为 "车类型,车长度" + int count = entry.getValue(); // 车辆数量 + + // 解析车类型和车长度 + String[] parts = key.split(","); + /*if (parts.length != 2) { + throw new IllegalArgumentException("键格式错误: " + key); + }*/ + String carType = parts[0]; // 车类型 + int carLong = Integer.parseInt(parts[1]); // 车长度 + + // 根据车辆数量生成多条记录 + for (int i = 0; i < count; i++) { + VehicleDemandCount vehicleDemandCount = new VehicleDemandCount(); + vehicleDemandCount.setCarType(carType) + .setCarLong(carLong) + .setVdNo(vehicleDemand.getVdNo()) + .setShipType(vehicleDemand.getShipType()) + .setDeliveryAddress(vehicleDemand.getDeliveryAddress()) + .setPickUpHub(vehicleDemand.getPickUpHub()); + resultList.add(vehicleDemandCount); + } + } + + return resultList; + } + + /** + * 拼凑一个用车需求计算的集合 + * @param vehicleDemand + * @param vehiclePallet + * @param vehicleDemandCounts + */ + /*private void insertVehicleDemandCount(VehicleDemand vehicleDemand, VehiclePallet vehiclePallet, List vehicleDemandCounts) { + VehicleDemandCount vehicleDemandCount = new VehicleDemandCount(); + vehicleDemandCount.setVdNo(vehicleDemand.getVdNo()) + .setShipType(vehicleDemand.getShipType()) + .setDeliveryArea(vehicleDemand.getDeliveryArea()) + .setDeliveryAddress(vehicleDemand.getDeliveryAddress()) + .setPickUpHub(vehicleDemand.getPickUpHub()) + .setCarType(vehiclePallet.getCarType()) + .setCarLong(vehiclePallet.getCarLong()); + vehicleDemandCounts.add(vehicleDemandCount); + }*/ + + + /* 原来的用车计算方法 public Result carCount(VehicleDemand vehicleDemand) { // 1.判断是否进行过用车计算 LambdaQueryWrapper countLambdaQueryWrapper = new LambdaQueryWrapper<>(); @@ -70,23 +209,5 @@ public class VehicleDemandServiceImpl extends ServiceImpl vehicleDemandCounts) { - VehicleDemandCount vehicleDemandCount = new VehicleDemandCount(); - vehicleDemandCount.setVdNo(vehicleDemand.getVdNo()) - .setShipType(vehicleDemand.getShipType()) - .setDeliveryArea(vehicleDemand.getDeliveryArea()) - .setDeliveryAddress(vehicleDemand.getDeliveryAddress()) - .setPickUpHub(vehicleDemand.getPickUpHub()) - .setCarType(vehiclePallet.getCarType()) - .setCarLong(vehiclePallet.getCarLong()); - vehicleDemandCounts.add(vehicleDemandCount); - } + }*/ }