diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/im/ImCredentialsController.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/im/ImCredentialsController.java index 5b0da4a..5d5d157 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/im/ImCredentialsController.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/im/ImCredentialsController.java @@ -2,19 +2,25 @@ package com.ruoyi.oa.im; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.oa.im.mapper.ImBindMapper; +import com.ruoyi.system.mapper.SysUserMapper; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** - * 当前用户的 IM 凭据(供 Web SDK 登录) + * 当前用户的 IM 凭据。 + * 没有绑定时自动给当前 OA 用户注册一个 IM 账号(imUserId = "oa_{userId}"),手机号不再是必须。 */ +@Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/system/user/im") @@ -23,17 +29,30 @@ public class ImCredentialsController extends BaseController { private final ImBindMapper bindMapper; private final OpenImClient openImClient; private final OpenImProperties props; + private final SysUserMapper userMapper; - /** 当前用户 IM 登录所需信息:imUserId + token + apiUrl + wsUrl */ + /** + * 获取当前用户 IM 登录所需信息。 + * @param platformId 1=iOS 2=Android 3=Windows 4=OSX 5=Web;默认 5 + */ @GetMapping("/credentials") - public R> credentials() { + public R> credentials( + @RequestParam(value = "platformId", required = false, defaultValue = "5") int platformId) { Long userId = LoginHelper.getUserId(); - ImBind bind = bindMapper.selectById(userId); - if (bind == null || bind.getImUserId() == null) { - return R.fail("当前账号未绑定 IM"); + if (userId == null) { + return R.fail("未登录"); } - // platformId=5: Web - String token = openImClient.issueUserToken(bind.getImUserId(), 5); + + ImBind bind = bindMapper.selectById(userId); + if (bind == null || bind.getImUserId() == null || bind.getImUserId().isEmpty()) { + // 自动注册:不再要求手机号 + bind = ensureBind(userId); + if (bind == null) { + return R.fail("IM 账号注册失败"); + } + } + + String token = openImClient.issueUserToken(bind.getImUserId(), platformId); if (token == null) { return R.fail("获取 IM token 失败"); } @@ -44,4 +63,29 @@ public class ImCredentialsController extends BaseController { data.put("wsUrl", props.getWsUrl()); return R.ok(data); } + + /** 给指定 OA userId 在 OpenIM 主 API 直接注册一个账号并落 bind 表 */ + private ImBind ensureBind(Long oaUserId) { + SysUser u = userMapper.selectById(oaUserId); + if (u == null) return null; + String nickname = u.getNickName() != null && !u.getNickName().isEmpty() + ? u.getNickName() + : (u.getUserName() != null ? u.getUserName() : ("user" + oaUserId)); + String imUserId = "oa_" + oaUserId; + + boolean ok = openImClient.registerImUserDirect(imUserId, nickname); + if (!ok) { + log.warn("[IM] registerImUserDirect failed for OA user {}", oaUserId); + return null; + } + + ImBind bind = new ImBind(); + bind.setUserId(oaUserId); + bind.setPhone(u.getPhonenumber()); + bind.setImUserId(imUserId); + bind.setBindStatus(1); + bindMapper.insert(bind); + log.info("[IM] auto-bound OA {} -> IM {}", oaUserId, imUserId); + return bind; + } } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/im/OpenImClient.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/im/OpenImClient.java index dddbae0..f9d6a44 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/im/OpenImClient.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/im/OpenImClient.java @@ -205,6 +205,39 @@ public class OpenImClient { return null; } + /** + * 直接在 OpenIM 主 API 注册账号(无需手机号、无需 chat 层)。 + * 用于给 OA 内部账号(如 admin / 后台账号)建立 IM 账号。 + * + * @param imUserId 业务侧自定义的 IM userID(建议格式 "oa_{oaUserId}") + * @param nickname 昵称 + * @return true 注册成功或已存在;false 失败 + */ + public boolean registerImUserDirect(String imUserId, String nickname) { + if (!props.isEnabled()) { return false; } + Map user = new HashMap<>(); + user.put("userID", imUserId); + user.put("nickname", nickname == null || nickname.isEmpty() ? imUserId : nickname); + user.put("faceURL", ""); + + Map body = new HashMap<>(); + body.put("secret", props.getSecret()); + body.put("users", java.util.Collections.singletonList(user)); + + JSONObject resp = postJson(props.getApiUrl() + "/user/user_register", body, getAdminToken()); + Integer errCode = resp.getInteger("errCode"); + if (errCode != null && errCode == 0) { + return true; + } + // 已存在通常 errCode 1102 或 20004,视为成功 + if (errCode != null && (errCode == 1102 || errCode == 20004)) { + log.debug("[OpenIM] imUser {} already exists, ok", imUserId); + return true; + } + log.warn("[OpenIM] registerImUserDirect failed for {}: {}", imUserId, resp); + return false; + } + private static String md5(String text) { try { MessageDigest md = MessageDigest.getInstance("MD5");