import APP from '@/services'; import { getUserId } from '@/services/bus/account'; import { getErrorInfoByCode } from '@/services/bus/error'; import { funCode } from '@/services/funcode/index'; import ProtobufCtr from '@/services/socket/protobuf/index'; import { Callback } from '@/utils/websocket'; import { Package50 } from '@/utils/websocket/package'; import { v4 as uuidv4 } from 'uuid'; import * as type from './interface'; import { CommonSearchParam, ParseRsp, SoleSearchParam } from './interface'; import { IMessageHead } from './proto.d'; import { getProtoHeadParam, HeadEnum } from './protoHeader'; /** * 构建proto50 报文 * 具体某个proto查询参数 SoleSearchParam * 通用查询查询参数 CommonSearchParam * @param param SoleSearchParam | CommonSearchParam */ export function buildProtoReq50(param: SoleSearchParam | CommonSearchParam): Package50 { if (Reflect.has(param, 'funCodeName')) { return buildSoleProtoReq50(param as SoleSearchParam); } else { return buildCommonProtoReq50(param as CommonSearchParam); } } /** * 解析proto50 报文 */ export function parseProtoRsp50(rspPackage: any, funCodeName?: string): ParseRsp { if (funCodeName) { // 解析funCodeName 相对应的报文 return parseSoleProtoRsp(rspPackage, funCodeName); } else { // 解析通用查询报文 return parseCommonProtoRsp(rspPackage); } } /** * 生成 Package50 报文 * @param funCodeName * @param req */ function setPackage50(funCodeName: string, req: any): Package50 { const buffer = new Uint8Array(req.toArrayBuffer()); const reqPackage = new Package50([funCode[funCodeName], buffer], undefined); return reqPackage; } /** * 获取proto实例 * @param protobufName */ function getProtoInstence(protobufName: string) { return ProtobufCtr.generateProtoInstence(protobufName); } /** * 构建单独的proto * @param protobufName * @param reqParams */ function buildSoleProtoReq(protobufName: string, reqParams: type.ReqParams) { // 登录报文 const Req = getProtoInstence(protobufName); // 此处处理 一:为了 不需要单独写 构建报文函数,二:是 处理 报文中例如 id 和 ID 各种大写混乱的情况 const ReqKeys = Object.keys(Req); const ReqKeysMap = new Map(); ReqKeys.forEach((e) => { ReqKeysMap.set(e.toLowerCase(), e); }); // 此处需要待优化,proto嵌套的时候存在问题 const paramKeys = Object.keys(reqParams); for (let i = 0; i < paramKeys.length; i++) { const item = paramKeys[i]; const lowerKey = item.toLowerCase(); if (ReqKeysMap.has(lowerKey)) { Req[ReqKeysMap.get(lowerKey)] = reqParams[item]; } else { console.error(`$报文包中不存在 ${item} 属性}`); } } console.log('Req', Req) return Req; } /** * 构建消息头部 * @param funCodeName funCodeName * @param params IMessageHead */ function buildMsgHead(funCodeName: string, params: IMessageHead) { const code = funCode[funCodeName]; if (!code) console.error('没有找到对应的funCode值,请配置'); const messageHead = getProtoInstence('MessageHead'); const head = { FunCode: code, UUID: uuidv4(), UserID: getUserId(), }; Object.assign(messageHead, head, params); return messageHead; } /** * 构建单独的proto请求 Package50 报文 * @param params SoleSearchParam */ function buildSoleProtoReq50(params: SoleSearchParam): Package50 { const { protobufName, funCodeName, reqParams, Header } = params; // // // 消息头 // const messageHead = buildMsgHead(funCodeName, headParam); // proto头部参数,站在用户角度,参数数据可以通用获取,但站在管理端的角度,则需要不能通用获取,而是需用选中对应数据中传入进来 // 登录报文 const header = typeof Header == 'object' ? buildMsgHead(funCodeName, Header) : getProtoHeadParam(funCodeName, Header); const Req = buildSoleProtoReq(protobufName, reqParams); Req.Header = header return setPackage50(funCodeName, Req); } /** * 构建通用查询 Package50 报文 * @param param CommonSearchParam */ function buildCommonProtoReq50(param: CommonSearchParam): Package50 { const { senNames, statement, params } = param; let queryReq = null; let funCodeName = ''; // 加密信息 if (senNames && senNames.length > 0) { queryReq = getProtoInstence('QueryCommonSenReq'); queryReq.setSenNames(senNames); funCodeName = 'QueryCommonSenReq'; } else { queryReq = getProtoInstence('QueryCommonReq'); funCodeName = 'QueryCommonReq'; } // 消息头 const messageHead = getProtoHeadParam(funCodeName, 0); queryReq.setStatement(statement); // 构建查询参数 const paramValues = []; for (const key in params) { const value = params[key]; const Req = getProtoInstence('ParamValue'); const hasId = (id: string): boolean => key.toLowerCase() === id; // 如果有accountid则需加到head中 if (hasId('accountid') || hasId('buyaccountid') || hasId('sellaccountid')) { messageHead.AccountID = value; } Req.setKey(key); Req.setValue(value); paramValues.push(Req); } queryReq.setParamValues(paramValues); queryReq.setHeader(messageHead); return setPackage50(funCodeName, queryReq); } function isErrer(rspPackage: any, funCodeName: string): void { if (APP.Socket.trade.connState !== 2) { console.error(`交易服务未连接!`); } // 判断回复FunCode是否正确 if (rspPackage.funCode !== funCode[funCodeName]) { console.error(`服务返回【${funCodeName}: ${funCode[funCodeName]}】解析包错误!`); } // 判断是否有数据体 if (!rspPackage.content) { console.error(`服务未返回数据体!`); } } /** * 解析 funCodeName 对应的报文 * @param rspPackage * @param funCodeName */ function parseSoleProtoRsp(rspPackage: any, funCodeName: string): ParseRsp { isErrer(rspPackage, funCodeName); const reqPackage = ProtobufCtr.generateProtoContent(funCodeName, rspPackage.content); const { RetCode, Status, RetDesc } = reqPackage; // console.warn(`解析${funCodeName}对应的报文错误:RetCode: ${RetCode}: RetDesc${RetDesc}`) if (RetCode !== null) { if (RetCode === 0) { return { isSuccess: true, result: reqPackage }; } if (RetCode === -1) { // 管理端错误消息 return { isSuccess: false, result: RetDesc }; } else { const errMsg = getErrorInfoByCode(String(RetCode)); const result = errMsg ? errMsg : `未知错误:${RetCode}`; return { isSuccess: false, result }; } } else { // 银行 业务 以 Status 作为判断依据 console.log(`Status:${Status}`); if (Status === 0) { return { isSuccess: true, result: getErrorInfoByCode(Status) }; } else { return { isSuccess: false, result: getErrorInfoByCode(Status) }; } } } /** * 解析通用查询报文 * @param rspParam */ function parseCommonProtoRsp(rspParam: any): ParseRsp { //是否加密 const funCodeName = rspParam.isSen ? 'QueryCommonSenRsp' : 'QueryCommonRsp'; const { isSuccess, result } = parseSoleProtoRsp(rspParam, funCodeName); if (isSuccess) { // 组装数据 const { FieldInfos, RowValues } = result.Rsps[0]; // 此处只是简单处理 const arr = RowValues.map((item: any) => { const result: any = {}; FieldInfos.forEach((el: any, i: number) => { const { FieldName, FieldType } = el; const dateType = ['date', 'time', 'datetime', 'timestamp']; let value = item.RowValues[i]; if (dateType.includes(lower(FieldType))) { // 解决safari时间问题 value = value.replace(/-/g, '/').replace('.0', ''); } result[lower(FieldName)] = value; }); return result; }); return { isSuccess: true, result: arr }; } else { return { isSuccess, result }; } } /** * 解析通知 * @param rspPackage 回应包 * @param funCodeName funcode 名 */ export const noticeParseRsp = (rspPackage: any, funCodeName: string) => { isErrer(rspPackage, funCodeName); const reqPackage = ProtobufCtr.generateProtoContent(funCodeName, rspPackage.content); const { RetCode } = reqPackage; let errMsg = getErrorInfoByCode(String(RetCode)); if (errMsg === '') { errMsg = `未知错误:${RetCode}`; } return errMsg; }; function lower(value: string): string { return value.toLowerCase(); } // 枚举 proto hader export function protoMiddleware(param: T, reqName: string, rspName: string, headerEnum: HeadEnum): Promise // 自定义 protp header export function protoMiddleware(param: T, reqName: string, rspName: string, headerEnum: IMessageHead): Promise /** * @param param proto除去head 剩下参数 * @param headerEnum 自定义的proto参数枚举值,减小重复抒写 默认0 * @param reqFuncodeName proto 发送请求定义的头名字,特别需要注意,自已的funCodeName要与这个一致 * @param rspFuncodeName proto 响应请求定义的头名字 * @returns */ export function protoMiddleware(param: T, reqName: string, rspName: string, headerEnumOrParam: HeadEnum | IMessageHead = 0): Promise { return new Promise((resolve, reject) => { const params = { protobufName: reqName, funCodeName: reqName, reqParams: param, Header: headerEnumOrParam, }; const package50 = buildProtoReq50(params); const code = rspName ? funCode[rspName] : undefined APP.sendTradingServer(package50, code, { onSuccess: (res) => { const { isSuccess, result } = parseProtoRsp50(res, rspName); if (isSuccess) { resolve(result); } else { reject(result); } }, onFail: (err) => { console.log(`proto:${reqName}错误消息:${err}`) reject(err.message) }, } as Callback); }); }