IM:code review 消息发送的实现

This commit is contained in:
YunaiV 2024-03-30 22:01:44 +08:00
parent 84cea03752
commit 2b891cb432
32 changed files with 99 additions and 90 deletions

View File

@ -6,6 +6,7 @@ import lombok.Getter;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
/**
* IM 会话类型枚举
* 参考 <a href="https://doc.rentsoft.cn/zh-Hans/sdks/enum/conversationType">会话类型</a> 文档
@ -38,7 +39,7 @@ public enum ConversationTypeEnum implements IntArrayValuable {
}
/**
* 生成会话编号
* 生成会话编号 TODO @anhaohao方法注释和下面参数之间要有空格
* @param fromUserId 发送者编号
* @param receiverId 接收者编号
* @param conversationType 会话类型

View File

@ -6,7 +6,7 @@ import lombok.Getter;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
/**
* IM 消息的类型枚举
* <p>
@ -34,11 +34,11 @@ public enum MessageContentTypeEnum implements IntArrayValuable {
QUOTE(114, "引用消息"),
FACE(115, "表情消息"),
ADVANCED_REVOKE(118, "高级撤回消息"),
//好友通知 1200-1299
// ========== 好友通知 1200-1299 ===========
FRIEND_ADDED(1201, "双方成为好友通知"),
//系统通知 1400
// ========== 系统通知 1400 ==========
OA_NOTIFICATION(1400, "系统通知"),
//群相关 1500-1599
// ========== 群相关 1500-1599 ==========
GROUP_CREATED(1501, "群创建通知"),
GROUP_INFO_CHANGED(1502, "群信息改变通知"),
MEMBER_QUIT(1504, "群成员退出通知"),

View File

@ -6,8 +6,9 @@ import lombok.RequiredArgsConstructor;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
/**
* IM 消息的消息来源 100-用户发送200-系统发送一般是通知不能为空
* IM 消息的消息来源
*/
@RequiredArgsConstructor
@Getter
@ -18,11 +19,11 @@ public enum MessageSourceEnum implements IntArrayValuable {
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageSourceEnum::getStatus).toArray();
// TODO @anhaohao应该是 source
/**
* 状态
*/
private final Integer status;
/**
* 名字
*/

View File

@ -6,6 +6,15 @@ import lombok.RequiredArgsConstructor;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
// TODO TODO 状态是这些哈客户端的视角
//
//- 草稿预留 0
//- 发送中 1
//- 发送成功 2
//- 发送失败 3
//- 已删除 4
//- 已撤回 5
/**
* IM 消息的状态枚举
*/

View File

@ -18,6 +18,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
// TODO @anhaohaoim 前缀少啦
@Tag(name = "管理后台 - IM 会话")
@RestController
@RequestMapping("/im/conversation")
@ -42,7 +43,6 @@ public class ConversationController {
return success(true);
}
// TODO @hao这个接口需要单独的 VO
@PostMapping("/update-last-read-time")
@Operation(summary = "更新最后已读时间")

View File

@ -7,10 +7,12 @@ import lombok.Data;
import java.time.LocalDateTime;
// TODO @anhaohao改成 ConversationUpdateLastReadTimeReqVO项目目前都是动名词哈更新置顶
@Schema(description = "管理后台 - 会话最后已读时间 Request VO")
@Data
public class ConversationLastTimeReqVO {
// TODO @anhaohaono 不用传递哈因为 userId + targetId 可以推出来
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "s_1_2")
@NotEmpty(message = "会话标志不能为空")
private String no;
@ -19,6 +21,7 @@ public class ConversationLastTimeReqVO {
@NotNull(message = "最后已读时间不能为空")
private LocalDateTime lastReadTime;
// TODO @anhaohaouserId 不用传递因为 token 已经能解析出当前用户
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@ -26,6 +29,6 @@ public class ConversationLastTimeReqVO {
private Long targetId;
@Schema(description = "会话类型",requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type; //枚举 ConversationTypeEnum
private Integer type; // 枚举 ConversationTypeEnum TODO 这里可以使用 @InEnum 校验这样这个注释就不用写了
}

View File

@ -8,10 +8,12 @@ import lombok.Data;
import java.time.LocalDateTime;
// TODO @anhaohao改成 ConversationUpdatePinnedReqVO项目目前都是动名词哈更新置顶
@Schema(description = "管理后台 - 会话置顶 Request VO")
@Data
public class ConversationPinnedReqVO {
// TODO @anhaohaono 不用传递哈因为 userId + targetId 可以推出来
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "s_1_2")
@NotEmpty(message = "会话标志不能为空")
private String no;
@ -20,6 +22,7 @@ public class ConversationPinnedReqVO {
@NotNull(message = "是否置顶不能为空")
private Boolean pinned;
// TODO @anhaohaouserId 不用传递因为 token 已经能解析出当前用户
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@ -27,8 +30,6 @@ public class ConversationPinnedReqVO {
private Long targetId;
@Schema(description = "会话类型",requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type; //枚举 ConversationTypeEnum
private Integer type; // 枚举 ConversationTypeEnum TODO 这里可以使用 @InEnum 校验这样这个注释就不用写了
}

View File

@ -28,6 +28,7 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
// TODO @芋艿得看看 createupdatedeletegetpage 这几个接口要保留哪些
@Tag(name = "管理后台 - 群")
@RestController
@RequestMapping("/im/group")
@ -78,6 +79,7 @@ public class ImGroupController {
return success(BeanUtils.toBean(pageResult, ImGroupRespVO.class));
}
// TODO @anhaohao导出可以先不做哈
@GetMapping("/export-excel")
@Operation(summary = "导出群 Excel")
@PreAuthorize("@ss.hasPermission('im:group:export')")

View File

@ -27,6 +27,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
// TODO @芋艿得看看 createupdatedeletegetpage 这几个接口要保留哪些
@Tag(name = "管理后台 - 群成员")
@RestController
@RequestMapping("/im/group-member")

View File

@ -5,6 +5,7 @@ import lombok.Data;
import java.time.LocalDateTime;
// TODO @anhaohaoim 前缀哈
@Schema(description = "管理后台 - 收件箱保存消息 Request VO")
@Data
public class InboxSaveMessageReqVO {
@ -20,7 +21,7 @@ public class InboxSaveMessageReqVO {
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
private String senderAvatar;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
private Long receiverId;

View File

@ -36,6 +36,7 @@ public class MessageController {
return success(messageService.sendMessage(getLoginUserId(), message));
}
// TODO @anhaohao我在想这个接口改成叫 pullMessageList会不会更好理解拉取消息列表
@GetMapping("/list-by-sequence")
@Operation(summary = "拉取大于 sequence 的消息列表")
@Parameter(name = "sequence", description = "序号", required = true, example = "1")
@ -46,6 +47,7 @@ public class MessageController {
return success(BeanUtils.toBean(messages, MessageReqVO.class));
}
// TODO @anhaohao直接叫 getMessageList不叫历史哈因为它只是给用户叫历史对系统来说就是消息列表
@GetMapping("/history")
@Operation(summary = "查询聊天记录-根据会话标志和发送时间进行分页查询")
public CommonResult<List<MessageReqVO>> getHistoryMessage(@Valid MessagePageReqVO pageReqVO) {

View File

@ -10,14 +10,19 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @anhaohao这个类不要交 Page而是消息列表MessageListReqVO
@Schema(description = "管理后台 - 消息分页 Request VO")
@Data
@ToString(callSuper = true)
public class MessagePageReqVO {
// TODO @anhaohao还是传递 targetId conversationType我们要弱化 no 的概念
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "g_1000")
private String conversationNo;
// TODO @anhaohao应该不传递时间范围而是传递分页的时间然后根据时间查询消息
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-27")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] sendTime;

View File

@ -12,6 +12,8 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @anhaohaoMessageRespVO 消息响应 Response VO
// TODO @anahaohao每个 example 都写下啊哈漏了地方要补下因为 http mock 的时候可以根据它去生成
@Schema(description = "管理后台 - 消息 Request VO")
@Data
public class MessageReqVO {
@ -22,6 +24,7 @@ public class MessageReqVO {
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
// TODO @anhaohao这个应该是 senderId
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long fromId; // 根据 conversationType 区分

View File

@ -7,6 +7,10 @@ import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
// TODO @anhaohaoMessageSendReqVO消息发送
// TODO @anhaohao不应该有 excel 相关的注解
@Schema(description = "管理后台 - 发送消息 Request VO")
@Data
@ExcelIgnoreUnannotated

View File

@ -10,6 +10,8 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @anhaohaoMessageSendRespVO消息发送结果
@Schema(description = "管理后台 - 发送消息 Response VO")
@Data
@AllArgsConstructor

View File

@ -9,6 +9,7 @@ import lombok.*;
import java.time.LocalDateTime;
// TODO @anhaohao还是有 IM 前缀哈
/**
* IM 会话 DO
*
@ -44,15 +45,19 @@ public class ConversationDO extends BaseDO {
/**
* 聊天对象编号
* <p>
* 1. 单聊时用户编号群聊时群编号
* 1. 单聊时用户编号
* 2. 群聊时群编号
*/
private Long targetId;
/**
* 会话标志 单聊s_{userId}_{targetId}需要排序 userId targetId 群聊g_groupId
* 会话标志
*
* 1. 单聊s_{userId}_{targetId}需要排序 userId targetId
* 2. 群聊g_groupId
*/
private String no;
/**
* 是否置顶 0否 1是
* 是否置顶
*/
private Boolean pinned;
/**

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
// TODO @anhaohao还是要有 IM
/**
* IM 群信息 DO
*

View File

@ -4,6 +4,7 @@ import lombok.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
// TODO @anhaohao还是要有 IM
/**
* IM 群成员 DO
*

View File

@ -1,11 +1,13 @@
package cn.iocoder.yudao.module.im.dal.dataobject.inbox;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
// TODO @anhaohao还是要有 IM
// TODO 我们要不要改成 ImMessageQueue 队列从理解上概念上可能都更清晰一点哈每个用户一个消息队列
/**
* IM 收件箱 DO
@ -35,6 +37,8 @@ public class InboxDO extends BaseDO {
private Long userId;
/**
* 消息编号
*
* 关联 {@link MessageDO#getId()}
*/
private Long messageId;
/**

View File

@ -12,6 +12,7 @@ import lombok.*;
import java.time.LocalDateTime;
// TODO @anhaohao还是要有 IM
/**
* IM 消息 DO
*
@ -63,15 +64,21 @@ public class MessageDO extends BaseDO {
*/
private Integer conversationType;
/**
* 会话标志 {@link ConversationTypeEnum} 的generateConversationNo() 方法生成
* 会话标志
*
* 生成规则{@link ConversationTypeEnum#generateConversationNo(Long, Long, Integer)} 方法
*/
private String conversationNo;
/**
* 消息类型 枚举 {@link MessageContentTypeEnum}
* 消息类型
*
* 枚举 {@link MessageContentTypeEnum}
*/
private Integer contentType;
/**
* 消息内容 JSON 格式 对应 dal/dataobject/message/content
* 消息内容
*
* JSON 格式 对应 dal/dataobject/message/content
*/
private String content;
/**
@ -79,11 +86,15 @@ public class MessageDO extends BaseDO {
*/
private LocalDateTime sendTime;
/**
* 消息来源 枚举 {@link MessageSourceEnum}
* 消息来源
*
* 枚举 {@link MessageSourceEnum}
*/
private Integer sendFrom;
/**
* 消息状态 枚举 {@link MessageStatusEnum}
* 消息状态
*
* 枚举 {@link MessageStatusEnum}
*/
private Integer messageStatus;

View File

@ -5,6 +5,8 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// TODO @anhaohao要有 IM
// TODO @芋艿后续要挪到 api 包下主要是给外部接口使用
/**
* 语音消息的 {@link MessageDO 字段 content} 的内容
*

View File

@ -15,12 +15,13 @@ import java.util.List;
@Mapper
public interface InboxMapper extends BaseMapperX<InboxDO> {
// TODO @anhaohao返回 List<InboxDO> 转换成 messageId 交给上层dao 尽量通用
default List<Long> selectMessageIdsByUserIdAndSequence(Long userId, Long sequence, Integer size) {
return selectList(new LambdaQueryWrapperX<InboxDO>()
.gt(InboxDO::getUserId, userId)
.gt(InboxDO::getSequence, sequence)
.orderByAsc(InboxDO::getSequence)
.last("limit 0," + size))
.last("limit 0," + size)) // TODO @anhaohao这里 limit 就可以了不用从 limit 0 开始
.stream()
.map(InboxDO::getMessageId)
.toList();

View File

@ -12,8 +12,6 @@ import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* IM 会话 Service 实现类
*
@ -43,6 +41,7 @@ public class ConversationServiceImpl implements ConversationService {
ConversationDO conversation = conversationMapper.selectByNo(updateReqVO.getNo());
if (conversation == null) {
ConversationDO conversationDO = new ConversationDO();
// TODO @haono 不是前端传递哈后端生成另外其实可以把 insert 写成一个公用方法get会话拿不到就 insert接着处理 update 操作首次多 update 一次无所谓的没多少量的
conversationDO.setNo(updateReqVO.getNo());
conversationDO.setPinned(updateReqVO.getPinned());
conversationDO.setUserId(updateReqVO.getUserId());
@ -50,11 +49,10 @@ public class ConversationServiceImpl implements ConversationService {
conversationDO.setType(updateReqVO.getType());
conversationMapper.insert(conversationDO);
} else {
// 更新
// 更新 TODO @anhaohao这里不要 toBean因为这里逻辑偏 tocnew ConversationDO 对象然后逐个 set 需要的值
ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
conversationMapper.updateById(updateObj);
}
}
@Override

View File

@ -27,4 +27,5 @@ public interface InboxService {
* @return 消息编号列表
*/
List<Long> selectMessageIdsByUserIdAndSequence(Long userId, Long sequence, Integer size);
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.im.service.inbox;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSendMessageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
@ -43,6 +42,7 @@ public class InboxServiceImpl implements InboxService {
@Resource
private GroupMemberService groupMemberService;
// TODO @anhaohao下面的逻辑最好是1. 保存收件箱 + 发送消息给用户 2. xxx这样看的人会更有感觉哈
@Override
public void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessage) {
// 保存收件箱 + 发送消息给用户
@ -64,12 +64,15 @@ public class InboxServiceImpl implements InboxService {
private void saveInboxAndSendMessageForUser(Long userId, InboxSaveMessageReqVO inboxSaveMessage) {
inboxLockRedisDAO.lock(userId, INBOX_LOCK_TIMEOUT, () -> {
Long userSequence = sequenceRedisDao.generateSequence(userId);
// TODO @anhaohao链式调用
InboxDO inbox = new InboxDO();
inbox.setUserId(userId);
inbox.setMessageId(inboxSaveMessage.getMessageId());
inbox.setSequence(userSequence);
inboxMapper.insert(inbox);
// TODO @anhaohao是不是 send 不用在加锁里面哈
// TODO @anhaohao再进一步是不是用 spring @async 可以并发推送噢
InboxSendMessageReqVO message = BeanUtils.toBean(inboxSaveMessage, InboxSendMessageReqVO.class);
message.setSequence(userSequence);
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, IM_MESSAGE_RECEIVE, message);

View File

@ -58,20 +58,25 @@ public class MessageServiceImpl implements MessageService {
@Override
public SendMessageRespVO sendMessage(Long fromUserId, SendMessageReqVO message) {
// 保存消息
// TODO @anhaohaoInboxSaveMessageReqVO 不用 new 出来传递到 saveMessage 方法里
InboxSaveMessageReqVO inboxSaveMessageReqVO = new InboxSaveMessageReqVO();
SendMessageRespVO sendMessageRespVO = saveMessage(fromUserId, message, inboxSaveMessageReqVO);
// 保存收件箱 + 发送消息给用户
// TODO @anhaohao考虑到少定义一些 VO直接传递 MessageDO 就完事了反正这两者也是强耦合的
inboxService.saveInboxAndSendMessage(inboxSaveMessageReqVO);
return sendMessageRespVO;
}
// TODO @anhaohao这个方法是不是定义成 private 然后返回是 MessageDO 对象设置最上面的 sendMessage 也是这个最终 controller 转成 SendMessageRespVO
public SendMessageRespVO saveMessage(Long fromUserId, SendMessageReqVO message, InboxSaveMessageReqVO inboxSaveMessageReqVO) {
//需要校验 receiverId 存在
// 需要校验 receiverId 存在
validateReceiverIdExists(message);
// 查询发送人昵称和发送人头像
AdminUserRespDTO fromUser = adminUserApi.getUser(fromUserId);
// 使用链式调用创建 MessageDO 对象
// TODO @anhaohao一部分字段可以 beanutils tobean 搞定
// TODO @anhaohao链式 set 的时候要把相同的放在一行例如说setSenderNicknamesetSenderAvatar本质上就是为了同类在一行阅读起来简单
MessageDO messageDO = new MessageDO()
.setClientMessageId(message.getClientMessageId())
.setSenderId(fromUserId)
@ -102,17 +107,19 @@ public class MessageServiceImpl implements MessageService {
return new SendMessageRespVO(messageDO.getId(), messageDO.getSendTime());
}
// TODO @anhaohaovalidateReceiver更简单一点哈不仅仅校验存在未来还可以校验自己是不是有好友关系群聊是否在群聊里面等等
private void validateReceiverIdExists(SendMessageReqVO message) {
// TODO @anhaohao这个不要这里校验交给 validator 校验掉
if (message.getReceiverId() == null) {
throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
}
if (message.getConversationType().equals(ConversationTypeEnum.SINGLE.getType())) {
// TODO @anhaohao// 之后要空一个空格中英文协作习惯中文和英文之间不能连着最后也不用
//校验用户是否存在
AdminUserRespDTO receiverUser = adminUserApi.getUser(message.getReceiverId());
if (receiverUser == null) {
throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
}
} else if (message.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
//校验群聊是否存在
List<GroupMemberDO> groupMemberDOS = groupMemberService.selectByGroupId(message.getReceiverId());

View File

@ -15,6 +15,7 @@ public class ImReceiveMessage {
@InEnum(ConversationTypeEnum.class)
private Integer conversationType;
// TODO @anhaohao我们应该是 senderId因为它和 receiverId 是相对应的哈
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long fromId; // 根据 conversationType 区分

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper">
<!-- TODO @haoxml 如果用不到,可以先删除哈 -->
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.groupmember.ImGroupMemberMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.message.MessageMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>