sms、邮件数据库配置

This commit is contained in:
2025-11-26 13:38:36 +08:00
parent 8d8ecf8763
commit 4ff1bc1101
16 changed files with 847 additions and 186 deletions

View File

@@ -217,35 +217,4 @@ public class MessageController {
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();
}
}

View File

@@ -0,0 +1,27 @@
package org.xyzh.message.event;
import org.springframework.context.ApplicationEvent;
/**
* 消息创建事件
* 用于在事务提交后触发异步发送
*
* @description 消息创建完成事件
* @filename MessageCreatedEvent.java
* @author yslg
* @copyright xyzh
* @since 2025-11-26
*/
public class MessageCreatedEvent extends ApplicationEvent {
private final String messageID;
public MessageCreatedEvent(Object source, String messageID) {
super(source);
this.messageID = messageID;
}
public String getMessageID() {
return messageID;
}
}

View File

@@ -6,11 +6,14 @@ 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.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
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.event.MessageCreatedEvent;
import org.xyzh.message.mapper.MessageMapper;
import org.xyzh.message.mapper.MessageUserMapper;
import org.xyzh.system.mapper.UserMapper;
@@ -47,6 +50,18 @@ public class MessageSendService {
@Autowired
private SmsUtils smsUtils;
/**
* 监听消息创建事件,在事务提交后触发异步发送
* 这样可以确保消息已经持久化到数据库,避免异步线程查询不到数据
*
* @param event 消息创建事件
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onMessageCreated(MessageCreatedEvent event) {
logger.info("接收到消息创建事件,准备异步发送消息:{}", event.getMessageID());
sendMessageAsync(event.getMessageID());
}
/**
* 异步发送消息
*
@@ -78,7 +93,6 @@ public class MessageSendService {
updateMessageStatus(message, "sent");
return;
}
// 3. 遍历发送
int successCount = 0;
int failedCount = 0;
@@ -280,8 +294,22 @@ public class MessageSendService {
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>");
// 发送人信息 - 安全处理null值
String senderName = message.getSenderName() != null ? message.getSenderName() : "系统";
String senderDept = message.getSenderDeptName() != null ? message.getSenderDeptName() : "";
if (senderDept.isEmpty()) {
html.append("<p>发送人:").append(senderName).append("</p>");
} else {
html.append("<p>发送人:").append(senderName).append(" (").append(senderDept).append(")</p>");
}
// 发送时间 - 使用实际发送时间或创建时间
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sendTime = message.getActualSendTime() != null ?
sdf.format(message.getActualSendTime()) :
(message.getCreateTime() != null ? sdf.format(message.getCreateTime()) : "");
if (!sendTime.isEmpty()) {
html.append("<p>发送时间:").append(sendTime).append("</p>");
}
html.append("<p>此邮件由系统自动发送,请勿回复。</p>");
html.append("</div>");
html.append("</div>");

View File

@@ -1,21 +1,25 @@
package org.xyzh.message.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
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.LoginDomain;
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.event.MessageCreatedEvent;
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 org.xyzh.system.utils.LoginUtil;
import java.util.*;
@@ -51,6 +55,9 @@ public class MessageServiceImpl implements MessageService {
@Autowired
private MessageSendService messageSendService;
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 创建消息
*/
@@ -60,8 +67,9 @@ public class MessageServiceImpl implements MessageService {
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
try {
// 1. 获取当前用户信息从Session或SecurityContext获取
String currentUserID = getCurrentUserID();
String currentDeptID = getCurrentUserDeptID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
String currentDeptID = currentUser.getRoles().get(0).getDeptID();
// 2. 设置消息主体基本信息
if (message.getID() == null) {
@@ -72,6 +80,7 @@ public class MessageServiceImpl implements MessageService {
}
message.setSenderID(currentUserID);
message.setSenderDeptID(currentDeptID);
message.setSenderName(currentUser.getUser().getUsername());
// 设置状态
if (message.getStatus() == null) {
@@ -104,10 +113,11 @@ public class MessageServiceImpl implements MessageService {
if (targets != null && !targets.isEmpty()) {
for (TbSysMessageTarget target : targets) {
// 权限校验scopeDeptID必须是当前部门或子部门
if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
rt.fail("无权向该部门发送消息");
return rt;
}
// TODO: 实现部门层级检查
// if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
// rt.fail("无权向该部门发送消息");
// return rt;
// }
if (target.getID() == null) {
target.setID(IDUtils.generateID());
@@ -130,9 +140,10 @@ public class MessageServiceImpl implements MessageService {
message.setTargetUserCount(userMessages.size());
messageMapper.updateMessage(message);
// 6. 如果是立即发送,异步发送消息
// 6. 如果是立即发送,发布事件触发异步发送
// 使用事件机制确保在事务提交后再发送,避免异步线程读不到数据
if ("immediate".equals(message.getSendMode())) {
messageSendService.sendMessageAsync(message.getMessageID());
eventPublisher.publishEvent(new MessageCreatedEvent(this, message.getMessageID()));
}
}
@@ -168,7 +179,8 @@ public class MessageServiceImpl implements MessageService {
}
message.setUpdateTime(new Date());
message.setUpdater(getCurrentUserID());
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
message.setUpdater(currentUser.getUser().getID());
messageMapper.updateMessage(message);
rt.success("更新成功", message);
return rt;
@@ -259,7 +271,8 @@ public class MessageServiceImpl implements MessageService {
pageParam = new PageParam();
}
String currentDeptID = getCurrentUserDeptID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentDeptID = currentUser.getRoles().get(0).getDeptID();
List<MessageVO> list = messageMapper.selectMessagePage(filter, currentDeptID);
int total = messageMapper.countMessage(filter, currentDeptID);
@@ -475,7 +488,8 @@ public class MessageServiceImpl implements MessageService {
pageParam = new PageParam();
}
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
// 使用新的动态查询方法
List<MessageUserVO> list = messageUserMapper.selectMyMessagesWithDynamicTargets(currentUserID, filter);
@@ -500,7 +514,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<MessageUserVO> getMyMessageDetail(String messageID) {
ResultDomain<MessageUserVO> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
MessageUserVO messageUserVO = messageUserMapper.selectOrCreateUserMessage(currentUserID, messageID);
if (messageUserVO == null) {
@@ -546,7 +561,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<TbSysMessageUser> markAsRead(String messageID) {
ResultDomain<TbSysMessageUser> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
// 先尝试更新已有记录
int result = messageUserMapper.markAsRead(currentUserID, messageID);
@@ -599,7 +615,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<Integer> batchMarkAsRead(List<String> messageIDs) {
ResultDomain<Integer> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
int count = messageUserMapper.batchMarkAsRead(currentUserID, messageIDs);
// 更新每条消息的已读数量
@@ -621,7 +638,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<Integer> getUnreadCount() {
ResultDomain<Integer> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
// 使用动态计算方法,统计用户应该看到的所有未读消息
Integer count = messageUserMapper.countUnreadWithDynamicTargets(currentUserID);
rt.success("查询成功", count != null ? count : 0);
@@ -636,29 +654,7 @@ public class MessageServiceImpl implements MessageService {
// ================== 辅助方法 ==================
@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;
}
// ================== 私有辅助方法 ==================
@@ -720,27 +716,4 @@ public class MessageServiceImpl implements MessageService {
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";
}
}

View File

@@ -166,12 +166,12 @@
<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,
send_mode, sender_name,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.sendMode}, #{message.senderName},#{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
#{message.retryCount}, #{message.maxRetryCount}, #{message.creator}, #{message.createTime},
#{message.updateTime}, #{message.deleted})
</insert>