package cn.iocoder.yudao.module.system.service.notify;
|
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
|
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
|
import cn.iocoder.yudao.module.system.service.notify.dto.NotifyMessagePushDTO;
|
import com.google.common.annotations.VisibleForTesting;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
import jakarta.annotation.Resource;
|
import java.util.Map;
|
import java.util.Objects;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
|
/**
|
* 站内信发送 Service 实现类
|
*
|
* @author xrcoder
|
*/
|
@Service
|
@Validated
|
@Slf4j
|
public class NotifySendServiceImpl implements NotifySendService {
|
|
@Resource
|
private NotifyTemplateService notifyTemplateService;
|
|
@Resource
|
private NotifyMessageService notifyMessageService;
|
|
@Resource
|
private WebSocketSenderApi webSocketSenderApi;
|
|
@Override
|
public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map<String, Object> templateParams) {
|
return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
|
}
|
|
@Override
|
public Long sendSingleNotifyToMember(Long userId, String templateCode, Map<String, Object> templateParams) {
|
return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
|
}
|
|
@Override
|
public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams) {
|
// 校验模版
|
NotifyTemplateDO template = validateNotifyTemplate(templateCode);
|
if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
|
log.info("[sendSingleNotify][模版({})已经关闭,无法给用户({}/{})发送]", templateCode, userId, userType);
|
return null;
|
}
|
// 校验参数
|
validateTemplateParams(template, templateParams);
|
|
// 发送站内信
|
String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams);
|
Long messageId = notifyMessageService.createNotifyMessage(userId, userType, template, content, templateParams);
|
// 实时推送:前端铃铛收到后立即刷新未读数,避免只能等待轮询
|
pushToWebSocket(userId, userType, messageId, template, content);
|
return messageId;
|
}
|
|
/**
|
* 通过 WebSocket 把站内信实时推送给用户(该用户所有在线页面都会收到)
|
*
|
* 说明:推送只是“提前通知前端刷新”,推送失败不影响站内信本身,因此这里吞掉异常仅记录日志。
|
*/
|
private void pushToWebSocket(Long userId, Integer userType, Long messageId,
|
NotifyTemplateDO template, String content) {
|
if (messageId == null) {
|
return;
|
}
|
NotifyMessagePushDTO message = new NotifyMessagePushDTO();
|
message.setMessageId(messageId);
|
message.setTemplateCode(template.getCode());
|
message.setTemplateNickname(template.getNickname());
|
message.setTemplateContent(content);
|
try {
|
webSocketSenderApi.sendObject(userType, userId, NotifyMessagePushDTO.MESSAGE_TYPE, message);
|
} catch (Exception ex) {
|
log.warn("[pushToWebSocket][向用户({}/{})推送站内信({})失败]", userId, userType, messageId, ex);
|
}
|
}
|
|
@VisibleForTesting
|
public NotifyTemplateDO validateNotifyTemplate(String templateCode) {
|
// 获得站内信模板。考虑到效率,从缓存中获取
|
NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode);
|
// 站内信模板不存在
|
if (template == null) {
|
throw exception(NOTICE_NOT_FOUND);
|
}
|
return template;
|
}
|
|
/**
|
* 校验站内信模版参数是否确实
|
*
|
* @param template 邮箱模板
|
* @param templateParams 参数列表
|
*/
|
@VisibleForTesting
|
public void validateTemplateParams(NotifyTemplateDO template, Map<String, Object> templateParams) {
|
template.getParams().forEach(key -> {
|
Object value = templateParams.get(key);
|
if (value == null) {
|
throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key);
|
}
|
});
|
}
|
}
|