消息模块、爬虫
This commit is contained in:
145
schoolNewsServ/.bin/mysql/sql/createTableMessage.sql
Normal file
145
schoolNewsServ/.bin/mysql/sql/createTableMessage.sql
Normal file
@@ -0,0 +1,145 @@
|
||||
-- =====================================================
|
||||
-- 消息通知模块 - 数据库表结构
|
||||
-- 包含3张表:消息主体表、消息接收对象表、用户消息表
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 消息主体表
|
||||
DROP TABLE IF EXISTS tb_sys_message;
|
||||
CREATE TABLE tb_sys_message (
|
||||
-- 基础标识
|
||||
id VARCHAR(50) PRIMARY KEY COMMENT '主键ID',
|
||||
message_id VARCHAR(50) NOT NULL UNIQUE COMMENT '消息唯一标识',
|
||||
|
||||
-- 消息内容
|
||||
title VARCHAR(200) NOT NULL COMMENT '消息标题',
|
||||
content TEXT NOT NULL COMMENT '消息内容',
|
||||
message_type VARCHAR(20) NOT NULL DEFAULT 'notification' COMMENT '消息类型:notification-通知/announcement-公告/warning-预警',
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'normal' COMMENT '优先级:normal-普通/important-重要/urgent-紧急',
|
||||
|
||||
-- 发送人信息
|
||||
sender_id VARCHAR(50) NOT NULL COMMENT '发送人用户ID',
|
||||
sender_name VARCHAR(100) COMMENT '发送人姓名(冗余字段)',
|
||||
sender_dept_id VARCHAR(50) NOT NULL COMMENT '发送人部门ID',
|
||||
sender_dept_name VARCHAR(100) COMMENT '发送人部门名称(冗余字段)',
|
||||
|
||||
-- 发送时间控制(定时发送功能)
|
||||
send_mode VARCHAR(20) NOT NULL DEFAULT 'immediate' COMMENT '发送模式:immediate-立即发送/scheduled-定时发送',
|
||||
scheduled_time DATETIME COMMENT '计划发送时间(sendMode=scheduled时必填)',
|
||||
actual_send_time DATETIME COMMENT '实际发送时间',
|
||||
|
||||
-- 状态管理
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft' COMMENT '状态:draft-草稿/pending-待发送/sending-发送中/sent-已发送/failed-失败/cancelled-已取消',
|
||||
|
||||
-- 统计信息
|
||||
target_user_count INT DEFAULT 0 COMMENT '目标用户总数',
|
||||
sent_count INT DEFAULT 0 COMMENT '已发送数量',
|
||||
success_count INT DEFAULT 0 COMMENT '发送成功数量',
|
||||
failed_count INT DEFAULT 0 COMMENT '发送失败数量',
|
||||
read_count INT DEFAULT 0 COMMENT '已读数量',
|
||||
|
||||
-- 失败处理和重试机制
|
||||
retry_count INT DEFAULT 0 COMMENT '当前重试次数',
|
||||
max_retry_count INT DEFAULT 3 COMMENT '最大重试次数',
|
||||
last_error TEXT COMMENT '最后错误信息',
|
||||
|
||||
-- 基础字段
|
||||
creator VARCHAR(50) COMMENT '创建人ID',
|
||||
updater VARCHAR(50) COMMENT '更新人ID',
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time DATETIME COMMENT '删除时间',
|
||||
deleted BIT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
|
||||
|
||||
-- 索引
|
||||
INDEX idx_message_id (message_id),
|
||||
INDEX idx_sender_id (sender_id),
|
||||
INDEX idx_sender_dept_id (sender_dept_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_send_mode (send_mode),
|
||||
INDEX idx_scheduled_time (scheduled_time),
|
||||
INDEX idx_create_time (create_time),
|
||||
INDEX idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='消息主体表';
|
||||
|
||||
|
||||
-- 2. 消息发送方式接收对象表
|
||||
DROP TABLE IF EXISTS tb_sys_message_target;
|
||||
CREATE TABLE tb_sys_message_target (
|
||||
-- 基础标识
|
||||
id VARCHAR(50) PRIMARY KEY COMMENT '主键ID',
|
||||
message_id VARCHAR(50) NOT NULL COMMENT '消息ID(关联tb_sys_message.message_id)',
|
||||
|
||||
-- 发送方式
|
||||
send_method VARCHAR(100) NOT NULL COMMENT '发送方式:system-系统消息/email-邮件/sms-短信(多选时逗号分隔,如:system,email)',
|
||||
|
||||
-- 接收对象
|
||||
target_type VARCHAR(20) NOT NULL COMMENT '接收对象类型:dept-部门/role-角色/user-人员',
|
||||
target_id VARCHAR(50) NOT NULL COMMENT '接收对象ID(部门ID/角色ID/用户ID)',
|
||||
target_name VARCHAR(100) COMMENT '接收对象名称(冗余字段,便于展示)',
|
||||
|
||||
-- 作用域部门(关键字段:限制角色的部门范围)
|
||||
scope_dept_id VARCHAR(50) NOT NULL COMMENT '作用域部门ID:dept时与target_id相同;role时表示该角色限定在哪个部门及其子部门范围内;user时为用户所属部门ID',
|
||||
|
||||
-- 基础字段
|
||||
creator VARCHAR(50) COMMENT '创建人ID',
|
||||
updater VARCHAR(50) COMMENT '更新人ID',
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time DATETIME COMMENT '删除时间',
|
||||
deleted tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
|
||||
|
||||
-- 索引
|
||||
INDEX idx_messageID (message_id),
|
||||
INDEX idx_target_type (target_type),
|
||||
INDEX idx_scope_dept_id (scope_dept_id),
|
||||
INDEX idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='消息发送方式接收对象表';
|
||||
|
||||
|
||||
-- 3. 用户接收消息表
|
||||
DROP TABLE IF EXISTS tb_sys_message_user;
|
||||
CREATE TABLE tb_sys_message_user (
|
||||
-- 基础标识
|
||||
id VARCHAR(50) PRIMARY KEY COMMENT '主键ID',
|
||||
message_id VARCHAR(50) NOT NULL COMMENT '消息ID(关联tb_sys_message.message_id)',
|
||||
user_id VARCHAR(50) NOT NULL COMMENT '接收用户ID',
|
||||
|
||||
-- 发送方式
|
||||
send_method VARCHAR(20) NOT NULL COMMENT '实际发送方式:system-系统消息/email-邮件/sms-短信',
|
||||
|
||||
-- 阅读状态
|
||||
is_read tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否已读:0-未读,1-已读',
|
||||
read_time DATETIME COMMENT '阅读时间',
|
||||
|
||||
-- 发送状态
|
||||
send_status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '发送状态:pending-待发送/success-发送成功/failed-发送失败',
|
||||
fail_reason TEXT COMMENT '失败原因',
|
||||
|
||||
-- 基础字段
|
||||
creator VARCHAR(50) COMMENT '创建人ID',
|
||||
updater VARCHAR(50) COMMENT '更新人ID',
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time DATETIME COMMENT '删除时间',
|
||||
deleted tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
|
||||
|
||||
-- 索引
|
||||
INDEX idx_message_id (message_id),
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_is_read (is_read),
|
||||
INDEX idx_send_status (send_status),
|
||||
INDEX idx_deleted (deleted),
|
||||
INDEX idx_user_read (user_id, is_read, deleted) COMMENT '用户未读消息查询索引'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户接收消息表';
|
||||
|
||||
|
||||
-- =====================================================
|
||||
-- 初始化说明
|
||||
-- =====================================================
|
||||
-- 1. 执行本SQL脚本创建3张表
|
||||
-- 2. 执行message_menu.sql插入菜单权限数据
|
||||
-- 3. 表结构说明:
|
||||
-- - tb_sys_message:存储消息主体信息,支持定时发送
|
||||
-- - tb_sys_message_target:存储接收对象配置,scopeDeptID限制权限范围
|
||||
-- - tb_sys_message_user:存储每个用户的消息记录,支持已读/未读状态
|
||||
-- =====================================================
|
||||
@@ -39,6 +39,8 @@ SOURCE createTableAchievement.sql;
|
||||
|
||||
SOURCE createTableCrontab.sql;
|
||||
|
||||
SOURCE createTableMessage.sql;
|
||||
|
||||
-- =====================================================
|
||||
-- 插入初始数据
|
||||
-- =====================================================
|
||||
|
||||
@@ -33,17 +33,18 @@ INSERT INTO `tb_sys_user_dept_role` (id, user_id, dept_id, role_id, creator, cre
|
||||
('1', '1', 'root_department', 'superadmin', '1', now());
|
||||
|
||||
-- 插入模块数据
|
||||
INSERT INTO `tb_sys_module` (id, module_id, name, code, description, icon, order_num, status, creator, create_time) VALUES
|
||||
INSERT INTO `tb_sys_module` (id, module_id, name, code, description, icon, order_num, status, creator, create_time) VALUES
|
||||
('1', 'module_system', '系统管理', 'system', '系统管理模块', 'el-icon-setting', 1, 1, '1', now()),
|
||||
('2', 'module_news', '新闻管理', 'news', '新闻管理模块', 'el-icon-document', 2, 1, '1', now()),
|
||||
('3', 'module_study', '学习管理', 'study', '学习管理模块', 'el-icon-reading', 3, 1, '1', now()),
|
||||
('4', 'module_ai', 'AI管理', 'ai', 'AI管理模块', 'el-icon-cpu', 4, 1, '1', now()),
|
||||
('5', 'module_usercenter', '用户中心', 'usercenter', '用户中心模块', 'el-icon-user', 5, 1, '1', now()),
|
||||
('6', 'module_file', '文件管理', 'file', '文件管理模块', 'el-icon-folder', 6, 1, '1', now()),
|
||||
('7', 'module_crontab', '定时任务', 'crontab', '定时任务管理模块', 'el-icon-alarm-clock', 7, 1, '1', now());
|
||||
('7', 'module_crontab', '定时任务', 'crontab', '定时任务管理模块', 'el-icon-alarm-clock', 7, 1, '1', now()),
|
||||
('8', 'module_message', '消息通知', 'message', '消息通知管理模块', 'el-icon-message', 8, 1, '1', now());
|
||||
|
||||
-- 插入权限数据
|
||||
INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, module_id, creator, create_time) VALUES
|
||||
INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, module_id, creator, create_time) VALUES
|
||||
('0','perm_default', '默认权限', 'default', '默认权限', 'module_system', '1', now()),
|
||||
('1','perm_system_manage', '系统管理', 'system:manage', '系统管理权限', 'module_system', '1', now()),
|
||||
('2','perm_system_dept_manage', '系统部门查看', 'system:dept:manage', '系统部门查看权限', 'module_system', '1', now()),
|
||||
@@ -60,7 +61,10 @@ INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, modu
|
||||
('12','perm_usercenter_manage', '用户中心管理', 'usercenter:manage', '用户中心管理权限', 'module_usercenter', '1', now()),
|
||||
('13','perm_file_manage', '文件管理', 'file:manage', '文件管理权限', 'module_file', '1', now()),
|
||||
('14','perm_crontab_manage', '定时任务管理', 'crontab:manage', '定时任务管理权限', 'module_crontab', '1', now()),
|
||||
('15','perm_crontab_execute', '定时任务执行', 'crontab:execute', '定时任务执行权限', 'module_crontab', '1', now());
|
||||
('15','perm_crontab_execute', '定时任务执行', 'crontab:execute', '定时任务执行权限', 'module_crontab', '1', now()),
|
||||
('16','perm_message_manage', '消息管理', 'message:manage', '消息管理权限(管理端)', 'module_message', '1', now()),
|
||||
('17','perm_message_send', '消息发送', 'message:send', '消息发送权限', 'module_message', '1', now()),
|
||||
('18','perm_message_view', '消息查看', 'message:view', '消息查看权限(用户端)', 'module_message', '1', now());
|
||||
|
||||
-- 插入角色-权限关联数据
|
||||
INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES
|
||||
@@ -82,6 +86,9 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
|
||||
('15', 'superadmin', 'perm_file_manage', '1', now()),
|
||||
('16', 'superadmin', 'perm_crontab_manage', '1', now()),
|
||||
('17', 'superadmin', 'perm_crontab_execute', '1', now()),
|
||||
('18', 'superadmin', 'perm_message_manage', '1', now()),
|
||||
('19', 'superadmin', 'perm_message_send', '1', now()),
|
||||
('19.1', 'superadmin', 'perm_message_view', '1', now()),
|
||||
|
||||
-- 管理员:拥有业务管理权限,但没有系统日志等系统管理权限
|
||||
('20', 'admin', 'perm_default', '1', now()),
|
||||
@@ -92,13 +99,17 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
|
||||
('25', 'admin', 'perm_ai_manage', '1', now()),
|
||||
('26', 'admin', 'perm_usercenter_manage', '1', now()),
|
||||
('27', 'admin', 'perm_file_manage', '1', now()),
|
||||
('28', 'admin', 'perm_message_manage', '1', now()),
|
||||
('29', 'admin', 'perm_message_send', '1', now()),
|
||||
('29.1', 'admin', 'perm_message_view', '1', now()),
|
||||
|
||||
-- 自由角色:拥有用户视图相关的所有权限(前台用户权限)
|
||||
('30', 'freedom', 'perm_default', '1', now()),
|
||||
('31', 'freedom', 'perm_news_article_add', '1', now()),
|
||||
('32', 'freedom', 'perm_ai_manage', '1', now()),
|
||||
('33', 'freedom', 'perm_usercenter_manage', '1', now()),
|
||||
('34', 'freedom', 'perm_file_manage', '1', now());
|
||||
('34', 'freedom', 'perm_file_manage', '1', now()),
|
||||
('35', 'freedom', 'perm_message_view', '1', now());
|
||||
|
||||
-- 插入前端菜单数据
|
||||
INSERT INTO `tb_sys_menu` VALUES
|
||||
@@ -106,7 +117,7 @@ INSERT INTO `tb_sys_menu` VALUES
|
||||
('100', 'menu_home', '首页', NULL, '/home', 'user/home/HomeView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
|
||||
('101', 'menu_resource_hot', '热门资源', NULL, '/resource-hot', 'user/resource-center/HotResourceView', NULL, 2, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
|
||||
('200', 'menu_resource_center', '资源中心', NULL, '/resource-center', 'user/resource-center/ResourceCenterView', NULL, 2, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
|
||||
('300', 'menu_study_plan', '学习计划', NULL, '/study-plan', 'user/study-plan/StudyPlanView', NULL, 3, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('300', 'menu_study_plan', '学习计划', NULL, '/study-plan', '', NULL, 3, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('301', 'menu_study_tasks', '学习任务', 'menu_study_plan', '/study-plan/tasks', 'user/study-plan/StudyTasksView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('302', 'menu_course_center', '课程中心', 'menu_study_plan', '/study-plan/course', 'user/study-plan/CourseCenterView', NULL, 2, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('303', 'menu_task_detail', '任务详情', 'menu_study_plan', '/study-plan/task-detail', 'user/study-plan/LearningTaskDetailView', NULL, 3, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
@@ -120,7 +131,6 @@ INSERT INTO `tb_sys_menu` VALUES
|
||||
('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/profile/ProfileView', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
|
||||
('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('600', 'menu_ai_assistant', '智能体模块', NULL, '/ai-assistant', 'user/ai-assistant/AIAssistantView', NULL, 6, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
-- 管理后台菜单 (1000-8999)
|
||||
('1000', 'menu_admin_overview', '系统总览', NULL, '/admin/overview', 'admin/overview/SystemOverviewView', 'admin/overview.svg', 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:32', NULL, 0),
|
||||
('2000', 'menu_sys_manage', '系统管理', NULL, '', '', 'admin/settings.svg', 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:35', NULL, 0),
|
||||
@@ -157,7 +167,12 @@ INSERT INTO `tb_sys_menu` VALUES
|
||||
('8000', 'menu_admin_crontab_manage', '定时任务管理', NULL, '', '', NULL, 8, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('8001', 'menu_admin_crontab_task', '任务管理', 'menu_admin_crontab_manage', '/admin/manage/crontab/task', 'admin/manage/crontab/TaskManagementView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0);
|
||||
('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
-- 消息通知模块菜单 (9000-9999)
|
||||
('9001', 'menu_admin_message_manage', '消息管理', NULL, '/admin/manage/message', 'admin/manage/message/MessageManageView', 'admin/message.svg', 9, 0, 'SidebarLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
|
||||
-- 用户端消息中心菜单 (650-699)
|
||||
('650', 'menu_user_message_center', '消息中心', NULL, '/user/message', 'user/message/MyMessageListView', NULL, 7, 1, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
|
||||
('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0);
|
||||
-- 插入菜单权限关联数据
|
||||
INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES
|
||||
-- 前端菜单权限关联
|
||||
@@ -174,7 +189,6 @@ INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, creat
|
||||
('115', 'perm_default', 'menu_profile', '1', now()),
|
||||
('116', 'perm_default', 'menu_personal_info', '1', now()),
|
||||
('117', 'perm_default', 'menu_account_settings', '1', now()),
|
||||
('118', 'perm_ai_manage', 'menu_ai_assistant', '1', now()),
|
||||
('119', 'perm_default', 'menu_user_dropdown', '1', now()),
|
||||
('120', 'perm_news_article_add', 'menu_article_add', '1', now()),
|
||||
('121', 'perm_default', 'menu_task_detail', '1', now()),
|
||||
@@ -220,4 +234,15 @@ INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, creat
|
||||
('232', 'perm_crontab_manage', 'menu_admin_crontab_manage', '1', now()),
|
||||
('233', 'perm_crontab_manage', 'menu_admin_crontab_task', '1', now()),
|
||||
('234', 'perm_crontab_manage', 'menu_admin_crontab_log', '1', now()),
|
||||
('235', 'perm_crontab_manage', 'menu_admin_news_crawler', '1', now());
|
||||
('235', 'perm_crontab_manage', 'menu_admin_news_crawler', '1', now()),
|
||||
|
||||
-- 消息通知管理菜单权限关联
|
||||
('240', 'perm_message_manage', 'menu_admin_message_manage', '1', now()),
|
||||
('241', 'perm_message_manage', 'menu_admin_message_list', '1', now()),
|
||||
('242', 'perm_message_send', 'menu_admin_message_create', '1', now()),
|
||||
('243', 'perm_message_manage', 'menu_admin_message_detail', '1', now()),
|
||||
|
||||
-- 用户端消息中心权限关联
|
||||
('250', 'perm_message_view', 'menu_user_message_center', '1', now()),
|
||||
('251', 'perm_message_view', 'menu_user_message_detail', '1', now());
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ org.xyzh.achievement.controller.AchievementController
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-usercenter</artifactId> <!-- 包含成就接口 -->
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -87,14 +87,14 @@ org.xyzh.achievement.controller.AchievementController
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 如果需要使用实现类(通常不需要,使用Dubbo远程调用) -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>achievement</name>
|
||||
<description>成就模块</description>
|
||||
@@ -25,28 +25,28 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>admin</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
@@ -34,57 +34,62 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>ai</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>crontab</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>message</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
@SpringBootApplication(scanBasePackages = "org.xyzh")
|
||||
@EnableScheduling
|
||||
@MapperScan({"org.xyzh.system.mapper", "org.xyzh.file.mapper", "org.xyzh.news.mapper", "org.xyzh.study.mapper",
|
||||
"org.xyzh.usercenter.mapper", "org.xyzh.ai.mapper", "org.xyzh.achievement.mapper", "org.xyzh.crontab.mapper"})
|
||||
"org.xyzh.usercenter.mapper", "org.xyzh.ai.mapper", "org.xyzh.achievement.mapper", "org.xyzh.crontab.mapper",
|
||||
"org.xyzh.message.mapper"})
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -114,7 +114,9 @@
|
||||
<Logger name="org.xyzh.crontab.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh.message.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<!-- 项目包日志配置 - Auth模块 -->
|
||||
<Logger name="org.xyzh.auth" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
@@ -173,6 +175,14 @@
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.message" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="Console"/>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>ai</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>ai</name>
|
||||
<description>智能体模块</description>
|
||||
@@ -25,18 +25,18 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-ai</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>api-achievement</name>
|
||||
<description>成就模块API接口</description>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-ai</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<name>api-ai</name>
|
||||
<description>智能体API接口定义</description>
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
@@ -57,5 +57,9 @@
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-crontab</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-message</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-auth</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-crontab</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
@@ -22,12 +22,12 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>api-file</name>
|
||||
<description>文件模块API</description>
|
||||
|
||||
33
schoolNewsServ/api/api-message/pom.xml
Normal file
33
schoolNewsServ/api/api-message/pom.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-message</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,207 @@
|
||||
package org.xyzh.api.message;
|
||||
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.common.dto.message.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息服务接口
|
||||
*
|
||||
* @description 消息通知模块的服务接口定义
|
||||
* @filename MessageService.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
public interface MessageService {
|
||||
|
||||
// ================== 消息管理 ==================
|
||||
|
||||
/**
|
||||
* 创建消息
|
||||
*
|
||||
* @param message 消息对象
|
||||
* @return ResultDomain<TbSysMessage> 创建结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> createMessage(TbSysMessage message);
|
||||
|
||||
/**
|
||||
* 更新消息(仅允许更新草稿状态的消息)
|
||||
*
|
||||
* @param message 消息对象
|
||||
* @return ResultDomain<TbSysMessage> 更新结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> updateMessage(TbSysMessage message);
|
||||
|
||||
/**
|
||||
* 删除消息(逻辑删除)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage> 删除结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> deleteMessage(String messageID);
|
||||
|
||||
/**
|
||||
* 根据ID查询消息详情
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<MessageVO> 消息详情
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<MessageVO> getMessageById(String messageID);
|
||||
|
||||
/**
|
||||
* 分页查询消息列表(管理端)
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @return ResultDomain<MessageVO> 消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<MessageVO> getMessagePage(TbSysMessage filter, PageParam pageParam);
|
||||
|
||||
// ================== 消息发送 ==================
|
||||
|
||||
/**
|
||||
* 发送消息(立即发送或定时发送)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage> 发送结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> sendMessage(String messageID);
|
||||
|
||||
/**
|
||||
* 立即发送(将定时消息改为立即发送)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage> 发送结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> sendNow(String messageID);
|
||||
|
||||
/**
|
||||
* 取消定时消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage> 取消结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> cancelMessage(String messageID);
|
||||
|
||||
/**
|
||||
* 修改定时发送时间
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @param scheduledTime 新的发送时间
|
||||
* @return ResultDomain<TbSysMessage> 修改结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> rescheduleMessage(String messageID, java.util.Date scheduledTime);
|
||||
|
||||
/**
|
||||
* 重试失败消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage> 重试结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessage> retryMessage(String messageID);
|
||||
|
||||
// ================== 用户消息 ==================
|
||||
|
||||
/**
|
||||
* 分页查询我的消息列表(用户端)
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @return ResultDomain<MessageUserVO> 我的消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<MessageUserVO> getMyMessagesPage(MessageUserVO filter, PageParam pageParam);
|
||||
|
||||
/**
|
||||
* 查询我的消息详情(用户端)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<MessageUserVO> 消息详情
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<MessageUserVO> getMyMessageDetail(String messageID);
|
||||
|
||||
/**
|
||||
* 标记消息为已读
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessageUser> 标记结果
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<TbSysMessageUser> markAsRead(String messageID);
|
||||
|
||||
/**
|
||||
* 批量标记消息为已读
|
||||
*
|
||||
* @param messageIDs 消息ID列表
|
||||
* @return ResultDomain<Integer> 标记成功的数量
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<Integer> batchMarkAsRead(List<String> messageIDs);
|
||||
|
||||
/**
|
||||
* 获取未读消息数量
|
||||
*
|
||||
* @return ResultDomain<Integer> 未读消息数量
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<Integer> getUnreadCount();
|
||||
|
||||
// ================== 辅助接口 ==================
|
||||
|
||||
/**
|
||||
* 获取可选的部门树(当前部门及子部门)
|
||||
*
|
||||
* @return ResultDomain<Map> 部门树数据
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<Map<String, Object>> getTargetDepts();
|
||||
|
||||
/**
|
||||
* 获取可选的角色列表(当前部门及子部门的角色)
|
||||
*
|
||||
* @return ResultDomain<Map> 角色列表数据
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<Map<String, Object>> getTargetRoles();
|
||||
|
||||
/**
|
||||
* 获取可选的用户列表(当前部门及子部门的用户)
|
||||
*
|
||||
* @return ResultDomain<Map> 用户列表数据
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
ResultDomain<Map<String, Object>> getTargetUsers();
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>api-news</name>
|
||||
<description>新闻API接口定义</description>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<name>api-usercenter</name>
|
||||
<description>个人中心API接口定义</description>
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>api-all</module>
|
||||
@@ -24,6 +24,7 @@
|
||||
<module>api-news</module>
|
||||
<module>api-file</module>
|
||||
<module>api-crontab</module>
|
||||
<module>api-message</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@@ -36,57 +37,62 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-auth</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-course</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-ai</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-crontab</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-message</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -94,12 +100,12 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
@@ -24,38 +24,38 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-exception</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-util</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-auth</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Common All-in-One</name>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-annotation</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,16 +6,22 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.xyzh.common.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建消息请求DTO
|
||||
*
|
||||
* @description 用于接收前端创建消息的请求参数
|
||||
* @filename MessageCreateDTO.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Data
|
||||
public class MessageCreateDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消息标题(必填)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容(必填)
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息类型:notification-通知/announcement-公告/warning-预警
|
||||
* 默认:notification
|
||||
*/
|
||||
private String messageType = "notification";
|
||||
|
||||
/**
|
||||
* 优先级:normal-普通/important-重要/urgent-紧急
|
||||
* 默认:normal
|
||||
*/
|
||||
private String priority = "normal";
|
||||
|
||||
/**
|
||||
* 发送模式:immediate-立即发送/scheduled-定时发送
|
||||
* 默认:immediate
|
||||
*/
|
||||
private String sendMode = "immediate";
|
||||
|
||||
/**
|
||||
* 计划发送时间(sendMode=scheduled时必填)
|
||||
*/
|
||||
private Date scheduledTime;
|
||||
|
||||
/**
|
||||
* 接收对象列表(必填)
|
||||
*/
|
||||
private List<MessageTargetDTO> targets;
|
||||
|
||||
/**
|
||||
* 内部类:接收对象DTO
|
||||
*/
|
||||
@Data
|
||||
public static class MessageTargetDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 发送方式:system/email/sms,多选时逗号分隔
|
||||
* 默认:system
|
||||
*/
|
||||
private String sendMethod = "system";
|
||||
|
||||
/**
|
||||
* 接收对象类型:dept-部门/role-角色/user-人员
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
/**
|
||||
* 接收对象ID
|
||||
*/
|
||||
private String targetID;
|
||||
|
||||
/**
|
||||
* 接收对象名称(前端传递,用于冗余存储)
|
||||
*/
|
||||
private String targetName;
|
||||
|
||||
/**
|
||||
* 作用域部门ID(关键字段)
|
||||
* - dept时:与targetID相同
|
||||
* - role时:限定该角色的部门范围
|
||||
* - user时:用户所属部门ID
|
||||
*/
|
||||
private String scopeDeptID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package org.xyzh.common.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户消息视图对象
|
||||
*
|
||||
* @description 用于前端展示的用户消息对象,包含用户信息和消息阅读状态
|
||||
* @filename MessageUserVO.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Data
|
||||
public class MessageUserVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String messageID;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userID;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
private String fullName;
|
||||
|
||||
/**
|
||||
* 用户所属部门ID
|
||||
*/
|
||||
private String deptID;
|
||||
|
||||
/**
|
||||
* 用户所属部门名称
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 实际发送方式:system-系统消息/email-邮件/sms-短信
|
||||
*/
|
||||
private String sendMethod;
|
||||
|
||||
/**
|
||||
* 是否已读:0-未读,1-已读
|
||||
*/
|
||||
private Boolean isRead;
|
||||
|
||||
/**
|
||||
* 阅读时间
|
||||
*/
|
||||
private Date readTime;
|
||||
|
||||
/**
|
||||
* 发送状态:pending-待发送/success-发送成功/failed-发送失败
|
||||
*/
|
||||
private String sendStatus;
|
||||
|
||||
/**
|
||||
* 失败原因
|
||||
*/
|
||||
private String failReason;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
// ========== 消息主体信息(用户端查看消息列表时需要) ==========
|
||||
/**
|
||||
* 消息标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 优先级
|
||||
*/
|
||||
private String priority;
|
||||
|
||||
/**
|
||||
* 发送人姓名
|
||||
*/
|
||||
private String senderName;
|
||||
|
||||
/**
|
||||
* 发送人部门名称
|
||||
*/
|
||||
private String senderDeptName;
|
||||
|
||||
/**
|
||||
* 实际发送时间
|
||||
*/
|
||||
private Date actualSendTime;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package org.xyzh.common.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息视图对象
|
||||
*
|
||||
* @description 用于前端展示的消息对象,包含消息主体信息和关联的接收对象列表
|
||||
* @filename MessageVO.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Data
|
||||
public class MessageVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// ========== 消息主体信息 ==========
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 消息唯一标识
|
||||
*/
|
||||
private String messageID;
|
||||
|
||||
/**
|
||||
* 消息标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息类型:notification-通知/announcement-公告/warning-预警
|
||||
*/
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 优先级:normal-普通/important-重要/urgent-紧急
|
||||
*/
|
||||
private String priority;
|
||||
|
||||
/**
|
||||
* 发送人用户ID
|
||||
*/
|
||||
private String senderID;
|
||||
|
||||
/**
|
||||
* 发送人姓名
|
||||
*/
|
||||
private String senderName;
|
||||
|
||||
/**
|
||||
* 发送人部门ID
|
||||
*/
|
||||
private String senderDeptID;
|
||||
|
||||
/**
|
||||
* 发送人部门名称
|
||||
*/
|
||||
private String senderDeptName;
|
||||
|
||||
/**
|
||||
* 发送模式:immediate-立即发送/scheduled-定时发送
|
||||
*/
|
||||
private String sendMode;
|
||||
|
||||
/**
|
||||
* 计划发送时间
|
||||
*/
|
||||
private Date scheduledTime;
|
||||
|
||||
/**
|
||||
* 实际发送时间
|
||||
*/
|
||||
private Date actualSendTime;
|
||||
|
||||
/**
|
||||
* 状态:draft-草稿/pending-待发送/sending-发送中/sent-已发送/failed-失败/cancelled-已取消
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 目标用户总数
|
||||
*/
|
||||
private Integer targetUserCount;
|
||||
|
||||
/**
|
||||
* 已发送数量
|
||||
*/
|
||||
private Integer sentCount;
|
||||
|
||||
/**
|
||||
* 发送成功数量
|
||||
*/
|
||||
private Integer successCount;
|
||||
|
||||
/**
|
||||
* 发送失败数量
|
||||
*/
|
||||
private Integer failedCount;
|
||||
|
||||
/**
|
||||
* 已读数量
|
||||
*/
|
||||
private Integer readCount;
|
||||
|
||||
/**
|
||||
* 当前重试次数
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private Integer maxRetryCount;
|
||||
|
||||
/**
|
||||
* 最后错误信息
|
||||
*/
|
||||
private String lastError;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
// ========== 关联信息 ==========
|
||||
/**
|
||||
* 接收对象列表
|
||||
*/
|
||||
private List<TbSysMessageTarget> targets;
|
||||
|
||||
/**
|
||||
* 用户接收记录列表(仅在详情查询时返回)
|
||||
*/
|
||||
private List<MessageUserVO> userMessages;
|
||||
|
||||
// ========== 计算属性 ==========
|
||||
/**
|
||||
* 发送进度百分比(0-100)
|
||||
*/
|
||||
public Integer getSendProgress() {
|
||||
if (targetUserCount == null || targetUserCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (sentCount == null) {
|
||||
return 0;
|
||||
}
|
||||
return (int) Math.round((sentCount * 100.0) / targetUserCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功率百分比(0-100)
|
||||
*/
|
||||
public Integer getSuccessRate() {
|
||||
if (sentCount == null || sentCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (successCount == null) {
|
||||
return 0;
|
||||
}
|
||||
return (int) Math.round((successCount * 100.0) / sentCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 已读率百分比(0-100)
|
||||
*/
|
||||
public Integer getReadRate() {
|
||||
if (successCount == null || successCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (readCount == null) {
|
||||
return 0;
|
||||
}
|
||||
return (int) Math.round((readCount * 100.0) / successCount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package org.xyzh.common.dto.message;
|
||||
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息主体表实体类
|
||||
*
|
||||
* @description 消息通知模块的主体表,存储消息的基本信息、发送配置和统计数据
|
||||
* @filename TbSysMessage.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TbSysMessage extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消息唯一标识
|
||||
*/
|
||||
private String messageID;
|
||||
|
||||
/**
|
||||
* 消息标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息类型:notification-通知/announcement-公告/warning-预警
|
||||
*/
|
||||
private String messageType;
|
||||
|
||||
/**
|
||||
* 优先级:normal-普通/important-重要/urgent-紧急
|
||||
*/
|
||||
private String priority;
|
||||
|
||||
/**
|
||||
* 发送人用户ID
|
||||
*/
|
||||
private String senderID;
|
||||
|
||||
/**
|
||||
* 发送人姓名(冗余字段)
|
||||
*/
|
||||
private String senderName;
|
||||
|
||||
/**
|
||||
* 发送人部门ID
|
||||
*/
|
||||
private String senderDeptID;
|
||||
|
||||
/**
|
||||
* 发送人部门名称(冗余字段)
|
||||
*/
|
||||
private String senderDeptName;
|
||||
|
||||
/**
|
||||
* 发送模式:immediate-立即发送/scheduled-定时发送
|
||||
*/
|
||||
private String sendMode;
|
||||
|
||||
/**
|
||||
* 计划发送时间(sendMode=scheduled时必填)
|
||||
*/
|
||||
private Date scheduledTime;
|
||||
|
||||
/**
|
||||
* 实际发送时间
|
||||
*/
|
||||
private Date actualSendTime;
|
||||
|
||||
/**
|
||||
* 状态:draft-草稿/pending-待发送/sending-发送中/sent-已发送/failed-失败/cancelled-已取消
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 目标用户总数
|
||||
*/
|
||||
private Integer targetUserCount;
|
||||
|
||||
/**
|
||||
* 已发送数量
|
||||
*/
|
||||
private Integer sentCount;
|
||||
|
||||
/**
|
||||
* 发送成功数量
|
||||
*/
|
||||
private Integer successCount;
|
||||
|
||||
/**
|
||||
* 发送失败数量
|
||||
*/
|
||||
private Integer failedCount;
|
||||
|
||||
/**
|
||||
* 已读数量
|
||||
*/
|
||||
private Integer readCount;
|
||||
|
||||
/**
|
||||
* 当前重试次数
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private Integer maxRetryCount;
|
||||
|
||||
/**
|
||||
* 最后错误信息
|
||||
*/
|
||||
private String lastError;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
private String creator;
|
||||
|
||||
/**
|
||||
* 更新人ID
|
||||
*/
|
||||
private String updater;
|
||||
|
||||
/**
|
||||
* 发送目标配置列表(前端辅助字段,不映射到数据库)
|
||||
*/
|
||||
private List<TbSysMessageTarget> targets;
|
||||
|
||||
/**
|
||||
* 发送方式列表(前端辅助字段,从targets聚合)
|
||||
*/
|
||||
private List<String> sendMethods;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.xyzh.common.dto.message;
|
||||
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 消息发送方式接收对象表实体类
|
||||
*
|
||||
* @description 存储消息的接收对象配置,包括发送方式、目标类型和作用域部门
|
||||
* @filename TbSysMessageTarget.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TbSysMessageTarget extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消息ID(关联tb_sys_message.messageID)
|
||||
*/
|
||||
private String messageID;
|
||||
|
||||
/**
|
||||
* 发送方式:system-系统消息/email-邮件/sms-短信
|
||||
* 多选时逗号分隔,如:system,email
|
||||
*/
|
||||
private String sendMethod;
|
||||
|
||||
/**
|
||||
* 接收对象类型:dept-部门/role-角色/user-人员
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
/**
|
||||
* 接收对象ID(部门ID/角色ID/用户ID)
|
||||
*/
|
||||
private String targetID;
|
||||
|
||||
/**
|
||||
* 接收对象名称(冗余字段,便于展示)
|
||||
*/
|
||||
private String targetName;
|
||||
|
||||
/**
|
||||
* 作用域部门ID
|
||||
* - dept时:与targetID相同
|
||||
* - role时:表示该角色限定在哪个部门及其子部门范围内
|
||||
* - user时:用户所属部门ID
|
||||
*/
|
||||
private String scopeDeptID;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
private String creator;
|
||||
|
||||
/**
|
||||
* 更新人ID
|
||||
*/
|
||||
private String updater;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.xyzh.common.dto.message;
|
||||
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户接收消息表实体类
|
||||
*
|
||||
* @description 存储每个用户接收到的消息记录,包括阅读状态和发送状态
|
||||
* @filename TbSysMessageUser.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TbSysMessageUser extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消息ID(关联tb_sys_message.messageID)
|
||||
*/
|
||||
private String messageID;
|
||||
|
||||
/**
|
||||
* 接收用户ID
|
||||
*/
|
||||
private String userID;
|
||||
|
||||
/**
|
||||
* 实际发送方式:system-系统消息/email-邮件/sms-短信
|
||||
*/
|
||||
private String sendMethod;
|
||||
|
||||
/**
|
||||
* 是否已读:0-未读,1-已读
|
||||
*/
|
||||
private Boolean isRead;
|
||||
|
||||
/**
|
||||
* 阅读时间
|
||||
*/
|
||||
private Date readTime;
|
||||
|
||||
/**
|
||||
* 发送状态:pending-待发送/success-发送成功/failed-发送失败
|
||||
*/
|
||||
private String sendStatus;
|
||||
|
||||
/**
|
||||
* 失败原因
|
||||
*/
|
||||
private String failReason;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
private String creator;
|
||||
|
||||
/**
|
||||
* 更新人ID
|
||||
*/
|
||||
private String updater;
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-exception</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package org.xyzh;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-jdbc</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
@@ -36,7 +36,7 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-util</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>common-core</module>
|
||||
@@ -42,37 +42,37 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-dto</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-annotation</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-exception</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-jdbc</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-util</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>crontab</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
@@ -23,25 +23,25 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-crontab</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Common模块依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<!-- System模块依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>file</name>
|
||||
<description>文件模块</description>
|
||||
@@ -27,14 +27,14 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 通用模块 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Web -->
|
||||
|
||||
96
schoolNewsServ/message/pom.xml
Normal file
96
schoolNewsServ/message/pom.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>message</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- API依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-message</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Common模块依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- System模块依赖(需要部门、角色、用户相关服务)-->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<parameters>true</parameters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.xyzh.message.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 定时任务和异步任务配置类
|
||||
*
|
||||
* @description 配置消息模块的定时任务线程池和异步任务线程池
|
||||
* @filename SchedulingConfig.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class SchedulingConfig implements SchedulingConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
taskRegistrar.setScheduler(messageSchedulerExecutor());
|
||||
}
|
||||
|
||||
@Bean(name = "messageSchedulerExecutor")
|
||||
public TaskScheduler messageSchedulerExecutor() {
|
||||
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
|
||||
// 线程池大小:5个线程用于定时任务扫描
|
||||
executor.setPoolSize(5);
|
||||
// 线程名称前缀
|
||||
executor.setThreadNamePrefix("message-scheduler-");
|
||||
// 等待所有任务完成后再关闭
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 等待时间(秒)
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
// 初始化
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步任务线程池配置
|
||||
*/
|
||||
@Bean(name = "messageAsyncExecutor")
|
||||
public Executor messageAsyncExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
// 核心线程数:10个线程用于异步发送消息
|
||||
executor.setCorePoolSize(10);
|
||||
// 最大线程数
|
||||
executor.setMaxPoolSize(20);
|
||||
// 队列容量
|
||||
executor.setQueueCapacity(500);
|
||||
// 线程名称前缀
|
||||
executor.setThreadNamePrefix("message-async-");
|
||||
// 等待所有任务完成后再关闭
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 等待时间(秒)
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
// 初始化
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package org.xyzh.message.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.message.MessageService;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.common.core.page.PageRequest;
|
||||
import org.xyzh.common.dto.message.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息控制器
|
||||
*
|
||||
* @description 消息通知模块的REST API接口
|
||||
* @filename MessageController.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/message")
|
||||
public class MessageController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageController.class);
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
// ================== 消息管理接口(管理端) ==================
|
||||
|
||||
/**
|
||||
* 创建消息
|
||||
*
|
||||
* @param message 消息对象
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PostMapping
|
||||
public ResultDomain<TbSysMessage> createMessage(@RequestBody TbSysMessage message) {
|
||||
logger.info("创建消息:{}", message.getTitle());
|
||||
return messageService.createMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息(仅草稿状态)
|
||||
*
|
||||
* @param message 消息对象
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PutMapping
|
||||
public ResultDomain<TbSysMessage> updateMessage(@RequestBody TbSysMessage message) {
|
||||
logger.info("更新消息:{}", message.getMessageID());
|
||||
return messageService.updateMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@DeleteMapping
|
||||
public ResultDomain<TbSysMessage> deleteMessage(@RequestBody TbSysMessage message) {
|
||||
return messageService.deleteMessage(message.getMessageID());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询消息详情
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<MessageVO>
|
||||
*/
|
||||
@GetMapping("/detail/{messageID}")
|
||||
public ResultDomain<MessageVO> getMessageById(@PathVariable String messageID) {
|
||||
return messageService.getMessageById(messageID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询消息列表(管理端)
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @return ResultDomain<MessageVO>
|
||||
*/
|
||||
@PostMapping("/page")
|
||||
public ResultDomain<MessageVO> getMessagePage(@RequestBody TbSysMessage filter, PageParam pageParam) {
|
||||
return messageService.getMessagePage(filter, pageParam);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================== 消息发送接口 ==================
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PostMapping("/send/{messageID}")
|
||||
public ResultDomain<TbSysMessage> sendMessage(@PathVariable String messageID) {
|
||||
logger.info("发送消息:{}", messageID);
|
||||
return messageService.sendMessage(messageID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即发送(定时消息改为立即发送)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PostMapping("/sendNow/{messageID}")
|
||||
public ResultDomain<TbSysMessage> sendNow(@PathVariable String messageID) {
|
||||
logger.info("立即发送消息:{}", messageID);
|
||||
return messageService.sendNow(messageID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消定时消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PostMapping("/cancel/{messageID}")
|
||||
public ResultDomain<TbSysMessage> cancelMessage(@PathVariable String messageID) {
|
||||
logger.info("取消消息:{}", messageID);
|
||||
return messageService.cancelMessage(messageID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改定时发送时间
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @param request 包含scheduledTime的请求对象
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PutMapping("/reschedule/{messageID}")
|
||||
public ResultDomain<TbSysMessage> rescheduleMessage(@PathVariable String messageID,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
logger.info("修改消息发送时间:{}", messageID);
|
||||
Date scheduledTime = (Date) request.get("scheduledTime");
|
||||
return messageService.rescheduleMessage(messageID, scheduledTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试失败消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessage>
|
||||
*/
|
||||
@PostMapping("/retry/{messageID}")
|
||||
public ResultDomain<TbSysMessage> retryMessage(@PathVariable String messageID) {
|
||||
logger.info("重试消息:{}", messageID);
|
||||
return messageService.retryMessage(messageID);
|
||||
}
|
||||
|
||||
// ================== 用户消息接口(用户端) ==================
|
||||
|
||||
/**
|
||||
* 分页查询我的消息列表
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @return ResultDomain<MessageUserVO>
|
||||
*/
|
||||
@PostMapping("/my/page")
|
||||
public ResultDomain<MessageUserVO> getMyMessagesPage(@RequestBody PageRequest<MessageUserVO> pageRequest) {
|
||||
return messageService.getMyMessagesPage(pageRequest.getFilter(), pageRequest.getPageParam());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的消息详情
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<MessageUserVO>
|
||||
*/
|
||||
@GetMapping("/my/detail/{messageID}")
|
||||
public ResultDomain<MessageUserVO> getMyMessageDetail(@PathVariable String messageID) {
|
||||
return messageService.getMyMessageDetail(messageID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记消息为已读
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return ResultDomain<TbSysMessageUser>
|
||||
*/
|
||||
@PostMapping("/my/markRead/{messageID}")
|
||||
public ResultDomain<TbSysMessageUser> markAsRead(@PathVariable String messageID) {
|
||||
return messageService.markAsRead(messageID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量标记消息为已读
|
||||
*
|
||||
* @param request 包含messageIDs的请求对象
|
||||
* @return ResultDomain<Integer>
|
||||
*/
|
||||
@PostMapping("/my/batchMarkRead")
|
||||
public ResultDomain<Integer> batchMarkAsRead(@RequestBody Map<String, List<String>> request) {
|
||||
List<String> messageIDs = request.get("messageIDs");
|
||||
return messageService.batchMarkAsRead(messageIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读消息数量
|
||||
*
|
||||
* @return ResultDomain<Integer>
|
||||
*/
|
||||
@GetMapping("/my/unreadCount")
|
||||
public ResultDomain<Integer> getUnreadCount() {
|
||||
return messageService.getUnreadCount();
|
||||
}
|
||||
|
||||
// ================== 辅助接口 ==================
|
||||
|
||||
/**
|
||||
* 获取可选的部门树
|
||||
*
|
||||
* @return ResultDomain<Map>
|
||||
*/
|
||||
@GetMapping("/targets/depts")
|
||||
public ResultDomain<Map<String, Object>> getTargetDepts() {
|
||||
return messageService.getTargetDepts();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可选的角色列表
|
||||
*
|
||||
* @return ResultDomain<Map>
|
||||
*/
|
||||
@GetMapping("/targets/roles")
|
||||
public ResultDomain<Map<String, Object>> getTargetRoles() {
|
||||
return messageService.getTargetRoles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可选的用户列表
|
||||
*
|
||||
* @return ResultDomain<Map>
|
||||
*/
|
||||
@GetMapping("/targets/users")
|
||||
public ResultDomain<Map<String, Object>> getTargetUsers() {
|
||||
return messageService.getTargetUsers();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package org.xyzh.message.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.xyzh.common.dto.message.TbSysMessage;
|
||||
import org.xyzh.common.dto.message.MessageVO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息Mapper接口
|
||||
*
|
||||
* @description 消息主体表的数据访问接口
|
||||
* @filename MessageMapper.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Mapper
|
||||
public interface MessageMapper extends BaseMapper<TbSysMessage> {
|
||||
|
||||
/**
|
||||
* 根据消息ID查询消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return TbSysMessage 消息信息
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
TbSysMessage selectMessageById(@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 插入消息
|
||||
*
|
||||
* @param message 消息信息
|
||||
* @return int 影响行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int insertMessage(@Param("message") TbSysMessage message);
|
||||
|
||||
/**
|
||||
* 更新消息
|
||||
*
|
||||
* @param message 消息信息
|
||||
* @return int 影响行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int updateMessage(@Param("message") TbSysMessage message);
|
||||
|
||||
/**
|
||||
* 删除消息(逻辑删除)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return int 影响行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int deleteMessage(@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 统计消息总数(带权限过滤)
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @param currentUserDeptID 当前用户部门ID
|
||||
* @return int 总数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int countMessage(@Param("filter") TbSysMessage filter,
|
||||
@Param("currentUserDeptID") String currentUserDeptID);
|
||||
|
||||
/**
|
||||
* 分页查询消息列表(带权限过滤)
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @param currentUserDeptID 当前用户部门ID
|
||||
* @return List<MessageVO> 消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<MessageVO> selectMessagePage(@Param("filter") TbSysMessage filter,
|
||||
@Param("currentUserDeptID") String currentUserDeptID);
|
||||
|
||||
/**
|
||||
* 查询消息详情
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return MessageVO 消息详情
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
MessageVO selectMessageDetail(@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 查询待发送的定时消息
|
||||
*
|
||||
* @param currentTime 当前时间
|
||||
* @return List<TbSysMessage> 待发送的消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<TbSysMessage> selectPendingScheduledMessages(@Param("currentTime") LocalDateTime currentTime);
|
||||
|
||||
/**
|
||||
* CAS更新消息状态(防止并发)
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @param expectedStatus 期望的当前状态
|
||||
* @param newStatus 新状态
|
||||
* @return int 更新的行数(1表示成功,0表示失败)
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int compareAndSetStatus(@Param("messageID") String messageID,
|
||||
@Param("expectedStatus") String expectedStatus,
|
||||
@Param("newStatus") String newStatus);
|
||||
|
||||
/**
|
||||
* 更新消息统计信息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @param sentCount 已发送数量
|
||||
* @param successCount 成功数量
|
||||
* @param failedCount 失败数量
|
||||
* @return int 更新的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int updateStatistics(@Param("messageID") String messageID,
|
||||
@Param("sentCount") Integer sentCount,
|
||||
@Param("successCount") Integer successCount,
|
||||
@Param("failedCount") Integer failedCount);
|
||||
|
||||
/**
|
||||
* 更新已读数量
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return int 更新的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int incrementReadCount(@Param("messageID") String messageID);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.xyzh.message.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.xyzh.common.dto.message.TbSysMessageTarget;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息接收对象Mapper接口
|
||||
*
|
||||
* @description 消息接收对象表的数据访问接口
|
||||
* @filename MessageTargetMapper.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Mapper
|
||||
public interface MessageTargetMapper extends BaseMapper<TbSysMessageTarget> {
|
||||
|
||||
/**
|
||||
* 根据消息ID查询接收对象列表
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return List<TbSysMessageTarget> 接收对象列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<TbSysMessageTarget> selectByMessageID(@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 批量插入接收对象
|
||||
*
|
||||
* @param targets 接收对象列表
|
||||
* @return int 插入的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int batchInsert(@Param("targets") List<TbSysMessageTarget> targets);
|
||||
|
||||
/**
|
||||
* 根据消息ID删除接收对象
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return int 删除的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int deleteByMessageID(@Param("messageID") String messageID);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.xyzh.message.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.xyzh.common.dto.message.TbSysMessageUser;
|
||||
import org.xyzh.common.dto.message.MessageUserVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户消息Mapper接口
|
||||
*
|
||||
* @description 用户接收消息表的数据访问接口
|
||||
* @filename MessageUserMapper.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Mapper
|
||||
public interface MessageUserMapper extends BaseMapper<TbSysMessageUser> {
|
||||
|
||||
/**
|
||||
* 根据消息ID查询用户消息列表
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
* @return List<MessageUserVO> 用户消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<MessageUserVO> selectByMessageID(@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 批量插入用户消息
|
||||
*
|
||||
* @param userMessages 用户消息列表
|
||||
* @return int 插入的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int batchInsert(@Param("userMessages") List<TbSysMessageUser> userMessages);
|
||||
|
||||
/**
|
||||
* 分页查询当前用户的消息列表
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @param filter 过滤条件
|
||||
* @return List<MessageUserVO> 消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<MessageUserVO> selectMyMessages(@Param("userID") String userID,
|
||||
@Param("filter") TbSysMessageUser filter);
|
||||
|
||||
/**
|
||||
* 查询当前用户的消息详情
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @param messageID 消息ID
|
||||
* @return MessageUserVO 消息详情
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
MessageUserVO selectMyMessageDetail(@Param("userID") String userID,
|
||||
@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 标记消息为已读
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @param messageID 消息ID
|
||||
* @return int 更新的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int markAsRead(@Param("userID") String userID,
|
||||
@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 批量标记消息为已读
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @param messageIDs 消息ID列表
|
||||
* @return int 更新的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int batchMarkAsRead(@Param("userID") String userID,
|
||||
@Param("messageIDs") List<String> messageIDs);
|
||||
|
||||
/**
|
||||
* 查询未读消息数量
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @return Integer 未读消息数量
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
Integer countUnread(@Param("userID") String userID);
|
||||
|
||||
/**
|
||||
* 动态计算未读消息数量(基于 target 配置)
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @return Integer 未读消息数量
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
Integer countUnreadWithDynamicTargets(@Param("userID") String userID);
|
||||
|
||||
/**
|
||||
* 更新用户消息的发送状态
|
||||
*
|
||||
* @param id 用户消息ID
|
||||
* @param sendStatus 发送状态
|
||||
* @param failReason 失败原因(可选)
|
||||
* @return int 更新的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int updateSendStatus(@Param("id") String id,
|
||||
@Param("sendStatus") String sendStatus,
|
||||
@Param("failReason") String failReason);
|
||||
|
||||
/**
|
||||
* 查询待发送的用户消息列表
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
* @return List<TbSysMessageUser> 用户消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<TbSysMessageUser> selectPendingUserMessages(@Param("filter") TbSysMessageUser filter);
|
||||
|
||||
/**
|
||||
* 动态查询当前用户可见的消息列表(基于 target 配置计算)
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @param filter 过滤条件
|
||||
* @return List<MessageUserVO> 消息列表
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
List<MessageUserVO> selectMyMessagesWithDynamicTargets(@Param("userID") String userID,
|
||||
@Param("filter") MessageUserVO filter);
|
||||
|
||||
/**
|
||||
* 查询或创建用户消息记录(upsert)
|
||||
*
|
||||
* @param userID 用户ID
|
||||
* @param messageID 消息ID
|
||||
* @return MessageUserVO 用户消息记录
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
MessageUserVO selectOrCreateUserMessage(@Param("userID") String userID,
|
||||
@Param("messageID") String messageID);
|
||||
|
||||
/**
|
||||
* 插入用户消息记录(如果不存在)
|
||||
*
|
||||
* @param userMessage 用户消息
|
||||
* @return int 插入的行数
|
||||
* @author Claude
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
int insertIfNotExists(@Param("userMessage") TbSysMessageUser userMessage);
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package org.xyzh.message.scheduler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.common.dto.message.TbSysMessage;
|
||||
import org.xyzh.message.mapper.MessageMapper;
|
||||
import org.xyzh.message.service.impl.MessageSendService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息定时任务扫描器
|
||||
*
|
||||
* @description 定时扫描待发送的定时消息并触发发送
|
||||
* @filename MessageScheduler.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Component
|
||||
public class MessageScheduler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageScheduler.class);
|
||||
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageSendService messageSendService;
|
||||
|
||||
/**
|
||||
* 扫描待发送的定时消息
|
||||
* 每分钟执行一次
|
||||
*/
|
||||
@Scheduled(cron = "0 * * * * ?")
|
||||
public void scanPendingScheduledMessages() {
|
||||
try {
|
||||
logger.debug("开始扫描待发送的定时消息...");
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
// 查询满足条件的消息:
|
||||
// 1. status=pending(待发送)
|
||||
// 2. sendMode=scheduled(定时发送)
|
||||
// 3. scheduledTime <= 当前时间
|
||||
// 4. deleted=0(未删除)
|
||||
List<TbSysMessage> messages = messageMapper.selectPendingScheduledMessages(now);
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
logger.debug("没有待发送的定时消息");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("发现 {} 条待发送的定时消息", messages.size());
|
||||
|
||||
// 处理每条消息
|
||||
for (TbSysMessage message : messages) {
|
||||
try {
|
||||
// 使用CAS(Compare-And-Set)更新状态,防止并发重复触发
|
||||
boolean updated = messageMapper.compareAndSetStatus(
|
||||
message.getMessageID(),
|
||||
"pending", // 期望的当前状态
|
||||
"sending" // 新状态
|
||||
) > 0;
|
||||
|
||||
if (!updated) {
|
||||
logger.warn("消息 {} 状态已被修改,跳过处理", message.getMessageID());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 更新实际发送时间和状态
|
||||
message.setActualSendTime(new Date());
|
||||
message.setStatus("sending");
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
// 异步发送消息
|
||||
messageSendService.sendMessageAsync(message.getMessageID());
|
||||
|
||||
logger.info("定时消息 {} 已触发发送", message.getMessageID());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("处理定时消息失败:{}", message.getMessageID(), e);
|
||||
handleSendError(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("定时消息扫描完成,已处理 {} 条消息", messages.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("扫描定时消息时发生错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理发送失败,支持重试机制
|
||||
*
|
||||
* @param message 消息对象
|
||||
* @param e 异常信息
|
||||
*/
|
||||
private void handleSendError(TbSysMessage message, Exception e) {
|
||||
try {
|
||||
int retryCount = message.getRetryCount() != null ? message.getRetryCount() : 0;
|
||||
int maxRetryCount = message.getMaxRetryCount() != null ? message.getMaxRetryCount() : 3;
|
||||
|
||||
if (retryCount < maxRetryCount) {
|
||||
// 未达到最大重试次数,增加重试计数
|
||||
message.setRetryCount(retryCount + 1);
|
||||
message.setStatus("pending"); // 重新置为待发送状态
|
||||
|
||||
// 设置下次重试时间(5分钟后)
|
||||
LocalDateTime nextRetryTime = LocalDateTime.now().plusMinutes(5);
|
||||
message.setScheduledTime(convertToDate(nextRetryTime));
|
||||
|
||||
message.setLastError(e.getMessage());
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
logger.warn("消息 {} 发送失败,将在5分钟后重试 ({}/{})",
|
||||
message.getMessageID(), retryCount + 1, maxRetryCount);
|
||||
|
||||
} else {
|
||||
// 超过最大重试次数,标记为失败
|
||||
message.setStatus("failed");
|
||||
message.setLastError("超过最大重试次数:" + e.getMessage());
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
logger.error("消息 {} 发送失败且已超过最大重试次数", message.getMessageID());
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.error("处理发送错误时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将LocalDateTime转换为Date
|
||||
*/
|
||||
private Date convertToDate(LocalDateTime localDateTime) {
|
||||
return java.sql.Timestamp.valueOf(localDateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每小时清理一次已取消和已失败的旧消息(可选功能)
|
||||
* 将超过30天的已取消/失败消息标记为删除
|
||||
*/
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
public void cleanupOldMessages() {
|
||||
try {
|
||||
logger.debug("开始清理旧消息...");
|
||||
|
||||
// 计算30天前的时间
|
||||
LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30);
|
||||
|
||||
// TODO: 实现清理逻辑
|
||||
// 清理条件:
|
||||
// 1. status in ('cancelled', 'failed')
|
||||
// 2. updateTime < 30天前
|
||||
// 3. deleted = 0
|
||||
|
||||
logger.debug("旧消息清理完成");
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("清理旧消息时发生错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每天凌晨生成消息统计报告(可选功能)
|
||||
*/
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void generateDailyReport() {
|
||||
try {
|
||||
logger.info("开始生成每日消息统计报告...");
|
||||
|
||||
// TODO: 实现统计报告生成逻辑
|
||||
// 统计内容:
|
||||
// 1. 今日发送消息总数
|
||||
// 2. 发送成功率
|
||||
// 3. 已读率
|
||||
// 4. 各发送方式的使用情况
|
||||
|
||||
logger.info("每日消息统计报告生成完成");
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("生成每日报告时发生错误", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
package org.xyzh.message.service.impl;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.xyzh.common.dto.message.TbSysMessage;
|
||||
import org.xyzh.common.dto.message.TbSysMessageUser;
|
||||
import org.xyzh.common.dto.user.TbSysUser;
|
||||
import org.xyzh.common.utils.EmailUtils;
|
||||
import org.xyzh.common.utils.SmsUtils;
|
||||
import org.xyzh.message.mapper.MessageMapper;
|
||||
import org.xyzh.message.mapper.MessageUserMapper;
|
||||
import org.xyzh.system.mapper.UserMapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息发送服务
|
||||
*
|
||||
* @description 异步发送消息的服务类
|
||||
* @filename MessageSendService.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class MessageSendService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageSendService.class);
|
||||
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageUserMapper messageUserMapper;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private EmailUtils emailUtils;
|
||||
|
||||
@Autowired
|
||||
private SmsUtils smsUtils;
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param messageID 消息ID
|
||||
*/
|
||||
@Async("messageAsyncExecutor")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void sendMessageAsync(String messageID) {
|
||||
try {
|
||||
logger.info("开始发送消息:{}", messageID);
|
||||
|
||||
// 1. 查询消息
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
logger.error("消息不存在:{}", messageID);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 查询所有待发送的用户消息
|
||||
TbSysMessageUser filter = new TbSysMessageUser();
|
||||
filter.setMessageID(messageID);
|
||||
filter.setSendStatus("pending");
|
||||
filter.setDeleted(false);
|
||||
List<TbSysMessageUser> userMessages = messageUserMapper.selectPendingUserMessages(filter);
|
||||
|
||||
if (userMessages.isEmpty()) {
|
||||
logger.warn("没有待发送的用户消息:{}", messageID);
|
||||
updateMessageStatus(message, "sent");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 遍历发送
|
||||
int successCount = 0;
|
||||
int failedCount = 0;
|
||||
|
||||
for (TbSysMessageUser userMessage : userMessages) {
|
||||
try {
|
||||
// 查询用户信息
|
||||
TbSysUser user = userMapper.selectUserById(userMessage.getUserID());
|
||||
if (user == null || user.getDeleted()) {
|
||||
logger.warn("用户不存在:{}", userMessage.getUserID());
|
||||
updateUserMessageStatus(userMessage.getID(), "failed", "用户不存在");
|
||||
failedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据发送方式发送消息
|
||||
String[] methods = userMessage.getSendMethod().split(",");
|
||||
boolean sent = false;
|
||||
|
||||
for (String method : methods) {
|
||||
method = method.trim();
|
||||
try {
|
||||
switch (method) {
|
||||
case "system":
|
||||
// 系统消息已在数据库中,无需额外操作
|
||||
sent = true;
|
||||
break;
|
||||
|
||||
case "email":
|
||||
sent = sendEmail(user, message);
|
||||
break;
|
||||
|
||||
case "sms":
|
||||
sent = sendSms(user, message);
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.warn("未知的发送方式:{}", method);
|
||||
}
|
||||
|
||||
if (sent) {
|
||||
break; // 任意一种方式成功即可
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("发送消息失败 [{}] - 用户:{}, 方式:{}", messageID, user.getUsername(), method, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户消息发送状态
|
||||
if (sent) {
|
||||
updateUserMessageStatus(userMessage.getID(), "success", null);
|
||||
successCount++;
|
||||
} else {
|
||||
updateUserMessageStatus(userMessage.getID(), "failed", "所有发送方式均失败");
|
||||
failedCount++;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("处理用户消息失败:{}", userMessage.getID(), e);
|
||||
updateUserMessageStatus(userMessage.getID(), "failed", e.getMessage());
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 更新消息统计信息
|
||||
int sentCount = successCount + failedCount;
|
||||
messageMapper.updateStatistics(messageID, sentCount, successCount, failedCount);
|
||||
|
||||
// 5. 更新消息状态
|
||||
if (failedCount == userMessages.size()) {
|
||||
// 全部失败
|
||||
updateMessageStatus(message, "failed");
|
||||
} else {
|
||||
// 至少有一个成功
|
||||
updateMessageStatus(message, "sent");
|
||||
}
|
||||
|
||||
logger.info("消息发送完成:{} - 成功:{}, 失败:{}", messageID, successCount, failedCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("发送消息异常:{}", messageID, e);
|
||||
// 更新消息状态为失败
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
if (message != null && !message.getDeleted()) {
|
||||
message.setStatus("failed");
|
||||
message.setLastError(e.getMessage());
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.error("更新消息状态失败", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*
|
||||
* @param user 用户
|
||||
* @param message 消息
|
||||
* @return 是否成功
|
||||
*/
|
||||
private boolean sendEmail(TbSysUser user, TbSysMessage message) {
|
||||
try {
|
||||
if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
|
||||
logger.warn("用户 {} 没有邮箱地址", user.getUsername());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 构建HTML邮件内容
|
||||
String htmlContent = buildEmailHtml(message);
|
||||
|
||||
// 发送邮件
|
||||
boolean result = emailUtils.sendHtmlEmail(
|
||||
user.getEmail(),
|
||||
message.getTitle(),
|
||||
htmlContent
|
||||
);
|
||||
|
||||
if (result) {
|
||||
logger.info("邮件发送成功 - 用户:{}, 邮箱:{}", user.getUsername(), user.getEmail());
|
||||
} else {
|
||||
logger.warn("邮件发送失败 - 用户:{}, 邮箱:{}", user.getUsername(), user.getEmail());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("发送邮件异常 - 用户:{}", user.getUsername(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
*
|
||||
* @param user 用户
|
||||
* @param message 消息
|
||||
* @return 是否成功
|
||||
*/
|
||||
private boolean sendSms(TbSysUser user, TbSysMessage message) {
|
||||
try {
|
||||
if (user.getPhone() == null || user.getPhone().trim().isEmpty()) {
|
||||
logger.warn("用户 {} 没有手机号", user.getUsername());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 短信内容(限制长度)
|
||||
String smsContent = message.getTitle();
|
||||
if (message.getContent() != null && message.getContent().length() < 50) {
|
||||
smsContent += ":" + message.getContent();
|
||||
}
|
||||
|
||||
// 发送短信
|
||||
boolean result = smsUtils.sendSms(user.getPhone(), smsContent, "xx");
|
||||
|
||||
if (result) {
|
||||
logger.info("短信发送成功 - 用户:{}, 手机:{}", user.getUsername(), user.getPhone());
|
||||
} else {
|
||||
logger.warn("短信发送失败 - 用户:{}, 手机:{}", user.getUsername(), user.getPhone());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("发送短信异常 - 用户:{}", user.getUsername(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建邮件HTML内容
|
||||
*/
|
||||
private String buildEmailHtml(TbSysMessage message) {
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<!DOCTYPE html>");
|
||||
html.append("<html>");
|
||||
html.append("<head>");
|
||||
html.append("<meta charset=\"UTF-8\">");
|
||||
html.append("<style>");
|
||||
html.append("body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }");
|
||||
html.append(".container { max-width: 600px; margin: 20px auto; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }");
|
||||
html.append(".header { background-color: #c8232c; color: white; padding: 15px; border-radius: 5px 5px 0 0; }");
|
||||
html.append(".content { padding: 20px; background-color: #f9f9f9; }");
|
||||
html.append(".footer { text-align: center; padding: 15px; font-size: 12px; color: #999; }");
|
||||
html.append(".priority-urgent { border-left: 4px solid #ff0000; }");
|
||||
html.append(".priority-important { border-left: 4px solid #ff9900; }");
|
||||
html.append(".priority-normal { border-left: 4px solid #00aa00; }");
|
||||
html.append("</style>");
|
||||
html.append("</head>");
|
||||
html.append("<body>");
|
||||
html.append("<div class=\"container priority-").append(message.getPriority()).append("\">");
|
||||
html.append("<div class=\"header\">");
|
||||
html.append("<h2>").append(message.getTitle()).append("</h2>");
|
||||
html.append("</div>");
|
||||
html.append("<div class=\"content\">");
|
||||
html.append(message.getContent());
|
||||
html.append("</div>");
|
||||
html.append("<div class=\"footer\">");
|
||||
html.append("<p>发送人:").append(message.getSenderName()).append(" (").append(message.getSenderDeptName()).append(")</p>");
|
||||
html.append("<p>发送时间:").append(message.getActualSendTime() != null ? message.getActualSendTime().toString() : "").append("</p>");
|
||||
html.append("<p>此邮件由系统自动发送,请勿回复。</p>");
|
||||
html.append("</div>");
|
||||
html.append("</div>");
|
||||
html.append("</body>");
|
||||
html.append("</html>");
|
||||
return html.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息状态
|
||||
*/
|
||||
private void updateMessageStatus(TbSysMessage message, String status) {
|
||||
try {
|
||||
message.setStatus(status);
|
||||
if ("sent".equals(status)) {
|
||||
message.setActualSendTime(new Date());
|
||||
}
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
} catch (Exception e) {
|
||||
logger.error("更新消息状态失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户消息发送状态
|
||||
*/
|
||||
private void updateUserMessageStatus(String id, String status, String failReason) {
|
||||
try {
|
||||
messageUserMapper.updateSendStatus(id, status, failReason);
|
||||
} catch (Exception e) {
|
||||
logger.error("更新用户消息状态失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,746 @@
|
||||
package org.xyzh.message.service.impl;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xyzh.api.message.MessageService;
|
||||
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.message.*;
|
||||
import org.xyzh.common.utils.IDUtils;
|
||||
import org.xyzh.message.mapper.MessageMapper;
|
||||
import org.xyzh.message.mapper.MessageTargetMapper;
|
||||
import org.xyzh.message.mapper.MessageUserMapper;
|
||||
import org.xyzh.system.mapper.DepartmentMapper;
|
||||
import org.xyzh.system.mapper.UserMapper;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 消息服务实现类
|
||||
*
|
||||
* @description 消息通知模块的服务实现
|
||||
* @filename MessageServiceImpl.java
|
||||
* @author Claude
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
@Service
|
||||
public class MessageServiceImpl implements MessageService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageTargetMapper messageTargetMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageUserMapper messageUserMapper;
|
||||
|
||||
@Autowired
|
||||
private DepartmentMapper departmentMapper;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageSendService messageSendService;
|
||||
|
||||
/**
|
||||
* 创建消息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> createMessage(TbSysMessage message) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
// 1. 获取当前用户信息(从Session或SecurityContext获取)
|
||||
String currentUserID = getCurrentUserID();
|
||||
String currentDeptID = getCurrentUserDeptID();
|
||||
|
||||
// 2. 设置消息主体基本信息
|
||||
if (message.getID() == null) {
|
||||
message.setID(IDUtils.generateID());
|
||||
}
|
||||
if (message.getMessageID() == null) {
|
||||
message.setMessageID(IDUtils.generateID());
|
||||
}
|
||||
message.setSenderID(currentUserID);
|
||||
message.setSenderDeptID(currentDeptID);
|
||||
|
||||
// 设置状态
|
||||
if (message.getStatus() == null) {
|
||||
if ("immediate".equals(message.getSendMode())) {
|
||||
message.setStatus("sending");
|
||||
} else {
|
||||
message.setStatus("pending");
|
||||
}
|
||||
}
|
||||
|
||||
if (message.getTargetUserCount() == null) {
|
||||
message.setTargetUserCount(0);
|
||||
}
|
||||
if (message.getRetryCount() == null) {
|
||||
message.setRetryCount(0);
|
||||
}
|
||||
if (message.getMaxRetryCount() == null) {
|
||||
message.setMaxRetryCount(3);
|
||||
}
|
||||
message.setCreator(currentUserID);
|
||||
message.setCreateTime(new Date());
|
||||
message.setUpdateTime(new Date());
|
||||
message.setDeleted(false);
|
||||
|
||||
// 保存消息主体
|
||||
messageMapper.insertMessage(message);
|
||||
|
||||
// 3. 保存接收对象配置
|
||||
List<TbSysMessageTarget> targets = message.getTargets();
|
||||
if (targets != null && !targets.isEmpty()) {
|
||||
for (TbSysMessageTarget target : targets) {
|
||||
// 权限校验:scopeDeptID必须是当前部门或子部门
|
||||
if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
|
||||
rt.fail("无权向该部门发送消息");
|
||||
return rt;
|
||||
}
|
||||
|
||||
if (target.getID() == null) {
|
||||
target.setID(IDUtils.generateID());
|
||||
}
|
||||
target.setMessageID(message.getMessageID());
|
||||
target.setCreator(currentUserID);
|
||||
target.setCreateTime(new Date());
|
||||
target.setUpdateTime(new Date());
|
||||
target.setDeleted(false);
|
||||
}
|
||||
messageTargetMapper.batchInsert(targets);
|
||||
|
||||
// 4. 解析接收对象,生成用户消息列表
|
||||
List<TbSysMessageUser> userMessages = resolveTargetUsers(message.getMessageID(), targets, currentUserID);
|
||||
if (!userMessages.isEmpty()) {
|
||||
messageUserMapper.batchInsert(userMessages);
|
||||
}
|
||||
|
||||
// 5. 更新目标用户总数
|
||||
message.setTargetUserCount(userMessages.size());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
// 6. 如果是立即发送,异步发送消息
|
||||
if ("immediate".equals(message.getSendMode())) {
|
||||
messageSendService.sendMessageAsync(message.getMessageID());
|
||||
}
|
||||
}
|
||||
|
||||
rt.success("创建成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("创建消息失败", e);
|
||||
rt.fail("创建消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息(仅允许更新草稿状态的消息)
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> updateMessage(TbSysMessage message) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
// 查询原消息
|
||||
TbSysMessage existingMessage = messageMapper.selectMessageById(message.getMessageID());
|
||||
if (existingMessage == null || existingMessage.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
// 只允许更新草稿状态的消息
|
||||
if (!"draft".equals(existingMessage.getStatus())) {
|
||||
rt.fail("只能更新草稿状态的消息");
|
||||
return rt;
|
||||
}
|
||||
|
||||
message.setUpdateTime(new Date());
|
||||
message.setUpdater(getCurrentUserID());
|
||||
messageMapper.updateMessage(message);
|
||||
rt.success("更新成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("更新消息失败", e);
|
||||
rt.fail("更新消息失败:"+e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息(逻辑删除)
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> deleteMessage(String messageID) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
int result = messageMapper.deleteMessage(messageID);
|
||||
if (result > 0) {
|
||||
// 同时删除关联的接收对象和用户消息
|
||||
messageTargetMapper.deleteByMessageID(messageID);
|
||||
rt.success("删除成功", message);
|
||||
} else {
|
||||
rt.fail("删除失败");
|
||||
}
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("删除消息失败", e);
|
||||
rt.fail("删除消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询消息详情
|
||||
*/
|
||||
@Override
|
||||
public ResultDomain<MessageVO> getMessageById(String messageID) {
|
||||
ResultDomain<MessageVO> rt = new ResultDomain<>();
|
||||
try {
|
||||
MessageVO messageVO = messageMapper.selectMessageDetail(messageID);
|
||||
if (messageVO == null) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
// 查询接收对象列表
|
||||
List<TbSysMessageTarget> targets = messageTargetMapper.selectByMessageID(messageID);
|
||||
messageVO.setTargets(targets);
|
||||
|
||||
// 查询用户接收记录
|
||||
List<MessageUserVO> userMessages = messageUserMapper.selectByMessageID(messageID);
|
||||
messageVO.setUserMessages(userMessages);
|
||||
|
||||
rt.success("查询成功", messageVO);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询消息详情失败", e);
|
||||
rt.fail("查询消息详情失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询消息列表(管理端)
|
||||
*/
|
||||
@Override
|
||||
public ResultDomain<MessageVO> getMessagePage(TbSysMessage filter, PageParam pageParam) {
|
||||
ResultDomain<MessageVO> rt = new ResultDomain<>();
|
||||
try {
|
||||
if (filter == null) {
|
||||
filter = new TbSysMessage();
|
||||
}
|
||||
filter.setDeleted(false);
|
||||
|
||||
if (pageParam == null) {
|
||||
pageParam = new PageParam();
|
||||
}
|
||||
|
||||
String currentDeptID = getCurrentUserDeptID();
|
||||
|
||||
List<MessageVO> list = messageMapper.selectMessagePage(filter, currentDeptID);
|
||||
int total = messageMapper.countMessage(filter, currentDeptID);
|
||||
|
||||
PageDomain<MessageVO> pageDomain = new PageDomain<>();
|
||||
pageDomain.setDataList(list);
|
||||
pageParam.setTotalElements(total);
|
||||
pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
|
||||
pageDomain.setPageParam(pageParam);
|
||||
|
||||
rt.success("查询成功", pageDomain);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询消息列表失败", e);
|
||||
rt.fail("查询消息列表失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> sendMessage(String messageID) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
// 更新状态为发送中
|
||||
message.setStatus("sending");
|
||||
message.setActualSendTime(new Date());
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
// 异步发送
|
||||
messageSendService.sendMessageAsync(messageID);
|
||||
|
||||
rt.success("发送成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("发送消息失败", e);
|
||||
rt.fail("发送消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即发送(将定时消息改为立即发送)
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> sendNow(String messageID) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
if (!"pending".equals(message.getStatus())) {
|
||||
rt.fail("只能立即发送待发送状态的消息");
|
||||
return rt;
|
||||
}
|
||||
|
||||
// 更新为立即发送模式
|
||||
message.setSendMode("immediate");
|
||||
message.setStatus("sending");
|
||||
message.setActualSendTime(new Date());
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
// 异步发送
|
||||
messageSendService.sendMessageAsync(messageID);
|
||||
|
||||
rt.success("立即发送成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("立即发送消息失败", e);
|
||||
rt.fail("立即发送消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消定时消息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> cancelMessage(String messageID) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
if (!"pending".equals(message.getStatus())) {
|
||||
rt.fail("只能取消待发送状态的消息");
|
||||
return rt;
|
||||
}
|
||||
|
||||
message.setStatus("cancelled");
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
rt.success("取消成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("取消消息失败", e);
|
||||
rt.fail("取消消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改定时发送时间
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> rescheduleMessage(String messageID, Date scheduledTime) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
if (!"pending".equals(message.getStatus())) {
|
||||
rt.fail("只能修改待发送状态的消息");
|
||||
return rt;
|
||||
}
|
||||
|
||||
message.setScheduledTime(scheduledTime);
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
rt.success("修改成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("修改定时时间失败", e);
|
||||
rt.fail("修改定时时间失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试失败消息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessage> retryMessage(String messageID) {
|
||||
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
|
||||
try {
|
||||
TbSysMessage message = messageMapper.selectMessageById(messageID);
|
||||
|
||||
if (message == null || message.getDeleted()) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
if (!"failed".equals(message.getStatus())) {
|
||||
rt.fail("只能重试失败状态的消息");
|
||||
return rt;
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
message.setStatus("sending");
|
||||
message.setRetryCount(message.getRetryCount() + 1);
|
||||
message.setUpdateTime(new Date());
|
||||
messageMapper.updateMessage(message);
|
||||
|
||||
// 异步发送
|
||||
messageSendService.sendMessageAsync(messageID);
|
||||
|
||||
rt.success("重试成功", message);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("重试消息失败", e);
|
||||
rt.fail("重试消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
// ================== 用户消息相关方法 ==================
|
||||
|
||||
@Override
|
||||
public ResultDomain<MessageUserVO> getMyMessagesPage(MessageUserVO filter, PageParam pageParam) {
|
||||
ResultDomain<MessageUserVO> rt = new ResultDomain<>();
|
||||
try {
|
||||
if (filter == null) {
|
||||
filter = new MessageUserVO();
|
||||
}
|
||||
|
||||
if (pageParam == null) {
|
||||
pageParam = new PageParam();
|
||||
}
|
||||
|
||||
String currentUserID = getCurrentUserID();
|
||||
|
||||
// 使用新的动态查询方法
|
||||
List<MessageUserVO> list = messageUserMapper.selectMyMessagesWithDynamicTargets(currentUserID, filter);
|
||||
|
||||
PageDomain<MessageUserVO> pageDomain = new PageDomain<>();
|
||||
pageDomain.setDataList(list);
|
||||
pageParam.setTotalElements(list.size());
|
||||
pageParam.setTotalPages((int) Math.ceil((double) list.size() / pageParam.getPageSize()));
|
||||
pageDomain.setPageParam(pageParam);
|
||||
|
||||
rt.success("查询成功", pageDomain);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询我的消息失败", e);
|
||||
rt.fail("查询我的消息失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<MessageUserVO> getMyMessageDetail(String messageID) {
|
||||
ResultDomain<MessageUserVO> rt = new ResultDomain<>();
|
||||
try {
|
||||
String currentUserID = getCurrentUserID();
|
||||
MessageUserVO messageUserVO = messageUserMapper.selectOrCreateUserMessage(currentUserID, messageID);
|
||||
|
||||
if (messageUserVO == null) {
|
||||
rt.fail("消息不存在");
|
||||
return rt;
|
||||
}
|
||||
|
||||
// 如果用户消息记录不存在(id 为 null),创建新记录
|
||||
if (messageUserVO.getId() == null) {
|
||||
logger.info("用户首次查看消息,创建用户消息记录:userID={}, messageID={}", currentUserID, messageID);
|
||||
|
||||
TbSysMessageUser userMessage = new TbSysMessageUser();
|
||||
userMessage.setID(IDUtils.generateID());
|
||||
userMessage.setMessageID(messageID);
|
||||
userMessage.setUserID(currentUserID);
|
||||
userMessage.setSendMethod("system"); // 默认发送方式
|
||||
userMessage.setIsRead(false); // 初始为未读
|
||||
userMessage.setSendStatus("success");
|
||||
userMessage.setCreator(currentUserID);
|
||||
userMessage.setCreateTime(new Date());
|
||||
userMessage.setUpdateTime(new Date());
|
||||
userMessage.setDeleted(false);
|
||||
|
||||
// 插入新记录
|
||||
messageUserMapper.insertIfNotExists(userMessage);
|
||||
|
||||
// 重新查询以获取完整信息
|
||||
messageUserVO = messageUserMapper.selectMyMessageDetail(currentUserID, messageID);
|
||||
}
|
||||
|
||||
rt.success("查询成功", messageUserVO);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询消息详情失败", e);
|
||||
rt.fail("查询消息详情失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<TbSysMessageUser> markAsRead(String messageID) {
|
||||
ResultDomain<TbSysMessageUser> rt = new ResultDomain<>();
|
||||
try {
|
||||
String currentUserID = getCurrentUserID();
|
||||
|
||||
// 先尝试更新已有记录
|
||||
int result = messageUserMapper.markAsRead(currentUserID, messageID);
|
||||
|
||||
// 如果没有更新任何记录,说明用户消息记录不存在,需要先插入
|
||||
if (result == 0) {
|
||||
logger.info("用户消息记录不存在,创建新记录:userID={}, messageID={}", currentUserID, messageID);
|
||||
|
||||
TbSysMessageUser userMessage = new TbSysMessageUser();
|
||||
userMessage.setID(IDUtils.generateID());
|
||||
userMessage.setMessageID(messageID);
|
||||
userMessage.setUserID(currentUserID);
|
||||
userMessage.setSendMethod("system"); // 默认发送方式
|
||||
userMessage.setIsRead(true); // 直接设置为已读
|
||||
userMessage.setSendStatus("success");
|
||||
userMessage.setCreator(currentUserID);
|
||||
userMessage.setCreateTime(new Date());
|
||||
userMessage.setUpdateTime(new Date());
|
||||
userMessage.setDeleted(false);
|
||||
|
||||
// 插入新记录
|
||||
int insertResult = messageUserMapper.insertIfNotExists(userMessage);
|
||||
|
||||
if (insertResult > 0) {
|
||||
// 插入成功后再次标记为已读(设置 read_time)
|
||||
messageUserMapper.markAsRead(currentUserID, messageID);
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (result > 0) {
|
||||
// 更新消息的已读数量
|
||||
messageMapper.incrementReadCount(messageID);
|
||||
TbSysMessageUser messageUser = new TbSysMessageUser();
|
||||
rt.success("标记成功", messageUser);
|
||||
} else {
|
||||
rt.fail("标记失败");
|
||||
}
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("标记已读失败", e);
|
||||
rt.fail("标记已读失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResultDomain<Integer> batchMarkAsRead(List<String> messageIDs) {
|
||||
ResultDomain<Integer> rt = new ResultDomain<>();
|
||||
try {
|
||||
String currentUserID = getCurrentUserID();
|
||||
int count = messageUserMapper.batchMarkAsRead(currentUserID, messageIDs);
|
||||
|
||||
// 更新每条消息的已读数量
|
||||
for (String messageID : messageIDs) {
|
||||
messageMapper.incrementReadCount(messageID);
|
||||
}
|
||||
|
||||
rt.success("批量标记成功", count);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("批量标记已读失败", e);
|
||||
rt.fail("批量标记已读失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Integer> getUnreadCount() {
|
||||
ResultDomain<Integer> rt = new ResultDomain<>();
|
||||
try {
|
||||
String currentUserID = getCurrentUserID();
|
||||
// 使用动态计算方法,统计用户应该看到的所有未读消息
|
||||
Integer count = messageUserMapper.countUnreadWithDynamicTargets(currentUserID);
|
||||
rt.success("查询成功", count != null ? count : 0);
|
||||
return rt;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询未读数量失败", e);
|
||||
rt.fail("查询未读数量失败:" + e.getMessage());
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
// ================== 辅助方法 ==================
|
||||
|
||||
@Override
|
||||
public ResultDomain<Map<String, Object>> getTargetDepts() {
|
||||
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
|
||||
// TODO: 实现获取可选部门树
|
||||
rt.success("查询成功", new HashMap<>());
|
||||
return rt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Map<String, Object>> getTargetRoles() {
|
||||
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
|
||||
// TODO: 实现获取可选角色列表
|
||||
rt.success("查询成功", new HashMap<>());
|
||||
return rt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Map<String, Object>> getTargetUsers() {
|
||||
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
|
||||
// TODO: 实现获取可选用户列表
|
||||
rt.success("查询成功", new HashMap<>());
|
||||
return rt;
|
||||
}
|
||||
|
||||
// ================== 私有辅助方法 ==================
|
||||
|
||||
/**
|
||||
* 解析接收对象,生成用户消息列表
|
||||
*/
|
||||
private List<TbSysMessageUser> resolveTargetUsers(String messageID, List<TbSysMessageTarget> targets, String creator) {
|
||||
Set<String> userIDSet = new HashSet<>();
|
||||
Map<String, String> userMethodMap = new HashMap<>();
|
||||
|
||||
for (TbSysMessageTarget target : targets) {
|
||||
List<String> userIDs = new ArrayList<>();
|
||||
|
||||
switch (target.getTargetType()) {
|
||||
case "dept":
|
||||
// 查询该部门及所有子部门的用户
|
||||
userIDs = userMapper.selectUserIdsByDeptId(target.getTargetID());
|
||||
logger.info("部门 {} 解析到 {} 个用户", target.getTargetID(), userIDs.size());
|
||||
break;
|
||||
case "role":
|
||||
// 查询scopeDeptID及子部门中该角色的用户
|
||||
String scopeDeptID = target.getScopeDeptID();
|
||||
if (scopeDeptID == null || scopeDeptID.isEmpty()) {
|
||||
logger.warn("角色目标缺少 scopeDeptID,跳过:{}", target.getTargetID());
|
||||
break;
|
||||
}
|
||||
userIDs = userMapper.selectUserIdsByDeptRole(scopeDeptID, target.getTargetID());
|
||||
logger.info("部门 {} 中角色 {} 解析到 {} 个用户", scopeDeptID, target.getTargetID(), userIDs.size());
|
||||
break;
|
||||
case "user":
|
||||
userIDs.add(target.getTargetID());
|
||||
break;
|
||||
}
|
||||
|
||||
for (String userID : userIDs) {
|
||||
userIDSet.add(userID);
|
||||
userMethodMap.put(userID, target.getSendMethod());
|
||||
}
|
||||
}
|
||||
|
||||
// 生成用户消息列表
|
||||
List<TbSysMessageUser> userMessages = new ArrayList<>();
|
||||
for (String userID : userIDSet) {
|
||||
TbSysMessageUser userMessage = new TbSysMessageUser();
|
||||
userMessage.setID(IDUtils.generateID());
|
||||
userMessage.setMessageID(messageID);
|
||||
userMessage.setUserID(userID);
|
||||
userMessage.setSendMethod(userMethodMap.get(userID));
|
||||
userMessage.setIsRead(false);
|
||||
userMessage.setSendStatus("pending");
|
||||
userMessage.setCreator(creator);
|
||||
userMessage.setCreateTime(new Date());
|
||||
userMessage.setUpdateTime(new Date());
|
||||
userMessage.setDeleted(false);
|
||||
userMessages.add(userMessage);
|
||||
}
|
||||
|
||||
logger.info("消息 {} 共解析到 {} 个目标用户", messageID, userMessages.size());
|
||||
return userMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查目标部门是否是当前部门或子部门
|
||||
*/
|
||||
private boolean isCurrentOrSubDept(String currentDeptID, String targetDeptID) {
|
||||
// TODO: 实现部门层级检查
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户ID
|
||||
*/
|
||||
private String getCurrentUserID() {
|
||||
// TODO: 从SecurityContext或Session获取
|
||||
return "1";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户部门ID
|
||||
*/
|
||||
private String getCurrentUserDeptID() {
|
||||
// TODO: 从SecurityContext或Session获取
|
||||
return "root_department";
|
||||
}
|
||||
}
|
||||
44
schoolNewsServ/message/src/main/resources/application.yml
Normal file
44
schoolNewsServ/message/src/main/resources/application.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
# Message模块配置文件
|
||||
# 此配置文件定义message模块独立运行时的配置
|
||||
# 如需作为微服务集成,请参考admin模块的bootstrap.yml配置
|
||||
|
||||
# 消息模块配置
|
||||
message:
|
||||
# 定时任务扫描配置
|
||||
scheduler:
|
||||
# 扫描频率(cron表达式),默认每分钟扫描一次
|
||||
cron: "0 * * * * ?"
|
||||
# 是否启用定时任务扫描
|
||||
enabled: true
|
||||
|
||||
# 消息发送配置
|
||||
send:
|
||||
# 异步发送线程池大小
|
||||
thread-pool-size: 10
|
||||
# 批量发送每批次大小
|
||||
batch-size: 100
|
||||
|
||||
# Spring Boot配置(如果需要独立运行)
|
||||
# server:
|
||||
# port: 8087
|
||||
#
|
||||
# spring:
|
||||
# application:
|
||||
# name: message-service
|
||||
#
|
||||
# datasource:
|
||||
# url: jdbc:mysql://localhost:3306/school_news?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
# username: root
|
||||
# password: your_password
|
||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# hikari:
|
||||
# maximum-pool-size: 20
|
||||
# minimum-idle: 5
|
||||
# connection-timeout: 30000
|
||||
#
|
||||
# mybatis-plus:
|
||||
# mapper-locations: classpath:mapper/*.xml
|
||||
# type-aliases-package: org.xyzh.common.dto.message
|
||||
# configuration:
|
||||
# map-underscore-to-camel-case: true
|
||||
# log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||
@@ -0,0 +1,279 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="org.xyzh.message.mapper.MessageMapper">
|
||||
|
||||
<!-- Result Map -->
|
||||
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.message.TbSysMessage">
|
||||
<id column="id" property="id" jdbcType="VARCHAR"/>
|
||||
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
|
||||
<result column="title" property="title" jdbcType="VARCHAR"/>
|
||||
<result column="content" property="content" jdbcType="VARCHAR"/>
|
||||
<result column="message_type" property="messageType" jdbcType="VARCHAR"/>
|
||||
<result column="priority" property="priority" jdbcType="VARCHAR"/>
|
||||
<result column="sender_id" property="senderID" jdbcType="VARCHAR"/>
|
||||
<result column="sender_name" property="senderName" jdbcType="VARCHAR"/>
|
||||
<result column="sender_dept_id" property="senderDeptID" jdbcType="VARCHAR"/>
|
||||
<result column="sender_dept_name" property="senderDeptName" jdbcType="VARCHAR"/>
|
||||
<result column="send_mode" property="sendMode" jdbcType="VARCHAR"/>
|
||||
<result column="scheduled_time" property="scheduledTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="actual_send_time" property="actualSendTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="status" property="status" jdbcType="VARCHAR"/>
|
||||
<result column="target_user_count" property="targetUserCount" jdbcType="INTEGER"/>
|
||||
<result column="sent_count" property="sentCount" jdbcType="INTEGER"/>
|
||||
<result column="success_count" property="successCount" jdbcType="INTEGER"/>
|
||||
<result column="failed_count" property="failedCount" jdbcType="INTEGER"/>
|
||||
<result column="read_count" property="readCount" jdbcType="INTEGER"/>
|
||||
<result column="retry_count" property="retryCount" jdbcType="INTEGER"/>
|
||||
<result column="max_retry_count" property="maxRetryCount" jdbcType="INTEGER"/>
|
||||
<result column="last_error" property="lastError" jdbcType="VARCHAR"/>
|
||||
<result column="creator" property="creator" jdbcType="VARCHAR"/>
|
||||
<result column="updater" property="updater" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="MessageVOMap" type="org.xyzh.common.dto.message.MessageVO">
|
||||
<id column="id" property="id" jdbcType="VARCHAR"/>
|
||||
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
|
||||
<result column="title" property="title" jdbcType="VARCHAR"/>
|
||||
<result column="content" property="content" jdbcType="VARCHAR"/>
|
||||
<result column="message_type" property="messageType" jdbcType="VARCHAR"/>
|
||||
<result column="priority" property="priority" jdbcType="VARCHAR"/>
|
||||
<result column="sender_id" property="senderID" jdbcType="VARCHAR"/>
|
||||
<result column="sender_name" property="senderName" jdbcType="VARCHAR"/>
|
||||
<result column="sender_dept_id" property="senderDeptID" jdbcType="VARCHAR"/>
|
||||
<result column="sender_dept_name" property="senderDeptName" jdbcType="VARCHAR"/>
|
||||
<result column="send_mode" property="sendMode" jdbcType="VARCHAR"/>
|
||||
<result column="scheduled_time" property="scheduledTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="actual_send_time" property="actualSendTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="status" property="status" jdbcType="VARCHAR"/>
|
||||
<result column="target_user_count" property="targetUserCount" jdbcType="INTEGER"/>
|
||||
<result column="sent_count" property="sentCount" jdbcType="INTEGER"/>
|
||||
<result column="success_count" property="successCount" jdbcType="INTEGER"/>
|
||||
<result column="failed_count" property="failedCount" jdbcType="INTEGER"/>
|
||||
<result column="read_count" property="readCount" jdbcType="INTEGER"/>
|
||||
<result column="retry_count" property="retryCount" jdbcType="INTEGER"/>
|
||||
<result column="max_retry_count" property="maxRetryCount" jdbcType="INTEGER"/>
|
||||
<result column="last_error" property="lastError" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 分页查询消息列表(带权限过滤) -->
|
||||
<select id="selectMessagePage" resultMap="MessageVOMap">
|
||||
SELECT
|
||||
m.*
|
||||
FROM tb_sys_message m
|
||||
WHERE m.deleted = 0
|
||||
<if test="filter != null">
|
||||
<if test="filter.title != null and filter.title != ''">
|
||||
AND m.title LIKE CONCAT('%', #{filter.title}, '%')
|
||||
</if>
|
||||
<if test="filter.messageType != null and filter.messageType != ''">
|
||||
AND m.message_type = #{filter.messageType}
|
||||
</if>
|
||||
<if test="filter.status != null and filter.status != ''">
|
||||
AND m.status = #{filter.status}
|
||||
</if>
|
||||
<if test="filter.sendMode != null and filter.sendMode != ''">
|
||||
AND m.send_mode = #{filter.sendMode}
|
||||
</if>
|
||||
<if test="filter.priority != null and filter.priority != ''">
|
||||
AND m.priority = #{filter.priority}
|
||||
</if>
|
||||
</if>
|
||||
<!-- 权限过滤:只能查看自己部门及子部门的消息 -->
|
||||
AND (
|
||||
m.sender_dept_id = #{currentUserDeptID}
|
||||
OR m.sender_dept_id IN (
|
||||
WITH RECURSIVE dept_tree AS (
|
||||
SELECT dept_id FROM tb_sys_dept
|
||||
WHERE dept_id = #{currentUserDeptID} AND deleted = 0
|
||||
UNION ALL
|
||||
SELECT d.dept_id FROM tb_sys_dept d
|
||||
INNER JOIN dept_tree dt ON d.parent_id = dt.dept_id
|
||||
WHERE d.deleted = 0
|
||||
)
|
||||
SELECT dept_id FROM dept_tree
|
||||
)
|
||||
)
|
||||
ORDER BY m.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询消息详情 -->
|
||||
<select id="selectMessageDetail" resultMap="MessageVOMap">
|
||||
SELECT *
|
||||
FROM tb_sys_message
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 查询待发送的定时消息 -->
|
||||
<select id="selectPendingScheduledMessages" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM tb_sys_message
|
||||
WHERE status = 'pending'
|
||||
AND send_mode = 'scheduled'
|
||||
AND scheduled_time <![CDATA[ <= ]]> #{currentTime}
|
||||
AND deleted = 0
|
||||
ORDER BY scheduled_time ASC
|
||||
LIMIT 100
|
||||
</select>
|
||||
|
||||
<!-- CAS更新消息状态 -->
|
||||
<update id="compareAndSetStatus">
|
||||
UPDATE tb_sys_message
|
||||
SET status = #{newStatus},
|
||||
update_time = NOW()
|
||||
WHERE message_id = #{messageID}
|
||||
AND status = #{expectedStatus}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 更新消息统计信息 -->
|
||||
<update id="updateStatistics">
|
||||
UPDATE tb_sys_message
|
||||
SET sent_count = #{sentCount},
|
||||
success_count = #{successCount},
|
||||
failed_count = #{failedCount},
|
||||
update_time = NOW()
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 更新已读数量 -->
|
||||
<update id="incrementReadCount">
|
||||
UPDATE tb_sys_message
|
||||
SET read_count = read_count + 1,
|
||||
update_time = NOW()
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 根据消息ID查询消息 -->
|
||||
<select id="selectMessageById" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM tb_sys_message
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 插入消息 -->
|
||||
<insert id="insertMessage">
|
||||
INSERT INTO tb_sys_message
|
||||
(id, message_id, title, content, message_type, priority, sender_id, sender_dept_id,
|
||||
send_mode, scheduled_time, status, target_user_count, retry_count, max_retry_count,
|
||||
creator, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(#{message.id}, #{message.messageID}, #{message.title}, #{message.content},
|
||||
#{message.messageType}, #{message.priority}, #{message.senderID}, #{message.senderDeptID},
|
||||
#{message.sendMode}, #{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
|
||||
#{message.retryCount}, #{message.maxRetryCount}, #{message.creator}, #{message.createTime},
|
||||
#{message.updateTime}, #{message.deleted})
|
||||
</insert>
|
||||
|
||||
<!-- 更新消息 -->
|
||||
<update id="updateMessage">
|
||||
UPDATE tb_sys_message
|
||||
<set>
|
||||
<if test="message.title != null and message.title != ''">
|
||||
title = #{message.title},
|
||||
</if>
|
||||
<if test="message.content != null">
|
||||
content = #{message.content},
|
||||
</if>
|
||||
<if test="message.messageType != null and message.messageType != ''">
|
||||
message_type = #{message.messageType},
|
||||
</if>
|
||||
<if test="message.priority != null and message.priority != ''">
|
||||
priority = #{message.priority},
|
||||
</if>
|
||||
<if test="message.sendMode != null and message.sendMode != ''">
|
||||
send_mode = #{message.sendMode},
|
||||
</if>
|
||||
<if test="message.scheduledTime != null">
|
||||
scheduled_time = #{message.scheduledTime},
|
||||
</if>
|
||||
<if test="message.actualSendTime != null">
|
||||
actual_send_time = #{message.actualSendTime},
|
||||
</if>
|
||||
<if test="message.status != null and message.status != ''">
|
||||
status = #{message.status},
|
||||
</if>
|
||||
<if test="message.targetUserCount != null">
|
||||
target_user_count = #{message.targetUserCount},
|
||||
</if>
|
||||
<if test="message.retryCount != null">
|
||||
retry_count = #{message.retryCount},
|
||||
</if>
|
||||
<if test="message.maxRetryCount != null">
|
||||
max_retry_count = #{message.maxRetryCount},
|
||||
</if>
|
||||
<if test="message.lastError != null">
|
||||
last_error = #{message.lastError},
|
||||
</if>
|
||||
<if test="message.updater != null and message.updater != ''">
|
||||
updater = #{message.updater},
|
||||
</if>
|
||||
<if test="message.updateTime != null">
|
||||
update_time = #{message.updateTime},
|
||||
</if>
|
||||
</set>
|
||||
WHERE message_id = #{message.messageID}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 删除消息(逻辑删除) -->
|
||||
<update id="deleteMessage">
|
||||
UPDATE tb_sys_message
|
||||
SET deleted = 1,
|
||||
delete_time = NOW(),
|
||||
update_time = NOW()
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 统计消息总数(带权限过滤) -->
|
||||
<select id="countMessage" resultType="java.lang.Integer">
|
||||
SELECT COUNT(*)
|
||||
FROM tb_sys_message m
|
||||
WHERE m.deleted = 0
|
||||
<if test="filter != null">
|
||||
<if test="filter.title != null and filter.title != ''">
|
||||
AND m.title LIKE CONCAT('%', #{filter.title}, '%')
|
||||
</if>
|
||||
<if test="filter.messageType != null and filter.messageType != ''">
|
||||
AND m.message_type = #{filter.messageType}
|
||||
</if>
|
||||
<if test="filter.status != null and filter.status != ''">
|
||||
AND m.status = #{filter.status}
|
||||
</if>
|
||||
<if test="filter.sendMode != null and filter.sendMode != ''">
|
||||
AND m.send_mode = #{filter.sendMode}
|
||||
</if>
|
||||
<if test="filter.priority != null and filter.priority != ''">
|
||||
AND m.priority = #{filter.priority}
|
||||
</if>
|
||||
</if>
|
||||
<!-- 权限过滤:只能查看自己部门及子部门的消息 -->
|
||||
AND (
|
||||
m.sender_dept_id = #{currentUserDeptID}
|
||||
OR m.sender_dept_id IN (
|
||||
WITH RECURSIVE dept_tree AS (
|
||||
SELECT dept_id FROM tb_sys_dept
|
||||
WHERE dept_id = #{currentUserDeptID} AND deleted = 0
|
||||
UNION ALL
|
||||
SELECT d.dept_id FROM tb_sys_dept d
|
||||
INNER JOIN dept_tree dt ON d.parent_id = dt.dept_id
|
||||
WHERE d.deleted = 0
|
||||
)
|
||||
SELECT dept_id FROM dept_tree
|
||||
)
|
||||
)
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="org.xyzh.message.mapper.MessageTargetMapper">
|
||||
|
||||
<!-- Result Map -->
|
||||
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.message.TbSysMessageTarget">
|
||||
<id column="id" property="id" jdbcType="VARCHAR"/>
|
||||
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
|
||||
<result column="send_method" property="sendMethod" jdbcType="VARCHAR"/>
|
||||
<result column="target_type" property="targetType" jdbcType="VARCHAR"/>
|
||||
<result column="target_id" property="targetID" jdbcType="VARCHAR"/>
|
||||
<result column="target_name" property="targetName" jdbcType="VARCHAR"/>
|
||||
<result column="scope_dept_id" property="scopeDeptID" jdbcType="VARCHAR"/>
|
||||
<result column="creator" property="creator" jdbcType="VARCHAR"/>
|
||||
<result column="updater" property="updater" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 根据消息ID查询接收对象列表 -->
|
||||
<select id="selectByMessageID" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM tb_sys_message_target
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
ORDER BY create_time ASC
|
||||
</select>
|
||||
|
||||
<!-- 批量插入接收对象 -->
|
||||
<insert id="batchInsert">
|
||||
INSERT INTO tb_sys_message_target
|
||||
(id, message_id, send_method, target_type, target_id, target_name, scope_dept_id, creator, create_time, update_time, deleted)
|
||||
VALUES
|
||||
<foreach collection="targets" item="item" separator=",">
|
||||
(#{item.id}, #{item.messageID}, #{item.sendMethod}, #{item.targetType}, #{item.targetID},
|
||||
#{item.targetName}, #{item.scopeDeptID}, #{item.creator}, NOW(), NOW(), 0)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 根据消息ID删除接收对象 -->
|
||||
<update id="deleteByMessageID">
|
||||
UPDATE tb_sys_message_target
|
||||
SET deleted = 1,
|
||||
delete_time = NOW(),
|
||||
update_time = NOW()
|
||||
WHERE message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,352 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="org.xyzh.message.mapper.MessageUserMapper">
|
||||
|
||||
<!-- Result Map -->
|
||||
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.message.TbSysMessageUser">
|
||||
<id column="id" property="id" jdbcType="VARCHAR"/>
|
||||
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
|
||||
<result column="user_id" property="userID" jdbcType="VARCHAR"/>
|
||||
<result column="send_method" property="sendMethod" jdbcType="VARCHAR"/>
|
||||
<result column="is_read" property="isRead" jdbcType="BOOLEAN"/>
|
||||
<result column="read_time" property="readTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="send_status" property="sendStatus" jdbcType="VARCHAR"/>
|
||||
<result column="fail_reason" property="failReason" jdbcType="VARCHAR"/>
|
||||
<result column="creator" property="creator" jdbcType="VARCHAR"/>
|
||||
<result column="updater" property="updater" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="MessageUserVOMap" type="org.xyzh.common.dto.message.MessageUserVO">
|
||||
<id column="id" property="id" jdbcType="VARCHAR"/>
|
||||
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
|
||||
<result column="user_id" property="userID" jdbcType="VARCHAR"/>
|
||||
<result column="username" property="username" jdbcType="VARCHAR"/>
|
||||
<result column="full_name" property="fullName" jdbcType="VARCHAR"/>
|
||||
<result column="dept_id" property="deptID" jdbcType="VARCHAR"/>
|
||||
<result column="name" property="deptName" jdbcType="VARCHAR"/>
|
||||
<result column="send_method" property="sendMethod" jdbcType="VARCHAR"/>
|
||||
<result column="is_read" property="isRead" jdbcType="BOOLEAN"/>
|
||||
<result column="read_time" property="readTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="send_status" property="sendStatus" jdbcType="VARCHAR"/>
|
||||
<result column="fail_reason" property="failReason" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="title" property="title" jdbcType="VARCHAR"/>
|
||||
<result column="content" property="content" jdbcType="VARCHAR"/>
|
||||
<result column="message_type" property="messageType" jdbcType="VARCHAR"/>
|
||||
<result column="priority" property="priority" jdbcType="VARCHAR"/>
|
||||
<result column="sender_name" property="senderName" jdbcType="VARCHAR"/>
|
||||
<result column="sender_dept_name" property="senderDeptName" jdbcType="VARCHAR"/>
|
||||
<result column="actual_send_time" property="actualSendTime" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 根据消息ID查询用户消息列表 -->
|
||||
<select id="selectByMessageID" resultMap="MessageUserVOMap">
|
||||
SELECT
|
||||
mu.*,
|
||||
u.username,
|
||||
ui.full_name as full_name,
|
||||
d.dept_id as dept_id,
|
||||
d.name as deptName
|
||||
FROM tb_sys_message_user mu
|
||||
LEFT JOIN tb_sys_user u ON mu.user_id = u.id
|
||||
LEFT JOIN tb_sys_user_info ui ON u.id = ui.user_id
|
||||
LEFT JOIN tb_sys_user_dept_role udr ON u.id = udr.user_id AND udr.deleted = 0
|
||||
LEFT JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
|
||||
WHERE mu.message_id = #{messageID}
|
||||
AND mu.deleted = 0
|
||||
ORDER BY mu.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 批量插入用户消息 -->
|
||||
<insert id="batchInsert">
|
||||
INSERT INTO tb_sys_message_user
|
||||
(id, message_id, user_id, send_method, is_read, send_status, creator, create_time, update_time, deleted)
|
||||
VALUES
|
||||
<foreach collection="userMessages" item="item" separator=",">
|
||||
(#{item.id}, #{item.messageID}, #{item.userID}, #{item.sendMethod},
|
||||
0, 'pending', #{item.creator}, NOW(), NOW(), 0)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 分页查询当前用户的消息列表 -->
|
||||
<select id="selectMyMessages" resultMap="MessageUserVOMap">
|
||||
SELECT
|
||||
mu.*,
|
||||
m.title,
|
||||
m.content,
|
||||
m.message_type as message_type,
|
||||
m.priority,
|
||||
m.sender_name as sender_name,
|
||||
m.sender_dept_name as sender_dept_name,
|
||||
m.actual_send_time as actual_send_time
|
||||
FROM tb_sys_message_user mu
|
||||
INNER JOIN tb_sys_message m ON mu.message_id = m.message_id
|
||||
WHERE mu.user_id = #{userID}
|
||||
AND mu.deleted = 0
|
||||
AND m.deleted = 0
|
||||
<if test="filter != null">
|
||||
<if test="filter.isRead != null">
|
||||
AND mu.is_read = #{filter.isRead}
|
||||
</if>
|
||||
<if test="filter.sendMethod != null and filter.sendMethod != ''">
|
||||
AND mu.send_method = #{filter.sendMethod}
|
||||
</if>
|
||||
</if>
|
||||
ORDER BY m.actual_send_time DESC, mu.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询当前用户的消息详情 -->
|
||||
<select id="selectMyMessageDetail" resultMap="MessageUserVOMap">
|
||||
SELECT
|
||||
mu.*,
|
||||
m.title,
|
||||
m.content,
|
||||
m.message_type as message_type,
|
||||
m.priority,
|
||||
m.sender_name as sender_name,
|
||||
m.sender_dept_name as sender_dept_name,
|
||||
m.actual_send_time as actual_send_time
|
||||
FROM tb_sys_message_user mu
|
||||
INNER JOIN tb_sys_message m ON mu.message_id = m.message_id
|
||||
WHERE mu.user_id = #{userID}
|
||||
AND mu.message_id = #{messageID}
|
||||
AND mu.deleted = 0
|
||||
AND m.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 标记消息为已读 -->
|
||||
<update id="markAsRead">
|
||||
UPDATE tb_sys_message_user
|
||||
SET is_read = 1,
|
||||
send_status = 'sent',
|
||||
read_time = NOW(),
|
||||
update_time = NOW()
|
||||
WHERE user_id = #{userID}
|
||||
AND message_id = #{messageID}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 批量标记消息为已读 -->
|
||||
<update id="batchMarkAsRead">
|
||||
UPDATE tb_sys_message_user
|
||||
SET is_read = 1,
|
||||
read_time = NOW(),
|
||||
update_time = NOW()
|
||||
WHERE user_id = #{userID}
|
||||
AND message_id IN
|
||||
<foreach collection="messageIDs" item="id" open="(" close=")" separator=",">
|
||||
#{id}
|
||||
</foreach>
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 查询未读消息数量 -->
|
||||
<select id="countUnread" resultType="java.lang.Integer">
|
||||
SELECT COUNT(*)
|
||||
FROM tb_sys_message_user
|
||||
WHERE user_id = #{userID}
|
||||
AND is_read = 0
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 动态计算未读消息数量(基于 target 配置) -->
|
||||
<select id="countUnreadWithDynamicTargets" resultType="java.lang.Integer">
|
||||
SELECT COUNT(DISTINCT m.message_id)
|
||||
FROM tb_sys_message m
|
||||
INNER JOIN tb_sys_message_target mt ON m.message_id = mt.message_id AND mt.deleted = 0
|
||||
LEFT JOIN tb_sys_message_user mu ON m.message_id = mu.message_id AND mu.user_id = #{userID} AND mu.deleted = 0
|
||||
WHERE m.deleted = 0
|
||||
AND m.status IN ('sent', 'sending', 'failed')
|
||||
AND COALESCE(mu.is_read, 0) = 0
|
||||
AND (
|
||||
-- 用户类型目标:直接匹配
|
||||
(mt.target_type = 'user' AND mt.target_id = #{userID})
|
||||
OR
|
||||
-- 部门类型目标:用户所在部门是目标部门或其子部门
|
||||
(mt.target_type = 'dept' AND EXISTS (
|
||||
SELECT 1 FROM tb_sys_user_dept_role udr
|
||||
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
|
||||
INNER JOIN tb_sys_dept target_dept ON target_dept.dept_id = mt.target_id AND target_dept.deleted = 0
|
||||
WHERE udr.user_id = #{userID}
|
||||
AND udr.deleted = 0
|
||||
AND (
|
||||
d.dept_id = target_dept.dept_id
|
||||
OR d.dept_path LIKE CONCAT(target_dept.dept_path, '%')
|
||||
)
|
||||
))
|
||||
OR
|
||||
-- 角色类型目标:用户在指定部门范围内拥有该角色
|
||||
(mt.target_type = 'role' AND EXISTS (
|
||||
SELECT 1 FROM tb_sys_user_dept_role udr
|
||||
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
|
||||
WHERE udr.user_id = #{userID}
|
||||
AND udr.deleted = 0
|
||||
AND udr.role_id = mt.target_id
|
||||
AND d.dept_path LIKE CONCAT(
|
||||
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = mt.scope_dept_id AND deleted = 0),
|
||||
'%'
|
||||
)
|
||||
))
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- 更新用户消息的发送状态 -->
|
||||
<update id="updateSendStatus">
|
||||
UPDATE tb_sys_message_user
|
||||
SET send_status = #{sendStatus},
|
||||
<if test="failReason != null and failReason != ''">
|
||||
fail_reason = #{failReason},
|
||||
</if>
|
||||
update_time = NOW()
|
||||
WHERE id = #{id}
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 查询待发送的用户消息列表 -->
|
||||
<select id="selectPendingUserMessages" resultMap="BaseResultMap">
|
||||
SELECT *
|
||||
FROM tb_sys_message_user
|
||||
WHERE deleted = 0
|
||||
<if test="filter != null">
|
||||
<if test="filter.messageID != null and filter.messageID != ''">
|
||||
AND message_id = #{filter.messageID}
|
||||
</if>
|
||||
<if test="filter.sendStatus != null and filter.sendStatus != ''">
|
||||
AND send_status = #{filter.sendStatus}
|
||||
</if>
|
||||
<if test="filter.deleted != null">
|
||||
AND deleted = #{filter.deleted}
|
||||
</if>
|
||||
</if>
|
||||
ORDER BY create_time ASC
|
||||
</select>
|
||||
|
||||
<!-- 动态查询当前用户可见的消息列表(基于 target 配置计算) -->
|
||||
<select id="selectMyMessagesWithDynamicTargets" resultMap="MessageUserVOMap">
|
||||
SELECT
|
||||
m.message_id,
|
||||
m.title,
|
||||
m.content,
|
||||
m.message_type,
|
||||
m.priority,
|
||||
m.sender_name,
|
||||
m.sender_dept_name,
|
||||
m.actual_send_time,
|
||||
MAX(COALESCE(mu.is_read, 0)) as is_read,
|
||||
MAX(mu.read_time) as read_time,
|
||||
MAX(COALESCE(mu.send_status, 'pending')) as send_status,
|
||||
MAX(mu.id) as id,
|
||||
MAX(mu.user_id) as user_id,
|
||||
MAX(mu.send_method) as send_method,
|
||||
MAX(mu.fail_reason) as fail_reason,
|
||||
MAX(mu.create_time) as create_time
|
||||
FROM tb_sys_message m
|
||||
INNER JOIN tb_sys_message_target mt ON m.message_id = mt.message_id AND mt.deleted = 0
|
||||
LEFT JOIN tb_sys_message_user mu ON m.message_id = mu.message_id AND mu.user_id = #{userID} AND mu.deleted = 0
|
||||
WHERE m.deleted = 0
|
||||
AND m.status IN ('sent', 'sending', 'failed')
|
||||
AND (
|
||||
-- 用户类型目标:直接匹配
|
||||
(mt.target_type = 'user' AND mt.target_id = #{userID})
|
||||
OR
|
||||
-- 部门类型目标:用户所在部门是目标部门或其子部门
|
||||
(mt.target_type = 'dept' AND EXISTS (
|
||||
SELECT 1 FROM tb_sys_user_dept_role udr
|
||||
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
|
||||
INNER JOIN tb_sys_dept target_dept ON target_dept.dept_id = mt.target_id AND target_dept.deleted = 0
|
||||
WHERE udr.user_id = #{userID}
|
||||
AND udr.deleted = 0
|
||||
AND (
|
||||
d.dept_id = target_dept.dept_id
|
||||
OR d.dept_path LIKE CONCAT(target_dept.dept_path, '%')
|
||||
)
|
||||
))
|
||||
OR
|
||||
-- 角色类型目标:用户在指定部门范围内拥有该角色
|
||||
(mt.target_type = 'role' AND EXISTS (
|
||||
SELECT 1 FROM tb_sys_user_dept_role udr
|
||||
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
|
||||
WHERE udr.user_id = #{userID}
|
||||
AND udr.deleted = 0
|
||||
AND udr.role_id = mt.target_id
|
||||
AND d.dept_path LIKE CONCAT(
|
||||
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = mt.scope_dept_id AND deleted = 0),
|
||||
'%'
|
||||
)
|
||||
))
|
||||
)
|
||||
<if test="filter != null">
|
||||
<if test="filter.isRead != null">
|
||||
AND COALESCE(mu.is_read, 0) = #{filter.isRead}
|
||||
</if>
|
||||
<if test="filter.sendMethod != null and filter.sendMethod != ''">
|
||||
AND mt.send_method = #{filter.sendMethod}
|
||||
</if>
|
||||
<if test="filter.sendStatus != null and filter.sendStatus != ''">
|
||||
AND mu.send_status = #{filter.sendStatus}
|
||||
</if>
|
||||
<if test="filter.priority != null and filter.priority != ''">
|
||||
AND m.priority = #{filter.priority}
|
||||
</if>
|
||||
<if test="filter.messageType != null and filter.messageType != ''">
|
||||
AND m.message_type = #{filter.messageType}
|
||||
</if>
|
||||
<if test="filter.title != null and filter.title != ''">
|
||||
AND m.title LIKE CONCAT('%', #{filter.title}, '%')
|
||||
</if>
|
||||
</if>
|
||||
GROUP BY m.message_id, m.title, m.content, m.message_type, m.priority,
|
||||
m.sender_name, m.sender_dept_name, m.actual_send_time
|
||||
ORDER BY m.actual_send_time DESC, m.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询或创建用户消息记录 -->
|
||||
<select id="selectOrCreateUserMessage" resultMap="MessageUserVOMap">
|
||||
SELECT
|
||||
mu.*,
|
||||
m.title,
|
||||
m.content,
|
||||
m.message_type,
|
||||
m.priority,
|
||||
m.sender_name,
|
||||
m.sender_dept_name,
|
||||
m.actual_send_time
|
||||
FROM tb_sys_message m
|
||||
LEFT JOIN tb_sys_message_user mu ON m.message_id = mu.message_id
|
||||
AND mu.user_id = #{userID}
|
||||
AND mu.deleted = 0
|
||||
WHERE m.message_id = #{messageID}
|
||||
AND m.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 插入用户消息记录(如果不存在) -->
|
||||
<insert id="insertIfNotExists">
|
||||
INSERT INTO tb_sys_message_user
|
||||
(id, message_id, user_id, send_method, is_read, send_status, creator, create_time, update_time, deleted)
|
||||
SELECT
|
||||
#{userMessage.id},
|
||||
#{userMessage.messageID},
|
||||
#{userMessage.userID},
|
||||
#{userMessage.sendMethod},
|
||||
0,
|
||||
'pending',
|
||||
#{userMessage.creator},
|
||||
NOW(),
|
||||
NOW(),
|
||||
0
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM tb_sys_message_user
|
||||
WHERE message_id = #{userMessage.messageID}
|
||||
AND user_id = #{userMessage.userID}
|
||||
AND deleted = 0
|
||||
)
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
@@ -22,22 +22,22 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<modules>
|
||||
<module>api</module>
|
||||
@@ -21,6 +21,7 @@
|
||||
<module>ai</module>
|
||||
<module>file</module>
|
||||
<module>crontab</module>
|
||||
<module>message</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@@ -57,7 +58,7 @@
|
||||
|
||||
<!-- excel -->
|
||||
<poi.version>5.2.3</poi.version>
|
||||
|
||||
|
||||
<lombok.version>1.18.40</lombok.version>
|
||||
</properties>
|
||||
|
||||
@@ -67,63 +68,68 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>course</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>usercenter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>ai</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>crontab</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>message</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>achievement</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>ai</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>crontab</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
</dependency>
|
||||
<!-- Spring相关依赖 -->
|
||||
<!-- 覆盖SpringFramework的依赖配置-->
|
||||
<dependency>
|
||||
@@ -273,11 +279,11 @@
|
||||
<artifactId>poi</artifactId>
|
||||
<version>${poi.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${poi.version}</version>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${poi.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
-- 为 tb_data_collection_item 表添加单条新闻执行状态和消息字段
|
||||
-- 执行日期: 2025-11-12
|
||||
|
||||
ALTER TABLE tb_data_collection_item
|
||||
ADD COLUMN execute_status INT DEFAULT 1 COMMENT '单条新闻执行状态(0:失败 1:成功)' AFTER processor,
|
||||
ADD COLUMN execute_message VARCHAR(500) DEFAULT NULL COMMENT '单条新闻执行消息(记录错误信息或成功提示)' AFTER execute_status;
|
||||
|
||||
-- 为现有数据设置默认值
|
||||
UPDATE tb_data_collection_item SET execute_status = 1 WHERE execute_status IS NULL;
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>study</name>
|
||||
<description>学习管理模块</description>
|
||||
@@ -25,17 +25,17 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
@@ -23,14 +23,14 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Common模块依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Web -->
|
||||
|
||||
@@ -50,6 +50,15 @@ public interface UserMapper extends BaseMapper<TbSysUser> {
|
||||
*/
|
||||
int deleteUser(@Param("userID") String userID);
|
||||
|
||||
/**
|
||||
* @description 根据用户ID查询用户
|
||||
* @param userID 用户ID
|
||||
* @return TbSysUser 用户信息
|
||||
* @author yslg
|
||||
* @since 2025-11-13
|
||||
*/
|
||||
TbSysUser selectUserById(@Param("userID") String userID);
|
||||
|
||||
/**
|
||||
* @description 根据用户名查询用户
|
||||
* @param username 用户名
|
||||
@@ -182,6 +191,21 @@ public interface UserMapper extends BaseMapper<TbSysUser> {
|
||||
*/
|
||||
UserVO selectUserInfoTotal(@Param("userId") String userId);
|
||||
|
||||
|
||||
|
||||
int countDeptUser(@Param("deptId") String deptId);
|
||||
|
||||
/**
|
||||
* @description 查询部门及其子部门的所有用户ID
|
||||
* @param deptId 部门ID
|
||||
* @return List<String> 用户ID列表
|
||||
*/
|
||||
List<String> selectUserIdsByDeptId(@Param("deptId") String deptId);
|
||||
|
||||
/**
|
||||
* @description 查询指定部门及其子部门中指定角色的所有用户ID
|
||||
* @param deptId 部门ID
|
||||
* @param roleId 角色ID
|
||||
* @return List<String> 用户ID列表
|
||||
*/
|
||||
List<String> selectUserIdsByDeptRole(@Param("deptId") String deptId, @Param("roleId") String roleId);
|
||||
}
|
||||
|
||||
@@ -143,6 +143,15 @@
|
||||
</sql>
|
||||
|
||||
<!-- 根据用户名查询用户 -->
|
||||
<!-- 根据用户ID查询用户 -->
|
||||
<select id="selectUserById" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM tb_sys_user
|
||||
WHERE id = #{userID}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<select id="selectByUsername" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
@@ -560,7 +569,7 @@
|
||||
AND tsui.deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- countDeptUser - 递归统计部门及其子部门的用户数量 -->
|
||||
<!-- countDeptUser - 递归统计部门及其子部门的用户数量 -->
|
||||
<select id="countDeptUser" resultType="int">
|
||||
SELECT COUNT(DISTINCT tudr.user_id)
|
||||
FROM tb_sys_user_dept_role tudr
|
||||
@@ -573,6 +582,35 @@
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- selectUserIdsByDeptId - 查询部门及其子部门的所有用户ID -->
|
||||
<select id="selectUserIdsByDeptId" resultType="java.lang.String">
|
||||
SELECT DISTINCT tudr.user_id
|
||||
FROM tb_sys_user_dept_role tudr
|
||||
INNER JOIN tb_sys_dept d ON tudr.dept_id = d.dept_id AND d.deleted = 0
|
||||
INNER JOIN tb_sys_user u ON tudr.user_id = u.id AND u.deleted = 0
|
||||
WHERE tudr.deleted = 0
|
||||
AND u.status = 1
|
||||
AND d.dept_path LIKE CONCAT(
|
||||
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = #{deptId} AND deleted = 0),
|
||||
'%'
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- selectUserIdsByDeptRole - 查询指定部门及其子部门中指定角色的所有用户ID -->
|
||||
<select id="selectUserIdsByDeptRole" resultType="java.lang.String">
|
||||
SELECT DISTINCT tudr.user_id
|
||||
FROM tb_sys_user_dept_role tudr
|
||||
INNER JOIN tb_sys_dept d ON tudr.dept_id = d.dept_id AND d.deleted = 0
|
||||
INNER JOIN tb_sys_user u ON tudr.user_id = u.id AND u.deleted = 0
|
||||
WHERE tudr.deleted = 0
|
||||
AND u.status = 1
|
||||
AND tudr.role_id = #{roleId}
|
||||
AND d.dept_path LIKE CONCAT(
|
||||
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = #{deptId} AND deleted = 0),
|
||||
'%'
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- selectLoginUser -->
|
||||
|
||||
<select id="selectLoginUser">
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>school-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>usercenter</name>
|
||||
<description>个人中心模块</description>
|
||||
@@ -25,33 +25,33 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-usercenter</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-news</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-study</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-all</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -93,7 +93,7 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-file</artifactId>
|
||||
<version>${school-news.version}</version>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user