Browse Source

提交新增客服端聊天页面用户信息侧边栏功能

master
[yxf] 1 month ago
parent
commit
f2f3f78b81
  1. 9
      im-platform/src/main/java/com/bx/implatform/entity/User.java
  2. 25
      im-platform/src/main/java/com/bx/implatform/entity/UserGroup.java
  3. 26
      im-platform/src/main/java/com/bx/implatform/entity/UserLabel.java
  4. 9
      im-platform/src/main/java/com/bx/implatform/mapper/UserGroupMapper.java
  5. 9
      im-platform/src/main/java/com/bx/implatform/mapper/UserLabelMapper.java
  6. 13
      im-platform/src/main/java/com/bx/implatform/service/UserGroupService.java
  7. 13
      im-platform/src/main/java/com/bx/implatform/service/UserLabelService.java
  8. 46
      im-platform/src/main/java/com/bx/implatform/service/impl/UserGroupServiceImpl.java
  9. 46
      im-platform/src/main/java/com/bx/implatform/service/impl/UserLabelServiceImpl.java
  10. 27
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  11. 19
      im-platform/src/main/java/com/bx/implatform/vo/UserVO.java
  12. 128
      im-web/src/components/chat/ChatBox.vue
  13. 6
      im-web/src/components/setting/Setting.vue

9
im-platform/src/main/java/com/bx/implatform/entity/User.java

@ -114,5 +114,14 @@ public class User {
*/
private String uniqueToken;
/**
* 标签ID
*/
private String labelIds;
/**
* 分组ID
*/
private String groupIds;
}

25
im-platform/src/main/java/com/bx/implatform/entity/UserGroup.java

@ -0,0 +1,25 @@
package com.bx.implatform.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* <p>
* 标签分组
* </p>
*
* @author blue
* @since 2022-10-01
*/
@Data
@TableName("im_user_group")
public class UserGroup {
@TableId(type = IdType.AUTO)
private Long id;
private String groupName;
}

26
im-platform/src/main/java/com/bx/implatform/entity/UserLabel.java

@ -0,0 +1,26 @@
package com.bx.implatform.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
/**
* <p>
* 标签分组
* </p>
*
* @author blue
* @since 2022-10-01
*/
@Data
@TableName("im_user_label")
public class UserLabel {
@TableId(type = IdType.AUTO)
private Long id;
private String labelName;
}

9
im-platform/src/main/java/com/bx/implatform/mapper/UserGroupMapper.java

@ -0,0 +1,9 @@
package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.UserGroup;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserGroupMapper extends BaseMapper<UserGroup> {
}

9
im-platform/src/main/java/com/bx/implatform/mapper/UserLabelMapper.java

@ -0,0 +1,9 @@
package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.UserLabel;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserLabelMapper extends BaseMapper<UserLabel> {
}

13
im-platform/src/main/java/com/bx/implatform/service/UserGroupService.java

@ -0,0 +1,13 @@
package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.implatform.entity.UserGroup;
import java.util.List;
public interface UserGroupService extends IService<UserGroup> {
/**
* 根据分组ID列表获取分组名称列表
*/
List<String> getGroupNamesByIds(String groupIds);
}

13
im-platform/src/main/java/com/bx/implatform/service/UserLabelService.java

@ -0,0 +1,13 @@
package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.implatform.entity.UserLabel;
import java.util.List;
public interface UserLabelService extends IService<UserLabel> {
/**
* 根据标签ID列表获取标签名称列表
*/
List<String> getLabelNamesByIds(String labelIds);
}

46
im-platform/src/main/java/com/bx/implatform/service/impl/UserGroupServiceImpl.java

@ -0,0 +1,46 @@
package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.implatform.entity.UserGroup;
import com.bx.implatform.mapper.UserGroupMapper;
import com.bx.implatform.service.UserGroupService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserGroupServiceImpl extends ServiceImpl<UserGroupMapper, UserGroup> implements UserGroupService {
@Override
public List<String> getGroupNamesByIds(String groupIds) {
if (!StringUtils.hasText(groupIds)) {
return Collections.emptyList();
}
// 将逗号分隔的ID字符串转换为Long列表
List<Long> ids = Arrays.stream(groupIds.split(","))
.map(String::trim)
.filter(StringUtils::hasText)
.map(Long::parseLong)
.collect(Collectors.toList());
if (ids.isEmpty()) {
return Collections.emptyList();
}
// 查询标签
LambdaQueryWrapper<UserGroup> wrapper = new LambdaQueryWrapper<>();
wrapper.in(UserGroup::getId, ids);
List<UserGroup> groups = this.list(wrapper);
// 返回标签名称列表
return groups.stream()
.map(UserGroup::getGroupName)
.collect(Collectors.toList());
}
}

46
im-platform/src/main/java/com/bx/implatform/service/impl/UserLabelServiceImpl.java

@ -0,0 +1,46 @@
package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.implatform.entity.UserLabel;
import com.bx.implatform.mapper.UserLabelMapper;
import com.bx.implatform.service.UserLabelService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserLabelServiceImpl extends ServiceImpl<UserLabelMapper, UserLabel> implements UserLabelService {
@Override
public List<String> getLabelNamesByIds(String labelIds) {
if (!StringUtils.hasText(labelIds)) {
return Collections.emptyList();
}
// 将逗号分隔的ID字符串转换为Long列表
List<Long> ids = Arrays.stream(labelIds.split(","))
.map(String::trim)
.filter(StringUtils::hasText)
.map(Long::parseLong)
.collect(Collectors.toList());
if (ids.isEmpty()) {
return Collections.emptyList();
}
// 查询标签
LambdaQueryWrapper<UserLabel> wrapper = new LambdaQueryWrapper<>();
wrapper.in(UserLabel::getId, ids);
List<UserLabel> labels = this.list(wrapper);
// 返回标签名称列表
return labels.stream()
.map(UserLabel::getLabelName)
.collect(Collectors.toList());
}
}

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

@ -22,10 +22,7 @@ import com.bx.implatform.enums.ResultCode;
import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.mapper.UserMapper;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.FriendService;
import com.bx.implatform.service.GroupMemberService;
import com.bx.implatform.service.PrivateMessageService;
import com.bx.implatform.service.UserService;
import com.bx.implatform.service.*;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
@ -36,9 +33,11 @@ import com.bx.implatform.vo.OnlineTerminalVO;
import com.bx.implatform.vo.UserVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
@ -57,6 +56,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
private final IMClient imClient;
private final SensitiveFilterUtil sensitiveFilterUtil;
private final PrivateMessageService privateMessageService;
@Autowired
private UserLabelService userLabelService;
@Autowired
private UserGroupService UserGroupService;
// @Override
// public LoginVO login(LoginDTO dto) {
@ -317,8 +320,24 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
@Override
public UserVO findUserById(Long id) {
User user = this.getById(id);
if (user == null) {
return null;
}
UserVO vo = BeanUtils.copyProperties(user, UserVO.class);
vo.setOnline(imClient.isOnline(id));
// 转换标签ID为标签名称
if (StringUtils.hasText(user.getLabelIds())) {
List<String> labelNames = userLabelService.getLabelNamesByIds(user.getLabelIds());
vo.setLabelNames(labelNames);
}
if (StringUtils.hasText(user.getGroupIds())) {
List<String> groupNames = UserGroupService.getGroupNamesByIds(user.getGroupIds());
vo.setGroupNames(groupNames);
}
return vo;
}

19
im-platform/src/main/java/com/bx/implatform/vo/UserVO.java

@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import java.util.List;
@Data
@Schema(description = "用户信息VO")
@ -48,4 +49,20 @@ public class UserVO {
@Schema(description = "被封禁原因")
private String reason;
}
@Schema(description = "用户IP")
private String lastLoginIp;
@Schema(description = "来源地址")
private String sourceUrl;
@Schema(description = "标签ID列表")
private String labelIds;
@Schema(description = "标签名称列表")
private List<String> labelNames;
@Schema(description = "分组名称列表")
private List<String> groupNames;
}

128
im-web/src/components/chat/ChatBox.vue

@ -4,6 +4,7 @@
<el-header height="50px">
<span>{{ title }}</span>
<span title="群聊信息" v-show="isGroup" class="btn-side el-icon-more" @click="showSide = !showSide"></span>
<span title="用户信息" v-show="isPrivate" class="btn-side el-icon-more" @click="showUserSide = !showUserSide"></span>
</el-header>
<el-main style="padding: 0;">
<el-container>
@ -72,6 +73,61 @@
<chat-group-side :group="group" :groupMembers="groupMembers" @reload="loadGroup(group.id)">
</chat-group-side>
</el-aside>
<el-aside class="side-box user-info-side" width="320px" v-if="showUserSide">
<div class="user-info-container">
<div class="user-info-header">
<img :src="userInfo.headImage || userInfo.headImageThumb" class="user-avatar-large" />
<h3 class="user-nickname">{{ userInfo.nickName }}</h3>
<p class="user-username">@{{ userInfo.userName }}</p>
</div>
<div class="user-info-content">
<div class="info-item">
<div class="info-label">用户ID</div>
<div class="info-value">{{ userInfo.id }}</div>
</div>
<div class="info-item" v-if="userInfo.lastLoginIp">
<div class="info-label">用户IP</div>
<div class="info-value">{{ userInfo.lastLoginIp }}</div>
</div>
<div class="info-item" v-if="userInfo.sourceUrl">
<div class="info-label">来源地址</div>
<div class="info-value">{{ userInfo.sourceUrl }}</div>
</div>
<div class="info-item" v-if="userInfo.sex !== undefined && userInfo.sex !== null">
<div class="info-label">性别</div>
<div class="info-value">{{ userInfo.sex === 1 ? '女' : '男' }}</div>
</div>
<!-- 添加标签信息 -->
<div class="info-item" v-if="userInfo.labelNames && userInfo.labelNames.length > 0">
<div class="info-label">标签</div>
<div class="info-value">
<el-tag
v-for="(label, index) in userInfo.labelNames"
:key="index"
size="small"
style="margin-right: 5px; margin-bottom: 5px;"
>
{{ label }}
</el-tag>
</div>
</div>
<!-- 添加群组信息 -->
<div class="info-item" v-if="userInfo.groupNames && userInfo.groupNames.length > 0">
<div class="info-label">群组</div>
<div class="info-value">
<el-tag
v-for="(group, index) in userInfo.groupNames"
:key="index"
size="small"
style="margin-right: 5px; margin-bottom: 5px;"
>
{{ group }}
</el-tag>
</div>
</div>
</div>
</div>
</el-aside>
</el-container>
</el-main>
<emotion ref="emoBox" @emotion="onEmotion"></Emotion>
@ -130,6 +186,7 @@ export default {
isReceipt: true,
showRecord: false, //
showSide: false, //
showUserSide: false, //
showHistory: false, //
showChangeCustomer: false, //
lockMessage: false, //
@ -280,6 +337,7 @@ export default {
},
onCloseSide() {
this.showSide = false;
this.showUserSide = false;
},
onScrollToTop() {
// 10
@ -631,9 +689,11 @@ export default {
method: 'GET'
}).then((userInfo) => {
this.userInfo = userInfo;
console.log(this.userInfo);
this.updateFriendInfo();
})
},
showName(msgInfo) {
if (!msgInfo) {
return ""
@ -843,6 +903,7 @@ export default {
//
this.scrollToBottom();
this.showSide = false;
this.showUserSide = false;
//
this.readedMessage()
// 30
@ -1010,5 +1071,72 @@ export default {
border-left: var(--im-border);
}
.user-info-side {
.user-info-container {
padding: 20px;
height: 100%;
overflow-y: auto;
.user-info-header {
text-align: center;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
.user-avatar-large {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
margin-bottom: 15px;
}
.user-nickname {
font-size: 18px;
font-weight: 600;
margin: 10px 0 5px;
color: #333;
}
.user-username {
font-size: 14px;
color: #999;
margin: 0;
}
}
.user-info-content {
padding: 20px 0;
.info-item {
display: flex;
padding: 10px 0;
border-bottom: 1px solid #f0f0f0;
.info-label {
width: 70px;
font-size: 14px;
color: #999;
flex-shrink: 0;
}
.info-value {
flex: 1;
font-size: 14px;
color: #333;
word-break: break-all;
/* 标签容器样式 */
.el-tag {
margin-right: 8px;
margin-bottom: 8px;
&:last-child {
margin-right: 0;
}
}
}
}
}
}
}
}
</style>

6
im-web/src/components/setting/Setting.vue

@ -246,9 +246,9 @@ export default {
});
const loading = this.$loading({
lock: true,
text: '正在切换账号...',
spinner: 'el-icon-loading'
lock: true,
text: '正在切换账号...',
spinner: 'el-icon-loading'
});
const res = await this.$http({

Loading…
Cancel
Save