BaseRequest.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. namespace App\Module\ThirdParty\Services;
  3. use App\Module\ThirdParty\Dto\Config;
  4. use App\Module\ThirdParty\Dto\Credential;
  5. use App\Module\ThirdParty\Models\ThirdPartyService as ServiceModel;
  6. use App\Module\ThirdParty\Models\ThirdPartyCredential;
  7. use App\Module\ThirdParty\Models\ThirdPartyLog;
  8. use App\Module\ThirdParty\Models\ThirdPartyQuota;
  9. use App\Module\ThirdParty\Enums\SERVICE_STATUS;
  10. use App\Module\ThirdParty\Enums\LOG_LEVEL;
  11. use Illuminate\Support\Facades\Http;
  12. use Illuminate\Support\Facades\Cache;
  13. use Illuminate\Http\Client\Response;
  14. /**
  15. * 第三方请求基类
  16. *
  17. * 抽象化请求概念,不局限于HTTP请求,扩展到调用包的请求方法
  18. * 基类处理配置读取、配额管理、日志记录等通用功能
  19. */
  20. abstract class BaseRequest
  21. {
  22. /**
  23. * 服务代码
  24. */
  25. protected string $serviceCode;
  26. /**
  27. * 服务配置
  28. */
  29. protected ?ServiceModel $service = null;
  30. /**
  31. * 认证凭证
  32. */
  33. protected ?ThirdPartyCredential $credential = null;
  34. /**
  35. * 请求ID
  36. */
  37. protected string $requestId;
  38. /**
  39. * 开始时间
  40. */
  41. protected float $startTime;
  42. /**
  43. * 构造函数
  44. *
  45. * @param string $serviceCode 服务代码
  46. */
  47. public function __construct(string $serviceCode)
  48. {
  49. $this->serviceCode = $serviceCode;
  50. $this->requestId = uniqid('req_', true);
  51. $this->startTime = microtime(true);
  52. // 初始化服务配置
  53. $this->initializeService();
  54. }
  55. /**
  56. * 初始化服务配置
  57. *
  58. * @throws \Exception
  59. */
  60. protected function initializeService(): void
  61. {
  62. $this->service = ServiceModel::where('code', $this->serviceCode)->first();
  63. if (!$this->service) {
  64. throw new \Exception("服务 {$this->serviceCode} 不存在");
  65. }
  66. if (!$this->service->canCallApi()) {
  67. throw new \Exception("服务 {$this->serviceCode} 当前不可用,状态:{$this->service->getStatusLabel()}");
  68. }
  69. // 获取认证凭证 - 智能环境匹配
  70. $this->credential = $this->getSmartCredential();
  71. if (!$this->credential) {
  72. throw new \Exception("服务 {$this->serviceCode} 没有可用的认证凭证");
  73. }
  74. }
  75. /**
  76. * 执行请求
  77. *
  78. * @param array $params 请求参数
  79. * @return array
  80. * @throws \Exception
  81. */
  82. public function request(array $params = []): array
  83. {
  84. // 检查配额
  85. if (!$this->checkQuota()) {
  86. throw new \Exception("服务 {$this->serviceCode} 配额已用完");
  87. }
  88. try {
  89. // 执行具体的请求逻辑
  90. $result = $this->handler($params);
  91. // 记录成功日志
  92. $this->logRequest($params, $result, true);
  93. // 更新配额
  94. $this->updateQuota();
  95. // 更新凭证使用统计
  96. $this->credential->updateUsageStats();
  97. return $result;
  98. } catch (\Exception $e) {
  99. // 记录失败日志
  100. $this->logRequest($params, [ 'error' => $e->getMessage() ], false);
  101. throw $e;
  102. }
  103. }
  104. /**
  105. * 具体的请求处理逻辑(由子类实现)
  106. *
  107. * @param array $params 请求参数
  108. * @return array
  109. */
  110. abstract protected function handler(array $params = []): array;
  111. /**
  112. * 检查配额
  113. *
  114. * @return bool
  115. */
  116. protected function checkQuota(): bool
  117. {
  118. return QuotaService::canUse($this->service->id);
  119. }
  120. /**
  121. * 更新配额
  122. */
  123. protected function updateQuota(): void
  124. {
  125. QuotaService::use($this->service->id);
  126. }
  127. /**
  128. * 记录请求日志
  129. *
  130. * @param array $params 请求参数
  131. * @param array $result 响应结果
  132. * @param bool $success 是否成功
  133. */
  134. protected function logRequest(array $params, array $result, bool $success): void
  135. {
  136. $responseTime = (int)((microtime(true) - $this->startTime) * 1000);
  137. ThirdPartyLog::create([
  138. 'service_id' => $this->service->id,
  139. 'credential_id' => $this->credential->id,
  140. 'request_id' => $this->requestId,
  141. 'method' => 'PACKAGE_CALL',
  142. 'url' => $this->serviceCode,
  143. 'request_headers' => json_encode([]),
  144. 'request_body' => json_encode($params),
  145. 'response_status' => $success ? 200 : 500,
  146. 'response_headers' => json_encode([]),
  147. 'response_body' => json_encode($result),
  148. 'response_time' => $responseTime,
  149. 'success' => $success,
  150. 'error_message' => $success ? null : ($result['error'] ?? '未知错误'),
  151. 'level' => $success ? LOG_LEVEL::INFO->value : LOG_LEVEL::ERROR->value,
  152. 'ip_address' => request()->ip(),
  153. 'user_agent' => request()->userAgent(),
  154. 'called_at' => now(),
  155. ]);
  156. }
  157. /**
  158. * 获取服务配置
  159. *
  160. * @param string|null $key 配置键名,为空则返回全部配置
  161. * @return mixed
  162. */
  163. protected function getConfig(?string $key = null)
  164. {
  165. $config = $this->service->config ?? [];
  166. if ($key === null) {
  167. return $config;
  168. }
  169. return $config[$key] ?? null;
  170. }
  171. /**
  172. * 获取服务信息
  173. *
  174. * @return ServiceModel
  175. */
  176. protected function getService(): ServiceModel
  177. {
  178. return $this->service;
  179. }
  180. /**
  181. * 获取认证凭证
  182. *
  183. * @return ThirdPartyCredential
  184. */
  185. protected function getCredential(): ThirdPartyCredential
  186. {
  187. return $this->credential;
  188. }
  189. /**
  190. * 获取请求ID
  191. *
  192. * @return string
  193. */
  194. protected function getRequestId(): string
  195. {
  196. return $this->requestId;
  197. }
  198. /**
  199. * 智能获取认证凭证
  200. *
  201. * 按优先级查找可用的认证凭证:
  202. * 1. 当前应用环境对应的凭证
  203. * 2. testing环境凭证
  204. * 3. production环境凭证
  205. * 4. 任何可用的激活凭证
  206. *
  207. * @return ThirdPartyCredential|null
  208. */
  209. protected function getSmartCredential(): ?ThirdPartyCredential
  210. {
  211. $appEnv = app()->environment();
  212. // 环境映射:将Laravel环境映射到凭证环境
  213. $envMapping = [
  214. 'local' => 'testing',
  215. 'development' => 'testing',
  216. 'testing' => 'testing',
  217. 'staging' => 'staging',
  218. 'production' => 'production',
  219. ];
  220. $preferredEnv = $envMapping[$appEnv] ?? 'production';
  221. // 按优先级尝试获取凭证
  222. $environments = [$preferredEnv, 'testing', 'production'];
  223. $environments = array_unique($environments); // 去重
  224. foreach ($environments as $env) {
  225. $credential = $this->service->getActiveCredential($env);
  226. if ($credential) {
  227. return $credential;
  228. }
  229. }
  230. // 如果以上都没找到,尝试获取任何可用的激活凭证
  231. return $this->service->credentials()
  232. ->where('is_active', true)
  233. ->where(function ($query) {
  234. $query->whereNull('expires_at')
  235. ->orWhere('expires_at', '>', now());
  236. })
  237. ->first();
  238. }
  239. }