# thirdparty-callback-hub **Repository Path**: ismartyx/thirdparty-callback-hub ## Basic Information - **Project Name**: thirdparty-callback-hub - **Description**: 一个可配置、可扩展的 Spring Boot 系统,用于统一处理第三方接口的异步结果: 支持回调:第三方主动推送结果到 /api/callback/{partnerCode}。 不支持回调:系统按配置自动轮询第三方状态接口,直到拿到终态或超时。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-12 - **Last Updated**: 2026-08-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Third-Party Callback Hub 一个可配置、可扩展的 Spring Boot 系统,用于统一处理第三方接口的异步结果: - **支持回调**:第三方主动推送结果到 `/api/callback/{partnerCode}`。 - **不支持回调**:系统按配置自动轮询第三方状态接口,直到拿到终态或超时。 ## 核心概念 - **PartnerConfig**:每个第三方渠道的配置(提交接口、状态查询接口、轮询间隔、字段解析、签名方式等)。 - **OutboundTask**:一次对外提交的任务,记录 `PENDING → SUBMITTED/PROCESSING → SUCCESS/FAILED/TIMEOUT` 生命周期。 - **CallbackRecord**:每次收到的回调原始内容,用于对账与排障。 - **PartnerClient**:渠道策略接口。默认实现 `DefaultPartnerClient` 通过配置模板即可适配多数 REST 接口;复杂渠道可单独实现并注册为 Spring Bean。 ## 目录结构 ``` thirdparty-callback-hub ├── pom.xml ├── README.md └── src/main/java/com/example/callbackhub ├── CallbackHubApplication.java ├── client # 渠道客户端策略 ├── config # 配置属性、RestTemplate、线程池 ├── controller # 提交、回调、任务查询接口 ├── dto # 请求/响应对象 ├── entity # JPA 实体 ├── enums # 任务状态枚举 ├── repository # Spring Data JPA ├── scheduler # 轮询调度器 ├── service # 业务逻辑 └── util # 签名、JSON 路径工具 ``` ## 快速启动 ```bash cd thirdparty-callback-hub mvn spring-boot:run ``` 启动后访问: - H2 Console: http://localhost:8080/h2-console - JDBC URL: `jdbc:h2:mem:callbackhub` ## 主要 API ### 1. 提交任务 ```bash curl -X POST http://localhost:8080/api/dispatch/submit \ -H "Content-Type: application/json" \ -d '{ "partnerCode": "demo-poll", "bizType": "order", "bizId": "B-20240812-001", "payload": "{\"amount\":100}" }' ``` 返回的 `data.id` 即任务 ID,可通过 `/api/tasks/{id}` 查询。 ### 2. 接收回调 ```bash curl -X POST http://localhost:8080/api/callback/demo-callback \ -H "Content-Type: application/json" \ -d '{ "taskId": "EXT-123456", "status": "SUCCESS" }' ``` ### 3. 查询任务 ```bash curl http://localhost:8080/api/tasks/1 ``` ## 配置渠道 在数据库表 `partner_config` 中维护一条记录即可新增渠道。关键字段说明: | 字段 | 含义 | |------|------| | `partner_code` | 渠道唯一标识 | | `supports_callback` | `true` 表示第三方会主动回调;`false` 表示需要系统轮询 | | `submit_url` / `submit_method` / `submit_headers` / `submit_body_template` | 提交接口定义 | | `submit_external_task_id_field` | 从提交响应解析外部任务号的 JSON 路径,如 `json.id` | | `status_url` / `status_method` / `status_headers` / `status_body_template` | 状态查询接口定义(轮询时使用) | | `status_result_status_field` | 轮询结果中状态字段路径,如 `json.status` | | `status_result_status_success_value` | 轮询结果中表示成功的值,如 `SUCCESS` | | `poll_interval_seconds` | 轮询间隔 | | `max_poll_duration_minutes` | 最大轮询时长,超时会置为 `TIMEOUT` | | `sign_type` / `sign_secret` | 签名方式:`NONE` / `MD5` / `HMAC_SHA256` | | `callback_*` | 回调报文解析字段 | 模板变量包括:`${bizId}`、`${bizType}`、`${payload}`、`${externalTaskId}`、`${timestamp}`、`${nonce}`、`${sign}`、`${notifyUrl}`。 ### 关于 `${notifyUrl}`(回调地址) 像支付宝、微信这类支持回调的第三方,通常要求你在**提交**时把自己的回调地址(`notify_url`)一起传给它们,等它们处理完成后主动 `POST` 到这个地址。本系统会自动为每次提交生成: ``` {callbackhub.callback.base-url}/api/callback/{partnerCode} ``` 只需要在 `submit_body_template` 里加上 `"notifyUrl":"${notifyUrl}"`(或第三方要求的字段名,如 `notify_url`),并在 `application.yml` 配置: ```yaml callbackhub: callback: base-url: https://your-domain.com ``` 本地调试没有公网地址时,可以用 ngrok/frp 等工具映射一个临时公网地址填进去,这样第三方才能真正回调到你本机。 ## 扩展复杂渠道 如果默认 HTTP 模板无法满足(如特殊签名、非 JSON 报文、多步流程),实现 `PartnerClient` 并注册为 Spring Bean 即可: ```java @Component @Order(100) public class MySpecialPartnerClient implements PartnerClient { @Override public boolean supports(PartnerConfig config) { return "my-special".equals(config.getPartnerCode()); } // ... } ``` ## 轮询调度 `PollScheduler` 每隔 `callbackhub.poll.fixed-delay-ms`(默认 10 秒)扫描一次待轮询任务: 1. 将超过最大轮询时长的任务置为 `TIMEOUT`。 2. 取出 `next_poll_time <= now` 的任务。 3. 调用对应 `PartnerClient.poll(...)` 获取状态并更新任务。 线程池与批量大小可通过 `application.yml` 调整: ```yaml callbackhub: poll: fixed-delay-ms: 10000 batch-size: 100 worker-pool-size: 10 ``` ## 切换数据库 默认使用 H2 内存数据库便于演示。生产环境替换 `spring.datasource` 为 MySQL/PostgreSQL 等即可,表结构由 JPA 自动生成。