package com.ruoyi.buss.controller; import com.alibaba.fastjson2.JSONObject; import com.ruoyi.buss.common.AssessUtil; import com.ruoyi.buss.common.NumberUtils; import com.ruoyi.buss.domain.*; import com.ruoyi.buss.domain.dto.TaskQueryParam; import com.ruoyi.buss.domain.vo.TaskAssess; import com.ruoyi.buss.domain.vo.TaskAssessHis; import com.ruoyi.buss.service.*; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @Tag(name = "能效评估接口") @RestController @RequestMapping("/buss/assess/ex") public class DMAssessController extends BaseController { @Autowired private IDmHarbor2Service iDmHarbor2Service; @Autowired private IDsTaskDetailService iDsTaskDetailService; @Autowired private IDsTaskList2Service iDsTaskList2Service; @Autowired private IDsEffectAssessService dsEffectAssessService; @Autowired private IDsEffectAssessListService dsEffectAssessListService; @Autowired private IDsEffectAssessHisService dsEffectAssessHisService; private Double OIL_PLACE_PER = 0.3; private Double MAT_PLACE_PER = 0.2; private Double AMMO_PLACE_PER = 0.5; @Operation(summary = "获取能效评估列表") @PostMapping("/list") @ResponseBody public AjaxResult assessList() { List list = dsEffectAssessService.selectDsEffectAssessList(new DsEffectAssess()); if(null != list){ return success(list); } return error("获取能效评估列表失败,请重试"); } @Operation(summary = "根据任务ID和部门编码获取评估详情") @GetMapping("/assess/{type}/{taskId}/{deptId}") @ResponseBody public AjaxResult getByTaskId(@Parameter(name = "type", required = true, description = "评估类型") @PathVariable("type") String type, @Parameter(name = "taskId", required = true, description = "任务ID") @PathVariable("taskId") Long taskId, @Parameter(name = "deptId", required = true, description = "部门ID") @PathVariable("deptId") Long deptId) { DsEffectAssess assess = new DsEffectAssess(); assess.setTaskId(taskId); assess.setDeptId(deptId); List assessList = dsEffectAssessService.selectDsEffectAssessList(assess); if(null != assessList && assessList.size() > 0){ DsEffectAssess dsEffectAssess = assessList.get(0); DsEffectAssessHis assessHis = new DsEffectAssessHis(); assessHis.setTaskId(taskId); assessHis.setDeptId(deptId); assessHis.setTYPE(type); List hisList = dsEffectAssessHisService.selectDsEffectAssessHisList(assessHis); TaskAssessHis taskAssess = new TaskAssessHis(); taskAssess.setList(hisList); taskAssess.setAssess(dsEffectAssess); return success(taskAssess); } else{ return calcDsEffectByTime(taskId, type); } } @Operation(summary = "实时-根据任务ID获取评估详情") @GetMapping("/assess/time/{type}/{taskid}") @ResponseBody public AjaxResult getTime(@Parameter(name = "type", required = true, description = "评估类型") @PathVariable("type") String type, @Parameter(name = "taskid", required = true, description = "任务ID") @PathVariable("taskid") Long taskid) { if(!type.equalsIgnoreCase("JD") && !type.equalsIgnoreCase("ZD")){ return error("获取评估类型错误,请检查参数"); } return calcDsEffectByTime(taskid, type); } // 实时计算评估值 private AjaxResult calcDsEffectByTime(Long taskid, String type){ DsEffectAssessList assessList = new DsEffectAssessList(); assessList.setTYPE(type); List weightList = dsEffectAssessListService.selectDsEffectAssessListList(assessList); List detailList = iDsTaskDetailService.selectDsTaskDetailByTaskId(taskid, null); TaskQueryParam taskQueryParam = new TaskQueryParam(); if(null != taskid){taskQueryParam.setTaskId(taskid);} List taskList2List = iDsTaskList2Service.selectDsTaskListByParam(taskQueryParam); List harbor2List = iDmHarbor2Service.selectDmHarborList(new DmHarbor2()); List dsEffectAssessLists = calcDsEffect(detailList, taskList2List, harbor2List); // 更新权重指标或者是数值 dsEffectAssessLists = updateWeight(dsEffectAssessLists, weightList); List hisList = covertToHis(dsEffectAssessLists, taskid); dsEffectAssessHisService.updateEffectAssessHisList(hisList); // 计算最终评估结果 double score = getScore(dsEffectAssessLists); DsEffectAssess dsEffectAssess = new DsEffectAssess(taskid, score); dsEffectAssess.setCreateTime(new Date()); dsEffectAssess.setCreateBy(getUsername()); dsEffectAssess.setDeptId(getDeptId()); // 保存评估结果 dsEffectAssessService.updateOrInsertDsEffectAssess(dsEffectAssess); TaskAssess assess = new TaskAssess(); assess.setList(dsEffectAssessLists); assess.setAssess(dsEffectAssess); if(null != dsEffectAssessLists){ return success(assess); } return error("获取能效评估详情失败,请重试"); } // 获取评价详细值 private List calcDsEffect(List detailList, List taskList2List, List harbor2List){ List result = new ArrayList<>(); if(null != detailList && detailList.size() >0){ int harborCount = 0; // 港口数量 int berthCount = 0; // 泊位数量 int supplyTime = 0; // 各港口补给任务总时长 int allocTime = 0; // 各港口分配任务总时长 int supplyTimeDif = 0; // 补给和任务分配时长差额总和 List maxTimeDifList = new ArrayList<>(); // 两港口最大时间差 int shipSupplyTime = 0; //舰艇补给时间总和 int shipAllocTime = 0; // 舰艇停泊时间总和 int allTime = 0; // 任务总时长 int workTime = 0; // 累计补给时间总计 int stopTime = 0; // 累计停靠时间 int oilTime = 0; // 油料补给总时长 int matTime = 0; // 物资补给总时长 int ammoTime = 0; // 弹药补给总时长 double platMaxPer = 0.0; // 场地占用最大值总和 int platHalfTime = 0; // 泊位占用过半时间 int posAreaNotSameCount = 0; Map> groupedTaskList = detailList.stream() .collect(Collectors.groupingBy(DsTaskDetail::getHarborId)); Set harborIds = new HashSet<>(); Set berthIds = new HashSet<>(); AtomicReference carTotalCountRef = new AtomicReference<>(0.0); // 各泊位物资车次总值 for (Map.Entry> entry : groupedTaskList.entrySet()) { harborIds.add(entry.getKey()); Map> groupedBerthTaskList = detailList.stream() .collect(Collectors.groupingBy(DsTaskDetail::getBerthId)); int maxHarborTime = 0; // 泊位最大时长 int allocHarborTime = 0; //s for (Map.Entry> berthEntry : groupedBerthTaskList.entrySet()) { berthIds.add(berthEntry.getKey()); int maxOilTime = berthEntry.getValue().stream() .mapToInt(DsTaskDetail::getOilTime) .max() .orElse(0); int maxMatTime = berthEntry.getValue().stream() .mapToInt(DsTaskDetail::getMatTime) .max() .orElse(0); int maxAmmoTime = berthEntry.getValue().stream() .mapToInt(DsTaskDetail::getAmmoTime) .max() .orElse(0); berthEntry.getValue().stream().forEach(item -> { int matTmpTime = item.getMatTime(); int totalCount = 0; if(null != item.getFood()){ totalCount += item.getFood(); } if(null != item.getFoodW()){ totalCount += item.getFoodW(); } if(null != item.getFoodO()){ totalCount += item.getFoodO(); } if(matTmpTime <= 24) { int finalTotalCount = totalCount; carTotalCountRef.updateAndGet(v -> (v + finalTotalCount)); } else{ int finalTotalCount1 = totalCount; carTotalCountRef.updateAndGet(v -> (v + NumberUtils.roundToTwoDecimalPlaces(finalTotalCount1 / matTmpTime * 24))); } }); int maxTime = Math.max(Math.max(maxOilTime, maxMatTime), maxAmmoTime); oilTime += maxOilTime; matTime += maxMatTime; ammoTime += maxAmmoTime; if(maxTime > maxHarborTime){maxHarborTime = maxTime;} supplyTime += maxTime; allocHarborTime += maxTime; if(allTime < maxTime) {allTime = maxTime;} platMaxPer += getMaxPlat(berthEntry.getValue()); platHalfTime += getPlatHalfTime(berthEntry.getValue()); } allocTime += groupedBerthTaskList.size() * maxHarborTime; supplyTimeDif += (allocTime - allocHarborTime); maxTimeDifList.add(maxHarborTime); } for (DsTaskDetail detail : detailList) { // 分配时间 int maxTime = Math.max(Math.max(NumberUtils.getOrDefault(detail.getAmmoEtime(), 0) , NumberUtils.getOrDefault(detail.getOilEtime(), 0)), NumberUtils.getOrDefault(detail.getMatEtime(), 0)); // 补给时间 int tApplyTime = 0; // 补给时间 if(null != detail.getSupplySeq() && (detail.getSupplySeq().equals("2") || detail.getSupplySeq().equals("3"))) { // 如果为先油后弹则两个时间相加 tApplyTime = (detail.getAmmoTime() == null ? 0 : detail.getAmmoTime())+ (null == detail.getOilTime() ? 0 : detail.getOilTime()); if(tApplyTime < detail.getMatTime()){ tApplyTime = detail.getMatTime(); } } else{ // 同时补充 选择最大值 tApplyTime = Math.max(Math.max(Math.max(NumberUtils.getOrDefault(detail.getAmmoTime(), 0), NumberUtils.getOrDefault(detail.getMatTime(), 0)), NumberUtils.getOrDefault(detail.getOilTime(), 0)), NumberUtils.getOrDefault(detail.getWaterTime(), 0)); } shipSupplyTime += tApplyTime; shipAllocTime += maxTime; if(!isShipPosAreaSame(taskList2List, detail.getTaskId(), detail.getShipNo(), harbor2List)){ posAreaNotSameCount++; } } workTime = oilTime + matTime; harborCount = harborIds.size(); berthCount = berthIds.size(); int maxMinTimeDif = 0; // 各港口补给作业最终结束两两时间差的最大值A3 if (!maxTimeDifList.isEmpty()) { int maxTimeDif = maxTimeDifList.stream().mapToInt(Integer::intValue).max().orElse(0); int minTimeDif = maxTimeDifList.stream().mapToInt(Integer::intValue).min().orElse(0); maxMinTimeDif = maxTimeDif - minTimeDif; // 这里可以根据需要使用 maxMinTimeDif 变量 } // 本批次舰艇数量 int shipCount = detailList.size(); // 本批次船数量 double days = NumberUtils.roundToTwoDecimalPlaces((double) allTime / 24); // 总批次补给天数 double carTotalCount = carTotalCountRef.get(); // 各泊位物资车次总值 // 补给方案 -- 批次总补给数量 double harborShipCountPercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForShipCount(shipCount)); // 补给方案 -- 批次单舰折叠补给时间 double harborShipEmptyPercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForShipEmpty(supplyTimeDif/ shipCount)); // 补给方案 -- 各港口结束最大时间差 double harborEndTimeAvgPercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForHarborTimeDif(maxMinTimeDif)); // 补给方案 -- 最大时间差占总时间比 double harborMaxDifPercent = NumberUtils.roundToTwoDecimalPlaces( 1 - (double)maxMinTimeDif / allTime ); // 补给方案 -- 本批次跨区调度舰船数 double harborZoomDifPercent = NumberUtils.roundToTwoDecimalPlaces(1 - (double)posAreaNotSameCount / shipCount); // 补给方案 -- 各港口结束时间差 0.4 开始时间 结束时间 // double harborAvgTimePercent = NumberUtils.roundToTwoDecimalPlaces(1-(double)(allocTime - supplyTime)/allocTime); // // 补给方案 -- 港口时间差占比均值 每个港口 补给时间 开始结束时间差 // double harborTimePercent = NumberUtils.roundToTwoDecimalPlaces(1- (double)supplyTime / allocTime); // 受补舰船 -- 单舰平均停靠时间 double applyShipAvgStopTimePercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForShipStopTime(supplyTime / shipCount)); // 受补舰船 -- 补给作业时间均值 double applyShipWorkTimePercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForApplyTime(supplyTime / shipCount)); // 受补舰船 -- 补给非作业时间均值 double applyShipWorkNoTimePercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForNotApplyTime(supplyTimeDif/ shipCount)); // 受补舰船 -- 补给作业停泊比例 0.2 所有船补给总和 所有船占用时长 double shipStopPercent = NumberUtils.roundToTwoDecimalPlaces((double)shipSupplyTime/shipAllocTime); // 受补舰船 -- 补给时间折叠比例 最大开始-结束时间 所有船补给时间总和 double shipEmptyPercent = NumberUtils.roundToTwoDecimalPlaces(1 - (double)(shipSupplyTime/detailList.size()) / allTime); // 补给兵力 -- 水保日作业时间比 0.2 默认值 1 (不计算) double applyWaterPercent = 1; // 补给兵力 -- 油保日作业时间比 加油总时长/泊位数/停靠总时长 double applyOilPercent = NumberUtils.roundToTwoDecimalPlaces((double)oilTime / berthCount / allTime ); // 补给兵力 -- 物资日作业时间比 加油总时长/泊位数/停靠总时长 double applyMatPercent = NumberUtils.roundToTwoDecimalPlaces((double)matTime / allocTime); // 补给设施 -- 泊位日均周转艘次 double facBerthPercent = NumberUtils.roundToTwoDecimalPlaces( AssessUtil.getPercentForBerthTime( NumberUtils.roundToTwoDecimalPlaces(shipCount / days / berthCount ))); // 补给设施 -- 前沿占地最大均值 0.2 double facPartPercent = NumberUtils.roundToTwoDecimalPlaces(platMaxPer / berthCount / 1.5); if(facPartPercent > 1) facPartPercent = 1.0; // 补给设施 -- 前沿占半时长均值 double facPartTimePercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForBerthHoldTime((double) platHalfTime / berthCount)); // 补给设施 -- 加油设施日均使用率 double facOilPercent = NumberUtils.roundToTwoDecimalPlaces((double)oilTime / berthCount / allTime ); // 补给设施 -- 各泊位物资车次均值 double facCarPercent = NumberUtils.roundToTwoDecimalPlaces(AssessUtil.getPercentForCar((double)carTotalCount / berthCount )); result.add(new DsEffectAssessList("JD","A12", harborShipCountPercent, 0.1, 0.4)); result.add(new DsEffectAssessList("JD","A13", harborShipEmptyPercent, 0.2, 0.4)); result.add(new DsEffectAssessList("JD","A14", harborEndTimeAvgPercent, 0.2, 0.4)); result.add(new DsEffectAssessList("JD","A15", harborMaxDifPercent, 0.3, 0.4)); result.add(new DsEffectAssessList("JD","A16", harborZoomDifPercent, 0.2, 0.4)); result.add(new DsEffectAssessList("JD","A21", applyShipAvgStopTimePercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A22", applyShipWorkTimePercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A23", applyShipWorkNoTimePercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A24", shipStopPercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A25", shipEmptyPercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A31", applyWaterPercent, 0.3, 0.2)); result.add(new DsEffectAssessList("JD","A32", applyOilPercent, 0.4, 0.2)); result.add(new DsEffectAssessList("JD","A33", applyMatPercent, 0.3,0.2)); result.add(new DsEffectAssessList("JD","A41", facBerthPercent, 0.3, 0.2)); result.add(new DsEffectAssessList("JD","A42", facPartPercent, 0.1, 0.2)); result.add(new DsEffectAssessList("JD","A43", facPartTimePercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A44", facOilPercent, 0.2, 0.2)); result.add(new DsEffectAssessList("JD","A45", facCarPercent, 0.2, 0.2)); } return result; } // 获取泊位最大场地占用 private Double getMaxPlat(List list){ double maxPlat = 0.0; for (int i = 1; i <= 24; i++) { int oilCount = 0; int ammoCount = 0; int matCount = 0; for (DsTaskDetail task : list) { if (task.getOilStime() != null && task.getOilStime() <= i && task.getOilEtime() >= i) { oilCount++; } else if (task.getAmmoStime() != null && task.getAmmoStime() <= i && task.getAmmoEtime() >= i) { ammoCount++; } else if (task.getMatStime() != null && task.getMatStime() <= i && task.getMatEtime() >= i) { matCount++; } } Double totalSupply = NumberUtils.roundToTwoDecimalPlaces(oilCount * OIL_PLACE_PER + ammoCount * AMMO_PLACE_PER + matCount * MAT_PLACE_PER); if(maxPlat < totalSupply){ maxPlat = totalSupply; } } return maxPlat; } // 获取泊位占用过半的时间 private Integer getPlatHalfTime(List list){ Integer platTime = 0; for (int i = 1; i <= 24; i++) { int oilCount = 0; int ammoCount = 0; int matCount = 0; for (DsTaskDetail task : list) { if (task.getOilStime() != null && task.getOilStime() <= i && task.getOilEtime() >= i) { oilCount++; } else if (task.getAmmoStime() != null && task.getAmmoStime() <= i && task.getAmmoEtime() >= i) { ammoCount++; } else if (task.getMatStime() != null && task.getMatStime() <= i && task.getMatEtime() >= i) { matCount++; } } Double totalSupply = NumberUtils.roundToTwoDecimalPlaces(oilCount * OIL_PLACE_PER + ammoCount * AMMO_PLACE_PER + matCount * MAT_PLACE_PER); if(totalSupply >= 0.5){ platTime++; } } return platTime; } // 计算最终分数 private Double getScore(List list){ Double score = 0.0; if(null != list && list.size() > 0){ Map> groupedByCode = list.stream() .collect(Collectors.groupingBy(item -> item.getCODE().substring(0, 2))); for (Map.Entry> berthEntry : groupedByCode.entrySet()) { DsEffectAssessList effectAssess = berthEntry.getValue().get(0); Double classifyWeight = null == effectAssess.getClassifyWeight()?0.0:effectAssess.getClassifyWeight(); Double totalValue = berthEntry.getValue().stream() .mapToDouble(item -> item.getVal() * item.getWEIGHT()) .sum(); score += totalValue * classifyWeight; } } return NumberUtils.roundToTwoDecimalPlaces(score * 10); } private Double getScore3(List list){ ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); try { // 构建JavaScript函数 String script = "function calculateScore(list) {" + " var score = 0;" + " if(list && list.length > 0) {" + " var groupedByCode = {};" + " for(var i = 0; i < list.length; i++) {" + " var code = list[i].CODE.substring(0,2);" + " if(!groupedByCode[code]) {" + " groupedByCode[code] = [];" + " }" + " groupedByCode[code].push(list[i]);" + " }" + " for(var code in groupedByCode) {" + " var items = groupedByCode[code];" + " var classifyWeight = items[0].classifyWeight || 0;" + " var totalValue = 0;" + " for(var j = 0; j < items.length; j++) {" + " totalValue += items[j].val * items[j].WEIGHT;" + " }" + " score += totalValue * classifyWeight;" + " }" + " }" + " return Math.round(score * 10 * 100) / 100;" + "}"; // 编译并执行脚本 engine.eval(script); // 将Java对象转换为JSON String jsonList = JSONObject.toJSONString(list); // 调用JavaScript函数 Invocable invocable = (Invocable) engine; Object result = invocable.invokeFunction("calculateScore", jsonList); return Double.parseDouble(result.toString()); } catch (Exception e) { e.printStackTrace(); System.out.println("计算分数失败"); return 0.0; } } /** * 根据任务ID和舷号获取对应的任务 * @param list * @param taskid * @param shipNo * @return */ private boolean isShipPosAreaSame(List list, Long taskid, String shipNo, List harbor2List){ Optional tmpTaskOptions = list.stream().filter(task -> task.getTaskId() == taskid && task.getShipNo().equals(shipNo)).findFirst(); if(!tmpTaskOptions.isEmpty()){ Optional harbor2 = harbor2List.stream().filter(dmHarbor2 -> dmHarbor2.getDOCKNAME().equals(tmpTaskOptions.get().getPosArea()) && tmpTaskOptions.get().getHarborId() == dmHarbor2.getPKID()).findFirst(); if(!harbor2.isEmpty()){ return true; } } return false; } private DsEffectAssessHis covertToHis(DsEffectAssessList assessList, Long taskId){ DsEffectAssessHis his = null; if(null != assessList){ his = new DsEffectAssessHis(); his.setClassifyIndex(assessList.getClassifyIndex()); his.setCODE(assessList.getCODE()); his.setClassifyWeight(assessList.getClassifyWeight()); his.setITEM(assessList.getITEM()); his.setDeptId(assessList.getDeptId()); his.setItemDetail(assessList.getItemDetail()); his.setNORMAL(assessList.getNORMAL()); his.setPLAN(assessList.getPLAN()); his.setTaskId(assessList.getAssessId()); his.setTYPE(assessList.getTYPE()); his.setWEIGHT(assessList.getWEIGHT()); his.setUNIT(assessList.getUNIT()); his.setCreateBy(assessList.getCreateBy()); his.setCreateTime(new Date()); his.setVal(assessList.getVal()); his.setTaskId(taskId); } return his; } private List covertToHis(List assessLists, Long taskId){ List list = new ArrayList<>(); for(DsEffectAssessList tmp : assessLists){ list.add(this.covertToHis(tmp, taskId)); } return list; } // 更新权重值 private List updateWeight(List list, List weightList){ Long deptId = getDeptId(); List result = new ArrayList<>(); if(null != list && list.size() > 0 && null != weightList && weightList.size() > 0) { for(DsEffectAssessList item : list){ DsEffectAssessList tmpAssess = getWeightValFromList(weightList, item); if(null != tmpAssess){ item.setWEIGHT(tmpAssess.getWEIGHT()); item.setClassifyWeight(tmpAssess.getClassifyWeight()); item.setPLAN(tmpAssess.getPLAN()); item.setITEM(tmpAssess.getITEM()); item.setItemDetail(tmpAssess.getItemDetail()); item.setUNIT(tmpAssess.getUNIT()); item.setNORMAL(tmpAssess.getNORMAL()); item.setClassifyIndex(tmpAssess.getClassifyIndex()); item.setDeptId(deptId); } result.add(item); } } return result; } // 获取对应的权重值行 private DsEffectAssessList getWeightValFromList(List list, DsEffectAssessList dsEffectAssessList){ for (DsEffectAssessList assessList : list ){ if(assessList.getTYPE().equals(dsEffectAssessList.getTYPE()) && assessList.getCODE().equals(dsEffectAssessList.getCODE())){ return assessList; } } return null; } }