WeChat applet | Real-time chat function based on applet + Java + WebSocket

1. Preface

This article mainly implements the chat dialogue function in the small program, using Java as the back-end language for support, with a friendly interface and easy development.

2. Development process and tool preparation

2.1. Register an account on the WeChat public platform.
2.2. Download and install IntelliJ IDEA (back-end language development tool), Mysql database, and WeChat Web developer tools.

3. Development steps

1. Create a maven project

First create a project named SpringBootDemo, select [New Project]
insert image description here

Then in the pop-up window below, select [New Project] on the left menu (Note: Unlike the idea version before 2022, there is no [Maven] option on the left here, do not select [Maven Archetype]!!!), enter Name (project name): SpringBootDemo, select [java] for language, select [maven] for build system, and then select jdk. I choose jdk18 here.

insert image description hereThen click【Create】
insert image description here

2. Create a module under the project

Right-click and select [new]-[Module...]
insert image description here
Select [Spring initializr] on the left, and quickly create a spring boot project through the Spring initializr tool integrated in the idea. On the right side of the window: name can be set according to your preference, group and artifact have the same rules as above, other options can keep the default value, [next]
insert image description here

Check [Spring Boot DevTools] for the Developer Tools module, and check [Spring Web] for the web module

insert image description here

At this point, a Springboot project has been built and subsequent functions can be developed

3. Write a message entity class, Mapper, service (three-tier architecture)

@Data
public class Chat {
    
    

    @TableId(type = IdType.AUTO)
    private Long id;

    private Long userId;

    private Long targetUserId;

    private LocalDateTime createTime;

    private String userName;

    private String targetUserName;

    private String content;

}

Since we use mybatis-plus, we don’t need to write simple additions, deletions, modifications, and queries. The framework comes with it. We only need to implement or inherit its Mapper and Service
insert image description hereinsert image description here

4. Write WebSocket service class

@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketService {
    
    


    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */
    private static ConcurrentHashMap<String, WebSocketService> webSocketMap = new ConcurrentHashMap<>();
    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收userId
     */
    private String userId = "";

    public static ChatMapper chatMapper = null;


    /**
     * 连接建立成功调用的方法
     * <p>
     * 1.用map存 每个客户端对应的MyWebSocket对象
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
    
    
        this.session = session;
        this.userId = userId;
        if (webSocketMap.containsKey(userId)) {
    
    
            webSocketMap.remove(userId);
            webSocketMap.put(userId, this);
            //加入set中
        } else {
    
    
            webSocketMap.put(userId, this);
            //加入set中
        }
    }


    /**
     * 报错
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
    
    
        error.printStackTrace();
    }

    /**
     * 实现服务器推送到对应的客户端
     */
    public void sendMessage(String message) {
    
    
        try {
    
    
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }


    /**
     * 自定义 指定的userId服务端向客户端发送消息
     */
    public static void sendInfo(Chat chat) {
    
    
        QueryWrapper<Chat> queryWrapper = new QueryWrapper();
        List<Chat> chats=chatMapper.selectList(queryWrapper.lambda()
                .eq(Chat::getTargetUserId, chat.getTargetUserId()).or().eq(Chat::getUserId, chat.getTargetUserId()).or().eq(Chat::getTargetUserId, chat.getUserId()).or().eq(Chat::getUserId, chat.getUserId()));

        //log.info("发送消息到:"+userId+",报文:"+message);
        if (!StringUtils.isEmpty(chat.getTargetUserId().toString()) && webSocketMap.containsKey(chat.getTargetUserId().toString())) {
    
    
            webSocketMap.get(chat.getUserId().toString()).sendMessage(JSONObject.toJSONString(chats));
            webSocketMap.get(chat.getTargetUserId().toString()).sendMessage(JSONObject.toJSONString(chats));
        } else {
    
    
            webSocketMap.get(chat.getUserId().toString()).sendMessage(JSONObject.toJSONString(chats));
        }
    }

    /**
     * 自定义关闭
     *
     * @param userId
     */
    public static void close(String userId) {
    
    
        if (webSocketMap.containsKey(userId)) {
    
    
            webSocketMap.remove(userId);
        }
    }

    /**
     * 获取在线用户信息
     *
     * @return
     */
    public static Map getOnlineUser() {
    
    
        return webSocketMap;
    }

5. Create the controller Controller

First create the Controller Package
insert image description here

Create a Controller
insert image description here
input class name, select [Class]
insert image description here

Because you want to write a Rest-style Api, you need to mark the @RestController annotation on the Controller

6. Create a specific API interface



@RestController
public class DemoController {
    
    

    @Autowired
    private ChatService chatService;

    @PostMapping("/push")
    public ResponseEntity<String> pushToWeb(@RequestBody Chat chat) throws IOException {
    
    
        chat.setCreateTime(LocalDateTime.now());
        chatService.save(chat);
        WebSocketService.sendInfo(chat);


        return ResponseEntity.ok("MSG SEND SUCCESS");
    }

    @GetMapping("/close")
    public String close(String userId) {
    
    
        WebSocketService.close(userId);
        return "ok";
    }


    @GetMapping("/getOnlineUser")
    public Map getOnlineUser() {
    
    
        return WebSocketService.getOnlineUser();
    }


    @GetMapping("/getMessage")
    public ResponseEntity<List<Chat>> getMessage(String userId) {
    
    
        QueryWrapper<Chat> queryWrapper = new QueryWrapper();
        List<Chat> list = chatService.
                list(queryWrapper.lambda().eq(Chat::getTargetUserId, userId).or().eq(Chat::getUserId, userId));
        return ResponseEntity.ok(list);
    }

}

7. Small program code
insert image description here
3.20. Create a folder under the pages folder and create a corresponding page file, and implement the chat dialog box style.
insert image description here

insert image description here

<view class="cu-chat" id="j_page">
  <view class="cu-item 'self'" wx:for="{
    
    {chatData}}">
    <view class="main">
      <view class="content shadow bg-green">
        <text>{
    
    {
    
    item.content}}</text>
      </view>
    </view>
    <view class="cu-avatar radius" style="background-image:url(../../../images/cat.jpg)"></view>
    <view class="date">{
    
    {
    
    item.createTime}}</view>
  </view>
</view>

<view class="cu-bar foot input {
    
    {InputBottom!=0?'cur':''}}" style="bottom:{
    
    {InputBottom}}px">
  <view class="action">
    <text class="cuIcon-sound text-grey"></text>
  </view>
  <input class="solid-bottom" value="{
    
    {content}}" bindinput="formMsg"  bindfocus="InputFocus" bindblur="InputBlur" adjust-position="{
    
    {false}}" focus="{
    
    {false}}" maxlength="300" cursor-spacing="10"></input>
  <view class="action">
    <text class="cuIcon-emojifill text-grey"></text>
  </view>
  <button class="cu-btn bg-green shadow" bindtap="sendMsg">发送</button>
</view>
.cu-chat {
    
    
	display: flex;
	flex-direction: column;
}

.cu-chat .cu-item {
    
    
	display: flex;
	padding: 30rpx 30rpx 70rpx;
	position: relative;
}

.cu-chat .cu-item>.cu-avatar {
    
    
	width: 80rpx;
	height: 80rpx;
}

.cu-chat .cu-item>.main {
    
    
	max-width: calc(100% - 260rpx);
	margin: 0 40rpx;
	display: flex;
	align-items: center;
}

.cu-chat .cu-item>image {
    
    
	height: 320rpx;
}

.cu-chat .cu-item>.main .content {
    
    
	padding: 20rpx;
	border-radius: 6rpx;
	display: inline-flex;
	max-width: 100%;
	align-items: center;
	font-size: 30rpx;
	position: relative;
	min-height: 80rpx;
	line-height: 40rpx;
	text-align: left;
}

.cu-chat .cu-item>.main .content:not([class*="bg-"]) {
    
    
	background-color: var(--white);
	color: var(--black);
}

.cu-chat .cu-item .date {
    
    
	position: absolute;
	font-size: 24rpx;
	color: var(--grey);
	width: calc(100% - 320rpx);
	bottom: 20rpx;
	left: 160rpx;
}

.cu-chat .cu-item .action {
    
    
	padding: 0 30rpx;
	display: flex;
	align-items: center;
}

.cu-chat .cu-item>.main .content::after {
    
    
	content: "";
	top: 27rpx;
	transform: rotate(45deg);
	position: absolute;
	z-index: 100;
	display: inline-block;
	overflow: hidden;
	width: 24rpx;
	height: 24rpx;
	left: -12rpx;
	right: initial;
	background-color: inherit;
}

.cu-chat .cu-item.self>.main .content::after {
    
    
	left: auto;
	right: -12rpx;
}

3.21. Implement the interface for requesting chat list and adding chat information in JS, use websocket to refresh chat function in real time, and provide onOpen, onClose, onError, onMessage methods.

var socket = null;
Page({
    
    
  data: {
    
    
    InputBottom: 0,
    chatData: [],
    content: '', //需要发送的内容
    userId: 2,
  },
  onLoad() {
    
    
    let that = this;

    socket = wx.connectSocket({
    
    
      url: 'ws://localhost:8080/imserver/2',
      success: res => {
    
    
          console.info('创建连接成功');
          //socketTaskId: 22
          // console.info(res);
      }
  });
  // console.info(socket);
  //事件监听
  socket.onOpen(function () {
    
    
      console.info('连接打开成功');
  });
  socket.onClose(function () {
    
    
      console.info('连接关闭成功');
  });
  socket.onError(function () {
    
    
      console.info('连接报错');
  });
  //服务器发送监听
  socket.onMessage(function (e) {
    
    
    console.info(e.data);
    var list=JSON.parse(e.data);
    
    that.setData({
    
    chatData:list});


  });
      wx.request({
    
    
        url: 'http://localhost:8080/getMessage?userId=2',
        method: 'get',
        dataType: "json",
        success: function (res) {
    
    
          that.setData({
    
    
            chatData: res.data
          });
        }
      });
      wx.pageScrollTo({
    
    
        scrollTop: 9999
      })
  },
  InputFocus(e) {
    
    
    this.setData({
    
    
      InputBottom: e.detail.height
    })
  },
  formMsg(e) {
    
    
    this.setData({
    
    
      content: e.detail.value.trim()
    })
  },
  //发送消息
  sendMsg() {
    
    
    let that = this;
    let info = {
    
    
      userName: '李四',
      content: that.data.content,
      userId: 2,
      targetUserId: 1,
      targetUserName: "张三"
    };
    wx.request({
    
    
      url: 'http://localhost:8080/push',
      data: JSON.stringify(info),
      method: 'post',
      contentType:"application/json",
      dataType: "json",
      success: function (identify) {
    
    
        that.setData({
    
    
          content: '',
        });
        //发送消息后
     
        wx.pageScrollTo({
    
    
          scrollTop: 9999
        })

      }
    });
  },

  InputBlur(e) {
    
    
    this.setData({
    
    
      InputBottom: 0
    })
  }
})

//查询消息列表
function selectMsg() {
    
    
  let that = this;

};

3.22. Prepare two avatars. In WXML, judge whether the chat record is sent by yourself according to the corresponding user name, and assign the corresponding class style. The subsequent step can be judged directly in the data returned by the interface. The interface for requesting the query list will be Just pass the user token as a parameter.


<view class="cu-chat" id="j_page">
  <view class="cu-item {
    
    {item.userId=='2'?'self':''}}" wx:for="{
    
    {chatData}}">
    <view class="cu-avatar radius" style="background-image:url(../../../images/cat.jpg)" wx:if="{
    
    {item.userId=='1'}}"></view>
    <view class="main">
      <view class="content shadow {
    
    {item.userId=='1'?'bg-green':''}}">
        <text>{
    
    {
    
    item.content}}</text>
      </view>
    </view>
    <view class="cu-avatar radius" style="background-image:url(../../../images/dog.jpg)" wx:if="{
    
    {item.userId=='2'}}"></view>
    <view class="date">{
    
    {
    
    item.createTime}}</view>
  </view>
</view> 

3.23. After the interface for requesting chat records and the interface for adding chat information are both running smoothly, we will copy the existing applet, and the JS in the copied applet will change the user name to Zhang San and Li Si , and then send the message. What needs to be noted here is that we need to position the page content at the bottom after each message is sent, and always keep a state of reading the latest news.

wx.pageScrollTo({
    
    
  scrollTop: 9999
})

The chat function implemented in a relatively simple way here uses the ws long connection method to realize this function. After completion, the source code will be uploaded and provided to everyone in the form of resources.

Guess you like

Origin blog.csdn.net/wml_JavaKill/article/details/127807331