第8章 十次方前端系统开发

网站前台-交友与聊天

学习目标:

1.推荐好友列表

1.1数据渲染

(1)easyMock模拟数据

URL: friend/friend/list

Method: GET

{
  "flag": true,
  "code": 20000,
  "data": [{
      "nickname": "小雅",
      "pic": "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1872448934,405071047&fm=27&gp=0.jpg",
      "age": 24,
      "constellation": "金牛座",
      "activity":"喝咖啡"
    },
    {
      "nickname": "婷婷",
      "pic": "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2989004090,2118875929&fm=27&gp=0.jpg",
      "age": 23,
      "constellation": "双子座",
      "activity":"看电影"
    },
    {
      "nickname": "慕冉",
      "pic": "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4253788808,3515932570&fm=27&gp=0.jpg",
      "age": 27,
      "constellation": "处女座",
      "activity":"逛街"
    },
    {
      "nickname": "茉莉莉",
      "pic": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1819234630,314130062&fm=27&gp=0.jpg",
      "age": 20,
      "constellation": "射手座",
      "activity":"玩游戏"
    }
  ]
}

(2)API编写

创建api/friend.js

import request from '@/utils/request'
const api_group = 'friend'
const api_name = 'friend'
export default {
  list() {   
    return request({
      url: `/${api_group}/${api_name}/list`,
      method: 'get'
    })
  }
}

(3)页面组件数据渲染

修改pages/friend/index.vue

<template>
  <div>
 <div class="banner"> 
   <div class="wrapper"> 
    <img src="~/assets/img/page-banner.png" alt="" /> 
   </div> 
  </div> 
  <!--两列布局--> 
  <div class="wrapper tag-item"> 
   <div class="fl left-list"> 
    <p class="full-info">为了获取更好的体验 请 <a href="makeFriends-edit.html" target="_blank">完善兴趣信息</a><span class="sui-icon icon-tb-close close"></span></p> 
    <div class="friend-dating-list"> 
     <ul class="friends"> 
      <li class="friend-item" v-for="(item,index) in friendList" :key="index" > 
       <div class="fl photo"> 
        <span class="cafe-more"></span> 
        <div class="img">
         <img :src="item.pic" alt="" height="148"  width="239"/>
        </div> 
        <div class="tag"> 
         <span class="tag-cafe cafe"><i class="fa fa-coffee" aria-hidden="true"></i> {{item.activity}}</span> 
        </div> 
       </div> 
       <div class="fl content"> 
        <p class="title"> <span class="name">{{item.nickname}}</span> 邀你一起 <span class="cafe">喝咖啡</span> <b class="bold">匹配度 96%</b> </p> 
        <p class="aa"> <span class="money"> {{item.age}}岁 | {{item.constellation}} | 教育 | 软件工程师</span> </p> 
        <p class="point"> 他刚刚分享了XXX文章</p> 
        <p class="desc"> 推荐理由:你们共同关注PHP、Python 等 5 个标签,都关注 XXX 活动。</p> 
       </div> 
       <div class="fr xy"> 
        <ul> 
         <li><i class="like sui-icon icon-tb-like"></i></li> 
         <li><i class="close sui-icon icon-tb-roundclose"></i></li> 
         <li><i class="message sui-icon icon-tb-comment"></i></li> 
        </ul> 
       </div> 
       <div class="clearfix"></div> 
      </li>       
     </ul>      
    </div> 
   </div> 
   <div class="fl right-tag"> 
    <div class="friend-info-card"> 
     <div class="card"> 
      <div class="photo-name"> 
       <img src="~/assets/img/widget-photo.png" alt="" /> 
       <div class="name-edit"> 
        <p><span class="name">用户名</span> <span class="edit">编辑兴趣资料</span></p> 
        <p>认证</p> 
       </div> 
       <div class="clearfix"></div> 
      </div> 
      <div class="like"> 
       <span class="like1">喜欢 <i class="num">400</i></span> 
       <span>被喜欢 <i class="num">5</i></span> 
      </div> 
     </div> 
    </div> 
    <div class="block-btn"> 
     <p>约一约有趣的人,会一会好的时光!</p> 
     <a class="sui-btn btn-block btn-share" href="~/assets/makeFriends-submit.html" target="_blank">发布约会</a> 
    </div> 
    <div class="rank"> 
     <div class="head"> 
      <h3 class="title">排行榜</h3> 
     </div> 
     <div class="rank-list"> 
      <ul class="rank"> 
       <li> <span class="fl dating">喝咖啡</span> <span class="fr cafe">102258</span> </li> 
       <li> <span class="fl dating">吃饭</span> <span class="fr eat">9878</span> </li> 
       <li> <span class="fl dating">看电影</span> <span class="fr movie">2564</span> </li> 
       <li> <span class="fl dating">旅行</span> <span class="fr travel">897</span> </li> 
      </ul> 
     </div> 
    </div> 
    <div class="friend-line-card"> 
     <div class="card"> 
      <p>找个合适的参加一场线下活动</p> 
     </div> 
    </div> 
    <div class="friend-line-card"> 
     <div class="card"> 
      <p>找个合适的参加一场线下活动</p> 
     </div> 
    </div> 
   </div> 
   <div class="clearfix"></div> 
  </div> 
  </div>
</template>
<script>
import '~/assets/css/page-sj-makeFriends-index.css'
import friendApi from '@/api/friend'
export default {
  asyncData ({ params, error }) {
    return friendApi.list().then( res=>{
      return {friendList: res.data.data }
    })
  }
}
</script>

1.2心动配对

(1)easyMock模拟数据

URL:friend/friend/like/{id}/{type}

Method:put

{
  "flag":true,
  "code":20000,
  "data": {
    "nickname": "悦悦",
    "pic": "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3814696406,295322094&fm=27&gp=0.jpg",
    "age": 21,
    "constellation": "狮子座",
    "activity":"聊人生"
  }
}

(2)编写API。 修改

  like(id,type) {
    return request({
      url: `/${api_group}/${api_name}/like/${id}/${type}`,
      method: 'put'
    })
  }

(3)页面调用 修改pages/friends/index.vue ,代码部分增加方法

  methods:{
    like(id,type){
      friendApi.like(id,type).then(  res=>{
          for(let i=0;i<this.friendList.length;i++){
              if(this.friendList[i].id===id){
                this.friendList.splice(i,1)
              }
          }          
          this.friendList.push(res.data.data)
      })
    }
  }

页面部分:

<li><i class="like sui-icon icon-tb-like" @click="like(item.id,1)"></i></li> 
<li><i class="close sui-icon icon-tb-roundclose"  @click="like(item.id,0)"></i></li> 

1.3样式处理

(1)修改代码部分

     changeActive($event){
        $event.currentTarget.className="like sui-icon icon-tb-likefill";
     },
     removeActive($event){
        $event.currentTarget.className="like sui-icon icon-tb-like";
     },
     changeClose($event){
        $event.currentTarget.className="close sui-icon icon-tb-roundclosefill";
     },
     removeClose($event){
        $event.currentTarget.className="close sui-icon icon-tb-roundclose";
     }

(2)修改页面部分

<li><i class="like sui-icon icon-tb-like" @click="like(item.id,1)"  @mouseover="changeActive($event)" @mouseout="removeActive($event)"></i></li> 
<li><i class="close sui-icon icon-tb-roundclose"  @click="like(item.id,0)"  @mouseover="changeClose($event)" @mouseout="removeClose($event)"></i></li> 

2.消息中心

2.1构建页面

创建pages/friends/friendList.vue

<template>
<div>
  <div class="banner"> 
   <div class="wrapper"> 
    <img src="~/assets/img/page-banner.png" alt="" /> 
   </div> 
  </div> 
  <!--两列布局--> 
  <div class="wrapper tag-item"> 
   <div class="fl left-list"> 
    <div class="friend-list"> 
     <h4>消息中心</h4> 
     <ul> 
      <li class="friend-item"> 
       <div class="tip"> 
        <span class="num">2</span> 
        <img src="~/assets/img/widget-photo.png" alt="" /> 
       </div> 
       <div class="msg"> 
        <p><span class="name">毕鹏</span><span class="date">2017-10-23</span></p> 
        <p class="msg-content">Hi 你在干什么呢?</p> 
       </div> </li> 
      <li class="friend-item"> 
       <div class="tip"> 
        <span class="num">2</span> 
        <img src="~/assets/img/widget-photo.png" alt="" /> 
       </div> 
       <div class="msg"> 
        <p><span class="name">毕鹏</span><span class="date">2017-10-23</span></p> 
        <p class="msg-content">Hi 你在干什么呢?</p> 
       </div> </li> 
      <li class="friend-item"> 
       <div class="tip"> 
        <span class="num">2</span> 
        <img src="~/assets/img/widget-photo.png" alt="" /> 
       </div> 
       <div class="msg"> 
        <p><span class="name">毕鹏</span><span class="date">2017-10-23</span></p> 
        <p class="msg-content">Hi 你在干什么呢?</p> 
       </div> </li> 
      <li class="friend-item"> 
       <div class="tip"> 
        <span class="num">2</span> 
        <img src="~/assets/img/widget-photo.png" alt="" /> 
       </div> 
       <div class="msg"> 
        <p><span class="name">毕鹏</span><span class="date">2017-10-23</span></p> 
        <p class="msg-content">Hi 你在干什么呢?</p> 
       </div> </li> 
      <li class="friend-item"> 
       <div class="tip"> 
        <span class="num">2</span> 
        <img src="~/assets/img/widget-photo.png" alt="" /> 
       </div> 
       <div class="msg"> 
        <p><span class="name">毕鹏</span><span class="date">2017-10-23</span></p> 
        <p class="msg-content">Hi 你在干什么呢?</p> 
       </div> </li> 
     </ul>      
    </div> 
   </div> 
   <div class="fl right-tag"> 
    <div class="friend-info-card"> 
     <div class="card"> 
      <div class="photo-name"> 
       <img src="~/assets/img/widget-photo.png" alt="" /> 
       <div class="name-edit"> 
        <p><span class="name">用户名</span> <span class="edit">编辑兴趣资料</span></p> 
        <p>认证</p> 
       </div> 
       <div class="clearfix"></div> 
      </div> 
      <div class="like"> 
       <span class="like1">喜欢 <i class="num">400</i></span> 
       <span>被喜欢 <i class="num">5</i></span> 
      </div> 
     </div> 
    </div> 
    <div class="block-btn"> 
     <p>约一约有趣的人,会一会好的时光!</p> 
     <a class="sui-btn btn-block btn-share" href="~/assets/makeFriends-submit.html" target="_blank">发布约会</a> 
    </div> 
    <div class="rank"> 
     <div class="head"> 
      <h3 class="title">排行榜</h3> 
     </div> 
     <div class="rank-list"> 
      <ul class="rank"> 
       <li> <span class="fl dating">喝咖啡</span> <span class="fr cafe">102258</span> </li> 
       <li> <span class="fl dating">吃饭</span> <span class="fr eat">9878</span> </li> 
       <li> <span class="fl dating">看电影</span> <span class="fr movie">2564</span> </li> 
       <li> <span class="fl dating">旅行</span> <span class="fr travel">897</span> </li> 
      </ul> 
     </div> 
    </div> 
    <div class="friend-line-card"> 
     <div class="card"> 
      <p>找个合适的参加一场线下活动</p> 
     </div> 
    </div> 
    <div class="friend-line-card"> 
     <div class="card"> 
      <p>找个合适的参加一场线下活动</p> 
     </div> 
    </div> 
   </div> 
   <div class="clearfix"></div> 
</div>
</div>
</template>
<script>
import '~/assets/css/page-sj-makeFriends-list.css'
export default {  
}
</script>

2.2数据渲染

(1)easyMock模拟数据

URL: friend/friend/mylist

Method:GET

{
  "flag": true,
  "code": 20000,
  "data":[
    {
      "id":"1",
      "nickname": "悦悦",
      "avatar": "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3016890200,2017113207&fm=27&gp=0.jpg",
      "unread": 3,
      "message": "在吗?",
      "lasttime":"2018-4-1"
    },
    {
      "id":"2",
      "nickname": "婉儿",
      "avatar": "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=394607255,721510492&fm=27&gp=0.jpg",
      "unread": 10,
      "message": "怎么不理我:(",
      "lasttime":"2018-4-2"
    },
    {
      "id":"3",
      "nickname": "雨柯",
      "avatar": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=359604599,305372113&fm=27&gp=0.jpg",
      "unread": 1,
      "message": "化妆品诚招代理,微信:abcd1234",
      "lasttime":"2018-4-3"
    }    
  ] 
}

(2)API编写。修改api/friend.js

  mylist() {   
    return request({
      url: `/${api_group}/${api_name}/mylist`,
      method: 'get'
    })
  }

(3)页面渲染。修改pages/friends/friendList.vue

<template>
<div>
  <div class="banner"> 
   <div class="wrapper"> 
    <img src="~/assets/img/page-banner.png" alt="" /> 
   </div> 
  </div> 
  <!--两列布局--> 
  <div class="wrapper tag-item"> 
   <div class="fl left-list"> 
    <div class="friend-list"> 
     <h4>消息中心</h4> 
     <ul> 
      <li class="friend-item" v-for="(item,index) in myFriendList" :key="index"> 
       <div class="tip"> 
        <span class="num">{{item.unread}}</span> 
        <img :src="item.avatar" alt="" height="50px"  width="50px"/> 
       </div> 
       <div class="msg"> 
        <p><span class="name">{{item.nickname}}</span><span class="date">{{item.lasttime}}</span></p> 
        <p class="msg-content">{{item.message}}</p> 
       </div> 
      </li>       
     </ul>      
    </div> 
   </div> 
   <div class="fl right-tag"> 
    <div class="friend-info-card"> 
     <div class="card"> 
      <div class="photo-name"> 
       <img src="~/assets/img/widget-photo.png" alt="" /> 
       <div class="name-edit"> 
        <p><span class="name">用户名</span> <span class="edit">编辑兴趣资料</span></p> 
        <p>认证</p> 
       </div> 
       <div class="clearfix"></div> 
      </div> 
      <div class="like"> 
       <span class="like1">喜欢 <i class="num">400</i></span> 
       <span>被喜欢 <i class="num">5</i></span> 
      </div> 
     </div> 
    </div> 
    <div class="block-btn"> 
     <p>约一约有趣的人,会一会好的时光!</p> 
     <a class="sui-btn btn-block btn-share" href="~/assets/makeFriends-submit.html" target="_blank">发布约会</a> 
    </div> 
    <div class="rank"> 
     <div class="head"> 
      <h3 class="title">排行榜</h3> 
     </div> 
     <div class="rank-list"> 
      <ul class="rank"> 
       <li> <span class="fl dating">喝咖啡</span> <span class="fr cafe">102258</span> </li> 
       <li> <span class="fl dating">吃饭</span> <span class="fr eat">9878</span> </li> 
       <li> <span class="fl dating">看电影</span> <span class="fr movie">2564</span> </li> 
       <li> <span class="fl dating">旅行</span> <span class="fr travel">897</span> </li> 
      </ul> 
     </div> 
    </div> 
    <div class="friend-line-card"> 
     <div class="card"> 
      <p>找个合适的参加一场线下活动</p> 
     </div> 
    </div> 
    <div class="friend-line-card"> 
     <div class="card"> 
      <p>找个合适的参加一场线下活动</p> 
     </div> 
    </div> 
   </div> 
   <div class="clearfix"></div> 
</div>
</div>
</template>
<script>
import '~/assets/css/page-sj-makeFriends-list.css'
import friendApi from '@/api/friend'
export default {
  asyncData ({ params, error }) {
    return friendApi.mylist().then( res=>{
      return {myFriendList: res.data.data }
    })
  }
}
</script>

3.WebSocket快速入门

3.1WebSocket简介

3.3.1 WebSocket

​ Websocket是html5提出的一个协议规范,参考rfc6455。

​ websocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似tcp的连接,从而方便c-s之间的通信。在websocket出现之前,web交互一般是基于http协议的短连接或者长连接。

​ WebSocket是为解决客户端与服务端实时通信而产生的技术。websocket协议本质上是一个基于tcp的协议,是先通过HTTP/HTTPS协议发起一条特殊的http请求进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。

​ 以前web server实现推送技术或者即时通讯,用的都是轮询(polling),在特点的时间间隔(比如1秒钟)由浏览器自动发出请求,将服务器的消息主动的拉回来,在这种情况下,我们需要不断的向服务器发送请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。

​ 而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。

​ WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。 浏览器和服务器只需要要做一个握手的动作,在建立连接之后,服务器可以主动传送数据给客户端,客户端也可以随时向服务器发送数据。 此外,服务器与客户端之间交换的标头信息很小。

​ WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息

3.3.2 SockJS

​ SockJS是一个浏览器JavaScript库(对WebSocket原生API进行了封装),它提供了一个类似于网络的对象。SockJS提供了一个连贯的、跨浏览器的Javascript API,它在浏览器和web服务器之间创建了一个低延迟、全双工、跨域通信通道。

​ 一些浏览器中缺少对WebSocket的支持,因此,回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。

​ SockJS的一大好处在于提供了浏览器兼容性。优先使用原生WebSocket,如果在不支持websocket的浏览器中,会自动降为轮询的方式。 除此之外,spring也对socketJS提供了支持。

​ 如果java代码中添加了withSockJS() ,服务器也会自动降级为轮询。

registry.addEndpoint("/coordination").withSockJS();

​ SockJS的目标是让应用程序使用WebSocket API,但在运行时需要在必要时返回到非WebSocket替代,即无需更改应用程序代码。

3.3.3 STOMP

​ STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。

​ STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。

​ STOMP是一个非常简单和容易实现的协议,其设计灵感源自于HTTP的简单性。尽管STOMP协议在服务器端的实现可能有一定的难度,但客户端的实现却很容易。例如,可以使用Telnet登录到任何的STOMP代理,并与STOMP代理进行交互。

3.3.4 STOMP.js

stomp.js(Stomp Over WebSocket)是使用H5 Web Socket API实现的Stomp客户端,可以实现消息实时推送.

3.2入门demo之匿名聊天室

3.2.1服务端代码

(1)创建spring boot工程 pom.xml引入起步依赖:

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
	</dependencies>

(2)创建启动类

@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

(3)创建配置类WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		config.enableSimpleBroker("/topic");
		config.setApplicationDestinationPrefixes("/app");
	}

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/my-websocket").withSockJS();
	}
}

这里配置了以“/app”开头的websocket请求url。和名为“my-websocket”的endpoint(端点)

(4)创建消息实体类SocketMessage

public class SocketMessage {

	private String message;

	private String date;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getDate() {
		return date;
	}

	public void setDate(String date) {
		this.date = date;
	}
}

(5)创建ChatController,用于接受和处理聊天请求

@Controller
public class ChatController {

    @MessageMapping("/send")
    @SendTo("/topic/send")
    public SocketMessage send(SocketMessage message) throws Exception {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        message.setDate(df.format(new Date()));
        return message;
    }
}

3.2.2客户端代码

在上面的SpringBoot工程的resources目录下创建static目录,static目录下创建index.html

<!DOCTYPE html>
<html>
<head>
<title>websocket</title>
<meta charset="UTF-8">
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script type="text/javascript">
	var stompClient=null;
	function connect(){
        var socket = new SockJS('http://localhost:8080/my-websocket');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            // 注册发送消息
            stompClient.subscribe('/topic/send', function(msg) {
                var body= JSON.parse(msg.body) ;
                document.getElementById('message').innerHTML += "<br>"+body.date+"  "+ body.message;
            });
        });
	}
    connect();//连接
	//发送
    function send() {
        stompClient.send("/app/send", {}, JSON.stringify({
            'message' : document.getElementById('content').value
        }));
    }
</script>
</head>
<body>
	<div>
		<input type="text"
			id="content"  placeholder="请输入内容..." />
		<button onclick="send()" type="button">发送</button>
		<br/> 消息列表: <br/>
		<div id="message"></div>
	</div>
</body>
</html>

3.3入门demo之服务端推送

我们刚才的例子是由客户端发起请求后,服务端给于响应,那如果由服务端主动发起如何实现呢?我们在上边的例子基础上再次添加新的功能,服务端每间隔一秒将服务端时间推送到客户端。

3.3.1服务端代码

创建任务类

@Component
@EnableScheduling
public class Task {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @Scheduled(fixedRate = 1000)
    public void callback() throws Exception {
        // 发现消息
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        messagingTemplate.convertAndSend("/topic/callback", df.format(new Date()));
    }
}

3.3.2客户端代码

修改index.html

function connect(){
        var socket = new SockJS('http://localhost:8080/my-websocket');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            // 注册发送消息
            //.......
          	
            // 注册推送时间回调
            stompClient.subscribe('/topic/callback', function(r) {
                document.getElementById("date").innerHTML='当前服务器时间:' + r.body;
            });
        });
	}

3.4 入门demo之STOMP监听类

(1)创建监听类,此类的方法onApplicationEvent在用户连接时执行,可以获取用户的sessionID以及从前端传递过来的其它信息。

@Component
public class STOMPConnectEventListener implements           ApplicationListener<SessionConnectEvent> {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @Override
    public void onApplicationEvent(SessionConnectEvent event) {
        StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
        String userId = sha.getNativeHeader("login").get(0);
        String sessionId = sha.getSessionId();
        System.out.println("STOMPConnectEventListener........"+userId+"-"+sessionId);    
    }
}

(2)修改页面index.html ,增加我的名字文本框,去掉connect()方法的自动调用

我的名字:<input id="username">
<button onclick="connect()" type="button">进入聊天室</button>

修改connect()方法 ,将当前登录用户名传递给后端

stompClient.connect({"login": document.getElementById("username").value },      function(frame) {
     // 注册发送消息
     // .......
});

测试一下,看看能不能再控制台打印出当前用户名和sessionId

3.5入门demo 之点对点发送

当前用户名可以获取了,我们接下来就可以实现点对点发送了,开启私聊模式 :)

(1)首先我们应该写个静态变量保存当前登录的用户列表,修改Application ,增加属性

	//当前用户列表
	public static Map<String,String> sessionMap=new HashMap<>();

我们以用户名作为key,sessionId为值存到hashMap中

(2)修改STOMPConnectEventListener的onApplicationEvent方法,将获取到的用户信息和sessionID保存到静态变量中

Application.sessionMap.put(agentId,sessionId);

(3)修改消息实体类SocketMessage

public class SocketMessage {
	private String fromUser;
	private String toUser;
	private String message;
	private String date;
	//getter and setter ....
}

(4)修改ChatController

@Controller
public class ChatController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @MessageMapping("/send")
    public void send(SocketMessage message) throws Exception {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        message.setDate(df.format(new Date()));
        String sessionId= Application.sessionMap.get(message.getToUser());
        System.out.println("sessionId:"+sessionId);        messagingTemplate.convertAndSendToUser(sessionId,"/topic/send",message,createHeaders(sessionId));
    }

    private MessageHeaders createHeaders(String sessionId) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);
        return headerAccessor.getMessageHeaders();
    }
}

(5)页面增加文本框

发送给:<input id="toUser">

(6)修改页面index.html 中的JS代码

	var stompClient=null;
	function connect(){
        var socket = new SockJS('http://localhost:8080/my-websocket');
        stompClient = Stomp.over(socket);
        stompClient.connect({"login": document.getElementById("username").value }, function(frame) {
            // 注册发送消息
            stompClient.subscribe('/user/topic/send', function(msg) {
                var body= JSON.parse(msg.body) ;
                document.getElementById('message').innerHTML += "<br>"+body.date+"  "+ body.message;
            });
        });
	}

	//发送
    function send() {
        stompClient.send("/app/send", {}, JSON.stringify({
			'toUser' :document.getElementById('toUser').value,
            'message': document.getElementById('content').value
        }));
    }

3.6 Vue中使用SockJS和StompJS

我们刚才所做的例子都是基于原生方式的写法,那么如何在我们的项目中使用sockJS和StompJS呢?我们这里需要想到有两个问题

  1. sockJS和StompJS如何引用
  2. 跨域如何解决(因为是前后端分离)

我们现在来看实现步骤:

(1)修改服务端代码,解决跨域问题。修改WebSocketConfig的registerStompEndpoints方法

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/my-websocket").setAllowedOrigins("*").withSockJS();
	}

setAllowedOrigins("*")用于解决跨域问题

(2)安装sockJS和StompJS

cnpm install sockjs-client --save
cnpm install stompjs --save
cnpm install net --save

(3)在前端工程增加聊天测试页面 chattest.vue

<template>
  <div>
      昵称:<input v-model="name">
      <button @click="connect">登录聊天室</button>
      发送给:
      <input v-model="message.toUser">
      消息:<input v-model="message.message">
      <button @click="send">发送</button>
      接受到消息:
      <div v-html="info">       
      </div>
  </div>
</template>
<script>
import SockJS from "sockjs-client"
import Stomp from "stompjs"
export default {  
  data(){
      return {name:"",stompClient:null ,message:{toUser:'',message:''},info:""}
  },
  methods:{
      connect(){         
         let socket=new SockJS('http://localhost:8080/my-websocket')
         this.stompClient = Stomp.over(socket)
         this.stompClient.connect({"login": this.name }, this.onConnected);        
      },
      onConnected(frame) {
           this.stompClient.subscribe('/user/topic/send', this.callback)
      },
      callback(msg){
            let body= JSON.parse(msg.body) 
            this.info += "<br>"+body.date+"  "+ body.message         
      },
      send(){
         this.stompClient.send("/app/send", {}, JSON.stringify(this.message));
      }
  }
}
</script>

4.十次方-好友私聊

4.1聊天微服务

(1)创建十次方聊天服务模块 tensquare_chat

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

(2)创建com.tensquare.chat包 ,包下创建启动类

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

(3)创建消息实体类

public class SocketMessage {

	private String fromUser;

	private String toUser;

	private String message;

	private String date;

	// getter and setter ....
}

(4)创建配置类

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		config.enableSimpleBroker("/topic");
		config.setApplicationDestinationPrefixes("/app");
	}

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/tensquare").setAllowedOrigins("*").withSockJS();
	}
}

4.2聊天前台页面

猜你喜欢

转载自blog.csdn.net/weixin_40826349/article/details/86532736
今日推荐