From 6e32289b98cf61c0cd5b50407be5eb21ef508331 Mon Sep 17 00:00:00 2001 From: xupei Date: Thu, 12 Jun 2025 16:50:27 +0800 Subject: [PATCH] =?UTF-8?q?TASK:Flux=E6=8E=A5=E5=85=A5=EF=BC=8C=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E4=BA=8Eto=20product=20image=E5=92=8Crelight?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/da/common/utils/SendRequestUtil.java | 43 ++++++ .../ai/da/controller/GenerateController.java | 22 +++ .../primary/entity/ToProductImageResult.java | 7 + .../ai/da/model/dto/ToProductImageDTO.java | 1 + .../com/ai/da/model/vo/MagicToolResultVO.java | 4 + .../com/ai/da/service/GenerateService.java | 7 + .../da/service/impl/GenerateServiceImpl.java | 89 +++++++++++- .../impl/UserLikeGroupServiceImpl.java | 135 +++++++++++++++--- 8 files changed, 284 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/ai/da/common/utils/SendRequestUtil.java b/src/main/java/com/ai/da/common/utils/SendRequestUtil.java index 1b170d64..05cc4415 100644 --- a/src/main/java/com/ai/da/common/utils/SendRequestUtil.java +++ b/src/main/java/com/ai/da/common/utils/SendRequestUtil.java @@ -9,6 +9,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import java.util.Map; + @Slf4j @Component public class SendRequestUtil { @@ -68,6 +70,26 @@ public class SendRequestUtil { } } + public String sendFluxPost(String url, String requestBodyStr){ + int status; + String body; + try (HttpResponse execute = HttpRequest.post(url) + .header(Header.CONTENT_TYPE, "application/json") + .header("x-key", "d447a0ac-2291-4f1c-9a36-f7614c385989") + .body(requestBodyStr) // Hutool 会自动处理 JSON 序列化 + .timeout(180000) // 设置超时(毫秒) + .execute()) { + + status = execute.getStatus(); + body = execute.body(); + if (status == 200) { + return body; + } + } + log.warn("请求失败,状态码为 : {}, body: {}", status, body); + return null; + } + public String sendPost(String url, String requestBodyStr){ int status; String body; @@ -87,6 +109,27 @@ public class SendRequestUtil { return null; } + public String sendGet(String url, Map params) { + int status; + String body; + try (HttpResponse execute = HttpRequest.get(url) + .form(params) // 直接传入Map,Hutool会正确处理 + .timeout(180000) + .execute()) { + + status = execute.getStatus(); + body = execute.body(); + if (status == 200) { + return body; + } + } catch (Exception e) { + log.error("请求发生异常: {}", e.getMessage(), e); + return null; + } + log.warn("请求失败,状态码为: {}, body: {}", status, body); + return body; + } + } diff --git a/src/main/java/com/ai/da/controller/GenerateController.java b/src/main/java/com/ai/da/controller/GenerateController.java index 4d53f283..837d535c 100644 --- a/src/main/java/com/ai/da/controller/GenerateController.java +++ b/src/main/java/com/ai/da/controller/GenerateController.java @@ -1,5 +1,6 @@ package com.ai.da.controller; +import com.ai.da.common.enums.CreditsEventsEnum; import com.ai.da.common.response.Response; import com.ai.da.model.dto.*; import com.ai.da.model.vo.*; @@ -188,7 +189,28 @@ public class GenerateController { return Response.success(generateService.getImageDescription(path)); } +// @ApiOperation(value = "试用flux") +// @GetMapping("/flux") + public Response flux(@RequestParam("path") String path, + @RequestParam("type") int type, + @RequestParam(value = "prompt", required = false) String prompt){ + CreditsEventsEnum typeEnum = CreditsEventsEnum.RELIGHT; + switch (type){ + case 1: + typeEnum = CreditsEventsEnum.TO_PRODUCT_IMAGE; + break; + case 2: + typeEnum = CreditsEventsEnum.IMAGE_TO_SKETCH; + break; + } + return Response.success(generateService.flux(typeEnum, prompt, path)); + } +// @ApiOperation(value = "获取flux结果") +// @GetMapping("/fluxResult") + public Response fluxResult(@RequestParam("taskId") String taskId){ + return Response.success(generateService.getFluxResult(taskId, 87L)); + } diff --git a/src/main/java/com/ai/da/mapper/primary/entity/ToProductImageResult.java b/src/main/java/com/ai/da/mapper/primary/entity/ToProductImageResult.java index c6b601cd..4180643d 100644 --- a/src/main/java/com/ai/da/mapper/primary/entity/ToProductImageResult.java +++ b/src/main/java/com/ai/da/mapper/primary/entity/ToProductImageResult.java @@ -20,6 +20,11 @@ public class ToProductImageResult implements Serializable { @ApiModelProperty(value = "elementId") private Long elementId; + /** + * 取值类型: DesignOutfit 以design的结果作为图片来源 + * ProductElement 上传的图片 + * ToProductImage 以to product image的结果作为图片来源(relight) + */ @ApiModelProperty(value = "elementType") private String elementType; @@ -53,4 +58,6 @@ public class ToProductImageResult implements Serializable { private Long projectId; private String taskIdBatch; + + private String modelName; } diff --git a/src/main/java/com/ai/da/model/dto/ToProductImageDTO.java b/src/main/java/com/ai/da/model/dto/ToProductImageDTO.java index 5fa93cc7..56c8bbbd 100644 --- a/src/main/java/com/ai/da/model/dto/ToProductImageDTO.java +++ b/src/main/java/com/ai/da/model/dto/ToProductImageDTO.java @@ -14,4 +14,5 @@ public class ToProductImageDTO { private BigDecimal imageStrength; private String direction; private Double brightenValue; + private String modelName; } diff --git a/src/main/java/com/ai/da/model/vo/MagicToolResultVO.java b/src/main/java/com/ai/da/model/vo/MagicToolResultVO.java index 1c26db5a..24b2a7ed 100644 --- a/src/main/java/com/ai/da/model/vo/MagicToolResultVO.java +++ b/src/main/java/com/ai/da/model/vo/MagicToolResultVO.java @@ -28,4 +28,8 @@ public class MagicToolResultVO { private String elementType; private Long elementId; + + public MagicToolResultVO(String status) { + this.status = status; + } } diff --git a/src/main/java/com/ai/da/service/GenerateService.java b/src/main/java/com/ai/da/service/GenerateService.java index e02be05d..8fa067e2 100644 --- a/src/main/java/com/ai/da/service/GenerateService.java +++ b/src/main/java/com/ai/da/service/GenerateService.java @@ -1,5 +1,6 @@ package com.ai.da.service; +import com.ai.da.common.enums.CreditsEventsEnum; import com.ai.da.mapper.primary.entity.Generate; import com.ai.da.mapper.primary.entity.GenerateDetail; import com.ai.da.model.dto.*; @@ -81,4 +82,10 @@ public interface GenerateService extends IService { String reimagineFreePik(String path, String prompt, String style) throws IOException; String getImageDescription(String imagePath); + + String flux(CreditsEventsEnum func, String prompt, String imagePath); + + String getFluxResult(String taskId, Long accountId); + + byte[] downloadVideoOrImage(String url); } diff --git a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java index 174b42a1..42923843 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -61,6 +61,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.ai.da.common.enums.CollectionLevel1TypeEnum.*; +import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE; @Slf4j @Service @@ -325,8 +326,8 @@ public class GenerateServiceImpl extends ServiceImpl i Boolean b = creditsService.taskCreditsDeduction(accountId, taskId); // 3、记录积分变更 if (b) creditsService.insertToCreditsDetail(accountId, - CreditsEventsEnum.TO_PRODUCT_IMAGE.getName(), - CreditsEventsEnum.TO_PRODUCT_IMAGE.getValue(), + TO_PRODUCT_IMAGE.getName(), + TO_PRODUCT_IMAGE.getValue(), "negative", null); } } @@ -992,7 +993,7 @@ public class GenerateServiceImpl extends ServiceImpl i String upgradeImageUrl = data.getBeanList("generated", String.class).get(0); String taskId = data.getStr("task_id"); - // 下载图片 + // 下载图片 freepik // byte[] bytes = downloadWithProxy(upgradeImageUrl); byte[] bytes = downloadVideoOrImage(upgradeImageUrl); // 2、上传图片到minio保存 @@ -1822,7 +1823,7 @@ public class GenerateServiceImpl extends ServiceImpl i poseTransformationMapper.updateById(poseTransformation); } - private byte[] downloadVideoOrImage(String url) { + public byte[] downloadVideoOrImage(String url) { try (CloseableHttpClient client = HttpClients.createDefault(); InputStream in = client.execute(new HttpGet(url)).getEntity().getContent()) { return IOUtils.toByteArray(in); @@ -1977,4 +1978,84 @@ public class GenerateServiceImpl extends ServiceImpl i } return null; // 未匹配到性别 } + + /** + * 接入flux模型,用于imageToSketch(sketch extract) || relighting || to product image + * @param func 功能枚举名 + * @param prompt 用户输入 + * @param imagePath 图片minio路径 + * @return 返回taskId,用于异步获取结果 + */ + public String flux(CreditsEventsEnum func, String prompt, String imagePath){ + String fluxRequestUrl = "https://api.bfl.ai/v1/flux-kontext-pro"; + if (StringUtil.isNullOrEmpty(prompt)){ + switch (func){ + case RELIGHT: + prompt = "a model standing on the beautiful beach, ultra high quality, 8k"; + break; + case IMAGE_TO_SKETCH: + prompt = "generate the sketch of the image, simple line, ultra high quality"; + break; + case TO_PRODUCT_IMAGE: + prompt = "change the image to real style, ultra high quality, 8k"; + break; + } + } + JSONObject requestBody = new JSONObject(); + + requestBody.set("prompt", prompt); + requestBody.set("seed", 42); +// requestBody.set("aspect_ratio", "9:16"); + requestBody.set("output_format", "png"); + + if (!StringUtil.isNullOrEmpty(imagePath)){ + try { + String imageAsBase64 = minioUtil.getImageAsBase64(imagePath); + requestBody.set("input_image", imageAsBase64); + } catch (IOException e) { + log.error("获取图片的base64格式失败,{}", String.valueOf(e)); + throw new BusinessException("Failed to obtain the image in base64 format."); + } + } + + String resp = sendRequestUtil.sendFluxPost(fluxRequestUrl, requestBody.toString()); + JSONObject respObj = JSONUtil.parseObj(resp); + log.info("flux 发起生成请求返回结果: {}", respObj); + if (StringUtil.isNullOrEmpty(respObj.getStr("id"))){ + return null; + } + return respObj.getStr("id"); + } + + public String getFluxResult(String taskId, Long accountId){ + String fluxResultRequestUrl = "https://api.bfl.ai/v1/get_result"; + HashMap params = new HashMap<>(); + params.put("id", taskId); + String resp = sendRequestUtil.sendGet(fluxResultRequestUrl, params); + log.info("获取flux生成的结果为:{}", resp); + JSONObject respObj = JSONUtil.parseObj(resp); + String status = respObj.getStr("status"); + switch (status){ + case "Task not found": + return "Failed"; + case "Pending": + case "Request Moderated": + case "Content Moderated": + // 处理中 + return "Pending"; + case "Ready": + // 已完成 获取结果 + String fluxResult = JSONUtil.parseObj(respObj.getStr("result")).getStr("sample"); + byte[] bytes = downloadVideoOrImage(fluxResult); + String objectName = accountId + "/product_image/" + taskId + ".png"; + minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png"); + +// return minioUtil.getPreSignedUrl(userBucket + "/" + objectName, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); + return userBucket + "/" + objectName; + case "Error": + // 出错 + return "Failed"; + } + return null; + } } diff --git a/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java b/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java index 44d0445b..2ca6efb3 100644 --- a/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java @@ -432,9 +432,18 @@ public class UserLikeGroupServiceImpl extends ServiceImpl results = new ArrayList<>(); Set collect = new HashSet<>(); taskIdList.forEach(taskId -> { + // 查记录 + QueryWrapper qw = new QueryWrapper<>(); + qw.lambda().eq(ToProductImageResult::getTaskId, taskId); + ToProductImageResult toProductImageResult = toProductImageResultMapper.selectOne(qw); + if (Objects.isNull(toProductImageResult)) { + throw new BusinessException("The source image does not exist."); + } + // 判断当任务从哪个模型获取结果 + if (!StringUtil.isNullOrEmpty(toProductImageResult.getModelName()) && toProductImageResult.getModelName().equals("flux")){ + Project project = projectMapper.selectById(toProductImageResult.getProjectId()); + if (Objects.isNull(project)){ + throw new BusinessException("unknown project"); + } + String fluxResult = generateService.getFluxResult(taskId, project.getAccountId()); + if (StringUtil.isNullOrEmpty(fluxResult)){ + results.add(new MagicToolResultVO()); + } else if (fluxResult.equals("Failed") || fluxResult.equals("Pending")) { + results.add(new MagicToolResultVO(fluxResult)); + } else { + results.add(processFluxResult(fluxResult, toProductImageResult, taskId)); + } + return; + } + String key = toProductImageResultKey + ":" + taskId; MagicToolResultVO magicToolResultVO = new Gson().fromJson(redisUtil.getFromString(key), MagicToolResultVO.class); if (!Objects.isNull(magicToolResultVO) && !StringUtil.isNullOrEmpty(magicToolResultVO.getUrl())) { @@ -603,12 +645,6 @@ public class UserLikeGroupServiceImpl extends ServiceImpl qw = new QueryWrapper<>(); - qw.lambda().eq(ToProductImageResult::getTaskId, taskId); - ToProductImageResult toProductImageResult = toProductImageResultMapper.selectOne(qw); - if (Objects.isNull(toProductImageResult)) { - throw new BusinessException("The source image does not exist."); - } magicToolResultVO.setResultType(toProductImageResult.getResultType()); magicToolResultVO.setElementId(toProductImageResult.getElementId()); magicToolResultVO.setElementType(toProductImageResult.getElementType()); @@ -629,6 +665,31 @@ public class UserLikeGroupServiceImpl extends ServiceImpl qw = new QueryWrapper<>(); @@ -871,9 +932,16 @@ public class UserLikeGroupServiceImpl extends ServiceImpl results = new ArrayList<>(); Set collect = new HashSet<>(); taskIdList.forEach(taskId -> { + QueryWrapper qw = new QueryWrapper<>(); + qw.lambda().eq(ToProductImageResult::getTaskId, taskId); + ToProductImageResult toProductImageResult = toProductImageResultMapper.selectOne(qw); + if (Objects.isNull(toProductImageResult)) { + throw new BusinessException("The source image does not exist."); + } + // 判断当任务从哪个模型获取结果 + if (!StringUtil.isNullOrEmpty(toProductImageResult.getModelName()) + && toProductImageResult.getModelName().equals("flux")){ + Project project = projectMapper.selectById(toProductImageResult.getProjectId()); + if (Objects.isNull(project)){ + throw new BusinessException("unknown project"); + } + String fluxResult = generateService.getFluxResult(taskId, project.getAccountId()); + + if (StringUtil.isNullOrEmpty(fluxResult)){ + results.add(new MagicToolResultVO()); + } else if (fluxResult.equals("Failed") || fluxResult.equals("Pending")) { + results.add(new MagicToolResultVO(fluxResult)); + } else { + results.add(processFluxResult(fluxResult, toProductImageResult, taskId)); + } + return; + } + String key = relightResultKey + ":" + taskId; MagicToolResultVO magicToolResultVO = new Gson().fromJson(redisUtil.getFromString(key), MagicToolResultVO.class); if (!Objects.isNull(magicToolResultVO) && !StringUtil.isNullOrEmpty(magicToolResultVO.getUrl())) { @@ -936,12 +1036,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl qw = new QueryWrapper<>(); - qw.lambda().eq(ToProductImageResult::getTaskId, taskId); - ToProductImageResult toProductImageResult = toProductImageResultMapper.selectOne(qw); - if (Objects.isNull(toProductImageResult)) { - throw new BusinessException("The source image does not exist."); - } + magicToolResultVO.setResultType(toProductImageResult.getResultType()); magicToolResultVO.setElementId(toProductImageResult.getElementId()); magicToolResultVO.setElementType(toProductImageResult.getElementType());