# 一单位一平台Java SDK

> 子应用对接一单位一平台的Java SDK，采用国密SM2+SM4数字信封加密方案

## 特性

- ✅ **国密加密**：SM2+SM4数字信封混合加密
- ✅ **自动加解密**：自动处理报文加密、解密、签名、验签
- ✅ **21个API接口**：覆盖人员、部门、角色、菜单、权限、接口管理、消息发送
- ✅ **自动重试**：支持可配置的重试机制
- ✅ **日志拦截**：完整的请求/响应日志
- ✅ **本地缓存**：支持Caffeine缓存，减少API调用
- ✅ **Spring Boot集成**：开箱即用的自动配置

## 快速开始

### 1. 添加依赖

```xml
<dependency>
    <groupId>cn.hztech</groupId>
    <artifactId>hztech-platform-sdk</artifactId>
    <version>1.0.0</version>
</dependency>
```

### 2. 配置文件

在`application.yml`中添加配置：

```yaml
hztech:
  platform:
    sdk:
      # 基础配置
      app-id: SUB_APP_001
      gateway-url: https://platform.example.com

      # 密钥配置（使用环境变量）
      private-key: ${HZTECH_PRIVATE_KEY}
      platform-public-key: ${HZTECH_PLATFORM_PUBLIC_KEY}

      # 超时配置（可选）
      connect-timeout: 10000    # 10秒
      read-timeout: 30000       # 30秒
      write-timeout: 30000      # 30秒

      # 日志配置（可选）
      enable-log: true
      log-level: INFO

      # 重试配置（可选）
      enable-retry: true
      max-retries: 3
      retry-interval: 1000

      # 时间戳容差（可选）
      timestamp-tolerance: 60000  # ±60秒
```

### 3. 环境变量配置

```bash
export HZTECH_PRIVATE_KEY="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC..."
export HZTECH_PLATFORM_PUBLIC_KEY="MFkwEQYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5s8aJ5LX8QY4ZJ5K8M5ZJL8qN3s..."
```

### 4. 使用示例

```java
@Service
public class UserService {

    @Autowired
    private UserApiClient userApiClient;

    /**
     * 查询用户详情
     */
    public User getUserDetail(String userId) {
        return userApiClient.getDetail(userId);
    }

    /**
     * 查询用户列表
     */
    public PageResult<User> getUserList(int pageNum, int pageSize) {
        UserListRequest request = new UserListRequest();
        request.setPageNum(pageNum);
        request.setPageSize(pageSize);
        request.setStatus(1); // 只查询启用的用户
        return userApiClient.getList(request);
    }

    /**
     * 搜索用户
     */
    public PageResult<User> searchUsers(String keyword) {
        UserSearchRequest request = new UserSearchRequest();
        request.setKeyword(keyword);
        request.setPageNum(1);
        request.setPageSize(20);
        return userApiClient.search(request);
    }
}
```

### 5. REST控制器示例

```java
@RestController
@RequestMapping("/api/demo")
public class DemoController {

    @Autowired
    private UserApiClient userApiClient;

    @GetMapping("/user/{userId}")
    public Result<User> getUser(@PathVariable String userId) {
        User user = userApiClient.getDetail(userId);
        return Result.success(user);
    }

    @PostMapping("/user/list")
    public Result<PageResult<User>> getUserList(@RequestBody UserListQuery query) {
        UserListRequest request = new UserListRequest();
        request.setPageNum(query.getPageNum());
        request.setPageSize(query.getPageSize());
        request.setDeptId(query.getDeptId());
        PageResult<User> result = userApiClient.getList(request);
        return Result.success(result);
    }
}
```

## API接口列表

### 人员管理API（UserApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `getDetail(userId)` | 查询人员详情 | `/hztech-application/user/detail` |
| `getList(request)` | 人员列表查询 | `/hztech-application/user/list` |
| `search(request)` | 人员搜索 | `/hztech-application/user/search` |
| `getRoles(userId)` | 查询人员角色 | `/hztech-application/user/roles` |
| `getDepartment(userId)` | 查询人员部门 | `/hztech-application/user/department` |

### 部门管理API（DepartmentApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `getDetail(deptId)` | 查询部门详情 | `/hztech-application/department/detail` |
| `getList(request)` | 部门列表查询 | `/hztech-application/department/list` |
| `getTree()` | 部门树查询 | `/hztech-application/department/tree` |
| `search(request)` | 部门搜索 | `/hztech-application/department/search` |

### 角色管理API（RoleApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `getDetail(roleId)` | 角色详情查询 | `/hztech-application/role/detail` |
| `getList(request)` | 角色列表查询 | `/hztech-application/role/list` |

### 菜单管理API（MenuApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `query(request)` | 查询菜单数据 | `/hztech-application/menu/query` |

### 权限管理API（PermissionApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `getUserMenu(userId)` | 查询用户菜单 | `/hztech-application/permission/user/menu` |
| `getRoleMenu(roleId)` | 查询角色菜单 | `/hztech-application/permission/role/menu` |

### 接口管理API（ApiManagementApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `create(request)` | 创建接口元数据 | `/hztech-interface/api` |
| `update(request)` | 更新接口元数据 | `/hztech-interface/api/update` |
| `delete(apiId)` | 删除接口元数据 | `/hztech-interface/api/delete` |
| `getDetail(apiId)` | 查询接口详情 | `/hztech-interface/api/detail` |
| `getList(request)` | 接口列表查询 | `/hztech-interface/api/list` |

### 消息发送API（MessageApiClient）

| 方法 | 说明 | 接口路径 |
|------|------|---------|
| `send(request)` | 发送单条消息 | `/hztech-message/send` |
| `batchSend(request)` | 批量发送消息 | `/hztech-message/batch-send` |

## 错误处理

### 异常类型

```java
// SDK基础异常
cn.hztech.sdk.exception.SdkException

// 加密异常
cn.hztech.sdk.exception.CryptoException

// API异常
cn.hztech.sdk.exception.ApiException
```

### 全局异常处理示例

```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ApiException.class)
    public Result<Void> handleApiException(ApiException e) {
        if (e.getMessage().contains("2501")) {
            return Result.error(404, "用户不存在");
        } else if (e.getMessage().contains("1003")) {
            return Result.error(401, "签名验证失败");
        } else if (e.getMessage().contains("1004")) {
            return Result.error(401, "解密失败");
        } else if (e.getMessage().contains("1005")) {
            return Result.error(403, "应用ID不存在");
        }
        return Result.error(500, "系统错误: " + e.getMessage());
    }

    @ExceptionHandler(CryptoException.class)
    public Result<Void> handleCryptoException(CryptoException e) {
        log.error("加密异常: {}", e.getMessage(), e);
        return Result.error(500, "数据处理异常");
    }
}
```

## 核心技术

### 加密方案

- **SM2非对称加密**：加密SM4密钥、签名验签
- **SM4对称加密**：加密业务数据
- **数字信封**：每次请求生成随机SM4密钥
- **防重放攻击**：时间戳验证 + Nonce唯一性验证

### 报文结构

```json
{
  "appId": "SUB_APP_001",
  "version": "1.0",
  "timestamp": 1735516800000,
  "nonce": "uuid-string",
  "encryptKey": "SM2加密的SM4密钥（Base64）",
  "encryptData": "SM4加密的业务数据（Base64）",
  "sign": "SM2签名（Base64）"
}
```

### 技术栈

- Java 17
- Spring Boot 3.2
- Bouncy Castle 1.77（国密加密）
- OkHttp 4.12（HTTP客户端）
- Jackson 2.16（JSON处理）
- Lombok 1.18（简化代码）

## 安全注意事项

### ⚠️ 重要提醒

- ❌ **不要**将密钥硬编码在代码中
- ❌ **不要**将密钥提交到Git仓库
- ❌ **不要**在日志中打印密钥
- ✅ **使用环境变量**存储密钥
- ✅ **使用密钥管理服务**（KMS）在生产环境
- ✅ **设置适当的文件权限**（600）

## 编译和安装

### 编译打包

```bash
cd hztech-platform-sdk
mvn clean package
```

### 安装到本地仓库

```bash
mvn clean install
```

### 发布到远程仓库

```bash
mvn deploy
```

## 测试

```bash
# 运行所有测试
mvn test

# 运行单个测试类
mvn test -Dtest=UserApiClientTest
```

## 常见问题

### Q1: 如何获取密钥？

向一单位一平台管理员申请应用ID和密钥对。

### Q2: 时间戳验证失败怎么办？

检查系统时间是否同步，确保误差在±60秒内。

### Q3: 如何启用调试日志？

在配置文件中设置：
```yaml
logging:
  level:
    cn.hztech.sdk: DEBUG
```

### Q4: 如何自定义HTTP客户端？

创建一个Bean并注入自定义的`SdkHttpClient`：

```java
@Bean
public SdkHttpClient customSdkHttpClient() {
    // 自定义配置
}
```

## 参考文档

- [接口对接方案](../../docs/01.对接文档/02.接口对接/01.接口对接方案.md)
- [人员管理接口](../../docs/01.对接文档/03.接口列表/01.人员管理接口.md)
- [部门管理接口](../../docs/01.对接文档/03.接口列表/02.部门管理接口.md)

## 许可证

本项目采用 MIT 许可证。

## 联系方式

如有问题，请联系技术支持。
