Skill: infochen-sub/psbc-treasury-api (version 1.0.1)

--- name: psbc-treasury-api description: （司库系统）API 全生命周期管理智能 Skill tools: [json-parser, http-client, schema-validator, code-generator] trigger: [财资系统接口，邮储财资 API，601113 接口，邮储银行财资系统，财资系统 API 列表，PSBC treasury，司库系统 API] --- version: 1.0.0 author: tanghaowen # （司库系统）API 全生命周期管理智能 Skill ## 概述 本技能提供中国邮政储蓄银行财资管理系统统一前置平台的完整接口能力，包括： - 📋 **接口清单查询** - 列出所有可用 API - 📖 **接口规范详情** - HTTP 方法、请求路径、报文格式、响应结构 - 🔢 **响应码查询** - 标准码与业务码的中英文说明 - 🧪 **报文验证** - JSON Schema 校验、字段合法性检查 - 🔧 **示例生成** - curl/Postman/Python 可运行示例 - 🎭 **Mock 模拟** - 预设成功/失败响应、延迟、异常场景 ## 使用场景 当用户询问以下内容时使用本技能： 1. "财资系统有哪些接口？" 2. "601113 接口怎么调用？" 3. "帮我生成一个支付接口的 curl 示例" 4. "验证这个请求报文是否正确" 5. "模拟一个余额查询的响应" 6. "ERR_INSUFFICIENT_BALANCE 是什么意思？" ## 核心能力 ### 1. 接口清单 | 交易码 | 接口名称 | 安全级别 | |--------|----------|----------| | 601113 | 查询租户下直连账户列表 | 10 | | 601114 | 批量工资代发 | 10 | | 601115 | 批量工资代发结果查询 | 10 | | 601116 | 生成单笔支付申请单或支付单 | 10 | | 601117 | 单笔支付申请单或者支付单结果查询 | 10 | | 601118 | 账户余额实时查询 | 10 | | 601119 | 查询明细 | 10 | | 601120 | 账户历史余额查询 | 10 | | 601203 | 明细关联回单信息查询 | 15 | | 601303 | 查询资金归集明细 | 10 | | 601304 | 手工下拨 | 10 | | 601311 | 融资中心付款单获取 | 10 | | 601324 | 新增申请单 | 10 | | 601325 | 申请单结果查询 | 10 | | 601383 | 获取免密登录令牌 | 10 | | 601417 | 资金计划科目同步 | 10 | | 601418 | 资金计划填报 | 10 | | 601419 | 资金计划追加 | 10 | | 601420 | 资金计划占用情况查询 | 10 | | 601732 | 手工下拨结果查询 | 10 | ### 2. 接口规范 **基础信息：** - 通讯方式：HTTPS + POST - Content-Type：`application/json;charset=UTF-8` - 字符编码：UTF-8 ### 3. 报文结构 **公共请求参数 (txComm)：** ```json { "txComm": { "sysTrackNo": "32 位系统跟踪号", "reqSysCode": "12 位接入系统代码", "txCode": "6 位交易码", "txTime": "yyyyMMddHHmmssSSS", "securityLevel": "10 或 15", "sign": "签名值", "bankCertSN": "银行证书序列号 (安全级别 10 必输)", "userCertSN": "接入系统证书序列号", "encData": "报文密文 (安全级别 10 必输)", "encKey": "加密秘钥密文 (安全级别 10 必输)" } } ``` ### 4. 响应码 **标准响应码：** | 代码 | 说明 | |------|------| | 200 | 请求成功 | | 400 | 请求参数错误 | | 401 | 认证失败 | | 403 | 权限不足 | | 429 | 请求限流 | | 500 | 服务器内部错误 | **业务响应码：** | 代码 | 说明 | 处理建议 | |------|------|----------| | 000000 | 操作成功 | - | | 010002 | 查询无记录 | 检查查询条件 | | 020300 | 对方账号不合法 | 核实收款账号 | | 020301 | 未查询到开户行信息 | 检查联行号 | | 020302 | 查询无记录 | 确认数据存在性 | | 999990 | 非法参数异常 | 检查报文格式 | ## 使用方法 ### 查询接口列表 ``` 列出所有财资系统 API 接口 ``` ### 获取接口详情 ``` 601113 接口的详细规范 查询租户下直连账户列表的报文格式 ``` ### 生成示例代码 ``` 生成 601118 接口的 curl 示例 帮我写一个 Python 调用 601116 的代码 ``` ### 报文验证 ``` 验证这个请求报文是否正确：{...} 检查 601114 接口的 fileContent 字段格式 ``` ### Mock 模拟 ``` 模拟一个 601118 余额查询的成功响应 模拟 601116 支付接口余额不足的错误响应 ``` ## 安全级别说明 **安全级别 10：** - 使用国密证书对报文进行加密/解密、签名/验签 - SM4 随机秘钥对称加密业务报文 - 银行证书加密 SM4 秘钥 **安全级别 15：** - 仅对报文进行签名/验签 - 不涉及报文加密/解密 - 业务报文平铺传输 ## 注意事项 1. **系统跟踪号规则：** 时间戳 (14 位) + 接入系统代码 (12 位) + 6 位唯一序列号 2. **签名顺序：** sysTrackNo → reqSysCode → txCode → txTime → (encData) → (encKey) 3. **日期格式：** 请求时间使用 `yyyyMMddHHmmssSSS`，业务日期使用 `yyyyMMdd` 或 `yyyy-MM-dd` 4. **金额精度：** 保留 2 位小数，使用 D(20,2) 格式 5. **分页参数：** pageSize 支持 1-100 ## 相关文件 - `apis.json` - 完整接口定义与 JSON Schema - `response-codes.json` - 响应码字典 - `examples/` - 各接口示例报文 - `validator.py` - 报文验证工具 - `mock.py` - Mock 响应生成器 ## 版本信息 - 文档版本：v1.2.5 - 更新日期：2026-03-04 - 技能版本：1.0.0

path: README.md
content: # 邮储银行财资管理系统接口技能

> 中国邮政储蓄银行财资管理系统统一前置平台接口完整实现

## 📦 文件结构

```
psbc-treasury-api/
├── SKILL.md                 # 技能主文档（接口清单、规范说明）
├── apis.json                # 20 个接口的完整定义和 JSON Schema
├── response-codes.json      # 响应码字典（标准码 + 业务码 + 附录代码表）
├── validator.py             # 报文验证工具
├── mock.py                  # Mock 响应生成器
├── examples/                # 示例代码
│   ├── 601118-curl-example.sh    # curl 示例
│   └── psbc_client.py            # Python 客户端示例
└── README.md                # 本文件
```

## 🚀 快速开始

### 1. 查看接口列表

```bash
cat SKILL.md | grep -A 30 "接口清单"
```

### 2. 验证报文

```bash
# 交互式验证
python3 validator.py --interactive

# 或编程方式
from validator import PSBCValidator

validator = PSBCValidator()
errors = validator.validate("601118", {
    "txCode": "100016",
    "tenantID": "eam_tenant_a_0001",
    "sendTime": "20250318163811",
    "srcSysId": "140001",
    "bankAccno": "951011013000006323"
})
print(errors)  # [] 表示验证通过
```

### 3. 生成 Mock 响应

```bash
# 交互式 Mock
python3 mock.py --interactive

# 或编程方式
from mock import PSBCMockServer

mock = PSBCMockServer()
response = mock.mock_request("601118", request_data)
```

### 4. 调用接口（Python）

```python
from examples.psbc_client import PSBCTreasuryClient

client = PSBCTreasuryClient(env="test")

# 余额查询
business_data = {
    "txCode": "100016",
    "tenantID": "eam_tenant_a_0001",
    "sendTime": "20250318163811",
    "srcSysId": "140001",
    "bankAccno": "951011013000006323"
}

request_data = client.build_request("601118", business_data)
# 发送请求前需实现国密算法
```

## 📋 接口列表

| 交易码 | 接口名称 | 安全级别 |
|--------|----------|----------|
| 601113 | 查询租户下直连账户列表 | 10 |
| 601114 | 批量工资代发 | 10 |
| 601115 | 批量工资代发结果查询 | 10 |
| 601116 | 生成单笔支付申请单或支付单 | 10 |
| 601117 | 单笔支付申请单或者支付单结果查询 | 10 |
| 601118 | 账户余额实时查询 | 10 |
| 601119 | 查询明细 | 10 |
| 601120 | 账户历史余额查询 | 10 |
| 601203 | 明细关联回单信息查询 | 15 |
| 601303 | 查询资金归集明细 | 10 |
| 601304 | 手工下拨 | 10 |
| 601311 | 融资中心付款单获取 | 10 |
| 601324 | 新增申请单 | 10 |
| 601325 | 申请单结果查询 | 10 |
| 601383 | 获取免密登录令牌 | 10 |
| 601417 | 资金计划科目同步 | 10 |
| 601418 | 资金计划填报 | 10 |
| 601419 | 资金计划追加 | 10 |
| 601420 | 资金计划占用情况查询 | 10 |
| 601732 | 手工下拨结果查询 | 10 |

## 🔐 安全说明

### 安全级别 10
- SM4 对称加密业务报文
- SM2 非对称加密 SM4 密钥
- SM2 签名/验签

### 安全级别 15
- 仅 SM2 签名/验签
- 业务报文平铺传输

### 国密算法集成
需要集成国密库（如 `gmssl` 或 `pysm2`）才能实现实际加解密：

```bash
pip install gmssl
```

## 🌐 环境地址

| 环境 | 地址 |
|------|------|

## 📝 常见错误码

| 代码 | 说明 | 处理建议 |
|------|------|----------|
| 000000 | 操作成功 | - |
| 020300 | 对方账号不合法 | 核实收款账号 |
| 020302 | 查询无记录 | 确认数据存在性 |
| 999990 | 非法参数异常 | 检查报文格式 |

## 📚 相关文档

- 《财资管理系统 - 统一前置平台接口规范 - 行外版-v1.2.5》
- 《统一前置平台安全认证开发指引-v1.0.6》

## ⚠️ 注意事项

1. 系统跟踪号必须全局唯一
2. 签名顺序不能改变
3. 金额字段保留 2 位小数
4. 日期格式严格按规范
5. 分页 pageSize 支持 1-100

## 📄 版本信息

- 接口规范版本：v1.2.5
- 更新日期：2026-03-04
- 技能版本：1.0.0


path: apis.json
content: {
  "version": "1.2.5",
  "updateDate": "2026-03-04",
  "baseUrl": {
  },
  "httpMethod": "POST",
  "contentType": "application/json;charset=UTF-8",
  "encoding": "UTF-8",
  "apis": [
    {
      "txCode": "601113",
      "name": "查询租户下直连账户列表",
      "businessCode": "100011",
      "securityLevel": "10",
      "description": "查询租户下所有直连账户信息，支持分页",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100011"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true, "description": "银行提供的租户标识"},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true, "format": "yyyyMMddHHmmss"},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true, "description": "银行提供，如 140001"},
          {"fieldId": "pageIndex", "name": "页码", "type": "N..19", "required": true, "description": "long 类型"},
          {"fieldId": "pageSize", "name": "每页条数", "type": "N..19", "required": true, "description": "支持 1-100"}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": true, "items": [
            {"fieldId": "id", "name": "ID", "type": "AN..32", "required": true},
            {"fieldId": "deptName", "name": "部门名称", "type": "ANS..100", "required": true},
            {"fieldId": "deptNo", "name": "部门编码", "type": "ANS..32", "required": true},
            {"fieldId": "bankClsName", "name": "金融机构分类名称", "type": "ANS..100", "required": true},
            {"fieldId": "finInstName", "name": "金融机构名称", "type": "ANS..200", "required": true},
            {"fieldId": "finInstNo", "name": "金融机构编码", "type": "AN12", "required": true},
            {"fieldId": "accName", "name": "户名", "type": "ANS..150", "required": true},
            {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
            {"fieldId": "createTime", "name": "创建时间", "type": "ANS..20", "required": true},
            {"fieldId": "lastModTime", "name": "最后修改时间", "type": "ANS..20", "required": true},
            {"fieldId": "accStatus", "name": "账户状态", "type": "ANS..2", "required": true, "enum": {"1": "未启用", "2": "正常", "3": "已销户", "4": "止付", "5": "待销户"}},
            {"fieldId": "currCode", "name": "币种", "type": "AN3", "required": true},
            {"fieldId": "currCnName", "name": "币种名称", "type": "ANS..100", "required": true},
            {"fieldId": "openaccDate", "name": "开户日期", "type": "N8", "required": true, "format": "yyyyMMdd"},
            {"fieldId": "accBal", "name": "账户余额", "type": "D(20,2)", "required": true},
            {"fieldId": "avalBal", "name": "账户可用余额", "type": "D(20,2)", "required": false},
            {"fieldId": "detailUpdateTime", "name": "明细更新时间", "type": "ANS..20", "required": true},
            {"fieldId": "openBedlFlag", "name": "联网方式", "type": "N1", "required": true, "enum": {"0": "非直联", "1": "直联", "2": "SWIFT"}},
            {"fieldId": "accAttrNo", "name": "账户性质编号", "type": "ANS..32", "required": true},
            {"fieldId": "accAttrName", "name": "账户性质名称", "type": "ANS..100", "required": true},
            {"fieldId": "balUpdateTime", "name": "余额更新时间", "type": "ANS..20", "required": true}
          ]},
          {"fieldId": "pagingResult", "name": "分页结果", "type": "JsonObject", "required": true, "properties": [
            {"fieldId": "pageIndex", "name": "页码", "type": "N..19", "required": true},
            {"fieldId": "pageSize", "name": "每页条数", "type": "N..19", "required": true},
            {"fieldId": "total", "name": "记录总条数", "type": "AN..10", "required": true}
          ]}
        ]
      }
    },
    {
      "txCode": "601114",
      "name": "批量工资代发",
      "businessCode": "100012",
      "securityLevel": "10",
      "description": "批量工资代发业务，支持文件上传",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易码", "type": "ANS..6", "required": true, "description": "固定值：100012"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true, "format": "yyyyMMddHHmmss"},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "fileName", "name": "文件名", "type": "ANS..200", "required": true, "description": "租户编号 (14 位)+ 交易种类 (3 位)+ 交易日期 (8 位)+ 序列号 (5 位)+ 方向标识"},
          {"fieldId": "importDate", "name": "导入日期", "type": "AN8", "required": true, "format": "yyyyMMdd"},
          {"fieldId": "fileContent", "name": "文件内容", "type": "string", "required": true, "description": "Base64 编码的压缩文件内容"},
          {"fieldId": "payerAccno", "name": "付款人账号", "type": "ANS..34", "required": true},
          {"fieldId": "creatorName", "name": "创建人姓名", "type": "ANS..100", "required": true},
          {"fieldId": "batchNo", "name": "批次号", "type": "AN..32", "required": false, "description": "发起系统支付单编号，要求全局唯一"}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true}
        ]
      }
    },
    {
      "txCode": "601115",
      "name": "批量工资代发结果查询",
      "businessCode": "100013",
      "securityLevel": "10",
      "description": "查询批量工资代发处理结果",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100013"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "fileName", "name": "文件名", "type": "ANS..200", "required": true},
          {"fieldId": "importDate", "name": "导入日期", "type": "AN8", "required": true},
          {"fieldId": "payerAccno", "name": "付款人账号", "type": "ANS..34", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "importFileStsCd", "name": "导入文件状态代码", "type": "AN1", "required": true, "enum": {"1": "校验中", "2": "校验通过", "3": "不通过", "4": "已提交", "5": "已作废", "6": "待送审", "7": "审批中", "8": "驳回", "9": "待付款", "a": "付款中", "b": "付款成功", "c": "部分成功", "d": "付款失败", "e": "已作废", "f": "已退回"}},
            {"fieldId": "appFormStaCd", "name": "申请单状态代码", "type": "AN1", "required": false, "enum": {"4": "待付款", "5": "付款中", "6": "付款成功", "7": "部分成功", "8": "付款失败"}},
            {"fieldId": "failMsg", "name": "文件批次整体失败原因说明", "type": "AN100", "required": false},
            {"fieldId": "fileContent", "name": "文件内容", "type": "AN100", "required": false},
            {"fieldId": "batchNo", "name": "批次号", "type": "AN..32", "required": false}
          ]}
        ]
      }
    },
    {
      "txCode": "601116",
      "name": "生成单笔支付申请单或支付单",
      "businessCode": "100014",
      "securityLevel": "10",
      "description": "生成单笔支付申请单或支付单，仅限原付款管理菜单租户使用",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100014"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "txType", "name": "交易类型", "type": "N1", "required": true, "enum": {"1": "对公支付", "2": "对私支付"}},
          {"fieldId": "busiTypeCode", "name": "业务类型", "type": "N3", "required": true, "description": "参考附录 A.7"},
          {"fieldId": "batchNo", "name": "批次号", "type": "AN..32", "required": true, "description": "发起系统支付单编号，要求全局唯一"},
          {"fieldId": "budgetNo", "name": "预算编号", "type": "ANS..32", "required": false},
          {"fieldId": "payerAccno", "name": "付款人账号", "type": "ANS..34", "required": true},
          {"fieldId": "payAmt", "name": "支付金额", "type": "N..20", "required": true},
          {"fieldId": "payType", "name": "支付模式", "type": "N1", "required": false, "enum": {"1": "即时支付", "2": "预约支付"}, "default": "2"},
          {"fieldId": "paymDate", "name": "应付日期", "type": "AN8", "required": false, "format": "yyyyMMdd", "description": "支付模式为 1 时可不输"},
          {"fieldId": "fundsUsageInfo", "name": "付款用途", "type": "ANS..500", "required": true},
          {"fieldId": "payMode", "name": "支付方式", "type": "N1", "required": true, "enum": {"1": "账户", "2": "票据"}},
          {"fieldId": "networkflagCode", "name": "联网标识代码", "type": "N2", "required": true, "enum": {"0": "非直连", "1": "直连"}},
          {"fieldId": "payeeAccno", "name": "对方账号", "type": "AN..32", "required": true},
          {"fieldId": "payeeAccname", "name": "收款人户名", "type": "AN..70", "required": true},
          {"fieldId": "recpayBankCnapsNo", "name": "收款银行联行号", "type": "ANS..12", "required": false, "description": "联网标识为 1 时必填"},
          {"fieldId": "txPtscrt", "name": "交易附言", "type": "ANS..300", "required": false},
          {"fieldId": "creatorName", "name": "创建人姓名", "type": "ANS..100", "required": true},
          {"fieldId": "remitBgnTime", "name": "预约支付时间开始", "type": "ANS..5", "required": false, "format": "HH:mm"},
          {"fieldId": "remitEndTime", "name": "预约支付时间结束", "type": "ANS..5", "required": false, "format": "HH:mm"},
          {"fieldId": "custFdsrcTpCd", "name": "支付单类型", "type": "ANS..2", "required": false, "enum": {"01": "普通支付单", "02": "关联支付单"}, "default": "01"}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true}
        ]
      }
    },
    {
      "txCode": "601117",
      "name": "单笔支付申请单或者支付单结果查询",
      "businessCode": "100015",
      "securityLevel": "10",
      "description": "查询单笔支付申请单或支付单处理结果",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100015"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "batchNo", "name": "批次号", "type": "AN..32", "required": true, "description": "发起系统支付单编号"},
          {"fieldId": "txType", "name": "交易类型", "type": "N1", "required": true, "enum": {"1": "对公支付", "2": "对私支付"}}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true},
          {"fieldId": "data", "name": "响应数据", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "payStatus", "name": "支付状态代码", "type": "AN1", "required": true, "enum": {"1": "待送审", "2": "审批中", "3": "驳回", "4": "待付款", "5": "付款中", "6": "付款成功", "7": "部分成功", "8": "付款失败", "9": "已作废", "a": "已退回"}},
            {"fieldId": "bankSriNo", "name": "银行流水号", "type": "AN..64", "required": false},
            {"fieldId": "finishDate", "name": "支付完成时间", "type": "AN14", "required": false, "format": "yyyyMMddHHmmss"}
          ]}
        ]
      }
    },
    {
      "txCode": "601118",
      "name": "账户余额实时查询",
      "businessCode": "100016",
      "securityLevel": "10",
      "description": "实时查询指定账户的余额和可用余额",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100016"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true},
          {"fieldId": "data", "name": "响应数据", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
            {"fieldId": "accBal", "name": "余额", "type": "D(20,2)", "required": true},
            {"fieldId": "accAvalBal", "name": "可用余额", "type": "D(20,2)", "required": true}
          ]}
        ]
      }
    },
    {
      "txCode": "601119",
      "name": "查询明细",
      "businessCode": "100017",
      "securityLevel": "10",
      "description": "查询账户交易明细，支持分页，时间范围最多 7 天",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100017"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
          {"fieldId": "txStartDate", "name": "交易开始时间", "type": "ANS..10", "required": true, "format": "yyyyMMdd", "description": "最早支持最近一年之内"},
          {"fieldId": "txEndDate", "name": "交易结束时间", "type": "ANS..10", "required": true, "format": "yyyyMMdd", "description": "开始结束时间仅支持 7 天"},
          {"fieldId": "pageIndex", "name": "页码", "type": "N..19", "required": true},
          {"fieldId": "pageSize", "name": "每页条数", "type": "N..19", "required": true, "description": "支持 1-100"}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": false, "items": [
            {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
            {"fieldId": "accName", "name": "户名", "type": "ANS..150", "required": true},
            {"fieldId": "txDate", "name": "交易日期", "type": "ANS10", "required": true},
            {"fieldId": "txTime", "name": "交易时间", "type": "ANS8", "required": true},
            {"fieldId": "opsAccNo", "name": "交易对手账号", "type": "AN..60", "required": true},
            {"fieldId": "opsAccName", "name": "交易对手户名", "type": "ANS..100", "required": true},
            {"fieldId": "dcFlag", "name": "借贷标志", "type": "N1", "required": true, "enum": {"0": "借", "1": "贷"}},
            {"fieldId": "currCode", "name": "币种", "type": "AN3", "required": true},
            {"fieldId": "currCnName", "name": "币种名称", "type": "ANS..100", "required": true},
            {"fieldId": "txAmt", "name": "交易金额", "type": "D(20,2)", "required": true},
            {"fieldId": "accBal", "name": "账户余额", "type": "D(20,2)", "required": true},
            {"fieldId": "txSriNo", "name": "银行流水号", "type": "ANS..80", "required": true},
            {"fieldId": "remark", "name": "附言", "type": "ANS..2000", "required": true},
            {"fieldId": "txSumm", "name": "摘要", "type": "ANS..400", "required": true},
            {"fieldId": "id", "name": "主键 ID", "type": "AN..32", "required": true},
            {"fieldId": "batchNo", "name": "批次号", "type": "AN..32", "required": false}
          ]},
          {"fieldId": "pagingResult", "name": "分页结果", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "pageIndex", "name": "页码", "type": "N..19", "required": true},
            {"fieldId": "pageSize", "name": "每页条数", "type": "N..19", "required": true},
            {"fieldId": "total", "name": "记录总条数", "type": "AN..10", "required": true}
          ]}
        ]
      }
    },
    {
      "txCode": "601120",
      "name": "账户历史余额查询",
      "businessCode": "100018",
      "securityLevel": "10",
      "description": "查询账户历史余额",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100018"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
          {"fieldId": "startDate", "name": "开始日期", "type": "ANS..10", "required": true},
          {"fieldId": "endDate", "name": "结束日期", "type": "ANS..10", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": false, "items": [
            {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
            {"fieldId": "date", "name": "日期", "type": "ANS..10", "required": true},
            {"fieldId": "accBal", "name": "当天余额", "type": "D(20,2)", "required": false},
            {"fieldId": "avalBal", "name": "可用余额", "type": "D(20,2)", "required": false}
          ]}
        ]
      }
    },
    {
      "txCode": "601203",
      "name": "明细关联回单信息查询",
      "businessCode": "100019",
      "securityLevel": "15",
      "description": "查询明细关联的回单信息，安全级别 15，业务报文平铺传输",
      "request": {
        "businessFields": [
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "id", "name": "明细 id", "type": "AN..32", "required": true}
        ],
        "flatMode": true
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
            {"fieldId": "feedAckTime", "name": "反馈确认时间", "type": "AN..19", "required": true, "description": "时间戳毫秒数"},
            {"fieldId": "id", "name": "明细 id", "type": "AN..32", "required": true},
            {"fieldId": "fileType", "name": "回单文件类型", "type": "ANA..6", "required": true, "description": "如 pdf、png"},
            {"fieldId": "file", "name": "回单文件", "type": "CLOB", "required": true, "description": "Base64 文件流字符串"}
          ]}
        ]
      }
    },
    {
      "txCode": "601303",
      "name": "查询资金归集明细",
      "businessCode": "100024",
      "securityLevel": "10",
      "description": "查询资金归集明细，支持分页",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100024"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
          {"fieldId": "txStartDate", "name": "交易开始时间", "type": "ANS..10", "required": true, "format": "yyyy-MM-dd"},
          {"fieldId": "txEndDate", "name": "交易结束时间", "type": "ANS..10", "required": true, "format": "yyyy-MM-dd"},
          {"fieldId": "pageIndex", "name": "页码", "type": "N..19", "required": true},
          {"fieldId": "pageSize", "name": "每页条数", "type": "N..19", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": false, "items": [
            {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
            {"fieldId": "accName", "name": "户名", "type": "ANS..150", "required": true},
            {"fieldId": "txDate", "name": "交易日期", "type": "ANS10", "required": true},
            {"fieldId": "txTime", "name": "交易时间", "type": "ANS8", "required": true},
            {"fieldId": "opsAccNo", "name": "交易对手账号", "type": "AN..60", "required": true},
            {"fieldId": "opsAccName", "name": "交易对手户名", "type": "ANS..100", "required": true},
            {"fieldId": "dcFlag", "name": "借贷标志", "type": "N1", "required": true},
            {"fieldId": "currCode", "name": "币种", "type": "AN3", "required": true},
            {"fieldId": "txAmt", "name": "交易金额", "type": "D(20,2)", "required": true},
            {"fieldId": "accBal", "name": "账户余额", "type": "D(20,2)", "required": true},
            {"fieldId": "txSriNo", "name": "银行流水号", "type": "ANS..80", "required": true},
            {"fieldId": "postscript", "name": "附言", "type": "ANS..2000", "required": false},
            {"fieldId": "txSumm", "name": "摘要", "type": "ANS..400", "required": true},
            {"fieldId": "id", "name": "主键 ID", "type": "AN..32", "required": true}
          ]},
          {"fieldId": "pagingResult", "name": "分页结果", "type": "JsonObject", "required": false}
        ]
      }
    },
    {
      "txCode": "601304",
      "name": "手工下拨",
      "businessCode": "100025",
      "securityLevel": "10",
      "description": "手工下拨资金到下级账户",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100025"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true, "description": "付方账号"},
          {"fieldId": "opsAccNo", "name": "交易对手账号", "type": "AN..60", "required": true, "description": "下拨对手账号"},
          {"fieldId": "txAmt", "name": "交易金额", "type": "D(20,2)", "required": true},
          {"fieldId": "postscript", "name": "附言", "type": "ANS..2000", "required": false},
          {"fieldId": "currCode", "name": "币种", "type": "AN3", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "bankAccno", "name": "账号", "type": "AN..60", "required": true},
            {"fieldId": "opsAccNo", "name": "交易对手账号", "type": "AN..60", "required": true},
            {"fieldId": "txSriNo", "name": "银行流水号", "type": "ANS..80", "required": true},
            {"fieldId": "applyNo", "name": "申请单编号", "type": "ANS..32", "required": false, "description": "校验通过后返回，后续通过 7.20 接口查询"}
          ]}
        ]
      }
    },
    {
      "txCode": "601311",
      "name": "融资中心付款单获取",
      "businessCode": "100023",
      "securityLevel": "10",
      "description": "获取融资中心已推送的付款单",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：601311"},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "deptNo", "name": "付款单位", "type": "ANS..100", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": false, "items": [
            {"fieldId": "deptNo", "name": "付款单位", "type": "ANS..100", "required": true},
            {"fieldId": "repayNo", "name": "还款计划编号", "type": "ANS..20", "required": true},
            {"fieldId": "repayType", "name": "业务类型", "type": "ANS..20", "required": true, "description": "还本/付息/还本付息"},
            {"fieldId": "repayAccNo", "name": "付款账号", "type": "ANS..50", "required": false},
            {"fieldId": "repayDate", "name": "预计还款日期", "type": "AN8", "required": true},
            {"fieldId": "repayAmt", "name": "付款金额", "type": "D(20,2)", "required": true},
            {"fieldId": "curName", "name": "付款币种", "type": "ANS..100", "required": true},
            {"fieldId": "payeeName", "name": "收款人", "type": "ANS..512", "required": true}
          ]}
        ]
      }
    },
    {
      "txCode": "601324",
      "name": "新增申请单",
      "businessCode": "100021",
      "securityLevel": "10",
      "description": "新增申请单，仅限付款管理（新）菜单租户使用",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100021"},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "oldBatchNo", "name": "来源系统批次号", "type": "ANS..64", "required": true},
          {"fieldId": "busiTypeCode", "name": "业务类型", "type": "ANS..100", "required": false},
          {"fieldId": "payblAmt", "name": "应付金额", "type": "D(20,2)", "required": true},
          {"fieldId": "orgInstCode", "name": "付方组织编码", "type": "ANS..32", "required": true},
          {"fieldId": "payerAccno", "name": "付方账号", "type": "ANS..32", "required": false},
          {"fieldId": "payerAccname", "name": "付方户名", "type": "ANS..32", "required": false},
          {"fieldId": "payeeAccno", "name": "收方账号", "type": "ANS..32", "required": true},
          {"fieldId": "payeeAccname", "name": "收方户名", "type": "ANS..32", "required": true},
          {"fieldId": "dealAttachList", "name": "附件列表", "type": "ANS..32", "required": false},
          {"fieldId": "payeeOacBankName", "name": "收方开户行", "type": "ANS..32", "required": false},
          {"fieldId": "recpayBankCnapsNo", "name": "收方联行号", "type": "ANS..32", "required": false},
          {"fieldId": "budgetNo", "name": "预算编号", "type": "ANS..32", "required": false}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": false}
        ]
      }
    },
    {
      "txCode": "601325",
      "name": "申请单结果查询",
      "businessCode": "100022",
      "securityLevel": "10",
      "description": "查询申请单处理结果，仅限付款管理（新）菜单租户使用",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS6", "required": true, "description": "固定值：100022"},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS32", "required": true},
          {"fieldId": "oldBatchNo", "name": "来源系统批次号", "type": "ANS64", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonArray", "required": true, "items": [
            {"fieldId": "payStatus", "name": "申请单状态", "type": "ANS..1", "required": true, "enum": {"0": "申请单待审核", "1": "申请单审核中", "2": "申请单已驳回", "f": "支付单待送审", "g": "支付单审批中", "h": "支付单已驳回", "3": "待付款", "4": "付款中", "5": "付款成功", "6": "部分成功", "7": "付款失败", "b": "已退回", "c": "已作废"}},
            {"fieldId": "applyNo", "name": "申请单编号", "type": "ANS..32", "required": true},
            {"fieldId": "oldBatchNo", "name": "来源系统批次号", "type": "ANS..64", "required": true},
            {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
            {"fieldId": "busiTypeCode", "name": "业务类型", "type": "ANS..100", "required": false},
            {"fieldId": "payblAmt", "name": "应付金额", "type": "D(20,2)", "required": true},
            {"fieldId": "orgInstCode", "name": "付方组织代码", "type": "ANS..32", "required": true},
            {"fieldId": "orgName", "name": "付方组织名称", "type": "ANS..32", "required": true},
            {"fieldId": "payerAccno", "name": "付方账号", "type": "ANS..32", "required": false},
            {"fieldId": "payerAccname", "name": "付方户名", "type": "ANS..32", "required": false},
            {"fieldId": "payeeAccno", "name": "收方账号", "type": "ANS..32", "required": true},
            {"fieldId": "payeeAccname", "name": "收方户名", "type": "ANS..32", "required": true},
            {"fieldId": "payList", "name": "支付单列表", "type": "list", "required": false, "items": [
              {"fieldId": "id", "name": "支付单编号", "type": "ANS..19", "required": false},
              {"fieldId": "txPayStaCd", "name": "支付单状态", "type": "AN1", "required": false},
              {"fieldId": "linkType", "name": "流程链路状态", "type": "AN1", "required": false},
              {"fieldId": "txType", "name": "支付类型", "type": "N1", "required": false},
              {"fieldId": "payMode", "name": "支付方式", "type": "N1", "required": false},
              {"fieldId": "networkflagCode", "name": "联网方式", "type": "N2", "required": false},
              {"fieldId": "payAmt", "name": "支付金额", "type": "N..20", "required": false},
              {"fieldId": "payType", "name": "支付模式", "type": "N1", "required": false},
              {"fieldId": "paymDate", "name": "预约日期", "type": "AN8", "required": false},
              {"fieldId": "finishDate", "name": "支付完成时间", "type": "AN14", "required": false},
              {"fieldId": "budgetNo", "name": "预算编号", "type": "ANS..32", "required": false}
            ]}
          ]}
        ]
      }
    },
    {
      "txCode": "601383",
      "name": "获取免密登录令牌",
      "businessCode": "100027",
      "securityLevel": "10",
      "description": "获取免密登录令牌，用于司库系统免密登录",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100027"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "userAccNo", "name": "用户账号", "type": "AN..100", "required": true, "description": "司库系统的登录账号 (手机号)"},
          {"fieldId": "tenantNo", "name": "令牌类型", "type": "ANS..40", "required": true, "description": "固定值：PASS_TOKEN"}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "urlWithToken", "name": "链接", "type": "AN..500", "required": true, "description": "带令牌的登录 URL"}
          ]}
        ]
      }
    },
    {
      "txCode": "601417",
      "name": "资金计划科目同步",
      "businessCode": "100028",
      "securityLevel": "10",
      "description": "将资金计划科目同步到司库系统",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100028"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "交易时间", "type": "AN14", "required": true},
          {"fieldId": "type", "name": "同步类型", "type": "AN1", "required": true, "enum": {"1": "新增", "2": "修改", "3": "停用", "4": "删除"}},
          {"fieldId": "items", "name": "科目列表", "type": "JsonArray", "required": true, "items": [
            {"fieldId": "cdSign", "name": "收支属性", "type": "ANS..2", "required": true, "enum": {"10": "流入", "20": "流出"}},
            {"fieldId": "code", "name": "科目编码", "type": "AN..32", "required": false, "description": "type 为 2/3/4 时必输"},
            {"fieldId": "name", "name": "科目名称", "type": "AN..100", "required": true},
            {"fieldId": "superCode", "name": "上级科目编码", "type": "ANS..32", "required": false},
            {"fieldId": "superName", "name": "上级科目名称", "type": "ANS..32", "required": false}
          ]}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "交易时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "List", "required": false, "items": [
            {"fieldId": "itemCode", "name": "科目编码", "type": "ANS..32", "required": false},
            {"fieldId": "itemName", "name": "科目名称", "type": "ANS..32", "required": false}
          ]}
        ]
      }
    },
    {
      "txCode": "601418",
      "name": "资金计划填报",
      "businessCode": "100029",
      "securityLevel": "10",
      "description": "上报年/季/月/周/旬资金计划金额",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100029"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "交易时间", "type": "AN14", "required": true},
          {"fieldId": "cur", "name": "币种", "type": "ANS..3", "required": true, "description": "如 CNY"},
          {"fieldId": "orgId", "name": "单位 id", "type": "ANS..40", "required": true},
          {"fieldId": "timePeriod", "name": "预算类型", "type": "AN2", "required": true, "enum": {"1": "年", "2": "季", "3": "月", "4": "旬", "5": "周"}},
          {"fieldId": "beginDate", "name": "预算开始日", "type": "AN8", "required": true, "format": "yyyyMMdd"},
          {"fieldId": "endDate", "name": "预算结束日", "type": "AN8", "required": true, "format": "yyyyMMdd"},
          {"fieldId": "items", "name": "预算请求提交金额 List", "type": "JsonArray", "required": true, "items": [
            {"fieldId": "itemCode", "name": "科目编码", "type": "ANS..32", "required": false},
            {"fieldId": "itemName", "name": "科目名称", "type": "ANS..32", "required": false},
            {"fieldId": "budgetAmount", "name": "填报金额", "type": "D(20,2)", "required": true}
          ]}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false}
        ]
      }
    },
    {
      "txCode": "601419",
      "name": "资金计划追加",
      "businessCode": "100030",
      "securityLevel": "10",
      "description": "对本期资金计划做追加，可追加负数表示减少",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100030"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "交易时间", "type": "AN14", "required": true},
          {"fieldId": "cur", "name": "币种", "type": "ANS..32", "required": true},
          {"fieldId": "orgId", "name": "单位 id", "type": "ANS..40", "required": true},
          {"fieldId": "timePeriod", "name": "预算类型", "type": "AN2", "required": true},
          {"fieldId": "beginDate", "name": "预算开始日", "type": "AN8", "required": true},
          {"fieldId": "endDate", "name": "预算结束日", "type": "AN8", "required": true},
          {"fieldId": "items", "name": "预算请求提交金额 List", "type": "JsonArray", "required": true, "items": [
            {"fieldId": "itemCode", "name": "科目编码", "type": "ANS..32", "required": false},
            {"fieldId": "itemName", "name": "科目名称", "type": "ANS..32", "required": false},
            {"fieldId": "addAmount", "name": "追加金额", "type": "D(20,2)", "required": true, "description": "可以为负"}
          ]}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false}
        ]
      }
    },
    {
      "txCode": "601420",
      "name": "资金计划占用情况查询",
      "businessCode": "100031",
      "securityLevel": "10",
      "description": "查询每期资金计划执行情况",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100031"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "交易时间", "type": "AN14", "required": true},
          {"fieldId": "current", "name": "当前页", "type": "Int", "required": true},
          {"fieldId": "size", "name": "每页数量", "type": "INT", "required": true},
          {"fieldId": "orgId", "name": "单位 id", "type": "ANS..40", "required": true},
          {"fieldId": "timePeriod", "name": "预算类型", "type": "int", "required": true},
          {"fieldId": "beginDate", "name": "开始时间", "type": "AN8", "required": true},
          {"fieldId": "endDate", "name": "结束时间", "type": "AN8", "required": true}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantId", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "交易日期", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..100", "required": true},
          {"fieldId": "data", "name": "响应内容", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "current", "name": "当前页", "type": "INT", "required": true},
            {"fieldId": "pages", "name": "页数", "type": "INT", "required": true},
            {"fieldId": "orgId", "name": "单位 id", "type": "ANS..40", "required": true},
            {"fieldId": "records", "name": "数据集合", "type": "List", "required": true, "items": [
              {"fieldId": "timePeriod", "name": "预算类型", "type": "int", "required": true},
              {"fieldId": "beginDate", "name": "开始时间", "type": "AN8", "required": true},
              {"fieldId": "endDate", "name": "结束时间", "type": "AN8", "required": true},
              {"fieldId": "budgetInSumAmount", "name": "资金收入预算总额", "type": "D(20,2)", "required": true},
              {"fieldId": "inCarrySumAmount", "name": "资金收入执行总额", "type": "D(20,2)", "required": true},
              {"fieldId": "budgetOutSumAmount", "name": "资金支出预算总额", "type": "D(20,2)", "required": true},
              {"fieldId": "outCarrySumAmount", "name": "资金支出执行总额", "type": "D(20,2)", "required": true},
              {"fieldId": "items", "name": "详细执行数据", "type": "List", "required": true, "items": [
                {"fieldId": "itemCode", "name": "科目编码", "type": "ANS..32", "required": true},
                {"fieldId": "itemName", "name": "科目名称", "type": "ANS..32", "required": true},
                {"fieldId": "cdsign", "name": "收支标识", "type": "Int", "required": true, "enum": {"10": "收入", "20": "支出"}},
                {"fieldId": "budgetAmount", "name": "预算金额合计", "type": "D(20,2)", "required": true},
                {"fieldId": "reportAmt", "name": "上报金额", "type": "D(20,2)", "required": true},
                {"fieldId": "addedTototalAmt", "name": "追加总额", "type": "D(20,2)", "required": true},
                {"fieldId": "processAmount", "name": "执行金额", "type": "D(20,2)", "required": true}
              ]}
            ]}
          ]}
        ]
      }
    },
    {
      "txCode": "601732",
      "name": "手工下拨结果查询",
      "businessCode": "100032",
      "securityLevel": "10",
      "description": "查询手工下拨处理结果",
      "request": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true, "description": "固定值：100032"},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "srcSysId", "name": "来源系统 ID", "type": "ANS..32", "required": true},
          {"fieldId": "applyNo", "name": "申请单编号", "type": "AN..32", "required": true, "description": "7.11 返回的申请单编号"}
        ]
      },
      "response": {
        "businessFields": [
          {"fieldId": "txCode", "name": "交易代码", "type": "ANS..6", "required": true},
          {"fieldId": "tenantID", "name": "租户 ID", "type": "ANS..32", "required": true},
          {"fieldId": "sendTime", "name": "发起日期", "type": "AN14", "required": true},
          {"fieldId": "feedbackTime", "name": "反馈时间", "type": "AN14", "required": true},
          {"fieldId": "respCode", "name": "响应码", "type": "AN6", "required": true},
          {"fieldId": "respInfo", "name": "响应信息", "type": "ANS..50", "required": true},
          {"fieldId": "data", "name": "响应数据", "type": "JsonObject", "required": false, "properties": [
            {"fieldId": "applyNo", "name": "申请单编号", "type": "AN..32", "required": true},
            {"fieldId": "applyStatus", "name": "审批状态", "type": "N2", "required": true, "enum": {"9": "审批中", "4": "审批通过", "5": "驳回"}},
            {"fieldId": "payStatus", "name": "支付状态代码", "type": "AN1", "required": true, "enum": {"0": "待支付", "1": "支付中", "3": "支付成功", "4": "支付失败"}},
            {"fieldId": "failReason", "name": "失败原因", "type": "AN..2048", "required": false},
            {"fieldId": "finishDate", "name": "支付完成时间", "type": "AN14", "required": false}
          ]}
        ]
      }
    }
  ]
}


path: examples/601118-curl-example.sh
content: #!/bin/bash
# 账户余额实时查询 - 601118 接口示例
# 安全级别：10

# 配置参数（请根据实际情况修改）
BASE_URL="https://olt.api-test.psbc.com:9902/gateway/std/"
REQ_SYS_CODE="99711940001"  # 接入系统代码
USER_CERT_SN="0817fbeecc848"  # 用户证书序列号
BANK_CERT_SN="12e15039e89ff93c"  # 银行证书序列号

# 生成系统跟踪号：时间戳 (14 位) + 接入系统代码 (12 位) + 6 位唯一序列号
TX_TIME=$(date +%Y%m%d%H%M%S%3N)
SYS_TRACK_NO="${TX_TIME:0:14}${REQ_SYS_CODE}$(printf '%06d' $RANDOM)"

# 业务报文（明文）
BUSINESS_PAYLOAD='{
  "txCode": "100016",
  "tenantID": "eam_tenant_a_0001",
  "sendTime": "'$(date +%Y%m%d%H%M%S)'",
  "srcSysId": "140001",
  "bankAccno": "951011013000006323"
}'

# 注意：实际使用时需要对业务报文进行 SM4 加密，并对签名数据进行国密签名
# 以下 encData 和 sign 为示例占位符，实际需调用国密库生成

# 完整请求报文
REQUEST_PAYLOAD=$(cat <<EOF
{
  "txComm": {
    "sysTrackNo": "${SYS_TRACK_NO}",
    "reqSysCode": "${REQ_SYS_CODE}",
    "txCode": "601118",
    "txTime": "${TX_TIME}",
    "securityLevel": "10",
    "sign": "<SM2 签名值 Base64>",
    "bankCertSN": "${BANK_CERT_SN}",
    "userCertSN": "${USER_CERT_SN}",
    "encData": "<SM4 加密后的业务报文 Base64>",
    "encKey": "<SM4 密钥使用银行证书加密后的 Base64>"
  }
}
EOF
)

# 发送请求
curl -X POST "${BASE_URL}" \
  -H "Content-Type: application/json;charset=UTF-8" \
  -d "${REQUEST_PAYLOAD}" \
  -v

# 响应示例（解密后）：
# {
#   "txComm": {
#     "respCode": "0000000000000000",
#     "respDesc": "交易成功"
#   },
#   "txCode": "100016",
#   "tenantID": "eam_tenant_a_0001",
#   "sendTime": "20250303163811",
#   "feedbackTime": "20250303163815",
#   "respCode": "000000",
#   "respInfo": "成功",
#   "data": {
#     "bankAccno": "951011013000006323",
#     "accBal": "1000000.00",
#     "accAvalBal": "1000000.00"
#   }
# }


path: examples/psbc_client.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
邮储银行财资管理系统接口调用示例
包含：报文加密/解密、签名/验签、HTTP 请求
"""

import json
import base64
import hashlib
import requests
from datetime import datetime
from typing import Dict, Any, Optional

# ==================== 配置区域 ====================
CONFIG = {
    "test": {
        "base_url": "https://olt.api-test.psbc.com:9902/gateway/std/",
        "req_sys_code": "99711940001",
        "user_cert_sn": "0817fbeecc848",
        "bank_cert_sn": "12e15039e89ff93c",
    },
    "staging": {
        "base_url": "https://olt.api-test.psbc.com:19902/gateway/std/",
    },
    "production": {
        "base_url": "https://api.psbc.com:443/gateway/std/",
    }
}

# ==================== 国密工具类（需替换为实际实现）====================
class SMCrypto:
    """
    国密算法工具类
    注意：实际使用时需要集成国密库，如 gmssl 或 pysm2
    """
    
    @staticmethod
    def sm4_encrypt(data: bytes, key: bytes) -> bytes:
        """SM4 加密"""
        # TODO: 使用实际国密库实现
        raise NotImplementedError("请集成国密库实现 SM4 加密")
    
    @staticmethod
    def sm4_decrypt(cipher: bytes, key: bytes) -> bytes:
        """SM4 解密"""
        # TODO: 使用实际国密库实现
        raise NotImplementedError("请集成国密库实现 SM4 解密")
    
    @staticmethod
    def sm2_sign(data: bytes, private_key: bytes) -> bytes:
        """SM2 签名"""
        # TODO: 使用实际国密库实现
        raise NotImplementedError("请集成国密库实现 SM2 签名")
    
    @staticmethod
    def sm2_verify(data: bytes, signature: bytes, public_key: bytes) -> bool:
        """SM2 验签"""
        # TODO: 使用实际国密库实现
        raise NotImplementedError("请集成国密库实现 SM2 验签")
    
    @staticmethod
    def sm3_hash(data: bytes) -> bytes:
        """SM3 哈希"""
        # TODO: 使用实际国密库实现
        raise NotImplementedError("请集成国密库实现 SM3 哈希")


# ==================== 报文处理类 ====================
class PSBCTreasuryClient:
    """邮储财资系统客户端"""
    
    def __init__(self, env: str = "test"):
        self.config = CONFIG.get(env, CONFIG["test"])
        self.base_url = self.config["base_url"]
        self.session = requests.Session()
        self.session.headers.update({
            "Content-Type": "application/json;charset=UTF-8"
        })
    
    def generate_sys_track_no(self) -> str:
        """生成系统跟踪号：时间戳 (14 位) + 接入系统代码 (12 位) + 6 位唯一序列号"""
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")[:14]
        req_sys_code = self.config["req_sys_code"]
        unique_seq = f"{id(self) % 1000000:06d}"
        return f"{timestamp}{req_sys_code}{unique_seq}"
    
    def build_request(self, tx_code: str, business_data: Dict[str, Any], 
                      security_level: str = "10") -> Dict[str, Any]:
        """
        构建请求报文
        
        Args:
            tx_code: 交易码（6 位）
            business_data: 业务报文字典
            security_level: 安全级别（10 或 15）
        
        Returns:
            完整请求报文字典
        """
        tx_time = datetime.now().strftime("%Y%m%d%H%M%S%f")[:17]
        sys_track_no = self.generate_sys_track_no()
        
        # 业务报文转 JSON 字符串
        business_json = json.dumps(business_data, ensure_ascii=False, separators=(',', ':'))
        
        if security_level == "10":
            # 生成随机 SM4 密钥
            sm4_key = self._generate_sm4_key()
            
            # SM4 加密业务报文
            enc_data = SMCrypto.sm4_encrypt(business_json.encode('utf-8'), sm4_key)
            enc_data_b64 = base64.b64encode(enc_data).decode('ascii')
            
            # 使用银行证书加密 SM4 密钥
            enc_key = self._encrypt_sm4_key(sm4_key)
            enc_key_b64 = base64.b64encode(enc_key).decode('ascii')
            
            # 构建签名字符串
            sign_str = f"{sys_track_no}{self.config['req_sys_code']}{tx_code}{tx_time}{enc_data_b64}{enc_key_b64}"
            signature = SMCrypto.sm2_sign(sign_str.encode('utf-8'), self._get_private_key())
            sign_b64 = base64.b64encode(signature).decode('ascii')
            
            return {
                "txComm": {
                    "sysTrackNo": sys_track_no,
                    "reqSysCode": self.config["req_sys_code"],
                    "txCode": tx_code,
                    "txTime": tx_time,
                    "securityLevel": security_level,
                    "sign": sign_b64,
                    "bankCertSN": self.config["bank_cert_sn"],
                    "userCertSN": self.config["user_cert_sn"],
                    "encData": enc_data_b64,
                    "encKey": enc_key_b64
                }
            }
        
        elif security_level == "15":
            # 安全级别 15 仅签名，不加密
            sign_str = f"{sys_track_no}{self.config['req_sys_code']}{tx_code}{tx_time}"
            signature = SMCrypto.sm2_sign(sign_str.encode('utf-8'), self._get_private_key())
            sign_b64 = base64.b64encode(signature).decode('ascii')
            
            # 业务报文平铺
            request_data = {
                "txComm": {
                    "sysTrackNo": sys_track_no,
                    "reqSysCode": self.config["req_sys_code"],
                    "txCode": tx_code,
                    "txTime": tx_time,
                    "securityLevel": security_level,
                    "sign": sign_b64,
                    "userCertSN": self.config["user_cert_sn"]
                }
            }
            request_data.update(business_data)
            return request_data
        
        else:
            raise ValueError(f"不支持的安全级别：{security_level}")
    
    def send_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """发送 HTTP 请求"""
        response = self.session.post(self.base_url, json=request_data, timeout=30)
        response.raise_for_status()
        return response.json()
    
    def parse_response(self, response_data: Dict[str, Any], 
                       security_level: str = "10") -> Dict[str, Any]:
        """
        解析响应报文
        
        Args:
            response_data: 原始响应数据
            security_level: 安全级别
        
        Returns:
            解析后的业务报文
        """
        tx_comm = response_data.get("txComm", {})
        resp_code = tx_comm.get("respCode", "")
        
        # 检查前置响应码
        if resp_code != "0000000000000000":
            raise Exception(f"请求失败：{tx_comm.get('respDesc', '未知错误')}")
        
        if security_level == "10":
            # 解密业务报文
            enc_data_b64 = tx_comm.get("encData", "")
            enc_data = base64.b64decode(enc_data_b64)
            sm4_key = self._decrypt_sm4_key(self._get_private_key())
            business_json = SMCrypto.sm4_decrypt(enc_data, sm4_key)
            return json.loads(business_json.decode('utf-8'))
        
        elif security_level == "15":
            # 安全级别 15，业务报文平铺
            business_data = {k: v for k, v in response_data.items() if k != "txComm"}
            return business_data
        
        else:
            raise ValueError(f"不支持的安全级别：{security_level}")
    
    # ==================== 辅助方法 ====================
    def _generate_sm4_key(self) -> bytes:
        """生成随机 SM4 密钥（16 字节）"""
        import os
        return os.urandom(16)
    
    def _encrypt_sm4_key(self, sm4_key: bytes) -> bytes:
        """使用银行证书公钥加密 SM4 密钥"""
        # TODO: 使用银行证书公钥进行 SM2 加密
        raise NotImplementedError("请实现银行证书公钥加密")
    
    def _decrypt_sm4_key(self, private_key: bytes) -> bytes:
        """使用私钥解密 SM4 密钥"""
        # TODO: 使用私钥进行 SM2 解密
        raise NotImplementedError("请实现私钥解密")
    
    def _get_private_key(self) -> bytes:
        """获取用户私钥"""
        # TODO: 从证书文件或密钥管理系统获取
        raise NotImplementedError("请配置用户私钥")


# ==================== 使用示例 ====================
def example_balance_query():
    """示例：账户余额实时查询（601118）"""
    client = PSBCTreasuryClient(env="test")
    
    # 构建业务报文
    business_data = {
        "txCode": "100016",
        "tenantID": "eam_tenant_a_0001",
        "sendTime": datetime.now().strftime("%Y%m%d%H%M%S"),
        "srcSysId": "140001",
        "bankAccno": "951011013000006323"
    }
    
    # 构建完整请求
    request_data = client.build_request(
        tx_code="601118",
        business_data=business_data,
        security_level="10"
    )
    
    print("请求报文:")
    print(json.dumps(request_data, indent=2, ensure_ascii=False))
    
    # 发送请求（需要实现国密算法后才能运行）
    # response_data = client.send_request(request_data)
    # business_response = client.parse_response(response_data)
    # print("响应报文:")
    # print(json.dumps(business_response, indent=2, ensure_ascii=False))


def example_payment_create():
    """示例：生成单笔支付申请单（601116）"""
    client = PSBCTreasuryClient(env="test")
    
    business_data = {
        "txCode": "100014",
        "tenantID": "eam_tenant_a_0001",
        "sendTime": datetime.now().strftime("%Y%m%d%H%M%S"),
        "srcSysId": "140001",
        "txType": "1",  # 对公支付
        "busiTypeCode": "002",  # 手续费
        "batchNo": "PAY202503180001",
        "payerAccno": "921026013000016778",
        "payAmt": "1000.00",
        "payType": "2",  # 预约支付
        "paymDate": "20250320",
        "fundsUsageInfo": "往来款",
        "payMode": "1",  # 账户
        "networkflagCode": "1",  # 直连
        "payeeAccno": "6217994660005823",
        "payeeAccname": "北京金帮伟业商贸中心",
        "recpayBankCnapsNo": "403100001082",
        "txPtscrt": "测试",
        "creatorName": "admin",
        "custFdsrcTpCd": "01"  # 普通支付单
    }
    
    request_data = client.build_request(
        tx_code="601116",
        business_data=business_data,
        security_level="10"
    )
    
    print("支付申请单请求报文:")
    print(json.dumps(request_data, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    # 运行示例
    example_balance_query()
    print("\n" + "="*50 + "\n")
    example_payment_create()


path: mock.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
邮储银行财资管理系统 - Mock 响应生成器
支持：预设成功/失败响应、延迟模拟、异常场景
"""

import json
import random
import time
from typing import Dict, Any, Optional
from datetime import datetime, timedelta


class PSBCMockServer:
    """邮储财资 Mock 响应服务器"""
    
    def __init__(self, apis_config: str = "apis.json", 
                 response_codes_config: str = "response-codes.json"):
        """
        初始化 Mock 服务器
        
        Args:
            apis_config: API 配置文件路径
            response_codes_config: 响应码配置文件路径
        """
        with open(apis_config, "r", encoding="utf-8") as f:
            self.apis = json.load(f)
        with open(response_codes_config, "r", encoding="utf-8") as f:
            self.response_codes = json.load(f)
        
        self.api_map = {api["txCode"]: api for api in self.apis["apis"]}
        
        # Mock 数据池
        self.mock_data = {
            "accounts": [
                {"bankAccno": "951011013000006323", "accName": "晨云儿有限公司", "accBal": 1000000.00, "avalBal": 1000000.00},
                {"bankAccno": "6217994660005821", "accName": "测试企业 A", "accBal": 500000.50, "avalBal": 450000.50},
                {"bankAccno": "921026013000016778", "accName": "示例公司 B", "accBal": 2000000.00, "avalBal": 1800000.00},
            ],
            "transactions": [
                {"txDate": "20250318", "txTime": "143022", "txAmt": 1000.00, "opsAccName": "供应商 A", "txSumm": "货款支付"},
                {"txDate": "20250317", "txTime": "091530", "txAmt": 5000.00, "opsAccName": "客户 B", "txSumm": "收款"},
                {"txDate": "20250316", "txTime": "162045", "txAmt": 200.00, "opsAccName": "水电费", "txSumm": "公用事业费"},
            ],
            "payment_statuses": ["1", "2", "3", "4", "5", "6"],  # 待送审到付款成功
        }
        
        # 场景配置
        self.scenarios = {
            "normal": {"success_rate": 1.0, "delay_ms": 100},
            "slow": {"success_rate": 1.0, "delay_ms": 3000},
            "flaky": {"success_rate": 0.7, "delay_ms": 500},
            "timeout": {"success_rate": 0.0, "timeout": True},
        }
        self.current_scenario = "normal"
    
    def set_scenario(self, scenario: str):
        """设置模拟场景"""
        if scenario not in self.scenarios:
            raise ValueError(f"未知场景：{scenario}，可用场景：{list(self.scenarios.keys())}")
        self.current_scenario = scenario
    
    def mock_request(self, tx_code: str, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        生成 Mock 响应
        
        Args:
            tx_code: 交易码
            request_data: 请求数据
        
        Returns:
            Mock 响应数据
        """
        scenario = self.scenarios[self.current_scenario]
        
        # 模拟超时
        if scenario.get("timeout"):
            time.sleep(31)  # 超过一般 timeout
            raise TimeoutError("请求超时")
        
        # 模拟延迟
        delay_ms = scenario.get("delay_ms", 100)
        time.sleep(delay_ms / 1000.0)
        
        # 随机失败
        if random.random() > scenario.get("success_rate", 1.0):
            return self._generate_error_response(tx_code, request_data)
        
        # 生成成功响应
        return self._generate_success_response(tx_code, request_data)
    
    def _generate_success_response(self, tx_code: str, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """生成成功响应"""
        if tx_code not in self.api_map:
            return self._generate_error_response(tx_code, request_data, "999990", f"未知交易码：{tx_code}")
        
        api = self.api_map[tx_code]
        tx_comm = request_data.get("txComm", {})
        
        # 公共响应头
        response = {
            "txComm": {
                "respCode": "0000000000000000",
                "respDesc": "交易成功"
            }
        }
        
        # 根据交易码生成具体响应
        if tx_code == "601118":
            # 账户余额实时查询
            response.update(self._mock_balance_query(request_data))
        
        elif tx_code == "601113":
            # 查询租户下直连账户列表
            response.update(self._mock_account_list(request_data))
        
        elif tx_code == "601119":
            # 查询明细
            response.update(self._mock_transaction_detail(request_data))
        
        elif tx_code == "601116":
            # 生成单笔支付申请单
            response.update(self._mock_payment_create(request_data))
        
        elif tx_code == "601117":
            # 单笔支付结果查询
            response.update(self._mock_payment_query(request_data))
        
        elif tx_code == "601114":
            # 批量工资代发
            response.update(self._mock_batch_payroll(request_data))
        
        elif tx_code == "601115":
            # 批量工资代发结果查询
            response.update(self._mock_batch_payroll_query(request_data))
        
        elif tx_code == "601203":
            # 明细关联回单信息查询
            response.update(self._mock_receipt_query(request_data))
        
        elif tx_code == "601304":
            # 手工下拨
            response.update(self._mock_manual_transfer(request_data))
        
        elif tx_code == "601732":
            # 手工下拨结果查询
            response.update(self._mock_manual_transfer_query(request_data))
        
        elif tx_code == "601383":
            # 获取免密登录令牌
            response.update(self._mock_token_query(request_data))
        
        else:
            # 通用响应
            response.update(self._mock_generic_response(tx_code, request_data))
        
        return response
    
    def _generate_error_response(self, tx_code: str, request_data: Dict[str, Any],
                                  error_code: Optional[str] = None, 
                                  error_msg: Optional[str] = None) -> Dict[str, Any]:
        """生成错误响应"""
        business_codes = self.response_codes.get("businessCodes", {})
        
        if error_code is None:
            # 随机选择一个错误码
            error_options = ["020302", "020300", "020301", "999990", "029999"]
            error_code = random.choice(error_options)
        
        code_info = business_codes.get(error_code, {})
        error_desc = error_msg or code_info.get("description", "未知错误")
        
        return {
            "txComm": {
                "respCode": "0000000000000000",
                "respDesc": "交易成功"
            },
            "txCode": request_data.get("txComm", {}).get("txCode", tx_code),
            "tenantID": request_data.get("tenantID", "unknown"),
            "sendTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": error_code,
            "respInfo": error_desc
        }
    
    # ==================== 具体接口 Mock 实现 ====================
    
    def _mock_balance_query(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 余额查询响应"""
        business_req = self._decrypt_business_data(request_data)
        bank_accno = business_req.get("bankAccno", "")
        
        # 查找模拟账户
        account = next((a for a in self.mock_data["accounts"] if a["bankAccno"] == bank_accno), None)
        
        if account:
            return {
                "txCode": "100016",
                "tenantID": business_req.get("tenantID", ""),
                "sendTime": business_req.get("sendTime", ""),
                "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
                "respCode": "000000",
                "respInfo": "成功",
                "data": {
                    "bankAccno": account["bankAccno"],
                    "accBal": f"{account['accBal']:.2f}",
                    "accAvalBal": f"{account['avalBal']:.2f}"
                }
            }
        else:
            return self._generate_error_response("601118", request_data, "020302", "查询无记录")
    
    def _mock_account_list(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 账户列表响应"""
        business_req = self._decrypt_business_data(request_data)
        
        accounts = []
        for acc in self.mock_data["accounts"]:
            accounts.append({
                "id": f"1914996334685585{random.randint(100, 999)}",
                "deptName": "总部",
                "deptNo": "13000001",
                "bankClsName": "中国邮政储蓄银行股份有限公司",
                "finInstName": f"中国邮政储蓄银行股份有限公司{acc['accName']}支行",
                "finInstNo": "403651000792",
                "accName": acc["accName"],
                "bankAccno": acc["bankAccno"],
                "createTime": "2025-01-01 00:00:00",
                "lastModTime": "2025-03-18 12:00:00",
                "accStatus": "2",
                "currCode": "156",
                "currCnName": "人民币",
                "openaccDate": "20250101",
                "accBal": acc["accBal"],
                "avalBal": acc["avalBal"],
                "detailUpdateTime": "2025-03-18 12:00:00",
                "openBedlFlag": "1",
                "accAttrNo": "ZH0002",
                "accAttrName": "一般存款账户",
                "balUpdateTime": "2025-03-18 12:00:00"
            })
        
        return {
            "txCode": "100011",
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": accounts,
            "pagingResult": {
                "pageIndex": business_req.get("pageIndex", 1),
                "pageSize": business_req.get("pageSize", 10),
                "total": str(len(accounts))
            }
        }
    
    def _mock_transaction_detail(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 交易明细响应"""
        business_req = self._decrypt_business_data(request_data)
        
        transactions = []
        for i, tx in enumerate(self.mock_data["transactions"]):
            transactions.append({
                "tenantId": business_req.get("tenantID", ""),
                "id": f"1930825892292329{i}",
                "bankAccno": business_req.get("bankAccno", ""),
                "accName": "测试账户",
                "txDate": tx["txDate"],
                "txTime": tx["txTime"],
                "opsAccNo": "6217994660005800",
                "opsAccName": tx["opsAccName"],
                "dcFlag": "0" if tx["txAmt"] > 0 else "1",
                "currCode": "156",
                "currCnName": "人民币",
                "txAmt": tx["txAmt"],
                "accBal": 1000000.00,
                "txSriNo": f"{tx['txDate']}{tx['txTime']}806tJ8nD",
                "remark": "",
                "txSumm": tx["txSumm"],
                "batchNo": "BATCH001"
            })
        
        return {
            "txCode": "100017",
            "tenantId": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": transactions,
            "pagingResult": {
                "pageIndex": business_req.get("pageIndex", 1),
                "pageSize": business_req.get("pageSize", 10),
                "total": str(len(transactions))
            }
        }
    
    def _mock_payment_create(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 支付申请单创建响应"""
        business_req = self._decrypt_business_data(request_data)
        
        # 模拟余额不足
        if float(business_req.get("payAmt", 0)) > 10000000:
            return self._generate_error_response("601116", request_data, "029999", "付款账户余额不足")
        
        return {
            "txCode": "100014",
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "成功"
        }
    
    def _mock_payment_query(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 支付结果查询响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": "100015",
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": {
                "payStatus": random.choice(self.mock_data["payment_statuses"]),
                "bankSriNo": f"20250318143022806tJ8nD",
                "finishDate": datetime.now().strftime("%Y%m%d%H%M%S")
            }
        }
    
    def _mock_batch_payroll(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 批量工资代发响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": "100012",
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "成功"
        }
    
    def _mock_batch_payroll_query(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 批量工资代发结果查询响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": "100013",
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": {
                "importFileStsCd": random.choice(["2", "6", "b"]),
                "appFormStaCd": "6",
                "failMsg": "",
                "batchNo": business_req.get("batchNo", "")
            }
        }
    
    def _mock_receipt_query(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 回单查询响应（安全级别 15，平铺模式）"""
        # 安全级别 15，业务报文平铺
        tenant_id = request_data.get("tenantID", "")
        
        return {
            "txComm": {
                "respCode": "0000000000000000",
                "respDesc": "交易成功"
            },
            "txCode": "100019",
            "tenantID": tenant_id,
            "sendTime": request_data.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "操作成功",
            "data": {
                "tenantId": tenant_id,
                "feedAckTime": int(datetime.now().timestamp() * 1000),
                "id": request_data.get("id", ""),
                "fileType": "pdf",
                "file": [REDACTED_SECRET]
            }
        }
    
    def _mock_manual_transfer(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 手工下拨响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": "100025",
            "tenantId": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": {
                "bankAccno": business_req.get("bankAccno", ""),
                "opsAccNo": business_req.get("opsAccNo", ""),
                "txSriNo": f"20250318143022806tJ8nD",
                "applyNo": f"APPLY{datetime.now().strftime('%Y%m%d%H%M%S')}"
            }
        }
    
    def _mock_manual_transfer_query(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 手工下拨结果查询响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": "100032",
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": {
                "applyNo": business_req.get("applyNo", ""),
                "applyStatus": "4",
                "payStatus": "3",
                "finishDate": datetime.now().strftime("%Y%m%d%H%M%S")
            }
        }
    
    def _mock_token_query(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """Mock 免密登录令牌响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": "100027",
            "tenantId": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": {
                "urlWithToken": f"https://treasury.psbc.com/sso/login?[REDACTED_SECRET], 999999)}_{datetime.now().timestamp()}"
            }
        }
    
    def _mock_generic_response(self, tx_code: str, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """通用 Mock 响应"""
        business_req = self._decrypt_business_data(request_data)
        
        return {
            "txCode": business_req.get("txCode", tx_code),
            "tenantID": business_req.get("tenantID", ""),
            "sendTime": business_req.get("sendTime", ""),
            "feedbackTime": datetime.now().strftime("%Y%m%d%H%M%S"),
            "respCode": "000000",
            "respInfo": "success",
            "data": {}
        }
    
    def _decrypt_business_data(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        解密业务数据（Mock 模式下直接返回或解析平铺数据）
        实际使用时需要实现 SM4 解密
        """
        tx_comm = request_data.get("txComm", {})
        security_level = tx_comm.get("securityLevel", "10")
        
        if security_level == "15":
            # 安全级别 15，业务报文平铺
            return {k: v for k, v in request_data.items() if k != "txComm"}
        
        # 安全级别 10，Mock 模式下假设已解密
        # 实际应解密 encData
        enc_data = tx_comm.get("encData", "")
        if enc_data:
            # TODO: 实现 SM4 解密
            pass
        
        # 返回示例数据
        return {
            "txCode": tx_comm.get("txCode", ""),
            "tenantID": "eam_tenant_a_0001",
            "sendTime": datetime.now().strftime("%Y%m%d%H%M%S"),
        }


# ==================== 命令行交互模式 ====================
def interactive_mock():
    """交互式 Mock 模式"""
    print("=" * 60)
    print("邮储银行财资管理系统 - Mock 响应生成器")
    print("=" * 60)
    
    mock_server = PSBCMockServer()
    
    print("\n可用场景:")
    for name, config in mock_server.scenarios.items():
        print(f"  {name}: 延迟{config.get('delay_ms', 0)}ms, 成功率{config.get('success_rate', 1.0)*100:.0f}%")
    
    print("\n当前场景：normal")
    print("\n请输入交易码（或输入 'q' 退出）: ", end="")
    tx_code = input().strip()
    
    if tx_code.lower() == 'q':
        return
    
    print("\n请输入请求报文 JSON（多行，空行结束）:")
    lines = []
    while True:
        line = input()
        if line.strip() == "":
            break
        lines.append(line)
    
    try:
        request_data = json.loads("\n".join(lines))
    except json.JSONDecodeError as e:
        print(f"JSON 解析错误：{e}")
        return
    
    print("\n生成 Mock 响应...")
    try:
        response = mock_server.mock_request(tx_code, request_data)
        print("\n响应结果:")
        print(json.dumps(response, indent=2, ensure_ascii=False))
    except TimeoutError as e:
        print(f"\n❌ 请求超时：{e}")


if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1 and sys.argv[1] == "--interactive":
        interactive_mock()
    else:
        # 示例：生成余额查询 Mock 响应
        mock_server = PSBCMockServer()
        
        request_data = {
            "txComm": {
                "sysTrackNo": "20250318143022997116600036518473",
                "reqSysCode": "99711940001",
                "txCode": "601118",
                "txTime": "20250318143022714",
                "securityLevel": "10",
                "sign": "xxx",
                "bankCertSN": "xxx",
                "userCertSN": "xxx",
                "encData": "xxx",
                "encKey": "xxx"
            }
        }
        
        response = mock_server.mock_request("601118", request_data)
        print("Mock 响应示例:")
        print(json.dumps(response, indent=2, ensure_ascii=False))


path: response-codes.json
content: {
  "version": "1.2.5",
  "updateDate": "2026-03-04",
  "standardCodes": {
    "200": {
      "code": "200",
      "description": "请求成功",
      "descriptionEn": "Request successful",
      "suggestion": "无需处理"
    },
    "400": {
      "code": "400",
      "description": "请求参数错误",
      "descriptionEn": "Bad request - invalid parameters",
      "suggestion": "检查请求参数格式和内容"
    },
    "401": {
      "code": "401",
      "description": "认证失败",
      "descriptionEn": "Unauthorized - authentication failed",
      "suggestion": "检查证书和签名配置"
    },
    "403": {
      "code": "403",
      "description": "权限不足",
      "descriptionEn": "Forbidden - insufficient permissions",
      "suggestion": "联系管理员确认权限配置"
    },
    "429": {
      "code": "429",
      "description": "请求限流",
      "descriptionEn": "Too many requests - rate limited",
      "suggestion": "降低请求频率，稍后重试"
    },
    "500": {
      "code": "500",
      "description": "服务器内部错误",
      "descriptionEn": "Internal server error",
      "suggestion": "联系银行技术支持"
    }
  },
  "businessCodes": {
    "000000": {
      "code": "000000",
      "description": "操作成功",
      "descriptionEn": "Operation successful",
      "suggestion": "无需处理"
    },
    "010001": {
      "code": "010001",
      "description": "匹配原账户信息无记录",
      "descriptionEn": "No matching account record found",
      "suggestion": "检查账户信息是否正确"
    },
    "010002": {
      "code": "010002",
      "description": "{0}（查询失败的描述信息）",
      "descriptionEn": "Query failed: {0}",
      "suggestion": "根据具体错误信息排查"
    },
    "020006": {
      "code": "020006",
      "description": "该批次的单据已经存在",
      "descriptionEn": "Batch document already exists",
      "suggestion": "更换批次号或查询已存在的单据"
    },
    "020300": {
      "code": "020300",
      "description": "对方账号不合法",
      "descriptionEn": "Invalid counterparty account number",
      "suggestion": "核实收款账号格式和有效性"
    },
    "020301": {
      "code": "020301",
      "description": "未查询到开户行信息",
      "descriptionEn": "Bank branch information not found",
      "suggestion": "检查联行号是否正确"
    },
    "020302": {
      "code": "020302",
      "description": "查询无记录",
      "descriptionEn": "No records found",
      "suggestion": "确认查询条件，检查数据是否存在"
    },
    "020303": {
      "code": "020303",
      "description": "支付结果查询参数异常",
      "descriptionEn": "Payment query parameter exception",
      "suggestion": "检查查询参数格式"
    },
    "020304": {
      "code": "020304",
      "description": "联行号校验错误或在系统中不存在",
      "descriptionEn": "Bank code validation failed or does not exist",
      "suggestion": "联系管理员检查联行号配置"
    },
    "020306": {
      "code": "020306",
      "description": "付方日期必须大于等于当前日期",
      "descriptionEn": "Payment date must be greater than or equal to current date",
      "suggestion": "修改应付日期为当前日期或之后"
    },
    "020311": {
      "code": "020311",
      "description": "业务类型不合法",
      "descriptionEn": "Invalid business type",
      "suggestion": "检查业务类型代码是否在允许范围内"
    },
    "029999": {
      "code": "029999",
      "description": "{0}",
      "descriptionEn": "Business error: {0}",
      "suggestion": "根据具体错误信息处理"
    },
    "030045": {
      "code": "030045",
      "description": "资金下拨支付中——他行下拨一定该状态",
      "descriptionEn": "Fund allocation in progress",
      "suggestion": "等待支付完成，稍后查询结果"
    },
    "999990": {
      "code": "999990",
      "description": "非法参数异常，%s(具体的参数校验异常信息)",
      "descriptionEn": "Invalid parameter exception: %s",
      "suggestion": "根据具体参数错误信息修正报文"
    }
  },
  "prefixCodes": {
    "0000000000000000": {
      "code": "0000000000000000",
      "description": "请求成功（统一前置系统响应码）",
      "descriptionEn": "Request successful (Unified Front-end Platform response code)",
      "suggestion": "可继续解析 encData 中的业务报文"
    }
  },
  "appendix": {
    "currency": {
      "156": "人民币"
    },
    "transactionType": {
      "1": "对公支付",
      "2": "对私支付",
      "3": "工资代发"
    },
    "businessType": {
      "001": "贷款",
      "002": "手续费",
      "003": "押金",
      "004": "保证金",
      "005": "质保金",
      "006": "押汇",
      "007": "客户费用",
      "008": "押汇还款",
      "009": "其他",
      "010": "材料款",
      "011": "设备款",
      "012": "工资"
    },
    "paymentMethod": {
      "1": "账户",
      "2": "票据"
    },
    "networkFlag": {
      "0": "非直连",
      "1": "直连",
      "2": "SWIFT"
    },
    "accountAttr": {
      "ZH0001": "基本存款账户",
      "ZH0002": "一般存款账户",
      "ZH0003": "专用存款账户",
      "ZH0004": "临时存款账户"
    },
    "accountStatus": {
      "1": "未启用",
      "2": "正常",
      "3": "已销户",
      "4": "待销户",
      "5": "止付"
    },
    "dcFlag": {
      "0": "借",
      "1": "贷"
    },
    "privatePaymentPurpose": {
      "100": "日常支出",
      "101": "还款",
      "102": "奖金",
      "103": "缴费",
      "104": "医保",
      "105": "保险",
      "106": "报销",
      "107": "补偿",
      "108": "补贴",
      "109": "差旅",
      "110": "冲正",
      "111": "代付",
      "112": "电费",
      "113": "短信",
      "114": "房租",
      "115": "分红",
      "116": "个税",
      "117": "公积金",
      "118": "理财",
      "119": "社保",
      "120": "水费",
      "121": "税款",
      "122": "学费",
      "123": "收费",
      "124": "捐赠",
      "125": "医药费",
      "126": "福利",
      "127": "保费",
      "128": "过节",
      "129": "劳保",
      "130": "通讯费",
      "131": "安置",
      "132": "权益",
      "133": "劳务",
      "134": "低保",
      "135": "过渡",
      "136": "退款",
      "137": "借款",
      "138": "贷款",
      "139": "年金",
      "140": "办公费",
      "141": "报刊费",
      "142": "餐费",
      "143": "会议费",
      "144": "工程款",
      "145": "律师费",
      "146": "垫付资金",
      "147": "柜台销售结算款",
      "148": "网上交易款",
      "149": "制片费",
      "150": "奖助学金",
      "151": "案件款",
      "152": "租赁费",
      "153": "转款",
      "154": "贷款",
      "155": "采购款",
      "156": "拨款",
      "157": "运费",
      "158": "服务费",
      "159": "广告费",
      "160": "物业费",
      "161": "检测费",
      "162": "佣金",
      "163": "租金",
      "164": "违约金",
      "165": "保证金",
      "166": "退休金",
      "167": "维修费",
      "168": "公共事业费",
      "169": "高温费",
      "170": "加班费",
      "171": "过节费",
      "172": "年终奖",
      "173": "住房补贴",
      "174": "交通补贴",
      "175": "利息",
      "176": "工资",
      "177": "其他合法款项"
    }
  }
}


path: validator.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
邮储银行财资管理系统报文验证工具
支持：JSON Schema 校验、字段类型/长度/取值范围验证
"""

import json
import re
from typing import Dict, Any, List, Optional, Tuple
from datetime import datetime


class ValidationError(Exception):
    """验证异常"""
    def __init__(self, field: str, message: str, value: Any = None):
        self.field = field
        self.message = message
        self.value = value
        super().__init__(f"[{field}] {message}: {value}")


class PSBCValidator:
    """邮储财资报文验证器"""
    
    # 数据类型正则
    PATTERNS = {
        "AN": r"^[A-Za-z0-9]*$",           # 字母数字
        "A": r"^[A-Za-z]*$",                # 字母
        "N": r"^\d*$",                      # 数字
        "ANS": r"^[A-Za-z0-9\u4e00-\u9fa5]*$",  # 字母数字汉字
        "D": r"^\d+(\.\d+)?$",              # 浮点数
    }
    
    def __init__(self, apis_config: str = "apis.json"):
        """
        初始化验证器
        
        Args:
            apis_config: API 配置文件路径
        """
        with open(apis_config, "r", encoding="utf-8") as f:
            self.apis = json.load(f)
        self.api_map = {api["txCode"]: api for api in self.apis["apis"]}
    
    def validate(self, tx_code: str, data: Dict[str, Any], 
                 direction: str = "request") -> List[str]:
        """
        验证报文
        
        Args:
            tx_code: 交易码
            data: 报文数据
            direction: 方向（request/response）
        
        Returns:
            错误列表，空列表表示验证通过
        """
        errors = []
        
        if tx_code not in self.api_map:
            errors.append(f"未知的交易码：{tx_code}")
            return errors
        
        api = self.api_map[tx_code]
        spec = api.get(direction, {})
        fields = spec.get("businessFields", [])
        
        for field_spec in fields:
            field_id = field_spec["fieldId"]
            field_name = field_spec["name"]
            field_type = field_spec["type"]
            required = field_spec.get("required", False)
            
            value = data.get(field_id)
            
            # 必填性检查
            if required and (value is None or value == ""):
                errors.append(f"[{field_id}] 必填字段缺失：{field_name}")
                continue
            
            if value is None:
                continue
            
            # 类型验证
            try:
                self._validate_field(field_id, field_name, field_type, value, field_spec)
            except ValidationError as e:
                errors.append(str(e))
            
            # 枚举值检查
            if "enum" in field_spec:
                enum_values = field_spec["enum"]
                if isinstance(enum_values, dict):
                    if str(value) not in enum_values:
                        errors.append(f"[{field_id}] 值不在允许范围内：{value}，允许值：{list(enum_values.keys())}")
                elif isinstance(enum_values, list):
                    if value not in enum_values:
                        errors.append(f"[{field_id}] 值不在允许范围内：{value}，允许值：{enum_values}")
        
        return errors
    
    def _validate_field(self, field_id: str, field_name: str, 
                        field_type: str, value: Any, spec: Dict) -> None:
        """验证单个字段"""
        
        # 解析类型定义
        type_match = re.match(r"^([A-Z]+)(\.\.)?(\d+)(?:,(\d+))?$", field_type)
        if not type_match:
            # 特殊类型处理
            if field_type in ["JsonArray", "JsonObject", "list", "CLOB", "Int", "INT", "double", "long"]:
                return  # 复杂类型暂不验证
            return
        
        base_type = type_match.group(1)
        is_variable = type_match.group(2) == ".."
        max_length = int(type_match.group(3))
        precision = int(type_match.group(4)) if type_match.group(4) else None
        
        # 长度检查
        str_value = str(value)
        if not is_variable:
            # 定长
            if len(str_value) != max_length:
                raise ValidationError(field_id, f"长度应为{max_length}位", f"实际{len(str_value)}位")
        else:
            # 变长
            if len(str_value) > max_length:
                raise ValidationError(field_id, f"长度不能超过{max_length}位", f"实际{len(str_value)}位")
        
        # 类型检查
        if base_type == "N" and precision is None:
            # 整数
            if not re.match(r"^-?\d+$", str_value):
                raise ValidationError(field_id, "应为整数", value)
        
        elif base_type == "D" and precision:
            # 浮点数 D(m,n)
            if not re.match(r"^-?\d+(\.\d+)?$", str_value):
                raise ValidationError(field_id, "应为浮点数", value)
            if "." in str_value:
                decimal_part = str_value.split(".")[1]
                if len(decimal_part) > precision:
                    raise ValidationError(field_id, f"小数位数不能超过{precision}位", f"实际{len(decimal_part)}位")
        
        elif base_type == "AN":
            if not re.match(r"^[A-Za-z0-9]*$", str_value):
                raise ValidationError(field_id, "应只包含字母和数字", value)
        
        elif base_type == "ANS":
            if not re.match(r"^[A-Za-z0-9\u4e00-\u9fa5]*$", str_value):
                raise ValidationError(field_id, "应只包含字母、数字和汉字", value)
        
        # 日期格式检查
        if "Date" in field_name or "Time" in field_name or field_id in ["sendTime", "txStartDate", "txEndDate"]:
            self._validate_date(field_id, str_value, spec)
        
        # 金额检查
        if "Amt" in field_id or "Bal" in field_id or "Amount" in field_id:
            self._validate_amount(field_id, value)
    
    def _validate_date(self, field_id: str, value: str, spec: Dict) -> None:
        """验证日期格式"""
        formats = [
            ("%Y%m%d%H%M%S", "yyyyMMddHHmmss"),
            ("%Y%m%d%H%M%S%f", "yyyyMMddHHmmssSSS"),
            ("%Y%m%d", "yyyyMMdd"),
            ("%Y-%m-%d", "yyyy-MM-dd"),
            ("%H:%M", "HH:mm"),
        ]
        
        for fmt, desc in formats:
            try:
                datetime.strptime(value, fmt)
                return
            except ValueError:
                continue
        
        raise ValidationError(field_id, f"日期格式错误，应为 {desc}", value)
    
    def _validate_amount(self, field_id: str, value: Any) -> None:
        """验证金额"""
        try:
            amount = float(value)
            if amount < 0:
                raise ValidationError(field_id, "金额不能为负数", value)
            if amount > 999999999999999999.99:
                raise ValidationError(field_id, "金额超出最大范围", value)
        except (ValueError, TypeError):
            raise ValidationError(field_id, "金额应为数字", value)
    
    def validate_tx_comm(self, data: Dict[str, Any], security_level: str = "10") -> List[str]:
        """
        验证公共请求参数 (txComm)
        
        Args:
            data: 请求数据
            security_level: 安全级别
        
        Returns:
            错误列表
        """
        errors = []
        tx_comm = data.get("txComm", {})
        
        # 必填字段
        required_fields = ["sysTrackNo", "reqSysCode", "txCode", "txTime", "securityLevel", "sign", "userCertSN"]
        for field in required_fields:
            if field not in tx_comm:
                errors.append(f"txComm 缺少必填字段：{field}")
        
        # 系统跟踪号长度检查
        sys_track_no = tx_comm.get("sysTrackNo", "")
        if len(sys_track_no) != 32:
            errors.append(f"系统跟踪号应为 32 位，实际{len(sys_track_no)}位")
        
        # 接入系统代码长度检查
        req_sys_code = tx_comm.get("reqSysCode", "")
        if len(req_sys_code) != 12:
            errors.append(f"接入系统代码应为 12 位，实际{len(req_sys_code)}位")
        
        # 交易码长度检查
        tx_code = tx_comm.get("txCode", "")
        if len(tx_code) != 6:
            errors.append(f"交易码应为 6 位，实际{len(tx_code)}位")
        
        # 请求时间格式检查
        tx_time = tx_comm.get("txTime", "")
        if not re.match(r"^\d{17}$", tx_time):
            errors.append(f"请求时间格式错误，应为 yyyyMMddHHmmssSSS")
        
        # 安全级别检查
        sec_level = tx_comm.get("securityLevel", "")
        if sec_level not in ["10", "15"]:
            errors.append(f"安全级别应为 10 或 15，实际{sec_level}")
        
        # 安全级别 10 的特殊字段
        if security_level == "10":
            if "bankCertSN" not in tx_comm:
                errors.append("安全级别 10 时 bankCertSN 为必填")
            if "encData" not in tx_comm:
                errors.append("安全级别 10 时 encData 为必填")
            if "encKey" not in tx_comm:
                errors.append("安全级别 10 时 encKey 为必填")
        
        return errors


def interactive_validate():
    """交互式验证模式"""
    print("=" * 60)
    print("邮储银行财资管理系统 - 报文验证工具")
    print("=" * 60)
    
    validator = PSBCValidator()
    
    # 列出所有接口
    print("\n可用接口列表:")
    for api in validator.apis["apis"]:
        print(f"  {api['txCode']} - {api['name']}")
    
    print("\n请输入交易码（或输入 'q' 退出）: ", end="")
    tx_code = input().strip()
    
    if tx_code.lower() == 'q':
        return
    
    if tx_code not in validator.api_map:
        print(f"错误：未知的交易码 {tx_code}")
        return
    
    print("\n请输入请求报文 JSON（支持多行，输入空行结束）:")
    lines = []
    while True:
        line = input()
        if line.strip() == "":
            break
        lines.append(line)
    
    try:
        data = json.loads("\n".join(lines))
    except json.JSONDecodeError as e:
        print(f"JSON 解析错误：{e}")
        return
    
    # 验证
    print("\n验证结果:")
    errors = validator.validate(tx_code, data, "request")
    
    # 验证 txComm
    if "txComm" in data:
        tx_comm_errors = validator.validate_tx_comm(data)
        errors.extend(tx_comm_errors)
    
    if errors:
        print(f"\n❌ 发现 {len(errors)} 个错误:")
        for i, error in enumerate(errors, 1):
            print(f"  {i}. {error}")
    else:
        print("\n✅ 验证通过！报文格式正确。")


if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1 and sys.argv[1] == "--interactive":
        interactive_validate()
    else:
        # 示例验证
        validator = PSBCValidator()
        
        # 测试用例：余额查询
        test_data = {
            "txCode": "100016",
            "tenantID": "eam_tenant_a_0001",
            "sendTime": "20250318163811",
            "srcSysId": "140001",
            "bankAccno": "951011013000006323"
        }
        
        errors = validator.validate("601118", test_data, "request")
        
        if errors:
            print("验证失败:")
            for error in errors:
                print(f"  - {error}")
        else:
            print("✅ 验证通过！")


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)