修改回调整合分发模式

This commit is contained in:
曹佳豪 2025-07-17 14:30:20 +08:00
parent 5e239c99a4
commit 83ed8e4150
3 changed files with 342 additions and 24 deletions

View File

@ -87,33 +87,12 @@ public class SysMessageServiceImpl extends ServiceImpl<SysMessageMapper, SysMess
@Override
@Transactional(rollbackFor = Exception.class)
public int sendMessageToUser(SysMessageBo message, Long userId) {
//加一个约定
// 保存消息
SysMessage entity = message.toEntity();
Date scheduledTime = entity.getScheduledTime();
// if (scheduledTime != null && scheduledTime.after(new Date())) {
// entity.setStatus("0"); // 未发送
// messageMapper.insert(entity);
// // 注册SnailJob单次任务
// SnailJobApiUtil.createSingleJob(entity.getId(), scheduledTime);
// // 检查消息与用户关联是否已存在
// 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;
// }
// // 定时消息不发布事件
// return rows;
// } else {
entity.setStatus("1"); // 已发送
messageMapper.insert(entity);
// 检查消息与用户关联是否已存在

View File

@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
@Slf4j
@RequestMapping("/ums/fans/")
@ -103,6 +104,42 @@ public class FansController {
// 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;
}
handleImFollow(fromId, java.util.Arrays.asList(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) {
@ -156,6 +193,47 @@ public class FansController {
// 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>>

View File

@ -0,0 +1,261 @@
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 org.apache.commons.codec.digest.DigestUtils;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.redis.utils.RedisUtils;
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;
@PostMapping
public R handleCallback(
@RequestParam(value = "Sign", required = false) String sign,
@RequestParam(value = "RequestTime", required = false) String requestTime,
@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);
// }
// 2. 快速响应
R response = R.ok();
// 3. 分发业务逻辑
if (StringUtils.isBlank(callbackCommand)) {
return R.fail(1002, "缺少CallbackCommand");
}
switch (callbackCommand) {
case "Sns.CallbackFriendAdd":
// 关注直接调用 FansController follow
fansController.follow(requestBody, null, null);
break;
case "Sns.CallbackFriendDelete":
// 取关直接调用 FansController cancel
fansController.cancel(requestBody, null, null);
break;
case "Profile.CallbackPortraitSet":
// 资料修改
handleProfileCallbackPortraitSet(requestBody);
break;
case "Sns.CallbackBlackListAdd":
handleImAddBlock(requestBody);
break;
case "Sns.CallbackBlackListDelete":
handleImCancelBlock(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);
}
}
}
}
}
}
// 签名校验方法
private boolean verifySignature(String token, String sign, String requestTime) {
if (sign == null || requestTime == null) return false;
long now = System.currentTimeMillis() / 1000;
if (Math.abs(now - Long.parseLong(requestTime)) > 60) return false;
String localSign = DigestUtils.sha256Hex(token + requestTime);
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;
}
}
}