ThirdPartyLog.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <?php
  2. namespace App\Module\ThirdParty\Models;
  3. use UCore\ModelCore;
  4. use App\Module\ThirdParty\Enums\LOG_LEVEL;
  5. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  6. /**
  7. * 第三方服务调用日志模型
  8. *
  9. * field start
  10. * @property int $id 主键ID
  11. * @property int $service_id 服务ID
  12. * @property int $credential_id 凭证ID
  13. * @property string $request_id 请求ID(用于追踪)
  14. * @property string $method 请求方法
  15. * @property string $url 请求URL
  16. * @property array $headers 请求头
  17. * @property array $params 请求参数
  18. * @property string $body 请求体
  19. * @property int $response_status 响应状态码
  20. * @property array $response_headers 响应头
  21. * @property string $response_body 响应体
  22. * @property int $response_time 响应时间(毫秒)
  23. * @property string $error_message 错误信息
  24. * @property string $level 日志级别
  25. * @property int $user_id 用户ID
  26. * @property string $ip_address IP地址
  27. * @property string $user_agent User Agent
  28. * @property \Carbon\Carbon $created_at 创建时间
  29. * field end
  30. */
  31. class ThirdPartyLog extends ModelCore
  32. {
  33. /**
  34. * 数据表名
  35. *
  36. * @var string
  37. */
  38. protected $table = 'thirdparty_logs';
  39. /**
  40. * 禁用updated_at字段
  41. *
  42. * @var bool
  43. */
  44. public $timestamps = false;
  45. /**
  46. * 可批量赋值的属性
  47. *
  48. * @var array
  49. */
  50. protected $fillable = [
  51. 'service_id',
  52. 'credential_id',
  53. 'request_id',
  54. 'method',
  55. 'url',
  56. 'headers',
  57. 'params',
  58. 'body',
  59. 'response_status',
  60. 'response_headers',
  61. 'response_body',
  62. 'response_time',
  63. 'error_message',
  64. 'level',
  65. 'user_id',
  66. 'ip_address',
  67. 'user_agent',
  68. 'created_at',
  69. ];
  70. /**
  71. * 属性类型转换
  72. *
  73. * @var array
  74. */
  75. protected $casts = [
  76. 'service_id' => 'integer',
  77. 'credential_id' => 'integer',
  78. 'headers' => 'array',
  79. 'params' => 'array',
  80. 'response_status' => 'integer',
  81. 'response_headers' => 'array',
  82. 'response_time' => 'integer',
  83. 'user_id' => 'integer',
  84. 'created_at' => 'datetime',
  85. ];
  86. /**
  87. * 获取日志级别枚举
  88. *
  89. * @return LOG_LEVEL
  90. */
  91. public function getLogLevelEnum(): LOG_LEVEL
  92. {
  93. return LOG_LEVEL::from($this->level);
  94. }
  95. /**
  96. * 获取日志级别标签
  97. *
  98. * @return string
  99. */
  100. public function getLevelLabel(): string
  101. {
  102. return $this->getLogLevelEnum()->getLabel();
  103. }
  104. /**
  105. * 检查是否为成功请求
  106. *
  107. * @return bool
  108. */
  109. public function isSuccessful(): bool
  110. {
  111. return $this->response_status >= 200 && $this->response_status < 300;
  112. }
  113. /**
  114. * 检查是否为错误请求
  115. *
  116. * @return bool
  117. */
  118. public function isError(): bool
  119. {
  120. return $this->getLogLevelEnum()->isError() || $this->response_status >= 400;
  121. }
  122. /**
  123. * 检查是否需要告警
  124. *
  125. * @return bool
  126. */
  127. public function needsAlert(): bool
  128. {
  129. return $this->getLogLevelEnum()->needsAlert();
  130. }
  131. /**
  132. * 获取响应时间(秒)
  133. *
  134. * @return float
  135. */
  136. public function getResponseTimeInSeconds(): float
  137. {
  138. return $this->response_time ? $this->response_time / 1000 : 0;
  139. }
  140. /**
  141. * 获取格式化的响应时间
  142. *
  143. * @return string
  144. */
  145. public function getFormattedResponseTime(): string
  146. {
  147. if (!$this->response_time) {
  148. return '-';
  149. }
  150. if ($this->response_time < 1000) {
  151. return $this->response_time . 'ms';
  152. }
  153. return number_format($this->getResponseTimeInSeconds(), 2) . 's';
  154. }
  155. /**
  156. * 获取状态码描述
  157. *
  158. * @return string
  159. */
  160. public function getStatusDescription(): string
  161. {
  162. if (!$this->response_status) {
  163. return '无响应';
  164. }
  165. $statusTexts = [
  166. 200 => 'OK',
  167. 201 => 'Created',
  168. 204 => 'No Content',
  169. 400 => 'Bad Request',
  170. 401 => 'Unauthorized',
  171. 403 => 'Forbidden',
  172. 404 => 'Not Found',
  173. 429 => 'Too Many Requests',
  174. 500 => 'Internal Server Error',
  175. 502 => 'Bad Gateway',
  176. 503 => 'Service Unavailable',
  177. 504 => 'Gateway Timeout',
  178. ];
  179. return $statusTexts[$this->response_status] ?? 'Unknown';
  180. }
  181. /**
  182. * 获取状态码颜色
  183. *
  184. * @return string
  185. */
  186. public function getStatusColor(): string
  187. {
  188. if (!$this->response_status) {
  189. return 'secondary';
  190. }
  191. if ($this->response_status >= 200 && $this->response_status < 300) {
  192. return 'success';
  193. }
  194. if ($this->response_status >= 300 && $this->response_status < 400) {
  195. return 'info';
  196. }
  197. if ($this->response_status >= 400 && $this->response_status < 500) {
  198. return 'warning';
  199. }
  200. return 'danger';
  201. }
  202. /**
  203. * 获取简化的请求体
  204. *
  205. * @param int $maxLength
  206. * @return string
  207. */
  208. public function getTruncatedBody(int $maxLength = 200): string
  209. {
  210. if (!$this->body) {
  211. return '';
  212. }
  213. if (strlen($this->body) <= $maxLength) {
  214. return $this->body;
  215. }
  216. return substr($this->body, 0, $maxLength) . '...';
  217. }
  218. /**
  219. * 获取简化的响应体
  220. *
  221. * @param int $maxLength
  222. * @return string
  223. */
  224. public function getTruncatedResponseBody(int $maxLength = 200): string
  225. {
  226. if (!$this->response_body) {
  227. return '';
  228. }
  229. if (strlen($this->response_body) <= $maxLength) {
  230. return $this->response_body;
  231. }
  232. return substr($this->response_body, 0, $maxLength) . '...';
  233. }
  234. /**
  235. * 获取请求的基本信息
  236. *
  237. * @return array
  238. */
  239. public function getRequestSummary(): array
  240. {
  241. return [
  242. 'method' => $this->method,
  243. 'url' => $this->url,
  244. 'status' => $this->response_status,
  245. 'response_time' => $this->getFormattedResponseTime(),
  246. 'level' => $this->getLevelLabel(),
  247. 'created_at' => $this->created_at->format('Y-m-d H:i:s'),
  248. ];
  249. }
  250. /**
  251. * 关联第三方服务
  252. *
  253. * @return BelongsTo
  254. */
  255. public function service(): BelongsTo
  256. {
  257. return $this->belongsTo(ThirdPartyService::class, 'service_id');
  258. }
  259. /**
  260. * 关联认证凭证
  261. *
  262. * @return BelongsTo
  263. */
  264. public function credential(): BelongsTo
  265. {
  266. return $this->belongsTo(ThirdPartyCredential::class, 'credential_id');
  267. }
  268. /**
  269. * 生成唯一的请求ID
  270. *
  271. * @return string
  272. */
  273. public static function generateRequestId(): string
  274. {
  275. return uniqid('req_', true);
  276. }
  277. /**
  278. * 创建日志记录
  279. *
  280. * @param array $data
  281. * @return static
  282. */
  283. public static function createLog(array $data): static
  284. {
  285. // 自动设置创建时间
  286. if (!isset($data['created_at'])) {
  287. $data['created_at'] = now();
  288. }
  289. // 自动生成请求ID
  290. if (!isset($data['request_id'])) {
  291. $data['request_id'] = static::generateRequestId();
  292. }
  293. // 自动设置日志级别
  294. if (!isset($data['level'])) {
  295. if (isset($data['response_status'])) {
  296. if ($data['response_status'] >= 400) {
  297. $data['level'] = LOG_LEVEL::ERROR->value;
  298. } elseif ($data['response_status'] >= 300) {
  299. $data['level'] = LOG_LEVEL::WARNING->value;
  300. } else {
  301. $data['level'] = LOG_LEVEL::INFO->value;
  302. }
  303. } else {
  304. $data['level'] = LOG_LEVEL::INFO->value;
  305. }
  306. }
  307. return static::create($data);
  308. }
  309. /**
  310. * 按日期范围查询
  311. *
  312. * @param \Illuminate\Database\Eloquent\Builder $query
  313. * @param string $startDate
  314. * @param string $endDate
  315. * @return \Illuminate\Database\Eloquent\Builder
  316. */
  317. public function scopeDateRange($query, string $startDate, string $endDate)
  318. {
  319. return $query->whereBetween('created_at', [$startDate, $endDate]);
  320. }
  321. /**
  322. * 按服务查询
  323. *
  324. * @param \Illuminate\Database\Eloquent\Builder $query
  325. * @param int $serviceId
  326. * @return \Illuminate\Database\Eloquent\Builder
  327. */
  328. public function scopeByService($query, int $serviceId)
  329. {
  330. return $query->where('service_id', $serviceId);
  331. }
  332. /**
  333. * 按日志级别查询
  334. *
  335. * @param \Illuminate\Database\Eloquent\Builder $query
  336. * @param string $level
  337. * @return \Illuminate\Database\Eloquent\Builder
  338. */
  339. public function scopeByLevel($query, string $level)
  340. {
  341. return $query->where('level', $level);
  342. }
  343. /**
  344. * 查询错误日志
  345. *
  346. * @param \Illuminate\Database\Eloquent\Builder $query
  347. * @return \Illuminate\Database\Eloquent\Builder
  348. */
  349. public function scopeErrors($query)
  350. {
  351. return $query->whereIn('level', [LOG_LEVEL::ERROR->value, LOG_LEVEL::CRITICAL->value])
  352. ->orWhere('response_status', '>=', 400);
  353. }
  354. }