Kaynağa Gözat

企德宝API接口SDK工程初始化

skyline 4 hafta önce
ebeveyn
işleme
fc425c826a

+ 1 - 0
.gitignore

@@ -49,3 +49,4 @@ tsconfig.tsbuildinfo
 
 # 强制包含被全局 gitignore 排除的目录
 !haha-admin-web/src/views/inventory/logs/
+qdb-sdk/target/

+ 492 - 0
qdb-sdk/README.md

@@ -0,0 +1,492 @@
+# 企得宝ERP Java SDK
+
+[![Java](https://img.shields.io/badge/Java-21%2B-orange)](https://www.oracle.com/java/technologies/downloads/)
+[![Maven](https://img.shields.io/badge/Maven-3.6%2B-red)](https://maven.apache.org/)
+
+**qdb-sdk** 是企得宝ERP开放平台的 Java SDK,提供对 73 个开放API接口的完整封装。支持签名生成、请求发送、响应解析等全链路功能。
+
+---
+
+## 📦 技术栈
+
+| 技术 | 版本 | 用途 |
+|------|------|------|
+| Java | 21 | 开发语言 |
+| Maven | 3.6+ | 项目构建 |
+| OkHttp | 4.12.0 | HTTP 客户端 |
+| FastJson2 | 2.0.53 | JSON 序列化/反序列化 |
+| Lombok | 1.18.36 | 代码简化 |
+| SLF4J | 2.0.13 | 日志门面 |
+
+> **注意**:本 SDK 为独立 Maven 项目,不依赖 haha 零售系统任何内部模块,可独立使用或引入到任意 Java 项目。
+
+---
+
+## 🚀 快速开始
+
+### 1. 引入依赖
+
+将 `qdb-sdk` 安装到本地 Maven 仓库后,在项目中添加依赖:
+
+```xml
+<dependency>
+    <groupId>com.qdb</groupId>
+    <artifactId>qdb-sdk</artifactId>
+    <version>1.0.0</version>
+</dependency>
+```
+
+或直接引入源码作为模块:
+
+```xml
+<dependency>
+    <groupId>com.qdb</groupId>
+    <artifactId>qdb-sdk</artifactId>
+    <version>1.0.0</version>
+    <scope>system</scope>
+    <systemPath>${project.basedir}/lib/qdb-sdk-1.0.0.jar</systemPath>
+</dependency>
+```
+
+### 2. 初始化 SDK
+
+```java
+import com.qdb.sdk.QdbClient;
+import com.qdb.sdk.QdbConfig;
+
+// 创建配置(必填:clientId 和 clientSecret)
+QdbConfig config = QdbConfig.builder()
+    .clientId("your_client_id")          // 开放平台分配的 clientId
+    .clientSecret("your_client_secret")  // 开放平台分配的 clientSecret
+    .build();
+
+// 校验配置
+config.validate();
+
+// 创建客户端(单例使用,线程安全)
+QdbClient client = new QdbClient(config);
+```
+
+### 3. 选择环境
+
+SDK 默认接入正式环境,切换测试环境只需修改 `apiUrl`:
+
+```java
+QdbConfig config = QdbConfig.builder()
+    .clientId("your_client_id")
+    .clientSecret("your_client_secret")
+    .apiUrl("https://apitest.7debao.com/router/api")  // 测试环境
+    .build();
+```
+
+| 环境 | 默认值 |
+|------|--------|
+| **正式环境** | `https://api.7debao.com/router/api` |
+| **测试环境** | `https://apitest.7debao.com/router/api` |
+
+---
+
+## 🏗️ 架构说明
+
+### 请求流程
+
+```
+┌─────────────────────────────────────────────────────┐
+│                    调用方代码                          │
+│  QdbClient.getXxxApi().method(request)               │
+└─────────────────┬───────────────────────────────────┘
+                  │
+                  ▼
+┌─────────────────────────────────────────────────────┐
+│                  BaseQdbApi                          │
+│  ① 序列化请求参数为 JSON                              │
+│  ② 调用 HttpUtil.post()                              │
+│  ③ 解析响应 QdbResponse<T>                           │
+│  ④ 检查 success 标识,失败则抛出 QdbException         │
+└─────────────────┬───────────────────────────────────┘
+                  │
+                  ▼
+┌─────────────────────────────────────────────────────┐
+│                   HttpUtil                           │
+│  ① 构建公共参数 Map(TreeMap 自动排序)               │
+│  ② 调用 SignUtil 计算 MD5 签名                       │
+│  ③ 拼接完整 URL:baseUrl?公共参数+sign                │
+│  ④ POST 请求,Body 为业务参数 JSON                    │
+│  ⑤ 返回原始响应 JSON 字符串                           │
+└─────────────────┬───────────────────────────────────┘
+                  │
+                  ▼
+┌─────────────────────────────────────────────────────┐
+│              企得宝ERP 开放平台                        │
+│  https://api.7debao.com/router/api                   │
+└─────────────────────────────────────────────────────┘
+```
+
+### 签名算法
+
+签名规则(与企得宝开放平台文档一致):
+
+1. 将所有**公共参数**按照参数名的首字母先后顺序排列(字典序)
+2. 把排序后的结果按照**参数名+参数值**的方式拼接
+3. 拼装好的字符串**首尾拼接** `client_secret` 进行 MD5(小写32位)
+
+```java
+// 示例:SDK 内部自动完成签名
+// 公共参数排序拼接后:client_idxxxformatjsonmethodxxxpartner_idxxx...
+// 加签:secret + 拼接字符串 + secret → MD5
+```
+
+### 统一响应格式
+
+所有 API 返回统一结构:
+
+```json
+{
+    "errorCode": null,
+    "msg": "操作成功",
+    "data": { ... },
+    "date": null,
+    "version": null,
+    "success": true
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| errorCode | String | 错误编码,成功时为 null |
+| msg | String | 提示信息 |
+| data | Object | 成功时返回数据,失败为 null |
+| date | Date | 响应时间 |
+| version | Integer | 版本信息 |
+| success | Boolean | 成功为 true,失败为 false |
+
+---
+
+## 💻 使用示例
+
+### 订单上传
+
+```java
+import com.qdb.sdk.QdbClient;
+import com.qdb.sdk.QdbConfig;
+import com.qdb.sdk.QdbException;
+import com.qdb.sdk.model.request.*;
+import com.qdb.sdk.model.response.QdbResponse;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public class OrderExample {
+
+    public static void main(String[] args) {
+        // 1. 初始化客户端
+        QdbConfig config = QdbConfig.builder()
+            .clientId("your_client_id")
+            .clientSecret("your_client_secret")
+            .build();
+
+        QdbClient client = new QdbClient(config);
+
+        try {
+            // 2. 构建订单商品
+            OrderGoods goods = OrderGoods.builder()
+                .platformGoodsCode("SKU001")           // 平台商品编码
+                .platformGoodsName("测试商品")           // 平台商品标题
+                .quantity(new BigDecimal("2"))          // 数量
+                .price(new BigDecimal("49.90"))         // 单价
+                .realPrice(new BigDecimal("45.00"))     // 实际售价
+                .build();
+
+            // 3. 构建支付信息(可选)
+            OrderPay pay = OrderPay.builder()
+                .payTypeCode("2")                       // 微信支付
+                .payTime("2024-01-24 15:30:00")
+                .payNo("WX202401241530001")
+                .build();
+
+            // 4. 构建订单上传请求
+            OrderSaveRequest request = OrderSaveRequest.builder()
+                .shopId(9900100000000136L)              // 店铺ID
+                .tradeNo("T202401240001")               // 平台交易单号
+                .platformTradeStatus("02")              // 待发货
+                .platformRefundStatus("0")              // 未退款
+                .orderTypeCode("2")                     // 在线交易
+                .postAmount(new BigDecimal("10.00"))    // 运费
+                .payAmount(new BigDecimal("100.00"))    // 付款金额
+                .receiverName("张三")
+                .receiverMobile("13800138000")
+                .receiverProvince("广东省")
+                .receiverCity("深圳市")
+                .receiverDistrict("南山区")
+                .receiverAddress("科技园南区A栋1001")
+                .ordersGoods(List.of(goods))            // 商品列表
+                .ordersPay(List.of(pay))                // 支付信息(可选)
+                .buyerMessage("请尽快发货")
+                .build();
+
+            // 5. 调用订单上传接口
+            QdbResponse<Void> response = client.getOrderApi().saveOrder(request);
+
+            if (response.isSuccess()) {
+                System.out.println("✅ 订单上传成功");
+            }
+
+        } catch (QdbException e) {
+            System.err.println("❌ 订单上传失败: [" + e.getErrorCode() + "] " + e.getMessage());
+
+        } finally {
+            // 6. 关闭客户端
+            client.shutdown();
+        }
+    }
+}
+```
+
+### Spring Boot 集成
+
+```java
+import com.qdb.sdk.QdbClient;
+import com.qdb.sdk.QdbConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class QdbSdkConfig {
+
+    @Value("${qdb.client-id}")
+    private String clientId;
+
+    @Value("${qdb.client-secret}")
+    private String clientSecret;
+
+    @Value("${qdb.api-url:https://api.7debao.com/router/api}")
+    private String apiUrl;
+
+    @Bean
+    public QdbClient qdbClient() {
+        QdbConfig config = QdbConfig.builder()
+            .clientId(clientId)
+            .clientSecret(clientSecret)
+            .apiUrl(apiUrl)
+            .build();
+        return new QdbClient(config);
+    }
+}
+```
+
+**application.yml 配置:**
+
+```yaml
+qdb:
+  client-id: your_client_id
+  client-secret: your_client_secret
+  api-url: https://api.7debao.com/router/api
+```
+
+---
+
+## 📋 API 模块列表
+
+SDK 目前已实现以下 API 模块(共 **73** 个接口),完整接口明细请参考对应文档:
+
+### 已实现
+
+| 模块 | API 类 | 已实现方法 | 对应文档 |
+|------|--------|-----------|----------|
+| 订单管理 | `OrderApi` | `saveOrder()` 订单上传 | [04-订单管理.md](./04-订单管理.md) |
+
+### 待扩展(基础框架已就绪,按需添加)
+
+| 模块 | 接口数 | 对应文档 |
+|------|--------|----------|
+| 基础资料 | 9 | [01-基础资料.md](./01-基础资料.md) |
+| 商品管理 | 6 | [02-商品管理.md](./02-商品管理.md) |
+| 平台商品管理(个性) | 19 | [03-平台商品管理(个性).md](./03-平台商品管理(个性).md) |
+| 订单管理(其余) | 15 | [04-订单管理.md](./04-订单管理.md) |
+| 采购管理 | 5 | [05-采购管理.md](./05-采购管理.md) |
+| 调拨管理 | 2 | [06-调拨管理.md](./06-调拨管理.md) |
+| 其他出入库管理 | 3 | [07-其他出入库管理.md](./07-其他出入库管理.md) |
+| 盘点管理 | 2 | [08-盘点管理.md](./08-盘点管理.md) |
+| 库存管理 | 2 | [09-库存管理.md](./09-库存管理.md) |
+| 消息推送(回调接收) | 8 | [10-消息推送.md](./10-消息推送.md) |
+| 路由管理 | 1 | [11-路由管理.md](./11-路由管理.md) |
+
+---
+
+## ⚠️ 异常处理
+
+SDK 统一使用 `QdbException` 异常类:
+
+```java
+import com.qdb.sdk.QdbException;
+
+try {
+    QdbResponse<Void> response = client.getOrderApi().saveOrder(request);
+} catch (QdbException e) {
+    // 错误编码(对应企得宝API返回的 errorCode)
+    String errorCode = e.getErrorCode();
+    // 错误信息
+    String errorMsg = e.getMessage();
+    // 原始异常(如有)
+    Throwable cause = e.getCause();
+
+    System.err.println("错误码: " + errorCode + ", 信息: " + errorMsg);
+}
+```
+
+### 常见异常场景
+
+| 异常场景 | 说明 |
+|----------|------|
+| `clientId`/`clientSecret` 为空 | 抛出 `IllegalArgumentException`,配置时需调用 `config.validate()` |
+| 网络异常 | `QdbException` 包装 `IOException`,检查网络连通性 |
+| HTTP 状态码非 200 | `QdbException("HTTP 请求失败,状态码: xxx")` |
+| API 业务错误 | `QdbException` 包含 `errorCode` 和 `msg`(从企得宝返回) |
+| 响应解析失败 | `QdbException("API 响应解析失败")` |
+
+---
+
+## 🔧 开发指南
+
+### 项目结构
+
+```
+qdb-sdk/
+├── pom.xml                              # Maven 构建文件(Java 21)
+├── src/main/java/com/qdb/sdk/
+│   ├── QdbClient.java                   # 主客户端入口
+│   ├── QdbConfig.java                   # SDK 配置类
+│   ├── QdbException.java                # 自定义异常
+│   ├── api/
+│   │   ├── BaseQdbApi.java              # API 基类(通用调用逻辑)
+│   │   └── OrderApi.java                # 订单管理 API
+│   ├── model/
+│   │   ├── request/                     # 请求参数模型
+│   │   │   ├── OrderSaveRequest.java    # 订单上传请求
+│   │   │   ├── OrderGoods.java          # 订单商品
+│   │   │   ├── OrderInvoice.java        # 订单发票
+│   │   │   └── OrderPay.java            # 订单支付
+│   │   └── response/
+│   │       └── QdbResponse.java         # 统一响应对象
+│   └── util/
+│       ├── SignUtil.java                # MD5 签名工具
+│       ├── HttpUtil.java                # HTTP 客户端(OkHttp)
+│       └── JsonUtil.java                # JSON 工具(FastJson2)
+├── 00-概述与接入指南.md                   # 企得宝 API 文档
+├── 01-基础资料.md
+├── ...                                  # 其他 API 文档
+```
+
+### 扩展新模块
+
+新增一个业务 API 模块只需 3 步:
+
+**1. 创建请求模型**(`model/request/` 目录下):
+
+```java
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ShopQueryRequest {
+    private Long shopId;
+    private String shopName;
+    // ... 其他请求参数
+}
+```
+
+**2. 创建 API 类**(`api/` 目录下):
+
+```java
+public class ShopApi extends BaseQdbApi {
+    private static final String METHOD_SHOP_LIST = "foonsu.erp.shop.shopInfo";
+
+    public ShopApi(QdbClient client) {
+        super(client);
+    }
+
+    public QdbResponse<List<ShopInfo>> queryShops(ShopQueryRequest request) throws QdbException {
+        return execute(METHOD_SHOP_LIST, request);
+    }
+}
+```
+
+**3. 注册到 QdbClient**:
+
+```java
+// 在 QdbClient.java 中添加
+private final ShopApi shopApi;
+
+// 在构造方法中初始化
+this.shopApi = new ShopApi(this);
+
+// 添加 Getter
+public ShopApi getShopApi() { return shopApi; }
+```
+
+### 构建与测试
+
+```bash
+# 编译
+mvn clean compile
+
+# 打包
+mvn clean package
+
+# 安装到本地仓库
+mvn clean install
+
+# 跳过测试打包
+mvn clean package -DskipTests
+```
+
+---
+
+## ❓ 常见问题
+
+### Q1: SDK 是否线程安全?
+
+**是的**。`QdbClient` 可在多线程环境中安全使用,建议作为单例管理(如在 Spring 中声明为 `@Bean`)。
+
+### Q2: 签名计算失败怎么办?
+
+检查 `clientId` 和 `clientSecret` 是否正确。SDK 内部已实现完整的签名逻辑,包括字典序排列、key+value 拼接、首尾加 secret、MD5 加密。
+
+### Q3: 如何切换正式/测试环境?
+
+修改 `QdbConfig.apiUrl`:
+- 正式:`https://api.7debao.com/router/api`(默认)
+- 测试:`https://apitest.7debao.com/router/api`
+
+### Q4: 如何获取 clientId 和 clientSecret?
+
+登录企得宝ERP开放平台后台,在应用管理中创建应用获取。
+
+### Q5: 响应中 success=false 但 data 不为 null?
+
+以 `success` 字段为准。SDK 内部通过 `isSuccess()` 判断,该方法只检查 `success` 是否为 `true`。
+
+---
+
+## 📄 参考文档
+
+- [企得宝ERP 开放平台](https://open.7debao.com)
+- [00-概述与接入指南](./00-概述与接入指南.md) - API 总览与接入规范
+- [01-基础资料.md](./01-基础资料.md) - 店铺、仓库、物流公司等
+- [02-商品管理.md](./02-商品管理.md) - 商品信息管理
+- [03-平台商品管理(个性).md](./03-平台商品管理(个性).md) - 平台商品独立管理
+- [04-订单管理.md](./04-订单管理.md) - 订单上传、查询、发货、售后
+- [05-采购管理.md](./05-采购管理.md) - 供应商、采购订单、入库
+- [06-调拨管理.md](./06-调拨管理.md) - 调拨单管理
+- [07-其他出入库管理.md](./07-其他出入库管理.md) - 其他出入库单
+- [08-盘点管理.md](./08-盘点管理.md) - 盘点单管理
+- [09-库存管理.md](./09-库存管理.md) - 库存查询
+- [10-消息推送.md](./10-消息推送.md) - 推送通知接收
+- [11-路由管理.md](./11-路由管理.md) - 物流路由查询
+
+---
+
+**版本**: 1.0.0  
+**Java**: 21+  
+**更新日期**: 2026-04-30

+ 94 - 0
qdb-sdk/pom.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.qdb</groupId>
+    <artifactId>qdb-sdk</artifactId>
+    <version>1.0.0</version>
+    <packaging>jar</packaging>
+
+    <name>qdb-sdk</name>
+    <description>企得宝ERP开放平台 Java SDK</description>
+
+    <properties>
+        <java.version>21</java.version>
+        <maven.compiler.source>${java.version}</maven.compiler.source>
+        <maven.compiler.target>${java.version}</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <okhttp.version>4.12.0</okhttp.version>
+        <fastjson2.version>2.0.53</fastjson2.version>
+        <lombok.version>1.18.36</lombok.version>
+        <slf4j.version>2.0.13</slf4j.version>
+        <junit.version>5.11.0</junit.version>
+    </properties>
+
+    <dependencies>
+        <!-- HTTP Client -->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>${okhttp.version}</version>
+        </dependency>
+
+        <!-- JSON Processing -->
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>${fastjson2.version}</version>
+        </dependency>
+
+        <!-- Lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>${lombok.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>${slf4j.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.11.0</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <parameters>true</parameters>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                            <version>${lombok.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 92 - 0
qdb-sdk/src/main/java/com/qdb/sdk/QdbClient.java

@@ -0,0 +1,92 @@
+package com.qdb.sdk;
+
+import com.qdb.sdk.api.OrderApi;
+import com.qdb.sdk.util.HttpUtil;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 企得宝ERP SDK 主客户端
+ * <p>
+ * 提供对企得宝ERP开放API的统一访问入口。
+ * 通过此类获取各业务模块的 API 实例进行调用。
+ * </p>
+ *
+ * <h3>使用示例:</h3>
+ * <pre>{@code
+ * // 创建配置
+ * QdbConfig config = QdbConfig.builder()
+ *     .clientId("your_client_id")
+ *     .clientSecret("your_client_secret")
+ *     .build();
+ * config.validate();
+ *
+ * // 创建客户端
+ * QdbClient client = new QdbClient(config);
+ *
+ * // 调用订单上传接口
+ * OrderSaveRequest request = OrderSaveRequest.builder()
+ *     .shopId(9900100000000136L)
+ *     .tradeNo("T202401240001")
+ *     // ... 设置其他参数
+ *     .build();
+ *
+ * QdbResponse<Void> response = client.getOrderApi().saveOrder(request);
+ * if (response.isSuccess()) {
+ *     System.out.println("订单上传成功");
+ * }
+ *
+ * // 使用完毕后关闭
+ * client.shutdown();
+ * }</pre>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Slf4j
+@Getter
+public class QdbClient {
+
+    /**
+     * SDK 配置信息
+     */
+    private final QdbConfig config;
+
+    /**
+     * HTTP 工具类
+     */
+    private final HttpUtil httpUtil;
+
+    // ====== 业务 API 实例 ======
+
+    /** 订单管理 API */
+    private final OrderApi orderApi;
+
+    /**
+     * 构造方法
+     *
+     * @param config SDK 配置信息
+     */
+    public QdbClient(QdbConfig config) {
+        // 校验配置
+        config.validate();
+
+        this.config = config;
+        this.httpUtil = new HttpUtil(config);
+
+        // 初始化各业务 API 模块
+        this.orderApi = new OrderApi(this);
+
+        log.info("企得宝ERP SDK 初始化成功, clientId: {}", config.getClientId());
+    }
+
+    /**
+     * 关闭客户端,释放资源
+     */
+    public void shutdown() {
+        if (httpUtil != null) {
+            httpUtil.close();
+        }
+        log.info("企得宝ERP SDK 已关闭");
+    }
+}

+ 92 - 0
qdb-sdk/src/main/java/com/qdb/sdk/QdbConfig.java

@@ -0,0 +1,92 @@
+package com.qdb.sdk;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 企得宝ERP SDK 配置类
+ * <p>
+ * 包含API访问所需的认证信息、请求地址及超时配置。
+ * 通过 {@link QdbConfigBuilder} 构建配置实例。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Data
+@Builder
+public class QdbConfig {
+
+    /**
+     * API请求地址
+     * <ul>
+     *   <li>正式环境:{@code https://api.7debao.com/router/api}</li>
+     *   <li>测试环境:{@code https://apitest.7debao.com/router/api}</li>
+     * </ul>
+     */
+    @Builder.Default
+    private String apiUrl = "https://api.7debao.com/router/api";
+
+    /**
+     * 分配给应用的 clientId(必填)
+     */
+    private String clientId;
+
+    /**
+     * 分配给应用的 clientSecret(必填)
+     */
+    private String clientSecret;
+
+    /**
+     * HTTP 连接超时时间(秒),默认 10
+     */
+    @Builder.Default
+    private int connectTimeout = 10;
+
+    /**
+     * HTTP 读取超时时间(秒),默认 30
+     */
+    @Builder.Default
+    private int readTimeout = 30;
+
+    /**
+     * HTTP 写入超时时间(秒),默认 30
+     */
+    @Builder.Default
+    private int writeTimeout = 30;
+
+    /**
+     * 响应格式,默认 json
+     */
+    @Builder.Default
+    private String format = "json";
+
+    /**
+     * API 版本,默认 1.0
+     */
+    @Builder.Default
+    private String version = "1.0";
+
+    /**
+     * 合作伙伴标识,默认 qdb-sdk-java-httpdns-20241113
+     */
+    @Builder.Default
+    private String partnerId = "qdb-sdk-java-httpdns-20241113";
+
+    /**
+     * 校验配置信息是否完整
+     *
+     * @throws IllegalArgumentException 配置信息不完整时抛出
+     */
+    public void validate() {
+        if (clientId == null || clientId.trim().isEmpty()) {
+            throw new IllegalArgumentException("clientId 不能为空");
+        }
+        if (clientSecret == null || clientSecret.trim().isEmpty()) {
+            throw new IllegalArgumentException("clientSecret 不能为空");
+        }
+        if (apiUrl == null || apiUrl.trim().isEmpty()) {
+            throw new IllegalArgumentException("apiUrl 不能为空");
+        }
+    }
+}

+ 57 - 0
qdb-sdk/src/main/java/com/qdb/sdk/QdbException.java

@@ -0,0 +1,57 @@
+package com.qdb.sdk;
+
+/**
+ * 企得宝ERP SDK 异常类
+ * <p>
+ * 封装 SDK 调用过程中发生的所有异常,包括网络异常、业务异常、签名异常等。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+public class QdbException extends Exception {
+
+    /**
+     * 错误编码,对应企得宝API返回的 errorCode
+     */
+    private String errorCode;
+
+    /**
+     * 构造方法
+     *
+     * @param message 异常信息
+     */
+    public QdbException(String message) {
+        super(message);
+    }
+
+    /**
+     * 构造方法
+     *
+     * @param message 异常信息
+     * @param cause   原始异常
+     */
+    public QdbException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * 构造方法
+     *
+     * @param errorCode 错误编码
+     * @param message   异常信息
+     */
+    public QdbException(String errorCode, String message) {
+        super(message);
+        this.errorCode = errorCode;
+    }
+
+    /**
+     * 获取错误编码
+     *
+     * @return 错误编码
+     */
+    public String getErrorCode() {
+        return errorCode;
+    }
+}

+ 97 - 0
qdb-sdk/src/main/java/com/qdb/sdk/api/BaseQdbApi.java

@@ -0,0 +1,97 @@
+package com.qdb.sdk.api;
+
+import com.alibaba.fastjson2.TypeReference;
+import com.qdb.sdk.QdbClient;
+import com.qdb.sdk.QdbException;
+import com.qdb.sdk.model.response.QdbResponse;
+import com.qdb.sdk.util.HttpUtil;
+import com.qdb.sdk.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 企得宝ERP API 基类
+ * <p>
+ * 提供通用的API调用方法,所有业务API类需继承此类。
+ * 封装了请求执行、响应解析、异常处理等通用逻辑。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Slf4j
+public abstract class BaseQdbApi {
+
+    protected final QdbClient client;
+    protected final HttpUtil httpUtil;
+
+    protected BaseQdbApi(QdbClient client) {
+        this.client = client;
+        this.httpUtil = client.getHttpUtil();
+    }
+
+    /**
+     * 执行API调用(返回泛型响应)
+     *
+     * @param method    API 方法名
+     * @param request   请求参数对象
+     * @param <T>       data 字段类型
+     * @return 解析后的响应对象
+     * @throws QdbException 调用失败时抛出
+     */
+    protected <T> QdbResponse<T> execute(String method, Object request) throws QdbException {
+        return execute(method, request, null);
+    }
+
+    /**
+     * 执行API调用(指定data类型)
+     *
+     * @param method      API 方法名
+     * @param request     请求参数对象
+     * @param dataClass   data 字段的具体类型,为 null 时不解析 data
+     * @param <T>         data 字段类型
+     * @return 解析后的响应对象
+     * @throws QdbException 调用失败时抛出
+     */
+    @SuppressWarnings("unchecked")
+    protected <T> QdbResponse<T> execute(String method, Object request, Class<T> dataClass) throws QdbException {
+        try {
+            // 序列化请求参数为 JSON
+            String businessJson = request != null ? JsonUtil.toJsonString(request) : "{}";
+
+            // 执行 HTTP 请求
+            String responseJson = httpUtil.post(client.getConfig(), method, businessJson);
+
+            log.debug("QDB API 原始响应: {}", responseJson);
+
+            // 解析通用响应
+            QdbResponse<T> qdbResponse = JsonUtil.parseObject(responseJson,
+                    new TypeReference<QdbResponse<T>>() {});
+
+            if (qdbResponse == null) {
+                throw new QdbException("API 响应解析失败,响应为空");
+            }
+
+            // 检查业务是否成功
+            if (!qdbResponse.isSuccess()) {
+                String errorMsg = qdbResponse.getMsg() != null ? qdbResponse.getMsg() : "未知错误";
+                String errorCode = qdbResponse.getErrorCode();
+                throw new QdbException(errorCode, "API 调用失败 [" + method + "]: " + errorMsg);
+            }
+
+            // 如果指定了 dataClass,尝试转换 data 字段
+            if (dataClass != null && qdbResponse.getData() != null) {
+                String dataJson = JsonUtil.toJsonString(qdbResponse.getData());
+                T typedData = JsonUtil.parseObject(dataJson, dataClass);
+                qdbResponse.setData(typedData);
+            }
+
+            return qdbResponse;
+
+        } catch (QdbException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("API 调用异常 [{}]", method, e);
+            throw new QdbException("API 调用异常 [" + method + "]: " + e.getMessage(), e);
+        }
+    }
+}

+ 41 - 0
qdb-sdk/src/main/java/com/qdb/sdk/api/OrderApi.java

@@ -0,0 +1,41 @@
+package com.qdb.sdk.api;
+
+import com.qdb.sdk.QdbClient;
+import com.qdb.sdk.QdbException;
+import com.qdb.sdk.model.request.OrderSaveRequest;
+import com.qdb.sdk.model.response.QdbResponse;
+
+/**
+ * 订单管理 API
+ * <p>
+ * 提供订单上传、查询、发货等接口的调用封装。
+ * 对应文档:04-订单管理.md
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+public class OrderApi extends BaseQdbApi {
+
+    /** 订单上传 */
+    private static final String METHOD_ORDER_SAVE = "foonsu.erp.orders.save";
+
+    public OrderApi(QdbClient client) {
+        super(client);
+    }
+
+    /**
+     * 订单上传
+     * <p>
+     * 上传订单到企得宝ERP(仅支持自建商城平台店铺)。
+     * 订单上传成功后,需要店铺开启自动下单或者订单审核页面手动拉单进系统。
+     * </p>
+     *
+     * @param request 订单上传请求参数
+     * @return 响应对象(data 为 null)
+     * @throws QdbException 调用失败时抛出
+     */
+    public QdbResponse<Void> saveOrder(OrderSaveRequest request) throws QdbException {
+        return execute(METHOD_ORDER_SAVE, request);
+    }
+}

+ 81 - 0
qdb-sdk/src/main/java/com/qdb/sdk/model/request/OrderGoods.java

@@ -0,0 +1,81 @@
+package com.qdb.sdk.model.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 订单商品信息
+ * <p>
+ * 对应订单上传接口中的 ordersGoods 集合元素。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class OrderGoods {
+
+    /** 平台商品编码(会当作平台商品SkuId以及平台商品Id) */
+    private String platformGoodsCode;
+
+    /** 平台商品标题 */
+    private String platformGoodsName;
+
+    /** 平台商品Sku编码 */
+    private String goodsPlatformCode;
+
+    /** 商品数量 */
+    private BigDecimal quantity;
+
+    /** 商品单价 单位元 */
+    private BigDecimal price;
+
+    /** 实际售价 单位元 */
+    private BigDecimal realPrice;
+
+    /** 图片地址链接 */
+    private String picUrl;
+
+    /** 替换的商品简称 */
+    private String shortGoodsName;
+
+    /** 单位 */
+    private String unit;
+
+    /** 商品重量 */
+    private BigDecimal weight;
+
+    /** 平台商品规格名称 */
+    private String platformGoodsSpecs;
+
+    /** 订单商品退款状态:0-未退款,1-已退款,4-退款中 */
+    private String platformRefundStatus;
+
+    /** 分销外部单号 */
+    private String extOrderNo;
+
+    /** 商品支付金额 */
+    private BigDecimal payAmount;
+
+    /** 商品实际金额 */
+    private BigDecimal orderAmount;
+
+    /** 运费 */
+    private BigDecimal postAmount;
+
+    /** 是否使用国补:0-否;1-是 */
+    private String useGovSubsidy;
+
+    /** 外部系统单据明细行号 */
+    private String externalDetailNo;
+
+    /** 微派服务:0-否;1-是 */
+    private String wpService;
+}

+ 63 - 0
qdb-sdk/src/main/java/com/qdb/sdk/model/request/OrderInvoice.java

@@ -0,0 +1,63 @@
+package com.qdb.sdk.model.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 订单发票信息
+ * <p>
+ * 对应订单上传接口中的 ordersInvoice 集合元素。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class OrderInvoice {
+
+    /** 发票种类:1-增值专用发票;2-增值普通发票 */
+    private String invoiceKind;
+
+    /** 发票抬头种类:1-个人;2-企业 */
+    private String invoiceTitleKind;
+
+    /** 发票类型:1-纸质发票;2-电子发票 */
+    private String invoiceTitleType;
+
+    /** 发票抬头 */
+    private String invoiceTitle;
+
+    /** 发票内容 */
+    private String invoiceContent;
+
+    /** 纳税人识别号 */
+    private String taxpayerNumber;
+
+    /** 开户行 */
+    private String depositBank;
+
+    /** 发票金额 单位元 */
+    private BigDecimal invoiceAmount;
+
+    /** 电话 */
+    private String phone;
+
+    /** 地址 */
+    private String address;
+
+    /** 备注 */
+    private String remark;
+
+    /** 未开票金额 单位元 */
+    private BigDecimal unvoiceAmount;
+
+    /** 帐号 */
+    private String accounts;
+}

+ 37 - 0
qdb-sdk/src/main/java/com/qdb/sdk/model/request/OrderPay.java

@@ -0,0 +1,37 @@
+package com.qdb.sdk.model.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 订单支付信息
+ * <p>
+ * 对应订单上传接口中的 ordersPay 集合元素。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class OrderPay {
+
+    /** 付款方式编号:1-支付宝;2-微信;3-线下支付;4-在线支付;5-代收货款 */
+    private String payTypeCode;
+
+    /** 支付时间 */
+    private String payTime;
+
+    /** 交易号 */
+    private String payNo;
+
+    /** 账号 */
+    private String accountNo;
+
+    /** 备注 */
+    private String remark;
+}

+ 166 - 0
qdb-sdk/src/main/java/com/qdb/sdk/model/request/OrderSaveRequest.java

@@ -0,0 +1,166 @@
+package com.qdb.sdk.model.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 订单上传请求参数
+ * <p>
+ * 对应 Method: {@code foonsu.erp.orders.save}
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class OrderSaveRequest {
+
+    /** 店铺ID(ERP内店铺主键ID) */
+    private Long shopId;
+
+    /** 平台交易单号 */
+    private String tradeNo;
+
+    /** 平台订单交易状态:01-待付款,02-待发货,03-已发货待签收,10-已签收,11-交易关闭 */
+    private String platformTradeStatus;
+
+    /** 订单退款状态:0-未退款,1-已退款,2-部分退款,4-退款中 */
+    private String platformRefundStatus;
+
+    /** 订单的交易类型:1-代收货款,2-在线交易,3-线下交易 */
+    private String orderTypeCode;
+
+    /** 运费 */
+    private BigDecimal postAmount;
+
+    /** 付款金额 单位元 */
+    private BigDecimal payAmount;
+
+    /** 收件人省份 */
+    private String receiverProvince;
+
+    /** 收件人城市 */
+    private String receiverCity;
+
+    /** 收件人区/县级市 */
+    private String receiverDistrict;
+
+    /** 收件人地址 */
+    private String receiverAddress;
+
+    /** 收件人名称 */
+    private String receiverName;
+
+    /** 收件人手机(与电话二选一入参即可) */
+    private String receiverMobile;
+
+    /** 收件人电话(与手机二选一入参即可) */
+    private String receiverPhone;
+
+    /** 发件人省份 */
+    private String sendProvince;
+
+    /** 发件人城市 */
+    private String sendCity;
+
+    /** 发件人区/县级市 */
+    private String sendDistrict;
+
+    /** 发件人地址 */
+    private String sendAddress;
+
+    /** 发件人名称 */
+    private String sendName;
+
+    /** 发件人手机(与电话二选一入参即可) */
+    private String sendMobile;
+
+    /** 发件人电话(与手机二选一入参即可) */
+    private String sendPhone;
+
+    /** 发件人邮编 */
+    private String sendZipCode;
+
+    /** 订单商品集合 */
+    private List<OrderGoods> ordersGoods;
+
+    /** 买家留言 */
+    private String buyerMessage;
+
+    /** 买家昵称 */
+    private String buyerNick;
+
+    /** 卖家备注 */
+    private String sellerMessage;
+
+    /** 会员编码 */
+    private String vipNo;
+
+    /** 会员名称 */
+    private String vipName;
+
+    /** 广告ID */
+    private String advertId;
+
+    /** 订单发票集合 */
+    private List<OrderInvoice> ordersInvoice;
+
+    /** 订单支付信息集合 */
+    private List<OrderPay> ordersPay;
+
+    /** 预售类型:0-非预售;1-预售 */
+    private String presellType;
+
+    /** 指定(预售)发货时间 yyyy-MM-dd HH:mm:ss */
+    private String presellDeliveryTime;
+
+    /** 预售付款状态:1-已付款;2-未付尾款;3-未付款(预售单必填) */
+    private String payStatus;
+
+    /** 订单付款时间 */
+    private String payDate;
+
+    /** 仓库名称,精确匹配 */
+    private String warehouseName;
+
+    /** 订单旗帜:0-空;1-红旗;2-黄旗;3-绿旗;4-蓝旗;5-紫旗 */
+    private String markLevel;
+
+    /** 运费付款方式:1-寄付;2-收方付;3-第三方付;4-寄付现结 */
+    private String paymentOfCharge;
+
+    /** 月结卡号 */
+    private String monthlyAccount;
+
+    /** 业务类型 */
+    private String carrierProduct;
+
+    /** 物流公司编码 */
+    private String logisticsCode;
+
+    /** 物流单号(isYddPush为1时必传) */
+    private String logisticsNo;
+
+    /** 是否自流转:1-是;0或null-否 */
+    private String isYddPush;
+
+    /** 顺丰同城订单ID */
+    private String sftcOrderId;
+
+    /** 分销外部单号 */
+    private String extOrderNo;
+
+    /** 订单标识:0-非拆非合;1-合;2-拆 */
+    private String orderMark;
+
+    /** 平台物流公司编码(匹配物流使用) */
+    private String platformLogisticsCompanyCode;
+}

+ 68 - 0
qdb-sdk/src/main/java/com/qdb/sdk/model/response/QdbResponse.java

@@ -0,0 +1,68 @@
+package com.qdb.sdk.model.response;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 企得宝ERP API 统一响应对象
+ * <p>
+ * 所有API统一返回以下格式:
+ * <pre>
+ * {
+ *   "errorCode": null,
+ *   "msg": "操作成功",
+ *   "data": { ... },
+ *   "date": null,
+ *   "version": null,
+ *   "success": true
+ * }
+ * </pre>
+ * </p>
+ *
+ * @param <T> data 字段数据类型
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Data
+public class QdbResponse<T> {
+
+    /**
+     * 错误编码,成功时为 null
+     */
+    private String errorCode;
+
+    /**
+     * 提示信息
+     */
+    private String msg;
+
+    /**
+     * 成功时返回数据,失败为 null
+     */
+    private T data;
+
+    /**
+     * 响应时间
+     */
+    private Date date;
+
+    /**
+     * 版本信息
+     */
+    private Integer version;
+
+    /**
+     * 成功为 true,失败为 false
+     */
+    private Boolean success;
+
+    /**
+     * 判断响应是否成功
+     *
+     * @return true 表示成功
+     */
+    public boolean isSuccess() {
+        return Boolean.TRUE.equals(success);
+    }
+}

+ 160 - 0
qdb-sdk/src/main/java/com/qdb/sdk/util/HttpUtil.java

@@ -0,0 +1,160 @@
+package com.qdb.sdk.util;
+
+import com.qdb.sdk.QdbConfig;
+import com.qdb.sdk.QdbException;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * HTTP 工具类
+ * <p>
+ * 基于 OkHttp 实现 HTTP 请求,提供 POST 请求(JSON 方式)的封装。
+ * 企得宝ERP API 调用方式:
+ * <ul>
+ *   <li>公共参数拼接到 URL Query String</li>
+ *   <li>业务参数以 JSON 格式放入 Request Body</li>
+ *   <li>签名由公共参数计算得出</li>
+ * </ul>
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+@Slf4j
+public class HttpUtil {
+
+    private final OkHttpClient client;
+    private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");
+
+    /**
+     * 构造方法
+     *
+     * @param config SDK 配置
+     */
+    public HttpUtil(QdbConfig config) {
+        this.client = new OkHttpClient.Builder()
+                .connectTimeout(config.getConnectTimeout(), TimeUnit.SECONDS)
+                .readTimeout(config.getReadTimeout(), TimeUnit.SECONDS)
+                .writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS)
+                .retryOnConnectionFailure(true)
+                .build();
+    }
+
+    /**
+     * 执行 API POST 请求
+     * <p>
+     * 请求方式:
+     * <ol>
+     *   <li>生成公共参数(包含 sign 签名)</li>
+     *   <li>将公共参数拼接到 URL Query String</li>
+     *   <li>业务参数以 JSON 格式放入 Request Body</li>
+     *   <li>发送 POST 请求</li>
+     * </ol>
+     * </p>
+     *
+     * @param config        SDK 配置
+     * @param method        API 方法名
+     * @param businessJson  业务参数 JSON 字符串
+     * @return 响应 JSON 字符串
+     * @throws QdbException 请求失败时抛出
+     */
+    public String post(QdbConfig config, String method, String businessJson) throws QdbException {
+        try {
+            // 构建公共参数
+            Map<String, String> publicParams = buildPublicParams(config, method);
+
+            // 计算签名
+            String sign = SignUtil.generateSign(publicParams, config.getClientSecret());
+            publicParams.put("sign", sign);
+
+            // 拼接完整 URL:baseUrl + query string
+            String url = buildUrl(config.getApiUrl(), publicParams);
+
+            log.debug("QDB API 请求 URL: {}", url);
+            log.debug("QDB API Method: {}", method);
+            log.debug("QDB API 业务参数: {}", businessJson);
+
+            // 创建请求体(JSON 格式)
+            RequestBody body = RequestBody.create(businessJson, JSON_TYPE);
+
+            // 创建请求
+            Request request = new Request.Builder()
+                    .url(url)
+                    .header("Content-Type", "application/json")
+                    .post(body)
+                    .build();
+
+            // 执行请求
+            try (Response response = client.newCall(request).execute()) {
+                log.debug("QDB API 响应状态码: {}", response.code());
+
+                if (!response.isSuccessful()) {
+                    throw new QdbException("HTTP 请求失败,状态码: " + response.code());
+                }
+
+                ResponseBody responseBody = response.body();
+                String responseStr = responseBody != null ? responseBody.string() : "";
+                log.debug("QDB API 响应体: {}", responseStr);
+                return responseStr;
+            }
+
+        } catch (IOException e) {
+            log.error("QDB API 网络请求异常", e);
+            throw new QdbException("网络请求异常: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 构建公共参数
+     *
+     * @param config SDK 配置
+     * @param method API 方法名
+     * @return 公共参数 Map
+     */
+    private Map<String, String> buildPublicParams(QdbConfig config, String method) {
+        Map<String, String> params = new TreeMap<>();
+        params.put("client_id", config.getClientId());
+        params.put("format", config.getFormat());
+        params.put("method", method);
+        params.put("partner_id", config.getPartnerId());
+        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
+        params.put("v", config.getVersion());
+        return params;
+    }
+
+    /**
+     * 拼接 URL 和查询参数
+     *
+     * @param baseUrl API 基础 URL
+     * @param params  查询参数
+     * @return 完整 URL
+     */
+    private String buildUrl(String baseUrl, Map<String, String> params) {
+        StringBuilder url = new StringBuilder(baseUrl);
+        url.append("?");
+        boolean first = true;
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            if (!first) {
+                url.append("&");
+            }
+            url.append(entry.getKey()).append("=").append(entry.getValue());
+            first = false;
+        }
+        return url.toString();
+    }
+
+    /**
+     * 关闭 HTTP 客户端,释放资源
+     */
+    public void close() {
+        if (client != null) {
+            client.dispatcher().executorService().shutdown();
+            client.connectionPool().evictAll();
+        }
+    }
+}

+ 67 - 0
qdb-sdk/src/main/java/com/qdb/sdk/util/JsonUtil.java

@@ -0,0 +1,67 @@
+package com.qdb.sdk.util;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.TypeReference;
+
+import java.util.List;
+
+/**
+ * JSON 工具类
+ * <p>
+ * 基于 FastJson2 提供 JSON 序列化与反序列化封装。
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+public class JsonUtil {
+
+    private JsonUtil() {
+    }
+
+    /**
+     * 将对象序列化为 JSON 字符串
+     *
+     * @param object 待序列化对象
+     * @return JSON 字符串
+     */
+    public static String toJsonString(Object object) {
+        return JSON.toJSONString(object);
+    }
+
+    /**
+     * 将 JSON 字符串解析为指定类型对象
+     *
+     * @param text  JSON 字符串
+     * @param clazz 目标类型
+     * @param <T>   泛型类型
+     * @return 解析后的对象
+     */
+    public static <T> T parseObject(String text, Class<T> clazz) {
+        return JSON.parseObject(text, clazz);
+    }
+
+    /**
+     * 将 JSON 字符串解析为泛型类型对象(支持嵌套泛型)
+     *
+     * @param text          JSON 字符串
+     * @param typeReference 类型引用
+     * @param <T>          泛型类型
+     * @return 解析后的对象
+     */
+    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
+        return JSON.parseObject(text, typeReference);
+    }
+
+    /**
+     * 将 JSON 字符串解析为指定类型的 List
+     *
+     * @param text  JSON 字符串
+     * @param clazz 元素类型
+     * @param <T>   泛型类型
+     * @return 解析后的 List
+     */
+    public static <T> List<T> parseArray(String text, Class<T> clazz) {
+        return JSON.parseArray(text, clazz);
+    }
+}

+ 86 - 0
qdb-sdk/src/main/java/com/qdb/sdk/util/SignUtil.java

@@ -0,0 +1,86 @@
+package com.qdb.sdk.util;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * 签名工具类
+ * <p>
+ * 企得宝ERP API 签名算法(MD5):
+ * <ol>
+ *   <li>将所有公共参数按照参数名的首字母先后顺序排列(字典序)</li>
+ *   <li>把排序后的结果按照参数名+参数值的方式拼接</li>
+ *   <li>拼装好的字符串首尾拼接 client_secret 进行 md5(小写32位)</li>
+ * </ol>
+ * </p>
+ *
+ * @author qdb-sdk
+ * @version 1.0.0
+ */
+public class SignUtil {
+
+    private SignUtil() {
+    }
+
+    /**
+     * 生成 API 请求签名
+     *
+     * @param params       公共参数(不包含 sign 本身)
+     * @param clientSecret 应用密钥
+     * @return 签名字符串(32位小写MD5)
+     */
+    public static String generateSign(Map<String, String> params, String clientSecret) {
+        // 使用TreeMap按key字典序排序
+        TreeMap<String, String> sortedParams = new TreeMap<>(params);
+
+        // 按照 key+value 方式拼接
+        StringBuilder sb = new StringBuilder();
+        for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
+            sb.append(entry.getKey()).append(entry.getValue());
+        }
+
+        // 首尾拼接 client_secret
+        String signStr = clientSecret + sb + clientSecret;
+
+        // MD5 加密(小写32位)
+        return md5(signStr);
+    }
+
+    /**
+     * 生成回调通知签名验证字符串
+     * <p>
+     * 与请求签名规则相同:所有参数(除 sign)按字典序排列,
+     * 按参数名+参数值拼接,两端加上 secret 后进行 MD5。
+     * </p>
+     *
+     * @param params 回调通知参数(不包含 sign)
+     * @param secret 企业密钥
+     * @return 验证签名字符串
+     */
+    public static String generateCallbackSign(Map<String, String> params, String secret) {
+        return generateSign(params, secret);
+    }
+
+    /**
+     * MD5 加密
+     *
+     * @param str 待加密字符串
+     * @return 32位小写 MD5 值
+     */
+    public static String md5(String str) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] bytes = md.digest(str.getBytes(StandardCharsets.UTF_8));
+            StringBuilder sb = new StringBuilder();
+            for (byte b : bytes) {
+                sb.append(String.format("%02x", b));
+            }
+            return sb.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MD5 加密失败", e);
+        }
+    }
+}