diff --git a/buyer-api/src/main/resources/application.yml b/buyer-api/src/main/resources/application.yml index 6d9b3c86..e6ca7b4e 100644 --- a/buyer-api/src/main/resources/application.yml +++ b/buyer-api/src/main/resources/application.yml @@ -73,9 +73,9 @@ spring: default-datasource: type: com.alibaba.druid.pool.DruidDataSource 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 - username: root - password: lilishop + url: jdbc:mysql://82.156.121.2:23306/shop_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: wzj + password: A085F27A43B0 maxActive: 50 initialSize: 10 maxWait: 60000 diff --git a/framework/pom.xml b/framework/pom.xml index 8dcc90f7..891fe545 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -116,10 +116,10 @@ commons-pool2 - - - - + + org.springframework.boot + spring-boot-starter-websocket + com.baomidou diff --git a/framework/src/main/java/cn/lili/common/vo/PageVO.java b/framework/src/main/java/cn/lili/common/vo/PageVO.java index c782a6aa..b0cb8f49 100644 --- a/framework/src/main/java/cn/lili/common/vo/PageVO.java +++ b/framework/src/main/java/cn/lili/common/vo/PageVO.java @@ -1,12 +1,20 @@ package cn.lili.common.vo; -import cn.hutool.core.text.CharSequenceUtil; -import cn.lili.common.utils.StringUtils; import io.swagger.annotations.ApiModelProperty; + import lombok.Data; 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; } - } + diff --git a/framework/src/main/java/cn/lili/common/vo/ResultMessage.java b/framework/src/main/java/cn/lili/common/vo/ResultMessage.java index 2268ef23..fd5abf18 100644 --- a/framework/src/main/java/cn/lili/common/vo/ResultMessage.java +++ b/framework/src/main/java/cn/lili/common/vo/ResultMessage.java @@ -1,6 +1,6 @@ package cn.lili.common.vo; - +import cn.lili.common.enums.ResultCode; import lombok.Data; import java.io.Serializable; @@ -39,4 +39,81 @@ public class ResultMessage implements Serializable { * 结果对象 */ 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 ResultMessage success(T result) { + return new ResultMessage<>(true, ResultCode.SUCCESS.code(), ResultCode.SUCCESS.message(), result); + } + + /** + * 构建成功消息 + * @param message 消息 + * @param result 结果 + * @return 消息对象 + */ + public static ResultMessage success(String message, T result) { + return new ResultMessage<>(true, ResultCode.SUCCESS.code(), message, result); + } + + /** + * 构建错误消息 + * @param code 错误码 + * @return 消息对象 + */ + public static ResultMessage error(Integer code) { + return new ResultMessage<>(false, code, null, null); + } + + /** + * 构建错误消息 + * @param code 错误码 + * @param message 错误消息 + * @return 消息对象 + */ + public static ResultMessage error(Integer code, String message) { + return new ResultMessage<>(false, code, message, null); + } + + /** + * 构建错误消息 + * @param resultCode 结果枚举 + * @return 消息对象 + */ + public static ResultMessage error(ResultCode resultCode) { + return new ResultMessage<>(false, resultCode.code(), resultCode.message(), null); + } + + /** + * 构建错误消息 + * @param resultCode 结果枚举 + * @param message 错误消息 + * @return 消息对象 + */ + public static ResultMessage error(ResultCode resultCode, String message) { + return new ResultMessage<>(false, resultCode.code(), message, null); + } } diff --git a/framework/src/main/java/cn/lili/common/vo/ResultPageVO.java b/framework/src/main/java/cn/lili/common/vo/ResultPageVO.java new file mode 100644 index 00000000..24796792 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/vo/ResultPageVO.java @@ -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 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 records; + + public ResultPageVO() { + } + + public ResultPageVO(Long total, Long pages, Long currentPage, Long size, List records) { + this.total = total; + this.pages = pages; + this.currentPage = currentPage; + this.size = size; + this.records = records; + } + + public static ResultPageVO empty() { + return new ResultPageVO<>(0L, 0L, 1L, 10L, null); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/controller/im/ImGroupController.java b/framework/src/main/java/cn/lili/controller/im/ImGroupController.java deleted file mode 100644 index 0519ecba..00000000 --- a/framework/src/main/java/cn/lili/controller/im/ImGroupController.java +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java b/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java index d9f825c0..0bbf684c 100644 --- a/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java +++ b/framework/src/main/java/cn/lili/modules/im/config/CustomSpringConfigurator.java @@ -1,10 +1,10 @@ package cn.lili.modules.im.config; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; import javax.websocket.server.ServerEndpointConfig; @@ -15,6 +15,7 @@ import javax.websocket.server.ServerEndpointConfig; * @version v1.0 * 2021-12-31 11:53 */ +@Component public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware { /** @@ -22,13 +23,13 @@ public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator */ private static volatile BeanFactory context; - @Override - public T getEndpointInstance(Class clazz) throws InstantiationException { - return context.getBean(clazz); - } - @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { CustomSpringConfigurator.context = applicationContext; } + + @Override + public T getEndpointInstance(Class clazz) throws InstantiationException { + return context.getBean(clazz); + } } diff --git a/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfig.java b/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfig.java new file mode 100644 index 00000000..70d733f7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfig.java @@ -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(); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java b/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java index c6de471b..e69de29b 100644 --- a/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java +++ b/framework/src/main/java/cn/lili/modules/im/config/WebSocketConfigurator.java @@ -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(); - } -} diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/Friend.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/Friend.java index 19b45a63..18082509 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/dos/Friend.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/Friend.java @@ -1,39 +1,82 @@ 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.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; + +import java.io.Serializable; import java.util.Date; + + + @Data + @TableName("li_friend") -public class Friend { - + +@ApiModel(value = "好友关系") + +public class Friend implements Serializable { + + + + private static final long serialVersionUID = 1L; + + + @TableId(type = IdType.ASSIGN_ID) + + @ApiModelProperty(value = "ID") + private String id; - - // 用户ID + + + + @ApiModelProperty(value = "用户ID") + private String userId; - - // 好友ID + + + + @ApiModelProperty(value = "好友ID") + private String friendId; - - // 好友昵称 - private String nickname; - - // 好友头像 - private String avatar; - - // 好友备注 + + + + @ApiModelProperty(value = "好友备注") + private String remark; - - // 关系状态 (0:待确认 1:已确认 2:已拒绝 3:已解除) + + + + @ApiModelProperty(value = "状态 1:已关注") + private Integer status; - - // 创建时间 + + + + @ApiModelProperty(value = "是否是店铺 0:否 1:是") + + private Integer storeFlag; + + + + @ApiModelProperty(value = "创建时间") + private Date createTime; - - // 更新时间 + + + + @ApiModelProperty(value = "更新时间") + private Date updateTime; -} \ No newline at end of file + @ApiModelProperty(value = "是否关注") + private Integer isMutual; +} + diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImGroupMember.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImGroupMember.java index 44151239..449550cb 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImGroupMember.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImGroupMember.java @@ -1,38 +1,51 @@ package cn.lili.modules.im.entity.dos; +import cn.lili.mybatis.BaseEntity; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.Date; @Data @TableName("li_im_group_member") -public class ImGroupMember { - @TableId(type = IdType.ASSIGN_ID) +@ApiModel(value = "群成员") +public class ImGroupMember extends BaseEntity { + + private static final long serialVersionUID = 1L; + @ApiModelProperty(value = "成员ID") private String id; - // 群ID + @ApiModelProperty(value = "群ID") private String groupId; - // 成员ID + @ApiModelProperty(value = "成员ID") private String memberId; - // 成员昵称 + @ApiModelProperty(value = "成员昵称") private String nickname; - // 成员角色(0:普通成员 1:管理员 2:群主) + @ApiModelProperty(value = "角色(0:普通成员 1:管理员 2:群主)") private Integer role; - // 是否被禁言(0:否 1:是) - private Integer isMuted; + @ApiModelProperty(value = "是否被禁言") + private int isMuted; - // 禁言结束时间 + @ApiModelProperty(value = "禁言结束时间") private Date muteEndTime; - // 加入时间 + @ApiModelProperty(value = "加入时间") private Date joinTime; - // 更新时间 + @ApiModelProperty(value = "创建者") + private String createBy; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "更新时间") private Date updateTime; -} \ No newline at end of file + + @ApiModelProperty(value = "删除标志") + private Boolean deleteFlag; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMember.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMember.java new file mode 100644 index 00000000..f089becc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMember.java @@ -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; + + // 其他需要的字段... +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java index f381faca..d3b9d94a 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImMessage.java @@ -4,8 +4,10 @@ import cn.lili.common.utils.SnowFlake; import cn.lili.modules.im.entity.enums.MessageTypeEnum; import cn.lili.modules.im.entity.vo.MessageOperation; import cn.lili.mybatis.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -17,48 +19,83 @@ import java.util.Date; */ @Data @TableName("li_im_message") -@ApiModel(value = "Im消息") +@ApiModel(value = "即时通讯消息") @NoArgsConstructor @AllArgsConstructor public class ImMessage extends BaseEntity { private static final long serialVersionUID = 1L; - - /** - * 发送者 - */ + @TableField("talk_id") + @ApiModelProperty(value = "会话ID") + private String talkId; + @TableField("from_user") + @ApiModelProperty(value = "发送者ID") private String fromUser; - - /** - * 接收者 - */ + @TableField("to_user") + @ApiModelProperty(value = "接收者ID") private String toUser; - /** - * 已阅 - */ - private Boolean isRead; - - /** - * 消息类型 - */ - private MessageTypeEnum messageType; - - /** - * 聊天id - */ - private String talkId; - - /** - * 消息实体 - */ + @ApiModelProperty(value = "消息内容") 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){ this.setFromUser(messageOperation.getFrom()); this.setMessageType(messageOperation.getMessageType()); this.setIsRead(false); +// this.setReadTime(new Date()); this.setText(messageOperation.getContext()); this.setTalkId(messageOperation.getTalkId()); this.setCreateTime(new Date()); @@ -66,4 +103,11 @@ public class ImMessage extends BaseEntity { this.setId(SnowFlake.getIdStr()); } + public void setMessageType(MessageTypeEnum messageType) { + this.messageType = messageType; +// if (messageType != null) { +// this.messageType = MessageTypeEnum.valueOf(messageType.getType()); // 确保type字段也使用大写 +// } + } + } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java index 7452756a..510f7da9 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java @@ -80,6 +80,8 @@ public class ImTalk extends BaseTenantEntity { @ApiModelProperty(value = "坐席名称") private String tenantName; + @ApiModelProperty(value = "是否关注") + private Integer isMutual; public ImTalk(String userId1, String userId2, @@ -135,6 +137,14 @@ public class ImTalk extends BaseTenantEntity { 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){ if(Long.parseLong(member.getId()) > Long.parseLong(store.getId())){ this.userId1 = store.getId(); diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/Member.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/Member.java new file mode 100644 index 00000000..022422fe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/Member.java @@ -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; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/Store.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/Store.java new file mode 100644 index 00000000..e2ee1421 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/Store.java @@ -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 + + // 其他字段可以根据需要添加 +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java b/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java index f9743302..0df2be31 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/dto/MessageQueryParams.java @@ -6,9 +6,13 @@ import cn.lili.common.exception.ServiceException; import cn.lili.common.vo.PageVO; import cn.lili.modules.im.entity.dos.ImMessage; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; +import java.util.Date; + /** * MessageQueryParams * @@ -18,6 +22,7 @@ import lombok.EqualsAndHashCode; */ @EqualsAndHashCode(callSuper = true) @Data +@ApiModel(value = "消息查询参数") public class MessageQueryParams extends PageVO { private static final long serialVersionUID = 3504156704697214077L; @@ -25,15 +30,39 @@ public class MessageQueryParams extends PageVO { /** * 聊天窗口 */ + @ApiModelProperty(value = "会话ID") 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; + /** + * 消息类型(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 initQueryWrapper() { if (CharSequenceUtil.isEmpty(talkId)) { @@ -45,8 +74,11 @@ public class MessageQueryParams extends PageVO { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(ImMessage::getTalkId, talkId); - if (CharSequenceUtil.isNotEmpty(lastMessageId)) { - lambdaQueryWrapper.lt(ImMessage::getId, lastMessageId); + if (CharSequenceUtil.isNotEmpty(earliestMsgId)) { + lambdaQueryWrapper.ge(ImMessage::getId, earliestMsgId); + } + if (CharSequenceUtil.isNotEmpty(latestMsgId)) { + lambdaQueryWrapper.le(ImMessage::getId, latestMsgId); } lambdaQueryWrapper.orderByDesc(ImMessage::getCreateTime); // lambdaQueryWrapper.last("limit " + num); diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java index 687c814c..b727f9ff 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageStatusEnum.java @@ -1,31 +1,53 @@ 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 { - //socket刚打开时发送的消息,这个一般是是刚打开socket链接,进行登录,传入token用 - CONNECT, - //心跳类型的消息,此种类型的消息只有 type 、 text 两种属性 - HEARTBEAT, - //用户打开一个对话框,准备跟某人聊天时 - OPEN, - //客服进行自动回复。客户端发起这种类型请求,则是在拉取对方是否有自动回复,如果有,服务端就会给客户端发送过自动回复的信息 - AUTO_REPLY, - //正常收发消息沟通,文字、表情等沟通 - MSG, - //扩展。比如发送商品、发送订单 - EXTEND, - //系统提示,如提示 对方已离线 - SYSTEM, - //服务端发送到客户端,用于设置客户端的用户信息。会吧 com.xnx3.yunkefu.core.vo.bean.User 传过去 - SET_USER, - //结束服务 - CLOSE_SERVICE; + + NORMAL(0, "正常"), + DELETED(1, "已删除"), + RECALLED(2, "已撤回"), + + // WebSocket相关状态 + CONNECT(100, "连接"), + HEARTBEAT(101, "心跳"), + OPEN(102, "打开对话"), + AUTO_REPLY(103, "自动回复"), + MSG(104, "普通消息"), + EXTEND(105, "扩展消息"), + SYSTEM(106, "系统消息"), + SET_USER(107, "设置用户"), + CLOSE_SERVICE(108, "结束服务"); + 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; + } } diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java index 6b06d3b7..0ab162d9 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/MessageTypeEnum.java @@ -1,23 +1,45 @@ package cn.lili.modules.im.entity.enums; /** - * 消息类型 - * - * @author liushuai + * 消息类型枚举 */ public enum MessageTypeEnum { - /** - * 消息类型枚举 - *

- * 普通消息 - * 图片 - * 语音 - * 视频 - */ - MESSAGE, - PICTURE, - VOICE, - GOODS, - ORDER, - VIDEO + MESSAGE("message", "普通消息"), + TEXT("TEXT", "文本消息"), + IMAGE("IMAGE", "图片消息"), + FILE("FILE", "文件消息"), + VOICE("VOICE", "语音消息"), + VIDEO("VIDEO", "视频消息"), + SYSTEM("SYSTEM", "系统消息"), + GROUP("GROUP", "群聊消息"), + LOCATION("LOCATION", "位置消息"); + + private final String type; + private final String description; + + 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; + } } diff --git a/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java b/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java index 5909222e..9d8c37bc 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/enums/OperationType.java @@ -21,6 +21,5 @@ public enum OperationType { MESSAGE, HISTORY, READ, - UNREAD, - + UNREAD } diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendRequestVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendRequestVO.java new file mode 100644 index 00000000..631b6ab5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendRequestVO.java @@ -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; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendVO.java index fe2a1635..3191e93f 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendVO.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/FriendVO.java @@ -4,18 +4,64 @@ import cn.lili.modules.im.entity.dos.Friend; import lombok.Data; 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; -} \ No newline at end of file +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; + + +} + diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/ImGroupMemberVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImGroupMemberVO.java new file mode 100644 index 00000000..b81004de --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImGroupMemberVO.java @@ -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; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/ImGroupVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImGroupVO.java new file mode 100644 index 00000000..f8e5ebf8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImGroupVO.java @@ -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; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/ImMessageVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImMessageVO.java new file mode 100644 index 00000000..e767a976 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImMessageVO.java @@ -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; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java index 6d80a8be..a78ce2d3 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/ImTalkVO.java @@ -53,6 +53,8 @@ public class ImTalkVO extends BaseTenantEntity { @ApiModelProperty(value = "未读数量") private Long unread; + @ApiModelProperty(value = "是否关注") + private Integer isMutual; public ImTalkVO() { @@ -66,6 +68,7 @@ public class ImTalkVO extends BaseTenantEntity { name = imTalk.getName1(); face = imTalk.getFace1(); storeFlag = imTalk.getStoreFlag1(); + isMutual=imTalk.getIsMutual(); } else { userId = imTalk.getUserId2(); top = imTalk.getTop2(); @@ -73,6 +76,7 @@ public class ImTalkVO extends BaseTenantEntity { name = imTalk.getName2(); face = imTalk.getFace2(); storeFlag = imTalk.getStoreFlag2(); + isMutual=imTalk.getIsMutual(); } lastTalkMessage = imTalk.getLastTalkMessage(); lastTalkTime = imTalk.getLastTalkTime(); diff --git a/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java b/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java index b4360bc6..e3dcb51b 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/vo/MessageOperation.java @@ -41,13 +41,13 @@ public class MessageOperation { public void setOperationType(String operationType) { if (!StringUtils.isEmpty(operationType)) { - this.operationType = OperationType.valueOf(operationType); + this.operationType = OperationType.valueOf(operationType.toUpperCase()); } } public void setMessageType(String messageType) { if (!StringUtils.isEmpty(messageType)) { - this.messageType = MessageTypeEnum.valueOf(messageType); + this.messageType = MessageTypeEnum.valueOf(messageType.toUpperCase()); } } } diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/FriendMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/FriendMapper.java index 2268608b..25c50d6e 100644 --- a/framework/src/main/java/cn/lili/modules/im/mapper/FriendMapper.java +++ b/framework/src/main/java/cn/lili/modules/im/mapper/FriendMapper.java @@ -2,9 +2,15 @@ package cn.lili.modules.im.mapper; import cn.lili.modules.im.entity.dos.Friend; + import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.mapstruct.Mapper; + +import org.apache.ibatis.annotations.Mapper; + + @Mapper + public interface FriendMapper extends BaseMapper { + } diff --git a/framework/src/main/java/cn/lili/modules/im/mapper/ImGroupMemberMapper.java b/framework/src/main/java/cn/lili/modules/im/mapper/ImGroupMemberMapper.java index 0c9b8d04..a9a889c6 100644 --- a/framework/src/main/java/cn/lili/modules/im/mapper/ImGroupMemberMapper.java +++ b/framework/src/main/java/cn/lili/modules/im/mapper/ImGroupMemberMapper.java @@ -1,8 +1,13 @@ package cn.lili.modules.im.mapper; import cn.lili.modules.im.entity.dos.ImGroupMember; -import cn.lili.modules.im.entity.dos.ImMessage; + import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + + +@Mapper public interface ImGroupMemberMapper extends BaseMapper { + } diff --git a/framework/src/main/java/cn/lili/modules/im/service/FriendService.java b/framework/src/main/java/cn/lili/modules/im/service/FriendService.java index 542009c5..4130bd9a 100644 --- a/framework/src/main/java/cn/lili/modules/im/service/FriendService.java +++ b/framework/src/main/java/cn/lili/modules/im/service/FriendService.java @@ -1,88 +1,88 @@ package cn.lili.modules.im.service; - import cn.lili.modules.im.entity.dos.Friend; - import cn.lili.modules.im.entity.vo.FriendVO; - +import cn.lili.modules.im.entity.vo.FriendRequestVO; import com.baomidou.mybatisplus.extension.service.IService; - import java.util.List; - - +/** + * 好友关系业务层 + * + * @author Chopper + */ public interface FriendService extends IService { - - - + /** - - * 获取互相关注的好友列表(包含详细信息) - + * 获取关注列表 * @param userId 用户ID - - * @return 好友列表 - + * @return 关注的用户列表 */ - List getMutualFriends(String userId); - - - + /** - - * 获取好友详细信息 - - * @param friendId 好友ID - - * @return 好友详细信息 - + * 获取用户详细信息 + * @param friendId 用户ID + * @return 用户详细信息 */ - FriendVO getFriendDetails(String friendId); - - - + /** - - * 解除好友关系 - - * @param userId 用户ID - - * @param friendId 好友ID - + * 取消关注 + * @param userId 当前用户ID + * @param friendId 要取消关注的用户ID */ - void removeFriend(String userId, String friendId); - - - + /** - - * 添加好友 - - * @param userId 用户ID - - * @param friendId 好友ID - + * 关注用户 + * @param userId 当前用户ID + * @param friendId 要关注的用户ID */ - void addFriend(String userId, String friendId); - - - + /** - - * 更新好友备注 - - * @param userId 用户ID - - * @param friendId 好友ID - + * 通过手机号关注用户 + * @param mobile 手机号 + * @param remark 备注 + */ + void addFriendByMobile(String mobile, String remark); + + /** + * 更新备注 + * @param userId 当前用户ID + * @param friendId 关注的用户ID * @param remark 备注 - */ - void updateRemark(String userId, String friendId, String remark); - +// +// /** +// * 获取好友申请列表 +// * @param userId 用户ID +// * @return 申请列表 +// */ +// List 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 searchUsers(String keyword, Boolean onlyStore, String currentUserId); } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/ImGroupService.java b/framework/src/main/java/cn/lili/modules/im/service/ImGroupService.java index 23a4454b..8c8de02f 100644 --- a/framework/src/main/java/cn/lili/modules/im/service/ImGroupService.java +++ b/framework/src/main/java/cn/lili/modules/im/service/ImGroupService.java @@ -1,58 +1,154 @@ package cn.lili.modules.im.service; 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 java.util.List; + + public interface ImGroupService extends IService { + /** - * 创建群聊 - * @param groupName 群名称 - * @param memberIds 邀请的成员ID列表 - * @return 群聊信息 + * 获取用户加入的群聊列表 + * @param userId 用户ID + * @return 群聊列表 */ + List getUserGroups(String userId); + + /** + * 获取群聊详情 + * @param groupId 群ID + * @return 群聊详情 + */ + ImGroupVO getGroupDetail(String groupId); + + /** + * 获取群成员列表 + * @param groupId 群ID + * @return 群成员列表 + */ + List 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 memberIds); + + /** + * 解散群聊 + * @param groupId 群ID + */ + void dismissGroup(String groupId); + + /** + * 邀请成员 + * @param groupId 群ID + * @param memberIds 成员ID列表 + */ + void inviteMembers(String groupId, List memberIds); + + /** + * 设置管理员 + * @param groupId 群ID + * @param memberId 成员ID + */ + void setAdmin(String groupId, String memberId); + + /** + * 取消管理员 + * @param groupId 群ID + * @param memberId 成员ID + */ + 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 memberId 成员ID + + * @param duration 禁言时长(分钟) + */ + + void muteMember(String groupId, String memberId, Integer duration); + + + + /** + + * 解除成员禁言 + + * @param groupId 群ID + + * @param memberId 成员ID + + */ + void unmuteMember(String groupId, String memberId); + } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java b/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java index 823369c0..9fb9f43a 100644 --- a/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java +++ b/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java @@ -1,7 +1,10 @@ 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.dto.MessageQueryParams; +import cn.lili.modules.im.entity.vo.ImMessageVO; import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; @@ -13,6 +16,33 @@ import java.util.List; */ public interface ImMessageService extends IService { + /** + * 获取历史消息 + * @param talkId 对话ID + * @param earliestMsgId 最早消息ID + * @param pageSize 每页大小 + * @return 消息列表 + */ + List getHistoryMessages(String talkId, String earliestMsgId, Integer pageSize); + + /** + * 获取最近消息 + * @param talkId 对话ID + * @param latestMsgId 最新消息ID + * @param pageSize 每页大小 + * @return 消息列表 + */ + List 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 { * 清空所有未读消息 */ void cleanUnreadMessage(); + + /** + * 获取群聊消息历史 + * @param groupId 群ID + * @param pageNumber 页码 + * @param pageSize 每页大小 + * @return 消息列表 + */ + ResultPageVO 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 loadHistoryMessages(String talkId, String earliestMsgId, Integer size); + + /** + * 加载最新消息 + * @param talkId 会话ID + * @param size 每页大小 + * @return 消息列表 + */ + List loadLatestMessages(String talkId, Integer size); + + /** + * 获取新消息 + * @param talkId 会话ID + * @param latestMsgId 最新消息ID + * @return 新消息列表 + */ + List getNewerMessages(String talkId, String latestMsgId); + + /** + * 标记消息已读 + * @param messages 消息列表 + * @param userId 用户ID + */ + void readMessages(List messages, String userId); } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/MessageSender.java b/framework/src/main/java/cn/lili/modules/im/service/MessageSender.java new file mode 100644 index 00000000..6be477b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/im/service/MessageSender.java @@ -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); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/FriendServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/FriendServiceImpl.java index 540e79d2..154a163b 100644 --- a/framework/src/main/java/cn/lili/modules/im/serviceimpl/FriendServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/FriendServiceImpl.java @@ -1,344 +1,404 @@ 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.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.ImTalk; 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.service.FriendService; - +import cn.lili.modules.im.service.ImTalkService; 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.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; 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.stereotype.Service; 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.List; -import java.util.ArrayList; - -import static cn.hutool.extra.pinyin.PinyinUtil.getFirstLetter; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +/** + * 好友业务层实现 + * + * @author Chopper + */ @Slf4j @Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class FriendServiceImpl extends ServiceImpl implements FriendService { - @Autowired - private MemberMapper memberMapper; + private final ImTalkService imTalkService; + private final MemberService memberService; + private final FriendMapper friendMapper; @Override public List getMutualFriends(String userId) { - // 获取好友关系列表 + // 验证用户登录状态 + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + // 获取当前用户的ID + String currentUserId = currentUser.getId(); + + // 构建查询条件 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Friend::getUserId, currentUserId) // 查询当前用户的关注列表 + .eq(Friend::getStatus, 1); // 只查询状态为1的记录 + + // 查询关注列表 + List friends = this.list(queryWrapper); + if (friends.isEmpty()) { + return new ArrayList<>(); // 如果没有找到,返回空列表 + } + + // 收集所有关注的用户ID + List 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 members = memberService.listByIds(friendIds); + Map 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 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 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Friend::getUserId, userId) - .eq(Friend::getStatus, 1); // 已确认的好友关系 - List friends = this.list(queryWrapper); + .eq(Friend::getFriendId, friendId); - - // 转换为 FriendVO 列表 - List friendVOs = new ArrayList<>(); - 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); + // 如果已经关注,抛出异常 + if (this.count(queryWrapper) > 0) { + throw new ServiceException("您已经关注过该用户"); } - // 按照昵称首字母排序 - 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 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 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 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Friend::getUserId, userId) .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 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)) { - - throw new ServiceException("解除好友关系失败"); - + throw new ServiceException("取消关注失败"); } - } - - @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) { - - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - - queryWrapper.eq(Friend::getUserId, userId) - - .eq(Friend::getFriendId, friendId); - - - - Friend friend = this.getOne(queryWrapper); - - if (friend == null) { - - throw new ServiceException("好友关系不存在"); - + // 验证用户登录状态 + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); } - - - friend.setRemark(remark); - - friend.setUpdateTime(new Date()); - - - - if (!this.updateById(friend)) { - - throw new ServiceException("更新好友备注失败"); - + // 验证操作权限 + if (!currentUser.getId().equals(userId)) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); } - } - - - - /** - - * 检查是否已经是好友关系 - - */ - - private boolean checkFriendship(String userId, String friendId) { - + // 更新备注 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(Friend::getUserId, userId) - .eq(Friend::getFriendId, friendId) - .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() + .eq(Friend::getUserId, currentUserId) + .eq(Friend::getFriendId, targetUserId) + .eq(Friend::getIsMutual, 1)); // 确保状态为已关注 + + if (count > 0) { + // 查询目标用户是否也关注当前用户 + Long mutualCount = friendMapper.selectCount(new LambdaQueryWrapper() + .eq(Friend::getUserId, targetUserId) + .eq(Friend::getFriendId, currentUserId) + .eq(Friend::getIsMutual, 1)); // 确保状态为已关注 + + return mutualCount > 0 ? 2 : 1; // 2 表示互相关注,1 表示单向关注 + } + + return 0; // 未关注 + } + + @Override + public List searchUsers(String keyword, Boolean onlyStore, String currentUserId) { + // 构建查询条件 + LambdaQueryWrapper 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 members = memberService.list(queryWrapper); + if (members.isEmpty()) { + return new ArrayList<>(); + } + + // 获取已关注的用户ID + List memberIds = members.stream() + .map(member -> member.getId().toString()) + .distinct() // 确保唯一 + .collect(Collectors.toList()); + + // 查询当前用户与这些用户的关注关系 + LambdaQueryWrapper friendWrapper = new LambdaQueryWrapper<>(); + friendWrapper.eq(Friend::getUserId, currentUserId) + .in(Friend::getFriendId, memberIds) + .eq(Friend::getStatus, 1); + List friends = this.list(friendWrapper); + + // 构建已关注用户的Map + Map 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()); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImGroupServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImGroupServiceImpl.java index 9a0a1e87..29add1d0 100644 --- a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImGroupServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImGroupServiceImpl.java @@ -1,50 +1,216 @@ 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.exception.ServiceException; import cn.lili.modules.im.entity.dos.ImGroup; 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.ImGroupMemberMapper; 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.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.stream.Collectors; @Service -@RequiredArgsConstructor +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class ImGroupServiceImpl extends ServiceImpl implements ImGroupService { private final ImGroupMemberMapper groupMemberMapper; + private final MemberMapper memberMapper; + private final FriendService friendService; @Override - @Transactional(rollbackFor = Exception.class) - public ImGroup createGroup(String groupName, List memberIds) { + public List getUserGroups(String userId) { + // 查询用户加入的群组 + LambdaQueryWrapper memberQuery = new LambdaQueryWrapper<>(); + memberQuery.eq(ImGroupMember::getMemberId, userId); + List groupMembers = groupMemberMapper.selectList(memberQuery); + + if (groupMembers.isEmpty()) { + return new ArrayList<>(); + } + + // 获取群组ID列表 + List groupIds = groupMembers.stream() + .map(ImGroupMember::getGroupId) + .collect(Collectors.toList()); + + // 批量查询群组信息 + List 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 getGroupMembers(String groupId) { + // 查询群成员 + LambdaQueryWrapper memberQuery = new LambdaQueryWrapper<>(); + memberQuery.eq(ImGroupMember::getGroupId, groupId) + .orderByDesc(ImGroupMember::getRole) + .orderByAsc(ImGroupMember::getJoinTime); + + List members = groupMemberMapper.selectList(memberQuery); + + if (members.isEmpty()) { + return new ArrayList<>(); + } + + // 获取成员用户信息 + List memberIds = members.stream() + .map(ImGroupMember::getMemberId) + .collect(Collectors.toList()); + + List users = memberMapper.selectBatchIds(memberIds); + Map 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 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(); + LambdaQueryWrapper 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 memberIds) { + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } // 创建群组 ImGroup group = new ImGroup(); group.setName(groupName); - group.setOwnerId(currentUserId); + group.setOwnerId(currentUser.getId()); group.setCreateTime(new Date()); group.setUpdateTime(new Date()); - + 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()) { for (String memberId : memberIds) { - addGroupMember(group.getId(), memberId, 0); + addGroupMember(group.getId(), memberId, 0); // 0表示普通成员 } } @@ -52,20 +218,23 @@ public class ImGroupServiceImpl extends ServiceImpl impl } @Override - @Transactional(rollbackFor = Exception.class) 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); - if (owner == null || owner.getRole() != 2) { - throw new ServiceException("只有群主能解散群聊"); + // 验证是否是群主 + String currentUserId = UserContext.getCurrentUser().getId(); + if (!group.getOwnerId().equals(currentUserId)) { + throw new ServiceException("只有群主能解散群组"); } // 删除群成员 - LambdaQueryWrapper memberWrapper = new LambdaQueryWrapper<>(); - memberWrapper.eq(ImGroupMember::getGroupId, groupId); - groupMemberMapper.delete(memberWrapper); + LambdaQueryWrapper memberQuery = new LambdaQueryWrapper<>(); + memberQuery.eq(ImGroupMember::getGroupId, groupId); + groupMemberMapper.delete(memberQuery); // 删除群组 this.removeById(groupId); @@ -75,13 +244,13 @@ public class ImGroupServiceImpl extends ServiceImpl impl @Transactional(rollbackFor = Exception.class) public void inviteMembers(String groupId, List memberIds) { String currentUserId = UserContext.getCurrentUser().getId(); - + // 检查权限 ImGroupMember operator = getGroupMember(groupId, currentUserId); if (operator == null || operator.getRole() < 1) { throw new ServiceException("没有邀请权限"); } - + // 添加成员 for (String memberId : memberIds) { if (getGroupMember(groupId, memberId) == null) { @@ -94,19 +263,19 @@ public class ImGroupServiceImpl extends ServiceImpl impl @Transactional(rollbackFor = Exception.class) public void setAdmin(String groupId, String memberId) { String currentUserId = UserContext.getCurrentUser().getId(); - + // 检查是否是群主 ImGroupMember owner = getGroupMember(groupId, currentUserId); if (owner == null || owner.getRole() != 2) { throw new ServiceException("只有群主能设置管理员"); } - + // 设置管理员 ImGroupMember member = getGroupMember(groupId, memberId); if (member == null) { throw new ServiceException("该成员不在群中"); } - + member.setRole(1); member.setUpdateTime(new Date()); groupMemberMapper.updateById(member); @@ -116,19 +285,19 @@ public class ImGroupServiceImpl extends ServiceImpl impl @Transactional(rollbackFor = Exception.class) public void removeAdmin(String groupId, String memberId) { String currentUserId = UserContext.getCurrentUser().getId(); - + // 检查是否是群主 ImGroupMember owner = getGroupMember(groupId, currentUserId); if (owner == null || owner.getRole() != 2) { throw new ServiceException("只有群主能取消管理员"); } - + // 取消管理员 ImGroupMember member = getGroupMember(groupId, memberId); if (member == null) { throw new ServiceException("该成员不在群中"); } - + member.setRole(0); member.setUpdateTime(new Date()); groupMemberMapper.updateById(member); @@ -138,24 +307,24 @@ public class ImGroupServiceImpl extends ServiceImpl impl @Transactional(rollbackFor = Exception.class) public void muteMember(String groupId, String memberId, Integer duration) { String currentUserId = UserContext.getCurrentUser().getId(); - + // 检查操作权限 ImGroupMember operator = getGroupMember(groupId, currentUserId); if (operator == null || operator.getRole() < 1) { throw new ServiceException("没有禁言权限"); } - + // 检查被禁言成员 ImGroupMember member = getGroupMember(groupId, memberId); if (member == null) { throw new ServiceException("该成员不在群中"); } - + // 管理员不能禁言群主和其他管理员 if (operator.getRole() == 1 && member.getRole() > 0) { throw new ServiceException("没有权限禁言该成员"); } - + // 设置禁言 member.setIsMuted(1); member.setMuteEndTime(new Date(System.currentTimeMillis() + duration * 60 * 1000)); @@ -167,24 +336,24 @@ public class ImGroupServiceImpl extends ServiceImpl impl @Transactional(rollbackFor = Exception.class) public void unmuteMember(String groupId, String memberId) { String currentUserId = UserContext.getCurrentUser().getId(); - + // 检查操作权限 ImGroupMember operator = getGroupMember(groupId, currentUserId); if (operator == null || operator.getRole() < 1) { throw new ServiceException("没有解除禁言权限"); } - + // 检查被解除禁言成员 ImGroupMember member = getGroupMember(groupId, memberId); if (member == null) { throw new ServiceException("该成员不在群中"); } - + // 管理员不能解除群主和其他管理员的禁言 if (operator.getRole() == 1 && member.getRole() > 0) { throw new ServiceException("没有权限解除该成员的禁言"); } - + // 解除禁言 member.setIsMuted(0); member.setMuteEndTime(null); @@ -192,6 +361,15 @@ public class ImGroupServiceImpl extends ServiceImpl impl groupMemberMapper.updateById(member); } + /** + * 获取群成员数量 + */ + private Integer getGroupMemberCount(String groupId) { + LambdaQueryWrapper countQuery = new LambdaQueryWrapper<>(); + countQuery.eq(ImGroupMember::getGroupId, groupId); + return Math.toIntExact(groupMemberMapper.selectCount(countQuery)); + } + /** * 添加群成员 */ @@ -200,9 +378,9 @@ public class ImGroupServiceImpl extends ServiceImpl impl member.setGroupId(groupId); member.setMemberId(memberId); member.setRole(role); - member.setIsMuted(0); member.setJoinTime(new Date()); - member.setUpdateTime(new Date()); + member.setIsMuted(0); + groupMemberMapper.insert(member); } @@ -212,7 +390,16 @@ public class ImGroupServiceImpl extends ServiceImpl impl private ImGroupMember getGroupMember(String groupId, String memberId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ImGroupMember::getGroupId, groupId) - .eq(ImGroupMember::getMemberId, memberId); + .eq(ImGroupMember::getMemberId, memberId); return groupMemberMapper.selectOne(queryWrapper); } -} \ No newline at end of file + + /** + * 检查是否是好友关系 + */ + private boolean isFriend(String userId, String friendId) { + List friends = friendService.getMutualFriends(userId); + return friends.stream() + .anyMatch(friend -> friend.getFriendId().equals(friendId)); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java index 6e75529d..758d31d3 100644 --- a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java @@ -6,10 +6,12 @@ import cn.lili.common.security.AuthUser; import cn.lili.common.security.context.UserContext; import cn.lili.common.security.enums.UserEnums; 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.mapper.ImMessageMapper; import cn.lili.modules.im.service.ImMessageService; import cn.lili.modules.im.service.ImTalkService; +import cn.lili.modules.im.websocket.WebSocketServer; import cn.lili.mybatis.util.PageUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.transaction.annotation.Transactional; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; +import java.util.*; +import java.util.stream.Collectors; + +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消息 业务实现 @@ -33,8 +40,26 @@ import java.util.Objects; @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class ImMessageServiceImpl extends ServiceImpl implements ImMessageService { +// @Autowired +// private final WebSocketServer imTalkService; + @Autowired - private ImTalkService imTalkService; + private ImGroupMapper imGroupMapper; + + @Override + public List getHistoryMessages(String talkId, String earliestMsgId, Integer pageSize) { + return null; + } + + @Override + public List getRecentMessages(String talkId, String latestMsgId, Integer pageSize) { + return null; + } + + @Override + public ImMessageVO sendMessage(String toId, String content, String type) { + return null; + } @Override public void read(String talkId, String accessToken) { @@ -43,6 +68,7 @@ public class ImMessageServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ImMessage::getToUser, userId); - queryWrapper.eq(ImMessage::getIsRead, false); +// queryWrapper.eq(ImMessage::getIsRead, false); return this.list(queryWrapper); } @@ -77,12 +103,23 @@ public class ImMessageServiceImpl extends ServiceImpl getList(MessageQueryParams messageQueryParams) { - List messageList = this.page(PageUtil.initPage(messageQueryParams), messageQueryParams.initQueryWrapper()).getRecords(); + // 使用 LambdaQueryWrapper 创建查询条件 + LambdaQueryWrapper queryWrapper = messageQueryParams.initQueryWrapper(); + + // 关联查询,假设 ImTalkMapper 是你的 Mapper 类 + queryWrapper.eq(ImMessage::getTalkId, messageQueryParams.getTalkId()); + + // 执行分页查询 + List messageList = this.page(PageUtil.initPage(messageQueryParams), queryWrapper).getRecords(); + + // 对消息列表进行排序和已读处理 ListSort(messageList); readMessage(messageList); + return messageList; } + @Override public Long unreadMessageCount() { AuthUser currentUser = UserContext.getCurrentUser(); @@ -101,6 +138,63 @@ public class ImMessageServiceImpl extends ServiceImpl().eq(ImMessage::getToUser,currentUser.getId()).set(ImMessage::getIsRead,true)); } + @Override + public ResultPageVO getGroupMessages(String groupId, Integer pageNumber, Integer pageSize) { + List messages = this.getMessagesByGroupId(groupId, pageNumber, pageSize); + + List messageVOs = messages.stream() + .map(message -> new ImMessageVO()) + .collect(Collectors.toList()); + + Long total = this.count(new LambdaQueryWrapper().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 loadHistoryMessages(String talkId, String earliestMsgId, Integer size) { + return null; + } + + @Override + public List loadLatestMessages(String talkId, Integer size) { + return null; + } + + @Override + public List getNewerMessages(String talkId, String latestMsgId) { + return null; + } + + @Override + public void readMessages(List messages, String userId) { + + } + + private List getMessagesByGroupId(String groupId, Integer pageNumber, Integer pageSize) { + return this.list(new LambdaQueryWrapper().eq(ImMessage::getGroupId, groupId) + .orderByDesc(ImMessage::getCreateTime) + .last("LIMIT " + (pageNumber - 1) * pageSize + ", " + pageSize)); + } + /** * 根据时间倒叙 * @@ -144,6 +238,7 @@ public class ImMessageServiceImpl extends ServiceImpl implements ImTalkService { + private final ImMessageService imMessageService; + @Autowired + public ImTalkServiceImpl(@Lazy ImMessageService imMessageService) { + this.imMessageService = imMessageService; + } +@Autowired +FriendMapper friendMapper; @Autowired private MemberService memberService; @Autowired private StoreService storeService; - @Autowired - private ImMessageService imMessageService; - public ImTalk getTalkByUser(String userId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser()); @@ -180,14 +192,52 @@ public class ImTalkServiceImpl extends ServiceImpl impleme if (authUser == null) { throw new ServiceException(ResultCode.USER_NOT_LOGIN); } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.and(wq -> wq.eq(ImTalk::getUserId1, authUser.getId()).or().eq(ImTalk::getUserId2, authUser.getId())); + 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); List imTalks = this.list(queryWrapper); - List imTalkVOList = imTalks.stream().map(imTalk -> new ImTalkVO(imTalk, authUser.getId())).collect(Collectors.toList()); + + // 获取好友关系 + List friendIds = imTalks.stream() + .flatMap(imTalk -> Stream.of(imTalk.getUserId1(), imTalk.getUserId2())) + .distinct() + .collect(Collectors.toList()); + + // 查询好友关系 + List friends = friendMapper.selectList(new LambdaQueryWrapper() + .in(Friend::getFriendId, friendIds) + .eq(Friend::getUserId, authUser.getId()) + .eq(Friend::getStatus, 1)); // 只查询状态为1的记录 + + // 创建一个 Set 来存储已关注的用户 ID + Set 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 imTalkVOList = imTalks.stream() + .map(imTalk -> new ImTalkVO(imTalk, authUser.getId())) + .collect(Collectors.toList()); + getUnread(imTalkVOList); return imTalkVOList; } diff --git a/im-api/src/main/java/cn/lili/controller/im/WebSocketServer.java b/framework/src/main/java/cn/lili/modules/im/websocket/WebSocketServer.java similarity index 62% rename from im-api/src/main/java/cn/lili/controller/im/WebSocketServer.java rename to framework/src/main/java/cn/lili/modules/im/websocket/WebSocketServer.java index 509b2706..70a5b48c 100644 --- a/im-api/src/main/java/cn/lili/controller/im/WebSocketServer.java +++ b/framework/src/main/java/cn/lili/modules/im/websocket/WebSocketServer.java @@ -1,7 +1,8 @@ -package cn.lili.controller.im; +package cn.lili.modules.im.websocket; import cn.hutool.json.JSONUtil; import cn.lili.cache.Cache; +import cn.lili.common.exception.ServiceException; import cn.lili.common.security.AuthUser; import cn.lili.common.security.context.UserContext; 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.service.ImMessageService; 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.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; @@ -40,6 +44,7 @@ import org.springframework.stereotype.Component; @Slf4j @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class WebSocketServer { + private final MemberService memberService; /** * 在线人数 PS 注意,只能单节点,如果多节点部署需要自行寻找方案 */ @@ -107,21 +112,36 @@ public class WebSocketServer { * @param messageOperation */ private void operation(String accessToken, MessageOperation messageOperation) { - AuthUser authUser = UserContext.getAuthUser(accessToken); + switch (messageOperation.getOperationType()) { case PING: break; 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.setCreateBy(authUser.getNickName()); + imMessage.setTalkId(imTalk.getId()); // 设置 talk_id 为 imTalk 的 id imMessageService.save(imMessage); - //修改最后消息信息 - imTalkService.update(new LambdaUpdateWrapper().eq(ImTalk::getId, messageOperation.getTalkId()) - .set(ImTalk::getLastTalkMessage, messageOperation.getContext()) - .set(ImTalk::getLastTalkTime, imMessage.getCreateTime()) - .set(ImTalk::getLastMessageType, imMessage.getMessageType())); - //发送消息 + log.info("Message saved: {}", imMessage); + + // 更新最后消息信息 + imTalkService.update(new LambdaUpdateWrapper() + .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)); break; case READ: @@ -131,17 +151,55 @@ public class WebSocketServer { break; case UNREAD: sendMessage(authUser.getId(), - new MessageVO(MessageResultType.UN_READ, imMessageService.unReadMessages(accessToken))); + new MessageVO(MessageResultType.UN_READ, imMessageService.unReadMessages(accessToken))); break; case HISTORY: sendMessage(authUser.getId(), new MessageVO(MessageResultType.HISTORY, - imMessageService.historyMessage(accessToken, messageOperation.getTo()))); + imMessageService.historyMessage(accessToken, messageOperation.getTo()))); break; default: break; } } + // 获取或创建聊天记录 + public ImTalk getOrCreateTalk(String userId1, String userId2) { + // 查询现有的聊天记录 + LambdaQueryWrapper 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; + } /** * 发送消息 * diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java index 8027050a..6763f87a 100644 --- a/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java @@ -6,6 +6,8 @@ import cn.lili.common.security.sensitive.enums.SensitiveStrategy; import cn.lili.common.utils.CommonUtil; import cn.lili.mybatis.BaseEntity; 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 io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -31,6 +33,24 @@ public class Member extends BaseEntity { 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 = "会员用户名") private String username; @@ -97,13 +117,13 @@ public class Member extends BaseEntity { @ApiModelProperty(value = "经验值数量") private Long experience; - public Member(String username, String password, String mobile) { this.username = username; this.password = password; this.mobile = mobile; this.nickName = CommonUtil.getSpecialStr("用户"); this.disabled = true; + this.deleteFlag=false; this.haveStore = false; this.sex = 0; this.point = 0L; @@ -115,6 +135,7 @@ public class Member extends BaseEntity { this.username = username; this.password = password; this.mobile = mobile; + this.deleteFlag=false; this.nickName = nickName; this.disabled = true; this.haveStore = false; diff --git a/im-api/pom.xml b/im-api/pom.xml index 8f6efc47..7f823cf4 100644 --- a/im-api/pom.xml +++ b/im-api/pom.xml @@ -19,11 +19,6 @@ framework ${revision} - - - org.springframework.boot - spring-boot-starter-websocket - diff --git a/im-api/src/main/java/cn/lili/ImApiApplication.java b/im-api/src/main/java/cn/lili/ImApiApplication.java index 67078506..34728ae4 100644 --- a/im-api/src/main/java/cn/lili/ImApiApplication.java +++ b/im-api/src/main/java/cn/lili/ImApiApplication.java @@ -2,8 +2,6 @@ package cn.lili; import org.springframework.boot.SpringApplication; 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) { SpringApplication.run(ImApiApplication.class, args); } - - /** - * 如果使用独立的servlet容器, - * 而不是直接使用springboot的内置容器, - * 就不要注入ServerEndpointExporter, - * 因为它将由容器自己提供和管理 - * - * @return - */ - @Bean - public ServerEndpointExporter serverEndpointExporter() { - return new ServerEndpointExporter(); - } } diff --git a/im-api/src/main/java/cn/lili/controller/im/FriendController.java b/im-api/src/main/java/cn/lili/controller/im/FriendController.java index a318ae17..c3923c96 100644 --- a/im-api/src/main/java/cn/lili/controller/im/FriendController.java +++ b/im-api/src/main/java/cn/lili/controller/im/FriendController.java @@ -2,48 +2,49 @@ package cn.lili.controller.im; import cn.lili.common.enums.ResultCode; 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.AuthUser; +import cn.lili.common.exception.ServiceException; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.im.entity.dos.Friend; import cn.lili.modules.im.entity.vo.FriendVO; import cn.lili.modules.im.service.FriendService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; -import java.util.List; - - /** - * @author Chopper - */ - @RestController - -@Api(tags = "好友管理接口") - +@Api(tags = "关注管理接口") @RequestMapping("/im/friend") - @RequiredArgsConstructor(onConstructor = @__(@Autowired)) - public class FriendController { - - private final FriendService friendService; + @GetMapping("/search") + @ApiOperation(value = "搜索用户") + public ResultMessage> 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 users = friendService.searchUsers(keyword, onlyStore, currentUser.getId()); + return ResultUtil.data(users); + } - @GetMapping("/mutual") - @ApiOperation(value = "查看所有互相关注好友") - public ResultMessage> getMutualFriends() { + @GetMapping("/following") + @ApiOperation(value = "获取关注列表") + public ResultMessage> getFollowingList() { AuthUser currentUser = UserContext.getCurrentUser(); if (currentUser == null) { throw new ServiceException(ResultCode.USER_NOT_LOGIN); @@ -52,49 +53,65 @@ public class FriendController { return ResultUtil.data(friends); } - @GetMapping("/{friendId}") - @ApiOperation(value = "查看好友资料") - public ResultMessage getFriendDetails(@PathVariable String friendId) { + @GetMapping("/user/{userId}") + @ApiOperation(value = "获取用户详情") + public ResultMessage getUserDetails( + @ApiParam(value = "用户ID", required = true) @PathVariable String userId) { AuthUser currentUser = UserContext.getCurrentUser(); if (currentUser == null) { throw new ServiceException(ResultCode.USER_NOT_LOGIN); } - FriendVO friend = friendService.getFriendDetails(friendId); + FriendVO friend = friendService.getFriendDetails(userId); return ResultUtil.data(friend); } - @DeleteMapping("/{friendId}") - @ApiOperation(value = "解除好友关系") - public ResultMessage removeFriend(@PathVariable String friendId) { + @PostMapping("/follow/{userId}") + @ApiOperation(value = "关注用户") + public ResultMessage followUser( + @ApiParam(value = "要关注的用户ID", required = true) @PathVariable String userId) { AuthUser currentUser = UserContext.getCurrentUser(); if (currentUser == null) { throw new ServiceException(ResultCode.USER_NOT_LOGIN); } - friendService.removeFriend(currentUser.getId(), friendId); + friendService.addFriend(currentUser.getId(), userId); return ResultUtil.success(); } - @PostMapping("/add/{friendId}") - @ApiOperation(value = "添加好友") - public ResultMessage addFriend(@PathVariable String friendId) { + @PostMapping("/follow-by-mobile") + @ApiOperation(value = "通过手机号关注用户") + public ResultMessage followByMobile( + @ApiParam(value = "手机号", required = true) @RequestParam String mobile, + @ApiParam(value = "备注") @RequestParam(required = false) String remark) { AuthUser currentUser = UserContext.getCurrentUser(); if (currentUser == null) { throw new ServiceException(ResultCode.USER_NOT_LOGIN); } - friendService.addFriend(currentUser.getId(), friendId); + friendService.addFriendByMobile(mobile, remark); return ResultUtil.success(); } - @PutMapping("/remark/{friendId}") - @ApiOperation(value = "更新好友备注") - public ResultMessage updateRemark(@PathVariable String friendId, @RequestParam String remark) { + @DeleteMapping("/unfollow/{userId}") + @ApiOperation(value = "取消关注") + public ResultMessage unfollowUser( + @ApiParam(value = "要取消关注的用户ID", required = true) @PathVariable String userId) { AuthUser currentUser = UserContext.getCurrentUser(); if (currentUser == null) { throw new ServiceException(ResultCode.USER_NOT_LOGIN); } - friendService.updateRemark(currentUser.getId(), friendId, remark); + friendService.removeFriend(currentUser.getId(), userId); return ResultUtil.success(); } - + @PutMapping("/remark/{userId}") + @ApiOperation(value = "更新备注") + public ResultMessage 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(); + } } \ No newline at end of file diff --git a/im-api/src/main/java/cn/lili/controller/im/ImGroupController.java b/im-api/src/main/java/cn/lili/controller/im/ImGroupController.java index 6de152e0..3da90bfb 100644 --- a/im-api/src/main/java/cn/lili/controller/im/ImGroupController.java +++ b/im-api/src/main/java/cn/lili/controller/im/ImGroupController.java @@ -1,78 +1,182 @@ package cn.lili.controller.im; - - +import cn.lili.common.enums.ResultCode; 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.ResultPageVO; 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.FriendService; +import cn.lili.modules.im.service.ImMessageService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; +/** + * 群聊管理接口 + */ @RestController @Api(tags = "群聊管理接口") @RequestMapping("/im/group") -@RequiredArgsConstructor +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class ImGroupController { private final ImGroupService imGroupService; + private final FriendService friendService; + private final ImMessageService imMessageService; + + @GetMapping("/list") + @ApiOperation(value = "获取我的群聊列表") + public ResultMessage> getMyGroups() { + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + List groups = imGroupService.getUserGroups(currentUser.getId()); + return ResultUtil.data(groups); + } + + @GetMapping("/{groupId}") + @ApiOperation(value = "获取群聊详情") + public ResultMessage 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> getGroupMembers( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId) { + List members = imGroupService.getGroupMembers(groupId); + return ResultUtil.data(members); + } + + @DeleteMapping("/{groupId}/quit") + @ApiOperation(value = "退出群聊") + public ResultMessage 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 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> getGroupMessages( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "页码") @RequestParam(defaultValue = "1") Integer pageNumber, + @ApiParam(value = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) { + ResultPageVO messages = imMessageService.getGroupMessages(groupId, pageNumber, pageSize); + return ResultUtil.data(messages); + } + + @PostMapping("/{groupId}/message") + @ApiOperation(value = "发送群聊消息") + public ResultMessage 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> getInvitableFriends() { + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + List friends = friendService.getMutualFriends(currentUser.getId()); + return ResultUtil.data(friends); + } @PostMapping @ApiOperation(value = "创建群聊") - public ResultMessage createGroup(@RequestParam String groupName, - @RequestParam(required = false) List memberIds) { + public ResultMessage createGroup( + @ApiParam(value = "群名称", required = true) @RequestParam String groupName, + @ApiParam(value = "初始成员ID列表") @RequestParam(required = false) List memberIds) { ImGroup group = imGroupService.createGroup(groupName, memberIds); return ResultUtil.data(group); } @DeleteMapping("/{groupId}") @ApiOperation(value = "解散群聊") - public ResultMessage dismissGroup(@PathVariable String groupId) { + public ResultMessage dismissGroup( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId) { imGroupService.dismissGroup(groupId); return ResultUtil.success(); } @PostMapping("/{groupId}/invite") @ApiOperation(value = "邀请成员") - public ResultMessage inviteMembers(@PathVariable String groupId, - @RequestParam List memberIds) { + public ResultMessage inviteMembers( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "成员ID列表", required = true) @RequestParam List memberIds) { imGroupService.inviteMembers(groupId, memberIds); return ResultUtil.success(); } @PostMapping("/{groupId}/admin/{memberId}") @ApiOperation(value = "设置管理员") - public ResultMessage setAdmin(@PathVariable String groupId, - @PathVariable String memberId) { + public ResultMessage setAdmin( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "成员ID", required = true) @PathVariable String memberId) { imGroupService.setAdmin(groupId, memberId); return ResultUtil.success(); } @DeleteMapping("/{groupId}/admin/{memberId}") @ApiOperation(value = "取消管理员") - public ResultMessage removeAdmin(@PathVariable String groupId, - @PathVariable String memberId) { + public ResultMessage removeAdmin( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "成员ID", required = true) @PathVariable String memberId) { imGroupService.removeAdmin(groupId, memberId); return ResultUtil.success(); } @PostMapping("/{groupId}/mute/{memberId}") @ApiOperation(value = "禁言成员") - public ResultMessage muteMember(@PathVariable String groupId, - @PathVariable String memberId, - @RequestParam Integer duration) { + public ResultMessage muteMember( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "成员ID", required = true) @PathVariable String memberId, + @ApiParam(value = "禁言时长(分钟)", required = true) @RequestParam Integer duration) { imGroupService.muteMember(groupId, memberId, duration); return ResultUtil.success(); } @DeleteMapping("/{groupId}/mute/{memberId}") @ApiOperation(value = "解除成员禁言") - public ResultMessage unmuteMember(@PathVariable String groupId, - @PathVariable String memberId) { + public ResultMessage unmuteMember( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "成员ID", required = true) @PathVariable String memberId) { imGroupService.unmuteMember(groupId, memberId); return ResultUtil.success(); } diff --git a/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java b/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java index ab562631..c804167c 100644 --- a/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java +++ b/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java @@ -4,11 +4,14 @@ import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultUtil; import cn.lili.common.exception.ServiceException; 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.dto.MessageQueryParams; +import cn.lili.modules.im.entity.vo.ImMessageVO; import cn.lili.modules.im.service.ImMessageService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -87,4 +90,24 @@ public class ImMessageController { imMessageService.cleanUnreadMessage(); return ResultUtil.success(); } + + @GetMapping("/{groupId}/messages") + @ApiOperation(value = "获取群聊消息历史") + public ResultMessage> getGroupMessages( + @ApiParam(value = "群ID", required = true) @PathVariable String groupId, + @ApiParam(value = "页码") @RequestParam(defaultValue = "1") Integer pageNumber, + @ApiParam(value = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) { + ResultPageVO messages = imMessageService.getGroupMessages(groupId, pageNumber, pageSize); + return ResultUtil.data(messages); + } + + @PostMapping("/{groupId}/message") + @ApiOperation(value = "发送群聊消息") + public ResultMessage 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); + } } diff --git a/im-api/src/main/java/cn/lili/modules/member/entity/Member.java b/im-api/src/main/java/cn/lili/modules/member/entity/Member.java deleted file mode 100644 index 0519ecba..00000000 --- a/im-api/src/main/java/cn/lili/modules/member/entity/Member.java +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/im-api/src/main/java/cn/lili/modules/member/mapper/MemberMapper.java b/im-api/src/main/java/cn/lili/modules/member/mapper/MemberMapper.java deleted file mode 100644 index 0519ecba..00000000 --- a/im-api/src/main/java/cn/lili/modules/member/mapper/MemberMapper.java +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/xxl-job/xxl-job/xxl-job-admin.log.2025-02-24.zip b/xxl-job/xxl-job/xxl-job-admin.log.2025-02-24.zip new file mode 100644 index 00000000..6e80bedd Binary files /dev/null and b/xxl-job/xxl-job/xxl-job-admin.log.2025-02-24.zip differ diff --git a/xxl-job/xxl-job/xxl-job-admin.log.2025-02-25.zip b/xxl-job/xxl-job/xxl-job-admin.log.2025-02-25.zip new file mode 100644 index 00000000..aed7df11 Binary files /dev/null and b/xxl-job/xxl-job/xxl-job-admin.log.2025-02-25.zip differ diff --git a/xxl-job/xxl-job/xxl-job-admin.log.2025-02-26.zip b/xxl-job/xxl-job/xxl-job-admin.log.2025-02-26.zip new file mode 100644 index 00000000..f63c8ca3 Binary files /dev/null and b/xxl-job/xxl-job/xxl-job-admin.log.2025-02-26.zip differ diff --git a/xxl-job/xxl-job/xxl-job-admin.log.2025-03-03.zip b/xxl-job/xxl-job/xxl-job-admin.log.2025-03-03.zip new file mode 100644 index 00000000..cfb60ebf Binary files /dev/null and b/xxl-job/xxl-job/xxl-job-admin.log.2025-03-03.zip differ