修改结算逻辑

This commit is contained in:
haozq 2026-01-22 17:45:16 +08:00
commit 463dfdc5ce
1 changed files with 503 additions and 0 deletions

View File

@ -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<String, Object> getNewSettlement(ProjectLeaseCostDetail o) {
// 获取领料明细
List<ProjectLeaseCostDetail> leaseDetails = queryProjectLeaseDetails(o);
// 获取退料明细
List<ProjectLeaseCostDetail> returnDetails = queryProjectReturnDetails(o);
// 创建一个新的列表来存储合并后的结果
List<ProjectLeaseCostDetail> mergedList = new ArrayList<>();
if (leaseDetails != null) {
mergedList.addAll(leaseDetails);
}
if (returnDetails != null) {
mergedList.addAll(returnDetails);
}
// 按照machineTypeId分组进行计算
Map<String, List<ProjectLeaseCostDetail>> groupedByMachineType = mergedList.stream().filter(Objects::nonNull)
.filter(item -> item.getMachineTypeId() != null)
.collect(Collectors.groupingBy(item -> String.valueOf(item.getMachineTypeId())));
// 存储计算结果
List<Map<String, Object>> 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<String, List<ProjectLeaseCostDetail>> entry : groupedByMachineType.entrySet()) {
Map<String, Object> timeLineData = new HashMap<>(); // 存储时间线上的数量变化
List<Map<String, Object>> segments = new ArrayList<>(); // 存储各个时间段
String machineTypeId = entry.getKey();
List<ProjectLeaseCostDetail> 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<String,Map<String,Object>> maps=new TreeMap<String, Map<String,Object>>();
//主要对时间内的操作进行操作
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<String,Object> map=maps.get(times)!=null?maps.get(times):new HashMap<String, Object>();
// 只处理统计期间开始之前的操作
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<ProjectLeaseCostDetail> list=(listObj != null) ?(List<ProjectLeaseCostDetail>) listObj:new ArrayList<ProjectLeaseCostDetail>();
//从操作中获取到数据
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<String, Map<String, Object>> mapObject : maps.entrySet()) {
Map<String, Object> 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<ProjectLeaseCostDetail> dataList=(listObj != null) ?(List<ProjectLeaseCostDetail>) listObj:new ArrayList<ProjectLeaseCostDetail>();
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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<ProjectLeaseCostDetail> sortedDetails = mergedList.stream().filter(Objects::nonNull).sorted(Comparator
.comparing(ProjectLeaseCostDetail::getOperateTime, Comparator.nullsFirst(Comparator.naturalOrder())))
.collect(Collectors.toList());
// 返回结果
Map<String, Object> 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<ProjectLeaseCostDetail> queryProjectLeaseDetails(ProjectLeaseCostDetail o) {
return projectCostDao.queryProjectLeaseDetails(o);
}
public List<ProjectLeaseCostDetail> queryProjectReturnDetails(ProjectLeaseCostDetail o) {
return projectCostDao.queryProjectReturnDetails(o);
}
}