Commit fc922b6975bf37085b2b66ca35616a9294447dc0

Authored by 王富生
1 parent 2cf45c5d

优化计算时间间隔天数

urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/inspectionplan/InspectionPlanServiceImpl.java
... ... @@ -13,7 +13,8 @@ import com.zteits.urbanops.module.garden.dal.mysql.inspectionplan.InspectionPlan
13 13 import com.zteits.urbanops.module.garden.dal.mysql.inspectionplan.InspectionPlanRoleMapper;
14 14 import com.zteits.urbanops.module.garden.enums.FinishStateEnum;
15 15 import com.zteits.urbanops.module.garden.enums.PlanConstants;
16   -import com.zteits.urbanops.module.garden.util.TimeIntervalCalculatorUtil;
  16 +import com.zteits.urbanops.module.garden.util.timeinterval.PeriodResult;
  17 +import com.zteits.urbanops.module.garden.util.timeinterval.TimeIntervalFrequencyUtil;
17 18 import com.zteits.urbanops.module.system.api.dept.DeptApi;
18 19 import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO;
19 20 import com.zteits.urbanops.module.system.api.permission.RoleApi;
... ... @@ -26,7 +27,6 @@ import org.springframework.transaction.annotation.Transactional;
26 27 import org.springframework.util.CollectionUtils;
27 28 import org.springframework.validation.annotation.Validated;
28 29  
29   -import java.time.LocalDate;
30 30 import java.time.LocalDateTime;
31 31 import java.time.format.DateTimeFormatter;
32 32 import java.util.*;
... ... @@ -78,20 +78,20 @@ public class InspectionPlanServiceImpl implements InspectionPlanService {
78 78 LocalDateTime now = LocalDateTime.now();
79 79 String userId = SecurityFrameworkUtils.getLoginUserId() + "";
80 80 String userName = SecurityFrameworkUtils.getLoginUserNickname();
81   - int rateValue;
  81 + String rateValue;
82 82 if (createReqVO.getRateValue().contains("/")) {
83   - rateValue = Integer.parseInt(createReqVO.getRateValue().split("/")[1]);
  83 + rateValue = createReqVO.getRateValue();
84 84 } else {
85   - rateValue = Integer.parseInt(createReqVO.getRateValue());
  85 + rateValue = createReqVO.getRateValue()+"/1";
86 86 }
87 87 //计算开始和计算时间 在间隔周期下每天的次数
88   - List<String> betweenDayList = TimeIntervalCalculatorUtil.calculateIntervals(createReqVO.getBeginTime(), createReqVO.getEndTime(), createReqVO.getCycleId(), rateValue);
89   - if (CollectionUtils.isEmpty(betweenDayList)) {
  88 + List<PeriodResult> calculate = TimeIntervalFrequencyUtil.calculate(createReqVO.getBeginTime().toString(), createReqVO.getEndTime().toString(), rateValue, createReqVO.getCycleId());
  89 + if (CollectionUtils.isEmpty(calculate)) {
90 90 log.warn("获取两个时间之间间隔天数错误");
91 91 return CommonResult.error(INSPECTION_CYCLE_ERROR);
92 92 }
93 93 //计算计划次数
94   - Integer planNum = betweenDayList.size();
  94 + Integer planNum = calculate.size();
95 95  
96 96  
97 97 //巡检计划list
... ... @@ -139,13 +139,12 @@ public class InspectionPlanServiceImpl implements InspectionPlanService {
139 139 });
140 140 /*3.插入巡查计划明细*/
141 141  
142   - for (int i = 0; i < betweenDayList.size(); i++) {
143   - String[] dateStr = betweenDayList.get(i).split("/");
  142 + for (PeriodResult periodResult : calculate) {
144 143 InspectionPlanDetailDO entity = getInspectionPlanDetailDO(batchNo, createReqVO, roadReqVO, now);
145 144 ThreadUtil.sleep(2);
146   - entity.setSortNum(i + 1);
147   - entity.setBeginTime(LocalDateTime.parse(dateStr[0] + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
148   - entity.setEndTime(LocalDateTime.parse(dateStr[1] + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  145 + entity.setSortNum(periodResult.getCount());
  146 + entity.setBeginTime(LocalDateTime.parse(periodResult.getStart() + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  147 + entity.setEndTime(LocalDateTime.parse(periodResult.getEnd() + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
149 148 detailDOList.add(entity);
150 149 }
151 150 });
... ...
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/maintainplan/MaintainPlanServiceImpl.java
... ... @@ -5,9 +5,9 @@ import cn.hutool.core.thread.ThreadUtil;
5 5 import cn.hutool.core.util.RandomUtil;
6 6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
7 7 import com.zteits.urbanops.framework.common.pojo.CommonResult;
8   -import com.zteits.urbanops.framework.common.util.collection.CollectionUtils;
9 8 import com.zteits.urbanops.framework.dict.core.DictFrameworkUtils;
10 9 import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils;
  10 +import com.zteits.urbanops.module.garden.dal.dataobject.inspectionplan.InspectionPlanDetailDO;
11 11 import com.zteits.urbanops.module.garden.dal.dataobject.maintainplan.MaintainPlanDetailDO;
12 12 import com.zteits.urbanops.module.garden.dal.dataobject.maintainplan.MaintainPlanRoleDO;
13 13 import com.zteits.urbanops.module.garden.dal.mysql.maintainplan.MaintainPlanDetailMapper;
... ... @@ -15,7 +15,8 @@ import com.zteits.urbanops.module.garden.dal.mysql.maintainplan.MaintainPlanInte
15 15 import com.zteits.urbanops.module.garden.dal.mysql.maintainplan.MaintainPlanRoleMapper;
16 16 import com.zteits.urbanops.module.garden.enums.FinishStateEnum;
17 17 import com.zteits.urbanops.module.garden.enums.PlanConstants;
18   -import com.zteits.urbanops.module.garden.util.TimeIntervalCalculatorUtil;
  18 +import com.zteits.urbanops.module.garden.util.timeinterval.PeriodResult;
  19 +import com.zteits.urbanops.module.garden.util.timeinterval.TimeIntervalFrequencyUtil;
19 20 import com.zteits.urbanops.module.system.api.dept.DeptApi;
20 21 import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO;
21 22 import com.zteits.urbanops.module.system.api.permission.RoleApi;
... ... @@ -24,13 +25,12 @@ import jodd.util.StringUtil;
24 25 import lombok.extern.slf4j.Slf4j;
25 26 import org.springframework.stereotype.Service;
26 27 import jakarta.annotation.Resource;
  28 +import org.springframework.util.CollectionUtils;
27 29 import org.springframework.validation.annotation.Validated;
28 30  
29   -import java.time.LocalDate;
30 31 import java.time.LocalDateTime;
31 32 import java.time.format.DateTimeFormatter;
32 33 import java.util.*;
33   -import java.util.concurrent.atomic.AtomicInteger;
34 34 import java.util.stream.Collectors;
35 35  
36 36 import com.zteits.urbanops.module.garden.controller.admin.maintainplan.vo.*;
... ... @@ -81,20 +81,21 @@ public class MaintainPlanServiceImpl implements MaintainPlanService {
81 81 LocalDateTime now = LocalDateTime.now();
82 82 String userId = SecurityFrameworkUtils.getLoginUserId()+"";
83 83 String userName = SecurityFrameworkUtils.getLoginUserNickname();
84   - int rateValue;
  84 + String rateValue;
85 85 if (createReqVO.getRateValue().contains("/")) {
86   - rateValue = Integer.parseInt(createReqVO.getRateValue().split("/")[1]);
  86 + rateValue = createReqVO.getRateValue();
87 87 } else {
88   - rateValue = Integer.parseInt(createReqVO.getRateValue());
  88 + rateValue = createReqVO.getRateValue()+"/1";
89 89 }
90 90 //计算开始和计算时间 在间隔周期下每天的次数
91   - List<String> betweenDayList = TimeIntervalCalculatorUtil.calculateIntervals(createReqVO.getBeginTime(), createReqVO.getEndTime(), createReqVO.getCycleId(), rateValue);
92   - if (CollectionUtils.isAnyEmpty(betweenDayList)) {
  91 + //计算开始和计算时间 在间隔周期下每天的次数
  92 + List<PeriodResult> calculate = TimeIntervalFrequencyUtil.calculate(createReqVO.getBeginTime().toString(), createReqVO.getEndTime().toString(), rateValue, createReqVO.getCycleId());
  93 + if (CollectionUtils.isEmpty(calculate)) {
93 94 log.warn("获取两个时间之间间隔天数错误");
94 95 return CommonResult.error(INSPECTION_CYCLE_ERROR);
95 96 }
96 97 //计算计划次数
97   - Integer planNum = betweenDayList.size();
  98 + Integer planNum = calculate.size();
98 99  
99 100  
100 101 //巡检计划list
... ... @@ -141,14 +142,12 @@ public class MaintainPlanServiceImpl implements MaintainPlanService {
141 142 roleList.add(maintainPlanRoleDO);
142 143 });
143 144 /*3.插入巡查计划明细*/
144   -
145   - for (int i = 0; i < betweenDayList.size(); i++) {
146   - String[] dateStr = betweenDayList.get(i).split("/");
  145 + for (PeriodResult periodResult : calculate) {
147 146 MaintainPlanDetailDO entity = getMaintainPlanDetailDO(batchNo, createReqVO, roadReqVO, now);
148 147 ThreadUtil.sleep(2);
149   - entity.setSortNum(i+1);
150   - entity.setBeginTime(LocalDateTime.parse(dateStr[0] + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
151   - entity.setEndTime(LocalDateTime.parse(dateStr[1] + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  148 + entity.setSortNum(periodResult.getCount());
  149 + entity.setBeginTime(LocalDateTime.parse(periodResult.getStart() + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  150 + entity.setEndTime(LocalDateTime.parse(periodResult.getEnd() + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
152 151 detailDOList.add(entity);
153 152 }
154 153 });
... ...
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/util/TimeIntervalCalculatorUtil.java deleted
1   -package com.zteits.urbanops.module.garden.util;
2   -
3   -
4   -import java.time.LocalDate;
5   -import java.util.ArrayList;
6   -import java.util.List;
7   -
8   -import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
9   -import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.INSPECTION_CYCLE_ID_ERROR;
10   -
11   -public class TimeIntervalCalculatorUtil {
12   - enum IntervalUnit {
13   - DAY, WEEK, MONTH, YEAR
14   - }
15   -
16   - public static void main(String[] args) {
17   -
18   - System.out.println("请输入开始日期(yyyy-MM-dd):");
19   - LocalDate startDate = LocalDate.of(2025,10,1);
20   -
21   - System.out.println("请输入结束日期(yyyy-MM-dd):");
22   - LocalDate endDate = LocalDate.of(2025,10,7);
23   -
24   - System.out.println("请选择间隔单位(1.天 2.周 3.月 4.年):");
25   - IntervalUnit unit = IntervalUnit.DAY;
26   -
27   - System.out.println("请输入间隔数量(如2表示每2天/周/月/年):");
28   - int intervalCount =2;
29   -
30   - List<String> intervals = calculateIntervals(startDate, endDate, 1, intervalCount);
31   -
32   - System.out.println("\n执行周期:");
33   - intervals.forEach(System.out::println);
34   - }
35   -
36   - /**
37   - * 执行次数计算
38   - * @param start
39   - * @param end
40   - * @param cycleId 1:日/次;2:周/次;3:月/次;4:年/次
41   - * @param intervalCount
42   - * @return
43   - */
44   - public static List<String> calculateIntervals(LocalDate start, LocalDate end, Integer cycleId, int intervalCount) {
45   - List<String> intervals = new ArrayList<>();
46   - LocalDate currentStart = start;
47   - IntervalUnit unit = getIntervalUnit(cycleId);
48   -
49   - while (currentStart.isBefore(end) || currentStart.isEqual(end)) {
50   - LocalDate currentEnd = calculateEndDate(currentStart, end, unit, intervalCount);
51   - intervals.add(currentStart + "/" + currentEnd);
52   - currentStart = currentEnd.plusDays(1);
53   - }
54   -
55   - return intervals;
56   - }
57   -
58   - private static LocalDate calculateEndDate(LocalDate currentStart, LocalDate endDate,
59   - IntervalUnit unit, int intervalCount) {
60   - switch (unit) {
61   - case DAY:
62   - LocalDate dayEnd = currentStart.plusDays(intervalCount - 1);
63   - return dayEnd.isAfter(endDate) ? endDate : dayEnd;
64   - case WEEK:
65   - LocalDate weekEnd = currentStart.plusDays(7 * intervalCount - 1);
66   - return weekEnd.isAfter(endDate) ? endDate : weekEnd;
67   - case MONTH:
68   - LocalDate monthEnd = currentStart.plusMonths(intervalCount)
69   - .withDayOfMonth(1).minusDays(1);
70   - return monthEnd.isAfter(endDate) ? endDate : monthEnd;
71   - case YEAR:
72   - LocalDate yearEnd = currentStart.plusYears(intervalCount)
73   - .withDayOfYear(1).minusDays(1);
74   - return yearEnd.isAfter(endDate) ? endDate : yearEnd;
75   - default:
76   - return endDate;
77   - }
78   - }
79   -
80   - /**
81   - * 转换枚举
82   - * @param cycleId 1:日/次;2:周/次;3:月/次;4:年/次
83   - * @return
84   - */
85   - public static IntervalUnit getIntervalUnit(Integer cycleId) {
86   - switch (cycleId) {
87   - case 1:
88   - return IntervalUnit.DAY;
89   - case 2:
90   - return IntervalUnit.WEEK;
91   - case 3:
92   - return IntervalUnit.MONTH;
93   - case 4:
94   - return IntervalUnit.YEAR;
95   - default:
96   - throw exception(INSPECTION_CYCLE_ID_ERROR);
97   - }
98   - }
99   -
100   -}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/util/timeinterval/PeriodResult.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.util.timeinterval;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.time.LocalDate;
  6 +import java.time.format.DateTimeFormatter;
  7 +
  8 +@Data
  9 +public class PeriodResult {
  10 + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  11 +
  12 + /**
  13 + * 开始时间
  14 + */
  15 + private final String start;
  16 + /**
  17 + * 结束时间
  18 + */
  19 + private final String end;
  20 + /**
  21 + * 执行次数
  22 + */
  23 + private final int count;
  24 +
  25 + public PeriodResult(LocalDate start, LocalDate end, int count) {
  26 + this.start = start.format(FORMATTER);
  27 + this.end = end.format(FORMATTER);
  28 + this.count = count;
  29 + }
  30 +
  31 + @Override
  32 + public String toString() {
  33 + return start + " " + end + " " + count + "次";
  34 + }
  35 +}
... ...
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/util/timeinterval/TimeIntervalFrequencyUtil.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.util.timeinterval;
  2 +
  3 +import java.time.LocalDate;
  4 +import java.time.format.DateTimeFormatter;
  5 +import java.util.ArrayList;
  6 +import java.util.Collections;
  7 +import java.util.List;
  8 +
  9 +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
  10 +import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.INSPECTION_CYCLE_ID_ERROR;
  11 +
  12 +/**
  13 + * 时间区间拆分+频次计算工具类
  14 + * 支持:按日/周/月/年拆分,区间临界点重叠,动态计算每个区间的执行频次
  15 + */
  16 +public class TimeIntervalFrequencyUtil {
  17 +
  18 + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  19 +
  20 + public enum IntervalUnit {
  21 + DAY, WEEK, MONTH, YEAR;
  22 + }
  23 +
  24 + /**
  25 + * 转换枚举
  26 + * @param cycleId 1:日/次;2:周/次;3:月/次;4:年/次
  27 + * @return
  28 + */
  29 + public static IntervalUnit getIntervalUnit(Integer cycleId) {
  30 + switch (cycleId) {
  31 + case 1:
  32 + return IntervalUnit.DAY;
  33 + case 2:
  34 + return IntervalUnit.WEEK;
  35 + case 3:
  36 + return IntervalUnit.MONTH;
  37 + case 4:
  38 + return IntervalUnit.YEAR;
  39 + default:
  40 + throw exception(INSPECTION_CYCLE_ID_ERROR);
  41 + }
  42 + }
  43 +
  44 + /**
  45 + * 计算时间段划分结果
  46 + *
  47 + * @param startStr 开始日期 (yyyy-MM-dd)
  48 + * @param endStr 结束日期 (yyyy-MM-dd)
  49 + * @param freq 频次值,格式 "x/y"
  50 + * @param cycleId cycleId 1:日/次;2:周/次;3:月/次;4:年/次
  51 + * @return 划分结果列表
  52 + */
  53 + public static List<PeriodResult> calculate(String startStr, String endStr, String freq, Integer cycleId) {
  54 + LocalDate start = LocalDate.parse(startStr, FORMATTER);
  55 + LocalDate end = LocalDate.parse(endStr, FORMATTER);
  56 +
  57 + if (start.isAfter(end)) {
  58 + return Collections.emptyList();
  59 + }
  60 +
  61 + String[] parts = freq.split("/", 2);
  62 + if (parts.length != 2) {
  63 + throw new IllegalArgumentException("频次值格式错误,应为 'x/y': " + freq);
  64 + }
  65 + int x = Integer.parseInt(parts[0]);
  66 + int y = Integer.parseInt(parts[1]);
  67 +
  68 + IntervalUnit unit = getIntervalUnit(cycleId);
  69 +
  70 + List<PeriodResult> results = new ArrayList<>();
  71 + LocalDate currentStart = start;
  72 +
  73 + while (!currentStart.isAfter(end)) {
  74 + // 计算当前周期的理论结束日期(包含 y 个单位)
  75 + LocalDate periodEnd = calculatePeriodEnd(currentStart, y, unit);
  76 +
  77 + // 实际段结束 = min(periodEnd, end)
  78 + LocalDate segEnd = periodEnd.isAfter(end) ? end : periodEnd;
  79 +
  80 + results.add(new PeriodResult(currentStart, segEnd, x));
  81 +
  82 + // 下一段从当前段结束的第二天开始
  83 + LocalDate nextStart = segEnd.plusDays(1);
  84 +
  85 + // 如果已经超过总结束时间,则终止
  86 + if (nextStart.isAfter(end)) {
  87 + break;
  88 + }
  89 +
  90 + currentStart = nextStart;
  91 + }
  92 +
  93 + return results;
  94 + }
  95 +
  96 + /**
  97 + * 计算从 start 开始,经过 y 个单位周期后的结束日期(包含)
  98 + */
  99 + private static LocalDate calculatePeriodEnd(LocalDate start, int y, IntervalUnit unit) {
  100 + switch (unit) {
  101 + case DAY:
  102 + return start.plusDays(y - 1); // 包含 y 天:start 到 start+y-1
  103 + case WEEK:
  104 + return start.plusDays((long) y * 7 - 1); // y周 = y*7天,减1得包含结束
  105 + case MONTH:
  106 + return addMonthsSafely(start, y).minusDays(1);
  107 + case YEAR:
  108 + return start.plusYears(y).minusDays(1);
  109 + default:
  110 + throw new UnsupportedOperationException("未支持的单位: " + unit);
  111 + }
  112 + }
  113 +
  114 +
  115 +
  116 + /**
  117 + * 安全地加上 y 个月(避免3月31日+1月变成异常)
  118 + * 这里使用 Java 8+ 的 plusMonths 自动调整规则
  119 + */
  120 + private static LocalDate addMonthsSafely(LocalDate date, int months) {
  121 + return date.plusMonths(months);
  122 + // Java 会自动处理:如 1月31日 +1月 → 2月28/29日(不会抛异常)
  123 + }
  124 +
  125 +
  126 +
  127 +
  128 + // ====================== 测试用例 ======================
  129 + //cycleId 1:日/次;2:周/次;3:月/次;4:年/次
  130 + public static void main(String[] args) {
  131 + System.out.println("=== 示例1:每3天一次,按日 ===");
  132 + List<PeriodResult> r1 = calculate("2025-01-01", "2025-01-07", "1/3", 1);
  133 + r1.forEach(System.out::println);
  134 +
  135 + System.out.println("\n=== 示例2:每3周一次(21天),按周 ===");
  136 + List<PeriodResult> r2 = calculate("2025-01-01", "2025-02-01", "1/3", 2);
  137 + r2.forEach(System.out::println);
  138 +
  139 + System.out.println("\n=== 示例3:每2个月一次,按月 ===");
  140 + List<PeriodResult> r3 = calculate("2025-01-01", "2025-12-31", "1/2", 3);
  141 + r3.forEach(System.out::println);
  142 +
  143 + System.out.println("\n=== 示例4:每3年一次,按年 ===");
  144 + List<PeriodResult> r4 = calculate("2025-01-01", "2035-06-01", "1/3", 3);
  145 + r4.forEach(System.out::println);
  146 +
  147 + System.out.println("\n=== 示例5:每2天2次,按日 ===");
  148 + List<PeriodResult> r5 = calculate("2025-01-01", "2025-01-05", "2/2", 1);
  149 + r5.forEach(System.out::println);
  150 +
  151 + System.out.println("\n=== 示例5:每2年1次,按年 ===");
  152 + List<PeriodResult> r6 = calculate("2025-01-01", "2028-01-05", "1/2", 4);
  153 + r6.forEach(System.out::println);
  154 + }
  155 +}
0 156 \ No newline at end of file
... ...