格式化

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 {
private Global global = new Global();
private List<Group> groups = new ArrayList<>();
public Global getGlobal() {
@ -44,12 +43,13 @@ public class TaskReminderConfig {
*/
public Group getGroupById(String groupId) {
return groups.stream()
.filter(group -> group.getId().equals(groupId))
.findFirst()
.orElse(null);
.filter(group -> group.getId().equals(groupId))
.findFirst()
.orElse(null);
}
public static class Global {
private int timeout = 5000;
@ -64,17 +64,12 @@ public class TaskReminderConfig {
public static class Group {
private String id;
private String name;
private Webhook webhook = new Webhook();
private TaskSystemType taskSystem;
private Map<String, Schedule> schedules = new HashMap<>();
private Zentao zentao = new Zentao();
private Map<String, String> userMapping = new HashMap<>();
public String getId() {
@ -132,6 +127,7 @@ public class TaskReminderConfig {
}
public Zentao getZentao() {
return zentao;
}
@ -163,9 +159,7 @@ public class TaskReminderConfig {
public static class Schedule {
private String time;
private String message;
private boolean enabled = true; // 默认启用
public String getTime() {
@ -195,15 +189,10 @@ public class TaskReminderConfig {
public static class Zentao {
private String apiUrl;
private String token;
private String username;
private String password;
private Integer projectId;
private Integer kanbanId;
public String getApiUrl() {

View File

@ -1,6 +1,7 @@
package com.zeodao.reminder.controller;
import com.zeodao.reminder.config.TaskReminderConfig;
import com.zeodao.reminder.enums.ScheduleType;
import com.zeodao.reminder.enums.TaskSystemType;
import com.zeodao.reminder.model.ZentaoTask;
import com.zeodao.reminder.model.ZentaoBug;
@ -102,26 +103,6 @@ public class TaskReminderController {
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}")
public ResponseEntity<Map<String, Object>> triggerGroupReminder(
@PathVariable String groupId,
@PathVariable String scheduleType) {
@PathVariable ScheduleType scheduleType) {
Map<String, Object> response = new HashMap<>();
try {

View File

@ -7,24 +7,23 @@ package com.zeodao.reminder.enums;
* @version 2.0.0
*/
public enum ScheduleType {
/**
* 早上提醒
*/
MORNING("morning", "早上提醒"),
/**
* 晚上提醒
*/
EVENING("evening", "晚上提醒"),
/**
* 逾期任务提醒
*/
OVERDUE_REMINDER("overdue-reminder", "逾期任务提醒");
private final String code;
private final String description;
ScheduleType(String code, String description) {

View File

@ -7,39 +7,38 @@ package com.zeodao.reminder.enums;
* @version 2.0.0
*/
public enum TaskSystemType {
/**
* 禅道
*/
ZENTAO("zentao", "禅道"),
/**
* 智能表格
*/
SMARTSHEET("smartsheet", "智能表格"),
/**
* Jira
*/
JIRA("jira", "Jira"),
/**
* Trello
*/
TRELLO("trello", "Trello"),
/**
* Asana
*/
ASANA("asana", "Asana"),
/**
* Notion
*/
NOTION("notion", "Notion");
private final String code;
private final String description;
TaskSystemType(String code, String description) {

View File

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

View File

@ -42,9 +42,9 @@ public class TaskReminderService {
private EnabledCheckService enabledCheckService;
/**
* 发送指定群组和时间段的任务提醒
* 发送指定群组和时间段的任务提醒字符串版本用于向后兼容
*/
public void sendReminder(String groupId, String scheduleType) {
public void sendReminder(String groupId, ScheduleType scheduleType) {
if (!shouldSendReminder()) {
logger.info("今天是节假日或周末,跳过任务提醒");
return;
@ -71,26 +71,19 @@ public class TaskReminderService {
logger.info("开始发送任务提醒 - 群组: {}, 类型: {}", group.getName(), scheduleType);
// 转换字符串类型为枚举类型
ScheduleType scheduleTypeEnum = ScheduleType.fromCode(scheduleType);
TaskSystemType taskSystemTypeEnum = group.getTaskSystem();
if (scheduleTypeEnum == null) {
logger.error("无效的提醒类型: {}", scheduleType);
return;
}
if (taskSystemTypeEnum == null) {
logger.error("无效的任务系统类型: {}", group.getTaskSystemCode());
return;
}
// 使用策略模式选择合适的处理器
ReminderHandler handler = reminderHandlerFactory.getHandler(scheduleTypeEnum, taskSystemTypeEnum);
ReminderHandler handler = reminderHandlerFactory.getHandler(scheduleType, taskSystemTypeEnum);
if (handler != null) {
handler.handleReminder(group, scheduleTypeEnum, schedule);
handler.handleReminder(group, scheduleType, schedule);
} else {
logger.error("未找到合适的提醒处理器 - 群组: {}, 类型: {}, 任务系统: {}",
group.getName(), scheduleTypeEnum.getDescription(), taskSystemTypeEnum.getDescription());
logger.error("未找到合适的提醒处理器 - 群组: {}, 类型: {}, 任务系统: {}", group.getName(), scheduleType.getDescription(), taskSystemTypeEnum.getDescription());
}
}
@ -101,7 +94,7 @@ public class TaskReminderService {
List<TaskReminderConfig.Group> allGroups = taskReminderConfig.getGroups();
for (TaskReminderConfig.Group group : allGroups) {
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();
for (TaskReminderConfig.Group group : allGroups) {
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();
for (TaskReminderConfig.Group group : allGroups) {
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;
}
/**
* 手动发送测试消息
*/
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.zeodao.reminder.config.TaskReminderConfig;
import com.zeodao.reminder.enums.TaskSystemType;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
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.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
@ -34,10 +29,24 @@ public class WechatWebhookService {
private static final Logger logger = LoggerFactory.getLogger(WechatWebhookService.class);
@Autowired
private TaskReminderConfig taskReminderConfig;
private final TaskReminderConfig taskReminderConfig;
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 {
int timeout = taskReminderConfig.getGlobal().getTimeout();
private boolean sendMessage(String webhookUrl, Map<String, Object> messageBody) {
logger.info("准备发送消息到企业微信URL: {}", webhookUrl);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(webhookUrl);
// 设置请求配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.build();
httpPost.setConfig(requestConfig);
try {
// 设置请求头
httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 设置请求体
String jsonBody = objectMapper.writeValueAsString(messageBody);
StringEntity entity = new StringEntity(jsonBody, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
// 创建请求实体
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(messageBody, headers);
logger.debug("发送消息内容: {}", jsonBody);
logger.debug("发送消息内容: {}", objectMapper.writeValueAsString(messageBody));
// 执行请求
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
// 发送请求
ResponseEntity<String> response = restTemplate.postForEntity(webhookUrl, requestEntity, String.class);
if (statusCode == 200) {
logger.info("企业微信消息发送成功,响应: {}", responseBody);
return true;
} else {
logger.error("企业微信消息发送失败,状态码: {}, 响应: {}", statusCode, responseBody);
return false;
}
if (response.getStatusCode() == HttpStatus.OK) {
logger.info("企业微信消息发送成功,响应: {}", response.getBody());
return true;
} else {
logger.error("企业微信消息发送失败,状态码: {}, 响应: {}",
response.getStatusCode(), response.getBody());
return false;
}
} catch (Exception e) {
logger.error("发送企业微信消息异常URL: {}", webhookUrl, e);
return false;
}
}
@ -248,19 +245,6 @@ public class WechatWebhookService {
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) {
case ZENTAO:
return "📋";
case SMARTSHEET:
return "📊";
case JIRA:
return "🎯";
case TRELLO:
return "📌";
case ASANA:
return "";
case NOTION:
return "📝";
default:
return "📋";
case ZENTAO: return "📋";
case SMARTSHEET: return "📊";
case JIRA: return "🎯";
case TRELLO: return "📌";
case ASANA: return "";
case NOTION: return "📝";
default: return "📋";
}
}
@ -293,37 +270,37 @@ public class WechatWebhookService {
private String getTaskSystemInstructions(TaskSystemType taskSystemType) {
if (taskSystemType == null) {
return "1. 登录任务管理系统\n" +
"2. 查看分配给自己的任务\n" +
"3. 更新任务状态和进度\n" +
"4. 添加必要的工作记录\n\n";
"2. 查看分配给自己的任务\n" +
"3. 更新任务状态和进度\n" +
"4. 添加必要的工作记录\n\n";
}
switch (taskSystemType) {
case ZENTAO:
return "1. 登录禅道系统\n" +
"2. 查看分配给自己的任务\n" +
"3. 更新任务状态和进度\n" +
"4. 添加必要的工作日志\n\n";
"2. 查看分配给自己的任务\n" +
"3. 更新任务状态和进度\n" +
"4. 添加必要的工作日志\n\n";
case SMARTSHEET:
return "1. 打开智能表格\n" +
"2. 找到自己负责的任务行\n" +
"3. 更新任务状态和完成百分比\n" +
"4. 添加备注说明进展情况\n\n";
"2. 找到自己负责的任务行\n" +
"3. 更新任务状态和完成百分比\n" +
"4. 添加备注说明进展情况\n\n";
case JIRA:
return "1. 登录Jira系统\n" +
"2. 查看分配给自己的Issue\n" +
"3. 更新Issue状态\n" +
"4. 记录工作日志和时间\n\n";
"2. 查看分配给自己的Issue\n" +
"3. 更新Issue状态\n" +
"4. 记录工作日志和时间\n\n";
case TRELLO:
return "1. 打开Trello看板\n" +
"2. 找到自己的任务卡片\n" +
"3. 移动卡片到对应状态列\n" +
"4. 添加评论记录进展\n\n";
"2. 找到自己的任务卡片\n" +
"3. 移动卡片到对应状态列\n" +
"4. 添加评论记录进展\n\n";
default:
return "1. 登录任务管理系统\n" +
"2. 查看分配给自己的任务\n" +
"3. 更新任务状态和进度\n" +
"4. 添加必要的工作记录\n\n";
"2. 查看分配给自己的任务\n" +
"3. 更新任务状态和进度\n" +
"4. 添加必要的工作记录\n\n";
}
}
@ -332,22 +309,14 @@ public class WechatWebhookService {
*/
private String getDayOfWeekInChinese(int dayOfWeek) {
switch (dayOfWeek) {
case 1:
return "星期一";
case 2:
return "星期二";
case 3:
return "星期三";
case 4:
return "星期四";
case 5:
return "星期五";
case 6:
return "星期六";
case 7:
return "星期日";
default:
return "未知";
case 1: return "星期一";
case 2: return "星期二";
case 3: return "星期三";
case 4: return "星期四";
case 5: return "星期五";
case 6: return "星期六";
case 7: return "星期日";
default: return "未知";
}
}
}

View File

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

View File

@ -16,7 +16,7 @@ public interface ReminderHandler {
/**
* 判断是否支持处理指定的提醒类型
*
* @param scheduleType 提醒类型
* @param scheduleType 提醒类型
* @param taskSystemType 任务系统类型
* @return 是否支持
*/

View File

@ -27,7 +27,7 @@ public class ReminderHandlerFactory {
/**
* 获取合适的提醒处理器
*
* @param scheduleType 提醒类型
* @param scheduleType 提醒类型
* @param taskSystemType 任务系统类型
* @return 提醒处理器如果没有找到则返回null
*/
@ -49,7 +49,7 @@ public class ReminderHandlerFactory {
* 获取合适的提醒处理器字符串参数版本用于兼容
*
* @param scheduleTypeCode 提醒类型代码
* @param taskSystemCode 任务系统类型代码
* @param taskSystemCode 任务系统类型代码
* @return 提醒处理器如果没有找到则返回null
*/
public ReminderHandler getHandler(String scheduleTypeCode, String taskSystemCode) {

View File

@ -35,7 +35,11 @@ public class TextReminderHandler implements ReminderHandler {
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);