import APP from '@/services'; import { getGoodsByCode } from '@/services/bus/goods'; import { geLoginID_number, isLogin } from '@/services/bus/login'; import { getToken } from '@/services/bus/token'; import { funCode } from '@/services/funcode/index'; import { QueryQuoteDayRsp } from '@/services/go/quote/interface'; import { Package40 } from '@/utils/websocket/package'; import Long from 'long'; import moment from 'moment'; import { SubscribeInfoType } from '../interface'; import { byteArrayToUInt, subscribeInfosToByteArrary } from './byteUtils'; /** * 构建行情订阅请求包 */ export function buildSubscribePeq(subscribeInfos: SubscribeInfoType[]): Package40 { // 如果是登录状态,就走正常行情链路. 否则走游客 const loginId = isLogin() ? Long.fromNumber(geLoginID_number()) : Long.fromNumber(2); const token = isLogin() ? getToken() : '2_TOKEN_NEKOT_'; const content = subscribeInfosToByteArrary(subscribeInfos, token, loginId); const reqPackage = new Package40([funCode.MainClassNumber_Quota_SubscriptReq, content], undefined); return reqPackage; } /** * 解析行情订阅请求包 * @param rspPackage */ export function parseSubscribeRsp(rspPackage: any): Promise { return new Promise((resolve, reject) => { const content = rspPackage.content; if (content.length === 0) { // 有可能空订阅,也有可能订阅返回数据为空,但其实已经订阅了商品 // 暂做正常返回 空字符串 处理,如遇到问题继续优化即可 console.warn('行情订阅请求包返回为空'); resolve(''); } const count = byteArrayToUInt(content.subarray(0, 4), false); if (count > 1000) { // 游客模式 刷新成长问题,为了不让页面卡死,暂时处理 return null; } switch (count) { case -1: reject('订阅失败'); break; case -2: reject('Token校验失败'); break; case -3: reject('无对应商品信息'); break; default: break; } // 判断返回的Count是否有问题 if (!count || count > (content.length - 4) / 66) { reject('行情订阅返回数据不正确'); } const result: SubscribeInfoType[] = []; let position = 4; for (let i = 0; i < count; i++) { const dataArray = content.subarray(position, position + 66); const subState = dataArray[0]; // FIXME: 目前行情接入服务不能正确返回交易所代码 const exchangeCode = dataArray[2]; const goodsCode = String.fromCharCode(...dataArray.subarray(2, dataArray.length)).trim(); result.push({ subState, goodsCode, exchangeCode }); position += 66; } resolve(result); }); } /** * 解析行情推送报文 * @param {Array} quotationData 行情信息 * @param {Boolean} isPushQuote 实时行情推送为true;盘面为false * */ export function parseReceivePush(quotationData: any) { // 目前发现可能会传入空数组 if (!quotationData.length) { return; } // 分解行正则 const regRow = /10\s.*?11/g; // 分解单行 key 正则 const regKey = /01\s.*?02/g; // 分解单行 value 正则 const regValue = /02\s.*?01|02\s.*?11/g; // 记录已经更新盘面的商品信息数组 // 0x10 ... 0x01 key 0x02 value 0x01 key 0x02 value ... 0x11 const hexString = Array.prototype.map.call(quotationData, (x) => ('00' + x.toString(16)).slice(-2)).join(' '); // let hexString = 'FF 10 01 55 02 45 46 47 48 01 66 02 48 47 46 45 11 10 01 77 02 AA BB CC DD 11 00'; // 获取单行行情 const rows = hexString.match(regRow); if (rows === null) { return; } for (const low of rows) { // 获取 key value 表列 const keys: any = low.match(regKey); const values: any = low.match(regValue); const goodsQuoteTik: any = {}; for (let i = 0; i < keys.length; i++) { const key = parseInt(keys[i].substring(3, 5), 16); const tmpValue = values[i]; const value = new Uint8Array( tmpValue .substring(3, tmpValue.length - 3) .split(' ') .map((byte: any) => parseInt(byte, 16)) ); // 缓存值 setQuoteTikFieldByByte(goodsQuoteTik, key, value); } const quoteDayInfo = APP.getRef('quoteDayInfo'); const itemQuote = quoteDayInfo.value.find((el: QueryQuoteDayRsp) => { const goodcode = el.refgoodscode || el.goodscode // console.log(el.goodscode, el.refgoodscode, goodsQuoteTik.goodscode) return goodcode === goodsQuoteTik.goodscode }); if (itemQuote) { const goods = getGoodsByCode(itemQuote.goodscode); if (goods) { console.log('goodsQuoteTik', goodsQuoteTik) // 处理报价小数为 const decimalplace = goods.decimalplace const num = Math.pow(10, decimalplace) const fn = (value: number) => +(value / num).toFixed(decimalplace) // // 处理 报价小数位 const handleDeimalplace = (key: string) => { return Reflect.has(goodsQuoteTik, key) ? fn(goodsQuoteTik[key]) : itemQuote[key] } // 处理 如果有值则更新值,没有的话,则取上次的值 // 注意: 0 也是有效数字 const handleNoneValue = (key: string) => { return Reflect.has(goodsQuoteTik, key) ? goodsQuoteTik[key] : itemQuote[key] } const changeValue = () => { // 实时行情由于行情源的问题可能不会下发现量,这时用盘面的总量来计算 if (!goodsQuoteTik.lastvolume) { if (itemQuote.totalvolume) { itemQuote.lastvolume = goodsQuoteTik.totalvolume - itemQuote.totalvolume; } } // 处理 最高最低价 if (Reflect.has(goodsQuoteTik, 'last')) { const last = itemQuote.last = handleDeimalplace('last') if (last < itemQuote.lowest) { itemQuote.lowest = handleDeimalplace('last') } else if (last > itemQuote.highest) { itemQuote.highest = handleDeimalplace('last') } } // 处理价格 itemQuote.decimalplace = decimalplace itemQuote.ask = handleDeimalplace('ask') itemQuote.ask2 = handleDeimalplace('ask2') itemQuote.ask3 = handleDeimalplace('ask3') itemQuote.ask4 = handleDeimalplace('ask4') itemQuote.ask5 = handleDeimalplace('ask5') itemQuote.ask6 = handleDeimalplace('ask6') itemQuote.ask7 = handleDeimalplace('ask7') itemQuote.ask8 = handleDeimalplace('ask8') itemQuote.ask9 = handleDeimalplace('ask9') itemQuote.ask10 = handleDeimalplace('ask10') itemQuote.bid = handleDeimalplace('bid') itemQuote.bid2 = handleDeimalplace('bid2') itemQuote.bid3 = handleDeimalplace('bid3') itemQuote.bid4 = handleDeimalplace('bid4') itemQuote.bid5 = handleDeimalplace('bid5') itemQuote.bid6 = handleDeimalplace('bid6') itemQuote.bid7 = handleDeimalplace('bid7') itemQuote.bid8 = handleDeimalplace('bid8') itemQuote.bid9 = handleDeimalplace('bid9') itemQuote.averageprice = handleDeimalplace('averageprice') itemQuote.grepmarketprice = handleDeimalplace('grepmarketprice') itemQuote.iep = handleDeimalplace('iep') itemQuote.limitdown = handleDeimalplace('limitdown') itemQuote.limitup = handleDeimalplace('limitup') itemQuote.opened = handleDeimalplace('opened') itemQuote.lastturnover = handleDeimalplace('lastturnover') itemQuote.nontotalturnover = handleDeimalplace('nontotalturnover') itemQuote.lastturnover = handleDeimalplace('lastturnover') itemQuote.preclose = handleDeimalplace('preclose') itemQuote.settle = handleDeimalplace('settle') itemQuote.totalturnover = handleDeimalplace('totalturnover') itemQuote.askorderid = handleNoneValue('askorderid') itemQuote.askorderid2 = handleNoneValue('askorderid2') itemQuote.askorderid3 = handleNoneValue('askorderid3') itemQuote.askorderid4 = handleNoneValue('askorderid4') itemQuote.askorderid5 = handleNoneValue('askorderid5') itemQuote.askordervolume = handleNoneValue('askordervolume') itemQuote.askordervolume2 = handleNoneValue('askordervolume2') itemQuote.askordervolume3 = handleNoneValue('askordervolume3') itemQuote.askordervolume4 = handleNoneValue('askordervolume4') itemQuote.askordervolume5 = handleNoneValue('askordervolume5') itemQuote.askordervolume6 = handleNoneValue('askordervolume6') itemQuote.askordervolume7 = handleNoneValue('askordervolume7') itemQuote.askordervolume8 = handleNoneValue('askordervolume8') itemQuote.askordervolume9 = handleNoneValue('askordervolume9') itemQuote.askqueueinfo = handleNoneValue('askqueueinfo') itemQuote.askvolume = handleNoneValue('askvolume') itemQuote.askvolume2 = handleNoneValue('askvolume2') itemQuote.askvolume3 = handleNoneValue('askvolume3') itemQuote.askvolume4 = handleNoneValue('askvolume4') itemQuote.askvolume5 = handleNoneValue('askvolume5') itemQuote.askvolume6 = handleNoneValue('askvolume6') itemQuote.askvolume7 = handleNoneValue('askvolume7') itemQuote.askvolume8 = handleNoneValue('askvolume8') itemQuote.askvolume9 = handleNoneValue('askvolume9') itemQuote.bidorderid = handleNoneValue('bidorderid') itemQuote.bidorderid2 = handleNoneValue('bidorderid2') itemQuote.bidorderid3 = handleNoneValue('bidorderid3') itemQuote.bidorderid4 = handleNoneValue('bidorderid4') itemQuote.bidorderid5 = handleNoneValue('bidorderid5') itemQuote.bidordervolume = handleNoneValue('bidordervolume') itemQuote.bidordervolume2 = handleNoneValue('bidordervolume2') itemQuote.bidordervolume3 = handleNoneValue('bidordervolume3') itemQuote.bidordervolume4 = handleNoneValue('bidordervolume4') itemQuote.bidordervolume5 = handleNoneValue('bidordervolume5') itemQuote.bidordervolume6 = handleNoneValue('bidordervolume6') itemQuote.bidordervolume7 = handleNoneValue('bidordervolume7') itemQuote.bidordervolume8 = handleNoneValue('bidordervolume8') itemQuote.bidordervolume9 = handleNoneValue('bidordervolume9') itemQuote.bidqueueinfo = handleNoneValue('bidqueueinfo') itemQuote.bidvolume = handleNoneValue('bidvolume') itemQuote.bidvolume2 = handleNoneValue('bidvolume2') itemQuote.bidvolume3 = handleNoneValue('bidvolume3') itemQuote.bidvolume4 = handleNoneValue('bidvolume4') itemQuote.bidvolume5 = handleNoneValue('bidvolume5') itemQuote.bidvolume6 = handleNoneValue('bidvolume6') itemQuote.bidvolume7 = handleNoneValue('bidvolume7') itemQuote.bidvolume8 = handleNoneValue('bidvolume8') itemQuote.bidvolume9 = handleNoneValue('bidvolume9') itemQuote.calloptionpremiums = handleNoneValue('calloptionpremiums') itemQuote.calloptionpremiums2 = handleNoneValue('calloptionpremiums2') itemQuote.calloptionpremiums3 = handleNoneValue('calloptionpremiums3') itemQuote.calloptionpremiums4 = handleNoneValue('calloptionpremiums4') itemQuote.calloptionpremiums5 = handleNoneValue('calloptionpremiums5') itemQuote.holdincrement = handleNoneValue('holdincrement') itemQuote.holdvolume = handleNoneValue('holdvolume') itemQuote.inventory = handleNoneValue('inventory') itemQuote.lastlot = handleNoneValue('lastlot') itemQuote.nontotalholdervolume = handleNoneValue('nontotalholdervolume') itemQuote.nontotallot = handleNoneValue('nontotallot') itemQuote.nontotalvolume = handleNoneValue('nontotalvolume') itemQuote.totallot = handleNoneValue('totallot') itemQuote.totalvolume = handleNoneValue('totalvolume') } // 判断是一下行情时间是否比现在的要早 if (goodsQuoteTik.lasttime) { const quoteTime = moment(goodsQuoteTik.lasttime, 'YYYY-MM-DD HH:mm:ss') const localTime = moment(itemQuote.lasttime, 'YYYY-MM-DD HH:mm:ss') if (quoteTime >= localTime) { changeValue() itemQuote.lasttime = handleNoneValue('lasttime') } } else { // 委托单 没有 行情变更时间 changeValue() } // goodsQuoteTik.averageprice = goodsQuoteTik.averageprice ? (fn(goodsQuoteTik.averageprice)) : '--' // goodsQuoteTik.grepmarketprice = goodsQuoteTik.grepmarketprice ? (fn(goodsQuoteTik.grepmarketprice)) : '--' // goodsQuoteTik.highest = goodsQuoteTik.highest ? (fn(goodsQuoteTik.highest)) : '--' // goodsQuoteTik.iep = goodsQuoteTik.iep ? (fn(goodsQuoteTik.iep)) : '--' // goodsQuoteTik.limitdown = goodsQuoteTik.limitdown ? (fn(goodsQuoteTik.limitdown)) : '--' // goodsQuoteTik.limitup = goodsQuoteTik.limitup ? (fn(goodsQuoteTik.limitup)) : '--' // goodsQuoteTik.lowest = goodsQuoteTik.lowest ? (fn(goodsQuoteTik.lowest)) : '--' // goodsQuoteTik.opened = goodsQuoteTik.opened ? (fn(goodsQuoteTik.opened)) : '--' // goodsQuoteTik.preclose = goodsQuoteTik.preclose ? (fn(goodsQuoteTik.preclose)) : '--' // goodsQuoteTik.settle = goodsQuoteTik.settle ? (fn(goodsQuoteTik.settle)) : '--' // goodsQuoteTik.strikeprice = goodsQuoteTik.strikeprice ? (fn(goodsQuoteTik.strikeprice)) : '--' // goodsQuoteTik.lasttime = moment(goodsQuoteTik.date + goodsQuoteTik.time, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss') // // 实时行情由于行情源的问题可能不会下发现量,这时用盘面的总量来计算 // if (!goodsQuoteTik.lastvolume) { // if (itemQuote.totalvolume) { // goodsQuoteTik.lastvolume = goodsQuoteTik.totalvolume - itemQuote.totalvolume; // } else { // goodsQuoteTik.lastvolume = "--"; // } // } // !goodsQuoteTik.holdvolume && (goodsQuoteTik.holdvolume = '--') // !goodsQuoteTik.holdincrement && (goodsQuoteTik.holdincrement = '--') // !goodsQuoteTik.totalturnover && (goodsQuoteTik.totalturnover = '--') // console.log('goodsQuoteTik', goodsQuoteTik) // mergeObj(itemQuote, goodsQuoteTik); } else { console.warn(`行情推送的商品goods:${itemQuote.goodscode}在商品列表里不存在`) } // 注意:此处已 go 服务查询出来的盘面数据为基准,查询盘面 先修改成go 服务,第一次进入项目时候回查询一次所有盘面 // 之前是 通过 websocket 查询的,故 可能存在字段发生变化问题,如遇到,则进行优化处理 // 判断是一下行情时间是否比现在的要早 // const quoteTime = moment(goodsQuoteTik.lasttime, 'YYYY-MM-DD HH:mm:ss') // const localTime = moment(itemQuote.lasttime, 'YYYY-MM-DD HH:mm:ss') // if (quoteTime >= localTime) { // mergeObj(itemQuote, goodsQuoteTik); // } } else { // 此处 待优化(存在字段变化问题) // console.log('itemQuote', itemQuote) // quoteDayInfo.value.push(itemQuote); } } } /** * 分解缓存行情字段的方法 * goodsQuoteTik * keyItem * valueItem */ function setQuoteTikFieldByByte(goodsQuoteTik: any, keyItem: any, valueItem: any) { const strValue = String.fromCharCode(...valueItem); switch (keyItem) { case 0x56: goodsQuoteTik.exchangecode = strValue; break; case 0x21: goodsQuoteTik.goodscode = strValue; break; case 0x24: goodsQuoteTik.last = Number(strValue); break; case 0x5b: goodsQuoteTik.holdvolume = Number(strValue); break; case 0x25: goodsQuoteTik.lastvolume = Number(strValue); break; case 0x3c: goodsQuoteTik.preholdvolume = Number(strValue); break; case 0x32: goodsQuoteTik.presettle = Number(strValue); break; case 0x33: goodsQuoteTik.settle = Number(strValue); break; case 0x29: goodsQuoteTik.totalturnover = Number(strValue); break; case 0x28: goodsQuoteTik.totalvolume = Number(strValue); break; case 0x35: goodsQuoteTik.limithigh = Number(strValue); break; case 0x36: goodsQuoteTik.limitlow = Number(strValue); break; case 'L'.charCodeAt(0): goodsQuoteTik.ask = Number(strValue); break; case 'M'.charCodeAt(0): goodsQuoteTik.ask2 = Number(strValue); break; case 'N'.charCodeAt(0): goodsQuoteTik.ask3 = Number(strValue); break; case 'O'.charCodeAt(0): goodsQuoteTik.ask4 = Number(strValue); break; case 'P'.charCodeAt(0): goodsQuoteTik.ask5 = Number(strValue); break; case 'Q'.charCodeAt(0): goodsQuoteTik.askvolume = Number(strValue); break; case 'R'.charCodeAt(0): goodsQuoteTik.askvolume2 = Number(strValue); break; case 'S'.charCodeAt(0): goodsQuoteTik.askvolume3 = Number(strValue); break; case 'T'.charCodeAt(0): goodsQuoteTik.askvolume4 = Number(strValue); break; case 'U'.charCodeAt(0): goodsQuoteTik.askvolume5 = Number(strValue); break; case 'B'.charCodeAt(0): goodsQuoteTik.bid = Number(strValue); break; case 'C'.charCodeAt(0): goodsQuoteTik.bid2 = Number(strValue); break; case 'D'.charCodeAt(0): goodsQuoteTik.bid3 = Number(strValue); break; case 'E'.charCodeAt(0): goodsQuoteTik.bid4 = Number(strValue); break; case 'F'.charCodeAt(0): goodsQuoteTik.bid5 = Number(strValue); break; case 'G'.charCodeAt(0): goodsQuoteTik.bidvolume = Number(strValue); break; case 'H'.charCodeAt(0): goodsQuoteTik.bidvolume2 = Number(strValue); break; case 'I'.charCodeAt(0): goodsQuoteTik.bidvolume3 = Number(strValue); break; case 'J'.charCodeAt(0): goodsQuoteTik.bidvolume4 = Number(strValue); break; case 'K'.charCodeAt(0): goodsQuoteTik.bidvolume5 = Number(strValue); break; case ','.charCodeAt(0): goodsQuoteTik.highest = Number(strValue); break; case '-'.charCodeAt(0): goodsQuoteTik.lowest = Number(strValue); break; case '@'.charCodeAt(0): goodsQuoteTik.date = strValue; break; case 'A'.charCodeAt(0): goodsQuoteTik.time = strValue; break; case '+'.charCodeAt(0): goodsQuoteTik.preclose = Number(strValue); break; case '.'.charCodeAt(0): goodsQuoteTik.opened = Number(strValue); break; case 0x5c: goodsQuoteTik.exerciseprice = Number(strValue); break; case 0x7a: goodsQuoteTik.inventory = Number(strValue); break; case 0x7c: goodsQuoteTik.exchangedate = Number(strValue); break; case 0x70: goodsQuoteTik.strbidorder = strValue; break; case 0x71: goodsQuoteTik.strbidorder2 = strValue; break; case 0x72: goodsQuoteTik.strbidorder3 = strValue; break; case 0x73: goodsQuoteTik.strbidorder4 = strValue; break; case 0x74: goodsQuoteTik.strbidorder5 = strValue; break; case 0x75: goodsQuoteTik.straskorder = strValue; break; case 0x76: goodsQuoteTik.straskorder2 = strValue; break; case 0x77: goodsQuoteTik.straskorder3 = strValue; break; case 0x78: goodsQuoteTik.straskorder4 = strValue; break; case 0x79: goodsQuoteTik.straskorder5 = strValue; break; case 0x7d: goodsQuoteTik.putoptionpremiums = strValue; break; case 0x7e: goodsQuoteTik.putoptionpremiums2 = strValue; break; case 0x80: goodsQuoteTik.putoptionpremiums3 = strValue; break; case 0x81: goodsQuoteTik.putoptionpremiums4 = strValue; break; case 0x82: goodsQuoteTik.putoptionpremiums5 = strValue; break; case 0x83: goodsQuoteTik.calloptionpremiums = strValue; break; case 0x84: goodsQuoteTik.calloptionpremiums2 = strValue; break; case 0x85: goodsQuoteTik.calloptionpremiums3 = strValue; break; case 0x86: goodsQuoteTik.calloptionpremiums4 = strValue; break; case 0x87: goodsQuoteTik.calloptionpremiums5 = strValue; break; default: break; } }