This commit is contained in:
cjh 2025-03-04 09:54:15 +08:00
parent de4cc4d73f
commit c79086cf5e
50 changed files with 2156 additions and 666 deletions

View File

@ -73,9 +73,9 @@ spring:
default-datasource: default-datasource:
type: com.alibaba.druid.pool.DruidDataSource type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai url: jdbc:mysql://82.156.121.2:23306/shop_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root username: wzj
password: lilishop password: A085F27A43B0
maxActive: 50 maxActive: 50
initialSize: 10 initialSize: 10
maxWait: 60000 maxWait: 60000

View File

@ -116,10 +116,10 @@
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
</dependency> </dependency>
<!-- &lt;!&ndash; Websocket &ndash;&gt;--> <!-- &lt;!&ndash; Websocket &ndash;&gt;-->
<!-- <dependency>--> <dependency>
<!-- <groupId>org.springframework.boot</groupId>--> <groupId>org.springframework.boot</groupId>
<!-- <artifactId>spring-boot-starter-websocket</artifactId>--> <artifactId>spring-boot-starter-websocket</artifactId>
<!-- </dependency>--> </dependency>
<!-- MybatisPlus --> <!-- MybatisPlus -->
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>

View File

@ -1,12 +1,20 @@
package cn.lili.common.vo; package cn.lili.common.vo;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.utils.StringUtils;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
/**
* 分页视图对象
* @author Chopper
*/
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.utils.StringUtils;
/** /**
* 查询参数 * 查询参数
* *
@ -42,5 +50,5 @@ public class PageVO implements Serializable {
} }
return sort; return sort;
} }
} }

View File

@ -1,6 +1,6 @@
package cn.lili.common.vo; package cn.lili.common.vo;
import cn.lili.common.enums.ResultCode;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@ -39,4 +39,81 @@ public class ResultMessage<T> implements Serializable {
* 结果对象 * 结果对象
*/ */
private T result; private T result;
/**
* 构造空参对象
*/
public ResultMessage() {
}
/**
* 构建消息对象
* @param success 是否成功
* @param code 状态码
* @param message 消息
* @param result 结果
*/
public ResultMessage(boolean success, Integer code, String message, T result) {
this.success = success;
this.code = code;
this.message = message;
this.result = result;
}
/**
* 构建成功消息
* @param result 结果
* @return 消息对象
*/
public static <T> ResultMessage<T> success(T result) {
return new ResultMessage<>(true, ResultCode.SUCCESS.code(), ResultCode.SUCCESS.message(), result);
}
/**
* 构建成功消息
* @param message 消息
* @param result 结果
* @return 消息对象
*/
public static <T> ResultMessage<T> success(String message, T result) {
return new ResultMessage<>(true, ResultCode.SUCCESS.code(), message, result);
}
/**
* 构建错误消息
* @param code 错误码
* @return 消息对象
*/
public static <T> ResultMessage<T> error(Integer code) {
return new ResultMessage<>(false, code, null, null);
}
/**
* 构建错误消息
* @param code 错误码
* @param message 错误消息
* @return 消息对象
*/
public static <T> ResultMessage<T> error(Integer code, String message) {
return new ResultMessage<>(false, code, message, null);
}
/**
* 构建错误消息
* @param resultCode 结果枚举
* @return 消息对象
*/
public static <T> ResultMessage<T> error(ResultCode resultCode) {
return new ResultMessage<>(false, resultCode.code(), resultCode.message(), null);
}
/**
* 构建错误消息
* @param resultCode 结果枚举
* @param message 错误消息
* @return 消息对象
*/
public static <T> ResultMessage<T> error(ResultCode resultCode, String message) {
return new ResultMessage<>(false, resultCode.code(), message, null);
}
} }

View File

@ -0,0 +1,46 @@
package cn.lili.common.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 分页结果视图对象
* @author Chopper
*/
@Data
public class ResultPageVO<T> implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "总条数")
private Long total;
@ApiModelProperty(value = "当前页数")
private Long currentPage;
@ApiModelProperty(value = "总页数")
private Long pages;
@ApiModelProperty(value = "每页显示的条数")
private Long size;
@ApiModelProperty(value = "数据列表")
private List<T> records;
public ResultPageVO() {
}
public ResultPageVO(Long total, Long pages, Long currentPage, Long size, List<T> records) {
this.total = total;
this.pages = pages;
this.currentPage = currentPage;
this.size = size;
this.records = records;
}
public static <T> ResultPageVO<T> empty() {
return new ResultPageVO<>(0L, 0L, 1L, 10L, null);
}
}

View File

@ -1,10 +1,10 @@
package cn.lili.modules.im.config; package cn.lili.modules.im.config;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.websocket.server.ServerEndpointConfig; import javax.websocket.server.ServerEndpointConfig;
@ -15,6 +15,7 @@ import javax.websocket.server.ServerEndpointConfig;
* @version v1.0 * @version v1.0
* 2021-12-31 11:53 * 2021-12-31 11:53
*/ */
@Component
public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware { public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
/** /**
@ -22,13 +23,13 @@ public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator
*/ */
private static volatile BeanFactory context; private static volatile BeanFactory context;
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
return context.getBean(clazz);
}
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CustomSpringConfigurator.context = applicationContext; CustomSpringConfigurator.context = applicationContext;
} }
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
return context.getBean(clazz);
}
} }

View File

@ -0,0 +1,14 @@
package cn.lili.modules.im.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@ -1,24 +0,0 @@
package cn.lili.modules.im.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* WebSocketConfigurator
*
* @author Chopper
* @version v1.0
* 2021-12-31 11:53
*/
@ConditionalOnWebApplication
@Configuration
public class WebSocketConfigurator {
@Bean
public CustomSpringConfigurator customSpringConfigurator() {
// This is just to get context
return new CustomSpringConfigurator();
}
}

View File

@ -1,39 +1,82 @@
package cn.lili.modules.im.entity.dos; package cn.lili.modules.im.entity.dos;
import com.baomidou.mybatisplus.annotation.TableName; import cn.lili.common.vo.SerializableStream;
import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
@Data @Data
@TableName("li_friend") @TableName("li_friend")
public class Friend {
@ApiModel(value = "好友关系")
public class Friend implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID) @TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "ID")
private String id; private String id;
// 用户ID
@ApiModelProperty(value = "用户ID")
private String userId; private String userId;
// 好友ID
@ApiModelProperty(value = "好友ID")
private String friendId; private String friendId;
// 好友昵称
private String nickname;
@ApiModelProperty(value = "好友备注")
// 好友头像
private String avatar;
// 好友备注
private String remark; private String remark;
// 关系状态 (0:待确认 1:已确认 2:已拒绝 3:已解除)
@ApiModelProperty(value = "状态 1:已关注")
private Integer status; private Integer status;
// 创建时间
@ApiModelProperty(value = "是否是店铺 0:否 1:是")
private Integer storeFlag;
@ApiModelProperty(value = "创建时间")
private Date createTime; private Date createTime;
// 更新时间
@ApiModelProperty(value = "更新时间")
private Date updateTime; private Date updateTime;
} @ApiModelProperty(value = "是否关注")
private Integer isMutual;
}

View File

@ -1,38 +1,51 @@
package cn.lili.modules.im.entity.dos; package cn.lili.modules.im.entity.dos;
import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType; import io.swagger.annotations.ApiModel;
import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
@Data @Data
@TableName("li_im_group_member") @TableName("li_im_group_member")
public class ImGroupMember { @ApiModel(value = "群成员")
@TableId(type = IdType.ASSIGN_ID) public class ImGroupMember extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "成员ID")
private String id; private String id;
// 群ID @ApiModelProperty(value = "群ID")
private String groupId; private String groupId;
// 成员ID @ApiModelProperty(value = "成员ID")
private String memberId; private String memberId;
// 成员昵称 @ApiModelProperty(value = "成员昵称")
private String nickname; private String nickname;
// 成员角色(0:普通成员 1:管理员 2:群主) @ApiModelProperty(value = "角色(0:普通成员 1:管理员 2:群主)")
private Integer role; private Integer role;
// 是否被禁言(0: 1:) @ApiModelProperty(value = "是否被禁言")
private Integer isMuted; private int isMuted;
// 禁言结束时间 @ApiModelProperty(value = "禁言结束时间")
private Date muteEndTime; private Date muteEndTime;
// 加入时间 @ApiModelProperty(value = "加入时间")
private Date joinTime; private Date joinTime;
// 更新时间 @ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "更新时间")
private Date updateTime; private Date updateTime;
}
@ApiModelProperty(value = "删除标志")
private Boolean deleteFlag;
}

View File

@ -0,0 +1,28 @@
package cn.lili.modules.im.entity.dos;
import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("li_im_member")
@ApiModel(value = "IM用户")
public class ImMember extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "头像")
private String avatar;
// 其他需要的字段...
}

View File

@ -4,8 +4,10 @@ import cn.lili.common.utils.SnowFlake;
import cn.lili.modules.im.entity.enums.MessageTypeEnum; import cn.lili.modules.im.entity.enums.MessageTypeEnum;
import cn.lili.modules.im.entity.vo.MessageOperation; import cn.lili.modules.im.entity.vo.MessageOperation;
import cn.lili.mybatis.BaseEntity; import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -17,48 +19,83 @@ import java.util.Date;
*/ */
@Data @Data
@TableName("li_im_message") @TableName("li_im_message")
@ApiModel(value = "Im消息") @ApiModel(value = "即时通讯消息")
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ImMessage extends BaseEntity { public class ImMessage extends BaseEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@TableField("talk_id")
/** @ApiModelProperty(value = "会话ID")
* 发送者 private String talkId;
*/ @TableField("from_user")
@ApiModelProperty(value = "发送者ID")
private String fromUser; private String fromUser;
@TableField("to_user")
/** @ApiModelProperty(value = "接收者ID")
* 接收者
*/
private String toUser; private String toUser;
/** @ApiModelProperty(value = "消息内容")
* 已阅
*/
private Boolean isRead;
/**
* 消息类型
*/
private MessageTypeEnum messageType;
/**
* 聊天id
*/
private String talkId;
/**
* 消息实体
*/
private String text; private String text;
@ApiModelProperty(value = "消息类型")
private String type;
@TableField("message_type")
@ApiModelProperty(value = "聊天类型")
private MessageTypeEnum messageType;
@ApiModelProperty(value = "消息状态")
private Integer status;
@TableField("is_read")
@ApiModelProperty(value = "是否已读")
private Boolean isRead;
// @TableField("read_time")
// @ApiModelProperty(value = "阅读时间")
// private Date readTime; // 确保类型为 Date
// 文件相关字段
@TableField("file_name")
@ApiModelProperty(value = "文件名")
private String fileName;
@TableField("file_size")
@ApiModelProperty(value = "文件大小")
private Long fileSize;
@TableField("file_url")
@ApiModelProperty(value = "文件URL")
private String fileUrl;
// 图片相关字段
@TableField("image_url")
@ApiModelProperty(value = "图片URL")
private String imageUrl;
@TableField("image_width")
@ApiModelProperty(value = "图片宽度")
private Integer imageWidth;
@TableField("image_height")
@ApiModelProperty(value = "图片高度")
private Integer imageHeight;
@TableField("thumbnail_url")
@ApiModelProperty(value = "缩略图URL")
private String thumbnailUrl;
// 语音相关字段
@TableField("voice_url")
@ApiModelProperty(value = "语音URL")
private String voiceUrl;
@ApiModelProperty(value = "语音时长")
private Integer duration;
@ApiModelProperty(value = "额外信息")
private String extra;
private String groupId;
public ImMessage(MessageOperation messageOperation){ public ImMessage(MessageOperation messageOperation){
this.setFromUser(messageOperation.getFrom()); this.setFromUser(messageOperation.getFrom());
this.setMessageType(messageOperation.getMessageType()); this.setMessageType(messageOperation.getMessageType());
this.setIsRead(false); this.setIsRead(false);
// this.setReadTime(new Date());
this.setText(messageOperation.getContext()); this.setText(messageOperation.getContext());
this.setTalkId(messageOperation.getTalkId()); this.setTalkId(messageOperation.getTalkId());
this.setCreateTime(new Date()); this.setCreateTime(new Date());
@ -66,4 +103,11 @@ public class ImMessage extends BaseEntity {
this.setId(SnowFlake.getIdStr()); this.setId(SnowFlake.getIdStr());
} }
public void setMessageType(MessageTypeEnum messageType) {
this.messageType = messageType;
// if (messageType != null) {
// this.messageType = MessageTypeEnum.valueOf(messageType.getType()); // 确保type字段也使用大写
// }
}
} }

View File

@ -80,6 +80,8 @@ public class ImTalk extends BaseTenantEntity {
@ApiModelProperty(value = "坐席名称") @ApiModelProperty(value = "坐席名称")
private String tenantName; private String tenantName;
@ApiModelProperty(value = "是否关注")
private Integer isMutual;
public ImTalk(String userId1, String userId2, public ImTalk(String userId1, String userId2,
@ -135,6 +137,14 @@ public class ImTalk extends BaseTenantEntity {
this.name2 = member2.getNickName(); this.name2 = member2.getNickName();
} }
} }
// public ImTalk2(String userId1, String userId2, String face1, String face2, String name1, String name2) {
// this.userId1 = userId1;
// this.userId2 = userId2;
// this.face1 = face1;
// this.face2 = face2;
// this.name1 = name1;
// this.name2 = name2;
// }
public ImTalk(Member member, Store store){ public ImTalk(Member member, Store store){
if(Long.parseLong(member.getId()) > Long.parseLong(store.getId())){ if(Long.parseLong(member.getId()) > Long.parseLong(store.getId())){
this.userId1 = store.getId(); this.userId1 = store.getId();

View File

@ -0,0 +1,84 @@
package cn.lili.modules.im.entity.dos;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.util.Date;
@Data
@TableName("li_member")
public class Member {
@TableId(type = IdType.AUTO)
private Long id;
// 创建者
private String createBy;
// 创建时间
private Date createTime;
// 删除标志 true/false
private Boolean deleteFlag;
// 更新者
private String updateBy;
// 更新时间
private Date updateTime;
// 会员生日
private Date birthday;
// 会员状态
private Boolean disabled;
// 会员头像
private String face;
// 是否开通店铺
private Boolean haveStore;
// 手机号码
private String mobile;
// 会员昵称
private String nickName;
// 会员密码
private String password;
// 积分数量
private Long point;
// 会员性别
private Integer sex;
// 店铺ID
private String storeId;
// 会员用户名
private String username;
// 会员地址
private String region;
// 会员地址ID
private String regionId;
// 客户端
private String clientEnum;
// 最后一次登录时间
private Date lastLoginDate;
// 等级ID
private String gradeId;
// 经验值
private Long experience;
// 总积分
private Long totalPoint;
}

View File

@ -0,0 +1,44 @@
package cn.lili.modules.im.entity.dos;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("li_store") // 表名
public class Store {
@TableId
private Long id; // 主键 ID
private String createBy; // 创建者
private Date createTime; // 创建时间
private Boolean deleteFlag; // 删除标志
private String updateBy; // 更新者
private Date updateTime; // 更新时间
private String memberId; // 会员 ID
private String memberName; // 会员名称
private Boolean selfOperated; // 是否自营
private String storeDisable; // 店铺状态
private Date storeEndTime; // 店铺结束时间
private String storeLogo; // 店铺 logo
private String storeName; // 店铺名称
private String storeAddressDetail; // 详细地址
private String storeAddressIdPath; // 地址 ID 路径
private String storeAddressPath; // 地址路径
private String storeCenter; // 经纬度
private String storeDesc; // 店铺介绍
private BigDecimal deliveryScore; // 送货评分
private BigDecimal descriptionScore; // 描述评分
private BigDecimal serviceScore; // 服务评分
private Integer goodsNum; // 商品数量
private Integer collectionNum; // 收藏数量
private String yzfMpSign; // yzf mp sign
private String yzfSign; // yzf sign
// 其他字段可以根据需要添加
}

View File

@ -6,9 +6,13 @@ import cn.lili.common.exception.ServiceException;
import cn.lili.common.vo.PageVO; import cn.lili.common.vo.PageVO;
import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dos.ImMessage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.Date;
/** /**
* MessageQueryParams * MessageQueryParams
* *
@ -18,6 +22,7 @@ import lombok.EqualsAndHashCode;
*/ */
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@ApiModel(value = "消息查询参数")
public class MessageQueryParams extends PageVO { public class MessageQueryParams extends PageVO {
private static final long serialVersionUID = 3504156704697214077L; private static final long serialVersionUID = 3504156704697214077L;
@ -25,15 +30,39 @@ public class MessageQueryParams extends PageVO {
/** /**
* 聊天窗口 * 聊天窗口
*/ */
@ApiModelProperty(value = "会话ID")
private String talkId; private String talkId;
/** /**
* 后一个消息 * 早消息ID(用于向上加载历史消息)
*/ */
private String lastMessageId; @ApiModelProperty(value = "最早消息ID(用于向上加载历史消息)")
private String earliestMsgId;
/**
* 最新消息ID(用于获取新消息)
*/
@ApiModelProperty(value = "最新消息ID(用于获取新消息)")
private String latestMsgId;
/** /**
* 获取消息数量 * 获取消息数量
*/ */
@ApiModelProperty(value = "返回消息数量", example = "20")
private Integer num; private Integer num;
/**
* 消息类型(text:文本,image:图片等)
*/
@ApiModelProperty(value = "消息类型(text:文本,image:图片等)")
private String type;
/**
* 发送者ID
*/
@ApiModelProperty(value = "发送者ID")
private String fromUser;
/**
* 接收者ID
*/
@ApiModelProperty(value = "接收者ID")
private String toUser;
public LambdaQueryWrapper<ImMessage> initQueryWrapper() { public LambdaQueryWrapper<ImMessage> initQueryWrapper() {
if (CharSequenceUtil.isEmpty(talkId)) { if (CharSequenceUtil.isEmpty(talkId)) {
@ -45,8 +74,11 @@ public class MessageQueryParams extends PageVO {
LambdaQueryWrapper<ImMessage> lambdaQueryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ImMessage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ImMessage::getTalkId, talkId); lambdaQueryWrapper.eq(ImMessage::getTalkId, talkId);
if (CharSequenceUtil.isNotEmpty(lastMessageId)) { if (CharSequenceUtil.isNotEmpty(earliestMsgId)) {
lambdaQueryWrapper.lt(ImMessage::getId, lastMessageId); lambdaQueryWrapper.ge(ImMessage::getId, earliestMsgId);
}
if (CharSequenceUtil.isNotEmpty(latestMsgId)) {
lambdaQueryWrapper.le(ImMessage::getId, latestMsgId);
} }
lambdaQueryWrapper.orderByDesc(ImMessage::getCreateTime); lambdaQueryWrapper.orderByDesc(ImMessage::getCreateTime);
// lambdaQueryWrapper.last("limit " + num); // lambdaQueryWrapper.last("limit " + num);

View File

@ -1,31 +1,53 @@
package cn.lili.modules.im.entity.enums; package cn.lili.modules.im.entity.enums;
/** /**
* 消息的类型 * 消息状态枚举
*
* @author liushuai(liushuai711 @ gmail.com)
* @version v4.0
* @Description:
* @since 2022/2/10 16:36
*/ */
public enum MessageStatusEnum { public enum MessageStatusEnum {
//socket刚打开时发送的消息这个一般是是刚打开socket链接进行登录传入token用
CONNECT, NORMAL(0, "正常"),
//心跳类型的消息此种类型的消息只有 type text 两种属性 DELETED(1, "已删除"),
HEARTBEAT, RECALLED(2, "已撤回"),
//用户打开一个对话框准备跟某人聊天时
OPEN, // WebSocket相关状态
//客服进行自动回复客户端发起这种类型请求则是在拉取对方是否有自动回复如果有服务端就会给客户端发送过自动回复的信息 CONNECT(100, "连接"),
AUTO_REPLY, HEARTBEAT(101, "心跳"),
//正常收发消息沟通文字表情等沟通 OPEN(102, "打开对话"),
MSG, AUTO_REPLY(103, "自动回复"),
//扩展比如发送商品发送订单 MSG(104, "普通消息"),
EXTEND, EXTEND(105, "扩展消息"),
//系统提示如提示 对方已离线 SYSTEM(106, "系统消息"),
SYSTEM, SET_USER(107, "设置用户"),
//服务端发送到客户端用于设置客户端的用户信息会吧 com.xnx3.yunkefu.core.vo.bean.User 传过去 CLOSE_SERVICE(108, "结束服务");
SET_USER,
//结束服务
CLOSE_SERVICE;
private final Integer status;
private final String description;
MessageStatusEnum(Integer status, String description) {
this.status = status;
this.description = description;
}
public Integer getStatus() {
return status;
}
public String getDescription() {
return description;
}
/**
* 根据状态码获取枚举
*/
public static MessageStatusEnum getByStatus(Integer status) {
if (status == null) {
return null;
}
for (MessageStatusEnum statusEnum : values()) {
if (statusEnum.getStatus().equals(status)) {
return statusEnum;
}
}
return null;
}
} }

View File

@ -1,23 +1,45 @@
package cn.lili.modules.im.entity.enums; package cn.lili.modules.im.entity.enums;
/** /**
* 消息类型 * 消息类型枚举
*
* @author liushuai
*/ */
public enum MessageTypeEnum { public enum MessageTypeEnum {
/** MESSAGE("message", "普通消息"),
* 消息类型枚举 TEXT("TEXT", "文本消息"),
* <p> IMAGE("IMAGE", "图片消息"),
* 普通消息 FILE("FILE", "文件消息"),
* 图片 VOICE("VOICE", "语音消息"),
* 语音 VIDEO("VIDEO", "视频消息"),
* 视频 SYSTEM("SYSTEM", "系统消息"),
*/ GROUP("GROUP", "群聊消息"),
MESSAGE, LOCATION("LOCATION", "位置消息");
PICTURE,
VOICE, private final String type;
GOODS, private final String description;
ORDER,
VIDEO MessageTypeEnum(String type, String description) {
this.type = type;
this.description = description;
}
public String getType() {
return type;
}
public String getDescription() {
return description;
}
public static MessageTypeEnum fromType(String type) {
if (type == null) {
return null;
}
for (MessageTypeEnum typeEnum : MessageTypeEnum.values()) {
if (typeEnum.getType().equalsIgnoreCase(type)) {
return typeEnum;
}
}
return null;
}
} }

View File

@ -21,6 +21,5 @@ public enum OperationType {
MESSAGE, MESSAGE,
HISTORY, HISTORY,
READ, READ,
UNREAD, UNREAD
} }

View File

@ -0,0 +1,35 @@
package cn.lili.modules.im.entity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel(value = "好友申请VO")
public class FriendRequestVO {
@ApiModelProperty(value = "申请ID")
private String id;
@ApiModelProperty(value = "申请者ID")
private String userId;
@ApiModelProperty(value = "申请者昵称")
private String nickname;
@ApiModelProperty(value = "申请者头像")
private String avatar;
@ApiModelProperty(value = "申请者手机号")
private String mobile;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "状态(0:待处理 1:已接受 2:已拒绝)")
private Integer status;
@ApiModelProperty(value = "申请时间")
private Date createTime;
}

View File

@ -4,18 +4,64 @@ import cn.lili.modules.im.entity.dos.Friend;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
@Data
public class FriendVO extends Friend {
// li_member 表获取的字段
private String username;
private String nickname;
private String face; // 头像
private String mobile;
private String email;
private String region;
private String gender;
private Date birthday;
// 昵称首字母用于排序
private String firstLetter;
} import cn.lili.modules.im.entity.dos.Friend;
import lombok.Data;
import java.util.Date;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@Data
@ApiModel(value = "好友VO")
public class FriendVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户ID")
private String id;
@ApiModelProperty(value = "关系ID")
private String friendId;
private String talkId;
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "头像")
private String avatar;
@ApiModelProperty(value = "地区")
private String region;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "是否是店铺 0:否 1:是")
private Integer storeFlag;
@ApiModelProperty(value = "是否互相关注")
private Integer isMutual;
@ApiModelProperty(value = "关注时间")
private Date createTime;
}

View File

@ -0,0 +1,33 @@
package cn.lili.modules.im.entity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel(value = "群成员信息")
public class ImGroupMemberVO {
@ApiModelProperty(value = "成员ID")
private String memberId;
@ApiModelProperty(value = "成员昵称")
private String nickname;
@ApiModelProperty(value = "成员头像")
private String avatar;
@ApiModelProperty(value = "角色(0:普通成员 1:管理员 2:群主)")
private Integer role;
@ApiModelProperty(value = "是否被禁言")
private Integer isMuted;
@ApiModelProperty(value = "禁言结束时间")
private Date muteEndTime;
@ApiModelProperty(value = "加入时间")
private Date joinTime;
private String displayName;
}

View File

@ -0,0 +1,39 @@
package cn.lili.modules.im.entity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel(value = "群聊信息")
public class ImGroupVO {
@ApiModelProperty(value = "群ID")
private String id;
@ApiModelProperty(value = "群名称")
private String name;
@ApiModelProperty(value = "群头像")
private String avatar;
@ApiModelProperty(value = "群公告")
private String notice;
@ApiModelProperty(value = "群主ID")
private String ownerId;
@ApiModelProperty(value = "成员数量")
private Integer memberCount;
@ApiModelProperty(value = "创建时间")
private Date createTime;
private Integer role;
private String displayName;
@ApiModelProperty(value = "最后一条消息")
private ImMessageVO lastMessage;
@ApiModelProperty(value = "未读消息数")
private Integer unreadCount;
}

View File

@ -0,0 +1,78 @@
package cn.lili.modules.im.entity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel(value = "即时通讯消息VO")
public class ImMessageVO {
@ApiModelProperty(value = "消息ID")
private String id;
@ApiModelProperty(value = "会话ID")
private String talkId;
@ApiModelProperty(value = "发送者ID")
private String fromUser;
@ApiModelProperty(value = "发送者昵称")
private String fromName;
@ApiModelProperty(value = "发送者头像")
private String fromAvatar;
@ApiModelProperty(value = "接收者ID")
private String toUser;
@ApiModelProperty(value = "消息内容")
private String text;
@ApiModelProperty(value = "消息类型")
private String type;
@ApiModelProperty(value = "聊天类型(单聊/群聊)")
private String messageType;
@ApiModelProperty(value = "消息状态")
private Integer status;
@ApiModelProperty(value = "是否已读")
private Boolean isRead;
@ApiModelProperty(value = "创建时间")
private Date createTime;
// 扩展字段
@ApiModelProperty(value = "文件名")
private String fileName;
@ApiModelProperty(value = "文件大小")
private Long fileSize;
@ApiModelProperty(value = "文件URL")
private String fileUrl;
@ApiModelProperty(value = "图片URL")
private String imageUrl;
@ApiModelProperty(value = "图片宽度")
private Integer imageWidth;
@ApiModelProperty(value = "图片高度")
private Integer imageHeight;
@ApiModelProperty(value = "缩略图URL")
private String thumbnailUrl;
@ApiModelProperty(value = "语音URL")
private String voiceUrl;
@ApiModelProperty(value = "语音时长")
private Integer duration;
@ApiModelProperty(value = "额外信息")
private String extra;
}

View File

@ -53,6 +53,8 @@ public class ImTalkVO extends BaseTenantEntity {
@ApiModelProperty(value = "未读数量") @ApiModelProperty(value = "未读数量")
private Long unread; private Long unread;
@ApiModelProperty(value = "是否关注")
private Integer isMutual;
public ImTalkVO() { public ImTalkVO() {
@ -66,6 +68,7 @@ public class ImTalkVO extends BaseTenantEntity {
name = imTalk.getName1(); name = imTalk.getName1();
face = imTalk.getFace1(); face = imTalk.getFace1();
storeFlag = imTalk.getStoreFlag1(); storeFlag = imTalk.getStoreFlag1();
isMutual=imTalk.getIsMutual();
} else { } else {
userId = imTalk.getUserId2(); userId = imTalk.getUserId2();
top = imTalk.getTop2(); top = imTalk.getTop2();
@ -73,6 +76,7 @@ public class ImTalkVO extends BaseTenantEntity {
name = imTalk.getName2(); name = imTalk.getName2();
face = imTalk.getFace2(); face = imTalk.getFace2();
storeFlag = imTalk.getStoreFlag2(); storeFlag = imTalk.getStoreFlag2();
isMutual=imTalk.getIsMutual();
} }
lastTalkMessage = imTalk.getLastTalkMessage(); lastTalkMessage = imTalk.getLastTalkMessage();
lastTalkTime = imTalk.getLastTalkTime(); lastTalkTime = imTalk.getLastTalkTime();

View File

@ -41,13 +41,13 @@ public class MessageOperation {
public void setOperationType(String operationType) { public void setOperationType(String operationType) {
if (!StringUtils.isEmpty(operationType)) { if (!StringUtils.isEmpty(operationType)) {
this.operationType = OperationType.valueOf(operationType); this.operationType = OperationType.valueOf(operationType.toUpperCase());
} }
} }
public void setMessageType(String messageType) { public void setMessageType(String messageType) {
if (!StringUtils.isEmpty(messageType)) { if (!StringUtils.isEmpty(messageType)) {
this.messageType = MessageTypeEnum.valueOf(messageType); this.messageType = MessageTypeEnum.valueOf(messageType.toUpperCase());
} }
} }
} }

View File

@ -2,9 +2,15 @@ package cn.lili.modules.im.mapper;
import cn.lili.modules.im.entity.dos.Friend; import cn.lili.modules.im.entity.dos.Friend;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.mapstruct.Mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface FriendMapper extends BaseMapper<Friend> { public interface FriendMapper extends BaseMapper<Friend> {
} }

View File

@ -1,8 +1,13 @@
package cn.lili.modules.im.mapper; package cn.lili.modules.im.mapper;
import cn.lili.modules.im.entity.dos.ImGroupMember; import cn.lili.modules.im.entity.dos.ImGroupMember;
import cn.lili.modules.im.entity.dos.ImMessage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ImGroupMemberMapper extends BaseMapper<ImGroupMember> { public interface ImGroupMemberMapper extends BaseMapper<ImGroupMember> {
} }

View File

@ -1,88 +1,88 @@
package cn.lili.modules.im.service; package cn.lili.modules.im.service;
import cn.lili.modules.im.entity.dos.Friend; import cn.lili.modules.im.entity.dos.Friend;
import cn.lili.modules.im.entity.vo.FriendVO; import cn.lili.modules.im.entity.vo.FriendVO;
import cn.lili.modules.im.entity.vo.FriendRequestVO;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List; import java.util.List;
/**
* 好友关系业务层
*
* @author Chopper
*/
public interface FriendService extends IService<Friend> { public interface FriendService extends IService<Friend> {
/** /**
* 获取关注列表
* 获取互相关注的好友列表包含详细信息
* @param userId 用户ID * @param userId 用户ID
* @return 关注的用户列表
* @return 好友列表
*/ */
List<FriendVO> getMutualFriends(String userId); List<FriendVO> getMutualFriends(String userId);
/** /**
* 获取用户详细信息
* 获取好友详细信息 * @param friendId 用户ID
* @return 用户详细信息
* @param friendId 好友ID
* @return 好友详细信息
*/ */
FriendVO getFriendDetails(String friendId); FriendVO getFriendDetails(String friendId);
/** /**
* 取消关注
* 解除好友关系 * @param userId 当前用户ID
* @param friendId 要取消关注的用户ID
* @param userId 用户ID
* @param friendId 好友ID
*/ */
void removeFriend(String userId, String friendId); void removeFriend(String userId, String friendId);
/** /**
* 关注用户
* 添加好友 * @param userId 当前用户ID
* @param friendId 要关注的用户ID
* @param userId 用户ID
* @param friendId 好友ID
*/ */
void addFriend(String userId, String friendId); void addFriend(String userId, String friendId);
/** /**
* 通过手机号关注用户
* 更新好友备注 * @param mobile 手机号
* @param remark 备注
* @param userId 用户ID */
void addFriendByMobile(String mobile, String remark);
* @param friendId 好友ID
/**
* 更新备注
* @param userId 当前用户ID
* @param friendId 关注的用户ID
* @param remark 备注 * @param remark 备注
*/ */
void updateRemark(String userId, String friendId, String remark); void updateRemark(String userId, String friendId, String remark);
//
// /**
// * 获取好友申请列表
// * @param userId 用户ID
// * @return 申请列表
// */
// List<FriendRequestVO> getFriendRequests(String userId);
//
// /**
// * 接受好友申请
// * @param requestId 申请ID
// * @param userId 当前用户ID
// */
// void acceptFriendRequest(String requestId, String userId);
//
// /**
// * 拒绝好友申请
// * @param requestId 申请ID
// * @param userId 当前用户ID
// */
// void rejectFriendRequest(String requestId, String userId);
/**
* 搜索用户
* @param keyword 搜索关键词(用户名/手机号)
* @param onlyStore 是否只搜索店铺
* @param currentUserId 当前用户ID
* @return 用户列表
*/
List<FriendVO> searchUsers(String keyword, Boolean onlyStore, String currentUserId);
} }

View File

@ -1,58 +1,154 @@
package cn.lili.modules.im.service; package cn.lili.modules.im.service;
import cn.lili.modules.im.entity.dos.ImGroup; import cn.lili.modules.im.entity.dos.ImGroup;
import cn.lili.modules.im.entity.vo.FriendVO;
import cn.lili.modules.im.entity.vo.ImGroupMemberVO;
import cn.lili.modules.im.entity.vo.ImGroupVO;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List; import java.util.List;
public interface ImGroupService extends IService<ImGroup> { public interface ImGroupService extends IService<ImGroup> {
/** /**
* 创建群聊 * 获取用户加入的群聊列表
* @param groupName 群名称 * @param userId 用户ID
* @param memberIds 邀请的成员ID列表 * @return 群聊列表
* @return 群聊信息
*/ */
List<ImGroupVO> getUserGroups(String userId);
/**
* 获取群聊详情
* @param groupId 群ID
* @return 群聊详情
*/
ImGroupVO getGroupDetail(String groupId);
/**
* 获取群成员列表
* @param groupId 群ID
* @return 群成员列表
*/
List<ImGroupMemberVO> getGroupMembers(String groupId);
/**
* 退出群聊
* @param groupId 群ID
* @param userId 用户ID
*/
void quitGroup(String groupId, String userId);
/**
* 修改群信息
* @param groupId 群ID
* @param groupName 群名称
* @param notice 群公告
* @param avatar 群头像
*/
void updateGroupInfo(String groupId, String groupName, String notice, String avatar);
/**
* 创建群聊
* @param groupName 群名称
* @param memberIds 邀请的成员ID列表
* @return 群聊信息
*/
ImGroup createGroup(String groupName, List<String> memberIds); ImGroup createGroup(String groupName, List<String> memberIds);
/** /**
* 解散群聊 * 解散群聊
* @param groupId 群ID * @param groupId 群ID
*/ */
void dismissGroup(String groupId); void dismissGroup(String groupId);
/** /**
* 邀请成员 * 邀请成员
* @param groupId 群ID * @param groupId 群ID
* @param memberIds 成员ID列表 * @param memberIds 成员ID列表
*/ */
void inviteMembers(String groupId, List<String> memberIds); void inviteMembers(String groupId, List<String> memberIds);
/** /**
* 设置管理员 * 设置管理员
* @param groupId 群ID * @param groupId 群ID
* @param memberId 成员ID * @param memberId 成员ID
*/ */
void setAdmin(String groupId, String memberId); void setAdmin(String groupId, String memberId);
/** /**
* 取消管理员 * 取消管理员
* @param groupId 群ID * @param groupId 群ID
* @param memberId 成员ID * @param memberId 成员ID
*/ */
void removeAdmin(String groupId, String memberId); void removeAdmin(String groupId, String memberId);
/**
* 禁言成员
* @param groupId 群ID
* @param memberId 成员ID
* @param duration 禁言时长(分钟)
*/
void muteMember(String groupId, String memberId, Integer duration);
/** /**
* 解除成员禁言
* 禁言成员
* @param groupId 群ID * @param groupId 群ID
* @param memberId 成员ID * @param memberId 成员ID
* @param duration 禁言时长(分钟)
*/ */
void muteMember(String groupId, String memberId, Integer duration);
/**
* 解除成员禁言
* @param groupId 群ID
* @param memberId 成员ID
*/
void unmuteMember(String groupId, String memberId); void unmuteMember(String groupId, String memberId);
} }

View File

@ -1,7 +1,10 @@
package cn.lili.modules.im.service; package cn.lili.modules.im.service;
import cn.lili.common.vo.PageVO;
import cn.lili.common.vo.ResultPageVO;
import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dos.ImMessage;
import cn.lili.modules.im.entity.dto.MessageQueryParams; import cn.lili.modules.im.entity.dto.MessageQueryParams;
import cn.lili.modules.im.entity.vo.ImMessageVO;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List; import java.util.List;
@ -13,6 +16,33 @@ import java.util.List;
*/ */
public interface ImMessageService extends IService<ImMessage> { public interface ImMessageService extends IService<ImMessage> {
/**
* 获取历史消息
* @param talkId 对话ID
* @param earliestMsgId 最早消息ID
* @param pageSize 每页大小
* @return 消息列表
*/
List<ImMessageVO> getHistoryMessages(String talkId, String earliestMsgId, Integer pageSize);
/**
* 获取最近消息
* @param talkId 对话ID
* @param latestMsgId 最新消息ID
* @param pageSize 每页大小
* @return 消息列表
*/
List<ImMessageVO> getRecentMessages(String talkId, String latestMsgId, Integer pageSize);
/**
* 发送消息
* @param toId 接收者ID
* @param content 消息内容
* @param type 消息类型
* @return 消息对象
*/
ImMessageVO sendMessage(String toId, String content, String type);
/** /**
* 阅读消息 * 阅读消息
* *
@ -63,4 +93,60 @@ public interface ImMessageService extends IService<ImMessage> {
* 清空所有未读消息 * 清空所有未读消息
*/ */
void cleanUnreadMessage(); void cleanUnreadMessage();
/**
* 获取群聊消息历史
* @param groupId 群ID
* @param pageNumber 页码
* @param pageSize 每页大小
* @return 消息列表
*/
ResultPageVO<ImMessageVO> getGroupMessages(String groupId, Integer pageNumber, Integer pageSize);
/**
* 发送群聊消息
* @param groupId 群ID
* @param content 消息内容
* @param type 消息类型
* @return 消息对象
*/
ImMessageVO sendGroupMessage(String groupId, String content, String type);
/**
* 撤回消息
* @param messageId 消息ID
*/
void recallMessage(String messageId);
/**
* 加载历史消息
* @param talkId 会话ID
* @param earliestMsgId 最早消息ID
* @param size 每页大小
* @return 消息列表
*/
List<ImMessageVO> loadHistoryMessages(String talkId, String earliestMsgId, Integer size);
/**
* 加载最新消息
* @param talkId 会话ID
* @param size 每页大小
* @return 消息列表
*/
List<ImMessageVO> loadLatestMessages(String talkId, Integer size);
/**
* 获取新消息
* @param talkId 会话ID
* @param latestMsgId 最新消息ID
* @return 新消息列表
*/
List<ImMessageVO> getNewerMessages(String talkId, String latestMsgId);
/**
* 标记消息已读
* @param messages 消息列表
* @param userId 用户ID
*/
void readMessages(List<ImMessage> messages, String userId);
} }

View File

@ -0,0 +1,7 @@
package cn.lili.modules.im.service;
import cn.lili.modules.im.entity.vo.MessageVO;
public interface MessageSender {
void sendMessage(String sessionId, MessageVO message);
}

View File

@ -1,344 +1,404 @@
package cn.lili.modules.im.serviceimpl; package cn.lili.modules.im.serviceimpl;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums; import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.StringUtils; import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.modules.im.entity.dos.Friend; import cn.lili.modules.im.entity.dos.Friend;
import cn.lili.modules.im.entity.dos.ImTalk;
import cn.lili.modules.im.entity.vo.FriendVO; import cn.lili.modules.im.entity.vo.FriendVO;
import cn.lili.modules.im.entity.vo.FriendRequestVO;
import cn.lili.modules.im.mapper.FriendMapper; import cn.lili.modules.im.mapper.FriendMapper;
import cn.lili.modules.im.service.FriendService; import cn.lili.modules.im.service.FriendService;
import cn.lili.modules.im.service.ImTalkService;
import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.mapper.MemberMapper; import cn.lili.modules.member.service.MemberService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import java.util.Collections; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.Map;
import java.util.Objects;
import static cn.hutool.extra.pinyin.PinyinUtil.getFirstLetter; import java.util.stream.Collectors;
/**
* 好友业务层实现
*
* @author Chopper
*/
@Slf4j @Slf4j
@Service @Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService { public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService {
@Autowired private final ImTalkService imTalkService;
private MemberMapper memberMapper; private final MemberService memberService;
private final FriendMapper friendMapper;
@Override @Override
public List<FriendVO> getMutualFriends(String userId) { public List<FriendVO> getMutualFriends(String userId) {
// 获取好友关系列表 // 验证用户登录状态
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
// 获取当前用户的ID
String currentUserId = currentUser.getId();
// 构建查询条件
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Friend::getUserId, currentUserId) // 查询当前用户的关注列表
.eq(Friend::getStatus, 1); // 只查询状态为1的记录
// 查询关注列表
List<Friend> friends = this.list(queryWrapper);
if (friends.isEmpty()) {
return new ArrayList<>(); // 如果没有找到返回空列表
}
// 收集所有关注的用户ID
List<String> friendIds = friends.stream()
.map(Friend::getFriendId) // 获取 friendId
.collect(Collectors.toList());
// 检查 friendIds 是否为空
if (friendIds.isEmpty()) {
log.warn("No friend IDs found for user: {}", currentUserId);
return new ArrayList<>(); // 或者抛出异常
}
// 批量查询用户信息
List<Member> members = memberService.listByIds(friendIds);
Map<String, Member> memberMap = members.stream()
.collect(Collectors.toMap(Member::getId, member -> member));
// 组装VO对象
return friends.stream().map(friend -> {
Member member = memberMap.get(friend.getFriendId());
if (member != null) {
FriendVO vo = new FriendVO();
vo.setId(member.getId().toString()); // 设置为用户的 ID
vo.setFriendId(friend.getFriendId()); // 设置为 Friend ID
vo.setNickname(member.getNickName());
vo.setAvatar(member.getFace());
// 其他字段设置...
return vo;
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
// 假设这个方法用于从 ImTalk 表中获取 talkId
private String getTalkId(String userId1, String userId2) {
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper
.or().eq(ImTalk::getUserId1, userId1).eq(ImTalk::getUserId2, userId2)
.or().eq(ImTalk::getUserId1, userId2).eq(ImTalk::getUserId2, userId1);
ImTalk imTalk = imTalkService.getOne(queryWrapper);
return imTalk != null ? imTalk.getId() : null; // 返回 talkId
}
@Override
public void addFriend(String userId, String friendId) {
// 验证用户登录状态
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
// 检查是否已经关注
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Friend::getUserId, userId) queryWrapper.eq(Friend::getUserId, userId)
.eq(Friend::getStatus, 1); // 已确认的好友关系 .eq(Friend::getFriendId, friendId);
List<Friend> friends = this.list(queryWrapper);
// 如果已经关注抛出异常
// 转换为 FriendVO 列表 if (this.count(queryWrapper) > 0) {
List<FriendVO> friendVOs = new ArrayList<>(); throw new ServiceException("您已经关注过该用户");
for (Friend friend : friends) {
FriendVO friendVO = new FriendVO();
BeanUtils.copyProperties(friend, friendVO);
// 查询好友的详细信息
Member member = memberMapper.selectById(friend.getFriendId());
if (member != null) {
// 复制会员信息到 VO
friendVO.setUsername(member.getUsername());
friendVO.setNickname(member.getNickName());
friendVO.setFace(member.getFace());
friendVO.setMobile(member.getMobile());
friendVO.setEmail(member.getRegion());
friendVO.setGender(member.getGradeId());
friendVO.setBirthday(member.getBirthday());
// 设置昵称首字母
String firstLetter = getFirstLetter(member.getNickName());
friendVO.setFirstLetter(firstLetter);
}
friendVOs.add(friendVO);
} }
// 按照昵称首字母排序
Collections.sort(friendVOs, (a, b) -> {
String letterA = a.getFirstLetter() != null ? a.getFirstLetter() : "#";
String letterB = b.getFirstLetter() != null ? b.getFirstLetter() : "#";
return letterA.compareTo(letterB);
});
// 创建新的关注关系
Friend friend = new Friend();
friend.setUserId(userId);
friend.setFriendId(friendId);
friend.setStatus(1); // 设置为已关注状态
friend.setIsMutual(1);
friend.setCreateTime(new Date());
// friend.setUpdateTime(new Date());
return friendVOs; // 检查对方是否也关注了当前用户
Integer mutualStatus = isFollowing(friendId, userId); // 检查 friendId 是否关注了 userId
// 设置互相关注状态
if (mutualStatus == 1) {
friend.setIsMutual(2); // 设置为互相关注
} else {
friend.setIsMutual(1); // 设置为单向关注
}
// 保存关注关系
this.save(friend);
} }
/**
* 获取字符串的首字母
* @param str 字符串
* @return 首字母大写
*/
private String getFirstLetter(String str) {
if (StringUtils.isEmpty(str)) {
return "#";
}
try {
// 创建 HanyuPinyinOutputFormat 对象
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
// 设置拼音小写
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
// 设置声调不输出
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
// 取第一个字符
char firstChar = str.charAt(0);
// 判断是否为汉字
if (Character.toString(firstChar).matches("[\\u4E00-\\u9FA5]+")) {
// 如果是汉字转换为拼音
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(firstChar, format);
if (pinyinArray != null && pinyinArray.length > 0) {
return pinyinArray[0].substring(0, 1).toUpperCase();
}
} else if (Character.toString(firstChar).matches("[a-zA-Z]")) {
// 如果是字母直接返回大写
return String.valueOf(firstChar).toUpperCase();
}
// 其他情况返回 #
return "#";
} catch (BadHanyuPinyinOutputFormatCombination e) {
log.error("获取拼音首字母失败", e);
return "#";
}
}
@Override @Override
public FriendVO getFriendDetails(String friendId) { public FriendVO getFriendDetails(String friendId) {
String userId = UserContext.getCurrentUser().getId(); // 验证用户登录状态
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
// 查询好友关系 // 获取好友信息
Member member = memberService.getById(friendId);
if (member == null) {
throw new ServiceException("用户不存在");
}
// 转换为VO
FriendVO vo = new FriendVO();
vo.setId(member.getId().toString());
vo.setNickname(member.getNickName());
vo.setAvatar(member.getFace());
vo.setRegion(member.getRegion());
vo.setMobile(member.getMobile());
// 设置默认值
vo.setRemark(""); // 或者设置为 null
vo.setStoreFlag(member.getHaveStore() ? 1 : 0); // 假设你有这个字段
vo.setCreateTime(new Date()); // 或者设置为 null
// 获取当前用户ID
String currentUserId = currentUser.getId();
// 查询Friend表以获取isMutual值
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Friend::getUserId, currentUserId)
.eq(Friend::getFriendId, friendId);
Friend friendRelation = this.getOne(queryWrapper);
if (friendRelation != null) {
vo.setIsMutual(friendRelation.getIsMutual()); // 设置isMutual值
vo.setFriendId(friendRelation.getFriendId());
} else {
vo.setIsMutual(0); // 如果没有找到关系设置为未关注
}
return vo;
}
@Override
public void removeFriend(String userId, String friendId) {
// 验证用户登录状态
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
// 验证操作权限
if (!currentUser.getId().equals(userId)) {
throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
}
// 取消关注
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Friend::getUserId, userId) queryWrapper.eq(Friend::getUserId, userId)
.eq(Friend::getFriendId, friendId); .eq(Friend::getFriendId, friendId);
Friend friend = this.getOne(queryWrapper);
if (friend == null) {
throw new ServiceException("好友关系不存在");
}
// 转换为 VO 并查询详细信息
FriendVO friendVO = new FriendVO();
BeanUtils.copyProperties(friend, friendVO);
// 查询好友的详细信息
Member member = memberMapper.selectById(friendId);
if (member != null) {
// 复制会员信息到 VO
friendVO.setUsername(member.getUsername());
friendVO.setNickname(member.getNickName());
friendVO.setFace(member.getFace());
friendVO.setMobile(member.getMobile());
friendVO.setEmail(member.getRegion());
friendVO.setGender(member.getGradeId());
friendVO.setBirthday(member.getBirthday());
}
return friendVO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeFriend(String userId, String friendId) {
// 删除双向的好友关系
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.and(wrapper -> wrapper
.or(w -> w.eq(Friend::getUserId, userId).eq(Friend::getFriendId, friendId))
.or(w -> w.eq(Friend::getUserId, friendId).eq(Friend::getFriendId, userId))
);
if (!this.remove(queryWrapper)) { if (!this.remove(queryWrapper)) {
throw new ServiceException("取消关注失败");
throw new ServiceException("解除好友关系失败");
} }
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void addFriend(String userId, String friendId) {
// 检查用户ID是否存在
if (!isUserExists(userId)) {
throw new ServiceException("用户ID不存在");
}
// 检查好友ID是否存在
if (!isUserExists(friendId)) {
throw new ServiceException("好友ID不存在");
}
// 检查是否已经是好友
if (checkFriendship(userId, friendId)) {
throw new ServiceException("已经是好友关系");
}
// 创建好友关系
Friend friend = new Friend();
friend.setUserId(userId);
friend.setFriendId(friendId);
friend.setStatus(1);
friend.setCreateTime(new Date());
friend.setUpdateTime(new Date());
// 保存双向好友关系
if (!this.save(friend)) {
throw new ServiceException("添加好友失败");
}
// 创建反向好友关系
Friend reverseFriend = new Friend();
reverseFriend.setUserId(friendId);
reverseFriend.setFriendId(userId);
reverseFriend.setStatus(1);
reverseFriend.setCreateTime(new Date());
reverseFriend.setUpdateTime(new Date());
if (!this.save(reverseFriend)) {
throw new ServiceException("添加好友失败");
}
}
private boolean isUserExists(String userId) {
return memberMapper.selectById(userId) != null;
}
@Override
public void updateRemark(String userId, String friendId, String remark) { public void updateRemark(String userId, String friendId, String remark) {
// 验证用户登录状态
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>(); AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
queryWrapper.eq(Friend::getUserId, userId) throw new ServiceException(ResultCode.USER_NOT_LOGIN);
.eq(Friend::getFriendId, friendId);
Friend friend = this.getOne(queryWrapper);
if (friend == null) {
throw new ServiceException("好友关系不存在");
} }
// 验证操作权限
if (!currentUser.getId().equals(userId)) {
friend.setRemark(remark); throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
friend.setUpdateTime(new Date());
if (!this.updateById(friend)) {
throw new ServiceException("更新好友备注失败");
} }
} // 更新备注
/**
* 检查是否已经是好友关系
*/
private boolean checkFriendship(String userId, String friendId) {
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Friend::getUserId, userId) queryWrapper.eq(Friend::getUserId, userId)
.eq(Friend::getFriendId, friendId) .eq(Friend::getFriendId, friendId)
.eq(Friend::getStatus, 1); .eq(Friend::getStatus, 1);
return this.count(queryWrapper) > 0; Friend friend = this.getOne(queryWrapper);
if (friend == null) {
throw new ServiceException("未关注该用户");
}
friend.setRemark(remark);
friend.setUpdateTime(new Date());
if (!this.updateById(friend)) {
throw new ServiceException("更新备注失败");
}
} }
@Override
public void addFriendByMobile(String mobile, String remark) {
// 查找目标用户
Member targetMember = memberService.findByMobile(mobile);
if (targetMember == null) {
throw new ServiceException("该手机号未注册");
}
String currentUserId = UserContext.getCurrentUser().getId();
String targetUserId = targetMember.getId().toString();
// 不能关注自己
if (currentUserId.equals(targetUserId)) {
throw new ServiceException("不能关注自己");
}
// 检查是否已经关注
if (isFollowing(currentUserId, targetUserId) > 0) {
throw new ServiceException("已经关注该用户");
}
} // 创建关注关系
Friend friend = new Friend();
friend.setUserId(currentUserId);
friend.setFriendId(targetUserId);
friend.setRemark(remark);
friend.setStatus(1);
friend.setIsMutual(1); // 直接设置为已关注状态
friend.setStoreFlag(targetMember.getHaveStore() ? 1 : 0); // 设置是否是店铺
friend.setCreateTime(new Date());
// friend.setUpdateTime(new Date());
// 检查对方是否也关注了当前用户
Integer mutualStatus = isFollowing(targetUserId, currentUserId);
// 设置互相关注状态
if (mutualStatus == 1) {
friend.setIsMutual(2); // 设置为互相关注
} else {
friend.setIsMutual(1); // 设置为单向关注
}
// 保存关注关系
this.save(friend);
}
/**
* 检查是否是好友关系互相关注
*/
private Integer isFriend(String userId, String friendId) {
// 检查是否互相关注
Integer isUserFollowingFriend = isFollowing(userId, friendId);
Integer isFriendFollowingUser = isFollowing(friendId, userId);
// 如果双方都关注返回 2如果只有一个方向关注返回 1否则返回 0
if (isUserFollowingFriend == 1 && isFriendFollowingUser == 1) {
return 2; // 互相关注
} else if (isUserFollowingFriend == 1 || isFriendFollowingUser == 1) {
return 1; // 单向关注
}
return 0; // 未关注
}
/**
* 检查是否已关注
*/
public Integer isFollowing(String currentUserId, String targetUserId) {
// 查询当前用户是否关注目标用户
Long count = friendMapper.selectCount(new LambdaQueryWrapper<Friend>()
.eq(Friend::getUserId, currentUserId)
.eq(Friend::getFriendId, targetUserId)
.eq(Friend::getIsMutual, 1)); // 确保状态为已关注
if (count > 0) {
// 查询目标用户是否也关注当前用户
Long mutualCount = friendMapper.selectCount(new LambdaQueryWrapper<Friend>()
.eq(Friend::getUserId, targetUserId)
.eq(Friend::getFriendId, currentUserId)
.eq(Friend::getIsMutual, 1)); // 确保状态为已关注
return mutualCount > 0 ? 2 : 1; // 2 表示互相关注1 表示单向关注
}
return 0; // 未关注
}
@Override
public List<FriendVO> searchUsers(String keyword, Boolean onlyStore, String currentUserId) {
// 构建查询条件
LambdaQueryWrapper<Member> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.and(wrapper -> wrapper
.like(Member::getNickName, keyword)
.or()
.like(Member::getMobile, keyword)
)
.ne(Member::getId, currentUserId); // 排除自己
if (Boolean.TRUE.equals(onlyStore)) {
queryWrapper.eq(Member::getHaveStore, true);
}
// 限制返回数量
queryWrapper.last("LIMIT 20");
// 查询用户
List<Member> members = memberService.list(queryWrapper);
if (members.isEmpty()) {
return new ArrayList<>();
}
// 获取已关注的用户ID
List<String> memberIds = members.stream()
.map(member -> member.getId().toString())
.distinct() // 确保唯一
.collect(Collectors.toList());
// 查询当前用户与这些用户的关注关系
LambdaQueryWrapper<Friend> friendWrapper = new LambdaQueryWrapper<>();
friendWrapper.eq(Friend::getUserId, currentUserId)
.in(Friend::getFriendId, memberIds)
.eq(Friend::getStatus, 1);
List<Friend> friends = this.list(friendWrapper);
// 构建已关注用户的Map
Map<String, Friend> friendMap = friends.stream()
.collect(Collectors.toMap(Friend::getFriendId, friend -> friend, (existing, replacement) -> existing)); // 处理重复键
// 转换为VO对象
return members.stream().map(member -> {
FriendVO vo = new FriendVO();
vo.setId(member.getId().toString());
vo.setNickname(member.getNickName());
vo.setAvatar(member.getFace());
vo.setRegion(member.getRegion());
vo.setMobile(member.getMobile());
vo.setStoreFlag(member.getHaveStore() ? 1 : 0);
// 设置是否互相关注
Friend friend = friendMap.get(member.getId().toString());
if (friend != null) {
vo.setFriendId(friend.getId());
vo.setRemark(friend.getRemark());
vo.setCreateTime(friend.getCreateTime());
// 检查是否互相关注
vo.setIsMutual(friend.getIsMutual() != null ? friend.getIsMutual() : 0);
} else {
vo.setIsMutual(0); // 如果没有找到对应的 Friend 记录设置为 0
}
return vo;
}).distinct() // 确保返回的 VO 对象唯一
.collect(Collectors.toList());
}
}

View File

@ -1,50 +1,216 @@
package cn.lili.modules.im.serviceimpl; package cn.lili.modules.im.serviceimpl;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.modules.im.entity.dos.ImGroup; import cn.lili.modules.im.entity.dos.ImGroup;
import cn.lili.modules.im.entity.dos.ImGroupMember; import cn.lili.modules.im.entity.dos.ImGroupMember;
import cn.lili.modules.im.entity.vo.ImGroupMemberVO;
import cn.lili.modules.im.entity.vo.ImGroupVO;
import cn.lili.modules.im.mapper.ImGroupMapper; import cn.lili.modules.im.mapper.ImGroupMapper;
import cn.lili.modules.im.mapper.ImGroupMemberMapper; import cn.lili.modules.im.mapper.ImGroupMemberMapper;
import cn.lili.modules.im.service.ImGroupService; import cn.lili.modules.im.service.ImGroupService;
import cn.lili.modules.im.service.FriendService;
import cn.lili.modules.im.entity.vo.FriendVO;
import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.mapper.MemberMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> implements ImGroupService { public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> implements ImGroupService {
private final ImGroupMemberMapper groupMemberMapper; private final ImGroupMemberMapper groupMemberMapper;
private final MemberMapper memberMapper;
private final FriendService friendService;
@Override @Override
@Transactional(rollbackFor = Exception.class) public List<ImGroupVO> getUserGroups(String userId) {
public ImGroup createGroup(String groupName, List<String> memberIds) { // 查询用户加入的群组
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
memberQuery.eq(ImGroupMember::getMemberId, userId);
List<ImGroupMember> groupMembers = groupMemberMapper.selectList(memberQuery);
if (groupMembers.isEmpty()) {
return new ArrayList<>();
}
// 获取群组ID列表
List<String> groupIds = groupMembers.stream()
.map(ImGroupMember::getGroupId)
.collect(Collectors.toList());
// 批量查询群组信息
List<ImGroup> groups = this.listByIds(groupIds);
// 转换为VO对象
return groups.stream().map(group -> {
ImGroupVO vo = new ImGroupVO();
BeanUtils.copyProperties(group, vo);
// 设置成员数量
vo.setMemberCount(getGroupMemberCount(group.getId()));
// TODO: 设置最后一条消息和未读数
return vo;
}).collect(Collectors.toList());
}
@Override
public ImGroupVO getGroupDetail(String groupId) {
// 获取群组基本信息
ImGroup group = this.getById(groupId);
if (group == null) {
throw new ServiceException("群组不存在");
}
// 转换为VO
ImGroupVO vo = new ImGroupVO();
BeanUtils.copyProperties(group, vo);
// 设置成员数量
vo.setMemberCount(getGroupMemberCount(groupId));
// TODO: 设置最后一条消息和未读数
return vo;
}
@Override
public List<ImGroupMemberVO> getGroupMembers(String groupId) {
// 查询群成员
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
memberQuery.eq(ImGroupMember::getGroupId, groupId)
.orderByDesc(ImGroupMember::getRole)
.orderByAsc(ImGroupMember::getJoinTime);
List<ImGroupMember> members = groupMemberMapper.selectList(memberQuery);
if (members.isEmpty()) {
return new ArrayList<>();
}
// 获取成员用户信息
List<String> memberIds = members.stream()
.map(ImGroupMember::getMemberId)
.collect(Collectors.toList());
List<Member> users = memberMapper.selectBatchIds(memberIds);
Map<String, Member> userMap = users.stream()
.collect(Collectors.toMap(member -> member.getId().toString(), member -> member));
// 转换为VO
return members.stream().map(member -> {
ImGroupMemberVO vo = new ImGroupMemberVO();
BeanUtils.copyProperties(member, vo);
Member user = userMap.get(member.getMemberId());
if (user != null) {
vo.setNickname(user.getNickName());
vo.setAvatar(user.getFace());
}
return vo;
}).collect(Collectors.toList());
}
@Override
public void quitGroup(String groupId, String userId) {
// 验证群组是否存在
ImGroup group = this.getById(groupId);
if (group == null) {
throw new ServiceException("群组不存在");
}
// 群主不能退群
if (group.getOwnerId().equals(userId)) {
throw new ServiceException("群主不能退群");
}
// 删除群成员记录
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
memberQuery.eq(ImGroupMember::getGroupId, groupId)
.eq(ImGroupMember::getMemberId, userId);
groupMemberMapper.delete(memberQuery);
}
@Override
public void updateGroupInfo(String groupId, String groupName, String notice, String avatar) {
// 验证群组是否存在
ImGroup group = this.getById(groupId);
if (group == null) {
throw new ServiceException("群组不存在");
}
// 验证操作权限
String currentUserId = UserContext.getCurrentUser().getId(); String currentUserId = UserContext.getCurrentUser().getId();
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
memberQuery.eq(ImGroupMember::getGroupId, groupId)
.eq(ImGroupMember::getMemberId, currentUserId)
.ge(ImGroupMember::getRole, 1); // 管理员及以上权限
if (groupMemberMapper.selectCount(memberQuery) == 0) {
throw new ServiceException("没有权限修改群信息");
}
// 更新群信息
ImGroup updateGroup = new ImGroup();
updateGroup.setId(groupId);
if (groupName != null) {
updateGroup.setName(groupName);
}
if (notice != null) {
updateGroup.setNotice(notice);
}
if (avatar != null) {
updateGroup.setAvatar(avatar);
}
updateGroup.setUpdateTime(new Date());
this.updateById(updateGroup);
}
@Override
public ImGroup createGroup(String groupName, List<String> memberIds) {
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
// 创建群组 // 创建群组
ImGroup group = new ImGroup(); ImGroup group = new ImGroup();
group.setName(groupName); group.setName(groupName);
group.setOwnerId(currentUserId); group.setOwnerId(currentUser.getId());
group.setCreateTime(new Date()); group.setCreateTime(new Date());
group.setUpdateTime(new Date()); group.setUpdateTime(new Date());
if (!this.save(group)) { if (!this.save(group)) {
throw new ServiceException("创建群聊失败"); throw new ServiceException("创建群失败");
} }
// 添加群主为成员 // 添加群主为成员
addGroupMember(group.getId(), currentUserId, 2); addGroupMember(group.getId(), currentUser.getId(), 2); // 2表示群主
// 添加其他成员 // 添加初始成员
if (memberIds != null && !memberIds.isEmpty()) { if (memberIds != null && !memberIds.isEmpty()) {
for (String memberId : memberIds) { for (String memberId : memberIds) {
addGroupMember(group.getId(), memberId, 0); addGroupMember(group.getId(), memberId, 0); // 0表示普通成员
} }
} }
@ -52,20 +218,23 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void dismissGroup(String groupId) { public void dismissGroup(String groupId) {
String currentUserId = UserContext.getCurrentUser().getId(); // 验证群组是否存在
ImGroup group = this.getById(groupId);
if (group == null) {
throw new ServiceException("群组不存在");
}
// 检查是否是群主 // 验证是否是群主
ImGroupMember owner = getGroupMember(groupId, currentUserId); String currentUserId = UserContext.getCurrentUser().getId();
if (owner == null || owner.getRole() != 2) { if (!group.getOwnerId().equals(currentUserId)) {
throw new ServiceException("只有群主能解散群"); throw new ServiceException("只有群主能解散群");
} }
// 删除群成员 // 删除群成员
LambdaQueryWrapper<ImGroupMember> memberWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
memberWrapper.eq(ImGroupMember::getGroupId, groupId); memberQuery.eq(ImGroupMember::getGroupId, groupId);
groupMemberMapper.delete(memberWrapper); groupMemberMapper.delete(memberQuery);
// 删除群组 // 删除群组
this.removeById(groupId); this.removeById(groupId);
@ -75,13 +244,13 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void inviteMembers(String groupId, List<String> memberIds) { public void inviteMembers(String groupId, List<String> memberIds) {
String currentUserId = UserContext.getCurrentUser().getId(); String currentUserId = UserContext.getCurrentUser().getId();
// 检查权限 // 检查权限
ImGroupMember operator = getGroupMember(groupId, currentUserId); ImGroupMember operator = getGroupMember(groupId, currentUserId);
if (operator == null || operator.getRole() < 1) { if (operator == null || operator.getRole() < 1) {
throw new ServiceException("没有邀请权限"); throw new ServiceException("没有邀请权限");
} }
// 添加成员 // 添加成员
for (String memberId : memberIds) { for (String memberId : memberIds) {
if (getGroupMember(groupId, memberId) == null) { if (getGroupMember(groupId, memberId) == null) {
@ -94,19 +263,19 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void setAdmin(String groupId, String memberId) { public void setAdmin(String groupId, String memberId) {
String currentUserId = UserContext.getCurrentUser().getId(); String currentUserId = UserContext.getCurrentUser().getId();
// 检查是否是群主 // 检查是否是群主
ImGroupMember owner = getGroupMember(groupId, currentUserId); ImGroupMember owner = getGroupMember(groupId, currentUserId);
if (owner == null || owner.getRole() != 2) { if (owner == null || owner.getRole() != 2) {
throw new ServiceException("只有群主能设置管理员"); throw new ServiceException("只有群主能设置管理员");
} }
// 设置管理员 // 设置管理员
ImGroupMember member = getGroupMember(groupId, memberId); ImGroupMember member = getGroupMember(groupId, memberId);
if (member == null) { if (member == null) {
throw new ServiceException("该成员不在群中"); throw new ServiceException("该成员不在群中");
} }
member.setRole(1); member.setRole(1);
member.setUpdateTime(new Date()); member.setUpdateTime(new Date());
groupMemberMapper.updateById(member); groupMemberMapper.updateById(member);
@ -116,19 +285,19 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void removeAdmin(String groupId, String memberId) { public void removeAdmin(String groupId, String memberId) {
String currentUserId = UserContext.getCurrentUser().getId(); String currentUserId = UserContext.getCurrentUser().getId();
// 检查是否是群主 // 检查是否是群主
ImGroupMember owner = getGroupMember(groupId, currentUserId); ImGroupMember owner = getGroupMember(groupId, currentUserId);
if (owner == null || owner.getRole() != 2) { if (owner == null || owner.getRole() != 2) {
throw new ServiceException("只有群主能取消管理员"); throw new ServiceException("只有群主能取消管理员");
} }
// 取消管理员 // 取消管理员
ImGroupMember member = getGroupMember(groupId, memberId); ImGroupMember member = getGroupMember(groupId, memberId);
if (member == null) { if (member == null) {
throw new ServiceException("该成员不在群中"); throw new ServiceException("该成员不在群中");
} }
member.setRole(0); member.setRole(0);
member.setUpdateTime(new Date()); member.setUpdateTime(new Date());
groupMemberMapper.updateById(member); groupMemberMapper.updateById(member);
@ -138,24 +307,24 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void muteMember(String groupId, String memberId, Integer duration) { public void muteMember(String groupId, String memberId, Integer duration) {
String currentUserId = UserContext.getCurrentUser().getId(); String currentUserId = UserContext.getCurrentUser().getId();
// 检查操作权限 // 检查操作权限
ImGroupMember operator = getGroupMember(groupId, currentUserId); ImGroupMember operator = getGroupMember(groupId, currentUserId);
if (operator == null || operator.getRole() < 1) { if (operator == null || operator.getRole() < 1) {
throw new ServiceException("没有禁言权限"); throw new ServiceException("没有禁言权限");
} }
// 检查被禁言成员 // 检查被禁言成员
ImGroupMember member = getGroupMember(groupId, memberId); ImGroupMember member = getGroupMember(groupId, memberId);
if (member == null) { if (member == null) {
throw new ServiceException("该成员不在群中"); throw new ServiceException("该成员不在群中");
} }
// 管理员不能禁言群主和其他管理员 // 管理员不能禁言群主和其他管理员
if (operator.getRole() == 1 && member.getRole() > 0) { if (operator.getRole() == 1 && member.getRole() > 0) {
throw new ServiceException("没有权限禁言该成员"); throw new ServiceException("没有权限禁言该成员");
} }
// 设置禁言 // 设置禁言
member.setIsMuted(1); member.setIsMuted(1);
member.setMuteEndTime(new Date(System.currentTimeMillis() + duration * 60 * 1000)); member.setMuteEndTime(new Date(System.currentTimeMillis() + duration * 60 * 1000));
@ -167,24 +336,24 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void unmuteMember(String groupId, String memberId) { public void unmuteMember(String groupId, String memberId) {
String currentUserId = UserContext.getCurrentUser().getId(); String currentUserId = UserContext.getCurrentUser().getId();
// 检查操作权限 // 检查操作权限
ImGroupMember operator = getGroupMember(groupId, currentUserId); ImGroupMember operator = getGroupMember(groupId, currentUserId);
if (operator == null || operator.getRole() < 1) { if (operator == null || operator.getRole() < 1) {
throw new ServiceException("没有解除禁言权限"); throw new ServiceException("没有解除禁言权限");
} }
// 检查被解除禁言成员 // 检查被解除禁言成员
ImGroupMember member = getGroupMember(groupId, memberId); ImGroupMember member = getGroupMember(groupId, memberId);
if (member == null) { if (member == null) {
throw new ServiceException("该成员不在群中"); throw new ServiceException("该成员不在群中");
} }
// 管理员不能解除群主和其他管理员的禁言 // 管理员不能解除群主和其他管理员的禁言
if (operator.getRole() == 1 && member.getRole() > 0) { if (operator.getRole() == 1 && member.getRole() > 0) {
throw new ServiceException("没有权限解除该成员的禁言"); throw new ServiceException("没有权限解除该成员的禁言");
} }
// 解除禁言 // 解除禁言
member.setIsMuted(0); member.setIsMuted(0);
member.setMuteEndTime(null); member.setMuteEndTime(null);
@ -192,6 +361,15 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
groupMemberMapper.updateById(member); groupMemberMapper.updateById(member);
} }
/**
* 获取群成员数量
*/
private Integer getGroupMemberCount(String groupId) {
LambdaQueryWrapper<ImGroupMember> countQuery = new LambdaQueryWrapper<>();
countQuery.eq(ImGroupMember::getGroupId, groupId);
return Math.toIntExact(groupMemberMapper.selectCount(countQuery));
}
/** /**
* 添加群成员 * 添加群成员
*/ */
@ -200,9 +378,9 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
member.setGroupId(groupId); member.setGroupId(groupId);
member.setMemberId(memberId); member.setMemberId(memberId);
member.setRole(role); member.setRole(role);
member.setIsMuted(0);
member.setJoinTime(new Date()); member.setJoinTime(new Date());
member.setUpdateTime(new Date()); member.setIsMuted(0);
groupMemberMapper.insert(member); groupMemberMapper.insert(member);
} }
@ -212,7 +390,16 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> impl
private ImGroupMember getGroupMember(String groupId, String memberId) { private ImGroupMember getGroupMember(String groupId, String memberId) {
LambdaQueryWrapper<ImGroupMember> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ImGroupMember> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ImGroupMember::getGroupId, groupId) queryWrapper.eq(ImGroupMember::getGroupId, groupId)
.eq(ImGroupMember::getMemberId, memberId); .eq(ImGroupMember::getMemberId, memberId);
return groupMemberMapper.selectOne(queryWrapper); return groupMemberMapper.selectOne(queryWrapper);
} }
}
/**
* 检查是否是好友关系
*/
private boolean isFriend(String userId, String friendId) {
List<FriendVO> friends = friendService.getMutualFriends(userId);
return friends.stream()
.anyMatch(friend -> friend.getFriendId().equals(friendId));
}
}

View File

@ -6,10 +6,12 @@ import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.enums.UserEnums;
import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dos.ImMessage;
import cn.lili.modules.im.entity.dos.ImTalk;
import cn.lili.modules.im.entity.dto.MessageQueryParams; import cn.lili.modules.im.entity.dto.MessageQueryParams;
import cn.lili.modules.im.mapper.ImMessageMapper; import cn.lili.modules.im.mapper.ImMessageMapper;
import cn.lili.modules.im.service.ImMessageService; import cn.lili.modules.im.service.ImMessageService;
import cn.lili.modules.im.service.ImTalkService; import cn.lili.modules.im.service.ImTalkService;
import cn.lili.modules.im.websocket.WebSocketServer;
import cn.lili.mybatis.util.PageUtil; import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -19,9 +21,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Comparator; import java.util.*;
import java.util.List; import java.util.stream.Collectors;
import java.util.Objects;
import cn.lili.modules.im.entity.vo.ImMessageVO;
import cn.lili.modules.im.entity.dos.ImGroup;
import cn.lili.modules.im.mapper.ImGroupMapper;
import cn.lili.modules.im.entity.vo.MessageOperation;
import cn.lili.common.vo.ResultPageVO;
/** /**
* Im消息 业务实现 * Im消息 业务实现
@ -33,8 +40,26 @@ import java.util.Objects;
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) @RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage> implements ImMessageService { public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage> implements ImMessageService {
// @Autowired
// private final WebSocketServer imTalkService;
@Autowired @Autowired
private ImTalkService imTalkService; private ImGroupMapper imGroupMapper;
@Override
public List<ImMessageVO> getHistoryMessages(String talkId, String earliestMsgId, Integer pageSize) {
return null;
}
@Override
public List<ImMessageVO> getRecentMessages(String talkId, String latestMsgId, Integer pageSize) {
return null;
}
@Override
public ImMessageVO sendMessage(String toId, String content, String type) {
return null;
}
@Override @Override
public void read(String talkId, String accessToken) { public void read(String talkId, String accessToken) {
@ -43,6 +68,7 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
updateWrapper.eq(ImMessage::getTalkId, talkId); updateWrapper.eq(ImMessage::getTalkId, talkId);
updateWrapper.eq(ImMessage::getToUser, userId); updateWrapper.eq(ImMessage::getToUser, userId);
updateWrapper.set(ImMessage::getIsRead, true); updateWrapper.set(ImMessage::getIsRead, true);
// updateWrapper.set(ImMessage::getReadTime,new Date());
this.update(updateWrapper); this.update(updateWrapper);
} }
@ -51,7 +77,7 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
String userId = UserContext.getAuthUser(accessToken).getId(); String userId = UserContext.getAuthUser(accessToken).getId();
LambdaQueryWrapper<ImMessage> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ImMessage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ImMessage::getToUser, userId); queryWrapper.eq(ImMessage::getToUser, userId);
queryWrapper.eq(ImMessage::getIsRead, false); // queryWrapper.eq(ImMessage::getIsRead, false);
return this.list(queryWrapper); return this.list(queryWrapper);
} }
@ -77,12 +103,23 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
@Override @Override
public List<ImMessage> getList(MessageQueryParams messageQueryParams) { public List<ImMessage> getList(MessageQueryParams messageQueryParams) {
List<ImMessage> messageList = this.page(PageUtil.initPage(messageQueryParams), messageQueryParams.initQueryWrapper()).getRecords(); // 使用 LambdaQueryWrapper 创建查询条件
LambdaQueryWrapper<ImMessage> queryWrapper = messageQueryParams.initQueryWrapper();
// 关联查询假设 ImTalkMapper 是你的 Mapper
queryWrapper.eq(ImMessage::getTalkId, messageQueryParams.getTalkId());
// 执行分页查询
List<ImMessage> messageList = this.page(PageUtil.initPage(messageQueryParams), queryWrapper).getRecords();
// 对消息列表进行排序和已读处理
ListSort(messageList); ListSort(messageList);
readMessage(messageList); readMessage(messageList);
return messageList; return messageList;
} }
@Override @Override
public Long unreadMessageCount() { public Long unreadMessageCount() {
AuthUser currentUser = UserContext.getCurrentUser(); AuthUser currentUser = UserContext.getCurrentUser();
@ -101,6 +138,63 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
this.update(new LambdaUpdateWrapper<ImMessage>().eq(ImMessage::getToUser,currentUser.getId()).set(ImMessage::getIsRead,true)); this.update(new LambdaUpdateWrapper<ImMessage>().eq(ImMessage::getToUser,currentUser.getId()).set(ImMessage::getIsRead,true));
} }
@Override
public ResultPageVO<ImMessageVO> getGroupMessages(String groupId, Integer pageNumber, Integer pageSize) {
List<ImMessage> messages = this.getMessagesByGroupId(groupId, pageNumber, pageSize);
List<ImMessageVO> messageVOs = messages.stream()
.map(message -> new ImMessageVO())
.collect(Collectors.toList());
Long total = this.count(new LambdaQueryWrapper<ImMessage>().eq(ImMessage::getGroupId, groupId));
return new ResultPageVO<>(total, (long) Math.ceil((double) total / pageSize), (long) pageNumber, (long) pageSize, messageVOs);
}
@Override
public ImMessageVO sendGroupMessage(String groupId, String content, String type) {
ImMessage imMessage = new ImMessage();
imMessage.setText(content);
imMessage.setType(type);
imMessage.setGroupId(groupId);
imMessage.setCreateTime(new Date());
this.save(imMessage);
return new ImMessageVO();
}
@Override
public void recallMessage(String messageId) {
}
@Override
public List<ImMessageVO> loadHistoryMessages(String talkId, String earliestMsgId, Integer size) {
return null;
}
@Override
public List<ImMessageVO> loadLatestMessages(String talkId, Integer size) {
return null;
}
@Override
public List<ImMessageVO> getNewerMessages(String talkId, String latestMsgId) {
return null;
}
@Override
public void readMessages(List<ImMessage> messages, String userId) {
}
private List<ImMessage> getMessagesByGroupId(String groupId, Integer pageNumber, Integer pageSize) {
return this.list(new LambdaQueryWrapper<ImMessage>().eq(ImMessage::getGroupId, groupId)
.orderByDesc(ImMessage::getCreateTime)
.last("LIMIT " + (pageNumber - 1) * pageSize + ", " + pageSize));
}
/** /**
* 根据时间倒叙 * 根据时间倒叙
* *
@ -144,6 +238,7 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
for (ImMessage imMessage : messageList) { for (ImMessage imMessage : messageList) {
if(Boolean.FALSE.equals(imMessage.getIsRead()) && imMessage.getToUser().equals(toUserId)){ if(Boolean.FALSE.equals(imMessage.getIsRead()) && imMessage.getToUser().equals(toUserId)){
imMessage.setIsRead(true); imMessage.setIsRead(true);
// imMessage.setReadTime(new Date());
} }
} }
} }

View File

@ -6,10 +6,12 @@ import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser; import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.enums.UserEnums;
import cn.lili.modules.im.entity.dos.Friend;
import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dos.ImMessage;
import cn.lili.modules.im.entity.dos.ImTalk; import cn.lili.modules.im.entity.dos.ImTalk;
import cn.lili.modules.im.entity.dto.IMTalkQueryParams; import cn.lili.modules.im.entity.dto.IMTalkQueryParams;
import cn.lili.modules.im.entity.vo.ImTalkVO; import cn.lili.modules.im.entity.vo.ImTalkVO;
import cn.lili.modules.im.mapper.FriendMapper;
import cn.lili.modules.im.mapper.ImTalkMapper; import cn.lili.modules.im.mapper.ImTalkMapper;
import cn.lili.modules.im.service.ImMessageService; import cn.lili.modules.im.service.ImMessageService;
import cn.lili.modules.im.service.ImTalkService; import cn.lili.modules.im.service.ImTalkService;
@ -20,33 +22,43 @@ import cn.lili.modules.store.service.StoreService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* 聊天 业务实现 * 聊天 业务实现
* *
* @author Chopper * @author Chopper
*/ */
@Slf4j
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) //@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> implements ImTalkService { public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> implements ImTalkService {
private final ImMessageService imMessageService;
@Autowired
public ImTalkServiceImpl(@Lazy ImMessageService imMessageService) {
this.imMessageService = imMessageService;
}
@Autowired
FriendMapper friendMapper;
@Autowired @Autowired
private MemberService memberService; private MemberService memberService;
@Autowired @Autowired
private StoreService storeService; private StoreService storeService;
@Autowired
private ImMessageService imMessageService;
public ImTalk getTalkByUser(String userId) { public ImTalk getTalkByUser(String userId) {
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser()); AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser());
@ -180,14 +192,52 @@ public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> impleme
if (authUser == null) { if (authUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN); throw new ServiceException(ResultCode.USER_NOT_LOGIN);
} }
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.and(wq -> wq.eq(ImTalk::getUserId1, authUser.getId()).or().eq(ImTalk::getUserId2, authUser.getId())); queryWrapper.and(wq -> wq.eq(ImTalk::getUserId1, authUser.getId()).or().eq(ImTalk::getUserId2, authUser.getId()));
if (CharSequenceUtil.isNotEmpty(imTalkQueryParams.getUserName())) { if (CharSequenceUtil.isNotEmpty(imTalkQueryParams.getUserName())) {
queryWrapper.and(wq -> wq.ne(ImTalk::getUserId1, authUser.getId()).like(ImTalk::getName1, imTalkQueryParams.getUserName()).or().ne(ImTalk::getUserId2, authUser.getId()).like(ImTalk::getName2, imTalkQueryParams.getUserName())); queryWrapper.and(wq -> wq.ne(ImTalk::getUserId1, authUser.getId())
.like(ImTalk::getName1, imTalkQueryParams.getUserName())
.or()
.ne(ImTalk::getUserId2, authUser.getId())
.like(ImTalk::getName2, imTalkQueryParams.getUserName()));
} }
queryWrapper.orderByDesc(ImTalk::getLastTalkTime); queryWrapper.orderByDesc(ImTalk::getLastTalkTime);
List<ImTalk> imTalks = this.list(queryWrapper); List<ImTalk> imTalks = this.list(queryWrapper);
List<ImTalkVO> imTalkVOList = imTalks.stream().map(imTalk -> new ImTalkVO(imTalk, authUser.getId())).collect(Collectors.toList());
// 获取好友关系
List<String> friendIds = imTalks.stream()
.flatMap(imTalk -> Stream.of(imTalk.getUserId1(), imTalk.getUserId2()))
.distinct()
.collect(Collectors.toList());
// 查询好友关系
List<Friend> friends = friendMapper.selectList(new LambdaQueryWrapper<Friend>()
.in(Friend::getFriendId, friendIds)
.eq(Friend::getUserId, authUser.getId())
.eq(Friend::getStatus, 1)); // 只查询状态为1的记录
// 创建一个 Set 来存储已关注的用户 ID
Set<String> mutualFriends = friends.stream()
.map(Friend::getFriendId)
.collect(Collectors.toSet());
// 更新 isMutual 字段
for (ImTalk imTalk : imTalks) {
if (mutualFriends.contains(imTalk.getUserId1()) || mutualFriends.contains(imTalk.getUserId2())) {
imTalk.setIsMutual(1); // 设置为单向关注
} else {
imTalk.setIsMutual(0); // 设置为未关注
}
}
// 将更新后的 imTalk 转换为 VO 对象
List<ImTalkVO> imTalkVOList = imTalks.stream()
.map(imTalk -> new ImTalkVO(imTalk, authUser.getId()))
.collect(Collectors.toList());
getUnread(imTalkVOList); getUnread(imTalkVOList);
return imTalkVOList; return imTalkVOList;
} }

View File

@ -1,7 +1,8 @@
package cn.lili.controller.im; package cn.lili.modules.im.websocket;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache; import cn.lili.cache.Cache;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser; import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.enums.UserEnums;
@ -13,8 +14,11 @@ import cn.lili.modules.im.entity.vo.MessageOperation;
import cn.lili.modules.im.entity.vo.MessageVO; import cn.lili.modules.im.entity.vo.MessageVO;
import cn.lili.modules.im.service.ImMessageService; import cn.lili.modules.im.service.ImMessageService;
import cn.lili.modules.im.service.ImTalkService; import cn.lili.modules.im.service.ImTalkService;
import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.service.MemberService;
import com.alibaba.druid.util.StringUtils; import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -40,6 +44,7 @@ import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) @RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WebSocketServer { public class WebSocketServer {
private final MemberService memberService;
/** /**
* 在线人数 PS 注意只能单节点如果多节点部署需要自行寻找方案 * 在线人数 PS 注意只能单节点如果多节点部署需要自行寻找方案
*/ */
@ -107,21 +112,36 @@ public class WebSocketServer {
* @param messageOperation * @param messageOperation
*/ */
private void operation(String accessToken, MessageOperation messageOperation) { private void operation(String accessToken, MessageOperation messageOperation) {
AuthUser authUser = UserContext.getAuthUser(accessToken); AuthUser authUser = UserContext.getAuthUser(accessToken);
switch (messageOperation.getOperationType()) { switch (messageOperation.getOperationType()) {
case PING: case PING:
break; break;
case MESSAGE: case MESSAGE:
//保存消息 // 获取或创建聊天记录
ImTalk imTalk = getOrCreateTalk(authUser.getId(), messageOperation.getTo());
// 如果 imTalk null说明创建失败记录日志并返回
if (imTalk == null) {
log.warn("Failed to create ImTalk for users: {} and {}", authUser.getId(), messageOperation.getTo());
return; // 或者抛出异常
}
// 保存消息
ImMessage imMessage = new ImMessage(messageOperation); ImMessage imMessage = new ImMessage(messageOperation);
imMessage.setCreateBy(authUser.getNickName());
imMessage.setTalkId(imTalk.getId()); // 设置 talk_id imTalk id
imMessageService.save(imMessage); imMessageService.save(imMessage);
//修改最后消息信息 log.info("Message saved: {}", imMessage);
imTalkService.update(new LambdaUpdateWrapper<ImTalk>().eq(ImTalk::getId, messageOperation.getTalkId())
.set(ImTalk::getLastTalkMessage, messageOperation.getContext()) // 更新最后消息信息
.set(ImTalk::getLastTalkTime, imMessage.getCreateTime()) imTalkService.update(new LambdaUpdateWrapper<ImTalk>()
.set(ImTalk::getLastMessageType, imMessage.getMessageType())); .eq(ImTalk::getId, imTalk.getId())
//发送消息 .set(ImTalk::getLastTalkMessage, messageOperation.getContext())
.set(ImTalk::getLastTalkTime, imMessage.getCreateTime())
.set(ImTalk::getLastMessageType, imMessage.getMessageType()));
// 发送消息
sendMessage(messageOperation.getTo(), new MessageVO(MessageResultType.MESSAGE, imMessage)); sendMessage(messageOperation.getTo(), new MessageVO(MessageResultType.MESSAGE, imMessage));
break; break;
case READ: case READ:
@ -131,17 +151,55 @@ public class WebSocketServer {
break; break;
case UNREAD: case UNREAD:
sendMessage(authUser.getId(), sendMessage(authUser.getId(),
new MessageVO(MessageResultType.UN_READ, imMessageService.unReadMessages(accessToken))); new MessageVO(MessageResultType.UN_READ, imMessageService.unReadMessages(accessToken)));
break; break;
case HISTORY: case HISTORY:
sendMessage(authUser.getId(), new MessageVO(MessageResultType.HISTORY, sendMessage(authUser.getId(), new MessageVO(MessageResultType.HISTORY,
imMessageService.historyMessage(accessToken, messageOperation.getTo()))); imMessageService.historyMessage(accessToken, messageOperation.getTo())));
break; break;
default: default:
break; break;
} }
} }
// 获取或创建聊天记录
public ImTalk getOrCreateTalk(String userId1, String userId2) {
// 查询现有的聊天记录
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper
.or().eq(ImTalk::getUserId1, userId1).eq(ImTalk::getUserId2, userId2)
.or().eq(ImTalk::getUserId1, userId2).eq(ImTalk::getUserId2, userId1);
ImTalk imTalk = imTalkService.getOne(queryWrapper);
if (imTalk != null) {
log.info("Found existing talk: {}", imTalk);
return imTalk;
}
// 如果没有找到创建新的聊天记录
log.info("Creating new talk between {} and {}", userId1, userId2);
// 获取用户信息
Member member1 = memberService.getById(userId1);
Member member2 = memberService.getById(userId2);
// 检查用户是否存在
if (member1 == null || member2 == null) {
log.warn("One of the members does not exist: member1={}, member2={}", member1, member2);
throw new ServiceException("One of the users does not exist."); // 或者根据需要处理
}
String face1 = member1.getFace() != null ? member1.getFace() : ""; // 获取头像
String face2 = member2.getFace() != null ? member2.getFace() : ""; // 获取头像
String name1 = member1.getNickName() != null ? member1.getNickName() : ""; // 获取昵称
String name2 = member2.getNickName() != null ? member2.getNickName() : ""; // 获取昵称
// 创建新的聊天记录
imTalk = new ImTalk(userId1, userId2, face1, face2, name1, name2);
imTalkService.save(imTalk);
log.info("New talk created: {}", imTalk);
return imTalk;
}
/** /**
* 发送消息 * 发送消息
* *

View File

@ -6,6 +6,8 @@ import cn.lili.common.security.sensitive.enums.SensitiveStrategy;
import cn.lili.common.utils.CommonUtil; import cn.lili.common.utils.CommonUtil;
import cn.lili.mybatis.BaseEntity; import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
@ -31,6 +33,24 @@ public class Member extends BaseEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private String id;
// 创建者
private String createBy;
// 创建时间
private Date createTime;
// 删除标志 true/false
private Boolean deleteFlag;
// 更新者
private String updateBy;
// 更新时间
private Date updateTime;
@ApiModelProperty(value = "会员用户名") @ApiModelProperty(value = "会员用户名")
private String username; private String username;
@ -97,13 +117,13 @@ public class Member extends BaseEntity {
@ApiModelProperty(value = "经验值数量") @ApiModelProperty(value = "经验值数量")
private Long experience; private Long experience;
public Member(String username, String password, String mobile) { public Member(String username, String password, String mobile) {
this.username = username; this.username = username;
this.password = password; this.password = password;
this.mobile = mobile; this.mobile = mobile;
this.nickName = CommonUtil.getSpecialStr("用户"); this.nickName = CommonUtil.getSpecialStr("用户");
this.disabled = true; this.disabled = true;
this.deleteFlag=false;
this.haveStore = false; this.haveStore = false;
this.sex = 0; this.sex = 0;
this.point = 0L; this.point = 0L;
@ -115,6 +135,7 @@ public class Member extends BaseEntity {
this.username = username; this.username = username;
this.password = password; this.password = password;
this.mobile = mobile; this.mobile = mobile;
this.deleteFlag=false;
this.nickName = nickName; this.nickName = nickName;
this.disabled = true; this.disabled = true;
this.haveStore = false; this.haveStore = false;

View File

@ -19,11 +19,6 @@
<artifactId>framework</artifactId> <artifactId>framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -2,8 +2,6 @@ package cn.lili;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/** /**
@ -15,17 +13,4 @@ public class ImApiApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ImApiApplication.class, args); SpringApplication.run(ImApiApplication.class, args);
} }
/**
* 如果使用独立的servlet容器
* 而不是直接使用springboot的内置容器
* 就不要注入ServerEndpointExporter
* 因为它将由容器自己提供和管理
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
} }

View File

@ -2,48 +2,49 @@ package cn.lili.controller.im;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil; import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.AuthUser;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.vo.ResultMessage; import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.im.entity.dos.Friend; import cn.lili.modules.im.entity.dos.Friend;
import cn.lili.modules.im.entity.vo.FriendVO; import cn.lili.modules.im.entity.vo.FriendVO;
import cn.lili.modules.im.service.FriendService; import cn.lili.modules.im.service.FriendService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.List;
/** /**
* @author Chopper * @author Chopper
*/ */
@RestController @RestController
@Api(tags = "关注管理接口")
@Api(tags = "好友管理接口")
@RequestMapping("/im/friend") @RequestMapping("/im/friend")
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) @RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FriendController { public class FriendController {
private final FriendService friendService; private final FriendService friendService;
@GetMapping("/search")
@ApiOperation(value = "搜索用户")
public ResultMessage<List<FriendVO>> searchUsers(
@ApiParam(value = "搜索关键词(用户名/手机号)") @RequestParam String keyword,
@ApiParam(value = "是否只搜索店铺") @RequestParam(required = false) Boolean onlyStore) {
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
List<FriendVO> users = friendService.searchUsers(keyword, onlyStore, currentUser.getId());
return ResultUtil.data(users);
}
@GetMapping("/mutual") @GetMapping("/following")
@ApiOperation(value = "查看所有互相关注好友") @ApiOperation(value = "获取关注列表")
public ResultMessage<List<FriendVO>> getMutualFriends() { public ResultMessage<List<FriendVO>> getFollowingList() {
AuthUser currentUser = UserContext.getCurrentUser(); AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) { if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN); throw new ServiceException(ResultCode.USER_NOT_LOGIN);
@ -52,49 +53,65 @@ public class FriendController {
return ResultUtil.data(friends); return ResultUtil.data(friends);
} }
@GetMapping("/{friendId}") @GetMapping("/user/{userId}")
@ApiOperation(value = "查看好友资料") @ApiOperation(value = "获取用户详情")
public ResultMessage<FriendVO> getFriendDetails(@PathVariable String friendId) { public ResultMessage<FriendVO> getUserDetails(
@ApiParam(value = "用户ID", required = true) @PathVariable String userId) {
AuthUser currentUser = UserContext.getCurrentUser(); AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) { if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN); throw new ServiceException(ResultCode.USER_NOT_LOGIN);
} }
FriendVO friend = friendService.getFriendDetails(friendId); FriendVO friend = friendService.getFriendDetails(userId);
return ResultUtil.data(friend); return ResultUtil.data(friend);
} }
@DeleteMapping("/{friendId}") @PostMapping("/follow/{userId}")
@ApiOperation(value = "解除好友关系") @ApiOperation(value = "关注用户")
public ResultMessage<Object> removeFriend(@PathVariable String friendId) { public ResultMessage<Object> followUser(
@ApiParam(value = "要关注的用户ID", required = true) @PathVariable String userId) {
AuthUser currentUser = UserContext.getCurrentUser(); AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) { if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN); throw new ServiceException(ResultCode.USER_NOT_LOGIN);
} }
friendService.removeFriend(currentUser.getId(), friendId); friendService.addFriend(currentUser.getId(), userId);
return ResultUtil.success(); return ResultUtil.success();
} }
@PostMapping("/add/{friendId}") @PostMapping("/follow-by-mobile")
@ApiOperation(value = "添加好友") @ApiOperation(value = "通过手机号关注用户")
public ResultMessage<Object> addFriend(@PathVariable String friendId) { public ResultMessage<Object> followByMobile(
@ApiParam(value = "手机号", required = true) @RequestParam String mobile,
@ApiParam(value = "备注") @RequestParam(required = false) String remark) {
AuthUser currentUser = UserContext.getCurrentUser(); AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) { if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN); throw new ServiceException(ResultCode.USER_NOT_LOGIN);
} }
friendService.addFriend(currentUser.getId(), friendId); friendService.addFriendByMobile(mobile, remark);
return ResultUtil.success(); return ResultUtil.success();
} }
@PutMapping("/remark/{friendId}") @DeleteMapping("/unfollow/{userId}")
@ApiOperation(value = "更新好友备注") @ApiOperation(value = "取消关注")
public ResultMessage<Object> updateRemark(@PathVariable String friendId, @RequestParam String remark) { public ResultMessage<Object> unfollowUser(
@ApiParam(value = "要取消关注的用户ID", required = true) @PathVariable String userId) {
AuthUser currentUser = UserContext.getCurrentUser(); AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) { if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN); throw new ServiceException(ResultCode.USER_NOT_LOGIN);
} }
friendService.updateRemark(currentUser.getId(), friendId, remark); friendService.removeFriend(currentUser.getId(), userId);
return ResultUtil.success(); return ResultUtil.success();
} }
@PutMapping("/remark/{userId}")
@ApiOperation(value = "更新备注")
public ResultMessage<Object> updateRemark(
@ApiParam(value = "用户ID", required = true) @PathVariable String userId,
@ApiParam(value = "备注", required = true) @RequestParam String remark) {
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
friendService.updateRemark(currentUser.getId(), userId, remark);
return ResultUtil.success();
}
} }

View File

@ -1,78 +1,182 @@
package cn.lili.controller.im; package cn.lili.controller.im;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil; import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.vo.ResultMessage; import cn.lili.common.vo.ResultMessage;
import cn.lili.common.vo.ResultPageVO;
import cn.lili.modules.im.entity.dos.ImGroup; import cn.lili.modules.im.entity.dos.ImGroup;
import cn.lili.modules.im.entity.vo.FriendVO;
import cn.lili.modules.im.entity.vo.ImGroupVO;
import cn.lili.modules.im.entity.vo.ImGroupMemberVO;
import cn.lili.modules.im.entity.vo.ImMessageVO;
import cn.lili.modules.im.service.ImGroupService; import cn.lili.modules.im.service.ImGroupService;
import cn.lili.modules.im.service.FriendService;
import cn.lili.modules.im.service.ImMessageService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
/**
* 群聊管理接口
*/
@RestController @RestController
@Api(tags = "群聊管理接口") @Api(tags = "群聊管理接口")
@RequestMapping("/im/group") @RequestMapping("/im/group")
@RequiredArgsConstructor @RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ImGroupController { public class ImGroupController {
private final ImGroupService imGroupService; private final ImGroupService imGroupService;
private final FriendService friendService;
private final ImMessageService imMessageService;
@GetMapping("/list")
@ApiOperation(value = "获取我的群聊列表")
public ResultMessage<List<ImGroupVO>> getMyGroups() {
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
List<ImGroupVO> groups = imGroupService.getUserGroups(currentUser.getId());
return ResultUtil.data(groups);
}
@GetMapping("/{groupId}")
@ApiOperation(value = "获取群聊详情")
public ResultMessage<ImGroupVO> getGroupDetail(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
ImGroupVO group = imGroupService.getGroupDetail(groupId);
return ResultUtil.data(group);
}
@GetMapping("/{groupId}/members")
@ApiOperation(value = "获取群成员列表")
public ResultMessage<List<ImGroupMemberVO>> getGroupMembers(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
List<ImGroupMemberVO> members = imGroupService.getGroupMembers(groupId);
return ResultUtil.data(members);
}
@DeleteMapping("/{groupId}/quit")
@ApiOperation(value = "退出群聊")
public ResultMessage<Void> quitGroup(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
imGroupService.quitGroup(groupId, currentUser.getId());
return ResultUtil.success();
}
@PutMapping("/{groupId}")
@ApiOperation(value = "修改群信息")
public ResultMessage<Void> updateGroupInfo(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "群名称") @RequestParam(required = false) String groupName,
@ApiParam(value = "群公告") @RequestParam(required = false) String notice,
@ApiParam(value = "群头像") @RequestParam(required = false) String avatar) {
imGroupService.updateGroupInfo(groupId, groupName, notice, avatar);
return ResultUtil.success();
}
@GetMapping("/{groupId}/messages")
@ApiOperation(value = "获取群聊消息历史")
public ResultMessage<ResultPageVO<ImMessageVO>> getGroupMessages(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "页码") @RequestParam(defaultValue = "1") Integer pageNumber,
@ApiParam(value = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) {
ResultPageVO<ImMessageVO> messages = imMessageService.getGroupMessages(groupId, pageNumber, pageSize);
return ResultUtil.data(messages);
}
@PostMapping("/{groupId}/message")
@ApiOperation(value = "发送群聊消息")
public ResultMessage<ImMessageVO> sendGroupMessage(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "消息内容", required = true) @RequestParam String content,
@ApiParam(value = "消息类型", required = true) @RequestParam String type) {
ImMessageVO message = imMessageService.sendGroupMessage(groupId, content, type);
return ResultUtil.data(message);
}
@GetMapping("/friends")
@ApiOperation(value = "获取可邀请的好友列表")
public ResultMessage<List<FriendVO>> getInvitableFriends() {
AuthUser currentUser = UserContext.getCurrentUser();
if (currentUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
List<FriendVO> friends = friendService.getMutualFriends(currentUser.getId());
return ResultUtil.data(friends);
}
@PostMapping @PostMapping
@ApiOperation(value = "创建群聊") @ApiOperation(value = "创建群聊")
public ResultMessage<ImGroup> createGroup(@RequestParam String groupName, public ResultMessage<ImGroup> createGroup(
@RequestParam(required = false) List<String> memberIds) { @ApiParam(value = "群名称", required = true) @RequestParam String groupName,
@ApiParam(value = "初始成员ID列表") @RequestParam(required = false) List<String> memberIds) {
ImGroup group = imGroupService.createGroup(groupName, memberIds); ImGroup group = imGroupService.createGroup(groupName, memberIds);
return ResultUtil.data(group); return ResultUtil.data(group);
} }
@DeleteMapping("/{groupId}") @DeleteMapping("/{groupId}")
@ApiOperation(value = "解散群聊") @ApiOperation(value = "解散群聊")
public ResultMessage<Void> dismissGroup(@PathVariable String groupId) { public ResultMessage<Void> dismissGroup(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
imGroupService.dismissGroup(groupId); imGroupService.dismissGroup(groupId);
return ResultUtil.success(); return ResultUtil.success();
} }
@PostMapping("/{groupId}/invite") @PostMapping("/{groupId}/invite")
@ApiOperation(value = "邀请成员") @ApiOperation(value = "邀请成员")
public ResultMessage<Void> inviteMembers(@PathVariable String groupId, public ResultMessage<Void> inviteMembers(
@RequestParam List<String> memberIds) { @ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "成员ID列表", required = true) @RequestParam List<String> memberIds) {
imGroupService.inviteMembers(groupId, memberIds); imGroupService.inviteMembers(groupId, memberIds);
return ResultUtil.success(); return ResultUtil.success();
} }
@PostMapping("/{groupId}/admin/{memberId}") @PostMapping("/{groupId}/admin/{memberId}")
@ApiOperation(value = "设置管理员") @ApiOperation(value = "设置管理员")
public ResultMessage<Void> setAdmin(@PathVariable String groupId, public ResultMessage<Void> setAdmin(
@PathVariable String memberId) { @ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId) {
imGroupService.setAdmin(groupId, memberId); imGroupService.setAdmin(groupId, memberId);
return ResultUtil.success(); return ResultUtil.success();
} }
@DeleteMapping("/{groupId}/admin/{memberId}") @DeleteMapping("/{groupId}/admin/{memberId}")
@ApiOperation(value = "取消管理员") @ApiOperation(value = "取消管理员")
public ResultMessage<Void> removeAdmin(@PathVariable String groupId, public ResultMessage<Void> removeAdmin(
@PathVariable String memberId) { @ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId) {
imGroupService.removeAdmin(groupId, memberId); imGroupService.removeAdmin(groupId, memberId);
return ResultUtil.success(); return ResultUtil.success();
} }
@PostMapping("/{groupId}/mute/{memberId}") @PostMapping("/{groupId}/mute/{memberId}")
@ApiOperation(value = "禁言成员") @ApiOperation(value = "禁言成员")
public ResultMessage<Void> muteMember(@PathVariable String groupId, public ResultMessage<Void> muteMember(
@PathVariable String memberId, @ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@RequestParam Integer duration) { @ApiParam(value = "成员ID", required = true) @PathVariable String memberId,
@ApiParam(value = "禁言时长(分钟)", required = true) @RequestParam Integer duration) {
imGroupService.muteMember(groupId, memberId, duration); imGroupService.muteMember(groupId, memberId, duration);
return ResultUtil.success(); return ResultUtil.success();
} }
@DeleteMapping("/{groupId}/mute/{memberId}") @DeleteMapping("/{groupId}/mute/{memberId}")
@ApiOperation(value = "解除成员禁言") @ApiOperation(value = "解除成员禁言")
public ResultMessage<Void> unmuteMember(@PathVariable String groupId, public ResultMessage<Void> unmuteMember(
@PathVariable String memberId) { @ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId) {
imGroupService.unmuteMember(groupId, memberId); imGroupService.unmuteMember(groupId, memberId);
return ResultUtil.success(); return ResultUtil.success();
} }

View File

@ -4,11 +4,14 @@ import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil; import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.common.vo.ResultMessage; import cn.lili.common.vo.ResultMessage;
import cn.lili.common.vo.ResultPageVO;
import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dos.ImMessage;
import cn.lili.modules.im.entity.dto.MessageQueryParams; import cn.lili.modules.im.entity.dto.MessageQueryParams;
import cn.lili.modules.im.entity.vo.ImMessageVO;
import cn.lili.modules.im.service.ImMessageService; import cn.lili.modules.im.service.ImMessageService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -87,4 +90,24 @@ public class ImMessageController {
imMessageService.cleanUnreadMessage(); imMessageService.cleanUnreadMessage();
return ResultUtil.success(); return ResultUtil.success();
} }
@GetMapping("/{groupId}/messages")
@ApiOperation(value = "获取群聊消息历史")
public ResultMessage<ResultPageVO<ImMessageVO>> getGroupMessages(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "页码") @RequestParam(defaultValue = "1") Integer pageNumber,
@ApiParam(value = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) {
ResultPageVO<ImMessageVO> messages = imMessageService.getGroupMessages(groupId, pageNumber, pageSize);
return ResultUtil.data(messages);
}
@PostMapping("/{groupId}/message")
@ApiOperation(value = "发送群聊消息")
public ResultMessage<ImMessageVO> sendGroupMessage(
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
@ApiParam(value = "消息内容", required = true) @RequestParam String content,
@ApiParam(value = "消息类型", required = true) @RequestParam String type) {
ImMessageVO message = imMessageService.sendGroupMessage(groupId, content, type);
return ResultUtil.data(message);
}
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.