OpenIMApiClient 替换

This commit is contained in:
wells
2026-08-08 15:20:45 +08:00
parent aa48225d85
commit 1f93719ee5
10 changed files with 176 additions and 40 deletions
@@ -1,5 +1,7 @@
package com.tailbet.common.constant;
import com.tailbet.common.enums.PlatformEnum;
/**
* OpenIM 协议相关常量
* <p>
@@ -117,13 +119,13 @@ public final class OpenImConstants {
* 服务端代操作使用的平台 ID(Web)。
* 与 App 端 iOS/Android 平台隔离,避免顶掉用户已登录 session。
*/
public static final int PLATFORM_ID_SERVER_WEB = 5;
public static final int PLATFORM_ID_SERVER_WEB = PlatformEnum.WEB.getCode();
/** App 端 iOS 平台 ID */
public static final int PLATFORM_ID_IOS = 1;
public static final int PLATFORM_ID_IOS = PlatformEnum.IOS.getCode();
/** App 端 Android 平台 ID */
public static final int PLATFORM_ID_ANDROID = 2;
public static final int PLATFORM_ID_ANDROID = PlatformEnum.ANDROID.getCode();
// ==================== 会话摘要 / 未读 ====================
@@ -0,0 +1,64 @@
package com.tailbet.common.enums;
/**
* OpenIM 平台 ID 枚举
* <p>
* 表示用户连接所在的终端平台。
* </p>
*
* @author socialapp团队
* @since 1.0.0
*/
public enum PlatformEnum {
IOS(1, "iOS"),
ANDROID(2, "Android"),
WINDOWS(3, "Windows"),
OSX(4, "macOS"),
WEB(5, "Web"),
MINI_WEB(6, "小程序或轻量 Web"),
LINUX(7, "Linux"),
APAD(8, "Android 平板"),
IPAD(9, "iPad"),
ADMIN(10, "管理端"),
HARMONYOS(11, "HarmonyOS"),
BOT(12, "Bot");
/**
* 平台 ID
*/
private final int code;
/**
* 平台名称
*/
private final String name;
PlatformEnum(int code, String name) {
this.code = code;
this.name = name;
}
public int getCode() {
return code;
}
public String getName() {
return name;
}
/**
* 根据 code 获取枚举
*
* @param code 平台 ID
* @return 对应的枚举值,未找到返回 null
*/
public static PlatformEnum fromCode(int code) {
for (PlatformEnum platform : values()) {
if (platform.code == code) {
return platform;
}
}
return null;
}
}
@@ -7,10 +7,11 @@ import com.tailbet.model.vo.LoginUserVo;
import com.tailbet.model.vo.LoginVo;
import com.tailbet.model.vo.R;
import com.tailbet.model.vo.UserContext;
import com.tailbet.openim.OpenIMClient;
import com.tailbet.openim.OpenIMApiClient;
import com.tailbet.service.IUserService;
import com.tailbet.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
@@ -23,13 +24,13 @@ public class UserController {
private final IUserService userService;
private final JwtUtil jwtUtil;
private final OpenIMClient openIMClient;
private final OpenIMApiClient openIMApiClient;
/**
* 注册
*/
@PostMapping("/register")
public R<Void> register(@RequestBody RegisterDTO dto) {
public R<Void> register(@RequestBody @Validated RegisterDTO dto) {
userService.register(dto.getUsername(), dto.getPassword());
return R.ok();
@@ -39,10 +40,10 @@ public class UserController {
* 登录
*/
@PostMapping("/login")
public R<LoginVo> login(@RequestBody LoginDTO dto) {
public R<LoginVo> login(@RequestBody @Validated LoginDTO dto) {
Long userId = userService.login(dto.getUsername(), dto.getPassword());
String token = jwtUtil.generateToken(userId, dto.getUsername());
String openIMToken = openIMClient.getUserToken(String.valueOf(userId));
String openIMToken = openIMApiClient.getUserToken(String.valueOf(userId), 2); // platformID 2=Android
LoginVo vo = new LoginVo();
vo.setToken(token);
+38 -11
View File
@@ -18,7 +18,7 @@ import com.tailbet.model.entity.RedPacketRecv;
import com.tailbet.model.entity.SysConfig;
import com.tailbet.model.entity.PendingTask;
import com.tailbet.model.PendingTaskInfo;
import com.tailbet.openim.OpenIMClient;
import com.tailbet.openim.OpenIMApiClient;
import com.tailbet.service.IGameService;
import com.tailbet.service.IRedPacketService;
import com.tailbet.service.IFakeUserService;
@@ -54,7 +54,8 @@ public class GameJob {
private final IGameService gameService;
private final IRedPacketService redPacketService;
private final IFakeUserService fakeUserService;
private final OpenIMClient openIMClient;
private final OpenIMApiClient openIMApiClient;
private final com.tailbet.config.OpenIMProperties openIMProperties;
// 局状态监控(使用内存缓存,实际生产应使用Redis)
private final Map<Long, RoundTask> roundTasks = new ConcurrentHashMap<>();
@@ -534,8 +535,13 @@ public class GameJob {
User user = userMapper.selectById(white.getUserId());
if (user != null) {
// 通过OpenIM发送通知
openIMClient.sendUserMessage(String.valueOf(user.getId()),
"尾数试算完成,请前往修改尾数。期号: " + round.getRoundNo());
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(user.getId()),
1, // sessionType 1=单聊
101, // contentType 101=文本
java.util.Map.of("content", "尾数试算完成,请前往修改尾数。期号: " + round.getRoundNo())
);
log.info("通知白名单用户尾数试算完成: userId={}, roundId={}", user.getId(), roundId);
}
}
@@ -546,24 +552,39 @@ public class GameJob {
*/
private void broadcastStartBet(Long groupId, String roundNo) {
// 注意: 实际生产中需要通过Group表映射获取OpenIM群ID
openIMClient.sendGroupMessage(String.valueOf(groupId),
roundNo + "期开始下注");
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(groupId),
3, // sessionType 3=群聊
101, // contentType 101=文本
java.util.Map.of("content", roundNo + "期开始下注")
);
}
/**
* 广播结束下注
*/
private void broadcastEndBet(Long groupId, String roundNo) {
openIMClient.sendGroupMessage(String.valueOf(groupId),
roundNo + "期结束下注");
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(groupId),
3, // sessionType 3=群聊
101, // contentType 101=文本
java.util.Map.of("content", roundNo + "期结束下注")
);
}
/**
* 广播开始发红包
*/
private void broadcastStartRp(Long groupId, String roundNo) {
openIMClient.sendGroupMessage(String.valueOf(groupId),
roundNo + "期开始发红包");
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(groupId),
3, // sessionType 3=群聊
101, // contentType 101=文本
java.util.Map.of("content", roundNo + "期开始发红包")
);
}
/**
@@ -581,7 +602,13 @@ public class GameJob {
message += "\n\n近30期:\n" + history;
}
openIMClient.sendGroupMessage(String.valueOf(groupId), message);
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(groupId),
3, // sessionType 3=群聊
101, // contentType 101=文本
java.util.Map.of("content", message)
);
}
/**
@@ -1,5 +1,7 @@
package com.tailbet.model.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
@@ -16,4 +18,24 @@ public class LoginDTO {
* 密码
*/
private String password;
/**
* 平台ID
* @see com.tailbet.common.enums.PlatformEnum
* <ul>
* <li>1=iOS</li>
* <li>2=Android</li>
* <li>3=Windows</li>
* <li>4=macOS</li>
* <li>5=Web</li>
* <li>6=小程序或轻量 Web</li>
* <li>7=Linux</li>
* <li>8=Android 平板</li>
* <li>9=iPad</li>
* <li>10=管理端</li>
* <li>11=HarmonyOS</li>
* <li>12=Bot</li>
* </ul>
*/
@NotNull(message = "platformID不能为空")
private Integer platformID;
}
@@ -1,5 +1,6 @@
package com.tailbet.model.dto;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
/**
@@ -10,10 +11,12 @@ public class RegisterDTO {
/**
* 账号
*/
@NotEmpty(message = "username不能为空")
private String username;
/**
* 密码
*/
@NotEmpty(message = "password不能为空")
private String password;
}
@@ -6,6 +6,7 @@ import com.tailbet.common.enums.ErrorCodeEnum;
import com.tailbet.common.exception.BusinessException;
import com.tailbet.config.OpenIMProperties;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
@@ -49,6 +50,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @author socialapp团队
* @since 1.0.0
*/
@RequiredArgsConstructor
@Slf4j
@Component
public class OpenIMApiClient {
@@ -90,11 +92,8 @@ public class OpenIMApiClient {
*/
private final AtomicBoolean deleteConversationsSupported = new AtomicBoolean(false);
/**
* OpenIM 专用 RestTemplate
*/
@Resource(name = "openIMRestTemplate")
private RestTemplate restTemplate;
private final RestTemplate restTemplate;
/**
* JSON 工具(合并 OpenIM ex 字段)
@@ -6,7 +6,7 @@ import com.tailbet.service.IBetService;
import com.tailbet.service.IGameService;
import com.tailbet.service.IRedPacketService;
import com.tailbet.service.IGroupService;
import com.tailbet.openim.OpenIMClient;
import com.tailbet.openim.OpenIMApiClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@@ -24,7 +24,8 @@ public class OpenIMCallbackController {
private final IGameService gameService;
private final IRedPacketService redPacketService;
private final IGroupService groupService;
private final OpenIMClient openIMClient;
private final OpenIMApiClient openIMApiClient;
private final com.tailbet.config.OpenIMProperties openIMProperties;
/**
* 接收群消息回调
@@ -164,7 +165,13 @@ public class OpenIMCallbackController {
*/
private void sendGroupMessage(String openimGroupId, String message) {
try {
openIMClient.sendGroupMessage(openimGroupId, message);
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
openimGroupId,
3, // sessionType 3=群聊
101, // contentType 101=文本
java.util.Map.of("content", message)
);
} catch (Exception e) {
log.error("发送群消息失败: {}", e.getMessage());
}
@@ -5,7 +5,7 @@ import com.tailbet.mapper.UserMapper;
import com.tailbet.mapper.DigitWhiteMapper;
import com.tailbet.model.entity.DigitWhite;
import com.tailbet.model.entity.User;
import com.tailbet.openim.OpenIMClient;
import com.tailbet.openim.OpenIMApiClient;
import com.tailbet.service.IPushService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
@@ -24,7 +24,8 @@ public class PushServiceImpl implements IPushService {
private final UserMapper userMapper;
private final DigitWhiteMapper digitWhiteMapper;
private final OpenIMClient openIMClient;
private final OpenIMApiClient openIMApiClient;
private final com.tailbet.config.OpenIMProperties openIMProperties;
/**
* 推送尾数试算完成通知给白名单用户
@@ -94,8 +95,13 @@ public class PushServiceImpl implements IPushService {
}
// 通过OpenIM单聊发送(生产环境可扩展FCM/APNs离线推送)
openIMClient.sendUserMessage(String.valueOf(user.getId()),
message.getTitle() + " - " + message.getContent());
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(user.getId()),
1, // sessionType 1=单聊
101, // contentType 101=文本
java.util.Map.of("content", message.getTitle() + " - " + message.getContent())
);
log.info("推送发送成功: userId={}, type={}", message.getUserId(), message.getType());
} catch (Exception e) {
@@ -110,9 +116,14 @@ public class PushServiceImpl implements IPushService {
@Override
public void broadcastToGroup(Long groupId, IPushService.PushMessage message) {
try {
// 通过OpenIM群消息发送
openIMClient.sendGroupMessage(String.valueOf(groupId),
message.getContent());
// 通过OpenIM群消息发送(使用管理员身份发送)
openIMApiClient.sendMsg(
openIMProperties.getAdminUserId(),
String.valueOf(groupId),
3, // sessionType 3=群聊
101, // contentType 101=文本
java.util.Map.of("content", message.getContent())
);
log.info("群广播发送成功: groupId={}, type={}", groupId, message.getType());
} catch (Exception e) {
log.error("群广播发送失败: groupId={}, type={}, error={}",
@@ -5,7 +5,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tailbet.model.entity.User;
import com.tailbet.mapper.UserMapper;
import com.tailbet.model.dto.UpdateUserDTO;
import com.tailbet.openim.OpenIMClient;
import com.tailbet.openim.OpenIMApiClient;
import com.tailbet.service.IUserService;
import com.tailbet.util.JwtUtil;
import com.tailbet.util.PasswordUtil;
@@ -26,8 +26,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
private final UserMapper userMapper;
private final PasswordUtil passwordUtil;
private final JwtUtil jwtUtil;
private final OpenIMClient openIMClient;
private final OpenIMApiClient openIMApiClient;
/**
@@ -59,9 +58,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
userMapper.insert(user);
// 在OpenIM创建用户
boolean openIMResult = openIMClient.createUser(String.valueOf(user.getId()), user.getNickname());
if (!openIMResult) {
log.warn("OpenIM用户创建失败: userId={}, username={}", user.getId(), username);
try {
openIMApiClient.userRegister(String.valueOf(user.getId()), user.getNickname(), null);
} catch (Exception e) {
log.warn("OpenIM用户创建失败: userId={}, username={}, error={}", user.getId(), username, e.getMessage());
throw new RuntimeException("OpenIM用户创建失败,请稍后再试!");
}