|
|
@@ -1,15 +1,18 @@
|
|
|
package cn.tr.module.mobile;
|
|
|
|
|
|
+import cn.hutool.core.lang.Pair;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
import cn.tr.module.mobile.config.ServerEventCallbackHandler;
|
|
|
+import cn.tr.module.mobile.dto.ImLoginSuccessDTO;
|
|
|
import cn.tr.module.mobile.dto.MsgDTO;
|
|
|
+import cn.tr.module.mobile.utils.UserUtils;
|
|
|
import io.netty.channel.Channel;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import net.x52im.mobileimsdk.server.event.ServerEventListener;
|
|
|
import net.x52im.mobileimsdk.server.processor.OnlineProcessor;
|
|
|
import net.x52im.mobileimsdk.server.protocal.Protocal;
|
|
|
import net.x52im.mobileimsdk.server.protocal.s.PKickoutInfo;
|
|
|
-
|
|
|
/**
|
|
|
* @author wangzl
|
|
|
* @description: TODO
|
|
|
@@ -31,6 +34,11 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
@Override
|
|
|
public int onUserLoginVerify(String userId, String token, String extra, Channel session) {
|
|
|
log.debug("【DEBUG_回调通知】正在调用回调方法:OnVerifyUserCallBack...(extra=" + extra + ")");
|
|
|
+ // 校验userId格式必须是"字符串-字符串"的格式
|
|
|
+ if (userId == null || !userId.matches("^[^-]+-[^-]+-[^-]+$")) {
|
|
|
+ log.warn("用户登录验证失败,userId格式不正确: {}", userId);
|
|
|
+ return 1025; // 返回自定义错误码,表示userId格式错误
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
/**
|
|
|
@@ -45,6 +53,9 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
@Override
|
|
|
public void onUserLoginSucess(String userId, String extra, Channel session) {
|
|
|
log.debug("【IM_回调通知onUserLoginSucess】用户:" + userId + " 上线了!");
|
|
|
+ ImLoginSuccessDTO loginUser = parseCompoundUserId(userId);
|
|
|
+ ServerEventCallbackHandler.onUserLoginSuccess.accept(loginUser);
|
|
|
+
|
|
|
}
|
|
|
/**
|
|
|
* 用户退出登录回调方法定义(可理解为下线通知回调)。
|
|
|
@@ -70,22 +81,8 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
*/
|
|
|
@Override
|
|
|
public boolean onTransferMessage4C2SBefore(Protocal p, Channel session) {
|
|
|
- String dataContent = p.getDataContent();
|
|
|
log.debug("收到C2S消息前处理: from={}, to={}, fp={}, typeu={}, dataContent={}",
|
|
|
- p.getFrom(), p.getTo(), p.getFp(), p.getTypeu(), dataContent);
|
|
|
-
|
|
|
- if(!JSONUtil.isTypeJSON(dataContent)){
|
|
|
- log.warn("收到的C2S消息不是有效的JSON格式: {}", dataContent);
|
|
|
- return false;
|
|
|
- }
|
|
|
- try {
|
|
|
- MsgDTO msg = JSONUtil.toBean(dataContent, MsgDTO.class);
|
|
|
- log.debug("解析C2S消息成功: clinicId={}, type={}, role={}",
|
|
|
- msg.getClinicId(), msg.getType(), msg.getRole());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("解析C2S消息失败: dataContent={}, error={}", dataContent, e.getMessage());
|
|
|
- return false;
|
|
|
- }
|
|
|
+ p.getFrom(), p.getTo(), p.getFp(), p.getTypeu(),p.getDataContent());
|
|
|
return true;
|
|
|
}
|
|
|
/**
|
|
|
@@ -99,27 +96,12 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
*/
|
|
|
@Override
|
|
|
public boolean onTransferMessage4C2CBefore(Protocal p, Channel session) {
|
|
|
- String dataContent = p.getDataContent();
|
|
|
log.debug("收到C2C消息前处理: from={}, to={}, fp={}, typeu={}, dataContent={}",
|
|
|
- p.getFrom(), p.getTo(), p.getFp(), p.getTypeu(), dataContent);
|
|
|
-
|
|
|
- if(!JSONUtil.isTypeJSON(dataContent)){
|
|
|
- log.warn("收到的C2C消息不是有效的JSON格式: {}", dataContent);
|
|
|
- return false;
|
|
|
- }
|
|
|
- try {
|
|
|
- MsgDTO msg = JSONUtil.toBean(dataContent, MsgDTO.class);
|
|
|
- log.debug("解析C2C消息成功: clinicId={}, type={}, role={}",
|
|
|
- msg.getClinicId(), msg.getType(), msg.getRole());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("解析C2C消息失败: dataContent={}, error={}", dataContent, e.getMessage());
|
|
|
- return false;
|
|
|
- }
|
|
|
+ p.getFrom(), p.getTo(), p.getFp(), p.getTypeu(),p.getDataContent());
|
|
|
return true;
|
|
|
}
|
|
|
/**
|
|
|
* 收到客户端发送给“服务端”的数据回调通知(即:消息路径为“C2S”的消息).
|
|
|
- *
|
|
|
* @param p 消息/指令的完整协议包对象
|
|
|
* @param session 此客户端连接对应的 netty “会话”
|
|
|
* @return true表示本方法已成功处理完成,否则表示未处理成功。此返回值目前框架中并没有特殊意义,仅作保留吧
|
|
|
@@ -128,27 +110,35 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
*/
|
|
|
@Override
|
|
|
public boolean onTransferMessage4C2S(Protocal p, Channel session) {
|
|
|
+ // 发送者uid
|
|
|
+ ImLoginSuccessDTO loginUser = parseCompoundUserId(p.getFrom());
|
|
|
+
|
|
|
// 接收者uid
|
|
|
String userId = p.getTo();
|
|
|
- // 发送者uid
|
|
|
- String from_user_id = p.getFrom();
|
|
|
// 消息或指令内容
|
|
|
String dataContent = p.getDataContent();
|
|
|
+ if(!JSONUtil.isTypeJSON(dataContent)){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
// 消息或指令指纹码(即唯一ID)
|
|
|
String fingerPrint = p.getFp();
|
|
|
// 【重要】用户定义的消息或指令协议类型(开发者可据此类型来区分具体的消息或指令)
|
|
|
int typeu = p.getTypeu();
|
|
|
try {
|
|
|
+ if(!validateMsgDTO(dataContent)){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
MsgDTO msg = JSONUtil.toBean(dataContent, MsgDTO.class);
|
|
|
- msg.setFromUserId(from_user_id);
|
|
|
+ UserUtils.setCurrentUser(loginUser.getUserId(),loginUser.getTenantId());
|
|
|
+ msg.setFromUserId(loginUser.getUserId());
|
|
|
msg.setToUserId(userId);
|
|
|
msg.setMsgId(fingerPrint);
|
|
|
+ msg.setTenantId(loginUser.getTenantId());
|
|
|
ServerEventCallbackHandler.onTransferMessage4C2S.accept(msg);
|
|
|
}catch (Exception e){
|
|
|
- log.error("【IM_回调通知】[typeu=" + typeu + "]收到了客户端" + from_user_id + "发给服务端的消息:str=" + dataContent);
|
|
|
+ log.error("【IM_回调通知】[typeu=" + typeu + "]收到了客户端" + p.getFrom() + "发给服务端的消息:str=" + dataContent);
|
|
|
}
|
|
|
-
|
|
|
- log.debug("【DEBUG_回调通知】[typeu=" + typeu + "]收到了客户端" + from_user_id + "发给服务端的消息:str=" + dataContent);
|
|
|
+ log.debug("【DEBUG_回调通知】[typeu=" + typeu + "]收到了客户端" + p.getFrom() + "发给服务端的消息:str=" + dataContent);
|
|
|
return true;
|
|
|
}
|
|
|
/**
|
|
|
@@ -184,7 +174,6 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
/**
|
|
|
* 服务端在进行消息发送时,当对方在线但实时发送失败、以及其它各种问题导致消息并没能正常发出时,将无条件走本回调通知。
|
|
|
*
|
|
|
- *
|
|
|
* @param p 消息/指令的完整协议包对象
|
|
|
* @return true表示应用层已经处理了离线消息,否则表示应用层没有处理
|
|
|
* @see Protocal
|
|
|
@@ -199,19 +188,8 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
String from_user_id = p.getFrom();
|
|
|
// 消息或指令内容
|
|
|
String dataContent = p.getDataContent();
|
|
|
- // 消息或指令指纹码(即唯一ID)
|
|
|
- String fingerPrint = p.getFp();
|
|
|
- // 【重要】用户定义的消息或指令协议类型(开发者可据此类型来区分具体的消息或指令)
|
|
|
+ // 【重要】用户定义的消息或指令协议类型(开发者可据此类型来区分具体的消息或指令)
|
|
|
int typeu = p.getTypeu();
|
|
|
- try {
|
|
|
- MsgDTO msg = JSONUtil.toBean(dataContent, MsgDTO.class);
|
|
|
- msg.setFromUserId(from_user_id);
|
|
|
- msg.setToUserId(userId);
|
|
|
- msg.setMsgId(fingerPrint);
|
|
|
- ServerEventCallbackHandler.onTransferMessage_RealTimeSendFaild.accept(msg);
|
|
|
- }catch (Exception e){
|
|
|
- log.error("【IM_回调通知】[typeu=" + typeu + "]收到了客户端" + from_user_id + "发给客户端" + userId + "的消息:str=" + dataContent);
|
|
|
- }
|
|
|
log.debug("【DEBUG_回调通知】[typeu=" + typeu + "]客户端" + from_user_id + "发给客户端" + userId + "的消息:str=" + dataContent + ",因实时发送没有成功,需要上层应用作离线处理哦,否则此消息将被丢弃.");
|
|
|
return false;
|
|
|
}
|
|
|
@@ -225,4 +203,65 @@ public class ServerEventListenerImpl implements ServerEventListener {
|
|
|
public void onTransferMessage4C2C_AfterBridge(Protocal p){
|
|
|
// 默认本方法可
|
|
|
}
|
|
|
-}
|
|
|
+
|
|
|
+
|
|
|
+ private boolean validateMsgDTO(String msgContent) {
|
|
|
+ if(!JSONUtil.isTypeJSON(msgContent)){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ MsgDTO msg=JSONUtil.toBean(msgContent, MsgDTO.class);
|
|
|
+ if (msg == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 校验MsgDTO每个字段都不为空
|
|
|
+ if (StrUtil.isEmpty(msg.getClinicId())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StrUtil.isEmpty(msg.getType())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StrUtil.isEmpty(msg.getContent())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StrUtil.isEmpty(msg.getRole())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析复合用户ID,从格式为 "tenantId-userId-clinicId" 的字符串中提取租户ID、用户ID和诊所ID
|
|
|
+ *
|
|
|
+ * 在多租户系统中,为了区分不同租户、不同诊所下的同名用户,用户ID采用复合格式,
|
|
|
+ * 即 tenantId+"-"+userId+"-"+clinicId 的形式。
|
|
|
+ * 该方法用于将这种复合格式的用户ID解析为独立的租户ID、用户ID和诊所ID三部分。
|
|
|
+ *
|
|
|
+ * @param compoundUserId 复合用户ID,格式为 tenantId+"-"+userId+"-"+clinicId,例如 "tenant1-user123-clinic456"
|
|
|
+ * @return Pair<String, String> 键值对,其中:
|
|
|
+ * - Key: 用户ID (userId)
|
|
|
+ * - Value: 包含租户ID和诊所ID的组合字符串,格式为 tenantId+"-"+clinicId
|
|
|
+ * 如果输入参数为空或不包含足够的"-"分隔符,则返回(null, null)
|
|
|
+ *
|
|
|
+ * @see Pair
|
|
|
+ *
|
|
|
+ * @example
|
|
|
+ * <pre>
|
|
|
+ * parseCompoundUserId("tenant1-user123-clinic456") returns Pair.of("user123", "tenant1-clinic456")
|
|
|
+ * parseCompoundUserId("invalidFormat") returns Pair.of(null, null)
|
|
|
+ * parseCompoundUserId(null) returns Pair.of(null, null)
|
|
|
+ * </pre>
|
|
|
+ */
|
|
|
+ private ImLoginSuccessDTO parseCompoundUserId(String compoundUserId) {
|
|
|
+ ImLoginSuccessDTO result = null;
|
|
|
+ if (compoundUserId != null && compoundUserId.contains("-")) {
|
|
|
+ String[] parts = compoundUserId.split("-", 3); // 分割成3部分
|
|
|
+ if (parts.length >= 3) {
|
|
|
+ result = ImLoginSuccessDTO.of(parts[0], parts[1],parts[2]); // userId, tenantId-clinicId
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+}
|