修改消息通过腾讯im发送
This commit is contained in:
parent
e2ef255d8f
commit
3a66394bbe
@ -25,13 +25,16 @@ import org.dromara.system.domain.SysMessage;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.domain.bo.SysMessageBo;
|
||||
import org.dromara.system.domain.bo.SysUserBo;
|
||||
import org.dromara.system.domain.vo.SysMessageTemplateVo;
|
||||
import org.dromara.system.domain.vo.SysMessageVo;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.system.service.ISysMessageService;
|
||||
import org.dromara.system.service.ISysMessageTemplateService;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -47,6 +50,7 @@ public class SysMessageController extends BaseController {
|
||||
|
||||
private final ISysMessageService messageService;
|
||||
private final ISysUserService userService;
|
||||
private final ISysMessageTemplateService templateService;
|
||||
|
||||
/**
|
||||
* 获取当前用户ID
|
||||
@ -61,7 +65,8 @@ public class SysMessageController extends BaseController {
|
||||
// @SaCheckPermission("system:message:list")
|
||||
@Tag(name = "查询消息列表")
|
||||
@PostMapping("/list")
|
||||
public R<Page<SysMessageVo>> list(@RequestBody SysMessageBo bo, @RequestBody Page<SysMessage> page) {
|
||||
public R<Page<SysMessageVo>> list(@RequestBody SysMessageBo bo, PageQuery pageQuery) {
|
||||
Page<SysMessage> page = pageQuery.build();
|
||||
Page<SysMessage> messagePage = messageService.page(page, bo.toWrapper());
|
||||
Page<SysMessageVo> voPage = new Page<>(messagePage.getCurrent(), messagePage.getSize(), messagePage.getTotal());
|
||||
voPage.setRecords(MapstructUtils.convert(messagePage.getRecords(), SysMessageVo.class));
|
||||
@ -86,15 +91,137 @@ public class SysMessageController extends BaseController {
|
||||
@Log(title = "消息管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/send")
|
||||
public R<Void> send(@Validated(AddGroup.class) @RequestBody SysMessageBo bo) {
|
||||
public R<Void> send(@RequestBody SysMessageBo bo) {
|
||||
// 设置发送者ID为当前登录用户ID
|
||||
bo.setSenderId(getUserId());
|
||||
|
||||
List<String> userIdStrings;
|
||||
// 如果提供了模板ID,则从模板中获取标题和内容
|
||||
Long templateId = bo.getTemplateId();
|
||||
if (templateId != null) {
|
||||
SysMessageTemplateVo template = templateService.selectTemplateById(templateId);
|
||||
if (template != null) {
|
||||
// 使用模板的标题和内容
|
||||
bo.setTitle(template.getTitle());
|
||||
bo.setContent(template.getTemplateContent());
|
||||
} else {
|
||||
return R.fail("未找到指定的消息模板");
|
||||
}
|
||||
}
|
||||
|
||||
// 获取第一个发送范围作为主要处理对象
|
||||
String scope = bo.getSendScope() != null && !bo.getSendScope().isEmpty() ?
|
||||
bo.getSendScope().get(0) : "";
|
||||
// 验证消息内容是否为空
|
||||
if (StringUtils.isBlank(bo.getContent())) {
|
||||
return R.fail("消息内容不能为空");
|
||||
}
|
||||
|
||||
// 验证发送范围是否为空
|
||||
List<String> sendScope = bo.getSendScope();
|
||||
if (sendScope == null || sendScope.isEmpty()) {
|
||||
return R.fail("发送范围不能为空");
|
||||
}
|
||||
|
||||
// 处理接收者
|
||||
List<String> userIdStrings = new ArrayList<>();
|
||||
|
||||
// 根据logmess值处理不同类型的消息接收者
|
||||
Integer logmess = bo.getLogmess();
|
||||
if (logmess != null) {
|
||||
// 获取发送范围作为主要处理对象
|
||||
if (sendScope == null || sendScope.isEmpty()) {
|
||||
return R.fail("发送范围不能为空");
|
||||
}
|
||||
|
||||
String scope = sendScope.get(0); // 获取第一个范围值
|
||||
|
||||
switch (logmess) {
|
||||
case 1: // 指定角色
|
||||
// 当logmess=1时,sendScope接收的是角色ID或特殊标识
|
||||
if ("all".equals(scope) || "expert".equals(scope) || "merchant".equals(scope) || "user".equals(scope)) {
|
||||
List<SysUserVo> users = userService.selectUserListByDept(null);
|
||||
List<Long> userIds;
|
||||
|
||||
switch (scope) {
|
||||
case "all":
|
||||
// 全部用户
|
||||
userIds = users.stream().map(SysUserVo::getUserId).toList();
|
||||
break;
|
||||
case "expert":
|
||||
// 达人
|
||||
userIds = users.stream()
|
||||
.filter(user -> "expert".equals(user.getUserType()))
|
||||
.map(SysUserVo::getUserId)
|
||||
.toList();
|
||||
break;
|
||||
case "merchant":
|
||||
// 商户
|
||||
userIds = users.stream()
|
||||
.filter(user -> "merchant".equals(user.getUserType()))
|
||||
.map(SysUserVo::getUserId)
|
||||
.toList();
|
||||
break;
|
||||
case "user":
|
||||
// 普通用户
|
||||
userIds = users.stream()
|
||||
.filter(user -> "user".equals(user.getUserType()))
|
||||
.map(SysUserVo::getUserId)
|
||||
.toList();
|
||||
break;
|
||||
default:
|
||||
userIds = List.of();
|
||||
}
|
||||
userIdStrings = userIds.stream().map(String::valueOf).toList();
|
||||
} else if (StringUtils.isNotBlank(scope)) {
|
||||
try {
|
||||
// 尝试将scope解析为角色ID
|
||||
Long roleId = Long.parseLong(scope);
|
||||
List<Long> userIds = userService.selectUserIdsByRoleId(roleId);
|
||||
if (userIds != null && !userIds.isEmpty()) {
|
||||
userIdStrings = userIds.stream().map(String::valueOf).toList();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return R.fail("角色ID格式不正确");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: // 指定用户
|
||||
// 当logmess=2时,sendScope接收的是用户ID数组
|
||||
if (sendScope != null && !sendScope.isEmpty()) {
|
||||
userIdStrings = new ArrayList<>();
|
||||
for (String userId : sendScope) {
|
||||
if (StringUtils.isNotBlank(userId)) {
|
||||
userIdStrings.add(userId);
|
||||
}
|
||||
}
|
||||
} else if (bo.getUserIds() != null && !bo.getUserIds().isEmpty()) {
|
||||
// 如果没有指定scope但有userIds,使用userIds
|
||||
userIdStrings = bo.getUserIds().stream().map(String::valueOf).toList();
|
||||
}
|
||||
break;
|
||||
case 3: // 指定群组/部门
|
||||
// 当logmess=3时,sendScope接收的是群组/部门ID
|
||||
if (StringUtils.isNotBlank(scope)) {
|
||||
try {
|
||||
Long deptId = Long.parseLong(scope);
|
||||
List<SysUserVo> users = userService.selectUserListByDept(deptId);
|
||||
if (users != null && !users.isEmpty()) {
|
||||
userIdStrings = users.stream()
|
||||
.map(user -> String.valueOf(user.getUserId()))
|
||||
.toList();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return R.fail("群组/部门ID格式不正确");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return R.fail("不支持的消息类型");
|
||||
}
|
||||
} else {
|
||||
// 如果没有指定logmess,则使用原有的处理逻辑
|
||||
if (sendScope == null || sendScope.isEmpty()) {
|
||||
return R.fail("发送范围不能为空");
|
||||
}
|
||||
|
||||
String scope = sendScope.get(0); // 获取第一个范围值
|
||||
|
||||
// 如果是群发消息类型,则根据类型筛选用户
|
||||
if ("all".equals(scope) || "expert".equals(scope) || "merchant".equals(scope) || "user".equals(scope)) {
|
||||
@ -136,15 +263,18 @@ public class SysMessageController extends BaseController {
|
||||
if (bo.getUserIds() != null && !bo.getUserIds().isEmpty()) {
|
||||
// 如果userIds不为空,使用它
|
||||
userIdStrings = bo.getUserIds().stream().map(String::valueOf).toList();
|
||||
} else if (bo.getSendScope() != null && !bo.getSendScope().isEmpty()) {
|
||||
// 如果userIds为空但sendScope不为空,将sendScope中的值作为用户ID使用
|
||||
userIdStrings = bo.getSendScope();
|
||||
} else {
|
||||
// 都为空,返回空列表
|
||||
userIdStrings = List.of();
|
||||
} else if (sendScope != null && !sendScope.isEmpty()) {
|
||||
// 如果userIds为空但sendScope不为空,将sendScope作为用户ID使用
|
||||
userIdStrings = sendScope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到接收者,返回失败
|
||||
if (userIdStrings.isEmpty()) {
|
||||
return R.fail("未找到消息接收者");
|
||||
}
|
||||
|
||||
return toAjax(messageService.sendMessageToUsers(bo, userIdStrings));
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,16 @@
|
||||
package org.dromara.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
@ -60,6 +63,22 @@ public class SysMessageTemplateController extends BaseController {
|
||||
SysMessageTemplate template = templateService.getById(id);
|
||||
return R.ok(MapstructUtils.convert(template, SysMessageTemplateVo.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查询消息模板列表(不分页)
|
||||
*
|
||||
* @param name 模板名称(可选,支持模糊查询)
|
||||
*/
|
||||
@SaCheckPermission("system:message:template:query")
|
||||
@Operation(summary = "根据名称查询消息模板列表(不分页)")
|
||||
@GetMapping("/listByName")
|
||||
public R<List<SysMessageTemplateVo>> listByName(@RequestParam(value = "name", required = false) String[] name) {
|
||||
String searchName = null;
|
||||
if (name != null && name.length > 0) {
|
||||
searchName = name[0];
|
||||
}
|
||||
return R.ok(templateService.selectTemplateListByName(searchName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增消息模板
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.dromara.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
@ -10,6 +11,8 @@ import org.dromara.common.tenant.core.TenantEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息对象 sys_message
|
||||
@ -47,4 +50,8 @@ public class SysMessage extends BaseAudit {
|
||||
|
||||
// /** 状态(0正常 1停用) */
|
||||
// private String status;
|
||||
|
||||
/** 扩展参数 */
|
||||
@TableField(exist = false)
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
}
|
||||
|
@ -32,10 +32,10 @@ public class SysMessageTemplate extends BaseAudit {
|
||||
private String templateType;
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 模板编码
|
||||
*/
|
||||
private String templateCode;
|
||||
// /**
|
||||
// * 模板编码
|
||||
// */
|
||||
// private String templateCode;
|
||||
|
||||
/**
|
||||
* 模板名称
|
||||
|
@ -112,4 +112,13 @@ public class SysUser extends TenantEntity {
|
||||
return SystemConstants.SUPER_ADMIN_ID.equals(this.userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置更新者,父类使用LocalDateTime而这里使用Long类型
|
||||
*
|
||||
* @param updateBy 更新者ID
|
||||
*/
|
||||
// @Override
|
||||
// public void setUpdateBy(Long updateBy) {
|
||||
// super.setUpdateBy(updateBy);
|
||||
// }
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.system.domain.SysMessage;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -27,7 +26,6 @@ import java.util.List;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = SysMessage.class, reverseConvertGenerate = false)
|
||||
public class SysMessageBo extends BaseAudit {
|
||||
|
||||
/** 主键ID */
|
||||
@ -35,7 +33,7 @@ public class SysMessageBo extends BaseAudit {
|
||||
private Long id;
|
||||
|
||||
/** 消息标题 */
|
||||
@NotBlank(message = "消息标题不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
// @NotBlank(message = "消息标题不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 0, max = 100, message = "消息标题长度不能超过100个字符")
|
||||
@ExcelProperty(value = "消息标题")
|
||||
private String title;
|
||||
@ -75,6 +73,12 @@ public class SysMessageBo extends BaseAudit {
|
||||
/** 扩展数据(JSON格式) */
|
||||
private String extraData;
|
||||
|
||||
/** 消息类型标识 1=指定角色 2=指定用户 3=指定群 */
|
||||
private Integer logmess;
|
||||
|
||||
/** 模板ID */
|
||||
private Long templateId;
|
||||
|
||||
/** 状态(0正常 1停用) */
|
||||
private String status;
|
||||
|
||||
|
@ -43,9 +43,9 @@ public class SysMessageTemplateBo extends BaseAudit {
|
||||
/**
|
||||
* 模板编码
|
||||
*/
|
||||
@NotBlank(message = "模板编码不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(max = 50, message = "模板编码长度不能超过{max}个字符")
|
||||
private String templateCode;
|
||||
// @NotBlank(message = "模板编码不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
// @Size(max = 50, message = "模板编码长度不能超过{max}个字符")
|
||||
// private String templateCode;
|
||||
|
||||
/**
|
||||
* 模板类型(SMS=短信 MAIL=邮件 WECHAT=微信 SYSTEM=系统消息)
|
||||
@ -79,7 +79,7 @@ public class SysMessageTemplateBo extends BaseAudit {
|
||||
public LambdaQueryWrapper<SysMessageTemplate> toWrapper() {
|
||||
LambdaQueryWrapper<SysMessageTemplate> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.like(StringUtils.isNotBlank(templateName), SysMessageTemplate::getTemplateName, templateName)
|
||||
.like(StringUtils.isNotBlank(templateCode), SysMessageTemplate::getTemplateCode, templateCode)
|
||||
// .like(StringUtils.isNotBlank(templateCode), SysMessageTemplate::getTemplateCode, templateCode)
|
||||
.eq(StringUtils.isNotBlank(templateType), SysMessageTemplate::getTemplateType, templateType)
|
||||
.eq(StringUtils.isNotBlank(status), SysMessageTemplate::getStatus, status)
|
||||
.orderByDesc(SysMessageTemplate::getCreateTime);
|
||||
@ -93,7 +93,7 @@ public class SysMessageTemplateBo extends BaseAudit {
|
||||
SysMessageTemplate entity = new SysMessageTemplate();
|
||||
entity.setId(id);
|
||||
entity.setTemplateName(templateName);
|
||||
entity.setTemplateCode(templateCode);
|
||||
// entity.setTemplateCode(templateCode);
|
||||
entity.setTemplateType(templateType);
|
||||
entity.setTemplateContent(templateContent);
|
||||
entity.setStatus(status);
|
||||
|
@ -8,6 +8,7 @@ import org.dromara.system.domain.SysMessage;
|
||||
import org.dromara.system.domain.SysMessageTemplate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 消息模板视图对象 sys_message_template
|
||||
@ -30,16 +31,21 @@ public class SysMessageTemplateVo implements Serializable {
|
||||
*/
|
||||
private String templateType;
|
||||
|
||||
/**
|
||||
* 模板编码
|
||||
*/
|
||||
private String templateCode;
|
||||
// /**
|
||||
// * 模板编码
|
||||
// */
|
||||
// private String templateCode;
|
||||
|
||||
/**
|
||||
* 模板名称
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 模板标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 模板内容
|
||||
*/
|
||||
@ -55,6 +61,26 @@ public class SysMessageTemplateVo implements Serializable {
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
// /**
|
||||
// * 备注
|
||||
// */
|
||||
|
@ -10,6 +10,8 @@ import org.dromara.system.domain.SysNotice;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息视图对象
|
||||
@ -17,6 +19,7 @@ import java.util.Date;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = SysMessage.class)
|
||||
public class SysMessageVo implements Serializable {
|
||||
|
||||
@ -62,4 +65,16 @@ public class SysMessageVo implements Serializable {
|
||||
/** 创建时间 */
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
private Date updateTime;
|
||||
|
||||
/** 创建者 */
|
||||
private String createBy;
|
||||
|
||||
/** 更新者 */
|
||||
private String updateBy;
|
||||
|
||||
/** 扩展参数 */
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
}
|
||||
|
@ -8,12 +8,19 @@ import org.dromara.system.config.RocketMQConfig;
|
||||
import org.dromara.system.consumer.MessageRocketMQConsumer;
|
||||
import org.dromara.system.domain.event.MessageEvent;
|
||||
import org.dromara.system.service.IRocketMQService;
|
||||
import org.dromara.system.service.ITencentIMService;
|
||||
import org.dromara.system.websocket.MessageWebSocketServer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息事件监听器
|
||||
*
|
||||
@ -28,6 +35,10 @@ public class MessageEventListener {
|
||||
|
||||
@Autowired
|
||||
private MessageWebSocketServer messageWebSocketServer;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("systemTencentIMService")
|
||||
private ITencentIMService tencentIMService;
|
||||
|
||||
/**
|
||||
* 处理消息事件
|
||||
@ -36,27 +47,115 @@ public class MessageEventListener {
|
||||
@EventListener
|
||||
public void handleMessageEvent(MessageEvent event) {
|
||||
try {
|
||||
// 提取消息和用户ID
|
||||
String userId = event.getUserIdStr();
|
||||
|
||||
// 创建消息包装对象
|
||||
MessageRocketMQConsumer.MessageWrapper wrapper = new MessageRocketMQConsumer.MessageWrapper();
|
||||
wrapper.setUserId(event.getUserIdStr());
|
||||
wrapper.setUserId(userId);
|
||||
wrapper.setMessage(event.getMessage());
|
||||
|
||||
// 尝试通过RocketMQ发送消息
|
||||
boolean sendSuccess = false;
|
||||
|
||||
// 首先尝试发送消息到腾讯IM
|
||||
boolean tencentIMSendSuccess = false;
|
||||
try {
|
||||
// 消息发送者可能是系统或管理员,这里使用固定的管理员账号作为发送者
|
||||
String fromUserId = "administrator";
|
||||
String toUserId = userId; // 接收者是事件中的用户ID
|
||||
String content = JsonUtils.toJsonString(event.getMessage()); // 消息内容序列化为JSON
|
||||
|
||||
// 处理消息变量替换(如果有)
|
||||
Map<String, String> variables = new HashMap<>();
|
||||
// 这里可以添加变量映射,例如:variables.put("userName", "张三");
|
||||
|
||||
// 从消息中提取标题和描述
|
||||
String title = event.getMessage().getTitle();
|
||||
String desc = event.getMessage().getContent();
|
||||
|
||||
// 处理变量替换
|
||||
if (variables.size() > 0) {
|
||||
title = tencentIMService.processMessageVariables(title, variables);
|
||||
desc = tencentIMService.processMessageVariables(desc, variables);
|
||||
}
|
||||
|
||||
// 扩展字段,可以在APP端获取
|
||||
String ext = null;
|
||||
if (event.getMessage().getParams() != null && !event.getMessage().getParams().isEmpty()) {
|
||||
ext = JsonUtils.toJsonString(event.getMessage().getParams());
|
||||
}
|
||||
|
||||
// 根据消息类型决定推送方式
|
||||
Map<String, Object> params = event.getMessage().getParams();
|
||||
String pushType = null;
|
||||
if (params != null && params.containsKey("pushType")) {
|
||||
pushType = String.valueOf(params.get("pushType"));
|
||||
}
|
||||
|
||||
if (pushType != null) {
|
||||
switch (pushType) {
|
||||
case "ALL":
|
||||
// 全员推送
|
||||
tencentIMSendSuccess = tencentIMService.pushToAll(title, desc, true, ext);
|
||||
break;
|
||||
case "ATTRIBUTE":
|
||||
// 属性推送
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
// 从消息参数中提取属性条件
|
||||
if (params != null && params.containsKey("attributes")) {
|
||||
Object attrObj = params.get("attributes");
|
||||
if (attrObj instanceof Map) {
|
||||
attributes = (Map<String, Object>) attrObj;
|
||||
}
|
||||
}
|
||||
tencentIMSendSuccess = tencentIMService.pushByAttributes(title, desc, attributes, true, ext);
|
||||
break;
|
||||
case "TAG":
|
||||
// 标签推送
|
||||
if (params != null && params.containsKey("tags")) {
|
||||
Object tagsObj = params.get("tags");
|
||||
if (tagsObj instanceof String[]) {
|
||||
tencentIMSendSuccess = tencentIMService.pushByTags(title, desc,
|
||||
Arrays.asList((String[]) tagsObj), true, ext);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "USER":
|
||||
default:
|
||||
// 单用户推送(默认)
|
||||
tencentIMSendSuccess = tencentIMService.pushToUsers(title, desc,
|
||||
Collections.singletonList(toUserId), true, ext);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 默认为单用户推送
|
||||
tencentIMSendSuccess = tencentIMService.sendMessageToTencentIM(fromUserId, toUserId, content);
|
||||
}
|
||||
|
||||
if (tencentIMSendSuccess) {
|
||||
log.info("消息已成功发送到腾讯IM,接收者: {}", toUserId);
|
||||
} else {
|
||||
log.warn("发送消息到腾讯IM失败,将尝试通过其他方式发送");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息到腾讯IM异常,将尝试通过其他方式发送", e);
|
||||
}
|
||||
|
||||
// 无论腾讯IM是否发送成功,都继续尝试通过RocketMQ发送消息
|
||||
// 这样可以确保消息至少通过一种方式发送出去
|
||||
boolean rocketMQSendSuccess = false;
|
||||
try {
|
||||
rocketMQService.sendMessage(
|
||||
RocketMQConfig.TOPIC_SYS_MSG,
|
||||
RocketMQConfig.TAG_SYS_MSG,
|
||||
wrapper
|
||||
);
|
||||
sendSuccess = true;
|
||||
log.info("消息事件已通过RocketMQ发送,userId: {}", event.getUserIdStr());
|
||||
rocketMQSendSuccess = true;
|
||||
log.info("消息事件已通过RocketMQ发送,userId: {}", userId);
|
||||
} catch (Exception e) {
|
||||
log.error("通过RocketMQ发送消息失败,将尝试直接通过WebSocket发送", e);
|
||||
}
|
||||
|
||||
// 如果RocketMQ发送失败,则尝试直接通过WebSocket发送
|
||||
if (!sendSuccess && event.getUserId() != null) {
|
||||
// 如果前两种方式都失败,则尝试直接通过WebSocket发送
|
||||
if (!tencentIMSendSuccess && !rocketMQSendSuccess && event.getUserId() != null) {
|
||||
try {
|
||||
messageWebSocketServer.sendMessage(
|
||||
event.getUserId(),
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.dromara.system.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.dromara.system.domain.SysTenant;
|
||||
import org.dromara.system.domain.vo.SysTenantVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
@ -9,6 +10,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysTenantMapper extends BaseMapperPlus<SysTenant, SysTenantVo> {
|
||||
|
||||
}
|
||||
|
@ -72,4 +72,12 @@ public interface ISysMessageTemplateService extends IService<SysMessageTemplate>
|
||||
* @return 结果
|
||||
*/
|
||||
int deleteTemplateById(Long id);
|
||||
|
||||
/**
|
||||
* 根据名称查询消息模板列表(不分页)
|
||||
*
|
||||
* @param name 模板名称(可选,支持模糊查询)
|
||||
* @return 消息模板列表
|
||||
*/
|
||||
List<SysMessageTemplateVo> selectTemplateListByName(String name);
|
||||
}
|
||||
|
@ -251,4 +251,12 @@ public interface ISysUserService {
|
||||
* @return 会员用户列表
|
||||
*/
|
||||
List<SysUserVo> selectMemberUsers(String keyword);
|
||||
|
||||
/**
|
||||
* 根据角色ID查询用户ID列表
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @return 用户ID列表
|
||||
*/
|
||||
List<Long> selectUserIdsByRoleId(Long roleId);
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
package org.dromara.system.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 腾讯IM服务接口
|
||||
*
|
||||
* @author wzj
|
||||
*/
|
||||
public interface ITencentIMService {
|
||||
|
||||
/**
|
||||
* 发送消息到腾讯IM
|
||||
*
|
||||
* @param fromUserId 发送方用户ID
|
||||
* @param toUserId 接收方用户ID
|
||||
* @param content 消息内容
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
boolean sendMessageToTencentIM(String fromUserId, String toUserId, String content);
|
||||
|
||||
/**
|
||||
* 创建腾讯IM账号
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户签名
|
||||
*/
|
||||
String createTencentIMAccount(String userId);
|
||||
|
||||
/**
|
||||
* 推送消息给全体用户
|
||||
*
|
||||
* @param title 消息标题
|
||||
* @param desc 消息描述
|
||||
* @param offlinePush 是否离线推送
|
||||
* @param ext 扩展字段(JSON字符串)
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
boolean pushToAll(String title, String desc, boolean offlinePush, String ext);
|
||||
|
||||
/**
|
||||
* 根据用户属性推送消息
|
||||
*
|
||||
* @param title 消息标题
|
||||
* @param desc 消息描述
|
||||
* @param attributes 用户属性条件
|
||||
* @param offlinePush 是否离线推送
|
||||
* @param ext 扩展字段(JSON字符串)
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
boolean pushByAttributes(String title, String desc, Map<String, Object> attributes, boolean offlinePush, String ext);
|
||||
|
||||
/**
|
||||
* 根据标签推送消息
|
||||
*
|
||||
* @param title 消息标题
|
||||
* @param desc 消息描述
|
||||
* @param tags 标签列表
|
||||
* @param offlinePush 是否离线推送
|
||||
* @param ext 扩展字段(JSON字符串)
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
boolean pushByTags(String title, String desc, List<String> tags, boolean offlinePush, String ext);
|
||||
|
||||
/**
|
||||
* 推送消息给指定用户
|
||||
*
|
||||
* @param title 消息标题
|
||||
* @param desc 消息描述
|
||||
* @param userIds 用户ID列表
|
||||
* @param offlinePush 是否离线推送
|
||||
* @param ext 扩展字段(JSON字符串)
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
boolean pushToUsers(String title, String desc, List<String> userIds, boolean offlinePush, String ext);
|
||||
|
||||
/**
|
||||
* 处理消息变量替换
|
||||
*
|
||||
* @param content 原始内容
|
||||
* @param variables 变量映射
|
||||
* @return 替换后的内容
|
||||
*/
|
||||
String processMessageVariables(String content, Map<String, String> variables);
|
||||
}
|
@ -85,12 +85,28 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
// 保存消息
|
||||
SysMessage entity = message.toEntity();
|
||||
messageMapper.insert(entity);
|
||||
// 创建消息用户关联
|
||||
SysMessageUser messageUser = new SysMessageUser();
|
||||
messageUser.setMessageId(entity.getId());
|
||||
messageUser.setUserId(userId);
|
||||
messageUser.setIsRead(false);
|
||||
int rows = messageUserMapper.insert(messageUser);
|
||||
|
||||
// 检查消息与用户关联是否已存在
|
||||
LambdaQueryWrapper<SysMessageUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SysMessageUser::getMessageId, entity.getId())
|
||||
.eq(SysMessageUser::getUserId, userId);
|
||||
int count = Math.toIntExact(messageUserMapper.selectCount(queryWrapper));
|
||||
|
||||
// 只有当关联不存在时才创建
|
||||
int rows = 0;
|
||||
if (count == 0) {
|
||||
// 创建消息用户关联
|
||||
SysMessageUser messageUser = new SysMessageUser();
|
||||
messageUser.setMessageId(entity.getId());
|
||||
messageUser.setUserId(userId);
|
||||
messageUser.setIsRead(false);
|
||||
rows = messageUserMapper.insert(messageUser);
|
||||
} else {
|
||||
log.info("消息与用户关联已存在,跳过创建: messageId={}, userId={}", entity.getId(), userId);
|
||||
// 已存在也认为操作成功
|
||||
rows = 1;
|
||||
}
|
||||
|
||||
// 发送WebSocket消息
|
||||
if (rows > 0) {
|
||||
SysMessageVo messageVo = MapstructUtils.convert(entity, SysMessageVo.class);
|
||||
@ -119,17 +135,33 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
|
||||
try {
|
||||
Long userId = Long.parseLong(userIdStr);
|
||||
SysMessageUser messageUser = new SysMessageUser();
|
||||
messageUser.setMessageId(entity.getId());
|
||||
messageUser.setUserId(userId);
|
||||
messageUser.setIsRead(false);
|
||||
int rows = messageUserMapper.insert(messageUser);
|
||||
if (rows > 0) {
|
||||
|
||||
// 检查消息与用户关联是否已存在
|
||||
LambdaQueryWrapper<SysMessageUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SysMessageUser::getMessageId, entity.getId())
|
||||
.eq(SysMessageUser::getUserId, userId);
|
||||
int existCount = Math.toIntExact(messageUserMapper.selectCount(queryWrapper));
|
||||
|
||||
// 只有当关联不存在时才创建
|
||||
if (existCount == 0) {
|
||||
SysMessageUser messageUser = new SysMessageUser();
|
||||
messageUser.setMessageId(entity.getId());
|
||||
messageUser.setUserId(userId);
|
||||
messageUser.setIsRead(false);
|
||||
int rows = messageUserMapper.insert(messageUser);
|
||||
if (rows > 0) {
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
log.info("消息与用户关联已存在,跳过创建: messageId={}, userId={}", entity.getId(), userId);
|
||||
// 已存在也计入成功数量
|
||||
count++;
|
||||
// 发送WebSocket消息
|
||||
SysMessageVo messageVo = MapstructUtils.convert(entity, SysMessageVo.class);
|
||||
eventPublisher.publishEvent(MessageEvent.createWithStringUserId(this, messageVo, userIdStr));
|
||||
}
|
||||
|
||||
// 无论关联是否新建,都发送事件通知
|
||||
SysMessageVo messageVo = MapstructUtils.convert(entity, SysMessageVo.class);
|
||||
eventPublisher.publishEvent(MessageEvent.createWithStringUserId(this, messageVo, userIdStr));
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("无法将String类型的用户ID转换为Long: {}", userIdStr);
|
||||
}
|
||||
@ -235,7 +267,17 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
|
||||
return sendMessageToUser(message, userIdLong);
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("无法将String类型的用户ID转换为Long: {}", userId);
|
||||
return 0;
|
||||
|
||||
// 尝试以字符串形式发送
|
||||
// 保存消息
|
||||
SysMessage entity = message.toEntity();
|
||||
messageMapper.insert(entity);
|
||||
|
||||
// 发送事件通知
|
||||
SysMessageVo messageVo = MapstructUtils.convert(entity, SysMessageVo.class);
|
||||
eventPublisher.publishEvent(MessageEvent.createWithStringUserId(this, messageVo, userId));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ public class SysMessageTemplateServiceImpl extends ServiceImpl<SysMessageTemplat
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getTemplateType()), SysMessageTemplate::getTemplateType, bo.getTemplateType())
|
||||
// .eq(StringUtils.isNotBlank(bo.getStatus()), SysMessageTemplate::getStatus, bo.getStatus())
|
||||
.like(StringUtils.isNotBlank(bo.getTemplateName()), SysMessageTemplate::getTemplateName, bo.getTemplateName())
|
||||
.like(StringUtils.isNotBlank(bo.getTemplateCode()), SysMessageTemplate::getTemplateCode, bo.getTemplateCode())
|
||||
// .like(StringUtils.isNotBlank(bo.getTemplateCode()), SysMessageTemplate::getTemplateCode, bo.getTemplateCode())
|
||||
.orderByDesc(SysMessageTemplate::getCreateTime);
|
||||
|
||||
Page<SysMessageTemplate> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
|
||||
@ -51,7 +51,7 @@ public class SysMessageTemplateServiceImpl extends ServiceImpl<SysMessageTemplat
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getTemplateType()), SysMessageTemplate::getTemplateType, bo.getTemplateType())
|
||||
// .eq(StringUtils.isNotBlank(bo.getStatus()), SysMessageTemplate::getStatus, bo.getStatus())
|
||||
.like(StringUtils.isNotBlank(bo.getTemplateName()), SysMessageTemplate::getTemplateName, bo.getTemplateName())
|
||||
.like(StringUtils.isNotBlank(bo.getTemplateCode()), SysMessageTemplate::getTemplateCode, bo.getTemplateCode())
|
||||
// .like(StringUtils.isNotBlank(bo.getTemplateCode()), SysMessageTemplate::getTemplateCode, bo.getTemplateCode())
|
||||
.orderByDesc(SysMessageTemplate::getCreateTime);
|
||||
return MapstructUtils.convert(templateMapper.selectList(lqw), SysMessageTemplateVo.class);
|
||||
}
|
||||
@ -80,4 +80,14 @@ public class SysMessageTemplateServiceImpl extends ServiceImpl<SysMessageTemplat
|
||||
public int deleteTemplateById(Long id) {
|
||||
return templateMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysMessageTemplateVo> selectTemplateListByName(String name) {
|
||||
LambdaQueryWrapper<SysMessageTemplate> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
queryWrapper.like(SysMessageTemplate::getTemplateName, name);
|
||||
}
|
||||
queryWrapper.orderByDesc(SysMessageTemplate::getCreateTime);
|
||||
return MapstructUtils.convert(templateMapper.selectList(queryWrapper), SysMessageTemplateVo.class);
|
||||
}
|
||||
}
|
||||
|
@ -796,6 +796,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
|
||||
@Override
|
||||
public List<SysUserVo> selectMemberUsers(String keyword) {
|
||||
// 尝试获取会员服务
|
||||
try {
|
||||
// 获取 Spring 上下文中的 MemberService bean
|
||||
IMemberService memberService = SpringUtils.getBean(IMemberService.class);
|
||||
@ -807,12 +808,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
// 如果有关键词,添加模糊查询条件(昵称、用户名、手机号)
|
||||
if (StringUtils.isNotBlank(keyword)) {
|
||||
queryWrapper.like(Member::getNickname, keyword)
|
||||
.or()
|
||||
.like(Member::getUserName, keyword)
|
||||
.or()
|
||||
.like(Member::getPhoneEncrypted, keyword)
|
||||
.or()
|
||||
.like(Member::getPhoneHidden, keyword);
|
||||
.or()
|
||||
.like(Member::getUserName, keyword)
|
||||
.or()
|
||||
.like(Member::getPhoneEncrypted, keyword)
|
||||
.or()
|
||||
.like(Member::getPhoneHidden, keyword);
|
||||
}
|
||||
|
||||
// 查询会员信息
|
||||
@ -856,4 +857,15 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色ID查询用户ID列表
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @return 用户ID列表
|
||||
*/
|
||||
@Override
|
||||
public List<Long> selectUserIdsByRoleId(Long roleId) {
|
||||
return userRoleMapper.selectUserIdsByRoleId(roleId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,447 @@
|
||||
package org.dromara.system.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.system.service.ITencentIMService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 腾讯IM服务实现类
|
||||
*
|
||||
* @author wzj
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("systemTencentIMService")
|
||||
public class TencentIMServiceImpl implements ITencentIMService {
|
||||
|
||||
@Value("${tencent.im.sdkappid}")
|
||||
private long sdkAppId;
|
||||
|
||||
@Value("${tencent.im.secretkey}")
|
||||
private String secretKey;
|
||||
|
||||
@Value("${tencent.im.admin}")
|
||||
private String adminAccount;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
// 变量替换的正则表达式模式
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$(\\w+)");
|
||||
|
||||
/**
|
||||
* 生成用户签名
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户签名
|
||||
*/
|
||||
private String generateUserSig(String userId) {
|
||||
// 注意: 这里需要实现腾讯云IM SDK的TLSSigAPIv2签名生成
|
||||
// 参考腾讯云文档: https://cloud.tencent.com/document/product/269/32688
|
||||
// 实现方法:
|
||||
// 1. 导入腾讯云IM SDK依赖
|
||||
// 2. 使用sdkAppId和secretKey生成签名
|
||||
// 示例代码:
|
||||
// TLSSigAPIv2 tlsSigApi = new TLSSigAPIv2(sdkAppId, secretKey);
|
||||
// return tlsSigApi.genUserSig(userId, 86400); // 有效期设为1天(86400秒)
|
||||
|
||||
log.info("生成用户签名: {}", userId);
|
||||
|
||||
// 当前为模拟实现,实际项目中请替换为真实签名生成逻辑
|
||||
try {
|
||||
// 临时返回一个固定值用于测试
|
||||
// 注意:这是临时方案,正式环境必须替换为正确实现
|
||||
return "eJyrVgrxDXFSsrS0MDA2MzY1NTawNDAzsTQ1sTCyMDI2NdRRKs9ILUpVsjKoLEgsKk7NswlPzYnIsagoyIgrsYl3Lg5IALITi5BkS1JzSlOLbMJKi32DKtNCglONDSNNXfMqk3wN0myrvVUYGBgYGZiaGBoamCgZ6hlYGOUb6Rmaq1QHAAD--wJe";
|
||||
} catch (Exception e) {
|
||||
log.error("生成用户签名异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成管理员签名
|
||||
*
|
||||
* @return 管理员签名
|
||||
*/
|
||||
private String generateAdminUserSig() {
|
||||
return generateUserSig(adminAccount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createTencentIMAccount(String userId) {
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
String random = String.valueOf(System.currentTimeMillis());
|
||||
|
||||
// 构建API请求URL
|
||||
String url = String.format(
|
||||
"https://console.tim.qq.com/v4/im_open_login_svc/account_import" +
|
||||
"?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json",
|
||||
sdkAppId,
|
||||
adminAccount,
|
||||
userSig,
|
||||
random);
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("UserID", userId);
|
||||
requestBody.put("Nick", userId);
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
log.info("创建腾讯IM账号结果: {}", response.getBody());
|
||||
|
||||
// 生成并返回用户签名
|
||||
return generateUserSig(userId);
|
||||
} catch (Exception e) {
|
||||
log.error("创建腾讯IM账号异常: {}", userId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendMessageToTencentIM(String fromUserId, String toUserId, String content) {
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
String random = String.valueOf(new Random().nextInt(999999999));
|
||||
|
||||
// 构建API请求URL
|
||||
String url = String.format(
|
||||
"https://console.tim.qq.com/v4/openim/sendmsg" +
|
||||
"?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json",
|
||||
sdkAppId,
|
||||
adminAccount,
|
||||
userSig,
|
||||
random);
|
||||
|
||||
// 构建消息内容
|
||||
Map<String, Object> msgContent = new HashMap<>();
|
||||
msgContent.put("Text", content);
|
||||
|
||||
// 构建消息体
|
||||
Map<String, Object> msgBody = new HashMap<>();
|
||||
msgBody.put("MsgType", "TIMTextElem");
|
||||
msgBody.put("MsgContent", msgContent);
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("SyncOtherMachine", 2); // 消息不同步至发送方
|
||||
requestBody.put("From_Account", fromUserId);
|
||||
requestBody.put("To_Account", toUserId);
|
||||
requestBody.put("MsgRandom", Integer.parseInt(random));
|
||||
requestBody.put("MsgBody", new Object[]{msgBody});
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
log.info("发送消息到腾讯IM结果: {}", response.getBody());
|
||||
|
||||
// 解析响应判断是否成功
|
||||
JSONObject responseJson = JSONObject.parseObject(response.getBody());
|
||||
return "OK".equals(responseJson.getString("ActionStatus"));
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息到腾讯IM异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushToAll(String title, String desc, boolean offlinePush, String ext) {
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
String random = String.valueOf(new Random().nextInt(999999999));
|
||||
|
||||
// 构建API请求URL - 使用全员推送接口
|
||||
String url = String.format(
|
||||
"https://console.tim.qq.com/v4/all_member_push/im_push" +
|
||||
"?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json",
|
||||
sdkAppId,
|
||||
adminAccount,
|
||||
userSig,
|
||||
random);
|
||||
|
||||
// 构建消息内容
|
||||
Map<String, Object> msgContent = new HashMap<>();
|
||||
msgContent.put("Title", title);
|
||||
msgContent.put("Desc", desc);
|
||||
|
||||
// 构建消息体
|
||||
Map<String, Object> msgBody = new HashMap<>();
|
||||
msgBody.put("MsgType", "TIMCustomElem");
|
||||
msgBody.put("MsgContent", msgContent);
|
||||
|
||||
// 如果有扩展字段,添加到消息内容中
|
||||
if (ext != null && !ext.isEmpty()) {
|
||||
msgContent.put("Ext", ext);
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("From_Account", adminAccount);
|
||||
requestBody.put("MsgRandom", Integer.parseInt(random));
|
||||
requestBody.put("MsgBody", new Object[]{msgBody});
|
||||
|
||||
// 设置离线推送信息
|
||||
if (offlinePush) {
|
||||
Map<String, Object> offlinePushInfo = new HashMap<>();
|
||||
offlinePushInfo.put("PushFlag", 1); // 1表示推送,0表示不离线推送
|
||||
offlinePushInfo.put("Title", title);
|
||||
offlinePushInfo.put("Desc", desc);
|
||||
offlinePushInfo.put("Ext", ext);
|
||||
requestBody.put("OfflinePushInfo", offlinePushInfo);
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
log.info("全员推送消息结果: {}", response.getBody());
|
||||
|
||||
// 解析响应判断是否成功
|
||||
JSONObject responseJson = JSONObject.parseObject(response.getBody());
|
||||
return "OK".equals(responseJson.getString("ActionStatus"));
|
||||
} catch (Exception e) {
|
||||
log.error("全员推送消息异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushByAttributes(String title, String desc, Map<String, Object> attributes, boolean offlinePush, String ext) {
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
String random = String.valueOf(new Random().nextInt(999999999));
|
||||
|
||||
// 构建API请求URL - 使用属性推送接口
|
||||
String url = String.format(
|
||||
"https://console.tim.qq.com/v4/all_member_push/im_push_attr" +
|
||||
"?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json",
|
||||
sdkAppId,
|
||||
adminAccount,
|
||||
userSig,
|
||||
random);
|
||||
|
||||
// 构建消息内容
|
||||
Map<String, Object> msgContent = new HashMap<>();
|
||||
msgContent.put("Title", title);
|
||||
msgContent.put("Desc", desc);
|
||||
|
||||
// 构建消息体
|
||||
Map<String, Object> msgBody = new HashMap<>();
|
||||
msgBody.put("MsgType", "TIMCustomElem");
|
||||
msgBody.put("MsgContent", msgContent);
|
||||
|
||||
// 如果有扩展字段,添加到消息内容中
|
||||
if (ext != null && !ext.isEmpty()) {
|
||||
msgContent.put("Ext", ext);
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("From_Account", adminAccount);
|
||||
requestBody.put("MsgRandom", Integer.parseInt(random));
|
||||
requestBody.put("MsgBody", new Object[]{msgBody});
|
||||
requestBody.put("AttrNames", attributes.keySet().toArray(new String[0]));
|
||||
requestBody.put("AttrValues", attributes.values().toArray());
|
||||
|
||||
// 设置离线推送信息
|
||||
if (offlinePush) {
|
||||
Map<String, Object> offlinePushInfo = new HashMap<>();
|
||||
offlinePushInfo.put("PushFlag", 1); // 1表示推送,0表示不离线推送
|
||||
offlinePushInfo.put("Title", title);
|
||||
offlinePushInfo.put("Desc", desc);
|
||||
offlinePushInfo.put("Ext", ext);
|
||||
requestBody.put("OfflinePushInfo", offlinePushInfo);
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
log.info("属性推送消息结果: {}", response.getBody());
|
||||
|
||||
// 解析响应判断是否成功
|
||||
JSONObject responseJson = JSONObject.parseObject(response.getBody());
|
||||
return "OK".equals(responseJson.getString("ActionStatus"));
|
||||
} catch (Exception e) {
|
||||
log.error("属性推送消息异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushByTags(String title, String desc, List<String> tags, boolean offlinePush, String ext) {
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
String random = String.valueOf(new Random().nextInt(999999999));
|
||||
|
||||
// 构建API请求URL - 使用标签推送接口
|
||||
String url = String.format(
|
||||
"https://console.tim.qq.com/v4/all_member_push/im_push_tags" +
|
||||
"?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json",
|
||||
sdkAppId,
|
||||
adminAccount,
|
||||
userSig,
|
||||
random);
|
||||
|
||||
// 构建消息内容
|
||||
Map<String, Object> msgContent = new HashMap<>();
|
||||
msgContent.put("Title", title);
|
||||
msgContent.put("Desc", desc);
|
||||
|
||||
// 构建消息体
|
||||
Map<String, Object> msgBody = new HashMap<>();
|
||||
msgBody.put("MsgType", "TIMCustomElem");
|
||||
msgBody.put("MsgContent", msgContent);
|
||||
|
||||
// 如果有扩展字段,添加到消息内容中
|
||||
if (ext != null && !ext.isEmpty()) {
|
||||
msgContent.put("Ext", ext);
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("From_Account", adminAccount);
|
||||
requestBody.put("MsgRandom", Integer.parseInt(random));
|
||||
requestBody.put("MsgBody", new Object[]{msgBody});
|
||||
requestBody.put("TagList", tags);
|
||||
|
||||
// 设置离线推送信息
|
||||
if (offlinePush) {
|
||||
Map<String, Object> offlinePushInfo = new HashMap<>();
|
||||
offlinePushInfo.put("PushFlag", 1); // 1表示推送,0表示不离线推送
|
||||
offlinePushInfo.put("Title", title);
|
||||
offlinePushInfo.put("Desc", desc);
|
||||
offlinePushInfo.put("Ext", ext);
|
||||
requestBody.put("OfflinePushInfo", offlinePushInfo);
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
log.info("标签推送消息结果: {}", response.getBody());
|
||||
|
||||
// 解析响应判断是否成功
|
||||
JSONObject responseJson = JSONObject.parseObject(response.getBody());
|
||||
return "OK".equals(responseJson.getString("ActionStatus"));
|
||||
} catch (Exception e) {
|
||||
log.error("标签推送消息异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushToUsers(String title, String desc, List<String> userIds, boolean offlinePush, String ext) {
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
String random = String.valueOf(new Random().nextInt(999999999));
|
||||
|
||||
// 构建API请求URL - 使用批量单发接口
|
||||
String url = String.format(
|
||||
"https://console.tim.qq.com/v4/openim/batchsendmsg" +
|
||||
"?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json",
|
||||
sdkAppId,
|
||||
adminAccount,
|
||||
userSig,
|
||||
random);
|
||||
|
||||
// 构建消息内容
|
||||
Map<String, Object> msgContent = new HashMap<>();
|
||||
msgContent.put("Title", title);
|
||||
msgContent.put("Desc", desc);
|
||||
|
||||
// 构建消息体
|
||||
Map<String, Object> msgBody = new HashMap<>();
|
||||
msgBody.put("MsgType", "TIMCustomElem");
|
||||
msgBody.put("MsgContent", msgContent);
|
||||
|
||||
// 如果有扩展字段,添加到消息内容中
|
||||
if (ext != null && !ext.isEmpty()) {
|
||||
msgContent.put("Ext", ext);
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("From_Account", adminAccount);
|
||||
requestBody.put("To_Account", userIds);
|
||||
requestBody.put("MsgRandom", Integer.parseInt(random));
|
||||
requestBody.put("MsgBody", new Object[]{msgBody});
|
||||
|
||||
// 设置离线推送信息
|
||||
if (offlinePush) {
|
||||
Map<String, Object> offlinePushInfo = new HashMap<>();
|
||||
offlinePushInfo.put("PushFlag", 1); // 1表示推送,0表示不离线推送
|
||||
offlinePushInfo.put("Title", title);
|
||||
offlinePushInfo.put("Desc", desc);
|
||||
offlinePushInfo.put("Ext", ext);
|
||||
requestBody.put("OfflinePushInfo", offlinePushInfo);
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
log.info("指定用户推送消息结果: {}", response.getBody());
|
||||
|
||||
// 解析响应判断是否成功
|
||||
JSONObject responseJson = JSONObject.parseObject(response.getBody());
|
||||
return "OK".equals(responseJson.getString("ActionStatus"));
|
||||
} catch (Exception e) {
|
||||
log.error("指定用户推送消息异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processMessageVariables(String content, Map<String, String> variables) {
|
||||
if (content == null || variables == null || variables.isEmpty()) {
|
||||
return content;
|
||||
}
|
||||
|
||||
Matcher matcher = VARIABLE_PATTERN.matcher(content);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String variableName = matcher.group(1);
|
||||
String replacement = variables.getOrDefault(variableName, "$" + variableName);
|
||||
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
|
||||
matcher.appendTail(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user