Learning
VOL. VII · NO. 108 · OTC Derivatives · 19 JUL 2026

Hcp Object Storage

OTC 衍生品 · 19 JUL 2026 · 10 min read · 1,884 words
· · ·

ODTS-58: HCP 对象存储 / S3——合同、报表和附件的持久化底座

目标读者:想理解 ODTS 的合同 PDF、报表文件、上传附件等二进制数据如何存储和访问的 BA/PM

相关文档:ODTS-55 (Doc Agent), ODTS-31 (合同运营), ODTS-17 (交易确认)

概述

HCP (Hitachi Content Platform) 是 ODTS 使用的对象存储系统,用于存储所有二进制文件——合同 PDF、报表输出、上传附件、证书文件等。

ODTS 的设计模式是:数据库存元数据,文件本体存 HCP。这意味着系统的数据库中只有 URL/路径引用,不存文件二进制内容。

HCP 本质

HCP 实现了 AWS S3 兼容 API。ODTS 通过 AWS Java SDK (com.amazonaws.services.s3) 的 AmazonS3Client 访问 HCP:

// HCPClientUtil.java  — HCP 连接初始化
AmazonS3ClientBuilder.standard()
  .withClientConfiguration(clientConfig)
  .withEndpointConfiguration(new EndpointConfiguration(endpoint, ""))
  .withCredentials(new AWSStaticCredentialsProvider(
      new BasicAWSCredentials(accessKey, secretKey)))
  .build();

连接配置

配置项来源说明
endPointConfigPropHCP 服务端地址(HTTP)
accessKeyConfigPropAccess Key
secretKeyConfigPropSecret Key
nameSpaceConfigProp默认 Bucket(namespace)
ofanameSpaceConfigPropOFA 专用 Bucket

协议:HTTP(不是 HTTPS),签名算法:S3SignerType(S3 v2 签名)。

两个 Bucket

ODTS 配置了两个 HCP namespace:

  1. nameSpace(主桶):交易确认书、监管报送文件、上传附件、证书文件等
  2. ofanameSpace(OFA 桶):OFA(中金运营)相关的报表附件,通过独立的 getOfaS3Client() 访问

数据模型

HCP 的存储路径按业务目录组织,使用 {bucket}/{prefix}/{category}/{date}/{detail} 模式:

主桶:
  {nameSpace}/confirm/SNB202507001.pdf                          ← 交易确认书
  {nameSpace}/report/SAC_20250716.xml                            ← SAC 报送文件
  {nameSpace}/upload/CLT0001_CSA_V2.pdf                          ← 客户上传的 CSA
  {nameSpace}/cert/import/SNB2025070001.pfx                      ← 证书导入文件
  {nameSpace}/data/godtsUatFile/geminiUpload/20250716/file.xlsx  ← Gemini 上传文件

OFA 桶:
  {ofaNameSpace}/.../option-attachment-20250716-010101-eds.csv   ← 期权附件清单
  {ofaNameSpace}/.../confirm-merged.pdf                           ← 合并确认书

OFA 报送路径规范

期权附件向 OFA 监管报送时遵循固定路径格式,在 UploadOptionAtts2S3Action 中构建:

CSV 路径:TEST/cicc/gm/ofa/data/regulator/interotc/{tradeDate}/{deskKey}/
  示例:TEST/cicc/gm/ofa/data/regulator/interotc/2025-07-16/eds/
  文件名:option-attachment-{yyyymmdd}-{deskId}-{deskKey}.csv

PDF 路径:TEST/cicc/gm/ofa/data/attachment/option/{tradeDate}/{deskId}/
  示例:TEST/cicc/gm/ofa/data/attachment/option/2025-07-16/010101/
  文件名:{contractId}-{confirmNo}.{ext}

deskId → deskKey 映射(DESKMAP):

deskId(柜台代码)deskKey(报送渠道)
010101eds
010201, 010202, 010301, 010401, 010601, 010701, 010801pb
030101, 030102wsc

HCPUtil —— 操作封装

HCPUtil 提供所有 HCP 操作的单例封装:

上传

方法输入用途
uploadFile(directory, uploadFilePath)目录 + 本地文件路径从本地文件上传
uploadFile(file, path, fileName)File + 路径 + 文件名直接传 File 对象
uploadFileStream(key, inputStream)S3 key + InputStream从流上传(通用方式)
uploadFileDocStream(key, doc)S3 key + XWPFDocument直接传 Word 文档对象
uploadFilePbConf(to, atomBean, doc)路径 + 合同信息 + Word 文档交易确认书专用上传
uploadOfaFileStream(key, inputStream)S3 key + InputStream上传到 OFA 桶

下载

方法输入输出
downloadFile(key, localFile)S3 key + 本地路径下载到本地文件
downloadFileByte(key)S3 keybyte[]
downloadFileToLocal(s3FilePath, localPath)S3 key + 本地路径boolean

管理和查询

方法说明
deleteByObject(filePath)删除单个文件
deleteByFoldername(folderPath)删除目录下所有文件(逐个删除)
copyObject(sourceKey, destKey)同一 bucket 内复制
rename(key, newKey)重命名(copy + delete)
doesObjectExist(filePath)检查文件是否存在
listFiles(key)列出目录下文件列表
listObjectsV2(prefix)按前缀列出对象(含 S3ObjectSummary)
getFileByFolderName(folderPath)获取目录下所有文件 key
getFileNamesByFolderPath(folderPath)获取目录下文件名列表(不含路径)

FileInfoHCP —— 文件迁移记录

FileInfoHCP.java
  contractFileId  — 合同文件 ID
  srcKey          — 源路径 (HCP)
  tarKey          — 目标路径 (HCP)
  memo            — 备注

用于记录文件从一个 HCP 路径迁移到另一个路径的信息(如 Doc Agent 转换后存储)。

S3OperationController —— REST API

系统通过 S3OperationController 将 HCP 操作暴露为 HTTP 接口:

路由方法说明
GET /s3/listObjectsV2listObjectsV2(prefix)按前缀列出文件
GET /s3/objectgetObject(key)下载文件(二进制流)
GET /s3/deleteByKeydeleteByKey(key)删除文件
GET /s3/saveReportToDisksaveReportToDisk(date, desk, type)保存 EOD 报表到 HCP
GET /s3/saveReportToDiskV2saveReportToDiskV2(date, desk, type)V2 版本
GET /s3/saveStandardReportToDisksaveStandardReportToDisk(date, desk, type)标准报表
GET /s3/downloadFileToLocaldownloadFileToLocal(s3Path, localPath)下载到本地
POST /s3/geminiUploadFilegeminiUploadFile(MultipartFile)Gemini 上传文件

文件下载流程(通过 S3OperationController)

用户请求下载 → GET /s3/object?key=confirm/SNB202507001.pdf


HCPUtil.downloadFileByte(key) → byte[]


HTTP Response:
  Content-Type: application/octet-stream
  Content-Disposition: attachment;filename=SNB202507001.pdf
  Content-Length: {bytes.length}


浏览器下载文件

Gemini 上传流程

外部系统上传文件 → POST /s3/geminiUploadFile (MultipartFile)


S3 路径: /data/godtsUatFile/geminiUpload/{yyyyMMdd}/{originalFileName}


HCPUtil.uploadFileStream(key, byteArrayInputStream)


返回: { "key": "...", "fileName": "..." }

文件类型清单

类别(category)文件内容上传者访问者
confirm交易确认书 PDF/WordDoc Agent运营、客户
upload客户/运营上传的合同扫描件运营运营、法务
report监管报送文件/报表eds-web-app运营
cert数字证书文件运营Cert App
margin保证金对账单eds-web-app运营、客户
template上传的模板文件管理员Doc Agent

元数据存储

ODTS 使用 java.util.HashMap<String, String> 存储文件的元数据:

KeyValue 示例说明
fileTypeCONFIRMATION文件类型
hcpUrl/hcp/ns/odts/confirm/SNB202507001.pdfHCP 存储路径
uploadTime2025-07-16T10:30:00上传时间
operatoroperator01上传人
fileSize245678文件大小(字节)
checksumsha256:abc123...文件校验和

这种”Map 即 schema”的模式在 ODTS 中很常见——灵活但无编译时类型检查。

为什么用 HCP 而不是数据库 BLOB?

维度数据库 BLOBHCP 对象存储
存储成本高(数据库存储贵)低(对象存储≈1/10 成本)
读写性能大文件影响数据库查询性能独立扩展,不影响数据库
备份恢复数据库备份包含大量二进制,又大又慢独立的备份策略
版本控制需要自己实现HCP 原生支持
CDN 分发不支持可通过网关做 CDN
API 兼容JDBCS3 兼容 API

代码架构评价

合理的设计

  • S3 兼容 API:HCP 暴露标准 S3 接口,如果未来要迁移到 AWS S3 或 MinIO,只需要改 endpoint
  • 单例模式HCPUtilHCPClientUtil 使用单例,避免重复创建 S3 客户端
  • HTTP 协议:内网环境使用 HTTP 减少 TLS 开销,合理
  • 操作覆盖完整:CRUD + 列表 + 复制 + 重命名 + 存在性检查

值得关注的问题

  1. S3 v2 签名S3SignerType 是旧的 v2 签名算法,部分新版本的 HCP 或 S3 兼容服务可能不再支持
  2. HTTP 明文:内网 HTTP 本身问题不大,但如果数据需要跨境传输或跨机房,缺乏加密
  3. 单点失败:HCP 客户端是进程内单例,重启前不会重连;如果 HCP 服务端切换 IP,需要重启应用
  4. 大文件下载downloadFileByte 将整个文件读入内存 byte[],大文件可能导致 OOM
  5. 没有分片上传uploadFileStream 没有使用 S3 的分片上传(multipart upload),大文件传输效率低
  6. 文件夹删除逐条执行deleteByFoldername 逐个调用 deleteByObject 删除文件,目录下有大量文件时性能很差

这些问题对业务的真实代价

技术问题触发场景业务后果量级
单点失败(#3)HCP 服务端切换 IP / 重启ODTS 取不到任何合同 PDF、报表、附件;且要恢复得重启整个 eds-web-app 应用(连带影响所有交易功能,不仅是文件)🔴 全局中断
HTTP 明文(#2)合同/证书/CSA 文件在内网传输文件内容是客户交易确认书、数字证书私钥引用——明文传输 + 47 文档里那些”已提交到 Git 的密钥”,组合风险:内部抓包即可拿到敏感文件🟠 合规风险
大文件 OOM(#4)运营导出合并确认书 / 大报表单次下载触发 JVM 内存溢出 → 该应用实例崩溃 → 该节点交易功能停摆🟠 稳定性
无分片上传(#5)上传大批量期权附件(OFA 报送)传输慢、超时重试,报送窗口内可能传不完 → 监管报送延迟(见 38)🟡 效率
v2 签名(#1)未来 HCP 升级 / 迁移 MinIO签名不兼容 → 全量文件读写失败 → 必须改代码重新发版才能恢复🟠 迁移风险

PM/BA 启示:

  • HCP 不是”一个存储”那么简单。它是合同法律效力(确认书)、监管证据(报送 XML)、运营凭证(附件)的唯一持久化底座(见 52 的”HCP 挂掉”爆炸半径)。它的可用性要直接进入 ODTS 的 SLA,而不是当成”基础设施自己管”。
  • “数据库存元数据、文件存 HCP”这个设计本身是对的(成本低、不拖慢 DB),但它制造了一个隐性的强耦合:HCP 不可达 = 文件全部不可达,而元数据还在 Oracle 里——运营会看到”记录存在但文件打不开”的怪异状态,排查成本极高。
  • 与 47 文档同源:内网 ≠ 安全。HCP 走 HTTP 明文 + 密钥曾经明文入库,合在一起意味着”能进内网的人”理论上能拿到客户合同原文。这是审计口径下的高风险组合。

关键文件

组件路径
HCP 操作封装eds-web-app/src/.../utils/HCPUtil.java
HCP 客户端eds-utility/src/.../utils/HCPClientUtil.java
REST 控制器eds-web-app/src/.../edsBoot/controller/S3OperationController.java
文件迁移模型eds-web-app/src/.../action/dynamic/FileInfoHCP.java
期权附件上传eds-web-app/src/.../action/dynamic/UploadOptionAtts2S3Action.java
交易确认书上传eds-web-app/src/.../action/dynamic/UploadOptionConfirmationAttsCSV2S3Action.java
OFA 报送保存eds-web-app/src/.../action/dynamic/SaveFiDttNReportToS3Action.java
保证金文件类型eds-web-app/src/.../edsBoot/model/margin/enums/MarginS3FileType.java
S3 文件摘要eds-web-app/src/.../edsBoot/service/margin/client/FileS3SummaryVo.java
HCP 测试eds-web-app/test/.../utils/HCPUtilTest.java
SAC 报送 HCPsac-report/src/.../utils/HCPUtil.java
参数选项 S3 JSoss-image/server/s3.js