doudian/hyperf-sdk

抖店开放平台 PHP SDK for Hyperf Framework - 支持协程、依赖注入、连接池的高性能 SDK

v1.2.0 2025-07-08 12:02 UTC

This package is auto-updated.

Last update: 2025-07-08 12:03:45 UTC


README

License PHP Version Hyperf Version

这是一个专为 Hyperf 框架优化的抖店开放平台 PHP SDK,支持协程并发、依赖注入、连接池等 Hyperf 特性。

特性

  • 协程支持: 基于 Hyperf Guzzle 的协程 HTTP 客户端
  • 依赖注入: 完全集成 Hyperf DI 容器
  • 连接池: 支持 HTTP 连接池,提高性能
  • 多应用配置: 支持同时管理多个抖店应用
  • 类型安全: 使用 PHP 8+ 严格类型声明
  • PSR 标准: 遵循 PSR-4 自动加载和 PSR-1 编码标准
  • 零依赖冲突: 与原版 SDK 可共存

安装

composer require doudian/hyperf-sdk

配置

1. 发布配置文件

php bin/hyperf.php vendor:publish doudian/hyperf-sdk

2. 编辑配置文件

编辑 config/autoload/doudian.php:

<?php

return [
    'default' => [
        'app_key' => env('DOUDIAN_APP_KEY', ''),
        'app_secret' => env('DOUDIAN_APP_SECRET', ''),
        'open_request_url' => env('DOUDIAN_OPEN_REQUEST_URL', 'https://openapi-fxg.jinritemai.com'),
        'http_connect_timeout' => (int) env('DOUDIAN_HTTP_CONNECT_TIMEOUT', 3),
        'http_read_timeout' => (int) env('DOUDIAN_HTTP_READ_TIMEOUT', 10),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => 60.0,
        ],
    ],
    // 支持多个应用配置
    'shops' => [
        'shop1' => [
            'app_key' => 'your_app_key_1',
            'app_secret' => 'your_app_secret_1',
            // 其他配置项...
        ],
        'shop2' => [
            'app_key' => 'your_app_key_2',
            'app_secret' => 'your_app_secret_2',
            // 其他配置项...
        ],
    ],
];

3. 环境变量

.env 文件中添加:

DOUDIAN_APP_KEY=your_app_key
DOUDIAN_APP_SECRET=your_app_secret
DOUDIAN_OPEN_REQUEST_URL=https://openapi-fxg.jinritemai.com
DOUDIAN_HTTP_CONNECT_TIMEOUT=3
DOUDIAN_HTTP_READ_TIMEOUT=10

快速开始

1. 在控制器中使用

<?php

namespace App\Controller;

use Doudian\Core\Contract\ClientFactoryInterface;
use Doudian\Core\AccessTokenBuilder;
use Doudian\Api\Product\ProductListRequest;
use Doudian\Api\Product\Param\ProductListParam;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;

#[Controller]
class DoudianController
{
    public function __construct(
        private ClientFactoryInterface $doudianClientFactory
    ) {}

    #[GetMapping('/doudian/products')]
    public function getProducts(): array
    {
        // 获取客户端(使用默认配置)
        $client = $this->doudianClientFactory->create();
        
        // 或者获取指定应用的客户端
        // $client = $this->doudianClientFactory->get('shop1');
        
        // 构建访问令牌
        $accessToken = AccessTokenBuilder::parse('your_access_token');
        
        // 构建请求
        $request = new ProductListRequest();
        $param = new ProductListParam();
        $param->page = 0;
        $param->size = 20;
        $param->status = 1; // 上架状态
        $request->setParam($param);
        
        // 发起请求
        $response = $client->request($request, $accessToken);
        
        return ['data' => $response];
    }
}

2. 在服务类中使用

<?php

namespace App\Service;

use Doudian\Core\Contract\ClientFactoryInterface;
use Doudian\Core\AccessToken;
use Doudian\Api\Product\ProductListRequest;
use Doudian\Api\Product\Param\ProductListParam;
use Hyperf\Di\Annotation\Inject;

class DoudianService
{
    #[Inject]
    private ClientFactoryInterface $clientFactory;

    public function getProductList(AccessToken $accessToken, int $page = 0, int $size = 10): object
    {
        $client = $this->clientFactory->create();
        
        $request = new ProductListRequest();
        $param = new ProductListParam();
        $param->page = $page;
        $param->size = $size;
        $request->setParam($param);
        
        return $client->request($request, $accessToken);
    }
    
    public function getShop1ProductList(AccessToken $accessToken): object
    {
        // 使用指定的应用配置
        $client = $this->clientFactory->get('shop1');
        
        $request = new ProductListRequest();
        $param = new ProductListParam();
        $param->page = 0;
        $param->size = 100;
        $request->setParam($param);
        
        return $client->request($request, $accessToken);
    }
}

访问令牌管理

获取访问令牌

use Doudian\Core\AccessTokenBuilder;

// 方式 1: 使用授权码获取访问令牌
$accessToken = AccessTokenBuilder::build(
    'your_authorization_code', 
    AccessTokenBuilder::ACCESS_TOKEN_CODE
);

// 方式 2: 使用 shop_id 获取访问令牌(自用型应用)
$accessToken = AccessTokenBuilder::build(
    'your_shop_id', 
    AccessTokenBuilder::ACCESS_TOKEN_SHOP_ID
);

// 方式 3: 使用指定应用配置
$accessToken = AccessTokenBuilder::build(
    'your_authorization_code', 
    AccessTokenBuilder::ACCESS_TOKEN_CODE,
    'shop1'  // 应用配置名称
);

if ($accessToken->isSuccess()) {
    echo "访问令牌: " . $accessToken->getAccessToken();
    echo "有效期: " . $accessToken->getExpireIn() . "";
    echo "刷新令牌: " . $accessToken->getRefreshToken();
} else {
    echo "获取失败: " . $accessToken->getMsg();
}

刷新访问令牌

// 使用刷新令牌刷新访问令牌
$refreshToken = 'your_refresh_token';
$accessToken = AccessTokenBuilder::refresh($refreshToken);

// 或者使用 AccessToken 对象直接刷新
$accessToken = AccessTokenBuilder::refresh($existingAccessToken);

解析已有的访问令牌

// 当你已经有访问令牌字符串时
$accessToken = AccessTokenBuilder::parse('your_access_token_string');

API 调用示例

产品相关 API

use Doudian\Api\Product\ProductListRequest;
use Doudian\Api\Product\Param\ProductListParam;

// 获取产品列表
$request = new ProductListRequest();
$param = new ProductListParam();
$param->page = 0;
$param->size = 20;
$param->status = 1; // 上架状态
$param->title = '搜索标题'; // 可选
$request->setParam($param);

$response = $client->request($request, $accessToken);

自定义 API 请求

如果您需要调用尚未封装的 API,可以继承 AbstractRequest 类:

<?php

namespace App\Api\Custom;

use Doudian\Core\AbstractRequest;

class CustomApiRequest extends AbstractRequest
{
    public function getUrlPath(): string
    {
        return '/your/custom/api/path';
    }
}

// 使用
$request = new CustomApiRequest();
$param = (object) [
    'your_param' => 'value',
    'another_param' => 123,
];
$request->setParam($param);

$response = $client->request($request, $accessToken);

错误处理

try {
    $response = $client->request($request, $accessToken);
    
    // 检查业务状态码
    if (property_exists($response, 'code') && $response->code == 10000) {
        // 成功
        $data = $response->data;
    } else {
        // 业务错误
        $errorMsg = $response->message ?? '未知错误';
        throw new \Exception("API 调用失败: $errorMsg");
    }
} catch (\GuzzleHttp\Exception\RequestException $e) {
    // HTTP 请求异常
    throw new \Exception("网络请求失败: " . $e->getMessage());
} catch (\Exception $e) {
    // 其他异常
    throw new \Exception("调用异常: " . $e->getMessage());
}

配置说明

配置项 类型 默认值 说明
app_key string - 应用 Key(必填)
app_secret string - 应用密钥(必填)
open_request_url string https://openapi-fxg.jinritemai.com API 请求地址
http_connect_timeout int 3 连接超时时间(秒)
http_read_timeout int 10 读取超时时间(秒)
pool.min_connections int 1 连接池最小连接数
pool.max_connections int 10 连接池最大连接数
pool.connect_timeout float 10.0 连接池连接超时
pool.wait_timeout float 3.0 连接池等待超时
pool.heartbeat int -1 心跳间隔(-1 为禁用)
pool.max_idle_time float 60.0 最大空闲时间

与原版 SDK 的区别

特性 原版 SDK Hyperf SDK
HTTP 客户端 cURL Hyperf Guzzle (协程)
依赖注入 不支持 完全支持
连接池 不支持 支持
多应用配置 不支持 支持
协程安全
类型声明 部分 严格类型
PSR 规范 部分 完全遵循

性能优化建议

  1. 使用连接池: 在高并发场景下,合理配置连接池参数
  2. 缓存访问令牌: 访问令牌有效期较长,建议缓存避免频繁获取
  3. 批量请求: 利用协程特性并发调用多个 API
  4. 错误重试: 对于网络错误,建议实现重试机制
// 协程并发示例
use Hyperf\Coroutine\Parallel;

$parallel = new Parallel();

$parallel->add(function () use ($client, $accessToken) {
    // 并发请求 1
    $request1 = new ProductListRequest();
    // ... 设置参数
    return $client->request($request1, $accessToken);
});

$parallel->add(function () use ($client, $accessToken) {
    // 并发请求 2
    $request2 = new AnotherRequest();
    // ... 设置参数
    return $client->request($request2, $accessToken);
});

$results = $parallel->wait();

常见问题

Q: 如何处理访问令牌过期?

A: 检查响应中的错误码,如果是令牌过期相关错误,使用刷新令牌重新获取:

if (property_exists($response, 'code') && in_array($response->code, [40006, 40007])) {
    // 令牌过期,尝试刷新
    $newAccessToken = AccessTokenBuilder::refresh($refreshToken);
    // 重新发起请求
}

Q: 如何在不同的 Shop 之间切换?

A: 在配置文件中定义多个应用,然后通过名称获取对应的客户端:

$shop1Client = $this->clientFactory->get('shop1');
$shop2Client = $this->clientFactory->get('shop2');

Q: 如何调试 API 请求?

A: 可以在 Hyperf 日志配置中增加 Guzzle 的请求日志:

// config/autoload/logger.php
'guzzle' => [
    'handler' => [
        'class' => \Monolog\Handler\StreamHandler::class,
        'constructor' => [
            'stream' => BASE_PATH . '/runtime/logs/guzzle.log',
            'level' => \Monolog\Logger::DEBUG,
        ],
    ],
],

许可证

MIT License. 详见 LICENSE 文件。

贡献

欢迎提交 Issue 和 Pull Request!

更新日志

v1.0.0

  • 初始版本发布
  • 支持 Hyperf 3.0+ 框架
  • 实现协程 HTTP 客户端
  • 支持依赖注入和多应用配置