diff --git a/src/main/java/xuan/service/AiService.java b/src/main/java/xuan/service/AiService.java index da9bb22..2596269 100644 --- a/src/main/java/xuan/service/AiService.java +++ b/src/main/java/xuan/service/AiService.java @@ -9,11 +9,15 @@ import xuan.config.AiProperties; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.cert.X509Certificate; @@ -33,27 +37,11 @@ public class AiService { } private OkHttpClient buildClient() { - try { - TrustManager[] trustManagers = new TrustManager[]{ - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - public void checkClientTrusted(X509Certificate[] certs, String authType) {} - public void checkServerTrusted(X509Certificate[] certs, String authType) {} - } - }; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustManagers, null); - SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - return new OkHttpClient.Builder() - .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0]) - .hostnameVerifier((hostname, session) -> true) - .connectTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS) - .readTimeout(properties.getReadTimeout(), TimeUnit.SECONDS) - .writeTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS) - .build(); - } catch (Exception e) { - throw new RuntimeException("Failed to build OkHttpClient", e); - } + return new OkHttpClient.Builder() + .connectTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS) + .readTimeout(properties.getReadTimeout(), TimeUnit.SECONDS) + .writeTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS) + .build(); } public String chat(String systemPrompt, String userMessage) { @@ -72,7 +60,7 @@ public class AiService { try { String baseUrl = properties.getBaseUrl().replaceAll("/+$", ""); - String url = baseUrl + "/v1/chat/completions"; + String urlStr = baseUrl + "/v1/chat/completions"; JSONObject requestBody = new JSONObject(); requestBody.put("model", properties.getModel()); @@ -95,20 +83,47 @@ public class AiService { String payload = requestBody.toJSONString(); - Request request = new Request.Builder() - .url(url) - .post(RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8"))) - .addHeader("Authorization", "Bearer " + apiKey) - .addHeader("Content-Type", "application/json; charset=utf-8") - .build(); + URL url = new URL(urlStr); + HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setDoOutput(true); + conn.setConnectTimeout(properties.getConnectTimeout() * 1000); + conn.setReadTimeout(properties.getReadTimeout() * 1000); + conn.setRequestProperty("Authorization", "Bearer " + apiKey); + conn.setRequestProperty("Content-Type", "application/json; charset=utf-8"); - try (Response response = client.newCall(request).execute()) { - String responseBody = response.body() != null ? response.body().string() : ""; - if (!response.isSuccessful()) { - return "AI服务请求失败(HTTP " + response.code() + "):" + responseBody; + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} } - return parseOpenAIResponse(responseBody); + }, null); + conn.setSSLSocketFactory(sslContext.getSocketFactory()); + conn.setHostnameVerifier((hostname, session) -> true); + + try (OutputStream os = conn.getOutputStream()) { + os.write(payload.getBytes(StandardCharsets.UTF_8)); } + + int responseCode = conn.getResponseCode(); + StringBuilder responseBody = new StringBuilder(); + try (BufferedReader br = new BufferedReader( + new InputStreamReader( + responseCode >= 200 && responseCode < 300 + ? conn.getInputStream() : conn.getErrorStream(), + StandardCharsets.UTF_8))) { + String line; + while ((line = br.readLine()) != null) { + responseBody.append(line); + } + } + + if (responseCode < 200 || responseCode >= 300) { + return "AI服务请求失败(HTTP " + responseCode + "):" + responseBody; + } + return parseOpenAIResponse(responseBody.toString()); } catch (Exception e) { e.printStackTrace(); return "AI分析暂时不可用,请稍后再试。错误信息:" + e.getMessage(); @@ -123,7 +138,14 @@ public class AiService { JSONObject choice = choices.getJSONObject(0); JSONObject message = choice.getJSONObject("message"); if (message != null) { - return message.getString("content"); + String content = message.getString("content"); + if (content != null && content.startsWith(" { }) } + let slowTimer = setTimeout(() => { + uni.showLoading({ + title: '测算时间有点长,请耐心等待...', + mask: true + }) + }, 30000) + uni.request({ url: BASE_URL + options.url, method: options.method || 'GET', @@ -18,12 +25,11 @@ const request = (options, retryCount = 0) => { 'Content-Type': 'application/json', ...options.header }, - timeout: 60000, + timeout: 300000, success: (res) => { + clearTimeout(slowTimer) + uni.hideLoading() if (res.statusCode === 200) { - if (retryCount > 0) { - uni.hideLoading() - } resolve(res.data) } else if (res.statusCode >= 500 && retryCount < MAX_RETRY) { retryCount++ @@ -35,25 +41,31 @@ const request = (options, retryCount = 0) => { request(options, retryCount).then(resolve).catch(reject) }, 2000) } else { - if (retryCount > 0) { - uni.hideLoading() - } reject(new Error('网络请求失败: ' + res.statusCode)) } }, fail: (err) => { + clearTimeout(slowTimer) + uni.hideLoading() const isTimeout = err.errMsg && (err.errMsg.indexOf('timeout') !== -1 || err.errMsg.indexOf('超时') !== -1) - if (isTimeout && retryCount < MAX_RETRY) { + if (isTimeout) { + uni.showModal({ + title: '测算失败', + content: 'AI分析超时,请稍后再试', + showCancel: false, + confirmText: '知道了' + }) + reject(new Error('测算超时')) + } else if (retryCount < MAX_RETRY) { retryCount++ uni.showLoading({ - title: '请求超时,重试中(' + retryCount + '/' + MAX_RETRY + ')...', + title: '重新连接中(' + retryCount + '/' + MAX_RETRY + ')...', mask: true }) setTimeout(() => { request(options, retryCount).then(resolve).catch(reject) }, 2000) - } else if (retryCount >= MAX_RETRY) { - uni.hideLoading() + } else { uni.showModal({ title: '请求失败', content: '已重试' + MAX_RETRY + '次仍无法连接服务器,请检查网络后重试', @@ -61,8 +73,6 @@ const request = (options, retryCount = 0) => { confirmText: '知道了' }) reject(new Error('重试' + MAX_RETRY + '次后仍失败:' + (err.errMsg || '网络连接失败'))) - } else { - reject(new Error(err.errMsg || '网络连接失败')) } } })