自定义时间规则

1.需求

用户可以自行选择时间规则,比如每天的几点-几点、每周的周几-周几、每月的几号-几号、每年的几月-几月……等等

  • 每天的 09:00:00 - 12:00:00 定时做什么事情、范围类可以做什么事情
  • 每周的 周一 - 周一 09:00:00 - 12:00:00 定时做什么事情、时间范围内可以做什么事情
  • 每月的 1号 2号 5号 09:00:00 - 12:00:00 定时做什么事情、时间范围内可以做什么事情
  • 每年的 1月 5月 7月 09:00:00 - 12:00:00 定时做什么事情、时间范围内可以做什么事情

2.代码设计

1.实体对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Data
@Entity
public class TimeRule extends BaseEntity {
private String ruleName; // 规则名称
private String ruleType; // 规则类型:DAILY/WEEKLY/MONTHLY/YEARLY
private LocalTime startTime; // 开始时间
private LocalTime endTime; // 结束时间
@ElementCollection
@CollectionTable(name = "rule_days_of_week")
private Set<Integer> daysOfWeek; // 星期几(可多选:1-7)
@ElementCollection
@CollectionTable(name = "rule_days_of_month")
private Set<Integer> daysOfMonth; // 每月几号(可多选:1-31)
@ElementCollection
@CollectionTable(name = "rule_months")
private Set<Integer> months; // 月份(可多选:1-12)
private Boolean isActive; // 规则是否激活
private Integer priority; // 规则优先级
@Column(length = 1000)
private String description; // 规则描述
}

2.验证工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.List;

public class TimeRuleUtils {
/**
* 检测当前时间是否在时间规则内(使用默认时区)
* @param activeRules
* @return
*/
public static boolean isWithinActiveRulesByNowTime(List<TimeRule> activeRules) {
LocalDateTime now = LocalDateTime.now();
for (TimeRule rule : activeRules) {
if (matchesRule(rule, now)) {
return true;
}
}
return false;
}
/**
* 检测当前时间是否在时间规则内(使用指定时区)
* @param activeRules
* @return
*/
public static boolean isWithinActiveRulesByNowTime(List<TimeRule> activeRules, String timeZoneId) {
ZoneId zoneId = ZoneId.of(timeZoneId);
LocalDateTime localDateTime = LocalDateTime.now(zoneId);
for (TimeRule rule : activeRules) {
if (matchesRule(rule, localDateTime)) {
return true;
}
}
return false;
}
/**
* 检测指定时间是否在时间规则内
* @param activeRules
* @param dateTime
* @return
*/
public static boolean isWithinActiveRulesByTime(List<TimeRule> activeRules, LocalDateTime dateTime) {
for (TimeRule rule : activeRules) {
if (matchesRule(rule, dateTime)) {
return true;
}
}
return false;
}
private static boolean matchesRule(TimeRule rule, LocalDateTime dateTime) {
// 检查时间范围
LocalTime time = dateTime.toLocalTime();
if (time.isBefore(rule.getStartTime()) || time.isAfter(rule.getEndTime())) {
return false;
}
return switch (rule.getRuleType()) {
case "DAILY" -> true;
case "WEEKLY" ->
// 检查是否在选择的星期几中
rule.getDaysOfWeek() != null &&
rule.getDaysOfWeek().contains(dateTime.getDayOfWeek().getValue());
case "MONTHLY" ->
// 检查是否在选择的日期中
rule.getDaysOfMonth() != null &&
rule.getDaysOfMonth().contains(dateTime.getDayOfMonth());
case "YEARLY" ->
// 检查是否在选择的月份和日期中
rule.getMonths() != null &&
rule.getDaysOfMonth() != null &&
rule.getMonths().contains(dateTime.getMonthValue()) &&
rule.getDaysOfMonth().contains(dateTime.getDayOfMonth());
default -> false;
};
}
}

3.实体规则验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class TimeRuleValidator {
public List<String> validateRule(TimeRule rule) {
List<String> errors = new ArrayList<>();
if (rule.getStartTime() == null || rule.getEndTime() == null) {
errors.add("开始时间和结束时间不能为空");
}
if (rule.getStartTime() != null && rule.getEndTime() != null
&& rule.getStartTime().isAfter(rule.getEndTime())) {
errors.add("开始时间不能晚于结束时间");
}
switch (rule.getRuleType()) {
case "WEEKLY":
if (rule.getDaysOfWeek() == null || rule.getDaysOfWeek().isEmpty()) {
errors.add("周规则必须选择星期几");
}
rule.getDaysOfWeek().forEach(day -> {
if (day < 1 || day > 7) {
errors.add("星期几必须在1-7之间");
}
});
break;
case "MONTHLY":
if (rule.getDaysOfMonth() == null || rule.getDaysOfMonth().isEmpty()) {
errors.add("月规则必须选择日期");
}
rule.getDaysOfMonth().forEach(day -> {
if (day < 1 || day > 31) {
errors.add("日期必须在1-31之间");
}
});
break;
case "YEARLY":
if (rule.getMonths() == null || rule.getMonths().isEmpty()) {
errors.add("年规则必须选择月份");
}
if (rule.getDaysOfMonth() == null || rule.getDaysOfMonth().isEmpty()) {
errors.add("年规则必须选择日期");
}
break;
}
return errors;
}
}

4.前端传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 每天的规则
{
"ruleName": "每天营业",
"ruleType": "DAILY",
"startTime": "09:00",
"endTime": "18:00",
"isActive": true,
"priority": 1,
"description": "每天营业"
}
// 每周二和周四的规则
{
"ruleName": "周二周四营业",
"ruleType": "WEEKLY",
"startTime": "09:00",
"endTime": "18:00",
"daysOfWeek": [2, 4],
"isActive": true,
"priority": 1,
"description": "每周二和周四营业"
}
// 每月1号和15号的规则
{
"ruleName": "月初月中营业",
"ruleType": "MONTHLY",
"startTime": "09:00",
"endTime": "18:00",
"daysOfMonth": [1, 15],
"isActive": true,
"priority": 2,
"description": "每月1号和15号营业"
}
// 特定月份的特定日期规则
{
"ruleName": "节假日营业",
"ruleType": "YEARLY",
"startTime": "10:00",
"endTime": "22:00",
"months": [1, 5, 10],
"daysOfMonth": [1, 2, 3],
"isActive": true,
"priority": 3,
"description": "元旦、五一、国庆期间营业"
}

5.代码处理实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 示例1:每周二和周四的上午9点到下午5点
TimeRule weeklyRule = new TimeRule();
weeklyRule.setRuleName("周二周四工作时间");
weeklyRule.setRuleType("WEEKLY");
weeklyRule.setStartTime(LocalTime.of(9, 0)); // 09:00
weeklyRule.setEndTime(LocalTime.of(17, 0)); // 17:00
weeklyRule.setDaysOfWeek(Set.of(2, 4)); // 周二和周四
weeklyRule.setIsActive(true);
weeklyRule.setPriority(1);
// 示例2:每月1号和15号的全天
TimeRule monthlyRule = new TimeRule();
monthlyRule.setRuleName("月初月中值班");
monthlyRule.setRuleType("MONTHLY");
monthlyRule.setStartTime(LocalTime.of(0, 0)); // 00:00
monthlyRule.setEndTime(LocalTime.of(23, 59)); // 23:59
monthlyRule.setDaysOfMonth(Set.of(1, 15)); // 1号和15号
monthlyRule.setIsActive(true);
monthlyRule.setPriority(2);
// 示例3:节假日期间(如:五一假期)
TimeRule holidayRule = new TimeRule();
holidayRule.setRuleName("五一假期");
holidayRule.setRuleType("YEARLY");
holidayRule.setStartTime(LocalTime.of(9, 0)); // 09:00
holidayRule.setEndTime(LocalTime.of(22, 0)); // 22:00
holidayRule.setMonths(Set.of(5)); // 5月
holidayRule.setDaysOfMonth(Set.of(1, 2, 3)); // 1-3号
holidayRule.setIsActive(true);
holidayRule.setPriority(3);