diff --git a/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql b/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql index df9b4aa..79a62b6 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql @@ -7,6 +7,8 @@ CREATE TABLE `tb_crontab_task` ( `task_id` VARCHAR(64) NOT NULL COMMENT '任务ID', `task_name` VARCHAR(100) NOT NULL COMMENT '任务名称', `task_group` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT' COMMENT '任务分组', + `meta_id` VARCHAR(64) NOT NULL COMMENT '任务元数据ID', + `default_recipient` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否使用默认接收人(0:否 1:是)', `bean_name` VARCHAR(100) NOT NULL COMMENT 'Bean名称', `method_name` VARCHAR(100) NOT NULL COMMENT '方法名称', `method_params` VARCHAR(500) DEFAULT NULL COMMENT '方法参数', @@ -94,4 +96,78 @@ CREATE TABLE `tb_data_collection_item` ( KEY `idx_status` (`status`), KEY `idx_publish_time` (`publish_time`), KEY `idx_source_url` (`source_url`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='数据采集项表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='数据采集项表'; + +-- ==================================================== +-- 定时任务元数据表(存储爬虫任务的元数据配置) +-- ==================================================== +DROP TABLE IF EXISTS `tb_crontab_task_meta`; +CREATE TABLE `tb_crontab_task_meta` ( + `id` VARCHAR(64) NOT NULL COMMENT '主键ID', + `meta_id` VARCHAR(64) NOT NULL COMMENT '元数据ID', + `name` VARCHAR(100) NOT NULL COMMENT '任务名称', + `description` VARCHAR(500) DEFAULT NULL COMMENT '任务描述', + `category` VARCHAR(50) NOT NULL COMMENT '任务分类(如:人民日报新闻爬取)', + `bean_name` VARCHAR(100) NOT NULL COMMENT 'Bean名称(执行器类名)', + `method_name` VARCHAR(100) NOT NULL COMMENT '执行方法名', + `script_path` VARCHAR(255) DEFAULT NULL COMMENT 'Python脚本路径(相对于basePath)', + `param_schema` TEXT DEFAULT NULL COMMENT '参数模板(JSON格式,定义参数名、类型、描述、默认值等)', + `auto_publish` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否自动发布', + `sort_order` INT DEFAULT 0 COMMENT '排序号', + `creator` VARCHAR(64) DEFAULT NULL COMMENT '创建者', + `updater` VARCHAR(64) DEFAULT NULL COMMENT '更新者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `delete_time` DATETIME DEFAULT NULL COMMENT '删除时间', + `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:否 1:是)', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_meta_id` (`meta_id`), + KEY `idx_category` (`category`), + KEY `idx_deleted` (`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务元数据表'; + +-- ==================================================== +-- 定时任务邮件通知默认接收人员表(关联任务元数据,定义默认接收人员) +-- ==================================================== +DROP TABLE IF EXISTS `tb_crontab_email_default`; +CREATE TABLE `tb_crontab_email_default` ( + `id` VARCHAR(64) NOT NULL COMMENT '主键ID', + `default_id` VARCHAR(64) NOT NULL COMMENT '默认ID', + `meta_id` VARCHAR(64) NOT NULL COMMENT '关联任务元数据ID', + `user_id` VARCHAR(64) NOT NULL COMMENT '关联用户ID', + `creator` VARCHAR(64) DEFAULT NULL COMMENT '创建者', + `updater` VARCHAR(64) DEFAULT NULL COMMENT '更新者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `delete_time` DATETIME DEFAULT NULL COMMENT '删除时间', + `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:否 1:是)', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_default_id` (`default_id`), + UNIQUE KEY `uk_meta_id` (`meta_id`), + KEY `idx_deleted` (`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务邮件通知默认接收人员表'; + +-- ==================================================== +-- 定时任务邮件接收人表(1对多) +-- ==================================================== +DROP TABLE IF EXISTS `tb_crontab_email_recipient`; +CREATE TABLE `tb_crontab_email_recipient` ( + `id` VARCHAR(64) NOT NULL COMMENT '主键ID', + `recipient_id` VARCHAR(64) NOT NULL COMMENT '接收人ID', + `task_id` VARCHAR(64) DEFAULT NULL COMMENT '关联任务ID(NULL表示不属于任何任务)', + `user_id` VARCHAR(64) DEFAULT NULL COMMENT '关联用户ID', + `email` VARCHAR(100) NOT NULL COMMENT '邮箱地址', + `name` VARCHAR(100) DEFAULT NULL COMMENT '接收人姓名', + `creator` VARCHAR(64) DEFAULT NULL COMMENT '创建者', + `updater` VARCHAR(64) DEFAULT NULL COMMENT '更新者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `delete_time` DATETIME DEFAULT NULL COMMENT '删除时间', + `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:否 1:是)', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_recipient_id` (`recipient_id`), + KEY `idx_task_id` (`task_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_email` (`email`), + KEY `idx_deleted` (`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务邮件接收人表'; \ No newline at end of file diff --git a/schoolNewsServ/.bin/mysql/sql/initAllData.sql b/schoolNewsServ/.bin/mysql/sql/initAllData.sql index 50e4ace..4046c9a 100644 --- a/schoolNewsServ/.bin/mysql/sql/initAllData.sql +++ b/schoolNewsServ/.bin/mysql/sql/initAllData.sql @@ -38,7 +38,9 @@ INSERT INTO `tb_sys_config` (id, config_key, config_value, config_type, config_g ('10', 'system.banner.interval', '5000', 'number', 'banner', 'Banner切换间隔(毫秒)', 0, '1', now()), ('11', 'system.resource.auto_publish', 'false', 'boolean', 'resource', '资源自动发布', 0, '1', now()), ('12', 'system.resource.auto_publish_time', '08:00', 'string', 'resource', '自动发布时间', 0, '1', now()), -('13', 'system.ai.enabled', 'true', 'boolean', 'ai', '是否启用智能体', 0, '1', now()); +('13', 'system.ai.enabled', 'true', 'boolean', 'ai', '是否启用智能体', 0, '1', now()), +('14', 'crawler.pythonPath', 'F:/Environment/Conda/envs/schoolNewsCrawler/python.exe', 'string', 'crawler', 'Python可执行文件路径', 1, '1', now()), +('15', 'crawler.basePath', 'F:/Project/schoolNews/schoolNewsCrawler', 'string', 'crawler', '爬虫脚本根目录', 1, '1', now()); -- 注意:默认superadmin用户已在 initMenuData.sql 中创建,此处无需重复创建 diff --git a/schoolNewsServ/.bin/mysql/sql/initCrontabMetaData.sql b/schoolNewsServ/.bin/mysql/sql/initCrontabMetaData.sql new file mode 100644 index 0000000..34e9408 --- /dev/null +++ b/schoolNewsServ/.bin/mysql/sql/initCrontabMetaData.sql @@ -0,0 +1,102 @@ +-- ==================================================== +-- 定时任务元数据初始化脚本 +-- ==================================================== + +-- 插入人民日报新闻爬取任务的元数据 + +-- 1. 关键字搜索爬取 +INSERT INTO `tb_crontab_task_meta` ( + `id`, `meta_id`, `name`, `description`, `category`, + `bean_name`, `method_name`, `script_path`, `param_schema`, + `sort_order`, `creator`, `create_time` +) VALUES ( + '1', + 'rmbr_keyword_search', + '关键字搜索爬取', + '根据关键字搜索人民日报新闻内容', + '人民日报新闻爬取', + 'newsCrewerTask', + 'execute', + 'crawler/RmrbSearch.py', + '[ + { + "name": "query", + "description": "搜索关键字", + "type": "String", + "value": "", + "required": true + }, + { + "name": "total", + "description": "总新闻数量", + "type": "Integer", + "value": 10, + "required": true + } + ]', + 1, + 'system', + NOW() +); + +-- 2. 排行榜爬取 +INSERT INTO `tb_crontab_task_meta` ( + `id`, `meta_id`, `name`, `description`, `category`, + `bean_name`, `method_name`, `script_path`, `param_schema`, + `sort_order`, `creator`, `create_time` +) VALUES ( + '2', + 'rmbr_hotpoint', + '排行榜爬取', + '爬取人民日报热门排行榜新闻', + '人民日报新闻爬取', + 'newsCrewerTask', + 'execute', + 'crawler/RmrbHotPoint.py', + '[]', + 2, + 'system', + NOW() +); + +-- 3. 往日精彩头条爬取 +INSERT INTO `tb_crontab_task_meta` ( + `id`, `meta_id`, `name`, `description`, `category`, + `bean_name`, `method_name`, `script_path`, `param_schema`, + `sort_order`, `creator`, `create_time` +) VALUES ( + '3', + 'rmbr_trending', + '往日精彩头条爬取', + '爬取人民日报往日精彩头条新闻', + '人民日报新闻爬取', + 'newsCrewerTask', + 'execute', + 'crawler/RmrbTrending.py', + '[ + { + "name": "startDate", + "description": "开始日期", + "type": "String", + "value": "", + "required": false + }, + { + "name": "endDate", + "description": "结束日期", + "type": "String", + "value": "", + "required": false + }, + { + "name": "yesterday", + "description": "是否是昨天", + "type": "Boolean", + "value": true, + "required": false + } + ]', + 3, + 'system', + NOW() +); \ No newline at end of file diff --git a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/controller/AchievementController.java b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/controller/AchievementController.java index 4ba2d72..84a1a84 100644 --- a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/controller/AchievementController.java +++ b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/controller/AchievementController.java @@ -52,7 +52,7 @@ public class AchievementController { * 删除成就 */ @DeleteMapping("/achievement") - public ResultDomain deleteAchievement(@RequestBody TbAchievement achievement) { + public ResultDomain deleteAchievement(@RequestBody TbAchievement achievement) { return achievementService.deleteAchievement(achievement.getAchievementID()); } @@ -125,7 +125,7 @@ public class AchievementController { * 撤销用户成就 */ @DeleteMapping("/revoke") - public ResultDomain revokeAchievement( + public ResultDomain revokeAchievement( @RequestParam(name = "userID") String userID, @RequestParam(name = "achievementID") String achievementID) { return achievementService.revokeAchievement(userID, achievementID); diff --git a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java index 8de4230..d898dec 100644 --- a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java +++ b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java @@ -168,8 +168,8 @@ public class ACHAchievementServiceImpl implements AchievementService { @Override @Transactional(rollbackFor = Exception.class) - public ResultDomain deleteAchievement(String achievementID) { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain deleteAchievement(String achievementID) { + ResultDomain resultDomain = new ResultDomain<>(); try { if (!StringUtils.hasText(achievementID)) { resultDomain.fail("成就ID不能为空"); @@ -188,7 +188,7 @@ public class ACHAchievementServiceImpl implements AchievementService { int result = achievementMapper.updateAchievement(achievement); if (result > 0) { - resultDomain.success("删除成就成功", (Void) null); + resultDomain.success("删除成就成功", true); } else { resultDomain.fail("删除成就失败"); } @@ -405,8 +405,8 @@ public class ACHAchievementServiceImpl implements AchievementService { @Override @Transactional(rollbackFor = Exception.class) - public ResultDomain revokeAchievement(String userID, String achievementID) { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain revokeAchievement(String userID, String achievementID) { + ResultDomain resultDomain = new ResultDomain<>(); try { if (!StringUtils.hasText(userID) || !StringUtils.hasText(achievementID)) { resultDomain.fail("参数不能为空"); @@ -417,7 +417,7 @@ public class ACHAchievementServiceImpl implements AchievementService { if (result > 0) { // 重置进度 progressMapper.deleteProgress(userID, achievementID); - resultDomain.success("撤销成就成功", (Void) null); + resultDomain.success("撤销成就成功", true); } else { resultDomain.fail("撤销成就失败"); } diff --git a/schoolNewsServ/api/api-achievement/src/main/java/org/xyzh/api/achievement/AchievementService.java b/schoolNewsServ/api/api-achievement/src/main/java/org/xyzh/api/achievement/AchievementService.java index 4913d64..86fc7d6 100644 --- a/schoolNewsServ/api/api-achievement/src/main/java/org/xyzh/api/achievement/AchievementService.java +++ b/schoolNewsServ/api/api-achievement/src/main/java/org/xyzh/api/achievement/AchievementService.java @@ -38,9 +38,9 @@ public interface AchievementService { /** * @description 删除成就 * @param achievementID 成就ID - * @return ResultDomain 删除结果 + * @return ResultDomain 删除结果 */ - ResultDomain deleteAchievement(String achievementID); + ResultDomain deleteAchievement(String achievementID); /** * @description 获取所有成就列表 @@ -112,9 +112,9 @@ public interface AchievementService { * @description 撤销用户成就(管理员功能) * @param userID 用户ID * @param achievementID 成就ID - * @return ResultDomain 撤销结果 + * @return ResultDomain 撤销结果 */ - ResultDomain revokeAchievement(String userID, String achievementID); + ResultDomain revokeAchievement(String userID, String achievementID); // ==================== 成就进度管理 ==================== diff --git a/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java index 3aa2751..2558310 100644 --- a/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java +++ b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java @@ -2,6 +2,7 @@ package org.xyzh.api.crontab; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.CreateTaskRequest; import org.xyzh.common.dto.crontab.TbCrontabTask; import org.xyzh.common.dto.crontab.TbCrontabLog; import org.xyzh.common.vo.CrontabTaskVO; @@ -27,6 +28,15 @@ public interface CrontabService { */ ResultDomain createTask(TbCrontabTask task); + /** + * @description 创建定时任务并绑定邮件接收人 + * @param request 创建任务请求(包含任务信息、是否使用默认接收人、额外接收人列表) + * @return ResultDomain 创建结果 + * @author yslg + * @since 2025-11-18 + */ + ResultDomain createTaskWithRecipients(CreateTaskRequest request); + /** * @description 更新定时任务 * @param task 任务对象 @@ -36,6 +46,15 @@ public interface CrontabService { */ ResultDomain updateTask(TbCrontabTask task); + /** + * @description 更新定时任务并更新邮件接收人 + * @param request 更新任务请求(包含任务信息、是否使用默认接收人、额外接收人列表) + * @return ResultDomain 更新结果 + * @author yslg + * @since 2025-11-18 + */ + ResultDomain updateTaskWithRecipients(CreateTaskRequest request); + /** * @description 删除定时任务 * @param taskId 任务ID diff --git a/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/EmailDefaultService.java b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/EmailDefaultService.java new file mode 100644 index 0000000..cea8f69 --- /dev/null +++ b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/EmailDefaultService.java @@ -0,0 +1,39 @@ +package org.xyzh.api.crontab; + +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.crontab.TbCrontabEmailDefault; + +/** + * @description 定时任务邮件通知默认接收人服务接口 + * @filename EmailDefaultService.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public interface EmailDefaultService { + + /** + * @description 创建默认接收人 + */ + ResultDomain createDefault(TbCrontabEmailDefault emailDefault); + + /** + * @description 更新默认接收人 + */ + ResultDomain updateDefault(TbCrontabEmailDefault emailDefault); + + /** + * @description 删除默认接收人 + */ + ResultDomain deleteDefault(String defaultId); + + /** + * @description 根据defaultId查询 + */ + ResultDomain getDefaultById(String defaultId); + + /** + * @description 根据metaId查询 + */ + ResultDomain getDefaultByMetaId(String metaId); +} diff --git a/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/EmailRecipientService.java b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/EmailRecipientService.java new file mode 100644 index 0000000..a1061be --- /dev/null +++ b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/EmailRecipientService.java @@ -0,0 +1,58 @@ +package org.xyzh.api.crontab; + +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient; + +import java.util.List; + +/** + * @description 定时任务邮件接收人服务接口 + * @filename EmailRecipientService.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public interface EmailRecipientService { + + /** + * @description 创建邮件接收人 + */ + ResultDomain createRecipient(TbCrontabEmailRecipient recipient); + + /** + * @description 批量创建邮件接收人 + */ + ResultDomain batchCreateRecipient(List recipients); + + /** + * @description 更新邮件接收人 + */ + ResultDomain updateRecipient(TbCrontabEmailRecipient recipient); + + /** + * @description 删除邮件接收人 + */ + ResultDomain deleteRecipient(String recipientId); + + /** + * @description 根据ID查询 + */ + ResultDomain getRecipientById(String recipientId); + + /** + * @description 根据任务ID查询接收人列表 + */ + ResultDomain getRecipientsByTaskId(String taskId); + + /** + * @description 分页查询邮件接收人 + */ + ResultDomain getRecipientPage(PageRequest request); + + + /** + * @description 删除任务的所有接收人 + */ + ResultDomain deleteRecipientsByTaskId(String taskId); +} diff --git a/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/TaskMetaService.java b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/TaskMetaService.java new file mode 100644 index 0000000..368117f --- /dev/null +++ b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/TaskMetaService.java @@ -0,0 +1,57 @@ +package org.xyzh.api.crontab; + +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabTaskMeta; + +import java.util.List; + +/** + * @description 定时任务元数据服务接口 + * @filename TaskMetaService.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public interface TaskMetaService { + + /** + * @description 创建任务元数据 + */ + ResultDomain createTaskMeta(TbCrontabTaskMeta taskMeta); + + /** + * @description 更新任务元数据 + */ + ResultDomain updateTaskMeta(TbCrontabTaskMeta taskMeta); + + /** + * @description 删除任务元数据 + */ + ResultDomain deleteTaskMeta(String metaId); + + /** + * @description 根据ID查询任务元数据 + */ + ResultDomain getTaskMetaById(String metaId); + + /** + * @description 根据任务ID查询任务元数据 + */ + ResultDomain getTaskMetaByTaskId(String taskId); + + /** + * @description 查询所有任务元数据 + */ + ResultDomain getAllTaskMeta(); + + /** + * @description 根据分类查询任务元数据 + */ + ResultDomain getTaskMetaByCategory(String category); + + /** + * @description 分页查询任务元数据 + */ + ResultDomain getTaskMetaPage(PageRequest request); +} diff --git a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/config/SysConfigService.java b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/config/SysConfigService.java index fe0cc1f..96eb9a3 100644 --- a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/config/SysConfigService.java +++ b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/config/SysConfigService.java @@ -9,6 +9,40 @@ package org.xyzh.api.system.config; */ public interface SysConfigService { - /** */ - String getSysConfig(String key); + Object getSysConfig(String key); + + /** + * 获取字符串类型配置 + * @param key 配置键 + * @return 配置值 + */ + String getStringConfig(String key); + + /** + * 获取整数类型配置 + * @param key 配置键 + * @return 配置值,如果不存在或解析失败返回null + */ + Integer getIntConfig(String key); + + /** + * 获取布尔类型配置 + * @param key 配置键 + * @return 配置值,如果不存在或解析失败返回null + */ + Boolean getBooleanConfig(String key); + + /** + * 获取浮点数类型配置 + * @param key 配置键 + * @return 配置值,如果不存在或解析失败返回null + */ + Double getDoubleConfig(String key); + + /** + * 获取长整数类型配置 + * @param key 配置键 + * @return 配置值,如果不存在或解析失败返回null + */ + Long getLongConfig(String key); } diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/CreateTaskRequest.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/CreateTaskRequest.java new file mode 100644 index 0000000..fbbc891 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/CreateTaskRequest.java @@ -0,0 +1,114 @@ +package org.xyzh.common.dto.crontab; + +import java.util.List; + +/** + * @description 创建定时任务请求 + * @filename CreateTaskRequest.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public class CreateTaskRequest { + + /** + * @description 任务信息 + */ + private TbCrontabTask task; + + /** + * @description 任务元数据ID(用于查找默认接收人配置) + */ + private String metaId; + + /** + * @description 额外添加的接收人列表(从系统用户中选择,前端传入完整信息避免重复查询) + */ + private List additionalRecipients; + + public TbCrontabTask getTask() { + return task; + } + + public void setTask(TbCrontabTask task) { + this.task = task; + } + + public String getMetaId() { + return metaId; + } + + public void setMetaId(String metaId) { + this.metaId = metaId; + } + + public List getAdditionalRecipients() { + return additionalRecipients; + } + + public void setAdditionalRecipients(List additionalRecipients) { + this.additionalRecipients = additionalRecipients; + } + + /** + * @description 接收人用户信息(从tb_sys_user选择) + */ + public static class RecipientUserInfo { + /** + * @description 用户ID + */ + private String userId; + + /** + * @description 用户邮箱 + */ + private String userEmail; + + /** + * @description 用户名称 + */ + private String username; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserEmail() { + return userEmail; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String toString() { + return "RecipientUserInfo{" + + "userId='" + userId + '\'' + + ", userEmail='" + userEmail + '\'' + + ", username='" + username + '\'' + + '}'; + } + } + + @Override + public String toString() { + return "CreateTaskRequest{" + + "task=" + task + + ", metaId='" + metaId + '\'' + + ", additionalRecipients=" + additionalRecipients + + '}'; + } +} diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabEmailDefault.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabEmailDefault.java new file mode 100644 index 0000000..a481d9e --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabEmailDefault.java @@ -0,0 +1,117 @@ +package org.xyzh.common.dto.crontab; + +import org.xyzh.common.dto.BaseDTO; + +/** + * @description 定时任务邮件通知默认接收人员表 + * @filename TbCrontabEmailDefault.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public class TbCrontabEmailDefault extends BaseDTO { + + private static final long serialVersionUID = 1L; + + /** + * @description 默认ID + */ + private String defaultId; + + /** + * @description 关联任务元数据ID + */ + private String metaId; + + /** + * @description 关联用户ID + */ + private String userId; + + /** + * @description 关联用户邮箱 + */ + private String userEmail; + + /** + * @description 关联用户名称 + */ + private String username; + + /** + * @description 创建者 + */ + private String creator; + /** + * @description 更新者 + */ + private String updater; + + public String getDefaultId() { + return defaultId; + } + + public void setDefaultId(String defaultId) { + this.defaultId = defaultId; + } + + public String getMetaId() { + return metaId; + } + + public void setMetaId(String metaId) { + this.metaId = metaId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserEmail() { + return userEmail; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getUpdater() { + return updater; + } + + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String toString() { + return "TbCrontabEmailDefault{" + + "id=" + getID() + + ", defaultId='" + defaultId + '\'' + + ", metaId='" + metaId + '\'' + + ", userId='" + userId + '\'' + + ", userEmail='" + userEmail + '\'' + + ", username='" + username + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabEmailRecipient.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabEmailRecipient.java new file mode 100644 index 0000000..fcc6fa0 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabEmailRecipient.java @@ -0,0 +1,118 @@ +package org.xyzh.common.dto.crontab; + +import org.xyzh.common.dto.BaseDTO; + +/** + * @description 定时任务邮件接收人表 + * @filename TbCrontabEmailRecipient.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public class TbCrontabEmailRecipient extends BaseDTO { + + private static final long serialVersionUID = 1L; + + /** + * @description 接收人ID + */ + private String recipientId; + + /** + * @description 关联任务ID(NULL表示不属于任务) + */ + private String taskId; + + /** + * @description 关联用户ID + */ + private String userId; + + /** + * @description 邮箱地址 + */ + private String email; + + /** + * @description 接收人姓名 + */ + private String name; + + /** + * @description 创建者 + */ + private String creator; + + /** + * @description 更新者 + */ + private String updater; + + public String getRecipientId() { + return recipientId; + } + + public void setRecipientId(String recipientId) { + this.recipientId = recipientId; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getUpdater() { + return updater; + } + + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String toString() { + return "TbCrontabEmailRecipient{" + + "id=" + getID() + + ", recipientId='" + recipientId + '\'' + + ", taskId='" + taskId + '\'' + + ", userId='" + userId + '\'' + + ", email='" + email + '\'' + + ", name='" + name + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java index 97be520..ce1a447 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java @@ -43,6 +43,16 @@ public class TbCrontabTask extends BaseDTO { */ private String methodParams; + /** + * @description 元数据ID(关联任务元数据表) + */ + private String metaId; + + /** + * @description 是否使用默认接收人(false:否 true:是) + */ + private Boolean defaultRecipient; + /** * @description Cron表达式 */ @@ -126,6 +136,22 @@ public class TbCrontabTask extends BaseDTO { this.methodParams = methodParams; } + public String getMetaId() { + return metaId; + } + + public void setMetaId(String metaId) { + this.metaId = metaId; + } + + public Boolean getDefaultRecipient() { + return defaultRecipient; + } + + public void setDefaultRecipient(Boolean defaultRecipient) { + this.defaultRecipient = defaultRecipient; + } + public String getCronExpression() { return cronExpression; } diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTaskMeta.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTaskMeta.java new file mode 100644 index 0000000..37784a3 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTaskMeta.java @@ -0,0 +1,187 @@ +package org.xyzh.common.dto.crontab; + +import org.xyzh.common.dto.BaseDTO; + +/** + * @description 定时任务元数据表 + * @filename TbCrontabTaskMeta.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +public class TbCrontabTaskMeta extends BaseDTO { + + private static final long serialVersionUID = 1L; + + /** + * @description 元数据ID + */ + private String metaId; + + /** + * @description 任务名称 + */ + private String name; + + /** + * @description 任务描述 + */ + private String description; + + /** + * @description 任务分类(如:人民日报新闻爬取) + */ + private String category; + + /** + * @description Bean名称(执行器类名) + */ + private String beanName; + + /** + * @description 执行方法名 + */ + private String methodName; + + /** + * @description Python脚本路径(相对于basePath) + */ + private String scriptPath; + + /** + * @description 参数模板(JSON格式) + */ + private String paramSchema; + + /** + * @description 是否自动发布 + */ + private Boolean autoPublish; + + /** + * @description 排序号 + */ + private Integer sortOrder; + + /** + * @description 创建者 + */ + private String creator; + + /** + * @description 更新者 + */ + private String updater; + + public String getMetaId() { + return metaId; + } + + public void setMetaId(String metaId) { + this.metaId = metaId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getScriptPath() { + return scriptPath; + } + + public void setScriptPath(String scriptPath) { + this.scriptPath = scriptPath; + } + + public String getParamSchema() { + return paramSchema; + } + + public void setParamSchema(String paramSchema) { + this.paramSchema = paramSchema; + } + + public Boolean getAutoPublish() { + return autoPublish; + } + + public void setAutoPublish(Boolean autoPublish) { + this.autoPublish = autoPublish; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getUpdater() { + return updater; + } + + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String toString() { + return "TbCrontabTaskMeta{" + + "id=" + getID() + + ", metaId='" + metaId + '\'' + + ", name='" + name + '\'' + + ", category='" + category + '\'' + + ", beanName='" + beanName + '\'' + + ", methodName='" + methodName + '\'' + + ", scriptPath='" + scriptPath + '\'' + + ", paramSchema='" + paramSchema + '\'' + + ", autoPublish=" + autoPublish + + ", sortOrder=" + sortOrder + + '}'; + } +} \ No newline at end of file diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java index 8810b34..78347d4 100644 --- a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java @@ -5,21 +5,14 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.xyzh.api.crontab.CrontabService; +import org.xyzh.api.crontab.TaskMetaService; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.crontab.TbCrontabTaskMeta; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.CreateTaskRequest; import org.xyzh.common.dto.crontab.TbCrontabTask; import org.xyzh.common.dto.crontab.TbCrontabLog; -import org.xyzh.crontab.pojo.CrontabItem; - -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; - -import org.xyzh.common.utils.spring.SpringContextUtil; -import org.xyzh.crontab.config.CrontabProperties; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; /** @@ -38,157 +31,124 @@ public class CrontabController { @Autowired private CrontabService crontabService; - /** - * 获取可创建的定时任务 - * @return - */ - @GetMapping("/getEnabledCrontabList") - public ResultDomain getEnabledCrontabList(@RequestParam(required = false) String param) { - ResultDomain rd = new ResultDomain<>(); - try { - // 仅返回爬虫能力的元信息(任务模版列表),不包含调度相关内容 - CrontabProperties props = - SpringContextUtil.getBean(CrontabProperties.class); - String jString = JSON.toJSONString(props); - props = JSON.parseObject(jString, CrontabProperties.class); - props.getItems().forEach(item->item.getMethods().forEach( - method->{ - method.setClazz(null); - method.setExcuete_method(null); - })); - rd.success("ok", props.getItems()); - } catch (Exception e) { - rd.fail("获取可创建定时任务失败: " + e.getMessage()); - } - return rd; - } + @Autowired + private TaskMetaService taskMetaService; /** - * 创建定时任务 - * @param crontabItem - * @return + * 获取可创建的定时任务(从数据库获取任务元数据列表) + * @return ResultDomain */ - @PostMapping("/crontabTask") - public ResultDomain createCrontab(@RequestBody TbCrontabTask crontabItem) { - ResultDomain rd = new ResultDomain<>(); + @GetMapping("/getEnabledCrontabList") + public ResultDomain getEnabledCrontabList(@RequestParam(required = false) String param) { try { - // 根据前端传入的taskGroup和methodName(都是中文显示名)查找配置 - if (crontabItem.getTaskGroup() == null || crontabItem.getTaskGroup().isEmpty()) { - rd.fail("任务分组不能为空"); - return rd; - } - if (crontabItem.getMethodName() == null || crontabItem.getMethodName().isEmpty()) { - rd.fail("方法名称不能为空"); - return rd; - } - - // 根据taskGroup和methodName查找配置 - CrontabItem.CrontabMethod method = findMethodByTaskGroupAndMethodName( - crontabItem.getTaskGroup(), - crontabItem.getMethodName() - ); - - if (method != null) { - // 填充beanName和实际的Java方法名 - crontabItem.setBeanName(method.getClazz()); - crontabItem.setMethodName(method.getExcuete_method()); - - // 将scriptPath添加到methodParams中 - JSONObject methodParams = JSON.parseObject(crontabItem.getMethodParams()); - methodParams.put("scriptPath", method.getPath()); - crontabItem.setMethodParams(methodParams.toJSONString()); - - logger.info("创建任务 - taskGroup: {}, methodName: {}, beanName: {}, excuete_method: {}, scriptPath: {}", - crontabItem.getTaskGroup(), method.getName(), method.getClazz(), - method.getExcuete_method(), method.getPath()); - } else { - rd.fail("未找到对应的配置: taskGroup=" + crontabItem.getTaskGroup() - + ", methodName=" + crontabItem.getMethodName()); - return rd; - } - - return crontabService.createTask(crontabItem); + // 从数据库查询所有任务元数据 + ResultDomain result = taskMetaService.getAllTaskMeta(); + result.getDataList().forEach(item->{ + item.setBeanName(""); + item.setMethodName(""); + item.setScriptPath(""); + }); + return result; } catch (Exception e) { - logger.error("创建定时任务失败", e); - rd.fail("创建定时任务失败: " + e.getMessage()); + ResultDomain rd = new ResultDomain<>(); + rd.fail("获取可创建定时任务失败: " + e.getMessage()); return rd; } } /** - * 根据taskGroup和methodName查找对应的方法配置 + * 创建定时任务并绑定邮件接收人(从数据库获取元数据配置) + * @param request 创建任务请求(包含任务信息、元数据ID、是否使用默认接收人、额外接收人列表) + * @return ResultDomain */ - private CrontabItem.CrontabMethod findMethodByTaskGroupAndMethodName(String taskGroup, String methodName) { - CrontabProperties props = SpringContextUtil.getBean(CrontabProperties.class); - if (props == null || props.getItems() == null) { - return null; - } - - for (CrontabItem item : props.getItems()) { - if (item.getName().equals(taskGroup)) { - for (CrontabItem.CrontabMethod method : item.getMethods()) { - if (method.getName().equals(methodName)) { - return method; - } - } + @PostMapping("/crontabTask") + public ResultDomain createCrontab(@RequestBody CreateTaskRequest request) { + ResultDomain rd = new ResultDomain<>(); + try { + TbCrontabTask crontabItem = request.getTask(); + String metaId = request.getMetaId(); + + // 验证元数据ID + if (metaId == null || metaId.isEmpty()) { + rd.fail("任务元数据ID不能为空"); + return rd; } + + // 从数据库查询任务元数据 + ResultDomain metaResult = taskMetaService.getTaskMetaById(metaId); + if (!metaResult.isSuccess() || metaResult.getData() == null) { + rd.fail("未找到对应的任务元数据: metaId=" + metaId); + return rd; + } + + TbCrontabTaskMeta taskMeta = metaResult.getData(); + + // 填充任务信息 + crontabItem.setTaskGroup(taskMeta.getCategory()); // 任务分组使用category + crontabItem.setBeanName(taskMeta.getBeanName()); + crontabItem.setMethodName(taskMeta.getMethodName()); + crontabItem.setMetaId(metaId); // 保存metaId,执行时从数据库读取scriptPath + + logger.info("创建任务并绑定接收人 - metaId: {}, name: {}, category: {}, defaultRecipient: {}, additionalCount: {}", + metaId, taskMeta.getName(), taskMeta.getCategory(), + crontabItem.getDefaultRecipient(), + request.getAdditionalRecipients() != null ? request.getAdditionalRecipients().size() : 0); + + return crontabService.createTaskWithRecipients(request); + } catch (Exception e) { + logger.error("创建定时任务并绑定接收人失败", e); + rd.fail("创建定时任务并绑定接收人失败: " + e.getMessage()); + return rd; } - return null; } /** - * 更新定时任务 - * @param crontabItem - * @return + * 更新定时任务并更新邮件接收人(从数据库获取元数据配置) + * @param request 更新任务请求(包含任务信息、元数据ID、是否使用默认接收人、额外接收人列表) + * @return ResultDomain */ @PutMapping("/crontabTask") - public ResultDomain updateCrontab(@RequestBody TbCrontabTask crontabItem) { + public ResultDomain updateCrontab(@RequestBody CreateTaskRequest request) { ResultDomain rd = new ResultDomain<>(); try { + TbCrontabTask crontabItem = request.getTask(); + String metaId = request.getMetaId(); + // 确保id字段正确传递(用于数据库更新) if (crontabItem.getTaskId() != null && crontabItem.getID() == null) { crontabItem.setID(crontabItem.getTaskId()); } - // 根据前端传入的taskGroup和methodName(都是中文显示名)查找配置 - if (crontabItem.getTaskGroup() == null || crontabItem.getTaskGroup().isEmpty()) { - rd.fail("任务分组不能为空"); - return rd; - } - if (crontabItem.getMethodName() == null || crontabItem.getMethodName().isEmpty()) { - rd.fail("方法名称不能为空"); + // 验证元数据ID + if (metaId == null || metaId.isEmpty()) { + rd.fail("任务元数据ID不能为空"); return rd; } - // 根据taskGroup和methodName查找配置 - CrontabItem.CrontabMethod method = findMethodByTaskGroupAndMethodName( - crontabItem.getTaskGroup(), - crontabItem.getMethodName() - ); - - if (method != null) { - // 填充beanName和实际的Java方法名 - crontabItem.setBeanName(method.getClazz()); - crontabItem.setMethodName(method.getExcuete_method()); - - // 将scriptPath添加到methodParams中 - JSONObject methodParams = JSON.parseObject(crontabItem.getMethodParams()); - methodParams.put("scriptPath", method.getPath()); - crontabItem.setMethodParams(methodParams.toJSONString()); - - logger.info("更新任务 - id: {}, taskGroup: {}, methodName: {}, beanName: {}, excuete_method: {}, scriptPath: {}", - crontabItem.getID(), crontabItem.getTaskGroup(), method.getName(), method.getClazz(), - method.getExcuete_method(), method.getPath()); - } else { - rd.fail("未找到对应的配置: taskGroup=" + crontabItem.getTaskGroup() - + ", methodName=" + crontabItem.getMethodName()); + // 从数据库查询任务元数据 + ResultDomain metaResult = taskMetaService.getTaskMetaById(metaId); + if (!metaResult.isSuccess() || metaResult.getData() == null) { + rd.fail("未找到对应的任务元数据: metaId=" + metaId); return rd; } - return crontabService.updateTask(crontabItem); + TbCrontabTaskMeta taskMeta = metaResult.getData(); + + // 填充任务信息 + crontabItem.setTaskGroup(taskMeta.getCategory()); + crontabItem.setBeanName(taskMeta.getBeanName()); + crontabItem.setMethodName(taskMeta.getMethodName()); + crontabItem.setMetaId(metaId); // 保存metaId,执行时从数据库读取scriptPath + + logger.info("更新任务 - id: {}, metaId: {}, name: {}, category: {}, defaultRecipient: {}", + crontabItem.getID(), metaId, taskMeta.getName(), taskMeta.getCategory(), + crontabItem.getDefaultRecipient()); + + // 调用带接收人更新的方法 + return crontabService.updateTaskWithRecipients(request); } catch (Exception e) { - logger.error("更新定时任务失败", e); - rd.fail("更新定时任务失败: " + e.getMessage()); + logger.error("更新定时任务并更新接收人失败", e); + rd.fail("更新定时任务并更新接收人失败: " + e.getMessage()); return rd; } } diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/EmailDefaultController.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/EmailDefaultController.java new file mode 100644 index 0000000..eafce79 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/EmailDefaultController.java @@ -0,0 +1,76 @@ +package org.xyzh.crontab.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.xyzh.api.crontab.EmailDefaultService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.crontab.TbCrontabEmailDefault; + +/** + * @description 定时任务邮件通知默认接收人控制器 + * @filename EmailDefaultController.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@RestController +@RequestMapping("/crontab/email/default") +public class EmailDefaultController { + + private static final Logger logger = LoggerFactory.getLogger(EmailDefaultController.class); + + @Autowired + private EmailDefaultService emailDefaultService; + + /** + * @description 创建默认接收人 + * @param emailDefault 默认接收人 + * @return ResultDomain + */ + @PostMapping + public ResultDomain createDefault(@RequestBody TbCrontabEmailDefault emailDefault) { + return emailDefaultService.createDefault(emailDefault); + } + + /** + * @description 更新默认接收人 + * @param emailDefault 默认接收人 + * @return ResultDomain + */ + @PutMapping + public ResultDomain updateDefault(@RequestBody TbCrontabEmailDefault emailDefault) { + return emailDefaultService.updateDefault(emailDefault); + } + + /** + * @description 删除默认接收人 + * @param defaultId 默认ID + * @return ResultDomain + */ + @DeleteMapping("/{defaultId}") + public ResultDomain deleteDefault(@PathVariable String defaultId) { + return emailDefaultService.deleteDefault(defaultId); + } + + /** + * @description 根据defaultId查询 + * @param defaultId 默认ID + * @return ResultDomain + */ + @GetMapping("/{defaultId}") + public ResultDomain getDefaultById(@PathVariable String defaultId) { + return emailDefaultService.getDefaultById(defaultId); + } + + /** + * @description 根据metaId查询 + * @param metaId 元数据ID + * @return ResultDomain + */ + @GetMapping("/meta/{metaId}") + public ResultDomain getDefaultByMetaId(@PathVariable String metaId) { + return emailDefaultService.getDefaultByMetaId(metaId); + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/EmailRecipientController.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/EmailRecipientController.java new file mode 100644 index 0000000..0bd7364 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/EmailRecipientController.java @@ -0,0 +1,109 @@ +package org.xyzh.crontab.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.xyzh.api.crontab.EmailRecipientService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient; + +import java.util.List; + +/** + * @description 定时任务邮件接收人控制器 + * @filename EmailRecipientController.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@RestController +@RequestMapping("/crontab/email/recipient") +public class EmailRecipientController { + + private static final Logger logger = LoggerFactory.getLogger(EmailRecipientController.class); + + @Autowired + private EmailRecipientService emailRecipientService; + + /** + * @description 创建邮件接收人 + * @param recipient 邮件接收人 + * @return ResultDomain + */ + @PostMapping + public ResultDomain createRecipient(@RequestBody TbCrontabEmailRecipient recipient) { + return emailRecipientService.createRecipient(recipient); + } + + /** + * @description 批量创建邮件接收人 + * @param recipients 邮件接收人列表 + * @return ResultDomain + */ + @PostMapping("/batch") + public ResultDomain batchCreateRecipient(@RequestBody List recipients) { + return emailRecipientService.batchCreateRecipient(recipients); + } + + /** + * @description 更新邮件接收人 + * @param recipient 邮件接收人 + * @return ResultDomain + */ + @PutMapping + public ResultDomain updateRecipient(@RequestBody TbCrontabEmailRecipient recipient) { + return emailRecipientService.updateRecipient(recipient); + } + + /** + * @description 删除邮件接收人 + * @param recipientId 接收人ID + * @return ResultDomain + */ + @DeleteMapping("/{recipientId}") + public ResultDomain deleteRecipient(@PathVariable String recipientId) { + return emailRecipientService.deleteRecipient(recipientId); + } + + /** + * @description 根据ID查询 + * @param recipientId 接收人ID + * @return ResultDomain + */ + @GetMapping("/{recipientId}") + public ResultDomain getRecipientById(@PathVariable String recipientId) { + return emailRecipientService.getRecipientById(recipientId); + } + + /** + * @description 根据任务ID查询接收人列表 + * @param taskId 任务ID + * @return ResultDomain + */ + @GetMapping("/task/{taskId}") + public ResultDomain getRecipientsByTaskId(@PathVariable String taskId) { + return emailRecipientService.getRecipientsByTaskId(taskId); + } + + /** + * @description 分页查询邮件接收人 + * @param pageRequest 分页请求 + * @return ResultDomain + */ + @PostMapping("/page") + public ResultDomain getRecipientPage(@RequestBody PageRequest pageRequest) { + return emailRecipientService.getRecipientPage(pageRequest); + } + + /** + * @description 删除任务的所有接收人 + * @param taskId 任务ID + * @return ResultDomain + */ + @DeleteMapping("/task/{taskId}") + public ResultDomain deleteRecipientsByTaskId(@PathVariable String taskId) { + return emailRecipientService.deleteRecipientsByTaskId(taskId); + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/TaskMetaController.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/TaskMetaController.java new file mode 100644 index 0000000..5f4be33 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/TaskMetaController.java @@ -0,0 +1,96 @@ +package org.xyzh.crontab.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.xyzh.api.crontab.TaskMetaService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabTaskMeta; + +/** + * @description 定时任务元数据控制器 + * @filename TaskMetaController.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@RestController +@RequestMapping("/crontab/meta") +public class TaskMetaController { + + private static final Logger logger = LoggerFactory.getLogger(TaskMetaController.class); + + @Autowired + private TaskMetaService taskMetaService; + + /** + * @description 创建任务元数据 + * @param taskMeta 任务元数据 + * @return ResultDomain + */ + @PostMapping + public ResultDomain createTaskMeta(@RequestBody TbCrontabTaskMeta taskMeta) { + return taskMetaService.createTaskMeta(taskMeta); + } + + /** + * @description 更新任务元数据 + * @param taskMeta 任务元数据 + * @return ResultDomain + */ + @PutMapping + public ResultDomain updateTaskMeta(@RequestBody TbCrontabTaskMeta taskMeta) { + return taskMetaService.updateTaskMeta(taskMeta); + } + + /** + * @description 删除任务元数据 + * @param metaId 元数据ID + * @return ResultDomain + */ + @DeleteMapping("/{metaId}") + public ResultDomain deleteTaskMeta(@PathVariable String metaId) { + return taskMetaService.deleteTaskMeta(metaId); + } + + /** + * @description 根据ID查询任务元数据 + * @param metaId 元数据ID + * @return ResultDomain + */ + @GetMapping("/{metaId}") + public ResultDomain getTaskMetaById(@PathVariable String metaId) { + return taskMetaService.getTaskMetaById(metaId); + } + + /** + * @description 查询所有任务元数据 + * @return ResultDomain + */ + @GetMapping("/all") + public ResultDomain getAllTaskMeta() { + return taskMetaService.getAllTaskMeta(); + } + + /** + * @description 根据分类查询任务元数据 + * @param category 分类 + * @return ResultDomain + */ + @GetMapping("/category/{category}") + public ResultDomain getTaskMetaByCategory(@PathVariable String category) { + return taskMetaService.getTaskMetaByCategory(category); + } + + /** + * @description 分页查询任务元数据 + * @param pageRequest 分页请求 + * @return ResultDomain + */ + @PostMapping("/page") + public ResultDomain getTaskMetaPage(@RequestBody PageRequest pageRequest) { + return taskMetaService.getTaskMetaPage(pageRequest); + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/EmailDefaultMapper.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/EmailDefaultMapper.java new file mode 100644 index 0000000..f3d85dc --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/EmailDefaultMapper.java @@ -0,0 +1,45 @@ +package org.xyzh.crontab.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.dto.crontab.TbCrontabEmailDefault; + +/** + * @description 定时任务邮件通知默认接收人访问层 + * @filename EmailDefaultMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@Mapper +public interface EmailDefaultMapper extends BaseMapper { + + /** + * @description 插入默认接收人 + */ + int insertDefault(@Param("emailDefault") TbCrontabEmailDefault emailDefault); + + /** + * @description 更新默认接收人 + */ + int updateDefault(@Param("emailDefault") TbCrontabEmailDefault emailDefault); + + /** + * @description 删除默认接收人(逻辑删除) + */ + int deleteDefault(@Param("defaultId") String defaultId); + + /** + * @description 根据defaultId查询 + */ + TbCrontabEmailDefault selectDefaultById(@Param("defaultId") String defaultId); + + /** + * @description 根据metaId查询 + */ + List selectDefaultByMetaId(@Param("metaId") String metaId); +} \ No newline at end of file diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/EmailRecipientMapper.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/EmailRecipientMapper.java new file mode 100644 index 0000000..01e562a --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/EmailRecipientMapper.java @@ -0,0 +1,66 @@ +package org.xyzh.crontab.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient; + +import java.util.List; + +/** + * @description 定时任务邮件接收人访问层 + * @filename EmailRecipientMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@Mapper +public interface EmailRecipientMapper extends BaseMapper { + + /** + * @description 插入邮件接收人 + */ + int insertRecipient(@Param("recipient") TbCrontabEmailRecipient recipient); + + /** + * @description 批量插入邮件接收人 + */ + int batchInsertRecipient(@Param("recipients") List recipients); + + /** + * @description 更新邮件接收人 + */ + int updateRecipient(@Param("recipient") TbCrontabEmailRecipient recipient); + + /** + * @description 删除邮件接收人(逻辑删除) + */ + int deleteRecipient(@Param("recipientId") String recipientId); + + /** + * @description 根据接收人ID查询 + */ + TbCrontabEmailRecipient selectRecipientById(@Param("recipientId") String recipientId); + + /** + * @description 根据任务ID查询接收人列表 + */ + List selectRecipientsByTaskId(@Param("taskId") String taskId); + + + /** + * @description 分页查询邮件接收人 + */ + List selectRecipientPage(@Param("filter") TbCrontabEmailRecipient filter, @Param("pageParam") PageParam pageParam); + + /** + * @description 查询邮件接收人总数 + */ + int countSelectRecipient(@Param("filter") TbCrontabEmailRecipient filter); + + /** + * @description 删除任务的所有接收人 + */ + int deleteRecipientsByTaskId(@Param("taskId") String taskId); +} \ No newline at end of file diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/TaskMetaMapper.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/TaskMetaMapper.java new file mode 100644 index 0000000..4f5effc --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/TaskMetaMapper.java @@ -0,0 +1,65 @@ +package org.xyzh.crontab.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.TbCrontabTaskMeta; + +import java.util.List; + +/** + * @description 定时任务元数据访问层 + * @filename TaskMetaMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@Mapper +public interface TaskMetaMapper extends BaseMapper { + + /** + * @description 插入任务元数据 + */ + int insertTaskMeta(@Param("taskMeta") TbCrontabTaskMeta taskMeta); + + /** + * @description 更新任务元数据 + */ + int updateTaskMeta(@Param("taskMeta") TbCrontabTaskMeta taskMeta); + + /** + * @description 删除任务元数据(逻辑删除) + */ + int deleteTaskMeta(@Param("metaId") String metaId); + + /** + * @description 根据元数据ID查询 + */ + TbCrontabTaskMeta selectTaskMetaById(@Param("metaId") String metaId); + + /** + * @description 根据任务ID查询 + */ + TbCrontabTaskMeta selectTaskMetaByTaskId(@Param("taskId") String taskId); + + /** + * @description 查询所有任务元数据 + */ + List selectAllTaskMeta(); + + /** + * @description 根据分类查询任务元数据 + */ + List selectTaskMetaByCategory(@Param("category") String category); + + /** + * @description 分页查询任务元数据 + */ + List selectTaskMetaPage(@Param("filter") TbCrontabTaskMeta filter, @Param("pageParam") PageParam pageParam); + + /** + * @description 查询任务元数据总数 + */ + int countSelectTaskMeta(@Param("filter") TbCrontabTaskMeta filter); +} \ No newline at end of file diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java index 5f6c930..18a6b0e 100644 --- a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java @@ -157,9 +157,12 @@ public class TaskExecutor { Map params = JSON.parseObject(methodParams, new TypeReference>(){}); - // 注入taskId和logId + // 注入taskId、logId和metaId params.put("taskId", task.getTaskId()); params.put("logId", log.getID()); + if (task.getMetaId() != null) { + params.put("metaId", task.getMetaId()); + } taskParams.setParams(params); diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java index 3f41b7e..7146473 100644 --- a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java @@ -7,9 +7,14 @@ import org.springframework.scheduling.support.CronExpression; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.xyzh.api.crontab.CrontabService; +import org.xyzh.api.crontab.EmailDefaultService; +import org.xyzh.api.crontab.EmailRecipientService; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.page.PageDomain; import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.CreateTaskRequest; +import org.xyzh.common.dto.crontab.TbCrontabEmailDefault; +import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient; import org.xyzh.common.dto.crontab.TbCrontabTask; import org.xyzh.common.dto.crontab.TbCrontabLog; import org.xyzh.common.utils.IDUtils; @@ -50,6 +55,12 @@ public class CrontabServiceImpl implements CrontabService { @Autowired private ResourcePermissionService resourcePermissionService; + @Autowired + private EmailDefaultService emailDefaultService; + + @Autowired + private EmailRecipientService emailRecipientService; + // ----------------定时任务管理-------------------------------- @Override @@ -115,6 +126,101 @@ public class CrontabServiceImpl implements CrontabService { return resultDomain; } + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain createTaskWithRecipients(CreateTaskRequest request) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + TbCrontabTask task = request.getTask(); + + // 1. 创建任务 + ResultDomain createResult = createTask(task); + if (!createResult.isSuccess()) { + return createResult; + } + + String taskId = task.getTaskId(); + String metaId = request.getMetaId(); + + + // 3. 添加额外的接收人 + if (request.getAdditionalRecipients() != null && !request.getAdditionalRecipients().isEmpty()) { + try { + for (CreateTaskRequest.RecipientUserInfo userInfo : request.getAdditionalRecipients()) { + TbCrontabEmailRecipient recipient = new TbCrontabEmailRecipient(); + recipient.setTaskId(taskId); + recipient.setUserId(userInfo.getUserId()); + recipient.setEmail(userInfo.getUserEmail()); + recipient.setName(userInfo.getUsername()); + recipient.setCreator(task.getCreator()); + + emailRecipientService.createRecipient(recipient); + } + logger.info("为任务{}添加了{}个额外接收人", taskId, request.getAdditionalRecipients().size()); + } catch (Exception e) { + logger.error("添加额外接收人异常,但不影响任务创建: {}", e.getMessage(), e); + } + } + + resultDomain.success("创建任务并绑定接收人成功", task); + } catch (Exception e) { + logger.error("创建任务并绑定接收人异常: ", e); + resultDomain.fail("创建任务并绑定接收人异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain updateTaskWithRecipients(CreateTaskRequest request) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + TbCrontabTask task = request.getTask(); + + // 1. 更新任务 + ResultDomain updateResult = updateTask(task); + if (!updateResult.isSuccess()) { + return updateResult; + } + + String taskId = task.getTaskId(); + String metaId = request.getMetaId(); + + // 2. 先删除该任务的旧接收人 + try { + emailRecipientService.deleteRecipientsByTaskId(taskId); + logger.info("已删除任务{}的旧接收人配置", taskId); + } catch (Exception e) { + logger.error("删除旧接收人异常: {}", e.getMessage(), e); + } + + // 4. 添加额外的接收人 + if (request.getAdditionalRecipients() != null && !request.getAdditionalRecipients().isEmpty()) { + try { + for (CreateTaskRequest.RecipientUserInfo userInfo : request.getAdditionalRecipients()) { + TbCrontabEmailRecipient recipient = new TbCrontabEmailRecipient(); + recipient.setTaskId(taskId); + recipient.setUserId(userInfo.getUserId()); + recipient.setEmail(userInfo.getUserEmail()); + recipient.setName(userInfo.getUsername()); + recipient.setUpdater(task.getUpdater()); + + emailRecipientService.createRecipient(recipient); + } + logger.info("为任务{}添加了{}个额外接收人", taskId, request.getAdditionalRecipients().size()); + } catch (Exception e) { + logger.error("添加额外接收人异常,但不影响任务更新: {}", e.getMessage(), e); + } + } + + resultDomain.success("更新任务并更新接收人成功", task); + } catch (Exception e) { + logger.error("更新任务并更新接收人异常: ", e); + resultDomain.fail("更新任务并更新接收人异常: " + e.getMessage()); + } + return resultDomain; + } + @Override @Transactional(rollbackFor = Exception.class) public ResultDomain updateTask(TbCrontabTask task) { diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/EmailDefaultServiceImpl.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/EmailDefaultServiceImpl.java new file mode 100644 index 0000000..4267581 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/EmailDefaultServiceImpl.java @@ -0,0 +1,129 @@ +package org.xyzh.crontab.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.xyzh.api.crontab.EmailDefaultService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.crontab.TbCrontabEmailDefault; +import org.xyzh.common.utils.IDUtils; +import org.xyzh.crontab.mapper.EmailDefaultMapper; + +import java.util.Date; +import java.util.List; + +/** + * @description 定时任务邮件通知默认接收人服务实现类 + * @filename EmailDefaultServiceImpl.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@Service +public class EmailDefaultServiceImpl implements EmailDefaultService { + + private static final Logger logger = LoggerFactory.getLogger(EmailDefaultServiceImpl.class); + + @Autowired + private EmailDefaultMapper emailDefaultMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain createDefault(TbCrontabEmailDefault emailDefault) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + + // 生成ID + emailDefault.setID(IDUtils.generateID()); + if (emailDefault.getDefaultId() == null || emailDefault.getDefaultId().isEmpty()) { + emailDefault.setDefaultId(IDUtils.generateID()); + } + emailDefault.setCreateTime(new Date()); + emailDefault.setDeleted(false); + + int count = emailDefaultMapper.insertDefault(emailDefault); + if (count > 0) { + resultDomain.success("创建默认接收人成功", emailDefault); + } else { + resultDomain.fail("创建默认接收人失败"); + } + } catch (Exception e) { + logger.error("创建默认接收人异常: {}", e.getMessage(), e); + resultDomain.fail("创建默认接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain updateDefault(TbCrontabEmailDefault emailDefault) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + emailDefault.setUpdateTime(new Date()); + int count = emailDefaultMapper.updateDefault(emailDefault); + if (count > 0) { + resultDomain.success("更新默认接收人成功", emailDefault); + } else { + resultDomain.fail("更新默认接收人失败"); + } + } catch (Exception e) { + logger.error("更新默认接收人异常: {}", e.getMessage(), e); + resultDomain.fail("更新默认接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain deleteDefault(String defaultId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + int count = emailDefaultMapper.deleteDefault(defaultId); + if (count > 0) { + resultDomain.success("删除默认接收人成功", true); + } else { + resultDomain.fail("删除默认接收人失败"); + } + } catch (Exception e) { + logger.error("删除默认接收人异常: {}", e.getMessage(), e); + resultDomain.fail("删除默认接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getDefaultById(String defaultId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + TbCrontabEmailDefault emailDefault = emailDefaultMapper.selectDefaultById(defaultId); + if (emailDefault != null) { + resultDomain.success("查询默认接收人成功", emailDefault); + } else { + resultDomain.fail("默认接收人不存在"); + } + } catch (Exception e) { + logger.error("查询默认接收人异常: {}", e.getMessage(), e); + resultDomain.fail("查询默认接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getDefaultByMetaId(String metaId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + List emailDefault = emailDefaultMapper.selectDefaultByMetaId(metaId); + if (emailDefault != null) { + resultDomain.success("查询默认接收人成功", emailDefault); + } else { + resultDomain.fail("该任务元数据未配置默认接收人"); + } + } catch (Exception e) { + logger.error("查询默认接收人异常: {}", e.getMessage(), e); + resultDomain.fail("查询默认接收人失败: " + e.getMessage()); + } + return resultDomain; + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/EmailRecipientServiceImpl.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/EmailRecipientServiceImpl.java new file mode 100644 index 0000000..b45eadf --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/EmailRecipientServiceImpl.java @@ -0,0 +1,190 @@ +package org.xyzh.crontab.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.xyzh.api.crontab.EmailRecipientService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageDomain; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient; +import org.xyzh.common.utils.IDUtils; +import org.xyzh.crontab.mapper.EmailRecipientMapper; + +import java.util.Date; +import java.util.List; + +/** + * @description 定时任务邮件接收人服务实现类 + * @filename EmailRecipientServiceImpl.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@Service +public class EmailRecipientServiceImpl implements EmailRecipientService { + + private static final Logger logger = LoggerFactory.getLogger(EmailRecipientServiceImpl.class); + + @Autowired + private EmailRecipientMapper emailRecipientMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain createRecipient(TbCrontabEmailRecipient recipient) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + // 生成ID + recipient.setID(IDUtils.generateID()); + if (recipient.getRecipientId() == null || recipient.getRecipientId().isEmpty()) { + recipient.setRecipientId(IDUtils.generateID()); + } + recipient.setCreateTime(new Date()); + recipient.setDeleted(false); + + int count = emailRecipientMapper.insertRecipient(recipient); + if (count > 0) { + resultDomain.success("创建邮件接收人成功", recipient); + } else { + resultDomain.fail("创建邮件接收人失败"); + } + } catch (Exception e) { + logger.error("创建邮件接收人异常: {}", e.getMessage(), e); + resultDomain.fail("创建邮件接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain batchCreateRecipient(List recipients) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + for (TbCrontabEmailRecipient recipient : recipients) { + recipient.setID(IDUtils.generateID()); + if (recipient.getRecipientId() == null || recipient.getRecipientId().isEmpty()) { + recipient.setRecipientId(IDUtils.generateID()); + } + recipient.setCreateTime(new Date()); + recipient.setDeleted(false); + } + + int count = emailRecipientMapper.batchInsertRecipient(recipients); + if (count > 0) { + resultDomain.setSuccess(true); + resultDomain.setMessage("批量创建邮件接收人成功"); + } else { + resultDomain.fail("批量创建邮件接收人失败"); + } + } catch (Exception e) { + logger.error("批量创建邮件接收人异常: {}", e.getMessage(), e); + resultDomain.fail("批量创建邮件接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain updateRecipient(TbCrontabEmailRecipient recipient) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + recipient.setUpdateTime(new Date()); + int count = emailRecipientMapper.updateRecipient(recipient); + if (count > 0) { + resultDomain.success("更新邮件接收人成功", recipient); + } else { + resultDomain.fail("更新邮件接收人失败"); + } + } catch (Exception e) { + logger.error("更新邮件接收人异常: {}", e.getMessage(), e); + resultDomain.fail("更新邮件接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain deleteRecipient(String recipientId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + int count = emailRecipientMapper.deleteRecipient(recipientId); + if (count > 0) { + resultDomain.success("删除邮件接收人成功", true); + } else { + resultDomain.fail("删除邮件接收人失败"); + } + } catch (Exception e) { + logger.error("删除邮件接收人异常: {}", e.getMessage(), e); + resultDomain.fail("删除邮件接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getRecipientById(String recipientId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + TbCrontabEmailRecipient recipient = emailRecipientMapper.selectRecipientById(recipientId); + if (recipient != null) { + resultDomain.success("查询邮件接收人成功", recipient); + } else { + resultDomain.fail("邮件接收人不存在"); + } + } catch (Exception e) { + logger.error("查询邮件接收人异常: {}", e.getMessage(), e); + resultDomain.fail("查询邮件接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getRecipientsByTaskId(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + List list = emailRecipientMapper.selectRecipientsByTaskId(taskId); + resultDomain.success("查询邮件接收人列表成功", list); + } catch (Exception e) { + logger.error("查询邮件接收人列表异常: {}", e.getMessage(), e); + resultDomain.fail("查询邮件接收人列表失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getRecipientPage(PageRequest request) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + PageParam pageParam = request.getPageParam(); + List list = emailRecipientMapper.selectRecipientPage(request.getFilter(), pageParam); + int total = emailRecipientMapper.countSelectRecipient(request.getFilter()); + + PageDomain pageDomain = new PageDomain<>(); + pageDomain.setPageParam(pageParam); + pageParam.setTotalElements(total); + pageDomain.setDataList(list); + + resultDomain.success("分页查询邮件接收人成功", pageDomain); + } catch (Exception e) { + logger.error("分页查询邮件接收人异常: {}", e.getMessage(), e); + resultDomain.fail("分页查询邮件接收人失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain deleteRecipientsByTaskId(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + int count = emailRecipientMapper.deleteRecipientsByTaskId(taskId); + resultDomain.success("删除任务的所有接收人成功,共" + count + "条", true); + } catch (Exception e) { + logger.error("删除任务的所有接收人异常: {}", e.getMessage(), e); + resultDomain.fail("删除任务的所有接收人失败: " + e.getMessage()); + } + return resultDomain; + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/TaskMetaServiceImpl.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/TaskMetaServiceImpl.java new file mode 100644 index 0000000..6fd5c30 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/TaskMetaServiceImpl.java @@ -0,0 +1,187 @@ +package org.xyzh.crontab.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.xyzh.api.crontab.TaskMetaService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageDomain; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabTaskMeta; +import org.xyzh.common.utils.IDUtils; +import org.xyzh.crontab.mapper.TaskMetaMapper; + +import java.util.Date; +import java.util.List; + +/** + * @description 定时任务元数据服务实现类 + * @filename TaskMetaServiceImpl.java + * @author yslg + * @copyright xyzh + * @since 2025-11-18 + */ +@Service +public class TaskMetaServiceImpl implements TaskMetaService { + + private static final Logger logger = LoggerFactory.getLogger(TaskMetaServiceImpl.class); + + @Autowired + private TaskMetaMapper taskMetaMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain createTaskMeta(TbCrontabTaskMeta taskMeta) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + // 检查meta_id是否已存在 + TbCrontabTaskMeta existing = taskMetaMapper.selectTaskMetaById(taskMeta.getMetaId()); + if (existing != null) { + resultDomain.fail("任务元数据ID已存在: " + taskMeta.getMetaId()); + return resultDomain; + } + + // 生成ID + taskMeta.setID(IDUtils.generateID()); + taskMeta.setCreateTime(new Date()); + taskMeta.setDeleted(false); + + // 默认值 + if (taskMeta.getSortOrder() == null) { + taskMeta.setSortOrder(0); + } + + int count = taskMetaMapper.insertTaskMeta(taskMeta); + if (count > 0) { + resultDomain.success("创建任务元数据成功", taskMeta); + } else { + resultDomain.fail("创建任务元数据失败"); + } + } catch (Exception e) { + logger.error("创建任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("创建任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain updateTaskMeta(TbCrontabTaskMeta taskMeta) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + taskMeta.setUpdateTime(new Date()); + int count = taskMetaMapper.updateTaskMeta(taskMeta); + if (count > 0) { + resultDomain.success("更新任务元数据成功", taskMeta); + } else { + resultDomain.fail("更新任务元数据失败"); + } + } catch (Exception e) { + logger.error("更新任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("更新任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain deleteTaskMeta(String metaId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + int count = taskMetaMapper.deleteTaskMeta(metaId); + if (count > 0) { + resultDomain.success("删除任务元数据成功", true); + } else { + resultDomain.fail("删除任务元数据失败"); + } + } catch (Exception e) { + logger.error("删除任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("删除任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskMetaById(String metaId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + TbCrontabTaskMeta taskMeta = taskMetaMapper.selectTaskMetaById(metaId); + if (taskMeta != null) { + resultDomain.success("查询任务元数据成功", taskMeta); + } else { + resultDomain.fail("任务元数据不存在"); + } + } catch (Exception e) { + logger.error("查询任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("查询任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskMetaByTaskId(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + TbCrontabTaskMeta taskMeta = taskMetaMapper.selectTaskMetaByTaskId(taskId); + if (taskMeta != null) { + resultDomain.success("查询任务元数据成功", taskMeta); + } else { + resultDomain.fail("任务元数据不存在"); + } + } catch (Exception e) { + logger.error("查询任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("查询任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getAllTaskMeta() { + ResultDomain resultDomain = new ResultDomain<>(); + try { + List list = taskMetaMapper.selectAllTaskMeta(); + resultDomain.success("查询所有任务元数据成功", list); + } catch (Exception e) { + logger.error("查询所有任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("查询所有任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskMetaByCategory(String category) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + List list = taskMetaMapper.selectTaskMetaByCategory(category); + resultDomain.success("查询分类任务元数据成功", list); + } catch (Exception e) { + logger.error("查询分类任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("查询分类任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskMetaPage(PageRequest request) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + PageParam pageParam = request.getPageParam(); + List list = taskMetaMapper.selectTaskMetaPage(request.getFilter(), pageParam); + int total = taskMetaMapper.countSelectTaskMeta(request.getFilter()); + + PageDomain pageDomain = new PageDomain<>(); + pageDomain.setPageParam(pageParam); + pageParam.setTotalElements(total); + pageDomain.setDataList(list); + + resultDomain.success("分页查询任务元数据成功", pageDomain); + } catch (Exception e) { + logger.error("分页查询任务元数据异常: {}", e.getMessage(), e); + resultDomain.fail("分页查询任务元数据失败: " + e.getMessage()); + } + return resultDomain; + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/PythonCommandTask.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/PythonCommandTask.java index ef60733..5323d29 100644 --- a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/PythonCommandTask.java +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/PythonCommandTask.java @@ -1,14 +1,14 @@ package org.xyzh.crontab.task; import org.springframework.beans.factory.annotation.Autowired; -import org.xyzh.crontab.config.CrawlerProperties; +import org.xyzh.api.system.config.SysConfigService; import org.xyzh.crontab.pojo.TaskParams; import java.util.ArrayList; import java.util.List; /** - * @description Python命令任务抽象类 + * @description Python命令任务抽象类(从数据库获取配置) * @filename PythonCommandTask.java * @author yslg * @copyright xyzh @@ -17,23 +17,36 @@ import java.util.List; public abstract class PythonCommandTask extends CommandTask { @Autowired - protected CrawlerProperties crawlerProperties; + protected SysConfigService sysConfigService; + /** - * 获取Python可执行文件路径 + * 获取Python可执行文件路径(从数据库系统配置获取) */ protected String getPythonPath() { - return crawlerProperties.getPythonPath() != null - ? crawlerProperties.getPythonPath() - : "python"; + try { + String value = sysConfigService.getStringConfig("crawler.pythonPath"); + if (value != null && !value.isEmpty()) { + return value; + } + } catch (Exception e) { + logger.warn("获取Python路径配置失败,使用默认值: {}", e.getMessage()); + } + return "python"; // 默认值 } /** - * 获取脚本基础路径 + * 获取脚本基础路径(从数据库系统配置获取) */ protected String getScriptBasePath() { - return crawlerProperties.getBasePath() != null - ? crawlerProperties.getBasePath() - : "../schoolNewsCrawler"; + try { + String value = sysConfigService.getStringConfig("crawler.basePath"); + if (value != null && !value.isEmpty()) { + return value; + } + } catch (Exception e) { + logger.warn("获取脚本基础路径配置失败,使用默认值: {}", e.getMessage()); + } + return "../schoolNewsCrawler"; // 默认值 } /** diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsCrawlerTask.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsCrawlerTask.java index 30d90ef..d9473ff 100644 --- a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsCrawlerTask.java +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsCrawlerTask.java @@ -4,11 +4,19 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.TypeReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.xyzh.api.crontab.CrontabService; import org.xyzh.api.crontab.DataCollectionItemService; +import org.xyzh.api.crontab.EmailDefaultService; +import org.xyzh.api.crontab.EmailRecipientService; +import org.xyzh.api.crontab.TaskMetaService; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.crontab.TbCrontabEmailDefault; +import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient; +import org.xyzh.common.dto.crontab.TbCrontabTask; +import org.xyzh.common.dto.crontab.TbCrontabTaskMeta; import org.xyzh.common.dto.crontab.TbDataCollectionItem; +import org.xyzh.common.utils.EmailUtils; import org.xyzh.common.utils.IDUtils; -import org.xyzh.crontab.config.CrontabProperties; import org.xyzh.crontab.pojo.TaskParams; import org.xyzh.crontab.task.PythonCommandTask; @@ -18,9 +26,9 @@ import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * @description 新闻爬虫定时任务 @@ -32,11 +40,23 @@ import java.util.Map; @Component("newsCrewerTask") public class NewsCrawlerTask extends PythonCommandTask { - @Autowired - private CrontabProperties crontabProperties; - @Autowired private DataCollectionItemService itemService; + + @Autowired + private TaskMetaService taskMetaService; + + @Autowired + private CrontabService crontabService; + + @Autowired + private EmailDefaultService emailDefaultService; + + @Autowired + private EmailRecipientService emailRecipientService; + + @Autowired + private EmailUtils emailUtils; /** * 构建Python脚本参数 @@ -45,11 +65,23 @@ public class NewsCrawlerTask extends PythonCommandTask { protected List buildPythonArgs(TaskParams taskParams) throws Exception { List args = new ArrayList<>(); - // 1. 从params获取scriptPath - String scriptPath = taskParams.getParamAsString("scriptPath"); - if (scriptPath == null || scriptPath.isEmpty()) { - throw new Exception("scriptPath参数缺失"); + // 1. 从数据库读取scriptPath(通过metaId) + String metaId = taskParams.getParamAsString("metaId"); + if (metaId == null || metaId.isEmpty()) { + throw new Exception("metaId参数缺失,无法获取scriptPath"); } + + ResultDomain metaResult = taskMetaService.getTaskMetaById(metaId); + if (!metaResult.isSuccess() || metaResult.getData() == null) { + throw new Exception("未找到任务元数据: metaId=" + metaId); + } + + String scriptPath = metaResult.getData().getScriptPath(); + if (scriptPath == null || scriptPath.isEmpty()) { + throw new Exception("任务元数据中scriptPath为空: metaId=" + metaId); + } + + logger.info("从数据库读取scriptPath: {}, metaId: {}", scriptPath, metaId); // 2. 生成输出文件名 String timestamp = String.valueOf(System.currentTimeMillis()); @@ -70,7 +102,7 @@ public class NewsCrawlerTask extends PythonCommandTask { Object value = entry.getValue(); // 跳过特殊参数 - if (key.startsWith("_") || key.equals("scriptPath") || + if (key.startsWith("_") || key.equals("metaId") || key.equals("taskId") || key.equals("logId")) { continue; } @@ -130,7 +162,12 @@ public class NewsCrawlerTask extends PythonCommandTask { // 保存新闻数据到数据库 if (taskId != null && !taskId.isEmpty() && logId != null && !logId.isEmpty()) { - saveNewsToDatabase(newsList, taskId, logId); + ResultDomain taskResult = crontabService.getTaskById(taskId); + + saveNewsToDatabase(newsList, taskResult.getData(), logId); + + // 发送邮件通知 + sendEmailNotification(taskId, taskResult.getData(), newsList); } else { logger.warn("未提供任务ID或日志ID,跳过数据保存"); } @@ -139,14 +176,20 @@ public class NewsCrawlerTask extends PythonCommandTask { /** * 将新闻数据保存到数据库 */ - private void saveNewsToDatabase(List newsList, String taskId, String logId) { + private void saveNewsToDatabase(List newsList, TbCrontabTask task, String logId) { + String taskId = task.getTaskId(); logger.info("开始保存 {} 条新闻到数据库,任务ID: {},日志ID: {}", newsList.size(), taskId, logId); try { List itemList = new ArrayList<>(); + ResultDomain metaResult = taskMetaService.getTaskMetaByTaskId(taskId); + if (!metaResult.isSuccess() || metaResult.getData() == null) { + throw new Exception("未找到任务元数据: taskId=" + taskId); + } + TbCrontabTaskMeta taskMeta = metaResult.getData(); + Date now = new Date(); SimpleDateFormat parser = new SimpleDateFormat("yyyy年MM月dd日HH:mm"); - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (ArticleStruct news : newsList) { @@ -209,9 +252,155 @@ public class NewsCrawlerTask extends PythonCommandTask { } else { logger.warn("没有有效的新闻数据需要保存"); } + if (taskMeta.getAutoPublish()){ + publishNewsToArticle(newsList, task, logId); + } } catch (Exception e) { logger.error("保存新闻数据到数据库异常: ", e); } } + + /** + * 发送邮件通知 + */ + private void sendEmailNotification(String taskId, TbCrontabTask task, List newsList) { + try { + List recipients = new ArrayList<>(); + + // 2. 如果使用默认接收人,查询默认接收人列表 + if (Boolean.TRUE.equals(task.getDefaultRecipient()) && task.getMetaId() != null) { + ResultDomain defaultResult = emailDefaultService.getDefaultByMetaId(task.getMetaId()); + if (defaultResult.isSuccess() && defaultResult.getDataList() != null) { + for (TbCrontabEmailDefault defaultRecipient : defaultResult.getDataList()) { + if (defaultRecipient.getUserEmail() != null && !defaultRecipient.getUserEmail().isEmpty()) { + recipients.add(defaultRecipient.getUserEmail()); + } + } + } + } + + // 3. 查询额外接收人 + ResultDomain recipientResult = emailRecipientService.getRecipientsByTaskId(taskId); + if (recipientResult.isSuccess() && recipientResult.getDataList() != null) { + for (TbCrontabEmailRecipient recipient : recipientResult.getDataList()) { + if (recipient.getEmail() != null && !recipient.getEmail().isEmpty()) { + recipients.add(recipient.getEmail()); + } + } + } + + // 4. 去重 + recipients = recipients.stream().distinct().collect(Collectors.toList()); + + if (recipients.isEmpty()) { + logger.info("任务 {} 没有配置接收人,跳过邮件发送", task.getTaskName()); + return; + } + + // 5. 构建邮件内容 + String subject = "【新闻爬虫通知】" + task.getTaskName() + " 执行完成"; + String content = buildEmailContent(task.getTaskName(), newsList); + + // 6. 发送邮件 + int successCount = 0; + for (String email : recipients) { + if (emailUtils.sendHtmlEmail(email, subject, content)) { + successCount++; + } + } + + logger.info("邮件发送完成,成功发送: {}/{},任务: {}", successCount, recipients.size(), task.getTaskName()); + + } catch (Exception e) { + logger.error("发送邮件通知异常,但不影响任务执行: ", e); + } + } + + /** + * 构建邮件HTML内容 + */ + private String buildEmailContent(String taskName, List newsList) { + StringBuilder html = new StringBuilder(); + html.append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("
") + .append("
") + .append("

新闻爬虫执行通知

") + .append("
") + .append("
"); + + // 摘要信息 + html.append("
") + .append("

任务名称:").append(taskName).append("

") + .append("

执行时间:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("

") + .append("

爬取数量:").append(newsList.size()).append(" 条

") + .append("
"); + + // 新闻列表 + html.append("
") + .append("

爬取内容:

"); + + int count = Math.min(newsList.size(), 10); // 最多显示10条 + for (int i = 0; i < count; i++) { + ArticleStruct news = newsList.get(i); + html.append("
") + .append("
").append(news.getTitle() != null ? news.getTitle() : "无标题").append("
") + .append("
") + .append("来源:").append(news.getSource() != null ? news.getSource() : "未知") + .append(" | ") + .append("发布时间:").append(news.getPublishTime() != null ? news.getPublishTime() : "未知"); + + if (news.getUrl() != null && !news.getUrl().isEmpty()) { + html.append(" | 查看原文"); + } + + html.append("
") + .append("
"); + } + + if (newsList.size() > 10) { + html.append("

") + .append("还有 ").append(newsList.size() - 10).append(" 条新闻未显示,请登录系统查看详情") + .append("

"); + } + + html.append("
"); // news-list + html.append("
"); // content + + // 页脚 + html.append("") + .append("
") // container + .append("") + .append(""); + + return html.toString(); + } + + // TODO 自动发布功能,把采集的数据发布到文章表 + private void publishNewsToArticle(List newsList, TbCrontabTask task, String logId) { + + } } diff --git a/schoolNewsServ/crontab/src/main/resources/application.yml b/schoolNewsServ/crontab/src/main/resources/application.yml index d8470e5..b092c85 100644 --- a/schoolNewsServ/crontab/src/main/resources/application.yml +++ b/schoolNewsServ/crontab/src/main/resources/application.yml @@ -47,5 +47,3 @@ crontab: description: 是否是昨天 type: Boolean value: true - - diff --git a/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml b/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml index a5710bd..ad3b592 100644 --- a/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml +++ b/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml @@ -11,6 +11,8 @@ + + @@ -26,7 +28,7 @@ - id, task_id, task_name, task_group, bean_name, method_name, method_params, + id, task_id, task_name, task_group, bean_name, method_name, method_params, meta_id, default_recipient, cron_expression, status, description, concurrent, misfire_policy, creator, updater, create_time, update_time, delete_time, deleted @@ -132,6 +134,8 @@ bean_name, method_name, method_params, + meta_id, + default_recipient, cron_expression, status, description, @@ -150,6 +154,8 @@ #{task.beanName}, #{task.methodName}, #{task.methodParams}, + #{task.metaId}, + #{task.defaultRecipient}, #{task.cronExpression}, #{task.status}, #{task.description}, @@ -170,6 +176,8 @@ bean_name = #{task.beanName}, method_name = #{task.methodName}, method_params = #{task.methodParams}, + meta_id = #{task.metaId}, + default_recipient = #{task.defaultRecipient}, cron_expression = #{task.cronExpression}, status = #{task.status}, description = #{task.description}, diff --git a/schoolNewsServ/crontab/src/main/resources/mapper/EmailDefaultMapper.xml b/schoolNewsServ/crontab/src/main/resources/mapper/EmailDefaultMapper.xml new file mode 100644 index 0000000..01df3fd --- /dev/null +++ b/schoolNewsServ/crontab/src/main/resources/mapper/EmailDefaultMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + id, default_id, meta_id, user_id, + creator, updater, create_time, update_time, delete_time, deleted + + + + + INSERT INTO tb_crontab_email_default ( + id, default_id, meta_id, user_id, creator, create_time + ) VALUES ( + #{emailDefault.ID}, #{emailDefault.defaultId}, #{emailDefault.metaId}, + #{emailDefault.userId}, #{emailDefault.creator}, NOW() + ) + + + + + UPDATE tb_crontab_email_default + SET meta_id = #{emailDefault.metaId}, + user_id = #{emailDefault.userId}, + updater = #{emailDefault.updater}, + update_time = NOW() + WHERE default_id = #{emailDefault.defaultId} + AND deleted = 0 + + + + + UPDATE tb_crontab_email_default + SET deleted = 1, + delete_time = NOW() + WHERE default_id = #{defaultId} + AND deleted = 0 + + + + + + + + + \ No newline at end of file diff --git a/schoolNewsServ/crontab/src/main/resources/mapper/EmailRecipientMapper.xml b/schoolNewsServ/crontab/src/main/resources/mapper/EmailRecipientMapper.xml new file mode 100644 index 0000000..909947e --- /dev/null +++ b/schoolNewsServ/crontab/src/main/resources/mapper/EmailRecipientMapper.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + id, recipient_id, task_id, user_id, email, name, + creator, updater, create_time, update_time, delete_time, deleted + + + + + + deleted = 0 + + AND recipient_id = #{filter.recipientId} + + + AND task_id = #{filter.taskId} + + + AND user_id = #{filter.userId} + + + AND email LIKE CONCAT('%', #{filter.email}, '%') + + + + + + + INSERT INTO tb_crontab_email_recipient ( + id, recipient_id, task_id, user_id, email, name, creator, create_time + ) VALUES ( + #{recipient.ID}, #{recipient.recipientId}, #{recipient.taskId}, + #{recipient.userId}, #{recipient.email}, #{recipient.name}, + #{recipient.creator}, NOW() + ) + + + + + INSERT INTO tb_crontab_email_recipient ( + id, recipient_id, task_id, user_id, email, name, creator, create_time + ) VALUES + + (#{item.ID}, #{item.recipientId}, #{item.taskId}, + #{item.userId}, #{item.email}, #{item.name}, #{item.creator}, NOW()) + + + + + + UPDATE tb_crontab_email_recipient + SET task_id = #{recipient.taskId}, + user_id = #{recipient.userId}, + email = #{recipient.email}, + name = #{recipient.name}, + updater = #{recipient.updater}, + update_time = NOW() + WHERE recipient_id = #{recipient.recipientId} + AND deleted = 0 + + + + + UPDATE tb_crontab_email_recipient + SET deleted = 1, + delete_time = NOW() + WHERE recipient_id = #{recipientId} + AND deleted = 0 + + + + + + + + + + + + + + + + + + + + + UPDATE tb_crontab_email_recipient + SET deleted = 1, + delete_time = NOW() + WHERE task_id = #{taskId} + AND deleted = 0 + + + \ No newline at end of file diff --git a/schoolNewsServ/crontab/src/main/resources/mapper/TaskMetaMapper.xml b/schoolNewsServ/crontab/src/main/resources/mapper/TaskMetaMapper.xml new file mode 100644 index 0000000..5f78d5f --- /dev/null +++ b/schoolNewsServ/crontab/src/main/resources/mapper/TaskMetaMapper.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, meta_id, name, description, category, bean_name, method_name, + script_path, param_schema, auto_publish, sort_order, + creator, updater, create_time, update_time, delete_time, deleted + + + + + + deleted = 0 + + AND meta_id = #{filter.metaId} + + + AND name LIKE CONCAT('%', #{filter.name}, '%') + + + AND category = #{filter.category} + + + + + + + INSERT INTO tb_crontab_task_meta ( + id, meta_id, name, description, category, bean_name, method_name, + script_path, param_schema, auto_publish, sort_order, creator, create_time + ) VALUES ( + #{taskMeta.ID}, #{taskMeta.metaId}, #{taskMeta.name}, #{taskMeta.description}, + #{taskMeta.category}, #{taskMeta.beanName}, #{taskMeta.methodName}, + #{taskMeta.scriptPath}, #{taskMeta.paramSchema}, #{taskMeta.autoPublish}, #{taskMeta.sortOrder}, + #{taskMeta.creator}, NOW() + ) + + + + + UPDATE tb_crontab_task_meta + SET name = #{taskMeta.name}, + description = #{taskMeta.description}, + category = #{taskMeta.category}, + bean_name = #{taskMeta.beanName}, + method_name = #{taskMeta.methodName}, + script_path = #{taskMeta.scriptPath}, + param_schema = #{taskMeta.paramSchema}, + auto_publish = #{taskMeta.autoPublish}, + sort_order = #{taskMeta.sortOrder}, + updater = #{taskMeta.updater}, + update_time = NOW() + WHERE meta_id = #{taskMeta.metaId} + AND deleted = 0 + + + + + UPDATE tb_crontab_task_meta + SET deleted = 1, + delete_time = NOW() + WHERE meta_id = #{metaId} + AND deleted = 0 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/SysConfigMapper.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/SysConfigMapper.java index 486242e..ec6e8b3 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/SysConfigMapper.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/SysConfigMapper.java @@ -2,6 +2,8 @@ package org.xyzh.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.system.TbSysConfig; import java.util.List; @@ -16,12 +18,55 @@ import java.util.List; @Mapper public interface SysConfigMapper extends BaseMapper { + /** + * @description 插入系统配置 + */ + int insertSysConfig(@Param("config") TbSysConfig config); + + /** + * @description 更新系统配置 + */ + int updateSysConfig(@Param("config") TbSysConfig config); + + /** + * @description 删除系统配置(逻辑删除) + */ + int deleteSysConfig(@Param("id") String id); + + /** + * @description 根据配置键查询 + */ + TbSysConfig selectSysConfigByKey(@Param("configKey") String configKey); + + /** + * @description 根据ID查询 + */ + TbSysConfig selectSysConfigById(@Param("id") String id); + + /** + * @description 查询所有系统配置 + */ + List selectAllSysConfig(); + + /** + * @description 根据分组查询系统配置 + */ + List selectSysConfigByGroup(@Param("configGroup") String configGroup); + /** * @description 查询系统配置列表 * @param filter 过滤条件 * @return List 系统配置列表 - * @author yslg - * @since 2025-10-15 */ - List selectSysConfigs(TbSysConfig filter); + List selectSysConfigs(@Param("filter") TbSysConfig filter); + + /** + * @description 分页查询系统配置 + */ + List selectSysConfigPage(@Param("filter") TbSysConfig filter, @Param("pageParam") PageParam pageParam); + + /** + * @description 查询系统配置总数 + */ + int countSelectSysConfig(@Param("filter") TbSysConfig filter); } diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/service/config/impl/SysConfigServiceImpl.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/service/config/impl/SysConfigServiceImpl.java index 9c5a68c..182aeac 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/service/config/impl/SysConfigServiceImpl.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/service/config/impl/SysConfigServiceImpl.java @@ -1,13 +1,208 @@ package org.xyzh.system.service.config.impl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.xyzh.api.system.config.SysConfigService; +import org.xyzh.common.dto.system.TbSysConfig; +import org.xyzh.system.mapper.SysConfigMapper; +/** + * @description 系统配置服务实现 + * @author AI Assistant + * @since 2025-11-18 + */ @Service -public class SysConfigServiceImpl implements SysConfigService{ +public class SysConfigServiceImpl implements SysConfigService { + + private static final Logger logger = LoggerFactory.getLogger(SysConfigServiceImpl.class); + + @Autowired + private SysConfigMapper sysConfigMapper; + + /** + * 根据key查询配置 + */ + private TbSysConfig getConfigByKey(String key) { + if (key == null || key.isEmpty()) { + return null; + } + return sysConfigMapper.selectSysConfigByKey(key); + } @Override - public String getSysConfig(String key) { - return null; + public Object getSysConfig(String key) { + try { + TbSysConfig config = getConfigByKey(key); + if (config == null) { + logger.warn("配置项不存在: {}", key); + return null; + } + + String configType = config.getConfigType(); + String configValue = config.getConfigValue(); + + if (configValue == null || configValue.isEmpty()) { + return null; + } + + // 根据config_type返回对应的类型 + if (configType == null || "string".equalsIgnoreCase(configType)) { + return configValue; + } else if ("number".equalsIgnoreCase(configType) || "integer".equalsIgnoreCase(configType)) { + try { + // 尝试解析为Integer,如果失败则解析为Long + return Integer.parseInt(configValue); + } catch (NumberFormatException e) { + try { + return Long.parseLong(configValue); + } catch (NumberFormatException ex) { + logger.error("配置项 {} 的值无法转换为数字: {}", key, configValue); + return configValue; + } + } + } else if ("boolean".equalsIgnoreCase(configType)) { + String value = configValue.toLowerCase().trim(); + if ("true".equals(value) || "1".equals(value) || "yes".equals(value) || "on".equals(value)) { + return true; + } else if ("false".equals(value) || "0".equals(value) || "no".equals(value) || "off".equals(value)) { + return false; + } else { + logger.warn("配置项 {} 的值无法识别为Boolean: {}", key, value); + return configValue; + } + } else if ("double".equalsIgnoreCase(configType) || "float".equalsIgnoreCase(configType)) { + try { + return Double.parseDouble(configValue); + } catch (NumberFormatException e) { + logger.error("配置项 {} 的值无法转换为Double: {}", key, configValue); + return configValue; + } + } else { + // 未知类型,直接返回字符串 + return configValue; + } + } catch (Exception e) { + logger.error("获取配置失败: {}", key, e); + return null; + } + } + + @Override + public String getStringConfig(String key) { + try { + TbSysConfig config = getConfigByKey(key); + if (config == null) { + logger.warn("配置项不存在: {}", key); + return null; + } + return config.getConfigValue(); + } catch (Exception e) { + logger.error("获取字符串配置失败: {}", key, e); + return null; + } + } + + @Override + public Integer getIntConfig(String key) { + try { + TbSysConfig config = getConfigByKey(key); + if (config == null) { + logger.warn("配置项不存在: {}", key); + return null; + } + + String value = config.getConfigValue(); + if (value == null || value.isEmpty()) { + return null; + } + + return Integer.parseInt(value); + } catch (NumberFormatException e) { + logger.error("配置项 {} 的值无法转换为Integer: {}", key, e.getMessage()); + return null; + } catch (Exception e) { + logger.error("获取Integer配置失败: {}", key, e); + return null; + } + } + + @Override + public Boolean getBooleanConfig(String key) { + try { + TbSysConfig config = getConfigByKey(key); + if (config == null) { + logger.warn("配置项不存在: {}", key); + return null; + } + + String value = config.getConfigValue(); + if (value == null || value.isEmpty()) { + return null; + } + + // 支持多种布尔值表示:true/false, 1/0, yes/no, on/off + value = value.toLowerCase().trim(); + if ("true".equals(value) || "1".equals(value) || "yes".equals(value) || "on".equals(value)) { + return true; + } else if ("false".equals(value) || "0".equals(value) || "no".equals(value) || "off".equals(value)) { + return false; + } else { + logger.warn("配置项 {} 的值无法识别为Boolean: {}", key, value); + return null; + } + } catch (Exception e) { + logger.error("获取Boolean配置失败: {}", key, e); + return null; + } + } + + @Override + public Double getDoubleConfig(String key) { + try { + TbSysConfig config = getConfigByKey(key); + if (config == null) { + logger.warn("配置项不存在: {}", key); + return null; + } + + String value = config.getConfigValue(); + if (value == null || value.isEmpty()) { + return null; + } + + return Double.parseDouble(value); + } catch (NumberFormatException e) { + logger.error("配置项 {} 的值无法转换为Double: {}", key, e.getMessage()); + return null; + } catch (Exception e) { + logger.error("获取Double配置失败: {}", key, e); + return null; + } + } + + @Override + public Long getLongConfig(String key) { + try { + TbSysConfig config = getConfigByKey(key); + if (config == null) { + logger.warn("配置项不存在: {}", key); + return null; + } + + String value = config.getConfigValue(); + if (value == null || value.isEmpty()) { + return null; + } + + return Long.parseLong(value); + } catch (NumberFormatException e) { + logger.error("配置项 {} 的值无法转换为Long: {}", key, e.getMessage()); + return null; + } catch (Exception e) { + logger.error("获取Long配置失败: {}", key, e); + return null; + } } } diff --git a/schoolNewsServ/system/src/main/resources/mapper/SysConfigMapper.xml b/schoolNewsServ/system/src/main/resources/mapper/SysConfigMapper.xml index cc93b9b..b813ffe 100644 --- a/schoolNewsServ/system/src/main/resources/mapper/SysConfigMapper.xml +++ b/schoolNewsServ/system/src/main/resources/mapper/SysConfigMapper.xml @@ -2,55 +2,138 @@ - + - - - - - - - - - - - - - + + + + + + + + + + + + + - + id, config_key, config_value, config_type, config_group, description, is_system, creator, updater, create_time, update_time, delete_time, deleted - - + + deleted = 0 - - AND config_key = #{configKey} + + AND config_key = #{filter.configKey} - - AND config_group = #{configGroup} + + AND config_group = #{filter.configGroup} - - AND config_type = #{configType} + + AND config_type = #{filter.configType} - - AND is_system = #{isSystem} + + AND is_system = #{filter.isSystem} - - + SELECT FROM tb_sys_config - + WHERE config_key = #{configKey} + AND deleted = 0 + + + + + + + + + + + + + + + + + + + diff --git a/schoolNewsWeb/src/apis/achievement/achievement.ts b/schoolNewsWeb/src/apis/achievement/achievement.ts index 37c83d0..45eefd6 100644 --- a/schoolNewsWeb/src/apis/achievement/achievement.ts +++ b/schoolNewsWeb/src/apis/achievement/achievement.ts @@ -36,10 +36,10 @@ export const achievementApi = { /** * 删除成就 * @param achievement 成就信息(包含achievementID) - * @returns Promise> + * @returns Promise> */ - async deleteAchievement(achievement: Achievement): Promise> { - const response = await api.delete('/achievements/achievement', achievement); + async deleteAchievement(achievement: Achievement): Promise> { + const response = await api.delete('/achievements/achievement', achievement); return response.data; }, @@ -138,10 +138,10 @@ export const achievementApi = { * 撤销用户成就 * @param userID 用户ID * @param achievementID 成就ID - * @returns Promise> + * @returns Promise> */ - async revokeAchievement(userID: string, achievementID: string): Promise> { - const response = await api.delete('/achievements/revoke', null, { + async revokeAchievement(userID: string, achievementID: string): Promise> { + const response = await api.delete('/achievements/revoke', null, { params: { userID, achievementID } }); return response.data; diff --git a/schoolNewsWeb/src/apis/ai/document-segment.ts b/schoolNewsWeb/src/apis/ai/document-segment.ts index d160266..f61b185 100644 --- a/schoolNewsWeb/src/apis/ai/document-segment.ts +++ b/schoolNewsWeb/src/apis/ai/document-segment.ts @@ -169,15 +169,15 @@ export const documentSegmentApi = { * @param documentId Dify文档ID * @param segmentId 分段ID * @param childChunkId 子块ID - * @returns Promise> + * @returns Promise> */ async deleteChildChunk( datasetId: string, documentId: string, segmentId: string, childChunkId: string - ): Promise> { - const response = await api.delete( + ): Promise> { + const response = await api.delete( `/ai/dify/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}` ); return response.data; diff --git a/schoolNewsWeb/src/apis/ai/file-upload.ts b/schoolNewsWeb/src/apis/ai/file-upload.ts index b6d225e..bbb0ad8 100644 --- a/schoolNewsWeb/src/apis/ai/file-upload.ts +++ b/schoolNewsWeb/src/apis/ai/file-upload.ts @@ -147,15 +147,15 @@ export const fileUploadApi = { * @param datasetId Dify数据集ID * @param documentId Dify文档ID * @param enabled 是否启用 - * @returns Promise> + * @returns Promise> */ async updateDocumentStatus( datasetId: string, documentId: string, enabled: boolean - ): Promise> { + ): Promise> { const action = enabled ? 'enable' : 'disable'; - const response = await api.post( + const response = await api.post( `/ai/dify/datasets/${datasetId}/documents/status/${action}`, { document_ids: [documentId] } ); diff --git a/schoolNewsWeb/src/apis/crontab/index.ts b/schoolNewsWeb/src/apis/crontab/index.ts index 123aab6..0058082 100644 --- a/schoolNewsWeb/src/apis/crontab/index.ts +++ b/schoolNewsWeb/src/apis/crontab/index.ts @@ -5,7 +5,18 @@ */ import { api } from '@/apis/index'; -import type { CrontabTask, CrontabLog, DataCollectionItem, CrontabItem, ResultDomain, PageParam } from '@/types'; +import type { + CrontabTask, + CrontabLog, + DataCollectionItem, + CrontabItem, + TaskMeta, + EmailDefault, + EmailRecipient, + CreateTaskRequest, + ResultDomain, + PageParam +} from '@/types'; /** * 定时任务API服务 @@ -16,11 +27,11 @@ export const crontabApi = { // ==================== 定时任务管理 ==================== /** - * 获取可创建的定时任务模板列表 - * @returns Promise> + * 获取可创建的定时任务列表(从数据库获取任务元数据) + * @returns Promise> */ - async getEnabledCrontabList(): Promise> { - const response = await api.get(`${this.baseUrl}/getEnabledCrontabList`); + async getEnabledCrontabList(): Promise> { + const response = await api.get(`${this.baseUrl}/getEnabledCrontabList`); return response.data; }, @@ -29,18 +40,18 @@ export const crontabApi = { * @param task 任务对象 * @returns Promise> */ - async createTask(task: CrontabTask): Promise> { + async createTask(task: CreateTaskRequest): Promise> { const response = await api.post(`${this.baseUrl}/crontabTask`, task); return response.data; }, /** * 更新定时任务 - * @param task 任务对象 + * @param request 更新任务请求(包含任务信息、元数据ID等) * @returns Promise> */ - async updateTask(task: CrontabTask): Promise> { - const response = await api.put(`${this.baseUrl}/crontabTask`, task); + async updateTask(request: CreateTaskRequest): Promise> { + const response = await api.put(`${this.baseUrl}/crontabTask`, request); return response.data; }, @@ -255,5 +266,254 @@ export const crontabApi = { const response = await api.put(`${this.baseUrl}/collection/item/${itemId}/status/${status}`); return response.data; }, + + + // ==================== 任务元数据管理 ==================== + + /** + * 创建任务元数据 + * @param taskMeta 任务元数据 + * @returns Promise> + */ + async createTaskMeta(taskMeta: TaskMeta): Promise> { + const response = await api.post(`${this.baseUrl}/meta`, taskMeta); + return response.data; + }, + + /** + * 更新任务元数据 + * @param taskMeta 任务元数据 + * @returns Promise> + */ + async updateTaskMeta(taskMeta: TaskMeta): Promise> { + const response = await api.put(`${this.baseUrl}/meta`, taskMeta); + return response.data; + }, + + /** + * 删除任务元数据 + * @param metaId 元数据ID + * @returns Promise> + */ + async deleteTaskMeta(metaId: string): Promise> { + const response = await api.delete(`${this.baseUrl}/meta/${metaId}`); + return response.data; + }, + + /** + * 根据ID查询任务元数据 + * @param metaId 元数据ID + * @returns Promise> + */ + async getTaskMetaById(metaId: string): Promise> { + const response = await api.get(`${this.baseUrl}/meta/${metaId}`); + return response.data; + }, + + /** + * 查询所有任务元数据 + * @returns Promise> + */ + async getAllTaskMeta(): Promise> { + const response = await api.get(`${this.baseUrl}/meta/all`); + return response.data; + }, + + /** + * 根据分类查询任务元数据 + * @param category 分类 + * @returns Promise> + */ + async getTaskMetaByCategory(category: string): Promise> { + const response = await api.get(`${this.baseUrl}/meta/category/${category}`); + return response.data; + }, + + /** + * 分页查询任务元数据 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @returns Promise> + */ + async getTaskMetaPage(filter?: Partial, pageParam?: PageParam): Promise> { + const response = await api.post(`${this.baseUrl}/meta/page`, { + filter, + pageParam: { + pageNumber: pageParam?.pageNumber || 1, + pageSize: pageParam?.pageSize || 10 + } + }); + return response.data; + }, + + // ==================== 邮件默认接收人管理 ==================== + + /** + * 创建默认接收人 + * @param emailDefault 默认接收人 + * @returns Promise> + */ + async createEmailDefault(emailDefault: EmailDefault): Promise> { + const response = await api.post(`${this.baseUrl}/email/default`, emailDefault); + return response.data; + }, + + /** + * 更新默认接收人 + * @param emailDefault 默认接收人 + * @returns Promise> + */ + async updateEmailDefault(emailDefault: EmailDefault): Promise> { + const response = await api.put(`${this.baseUrl}/email/default`, emailDefault); + return response.data; + }, + + /** + * 删除默认接收人 + * @param defaultId 默认ID + * @returns Promise> + */ + async deleteEmailDefault(defaultId: string): Promise> { + const response = await api.delete(`${this.baseUrl}/email/default/${defaultId}`); + return response.data; + }, + + /** + * 根据defaultId查询 + * @param defaultId 默认ID + * @returns Promise> + */ + async getEmailDefaultById(defaultId: string): Promise> { + const response = await api.get(`${this.baseUrl}/email/default/${defaultId}`); + return response.data; + }, + + /** + * 根据metaId查询默认接收人 + * @param metaId 元数据ID + * @returns Promise> + */ + async getEmailDefaultByMetaId(metaId: string): Promise> { + const response = await api.get(`${this.baseUrl}/email/default/meta/${metaId}`); + return response.data; + }, + + // ==================== 邮件接收人管理 ==================== + + /** + * 创建邮件接收人 + * @param recipient 邮件接收人 + * @returns Promise> + */ + async createEmailRecipient(recipient: EmailRecipient): Promise> { + const response = await api.post(`${this.baseUrl}/email/recipient`, recipient); + return response.data; + }, + + /** + * 批量创建邮件接收人 + * @param recipients 邮件接收人列表 + * @returns Promise> + */ + async batchCreateEmailRecipient(recipients: EmailRecipient[]): Promise> { + const response = await api.post(`${this.baseUrl}/email/recipient/batch`, recipients); + return response.data; + }, + + /** + * 更新邮件接收人 + * @param recipient 邮件接收人 + * @returns Promise> + */ + async updateEmailRecipient(recipient: EmailRecipient): Promise> { + const response = await api.put(`${this.baseUrl}/email/recipient`, recipient); + return response.data; + }, + + /** + * 删除邮件接收人 + * @param recipientId 接收人ID + * @returns Promise> + */ + async deleteEmailRecipient(recipientId: string): Promise> { + const response = await api.delete(`${this.baseUrl}/email/recipient/${recipientId}`); + return response.data; + }, + + /** + * 根据ID查询接收人 + * @param recipientId 接收人ID + * @returns Promise> + */ + async getEmailRecipientById(recipientId: string): Promise> { + const response = await api.get(`${this.baseUrl}/email/recipient/${recipientId}`); + return response.data; + }, + + /** + * 根据default_id查询接收人列表 + * @param defaultId 默认ID + * @returns Promise> + */ + async getRecipientsByDefaultId(defaultId: string): Promise> { + const response = await api.get(`${this.baseUrl}/email/recipient/default/${defaultId}`); + return response.data; + }, + + /** + * 根据任务ID查询接收人列表 + * @param taskId 任务ID + * @returns Promise> + */ + async getRecipientsByTaskId(taskId: string): Promise> { + const response = await api.get(`${this.baseUrl}/email/recipient/task/${taskId}`); + return response.data; + }, + + /** + * 查询所有启用的接收人 + * @returns Promise> + */ + async getAllEnabledRecipients(): Promise> { + const response = await api.get(`${this.baseUrl}/email/recipient/enabled`); + return response.data; + }, + + /** + * 分页查询邮件接收人 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @returns Promise> + */ + async getEmailRecipientPage(filter?: Partial, pageParam?: PageParam): Promise> { + const response = await api.post(`${this.baseUrl}/email/recipient/page`, { + filter, + pageParam: { + pageNumber: pageParam?.pageNumber || 1, + pageSize: pageParam?.pageSize || 10 + } + }); + return response.data; + }, + + /** + * 删除default_id的所有接收人 + * @param defaultId 默认ID + * @returns Promise> + */ + async deleteRecipientsByDefaultId(defaultId: string): Promise> { + const response = await api.delete(`${this.baseUrl}/email/recipient/default/${defaultId}`); + return response.data; + }, + + /** + * 删除任务的所有接收人 + * @param taskId 任务ID + * @returns Promise> + */ + async deleteRecipientsByTaskId(taskId: string): Promise> { + const response = await api.delete(`${this.baseUrl}/email/recipient/task/${taskId}`); + return response.data; + }, }; diff --git a/schoolNewsWeb/src/types/crontab/index.ts b/schoolNewsWeb/src/types/crontab/index.ts index 0a3775b..47f0c77 100644 --- a/schoolNewsWeb/src/types/crontab/index.ts +++ b/schoolNewsWeb/src/types/crontab/index.ts @@ -16,6 +16,10 @@ export interface CrontabTask extends BaseDTO { taskName?: string; /** 任务分组 */ taskGroup?: string; + /** 元数据ID(关联任务元数据表) */ + metaId?: string; + /** 是否使用默认接收人 */ + defaultRecipient?: boolean; /** Bean名称 */ beanName?: string; /** 方法名称 */ @@ -172,6 +176,8 @@ export interface CrontabMethod { excuete_method?: string; /** Python脚本路径 */ path: string; + /** 元数据ID(从数据库加载时使用) */ + metaId?: string; /** 参数定义列表 */ params?: CrontabParam[]; } @@ -186,3 +192,95 @@ export interface CrontabItem { methods: CrontabMethod[]; } +/** + * 定时任务元数据 + */ +export interface TaskMeta extends BaseDTO { + /** 元数据ID */ + metaId?: string; + /** 任务名称 */ + name?: string; + /** 任务描述 */ + description?: string; + /** 任务分类 */ + category?: string; + /** Bean名称 */ + beanName?: string; + /** 方法名称 */ + methodName?: string; + /** 脚本路径 */ + scriptPath?: string; + /** 参数模式(JSON Schema) */ + paramSchema?: string; + /** 是否自动发布 */ + autoPublish?: boolean; + /** 排序 */ + sortOrder?: number; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 邮件默认接收人 + */ +export interface EmailDefault extends BaseDTO { + /** 默认ID */ + defaultId?: string; + /** 元数据ID */ + metaId?: string; + /** 用户ID */ + userId?: string; + userEmail?: string; + username?:string; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 邮件接收人 + */ +export interface EmailRecipient extends BaseDTO { + /** 接收人ID */ + recipientId?: string; + /** 任务ID */ + taskId?: string; + /** 用户ID */ + userId?: string; + /** 邮箱 */ + email?: string; + /** 姓名 */ + name?: string; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 接收人用户信息 + */ +export interface RecipientUserInfo { + /** 用户ID */ + userId: string; + /** 用户邮箱 */ + userEmail: string; + /** 用户名称 */ + username: string; +} + +/** + * 创建任务请求 + */ +export interface CreateTaskRequest { + /** 任务信息 */ + task: CrontabTask; + /** 任务元数据ID */ + metaId: string; + /** 额外添加的接收人列表 */ + additionalRecipients?: RecipientUserInfo[]; +} + diff --git a/schoolNewsWeb/src/types/study/index.ts b/schoolNewsWeb/src/types/study/index.ts index ed29caa..f2ac27f 100644 --- a/schoolNewsWeb/src/types/study/index.ts +++ b/schoolNewsWeb/src/types/study/index.ts @@ -320,7 +320,7 @@ export interface TaskItemVO extends LearningTask { username?: string; deptID?: string; deptName?: string; - parentDeptID?: string; + parentID?: string; /** 是否必修 */ required?: boolean; /** 排序号 */ diff --git a/schoolNewsWeb/src/types/user/index.ts b/schoolNewsWeb/src/types/user/index.ts index 56993c3..c23296a 100644 --- a/schoolNewsWeb/src/types/user/index.ts +++ b/schoolNewsWeb/src/types/user/index.ts @@ -73,7 +73,7 @@ export interface UserVO extends BaseDTO { /** 学习等级 */ level?: number; deptID?: string; - parentDeptID?: string; + parentID?: string; /** 部门名称 */ deptName?: string; /** 角色名称 */ diff --git a/schoolNewsWeb/src/views/admin/manage/crontab/NewsCrawlerView.vue b/schoolNewsWeb/src/views/admin/manage/crontab/NewsCrawlerView.vue index 9c0256d..0c92890 100644 --- a/schoolNewsWeb/src/views/admin/manage/crontab/NewsCrawlerView.vue +++ b/schoolNewsWeb/src/views/admin/manage/crontab/NewsCrawlerView.vue @@ -206,16 +206,16 @@
爬取方法 @@ -282,15 +282,42 @@ placeholder="请输入爬虫描述" />
+ +
- 是否允许并发 - - 允许 - 禁止 - - - 建议禁止并发,避免重复抓取 + 邮件通知 + + 使用默认接收人 + + + 默认接收人:{{ defaultRecipients.map(r => r.username).join('、') }} + + 该任务模板暂无默认接收人 + +
+ +
+ 额外接收人 +
+ + {{ recipient.username }} + +
+ + 选择接收人 + +
@@ -305,17 +332,38 @@ + +