buildReq.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import APP from '@/services';
  2. import { getUserId } from '@/services/bus/account';
  3. import { getErrorInfoByCode } from '@/services/bus/error';
  4. import { funCode } from '@/services/funcode/index';
  5. import ProtobufCtr from '@/services/socket/protobuf/index';
  6. import { Callback } from '@/utils/websocket';
  7. import { Package50 } from '@/utils/websocket/package';
  8. import { v4 as uuidv4 } from 'uuid';
  9. import * as type from './interface';
  10. import { CommonSearchParam, ParseRsp, SoleSearchParam } from './interface';
  11. import { IMessageHead } from './proto.d';
  12. import { getProtoHeadParam, HeadEnum } from './protoHeader';
  13. /**
  14. * 构建proto50 报文
  15. * 具体某个proto查询参数 SoleSearchParam
  16. * 通用查询查询参数 CommonSearchParam
  17. * @param param SoleSearchParam | CommonSearchParam
  18. */
  19. export function buildProtoReq50(param: SoleSearchParam | CommonSearchParam): Package50 {
  20. if (Reflect.has(param, 'funCodeName')) {
  21. return buildSoleProtoReq50(param as SoleSearchParam);
  22. } else {
  23. return buildCommonProtoReq50(param as CommonSearchParam);
  24. }
  25. }
  26. /**
  27. * 解析proto50 报文
  28. */
  29. export function parseProtoRsp50(rspPackage: any, funCodeName?: string): ParseRsp {
  30. if (funCodeName) {
  31. // 解析funCodeName 相对应的报文
  32. return parseSoleProtoRsp(rspPackage, funCodeName);
  33. } else {
  34. // 解析通用查询报文
  35. return parseCommonProtoRsp(rspPackage);
  36. }
  37. }
  38. /**
  39. * 生成 Package50 报文
  40. * @param funCodeName
  41. * @param req
  42. */
  43. function setPackage50(funCodeName: string, req: any): Package50 {
  44. const buffer = new Uint8Array(req.toArrayBuffer());
  45. const reqPackage = new Package50([funCode[funCodeName], buffer], undefined);
  46. return reqPackage;
  47. }
  48. /**
  49. * 获取proto实例
  50. * @param protobufName
  51. */
  52. function getProtoInstence(protobufName: string) {
  53. return ProtobufCtr.generateProtoInstence(protobufName);
  54. }
  55. /**
  56. * 构建单独的proto
  57. * @param protobufName
  58. * @param reqParams
  59. */
  60. function buildSoleProtoReq(protobufName: string, reqParams: type.ReqParams) {
  61. // 登录报文
  62. const Req = getProtoInstence(protobufName);
  63. // 此处处理 一:为了 不需要单独写 构建报文函数,二:是 处理 报文中例如 id 和 ID 各种大写混乱的情况
  64. const ReqKeys = Object.keys(Req);
  65. const ReqKeysMap = new Map();
  66. ReqKeys.forEach((e) => {
  67. ReqKeysMap.set(e.toLowerCase(), e);
  68. });
  69. // 此处需要待优化,proto嵌套的时候存在问题
  70. const paramKeys = Object.keys(reqParams);
  71. for (let i = 0; i < paramKeys.length; i++) {
  72. const item = paramKeys[i];
  73. const lowerKey = item.toLowerCase();
  74. if (ReqKeysMap.has(lowerKey)) {
  75. Req[ReqKeysMap.get(lowerKey)] = reqParams[item];
  76. } else {
  77. console.error(`$报文包中不存在 ${item} 属性}`);
  78. }
  79. }
  80. return Req;
  81. }
  82. /**
  83. * 构建消息头部
  84. * @param funCodeName funCodeName
  85. * @param params IMessageHead
  86. */
  87. function buildMsgHead(funCodeName: string, params: IMessageHead) {
  88. const code = funCode[funCodeName];
  89. if (!code) console.error('没有找到对应的funCode值,请配置');
  90. const messageHead = getProtoInstence('MessageHead');
  91. const head = {
  92. FunCode: code,
  93. UUID: uuidv4(),
  94. UserID: getUserId(),
  95. };
  96. Object.assign(messageHead, head, params);
  97. return messageHead;
  98. }
  99. /**
  100. * 构建单独的proto请求 Package50 报文
  101. * @param params SoleSearchParam
  102. */
  103. function buildSoleProtoReq50(params: SoleSearchParam): Package50 {
  104. const { protobufName, funCodeName, reqParams, headerEnum, headParam } = params;
  105. // // // 消息头
  106. // const messageHead = buildMsgHead(funCodeName, headParam);
  107. // proto头部参数,站在用户角度,参数数据可以通用获取,但站在管理端的角度,则需要不能通用获取,而是需用选中对应数据中传入进来
  108. // 登录报文
  109. const header = headParam ? buildMsgHead(funCodeName, headParam) : getProtoHeadParam(funCodeName, headerEnum);
  110. const Req = buildSoleProtoReq(protobufName, reqParams);
  111. Req.Header = header
  112. return setPackage50(funCodeName, Req);
  113. }
  114. /**
  115. * 构建通用查询 Package50 报文
  116. * @param param CommonSearchParam
  117. */
  118. function buildCommonProtoReq50(param: CommonSearchParam): Package50 {
  119. const { senNames, statement, params } = param;
  120. let queryReq = null;
  121. let funCodeName = '';
  122. // 加密信息
  123. if (senNames && senNames.length > 0) {
  124. queryReq = getProtoInstence('QueryCommonSenReq');
  125. queryReq.setSenNames(senNames);
  126. funCodeName = 'QueryCommonSenReq';
  127. } else {
  128. queryReq = getProtoInstence('QueryCommonReq');
  129. funCodeName = 'QueryCommonReq';
  130. }
  131. // 消息头
  132. const messageHead = getProtoHeadParam(funCodeName, 0);
  133. queryReq.setStatement(statement);
  134. // 构建查询参数
  135. const paramValues = [];
  136. for (const key in params) {
  137. const value = params[key];
  138. const Req = getProtoInstence('ParamValue');
  139. const hasId = (id: string): boolean => key.toLowerCase() === id;
  140. // 如果有accountid则需加到head中
  141. if (hasId('accountid') || hasId('buyaccountid') || hasId('sellaccountid')) {
  142. messageHead.AccountID = value;
  143. }
  144. Req.setKey(key);
  145. Req.setValue(value);
  146. paramValues.push(Req);
  147. }
  148. queryReq.setParamValues(paramValues);
  149. queryReq.setHeader(messageHead);
  150. return setPackage50(funCodeName, queryReq);
  151. }
  152. function isErrer(rspPackage: any, funCodeName: string): void {
  153. if (APP.Socket.trade.connState !== 2) {
  154. console.error(`交易服务未连接!`);
  155. }
  156. // 判断回复FunCode是否正确
  157. if (rspPackage.funCode !== funCode[funCodeName]) {
  158. console.error(`服务返回【${funCodeName}: ${funCode[funCodeName]}】解析包错误!`);
  159. }
  160. // 判断是否有数据体
  161. if (!rspPackage.content) {
  162. console.error(`服务未返回数据体!`);
  163. }
  164. }
  165. /**
  166. * 解析 funCodeName 对应的报文
  167. * @param rspPackage
  168. * @param funCodeName
  169. */
  170. function parseSoleProtoRsp(rspPackage: any, funCodeName: string): ParseRsp {
  171. isErrer(rspPackage, funCodeName);
  172. const reqPackage = ProtobufCtr.generateProtoContent(funCodeName, rspPackage.content);
  173. const { RetCode, Status, RetDesc } = reqPackage;
  174. console.warn(`解析${funCodeName}对应的报文错误:RetCode: ${RetCode}: RetDesc${RetDesc}`)
  175. if (RetCode !== null) {
  176. if (RetCode === 0) {
  177. return { isSuccess: true, result: reqPackage };
  178. } if (RetCode === -1) { // 管理端错误消息
  179. return { isSuccess: false, result: RetDesc };
  180. } else {
  181. const errMsg = getErrorInfoByCode(String(RetCode));
  182. const result = errMsg ? errMsg : `未知错误:${RetCode}`;
  183. return { isSuccess: false, result };
  184. }
  185. } else {
  186. // 银行 业务 以 Status 作为判断依据
  187. console.log(`Status:${Status}`);
  188. if (Status === 0) {
  189. return { isSuccess: true, result: getErrorInfoByCode(Status) };
  190. } else {
  191. return { isSuccess: false, result: getErrorInfoByCode(Status) };
  192. }
  193. }
  194. }
  195. /**
  196. * 解析通用查询报文
  197. * @param rspParam
  198. */
  199. function parseCommonProtoRsp(rspParam: any): ParseRsp {
  200. //是否加密
  201. const funCodeName = rspParam.isSen ? 'QueryCommonSenRsp' : 'QueryCommonRsp';
  202. const { isSuccess, result } = parseSoleProtoRsp(rspParam, funCodeName);
  203. if (isSuccess) {
  204. // 组装数据
  205. const { FieldInfos, RowValues } = result.Rsps[0];
  206. // 此处只是简单处理
  207. const arr = RowValues.map((item: any) => {
  208. const result: any = {};
  209. FieldInfos.forEach((el: any, i: number) => {
  210. const { FieldName, FieldType } = el;
  211. const dateType = ['date', 'time', 'datetime', 'timestamp'];
  212. let value = item.RowValues[i];
  213. if (dateType.includes(lower(FieldType))) {
  214. // 解决safari时间问题
  215. value = value.replace(/-/g, '/').replace('.0', '');
  216. }
  217. result[lower(FieldName)] = value;
  218. });
  219. return result;
  220. });
  221. return { isSuccess: true, result: arr };
  222. } else {
  223. return { isSuccess, result };
  224. }
  225. }
  226. /**
  227. * 解析通知
  228. * @param rspPackage 回应包
  229. * @param funCodeName funcode 名
  230. */
  231. export const noticeParseRsp = (rspPackage: any, funCodeName: string) => {
  232. isErrer(rspPackage, funCodeName);
  233. const reqPackage = ProtobufCtr.generateProtoContent(funCodeName, rspPackage.content);
  234. const { RetCode } = reqPackage;
  235. let errMsg = getErrorInfoByCode(String(RetCode));
  236. if (errMsg === '') {
  237. errMsg = `未知错误:${RetCode}`;
  238. }
  239. return errMsg;
  240. };
  241. function lower(value: string): string {
  242. return value.toLowerCase();
  243. }
  244. /**
  245. * 此方法站在 用户角度
  246. * @param param proto除去head 剩下参数
  247. * @param headerEnum 自定义的proto参数枚举值,减小重复抒写 默认0
  248. * @param reqFuncodeName proto 发送请求定义的头名字,特别需要注意,自已的funCodeName要与这个一致
  249. * @param rspFuncodeName proto 响应请求定义的头名字
  250. * @returns
  251. */
  252. export function protoMiddleware<T>(param: T, reqName: string, rspName: string, headerEnum: HeadEnum = 0): Promise<any> {
  253. return new Promise((resolve, reject) => {
  254. const params = {
  255. protobufName: reqName,
  256. funCodeName: reqName,
  257. reqParams: param,
  258. headerEnum,
  259. };
  260. const package50 = buildProtoReq50(params);
  261. const code = rspName ? funCode[rspName] : undefined
  262. APP.sendTradingServer(package50, code, {
  263. onSuccess: (res) => {
  264. const { isSuccess, result } = parseProtoRsp50(res, rspName);
  265. if (isSuccess) {
  266. resolve(result);
  267. } else {
  268. reject(result);
  269. }
  270. },
  271. onFail: (err) => {
  272. console.log(`proto:${reqName}错误消息:${err}`)
  273. reject(err.message)
  274. },
  275. } as Callback);
  276. });
  277. }
  278. /**
  279. * 此方法站在 管理端角度
  280. * @param param proto除去head 剩下参数
  281. * @param headParam IMessageHead
  282. * @param reqFuncodeName proto 发送请求定义的头名字,特别需要注意,自已的funCodeName要与这个一致
  283. * @param rspFuncodeName proto 响应请求定义的头名字
  284. * @returns
  285. */
  286. export function protoMiddleware_manager<T>(param: T, reqName: string, rspName: string, headParam: IMessageHead): Promise<any> {
  287. return new Promise((resolve, reject) => {
  288. const params = {
  289. protobufName: reqName,
  290. funCodeName: reqName,
  291. reqParams: param,
  292. headParam,
  293. };
  294. const package50 = buildProtoReq50(params);
  295. APP.sendTradingServer(package50, undefined, {
  296. onSuccess: (res) => {
  297. const { isSuccess, result } = parseProtoRsp50(res, rspName);
  298. if (isSuccess) {
  299. resolve(result);
  300. } else {
  301. reject(result);
  302. }
  303. },
  304. onFail: (err) => reject(err.message),
  305. } as Callback);
  306. });
  307. }