diff --git a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java index 5b554ad..fd8225f 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java @@ -31,4 +31,7 @@ public class LoginDTO { @Schema(description = "来源网址") private String sourceUrl; + @Schema(description = "token标识") + private String uniqueToken; + } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/User.java b/im-platform/src/main/java/com/bx/implatform/entity/User.java index 76521a1..0cf0c10 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/User.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/User.java @@ -109,5 +109,10 @@ public class User { */ private String sourceUrl; + /** + * token标识 + */ + private String uniqueToken; + } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java index 0bd6cbc..349e682 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java @@ -13,10 +13,12 @@ import com.bx.imcommon.util.ThreadPoolExecutorFactory; import com.bx.implatform.contant.Constant; import com.bx.implatform.dto.PrivateMessageDTO; import com.bx.implatform.entity.PrivateMessage; +import com.bx.implatform.entity.User; import com.bx.implatform.enums.MessageStatus; import com.bx.implatform.enums.MessageType; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.PrivateMessageMapper; +import com.bx.implatform.mapper.UserMapper; import com.bx.implatform.service.FriendService; import com.bx.implatform.service.PrivateMessageService; import com.bx.implatform.session.SessionContext; @@ -46,15 +48,27 @@ public class PrivateMessageServiceImpl extends ServiceImpl implements Us @Override public LoginVO login(LoginDTO dto) { - log.info("【测试】前端传的IP:{}", dto.getSourceUrl()); + log.info("【测试】前端传的uniqueToken:{}", dto.getUniqueToken()); // 生成游客唯一标识UUID String guestUuid = UUID.randomUUID().toString(); @@ -106,18 +106,13 @@ public class UserServiceImpl extends ServiceImpl implements Us guestUser.setNickName(guestUserName); guestUser.setPassword(""); guestUser.setUuid(guestUuid); - // ========== 先设置IP ========== guestUser.setLastLoginIp(dto.getIp()); - guestUser.setSourceUrl(dto.getSourceUrl()); + guestUser.setSourceUrl(dto.getSourceUrl()); + guestUser.setUniqueToken(dto.getUniqueToken()); // 保存到数据库 this.save(guestUser); - // ========== 正确更新 IP 和地址 ========== -// if(StrUtil.isNotBlank(dto.getIp())){ -// this.updateIpAndAddress(guestUser); -// } - - Long customerServiceId = this.getRandomCustomerServiceId(); + Long customerServiceId = this.getCustomerServiceIdByUniqueToken(dto.getUniqueToken()); UserSession guestSession = new UserSession(); guestSession.setUserId(guestUser.getId()); @@ -148,23 +143,33 @@ public class UserServiceImpl extends ServiceImpl implements Us vo.setCustomerServiceId(customerServiceId == null ? -1 : customerServiceId); vo.setUser(guestUser); - log.info("游客登录成功,userId:{}, uuid:{}, ip:{}", guestUser.getId(), guestUuid, dto.getIp()); + log.info("游客登录成功,userId:{}, uniqueToken:{},分配客服ID:{}", + guestUser.getId(), dto.getUniqueToken(), customerServiceId); return vo; } - - public Long getRandomCustomerServiceId() { + /** + * 根据 uniqueToken 查询【客服】 + * 客服条件:is_customer = 2 且 unique_token = 传入的token + */ + public Long getCustomerServiceIdByUniqueToken(String uniqueToken) { + // 1. 构建查询条件 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); - // 条件:is_customer = 2 表示客服 - queryWrapper.eq(User::getIsCustomer, 2); - // 只查id字段,提升效率 + queryWrapper.eq(User::getIsCustomer, 2); // 只查客服 + + // 2. 有 token 才按 token 匹配 + if (StrUtil.isNotBlank(uniqueToken)) { + queryWrapper.eq(User::getUniqueToken, uniqueToken); + } + + // 3. 只查 ID queryWrapper.select(User::getId); - // 随机排序(mysql用rand()) - queryWrapper.last("ORDER BY RAND() LIMIT 1"); - User customerService = this.getOne(queryWrapper, false); // false=无结果不抛异常 + // 4. 查一条(false=查不到不抛异常) + User customer = this.getOne(queryWrapper, false); - return customerService == null ? null : customerService.getId(); + // 5. 有客服返回ID,没有返回null + return customer == null ? null : customer.getId(); } diff --git a/im-uniapp/pages/login/login.vue b/im-uniapp/pages/login/login.vue index 2fec36d..8ef2251 100644 --- a/im-uniapp/pages/login/login.vue +++ b/im-uniapp/pages/login/login.vue @@ -19,7 +19,8 @@ export default { userName: '', password: '', ip: '', - sourceUrl: '' + sourceUrl: '', + uniqueToken: '' // 添加token字段 } } }, @@ -53,35 +54,79 @@ export default { console.log("来源网址:", this.dataForm.sourceUrl); }, + + // 获取URL参数中的token + getTokenFromUrl() { + // #ifdef H5 + // 方法1:从当前页面的options获取 + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const options = currentPage.options || {}; + + if (options.token) { + this.dataForm.uniqueToken = options.token; + console.log("从options获取到token:", this.dataForm.uniqueToken); + return; + } + + // 方法2:直接从URL解析 + const urlParams = new URLSearchParams(window.location.search); + const token = urlParams.get('token'); + if (token) { + this.dataForm.uniqueToken = token; + console.log("从URL解析获取到token:", this.dataForm.uniqueToken); + } + // #endif + + // #ifdef APP-PLUS + // App端获取参数 + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const options = currentPage.options || {}; + if (options.token) { + this.dataForm.uniqueToken = options.token; + console.log("App端获取到token:", this.dataForm.uniqueToken); + } + // #endif + + // #ifdef MP-WEIXIN + // 小程序端获取参数 + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const options = currentPage.options || {}; + if (options.token) { + this.dataForm.uniqueToken = options.token; + console.log("小程序端获取到token:", this.dataForm.uniqueToken); + } + // #endif + }, + async autoLogin() { if (GLOBAL_AUTO_LOGIN_LOCK) return; GLOBAL_AUTO_LOGIN_LOCK = true; - // // 读取缓存 - // const loginInfo = uni.getStorageSync("loginInfo"); - - // // 已登录 → 先初始化,再跳转(修复点!) - // if (loginInfo) { - // try { - // // 必须执行全局IM初始化 - // getApp().$vm.init(); - // getApp().$vm.unloadStore(); - - // uni.reLaunch({ - // url: "/pages/chat/chat-box?chatIdx=0" - // }); - // } catch (err) { - // console.log("自动登录初始化失败", err); - // } - // return; - // } + // 获取token(优先使用URL中的token) + this.getTokenFromUrl(); + // 获取来源网址 this.getSourceUrl(); await this.getIp(); + // 准备登录数据 + const loginData = { + terminal: this.dataForm.terminal, + userName: this.dataForm.userName, + password: this.dataForm.password, + ip: this.dataForm.ip, + sourceUrl: this.dataForm.sourceUrl, + uniqueToken: this.dataForm.uniqueToken, + }; + + console.log("登录参数:", loginData); + this.$http({ url: '/login', - data: this.dataForm, + data: loginData, method: 'POST' }).then(loginInfo => { uni.setStorageSync("isAgree", this.isAgree); @@ -89,6 +134,7 @@ export default { getApp().$vm.init() getApp().$vm.unloadStore(); + console.log(loginInfo.customerServiceId); this.$http({ url: "/friend/add?friendId=" + loginInfo.customerServiceId, method: "POST" @@ -102,17 +148,25 @@ export default { } this.friendStore.addFriend(friend); - // ✅ 修改:传递客服ID和类型 - uni.reLaunch({ - url: `/pages/chat/chat-box?targetId=${loginInfo.customerServiceId}&type=PRIVATE` - }); + // 清除 URL 中的 token 参数 + // #ifdef H5 + // 直接使用 window.location 跳转,清除所有参数 + const cleanUrl = window.location.origin + window.location.pathname + '#/pages/chat/chat-box?targetId=' + loginInfo.customerServiceId + '&type=PRIVATE'; + window.location.href = cleanUrl; + // #endif + + // #ifndef H5 + uni.reLaunch({ + url: `/pages/chat/chat-box?targetId=${loginInfo.customerServiceId}&type=PRIVATE` + }); + // #endif }) }).catch(err => { console.log("自动登录失败", err); }); - }, + getIp() { // 获取本机ip return new Promise((resolve, reject) => { @@ -120,17 +174,12 @@ export default { url: 'https://api.ipify.org?format=json', method: 'GET', success: (res) => { - // console.log("获取IP成功:", res.data); this.dataForm.ip = res.data.ip; - // console.log("登录参数:", this.dataForm); - resolve(res.data.ip); // 成功时解析Promise + resolve(res.data.ip); }, fail: (err) => { - // console.log("获取IP失败:", err); - // 获取IP失败时,将ip设为空字符串并继续执行 this.dataForm.ip = ''; - // console.log("登录参数:", this.dataForm); - resolve(''); // 解析Promise,继续执行后续操作 + resolve(''); } }); }); @@ -141,7 +190,12 @@ export default { } }, - onLoad() { + onLoad(options) { + // 在onLoad中直接获取options中的token + if (options.token) { + this.dataForm.uniqueToken = options.token; + console.log("onLoad获取到token:", this.dataForm.uniqueToken); + } // 延迟执行,彻底避开双加载 setTimeout(() => { @@ -153,182 +207,4 @@ export default { } } - - - \ No newline at end of file + \ No newline at end of file diff --git a/im-web/src/components/setting/Setting.vue b/im-web/src/components/setting/Setting.vue index 57a5729..ea907be 100644 --- a/im-web/src/components/setting/Setting.vue +++ b/im-web/src/components/setting/Setting.vue @@ -1,3 +1,5 @@ + +