From 2f3fa695b23b897509f4a8c099dbd6064170c82d Mon Sep 17 00:00:00 2001 From: xupei Date: Tue, 18 Nov 2025 10:27:29 +0800 Subject: [PATCH] =?UTF-8?q?BUGFIX:=20=E8=A7=A3=E5=86=B3=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E6=B3=A8=E5=85=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../da/common/RabbitMQ/GenerateConsumer.java | 29 +- .../ai/da/controller/PortfolioController.java | 10 +- .../java/com/ai/da/service/DesignService.java | 6 - .../da/service/MoodboardPositionService.java | 9 + .../com/ai/da/service/PortfolioService.java | 12 - .../ai/da/service/PythonTAllInfoService.java | 3 + .../service/ToProductImageResultService.java | 9 + .../com/ai/da/service/UserFollowService.java | 24 + .../ai/da/service/UserLikeGroupService.java | 4 +- .../com/ai/da/service/WorkspaceService.java | 2 + .../da/service/impl/AccountServiceImpl.java | 39 +- .../impl/CollectionElementServiceImpl.java | 58 +- .../service/impl/DesignItemServiceImpl.java | 242 +- .../ai/da/service/impl/DesignServiceImpl.java | 418 +- .../da/service/impl/GenerateServiceImpl.java | 8723 ++++++++--------- .../impl/LibraryModelPointServiceImpl.java | 37 +- .../impl/MessageCenterServiceImpl.java | 123 +- .../impl/MoodboardPositionServiceImpl.java | 90 + .../da/service/impl/PortfolioServiceImpl.java | 230 +- .../impl/PythonTAllInfoServiceImpl.java | 31 + .../impl/ToProductImageResultServiceImpl.java | 78 + .../service/impl/UserFollowServiceImpl.java | 125 + .../impl/UserLikeGroupServiceImpl.java | 202 +- .../da/service/impl/UserLikeServiceImpl.java | 38 +- .../da/service/impl/WorkspaceServiceImpl.java | 32 +- 25 files changed, 5248 insertions(+), 5326 deletions(-) create mode 100644 src/main/java/com/ai/da/service/MoodboardPositionService.java create mode 100644 src/main/java/com/ai/da/service/ToProductImageResultService.java create mode 100644 src/main/java/com/ai/da/service/UserFollowService.java create mode 100644 src/main/java/com/ai/da/service/impl/MoodboardPositionServiceImpl.java create mode 100644 src/main/java/com/ai/da/service/impl/ToProductImageResultServiceImpl.java create mode 100644 src/main/java/com/ai/da/service/impl/UserFollowServiceImpl.java diff --git a/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java b/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java index a48e781f..ded8b9a3 100644 --- a/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java +++ b/src/main/java/com/ai/da/common/RabbitMQ/GenerateConsumer.java @@ -6,10 +6,7 @@ import com.ai.da.common.utils.RedisUtil; import com.ai.da.model.dto.GenerateThroughImageTextDTO; import com.ai.da.model.vo.GenerateResultVO; import com.ai.da.model.vo.PoseTransformationVO; -import com.ai.da.service.CloudTaskService; -import com.ai.da.service.DesignService; -import com.ai.da.service.GenerateService; -import com.ai.da.service.UserLikeGroupService; +import com.ai.da.service.*; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.JsonParseException; @@ -17,17 +14,16 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.rabbitmq.client.Channel; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.text.StringEscapeUtils; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; -import javax.annotation.Resource; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -37,25 +33,22 @@ import java.util.Objects; @Slf4j @Component +@RequiredArgsConstructor public class GenerateConsumer { - @Resource - private GenerateService generateService; + private final GenerateService generateService; - @Resource - private UserLikeGroupService userLikeGroupService; + private final UserLikeGroupService userLikeGroupService; - @Resource - private DesignService designService; + private final DesignService designService; - @Resource - private CloudTaskService cloudTaskService; + private final CloudTaskService cloudTaskService; - @Autowired - private RabbitMQProperties rabbitMQProperties; + private final RabbitMQProperties rabbitMQProperties; - @Resource - private RedisUtil redisUtil; + private final RedisUtil redisUtil; + + private final MessageCenterService messageCenterService; @Value("${redis.key.orderForGenerate}") private String consumptionOrderKey; diff --git a/src/main/java/com/ai/da/controller/PortfolioController.java b/src/main/java/com/ai/da/controller/PortfolioController.java index 5786c078..bfa073af 100644 --- a/src/main/java/com/ai/da/controller/PortfolioController.java +++ b/src/main/java/com/ai/da/controller/PortfolioController.java @@ -3,13 +3,12 @@ package com.ai.da.controller; import com.ai.da.common.config.exception.BusinessException; import com.ai.da.common.response.PageBaseResponse; import com.ai.da.common.response.Response; -import com.ai.da.mapper.primary.entity.Account; import com.ai.da.model.dto.*; import com.ai.da.model.vo.*; import com.ai.da.model.vo.CommentVO; import com.ai.da.model.vo.PortfolioVO; -import com.ai.da.model.vo.UserLikeChooseVO; import com.ai.da.service.PortfolioService; +import com.ai.da.service.UserFollowService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -29,6 +28,9 @@ public class PortfolioController { @Resource private PortfolioService portfolioService; + @Resource + private UserFollowService userFollowService; + @ApiOperation(value = "发布作品集") @PostMapping("/publish") public Response publish(@RequestParam("file") MultipartFile canvas, @RequestParam("data") String data) { @@ -138,13 +140,13 @@ public class PortfolioController { @ApiOperation(value = "获取关注列表") @PostMapping("/getFolloweeList") public Response> getFolloweeList(@Valid @RequestBody GetFollowListDTO getFollowListDTO) { - return Response.success(portfolioService.getFolloweeList(getFollowListDTO)); + return Response.success(userFollowService.getFolloweeList(getFollowListDTO)); } @ApiOperation(value = "获取粉丝列表") @PostMapping("/getFollowerList") public Response> getFollowerList(@Valid @RequestBody GetFollowListDTO getFollowListDTO) { - return Response.success(portfolioService.getFollowerList(getFollowListDTO)); + return Response.success(userFollowService.getFollowerList(getFollowListDTO)); } diff --git a/src/main/java/com/ai/da/service/DesignService.java b/src/main/java/com/ai/da/service/DesignService.java index 0964685d..7e313c5c 100644 --- a/src/main/java/com/ai/da/service/DesignService.java +++ b/src/main/java/com/ai/da/service/DesignService.java @@ -95,10 +95,6 @@ public interface DesignService extends IService { Integer designProcess(String processId); - void parseMoodboardPosition(String moodboardPosition, Long collectionIdParam); - - void relationImageId(DesignPythonObjects objects); - List sketchesBoundingBox(ReDesignCollectionDTO reDesignCollectionDTO); List getModel(List designItemIdList); @@ -124,6 +120,4 @@ public interface DesignService extends IService { Boolean cloudTaskNameUpdate(CloudTaskDTO cloudTaskDTO); Boolean cloudTaskDelete(CloudTaskDTO cloudTaskDTO); - - String getAgeGroupByProjectOrCollectionId(Long projectId, Long collectionId); } diff --git a/src/main/java/com/ai/da/service/MoodboardPositionService.java b/src/main/java/com/ai/da/service/MoodboardPositionService.java new file mode 100644 index 00000000..e0fe3aeb --- /dev/null +++ b/src/main/java/com/ai/da/service/MoodboardPositionService.java @@ -0,0 +1,9 @@ +package com.ai.da.service; + +import com.ai.da.mapper.primary.entity.MoodboardPosition; +import com.baomidou.mybatisplus.extension.service.IService; + +public interface MoodboardPositionService extends IService { + + void parseMoodboardPosition(String moodboardPosition, Long collectionIdParam); +} diff --git a/src/main/java/com/ai/da/service/PortfolioService.java b/src/main/java/com/ai/da/service/PortfolioService.java index f26a4e43..274b4184 100644 --- a/src/main/java/com/ai/da/service/PortfolioService.java +++ b/src/main/java/com/ai/da/service/PortfolioService.java @@ -47,20 +47,8 @@ public interface PortfolioService extends IService { void cancelFollow(Long followeeId); - Long getFolloweeCount(Long accountId); - - List getFolloweeList(GetFollowListDTO getFollowListDTO); - - Long getFollowerCount(Long accountId); - - List getFollowerList(GetFollowListDTO getFollowListDTO); - - Integer getIfFollowed(Long followeeId, Long followerId); - Long getPortfolioCount(Long accountId); - List getFolloweeList(Long accountId); - // List queryPortfolioByTag(String tagName, Long tagId); void setPortfolioToPublic(Long portfolioId); diff --git a/src/main/java/com/ai/da/service/PythonTAllInfoService.java b/src/main/java/com/ai/da/service/PythonTAllInfoService.java index 8fd59225..72ad246a 100644 --- a/src/main/java/com/ai/da/service/PythonTAllInfoService.java +++ b/src/main/java/com/ai/da/service/PythonTAllInfoService.java @@ -2,6 +2,7 @@ package com.ai.da.service; import com.ai.da.mapper.primary.entity.PythonTAllInfo; +import com.ai.da.python.vo.DesignPythonObjects; import com.baomidou.mybatisplus.extension.service.IService; /** @@ -14,4 +15,6 @@ public interface PythonTAllInfoService extends IService { Long getImageIdByPath(String path); + + void relationImageId(DesignPythonObjects pythonObjects); } diff --git a/src/main/java/com/ai/da/service/ToProductImageResultService.java b/src/main/java/com/ai/da/service/ToProductImageResultService.java new file mode 100644 index 00000000..f5c8e31f --- /dev/null +++ b/src/main/java/com/ai/da/service/ToProductImageResultService.java @@ -0,0 +1,9 @@ +package com.ai.da.service; + +import com.ai.da.mapper.primary.entity.ToProductImageResult; +import com.baomidou.mybatisplus.extension.service.IService; + +public interface ToProductImageResultService extends IService { + + Long getUnlikedResultParentId(ToProductImageResult toProductImageResult, String url); +} diff --git a/src/main/java/com/ai/da/service/UserFollowService.java b/src/main/java/com/ai/da/service/UserFollowService.java new file mode 100644 index 00000000..7eb7c23d --- /dev/null +++ b/src/main/java/com/ai/da/service/UserFollowService.java @@ -0,0 +1,24 @@ +package com.ai.da.service; + +import com.ai.da.mapper.primary.entity.UserFollow; +import com.ai.da.model.dto.GetFollowListDTO; +import com.ai.da.model.vo.AccountFollowVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +public interface UserFollowService extends IService { + + Long getFollowerCount(Long accountId); + + List getFollowerList(GetFollowListDTO getFollowListDTO); + + Long getFolloweeCount(Long accountId); + + List getFolloweeList(GetFollowListDTO getFollowListDTO); + + Integer getIfFollowed(Long followeeId, Long followerId); + + List getFolloweeList(Long accountId); + +} diff --git a/src/main/java/com/ai/da/service/UserLikeGroupService.java b/src/main/java/com/ai/da/service/UserLikeGroupService.java index 9db9f0fa..093b0e03 100644 --- a/src/main/java/com/ai/da/service/UserLikeGroupService.java +++ b/src/main/java/com/ai/da/service/UserLikeGroupService.java @@ -42,7 +42,7 @@ public interface UserLikeGroupService extends IService { void deleteTrialData(Long id); - void updateDate(Long id,String timeZone); + void updateDate(Long id, String timeZone); Long exportSave(MultipartFile file, Long projectId, String module, Long designItemDetailId); @@ -121,6 +121,4 @@ public interface UserLikeGroupService extends IService { Boolean toProductImageElementDelete(Long id); ToProductElementVO convertRelightElement(Long id); - - Long getUnlikedResultParentId(ToProductImageResult toProductImageResult, String url); } diff --git a/src/main/java/com/ai/da/service/WorkspaceService.java b/src/main/java/com/ai/da/service/WorkspaceService.java index 1c967b38..65450878 100644 --- a/src/main/java/com/ai/da/service/WorkspaceService.java +++ b/src/main/java/com/ai/da/service/WorkspaceService.java @@ -70,4 +70,6 @@ public interface WorkspaceService extends IService { String getProjectSexById(Long projectId); String getStyleByProjectId(Long projectId); + + String getAgeGroupByProjectOrCollectionId(Long projectId, Long collectionId); } diff --git a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java index 6fe96c43..09286981 100644 --- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java @@ -143,6 +143,9 @@ public class AccountServiceImpl extends ServiceImpl impl @Resource private AffiliateService affiliateService; + @Resource + private UserFollowService userFollowService; + @Override @Transactional(rollbackFor = Exception.class) public AccountPreLoginVO preLogin(AccountPreLoginDTO accountDTO) { @@ -261,8 +264,8 @@ public class AccountServiceImpl extends ServiceImpl impl avatar = account.getAvatar(); } response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); - response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); + response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId())); + response.setFollowerCount(userFollowService.getFollowerCount(account.getId())); //判断是否常用ip 不是则发邮件提示 calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account); return response; @@ -2050,8 +2053,8 @@ public class AccountServiceImpl extends ServiceImpl impl personalHomepageVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); personalHomepageVO.setPortfolioCount(portfolioService.getPortfolioCount(accountId)); - personalHomepageVO.setFolloweeCount(portfolioService.getFolloweeCount(accountId)); - personalHomepageVO.setFollowerCount(portfolioService.getFollowerCount(accountId)); + personalHomepageVO.setFolloweeCount(userFollowService.getFolloweeCount(accountId)); + personalHomepageVO.setFollowerCount(userFollowService.getFollowerCount(accountId)); personalHomepageVO.setHomepageViewCount(viewPersonalHomepageCount(0L)); @@ -2061,7 +2064,7 @@ public class AccountServiceImpl extends ServiceImpl impl // 只有本人才能看到个人主页浏览量 personalHomepageVO.setHomepageViewCount(viewCount == null ? 0 : viewCount); } else { - personalHomepageVO.setIsFollow(portfolioService.getIfFollowed(accountId, currentUserId)); + personalHomepageVO.setIsFollow(userFollowService.getIfFollowed(accountId, currentUserId)); // 非本人浏览主页时增加浏览量 viewsIncrease(accountId); } @@ -2925,8 +2928,8 @@ public class AccountServiceImpl extends ServiceImpl impl avatar = account.getAvatar(); } response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); - response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); + response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId())); + response.setFollowerCount(userFollowService.getFollowerCount(account.getId())); return response; } else { throw new IllegalArgumentException("Invalid ID token."); @@ -3030,8 +3033,8 @@ public class AccountServiceImpl extends ServiceImpl impl avatar = account.getAvatar(); } response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); - response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); + response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId())); + response.setFollowerCount(userFollowService.getFollowerCount(account.getId())); return response; } @@ -3122,8 +3125,8 @@ public class AccountServiceImpl extends ServiceImpl impl avatar = account.getAvatar(); } response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); - response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); + response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId())); + response.setFollowerCount(userFollowService.getFollowerCount(account.getId())); QueryWrapper qw = new QueryWrapper<>(); qw.lambda().eq(AccountExtend::getAccountId, response.getUserId()); @@ -3651,19 +3654,19 @@ public class AccountServiceImpl extends ServiceImpl impl } // todo 机构管理员再次订阅时,需要同步机构的激活状态 - private void setOrganizationStatus(Account adminAcc, boolean isActive){ + private void setOrganizationStatus(Account adminAcc, boolean isActive) { Organization organization; - if (Objects.nonNull(adminAcc.getOrganizationId())){ + if (Objects.nonNull(adminAcc.getOrganizationId())) { organization = organizationMapper.selectById(adminAcc.getOrganizationId()); - } else if (!StringUtil.isNullOrEmpty(adminAcc.getOrganizationName())){ + } else if (!StringUtil.isNullOrEmpty(adminAcc.getOrganizationName())) { organization = convenientInquiryService.checkOrganization(adminAcc.getSystemUser(), adminAcc.getOrganizationName()); } else { return; } - if (Objects.isNull(organization)){ + if (Objects.isNull(organization)) { organization = convenientInquiryService.addOrganization(adminAcc.getOrganizationName(), getOrganizationTypeByRole(adminAcc.getSystemUser())); } - if (isActive){ + if (isActive) { organization.setStatus(1); } else { organization.setStatus(0); @@ -3672,9 +3675,9 @@ public class AccountServiceImpl extends ServiceImpl impl organizationMapper.updateById(organization); } - public String getOrganizationTypeByRole(Integer roleNum){ + public String getOrganizationTypeByRole(Integer roleNum) { String type; - switch (roleNum){ + switch (roleNum) { case 5: type = "Enterprise"; break; 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 bd02a75a..aa43833b 100644 --- a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java @@ -59,29 +59,19 @@ import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class CollectionElementServiceImpl extends ServiceImpl implements CollectionElementService { - @Resource - private CollectionElementMapper collectionElementMapper; - @Resource - private FileProperties fileProperties; - @Resource - private PanToneService panToneService; - @Resource - private PythonService pythonService; - @Resource - private LibraryService libraryService; - @Resource - private SysFileService sysFileService; - @Resource - private GenerateMapper generateMapper; - @Resource - private GenerateDetailMapper generateDetailMapper; - @Resource - private LibraryModelPointService libraryModelPointService; - @Resource - private TCollectionElementRelationService tCollectionElementRelationService; - @Resource - private MinioUtil minioUtil; - + private final CollectionElementMapper collectionElementMapper; + private final EmailService emailService; + private final FileProperties fileProperties; + private final GenerateMapper generateMapper; + private final GenerateDetailMapper generateDetailMapper; + private final LibraryModelPointService libraryModelPointService; + private final LibraryService libraryService; + private final MinioUtil minioUtil; + private final PanToneService panToneService; + private final PythonService pythonService; + private final RedisUtil redisUtil; + private final SysFileService sysFileService; + private final TCollectionElementRelationService tCollectionElementRelationService; private final WorkspaceService workspaceService; @Value("${minio.bucketName.collectionElement}") @@ -90,13 +80,7 @@ public class CollectionElementServiceImpl extends ServiceImpl libraryList = CopyUtil.copyList(elements, Library.class, (o, d) -> { - if (StringUtil.isNullOrEmpty(o.getLevel2Type())){ - switch(d.getLevel1Type()){ + if (StringUtil.isNullOrEmpty(o.getLevel2Type())) { + switch (d.getLevel1Type()) { case "Sketchboard": d.setLevel2Type("Outwear"); break; @@ -391,8 +375,8 @@ public class CollectionElementServiceImpl extends ServiceImpl().eq(CollectionElement::getId, sketchBoard.getSketchBoardId()).select(CollectionElement::getLevel3Type)).getLevel3Type(); - } else if ("Library".equals(sketchBoard.getDesignType())){ + } else if ("Library".equals(sketchBoard.getDesignType())) { level3Type = libraryService.getById(sketchBoard.getSketchBoardId()).getLevel3Type(); - } else if ("Generate".equals(sketchBoard.getDesignType())){ + } else if ("Generate".equals(sketchBoard.getDesignType())) { GenerateDetail generateDetail = generateDetailMapper.selectById(sketchBoard.getSketchBoardId()); assert generateDetail != null; Generate generate = generateMapper.selectById(generateDetail.getGenerateId()); @@ -1032,8 +1016,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); diff --git a/src/main/java/com/ai/da/service/impl/DesignItemServiceImpl.java b/src/main/java/com/ai/da/service/impl/DesignItemServiceImpl.java index 733e98a4..87c1f429 100644 --- a/src/main/java/com/ai/da/service/impl/DesignItemServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/DesignItemServiceImpl.java @@ -10,6 +10,7 @@ import com.ai.da.common.enums.SingleOverallEnum; import com.ai.da.common.enums.SysFileLevel2TypeEnum; import com.ai.da.common.utils.*; import com.ai.da.mapper.primary.DesignItemMapper; +import com.ai.da.mapper.primary.DesignMapper; import com.ai.da.mapper.primary.UserLikeMapper; import com.ai.da.mapper.primary.entity.*; import com.ai.da.model.dto.*; @@ -27,6 +28,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.google.common.collect.Lists; import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.SerializationUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -55,37 +57,25 @@ import java.util.stream.Collectors; */ @Slf4j @Service +@RequiredArgsConstructor public class DesignItemServiceImpl extends ServiceImpl implements DesignItemService { - @Resource - private DesignItemMapper designItemMapper; - @Resource - private SysFileService sysFileService; - @Resource - private PythonService pythonService; - @Resource - private DesignService designService; - @Resource - private DesignItemDetailService designItemDetailService; - @Resource - private LibraryModelPointService libraryModelPointService; - @Resource - private LibraryService libraryService; - @Resource - private CollectionElementService collectionElementService; - @Resource - private ITDesignPythonOutfitService designPythonOutfitService; - @Resource - private ITDesignPythonOutfitDetailService designPythonOutfitDetailService; - @Resource - private PanToneService panToneService; - @Resource - private DesignItemDetailPrintService designItemDetailPrintService; - @Resource - private MinioUtil minioUtil; - @Resource - private UserLikeService userLikeService; - @Resource - private UserLikeGroupService userLikeGroupService; + + private final CollectionElementService collectionElementService; + private final DesignMapper designMapper; + private final DesignItemMapper designItemMapper; + private final DesignItemDetailService designItemDetailService; + private final DesignItemDetailPrintService designItemDetailPrintService; + private final ITDesignPythonOutfitService designPythonOutfitService; + private final ITDesignPythonOutfitDetailService designPythonOutfitDetailService; + private final LibraryService libraryService; + private final LibraryModelPointService libraryModelPointService; + private final MinioUtil minioUtil; + private final PythonService pythonService; + private final PanToneService panToneService; + private final SysFileService sysFileService; + private final UserLikeService userLikeService; + private final UserLikeGroupService userLikeGroupService; + private final WorkspaceService workspaceService; @Value("${minio.bucketName.modifiedSketch}") private String modifiedSketchBucket; @@ -182,9 +172,9 @@ public class DesignItemServiceImpl extends ServiceImpl> priorityOffset = null; - if (!isSingleCollectionFlag){ + if (!isSingleCollectionFlag) { priorityOffset = designSingleItemDTOList.stream() .collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset)); } @@ -444,7 +435,7 @@ public class DesignItemServiceImpl extends ServiceImpl { + clone.getDesignSingleItemDTOList().forEach(i -> { // 渐变色 - if (!Objects.isNull(i.getGradient()) && !StringUtil.isNullOrEmpty(i.getGradient().getColorImg())){ + if (!Objects.isNull(i.getGradient()) && !StringUtil.isNullOrEmpty(i.getGradient().getColorImg())) { log.info("set gradient colorImage为空,便于日志打印"); i.getGradient().setColorImg(null); } // 画笔修改过的sketch - if (!StringUtil.isNullOrEmpty(i.getSketchString())){ + if (!StringUtil.isNullOrEmpty(i.getSketchString())) { log.info("set sketchString为空,便于日志打印"); i.setSketchString(null); } // 标注过的mask - if (!StringUtil.isNullOrEmpty(i.getMaskUrl())){ + if (!StringUtil.isNullOrEmpty(i.getMaskUrl())) { log.info("set labelingMask为空,便于日志打印"); i.setMaskUrl(null); } if (!Objects.isNull(i.getPartialDesign()) && - !StringUtil.isNullOrEmpty(i.getPartialDesign().getPartialDesignBase64())){ + !StringUtil.isNullOrEmpty(i.getPartialDesign().getPartialDesignBase64())) { log.info("set partialDesignBase64为空,便于日志打印"); i.getPartialDesign().setPartialDesignBase64(null); } @@ -501,7 +492,7 @@ public class DesignItemServiceImpl extends ServiceImpl> priorityOffset = new HashMap<>(); // 单品设计标志 boolean isSingleCollectionFlag = true; - if (design.getSingleOverall().equals("overall")){ + if (design.getSingleOverall().equals("overall")) { isSingleCollectionFlag = false; // 优先级 1、判断当前入参中是否有model数据 无 -> 2、判断design item 中是否有model数据 无 -> 3、从design表中拿model数据 仍然没有 报错 - if (!Objects.isNull(designSingleIncludeLayersDTO.getModelId()) && !StringUtil.isNullOrEmpty(designSingleIncludeLayersDTO.getModelType())){ + if (!Objects.isNull(designSingleIncludeLayersDTO.getModelId()) && !StringUtil.isNullOrEmpty(designSingleIncludeLayersDTO.getModelType())) { modelId = designSingleIncludeLayersDTO.getModelId(); modelType = designSingleIncludeLayersDTO.getModelType(); changeModelFlag = true; @@ -543,7 +534,7 @@ public class DesignItemServiceImpl extends ServiceImpl 不存数据库 submit -> 存数据库 List tDesignPythonOutfitDetails; JSONObject data = jsonObject.getJSONObject("data"); - if (data == null || data.toJSONString().equals("{}") ) { + if (data == null || data.toJSONString().equals("{}")) { throw new BusinessException("python response data is null"); } JSONObject outfit = data.getJSONObject("0"); @@ -626,17 +617,17 @@ public class DesignItemServiceImpl extends ServiceImpl { // 如果sketch截图不为空,则将该截图上传,并替换path - if (!StringUtil.isNullOrEmpty(item.getSketchString())){ - if (StringUtil.isNullOrEmpty(item.getPath())){ + if (!StringUtil.isNullOrEmpty(item.getSketchString())) { + if (StringUtil.isNullOrEmpty(item.getPath())) { throw new BusinessException("path.cannot.be.empty"); } String sourcePath = item.getPath(); @@ -671,7 +662,7 @@ public class DesignItemServiceImpl extends ServiceImpl { // 如果sketch截图不为空,则将该截图上传,并替换path - if (setNull){ + if (setNull) { log.info("服装类型为{}, set maskUrl为null", item.getType()); item.setMaskUrl(null); - }else { - if (!StringUtil.isNullOrEmpty(item.getMaskUrl())){ + } else { + if (!StringUtil.isNullOrEmpty(item.getMaskUrl())) { log.info("服装{} 的maskUrl不为null", item.getType()); // 由于前端不好处理这块,所以,当mask没有做任何修改的时候,仍然会传原始mask的minio地址 - if (!item.getMaskUrl().startsWith("data:image") && item.getMaskUrl().startsWith("https://")){ + if (!item.getMaskUrl().startsWith("data:image") && item.getMaskUrl().startsWith("https://")) { // 当没有修改mask时,还是用之前的mask地址 item.setMaskUrl(item.getMaskMinioUrl()); log.info("服装{} 的maskUrl没有被编辑", item.getType()); - }else { + } else { // 将原图地址作为修改后的图片地址,放在不同的桶 String path = minioUtil.base64UploadToPath(item.getMaskUrl(), clothingBucket, "labelingMask/" + UUID.randomUUID()); log.info("服装{} 的maskUrl已被编辑, 新的path为{}", item.getType(), path); - if (StringUtil.isNullOrEmpty(path)){ + if (StringUtil.isNullOrEmpty(path)) { log.error("标注的mask图片上传失败"); throw new BusinessException("image.modify.failed"); } @@ -710,53 +701,55 @@ public class DesignItemServiceImpl extends ServiceImpl { PartialDesignDTO partialDesignDTO = item.getPartialDesign(); if (!Objects.isNull(item.getPartialDesign()) - && !StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignBase64())){ - String path ; - if (!StringUtil.isNullOrEmpty(partialDesignDTO.getPartialDesignMinioPath()) && !preview){ + && !StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignBase64())) { + String path; + if (!StringUtil.isNullOrEmpty(partialDesignDTO.getPartialDesignMinioPath()) && !preview) { String sourcePath = partialDesignDTO.getPartialDesignMinioPath(); path = sourcePath.substring(sourcePath.indexOf("/") + 1, sourcePath.lastIndexOf(".")); - }else if (preview){ + } else if (preview) { path = accountId + "/" + CommonConstant.PARTIAL_DESIGN_PREVIEW_FILENAME + "/" + UUID.randomUUID(); - }else { + } else { path = accountId + "/" + CommonConstant.PARTIAL_DESIGN_FILENAME + "/" + UUID.randomUUID(); } String newPath = minioUtil.base64UploadToPath(partialDesignDTO.getPartialDesignBase64(), partialDesignBucket, path); - if (StringUtil.isNullOrEmpty(newPath)){ + if (StringUtil.isNullOrEmpty(newPath)) { log.error("局部design图片上传失败"); throw new BusinessException("partial.design.failed"); } item.getPartialDesign().setPartialDesignMinioPath(newPath); - }else if (Objects.isNull(item.getPartialDesign()) - || StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())){ + } else if (Objects.isNull(item.getPartialDesign()) + || StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) { item.setPartialDesign(new PartialDesignDTO(null)); } }); } @Override - public Map> setPriorityAndUndividedLayer(JSONArray layers){ + public Map> setPriorityAndUndividedLayer(JSONArray layers) { HashMap> priorityAndLayer = new HashMap<>(); for (int i = 0; i < layers.size(); i++) { JSONObject jsonObject = layers.getJSONObject(i); String priority = jsonObject.getString("priority"); String category = jsonObject.getString("image_category").split("_")[0]; - if (!category.equals("body") && !priorityAndLayer.containsKey(priority)) priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url"))); + if (!category.equals("body") && !priorityAndLayer.containsKey(priority)) + priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url"))); } return priorityAndLayer; } // 由于在design过程中没有priority 优先级的概念,并且在design时,不会出现上下两件使用相同服装类型的情况,所以这里依然保留这个方法。 @Override - public Map setTypeAndUndividedLayer(JSONArray layers){ + public Map setTypeAndUndividedLayer(JSONArray layers) { HashMap typeAndLayer = new HashMap<>(); for (int i = 0; i < layers.size(); i++) { JSONObject jsonObject = layers.getJSONObject(i); String category = jsonObject.getString("image_category").split("_")[0]; - if (!category.equals("body") && !typeAndLayer.containsKey(category)) typeAndLayer.put(category, jsonObject.getString("pattern_image_url")); + if (!category.equals("body") && !typeAndLayer.containsKey(category)) + typeAndLayer.put(category, jsonObject.getString("pattern_image_url")); } return typeAndLayer; } @@ -767,18 +760,18 @@ public class DesignItemServiceImpl extends ServiceImpl { TDesignPythonOutfitDetail detail = designPythonOutfitDetailService.getById(layer.getId()); - if (Objects.isNull(detail)){ + if (Objects.isNull(detail)) { log.error(layer.getImageCategory() + " layer does not exists!"); throw new BusinessException("layers.does.not.exists"); } @@ -833,24 +826,25 @@ public class DesignItemServiceImpl extends ServiceImpl designItemDetailPrints, String printType, Map designItemDetailTypeIdMap, - String timeZone){ + String timeZone) { // todo 这边这样做对吗 DesignSinglePrintDTO printObject; - if (printType.equals("print")){ + if (printType.equals("print")) { printObject = designSingleItem.getPrintObject(); - }else { + } else { printObject = designSingleItem.getTrims(); } @@ -971,9 +966,9 @@ public class DesignItemServiceImpl extends ServiceImpl selectDesignIdById(List designItemIdList){ + public List selectDesignIdById(List designItemIdList) { QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.in("id",designItemIdList); + queryWrapper.in("id", designItemIdList); return designItemMapper.selectList(queryWrapper); } - private void saveCollectionElement(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO){ + private void saveCollectionElement(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) { ArrayList libraries = new ArrayList<>(); // 添加sketch到library designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(designSingleItem -> { - if (!StringUtil.isNullOrEmpty(designSingleItem.getDesignType()) && designSingleItem.getDesignType().equals("Collection")){ + if (!StringUtil.isNullOrEmpty(designSingleItem.getDesignType()) && designSingleItem.getDesignType().equals("Collection")) { String path = minioUtil.getPreSignedUrl(designSingleItem.getPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME); try { String md5 = MD5Utils.encryptFile(path, false); @@ -1026,13 +1021,13 @@ public class DesignItemServiceImpl extends ServiceImpl { - if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")){ + if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) { String path = minioUtil.getPreSignedUrl(print.getMinIOPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME); try { String md5 = MD5Utils.encryptFile(path, false); @@ -1057,7 +1052,7 @@ public class DesignItemServiceImpl extends ServiceImpl accountIds){ + public Long getCountByUserAndTime(String startTime, String endTime, List accountIds) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.gt("create_date", startTime) .lt("create_date", endTime) .select("count(distinct design_id) as count"); - if (!accountIds.isEmpty()){ + if (!accountIds.isEmpty()) { queryWrapper.in("account_id", accountIds); } @@ -1091,7 +1086,7 @@ public class DesignItemServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.ne("design_item_id", -1) .ne("converted", 1) - .groupBy(Arrays.asList("design_outfit_id"," design_item_id")).select(" design_item_id", "design_outfit_id"); + .groupBy(Arrays.asList("design_outfit_id", " design_item_id")).select(" design_item_id", "design_outfit_id"); List userLikes = userLikeMapper.selectList(queryWrapper); // List> userLikes = userLikeMapper.selectMaps(queryWrapper); @@ -1158,10 +1154,10 @@ public class DesignItemServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.ne("design_item_id", -1) .ne("converted", 1) - .groupBy(Arrays.asList("design_outfit_id"," design_item_id")).select(" design_item_id", "design_outfit_id"); + .groupBy(Arrays.asList("design_outfit_id", " design_item_id")).select(" design_item_id", "design_outfit_id"); List userLikes = userLikeMapper.selectList(queryWrapper); log.info("userLike 总数 :{}", userLikes.size()); @@ -1207,7 +1203,7 @@ public class DesignItemServiceImpl extends ServiceImpl implements DesignService { - @Resource - private DesignMapper designMapper; - @Resource - private CollectionElementService collectionElementService; - @Resource - private CollectionService collectionService; - @Resource - private PythonService pythonService; - @Resource - private LibraryService libraryService; - @Resource - private GenerateDetailMapper generateDetailMapper; - @Resource - private DesignItemService designItemService; - @Resource - private DesignItemDetailService designItemDetailService; - @Resource - private FileProperties fileProperties; - @Resource - private UserLikeGroupService userLikeGroupService; - @Resource - private UserLikeService userLikeService; - @Resource - private SysFileService sysFileService; - @Resource - private TCollectionElementRelationService tCollectionElementRelationService; - @Resource - private ITDesignPythonOutfitService designPythonOutfitService; - @Resource - private ITDesignPythonOutfitDetailService designPythonOutfitDetailService; - @Resource - private PanToneService panToneService; - @Resource - private PythonTAllInfoService pythonTAllInfoService; - @Resource - private DesignItemDetailPrintService designItemDetailPrintService; - - @Resource - private TDesignPythonOutfitMapper designPythonOutfitMapper; - @Resource - private DesignItemDetailMapper designItemDetailMapper; - @Resource - private ToProductImageResultMapper toProductImageResultMapper; - @Resource - private ToProductElementMapper toProductElementMapper; - @Resource - private MoodboardPositionMapper moodboardPositionMapper; - @Resource - private CollectionSortService collectionSortService; + private final CloudTaskMapper cloudTaskMapper; + private final CloudTaskService cloudTaskService; + private final CreditsService creditsService; + private final CollectionElementService collectionElementService; + private final CollectionService collectionService; + private final CollectionSortService collectionSortService; + private final ColorLoopUpTableService colorLoopUpTableService; + private final CollectionElementRelModelMapper collectionElementRelModelMapper; + private final DesignBatchMapper designBatchMapper; + private final DesignMapper designMapper; + private final DesignItemService designItemService; + private final DesignItemDetailMapper designItemDetailMapper; + private final DesignItemDetailService designItemDetailService; + private final DesignItemDetailPrintService designItemDetailPrintService; + private final TDesignPythonOutfitMapper designPythonOutfitMapper; + private final ITDesignPythonOutfitService designPythonOutfitService; + private final ITDesignPythonOutfitDetailService designPythonOutfitDetailService; + private final FileProperties fileProperties; + private final GenerateDetailMapper generateDetailMapper; + private final LibraryService libraryService; + private final MinioUtil minioUtil; + private final MoodboardPositionMapper moodboardPositionMapper; + private final ProjectService projectService; + private final PythonService pythonService; + private final PanToneMapper panToneMapper; + private final PanToneService panToneService; + private final PoseTransformationMapper poseTransformationMapper; + private final PythonTAllInfoService pythonTAllInfoService; + private final RedisUtil redisUtil; + private final SysFileService sysFileService; + private final TCollectionElementRelationService tCollectionElementRelationService; + private final ToProductImageResultMapper toProductImageResultMapper; + private final ToProductElementMapper toProductElementMapper; + private final ToProductImageRecordMapper toProductImageRecordMapper; + private final UserLikeGroupService userLikeGroupService; + private final UserLikeService userLikeService; + private final UserBehaviorMapper userBehaviorMapper; + private final UserPreferenceLogMapper userPreferenceLogMapper; + private final WorkspaceService workspaceService; @Value("${minio.endpoint}") private String endpoint; @@ -123,34 +115,6 @@ public class DesignServiceImpl extends ServiceImpl impleme @Value("${access.python.port:''}") private String accessPythonPort; - @Resource - private RedisUtil redisUtil; - @Resource - private PoseTransformationMapper poseTransformationMapper; - - @Resource - private DesignBatchMapper designBatchMapper; - @Resource - private ProjectService projectService; - @Resource - private WorkspaceService workspaceService; - @Resource - private CloudTaskMapper cloudTaskMapper; - @Resource - private CloudTaskService cloudTaskService; - @Resource - private CreditsService creditsService; - @Resource - private ToProductImageRecordMapper toProductImageRecordMapper; - @Resource - private ColorLoopUpTableService colorLoopUpTableService; - @Resource - private UserBehaviorMapper userBehaviorMapper; - @Resource - private UserPreferenceLogMapper userPreferenceLogMapper; - @Resource - private CollectionElementRelModelMapper collectionElementRelModelMapper; - private final ConcurrentHashMap> designContext = new ConcurrentHashMap<>(); @@ -323,11 +287,11 @@ public class DesignServiceImpl extends ServiceImpl impleme // List reLationelementIds = reLationelements.stream().map(CollectionElement::getId).collect(Collectors.toList()); // handleCollectionElementRelation(collectionId, (null == collectionIdParam) ? false : true, reLationelementIds); // //保存python返回信息 -//// return savePythonDesignItemAndDetail(pythonObjects,designId,collectionId,userInfo,designDTO.getTimeZone(),responseJSONObject); + + /// / return savePythonDesignItemAndDetail(pythonObjects,designId,collectionId,userInfo,designDTO.getTimeZone(),responseJSONObject); // //保存designItem 和detail // return saveDesignItemAndDetail(pythonObjects, designId, collectionId, userInfo, designDTO.getTimeZone()); // } - private String designOrRedesignOperateNew(DesignCollectionDTO designDTO, AuthPrincipalVo userInfo, Long collectionIdParam, ValidateElementVO elementVO) { // if (CollectionUtil.isNotEmpty(designDTO.getSketchBoards())) { @@ -342,7 +306,7 @@ public class DesignServiceImpl extends ServiceImpl impleme Long collectionId; if (null == collectionIdParam) { collectionId = collectionService.saveCollection(userInfo.getId(), designDTO.getTimeZone(), designDTO.getMoodTemplateId(), designDTO.getMoodboardPosition()); - }else { + } else { collectionId = collectionIdParam; } List elementIds = getElementId(elementVO); @@ -388,7 +352,7 @@ public class DesignServiceImpl extends ServiceImpl impleme // 生成library - 根据设计元素生成设计库信息 startTime = System.currentTimeMillis(); - String ageGroup = getAgeGroupByProjectOrCollectionId(designDTO.getProjectId(), collectionId); + String ageGroup = workspaceService.getAgeGroupByProjectOrCollectionId(designDTO.getProjectId(), collectionId); generateLibrary(elementVO, designDTO.getTimeZone(), ageGroup); //处理关联关系,修复element覆盖得情况 // List relationElements = collectionElementService.getByOnlyCollectionId(collectionId); @@ -419,124 +383,6 @@ public class DesignServiceImpl extends ServiceImpl impleme return requestId; } - public String getAgeGroupByProjectOrCollectionId(Long projectId, Long collectionId){ - if (Objects.nonNull(projectId)){ - Workspace workspace = workspaceService.getWSByProjectId(projectId); - if (workspace != null && !StringUtil.isNullOrEmpty(workspace.getAgeGroup())){ - return workspace.getAgeGroup(); - } - } else if (Objects.nonNull(collectionId) && collectionId != 0){ - List byCollectionId = collectionElementService.getByCollectionId(collectionId); - if (byCollectionId != null && !byCollectionId.isEmpty()){ - projectId = byCollectionId.get(0).getProjectId(); - if (projectId != null && projectId != 0L){ - Workspace workspace = workspaceService.getWSByProjectId(projectId); - if (workspace != null && !StringUtil.isNullOrEmpty(workspace.getAgeGroup())){ - return workspace.getAgeGroup(); - } - } - } - } - return AgeGroup.ADULT.getValue(); - } - - @Override - public void parseMoodboardPosition(String moodboardPosition, Long collectionIdParam) { - if (!StringUtils.isEmpty(moodboardPosition)) { - // 将 JSON 字符串解析为 JSONObject - JSONObject moodboardPositionJson = JSONObject.parseObject(moodboardPosition); - - // 准备保存的 MoodboardPosition 列表 - List moodboardPositions = new ArrayList<>(); - - // 遍历 JSON 对象的 key(即样式类型) - for (String key : moodboardPositionJson.keySet()) { - // 特殊处理 "class" 字段 - if ("class".equals(key)) { - // 获取 "class" 字段的值并将其转为 List - JSONArray classArray = moodboardPositionJson.getJSONArray(key); - if (classArray != null) { - - for (int j = 0; j < classArray.size(); j++) { - // 将 classList 存入 MoodboardPosition(或者其他结构) - - MoodboardPosition position = new MoodboardPosition() - .setCollectionId(collectionIdParam) // 关联 Collection ID - .setType(key) // 样式类型 - .setStyleData(classArray.getString(j)) // 设置 class 字段 - .setSequence(j) // 根据索引值设置顺序 - .setCreateTime(LocalDateTime.now()) // 创建时间 - .setUpdateTime(LocalDateTime.now()); // 更新时间 - - // 添加到列表中 - moodboardPositions.add(position); - } - } - continue; // 跳过 "class" 字段的常规处理 - } - - JSONArray styleArray = moodboardPositionJson.getJSONArray(key); - if (styleArray != null) { - for (int i = 0; i < styleArray.size(); i++) { - // 获取当前样式数据 - JSONObject styleData = styleArray.getJSONObject(i); - - // 构建 MoodboardPosition 实例 - MoodboardPosition position = new MoodboardPosition() - .setCollectionId(collectionIdParam) // 关联 Collection ID - .setType(key) // 样式类型 - .setStyleData(styleData.toJSONString()) // 样式数据存为 JSON 字符串 - .setSequence(i) // 根据索引值设置顺序 - .setCreateTime(LocalDateTime.now()) // 创建时间 - .setUpdateTime(LocalDateTime.now()); // 更新时间 - - // 添加到列表中 - moodboardPositions.add(position); - } - } - } - // 如果解析结果非空,保存到数据库 - if (!moodboardPositions.isEmpty()) { - for (MoodboardPosition position : moodboardPositions) { - moodboardPositionMapper.insert(position); - } - log.info("成功解析并保存 {} 条 MoodboardPosition 数据", moodboardPositions.size()); - } else { - log.warn("未找到可保存的 MoodboardPosition 数据"); - } - } else { - log.warn("传入的 moodboardPosition 字段为空"); - } - } - - - @Override - public void relationImageId(DesignPythonObjects pythonObjects) { - if (Objects.isNull(pythonObjects)) { - return; - } - pythonObjects.getObjects().forEach( - o -> { - for (DesignPythonItem item : o.getItems()) { - List list = new ArrayList<>(); - list.add(1L); - list.add(1L); - item.setOffset(list); - item.setResize_scale(new Float[]{1.0f,1.0f}); - String path = item.getPath(); - if (StringUtils.isEmpty(path)) { - String bodyPath = item.getBody_path(); - Long imageId = pythonTAllInfoService.getImageIdByPath(bodyPath); - item.setImage_id(imageId); - } else { - Long imageId = pythonTAllInfoService.getImageIdByPath(path); - item.setImage_id(imageId); - } - } - } - ); - } - @Override public List sketchesBoundingBox(ReDesignCollectionDTO reDesignDTO) { List sketchBoards = new ArrayList<>(); @@ -610,18 +456,18 @@ public class DesignServiceImpl extends ServiceImpl impleme if (url.contains(".")) { String[] split = url.split("\\."); vo.setUrlWithWhiteSide(minioUtil.getPreSignedUrl(split[0] + "-show." + split[1], 24 * 60, true)); - }else { + } else { vo.setUrlWithWhiteSide(minioUtil.getPreSignedUrl(url + "-show", 24 * 60, true)); } result.add(vo); - }else if (collectionSketchDTO.getDesignType().equals(DesignTypeEnum.GENERATE.getRealName())) { + } else if (collectionSketchDTO.getDesignType().equals(DesignTypeEnum.GENERATE.getRealName())) { GenerateDetail generateDetail = generateDetailMapper.selectById(collectionSketchDTO.getSketchBoardId()); libraryService.processSketchBoards(generateDetail.getUrl(), collectionSketchDTO.getLevel2Type()); String url = generateDetail.getUrl(); if (url.contains(".")) { String[] split = url.split("\\."); vo.setUrlWithWhiteSide(minioUtil.getPreSignedUrl(split[0] + "-show." + split[1], 24 * 60, true)); - }else { + } else { vo.setUrlWithWhiteSide(minioUtil.getPreSignedUrl(url + "-show", 24 * 60, true)); } result.add(vo); @@ -639,7 +485,7 @@ public class DesignServiceImpl extends ServiceImpl impleme if (url.contains(".")) { String[] split = url.split("\\."); vo.setUrlWithWhiteSide(minioUtil.getPreSignedUrl(split[0] + "-show." + split[1], 24 * 60, true)); - }else { + } else { vo.setUrlWithWhiteSide(minioUtil.getPreSignedUrl(url + "-show", 24 * 60, true)); } result.add(vo); @@ -662,7 +508,7 @@ public class DesignServiceImpl extends ServiceImpl impleme list.add(1L); list.add(1L); item.setOffset(list); - item.setResize_scale(new Float[]{1.0f,1.0f}); + item.setResize_scale(new Float[]{1.0f, 1.0f}); String path = item.getPath(); if (StringUtils.isEmpty(path)) { String bodyPath = item.getBody_path(); @@ -757,8 +603,6 @@ public class DesignServiceImpl extends ServiceImpl impleme LocalCacheUtils.setDesignProcessCache(userId, saveNames); } - @Resource - private MinioUtil minioUtil; private DesignCollectionVO savePythonDesignItemAndDetailSingle(DesignPythonObjects pythonObjects, Long designId, Long collectionId, AuthPrincipalVo userInfo, String timeZone, JSONObject outfit, String singleOverall, Map context) { Object designCollectionVO = context.get("DesignCollectionVO"); @@ -769,7 +613,7 @@ public class DesignServiceImpl extends ServiceImpl impleme response.setDesignId(designId); response.setCollectionId(collectionId); response.setProcessId(pythonObjects.getProcess_id()); - }else { + } else { response = (DesignCollectionVO) designCollectionVO; designCollectionItems = response.getDesignCollectionItems(); } @@ -882,7 +726,7 @@ public class DesignServiceImpl extends ServiceImpl impleme designItemDetail.setCollectionElementId(detail.getElementId()); designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone)); log.info("detail.getType():{}", detail.getType()); - if (!detail.getType().equals("Body")){ + if (!detail.getType().equals("Body")) { log.info("layer : {}", typeAndUndividedLayer.get(designItemDetail.getType())); designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType())); } @@ -893,7 +737,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } designItemDetail.setIconPath(detail.getIcon()); designItemDetail.setPriority(typePriority.get(detail.getType().toLowerCase())); - if (!detail.getType().equals("Body")){ + if (!detail.getType().equals("Body")) { DesignPythonItemPrint printObject = detail.getPrint().getOverall(); // designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath()); designItemDetail.setPrintPath(CollectionUtils.isEmpty(printObject.getPrint_path_list()) ? "" : printObject.getPrint_path_list().get(0)); @@ -928,6 +772,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } return response; } + private DesignCollectionVO savePythonDesignItemAndDetail(DesignPythonObjects pythonObjects , Long designId, Long collectionId, AuthPrincipalVo userInfo, String timeZone, JSONObject responseJSONObject, String singleOverall) { DesignCollectionVO response = new DesignCollectionVO(); @@ -1005,8 +850,8 @@ public class DesignServiceImpl extends ServiceImpl impleme List designItemDetails = Lists.newArrayList(); Map typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0], - d -> Math.abs(d.getPriority()), - (existing, replacement) -> replacement)); + d -> Math.abs(d.getPriority()), + (existing, replacement) -> replacement)); Map typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers); for (DesignPythonItem detail : item.getItems()) { if (null == detail) { @@ -1018,7 +863,7 @@ public class DesignServiceImpl extends ServiceImpl impleme designItemDetail.setDesignItemId(designItemId); designItemDetail.setCollectionElementId(detail.getElementId()); designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone)); - if (!detail.getType().equals("Body")){ + if (!detail.getType().equals("Body")) { designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType())); } @@ -1029,7 +874,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } designItemDetail.setIconPath(detail.getIcon()); designItemDetail.setPriority(typePriority.get(detail.getType().toLowerCase())); - if (!detail.getType().equals("Body")){ + if (!detail.getType().equals("Body")) { DesignPythonItemPrint printObject = detail.getPrint().getOverall(); // designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath()); designItemDetail.setPrintPath(CollectionUtils.isEmpty(printObject.getPrint_path_list()) ? "" : printObject.getPrint_path_list().get(0)); @@ -1197,7 +1042,7 @@ public class DesignServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public DesignLikeVO like(DesignLikeDTO designLikeDTO) { - Long userGroupId = designLikeDTO.getUserGroupId();; + Long userGroupId = designLikeDTO.getUserGroupId(); Long groupDetailId; AuthPrincipalVo userInfo = UserContext.getUserHolder(); DesignItem designItem = designItemService.getById(designLikeDTO.getDesignItemId()); @@ -1389,7 +1234,7 @@ public class DesignServiceImpl extends ServiceImpl impleme @Override public String generateHighDesign(GenerateHighDesignDTO generateHighDesignDTO) { DesignItem designItem = designItemService.getById(generateHighDesignDTO.getDesignItemId()); - if (Objects.isNull(designItem)){ + if (Objects.isNull(designItem)) { throw new BusinessException("design.item.does.not.exist"); } String highUrl = pythonService.generateHighDesign(designItem.getDesignUrl()); @@ -1401,7 +1246,7 @@ public class DesignServiceImpl extends ServiceImpl impleme @Override public Boolean deleteHighDesign(GenerateHighDesignDTO generateHighDesignDTO) { DesignItem designItem = designItemService.getById(generateHighDesignDTO.getDesignItemId()); - if (Objects.isNull(designItem)){ + if (Objects.isNull(designItem)) { throw new BusinessException("design.item.does.not.exist"); } if (StringUtils.isEmpty(designItem.getHighDesignUrl())) { @@ -1444,7 +1289,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } // 为没有优先级的衣服添加优先级 - if (!StringUtil.isNullOrEmpty(design.getSingleOverall()) && design.getSingleOverall().equals("overall")){ + if (!StringUtil.isNullOrEmpty(design.getSingleOverall()) && design.getSingleOverall().equals("overall")) { designItemDetailService.setDesignItemDetailPriority(designItemDetails); } @@ -1456,7 +1301,7 @@ public class DesignServiceImpl extends ServiceImpl impleme response.setHighDesignUrl(designItem.getHighDesignUrl()); List filterDetail = designItemDetails.stream() .filter(f -> OUTWEAR_DRESS_BLOUSE.contains(f.getType()) || SKIRT_TROUSERS.contains(f.getType()) - || TOPS.contains(f.getType()) || BOTTOMS.contains(f.getType())|| OTHERS.contains(f.getType())) + || TOPS.contains(f.getType()) || BOTTOMS.contains(f.getType()) || OTHERS.contains(f.getType())) .collect(Collectors.toList()); response.setClothes(CopyUtil.copyList(filterDetail, DesignItemClothesDetailVO.class, (o, d) -> { d.setId(o.getId()); @@ -1465,10 +1310,10 @@ public class DesignServiceImpl extends ServiceImpl impleme d.setScope(o.getPath().startsWith("aida-sys-image") ? "sys" : "user"); d.setLevel1Type(converTypeToLevel1(o.getType())); d.setGradient(JSONObject.parseObject(o.getGradientString(), Gradient.class)); - if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())){ + if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())) { d.setUndividedLayer(minioUtil.getPreSignedUrl(o.getUndividedLayer(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); } - if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())){ + if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())) { d.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(o.getUndividedLayerWithSinglePrint(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); } // 根据designItemDetailId获取印花 @@ -1489,7 +1334,7 @@ public class DesignServiceImpl extends ServiceImpl impleme String preSigned = StringUtil.isNullOrEmpty(o.getPartialDesign()) ? null : minioUtil.getPreSignedUrl(o.getPartialDesign(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME); d.setPartialDesign(new PartialDesignDTO(o.getPartialDesign(), preSigned)); DesignItemDetailCanvas designItemDetailCanvas = designItemDetailService.getDIDCByDesignItemDetailId(o.getId()); - if (Objects.nonNull(designItemDetailCanvas)){ + if (Objects.nonNull(designItemDetailCanvas)) { d.setCanvasId(designItemDetailCanvas.getExportFileId()); } })); @@ -1627,7 +1472,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } }); - if (!CollectionUtil.isEmpty(designItemDetailIdColor)){ + if (!CollectionUtil.isEmpty(designItemDetailIdColor)) { Map pantoneByRgbBatch = panToneService.getPantoneByRgbBatch(new ArrayList<>(designItemDetailIdColor.values())); designItemDetailVO.getClothes().forEach(c -> { PantoneVO pantoneVO = pantoneByRgbBatch.get(designItemDetailIdColor.get(c.getId())); @@ -1649,13 +1494,13 @@ public class DesignServiceImpl extends ServiceImpl impleme if (flag) { // 1、判断designPythonOutfitId查出的图层信息是否为空(不允许为空,系统内部错误) List details = designPythonOutfitDetailService.getDetailByDesignPythonOutfitId(designPythonOutfit.getId()); - if (Objects.isNull(details)){ + if (Objects.isNull(details)) { log.error("Layer information is empty! DesignPythonOutfitId is " + designPythonOutfit.getId()); throw new BusinessException("layer.information.not.found"); } // 为没有优先级的图层添加优先级 - if (!StringUtil.isNullOrEmpty(design.getSingleOverall()) && design.getSingleOverall().equals("overall")){ + if (!StringUtil.isNullOrEmpty(design.getSingleOverall()) && design.getSingleOverall().equals("overall")) { designPythonOutfitDetailService.setDesignPythonOutfitDetailPriority(details); } @@ -1705,16 +1550,16 @@ public class DesignServiceImpl extends ServiceImpl impleme designSinglePrint.setMinIOPath(detailPrint.getPath()); designSinglePrint.setIfSingle(detailPrint.getSingleOrOverall().equals("single") ? Boolean.TRUE : Boolean.FALSE); List scales; - try{ + try { scales = JSONArray.parseArray(detailPrint.getScale(), Float.class); - }catch (JSONException e){ + } catch (JSONException e) { Float scale = Float.valueOf(detailPrint.getScale()); - if (designSinglePrint.getIfSingle()){ + if (designSinglePrint.getIfSingle()) { List printWAndH = minioUtil.getImagesWidthAndHeight(detailPrint.getPath()); List sketchWAndH = minioUtil.getImagesWidthAndHeight(sketchPath); scales = Arrays.asList(scale * printWAndH.get(0) / sketchWAndH.get(0), scale * printWAndH.get(1) / sketchWAndH.get(1)); - }else { + } else { scales = Arrays.asList(scale, scale); } } @@ -1727,15 +1572,15 @@ public class DesignServiceImpl extends ServiceImpl impleme // if (print.getSingleOrOverall().equals("single")) { List scales; Boolean ifSingle = print.getSingleOrOverall().equals("single") ? Boolean.TRUE : Boolean.FALSE; - try{ + try { scales = JSONArray.parseArray(print.getScale(), Float.class); - }catch (JSONException e){ + } catch (JSONException e) { Float scale = Float.valueOf(print.getScale()); - if (ifSingle){ + if (ifSingle) { List printWAndH = minioUtil.getImagesWidthAndHeight(print.getPath()); List sketchWAndH = minioUtil.getImagesWidthAndHeight(sketchPath); scales = Arrays.asList(scale * printWAndH.get(0) / sketchWAndH.get(0), scale * printWAndH.get(1) / sketchWAndH.get(1)); - }else { + } else { scales = Arrays.asList(scale, scale); } } @@ -1756,27 +1601,27 @@ public class DesignServiceImpl extends ServiceImpl impleme } @Override - public List getModel(List designItemIdList){ + public List getModel(List designItemIdList) { ArrayList models = new ArrayList<>(); List designIdById = designItemService.selectDesignIdById(designItemIdList); - if (CollectionUtil.isEmpty(designIdById)){ + if (CollectionUtil.isEmpty(designIdById)) { log.info("according to the designItemIdList cannot find the designIdList"); throw new BusinessException("design.not.found"); } List designIdList = designIdById.stream().map(DesignItem::getDesignId).collect(Collectors.toList()); List designs = selectList(designIdList); - if (CollectionUtil.isEmpty(designIdList)){ + if (CollectionUtil.isEmpty(designIdList)) { log.info("according to the designIdList cannot find the design"); throw new BusinessException("design.not.found"); } List modelFromLibIds = designs.stream().filter(design -> design.getModelType().equals("Library")).map(Design::getTemplateId).collect(Collectors.toList()); - if (!CollectionUtil.isEmpty(modelFromLibIds)){ + if (!CollectionUtil.isEmpty(modelFromLibIds)) { models.addAll(libraryService.getByIds(modelFromLibIds).stream() .map(d -> minioUtil.getPreSignedUrl(d.getUrl(), 24 * 60)) .collect(Collectors.toList())); } List modelFromSysIds = designs.stream().filter(design -> design.getModelType().equals("System")).map(Design::getTemplateId).collect(Collectors.toList()); - if (!CollectionUtil.isEmpty(modelFromSysIds)){ + if (!CollectionUtil.isEmpty(modelFromSysIds)) { models.addAll(sysFileService.getByIds(modelFromSysIds).stream() .map(d -> minioUtil.getPreSignedUrl(d.getUrl(), 24 * 60)) .collect(Collectors.toList())); @@ -1785,21 +1630,21 @@ public class DesignServiceImpl extends ServiceImpl impleme return models; } - private List selectList(List designIdList){ + private List selectList(List designIdList) { QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.in("id",designIdList); + queryWrapper.in("id", designIdList); return designMapper.selectList(queryWrapper); } // 查询指定用户在指定时间内使用了多少次design - public Long getCountByUserAndTime(String startTime, String endTime, List accountIds){ + public Long getCountByUserAndTime(String startTime, String endTime, List accountIds) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.gt("create_date", startTime) .lt("create_date", endTime) .select("count(id) as count"); - if (!Objects.isNull(accountIds) && !accountIds.isEmpty() ){ + if (!Objects.isNull(accountIds) && !accountIds.isEmpty()) { queryWrapper.in("account_id", accountIds); } @@ -1807,7 +1652,7 @@ public class DesignServiceImpl extends ServiceImpl impleme if (result != null && !result.isEmpty()) { Object countObj = result.get(0).get("count"); return (Long) countObj; - }else { + } else { return 0L; } } @@ -1885,7 +1730,7 @@ public class DesignServiceImpl extends ServiceImpl impleme //design String taskId = designBatch(designDTO, userInfo, null, elementVO, cloudTaskDTO, projectId); return taskId; - }else if (cloudTaskDTO.getBuildType().equals(BuildType.TO_PRODUCT_IMAGE.getValue())) { + } else if (cloudTaskDTO.getBuildType().equals(BuildType.TO_PRODUCT_IMAGE.getValue())) { ToProductImageDTO toProductImageDTO = cloudTaskDTO.getToProductImage(); // 判断用户当前积分是否够本次生成消耗 Boolean preDeduction = creditsService.creditsPreDeduction(CreditsEventsEnum.TO_PRODUCT_IMAGE, cloudTaskDTO.getNums()); @@ -1974,17 +1819,17 @@ public class DesignServiceImpl extends ServiceImpl impleme if (design.getSingleOverall().equals("single")) { productType = "single"; sb.append(collect); - }else { + } else { if (collect.contains("Tops")) { sb.append("a handsome man,"); - }else { + } else { sb.append("a beautiful women,"); } sb.append("wearing ").append(collect); } if (StringUtils.isEmpty(prompt)) { sb.append(",high quality clothing details,8K realistic,HDR"); - }else { + } else { sb.append(",high quality clothing details,").append(prompt).append(",8K realistic,HDR"); } // 走模型 @@ -2017,17 +1862,17 @@ public class DesignServiceImpl extends ServiceImpl impleme toProductImageResult.setImageStrength(randomFromRange); toProductImageResultMapper.insert(toProductImageResult); result.add(toProductImageResult); - i ++; + i++; } pythonService.toProductImageBatch(batchTaskId, paramList, userHolder.getId().toString()); // 添加需要扣除的积分到预扣除区 creditsService.addRecordToCreditsDeduction(userHolder.getId(), batchTaskId, CreditsEventsEnum.TO_PRODUCT_IMAGE, cloudTaskDTO.getNums()); // 预插入数据到积分变更表 - creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.TO_PRODUCT_IMAGE.getName(), batchTaskId, true, null,cloudTaskDTO.getNums() ); - }else { + creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.TO_PRODUCT_IMAGE.getName(), batchTaskId, true, null, cloudTaskDTO.getNums()); + } else { if (StringUtils.isEmpty(prompt)) { sb.append(",high quality clothing details,8K realistic,HDR"); - }else { + } else { sb.append(",high quality clothing details,").append(prompt).append(",8K realistic,HDR"); } ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageVO.getElementId()); @@ -2061,19 +1906,19 @@ public class DesignServiceImpl extends ServiceImpl impleme toProductImageResult.setImageStrength(randomFromRange); toProductImageResultMapper.insert(toProductImageResult); result.add(toProductImageResult); - i ++; + i++; } pythonService.toProductImageBatch(batchTaskId, paramList, userHolder.getId().toString()); // 添加需要扣除的积分到预扣除区 creditsService.addRecordToCreditsDeduction(userHolder.getId(), batchTaskId, CreditsEventsEnum.TO_PRODUCT_IMAGE, cloudTaskDTO.getNums()); // 预插入数据到积分变更表 - creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.TO_PRODUCT_IMAGE.getName(), batchTaskId, true, null,cloudTaskDTO.getNums() ); + creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.TO_PRODUCT_IMAGE.getName(), batchTaskId, true, null, cloudTaskDTO.getNums()); } sb = new StringBuilder("The best quality, masterpiece, real image."); } return batchTaskId; - }else if (cloudTaskDTO.getBuildType().equals(BuildType.RELIGHT.getValue())) { + } else if (cloudTaskDTO.getBuildType().equals(BuildType.RELIGHT.getValue())) { ToProductImageDTO toProductImageDTO = cloudTaskDTO.getToProductImage(); // 判断用户当前积分是否够本次生成消耗 Boolean preDeduction = creditsService.creditsPreDeduction(CreditsEventsEnum.RELIGHT, cloudTaskDTO.getNums()); @@ -2140,7 +1985,7 @@ public class DesignServiceImpl extends ServiceImpl impleme String s = ""; if (!StringUtil.isNullOrEmpty(prompt)) { s = pythonService.promptTranslate(prompt); - }else { + } else { s = "Snow moutain, snowy day, natural light"; } Map toProductImageVOIntegerMap = allocateElements(toProductImageDTO.getToProductImageVOList(), cloudTaskDTO.getNums()); @@ -2188,18 +2033,18 @@ public class DesignServiceImpl extends ServiceImpl impleme toProductImageResult.setDirection(toProductImageDTO.getDirection()); toProductImageResultMapper.insert(toProductImageResult); result.add(toProductImageResult); - i ++; + i++; } // 走模型 pythonService.relightBatch(batchTaskId, paramList, userHolder.getId().toString()); // 添加需要扣除的积分到预扣除区 creditsService.addRecordToCreditsDeduction(userHolder.getId(), batchTaskId, CreditsEventsEnum.RELIGHT, cloudTaskDTO.getNums()); // 预插入数据到积分变更表 - creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.RELIGHT.getName(), batchTaskId, true, null,cloudTaskDTO.getNums() ); + creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.RELIGHT.getName(), batchTaskId, true, null, cloudTaskDTO.getNums()); } - }else { + } else { ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageVO.getElementId()); // 走模型 List paramList = new ArrayList<>(); @@ -2233,14 +2078,14 @@ public class DesignServiceImpl extends ServiceImpl impleme toProductImageResult.setDirection(toProductImageDTO.getDirection()); toProductImageResultMapper.insert(toProductImageResult); result.add(toProductImageResult); - i ++; + i++; } // 走模型 pythonService.relightBatch(batchTaskId, paramList, userHolder.getId().toString()); // 添加需要扣除的积分到预扣除区 creditsService.addRecordToCreditsDeduction(userHolder.getId(), batchTaskId, CreditsEventsEnum.RELIGHT, cloudTaskDTO.getNums()); // 预插入数据到积分变更表 - creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.RELIGHT.getName(), batchTaskId, true, null,cloudTaskDTO.getNums() ); + creditsService.preInsert(userHolder.getId(), CreditsEventsEnum.RELIGHT.getName(), batchTaskId, true, null, cloudTaskDTO.getNums()); } } return batchTaskId; @@ -2309,12 +2154,12 @@ public class DesignServiceImpl extends ServiceImpl impleme poseTransformationMapper.insert(poseTransformation); Boolean b = pythonService.poseTransformationBatch(poseTransformBatchDTO.getProductImage(), poseTransformBatchDTO.getPoseId(), taskId); - if (b){ + if (b) { // 6、添加预扣除积分到redis creditsService.addRecordToCreditsDeduction(accountId, taskBatchId, creditsEventsEnum, cloudTaskDTO.getNums()); // 6.1 添加积分扣除记录到db creditsService.preInsert(accountId, creditsEventsEnum.getName(), taskBatchId, Boolean.TRUE, null, cloudTaskDTO.getNums()); - }else { + } else { throw new BusinessException("pose transformation error", ResultEnum.ERROR.getCode()); } } @@ -2366,7 +2211,7 @@ public class DesignServiceImpl extends ServiceImpl impleme if (collectionElement.getIsCompositeImage() != null && collectionElement.getIsCompositeImage() == 1) { designCollectionDTO.setMoodTemplateId(String.valueOf(collectionElement.getId())); designCollectionDTO.setMoodboardPosition(collectionService.getMoodboardPositionString(collectionElement.getId())); - }else { + } else { DesignCollectionElementDTO dto = new DesignCollectionElementDTO(); dto.setId(collectionElement.getId()); dto.setDesignType(DesignTypeEnum.COLLECTION.getRealName()); @@ -2374,7 +2219,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } } designCollectionDTO.setMoodBoards(moodBoards); - }else { + } else { designCollectionDTO.setMoodBoards(new ArrayList<>()); } if (CollectionUtil.isNotEmpty(groupedMap.get("Printboard"))) { @@ -2387,7 +2232,7 @@ public class DesignServiceImpl extends ServiceImpl impleme printBoards.add(dto); } designCollectionDTO.setPrintBoards(printBoards); - }else { + } else { designCollectionDTO.setPrintBoards(new ArrayList<>()); } if (CollectionUtil.isNotEmpty(groupedMap.get("Colorboard"))) { @@ -2409,7 +2254,7 @@ public class DesignServiceImpl extends ServiceImpl impleme colorBoards.add(dto); } designCollectionDTO.setColorBoards(colorBoards); - }else { + } else { designCollectionDTO.setColorBoards(new ArrayList<>()); } if (CollectionUtil.isNotEmpty(groupedMap.get("Sketchboard"))) { @@ -2424,7 +2269,7 @@ public class DesignServiceImpl extends ServiceImpl impleme sketchBoards.add(dto); } designCollectionDTO.setSketchBoards(sketchBoards); - }else { + } else { designCollectionDTO.setSketchBoards(new ArrayList<>()); } if (CollectionUtil.isNotEmpty(groupedMap.get("Models"))) { @@ -2442,10 +2287,10 @@ public class DesignServiceImpl extends ServiceImpl impleme } } - }else { + } else { designCollectionDTO.setMannequins(new ArrayList<>()); } - }else { + } else { designCollectionDTO.setMoodBoards(new ArrayList<>()); designCollectionDTO.setPrintBoards(new ArrayList<>()); designCollectionDTO.setColorBoards(new ArrayList<>()); @@ -2465,7 +2310,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } if (workspace.getPosition().equals("Overall")) { designCollectionDTO.setSingleOverall("overall"); - }else { + } else { designCollectionDTO.setSingleOverall("single"); designCollectionDTO.setSwitchCategory(workspace.getPosition()); } @@ -2479,7 +2324,7 @@ public class DesignServiceImpl extends ServiceImpl impleme Long collectionId; if (null == collectionIdParam) { collectionId = collectionService.saveCollection(userInfo.getId(), designDTO.getTimeZone(), designDTO.getMoodTemplateId(), designDTO.getMoodboardPosition()); - }else { + } else { collectionId = collectionIdParam; } List elementIds = getElementId(elementVO); @@ -2526,7 +2371,7 @@ public class DesignServiceImpl extends ServiceImpl impleme log.info("design python端运行时间:" + totalTimeInSeconds + " 秒"); //生成library startTime = System.currentTimeMillis(); - String ageGroup = getAgeGroupByProjectOrCollectionId(designDTO.getProjectId(), collectionId); + String ageGroup = workspaceService.getAgeGroupByProjectOrCollectionId(designDTO.getProjectId(), collectionId); generateLibrary(elementVO, designDTO.getTimeZone(), ageGroup); //处理关联关系,修复element覆盖得情况 // List relationElements = collectionElementService.getByOnlyCollectionId(collectionId); @@ -2577,7 +2422,7 @@ public class DesignServiceImpl extends ServiceImpl impleme cloudTaskMapper.updateById(cloudTask); } } - }else { + } else { String taskId = (String) designBatchResult.get("task_id"); // QueryWrapper qw = new QueryWrapper<>(); // qw.lambda().eq(CloudTask::getTaskId, taskId); @@ -2609,7 +2454,7 @@ public class DesignServiceImpl extends ServiceImpl impleme String timeZone = (String) context.get("timeZone"); String singleOverall = (String) context.get("singleOverall"); - DesignPythonObject item = pythonObjects.getObjects().get(i-1); + DesignPythonObject item = pythonObjects.getObjects().get(i - 1); DesignItem designItem = new DesignItem(); designItem.setAccountId(userInfo.getId()); designItem.setCollectionId(collectionId); @@ -2686,7 +2531,7 @@ public class DesignServiceImpl extends ServiceImpl impleme designItemDetail.setDesignItemId(designItemId); designItemDetail.setCollectionElementId(detail.getElementId()); designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone)); - if (!detail.getType().equals("Body")){ + if (!detail.getType().equals("Body")) { designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType())); } if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) { @@ -2696,7 +2541,7 @@ public class DesignServiceImpl extends ServiceImpl impleme } designItemDetail.setIconPath(detail.getIcon()); designItemDetail.setPriority(typePriority.get(detail.getType().toLowerCase())); - if (!detail.getType().equals("Body")){ + if (!detail.getType().equals("Body")) { DesignPythonItemPrint printObject = detail.getPrint().getOverall(); // designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath()); designItemDetail.setPrintPath(CollectionUtils.isEmpty(printObject.getPrint_path_list()) ? "" : printObject.getPrint_path_list().get(0)); @@ -2757,7 +2602,7 @@ public class DesignServiceImpl extends ServiceImpl impleme CloudTaskVO cloudTaskVO = CopyUtil.copyObject(cloudTask, CloudTaskVO.class); if (cloudTaskVO.getCompletedNum() == null) { cloudTaskVO.setProcess("0%"); - }else { + } else { BigDecimal completed = BigDecimal.valueOf(cloudTaskVO.getCompletedNum()); BigDecimal total = BigDecimal.valueOf(cloudTaskVO.getNums()); @@ -2813,7 +2658,7 @@ public class DesignServiceImpl extends ServiceImpl impleme // }); // result.setDesign(PageBaseResponse.success(convert)); return result; - }else { + } else { result.setDesign(new ArrayList<>()); return result; } @@ -2838,7 +2683,7 @@ public class DesignServiceImpl extends ServiceImpl impleme if (toProductImageResult.getElementType().equals("ProductElement")) { ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageResult.getElementId()); magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(toProductElement.getUrl(), 24 * 60)); - }else { + } else { TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(toProductImageResult.getElementId()); magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(tDesignPythonOutfit.getDesignUrl(), 24 * 60)); } @@ -2864,7 +2709,7 @@ public class DesignServiceImpl extends ServiceImpl impleme if (toProductImageResult.getElementType().equals("ProductElement")) { ToProductElement toProductElement = toProductElementMapper.selectById(toProductImageResult.getElementId()); magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(toProductElement.getUrl(), 24 * 60)); - }else { + } else { ToProductImageResult toProductImageResult1 = toProductImageResultMapper.selectById(toProductImageResult.getElementId()); magicToolResultVO.setSourceUrl(minioUtil.getPreSignedUrl(toProductImageResult1.getUrl(), 24 * 60)); } @@ -2885,13 +2730,13 @@ public class DesignServiceImpl extends ServiceImpl impleme poseTransformationVO.setStatus("Success"); poseTransformationVO.setTaskId(poseTransformation.getUniqueId()); poseTransformationVO.setProductImage(minioUtil.getPreSignedUrl(poseTransformation.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - if (null != poseTransformation.getGifUrl()){ + if (null != poseTransformation.getGifUrl()) { poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(poseTransformation.getGifUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); } - if (null != poseTransformation.getVideoUrl()){ + if (null != poseTransformation.getVideoUrl()) { poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(poseTransformation.getVideoUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); } - if (null != poseTransformation.getFirstFrameUrl()){ + if (null != poseTransformation.getFirstFrameUrl()) { poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(poseTransformation.getFirstFrameUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); } voList.add(poseTransformationVO); @@ -2902,9 +2747,6 @@ public class DesignServiceImpl extends ServiceImpl impleme return result; } - @Resource - private PanToneMapper panToneMapper; - @Override public Boolean receiveDesignParams(ReceiveDesignParam receiveDesignParam) { List receiveCollectionElementList = receiveDesignParam.getReceiveCollectionElementList(); @@ -2928,7 +2770,7 @@ public class DesignServiceImpl extends ServiceImpl impleme collectionElement.setUrl(url); collectionElement.setHasPin((byte) 0); collectionElement.setCreateDate(date); - }else if (receiveCollectionElement.getLevel1Type().equals(CollectionLevel1TypeEnum.MOOD_BOARD.getRealName())) { + } else if (receiveCollectionElement.getLevel1Type().equals(CollectionLevel1TypeEnum.MOOD_BOARD.getRealName())) { String url = receiveCollectionElement.getUrl(); String[] split = url.split("/"); String pictureName = split[split.length - 1]; @@ -2940,7 +2782,7 @@ public class DesignServiceImpl extends ServiceImpl impleme collectionElement.setUrl(url); collectionElement.setHasPin((byte) 0); collectionElement.setCreateDate(date); - }else if (receiveCollectionElement.getLevel1Type().equals(CollectionLevel1TypeEnum.SKETCH_BOARD.getRealName())) { + } else if (receiveCollectionElement.getLevel1Type().equals(CollectionLevel1TypeEnum.SKETCH_BOARD.getRealName())) { String url = receiveCollectionElement.getUrl(); String[] split = url.split("/"); String pictureName = split[split.length - 1]; @@ -2955,7 +2797,7 @@ public class DesignServiceImpl extends ServiceImpl impleme collectionElement.setHasPin((byte) 0); collectionElement.setCreateDate(date); libraryService.processSketchBoards(url, collectionElement.getLevel2Type()); - }else if (receiveCollectionElement.getLevel1Type().equals(CollectionLevel1TypeEnum.COLOR_BOARD.getRealName())) { + } else if (receiveCollectionElement.getLevel1Type().equals(CollectionLevel1TypeEnum.COLOR_BOARD.getRealName())) { /*String color = receiveCollectionElement.getHsv(); String[] parts = color.split("\\s+"); int h = Math.round(Float.parseFloat(parts[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 0e407587..8d21451d 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -1,4363 +1,4360 @@ -package com.ai.da.service.impl; - -import cn.hutool.core.img.gif.AnimatedGifEncoder; -import cn.hutool.http.Header; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpResponse; -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; -import com.ai.da.common.utils.*; -import com.ai.da.mapper.primary.*; -import com.ai.da.mapper.primary.entity.*; -import com.ai.da.model.dto.*; -import com.ai.da.model.enums.CollectionType; -import com.ai.da.model.enums.Module; -import com.ai.da.model.enums.SketchStyle; -import com.ai.da.model.vo.*; -import com.ai.da.python.PythonService; -import com.ai.da.service.*; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; -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.LambdaQueryWrapper; -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.RequiredArgsConstructor; -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; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.bytedeco.javacv.FFmpegFrameGrabber; -import org.bytedeco.javacv.Java2DFrameConverter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; - -import javax.annotation.Resource; -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -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.PATTERN; -import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE; -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; - -@Slf4j -@Service -@RequiredArgsConstructor -public class GenerateServiceImpl extends ServiceImpl implements GenerateService { - - private final CollectionElementMapper collectionElementMapper; - private final GenerateDetailMapper generateDetailMapper; - private final LibraryService libraryService; - private final PythonService pythonService; - private final CollectionElementService collectionElementService; - private final CreditsService creditsService; - private final MinioUtil minioUtil; - private final RabbitMQService rabbitMQService; - private final RedisUtil redisUtil; - private final GenerateCancelMapper generateCancelMapper; - private final SketchReconstructionMapper sketchReconstructionMapper; - private final SendRequestUtil sendRequestUtil; - private final ProjectService projectService; - private final CollectionSortService collectionSortService; - private final CloudTaskService cloudTaskService; - private final APIGenerateMapper apiGenerateMapper; - private final CollectionSortMapper collectionSortMapper; - private final SysFileService sysFileService; - private final LibraryModelPointService libraryModelPointService; - private final PoseTransformationMapper poseTransformationMapper; - private final CloudTaskMapper cloudTaskMapper; - private final ToProductImageResultMapper toProductImageResultMapper; - private final AccountService accountService; - private final APIGenerateService apiGenerateService; - private final UserLikeGroupService userLikeGroupService; - private final MessageCenterService messageCenterService; - - @Value("${redis.key.orderForGenerate}") - private String consumptionOrderKey; - - @Value("${redis.key.generateCancelSet}") - private String cancelSetKey; - - @Value("${redis.key.generateExceptionMap}") - private String exceptionMapKey; - - @Value("${redis.key.generateResult}") - private String generateResultKey; - - @Value("${minio.bucketName.slogan}") - private String sloganBucket; - - @Value("${minio.bucketName.users}") - private String userBucket; - - @Value("${redis.key.relightResultKey}") - private String relightResultKey; - - @Value("${redis.key.toProductImageResultKey}") - private String toProductImageResultKey; - - @Value("${access.python.generate_sr_port}") - private String generateServicePort; - - @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; - - - // 创建 Random 对象 - Random random = new Random(); - - @Override - public GenerateCaptionVO generateCaption(Long sketchElementId) { - CollectionElement collectionElement = collectionElementMapper.selectById(sketchElementId); - if (Objects.isNull(collectionElement)) { - throw new BusinessException("the.image.does.not.exist.please.reselect"); - } - String url = collectionElement.getUrl(); -// String caption = pythonService.generateSketchCaption(url); - GenerateCaptionVO recognized_caption = new GenerateCaptionVO("recognized caption"); - - return recognized_caption; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void generateThroughImageText(GenerateThroughImageTextDTO generateThroughImageTextDTO) { - // 1、获取用户信息 - Long accountId = generateThroughImageTextDTO.getUserId(); - - GenerateModeEnum modeEnum = getMode(generateThroughImageTextDTO); - String generateType = modeEnum.getValue(); - // 2、判断必须入参是否为非空(在prepare阶段已校验) - Generate generate = new Generate(); - generate.setAccountId(accountId); - generate.setUniqueId(generateThroughImageTextDTO.getUniqueId()); - generate.setLevel1Type(generateThroughImageTextDTO.getLevel1Type()); - generate.setLevel2Type(generateThroughImageTextDTO.getLevel2Type()); - generate.setSeed(StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getSeed()) ? "" : generateThroughImageTextDTO.getSeed()); - // 当level1type是sketchboard时,存数据库需要加上当前性别 - generate.setGenerateType(generate.getLevel1Type().equals(SKETCH_BOARD.getRealName()) ? - generateType + " (" + generateThroughImageTextDTO.getGender() + ")" : - generateType); - generate.setModelName(StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getModelName()) ? ModelNameEnum.MODEL_0.getCode() : generateThroughImageTextDTO.getModelName()); - generate.setCreateDate(DateUtil.getByTimeZone(generateThroughImageTextDTO.getTimeZone())); - generate.setElementSource(StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getDesignType()) ? null : generateThroughImageTextDTO.getDesignType()); - - String text = generateThroughImageTextDTO.getText(); - generate.setText(text); - Long elementId = generateThroughImageTextDTO.getCollectionElementId(); -// validateGeneraType(generate, text, elementId); - if (!StringUtil.isNullOrEmpty(text)) { - text = modifyPrompt(text, generate, generateThroughImageTextDTO.getLevel1Type(), generateThroughImageTextDTO.getAgeGroup()); - } - - // todo 这一步现在还是有必要的吗? - // 2.1 sketch或print在t_collection_element表/t_library表中的信息是否需要更新 如 level2Type - CollectionElement collectionElement = collectionElementService.editLevel2Type(elementId, generateThroughImageTextDTO.getLevel2Type(), generateThroughImageTextDTO.getDesignType()); - - // 3、向模型发起请求 - String mode = modeEnum.getType(); - String category = generateThroughImageTextDTO.getLevel1Type().equals(SKETCH_BOARD.getRealName()) ? "sketch" : - generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) ? "print" : "moodboard"; - String path = CommonConstant.GENERATE_PATH; - String port = generateServicePort; - String jsonString = ""; - HashMap params = new HashMap<>(); - String version = null; - if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getModelName()) && generateThroughImageTextDTO.getModelName().equals("high")) { - version = "high"; - params.put("version", "high"); - } else if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getModelName()) && generateThroughImageTextDTO.getModelName().equals("normal")) { - version = "fast"; - params.put("version", "fast"); - } - // 3.1 确定不同类型的印花分别调哪个接口 - if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) { - switch (generateThroughImageTextDTO.getLevel2Type()) { - case "Logo": - path = CommonConstant.GENERATE_SINGLE_LOGO; - params.put("tasks_id", generateThroughImageTextDTO.getUniqueId()); - params.put("prompt", text); - params.put("seed", generateThroughImageTextDTO.getSeed()); - jsonString = JSON.toJSONString(params, SerializerFeature.WriteMapNullValue); - break; - case "Slogan": - path = CommonConstant.GENERATE_SLOGAN; - port = CommonConstant.PYTHON_PORT_9997; - params.put("num_point", "16"); - params.put("tasks_id", generateThroughImageTextDTO.getUniqueId()); - params.put("prompt", text); - params.put("image_url", collectionElement.getUrl()); - jsonString = JSON.toJSONString(params, SerializerFeature.WriteMapNullValue); - break; - case "Pattern": - GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), - mode, category, generateThroughImageTextDTO.getGender(), version); - jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); - } - } else { - GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), - mode, category, generateThroughImageTextDTO.getGender(), version); - jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); - } - - Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path); - - // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 - save(generate); - - // 5、将本次请求存入redis - String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId(); - String status; - if (requestResult) { - status = "Executing"; - } else { - status = "Fail"; - } - GenerateResultVO generateResultVO = new GenerateResultVO(generateThroughImageTextDTO.getUniqueId(), null, null, status); - redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - } - - public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) { - if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) { - if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { - return GenerateModeEnum.TEXT_IMAGE; - } else { - return GenerateModeEnum.TEXT; - } - } else { - if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { - return GenerateModeEnum.IMAGE; - } - } - return GenerateModeEnum.TEXT; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processGenerateResult(String taskId, String url, String category) { - // 1、处理模型返回的数据 - GenerateDetail generateDetail = new GenerateDetail(); - GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); - Generate generate; - try { - generate = selectByUniqueId(taskId); - } catch (MybatisPlusException e) { - log.error(e.getMessage()); - if (e.getMessage().equals("One record is expected, but the query result is multiple records")) { - generate = selectListByUniqueId(taskId).get(0); - } else { - log.error("There are some problems with database query, please try again."); - return; - } - } -// Generate generate = selectByUniqueId(taskId); - String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, 24 * 60), Boolean.FALSE); - // 通过MD5值和level1Type,判断不同level1Type下相同的图片是否被like过 - List> libraryIdList = generateDetailMapper.getLibraryIdThroughMD5(md5, generate.getLevel1Type()); - if (!libraryIdList.isEmpty()) { - generateDetail.setIsLike((byte) 1); - generateDetail.setLibraryId(libraryIdList.get(0).get("library_id")); - generateCollectionItemVO.setIsLiked(Boolean.TRUE); - } - generateDetail.setUrl(url); - generateDetail.setGenerateId(generate.getId()); - generateDetail.setCreateDate(LocalDateTime.now()); - generateDetail.setMd5(md5); - // 将相应的url保存到数据库 - generateDetailMapper.insert(generateDetail); - -// String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-")); - String key = generateResultKey + ":" + taskId; - String imageName = url.substring(url.lastIndexOf("/") + 1); - String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; - if (StringUtil.isNullOrEmpty(category)) { - Generate generateRecord = selectByUniqueId(taskId); - category = generateRecord.getLevel2Type(); - } - GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category); - // 更新redis - redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - // 执行积分扣除 - // ** 注:如果生成的图片都是空白 则不扣积分 - if (!status.equals("Invalid")) { - String uuid = taskId.substring(0, taskId.lastIndexOf("-", taskId.lastIndexOf("-") - 1)); - String accountId = taskId.substring(taskId.lastIndexOf("-") + 1); - Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), uuid); - if (flag) creditsService.updateChangedCredits(accountId, uuid); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processToProductImageResult(String taskId, String url, String category) { - QueryWrapper qw = new QueryWrapper<>(); - qw.lambda().eq(ToProductImageResult::getTaskId, taskId); - List toProductImageResults = toProductImageResultMapper.selectList(qw); - if (CollectionUtils.isEmpty(toProductImageResults)) { - return; -// throw new BusinessException(""); - } - ToProductImageResult toProductImageResult = toProductImageResults.get(0); - toProductImageResult.setUrl(url); - toProductImageResult.setStatus("Success"); -// toProductImageResult.setResultType("ToProductImage"); - toProductImageResultMapper.updateById(toProductImageResult); - - String key = toProductImageResultKey + ":" + taskId; - String imageName = url.substring(url.lastIndexOf("/") + 1); - String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; - GenerateResultVO generateResultVO = new GenerateResultVO(taskId, toProductImageResult.getId(), url, status, category); - redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - Long accountId = Long.parseLong(taskId.substring(taskId.lastIndexOf("-") + 1)); - if (!status.equals("Invalid")) { - // 4、扣除积分 - Boolean b = creditsService.taskCreditsDeduction(accountId, taskId); - // 3、记录积分变更 - if (b) creditsService.insertToCreditsDetail(accountId, - TO_PRODUCT_IMAGE.getName(), - TO_PRODUCT_IMAGE.getValue(), - "negative", null); - } - } - - 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)) { - toProductImageResult.setStatus(status); - toProductImageResultMapper.updateById(toProductImageResult); - } - } - } - - private void validateGeneraType(Generate generate, String text, Long elementId) { - String generateType = ""; - if (StringUtil.isNullOrEmpty(text.trim()) && Objects.isNull(elementId)) { - throw new BusinessException("please.input.the.caption.or.choose.an.image"); - } else if (!StringUtil.isNullOrEmpty(text.trim()) && !Objects.isNull(elementId)) { - generateType = "text-image"; - generate.setText(text); - generate.setElementId(elementId); - } else if (!StringUtil.isNullOrEmpty(text.trim())) { - generateType = "text"; - generate.setText(text); - } else if (!Objects.isNull(elementId)) { - generateType = "image"; - generate.setElementId(elementId); - } - generate.setGenerateType(generateType); - - } - - private String modifyPrompt(String userInput, Generate generate, String level1Type, String ageGroup) { - String text = ""; - String prefix = ""; - if (userInput.startsWith("Painting Style") - || userInput.startsWith("Illustration Style") - || userInput.startsWith("Real Style")) { - prefix = userInput.substring(0, userInput.indexOf(",")) + ", "; - userInput = userInput.substring(userInput.indexOf(",") + 1); - } - // 替换用户输入中的中文字符 - log.info("用户输入,处理前:{}", userInput); - userInput = ComprehensivePunctuationConverter.convertToHalfWidth(userInput); - log.info("用户输入,处理后:{}", userInput); - String translated = prefix + pythonService.promptTranslate(userInput); - switch (level1Type) { - case "Moodboard": - text = translated + ",high quality"; -// generate.setText(text); - break; - case "Printboard": - case "Pattern": - text = translated; - /*if (prefix.contains("Painting Style")) { - text = "Picasso,increased color saturation,increased glossiness," + translated + ", fabric print, high quality"; - } else if (prefix.contains("Illustration Style")) { - text = "Flat coating,romantic,soft,pencil strokes,accentuating and widening the depth of pencil strokes,paper patterns,block colors,crayons,reducing image contrast,and hand drawn painting marks," + translated + ", fabric print, high quality"; - } else if (prefix.contains("Real Style")) { - text = "Hyper realism,3d,deepened projection,increased permutation value,increased concavity and convexity value," + translated + ", fabric print, high quality"; - } else { - text = translated; - }*/ -// text = userInput + ", fabric print, high quality"; -// generate.setText(text); - break; - case "Sketchboard": -// text = "clear lines, simple outlines monochrome white vector image of " + translated + ", no background, sketch flat, front view display, best quality, ultra-high resolution 8k"; - text = "a single item of sketch of " + translated + ", 4k, white background"; - if (Objects.nonNull(generate) && generate.getModelName().equals("high")) { - text = text + ", only technical pattern of a single garment"; - } - if (!StringUtil.isNullOrEmpty(ageGroup) && ageGroup.equals("Child")) { - text = text + ", Children's clothing"; - } - break; -// generate.setText(text); - default: - text = translated; - } - return text; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public GenerateLikeVO generateLike(GenerateLikeDTO generateLikeDTO) { - // 1、判断参数是否正确 - // 1.1 必须参数是否非空 - if (SKETCH_BOARD.getRealName().equals(generateLikeDTO.getLevel1Type())) { - if (StringUtil.isNullOrEmpty(generateLikeDTO.getLevel2Type())) { - throw new BusinessException("level2Type.cannot.be.empty"); - } - if (StringUtil.isNullOrEmpty(generateLikeDTO.getGender())) { - throw new BusinessException("gender.cannot.be.empty"); - } - } - if (PRINT_BOARD.getRealName().equals(generateLikeDTO.getLevel1Type())) { - if (StringUtil.isNullOrEmpty(generateLikeDTO.getLevel2Type())) { - throw new BusinessException("level2Type.cannot.be.empty"); - } - } - // 1.2 判断参数是否真实有效 - Long generateDetailId = generateLikeDTO.getGenerateDetailId(); - GenerateDetail generateDetail = generateDetailMapper.selectById(generateDetailId); - if (Objects.isNull(generateDetail)) { - throw new BusinessException("generateItem.does.not.exist"); - } - Generate generate = getById(generateDetail.getGenerateId()); - if (!generateLikeDTO.getLevel1Type().equals(generate.getLevel1Type())) { - throw new BusinessException("level1Type.does.not.match"); - } - - // 2、将like的图片信息存入library - // 2.1、不能重复喜欢 - // 2.1.1 判断该图片是否被喜欢过 - Library libraryDetail = libraryService.getById(generateDetail.getLibraryId()); - if ((Objects.nonNull(generateDetail.getLibraryId()) && !generateDetail.getLibraryId().equals(0L)) - || Objects.nonNull(libraryDetail)) { - throw new BusinessException("duplicate.likes.are.not.allowed"); - } - - // todo 2.1.2、判断library中是否有相同MD5的图片 - - // 2.2、添加到library - AuthPrincipalVo userInfo = UserContext.getUserHolder(); - Long accountId = userInfo.getId(); - Library library = setLibrary(accountId, generateLikeDTO, generateDetail.getUrl(), generateLikeDTO.getAgeGroup()); - libraryService.save(library); - - // 3、更新generateDetail表的isLike列和libraryId列 - updateLikeStatus(generateLikeDTO.getGenerateDetailId(), (byte) 1, library.getId(), generateLikeDTO.getTimeZone()); - - return new GenerateLikeVO(library.getId()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean generateDislike(Long generateDetailId, String timeZone) { - // 1、确定generateDetail中是否有这条记录 - GenerateDetail generateDetail = generateDetailMapper.selectById(generateDetailId); - if (Objects.isNull(generateDetail)) { - throw new BusinessException("generateItem.does.not.exist"); - } - - // 2、修改generateDetail表中的isLike、libraryId字段 - updateLikeStatus(generateDetailId, (byte) 0, 0L, timeZone); - - // 3、确定library中是否添加该条记录 - Library libraryDetail = libraryService.getById(generateDetail.getLibraryId()); - if (Objects.isNull(libraryDetail)) { - return Boolean.TRUE; - } - - // 4、删除library相关记录 - libraryService.removeById(libraryDetail.getId()); - - // 5、返回成功 - return Boolean.TRUE; - } - - public Library setLibrary(Long accountId, GenerateLikeDTO generateLikeDTO, String imageUrl, String ageGroup) { - Library library = new Library(); - library.setAccountId(accountId); - library.setLevel1Type(generateLikeDTO.getLevel1Type()); - library.setLevel2Type(StringUtil.isNullOrEmpty(generateLikeDTO.getLevel2Type()) ? null : generateLikeDTO.getLevel2Type()); - library.setLevel3Type(StringUtil.isNullOrEmpty(generateLikeDTO.getGender()) ? null : generateLikeDTO.getGender()); - library.setAgeGroup(ageGroup); - library.setName(DateUtil.getTimeStamp(generateLikeDTO.getTimeZone()) + "_N_G"); - library.setUrl(imageUrl); - try { - library.setMd5(MD5Utils.encryptFile(minioUtil.download(imageUrl))); - } catch (MinioException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - library.setCreateDate(DateUtil.getByTimeZone(generateLikeDTO.getTimeZone())); - return library; - } - - public void updateLikeStatus(Long generateDetailId, Byte hasLike, Long libraryId, String timeZone) { - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("id", generateDetailId); - - GenerateDetail generateDetail = new GenerateDetail(); - generateDetail.setIsLike(hasLike); - generateDetail.setLibraryId(libraryId); - generateDetail.setUpdateDate(DateUtil.getByTimeZone(timeZone)); - generateDetailMapper.update(generateDetail, queryWrapper); - } - - // 避免循环注入,已移到libraryService中 - public void updateLikeStatusBatch(List generateDetailIdList, Byte hasLike, Long libraryId, String timeZone) { - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.in("id", generateDetailIdList); - - GenerateDetail generateDetail = new GenerateDetail(); - generateDetail.setIsLike(hasLike); - generateDetail.setLibraryId(libraryId); - generateDetail.setUpdateDate(DateUtil.getByTimeZone(timeZone)); - - generateDetailMapper.update(generateDetail, queryWrapper); - } - - // 避免循环注入,已移到libraryService中 - public List selectBatchByLibraryId(List libraryId) { - QueryWrapper qw = new QueryWrapper<>(); - qw.in("library_id", libraryId); - - return generateDetailMapper.selectList(qw); - } - - @Override - public PrepareForGenerateVO prepareForGenerate(GenerateThroughImageTextDTO generateDTO) { - // 1、参数检查,判断必须参数是否为空 - validateRequiredParams(generateDTO); - - // 2、处理特殊模型情况(wx/flux) - if (isWxModel(generateDTO)) { - return handleWxModelGeneration(generateDTO); - } - if (isFluxPatternModel(generateDTO)) { - return handleFluxPatternGeneration(generateDTO); - } - - String modelName = generateDTO.getModelName(); - if (StringUtil.isNullOrEmpty(modelName)) { - return handleStandardGeneration(generateDTO); - } - - 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.SCETCH_TEXT2IMG); - // 创建生成任务 - String taskId = null; - try { - taskId = createGoogleAsyncTask(generateDTO, useModel, prompt); - } catch (Exception e) { - e.printStackTrace(); - } - processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.SCETCH_TEXT2IMG); - return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); - } - - private PrepareForGenerateVO handleNanoBananaModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { - // 校验积分是否足够 - validateCredits(CreditsEventsEnum.SCETCH_IMG2IMG); - // 创建生成任务 - String taskId = null; - try { - taskId = createGoogleAsyncTask(generateDTO, useModel, prompt); - } catch (Exception e) { - e.printStackTrace(); - } - processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.SCETCH_IMG2IMG); - return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); - } - - public String toProductAsyncTask(String imagePath, String useModel, String prompt) { - - long startTime = System.currentTimeMillis(); - log.info("开始执行toProductAsyncTask - model: {}, imagePath: {}, prompt: {}", - useModel, imagePath, prompt); - - if (StringUtil.isNullOrEmpty(imagePath) || StringUtil.isNullOrEmpty(useModel) || StringUtil.isNullOrEmpty(prompt)) { - log.error("参数验证失败 - imagePath: {}, useModel: {}, prompt: {}", imagePath, useModel, prompt); - throw new BusinessException("Parameter Exception"); - } - AuthPrincipalVo userHolder = UserContext.getUserHolder(); - Long userId = userHolder.getId(); - - String uuid = UUID.randomUUID().toString(); - // 生成唯一的任务ID - String taskId = uuid + "-" + userId; - String finalImagePath = null; - try { - finalImagePath = addWhiteBackground(imagePath); - // 如果白色背景处理成功,对base64图片进行压缩以降低模型成本 - if (!StringUtil.isNullOrEmpty(finalImagePath)) { - // 压缩到512x512分辨率 - finalImagePath = minioUtil.compressBase64Image(finalImagePath, 512, 512); - //去掉"data:image/png;base64," - finalImagePath = finalImagePath.replace("data:image/png;base64,", ""); - } else { - // 如果白色背景处理失败或不需要,直接获取原图的压缩base64编码 - finalImagePath = minioUtil.getCompressedImageAsBase64(imagePath, 512, 512); - } - } catch (IOException e) { - log.error("Error getting image as base64 taskId: {} ", taskId, e); - throw new BusinessException("Parameter Exception"); - } - - // 初始化Redis中的任务状态为"Executing" - String key = generateResultKey + ":" + taskId; - // 异步处理token获取和API调用 - String projectId = "aida-461108"; - String location = "global"; - String endpoint = String.format( - "https://aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:generateContent", - projectId, location, ModelConstants.NANO_BANANA - ); - - JSONObject requestBody = new JSONObject(); - // 使用 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("IMAGE")); -// generationConfig.set("topP", 0.95); - JSONObject imageConfig = new JSONObject(); - imageConfig.set("aspectRatio", "9:16"); - generationConfig.set("imageConfig", imageConfig); - requestBody.set("generationConfig", generationConfig); - String jsonBody = requestBody.toString(); -// log.info("Google 请求入参:{}", jsonBody); - - GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Pending"); - redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - 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请求 - 优化网络配置以解决连接中断问题 - ConnectionPool connectionPool = new ConnectionPool(10, 5, TimeUnit.MINUTES); - OkHttpClient client = new OkHttpClient.Builder() - .connectTimeout(45, TimeUnit.SECONDS) // 增加连接超时时间 - .readTimeout(120, TimeUnit.SECONDS) // 增加读取超时时间 - .writeTimeout(120, TimeUnit.SECONDS) // 增加写入超时时间 - .callTimeout(180, TimeUnit.SECONDS) // 添加总调用超时时间 - .connectionPool(connectionPool) // 使用连接池 - .retryOnConnectionFailure(true) // 启用连接失败重试 - .build(); - Request request = new Request.Builder() - .url(endpoint) - // 移除Connection: close头,使用Keep-Alive连接 - .addHeader("Authorization", "Bearer " + tokenValue) - .addHeader("Content-Type", "application/json") - .addHeader("User-Agent", "AIDA-Client/1.0") - .addHeader("Accept", "application/json") - .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) - .build(); - - // 实现重试逻辑来处理网络连接问题 - int maxRetries = 3; - int retryDelay = 2000; // 2秒 - boolean success = false; - - for (int attempt = 1; attempt <= maxRetries && !success; attempt++) { - try { - log.info("发起HTTP请求 - 尝试次数: {}/{}, URL: {}, taskId: {}", attempt, maxRetries, request.url(), taskId); - long requestStartTime = System.currentTimeMillis(); - - try (Response response = client.newCall(request).execute()) { - long requestEndTime = System.currentTimeMillis(); - log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}", - response.code(), (requestEndTime - requestStartTime), taskId); - if (!response.isSuccessful()) { - log.warn("Google API响应失败,状态码: {} for taskId: {}", response.code(), taskId); - if (attempt < maxRetries) { - Thread.sleep(retryDelay * attempt); // 递增延迟 - continue; - } else { - throw new IOException("HTTP error code: " + response.code()); - } - } - - 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); - String finishReason = candidate.getString("finishReason"); - if (finishReason != null && finishReason.equals("STOP")) { - success = true; - com.alibaba.fastjson.JSONObject contentResult = candidate.getJSONObject("content"); - JSONArray parts = contentResult.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 inlineDataResult = part.getJSONObject("inlineData"); - base64Data = inlineDataResult.getString("data"); - break; - } - } - } else if (finishReason != null && finishReason.equals("IMAGE_SAFETY")) { - String finishMessage = candidate.getString("finishMessage"); - if (finishMessage != null && finishMessage.contains("Try rephrasing the prompt")) { - finishMessage = "Try rephrasing the prompt or modifying the model image. If you think this was an error, send feedback."; - LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); - Account account = accountService.getOne(select); - if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { - finishMessage = "请尝试重新表述提示词,或修改模特图。若您认为这是误判,可提交反馈。"; - } - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, finishMessage, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - return; - } - } - - } - } - - if (base64Data != null && !base64Data.isEmpty()) { - String resultPath = userId + "/product_image" + "/" + uuid; - String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); - if (StringUtil.isNullOrEmpty(minioPath)){ - log.warn("Google API调用成功,但图片保存失败 for taskId: {}", taskId); - } - // 生成成功,更新Redis状态和URL - GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); - redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - success = true; - log.info("Google API调用成功 for taskId: {}", taskId); - } 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); - success = true; // 这是业务逻辑失败,不需要重试 - } - } - } catch (IOException e) { - log.warn("网络连接问题 - 异常类型: {}, 尝试: {}/{}, taskId: {}, 错误详情: {}", - e.getClass().getSimpleName(), attempt, maxRetries, taskId, e.getMessage()); - if (attempt < maxRetries) { - int delayMs = retryDelay * attempt; - log.info("准备重试 - 延迟: {}ms, 下次尝试: {}/{}, taskId: {}", - delayMs, (attempt + 1), maxRetries, taskId); - try { - Thread.sleep(delayMs); // 递增延迟重试 - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - log.error("重试被中断 - taskId: {}", taskId, ie); - throw new RuntimeException("重试被中断", ie); - } - } else { - log.error("Google API调用最终失败 - 已达到最大重试次数: {}, taskId: {}", maxRetries, taskId, e); - throw e; // 最后一次尝试失败,抛出异常 - } - } - } - } 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); - - long endTime = System.currentTimeMillis(); - log.info("toProductAsyncTask执行完成 - taskId: {}, 总耗时: {}ms", taskId, (endTime - startTime)); - return taskId; - } - - - public String createGoogleAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { - // 从 resources 加载 JSON 文件 - 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("IMAGE")); - JSONObject imageConfig = new JSONObject(); - imageConfig.set("aspectRatio", "9:16"); - generationConfig.set("imageConfig", imageConfig); -// 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); - parametersObj.set("aspectRatio", "9:16"); - - // 构建完整的请求体 - 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(() -> { - long startTime = System.currentTimeMillis(); - log.info("开始执行 createGoogleAsyncTask 方法,taskId: {}, model: {}, prompt: {}", taskId, useModel, prompt); - - final int maxRetries = 3; - final int retryDelayMs = 2000; - - for (int attempt = 1; attempt <= maxRetries; attempt++) { - try { - log.info("Google API 调用尝试 {}/{} - taskId: {}", attempt, maxRetries, taskId); - - // 异步获取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() - .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)) - .connectTimeout(45, TimeUnit.SECONDS) - .readTimeout(120, TimeUnit.SECONDS) - .writeTimeout(120, TimeUnit.SECONDS) - .callTimeout(180, TimeUnit.SECONDS) - .retryOnConnectionFailure(true) - .build(); - - Request request = new Request.Builder() - .url(endpoint) - .addHeader("Authorization", "Bearer " + tokenValue) - .addHeader("Content-Type", "application/json") - .addHeader("User-Agent", "AIDA-Backend/1.0") - .addHeader("Accept", "application/json") - .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) - .build(); - - long requestStartTime = System.currentTimeMillis(); - try (Response response = client.newCall(request).execute()) { - long requestDuration = System.currentTimeMillis() - requestStartTime; - log.info("Google API 请求完成 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 耗时: {}ms", - taskId, attempt, endpoint, response.code(), requestDuration); - - 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"); - String finishReason = candidate.getString("finishReason"); - if (finishReason != null && finishReason.equals("STOP")) { - 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 (finishReason != null && finishReason.equals("IMAGE_SAFETY")) { - String finishMessage = candidate.getString("finishMessage"); - if (finishMessage != null && finishMessage.contains("Try rephrasing the prompt")) { - finishMessage = "Try rephrasing the prompt or replacing the image. If you think this was an error, send feedback."; - LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); - Account account = accountService.getOne(select); - if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { - finishMessage = "请尝试重新表述提示词,或更换图片。若您认为这是误判,可提交反馈。"; - } - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, finishMessage, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } - return; - } - - } - } 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"); - String raiFilteredReason = prediction.getString("raiFilteredReason"); - if (raiFilteredReason != null && raiFilteredReason.contains("Try rephrasing the prompt")) { - raiFilteredReason = "Input data may contain inappropriate content."; - LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); - Account account = accountService.getOne(select); - if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { - raiFilteredReason = "输入数据可能包含不当内容。"; - } - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, raiFilteredReason, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } - } - } - - 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); - - long totalDuration = System.currentTimeMillis() - startTime; - log.info("Google 图片生成成功 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); - return; // 成功后立即返回 - } 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); - return; // 业务逻辑失败,直接返回不重试 - } - } - } catch (IOException e) { - // 只对 IOException 进行重试 - log.warn("Google API 调用发生 IOException - taskId: {}, 尝试: {}/{}, 异常: {}", - taskId, attempt, maxRetries, e.getMessage()); - - if (attempt < maxRetries) { - try { - long delay = retryDelayMs * attempt; // 递增延迟 - log.info("等待 {}ms 后进行第 {} 次重试 - taskId: {}", delay, attempt + 1, taskId); - Thread.sleep(delay); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - log.error("重试等待被中断 - taskId: {}", taskId); - break; - } - } else { - // 达到最大重试次数,记录错误并更新Redis状态 - log.error("Google API 调用最终失败,已达到最大重试次数 {} - taskId: {}", maxRetries, taskId, e); - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } - } catch (Exception e) { - // 对于其他异常(非IOException),记录错误并直接返回,不重试 - log.error("Google API 调用发生意外异常 - taskId: {}, 异常类型: {}, 异常信息: {}", - taskId, e.getClass().getSimpleName(), e.getMessage(), e); - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - return; // 非网络异常,直接返回不重试 - } - } - - // 如果所有重试都失败了,记录最终失败日志 - long totalDuration = System.currentTimeMillis() - startTime; - log.error("Google API 调用最终失败,所有重试都已用尽 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); - 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, - generateDTO.getText(), - "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; - } - - /** - * 参数校验 - */ - private void validateRequiredParams(GenerateThroughImageTextDTO generateDTO) { - if (Objects.isNull(generateDTO.getUserId())) { - throw new BusinessException("userId.cannot.be.empty"); - } - - // Printboard必须要有level2Type - if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) - && StringUtil.isNullOrEmpty(generateDTO.getLevel2Type())) { - throw new BusinessException("level2Type.cannot.be.empty"); - } - if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) && generateDTO.getLevel2Type().equals(CreditsEventsEnum.PATTERN.getName())) { - 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"); - } - } - - } - - /** - * 判断是否为wx模型 - */ - private boolean isWxModel(GenerateThroughImageTextDTO generateDTO) { - return !StringUtil.isNullOrEmpty(generateDTO.getModelName()) - && "wx".equals(generateDTO.getModelName()); - } - - /** - * 处理wx模型生成 - */ - private PrepareForGenerateVO handleWxModelGeneration(GenerateThroughImageTextDTO generateDTO) { - // 校验积分是否足够 - validateCredits(CreditsEventsEnum.WX_TEXT2IMG); - // 创建生成任务 - String taskId = createAsyncTask(generateDTO); - processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.WX_TEXT2IMG); - return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); - } - - private PrepareForGenerateVO handleQwenModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { - - validateCredits(CreditsEventsEnum.QWEN_TEXT2IMG); - String taskId = createQwenAsyncTask(generateDTO, useModel, prompt); - processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.QWEN_TEXT2IMG); - return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); - } - - private PrepareForGenerateVO handleDouBaoModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { - CreditsEventsEnum creditsEvents = null; - if (useModel.equals(ModelConstants.PRINTBOARD_ADVANCED_I2I)){ - creditsEvents = CreditsEventsEnum.DOUBAO_IMG2IMG_ADVANCED; - }else if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_I2I)){ - creditsEvents = CreditsEventsEnum.DOUBAO_IMG2IMG_HIGH; - }else if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_T2I)||useModel.equals(ModelConstants.MOODBOARD_ADVANCED)){ - creditsEvents = CreditsEventsEnum.DOUBAO_TEXT2IMG; - } - validateCredits(creditsEvents); - String taskId = createDouBaoAsyncTask(generateDTO, useModel, prompt); - processCreditDeduction(generateDTO.getUserId(), taskId, creditsEvents); - - 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) { - - String exceptionMessage = e.getMessage(); - if (exceptionMessage != null && exceptionMessage.contains("\"message\"")) { - try { - // 解析JSON格式的异常信息 - JSONObject jsonObj = JSONUtil.parseObj(exceptionMessage); - exceptionMessage = jsonObj.getStr("message"); - if ("Input data may contain inappropriate content.".equals(exceptionMessage)) { - LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); - Account account = accountService.getOne(select); - if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { - exceptionMessage = "输入数据可能包含不当内容。"; - } - } - throw new BusinessException(exceptionMessage, ResultEnum.PROMPT.getCode()); - } catch (Exception parseException) { - // 如果解析失败,返回原始消息 - throw new BusinessException(exceptionMessage, ResultEnum.PROMPT.getCode()); - } - } - throw new BusinessException(e.getMessage(), ResultEnum.PROMPT.getCode()); - } - String taskId = result.getOutput().getTaskId(); - log.info("qwen text2image 请求生成:{}, taskId:{}", JsonUtils.toJson(result), taskId); - - Generate generate = new Generate(userId, taskId, level1Type, level2Type, generateDTO.getText(), "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); - } - - // 保存生成记录到数据库 - Generate generate = new Generate( - userId, - taskId, - level1Type, - level2Type, - generateDTO.getText(), - "text(" + gender + ")", - useModel, // 记录使用的具体模型名称 - new Date() - ); - save(generate); - - 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); - - } catch (Exception e) { - log.error("Doubao image generation failed for taskId: {}", taskId, e); - LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); - Account account = accountService.getOne(select); - - // 根据用户语言设置默认错误信息 - String errorMessage = "图像生成失败,请稍后重试"; // 默认中文 - String userLanguage = "CHINESE_SIMPLIFIED"; // 默认语言 - - if (account != null && account.getLanguage() != null) { - userLanguage = account.getLanguage(); - if (account.getLanguage().equals("ENGLISH")) { - errorMessage = "Image generation failed, please try again later"; - } else if (account.getLanguage().equals("CHINESE_SIMPLIFIED")) { - errorMessage = "图像生成失败,请稍后重试"; - } - } - String errorCode = null; - - // 检查是否为ArkHttpException,提取code和detailMessage - try { - // 通过反射获取code字段 - java.lang.reflect.Field codeField = e.getClass().getDeclaredField("code"); - codeField.setAccessible(true); - Object codeValue = codeField.get(e); - if (codeValue != null) { - errorCode = codeValue.toString(); - } - - // 通过反射获取detailMessage字段 - e.printStackTrace(); - String detailMessageValue = e.getMessage(); - if (detailMessageValue != null) { - String detailMessage = detailMessageValue.toString(); - - // 根据错误码返回相应的用户友好错误信息 - errorMessage = getDoubaoErrorMessage(errorCode, detailMessage, userLanguage); - } - } catch (Exception reflectionException) { - reflectionException.printStackTrace(); - log.warn("Failed to extract error details from ArkHttpException: {}", reflectionException.getMessage()); - // 如果反射失败,尝试从异常消息中提取信息 - String exceptionMessage = e.getMessage(); - if (exceptionMessage != null) { - boolean isEnglish = "ENGLISH".equals(userLanguage); - if (exceptionMessage.contains("OutputImageSensitiveContentDetected")) { - errorMessage = isEnglish ? "Generated image may contain sensitive content, please try with different input" : "生成的图像可能包含敏感信息,请更换输入内容后重试"; - } else if (exceptionMessage.contains("InputTextSensitiveContentDetected")) { - errorMessage = isEnglish ? "Input text may contain sensitive content, please try again" : "输入文本可能包含敏感信息,请更换后重试"; - } else if (exceptionMessage.contains("InputImageSensitiveContentDetected")) { - errorMessage = isEnglish ? "Input image may contain sensitive content, please try again" : "输入图像可能包含敏感信息,请更换后重试"; - } else if (exceptionMessage.contains("SensitiveContentDetected")) { - errorMessage = isEnglish ? "Input content may contain sensitive information, please try again" : "输入内容可能包含敏感信息,请更换后重试"; - } else if (exceptionMessage.contains("MissingParameter")) { - errorMessage = isEnglish ? "Request parameters are incomplete, please check input" : "请求参数不完整,请检查输入内容"; - } else if (exceptionMessage.contains("InvalidParameter")) { - errorMessage = isEnglish ? "Request parameters are invalid, please check input format" : "请求参数无效,请检查输入格式"; - } else if (exceptionMessage.contains("InvalidImageURL")) { - errorMessage = isEnglish ? "Image format is incorrect or data is corrupted, please re-upload" : "图片格式不正确或数据损坏,请重新上传"; - } else if (exceptionMessage.contains("OutofContextError")) { - errorMessage = isEnglish ? "Input content is too long, please shorten text or image size" : "输入内容过长,请缩短文本或图片尺寸"; - } else if (exceptionMessage.contains("AuthenticationError")) { - errorMessage = isEnglish ? "Service authentication failed, please contact administrator" : "服务认证失败,请联系管理员"; - } else if (exceptionMessage.contains("TooManyRequests") || exceptionMessage.contains("429")) { - errorMessage = isEnglish ? "Too many requests, please try again later" : "请求过于频繁,请稍后重试"; - } else if (exceptionMessage.contains("InternalServerError") || exceptionMessage.contains("500")) { - errorMessage = isEnglish ? "Image generation service is temporarily unavailable, please try again later" : "图像生成服务暂时不可用,请稍后重试"; - } - } - } - - // 更新Redis状态为失败 - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, errorMessage, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - // 记录详细错误信息用于调试 - log.error("Doubao API error - taskId: {}, errorCode: {}, errorMessage: {}", - taskId, errorCode, errorMessage); - - } - }); - - 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 - */ - private boolean isFluxPatternModel(GenerateThroughImageTextDTO generateDTO) { - return !StringUtil.isNullOrEmpty(generateDTO.getModelName()) - && "flux".equals(generateDTO.getModelName()) - && "Pattern".equals(generateDTO.getLevel2Type()); - } - - /** - * 处理flux pattern生成 - */ - private PrepareForGenerateVO handleFluxPatternGeneration(GenerateThroughImageTextDTO generateDTO) { - // 校验积分是否足够 - validateCredits(CreditsEventsEnum.FLUX_IMG2IMG); - - // 获取图片路径 - String imagePath = geti2iImagePath(generateDTO); - - // 创建生成任务 - String taskId = flux(PATTERN, generateDTO.getText(), imagePath, false); - - // 保存生成记录 - saveGenerateRecord(generateDTO, taskId, imagePath); - - // 处理积分扣除 - processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.FLUX_IMG2IMG); - - return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); - } - - /** - * 获取i2i需要传入模型的图片路径 - */ - private String geti2iImagePath(GenerateThroughImageTextDTO generateDTO) { - if (Objects.isNull(generateDTO.getCollectionElementId()) - || StringUtil.isNullOrEmpty(generateDTO.getDesignType())) { - return null; - } - - switch (generateDTO.getDesignType()) { - case "collection": - CollectionElement element = collectionElementMapper.selectById(generateDTO.getCollectionElementId()); - return element != null ? element.getUrl() : null; - case "library": - Library library = libraryService.getById(generateDTO.getCollectionElementId()); - return library != null ? library.getUrl() : null; - default: - return null; - } - } - - /** - * 保存生成记录 - */ - private void saveGenerateRecord(GenerateThroughImageTextDTO generateDTO, String taskId, String imagePath) { - Generate generate = CopyUtil.copyObject(generateDTO, Generate.class); - generate.setAccountId(generateDTO.getUserId()); - generate.setUniqueId(taskId); - generate.setElementSource(generateDTO.getDesignType()); - generate.setElementId(generateDTO.getCollectionElementId()); - - // 确定生成类型 - String generateType = determineGenerateType(generateDTO); - generate.setGenerateType(generateType); - generate.setModelName("flux"); - generate.setCreateDate(new Date()); - - save(generate); - } - - /** - * 确定生成类型 - */ - private String determineGenerateType(GenerateThroughImageTextDTO generateDTO) { - if (Objects.nonNull(generateDTO.getCollectionElementId())) { - return StringUtil.isNullOrEmpty(generateDTO.getText()) ? "image" : "text-image"; - } - return "text"; - } - - /** - * 处理标准生成流程 - */ - private PrepareForGenerateVO handleStandardGeneration(GenerateThroughImageTextDTO generateDTO) { - // 确定积分事件和生成次数 - GenerationConfig config = determineGenerationConfig(generateDTO); - - // 对Slogan功能限流 - if (config.creditsEvent.equals(CreditsEventsEnum.SLOGAN)) { - boolean b = redisUtil.allowRequest("Slogan"); - if (!b) { - return new PrepareForGenerateVO(429); - } - } - - // 校验积分是否足够 - validateCredits(config.creditsEvent); - - // 创建生成任务 - List taskIds = createGenerationTasks(generateDTO, config.times); - - // 截取任务id - String original = taskIds.get(0); - String taskId = original.substring(0, original.lastIndexOf("-", original.lastIndexOf("-") - 1)); - - // 处理积分扣除 - processCreditDeduction(generateDTO.getUserId(), taskId, config.creditsEvent); - - return new PrepareForGenerateVO(taskIds, 200); - } - - /** - * 确定生成配置(积分事件和生成次数) - */ - private GenerationConfig determineGenerationConfig(GenerateThroughImageTextDTO generateDTO) { - CreditsEventsEnum creditsEvent = CreditsEventsEnum.OTHER; - int times = 4; - - // 根据不同类型确定配置 - // high -> 生成图片质量高,但生成速度慢,每次生成只返回一张图片 - // fast -> 生成图片质量低,但生成速度快,每次生成返回四张图片 - switch (generateDTO.getLevel1Type()) { - case "Printboard": - GenerationConfig generationConfig = handlePrintboardConfig(generateDTO); - creditsEvent = generationConfig.creditsEvent; - times = generationConfig.times; - break; - case "Moodboard": - creditsEvent = CreditsEventsEnum.MOOD_BOARD; - if (isHighModel(generateDTO)) { - creditsEvent = CreditsEventsEnum.LOCAL_TEXT2IMG_HIGH; - times = 1; - } - break; - case "Sketchboard": - creditsEvent = CreditsEventsEnum.SKETCH_BOARD; - if (isHighModel(generateDTO)) { - creditsEvent = CreditsEventsEnum.LOCAL_TEXT2IMG_HIGH; - times = 1; - } - break; - } - - return new GenerationConfig(creditsEvent, times); - } - - /** - * 处理Printboard的特殊配置 - */ - private GenerationConfig handlePrintboardConfig(GenerateThroughImageTextDTO generateDTO) { - String level2Type = generateDTO.getLevel2Type(); - CreditsEventsEnum creditsEvent = CreditsEventsEnum.OTHER; - int times = 4; - - if (!CollectionLevel2TypeEnum.printType().contains(level2Type)) { - throw new BusinessException("unknown.parameter.level2Type"); - } - - switch (level2Type) { - case "Pattern": - // Pattern参数校验 - Generate generate = new Generate(); - validateGeneraType(generate, generateDTO.getText(), generateDTO.getCollectionElementId()); - generateDTO.setGenerateType(generate.getGenerateType()); - - creditsEvent = CreditsEventsEnum.PATTERN; - if (isHighModel(generateDTO)) { - creditsEvent = CreditsEventsEnum.LOCAL_TEXT2IMG_HIGH; - times = 1; - } - break; - - case "Slogan": - validateSloganParams(generateDTO); - processSloganImage(generateDTO); - creditsEvent = CreditsEventsEnum.SLOGAN; - times = 1; - break; - - case "Logo": - validateLogoParams(generateDTO); - generateDTO.setSeed(String.valueOf(random.nextInt(501))); - creditsEvent = CreditsEventsEnum.LOGO; - times = 1; - break; - } - - return new GenerationConfig(creditsEvent, times); - } - - /** - * 校验Slogan参数 - */ - private void validateSloganParams(GenerateThroughImageTextDTO generateDTO) { - if (StringUtil.isNullOrEmpty(generateDTO.getSloganBase64())) { - log.error("Printboard-Slogan模式下,slogan image为空"); - throw new BusinessException("slogan.image.cannot.be.empty"); - } - if (StringUtil.isNullOrEmpty(generateDTO.getText())) { - log.error("Printboard-Slogan模式下,slogan text为空"); - throw new BusinessException("slogan.style.cannot.be.empty"); - } - } - - /** - * 处理Slogan图片上传 - */ - private void processSloganImage(GenerateThroughImageTextDTO generateDTO) { - // 上传图片到服务器 - String path = minioUtil.base64UploadToPath(generateDTO.getSloganBase64(), sloganBucket, null); - String name = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); - - // 保存到数据库 - CollectionElement element = new CollectionElement(); - element.setAccountId(generateDTO.getUserId()); - element.setCollectionId(0L); - element.setLevel1Type(PRINT_BOARD.getRealName()); - element.setLevel2Type(CollectionLevel2TypeEnum.SLOGAN.getRealName()); - element.setName(name); - element.setUrl(path); - element.setHasPin((byte) 0); - element.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(path, 24 * 60), Boolean.FALSE)); - element.setCreateDate(DateUtil.getByTimeZone(generateDTO.getTimeZone())); - collectionElementService.save(element); - - // 更新DTO - generateDTO.setCollectionElementId(element.getId()); - generateDTO.setSloganBase64(null); - generateDTO.setDesignType("collection"); - } - - /** - * 校验Logo参数 - */ - private void validateLogoParams(GenerateThroughImageTextDTO generateDTO) { - if (StringUtil.isNullOrEmpty(generateDTO.getText().trim())) { - throw new BusinessException("please.input.the.prompt"); - } - } - - /** - * 判断是否为high模型 - */ - private boolean isHighModel(GenerateThroughImageTextDTO generateDTO) { - return !StringUtil.isNullOrEmpty(generateDTO.getModelName()) - && "high".equals(generateDTO.getModelName()); - } - - /** - * 校验积分是否足够 - */ - private void validateCredits(CreditsEventsEnum creditsEvent) { - if (!creditsService.creditsPreDeduction(creditsEvent, 1)) { - throw new BusinessException("remaining.credits.insufficient", ResultEnum.WARNING.getCode()); - } - } - - /** - * 创建生成任务 - */ - private List createGenerationTasks(GenerateThroughImageTextDTO generateDTO, int times) { - String uuid = UUID.randomUUID().toString(); - List taskIds = new ArrayList<>(); - - // 特殊处理:某些情况下需要清空modelName - if ("Printboard".equals(generateDTO.getLevel1Type()) - && !"Pattern".equals(generateDTO.getLevel2Type())) { - // Logo 和 Slogan 没有模型可选 - generateDTO.setModelName(null); - } - - for (int i = 1; i <= times; i++) { - String taskId = uuid + "-" + i + "-" + generateDTO.getUserId(); - taskIds.add(taskId); - generateDTO.setUniqueId(taskId); - - // 序列化为JSON - String jsonString = JSON.toJSONString(generateDTO); - - // 加入Redis队列 - addToRedisQueue(taskId, jsonString); - } - - return taskIds; - } - - /** - * 添加到Redis队列 - */ - private void addToRedisQueue(String taskId, String jsonString) { - // 加入排队队列 -// Double maxScore = redisUtil.getMaxScore(consumptionOrderKey); -// redisUtil.addToZSet(consumptionOrderKey, taskId, maxScore); - - // 加入结果映射 - String key = generateResultKey + ":" + taskId; - GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Waiting"); - redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - // 发布到MQ - rabbitMQService.publishMessageToGenerate(jsonString); - } - - /** - * 处理积分扣除 - */ - private void processCreditDeduction(Long userId, String taskId, CreditsEventsEnum creditsEvent) { - // 添加到Redis - creditsService.addRecordToCreditsDeduction(userId, taskId, creditsEvent); - // 预插入到数据库 - creditsService.preInsert(userId, creditsEvent.getName(), taskId, Boolean.TRUE, null); - } - -// ============== 配置类 ============== - - /** - * 生成任务配置类 - * 包含积分事件类型和生成次数 - */ - private static class GenerationConfig { - final CreditsEventsEnum creditsEvent; - final int times; - - GenerationConfig(CreditsEventsEnum creditsEvent, int times) { - this.creditsEvent = creditsEvent; - this.times = times; - } - } - - @Override - public Long getRankPosition(String uniqueId) { - // rank 从0开始 - return redisUtil.getRank(consumptionOrderKey, uniqueId); - } - - @Override - public List getGenerateResultList(List taskIdList) { - List results = new ArrayList<>(); - Set collect = new HashSet<>(); - boolean flag = true; - String type = null; - for (String taskId : taskIdList) { - String key = generateResultKey + ":" + taskId; - GenerateResultVO generateResultVO = new Gson().fromJson(redisUtil.getFromString(key), GenerateResultVO.class); - - if (flag) { - type = resolveModelType(taskId, null); - flag = false; - } - // 暂定万象每次生成1个 - 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; - } else if (type.equals("flux")) { - results.add(getFluxResultAndSave(taskId)); - continue; - } - - if (generateResultVO != null && !StringUtil.isNullOrEmpty(generateResultVO.getUrl())) { - String url = generateResultVO.getUrl(); - if (url.substring(url.lastIndexOf("/") + 1).equals("white_image.jpg")) { - generateResultVO.setStatus("Invalid"); - } else { - generateResultVO.setUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - } else if (generateResultVO == null) { - generateResultVO = new GenerateResultVO(); - } - - if (!StringUtil.isNullOrEmpty(generateResultVO.getStatus())) { - collect.add(generateResultVO.getStatus()); - } - results.add(generateResultVO); - } - - if (taskIdList.size() == 4 && collect.size() == 1 && collect.contains("Fail")) { - log.info("当前4个生成结果均为失败"); - throw new BusinessException("generate.result.below.standard"); - } - return results; - } - - - public Generate selectByUniqueId(String uniqueId) { - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("unique_id", uniqueId); - - return getOne(qw); - } - - public List selectListByUniqueId(String uniqueId) { - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("unique_id", uniqueId).orderByDesc("id"); - - return baseMapper.selectList(qw); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void cancelGenerate(Long userId, List uniqueIdList, String timeZone, String type) { - // todo 取消待优化 - uniqueIdList.forEach(uniqueId -> { - // 1、将需要取消的唯一id加入redis,以便及时取消生成 - redisUtil.addToSet(cancelSetKey, uniqueId, CommonConstant.REDIS_SET_EXPIRE_TIME); - - /*// 1、确认当前消息是否还在排队中 - Boolean exists = redisUtil.isElementExistsInZSet(consumptionOrderKey, uniqueId); - Boolean flag = Boolean.FALSE; - if (exists) flag = redisUtil.getRank(consumptionOrderKey, uniqueId) > 1L ? Boolean.TRUE : Boolean.FALSE; - // 不管flag的默认值是true还是false,只要exists为false,&& 将短路 - if (exists && flag) { - // 1.1、将需要取消的唯一id加入redis,以便及时取消生成 - redisUtil.addToSet(cancelSetKey, uniqueId); - // 1.2 将需要取消的id从redis的ConsumptionOrder中删除 - redisUtil.removeFromZSet(consumptionOrderKey, uniqueId); - } else { - // 2、判断该消息是否异常 - boolean hasKey = redisUtil.isElementExistsInMap(exceptionMapKey, uniqueId); - // 3、判断该消息是否已经消费结束 - Boolean existsInResult = redisUtil.isElementExistsInMap(resultMapKey, uniqueId); - if (!hasKey && !existsInResult) { - // 设置取等待状态为false - AsyncCallerUtil.waitingStatus.put(uniqueId, false); - // 3、直接发送取消请求到python端 - pythonService.cancelGenerateTask(uniqueId); - } - }*/ - String path; - if (type.equals("Logo")) { - path = CommonConstant.GENERATE_LOGO_SINGLE_CANCEL; - } else if (type.equals("PoseTransformation")) { - path = CommonConstant.POSE_TRANSFORMATION_CANCEL; - } else { - path = CommonConstant.GENERATE_CANCEL; - } - - String key = generateResultKey + ":" + uniqueId; - GenerateResultVO generateResultVO = new Gson().fromJson(redisUtil.getFromString(key), GenerateResultVO.class); - if (Objects.isNull(generateResultVO)) { - log.warn("任务不存在,无法取消"); - return; - } - // 判断当前task的状态是不是Fail - if (!generateResultVO.getStatus().equals("Fail")) { - // 2、不是,直接发送取消请求到python端 - pythonService.cancelGenerateTask(uniqueId, path); - // 3、更改result中当前taskId的状态 - redisUtil.addToString(key, new Gson().toJson(new GenerateResultVO(uniqueId, null, null, "Cancelled")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } - - // 3、考虑加一张表,专门用于记录哪些用户在什么时间进行了取消操作,包括已经异常的请求 - GenerateCancel generateCancel = new GenerateCancel(userId, uniqueId, DateUtil.getByTimeZone(timeZone)); - generateCancelMapper.insert(generateCancel); - }); - - - } - - @Override - public void processRelightResult(String taskId, String url, String category) { - QueryWrapper qw = new QueryWrapper<>(); - qw.lambda().eq(ToProductImageResult::getTaskId, taskId); - List toProductImageResults = toProductImageResultMapper.selectList(qw); - if (CollectionUtils.isEmpty(toProductImageResults)) { - return; -// throw new BusinessException(""); - } - ToProductImageResult toProductImageResult = toProductImageResults.get(0); - if (toProductImageResult.getBrightenValue() != null && toProductImageResult.getBrightenValue() != 1.0) { - pythonService.bright(url, toProductImageResult.getBrightenValue()); - } - toProductImageResult.setUrl(url); - toProductImageResult.setStatus("Success"); -// toProductImageResult.setResultType("Relight"); - toProductImageResultMapper.updateById(toProductImageResult); - - String key = relightResultKey + ":" + taskId; - String imageName = url.substring(url.lastIndexOf("/") + 1); - String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; - GenerateResultVO generateResultVO = new GenerateResultVO(taskId, toProductImageResult.getId(), url, status, category); - redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - Long accountId = Long.parseLong(taskId.substring(taskId.lastIndexOf("-") + 1)); - if (!status.equals("Invalid")) { - // 4、扣除积分 - Boolean b = creditsService.taskCreditsDeduction(accountId, taskId); - // 3、记录积分变更 - if (b) creditsService.insertToCreditsDetail(accountId, - CreditsEventsEnum.RELIGHT.getName(), - CreditsEventsEnum.RELIGHT.getValue(), - "negative", null); - } - } - - // 判断试用用户试用generate机会是否使用完毕 每个board 3次机会 - private int getTrialsCount(Long userId, String level1Type) { - List getGenerateList = getGenerateByAccountId(userId, level1Type); - int trialsCount; - if (getGenerateList.isEmpty()) { - trialsCount = 0; - } else if (getGenerateList.size() == 1 || (getGenerateList.size() >= 4 && getGenerateList.size() / 4 == 1)) { - trialsCount = 1; - } else if (getGenerateList.size() == 2 || (getGenerateList.size() >= 4 && getGenerateList.size() / 4 == 2)) { - trialsCount = 2; - } else { - trialsCount = 2; - } - return trialsCount; - } - - public List getGenerateByAccountId(Long accountId, String level1Type) { - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("account_id", accountId); - qw.eq("level1_type", level1Type); - - return baseMapper.selectList(qw); - } - - public List> getCountByUserAndTime(String startTime, String endTime, List accountIdList) { - List> byTypeAndTime = baseMapper.getByTypeAndTime(startTime, endTime, accountIdList); - return byTypeAndTime; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public GenerateResultVO imageToSketch(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, Long projectId) { - - Long accountId = UserContext.getUserHolder().getId(); - log.info("imageToSketch parameter : {}", imageToSketchDTO); - - // 检查积分是否够本次扣除 - CreditsEventsEnum event = CreditsEventsEnum.IMAGE_TO_SKETCH; - Boolean b = creditsService.checkCredits(accountId, event, 1); - if (!b) { - throw new BusinessException("remaining.credits.insufficient", ResultEnum.PROMPT.getCode()); - } - - String style = imageToSketchDTO.getStyle(); - String styleCode = style.equals(SketchStyle.THICK.getValue()) ? "1" : - style.equals(SketchStyle.MEDIUM.getValue()) ? "2" : - style.equals(SketchStyle.THIN.getValue()) ? "3" : "Custom"; - - // 线稿提取 - String sketchPath = requestSketchExtract(imageToSketchDTO, collagePictureUrl, accountId, styleCode); - // 存数据库 - Generate generate = saveExtractSketchRequest(imageToSketchDTO, collagePictureUrl, projectId, - accountId, styleCode, "local", "0"); - GenerateResultVO generateResultVO = saveExtractSketchResult(generate, sketchPath, imageToSketchDTO.getGender()); - // 积分扣除 - doCreditsSubtract(accountId, event); - - return generateResultVO; - } - - private String requestSketchExtract(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, - Long accountId, String styleCode) { - String imagePath; - if (StringUtil.isNullOrEmpty(collagePictureUrl)) { - CollectionElement collectionElement = collectionElementService.getById(imageToSketchDTO.getElementId()); - imagePath = collectionElement.getUrl(); - } else { - imagePath = collagePictureUrl; - } - - log.info(minioUtil.getPreSignedUrl(imagePath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - String imageName = UUID.randomUUID().toString(); - String objectName = accountId + "/imageToSketch/" + imageName; - - String styleImage; - if (!Objects.isNull(imageToSketchDTO.getStyleImageId())) { - CollectionElement styleElement = collectionElementService.getById(imageToSketchDTO.getElementId()); - styleImage = styleElement.getUrl(); - } else { - styleImage = ""; - } - String sketchPath = pythonService.imageToSketch(imagePath, userBucket, objectName, styleCode, styleImage); - log.info("初步图片提取结果:{}", sketchPath); - return sketchPath; - } - - private Generate saveExtractSketchRequest(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, - Long projectId, Long accountId, String styleCode, - String modelName, String taskId) { - // 存DB - Generate generate = new Generate(); - generate.setAccountId(accountId); - generate.setUniqueId(taskId); - generate.setLevel1Type(SKETCH_BOARD.getRealName()); - generate.setLevel2Type("ImageToSketch"); - generate.setElementSource("collection"); - generate.setElementId(imageToSketchDTO.getElementId()); - generate.setGenerateType("image(" + imageToSketchDTO.getGender() + ")"); - generate.setModelName(modelName); - generate.setSketchStyle(styleCode); - generate.setStyleImageElementId(imageToSketchDTO.getStyleImageId()); - generate.setProjectId(projectId); - generate.setInputImageUrl(collagePictureUrl); - generate.setCreateDate(new Date()); - baseMapper.insert(generate); - return generate; - } - - public GenerateResultVO saveExtractSketchResult(Generate generate, String sketchPath, String gender) { - // 将生成结果存入DB - GenerateDetail generateDetail = new GenerateDetail(); - generateDetail.setGenerateId(generate.getId()); - generateDetail.setUrl(sketchPath); - generateDetail.setIsLike((byte) 0); - generateDetail.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(sketchPath, 24 * 60), Boolean.FALSE)); - generateDetail.setCreateDate(LocalDateTime.now()); - generateDetailMapper.insert(generateDetail); - - String clothCategory = pythonService.getClothCategory(sketchPath, gender); - - return new GenerateResultVO(generate.getUniqueId(), generateDetail.getId(), - minioUtil.getPreSignedUrl(sketchPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), "Success", clothCategory); - } - - public void doCreditsSubtract(Long accountId, CreditsEventsEnum event) { - Account account = accountService.getById(accountId); - BigDecimal existingCredits = account.getCredits(); - BigDecimal subtract = existingCredits.subtract(new BigDecimal(event.getValue())); - BigDecimal creditsUsage = null; - if (!StringUtil.isNullOrEmpty(account.getOrganizationName())) { - creditsUsage = Objects.isNull(account.getCreditsUsage()) ? BigDecimal.ZERO : account.getCreditsUsage(); - creditsUsage = creditsUsage.add(new BigDecimal(event.getValue())); - } - accountService.updateCreditsAndEndTime(account, subtract.toString(), null, creditsUsage); - creditsService.preInsert(accountId, event.getName(), null, Boolean.FALSE, event.getValue()); - } - - // 注入线程池(可在配置类中定义) - @Resource - private Executor asyncTaskExecutor; - - public String imageToSketchAsync(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, Long projectId) { - Long accountId = UserContext.getUserHolder().getId(); - log.info("imageToSketch parameter : {}", imageToSketchDTO); - // 目前没有用其他提取模型,暂时写死flux - imageToSketchDTO.setModelName("flux"); - - // 检查积分是否够本次扣除 -// CreditsEventsEnum event = CreditsEventsEnum.IMAGE_TO_SKETCH; - CreditsEventsEnum event = CreditsEventsEnum.IMAGE_TO_SKETCH_FLUX; - Boolean b = creditsService.checkCredits(accountId, event, 1); - if (!b) { - throw new BusinessException("remaining.credits.insufficient", ResultEnum.PROMPT.getCode()); - } - - // 生成唯一任务ID - String taskId; - if (!StringUtil.isNullOrEmpty(imageToSketchDTO.getModelName()) - && imageToSketchDTO.getModelName().equals("flux")) { - String imagePath; - // todo 拼贴图的线稿提取是否能用flux - if (StringUtil.isNullOrEmpty(collagePictureUrl)) { - CollectionElement collectionElement = collectionElementService.getById(imageToSketchDTO.getElementId()); - imagePath = collectionElement.getUrl(); - } else { - imagePath = collagePictureUrl; - } - taskId = flux(event, null, imagePath, false); - // 存数据库 - saveExtractSketchRequest(imageToSketchDTO, collagePictureUrl, projectId, - accountId, imageToSketchDTO.getStyle(), "flux", taskId); - - // 6、添加预扣除积分到redis - creditsService.addRecordToCreditsDeduction(accountId, taskId, event); - // 6.1 添加积分扣除记录到db - creditsService.preInsert(accountId, event.getName(), taskId, Boolean.TRUE, null); - - return taskId; - } - - taskId = UUID.randomUUID().toString(); - // 异步执行耗时操作(由于prompt提取耗时较长,页面暂时只提供flux生成,废弃以下部分) - CompletableFuture.runAsync(() -> { - try { - processImageToSketch(taskId, imageToSketchDTO, collagePictureUrl, projectId, accountId, event); - } catch (Exception e) { - log.error("异步处理图片转sketch失败, taskId: {}", taskId, e); - // 更新redis - redisUtil.addToString(generateResultKey + ":" + taskId, new Gson().toJson(new GenerateResultVO(taskId, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } - }, asyncTaskExecutor); - return taskId; - } - - private void processImageToSketch(String taskId, ImageToSketchDTO imageToSketchDTO, - String collagePictureUrl, Long projectId, - Long accountId, CreditsEventsEnum event) throws IOException { - // 设置任务状态为处理中 - redisUtil.addToString(generateResultKey + ":" + taskId, new Gson().toJson(new GenerateResultVO(taskId, "Executing")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - String style = imageToSketchDTO.getStyle(); - String styleCode = style.equals(SketchStyle.THICK.getValue()) ? "1" : - style.equals(SketchStyle.MEDIUM.getValue()) ? "2" : - style.equals(SketchStyle.THIN.getValue()) ? "3" : "Custom"; - // 请求记录存数据库 - Generate generate = saveExtractSketchRequest(imageToSketchDTO, collagePictureUrl, projectId, - accountId, styleCode, "freepik", taskId); - // 1、初步提取结果 - String sketchPath = requestSketchExtract(imageToSketchDTO, collagePictureUrl, accountId, styleCode); - // 2、获取输入图的描述 - String imageDescription = getImageDescription(sketchPath); - // 3、请求freepik reimage - String dataStr = reimagineFreePik(sketchPath, imageDescription, "vivid"); - if (StringUtil.isNullOrEmpty(dataStr)) { - throw new BusinessException("extract sketch failed"); - } - - JSONObject data = JSONUtil.parseObj(dataStr); - String upgradeImageUrl = data.getBeanList("generated", String.class).get(0); - String freepikTaskId = data.getStr("task_id"); - - // 4、下载图片 - byte[] bytes = downloadVideoOrImage(upgradeImageUrl); -// byte[] bytes = downloadWithProxy(upgradeImageUrl); - // 5、上传图片到minio保存 - String objectName = accountId + "/imageToSketch/" + freepikTaskId + ".png"; - minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png"); - // 6、保存结果到db - GenerateResultVO generateResultVO = saveExtractSketchResult(generate, userBucket + "/" + objectName, imageToSketchDTO.getGender()); - // 7、积分扣除 - doCreditsSubtract(accountId, event); - // 8、将结果存入Redis - redisUtil.addToString(generateResultKey + ":" + taskId, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } - - // 对提取出来的sketch做调整 - // 输入 base64,以及 性别 分类,将图片添加到library - @Override - @Transactional(rollbackFor = Exception.class) - public GenerateResultVO modifySketch(GenerateModifyDTO generateModifyDTO) { - log.info("修改生成或library中的sketch或print,并加入到library"); - - // 提取常用参数 - Long accountId = UserContext.getUserHolder().getId(); - String base64 = generateModifyDTO.getBase64(); - String gender = generateModifyDTO.getGender().toLowerCase(); - String category = generateModifyDTO.getCategory(); - Long originalId = generateModifyDTO.getOriginalId(); - String originalIdSource = generateModifyDTO.getOriginalIdSource(); - boolean isOverride = generateModifyDTO.getIsOverride(); - boolean isSketch = generateModifyDTO.getType().equals(SKETCH_BOARD.getRealName()); - - // 获取原始路径和可能的generateId - PathInfo pathInfo = getOriginalPathAndGenerateId(originalIdSource, originalId); - if (Objects.isNull(pathInfo)) { - throw new BusinessException("unknown sourceIdType", ResultEnum.PROMPT.getCode()); - } - - // 确定存储路径 - String storagePath = isOverride - ? pathInfo.originalPath.replaceFirst("^[^/]+/", "").replaceFirst("\\.[^.]+$", "") - : isSketch - ? accountId + "/sketchboard/" + gender + "/" + category + "/" + UUID.randomUUID() - : accountId + "/printboard/" + UUID.randomUUID(); - - // 上传到MinIO - String minioPath = minioUtil.base64UploadToPath(base64, userBucket, storagePath); - log.info("修改后的图片:{}", minioPath); - - // 保存到数据库并返回结果 - return originalIdSource.equals("Library") - ? handleLibrarySave(accountId, originalId, minioPath, category, gender, isOverride, generateModifyDTO.getType()) - : originalIdSource.equals("Generate") - ? handleGenerateSave(originalId, pathInfo.generateId, minioPath, category, isOverride) - : handleUploadSave(accountId, originalId, minioPath, category, generateModifyDTO.getGender(), isOverride, generateModifyDTO.getType()); - } - - private static class PathInfo { - String originalPath; - Long generateId; - - PathInfo(String originalPath, Long generateId) { - this.originalPath = originalPath; - this.generateId = generateId; - } - } - - private PathInfo getOriginalPathAndGenerateId(String originalIdSource, Long originalId) { - switch (originalIdSource) { - case "Library": - return new PathInfo(libraryService.getById(originalId).getUrl(), null); - case "Generate": - GenerateDetail detail = generateDetailMapper.selectById(originalId); - return new PathInfo(detail.getUrl(), detail.getGenerateId()); - case "Collection": - CollectionElement collectionElement = collectionElementMapper.selectById(originalId); - return new PathInfo(collectionElement.getUrl(), null); - default: - return null; - } - } - - private GenerateResultVO handleLibrarySave(Long accountId, Long libraryId, String minioPath, - String category, String gender, boolean isOverride, String level1Type) { - Library library; - String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false); - if (isOverride) { - library = new Library(); - library.setId(libraryId); - library.setUrl(minioPath); - library.setUpdateDate(new Date()); - libraryService.updateById(library); - } else { - library = new Library(accountId, level1Type, category, gender, minioPath, md5, new Date()); - libraryService.save(library); - libraryId = library.getId(); - } - return buildResultVO(libraryId, minioPath, category); - } - - private GenerateResultVO handleGenerateSave(Long originalId, Long generateId, String minioPath, - String category, boolean isOverride) { - GenerateDetail generateDetail = new GenerateDetail(); - String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true), Boolean.FALSE); - if (isOverride) { - generateDetail.setId(originalId); - generateDetail.setUrl(minioPath); - generateDetail.setUpdateDate(new Date()); - generateDetailMapper.updateById(generateDetail); - } else { - generateDetail.setGenerateId(generateId); - generateDetail.setUrl(minioPath); - generateDetail.setIsLike((byte) 0); - generateDetail.setMd5(md5); - generateDetail.setCreateDate(LocalDateTime.now()); - generateDetailMapper.insert(generateDetail); - originalId = generateDetail.getId(); - } - return buildResultVO(originalId, minioPath, category); - } - - private GenerateResultVO handleUploadSave(Long accountId, Long originalId, String minioPath, - String category, String gender, boolean isOverride, String level1Type) { - CollectionElement collectionElement; - String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false); - if (isOverride) { - collectionElement = new CollectionElement(); - collectionElement.setId(originalId); - collectionElement.setUrl(minioPath); - collectionElement.setMd5(md5); - collectionElement.setUpdateDate(new Date()); - collectionElementMapper.updateById(collectionElement); - } else { - CollectionElement originalElement = collectionElementMapper.selectById(originalId); - String name = minioPath.substring(minioPath.lastIndexOf("/") + 1, minioPath.lastIndexOf(".")); - if ("Sketchboard".equals(level1Type)) { - collectionElement = new CollectionElement(accountId, level1Type, category, gender, name, minioPath, (byte) 0, md5, new Date(), originalElement.getProjectId()); - } else { - collectionElement = new CollectionElement(accountId, level1Type, category, name, minioPath, (byte) 0, md5, new Date(), originalElement.getProjectId()); - } - collectionElementMapper.insert(collectionElement); - } - return buildResultVO(collectionElement.getId(), minioPath, category); - } - - private GenerateResultVO buildResultVO(Long id, String minioPath, String category) { - String url = minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true); - return new GenerateResultVO(id, url, "Success", category); - } - - public ToProductImageResultVO poseTransform(PoseTransformDTO poseTransformDTO) { - // 参数判断 - handleMotionParams(poseTransformDTO); - - Long accountId = UserContext.getUserHolder().getId(); - Long projectId = poseTransformDTO.getProjectId(); - String productImage = poseTransformDTO.getProductImage(); - Integer poseId = poseTransformDTO.getPoseId(); - boolean wxTask = StringUtil.isNullOrEmpty(poseTransformDTO.getModelName()) && poseTransformDTO.getModelName().equals("wx"); - - // 1、判断用户当前积分是否够本次生成消耗 - CreditsEventsEnum creditsEventsEnum = wxTask ? CreditsEventsEnum.WX_ANIMATION : CreditsEventsEnum.LOCAL_ANIMATION; - Boolean preDeduction = creditsService.creditsPreDeduction(creditsEventsEnum, 1); - if (!preDeduction) { - throw new BusinessException("remaining.credits.insufficient", ResultEnum.WARNING.getCode()); - } - - // 3、生成唯一id 使用uuid,由于uuid重复的几率很小,故取消对uuid重复性的校验 - String taskId; - Boolean isRequestSuccess = false; - PoseTransformation poseTransformation = new PoseTransformation(); - if (!StringUtil.isNullOrEmpty(poseTransformDTO.getModelName()) && poseTransformDTO.getModelName().equals("wx")) { - taskId = animateAnyone(poseTransformDTO, accountId); - if (!StringUtil.isNullOrEmpty(taskId)) { - isRequestSuccess = true; - apiGenerateService.addAPIGenerateRecordAsync(accountId, taskId, Module.poseTransfer.getValue(), "wx", "Pending"); - } - poseTransformation.setModelName("wx"); - } else { - String uuid = UUID.randomUUID().toString(); - taskId = uuid + "-" + accountId; - - com.alibaba.fastjson.JSONObject params = createParamsForMotion(poseTransformDTO, taskId); - String api = params.getString("api"); - params.remove("api"); - isRequestSuccess = pythonService.poseTransformation(params, api); - } - - poseTransformation.setProjectId(projectId); - poseTransformation.setAccountId(accountId); - poseTransformation.setUniqueId(taskId); - poseTransformation.setProductImage(productImage); - poseTransformation.setLastFrameProductImage(poseTransformDTO.getLastFrameProductImage()); - poseTransformation.setPoseId(poseId); - poseTransformation.setIsLiked((byte) 0); - String taskStatus = isRequestSuccess ? "Executing" : "Fail"; - poseTransformation.setTaskStatus(taskStatus); - poseTransformation.setPrompt(poseTransformDTO.getPrompt()); - poseTransformation.setCreateTime(LocalDateTime.now()); - poseTransformationMapper.insert(poseTransformation); - // 当需要默认like - ToProductImageResultVO toProductImageResultVO = new ToProductImageResultVO(); - toProductImageResultVO.setId(poseTransformation.getId()); - toProductImageResultVO.setParentId(poseTransformDTO.getParentId()); - toProductImageResultVO.setResultType(Module.poseTransfer.getValue()); - toProductImageResultVO.setTaskId(taskId); - toProductImageResultVO.setStatus(taskStatus); - toProductImageResultVO.setSourceUrl(minioUtil.getPreSignedUrl(productImage, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - toProductImageResultVO.setPoseId(poseId); - toProductImageResultVO.setModelName(poseTransformDTO.getModelName()); - - if (Objects.nonNull(poseTransformDTO.getIsDefaultLike()) && poseTransformDTO.getIsDefaultLike()) { - // 满足条件下添加到like - poseTransformation.setIsLiked((byte) 1); - poseTransformation.setUpdateTime(LocalDateTime.now()); - poseTransformationMapper.updateById(poseTransformation); - CollectionSort collectionSort = addPoseTransferLike(poseTransformDTO, poseTransformation.getId()); - Integer reSort = collectionSortService.rearrangeChildSort(poseTransformation.getId(), CollectionType.POSE_TRANSFORM.getValue(), - poseTransformDTO.getParentId(), poseTransformDTO.getUserLikeSortId()); - toProductImageResultVO.setSort(Objects.isNull(reSort) ? Objects.isNull(collectionSort) ? null : collectionSort.getSort() : reSort); - toProductImageResultVO.setParentId(poseTransformDTO.getParentId()); - toProductImageResultVO.setUserLikeSortId(Objects.isNull(collectionSort) ? null : collectionSort.getId()); - } else if (Objects.nonNull(poseTransformDTO.getIsDefaultLike()) && Objects.nonNull(poseTransformDTO.getParentId())) { - toProductImageResultVO.setParentId(poseTransformDTO.getParentId()); - } - - - if (isRequestSuccess) { - // 6、添加预扣除积分到redis - creditsService.addRecordToCreditsDeduction(accountId, taskId, creditsEventsEnum); - // 6.1 添加积分扣除记录到db - creditsService.preInsert(accountId, creditsEventsEnum.getName(), taskId, Boolean.TRUE, null); - // 更新项目更新时间 - projectService.modifyProjectUpdateTime(projectId); - return toProductImageResultVO; - } - throw new BusinessException("pose.transformation.error", ResultEnum.ERROR.getCode()); - } - - private void handleMotionParams(PoseTransformDTO poseTransformDTO) { - if (Objects.isNull(poseTransformDTO.getMode()) || poseTransformDTO.getMode() == 0) { - throw new BusinessException("Mode cannot be empty"); - } - MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode()); - if (Objects.isNull(motionModeEnum)) { - throw new BusinessException("unknown.mode"); - } - if (poseTransformDTO.getMode() == 1 && Objects.isNull(poseTransformDTO.getPoseId())) { - throw new BusinessException("Please choose a pose"); - } - if (poseTransformDTO.getMode() == 3 && StringUtil.isNullOrEmpty(poseTransformDTO.getLastFrameProductImage())) { - throw new BusinessException("Last frame cannot be empty"); - } - } - - - private CollectionSort addPoseTransferLike(PoseTransformDTO poseTransformDTO, Long poseTransformationId) { - if (Objects.nonNull(poseTransformDTO.getParentId()) - && !poseTransformDTO.getParentId().equals(0L)) { - return disOrLikePose(poseTransformationId, "like", - poseTransformDTO.getProjectId(), poseTransformDTO.getParentId()); - } - return null; - } - - public void processPoseTransformResult(String taskId, String gifUrl, String videoUrl, String imageUrl) { - // 1、存储模型返回的数据 - PoseTransformation poseTransformation; - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("unique_id", taskId); - List poseTransformations = poseTransformationMapper.selectList(qw); - if (poseTransformations != null && poseTransformations.size() > 1) { - log.warn("通过taskId {} 查询到的PoseTransformation的结果不止一条", taskId); - } else if (poseTransformations == null || poseTransformations.isEmpty()) { - return; - } - poseTransformation = poseTransformations.get(0); - poseTransformation.setGifUrl(gifUrl); - poseTransformation.setVideoUrl(videoUrl); - poseTransformation.setFirstFrameUrl(imageUrl); - poseTransformation.setTaskStatus("Success"); - poseTransformation.setUpdateTime(LocalDateTime.now()); - poseTransformationMapper.updateById(poseTransformation); - - String key = generateResultKey + ":" + taskId; - PoseTransformationVO poseTransformationVO = new PoseTransformationVO( - poseTransformation.getId(), taskId, gifUrl, videoUrl, imageUrl, (byte) 0, "Success"); - poseTransformationVO.setPoseId(poseTransformation.getPoseId()); - poseTransformationVO.setModelName(poseTransformation.getModelName()); - - // 2、更新redis - redisUtil.addToString(key, new Gson().toJson(poseTransformationVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - // 3、执行积分扣除 - String accountId = taskId.substring(taskId.lastIndexOf("-") + 1); -// String uuid = taskId.substring(0, taskId.lastIndexOf("-")); - Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), taskId); - if (flag) creditsService.updateChangedCredits(accountId, taskId); - - Project project = projectService.getById(poseTransformation.getProjectId()); - // 发通知 - if (Objects.nonNull(project) && !StringUtil.isNullOrEmpty(project.getName())) { - messageCenterService.videoFinishedMsg(poseTransformation.getAccountId(), project.getName() , true); - } - } - - public List getPoseTransformationResult(List taskIdList, Long projectId, Boolean like) { - List resultList = new ArrayList<>(); - - // 处理按taskId列表查询的情况 - if (taskIdList != null && !taskIdList.isEmpty()) { - for (String taskId : taskIdList) { - PoseTransformationVO vo = buildPoseTransformationVO(taskId, null); - if (vo != null) { - resultList.add(vo); - } - } - } - // 处理按projectId和like查询的情况 - else if (projectId != null) { - QueryWrapper queryWrapper = new QueryWrapper() - .eq("project_id", projectId) - .ne("task_status", "Fail"); - - if (like != null) { - queryWrapper.eq("is_liked", like ? 1 : 0); - } - - List poseTransformations = poseTransformationMapper.selectList(queryWrapper); - - if (!CollectionUtils.isEmpty(poseTransformations)) { - for (PoseTransformation item : poseTransformations) { - PoseTransformationVO vo = buildPoseTransformationVO(item.getUniqueId(), item); - if (vo != null && !isInvalidStatus(vo.getStatus())) { - resultList.add(vo); - } - } - } - } - - return resultList; - } - - private PoseTransformationVO buildPoseTransformationVO(String taskId, PoseTransformation dbItem) { - String type = resolveModelType(taskId, CreditsEventsEnum.POSE_TRANSFORMATION.getValue()); - String key = generateResultKey + ":" + taskId; - String resultJson = redisUtil.getFromString(key); - - PoseTransformationVO vo; - - // 1. 优先从Redis获取数据 - if (!StringUtil.isNullOrEmpty(resultJson)) { - vo = new Gson().fromJson(resultJson, PoseTransformationVO.class); - - // 设置数据库中的额外字段 - if (dbItem != null) { - vo.setId(dbItem.getId()); - vo.setIsLiked(dbItem.getIsLiked()); - } - - // 处理成功状态的数据 - if ("Success".equals(vo.getStatus()) && !"wx".equals(type)) { - processAllUrls(vo, dbItem); - } - - vo.setResultType(CollectionType.POSE_TRANSFORM.getValue()); - } - // 2. 处理wx类型的情况 - else if ("wx".equals(type)) { - vo = getAnimateResult(taskId); - } - // 3. 处理既没有Redis数据也不是wx类型的情况 - else { - // 如果有数据库记录 - if (dbItem != null) { - vo = CopyUtil.copyObject(dbItem, PoseTransformationVO.class); - vo.setTaskId(taskId); - - // 设置产品图片URL(首帧) - if (dbItem.getProductImage() != null) { - vo.setProductImage(minioUtil.getPreSignedUrl( - dbItem.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - // 设置产品图片URL(尾帧) - if (dbItem.getLastFrameProductImage() != null) { - vo.setLastFrameProductImage(minioUtil.getPreSignedUrl( - dbItem.getLastFrameProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - - // 如果视频URL为空,直接返回 - if (StringUtil.isNullOrEmpty(dbItem.getVideoUrl())) { - return vo; - } - - processAllUrls(vo, dbItem); - } - // 如果没有数据库记录 - else { - vo = new PoseTransformationVO(taskId, "Executing"); - } - } - - AuthPrincipalVo userHolder = UserContext.getUserHolder(); - Long userId = userHolder.getId(); - if (userId != null){ - // 处理父ID逻辑 - processParentId(vo, dbItem != null ? dbItem : - poseTransformationMapper.selectOne(new QueryWrapper().eq("unique_id", taskId).eq("account_id", userId))); - - }else { - // 处理父ID逻辑 - processParentId(vo, dbItem != null ? dbItem : - poseTransformationMapper.selectList(new QueryWrapper().eq("unique_id", taskId)).get(0)); - - } - - return vo; - } - - private void processAllUrls(PoseTransformationVO vo, PoseTransformation dbItem) { - // 处理各种URL - processUrl(vo.getGifUrl(), url -> - vo.setGifUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - processUrl(vo.getVideoUrl(), url -> - vo.setVideoUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - processUrl(vo.getFirstFrameUrl(), url -> - vo.setFirstFrameUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - } - - private void processParentId(PoseTransformationVO vo, PoseTransformation poseTransformation) { - if (poseTransformation != null) { - ToProductImageResult productResult = getProductResultByPath(poseTransformation); - if (productResult != null) { - Long parentId = collectionSortService.getParentIdByElementIdAndElementType( - productResult.getId(), CollectionType.TO_PRODUCT_IMAGE.getValue()); - if (Objects.isNull(parentId)) { - parentId = userLikeGroupService.getUnlikedResultParentId(null, poseTransformation.getProductImage()); - } - vo.setParentId(parentId); - vo.setId(poseTransformation.getId()); - vo.setModelName(poseTransformation.getModelName()); - vo.setRelationType(Module.poseTransfer.getValue()); - vo.setProductImage(minioUtil.getPreSignedUrl(productResult.getUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - } - } - - private boolean isInvalidStatus(String status) { - return "Invalid".equals(status) || "Fail".equals(status); - } - - 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())) { - poseTransformation.setTaskStatus(status); - poseTransformation.setUpdateTime(LocalDateTime.now()); - poseTransformationMapper.updateById(poseTransformation); - } - } - - private ToProductImageResult getProductResultByPath(PoseTransformation poseTransformation) { - QueryWrapper qw = new QueryWrapper<>(); - //增加projectID匹配,避免因为二创导致查询到多条记录 - qw.lambda().eq(ToProductImageResult::getProjectId, poseTransformation.getProjectId()); - qw.lambda().eq(ToProductImageResult::getUrl, poseTransformation.getUniqueId()); - return toProductImageResultMapper.selectOne(qw); - } - - /*public List getPoseTransformationResultList(Long projectId, boolean like) { - List poseTransformations = poseTransformationMapper.selectList(new QueryWrapper().eq("project_id", projectId) - .eq("is_liked", like ? 1 : 0).ne("task_status", "Fail")); - List vos = new ArrayList<>(); -// if (poseTransformations != null && poseTransformations.size() > 1){ - if (!CollectionUtils.isEmpty(poseTransformations)) { - for (PoseTransformation item : poseTransformations) { - String taskId = item.getUniqueId(); - String key = generateResultKey + ":" + taskId; - String resultJson = redisUtil.getFromString(key); - - PoseTransformationVO poseTransformationVO; - - if (!StringUtil.isNullOrEmpty(resultJson)) { - // 从Redis获取并转换数据 - poseTransformationVO = new Gson().fromJson(resultJson, PoseTransformationVO.class); - poseTransformationVO.setId(item.getId()); - poseTransformationVO.setIsLiked(item.getIsLiked()); - - // 处理成功状态的数据 - if ("Success".equals(poseTransformationVO.getStatus())) { - poseTransformationVO.setProductImage( - minioUtil.getPreSignedUrl(item.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - - // 处理各种URL - processUrl(poseTransformationVO.getGifUrl(), url -> - poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - processUrl(poseTransformationVO.getVideoUrl(), url -> - poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - processUrl(poseTransformationVO.getFirstFrameUrl(), url -> - poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - } - - // 添加有效数据到结果列表 - if (!"Invalid".equals(poseTransformationVO.getStatus()) && !"Fail".equals(poseTransformationVO.getStatus())) { - vos.add(poseTransformationVO); - } - } else { - // 处理Redis中没有缓存的情况 - poseTransformationVO = CopyUtil.copyObject(item, PoseTransformationVO.class); - - poseTransformationVO.setTaskId(taskId); - poseTransformationVO.setProductImage( - minioUtil.getPreSignedUrl(item.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - - // todo 面对没有生成结束的情况,返回taskId - if (StringUtil.isNullOrEmpty(item.getVideoUrl())) { - vos.add(poseTransformationVO); - continue; - } - // 处理各种URL - processUrl(poseTransformationVO.getGifUrl(), url -> - poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - processUrl(poseTransformationVO.getVideoUrl(), url -> - poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - processUrl(poseTransformationVO.getFirstFrameUrl(), url -> - poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); - - vos.add(poseTransformationVO); - } - } - } - return vos; - }*/ - - // 辅助方法:处理URL - private void processUrl(String url, Consumer processor) { - if (!StringUtil.isNullOrEmpty(url) && !"None".equals(url)) { - processor.accept(url); - } - } - - public CollectionSort disOrLikePose(Long transformedId, String likeOrDislike, Long projectId, Long collectionSortParentId) { - PoseTransformation poseTransformation = poseTransformationMapper.selectById(transformedId); - CollectionSort collectionSort = null; - if (Objects.nonNull(poseTransformation)) { - if (likeOrDislike.equals("like")) { - poseTransformation.setIsLiked((byte) 1); - if (null != collectionSortParentId) { - collectionSort = collectionSortService.addCollectionSort(poseTransformation.getId(), CollectionType.POSE_TRANSFORM.getValue(), projectId, collectionSortParentId); - } - } else if (likeOrDislike.equals("dislike")) { - poseTransformation.setIsLiked((byte) 0); - if (null != collectionSortParentId) { - collectionSortService.deleteCollectionSort(poseTransformation.getId(), CollectionType.POSE_TRANSFORM.getValue(), projectId, collectionSortParentId); - } - } - poseTransformation.setUpdateTime(LocalDateTime.now()); - poseTransformationMapper.updateById(poseTransformation); - } - if (Objects.nonNull(collectionSort)) { - projectService.modifyProjectUpdateTime(projectId); - } - return collectionSort; - } - - public String modifyModelProportion(ModifyModelProportionDTO proportionDTO) { - log.info("modifyModelProportion params: {}", proportionDTO); - String name; - String gender; - Library model = null; - Long accountId = UserContext.getUserHolder().getId(); - String uuid = UUID.randomUUID().toString(); - // 所有修改的图片都另存为,不覆盖原图 - if (proportionDTO.getType().equals("Library")) { - model = libraryService.getById(proportionDTO.getId()); - if (Objects.isNull(model)) { - throw new BusinessException("model.not.found"); - } - String url = model.getUrl(); - name = url.substring(url.indexOf("/") + 1, url.lastIndexOf("/")) + "/" + uuid; -// gender = model.getLevel2Type(); - } else { - SysFileVO sysModel = sysFileService.getById(proportionDTO.getId()); - if (Objects.isNull(sysModel)) { - throw new BusinessException("model.not.found"); - } - gender = sysModel.getLevel2Type(); - name = accountId + "/models/" + gender.toLowerCase() + "/" + uuid; - } - // 只需要将结果存入library - String modifiedModel = pythonService.modifyModelProportion(proportionDTO.getModelPath(), proportionDTO.getStretch(), name, proportionDTO.getTop(), proportionDTO.getBottom()); -// List imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(modifiedModel); - - /*// 存储修改后的模特到个人library - model = new Library(); - model.setAccountId(accountId); - model.setLevel1Type(LibraryLevel1TypeEnum.MODELS.getRealName()); - model.setLevel2Type(gender); - model.setName(uuid); - model.setUrl(modifiedModel); - model.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(modifiedModel, 24 * 60),false)); - model.setWidth(imagesWidthAndHeight.get(0)); - model.setHigh(imagesWidthAndHeight.get(1)); - model.setCreateDate(new Date()); - libraryService.save(model);*/ - -/* // 新建模特点位信息 - LibraryModelPoint libraryModelPoint = new LibraryModelPoint(); - libraryModelPoint.setModelType("Library"); - libraryModelPoint.setRelationId(model.getId()); - libraryModelPoint.setShoulderLeft(Arrays.toString(proportionDTO.getShoulderLeft())); - libraryModelPoint.setShoulderRight(Arrays.toString(proportionDTO.getShoulderRight())); - libraryModelPoint.setWaistbandLeft(Arrays.toString(proportionDTO.getWaistbandLeft())); - libraryModelPoint.setWaistbandRight(Arrays.toString(proportionDTO.getWaistbandRight())); - libraryModelPoint.setHandLeft(Arrays.toString(proportionDTO.getHandLeft())); - libraryModelPoint.setHandRight(Arrays.toString(proportionDTO.getHandRight())); - libraryModelPoint.setCreateDate(new Date()); - libraryModelPointService.save(libraryModelPoint);*/ - return minioUtil.getPreSignedUrl(modifiedModel, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); - } - - /** - * String collagePicture(Base64) - * List elements - * File file - * - * @return - */ - @Transactional(rollbackFor = Exception.class) - public GenerateResultVO sketchReconstructionGenerate(SketchReconstructionDTO sketchReconstructionDTO) { -// log.info("sketchReconstructionGenerate params: {}", sketchReconstructionDTO); - - Long accountId = UserContext.getUserHolder().getId(); - // 1、线稿生成 - String collagePictureBase64 = sketchReconstructionDTO.getCollagePicture(); - String path = accountId + "/CollagePicture/" + UUID.randomUUID(); - String minioPath = minioUtil.base64UploadToPath(collagePictureBase64, userBucket, path); - - Long projectId = sketchReconstructionDTO.getProjectId(); - - GenerateResultVO generateResultVO = imageToSketch(new ImageToSketchDTO(null, "2", sketchReconstructionDTO.getGender()), minioPath, projectId); - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("project_id", projectId); - SketchReconstruction sketchReconstruction = sketchReconstructionMapper.selectOne(qw); - - String url = generateResultVO.getUrl(); - // 找到路径的起始位置(从"://"之后查找第一个"/") - int pathStartIndex = url.indexOf("/", url.indexOf("://") + 3); - // 找到查询参数的起始位置("?" 的位置) - int queryStartIndex = url.indexOf("?"); - // 截取目标部分 - String targetPath = url.substring(pathStartIndex + 1, queryStartIndex); - - try { - if (Objects.isNull(sketchReconstruction)) { - sketchReconstruction = new SketchReconstruction(); - sketchReconstruction.setProjectId(projectId); - sketchReconstruction.setCollageImgSketchUrl(targetPath); - sketchReconstruction.setGenerateDetailId(generateResultVO.getId()); - sketchReconstruction.setGender(sketchReconstructionDTO.getGender()); - sketchReconstruction.setCreateTime(LocalDateTime.now()); - sketchReconstructionMapper.insert(sketchReconstruction); - } else { - sketchReconstruction.setCollageImgSketchUrl(targetPath); - sketchReconstruction.setGenerateDetailId(generateResultVO.getId()); - sketchReconstructionMapper.updateById(sketchReconstruction); - } - } catch (DuplicateKeyException e) { - // 如果发生唯一键冲突,说明其他请求已经创建了记录 - // 重新查询并更新 - log.info("sketch拼贴,唯一键(project_id)冲突,改为更新"); - sketchReconstruction = sketchReconstructionMapper.selectOne(qw); - sketchReconstruction.setCollageImgSketchUrl(targetPath); - sketchReconstruction.setGenerateDetailId(generateResultVO.getId()); - sketchReconstructionMapper.updateById(sketchReconstruction); - } - - return generateResultVO; - } - - public SketchReconstructionVO getSketchReconstruction(Long projectId) { - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("project_id", projectId); - SketchReconstruction sketchReconstruction = sketchReconstructionMapper.selectOne(qw); - - SketchReconstructionVO vo = new SketchReconstructionVO(); - if (Objects.nonNull(sketchReconstruction) && Objects.nonNull(sketchReconstruction.getGenerateDetailId())) { - GenerateDetail generateDetail = generateDetailMapper.selectById(sketchReconstruction.getGenerateDetailId()); - vo.setCollageSketchUrl(minioUtil.getPreSignedUrl(generateDetail.getUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - vo.setLiked(generateDetail.getIsLike().equals((byte) 1)); - String clothCategory = pythonService.getClothCategory(generateDetail.getUrl(), sketchReconstruction.getGender()); - String messageFromResource = BusinessException.getMessageFromResource(clothCategory.toUpperCase()); - vo.setCategory(clothCategory); - vo.setCategoryValue(messageFromResource); - } - List collectionElements = collectionElementService.getByProjectId(projectId); - 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 { - vo.setUploadImages(new ArrayList<>()); - } - return vo; - } - - public List> getAllPose() { - List> propertyList = PoseEnum.getPropertyList(); - propertyList.forEach(item -> { -// item.put("gif", minioUtil.getPreSignedUrl(item.get("gif"), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - item.put("video", minioUtil.getPreSignedUrl(item.get("video"), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - item.put("firstFrame", minioUtil.getPreSignedUrl(item.get("firstFrame"), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - }); - return propertyList; - } - - @Override - @Transactional - public void processPoseTransformResultBatch(String taskId, String gifUrl, String videoUrl, String imageUrl, String progress) { - // 1、存储模型返回的数据 - PoseTransformation poseTransformation; - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("unique_id", taskId); - List poseTransformations = poseTransformationMapper.selectList(qw); - if (poseTransformations != null && poseTransformations.size() > 1) { - log.warn("通过taskId {} 查询到的PoseTransformation的结果不止一条", taskId); - } else if (poseTransformations == null || poseTransformations.isEmpty()) { - return; - } - poseTransformation = poseTransformations.get(0); - poseTransformation.setGifUrl(gifUrl); - poseTransformation.setVideoUrl(videoUrl); - poseTransformation.setFirstFrameUrl(imageUrl); - poseTransformation.setTaskStatus("Success"); - poseTransformation.setUpdateTime(LocalDateTime.now()); - poseTransformationMapper.updateById(poseTransformation); - - String taskIdBatch = poseTransformation.getTaskIdBatch(); - QueryWrapper cloudTaskQueryWrapper = new QueryWrapper<>(); - cloudTaskQueryWrapper.lambda().eq(CloudTask::getTaskId, taskIdBatch); - CloudTask cloudTask = cloudTaskMapper.selectOne(cloudTaskQueryWrapper); - if (Objects.nonNull(cloudTask)) { - cloudTask.setUpdateTime(LocalDateTime.now()); - cloudTaskMapper.updateById(cloudTask); - } -// if (Objects.nonNull(cloudTask)) { -// if (cloudTask.getCompletedNum() == null) { -// cloudTask.setCompletedNum(1); -// }else { -// cloudTask.setCompletedNum(cloudTask.getCompletedNum() + 1); -// } -// cloudTaskMapper.updateById(cloudTask); -// } - cloudTaskMapper.increaseCompletedNum(taskIdBatch); - - String key = generateResultKey + ":" + taskId; - PoseTransformationVO poseTransformationVO = new PoseTransformationVO( - poseTransformation.getId(), taskId, gifUrl, videoUrl, imageUrl, (byte) 0, "Success"); - - // 2、更新redis - redisUtil.addToString(key, new Gson().toJson(poseTransformationVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - /*// 3、执行积分扣除 - String accountId = taskId.substring(taskId.lastIndexOf("-") + 1); - String uuid = taskId.substring(0, taskId.lastIndexOf("-")); - Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), taskIdBatch); - if (flag) creditsService.updateChangedCredits(accountId, taskIdBatch);*/ - } - - @Transactional - public void processPoseTransformResultBatch(String progress, String taskId) { - // 1、存储模型返回的数据 - PoseTransformation poseTransformation; - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("unique_id", taskId); - List poseTransformations = poseTransformationMapper.selectList(qw); - log.info("poseTransformations : {}", poseTransformations); - if (poseTransformations != null && poseTransformations.size() > 1) { - log.warn("通过taskId {} 查询到的PoseTransformation的结果不止一条", taskId); - } else if (poseTransformations == null || poseTransformations.isEmpty()) { - return; - } - poseTransformation = poseTransformations.get(0); - String taskIdBatch = poseTransformation.getTaskIdBatch(); - log.info("progress:{}", progress); - log.info("taskIdBatch:{}", taskIdBatch); - if (progress.equals("OK")) { - if (!StringUtils.isEmpty(taskIdBatch)) { - cloudTaskService.completeTask(taskIdBatch); - } - } else if (progress.startsWith("0/")) { - if (!StringUtils.isEmpty(taskIdBatch)) { - cloudTaskService.startTask(taskIdBatch); - } - } - } - - - @Transactional(rollbackFor = Exception.class) - public void deleteGeneratedPose(Long projectId, Long id) { - // 1. 权限校验 - Long accountId = UserContext.getUserHolder().getId(); - Project project = projectService.getById(projectId); - if (!project.getAccountId().equals(accountId)) { - throw new IllegalArgumentException("项目不属于当前账号"); - } - - // 2. 软删除主表数据 - int update = poseTransformationMapper.update(null, - new UpdateWrapper() - .eq("id", id) - .set("is_deleted", 1) - .set("update_time", LocalDateTime.now())); - - log.info("删除PoseTransfer 结果, id为{}, 影响行数={}", id, update); - - if (update == 0) return; - - // 3. 查询可能删除的多个排序项(存在脏数据可能,所有会查出多个) - List deletedItems = collectionSortMapper.selectList( - new QueryWrapper() - .eq("project_id", projectId) - .eq("relation_id", id) - .eq("relation_type", "PoseTransfer") - .orderByAsc("sort") - ); - - if (deletedItems.isEmpty()) return; - - // 4. 删除这些排序记录 - int deletedCount = collectionSortMapper.delete( - new QueryWrapper() - .eq("project_id", projectId) - .eq("relation_id", id) - .eq("relation_type", "PoseTransfer") - ); - - // 5. 重新调整剩余记录的排序(两种方案可选) - // 方案一:精确调整(每条被删除记录单独处理) - for (CollectionSort deletedItem : deletedItems) { - collectionSortMapper.update( - null, - new UpdateWrapper() - .eq("project_id", projectId) - .gt("sort", deletedItem.getSort()) - .setSql("sort = sort - 1") - ); - } - - log.info("删除PoseTransfer排序记录:id={}, 删除{}条,已重新排序后续记录", id, deletedCount); - } - - - /** - * 万象专业版 - * 1、MoodBoard t2i - * 2、PrintBoard t2i - * 3、SketchBoard t2i - * 4、pose transfer 图生舞蹈视频-舞动人像AnimateAnyone - */ - - /** - * 创建异步任务 - * - * @return taskId - */ - public String createAsyncTask(GenerateThroughImageTextDTO generateDTO) { -// String prompt = "一间有着精致窗户的花店,漂亮的木质门,摆放着花朵"; - String level1Type = generateDTO.getLevel1Type(); - String level2Type = generateDTO.getLevel2Type(); - String prompt = generateDTO.getText(); - Long userId = generateDTO.getUserId(); - String gender = generateDTO.getGender(); - - // 添加预设prompt,使生成结果更加具有指向性(区分不同的board) - switch (level1Type) { - case "Moodboard": - break; - case "Printboard": - prompt = "pattern image, " + prompt; - break; - case "Sketchboard": - prompt = "a single item of sketch of " + prompt + ", clean white background, simple lines"; - break; - default: - log.warn("未知类型 type:{}", level1Type); - } - HashMap promptExtend = new HashMap<>(); - promptExtend.put("prompt_extend", false); - ImageSynthesisParam param = - ImageSynthesisParam.builder() - .apiKey(ALIYUN_API_KEY) - .model("wanx2.1-t2i-plus") - .prompt(prompt) - .n(1) -// .size("1024*1024") 采用默认值;1328*1328(默认值):1:1。 - .parameters(promptExtend) - .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 + ")", "wx", new Date()); - save(generate); - return taskId; - } - - /** - * 获取异步任务结果 - * - * @param taskId 任务id - */ - public GenerateResultVO getAsyncTaskResult(String taskId) { - ImageSynthesis imageSynthesis = new ImageSynthesis(); - ImageSynthesisResult result = null; - try { - //如果已经在环境变量中设置了 DASHSCOPE_API_KEY,wait()方法可将apiKey设置为null - result = imageSynthesis.fetch(taskId, ALIYUN_API_KEY); - log.info(JsonUtils.toJson(result)); - //PENDING:任务排队中; RUNNING:任务处理中; SUCCEEDED:任务执行成功; FAILED:任务执行失败; CANCELED:任务取消成功; UNKNOWN:任务不存在或状态未知 - String taskStatus = result.getOutput().getTaskStatus(); - - if (taskStatus.equals("SUCCEEDED")) { - List generates = selectListByUniqueId(taskId); - String url = result.getOutput().getResults().get(0).get("url"); - - 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"); - } - } else if (taskStatus.equals("PENDING") || taskStatus.equals("RUNNING")) { - log.info("阿里 异步接口返回生成状态为:{}", taskStatus); - return new GenerateResultVO(taskId, null, null, "Executing"); - } else { - log.warn("阿里 异步接口返回生成状态为:{}", taskStatus); - return new GenerateResultVO(taskId, null, null, "Fail"); - } - } catch (ApiException | NoApiKeyException e) { - throw new RuntimeException(e.getMessage()); - } catch (Exception e) { - log.error("从aliyun下载图片失败, {}", e.getMessage()); - throw new BusinessException("Generation result retrieval failed"); - } - } - - 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 if ("Fail".equals(cachedResult.getStatus())) { - // 获取失败原因 - String errorMessage = cachedResult.getUrl(); - if (errorMessage != null) { - throw new BusinessException(errorMessage, ResultEnum.PROMPT.getCode()); - } - } 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"); - } - } else if ("Fail".equals(cachedResult.getStatus())) { - // 获取失败原因 - String errorMessage = cachedResult.getUrl(); - if (errorMessage != null) { - throw new BusinessException(errorMessage, ResultEnum.PROMPT.getCode()); - } - } - - // 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/"; - private static final String ANIMATE = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis/"; - - public String animateAnyone(PoseTransformDTO poseTransformDTO, Long accountId) { - AccessLimitUtils.validate("animation", 5); - String inputImage = poseTransformDTO.getProductImage(); - String inputImageUrl = minioUtil.getPreSignedUrl(inputImage, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); - // 1、输入图片检测 - checkImage(inputImageUrl); - - // 2、动作模板生成 - String videoTemplateId = PoseEnum.getById(poseTransformDTO.getPoseId()).getTemplateId(); - if (StringUtil.isNullOrEmpty(videoTemplateId)) { - throw new BusinessException("unknown pose"); - } - // 3、生成动图 - JSONObject requestBody1 = new JSONObject(); - requestBody1.set("model", "animate-anyone-gen2"); - - JSONObject input1 = new JSONObject(); - input1.set("image_url", inputImageUrl); // 替换为实际图片URL - input1.set("template_id", videoTemplateId); // 替换为实际图片URL - JSONObject parameters1 = new JSONObject(); - parameters1.set("use_ref_img_bg", false); - parameters1.set("video_ratio", "9:16"); - - requestBody1.set("input", input1); - requestBody1.set("parameters", parameters1); - - log.info("万象 pose transfer 请求入参:{}", requestBody1); - String resp = sendRequestUtil.sendAliYunPostAsync(ANIMATE, requestBody1.toString()); -// String resp = "{\"request_id\":\"656c4339-59e5-9b34-a010-b5aa625a4008\",\"output\":{\"task_id\":\"05c0fe3e-8d93-4754-babe-28a1efc62151\",\"task_status\":\"PENDING\"}}"; - log.info("wx pose transform 请求生成,获取taskId:{}", resp); - JSONObject jsonResponse = JSONUtil.parseObj(resp); - JSONObject output = jsonResponse.getJSONObject("output"); - String status = output.getStr("task_status"); - if (status.equals(FAILED.getName()) || status.equals(UNKNOWN_W.getName())) { - return null; - } - return output.getStr("task_id"); - } - - public void checkImage(String inputImageUrl) { - JSONObject requestBody = new JSONObject(); - requestBody.set("model", "animate-anyone-detect-gen2"); - - JSONObject input = new JSONObject(); - input.set("image_url", inputImageUrl); // 替换为实际图片URL - JSONObject parameters = new JSONObject(); - - requestBody.set("input", input); - requestBody.set("parameters", parameters); - - String response = sendRequestUtil.sendAliYunPost(IMAGE_DETECT, requestBody.toString()); - - System.out.println("API响应: " + response); - JSONObject jsonResponse = JSONUtil.parseObj(response); - // 获取check_pass值 - JSONObject output = jsonResponse.getJSONObject("output"); - Boolean checkPass = output.getBool("check_pass"); - - if (!checkPass) { - String reason = output.getStr("reason"); - log.info("原因: {}", reason); - throw new BusinessException("输入的图片不满足要求"); - } - } - - // 轮询配置 - private static final int MAX_RETRIES = 30; // 最大重试次数 - private static final int POLL_INTERVAL = 20000; // 轮询间隔(毫秒) - - public String getVideoTemplateId(String videoPath) { - boolean contains = PoseEnum.getVideoList().contains(videoPath); - - String templateId; - if (!contains) { - String videoUrl = minioUtil.getPreSignedUrl(videoPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); - JSONObject requestBody = new JSONObject(); - requestBody.set("model", "animate-anyone-template-gen2"); - JSONObject input = new JSONObject(); - input.set("video_url", videoUrl); // 替换为实际图片URL - JSONObject parameters = new JSONObject(); - requestBody.set("input", input); - requestBody.set("parameters", parameters); - - log.info("获取pose的模板id 请求数据:{}", requestBody); - String resp = sendRequestUtil.sendAliYunPostAsync(TEMPLATE_ID_GEN, requestBody.toString()); - if (StringUtil.isNullOrEmpty(resp)) { - throw new BusinessException("请求获取video template id失败"); - } - JSONObject jsonResponse = JSONUtil.parseObj(resp); - log.info("getVideoTemplateId response:{}", jsonResponse); - JSONObject output = jsonResponse.getJSONObject("output"); - String taskId = output.getStr("task_id"); - - // 暂时用while循环轮询 - templateId = pollTemplateIdResult(taskId); - if (StringUtil.isNullOrEmpty(templateId)) { - throw new BusinessException("获取动作模板失败"); - } -// templateId = "AACT.8090e67b.-E3pujumEfCbDTI_rjSH-A.LwIlGT3j"; - } else { - templateId = PoseEnum.getByVideoPath(videoPath).getTemplateId(); - } - - return templateId; - } - - public String pollTemplateIdResult(String taskId) { - int attempt = 0; - boolean isCompleted = false; - String templateId = null; - - while (attempt < MAX_RETRIES && !isCompleted) { - attempt++; - System.out.printf("尝试第 %d 次查询...%n", attempt); - - try { - // 发送GET请求查询任务状态 - HttpResponse httpResponse = HttpRequest.get(GET_ASYNC_RESULT + taskId) - .header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY) - .timeout(10000) - .execute(); - - if (httpResponse.getStatus() == 200) { - JSONObject response = JSONUtil.parseObj(httpResponse.body()); - JSONObject output = JSONUtil.parseObj(response.getStr("output")); - String taskStatus = output.getStr("task_status", "UNKNOWN"); - WangXiangTaskStatusEnum statusEnum = WangXiangTaskStatusEnum.fromName(taskStatus); - System.out.println("当前任务状态: " + taskStatus); - - switch (statusEnum) { - case SUCCEEDED: - templateId = handleSuccessResponse(response); - isCompleted = true; - break; - case FAILED: - case UNKNOWN_W: - handleFailedResponse(response); - isCompleted = true; - break; - case RUNNING: - case PENDING_W: - // 任务仍在运行,继续等待 - break; - default: - System.out.println("未知状态: " + taskStatus); - } - } else { - System.out.println("请求失败,状态码: " + httpResponse.getStatus()); - } - - // 如果不是最终状态,等待一段时间再重试 - if (!isCompleted && attempt < MAX_RETRIES) { - TimeUnit.MILLISECONDS.sleep(POLL_INTERVAL); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - System.out.println("轮询被中断"); - break; - } catch (Exception e) { - System.out.println("请求发生异常: " + e.getMessage()); - // 发生异常时可以选择重试或退出 - break; - } - } - if (!isCompleted) { - System.out.println("达到最大重试次数仍未获取最终结果"); - } - return templateId; - } - - private static String handleSuccessResponse(JSONObject response) { - log.info("任务执行成功!"); - // 提取成功结果 - JSONObject output = response.getJSONObject("output"); - if (output != null) { - log.info("任务输出: {}", output.toStringPretty()); - return output.getStr("template_id"); - } else { - return null; - } - } - - private static void handleFailedResponse(JSONObject response) { - log.info("任务执行失败!"); - // 提取失败原因 - String errorMsg = response.getStr("error_message", "未知错误"); - log.info("失败原因: {}", errorMsg); - } - - - public PoseTransformationVO getAnimateResult(String taskId) { - String fullUrl = GET_ASYNC_RESULT + taskId; - // 从接口获取当前任务的结果 - String respBody = sendRequestUtil.sendAliYunGet(fullUrl); - log.info("获取wx pose transform 的结果: {}", respBody); - - String outputStr = JSONUtil.parseObj(respBody).getStr("output"); - JSONObject output = JSONUtil.parseObj(outputStr); - String videoUrl = output.getStr("video_url"); - String status = output.getStr("task_status"); - - // 更新api_generate表 - apiGenerateService.updateAPIGenerateStatusAsync(taskId, status); - - List poseTransformations = poseTransformationMapper.selectList(new QueryWrapper().eq("unique_id", taskId).orderByDesc("id")); - PoseTransformation poseTransformation; - if (!poseTransformations.isEmpty()) { - poseTransformation = poseTransformations.get(0); - } else { - throw new BusinessException("unknown motion task"); - } - Long accountId = poseTransformation.getAccountId(); - PoseTransformationVO poseTransformationVO = new PoseTransformationVO(); - WangXiangTaskStatusEnum statusEnum = WangXiangTaskStatusEnum.fromName(status); - switch (statusEnum) { - case SUCCEEDED: - AccessLimitUtils.validateOut("animation"); - poseTransformationVO = CopyUtil.copyObject(poseTransformation, PoseTransformationVO.class); - poseTransformationVO.setStatus("Success"); - - // 生成视频的gif和第一帧图片并上传图片、视频、gif等数据,更新pose transformation表 - processVideo(videoUrl, poseTransformation); - poseTransformationVO.setId(poseTransformation.getId()); - if (!StringUtil.isNullOrEmpty(poseTransformation.getGifUrl())) { - poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(poseTransformation.getGifUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - if (!StringUtil.isNullOrEmpty(poseTransformation.getVideoUrl())) { - poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(poseTransformation.getVideoUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - if (!StringUtil.isNullOrEmpty(poseTransformation.getFirstFrameUrl())) { - poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(poseTransformation.getFirstFrameUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - } - // 执行积分扣除 - Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId); - if (flag) creditsService.updateChangedCredits(String.valueOf(accountId), taskId); - - // 保存数据到redis - String key = generateResultKey + ":" + taskId; - redisUtil.addToString(key, new Gson().toJson(poseTransformationVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - break; - case FAILED: - AccessLimitUtils.validateOut("animation"); - updatePoseTransferStatus(taskId, "Fail", poseTransformation); - // 将积分暂扣区的积分移除 - creditsService.deleteCreditsDeduction(accountId, taskId); - throw new BusinessException(output.getStr("message"), ResultEnum.PROMPT.getCode()); - case UNKNOWN_W: - AccessLimitUtils.validateOut("animation"); - poseTransformationVO.setStatus("Fail"); - updatePoseTransferStatus(taskId, "Fail", poseTransformation); - // 将积分暂扣区的积分移除 - creditsService.deleteCreditsDeduction(accountId, taskId); - break; - case RUNNING: - case PENDING_W: - // 任务仍在运行,继续等待 - poseTransformationVO.setStatus("Executing"); - break; - default: - AccessLimitUtils.validateOut("animation"); - log.info("未知状态: {}", status); - poseTransformationVO.setStatus("Fail"); - updatePoseTransferStatus(taskId, "Fail", poseTransformation); - // 将积分暂扣区的积分移除 - creditsService.deleteCreditsDeduction(accountId, taskId); - } - poseTransformationVO.setTaskId(taskId); - - return poseTransformationVO; - } - - public void processVideo(String aliyunVideoUrl, PoseTransformation poseTransformation) /*throws Exception*/ { - // 1. 从阿里云下载视频到内存 - byte[] videoBytes = downloadVideoOrImage(aliyunVideoUrl); - Long accountId = poseTransformation.getAccountId(); - String taskId = poseTransformation.getUniqueId(); - - // 2. 提取第一帧和生成GIF - ByteArrayOutputStream firstFrameOutput = new ByteArrayOutputStream(); - ByteArrayOutputStream gifOutput = new ByteArrayOutputStream(); - - try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(videoBytes))) { - grabber.start(); - // 提取第一帧 - BufferedImage firstFrame = new Java2DFrameConverter().convert(grabber.grabImage()); - ImageIO.write(firstFrame, "jpg", firstFrameOutput); - - // 生成GIF(取前12秒,50帧) - generateGif(grabber, gifOutput, 12, 60); - } catch (Exception e) { - throw new RuntimeException(e); - } - - // 3. 上传所有文件到MinIO - String videoPrefix = accountId + "/pose_transform_video/" + taskId + ".mp4"; - String imgPrefix = accountId + "/pose_transform_first_img/" + taskId + ".jpg"; - String gifPrefix = accountId + "/pose_transform_gif/" + taskId + ".gif"; - - minioUtil.uploadToMinio(videoBytes, userBucket, videoPrefix, "video/mp4"); - minioUtil.uploadToMinio(firstFrameOutput.toByteArray(), userBucket, imgPrefix, "image/jpeg"); - minioUtil.uploadToMinio(gifOutput.toByteArray(), userBucket, gifPrefix, "image/gif"); - // 存储数据到数据库 - poseTransformation.setGifUrl(userBucket + "/" + gifPrefix); - poseTransformation.setVideoUrl(userBucket + "/" + videoPrefix); - poseTransformation.setFirstFrameUrl(userBucket + "/" + imgPrefix); - poseTransformation.setTaskStatus("Success"); - poseTransformation.setUpdateTime(LocalDateTime.now()); - poseTransformationMapper.updateById(poseTransformation); - } - - public byte[] downloadVideoOrImage(String url) { - try (CloseableHttpClient client = HttpClients.createDefault(); - InputStream in = client.execute(new HttpGet(url)).getEntity().getContent()) { - return IOUtils.toByteArray(in); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - // 增强版下载方法 todo 最好不要报错 - private byte[] downloadVideoOrImageWithValidation(String url) throws IOException { - CloseableHttpClient client = HttpClients.createDefault(); - HttpGet request = new HttpGet(url); - - try (CloseableHttpResponse response = client.execute(request)) { - // 状态码检查 - if (response.getStatusLine().getStatusCode() != 200) { - throw new IOException("Invalid status: " + response.getStatusLine()); - } - - // 内容类型检查 - org.apache.http.Header contentTypeHeader = response.getFirstHeader("Content-Type"); - if (contentTypeHeader == null || !contentTypeHeader.getValue().startsWith("image/")) { - throw new IOException("Invalid content type: " + - (contentTypeHeader != null ? contentTypeHeader.getValue() : "null")); - } - - // 内容长度检查 - org.apache.http.Header contentLengthHeader = response.getFirstHeader("Content-Length"); - if (contentLengthHeader != null) { - long length = Long.parseLong(contentLengthHeader.getValue()); - if (length <= 0) { - throw new IOException("Empty content"); - } - } - - return IOUtils.toByteArray(response.getEntity().getContent()); - } - } - - public byte[] downloadWithProxy(String url) throws IOException { - // 获取系统代理设置(适用于大多数VPN) -// String proxyHost = System.getProperty("http.proxyHost"); -// String proxyPort = System.getProperty("http.proxyPort"); - String proxyHost = "localhost"; - String proxyPort = "7890"; - - CloseableHttpClient client; - if (proxyHost != null && proxyPort != null) { - // 配置代理 - HttpHost proxy = new HttpHost(proxyHost, Integer.parseInt(proxyPort)); - RequestConfig config = RequestConfig.custom().setProxy(proxy).build(); - client = HttpClients.custom().setDefaultRequestConfig(config).build(); - } else { - client = HttpClients.createDefault(); - } - - try { - return client.execute(new HttpGet(url), response -> { - if (response.getStatusLine().getStatusCode() == 200) { - return IOUtils.toByteArray(response.getEntity().getContent()); - } else { - throw new IOException("HTTP Error: " + response.getStatusLine()); - } - }); - } finally { - client.close(); - } - } - - public static void generateGif(FFmpegFrameGrabber grabber, OutputStream output, - int durationSec, int frameCount) throws Exception { - Java2DFrameConverter converter = new Java2DFrameConverter(); - AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder(); - - // 配置GIF参数 - gifEncoder.start(output); - gifEncoder.setDelay(100); // 每帧延迟(毫秒) - gifEncoder.setRepeat(0); // 0=无限循环 - - int totalFrames = (int) (grabber.getFrameRate() * durationSec); - int step = Math.max(1, totalFrames / frameCount); - - // 逐帧处理 - for (int i = 0; i < totalFrames; i += step) { - grabber.setVideoFrameNumber(i); - BufferedImage frame = converter.convert(grabber.grabImage()); - if (frame != null) { - gifEncoder.addFrame(frame); - } - } - gifEncoder.finish(); - } - - /** - * Freepik - * To Product Image - */ - public String reimagineFreePik(String path, String prompt, String style) throws IOException { - String imageAsBase64 = minioUtil.getImageAsBase64(path); - log.info(minioUtil.getPreSignedUrl(path, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - JSONObject requestBody = new JSONObject(); - requestBody.set("image", imageAsBase64); - requestBody.set("prompt", prompt); - requestBody.set("imagination", style); - - String resp = sendRequestUtil.sendFreepikPost(requestBody.toString()); - if (!StringUtil.isNullOrEmpty(resp)) { - JSONObject jsonResp = JSONUtil.parseObj(resp); - JSONObject data = JSONUtil.parseObj(jsonResp.get("data")); - String status = data.getStr("status"); - if (status.equals("COMPLETED")) { -// List generated = data.getBeanList("generated", String.class); - log.info("freepik 调用结果:{}", jsonResp); - return jsonResp.getStr("data"); - } - } - return null; - } - - /** - * imagePath 图片的minio地址 - * ollama - * prompt 助手 - */ - public String getImageDescription(String imagePath) { -/* // 1. 读取图片并编码为 Base64 - String imageAsBase64 = null; - try { - imageAsBase64 = minioUtil.getImageAsBase64(imagePath); - } catch (IOException e) { - throw new RuntimeException(e); - } - - // 2. 构建 JSON 请求体 - JSONObject message = new JSONObject(); - message.set("role", "user"); - message.set("content", "Please describe the clothing in the image and provide a line art description of the outfit. The description should allow for the reconstruction of the corresponding line art based on the details given."); - message.set("images", JSONUtil.createArray().set(imageAsBase64)); - - JSONObject requestBody = new JSONObject(); - requestBody.set("model", "llama3.2-vision"); - requestBody.set("messages", JSONUtil.createArray().set(message)); - requestBody.set("stream", false);*/ - -// log.info("request body:{}", requestBody); - JSONObject requestBody = new JSONObject(); - requestBody.set("img", imagePath); -// String description = sendRequestUtil.sendPost("http://localhost:8000/api/img2prompt", requestBody.toString()); - String description = sendRequestUtil.sendPost("http://18.167.251.121:9994/api/img2prompt", requestBody.toString()); - if (StringUtil.isNullOrEmpty(description)) { - throw new BusinessException("从ollama获取图片描述失败"); - } - /*Object msg = JSONUtil.parseObj(resp).get("message"); - String description = JSONUtil.parseObj(msg).getStr("content");*/ - log.info("image :{} \n, description: {}", - minioUtil.getPreSignedUrl(imagePath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), description); - return description; - } - - private String resolveModelType(String taskId, String func) { - // 判断当前task来自哪个模型 - if (!StringUtil.isNullOrEmpty(func) - && func.equals(CreditsEventsEnum.POSE_TRANSFORMATION.getValue())) { - List poseTransformations = poseTransformationMapper.selectList( - new QueryWrapper().eq("unique_id", taskId)); - if (!poseTransformations.isEmpty() - && !StringUtil.isNullOrEmpty(poseTransformations.get(0).getModelName()) - && poseTransformations.get(0).getModelName().equals("wx")) { - return "wx"; - } else { - return "local"; - } - } - - Generate generate = selectByUniqueId(taskId); - if (Objects.nonNull(generate) && - !StringUtil.isNullOrEmpty(generate.getModelName()) && - (generate.getModelName().equals("wx") - || generate.getModelName().equals("freepik") - || 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"; - } - } - - public static String extractGender(String text) { - // 匹配末尾的 (Male) 或 (Female),忽略大小写 - Pattern pattern = Pattern.compile("\\(([Mm]ale|[Ff]emale)\\)$"); - Matcher matcher = pattern.matcher(text); - - if (matcher.find()) { - return matcher.group(1); // 返回括号内的内容 - } - return null; // 未匹配到性别 - } - - /** - * 接入flux模型,用于imageToSketch(sketch extract) || relighting || to product image - * - * @param func 功能枚举名,指定使用flux模型的具体功能类型 - * @param prompt 用户输入的提示词,如果为空则使用默认提示词 - * @param imagePath 图片minio路径,作为输入图像的base64编码源 - * @param childStyle 是否为儿童风格,影响提示词的构建 - * @return 返回taskId,用于异步获取结果 - */ - public String flux(CreditsEventsEnum func, String prompt, String imagePath, boolean childStyle) { - // Flux API的请求地址 - String fluxRequestUrl = "https://api.bfl.ai/v1/flux-kontext-pro"; - - // 如果用户没有提供提示词,根据功能类型设置默认提示词 - if (StringUtil.isNullOrEmpty(prompt)) { - switch (func) { - case RELIGHT_FLUX: - // 重新打光功能的默认提示词 - prompt = "a model standing on the beautiful beach, ultra high quality, 8k"; - break; - case IMAGE_TO_SKETCH_FLUX: - // 图片转线稿功能的默认提示词 - prompt = "generate the sketch of the image, simple line, ultra high quality"; - break; - case TO_PRODUCT_IMAGE_ADVANCED: - // 转产品图功能的默认提示词 - prompt = "change the image to real style, ultra high quality, 8k"; - // 如果是儿童风格,添加儿童面部特征描述 - if (childStyle) prompt = prompt + ", Children's face"; - break; - } - } else { - // 如果用户提供了提示词,先进行提示词优化处理 - prompt = modifyPrompt(prompt, null, func.getName(), null); - // 根据不同功能类型,为提示词添加特定前缀 - switch (func) { - case PATTERN: - // 图案生成功能,添加图案前缀 - prompt = "pattern image, " + prompt; - break; - case SKETCH_BOARD: - // 线稿板功能,添加线稿描述 - prompt = "a single item of sketch of " + prompt + ", clean white background, simple lines"; - break; - } - } - - // 构建Flux API请求体 - JSONObject requestBody = new JSONObject(); - - // 设置生成提示词 - requestBody.set("prompt", prompt); - // 设置随机种子,确保结果的可重现性 - requestBody.set("seed", 42); - // 根据功能类型设置图片宽高比 - if (func.equals(PATTERN)) { - // 图案生成使用正方形比例 - requestBody.set("aspect_ratio", "1:1"); - } else { - // 其他功能使用竖屏比例 - requestBody.set("aspect_ratio", "9:16"); - } - // 设置输出格式为PNG - requestBody.set("output_format", "png"); - - log.info("flux 请求入参:{}", requestBody); - // 提示词不能为空的校验 - if (prompt.isEmpty()) throw new BusinessException("test"); - - // 如果提供了输入图片路径,需要将图片转换为base64格式 - if (!StringUtil.isNullOrEmpty(imagePath)) { - try { - String imageAsBase64 = null; - // 对于转产品图功能,先添加白色背景处理 - if (func.equals(TO_PRODUCT_IMAGE_ADVANCED)) { - imageAsBase64 = addWhiteBackground(imagePath); - } - // 如果白色背景处理失败或不需要,直接获取原图的base64编码 - if (StringUtil.isNullOrEmpty(imageAsBase64)) { - imageAsBase64 = minioUtil.getImageAsBase64(imagePath); - } - // 将base64编码的图片添加到请求体中 - 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."); - } - } - - // 发送POST请求到Flux API - String resp = sendRequestUtil.sendFluxPost(fluxRequestUrl, requestBody.toString()); - JSONObject respObj = JSONUtil.parseObj(resp); - log.info("flux 发起生成请求返回结果: {}", respObj); - - // 从响应中提取任务ID - String taskId = respObj.getStr("id"); - if (StringUtil.isNullOrEmpty(taskId)) { - // 任务创建失败,记录错误信息并抛出异常 - requestBody.set("input_image", imagePath); - log.error("flux生成任务创建失败,func :{}, requestBody:{}", func.getName(), requestBody); - throw new BusinessException("Failed to generate task. Please retry later."); - } - - // 获取轮询URL,用于后续查询任务状态 - String pollingUrl = respObj.getStr("polling_url"); - String key = RedisUtil.FLUX_POLLING_URL + taskId; - // 将轮询URL存储到Redis中,设置过期时间 - redisUtil.addToString(key, pollingUrl, CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - - // 添加到api_generate表中,以便之后对结果查询做补偿机制 - apiGenerateService.addAPIGenerateRecordAsync(UserContext.getUserHolder().getId(), taskId, func.getName(), "flux", "Pending"); - - // 返回任务ID,用于异步查询结果 - return taskId; - } - - @Override - public String getFluxResult(String taskId, String objectName) { - // 获取轮询URL - String pollingUrl = redisUtil.getFromString(RedisUtil.FLUX_POLLING_URL + taskId); - - // 准备请求参数 - String fluxResultRequestUrl = StringUtil.isNullOrEmpty(pollingUrl) - ? "https://api.bfl.ai/v1/get_result" - : pollingUrl; - - HashMap params = new HashMap<>(); - if (StringUtil.isNullOrEmpty(pollingUrl)) { - params.put("id", taskId); - } - - // 发送请求并解析响应 - String resp = sendRequestUtil.sendGet(fluxResultRequestUrl, params); - log.info("获取flux生成的结果为:{}", resp); - - JSONObject respObj = JSONUtil.parseObj(resp); - String status = respObj.getStr("status"); - FluxTaskStatusEnum statusEnum = FluxTaskStatusEnum.fromName(status); - // 异步更新状态 - apiGenerateService.updateAPIGenerateStatusAsync(taskId, status); - - // 处理不同状态 - switch (statusEnum) { - case TASK_NOT_FOUND: - // 审核没过 - case REQUEST_MODERATED: - // 审核没过 - case CONTENT_MODERATED: - // 出错 - case ERROR: - return "Fail"; - case PENDING_F: - return "Pending"; - case SUCCESS: - // 已完成 获取结果 - return handleReadyStatus(respObj, objectName); - default: - return null; - } - } - - private String handleReadyStatus(JSONObject respObj, String objectName) { - // 1. 首先检查MinIO中是否已存在该图片 - if (minioUtil.doesObjectExist(userBucket, objectName)) { - return userBucket + "/" + objectName; - } - - // 2. 解析响应获取结果URL和生成时间 - JSONObject resultObj = JSONUtil.parseObj(respObj.getStr("result")); - String fluxResult = resultObj.getStr("sample"); - double endTime = resultObj.getDouble("end_time"); // 获取任务结束时间戳 - - // 3. 检查图片链接是否已过期(超过10分钟) - long currentTime = System.currentTimeMillis() / 1000; // 当前Unix时间戳(秒) - long generateTime = (long) endTime; // 生成结束时间戳 - // 图片10分钟过期,保险起见,保留一分钟 - long tenMinutesInSeconds = 9 * 60; - - if (currentTime - generateTime > tenMinutesInSeconds) { - log.warn("Flux result image has expired, generateTime: {}, currentTime: {}", - generateTime, currentTime); - return null; - } - - // 4. 图片未过期,下载并上传到MinIO - try { - byte[] bytes = downloadVideoOrImage(fluxResult); - minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png"); - return userBucket + "/" + objectName; - } catch (Exception e) { - log.error("Failed to download or upload Flux result image", e); - return null; - } - } - - private GenerateResultVO getFluxResultAndSave(String taskId) { - Generate generate = selectByUniqueId(taskId); - if (Objects.nonNull(generate)) { - GenerateDetail generateDetail = generateDetailMapper.selectOne(new QueryWrapper().eq("generate_id", generate.getId())); - Long accountId = generate.getAccountId(); - String objectName = accountId + "/imageToSketch/" + taskId + ".png"; - String fluxResult = getFluxResult(taskId, objectName); - if (Objects.isNull(generateDetail)) { - if (StringUtil.isNullOrEmpty(fluxResult)) { - // 将积分暂扣区的积分移除 - creditsService.deleteCreditsDeduction(accountId, taskId); - return new GenerateResultVO(taskId, "Fail"); - } else if (fluxResult.equals("Fail") || fluxResult.equals("Pending")) { - String status = fluxResult.equals("Fail") ? "Fail" : "Executing"; - if (status.equals("Fail")) { - creditsService.deleteCreditsDeduction(accountId, taskId); - } - return new GenerateResultVO(taskId, status); - } - generateDetail = new GenerateDetail(generate.getId(), fluxResult, - MD5Utils.encryptFile( - minioUtil.getPreSignedUrl(fluxResult, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false), - LocalDateTime.now()); - generateDetailMapper.insert(generateDetail); - // 扣积分 - Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId); - if (flag) creditsService.updateChangedCredits(String.valueOf(accountId), taskId); - } else if (StringUtil.isNullOrEmpty(generateDetail.getUrl())) { - // 结果已经存入db,一般走不到这条线 - generateDetail.setGenerateId(generate.getId()); - generateDetail.setUrl(fluxResult); - generateDetail.setMd5(MD5Utils.encryptFile( - minioUtil.getPreSignedUrl(fluxResult, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false)); - generateDetail.setUpdateDate(new Date()); - generateDetailMapper.updateById(generateDetail); - } - String url = generateDetail.getUrl(); - String category; - if (generate.getLevel1Type().equals(SKETCH_BOARD.getRealName())) { - category = pythonService.getClothCategory(url, extractGender(generate.getGenerateType())); - } else { - category = generate.getLevel2Type(); - } - - return new GenerateResultVO(taskId, generateDetail.getId(), - minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), "Success", category); - } else { - throw new BusinessException("unknown generate"); - } - } - - private String addWhiteBackground(String minioPath) { - // 1、先通过后缀判断输入图片类型有没有透明通道 - String extension = minioPath.substring(minioPath.lastIndexOf(".") + 1); - - // 2、如果有,为其添加白色背景 - if (extension.equals("png")) { - return minioUtil.changeToWhiteBackground(minioPath); - } else { - log.info("图片 {} 没有透明通道, 不用添加白底", minioPath); - return null; - } - } - - public - com.alibaba.fastjson.JSONObject createParamsForMotion(PoseTransformDTO poseTransformDTO, String taskId) { - - com.alibaba.fastjson.JSONObject params = new com.alibaba.fastjson.JSONObject(); - params.put("tasks_id", taskId); - // 判断当前使用的哪种生成模式 - MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode()); - switch (motionModeEnum){ - case POSE_TO_VIDEO: - params.put("pose_id", poseTransformDTO.getPoseId()); - params.put("image_url", poseTransformDTO.getProductImage()); - break; - case PROMPT_TO_VIDEO: - params.put("image_url", poseTransformDTO.getProductImage()); - params.put("prompt", poseTransformDTO.getPrompt()); - break; - case FIRST_LAST_FRAME_TO_VIDEO: - params.put("start_image_url", poseTransformDTO.getProductImage()); - params.put("end_image_url", poseTransformDTO.getLastFrameProductImage()); - params.put("prompt", poseTransformDTO.getPrompt()); - break; - default: - throw new BusinessException("unknown.mode"); - } - params.put("api", motionModeEnum.getUrl()); - return params; - } - - /** - * 根据豆包错误码和详细信息返回用户友好的错误信息 - * - * @param errorCode 错误码 - * @param detailMessage 详细错误信息 - * @return 用户友好的错误信息 - */ - private String getDoubaoErrorMessage(String errorCode, String detailMessage, String userLanguage) { - boolean isEnglish = "ENGLISH".equals(userLanguage); - - if (errorCode == null) { - return isEnglish ? "Image generation failed, please try again later" : "图像生成失败,请稍后重试"; - } - - switch (errorCode) { - // === 用户输入问题(用户可以自行解决) === - - // 敏感内容检测 - 用户需要修改提示词 - case "SensitiveContentDetected": - case "SensitiveContentDetected.SevereViolation": - case "SensitiveContentDetected.Violence": - case "InputTextSensitiveContentDetected": - return isEnglish ? "Your prompt contains sensitive content. Please modify your description and try again." - : "您的提示词包含敏感内容,请修改描述后重试"; - case "InputImageSensitiveContentDetected": - return isEnglish ? "Your uploaded image contains sensitive content. Please use a different image and try again." - : "您上传的图片包含敏感内容,请更换图片后重试"; - case "InputVideoSensitiveContentDetected": - return isEnglish ? "Your uploaded video contains sensitive content. Please use a different video and try again." - : "您上传的视频包含敏感内容,请更换视频后重试"; - case "OutputTextSensitiveContentDetected": - case "OutputImageSensitiveContentDetected": - case "OutputVideoSensitiveContentDetected": - return isEnglish ? "The generated content may contain sensitive information. Please modify your prompt and try again." - : "生成的内容可能包含敏感信息,请修改提示词后重试"; - - // 图片格式问题 - 用户需要检查图片 - case "InvalidImageURL.EmptyURL": - return isEnglish ? "No image was uploaded. Please upload an image and try again." - : "未上传图片,请上传图片后重试"; - case "InvalidImageURL.InvalidFormat": - return isEnglish ? "The image format is not supported or the image is corrupted. Please upload a valid image (JPG, PNG, etc.) and try again." - : "图片格式不支持或图片已损坏,请上传有效的图片格式(JPG、PNG等)后重试"; - - // 内容长度问题 - 用户需要缩短内容 - case "OutofContextError": - return isEnglish ? "Your prompt and image content is too long. Please shorten your description or use a smaller image and try again." - : "您的提示词和图片内容过长,请缩短描述或使用更小的图片后重试"; - - // 请求频率问题 - 用户需要等待 - case "TooManyRequests": - return isEnglish ? "You are sending requests too frequently. Please wait a moment and try again." - : "您的请求过于频繁,请稍等片刻后重试"; - - // === 系统问题(需要联系管理员) === - - // 参数格式错误 - 系统问题 - case "MissingParameter": - case "InvalidParameter": - case "Duplicate.Tags.Key": - case "InvalidArgumentError": - case "InvalidArgumentError.UnknownRole": - case "InvalidArgumentError.InvalidImageDetail": - case "InvalidArgumentError.InvalidPixelLimit": - return isEnglish ? "System error: Invalid request parameters. Please contact the administrator." - : "系统错误:请求参数格式有误,请联系管理员"; - - // 认证问题 - 系统问题 - case "AuthenticationError": - return isEnglish ? "System error: Authentication failed. Please contact the administrator." - : "系统错误:身份验证失败,请联系管理员"; - - // 服务配置问题 - 系统问题 - case "InvalidEndpoint.ClosedEndpoint": - case "OperationDenied.InvalidState": - case "OperationDenied.ConflictedValidationSet": - case "NotFound.Model": - case "NotFound.Endpoint": - case "NotFound.Context": - case "InvalidOperation.Conflict": - case "ModelNotOpen": - return isEnglish ? "System error: Service configuration issue. Please contact the administrator." - : "系统错误:服务配置问题,请联系管理员"; - - // 配额/费用问题 - 系统问题 - case "QuotaExceeded.DailyQuota": - case "QuotaExceeded.MonthlyQuota": - case "QuotaExceeded.TotalQuota": - case "QuotaExceeded.RPM": - case "QuotaExceeded.TPM": - case "QuotaExceeded.RPD": - case "QuotaExceeded.TPD": - return isEnglish ? "System error: Service quota exceeded. Please contact the administrator." - : "系统错误:服务配额已用完,请联系管理员"; - - // 服务器错误 - 系统问题 - case "InternalError": - case "ServiceUnavailable": - return isEnglish ? "System error: Service is temporarily unavailable. Please contact the administrator or try again later." - : "系统错误:服务暂时不可用,请联系管理员或稍后重试"; - - default: - // 根据详细信息判断是用户问题还是系统问题 - if (detailMessage != null) { - if (detailMessage.contains("sensitive") || detailMessage.contains("敏感")) { - return isEnglish ? "Your content contains sensitive information. Please modify and try again." - : "您的内容包含敏感信息,请修改后重试"; - } else if (detailMessage.contains("quota") || detailMessage.contains("配额") || - detailMessage.contains("parameter") || detailMessage.contains("参数") || - detailMessage.contains("auth") || detailMessage.contains("认证")) { - return isEnglish ? "System error occurred. Please contact the administrator." - : "系统错误,请联系管理员"; - } - } - return isEnglish ? "Image generation failed. Please try again later or contact the administrator if the problem persists." - : "图像生成失败,请稍后重试,如问题持续请联系管理员"; - } - } -} +package com.ai.da.service.impl; + +import cn.hutool.core.img.gif.AnimatedGifEncoder; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +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; +import com.ai.da.common.utils.*; +import com.ai.da.mapper.primary.*; +import com.ai.da.mapper.primary.entity.*; +import com.ai.da.model.dto.*; +import com.ai.da.model.enums.CollectionType; +import com.ai.da.model.enums.Module; +import com.ai.da.model.enums.SketchStyle; +import com.ai.da.model.vo.*; +import com.ai.da.python.PythonService; +import com.ai.da.service.*; +import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; +import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam; +import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; +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.LambdaQueryWrapper; +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.RequiredArgsConstructor; +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; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.bytedeco.javacv.FFmpegFrameGrabber; +import org.bytedeco.javacv.Java2DFrameConverter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +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.PATTERN; +import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE; +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; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GenerateServiceImpl extends ServiceImpl implements GenerateService { + + private final AccountService accountService; + private final APIGenerateService apiGenerateService; + private final CollectionElementMapper collectionElementMapper; + private final CollectionElementService collectionElementService; + private final CreditsService creditsService; + private final CollectionSortMapper collectionSortMapper; + private final CollectionSortService collectionSortService; + private final CloudTaskMapper cloudTaskMapper; + private final CloudTaskService cloudTaskService; + private final GenerateCancelMapper generateCancelMapper; + private final GenerateDetailMapper generateDetailMapper; + private final LibraryService libraryService; + // private final LibraryModelPointService libraryModelPointService; + private final MinioUtil minioUtil; + private final MessageCenterService messageCenterService; + private final PythonService pythonService; + private final ProjectService projectService; + private final PoseTransformationMapper poseTransformationMapper; + private final RabbitMQService rabbitMQService; + private final RedisUtil redisUtil; + private final SketchReconstructionMapper sketchReconstructionMapper; + private final SendRequestUtil sendRequestUtil; + private final SysFileService sysFileService; + private final ToProductImageResultMapper toProductImageResultMapper; + private final ToProductImageResultService toProductImageResultService; + @Value("${redis.key.orderForGenerate}") + private String consumptionOrderKey; + + @Value("${redis.key.generateCancelSet}") + private String cancelSetKey; + + @Value("${redis.key.generateExceptionMap}") + private String exceptionMapKey; + + @Value("${redis.key.generateResult}") + private String generateResultKey; + + @Value("${minio.bucketName.slogan}") + private String sloganBucket; + + @Value("${minio.bucketName.users}") + private String userBucket; + + @Value("${redis.key.relightResultKey}") + private String relightResultKey; + + @Value("${redis.key.toProductImageResultKey}") + private String toProductImageResultKey; + + @Value("${access.python.generate_sr_port}") + private String generateServicePort; + + @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; + + + // 创建 Random 对象 + Random random = new Random(); + + @Override + public GenerateCaptionVO generateCaption(Long sketchElementId) { + CollectionElement collectionElement = collectionElementMapper.selectById(sketchElementId); + if (Objects.isNull(collectionElement)) { + throw new BusinessException("the.image.does.not.exist.please.reselect"); + } + String url = collectionElement.getUrl(); +// String caption = pythonService.generateSketchCaption(url); + GenerateCaptionVO recognized_caption = new GenerateCaptionVO("recognized caption"); + + return recognized_caption; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void generateThroughImageText(GenerateThroughImageTextDTO generateThroughImageTextDTO) { + // 1、获取用户信息 + Long accountId = generateThroughImageTextDTO.getUserId(); + + GenerateModeEnum modeEnum = getMode(generateThroughImageTextDTO); + String generateType = modeEnum.getValue(); + // 2、判断必须入参是否为非空(在prepare阶段已校验) + Generate generate = new Generate(); + generate.setAccountId(accountId); + generate.setUniqueId(generateThroughImageTextDTO.getUniqueId()); + generate.setLevel1Type(generateThroughImageTextDTO.getLevel1Type()); + generate.setLevel2Type(generateThroughImageTextDTO.getLevel2Type()); + generate.setSeed(StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getSeed()) ? "" : generateThroughImageTextDTO.getSeed()); + // 当level1type是sketchboard时,存数据库需要加上当前性别 + generate.setGenerateType(generate.getLevel1Type().equals(SKETCH_BOARD.getRealName()) ? + generateType + " (" + generateThroughImageTextDTO.getGender() + ")" : + generateType); + generate.setModelName(StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getModelName()) ? ModelNameEnum.MODEL_0.getCode() : generateThroughImageTextDTO.getModelName()); + generate.setCreateDate(DateUtil.getByTimeZone(generateThroughImageTextDTO.getTimeZone())); + generate.setElementSource(StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getDesignType()) ? null : generateThroughImageTextDTO.getDesignType()); + + String text = generateThroughImageTextDTO.getText(); + generate.setText(text); + Long elementId = generateThroughImageTextDTO.getCollectionElementId(); +// validateGeneraType(generate, text, elementId); + if (!StringUtil.isNullOrEmpty(text)) { + text = modifyPrompt(text, generate, generateThroughImageTextDTO.getLevel1Type(), generateThroughImageTextDTO.getAgeGroup()); + } + + // todo 这一步现在还是有必要的吗? + // 2.1 sketch或print在t_collection_element表/t_library表中的信息是否需要更新 如 level2Type + CollectionElement collectionElement = collectionElementService.editLevel2Type(elementId, generateThroughImageTextDTO.getLevel2Type(), generateThroughImageTextDTO.getDesignType()); + + // 3、向模型发起请求 + String mode = modeEnum.getType(); + String category = generateThroughImageTextDTO.getLevel1Type().equals(SKETCH_BOARD.getRealName()) ? "sketch" : + generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) ? "print" : "moodboard"; + String path = CommonConstant.GENERATE_PATH; + String port = generateServicePort; + String jsonString = ""; + HashMap params = new HashMap<>(); + String version = null; + if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getModelName()) && generateThroughImageTextDTO.getModelName().equals("high")) { + version = "high"; + params.put("version", "high"); + } else if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getModelName()) && generateThroughImageTextDTO.getModelName().equals("normal")) { + version = "fast"; + params.put("version", "fast"); + } + // 3.1 确定不同类型的印花分别调哪个接口 + if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) { + switch (generateThroughImageTextDTO.getLevel2Type()) { + case "Logo": + path = CommonConstant.GENERATE_SINGLE_LOGO; + params.put("tasks_id", generateThroughImageTextDTO.getUniqueId()); + params.put("prompt", text); + params.put("seed", generateThroughImageTextDTO.getSeed()); + jsonString = JSON.toJSONString(params, SerializerFeature.WriteMapNullValue); + break; + case "Slogan": + path = CommonConstant.GENERATE_SLOGAN; + port = CommonConstant.PYTHON_PORT_9997; + params.put("num_point", "16"); + params.put("tasks_id", generateThroughImageTextDTO.getUniqueId()); + params.put("prompt", text); + params.put("image_url", collectionElement.getUrl()); + jsonString = JSON.toJSONString(params, SerializerFeature.WriteMapNullValue); + break; + case "Pattern": + GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), + mode, category, generateThroughImageTextDTO.getGender(), version); + jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); + } + } else { + GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), + mode, category, generateThroughImageTextDTO.getGender(), version); + jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); + } + + Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path); + + // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 + save(generate); + + // 5、将本次请求存入redis + String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId(); + String status; + if (requestResult) { + status = "Executing"; + } else { + status = "Fail"; + } + GenerateResultVO generateResultVO = new GenerateResultVO(generateThroughImageTextDTO.getUniqueId(), null, null, status); + redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + } + + public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) { + if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) { + if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { + return GenerateModeEnum.TEXT_IMAGE; + } else { + return GenerateModeEnum.TEXT; + } + } else { + if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { + return GenerateModeEnum.IMAGE; + } + } + return GenerateModeEnum.TEXT; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void processGenerateResult(String taskId, String url, String category) { + // 1、处理模型返回的数据 + GenerateDetail generateDetail = new GenerateDetail(); + GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); + Generate generate; + try { + generate = selectByUniqueId(taskId); + } catch (MybatisPlusException e) { + log.error(e.getMessage()); + if (e.getMessage().equals("One record is expected, but the query result is multiple records")) { + generate = selectListByUniqueId(taskId).get(0); + } else { + log.error("There are some problems with database query, please try again."); + return; + } + } +// Generate generate = selectByUniqueId(taskId); + String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, 24 * 60), Boolean.FALSE); + // 通过MD5值和level1Type,判断不同level1Type下相同的图片是否被like过 + List> libraryIdList = generateDetailMapper.getLibraryIdThroughMD5(md5, generate.getLevel1Type()); + if (!libraryIdList.isEmpty()) { + generateDetail.setIsLike((byte) 1); + generateDetail.setLibraryId(libraryIdList.get(0).get("library_id")); + generateCollectionItemVO.setIsLiked(Boolean.TRUE); + } + generateDetail.setUrl(url); + generateDetail.setGenerateId(generate.getId()); + generateDetail.setCreateDate(LocalDateTime.now()); + generateDetail.setMd5(md5); + // 将相应的url保存到数据库 + generateDetailMapper.insert(generateDetail); + +// String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-")); + String key = generateResultKey + ":" + taskId; + String imageName = url.substring(url.lastIndexOf("/") + 1); + String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; + if (StringUtil.isNullOrEmpty(category)) { + Generate generateRecord = selectByUniqueId(taskId); + category = generateRecord.getLevel2Type(); + } + GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category); + // 更新redis + redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 执行积分扣除 + // ** 注:如果生成的图片都是空白 则不扣积分 + if (!status.equals("Invalid")) { + String uuid = taskId.substring(0, taskId.lastIndexOf("-", taskId.lastIndexOf("-") - 1)); + String accountId = taskId.substring(taskId.lastIndexOf("-") + 1); + Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), uuid); + if (flag) creditsService.updateChangedCredits(accountId, uuid); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void processToProductImageResult(String taskId, String url, String category) { + QueryWrapper qw = new QueryWrapper<>(); + qw.lambda().eq(ToProductImageResult::getTaskId, taskId); + List toProductImageResults = toProductImageResultMapper.selectList(qw); + if (CollectionUtils.isEmpty(toProductImageResults)) { + return; +// throw new BusinessException(""); + } + ToProductImageResult toProductImageResult = toProductImageResults.get(0); + toProductImageResult.setUrl(url); + toProductImageResult.setStatus("Success"); +// toProductImageResult.setResultType("ToProductImage"); + toProductImageResultMapper.updateById(toProductImageResult); + + String key = toProductImageResultKey + ":" + taskId; + String imageName = url.substring(url.lastIndexOf("/") + 1); + String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; + GenerateResultVO generateResultVO = new GenerateResultVO(taskId, toProductImageResult.getId(), url, status, category); + redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + Long accountId = Long.parseLong(taskId.substring(taskId.lastIndexOf("-") + 1)); + if (!status.equals("Invalid")) { + // 4、扣除积分 + Boolean b = creditsService.taskCreditsDeduction(accountId, taskId); + // 3、记录积分变更 + if (b) creditsService.insertToCreditsDetail(accountId, + TO_PRODUCT_IMAGE.getName(), + TO_PRODUCT_IMAGE.getValue(), + "negative", null); + } + } + + 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)) { + toProductImageResult.setStatus(status); + toProductImageResultMapper.updateById(toProductImageResult); + } + } + } + + private void validateGeneraType(Generate generate, String text, Long elementId) { + String generateType = ""; + if (StringUtil.isNullOrEmpty(text.trim()) && Objects.isNull(elementId)) { + throw new BusinessException("please.input.the.caption.or.choose.an.image"); + } else if (!StringUtil.isNullOrEmpty(text.trim()) && !Objects.isNull(elementId)) { + generateType = "text-image"; + generate.setText(text); + generate.setElementId(elementId); + } else if (!StringUtil.isNullOrEmpty(text.trim())) { + generateType = "text"; + generate.setText(text); + } else if (!Objects.isNull(elementId)) { + generateType = "image"; + generate.setElementId(elementId); + } + generate.setGenerateType(generateType); + + } + + private String modifyPrompt(String userInput, Generate generate, String level1Type, String ageGroup) { + String text = ""; + String prefix = ""; + if (userInput.startsWith("Painting Style") + || userInput.startsWith("Illustration Style") + || userInput.startsWith("Real Style")) { + prefix = userInput.substring(0, userInput.indexOf(",")) + ", "; + userInput = userInput.substring(userInput.indexOf(",") + 1); + } + // 替换用户输入中的中文字符 + log.info("用户输入,处理前:{}", userInput); + userInput = ComprehensivePunctuationConverter.convertToHalfWidth(userInput); + log.info("用户输入,处理后:{}", userInput); + String translated = prefix + pythonService.promptTranslate(userInput); + switch (level1Type) { + case "Moodboard": + text = translated + ",high quality"; +// generate.setText(text); + break; + case "Printboard": + case "Pattern": + text = translated; + /*if (prefix.contains("Painting Style")) { + text = "Picasso,increased color saturation,increased glossiness," + translated + ", fabric print, high quality"; + } else if (prefix.contains("Illustration Style")) { + text = "Flat coating,romantic,soft,pencil strokes,accentuating and widening the depth of pencil strokes,paper patterns,block colors,crayons,reducing image contrast,and hand drawn painting marks," + translated + ", fabric print, high quality"; + } else if (prefix.contains("Real Style")) { + text = "Hyper realism,3d,deepened projection,increased permutation value,increased concavity and convexity value," + translated + ", fabric print, high quality"; + } else { + text = translated; + }*/ +// text = userInput + ", fabric print, high quality"; +// generate.setText(text); + break; + case "Sketchboard": +// text = "clear lines, simple outlines monochrome white vector image of " + translated + ", no background, sketch flat, front view display, best quality, ultra-high resolution 8k"; + text = "a single item of sketch of " + translated + ", 4k, white background"; + if (Objects.nonNull(generate) && generate.getModelName().equals("high")) { + text = text + ", only technical pattern of a single garment"; + } + if (!StringUtil.isNullOrEmpty(ageGroup) && ageGroup.equals("Child")) { + text = text + ", Children's clothing"; + } + break; +// generate.setText(text); + default: + text = translated; + } + return text; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public GenerateLikeVO generateLike(GenerateLikeDTO generateLikeDTO) { + // 1、判断参数是否正确 + // 1.1 必须参数是否非空 + if (SKETCH_BOARD.getRealName().equals(generateLikeDTO.getLevel1Type())) { + if (StringUtil.isNullOrEmpty(generateLikeDTO.getLevel2Type())) { + throw new BusinessException("level2Type.cannot.be.empty"); + } + if (StringUtil.isNullOrEmpty(generateLikeDTO.getGender())) { + throw new BusinessException("gender.cannot.be.empty"); + } + } + if (PRINT_BOARD.getRealName().equals(generateLikeDTO.getLevel1Type())) { + if (StringUtil.isNullOrEmpty(generateLikeDTO.getLevel2Type())) { + throw new BusinessException("level2Type.cannot.be.empty"); + } + } + // 1.2 判断参数是否真实有效 + Long generateDetailId = generateLikeDTO.getGenerateDetailId(); + GenerateDetail generateDetail = generateDetailMapper.selectById(generateDetailId); + if (Objects.isNull(generateDetail)) { + throw new BusinessException("generateItem.does.not.exist"); + } + Generate generate = getById(generateDetail.getGenerateId()); + if (!generateLikeDTO.getLevel1Type().equals(generate.getLevel1Type())) { + throw new BusinessException("level1Type.does.not.match"); + } + + // 2、将like的图片信息存入library + // 2.1、不能重复喜欢 + // 2.1.1 判断该图片是否被喜欢过 + Library libraryDetail = libraryService.getById(generateDetail.getLibraryId()); + if ((Objects.nonNull(generateDetail.getLibraryId()) && !generateDetail.getLibraryId().equals(0L)) + || Objects.nonNull(libraryDetail)) { + throw new BusinessException("duplicate.likes.are.not.allowed"); + } + + // todo 2.1.2、判断library中是否有相同MD5的图片 + + // 2.2、添加到library + AuthPrincipalVo userInfo = UserContext.getUserHolder(); + Long accountId = userInfo.getId(); + Library library = setLibrary(accountId, generateLikeDTO, generateDetail.getUrl(), generateLikeDTO.getAgeGroup()); + libraryService.save(library); + + // 3、更新generateDetail表的isLike列和libraryId列 + updateLikeStatus(generateLikeDTO.getGenerateDetailId(), (byte) 1, library.getId(), generateLikeDTO.getTimeZone()); + + return new GenerateLikeVO(library.getId()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean generateDislike(Long generateDetailId, String timeZone) { + // 1、确定generateDetail中是否有这条记录 + GenerateDetail generateDetail = generateDetailMapper.selectById(generateDetailId); + if (Objects.isNull(generateDetail)) { + throw new BusinessException("generateItem.does.not.exist"); + } + + // 2、修改generateDetail表中的isLike、libraryId字段 + updateLikeStatus(generateDetailId, (byte) 0, 0L, timeZone); + + // 3、确定library中是否添加该条记录 + Library libraryDetail = libraryService.getById(generateDetail.getLibraryId()); + if (Objects.isNull(libraryDetail)) { + return Boolean.TRUE; + } + + // 4、删除library相关记录 + libraryService.removeById(libraryDetail.getId()); + + // 5、返回成功 + return Boolean.TRUE; + } + + public Library setLibrary(Long accountId, GenerateLikeDTO generateLikeDTO, String imageUrl, String ageGroup) { + Library library = new Library(); + library.setAccountId(accountId); + library.setLevel1Type(generateLikeDTO.getLevel1Type()); + library.setLevel2Type(StringUtil.isNullOrEmpty(generateLikeDTO.getLevel2Type()) ? null : generateLikeDTO.getLevel2Type()); + library.setLevel3Type(StringUtil.isNullOrEmpty(generateLikeDTO.getGender()) ? null : generateLikeDTO.getGender()); + library.setAgeGroup(ageGroup); + library.setName(DateUtil.getTimeStamp(generateLikeDTO.getTimeZone()) + "_N_G"); + library.setUrl(imageUrl); + try { + library.setMd5(MD5Utils.encryptFile(minioUtil.download(imageUrl))); + } catch (MinioException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + library.setCreateDate(DateUtil.getByTimeZone(generateLikeDTO.getTimeZone())); + return library; + } + + public void updateLikeStatus(Long generateDetailId, Byte hasLike, Long libraryId, String timeZone) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", generateDetailId); + + GenerateDetail generateDetail = new GenerateDetail(); + generateDetail.setIsLike(hasLike); + generateDetail.setLibraryId(libraryId); + generateDetail.setUpdateDate(DateUtil.getByTimeZone(timeZone)); + generateDetailMapper.update(generateDetail, queryWrapper); + } + + // 避免循环注入,已移到libraryService中 + public void updateLikeStatusBatch(List generateDetailIdList, Byte hasLike, Long libraryId, String timeZone) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("id", generateDetailIdList); + + GenerateDetail generateDetail = new GenerateDetail(); + generateDetail.setIsLike(hasLike); + generateDetail.setLibraryId(libraryId); + generateDetail.setUpdateDate(DateUtil.getByTimeZone(timeZone)); + + generateDetailMapper.update(generateDetail, queryWrapper); + } + + // 避免循环注入,已移到libraryService中 + public List selectBatchByLibraryId(List libraryId) { + QueryWrapper qw = new QueryWrapper<>(); + qw.in("library_id", libraryId); + + return generateDetailMapper.selectList(qw); + } + + @Override + public PrepareForGenerateVO prepareForGenerate(GenerateThroughImageTextDTO generateDTO) { + // 1、参数检查,判断必须参数是否为空 + validateRequiredParams(generateDTO); + + // 2、处理特殊模型情况(wx/flux) + if (isWxModel(generateDTO)) { + return handleWxModelGeneration(generateDTO); + } + if (isFluxPatternModel(generateDTO)) { + return handleFluxPatternGeneration(generateDTO); + } + + String modelName = generateDTO.getModelName(); + if (StringUtil.isNullOrEmpty(modelName)) { + return handleStandardGeneration(generateDTO); + } + + 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.SCETCH_TEXT2IMG); + // 创建生成任务 + String taskId = null; + try { + taskId = createGoogleAsyncTask(generateDTO, useModel, prompt); + } catch (Exception e) { + e.printStackTrace(); + } + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.SCETCH_TEXT2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + private PrepareForGenerateVO handleNanoBananaModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + // 校验积分是否足够 + validateCredits(CreditsEventsEnum.SCETCH_IMG2IMG); + // 创建生成任务 + String taskId = null; + try { + taskId = createGoogleAsyncTask(generateDTO, useModel, prompt); + } catch (Exception e) { + e.printStackTrace(); + } + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.SCETCH_IMG2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + public String toProductAsyncTask(String imagePath, String useModel, String prompt) { + + long startTime = System.currentTimeMillis(); + log.info("开始执行toProductAsyncTask - model: {}, imagePath: {}, prompt: {}", + useModel, imagePath, prompt); + + if (StringUtil.isNullOrEmpty(imagePath) || StringUtil.isNullOrEmpty(useModel) || StringUtil.isNullOrEmpty(prompt)) { + log.error("参数验证失败 - imagePath: {}, useModel: {}, prompt: {}", imagePath, useModel, prompt); + throw new BusinessException("Parameter Exception"); + } + AuthPrincipalVo userHolder = UserContext.getUserHolder(); + Long userId = userHolder.getId(); + + String uuid = UUID.randomUUID().toString(); + // 生成唯一的任务ID + String taskId = uuid + "-" + userId; + String finalImagePath = null; + try { + finalImagePath = addWhiteBackground(imagePath); + // 如果白色背景处理成功,对base64图片进行压缩以降低模型成本 + if (!StringUtil.isNullOrEmpty(finalImagePath)) { + // 压缩到512x512分辨率 + finalImagePath = minioUtil.compressBase64Image(finalImagePath, 512, 512); + //去掉"data:image/png;base64," + finalImagePath = finalImagePath.replace("data:image/png;base64,", ""); + } else { + // 如果白色背景处理失败或不需要,直接获取原图的压缩base64编码 + finalImagePath = minioUtil.getCompressedImageAsBase64(imagePath, 512, 512); + } + } catch (IOException e) { + log.error("Error getting image as base64 taskId: {} ", taskId, e); + throw new BusinessException("Parameter Exception"); + } + + // 初始化Redis中的任务状态为"Executing" + String key = generateResultKey + ":" + taskId; + // 异步处理token获取和API调用 + String projectId = "aida-461108"; + String location = "global"; + String endpoint = String.format( + "https://aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:generateContent", + projectId, location, ModelConstants.NANO_BANANA + ); + + JSONObject requestBody = new JSONObject(); + // 使用 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("IMAGE")); +// generationConfig.set("topP", 0.95); + JSONObject imageConfig = new JSONObject(); + imageConfig.set("aspectRatio", "9:16"); + generationConfig.set("imageConfig", imageConfig); + requestBody.set("generationConfig", generationConfig); + String jsonBody = requestBody.toString(); +// log.info("Google 请求入参:{}", jsonBody); + + GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Pending"); + redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + 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请求 - 优化网络配置以解决连接中断问题 + ConnectionPool connectionPool = new ConnectionPool(10, 5, TimeUnit.MINUTES); + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(45, TimeUnit.SECONDS) // 增加连接超时时间 + .readTimeout(120, TimeUnit.SECONDS) // 增加读取超时时间 + .writeTimeout(120, TimeUnit.SECONDS) // 增加写入超时时间 + .callTimeout(180, TimeUnit.SECONDS) // 添加总调用超时时间 + .connectionPool(connectionPool) // 使用连接池 + .retryOnConnectionFailure(true) // 启用连接失败重试 + .build(); + Request request = new Request.Builder() + .url(endpoint) + // 移除Connection: close头,使用Keep-Alive连接 + .addHeader("Authorization", "Bearer " + tokenValue) + .addHeader("Content-Type", "application/json") + .addHeader("User-Agent", "AIDA-Client/1.0") + .addHeader("Accept", "application/json") + .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) + .build(); + + // 实现重试逻辑来处理网络连接问题 + int maxRetries = 3; + int retryDelay = 2000; // 2秒 + boolean success = false; + + for (int attempt = 1; attempt <= maxRetries && !success; attempt++) { + try { + log.info("发起HTTP请求 - 尝试次数: {}/{}, URL: {}, taskId: {}", attempt, maxRetries, request.url(), taskId); + long requestStartTime = System.currentTimeMillis(); + + try (Response response = client.newCall(request).execute()) { + long requestEndTime = System.currentTimeMillis(); + log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}", + response.code(), (requestEndTime - requestStartTime), taskId); + if (!response.isSuccessful()) { + log.warn("Google API响应失败,状态码: {} for taskId: {}", response.code(), taskId); + if (attempt < maxRetries) { + Thread.sleep(retryDelay * attempt); // 递增延迟 + continue; + } else { + throw new IOException("HTTP error code: " + response.code()); + } + } + + 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); + String finishReason = candidate.getString("finishReason"); + if (finishReason != null && finishReason.equals("STOP")) { + success = true; + com.alibaba.fastjson.JSONObject contentResult = candidate.getJSONObject("content"); + JSONArray parts = contentResult.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 inlineDataResult = part.getJSONObject("inlineData"); + base64Data = inlineDataResult.getString("data"); + break; + } + } + } else if (finishReason != null && finishReason.equals("IMAGE_SAFETY")) { + String finishMessage = candidate.getString("finishMessage"); + if (finishMessage != null && finishMessage.contains("Try rephrasing the prompt")) { + finishMessage = "Try rephrasing the prompt or modifying the model image. If you think this was an error, send feedback."; + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { + finishMessage = "请尝试重新表述提示词,或修改模特图。若您认为这是误判,可提交反馈。"; + } + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, finishMessage, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + return; + } + } + + } + } + + if (base64Data != null && !base64Data.isEmpty()) { + String resultPath = userId + "/product_image" + "/" + uuid; + String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); + if (StringUtil.isNullOrEmpty(minioPath)) { + log.warn("Google API调用成功,但图片保存失败 for taskId: {}", taskId); + } + // 生成成功,更新Redis状态和URL + GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); + redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + success = true; + log.info("Google API调用成功 for taskId: {}", taskId); + } 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); + success = true; // 这是业务逻辑失败,不需要重试 + } + } + } catch (IOException e) { + log.warn("网络连接问题 - 异常类型: {}, 尝试: {}/{}, taskId: {}, 错误详情: {}", + e.getClass().getSimpleName(), attempt, maxRetries, taskId, e.getMessage()); + if (attempt < maxRetries) { + int delayMs = retryDelay * attempt; + log.info("准备重试 - 延迟: {}ms, 下次尝试: {}/{}, taskId: {}", + delayMs, (attempt + 1), maxRetries, taskId); + try { + Thread.sleep(delayMs); // 递增延迟重试 + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("重试被中断 - taskId: {}", taskId, ie); + throw new RuntimeException("重试被中断", ie); + } + } else { + log.error("Google API调用最终失败 - 已达到最大重试次数: {}, taskId: {}", maxRetries, taskId, e); + throw e; // 最后一次尝试失败,抛出异常 + } + } + } + } 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); + + long endTime = System.currentTimeMillis(); + log.info("toProductAsyncTask执行完成 - taskId: {}, 总耗时: {}ms", taskId, (endTime - startTime)); + return taskId; + } + + + public String createGoogleAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + // 从 resources 加载 JSON 文件 + 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("IMAGE")); + JSONObject imageConfig = new JSONObject(); + imageConfig.set("aspectRatio", "9:16"); + generationConfig.set("imageConfig", imageConfig); +// 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); + parametersObj.set("aspectRatio", "9:16"); + + // 构建完整的请求体 + 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(() -> { + long startTime = System.currentTimeMillis(); + log.info("开始执行 createGoogleAsyncTask 方法,taskId: {}, model: {}, prompt: {}", taskId, useModel, prompt); + + final int maxRetries = 3; + final int retryDelayMs = 2000; + + for (int attempt = 1; attempt <= maxRetries; attempt++) { + try { + log.info("Google API 调用尝试 {}/{} - taskId: {}", attempt, maxRetries, taskId); + + // 异步获取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() + .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)) + .connectTimeout(45, TimeUnit.SECONDS) + .readTimeout(120, TimeUnit.SECONDS) + .writeTimeout(120, TimeUnit.SECONDS) + .callTimeout(180, TimeUnit.SECONDS) + .retryOnConnectionFailure(true) + .build(); + + Request request = new Request.Builder() + .url(endpoint) + .addHeader("Authorization", "Bearer " + tokenValue) + .addHeader("Content-Type", "application/json") + .addHeader("User-Agent", "AIDA-Backend/1.0") + .addHeader("Accept", "application/json") + .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) + .build(); + + long requestStartTime = System.currentTimeMillis(); + try (Response response = client.newCall(request).execute()) { + long requestDuration = System.currentTimeMillis() - requestStartTime; + log.info("Google API 请求完成 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 耗时: {}ms", + taskId, attempt, endpoint, response.code(), requestDuration); + + 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"); + String finishReason = candidate.getString("finishReason"); + if (finishReason != null && finishReason.equals("STOP")) { + 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 (finishReason != null && finishReason.equals("IMAGE_SAFETY")) { + String finishMessage = candidate.getString("finishMessage"); + if (finishMessage != null && finishMessage.contains("Try rephrasing the prompt")) { + finishMessage = "Try rephrasing the prompt or replacing the image. If you think this was an error, send feedback."; + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { + finishMessage = "请尝试重新表述提示词,或更换图片。若您认为这是误判,可提交反馈。"; + } + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, finishMessage, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + return; + } + + } + } 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"); + String raiFilteredReason = prediction.getString("raiFilteredReason"); + if (raiFilteredReason != null && raiFilteredReason.contains("Try rephrasing the prompt")) { + raiFilteredReason = "Input data may contain inappropriate content."; + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { + raiFilteredReason = "输入数据可能包含不当内容。"; + } + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, raiFilteredReason, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + } + } + + 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); + + long totalDuration = System.currentTimeMillis() - startTime; + log.info("Google 图片生成成功 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); + return; // 成功后立即返回 + } 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); + return; // 业务逻辑失败,直接返回不重试 + } + } + } catch (IOException e) { + // 只对 IOException 进行重试 + log.warn("Google API 调用发生 IOException - taskId: {}, 尝试: {}/{}, 异常: {}", + taskId, attempt, maxRetries, e.getMessage()); + + if (attempt < maxRetries) { + try { + long delay = retryDelayMs * attempt; // 递增延迟 + log.info("等待 {}ms 后进行第 {} 次重试 - taskId: {}", delay, attempt + 1, taskId); + Thread.sleep(delay); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("重试等待被中断 - taskId: {}", taskId); + break; + } + } else { + // 达到最大重试次数,记录错误并更新Redis状态 + log.error("Google API 调用最终失败,已达到最大重试次数 {} - taskId: {}", maxRetries, taskId, e); + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + } catch (Exception e) { + // 对于其他异常(非IOException),记录错误并直接返回,不重试 + log.error("Google API 调用发生意外异常 - taskId: {}, 异常类型: {}, 异常信息: {}", + taskId, e.getClass().getSimpleName(), e.getMessage(), e); + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + return; // 非网络异常,直接返回不重试 + } + } + + // 如果所有重试都失败了,记录最终失败日志 + long totalDuration = System.currentTimeMillis() - startTime; + log.error("Google API 调用最终失败,所有重试都已用尽 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); + 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, + generateDTO.getText(), + "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; + } + + /** + * 参数校验 + */ + private void validateRequiredParams(GenerateThroughImageTextDTO generateDTO) { + if (Objects.isNull(generateDTO.getUserId())) { + throw new BusinessException("userId.cannot.be.empty"); + } + + // Printboard必须要有level2Type + if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) + && StringUtil.isNullOrEmpty(generateDTO.getLevel2Type())) { + throw new BusinessException("level2Type.cannot.be.empty"); + } + if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) && generateDTO.getLevel2Type().equals(CreditsEventsEnum.PATTERN.getName())) { + 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"); + } + } + + } + + /** + * 判断是否为wx模型 + */ + private boolean isWxModel(GenerateThroughImageTextDTO generateDTO) { + return !StringUtil.isNullOrEmpty(generateDTO.getModelName()) + && "wx".equals(generateDTO.getModelName()); + } + + /** + * 处理wx模型生成 + */ + private PrepareForGenerateVO handleWxModelGeneration(GenerateThroughImageTextDTO generateDTO) { + // 校验积分是否足够 + validateCredits(CreditsEventsEnum.WX_TEXT2IMG); + // 创建生成任务 + String taskId = createAsyncTask(generateDTO); + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.WX_TEXT2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + private PrepareForGenerateVO handleQwenModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + + validateCredits(CreditsEventsEnum.QWEN_TEXT2IMG); + String taskId = createQwenAsyncTask(generateDTO, useModel, prompt); + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.QWEN_TEXT2IMG); + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + private PrepareForGenerateVO handleDouBaoModelGeneration(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + CreditsEventsEnum creditsEvents = null; + if (useModel.equals(ModelConstants.PRINTBOARD_ADVANCED_I2I)) { + creditsEvents = CreditsEventsEnum.DOUBAO_IMG2IMG_ADVANCED; + } else if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_I2I)) { + creditsEvents = CreditsEventsEnum.DOUBAO_IMG2IMG_HIGH; + } else if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_T2I) || useModel.equals(ModelConstants.MOODBOARD_ADVANCED)) { + creditsEvents = CreditsEventsEnum.DOUBAO_TEXT2IMG; + } + validateCredits(creditsEvents); + String taskId = createDouBaoAsyncTask(generateDTO, useModel, prompt); + processCreditDeduction(generateDTO.getUserId(), taskId, creditsEvents); + + 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) { + + String exceptionMessage = e.getMessage(); + if (exceptionMessage != null && exceptionMessage.contains("\"message\"")) { + try { + // 解析JSON格式的异常信息 + JSONObject jsonObj = JSONUtil.parseObj(exceptionMessage); + exceptionMessage = jsonObj.getStr("message"); + if ("Input data may contain inappropriate content.".equals(exceptionMessage)) { + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())) { + exceptionMessage = "输入数据可能包含不当内容。"; + } + } + throw new BusinessException(exceptionMessage, ResultEnum.PROMPT.getCode()); + } catch (Exception parseException) { + // 如果解析失败,返回原始消息 + throw new BusinessException(exceptionMessage, ResultEnum.PROMPT.getCode()); + } + } + throw new BusinessException(e.getMessage(), ResultEnum.PROMPT.getCode()); + } + String taskId = result.getOutput().getTaskId(); + log.info("qwen text2image 请求生成:{}, taskId:{}", JsonUtils.toJson(result), taskId); + + Generate generate = new Generate(userId, taskId, level1Type, level2Type, generateDTO.getText(), "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); + } + + // 保存生成记录到数据库 + Generate generate = new Generate( + userId, + taskId, + level1Type, + level2Type, + generateDTO.getText(), + "text(" + gender + ")", + useModel, // 记录使用的具体模型名称 + new Date() + ); + save(generate); + + 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); + + } catch (Exception e) { + log.error("Doubao image generation failed for taskId: {}", taskId, e); + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + + // 根据用户语言设置默认错误信息 + String errorMessage = "图像生成失败,请稍后重试"; // 默认中文 + String userLanguage = "CHINESE_SIMPLIFIED"; // 默认语言 + + if (account != null && account.getLanguage() != null) { + userLanguage = account.getLanguage(); + if (account.getLanguage().equals("ENGLISH")) { + errorMessage = "Image generation failed, please try again later"; + } else if (account.getLanguage().equals("CHINESE_SIMPLIFIED")) { + errorMessage = "图像生成失败,请稍后重试"; + } + } + String errorCode = null; + + // 检查是否为ArkHttpException,提取code和detailMessage + try { + // 通过反射获取code字段 + java.lang.reflect.Field codeField = e.getClass().getDeclaredField("code"); + codeField.setAccessible(true); + Object codeValue = codeField.get(e); + if (codeValue != null) { + errorCode = codeValue.toString(); + } + + // 通过反射获取detailMessage字段 + e.printStackTrace(); + String detailMessageValue = e.getMessage(); + if (detailMessageValue != null) { + String detailMessage = detailMessageValue.toString(); + + // 根据错误码返回相应的用户友好错误信息 + errorMessage = getDoubaoErrorMessage(errorCode, detailMessage, userLanguage); + } + } catch (Exception reflectionException) { + reflectionException.printStackTrace(); + log.warn("Failed to extract error details from ArkHttpException: {}", reflectionException.getMessage()); + // 如果反射失败,尝试从异常消息中提取信息 + String exceptionMessage = e.getMessage(); + if (exceptionMessage != null) { + boolean isEnglish = "ENGLISH".equals(userLanguage); + if (exceptionMessage.contains("OutputImageSensitiveContentDetected")) { + errorMessage = isEnglish ? "Generated image may contain sensitive content, please try with different input" : "生成的图像可能包含敏感信息,请更换输入内容后重试"; + } else if (exceptionMessage.contains("InputTextSensitiveContentDetected")) { + errorMessage = isEnglish ? "Input text may contain sensitive content, please try again" : "输入文本可能包含敏感信息,请更换后重试"; + } else if (exceptionMessage.contains("InputImageSensitiveContentDetected")) { + errorMessage = isEnglish ? "Input image may contain sensitive content, please try again" : "输入图像可能包含敏感信息,请更换后重试"; + } else if (exceptionMessage.contains("SensitiveContentDetected")) { + errorMessage = isEnglish ? "Input content may contain sensitive information, please try again" : "输入内容可能包含敏感信息,请更换后重试"; + } else if (exceptionMessage.contains("MissingParameter")) { + errorMessage = isEnglish ? "Request parameters are incomplete, please check input" : "请求参数不完整,请检查输入内容"; + } else if (exceptionMessage.contains("InvalidParameter")) { + errorMessage = isEnglish ? "Request parameters are invalid, please check input format" : "请求参数无效,请检查输入格式"; + } else if (exceptionMessage.contains("InvalidImageURL")) { + errorMessage = isEnglish ? "Image format is incorrect or data is corrupted, please re-upload" : "图片格式不正确或数据损坏,请重新上传"; + } else if (exceptionMessage.contains("OutofContextError")) { + errorMessage = isEnglish ? "Input content is too long, please shorten text or image size" : "输入内容过长,请缩短文本或图片尺寸"; + } else if (exceptionMessage.contains("AuthenticationError")) { + errorMessage = isEnglish ? "Service authentication failed, please contact administrator" : "服务认证失败,请联系管理员"; + } else if (exceptionMessage.contains("TooManyRequests") || exceptionMessage.contains("429")) { + errorMessage = isEnglish ? "Too many requests, please try again later" : "请求过于频繁,请稍后重试"; + } else if (exceptionMessage.contains("InternalServerError") || exceptionMessage.contains("500")) { + errorMessage = isEnglish ? "Image generation service is temporarily unavailable, please try again later" : "图像生成服务暂时不可用,请稍后重试"; + } + } + } + + // 更新Redis状态为失败 + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, errorMessage, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 记录详细错误信息用于调试 + log.error("Doubao API error - taskId: {}, errorCode: {}, errorMessage: {}", + taskId, errorCode, errorMessage); + + } + }); + + 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 + */ + private boolean isFluxPatternModel(GenerateThroughImageTextDTO generateDTO) { + return !StringUtil.isNullOrEmpty(generateDTO.getModelName()) + && "flux".equals(generateDTO.getModelName()) + && "Pattern".equals(generateDTO.getLevel2Type()); + } + + /** + * 处理flux pattern生成 + */ + private PrepareForGenerateVO handleFluxPatternGeneration(GenerateThroughImageTextDTO generateDTO) { + // 校验积分是否足够 + validateCredits(CreditsEventsEnum.FLUX_IMG2IMG); + + // 获取图片路径 + String imagePath = geti2iImagePath(generateDTO); + + // 创建生成任务 + String taskId = flux(PATTERN, generateDTO.getText(), imagePath, false); + + // 保存生成记录 + saveGenerateRecord(generateDTO, taskId, imagePath); + + // 处理积分扣除 + processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.FLUX_IMG2IMG); + + return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); + } + + /** + * 获取i2i需要传入模型的图片路径 + */ + private String geti2iImagePath(GenerateThroughImageTextDTO generateDTO) { + if (Objects.isNull(generateDTO.getCollectionElementId()) + || StringUtil.isNullOrEmpty(generateDTO.getDesignType())) { + return null; + } + + switch (generateDTO.getDesignType()) { + case "collection": + CollectionElement element = collectionElementMapper.selectById(generateDTO.getCollectionElementId()); + return element != null ? element.getUrl() : null; + case "library": + Library library = libraryService.getById(generateDTO.getCollectionElementId()); + return library != null ? library.getUrl() : null; + default: + return null; + } + } + + /** + * 保存生成记录 + */ + private void saveGenerateRecord(GenerateThroughImageTextDTO generateDTO, String taskId, String imagePath) { + Generate generate = CopyUtil.copyObject(generateDTO, Generate.class); + generate.setAccountId(generateDTO.getUserId()); + generate.setUniqueId(taskId); + generate.setElementSource(generateDTO.getDesignType()); + generate.setElementId(generateDTO.getCollectionElementId()); + + // 确定生成类型 + String generateType = determineGenerateType(generateDTO); + generate.setGenerateType(generateType); + generate.setModelName("flux"); + generate.setCreateDate(new Date()); + + save(generate); + } + + /** + * 确定生成类型 + */ + private String determineGenerateType(GenerateThroughImageTextDTO generateDTO) { + if (Objects.nonNull(generateDTO.getCollectionElementId())) { + return StringUtil.isNullOrEmpty(generateDTO.getText()) ? "image" : "text-image"; + } + return "text"; + } + + /** + * 处理标准生成流程 + */ + private PrepareForGenerateVO handleStandardGeneration(GenerateThroughImageTextDTO generateDTO) { + // 确定积分事件和生成次数 + GenerationConfig config = determineGenerationConfig(generateDTO); + + // 对Slogan功能限流 + if (config.creditsEvent.equals(CreditsEventsEnum.SLOGAN)) { + boolean b = redisUtil.allowRequest("Slogan"); + if (!b) { + return new PrepareForGenerateVO(429); + } + } + + // 校验积分是否足够 + validateCredits(config.creditsEvent); + + // 创建生成任务 + List taskIds = createGenerationTasks(generateDTO, config.times); + + // 截取任务id + String original = taskIds.get(0); + String taskId = original.substring(0, original.lastIndexOf("-", original.lastIndexOf("-") - 1)); + + // 处理积分扣除 + processCreditDeduction(generateDTO.getUserId(), taskId, config.creditsEvent); + + return new PrepareForGenerateVO(taskIds, 200); + } + + /** + * 确定生成配置(积分事件和生成次数) + */ + private GenerationConfig determineGenerationConfig(GenerateThroughImageTextDTO generateDTO) { + CreditsEventsEnum creditsEvent = CreditsEventsEnum.OTHER; + int times = 4; + + // 根据不同类型确定配置 + // high -> 生成图片质量高,但生成速度慢,每次生成只返回一张图片 + // fast -> 生成图片质量低,但生成速度快,每次生成返回四张图片 + switch (generateDTO.getLevel1Type()) { + case "Printboard": + GenerationConfig generationConfig = handlePrintboardConfig(generateDTO); + creditsEvent = generationConfig.creditsEvent; + times = generationConfig.times; + break; + case "Moodboard": + creditsEvent = CreditsEventsEnum.MOOD_BOARD; + if (isHighModel(generateDTO)) { + creditsEvent = CreditsEventsEnum.LOCAL_TEXT2IMG_HIGH; + times = 1; + } + break; + case "Sketchboard": + creditsEvent = CreditsEventsEnum.SKETCH_BOARD; + if (isHighModel(generateDTO)) { + creditsEvent = CreditsEventsEnum.LOCAL_TEXT2IMG_HIGH; + times = 1; + } + break; + } + + return new GenerationConfig(creditsEvent, times); + } + + /** + * 处理Printboard的特殊配置 + */ + private GenerationConfig handlePrintboardConfig(GenerateThroughImageTextDTO generateDTO) { + String level2Type = generateDTO.getLevel2Type(); + CreditsEventsEnum creditsEvent = CreditsEventsEnum.OTHER; + int times = 4; + + if (!CollectionLevel2TypeEnum.printType().contains(level2Type)) { + throw new BusinessException("unknown.parameter.level2Type"); + } + + switch (level2Type) { + case "Pattern": + // Pattern参数校验 + Generate generate = new Generate(); + validateGeneraType(generate, generateDTO.getText(), generateDTO.getCollectionElementId()); + generateDTO.setGenerateType(generate.getGenerateType()); + + creditsEvent = CreditsEventsEnum.PATTERN; + if (isHighModel(generateDTO)) { + creditsEvent = CreditsEventsEnum.LOCAL_TEXT2IMG_HIGH; + times = 1; + } + break; + + case "Slogan": + validateSloganParams(generateDTO); + processSloganImage(generateDTO); + creditsEvent = CreditsEventsEnum.SLOGAN; + times = 1; + break; + + case "Logo": + validateLogoParams(generateDTO); + generateDTO.setSeed(String.valueOf(random.nextInt(501))); + creditsEvent = CreditsEventsEnum.LOGO; + times = 1; + break; + } + + return new GenerationConfig(creditsEvent, times); + } + + /** + * 校验Slogan参数 + */ + private void validateSloganParams(GenerateThroughImageTextDTO generateDTO) { + if (StringUtil.isNullOrEmpty(generateDTO.getSloganBase64())) { + log.error("Printboard-Slogan模式下,slogan image为空"); + throw new BusinessException("slogan.image.cannot.be.empty"); + } + if (StringUtil.isNullOrEmpty(generateDTO.getText())) { + log.error("Printboard-Slogan模式下,slogan text为空"); + throw new BusinessException("slogan.style.cannot.be.empty"); + } + } + + /** + * 处理Slogan图片上传 + */ + private void processSloganImage(GenerateThroughImageTextDTO generateDTO) { + // 上传图片到服务器 + String path = minioUtil.base64UploadToPath(generateDTO.getSloganBase64(), sloganBucket, null); + String name = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); + + // 保存到数据库 + CollectionElement element = new CollectionElement(); + element.setAccountId(generateDTO.getUserId()); + element.setCollectionId(0L); + element.setLevel1Type(PRINT_BOARD.getRealName()); + element.setLevel2Type(CollectionLevel2TypeEnum.SLOGAN.getRealName()); + element.setName(name); + element.setUrl(path); + element.setHasPin((byte) 0); + element.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(path, 24 * 60), Boolean.FALSE)); + element.setCreateDate(DateUtil.getByTimeZone(generateDTO.getTimeZone())); + collectionElementService.save(element); + + // 更新DTO + generateDTO.setCollectionElementId(element.getId()); + generateDTO.setSloganBase64(null); + generateDTO.setDesignType("collection"); + } + + /** + * 校验Logo参数 + */ + private void validateLogoParams(GenerateThroughImageTextDTO generateDTO) { + if (StringUtil.isNullOrEmpty(generateDTO.getText().trim())) { + throw new BusinessException("please.input.the.prompt"); + } + } + + /** + * 判断是否为high模型 + */ + private boolean isHighModel(GenerateThroughImageTextDTO generateDTO) { + return !StringUtil.isNullOrEmpty(generateDTO.getModelName()) + && "high".equals(generateDTO.getModelName()); + } + + /** + * 校验积分是否足够 + */ + private void validateCredits(CreditsEventsEnum creditsEvent) { + if (!creditsService.creditsPreDeduction(creditsEvent, 1)) { + throw new BusinessException("remaining.credits.insufficient", ResultEnum.WARNING.getCode()); + } + } + + /** + * 创建生成任务 + */ + private List createGenerationTasks(GenerateThroughImageTextDTO generateDTO, int times) { + String uuid = UUID.randomUUID().toString(); + List taskIds = new ArrayList<>(); + + // 特殊处理:某些情况下需要清空modelName + if ("Printboard".equals(generateDTO.getLevel1Type()) + && !"Pattern".equals(generateDTO.getLevel2Type())) { + // Logo 和 Slogan 没有模型可选 + generateDTO.setModelName(null); + } + + for (int i = 1; i <= times; i++) { + String taskId = uuid + "-" + i + "-" + generateDTO.getUserId(); + taskIds.add(taskId); + generateDTO.setUniqueId(taskId); + + // 序列化为JSON + String jsonString = JSON.toJSONString(generateDTO); + + // 加入Redis队列 + addToRedisQueue(taskId, jsonString); + } + + return taskIds; + } + + /** + * 添加到Redis队列 + */ + private void addToRedisQueue(String taskId, String jsonString) { + // 加入排队队列 +// Double maxScore = redisUtil.getMaxScore(consumptionOrderKey); +// redisUtil.addToZSet(consumptionOrderKey, taskId, maxScore); + + // 加入结果映射 + String key = generateResultKey + ":" + taskId; + GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Waiting"); + redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 发布到MQ + rabbitMQService.publishMessageToGenerate(jsonString); + } + + /** + * 处理积分扣除 + */ + private void processCreditDeduction(Long userId, String taskId, CreditsEventsEnum creditsEvent) { + // 添加到Redis + creditsService.addRecordToCreditsDeduction(userId, taskId, creditsEvent); + // 预插入到数据库 + creditsService.preInsert(userId, creditsEvent.getName(), taskId, Boolean.TRUE, null); + } + +// ============== 配置类 ============== + + /** + * 生成任务配置类 + * 包含积分事件类型和生成次数 + */ + private static class GenerationConfig { + final CreditsEventsEnum creditsEvent; + final int times; + + GenerationConfig(CreditsEventsEnum creditsEvent, int times) { + this.creditsEvent = creditsEvent; + this.times = times; + } + } + + @Override + public Long getRankPosition(String uniqueId) { + // rank 从0开始 + return redisUtil.getRank(consumptionOrderKey, uniqueId); + } + + @Override + public List getGenerateResultList(List taskIdList) { + List results = new ArrayList<>(); + Set collect = new HashSet<>(); + boolean flag = true; + String type = null; + for (String taskId : taskIdList) { + String key = generateResultKey + ":" + taskId; + GenerateResultVO generateResultVO = new Gson().fromJson(redisUtil.getFromString(key), GenerateResultVO.class); + + if (flag) { + type = resolveModelType(taskId, null); + flag = false; + } + // 暂定万象每次生成1个 + 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; + } else if (type.equals("flux")) { + results.add(getFluxResultAndSave(taskId)); + continue; + } + + if (generateResultVO != null && !StringUtil.isNullOrEmpty(generateResultVO.getUrl())) { + String url = generateResultVO.getUrl(); + if (url.substring(url.lastIndexOf("/") + 1).equals("white_image.jpg")) { + generateResultVO.setStatus("Invalid"); + } else { + generateResultVO.setUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + } else if (generateResultVO == null) { + generateResultVO = new GenerateResultVO(); + } + + if (!StringUtil.isNullOrEmpty(generateResultVO.getStatus())) { + collect.add(generateResultVO.getStatus()); + } + results.add(generateResultVO); + } + + if (taskIdList.size() == 4 && collect.size() == 1 && collect.contains("Fail")) { + log.info("当前4个生成结果均为失败"); + throw new BusinessException("generate.result.below.standard"); + } + return results; + } + + + public Generate selectByUniqueId(String uniqueId) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("unique_id", uniqueId); + + return getOne(qw); + } + + public List selectListByUniqueId(String uniqueId) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("unique_id", uniqueId).orderByDesc("id"); + + return baseMapper.selectList(qw); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelGenerate(Long userId, List uniqueIdList, String timeZone, String type) { + // todo 取消待优化 + uniqueIdList.forEach(uniqueId -> { + // 1、将需要取消的唯一id加入redis,以便及时取消生成 + redisUtil.addToSet(cancelSetKey, uniqueId, CommonConstant.REDIS_SET_EXPIRE_TIME); + + /*// 1、确认当前消息是否还在排队中 + Boolean exists = redisUtil.isElementExistsInZSet(consumptionOrderKey, uniqueId); + Boolean flag = Boolean.FALSE; + if (exists) flag = redisUtil.getRank(consumptionOrderKey, uniqueId) > 1L ? Boolean.TRUE : Boolean.FALSE; + // 不管flag的默认值是true还是false,只要exists为false,&& 将短路 + if (exists && flag) { + // 1.1、将需要取消的唯一id加入redis,以便及时取消生成 + redisUtil.addToSet(cancelSetKey, uniqueId); + // 1.2 将需要取消的id从redis的ConsumptionOrder中删除 + redisUtil.removeFromZSet(consumptionOrderKey, uniqueId); + } else { + // 2、判断该消息是否异常 + boolean hasKey = redisUtil.isElementExistsInMap(exceptionMapKey, uniqueId); + // 3、判断该消息是否已经消费结束 + Boolean existsInResult = redisUtil.isElementExistsInMap(resultMapKey, uniqueId); + if (!hasKey && !existsInResult) { + // 设置取等待状态为false + AsyncCallerUtil.waitingStatus.put(uniqueId, false); + // 3、直接发送取消请求到python端 + pythonService.cancelGenerateTask(uniqueId); + } + }*/ + String path; + if (type.equals("Logo")) { + path = CommonConstant.GENERATE_LOGO_SINGLE_CANCEL; + } else if (type.equals("PoseTransformation")) { + path = CommonConstant.POSE_TRANSFORMATION_CANCEL; + } else { + path = CommonConstant.GENERATE_CANCEL; + } + + String key = generateResultKey + ":" + uniqueId; + GenerateResultVO generateResultVO = new Gson().fromJson(redisUtil.getFromString(key), GenerateResultVO.class); + if (Objects.isNull(generateResultVO)) { + log.warn("任务不存在,无法取消"); + return; + } + // 判断当前task的状态是不是Fail + if (!generateResultVO.getStatus().equals("Fail")) { + // 2、不是,直接发送取消请求到python端 + pythonService.cancelGenerateTask(uniqueId, path); + // 3、更改result中当前taskId的状态 + redisUtil.addToString(key, new Gson().toJson(new GenerateResultVO(uniqueId, null, null, "Cancelled")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + + // 3、考虑加一张表,专门用于记录哪些用户在什么时间进行了取消操作,包括已经异常的请求 + GenerateCancel generateCancel = new GenerateCancel(userId, uniqueId, DateUtil.getByTimeZone(timeZone)); + generateCancelMapper.insert(generateCancel); + }); + + + } + + @Override + public void processRelightResult(String taskId, String url, String category) { + QueryWrapper qw = new QueryWrapper<>(); + qw.lambda().eq(ToProductImageResult::getTaskId, taskId); + List toProductImageResults = toProductImageResultMapper.selectList(qw); + if (CollectionUtils.isEmpty(toProductImageResults)) { + return; +// throw new BusinessException(""); + } + ToProductImageResult toProductImageResult = toProductImageResults.get(0); + if (toProductImageResult.getBrightenValue() != null && toProductImageResult.getBrightenValue() != 1.0) { + pythonService.bright(url, toProductImageResult.getBrightenValue()); + } + toProductImageResult.setUrl(url); + toProductImageResult.setStatus("Success"); +// toProductImageResult.setResultType("Relight"); + toProductImageResultMapper.updateById(toProductImageResult); + + String key = relightResultKey + ":" + taskId; + String imageName = url.substring(url.lastIndexOf("/") + 1); + String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; + GenerateResultVO generateResultVO = new GenerateResultVO(taskId, toProductImageResult.getId(), url, status, category); + redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + Long accountId = Long.parseLong(taskId.substring(taskId.lastIndexOf("-") + 1)); + if (!status.equals("Invalid")) { + // 4、扣除积分 + Boolean b = creditsService.taskCreditsDeduction(accountId, taskId); + // 3、记录积分变更 + if (b) creditsService.insertToCreditsDetail(accountId, + CreditsEventsEnum.RELIGHT.getName(), + CreditsEventsEnum.RELIGHT.getValue(), + "negative", null); + } + } + + // 判断试用用户试用generate机会是否使用完毕 每个board 3次机会 + private int getTrialsCount(Long userId, String level1Type) { + List getGenerateList = getGenerateByAccountId(userId, level1Type); + int trialsCount; + if (getGenerateList.isEmpty()) { + trialsCount = 0; + } else if (getGenerateList.size() == 1 || (getGenerateList.size() >= 4 && getGenerateList.size() / 4 == 1)) { + trialsCount = 1; + } else if (getGenerateList.size() == 2 || (getGenerateList.size() >= 4 && getGenerateList.size() / 4 == 2)) { + trialsCount = 2; + } else { + trialsCount = 2; + } + return trialsCount; + } + + public List getGenerateByAccountId(Long accountId, String level1Type) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("account_id", accountId); + qw.eq("level1_type", level1Type); + + return baseMapper.selectList(qw); + } + + public List> getCountByUserAndTime(String startTime, String endTime, List accountIdList) { + List> byTypeAndTime = baseMapper.getByTypeAndTime(startTime, endTime, accountIdList); + return byTypeAndTime; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public GenerateResultVO imageToSketch(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, Long projectId) { + + Long accountId = UserContext.getUserHolder().getId(); + log.info("imageToSketch parameter : {}", imageToSketchDTO); + + // 检查积分是否够本次扣除 + CreditsEventsEnum event = CreditsEventsEnum.IMAGE_TO_SKETCH; + Boolean b = creditsService.checkCredits(accountId, event, 1); + if (!b) { + throw new BusinessException("remaining.credits.insufficient", ResultEnum.PROMPT.getCode()); + } + + String style = imageToSketchDTO.getStyle(); + String styleCode = style.equals(SketchStyle.THICK.getValue()) ? "1" : + style.equals(SketchStyle.MEDIUM.getValue()) ? "2" : + style.equals(SketchStyle.THIN.getValue()) ? "3" : "Custom"; + + // 线稿提取 + String sketchPath = requestSketchExtract(imageToSketchDTO, collagePictureUrl, accountId, styleCode); + // 存数据库 + Generate generate = saveExtractSketchRequest(imageToSketchDTO, collagePictureUrl, projectId, + accountId, styleCode, "local", "0"); + GenerateResultVO generateResultVO = saveExtractSketchResult(generate, sketchPath, imageToSketchDTO.getGender()); + // 积分扣除 + doCreditsSubtract(accountId, event); + + return generateResultVO; + } + + private String requestSketchExtract(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, + Long accountId, String styleCode) { + String imagePath; + if (StringUtil.isNullOrEmpty(collagePictureUrl)) { + CollectionElement collectionElement = collectionElementService.getById(imageToSketchDTO.getElementId()); + imagePath = collectionElement.getUrl(); + } else { + imagePath = collagePictureUrl; + } + + log.info(minioUtil.getPreSignedUrl(imagePath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + String imageName = UUID.randomUUID().toString(); + String objectName = accountId + "/imageToSketch/" + imageName; + + String styleImage; + if (!Objects.isNull(imageToSketchDTO.getStyleImageId())) { + CollectionElement styleElement = collectionElementService.getById(imageToSketchDTO.getElementId()); + styleImage = styleElement.getUrl(); + } else { + styleImage = ""; + } + String sketchPath = pythonService.imageToSketch(imagePath, userBucket, objectName, styleCode, styleImage); + log.info("初步图片提取结果:{}", sketchPath); + return sketchPath; + } + + private Generate saveExtractSketchRequest(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, + Long projectId, Long accountId, String styleCode, + String modelName, String taskId) { + // 存DB + Generate generate = new Generate(); + generate.setAccountId(accountId); + generate.setUniqueId(taskId); + generate.setLevel1Type(SKETCH_BOARD.getRealName()); + generate.setLevel2Type("ImageToSketch"); + generate.setElementSource("collection"); + generate.setElementId(imageToSketchDTO.getElementId()); + generate.setGenerateType("image(" + imageToSketchDTO.getGender() + ")"); + generate.setModelName(modelName); + generate.setSketchStyle(styleCode); + generate.setStyleImageElementId(imageToSketchDTO.getStyleImageId()); + generate.setProjectId(projectId); + generate.setInputImageUrl(collagePictureUrl); + generate.setCreateDate(new Date()); + baseMapper.insert(generate); + return generate; + } + + public GenerateResultVO saveExtractSketchResult(Generate generate, String sketchPath, String gender) { + // 将生成结果存入DB + GenerateDetail generateDetail = new GenerateDetail(); + generateDetail.setGenerateId(generate.getId()); + generateDetail.setUrl(sketchPath); + generateDetail.setIsLike((byte) 0); + generateDetail.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(sketchPath, 24 * 60), Boolean.FALSE)); + generateDetail.setCreateDate(LocalDateTime.now()); + generateDetailMapper.insert(generateDetail); + + String clothCategory = pythonService.getClothCategory(sketchPath, gender); + + return new GenerateResultVO(generate.getUniqueId(), generateDetail.getId(), + minioUtil.getPreSignedUrl(sketchPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), "Success", clothCategory); + } + + public void doCreditsSubtract(Long accountId, CreditsEventsEnum event) { + Account account = accountService.getById(accountId); + BigDecimal existingCredits = account.getCredits(); + BigDecimal subtract = existingCredits.subtract(new BigDecimal(event.getValue())); + BigDecimal creditsUsage = null; + if (!StringUtil.isNullOrEmpty(account.getOrganizationName())) { + creditsUsage = Objects.isNull(account.getCreditsUsage()) ? BigDecimal.ZERO : account.getCreditsUsage(); + creditsUsage = creditsUsage.add(new BigDecimal(event.getValue())); + } + accountService.updateCreditsAndEndTime(account, subtract.toString(), null, creditsUsage); + creditsService.preInsert(accountId, event.getName(), null, Boolean.FALSE, event.getValue()); + } + + // 注入线程池(可在配置类中定义) + @Resource + private Executor asyncTaskExecutor; + + public String imageToSketchAsync(ImageToSketchDTO imageToSketchDTO, String collagePictureUrl, Long projectId) { + Long accountId = UserContext.getUserHolder().getId(); + log.info("imageToSketch parameter : {}", imageToSketchDTO); + // 目前没有用其他提取模型,暂时写死flux + imageToSketchDTO.setModelName("flux"); + + // 检查积分是否够本次扣除 +// CreditsEventsEnum event = CreditsEventsEnum.IMAGE_TO_SKETCH; + CreditsEventsEnum event = CreditsEventsEnum.IMAGE_TO_SKETCH_FLUX; + Boolean b = creditsService.checkCredits(accountId, event, 1); + if (!b) { + throw new BusinessException("remaining.credits.insufficient", ResultEnum.PROMPT.getCode()); + } + + // 生成唯一任务ID + String taskId; + if (!StringUtil.isNullOrEmpty(imageToSketchDTO.getModelName()) + && imageToSketchDTO.getModelName().equals("flux")) { + String imagePath; + // todo 拼贴图的线稿提取是否能用flux + if (StringUtil.isNullOrEmpty(collagePictureUrl)) { + CollectionElement collectionElement = collectionElementService.getById(imageToSketchDTO.getElementId()); + imagePath = collectionElement.getUrl(); + } else { + imagePath = collagePictureUrl; + } + taskId = flux(event, null, imagePath, false); + // 存数据库 + saveExtractSketchRequest(imageToSketchDTO, collagePictureUrl, projectId, + accountId, imageToSketchDTO.getStyle(), "flux", taskId); + + // 6、添加预扣除积分到redis + creditsService.addRecordToCreditsDeduction(accountId, taskId, event); + // 6.1 添加积分扣除记录到db + creditsService.preInsert(accountId, event.getName(), taskId, Boolean.TRUE, null); + + return taskId; + } + + taskId = UUID.randomUUID().toString(); + // 异步执行耗时操作(由于prompt提取耗时较长,页面暂时只提供flux生成,废弃以下部分) + CompletableFuture.runAsync(() -> { + try { + processImageToSketch(taskId, imageToSketchDTO, collagePictureUrl, projectId, accountId, event); + } catch (Exception e) { + log.error("异步处理图片转sketch失败, taskId: {}", taskId, e); + // 更新redis + redisUtil.addToString(generateResultKey + ":" + taskId, new Gson().toJson(new GenerateResultVO(taskId, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + }, asyncTaskExecutor); + return taskId; + } + + private void processImageToSketch(String taskId, ImageToSketchDTO imageToSketchDTO, + String collagePictureUrl, Long projectId, + Long accountId, CreditsEventsEnum event) throws IOException { + // 设置任务状态为处理中 + redisUtil.addToString(generateResultKey + ":" + taskId, new Gson().toJson(new GenerateResultVO(taskId, "Executing")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + String style = imageToSketchDTO.getStyle(); + String styleCode = style.equals(SketchStyle.THICK.getValue()) ? "1" : + style.equals(SketchStyle.MEDIUM.getValue()) ? "2" : + style.equals(SketchStyle.THIN.getValue()) ? "3" : "Custom"; + // 请求记录存数据库 + Generate generate = saveExtractSketchRequest(imageToSketchDTO, collagePictureUrl, projectId, + accountId, styleCode, "freepik", taskId); + // 1、初步提取结果 + String sketchPath = requestSketchExtract(imageToSketchDTO, collagePictureUrl, accountId, styleCode); + // 2、获取输入图的描述 + String imageDescription = getImageDescription(sketchPath); + // 3、请求freepik reimage + String dataStr = reimagineFreePik(sketchPath, imageDescription, "vivid"); + if (StringUtil.isNullOrEmpty(dataStr)) { + throw new BusinessException("extract sketch failed"); + } + + JSONObject data = JSONUtil.parseObj(dataStr); + String upgradeImageUrl = data.getBeanList("generated", String.class).get(0); + String freepikTaskId = data.getStr("task_id"); + + // 4、下载图片 + byte[] bytes = downloadVideoOrImage(upgradeImageUrl); +// byte[] bytes = downloadWithProxy(upgradeImageUrl); + // 5、上传图片到minio保存 + String objectName = accountId + "/imageToSketch/" + freepikTaskId + ".png"; + minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png"); + // 6、保存结果到db + GenerateResultVO generateResultVO = saveExtractSketchResult(generate, userBucket + "/" + objectName, imageToSketchDTO.getGender()); + // 7、积分扣除 + doCreditsSubtract(accountId, event); + // 8、将结果存入Redis + redisUtil.addToString(generateResultKey + ":" + taskId, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + } + + // 对提取出来的sketch做调整 + // 输入 base64,以及 性别 分类,将图片添加到library + @Override + @Transactional(rollbackFor = Exception.class) + public GenerateResultVO modifySketch(GenerateModifyDTO generateModifyDTO) { + log.info("修改生成或library中的sketch或print,并加入到library"); + + // 提取常用参数 + Long accountId = UserContext.getUserHolder().getId(); + String base64 = generateModifyDTO.getBase64(); + String gender = generateModifyDTO.getGender().toLowerCase(); + String category = generateModifyDTO.getCategory(); + Long originalId = generateModifyDTO.getOriginalId(); + String originalIdSource = generateModifyDTO.getOriginalIdSource(); + boolean isOverride = generateModifyDTO.getIsOverride(); + boolean isSketch = generateModifyDTO.getType().equals(SKETCH_BOARD.getRealName()); + + // 获取原始路径和可能的generateId + PathInfo pathInfo = getOriginalPathAndGenerateId(originalIdSource, originalId); + if (Objects.isNull(pathInfo)) { + throw new BusinessException("unknown sourceIdType", ResultEnum.PROMPT.getCode()); + } + + // 确定存储路径 + String storagePath = isOverride + ? pathInfo.originalPath.replaceFirst("^[^/]+/", "").replaceFirst("\\.[^.]+$", "") + : isSketch + ? accountId + "/sketchboard/" + gender + "/" + category + "/" + UUID.randomUUID() + : accountId + "/printboard/" + UUID.randomUUID(); + + // 上传到MinIO + String minioPath = minioUtil.base64UploadToPath(base64, userBucket, storagePath); + log.info("修改后的图片:{}", minioPath); + + // 保存到数据库并返回结果 + return originalIdSource.equals("Library") + ? handleLibrarySave(accountId, originalId, minioPath, category, gender, isOverride, generateModifyDTO.getType()) + : originalIdSource.equals("Generate") + ? handleGenerateSave(originalId, pathInfo.generateId, minioPath, category, isOverride) + : handleUploadSave(accountId, originalId, minioPath, category, generateModifyDTO.getGender(), isOverride, generateModifyDTO.getType()); + } + + private static class PathInfo { + String originalPath; + Long generateId; + + PathInfo(String originalPath, Long generateId) { + this.originalPath = originalPath; + this.generateId = generateId; + } + } + + private PathInfo getOriginalPathAndGenerateId(String originalIdSource, Long originalId) { + switch (originalIdSource) { + case "Library": + return new PathInfo(libraryService.getById(originalId).getUrl(), null); + case "Generate": + GenerateDetail detail = generateDetailMapper.selectById(originalId); + return new PathInfo(detail.getUrl(), detail.getGenerateId()); + case "Collection": + CollectionElement collectionElement = collectionElementMapper.selectById(originalId); + return new PathInfo(collectionElement.getUrl(), null); + default: + return null; + } + } + + private GenerateResultVO handleLibrarySave(Long accountId, Long libraryId, String minioPath, + String category, String gender, boolean isOverride, String level1Type) { + Library library; + String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false); + if (isOverride) { + library = new Library(); + library.setId(libraryId); + library.setUrl(minioPath); + library.setUpdateDate(new Date()); + libraryService.updateById(library); + } else { + library = new Library(accountId, level1Type, category, gender, minioPath, md5, new Date()); + libraryService.save(library); + libraryId = library.getId(); + } + return buildResultVO(libraryId, minioPath, category); + } + + private GenerateResultVO handleGenerateSave(Long originalId, Long generateId, String minioPath, + String category, boolean isOverride) { + GenerateDetail generateDetail = new GenerateDetail(); + String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true), Boolean.FALSE); + if (isOverride) { + generateDetail.setId(originalId); + generateDetail.setUrl(minioPath); + generateDetail.setUpdateDate(new Date()); + generateDetailMapper.updateById(generateDetail); + } else { + generateDetail.setGenerateId(generateId); + generateDetail.setUrl(minioPath); + generateDetail.setIsLike((byte) 0); + generateDetail.setMd5(md5); + generateDetail.setCreateDate(LocalDateTime.now()); + generateDetailMapper.insert(generateDetail); + originalId = generateDetail.getId(); + } + return buildResultVO(originalId, minioPath, category); + } + + private GenerateResultVO handleUploadSave(Long accountId, Long originalId, String minioPath, + String category, String gender, boolean isOverride, String level1Type) { + CollectionElement collectionElement; + String md5 = MD5Utils.encryptFile(minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false); + if (isOverride) { + collectionElement = new CollectionElement(); + collectionElement.setId(originalId); + collectionElement.setUrl(minioPath); + collectionElement.setMd5(md5); + collectionElement.setUpdateDate(new Date()); + collectionElementMapper.updateById(collectionElement); + } else { + CollectionElement originalElement = collectionElementMapper.selectById(originalId); + String name = minioPath.substring(minioPath.lastIndexOf("/") + 1, minioPath.lastIndexOf(".")); + if ("Sketchboard".equals(level1Type)) { + collectionElement = new CollectionElement(accountId, level1Type, category, gender, name, minioPath, (byte) 0, md5, new Date(), originalElement.getProjectId()); + } else { + collectionElement = new CollectionElement(accountId, level1Type, category, name, minioPath, (byte) 0, md5, new Date(), originalElement.getProjectId()); + } + collectionElementMapper.insert(collectionElement); + } + return buildResultVO(collectionElement.getId(), minioPath, category); + } + + private GenerateResultVO buildResultVO(Long id, String minioPath, String category) { + String url = minioUtil.getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true); + return new GenerateResultVO(id, url, "Success", category); + } + + public ToProductImageResultVO poseTransform(PoseTransformDTO poseTransformDTO) { + // 参数判断 + handleMotionParams(poseTransformDTO); + + Long accountId = UserContext.getUserHolder().getId(); + Long projectId = poseTransformDTO.getProjectId(); + String productImage = poseTransformDTO.getProductImage(); + Integer poseId = poseTransformDTO.getPoseId(); + boolean wxTask = StringUtil.isNullOrEmpty(poseTransformDTO.getModelName()) && poseTransformDTO.getModelName().equals("wx"); + + // 1、判断用户当前积分是否够本次生成消耗 + CreditsEventsEnum creditsEventsEnum = wxTask ? CreditsEventsEnum.WX_ANIMATION : CreditsEventsEnum.LOCAL_ANIMATION; + Boolean preDeduction = creditsService.creditsPreDeduction(creditsEventsEnum, 1); + if (!preDeduction) { + throw new BusinessException("remaining.credits.insufficient", ResultEnum.WARNING.getCode()); + } + + // 3、生成唯一id 使用uuid,由于uuid重复的几率很小,故取消对uuid重复性的校验 + String taskId; + Boolean isRequestSuccess = false; + PoseTransformation poseTransformation = new PoseTransformation(); + if (!StringUtil.isNullOrEmpty(poseTransformDTO.getModelName()) && poseTransformDTO.getModelName().equals("wx")) { + taskId = animateAnyone(poseTransformDTO, accountId); + if (!StringUtil.isNullOrEmpty(taskId)) { + isRequestSuccess = true; + apiGenerateService.addAPIGenerateRecordAsync(accountId, taskId, Module.poseTransfer.getValue(), "wx", "Pending"); + } + poseTransformation.setModelName("wx"); + } else { + String uuid = UUID.randomUUID().toString(); + taskId = uuid + "-" + accountId; + + com.alibaba.fastjson.JSONObject params = createParamsForMotion(poseTransformDTO, taskId); + String api = params.getString("api"); + params.remove("api"); + isRequestSuccess = pythonService.poseTransformation(params, api); + } + + poseTransformation.setProjectId(projectId); + poseTransformation.setAccountId(accountId); + poseTransformation.setUniqueId(taskId); + poseTransformation.setProductImage(productImage); + poseTransformation.setLastFrameProductImage(poseTransformDTO.getLastFrameProductImage()); + poseTransformation.setPoseId(poseId); + poseTransformation.setIsLiked((byte) 0); + String taskStatus = isRequestSuccess ? "Executing" : "Fail"; + poseTransformation.setTaskStatus(taskStatus); + poseTransformation.setPrompt(poseTransformDTO.getPrompt()); + poseTransformation.setCreateTime(LocalDateTime.now()); + poseTransformationMapper.insert(poseTransformation); + // 当需要默认like + ToProductImageResultVO toProductImageResultVO = new ToProductImageResultVO(); + toProductImageResultVO.setId(poseTransformation.getId()); + toProductImageResultVO.setParentId(poseTransformDTO.getParentId()); + toProductImageResultVO.setResultType(Module.poseTransfer.getValue()); + toProductImageResultVO.setTaskId(taskId); + toProductImageResultVO.setStatus(taskStatus); + toProductImageResultVO.setSourceUrl(minioUtil.getPreSignedUrl(productImage, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + toProductImageResultVO.setPoseId(poseId); + toProductImageResultVO.setModelName(poseTransformDTO.getModelName()); + + if (Objects.nonNull(poseTransformDTO.getIsDefaultLike()) && poseTransformDTO.getIsDefaultLike()) { + // 满足条件下添加到like + poseTransformation.setIsLiked((byte) 1); + poseTransformation.setUpdateTime(LocalDateTime.now()); + poseTransformationMapper.updateById(poseTransformation); + CollectionSort collectionSort = addPoseTransferLike(poseTransformDTO, poseTransformation.getId()); + Integer reSort = collectionSortService.rearrangeChildSort(poseTransformation.getId(), CollectionType.POSE_TRANSFORM.getValue(), + poseTransformDTO.getParentId(), poseTransformDTO.getUserLikeSortId()); + toProductImageResultVO.setSort(Objects.isNull(reSort) ? Objects.isNull(collectionSort) ? null : collectionSort.getSort() : reSort); + toProductImageResultVO.setParentId(poseTransformDTO.getParentId()); + toProductImageResultVO.setUserLikeSortId(Objects.isNull(collectionSort) ? null : collectionSort.getId()); + } else if (Objects.nonNull(poseTransformDTO.getIsDefaultLike()) && Objects.nonNull(poseTransformDTO.getParentId())) { + toProductImageResultVO.setParentId(poseTransformDTO.getParentId()); + } + + + if (isRequestSuccess) { + // 6、添加预扣除积分到redis + creditsService.addRecordToCreditsDeduction(accountId, taskId, creditsEventsEnum); + // 6.1 添加积分扣除记录到db + creditsService.preInsert(accountId, creditsEventsEnum.getName(), taskId, Boolean.TRUE, null); + // 更新项目更新时间 + projectService.modifyProjectUpdateTime(projectId); + return toProductImageResultVO; + } + throw new BusinessException("pose.transformation.error", ResultEnum.ERROR.getCode()); + } + + private void handleMotionParams(PoseTransformDTO poseTransformDTO) { + if (Objects.isNull(poseTransformDTO.getMode()) || poseTransformDTO.getMode() == 0) { + throw new BusinessException("Mode cannot be empty"); + } + MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode()); + if (Objects.isNull(motionModeEnum)) { + throw new BusinessException("unknown.mode"); + } + if (poseTransformDTO.getMode() == 1 && Objects.isNull(poseTransformDTO.getPoseId())) { + throw new BusinessException("Please choose a pose"); + } + if (poseTransformDTO.getMode() == 3 && StringUtil.isNullOrEmpty(poseTransformDTO.getLastFrameProductImage())) { + throw new BusinessException("Last frame cannot be empty"); + } + } + + + private CollectionSort addPoseTransferLike(PoseTransformDTO poseTransformDTO, Long poseTransformationId) { + if (Objects.nonNull(poseTransformDTO.getParentId()) + && !poseTransformDTO.getParentId().equals(0L)) { + return disOrLikePose(poseTransformationId, "like", + poseTransformDTO.getProjectId(), poseTransformDTO.getParentId()); + } + return null; + } + + public void processPoseTransformResult(String taskId, String gifUrl, String videoUrl, String imageUrl) { + // 1、存储模型返回的数据 + PoseTransformation poseTransformation; + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("unique_id", taskId); + List poseTransformations = poseTransformationMapper.selectList(qw); + if (poseTransformations != null && poseTransformations.size() > 1) { + log.warn("通过taskId {} 查询到的PoseTransformation的结果不止一条", taskId); + } else if (poseTransformations == null || poseTransformations.isEmpty()) { + return; + } + poseTransformation = poseTransformations.get(0); + poseTransformation.setGifUrl(gifUrl); + poseTransformation.setVideoUrl(videoUrl); + poseTransformation.setFirstFrameUrl(imageUrl); + poseTransformation.setTaskStatus("Success"); + poseTransformation.setUpdateTime(LocalDateTime.now()); + poseTransformationMapper.updateById(poseTransformation); + + String key = generateResultKey + ":" + taskId; + PoseTransformationVO poseTransformationVO = new PoseTransformationVO( + poseTransformation.getId(), taskId, gifUrl, videoUrl, imageUrl, (byte) 0, "Success"); + poseTransformationVO.setPoseId(poseTransformation.getPoseId()); + poseTransformationVO.setModelName(poseTransformation.getModelName()); + + // 2、更新redis + redisUtil.addToString(key, new Gson().toJson(poseTransformationVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 3、执行积分扣除 + String accountId = taskId.substring(taskId.lastIndexOf("-") + 1); +// String uuid = taskId.substring(0, taskId.lastIndexOf("-")); + Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), taskId); + if (flag) creditsService.updateChangedCredits(accountId, taskId); + + Project project = projectService.getById(poseTransformation.getProjectId()); + // 发通知 + if (Objects.nonNull(project) && !StringUtil.isNullOrEmpty(project.getName())) { + messageCenterService.videoFinishedMsg(poseTransformation.getAccountId(), project.getName(), true); + } + } + + public List getPoseTransformationResult(List taskIdList, Long projectId, Boolean like) { + List resultList = new ArrayList<>(); + + // 处理按taskId列表查询的情况 + if (taskIdList != null && !taskIdList.isEmpty()) { + for (String taskId : taskIdList) { + PoseTransformationVO vo = buildPoseTransformationVO(taskId, null); + if (vo != null) { + resultList.add(vo); + } + } + } + // 处理按projectId和like查询的情况 + else if (projectId != null) { + QueryWrapper queryWrapper = new QueryWrapper() + .eq("project_id", projectId) + .ne("task_status", "Fail"); + + if (like != null) { + queryWrapper.eq("is_liked", like ? 1 : 0); + } + + List poseTransformations = poseTransformationMapper.selectList(queryWrapper); + + if (!CollectionUtils.isEmpty(poseTransformations)) { + for (PoseTransformation item : poseTransformations) { + PoseTransformationVO vo = buildPoseTransformationVO(item.getUniqueId(), item); + if (vo != null && !isInvalidStatus(vo.getStatus())) { + resultList.add(vo); + } + } + } + } + + return resultList; + } + + private PoseTransformationVO buildPoseTransformationVO(String taskId, PoseTransformation dbItem) { + String type = resolveModelType(taskId, CreditsEventsEnum.POSE_TRANSFORMATION.getValue()); + String key = generateResultKey + ":" + taskId; + String resultJson = redisUtil.getFromString(key); + + PoseTransformationVO vo; + + // 1. 优先从Redis获取数据 + if (!StringUtil.isNullOrEmpty(resultJson)) { + vo = new Gson().fromJson(resultJson, PoseTransformationVO.class); + + // 设置数据库中的额外字段 + if (dbItem != null) { + vo.setId(dbItem.getId()); + vo.setIsLiked(dbItem.getIsLiked()); + } + + // 处理成功状态的数据 + if ("Success".equals(vo.getStatus()) && !"wx".equals(type)) { + processAllUrls(vo, dbItem); + } + + vo.setResultType(CollectionType.POSE_TRANSFORM.getValue()); + } + // 2. 处理wx类型的情况 + else if ("wx".equals(type)) { + vo = getAnimateResult(taskId); + } + // 3. 处理既没有Redis数据也不是wx类型的情况 + else { + // 如果有数据库记录 + if (dbItem != null) { + vo = CopyUtil.copyObject(dbItem, PoseTransformationVO.class); + vo.setTaskId(taskId); + + // 设置产品图片URL(首帧) + if (dbItem.getProductImage() != null) { + vo.setProductImage(minioUtil.getPreSignedUrl( + dbItem.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + // 设置产品图片URL(尾帧) + if (dbItem.getLastFrameProductImage() != null) { + vo.setLastFrameProductImage(minioUtil.getPreSignedUrl( + dbItem.getLastFrameProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + + // 如果视频URL为空,直接返回 + if (StringUtil.isNullOrEmpty(dbItem.getVideoUrl())) { + return vo; + } + + processAllUrls(vo, dbItem); + } + // 如果没有数据库记录 + else { + vo = new PoseTransformationVO(taskId, "Executing"); + } + } + + AuthPrincipalVo userHolder = UserContext.getUserHolder(); + Long userId = userHolder.getId(); + if (userId != null) { + // 处理父ID逻辑 + processParentId(vo, dbItem != null ? dbItem : + poseTransformationMapper.selectOne(new QueryWrapper().eq("unique_id", taskId).eq("account_id", userId))); + + } else { + // 处理父ID逻辑 + processParentId(vo, dbItem != null ? dbItem : + poseTransformationMapper.selectList(new QueryWrapper().eq("unique_id", taskId)).get(0)); + + } + + return vo; + } + + private void processAllUrls(PoseTransformationVO vo, PoseTransformation dbItem) { + // 处理各种URL + processUrl(vo.getGifUrl(), url -> + vo.setGifUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + processUrl(vo.getVideoUrl(), url -> + vo.setVideoUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + processUrl(vo.getFirstFrameUrl(), url -> + vo.setFirstFrameUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + } + + private void processParentId(PoseTransformationVO vo, PoseTransformation poseTransformation) { + if (poseTransformation != null) { + ToProductImageResult productResult = getProductResultByPath(poseTransformation); + if (productResult != null) { + Long parentId = collectionSortService.getParentIdByElementIdAndElementType( + productResult.getId(), CollectionType.TO_PRODUCT_IMAGE.getValue()); + if (Objects.isNull(parentId)) { + parentId = toProductImageResultService.getUnlikedResultParentId(null, poseTransformation.getProductImage()); + } + vo.setParentId(parentId); + vo.setId(poseTransformation.getId()); + vo.setModelName(poseTransformation.getModelName()); + vo.setRelationType(Module.poseTransfer.getValue()); + vo.setProductImage(minioUtil.getPreSignedUrl(productResult.getUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + } + } + + private boolean isInvalidStatus(String status) { + return "Invalid".equals(status) || "Fail".equals(status); + } + + 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())) { + poseTransformation.setTaskStatus(status); + poseTransformation.setUpdateTime(LocalDateTime.now()); + poseTransformationMapper.updateById(poseTransformation); + } + } + + private ToProductImageResult getProductResultByPath(PoseTransformation poseTransformation) { + QueryWrapper qw = new QueryWrapper<>(); + //增加projectID匹配,避免因为二创导致查询到多条记录 + qw.lambda().eq(ToProductImageResult::getProjectId, poseTransformation.getProjectId()); + qw.lambda().eq(ToProductImageResult::getUrl, poseTransformation.getUniqueId()); + return toProductImageResultMapper.selectOne(qw); + } + + /*public List getPoseTransformationResultList(Long projectId, boolean like) { + List poseTransformations = poseTransformationMapper.selectList(new QueryWrapper().eq("project_id", projectId) + .eq("is_liked", like ? 1 : 0).ne("task_status", "Fail")); + List vos = new ArrayList<>(); +// if (poseTransformations != null && poseTransformations.size() > 1){ + if (!CollectionUtils.isEmpty(poseTransformations)) { + for (PoseTransformation item : poseTransformations) { + String taskId = item.getUniqueId(); + String key = generateResultKey + ":" + taskId; + String resultJson = redisUtil.getFromString(key); + + PoseTransformationVO poseTransformationVO; + + if (!StringUtil.isNullOrEmpty(resultJson)) { + // 从Redis获取并转换数据 + poseTransformationVO = new Gson().fromJson(resultJson, PoseTransformationVO.class); + poseTransformationVO.setId(item.getId()); + poseTransformationVO.setIsLiked(item.getIsLiked()); + + // 处理成功状态的数据 + if ("Success".equals(poseTransformationVO.getStatus())) { + poseTransformationVO.setProductImage( + minioUtil.getPreSignedUrl(item.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + + // 处理各种URL + processUrl(poseTransformationVO.getGifUrl(), url -> + poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + processUrl(poseTransformationVO.getVideoUrl(), url -> + poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + processUrl(poseTransformationVO.getFirstFrameUrl(), url -> + poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + } + + // 添加有效数据到结果列表 + if (!"Invalid".equals(poseTransformationVO.getStatus()) && !"Fail".equals(poseTransformationVO.getStatus())) { + vos.add(poseTransformationVO); + } + } else { + // 处理Redis中没有缓存的情况 + poseTransformationVO = CopyUtil.copyObject(item, PoseTransformationVO.class); + + poseTransformationVO.setTaskId(taskId); + poseTransformationVO.setProductImage( + minioUtil.getPreSignedUrl(item.getProductImage(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + + // todo 面对没有生成结束的情况,返回taskId + if (StringUtil.isNullOrEmpty(item.getVideoUrl())) { + vos.add(poseTransformationVO); + continue; + } + // 处理各种URL + processUrl(poseTransformationVO.getGifUrl(), url -> + poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + processUrl(poseTransformationVO.getVideoUrl(), url -> + poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + processUrl(poseTransformationVO.getFirstFrameUrl(), url -> + poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME))); + + vos.add(poseTransformationVO); + } + } + } + return vos; + }*/ + + // 辅助方法:处理URL + private void processUrl(String url, Consumer processor) { + if (!StringUtil.isNullOrEmpty(url) && !"None".equals(url)) { + processor.accept(url); + } + } + + public CollectionSort disOrLikePose(Long transformedId, String likeOrDislike, Long projectId, Long collectionSortParentId) { + PoseTransformation poseTransformation = poseTransformationMapper.selectById(transformedId); + CollectionSort collectionSort = null; + if (Objects.nonNull(poseTransformation)) { + if (likeOrDislike.equals("like")) { + poseTransformation.setIsLiked((byte) 1); + if (null != collectionSortParentId) { + collectionSort = collectionSortService.addCollectionSort(poseTransformation.getId(), CollectionType.POSE_TRANSFORM.getValue(), projectId, collectionSortParentId); + } + } else if (likeOrDislike.equals("dislike")) { + poseTransformation.setIsLiked((byte) 0); + if (null != collectionSortParentId) { + collectionSortService.deleteCollectionSort(poseTransformation.getId(), CollectionType.POSE_TRANSFORM.getValue(), projectId, collectionSortParentId); + } + } + poseTransformation.setUpdateTime(LocalDateTime.now()); + poseTransformationMapper.updateById(poseTransformation); + } + if (Objects.nonNull(collectionSort)) { + projectService.modifyProjectUpdateTime(projectId); + } + return collectionSort; + } + + public String modifyModelProportion(ModifyModelProportionDTO proportionDTO) { + log.info("modifyModelProportion params: {}", proportionDTO); + String name; + String gender; + Library model = null; + Long accountId = UserContext.getUserHolder().getId(); + String uuid = UUID.randomUUID().toString(); + // 所有修改的图片都另存为,不覆盖原图 + if (proportionDTO.getType().equals("Library")) { + model = libraryService.getById(proportionDTO.getId()); + if (Objects.isNull(model)) { + throw new BusinessException("model.not.found"); + } + String url = model.getUrl(); + name = url.substring(url.indexOf("/") + 1, url.lastIndexOf("/")) + "/" + uuid; +// gender = model.getLevel2Type(); + } else { + SysFileVO sysModel = sysFileService.getById(proportionDTO.getId()); + if (Objects.isNull(sysModel)) { + throw new BusinessException("model.not.found"); + } + gender = sysModel.getLevel2Type(); + name = accountId + "/models/" + gender.toLowerCase() + "/" + uuid; + } + // 只需要将结果存入library + String modifiedModel = pythonService.modifyModelProportion(proportionDTO.getModelPath(), proportionDTO.getStretch(), name, proportionDTO.getTop(), proportionDTO.getBottom()); +// List imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(modifiedModel); + + /*// 存储修改后的模特到个人library + model = new Library(); + model.setAccountId(accountId); + model.setLevel1Type(LibraryLevel1TypeEnum.MODELS.getRealName()); + model.setLevel2Type(gender); + model.setName(uuid); + model.setUrl(modifiedModel); + model.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(modifiedModel, 24 * 60),false)); + model.setWidth(imagesWidthAndHeight.get(0)); + model.setHigh(imagesWidthAndHeight.get(1)); + model.setCreateDate(new Date()); + libraryService.save(model);*/ + +/* // 新建模特点位信息 + LibraryModelPoint libraryModelPoint = new LibraryModelPoint(); + libraryModelPoint.setModelType("Library"); + libraryModelPoint.setRelationId(model.getId()); + libraryModelPoint.setShoulderLeft(Arrays.toString(proportionDTO.getShoulderLeft())); + libraryModelPoint.setShoulderRight(Arrays.toString(proportionDTO.getShoulderRight())); + libraryModelPoint.setWaistbandLeft(Arrays.toString(proportionDTO.getWaistbandLeft())); + libraryModelPoint.setWaistbandRight(Arrays.toString(proportionDTO.getWaistbandRight())); + libraryModelPoint.setHandLeft(Arrays.toString(proportionDTO.getHandLeft())); + libraryModelPoint.setHandRight(Arrays.toString(proportionDTO.getHandRight())); + libraryModelPoint.setCreateDate(new Date()); + libraryModelPointService.save(libraryModelPoint);*/ + return minioUtil.getPreSignedUrl(modifiedModel, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); + } + + /** + * String collagePicture(Base64) + * List elements + * File file + * + * @return + */ + @Transactional(rollbackFor = Exception.class) + public GenerateResultVO sketchReconstructionGenerate(SketchReconstructionDTO sketchReconstructionDTO) { +// log.info("sketchReconstructionGenerate params: {}", sketchReconstructionDTO); + + Long accountId = UserContext.getUserHolder().getId(); + // 1、线稿生成 + String collagePictureBase64 = sketchReconstructionDTO.getCollagePicture(); + String path = accountId + "/CollagePicture/" + UUID.randomUUID(); + String minioPath = minioUtil.base64UploadToPath(collagePictureBase64, userBucket, path); + + Long projectId = sketchReconstructionDTO.getProjectId(); + + GenerateResultVO generateResultVO = imageToSketch(new ImageToSketchDTO(null, "2", sketchReconstructionDTO.getGender()), minioPath, projectId); + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("project_id", projectId); + SketchReconstruction sketchReconstruction = sketchReconstructionMapper.selectOne(qw); + + String url = generateResultVO.getUrl(); + // 找到路径的起始位置(从"://"之后查找第一个"/") + int pathStartIndex = url.indexOf("/", url.indexOf("://") + 3); + // 找到查询参数的起始位置("?" 的位置) + int queryStartIndex = url.indexOf("?"); + // 截取目标部分 + String targetPath = url.substring(pathStartIndex + 1, queryStartIndex); + + try { + if (Objects.isNull(sketchReconstruction)) { + sketchReconstruction = new SketchReconstruction(); + sketchReconstruction.setProjectId(projectId); + sketchReconstruction.setCollageImgSketchUrl(targetPath); + sketchReconstruction.setGenerateDetailId(generateResultVO.getId()); + sketchReconstruction.setGender(sketchReconstructionDTO.getGender()); + sketchReconstruction.setCreateTime(LocalDateTime.now()); + sketchReconstructionMapper.insert(sketchReconstruction); + } else { + sketchReconstruction.setCollageImgSketchUrl(targetPath); + sketchReconstruction.setGenerateDetailId(generateResultVO.getId()); + sketchReconstructionMapper.updateById(sketchReconstruction); + } + } catch (DuplicateKeyException e) { + // 如果发生唯一键冲突,说明其他请求已经创建了记录 + // 重新查询并更新 + log.info("sketch拼贴,唯一键(project_id)冲突,改为更新"); + sketchReconstruction = sketchReconstructionMapper.selectOne(qw); + sketchReconstruction.setCollageImgSketchUrl(targetPath); + sketchReconstruction.setGenerateDetailId(generateResultVO.getId()); + sketchReconstructionMapper.updateById(sketchReconstruction); + } + + return generateResultVO; + } + + public SketchReconstructionVO getSketchReconstruction(Long projectId) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("project_id", projectId); + SketchReconstruction sketchReconstruction = sketchReconstructionMapper.selectOne(qw); + + SketchReconstructionVO vo = new SketchReconstructionVO(); + if (Objects.nonNull(sketchReconstruction) && Objects.nonNull(sketchReconstruction.getGenerateDetailId())) { + GenerateDetail generateDetail = generateDetailMapper.selectById(sketchReconstruction.getGenerateDetailId()); + vo.setCollageSketchUrl(minioUtil.getPreSignedUrl(generateDetail.getUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + vo.setLiked(generateDetail.getIsLike().equals((byte) 1)); + String clothCategory = pythonService.getClothCategory(generateDetail.getUrl(), sketchReconstruction.getGender()); + String messageFromResource = BusinessException.getMessageFromResource(clothCategory.toUpperCase()); + vo.setCategory(clothCategory); + vo.setCategoryValue(messageFromResource); + } + List collectionElements = collectionElementService.getByProjectId(projectId); + 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 { + vo.setUploadImages(new ArrayList<>()); + } + return vo; + } + + public List> getAllPose() { + List> propertyList = PoseEnum.getPropertyList(); + propertyList.forEach(item -> { +// item.put("gif", minioUtil.getPreSignedUrl(item.get("gif"), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + item.put("video", minioUtil.getPreSignedUrl(item.get("video"), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + item.put("firstFrame", minioUtil.getPreSignedUrl(item.get("firstFrame"), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + }); + return propertyList; + } + + @Override + @Transactional + public void processPoseTransformResultBatch(String taskId, String gifUrl, String videoUrl, String imageUrl, String progress) { + // 1、存储模型返回的数据 + PoseTransformation poseTransformation; + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("unique_id", taskId); + List poseTransformations = poseTransformationMapper.selectList(qw); + if (poseTransformations != null && poseTransformations.size() > 1) { + log.warn("通过taskId {} 查询到的PoseTransformation的结果不止一条", taskId); + } else if (poseTransformations == null || poseTransformations.isEmpty()) { + return; + } + poseTransformation = poseTransformations.get(0); + poseTransformation.setGifUrl(gifUrl); + poseTransformation.setVideoUrl(videoUrl); + poseTransformation.setFirstFrameUrl(imageUrl); + poseTransformation.setTaskStatus("Success"); + poseTransformation.setUpdateTime(LocalDateTime.now()); + poseTransformationMapper.updateById(poseTransformation); + + String taskIdBatch = poseTransformation.getTaskIdBatch(); + QueryWrapper cloudTaskQueryWrapper = new QueryWrapper<>(); + cloudTaskQueryWrapper.lambda().eq(CloudTask::getTaskId, taskIdBatch); + CloudTask cloudTask = cloudTaskMapper.selectOne(cloudTaskQueryWrapper); + if (Objects.nonNull(cloudTask)) { + cloudTask.setUpdateTime(LocalDateTime.now()); + cloudTaskMapper.updateById(cloudTask); + } +// if (Objects.nonNull(cloudTask)) { +// if (cloudTask.getCompletedNum() == null) { +// cloudTask.setCompletedNum(1); +// }else { +// cloudTask.setCompletedNum(cloudTask.getCompletedNum() + 1); +// } +// cloudTaskMapper.updateById(cloudTask); +// } + cloudTaskMapper.increaseCompletedNum(taskIdBatch); + + String key = generateResultKey + ":" + taskId; + PoseTransformationVO poseTransformationVO = new PoseTransformationVO( + poseTransformation.getId(), taskId, gifUrl, videoUrl, imageUrl, (byte) 0, "Success"); + + // 2、更新redis + redisUtil.addToString(key, new Gson().toJson(poseTransformationVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + /*// 3、执行积分扣除 + String accountId = taskId.substring(taskId.lastIndexOf("-") + 1); + String uuid = taskId.substring(0, taskId.lastIndexOf("-")); + Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), taskIdBatch); + if (flag) creditsService.updateChangedCredits(accountId, taskIdBatch);*/ + } + + @Transactional + public void processPoseTransformResultBatch(String progress, String taskId) { + // 1、存储模型返回的数据 + PoseTransformation poseTransformation; + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("unique_id", taskId); + List poseTransformations = poseTransformationMapper.selectList(qw); + log.info("poseTransformations : {}", poseTransformations); + if (poseTransformations != null && poseTransformations.size() > 1) { + log.warn("通过taskId {} 查询到的PoseTransformation的结果不止一条", taskId); + } else if (poseTransformations == null || poseTransformations.isEmpty()) { + return; + } + poseTransformation = poseTransformations.get(0); + String taskIdBatch = poseTransformation.getTaskIdBatch(); + log.info("progress:{}", progress); + log.info("taskIdBatch:{}", taskIdBatch); + if (progress.equals("OK")) { + if (!StringUtils.isEmpty(taskIdBatch)) { + cloudTaskService.completeTask(taskIdBatch); + } + } else if (progress.startsWith("0/")) { + if (!StringUtils.isEmpty(taskIdBatch)) { + cloudTaskService.startTask(taskIdBatch); + } + } + } + + + @Transactional(rollbackFor = Exception.class) + public void deleteGeneratedPose(Long projectId, Long id) { + // 1. 权限校验 + Long accountId = UserContext.getUserHolder().getId(); + Project project = projectService.getById(projectId); + if (!project.getAccountId().equals(accountId)) { + throw new IllegalArgumentException("项目不属于当前账号"); + } + + // 2. 软删除主表数据 + int update = poseTransformationMapper.update(null, + new UpdateWrapper() + .eq("id", id) + .set("is_deleted", 1) + .set("update_time", LocalDateTime.now())); + + log.info("删除PoseTransfer 结果, id为{}, 影响行数={}", id, update); + + if (update == 0) return; + + // 3. 查询可能删除的多个排序项(存在脏数据可能,所有会查出多个) + List deletedItems = collectionSortMapper.selectList( + new QueryWrapper() + .eq("project_id", projectId) + .eq("relation_id", id) + .eq("relation_type", "PoseTransfer") + .orderByAsc("sort") + ); + + if (deletedItems.isEmpty()) return; + + // 4. 删除这些排序记录 + int deletedCount = collectionSortMapper.delete( + new QueryWrapper() + .eq("project_id", projectId) + .eq("relation_id", id) + .eq("relation_type", "PoseTransfer") + ); + + // 5. 重新调整剩余记录的排序(两种方案可选) + // 方案一:精确调整(每条被删除记录单独处理) + for (CollectionSort deletedItem : deletedItems) { + collectionSortMapper.update( + null, + new UpdateWrapper() + .eq("project_id", projectId) + .gt("sort", deletedItem.getSort()) + .setSql("sort = sort - 1") + ); + } + + log.info("删除PoseTransfer排序记录:id={}, 删除{}条,已重新排序后续记录", id, deletedCount); + } + + + /** + * 万象专业版 + * 1、MoodBoard t2i + * 2、PrintBoard t2i + * 3、SketchBoard t2i + * 4、pose transfer 图生舞蹈视频-舞动人像AnimateAnyone + */ + + /** + * 创建异步任务 + * + * @return taskId + */ + public String createAsyncTask(GenerateThroughImageTextDTO generateDTO) { +// String prompt = "一间有着精致窗户的花店,漂亮的木质门,摆放着花朵"; + String level1Type = generateDTO.getLevel1Type(); + String level2Type = generateDTO.getLevel2Type(); + String prompt = generateDTO.getText(); + Long userId = generateDTO.getUserId(); + String gender = generateDTO.getGender(); + + // 添加预设prompt,使生成结果更加具有指向性(区分不同的board) + switch (level1Type) { + case "Moodboard": + break; + case "Printboard": + prompt = "pattern image, " + prompt; + break; + case "Sketchboard": + prompt = "a single item of sketch of " + prompt + ", clean white background, simple lines"; + break; + default: + log.warn("未知类型 type:{}", level1Type); + } + HashMap promptExtend = new HashMap<>(); + promptExtend.put("prompt_extend", false); + ImageSynthesisParam param = + ImageSynthesisParam.builder() + .apiKey(ALIYUN_API_KEY) + .model("wanx2.1-t2i-plus") + .prompt(prompt) + .n(1) +// .size("1024*1024") 采用默认值;1328*1328(默认值):1:1。 + .parameters(promptExtend) + .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 + ")", "wx", new Date()); + save(generate); + return taskId; + } + + /** + * 获取异步任务结果 + * + * @param taskId 任务id + */ + public GenerateResultVO getAsyncTaskResult(String taskId) { + ImageSynthesis imageSynthesis = new ImageSynthesis(); + ImageSynthesisResult result = null; + try { + //如果已经在环境变量中设置了 DASHSCOPE_API_KEY,wait()方法可将apiKey设置为null + result = imageSynthesis.fetch(taskId, ALIYUN_API_KEY); + log.info(JsonUtils.toJson(result)); + //PENDING:任务排队中; RUNNING:任务处理中; SUCCEEDED:任务执行成功; FAILED:任务执行失败; CANCELED:任务取消成功; UNKNOWN:任务不存在或状态未知 + String taskStatus = result.getOutput().getTaskStatus(); + + if (taskStatus.equals("SUCCEEDED")) { + List generates = selectListByUniqueId(taskId); + String url = result.getOutput().getResults().get(0).get("url"); + + 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"); + } + } else if (taskStatus.equals("PENDING") || taskStatus.equals("RUNNING")) { + log.info("阿里 异步接口返回生成状态为:{}", taskStatus); + return new GenerateResultVO(taskId, null, null, "Executing"); + } else { + log.warn("阿里 异步接口返回生成状态为:{}", taskStatus); + return new GenerateResultVO(taskId, null, null, "Fail"); + } + } catch (ApiException | NoApiKeyException e) { + throw new RuntimeException(e.getMessage()); + } catch (Exception e) { + log.error("从aliyun下载图片失败, {}", e.getMessage()); + throw new BusinessException("Generation result retrieval failed"); + } + } + + 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 if ("Fail".equals(cachedResult.getStatus())) { + // 获取失败原因 + String errorMessage = cachedResult.getUrl(); + if (errorMessage != null) { + throw new BusinessException(errorMessage, ResultEnum.PROMPT.getCode()); + } + } 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"); + } + } else if ("Fail".equals(cachedResult.getStatus())) { + // 获取失败原因 + String errorMessage = cachedResult.getUrl(); + if (errorMessage != null) { + throw new BusinessException(errorMessage, ResultEnum.PROMPT.getCode()); + } + } + + // 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/"; + private static final String ANIMATE = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis/"; + + public String animateAnyone(PoseTransformDTO poseTransformDTO, Long accountId) { + AccessLimitUtils.validate("animation", 5); + String inputImage = poseTransformDTO.getProductImage(); + String inputImageUrl = minioUtil.getPreSignedUrl(inputImage, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); + // 1、输入图片检测 + checkImage(inputImageUrl); + + // 2、动作模板生成 + String videoTemplateId = PoseEnum.getById(poseTransformDTO.getPoseId()).getTemplateId(); + if (StringUtil.isNullOrEmpty(videoTemplateId)) { + throw new BusinessException("unknown pose"); + } + // 3、生成动图 + JSONObject requestBody1 = new JSONObject(); + requestBody1.set("model", "animate-anyone-gen2"); + + JSONObject input1 = new JSONObject(); + input1.set("image_url", inputImageUrl); // 替换为实际图片URL + input1.set("template_id", videoTemplateId); // 替换为实际图片URL + JSONObject parameters1 = new JSONObject(); + parameters1.set("use_ref_img_bg", false); + parameters1.set("video_ratio", "9:16"); + + requestBody1.set("input", input1); + requestBody1.set("parameters", parameters1); + + log.info("万象 pose transfer 请求入参:{}", requestBody1); + String resp = sendRequestUtil.sendAliYunPostAsync(ANIMATE, requestBody1.toString()); +// String resp = "{\"request_id\":\"656c4339-59e5-9b34-a010-b5aa625a4008\",\"output\":{\"task_id\":\"05c0fe3e-8d93-4754-babe-28a1efc62151\",\"task_status\":\"PENDING\"}}"; + log.info("wx pose transform 请求生成,获取taskId:{}", resp); + JSONObject jsonResponse = JSONUtil.parseObj(resp); + JSONObject output = jsonResponse.getJSONObject("output"); + String status = output.getStr("task_status"); + if (status.equals(FAILED.getName()) || status.equals(UNKNOWN_W.getName())) { + return null; + } + return output.getStr("task_id"); + } + + public void checkImage(String inputImageUrl) { + JSONObject requestBody = new JSONObject(); + requestBody.set("model", "animate-anyone-detect-gen2"); + + JSONObject input = new JSONObject(); + input.set("image_url", inputImageUrl); // 替换为实际图片URL + JSONObject parameters = new JSONObject(); + + requestBody.set("input", input); + requestBody.set("parameters", parameters); + + String response = sendRequestUtil.sendAliYunPost(IMAGE_DETECT, requestBody.toString()); + + System.out.println("API响应: " + response); + JSONObject jsonResponse = JSONUtil.parseObj(response); + // 获取check_pass值 + JSONObject output = jsonResponse.getJSONObject("output"); + Boolean checkPass = output.getBool("check_pass"); + + if (!checkPass) { + String reason = output.getStr("reason"); + log.info("原因: {}", reason); + throw new BusinessException("输入的图片不满足要求"); + } + } + + // 轮询配置 + private static final int MAX_RETRIES = 30; // 最大重试次数 + private static final int POLL_INTERVAL = 20000; // 轮询间隔(毫秒) + + public String getVideoTemplateId(String videoPath) { + boolean contains = PoseEnum.getVideoList().contains(videoPath); + + String templateId; + if (!contains) { + String videoUrl = minioUtil.getPreSignedUrl(videoPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME); + JSONObject requestBody = new JSONObject(); + requestBody.set("model", "animate-anyone-template-gen2"); + JSONObject input = new JSONObject(); + input.set("video_url", videoUrl); // 替换为实际图片URL + JSONObject parameters = new JSONObject(); + requestBody.set("input", input); + requestBody.set("parameters", parameters); + + log.info("获取pose的模板id 请求数据:{}", requestBody); + String resp = sendRequestUtil.sendAliYunPostAsync(TEMPLATE_ID_GEN, requestBody.toString()); + if (StringUtil.isNullOrEmpty(resp)) { + throw new BusinessException("请求获取video template id失败"); + } + JSONObject jsonResponse = JSONUtil.parseObj(resp); + log.info("getVideoTemplateId response:{}", jsonResponse); + JSONObject output = jsonResponse.getJSONObject("output"); + String taskId = output.getStr("task_id"); + + // 暂时用while循环轮询 + templateId = pollTemplateIdResult(taskId); + if (StringUtil.isNullOrEmpty(templateId)) { + throw new BusinessException("获取动作模板失败"); + } +// templateId = "AACT.8090e67b.-E3pujumEfCbDTI_rjSH-A.LwIlGT3j"; + } else { + templateId = PoseEnum.getByVideoPath(videoPath).getTemplateId(); + } + + return templateId; + } + + public String pollTemplateIdResult(String taskId) { + int attempt = 0; + boolean isCompleted = false; + String templateId = null; + + while (attempt < MAX_RETRIES && !isCompleted) { + attempt++; + System.out.printf("尝试第 %d 次查询...%n", attempt); + + try { + // 发送GET请求查询任务状态 + HttpResponse httpResponse = HttpRequest.get(GET_ASYNC_RESULT + taskId) + .header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY) + .timeout(10000) + .execute(); + + if (httpResponse.getStatus() == 200) { + JSONObject response = JSONUtil.parseObj(httpResponse.body()); + JSONObject output = JSONUtil.parseObj(response.getStr("output")); + String taskStatus = output.getStr("task_status", "UNKNOWN"); + WangXiangTaskStatusEnum statusEnum = WangXiangTaskStatusEnum.fromName(taskStatus); + System.out.println("当前任务状态: " + taskStatus); + + switch (statusEnum) { + case SUCCEEDED: + templateId = handleSuccessResponse(response); + isCompleted = true; + break; + case FAILED: + case UNKNOWN_W: + handleFailedResponse(response); + isCompleted = true; + break; + case RUNNING: + case PENDING_W: + // 任务仍在运行,继续等待 + break; + default: + System.out.println("未知状态: " + taskStatus); + } + } else { + System.out.println("请求失败,状态码: " + httpResponse.getStatus()); + } + + // 如果不是最终状态,等待一段时间再重试 + if (!isCompleted && attempt < MAX_RETRIES) { + TimeUnit.MILLISECONDS.sleep(POLL_INTERVAL); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("轮询被中断"); + break; + } catch (Exception e) { + System.out.println("请求发生异常: " + e.getMessage()); + // 发生异常时可以选择重试或退出 + break; + } + } + if (!isCompleted) { + System.out.println("达到最大重试次数仍未获取最终结果"); + } + return templateId; + } + + private static String handleSuccessResponse(JSONObject response) { + log.info("任务执行成功!"); + // 提取成功结果 + JSONObject output = response.getJSONObject("output"); + if (output != null) { + log.info("任务输出: {}", output.toStringPretty()); + return output.getStr("template_id"); + } else { + return null; + } + } + + private static void handleFailedResponse(JSONObject response) { + log.info("任务执行失败!"); + // 提取失败原因 + String errorMsg = response.getStr("error_message", "未知错误"); + log.info("失败原因: {}", errorMsg); + } + + + public PoseTransformationVO getAnimateResult(String taskId) { + String fullUrl = GET_ASYNC_RESULT + taskId; + // 从接口获取当前任务的结果 + String respBody = sendRequestUtil.sendAliYunGet(fullUrl); + log.info("获取wx pose transform 的结果: {}", respBody); + + String outputStr = JSONUtil.parseObj(respBody).getStr("output"); + JSONObject output = JSONUtil.parseObj(outputStr); + String videoUrl = output.getStr("video_url"); + String status = output.getStr("task_status"); + + // 更新api_generate表 + apiGenerateService.updateAPIGenerateStatusAsync(taskId, status); + + List poseTransformations = poseTransformationMapper.selectList(new QueryWrapper().eq("unique_id", taskId).orderByDesc("id")); + PoseTransformation poseTransformation; + if (!poseTransformations.isEmpty()) { + poseTransformation = poseTransformations.get(0); + } else { + throw new BusinessException("unknown motion task"); + } + Long accountId = poseTransformation.getAccountId(); + PoseTransformationVO poseTransformationVO = new PoseTransformationVO(); + WangXiangTaskStatusEnum statusEnum = WangXiangTaskStatusEnum.fromName(status); + switch (statusEnum) { + case SUCCEEDED: + AccessLimitUtils.validateOut("animation"); + poseTransformationVO = CopyUtil.copyObject(poseTransformation, PoseTransformationVO.class); + poseTransformationVO.setStatus("Success"); + + // 生成视频的gif和第一帧图片并上传图片、视频、gif等数据,更新pose transformation表 + processVideo(videoUrl, poseTransformation); + poseTransformationVO.setId(poseTransformation.getId()); + if (!StringUtil.isNullOrEmpty(poseTransformation.getGifUrl())) { + poseTransformationVO.setGifUrl(minioUtil.getPreSignedUrl(poseTransformation.getGifUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + if (!StringUtil.isNullOrEmpty(poseTransformation.getVideoUrl())) { + poseTransformationVO.setVideoUrl(minioUtil.getPreSignedUrl(poseTransformation.getVideoUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + if (!StringUtil.isNullOrEmpty(poseTransformation.getFirstFrameUrl())) { + poseTransformationVO.setFirstFrameUrl(minioUtil.getPreSignedUrl(poseTransformation.getFirstFrameUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + } + // 执行积分扣除 + Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId); + if (flag) creditsService.updateChangedCredits(String.valueOf(accountId), taskId); + + // 保存数据到redis + String key = generateResultKey + ":" + taskId; + redisUtil.addToString(key, new Gson().toJson(poseTransformationVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + break; + case FAILED: + AccessLimitUtils.validateOut("animation"); + updatePoseTransferStatus(taskId, "Fail", poseTransformation); + // 将积分暂扣区的积分移除 + creditsService.deleteCreditsDeduction(accountId, taskId); + throw new BusinessException(output.getStr("message"), ResultEnum.PROMPT.getCode()); + case UNKNOWN_W: + AccessLimitUtils.validateOut("animation"); + poseTransformationVO.setStatus("Fail"); + updatePoseTransferStatus(taskId, "Fail", poseTransformation); + // 将积分暂扣区的积分移除 + creditsService.deleteCreditsDeduction(accountId, taskId); + break; + case RUNNING: + case PENDING_W: + // 任务仍在运行,继续等待 + poseTransformationVO.setStatus("Executing"); + break; + default: + AccessLimitUtils.validateOut("animation"); + log.info("未知状态: {}", status); + poseTransformationVO.setStatus("Fail"); + updatePoseTransferStatus(taskId, "Fail", poseTransformation); + // 将积分暂扣区的积分移除 + creditsService.deleteCreditsDeduction(accountId, taskId); + } + poseTransformationVO.setTaskId(taskId); + + return poseTransformationVO; + } + + public void processVideo(String aliyunVideoUrl, PoseTransformation poseTransformation) /*throws Exception*/ { + // 1. 从阿里云下载视频到内存 + byte[] videoBytes = downloadVideoOrImage(aliyunVideoUrl); + Long accountId = poseTransformation.getAccountId(); + String taskId = poseTransformation.getUniqueId(); + + // 2. 提取第一帧和生成GIF + ByteArrayOutputStream firstFrameOutput = new ByteArrayOutputStream(); + ByteArrayOutputStream gifOutput = new ByteArrayOutputStream(); + + try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(videoBytes))) { + grabber.start(); + // 提取第一帧 + BufferedImage firstFrame = new Java2DFrameConverter().convert(grabber.grabImage()); + ImageIO.write(firstFrame, "jpg", firstFrameOutput); + + // 生成GIF(取前12秒,50帧) + generateGif(grabber, gifOutput, 12, 60); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // 3. 上传所有文件到MinIO + String videoPrefix = accountId + "/pose_transform_video/" + taskId + ".mp4"; + String imgPrefix = accountId + "/pose_transform_first_img/" + taskId + ".jpg"; + String gifPrefix = accountId + "/pose_transform_gif/" + taskId + ".gif"; + + minioUtil.uploadToMinio(videoBytes, userBucket, videoPrefix, "video/mp4"); + minioUtil.uploadToMinio(firstFrameOutput.toByteArray(), userBucket, imgPrefix, "image/jpeg"); + minioUtil.uploadToMinio(gifOutput.toByteArray(), userBucket, gifPrefix, "image/gif"); + // 存储数据到数据库 + poseTransformation.setGifUrl(userBucket + "/" + gifPrefix); + poseTransformation.setVideoUrl(userBucket + "/" + videoPrefix); + poseTransformation.setFirstFrameUrl(userBucket + "/" + imgPrefix); + poseTransformation.setTaskStatus("Success"); + poseTransformation.setUpdateTime(LocalDateTime.now()); + poseTransformationMapper.updateById(poseTransformation); + } + + public byte[] downloadVideoOrImage(String url) { + try (CloseableHttpClient client = HttpClients.createDefault(); + InputStream in = client.execute(new HttpGet(url)).getEntity().getContent()) { + return IOUtils.toByteArray(in); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // 增强版下载方法 todo 最好不要报错 + private byte[] downloadVideoOrImageWithValidation(String url) throws IOException { + CloseableHttpClient client = HttpClients.createDefault(); + HttpGet request = new HttpGet(url); + + try (CloseableHttpResponse response = client.execute(request)) { + // 状态码检查 + if (response.getStatusLine().getStatusCode() != 200) { + throw new IOException("Invalid status: " + response.getStatusLine()); + } + + // 内容类型检查 + org.apache.http.Header contentTypeHeader = response.getFirstHeader("Content-Type"); + if (contentTypeHeader == null || !contentTypeHeader.getValue().startsWith("image/")) { + throw new IOException("Invalid content type: " + + (contentTypeHeader != null ? contentTypeHeader.getValue() : "null")); + } + + // 内容长度检查 + org.apache.http.Header contentLengthHeader = response.getFirstHeader("Content-Length"); + if (contentLengthHeader != null) { + long length = Long.parseLong(contentLengthHeader.getValue()); + if (length <= 0) { + throw new IOException("Empty content"); + } + } + + return IOUtils.toByteArray(response.getEntity().getContent()); + } + } + + public byte[] downloadWithProxy(String url) throws IOException { + // 获取系统代理设置(适用于大多数VPN) +// String proxyHost = System.getProperty("http.proxyHost"); +// String proxyPort = System.getProperty("http.proxyPort"); + String proxyHost = "localhost"; + String proxyPort = "7890"; + + CloseableHttpClient client; + if (proxyHost != null && proxyPort != null) { + // 配置代理 + HttpHost proxy = new HttpHost(proxyHost, Integer.parseInt(proxyPort)); + RequestConfig config = RequestConfig.custom().setProxy(proxy).build(); + client = HttpClients.custom().setDefaultRequestConfig(config).build(); + } else { + client = HttpClients.createDefault(); + } + + try { + return client.execute(new HttpGet(url), response -> { + if (response.getStatusLine().getStatusCode() == 200) { + return IOUtils.toByteArray(response.getEntity().getContent()); + } else { + throw new IOException("HTTP Error: " + response.getStatusLine()); + } + }); + } finally { + client.close(); + } + } + + public static void generateGif(FFmpegFrameGrabber grabber, OutputStream output, + int durationSec, int frameCount) throws Exception { + Java2DFrameConverter converter = new Java2DFrameConverter(); + AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder(); + + // 配置GIF参数 + gifEncoder.start(output); + gifEncoder.setDelay(100); // 每帧延迟(毫秒) + gifEncoder.setRepeat(0); // 0=无限循环 + + int totalFrames = (int) (grabber.getFrameRate() * durationSec); + int step = Math.max(1, totalFrames / frameCount); + + // 逐帧处理 + for (int i = 0; i < totalFrames; i += step) { + grabber.setVideoFrameNumber(i); + BufferedImage frame = converter.convert(grabber.grabImage()); + if (frame != null) { + gifEncoder.addFrame(frame); + } + } + gifEncoder.finish(); + } + + /** + * Freepik + * To Product Image + */ + public String reimagineFreePik(String path, String prompt, String style) throws IOException { + String imageAsBase64 = minioUtil.getImageAsBase64(path); + log.info(minioUtil.getPreSignedUrl(path, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + JSONObject requestBody = new JSONObject(); + requestBody.set("image", imageAsBase64); + requestBody.set("prompt", prompt); + requestBody.set("imagination", style); + + String resp = sendRequestUtil.sendFreepikPost(requestBody.toString()); + if (!StringUtil.isNullOrEmpty(resp)) { + JSONObject jsonResp = JSONUtil.parseObj(resp); + JSONObject data = JSONUtil.parseObj(jsonResp.get("data")); + String status = data.getStr("status"); + if (status.equals("COMPLETED")) { +// List generated = data.getBeanList("generated", String.class); + log.info("freepik 调用结果:{}", jsonResp); + return jsonResp.getStr("data"); + } + } + return null; + } + + /** + * imagePath 图片的minio地址 + * ollama + * prompt 助手 + */ + public String getImageDescription(String imagePath) { +/* // 1. 读取图片并编码为 Base64 + String imageAsBase64 = null; + try { + imageAsBase64 = minioUtil.getImageAsBase64(imagePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // 2. 构建 JSON 请求体 + JSONObject message = new JSONObject(); + message.set("role", "user"); + message.set("content", "Please describe the clothing in the image and provide a line art description of the outfit. The description should allow for the reconstruction of the corresponding line art based on the details given."); + message.set("images", JSONUtil.createArray().set(imageAsBase64)); + + JSONObject requestBody = new JSONObject(); + requestBody.set("model", "llama3.2-vision"); + requestBody.set("messages", JSONUtil.createArray().set(message)); + requestBody.set("stream", false);*/ + +// log.info("request body:{}", requestBody); + JSONObject requestBody = new JSONObject(); + requestBody.set("img", imagePath); +// String description = sendRequestUtil.sendPost("http://localhost:8000/api/img2prompt", requestBody.toString()); + String description = sendRequestUtil.sendPost("http://18.167.251.121:9994/api/img2prompt", requestBody.toString()); + if (StringUtil.isNullOrEmpty(description)) { + throw new BusinessException("从ollama获取图片描述失败"); + } + /*Object msg = JSONUtil.parseObj(resp).get("message"); + String description = JSONUtil.parseObj(msg).getStr("content");*/ + log.info("image :{} \n, description: {}", + minioUtil.getPreSignedUrl(imagePath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), description); + return description; + } + + private String resolveModelType(String taskId, String func) { + // 判断当前task来自哪个模型 + if (!StringUtil.isNullOrEmpty(func) + && func.equals(CreditsEventsEnum.POSE_TRANSFORMATION.getValue())) { + List poseTransformations = poseTransformationMapper.selectList( + new QueryWrapper().eq("unique_id", taskId)); + if (!poseTransformations.isEmpty() + && !StringUtil.isNullOrEmpty(poseTransformations.get(0).getModelName()) + && poseTransformations.get(0).getModelName().equals("wx")) { + return "wx"; + } else { + return "local"; + } + } + + Generate generate = selectByUniqueId(taskId); + if (Objects.nonNull(generate) && + !StringUtil.isNullOrEmpty(generate.getModelName()) && + (generate.getModelName().equals("wx") + || generate.getModelName().equals("freepik") + || 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"; + } + } + + public static String extractGender(String text) { + // 匹配末尾的 (Male) 或 (Female),忽略大小写 + Pattern pattern = Pattern.compile("\\(([Mm]ale|[Ff]emale)\\)$"); + Matcher matcher = pattern.matcher(text); + + if (matcher.find()) { + return matcher.group(1); // 返回括号内的内容 + } + return null; // 未匹配到性别 + } + + /** + * 接入flux模型,用于imageToSketch(sketch extract) || relighting || to product image + * + * @param func 功能枚举名,指定使用flux模型的具体功能类型 + * @param prompt 用户输入的提示词,如果为空则使用默认提示词 + * @param imagePath 图片minio路径,作为输入图像的base64编码源 + * @param childStyle 是否为儿童风格,影响提示词的构建 + * @return 返回taskId,用于异步获取结果 + */ + public String flux(CreditsEventsEnum func, String prompt, String imagePath, boolean childStyle) { + // Flux API的请求地址 + String fluxRequestUrl = "https://api.bfl.ai/v1/flux-kontext-pro"; + + // 如果用户没有提供提示词,根据功能类型设置默认提示词 + if (StringUtil.isNullOrEmpty(prompt)) { + switch (func) { + case RELIGHT_FLUX: + // 重新打光功能的默认提示词 + prompt = "a model standing on the beautiful beach, ultra high quality, 8k"; + break; + case IMAGE_TO_SKETCH_FLUX: + // 图片转线稿功能的默认提示词 + prompt = "generate the sketch of the image, simple line, ultra high quality"; + break; + case TO_PRODUCT_IMAGE_ADVANCED: + // 转产品图功能的默认提示词 + prompt = "change the image to real style, ultra high quality, 8k"; + // 如果是儿童风格,添加儿童面部特征描述 + if (childStyle) prompt = prompt + ", Children's face"; + break; + } + } else { + // 如果用户提供了提示词,先进行提示词优化处理 + prompt = modifyPrompt(prompt, null, func.getName(), null); + // 根据不同功能类型,为提示词添加特定前缀 + switch (func) { + case PATTERN: + // 图案生成功能,添加图案前缀 + prompt = "pattern image, " + prompt; + break; + case SKETCH_BOARD: + // 线稿板功能,添加线稿描述 + prompt = "a single item of sketch of " + prompt + ", clean white background, simple lines"; + break; + } + } + + // 构建Flux API请求体 + JSONObject requestBody = new JSONObject(); + + // 设置生成提示词 + requestBody.set("prompt", prompt); + // 设置随机种子,确保结果的可重现性 + requestBody.set("seed", 42); + // 根据功能类型设置图片宽高比 + if (func.equals(PATTERN)) { + // 图案生成使用正方形比例 + requestBody.set("aspect_ratio", "1:1"); + } else { + // 其他功能使用竖屏比例 + requestBody.set("aspect_ratio", "9:16"); + } + // 设置输出格式为PNG + requestBody.set("output_format", "png"); + + log.info("flux 请求入参:{}", requestBody); + // 提示词不能为空的校验 + if (prompt.isEmpty()) throw new BusinessException("test"); + + // 如果提供了输入图片路径,需要将图片转换为base64格式 + if (!StringUtil.isNullOrEmpty(imagePath)) { + try { + String imageAsBase64 = null; + // 对于转产品图功能,先添加白色背景处理 + if (func.equals(TO_PRODUCT_IMAGE_ADVANCED)) { + imageAsBase64 = addWhiteBackground(imagePath); + } + // 如果白色背景处理失败或不需要,直接获取原图的base64编码 + if (StringUtil.isNullOrEmpty(imageAsBase64)) { + imageAsBase64 = minioUtil.getImageAsBase64(imagePath); + } + // 将base64编码的图片添加到请求体中 + 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."); + } + } + + // 发送POST请求到Flux API + String resp = sendRequestUtil.sendFluxPost(fluxRequestUrl, requestBody.toString()); + JSONObject respObj = JSONUtil.parseObj(resp); + log.info("flux 发起生成请求返回结果: {}", respObj); + + // 从响应中提取任务ID + String taskId = respObj.getStr("id"); + if (StringUtil.isNullOrEmpty(taskId)) { + // 任务创建失败,记录错误信息并抛出异常 + requestBody.set("input_image", imagePath); + log.error("flux生成任务创建失败,func :{}, requestBody:{}", func.getName(), requestBody); + throw new BusinessException("Failed to generate task. Please retry later."); + } + + // 获取轮询URL,用于后续查询任务状态 + String pollingUrl = respObj.getStr("polling_url"); + String key = RedisUtil.FLUX_POLLING_URL + taskId; + // 将轮询URL存储到Redis中,设置过期时间 + redisUtil.addToString(key, pollingUrl, CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + + // 添加到api_generate表中,以便之后对结果查询做补偿机制 + apiGenerateService.addAPIGenerateRecordAsync(UserContext.getUserHolder().getId(), taskId, func.getName(), "flux", "Pending"); + + // 返回任务ID,用于异步查询结果 + return taskId; + } + + @Override + public String getFluxResult(String taskId, String objectName) { + // 获取轮询URL + String pollingUrl = redisUtil.getFromString(RedisUtil.FLUX_POLLING_URL + taskId); + + // 准备请求参数 + String fluxResultRequestUrl = StringUtil.isNullOrEmpty(pollingUrl) + ? "https://api.bfl.ai/v1/get_result" + : pollingUrl; + + HashMap params = new HashMap<>(); + if (StringUtil.isNullOrEmpty(pollingUrl)) { + params.put("id", taskId); + } + + // 发送请求并解析响应 + String resp = sendRequestUtil.sendGet(fluxResultRequestUrl, params); + log.info("获取flux生成的结果为:{}", resp); + + JSONObject respObj = JSONUtil.parseObj(resp); + String status = respObj.getStr("status"); + FluxTaskStatusEnum statusEnum = FluxTaskStatusEnum.fromName(status); + // 异步更新状态 + apiGenerateService.updateAPIGenerateStatusAsync(taskId, status); + + // 处理不同状态 + switch (statusEnum) { + case TASK_NOT_FOUND: + // 审核没过 + case REQUEST_MODERATED: + // 审核没过 + case CONTENT_MODERATED: + // 出错 + case ERROR: + return "Fail"; + case PENDING_F: + return "Pending"; + case SUCCESS: + // 已完成 获取结果 + return handleReadyStatus(respObj, objectName); + default: + return null; + } + } + + private String handleReadyStatus(JSONObject respObj, String objectName) { + // 1. 首先检查MinIO中是否已存在该图片 + if (minioUtil.doesObjectExist(userBucket, objectName)) { + return userBucket + "/" + objectName; + } + + // 2. 解析响应获取结果URL和生成时间 + JSONObject resultObj = JSONUtil.parseObj(respObj.getStr("result")); + String fluxResult = resultObj.getStr("sample"); + double endTime = resultObj.getDouble("end_time"); // 获取任务结束时间戳 + + // 3. 检查图片链接是否已过期(超过10分钟) + long currentTime = System.currentTimeMillis() / 1000; // 当前Unix时间戳(秒) + long generateTime = (long) endTime; // 生成结束时间戳 + // 图片10分钟过期,保险起见,保留一分钟 + long tenMinutesInSeconds = 9 * 60; + + if (currentTime - generateTime > tenMinutesInSeconds) { + log.warn("Flux result image has expired, generateTime: {}, currentTime: {}", + generateTime, currentTime); + return null; + } + + // 4. 图片未过期,下载并上传到MinIO + try { + byte[] bytes = downloadVideoOrImage(fluxResult); + minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png"); + return userBucket + "/" + objectName; + } catch (Exception e) { + log.error("Failed to download or upload Flux result image", e); + return null; + } + } + + private GenerateResultVO getFluxResultAndSave(String taskId) { + Generate generate = selectByUniqueId(taskId); + if (Objects.nonNull(generate)) { + GenerateDetail generateDetail = generateDetailMapper.selectOne(new QueryWrapper().eq("generate_id", generate.getId())); + Long accountId = generate.getAccountId(); + String objectName = accountId + "/imageToSketch/" + taskId + ".png"; + String fluxResult = getFluxResult(taskId, objectName); + if (Objects.isNull(generateDetail)) { + if (StringUtil.isNullOrEmpty(fluxResult)) { + // 将积分暂扣区的积分移除 + creditsService.deleteCreditsDeduction(accountId, taskId); + return new GenerateResultVO(taskId, "Fail"); + } else if (fluxResult.equals("Fail") || fluxResult.equals("Pending")) { + String status = fluxResult.equals("Fail") ? "Fail" : "Executing"; + if (status.equals("Fail")) { + creditsService.deleteCreditsDeduction(accountId, taskId); + } + return new GenerateResultVO(taskId, status); + } + generateDetail = new GenerateDetail(generate.getId(), fluxResult, + MD5Utils.encryptFile( + minioUtil.getPreSignedUrl(fluxResult, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false), + LocalDateTime.now()); + generateDetailMapper.insert(generateDetail); + // 扣积分 + Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId); + if (flag) creditsService.updateChangedCredits(String.valueOf(accountId), taskId); + } else if (StringUtil.isNullOrEmpty(generateDetail.getUrl())) { + // 结果已经存入db,一般走不到这条线 + generateDetail.setGenerateId(generate.getId()); + generateDetail.setUrl(fluxResult); + generateDetail.setMd5(MD5Utils.encryptFile( + minioUtil.getPreSignedUrl(fluxResult, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false)); + generateDetail.setUpdateDate(new Date()); + generateDetailMapper.updateById(generateDetail); + } + String url = generateDetail.getUrl(); + String category; + if (generate.getLevel1Type().equals(SKETCH_BOARD.getRealName())) { + category = pythonService.getClothCategory(url, extractGender(generate.getGenerateType())); + } else { + category = generate.getLevel2Type(); + } + + return new GenerateResultVO(taskId, generateDetail.getId(), + minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), "Success", category); + } else { + throw new BusinessException("unknown generate"); + } + } + + private String addWhiteBackground(String minioPath) { + // 1、先通过后缀判断输入图片类型有没有透明通道 + String extension = minioPath.substring(minioPath.lastIndexOf(".") + 1); + + // 2、如果有,为其添加白色背景 + if (extension.equals("png")) { + return minioUtil.changeToWhiteBackground(minioPath); + } else { + log.info("图片 {} 没有透明通道, 不用添加白底", minioPath); + return null; + } + } + + public com.alibaba.fastjson.JSONObject createParamsForMotion(PoseTransformDTO poseTransformDTO, String taskId) { + + com.alibaba.fastjson.JSONObject params = new com.alibaba.fastjson.JSONObject(); + params.put("tasks_id", taskId); + // 判断当前使用的哪种生成模式 + MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode()); + switch (motionModeEnum) { + case POSE_TO_VIDEO: + params.put("pose_id", poseTransformDTO.getPoseId()); + params.put("image_url", poseTransformDTO.getProductImage()); + break; + case PROMPT_TO_VIDEO: + params.put("image_url", poseTransformDTO.getProductImage()); + params.put("prompt", poseTransformDTO.getPrompt()); + break; + case FIRST_LAST_FRAME_TO_VIDEO: + params.put("start_image_url", poseTransformDTO.getProductImage()); + params.put("end_image_url", poseTransformDTO.getLastFrameProductImage()); + params.put("prompt", poseTransformDTO.getPrompt()); + break; + default: + throw new BusinessException("unknown.mode"); + } + params.put("api", motionModeEnum.getUrl()); + return params; + } + + /** + * 根据豆包错误码和详细信息返回用户友好的错误信息 + * + * @param errorCode 错误码 + * @param detailMessage 详细错误信息 + * @return 用户友好的错误信息 + */ + private String getDoubaoErrorMessage(String errorCode, String detailMessage, String userLanguage) { + boolean isEnglish = "ENGLISH".equals(userLanguage); + + if (errorCode == null) { + return isEnglish ? "Image generation failed, please try again later" : "图像生成失败,请稍后重试"; + } + + switch (errorCode) { + // === 用户输入问题(用户可以自行解决) === + + // 敏感内容检测 - 用户需要修改提示词 + case "SensitiveContentDetected": + case "SensitiveContentDetected.SevereViolation": + case "SensitiveContentDetected.Violence": + case "InputTextSensitiveContentDetected": + return isEnglish ? "Your prompt contains sensitive content. Please modify your description and try again." + : "您的提示词包含敏感内容,请修改描述后重试"; + case "InputImageSensitiveContentDetected": + return isEnglish ? "Your uploaded image contains sensitive content. Please use a different image and try again." + : "您上传的图片包含敏感内容,请更换图片后重试"; + case "InputVideoSensitiveContentDetected": + return isEnglish ? "Your uploaded video contains sensitive content. Please use a different video and try again." + : "您上传的视频包含敏感内容,请更换视频后重试"; + case "OutputTextSensitiveContentDetected": + case "OutputImageSensitiveContentDetected": + case "OutputVideoSensitiveContentDetected": + return isEnglish ? "The generated content may contain sensitive information. Please modify your prompt and try again." + : "生成的内容可能包含敏感信息,请修改提示词后重试"; + + // 图片格式问题 - 用户需要检查图片 + case "InvalidImageURL.EmptyURL": + return isEnglish ? "No image was uploaded. Please upload an image and try again." + : "未上传图片,请上传图片后重试"; + case "InvalidImageURL.InvalidFormat": + return isEnglish ? "The image format is not supported or the image is corrupted. Please upload a valid image (JPG, PNG, etc.) and try again." + : "图片格式不支持或图片已损坏,请上传有效的图片格式(JPG、PNG等)后重试"; + + // 内容长度问题 - 用户需要缩短内容 + case "OutofContextError": + return isEnglish ? "Your prompt and image content is too long. Please shorten your description or use a smaller image and try again." + : "您的提示词和图片内容过长,请缩短描述或使用更小的图片后重试"; + + // 请求频率问题 - 用户需要等待 + case "TooManyRequests": + return isEnglish ? "You are sending requests too frequently. Please wait a moment and try again." + : "您的请求过于频繁,请稍等片刻后重试"; + + // === 系统问题(需要联系管理员) === + + // 参数格式错误 - 系统问题 + case "MissingParameter": + case "InvalidParameter": + case "Duplicate.Tags.Key": + case "InvalidArgumentError": + case "InvalidArgumentError.UnknownRole": + case "InvalidArgumentError.InvalidImageDetail": + case "InvalidArgumentError.InvalidPixelLimit": + return isEnglish ? "System error: Invalid request parameters. Please contact the administrator." + : "系统错误:请求参数格式有误,请联系管理员"; + + // 认证问题 - 系统问题 + case "AuthenticationError": + return isEnglish ? "System error: Authentication failed. Please contact the administrator." + : "系统错误:身份验证失败,请联系管理员"; + + // 服务配置问题 - 系统问题 + case "InvalidEndpoint.ClosedEndpoint": + case "OperationDenied.InvalidState": + case "OperationDenied.ConflictedValidationSet": + case "NotFound.Model": + case "NotFound.Endpoint": + case "NotFound.Context": + case "InvalidOperation.Conflict": + case "ModelNotOpen": + return isEnglish ? "System error: Service configuration issue. Please contact the administrator." + : "系统错误:服务配置问题,请联系管理员"; + + // 配额/费用问题 - 系统问题 + case "QuotaExceeded.DailyQuota": + case "QuotaExceeded.MonthlyQuota": + case "QuotaExceeded.TotalQuota": + case "QuotaExceeded.RPM": + case "QuotaExceeded.TPM": + case "QuotaExceeded.RPD": + case "QuotaExceeded.TPD": + return isEnglish ? "System error: Service quota exceeded. Please contact the administrator." + : "系统错误:服务配额已用完,请联系管理员"; + + // 服务器错误 - 系统问题 + case "InternalError": + case "ServiceUnavailable": + return isEnglish ? "System error: Service is temporarily unavailable. Please contact the administrator or try again later." + : "系统错误:服务暂时不可用,请联系管理员或稍后重试"; + + default: + // 根据详细信息判断是用户问题还是系统问题 + if (detailMessage != null) { + if (detailMessage.contains("sensitive") || detailMessage.contains("敏感")) { + return isEnglish ? "Your content contains sensitive information. Please modify and try again." + : "您的内容包含敏感信息,请修改后重试"; + } else if (detailMessage.contains("quota") || detailMessage.contains("配额") || + detailMessage.contains("parameter") || detailMessage.contains("参数") || + detailMessage.contains("auth") || detailMessage.contains("认证")) { + return isEnglish ? "System error occurred. Please contact the administrator." + : "系统错误,请联系管理员"; + } + } + return isEnglish ? "Image generation failed. Please try again later or contact the administrator if the problem persists." + : "图像生成失败,请稍后重试,如问题持续请联系管理员"; + } + } +} diff --git a/src/main/java/com/ai/da/service/impl/LibraryModelPointServiceImpl.java b/src/main/java/com/ai/da/service/impl/LibraryModelPointServiceImpl.java index 56dfa48f..2966be9e 100644 --- a/src/main/java/com/ai/da/service/impl/LibraryModelPointServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/LibraryModelPointServiceImpl.java @@ -17,21 +17,20 @@ import com.ai.da.model.enums.Sex; import com.ai.da.model.vo.LibraryModelPointVO; import com.ai.da.python.PythonService; import com.ai.da.python.vo.DesignPythonObjects; -import com.ai.da.service.DesignService; import com.ai.da.service.LibraryModelPointService; import com.ai.da.service.LibraryService; +import com.ai.da.service.PythonTAllInfoService; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import javax.annotation.Resource; import java.util.Collections; import java.util.Date; import java.util.List; @@ -45,17 +44,13 @@ import java.util.Objects; */ @Slf4j @Service +@RequiredArgsConstructor public class LibraryModelPointServiceImpl extends ServiceImpl implements LibraryModelPointService { - @Resource - private LibraryModelPointMapper libraryModelPointMapper; - @Resource - private LibraryService libraryService; - @Resource - private PythonService pythonService; - @Resource - private DesignService designService; - @Autowired - private MinioUtil minioUtil; + private final LibraryModelPointMapper libraryModelPointMapper; + private final LibraryService libraryService; + private final MinioUtil minioUtil; + private final PythonService pythonService; + private final PythonTAllInfoService pythonTAllInfoService; @Override public LibraryModelPointVO saveOrEditTemplatePoint(LibraryModelPointDTO libraryModelPointDTO) { @@ -63,7 +58,7 @@ public class LibraryModelPointServiceImpl extends ServiceImpl imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url); @@ -84,7 +79,7 @@ public class LibraryModelPointServiceImpl extends ServiceImpl implements MessageCenterService { - @Resource - private NotificationConnection notificationConnection; - @Resource - private SysNotificationReadStatusMapper sysNotificationReadStatusMapper; - @Resource - private AccountService accountService; - @Resource - private MinioUtil minioUtil; - @Resource - private PortfolioService portfolioService; - @Resource - private CanvasMapper canvasMapper; - @Resource - private RedisUtil redisUtil; + private final AccountService accountService; + private final CanvasMapper canvasMapper; + private final MinioUtil minioUtil; + private final NotificationConnection notificationConnection; + private final PortfolioMapper portfolioMapper; + private final RedisUtil redisUtil; + private final SysNotificationReadStatusMapper sysNotificationReadStatusMapper; + private final UserFollowService userFollowService; + @Value("${redis.key.newPosted}") private String lastViewNewPostedTimeKey; @@ -84,12 +74,12 @@ public class MessageCenterServiceImpl extends ServiceImpl getHistoryNotification(GetNotificationDTO getNotificationDTO) { log.info("获取历史消息:parameter => {}", getNotificationDTO); - if (StringUtil.isNullOrEmpty(getNotificationDTO.getType())){ + if (StringUtil.isNullOrEmpty(getNotificationDTO.getType())) { throw new BusinessException("type.cannot.be.empty"); } Long accountId = UserContext.getUserHolder().getId(); // 查动态 - if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")){ + if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")) { return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize()); } @@ -99,7 +89,7 @@ public class MessageCenterServiceImpl extends ServiceImpl wrapper .isNull(Notification::getReceiverId) @@ -116,35 +106,35 @@ public class MessageCenterServiceImpl extends ServiceImpl convert = notificationPage.convert(o -> { NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class); Account senderAccount = accountService.getById(notificationVO.getSenderId()); - if (Objects.nonNull(senderAccount)){ + if (Objects.nonNull(senderAccount)) { notificationVO.setUserName(senderAccount.getUserName()); - }else { + } else { notificationVO.setUserName("--"); } // notificationVO.setSenderUserAvatar(StringUtils.isNullOrEmpty(senderAccount.getAvatar()) ? null : minioUtil.getPreSignedUrl(senderAccount.getAvatar(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - if (Objects.isNull(notificationVO.getPortfolioId())){ + if (Objects.isNull(notificationVO.getPortfolioId())) { notificationVO.setPortfolioId(null); - }else { - Portfolio byId = portfolioService.getById(notificationVO.getPortfolioId()); - if (!Objects.isNull(byId)){ - if (Objects.isNull(byId.getPortfolioName())){ + } else { + Portfolio byId = portfolioMapper.selectById(notificationVO.getPortfolioId()); + if (!Objects.isNull(byId)) { + if (Objects.isNull(byId.getPortfolioName())) { notificationVO.setPortfolioName(null); - }else { + } else { notificationVO.setPortfolioName(byId.getPortfolioName()); } - }else { + } else { String name = UserContext.getUserHolder().getLanguage().equals("ENGLISH") ? CommonConstant.PORTFOLIO_DELETED_EN : CommonConstant.PORTFOLIO_DELETED_CN; notificationVO.setPortfolioName(name); } } // 设置单个人 系统消息是否已读 - if (notificationVO.getType().equals("system")){ - if (unreadSysNotificationIds.contains(notificationVO.getId())){ + if (notificationVO.getType().equals("system")) { + if (unreadSysNotificationIds.contains(notificationVO.getId())) { notificationVO.setIsRead(0); - }else { + } else { notificationVO.setIsRead(1); } - }else { + } else { String avatar = Objects.isNull(senderAccount) || StringUtil.isNullOrEmpty(senderAccount.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : senderAccount.getAvatar(); notificationVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); } @@ -153,9 +143,9 @@ public class MessageCenterServiceImpl extends ServiceImpl followee = new HashMap<>(); HashMap follower = new HashMap<>(); - follower.put("followerCount",portfolioService.getFollowerCount(receiverId)); - followee.put("followeeCount",portfolioService.getFolloweeCount(receiverId)); + follower.put("followerCount", userFollowService.getFollowerCount(receiverId)); + followee.put("followeeCount", userFollowService.getFolloweeCount(receiverId)); resp.add(followee); resp.add(follower); } @@ -229,7 +220,7 @@ public class MessageCenterServiceImpl extends ServiceImpl unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId); @@ -322,44 +313,44 @@ public class MessageCenterServiceImpl extends ServiceImpl followeeList = portfolioService.getFolloweeList(accountId); + List followeeList = userFollowService.getFolloweeList(accountId); // 1.2 查询我关注的用户在我上次查看动态之后发布的作品数量 String lastViewTime = redisUtil.getFromString(lastViewNewPostedTimeKey + ":" + accountId); QueryWrapper queryWrapper = new QueryWrapper<>(); - if (!followeeList.isEmpty()){ + if (!followeeList.isEmpty()) { queryWrapper.in("account_id", followeeList); - }else { + } else { return 0L; } - if (!StringUtil.isNullOrEmpty(lastViewTime)){ + if (!StringUtil.isNullOrEmpty(lastViewTime)) { queryWrapper.gt("create_date", lastViewTime); - }else { + } else { return 0L; } - return portfolioService.getBaseMapper().selectCount(queryWrapper); + return portfolioMapper.selectCount(queryWrapper); } /** * 获取关注用户发布的新作品 分页查询 */ - public PageBaseResponse getNewPosted(Long accountId, Integer page, Integer size){ + public PageBaseResponse getNewPosted(Long accountId, Integer page, Integer size) { // 1、获取关注用户发布的所有作品 // 1.1 获取我关注的所有用户 - List followeeList = portfolioService.getFolloweeList(accountId); + List followeeList = userFollowService.getFolloweeList(accountId); // 1.2 分页查询我关注的用户发布的作品 QueryWrapper queryWrapper = new QueryWrapper<>(); - if (!followeeList.isEmpty()){ + if (!followeeList.isEmpty()) { queryWrapper.in("account_id", followeeList); - }else { + } else { return new PageBaseResponse<>(new ArrayList<>(), page, size, 0, 0); } queryWrapper.orderByDesc("create_date"); - Page portfolioPage = portfolioService.getBaseMapper().selectPage(new Page<>(page, size), queryWrapper); + Page portfolioPage = portfolioMapper.selectPage(new Page<>(page, size), queryWrapper); // 2、组装返回的数据 IPage convert = portfolioPage.convert(o -> { @@ -404,7 +395,7 @@ public class MessageCenterServiceImpl extends ServiceImpl implements MoodboardPositionService { + + @Override + public void parseMoodboardPosition(String moodboardPosition, Long collectionIdParam) { + if (!StringUtils.isEmpty(moodboardPosition)) { + // 将 JSON 字符串解析为 JSONObject + JSONObject moodboardPositionJson = JSONObject.parseObject(moodboardPosition); + + // 准备保存的 MoodboardPosition 列表 + List moodboardPositions = new ArrayList<>(); + + // 遍历 JSON 对象的 key(即样式类型) + for (String key : moodboardPositionJson.keySet()) { + // 特殊处理 "class" 字段 + if ("class".equals(key)) { + // 获取 "class" 字段的值并将其转为 List + JSONArray classArray = moodboardPositionJson.getJSONArray(key); + if (classArray != null) { + + for (int j = 0; j < classArray.size(); j++) { + // 将 classList 存入 MoodboardPosition(或者其他结构) + + MoodboardPosition position = new MoodboardPosition() + .setCollectionId(collectionIdParam) // 关联 Collection ID + .setType(key) // 样式类型 + .setStyleData(classArray.getString(j)) // 设置 class 字段 + .setSequence(j) // 根据索引值设置顺序 + .setCreateTime(LocalDateTime.now()) // 创建时间 + .setUpdateTime(LocalDateTime.now()); // 更新时间 + + // 添加到列表中 + moodboardPositions.add(position); + } + } + continue; // 跳过 "class" 字段的常规处理 + } + + JSONArray styleArray = moodboardPositionJson.getJSONArray(key); + if (styleArray != null) { + for (int i = 0; i < styleArray.size(); i++) { + // 获取当前样式数据 + JSONObject styleData = styleArray.getJSONObject(i); + + // 构建 MoodboardPosition 实例 + MoodboardPosition position = new MoodboardPosition() + .setCollectionId(collectionIdParam) // 关联 Collection ID + .setType(key) // 样式类型 + .setStyleData(styleData.toJSONString()) // 样式数据存为 JSON 字符串 + .setSequence(i) // 根据索引值设置顺序 + .setCreateTime(LocalDateTime.now()) // 创建时间 + .setUpdateTime(LocalDateTime.now()); // 更新时间 + + // 添加到列表中 + moodboardPositions.add(position); + } + } + } + // 如果解析结果非空,保存到数据库 + if (!moodboardPositions.isEmpty()) { + for (MoodboardPosition position : moodboardPositions) { + baseMapper.insert(position); + } + log.info("成功解析并保存 {} 条 MoodboardPosition 数据", moodboardPositions.size()); + } else { + log.warn("未找到可保存的 MoodboardPosition 数据"); + } + } else { + log.warn("传入的 moodboardPosition 字段为空"); + } + } + +} 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 158c63c6..5a372a14 100644 --- a/src/main/java/com/ai/da/service/impl/PortfolioServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/PortfolioServiceImpl.java @@ -30,6 +30,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.base.Function; import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -45,107 +46,41 @@ import java.util.stream.Collectors; @Slf4j @Service +@RequiredArgsConstructor public class PortfolioServiceImpl extends ServiceImpl implements PortfolioService { - @Resource - private UserLikeGroupMapper userLikeGroupMapper; - - @Resource - private CollectionMapper collectionMapper; - - @Resource - private CollectionElementService collectionElementService; - - @Resource - private CollectionElementMapper collectionElementMapper; - @Resource - private CollectionSortService collectionSortService; - @Resource - private CollectionSortMapper collectionSortMapper; - @Resource - private TCollectionElementRelationMapper collectionElementRelationMapper; - - @Resource - private PortfolioMapper portfolioMapper; - - @Resource - private UserLikeService userLikeService; - - @Resource - private UserLikeMapper userLikeMapper; - - @Resource - private TDesignPythonOutfitMapper designPythonOutfitMapper; - - @Resource - private TDesignPythonOutfitDetailMapper designPythonOutfitDetailMapper; - - @Resource - private DesignItemMapper designItemMapper; - - @Resource - private DesignItemDetailMapper designItemDetailMapper; - - @Resource - private DesignItemDetailPrintMapper designItemDetailPrintMapper; - - - @Resource - private MinioUtil minioUtil; - - @Resource - private WorkspaceService workspaceService; - - @Resource - private DesignMapper designMapper; - - @Resource - private UserLikeGroupService userLikeGroupService; - - @Resource - private CanvasMapper canvasMapper; - - @Resource - private AccountMapper accountMapper; - - @Resource - private WorkspaceMapper workspaceMapper; - - @Resource - private SysFileMapper sysFileMapper; - - @Resource - private LibraryMapper libraryMapper; - - @Resource - private StyleMapper styleMapper; - - @Resource - private WorkspaceRelStyleMapper workspaceRelStyleMapper; - - @Resource - private UserFollowMapper userFollowMapper; - - @Resource - private PortfolioTagsMapper portfolioTagsMapper; - - @Resource - private TagsService tagsService; - - @Resource - private TagsMapper tagsMapper; - - @Resource - private ProjectMapper projectMapper; - - @Resource - private CollectionElementRelModelMapper collectionElementRelModelMapper; - @Resource - private ToProductImageResultMapper toProductImageResultMapper; - @Resource - private ToProductImageRecordMapper toProductImageRecordMapper; - @Resource - private PoseTransformationMapper poseTransformationMapper; + private final AccountMapper accountMapper; + private final CanvasMapper canvasMapper; + private final CollectionMapper collectionMapper; + private final CollectionElementMapper collectionElementMapper; + private final CollectionElementService collectionElementService; + private final CollectionSortMapper collectionSortMapper; + private final CollectionSortService collectionSortService; + private final CollectionElementRelModelMapper collectionElementRelModelMapper; + private final TCollectionElementRelationMapper collectionElementRelationMapper; + private final DesignMapper designMapper; + private final DesignItemMapper designItemMapper; + private final DesignItemDetailMapper designItemDetailMapper; + private final DesignItemDetailPrintMapper designItemDetailPrintMapper; + private final TDesignPythonOutfitMapper designPythonOutfitMapper; + private final TDesignPythonOutfitDetailMapper designPythonOutfitDetailMapper; + private final MinioUtil minioUtil; + private final ProjectMapper projectMapper; + private final PortfolioMapper portfolioMapper; + private final PortfolioTagsMapper portfolioTagsMapper; + private final PoseTransformationMapper poseTransformationMapper; + private final TagsMapper tagsMapper; + private final TagsService tagsService; + private final ToProductImageResultMapper toProductImageResultMapper; + private final ToProductImageRecordMapper toProductImageRecordMapper; + private final UserFollowMapper userFollowMapper; + private final UserFollowService userFollowService; + private final UserLikeService userLikeService; + private final UserLikeMapper userLikeMapper; + private final UserLikeGroupMapper userLikeGroupMapper; + private final UserLikeGroupService userLikeGroupService; + private final WorkspaceMapper workspaceMapper; + private final WorkspaceService workspaceService; @Override @Transactional(rollbackFor = Exception.class) @@ -585,7 +520,7 @@ public class PortfolioServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("follower_id", accountId).select("followee_id"); - return userFollowMapper.selectCount(queryWrapper); - } - - // 获取某个用户的关注列表 + 按名字查询 - public List getFolloweeList(GetFollowListDTO getFollowListDTO) { - Long accountId = UserContext.getUserHolder().getId(); - // 1、判断是否有按用户名查询 - List followeeList; - if (!StringUtil.isNullOrEmpty(getFollowListDTO.getSearchByName())) { - followeeList = userFollowMapper.getFolloweeListByName(getFollowListDTO.getSearchByName(), accountId); - } else { - // 2、查全部 分页查询 - String order = StringUtil.isNullOrEmpty(getFollowListDTO.getOrder()) ? "DESC" : getFollowListDTO.getOrder().equals("DESC") ? "DESC" : "ASC"; - Integer limit = getFollowListDTO.getSize() > 0 ? getFollowListDTO.getSize() : 20; - Integer offset = getFollowListDTO.getPage() > 0 ? (getFollowListDTO.getPage() - 1) * getFollowListDTO.getSize() : 0; - followeeList = userFollowMapper.getFolloweeListByFollower(accountId, limit, offset, order); - } - - if (!followeeList.isEmpty()) { - followeeList.forEach(followee -> { - String avatar = StringUtil.isNullOrEmpty(followee.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : followee.getAvatar(); - followee.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - }); - return followeeList; - } - return new ArrayList<>(); - } - - public Long getFollowerCount(Long accountId) { - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("followee_id", accountId).select("follower_id"); - return userFollowMapper.selectCount(queryWrapper); - } - - // 获取某个用户的粉丝列表 + 按名字查询 需返回是否关注该粉丝 - public List getFollowerList(GetFollowListDTO getFollowListDTO) { - Long accountId = UserContext.getUserHolder().getId(); - - // 获取当前用户的所有粉丝 - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("follower_id", accountId).select("followee_id", "create_time"); - List userFollows = userFollowMapper.selectList(qw); - Map followeeMap = userFollows.stream().collect(Collectors.toMap(UserFollow::getFolloweeId, UserFollow::getCreateTime)); - - List followerList; - // 1、判断是否有按用户名查询粉丝 - if (!StringUtil.isNullOrEmpty(getFollowListDTO.getSearchByName())) { - followerList = userFollowMapper.getFollowerListByName(getFollowListDTO.getSearchByName(), accountId); - } else { - // 2、查全部 分页查询 - String order = StringUtil.isNullOrEmpty(getFollowListDTO.getOrder()) ? "DESC" : getFollowListDTO.getOrder().equals("DESC") ? "DESC" : "ASC"; - Integer limit = getFollowListDTO.getSize() > 0 ? getFollowListDTO.getSize() : 20; - Integer offset = getFollowListDTO.getPage() > 0 ? (getFollowListDTO.getPage() - 1) * getFollowListDTO.getSize() : 0; - followerList = userFollowMapper.getFollowerListByFollowee(accountId, limit, offset, order); - } - - if (!followerList.isEmpty()) { - // 判断当前用户是否与粉丝互关 - followerList.forEach(follower -> { - String avatar = StringUtil.isNullOrEmpty(follower.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : follower.getAvatar(); - follower.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - follower.setIsFollow(Objects.isNull(followeeMap.get(follower.getSenderId())) ? 0 : 1); -// follower.setFollowTime(followeeMap.get(follower.getUserId())); - }); - return followerList; - } - return new ArrayList<>(); - } - - public Integer getIfFollowed(Long followeeId, Long followerId) { - // 设置当前用户是否关注了所查看作品的作者 - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("followee_id", followeeId).eq("follower_id", followerId); - UserFollow userFollow = userFollowMapper.selectOne(queryWrapper); - return Objects.isNull(userFollow) ? 0 : 1; - } - public Long getPortfolioCount(Long accountId) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("account_id", accountId); @@ -1363,19 +1218,6 @@ public class PortfolioServiceImpl extends ServiceImpl getFolloweeList(Long accountId) { - QueryWrapper qw = new QueryWrapper<>(); - qw.eq("follower_id", accountId).select("followee_id"); - - List userFollows = userFollowMapper.selectList(qw); - return userFollows.stream().map(UserFollow::getFolloweeId).collect(Collectors.toList()); - } public void setPortfolioToPublic(Long portfolioId) { // 判断当前用户与作品用户是不是一家公司的 diff --git a/src/main/java/com/ai/da/service/impl/PythonTAllInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/PythonTAllInfoServiceImpl.java index 03d003c3..9b6c07b1 100644 --- a/src/main/java/com/ai/da/service/impl/PythonTAllInfoServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/PythonTAllInfoServiceImpl.java @@ -4,6 +4,8 @@ package com.ai.da.service.impl; import com.ai.da.common.config.exception.BusinessException; import com.ai.da.mapper.primary.PythonTAllInfoMapper; import com.ai.da.mapper.primary.entity.PythonTAllInfo; +import com.ai.da.python.vo.DesignPythonItem; +import com.ai.da.python.vo.DesignPythonObjects; import com.ai.da.service.PythonTAllInfoService; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -13,7 +15,9 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * (PythonTAllInfo)表服务实现类 @@ -48,4 +52,31 @@ public class PythonTAllInfoServiceImpl extends ServiceImpl { + for (DesignPythonItem item : o.getItems()) { + List list = new ArrayList<>(); + list.add(1L); + list.add(1L); + item.setOffset(list); + item.setResize_scale(new Float[]{1.0f, 1.0f}); + String path = item.getPath(); + if (StringUtils.isEmpty(path)) { + String bodyPath = item.getBody_path(); + Long imageId = getImageIdByPath(bodyPath); + item.setImage_id(imageId); + } else { + Long imageId = getImageIdByPath(path); + item.setImage_id(imageId); + } + } + } + ); + } } diff --git a/src/main/java/com/ai/da/service/impl/ToProductImageResultServiceImpl.java b/src/main/java/com/ai/da/service/impl/ToProductImageResultServiceImpl.java new file mode 100644 index 00000000..f2e5ba24 --- /dev/null +++ b/src/main/java/com/ai/da/service/impl/ToProductImageResultServiceImpl.java @@ -0,0 +1,78 @@ +package com.ai.da.service.impl; + +import com.ai.da.mapper.primary.ToProductImageResultMapper; +import com.ai.da.mapper.primary.entity.CollectionSort; +import com.ai.da.mapper.primary.entity.ToProductImageResult; +import com.ai.da.mapper.primary.entity.UserLike; +import com.ai.da.service.CollectionSortService; +import com.ai.da.service.ToProductImageResultService; +import com.ai.da.service.UserLikeService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ToProductImageResultServiceImpl extends ServiceImpl implements ToProductImageResultService { + + private final CollectionSortService collectionSortService; + + private final UserLikeService userLikeService; + + // 获取未被like的to product\relight的parentId + public Long getUnlikedResultParentId(ToProductImageResult toProductImageResult, String url) { + // 卫语句处理null情况 + if (toProductImageResult == null && StringUtil.isNullOrEmpty(url)) { + return null; + } + + // 如果需要查询结果对象 + if (toProductImageResult == null) { + toProductImageResult = baseMapper.selectOne( + new QueryWrapper().eq("url", url)); + } + + // 卫语句处理查询结果为空的情况 + if (toProductImageResult == null) { + return null; + } + + // 根据不同类型处理 + String elementType = toProductImageResult.getElementType(); + if ("DesignOutfit".equals(elementType)) { + return handleDesignOutfitCase(toProductImageResult); + } else if ("ToProductImage".equals(elementType)) { + // 两种情况 resultType : ToProductImage | Relight + return handleToProductImageCase(toProductImageResult); + } + + return null; + } + + private Long handleDesignOutfitCase(ToProductImageResult result) { + UserLike userLike = userLikeService.getByDesignOutfitId(result.getElementId()); + if (userLike == null) { + return null; + } + + CollectionSort collectionSort = collectionSortService.queryCollectionSortByRelation( + userLike.getId(), "Design", result.getProjectId()); + return collectionSort != null ? collectionSort.getId() : null; + } + + private Long handleToProductImageCase(ToProductImageResult result) { + CollectionSort collectionSort = collectionSortService.queryCollectionSortByRelation( + result.getElementId(), result.getResultType(), result.getProjectId()); + if (Objects.isNull(collectionSort) && result.getElementType().equals("ToProductImage")) { + ToProductImageResult toProductImageResult = baseMapper.selectById(result.getElementId()); + return getUnlikedResultParentId(toProductImageResult, null); + } + return collectionSort != null ? collectionSort.getParentId() : null; + } +} diff --git a/src/main/java/com/ai/da/service/impl/UserFollowServiceImpl.java b/src/main/java/com/ai/da/service/impl/UserFollowServiceImpl.java new file mode 100644 index 00000000..26d268cb --- /dev/null +++ b/src/main/java/com/ai/da/service/impl/UserFollowServiceImpl.java @@ -0,0 +1,125 @@ +package com.ai.da.service.impl; + +import com.ai.da.common.constant.CommonConstant; +import com.ai.da.common.context.UserContext; +import com.ai.da.common.utils.MinioUtil; +import com.ai.da.mapper.primary.UserFollowMapper; +import com.ai.da.mapper.primary.entity.UserFollow; +import com.ai.da.model.dto.GetFollowListDTO; +import com.ai.da.model.vo.AccountFollowVO; +import com.ai.da.service.UserFollowService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UserFollowServiceImpl extends ServiceImpl implements UserFollowService { + + private final MinioUtil minioUtil; + + public Long getFolloweeCount(Long accountId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("follower_id", accountId).select("followee_id"); + return baseMapper.selectCount(queryWrapper); + } + + // 获取某个用户的关注列表 + 按名字查询 + public List getFolloweeList(GetFollowListDTO getFollowListDTO) { + Long accountId = UserContext.getUserHolder().getId(); + // 1、判断是否有按用户名查询 + List followeeList; + if (!StringUtil.isNullOrEmpty(getFollowListDTO.getSearchByName())) { + followeeList = baseMapper.getFolloweeListByName(getFollowListDTO.getSearchByName(), accountId); + } else { + // 2、查全部 分页查询 + String order = StringUtil.isNullOrEmpty(getFollowListDTO.getOrder()) ? "DESC" : getFollowListDTO.getOrder().equals("DESC") ? "DESC" : "ASC"; + Integer limit = getFollowListDTO.getSize() > 0 ? getFollowListDTO.getSize() : 20; + Integer offset = getFollowListDTO.getPage() > 0 ? (getFollowListDTO.getPage() - 1) * getFollowListDTO.getSize() : 0; + followeeList = baseMapper.getFolloweeListByFollower(accountId, limit, offset, order); + } + + if (!followeeList.isEmpty()) { + followeeList.forEach(followee -> { + String avatar = StringUtil.isNullOrEmpty(followee.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : followee.getAvatar(); + followee.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + }); + return followeeList; + } + return new ArrayList<>(); + } + + public Long getFollowerCount(Long accountId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("followee_id", accountId).select("follower_id"); + return baseMapper.selectCount(queryWrapper); + } + + // 获取某个用户的粉丝列表 + 按名字查询 需返回是否关注该粉丝 + public List getFollowerList(GetFollowListDTO getFollowListDTO) { + Long accountId = UserContext.getUserHolder().getId(); + + // 获取当前用户的所有粉丝 + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("follower_id", accountId).select("followee_id", "create_time"); + List userFollows = baseMapper.selectList(qw); + Map followeeMap = userFollows.stream().collect(Collectors.toMap(UserFollow::getFolloweeId, UserFollow::getCreateTime)); + + List followerList; + // 1、判断是否有按用户名查询粉丝 + if (!StringUtil.isNullOrEmpty(getFollowListDTO.getSearchByName())) { + followerList = baseMapper.getFollowerListByName(getFollowListDTO.getSearchByName(), accountId); + } else { + // 2、查全部 分页查询 + String order = StringUtil.isNullOrEmpty(getFollowListDTO.getOrder()) ? "DESC" : getFollowListDTO.getOrder().equals("DESC") ? "DESC" : "ASC"; + Integer limit = getFollowListDTO.getSize() > 0 ? getFollowListDTO.getSize() : 20; + Integer offset = getFollowListDTO.getPage() > 0 ? (getFollowListDTO.getPage() - 1) * getFollowListDTO.getSize() : 0; + followerList = baseMapper.getFollowerListByFollowee(accountId, limit, offset, order); + } + + if (!followerList.isEmpty()) { + // 判断当前用户是否与粉丝互关 + followerList.forEach(follower -> { + String avatar = StringUtil.isNullOrEmpty(follower.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : follower.getAvatar(); + follower.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); + follower.setIsFollow(Objects.isNull(followeeMap.get(follower.getSenderId())) ? 0 : 1); +// follower.setFollowTime(followeeMap.get(follower.getUserId())); + }); + return followerList; + } + return new ArrayList<>(); + } + + public Integer getIfFollowed(Long followeeId, Long followerId) { + // 设置当前用户是否关注了所查看作品的作者 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("followee_id", followeeId).eq("follower_id", followerId); + UserFollow userFollow = baseMapper.selectOne(queryWrapper); + return Objects.isNull(userFollow) ? 0 : 1; + } + + /** + * 获取关注列表 + * + * @param accountId + * @return + */ + public List getFolloweeList(Long accountId) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("follower_id", accountId).select("followee_id"); + + List userFollows = baseMapper.selectList(qw); + return userFollows.stream().map(UserFollow::getFolloweeId).collect(Collectors.toList()); + } +} 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 9506e816..a1526e95 100644 --- a/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java @@ -29,7 +29,9 @@ import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; + import java.util.Comparator; + import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -38,6 +40,7 @@ import com.google.common.base.Function; import com.google.gson.Gson; import io.minio.errors.MinioException; import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -66,98 +69,54 @@ import java.util.stream.Collectors; */ @Slf4j @Service +@RequiredArgsConstructor public class UserLikeGroupServiceImpl extends ServiceImpl implements UserLikeGroupService { - @Resource - private UserLikeGroupMapper userLikeGroupMapper; - @Resource - private AccountService accountService; - @Resource - private CollectionService collectionService; - @Resource - private UserLikeService userLikeService; - @Resource - private WorkspaceService workspaceService; - @Resource - private UserLikeMapper userLikeMapper; - @Resource - private MinioUtil minioUtil; - @Resource - private TDesignPythonOutfitMapper designPythonOutfitMapper; - @Resource - private DesignMapper designMapper; - @Resource - private DesignService designService; - @Resource - private SysFileMapper sysFileMapper; - @Resource - private LibraryMapper libraryMapper; - @Resource - private GenerateDetailMapper generateDetailMapper; - @Resource - private PortfolioMapper portfolioMapper; - @Resource - private TagsMapper tagsMapper; - @Resource - private DesignItemDetailService designItemDetailService; - @Resource - private CollectionElementMapper collectionElementMapper; - @Resource - private AttributeRetrievalMapper attributeRetrievalMapper; - @Resource - private ProductImageAttributeMapper productImageAttributeMapper; - @Resource - private CollectionSortMapper collectionSortMapper; - @Resource - private ClassificationService classificationService; - @Resource -// private ProjectMapper projectMapper; - private ProjectService projectService; - @Resource - private PortfolioService portfolioService; - @Resource - private LibraryModelPointMapper libraryModelPointMapper; - @Resource - private LibraryService libraryService; - @Resource - private BrandDNAMapper brandDNAMapper; - @Resource - private BrandRelLibraryMapper brandRelLibraryMapper; - @Resource - private ThreeDLayoutMapper threeDLayoutMapper; - @Resource - private ThreeDPatternLayoutMapper threeDPatternLayoutMapper; - @Resource - private ThreeDDetailMapper threeDDetailMapper; - @Resource - private ThreeDSimpleMapper threeDSimpleMapper; - @Resource - private ThreeDModuleMapper threeDModuleMapper; - @Resource - private ProductImageService productImageService; - @Resource - private CollectionElementRelModelMapper collectionElementRelModelMapper; - @Resource - private CollectionSortService collectionSortService; - @Resource - private GenerateService generateService; - @Resource - private ToProductElementMapper toProductElementMapper; - @Resource - private ToProductImageRecordMapper toProductImageRecordMapper; - @Resource - private ToProductImageResultMapper toProductImageResultMapper; - @Resource - private CloudTaskMapper cloudTaskMapper; - @Resource - private PythonService pythonService; - @Resource - private CreditsService creditsService; - @Resource - private PoseTransformationMapper poseTransformationMapper; - @Resource - private ExportFileMapper exportFileMapper; - @Resource - private DesignItemDetailCanvasMapper designItemDetailCanvasMapper; + private final UserLikeGroupMapper userLikeGroupMapper; + private final AccountService accountService; + private final CollectionService collectionService; + private final UserLikeService userLikeService; + private final WorkspaceService workspaceService; + private final UserLikeMapper userLikeMapper; + private final MinioUtil minioUtil; + private final TDesignPythonOutfitMapper designPythonOutfitMapper; + private final DesignMapper designMapper; + private final MoodboardPositionService moodboardPositionService; + private final SysFileMapper sysFileMapper; + private final LibraryMapper libraryMapper; + private final GenerateDetailMapper generateDetailMapper; + private final PortfolioMapper portfolioMapper; + private final TagsMapper tagsMapper; + private final DesignItemDetailService designItemDetailService; + private final CollectionElementMapper collectionElementMapper; + private final AttributeRetrievalMapper attributeRetrievalMapper; + private final ProductImageAttributeMapper productImageAttributeMapper; + private final CollectionSortMapper collectionSortMapper; + private final ClassificationService classificationService; + // private ProjectMapper projectMapper; + private final ProjectService projectService; + private final LibraryModelPointMapper libraryModelPointMapper; + private final LibraryService libraryService; + private final BrandDNAMapper brandDNAMapper; + private final BrandRelLibraryMapper brandRelLibraryMapper; + private final ThreeDLayoutMapper threeDLayoutMapper; + private final ThreeDPatternLayoutMapper threeDPatternLayoutMapper; + private final ThreeDDetailMapper threeDDetailMapper; + private final ThreeDSimpleMapper threeDSimpleMapper; + private final ThreeDModuleMapper threeDModuleMapper; + private final ProductImageService productImageService; + private final CollectionElementRelModelMapper collectionElementRelModelMapper; + private final CollectionSortService collectionSortService; + private final GenerateService generateService; + private final ToProductElementMapper toProductElementMapper; + private final ToProductImageRecordMapper toProductImageRecordMapper; + private final ToProductImageResultMapper toProductImageResultMapper; + private final ToProductImageResultService toProductImageResultService; + private final CloudTaskMapper cloudTaskMapper; + private final PythonService pythonService; + private final CreditsService creditsService; + private final PoseTransformationMapper poseTransformationMapper; + private final ExportFileMapper exportFileMapper; + private final DesignItemDetailCanvasMapper designItemDetailCanvasMapper; @Value("${redis.key.generateResult}") private String generateResultKey; @@ -2033,7 +1992,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl= 1; } - // 获取未被like的to product\relight的parentId - public Long getUnlikedResultParentId(ToProductImageResult toProductImageResult, String url) { - // 卫语句处理null情况 - if (toProductImageResult == null && StringUtil.isNullOrEmpty(url)) { - return null; - } - - // 如果需要查询结果对象 - if (toProductImageResult == null) { - toProductImageResult = toProductImageResultMapper.selectOne( - new QueryWrapper().eq("url", url)); - } - - // 卫语句处理查询结果为空的情况 - if (toProductImageResult == null) { - return null; - } - - // 根据不同类型处理 - String elementType = toProductImageResult.getElementType(); - if ("DesignOutfit".equals(elementType)) { - return handleDesignOutfitCase(toProductImageResult); - } else if ("ToProductImage".equals(elementType)) { - // 两种情况 resultType : ToProductImage | Relight - return handleToProductImageCase(toProductImageResult); - } - - return null; - } - - private Long handleDesignOutfitCase(ToProductImageResult result) { - UserLike userLike = userLikeService.getByDesignOutfitId(result.getElementId()); - if (userLike == null) { - return null; - } - - CollectionSort collectionSort = collectionSortService.queryCollectionSortByRelation( - userLike.getId(), "Design", result.getProjectId()); - return collectionSort != null ? collectionSort.getId() : null; - } - - private Long handleToProductImageCase(ToProductImageResult result) { - CollectionSort collectionSort = collectionSortService.queryCollectionSortByRelation( - result.getElementId(), result.getResultType(), result.getProjectId()); - if (Objects.isNull(collectionSort) && result.getElementType().equals("ToProductImage")) { - ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(result.getElementId()); - return getUnlikedResultParentId(toProductImageResult, null); - } - return collectionSort != null ? collectionSort.getParentId() : null; - } @Override @Transactional @@ -2568,7 +2477,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl implements UserLikeService { - @Resource - private UserLikeMapper userLikeMapper; - @Resource - private UserLikeGroupMapper userLikeGroupMapper; - @Resource - private DesignItemServiceImpl designItemService; + private final UserLikeMapper userLikeMapper; + private final UserLikeGroupMapper userLikeGroupMapper; @Override public List getGroupDetail(Long userGroupId) { @@ -50,12 +44,6 @@ public class UserLikeServiceImpl extends ServiceImpl i return Lists.newArrayList(); } return CopyUtil.copyList(userLikes, UserLikeVO.class); -// List designItemIds = userLikes.stream().map(UserLike::getDesignItemId).collect(Collectors.toList()); -// List designItems = designItemService.listByIds(designItemIds); -// Map idToUrlMap = designItems.stream().collect(Collectors.toMap(DesignItem::getId,DesignItem::getDesignUrl)); -// return CopyUtil.copyList(userLikes,UserLikeVO.class,(o,d) ->{ -// d.setUrl(idToUrlMap.get(o.getDesignItemId())); -// }); } @Override @@ -72,11 +60,11 @@ public class UserLikeServiceImpl extends ServiceImpl i } @Override - public UserLike getByDesignItemId(Long designItemId){ + public UserLike getByDesignItemId(Long designItemId) { QueryWrapper qw = new QueryWrapper<>(); - qw.eq("design_item_id",designItemId); + qw.eq("design_item_id", designItemId); List userLikes = baseMapper.selectList(qw); - if (!userLikes.isEmpty()){ + if (!userLikes.isEmpty()) { return baseMapper.selectList(qw).get(0); } return null; @@ -84,13 +72,13 @@ public class UserLikeServiceImpl extends ServiceImpl i @Override @Transactional(rollbackFor = Exception.class) - public void updateDate(Long designItemId,String timeZone){ + public void updateDate(Long designItemId, String timeZone) { UpdateWrapper uw = new UpdateWrapper<>(); - uw.eq("design_item_id",designItemId); - uw.set("update_date",DateUtil.getByTimeZone(timeZone)); + uw.eq("design_item_id", designItemId); + uw.set("update_date", DateUtil.getByTimeZone(timeZone)); uw.set("converted", 1); - baseMapper.update(null,uw); + baseMapper.update(null, uw); } @Override @@ -123,12 +111,12 @@ public class UserLikeServiceImpl extends ServiceImpl i } @Override - public UserLike getByDesignOutfitId(Long designOutfitId){ + public UserLike getByDesignOutfitId(Long designOutfitId) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda().eq(UserLike::getDesignOutfitId, designOutfitId); // 有脏数据 List userLikes = baseMapper.selectList(qw); - if (!userLikes.isEmpty()){ + if (!userLikes.isEmpty()) { return userLikes.get(0); } return null; diff --git a/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java b/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java index d311ea3b..fce34680 100644 --- a/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java @@ -731,7 +731,7 @@ public class WorkspaceServiceImpl 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){ + if (collectionElementMapper.selectCount(queryWrapper) == 0) { CollectionElement collectionElement = new CollectionElement(); collectionElement.setAccountId(userInfo.getId()); collectionElement.setProjectId(projectId); @@ -792,7 +792,7 @@ public class WorkspaceServiceImpl extends ServiceImpl byCollectionId = getByCollectionId(collectionId); + if (byCollectionId != null && !byCollectionId.isEmpty()) { + projectId = byCollectionId.get(0).getProjectId(); + if (projectId != null && projectId != 0L) { + Workspace workspace = getWSByProjectId(projectId); + if (workspace != null && !StringUtil.isNullOrEmpty(workspace.getAgeGroup())) { + return workspace.getAgeGroup(); + } + } + } + } + return AgeGroup.ADULT.getValue(); + } + + private List getByCollectionId(Long collectionId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.lambda().eq(CollectionElement::getCollectionId, collectionId); + return collectionElementMapper.selectList(queryWrapper); + } + }