1212
1313package com .tinyengine .it .service .app .impl .v1 ;
1414
15+ import com .aliyun .bailian20231229 .Client ;
16+ import com .aliyun .tea .TeaException ;
17+ import com .aliyun .teaopenapi .models .Config ;
18+ import com .aliyun .teaopenapi .models .OpenApiRequest ;
19+ import com .aliyun .teaopenapi .models .Params ;
20+ import com .aliyun .teautil .models .RuntimeOptions ;
1521import com .fasterxml .jackson .databind .JsonNode ;
22+ import com .tinyengine .it .common .base .Result ;
1623import com .tinyengine .it .common .log .SystemServiceLog ;
1724import com .tinyengine .it .common .utils .JsonUtils ;
1825import com .tinyengine .it .config .OpenAIConfig ;
1926import com .tinyengine .it .model .dto .ChatRequest ;
27+ import com .tinyengine .it .model .dto .NodeDto ;
2028import com .tinyengine .it .service .app .v1 .AiChatV1Service ;
29+ import org .slf4j .Logger ;
30+ import org .slf4j .LoggerFactory ;
2131import org .springframework .stereotype .Service ;
2232import org .springframework .web .servlet .mvc .method .annotation .StreamingResponseBody ;
2333
2939import java .net .http .HttpResponse ;
3040import java .nio .charset .StandardCharsets ;
3141import java .time .Duration ;
42+ import java .util .ArrayList ;
3243import java .util .HashMap ;
44+ import java .util .List ;
3345import java .util .Map ;
3446
3547/**
3951 */
4052@ Service
4153public class AiChatV1ServiceImpl implements AiChatV1Service {
54+ private static final String ACCESS_KEY_ID = System .getenv ("ACCESS_KEY_ID" );
55+ private static final String ACCESS_KEY_SECRET = System .getenv ("ACCESS_KEY_SECRET" );
56+ private static final String ENDPOINT = "bailian.cn-beijing.aliyuncs.com" ;
57+ private static final String INDEX_ID = System .getenv ("INDEX_ID" );
58+ private static final String WORK_SPACE_ID = System .getenv ("WORK_SPACE_ID" );
4259 private final OpenAIConfig config = new OpenAIConfig ();
60+ private static final Logger log = LoggerFactory .getLogger (AiChatV1ServiceImpl .class );
4361 private HttpClient httpClient = HttpClient .newBuilder ()
44- .connectTimeout (Duration .ofSeconds (config .getTimeoutSeconds ()))
45- .build ();
62+ .connectTimeout (Duration .ofSeconds (config .getTimeoutSeconds ()))
63+ .build ();
4664
4765 /**
4866 * chatCompletion.
@@ -61,10 +79,10 @@ public Object chatCompletion(ChatRequest request) throws Exception {
6179 String normalizedUrl = normalizeApiUrl (baseUrl );
6280
6381 HttpRequest .Builder requestBuilder = HttpRequest .newBuilder ()
64- .uri (URI .create (normalizedUrl ))
65- .header ("Content-Type" , "application/json" )
66- .header ("Authorization" , "Bearer " + apiKey )
67- .POST (HttpRequest .BodyPublishers .ofString (requestBody ));
82+ .uri (URI .create (normalizedUrl ))
83+ .header ("Content-Type" , "application/json" )
84+ .header ("Authorization" , "Bearer " + apiKey )
85+ .POST (HttpRequest .BodyPublishers .ofString (requestBody ));
6886 if (request .isStream ()) {
6987 requestBuilder .header ("Accept" , "text/event-stream" );
7088 return processStreamResponse (requestBuilder );
@@ -90,7 +108,7 @@ private String normalizeApiUrl(String baseUrl) {
90108 return ensureUrlProtocol (baseUrl ) + "/chat/completions" ;
91109 } else {
92110 return ensureUrlProtocol (baseUrl ) + "/v1/chat/completions" ;
93- }
111+ }
94112 }
95113
96114 /**
@@ -104,6 +122,160 @@ private String ensureUrlProtocol(String url) {
104122 return "https://" + url ;
105123 }
106124
125+ /**
126+ * 创建客户端
127+ */
128+ private Client createClient () throws Exception {
129+ return new Client (new Config ()
130+ .setAccessKeyId (ACCESS_KEY_ID )
131+ .setAccessKeySecret (ACCESS_KEY_SECRET )
132+ .setEndpoint (ENDPOINT )
133+ .setEndpointType ("access_key" ));
134+ }
135+
136+ /**
137+ * 创建API信息
138+ */
139+ private Params createApiInfo (String WorkspaceId ) throws Exception {
140+ return new Params ()
141+ // 接口名称
142+ .setAction ("Retrieve" )
143+ // 接口版本
144+ .setVersion ("2023-12-29" )
145+ // 接口协议
146+ .setProtocol ("HTTPS" )
147+ // 接口 HTTP 方法
148+ .setMethod ("POST" )
149+ .setAuthType ("AK" )
150+ .setStyle ("ROA" )
151+ // 接口 PATH
152+ .setPathname ("/" + com .aliyun .openapiutil .Client .getEncodeParam (WorkspaceId ) + "/index/retrieve" )
153+ // 接口请求体内容格式
154+ .setReqBodyType ("json" )
155+ // 接口响应体内容格式
156+ .setBodyType ("json" );
157+ }
158+
159+ /**
160+ * 安全类型转换工具方法
161+ */
162+ private <T > T safeCast (Object obj , Class <T > clazz , T defaultValue ) {
163+ if (obj == null ) {
164+ return defaultValue ;
165+ }
166+ try {
167+ return clazz .cast (obj );
168+ } catch (ClassCastException e ) {
169+ log .warn ("类型转换失败: {} 无法转换为 {}" , obj .getClass ().getName (), clazz .getName ());
170+ return defaultValue ;
171+ }
172+ }
173+
174+ private String safeCastToString (Object obj ) {
175+ return safeCast (obj , String .class , "" );
176+ }
177+
178+ private Double safeCastToDouble (Object obj ) {
179+ return safeCast (obj , Double .class , 0.0 );
180+ }
181+
182+ private Long safeCastToLong (Object obj ) {
183+ return safeCast (obj , Long .class , 0L );
184+ }
185+
186+ /**
187+ * chatSearch.
188+ *
189+ * @param content the content
190+ * @return String the String
191+ */
192+ public Result chatSearch (String content ) {
193+ try {
194+ Client client = createClient ();
195+ Params params = createApiInfo (WORK_SPACE_ID );
196+
197+ Map <String , Object > queries = new HashMap <>();
198+ queries .put ("IndexId" , INDEX_ID );
199+ queries .put ("Query" , content );
200+ queries .put ("EnableRewrite" , "true" );
201+
202+ RuntimeOptions runtime = new RuntimeOptions ();
203+ OpenApiRequest request = new OpenApiRequest ()
204+ .setQuery (com .aliyun .openapiutil .Client .query (queries ));
205+
206+ Map <String , ?> response = client .callApi (params , request , runtime );
207+ Map <String , Object > body = (Map <String , Object >) response .get ("body" );
208+
209+ if (body == null ) {
210+ return Result .failed ("响应体为空" );
211+ }
212+
213+ long status = safeCastToLong (body .get ("Status" ));
214+ if (status != 200L ) {
215+ String message = safeCastToString (body .get ("Message" ));
216+ log .error ("搜索失败: status={}, message={}" , status , message );
217+ return Result .failed ("搜索失败: " + message );
218+ }
219+
220+ Map data = safeCast (body .get ("Data" ), Map .class , new HashMap <>());
221+ if (data == null || data .isEmpty ()) {
222+ return Result .success (new ArrayList <>());
223+ }
224+
225+ List nodes = safeCast (data .get ("Nodes" ), List .class , new ArrayList <>());
226+ if (nodes .isEmpty ()) {
227+ return Result .success (new ArrayList <>());
228+ }
229+
230+ List nodeDtos = convertToNodeDtos (nodes );
231+ return Result .success (nodeDtos );
232+
233+ } catch (TeaException e ) {
234+ log .error ("阿里云Tea异常: {}" , e .getMessage (), e );
235+ return Result .failed ("阿里云服务异常: " + e .getMessage ());
236+ } catch (Exception e ) {
237+ log .error ("搜索异常: {}" , e .getMessage (), e );
238+ return Result .failed ("系统异常: " + e .getMessage ());
239+ }
240+ }
241+
242+ /**
243+ * 转换节点数据
244+ */
245+ private List <NodeDto > convertToNodeDtos (List <Map <String , Object >> nodes ) {
246+ List <NodeDto > nodeDtos = new ArrayList <>();
247+
248+ for (Map <String , Object > node : nodes ) {
249+ try {
250+ NodeDto nodeDto = new NodeDto ();
251+
252+ // 安全获取文本内容
253+ nodeDto .setContent (safeCastToString (node .get ("Text" )));
254+
255+ // 安全获取分数
256+ Object scoreObj = node .get ("Score" );
257+ if (scoreObj instanceof Number ) {
258+ nodeDto .setScore (((Number ) scoreObj ).doubleValue ());
259+ } else {
260+ nodeDto .setScore (safeCastToDouble (scoreObj ));
261+ }
262+
263+ // 安全获取元数据
264+ Map metadata = safeCast (node .get ("Metadata" ), Map .class , new HashMap <>());
265+ if (metadata != null ) {
266+ nodeDto .setDocName (safeCastToString (metadata .get ("doc_name" )));
267+ }
268+
269+ nodeDtos .add (nodeDto );
270+
271+ } catch (Exception e ) {
272+ log .warn ("节点数据转换失败: {}" , e .getMessage ());
273+ }
274+ }
275+
276+ return nodeDtos ;
277+ }
278+
107279 private String buildRequestBody (ChatRequest request ) {
108280 Map <String , Object > body = new HashMap <>();
109281 body .put ("model" , request .getModel () != null ? request .getModel () : config .getDefaultModel ());
@@ -120,9 +292,9 @@ private String buildRequestBody(ChatRequest request) {
120292 }
121293
122294 private JsonNode processStandardResponse (HttpRequest .Builder requestBuilder )
123- throws Exception {
295+ throws Exception {
124296 HttpResponse <String > response = httpClient .send (
125- requestBuilder .build (), HttpResponse .BodyHandlers .ofString ());
297+ requestBuilder .build (), HttpResponse .BodyHandlers .ofString ());
126298 return JsonUtils .MAPPER .readTree (response .body ());
127299 }
128300
@@ -131,8 +303,8 @@ private StreamingResponseBody processStreamResponse(HttpRequest.Builder requestB
131303 try {
132304 HttpClient client = HttpClient .newHttpClient ();
133305 HttpResponse <InputStream > response = client .send (
134- requestBuilder .build (),
135- HttpResponse .BodyHandlers .ofInputStream ()
306+ requestBuilder .build (),
307+ HttpResponse .BodyHandlers .ofInputStream ()
136308 );
137309 if (response .statusCode () != 200 ) {
138310 String errorBody = new String (response .body ().readAllBytes (), StandardCharsets .UTF_8 );
0 commit comments