[fix]修改消息的逻辑,将消息通过mq放出来,然后再由消费者发送给腾讯
This commit is contained in:
parent
8859977761
commit
03a9967da4
5
pom.xml
5
pom.xml
@ -409,11 +409,6 @@
|
||||
<artifactId>ruoyi-common-mq</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
<version>2.5.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
|
@ -0,0 +1,45 @@
|
||||
package org.dromara.app;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.wzj.soopin.member.convert.FeedbackConvert;
|
||||
import com.wzj.soopin.member.domain.bo.FeedbackBO;
|
||||
import com.wzj.soopin.member.service.IFeedbackService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.system.domain.vo.SysDictDataVo;
|
||||
import org.dromara.system.service.ISysDictDataService;
|
||||
import org.dromara.system.service.ISysDictTypeService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "VlogController 短视频相关业务功能的接口")
|
||||
@RequestMapping("/app/sys/dict")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class AppDictController {
|
||||
private final ISysDictDataService dictDataService;
|
||||
private final ISysDictTypeService dictTypeService;
|
||||
|
||||
/**
|
||||
* 根据字典类型查询字典数据信息
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
*/
|
||||
@GetMapping(value = "/type/{dictType}")
|
||||
public R<List<SysDictDataVo>> dictType(@PathVariable String dictType) {
|
||||
List<SysDictDataVo> data = dictTypeService.selectDictDataByType(dictType);
|
||||
if (ObjectUtil.isNull(data)) {
|
||||
data = new ArrayList<>();
|
||||
}
|
||||
return R.ok(data);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package org.dromara.app;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.wzj.soopin.content.domain.bo.*;
|
||||
import com.wzj.soopin.content.domain.po.Vlog;
|
||||
import com.wzj.soopin.content.domain.vo.IndexVlogVO;
|
||||
import com.wzj.soopin.content.service.VlogService;
|
||||
import com.wzj.soopin.content.service.VlogUploadService;
|
||||
import com.wzj.soopin.content.utils.QcCloud;
|
||||
import com.wzj.soopin.content.utils.RedisOperator;
|
||||
import com.wzj.soopin.member.convert.FeedbackConvert;
|
||||
import com.wzj.soopin.member.domain.bo.FeedbackBO;
|
||||
import com.wzj.soopin.member.service.IFeedbackService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.wzj.soopin.content.domain.base.BaseInfoProperties.*;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "VlogController 短视频相关业务功能的接口")
|
||||
@RequestMapping("/app/feedback")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class AppFeedbackController {
|
||||
private final IFeedbackService service;
|
||||
private final FeedbackConvert convert;
|
||||
|
||||
@Tag(name = "修改意见反馈备注信息")
|
||||
@Log(title = "新增意见反馈", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/add")
|
||||
public R add(@RequestBody FeedbackBO feedback) {
|
||||
return R.ok(service.save(convert.toPo(feedback)));
|
||||
}
|
||||
}
|
@ -18,11 +18,16 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.mq.domain.MQMessage;
|
||||
import org.dromara.common.mq.enums.MQMessageType;
|
||||
import org.dromara.common.mq.enums.MessageActionEnum;
|
||||
import org.dromara.common.mq.utils.MqUtil;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -150,15 +155,14 @@ public class AppVlogController {
|
||||
|
||||
|
||||
@PostMapping("like")
|
||||
public R<Void> like(@RequestBody Map<String, String> params) {
|
||||
public R<Void> like(@RequestBody VlogBO vlogBO) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new ServiceException("用户未登录");
|
||||
}
|
||||
|
||||
String userId = String.valueOf(loginUser.getUserId());
|
||||
String vlogId = params.get("vlogId");
|
||||
|
||||
String vlogId = vlogBO.getId();
|
||||
|
||||
|
||||
//获取vlog
|
||||
@ -188,6 +192,22 @@ public class AppVlogController {
|
||||
vlogService.flushCounts(vlogId, counts);
|
||||
}
|
||||
}
|
||||
if (userId != null && vlog.getMemberId() != null && !userId.equals(vlog.getMemberId())) {
|
||||
// 新版:使用模板类型编号和参数
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("userId", userId);
|
||||
params.put("nickname", loginUser.getNickname());
|
||||
params.put("action", MessageActionEnum.INTERACTION_LIKE.name());
|
||||
params.put("toUserId",vlog.getMemberId());
|
||||
MQMessage message = MQMessage.builder()
|
||||
.messageType(MQMessageType.IM.name())
|
||||
.data(params)
|
||||
.source("member")
|
||||
.build();
|
||||
// 关注消息
|
||||
MqUtil.sendIMMessage(message);
|
||||
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
@ -57,4 +57,6 @@ public interface CacheConstants {
|
||||
String CHAT="CHAT:";
|
||||
|
||||
|
||||
String IM_MSG_RECEIVE="im:msg:receive:";
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||
<version>2.2.3</version>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Messaging for RocketMQ -->
|
||||
|
@ -11,6 +11,7 @@ import java.time.LocalDateTime;
|
||||
@Data
|
||||
@Builder(toBuilder = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class MQMessage {
|
||||
/**
|
||||
* 消息ID
|
||||
|
@ -0,0 +1,7 @@
|
||||
package org.dromara.common.mq.enums;
|
||||
|
||||
public enum MQMessageType {
|
||||
IM,
|
||||
VLOG,
|
||||
COMMENT;
|
||||
}
|
@ -2,39 +2,37 @@ package org.dromara.common.mq.enums;
|
||||
|
||||
public enum MessageActionEnum {
|
||||
|
||||
NEW_FOUCS(1,"新的关注"), //新的关注
|
||||
SYSTEM_NOTIFY(2, "系统->通知"), // 系统->通知
|
||||
SYSTEM_REPORT(3, "系统->举报下架(视频,视频评论) 视频评论"), // 系统->举报下架(视频,视频评论) 视频评论
|
||||
SYSTEM_CHECK(4, "系统->审核结果(复审,驳回 ,通过)"), // 系统->审核结果(复审,驳回 ,通过)
|
||||
SYSTEM_PUSH(5, "系统->推广类的"), //系统->推广类的
|
||||
INTERACTION_COMMENT(6, "互动->评论"), //互动->评论
|
||||
INTERACTION_AT(7, "互动->视频评论中的@"), //互动->视频评论中的@
|
||||
INTERACTION_LIKE(8, "互动->点赞"), //互动->点赞
|
||||
INTERACTION_REPLY(9, "互动->评论回复"), //互动->评论回复
|
||||
ORDER_RECHARGE(10, "订单->充值 online"), //订单->充值 online
|
||||
ORDER_PAY(11, "订单->订单交易成功通知 online"), //订单->订单交易成功通知 online
|
||||
ORDER_REFUND(12, "订单->退款结果通知"), //订单->退款结果通知
|
||||
GROUP_NOTIFY_CHECK(13, "群通知->进群申请 online"), //群通知->进群申请 online
|
||||
GROUP_NOTIFY_ACCEPT(14, "群通知->进群审核审核通过 online"), // 群通知->进群审核审核通过 online
|
||||
GROUP_NOTIFY_FAIL(15, "群通知->进群审核审核拒绝 online"), // 群通知->进群审核审核拒绝 online
|
||||
GROUP_NOTIFY_LEAVE_UP(16, "群通知->群升级为达人群通知"), // 群通知->群升级为达人群通知
|
||||
GROUP_NOTIFY_LEAVE_DOWN(17, "群通知->群降级为普通群通知"); // 群通知->群降级为普通群通知
|
||||
NEW_FOUCS(1,"newFocus"), //新的关注
|
||||
SYSTEM_NOTIFY(2, "system"), // 系统->通知
|
||||
SYSTEM_REPORT(3, "system"), // 系统->举报下架(视频,视频评论) 视频评论
|
||||
SYSTEM_CHECK(4, "system"), // 系统->审核结果(复审,驳回 ,通过)
|
||||
SYSTEM_PUSH(5, "system"), //系统->推广类的
|
||||
INTERACTION_COMMENT(6, "interaction"), //互动->评论
|
||||
INTERACTION_AT(7, "interaction"), //互动->视频评论中的@
|
||||
INTERACTION_LIKE(8, "interaction"), //互动->点赞
|
||||
INTERACTION_REPLY(9, "interaction"), //互动->评论回复
|
||||
ORDER_RECHARGE(10, "order"), //订单->充值 online
|
||||
ORDER_PAY(11, "order"), //订单->订单交易成功通知 online
|
||||
ORDER_REFUND(12, "order"), //订单->退款结果通知
|
||||
GROUP_NOTIFY_CHECK(13, "groupNotify"), //群通知->进群申请 online
|
||||
GROUP_NOTIFY_ACCEPT(14, "groupNotify"), // 群通知->进群审核审核通过 online
|
||||
GROUP_NOTIFY_FAIL(15, "groupNotify"), // 群通知->进群审核审核拒绝 online
|
||||
GROUP_NOTIFY_LEAVE_UP(16, "groupNotify"), // 群通知->群升级为达人群通知
|
||||
GROUP_NOTIFY_LEAVE_DOWN(17, "groupNotify"); // 群通知->群降级为普通群通知
|
||||
|
||||
private int code;
|
||||
private String account;
|
||||
private String desc;
|
||||
|
||||
MessageActionEnum(int code, String desc) {
|
||||
MessageActionEnum(int code, String account) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
public String getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public static MessageActionEnum getByCode(int code) {
|
||||
|
@ -1,16 +1,14 @@
|
||||
package com.wzj.soopin.im.consumer;
|
||||
|
||||
import com.wzj.soopin.im.service.IMQMessageHandleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||
import org.apache.rocketmq.spring.core.RocketMQListener;
|
||||
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
//import org.dromara.system.config.RocketMQConfig;
|
||||
import com.wzj.soopin.im.domain.vo.SysMessageVo;
|
||||
import org.dromara.common.mq.config.RocketMQConfig;
|
||||
import org.dromara.system.websocket.MessageWebSocketServer;
|
||||
import org.dromara.common.mq.domain.MQMessage;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@ -24,66 +22,45 @@ import org.springframework.stereotype.Component;
|
||||
@RocketMQMessageListener(
|
||||
topic = RocketMQConfig.TOPIC_IM_MSG,
|
||||
consumerGroup = RocketMQConfig.CONSUMER_GROUP_SYS_MSG,
|
||||
selectorExpression = RocketMQConfig.TAG_SYS_MSG
|
||||
selectorExpression = "*"
|
||||
// ackMode = AckMode.MANUAL
|
||||
)
|
||||
public class MessageRocketMQConsumer implements RocketMQListener<String> {
|
||||
public class MessageRocketMQConsumer implements RocketMQListener<MessageExt> {
|
||||
|
||||
private final MessageWebSocketServer messageWebSocketServer;
|
||||
private final IMQMessageHandleService messageHandleService;
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
try {
|
||||
log.info("接收到RocketMQ消息: {}", message);
|
||||
// 解析消息格式,格式为: {"userId": "123", "message": {...}}
|
||||
MessageWrapper wrapper = JsonUtils.parseObject(message, MessageWrapper.class);
|
||||
if (wrapper != null && wrapper.getUserId() != null && wrapper.getMessage() != null) {
|
||||
// 将String类型的userId转换为Long类型
|
||||
Long userIdLong = null;
|
||||
try {
|
||||
if (StringUtils.isNotBlank(wrapper.getUserId())) {
|
||||
userIdLong = Long.parseLong(wrapper.getUserId());
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("用户ID转换失败: {}", wrapper.getUserId(), e);
|
||||
return;
|
||||
}
|
||||
public void onMessage(MessageExt messageExt) { // 参数为MessageExt
|
||||
String message = new String(messageExt.getBody());
|
||||
log.info("接收到RocketMQ消息: {}, msgId: {}", message, messageExt.getMsgId());
|
||||
|
||||
if (userIdLong != null) {
|
||||
// 发送WebSocket消息
|
||||
messageWebSocketServer.sendMessage(userIdLong, JsonUtils.toJsonString(wrapper.getMessage()));
|
||||
log.info("通过WebSocket发送消息成功,userId: {}", userIdLong);
|
||||
try {
|
||||
MQMessage mqMessage = JsonUtils.parseObject(message, MQMessage.class);
|
||||
if (mqMessage != null) {
|
||||
boolean result = messageHandleService.handleMessage(mqMessage);
|
||||
if (result) {
|
||||
// 消息处理成功,手动确认
|
||||
// messageExt.acknowledge(); // 2.3.1版本支持该方法
|
||||
log.info("消息确认成功, msgId: {}", messageExt.getMsgId());
|
||||
} else {
|
||||
log.warn("用户ID为空或无效: {}", wrapper.getUserId());
|
||||
// 业务失败,抛出异常触发重试
|
||||
throw new RuntimeException("业务处理失败,将重试");
|
||||
}
|
||||
} else {
|
||||
log.warn("消息格式不正确: {}", message);
|
||||
log.warn("消息格式错误,直接确认避免重试: {}", message);
|
||||
// messageExt.acknowledge(); // 格式错误无需重试
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理RocketMQ消息失败", e);
|
||||
log.error("消息处理异常, msgId: {}", messageExt.getMsgId(), e);
|
||||
// 达到最大重试次数后确认消息
|
||||
if (messageExt.getReconsumeTimes() >= 3) {
|
||||
log.error("达到最大重试次数,确认消息进入死信队列, msgId: {}", messageExt.getMsgId());
|
||||
// messageExt.acknowledge();
|
||||
} else {
|
||||
// 未达重试上限,抛出异常触发重试
|
||||
throw new RuntimeException("消息处理异常,将重试", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息包装类
|
||||
*/
|
||||
public static class MessageWrapper {
|
||||
private String userId;
|
||||
private SysMessageVo message;
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public SysMessageVo getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(SysMessageVo message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,4 +64,9 @@ public class SysMessageTemplate extends BaseAudit {
|
||||
* 备注
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 模板参数
|
||||
*/
|
||||
private String params;
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ public class MessageEventListener {
|
||||
String userId = event.getUserIdStr();
|
||||
|
||||
// 创建消息包装对象
|
||||
MessageRocketMQConsumer.MessageWrapper wrapper = new MessageRocketMQConsumer.MessageWrapper();
|
||||
wrapper.setUserId(userId);
|
||||
wrapper.setMessage(event.getMessage());
|
||||
// MessageRocketMQConsumer.MessageWrapper wrapper = new MessageRocketMQConsumer.MessageWrapper();
|
||||
// wrapper.setUserId(userId);
|
||||
// wrapper.setMessage(event.getMessage());
|
||||
|
||||
// 首先尝试发送消息到腾讯IM
|
||||
boolean tencentIMSendSuccess = false;
|
||||
@ -129,35 +129,18 @@ public class MessageEventListener {
|
||||
}
|
||||
} else {
|
||||
// 默认为单用户推送
|
||||
TencentIMServiceImpl.TencentIMResult imResult = tencentIMService.sendMessageToTencentIM(fromUserId, toUserId, content,ext);
|
||||
tencentIMSendSuccess = imResult.isSuccess();
|
||||
if (tencentIMSendSuccess) {
|
||||
log.info("腾讯IM推送成功,userId={}, response={}", toUserId, imResult.getRawResponse());
|
||||
} else {
|
||||
log.warn("腾讯IM推送失败,userId={}, errorCode={}, errorInfo={}, response={}",
|
||||
toUserId, imResult.getErrorCode(), imResult.getErrorInfo(), imResult.getRawResponse());
|
||||
}
|
||||
tencentIMSendSuccess = tencentIMService.sendMessageToTencentIM(fromUserId, toUserId, content,ext);
|
||||
|
||||
}
|
||||
if (tencentIMSendSuccess) {
|
||||
log.info("腾讯IM推送成功,userId={}", toUserId);
|
||||
} else {
|
||||
log.warn("腾讯IM推送失败,userId={}", toUserId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息到腾讯IM异常,将尝试通过其他方式发送", e);
|
||||
}
|
||||
|
||||
// 无论腾讯IM是否发送成功,都继续尝试通过RocketMQ发送消息
|
||||
// 这样可以确保消息至少通过一种方式发送出去
|
||||
/*
|
||||
boolean rocketMQSendSuccess = false;
|
||||
try {
|
||||
rocketMQService.sendMessage(
|
||||
RocketMQConfig.TOPIC_SYS_MSG,
|
||||
RocketMQConfig.TAG_SYS_MSG,
|
||||
wrapper
|
||||
);
|
||||
rocketMQSendSuccess = true;
|
||||
log.info("消息事件已通过RocketMQ发送,userId: {}", userId);
|
||||
} catch (Exception e) {
|
||||
log.error("通过RocketMQ发送消息失败,将尝试直接通过WebSocket发送", e);
|
||||
}
|
||||
*/
|
||||
boolean rocketMQSendSuccess = false; // 直接设为false,表示不走MQ
|
||||
|
||||
// 如果前两种方式都失败,则尝试直接通过WebSocket发送
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.wzj.soopin.im.service;
|
||||
|
||||
import org.dromara.common.mq.domain.MQMessage;
|
||||
|
||||
public interface IMQMessageHandleService {
|
||||
boolean handleMessage(MQMessage message);
|
||||
}
|
@ -66,4 +66,12 @@ public interface ISysMessageTemplateService extends IService<SysMessageTemplate>
|
||||
*/
|
||||
List<SysMessageTemplateVo> selectTemplateListByName(String name);
|
||||
SysMessageTemplate selectByTemplateType(String templateType) ;
|
||||
|
||||
/**
|
||||
* 根据action获取消息模板
|
||||
* @param action
|
||||
* @return
|
||||
*/
|
||||
SysMessageTemplate getTemplateByAction(Integer action) ;
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public interface ITencentIMService {
|
||||
* @param content 消息内容
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
TencentIMServiceImpl.TencentIMResult sendMessageToTencentIM(String fromUserId, String toUserId, String content, String cloudCustomData);
|
||||
boolean sendMessageToTencentIM(String fromUserId, String toUserId, String content, String cloudCustomData);
|
||||
|
||||
/**
|
||||
* 推送消息给全体用户
|
||||
|
@ -0,0 +1,165 @@
|
||||
package com.wzj.soopin.im.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.wzj.soopin.im.consumer.MessageRocketMQConsumer;
|
||||
import com.wzj.soopin.im.domain.SysMessageTemplate;
|
||||
import com.wzj.soopin.im.service.IMQMessageHandleService;
|
||||
import com.wzj.soopin.im.service.ISysMessageTemplateService;
|
||||
import com.wzj.soopin.im.service.ITencentIMService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.CacheConstants;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.mq.domain.MQMessage;
|
||||
import org.dromara.common.mq.enums.MessageActionEnum;
|
||||
import org.dromara.common.redis.redis.RedisCache;
|
||||
import org.dromara.system.websocket.MessageWebSocketServer;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MQMessageHandleServiceImpl implements IMQMessageHandleService {
|
||||
// 变量替换的正则表达式模式
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{\\w+}");
|
||||
private final RedisCache redisCache;
|
||||
private final ISysMessageTemplateService templateService;
|
||||
private final ITencentIMService tencentIMService;
|
||||
private final MessageWebSocketServer messageWebSocketServer;
|
||||
|
||||
public boolean handleMessage(MQMessage message) {
|
||||
// 消息幂等性检查
|
||||
String messageId = message.getMessageId();
|
||||
// 检查消息是否已处理
|
||||
if (redisCache.getCacheObject(CacheConstants.IM_MSG_RECEIVE + messageId) != null) {
|
||||
log.info("消息已处理,忽略重复消息:{}", messageId);
|
||||
return true;
|
||||
}
|
||||
//创建im消息
|
||||
//根据消息类型获取消息模板
|
||||
Map<String, Object> params = (Map<String, Object>) message.getData();
|
||||
String action = (String) params.get("action");
|
||||
//根据action获取消息模板
|
||||
MessageActionEnum actionEnum = MessageActionEnum.valueOf(action);
|
||||
|
||||
SysMessageTemplate template = templateService.getTemplateByAction(actionEnum.getCode());
|
||||
|
||||
if (template == null) {
|
||||
log.error("根据action获取消息模板失败,action:{}", action);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 首先尝试发送消息到腾讯IM
|
||||
boolean tencentIMSendSuccess = false;
|
||||
try {
|
||||
// 消息发送者可能是系统或管理员,这里使用action对应的账号发送
|
||||
String fromUserId = actionEnum.getAccount();
|
||||
String toUserId = ""; // 接收者是事件中的用户ID
|
||||
String content = template.getContent(); // 只取content字段
|
||||
|
||||
// 处理消息变量替换(如果有)
|
||||
// 从消息中提取标题和描述
|
||||
String title = template.getTitle();
|
||||
String desc = template.getContent();
|
||||
// 扩展字段,可以在APP端获取
|
||||
String ext = template.getExt();
|
||||
// 处理变量替换
|
||||
if (params.size() > 0) {
|
||||
title = processMessageVariables(title, params);
|
||||
desc = processMessageVariables(desc, params);
|
||||
content = processMessageVariables(content, params);
|
||||
ext = processMessageVariables(ext, params);
|
||||
toUserId = String.valueOf(params.get("toUserId"));
|
||||
}
|
||||
|
||||
String pushType = null;
|
||||
// 根据消息类型决定推送方式
|
||||
String templateParams = template.getParams();
|
||||
if (templateParams != null) {
|
||||
JSONObject param = new JSONObject(templateParams);
|
||||
// 从消息参数中提取接收者
|
||||
if (param != null && param.containsKey("pushType")) {
|
||||
pushType = String.valueOf(param.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, ext);
|
||||
}
|
||||
if (tencentIMSendSuccess) {
|
||||
log.info("腾讯IM推送成功,userId={}", toUserId);
|
||||
} else {
|
||||
log.error("腾讯IM推送失败,userId={}", toUserId);
|
||||
}
|
||||
// 如果前两种方式都失败,则尝试直接通过WebSocket发送
|
||||
if (!tencentIMSendSuccess) {
|
||||
try {
|
||||
messageWebSocketServer.sendMessage(
|
||||
Long.parseLong(toUserId),
|
||||
JsonUtils.toJsonString(message)
|
||||
);
|
||||
log.info("消息事件已直接通过WebSocket发送,userId: {}", toUserId);
|
||||
} catch (Exception e) {
|
||||
log.error("通过WebSocket发送消息失败", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息到腾讯IM异常,将尝试通过其他方式发送", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String processMessageVariables(String content, Map<String, Object> variables) {
|
||||
if (content == null || variables == null || variables.isEmpty()) {
|
||||
return content;
|
||||
}
|
||||
for (String key : variables.keySet()) {
|
||||
content = content.replaceAll("\\$\\{" + key + "\\}", variables.get(key).toString());
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}
|
@ -78,4 +78,10 @@ public class SysMessageTemplateServiceImpl extends ServiceImpl<SysMessageTemplat
|
||||
lqw.eq(SysMessageTemplate::getType, templateType).eq(SysMessageTemplate::getStatus, 0);
|
||||
return templateMapper.selectOne(lqw);
|
||||
}
|
||||
@Override
|
||||
public SysMessageTemplate getTemplateByAction(Integer action) {
|
||||
LambdaQueryWrapper<SysMessageTemplate> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(SysMessageTemplate::getAction, action).eq(SysMessageTemplate::getStatus, 0);
|
||||
return templateMapper.selectOne(lqw);
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ public class TencentIMServiceImpl implements ITencentIMService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TencentIMResult sendMessageToTencentIM(String fromUserId, String toUserId, String msgBody,String cloudCustomData) {
|
||||
public boolean sendMessageToTencentIM(String fromUserId, String toUserId, String msgBody,String cloudCustomData) {
|
||||
TencentIMResult result = new TencentIMResult();
|
||||
try {
|
||||
String userSig = generateAdminUserSig();
|
||||
@ -206,10 +206,9 @@ public class TencentIMServiceImpl implements ITencentIMService {
|
||||
result.setSuccess("OK".equals(result.getActionStatus()) && result.getErrorCode() == 0);
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息到腾讯IM异常", e);
|
||||
result.setSuccess(false);
|
||||
result.setErrorInfo(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,6 +22,9 @@ import org.dromara.common.core.constant.CacheConstants;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mq.domain.MQMessage;
|
||||
import org.dromara.common.mq.enums.MessageActionEnum;
|
||||
import org.dromara.common.mq.utils.MqUtil;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -88,201 +91,7 @@ public class FansController {
|
||||
return R.ok(service.removeById(id));
|
||||
}
|
||||
|
||||
// 提取IM关注处理逻辑为私有方法
|
||||
private void handleImFollow(Long myId, List<Long> vloggerIds) {
|
||||
if (myId != null && vloggerIds != null) {
|
||||
for (Long vloggerId : vloggerIds) {
|
||||
service.doFollow(myId, vloggerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("follow")
|
||||
public Object follow(@RequestBody(required = false) Map<String, Object> callbackData,
|
||||
@RequestParam(required = false) Long myId,
|
||||
@RequestParam(required = false) Long vloggerId) {
|
||||
// 1. IM回调格式
|
||||
if (callbackData != null && callbackData.containsKey("CallbackCommand")) {
|
||||
log.info("收到IM关注回调: {}", callbackData);
|
||||
|
||||
// 1.1 兼容腾讯IM的 PairList 回调格式
|
||||
Object pairListObj = callbackData.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
Long fromId = null;
|
||||
Long toId = null;
|
||||
try {
|
||||
fromId = Long.valueOf(fromAccountObj.toString());
|
||||
toId = Long.valueOf(toAccountObj.toString());
|
||||
} catch (Exception e) {
|
||||
log.warn("IM回调Pair参数转换失败: {}", pair);
|
||||
continue;
|
||||
}
|
||||
service.doFollow(fromId, toId);
|
||||
log.info("IM回调处理成功: fromId={}, vloggerId={}", fromId, toId);
|
||||
} else {
|
||||
log.warn("IM回调Pair参数不完整: {}", pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回IM平台要求的应答
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("ActionStatus", "OK");
|
||||
resp.put("ErrorCode", 0);
|
||||
resp.put("ErrorInfo", "");
|
||||
return resp;
|
||||
}
|
||||
|
||||
// 1.2 兼容原有 FollowList 格式
|
||||
String fromAccount = (String) callbackData.get("From_Account");
|
||||
List<Map<String, String>> followList = (List<Map<String, String>>) callbackData.get("FollowList");
|
||||
if (fromAccount != null && followList != null) {
|
||||
Long fromId = Long.valueOf(fromAccount.replace("UserID_", ""));
|
||||
List<Long> vloggerIds = new java.util.ArrayList<>();
|
||||
for (Map<String, String> follow : followList) {
|
||||
String toAccount = follow.get("To_Account");
|
||||
vloggerIds.add(Long.valueOf(toAccount.replace("UserID_", "")));
|
||||
}
|
||||
handleImFollow(fromId, vloggerIds);
|
||||
log.info("IM回调处理成功: fromId={}, vloggerIds={}", fromId, vloggerIds);
|
||||
} else {
|
||||
log.warn("IM回调参数不完整: {}", callbackData);
|
||||
}
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("ActionStatus", "OK");
|
||||
resp.put("ErrorCode", 0);
|
||||
resp.put("ErrorInfo", "");
|
||||
return resp;
|
||||
}
|
||||
// 2. 兼容JSON方式传递myId和vloggerId
|
||||
if (callbackData != null && callbackData.containsKey("myId") && callbackData.containsKey("vloggerId")) {
|
||||
log.info("收到JSON关注回调: {}", callbackData);
|
||||
Object myIdObj = callbackData.get("myId");
|
||||
Object vloggerIdObj = callbackData.get("vloggerId");
|
||||
if (myIdObj != null && vloggerIdObj != null) {
|
||||
Long myIdVal = Long.valueOf(myIdObj.toString());
|
||||
Long vloggerIdVal = Long.valueOf(vloggerIdObj.toString());
|
||||
handleImFollow(myIdVal, java.util.Arrays.asList(vloggerIdVal));
|
||||
log.info("JSON回调处理成功: myId={}, vloggerId={}", myIdVal, vloggerIdVal);
|
||||
return R.ok("关注成功");
|
||||
} else {
|
||||
log.warn("JSON回调参数不完整: {}", callbackData);
|
||||
}
|
||||
}
|
||||
// 3. 普通参数,自动组装成IM格式处理
|
||||
if (myId != null && vloggerId != null) {
|
||||
log.info("收到参数关注回调: myId={}, vloggerId={}", myId, vloggerId);
|
||||
handleImFollow(myId, java.util.Arrays.asList(vloggerId));
|
||||
log.info("参数回调处理成功: myId={}, vloggerId={}", myId, vloggerId);
|
||||
return R.ok("关注成功");
|
||||
}
|
||||
log.error("关注回调失败,参数为空: callbackData={}, myId={}, vloggerId={}", callbackData, myId, vloggerId);
|
||||
return R.fail("id不能为空");
|
||||
}
|
||||
|
||||
@PostMapping("cancel")
|
||||
public Object cancel(@RequestBody(required = false) Map<String, Object> callbackData,
|
||||
@RequestParam(required = false) Long myId,
|
||||
@RequestParam(required = false) Long vloggerId) {
|
||||
// 1. 判断是否为IM回调格式
|
||||
if (callbackData != null && callbackData.containsKey("CallbackCommand")) {
|
||||
log.info("收到IM取消关注回调: {}", callbackData);
|
||||
|
||||
// 1.1 兼容腾讯IM的 PairList 回调格式
|
||||
Object pairListObj = callbackData.get("PairList");
|
||||
log.info("PairList 类型: {}, 内容: {}", pairListObj != null ? pairListObj.getClass() : "null", pairListObj);
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
Long fromId = null;
|
||||
Long toId = null;
|
||||
try {
|
||||
fromId = Long.valueOf(fromAccountObj.toString());
|
||||
toId = Long.valueOf(toAccountObj.toString());
|
||||
} catch (Exception e) {
|
||||
log.warn("IM回调Pair参数转换失败: {}", pair);
|
||||
continue;
|
||||
}
|
||||
service.doCancel(fromId, toId);
|
||||
log.info("IM取消关注回调处理成功: fromId={}, toId={}", fromId, toId);
|
||||
} else {
|
||||
log.warn("IM取消关注回调Pair参数不完整: {}", pair);
|
||||
}
|
||||
} else {
|
||||
log.warn("PairList元素不是Map: {}", pairObj);
|
||||
}
|
||||
}
|
||||
// 返回IM平台要求的应答
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("ActionStatus", "OK");
|
||||
resp.put("ErrorCode", 0);
|
||||
resp.put("ErrorInfo", "");
|
||||
return resp;
|
||||
} else {
|
||||
log.warn("PairList 不是 List 类型: {}", pairListObj);
|
||||
}
|
||||
|
||||
// 1.2 兼容原有 To_Account 逻辑
|
||||
String fromAccount = (String) callbackData.get("From_Account");
|
||||
List<String> toAccounts = null;
|
||||
// 兼容腾讯IM的 To_Account 可能是 List<String> 或 List<Map<String, String>>
|
||||
Object toAccountObj = callbackData.get("To_Account");
|
||||
if (toAccountObj instanceof List) {
|
||||
List<?> list = (List<?>) toAccountObj;
|
||||
if (!list.isEmpty() && list.get(0) instanceof String) {
|
||||
toAccounts = (List<String>) list;
|
||||
} else if (!list.isEmpty() && list.get(0) instanceof Map) {
|
||||
// 兼容部分IM平台格式
|
||||
toAccounts = new java.util.ArrayList<>();
|
||||
for (Object obj : list) {
|
||||
Map<?, ?> map = (Map<?, ?>) obj;
|
||||
Object acc = map.get("To_Account");
|
||||
if (acc != null) toAccounts.add(acc.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fromAccount != null && toAccounts != null) {
|
||||
Long fromId = Long.valueOf(fromAccount.replace("UserID_", ""));
|
||||
for (String toAccount : toAccounts) {
|
||||
Long toId = Long.valueOf(toAccount.replace("UserID_", ""));
|
||||
service.doCancel(fromId, toId);
|
||||
}
|
||||
log.info("IM取消关注回调处理成功: fromId={}, toAccounts={}", fromId, toAccounts);
|
||||
} else {
|
||||
log.warn("IM取消关注回调参数不完整: {}", callbackData);
|
||||
}
|
||||
// 返回IM平台要求的应答
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("ActionStatus", "OK");
|
||||
resp.put("ErrorCode", 0);
|
||||
resp.put("ErrorInfo", "");
|
||||
return resp;
|
||||
}
|
||||
// 2. 原有取关逻辑
|
||||
if (myId == null || vloggerId == null) {
|
||||
return R.fail("id不能为空");
|
||||
}
|
||||
boolean flow = service.queryDoIFollowVloger(myId, vloggerId);
|
||||
if (!flow) {
|
||||
return R.fail("没有关注信息");
|
||||
}
|
||||
service.doCancel(myId, vloggerId);
|
||||
// 博主的粉丝-1,我的关注-1
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FANS + ":" + vloggerId);
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FOLLOW + ":" + myId);
|
||||
return R.ok("取消成功");
|
||||
}
|
||||
|
||||
@GetMapping("block/list")
|
||||
@MemberFillMethod
|
||||
|
@ -1,38 +1,25 @@
|
||||
package com.wzj.soopin.member.controller;
|
||||
|
||||
import com.wzj.soopin.member.domain.po.Member;
|
||||
import com.wzj.soopin.member.service.IMemberService;
|
||||
import com.wzj.soopin.member.service.IFansService;
|
||||
import com.wzj.soopin.member.domain.vo.IMCallbackVO;
|
||||
import com.wzj.soopin.member.service.IMCallbackService;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.dromara.common.core.constant.CacheConstants;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.wzj.soopin.member.service.IMemberBlockService;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/callback/api")
|
||||
public class ImCallbackController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ImCallbackController.class);
|
||||
// private static final String CALLBACK_TOKEN = "your_auth_token"; //
|
||||
@Autowired
|
||||
private IMemberService memberService;
|
||||
@Autowired
|
||||
private IFansService fansService;
|
||||
@Autowired
|
||||
private FansController fansController;
|
||||
@Autowired
|
||||
private IMemberBlockService memberBlockService;
|
||||
private IMCallbackService imCallbackService;
|
||||
private static final String CALLBACK_TOKEN = "your_auth_token"; //
|
||||
|
||||
@PostMapping
|
||||
public R handleCallback(
|
||||
@ -41,13 +28,13 @@ public class ImCallbackController {
|
||||
@RequestParam(value = "CallbackCommand", required = false) String callbackCommand,
|
||||
@RequestBody Map<String, Object> requestBody) {
|
||||
|
||||
// // 1. 签名验证
|
||||
// if (!verifySignature(CALLBACK_TOKEN, sign, requestTime)) {
|
||||
// return Map.of("ActionStatus", "FAIL", "ErrorCode", 1001);
|
||||
// }
|
||||
// 1. 签名验证
|
||||
if (sign != null && !verifySignature(CALLBACK_TOKEN, sign, requestTime)) {
|
||||
return R.fail(1001, "签名校验失败");
|
||||
}
|
||||
|
||||
// 2. 快速响应
|
||||
R response = R.ok();
|
||||
IMCallbackVO callbackVO = new IMCallbackVO();
|
||||
|
||||
// 3. 分发业务逻辑
|
||||
if (StringUtils.isBlank(callbackCommand)) {
|
||||
@ -56,148 +43,33 @@ public class ImCallbackController {
|
||||
switch (callbackCommand) {
|
||||
case "Sns.CallbackFriendAdd"://好友添加
|
||||
// 关注,直接调用 FansController 的 follow
|
||||
fansController.follow(requestBody, null, null);
|
||||
callbackVO = imCallbackService.handleImFollow(requestBody);
|
||||
break;
|
||||
case "Sns.CallbackFriendDelete"://好友删除
|
||||
// 取关,直接调用 FansController 的 cancel
|
||||
fansController.cancel(requestBody, null, null);
|
||||
imCallbackService.handleImCancelFollow(requestBody);
|
||||
break;
|
||||
case "Profile.CallbackPortraitSet":
|
||||
// 资料修改
|
||||
handleProfileCallbackPortraitSet(requestBody);
|
||||
imCallbackService.handleProfileCallbackPortraitSet(requestBody);
|
||||
break;
|
||||
case "Sns.CallbackBlackListAdd":
|
||||
handleImAddBlock(requestBody);
|
||||
imCallbackService.handleImAddBlock(requestBody);
|
||||
break;
|
||||
case "Sns.CallbackBlackListDelete":
|
||||
handleImCancelBlock(requestBody);
|
||||
imCallbackService.handleImCancelBlock(requestBody);
|
||||
break;
|
||||
case "Follow.CallbackAfterFollowAdd"://关注
|
||||
fansController.follow(requestBody, null, null);
|
||||
imCallbackService.handleImFollow(requestBody);
|
||||
break;
|
||||
case "Follow.CallbackAfterFollowDelete"://取关
|
||||
fansController.cancel(requestBody, null, null);
|
||||
imCallbackService.handleImCancelFollow(requestBody);
|
||||
break;
|
||||
default:
|
||||
log.info("收到未知事件类型: {}, 参数: {}", callbackCommand, requestBody);
|
||||
break;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// 处理IM关注事件
|
||||
private void handleImFollow(Map<String, Object> requestBody) {
|
||||
Object pairListObj = requestBody.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
Long fromId = Long.valueOf(fromAccountObj.toString());
|
||||
Long toId = Long.valueOf(toAccountObj.toString());
|
||||
fansService.doFollow(fromId, toId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理IM取关事件
|
||||
private void handleImCancel(Map<String, Object> requestBody) {
|
||||
Object pairListObj = requestBody.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
Long fromId = Long.valueOf(fromAccountObj.toString());
|
||||
Long toId = Long.valueOf(toAccountObj.toString());
|
||||
fansService.doCancel(fromId, toId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理IM黑名单添加事件
|
||||
private void handleImAddBlock(Map<String, Object> requestBody) {
|
||||
Object pairListObj = requestBody.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
try {
|
||||
Long myId = Long.valueOf(fromAccountObj.toString());
|
||||
Long vloggerId = Long.valueOf(toAccountObj.toString());
|
||||
// 1. 判断两个id不能为空
|
||||
if (myId == null || vloggerId == null) {
|
||||
log.warn("黑名单回调参数id为空: myId={}, vloggerId={}", myId, vloggerId);
|
||||
continue;
|
||||
}
|
||||
// 2. 是否已经存在关注关系
|
||||
boolean flow = fansService.queryDoIFollowVloger(myId, vloggerId);
|
||||
if (flow) {
|
||||
// 删除关注
|
||||
fansService.doCancel(myId, vloggerId);
|
||||
// 博主的粉丝-1,我的关注-1
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FANS + ":" + vloggerId);
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FOLLOW + ":" + myId);
|
||||
}
|
||||
// 3. 检查是否已在黑名单中
|
||||
boolean hasblock = memberBlockService.hasBlocked(myId, vloggerId);
|
||||
if (hasblock) {
|
||||
log.info("用户{}已拉黑{}", myId, vloggerId);
|
||||
continue;
|
||||
}
|
||||
// 4. 拉黑
|
||||
memberBlockService.addBlock(myId, vloggerId);
|
||||
log.info("用户{}成功拉黑{}", myId, vloggerId);
|
||||
} catch (Exception e) {
|
||||
log.warn("黑名单回调参数转换失败: {}", pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理IM取消拉黑事件
|
||||
private void handleImCancelBlock(Map<String, Object> requestBody) {
|
||||
Object pairListObj = requestBody.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
try {
|
||||
Long myId = Long.valueOf(fromAccountObj.toString());
|
||||
Long vloggerId = Long.valueOf(toAccountObj.toString());
|
||||
if (myId == null || vloggerId == null) {
|
||||
log.warn("取消拉黑回调参数id为空: myId={}, vloggerId={}", myId, vloggerId);
|
||||
continue;
|
||||
}
|
||||
memberBlockService.removeBlock(myId, vloggerId);
|
||||
log.info("用户{}已取消拉黑{}", myId, vloggerId);
|
||||
} catch (Exception e) {
|
||||
log.warn("取消拉黑回调参数转换失败: {}", pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return R.ok(callbackVO);
|
||||
}
|
||||
|
||||
// 签名校验方法
|
||||
@ -209,58 +81,5 @@ public class ImCallbackController {
|
||||
return localSign.equals(sign);
|
||||
}
|
||||
|
||||
// 资料修改回调处理逻辑
|
||||
private void handleProfileCallbackPortraitSet(Map<String, Object> callbackData) {
|
||||
log.info("收到IM用户资料更新回调: {}", callbackData);
|
||||
String fromAccount = (String) callbackData.get("From_Account");
|
||||
List<Map<String, Object>> profileItems = (List<Map<String, Object>>) callbackData.get("ProfileItem");
|
||||
if (fromAccount != null && profileItems != null) {
|
||||
Long userId = null;
|
||||
try {
|
||||
userId = Long.valueOf(fromAccount.replace("UserID_", ""));
|
||||
} catch (Exception e) {
|
||||
log.warn("用户ID转换失败: {}", fromAccount);
|
||||
}
|
||||
if (userId != null) {
|
||||
Member memberToUpdate = memberService.getById(userId);
|
||||
if (memberToUpdate != null) {
|
||||
for (Map<String, Object> item : profileItems) {
|
||||
String tag = (String) item.get("Tag");
|
||||
Object value = item.get("Value");
|
||||
updateMemberProfile(memberToUpdate, tag, value);
|
||||
}
|
||||
memberService.updateById(memberToUpdate);
|
||||
log.info("IM用户资料更新回调处理成功: userId={}, profileItems={}", userId, profileItems);
|
||||
} else {
|
||||
log.warn("IM用户资料更新回调中用户不存在: userId={}", userId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("IM用户资料更新回调参数不完整: {}", callbackData);
|
||||
}
|
||||
}
|
||||
|
||||
// 只更新 nickname/gender 字段
|
||||
private void updateMemberProfile(Member member, String tag, Object value) {
|
||||
if (tag == null || value == null) {
|
||||
return;
|
||||
}
|
||||
switch (tag) {
|
||||
case "Tag_Profile_IM_Nick":
|
||||
member.setNickname(value.toString());
|
||||
break;
|
||||
case "Tag_Profile_IM_Gender":
|
||||
if ("Gender_Type_Male".equals(value)) {
|
||||
member.setGender(1);
|
||||
} else if ("Gender_Type_Female".equals(value)) {
|
||||
member.setGender(2);
|
||||
} else {
|
||||
member.setGender(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.debug("未处理的用户资料字段: tag={}, value={}", tag, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package com.wzj.soopin.member.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class IMCallbackVO {
|
||||
|
||||
private String ActionStatus;
|
||||
private Integer ErrorCode;
|
||||
private String ErrorInfo;
|
||||
|
||||
|
||||
public IMCallbackVO() {
|
||||
this.ActionStatus = "OK";
|
||||
this.ErrorCode = 0;
|
||||
this.ErrorInfo = "";
|
||||
}
|
||||
public IMCallbackVO(String ActionStatus, Integer ErrorCode, String ErrorInfo) {
|
||||
this.ActionStatus = ActionStatus;
|
||||
this.ErrorCode = ErrorCode;
|
||||
this.ErrorInfo = ErrorInfo;
|
||||
}
|
||||
|
||||
public static IMCallbackVO success() {
|
||||
return new IMCallbackVO();
|
||||
}
|
||||
public static IMCallbackVO fail() {
|
||||
return new IMCallbackVO("FAIL", 400, "");
|
||||
}
|
||||
|
||||
}
|
@ -11,12 +11,12 @@ public interface IFansService extends IService<Fans> {
|
||||
/**
|
||||
* 关注
|
||||
*/
|
||||
public void doFollow(Long myId, Long vloggerId);
|
||||
public boolean doFollow(Long myId, Long vloggerId);
|
||||
|
||||
/**
|
||||
* 取关
|
||||
*/
|
||||
public void doCancel(Long myId, Long vloggerId);
|
||||
public boolean doCancel(Long myId, Long vloggerId);
|
||||
|
||||
/**
|
||||
* 查询用户是否关注博主
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.wzj.soopin.member.service;
|
||||
|
||||
import com.wzj.soopin.member.domain.vo.IMCallbackVO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface IMCallbackService {
|
||||
IMCallbackVO handleImFollow(Map<String, Object> params);
|
||||
|
||||
IMCallbackVO handleImCancelFollow(Map<String, Object> params);
|
||||
IMCallbackVO handleImAddBlock(Map<String, Object> params);
|
||||
|
||||
IMCallbackVO handleImCancelBlock(Map<String, Object> params);
|
||||
|
||||
IMCallbackVO handleProfileCallbackPortraitSet(Map<String, Object> params);
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ import org.dromara.common.mq.domain.MQMessage;
|
||||
import org.dromara.common.mq.enums.MessageActionEnum;
|
||||
import org.dromara.common.mq.utils.MqUtil;
|
||||
import org.dromara.common.redis.redis.RedisCache;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -39,16 +40,18 @@ public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IF
|
||||
@Autowired
|
||||
private IMemberService memberService;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void doFollow(Long myId, Long vloggerId) {
|
||||
public boolean doFollow(Long myId, Long vloggerId) {
|
||||
// 幂等性校验:已存在则不再插入
|
||||
|
||||
|
||||
|
||||
Fans existing = queryFansRelationship(myId, vloggerId);
|
||||
if (existing != null) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
Fans fans = new Fans();
|
||||
@ -73,9 +76,11 @@ public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IF
|
||||
if (follower != null && vlogger != null && !myId.equals(vloggerId)) {
|
||||
// 新版:使用模板类型编号和参数
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("followerNickname", follower.getNickname() == null ? "" : follower.getNickname());
|
||||
params.put("vloggerNickname", vlogger.getNickname() == null ? "" : vlogger.getNickname());
|
||||
params.put("userId", follower.getId());
|
||||
params.put("nickname", follower.getNickname() == null ? "" : follower.getNickname());
|
||||
params.put("faceUrl", vlogger.getAvatar() == null ? "" : vlogger.getAvatar());
|
||||
params.put("action", MessageActionEnum.NEW_FOUCS.name());
|
||||
params.put("toUserId",vloggerId);
|
||||
MQMessage message = MQMessage.builder()
|
||||
.messageType("follow")
|
||||
.data(params)
|
||||
@ -85,6 +90,7 @@ public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IF
|
||||
MqUtil.sendIMMessage(message);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Fans queryFansRelationship(Long fanId, Long vlogerId) {
|
||||
@ -97,7 +103,7 @@ public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IF
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void doCancel(Long myId, Long vloggerId) {
|
||||
public boolean doCancel(Long myId, Long vloggerId) {
|
||||
// 判断我们是否朋友关系,如果是,则需要取消双方的关系
|
||||
Fans fan = queryFansRelationship(myId, vloggerId);
|
||||
if (fan != null && YesOrNo.YES.type.equals(fan.getFriendFlag())) {
|
||||
@ -117,23 +123,31 @@ public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IF
|
||||
baseMapper.delete(deleteWrapper);
|
||||
} else {
|
||||
log.warn("取消关注时未找到自己的粉丝关系,myId={}, vloggerId={}", myId, vloggerId);
|
||||
return true;
|
||||
}
|
||||
// 博主的粉丝-1,我的关注-1
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FANS + ":" + vloggerId);
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FOLLOW + ":" + myId);
|
||||
// 推送取关消息
|
||||
Member follower = memberService.getById(myId);
|
||||
Member vlogger = memberService.getById(vloggerId);
|
||||
if (follower != null && vlogger != null && !myId.equals(vloggerId)) {
|
||||
// 新版:使用模板类型编号和参数
|
||||
// Map<String, Object> params = new HashMap<>();
|
||||
// params.put("followerNickname", follower.getNickname() == null ? "" : follower.getNickname());
|
||||
// params.put("vloggerNickname", vlogger.getNickname() == null ? "" : vlogger.getNickname());
|
||||
// SysMessageBo messageBo = new SysMessageBo();
|
||||
// messageBo.setTemplateType(org.dromara.system.domain.MessageTemplateType.UNFOLLOW); // 取关类型编号
|
||||
// messageBo.setTemplateParams(params);
|
||||
// messageBo.setSenderId(myId);
|
||||
// // 补充:设置消息标题,防止title为null
|
||||
// messageBo.setTitle("取消关注通知");
|
||||
// sysMessageService.sendMessageToUser(messageBo, vloggerId);
|
||||
// 新版:使用模板类型编号和参数
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("userId", follower.getUserId());
|
||||
params.put("nickname", follower.getNickname() == null ? "" : follower.getNickname());
|
||||
params.put("faceUrl", vlogger.getAvatar() == null ? "" : vlogger.getAvatar());
|
||||
params.put("action", MessageActionEnum.NEW_FOUCS.name());
|
||||
params.put("toUserId",vloggerId);
|
||||
MQMessage message = MQMessage.builder()
|
||||
.messageType("follow")
|
||||
.data(params)
|
||||
.source("member")
|
||||
.build();
|
||||
// 关注消息
|
||||
MqUtil.sendIMMessage(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,324 @@
|
||||
package com.wzj.soopin.member.service.impl;
|
||||
|
||||
import com.wzj.soopin.member.domain.po.Member;
|
||||
import com.wzj.soopin.member.domain.vo.IMCallbackVO;
|
||||
import com.wzj.soopin.member.service.IFansService;
|
||||
import com.wzj.soopin.member.service.IMCallbackService;
|
||||
import com.wzj.soopin.member.service.IMemberBlockService;
|
||||
import com.wzj.soopin.member.service.IMemberService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.dromara.common.core.constant.CacheConstants;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.mq.domain.MQMessage;
|
||||
import org.dromara.common.mq.enums.MessageActionEnum;
|
||||
import org.dromara.common.mq.utils.MqUtil;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class IMCallbackServiceImpl implements IMCallbackService {
|
||||
private final IFansService fansService;
|
||||
private final IMemberBlockService memberBlockService;
|
||||
private final IMemberService memberService;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public IMCallbackVO handleImFollow(Map<String, Object> callbackData) {
|
||||
// 幂等性校验:已存在则不再插入
|
||||
// 1. IM回调格式
|
||||
if (callbackData != null && callbackData.containsKey("CallbackCommand")) {
|
||||
log.info("收到IM关注回调: {}", callbackData);
|
||||
|
||||
// 1.1 兼容腾讯IM的 PairList 回调格式
|
||||
Object pairListObj = callbackData.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
Long fromId = null;
|
||||
Long toId = null;
|
||||
try {
|
||||
fromId = Long.valueOf(fromAccountObj.toString());
|
||||
toId = Long.valueOf(toAccountObj.toString());
|
||||
} catch (Exception e) {
|
||||
log.warn("IM回调Pair参数转换失败: {}", pair);
|
||||
continue;
|
||||
}
|
||||
fansService.doFollow(fromId, toId);
|
||||
log.info("IM回调处理成功: fromId={}, vloggerId={}", fromId, toId);
|
||||
} else {
|
||||
log.warn("IM回调Pair参数不完整: {}", pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回IM平台要求的应答
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
|
||||
// 1.2 兼容原有 FollowList 格式
|
||||
String fromAccount = (String) callbackData.get("From_Account");
|
||||
List<Map<String, String>> followList = (List<Map<String, String>>) callbackData.get("FollowList");
|
||||
if (fromAccount != null && followList != null) {
|
||||
Long fromId = Long.valueOf(fromAccount.replace("UserID_", ""));
|
||||
List<Long> vloggerIds = new java.util.ArrayList<>();
|
||||
for (Map<String, String> follow : followList) {
|
||||
String toAccount = follow.get("To_Account");
|
||||
fansService.doFollow(fromId, Long.valueOf(toAccount.replace("UserID_", "")));
|
||||
}
|
||||
|
||||
log.info("IM回调处理成功: fromId={}, vloggerIds={}", fromId, vloggerIds);
|
||||
} else {
|
||||
log.warn("IM回调参数不完整: {}", callbackData);
|
||||
}
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
// 2. 兼容JSON方式传递myId和vloggerId
|
||||
if (callbackData != null && callbackData.containsKey("myId") && callbackData.containsKey("vloggerId")) {
|
||||
log.info("收到JSON关注回调: {}", callbackData);
|
||||
Object myIdObj = callbackData.get("myId");
|
||||
Object vloggerIdObj = callbackData.get("vloggerId");
|
||||
if (myIdObj != null && vloggerIdObj != null) {
|
||||
Long myIdVal = Long.valueOf(myIdObj.toString());
|
||||
Long vloggerIdVal = Long.valueOf(vloggerIdObj.toString());
|
||||
fansService.doFollow(myIdVal, vloggerIdVal);
|
||||
|
||||
log.info("JSON回调处理成功: myId={}, vloggerId={}", myIdVal, vloggerIdVal);
|
||||
return IMCallbackVO.success();
|
||||
} else {
|
||||
log.warn("JSON回调参数不完整: {}", callbackData);
|
||||
}
|
||||
}
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IMCallbackVO handleImCancelFollow(Map<String, Object> callbackData) {
|
||||
// 1. 判断是否为IM回调格式
|
||||
if (callbackData != null && callbackData.containsKey("CallbackCommand")) {
|
||||
log.info("收到IM取消关注回调: {}", callbackData);
|
||||
|
||||
// 1.1 兼容腾讯IM的 PairList 回调格式
|
||||
Object pairListObj = callbackData.get("PairList");
|
||||
log.info("PairList 类型: {}, 内容: {}", pairListObj != null ? pairListObj.getClass() : "null", pairListObj);
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
Long fromId = null;
|
||||
Long toId = null;
|
||||
try {
|
||||
fromId = Long.valueOf(fromAccountObj.toString());
|
||||
toId = Long.valueOf(toAccountObj.toString());
|
||||
} catch (Exception e) {
|
||||
log.warn("IM回调Pair参数转换失败: {}", pair);
|
||||
continue;
|
||||
}
|
||||
fansService.doCancel(fromId, toId);
|
||||
log.info("IM取消关注回调处理成功: fromId={}, toId={}", fromId, toId);
|
||||
} else {
|
||||
log.warn("IM取消关注回调Pair参数不完整: {}", pair);
|
||||
}
|
||||
} else {
|
||||
log.warn("PairList元素不是Map: {}", pairObj);
|
||||
}
|
||||
}
|
||||
// 返回IM平台要求的应答
|
||||
|
||||
return IMCallbackVO.success();
|
||||
} else {
|
||||
log.warn("PairList 不是 List 类型: {}", pairListObj);
|
||||
}
|
||||
|
||||
// 1.2 兼容原有 To_Account 逻辑
|
||||
String fromAccount = (String) callbackData.get("From_Account");
|
||||
List<String> toAccounts = null;
|
||||
// 兼容腾讯IM的 To_Account 可能是 List<String> 或 List<Map<String, String>>
|
||||
Object toAccountObj = callbackData.get("To_Account");
|
||||
if (toAccountObj instanceof List) {
|
||||
List<?> list = (List<?>) toAccountObj;
|
||||
if (!list.isEmpty() && list.get(0) instanceof String) {
|
||||
toAccounts = (List<String>) list;
|
||||
} else if (!list.isEmpty() && list.get(0) instanceof Map) {
|
||||
// 兼容部分IM平台格式
|
||||
toAccounts = new java.util.ArrayList<>();
|
||||
for (Object obj : list) {
|
||||
Map<?, ?> map = (Map<?, ?>) obj;
|
||||
Object acc = map.get("To_Account");
|
||||
if (acc != null) toAccounts.add(acc.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fromAccount != null && toAccounts != null) {
|
||||
Long fromId = Long.valueOf(fromAccount.replace("UserID_", ""));
|
||||
for (String toAccount : toAccounts) {
|
||||
Long toId = Long.valueOf(toAccount.replace("UserID_", ""));
|
||||
fansService.doCancel(fromId, toId);
|
||||
}
|
||||
log.info("IM取消关注回调处理成功: fromId={}, toAccounts={}", fromId, toAccounts);
|
||||
} else {
|
||||
log.warn("IM取消关注回调参数不完整: {}", callbackData);
|
||||
}
|
||||
// 返回IM平台要求的应答
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
return IMCallbackVO.success();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 资料修改回调处理逻辑
|
||||
@Override
|
||||
public IMCallbackVO handleProfileCallbackPortraitSet(Map<String, Object> callbackData) {
|
||||
log.info("收到IM用户资料更新回调: {}", callbackData);
|
||||
String fromAccount = (String) callbackData.get("From_Account");
|
||||
List<Map<String, Object>> profileItems = (List<Map<String, Object>>) callbackData.get("ProfileItem");
|
||||
if (fromAccount != null && profileItems != null) {
|
||||
Long userId = null;
|
||||
try {
|
||||
userId = Long.valueOf(fromAccount.replace("UserID_", ""));
|
||||
} catch (Exception e) {
|
||||
log.warn("用户ID转换失败: {}", fromAccount);
|
||||
}
|
||||
if (userId != null) {
|
||||
Member memberToUpdate = memberService.getById(userId);
|
||||
if (memberToUpdate != null) {
|
||||
for (Map<String, Object> item : profileItems) {
|
||||
String tag = (String) item.get("Tag");
|
||||
Object value = item.get("Value");
|
||||
updateMemberProfile(memberToUpdate, tag, value);
|
||||
}
|
||||
memberService.updateById(memberToUpdate);
|
||||
log.info("IM用户资料更新回调处理成功: userId={}, profileItems={}", userId, profileItems);
|
||||
} else {
|
||||
log.warn("IM用户资料更新回调中用户不存在: userId={}", userId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("IM用户资料更新回调参数不完整: {}", callbackData);
|
||||
}
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
|
||||
// 只更新 nickname/gender 字段
|
||||
private void updateMemberProfile(Member member, String tag, Object value) {
|
||||
if (tag == null || value == null) {
|
||||
return;
|
||||
}
|
||||
switch (tag) {
|
||||
case "Tag_Profile_IM_Nick":
|
||||
member.setNickname(value.toString());
|
||||
break;
|
||||
case "Tag_Profile_IM_Gender":
|
||||
if ("Gender_Type_Male".equals(value)) {
|
||||
member.setGender(1);
|
||||
} else if ("Gender_Type_Female".equals(value)) {
|
||||
member.setGender(2);
|
||||
} else {
|
||||
member.setGender(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.debug("未处理的用户资料字段: tag={}, value={}", tag, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理IM黑名单添加事件
|
||||
@Override
|
||||
public IMCallbackVO handleImAddBlock(Map<String, Object> requestBody) {
|
||||
Object pairListObj = requestBody.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
try {
|
||||
Long myId = Long.valueOf(fromAccountObj.toString());
|
||||
Long vloggerId = Long.valueOf(toAccountObj.toString());
|
||||
// 1. 判断两个id不能为空
|
||||
if (myId == null || vloggerId == null) {
|
||||
log.warn("黑名单回调参数id为空: myId={}, vloggerId={}", myId, vloggerId);
|
||||
continue;
|
||||
}
|
||||
// 2. 是否已经存在关注关系
|
||||
boolean flow = fansService.queryDoIFollowVloger(myId, vloggerId);
|
||||
if (flow) {
|
||||
// 删除关注
|
||||
fansService.doCancel(myId, vloggerId);
|
||||
// 博主的粉丝-1,我的关注-1
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FANS + ":" + vloggerId);
|
||||
RedisUtils.decrAtomicValue(CacheConstants.MEMBER_FOLLOW + ":" + myId);
|
||||
}
|
||||
// 3. 检查是否已在黑名单中
|
||||
boolean hasblock = memberBlockService.hasBlocked(myId, vloggerId);
|
||||
if (hasblock) {
|
||||
log.info("用户{}已拉黑{}", myId, vloggerId);
|
||||
continue;
|
||||
}
|
||||
// 4. 拉黑
|
||||
memberBlockService.addBlock(myId, vloggerId);
|
||||
log.info("用户{}成功拉黑{}", myId, vloggerId);
|
||||
} catch (Exception e) {
|
||||
log.warn("黑名单回调参数转换失败: {}", pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
|
||||
|
||||
// 处理IM取消拉黑事件
|
||||
@Override
|
||||
public IMCallbackVO handleImCancelBlock(Map<String, Object> requestBody) {
|
||||
Object pairListObj = requestBody.get("PairList");
|
||||
if (pairListObj instanceof List) {
|
||||
List<?> pairList = (List<?>) pairListObj;
|
||||
for (Object pairObj : pairList) {
|
||||
if (pairObj instanceof Map) {
|
||||
Map<?, ?> pair = (Map<?, ?>) pairObj;
|
||||
Object fromAccountObj = pair.get("From_Account");
|
||||
Object toAccountObj = pair.get("To_Account");
|
||||
if (fromAccountObj != null && toAccountObj != null) {
|
||||
try {
|
||||
Long myId = Long.valueOf(fromAccountObj.toString());
|
||||
Long vloggerId = Long.valueOf(toAccountObj.toString());
|
||||
if (myId == null || vloggerId == null) {
|
||||
log.warn("取消拉黑回调参数id为空: myId={}, vloggerId={}", myId, vloggerId);
|
||||
continue;
|
||||
}
|
||||
memberBlockService.removeBlock(myId, vloggerId);
|
||||
log.info("用户{}已取消拉黑{}", myId, vloggerId);
|
||||
} catch (Exception e) {
|
||||
log.warn("取消拉黑回调参数转换失败: {}", pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return IMCallbackVO.success();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user