import { buildTamRequestPayload, tamRequest, type TamRequestPayload, type TamResponse, } from '@/api/tam' /** * 人行头寸余额“发送”交易请求报文。 * * 该交易不携带业务 body,仅用于向后端发起一次余额查询申请, * 后端返回的申请平台流水号会作为后续“查询”动作的匹配条件。 */ export type PbcPositionSendPayload = { appHead: TamRequestPayload['appHead'] body: PbcPositionEmptyBody head: TamRequestPayload['head'] } /** * 人行头寸余额列表查询请求报文。 * * 列表查询同样为空 body,后端按当前登录用户、机构、工作日等报文头信息 * 返回可继续查询明细的记录集合。 */ export type PbcPositionQueryPayload = { appHead: TamRequestPayload['appHead'] body: PbcPositionEmptyBody head: TamRequestPayload['head'] } /** * 人行头寸余额明细查询请求报文。 * * 明细交易需要列表记录中的应答平台流水号和清算账户行号, * 查询结果再与发送交易返回的申请平台流水号进行匹配。 */ export type PbcPositionDetailPayload = { appHead: TamRequestPayload['appHead'] body: PbcPositionDetailRequestBody head: TamRequestPayload['head'] } /** “发送”交易响应,body 中主要使用后端返回的申请平台流水号。 */ export type PbcPositionSendResult = { body: PbcPositionSendBody head: TamResponse['head'] } /** “列表查询”交易响应,body 可能是数组,也可能被 resultList 包裹。 */ export type PbcPositionQueryResult = { body: PbcPositionQueryBody head: TamResponse['head'] } /** “明细查询”交易响应,body 为页面最终展示的扩展余额信息。 */ export type PbcPositionDetailResult = { body: PbcPositionDetailBody head: TamResponse['head'] } /** 列表交易返回的基础记录,也是明细查询的入参来源。 */ export type PbcPositionRecord = { /** 应答平台流水号,作为明细查询 body.refNo。 */ refNo?: string /** 清算账户行号,作为明细查询 body.settleBankNo。 */ settleBankNo?: string /** 清算账户余额,接口按元返回。 */ settleBal?: string /** 可用头寸,接口按元返回。 */ avaAmt?: string /** 预期头寸,接口按元返回。 */ expectAmt?: string /** 余额数据更新时间,页面原样展示。 */ updateTime?: string } /** 明细交易返回的完整余额记录,补充排队金额和辖内非清算账户信息。 */ export type PbcPositionDetailBody = PbcPositionRecord & { /** 申请平台流水号,用于与发送交易返回的流水号匹配。 */ refNo?: string /** 明细返回的应答平台流水号,页面展示使用。 */ resRefNo?: string /** 排队借记总金额,接口按元返回。 */ lineDrAmt?: string /** 排队贷记总金额,接口按元返回。 */ lineCrAmt?: string /** 所辖非清算账户数。 */ subAcctNumber?: string /** 所辖非清算总余额,接口按元返回。 */ subAcctAmt?: string } /** * 页面使用的归一化余额模型。 * * 列表记录和明细记录会合并到同一个对象中,额外保留发送交易返回的 * requestRefNo,便于页面判断是否允许继续查询并展示当前申请编号。 */ export type PbcPositionBalance = PbcPositionRecord & PbcPositionDetailBody & { /** 发送交易返回的申请平台流水号。 */ requestRefNo?: string } /** 人行头寸余额相关交易均没有业务入参,body 按接口约定传空对象。 */ type PbcPositionEmptyBody = Record /** 发送交易响应体,仅返回本次申请的平台流水号。 */ type PbcPositionSendBody = { /** 申请平台流水号,后续查询用它匹配最终明细。 */ refNo?: string } /** 列表查询响应体,兼容后端返回数组或 { resultList } 两种结构。 */ type PbcPositionQueryBody = | PbcPositionRecord[] | { resultList?: PbcPositionRecord[] } /** 明细查询请求体,两个字段均来自列表记录。 */ type PbcPositionDetailRequestBody = { /** 列表记录中的应答平台流水号。 */ refNo: string /** 列表记录中的清算账户行号。 */ settleBankNo: string } /** 140104009:发起人行头寸余额查询申请。 */ const SEND_TRAN_CODE = '/TAM/0001/140104009' /** 140104010:查询可用于继续取明细的人行头寸余额记录列表。 */ const QUERY_TRAN_CODE = '/TAM/0001/140104010' /** 140104011:按列表记录查询人行头寸余额明细。 */ const DETAIL_TRAN_CODE = '/TAM/0001/140104011' /** 构造“发送”交易报文,填充 TAM 公共报文头并传空业务 body。 */ export function buildPbcPositionSendPayload(): PbcPositionSendPayload { return buildTamRequestPayload({ tranCode: SEND_TRAN_CODE, body: {}, }) } /** 调用“发送”交易,成功后返回申请平台流水号。 */ export async function sendPbcPositionRequest( payload: PbcPositionSendPayload, ): Promise { return tamRequest(payload) } /** 构造“列表查询”交易报文,按登录上下文查询当前可见的人行头寸记录。 */ export function buildPbcPositionQueryPayload(): PbcPositionQueryPayload { return buildTamRequestPayload({ tranCode: QUERY_TRAN_CODE, body: {}, }) } /** 调用“列表查询”交易,返回可进一步查询明细的记录集合。 */ export async function queryPbcPositionList( payload: PbcPositionQueryPayload, ): Promise { return tamRequest(payload) } /** 根据列表记录构造“明细查询”交易报文。 */ export function buildPbcPositionDetailPayload( record: Pick, ): PbcPositionDetailPayload { return buildTamRequestPayload({ tranCode: DETAIL_TRAN_CODE, body: { refNo: record.refNo || '', settleBankNo: record.settleBankNo || '', }, }) } /** 调用“明细查询”交易,返回单条人行头寸余额的完整字段。 */ export async function queryPbcPositionDetail( payload: PbcPositionDetailPayload, ): Promise { return tamRequest(payload) } /** * 按发送交易返回的申请平台流水号查询最终余额明细。 * * 后端流程拆成“列表查询”和“逐条明细查询”两步:先取到候选记录列表, * 再使用每条记录的应答流水号和清算账户行号查询明细;只有明细中的 * refNo 与发送返回的 requestRefNo 一致时,才认为命中了本次申请结果。 */ export async function queryPbcPositionBalance(requestRefNo: string): Promise { // 第一步:获取当前登录上下文下可查询的候选人行头寸记录。 const queryPayload = buildPbcPositionQueryPayload() console.log('[人行头寸余额查询1入参]', queryPayload) const queryResponse = await queryPbcPositionList(queryPayload) const records = getPbcPositionRecords(queryResponse.body) if (records.length === 0) { throw new Error('人行头寸余额查询结果为空') } for (const record of records) { // 明细交易必须同时传入应答流水号和清算账户行号,缺任一字段时跳过。 if (!record.refNo || !record.settleBankNo) continue // 第二步:逐条查询明细,因为列表交易不直接返回全部展示字段。 const detailPayload = buildPbcPositionDetailPayload(record) console.log('[人行头寸余额查询2入参]', detailPayload) const detailResponse = await queryPbcPositionDetail(detailPayload) // 明细 refNo 表示申请平台流水号,用它与发送交易返回值做最终匹配。 if (detailResponse.body.refNo !== requestRefNo) continue // 保留列表字段,使用明细字段覆盖同名字段,并补齐页面需要的流水号别名。 return { ...record, ...detailResponse.body, requestRefNo, resRefNo: detailResponse.body.resRefNo || record.refNo, } } throw new Error(`未查询到编号 ${requestRefNo} 对应的余额结果`) } /** 统一提取列表记录,屏蔽后端数组和 resultList 包裹结构的差异。 */ function getPbcPositionRecords(body: PbcPositionQueryBody) { if (Array.isArray(body)) return body return body.resultList || [] }