WebsocketServer:
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author yangfeng
**/
@ServerEndpoint("/websocket/{orgCode}")
@Component
public class WebsocketServer {
private static Logger LOG = LoggerFactory.getLogger(WebsocketServer.class);
/**
* 存放所有在线的客户端
*/
private static Map<String, List<Session>> clients = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(@PathParam(value = "orgCode") String orgCode, Session session) {
LOG.info("{}机构,新的客户端连接了: {}", orgCode, session.getId());
List<Session> sessions = clients.get(orgCode);
if (sessions == null) {
sessions = new ArrayList<>();
}
sessions.add(session);
clients.put(orgCode, sessions);
}
/**
* 客户端关闭
*
* @param session session
*/
@OnClose
public void onClose(@PathParam(value = "orgCode") String orgCode, Session session) {
LOG.info("{}机构,用户断开了, id为:{}", orgCode, session.getId());
List<Session> sessions = clients.get(orgCode);
if (!CollectionUtils.isEmpty(sessions)) {
Iterator<Session> sessionIt = sessions.iterator();
while (sessionIt.hasNext()) {
Session s = sessionIt.next();
if (s.getId().equalsIgnoreCase(session.getId())) {
//将掉线的用户移除在线的组里
sessionIt.remove();
}
}
}
}
/**
* 发生错误
*
* @param throwable e
*/
@OnError
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
/**
* 收到客户端发来消息
*
* @param message 消息对象
*/
@OnMessage
public void onMessage(@PathParam(value = "orgCode") String orgCode, String message) {
LOG.info("{}机构,服务端收到客户端发来的消息: {}", orgCode, message);
this.sendAll(orgCode, message);
}
/**
* 群发消息
*
* @param message 消息内容
*/
private void sendAll(String orgCode, String message) {
List<Session> sessions = clients.get(orgCode);
if (!CollectionUtils.isEmpty(sessions)) {
sessions.stream().forEach(o -> o.getAsyncRemote().sendText(message));
}
}
}
vue:
websocket:
<template lang="html">
<header class="header fBox">
<BreadBar/>
<div class="fBoxJe">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<el-button type="warning" icon="el-icon-user-solid" circle size="mini"></el-button> {{userName}} <i
class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="updatePassword" icon="el-icon-key">修改密码</el-dropdown-item>
<el-dropdown-item command="signOut" icon="el-icon-switch-button">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<template v-if="formVisible">
<div class="customClass">
<el-dialog title="修改密码" :visible.sync="formVisible" width="30%">
<el-form :model="form" :rules="rules" ref="form" class="form-class form-input">
<el-form-item label="账户:" label-width="110px">
<el-input v-model="form.userName" disabled="disabled" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="原密码:" label-width="110px" required>
<el-input type="password" v-model="form.originPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="新密码:" label-width="110px" required prop="newPassword">
<el-input type="password" v-model="form.newPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码:" label-width="110px" required>
<el-input type="password" v-model="form.conformPassword" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="formVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="submit" :loading="editLoading">确定</el-button>
</div>
</el-dialog>
</div>
</template>
</div>
</header>
</template>
<script>
import {mapActions, mapState} from 'vuex'
import BreadBar from './BreadBar'
export default {
name: 'headerBar',
components: {BreadBar},
data () {
return {
websock: null,
wsUrl: null,
timer: null,
form: {
userName: '',
originPassword: '',
newPassword: '',
conformPassword: ''
},
formVisible: false,
rules:
{
newPassword: [
{required: true, message: '请输入新密码'},
{pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/, message: '密码为长度6-20的字母和数字组成', trigger: 'blur'}
]
}
}
},
created () {
this.initWebSocket()
},
destroyed () {
this.websock.close() //离开路由之后断开websocket连接
clearInterval(this.timer)
},
computed: {
...mapState('login', [
'user'
]),
userName: function () {
let user = JSON.parse(this.user)
if (user) {
return user.trueName ? user.trueName : user.userName
}
},
...mapState('user', [
'editLoading',
]),
},
methods: {
initWebSocket () { //初始化
let user = JSON.parse(localStorage.getItem('user'))
if (user) {
this.wsUrl = 'ws://192.168.1.121:8080/websocket/' + user.agencyCode
} else {
console.log('用户未登录')
return
}
this.websock = new WebSocket(this.wsUrl)
this.websock.onmessage = this.onmessage
this.websock.onopen = this.onopen
this.websock.onerror = this.onerror
this.websock.onclose = this.onclose
},
onopen () { //连接建立之后执行send方法发送数据
console.log('socket连接成功')
// 添加心跳检测,每30秒发一次数据,防止连接断开(这跟服务器的设置有关,如果服务器没有设置每隔多长时间不发消息断开,可以不进行心跳设置)
let self = this
this.timer = setInterval(() => {
try {
self.websock.send('heart check')
} catch (err) {
console.log('断开了:' + err)
//self.initWebSocket()
}
}, 30000)
},
onmessage (ev) {
if (ev && ev.data && ev.data != 'heart check') {
this.$notify({
title: '系统通知',
dangerouslyUseHTMLString: true,
message: ev.data,
position: 'bottom-right',
duration: 60000,
type: 'warning'
})
}
console.log(ev, '连接正常')
},
onerror () {
console.log('websocket服务出错了---onerror')
this.initWebSocket() // 连接失败后尝试重新连接
},
onclose () {
console.log('websocket服务关闭了+++onclose')
},
...mapActions('login', [
'logout'
]),
...mapActions('user', [
'updatePwd'
]),
handleCommand (command) {
if (command == 'signOut') {
this.signOut()
} else if (command == 'updatePassword') {
this.formVisible = true
let user = JSON.parse(this.user)
this.form.userName = user.userName
this.form.originPassword = ''
this.form.newPassword = ''
this.form.conformPassword = ''
}
},
//修改密码
submit () {
this.$refs['form'].validate((valid) => {
if (valid) {
if (this.form.newPassword != this.form.conformPassword) {
this.$notify.error({
title: '提示',
message: '新密码和确认密码输入不一致'
})
return
}
let data = new FormData()
data.append('userName', this.form.userName)
data.append('originPassword', this.form.originPassword)
data.append('newPassword', this.form.newPassword)
this.updatePwd(data).then(() => {
this.$notify.success({
title: '提示',
message: '操作成功'
})
this.formVisible = false
})
}
})
},
signOut () {
this.$confirm('确定退出登录?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
callback: action => {
if (action == 'confirm') {
this.logout()
.then(res => {
localStorage.clear()
this.$router.replace({name: 'Login'})
})
}
}
})
}
}
}
</script>
<style lang="less">
.header {
width: 100%;
height: 3.17rem;
min-height: 3.17rem;
color: #8492A6;
background: #fff;
padding: 0 2.17rem 0 0.8rem;
box-sizing: border-box;
border-bottom: 1px solid #E7EBEF;
.userName {
margin-left: 5px;
font-size: 1.17rem;
}
.el-dropdown-link {
cursor: pointer;
}
.el-icon-arrow-down {
font-size: 12px;
}
.icon {
width: 1.33rem;
margin-left: 1.67rem;
&.logout {
cursor: pointer;
&:hover {
opacity: .6;
}
}
}
}
</style>