# lingxing **Repository Path**: dragonlhp/lingxing ## Basic Information - **Project Name**: lingxing - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-15 - **Last Updated**: 2026-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # dragonlhp/lingxing > 领星(Lingxing)OpenAPI PHP SDK —— 19 个业务品类的强类型客户端、统一入口,内置签名前置与令牌桶限流。 基于领星开放平台 OpenAPI 描述文件生成,对外暴露一个统一入口类 `dragonlhp\lingxing\Client`, 按业务品类(广告、财务、FBA、采购、仓库……)拆分出 19 个子客户端,方法名与接口一一对应, 请求/响应均为强类型 Model,配合 IDE 可直接补全。 --- ## 一、项目用途 本 SDK 用于以面向对象的方式调用领星 ERP 开放平台接口,替代手工拼装 query、签名、限流与重试的工作。核心能力: - **统一入口**:`Client::getInstance($config)` 单例获取,一处配置全局复用。 - **19 个业务品类**:每个品类一个子客户端,惰性创建并缓存,接口方法强类型。 - **自动签名前置**:业务请求自动注入 `access_token` / `timestamp` / `app_key` / `sign`,业务代码无需关心签名算法。 - **令牌桶限流**:以 `appId + 接口 url` 为维度自研令牌桶,命中领星限流错误码后自动退避重试,保护接口不被封禁。 - **令牌生命周期管理**:`generateAccessToken` / `refreshToken` / `ensureAccessToken` 覆盖获取、续约、兜底重建三种场景。 - **可测试**:HTTP 客户端支持注入(PSR-18),单元测试不访问真实网络。 > 适用场景:凡需要对接领星开放平台(拉取广告报表、FBA 库存、财务数据,或推送产品/Listing 等)的 PHP 项目。 --- ## 二、环境要求 | 依赖 | 版本 | 说明 | | --- | --- | --- | | PHP | >= 7.4 | 使用类型属性、箭头函数等特性 | | ext-json | * | JSON 解析 | | ext-openssl | * | 签名 AES-128-ECB 加密 | | guzzlehttp/guzzle | ^7.0 | 默认 PSR-18 HTTP 客户端 | | jane-php/open-api-runtime | ^7.5 | 生成代码运行时 | | php-http/client-common / discovery / multipart-stream-builder | ^2.0 / ^1.14 / ^1.0 | PSR-18/17 自动发现与插件 | | psr/http-client / http-factory / http-message | ^1.0 | PSR 接口 | | symfony/options-resolver、symfony/serializer | ^5.4 \|\| ^6.0 | 参数解析与序列化 | | topthink/framework | ^6.0 | 令牌桶状态依赖 `think\facade\Cache` | > 注意:因令牌桶限流状态通过 `think\facade\Cache` 存储,本 SDK 目前运行于 **ThinkPHP 6** 环境; > 独立的 PHP 项目使用前需先引入 ThinkPHP 6,或自行替换 `src/Support/TokenBucket.php` 中的缓存实现。 --- ## 三、安装 ### 方式一:Composer 引入(作为 Gitee VCS 依赖) 在业务项目根目录的 `composer.json` 中登记仓库地址: ```json { "repositories": [ { "type": "vcs", "url": "https://gitee.com/dragonlhp/lingxing.git" } ] } ``` 然后安装: ```bash composer require dragonlhp/lingxing:dev-master ``` ### 方式二:ThinkPHP 6 扩展目录 克隆到项目的 `extend/Lingxing` 目录,并在业务项目 `composer.json` 的 `autoload.psr-4` 中登记命名空间: ```json { "autoload": { "psr-4": { "dragonlhp\\lingxing\\": "extend/Lingxing/src/" } } } ``` 修改后执行: ```bash composer dump-autoload ``` --- ## 四、配置 SDK 支持**两种配置风格**,并在传入配置为空时**自动回退读取 `config('lingxing')`**。 ### 1. ThinkPHP 配置文件 新建 `config/lingxing.php`: ```php [ 'url' => 'https://openapi.lingxing.com', 'appId' => Env::get('lingxing.app_id'), 'appSecret' => Env::get('lingxing.app_secret'), ], ]; ``` ### 2. 环境变量(.env) 敏感凭据放入 `.env`,避免提交到仓库: ```ini [LINGXING] app_id = your_app_id app_secret = your_app_secret ``` ### 3. 配置键名对照 | 逻辑含义 | 简化风格键名 | ThinkPHP 风格键名 | 是否必填 | | --- | --- | --- | --- | | 接口地址 | `host` | `url` | 否,缺省内置 `https://openapi.lingxing.com` | | 应用 ID | `app_id` | `appId` | 是 | | 应用密钥 | `app_secret` | `appSecret` | 是 | > 仅 `app_id` / `app_secret` 为必填;缺失时构造函数抛出 `InvalidArgumentException` 并提示缺失项。 > 接口地址已内置为常量 `dragonlhp\lingxing\Client::DEFAULT_HOST`(`https://openapi.lingxing.com`), > 默认无需填写,仅当对接私有化部署或代理地址时才需显式传入 `host` / `url`。 --- ## 五、快速开始 ```php 'your_app_id', 'app_secret' => 'your_app_secret', ]); // 2) 确保拿到有效令牌(内部按 60s 安全余量自动续约/重建) $client->ensureAccessToken(); // 3) 通过品类子客户端调用具体接口,请求体为强类型 Model $body = new \dragonlhp\lingxing\Ad\Model\BasicOpenAdReportAnalyzeKeywordPostBody(); // ... 按需 set 业务字段 $res = $client->ad()->postBasicopenAdreportAnalyzeKeyword($body); ``` **手动管理令牌**(如需从外部缓存恢复,避免每次进程冷启动都重新获取): ```php $client->setAccessToken($accessToken, $refreshToken, $expireAt); echo $client->getAccessToken(); echo $client->getRefreshToken(); echo $client->getExpireAt(); ``` > 令牌仅保存在**当前进程内存**中,不落库、不写 Cache。每个 `refresh_token` 只能用一次。 --- ## 六、业务品类(19 个) 通过总入口的以下方法获取对应子客户端: | 品类 | 访问方法 | 命名空间 | | --- | --- | --- | | 广告 | `ad()` | `dragonlhp\lingxing\Ad` | | 亚马逊源数据 | `amazonSource()` | `dragonlhp\lingxing\AmazonSource` | | 授权 | `auth()` | `dragonlhp\lingxing\Auth` | | 基础数据 | `basicData()` | `dragonlhp\lingxing\BasicData` | | 客服 | `customerService()` | `dragonlhp\lingxing\CustomerService` | | FBA | `fba()` | `dragonlhp\lingxing\Fba` | | 财务 | `finance()` | `dragonlhp\lingxing\Finance` | | 物流 | `logistics()` | `dragonlhp\lingxing\Logistics` | | 多平台 | `multiPlatform()` | `dragonlhp\lingxing\MultiPlatform` | | 运营计划 | `opsPlan()` | `dragonlhp\lingxing\OpsPlan` | | 产品 | `product()` | `dragonlhp\lingxing\Product` | | 采购 | `purchase()` | `dragonlhp\lingxing\Purchase` | | 补货建议 | `replenishAdvice()` | `dragonlhp\lingxing\ReplenishAdvice` | | 补货限制 | `replenishLimit()` | `dragonlhp\lingxing\ReplenishLimit` | | 销售 | `sales()` | `dragonlhp\lingxing\Sales` | | 统计 | `statistics()` | `dragonlhp\lingxing\Statistics` | | 工具 | `tool()` | `dragonlhp\lingxing\Tool` | | VC | `vc()` | `dragonlhp\lingxing\Vc` | | 仓库 | `warehouse()` | `dragonlhp\lingxing\Warehouse` | 每个品类下按 `Endpoint`(接口实现)、`Model`(请求/响应模型)、`Normalizer`(反序列化)组织。 --- ## 七、核心机制 ### 1. 签名前置(`src/Support/Signer.php`、`RequestPipeline.php`) 业务请求发出前,管线会合并 query 与 JSON body 参数,追加公共参数 `access_token` / `timestamp` / `app_key`,并按领星规则生成 `sign`: 1. 参数 key 按 ASCII 升序排序; 2. 以 `key=value&...` 拼接(空字符串不参与,数组去 null 后 `json_encode` 参与); 3. 拼接串 MD5 后转大写; 4. 以 `app_id` 为密钥做 `AES-128-ECB` 加密后 `base64_encode`。 > 授权接口(`/api/auth-server/oauth/*`)不注入公共参数、不签名。 ### 2. 令牌桶限流(`src/Support/TokenBucket.php`) - 维度:`appId + 接口 url`; - 补充速率:`容量 / 120s`,请求完成后令牌归还; - 最小间隔:上次为单店铺间隔 1s,多店铺(`sid` 含英文逗号)间隔 10s; - 命中限流错误码 `3001008` / `103` 时,最多重试 3 次、每次等待 1s; - 状态经 `think\facade\Cache` 存储,TTL 略大于回收时间。 --- ## 八、目录结构 ``` extend/Lingxing/ ├── composer.json # 包定义(dragonlhp/lingxing)与 test 脚本 ├── phpunit.xml # PHPUnit 配置 ├── openapi.json # 领星 OpenAPI 描述文件(生成来源) ├── README.md ├── src/ │ ├── Client.php # 统一入口(单例) │ ├── Support/ # 支撑层:Context / Signer / TokenBucket / RequestPipeline │ └── {品类}/ # 19 个品类:Client / Endpoint / Model / Normalizer / Runtime └── tests/ # PHPUnit 单元测试(bootstrap.php 为引导文件) ``` --- ## 九、测试 测试通过注入桩 HTTP 客户端完成,不访问真实网络。配置见 `phpunit.xml`,引导文件 `tests/bootstrap.php`。 ```bash # 安装开发依赖后执行(composer scripts 已内置 test 命令) composer install composer test # 或直接调用 phpunit vendor/bin/phpunit --configuration phpunit.xml ``` > SDK 内嵌于其他项目(如本仓库位于 `extend/Lingxing`)且未单独安装依赖时, > `tests/bootstrap.php` 会自动回退到上层项目的 `vendor/autoload.php`, > 此时可直接用上层 phpunit 运行:`php ../../vendor/bin/phpunit --configuration phpunit.xml`。 覆盖内容:配置校验、令牌设置/读取、`ensureAccessToken` 短路与续约、`generateAccessToken` 解析、 品类子客户端惰性缓存,以及签名、令牌桶、请求管线。全部用例应全部通过: ``` OK (24 tests, 70 assertions) ``` --- ## 十、命名空间与常见问题 ### 1. 命名空间约定 SDK 统一使用 `dragonlhp\lingxing\` 命名空间,与目录一一对应: | 内容 | 命名空间 | 目录 | | --- | --- | --- | | 源码 | `dragonlhp\lingxing\` | `src/` | | 测试 | `dragonlhp\lingxing\Tests\` | `tests/` | > 若业务代码中仍存在 `cherp\lingxing\...` 的引用,请一并替换为 `dragonlhp\lingxing\...`。 ### 2. 已修复:部分品类残留旧命名空间 早期由 OpenAPI 描述文件生成的代码,在 **Finance、Fba、Purchase、CustomerService、Logistics、 ReplenishAdvice、ReplenishLimit、MultiPlatform** 等品类中残留了旧命名空间 `cherp\lingxing`(约 780 处)。 由于这些类的实际命名空间与 PSR-4 映射不一致,对应品类的子客户端无法被自动加载,典型报错: ```text Error: Class "cherp\lingxing\Finance\Runtime\Client\Client" not found ``` 现已将全仓库统一为 `dragonlhp\lingxing`,单元测试全量通过。使用修复前版本的项目,升级到本版本即可解决。 ### 3. 报「Class not found / 类找不到」时 - **Composer 安装方式**:确认已执行 `composer dump-autoload` 重新生成自动加载映射。 - **扩展目录内嵌方式**:确认业务项目 `composer.json` 的 `autoload.psr-4` 已登记 `"dragonlhp\\lingxing\\": "extend/Lingxing/src/"`,并重新执行 `composer dump-autoload`。 - **运行测试时**:`tests/bootstrap.php` 会在缺少 SDK 自身 `vendor` 时回退到上层项目 `vendor/autoload.php`,因此请确保上层项目已 `composer install`。 --- ## 十一、许可 MIT License。