..
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
package com.budwk.app.sys.controller.message;
|
|
||||||
|
|
||||||
import com.budwk.app.sys.models.Sys_user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息发送者
|
|
||||||
*/
|
|
||||||
public interface SysMessageSender {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送消息
|
|
||||||
* @param message 消息内容
|
|
||||||
* @param receiver 接收人
|
|
||||||
* @return 发送结果
|
|
||||||
*/
|
|
||||||
// boolean send(Message message, Sys_user receiver);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支持的渠道类型
|
|
||||||
* @return 渠道类型
|
|
||||||
*/
|
|
||||||
String getChannelType();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
package com.budwk.app.sys.controller.message;
|
|
||||||
|
|
||||||
public class SysSmsMessageSender {
|
|
||||||
}
|
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息通道
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum GlobalMessageChannel {
|
||||||
|
|
||||||
|
LOCAL("本地"),
|
||||||
|
|
||||||
|
WECHAT("微信"),
|
||||||
|
|
||||||
|
EMAIL("邮件"),
|
||||||
|
|
||||||
|
SMS("短信"),
|
||||||
|
|
||||||
|
DINGDING("钉钉"),
|
||||||
|
|
||||||
|
WECHAT_ENTERPRISE("企业微信");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.mode;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class GlobalMessageSendRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件
|
||||||
|
*/
|
||||||
|
private List<JSONObject> attachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息类型(1:系统通知 2:待办通知)
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置
|
||||||
|
*/
|
||||||
|
private JSONObject config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收人ID
|
||||||
|
*/
|
||||||
|
private List<String> receiverIds;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.models;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.budwk.app.base.model.BaseModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.nutz.dao.entity.annotation.*;
|
||||||
|
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Table("global_message")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||||
|
@Comment("全局消息")
|
||||||
|
public class GlobalMessage extends BaseModel {
|
||||||
|
|
||||||
|
@Name
|
||||||
|
@Comment("ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
@PrevInsert(uu32 = true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("标题")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("内容")
|
||||||
|
@ColDefine(type = ColType.TEXT)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("附件")
|
||||||
|
@ColDefine(type = ColType.MYSQL_JSON)
|
||||||
|
private List<JSONObject> attachments;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("消息类型(1:系统公告 2:消息通知)")
|
||||||
|
@ColDefine(type = ColType.INT)
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("发送人ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String senderId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("发送人姓名")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||||
|
private String senderName;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("发送人工号")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||||
|
private String senderLoginName;
|
||||||
|
|
||||||
|
// @Column
|
||||||
|
// @Comment("消息通道(用于第三方消息)")
|
||||||
|
// @ColDefine(type = ColType.MYSQL_JSON)
|
||||||
|
// private List<String> channels;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("扩展字段")
|
||||||
|
@ColDefine(type = ColType.MYSQL_JSON)
|
||||||
|
private JSONObject ext;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("状态(1:草稿 2:已发送 3:已撤回 4:已删除)")
|
||||||
|
@ColDefine(type = ColType.INT)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("发送成功")
|
||||||
|
@ColDefine(type = ColType.BOOLEAN)
|
||||||
|
@Default("0")
|
||||||
|
private Boolean sendSuccess;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("发送结果")
|
||||||
|
@ColDefine(type = ColType.MYSQL_JSON)
|
||||||
|
private String sendResult;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.models;
|
||||||
|
|
||||||
|
import com.budwk.app.base.model.BaseModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.nutz.dao.entity.annotation.*;
|
||||||
|
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Table("global_message_receiver")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||||
|
@Comment("全局消息接收用户")
|
||||||
|
public class GlobalMessageReceiver extends BaseModel {
|
||||||
|
|
||||||
|
@Name
|
||||||
|
@Comment("ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
@PrevInsert(uu32 = true)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("消息ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String messageId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("接收人ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String receiverId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("接收人姓名")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||||
|
private String receiverName;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("接收人工号")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||||
|
private String receiverLoginName;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("阅读状态")
|
||||||
|
@ColDefine(type = ColType.BOOLEAN)
|
||||||
|
@Default("0")
|
||||||
|
private Boolean isRead;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("阅读时间")
|
||||||
|
@ColDefine(type = ColType.DATETIME)
|
||||||
|
private Date readTime;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+228
@@ -0,0 +1,228 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.service;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.mode.GlobalMessageSendRequest;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.strategy.GlobalMessageSendStrategy;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.ioc.Ioc;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局消息发送服务
|
||||||
|
*/
|
||||||
|
@IocBean(create = "initStrategies")
|
||||||
|
@Slf4j
|
||||||
|
public class GlobalMessageSendService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Dao dao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Ioc ioc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送策略缓存
|
||||||
|
*/
|
||||||
|
private final Map<GlobalMessageChannel, GlobalMessageSendStrategy> strategyMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地消息策略(必须存在)
|
||||||
|
*/
|
||||||
|
private GlobalMessageSendStrategy localStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化策略
|
||||||
|
*/
|
||||||
|
public void initStrategies() {
|
||||||
|
try {
|
||||||
|
log.info("开始初始化消息发送策略...");
|
||||||
|
|
||||||
|
// 获取所有实现了GlobalMessageSendStrategy接口的Bean
|
||||||
|
String[] beanNames = ioc.getNames();
|
||||||
|
for (String beanName : beanNames) {
|
||||||
|
try {
|
||||||
|
Object bean = ioc.get(null, beanName);
|
||||||
|
if (bean instanceof GlobalMessageSendStrategy) {
|
||||||
|
GlobalMessageSendStrategy strategy = (GlobalMessageSendStrategy) bean;
|
||||||
|
GlobalMessageChannel channel = strategy.getChannel();
|
||||||
|
strategyMap.put(channel, strategy);
|
||||||
|
|
||||||
|
// 缓存本地策略
|
||||||
|
if (channel == GlobalMessageChannel.LOCAL) {
|
||||||
|
localStrategy = strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("注册消息发送策略:{} -> {}", channel.getValue(), strategy.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("初始化Bean {}时出错:{}", beanName, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStrategy == null) {
|
||||||
|
throw new RuntimeException("本地消息发送策略未找到,系统无法正常工作");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("消息发送策略初始化完成,共注册{}个策略", strategyMap.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("消息发送策略初始化失败", e);
|
||||||
|
throw new RuntimeException("消息发送策略初始化失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息(简化API)
|
||||||
|
*/
|
||||||
|
public void sendMessage(String title, String content, List<String> receiverIds) {
|
||||||
|
sendMessage(title, content, 1, receiverIds, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息(完整API)
|
||||||
|
*/
|
||||||
|
public void sendMessage(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
|
||||||
|
GlobalMessageSendRequest request = new GlobalMessageSendRequest();
|
||||||
|
request.setTitle(title);
|
||||||
|
request.setContent(content);
|
||||||
|
request.setType(type);
|
||||||
|
request.setReceiverIds(receiverIds);
|
||||||
|
request.setConfig(config);
|
||||||
|
|
||||||
|
sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息(使用请求对象)
|
||||||
|
*/
|
||||||
|
public void sendMessage(GlobalMessageSendRequest request) {
|
||||||
|
try {
|
||||||
|
log.info("开始发送全局消息,标题:{},接收人数量:{}", request.getTitle(),
|
||||||
|
request.getReceiverIds() != null ? request.getReceiverIds().size() : 0);
|
||||||
|
|
||||||
|
// 参数校验
|
||||||
|
validateRequest(request);
|
||||||
|
|
||||||
|
// 获取启用的策略
|
||||||
|
List<GlobalMessageSendStrategy> enabledStrategies = getEnabledStrategies();
|
||||||
|
|
||||||
|
if (enabledStrategies.isEmpty()) {
|
||||||
|
log.warn("没有找到启用的消息发送策略");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行消息发送 - 本地消息优先,然后是外部策略
|
||||||
|
for (GlobalMessageSendStrategy strategy : enabledStrategies) {
|
||||||
|
try {
|
||||||
|
// 本地消息必须同步发送,确保优先完成
|
||||||
|
if (strategy.getChannel() == GlobalMessageChannel.LOCAL) {
|
||||||
|
strategy.send(request.getTitle(), request.getContent(), request.getType(),
|
||||||
|
request.getReceiverIds(), request.getConfig());
|
||||||
|
log.info("本地消息发送成功");
|
||||||
|
} else {
|
||||||
|
// 外部消息策略根据配置决定同步或异步
|
||||||
|
if (strategy.supportAsync()) {
|
||||||
|
// 异步发送
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
strategy.send(request.getTitle(), request.getContent(), request.getType(),
|
||||||
|
request.getReceiverIds(), request.getConfig());
|
||||||
|
log.info("异步发送消息成功,渠道:{}", strategy.getChannel().getValue());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("异步发送消息失败,渠道:{},错误:{}", strategy.getChannel().getValue(), e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 同步发送
|
||||||
|
strategy.send(request.getTitle(), request.getContent(), request.getType(),
|
||||||
|
request.getReceiverIds(), request.getConfig());
|
||||||
|
log.info("同步发送消息成功,渠道:{}", strategy.getChannel().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送消息失败,渠道:{},错误:{}", strategy.getChannel().getValue(), e.getMessage(), e);
|
||||||
|
// 本地消息发送失败时,记录严重错误并抛出异常
|
||||||
|
if (strategy.getChannel() == GlobalMessageChannel.LOCAL) {
|
||||||
|
log.error("本地消息发送失败,这可能影响消息的完整性");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("全局消息发送完成,标题:{}", request.getTitle());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("全局消息发送失败,标题:{},错误:{}", request.getTitle(), e.getMessage(), e);
|
||||||
|
throw new RuntimeException("消息发送失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数校验
|
||||||
|
*/
|
||||||
|
private void validateRequest(GlobalMessageSendRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("消息发送请求不能为空");
|
||||||
|
}
|
||||||
|
if (request.getTitle() == null || request.getTitle().trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("消息标题不能为空");
|
||||||
|
}
|
||||||
|
if (request.getContent() == null || request.getContent().trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("消息内容不能为空");
|
||||||
|
}
|
||||||
|
if (request.getReceiverIds() == null || request.getReceiverIds().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("接收人不能为空");
|
||||||
|
}
|
||||||
|
if (request.getType() == null) {
|
||||||
|
request.setType(1); // 默认为系统消息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取启用的发送策略
|
||||||
|
* 本地消息策略必须启用,其他策略根据isEnabled()方法判断
|
||||||
|
*/
|
||||||
|
private List<GlobalMessageSendStrategy> getEnabledStrategies() {
|
||||||
|
List<GlobalMessageSendStrategy> enabledStrategies = new ArrayList<>();
|
||||||
|
|
||||||
|
// 本地消息策略必须存在且优先添加
|
||||||
|
if (localStrategy != null) {
|
||||||
|
enabledStrategies.add(localStrategy);
|
||||||
|
log.debug("添加本地消息策略:{}", localStrategy.getChannel().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找其他启用的策略
|
||||||
|
for (GlobalMessageSendStrategy strategy : strategyMap.values()) {
|
||||||
|
// 跳过本地策略(已经添加)
|
||||||
|
if (strategy.getChannel() == GlobalMessageChannel.LOCAL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查策略是否启用
|
||||||
|
if (strategy.isEnabled()) {
|
||||||
|
enabledStrategies.add(strategy);
|
||||||
|
log.debug("添加启用的外部消息策略:{}", strategy.getChannel().getValue());
|
||||||
|
} else {
|
||||||
|
log.debug("跳过未启用的消息策略:{}", strategy.getChannel().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("共找到{}个启用的消息发送策略", enabledStrategies.size());
|
||||||
|
return enabledStrategies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有可用的发送策略
|
||||||
|
*/
|
||||||
|
public Map<GlobalMessageChannel, GlobalMessageSendStrategy> getAvailableStrategies() {
|
||||||
|
return new HashMap<>(strategyMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.strategy;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局消息发送策略
|
||||||
|
*/
|
||||||
|
public interface GlobalMessageSendStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取渠道类型
|
||||||
|
*/
|
||||||
|
GlobalMessageChannel getChannel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否支持异步发送
|
||||||
|
*/
|
||||||
|
default boolean supportAsync() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用该策略
|
||||||
|
* 默认启用,子类可以重写此方法来控制策略的启用状态
|
||||||
|
*/
|
||||||
|
default boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param content 内容
|
||||||
|
* @param type 0:系统消息 1:待办消息
|
||||||
|
* @param receiverIds 接收人ID
|
||||||
|
* @param config 配置
|
||||||
|
*/
|
||||||
|
void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param content 内容
|
||||||
|
* @param type 0:系统消息 1:待办消息
|
||||||
|
* @param receiverLoginNames 接收人登录名
|
||||||
|
* @param config 配置
|
||||||
|
*/
|
||||||
|
void sendByLoginName(String title, String content, Integer type, List<String> receiverLoginNames, JSONObject config);
|
||||||
|
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.strategy.impl;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.budwk.app.sys.models.Sys_user;
|
||||||
|
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.models.GlobalMessage;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.models.GlobalMessageReceiver;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.strategy.GlobalMessageSendStrategy;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地消息发送策略
|
||||||
|
*/
|
||||||
|
@IocBean
|
||||||
|
@Slf4j
|
||||||
|
public class LocalMessageSendStrategy implements GlobalMessageSendStrategy {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Dao dao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlobalMessageChannel getChannel() {
|
||||||
|
return GlobalMessageChannel.LOCAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportAsync() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
|
||||||
|
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getId, "in", receiverIds));
|
||||||
|
sendMessage(title, content, type, users, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendByLoginName(String title, String content, Integer type, List<String> receiverLoginNames, JSONObject config) {
|
||||||
|
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getLoginname, "in", receiverLoginNames));
|
||||||
|
sendMessage(title, content, type, users, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessage(String title, String content, Integer type, List<Sys_user> receivers, JSONObject config) {
|
||||||
|
try {
|
||||||
|
log.info("开始发送本地消息,标题:{},接收人数量:{}", title, receivers.size());
|
||||||
|
|
||||||
|
// 消息主体
|
||||||
|
GlobalMessage globalMessage = new GlobalMessage();
|
||||||
|
globalMessage.setTitle(title);
|
||||||
|
globalMessage.setContent(content);
|
||||||
|
globalMessage.setType(type);
|
||||||
|
globalMessage.setSenderId(SecurityUtil.getUserId());
|
||||||
|
globalMessage.setSenderName(SecurityUtil.getUserUsername());
|
||||||
|
globalMessage.setSenderLoginName(SecurityUtil.getUserLoginname());
|
||||||
|
globalMessage.setStatus(1); // 草稿状态
|
||||||
|
globalMessage.setSendSuccess(false);
|
||||||
|
dao.insert(globalMessage);
|
||||||
|
|
||||||
|
// 消息接收人
|
||||||
|
List<GlobalMessageReceiver> receiverList = receivers.stream().map(r -> {
|
||||||
|
GlobalMessageReceiver receiver = new GlobalMessageReceiver();
|
||||||
|
receiver.setMessageId(globalMessage.getId());
|
||||||
|
receiver.setReceiverId(r.getId());
|
||||||
|
receiver.setReceiverName(r.getUsername()); // 修复:使用用户名而不是登录名
|
||||||
|
receiver.setReceiverLoginName(r.getLoginname());
|
||||||
|
receiver.setIsRead(false);
|
||||||
|
return receiver;
|
||||||
|
}).toList();
|
||||||
|
dao.insert(receiverList);
|
||||||
|
|
||||||
|
// 更新消息状态为已发送
|
||||||
|
globalMessage.setStatus(2);
|
||||||
|
globalMessage.setSendSuccess(true);
|
||||||
|
globalMessage.setSendResult("本地消息发送成功");
|
||||||
|
dao.update(globalMessage);
|
||||||
|
|
||||||
|
log.info("本地消息发送成功,消息ID:{},接收人数量:{}", globalMessage.getId(), receivers.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("本地消息发送失败,标题:{},错误信息:{}", title, e.getMessage(), e);
|
||||||
|
throw new RuntimeException("本地消息发送失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
package com.budwk.app.zhgh.dayofficework.message.strategy.impl;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.budwk.app.sys.models.Sys_user;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.strategy.GlobalMessageSendStrategy;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信发送策略
|
||||||
|
*/
|
||||||
|
@IocBean
|
||||||
|
@Slf4j
|
||||||
|
public class SmsMessageSendStrategy implements GlobalMessageSendStrategy {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Dao dao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlobalMessageChannel getChannel() {
|
||||||
|
return GlobalMessageChannel.SMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportAsync() {
|
||||||
|
return true; // 短信发送支持异步
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public boolean isEnabled() {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
|
||||||
|
try {
|
||||||
|
log.info("开始发送短信消息,标题:{},接收人数量:{}", title, receiverIds.size());
|
||||||
|
|
||||||
|
// 查询用户手机号
|
||||||
|
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getId, "in", receiverIds));
|
||||||
|
sendSmsMessage(title, content, type, users, config);
|
||||||
|
|
||||||
|
log.info("短信消息发送完成,标题:{},接收人数量:{}", title, users.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("短信消息发送失败,标题:{},错误:{}", title, e.getMessage(), e);
|
||||||
|
throw new RuntimeException("短信消息发送失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendByLoginName(String title, String content, Integer type, List<String> receiverLoginNames, JSONObject config) {
|
||||||
|
try {
|
||||||
|
log.info("开始发送短信消息(按登录名),标题:{},接收人数量:{}", title, receiverLoginNames.size());
|
||||||
|
|
||||||
|
// 查询用户手机号
|
||||||
|
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getLoginname, "in", receiverLoginNames));
|
||||||
|
sendSmsMessage(title, content, type, users, config);
|
||||||
|
|
||||||
|
log.info("短信消息发送完成(按登录名),标题:{},接收人数量:{}", title, users.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("短信消息发送失败(按登录名),标题:{},错误:{}", title, e.getMessage(), e);
|
||||||
|
throw new RuntimeException("短信消息发送失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送短信消息
|
||||||
|
*/
|
||||||
|
private void sendSmsMessage(String title, String content, Integer type, List<Sys_user> users, JSONObject config) {
|
||||||
|
for (Sys_user user : users) {
|
||||||
|
try {
|
||||||
|
String mobile = user.getMobile();
|
||||||
|
if (mobile == null || mobile.trim().isEmpty()) {
|
||||||
|
log.warn("用户{}没有手机号,跳过短信发送", user.getUsername());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建短信内容
|
||||||
|
String smsContent = buildSmsContent(title, content, type);
|
||||||
|
|
||||||
|
// 执行发送
|
||||||
|
|
||||||
|
boolean success = true;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
log.info("短信发送成功:用户{},手机号:{}", user.getUsername(), mobile);
|
||||||
|
} else {
|
||||||
|
log.warn("短信发送失败:用户{},手机号:{}", user.getUsername(), mobile);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送短信给用户{}时出错:{}", user.getUsername(), e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建短信内容
|
||||||
|
*/
|
||||||
|
private String buildSmsContent(String title, String content, Integer type) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
// 根据消息类型添加前缀
|
||||||
|
if (type != null && type == 2) {
|
||||||
|
sb.append("【待办通知】");
|
||||||
|
} else {
|
||||||
|
sb.append("【系统通知】");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加标题和内容
|
||||||
|
sb.append(title);
|
||||||
|
if (content != null && !content.trim().isEmpty()) {
|
||||||
|
sb.append(":").append(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制短信长度(一般短信限制70个字符)
|
||||||
|
String smsContent = sb.toString();
|
||||||
|
if (smsContent.length() > 67) {
|
||||||
|
smsContent = smsContent.substring(0, 67) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
return smsContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
-2
@@ -6,18 +6,24 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.budwk.app.base.page.Pagination;
|
import com.budwk.app.base.page.Pagination;
|
||||||
import com.budwk.app.base.param.PageForm;
|
import com.budwk.app.base.param.PageForm;
|
||||||
import com.budwk.app.base.result.Result;
|
import com.budwk.app.base.result.Result;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
|
||||||
import com.budwk.app.zhgh.staffbenefit.huimin.models.Huimin;
|
import com.budwk.app.zhgh.staffbenefit.huimin.models.Huimin;
|
||||||
import com.budwk.app.zhgh.staffbenefit.huimin.service.HuiminService;
|
import com.budwk.app.zhgh.staffbenefit.huimin.service.HuiminService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.dao.Sqls;
|
||||||
|
import org.nutz.dao.sql.Sql;
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.mvc.annotation.At;
|
import org.nutz.mvc.annotation.At;
|
||||||
import org.nutz.mvc.annotation.Ok;
|
import org.nutz.mvc.annotation.Ok;
|
||||||
import org.nutz.mvc.annotation.Param;
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/huimin/manage")
|
@At("/platform/huimin/manage")
|
||||||
@Api(("惠民服务管理"))
|
@Api(("惠民服务管理"))
|
||||||
@@ -28,6 +34,9 @@ public class HuiminManageController {
|
|||||||
@Inject
|
@Inject
|
||||||
private HuiminService huiminService;
|
private HuiminService huiminService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalMessageSendService globalMessageSendService;
|
||||||
|
|
||||||
@At("")
|
@At("")
|
||||||
@SaCheckPermission("huimin.manage")
|
@SaCheckPermission("huimin.manage")
|
||||||
@Ok("beetl:/platform/zhgh/staffbenefit/huimin/manage/index.html")
|
@Ok("beetl:/platform/zhgh/staffbenefit/huimin/manage/index.html")
|
||||||
@@ -37,11 +46,30 @@ public class HuiminManageController {
|
|||||||
@At
|
@At
|
||||||
@SaCheckPermission("huimin.manage")
|
@SaCheckPermission("huimin.manage")
|
||||||
public Result pageData(PageForm pageForm) {
|
public Result pageData(PageForm pageForm) {
|
||||||
|
Sql sql = Sqls.create("""
|
||||||
|
SELECT
|
||||||
|
t1.*
|
||||||
|
FROM
|
||||||
|
huimin t1
|
||||||
|
LEFT JOIN activity_user_scope aus ON t1.groupId = aus.groupId
|
||||||
|
AND aus.userId = @userId
|
||||||
|
$condition
|
||||||
|
""");
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
if(StrUtil.isNotBlank(pageForm.getSearchKeyword())){
|
if(StrUtil.isNotBlank(pageForm.getSearchKeyword())){
|
||||||
cnd.where().andLike(Huimin::getTitle, pageForm.getSearchKeyword());
|
cnd.where().andLike("t1.title", "%" + pageForm.getSearchKeyword() + "%");
|
||||||
}
|
}
|
||||||
Pagination pagination = huiminService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
|
cnd.and(Cnd.exps("aus.groupId","is not",null).or("t1.groupId","is",null));
|
||||||
|
sql.setCondition(cnd);
|
||||||
|
Pagination pagination = huiminService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||||
|
|
||||||
|
String title = "审批待办";
|
||||||
|
String content = "您有一个新的审批任务需要处理,请及时登录系统查看。";
|
||||||
|
List<String> receiverIds = Arrays.asList("00198c31ee094466845979ab8dabf83b", "00266dbf9e014376ab76072851b1a8ac");
|
||||||
|
|
||||||
|
// 自动发送到所有启用的渠道
|
||||||
|
globalMessageSendService.sendMessage(title, content, 2, receiverIds, null);
|
||||||
|
|
||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user