extends Node ## 网络服务 ## 封装 HTTP 请求,提供统一的网络通信接口 # API 基础 URL const API_BASE_URL: String = "https://whaletownui.angforever.top" # 请求超时时间(秒) const REQUEST_TIMEOUT: float = 30.0 # 最大重试次数 const MAX_RETRY: int = 3 # 是否开启调试日志(显示请求和响应详情) const DEBUG_MODE: bool = true ## 发送 POST 请求 func post(endpoint: String, data: Dictionary, headers: Array = []) -> Dictionary: var url = API_BASE_URL + endpoint return await _request(url, HTTPClient.METHOD_POST, data, headers) ## 发送 GET 请求 func sendGet(endpoint: String, headers: Array = []) -> Dictionary: var url = API_BASE_URL + endpoint return await _request(url, HTTPClient.METHOD_GET, {}, headers) ## 发送带 Token 的 POST 请求 func authenticatedPost(endpoint: String, data: Dictionary) -> Dictionary: var tokenData = StorageService.loadToken() var token = tokenData.get("token", "") if token.is_empty(): return { "success": false, "error_code": ErrorCode.TOKEN_INVALID, "message": ErrorCode.getMessage(ErrorCode.TOKEN_INVALID) } var headers = [ "Authorization: Bearer " + token ] return await post(endpoint, data, headers) ## 核心请求方法 func _request(url: String, method: HTTPClient.Method, data: Dictionary, customHeaders: Array) -> Dictionary: # 创建 HTTPRequest 节点 var httpRequest = HTTPRequest.new() add_child(httpRequest) # 设置超时 httpRequest.timeout = REQUEST_TIMEOUT # 构建请求头 var headers = [ "Content-Type: application/json", "Accept: application/json" ] headers.append_array(customHeaders) # 准备请求体 var body = "" if method == HTTPClient.METHOD_POST or method == HTTPClient.METHOD_PUT: body = JSON.stringify(data) # 调试日志:请求信息 if DEBUG_MODE: _logRequest(url, method, headers, body) # 发送请求 var error = httpRequest.request(url, headers, method, body) if error != OK: httpRequest.queue_free() return _createErrorResponse(ErrorCode.NETWORK_ERROR, "发送请求失败") # 等待响应 var result = await httpRequest.request_completed # 调试日志:响应信息 if DEBUG_MODE: _logResponse(result) # 清理 httpRequest.queue_free() # 解析响应 return _parseResponse(result) ## 解析 HTTP 响应 func _parseResponse(result: Array) -> Dictionary: var httpResult: int = result[0] var responseCode: int = result[1] var _headers: PackedStringArray = result[2] var body: PackedByteArray = result[3] # 检查 HTTP 请求错误 if httpResult != HTTPRequest.RESULT_SUCCESS: return _handleHttpError(httpResult) # 检查 HTTP 状态码 if responseCode < 200 or responseCode >= 300: return _handleStatusCodeError(responseCode, body) # 解析 JSON 响应体 var bodyString = body.get_string_from_utf8() if bodyString.is_empty(): return _createErrorResponse(ErrorCode.SERVER_ERROR, "服务器返回空响应") var json = JSON.new() var parseError = json.parse(bodyString) if parseError != OK: push_error("JSON 解析失败: " + bodyString) return _createErrorResponse(ErrorCode.SERVER_ERROR, "响应数据格式错误") var responseData = json.data # 根据后端API格式解析(通用格式,后续可能需要调整) if typeof(responseData) == TYPE_DICTIONARY: # 检查是否有 success 字段(新版 API 格式) if responseData.has("success"): var success = responseData.get("success", false) if success: return { "success": true, "data": responseData.get("data", {}), "message": responseData.get("message", "操作成功") } else: # 业务错误 return { "success": false, "error_code": responseData.get("error_code", ErrorCode.SERVER_ERROR), "message": responseData.get("message", "操作失败") } # 检查是否有 code 字段(旧版 API 格式) elif responseData.has("code"): var code = responseData.get("code", -1) if code == 0 or code == 200: # 成功 return { "success": true, "data": responseData.get("data", {}), "message": responseData.get("message", "操作成功") } else: # 业务错误 return { "success": false, "error_code": code, "message": responseData.get("message", "操作失败") } else: # 假设整个响应就是数据 return { "success": true, "data": responseData, "message": "操作成功" } return _createErrorResponse(ErrorCode.SERVER_ERROR, "响应格式不正确") ## 处理 HTTP 请求错误 func _handleHttpError(httpResult: int) -> Dictionary: match httpResult: HTTPRequest.RESULT_TIMEOUT: return _createErrorResponse(ErrorCode.TIMEOUT, "请求超时") HTTPRequest.RESULT_CONNECTION_ERROR: return _createErrorResponse(ErrorCode.NETWORK_ERROR, "网络连接失败") HTTPRequest.RESULT_CANT_CONNECT: return _createErrorResponse(ErrorCode.NETWORK_ERROR, "无法连接到服务器") _: return _createErrorResponse(ErrorCode.NETWORK_ERROR, "网络请求失败: " + str(httpResult)) ## 处理 HTTP 状态码错误 func _handleStatusCodeError(statusCode: int, body: PackedByteArray) -> Dictionary: var bodyString = body.get_string_from_utf8() # 尝试解析错误信息 var json = JSON.new() if json.parse(bodyString) == OK: var data = json.data if typeof(data) == TYPE_DICTIONARY and data.has("message"): var message = data.get("message", "") # 确保 message 是字符串类型 if typeof(message) == TYPE_ARRAY: message = ", ".join(message) elif typeof(message) != TYPE_STRING: message = str(message) return _createErrorResponse(statusCode, message) # 通用状态码错误处理 match statusCode: 400: return _createErrorResponse(statusCode, "请求参数错误") 401: return _createErrorResponse(statusCode, "未授权,请重新登录") 403: return _createErrorResponse(statusCode, "没有权限访问") 404: return _createErrorResponse(statusCode, "请求的资源不存在") 500: return _createErrorResponse(statusCode, "服务器内部错误") 502: return _createErrorResponse(statusCode, "网关错误") 503: return _createErrorResponse(statusCode, "服务暂时不可用") _: return _createErrorResponse(statusCode, "HTTP 错误: " + str(statusCode)) ## 创建错误响应 func _createErrorResponse(errorCode: int, message: String) -> Dictionary: return { "success": false, "error_code": errorCode, "message": message } ## 记录请求日志 func _logRequest(url: String, method: HTTPClient.Method, headers: Array, body: String) -> void: print("\n========== HTTP 请求 ==========") print("URL: ", url) print("方法: ", _getMethodName(method)) print("请求头:") for header in headers: print(" ", header) if not body.is_empty(): print("请求体:") # 尝试格式化 JSON var json = JSON.new() if json.parse(body) == OK: print(" ", JSON.stringify(json.data, " ")) else: print(" ", body) print("==============================\n") ## 记录响应日志 func _logResponse(result: Array) -> void: var httpResult: int = result[0] var responseCode: int = result[1] var responseHeaders: PackedStringArray = result[2] var body: PackedByteArray = result[3] print("\n========== HTTP 响应 ==========") print("请求结果: ", _getHttpResultName(httpResult)) print("状态码: ", responseCode) print("响应头:") for header in responseHeaders: print(" ", header) var bodyString = body.get_string_from_utf8() if not bodyString.is_empty(): print("响应体:") # 尝试格式化 JSON var json = JSON.new() if json.parse(bodyString) == OK: print(" ", JSON.stringify(json.data, " ")) else: print(" ", bodyString) else: print("响应体: (空)") print("==============================\n") ## 获取 HTTP 方法名称 func _getMethodName(method: HTTPClient.Method) -> String: match method: HTTPClient.METHOD_GET: return "GET" HTTPClient.METHOD_POST: return "POST" HTTPClient.METHOD_PUT: return "PUT" HTTPClient.METHOD_DELETE: return "DELETE" HTTPClient.METHOD_PATCH: return "PATCH" _: return "UNKNOWN" ## 获取 HTTP 请求结果名称 func _getHttpResultName(result: int) -> String: match result: HTTPRequest.RESULT_SUCCESS: return "成功" HTTPRequest.RESULT_TIMEOUT: return "超时" HTTPRequest.RESULT_CONNECTION_ERROR: return "连接错误" HTTPRequest.RESULT_CANT_CONNECT: return "无法连接" HTTPRequest.RESULT_NO_RESPONSE: return "无响应" _: return "错误 (" + str(result) + ")"