diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/wenduoduo/api/WddApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/wenduoduo/api/WddApi.java new file mode 100644 index 0000000000..b5467002f4 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/wenduoduo/api/WddApi.java @@ -0,0 +1,449 @@ +package cn.iocoder.yudao.framework.ai.core.model.wenduoduo.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpRequest; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * 文多多 API + *

+ *

+ * * 对接文多多:PPT 生成 API + * + * @author xiaoxin + */ +@Slf4j +public class WddApi { + + public static final String BASE_URL = "https://docmee.cn"; + + private final WebClient webClient; + + private final Predicate STATUS_PREDICATE = status -> !status.is2xxSuccessful(); + + private final Function>> EXCEPTION_FUNCTION = + reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> { + HttpRequest request = response.request(); + log.error("[wdd-api] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]", + request.getMethod(), request.getURI(), reqParam, responseBody); + sink.error(new IllegalStateException("[wdd-api] 调用失败!")); + }); + + public WddApi(String baseUrl) { + this.webClient = WebClient.builder() + .baseUrl(baseUrl) + .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON)) + .build(); + } + + /** + * 创建API令牌 + * + * @param apiKey API密钥 + * @param uid 用户ID + * @param limit 限制 + * @return API令牌 + */ + public String createApiToken(String apiKey, String uid, Integer limit) { + CreateApiTokenRequest request = new CreateApiTokenRequest(uid, limit); + return this.webClient.post() + .uri("/api/user/createApiToken") + .header("Api-Key", apiKey) + .body(Mono.just(request), CreateApiTokenRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(ApiResponse.class) + .handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("创建apiToken异常," + response.message)); + return; + } + sink.next(response.data.get("token").toString()); + }) + .block(); + } + + /** + * 解析文件数据 + * + * @param apiToken API令牌 + * @param content 内容 + * @param fileUrl 文件URL + * @return 数据URL + */ + public String parseFileData(String apiToken, String content, String fileUrl) { + ParseFileDataRequest request = new ParseFileDataRequest(content, fileUrl); + return this.webClient.post() + .uri("/api/ppt/parseFileData") + .header("token", apiToken) + .body(Mono.just(request), ParseFileDataRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(ApiResponse.class) + .handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("解析文件或内容异常," + response.message)); + return; + } + sink.next(response.data.get("dataUrl").toString()); + }) + .block(); + } + + /** + * 生成大纲 + * + * @param apiToken API令牌 + * @param subject 主题 + * @param dataUrl 数据URL + * @param prompt 提示词 + * @return 大纲内容 + */ + public String generateOutline(String apiToken, String subject, String dataUrl, String prompt) { + GenerateOutlineRequest request = new GenerateOutlineRequest(subject, dataUrl, prompt); + return this.webClient.post() + .uri("/api/ppt/generateOutline") + .header("token", apiToken) + .body(Mono.just(request), GenerateOutlineRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(String.class) + .block(); + } + + /** + * 生成大纲内容 + * + * @param apiToken API令牌 + * @param outlineMarkdown 大纲Markdown + * @param dataUrl 数据URL + * @param prompt 提示词 + * @return 大纲内容 + */ + public String generateContent(String apiToken, String outlineMarkdown, String dataUrl, String prompt) { + GenerateContentRequest request = new GenerateContentRequest(outlineMarkdown, dataUrl, prompt); + return this.webClient.post() + .uri("/api/ppt/generateContent") + .header("token", apiToken) + .body(Mono.just(request), GenerateContentRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(String.class) + .block(); + } + + /** + * 异步生成大纲内容 + * + * @param apiToken API令牌 + * @param outlineMarkdown 大纲Markdown + * @param dataUrl 数据URL + * @param templateId 模板ID + * @param prompt 提示词 + * @return 大纲内容和PPT ID + */ + public Map asyncGenerateContent(String apiToken, String outlineMarkdown, String dataUrl, String templateId, String prompt) { + AsyncGenerateContentRequest request = new AsyncGenerateContentRequest(outlineMarkdown, dataUrl, templateId, prompt); + return this.webClient.post() + .uri("/api/ppt/generateContent") + .header("token", apiToken) + .body(Mono.just(request), AsyncGenerateContentRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(new ParameterizedTypeReference>() { + }) + .block(); + } + + /** + * 随机获取一个模板ID + * + * @param apiToken API令牌 + * @return 模板ID + */ + public String randomOneTemplateId(String apiToken) { + RandomTemplateRequest request = new RandomTemplateRequest(1, new TemplateFilter(1)); + return this.webClient.post() + .uri("/api/ppt/randomTemplates") + .header("token", apiToken) + .body(Mono.just(request), RandomTemplateRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(ApiResponse.class) + .handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("获取模板异常," + response.message)); + return; + } + sink.next(((Map) ((Object[]) response.data.get("data"))[0]).get("id").toString()); + }) + .block(); + } + + /** + * 生成PPT + * + * @param apiToken API令牌 + * @param templateId 模板ID + * @param markdown Markdown内容 + * @param pptxProperty PPT属性 + * @return PPT信息 + */ + public Map generatePptx(String apiToken, String templateId, String markdown, boolean pptxProperty) { + GeneratePptxRequest request = new GeneratePptxRequest(templateId, markdown, pptxProperty); + return this.webClient.post() + .uri("/api/ppt/generatePptx") + .header("token", apiToken) + .body(Mono.just(request), GeneratePptxRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(ApiResponse.class) + .>handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("生成PPT异常," + response.message)); + return; + } + sink.next((Map) response.data.get("pptInfo")); + }) + .block(); + } + + /** + * 下载PPT + * + * @param apiToken API令牌 + * @param id PPT ID + * @return 下载信息 + */ + public Map downloadPptx(String apiToken, String id) { + DownloadPptxRequest request = new DownloadPptxRequest(id); + return this.webClient.post() + .uri("/api/ppt/downloadPptx") + .header("token", apiToken) + .body(Mono.just(request), DownloadPptxRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(ApiResponse.class) + .>handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("下载PPT异常," + response.message)); + return; + } + sink.next(response.data); + }) + .block(); + } + + /** + * 直接生成PPT + * + * @param apiToken API令牌 + * @param templateId 模板ID + * @param subject 主题 + * @param dataUrl 数据URL + * @param prompt 提示词 + * @param pptxProperty PPT属性 + * @return PPT信息 + */ + public Map directGeneratePptx(String apiToken, String templateId, String subject, String dataUrl, String prompt, boolean pptxProperty) { + DirectGeneratePptxRequest request = new DirectGeneratePptxRequest(false, templateId, subject, dataUrl, prompt, pptxProperty); + return this.webClient.post() + .uri("/api/ppt/directGeneratePptx") + .header("token", apiToken) + .body(Mono.just(request), DirectGeneratePptxRequest.class) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request)) + .bodyToMono(ApiResponse.class) + .>handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("生成PPT异常," + response.message)); + return; + } + sink.next((Map) response.data.get("pptInfo")); + }) + .block(); + } + + /** + * 查询所有PPT列表 + * + * @param apiToken API令牌 + * @param body 请求体 + * @return PPT列表 + */ + public Map listAllPptx(String apiToken, String body) { + return this.webClient.post() + .uri("/api/ppt/listAllPptx") + .header("token", apiToken) + .bodyValue(body) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body)) + .bodyToMono(ApiResponse.class) + .>handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("查询所有PPT列表异常," + response.message)); + return; + } + sink.next(response.data); + }) + .block(); + } + + /** + * 分页查询PPT模板 + * + * @param apiToken API令牌 + * @param body 请求体 + * @return 模板列表 + */ + public Map getPptTemplates(String apiToken, String body) { + return this.webClient.post() + .uri("/api/ppt/templates") + .header("token", apiToken) + .bodyValue(body) + .retrieve() + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body)) + .bodyToMono(ApiResponse.class) + .>handle((response, sink) -> { + if (response.code != 0) { + sink.error(new IllegalStateException("分页查询PPT模板异常," + response.message)); + return; + } + sink.next(response.data); + }) + .block(); + } + + /** + * API响应 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record ApiResponse( + Integer code, + String message, + Map data + ) { + } + + /** + * 创建API令牌请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record CreateApiTokenRequest( + String uid, + Integer limit + ) { + } + + /** + * 解析文件数据请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record ParseFileDataRequest( + String content, + String fileUrl + ) { + } + + /** + * 生成大纲请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record GenerateOutlineRequest( + String subject, + String dataUrl, + String prompt + ) { + } + + /** + * 生成大纲内容请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record GenerateContentRequest( + String outlineMarkdown, + String dataUrl, + String prompt + ) { + } + + /** + * 异步生成大纲内容请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record AsyncGenerateContentRequest( + String outlineMarkdown, + String dataUrl, + String templateId, + String prompt, + @JsonProperty("asyncGenPptx") boolean asyncGenPptx + ) { + public AsyncGenerateContentRequest(String outlineMarkdown, String dataUrl, String templateId, String prompt) { + this(outlineMarkdown, dataUrl, templateId, prompt, true); + } + } + + /** + * 随机模板请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record RandomTemplateRequest( + Integer size, + TemplateFilter filters + ) { + } + + /** + * 模板过滤器 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record TemplateFilter( + Integer type + ) { + } + + /** + * 生成PPT请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record GeneratePptxRequest( + String templateId, + @JsonProperty("outlineContentMarkdown") String outlineContentMarkdown, + boolean pptxProperty + ) { + } + + /** + * 下载PPT请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record DownloadPptxRequest( + String id + ) { + } + + /** + * 直接生成PPT请求 + */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + public record DirectGeneratePptxRequest( + boolean stream, + String templateId, + String subject, + String dataUrl, + String prompt, + boolean pptxProperty + ) { + } +} \ No newline at end of file