index.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. import APP from '@/services';
  2. import { getLongTypeLoginID, isLogin } from '@/services/bus/login';
  3. import { getToken } from '@/services/bus/token';
  4. import { funCode } from '@/services/funcode/index';
  5. import { QueryQuoteDayRsp } from '@/services/go/quote/interface';
  6. import { mergeObjSameProperty } from '@/utils/objHandle';
  7. import { Package40 } from '@/utils/websocket/package';
  8. import Long from 'long';
  9. import { subscribeInfoType } from '../interface';
  10. import { byteArrayToUInt, subscribeInfosToByteArrary } from './byteUtils';
  11. /**
  12. * 构建行情订阅请求包
  13. */
  14. export function buildSubscribePeq(subscribeInfos: subscribeInfoType[]): Package40 {
  15. // 如果是登录状态,就走正常行情链路. 否则走游客
  16. const loginId = isLogin() ? getLongTypeLoginID() : Long.fromNumber(2);
  17. const token = isLogin() ? getToken() : '2_TOKEN_NEKOT_';
  18. const content = subscribeInfosToByteArrary(subscribeInfos, token, loginId);
  19. const reqPackage = new Package40([funCode.MainClassNumber_Quota_SubscriptReq, content], undefined);
  20. return reqPackage;
  21. }
  22. /**
  23. * 解析行情订阅请求包
  24. * @param rspPackage
  25. */
  26. export function parseSubscribeRsp(rspPackage: any): Promise<string | subscribeInfoType[]> {
  27. return new Promise((resolve, reject) => {
  28. const content = rspPackage.content;
  29. if (content.length === 0) {
  30. // 有可能空订阅,也有可能订阅返回数据为空,但其实已经订阅了商品
  31. // 暂做正常返回 空字符串 处理,如遇到问题继续优化即可
  32. console.warn('行情订阅请求包返回为空');
  33. resolve('');
  34. }
  35. const count = byteArrayToUInt(content.subarray(0, 4), false);
  36. if (count > 1000) {
  37. // 游客模式 刷新成长问题,为了不让页面卡死,暂时处理
  38. return null;
  39. }
  40. switch (count) {
  41. case -1:
  42. reject('订阅失败');
  43. case -2:
  44. reject('Token校验失败');
  45. case -3:
  46. reject('无对应商品信息');
  47. default:
  48. break;
  49. }
  50. // 判断返回的Count是否有问题
  51. if (!count || count > (content.length - 4) / 66) {
  52. reject('行情订阅返回数据不正确');
  53. }
  54. const result: subscribeInfoType[] = [];
  55. let position = 4;
  56. for (let i = 0; i < count; i++) {
  57. const dataArray = content.subarray(position, position + 66);
  58. const subState = dataArray[0];
  59. // FIXME: 目前行情接入服务不能正确返回交易所代码
  60. const exchangeCode = dataArray[2];
  61. const goodsCode = String.fromCharCode(...dataArray.subarray(2, dataArray.length)).trim();
  62. result.push({ subState, goodsCode, exchangeCode });
  63. position += 66;
  64. }
  65. resolve(result);
  66. });
  67. }
  68. /**
  69. * 解析行情推送报文
  70. * @param {Array} quotationData 行情信息
  71. * @param {Boolean} isPushQuote 实时行情推送为true;盘面为false
  72. *
  73. */
  74. export function parseReceivePush(quotationData: any) {
  75. // 目前发现可能会传入空数组
  76. if (!quotationData.length) {
  77. return;
  78. }
  79. // 分解行正则
  80. const regRow = /10\s.*?11/g;
  81. // 分解单行 key 正则
  82. const regKey = /01\s.*?02/g;
  83. // 分解单行 value 正则
  84. const regValue = /02\s.*?01|02\s.*?11/g;
  85. // 记录已经更新盘面的商品信息数组
  86. // 0x10 ... 0x01 key 0x02 value 0x01 key 0x02 value ... 0x11
  87. const hexString = Array.prototype.map.call(quotationData, (x) => ('00' + x.toString(16)).slice(-2)).join(' ');
  88. // 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';
  89. // 获取单行行情
  90. const rows = hexString.match(regRow);
  91. if (rows === null) {
  92. return;
  93. }
  94. for (const low of rows) {
  95. // 获取 key value 表列
  96. const keys: any = low.match(regKey);
  97. const values: any = low.match(regValue);
  98. const goodsQuoteTik: any = {};
  99. for (let i = 0; i < keys.length; i++) {
  100. const key = parseInt(keys[i].substring(3, 5), 16);
  101. const tmpValue = values[i];
  102. const value = new Uint8Array(
  103. tmpValue
  104. .substring(3, tmpValue.length - 3)
  105. .split(' ')
  106. .map((byte: any) => parseInt(byte, 16))
  107. );
  108. // 缓存值
  109. setQuoteTikFieldByByte(goodsQuoteTik, key, value);
  110. }
  111. const quoteDayInfo = APP.getRef('quoteDayInfo');
  112. const itemQuote = quoteDayInfo.value.find((el: QueryQuoteDayRsp) => el.goodscode === goodsQuoteTik.goodscode);
  113. if (itemQuote) {
  114. // 注意:此处已 go 服务查询出来的盘面数据为基准,查询盘面 先修改成go 服务,第一次进入项目时候回查询一次所有盘面
  115. // 之前是 通过 websocket 查询的,故 可能存在字段发生变化问题,如遇到,则进行优化处理
  116. mergeObjSameProperty(itemQuote, goodsQuoteTik);
  117. } else {
  118. // 此处 待优化(存在字段变化问题)
  119. quoteDayInfo.push(itemQuote);
  120. }
  121. }
  122. }
  123. /**
  124. * 分解缓存行情字段的方法
  125. * goodsQuoteTik
  126. * keyItem
  127. * valueItem
  128. */
  129. function setQuoteTikFieldByByte(goodsQuoteTik: any, keyItem: any, valueItem: any) {
  130. const strValue = String.fromCharCode(...valueItem);
  131. switch (keyItem) {
  132. case 0x56:
  133. goodsQuoteTik.exchangecode = strValue;
  134. break;
  135. case 0x21:
  136. goodsQuoteTik.goodscode = strValue;
  137. break;
  138. case 0x24:
  139. goodsQuoteTik.last = Number(strValue);
  140. break;
  141. case 0x5b:
  142. goodsQuoteTik.holdvolume = Number(strValue);
  143. break;
  144. case 0x25:
  145. goodsQuoteTik.lastvolume = Number(strValue);
  146. break;
  147. case 0x3c:
  148. goodsQuoteTik.preholdvolume = Number(strValue);
  149. break;
  150. case 0x32:
  151. goodsQuoteTik.presettle = Number(strValue);
  152. break;
  153. case 0x33:
  154. goodsQuoteTik.settle = Number(strValue);
  155. break;
  156. case 0x29:
  157. goodsQuoteTik.totalturnover = Number(strValue);
  158. break;
  159. case 0x28:
  160. goodsQuoteTik.totalvolume = Number(strValue);
  161. break;
  162. case 0x35:
  163. goodsQuoteTik.limithigh = Number(strValue);
  164. break;
  165. case 0x36:
  166. goodsQuoteTik.limitlow = Number(strValue);
  167. break;
  168. case 'L'.charCodeAt(0):
  169. goodsQuoteTik.ask = Number(strValue);
  170. break;
  171. case 'M'.charCodeAt(0):
  172. goodsQuoteTik.ask2 = Number(strValue);
  173. break;
  174. case 'N'.charCodeAt(0):
  175. goodsQuoteTik.ask3 = Number(strValue);
  176. break;
  177. case 'O'.charCodeAt(0):
  178. goodsQuoteTik.ask4 = Number(strValue);
  179. break;
  180. case 'P'.charCodeAt(0):
  181. goodsQuoteTik.ask5 = Number(strValue);
  182. break;
  183. case 'Q'.charCodeAt(0):
  184. goodsQuoteTik.askvolume = Number(strValue);
  185. break;
  186. case 'R'.charCodeAt(0):
  187. goodsQuoteTik.askvolume2 = Number(strValue);
  188. break;
  189. case 'S'.charCodeAt(0):
  190. goodsQuoteTik.askvolume3 = Number(strValue);
  191. break;
  192. case 'T'.charCodeAt(0):
  193. goodsQuoteTik.askvolume4 = Number(strValue);
  194. break;
  195. case 'U'.charCodeAt(0):
  196. goodsQuoteTik.askvolume5 = Number(strValue);
  197. break;
  198. case 'B'.charCodeAt(0):
  199. goodsQuoteTik.bid = Number(strValue);
  200. break;
  201. case 'C'.charCodeAt(0):
  202. goodsQuoteTik.bid2 = Number(strValue);
  203. break;
  204. case 'D'.charCodeAt(0):
  205. goodsQuoteTik.bid3 = Number(strValue);
  206. break;
  207. case 'E'.charCodeAt(0):
  208. goodsQuoteTik.bid4 = Number(strValue);
  209. break;
  210. case 'F'.charCodeAt(0):
  211. goodsQuoteTik.bid5 = Number(strValue);
  212. break;
  213. case 'G'.charCodeAt(0):
  214. goodsQuoteTik.bidvolume = Number(strValue);
  215. break;
  216. case 'H'.charCodeAt(0):
  217. goodsQuoteTik.bidvolume2 = Number(strValue);
  218. break;
  219. case 'I'.charCodeAt(0):
  220. goodsQuoteTik.bidvolume3 = Number(strValue);
  221. break;
  222. case 'J'.charCodeAt(0):
  223. goodsQuoteTik.bidvolume4 = Number(strValue);
  224. break;
  225. case 'K'.charCodeAt(0):
  226. goodsQuoteTik.bidvolume5 = Number(strValue);
  227. break;
  228. case ','.charCodeAt(0):
  229. goodsQuoteTik.highest = Number(strValue);
  230. break;
  231. case '-'.charCodeAt(0):
  232. goodsQuoteTik.lowest = Number(strValue);
  233. break;
  234. case '@'.charCodeAt(0):
  235. goodsQuoteTik.date = strValue;
  236. break;
  237. case 'A'.charCodeAt(0):
  238. goodsQuoteTik.time = strValue;
  239. break;
  240. case '+'.charCodeAt(0):
  241. goodsQuoteTik.preclose = Number(strValue);
  242. break;
  243. case '.'.charCodeAt(0):
  244. goodsQuoteTik.opened = Number(strValue);
  245. break;
  246. case 0x5c:
  247. goodsQuoteTik.exerciseprice = Number(strValue);
  248. break;
  249. case 0x7a:
  250. goodsQuoteTik.inventory = Number(strValue);
  251. break;
  252. case 0x7c:
  253. goodsQuoteTik.exchangedate = Number(strValue);
  254. break;
  255. case 0x70:
  256. goodsQuoteTik.strbidorder = strValue;
  257. break;
  258. case 0x71:
  259. goodsQuoteTik.strbidorder2 = strValue;
  260. break;
  261. case 0x72:
  262. goodsQuoteTik.strbidorder3 = strValue;
  263. break;
  264. case 0x73:
  265. goodsQuoteTik.strbidorder4 = strValue;
  266. break;
  267. case 0x74:
  268. goodsQuoteTik.strbidorder5 = strValue;
  269. break;
  270. case 0x75:
  271. goodsQuoteTik.straskorder = strValue;
  272. break;
  273. case 0x76:
  274. goodsQuoteTik.straskorder2 = strValue;
  275. break;
  276. case 0x77:
  277. goodsQuoteTik.straskorder3 = strValue;
  278. break;
  279. case 0x78:
  280. goodsQuoteTik.straskorder4 = strValue;
  281. break;
  282. case 0x79:
  283. goodsQuoteTik.straskorder5 = strValue;
  284. break;
  285. case 0x7d:
  286. goodsQuoteTik.putoptionpremiums = strValue;
  287. break;
  288. case 0x7e:
  289. goodsQuoteTik.putoptionpremiums2 = strValue;
  290. break;
  291. case 0x80:
  292. goodsQuoteTik.putoptionpremiums3 = strValue;
  293. break;
  294. case 0x81:
  295. goodsQuoteTik.putoptionpremiums4 = strValue;
  296. break;
  297. case 0x82:
  298. goodsQuoteTik.putoptionpremiums5 = strValue;
  299. break;
  300. case 0x83:
  301. goodsQuoteTik.calloptionpremiums = strValue;
  302. break;
  303. case 0x84:
  304. goodsQuoteTik.calloptionpremiums2 = strValue;
  305. break;
  306. case 0x85:
  307. goodsQuoteTik.calloptionpremiums3 = strValue;
  308. break;
  309. case 0x86:
  310. goodsQuoteTik.calloptionpremiums4 = strValue;
  311. break;
  312. case 0x87:
  313. goodsQuoteTik.calloptionpremiums5 = strValue;
  314. break;
  315. default:
  316. break;
  317. }
  318. }