格式化

This commit is contained in:
dengqichen 2025-05-30 16:00:35 +08:00
parent 1975ff1ffc
commit 5d48ce35ce
11 changed files with 128 additions and 235 deletions

View File

@ -20,7 +20,6 @@ import java.util.Map;
public class TaskReminderConfig { public class TaskReminderConfig {
private Global global = new Global(); private Global global = new Global();
private List<Group> groups = new ArrayList<>(); private List<Group> groups = new ArrayList<>();
public Global getGlobal() { public Global getGlobal() {
@ -50,6 +49,7 @@ public class TaskReminderConfig {
} }
public static class Global { public static class Global {
private int timeout = 5000; private int timeout = 5000;
@ -64,17 +64,12 @@ public class TaskReminderConfig {
public static class Group { public static class Group {
private String id; private String id;
private String name; private String name;
private Webhook webhook = new Webhook(); private Webhook webhook = new Webhook();
private TaskSystemType taskSystem; private TaskSystemType taskSystem;
private Map<String, Schedule> schedules = new HashMap<>(); private Map<String, Schedule> schedules = new HashMap<>();
private Zentao zentao = new Zentao(); private Zentao zentao = new Zentao();
private Map<String, String> userMapping = new HashMap<>(); private Map<String, String> userMapping = new HashMap<>();
public String getId() { public String getId() {
@ -132,6 +127,7 @@ public class TaskReminderConfig {
} }
public Zentao getZentao() { public Zentao getZentao() {
return zentao; return zentao;
} }
@ -163,9 +159,7 @@ public class TaskReminderConfig {
public static class Schedule { public static class Schedule {
private String time; private String time;
private String message; private String message;
private boolean enabled = true; // 默认启用 private boolean enabled = true; // 默认启用
public String getTime() { public String getTime() {
@ -195,15 +189,10 @@ public class TaskReminderConfig {
public static class Zentao { public static class Zentao {
private String apiUrl; private String apiUrl;
private String token; private String token;
private String username; private String username;
private String password; private String password;
private Integer projectId; private Integer projectId;
private Integer kanbanId; private Integer kanbanId;
public String getApiUrl() { public String getApiUrl() {

View File

@ -1,6 +1,7 @@
package com.zeodao.reminder.controller; package com.zeodao.reminder.controller;
import com.zeodao.reminder.config.TaskReminderConfig; import com.zeodao.reminder.config.TaskReminderConfig;
import com.zeodao.reminder.enums.ScheduleType;
import com.zeodao.reminder.enums.TaskSystemType; import com.zeodao.reminder.enums.TaskSystemType;
import com.zeodao.reminder.model.ZentaoTask; import com.zeodao.reminder.model.ZentaoTask;
import com.zeodao.reminder.model.ZentaoBug; import com.zeodao.reminder.model.ZentaoBug;
@ -102,26 +103,6 @@ public class TaskReminderController {
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} }
/**
* 手动发送测试消息
*/
@PostMapping("/test")
public ResponseEntity<Map<String, Object>> sendTestMessage() {
Map<String, Object> response = new HashMap<>();
try {
boolean success = taskReminderService.sendTestMessage();
response.put("success", success);
response.put("message", success ? "测试消息发送成功" : "测试消息发送失败");
} catch (Exception e) {
logger.error("发送测试消息失败", e);
response.put("success", false);
response.put("message", "发送测试消息失败:" + e.getMessage());
}
return ResponseEntity.ok(response);
}
/** /**
* 手动触发早上提醒 * 手动触发早上提醒
*/ */
@ -188,7 +169,7 @@ public class TaskReminderController {
@PostMapping("/groups/{groupId}/{scheduleType}") @PostMapping("/groups/{groupId}/{scheduleType}")
public ResponseEntity<Map<String, Object>> triggerGroupReminder( public ResponseEntity<Map<String, Object>> triggerGroupReminder(
@PathVariable String groupId, @PathVariable String groupId,
@PathVariable String scheduleType) { @PathVariable ScheduleType scheduleType) {
Map<String, Object> response = new HashMap<>(); Map<String, Object> response = new HashMap<>();
try { try {

View File

@ -24,7 +24,6 @@ public enum ScheduleType {
OVERDUE_REMINDER("overdue-reminder", "逾期任务提醒"); OVERDUE_REMINDER("overdue-reminder", "逾期任务提醒");
private final String code; private final String code;
private final String description; private final String description;
ScheduleType(String code, String description) { ScheduleType(String code, String description) {

View File

@ -39,7 +39,6 @@ public enum TaskSystemType {
NOTION("notion", "Notion"); NOTION("notion", "Notion");
private final String code; private final String code;
private final String description; private final String description;
TaskSystemType(String code, String description) { TaskSystemType(String code, String description) {

View File

@ -1,6 +1,7 @@
package com.zeodao.reminder.scheduler; package com.zeodao.reminder.scheduler;
import com.zeodao.reminder.config.TaskReminderConfig; import com.zeodao.reminder.config.TaskReminderConfig;
import com.zeodao.reminder.enums.ScheduleType;
import com.zeodao.reminder.service.EnabledCheckService; import com.zeodao.reminder.service.EnabledCheckService;
import com.zeodao.reminder.service.TaskReminderService; import com.zeodao.reminder.service.TaskReminderService;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -60,36 +61,43 @@ public class DynamicTaskScheduler {
logger.info("为群组 {} 创建定时任务", group.getName()); logger.info("为群组 {} 创建定时任务", group.getName());
for (Map.Entry<String, TaskReminderConfig.Schedule> entry : group.getSchedules().entrySet()) { for (Map.Entry<String, TaskReminderConfig.Schedule> entry : group.getSchedules().entrySet()) {
String scheduleType = entry.getKey(); String scheduleTypeCode = entry.getKey();
TaskReminderConfig.Schedule schedule = entry.getValue(); TaskReminderConfig.Schedule schedule = entry.getValue();
// 检查该提醒类型是否启用 // 将字符串转换为枚举
if (!enabledCheckService.isReminderEnabled(group, scheduleType)) { ScheduleType scheduleType = ScheduleType.fromCode(scheduleTypeCode);
logger.debug("跳过禁用的提醒类型: {} - {}", group.getName(), scheduleType); if (scheduleType == null) {
logger.warn("无效的提醒类型代码: {} - 跳过", scheduleTypeCode);
continue; continue;
} }
String taskKey = group.getId() + "-" + scheduleType; // 检查该提醒类型是否启用
if (!enabledCheckService.isReminderEnabled(group, scheduleType)) {
logger.debug("跳过禁用的提醒类型: {} - {}", group.getName(), scheduleType.getDescription());
continue;
}
String taskKey = group.getId() + "-" + scheduleTypeCode;
try { try {
CronTrigger cronTrigger = new CronTrigger(schedule.getTime()); CronTrigger cronTrigger = new CronTrigger(schedule.getTime());
Runnable task = () -> { Runnable task = () -> {
try { try {
logger.info("=== 执行定时任务: {} - {} ===", group.getName(), scheduleType); logger.info("=== 执行定时任务: {} - {} ===", group.getName(), scheduleType.getDescription());
taskReminderService.sendReminder(group.getId(), scheduleType); taskReminderService.sendReminder(group.getId(), scheduleType);
} catch (Exception e) { } catch (Exception e) {
logger.error("定时任务执行失败: {} - {}", group.getName(), scheduleType, e); logger.error("定时任务执行失败: {} - {}", group.getName(), scheduleType.getDescription(), e);
} }
}; };
ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(task, cronTrigger); ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(task, cronTrigger);
scheduledTasks.put(taskKey, scheduledFuture); scheduledTasks.put(taskKey, scheduledFuture);
logger.info("创建定时任务成功: {} - {} ({})", group.getName(), scheduleType, schedule.getTime()); logger.info("创建定时任务成功: {} - {} ({})", group.getName(), scheduleType.getDescription(), schedule.getTime());
} catch (Exception e) { } catch (Exception e) {
logger.error("创建定时任务失败: {} - {} ({})", group.getName(), scheduleType, schedule.getTime(), e); logger.error("创建定时任务失败: {} - {} ({})", group.getName(), scheduleType.getDescription(), schedule.getTime(), e);
} }
} }
} }
@ -144,11 +152,13 @@ public class DynamicTaskScheduler {
info.append("总任务数: ").append(scheduledTasks.size()).append("\n"); info.append("总任务数: ").append(scheduledTasks.size()).append("\n");
info.append("活跃任务数: ").append(getActiveTaskCount()).append("\n"); info.append("活跃任务数: ").append(getActiveTaskCount()).append("\n");
info.append("配置群组数: ").append(taskReminderConfig.getGroups().size()).append("\n\n"); info.append("配置群组数: ").append(taskReminderConfig.getGroups().size()).append("\n\n");
info.append("=== 群组配置详情 ===\n"); info.append("=== 群组配置详情 ===\n");
for (TaskReminderConfig.Group group : taskReminderConfig.getGroups()) { for (TaskReminderConfig.Group group : taskReminderConfig.getGroups()) {
info.append("群组: ").append(group.getName()).append(" (").append(group.getId()).append(")\n"); info.append("群组: ").append(group.getName()).append(" (").append(group.getId()).append(")\n");
info.append("任务系统: ").append(group.getTaskSystem()).append("\n"); info.append("任务系统: ").append(group.getTaskSystem()).append("\n");
info.append("定时任务:\n"); info.append("定时任务:\n");
for (Map.Entry<String, TaskReminderConfig.Schedule> entry : group.getSchedules().entrySet()) { for (Map.Entry<String, TaskReminderConfig.Schedule> entry : group.getSchedules().entrySet()) {
String scheduleType = entry.getKey(); String scheduleType = entry.getKey();
TaskReminderConfig.Schedule schedule = entry.getValue(); TaskReminderConfig.Schedule schedule = entry.getValue();
@ -166,7 +176,8 @@ public class DynamicTaskScheduler {
status = "已停止"; status = "已停止";
} }
info.append(" - ").append(scheduleType).append(": ").append(schedule.getTime()).append(" (").append(status).append(")\n"); info.append(" - ").append(scheduleType).append(": ").append(schedule.getTime())
.append(" (").append(status).append(")\n");
} }
info.append("\n"); info.append("\n");
} }

View File

@ -42,9 +42,9 @@ public class TaskReminderService {
private EnabledCheckService enabledCheckService; private EnabledCheckService enabledCheckService;
/** /**
* 发送指定群组和时间段的任务提醒 * 发送指定群组和时间段的任务提醒字符串版本用于向后兼容
*/ */
public void sendReminder(String groupId, String scheduleType) { public void sendReminder(String groupId, ScheduleType scheduleType) {
if (!shouldSendReminder()) { if (!shouldSendReminder()) {
logger.info("今天是节假日或周末,跳过任务提醒"); logger.info("今天是节假日或周末,跳过任务提醒");
return; return;
@ -71,26 +71,19 @@ public class TaskReminderService {
logger.info("开始发送任务提醒 - 群组: {}, 类型: {}", group.getName(), scheduleType); logger.info("开始发送任务提醒 - 群组: {}, 类型: {}", group.getName(), scheduleType);
// 转换字符串类型为枚举类型 // 转换字符串类型为枚举类型
ScheduleType scheduleTypeEnum = ScheduleType.fromCode(scheduleType);
TaskSystemType taskSystemTypeEnum = group.getTaskSystem(); TaskSystemType taskSystemTypeEnum = group.getTaskSystem();
if (scheduleTypeEnum == null) {
logger.error("无效的提醒类型: {}", scheduleType);
return;
}
if (taskSystemTypeEnum == null) { if (taskSystemTypeEnum == null) {
logger.error("无效的任务系统类型: {}", group.getTaskSystemCode()); logger.error("无效的任务系统类型: {}", group.getTaskSystemCode());
return; return;
} }
// 使用策略模式选择合适的处理器 // 使用策略模式选择合适的处理器
ReminderHandler handler = reminderHandlerFactory.getHandler(scheduleTypeEnum, taskSystemTypeEnum); ReminderHandler handler = reminderHandlerFactory.getHandler(scheduleType, taskSystemTypeEnum);
if (handler != null) { if (handler != null) {
handler.handleReminder(group, scheduleTypeEnum, schedule); handler.handleReminder(group, scheduleType, schedule);
} else { } else {
logger.error("未找到合适的提醒处理器 - 群组: {}, 类型: {}, 任务系统: {}", logger.error("未找到合适的提醒处理器 - 群组: {}, 类型: {}, 任务系统: {}", group.getName(), scheduleType.getDescription(), taskSystemTypeEnum.getDescription());
group.getName(), scheduleTypeEnum.getDescription(), taskSystemTypeEnum.getDescription());
} }
} }
@ -101,7 +94,7 @@ public class TaskReminderService {
List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups(); List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups();
for (TaskReminderConfig.Group group : allGroups) { for (TaskReminderConfig.Group group : allGroups) {
if (enabledCheckService.isReminderEnabled(group, ScheduleType.MORNING)) { if (enabledCheckService.isReminderEnabled(group, ScheduleType.MORNING)) {
sendReminder(group.getId(), "morning"); sendReminder(group.getId(), ScheduleType.MORNING);
} }
} }
} }
@ -113,7 +106,7 @@ public class TaskReminderService {
List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups(); List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups();
for (TaskReminderConfig.Group group : allGroups) { for (TaskReminderConfig.Group group : allGroups) {
if (enabledCheckService.isReminderEnabled(group, ScheduleType.EVENING)) { if (enabledCheckService.isReminderEnabled(group, ScheduleType.EVENING)) {
sendReminder(group.getId(), "evening"); sendReminder(group.getId(), ScheduleType.EVENING);
} }
} }
} }
@ -125,27 +118,11 @@ public class TaskReminderService {
List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups(); List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups();
for (TaskReminderConfig.Group group : allGroups) { for (TaskReminderConfig.Group group : allGroups) {
if (enabledCheckService.isReminderEnabled(group, ScheduleType.OVERDUE_REMINDER)) { if (enabledCheckService.isReminderEnabled(group, ScheduleType.OVERDUE_REMINDER)) {
sendReminder(group.getId(), "overdue-reminder"); sendReminder(group.getId(), ScheduleType.OVERDUE_REMINDER);
} }
} }
} }
/**
* 兼容旧版本的方法
*/
@Deprecated
public void sendMorningReminder() {
sendAllMorningReminders();
}
/**
* 兼容旧版本的方法
*/
@Deprecated
public void sendEveningReminder() {
sendAllEveningReminders();
}
/** /**
* 判断是否应该发送提醒 * 判断是否应该发送提醒
* 排除周末和节假日 * 排除周末和节假日
@ -168,40 +145,4 @@ public class TaskReminderService {
return true; return true;
} }
/**
* 手动发送测试消息
*/
public boolean sendTestMessage() {
logger.info("发送测试消息");
String testMessage = wechatWebhookService.createTaskReminderMessage(
"这是一条测试消息用于验证企业微信webhook是否正常工作。",
"测试消息"
);
boolean success = wechatWebhookService.sendMarkdownMessage(testMessage);
if (success) {
logger.info("测试消息发送成功");
} else {
logger.error("测试消息发送失败");
}
return success;
}
/**
* 获取下次提醒时间信息
*/
public String getNextReminderInfo() {
LocalDateTime now = LocalDateTime.now();
StringBuilder info = new StringBuilder();
info.append("当前时间:").append(now.toString()).append("\n");
info.append("今天是否工作日:").append(shouldSendReminder() ? "" : "").append("\n");
info.append("早上提醒时间:每个工作日 09:00\n");
info.append("晚上提醒时间:每个工作日 17:30\n");
return info.toString();
}
} }

View File

@ -3,20 +3,15 @@ package com.zeodao.reminder.service;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.zeodao.reminder.config.TaskReminderConfig; import com.zeodao.reminder.config.TaskReminderConfig;
import com.zeodao.reminder.enums.TaskSystemType; import com.zeodao.reminder.enums.TaskSystemType;
import org.apache.http.client.config.RequestConfig; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.apache.http.client.methods.CloseableHttpResponse; import org.springframework.http.*;
import org.apache.http.client.methods.HttpPost; import org.springframework.web.client.RestTemplate;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException; import java.time.Duration;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
@ -34,10 +29,24 @@ public class WechatWebhookService {
private static final Logger logger = LoggerFactory.getLogger(WechatWebhookService.class); private static final Logger logger = LoggerFactory.getLogger(WechatWebhookService.class);
@Autowired private final TaskReminderConfig taskReminderConfig;
private TaskReminderConfig taskReminderConfig;
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
private final RestTemplate restTemplate;
public WechatWebhookService(TaskReminderConfig taskReminderConfig) {
this.taskReminderConfig = taskReminderConfig;
// 根据配置设置超时时间如果配置为空则使用默认值
int timeoutSeconds = taskReminderConfig != null && taskReminderConfig.getGlobal() != null
? taskReminderConfig.getGlobal().getTimeout() / 1000
: 10;
this.restTemplate = new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(timeoutSeconds))
.setReadTimeout(Duration.ofSeconds(timeoutSeconds))
.build();
}
/** /**
* 发送文本消息到指定群组 * 发送文本消息到指定群组
@ -175,45 +184,33 @@ public class WechatWebhookService {
/** /**
* 发送消息到企业微信 * 发送消息到企业微信
*/ */
private boolean sendMessage(String webhookUrl, Map<String, Object> messageBody) throws IOException { private boolean sendMessage(String webhookUrl, Map<String, Object> messageBody) {
int timeout = taskReminderConfig.getGlobal().getTimeout();
logger.info("准备发送消息到企业微信URL: {}", webhookUrl); logger.info("准备发送消息到企业微信URL: {}", webhookUrl);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) { try {
HttpPost httpPost = new HttpPost(webhookUrl);
// 设置请求配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.build();
httpPost.setConfig(requestConfig);
// 设置请求头 // 设置请求头
httpPost.setHeader("Content-Type", "application/json; charset=utf-8"); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 设置请求体 // 创建请求实体
String jsonBody = objectMapper.writeValueAsString(messageBody); HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(messageBody, headers);
StringEntity entity = new StringEntity(jsonBody, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
logger.debug("发送消息内容: {}", jsonBody); logger.debug("发送消息内容: {}", objectMapper.writeValueAsString(messageBody));
// 执行请求 // 发送请求
try (CloseableHttpResponse response = httpClient.execute(httpPost)) { ResponseEntity<String> response = restTemplate.postForEntity(webhookUrl, requestEntity, String.class);
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
if (statusCode == 200) { if (response.getStatusCode() == HttpStatus.OK) {
logger.info("企业微信消息发送成功,响应: {}", responseBody); logger.info("企业微信消息发送成功,响应: {}", response.getBody());
return true; return true;
} else { } else {
logger.error("企业微信消息发送失败,状态码: {}, 响应: {}", statusCode, responseBody); logger.error("企业微信消息发送失败,状态码: {}, 响应: {}",
response.getStatusCode(), response.getBody());
return false; return false;
} }
} } catch (Exception e) {
logger.error("发送企业微信消息异常URL: {}", webhookUrl, e);
return false;
} }
} }
@ -248,19 +245,6 @@ public class WechatWebhookService {
return message.toString(); return message.toString();
} }
/**
* 兼容旧版本的方法
*/
@Deprecated
public String createTaskReminderMessage(String baseMessage, String timeType) {
List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups();
if (allGroups.isEmpty()) {
return baseMessage;
}
return createTaskReminderMessage(allGroups.get(0).getId(), baseMessage, timeType);
}
/** /**
* 获取任务管理系统的图标 * 获取任务管理系统的图标
*/ */
@ -270,20 +254,13 @@ public class WechatWebhookService {
} }
switch (taskSystemType) { switch (taskSystemType) {
case ZENTAO: case ZENTAO: return "📋";
return "📋"; case SMARTSHEET: return "📊";
case SMARTSHEET: case JIRA: return "🎯";
return "📊"; case TRELLO: return "📌";
case JIRA: case ASANA: return "";
return "🎯"; case NOTION: return "📝";
case TRELLO: default: return "📋";
return "📌";
case ASANA:
return "";
case NOTION:
return "📝";
default:
return "📋";
} }
} }
@ -332,22 +309,14 @@ public class WechatWebhookService {
*/ */
private String getDayOfWeekInChinese(int dayOfWeek) { private String getDayOfWeekInChinese(int dayOfWeek) {
switch (dayOfWeek) { switch (dayOfWeek) {
case 1: case 1: return "星期一";
return "星期一"; case 2: return "星期二";
case 2: case 3: return "星期三";
return "星期二"; case 4: return "星期四";
case 3: case 5: return "星期五";
return "星期三"; case 6: return "星期六";
case 4: case 7: return "星期日";
return "星期四"; default: return "未知";
case 5:
return "星期五";
case 6:
return "星期六";
case 7:
return "星期日";
default:
return "未知";
} }
} }
} }

View File

@ -70,7 +70,7 @@ public class ZentaoTaskReminderService {
// 如果任务和BUG都为空发送无事项消息 // 如果任务和BUG都为空发送无事项消息
if (incompleteTasks.isEmpty() && unresolvedBugs.isEmpty()) { if (incompleteTasks.isEmpty() && unresolvedBugs.isEmpty()) {
logger.info("No incomplete tasks or unresolved bugs found for group: {}", group.getId()); logger.info("No incomplete tasks or unresolved bugs found for group: {}", group.getId());
sendNoItemsMessage(group, projectInfo); // sendNoItemsMessage(group, projectInfo);
return; return;
} }

View File

@ -35,7 +35,11 @@ public class TextReminderHandler implements ReminderHandler {
logger.info("处理文本提醒 - 群组: {}, 类型: {}", group.getName(), scheduleType.getDescription()); logger.info("处理文本提醒 - 群组: {}, 类型: {}", group.getName(), scheduleType.getDescription());
// 生成带格式的提醒消息 // 生成带格式的提醒消息
String message = wechatWebhookService.createTaskReminderMessage(group.getId(), schedule.getMessage(), scheduleType.getDescription()); String message = wechatWebhookService.createTaskReminderMessage(
group.getId(),
schedule.getMessage(),
scheduleType.getDescription()
);
// 发送消息 // 发送消息
boolean success = wechatWebhookService.sendMarkdownMessage(group.getId(), message); boolean success = wechatWebhookService.sendMarkdownMessage(group.getId(), message);