From ea19e915db14eb16c583c5c3150a042ba9af5e9c Mon Sep 17 00:00:00 2001 From: litianxiang Date: Mon, 22 Sep 2025 15:03:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=E4=BA=8C=E5=88=9B=E6=97=A7=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/da/service/impl/PortfolioServiceImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ai/da/service/impl/PortfolioServiceImpl.java b/src/main/java/com/ai/da/service/impl/PortfolioServiceImpl.java index 41b77f65..9179ff06 100644 --- a/src/main/java/com/ai/da/service/impl/PortfolioServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/PortfolioServiceImpl.java @@ -742,10 +742,21 @@ public class PortfolioServiceImpl extends ServiceImpl userLikeSnapshot.getDesignItem().getDesignId())); userLikeMapByDesignId.forEach((designId, userLikeListOld1) -> { Design design = designMapper.selectById(designId); - //数据库中存在designId为-1的情况,此处值从workspace里取 + //数据库中存在designId为-1的情况 if (design == null) { design = new Design(); + //此处值从workspace里取 design.setSystemScale(BigDecimal.valueOf(workspaceOld.getSystemDesignerPercentage()).movePointLeft(2)); + //根据CollectionElement存在model来判断是否是single + List modelTypeElements = collectionElementListOld.stream() + .filter(element -> element.getLevel1Type().equals(CollectionLevel1TypeEnum.MODEL.getRealName())) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(modelTypeElements)){ + design.setSingleOverall("overall"); + }else { + design.setSingleOverall("single"); + } + } design.setId(null); design.setCollectionId(collectionIdNew); From ab6cc044835022761990b1d67f4e8e2ff6507ff7 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 30 Sep 2025 17:22:23 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=B8=BA=E6=A8=A1=E7=89=B9=E5=92=8C?= =?UTF-8?q?=E7=BA=BF=E7=A8=BF=E8=AE=BE=E7=BD=AE=E6=80=A7=E5=88=AB=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AE=BE=E7=BD=AE=E6=A8=A1=E7=89=B9=E4=BC=9A?= =?UTF-8?q?=E5=A2=9E=E5=A4=9A=E9=97=AE=E9=A2=98=20=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=9B=B4=E6=8D=A2=20=E8=AE=BE=E7=BD=AE=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E7=94=B7=E5=A5=B3=E5=85=BC=E5=AE=B9=20TODO?= =?UTF-8?q?=EF=BC=9A=E6=89=A3=E9=99=A4=E7=A7=AF=E5=88=86=E4=BB=B7=E6=A0=BC?= =?UTF-8?q?=20=E8=BD=AC=E4=BA=A7=E5=93=81=E5=9B=BE=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 13 + .../ai/da/common/constant/ModelConstants.java | 42 + .../ai/da/common/enums/CreditsEventsEnum.java | 4 +- .../primary/entity/CollectionElement.java | 6 + .../com/ai/da/service/GenerateService.java | 2 + .../com/ai/da/service/WorkspaceService.java | 2 + .../impl/CollectionElementServiceImpl.java | 351 +++++--- .../ai/da/service/impl/DesignServiceImpl.java | 28 +- .../da/service/impl/GenerateServiceImpl.java | 769 ++++++++++++++++-- .../impl/UserLikeGroupServiceImpl.java | 60 +- .../da/service/impl/WorkspaceServiceImpl.java | 58 +- .../resources/aida-461108-b4afaabebb84.json | 13 + .../mapper/primary/UserLikeGroupMapper.xml | 16 +- 13 files changed, 1147 insertions(+), 217 deletions(-) create mode 100644 src/main/java/com/ai/da/common/constant/ModelConstants.java create mode 100644 src/main/resources/aida-461108-b4afaabebb84.json diff --git a/pom.xml b/pom.xml index b004143b..cd9bc433 100644 --- a/pom.xml +++ b/pom.xml @@ -400,6 +400,19 @@ ${javacpp.platform.linux-x86_64} + + com.volcengine + volcengine-java-sdk-ark-runtime + LATEST + + + + + com.google.auth + google-auth-library-oauth2-http + LATEST + + diff --git a/src/main/java/com/ai/da/common/constant/ModelConstants.java b/src/main/java/com/ai/da/common/constant/ModelConstants.java new file mode 100644 index 00000000..136abcbe --- /dev/null +++ b/src/main/java/com/ai/da/common/constant/ModelConstants.java @@ -0,0 +1,42 @@ +package com.ai.da.common.constant; + +/** + * 模型相关常量类 + * 用于存放 chooseModelAndPrompt 方法中的字符串常量 + */ +public class ModelConstants { + + // 类型常量 + public static final String PRINTBOARD = "Printboard"; + public static final String MOODBOARD = "Moodboard"; + public static final String SKETCHBOARD = "Sketchboard"; + + // 模型级别常量 + public static final String ADVANCED = "advanced"; + public static final String HIGH = "high"; + public static final String NORMAL = "normal"; + + // 模型名称常量 + public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image"; + public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415"; + public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415"; + public static final String PRINTBOARD_HIGH_I2I = "doubao-seededit-3-0-i2i-250628"; + public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828"; + public static final String IMAGEN_MODEL = "imagen-4.0-generate-001"; + public static final String NANO_BANANA = "gemini-2.5-flash-image-preview"; + public static final String LOCAL_MODEL = "local"; + + // 风格常量 + public static final String PAINTING_STYLE = "Painting Style"; + public static final String ILLUSTRATION_STYLE = "Illustration Style"; + public static final String REAL_STYLE = "Real Style"; + + // 映射键 + public static final String PROMPT = "prompt"; + public static final String USE_MODEL = "UseModel"; + + // 防止实例化 + private ModelConstants() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } +} \ No newline at end of file diff --git a/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java b/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java index 1635d492..7fdf9cea 100644 --- a/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java +++ b/src/main/java/com/ai/da/common/enums/CreditsEventsEnum.java @@ -41,7 +41,7 @@ public enum CreditsEventsEnum { MOOD_BOARD("MoodBoard","5"), SKETCH_BOARD("SketchBoard","5"), TO_PRODUCT_IMAGE("ToProductImage","5"), - TO_PRODUCT_IMAGE_FLUX("ToProductImageFlux","10"), + TO_PRODUCT_IMAGE_ADVANCED("ToProductImageAdvanced","10"), RELIGHT("Relight","5"), RELIGHT_FLUX("RelightFlux","10"), QUESTIONNAIRE("Questionnaire","100"), @@ -51,6 +51,8 @@ public enum CreditsEventsEnum { OTHER("Other","5"), WX_TEXT2IMG("WX_Text2Image", "5"), + QWEN_TEXT2IMG("QWEN_Text2Image", "5"), + DOUBAO_TEXT2IMG("Doubao_Text2Image", "5"), WX_ANIMATION("WX_Animation", "30"), FREEPIK_IMG2IMG("Freepik_img2img", "20"), FLUX_IMG2IMG("Flux_img2img","10"), diff --git a/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java b/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java index 437ba163..8b233808 100644 --- a/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java +++ b/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java @@ -52,6 +52,12 @@ public class CollectionElement implements Serializable { */ private String level2Type; + /** + * 三级类型 目前只为线稿标注性别 + */ + @TableField("level3_type") + private String level3Type; + /** * 元素名(如果是颜色board 存潘通id+潘通名字 形式 ""11_mds") */ diff --git a/src/main/java/com/ai/da/service/GenerateService.java b/src/main/java/com/ai/da/service/GenerateService.java index 587f4592..da3c09bb 100644 --- a/src/main/java/com/ai/da/service/GenerateService.java +++ b/src/main/java/com/ai/da/service/GenerateService.java @@ -96,4 +96,6 @@ public interface GenerateService extends IService { String getFluxResult(String taskId, String objectName); byte[] downloadVideoOrImage(String url); + + String createGoogleAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt); } diff --git a/src/main/java/com/ai/da/service/WorkspaceService.java b/src/main/java/com/ai/da/service/WorkspaceService.java index a43947cd..5c4dedfe 100644 --- a/src/main/java/com/ai/da/service/WorkspaceService.java +++ b/src/main/java/com/ai/da/service/WorkspaceService.java @@ -63,4 +63,6 @@ public interface WorkspaceService extends IService { SaveOrUpdateProjectVO saveOrUpdateProject(ProjectDTO projectDTO); Long getByProjectId(Long projectId); + + String getProjectSexById(Long projectId); } diff --git a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java index c4b1ba51..074ce39d 100644 --- a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java @@ -23,6 +23,7 @@ import com.ai.da.python.vo.ImageSegmentation; import com.ai.da.service.*; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -30,6 +31,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Lists; import com.google.gson.Gson; import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; @@ -55,6 +57,7 @@ import java.util.stream.Collectors; */ @Slf4j @Service +@RequiredArgsConstructor public class CollectionElementServiceImpl extends ServiceImpl implements CollectionElementService { @Resource private CollectionElementMapper collectionElementMapper; @@ -79,6 +82,8 @@ public class CollectionElementServiceImpl extends ServiceImpl printPath = Arrays.asList(url1, url2); //调取python 接口 - String generateUrl = pythonService.generatePrint(printPath,userId); + String generateUrl = pythonService.generatePrint(printPath, userId); if (StringUtils.isEmpty(generateUrl)) { throw new BusinessException("generate.interface.exception"); } @@ -367,7 +375,7 @@ public class CollectionElementServiceImpl extends ServiceImpl + * 功能说明: + * - 验证设计集合DTO中的各种元素(moodBoards、printBoards、sketchBoards等) + * - 处理渐变色彩板的base64图片上传到MinIO + * - 校验PIN数量限制(上衣、下衣、外套各不超过8个) + * - 验证模特信息和模板点位 + * - 转换并整合来自library和generate的元素数据 + * + * @param designDTO 设计集合DTO,包含所有设计元素信息 + * @return ValidateElementVO 验证后的元素VO对象,包含处理后的所有元素数据 + * @throws BusinessException 当数据验证失败时抛出业务异常 + */ @Override public ValidateElementVO validateElement(DesignCollectionDTO designDTO) { + // 将设计DTO转换为验证元素VO对象 ValidateElementVO elementVO = CopyUtil.copyObject(designDTO, ValidateElementVO.class); + + // 处理颜色板中的渐变色彩 List colorBoards = elementVO.getColorBoards(); for (CollectionColorDTO colorBoard : colorBoards) { if (Objects.nonNull(colorBoard.getGradient())) { + // 解析base64格式的渐变图片数据 String colorImg = colorBoard.getGradient().getColorImg(); String[] parts = colorImg.split(","); - String imageType = parts[0].split("/")[1].split(";")[0]; - String base64Data = parts[1]; + String imageType = parts[0].split("/")[1].split(";")[0]; // 提取图片类型 + String base64Data = parts[1]; // 提取base64数据 + + // 将base64图片上传到MinIO存储 String gradientMinioUrl = minioUtil.uploadImageFromBase64(gradientBucketName, base64Data, imageType); colorBoard.setGradientMinioUrl(gradientMinioUrl); - colorBoard.getGradient().setColorImg(null); - colorBoard.setGradientString(JSON.toJSONString(colorBoard.getGradient())); + colorBoard.getGradient().setColorImg(null); // 清空原始base64数据 + colorBoard.setGradientString(JSON.toJSONString(colorBoard.getGradient())); // 保存渐变配置JSON } } elementVO.setColorBoards(colorBoards); - List usedElementIds = elementVO.getUsedElementIds(); - List libraryCollectionElements = elementVO.getLibraryCollectionElements(); - List generateCollectionElements = elementVO.getGenerateCollectionElements(); - //校验moodboard + + // 初始化用于收集各类元素的集合 + List usedElementIds = elementVO.getUsedElementIds(); // 已使用的元素ID列表 + List libraryCollectionElements = elementVO.getLibraryCollectionElements(); // 来自library的元素 + List generateCollectionElements = elementVO.getGenerateCollectionElements(); // 来自generate的元素 + // ========== 校验和处理moodBoards(情绪板) ========== if (CollectionUtil.isNotEmpty(designDTO.getMoodBoards())) { - //校验designType + // 校验designType字段是否有效 validateDesignType(designDTO.getMoodBoards(), "moodBoards"); + + // 提取类型为COLLECTION的moodBoard ID列表 List moodBoardIds = designDTO.getMoodBoards().stream() .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())) .map(DesignCollectionElementDTO::getId) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(moodBoardIds)) { + // 从数据库批量查询moodBoard元素 List MoodBoardElements = collectionElementMapper.selectBatchIds(moodBoardIds); + // 验证查询结果的完整性 if (CollectionUtil.isEmpty(MoodBoardElements) || MoodBoardElements.size() != moodBoardIds.size()) { throw new BusinessException("get.moodBoards.data.is.mismatch"); } elementVO.setMoodBoardElements(MoodBoardElements); - usedElementIds.addAll(moodBoardIds); + usedElementIds.addAll(moodBoardIds); // 记录已使用的元素ID } - //library + // 处理类型为LIBRARY的moodBoard元素 List libraryIds = designDTO.getMoodBoards().stream() .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName())) .map(DesignCollectionElementDTO::getId) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(libraryIds)) { List librarys = libraryService.getByIds(libraryIds); - //不校验了防止用户在library删除 对应不上 + // 不严格校验,防止用户在library中删除元素导致对应不上 if (CollectionUtil.isNotEmpty(librarys)) { libraryCollectionElements.addAll(covertLibrarysToCollections(librarys, null)); } } - // generate + + // 处理类型为GENERATE的moodBoard元素 List generateIds = designDTO.getMoodBoards().stream() .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName()))) .map(DesignCollectionElementDTO::getId) @@ -452,38 +487,45 @@ public class CollectionElementServiceImpl extends ServiceImpl printBoardIds = designDTO.getPrintBoards().stream() .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())) .map(DesignCollectionPrintElementDTO::getId) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(printBoardIds)) { - //校验printboard + // 从数据库批量查询printBoard元素 List printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds); + // 验证查询结果的完整性 if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) { throw new BusinessException("get.printBoards.data.is.mismatch"); } elementVO.setPrintBoardElements(printBoardElements); - usedElementIds.addAll(printBoardIds); + usedElementIds.addAll(printBoardIds); // 记录已使用的元素ID } - //library + // 处理类型为LIBRARY的printBoard元素 List libraryIds = designDTO.getPrintBoards().stream() .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName())) .map(DesignCollectionPrintElementDTO::getId) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(libraryIds)) { List librarys = libraryService.getByIds(libraryIds); - //不校验了防止用户在library删除 对应不上 + // 不严格校验,防止用户在library中删除元素导致对应不上 if (CollectionUtil.isNotEmpty(librarys)) { + // 创建ID到printBoard元素的映射,用于保持元素属性 Map idToMap = designDTO.getPrintBoards() .stream() .collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v)); libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap)); } } - // generate + + // 处理类型为GENERATE的printBoard元素 List generateIds = designDTO.getPrintBoards().stream() .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName()))) .map(DesignCollectionPrintElementDTO::getId) @@ -491,6 +533,7 @@ public class CollectionElementServiceImpl extends ServiceImpl generateDetailList = generateDetailMapper.selectBatchIds(generateIds); if (CollectionUtil.isNotEmpty(generateDetailList)) { + // 创建ID到printBoard元素的映射,用于保持元素属性 Map idToMap = designDTO.getPrintBoards() .stream() .collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v)); @@ -498,80 +541,113 @@ public class CollectionElementServiceImpl extends ServiceImpl skecth.getIsPin() == 1 - && DesignPythonItem.DRESS_BLOUSE.contains(skecth.getLevel2Type())).count(); - bottomNum = designDTO.getSketchBoards().stream() - .filter(skecth -> skecth.getIsPin() == 1 - && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count(); - }else if (designDTO.getModelSex().equals(Sex.MALE.getValue())) { - topNum= designDTO.getSketchBoards().stream() - .filter(skecth -> skecth.getIsPin() == 1 - && DesignPythonItem.TOPS.contains(skecth.getLevel2Type())).count(); - bottomNum = designDTO.getSketchBoards().stream() - .filter(skecth -> skecth.getIsPin() == 1 - && DesignPythonItem.BOTTOMS.contains(skecth.getLevel2Type())).count(); - } - outerwearNum = designDTO.getSketchBoards().stream() - .filter(skecth -> skecth.getIsPin() == 1 - && DesignPythonItem.OUTWEAR.contains(skecth.getLevel2Type())).count(); - if (topNum > 8 || bottomNum > 8 || outerwearNum > 8) { - throw new BusinessException("the.number.of.PIN.top.or.bottom.or.outerwear.sketchBoard.cannot.be.more.than.8", ResultEnum.PROMPT.getCode()); - } - //校验designType - Boolean result = designDTO.getSketchBoards().stream() - .filter(mood -> StringUtils.isEmpty(mood.getDesignType())) - .findFirst().isPresent(); - if (result) { - throw new BusinessException("sketchBoards.designType.cannot.be.empty"); + //去掉与性别不符合的线稿元素 - 使用 Iterator 安全移除元素 + Iterator sketchIterator = designDTO.getSketchBoards().iterator(); + while (sketchIterator.hasNext()) { + CollectionSketchDTO sketchBoard = sketchIterator.next(); + String level2Type = sketchBoard.getLevel2Type(); + //判断性别和当前project性别是否一致,不一致则移除 + String level3Type = collectionElementMapper.selectOne(new LambdaQueryWrapper().eq(CollectionElement::getId, sketchBoard.getSketchBoardId()).select(CollectionElement::getLevel3Type)).getLevel3Type(); + if (!level3Type.equalsIgnoreCase(designDTO.getModelSex())) { + sketchIterator.remove(); + } } + //再次判断草图板数量,如果为null,则跳出当前if + if (CollectionUtil.isNotEmpty(designDTO.getSketchBoards())) { - List sketchBoardIds = designDTO.getSketchBoards().stream() - .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())) - .map(CollectionSketchDTO::getSketchBoardId) - .collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(sketchBoardIds)) { - //校验sketchBoard - List sketchBoardElements = collectionElementMapper.selectBatchIds(sketchBoardIds); - if (CollectionUtil.isEmpty(sketchBoardElements) || sketchBoardElements.size() != sketchBoardIds.size()) { - throw new BusinessException("get.sketchBoards.data.is.mismatch"); + // 根据模特性别统计不同类型的PIN数量 + if (designDTO.getModelSex().equals(Sex.FEMALE.getValue())) { + // 女性模特:统计连衣裙和上衣类PIN数量 + topNum = designDTO.getSketchBoards().stream() + .filter(skecth -> skecth.getIsPin() == 1 + && DesignPythonItem.DRESS_BLOUSE.contains(skecth.getLevel2Type())).count(); + // 女性模特:统计裙子和裤子类PIN数量 + bottomNum = designDTO.getSketchBoards().stream() + .filter(skecth -> skecth.getIsPin() == 1 + && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count(); + } else if (designDTO.getModelSex().equals(Sex.MALE.getValue())) { + // 男性模特:统计上衣类PIN数量 + topNum = designDTO.getSketchBoards().stream() + .filter(skecth -> skecth.getIsPin() == 1 + && DesignPythonItem.TOPS.contains(skecth.getLevel2Type())).count(); + // 男性模特:统计下装类PIN数量 + bottomNum = designDTO.getSketchBoards().stream() + .filter(skecth -> skecth.getIsPin() == 1 + && DesignPythonItem.BOTTOMS.contains(skecth.getLevel2Type())).count(); } - elementVO.setSketchBoardElements(sketchBoardElements); - usedElementIds.addAll(sketchBoardIds); - } - //library - List libraryIds = designDTO.getSketchBoards().stream() - .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName())) - .map(CollectionSketchDTO::getSketchBoardId) - .collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(libraryIds)) { - List librarys = libraryService.getByIds(libraryIds); - //不校验了防止用户在library删除 对应不上 - if (CollectionUtil.isNotEmpty(librarys)) { - Map idToMap = designDTO.getSketchBoards() - .stream() - .collect(Collectors.toMap(CollectionSketchDTO::getSketchBoardId, v -> v)); - libraryCollectionElements.addAll(covertLibrarysToCollections(librarys, idToMap)); + // 统计外套类PIN数量(男女通用) + outerwearNum = designDTO.getSketchBoards().stream() + .filter(skecth -> skecth.getIsPin() == 1 + && DesignPythonItem.OUTWEAR.contains(skecth.getLevel2Type())).count(); + + // 验证PIN数量限制:每个类别不能超过8个 + if (topNum > 8 || bottomNum > 8 || outerwearNum > 8) { + throw new BusinessException("the.number.of.PIN.top.or.bottom.or.outerwear.sketchBoard.cannot.be.more.than.8", ResultEnum.PROMPT.getCode()); } - } - // generate - List generateIds = designDTO.getSketchBoards().stream() - .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName()))) - .map(CollectionSketchDTO::getSketchBoardId) - .collect((Collectors.toList())); - if (CollectionUtil.isNotEmpty(generateIds)) { - List generateDetailList = generateDetailMapper.selectBatchIds(generateIds); - if (CollectionUtil.isNotEmpty(generateDetailList)) { - Map idToMap = designDTO.getSketchBoards() - .stream() - .collect(Collectors.toMap(CollectionSketchDTO::getSketchBoardId, v -> v)); - generateCollectionElements.addAll(covertGeneratesToCollections(generateDetailList, idToMap)); + + // 校验designType字段不能为空 + Boolean result = designDTO.getSketchBoards().stream() + .filter(mood -> StringUtils.isEmpty(mood.getDesignType())) + .findFirst().isPresent(); + if (result) { + throw new BusinessException("sketchBoards.designType.cannot.be.empty"); + } + + // 提取类型为COLLECTION的sketchBoard ID列表 + List sketchBoardIds = designDTO.getSketchBoards().stream() + .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())) + .map(CollectionSketchDTO::getSketchBoardId) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(sketchBoardIds)) { + // 从数据库查询验证sketchBoard元素是否存在 + List sketchBoardElements = collectionElementMapper.selectBatchIds(sketchBoardIds); + if (CollectionUtil.isEmpty(sketchBoardElements) || sketchBoardElements.size() != sketchBoardIds.size()) { + throw new BusinessException("get.sketchBoards.data.is.mismatch"); + } + // 设置验证通过的sketchBoard元素到结果对象 + elementVO.setSketchBoardElements(sketchBoardElements); + // 记录已使用的元素ID + usedElementIds.addAll(sketchBoardIds); + } + + // 处理类型为LIBRARY的sketchBoard元素 + List libraryIds = designDTO.getSketchBoards().stream() + .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName())) + .map(CollectionSketchDTO::getSketchBoardId) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(libraryIds)) { + List librarys = libraryService.getByIds(libraryIds); + // 不严格校验,防止用户在library中删除元素导致对应不上 + if (CollectionUtil.isNotEmpty(librarys)) { + // 创建ID到sketchBoard元素的映射,用于保持元素属性 + Map idToMap = designDTO.getSketchBoards() + .stream() + .collect(Collectors.toMap(CollectionSketchDTO::getSketchBoardId, v -> v)); + libraryCollectionElements.addAll(covertLibrarysToCollections(librarys, idToMap)); + } + } + + // 处理类型为GENERATE的sketchBoard元素 + List generateIds = designDTO.getSketchBoards().stream() + .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName()))) + .map(CollectionSketchDTO::getSketchBoardId) + .collect((Collectors.toList())); + if (CollectionUtil.isNotEmpty(generateIds)) { + List generateDetailList = generateDetailMapper.selectBatchIds(generateIds); + if (CollectionUtil.isNotEmpty(generateDetailList)) { + // 创建ID到sketchBoard元素的映射,用于保持元素属性 + Map idToMap = designDTO.getSketchBoards() + .stream() + .collect(Collectors.toMap(CollectionSketchDTO::getSketchBoardId, v -> v)); + generateCollectionElements.addAll(covertGeneratesToCollections(generateDetailList, idToMap)); + } } } } @@ -604,13 +680,17 @@ public class CollectionElementServiceImpl extends ServiceImpl {}", designDTO); throw new BusinessException("unknown.parameter.singleOverall"); } + + // 如果是单品生成模式,需要验证switchCategory参数 if (SingleOverallEnum.SINGLE.equals(singleOverall)) { SwitchCategoryEnum switchCategory = SwitchCategoryEnum.of(designDTO.getSwitchCategory()); if (Objects.isNull(switchCategory)) { @@ -619,36 +699,57 @@ public class CollectionElementServiceImpl extends ServiceImpl designLibraryModelPointVOList = new ArrayList<>(); - for (MannequinDTO mannequin : designDTO.getMannequins()) { + Iterator mannequinIterator = designDTO.getMannequins().iterator(); + while (mannequinIterator.hasNext()) { + MannequinDTO mannequin = mannequinIterator.next(); if (mannequin.getType().equals("System")) { + // 处理系统模特 SysFileVO byId = sysFileService.getById(mannequin.getId()); - if (Objects.isNull(byId)){ + if (Objects.isNull(byId)) { log.info("未知模特:{}:{}", mannequin.getId(), "System"); throw new BusinessException("model.not.found"); } + if (!designDTO.getModelSex().equalsIgnoreCase(byId.getLevel2Type())) { + mannequinIterator.remove(); + continue; + } + // 注释:女性模特的风格设置(已禁用) // if (!StringUtils.isEmpty(byId.getLevel3Type()) && byId.getLevel2Type().equals("Female")) { // elementVO.setStyle(byId.getLevel3Type()); // } + // 获取模特点位信息并计算模板点位 LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), mannequin.getType()); designLibraryModelPointVOList.add(calculateTemplatePointTemplate(modelPoint, 700, 320, byId.getUrl())); - }else { + } else { + // 处理用户自定义模特 Library byId = libraryService.getById(mannequin.getId()); + //如果当前模特性别和project模特性别不一致,则为此模特进行design + if (!designDTO.getModelSex().equalsIgnoreCase(byId.getLevel2Type())) { + mannequinIterator.remove(); + continue; + } LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), mannequin.getType()); designLibraryModelPointVOList.add(calculateTemplatePointTemplate(modelPoint, byId.getHigh(), byId.getWidth(), byId.getUrl())); } } elementVO.setMannequins(designLibraryModelPointVOList); - }else { + } else { + // 处理单个模特的情况(通过templateId指定) if (!StringUtils.isEmpty(designDTO.getModelType())) { if (designDTO.getModelType().equals(ModelType.LIBRARY.getValue())) { + // 处理用户自定义模特 Library byId = libraryService.getById(designDTO.getTemplateId()); LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), designDTO.getModelType()); elementVO.setDesignLibraryModelPoint(calculateTemplatePointTemplate(modelPoint, byId.getHigh(), byId.getWidth(), byId.getUrl())); } else if (designDTO.getModelType().equals(ModelType.SYSTEM.getValue())) { + // 处理系统模特 SysFileVO byId = sysFileService.getById(designDTO.getTemplateId()); + // 设置女性模特的风格类型 if (Objects.nonNull(byId) && !StringUtils.isEmpty(byId.getLevel3Type()) && byId.getLevel2Type().equals("Female")) { elementVO.setStyle(byId.getLevel3Type()); } @@ -657,17 +758,25 @@ public class CollectionElementServiceImpl extends ServiceImpl sketchBoardElements = collectionElementMapper.selectBatchIds(sketchBoardIds); elementVO.setSketchBoardElements(sketchBoardElements); - }else { + } else { elementVO.setSketchBoardElements(new ArrayList<>()); } } @@ -804,7 +913,7 @@ public class CollectionElementServiceImpl extends ServiceImpl printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds); elementVO.setPrintBoardElements(printBoardElements); - }else { + } else { elementVO.setPrintBoardElements(new ArrayList<>()); } } @@ -858,8 +967,8 @@ public class CollectionElementServiceImpl extends ServiceImpl relations = list.stream().map(element -> - TCollectionElementRelation.builder().elementId(element.getId()) - .collectionId(element.getCollectionId()).createDate(new Date()).build()) + TCollectionElementRelation.builder().elementId(element.getId()) + .collectionId(element.getCollectionId()).createDate(new Date()).build()) .collect(Collectors.toList()); tCollectionElementRelationService.saveBatch(relations); pageQuery.setPage(pageQuery.getPage() + 1); @@ -936,8 +1045,8 @@ public class CollectionElementServiceImpl extends ServiceImpl> libraryIds = generateDetailMapper.getLibraryIdThroughMD5(md5, CollectionLevel1TypeEnum.PRINT_BOARD.getRealName()); - if (libraryIds.isEmpty()){ + if (libraryIds.isEmpty()) { generateDetail.setIsLike((byte) 0); - }else { + } else { generateDetail.setIsLike((byte) 1); generateDetail.setLibraryId(libraryIds.get(0).get("library_id")); } @@ -1087,14 +1196,14 @@ public class CollectionElementServiceImpl extends ServiceImpl impleme Long designId = saveOne(designDTO, collectionId, userInfo.getId()); //计算library // calculateLibraryAndSysFile(designDTO, elementVO, userInfo); - //组装design入参 + + // ========== AI设计生成阶段 ========== + + // 组装design入参 - 将业务参数转换为Python AI服务所需的格式 long startTime = System.currentTimeMillis(); DesignPythonObjects pythonObjects = pythonService.covertDesignParam(designDTO.getSystemScale(), designDTO.getSingleOverall(), designDTO.getSwitchCategory(), elementVO, designDTO.getProcessId(), collectionIdParam); long endTime = System.currentTimeMillis(); long totalTimeInSeconds = (endTime - startTime) / 1000; log.info("组装入参运行时间:" + totalTimeInSeconds + " 秒"); - // pythonObjects增加image_id关联 + + // pythonObjects增加image_id关联 - 建立设计元素与图片的关联关系 startTime = System.currentTimeMillis(); relationImageIds(pythonObjects); endTime = System.currentTimeMillis(); totalTimeInSeconds = (endTime - startTime) / 1000; log.info("增加image_id关联运行时间:" + totalTimeInSeconds + " 秒"); - //design + + // design - 调用Python AI服务进行设计生成 startTime = System.currentTimeMillis(); String requestId = UUID.randomUUID().toString(); pythonObjects.setRequestId(requestId); @@ -383,10 +388,13 @@ public class DesignServiceImpl extends ServiceImpl impleme endTime = System.currentTimeMillis(); totalTimeInSeconds = (endTime - startTime) / 1000; log.info("design python端运行时间:" + totalTimeInSeconds + " 秒"); - //生成library + // ========== 后处理阶段 ========== + + // 生成library - 根据设计元素生成设计库信息 startTime = System.currentTimeMillis(); generateLibrary(elementVO, designDTO.getTimeZone()); - //处理关联关系,修复element覆盖得情况 + + // 处理关联关系,修复element覆盖的情况(已注释) // List relationElements = collectionElementService.getByOnlyCollectionId(collectionId); // List relationElementIds = relationElements.stream().map(CollectionElement::getId).collect(Collectors.toList()); // handleCollectionElementRelation(collectionId, null != collectionIdParam, relationElementIds); @@ -394,7 +402,7 @@ public class DesignServiceImpl extends ServiceImpl impleme totalTimeInSeconds = (endTime - startTime) / 1000; log.info("处理关联关系运行时间:" + totalTimeInSeconds + " 秒"); - + // 构建设计上下文信息 - 保存设计过程中的关键参数和状态 Map context = new HashMap<>(); context.put("pythonObjects", pythonObjects); // 转换后的 Python 请求参数 context.put("designId", designId); // 设计 ID @@ -404,11 +412,14 @@ public class DesignServiceImpl extends ServiceImpl impleme context.put("singleOverall", designDTO.getSingleOverall()); // 其他设计参数 context.put("requestIdList", elementVO.getRequestIdList()); - // 将上下文存入全局设计上下文中 + // 将上下文存入全局设计上下文中 - 用于后续异步查询和处理 designContext.put(requestId, context); - //保存python返回信息;保存designItem和detail + // 保存python返回信息;保存designItem和detail(注释说明) + // 注意:实际的设计结果保存是通过异步回调方式处理的,这里只是准备阶段 // return savePythonDesignItemAndDetail(pythonObjects, designId, collectionId, userInfo, designDTO.getTimeZone(), responseJSONObject, designDTO.getSingleOverall()); + + // 返回请求ID - 客户端可以使用此ID查询设计进度和结果 return requestId; } @@ -2904,6 +2915,7 @@ public class DesignServiceImpl extends ServiceImpl impleme collectionElement.setAccountId(project.getAccountId()); collectionElement.setProjectId(project.getId()); collectionElement.setLevel2Type(receiveCollectionElement.getLevel2Type()); + collectionElement.setLevel3Type(workspaceService.getProjectSexById(project.getId())); collectionElement.setName(split1[0]); collectionElement.setUrl(url); collectionElement.setHasPin((byte) 0); 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 95d3cadc..291d19c8 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -8,6 +8,7 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.ai.da.common.config.exception.BusinessException; import com.ai.da.common.constant.CommonConstant; +import com.ai.da.common.constant.ModelConstants; import com.ai.da.common.context.UserContext; import com.ai.da.common.enums.*; import com.ai.da.common.response.ResultEnum; @@ -28,16 +29,22 @@ import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.utils.JsonUtils; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.serializer.SerializerFeature; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.auth.oauth2.GoogleCredentials; import com.google.gson.Gson; +import com.volcengine.ark.runtime.model.images.generation.GenerateImagesRequest; +import com.volcengine.ark.runtime.model.images.generation.ImagesResponse; +import com.volcengine.ark.runtime.service.ArkService; import io.minio.errors.MinioException; import io.netty.util.internal.StringUtil; import lombok.extern.slf4j.Slf4j; +import okhttp3.*; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; @@ -70,7 +77,7 @@ import java.util.regex.Pattern; import static com.ai.da.common.enums.CollectionLevel1TypeEnum.*; import static com.ai.da.common.enums.CreditsEventsEnum.PATTERN; import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE; -import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE_FLUX; +import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE_ADVANCED; import static com.ai.da.common.enums.WangXiangTaskStatusEnum.FAILED; import static com.ai.da.common.enums.WangXiangTaskStatusEnum.UNKNOWN_W; @@ -159,6 +166,10 @@ public class GenerateServiceImpl extends ServiceImpl i @Value("${ALIYUN_API_KEY}") private String ALIYUN_API_KEY; + @Value("${DOUBAO_API_KEY}") + private String DOUBAO_API_KEY; + + @Value("${FREEPIK_API_KEY}") private String FREEPIK_API_KEY; @@ -278,15 +289,15 @@ public class GenerateServiceImpl extends ServiceImpl i } - public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO){ - if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())){ - if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())){ + public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) { + if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) { + if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { return GenerateModeEnum.TEXT_IMAGE; - }else { + } else { return GenerateModeEnum.TEXT; } - }else { - if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())){ + } else { + if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { return GenerateModeEnum.IMAGE; } } @@ -330,7 +341,7 @@ public class GenerateServiceImpl extends ServiceImpl i String key = generateResultKey + ":" + taskId; String imageName = url.substring(url.lastIndexOf("/") + 1); String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; - if (StringUtil.isNullOrEmpty(category)){ + if (StringUtil.isNullOrEmpty(category)) { Generate generateRecord = selectByUniqueId(taskId); category = generateRecord.getLevel2Type(); } @@ -382,13 +393,13 @@ public class GenerateServiceImpl extends ServiceImpl i } } - public void updateToProductTaskStatus(String taskId, String status){ + public void updateToProductTaskStatus(String taskId, String status) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda().eq(ToProductImageResult::getTaskId, taskId); List toProductImageResults = toProductImageResultMapper.selectList(qw); if (!CollectionUtils.isEmpty(toProductImageResults)) { ToProductImageResult toProductImageResult = toProductImageResults.get(0); - if (StringUtil.isNullOrEmpty(toProductImageResult.getStatus()) || !toProductImageResult.getStatus().equals(status)){ + if (StringUtil.isNullOrEmpty(toProductImageResult.getStatus()) || !toProductImageResult.getStatus().equals(status)) { toProductImageResult.setStatus(status); toProductImageResultMapper.updateById(toProductImageResult); } @@ -602,11 +613,354 @@ public class GenerateServiceImpl extends ServiceImpl i return handleFluxPatternGeneration(generateDTO); } - // 3、处理标准生成流程 + String modelName = generateDTO.getModelName(); + + HashMap modelAndPromptMap = chooseModelAndPrompt(generateDTO, modelName); + String useModel = modelAndPromptMap.get(ModelConstants.USE_MODEL); + String prompt = modelAndPromptMap.get(ModelConstants.PROMPT); + + if (useModel != null) { + if (useModel.contains("doubao")) { + return handleDouBaoModelGeneration(generateDTO, useModel, prompt); + } else if (useModel.contains("qwen")) { + return handleQwenModelGeneration(generateDTO, useModel, prompt); + } else if (useModel.contains("imagen")) { + return handleImagenModelGeneration(generateDTO, useModel, prompt); + } else if (useModel.contains("gemini")) { + return handleNanoBananaModelGeneration(generateDTO, useModel, prompt); + } else if (ModelConstants.LOCAL_MODEL.equals(useModel)) { + return handleStandardGeneration(generateDTO); + } + } + return handleStandardGeneration(generateDTO); + } -// ============== 以下是辅助方法 ============== + + // ============== 以下是辅助方法 ============== + private PrepareForGenerateVO handleImagenModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + // 校验积分是否足够 +// validateCredits(CreditsEventsEnum.WX_TEXT2IMG); + // 创建生成任务 + String taskId = null; + try { + taskId = createGoogleAsyncTask(generateDTO, useModel, prompt); + } catch (Exception e) { + e.printStackTrace(); + } +// processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.WX_TEXT2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + private PrepareForGenerateVO handleNanoBananaModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + // 校验积分是否足够 +// validateCredits(CreditsEventsEnum.WX_TEXT2IMG); + // 创建生成任务 + String taskId = null; + try { + taskId = createGoogleAsyncTask(generateDTO, useModel, prompt); + } catch (Exception e) { + e.printStackTrace(); + } +// processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.WX_TEXT2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + public String createGoogleAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + // 从 resources 加载 JSON 文件 + System.setProperty("https.proxyHost", "127.0.0.1"); + System.setProperty("https.proxyPort", "10809"); + String level1Type = generateDTO.getLevel1Type(); + String level2Type = generateDTO.getLevel2Type(); + Long userId = generateDTO.getUserId(); + String gender = generateDTO.getGender(); + + + boolean isUseImage; + String resultPath; + String uuid = UUID.randomUUID().toString(); + if (Objects.isNull(generateDTO.getCollectionElementId()) + || StringUtil.isNullOrEmpty(generateDTO.getDesignType())) { + isUseImage = false; + } else { + isUseImage = true; + } + String model; + String finalImagePath = null; + if (isUseImage) { + String imagePath = geti2iImagePath(generateDTO); + + // 只有当imagePath不为null时才获取图片的base64编码 + if (imagePath != null) { + try { + finalImagePath = minioUtil.getImageAsBase64(imagePath); + } catch (IOException e) { + log.error("获取图片base64编码失败", e); + e.printStackTrace(); + } + } + resultPath = userId + "/imageToSketch" + "/" + uuid; + model = ModelConstants.NANO_BANANA; + } else { + resultPath = userId + "/sketch" + "/" + uuid; + model = ModelConstants.IMAGEN_MODEL; + } + // 拼接 endpoint + String projectId = "aida-461108"; + String location; + String endpoint; + + + JSONObject instanceObj = new JSONObject(); + JSONObject parametersObj = new JSONObject(); + JSONObject requestBody = new JSONObject(); + + if (isUseImage && finalImagePath != null) { + location = "global"; + endpoint = String.format( + "https://aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:generateContent", + projectId, location, model + ); + + // 使用 gemini-2.5-flash-image-preview 模型时的请求体格式 + // 创建图片部分 + JSONObject imagePart = new JSONObject(); + JSONObject inlineData = new JSONObject(); + inlineData.set("mimeType", "image/png"); + inlineData.set("data", finalImagePath); + imagePart.set("inlineData", inlineData); + + // 创建文本部分 + JSONObject textPart = new JSONObject(); + textPart.set("text", prompt); + + // 创建内容对象 + JSONObject content = new JSONObject(); + content.set("role", "user"); + content.set("parts", Arrays.asList(imagePart, textPart)); + + // 设置 contents 数组 + requestBody.set("contents", Arrays.asList(content)); + + // 设置 generationConfig + JSONObject generationConfig = new JSONObject(); +// generationConfig.set("temperature", 1); + generationConfig.set("maxOutputTokens", 8192); + generationConfig.set("responseModalities", Arrays.asList("TEXT", "IMAGE")); +// generationConfig.set("topP", 0.95); + requestBody.set("generationConfig", generationConfig); + + + } else { + location = "us-central1"; + endpoint = String.format( + "https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:predict", + location, projectId, location, model + ); + // 使用 imagen 模型时的请求体格式 + instanceObj.set("prompt", prompt); + + // imagen 模型的参数 + parametersObj.set("addWatermark", false); + parametersObj.set("sampleCount", 1); + + // 构建完整的请求体 + requestBody.set("instances", Arrays.asList(instanceObj)); + requestBody.set("parameters", parametersObj); + } + + String jsonBody = requestBody.toString(); + + log.info("Google 请求入参:{}", jsonBody); + + // 生成唯一的任务ID + String taskId = uuid + "-" + userId; + + // 初始化Redis中的任务状态为"Executing" + String key = generateResultKey + ":" + taskId; + GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Executing"); + redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 异步处理token获取和API调用 + CompletableFuture.runAsync(() -> { + try { + // 异步获取token + String tokenValue = null; + try (InputStream inputStream = GenerateServiceImpl.class.getClassLoader() + .getResourceAsStream("aida-461108-b4afaabebb84.json")) { + + GoogleCredentials credentials = GoogleCredentials + .fromStream(inputStream) + .createScoped(Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")); + + credentials.refreshIfExpired(); + tokenValue = credentials.getAccessToken().getTokenValue(); + } + if (tokenValue == null) { + throw new RuntimeException("google token error"); + } + + // 异步发送API请求 + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) // 连接超时时间 + .readTimeout(60, TimeUnit.SECONDS) // 读取超时时间 + .writeTimeout(60, TimeUnit.SECONDS) // 写入超时时间 + .build(); + Request request = new Request.Builder() + .url(endpoint) + .addHeader("Authorization", "Bearer " + tokenValue) + .addHeader("Content-Type", "application/json") + .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) + .build(); + + try (Response response = client.newCall(request).execute()) { + String result = response.body().string(); + + log.info("Google 响应结果:{}", result); + com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); + + String base64Data = null; + + //根据模型类别按照api取出结果 + if (ModelConstants.NANO_BANANA.equals(useModel)) { + JSONArray candidates = jsonResponse.getJSONArray("candidates"); + + if (candidates != null && !candidates.isEmpty()) { + com.alibaba.fastjson.JSONObject candidate = candidates.getJSONObject(0); + com.alibaba.fastjson.JSONObject content = candidate.getJSONObject("content"); + JSONArray parts = content.getJSONArray("parts"); + + // 遍历parts数组找到包含inlineData的对象 + for (int i = 0; i < parts.size(); i++) { + com.alibaba.fastjson.JSONObject part = parts.getJSONObject(i); + if (part.containsKey("inlineData")) { + com.alibaba.fastjson.JSONObject inlineData = part.getJSONObject("inlineData"); + base64Data = inlineData.getString("data"); + break; + } + } + } + } else if (ModelConstants.IMAGEN_MODEL.equals(useModel)) { + JSONArray predictions = jsonResponse.getJSONArray("predictions"); + + if (predictions != null && !predictions.isEmpty()) { + com.alibaba.fastjson.JSONObject prediction = predictions.getJSONObject(0); + base64Data = prediction.getString("bytesBase64Encoded"); + } + } + + if (base64Data != null && !base64Data.isEmpty()) { + String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); + // 生成成功,更新Redis状态和URL + GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); + redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } else { + // 没有找到图像数据或数据为空,标记为失败 + log.warn("Google generation response does not contain valid image data for taskId: {}", taskId); + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + } + } catch (Exception e) { + log.error("Google generation failed for taskId: {}", taskId, e); + // 生成失败,更新Redis状态 + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + }, asyncTaskExecutor); + + // 保存生成记录到数据库 + Generate generate = new Generate( + userId, + taskId, + level1Type, + level2Type, + prompt, + "text(" + gender + ")", + useModel, // 记录使用的具体模型名称 + new Date() + ); + save(generate); + return taskId; + } + + /** + * @param generateDTO + * @param modelName advanced high normal + */ + private HashMap chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) { + HashMap modelAndPromptMap = new HashMap<>(); + boolean isUseImage; + if (Objects.isNull(generateDTO.getCollectionElementId()) + || StringUtil.isNullOrEmpty(generateDTO.getDesignType())) { + isUseImage = false; + } else { + isUseImage = true; + } + if (ModelConstants.PRINTBOARD.equals(generateDTO.getLevel1Type())) { + + int firstCommaIndex = generateDTO.getText().indexOf(","); + + String style = generateDTO.getText().substring(0, firstCommaIndex).trim(); + + String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim(); + prompt = getPrintboardPrompt(style, prompt); + modelAndPromptMap.put(ModelConstants.PROMPT, prompt); + + + if (ModelConstants.ADVANCED.equals(modelName)) { + if (isUseImage) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.PRINTBOARD_ADVANCED_I2I);// i2i + } else { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.PRINTBOARD_ADVANCED_T2I);// t2i + } + } else if (ModelConstants.HIGH.equals(modelName)) { + if (isUseImage) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.PRINTBOARD_HIGH_I2I);// i2i + } else { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.PRINTBOARD_HIGH_T2I);// t2i + } + } else if (ModelConstants.NORMAL.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); + } + + } else if (ModelConstants.MOODBOARD.equals(generateDTO.getLevel1Type())) { + String prompt = generateDTO.getText() + "high-resolution, ultra-detailed, realistic textures, perfect anatomy, cinematic lighting, 8k render, editorial photography style"; + modelAndPromptMap.put(ModelConstants.PROMPT, prompt); + + if (ModelConstants.ADVANCED.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.MOODBOARD_ADVANCED); + } else if (ModelConstants.HIGH.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); + } else if (ModelConstants.NORMAL.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); + } + } else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) { + String prompt = generateDTO.getText() + "rules:front view sketch only,plain white background, single garment only, orthographic, centered on white background, borderless canvas, thin monochrome black line art.\n" + + " No clothes hanger, no fake clothes hanger, no human-related lines, no color fill, no words, no text, no black background, no boundary or frame."; + modelAndPromptMap.put(ModelConstants.PROMPT, prompt); + if (isUseImage) { + if (ModelConstants.ADVANCED.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.NANO_BANANA); + } else if (ModelConstants.HIGH.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.NANO_BANANA); + } else if (ModelConstants.NORMAL.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); + } + } else { + if (ModelConstants.ADVANCED.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.IMAGEN_MODEL); + } else if (ModelConstants.HIGH.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.IMAGEN_MODEL); + } else if (ModelConstants.NORMAL.equals(modelName)) { + modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); + } + } + + } + return modelAndPromptMap; + } /** * 参数校验 @@ -621,6 +975,15 @@ public class GenerateServiceImpl extends ServiceImpl i && StringUtil.isNullOrEmpty(generateDTO.getLevel2Type())) { throw new BusinessException("level2Type.cannot.be.empty"); } + if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) { + int firstCommaIndex = generateDTO.getText().indexOf(","); + String style = generateDTO.getText().substring(0, firstCommaIndex).trim(); + //如果style不等于painting style,illustration style,real style中的一种 + if (!style.equals(ModelConstants.PAINTING_STYLE) && !style.equals(ModelConstants.ILLUSTRATION_STYLE) && !style.equals(ModelConstants.REAL_STYLE)) { + throw new BusinessException("style Parameter Exception"); + } + } + } /** @@ -643,6 +1006,168 @@ public class GenerateServiceImpl extends ServiceImpl i return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); } + private PrepareForGenerateVO handleQwenModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + + //TODO:校验积分是否足够 + validateCredits(CreditsEventsEnum.QWEN_TEXT2IMG); + String taskId = createQwenAsyncTask(generateDTO, useModel, prompt); + // TODO:处理积分扣除 + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.QWEN_TEXT2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + private PrepareForGenerateVO handleDouBaoModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + //TODO:校验积分是否足够 + validateCredits(CreditsEventsEnum.DOUBAO_TEXT2IMG); + String taskId = createDouBaoAsyncTask(generateDTO, useModel, prompt); + // TODO:处理积分扣除 + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.DOUBAO_TEXT2IMG); + + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + public String createQwenAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + + String level1Type = generateDTO.getLevel1Type(); + String level2Type = generateDTO.getLevel2Type(); + + Long userId = generateDTO.getUserId(); + String gender = generateDTO.getGender(); + + + Map parameters = new HashMap<>(); + parameters.put("prompt_extend", false); + parameters.put("watermark", false); + ImageSynthesisParam param = + ImageSynthesisParam.builder() + .apiKey(ALIYUN_API_KEY) + .model(useModel) + .prompt(prompt) + .n(1) + .size("1328*1328") + .parameters(parameters) + .build(); + log.info(param.toString()); + + ImageSynthesis imageSynthesis = new ImageSynthesis(); + ImageSynthesisResult result = null; + try { + result = imageSynthesis.asyncCall(param); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + String taskId = result.getOutput().getTaskId(); + log.info("wx text2image 请求生成:{}, taskId:{}", JsonUtils.toJson(result), taskId); + + Generate generate = new Generate(userId, taskId, level1Type, level2Type, prompt, "text(" + gender + ")", "qwen-image", new Date()); + save(generate); + return taskId; + } + + public String createDouBaoAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + // 从DTO中获取基础参数 + String level1Type = generateDTO.getLevel1Type(); + String level2Type = generateDTO.getLevel2Type(); + Long userId = generateDTO.getUserId(); + String gender = generateDTO.getGender(); + // 获取图片路径 + String imagePath = geti2iImagePath(generateDTO); + String finalImagePath = null; + + // 只有当imagePath不为null时才获取图片的base64编码 + if (imagePath != null) { + try { + String imageAsBase64 = minioUtil.getImageAsBase64(imagePath); + finalImagePath = "data:image/png;base64," + imageAsBase64; + } catch (IOException e) { + log.error("获取图片base64编码失败", e); + e.printStackTrace(); + } + } + + // 生成唯一的任务ID + String taskId = UUID.randomUUID().toString() + "-" + userId; + + // 初始化Redis中的任务状态为"Executing" + String key = generateResultKey + ":" + taskId; + GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Executing"); + redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + String finalImagePath1 = finalImagePath; + // 异步执行图像生成任务 + CompletableFuture.runAsync(() -> { + try { + ConnectionPool connectionPool = new ConnectionPool(5, 1, TimeUnit.SECONDS); + Dispatcher dispatcher = new Dispatcher(); + ArkService service = ArkService.builder().dispatcher(dispatcher).connectionPool(connectionPool).apiKey(DOUBAO_API_KEY).build(); + + // 根据是否有图片路径来构建请求 + GenerateImagesRequest.Builder requestBuilder = GenerateImagesRequest.builder() + .watermark(false) + .model(useModel) + .prompt(prompt); + + // 只有当finalImagePath不为null时才设置image参数 + if (imagePath != null) { + requestBuilder.image(finalImagePath1); + } + + GenerateImagesRequest generateRequest = requestBuilder.build(); + + ImagesResponse imagesResponse = service.generateImages(generateRequest); + String imageUrl = imagesResponse.getData().get(0).getUrl(); + + service.shutdownExecutor(); + + // 生成成功,更新Redis状态和URL + GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, imageUrl, "Success"); + redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 保存生成记录到数据库 + Generate generate = new Generate( + userId, + taskId, + level1Type, + level2Type, + prompt, + "text(" + gender + ")", + useModel, // 记录使用的具体模型名称 + new Date() + ); + save(generate); + + // TODO: 处理积分扣除逻辑 + + } catch (Exception e) { + log.error("Doubao image generation failed for taskId: {}", taskId, e); + // 生成失败,更新Redis状态 + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + } + }); + + return taskId; + } + + + private String getPrintboardPrompt(String style, String prompt) { + if ("Painting Style".equals(style)) { + prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" + + "2.Core Theme: " + prompt + "\n" + + "3.Style: painting_style-The painting style refers to the overall approach, techniques, and artistic philosophy used in the artwork. For fashion designs that will be applied to printboards, it is important to define the unique stylistic elements that would translate well into wearable patterns."; + } else if ("Illustration Style".equals(style)) { + prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" + + "2.Core Theme: " + prompt + "\n" + + "3.Style: illustration_style-Illustration style focuses on the visual storytellingaspect, often used to depict narratives, characters, or thematic concepts. Forfashion, this style can introduce vivid and artistic interpretations, often aligned with specific themes."; + } else if ("Real Style".equals(style)) { + prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" + + "2.Core Theme: " + prompt + "\n" + + "3.Style: real_style-Real style in fashion is all about authenticity. It featuresnatural fabrics, simple cuts that mirror real life silhouettes, and colors inspired bythe everyday world, exuding a down-to-earth and genuine charm."; + } + return prompt; + } + /** * 判断是否为flux模型且类型为Pattern */ @@ -660,7 +1185,7 @@ public class GenerateServiceImpl extends ServiceImpl i validateCredits(CreditsEventsEnum.FLUX_IMG2IMG); // 获取图片路径 - String imagePath = getImagePathForFlux(generateDTO); + String imagePath = geti2iImagePath(generateDTO); // 创建生成任务 String taskId = flux(PATTERN, generateDTO.getText(), imagePath, false); @@ -675,9 +1200,9 @@ public class GenerateServiceImpl extends ServiceImpl i } /** - * 获取flux模型需要的图片路径 + * 获取i2i需要传入模型的图片路径 */ - private String getImagePathForFlux(GenerateThroughImageTextDTO generateDTO) { + private String geti2iImagePath(GenerateThroughImageTextDTO generateDTO) { if (Objects.isNull(generateDTO.getCollectionElementId()) || StringUtil.isNullOrEmpty(generateDTO.getDesignType())) { return null; @@ -732,9 +1257,9 @@ public class GenerateServiceImpl extends ServiceImpl i GenerationConfig config = determineGenerationConfig(generateDTO); // 对Slogan功能限流 - if (config.creditsEvent.equals(CreditsEventsEnum.SLOGAN)){ + if (config.creditsEvent.equals(CreditsEventsEnum.SLOGAN)) { boolean b = redisUtil.allowRequest("Slogan"); - if (!b){ + if (!b) { return new PrepareForGenerateVO(429); } } @@ -994,8 +1519,12 @@ public class GenerateServiceImpl extends ServiceImpl i flag = false; } // 暂定万象每次生成1个 - if (type.equals("wx")) { + if (type.equals("wx") || type.contains("qwen")) { return Collections.singletonList(getAsyncTaskResult(taskId)); + } else if (type.contains("imagen") || type.contains("gemini")) { + return Collections.singletonList(getGoogleAsyncResult(taskId)); + } else if (type.contains("doubao")) { + return Collections.singletonList(getDoubaoAsyncResult(taskId)); } else if (type.equals("freepik")) { results.add(generateResultVO); continue; @@ -1269,7 +1798,7 @@ public class GenerateServiceImpl extends ServiceImpl i BigDecimal existingCredits = account.getCredits(); BigDecimal subtract = existingCredits.subtract(new BigDecimal(event.getValue())); BigDecimal creditsUsage = null; - if (!StringUtil.isNullOrEmpty(account.getOrganizationName())){ + if (!StringUtil.isNullOrEmpty(account.getOrganizationName())) { creditsUsage = Objects.isNull(account.getCreditsUsage()) ? BigDecimal.ZERO : account.getCreditsUsage(); creditsUsage = creditsUsage.add(new BigDecimal(event.getValue())); } @@ -1393,7 +1922,7 @@ public class GenerateServiceImpl extends ServiceImpl i // 获取原始路径和可能的generateId PathInfo pathInfo = getOriginalPathAndGenerateId(originalIdSource, originalId); - if (Objects.isNull(pathInfo)){ + if (Objects.isNull(pathInfo)) { throw new BusinessException("unknown sourceIdType", ResultEnum.PROMPT.getCode()); } @@ -1471,7 +2000,7 @@ public class GenerateServiceImpl extends ServiceImpl i } else { generateDetail.setGenerateId(generateId); generateDetail.setUrl(minioPath); - generateDetail.setIsLike((byte)0); + generateDetail.setIsLike((byte) 0); generateDetail.setMd5(md5); generateDetail.setCreateDate(LocalDateTime.now()); generateDetailMapper.insert(generateDetail); @@ -1481,20 +2010,20 @@ public class GenerateServiceImpl extends ServiceImpl i } private GenerateResultVO handleUploadSave(Long accountId, Long originalId, String minioPath, - String category, boolean isOverride, String level1Type){ + String category, boolean isOverride, String level1Type) { CollectionElement collectionElement; String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false); - if (isOverride){ + if (isOverride) { collectionElement = new CollectionElement(); collectionElement.setId(originalId); collectionElement.setUrl(minioPath); collectionElement.setMd5(md5); collectionElement.setUpdateDate(new Date()); collectionElementMapper.updateById(collectionElement); - }else { + } else { CollectionElement originalElement = collectionElementMapper.selectById(originalId); String name = minioPath.substring(minioPath.lastIndexOf("/") + 1, minioPath.lastIndexOf(".")); - collectionElement = new CollectionElement(accountId, level1Type, category, name, minioPath, (byte)0, md5, new Date(), originalElement.getProjectId()); + collectionElement = new CollectionElement(accountId, level1Type, category, name, minioPath, (byte) 0, md5, new Date(), originalElement.getProjectId()); collectionElementMapper.insert(collectionElement); } return buildResultVO(collectionElement.getId(), minioPath, category); @@ -1525,7 +2054,7 @@ public class GenerateServiceImpl extends ServiceImpl i PoseTransformation poseTransformation = new PoseTransformation(); if (!StringUtil.isNullOrEmpty(poseTransformDTO.getModelName()) && poseTransformDTO.getModelName().equals("wx")) { taskId = animateAnyone(poseTransformDTO, accountId); - if (!StringUtil.isNullOrEmpty(taskId)){ + if (!StringUtil.isNullOrEmpty(taskId)) { isRequestSuccess = true; apiGenerateService.addAPIGenerateRecordAsync(accountId, taskId, Module.poseTransfer.getValue(), "wx", "Pending"); } @@ -1742,7 +2271,7 @@ public class GenerateServiceImpl extends ServiceImpl i if (productResult != null) { Long parentId = collectionSortService.getParentIdByElementIdAndElementType( productResult.getId(), CollectionType.TO_PRODUCT_IMAGE.getValue()); - if (Objects.isNull(parentId)){ + if (Objects.isNull(parentId)) { parentId = userLikeGroupService.getUnlikedResultParentId(null, poseTransformation.getProductImage()); } vo.setParentId(parentId); @@ -1758,12 +2287,12 @@ public class GenerateServiceImpl extends ServiceImpl i return "Invalid".equals(status) || "Fail".equals(status); } - public void updatePoseTransferStatus(String taskId, String status, PoseTransformation poseTransformation){ - if (Objects.isNull(poseTransformation)){ + public void updatePoseTransferStatus(String taskId, String status, PoseTransformation poseTransformation) { + if (Objects.isNull(poseTransformation)) { poseTransformation = poseTransformationMapper.selectOne(new QueryWrapper().eq("unique_id", taskId)); } - if (StringUtil.isNullOrEmpty(poseTransformation.getTaskStatus()) || !status.equals(poseTransformation.getTaskStatus())){ + if (StringUtil.isNullOrEmpty(poseTransformation.getTaskStatus()) || !status.equals(poseTransformation.getTaskStatus())) { poseTransformation.setTaskStatus(status); poseTransformation.setUpdateTime(LocalDateTime.now()); poseTransformationMapper.updateById(poseTransformation); @@ -1884,7 +2413,7 @@ public class GenerateServiceImpl extends ServiceImpl i // 所有修改的图片都另存为,不覆盖原图 if (proportionDTO.getType().equals("Library")) { model = libraryService.getById(proportionDTO.getId()); - if (Objects.isNull(model)){ + if (Objects.isNull(model)) { throw new BusinessException("model.not.found"); } String url = model.getUrl(); @@ -1892,7 +2421,7 @@ public class GenerateServiceImpl extends ServiceImpl i // gender = model.getLevel2Type(); } else { SysFileVO sysModel = sysFileService.getById(proportionDTO.getId()); - if (Objects.isNull(sysModel)){ + if (Objects.isNull(sysModel)) { throw new BusinessException("model.not.found"); } gender = sysModel.getLevel2Type(); @@ -2005,12 +2534,12 @@ public class GenerateServiceImpl extends ServiceImpl i vo.setCategoryValue(messageFromResource); } List collectionElements = collectionElementService.getByProjectId(projectId); - if (!collectionElements.isEmpty()){ + if (!collectionElements.isEmpty()) { collectionElements.forEach(item -> { item.setUrl(StringUtil.isNullOrEmpty(item.getUrl()) ? null : minioUtil.getPreSignedUrl(item.getUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); }); vo.setUploadImages(collectionElements); - }else { + } else { vo.setUploadImages(new ArrayList<>()); } return vo; @@ -2050,7 +2579,7 @@ public class GenerateServiceImpl extends ServiceImpl i QueryWrapper cloudTaskQueryWrapper = new QueryWrapper<>(); cloudTaskQueryWrapper.lambda().eq(CloudTask::getTaskId, taskIdBatch); CloudTask cloudTask = cloudTaskMapper.selectOne(cloudTaskQueryWrapper); - if (Objects.nonNull(cloudTask)){ + if (Objects.nonNull(cloudTask)) { cloudTask.setUpdateTime(LocalDateTime.now()); cloudTaskMapper.updateById(cloudTask); } @@ -2099,7 +2628,7 @@ public class GenerateServiceImpl extends ServiceImpl i if (!StringUtils.isEmpty(taskIdBatch)) { cloudTaskService.completeTask(taskIdBatch); } - }else if (progress.startsWith("0/")) { + } else if (progress.startsWith("0/")) { if (!StringUtils.isEmpty(taskIdBatch)) { cloudTaskService.startTask(taskIdBatch); } @@ -2107,7 +2636,6 @@ public class GenerateServiceImpl extends ServiceImpl i } - @Transactional(rollbackFor = Exception.class) public void deleteGeneratedPose(Long projectId, Long id) { // 1. 权限校验 @@ -2163,7 +2691,6 @@ public class GenerateServiceImpl extends ServiceImpl i } - /** * 万象专业版 * 1、MoodBoard t2i @@ -2278,7 +2805,7 @@ public class GenerateServiceImpl extends ServiceImpl i } else { log.warn("未提取到性别"); } - }else if (generate.getLevel1Type().equals(PRINT_BOARD.getRealName())){ + } else if (generate.getLevel1Type().equals(PRINT_BOARD.getRealName())) { Generate generateRecord = selectByUniqueId(taskId); generateResultVO.setCategory(generateRecord.getLevel2Type()); } @@ -2287,10 +2814,10 @@ public class GenerateServiceImpl extends ServiceImpl i throw new BusinessException("Unknown generate task"); } } else if (taskStatus.equals("PENDING") || taskStatus.equals("RUNNING")) { - log.info("万象 异步接口返回生成状态为:{}", taskStatus); + log.info("阿里 异步接口返回生成状态为:{}", taskStatus); return new GenerateResultVO(taskId, null, null, "Executing"); } else { - log.warn("万象 异步接口返回生成状态为:{}", taskStatus); + log.warn("阿里 异步接口返回生成状态为:{}", taskStatus); return new GenerateResultVO(taskId, null, null, "Fail"); } } catch (ApiException | NoApiKeyException e) { @@ -2301,6 +2828,140 @@ public class GenerateServiceImpl extends ServiceImpl i } } + public GenerateResultVO getGoogleAsyncResult(String taskId) { + // 1. 从 Redis 获取当前任务状态 + String key = generateResultKey + ":" + taskId; + String redisResult = redisUtil.getFromString(key); + + if (StringUtil.isNullOrEmpty(redisResult)) { + // Redis 中没有该任务记录,可能任务已过期或 taskId 无效 + return new GenerateResultVO(taskId, null, null, "Unknown"); + } + + GenerateResultVO cachedResult = new Gson().fromJson(redisResult, GenerateResultVO.class); + + // 2. 如果任务还在执行中,直接返回当前状态 + if ("Executing".equals(cachedResult.getStatus())) { + // 这里可以添加额外的检查逻辑,比如检查是否执行超时 + // 如果需要,可以从 Doubao API 查询实际状态 + return cachedResult; + } + + // 3. 如果任务已经完成(成功或失败),直接返回结果 + if ("Success".equals(cachedResult.getStatus())) { + List generates = selectListByUniqueId(taskId); + String url = cachedResult.getUrl(); + + String path = null; + if (!generates.isEmpty()) { + Generate generate = generates.get(0); + Long accountId = generate.getAccountId(); + + + // 3、生成结果保存到db + GenerateDetail generateDetail = new GenerateDetail(); + generateDetail.setGenerateId(generate.getId()); + generateDetail.setUrl(url); + generateDetail.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, 24 * 60), false)); + generateDetail.setCreateDate(LocalDateTime.now()); + generateDetailMapper.insert(generateDetail); + // 4、扣积分 + Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId); + if (flag) creditsService.updateChangedCredits(String.valueOf(generate.getAccountId()), taskId); + + GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), minioUtil.getPreSignedUrl(url, 24 * 60), "Success"); + if (generate.getLevel1Type().equals(SKETCH_BOARD.getRealName())) { + String gender = extractGender(generate.getGenerateType()); + if (!StringUtil.isNullOrEmpty(gender)) { + String clothCategory = pythonService.getClothCategory(url, gender); + generateResultVO.setCategory(clothCategory); + } else { + log.warn("未提取到性别"); + } + } else if (generate.getLevel1Type().equals(PRINT_BOARD.getRealName())) { + Generate generateRecord = selectByUniqueId(taskId); + generateResultVO.setCategory(generateRecord.getLevel2Type()); + } + return generateResultVO; + } else { + throw new BusinessException("Unknown generate task"); + } + } + + // 4. 其他状态(如 Unknown),返回默认失败 + return new GenerateResultVO(taskId, null, null, "Fail"); + } + + public GenerateResultVO getDoubaoAsyncResult(String taskId) { + // 1. 从 Redis 获取当前任务状态 + String key = generateResultKey + ":" + taskId; + String redisResult = redisUtil.getFromString(key); + + if (StringUtil.isNullOrEmpty(redisResult)) { + // Redis 中没有该任务记录,可能任务已过期或 taskId 无效 + return new GenerateResultVO(taskId, null, null, "Unknown"); + } + + GenerateResultVO cachedResult = new Gson().fromJson(redisResult, GenerateResultVO.class); + + // 2. 如果任务还在执行中,直接返回当前状态 + if ("Executing".equals(cachedResult.getStatus())) { + // 这里可以添加额外的检查逻辑,比如检查是否执行超时 + // 如果需要,可以从 Doubao API 查询实际状态 + return cachedResult; + } + + // 3. 如果任务已经完成(成功或失败),直接返回结果 + if ("Success".equals(cachedResult.getStatus())) { + List generates = selectListByUniqueId(taskId); + String url = cachedResult.getUrl(); + + String path = null; + if (!generates.isEmpty()) { + Generate generate = generates.get(0); + Long accountId = generate.getAccountId(); + + // 1、下载图片 +// InputStream inputStream = downloadImageFromAliyun(url); + byte[] bytes = downloadVideoOrImage(url); + // 2、上传图片到minio保存 + String objectName = accountId + "/" + generate.getLevel1Type().toLowerCase() + "/" + taskId + ".png"; + minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png"); + path = userBucket + "/" + objectName; + // 3、生成结果保存到db + GenerateDetail generateDetail = new GenerateDetail(); + generateDetail.setGenerateId(generate.getId()); + generateDetail.setUrl(path); + generateDetail.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(path, 24 * 60), false)); + generateDetail.setCreateDate(LocalDateTime.now()); + generateDetailMapper.insert(generateDetail); + // 4、扣积分 + Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId); + if (flag) creditsService.updateChangedCredits(String.valueOf(generate.getAccountId()), taskId); + + GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), minioUtil.getPreSignedUrl(path, 24 * 60), "Success"); + if (generate.getLevel1Type().equals(SKETCH_BOARD.getRealName())) { + String gender = extractGender(generate.getGenerateType()); + if (!StringUtil.isNullOrEmpty(gender)) { + String clothCategory = pythonService.getClothCategory(path, gender); + generateResultVO.setCategory(clothCategory); + } else { + log.warn("未提取到性别"); + } + } else if (generate.getLevel1Type().equals(PRINT_BOARD.getRealName())) { + Generate generateRecord = selectByUniqueId(taskId); + generateResultVO.setCategory(generateRecord.getLevel2Type()); + } + return generateResultVO; + } else { + throw new BusinessException("Unknown generate task"); + } + } + + // 4. 其他状态(如 Unknown),返回默认失败 + return new GenerateResultVO(taskId, null, null, "Fail"); + } + private static final String IMAGE_DETECT = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/aa-detect"; private static final String TEMPLATE_ID_GEN = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/aa-template-generation/"; private static final String GET_ASYNC_RESULT = "https://dashscope.aliyuncs.com/api/v1/tasks/"; @@ -2796,7 +3457,11 @@ public class GenerateServiceImpl extends ServiceImpl i !StringUtil.isNullOrEmpty(generate.getModelName()) && (generate.getModelName().equals("wx") || generate.getModelName().equals("freepik") - || generate.getModelName().equals("flux"))) { + || generate.getModelName().equals("flux") + || generate.getModelName().contains("qwen") + || generate.getModelName().contains("imagen") + || generate.getModelName().contains("gemini") + || generate.getModelName().contains("doubao"))) { return generate.getModelName(); } else { return "local"; @@ -2832,7 +3497,7 @@ public class GenerateServiceImpl extends ServiceImpl i case IMAGE_TO_SKETCH_FLUX: prompt = "generate the sketch of the image, simple line, ultra high quality"; break; - case TO_PRODUCT_IMAGE_FLUX: + case TO_PRODUCT_IMAGE_ADVANCED: prompt = "change the image to real style, ultra high quality, 8k"; if (childStyle) prompt = prompt + ", Children's face"; break; @@ -2852,7 +3517,7 @@ public class GenerateServiceImpl extends ServiceImpl i requestBody.set("prompt", prompt); requestBody.set("seed", 42); - if (func.equals(PATTERN)){ + if (func.equals(PATTERN)) { requestBody.set("aspect_ratio", "1:1"); } else { requestBody.set("aspect_ratio", "9:16"); @@ -2860,11 +3525,11 @@ public class GenerateServiceImpl extends ServiceImpl i requestBody.set("output_format", "png"); log.info("flux 请求入参:{}", requestBody); - if (prompt.isEmpty())throw new BusinessException("test"); + if (prompt.isEmpty()) throw new BusinessException("test"); if (!StringUtil.isNullOrEmpty(imagePath)) { try { String imageAsBase64 = null; - if (func.equals(TO_PRODUCT_IMAGE_FLUX)) { + if (func.equals(TO_PRODUCT_IMAGE_ADVANCED)) { imageAsBase64 = addWhiteBackground(imagePath); } if (StringUtil.isNullOrEmpty(imageAsBase64)) { @@ -2924,11 +3589,11 @@ public class GenerateServiceImpl extends ServiceImpl i // 处理不同状态 switch (statusEnum) { case TASK_NOT_FOUND: - // 审核没过 + // 审核没过 case REQUEST_MODERATED: - // 审核没过 + // 审核没过 case CONTENT_MODERATED: - // 出错 + // 出错 case ERROR: return "Fail"; case PENDING_F: @@ -2989,7 +3654,7 @@ public class GenerateServiceImpl extends ServiceImpl i return new GenerateResultVO(taskId, "Fail"); } else if (fluxResult.equals("Fail") || fluxResult.equals("Pending")) { String status = fluxResult.equals("Fail") ? "Fail" : "Executing"; - if (status.equals("Fail")){ + if (status.equals("Fail")) { creditsService.deleteCreditsDeduction(accountId, taskId); } return new GenerateResultVO(taskId, status); @@ -3012,8 +3677,8 @@ public class GenerateServiceImpl extends ServiceImpl i generateDetailMapper.updateById(generateDetail); } String url = generateDetail.getUrl(); - String category ; - if (generate.getLevel1Type().equals(SKETCH_BOARD.getRealName())){ + String category; + if (generate.getLevel1Type().equals(SKETCH_BOARD.getRealName())) { category = pythonService.getClothCategory(url, extractGender(generate.getGenerateType())); } else { category = generate.getLevel2Type(); 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 6e88c6c2..21b199f1 100644 --- a/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java @@ -3,6 +3,7 @@ package com.ai.da.service.impl; import cn.hutool.core.collection.CollectionUtil; import com.ai.da.common.config.exception.BusinessException; import com.ai.da.common.constant.CommonConstant; +import com.ai.da.common.constant.ModelConstants; import com.ai.da.common.context.UserContext; import com.ai.da.common.enums.CollectionLevel1TypeEnum; import com.ai.da.common.enums.CreditsEventsEnum; @@ -405,9 +406,9 @@ public class UserLikeGroupServiceImpl extends ServiceImpl toProduct(ToProductImageDTO toProductImageDTO) { // 判断用户当前积分是否够本次生成消耗 - boolean fluxTask = !StringUtil.isNullOrEmpty(toProductImageDTO.getModelName()) - && toProductImageDTO.getModelName().equals("flux"); - CreditsEventsEnum creditsEventsEnum = fluxTask ? CreditsEventsEnum.TO_PRODUCT_IMAGE_FLUX : CreditsEventsEnum.TO_PRODUCT_IMAGE; + boolean nanoBananaTask = !StringUtil.isNullOrEmpty(toProductImageDTO.getModelName()) + && toProductImageDTO.getModelName().equals(ModelConstants.ADVANCED)||toProductImageDTO.getModelName().equals(ModelConstants.HIGH); + CreditsEventsEnum creditsEventsEnum = nanoBananaTask ? CreditsEventsEnum.TO_PRODUCT_IMAGE_ADVANCED : CreditsEventsEnum.TO_PRODUCT_IMAGE; Boolean preDeduction = creditsService.creditsPreDeduction(creditsEventsEnum, toProductImageDTO.getToProductImageVOList().size()); if (!preDeduction) { throw new BusinessException("Your remaining credits are insufficient for this generation. Please recharge.", ResultEnum.WARNING.getCode()); @@ -487,12 +488,29 @@ public class UserLikeGroupServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper().eq(CollectionElement::getProjectId, projectId) + .eq(CollectionElement::getLevel1Type, CollectionLevel1TypeEnum.MODEL.getRealName()) + .eq(CollectionElement::getLevel3Type, workspaceNew.getSex()); + if (collectionElementMapper.selectCount(queryWrapper) == 0){ + SysFile sysFile = sysFileService.getOneBySex(projectDTO.getStyleId(), projectDTO.getWorkspace().getSex(), projectDTO.getWorkspace().getAgeGroup()); + CollectionElement collectionElement = new CollectionElement(); + collectionElement.setAccountId(userInfo.getId()); + collectionElement.setProjectId(projectId); + collectionElement.setLevel1Type(CollectionLevel1TypeEnum.MODEL.getRealName()); + collectionElement.setLevel3Type(projectDTO.getWorkspace().getSex()); + collectionElement.setName(sysFile.getName()); + collectionElement.setUrl(sysFile.getUrl()); + collectionElement.setMd5(sysFile.getMd5()); + collectionElement.setCreateDate(new Date()); + collectionElement.setHasPin((byte) 0); + collectionElementMapper.insert(collectionElement); + CollectionElementRelModel collectionElementRelModel = new CollectionElementRelModel(); + collectionElementRelModel.setCollectionElementId(collectionElement.getId()); + collectionElementRelModel.setRelationId(sysFile.getId()); + collectionElementRelModel.setRelationType("System"); + collectionElementRelModelMapper.insert(collectionElementRelModel); + } + } } - SysFile sysFile = sysFileService.getOneBySex(projectDTO.getStyleId(), projectDTO.getWorkspace().getSex(), projectDTO.getWorkspace().getAgeGroup()); - CollectionElement collectionElement = new CollectionElement(); - collectionElement.setAccountId(userInfo.getId()); - collectionElement.setProjectId(projectId); - collectionElement.setLevel1Type(CollectionLevel1TypeEnum.MODEL.getRealName()); - collectionElement.setName(sysFile.getName()); - collectionElement.setUrl(sysFile.getUrl()); - collectionElement.setMd5(sysFile.getMd5()); - collectionElement.setCreateDate(new Date()); - collectionElement.setHasPin((byte) 0); - collectionElementMapper.insert(collectionElement); - CollectionElementRelModel collectionElementRelModel = new CollectionElementRelModel(); - collectionElementRelModel.setCollectionElementId(collectionElement.getId()); - collectionElementRelModel.setRelationId(sysFile.getId()); - collectionElementRelModel.setRelationType("System"); - collectionElementRelModelMapper.insert(collectionElementRelModel); + } workspaceNew.setId(workspace.getId()); workspace.setUpdateTime(LocalDateTime.now()); @@ -857,6 +868,17 @@ public class WorkspaceServiceImpl extends ServiceImpl workspaceLambdaQueryWrapper = new LambdaQueryWrapper<>(); + workspaceLambdaQueryWrapper.eq(Workspace::getProjectId, projectId).select(Workspace::getSex); + Workspace workspace = workspaceMapper.selectOne(workspaceLambdaQueryWrapper); + if (workspace != null) { + return workspace.getSex(); + } + return null; + } + public static List getPNGFiles(String directoryPath) { List pngFiles = new ArrayList<>(); File directory = new File(directoryPath); diff --git a/src/main/resources/aida-461108-b4afaabebb84.json b/src/main/resources/aida-461108-b4afaabebb84.json new file mode 100644 index 00000000..9948d5ee --- /dev/null +++ b/src/main/resources/aida-461108-b4afaabebb84.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "aida-461108", + "private_key_id": "b4afaabebb84da24502b318a5fa175f1dc5c096a", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCmk7LKrp8g9yD1\nWmF+mY2qHCEZ/5aIx6QRh0QoVPBL7Yi7ce009QxaE8fu8+QMgg8l3xMreXvgpt56\noFnVwpFusLjSdjgoFluElM2hYxXlO9q8cbBoU2nehOBLLJzGzkodT7xu/BOjNvKC\n//aTbjtJyk8Kj+ENa0/dPaUZs/PCtQqpAu8ag5nXrordVWfO0K25EjeYyoba35zk\nPp2fBi8KALZZI5Xfd2z9++K0K2mWWIMJic30idHvquj0WxlTRK2Pq8BmJXCQpJIi\nQ5E4egue16BfKjrF0Kxkpqd1RmdlEmaSKbbkZXe2z4jg0qknESRFOmRy8C3LnaB2\nHHJWLYM3AgMBAAECggEACUdroOQJSTTQSS/iWRhZ+S0yoC10nTnsZxg527qfiBs7\nOqB7WNqC+Ew8dDsca6CdvLuoaGDkCFJDTQwRn66u8JOM4sG4bxiPuzBEJBv45EQT\n8zCsuvhVNWgBdoPjAnq19jFdixvPnDqQrRYaY4FdxsaA5f24c57pW/xLGMYawLBt\n9RJZSuWmJdzKG1i5W8a8+4f/seNtuo2MtXU3mPJZPqRWPXTAZeaQPM/57ZQ+kzig\nOkAbQZNRmt1yPCjPCQD8vc8yCBMmjus/rlHXD/L7okYUlVZkob5I3FBrLl+ZyIXS\nqxEsBLBwRW3w8WbX+ZSVciQ72JK68W7LnOHSAENmAQKBgQDgBTCqp87KGLWVPb8w\nK+s1Sfh+nM3M4AlbLdcGBs1JCoddF6pAeY4wpf/ow1Tm4rqEuCYzMClPwxvkue+D\nY7lCQgy2FK3ahUzn8oVmvEPD/YPAojDSY3bH0lquHuS6oVKk834JUykButaAU3XY\nvUGNQuKdLKAeQRT8Q6um4m+EYQKBgQC+Wz6nYESKH6GiNnuFTH8hIkThPlbi4wua\nU1kGnPKe3ouE4zRLfPwQ6RRf1slQ/2hFLOatiTLYUgZWZQeBPSWp2EjYcOSzob+7\n11+KqeIRCD5DKxgf0cjJdihK9AM639OKlH2NvZ2507TksdeTPDzdaOMLwLWKexP5\nlYrdob0ulwKBgD81t7Gvf83Ogw4FSjkRa2Cx6ofvPrKcVIeBu7ZbnPkLG37M+qEO\nq2xWqorG8uHi/7YLL9wprr5u0yQKwuZT8SYc9PE7jIKoMjcQW0vNu2FF2zMzkIsM\nvatMU4Hl/awbcPJSMjH3YQ635WZ4Jjxtyl1NjhvDR7rBqmYzwe9o3QaBAoGANhPB\n1tbYYczepDCKIrI6o3US0FJfaJFLqInpDqHjoxJh3FyXbKKTEVLFwPxJsML+IjjB\nR6dkVGPo/P4yhZqTao7REvvvXMCksX5b3A6q9F+9IGPLtK5qNiFlDPYJPN59QC8z\nA+NMPZBRIW8MaP2B5Px5E8upRy/z2sGK86+RCP0CgYATGs75F97q+Zf8q+Pe3Nsb\ngqmhLoI3PZUSWgBcQgNF4nyCZceUrEl72wKO/NWLgxqQPtlra187ce69g7qARHLb\ntHq80nb0f7lil74B6+OlyNNO1htWA90fmGR2s16Mt0BwJRT+/EFuNqbJIUSLxKiW\nqlXBUbmHHzamo5DPYL8S/w==\n-----END PRIVATE KEY-----\n", + "client_email": "aida-239@aida-461108.iam.gserviceaccount.com", + "client_id": "103102077955178349079", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/aida-239%40aida-461108.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +} diff --git a/src/main/resources/mapper/primary/UserLikeGroupMapper.xml b/src/main/resources/mapper/primary/UserLikeGroupMapper.xml index 00d7cb95..559e84fc 100644 --- a/src/main/resources/mapper/primary/UserLikeGroupMapper.xml +++ b/src/main/resources/mapper/primary/UserLikeGroupMapper.xml @@ -38,7 +38,7 @@ - SELECT + SELECT tpir.id, tpir.url, tpir.element_type, @@ -119,7 +119,7 @@ - SELECT + SELECT id, url, project_id, @@ -154,7 +154,7 @@ - SELECT + SELECT id as record_id, prompt FROM to_product_image_record - WHERE id IN + WHERE id IN #{recordId}