diff --git a/src/com/bonus/cost/service/NewSettlementService.java b/src/com/bonus/cost/service/NewSettlementService.java new file mode 100644 index 0000000..d45d5c8 --- /dev/null +++ b/src/com/bonus/cost/service/NewSettlementService.java @@ -0,0 +1,503 @@ +package com.bonus.cost.service; + +import com.bonus.core.ExcelUtils; +import com.bonus.cost.beans.ProjectCostCalculation; +import com.bonus.cost.beans.ProjectCostCalculationDetail; +import com.bonus.cost.beans.ProjectCostCalculationSegment; +import com.bonus.cost.beans.ProjectLeaseCostDetail; +import com.bonus.cost.beans.ProjectSettlement; +import com.bonus.cost.dao.ProjectCostDao; +import com.bonus.plan.beans.PlanDevBean; +import com.bonus.plan.beans.PlanProBean; +import com.bonus.plan.dao.PlanApplicationDao; +import com.bonus.sys.UserShiroHelper; +import com.bonus.sys.beans.UserBean; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFCellStyle; +import org.apache.poi.hssf.usermodel.HSSFDataFormat; +import org.apache.poi.hssf.usermodel.HSSFFont; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.OutputStream; +import java.net.URLEncoder; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +/** + * @author : 阮世耀 + * @version : 1.0 + * @PackagePath: com.bonus.cost.service + * @CreateTime: 2025-05-14 15:08 + */ +@Service("NewSettlementService") +public class NewSettlementService { + + @Autowired + private ProjectCostDao projectCostDao; + + @Autowired + private PlanApplicationDao planApplicationDao; + + // 定义两个格式化器:输入格式(带时分秒)、输出格式(仅年月日) + private static final DateTimeFormatter INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + /** + * 新的结算逻辑 + * + * + */ + public Map getNewSettlement(ProjectLeaseCostDetail o) { + // 获取领料明细 + List leaseDetails = queryProjectLeaseDetails(o); + // 获取退料明细 + List returnDetails = queryProjectReturnDetails(o); + + // 创建一个新的列表来存储合并后的结果 + List mergedList = new ArrayList<>(); + if (leaseDetails != null) { + mergedList.addAll(leaseDetails); + } + if (returnDetails != null) { + mergedList.addAll(returnDetails); + } + + // 按照machineTypeId分组进行计算 + Map> groupedByMachineType = mergedList.stream().filter(Objects::nonNull) + .filter(item -> item.getMachineTypeId() != null) + .collect(Collectors.groupingBy(item -> String.valueOf(item.getMachineTypeId()))); + + // 存储计算结果 + List> calculationResults = new ArrayList<>(); + // 获取统计开始和结束日期 + String startTimeStr = o.getStartTime(); + String endTimeStr = o.getEndTime(); + //开始时间-转成日期 + //结束时间-转成日期 + LocalDateTime startDate = LocalDateTime.parse(startTimeStr + "T00:00:00"); + LocalDateTime endDate = LocalDateTime.parse(endTimeStr + "T23:59:59"); + + + double totalAmount = 0.0; + + //计算金额 + for (Map.Entry> entry : groupedByMachineType.entrySet()) { + Map timeLineData = new HashMap<>(); // 存储时间线上的数量变化 + List> segments = new ArrayList<>(); // 存储各个时间段 + + String machineTypeId = entry.getKey(); + List items = entry.getValue(); + items.sort(Comparator.comparing(ProjectLeaseCostDetail::getOperateTime, + Comparator.nullsFirst(Comparator.naturalOrder()))); + ProjectLeaseCostDetail firstItem = items.get(0); + // + if("2402".equals(machineTypeId)) { + System.err.println(machineTypeId); + } + double totalItemAmount = 0.0; + //循环逻辑处理 + //默认在操作日期之前 存量 + int currentCount = 0; + //计算统计日期之内的 + Map> maps=new TreeMap>(); + //主要对时间内的操作进行操作 + boolean add=true; + for (ProjectLeaseCostDetail item : items) { + if (item == null || item.getOperateTime() == null) { + continue; + } + //操作时间格式化 + LocalDateTime operateTime = LocalDateTime.parse(item.getOperateTime(), INPUT_FORMATTER); + String times = formatLocalDateTimeToDate(operateTime); + //获取按照时间存储的数据 + Map map=maps.get(times)!=null?maps.get(times):new HashMap(); + // 只处理统计期间开始之前的操作 + if (operateTime.isBefore(startDate)) { + boolean isLease = (item.getOperateType() == null || item.getOperateType() != 2); + if (isLease) { + int quantity = item.getLeaseNum() != null ? item.getLeaseNum() : 0; + currentCount += quantity; + } else { + int quantity = item.getReturnNum() != null ? item.getReturnNum() : 0; + currentCount -= quantity; + } + }else { + Object addObj = map.get("add"); + int addNum = (addObj != null) ? (int) addObj : 0; // 无值时给默认值0 + Object delObj = map.get("del"); + int delNum = (delObj != null) ? (int) delObj : 0; // 无值时给默认值0 + Object listObj = map.get("list"); + @SuppressWarnings("unchecked") + List list=(listObj != null) ?(List) listObj:new ArrayList(); + //从操作中获取到数据 + boolean isLease = (item.getOperateType() == null || item.getOperateType() != 2); + list.add(item); + //出库 + if (isLease) { + int quantity = item.getLeaseNum() != null ? item.getLeaseNum() : 0; + addNum += quantity; + } else { + //入库 + int quantity = item.getReturnNum() != null ? item.getReturnNum() : 0; + delNum += quantity; + } + //统计添加到map中 + map.put("add", addNum); + map.put("del", delNum); + map.put("list", list); + maps.put(times, map); + } + } + /** + * 第一个开始时间 + */ + LocalDateTime fasetStarTime=null; + if(currentCount>0) { + //如果之前有数据 则必须是条件的第天 + fasetStarTime=startDate; + } + double unitPrice = firstItem.getPrice() != null ? firstItem.getPrice() : 0; + //直接循环获取数据 + // String fasetStarTimeStr = formatLocalDateTimeToDate(fasetStarTime); + + for (Map.Entry> mapObject : maps.entrySet()) { + Map mapData=mapObject.getValue(); + String key=mapObject.getKey(); + //从数据中获取到相关数据 + Object addObj = mapData.get("add"); + int addNum = (addObj != null) ? (int) addObj : 0; // 无值时给默认值0 + Object delObj = mapData.get("del"); + int delNum = (delObj != null) ? (int) delObj : 0; // 无值时给默认值0 + Object listObj = mapData.get("list"); + @SuppressWarnings("unchecked") + List dataList=(listObj != null) ?(List) listObj:new ArrayList(); + ProjectLeaseCostDetail itemData=dataList.get(dataList.size()-1); + LocalDateTime operateTime = LocalDateTime.parse(itemData.getOperateTime(), INPUT_FORMATTER); + System.out.println("日期:" + key); + //正对两种特殊的情况 -一种是 操作在开始时间的 + //一种操作在结束时间的 + //操作在开始时间的 + if(key.equals(startTimeStr)) { + //先把数量加上来 + currentCount=currentCount+addNum; + //记录操作时间 + fasetStarTime=operateTime; + //初始化时间只有领取的 -无退换的 则不需要计算 下方是有退料的 则需要将使用的时间计算出来 + if(delNum!=0) { + //如果今天有退还的 并且是开始的时间必须要按照一天计算 + long daysBetween=getDay(fasetStarTime,operateTime); + if(daysBetween==0) { + add=false; + daysBetween=daysBetween+1; + double segmentAmount = currentCount * unitPrice * daysBetween; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", daysBetween); + totalItemAmount=totalItemAmount+segmentAmount; + //计算数量 + currentCount=currentCount+addNum; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + //下次计算进行减去今日退换的 + currentCount=currentCount-delNum; + } + } + //操作在结束时间点 + }else if(key.equals(endTimeStr)) { + + //存在之前领用的数据的 先将之前的数据计算出来 + if(currentCount>0) { + //有领用的 + if(addNum>0) { + //计算天数 + long daysBetween=getDay(fasetStarTime,operateTime); + if(daysBetween==0) { + daysBetween=1; + add=false; + } + double segmentAmount = currentCount * unitPrice * daysBetween; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", daysBetween); + totalItemAmount=totalItemAmount+segmentAmount; + + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + fasetStarTime=operateTime; + //计算数量 + currentCount=currentCount+addNum; + // 然后将领取的座位最后一天单独计算 + double segmentAmount2 = currentCount * unitPrice * 1; + Map segment2 = new HashMap<>(); + segment2.put("startTime",fasetStarTime.toString()); + segment2.put("endTime", operateTime.toString()); + segment2.put("days", 1); + segment2.put("count", currentCount); + segment2.put("amount", segmentAmount2); + segments.add(segment2); + totalItemAmount=totalItemAmount+segmentAmount2; + }else { + //计算天数 + long daysBetween=getDay(fasetStarTime,operateTime); + if(add) { + daysBetween=daysBetween+1; + } + double segmentAmount = currentCount * unitPrice * daysBetween; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", daysBetween); + totalItemAmount=totalItemAmount+segmentAmount; + //计算数量 + currentCount=currentCount+addNum; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + fasetStarTime=operateTime; + + } + }else { + if(fasetStarTime==null) { + fasetStarTime=operateTime; + } + //计算数量 + currentCount=currentCount+addNum; + //计算最后一天的数据 + double segmentAmount = currentCount * unitPrice * 1; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", 1); + totalItemAmount=totalItemAmount+segmentAmount; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + + segments.add(segment); + fasetStarTime=operateTime; + } + + }else { + if(delNum>0) { + //先把领取的 + + long daysBetween=getDay(fasetStarTime,operateTime); + if(daysBetween==0) { + daysBetween=1; + add =false; + } + + double segmentAmount = currentCount * unitPrice * daysBetween; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", daysBetween); + totalItemAmount=totalItemAmount+segmentAmount; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + currentCount=currentCount+addNum; + + // 然后将领取的座位最后一天单独计算 + double segmentAmount2 = currentCount * unitPrice * 1; + Map segment2 = new HashMap<>(); + LocalDateTime nextDayTime = operateTime.plusDays(1); + segment2.put("startTime",operateTime.toString()); + segment2.put("endTime", nextDayTime.toString()); + segment2.put("days", 1); + segment2.put("count", currentCount); + segment2.put("amount", segmentAmount2); + segments.add(segment2); + totalItemAmount=totalItemAmount+segmentAmount2; + //然后减去 + currentCount=currentCount-delNum; + fasetStarTime=nextDayTime; + + }else { + //无之前的数据的 第一次进来的 + if(fasetStarTime==null) { + currentCount=currentCount+addNum; + fasetStarTime=operateTime; + if(delNum!=0) { + //如果今天有退还的 并且是开始的时间必须要按照一天计算 + long daysBetween=getDay(fasetStarTime,operateTime); + if(daysBetween==0) { + double segmentAmount = currentCount * unitPrice * daysBetween; + daysBetween=daysBetween+1; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", daysBetween); + totalItemAmount=totalItemAmount+segmentAmount; + //计算数量 + currentCount=currentCount+addNum; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + //下次计算进行减去今日退换的 + currentCount=currentCount-delNum; + } + } + }else { + //处于中间的 之前有数据的 + //有添加数据的 + long daysBetween=getDay(fasetStarTime,operateTime); + if(daysBetween==0) { + daysBetween=daysBetween+1; + add=false; + } + if(currentCount+addNum-addNum==0) { + daysBetween=daysBetween+1; + add=false; + } + + double segmentAmount = currentCount * unitPrice * daysBetween; + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", operateTime.toString()); + segment.put("days", daysBetween); + totalItemAmount=totalItemAmount+segmentAmount; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + fasetStarTime=operateTime; + //将数量减少-或者添加 + currentCount=currentCount+addNum; + currentCount=currentCount-delNum; + } + + + + + } + + + } + } + //如果还是存在数据--并且 最后一天未计算的 + String fasetStarTimeStr = formatLocalDateTimeToDate(fasetStarTime); + if(currentCount>0 && !fasetStarTimeStr.equals(endTimeStr)) { + long daysBetween=getDay(fasetStarTime,endDate); + if(daysBetween==0) { + daysBetween=1; + }else { + if(add) { + daysBetween=daysBetween+1; + } + + } + Map segment = new HashMap<>(); + segment.put("startTime",fasetStarTime.toString()); + segment.put("endTime", endDate.toString()); + segment.put("days", daysBetween); + double segmentAmount = currentCount * unitPrice * daysBetween; + totalItemAmount=totalItemAmount+segmentAmount; + segment.put("count", currentCount); + segment.put("amount", segmentAmount); + segments.add(segment); + } + //至此计算结束 + // 创建计算结果对象 + Map calculationResult = new HashMap<>(); + calculationResult.put("machineTypeId", machineTypeId); + calculationResult.put("machineTypeName", firstItem.getMachineTypeName()); + calculationResult.put("machineModel", firstItem.getMachineModel()); + calculationResult.put("machineUnit", firstItem.getMachineUnit()); + calculationResult.put("price", unitPrice); + calculationResult.put("currentCount", currentCount); + calculationResult.put("amount", totalItemAmount); + calculationResult.put("timeline", timeLineData); + calculationResult.put("segments", segments); + calculationResult.put("details", items); + calculationResults.add(calculationResult); + // 更新总金额 + totalAmount += totalItemAmount; + } + // 将原始明细按操作时间排序 + List sortedDetails = mergedList.stream().filter(Objects::nonNull).sorted(Comparator + .comparing(ProjectLeaseCostDetail::getOperateTime, Comparator.nullsFirst(Comparator.naturalOrder()))) + .collect(Collectors.toList()); + + // 返回结果 + Map result = new HashMap<>(); + result.put("details", sortedDetails); // 原始按时间排序的明细 + result.put("calculationResults", calculationResults); // 计算结果 + result.put("totalAmount", totalAmount); // 总金额 + return result; + + } + + + + /** + * 将local日期 转成当日日期 + * @param dateTime + * @return + */ + public static String formatLocalDateTimeToDate(LocalDateTime dateTime) { + if (dateTime == null) { + return null; // 处理空值,避免空指针 + } + return dateTime.format(OUTPUT_FORMATTER); + } + + /** + * 计算两个日期的 天数 + * @param operateTime + * @param endtime + * @return + */ + public long getDay(LocalDateTime operateTime,LocalDateTime endtime) { + LocalDate startDate = operateTime.toLocalDate(); + LocalDate endDate = endtime.toLocalDate(); + long naturalDays = ChronoUnit.DAYS.between(startDate, endDate); + // System.out.println("自然日天数差(正确结果):" + naturalDays); + return naturalDays; + } + + /** + * 获取两个日期的时间 + * @param starTime + * @param endTime + * @return + */ + public static long getDay(String starTime,String endTime) { + LocalDate startDate = LocalDate.parse(starTime, OUTPUT_FORMATTER); + LocalDate endDate = LocalDate.parse(endTime, OUTPUT_FORMATTER); + long naturalDays = ChronoUnit.DAYS.between(startDate, endDate); + // System.out.println("自然日天数差(正确结果):" + naturalDays); + return naturalDays; + } + + + public static void main(String[] args) { + System.err.print(getDay("2025-01-22","2025-01-22")); + } + + + + public List queryProjectLeaseDetails(ProjectLeaseCostDetail o) { + return projectCostDao.queryProjectLeaseDetails(o); + } + + + public List queryProjectReturnDetails(ProjectLeaseCostDetail o) { + return projectCostDao.queryProjectReturnDetails(o); + } + + +} \ No newline at end of file