Merge branch 'dev/dev_xp' into dev/dev

# Conflicts:
#	src/main/java/com/ai/da/model/dto/ToProductImageDTO.java
This commit is contained in:
2025-06-12 16:54:15 +08:00
8 changed files with 284 additions and 24 deletions

View File

@@ -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<String, Object> params) {
int status;
String body;
try (HttpResponse execute = HttpRequest.get(url)
.form(params) // 直接传入MapHutool会正确处理
.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;
}
}

View File

@@ -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<String> 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<String> fluxResult(@RequestParam("taskId") String taskId){
return Response.success(generateService.getFluxResult(taskId, 87L));
}

View File

@@ -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;
}

View File

@@ -16,4 +16,5 @@ public class ToProductImageDTO {
private Double brightenValue;
private BigDecimal imageStrengthMin;
private BigDecimal imageStrengthMax;
private String modelName;
}

View File

@@ -28,4 +28,8 @@ public class MagicToolResultVO {
private String elementType;
private Long elementId;
public MagicToolResultVO(String status) {
this.status = status;
}
}

View File

@@ -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<Generate> {
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);
}

View File

@@ -62,6 +62,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
@@ -326,8 +327,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> 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);
}
}
@@ -993,7 +994,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> 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保存
@@ -1833,7 +1834,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> 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);
@@ -1988,4 +1989,84 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> 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<String, Object> 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;
}
}

View File

@@ -440,9 +440,18 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}else {
sb.append(",high quality clothing details,").append(prompt).append(",8K realistic,HDR");
}
// 走模型
pythonService.toProductImage(tDesignPythonOutfit.getDesignUrl(), taskId, sb.toString(), toProductImageDTO.getImageStrength(), productType);
ToProductImageResult toProductImageResult = new ToProductImageResult();
if (!StringUtil.isNullOrEmpty(toProductImageDTO.getModelName())
&& toProductImageDTO.getModelName().equals("flux")){
taskId = generateService.flux(CreditsEventsEnum.TO_PRODUCT_IMAGE, sb.toString(), tDesignPythonOutfit.getDesignUrl());
toProductImageResult.setModelName("flux");
toProductImageResult.setResultType("ToProductImage");
} else {
// 走模型
pythonService.toProductImage(tDesignPythonOutfit.getDesignUrl(), taskId, sb.toString(), toProductImageDTO.getImageStrength(), productType);
toProductImageResult.setModelName("local");
}
toProductImageResult.setElementId(tDesignPythonOutfit.getId());
toProductImageResult.setElementType("DesignOutfit");
toProductImageResult.setCreateTime(LocalDateTime.now());
@@ -467,9 +476,18 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
taskId = UUID.randomUUID() + "-" + i + "-" + userHolder.getId();
ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageVO.getElementId());
// 走模型
pythonService.toProductImage(toProductElement.getUrl(), taskId, sb.toString(), toProductImageDTO.getImageStrength(), "overall");
ToProductImageResult toProductImageResult = new ToProductImageResult();
if (!StringUtil.isNullOrEmpty(toProductImageDTO.getModelName())
&& toProductImageDTO.getModelName().equals("flux")){
taskId = generateService.flux(CreditsEventsEnum.TO_PRODUCT_IMAGE, sb.toString(), toProductElement.getUrl());
toProductImageResult.setModelName("flux");
toProductImageResult.setResultType("ToProductImage");
} else {
// 走模型
pythonService.toProductImage(toProductElement.getUrl(), taskId, sb.toString(), toProductImageDTO.getImageStrength(), "overall");
toProductImageResult.setModelName("local");
}
toProductImageResult.setElementId(toProductElement.getId());
toProductImageResult.setElementType("ProductElement");
toProductImageResult.setCreateTime(LocalDateTime.now());
@@ -612,6 +630,30 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
List<MagicToolResultVO> results = new ArrayList<>();
Set<String> collect = new HashSet<>();
taskIdList.forEach(taskId -> {
// 查记录
QueryWrapper<ToProductImageResult> 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())) {
@@ -620,12 +662,6 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
magicToolResultVO.setStatus("Invalid");
} else {
magicToolResultVO.setUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
QueryWrapper<ToProductImageResult> 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());
@@ -646,6 +682,31 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
return results;
}
private MagicToolResultVO processFluxResult(String fluxImgMinioPath, ToProductImageResult toProductImageResult, String taskId){
toProductImageResult.setUrl(fluxImgMinioPath);
toProductImageResultMapper.updateById(toProductImageResult);
MagicToolResultVO magicToolResultVO = new MagicToolResultVO();
magicToolResultVO.setTaskId(taskId);
magicToolResultVO.setId(toProductImageResult.getId());
magicToolResultVO.setUrl(minioUtil.getPreSignedUrl(fluxImgMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
magicToolResultVO.setStatus("Success");
magicToolResultVO.setResultType(toProductImageResult.getResultType());
magicToolResultVO.setElementId(toProductImageResult.getElementId());
magicToolResultVO.setElementType(toProductImageResult.getElementType());
if (toProductImageResult.getElementType().equals("ProductElement")) {
ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageResult.getElementId());
magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(toProductElement.getUrl(), 24 * 60));
} else if (toProductImageResult.getElementType().equals("DesignOutfit")){
TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(toProductImageResult.getElementId());
magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(tDesignPythonOutfit.getDesignUrl(), 24 * 60));
} else if (toProductImageResult.getElementType().equals("ToProductImage")){
ToProductImageResult toProductImage = toProductImageResultMapper.selectById(toProductImageResult.getElementId());
magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(toProductImage.getUrl(), 24 * 60));
}
return magicToolResultVO;
}
@Override
public JSONObject exportSearch(ExportSearchDTO exportSearchDTO) {
QueryWrapper<ExportFile> qw = new QueryWrapper<>();
@@ -894,9 +955,16 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
}
// 走模型
pythonService.relight(toProductImageResult1.getUrl(), taskId, s, toProductImageDTO.getDirection(), relightType);
ToProductImageResult toProductImageResult = new ToProductImageResult();
if (!StringUtil.isNullOrEmpty(toProductImageDTO.getModelName())
&& toProductImageDTO.getModelName().equals("flux")){
taskId = generateService.flux(CreditsEventsEnum.RELIGHT, s, toProductImageResult1.getUrl());
toProductImageResult.setModelName("flux");
toProductImageResult.setResultType("Relight");
} else {
// 走模型
pythonService.relight(toProductImageResult1.getUrl(), taskId, s, toProductImageDTO.getDirection(), relightType);
}
toProductImageResult.setElementId(toProductImageResult1.getId());
toProductImageResult.setElementType("ToProductImage");
toProductImageResult.setCreateTime(LocalDateTime.now());
@@ -917,9 +985,16 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
result.add(toProductImageResult);
}else {
ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageVO.getElementId());
// 走模型
pythonService.relight(toProductElement.getUrl(), taskId, s, toProductImageDTO.getDirection(), "overall");
ToProductImageResult toProductImageResult = new ToProductImageResult();
if (!StringUtil.isNullOrEmpty(toProductImageDTO.getModelName())
&& toProductImageDTO.getModelName().equals("flux")){
taskId = generateService.flux(CreditsEventsEnum.RELIGHT, s, toProductElement.getUrl());
toProductImageResult.setModelName("flux");
toProductImageResult.setResultType("Relight");
} else {
// 走模型
pythonService.relight(toProductElement.getUrl(), taskId, s, toProductImageDTO.getDirection(), "overall");
}
toProductImageResult.setElementId(toProductElement.getId());
toProductImageResult.setElementType("ProductElement");
toProductImageResult.setCreateTime(LocalDateTime.now());
@@ -951,6 +1026,31 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
List<MagicToolResultVO> results = new ArrayList<>();
Set<String> collect = new HashSet<>();
taskIdList.forEach(taskId -> {
QueryWrapper<ToProductImageResult> 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())) {
@@ -959,12 +1059,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
magicToolResultVO.setStatus("Invalid");
} else {
magicToolResultVO.setUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
QueryWrapper<ToProductImageResult> 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());