Commit c357e7ae55d9bb8a76b67775974c2a6d4f39dba7

Authored by 芋道源码
Committed by Gitee
2 parents 7179786a 47df4bb2

!1317 【优化代码】AI PPT:优化文多多、讯飞 API

Merge pull request !1317 from 小新/feature/ai
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/wenduoduo/api/WddPptApi.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java
@@ -8,6 +8,7 @@ import org.springframework.core.ParameterizedTypeReference; @@ -8,6 +8,7 @@ import org.springframework.core.ParameterizedTypeReference;
8 import org.springframework.http.HttpRequest; 8 import org.springframework.http.HttpRequest;
9 import org.springframework.http.HttpStatusCode; 9 import org.springframework.http.HttpStatusCode;
10 import org.springframework.http.MediaType; 10 import org.springframework.http.MediaType;
  11 +import org.springframework.util.Assert;
11 import org.springframework.util.LinkedMultiValueMap; 12 import org.springframework.util.LinkedMultiValueMap;
12 import org.springframework.util.MultiValueMap; 13 import org.springframework.util.MultiValueMap;
13 import org.springframework.web.multipart.MultipartFile; 14 import org.springframework.web.multipart.MultipartFile;
@@ -24,18 +25,18 @@ import java.util.Objects; @@ -24,18 +25,18 @@ import java.util.Objects;
24 import java.util.function.Function; 25 import java.util.function.Function;
25 import java.util.function.Predicate; 26 import java.util.function.Predicate;
26 27
27 -// TODO @新:要不改成 WenDuoDuoPptApi 28 +
28 /** 29 /**
29 * 文多多 API 30 * 文多多 API
30 * 31 *
31 - * @see <a href="https://docmee.cn/open-platform/api">PPT 生成 API</a>  
32 - *  
33 * @author xiaoxin 32 * @author xiaoxin
  33 + * @see <a href="https://docmee.cn/open-platform/api">PPT 生成 API</a>
34 */ 34 */
35 @Slf4j 35 @Slf4j
36 -public class WddPptApi { 36 +public class WenDuoDuoPptApi {
37 37
38 public static final String BASE_URL = "https://docmee.cn"; 38 public static final String BASE_URL = "https://docmee.cn";
  39 + public static final String TOKEN_NAME = "token";
39 40
40 private final WebClient webClient; 41 private final WebClient webClient;
41 42
@@ -44,17 +45,24 @@ public class WddPptApi { @@ -44,17 +45,24 @@ public class WddPptApi {
44 private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION = 45 private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
45 reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> { 46 reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
46 HttpRequest request = response.request(); 47 HttpRequest request = response.request();
47 - log.error("[wdd-api] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]", 48 + log.error("[WenDuoDuoPptApi] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
48 request.getMethod(), request.getURI(), reqParam, responseBody); 49 request.getMethod(), request.getURI(), reqParam, responseBody);
49 - sink.error(new IllegalStateException("[wdd-api] 调用失败!")); 50 + sink.error(new IllegalStateException("[WenDuoDuoPptApi] 调用失败!"));
50 }); 51 });
51 52
52 - // TODO @新:是不是不用 baseUrl 哈  
53 - public WddPptApi(String baseUrl) { 53 + /**
  54 + * 构造方法
  55 + *
  56 + * @param token API令牌,可为空,后续API调用时单独指定
  57 + */
  58 + public WenDuoDuoPptApi(String token) {
  59 + Assert.hasText(token, "token 不能为空");
54 this.webClient = WebClient.builder() 60 this.webClient = WebClient.builder()
55 - .baseUrl(baseUrl)  
56 - // TODO @新:建议,token 作为 defaultHeader  
57 - .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON)) 61 + .baseUrl(BASE_URL)
  62 + .defaultHeaders((headers) -> {
  63 + headers.setContentType(MediaType.APPLICATION_JSON);
  64 + headers.add(TOKEN_NAME, token);
  65 + })
58 .build(); 66 .build();
59 } 67 }
60 68
@@ -82,37 +90,16 @@ public class WddPptApi { @@ -82,37 +90,16 @@ public class WddPptApi {
82 .block(); 90 .block();
83 } 91 }
84 92
85 - // TODO @xin:是不是给个 API 连接,这样 type、content、files 都不用写注释太细了  
86 /** 93 /**
87 * 创建任务 94 * 创建任务
88 * 95 *
89 * @param type 类型 96 * @param type 类型
90 - * 1.智能生成(主题、要求)  
91 - * 2.上传文件生成  
92 - * 3.上传思维导图生成  
93 - * 4.通过word精准转ppt  
94 - * 5.通过网页链接生成  
95 - * 6.粘贴文本内容生成  
96 - * 7.Markdown大纲生成  
97 * @param content 内容 97 * @param content 内容
98 - * type=1 用户输入主题或要求(不超过1000字符)  
99 - * type=2、4 不传  
100 - * type=3 幕布等分享链接  
101 - * type=5 网页链接地址(http/https)  
102 - * type=6 粘贴文本内容(不超过20000字符)  
103 - * type=7 大纲内容(markdown)  
104 * @param files 文件列表 98 * @param files 文件列表
105 - * 文件列表(文件数不超过5个,总大小不超过50M):  
106 - * type=1 上传参考文件(非必传,支持多个)  
107 - * type=2 上传文件(支持多个)  
108 - * type=3 上传思维导图(xmind/mm/md)(仅支持一个)  
109 - * type=4 上传word文件(仅支持一个)  
110 - * type=5、6、7 不传  
111 - * <p>  
112 - * 支持格式:doc/docx/pdf/ppt/pptx/txt/md/xls/xlsx/csv/html/epub/mobi/xmind/mm  
113 * @return 任务ID 99 * @return 任务ID
  100 + * @see <a href="https://docmee.cn/open-platform/api#%E5%88%9B%E5%BB%BA%E4%BB%BB%E5%8A%A1">创建任务</a>
114 */ 101 */
115 - public ApiResponse createTask(String token, Integer type, String content, List<MultipartFile> files) { 102 + public ApiResponse createTask(Integer type, String content, List<MultipartFile> files) {
116 MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>(); 103 MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
117 formData.add("type", type); 104 formData.add("type", type);
118 if (content != null) { 105 if (content != null) {
@@ -125,7 +112,6 @@ public class WddPptApi { @@ -125,7 +112,6 @@ public class WddPptApi {
125 } 112 }
126 return this.webClient.post() 113 return this.webClient.post()
127 .uri("/api/ppt/v2/createTask") 114 .uri("/api/ppt/v2/createTask")
128 - .header("token", token)  
129 .contentType(MediaType.MULTIPART_FORM_DATA) 115 .contentType(MediaType.MULTIPART_FORM_DATA)
130 .body(BodyInserters.fromMultipartData(formData)) 116 .body(BodyInserters.fromMultipartData(formData))
131 .retrieve() 117 .retrieve()
@@ -168,10 +154,9 @@ public class WddPptApi { @@ -168,10 +154,9 @@ public class WddPptApi {
168 * @param request 请求体 154 * @param request 请求体
169 * @return 模板列表 155 * @return 模板列表
170 */ 156 */
171 - public PagePptTemplateInfo getTemplatePage(String token, TemplateQueryRequest request) { 157 + public PagePptTemplateInfo getTemplatePage(TemplateQueryRequest request) {
172 return this.webClient.post() 158 return this.webClient.post()
173 .uri("/api/ppt/templates") 159 .uri("/api/ppt/templates")
174 - .header("token", token)  
175 .bodyValue(request) 160 .bodyValue(request)
176 .retrieve() 161 .retrieve()
177 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) 162 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
@@ -185,10 +170,9 @@ public class WddPptApi { @@ -185,10 +170,9 @@ public class WddPptApi {
185 * 170 *
186 * @return 大纲内容流 171 * @return 大纲内容流
187 */ 172 */
188 - public Flux<Map<String, Object>> createOutline(String token, CreateOutlineRequest request) { 173 + public Flux<Map<String, Object>> createOutline(CreateOutlineRequest request) {
189 return this.webClient.post() 174 return this.webClient.post()
190 .uri("/api/ppt/v2/generateContent") 175 .uri("/api/ppt/v2/generateContent")
191 - .header("token", token)  
192 .body(Mono.just(request), CreateOutlineRequest.class) 176 .body(Mono.just(request), CreateOutlineRequest.class)
193 .retrieve() 177 .retrieve()
194 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) 178 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
@@ -202,10 +186,9 @@ public class WddPptApi { @@ -202,10 +186,9 @@ public class WddPptApi {
202 * @param request 请求体 186 * @param request 请求体
203 * @return 大纲内容流 187 * @return 大纲内容流
204 */ 188 */
205 - public Flux<Map<String, Object>> updateOutline(String token, UpdateOutlineRequest request) { 189 + public Flux<Map<String, Object>> updateOutline(UpdateOutlineRequest request) {
206 return this.webClient.post() 190 return this.webClient.post()
207 .uri("/api/ppt/v2/updateContent") 191 .uri("/api/ppt/v2/updateContent")
208 - .header("token", token)  
209 .body(Mono.just(request), UpdateOutlineRequest.class) 192 .body(Mono.just(request), UpdateOutlineRequest.class)
210 .retrieve() 193 .retrieve()
211 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) 194 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
@@ -218,11 +201,10 @@ public class WddPptApi { @@ -218,11 +201,10 @@ public class WddPptApi {
218 * 201 *
219 * @return PPT信息 202 * @return PPT信息
220 */ 203 */
221 - public PptInfo create(String token, CreatePptRequest request) { 204 + public PptInfo create(PptCreateRequest request) {
222 return this.webClient.post() 205 return this.webClient.post()
223 .uri("/api/ppt/v2/generatePptx") 206 .uri("/api/ppt/v2/generatePptx")
224 - .header("token", token)  
225 - .body(Mono.just(request), CreatePptRequest.class) 207 + .body(Mono.just(request), PptCreateRequest.class)
226 .retrieve() 208 .retrieve()
227 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) 209 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
228 .bodyToMono(ApiResponse.class) 210 .bodyToMono(ApiResponse.class)
@@ -236,7 +218,9 @@ public class WddPptApi { @@ -236,7 +218,9 @@ public class WddPptApi {
236 .block(); 218 .block();
237 } 219 }
238 220
239 - 221 + /**
  222 + * 创建Token请求参数
  223 + */
240 @JsonInclude(value = JsonInclude.Include.NON_NULL) 224 @JsonInclude(value = JsonInclude.Include.NON_NULL)
241 public record CreateTokenRequest( 225 public record CreateTokenRequest(
242 String apiKey, 226 String apiKey,
@@ -258,7 +242,8 @@ public class WddPptApi { @@ -258,7 +242,8 @@ public class WddPptApi {
258 Integer code, 242 Integer code,
259 String message, 243 String message,
260 Map<String, Object> data 244 Map<String, Object> data
261 - ) { } 245 + ) {
  246 + }
262 247
263 /** 248 /**
264 * 创建任务 249 * 创建任务
@@ -268,7 +253,8 @@ public class WddPptApi { @@ -268,7 +253,8 @@ public class WddPptApi {
268 Integer type, 253 Integer type,
269 String content, 254 String content,
270 List<MultipartFile> files 255 List<MultipartFile> files
271 - ) { } 256 + ) {
  257 + }
272 258
273 /** 259 /**
274 * 生成大纲内容请求 260 * 生成大纲内容请求
@@ -281,7 +267,8 @@ public class WddPptApi { @@ -281,7 +267,8 @@ public class WddPptApi {
281 String audience, 267 String audience,
282 String lang, 268 String lang,
283 String prompt 269 String prompt
284 - ) { } 270 + ) {
  271 + }
285 272
286 /** 273 /**
287 * 修改大纲内容请求 274 * 修改大纲内容请求
@@ -291,20 +278,23 @@ public class WddPptApi { @@ -291,20 +278,23 @@ public class WddPptApi {
291 String id, 278 String id,
292 String markdown, 279 String markdown,
293 String question 280 String question
294 - ) { } 281 + ) {
  282 + }
295 283
296 /** 284 /**
297 - * 生成 PPT 请求 285 + * 生成 PPT 请求参数
298 */ 286 */
299 - // TODO @新:要不按照 PptCreateRequest 这样的风格  
300 @JsonInclude(value = JsonInclude.Include.NON_NULL) 287 @JsonInclude(value = JsonInclude.Include.NON_NULL)
301 - public record CreatePptRequest( 288 + public record PptCreateRequest(
302 String id, 289 String id,
303 String templateId, 290 String templateId,
304 String markdown 291 String markdown
305 - ) { } 292 + ) {
  293 + }
306 294
307 - // TODO @新:要不写下类注释 295 + /**
  296 + * PPT 信息
  297 + */
308 @JsonInclude(value = JsonInclude.Include.NON_NULL) 298 @JsonInclude(value = JsonInclude.Include.NON_NULL)
309 public record PptInfo( 299 public record PptInfo(
310 String id, 300 String id,
@@ -323,9 +313,12 @@ public class WddPptApi { @@ -323,9 +313,12 @@ public class WddPptApi {
323 LocalDateTime createTime, 313 LocalDateTime createTime,
324 String createUser, 314 String createUser,
325 String updateUser 315 String updateUser
326 - ) { } 316 + ) {
  317 + }
327 318
328 - // TODO @新:要不写下类注释 319 + /**
  320 + * 模板查询请求参数
  321 + */
329 @JsonInclude(value = JsonInclude.Include.NON_NULL) 322 @JsonInclude(value = JsonInclude.Include.NON_NULL)
330 public record TemplateQueryRequest( 323 public record TemplateQueryRequest(
331 int page, 324 int page,
@@ -333,25 +326,33 @@ public class WddPptApi { @@ -333,25 +326,33 @@ public class WddPptApi {
333 Filter filters 326 Filter filters
334 ) { 327 ) {
335 328
  329 + /**
  330 + * 模板查询过滤条件
  331 + */
336 @JsonInclude(value = JsonInclude.Include.NON_NULL) 332 @JsonInclude(value = JsonInclude.Include.NON_NULL)
337 public record Filter( 333 public record Filter(
338 int type, 334 int type,
339 String category, 335 String category,
340 String style, 336 String style,
341 String themeColor 337 String themeColor
342 - ) { } 338 + ) {
  339 + }
343 340
344 } 341 }
345 342
346 - // TODO @新:要不写下类注释 343 + /**
  344 + * PPT模板分页信息
  345 + */
347 @JsonInclude(value = JsonInclude.Include.NON_NULL) 346 @JsonInclude(value = JsonInclude.Include.NON_NULL)
348 public record PagePptTemplateInfo( 347 public record PagePptTemplateInfo(
349 List<PptTemplateInfo> data, 348 List<PptTemplateInfo> data,
350 String total 349 String total
351 - ) {}  
352 - 350 + ) {
  351 + }
353 352
354 - // TODO @新:要不写下类注释 353 + /**
  354 + * PPT模板信息
  355 + */
355 @JsonInclude(value = JsonInclude.Include.NON_NULL) 356 @JsonInclude(value = JsonInclude.Include.NON_NULL)
356 public record PptTemplateInfo( 357 public record PptTemplateInfo(
357 String id, 358 String id,
@@ -380,6 +381,7 @@ public class WddPptApi { @@ -380,6 +381,7 @@ public class WddPptApi {
380 LocalDateTime createTime, 381 LocalDateTime createTime,
381 String createUser, 382 String createUser,
382 String updateUser 383 String updateUser
383 - ) { } 384 + ) {
  385 + }
384 386
385 } 387 }
386 \ No newline at end of file 388 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/api/XunfeiPptApi.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java
1 package cn.iocoder.yudao.framework.ai.core.model.xinghuo.api; 1 package cn.iocoder.yudao.framework.ai.core.model.xinghuo.api;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.hutool.crypto.SecureUtil;
  5 +import cn.hutool.crypto.digest.HmacAlgorithm;
3 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 import com.fasterxml.jackson.annotation.JsonInclude; 7 import com.fasterxml.jackson.annotation.JsonInclude;
  8 +import lombok.Builder;
5 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
6 import org.springframework.core.io.ByteArrayResource; 10 import org.springframework.core.io.ByteArrayResource;
7 import org.springframework.http.HttpStatusCode; 11 import org.springframework.http.HttpStatusCode;
8 import org.springframework.http.MediaType; 12 import org.springframework.http.MediaType;
9 import org.springframework.util.LinkedMultiValueMap; 13 import org.springframework.util.LinkedMultiValueMap;
10 import org.springframework.util.MultiValueMap; 14 import org.springframework.util.MultiValueMap;
  15 +import org.springframework.util.StringUtils;
11 import org.springframework.web.multipart.MultipartFile; 16 import org.springframework.web.multipart.MultipartFile;
12 import org.springframework.web.reactive.function.BodyInserters; 17 import org.springframework.web.reactive.function.BodyInserters;
13 import org.springframework.web.reactive.function.client.ClientResponse; 18 import org.springframework.web.reactive.function.client.ClientResponse;
14 import org.springframework.web.reactive.function.client.WebClient; 19 import org.springframework.web.reactive.function.client.WebClient;
15 import reactor.core.publisher.Mono; 20 import reactor.core.publisher.Mono;
16 21
17 -import javax.crypto.Mac;  
18 -import javax.crypto.spec.SecretKeySpec;  
19 import java.io.IOException; 22 import java.io.IOException;
20 -import java.nio.charset.StandardCharsets;  
21 -import java.security.InvalidKeyException;  
22 -import java.security.MessageDigest;  
23 -import java.security.NoSuchAlgorithmException;  
24 -import java.util.Base64;  
25 import java.util.HashMap; 23 import java.util.HashMap;
26 import java.util.List; 24 import java.util.List;
27 import java.util.Map; 25 import java.util.Map;
28 import java.util.function.Function; 26 import java.util.function.Function;
29 import java.util.function.Predicate; 27 import java.util.function.Predicate;
30 28
31 -// TODO @新:要不改成 XunFeiPptApi  
32 /** 29 /**
33 * 讯飞智能 PPT 生成 API 30 * 讯飞智能 PPT 生成 API
34 * 31 *
35 - * @see <a href="https://www.xfyun.cn/doc/spark/PPTv2.html">智能 PPT 生成 API</a>  
36 - *  
37 * @author xiaoxin 32 * @author xiaoxin
  33 + * @see <a href="https://www.xfyun.cn/doc/spark/PPTv2.html">智能 PPT 生成 API</a>
38 */ 34 */
39 @Slf4j 35 @Slf4j
40 -public class XunfeiPptApi { 36 +public class XunFeiPptApi {
41 37
42 public static final String BASE_URL = "https://zwapi.xfyun.cn/api/ppt/v2"; 38 public static final String BASE_URL = "https://zwapi.xfyun.cn/api/ppt/v2";
  39 + private static final String HEADER_APP_ID = "appId";
  40 + private static final String HEADER_TIMESTAMP = "timestamp";
  41 + private static final String HEADER_SIGNATURE = "signature";
43 42
44 private final WebClient webClient; 43 private final WebClient webClient;
45 private final String appId; 44 private final String appId;
@@ -49,18 +48,21 @@ public class XunfeiPptApi { @@ -49,18 +48,21 @@ public class XunfeiPptApi {
49 48
50 private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION = 49 private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
51 reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> { 50 reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
52 - log.error("[xunfei-ppt-api] 调用失败!请求参数:[{}],响应数据: [{}]", reqParam, responseBody);  
53 - sink.error(new IllegalStateException("[xunfei-ppt-api] 调用失败!")); 51 + log.error("[XunFeiPptApi] 调用失败!请求参数:[{}],响应数据: [{}]", reqParam, responseBody);
  52 + sink.error(new IllegalStateException("[XunFeiPptApi] 调用失败!"));
54 }); 53 });
55 54
56 - // TODO @新:是不是不用 baseUrl 哈  
57 - public XunfeiPptApi(String baseUrl, String appId, String apiSecret) {  
58 - // TODO @新:建议,增加 defaultheaders,例如说 appid 之类的;或者每个请求,通过 headers customer 处理。  
59 - this.webClient = WebClient.builder()  
60 - .baseUrl(baseUrl)  
61 - .build(); 55 + public XunFeiPptApi(String appId, String apiSecret) {
62 this.appId = appId; 56 this.appId = appId;
63 this.apiSecret = apiSecret; 57 this.apiSecret = apiSecret;
  58 + this.webClient = WebClient.builder()
  59 + .baseUrl(BASE_URL)
  60 + .defaultHeaders((headers) -> {
  61 + headers.setContentType(MediaType.APPLICATION_JSON);
  62 + headers.add(HEADER_APP_ID, appId);
  63 + })
  64 + .build();
  65 +
64 } 66 }
65 67
66 /** 68 /**
@@ -72,7 +74,7 @@ public class XunfeiPptApi { @@ -72,7 +74,7 @@ public class XunfeiPptApi {
72 long timestamp = System.currentTimeMillis() / 1000; 74 long timestamp = System.currentTimeMillis() / 1000;
73 String ts = String.valueOf(timestamp); 75 String ts = String.valueOf(timestamp);
74 String signature = generateSignature(appId, apiSecret, timestamp); 76 String signature = generateSignature(appId, apiSecret, timestamp);
75 - return new SignatureInfo(appId, ts, signature); 77 + return new SignatureInfo(ts, signature);
76 } 78 }
77 79
78 /** 80 /**
@@ -84,43 +86,8 @@ public class XunfeiPptApi { @@ -84,43 +86,8 @@ public class XunfeiPptApi {
84 * @return 签名 86 * @return 签名
85 */ 87 */
86 private String generateSignature(String appId, String apiSecret, long timestamp) { 88 private String generateSignature(String appId, String apiSecret, long timestamp) {
87 - try {  
88 - // TODO @新:使用 hutool 简化  
89 - String auth = md5(appId + timestamp);  
90 - return hmacSHA1Encrypt(auth, apiSecret);  
91 - } catch (NoSuchAlgorithmException | InvalidKeyException e) {  
92 - log.error("[xunfei-ppt-api] 生成签名失败", e);  
93 - throw new IllegalStateException("[xunfei-ppt-api] 生成签名失败");  
94 - }  
95 - }  
96 -  
97 - /**  
98 - * HMAC SHA1 加密  
99 - */  
100 - private String hmacSHA1Encrypt(String encryptText, String encryptKey)  
101 - throws NoSuchAlgorithmException, InvalidKeyException {  
102 - SecretKeySpec keySpec = new SecretKeySpec(  
103 - encryptKey.getBytes(StandardCharsets.UTF_8), "HmacSHA1");  
104 -  
105 - Mac mac = Mac.getInstance("HmacSHA1");  
106 - mac.init(keySpec);  
107 - byte[] result = mac.doFinal(encryptText.getBytes(StandardCharsets.UTF_8));  
108 -  
109 - return Base64.getEncoder().encodeToString(result);  
110 - }  
111 -  
112 - /**  
113 - * MD5 哈希  
114 - */  
115 - private String md5(String text) throws NoSuchAlgorithmException {  
116 - MessageDigest md = MessageDigest.getInstance("MD5");  
117 - byte[] digest = md.digest(text.getBytes(StandardCharsets.UTF_8));  
118 -  
119 - StringBuilder sb = new StringBuilder();  
120 - for (byte b : digest) {  
121 - sb.append(String.format("%02x", b));  
122 - }  
123 - return sb.toString(); 89 + String auth = SecureUtil.md5(appId + timestamp);
  90 + return SecureUtil.hmac(HmacAlgorithm.HmacSHA1, apiSecret).digestBase64(auth, false);
124 } 91 }
125 92
126 /** 93 /**
@@ -134,13 +101,11 @@ public class XunfeiPptApi { @@ -134,13 +101,11 @@ public class XunfeiPptApi {
134 SignatureInfo signInfo = getSignature(); 101 SignatureInfo signInfo = getSignature();
135 Map<String, Object> requestBody = new HashMap<>(); 102 Map<String, Object> requestBody = new HashMap<>();
136 requestBody.put("style", style); 103 requestBody.put("style", style);
137 - // TODO @新:可以使用 ObjUtil.defaultIfNull  
138 - requestBody.put("pageSize", pageSize != null ? pageSize : 10); 104 + requestBody.put("pageSize", ObjUtil.defaultIfNull(pageSize, 20));
139 return this.webClient.post() 105 return this.webClient.post()
140 .uri("/template/list") 106 .uri("/template/list")
141 - .header("appId", signInfo.appId)  
142 - .header("timestamp", signInfo.timestamp)  
143 - .header("signature", signInfo.signature) 107 + .header(HEADER_TIMESTAMP, signInfo.timestamp)
  108 + .header(HEADER_SIGNATURE, signInfo.signature)
144 .contentType(MediaType.APPLICATION_JSON) 109 .contentType(MediaType.APPLICATION_JSON)
145 .bodyValue(requestBody) 110 .bodyValue(requestBody)
146 .retrieve() 111 .retrieve()
@@ -161,9 +126,8 @@ public class XunfeiPptApi { @@ -161,9 +126,8 @@ public class XunfeiPptApi {
161 formData.add("query", query); 126 formData.add("query", query);
162 return this.webClient.post() 127 return this.webClient.post()
163 .uri("/createOutline") 128 .uri("/createOutline")
164 - .header("appId", signInfo.appId)  
165 - .header("timestamp", signInfo.timestamp)  
166 - .header("signature", signInfo.signature) 129 + .header(HEADER_TIMESTAMP, signInfo.timestamp)
  130 + .header(HEADER_SIGNATURE, signInfo.signature)
167 .contentType(MediaType.MULTIPART_FORM_DATA) 131 .contentType(MediaType.MULTIPART_FORM_DATA)
168 .body(BodyInserters.fromMultipartData(formData)) 132 .body(BodyInserters.fromMultipartData(formData))
169 .retrieve() 133 .retrieve()
@@ -207,12 +171,11 @@ public class XunfeiPptApi { @@ -207,12 +171,11 @@ public class XunfeiPptApi {
207 */ 171 */
208 public CreateResponse create(CreatePptRequest request) { 172 public CreateResponse create(CreatePptRequest request) {
209 SignatureInfo signInfo = getSignature(); 173 SignatureInfo signInfo = getSignature();
210 - MultiValueMap<String, Object> formData = buildCreateFormData(request); 174 + MultiValueMap<String, Object> formData = buildCreatePptFormData(request);
211 return this.webClient.post() 175 return this.webClient.post()
212 .uri("/create") 176 .uri("/create")
213 - .header("appId", signInfo.appId)  
214 - .header("timestamp", signInfo.timestamp)  
215 - .header("signature", signInfo.signature) 177 + .header(HEADER_TIMESTAMP, signInfo.timestamp)
  178 + .header(HEADER_SIGNATURE, signInfo.signature)
216 .contentType(MediaType.MULTIPART_FORM_DATA) 179 .contentType(MediaType.MULTIPART_FORM_DATA)
217 .body(BodyInserters.fromMultipartData(formData)) 180 .body(BodyInserters.fromMultipartData(formData))
218 .retrieve() 181 .retrieve()
@@ -247,9 +210,8 @@ public class XunfeiPptApi { @@ -247,9 +210,8 @@ public class XunfeiPptApi {
247 SignatureInfo signInfo = getSignature(); 210 SignatureInfo signInfo = getSignature();
248 return this.webClient.post() 211 return this.webClient.post()
249 .uri("/createPptByOutline") 212 .uri("/createPptByOutline")
250 - .header("appId", signInfo.appId)  
251 - .header("timestamp", signInfo.timestamp)  
252 - .header("signature", signInfo.signature) 213 + .header(HEADER_TIMESTAMP, signInfo.timestamp)
  214 + .header(HEADER_SIGNATURE, signInfo.signature)
253 .contentType(MediaType.APPLICATION_JSON) 215 .contentType(MediaType.APPLICATION_JSON)
254 .bodyValue(request) 216 .bodyValue(request)
255 .retrieve() 217 .retrieve()
@@ -271,9 +233,8 @@ public class XunfeiPptApi { @@ -271,9 +233,8 @@ public class XunfeiPptApi {
271 .path("/progress") 233 .path("/progress")
272 .queryParam("sid", sid) 234 .queryParam("sid", sid)
273 .build()) 235 .build())
274 - .header("appId", signInfo.appId)  
275 - .header("timestamp", signInfo.timestamp)  
276 - .header("signature", signInfo.signature) 236 + .header(HEADER_TIMESTAMP, signInfo.timestamp)
  237 + .header(HEADER_SIGNATURE, signInfo.signature)
277 .retrieve() 238 .retrieve()
278 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(sid)) 239 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(sid))
279 .bodyToMono(ProgressResponse.class) 240 .bodyToMono(ProgressResponse.class)
@@ -285,10 +246,10 @@ public class XunfeiPptApi { @@ -285,10 +246,10 @@ public class XunfeiPptApi {
285 */ 246 */
286 @JsonInclude(value = JsonInclude.Include.NON_NULL) 247 @JsonInclude(value = JsonInclude.Include.NON_NULL)
287 private record SignatureInfo( 248 private record SignatureInfo(
288 - String appId,  
289 String timestamp, 249 String timestamp,
290 String signature 250 String signature
291 - ) { } 251 + ) {
  252 + }
292 253
293 /** 254 /**
294 * 模板列表响应 255 * 模板列表响应
@@ -300,7 +261,8 @@ public class XunfeiPptApi { @@ -300,7 +261,8 @@ public class XunfeiPptApi {
300 String desc, 261 String desc,
301 Integer count, 262 Integer count,
302 TemplatePageData data 263 TemplatePageData data
303 - ) { } 264 + ) {
  265 + }
304 266
305 /** 267 /**
306 * 模板列表数据 268 * 模板列表数据
@@ -310,7 +272,8 @@ public class XunfeiPptApi { @@ -310,7 +272,8 @@ public class XunfeiPptApi {
310 String total, 272 String total,
311 List<TemplateInfo> records, 273 List<TemplateInfo> records,
312 Integer pageNum 274 Integer pageNum
313 - ) { } 275 + ) {
  276 + }
314 277
315 /** 278 /**
316 * 模板信息 279 * 模板信息
@@ -324,7 +287,8 @@ public class XunfeiPptApi { @@ -324,7 +287,8 @@ public class XunfeiPptApi {
324 String industry, 287 String industry,
325 String style, 288 String style,
326 String detailImage 289 String detailImage
327 - ) { } 290 + ) {
  291 + }
328 292
329 /** 293 /**
330 * 创建响应 294 * 创建响应
@@ -336,7 +300,8 @@ public class XunfeiPptApi { @@ -336,7 +300,8 @@ public class XunfeiPptApi {
336 String desc, 300 String desc,
337 Integer count, 301 Integer count,
338 CreateResponseData data 302 CreateResponseData data
339 - ) { } 303 + ) {
  304 + }
340 305
341 /** 306 /**
342 * 创建响应数据 307 * 创建响应数据
@@ -348,7 +313,8 @@ public class XunfeiPptApi { @@ -348,7 +313,8 @@ public class XunfeiPptApi {
348 String title, 313 String title,
349 String subTitle, 314 String subTitle,
350 OutlineData outline 315 OutlineData outline
351 - ) { } 316 + ) {
  317 + }
352 318
353 /** 319 /**
354 * 大纲数据结构 320 * 大纲数据结构
@@ -375,7 +341,8 @@ public class XunfeiPptApi { @@ -375,7 +341,8 @@ public class XunfeiPptApi {
375 @JsonInclude(value = JsonInclude.Include.NON_NULL) 341 @JsonInclude(value = JsonInclude.Include.NON_NULL)
376 public record ChapterContent( 342 public record ChapterContent(
377 String chapterTitle 343 String chapterTitle
378 - ) { } 344 + ) {
  345 + }
379 346
380 } 347 }
381 348
@@ -397,7 +364,8 @@ public class XunfeiPptApi { @@ -397,7 +364,8 @@ public class XunfeiPptApi {
397 int code, 364 int code,
398 String desc, 365 String desc,
399 ProgressResponseData data 366 ProgressResponseData data
400 - ) { } 367 + ) {
  368 + }
401 369
402 /** 370 /**
403 * 进度响应数据 371 * 进度响应数据
@@ -407,13 +375,12 @@ public class XunfeiPptApi { @@ -407,13 +375,12 @@ public class XunfeiPptApi {
407 int process, 375 int process,
408 String pptId, 376 String pptId,
409 String pptUrl, 377 String pptUrl,
410 - // TODO @新:字段注释,去掉  
411 - String pptStatus, // PPT构建状态:building(构建中),done(已完成),build_failed(生成失败)  
412 - String aiImageStatus, // ai配图状态:building(构建中),done(已完成)  
413 - String cardNoteStatus, // 演讲备注状态:building(构建中),done(已完成)  
414 - String errMsg, // 生成PPT的失败信息  
415 - Integer totalPages, // 生成PPT的总页数  
416 - Integer donePages // 生成PPT的完成页数 378 + String pptStatus,
  379 + String aiImageStatus,
  380 + String cardNoteStatus,
  381 + String errMsg,
  382 + Integer totalPages,
  383 + Integer donePages
417 ) { 384 ) {
418 385
419 /** 386 /**
@@ -454,6 +421,7 @@ public class XunfeiPptApi { @@ -454,6 +421,7 @@ public class XunfeiPptApi {
454 * 通过大纲创建 PPT 请求参数 421 * 通过大纲创建 PPT 请求参数
455 */ 422 */
456 @JsonInclude(value = JsonInclude.Include.NON_NULL) 423 @JsonInclude(value = JsonInclude.Include.NON_NULL)
  424 + @Builder
457 public record CreatePptByOutlineRequest( 425 public record CreatePptByOutlineRequest(
458 String query, // 用户生成PPT要求(最多8000字) 426 String query, // 用户生成PPT要求(最多8000字)
459 String outlineSid, // 已生成大纲后,响应返回的请求大纲唯一id 427 String outlineSid, // 已生成大纲后,响应返回的请求大纲唯一id
@@ -469,122 +437,18 @@ public class XunfeiPptApi { @@ -469,122 +437,18 @@ public class XunfeiPptApi {
469 Boolean isFigure, // 是否自动配图 437 Boolean isFigure, // 是否自动配图
470 String aiImage // ai配图类型:normal、advanced 438 String aiImage // ai配图类型:normal、advanced
471 ) { 439 ) {
472 -  
473 - /**  
474 - * 创建构建器  
475 - *  
476 - * @return 构建器  
477 - */  
478 - public static Builder builder() {  
479 - return new Builder();  
480 - }  
481 -  
482 - // TODO @新:这个可以用 lombok 简化么?  
483 - /**  
484 - * 构建器类  
485 - */  
486 - public static class Builder {  
487 -  
488 - private String query;  
489 - private String outlineSid;  
490 - private OutlineData outline;  
491 - private String templateId;  
492 - private String businessId;  
493 - private String author;  
494 - private Boolean isCardNote;  
495 - private Boolean search;  
496 - private String language;  
497 - private String fileUrl;  
498 - private String fileName;  
499 - private Boolean isFigure;  
500 - private String aiImage;  
501 -  
502 - public Builder query(String query) {  
503 - this.query = query;  
504 - return this;  
505 - }  
506 -  
507 - public Builder outlineSid(String outlineSid) {  
508 - this.outlineSid = outlineSid;  
509 - return this;  
510 - }  
511 -  
512 - public Builder outline(OutlineData outline) {  
513 - this.outline = outline;  
514 - return this;  
515 - }  
516 -  
517 - public Builder templateId(String templateId) {  
518 - this.templateId = templateId;  
519 - return this;  
520 - }  
521 -  
522 - public Builder businessId(String businessId) {  
523 - this.businessId = businessId;  
524 - return this;  
525 - }  
526 -  
527 - public Builder author(String author) {  
528 - this.author = author;  
529 - return this;  
530 - }  
531 -  
532 - public Builder isCardNote(Boolean isCardNote) {  
533 - this.isCardNote = isCardNote;  
534 - return this;  
535 - }  
536 -  
537 - public Builder search(Boolean search) {  
538 - this.search = search;  
539 - return this;  
540 - }  
541 -  
542 - public Builder language(String language) {  
543 - this.language = language;  
544 - return this;  
545 - }  
546 -  
547 - public Builder fileUrl(String fileUrl) {  
548 - this.fileUrl = fileUrl;  
549 - return this;  
550 - }  
551 -  
552 - public Builder fileName(String fileName) {  
553 - this.fileName = fileName;  
554 - return this;  
555 - }  
556 -  
557 - public Builder isFigure(Boolean isFigure) {  
558 - this.isFigure = isFigure;  
559 - return this;  
560 - }  
561 -  
562 - public Builder aiImage(String aiImage) {  
563 - this.aiImage = aiImage;  
564 - return this;  
565 - }  
566 -  
567 - public CreatePptByOutlineRequest build() {  
568 - return new CreatePptByOutlineRequest(  
569 - query, outlineSid, outline, templateId, businessId, author,  
570 - isCardNote, search, language, fileUrl, fileName, isFigure, aiImage  
571 - );  
572 - }  
573 - }  
574 } 440 }
575 441
  442 +
576 /** 443 /**
577 * 构建创建 PPT 的表单数据 444 * 构建创建 PPT 的表单数据
578 * 445 *
579 * @param request 请求参数 446 * @param request 请求参数
580 * @return 表单数据 447 * @return 表单数据
581 */ 448 */
582 - private MultiValueMap<String, Object> buildCreateFormData(CreatePptRequest request) { 449 + private MultiValueMap<String, Object> buildCreatePptFormData(CreatePptRequest request) {
583 MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>(); 450 MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
584 - // 添加请求参数  
585 - if (request.query() != null) {  
586 - formData.add("query", request.query());  
587 - } 451 +
588 if (request.file() != null) { 452 if (request.file() != null) {
589 try { 453 try {
590 formData.add("file", new ByteArrayResource(request.file().getBytes()) { 454 formData.add("file", new ByteArrayResource(request.file().getBytes()) {
@@ -594,48 +458,52 @@ public class XunfeiPptApi { @@ -594,48 +458,52 @@ public class XunfeiPptApi {
594 } 458 }
595 }); 459 });
596 } catch (IOException e) { 460 } catch (IOException e) {
597 - log.error("[xunfei-ppt-api] 文件处理失败", e);  
598 - throw new IllegalStateException("[xunfei-ppt-api] 文件处理失败", e); 461 + log.error("[XunFeiPptApi] 文件处理失败", e);
  462 + throw new IllegalStateException("[XunFeiPptApi] 文件处理失败", e);
599 } 463 }
600 } 464 }
601 - // TODO @新:要不搞个 MapUtil.addIfPresent 方法?  
602 - if (request.fileUrl() != null) {  
603 - formData.add("fileUrl", request.fileUrl());  
604 - }  
605 - if (request.fileName() != null) {  
606 - formData.add("fileName", request.fileName());  
607 - }  
608 - if (request.templateId() != null) {  
609 - formData.add("templateId", request.templateId());  
610 - }  
611 - if (request.businessId() != null) {  
612 - formData.add("businessId", request.businessId());  
613 - }  
614 - if (request.author() != null) {  
615 - formData.add("author", request.author());  
616 - }  
617 - if (request.isCardNote() != null) {  
618 - formData.add("isCardNote", request.isCardNote().toString());  
619 - }  
620 - if (request.search() != null) {  
621 - formData.add("search", request.search().toString());  
622 - }  
623 - if (request.language() != null) {  
624 - formData.add("language", request.language()); 465 + Map<String, Object> param = new HashMap<>();
  466 + addIfPresent(param, "query", request.query());
  467 + addIfPresent(param, "fileUrl", request.fileUrl());
  468 + addIfPresent(param, "fileName", request.fileName());
  469 + addIfPresent(param, "templateId", request.templateId());
  470 + addIfPresent(param, "businessId", request.businessId());
  471 + addIfPresent(param, "author", request.author());
  472 + addIfPresent(param, "isCardNote", request.isCardNote());
  473 + addIfPresent(param, "search", request.search());
  474 + addIfPresent(param, "language", request.language());
  475 + addIfPresent(param, "isFigure", request.isFigure());
  476 + addIfPresent(param, "aiImage", request.aiImage());
  477 + param.forEach(formData::add);
  478 + return formData;
  479 + }
  480 +
  481 + public static <K, V> void addIfPresent(Map<K, V> map, K key, V value) {
  482 + if (ObjUtil.isNull(key) || ObjUtil.isNull(map)) {
  483 + return;
625 } 484 }
626 - if (request.isFigure() != null) {  
627 - formData.add("isFigure", request.isFigure().toString()); 485 +
  486 + boolean isPresent = false;
  487 + if (ObjUtil.isNotNull(value)) {
  488 + if (value instanceof String) {
  489 + // 字符串:需要有实际内容
  490 + isPresent = StringUtils.hasText((String) value);
  491 + } else {
  492 + // 其他类型:非 null 即视为存在
  493 + isPresent = true;
  494 + }
628 } 495 }
629 - if (request.aiImage() != null) {  
630 - formData.add("aiImage", request.aiImage()); 496 +
  497 + if (isPresent) {
  498 + map.put(key, value);
631 } 499 }
632 - return formData;  
633 } 500 }
634 501
635 /** 502 /**
636 * 直接生成PPT请求参数 503 * 直接生成PPT请求参数
637 */ 504 */
638 @JsonInclude(value = JsonInclude.Include.NON_NULL) 505 @JsonInclude(value = JsonInclude.Include.NON_NULL)
  506 + @Builder
639 public record CreatePptRequest( 507 public record CreatePptRequest(
640 String query, // 用户生成PPT要求(最多8000字) 508 String query, // 用户生成PPT要求(最多8000字)
641 MultipartFile file, // 上传文件 509 MultipartFile file, // 上传文件
@@ -651,109 +519,6 @@ public class XunfeiPptApi { @@ -651,109 +519,6 @@ public class XunfeiPptApi {
651 String aiImage // ai配图类型:normal、advanced 519 String aiImage // ai配图类型:normal、advanced
652 ) { 520 ) {
653 521
654 - /**  
655 - * 创建构建器  
656 - *  
657 - * @return 构建器  
658 - */  
659 - public static Builder builder() {  
660 - return new Builder();  
661 - }  
662 -  
663 - /**  
664 - * 构建器类  
665 - */  
666 - public static class Builder {  
667 -  
668 - private String query;  
669 - private MultipartFile file;  
670 - private String fileUrl;  
671 - private String fileName;  
672 - private String templateId;  
673 - private String businessId;  
674 - private String author;  
675 - private Boolean isCardNote;  
676 - private Boolean search;  
677 - private String language;  
678 - private Boolean isFigure;  
679 - private String aiImage;  
680 -  
681 - // TODO @新:这个可以用 lombok 简化么?  
682 -  
683 - public Builder query(String query) {  
684 - this.query = query;  
685 - return this;  
686 - }  
687 -  
688 - public Builder file(MultipartFile file) {  
689 - this.file = file;  
690 - return this;  
691 - }  
692 -  
693 - public Builder fileUrl(String fileUrl) {  
694 - this.fileUrl = fileUrl;  
695 - return this;  
696 - }  
697 -  
698 - public Builder fileName(String fileName) {  
699 - this.fileName = fileName;  
700 - return this;  
701 - }  
702 -  
703 - public Builder templateId(String templateId) {  
704 - this.templateId = templateId;  
705 - return this;  
706 - }  
707 -  
708 - public Builder businessId(String businessId) {  
709 - this.businessId = businessId;  
710 - return this;  
711 - }  
712 -  
713 - public Builder author(String author) {  
714 - this.author = author;  
715 - return this;  
716 - }  
717 -  
718 - public Builder isCardNote(Boolean isCardNote) {  
719 - this.isCardNote = isCardNote;  
720 - return this;  
721 - }  
722 -  
723 - public Builder search(Boolean search) {  
724 - this.search = search;  
725 - return this;  
726 - }  
727 -  
728 - public Builder language(String language) {  
729 - this.language = language;  
730 - return this;  
731 - }  
732 -  
733 - public Builder isFigure(Boolean isFigure) {  
734 - this.isFigure = isFigure;  
735 - return this;  
736 - }  
737 -  
738 - public Builder aiImage(String aiImage) {  
739 - this.aiImage = aiImage;  
740 - return this;  
741 - }  
742 -  
743 - public CreatePptRequest build() {  
744 - // 验证参数  
745 - if (query == null && file == null && fileUrl == null) {  
746 - throw new IllegalArgumentException("query、file、fileUrl必填其一");  
747 - }  
748 - if ((file != null || fileUrl != null) && fileName == null) {  
749 - throw new IllegalArgumentException("如果传file或者fileUrl,fileName必填");  
750 - }  
751 - return new CreatePptRequest(  
752 - query, file, fileUrl, fileName, templateId, businessId, author,  
753 - isCardNote, search, language, isFigure, aiImage  
754 - );  
755 - }  
756 - }  
757 } 522 }
758 523
759 } 524 }
760 \ No newline at end of file 525 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mcp/DouBaoMcpTests.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.mcp;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.ai.chat.client.ChatClient;
  6 +import org.springframework.ai.openai.OpenAiChatModel;
  7 +import org.springframework.ai.openai.OpenAiChatOptions;
  8 +import org.springframework.ai.openai.api.OpenAiApi;
  9 +import org.springframework.ai.tool.annotation.Tool;
  10 +import org.springframework.ai.tool.method.MethodToolCallbackProvider;
  11 +
  12 +
  13 +public class DouBaoMcpTests {
  14 +
  15 +
  16 + private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
  17 + .openAiApi(OpenAiApi.builder()
  18 + .baseUrl(DouBaoChatModel.BASE_URL)
  19 + .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
  20 + .build())
  21 + .defaultOptions(OpenAiChatOptions.builder()
  22 + .model("doubao-1-5-lite-32k-250115") // 模型(doubao)
  23 + .temperature(0.7)
  24 + .build())
  25 + .build();
  26 +
  27 + private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);
  28 +
  29 + private final MethodToolCallbackProvider provider = MethodToolCallbackProvider.builder()
  30 + .toolObjects(new UserService())
  31 + .build();
  32 +
  33 + private final ChatClient chatClient = ChatClient.builder(chatModel)
  34 + .defaultTools(provider)
  35 + .build();
  36 +
  37 + @Test
  38 + public void testMcpGetUserInfo() {
  39 +
  40 + // 打印结果
  41 + System.out.println(chatClient.prompt()
  42 + .user("目前有哪些工具可以使用")
  43 + .call()
  44 + .content());
  45 + System.out.println("====================================");
  46 + // 打印结果
  47 + System.out.println(chatClient.prompt()
  48 + .user("小新的年龄是多少")
  49 + .call()
  50 + .content());
  51 + System.out.println("====================================");
  52 + // 打印结果
  53 + System.out.println(chatClient.prompt()
  54 + .user("获取小新的基本信息")
  55 + .call()
  56 + .content());
  57 + System.out.println("====================================");
  58 + // 打印结果
  59 + System.out.println(chatClient.prompt()
  60 + .user("小新是什么职业的")
  61 + .call()
  62 + .content());
  63 + System.out.println("====================================");
  64 + // 打印结果
  65 + System.out.println(chatClient.prompt()
  66 + .user("小新的教育背景")
  67 + .call()
  68 + .content());
  69 + System.out.println("====================================");
  70 + // 打印结果
  71 + System.out.println(chatClient.prompt()
  72 + .user("小新的兴趣爱好是什么")
  73 + .call()
  74 + .content());
  75 + System.out.println("====================================");
  76 +
  77 + }
  78 +
  79 +
  80 + static class UserService {
  81 +
  82 + @Tool(name = "getUserAge", description = "获取用户年龄")
  83 + public String getUserAge(String userName) {
  84 + return "《" + userName + "》的年龄为:18";
  85 + }
  86 +
  87 + @Tool(name = "getUserSex", description = "获取用户性别")
  88 + public String getUserSex(String userName) {
  89 + return "《" + userName + "》的性别为:男";
  90 + }
  91 +
  92 + @Tool(name = "getUserBasicInfo", description = "获取用户基本信息,包括姓名、年龄、性别等")
  93 + public String getUserBasicInfo(String userName) {
  94 + return "《" + userName + "》的基本信息:\n姓名:" + userName + "\n年龄:18\n性别:男\n身高:175cm\n体重:65kg";
  95 + }
  96 +
  97 + @Tool(name = "getUserContact", description = "获取用户联系方式,包括电话、邮箱等")
  98 + public String getUserContact(String userName) {
  99 + return "《" + userName + "》的联系方式:\n电话:138****1234\n邮箱:" + userName.toLowerCase() + "@example.com\nQQ:123456789";
  100 + }
  101 +
  102 + @Tool(name = "getUserAddress", description = "获取用户地址信息")
  103 + public String getUserAddress(String userName) {
  104 + return "《" + userName + "》的地址信息:北京市朝阳区科技园区88号";
  105 + }
  106 +
  107 + @Tool(name = "getUserJob", description = "获取用户职业信息")
  108 + public String getUserJob(String userName) {
  109 + return "《" + userName + "》的职业信息:软件工程师,就职于ABC科技有限公司,工作年限5年";
  110 + }
  111 +
  112 + @Tool(name = "getUserHobbies", description = "获取用户兴趣爱好")
  113 + public String getUserHobbies(String userName) {
  114 + return "《" + userName + "》的兴趣爱好:编程、阅读、旅游、摄影、打篮球";
  115 + }
  116 +
  117 + @Tool(name = "getUserEducation", description = "获取用户教育背景")
  118 + public String getUserEducation(String userName) {
  119 + return "《" + userName + "》的教育背景:\n本科:计算机科学与技术专业,北京大学\n硕士:软件工程专业,清华大学";
  120 + }
  121 +
  122 + }
  123 +
  124 +}
0 \ No newline at end of file 125 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WddPptApiTests.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java
1 package cn.iocoder.yudao.framework.ai.ppt.wdd; 1 package cn.iocoder.yudao.framework.ai.ppt.wdd;
2 2
3 -import cn.iocoder.yudao.framework.ai.core.model.wenduoduo.api.WddPptApi; 3 +import cn.iocoder.yudao.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi;
4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 import org.junit.jupiter.api.Disabled; 5 import org.junit.jupiter.api.Disabled;
6 import org.junit.jupiter.api.Test; 6 import org.junit.jupiter.api.Test;
@@ -10,24 +10,28 @@ import java.util.Map; @@ -10,24 +10,28 @@ import java.util.Map;
10 import java.util.Objects; 10 import java.util.Objects;
11 11
12 /** 12 /**
13 - * {@link WddPptApi} 集成测试 13 + * {@link WenDuoDuoPptApi} 集成测试
14 * 14 *
15 * @author xiaoxin 15 * @author xiaoxin
16 */ 16 */
17 -public class WddPptApiTests { 17 +public class WenDuoDuoPptApiTests {
18 18
19 - private final WddPptApi wddPptApi = new WddPptApi("https://docmee.cn"); 19 + private final WenDuoDuoPptApi wenDuoDuoPptApi;
20 20
21 private final String token = ""; // API Token 21 private final String token = ""; // API Token
22 22
  23 + {
  24 + wenDuoDuoPptApi = new WenDuoDuoPptApi(token);
  25 + }
  26 +
23 @Test 27 @Test
24 @Disabled 28 @Disabled
25 public void testCreateApiToken() { 29 public void testCreateApiToken() {
26 // 准备参数 30 // 准备参数
27 String apiKey = ""; 31 String apiKey = "";
28 - WddPptApi.CreateTokenRequest request = new WddPptApi.CreateTokenRequest(apiKey); 32 + WenDuoDuoPptApi.CreateTokenRequest request = new WenDuoDuoPptApi.CreateTokenRequest(apiKey);
29 // 调用方法 33 // 调用方法
30 - String token = wddPptApi.createApiToken(request); 34 + String token = wenDuoDuoPptApi.createApiToken(request);
31 // 打印结果 35 // 打印结果
32 System.out.println(token); 36 System.out.println(token);
33 } 37 }
@@ -38,7 +42,7 @@ public class WddPptApiTests { @@ -38,7 +42,7 @@ public class WddPptApiTests {
38 @Test 42 @Test
39 @Disabled 43 @Disabled
40 public void testCreateTask() { 44 public void testCreateTask() {
41 - WddPptApi.ApiResponse apiResponse = wddPptApi.createTask(token, 1, "dify 介绍", null); 45 + WenDuoDuoPptApi.ApiResponse apiResponse = wenDuoDuoPptApi.createTask(1, "dify 介绍", null);
42 System.out.println(apiResponse); 46 System.out.println(apiResponse);
43 } 47 }
44 48
@@ -46,10 +50,10 @@ public class WddPptApiTests { @@ -46,10 +50,10 @@ public class WddPptApiTests {
46 @Test // 创建大纲 50 @Test // 创建大纲
47 @Disabled 51 @Disabled
48 public void testGenerateOutlineRequest() { 52 public void testGenerateOutlineRequest() {
49 - WddPptApi.CreateOutlineRequest request = new WddPptApi.CreateOutlineRequest( 53 + WenDuoDuoPptApi.CreateOutlineRequest request = new WenDuoDuoPptApi.CreateOutlineRequest(
50 "1901539019628613632", "medium", null, null, null, null); 54 "1901539019628613632", "medium", null, null, null, null);
51 // 调用 55 // 调用
52 - Flux<Map<String, Object>> flux = wddPptApi.createOutline(token, request); 56 + Flux<Map<String, Object>> flux = wenDuoDuoPptApi.createOutline(request);
53 StringBuffer contentBuffer = new StringBuffer(); 57 StringBuffer contentBuffer = new StringBuffer();
54 flux.doOnNext(chunk -> { 58 flux.doOnNext(chunk -> {
55 contentBuffer.append(chunk.get("text")); 59 contentBuffer.append(chunk.get("text"));
@@ -69,10 +73,10 @@ public class WddPptApiTests { @@ -69,10 +73,10 @@ public class WddPptApiTests {
69 @Test 73 @Test
70 @Disabled 74 @Disabled
71 public void testUpdateOutlineRequest() { 75 public void testUpdateOutlineRequest() {
72 - WddPptApi.UpdateOutlineRequest request = new WddPptApi.UpdateOutlineRequest( 76 + WenDuoDuoPptApi.UpdateOutlineRequest request = new WenDuoDuoPptApi.UpdateOutlineRequest(
73 "1901539019628613632", TEST_OUT_LINE_CONTENT, "精简一点,三个章节即可"); 77 "1901539019628613632", TEST_OUT_LINE_CONTENT, "精简一点,三个章节即可");
74 // 调用 78 // 调用
75 - Flux<Map<String, Object>> flux = wddPptApi.updateOutline(token, request); 79 + Flux<Map<String, Object>> flux = wenDuoDuoPptApi.updateOutline(request);
76 StringBuffer contentBuffer = new StringBuffer(); 80 StringBuffer contentBuffer = new StringBuffer();
77 flux.doOnNext(chunk -> { 81 flux.doOnNext(chunk -> {
78 contentBuffer.append(chunk.get("text")); 82 contentBuffer.append(chunk.get("text"));
@@ -94,11 +98,11 @@ public class WddPptApiTests { @@ -94,11 +98,11 @@ public class WddPptApiTests {
94 @Disabled 98 @Disabled
95 public void testGetPptTemplatePage() { 99 public void testGetPptTemplatePage() {
96 // 准备参数 100 // 准备参数
97 - WddPptApi.TemplateQueryRequest.Filter filter = new WddPptApi.TemplateQueryRequest.Filter( 101 + WenDuoDuoPptApi.TemplateQueryRequest.Filter filter = new WenDuoDuoPptApi.TemplateQueryRequest.Filter(
98 1, null, null, null); 102 1, null, null, null);
99 - WddPptApi.TemplateQueryRequest request = new WddPptApi.TemplateQueryRequest(1, 10, filter); 103 + WenDuoDuoPptApi.TemplateQueryRequest request = new WenDuoDuoPptApi.TemplateQueryRequest(1, 10, filter);
100 // 调用 104 // 调用
101 - WddPptApi.PagePptTemplateInfo pptTemplatePage = wddPptApi.getTemplatePage(token, request); 105 + WenDuoDuoPptApi.PagePptTemplateInfo pptTemplatePage = wenDuoDuoPptApi.getTemplatePage(request);
102 // 打印结果 106 // 打印结果
103 System.out.println(pptTemplatePage); 107 System.out.println(pptTemplatePage);
104 } 108 }
@@ -110,9 +114,9 @@ public class WddPptApiTests { @@ -110,9 +114,9 @@ public class WddPptApiTests {
110 @Disabled 114 @Disabled
111 public void testGeneratePptx() { 115 public void testGeneratePptx() {
112 // 准备参数 116 // 准备参数
113 - WddPptApi.CreatePptRequest request = new WddPptApi.CreatePptRequest("1901539019628613632", "1805081814809960448", TEST_OUT_LINE_CONTENT); 117 + WenDuoDuoPptApi.PptCreateRequest request = new WenDuoDuoPptApi.PptCreateRequest("1901539019628613632", "1805081814809960448", TEST_OUT_LINE_CONTENT);
114 // 调用 118 // 调用
115 - WddPptApi.PptInfo pptInfo = wddPptApi.create(token, request); 119 + WenDuoDuoPptApi.PptInfo pptInfo = wenDuoDuoPptApi.create(request);
116 // 打印结果 120 // 打印结果
117 System.out.println(pptInfo); 121 System.out.println(pptInfo);
118 } 122 }
@@ -309,6 +313,7 @@ public class WddPptApiTests { @@ -309,6 +313,7 @@ public class WddPptApiTests {
309 #### 7.2.2 合作共赢 313 #### 7.2.2 合作共赢
310 期待与更多的企业和机构合作,共同推动AI技术的应用。 314 期待与更多的企业和机构合作,共同推动AI技术的应用。
311 #### 7.2.3 共创未来 315 #### 7.2.3 共创未来
312 - 让我们一起用AI技术改变世界,共创美好未来。"""; 316 + 让我们一起用AI技术改变世界,共创美好未来。
  317 + """;
313 318
314 } 319 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/xunfei/XunfeiPptApiTests.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/ppt/xunfei/XunFeiPptApiTests.java
1 package cn.iocoder.yudao.framework.ai.ppt.xunfei; 1 package cn.iocoder.yudao.framework.ai.ppt.xunfei;
2 2
3 import cn.hutool.core.io.FileUtil; 3 import cn.hutool.core.io.FileUtil;
4 -import cn.iocoder.yudao.framework.ai.core.model.xinghuo.api.XunfeiPptApi; 4 +import cn.iocoder.yudao.framework.ai.core.model.xinghuo.api.XunFeiPptApi;
5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 import org.junit.jupiter.api.Disabled; 6 import org.junit.jupiter.api.Disabled;
7 import org.junit.jupiter.api.Test; 7 import org.junit.jupiter.api.Test;
@@ -11,17 +11,17 @@ import org.springframework.web.multipart.MultipartFile; @@ -11,17 +11,17 @@ import org.springframework.web.multipart.MultipartFile;
11 import java.io.File; 11 import java.io.File;
12 12
13 /** 13 /**
14 - * {@link XunfeiPptApi} 集成测试 14 + * {@link XunFeiPptApi} 集成测试
15 * 15 *
16 * @author xiaoxin 16 * @author xiaoxin
17 */ 17 */
18 -public class XunfeiPptApiTests { 18 +public class XunFeiPptApiTests {
19 19
20 // 讯飞 API 配置信息,实际使用时请替换为您的应用信息 20 // 讯飞 API 配置信息,实际使用时请替换为您的应用信息
21 - private static final String APP_ID = "";  
22 - private static final String API_SECRET = ""; 21 + private static final String APP_ID = "6c8ac023";
  22 + private static final String API_SECRET = "Y2RjM2Q1MWJjZTdkYmFiODc0OGE5NmRk";
23 23
24 - private final XunfeiPptApi xunfeiPptApi = new XunfeiPptApi(XunfeiPptApi.BASE_URL, APP_ID, API_SECRET); 24 + private final XunFeiPptApi xunfeiPptApi = new XunFeiPptApi(APP_ID, API_SECRET);
25 25
26 /** 26 /**
27 * 获取 PPT 模板列表 27 * 获取 PPT 模板列表
@@ -30,7 +30,7 @@ public class XunfeiPptApiTests { @@ -30,7 +30,7 @@ public class XunfeiPptApiTests {
30 @Disabled 30 @Disabled
31 public void testGetTemplatePage() { 31 public void testGetTemplatePage() {
32 // 调用方法 32 // 调用方法
33 - XunfeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage("商务", 10); 33 + XunFeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage("商务", 10);
34 // 打印结果 34 // 打印结果
35 System.out.println("模板列表响应:" + JsonUtils.toJsonString(response)); 35 System.out.println("模板列表响应:" + JsonUtils.toJsonString(response));
36 36
@@ -41,7 +41,7 @@ public class XunfeiPptApiTests { @@ -41,7 +41,7 @@ public class XunfeiPptApiTests {
41 41
42 // 打印第一个模板的信息(如果存在) 42 // 打印第一个模板的信息(如果存在)
43 if (!response.data().records().isEmpty()) { 43 if (!response.data().records().isEmpty()) {
44 - XunfeiPptApi.TemplateInfo firstTemplate = response.data().records().get(0); 44 + XunFeiPptApi.TemplateInfo firstTemplate = response.data().records().get(0);
45 System.out.println("模板ID:" + firstTemplate.templateIndexId()); 45 System.out.println("模板ID:" + firstTemplate.templateIndexId());
46 System.out.println("模板风格:" + firstTemplate.style()); 46 System.out.println("模板风格:" + firstTemplate.style());
47 System.out.println("模板颜色:" + firstTemplate.color()); 47 System.out.println("模板颜色:" + firstTemplate.color());
@@ -56,7 +56,7 @@ public class XunfeiPptApiTests { @@ -56,7 +56,7 @@ public class XunfeiPptApiTests {
56 @Test 56 @Test
57 @Disabled 57 @Disabled
58 public void testCreateOutline() { 58 public void testCreateOutline() {
59 - XunfeiPptApi.CreateResponse response = getCreateResponse(); 59 + XunFeiPptApi.CreateResponse response = getCreateResponse();
60 // 打印结果 60 // 打印结果
61 System.out.println("创建大纲响应:" + JsonUtils.toJsonString(response)); 61 System.out.println("创建大纲响应:" + JsonUtils.toJsonString(response));
62 62
@@ -75,9 +75,10 @@ public class XunfeiPptApiTests { @@ -75,9 +75,10 @@ public class XunfeiPptApiTests {
75 75
76 /** 76 /**
77 * 创建大纲(通过文本) 77 * 创建大纲(通过文本)
  78 + *
78 * @return 创建大纲响应 79 * @return 创建大纲响应
79 */ 80 */
80 - private XunfeiPptApi.CreateResponse getCreateResponse() { 81 + private XunFeiPptApi.CreateResponse getCreateResponse() {
81 String param = "智能体平台 Dify 介绍"; 82 String param = "智能体平台 Dify 介绍";
82 return xunfeiPptApi.createOutline(param); 83 return xunfeiPptApi.createOutline(param);
83 } 84 }
@@ -89,9 +90,9 @@ public class XunfeiPptApiTests { @@ -89,9 +90,9 @@ public class XunfeiPptApiTests {
89 @Disabled 90 @Disabled
90 public void testCreatePptByOutlineWithFullParams() { 91 public void testCreatePptByOutlineWithFullParams() {
91 // 创建大纲对象 92 // 创建大纲对象
92 - XunfeiPptApi.CreateResponse createResponse = getCreateResponse(); 93 + XunFeiPptApi.CreateResponse createResponse = getCreateResponse();
93 // 调用方法 94 // 调用方法
94 - XunfeiPptApi.CreateResponse response = xunfeiPptApi.createPptByOutline(createResponse.data().outline(), "精简一些,不要超过6个章节"); 95 + XunFeiPptApi.CreateResponse response = xunfeiPptApi.createPptByOutline(createResponse.data().outline(), "精简一些,不要超过6个章节");
95 // 打印结果 96 // 打印结果
96 System.out.println("通过大纲创建 PPT 响应:" + JsonUtils.toJsonString(response)); 97 System.out.println("通过大纲创建 PPT 响应:" + JsonUtils.toJsonString(response));
97 98
@@ -114,13 +115,13 @@ public class XunfeiPptApiTests { @@ -114,13 +115,13 @@ public class XunfeiPptApiTests {
114 String sid = "e96dac09f2ec4ee289f029a5fb874ecd"; // 替换为实际的sid 115 String sid = "e96dac09f2ec4ee289f029a5fb874ecd"; // 替换为实际的sid
115 116
116 // 调用方法 117 // 调用方法
117 - XunfeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid); 118 + XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);
118 // 打印结果 119 // 打印结果
119 System.out.println("检查进度响应:" + JsonUtils.toJsonString(response)); 120 System.out.println("检查进度响应:" + JsonUtils.toJsonString(response));
120 121
121 // 安全地访问响应数据 122 // 安全地访问响应数据
122 if (response != null && response.data() != null) { 123 if (response != null && response.data() != null) {
123 - XunfeiPptApi.ProgressResponseData data = response.data(); 124 + XunFeiPptApi.ProgressResponseData data = response.data();
124 125
125 // 打印PPT生成状态 126 // 打印PPT生成状态
126 System.out.println("PPT 构建状态: " + data.pptStatus()); 127 System.out.println("PPT 构建状态: " + data.pptStatus());
@@ -160,7 +161,7 @@ public class XunfeiPptApiTests { @@ -160,7 +161,7 @@ public class XunfeiPptApiTests {
160 @Disabled 161 @Disabled
161 public void testPollCheckProgress() throws InterruptedException { 162 public void testPollCheckProgress() throws InterruptedException {
162 // 准备参数 - 使用之前创建 PP T时返回的 sid 163 // 准备参数 - 使用之前创建 PP T时返回的 sid
163 - String sid = "fa36e926f2ed434987fcb4c1f0776ffb"; // 替换为实际的sid 164 + String sid = "1690ef6ee0344e72b5c5434f403b8eaa"; // 替换为实际的sid
164 165
165 // 最大轮询次数 166 // 最大轮询次数
166 int maxPolls = 20; 167 int maxPolls = 20;
@@ -171,11 +172,11 @@ public class XunfeiPptApiTests { @@ -171,11 +172,11 @@ public class XunfeiPptApiTests {
171 System.out.println("第" + (i + 1) + "次查询进度..."); 172 System.out.println("第" + (i + 1) + "次查询进度...");
172 173
173 // 调用方法 174 // 调用方法
174 - XunfeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid); 175 + XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);
175 176
176 // 安全地访问响应数据 177 // 安全地访问响应数据
177 if (response != null && response.data() != null) { 178 if (response != null && response.data() != null) {
178 - XunfeiPptApi.ProgressResponseData data = response.data(); 179 + XunFeiPptApi.ProgressResponseData data = response.data();
179 180
180 // 打印进度信息 181 // 打印进度信息
181 System.out.println("PPT 构建状态: " + data.pptStatus()); 182 System.out.println("PPT 构建状态: " + data.pptStatus());
@@ -218,7 +219,7 @@ public class XunfeiPptApiTests { @@ -218,7 +219,7 @@ public class XunfeiPptApiTests {
218 String query = "合肥天气趋势分析,包括近5年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响"; 219 String query = "合肥天气趋势分析,包括近5年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响";
219 220
220 // 调用方法 221 // 调用方法
221 - XunfeiPptApi.CreateResponse response = xunfeiPptApi.create(query); 222 + XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(query);
222 // 打印结果 223 // 打印结果
223 System.out.println("直接创建 PPT 响应:" + JsonUtils.toJsonString(response)); 224 System.out.println("直接创建 PPT 响应:" + JsonUtils.toJsonString(response));
224 225
@@ -244,7 +245,7 @@ public class XunfeiPptApiTests { @@ -244,7 +245,7 @@ public class XunfeiPptApiTests {
244 MultipartFile multipartFile = convertFileToMultipartFile(file); 245 MultipartFile multipartFile = convertFileToMultipartFile(file);
245 246
246 // 调用方法 247 // 调用方法
247 - XunfeiPptApi.CreateResponse response = xunfeiPptApi.create(multipartFile, file.getName()); 248 + XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(multipartFile, file.getName());
248 // 打印结果 249 // 打印结果
249 System.out.println("通过文件创建PPT响应:" + JsonUtils.toJsonString(response)); 250 System.out.println("通过文件创建PPT响应:" + JsonUtils.toJsonString(response));
250 251
@@ -269,7 +270,7 @@ public class XunfeiPptApiTests { @@ -269,7 +270,7 @@ public class XunfeiPptApiTests {
269 String query = "合肥天气趋势分析,包括近 5 年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响"; 270 String query = "合肥天气趋势分析,包括近 5 年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响";
270 271
271 // 创建请求对象 272 // 创建请求对象
272 - XunfeiPptApi.CreatePptRequest request = XunfeiPptApi.CreatePptRequest.builder() 273 + XunFeiPptApi.CreatePptRequest request = XunFeiPptApi.CreatePptRequest.builder()
273 .query(query) 274 .query(query)
274 .language("cn") 275 .language("cn")
275 .isCardNote(true) 276 .isCardNote(true)
@@ -280,7 +281,7 @@ public class XunfeiPptApiTests { @@ -280,7 +281,7 @@ public class XunfeiPptApiTests {
280 .build(); 281 .build();
281 282
282 // 调用方法 283 // 调用方法
283 - XunfeiPptApi.CreateResponse response = xunfeiPptApi.create(request); 284 + XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(request);
284 // 打印结果 285 // 打印结果
285 System.out.println("使用完整参数创建 PPT 响应:" + JsonUtils.toJsonString(response)); 286 System.out.println("使用完整参数创建 PPT 响应:" + JsonUtils.toJsonString(response));
286 287
@@ -296,9 +297,9 @@ public class XunfeiPptApiTests { @@ -296,9 +297,9 @@ public class XunfeiPptApiTests {
296 297
297 // 立即查询一次进度 298 // 立即查询一次进度
298 System.out.println("立即查询进度..."); 299 System.out.println("立即查询进度...");
299 - XunfeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid); 300 + XunFeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid);
300 if (progressResponse != null && progressResponse.data() != null) { 301 if (progressResponse != null && progressResponse.data() != null) {
301 - XunfeiPptApi.ProgressResponseData progressData = progressResponse.data(); 302 + XunFeiPptApi.ProgressResponseData progressData = progressResponse.data();
302 System.out.println("PPT 构建状态: " + progressData.pptStatus()); 303 System.out.println("PPT 构建状态: " + progressData.pptStatus());
303 if (progressData.totalPages() != null && progressData.donePages() != null) { 304 if (progressData.totalPages() != null && progressData.donePages() != null) {
304 System.out.println("完成进度: " + progressData.donePages() + "/" + progressData.totalPages() 305 System.out.println("完成进度: " + progressData.donePages() + "/" + progressData.totalPages()