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 8 import org.springframework.http.HttpRequest;
9 9 import org.springframework.http.HttpStatusCode;
10 10 import org.springframework.http.MediaType;
  11 +import org.springframework.util.Assert;
11 12 import org.springframework.util.LinkedMultiValueMap;
12 13 import org.springframework.util.MultiValueMap;
13 14 import org.springframework.web.multipart.MultipartFile;
... ... @@ -24,18 +25,18 @@ import java.util.Objects;
24 25 import java.util.function.Function;
25 26 import java.util.function.Predicate;
26 27  
27   -// TODO @新:要不改成 WenDuoDuoPptApi
  28 +
28 29 /**
29 30 * 文多多 API
30 31 *
31   - * @see <a href="https://docmee.cn/open-platform/api">PPT 生成 API</a>
32   - *
33 32 * @author xiaoxin
  33 + * @see <a href="https://docmee.cn/open-platform/api">PPT 生成 API</a>
34 34 */
35 35 @Slf4j
36   -public class WddPptApi {
  36 +public class WenDuoDuoPptApi {
37 37  
38 38 public static final String BASE_URL = "https://docmee.cn";
  39 + public static final String TOKEN_NAME = "token";
39 40  
40 41 private final WebClient webClient;
41 42  
... ... @@ -44,17 +45,24 @@ public class WddPptApi {
44 45 private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
45 46 reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
46 47 HttpRequest request = response.request();
47   - log.error("[wdd-api] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
  48 + log.error("[WenDuoDuoPptApi] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
48 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 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 66 .build();
59 67 }
60 68  
... ... @@ -82,37 +90,16 @@ public class WddPptApi {
82 90 .block();
83 91 }
84 92  
85   - // TODO @xin:是不是给个 API 连接,这样 type、content、files 都不用写注释太细了
86 93 /**
87 94 * 创建任务
88 95 *
89 96 * @param type 类型
90   - * 1.智能生成(主题、要求)
91   - * 2.上传文件生成
92   - * 3.上传思维导图生成
93   - * 4.通过word精准转ppt
94   - * 5.通过网页链接生成
95   - * 6.粘贴文本内容生成
96   - * 7.Markdown大纲生成
97 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 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 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 103 MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
117 104 formData.add("type", type);
118 105 if (content != null) {
... ... @@ -125,7 +112,6 @@ public class WddPptApi {
125 112 }
126 113 return this.webClient.post()
127 114 .uri("/api/ppt/v2/createTask")
128   - .header("token", token)
129 115 .contentType(MediaType.MULTIPART_FORM_DATA)
130 116 .body(BodyInserters.fromMultipartData(formData))
131 117 .retrieve()
... ... @@ -168,10 +154,9 @@ public class WddPptApi {
168 154 * @param request 请求体
169 155 * @return 模板列表
170 156 */
171   - public PagePptTemplateInfo getTemplatePage(String token, TemplateQueryRequest request) {
  157 + public PagePptTemplateInfo getTemplatePage(TemplateQueryRequest request) {
172 158 return this.webClient.post()
173 159 .uri("/api/ppt/templates")
174   - .header("token", token)
175 160 .bodyValue(request)
176 161 .retrieve()
177 162 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
... ... @@ -185,10 +170,9 @@ public class WddPptApi {
185 170 *
186 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 174 return this.webClient.post()
190 175 .uri("/api/ppt/v2/generateContent")
191   - .header("token", token)
192 176 .body(Mono.just(request), CreateOutlineRequest.class)
193 177 .retrieve()
194 178 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
... ... @@ -202,10 +186,9 @@ public class WddPptApi {
202 186 * @param request 请求体
203 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 190 return this.webClient.post()
207 191 .uri("/api/ppt/v2/updateContent")
208   - .header("token", token)
209 192 .body(Mono.just(request), UpdateOutlineRequest.class)
210 193 .retrieve()
211 194 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
... ... @@ -218,11 +201,10 @@ public class WddPptApi {
218 201 *
219 202 * @return PPT信息
220 203 */
221   - public PptInfo create(String token, CreatePptRequest request) {
  204 + public PptInfo create(PptCreateRequest request) {
222 205 return this.webClient.post()
223 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 208 .retrieve()
227 209 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
228 210 .bodyToMono(ApiResponse.class)
... ... @@ -236,7 +218,9 @@ public class WddPptApi {
236 218 .block();
237 219 }
238 220  
239   -
  221 + /**
  222 + * 创建Token请求参数
  223 + */
240 224 @JsonInclude(value = JsonInclude.Include.NON_NULL)
241 225 public record CreateTokenRequest(
242 226 String apiKey,
... ... @@ -258,7 +242,8 @@ public class WddPptApi {
258 242 Integer code,
259 243 String message,
260 244 Map<String, Object> data
261   - ) { }
  245 + ) {
  246 + }
262 247  
263 248 /**
264 249 * 创建任务
... ... @@ -268,7 +253,8 @@ public class WddPptApi {
268 253 Integer type,
269 254 String content,
270 255 List<MultipartFile> files
271   - ) { }
  256 + ) {
  257 + }
272 258  
273 259 /**
274 260 * 生成大纲内容请求
... ... @@ -281,7 +267,8 @@ public class WddPptApi {
281 267 String audience,
282 268 String lang,
283 269 String prompt
284   - ) { }
  270 + ) {
  271 + }
285 272  
286 273 /**
287 274 * 修改大纲内容请求
... ... @@ -291,20 +278,23 @@ public class WddPptApi {
291 278 String id,
292 279 String markdown,
293 280 String question
294   - ) { }
  281 + ) {
  282 + }
295 283  
296 284 /**
297   - * 生成 PPT 请求
  285 + * 生成 PPT 请求参数
298 286 */
299   - // TODO @新:要不按照 PptCreateRequest 这样的风格
300 287 @JsonInclude(value = JsonInclude.Include.NON_NULL)
301   - public record CreatePptRequest(
  288 + public record PptCreateRequest(
302 289 String id,
303 290 String templateId,
304 291 String markdown
305   - ) { }
  292 + ) {
  293 + }
306 294  
307   - // TODO @新:要不写下类注释
  295 + /**
  296 + * PPT 信息
  297 + */
308 298 @JsonInclude(value = JsonInclude.Include.NON_NULL)
309 299 public record PptInfo(
310 300 String id,
... ... @@ -323,9 +313,12 @@ public class WddPptApi {
323 313 LocalDateTime createTime,
324 314 String createUser,
325 315 String updateUser
326   - ) { }
  316 + ) {
  317 + }
327 318  
328   - // TODO @新:要不写下类注释
  319 + /**
  320 + * 模板查询请求参数
  321 + */
329 322 @JsonInclude(value = JsonInclude.Include.NON_NULL)
330 323 public record TemplateQueryRequest(
331 324 int page,
... ... @@ -333,25 +326,33 @@ public class WddPptApi {
333 326 Filter filters
334 327 ) {
335 328  
  329 + /**
  330 + * 模板查询过滤条件
  331 + */
336 332 @JsonInclude(value = JsonInclude.Include.NON_NULL)
337 333 public record Filter(
338 334 int type,
339 335 String category,
340 336 String style,
341 337 String themeColor
342   - ) { }
  338 + ) {
  339 + }
343 340  
344 341 }
345 342  
346   - // TODO @新:要不写下类注释
  343 + /**
  344 + * PPT模板分页信息
  345 + */
347 346 @JsonInclude(value = JsonInclude.Include.NON_NULL)
348 347 public record PagePptTemplateInfo(
349 348 List<PptTemplateInfo> data,
350 349 String total
351   - ) {}
352   -
  350 + ) {
  351 + }
353 352  
354   - // TODO @新:要不写下类注释
  353 + /**
  354 + * PPT模板信息
  355 + */
355 356 @JsonInclude(value = JsonInclude.Include.NON_NULL)
356 357 public record PptTemplateInfo(
357 358 String id,
... ... @@ -380,6 +381,7 @@ public class WddPptApi {
380 381 LocalDateTime createTime,
381 382 String createUser,
382 383 String updateUser
383   - ) { }
  384 + ) {
  385 + }
384 386  
385 387 }
386 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 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 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 7 import com.fasterxml.jackson.annotation.JsonInclude;
  8 +import lombok.Builder;
5 9 import lombok.extern.slf4j.Slf4j;
6 10 import org.springframework.core.io.ByteArrayResource;
7 11 import org.springframework.http.HttpStatusCode;
8 12 import org.springframework.http.MediaType;
9 13 import org.springframework.util.LinkedMultiValueMap;
10 14 import org.springframework.util.MultiValueMap;
  15 +import org.springframework.util.StringUtils;
11 16 import org.springframework.web.multipart.MultipartFile;
12 17 import org.springframework.web.reactive.function.BodyInserters;
13 18 import org.springframework.web.reactive.function.client.ClientResponse;
14 19 import org.springframework.web.reactive.function.client.WebClient;
15 20 import reactor.core.publisher.Mono;
16 21  
17   -import javax.crypto.Mac;
18   -import javax.crypto.spec.SecretKeySpec;
19 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 23 import java.util.HashMap;
26 24 import java.util.List;
27 25 import java.util.Map;
28 26 import java.util.function.Function;
29 27 import java.util.function.Predicate;
30 28  
31   -// TODO @新:要不改成 XunFeiPptApi
32 29 /**
33 30 * 讯飞智能 PPT 生成 API
34 31 *
35   - * @see <a href="https://www.xfyun.cn/doc/spark/PPTv2.html">智能 PPT 生成 API</a>
36   - *
37 32 * @author xiaoxin
  33 + * @see <a href="https://www.xfyun.cn/doc/spark/PPTv2.html">智能 PPT 生成 API</a>
38 34 */
39 35 @Slf4j
40   -public class XunfeiPptApi {
  36 +public class XunFeiPptApi {
41 37  
42 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 43 private final WebClient webClient;
45 44 private final String appId;
... ... @@ -49,18 +48,21 @@ public class XunfeiPptApi {
49 48  
50 49 private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
51 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 56 this.appId = appId;
63 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 74 long timestamp = System.currentTimeMillis() / 1000;
73 75 String ts = String.valueOf(timestamp);
74 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 86 * @return 签名
85 87 */
86 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 101 SignatureInfo signInfo = getSignature();
135 102 Map<String, Object> requestBody = new HashMap<>();
136 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 105 return this.webClient.post()
140 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 109 .contentType(MediaType.APPLICATION_JSON)
145 110 .bodyValue(requestBody)
146 111 .retrieve()
... ... @@ -161,9 +126,8 @@ public class XunfeiPptApi {
161 126 formData.add("query", query);
162 127 return this.webClient.post()
163 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 131 .contentType(MediaType.MULTIPART_FORM_DATA)
168 132 .body(BodyInserters.fromMultipartData(formData))
169 133 .retrieve()
... ... @@ -207,12 +171,11 @@ public class XunfeiPptApi {
207 171 */
208 172 public CreateResponse create(CreatePptRequest request) {
209 173 SignatureInfo signInfo = getSignature();
210   - MultiValueMap<String, Object> formData = buildCreateFormData(request);
  174 + MultiValueMap<String, Object> formData = buildCreatePptFormData(request);
211 175 return this.webClient.post()
212 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 179 .contentType(MediaType.MULTIPART_FORM_DATA)
217 180 .body(BodyInserters.fromMultipartData(formData))
218 181 .retrieve()
... ... @@ -247,9 +210,8 @@ public class XunfeiPptApi {
247 210 SignatureInfo signInfo = getSignature();
248 211 return this.webClient.post()
249 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 215 .contentType(MediaType.APPLICATION_JSON)
254 216 .bodyValue(request)
255 217 .retrieve()
... ... @@ -271,9 +233,8 @@ public class XunfeiPptApi {
271 233 .path("/progress")
272 234 .queryParam("sid", sid)
273 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 238 .retrieve()
278 239 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(sid))
279 240 .bodyToMono(ProgressResponse.class)
... ... @@ -285,10 +246,10 @@ public class XunfeiPptApi {
285 246 */
286 247 @JsonInclude(value = JsonInclude.Include.NON_NULL)
287 248 private record SignatureInfo(
288   - String appId,
289 249 String timestamp,
290 250 String signature
291   - ) { }
  251 + ) {
  252 + }
292 253  
293 254 /**
294 255 * 模板列表响应
... ... @@ -300,7 +261,8 @@ public class XunfeiPptApi {
300 261 String desc,
301 262 Integer count,
302 263 TemplatePageData data
303   - ) { }
  264 + ) {
  265 + }
304 266  
305 267 /**
306 268 * 模板列表数据
... ... @@ -310,7 +272,8 @@ public class XunfeiPptApi {
310 272 String total,
311 273 List<TemplateInfo> records,
312 274 Integer pageNum
313   - ) { }
  275 + ) {
  276 + }
314 277  
315 278 /**
316 279 * 模板信息
... ... @@ -324,7 +287,8 @@ public class XunfeiPptApi {
324 287 String industry,
325 288 String style,
326 289 String detailImage
327   - ) { }
  290 + ) {
  291 + }
328 292  
329 293 /**
330 294 * 创建响应
... ... @@ -336,7 +300,8 @@ public class XunfeiPptApi {
336 300 String desc,
337 301 Integer count,
338 302 CreateResponseData data
339   - ) { }
  303 + ) {
  304 + }
340 305  
341 306 /**
342 307 * 创建响应数据
... ... @@ -348,7 +313,8 @@ public class XunfeiPptApi {
348 313 String title,
349 314 String subTitle,
350 315 OutlineData outline
351   - ) { }
  316 + ) {
  317 + }
352 318  
353 319 /**
354 320 * 大纲数据结构
... ... @@ -375,7 +341,8 @@ public class XunfeiPptApi {
375 341 @JsonInclude(value = JsonInclude.Include.NON_NULL)
376 342 public record ChapterContent(
377 343 String chapterTitle
378   - ) { }
  344 + ) {
  345 + }
379 346  
380 347 }
381 348  
... ... @@ -397,7 +364,8 @@ public class XunfeiPptApi {
397 364 int code,
398 365 String desc,
399 366 ProgressResponseData data
400   - ) { }
  367 + ) {
  368 + }
401 369  
402 370 /**
403 371 * 进度响应数据
... ... @@ -407,13 +375,12 @@ public class XunfeiPptApi {
407 375 int process,
408 376 String pptId,
409 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 421 * 通过大纲创建 PPT 请求参数
455 422 */
456 423 @JsonInclude(value = JsonInclude.Include.NON_NULL)
  424 + @Builder
457 425 public record CreatePptByOutlineRequest(
458 426 String query, // 用户生成PPT要求(最多8000字)
459 427 String outlineSid, // 已生成大纲后,响应返回的请求大纲唯一id
... ... @@ -469,122 +437,18 @@ public class XunfeiPptApi {
469 437 Boolean isFigure, // 是否自动配图
470 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 444 * 构建创建 PPT 的表单数据
578 445 *
579 446 * @param request 请求参数
580 447 * @return 表单数据
581 448 */
582   - private MultiValueMap<String, Object> buildCreateFormData(CreatePptRequest request) {
  449 + private MultiValueMap<String, Object> buildCreatePptFormData(CreatePptRequest request) {
583 450 MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
584   - // 添加请求参数
585   - if (request.query() != null) {
586   - formData.add("query", request.query());
587   - }
  451 +
588 452 if (request.file() != null) {
589 453 try {
590 454 formData.add("file", new ByteArrayResource(request.file().getBytes()) {
... ... @@ -594,48 +458,52 @@ public class XunfeiPptApi {
594 458 }
595 459 });
596 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 503 * 直接生成PPT请求参数
637 504 */
638 505 @JsonInclude(value = JsonInclude.Include.NON_NULL)
  506 + @Builder
639 507 public record CreatePptRequest(
640 508 String query, // 用户生成PPT要求(最多8000字)
641 509 MultipartFile file, // 上传文件
... ... @@ -651,109 +519,6 @@ public class XunfeiPptApi {
651 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 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 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 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 4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 5 import org.junit.jupiter.api.Disabled;
6 6 import org.junit.jupiter.api.Test;
... ... @@ -10,24 +10,28 @@ import java.util.Map;
10 10 import java.util.Objects;
11 11  
12 12 /**
13   - * {@link WddPptApi} 集成测试
  13 + * {@link WenDuoDuoPptApi} 集成测试
14 14 *
15 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 21 private final String token = ""; // API Token
22 22  
  23 + {
  24 + wenDuoDuoPptApi = new WenDuoDuoPptApi(token);
  25 + }
  26 +
23 27 @Test
24 28 @Disabled
25 29 public void testCreateApiToken() {
26 30 // 准备参数
27 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 36 System.out.println(token);
33 37 }
... ... @@ -38,7 +42,7 @@ public class WddPptApiTests {
38 42 @Test
39 43 @Disabled
40 44 public void testCreateTask() {
41   - WddPptApi.ApiResponse apiResponse = wddPptApi.createTask(token, 1, "dify 介绍", null);
  45 + WenDuoDuoPptApi.ApiResponse apiResponse = wenDuoDuoPptApi.createTask(1, "dify 介绍", null);
42 46 System.out.println(apiResponse);
43 47 }
44 48  
... ... @@ -46,10 +50,10 @@ public class WddPptApiTests {
46 50 @Test // 创建大纲
47 51 @Disabled
48 52 public void testGenerateOutlineRequest() {
49   - WddPptApi.CreateOutlineRequest request = new WddPptApi.CreateOutlineRequest(
  53 + WenDuoDuoPptApi.CreateOutlineRequest request = new WenDuoDuoPptApi.CreateOutlineRequest(
50 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 57 StringBuffer contentBuffer = new StringBuffer();
54 58 flux.doOnNext(chunk -> {
55 59 contentBuffer.append(chunk.get("text"));
... ... @@ -69,10 +73,10 @@ public class WddPptApiTests {
69 73 @Test
70 74 @Disabled
71 75 public void testUpdateOutlineRequest() {
72   - WddPptApi.UpdateOutlineRequest request = new WddPptApi.UpdateOutlineRequest(
  76 + WenDuoDuoPptApi.UpdateOutlineRequest request = new WenDuoDuoPptApi.UpdateOutlineRequest(
73 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 80 StringBuffer contentBuffer = new StringBuffer();
77 81 flux.doOnNext(chunk -> {
78 82 contentBuffer.append(chunk.get("text"));
... ... @@ -94,11 +98,11 @@ public class WddPptApiTests {
94 98 @Disabled
95 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 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 107 System.out.println(pptTemplatePage);
104 108 }
... ... @@ -110,9 +114,9 @@ public class WddPptApiTests {
110 114 @Disabled
111 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 121 System.out.println(pptInfo);
118 122 }
... ... @@ -309,6 +313,7 @@ public class WddPptApiTests {
309 313 #### 7.2.2 合作共赢
310 314 期待与更多的企业和机构合作,共同推动AI技术的应用。
311 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 1 package cn.iocoder.yudao.framework.ai.ppt.xunfei;
2 2  
3 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 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 6 import org.junit.jupiter.api.Disabled;
7 7 import org.junit.jupiter.api.Test;
... ... @@ -11,17 +11,17 @@ import org.springframework.web.multipart.MultipartFile;
11 11 import java.io.File;
12 12  
13 13 /**
14   - * {@link XunfeiPptApi} 集成测试
  14 + * {@link XunFeiPptApi} 集成测试
15 15 *
16 16 * @author xiaoxin
17 17 */
18   -public class XunfeiPptApiTests {
  18 +public class XunFeiPptApiTests {
19 19  
20 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 27 * 获取 PPT 模板列表
... ... @@ -30,7 +30,7 @@ public class XunfeiPptApiTests {
30 30 @Disabled
31 31 public void testGetTemplatePage() {
32 32 // 调用方法
33   - XunfeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage("商务", 10);
  33 + XunFeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage("商务", 10);
34 34 // 打印结果
35 35 System.out.println("模板列表响应:" + JsonUtils.toJsonString(response));
36 36  
... ... @@ -41,7 +41,7 @@ public class XunfeiPptApiTests {
41 41  
42 42 // 打印第一个模板的信息(如果存在)
43 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 45 System.out.println("模板ID:" + firstTemplate.templateIndexId());
46 46 System.out.println("模板风格:" + firstTemplate.style());
47 47 System.out.println("模板颜色:" + firstTemplate.color());
... ... @@ -56,7 +56,7 @@ public class XunfeiPptApiTests {
56 56 @Test
57 57 @Disabled
58 58 public void testCreateOutline() {
59   - XunfeiPptApi.CreateResponse response = getCreateResponse();
  59 + XunFeiPptApi.CreateResponse response = getCreateResponse();
60 60 // 打印结果
61 61 System.out.println("创建大纲响应:" + JsonUtils.toJsonString(response));
62 62  
... ... @@ -75,9 +75,10 @@ public class XunfeiPptApiTests {
75 75  
76 76 /**
77 77 * 创建大纲(通过文本)
  78 + *
78 79 * @return 创建大纲响应
79 80 */
80   - private XunfeiPptApi.CreateResponse getCreateResponse() {
  81 + private XunFeiPptApi.CreateResponse getCreateResponse() {
81 82 String param = "智能体平台 Dify 介绍";
82 83 return xunfeiPptApi.createOutline(param);
83 84 }
... ... @@ -89,9 +90,9 @@ public class XunfeiPptApiTests {
89 90 @Disabled
90 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 97 System.out.println("通过大纲创建 PPT 响应:" + JsonUtils.toJsonString(response));
97 98  
... ... @@ -114,13 +115,13 @@ public class XunfeiPptApiTests {
114 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 120 System.out.println("检查进度响应:" + JsonUtils.toJsonString(response));
120 121  
121 122 // 安全地访问响应数据
122 123 if (response != null && response.data() != null) {
123   - XunfeiPptApi.ProgressResponseData data = response.data();
  124 + XunFeiPptApi.ProgressResponseData data = response.data();
124 125  
125 126 // 打印PPT生成状态
126 127 System.out.println("PPT 构建状态: " + data.pptStatus());
... ... @@ -160,7 +161,7 @@ public class XunfeiPptApiTests {
160 161 @Disabled
161 162 public void testPollCheckProgress() throws InterruptedException {
162 163 // 准备参数 - 使用之前创建 PP T时返回的 sid
163   - String sid = "fa36e926f2ed434987fcb4c1f0776ffb"; // 替换为实际的sid
  164 + String sid = "1690ef6ee0344e72b5c5434f403b8eaa"; // 替换为实际的sid
164 165  
165 166 // 最大轮询次数
166 167 int maxPolls = 20;
... ... @@ -171,11 +172,11 @@ public class XunfeiPptApiTests {
171 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 178 if (response != null && response.data() != null) {
178   - XunfeiPptApi.ProgressResponseData data = response.data();
  179 + XunFeiPptApi.ProgressResponseData data = response.data();
179 180  
180 181 // 打印进度信息
181 182 System.out.println("PPT 构建状态: " + data.pptStatus());
... ... @@ -218,7 +219,7 @@ public class XunfeiPptApiTests {
218 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 224 System.out.println("直接创建 PPT 响应:" + JsonUtils.toJsonString(response));
224 225  
... ... @@ -244,7 +245,7 @@ public class XunfeiPptApiTests {
244 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 250 System.out.println("通过文件创建PPT响应:" + JsonUtils.toJsonString(response));
250 251  
... ... @@ -269,7 +270,7 @@ public class XunfeiPptApiTests {
269 270 String query = "合肥天气趋势分析,包括近 5 年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响";
270 271  
271 272 // 创建请求对象
272   - XunfeiPptApi.CreatePptRequest request = XunfeiPptApi.CreatePptRequest.builder()
  273 + XunFeiPptApi.CreatePptRequest request = XunFeiPptApi.CreatePptRequest.builder()
273 274 .query(query)
274 275 .language("cn")
275 276 .isCardNote(true)
... ... @@ -280,7 +281,7 @@ public class XunfeiPptApiTests {
280 281 .build();
281 282  
282 283 // 调用方法
283   - XunfeiPptApi.CreateResponse response = xunfeiPptApi.create(request);
  284 + XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(request);
284 285 // 打印结果
285 286 System.out.println("使用完整参数创建 PPT 响应:" + JsonUtils.toJsonString(response));
286 287  
... ... @@ -296,9 +297,9 @@ public class XunfeiPptApiTests {
296 297  
297 298 // 立即查询一次进度
298 299 System.out.println("立即查询进度...");
299   - XunfeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid);
  300 + XunFeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid);
300 301 if (progressResponse != null && progressResponse.data() != null) {
301   - XunfeiPptApi.ProgressResponseData progressData = progressResponse.data();
  302 + XunFeiPptApi.ProgressResponseData progressData = progressResponse.data();
302 303 System.out.println("PPT 构建状态: " + progressData.pptStatus());
303 304 if (progressData.totalPages() != null && progressData.donePages() != null) {
304 305 System.out.println("完成进度: " + progressData.donePages() + "/" + progressData.totalPages()
... ...