zhuyifan

feat(task): 实现自动创建餐单的周任务

...@@ -6,6 +6,7 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper; ...@@ -6,6 +6,7 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper;
6 import com.infoloop.tianting.server.StorageService; 6 import com.infoloop.tianting.server.StorageService;
7 import com.infoloop.tianting.server.StorageServiceFactory; 7 import com.infoloop.tianting.server.StorageServiceFactory;
8 import com.infoloop.tianting.store.LoginCodeStore; 8 import com.infoloop.tianting.store.LoginCodeStore;
9 +import com.infoloop.tianting.store.WeeklyTaskStore;
9 import com.infoloop.tianting.utils.SmsUtil; 10 import com.infoloop.tianting.utils.SmsUtil;
10 import io.lettuce.core.api.StatefulRedisConnection; 11 import io.lettuce.core.api.StatefulRedisConnection;
11 import org.springframework.beans.factory.annotation.Autowired; 12 import org.springframework.beans.factory.annotation.Autowired;
...@@ -16,6 +17,8 @@ import org.springframework.context.annotation.Configuration; ...@@ -16,6 +17,8 @@ import org.springframework.context.annotation.Configuration;
16 import org.springframework.context.annotation.Import; 17 import org.springframework.context.annotation.Import;
17 import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; 18 import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
18 19
20 +import java.time.Duration;
21 +
19 import static com.infoloop.tianting.constant.ConfigConstants.REDIS_CONNECTION; 22 import static com.infoloop.tianting.constant.ConfigConstants.REDIS_CONNECTION;
20 23
21 @Configuration 24 @Configuration
...@@ -29,6 +32,13 @@ public class AppConfig { ...@@ -29,6 +32,13 @@ public class AppConfig {
29 return new LoginCodeStore(commands, 300); 32 return new LoginCodeStore(commands, 300);
30 } 33 }
31 34
35 + @Bean
36 + public WeeklyTaskStore weeklyTaskStore(@Autowired @Qualifier(REDIS_CONNECTION) final StatefulRedisConnection<String, String> connection) {
37 + final var commands = connection.sync();
38 + final var expireSeconds = Duration.ofDays(7).getSeconds();
39 + return new WeeklyTaskStore(commands, expireSeconds);
40 + }
41 +
32 // init bean 42 // init bean
33 @Bean 43 @Bean
34 public SmsUtil smsUtil(@Autowired SmsConfig smsConfig) { 44 public SmsUtil smsUtil(@Autowired SmsConfig smsConfig) {
......
...@@ -11,7 +11,9 @@ import com.infoloop.tianting.mealorderservice.SingleGradeClassRpcResponse; ...@@ -11,7 +11,9 @@ import com.infoloop.tianting.mealorderservice.SingleGradeClassRpcResponse;
11 import com.infoloop.tianting.mealorderservice.SingleMealMenuRecordRpcResponse; 11 import com.infoloop.tianting.mealorderservice.SingleMealMenuRecordRpcResponse;
12 import com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse; 12 import com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse;
13 import com.infoloop.tianting.service.client.ClientInventoryServiceRpcClient; 13 import com.infoloop.tianting.service.client.ClientInventoryServiceRpcClient;
14 +import com.infoloop.tianting.service.client.DeliveryRuleServiceRpcClient;
14 import com.infoloop.tianting.service.client.MealOrderServiceRpcClient; 15 import com.infoloop.tianting.service.client.MealOrderServiceRpcClient;
16 +import com.infoloop.tianting.store.WeeklyTaskStore;
15 import com.infoloop.tianting.utils.DateUtil; 17 import com.infoloop.tianting.utils.DateUtil;
16 import lombok.RequiredArgsConstructor; 18 import lombok.RequiredArgsConstructor;
17 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
...@@ -20,10 +22,12 @@ import org.springframework.stereotype.Component; ...@@ -20,10 +22,12 @@ import org.springframework.stereotype.Component;
20 22
21 import java.time.DayOfWeek; 23 import java.time.DayOfWeek;
22 import java.time.Instant; 24 import java.time.Instant;
23 -import java.time.LocalDate; 25 +import java.time.LocalDateTime;
24 import java.time.LocalTime; 26 import java.time.LocalTime;
25 import java.time.ZonedDateTime; 27 import java.time.ZonedDateTime;
26 import java.time.temporal.TemporalAdjusters; 28 import java.time.temporal.TemporalAdjusters;
29 +import java.util.ArrayList;
30 +import java.util.Collections;
27 import java.util.List; 31 import java.util.List;
28 import java.util.Map; 32 import java.util.Map;
29 import java.util.Objects; 33 import java.util.Objects;
...@@ -40,10 +44,40 @@ public class AutoCreateMealOrderTask { ...@@ -40,10 +44,40 @@ public class AutoCreateMealOrderTask {
40 44
41 private final ClientInventoryServiceRpcClient clientInventoryServiceRpcClient; 45 private final ClientInventoryServiceRpcClient clientInventoryServiceRpcClient;
42 46
43 - @Scheduled(cron = "59 59 23 ? * SUN") 47 + private final DeliveryRuleServiceRpcClient deliveryRuleServiceRpcClient;
48 +
49 + private final WeeklyTaskStore weeklyTaskStore;
50 +
51 + @Scheduled(cron = "0 * * ? * *")
44 public void executeTask() { 52 public void executeTask() {
45 final int enterpriseId = 449; 53 final int enterpriseId = 449;
46 - final var today = LocalDate.now(); 54 + final var now = LocalDateTime.now();
55 + final var today = now.toLocalDate();
56 + // 判断是否本周已执行
57 + final var keyPrefix = "create-meal-order-task";
58 + if (weeklyTaskStore.isExecutedThisWeek(keyPrefix, enterpriseId, today)) return;
59 +
60 + final var rules = deliveryRuleServiceRpcClient.getDefaultDeliveryTemplateAndRule(enterpriseId).getRule();
61 + if (rules.getConfigJson().getRulesList().isEmpty()) {
62 + return;
63 + }
64 + final var rule = rules.getConfigJson().getRules(0);
65 + final var endTime = LocalTime.parse(rule.getEndTime(), DateUtil.HH_MM_SS);
66 + final var enabledDays = new ArrayList<DayOfWeek>();
67 + if (Boolean.TRUE.equals(rule.getMonday())) enabledDays.add(DayOfWeek.MONDAY);
68 + if (Boolean.TRUE.equals(rule.getTuesday())) enabledDays.add(DayOfWeek.TUESDAY);
69 + if (Boolean.TRUE.equals(rule.getWednesday())) enabledDays.add(DayOfWeek.WEDNESDAY);
70 + if (Boolean.TRUE.equals(rule.getThursday())) enabledDays.add(DayOfWeek.THURSDAY);
71 + if (Boolean.TRUE.equals(rule.getFriday())) enabledDays.add(DayOfWeek.FRIDAY);
72 + if (Boolean.TRUE.equals(rule.getSaturday())) enabledDays.add(DayOfWeek.SATURDAY);
73 + if (Boolean.TRUE.equals(rule.getSunday())) enabledDays.add(DayOfWeek.SUNDAY);
74 + if (enabledDays.isEmpty()) {
75 + return;
76 + }
77 + final var maxDay = Collections.max(enabledDays);
78 + final var endDateTime = today.with(TemporalAdjusters.nextOrSame(maxDay)).atTime(endTime);
79 + if (now.isBefore(endDateTime)) return;
80 +
47 final var thisWeekStart = today.with(DayOfWeek.MONDAY).atStartOfDay().atZone(DateUtil.CHINA_ZONE); 81 final var thisWeekStart = today.with(DayOfWeek.MONDAY).atStartOfDay().atZone(DateUtil.CHINA_ZONE);
48 final var thisWeekEnd = today.with(DayOfWeek.SUNDAY).atTime(LocalTime.MAX).atZone(DateUtil.CHINA_ZONE); 82 final var thisWeekEnd = today.with(DayOfWeek.SUNDAY).atTime(LocalTime.MAX).atZone(DateUtil.CHINA_ZONE);
49 final var nextWeekStart = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).atStartOfDay().atZone(DateUtil.CHINA_ZONE); 83 final var nextWeekStart = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).atStartOfDay().atZone(DateUtil.CHINA_ZONE);
...@@ -78,6 +112,8 @@ public class AutoCreateMealOrderTask { ...@@ -78,6 +112,8 @@ public class AutoCreateMealOrderTask {
78 .build(); 112 .build();
79 mealOrderServiceRpcClient.batchCreateMealOrders(loginInfo, creations); 113 mealOrderServiceRpcClient.batchCreateMealOrders(loginInfo, creations);
80 log.info("本周未订餐学生已自动补单 {} 条", creations.size()); 114 log.info("本周未订餐学生已自动补单 {} 条", creations.size());
115 + // 标记本周已执行
116 + weeklyTaskStore.markExecuted(keyPrefix, enterpriseId, today);
81 } 117 }
82 118
83 private MealOrderCreation toMealOrderCreation(SingleDinerRpcResponse diner, 119 private MealOrderCreation toMealOrderCreation(SingleDinerRpcResponse diner,
......
1 +package com.infoloop.tianting.store;
2 +
3 +import io.lettuce.core.SetArgs;
4 +import io.lettuce.core.api.sync.RedisCommands;
5 +
6 +import java.time.DayOfWeek;
7 +import java.time.LocalDate;
8 +import java.time.temporal.WeekFields;
9 +
10 +public class WeeklyTaskStore {
11 +
12 + private final RedisCommands<String, String> commands;
13 + private final long expire; // 过期时间(秒)
14 +
15 + public WeeklyTaskStore(final RedisCommands<String, String> commands, final long expire) {
16 + this.commands = commands;
17 + this.expire = expire;
18 + }
19 +
20 + public boolean isExecutedThisWeek(String keyPrefix, int enterpriseId, LocalDate date) {
21 + String key = getWeeklyKey(keyPrefix, enterpriseId, date);
22 + return commands.exists(key) > 0;
23 + }
24 +
25 + public void markExecuted(String keyPrefix, int enterpriseId, LocalDate date) {
26 + String key = getWeeklyKey(keyPrefix, enterpriseId, date);
27 + commands.set(key, "1", new SetArgs().ex(expire));
28 + }
29 +
30 + private String getWeeklyKey(String keyPrefix, int enterpriseId, LocalDate date) {
31 + WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
32 + int year = date.getYear();
33 + int week = date.get(weekFields.weekOfWeekBasedYear());
34 + return String.format("%s:%d:%d-W%d", keyPrefix, enterpriseId, year, week);
35 + }
36 +}