(new)区间费用计算功能开发

This commit is contained in:
syruan 2025-08-30 16:53:11 +08:00
parent 03b9e4ce8c
commit 5a6aaf5da7
9 changed files with 911 additions and 9 deletions

View File

@ -67,19 +67,19 @@ public class LeaseOutDetailsServiceImpl implements ILeaseOutDetailsService {
private static final Integer SYNC_RESOURCE = GlobalConstants.INT_2;
@Autowired
LeaseApplyInfoMapper leaseApplyInfoMapper;
private LeaseApplyInfoMapper leaseApplyInfoMapper;
@Autowired
private LeaseOutDetailsMapper leaseOutDetailsMapper;
@Autowired
LeaseApplyDetailsMapper leaseApplyDetailsMapper;
private LeaseApplyDetailsMapper leaseApplyDetailsMapper;
@Autowired
MachineMapper machineMapper;
private MachineMapper machineMapper;
@Autowired
TypeMapper typeMapper;
private TypeMapper typeMapper;
@Autowired
private TmTaskMapper tmTaskMapper;
@ -88,10 +88,10 @@ public class LeaseOutDetailsServiceImpl implements ILeaseOutDetailsService {
private SltAgreementInfoMapper sltAgreementInfoMapper;
@Autowired
BmAgreementInfoMapper bmAgreementInfoMapper;
private BmAgreementInfoMapper bmAgreementInfoMapper;
@Autowired
TmTaskAgreementMapper tmTaskAgreementMapper;
private TmTaskAgreementMapper tmTaskAgreementMapper;
@Resource
private BmQrBoxMapper bmQrBoxMapper;

View File

@ -15,10 +15,9 @@ import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.bonus.common.biz.config.ListPagingUtil;
import com.bonus.common.biz.config.PoiOutPage;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.ServletUtils;
import com.bonus.common.log.enums.OperaType;
import com.bonus.common.security.utils.SecurityUtils;
import com.bonus.common.log.enums.OperatorType;
import com.bonus.material.basic.domain.BmProject;
import com.bonus.material.common.annotation.PreventRepeatSubmit;
import com.bonus.material.common.domain.dto.SelectDto;
@ -42,12 +41,17 @@ import org.springframework.web.bind.annotation.*;
import com.bonus.common.log.annotation.SysLog;
import com.bonus.common.security.annotation.RequiresPermissions;
import com.bonus.material.settlement.domain.SltAgreementInfo;
import com.bonus.material.settlement.domain.dto.PeriodCostQueryDto;
import com.bonus.material.settlement.domain.vo.PeriodCostResultVo;
import com.bonus.material.settlement.domain.vo.PeriodCostSummaryVo;
import com.bonus.material.settlement.service.ISltAgreementInfoService;
import com.bonus.common.core.web.controller.BaseController;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.core.utils.poi.ExcelUtil;
import com.bonus.common.core.web.page.TableDataInfo;
import java.util.ArrayList;
/**
* 结算信息Controller
*
@ -1530,4 +1534,72 @@ public class SltAgreementInfoController extends BaseController {
return error("系统错误, " + e.getMessage());
}
}
/**
* 查询租赁区间费用明细
*/
@ApiOperation(value = "查询租赁区间费用明细")
//@RequiresPermissions("settlement:info:periodCost")
@GetMapping("/leasePeriodCostDetails")
public TableDataInfo leasePeriodCostDetails(PeriodCostQueryDto queryDto) {
try {
//startPage();
List<PeriodCostResultVo> list = sltAgreementInfoService.selectPeriodCostList(queryDto);
return getDataTable(list);
} catch (Exception e) {
log.error("查询区间费用失败", e);
return getDataTable(new ArrayList<>());
}
}
/**
* 导出区间费用计算结果
*/
@ApiOperation(value = "导出区间费用计算结果")
//@RequiresPermissions("settlement:info:periodCostExport")
@SysLog(title = "区间费用计算", module = "费用报表", operatorType = OperatorType.MANAGE)
@PostMapping("/periodCost/export")
public void exportPeriodCost(HttpServletResponse response, @RequestBody PeriodCostQueryDto queryDto) {
try {
List<PeriodCostResultVo> list = sltAgreementInfoService.selectPeriodCostList(queryDto);
ExcelUtil<PeriodCostResultVo> util = new ExcelUtil<>(PeriodCostResultVo.class);
util.exportExcel(response, list, "区间费用计算结果");
} catch (Exception e) {
log.error("导出区间费用失败", e);
}
}
/**
* 查询区间租赁费用汇总报表按协议汇总
*/
@ApiOperation(value = "查询区间租赁费用汇总报表")
//@RequiresPermissions("settlement:info:periodCostSummary")
@GetMapping("leasePeriodCostList")
public TableDataInfo leasePeriodCostList(PeriodCostQueryDto queryDto) {
try {
//startPage();
List<PeriodCostSummaryVo> list = sltAgreementInfoService.selectPeriodCostSummary(queryDto);
return getDataTable(list);
} catch (Exception e) {
log.error("查询区间费用汇总失败", e);
return getDataTable(new ArrayList<>());
}
}
/**
* 导出区间费用汇总结果
*/
@ApiOperation(value = "导出区间费用汇总结果")
@RequiresPermissions("settlement:info:periodCostSummaryExport")
@SysLog(title = "区间费用汇总", module = "费用报表", operatorType = OperatorType.MANAGE)
@PostMapping("/periodCost/summary/export")
public void exportPeriodCostSummary(HttpServletResponse response, @RequestBody PeriodCostQueryDto queryDto) {
try {
List<PeriodCostSummaryVo> list = sltAgreementInfoService.selectPeriodCostSummary(queryDto);
ExcelUtil<PeriodCostSummaryVo> util = new ExcelUtil<>(PeriodCostSummaryVo.class);
util.exportExcel(response, list, "区间费用汇总结果");
} catch (Exception e) {
log.error("导出区间费用汇总失败", e);
}
}
}

View File

@ -0,0 +1,64 @@
package com.bonus.material.settlement.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* 区间费用查询DTO
*
* @author system
* @date 2024-12-30
*/
@Data
@ApiModel("区间费用查询DTO")
public class PeriodCostQueryDto {
@ApiModelProperty(value = "开始日期", required = true)
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "开始日期不能为空")
private Date startDate;
@ApiModelProperty(value = "结束日期", required = true)
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "结束日期不能为空")
private Date endDate;
@ApiModelProperty(value = "协议ID列表")
private List<Long> agreementIds;
@ApiModelProperty(value = "协议ID")
private Long agreementId;
@ApiModelProperty(value = "往来单位ID列表")
private List<Long> unitIds;
@ApiModelProperty(value = "工程标段ID列表")
private List<Long> projectIds;
@ApiModelProperty(value = "机具规格ID列表")
private List<Long> typeIds;
@ApiModelProperty(value = "结算状态 0-未结算 1-已结算")
private String settlementStatus;
@ApiModelProperty(value = "协议编号")
private String agreementCode;
@ApiModelProperty(value = "往来单位名称")
private String unitName;
@ApiModelProperty(value = "工程标段名称")
private String projectName;
@ApiModelProperty(value = "结算管理类型 1工器具 2安全工器具")
private Byte sltManageType;
@ApiModelProperty(value = "数据所属组织ID")
private Long companyId;
}

View File

@ -0,0 +1,96 @@
package com.bonus.material.settlement.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 区间费用计算结果VO
*
* @author syruan
*/
@Data
@ApiModel("区间费用计算结果VO")
public class PeriodCostResultVo {
@ApiModelProperty(value = "协议ID")
private Long agreementId;
@ApiModelProperty(value = "协议编号")
private String agreementCode;
@ApiModelProperty(value = "往来单位ID")
private Long unitId;
@ApiModelProperty(value = "往来单位名称")
private String unitName;
@ApiModelProperty(value = "工程标段ID")
private Long projectId;
@ApiModelProperty(value = "工程标段名称")
private String projectName;
@ApiModelProperty(value = "机具规格ID")
private Long typeId;
@ApiModelProperty(value = "机具规格名称")
private String typeName;
@ApiModelProperty(value = "规格型号")
private String modelName;
@ApiModelProperty(value = "机具ID")
private Long maId;
@ApiModelProperty(value = "机具编号")
private String maCode;
@ApiModelProperty(value = "租赁数量")
private BigDecimal num;
@ApiModelProperty(value = "租赁单价")
private BigDecimal leasePrice;
@ApiModelProperty(value = "计算开始时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date calcStartTime;
@ApiModelProperty(value = "计算结束时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date calcEndTime;
@ApiModelProperty(value = "租赁天数")
private Long leaseDays;
@ApiModelProperty(value = "租赁费用")
private BigDecimal leaseCost;
@ApiModelProperty(value = "是否已结算 0-未结算 1-已结算")
private String isSettled;
@ApiModelProperty(value = "结算时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date settlementTime;
@ApiModelProperty(value = "领料时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date startTime;
@ApiModelProperty(value = "退料时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date endTime;
@ApiModelProperty(value = "状态 0-在用 1-退回")
private String status;
@ApiModelProperty(value = "计量单位")
private String mtUnitName;
@ApiModelProperty(value = "备注")
private String remark;
}

View File

@ -0,0 +1,100 @@
package com.bonus.material.settlement.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.bonus.common.core.annotation.Excel;
import java.math.BigDecimal;
import java.util.Date;
/**
* 区间费用汇总结果VO按协议汇总
*
* @author system
* @date 2024-12-30
*/
@Data
@ApiModel("区间费用汇总结果VO")
public class PeriodCostSummaryVo {
@ApiModelProperty(value = "协议ID")
private Long agreementId;
@ApiModelProperty(value = "协议编号")
@Excel(name = "协议编号")
private String agreementCode;
@ApiModelProperty(value = "往来单位ID")
private Long unitId;
@ApiModelProperty(value = "往来单位名称")
@Excel(name = "往来单位")
private String unitName;
@ApiModelProperty(value = "工程标段ID")
private Long projectId;
@ApiModelProperty(value = "工程标段名称")
@Excel(name = "工程标段")
private String projectName;
@ApiModelProperty(value = "协议签订日期")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "签订日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date signTime;
@ApiModelProperty(value = "是否已结算 0-未结算 1-已结算")
@Excel(name = "结算状态", readConverterExp = "0=未结算,1=已结算")
private String isSettled;
@ApiModelProperty(value = "设备种类数量")
@Excel(name = "设备种类数")
private Integer equipmentTypeCount;
@ApiModelProperty(value = "设备总数量")
@Excel(name = "设备总数")
private BigDecimal totalEquipmentCount;
@ApiModelProperty(value = "总租赁天数")
@Excel(name = "总租赁天数")
private Long totalLeaseDays;
@ApiModelProperty(value = "总租赁费用")
@Excel(name = "总租赁费用")
private BigDecimal totalLeaseCost;
@ApiModelProperty(value = "平均日租金")
@Excel(name = "平均日租金")
private BigDecimal avgDailyRent;
@ApiModelProperty(value = "查询区间开始时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "查询开始日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date queryStartDate;
@ApiModelProperty(value = "查询区间结束时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "查询结束日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date queryEndDate;
@ApiModelProperty(value = "最早领料时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "最早领料时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date earliestLeaseTime;
@ApiModelProperty(value = "最晚退料时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "最晚退料时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date latestReturnTime;
@ApiModelProperty(value = "结算时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "结算时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date settlementTime;
@ApiModelProperty(value = "备注")
@Excel(name = "备注")
private String remark;
}

View File

@ -11,6 +11,8 @@ import com.bonus.material.settlement.domain.SltAgreementApply;
import com.bonus.material.settlement.domain.SltAgreementInfo;
import com.bonus.material.settlement.domain.SltAgreementRelation;
import com.bonus.material.settlement.domain.vo.SltInfoVo;
import com.bonus.material.settlement.domain.dto.PeriodCostQueryDto;
import com.bonus.material.settlement.domain.vo.PeriodCostResultVo;
import com.bonus.material.task.domain.TmTask;
import org.apache.ibatis.annotations.Param;
@ -267,4 +269,11 @@ public interface SltAgreementInfoMapper {
* @return
*/
List<SltAgreementInfo> getSltAgreementInfoById(SltAgreementInfo info);
/**
* 查询区间费用计算结果
* @param queryDto 查询条件
* @return 区间费用计算结果列表
*/
List<PeriodCostResultVo> selectLeasePeriodCostList(PeriodCostQueryDto queryDto);
}

View File

@ -5,10 +5,12 @@ import java.util.List;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.material.basic.domain.BmProject;
import com.bonus.material.common.domain.dto.SelectDto;
import com.bonus.material.countersign.domain.SignConfigVo;
import com.bonus.material.settlement.domain.SltAgreementApply;
import com.bonus.material.settlement.domain.SltAgreementInfo;
import com.bonus.material.settlement.domain.vo.SltInfoVo;
import com.bonus.material.settlement.domain.dto.PeriodCostQueryDto;
import com.bonus.material.settlement.domain.vo.PeriodCostResultVo;
import com.bonus.material.settlement.domain.vo.PeriodCostSummaryVo;
/**
* 结算信息Service接口
@ -156,4 +158,20 @@ public interface ISltAgreementInfoService {
AjaxResult getAgreementInfoById(SelectDto dto);
/**
* 查询区间费用计算结果
*
* @param queryDto 查询条件
* @return 区间费用计算结果列表
*/
List<PeriodCostResultVo> selectPeriodCostList(PeriodCostQueryDto queryDto);
/**
* 查询区间费用汇总结果按协议汇总
*
* @param queryDto 查询条件
* @return 区间费用汇总结果列表
*/
List<PeriodCostSummaryVo> selectPeriodCostSummary(PeriodCostQueryDto queryDto);
}

View File

@ -32,11 +32,19 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bonus.material.settlement.mapper.SltAgreementInfoMapper;
import com.bonus.material.settlement.domain.SltAgreementInfo;
import com.bonus.material.settlement.domain.dto.PeriodCostQueryDto;
import com.bonus.material.settlement.domain.vo.PeriodCostResultVo;
import com.bonus.material.settlement.domain.vo.PeriodCostSummaryVo;
import com.bonus.material.settlement.service.ISltAgreementInfoService;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
/**
* 结算信息Service业务层处理
@ -1059,4 +1067,419 @@ public class SltAgreementInfoServiceImpl implements ISltAgreementInfoService {
}
return AjaxResult.success(vo);
}
/**
* 查询区间费用计算结果
*
* @param queryDto 查询条件
* @return 区间费用计算结果列表
*/
@Override
public List<PeriodCostResultVo> selectPeriodCostList(@NotNull(message = "查询条件不能为空") PeriodCostQueryDto queryDto) {
// 参数校验
if (queryDto.getStartDate() == null || queryDto.getEndDate() == null) {
throw new ServiceException("开始日期和结束日期不能为空");
}
if (queryDto.getStartDate().after(queryDto.getEndDate())) {
throw new ServiceException("开始日期不能大于结束日期");
}
try {
// 查询当前登陆用户的结算管理权限
Byte settlementType = this.checkLoginUserHasSettlementPermission();
queryDto.setSltManageType(settlementType);
// 从数据库查询基础数据
List<PeriodCostResultVo> rawResults = sltAgreementInfoMapper.selectLeasePeriodCostList(queryDto);
// 先过滤掉不在查询范围内的数据
List<PeriodCostResultVo> filteredResults = filterDataInRange(rawResults, queryDto.getStartDate(), queryDto.getEndDate());
// 在Service层进行逻辑计算
return calculatePeriodCosts(filteredResults, queryDto.getStartDate(), queryDto.getEndDate());
} catch (Exception e) {
throw new ServiceException("查询区间费用失败:" + e.getMessage());
}
}
/**
* 过滤掉不在查询范围内的数据
*
* @param rawResults 原始查询结果
* @param inputStartDate 前端输入的开始日期
* @param inputEndDate 前端输入的结束日期
* @return 过滤后的结果列表
*/
private List<PeriodCostResultVo> filterDataInRange(List<PeriodCostResultVo> rawResults,
Date inputStartDate, Date inputEndDate) {
List<PeriodCostResultVo> filteredResults = new ArrayList<>();
for (PeriodCostResultVo result : rawResults) {
if (isDataInRange(result, inputStartDate, inputEndDate)) {
filteredResults.add(result);
}
}
return filteredResults;
}
/**
* 判断数据是否在查询范围内
*
* @param result 数据记录
* @param inputStartDate 前端输入的开始日期
* @param inputEndDate 前端输入的结束日期
* @return 是否在范围内
*/
private boolean isDataInRange(PeriodCostResultVo result, Date inputStartDate, Date inputEndDate) {
Date leaseStartTime = result.getStartTime(); // 领料时间
Date returnTime = result.getEndTime(); // 退料时间
Date settlementTime = result.getSettlementTime(); // 结算时间
// 如果是已结算协议
if ("1".equals(result.getIsSettled())) {
// 检查领料时间退料时间结算时间是否有任何一个在查询范围内
boolean leaseInRange = isDateInRange(leaseStartTime, inputStartDate, inputEndDate);
boolean returnInRange = isDateInRange(returnTime, inputStartDate, inputEndDate);
boolean settlementInRange = isDateInRange(settlementTime, inputStartDate, inputEndDate);
return leaseInRange || returnInRange || settlementInRange;
} else {
// 如果是未结算协议
// 检查领料时间退料时间是否有任何一个在查询范围内
boolean leaseInRange = isDateInRange(leaseStartTime, inputStartDate, inputEndDate);
boolean returnInRange = isDateInRange(returnTime, inputStartDate, inputEndDate);
return leaseInRange || returnInRange;
}
}
/**
* 判断日期是否在指定范围内
*
* @param date 要检查的日期
* @param startDate 范围开始日期
* @param endDate 范围结束日期
* @return 是否在范围内
*/
private boolean isDateInRange(Date date, Date startDate, Date endDate) {
if (date == null) {
return false;
}
return !date.before(startDate) && !date.after(endDate);
}
/**
* 计算区间费用
*
* @param filteredResults 过滤后的查询结果
* @param inputStartDate 前端输入的开始日期
* @param inputEndDate 前端输入的结束日期
* @return 计算后的结果列表
*/
private List<PeriodCostResultVo> calculatePeriodCosts(List<PeriodCostResultVo> filteredResults,
Date inputStartDate, Date inputEndDate) {
List<PeriodCostResultVo> calculatedResults = new ArrayList<>();
for (PeriodCostResultVo result : filteredResults) {
try {
// 计算实际的开始和结束时间
Date calcStartTime;
Date calcEndTime;
if ("1".equals(result.getIsSettled())) {
// 已结算协议计算规则
calcStartTime = calculateSettledStartTime(result, inputStartDate);
calcEndTime = calculateSettledEndTime(result, inputEndDate);
} else {
// 未结算协议计算规则
calcStartTime = calculateUnsettledStartTime(result, inputStartDate);
calcEndTime = calculateUnsettledEndTime(result, inputEndDate);
}
// 确保计算时间在输入区间内且合理
if (calcStartTime != null && calcEndTime != null && !calcStartTime.after(calcEndTime)) {
// 计算租赁天数
long leaseDays = calculateDaysBetween(calcStartTime, calcEndTime);
// 计算租赁费用
BigDecimal leaseCost = BigDecimal.ZERO;
if (result.getNum() != null && result.getLeasePrice() != null && leaseDays > 0) {
leaseCost = result.getNum()
.multiply(result.getLeasePrice())
.multiply(new BigDecimal(leaseDays))
.setScale(2, RoundingMode.HALF_UP);
}
// 设置计算结果
result.setCalcStartTime(calcStartTime);
result.setCalcEndTime(calcEndTime);
result.setLeaseDays(leaseDays);
result.setLeaseCost(leaseCost);
calculatedResults.add(result);
}
} catch (Exception e) {
// 记录错误但不中断处理
System.err.println("计算协议 " + result.getAgreementCode() + " 费用时出错: " + e.getMessage());
}
}
return calculatedResults;
}
/**
* 计算已结算协议的开始时间
*/
private Date calculateSettledStartTime(PeriodCostResultVo result, Date inputStartDate) {
Date leaseStartTime = result.getStartTime(); // 领料时间
if (leaseStartTime == null) {
return inputStartDate;
}
// 如果领料时间在输入区间开始之前使用输入开始时间
if (leaseStartTime.before(inputStartDate)) {
return inputStartDate;
}
// 否则使用领料时间
return leaseStartTime;
}
/**
* 计算已结算协议的结束时间
*/
private Date calculateSettledEndTime(PeriodCostResultVo result, Date inputEndDate) {
Date settlementTime = result.getSettlementTime(); // 结算时间
Date returnTime = result.getEndTime(); // 退料时间
// 如果有退料时间优先使用退料时间
if (returnTime != null) {
// 如果退料时间在输入区间结束之后使用输入结束时间
if (returnTime.after(inputEndDate)) {
return inputEndDate;
}
return returnTime;
}
// 如果没有退料时间但有结算时间使用结算时间作为退料时间
if (settlementTime != null) {
// 如果结算时间在输入区间结束之前使用结算时间
if (settlementTime.before(inputEndDate) || settlementTime.equals(inputEndDate)) {
return settlementTime;
}
// 如果结算时间在输入区间结束之后使用输入结束时间
return inputEndDate;
}
// 如果既没有退料时间也没有结算时间使用输入结束时间
return inputEndDate;
}
/**
* 计算未结算协议的开始时间
*/
private Date calculateUnsettledStartTime(PeriodCostResultVo result, Date inputStartDate) {
Date leaseStartTime = result.getStartTime(); // 领料时间
if (leaseStartTime == null) {
return inputStartDate;
}
// 如果领料时间在输入区间开始之前使用输入开始时间
if (leaseStartTime.before(inputStartDate)) {
return inputStartDate;
}
// 否则使用领料时间
return leaseStartTime;
}
/**
* 计算未结算协议的结束时间
*/
private Date calculateUnsettledEndTime(PeriodCostResultVo result, Date inputEndDate) {
Date returnTime = result.getEndTime(); // 退料时间
// 如果有退料时间
if (returnTime != null) {
// 如果退料时间在输入区间结束之后使用输入结束时间
if (returnTime.after(inputEndDate)) {
return inputEndDate;
}
return returnTime;
}
// 如果没有退料时间使用输入结束时间
return inputEndDate;
}
/**
* 计算两个日期之间的天数包含起始和结束日期
* 只计算日期不考虑具体时间
* 例如8月15日8月16日 = 2天
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 天数
*/
private long calculateDaysBetween(Date startDate, Date endDate) {
if (startDate == null || endDate == null) {
return 0;
}
if (startDate.after(endDate)) {
return 0;
}
// 使用Calendar来确保只计算日期部分忽略时间
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
startCal.set(Calendar.HOUR_OF_DAY, 0);
startCal.set(Calendar.MINUTE, 0);
startCal.set(Calendar.SECOND, 0);
startCal.set(Calendar.MILLISECOND, 0);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
endCal.set(Calendar.HOUR_OF_DAY, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.MILLISECOND, 0);
// 计算天数差异
long diffInMillies = endCal.getTimeInMillis() - startCal.getTimeInMillis();
long diffInDays = diffInMillies / (24 * 60 * 60 * 1000);
// 包含起始和结束日期所以要加1
return diffInDays + 1;
}
/**
* 查询区间费用汇总结果按协议汇总
*
* @param queryDto 查询条件
* @return 区间费用汇总结果列表
*/
@Override
public List<PeriodCostSummaryVo> selectPeriodCostSummary(PeriodCostQueryDto queryDto) {
// 参数校验
if (queryDto.getStartDate() == null || queryDto.getEndDate() == null) {
throw new ServiceException("开始日期和结束日期不能为空");
}
if (queryDto.getStartDate().after(queryDto.getEndDate())) {
throw new ServiceException("开始日期不能大于结束日期");
}
try {
// 先获取明细数据
List<PeriodCostResultVo> detailList = this.selectPeriodCostList(queryDto);
// 按协议ID分组汇总
return aggregateByAgreement(detailList, queryDto.getStartDate(), queryDto.getEndDate());
} catch (Exception e) {
throw new ServiceException("查询区间费用汇总失败:" + e.getMessage());
}
}
/**
* 按协议汇总费用数据
*
* @param detailList 明细数据列表
* @param queryStartDate 查询开始日期
* @param queryEndDate 查询结束日期
* @return 汇总结果列表
*/
private List<PeriodCostSummaryVo> aggregateByAgreement(List<PeriodCostResultVo> detailList,
Date queryStartDate, Date queryEndDate) {
// 按协议ID分组
Map<Long, List<PeriodCostResultVo>> groupedByAgreement = detailList.stream()
.collect(Collectors.groupingBy(PeriodCostResultVo::getAgreementId));
List<PeriodCostSummaryVo> summaryList = new ArrayList<>();
for (Map.Entry<Long, List<PeriodCostResultVo>> entry : groupedByAgreement.entrySet()) {
Long agreementId = entry.getKey();
List<PeriodCostResultVo> agreementDetails = entry.getValue();
if (agreementDetails.isEmpty()) {
continue;
}
// 取第一条记录的基本信息
PeriodCostResultVo firstDetail = agreementDetails.get(0);
PeriodCostSummaryVo summary = new PeriodCostSummaryVo();
summary.setAgreementId(agreementId);
summary.setAgreementCode(firstDetail.getAgreementCode());
summary.setUnitId(firstDetail.getUnitId());
summary.setUnitName(firstDetail.getUnitName());
summary.setProjectId(firstDetail.getProjectId());
summary.setProjectName(firstDetail.getProjectName());
summary.setIsSettled(firstDetail.getIsSettled());
summary.setQueryStartDate(queryStartDate);
summary.setQueryEndDate(queryEndDate);
summary.setRemark(firstDetail.getRemark());
// 计算汇总数据
int equipmentTypeCount = agreementDetails.size(); // 设备种类数量
BigDecimal totalEquipmentCount = agreementDetails.stream()
.map(detail -> detail.getNum() != null ? detail.getNum() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add); // 设备总数量
long totalLeaseDays = agreementDetails.stream()
.mapToLong(detail -> detail.getLeaseDays() != null ? detail.getLeaseDays() : 0L)
.sum(); // 总租赁天数
BigDecimal totalLeaseCost = agreementDetails.stream()
.map(detail -> detail.getLeaseCost() != null ? detail.getLeaseCost() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add); // 总租赁费用
// 计算平均日租金
BigDecimal avgDailyRent = BigDecimal.ZERO;
if (totalLeaseDays > 0) {
avgDailyRent = totalLeaseCost.divide(new BigDecimal(totalLeaseDays), 2, RoundingMode.HALF_UP);
}
// 找出最早领料时间和最晚退料时间
Date earliestLeaseTime = agreementDetails.stream()
.map(PeriodCostResultVo::getStartTime)
.filter(Objects::nonNull)
.min(Date::compareTo)
.orElse(null);
Date latestReturnTime = agreementDetails.stream()
.map(PeriodCostResultVo::getEndTime)
.filter(Objects::nonNull)
.max(Date::compareTo)
.orElse(null);
Date settlementTime = agreementDetails.stream()
.map(PeriodCostResultVo::getSettlementTime)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
summary.setEquipmentTypeCount(equipmentTypeCount);
summary.setTotalEquipmentCount(totalEquipmentCount);
summary.setTotalLeaseDays(totalLeaseDays);
summary.setTotalLeaseCost(totalLeaseCost);
summary.setAvgDailyRent(avgDailyRent);
summary.setEarliestLeaseTime(earliestLeaseTime);
summary.setLatestReturnTime(latestReturnTime);
summary.setSettlementTime(settlementTime);
summaryList.add(summary);
}
// 按协议编号排序
summaryList.sort(Comparator.comparing(PeriodCostSummaryVo::getAgreementCode));
return summaryList;
}
}

View File

@ -25,6 +25,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="updateTime" column="update_time" />
</resultMap>
<!-- 区间费用计算查询 -->
<resultMap type="com.bonus.material.settlement.domain.vo.PeriodCostResultVo" id="PeriodCostResultMap">
<result property="agreementId" column="agreement_id"/>
<result property="agreementCode" column="agreement_code"/>
<result property="unitId" column="unit_id"/>
<result property="unitName" column="unit_name"/>
<result property="projectId" column="project_id"/>
<result property="projectName" column="project_name"/>
<result property="typeId" column="type_id"/>
<result property="typeName" column="type_name"/>
<result property="modelName" column="model_name"/>
<result property="maId" column="ma_id"/>
<result property="maCode" column="ma_code"/>
<result property="num" column="num"/>
<result property="leasePrice" column="lease_price"/>
<result property="isSettled" column="is_settled"/>
<result property="settlementTime" column="settlement_time"/>
<result property="startTime" column="start_time"/>
<result property="endTime" column="end_time"/>
<result property="status" column="status"/>
<result property="mtUnitName" column="mt_unit_name"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="selectSltAgreementInfoVo">
select id, agreement_id, type_id, ma_id, num, start_time, end_time, status, lease_id, back_id, lease_price, buy_price, is_slt, slt_time, company_id, lease_type, trim_day, create_time, update_time from slt_agreement_info
</sql>
@ -1082,4 +1106,100 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
GROUP BY
sai.agreement_id,mt.type_id
</select>
<select id="selectLeasePeriodCostList" parameterType="com.bonus.material.settlement.domain.dto.PeriodCostQueryDto" resultMap="PeriodCostResultMap">
SELECT
bai.agreement_id,
bai.agreement_code,
bai.unit_id,
bu.unit_name,
bai.project_id,
bp.pro_name as project_name,
sai.type_id,
mt.type_name as model_name,
mt1.type_name as type_name,
sai.ma_id,
sai.num,
sai.lease_price,
sai.start_time,
sai.end_time,
bai.is_slt AS is_settled,
sai.slt_time AS settlement_time,
sai.status,
mt.unit_name as mt_unit_name,
bai.remark
FROM bm_agreement_info bai
INNER JOIN slt_agreement_info sai ON bai.agreement_id = sai.agreement_id
LEFT JOIN bm_unit bu ON bai.unit_id = bu.unit_id
LEFT JOIN bm_project bp ON bai.project_id = bp.pro_id
LEFT JOIN ma_type mt ON sai.type_id = mt.type_id AND mt.`level` = '4'
LEFT JOIN ma_type mt1 ON mt.parent_id = mt1.type_id AND mt1.`level` = '3'
<where>
bai.status = '1'
AND sai.status IN ('0', '1')
AND mt.jiju_type = #{sltManageType}
<if test="agreementCode != null and agreementCode != ''">
AND bai.agreement_code LIKE concat('%',#{agreementCode},'%')
</if>
<if test="agreementId != null and agreementId > 0">
AND bai.agreement_id = #{agreementId}
</if>
<if test="agreementIds != null and agreementIds.size() > 0">
AND bai.agreement_id IN
<foreach collection="agreementIds" item="agreementId" open="(" separator="," close=")">
#{agreementId}
</foreach>
</if>
<if test="unitIds != null and unitIds.size() > 0">
AND bai.unit_id IN
<foreach collection="unitIds" item="unitId" open="(" separator="," close=")">
#{unitId}
</foreach>
</if>
<if test="projectIds != null and projectIds.size() > 0">
AND bai.project_id IN
<foreach collection="projectIds" item="projectId" open="(" separator="," close=")">
#{projectId}
</foreach>
</if>
<if test="typeIds != null and typeIds.size() > 0">
AND sai.type_id IN
<foreach collection="typeIds" item="typeId" open="(" separator="," close=")">
#{typeId}
</foreach>
</if>
<if test="settlementStatus != null and settlementStatus != ''">
AND bai.is_slt = #{settlementStatus}
</if>
<if test="agreementCode != null and agreementCode != ''">
AND bai.agreement_code LIKE CONCAT('%', #{agreementCode}, '%')
</if>
<if test="unitName != null and unitName != ''">
AND bu.unit_name LIKE CONCAT('%', #{unitName}, '%')
</if>
<if test="projectName != null and projectName != ''">
AND bp.project_name LIKE CONCAT('%', #{projectName}, '%')
</if>
<if test="companyId != null">
AND bai.company_id = #{companyId}
</if>
<!-- 数据库层面的时间范围过滤 -->
AND (
<!-- 已结算协议:领料时间、退料时间、结算时间中至少有一个在查询范围内 -->
(bai.is_slt = 1 AND (
(sai.start_time IS NOT NULL AND sai.start_time BETWEEN #{startDate} AND #{endDate})
OR (sai.end_time IS NOT NULL AND sai.end_time BETWEEN #{startDate} AND #{endDate})
OR (sai.slt_time IS NOT NULL AND sai.slt_time BETWEEN #{startDate} AND #{endDate})
))
OR
<!-- 未结算协议:领料时间、退料时间中至少有一个在查询范围内 -->
(bai.is_slt = 0 AND (
(sai.start_time IS NOT NULL AND sai.start_time BETWEEN #{startDate} AND #{endDate})
OR (sai.end_time IS NOT NULL AND sai.end_time BETWEEN #{startDate} AND #{endDate})
))
)
</where>
ORDER BY bai.agreement_code, sai.type_id, sai.ma_id
</select>
</mapper>