Commit 63a3b3de37eb0889a951df506902af853f203941

Authored by wangqian
1 parent 58f525c9

增加自有机械折旧定时任务及派单超时处理定时任务

urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/mechanicaldepreciation/MechanicalDepreciationController.java
@@ -100,5 +100,9 @@ public class MechanicalDepreciationController { @@ -100,5 +100,9 @@ public class MechanicalDepreciationController {
100 ExcelUtils.write(response, "自有机械折旧子.xls", "数据", MechanicalDepreciationRespVO.class, 100 ExcelUtils.write(response, "自有机械折旧子.xls", "数据", MechanicalDepreciationRespVO.class,
101 BeanUtils.toBean(list, MechanicalDepreciationRespVO.class)); 101 BeanUtils.toBean(list, MechanicalDepreciationRespVO.class));
102 } 102 }
  103 + @GetMapping(value = "/batchSaveMechDepr")
  104 + public void batchSaveMechDepr(@RequestParam String depreciationMethod) {
  105 + mechanicalDepreciationService.batchSaveMechDepr(depreciationMethod);
  106 + }
103 107
104 -}  
105 \ No newline at end of file 108 \ No newline at end of file
  109 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/mechanicalcost/MechanicalCostDO.java
@@ -77,7 +77,7 @@ public class MechanicalCostDO extends BaseDO { @@ -77,7 +77,7 @@ public class MechanicalCostDO extends BaseDO {
77 /** 77 /**
78 * 使用年限 78 * 使用年限
79 */ 79 */
80 - private Long serviceLife; 80 + private Integer serviceLife;
81 /** 81 /**
82 * 结束使用时间 82 * 结束使用时间
83 */ 83 */
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/job/costfee/CostFeeMachDepr.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.job.costfee;
  2 +
  3 +import com.zteits.urbanops.framework.quartz.core.handler.JobHandler;
  4 +import com.zteits.urbanops.module.garden.service.mechanicaldepreciation.MechanicalDepreciationService;
  5 +import jakarta.annotation.Resource;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import java.time.LocalDate;
  10 +
  11 +/**
  12 + * @Classname CostFeeMachDepr
  13 + * @Description 人机材自有机械折旧处理
  14 + * @Date 2025/12/25 10:43
  15 + * @Created by wangqian
  16 + */
  17 +@Component
  18 +@Slf4j
  19 +public class CostFeeMachDepr implements JobHandler {
  20 + @Resource
  21 + private MechanicalDepreciationService mechanicalDepreciationService;
  22 + @Override
  23 + public String execute(String param) throws Exception {
  24 + log.info("自有机械折旧定时任务,开始========"+ LocalDate.now());
  25 + int count = mechanicalDepreciationService.batchSaveMechDepr(param);
  26 + log.info("自有机械折,mechanicalCostList.size=" + count);
  27 + log.info("自有机械折旧定时任务,结束========"+ LocalDate.now());
  28 + return "Successfully";
  29 + }
  30 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/job/task/AssignTaskJob.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.job.task;
  2 +
  3 +import com.zteits.urbanops.framework.quartz.core.handler.JobHandler;
  4 +import com.zteits.urbanops.module.garden.service.assigntasks.AssignTasksService;
  5 +import jakarta.annotation.Resource;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * @Classname AssignTaskJob
  11 + * @Description 派单超时处理
  12 + * @Date 2025/12/25 10:37
  13 + * @Created by wangqian
  14 + */
  15 +@Component
  16 +@Slf4j
  17 +public class AssignTaskJob implements JobHandler {
  18 +
  19 + @Resource
  20 + private AssignTasksService assignTasksService;
  21 +
  22 + @Override
  23 + public String execute(String param) throws Exception {
  24 + assignTasksService.tasksOutTimeAuto();
  25 + return "Successfully";
  26 + }
  27 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/assigntasks/AssignTasksServiceImpl.java
@@ -219,7 +219,7 @@ public class AssignTasksServiceImpl implements AssignTasksService { @@ -219,7 +219,7 @@ public class AssignTasksServiceImpl implements AssignTasksService {
219 List<AssignTasksInfoDO> taskInfoList = assiAssignTasksInfoMapper.selectList(queryWrapper); 219 List<AssignTasksInfoDO> taskInfoList = assiAssignTasksInfoMapper.selectList(queryWrapper);
220 if (CollectionUtils.isEmpty(taskInfoList)) { 220 if (CollectionUtils.isEmpty(taskInfoList)) {
221 log.info("未找到未完成的派单详情,无需处理"); 221 log.info("未找到未完成的派单详情,无需处理");
222 - return 0; 222 + continue;
223 } 223 }
224 224
225 // 4. 筛选“已超时”的派单详情(截止时间 < 当前时间) 225 // 4. 筛选“已超时”的派单详情(截止时间 < 当前时间)
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/mechanicaldepreciation/MechanicalDepreciationService.java
@@ -67,4 +67,6 @@ public interface MechanicalDepreciationService { @@ -67,4 +67,6 @@ public interface MechanicalDepreciationService {
67 */ 67 */
68 void deleteMechanicalDepreciationByMechId(Long mechId); 68 void deleteMechanicalDepreciationByMechId(Long mechId);
69 69
  70 + public int batchSaveMechDepr(String depreciationMethod);
  71 +
70 } 72 }
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/mechanicaldepreciation/MechanicalDepreciationServiceImpl.java
@@ -6,13 +6,27 @@ import com.zteits.urbanops.framework.common.pojo.PageResult; @@ -6,13 +6,27 @@ import com.zteits.urbanops.framework.common.pojo.PageResult;
6 import com.zteits.urbanops.framework.common.util.object.BeanUtils; 6 import com.zteits.urbanops.framework.common.util.object.BeanUtils;
7 import com.zteits.urbanops.module.garden.controller.admin.mechanicaldepreciation.vo.MechanicalDepreciationPageReqVO; 7 import com.zteits.urbanops.module.garden.controller.admin.mechanicaldepreciation.vo.MechanicalDepreciationPageReqVO;
8 import com.zteits.urbanops.module.garden.controller.admin.mechanicaldepreciation.vo.MechanicalDepreciationSaveReqVO; 8 import com.zteits.urbanops.module.garden.controller.admin.mechanicaldepreciation.vo.MechanicalDepreciationSaveReqVO;
  9 +import com.zteits.urbanops.module.garden.dal.dataobject.mechanicalcost.MechanicalCostDO;
9 import com.zteits.urbanops.module.garden.dal.dataobject.mechanicaldepreciation.MechanicalDepreciationDO; 10 import com.zteits.urbanops.module.garden.dal.dataobject.mechanicaldepreciation.MechanicalDepreciationDO;
  11 +import com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper;
10 import com.zteits.urbanops.module.garden.dal.mysql.mechanicaldepreciation.MechanicalDepreciationMapper; 12 import com.zteits.urbanops.module.garden.dal.mysql.mechanicaldepreciation.MechanicalDepreciationMapper;
11 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
  14 +import lombok.extern.slf4j.Slf4j;
12 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
  16 +import org.springframework.util.CollectionUtils;
13 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
14 18
  19 +import java.math.BigDecimal;
  20 +import java.time.LocalDate;
  21 +import java.time.LocalDateTime;
  22 +import java.time.YearMonth;
  23 +import java.time.ZoneId;
  24 +import java.time.format.DateTimeFormatter;
  25 +import java.util.ArrayList;
  26 +import java.util.Date;
15 import java.util.List; 27 import java.util.List;
  28 +import java.util.Set;
  29 +import java.util.stream.Collectors;
16 30
17 import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; 31 import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
18 import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.MECHANICAL_DEPRECIATION_NOT_EXISTS; 32 import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.MECHANICAL_DEPRECIATION_NOT_EXISTS;
@@ -25,10 +39,13 @@ import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.MECHANI @@ -25,10 +39,13 @@ import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.MECHANI
25 */ 39 */
26 @Service 40 @Service
27 @Validated 41 @Validated
  42 +@Slf4j
28 public class MechanicalDepreciationServiceImpl implements MechanicalDepreciationService { 43 public class MechanicalDepreciationServiceImpl implements MechanicalDepreciationService {
29 44
30 @Resource 45 @Resource
31 private MechanicalDepreciationMapper mechanicalDepreciationMapper; 46 private MechanicalDepreciationMapper mechanicalDepreciationMapper;
  47 + @Resource
  48 + private MechanicalCostMapper mechanicalCostMapper;
32 49
33 @Override 50 @Override
34 public Long createMechanicalDepreciation(MechanicalDepreciationSaveReqVO createReqVO) { 51 public Long createMechanicalDepreciation(MechanicalDepreciationSaveReqVO createReqVO) {
@@ -90,4 +107,172 @@ public class MechanicalDepreciationServiceImpl implements MechanicalDepreciation @@ -90,4 +107,172 @@ public class MechanicalDepreciationServiceImpl implements MechanicalDepreciation
90 mechanicalDepreciationMapper.delete(queryWrapper); 107 mechanicalDepreciationMapper.delete(queryWrapper);
91 } 108 }
92 109
  110 + @Override
  111 + public int batchSaveMechDepr(String depreciationMethod) {
  112 + log.info("自有机械折旧业务处理,start");
  113 + List<MechanicalCostDO> mechanicalCostList = mechanicalCostMapper.selectList(MechanicalCostDO::getStatus, 0);
  114 + if (!CollectionUtils.isEmpty(mechanicalCostList)) {
  115 + straightLineMethod(mechanicalCostList);
  116 + }
  117 + log.info("自有机械折旧业务处理,end");
  118 + return mechanicalCostList.size();
  119 + }
  120 + /**
  121 + * @Author wangqian
  122 + * @Description 直线法
  123 + * @Date 2025/8/3 10:47
  124 + * @Param mechanicalCostList
  125 + * @Return int
  126 + */
  127 + public void straightLineMethod(List<MechanicalCostDO> mechanicalCostList) {
  128 + if (CollectionUtils.isEmpty(mechanicalCostList)) {
  129 + return; // 空集合直接返回,避免无效处理
  130 + }
  131 +
  132 + // 1. 批量查询已有折旧记录
  133 + Set<Long> mechIdSet = mechanicalCostList.stream()
  134 + .map(MechanicalCostDO::getId)
  135 + .collect(Collectors.toSet());
  136 + LambdaQueryWrapper<MechanicalDepreciationDO> queryWrapper = new LambdaQueryWrapper<>();
  137 + queryWrapper.in(MechanicalDepreciationDO::getMechId, mechIdSet);
  138 + List<MechanicalDepreciationDO> existingDepreciations = mechanicalDepreciationMapper.selectList(queryWrapper);
  139 + Set<Long> existingMechIdSet = existingDepreciations.stream()
  140 + .map(MechanicalDepreciationDO::getMechId)
  141 + .collect(Collectors.toSet());
  142 +
  143 + // 2. 统一获取当前时间
  144 +
  145 + for (MechanicalCostDO cost : mechanicalCostList) {
  146 + Long mechId = cost.getId();
  147 + // 跳过已存在折旧记录的资产
  148 + if (existingMechIdSet.contains(mechId)) {
  149 + continue;
  150 + }
  151 + // 3. 单价
  152 + BigDecimal originalValue = cost.getUnitPrice();
  153 + Integer serviceLifeYears = cost.getServiceLife();
  154 + if (originalValue == null || originalValue.compareTo(BigDecimal.ZERO) <= 0
  155 + || serviceLifeYears == null || serviceLifeYears <= 0) {
  156 + log.warn("资产ID:{} 折旧参数异常,原值:{}, 使用年限:{}",
  157 + mechId, originalValue, serviceLifeYears);
  158 + continue;
  159 + }
  160 + List<MechanicalDepreciationDO> toSaveList = new ArrayList<>(); // 收集待保存对象,批量插入
  161 +
  162 + // 4. 计算固定参数
  163 + Integer totalMonths = serviceLifeYears * 12;
  164 + BigDecimal residualRate = BigDecimal.ZERO; // 默认残值率0
  165 + BigDecimal netValue = originalValue.multiply(BigDecimal.ONE.subtract(residualRate));
  166 +
  167 + // 优化尾差计算:基础月折旧额 + 最后一个月调整
  168 + // 先计算未四舍五入的精确月折旧额
  169 + BigDecimal exactMonthlyDepreciation = netValue.divide(new BigDecimal(totalMonths), 10, BigDecimal.ROUND_HALF_UP);
  170 + // 计算四舍五入到分的基础月折旧额
  171 + BigDecimal baseMonthlyDepreciation = exactMonthlyDepreciation.setScale(2, BigDecimal.ROUND_HALF_UP);
  172 + // 计算总折旧额(基础月折旧 × 总月数)
  173 + BigDecimal totalDepreciation = baseMonthlyDepreciation.multiply(new BigDecimal(totalMonths));
  174 + // 计算尾差(需要调整的金额)
  175 + BigDecimal tailDifference = netValue.subtract(totalDepreciation);
  176 +
  177 + // 5. 生成折旧期间列表(如YYYY-MM)
  178 + List<String> depreciationPeriods = generateMonths(cost.getStartUseTime(), serviceLifeYears);
  179 + if (depreciationPeriods.size() != totalMonths) {
  180 + log.warn("资产ID:{} 折旧期间数量不匹配,计算月数:{}, 实际生成:{}",
  181 + mechId, totalMonths, depreciationPeriods.size());
  182 + continue; // 期间数量异常时跳过,避免数据错误
  183 + }
  184 +
  185 + // 6. 循环生成每月折旧记录
  186 + BigDecimal accumulatedDepreciation = BigDecimal.ZERO; // 累计折旧初始化为0
  187 + for (int i = 0; i < depreciationPeriods.size(); i++) {
  188 + MechanicalDepreciationDO depreciation = new MechanicalDepreciationDO();
  189 +
  190 + // 固定属性(一次设置)
  191 + depreciation.setMechId(mechId);
  192 + depreciation.setOriginalValue(originalValue);
  193 + depreciation.setDepreciationMethod("直线法");
  194 + depreciation.setResidualRate(residualRate);
  195 + depreciation.setServiceLife(serviceLifeYears);
  196 + depreciation.setNetValue(netValue);
  197 +
  198 + // 月度变化属性 - 处理尾差
  199 + BigDecimal monthlyDepreciation;
  200 + // 如果是最后一个月,加上尾差调整
  201 + if (i == depreciationPeriods.size() - 1) {
  202 + monthlyDepreciation = baseMonthlyDepreciation.add(tailDifference);
  203 + // 确保最后调整后没有因精度问题产生的微小偏差
  204 + monthlyDepreciation = monthlyDepreciation.setScale(2, BigDecimal.ROUND_HALF_UP);
  205 + } else {
  206 + monthlyDepreciation = baseMonthlyDepreciation;
  207 + }
  208 +
  209 + accumulatedDepreciation = accumulatedDepreciation.add(monthlyDepreciation);
  210 + depreciation.setDepreciationAmount(monthlyDepreciation);
  211 + depreciation.setAccumulatedDepreciation(accumulatedDepreciation);
  212 + depreciation.setRemainingPeriods(totalMonths - (i + 1)); // 剩余期数=总月数-已计提月数
  213 + depreciation.setDepreciationPeriod(depreciationPeriods.get(i));
  214 +
  215 + toSaveList.add(depreciation);
  216 + }
  217 +
  218 + // 额外校验:确保累计折旧等于净残值,避免计算错误
  219 + if (accumulatedDepreciation.compareTo(netValue) != 0) {
  220 + log.error("资产ID:{} 折旧总和校验失败,应有:{}, 实际计算:{}",
  221 + mechId, netValue, accumulatedDepreciation);
  222 + continue;
  223 + }
  224 +
  225 + // 7. 批量插入,减少数据库交互
  226 + if (!toSaveList.isEmpty()) {
  227 + mechanicalDepreciationMapper.insertBatch(toSaveList); // 假设新增批量插入方法
  228 + }
  229 + }
  230 + }
  231 +
  232 +
  233 +
  234 + /**
  235 + * 生成从开始日期起指定年限内的所有月份(格式:yyyy-MM)
  236 + *
  237 + * @param localStartDate 开始日期(不能为null)
  238 + * @param years 年限(必须大于等于0)
  239 + * @return 月份字符串集合,若输入参数无效则返回空集合
  240 + */
  241 + public static List<String> generateMonths(LocalDate localStartDate, Integer years) {
  242 + // 初始化返回集合
  243 + List<String> months = new ArrayList<>();
  244 +
  245 + // 参数校验
  246 + if (localStartDate == null || years == null || years < 0) {
  247 + return months;
  248 + }
  249 +
  250 + try {
  251 + // 转换为年月对象,从开始日期的当月开始
  252 + YearMonth current = YearMonth.from(localStartDate);
  253 +
  254 + // 计算结束年月(开始日期加上指定年数)
  255 + YearMonth end = current.plusYears(years).minusMonths(1);
  256 +
  257 + // 定义日期格式化器(线程安全)
  258 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
  259 +
  260 + // 循环添加每个月
  261 + while (!current.isAfter(end)) {
  262 + months.add(current.format(formatter));
  263 + current = current.plusMonths(1);
  264 + }
  265 + } catch (Exception e) {
  266 + // 捕获所有异常,避免方法抛出异常影响调用方
  267 + // 实际应用中可根据需要记录日志
  268 + e.printStackTrace();
  269 + }
  270 +
  271 + return months;
  272 + }
  273 +
  274 + public static void main(String[] args) {
  275 + LocalDate localDate = LocalDate.of(2024, 5, 6);
  276 + System.out.println(generateMonths(localDate, 1));
  277 + }
93 } 278 }