diff --git a/docs/一站式平台对接-接口文档.md b/docs/一站式平台对接-接口文档.md new file mode 100644 index 0000000..7834631 --- /dev/null +++ b/docs/一站式平台对接-接口文档.md @@ -0,0 +1,225 @@ +# 汇吉采 ↔ 一站式代理平台 对接接口文档 + +> 版本:v1.0 +> 适用:汇吉采标书购买平台(下称「官方网」)与一站式代理平台(下称「一站式」)之间双向数据互通。 +> 相关:鉴权规则见 `docs/一站式平台推送-接口鉴权规范.md`;建表见 `src/main/resources/sql/hjc_init.sql`。 + +--- + +## 0. 概述 + +| 方向 | 触发时机 | 接口 | 位置 | +|---|---|---|---| +| **入向**(一站式 → 官方网) | 项目流程走到「发布中标公告」时 | `POST {官方网}/api/hjc/push/project` | 官方网接收标书/中标公告数据,按 `projectNo` upsert 落库 | +| **出向**(官方网 → 一站式) | C 端购买标书**支付成功后** / 退款时 | `POST {一站式}/api/biz/createPurchaseDetails` | 官方网把购买记录/订单信息推送到一站式落盘 | + +**共享凭证**(双方约定,不通过网络传输明文密钥): + +| 参数 | 值 | 说明 | +|---|---|---| +| `appKey` | `HJC_Official_Website` | 调用方标识,明文放请求头 | +| `password` | `vQ8$kR3#mW6@xP2!nF` | 签名密钥(Secret),仅参与签名,**不进报文** | + +--- + +## 1. 鉴权规范(双向通用) + +请求头携带: + +| Header | 示例 | 说明 | +|---|---|---| +| `appKey` | `HJC_Official_Website` | 双方一致 | +| `timestamp` | `202604080103` | 格式 `yyyyMMddHHmm`(24 小时,北京时区,精确到分) | +| `sign` | `9f7c…(32位小写hex)` | 签名值 | + +**签名算法**:`sign = MD5(appKey + password + timestamp)`(无分隔符,小写 32 位)。 + +**校验顺序**(任一步不过即拒绝,建议返回 `401`/`400`): +1. `appKey == HJC_Official_Website` +2. `timestamp` 与接收方服务器当前时间差 **≤ ±10 分钟**(防重放) +3. 用约定 `password` 重算 `MD5(appKey+password+timestamp)`,与 `sign` 不区分大小写比对 + +> 完整规则、示例(Python/Java)与「明文 password 头」兼容退路见 `docs/一站式平台推送-接口鉴权规范.md`。 + +--- + +## 2. 入向接口:一站式 → 官方网(接收标书/中标公告) + +### 2.1 接口地址 + +``` +POST {官方网BaseUrl}/api/hjc/push/project +``` + +> 官方网侧需在 SecurityConfig 放行该路径(已内置,`/api/hjc/push/project` 为公开接口,鉴权由本接口的 appKey/sign 自行校验)。 + +### 2.2 请求头 + +| Header | 值 | +|---|---| +| `appKey` | `HJC_Official_Website` | +| `timestamp` | `yyyyMMddHHmm` | +| `sign` | `MD5(appKey+password+timestamp)` | +| `tenantId` | `10626`(官方网当前固定租户,**必传**,作为落库租户) | +| `Content-Type` | `application/json` | + +### 2.3 请求体字段 + +| 字段 | 类型 | 必填 | 说明 | 对应内部字段 | +|---|---|---|---|---| +| `projectNo` | string | ✔ | 项目编号 | `project_no`(upsert 主键) | +| `projectName` | string | ✔ | 项目名称 | `project_name` | +| `tenderPrice` | number | | 标书价格/信息服务费 | `tender_price` | +| `files` | string(JSON/逗号分隔) | | 标书附件 | `tender_file` | +| `tenderOnsaleTime` | string | | 开售时间 `yyyy-MM-dd HH:mm:ss` | `onsale_time` | +| `tenderOffsaleTime` | string | | 停售时间 `yyyy-MM-dd HH:mm:ss` | `offsale_time` | +| `bulletinName` | string | | 公告标题 | `bulletin_title` | +| `customerName` | string | | 招标人 | `tenderer` | +| `supplierName` | string | | 中标公司 | `winner_supplier` | +| `bidAmount` | number | | 中标金额 | `bid_amount` | +| `content` | string | | 公告正文 | `bulletin_content` | +| `fileList` | string(JSON/逗号分隔) | | 公告附件 | `bulletin_file_list` | +| `needSellTender` | number | | 是否卖标书 0/1 | `need_sell` | +| `sellingMethod` | number | | 售卖方式 1公司财务 2公众号 3交易中心 4政采云 | `selling_method` | + +### 2.4 响应 + +```json +{ + "code": 0, + "message": "接收成功", + "data": { "id": 12, "projectNo": "ZB-2026-0001", "projectName": "xxx", "dataSource": "PUSH", "status": 1 } +} +``` +- `code == 0` 成功;`code != 0` 失败(如 `message=鉴权失败` / `租户ID不能为空` / `项目编号不能为空`)。 + +### 2.5 落库规则(官方网侧) + +- 按 `projectNo` **upsert**:不存在则插入(`tenantId=请求头tenantId`,`status=1` 上架,`dataSource=PUSH`,`saleCount=0`);已存在则更新推送到字段。 +- 兼容性:`tenderOnsaleTime`/`tenderOffsaleTime` 支持 `yyyy-MM-dd HH:mm:ss`、`yyyyMMddHHmmss`、ISO 带 `T` 三种格式解析;解析失败置空。 +- 其余标书项目字段(分类、上下架、人工维护数据)仍可在管理后台手工调整。 + +--- + +## 3. 出向接口:官方网 → 一站式(推送购买记录/订单) + +### 3.1 接口地址 + +``` +POST {一站式BaseUrl}/api/biz/createPurchaseDetails +``` + +### 3.2 触发时机 + +- **支付成功后**(官方网 `PUT /api/hjc/order/mark-paid` 置已付)自动推送,请求状态 `status=PAID`。 +- **退款时**(官方网 `POST /api/hjc/order/refund` 置已退款)推送,请求状态 `status=REFUNDED`。 + +### 3.3 请求头 + +| Header | 值 | +|---|---| +| `appKey` | `HJC_Official_Website` | +| `timestamp` | `yyyyMMddHHmm` | +| `sign` | `MD5(appKey+password+timestamp)` | +| `Content-Type` | `application/json` | + +> `password` 不进请求头;`sign` 每次发送须按新的 `timestamp` 重新计算。 + +### 3.4 请求体字段 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `idempotencyKey` | string | 幂等键,`HJC_ORD_` | +| `orderNo` | string | 官方网订单号(一站式去重键) | +| `projectNo` | string | 项目编号 | +| `projectName` | string | 项目名称 | +| `tenderPrice` | number | 标书单价(信息服务费) | +| `quantity` | number | 购买数量 | +| `totalAmount` | number | 订单总额 | +| `buyer.enterpriseName` | string | 购买企业名称 | +| `buyer.creditCode` | string | 企业统一社会信用代码 | +| `buyer.contactName` | string | 经办人姓名 | +| `buyer.contactPhone` | string | 经办人手机号 | +| `buyer.contactEmail` | string | 经办人邮箱 | +| `paidAt` | string | 支付时间 `yyyy-MM-dd HH:mm:ss` | +| `payMethod` | string | `WECHAT_NATIVE` / `ALIPAY` | +| `status` | string | `PAID`(已支付)/ `REFUNDED`(已退款) | +| `invoiceStatus` | string | `NONE` / `APPLIED` / `ISSUED` | + +请求体示例: + +```json +{ + "idempotencyKey": "HJC_ORD_HJC2026080101ABCD", + "orderNo": "HJC2026080101ABCD", + "projectNo": "ZB-2026-0001", + "projectName": "xxx采购项目", + "tenderPrice": 500.00, + "quantity": 1, + "totalAmount": 500.00, + "buyer": { + "enterpriseName": "某某科技有限公司", + "creditCode": "91110XXXX", + "contactName": "张三", + "contactPhone": "13800000000", + "contactEmail": "a@b.com" + }, + "paidAt": "2026-08-01 10:30:00", + "payMethod": "WECHAT_NATIVE", + "status": "PAID", + "invoiceStatus": "NONE" +} +``` + +### 3.5 响应约定 + +- 一站式按自身 `API` 返回;官方网侧把 `HTTP 2xx` 视为成功,否则记入推送日志(`hjc_order_push_log`)待重试。 +- **幂等**:建议一站式以 `orderNo`/`idempotencyKey` 去重,同单重复提交返回成功、不重复插入。 + +### 3.6 重试策略(官方网侧) + +- 失败(非 2xx / 网络异常)写入 `push_status=2`,`next_retry_time = now + 30min`,`attempt_count + 1`。 +- 由定时任务 `retryPendingPush()` 扫描待推送/失败记录重发;重发时 `timestamp/sign` 重新计算。 + +--- + +## 4. 字段映射总表 + +| 一站式字段 | 含义 | 官方网内部字段 | +|---|---|---| +| `projectNo` | 项目编号 | `hjc_bid_project.project_no` | +| `projectName` | 项目名称 | `project_name` | +| `tenderPrice` | 标书价格 | `tender_price` | +| `files` | 标书附件 | `tender_file` | +| `tenderOnsaleTime` | 开售时间 | `onsale_time` | +| `tenderOffsaleTime` | 停售时间 | `offsale_time` | +| `bulletinName` | 公告标题 | `bulletin_title` | +| `customerName` | 招标人 | `tenderer` | +| `supplierName` | 中标公司 | `winner_supplier` | +| `bidAmount` | 中标金额 | `bid_amount` | +| `content` | 公告正文 | `bulletin_content` | +| `fileList` | 公告附件 | `bulletin_file_list` | +| `needSellTender` | 是否卖标书 | `need_sell` | +| `sellingMethod` | 售卖方式 | `selling_method` | + +--- + +## 5. 配置清单(待双方/部署方填写) + +| 项 | 值 | 位置 | +|---|---|---| +| 官方网 base-url | `http://<官方网域名>/api` | 一站式侧调用方配置 | +| 一站式 base-url | `http://<一站式域名>` | `mp-java/application.yml` → `hjc.one-stop.base-url` | +| createPurchaseDetails 路径 | `/api/biz/createPurchaseDetails` | `hjc.one-stop.create-purchase-details-path` | +| `appKey` | `HJC_Official_Website` | 双方约定 | +| `password` | `vQ8$kR3#mW6@xP2!nF` | 双方约定 | +| 租户 ID | `10626` | 官方网请求头 `tenantId` | + +--- + +## 6. 待确认事项 + +1. 出向 `sign` 拼接顺序(`appKey+password+timestamp`)与一站式既有算法是否一致;不一致请给出一站式现网签名规范。 +2. `timestamp` 格式、时区、±10min 窗口是否认可。 +3. `createPurchaseDetails` 字段名是否需与一站式现网入参完全一致(现按本表定义);如需调整字段名请给出一站式入参清单。 +4. 入向:一站式调用官方网 `/api/hjc/push/project` 时是否也按同一套 `appKey/timestamp/sign` 鉴权(本表默认对称鉴权)。