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

239 lines
6.4 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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);
}
}