pangu-user-platform/scripts/test/OpenApiClient.php

239 lines
6.4 KiB
PHP
Raw Normal View History

<?php
/**
* 盘古开放接口 PHP 客户端
*
* 功能:
* 1. 自动计算 MD5 签名
* 2. 添加必要的请求头
* 3. 支持 GET/POST 请求
* 4. 返回 JSON 解析后的数据
*
* @author pangu
* @date 2026-02-04
*/
class OpenApiClient
{
/**
* API 基础 URL
*/
private string $baseUrl;
/**
* 应用编码
*/
private string $appCode;
/**
* 应用密钥(需保密)
*/
private string $appSecret;
/**
* 调试模式
*/
private bool $debug;
/**
* 构造函数
*
* @param string $baseUrl API 基础 URLhttp://localhost:8080
* @param string $appCode 应用编码
* @param string $appSecret 应用密钥
* @param bool $debug 是否开启调试模式
*/
public function __construct(string $baseUrl, string $appCode, string $appSecret, bool $debug = false)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->appCode = $appCode;
$this->appSecret = $appSecret;
$this->debug = $debug;
}
/**
* 发送 GET 请求
*
* @param string $path 接口路径(如:/open/api/student/list
* @param array $params 请求参数
* @return array 响应数据
* @throws Exception
*/
public function get(string $path, array $params = []): array
{
return $this->request('GET', $path, $params);
}
/**
* 发送 POST 请求
*
* @param string $path 接口路径
* @param array $params 请求参数
* @return array 响应数据
* @throws Exception
*/
public function post(string $path, array $params = []): array
{
return $this->request('POST', $path, $params);
}
/**
* 发送请求
*
* @param string $method 请求方法GET/POST
* @param string $path 接口路径
* @param array $params 请求参数
* @return array 响应数据
* @throws Exception
*/
private function request(string $method, string $path, array $params): array
{
// 1. 计算签名
$timestamp = $this->getTimestamp();
$sign = $this->calculateSign($params);
// 2. 构建请求头
$headers = [
'X-App-Id: ' . $this->appCode,
'X-Timestamp: ' . $timestamp,
'X-Sign: ' . $sign,
'Content-Type: application/json'
];
// 3. 构建完整 URL
$url = $this->baseUrl . $path;
if ($method === 'GET' && !empty($params)) {
$url .= '?' . http_build_query($params);
}
// 4. 发送请求
if ($this->debug) {
$this->printDebugInfo($method, $url, $headers, $params, $sign);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception("请求失败: {$error}");
}
if ($this->debug) {
echo "\n【响应状态码】{$httpCode}\n";
echo "【响应内容】\n{$response}\n\n";
}
// 5. 解析响应
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("响应解析失败: " . json_last_error_msg());
}
return $data;
}
/**
* 计算签名
*
* 签名算法:
* 1. 将所有请求参数按 key ASCII 码升序排序
* 2. 按照 key1=value1&key2=value2... 格式拼接
* 3. 末尾追加 &appSecret=xxx
* 4. 对整个字符串进行 MD5 加密,转大写
*
* @param array $params 请求参数
* @return string 签名字符串
*/
private function calculateSign(array $params): string
{
// 1. 按 key 升序排序
ksort($params);
// 2. 拼接参数
$signStr = '';
foreach ($params as $key => $value) {
// 跳过空值
if ($value === '' || $value === null) {
continue;
}
if ($signStr !== '') {
$signStr .= '&';
}
$signStr .= $key . '=' . $value;
}
// 3. 追加密钥
if ($signStr !== '') {
$signStr .= '&';
}
$signStr .= 'appSecret=' . $this->appSecret;
// 4. MD5 加密并转大写
return strtoupper(md5($signStr));
}
/**
* 获取当前时间戳(毫秒)
*
* @return int 时间戳
*/
private function getTimestamp(): int
{
return intval(microtime(true) * 1000);
}
/**
* 打印调试信息
*/
private function printDebugInfo(string $method, string $url, array $headers, array $params, string $sign): void
{
echo "========================================\n";
echo "【开放接口调用】\n";
echo "========================================\n";
echo "【请求方法】{$method}\n";
echo "【请求 URL】{$url}\n";
echo "【请求头】\n";
foreach ($headers as $header) {
echo " {$header}\n";
}
echo "【请求参数】\n";
echo json_encode($params, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
// 显示签名计算过程
ksort($params);
$signStr = '';
foreach ($params as $key => $value) {
if ($value === '' || $value === null) continue;
if ($signStr !== '') $signStr .= '&';
$signStr .= $key . '=' . $value;
}
if ($signStr !== '') $signStr .= '&';
$signStr .= 'appSecret=' . $this->appSecret;
echo "【签名计算】\n";
echo " 签名字符串: {$signStr}\n";
echo " MD5 结果: {$sign}\n";
echo "========================================\n";
}
/**
* 静态工厂方法
*/
public static function create(string $baseUrl, string $appCode, string $appSecret, bool $debug = false): self
{
return new self($baseUrl, $appCode, $appSecret, $debug);
}
}