2cd60270b9
hjc 包的进展(按 AGENTS.md 约定,改动集中在 hjc 包内):
- HjcWechatController 新增三接口:
- GET /api/hjc/wechat/readiness 管理员自检,逐项报告小程序/公众号/支付/serverUrl
是否配好、支付 appId 与小程序 appId 是否一致;?probe=true 实调微信验证 appSecret
(失败只记日志、不抛异常)。appId 与商户号在 detail 里打码,appSecret 永不输出。
- GET /api/hjc/wechat/mp-appid 只回 appId,供 H5 渲染开放标签(不含密钥)。
- POST /api/hjc/wechat/mp-login uni.login 的 code 换小程序 openid/unionid;匿名放行,
租户取 HjcAuthProperties 的配置值而非可伪造的请求头。
配套 HjcWechatReadinessUtil(含单测)与 HjcWechatController 的两套配置读取:
公众号 cache{t}:setting:wx-official、小程序 mp-weixin:{t} → setting:mp-weixin:{t} →
跨库回源 gxwebsoft_core.sys_setting。
- HjcBannerController / Service / ServiceImpl / Mapper(+XML) / HjcBannerVo:只读
GET /api/hjc/banner/list,按租户 + position + 启用状态 + 生效时间窗口过滤 CMS
轮播组并扁平化;租户隔离交给 MyBatis-Plus 租户拦截器,不改 cms 及其他项目代码。
配套 HjcBannerApiTest(最小上下文 MockMvc,含不串租户与位置过滤)。
- 一站式出向推送:CreatePurchaseDetails 补退款字段,HjcOrder 补 refund_time /
refund_reason(配套 hjc_order_add_refund.sql 与 hjc_init.sql),HjcBizServiceImpl
在 REFUNDED 时把退款时间与原因一并推送;配套推送报文单测。
- 共享文件 SecurityConfig.java 仅追加一行:匿名放行 /api/hjc/wechat/mp-login
(注册页证件上传与 OCR 早先已放行)。这是本包唯一改到 common 的地方。
- 文档:CONTEXT.md、docs/一站式平台对接-接口文档.md。
**不含** scripts/hjc_test_push_out.py:该联调探针脚本内含与 HjcOneStopAuthUtil.java
相同的 APP_KEY / PASSWORD 明文,按此前排查记录「不得提交」处理,保留在工作区未跟踪。
261 lines
11 KiB
Markdown
261 lines
11 KiB
Markdown
# 汇吉采 ↔ 一站式代理平台 对接接口文档
|
||
|
||
> 版本: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`,**必须**携带 `refundTime`(退款时间)与 `refundReason`(退款原因)。
|
||
- `refundTime`:官方网记录退款动作发生时间(服务器时间),落库 `hjc_order.refund_time`。
|
||
- `refundReason`:后台退款弹窗填写(≤200 字),落库 `hjc_order.refund_reason`;未填写时取默认「管理员操作退款」。
|
||
- 推送失败进重试队列后,重试报文的退款时间/原因按订单落库值重建,不会丢失。
|
||
|
||
### 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>` |
|
||
| `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`(已退款) |
|
||
| `refundTime` | string | **退款时必传**,退款时间 `yyyy-MM-dd HH:mm:ss`(`status=REFUNDED` 时推送,其余状态不出现在报文中) |
|
||
| `refundReason` | string | **退款时必传**,退款原因(≤255 字符;后台未填写时取默认「管理员操作退款」) |
|
||
| `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"
|
||
}
|
||
```
|
||
|
||
退款推送请求体示例(`status=REFUNDED`,多出 `refundTime`/`refundReason`):
|
||
|
||
```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": "REFUNDED",
|
||
"refundTime": "2026-08-03 09:15:00",
|
||
"refundReason": "项目终止,客户申请退款",
|
||
"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` |
|
||
| `refundTime` | 退款时间(出向) | `hjc_order.refund_time` |
|
||
| `refundReason` | 退款原因(出向) | `hjc_order.refund_reason` |
|
||
|
||
---
|
||
|
||
## 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` 鉴权(本表默认对称鉴权)。
|
||
5. 退款字段名 `refundTime` / `refundReason` 与一站式现网入参是否一致;格式默认 `yyyy-MM-dd HH:mm:ss`,如一站式要求其他命名或时间格式请同步调整 `CreatePurchaseDetails`。
|