Commit ea1d9f0075f9dc5f00d7e9b26180140856f4ce09

Authored by xiaoxin
1 parent 32e1ef4d

【功能新增】AI:文多多对接

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/wenduoduo/api/WddApi.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.core.model.wenduoduo.api;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonInclude;
  4 +import com.fasterxml.jackson.annotation.JsonProperty;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.springframework.core.ParameterizedTypeReference;
  7 +import org.springframework.http.HttpRequest;
  8 +import org.springframework.http.HttpStatusCode;
  9 +import org.springframework.http.MediaType;
  10 +import org.springframework.web.reactive.function.client.ClientResponse;
  11 +import org.springframework.web.reactive.function.client.WebClient;
  12 +import reactor.core.publisher.Mono;
  13 +
  14 +import java.util.Map;
  15 +import java.util.function.Function;
  16 +import java.util.function.Predicate;
  17 +
  18 +/**
  19 + * 文多多 API
  20 + * <p>
  21 + * <p>
  22 + * * 对接文多多:<a href="https://docmee.cn/open-platform/api">PPT 生成 API</a>
  23 + *
  24 + * @author xiaoxin
  25 + */
  26 +@Slf4j
  27 +public class WddApi {
  28 +
  29 + public static final String BASE_URL = "https://docmee.cn";
  30 +
  31 + private final WebClient webClient;
  32 +
  33 + private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
  34 +
  35 + private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
  36 + reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
  37 + HttpRequest request = response.request();
  38 + log.error("[wdd-api] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
  39 + request.getMethod(), request.getURI(), reqParam, responseBody);
  40 + sink.error(new IllegalStateException("[wdd-api] 调用失败!"));
  41 + });
  42 +
  43 + public WddApi(String baseUrl) {
  44 + this.webClient = WebClient.builder()
  45 + .baseUrl(baseUrl)
  46 + .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
  47 + .build();
  48 + }
  49 +
  50 + /**
  51 + * 创建API令牌
  52 + *
  53 + * @param apiKey API密钥
  54 + * @param uid 用户ID
  55 + * @param limit 限制
  56 + * @return API令牌
  57 + */
  58 + public String createApiToken(String apiKey, String uid, Integer limit) {
  59 + CreateApiTokenRequest request = new CreateApiTokenRequest(uid, limit);
  60 + return this.webClient.post()
  61 + .uri("/api/user/createApiToken")
  62 + .header("Api-Key", apiKey)
  63 + .body(Mono.just(request), CreateApiTokenRequest.class)
  64 + .retrieve()
  65 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  66 + .bodyToMono(ApiResponse.class)
  67 + .<String>handle((response, sink) -> {
  68 + if (response.code != 0) {
  69 + sink.error(new IllegalStateException("创建apiToken异常," + response.message));
  70 + return;
  71 + }
  72 + sink.next(response.data.get("token").toString());
  73 + })
  74 + .block();
  75 + }
  76 +
  77 + /**
  78 + * 解析文件数据
  79 + *
  80 + * @param apiToken API令牌
  81 + * @param content 内容
  82 + * @param fileUrl 文件URL
  83 + * @return 数据URL
  84 + */
  85 + public String parseFileData(String apiToken, String content, String fileUrl) {
  86 + ParseFileDataRequest request = new ParseFileDataRequest(content, fileUrl);
  87 + return this.webClient.post()
  88 + .uri("/api/ppt/parseFileData")
  89 + .header("token", apiToken)
  90 + .body(Mono.just(request), ParseFileDataRequest.class)
  91 + .retrieve()
  92 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  93 + .bodyToMono(ApiResponse.class)
  94 + .<String>handle((response, sink) -> {
  95 + if (response.code != 0) {
  96 + sink.error(new IllegalStateException("解析文件或内容异常," + response.message));
  97 + return;
  98 + }
  99 + sink.next(response.data.get("dataUrl").toString());
  100 + })
  101 + .block();
  102 + }
  103 +
  104 + /**
  105 + * 生成大纲
  106 + *
  107 + * @param apiToken API令牌
  108 + * @param subject 主题
  109 + * @param dataUrl 数据URL
  110 + * @param prompt 提示词
  111 + * @return 大纲内容
  112 + */
  113 + public String generateOutline(String apiToken, String subject, String dataUrl, String prompt) {
  114 + GenerateOutlineRequest request = new GenerateOutlineRequest(subject, dataUrl, prompt);
  115 + return this.webClient.post()
  116 + .uri("/api/ppt/generateOutline")
  117 + .header("token", apiToken)
  118 + .body(Mono.just(request), GenerateOutlineRequest.class)
  119 + .retrieve()
  120 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  121 + .bodyToMono(String.class)
  122 + .block();
  123 + }
  124 +
  125 + /**
  126 + * 生成大纲内容
  127 + *
  128 + * @param apiToken API令牌
  129 + * @param outlineMarkdown 大纲Markdown
  130 + * @param dataUrl 数据URL
  131 + * @param prompt 提示词
  132 + * @return 大纲内容
  133 + */
  134 + public String generateContent(String apiToken, String outlineMarkdown, String dataUrl, String prompt) {
  135 + GenerateContentRequest request = new GenerateContentRequest(outlineMarkdown, dataUrl, prompt);
  136 + return this.webClient.post()
  137 + .uri("/api/ppt/generateContent")
  138 + .header("token", apiToken)
  139 + .body(Mono.just(request), GenerateContentRequest.class)
  140 + .retrieve()
  141 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  142 + .bodyToMono(String.class)
  143 + .block();
  144 + }
  145 +
  146 + /**
  147 + * 异步生成大纲内容
  148 + *
  149 + * @param apiToken API令牌
  150 + * @param outlineMarkdown 大纲Markdown
  151 + * @param dataUrl 数据URL
  152 + * @param templateId 模板ID
  153 + * @param prompt 提示词
  154 + * @return 大纲内容和PPT ID
  155 + */
  156 + public Map<String, Object> asyncGenerateContent(String apiToken, String outlineMarkdown, String dataUrl, String templateId, String prompt) {
  157 + AsyncGenerateContentRequest request = new AsyncGenerateContentRequest(outlineMarkdown, dataUrl, templateId, prompt);
  158 + return this.webClient.post()
  159 + .uri("/api/ppt/generateContent")
  160 + .header("token", apiToken)
  161 + .body(Mono.just(request), AsyncGenerateContentRequest.class)
  162 + .retrieve()
  163 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  164 + .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
  165 + })
  166 + .block();
  167 + }
  168 +
  169 + /**
  170 + * 随机获取一个模板ID
  171 + *
  172 + * @param apiToken API令牌
  173 + * @return 模板ID
  174 + */
  175 + public String randomOneTemplateId(String apiToken) {
  176 + RandomTemplateRequest request = new RandomTemplateRequest(1, new TemplateFilter(1));
  177 + return this.webClient.post()
  178 + .uri("/api/ppt/randomTemplates")
  179 + .header("token", apiToken)
  180 + .body(Mono.just(request), RandomTemplateRequest.class)
  181 + .retrieve()
  182 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  183 + .bodyToMono(ApiResponse.class)
  184 + .<String>handle((response, sink) -> {
  185 + if (response.code != 0) {
  186 + sink.error(new IllegalStateException("获取模板异常," + response.message));
  187 + return;
  188 + }
  189 + sink.next(((Map<String, Object>) ((Object[]) response.data.get("data"))[0]).get("id").toString());
  190 + })
  191 + .block();
  192 + }
  193 +
  194 + /**
  195 + * 生成PPT
  196 + *
  197 + * @param apiToken API令牌
  198 + * @param templateId 模板ID
  199 + * @param markdown Markdown内容
  200 + * @param pptxProperty PPT属性
  201 + * @return PPT信息
  202 + */
  203 + public Map<String, Object> generatePptx(String apiToken, String templateId, String markdown, boolean pptxProperty) {
  204 + GeneratePptxRequest request = new GeneratePptxRequest(templateId, markdown, pptxProperty);
  205 + return this.webClient.post()
  206 + .uri("/api/ppt/generatePptx")
  207 + .header("token", apiToken)
  208 + .body(Mono.just(request), GeneratePptxRequest.class)
  209 + .retrieve()
  210 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  211 + .bodyToMono(ApiResponse.class)
  212 + .<Map<String, Object>>handle((response, sink) -> {
  213 + if (response.code != 0) {
  214 + sink.error(new IllegalStateException("生成PPT异常," + response.message));
  215 + return;
  216 + }
  217 + sink.next((Map<String, Object>) response.data.get("pptInfo"));
  218 + })
  219 + .block();
  220 + }
  221 +
  222 + /**
  223 + * 下载PPT
  224 + *
  225 + * @param apiToken API令牌
  226 + * @param id PPT ID
  227 + * @return 下载信息
  228 + */
  229 + public Map<String, Object> downloadPptx(String apiToken, String id) {
  230 + DownloadPptxRequest request = new DownloadPptxRequest(id);
  231 + return this.webClient.post()
  232 + .uri("/api/ppt/downloadPptx")
  233 + .header("token", apiToken)
  234 + .body(Mono.just(request), DownloadPptxRequest.class)
  235 + .retrieve()
  236 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  237 + .bodyToMono(ApiResponse.class)
  238 + .<Map<String, Object>>handle((response, sink) -> {
  239 + if (response.code != 0) {
  240 + sink.error(new IllegalStateException("下载PPT异常," + response.message));
  241 + return;
  242 + }
  243 + sink.next(response.data);
  244 + })
  245 + .block();
  246 + }
  247 +
  248 + /**
  249 + * 直接生成PPT
  250 + *
  251 + * @param apiToken API令牌
  252 + * @param templateId 模板ID
  253 + * @param subject 主题
  254 + * @param dataUrl 数据URL
  255 + * @param prompt 提示词
  256 + * @param pptxProperty PPT属性
  257 + * @return PPT信息
  258 + */
  259 + public Map<String, Object> directGeneratePptx(String apiToken, String templateId, String subject, String dataUrl, String prompt, boolean pptxProperty) {
  260 + DirectGeneratePptxRequest request = new DirectGeneratePptxRequest(false, templateId, subject, dataUrl, prompt, pptxProperty);
  261 + return this.webClient.post()
  262 + .uri("/api/ppt/directGeneratePptx")
  263 + .header("token", apiToken)
  264 + .body(Mono.just(request), DirectGeneratePptxRequest.class)
  265 + .retrieve()
  266 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
  267 + .bodyToMono(ApiResponse.class)
  268 + .<Map<String, Object>>handle((response, sink) -> {
  269 + if (response.code != 0) {
  270 + sink.error(new IllegalStateException("生成PPT异常," + response.message));
  271 + return;
  272 + }
  273 + sink.next((Map<String, Object>) response.data.get("pptInfo"));
  274 + })
  275 + .block();
  276 + }
  277 +
  278 + /**
  279 + * 查询所有PPT列表
  280 + *
  281 + * @param apiToken API令牌
  282 + * @param body 请求体
  283 + * @return PPT列表
  284 + */
  285 + public Map<String, Object> listAllPptx(String apiToken, String body) {
  286 + return this.webClient.post()
  287 + .uri("/api/ppt/listAllPptx")
  288 + .header("token", apiToken)
  289 + .bodyValue(body)
  290 + .retrieve()
  291 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body))
  292 + .bodyToMono(ApiResponse.class)
  293 + .<Map<String, Object>>handle((response, sink) -> {
  294 + if (response.code != 0) {
  295 + sink.error(new IllegalStateException("查询所有PPT列表异常," + response.message));
  296 + return;
  297 + }
  298 + sink.next(response.data);
  299 + })
  300 + .block();
  301 + }
  302 +
  303 + /**
  304 + * 分页查询PPT模板
  305 + *
  306 + * @param apiToken API令牌
  307 + * @param body 请求体
  308 + * @return 模板列表
  309 + */
  310 + public Map<String, Object> getPptTemplates(String apiToken, String body) {
  311 + return this.webClient.post()
  312 + .uri("/api/ppt/templates")
  313 + .header("token", apiToken)
  314 + .bodyValue(body)
  315 + .retrieve()
  316 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body))
  317 + .bodyToMono(ApiResponse.class)
  318 + .<Map<String, Object>>handle((response, sink) -> {
  319 + if (response.code != 0) {
  320 + sink.error(new IllegalStateException("分页查询PPT模板异常," + response.message));
  321 + return;
  322 + }
  323 + sink.next(response.data);
  324 + })
  325 + .block();
  326 + }
  327 +
  328 + /**
  329 + * API响应
  330 + */
  331 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  332 + public record ApiResponse(
  333 + Integer code,
  334 + String message,
  335 + Map<String, Object> data
  336 + ) {
  337 + }
  338 +
  339 + /**
  340 + * 创建API令牌请求
  341 + */
  342 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  343 + public record CreateApiTokenRequest(
  344 + String uid,
  345 + Integer limit
  346 + ) {
  347 + }
  348 +
  349 + /**
  350 + * 解析文件数据请求
  351 + */
  352 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  353 + public record ParseFileDataRequest(
  354 + String content,
  355 + String fileUrl
  356 + ) {
  357 + }
  358 +
  359 + /**
  360 + * 生成大纲请求
  361 + */
  362 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  363 + public record GenerateOutlineRequest(
  364 + String subject,
  365 + String dataUrl,
  366 + String prompt
  367 + ) {
  368 + }
  369 +
  370 + /**
  371 + * 生成大纲内容请求
  372 + */
  373 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  374 + public record GenerateContentRequest(
  375 + String outlineMarkdown,
  376 + String dataUrl,
  377 + String prompt
  378 + ) {
  379 + }
  380 +
  381 + /**
  382 + * 异步生成大纲内容请求
  383 + */
  384 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  385 + public record AsyncGenerateContentRequest(
  386 + String outlineMarkdown,
  387 + String dataUrl,
  388 + String templateId,
  389 + String prompt,
  390 + @JsonProperty("asyncGenPptx") boolean asyncGenPptx
  391 + ) {
  392 + public AsyncGenerateContentRequest(String outlineMarkdown, String dataUrl, String templateId, String prompt) {
  393 + this(outlineMarkdown, dataUrl, templateId, prompt, true);
  394 + }
  395 + }
  396 +
  397 + /**
  398 + * 随机模板请求
  399 + */
  400 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  401 + public record RandomTemplateRequest(
  402 + Integer size,
  403 + TemplateFilter filters
  404 + ) {
  405 + }
  406 +
  407 + /**
  408 + * 模板过滤器
  409 + */
  410 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  411 + public record TemplateFilter(
  412 + Integer type
  413 + ) {
  414 + }
  415 +
  416 + /**
  417 + * 生成PPT请求
  418 + */
  419 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  420 + public record GeneratePptxRequest(
  421 + String templateId,
  422 + @JsonProperty("outlineContentMarkdown") String outlineContentMarkdown,
  423 + boolean pptxProperty
  424 + ) {
  425 + }
  426 +
  427 + /**
  428 + * 下载PPT请求
  429 + */
  430 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  431 + public record DownloadPptxRequest(
  432 + String id
  433 + ) {
  434 + }
  435 +
  436 + /**
  437 + * 直接生成PPT请求
  438 + */
  439 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  440 + public record DirectGeneratePptxRequest(
  441 + boolean stream,
  442 + String templateId,
  443 + String subject,
  444 + String dataUrl,
  445 + String prompt,
  446 + boolean pptxProperty
  447 + ) {
  448 + }
  449 +}
0 450 \ No newline at end of file
... ...