Browse Source

修改每个客服一个链接 token后面拼kefuuid与客服回答

master
[yxf] 4 weeks ago
parent
commit
3b211e2585
  1. 3
      im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java
  2. 4
      im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java
  3. 82
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  4. 64
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  5. 168
      im-uniapp/pages/chat/chat-box.vue
  6. 38
      im-uniapp/pages/login/login.vue

3
im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java

@ -34,4 +34,7 @@ public class LoginDTO {
@Schema(description = "token标识")
private String uniqueToken;
@Schema(description = "客服ID")
private String keFuId;
}

4
im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java

@ -13,6 +13,9 @@ public class PrivateMessageDTO {
@Schema(description = "临时id")
private String tmpId;
@Schema(description = "发送用户id(可选,不传则使用当前登录用户)")
private Long sendId; // 新增字段
@NotNull(message = "接收用户id不可为空")
@Schema(description = "接收用户id")
private Long recvId;
@ -26,4 +29,5 @@ public class PrivateMessageDTO {
@Schema(description = "消息类型 0:文字 1:图片 2:文件 3:语音 4:视频")
private Integer type;
}

82
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -51,44 +51,106 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
private final UserMapper userMapper;
private static final ScheduledThreadPoolExecutor EXECUTOR = ThreadPoolExecutorFactory.getThreadPoolExecutor();
// @Override
// public PrivateMessageVO sendMessage(PrivateMessageDTO dto) {
// UserSession session = SessionContext.getSession();
// Long sendUserId = session.getUserId();
// Long recvUserId = dto.getRecvId();
// Boolean isFriends = friendService.isFriend(session.getUserId(), dto.getRecvId());
// if (Boolean.FALSE.equals(isFriends)) {
// throw new GlobalException("您已不是对方好友,无法发送消息");
// }
// User sendUser = userMapper.selectById(sendUserId);
// User recvUser = userMapper.selectById(recvUserId);
// // 打印发送用户和接收用户信息
// log.info("发送私聊消息 - 发送用户ID: {}, 接收用户ID: {}",
// recvUser, sendUser);
//
// if(!recvUser.getUniqueToken().equals(sendUser.getUniqueToken())){
// throw new GlobalException("非法客服,发送失败");
// }
// // 保存消息
// PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
// msg.setSendId(session.getUserId());
// msg.setStatus(MessageStatus.PENDING.code());
// msg.setSendTime(new Date());
// // 过滤内容中的敏感词
// if (MessageType.TEXT.code().equals(dto.getType())) {
// msg.setContent(sensitiveFilterUtil.filter(dto.getContent()));
// }
// this.save(msg);
// // 推送消息
// PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
// IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
// sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
// sendMessage.setRecvId(msgInfo.getRecvId());
// sendMessage.setSendToSelf(true);
// sendMessage.setData(msgInfo);
// sendMessage.setSendResult(true);
// imClient.sendPrivateMessage(sendMessage);
// log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), dto.getRecvId(), dto.getContent());
// return msgInfo;
// }
@Override
public PrivateMessageVO sendMessage(PrivateMessageDTO dto) {
UserSession session = SessionContext.getSession();
Long sendUserId = session.getUserId();
// 如果传了sendId就用传入的,否则用当前登录用户
Long sendUserId = dto.getSendId() != null ? dto.getSendId() : session.getUserId();
Long recvUserId = dto.getRecvId();
Boolean isFriends = friendService.isFriend(session.getUserId(), dto.getRecvId());
// 如果不是自己发送(模拟发送),需要验证权限
if (!sendUserId.equals(session.getUserId())) {
User sendUser = userMapper.selectById(sendUserId);
User currentUser = userMapper.selectById(session.getUserId());
// 验证是否属于同一商户
if (sendUser == null || !sendUser.getUniqueToken().equals(currentUser.getUniqueToken())) {
throw new GlobalException("无权限模拟该用户发送消息");
}
log.info("模拟发送消息 - 操作人:{}, 发送者:{}, 接收者:{}",
session.getUserId(), sendUserId, recvUserId);
}
Boolean isFriends = friendService.isFriend(sendUserId, recvUserId);
if (Boolean.FALSE.equals(isFriends)) {
throw new GlobalException("您已不是对方好友,无法发送消息");
}
User sendUser = userMapper.selectById(sendUserId);
User recvUser = userMapper.selectById(recvUserId);
// 打印发送用户和接收用户信息
log.info("发送私聊消息 - 发送用户ID: {}, 接收用户ID: {}",
recvUser, sendUser);
log.info("发送私聊消息 - 发送用户ID: {}, 接收用户ID: {}", sendUserId, recvUserId);
if(!recvUser.getUniqueToken().equals(sendUser.getUniqueToken())){
throw new GlobalException("非法客服,发送失败");
}
// 保存消息
// 保存消息(后面的代码保持不变)
PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
msg.setSendId(session.getUserId());
msg.setSendId(sendUserId); // 使用处理后的发送者ID
msg.setStatus(MessageStatus.PENDING.code());
msg.setSendTime(new Date());
// 过滤内容中的敏感词
if (MessageType.TEXT.code().equals(dto.getType())) {
msg.setContent(sensitiveFilterUtil.filter(dto.getContent()));
}
this.save(msg);
// 推送消息
PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setSender(new IMUserInfo(sendUserId, session.getTerminal()));
sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(true);
// 如果是模拟发送,不发送给自己
sendMessage.setSendToSelf(sendUserId.equals(session.getUserId()));
sendMessage.setData(msgInfo);
sendMessage.setSendResult(true);
imClient.sendPrivateMessage(sendMessage);
log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), dto.getRecvId(), dto.getContent());
log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", sendUserId, recvUserId, dto.getContent());
return msgInfo;
}

64
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -95,7 +95,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
@Override
public LoginVO login(LoginDTO dto) {
log.info("【测试】前端传的uniqueToken:{}", dto.getUniqueToken());
log.info("【测试】前端传的uniqueToken:{},kefuid:{}", dto.getUniqueToken(), dto.getKeFuId());
String uniqueToken = dto.getUniqueToken();
// 检查uniqueToken是否为空或已过期
@ -122,7 +122,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
// 保存到数据库
this.save(guestUser);
Long customerServiceId = this.getCustomerServiceIdByUniqueToken(dto.getUniqueToken());
// Long customerServiceId = this.getCustomerServiceIdByUniqueToken(dto);
Long customerServiceId = this.getCustomerServiceId(dto);
UserSession guestSession = new UserSession();
guestSession.setUserId(guestUser.getId());
@ -158,28 +159,71 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return vo;
}
/**
* 获取客服ID
* 优先使用前端传入的kefuid没有则通过uniqueToken查询
*/
private Long getCustomerServiceId(LoginDTO dto) {
// 1. 如果前端传了 kefuid,直接使用并验证该客服是否有效
if (StrUtil.isNotBlank(dto.getKeFuId())) {
try {
Long kefuid = Long.parseLong(dto.getKeFuId());
// 验证该客服是否存在且有效
User customer = this.getById(kefuid);
if (customer != null && customer.getIsCustomer() == 2) {
// 验证该客服的uniqueToken是否与传入的匹配
if (dto.getUniqueToken().equals(customer.getUniqueToken())) {
return kefuid;
} else {
log.warn("前端传入的客服ID {} 的uniqueToken与当前不匹配,将使用token查询", kefuid);
}
} else {
log.warn("前端传入的客服ID {} 无效或不是客服,将使用token查询", kefuid);
}
} catch (NumberFormatException e) {
log.warn("前端传入的kefuid格式错误:{}", dto.getKeFuId());
}
}
// 2. 通过uniqueToken查询客服
return getCustomerServiceIdByUniqueToken(dto.getUniqueToken());
}
/**
* 通过uniqueToken查询客服ID如果有多条匹配则随机返回一条
*/
public Long getCustomerServiceIdByUniqueToken(String uniqueToken) {
// 1. 构建查询条件
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(User::getIsCustomer, 2); // 只查客服
// 2. 有 token 才按 token 匹配
if (StrUtil.isNotBlank(uniqueToken)) {
queryWrapper.eq(User::getUniqueToken, uniqueToken);
}
// 3. 只查 ID
queryWrapper.select(User::getId);
// 4. 查一条(false=查不到不抛异常)
User customer = this.getOne(queryWrapper, false);
if (customer == null) {
// 2. 查询所有匹配的客服
List<User> customerList = this.list(queryWrapper);
// 3. 如果没有匹配的客服,抛出异常
if (customerList == null || customerList.isEmpty()) {
throw new GlobalException("未找到对应的客服,请检查 uniqueToken 是否正确");
}
// 5. 有客服返回ID,没有返回null
return customer == null ? null : customer.getId();
}
// 4. 如果只有一条,直接返回
if (customerList.size() == 1) {
Long customerId = customerList.get(0).getId();
return customerId;
}
// 5. 如果有多条,随机选择一条返回
int randomIndex = new Random().nextInt(customerList.size());
Long randomCustomerId = customerList.get(randomIndex).getId();
return randomCustomerId;
}
@Override
public LoginVO loginCustom(LoginDTO dto) {

168
im-uniapp/pages/chat/chat-box.vue

@ -298,50 +298,160 @@ export default {
return;
}
let msgInfo = {
// ========== replyTitle ==========
let userMsgInfo = {
tmpId: this.generateId(),
receipt: this.isReceipt,
type: this.$enums.MESSAGE_TYPE.TEXT,
content: autoReply.replyTitle,
receipt: this.isReceipt
};
if (autoReply.replyType === 0) {
//
msgInfo.type = this.$enums.MESSAGE_TYPE.TEXT;
msgInfo.content = autoReply.replyContent;
} else if (autoReply.replyType === 1) {
//
msgInfo.type = this.$enums.MESSAGE_TYPE.IMAGE;
msgInfo.content = JSON.stringify({
originUrl: autoReply.replyContent,
thumbUrl: autoReply.replyContent,
});
} else {
return;
}
this.fillTargetId(msgInfo, this.chat.targetId);
this.fillTargetId(userMsgInfo, this.chat.targetId);
const chat = this.chat;
if (!chat) return;
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
let tmpUserMessage = this.buildTmpMessage(userMsgInfo);
this.chatStore.insertMessage(tmpUserMessage, chat);
this.moveChatToTop();
this.sendMessageRequest(msgInfo)
//
this.sendMessageRequest(userMsgInfo)
.then((m) => {
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.id = m.id;
tmpMessage.status = m.status;
this.chatStore.updateMessage(tmpMessage, chat);
tmpUserMessage = JSON.parse(JSON.stringify(tmpUserMessage));
tmpUserMessage.id = m.id;
tmpUserMessage.status = m.status;
this.chatStore.updateMessage(tmpUserMessage, chat);
// ========== ==========
this.triggerAutoReply(autoReply);
this.scrollToBottom();
this.isReceipt = false;
})
.catch(() => {
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.updateMessage(tmpMessage, chat);
tmpUserMessage = JSON.parse(JSON.stringify(tmpUserMessage));
tmpUserMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.updateMessage(tmpUserMessage, chat);
});
},
//
triggerAutoReply(autoReply) {
// ID
let tmpId = this.generateId();
//
let replyMsgInfo = {
tmpId: tmpId, // tmpId
sendId: this.chat.targetId, // ID
recvId: this.mine.id, //
type: autoReply.replyType, //
receipt: false
};
//
if (autoReply.replyType === 0) {
//
replyMsgInfo.content = autoReply.replyContent;
} else if (autoReply.replyType === 1) {
//
replyMsgInfo.content = JSON.stringify({
originUrl: autoReply.replyContent,
thumbUrl: autoReply.replyContent,
});
}
// ""
let typingMessage = {
id: this.generateId(),
tmpId: tmpId,
sendId: this.chat.targetId,
recvId: this.mine.id,
selfSend: false,
sendTime: new Date().getTime(),
type: this.$enums.MESSAGE_TYPE.TIP_TEXT,
content: "客服正在输入...",
status: this.$enums.MESSAGE_STATUS.SENDING
};
this.chatStore.insertMessage(typingMessage, this.chat);
//
this.$http({
url: "/message/private/send",
method: "post",
data: replyMsgInfo
}).then(res => {
console.log("自动回复发送成功", res);
// ""
this.chatStore.deleteMessage(typingMessage, this.chat);
// WebSocket
//
if (res && res.id) {
let replyMessage = {
id: res.id,
tmpId: tmpId,
sendId: this.chat.targetId,
recvId: this.mine.id,
selfSend: false,
sendTime: new Date().getTime(),
type: autoReply.replyType,
content: replyMsgInfo.content,
status: this.$enums.MESSAGE_STATUS.SENDED
};
this.chatStore.insertMessage(replyMessage, this.chat);
this.scrollToBottom();
}
}).catch(err => {
console.error("自动回复发送失败", err);
// ""
this.chatStore.deleteMessage(typingMessage, this.chat);
//
uni.showToast({
title: "自动回复失败",
icon: "none"
});
});
},
//
// triggerAutoReply(autoReply) {
// // sendId
// let replyMsgInfo = {
// sendId: this.chat.targetId, // ID
// recvId: this.mine.id, //
// type: autoReply.replyType, //
// };
// console.log(replyMsgInfo)
// //
// if (autoReply.replyType === 0) {
// //
// replyMsgInfo.content = autoReply.replyContent;
// } else if (autoReply.replyType === 1) {
// //
// replyMsgInfo.content = JSON.stringify({
// originUrl: autoReply.replyContent,
// thumbUrl: autoReply.replyContent,
// });
// }
// //
// this.$http({
// url: "/message/private/send",
// method: "post",
// data: replyMsgInfo
// }).then(res => {
// console.log("", res);
// // WebSocket
// }).catch(err => {
// console.error("", err);
// });
// },
onRecorderInput() {
this.showRecord = true;
this.switchChatTabBox("none");
@ -1070,6 +1180,7 @@ export default {
sendMessageRequest(msgInfo) {
return new Promise((resolve, reject) => {
// ""
this.reqQueue.push({ msgInfo, resolve, reject });
this.processReqQueue();
});
@ -1078,6 +1189,7 @@ export default {
if (this.reqQueue.length && !this.isSending) {
this.isSending = true;
const reqData = this.reqQueue.shift();
console.log(reqData.msgInfo);
this.$http({
url: this.messageAction,
method: "post",

38
im-uniapp/pages/login/login.vue

@ -20,7 +20,8 @@ export default {
password: '',
ip: '',
sourceUrl: '',
uniqueToken: '' // token
uniqueToken: '' ,// token
kefuid: '' // ID
}
}
},
@ -55,10 +56,9 @@ export default {
console.log("来源网址:", this.dataForm.sourceUrl);
},
// URLtoken
// URLtokenkefuid
getTokenFromUrl() {
// #ifdef H5
// 1options
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const options = currentPage.options || {};
@ -66,16 +66,26 @@ export default {
if (options.token) {
this.dataForm.uniqueToken = options.token;
console.log("从options获取到token:", this.dataForm.uniqueToken);
return;
}
// 2URL
if (options.kefuid) {
this.dataForm.kefuid = options.kefuid;
console.log("从options获取到kefuid:", this.dataForm.kefuid);
}
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
const kefuid = urlParams.get('kefuid');
if (token) {
this.dataForm.uniqueToken = token;
console.log("从URL解析获取到token:", this.dataForm.uniqueToken);
}
if (kefuid) {
this.dataForm.kefuid = kefuid;
console.log("从URL解析获取到kefuid:", this.dataForm.kefuid);
}
// #endif
// #ifdef APP-PLUS
@ -85,7 +95,9 @@ export default {
const options = currentPage.options || {};
if (options.token) {
this.dataForm.uniqueToken = options.token;
console.log("App端获取到token:", this.dataForm.uniqueToken);
}
if (options.kefuid) {
this.dataForm.kefuid = options.kefuid;
}
// #endif
@ -96,7 +108,10 @@ export default {
const options = currentPage.options || {};
if (options.token) {
this.dataForm.uniqueToken = options.token;
console.log("小程序端获取到token:", this.dataForm.uniqueToken);
}
console.log(options);
if (options.kefuid) {
this.dataForm.kefuid = options.kefuid;
}
// #endif
},
@ -138,10 +153,9 @@ export default {
ip: this.dataForm.ip,
sourceUrl: this.dataForm.sourceUrl,
uniqueToken: this.dataForm.uniqueToken,
keFuId: this.dataForm.kefuid, // kefuid
};
console.log("登录参数:", loginData);
this.$http({
url: '/login',
data: loginData,
@ -152,7 +166,6 @@ export default {
getApp().$vm.init()
getApp().$vm.unloadStore();
console.log(loginInfo.customerServiceId);
this.$http({
url: "/friend/add?friendId=" + loginInfo.customerServiceId,
method: "POST"
@ -227,7 +240,10 @@ export default {
this.dataForm.uniqueToken = options.token;
console.log("onLoad获取到token:", this.dataForm.uniqueToken);
}
if (options.kefuid) {
this.dataForm.kefuid = options.kefuid;
console.log("onLoad获取到kefuid:", this.dataForm.kefuid);
}
//
setTimeout(() => {
this.autoLogin();

Loading…
Cancel
Save