diff --git a/src/main/java/com/rj/common/PasswordUtil.java b/src/main/java/com/rj/common/PasswordUtil.java index aae39b4..2eab882 100644 --- a/src/main/java/com/rj/common/PasswordUtil.java +++ b/src/main/java/com/rj/common/PasswordUtil.java @@ -172,6 +172,11 @@ public class PasswordUtil { + + + + + diff --git a/src/main/java/com/rj/common/ServiceManager.java b/src/main/java/com/rj/common/ServiceManager.java index e88cb5e..89ddbbd 100644 --- a/src/main/java/com/rj/common/ServiceManager.java +++ b/src/main/java/com/rj/common/ServiceManager.java @@ -156,6 +156,11 @@ public class ServiceManager { + + + + + diff --git a/src/main/java/com/rj/config/AliyunConfig.java b/src/main/java/com/rj/config/AliyunConfig.java index 0a96bbe..3919531 100644 --- a/src/main/java/com/rj/config/AliyunConfig.java +++ b/src/main/java/com/rj/config/AliyunConfig.java @@ -133,6 +133,11 @@ public class AliyunConfig { + + + + + diff --git a/src/main/java/com/rj/controller/ImageModelController.java b/src/main/java/com/rj/controller/ImageModelController.java index 8a22649..f440624 100644 --- a/src/main/java/com/rj/controller/ImageModelController.java +++ b/src/main/java/com/rj/controller/ImageModelController.java @@ -32,17 +32,11 @@ import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import com.rj.service.MinIOService; -import java.io.*; -import java.net.URL; /** - * 图像模型控制器 - * - * 提供图像模型相关的RESTful API接口,包括: - * - 图像模型记录的创建、查询、更新、删除操作 - * - 分页查询和条件查询功能 - * - 根据各种条件查询图像模型记录 - * - 统一的响应格式和异常处理 + * 图像模型控制 + * + * https://help.aliyun.com/zh/model-studio/text-to-image#20e9dbb913b9s * * 使用Swagger注解进行API文档生成,支持在线API测试。 * 所有接口都包含完整的参数验证、异常处理和日志记录。 @@ -678,9 +672,9 @@ public class ImageModelController { } } - @PostMapping("/image-gen-byText") + @PostMapping("/text-to-image") @Operation(summary = "文本生成图像", description = "基于文本提示词生成图像") - public ResponseEntity> imageGen( + public ResponseEntity> textToImage( @Parameter(description = "文本生成图像") @Valid @RequestBody TextModelController.ImageProcessingRequest request) { log.info("开始文本生成图像,request: {}", request); @@ -906,36 +900,7 @@ public class ImageModelController { } } - /** - * 从URL下载图像 - * - * @param imageUrl 图像URL - * @return 图像字节数组 - * @throws IOException 下载失败时抛出异常 - */ - private byte[] downloadImageFromUrl11(String imageUrl) throws IOException { - try { - log.info("开始下载图像: {}", imageUrl); - - URL url = new URL(imageUrl); - try (InputStream inputStream = url.openStream(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - - byte[] buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - } - - byte[] imageBytes = outputStream.toByteArray(); - log.info("图像下载完成,大小: {} bytes", imageBytes.length); - return imageBytes; - } - } catch (Exception e) { - log.error("下载图像失败: {}", imageUrl, e); - throw new IOException("下载图像失败: " + e.getMessage(), e); - } - } + /** @@ -1281,5 +1246,54 @@ public class ImageModelController { private String generateRequestId() { return "REQ_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000); } + + /** + * 调用阿里云 根据图片和指令,生成新图 + * + * @return ResponseEntity 包含生成结果的响应实体 + */ + @PostMapping("/image-to-image") + @Operation(summary = "图生图", description = "根据图片和指令,生成新图片") + public ResponseEntity> generateimageToImage( + @Parameter(description = "文件", required = true) + @RequestParam("multipartFile") MultipartFile multipartFile, + @Parameter(description = "参考提示词", required = true) + @RequestParam("refPrompt") String refPrompt, + @Parameter(description = "提示词", required = true) + @RequestParam("prompt") String prompt, + @Parameter(description = "所属人姓名", required = true) + @RequestParam("ownerName") String ownerName, + @Parameter(description = "所属人电话", required = true) + @RequestParam("ownerPhone") String ownerPhone, + @Parameter(description = "图片名称", required = true) + @RequestParam("imageName") String imageName, + @Parameter(description = "模型名称", required = false) + @RequestParam(value = "model") String model, + @Parameter(description = "参数", required = false) + @RequestParam(value = "parameters", required = false) String parameters) { + + try { + log.info("开始图生图处理,ownerName: {}, imageName: {}, prompt: {}", + ownerName, imageName, prompt); + + // 调用业务层方法处理图生图逻辑 + Map result = imageModelService.generateImageToImage( + multipartFile, prompt, ownerName, ownerPhone, imageName, model, parameters); + + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("图生图API失败: {}", e.getMessage(), e); + + Map errorResult = new HashMap<>(); + errorResult.put("success", false); + errorResult.put("message", "图生图生成失败: " + e.getMessage()); + errorResult.put("error", e.getClass().getSimpleName()); + errorResult.put("timestamp", LocalDateTime.now()); + errorResult.put("requestId", generateRequestId()); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult); + } + } } diff --git a/src/main/java/com/rj/controller/TextModelController.java b/src/main/java/com/rj/controller/TextModelController.java index d60257e..72e3f65 100644 --- a/src/main/java/com/rj/controller/TextModelController.java +++ b/src/main/java/com/rj/controller/TextModelController.java @@ -27,6 +27,7 @@ import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.aigc.generation.TranslationOptions; import com.alibaba.dashscope.common.Message; import com.alibaba.dashscope.common.Role; +import org.springframework.web.multipart.MultipartFile; /** @@ -124,7 +125,11 @@ public class TextModelController { @NotBlank(message = "参考图片URL不能为空") private String refImageUrl; - + + //上传的文件 + private MultipartFile multipartFile; + + @NotBlank(message = "参考提示词不能为空") private String refPrompt; diff --git a/src/main/java/com/rj/controller/sys/MenuController.java b/src/main/java/com/rj/controller/sys/MenuController.java index ed3ca43..47ce429 100644 --- a/src/main/java/com/rj/controller/sys/MenuController.java +++ b/src/main/java/com/rj/controller/sys/MenuController.java @@ -394,6 +394,11 @@ public class MenuController { + + + + + diff --git a/src/main/java/com/rj/controller/sys/RoleController.java b/src/main/java/com/rj/controller/sys/RoleController.java index baa9b36..9da6b72 100644 --- a/src/main/java/com/rj/controller/sys/RoleController.java +++ b/src/main/java/com/rj/controller/sys/RoleController.java @@ -364,6 +364,11 @@ public class RoleController { + + + + + diff --git a/src/main/java/com/rj/controller/sys/UserRoleController.java b/src/main/java/com/rj/controller/sys/UserRoleController.java index bf76a4b..c9be3e0 100644 --- a/src/main/java/com/rj/controller/sys/UserRoleController.java +++ b/src/main/java/com/rj/controller/sys/UserRoleController.java @@ -370,6 +370,11 @@ public class UserRoleController { + + + + + diff --git a/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java b/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java index 21f0fa7..7f0f766 100644 --- a/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java +++ b/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java @@ -136,6 +136,11 @@ public class DifyWorkflowResponseDto { + + + + + diff --git a/src/main/java/com/rj/dto/PosterGenerationRequestDto.java b/src/main/java/com/rj/dto/PosterGenerationRequestDto.java new file mode 100644 index 0000000..8e8e40f --- /dev/null +++ b/src/main/java/com/rj/dto/PosterGenerationRequestDto.java @@ -0,0 +1,98 @@ +package com.rj.dto; + +import lombok.Data; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +/** + * 阿里云海报生成请求DTO + * + * @author 系统生成 + * @since 2025-01-30 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PosterGenerationRequestDto { + + /** + * 模型名称 + */ + private String model = "wanx-poster-generation-v1"; + + /** + * 输入参数 + */ + private Input input; + + /** + * 其他参数 + */ + private java.util.Map parameters; + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Input { + /** + * 标题 + */ + private String title; + + /** + * 副标题 + */ + private String sub_title; + + /** + * 正文内容 + */ + private String body_text; + + /** + * 中文提示词 + */ + private String prompt_text_zh; + + /** + * 宽高比 + */ + private String wh_ratios; + + /** + * LoRA模型名称 + */ + private String lora_name; + + /** + * LoRA权重 + */ + private Double lora_weight; + + /** + * 控制比例 + */ + private Double ctrl_ratio; + + /** + * 控制步数 + */ + private Double ctrl_step; + + /** + * 生成模式 + */ + private String generate_mode; + + /** + * 辅助参数 + */ + private String auxiliary_parameters; + + /** + * 生成数量 + */ + private Integer generate_num; + } +} + diff --git a/src/main/java/com/rj/dto/PosterGenerationResponseDto.java b/src/main/java/com/rj/dto/PosterGenerationResponseDto.java new file mode 100644 index 0000000..087181b --- /dev/null +++ b/src/main/java/com/rj/dto/PosterGenerationResponseDto.java @@ -0,0 +1,68 @@ +package com.rj.dto; + +import lombok.Data; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +/** + * 阿里云海报生成响应DTO + * + * @author 系统生成 + * @since 2025-01-30 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PosterGenerationResponseDto { + + /** + * 输出结果 + */ + private Output output; + + /** + * 请求ID + */ + private String request_id; + + /** + * 使用情况 + */ + private Usage usage; + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Output { + /** + * 任务ID + */ + private String task_id; + + /** + * 任务状态 + */ + private String task_status; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Usage { + /** + * 输入token数 + */ + private Integer input_tokens; + + /** + * 输出token数 + */ + private Integer output_tokens; + + /** + * 总token数 + */ + private Integer total_tokens; + } +} + diff --git a/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java b/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java index 0530ac2..e228612 100644 --- a/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java +++ b/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java @@ -79,6 +79,11 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper { * @return 生成结果,包含图像URL等信息 */ Map generateImageByText(TextModelController.ImageProcessingRequest request); + + /** + * 图生图 + * + * 根据图片和指令生成新图片,包括: + * - 验证API Key和参数 + * - 构建请求体 + * - 调用阿里云图生图API + * - 处理异步响应 + * - 返回任务信息 + * + * @param multipartFile 上传的图片文件 + * @param refPrompt 参考提示词 + * @param ownerName 所属人姓名 + * @param ownerPhone 所属人电话 + * @param imageName 图片名称 + * @param model 模型名称,默认为wanx-v1 + * @param parameters 参数字符串(JSON格式) + * @return 生成结果,包含任务ID等信息 + */ + Map generateImageToImage(org.springframework.web.multipart.MultipartFile multipartFile, + String refPrompt, String ownerName, String ownerPhone, + String imageName, String model, String parameters); } diff --git a/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java b/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java index 2dab9fc..317dd69 100644 --- a/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java +++ b/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java @@ -100,6 +100,11 @@ public interface ICustomerProfileAnalysisService extends IService generateImageToImage(MultipartFile multipartFile, String prompt, + String ownerName, String ownerPhone, String imageName, + String model, String parameters) { + Map result = new HashMap<>(); + + try { + log.info("开始图生图处理,ownerName: {}, imageName: {}, refPrompt: {}", + ownerName, imageName, prompt); + + // 1. 验证API Key + String apiKey = System.getenv("DASHSCOPE_API_KEY"); + if (apiKey == null || apiKey.isEmpty()) { + throw new RuntimeException("DASHSCOPE_API_KEY 环境变量未设置"); + } + + // 2. 上传文件到MinIO并生成临时访问URL + log.info("开始上传文件到MinIO,文件名: {}", multipartFile.getOriginalFilename()); + + // 生成唯一文件名 + String originalFilename = multipartFile.getOriginalFilename(); + String fileExtension = getFileExtension(originalFilename); + String uniqueFileName = generateUniqueFileName(fileExtension); + + // 上传到MinIO + String materialUrl = minIOService.uploadFileWithName(multipartFile, uniqueFileName); + String materialTempUrl = minIOService.generateTempUrl(uniqueFileName); + + log.info("文件上传到MinIO成功:"); + log.info("永久URL: {}", materialUrl); + log.info("临时访问URL: {}", materialTempUrl); + + // 输出到控制台 + System.out.println("=== 图生图文件上传信息 ==="); + System.out.println("原始文件名: " + originalFilename); + System.out.println("生成的文件名: " + uniqueFileName); + System.out.println("MinIO永久URL: " + materialUrl); + System.out.println("MinIO临时访问URL: " + materialTempUrl); + System.out.println("========================="); + + // 3. 创建ImageModel记录并保存到数据库 + ImageModel imageModel = new ImageModel(); + imageModel.setImageName(imageName != null ? imageName : originalFilename); + imageModel.setOwnerName(ownerName); + imageModel.setOwnerPhone(ownerPhone); + imageModel.setImageType("material"); + imageModel.setPrompt(prompt); + imageModel.setModelName(model); + imageModel.setMaterialUrl(materialUrl); + imageModel.setMaterialTempUrl(materialTempUrl); + imageModel.setCreateTime(LocalDateTime.now()); + imageModel.setUpdateTime(LocalDateTime.now()); + + // 保存到数据库 + boolean saveResult = this.saveImageModel(imageModel); + if (!saveResult) { + throw new RuntimeException("保存图生图记录失败"); + } + + log.info("图生图记录已保存到数据库,ID: {}", imageModel.getUuid()); + + // 4. 设置API基础URL + Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1"; + + // 5. 使用MultiModalConversation调用阿里云API + MultiModalConversation conv = new MultiModalConversation(); + + // 构建多模态消息 + MultiModalMessage userMessage = MultiModalMessage.builder() + .role(Role.USER.getValue()) + .content(Arrays.asList( + Collections.singletonMap("image", materialTempUrl), // 使用临时访问URL + Collections.singletonMap("text", prompt) + )).build(); + + // 解析parameters参数 + Map parametersMap = new HashMap<>(); + if (parameters != null && !parameters.isEmpty()) { + try { + @SuppressWarnings("unchecked") + Map parsedParams = objectMapper.readValue(parameters, Map.class); + parametersMap = parsedParams; + } catch (Exception e) { + log.warn("解析parameters参数失败,使用默认值: {}", e.getMessage()); + } + } + + // 设置默认参数 + parametersMap.put("watermark", false); + if (!parametersMap.containsKey("negative_prompt")) { + parametersMap.put("negative_prompt", " "); + } + + // 构建API参数 + MultiModalConversationParam param = MultiModalConversationParam.builder() + .apiKey(apiKey) + .model(model) // 使用图生图模型 + .messages(Collections.singletonList(userMessage)) + .parameters(parametersMap) + .build(); + + // 输出请求参数到控制台 + System.out.println("=== 发送给阿里云的请求参数 ==="); + System.out.println("模型: qwen-image-edit"); + System.out.println("图片URL: " + materialTempUrl); + System.out.println("提示词: " + prompt); + System.out.println("参数: " + JsonUtils.toJson(parametersMap)); + System.out.println("========================="); + + // 6. 调用阿里云API + log.info("开始调用阿里云图生图API"); + MultiModalConversationResult conversationResult = conv.call(param); + + // 输出完整响应到控制台 + System.out.println("=== 阿里云API完整响应 ==="); + System.out.println(JsonUtils.toJson(conversationResult)); + System.out.println("======================"); + + // 7. 处理生成结果 + if (conversationResult != null && conversationResult.getOutput() != null && + conversationResult.getOutput().getChoices() != null && + !conversationResult.getOutput().getChoices().isEmpty()) { + + log.info("图生图API调用成功,开始处理结果"); + + // 从多模态对话结果中提取图像URL + String generatedImageUrl = null; + try { + var choices = conversationResult.getOutput().getChoices(); + if (!choices.isEmpty()) { + var message = choices.get(0).getMessage(); + var content = message.getContent(); + + log.info("API返回的content: {}", JsonUtils.toJson(content)); + + // 解析content数组中的image字段 + if (content != null && !content.isEmpty()) { + for (Object contentItem : content) { + if (contentItem instanceof Map) { + @SuppressWarnings("unchecked") + Map contentMap = (Map) contentItem; + if (contentMap.containsKey("image")) { + generatedImageUrl = (String) contentMap.get("image"); + log.info("提取到生成图像URL: {}", generatedImageUrl); + break; + } + } + } + } + } + + if (generatedImageUrl == null || generatedImageUrl.trim().isEmpty()) { + log.warn("未能从API返回结果中提取图像URL"); + throw new RuntimeException("未能从API返回结果中提取图像URL"); + } + + } catch (Exception e) { + log.error("解析API返回结果失败", e); + throw new RuntimeException("解析API返回结果失败: " + e.getMessage(), e); + } + + // 8. 下载生成的图片并上传到MinIO + if (generatedImageUrl != null && !generatedImageUrl.trim().isEmpty()) { + try { + log.info("开始下载生成的图片: {}", generatedImageUrl); + + // 下载图片到字节数组 + byte[] imageBytes = downloadImageToBytes(generatedImageUrl); + log.info("图片下载成功,大小: {} bytes", imageBytes.length); + + // 生成唯一文件名 + String resultFileName = generateUniqueFileName("png"); + + // 上传到MinIO + String resultImageMinIOUrl = minIOService.uploadFile(imageBytes, resultFileName, "image/png"); + log.info("图片上传到MinIO成功: {}", resultImageMinIOUrl); + + // 生成7天临时访问链接 + String resultImageTempUrl = minIOService.generateTempUrl(resultFileName); + log.info("生成7天临时访问链接: {}", resultImageTempUrl); + + // 更新ImageModel对象 + imageModel.setResultImageUrl(resultImageMinIOUrl); + imageModel.setResultImageTempUrl(resultImageTempUrl); + imageModel.setUpdateTime(LocalDateTime.now()); + imageModel.setImageType("result"); + // 更新数据库记录 + boolean updateResult = this.updateById(imageModel); + if (!updateResult) { + log.error("更新图生图结果记录失败"); + } else { + log.info("图生图结果已更新到数据库"); + } + + // 构建返回结果 + result.put("success", true); + result.put("message", "图生图处理完成"); + result.put("imageCount", 1); + result.put("imageUrl", resultImageTempUrl); + result.put("imageMinIOUrl", resultImageMinIOUrl); + result.put("originalImageUrl", materialTempUrl); + result.put("prompt", model); + result.put("model", model); + result.put("requestId", imageModel.getUuid()); + result.put("timestamp", LocalDateTime.now()); + + log.info("✅ 图生图处理完成,结果已保存到数据库,ID: {}", imageModel.getUuid()); + + } catch (Exception e) { + log.error("处理生成的图片失败", e); + throw new RuntimeException("处理生成的图片失败: " + e.getMessage(), e); + } + } else { + log.warn("生成的图片URL为空"); + throw new RuntimeException("生成的图片URL为空"); + } + } else { + log.warn("图生图结果为空"); + throw new RuntimeException("图生图结果为空"); + } + + return result; + + } catch (ApiException | NoApiKeyException | UploadFileException e) { + log.error("阿里云API调用失败", e); + throw new RuntimeException("阿里云API调用失败: " + e.getMessage(), e); + } catch (Exception e) { + log.error("图生图处理失败: {}", e.getMessage(), e); + + result.put("success", false); + result.put("message", "图生图处理失败: " + e.getMessage()); + result.put("error", e.getClass().getSimpleName()); + result.put("timestamp", LocalDateTime.now()); + result.put("requestId", generateRequestId()); + + throw new RuntimeException("图生图处理失败: " + e.getMessage(), e); + } + } + + /** + * 生成请求ID + * + * @return 唯一的请求ID + */ + private String generateRequestId() { + return "req_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8); + } + } diff --git a/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java b/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java index 280e38c..78cb3aa 100644 --- a/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java @@ -107,6 +107,11 @@ public class MenuServiceImpl extends ServiceImpl implements IM + + + + + diff --git a/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java b/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java index 48e858c..6af5324 100644 --- a/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java @@ -107,6 +107,11 @@ public class RoleServiceImpl extends ServiceImpl implements IR + + + + + diff --git a/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java b/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java index 0917a5a..e13549a 100644 --- a/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java @@ -107,6 +107,11 @@ public class UserRoleServiceImpl extends ServiceImpl i + + + + + diff --git a/src/main/resources/application-audio.yml b/src/main/resources/application-audio.yml index ebd2309..c635704 100644 --- a/src/main/resources/application-audio.yml +++ b/src/main/resources/application-audio.yml @@ -116,6 +116,11 @@ spring: + + + + + diff --git a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml index dce7c6a..01ad0c2 100644 --- a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml +++ b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml @@ -93,6 +93,11 @@ + + + + + diff --git a/src/main/resources/static/tts-demo.html b/src/main/resources/static/tts-demo.html index 6a67a93..7d0cf1a 100644 --- a/src/main/resources/static/tts-demo.html +++ b/src/main/resources/static/tts-demo.html @@ -292,6 +292,11 @@ + + + + + diff --git a/src/test/java/com/rj/controller/PosterGenerationTest.java b/src/test/java/com/rj/controller/PosterGenerationTest.java new file mode 100644 index 0000000..fcb845d --- /dev/null +++ b/src/test/java/com/rj/controller/PosterGenerationTest.java @@ -0,0 +1,196 @@ + +package com.rj.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.*; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class PosterGenerationTest { + + private static final Logger log = LoggerFactory.getLogger(PosterGenerationTest.class); + private final RestTemplate restTemplate = new RestTemplate(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + public static void main(String[] args) { + System.out.println("程序开始运行..."); + try { + new PosterGenerationTest().testPosterGeneration(); + } catch (Exception e) { + System.out.println("程序运行出错: " + e.getMessage()); + e.printStackTrace(); + } + System.out.println("程序结束"); + } + public void testPosterGeneration() { + try { + System.out.println("开始测试阿里云海报生成API"); + log.info("开始测试阿里云海报生成API"); + + // 验证API Key + String apiKey = System.getenv("DASHSCOPE_API_KEY"); + if (apiKey == null || apiKey.isEmpty()) { + System.out.println("DASHSCOPE_API_KEY 环境变量未设置"); + log.error("DASHSCOPE_API_KEY 环境变量未设置"); + return; + } + System.out.println("API Key 已设置,开始构建请求..."); + System.out.println("API Key: " + apiKey.substring(0, 10) + "..."); + + // 构建请求体 - 使用阿里云官方文档的标准格式 + Map requestBody = new HashMap<>(); + requestBody.put("model", "wanx-poster-generation-v1"); + + // 设置输入参数 - 使用完整的官方参数 + Map input = new HashMap<>(); + input.put("title", "春节快乐"); + input.put("sub_title", "家庭团聚,共享天伦之乐"); + input.put("body_text", "春节是中国最重要的传统节日之一,它象征着新的开始和希望"); + input.put("prompt_text_zh", "灯笼,小猫,梅花"); + input.put("wh_ratios", "竖版"); + input.put("lora_name", "童话油画"); + input.put("lora_weight", 0.8); + input.put("ctrl_ratio", 0.7); + input.put("ctrl_step", 0.7); + input.put("generate_mode", "generate"); + // auxiliary_parameters 在首次调用时应该不传或者传空字符串 +// input.put("auxiliary_parameters", "[]"); + input.put("generate_num", 1); + + requestBody.put("input", input); + requestBody.put("parameters", new HashMap<>()); + + // 构建请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("X-DashScope-Async", "enable"); // 启用异步调用 + headers.set("Authorization", "Bearer " + apiKey); + headers.setContentType(MediaType.APPLICATION_JSON); + + // 创建HTTP实体 + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + // 发送请求 + String apiUrl = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis"; + System.out.println("发送请求到阿里云海报生成API: " + apiUrl); + System.out.println("请求体: " + objectMapper.writeValueAsString(requestBody)); + log.info("发送请求到阿里云海报生成API: {}", apiUrl); + log.info("请求体: {}", objectMapper.writeValueAsString(requestBody)); // 打印请求体 + + ResponseEntity response = restTemplate.exchange( + apiUrl, + HttpMethod.POST, + entity, + String.class + ); + + // 处理响应 + System.out.println("响应状态码: " + response.getStatusCode()); + System.out.println("响应体: " + response.getBody()); + log.info("响应状态码: {}", response.getStatusCode()); + log.info("响应体: {}", response.getBody()); + + if (response.getStatusCode().is2xxSuccessful()) { + System.out.println("✅ 阿里云海报生成API调用成功"); + log.info("✅ 阿里云海报生成API调用成功"); + + // 解析响应获取task_id + JsonNode responseNode = objectMapper.readTree(response.getBody()); + JsonNode outputNode = responseNode.path("output"); + + if (!outputNode.isMissingNode() && outputNode.has("task_id")) { + String taskId = outputNode.path("task_id").asText(); + String taskStatus = outputNode.path("task_status").asText(); + + log.info("任务ID: {}", taskId); + log.info("任务状态: {}", taskStatus); + + // 轮询获取任务结果 + getTaskResult(apiKey, taskId); + } else { + log.error("❌ 响应中未找到task_id或output节点"); + } + } else { + log.error("❌ 阿里云海报生成API调用失败,状态码: {}", response.getStatusCode()); + log.error("❌ 响应详情: {}", response.getBody()); + } + + } catch (Exception e) { + log.error("测试阿里云海报生成API失败: {}", e.getMessage(), e); + } + } + + /** + * 轮询获取任务结果 + * @param apiKey API Key + * @param taskId 任务ID + */ + private void getTaskResult(String apiKey, String taskId) { + String resultUrl = "https://dashscope.aliyuncs.com/api/v1/tasks/" + taskId; + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + apiKey); + HttpEntity entity = new HttpEntity<>(headers); + + int maxRetries = 30; + int intervalSeconds = 5; + + for (int i = 0; i < maxRetries; i++) { + try { + ResponseEntity response = restTemplate.exchange(resultUrl, HttpMethod.GET, entity, String.class); + if (response.getStatusCode().is2xxSuccessful()) { + JsonNode resultNode = objectMapper.readTree(response.getBody()); + JsonNode statusNode = resultNode.path("output").path("task_status"); + + if (!statusNode.isMissingNode()) { + String status = statusNode.asText(); + log.info("当前任务状态: {}", status); + + if ("SUCCEEDED".equalsIgnoreCase(status)) { + log.info("🎉 任务执行成功!"); + log.info("任务结果详情: {}", response.getBody()); + return; + } else if ("FAILED".equalsIgnoreCase(status)) { + log.error("❌ 任务执行失败!"); + log.error("失败详情: {}", response.getBody()); + // 如果失败,打印更详细的错误信息 + JsonNode messageNode = resultNode.path("output").path("message"); + if (!messageNode.isMissingNode()) { + log.error("失败具体原因: {}", messageNode.asText()); + } + return; + } + // 如果是 RUNNING 或其他中间状态,继续轮询 + } else { + log.error("❌ 获取任务状态失败,响应中缺少 task_status 字段。"); + log.error("响应内容: {}", response.getBody()); + return; + } + } else { + log.error("❌ 查询任务状态失败,状态码: {}", response.getStatusCode()); + log.error("响应内容: {}", response.getBody()); + } + } catch (Exception e) { + log.error("轮询任务结果时发生异常: {}", e.getMessage(), e); + } + + log.info("等待 {} 秒后继续轮询...", intervalSeconds); + try { + TimeUnit.SECONDS.sleep(intervalSeconds); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("轮询被中断"); + return; + } + } + + log.error("❌ 超时,未能获取最终任务结果。"); + } +} + + + diff --git a/src/test/java/com/rj/image/QwenImageEdit.java b/src/test/java/com/rj/image/QwenImageEdit.java new file mode 100644 index 0000000..c49438b --- /dev/null +++ b/src/test/java/com/rj/image/QwenImageEdit.java @@ -0,0 +1,68 @@ +package com.rj.image; + +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; +import com.alibaba.dashscope.common.MultiModalMessage; +import com.alibaba.dashscope.common.Role; +import com.alibaba.dashscope.exception.ApiException; +import com.alibaba.dashscope.exception.NoApiKeyException; +import com.alibaba.dashscope.exception.UploadFileException; +import com.alibaba.dashscope.utils.Constants; +import com.alibaba.dashscope.utils.JsonUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class QwenImageEdit { + + static { + // 以下为中国(北京)地域url,若使用新加坡地域的模型,需将url替换为:https://dashscope-intl.aliyuncs.com/api/v1 + Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1"; + } + + // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key + // 若没有配置环境变量,请用百炼 API Key 将下行替换为:apiKey="sk-xxx" + static String apiKey = System.getenv("DASHSCOPE_API_KEY"); + + public static void call() throws ApiException, NoApiKeyException, UploadFileException, IOException { + + MultiModalConversation conv = new MultiModalConversation(); + + // 模型支持输入1-3张图片 + MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue()) + .content(Arrays.asList( + Collections.singletonMap("image", "http://101.35.52.237:19005/car/1760778801381_925c0074cde844d8bf6eeee7f98723e4.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20251018%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251018T091321Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=e99e8e4d023c452af18961c9ee93c70b589f6ae575a2190b13d3a4c0a6c818f3"), + Collections.singletonMap("image", "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/iclsnx/input2.png"), + Collections.singletonMap("image", "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/gborgw/input3.png"), + Collections.singletonMap("text", "图1中的女生穿着图2中的黑色裙子按图3的姿势坐下") + )).build(); + + Map parameters = new HashMap<>(); + parameters.put("watermark", false); + parameters.put("negative_prompt", " "); + + MultiModalConversationParam param = MultiModalConversationParam.builder() + .apiKey(apiKey) + .model("qwen-image-edit") + .messages(Collections.singletonList(userMessage)) + .parameters(parameters) + .build(); + + MultiModalConversationResult result = conv.call(param); + // 如需查看完整响应,请取消下行注释 + // System.out.println(JsonUtils.toJson(result)); + System.out.println("输出图像的URL:" + result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("image")); + } + + public static void main(String[] args) { + try { + call(); + } catch (ApiException | NoApiKeyException | UploadFileException | IOException e) { + System.out.println(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/rj/service/FaceDetectImageCountTest.java b/src/test/java/com/rj/service/FaceDetectImageCountTest.java index 1a386f8..d1edbda 100644 --- a/src/test/java/com/rj/service/FaceDetectImageCountTest.java +++ b/src/test/java/com/rj/service/FaceDetectImageCountTest.java @@ -230,6 +230,11 @@ public class FaceDetectImageCountTest { + + + + + diff --git a/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java b/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java index d8c1a89..32f8218 100644 --- a/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java +++ b/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java @@ -171,6 +171,11 @@ public class TtsRequestLogShortUrlTest { + + + + + diff --git a/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java b/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java index 9c2efc2..703cccd 100644 --- a/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java +++ b/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java @@ -149,6 +149,11 @@ public class VideoSynthesisTempUrlTest { + + + + + diff --git a/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java b/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java index 640cce3..4a6e5e3 100644 --- a/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java +++ b/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java @@ -124,6 +124,11 @@ public class VideoSynthesisVideoNameTest { + + + + +