OpenApiStats.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <?php
  2. namespace App\Module\OpenAPI\Models;
  3. use UCore\ModelCore;
  4. /**
  5. * OpenAPI统计数据模型
  6. *
  7. * field start
  8. * field end
  9. */
  10. class OpenApiStats extends ModelCore
  11. {
  12. /**
  13. * 数据类型转换
  14. *
  15. * @var array
  16. */
  17. protected $casts = [
  18. 'date' => 'date',
  19. 'hour' => 'integer',
  20. 'request_count' => 'integer',
  21. 'success_count' => 'integer',
  22. 'error_count' => 'integer',
  23. 'avg_response_time' => 'float',
  24. 'max_response_time' => 'float',
  25. 'min_response_time' => 'float',
  26. 'rate_limit_hits' => 'integer',
  27. 'unique_ips' => 'integer',
  28. 'error_details' => 'array',
  29. ];
  30. /**
  31. * 默认值
  32. *
  33. * @var array
  34. */
  35. protected $attributes = [
  36. 'request_count' => 0,
  37. 'success_count' => 0,
  38. 'error_count' => 0,
  39. 'avg_response_time' => 0,
  40. 'max_response_time' => 0,
  41. 'min_response_time' => 0,
  42. 'rate_limit_hits' => 0,
  43. 'unique_ips' => 0,
  44. ];
  45. /**
  46. * 关联应用模型
  47. *
  48. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  49. */
  50. public function app()
  51. {
  52. return $this->belongsTo(OpenApiApp::class, 'app_id', 'app_id');
  53. }
  54. /**
  55. * 获取成功率
  56. *
  57. * @return float
  58. */
  59. public function getSuccessRateAttribute(): float
  60. {
  61. if ($this->request_count === 0) {
  62. return 0;
  63. }
  64. return round(($this->success_count / $this->request_count) * 100, 2);
  65. }
  66. /**
  67. * 获取错误率
  68. *
  69. * @return float
  70. */
  71. public function getErrorRateAttribute(): float
  72. {
  73. if ($this->request_count === 0) {
  74. return 0;
  75. }
  76. return round(($this->error_count / $this->request_count) * 100, 2);
  77. }
  78. /**
  79. * 获取限流命中率
  80. *
  81. * @return float
  82. */
  83. public function getRateLimitHitRateAttribute(): float
  84. {
  85. if ($this->request_count === 0) {
  86. return 0;
  87. }
  88. return round(($this->rate_limit_hits / $this->request_count) * 100, 2);
  89. }
  90. /**
  91. * 获取时间段标识
  92. *
  93. * @return string
  94. */
  95. public function getTimePeriodAttribute(): string
  96. {
  97. if ($this->hour !== null) {
  98. return $this->date->format('Y-m-d') . ' ' . sprintf('%02d:00', $this->hour);
  99. }
  100. return $this->date->format('Y-m-d');
  101. }
  102. /**
  103. * 获取格式化的平均响应时间
  104. *
  105. * @return string
  106. */
  107. public function getFormattedAvgResponseTimeAttribute(): string
  108. {
  109. if ($this->avg_response_time < 1000) {
  110. return round($this->avg_response_time, 2) . 'ms';
  111. }
  112. return round($this->avg_response_time / 1000, 2) . 's';
  113. }
  114. /**
  115. * 更新统计数据
  116. *
  117. * @param array $data
  118. * @return void
  119. */
  120. public function updateStats(array $data): void
  121. {
  122. $this->increment('request_count', $data['request_count'] ?? 1);
  123. if (isset($data['success']) && $data['success']) {
  124. $this->increment('success_count');
  125. } else {
  126. $this->increment('error_count');
  127. }
  128. if (isset($data['response_time'])) {
  129. $this->updateResponseTime($data['response_time']);
  130. }
  131. if (isset($data['rate_limit_hit']) && $data['rate_limit_hit']) {
  132. $this->increment('rate_limit_hits');
  133. }
  134. if (isset($data['unique_ip'])) {
  135. $this->updateUniqueIps($data['unique_ip']);
  136. }
  137. if (isset($data['error_details'])) {
  138. $this->updateErrorDetails($data['error_details']);
  139. }
  140. }
  141. /**
  142. * 更新响应时间统计
  143. *
  144. * @param float $responseTime
  145. * @return void
  146. */
  147. protected function updateResponseTime(float $responseTime): void
  148. {
  149. // 更新平均响应时间
  150. $totalTime = $this->avg_response_time * ($this->request_count - 1) + $responseTime;
  151. $this->avg_response_time = $totalTime / $this->request_count;
  152. // 更新最大响应时间
  153. if ($responseTime > $this->max_response_time) {
  154. $this->max_response_time = $responseTime;
  155. }
  156. // 更新最小响应时间
  157. if ($this->min_response_time === 0 || $responseTime < $this->min_response_time) {
  158. $this->min_response_time = $responseTime;
  159. }
  160. $this->save();
  161. }
  162. /**
  163. * 更新唯一IP统计
  164. *
  165. * @param string $ip
  166. * @return void
  167. */
  168. protected function updateUniqueIps(string $ip): void
  169. {
  170. // 这里可以使用Redis Set来精确统计唯一IP
  171. // 为了简化,这里只是增加计数
  172. $this->increment('unique_ips');
  173. }
  174. /**
  175. * 更新错误详情
  176. *
  177. * @param array $errorDetails
  178. * @return void
  179. */
  180. protected function updateErrorDetails(array $errorDetails): void
  181. {
  182. $currentDetails = $this->error_details ?? [];
  183. $errorCode = $errorDetails['code'] ?? 'unknown';
  184. if (!isset($currentDetails[$errorCode])) {
  185. $currentDetails[$errorCode] = [
  186. 'count' => 0,
  187. 'message' => $errorDetails['message'] ?? '',
  188. 'first_seen' => now()->toISOString(),
  189. ];
  190. }
  191. $currentDetails[$errorCode]['count']++;
  192. $currentDetails[$errorCode]['last_seen'] = now()->toISOString();
  193. $this->update(['error_details' => $currentDetails]);
  194. }
  195. /**
  196. * 按应用ID查询
  197. *
  198. * @param \Illuminate\Database\Eloquent\Builder $query
  199. * @param string $appId
  200. * @return \Illuminate\Database\Eloquent\Builder
  201. */
  202. public function scopeByApp($query, string $appId)
  203. {
  204. return $query->where('app_id', $appId);
  205. }
  206. /**
  207. * 按日期查询
  208. *
  209. * @param \Illuminate\Database\Eloquent\Builder $query
  210. * @param string $date
  211. * @return \Illuminate\Database\Eloquent\Builder
  212. */
  213. public function scopeByDate($query, string $date)
  214. {
  215. return $query->where('date', $date);
  216. }
  217. /**
  218. * 按日期范围查询
  219. *
  220. * @param \Illuminate\Database\Eloquent\Builder $query
  221. * @param string $startDate
  222. * @param string $endDate
  223. * @return \Illuminate\Database\Eloquent\Builder
  224. */
  225. public function scopeDateRange($query, string $startDate, string $endDate)
  226. {
  227. return $query->whereBetween('date', [$startDate, $endDate]);
  228. }
  229. /**
  230. * 按小时查询
  231. *
  232. * @param \Illuminate\Database\Eloquent\Builder $query
  233. * @param int $hour
  234. * @return \Illuminate\Database\Eloquent\Builder
  235. */
  236. public function scopeByHour($query, int $hour)
  237. {
  238. return $query->where('hour', $hour);
  239. }
  240. /**
  241. * 按接口端点查询
  242. *
  243. * @param \Illuminate\Database\Eloquent\Builder $query
  244. * @param string $endpoint
  245. * @return \Illuminate\Database\Eloquent\Builder
  246. */
  247. public function scopeByEndpoint($query, string $endpoint)
  248. {
  249. return $query->where('endpoint', $endpoint);
  250. }
  251. /**
  252. * 查询今日统计
  253. *
  254. * @param \Illuminate\Database\Eloquent\Builder $query
  255. * @return \Illuminate\Database\Eloquent\Builder
  256. */
  257. public function scopeToday($query)
  258. {
  259. return $query->where('date', now()->toDateString());
  260. }
  261. /**
  262. * 查询昨日统计
  263. *
  264. * @param \Illuminate\Database\Eloquent\Builder $query
  265. * @return \Illuminate\Database\Eloquent\Builder
  266. */
  267. public function scopeYesterday($query)
  268. {
  269. return $query->where('date', now()->subDay()->toDateString());
  270. }
  271. /**
  272. * 查询本周统计
  273. *
  274. * @param \Illuminate\Database\Eloquent\Builder $query
  275. * @return \Illuminate\Database\Eloquent\Builder
  276. */
  277. public function scopeThisWeek($query)
  278. {
  279. return $query->whereBetween('date', [
  280. now()->startOfWeek()->toDateString(),
  281. now()->endOfWeek()->toDateString()
  282. ]);
  283. }
  284. /**
  285. * 查询本月统计
  286. *
  287. * @param \Illuminate\Database\Eloquent\Builder $query
  288. * @return \Illuminate\Database\Eloquent\Builder
  289. */
  290. public function scopeThisMonth($query)
  291. {
  292. return $query->whereBetween('date', [
  293. now()->startOfMonth()->toDateString(),
  294. now()->endOfMonth()->toDateString()
  295. ]);
  296. }
  297. }