Browse Source

修改合约ws断掉后自动重新获取ws数据

master
liaoxinyu 1 month ago
parent
commit
d88af00904
  1. 4
      src/api/contract.js
  2. 341
      src/api/server/Socket.js
  3. 328
      src/views/contract/handicap.vue
  4. 531
      src/views/contract/index.vue
  5. 191
      src/views/option/kline.vue

4
src/api/contract.js

@ -11,14 +11,14 @@ class Contract {
} }
static getMarketInfo(data) { static getMarketInfo(data) {
return server.get(`/contract/getMarketInfo`, {params:data}) return server.get(`/contract/getMarketInfo`, {params:data,config:{loading:false}})
} }
/** /**
* 获取合约市场 * 获取合约市场
*/ */
static getMarketList(data) { static getMarketList(data) {
return server.get('/contract/getMarketList', {params:data}) return server.get('/contract/getMarketList', {params:data,config:{loading:false}})
} }
/** /**

341
src/api/server/Socket.js

@ -1,52 +1,217 @@
// class Socket {
// constructor(link, ...args) {
// // 初始化socket
// if (link.constructor === WebSocket) {
// this.socket = link;
// } else {
// this.socket = new WebSocket(link);
// }
// // this.socket.binaryType = 'arraybuffer';
// this.doOpen();
// // 连接状态的标识符
// this.readyState = this.socket.readyState;
// // 订阅/发布模型
// this._events = {
// // 订阅的事件 : 发布的方法
// };
// // 定时验证的标识符
// this.heartBeatTimer = null;
// }
// // 执行socket并发布事件
// doOpen() {
// this.afterOpenEmit = [];
// // 执行socket连接 并初始化验证请求
// this.socket.addEventListener("open", evt => this.onOpen(evt));
// // 接收socket数据
// this.socket.addEventListener("message", evt => this.onMessage(evt));
// // 关闭socket连接
// this.socket.addEventListener("close", evt => this.onClose(evt));
// // 请求发生错误
// this.socket.addEventListener("error", err => this.onError(err));
// }
// // 发布后通知订阅者
// Notify(entry) {
// // 检查是否有订阅者 返回队列
// const cbQueue = this._events[entry.Event];
// if (cbQueue && cbQueue.length) {
// for (let callback of cbQueue) {
// if (callback instanceof Function) callback(entry.Data);
// }
// }
// }
// // 请求数据的方法
// onOpen(evt) {
// // 每隔20s检查连接
// // this.heartBeatTimer = setInterval(() => this.send({
// // 'cmd': 'ping',
// // 'args': ''
// // }), 20000);
// // 通知订阅
// this.Notify({Event: 'open', Data : evt});
// }
// /**
// * 订阅所有的数据
// * @param {array|object} datas 订阅参数集合
// */
// send(datas) {
// if (datas.constructor != Array) {
// datas = [datas];
// }
// for (let item of datas) {
// this.socket.send(JSON.stringify(item));
// }
// }
// onMessage(evt) {
// try {
// // 解析推送的数据
// const data = JSON.parse(evt.data);
// // 通知订阅者
// this.Notify({
// Event: 'message',
// Data: data
// });
// } catch (err) {
// console.error(' >> Data parsing error:', err);
// // 通知订阅者
// this.Notify({
// Event: 'error',
// Data: err
// });
// }
// }
// // 添加事件监听
// on(name, handler) {
// this.subscribe(name, handler);
// }
// // 取消订阅事件
// off(name, handler) {
// this.unsubscribe(name, handler);
// }
// // 订阅事件的方法
// subscribe(name, handler) {
// if (this._events.hasOwnProperty(name)) {
// this._events[name].push(handler); // 追加事件
// } else {
// this._events[name] = [handler]; // 添加事件
// }
// }
// // 取消订阅事件
// unsubscribe(name, handler) {
// let start = this._events[name].findIndex(item => item === handler);
// // 删除该事件
// this._events[name].splice(start, 1);
// }
// checkOpen() {
// return this.readyState >= 2;
// }
// onClose(evt) {
// this.Notify({Event: 'close', Data : evt});
// }
// onError(err) {
// this.Notify({Event: 'error', Data : err});
// }
// emit(data) {
// return new Promise((resolve) => {
// this.send(JSON.stringify(data));
// this.on('message', function (data) {
// resolve(data);
// });
// });
// }
// doClose() {
// this.socket.close();
// }
// destroy() {
// if (this.heartBeatTimer) {
// clearInterval(this.heartBeatTimer);
// this.heartBeatTimer = null;
// }
// this.doClose();
// this._events = {};
// this.readyState = 0;
// this.socket = null;
// }
// }
// export default Socket
class Socket { class Socket {
constructor(link, ...args) { constructor(link, ...args) {
// 初始化socket // 修复1:确保 this.link 存的是字符串,方便后面断线重连
if (link.constructor === WebSocket) { if (link.constructor === WebSocket) {
this.socket = link; this.socket = link;
this.link = link.url;
} else { } else {
this.socket = new WebSocket(link); this.socket = new WebSocket(link);
this.link = link;
} }
// this.socket.binaryType = 'arraybuffer';
this.doOpen(); this.doOpen();
// 连接状态的标识符
this.readyState = this.socket.readyState; this.readyState = this.socket.readyState;
// 订阅/发布模型 this._events = {};
this._events = {
// 订阅的事件 : 发布的方法
};
// 定时验证的标识符
this.heartBeatTimer = null; this.heartBeatTimer = null;
// 新增:重连相关的标识符
this.reconnectTimer = null;
this.isReconnecting = false;
this.manualClose = false; // 判断是否是用户主动退出页面
} }
// 执行socket并发布事件
doOpen() { doOpen() {
this.afterOpenEmit = []; this.afterOpenEmit = [];
// 执行socket连接 并初始化验证请求
this.socket.addEventListener("open", evt => this.onOpen(evt)); this.socket.addEventListener("open", evt => this.onOpen(evt));
// 接收socket数据
this.socket.addEventListener("message", evt => this.onMessage(evt)); this.socket.addEventListener("message", evt => this.onMessage(evt));
// 关闭socket连接
this.socket.addEventListener("close", evt => this.onClose(evt)); this.socket.addEventListener("close", evt => this.onClose(evt));
// 请求发生错误
this.socket.addEventListener("error", err => this.onError(err)); this.socket.addEventListener("error", err => this.onError(err));
} }
// 发布后通知订阅者
Notify(entry) { Notify(entry) {
// 检查是否有订阅者 返回队列
const cbQueue = this._events[entry.Event]; const cbQueue = this._events[entry.Event];
if (cbQueue && cbQueue.length) { if (cbQueue && cbQueue.length) {
for (let callback of cbQueue) { for (let callback of cbQueue) {
@ -55,118 +220,152 @@ class Socket {
} }
} }
// 请求数据的方法
onOpen(evt) { onOpen(evt) {
// 连接成功,重置状态
this.isReconnecting = false;
this.manualClose = false;
// 每次连上先清空旧的心跳,防止重复
if (this.heartBeatTimer) clearInterval(this.heartBeatTimer);
// 每隔20s发送心跳
this.heartBeatTimer = setInterval(() => {
this.send({
'cmd': 'ping',
'args': ''
});
}, 20000);
// 每隔20s检查连接
// this.heartBeatTimer = setInterval(() => this.send({
// 'cmd': 'ping',
// 'args': ''
// }), 20000);
// 通知订阅
this.Notify({Event: 'open', Data : evt}); this.Notify({Event: 'open', Data : evt});
} }
/**
* 订阅所有的数据
* @param {array|object} datas 订阅参数集合
*/
send(datas) { send(datas) {
if (datas.constructor != Array) { if (datas.constructor != Array) {
datas = [datas]; datas = [datas];
} }
for (let item of datas) { for (let item of datas) {
this.socket.send(JSON.stringify(item)); // 修复2:发送前必须检查状态,彻底解决 "CLOSING or CLOSED state" 报错!
if (this.socket && this.socket.readyState === 1) {
this.socket.send(JSON.stringify(item));
} else {
console.warn("WebSocket未连接,已拦截发送请求");
}
} }
} }
onMessage(evt) { onMessage(evt) {
try { try {
// 解析推送的数据
const data = JSON.parse(evt.data); const data = JSON.parse(evt.data);
this.Notify({ Event: 'message', Data: data });
// 通知订阅者
this.Notify({
Event: 'message',
Data: data
});
} catch (err) { } catch (err) {
console.error(' >> Data parsing error:', err); console.error(' >> Data parsing error:', err);
this.Notify({ Event: 'error', Data: err });
// 通知订阅者
this.Notify({
Event: 'error',
Data: err
});
} }
} }
// 添加事件监听
on(name, handler) { on(name, handler) {
this.subscribe(name, handler); this.subscribe(name, handler);
} }
// 取消订阅事件
off(name, handler) { off(name, handler) {
this.unsubscribe(name, handler); this.unsubscribe(name, handler);
} }
// 订阅事件的方法
subscribe(name, handler) { subscribe(name, handler) {
if (this._events.hasOwnProperty(name)) { if (this._events.hasOwnProperty(name)) {
this._events[name].push(handler); // 追加事件 this._events[name].push(handler);
} else { } else {
this._events[name] = [handler]; // 添加事件 this._events[name] = [handler];
} }
} }
// 取消订阅事件
unsubscribe(name, handler) { unsubscribe(name, handler) {
if (!this._events[name]) return;
let start = this._events[name].findIndex(item => item === handler); let start = this._events[name].findIndex(item => item === handler);
if (start > -1) {
// 删除该事件 this._events[name].splice(start, 1);
this._events[name].splice(start, 1); }
} }
checkOpen() { checkOpen() {
return this.readyState >= 2; return this.readyState >= 2;
} }
// 修复3:核心重连逻辑
reconnect() {
// 如果是手动关闭(退出页面),或者正在重连中,则放弃重连
if (this.manualClose || this.isReconnecting) return;
this.isReconnecting = true;
// 断网了,立刻停止发心跳
if (this.heartBeatTimer) {
clearInterval(this.heartBeatTimer);
this.heartBeatTimer = null;
}
console.log("WebSocket 掉线,3秒后尝试重新连接...");
this.reconnectTimer = setTimeout(() => {
try {
this.socket = new WebSocket(this.link);
this.doOpen();
} catch (e) {
console.error("重连失败", e);
// 即使抛出异常也解除锁,等待下一次重连尝试
this.isReconnecting = false;
this.reconnect();
return;
}
}, 3000);
}
onClose(evt) { onClose(evt) {
this.Notify({Event: 'close', Data : evt}); this.Notify({Event: 'close', Data : evt});
// 触发关闭时,只要不是主动退出的,就去重连
if (!this.manualClose) this.reconnect();
} }
onError(err) { onError(err) {
this.Notify({Event: 'error', Data : err}); this.Notify({Event: 'error', Data : err});
// 发生错误断开时,去重连
if (!this.manualClose) this.reconnect();
} }
// 修复4:解决内存泄漏,用完立刻注销
emit(data) { emit(data) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.send(JSON.stringify(data)); this.send(JSON.stringify(data));
this.on('message', function (data) {
resolve(data); const handler = (resData) => {
}); resolve(resData);
this.off('message', handler); // 立刻注销,释放内存
};
this.on('message', handler);
}); });
} }
doClose() { doClose() {
this.socket.close(); this.manualClose = true; // 标记为主动关闭,防止变成僵尸无限重连
if (this.socket) {
this.socket.close();
}
} }
destroy() { destroy() {
this.manualClose = true;
if (this.heartBeatTimer) { if (this.heartBeatTimer) {
clearInterval(this.heartBeatTimer); clearInterval(this.heartBeatTimer);
this.heartBeatTimer = null; this.heartBeatTimer = null;
} }
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
this.doClose(); this.doClose();
this._events = {}; this._events = {};
this.readyState = 0; this.readyState = 0;
@ -174,4 +373,4 @@ class Socket {
} }
} }
export default Socket export default Socket;

328
src/views/contract/handicap.vue

@ -136,6 +136,18 @@ export default {
buyList: [], buyList: [],
tradeList: [], tradeList: [],
newPriceObj: {}, newPriceObj: {},
//
wsOpenHandler: null,
wsMessageHandler: null,
collapseHandler: null,
//
isRetrying: false,
lastMessageTime: 0, //
watchdogTimer: null, //
watchdogTimeout: 5000 // 5
}; };
}, },
props: { props: {
@ -194,6 +206,7 @@ export default {
if (this.symbol) { if (this.symbol) {
this.getMarketInfo(); this.getMarketInfo();
this.linkSocket(); this.linkSocket();
this.startWatchdog();
} }
bus.$on('collapse', msg => { bus.$on('collapse', msg => {
this.newPriceObj.price =this.symbol=='BTC'? (msg.close).toFixed(1):(msg.close).toFixed(3); this.newPriceObj.price =this.symbol=='BTC'? (msg.close).toFixed(1):(msg.close).toFixed(3);
@ -201,23 +214,70 @@ export default {
localStorage.setItem("price",this.newPriceObj.price) localStorage.setItem("price",this.newPriceObj.price)
}); });
}, },
beforeDestroy() {
this.unLink(this.symbol);
this.removeSocketListeners();
this.stopWatchdog();
},
// //
methods: { methods: {
parseTime: date.parseTime, parseTime: date.parseTime,
omitTo: math.omitTo, omitTo: math.omitTo,
getMarketInfo() { // getMarketInfo() {
let data = { // let data = {
symbol: this.symbol // symbol: this.symbol
}; // };
Contract.getMarketInfo(data).then(res => { // Contract.getMarketInfo(data).then(res => {
this.sellList = res.swapSellList; // this.sellList = res.swapSellList;
this.buyList = res.swapBuyList; // this.buyList = res.swapBuyList;
this.tradeList = res.swapTradeList; // this.tradeList = res.swapTradeList;
this.newPriceObj = this.tradeList[0]; // this.newPriceObj = this.tradeList[0];
this.$emit("input", this.newPriceObj); // this.$emit("input", this.newPriceObj);
localStorage.setItem("price",this.newPriceObj.price) // localStorage.setItem("price",this.newPriceObj.price)
}); // });
}, // },
// .catch
getMarketInfo() {
if (!this.symbol) return;
let data = { symbol: this.symbol };
Contract.getMarketInfo(data).then(res => {
this.isRetrying = false; //
this.sellList = res.swapSellList;
this.buyList = res.swapBuyList;
this.tradeList = res.swapTradeList;
if(this.tradeList && this.tradeList.length > 0){
this.newPriceObj = this.tradeList[0];
}
this.$emit("input", this.newPriceObj);
localStorage.setItem("price", this.newPriceObj.price);
}).catch(err => {
// 🔥 (Uncaught in promise)
console.error("盘口接口请求失败/超时,3秒后自动重试...", err);
//
if (!this.isRetrying) {
this.isRetrying = true;
setTimeout(() => {
this.isRetrying = false;
this.getMarketInfo(); //
// 便 WS
if (this.ws && !this.ws.checkOpen()) {
console.log("检测到网络较差,尝试重连 WebSocket...");
this.ws.reconnect();
}
}, 3000); // 3
}
});
},
// //
getValue(amount) { getValue(amount) {
const arr = this.buyListShow const arr = this.buyListShow
@ -226,74 +286,182 @@ export default {
let max = Math.max(...arr); let max = Math.max(...arr);
return math.division(amount, max, 2) * 100; return math.division(amount, max, 2) * 100;
}, },
// socket
linkSocket() {
if (this.ws.socket.readyState == 1) {
this.ws.send({
cmd: "sub",
msg: this.msg.buy
});
this.ws.send({
cmd: "sub",
msg: this.msg.sell
});
this.ws.send({
cmd: "sub",
msg: this.msg.trade
});
} else {
this.ws.on("open", () => {
this.ws.send({
cmd: "sub",
msg: this.msg.buy
});
this.ws.send({
cmd: "sub",
msg: this.msg.sell
});
this.ws.send({
cmd: "sub",
msg: this.msg.trade
});
});
}
this.ws.on("message", res => {
let { data, msg, code, sub, type, status, cmd } = res;
if (sub == this.msg.buy) {
this.buyList = data;
} else if (sub == this.msg.sell) {
this.sellList = data;
} else if (sub == this.msg.trade) {
this.tradeList.unshift(data);
this.tradeList.pop();
// this.newPriceObj = data;
// this.$emit("input", this.newPriceObj);
} else if (type == "ping" ||cmd == "ping") {
this.ws.send({
cmd: "pong"
});
}
});
},
// //
unLink(symbol) { sendSubscribe() {
// 线 if (!this.ws || !this.ws.socket || this.ws.socket.readyState !== 1) return;
this.ws.send({ this.ws.send({ cmd: "sub", msg: this.msg.buy });
cmd: "unsub", this.ws.send({ cmd: "sub", msg: this.msg.sell });
msg: `swapBuyList_${symbol}` this.ws.send({ cmd: "sub", msg: this.msg.trade });
}); },
// 线
this.ws.send({ //
cmd: "unsub", removeSocketListeners() {
msg: `swapSellList_${symbol}` if (this.wsOpenHandler && this.ws) {
}); this.ws.off("open", this.wsOpenHandler);
// }
this.ws.send({ if (this.wsMessageHandler && this.ws) {
cmd: "unsub", this.ws.off("message", this.wsMessageHandler);
msg: `swapTradeList_${symbol}` }
}); },
}
// WebSocket
startWatchdog() {
this.stopWatchdog();
this.lastMessageTime = Date.now();
this.watchdogTimer = setInterval(() => {
const now = Date.now();
if (now - this.lastMessageTime > this.watchdogTimeout) {
console.error(`[看门狗报警] 已经 ${this.watchdogTimeout/1000} 秒没收到推送了!判定为后端假死!正在强行掐断并新建 WebSocket...`);
// 1. HTTP
this.getMarketInfo();
// 2. Socket
if (this.ws) {
//
if (this.ws.socket) {
this.ws.socket.close();
}
// reconnect
this.ws.isReconnecting = false;
this.ws.manualClose = false;
this.ws.reconnect();
}
// 3.
this.lastMessageTime = Date.now();
}
}, 5000); // 5
},
// 🔥
stopWatchdog() {
if (this.watchdogTimer) {
clearInterval(this.watchdogTimer);
this.watchdogTimer = null;
}
},
// WebSocket
linkSocket() {
if (!this.ws) return;
this.removeSocketListeners();
// open
this.wsOpenHandler = () => {
this.sendSubscribe();
};
this.ws.on("open", this.wsOpenHandler);
//
this.wsMessageHandler = res => {
let { data, msg, code, sub, type, status, cmd } = res;
if (sub == this.msg.buy || sub == this.msg.sell || sub == this.msg.trade) {
this.lastMessageTime = Date.now();
}
if (sub == this.msg.buy) {
this.buyList = data;
} else if (sub == this.msg.sell) {
this.sellList = data;
} else if (sub == this.msg.trade) {
this.tradeList.unshift(data);
this.tradeList.pop();
} else if (type == "ping" || cmd == "ping") {
this.ws.send({ cmd: "pong" });
}
};
this.ws.on("message", this.wsMessageHandler);
//
if (this.ws.socket && this.ws.socket.readyState === 1) {
this.sendSubscribe();
}
this.startWatchdog(); //
},
unLink(symbol) {
if (!this.ws || !this.ws.socket || this.ws.socket.readyState !== 1) return;
this.ws.send({ cmd: "unsub", msg: `swapBuyList_${symbol}` });
this.ws.send({ cmd: "unsub", msg: `swapSellList_${symbol}` });
this.ws.send({ cmd: "unsub", msg: `swapTradeList_${symbol}` });
}
// // socket
// linkSocket() {
// if (this.ws.socket.readyState == 1) {
// this.ws.send({
// cmd: "sub",
// msg: this.msg.buy
// });
// this.ws.send({
// cmd: "sub",
// msg: this.msg.sell
// });
// this.ws.send({
// cmd: "sub",
// msg: this.msg.trade
// });
// } else {
// this.ws.on("open", () => {
// this.ws.send({
// cmd: "sub",
// msg: this.msg.buy
// });
// this.ws.send({
// cmd: "sub",
// msg: this.msg.sell
// });
// this.ws.send({
// cmd: "sub",
// msg: this.msg.trade
// });
// });
// }
// this.ws.on("message", res => {
// let { data, msg, code, sub, type, status, cmd } = res;
// if (sub == this.msg.buy) {
// this.buyList = data;
// } else if (sub == this.msg.sell) {
// this.sellList = data;
// } else if (sub == this.msg.trade) {
// this.tradeList.unshift(data);
// this.tradeList.pop();
// // this.newPriceObj = data;
// // this.$emit("input", this.newPriceObj);
// } else if (type == "ping" ||cmd == "ping") {
// this.ws.send({
// cmd: "pong"
// });
// }
// });
// },
// //
// unLink(symbol) {
// // 线
// this.ws.send({
// cmd: "unsub",
// msg: `swapBuyList_${symbol}`
// });
// // 线
// this.ws.send({
// cmd: "unsub",
// msg: `swapSellList_${symbol}`
// });
// //
// this.ws.send({
// cmd: "unsub",
// msg: `swapTradeList_${symbol}`
// });
// }
} }
}; };
</script> </script>

531
src/views/contract/index.vue

@ -223,6 +223,259 @@
</div> </div>
</template> </template>
<script> <script>
// import kline from "../option/kline";
// import handicap from "./handicap.vue";
// import account from "./account.vue";
// import exchangeStore from "./exchange-store.vue";
// import pageBottom from "./page-bottom.vue";
// import Contract from "../../api/contract";
// import Socket from "@/api/server/Socket.js";
// import Home from "@/api/home";
// import bus from "@/components/bus.js";
// export default {
// components: {
// kline,
// handicap,
// account,
// exchangeStore,
// pageBottom
// },
// data() {
// return {
// contractList: [],
// activeSymbol: "",
// holdPositionAll: false,
// holdPositionList: [],
// newPriceObj: {},
// pcBannerList:[],
// accountInfo: {},
// defaultPrice: "",
// wsUrl: this.Globals.Server.Path.WS1,
// ws: new Socket(this.Globals.Server.Path.WS1),
// _time: null,
// contractOpen: false,
// contractAgreement: {},
// symbolDetail:{},
// currentIcon: '',
// filterCoin: "",
// contractListFilter: [],
// price1:0,
// intervalId:''
// };
// },
// computed: {
// activeContract(val) {
// let contractList=this.contractList
// .map(item => item.marketInfoList)
// .flat()
// .find(item => item.symbol == this.activeSymbol) || {}
// if(val.price){
// delete contractList.price
// }
// return contractList;
// },
// isLogin() {
// return Boolean(localStorage.token);
// },
// // contractListFilter(){
// // return this.contractList.map((item1, index1)=>{
// // item1.marketInfoList.filter(item2=>{
// // return item2.symbol.indexOf(this.filterCoin)!=-1
// // })
// // return item1;
// // })
// // }
// },
// watch: {
// activeSymbol() {
// this.holdPosition();
// },
// // filterCoin: {
// // handler(n,o){
// // if(!n){
// // this.contractListFilter = this.contractList;
// // }else{
// // this.contractListFilter = this.contractList.map((item1, index1)=>{
// // let arr = item1;
// // arr.marketInfoList = item1.marketInfoList.filter(item2=>{
// // return item2.symbol.indexOf(n.toUpperCase())!=-1
// // })
// // return arr;
// // })
// // }
// // },
// // immediate: true
// // }
// },
// destroyed() {
// clearInterval(this._time);
// },
// created() {
// // this.openStatus();
// this.getMarketList();
// this.holdPosition();
// this.indexList();
// this._time = setInterval(() => {
// if (this.contractOpen) {
// this.holdPosition();
// }
// }, 3000);
// this.ws.on("open", () => {
// this.swapMarketList();
// });
// // console.info(this.$refs)
// bus.$on('collapse', msg => {
// this.activeContract.price =this.activeContract.symbol=='BTC'? (msg.close).toFixed(1):(msg.close).toFixed(3);
// this.holdPositionList.map(item=>{
// if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price
// })
// });
// this.startWatchingPrice();
// },
// //
// mounted: function () {
// setInterval(() => {
// this.holdPosition();
// }, 2000)
// },
// methods: {
// startWatchingPrice(){
// this.intervalId = setInterval(() => {
// let newPrice = localStorage.getItem('price');
// this.price1 = newPrice;
// }, 10); //
// },
// ispopover1(item){
// this.activeSymbol=item;
// // this.$refs.popover.showPopper = false;
// this.getMarketList();
// },
// swapMarketList() {
// let msg = "swapMarketList";
// this.ws.send({
// cmd: "sub",
// msg: msg
// });
// this.ws.on("message", res => {
// let { data, sub,cmd } = res;
// if (sub == msg) {
// if( data.symbol=='BTC' )(data.price).toFixed(1)
// this.contractList = data;
// if(this.filterCoin==''){
// this.contractListFilter = data;
// }
// }else if (cmd == "ping") {
// this.ws.send({
// cmd: "pong"
// });
// }
// });
// this.ws.on('close',()=>{
// this.ws= new Socket(this.Globals.Server.Path.WS1),
// console.log('');
// this.wsOpen();
// })
// },
// wsOpen(){
// this.ws.on("open", () => {
// this.swapMarketList();
// this.$refs.handicap.linkSocket()
// });
// },
// //
// getMarketList() {
// this.currentIcon = "";
// Contract.getMarketList().then(res => {
// if(this.activeSymbol==''){
// //
// this.contractList = res;
// if(this.filterCoin==''){
// this.contractListFilter = res;
// }
// //
// let firstParent = res[0];
// if (firstParent) {
// let first = firstParent.marketInfoList[0];
// if (first) this.activeSymbol = first.symbol;
// this.currentIcon = res[0].marketInfoList[0].icon;
// }
// }else{
// // icon
// for(let item of res[0].marketInfoList){
// if(this.activeSymbol==item.symbol){
// this.currentIcon = item.icon;
// }
// }
// }
// });
// },
// //
// holdPosition() {
// if (!this.isLogin) return;
// let data = {
// symbol: (this.holdPositionAll && this.activeSymbol) || ""
// };
// Contract.holdPosition(data, { loading: false }).then(res => {
// // console.info(res)
// res.map(item=>{
// if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price
// })
// this.holdPositionList = res;
// });
// },
// //
// openStatus() {
// if(!this.isLogin) return;
// Contract.openStatus().then(res => {
// this.contractOpen = res.open;
// if (!this.contractOpen) {
// this.contractAgreement = res.contractAgreement;
// $("#openContract").modal("show");
// }
// });
// },
// //
// opening() {
// Contract.opening().then(res => {
// $("#openContract").modal("hide");
// this.contractOpen = true;
// this.$message.success(this.$t('contract.j9'));
// });
// },
// indexList() {
// Home.indexList().then((res) => {
// this.pcBannerList = res.pcBannerList
// setTimeout(() => {
// this.skroll();
// }, 100);
// }).catch((res) => {});
// },
// //
// setactiveItem(index=0){
// this.accountInfo=this.holdPositionList[index]
// },
// enterFilter(){
// },
// //
// isShow(symbol) {
// const reg = new RegExp(this.filterCoin, "gi");
// if (!this.filterCoin) return true;
// else {
// // let name = symbol.coinName || symbol.pair;
// // return name.search(reg) >= 0;
// let name = symbol.pair_name || symbol.symbol;
// return name.search(reg) >= 0;
// }
// // return !this.keyword || symbol.coinName.concat(symbol.pair).search(reg) >= 0;
// },
// }
// };
import kline from "../option/kline"; import kline from "../option/kline";
import handicap from "./handicap.vue"; import handicap from "./handicap.vue";
import account from "./account.vue"; import account from "./account.vue";
@ -232,6 +485,7 @@ import Contract from "../../api/contract";
import Socket from "@/api/server/Socket.js"; import Socket from "@/api/server/Socket.js";
import Home from "@/api/home"; import Home from "@/api/home";
import bus from "@/components/bus.js"; import bus from "@/components/bus.js";
export default { export default {
components: { components: {
kline, kline,
@ -250,17 +504,32 @@ export default {
pcBannerList:[], pcBannerList:[],
accountInfo: {}, accountInfo: {},
defaultPrice: "", defaultPrice: "",
// Socket
wsUrl: this.Globals.Server.Path.WS1, wsUrl: this.Globals.Server.Path.WS1,
ws: new Socket(this.Globals.Server.Path.WS1), ws: null,
_time: null, _time: null,
holdTimer: null, //
contractOpen: false, contractOpen: false,
contractAgreement: {}, contractAgreement: {},
symbolDetail:{}, symbolDetail:{},
currentIcon: '', currentIcon: '',
filterCoin: "", filterCoin: "",
contractListFilter: [], contractListFilter: [],
price1:0, Liste:[],
intervalId:'' price1:0,
intervalId:'',
//
swapMarketHandler: null,
wsOpenHandler: null,
collapseHandler: null,
// 🔥
lastMessageTime: 0,
watchdogTimer: null,
watchdogTimeout: 15000 // 15
}; };
}, },
computed: { computed: {
@ -277,141 +546,203 @@ export default {
isLogin() { isLogin() {
return Boolean(localStorage.token); return Boolean(localStorage.token);
}, },
// contractListFilter(){ set_price() {
// return this.contractList.map((item1, index1)=>{ return this.$store.getters.set_price;
// item1.marketInfoList.filter(item2=>{ }
// return item2.symbol.indexOf(this.filterCoin)!=-1
// })
// return item1;
// })
// }
}, },
watch: { watch: {
activeSymbol() { activeSymbol() {
this.holdPosition(); this.holdPosition();
}, },
// filterCoin: {
// handler(n,o){
// if(!n){
// this.contractListFilter = this.contractList;
// }else{
// this.contractListFilter = this.contractList.map((item1, index1)=>{
// let arr = item1;
// arr.marketInfoList = item1.marketInfoList.filter(item2=>{
// return item2.symbol.indexOf(n.toUpperCase())!=-1
// })
// return arr;
// })
// }
// },
// immediate: true
// }
},
destroyed() {
clearInterval(this._time);
}, },
created() { created() {
// this.openStatus(); // Socket
this.ws = new Socket(this.wsUrl);
this.getMarketList(); this.getMarketList();
this.holdPosition(); this.holdPosition();
this.indexList(); this.indexList();
this._time = setInterval(() => { this._time = setInterval(() => {
if (this.contractOpen) { if (this.contractOpen) {
this.holdPosition(); this.holdPosition();
} }
}, 3000); }, 3000);
this.ws.on("open", () => {
this.swapMarketList(); //
}); this.collapseHandler = msg => {
// console.info(this.$refs) this.activeContract.price = this.activeContract.symbol=='BTC'? (msg.close).toFixed(1):(msg.close).toFixed(3);
bus.$on('collapse', msg => {
this.activeContract.price =this.activeContract.symbol=='BTC'? (msg.close).toFixed(1):(msg.close).toFixed(3);
this.holdPositionList.map(item=>{ this.holdPositionList.map(item=>{
if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price
}) })
}); };
this.startWatchingPrice(); bus.$on('collapse', this.collapseHandler);
// 🔥 WebSocket Open
this.wsOpenHandler = () => {
console.log("PC主页面 WebSocket 连上/重连成功,请求市场列表...");
this.swapMarketList();
};
this.ws.on("open", this.wsOpenHandler);
this.startWatchingPrice();
this.startWatchdog(); //
}, },
//
mounted: function () { mounted() {
setInterval(() => { //
this.holdTimer = setInterval(() => {
this.holdPosition(); this.holdPosition();
}, 2000) }, 2000);
}, },
//
beforeDestroy() {
clearInterval(this._time);
clearInterval(this.intervalId);
if (this.holdTimer) clearInterval(this.holdTimer);
this.stopWatchdog();
if (this.collapseHandler) {
bus.$off('collapse', this.collapseHandler);
}
if (this.ws) {
if (this.wsOpenHandler) this.ws.off("open", this.wsOpenHandler);
if (this.swapMarketHandler) this.ws.off("message", this.swapMarketHandler);
this.ws.send({ cmd: "unsub", msg: "swapMarketList" });
this.ws.destroy();
this.ws = null;
}
},
methods: { methods: {
startWatchingPrice(){ startWatchingPrice(){
this.intervalId = setInterval(() => { // 10 100 CPU
let newPrice = localStorage.getItem('price'); this.intervalId = setInterval(() => {
this.price1 = newPrice; let newPrice = localStorage.getItem('price');
}, 10); // this.price1 = newPrice;
}, }, 100);
},
ispopover1(item){ ispopover1(item){
this.activeSymbol=item; this.activeSymbol=item;
// this.$refs.popover.showPopper = false;
this.getMarketList(); this.getMarketList();
}, },
// 🔥
startWatchdog() {
this.stopWatchdog();
this.lastMessageTime = Date.now();
this.watchdogTimer = setInterval(() => {
if (Date.now() - this.lastMessageTime > this.watchdogTimeout) {
console.error("[PC市场列表看门狗] 超过15秒未收到推送数据,判定后端假死!正在强行掐断并新建 WS...");
// HTTP
this.getMarketList();
//
if (this.ws) {
if (this.ws.socket) this.ws.socket.close();
this.ws.isReconnecting = false;
this.ws.manualClose = false;
this.ws.reconnect();
}
this.lastMessageTime = Date.now(); //
}
}, 5000);
},
stopWatchdog() {
if (this.watchdogTimer) {
clearInterval(this.watchdogTimer);
this.watchdogTimer = null;
}
},
swapMarketList() { swapMarketList() {
let msg = "swapMarketList"; let msg = "swapMarketList";
this.ws.send({
cmd: "sub", //
msg: msg if (this.ws && this.ws.checkOpen()) {
}); this.ws.send({ cmd: "sub", msg: msg });
this.ws.on("message", res => { }
let { data, sub,cmd } = res;
//
if (this.swapMarketHandler) {
this.ws.off("message", this.swapMarketHandler);
}
//
this.swapMarketHandler = res => {
let { data, sub, cmd } = res;
//
if (sub == msg || cmd == "ping") {
this.lastMessageTime = Date.now();
}
if (sub == msg) { if (sub == msg) {
if( data.symbol=='BTC' )(data.price).toFixed(1) if( data.symbol=='BTC' )(data.price).toFixed(1)
this.contractList = data; this.contractList = data;
if(this.filterCoin==''){ if(this.filterCoin==''){
this.contractListFilter = data; this.contractListFilter = data;
this.Listes();
} }
}else if (cmd == "ping") { } else if (cmd == "ping") {
this.ws.send({ this.ws.send({ cmd: "pong" });
cmd: "pong"
});
} }
}); };
this.ws.on('close',()=>{
this.ws= new Socket(this.Globals.Server.Path.WS1), this.ws.on("message", this.swapMarketHandler);
console.log('链接关闭');
this.wsOpen();
})
},
wsOpen(){
this.ws.on("open", () => {
this.swapMarketList();
this.$refs.handicap.linkSocket()
});
}, },
// //
getMarketList() { getMarketList() {
this.currentIcon = ""; this.currentIcon = "";
Contract.getMarketList().then(res => { Contract.getMarketList().then(res => {
if(this.activeSymbol==''){ if(this.activeSymbol==''){
//
this.contractList = res; this.contractList = res;
if(this.filterCoin==''){ if(this.filterCoin==''){
this.contractListFilter = res; this.contractListFilter = res;
this.Listes();
} }
//
let firstParent = res[0]; let firstParent = res[0];
if (firstParent) { if (firstParent) {
let first = firstParent.marketInfoList[0]; let first = firstParent.marketInfoList[0];
if (first) this.activeSymbol = first.symbol; if (first) this.activeSymbol = first.symbol;
this.currentIcon = res[0].marketInfoList[0].icon; this.currentIcon = res[0].marketInfoList[0].icon;
} }
}else{ } else {
// icon if (res && res[0]) {
for(let item of res[0].marketInfoList){ for(let item of res[0].marketInfoList){
if(this.activeSymbol==item.symbol){ if(this.activeSymbol==item.symbol){
this.currentIcon = item.icon; this.currentIcon = item.icon;
} }
}
} }
} }
}); });
}, },
Listes(){
this.Liste = [];
if (!this.contractListFilter || !this.contractListFilter[0] || !this.contractListFilter[0].marketInfoList) return;
this.contractListFilter[0].marketInfoList.map(item => {
this.Liste.push({
pair_name: item.pair_name,
symbol: item.symbol,
icon: item.icon
});
});
},
// //
holdPosition() { holdPosition() {
if (!this.isLogin) return; if (!this.isLogin) return;
@ -419,14 +750,13 @@ export default {
symbol: (this.holdPositionAll && this.activeSymbol) || "" symbol: (this.holdPositionAll && this.activeSymbol) || ""
}; };
Contract.holdPosition(data, { loading: false }).then(res => { Contract.holdPosition(data, { loading: false }).then(res => {
// console.info(res)
res.map(item=>{ res.map(item=>{
if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price
}) })
this.holdPositionList = res; this.holdPositionList = res;
}); });
}, },
//
openStatus() { openStatus() {
if(!this.isLogin) return; if(!this.isLogin) return;
Contract.openStatus().then(res => { Contract.openStatus().then(res => {
@ -437,7 +767,7 @@ export default {
} }
}); });
}, },
//
opening() { opening() {
Contract.opening().then(res => { Contract.opening().then(res => {
$("#openContract").modal("hide"); $("#openContract").modal("hide");
@ -445,35 +775,32 @@ export default {
this.$message.success(this.$t('contract.j9')); this.$message.success(this.$t('contract.j9'));
}); });
}, },
indexList() { indexList() {
Home.indexList().then((res) => { Home.indexList().then((res) => {
this.pcBannerList = res.pcBannerList this.pcBannerList = res.pcBannerList
setTimeout(() => {
this.skroll();
}, 100);
}).catch((res) => {}); }).catch((res) => {});
}, },
//
setactiveItem(index=0){ setactiveItem(index=0){
this.accountInfo=this.holdPositionList[index] this.accountInfo = this.holdPositionList[index];
}, },
enterFilter(){
isShow(symbol) {
const reg = new RegExp(this.filterCoin, "gi");
if (!this.filterCoin) return true;
else {
let name = symbol.pair_name || symbol.symbol;
return name.search(reg) >= 0;
}
}, },
//
isShow(symbol) { enterFilter(){
const reg = new RegExp(this.filterCoin, "gi"); //
if (!this.filterCoin) return true; }
else {
// let name = symbol.coinName || symbol.pair;
// return name.search(reg) >= 0;
let name = symbol.pair_name || symbol.symbol;
return name.search(reg) >= 0;
}
// return !this.keyword || symbol.coinName.concat(symbol.pair).search(reg) >= 0;
},
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.justify-content-between ::v-deep .dark-app .theme-switch .theme-switch-button .left, .dark-app .theme-switch .theme-switch-button .right{ .justify-content-between ::v-deep .dark-app .theme-switch .theme-switch-button .left, .dark-app .theme-switch .theme-switch-button .right{

191
src/views/option/kline.vue

@ -122,60 +122,155 @@ data() {
sub: "", sub: "",
isCreateSocket: false, isCreateSocket: false,
page: {}, page: {},
tt:undefined tt:undefined,
//
wsOpenHandler: null,
wsMessageHandler: null,
// K线
lastMessageTime: 0,
watchdogTimer: null,
watchdogTimeout: 5000 // 5
}; };
}, },
// Socket
beforeDestroy() {
if (this.ws) {
this.stopWatchdog();
if (this.sub) this.unsub(this.ajaxTv.getSymbol(this.symbol), this.timer);
this.ws.destroy(); // destroy
this.ws = null;
}
},
methods: { methods: {
reconnect() { // reconnect() {
if(this.isCreateSocket) { // if(this.isCreateSocket) {
return; // return;
}; // };
let msgObj = { // let msgObj = {
cmd: "sub", // cmd: "sub",
msg: this.sub, // msg: this.sub,
}; // };
this.isCreateSocket = true; // this.isCreateSocket = true;
// // //
this.tt && clearTimeout(this.tt); // this.tt && clearTimeout(this.tt);
let th=this // let th=this
th.tt = setTimeout(function () { // th.tt = setTimeout(function () {
th.isCreateSocket = false; // th.isCreateSocket = false;
th.linkSocket(() => { // th.linkSocket(() => {
th.ws.send(msgObj); // th.ws.send(msgObj);
}); // });
}, 4000); // }, 4000);
}, // },
// socket // // socket
linkSocket(call) { // linkSocket(call) {
const ws = new Socket(this.wsUrl); // const ws = new Socket(this.wsUrl);
ws.on("open", () => { // ws.on("open", () => {
this.ws = ws; // this.ws = ws;
call && call(); // call && call();
}); // });
// ws.on("message", (res) => {
// let { data, msg, code, sub, type, status, req, cmd } = res;
// if (sub == this.sub && this.onRealtimeCallback) {
// if(sub.indexOf("BTC")!= -1) (data.close).toFixed(1)
// this.onRealtimeCallback(this.getMap(data));
// bus.$emit('collapse', data);
// } else if (type == "ping" || cmd == "ping") {
// this.ws.send({
// cmd: "pong",
// });
// }
// });
// ws.on('close',()=>{
// console.log('');
// this.reconnect();
// })
// ws.on('error',()=>{
// console.log('');
// this.reconnect();
// })
// },
// K线 15 WS
startWatchdog() {
this.stopWatchdog();
this.lastMessageTime = Date.now();
this.watchdogTimer = setInterval(() => {
if (Date.now() - this.lastMessageTime > this.watchdogTimeout) {
console.warn("[K线报警] 15秒没收到K线数据,强制重启底层WebSocket!");
if (this.ws) {
if (this.ws.socket) this.ws.socket.close();
this.ws.isReconnecting = false;
this.ws.manualClose = false;
this.ws.reconnect();
}
this.lastMessageTime = Date.now(); //
}
}, 5000);
},
stopWatchdog() {
if (this.watchdogTimer) {
clearInterval(this.watchdogTimer);
this.watchdogTimer = null;
}
},
// reconnect Socket.js
linkSocket(call) {
// WS
if (!this.ws) {
this.ws = new Socket(this.wsUrl);
// open线
this.ws.on("open", () => {
console.log("K线 WebSocket 连接/重连成功!");
//
if (call) {
call();
call = null; //
}
//
if (this.sub) {
this.ws.send({ cmd: "sub", msg: this.sub });
}
});
this.ws.on("message", (res) => {
let { data, sub, type, cmd } = res;
//
if (sub == this.sub || type == "ping" || cmd == "ping") {
this.lastMessageTime = Date.now();
}
if (sub == this.sub && this.onRealtimeCallback) {
if(sub.indexOf("BTC") != -1) (data.close).toFixed(1);
this.onRealtimeCallback(this.getMap(data));
bus.$emit('collapse', data);
} else if (type == "ping" || cmd == "ping") {
this.ws.send({ cmd: "pong" });
}
});
} else {
//
if (call) call();
if (this.ws.checkOpen() && this.sub) {
this.ws.send({ cmd: "sub", msg: this.sub });
}
}
this.startWatchdog(); //
},
ws.on("message", (res) => {
let { data, msg, code, sub, type, status, req, cmd } = res;
if (sub == this.sub && this.onRealtimeCallback) {
if(sub.indexOf("BTC")!= -1) (data.close).toFixed(1)
this.onRealtimeCallback(this.getMap(data));
bus.$emit('collapse', data);
} else if (type == "ping" || cmd == "ping") {
this.ws.send({
cmd: "pong",
});
}
});
ws.on('close',()=>{
console.log('链接关闭');
this.reconnect();
})
ws.on('error',()=>{
console.log('发生异常了');
this.reconnect();
})
},
// //
unsub(pair_id, timer) { unsub(pair_id, timer) {
let msgObj = { let msgObj = {

Loading…
Cancel
Save