errors.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. package errors
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. // ErrorType 错误类型
  7. type ErrorType string
  8. const (
  9. ErrorTypeInvalidRequest ErrorType = "invalid_request"
  10. ErrorTypeAuthentication ErrorType = "authentication_error"
  11. ErrorTypePermissionDenied ErrorType = "permission_denied"
  12. ErrorTypeRateLimitError ErrorType = "rate_limit_error" // 与 Node.js 版本一致
  13. ErrorTypeProviderError ErrorType = "provider_error"
  14. ErrorTypeCircuitBreakerOpen ErrorType = "circuit_breaker_open"
  15. ErrorTypeInternal ErrorType = "internal_error"
  16. ErrorTypeNotFound ErrorType = "not_found"
  17. )
  18. // ErrorCategory 错误分类 - 用于区分错误处理策略
  19. type ErrorCategory int
  20. const (
  21. // CategoryProviderError 供应商问题(所有 4xx/5xx HTTP 错误)→ 计入熔断器 + 直接切换
  22. CategoryProviderError ErrorCategory = iota
  23. // CategorySystemError 系统/网络问题(fetch 网络异常)→ 不计入熔断器 + 先重试1次
  24. CategorySystemError
  25. // CategoryClientAbort 客户端主动中断 → 不计入熔断器 + 不重试 + 直接返回
  26. CategoryClientAbort
  27. // CategoryNonRetryableClientError 客户端输入错误 → 不计入熔断器 + 不重试 + 直接返回
  28. CategoryNonRetryableClientError
  29. // CategoryResourceNotFound 上游 404 错误 → 不计入熔断器 + 直接切换供应商
  30. CategoryResourceNotFound
  31. )
  32. // ErrorCode 错误码
  33. type ErrorCode string
  34. const (
  35. // 认证错误
  36. CodeInvalidAPIKey ErrorCode = "invalid_api_key"
  37. CodeExpiredAPIKey ErrorCode = "expired_api_key"
  38. CodeDisabledAPIKey ErrorCode = "disabled_api_key"
  39. CodeDisabledUser ErrorCode = "disabled_user"
  40. CodeUnauthorized ErrorCode = "unauthorized"
  41. CodeInvalidToken ErrorCode = "invalid_token"
  42. CodeTokenRequired ErrorCode = "token_required"
  43. CodeInvalidCredentials ErrorCode = "invalid_credentials"
  44. CodeSessionExpired ErrorCode = "session_expired"
  45. // 权限错误
  46. CodeModelNotAllowed ErrorCode = "model_not_allowed"
  47. CodeClientNotAllowed ErrorCode = "client_not_allowed"
  48. CodePermissionDenied ErrorCode = "permission_denied"
  49. // 限流错误
  50. CodeRateLimitExceeded ErrorCode = "rate_limit_exceeded"
  51. CodeRPMLimitExceeded ErrorCode = "rpm_limit_exceeded"
  52. Code5HLimitExceeded ErrorCode = "5h_limit_exceeded"
  53. CodeDailyLimitExceeded ErrorCode = "daily_limit_exceeded"
  54. CodeWeeklyLimitExceeded ErrorCode = "weekly_limit_exceeded"
  55. CodeMonthlyLimitExceeded ErrorCode = "monthly_limit_exceeded"
  56. CodeTotalLimitExceeded ErrorCode = "total_limit_exceeded"
  57. CodeConcurrentSessionsExceeded ErrorCode = "concurrent_sessions_exceeded"
  58. // 供应商错误
  59. CodeNoProviderAvailable ErrorCode = "no_provider_available"
  60. CodeProviderTimeout ErrorCode = "provider_timeout"
  61. CodeProviderError ErrorCode = "provider_error"
  62. CodeEmptyResponse ErrorCode = "empty_response"
  63. // 熔断错误
  64. CodeCircuitOpen ErrorCode = "circuit_open"
  65. // 内部错误
  66. CodeInternalError ErrorCode = "internal_error"
  67. CodeDatabaseError ErrorCode = "database_error"
  68. CodeRedisError ErrorCode = "redis_error"
  69. // 请求错误
  70. CodeInvalidRequest ErrorCode = "invalid_request"
  71. CodeNotFound ErrorCode = "not_found"
  72. // 验证错误
  73. CodeRequiredField ErrorCode = "required_field"
  74. CodeUserNameRequired ErrorCode = "user_name_required"
  75. CodeAPIKeyRequired ErrorCode = "api_key_required"
  76. CodeProviderName ErrorCode = "provider_name_required"
  77. CodeProviderURL ErrorCode = "provider_url_required"
  78. CodeMinLength ErrorCode = "min_length"
  79. CodeMaxLength ErrorCode = "max_length"
  80. CodeMinValue ErrorCode = "min_value"
  81. CodeMaxValue ErrorCode = "max_value"
  82. CodeMustBeInteger ErrorCode = "must_be_integer"
  83. CodeMustBePositive ErrorCode = "must_be_positive"
  84. CodeInvalidEmail ErrorCode = "invalid_email"
  85. CodeInvalidURL ErrorCode = "invalid_url"
  86. CodeInvalidType ErrorCode = "invalid_type"
  87. CodeInvalidFormat ErrorCode = "invalid_format"
  88. CodeDuplicateName ErrorCode = "duplicate_name"
  89. CodeInvalidRange ErrorCode = "invalid_range"
  90. CodeEmptyUpdate ErrorCode = "empty_update"
  91. // 网络错误
  92. CodeConnectionFailed ErrorCode = "connection_failed"
  93. CodeTimeout ErrorCode = "timeout"
  94. CodeNetworkError ErrorCode = "network_error"
  95. // 业务错误
  96. CodeQuotaExceeded ErrorCode = "quota_exceeded"
  97. CodeResourceBusy ErrorCode = "resource_busy"
  98. CodeInvalidState ErrorCode = "invalid_state"
  99. CodeConflict ErrorCode = "conflict"
  100. // 操作错误
  101. CodeOperationFailed ErrorCode = "operation_failed"
  102. CodeCreateFailed ErrorCode = "create_failed"
  103. CodeUpdateFailed ErrorCode = "update_failed"
  104. CodeDeleteFailed ErrorCode = "delete_failed"
  105. )
  106. // AppError 应用错误
  107. type AppError struct {
  108. Type ErrorType `json:"type"`
  109. Message string `json:"message"`
  110. Code ErrorCode `json:"code"`
  111. Details map[string]interface{} `json:"details,omitempty"`
  112. HTTPStatus int `json:"-"`
  113. Err error `json:"-"`
  114. }
  115. // Error 实现 error 接口
  116. func (e *AppError) Error() string {
  117. if e.Err != nil {
  118. return fmt.Sprintf("%s: %s (%v)", e.Type, e.Message, e.Err)
  119. }
  120. return fmt.Sprintf("%s: %s", e.Type, e.Message)
  121. }
  122. // Unwrap 实现 errors.Unwrap
  123. func (e *AppError) Unwrap() error {
  124. return e.Err
  125. }
  126. // WithDetails 添加详情
  127. func (e *AppError) WithDetails(details map[string]interface{}) *AppError {
  128. e.Details = details
  129. return e
  130. }
  131. // WithError 包装原始错误
  132. func (e *AppError) WithError(err error) *AppError {
  133. e.Err = err
  134. return e
  135. }
  136. // ErrorResponse API 错误响应格式
  137. type ErrorResponse struct {
  138. Error ErrorResponseBody `json:"error"`
  139. }
  140. // ErrorResponseBody 错误响应体
  141. type ErrorResponseBody struct {
  142. Type ErrorType `json:"type"`
  143. Message string `json:"message"`
  144. Code ErrorCode `json:"code"`
  145. Details map[string]interface{} `json:"details,omitempty"`
  146. }
  147. // ToResponse 转换为 API 响应格式
  148. func (e *AppError) ToResponse() ErrorResponse {
  149. return ErrorResponse{
  150. Error: ErrorResponseBody{
  151. Type: e.Type,
  152. Message: e.Message,
  153. Code: e.Code,
  154. Details: e.Details,
  155. },
  156. }
  157. }
  158. // 预定义错误构造函数
  159. // NewInvalidRequest 创建无效请求错误
  160. func NewInvalidRequest(message string) *AppError {
  161. return &AppError{
  162. Type: ErrorTypeInvalidRequest,
  163. Message: message,
  164. Code: CodeInvalidRequest,
  165. HTTPStatus: http.StatusBadRequest,
  166. }
  167. }
  168. // NewAuthenticationError 创建认证错误
  169. func NewAuthenticationError(message string, code ErrorCode) *AppError {
  170. return &AppError{
  171. Type: ErrorTypeAuthentication,
  172. Message: message,
  173. Code: code,
  174. HTTPStatus: http.StatusUnauthorized,
  175. }
  176. }
  177. // NewPermissionDenied 创建权限拒绝错误
  178. func NewPermissionDenied(message string, code ErrorCode) *AppError {
  179. return &AppError{
  180. Type: ErrorTypePermissionDenied,
  181. Message: message,
  182. Code: code,
  183. HTTPStatus: http.StatusForbidden,
  184. }
  185. }
  186. // NewRateLimitExceeded 创建限流错误
  187. func NewRateLimitExceeded(message string, code ErrorCode) *AppError {
  188. return &AppError{
  189. Type: ErrorTypeRateLimitError,
  190. Message: message,
  191. Code: code,
  192. HTTPStatus: http.StatusTooManyRequests,
  193. }
  194. }
  195. // NewProviderError 创建供应商错误
  196. func NewProviderError(message string, code ErrorCode) *AppError {
  197. return &AppError{
  198. Type: ErrorTypeProviderError,
  199. Message: message,
  200. Code: code,
  201. HTTPStatus: http.StatusBadGateway,
  202. }
  203. }
  204. // NewCircuitBreakerOpen 创建熔断器开启错误
  205. func NewCircuitBreakerOpen(providerName string) *AppError {
  206. return &AppError{
  207. Type: ErrorTypeCircuitBreakerOpen,
  208. Message: fmt.Sprintf("Circuit breaker is open for provider: %s", providerName),
  209. Code: CodeCircuitOpen,
  210. HTTPStatus: http.StatusServiceUnavailable,
  211. }
  212. }
  213. // NewInternalError 创建内部错误
  214. func NewInternalError(message string) *AppError {
  215. return &AppError{
  216. Type: ErrorTypeInternal,
  217. Message: message,
  218. Code: CodeInternalError,
  219. HTTPStatus: http.StatusInternalServerError,
  220. }
  221. }
  222. // NewNotFoundError 创建资源不存在错误
  223. func NewNotFoundError(resource string) *AppError {
  224. return &AppError{
  225. Type: ErrorTypeNotFound,
  226. Message: fmt.Sprintf("%s not found", resource),
  227. Code: CodeNotFound,
  228. HTTPStatus: http.StatusNotFound,
  229. }
  230. }
  231. // NewDatabaseError 创建数据库错误
  232. func NewDatabaseError(err error) *AppError {
  233. return &AppError{
  234. Type: ErrorTypeInternal,
  235. Message: "Database error",
  236. Code: CodeDatabaseError,
  237. HTTPStatus: http.StatusInternalServerError,
  238. Err: err,
  239. }
  240. }
  241. // NewRedisError 创建 Redis 错误
  242. func NewRedisError(err error) *AppError {
  243. return &AppError{
  244. Type: ErrorTypeInternal,
  245. Message: "Redis error",
  246. Code: CodeRedisError,
  247. HTTPStatus: http.StatusInternalServerError,
  248. Err: err,
  249. }
  250. }
  251. // Is 检查错误类型
  252. func Is(err error, target ErrorType) bool {
  253. if appErr, ok := err.(*AppError); ok {
  254. return appErr.Type == target
  255. }
  256. return false
  257. }
  258. // IsCode 检查错误码
  259. func IsCode(err error, code ErrorCode) bool {
  260. if appErr, ok := err.(*AppError); ok {
  261. return appErr.Code == code
  262. }
  263. return false
  264. }
  265. // ============================================================================
  266. // 代理模块专用错误类型 - 与 Node.js 版本 src/app/v1/_lib/proxy/errors.ts 对齐
  267. // ============================================================================
  268. // LimitType 限流类型
  269. type LimitType string
  270. const (
  271. LimitTypeRPM LimitType = "rpm"
  272. LimitTypeUSD5H LimitType = "usd_5h"
  273. LimitTypeUSDWeekly LimitType = "usd_weekly"
  274. LimitTypeUSDMonthly LimitType = "usd_monthly"
  275. LimitTypeUSDTotal LimitType = "usd_total"
  276. LimitTypeConcurrentSessions LimitType = "concurrent_sessions"
  277. LimitTypeDailyQuota LimitType = "daily_quota"
  278. )
  279. // RateLimitError 限流错误 - 携带详细的限流上下文信息
  280. // 与 Node.js 版本的 RateLimitError 类对齐
  281. type RateLimitError struct {
  282. Type string `json:"type"` // 固定为 "rate_limit_error"
  283. Message string `json:"message"` // 人类可读的错误消息
  284. LimitType LimitType `json:"limit_type"` // 限流类型
  285. CurrentUsage float64 `json:"current_usage"` // 当前使用量
  286. LimitValue float64 `json:"limit_value"` // 限制值
  287. ResetTime string `json:"reset_time"` // 重置时间(ISO 8601 格式)
  288. ProviderID *int `json:"provider_id"` // 供应商 ID(可选)
  289. }
  290. // Error 实现 error 接口
  291. func (e *RateLimitError) Error() string {
  292. return e.Message
  293. }
  294. // NewRateLimitError 创建限流错误
  295. func NewRateLimitError(
  296. message string,
  297. limitType LimitType,
  298. currentUsage float64,
  299. limitValue float64,
  300. resetTime string,
  301. providerID *int,
  302. ) *RateLimitError {
  303. return &RateLimitError{
  304. Type: "rate_limit_error",
  305. Message: message,
  306. LimitType: limitType,
  307. CurrentUsage: currentUsage,
  308. LimitValue: limitValue,
  309. ResetTime: resetTime,
  310. ProviderID: providerID,
  311. }
  312. }
  313. // ToJSON 获取适合记录到数据库的 JSON 元数据
  314. func (e *RateLimitError) ToJSON() map[string]interface{} {
  315. return map[string]interface{}{
  316. "type": e.Type,
  317. "limit_type": e.LimitType,
  318. "current_usage": e.CurrentUsage,
  319. "limit_value": e.LimitValue,
  320. "reset_time": e.ResetTime,
  321. "provider_id": e.ProviderID,
  322. "message": e.Message,
  323. }
  324. }
  325. // IsRateLimitError 类型守卫:检查是否为 RateLimitError
  326. func IsRateLimitError(err error) bool {
  327. _, ok := err.(*RateLimitError)
  328. return ok
  329. }
  330. // AsRateLimitError 类型转换:将 error 转换为 RateLimitError
  331. func AsRateLimitError(err error) (*RateLimitError, bool) {
  332. e, ok := err.(*RateLimitError)
  333. return e, ok
  334. }
  335. // UpstreamError 上游错误信息
  336. type UpstreamError struct {
  337. Body string `json:"body"` // 原始响应体(智能截断)
  338. Parsed interface{} `json:"parsed,omitempty"` // 解析后的 JSON(如果有)
  339. ProviderID *int `json:"provider_id,omitempty"` // 供应商 ID
  340. ProviderName string `json:"provider_name,omitempty"` // 供应商名称
  341. RequestID string `json:"request_id,omitempty"` // 上游请求 ID
  342. }
  343. // ProxyError 代理错误 - 携带上游完整错误信息
  344. // 与 Node.js 版本的 ProxyError 类对齐
  345. type ProxyError struct {
  346. Message string `json:"message"`
  347. StatusCode int `json:"status_code"`
  348. UpstreamError *UpstreamError `json:"upstream_error,omitempty"`
  349. }
  350. // Error 实现 error 接口
  351. func (e *ProxyError) Error() string {
  352. return e.Message
  353. }
  354. // NewProxyError 创建代理错误
  355. func NewProxyError(message string, statusCode int, upstreamError *UpstreamError) *ProxyError {
  356. return &ProxyError{
  357. Message: message,
  358. StatusCode: statusCode,
  359. UpstreamError: upstreamError,
  360. }
  361. }
  362. // GetDetailedErrorMessage 获取适合记录到数据库的详细错误信息
  363. // 格式:Provider {name} returned {status}: {message} | Upstream: {body}
  364. func (e *ProxyError) GetDetailedErrorMessage() string {
  365. if e.UpstreamError != nil && e.UpstreamError.ProviderName != "" {
  366. msg := fmt.Sprintf("Provider %s returned %d: %s",
  367. e.UpstreamError.ProviderName, e.StatusCode, e.Message)
  368. if e.UpstreamError.Body != "" {
  369. msg += " | Upstream: " + e.UpstreamError.Body
  370. }
  371. return msg
  372. }
  373. return e.Message
  374. }
  375. // GetClientSafeMessage 获取适合返回给客户端的安全错误信息
  376. // 不包含供应商名称等敏感信息
  377. func (e *ProxyError) GetClientSafeMessage() string {
  378. return e.Message
  379. }
  380. // IsProxyError 类型守卫:检查是否为 ProxyError
  381. func IsProxyError(err error) bool {
  382. _, ok := err.(*ProxyError)
  383. return ok
  384. }
  385. // AsProxyError 类型转换:将 error 转换为 ProxyError
  386. func AsProxyError(err error) (*ProxyError, bool) {
  387. e, ok := err.(*ProxyError)
  388. return e, ok
  389. }
  390. // EmptyResponseReason 空响应原因
  391. type EmptyResponseReason string
  392. const (
  393. EmptyResponseReasonEmptyBody EmptyResponseReason = "empty_body"
  394. EmptyResponseReasonNoOutputTokens EmptyResponseReason = "no_output_tokens"
  395. EmptyResponseReasonMissingContent EmptyResponseReason = "missing_content"
  396. )
  397. // EmptyResponseError 空响应错误 - 用于检测上游返回空响应或缺少输出 token 的情况
  398. // 与 Node.js 版本的 EmptyResponseError 类对齐
  399. type EmptyResponseError struct {
  400. ProviderID int `json:"provider_id"`
  401. ProviderName string `json:"provider_name"`
  402. Reason EmptyResponseReason `json:"reason"`
  403. message string
  404. }
  405. // Error 实现 error 接口
  406. func (e *EmptyResponseError) Error() string {
  407. return e.message
  408. }
  409. // NewEmptyResponseError 创建空响应错误
  410. func NewEmptyResponseError(providerID int, providerName string, reason EmptyResponseReason) *EmptyResponseError {
  411. reasonMessages := map[EmptyResponseReason]string{
  412. EmptyResponseReasonEmptyBody: "Response body is empty",
  413. EmptyResponseReasonNoOutputTokens: "Response has no output tokens",
  414. EmptyResponseReasonMissingContent: "Response is missing content field",
  415. }
  416. message := fmt.Sprintf("Empty response from provider %s: %s", providerName, reasonMessages[reason])
  417. return &EmptyResponseError{
  418. ProviderID: providerID,
  419. ProviderName: providerName,
  420. Reason: reason,
  421. message: message,
  422. }
  423. }
  424. // ToJSON 获取适合记录的 JSON 元数据
  425. func (e *EmptyResponseError) ToJSON() map[string]interface{} {
  426. return map[string]interface{}{
  427. "type": "empty_response_error",
  428. "provider_id": e.ProviderID,
  429. "provider_name": e.ProviderName,
  430. "reason": e.Reason,
  431. "message": e.message,
  432. }
  433. }
  434. // GetClientSafeMessage 获取适合返回给客户端的安全错误信息
  435. // 不包含供应商名称等敏感信息
  436. func (e *EmptyResponseError) GetClientSafeMessage() string {
  437. reasonMessages := map[EmptyResponseReason]string{
  438. EmptyResponseReasonEmptyBody: "Response body is empty",
  439. EmptyResponseReasonNoOutputTokens: "Response has no output tokens",
  440. EmptyResponseReasonMissingContent: "Response is missing content field",
  441. }
  442. return fmt.Sprintf("Empty response: %s", reasonMessages[e.Reason])
  443. }
  444. // IsEmptyResponseError 类型守卫:检查是否为 EmptyResponseError
  445. func IsEmptyResponseError(err error) bool {
  446. _, ok := err.(*EmptyResponseError)
  447. return ok
  448. }
  449. // AsEmptyResponseError 类型转换:将 error 转换为 EmptyResponseError
  450. func AsEmptyResponseError(err error) (*EmptyResponseError, bool) {
  451. e, ok := err.(*EmptyResponseError)
  452. return e, ok
  453. }
  454. // ============================================================================
  455. // 错误分类函数 - 与 Node.js 版本 categorizeErrorAsync 对齐
  456. // ============================================================================
  457. // CategorizeError 判断错误类型
  458. // 分类规则(优先级从高到低):
  459. // 1. 客户端主动中断 → CategoryClientAbort
  460. // 2. 不可重试的客户端输入错误 → CategoryNonRetryableClientError
  461. // 3. ProxyError 404 → CategoryResourceNotFound
  462. // 4. ProxyError 其他 → CategoryProviderError
  463. // 5. EmptyResponseError → CategoryProviderError
  464. // 6. 其他 → CategorySystemError
  465. func CategorizeError(err error) ErrorCategory {
  466. // 优先级 1: 客户端中断检测
  467. if IsClientAbortError(err) {
  468. return CategoryClientAbort
  469. }
  470. // 优先级 2: 不可重试的客户端输入错误(需要配合错误规则检测器)
  471. // 注意:这里需要外部调用者提供规则检测结果
  472. // Go 版本中可以通过 context 或者单独的检测函数实现
  473. // 优先级 3: ProxyError
  474. if proxyErr, ok := AsProxyError(err); ok {
  475. if proxyErr.StatusCode == http.StatusNotFound {
  476. return CategoryResourceNotFound
  477. }
  478. return CategoryProviderError
  479. }
  480. // 优先级 4: EmptyResponseError
  481. if IsEmptyResponseError(err) {
  482. return CategoryProviderError
  483. }
  484. // 优先级 5: 其他都是系统错误
  485. return CategorySystemError
  486. }
  487. // IsClientAbortError 检测是否为客户端中断错误
  488. // 采用白名单模式,精确检测客户端主动中断的错误
  489. func IsClientAbortError(err error) bool {
  490. if err == nil {
  491. return false
  492. }
  493. // 检查 ProxyError 状态码 499(Client Closed Request)
  494. if proxyErr, ok := AsProxyError(err); ok {
  495. if proxyErr.StatusCode == 499 {
  496. return true
  497. }
  498. }
  499. // 检查错误消息中的中断标识
  500. errMsg := err.Error()
  501. abortMessages := []string{
  502. "context canceled",
  503. "context deadline exceeded",
  504. "client disconnected",
  505. "connection reset by peer",
  506. }
  507. for _, msg := range abortMessages {
  508. if contains(errMsg, msg) {
  509. return true
  510. }
  511. }
  512. return false
  513. }
  514. // contains 检查字符串是否包含子串(不区分大小写)
  515. func contains(s, substr string) bool {
  516. return len(s) >= len(substr) &&
  517. (s == substr ||
  518. len(s) > len(substr) && findSubstring(s, substr))
  519. }
  520. // findSubstring 简单的子串查找
  521. func findSubstring(s, substr string) bool {
  522. for i := 0; i <= len(s)-len(substr); i++ {
  523. if s[i:i+len(substr)] == substr {
  524. return true
  525. }
  526. }
  527. return false
  528. }