SpringBoot chat message persistence

Single chat – realize the function

  • Sending text messages
  • Chat list
  • Chat Message Details

By integrating Netty-Socketio with SpringBoot, the client can send messages to the server, and the server can receive them normally

The received messages are stored persistently, mysql database is used for data storage, and Mybatis operates database

If you don’t know anything about databases, look at this

MySql met

MySql advanced

Chat message list storage

Create message record column table table

CREATE TABLE `t_message_record` (
  `msg_id` bigint(20) NOT NULL COMMENT 'the message Id'.`send_id` bigint(20) NOT NULL COMMENT 'Sender Id'.`receive_id` bigint(20) NOT NULL COMMENT 'Recipient Id'.`type` char(1) NOT NULL COMMENT 'Message type'.`chat_type` char(1) NOT NULL COMMENT 'Type of chat'.`content` varchar(500) NOT NULL COMMENT 'Chat content'.`send_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Message sent time'.`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time'.`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time',
  PRIMARY KEY (`msg_id`))ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Message log Sheet';

Copy the code

Write MessageRecordMapper operation database


/ * * *@author taohua
 */

@Mapper
public interface MessageRecordMapper {

  /** * single chat * <p> * Insert a single chat message **@paramMessageRecordDO Message entity *@returnSucceeded in adding */
  int insertMessageRecord(MessageRecordDO messageRecordDO);
}
Copy the code

Write the MessageRecordMapper XML mapping file


      
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.moose.operator.mapper.MessageRecordMapper">

  <insert id="insertMessageRecord"
      parameterType="com.moose.operator.model.domain.message.MessageRecordDO">
    INSERT INTO `t_message_record` (msg_id, send_id, receive_id, type, chat_type, content)
    VALUES (#{msgId}, #{sendId}, #{receiveId}, #{type}, #{chatType}, #{content})
  </insert>

</mapper>
Copy the code

Used in SingleMessageServiceImpl

  • saveMessageThe main logic
  @Resource
  private SocketConnection socketConnection;

  / / injection MessageRecordMapper
  @Resource
  private MessageRecordMapper messageRecordMapper;

  @Override public void saveMessage(MessageTemplate template) {

    // TODO: message ack
    MessageRecordDO messageRecord = null;
    try {
      // mock
      //String fromUserId = template.getFromUserId();
      //SocketIOClient fromSocket = socketIOClientMap.get(fromUserId);
      //if ("786600935907659776".equals(toUserId)) {
      //  if (null != fromSocket && fromSocket.isChannelOpen()) {
      // fromsocket. sendEvent(chatconstant. SINGLE_CHAT, "you are not friends!!") );
      / /}
      // return;
      / /}

      //log.info("{}", request);

      // Check message template parameters...

      // Check whether the user exists...

      // Whether a friend...

      /////////////////////////// save message record ///////////////////////////

      // Sender ID
      Long sendId = template.getSendId();

      // Recipient ID
      Long receiveId = template.getReceiveId();

      // Build the message
      messageRecord = new MessageRecordDO();
      messageRecord.setMsgId(snowflakeIdWorker.nextId());
      messageRecord.setSendId(sendId);
      messageRecord.setReceiveId(receiveId);
      messageRecord.setContent(template.getContent());
      messageRecord.setType(MessageUtils.diffMessageType(template.getType()));
      messageRecord.setChatType(MessageUtils.diffChatType(template.getChatType()));
      // Save the message
      messageRecordMapper.insertMessageRecord(messageRecord);

    } catch (Exception exception) {
      exception.printStackTrace();
      log.info("Message parsing: Error [{}] message [{}]", exception.getMessage(), template); }}Copy the code

Service unit test cases


@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class SingleMessageServiceTest {

  @Resource
  private SingleMessageService singleMessageService;

  @Test
  public void testSaveMessage(a) {
    MessageTemplate template = new MessageTemplate();
    mockJiangJing(template);
    mockTaohua(template);
    mockWangZhaoJun(template);
  }

  private void mockWangZhaoJun(MessageTemplate template) {
    / / wang zhaojun
    template.setSendId(775113183131074560L);
    / / jiang jing
    template.setReceiveId(775113183131074580L);
    template.setType("MS:TEXT");
    template.setChatType("CT:SINGLE");
    template.setContent("Hello, Jiang Jing, I'm Wang Zhaojun.");
    singleMessageService.saveMessage(template);
  }

  private void mockTaohua(MessageTemplate template) {

    / / the peach blossom
    template.setSendId(786600935907659776L);
    / / wang zhaojun
    template.setReceiveId(775113183131074560L);
    template.setType("MS:TEXT");
    template.setChatType("CT:SINGLE");
    template.setContent("Hello, Wang Zhaojun, I am peach blossom.");
    singleMessageService.saveMessage(template);

  }

  private void mockJiangJing(MessageTemplate template) {

    / / jiang jing
    template.setSendId(775113183131074580L);

    / / the peach blossom
    template.setReceiveId(786600935907659776L);
    template.setType("MS:TEXT");
    template.setChatType("CT:SINGLE");
    template.setContent("Hello, peach blossom, I am river view.");
    singleMessageService.saveMessage(template);

    / / wang zhaojun
    template.setReceiveId(775113183131074560L);
    template.setType("MS:TEXT");
    template.setChatType("CT:SINGLE");
    template.setContent("Hello, Wang Zhaojun, I am Jiang Jing."); singleMessageService.saveMessage(template); }}Copy the code

Single chat implementation

  • Save the message before sending it?
  • fromsocketConnectionTo obtain, need to determine when online
  • Send messages using SocketIOClient sendEvent

code


// TODO:Failure of message transfer process
// TODO:The concurrent situation
// Send a message
// Check whether it is online
SocketIOClient receiveSocket = socketConnection.getSocketIOClient(String.valueOf(receiveId));
if (null! = receiveSocket && receiveSocket.isChannelOpen()) {// The client listens for chat events
  receiveSocket.sendEvent(ChatConstant.SINGLE_CHAT, MapperUtils.obj2json(template));
  // TODO: un read count compute --> 0
  // messageRecord.setUnReadCount(0);
} else {
  // TODO: un read count compute --> redis increment or decrement
  // messageRecord.setUnReadCount(0);
}

Copy the code

debugging

Socket_io_client is used for debugging. Dart language module, behind the integration of Flutter convenience

Please refer to gitee.com/shizidada/d…

Implementation effect

Long connection, received data can be rendered on the UI

Get chat list

MessageRecordMapper Java Adds a query method

  /** * Chat list **@paramUserId Current user *@returnChat list */
  List<MessageRecordDO> selectMessageRecordList(Long userId);

  /** * Query chat list details **@paramSendId Sender Id *@paramReceiveId Recipient Id *@returnChat details */
  List<MessageRecordDO> selectChatMessageList(@Param("sendId") Long sendId,
      @Param("receiveId") Long receiveId);
Copy the code

MessageRecordMapper XML Adds a query mapping

  <select id="selectMessageRecordList" resultMap="BaseResultMap"><! [CDATA[ SELECT t.* FROM ( SELECT msg_id, send_id, receive_id, type, chat_type, content, send_time FROM ( SELECT receive_id AS receiver, msg_id, send_id, receive_id, type, chat_type, content, send_time FROM t_message_record WHERE send_id = #{userId} AND receive_id <> #{userId} UNION SELECT send_id AS receiver, msg_id, send_id, receive_id, type, chat_type, content, send_time FROM t_message_record WHERE send_id <> #{userId} AND receive_id = #{userId} ORDER BY send_time DESC ) AS newTable GROUP BY receiver ORDER BY send_time DESC ) AS t ]]></select>

  <select id="selectChatMessageList" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column"/>
    FROM
    t_message_record
    WHERE
    ( send_id = #{sendId} AND receive_id = #{receiveId} )
    OR
    ( send_id = #{receiveId} AND receive_id = #{sendId} )
    ORDER BY send_time ASC
  </select>
Copy the code

Unit testing

  @Test
  public void testSelectMessageRecordList(a) {
    / / jiang jing
    Long userId = 775113183131074580L;
    List<MessageRecordDO> messageRecordDOS = messageRecordMapper.selectMessageRecordList(userId);
    log.info("{}", messageRecordDOS);
  }
Copy the code

Write the interface

Get the chat list service MessageRecordService

public interface MessageRecordService {

  /** * Get the list of chat messages **@return List<ChatMessageRecord>
   */
  List<MessageRecordDO> listMessageRecord(a);
    
  /** * Query the chat list **@paramUserId Peer Id *@paramPageNum page *@returnChat list details */
  List<MessageRecordDO> listMessageChat(Long userId, Integer pageNum);
}
Copy the code

The service implementation is MessageRecordServiceImpl

@Slf4j
@Service
public class MessageRecordServiceImpl implements MessageRecordService {

  @Resource
  private UserInfoService userInfoService;

  @Resource
  private MessageRecordMapper messageRecordMapper;

  @Override public List<MessageRecordDO> listMessageRecord(a) {
    Long userId = userInfoService.getCurrentUserId();
    return messageRecordMapper.selectMessageRecordList(userId);
  }

  @Override public List<MessageRecordDO> listMessageChat(Long userId, Integer pageNum) {
    Long currentUserId = userInfoService.getCurrentUserId();
    PageHelper.startPage(pageNum, 10);
    returnmessageRecordMapper.selectChatMessageList(currentUserId, userId); }}Copy the code

PageHelper performs paging queries and queries only 10 items of data at a time based on paging

PageHelper.startPage(pageNum, 10);

Message interface MessageController


@RestController
@RequestMapping("/api/v1/message")
public class MessageController {

  @Resource
  private MessageRecordService messageRecordService;

 /** * Gets the chat list of the currently logged in user **@returnR<? > Chat list */
  @PostMapping(value = "/record/list")
  publicR<? > getChatMessageList() {return R.ok(messageRecordService.listMessageRecord());
  }
    
  /** * get Id **@returnR<? < span style = "max-width: 100%; clear: both; min-height: 1em
  @PostMapping(value = "/chat/list")
  publicR<? > getMessageChatList(@RequestParam("chatId") Long chatId,
      @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum
  ) {
    returnR.ok(messageRecordService.listMessageChat(chatId, pageNum)); }}Copy the code

Enabling service access

Gets the chat history listhttp://localhost:7000/api/v1/message/record/list

Gets the chat history listhttp://localhost:7000/api/v1/message/chat/list

TODO for

  • Group chat, sending binary messages (picture messages, video messages…)
  • Check the message template parameters
  • Check whether the user exists
  • Are you friends?
  • Active push? Again, pull the list of messages
  • Message storagereadDiffusion,writeThe spread of
  • In the distributed case, message push
  • .

Pay attention to the public account “full stack technology Department”, continue to learn more interesting technical knowledge.