微服务改造

This commit is contained in:
litianxiang
2026-04-27 11:47:15 +08:00
parent 7c8f1bee6a
commit d36baf747f
10 changed files with 3923 additions and 3592 deletions

View File

@@ -102,7 +102,7 @@ jobs:
- ./temp:/temp
- ./uploads:/temp/uploads
ports:
- '10090:5567'
- '10090:5566'
restart: always
EOF
# 验证docker-compose.yml生成

View File

@@ -10,7 +10,7 @@ public class CommonConstant {
// 单位 秒 两天过期
public static final Long CREDITS_EXPIRE_TIME = 2 * 24 * 60 * 60L;
// 单位 分钟
public static final Integer MINIO_IMAGE_EXPIRE_TIME = 24 * 60;
public static final Integer MINIO_IMAGE_EXPIRE_TIME = 24 * 60 * 7;
// 单位 秒 一天过期 in redis
public static final Long GENERATE_RESULT_EXPIRE_TIME = 24 * 60 * 60L;
// 单位 秒 7天过期

View File

@@ -14,6 +14,7 @@ import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -41,6 +42,9 @@ public class MinioUtil {
@Autowired
private MinioClient minioClient;
@Value("${minio.endpoint}")
private String endpoint;
/**
* 获取MinIO客户端实例
*/
@@ -958,6 +962,166 @@ public class MinioUtil {
}
}
/**
* 检测字符串是否为预签名URL
* 通过检查URL中是否包含minio endpoint来判断
*
* @param str 待检测的字符串
* @return true表示是预签名URLfalse表示不是
*/
public boolean isPresignedUrl(String str) {
if (str == null || str.isEmpty()) {
return false;
}
try {
// 检查字符串是否是一个有效的URL
URL url = new URL(str);
String host = url.getHost();
// 获取endpoint中的主机部分去掉http://或https://
String endpointHost = endpoint;
if (endpointHost.startsWith("http://")) {
endpointHost = endpointHost.substring(7);
} else if (endpointHost.startsWith("https://")) {
endpointHost = endpointHost.substring(8);
}
// 去掉端口号
if (endpointHost.contains(":")) {
endpointHost = endpointHost.substring(0, endpointHost.indexOf(":"));
}
// 检查URL的host是否与endpoint的host匹配
return host.equals(endpointHost);
} catch (Exception e) {
// 不是有效的URL
return false;
}
}
/**
* 检测字符串是否为MinIO逻辑路径bucketName/objectName格式
* 逻辑路径特点:
* 1. 包含 "/"(桶名和对象名之间的分隔符)
* 2. 不是完整的URL不以http://或https://开头)
* 3. 路径中没有查询参数
*
* @param str 待检测的字符串
* @return true表示是MinIO逻辑路径false表示不是
*/
public boolean isMinioLogicalPath(String str) {
if (str == null || str.isEmpty()) {
return false;
}
// 必须是字符串
if (!(str instanceof String)) {
return false;
}
String trimStr = str.trim();
// 不应该以http://或https://开头
if (trimStr.startsWith("http://") || trimStr.startsWith("https://")) {
return false;
}
// 应该包含 "/"bucket/object格式
if (!trimStr.contains("/")) {
return false;
}
// 不应该包含空格或特殊字符
if (trimStr.contains(" ") || trimStr.contains("\n") || trimStr.contains("\t")) {
return false;
}
return true;
}
/**
* 将预签名URL转换为逻辑路径
*
* @param presignedUrl 预签名URL
* @return 逻辑路径格式bucketName/objectName
*/
public String getLogicalPathFromPresignedUrl(String presignedUrl) {
try {
// 解析URL
URL url = new URL(presignedUrl);
// 获取路径部分(去掉开头的/
String path = url.getPath();
if (path.startsWith("/")) {
path = path.substring(1);
}
// 路径格式为 bucketName/objectName
// Minio路径中可能包含多个/,需要正确分割
int firstSlashIndex = path.indexOf("/");
if (firstSlashIndex <= 0) {
throw new MinioException("预签名URL路径格式无效应包含桶名和对象名: " + presignedUrl);
}
String bucketName = path.substring(0, firstSlashIndex);
String objectName = path.substring(firstSlashIndex + 1);
// log.info("预签名URL转换成功桶名: {}, 对象名: {}", bucketName, objectName);
return bucketName + "/" + objectName;
} catch (Exception e) {
log.error("预签名URL解析失败: {}", e.getMessage(), e);
throw new BusinessException("system.error");
}
}
/**
* 处理MinIO资源预签名URL或逻辑路径统一生成预签名URL
*
* @param resource 预签名URL或逻辑路径
* @param expires 过期时间(秒)
* @return 新的预签名URL
*/
public String processMinioResource(String resource, int expires) {
try {
String logicalPath;
if (isPresignedUrl(resource)) {
// 是预签名URL解析为逻辑路径
logicalPath = getLogicalPathFromPresignedUrl(resource);
} else if (isMinioLogicalPath(resource)) {
// 本身就是逻辑路径
logicalPath = resource.trim();
} else {
// 不认识的内容,直接返回原始值
log.warn("未识别的MinIO资源格式: {}", resource);
return resource;
}
// 统一生成预签名URL
return getPreSignedUrl(logicalPath, expires);
} catch (Exception e) {
log.error("处理MinIO资源失败: {}, error: {}", resource, e.getMessage(), e);
// 如果失败,返回原始内容
return resource;
}
}
/**
* 将任意MinIO URL转换为逻辑路径
* 检测URL类型并转换为逻辑路径返回
*
* @param url 预签名URL或逻辑路径
* @return 逻辑路径格式bucketName/objectName
* @throws MinioException 如果不是有效的MinIO资源
*/
public String convertToLogicalPath(String url) {
if (url == null || url.isEmpty()) {
throw new BusinessException("url.cannot.be.empty");
}
if (isMinioLogicalPath(url)) {
// 本身就是逻辑路径,直接返回
return url.trim();
} else if (isPresignedUrl(url)) {
// 是预签名URL转换为逻辑路径
return getLogicalPathFromPresignedUrl(url);
} else {
// 不认识的内容,抛出异常
throw new BusinessException("无法识别的MinIO资源格式: " + url + "请提供有效的预签名URL或逻辑路径");
}
}
}

View File

@@ -0,0 +1,31 @@
package com.ai.da.seller;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 设计URLs DTO
*/
@Data
@Schema(description = "设计URLs数据传输对象")
public class DesignUrlsDTO {
/**
* 设计项ID
*/
@Schema(description = "设计项ID", example = "1")
private Long designItemId;
/**
* TO_PRODUCT_IMAGE类型的URL列表
*/
@Schema(description = "TO_PRODUCT_IMAGE类型的URL列表")
private List<String> toProductImageUrls;
/**
* DesignItemDetail的path列表
*/
@Schema(description = "DesignItemDetail的path列表")
private List<String> clothes;
}

View File

@@ -0,0 +1,44 @@
package com.ai.da.seller;
import com.ai.da.common.response.Response;
import com.ai.da.service.UserLikeGroupService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* Seller Controller
*/
@RestController
@RequestMapping("/api/seller")
@RequiredArgsConstructor
@Tag(name = "Seller", description = "Seller相关接口")
public class SellerController {
private final UserLikeGroupService userLikeGroupService;
/**
* 根据designItemId列表获取设计相关的URL列表
* @param designItemIds designItemId列表
* @return 设计URLs DTO列表
*/
@GetMapping("/sketchDetail")
@Operation(summary = "获取设计相关URL列表", description = "根据designItemId列表获取设计相关的URL列表包括TO_PRODUCT_IMAGE类型的URL和DesignItemDetail的path列表")
public Response<List<DesignUrlsDTO>> getDesignUrlsByDesignItemIds(
@Parameter(description = "设计项ID列表", required = true, example = "1,2,3")
@RequestParam List<Long> designItemIds) {
List<DesignUrlsDTO> designUrlsDTOList = new ArrayList<>();
for (Long designItemId : designItemIds) {
DesignUrlsDTO designUrlsDTO = userLikeGroupService.getToProductImageUrlsByDesignItemId(designItemId);
designUrlsDTOList.add(designUrlsDTO);
}
return Response.success(designUrlsDTOList);
}
}

View File

@@ -1,124 +1,132 @@
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import io.minio.errors.MinioException;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* 服务类
*
* @author yanglei
* @since 2022-09-11
*/
public interface UserLikeGroupService extends IService<UserLikeGroup> {
void deleteUserGroup(Long userGroupId);
HistoryUpdateVO updateUserGroupName(Long userGroupId, String userGroupName, String timeZone);
Long insertUserGroup(Long userId, Long collectionId, String timeZone, Long projectId);
/**
* choose
*
* @param userGroupId
* @return
*/
UserLikeChooseVO choose(Long userGroupId);
ProjectChooseVO choose(ProjectDTO projectDTO);
UserLikeGroup getByProjectId(Long projectId);
void deleteTrialData(Long id);
void updateDate(Long id, String timeZone);
Long exportSave(MultipartFile file, Long projectId, String module, Long designItemDetailId);
List<ToProductImageResultVO> toProduct(ToProductImageDTO toProductImageDTO);
void toProduct(String taskId);
ToProductElementVO toProductImageElementUpload(MultipartFile file, Long projectId, String type);
CollectionSort productImageLike(ProductImageLikeDTO productImageLikeDTO);
List<MagicToolResultVO> getToProductImageResultList(List<String> taskIdList);
JSONObject exportSearch(ExportSearchDTO exportSearchDTO);
CanvasElementUpload canvasElementUpload(MultipartFile file);
List<ToProductImageResultVO> productImageLikeList(ToProductImageDTO toProductImageDTO);
Boolean productImageUnLike(ProductImageLikeDTO productImageLikeDTO);
void relight(String taskId);
List<ToProductImageResultVO> relight(ToProductImageDTO toProductImageDTO);
List<MagicToolResultVO> getRelightResult(List<String> taskIdList);
void deleteToProductRelightResult(Long id, Long projectId, String type);
String likeHistoryRelSketch();
String download();
Boolean productImageInitialize(ProductImageInitializeDTO productImageInitializeDTO);
InitializeProgressVO getInitializeProgress(ProductImageInitializeDTO productImageInitializeDTO);
IPage<ProjectVO> getPage(ProjectQueryDTO projectQueryDTO);
ModuleChooseVO getModuleContent(ProjectDTO projectDTO);
ModuleChooseVO saveModuleContent(ModuleSaveDTO moduleSaveDTO);
QueryLibraryPageVO getMannequinDetail(MannequinDTO mannequinDTO);
BrandLogoUploadVO brandLogoUpload(MultipartFile file);
Boolean brandDNASaveOrUpdate(BrandDNADTO brandDNADTO);
LibraryUpdateVo brandDNAUpload(MultipartFile file, Long brandId) throws IOException;
PageBaseResponse<BrandDNAVO> brandDNAPage(BrandDNAQueryDTO brandDNAQueryDTO);
BrandDNAGenerateVO brandDNAGenerate(String prompt);
IPage<ThreeDLayoutVO> getThreeDLayoutPage(ThreeDLayoutQueryDTO threeDLayoutQueryDTO);
ThreeDVO getLayoutDetail(Long threeDSimpleId);
ThreeDSizeVO getThreeDSize(Long threeDSimpleId);
void getThreeDGlb(Long threeDSimpleId, HttpServletResponse response) throws MinioException, IOException;
String downloadZip(Long threeDSimpleId, String sizeType, String size, HttpServletResponse response) throws MinioException, IOException;
Boolean delete(Long projectId);
Boolean brandDNADelete(BrandDNADTO brandDNADTO);
void toProductBatch(String taskId, String url, String progress) throws InterruptedException;
void relightBatch(String taskId, String url, String progress);
Boolean collectionLikeUpdate(CollectionLikeUpdateDTO collectionLikeUpdateDTO);
Boolean toProductImageElementDelete(Long id);
ToProductElementVO convertRelightElement(Long id);
}
package com.ai.da.service;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.*;
import com.ai.da.seller.DesignUrlsDTO;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import io.minio.errors.MinioException;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* 服务类
*
* @author yanglei
* @since 2022-09-11
*/
public interface UserLikeGroupService extends IService<UserLikeGroup> {
void deleteUserGroup(Long userGroupId);
HistoryUpdateVO updateUserGroupName(Long userGroupId, String userGroupName, String timeZone);
Long insertUserGroup(Long userId, Long collectionId, String timeZone, Long projectId);
/**
* choose
*
* @param userGroupId
* @return
*/
UserLikeChooseVO choose(Long userGroupId);
ProjectChooseVO choose(ProjectDTO projectDTO);
UserLikeGroup getByProjectId(Long projectId);
void deleteTrialData(Long id);
void updateDate(Long id, String timeZone);
Long exportSave(MultipartFile file, Long projectId, String module, Long designItemDetailId);
List<ToProductImageResultVO> toProduct(ToProductImageDTO toProductImageDTO);
void toProduct(String taskId);
ToProductElementVO toProductImageElementUpload(MultipartFile file, Long projectId, String type);
CollectionSort productImageLike(ProductImageLikeDTO productImageLikeDTO);
List<MagicToolResultVO> getToProductImageResultList(List<String> taskIdList);
JSONObject exportSearch(ExportSearchDTO exportSearchDTO);
CanvasElementUpload canvasElementUpload(MultipartFile file);
List<ToProductImageResultVO> productImageLikeList(ToProductImageDTO toProductImageDTO);
Boolean productImageUnLike(ProductImageLikeDTO productImageLikeDTO);
void relight(String taskId);
List<ToProductImageResultVO> relight(ToProductImageDTO toProductImageDTO);
List<MagicToolResultVO> getRelightResult(List<String> taskIdList);
void deleteToProductRelightResult(Long id, Long projectId, String type);
String likeHistoryRelSketch();
String download();
Boolean productImageInitialize(ProductImageInitializeDTO productImageInitializeDTO);
InitializeProgressVO getInitializeProgress(ProductImageInitializeDTO productImageInitializeDTO);
IPage<ProjectVO> getPage(ProjectQueryDTO projectQueryDTO);
ModuleChooseVO getModuleContent(ProjectDTO projectDTO);
ModuleChooseVO saveModuleContent(ModuleSaveDTO moduleSaveDTO);
QueryLibraryPageVO getMannequinDetail(MannequinDTO mannequinDTO);
BrandLogoUploadVO brandLogoUpload(MultipartFile file);
Boolean brandDNASaveOrUpdate(BrandDNADTO brandDNADTO);
LibraryUpdateVo brandDNAUpload(MultipartFile file, Long brandId) throws IOException;
PageBaseResponse<BrandDNAVO> brandDNAPage(BrandDNAQueryDTO brandDNAQueryDTO);
BrandDNAGenerateVO brandDNAGenerate(String prompt);
IPage<ThreeDLayoutVO> getThreeDLayoutPage(ThreeDLayoutQueryDTO threeDLayoutQueryDTO);
ThreeDVO getLayoutDetail(Long threeDSimpleId);
ThreeDSizeVO getThreeDSize(Long threeDSimpleId);
void getThreeDGlb(Long threeDSimpleId, HttpServletResponse response) throws MinioException, IOException;
String downloadZip(Long threeDSimpleId, String sizeType, String size, HttpServletResponse response) throws MinioException, IOException;
Boolean delete(Long projectId);
Boolean brandDNADelete(BrandDNADTO brandDNADTO);
void toProductBatch(String taskId, String url, String progress) throws InterruptedException;
void relightBatch(String taskId, String url, String progress);
Boolean collectionLikeUpdate(CollectionLikeUpdateDTO collectionLikeUpdateDTO);
Boolean toProductImageElementDelete(Long id);
ToProductElementVO convertRelightElement(Long id);
/**
* 根据designItemId获取TO_PRODUCT_IMAGE类型的URL列表和DesignItemDetail的path列表
* @param designItemId designItemId
* @return 包含TO_PRODUCT_IMAGE类型的URL列表和DesignItemDetail的path列表的对象
*/
DesignUrlsDTO getToProductImageUrlsByDesignItemId(Long designItemId);
}

View File

@@ -5,7 +5,7 @@
# ============================================================
server:
port: 5567
port: 5566
spring:
application:

View File

@@ -111,6 +111,7 @@ waistbandRight.cannot.be.empty=waistbandRight cannot be empty.
handLeft.cannot.be.empty=handLeft cannot be empty.
handRight.cannot.be.empty=handRight cannot be empty.
id.cannot.be.empty=id cannot be empty.
url.cannot.be.empty=url cannot be empty.
type.cannot.be.empty=type cannot be empty.
color.cannot.be.empty=color cannot be empty.
generateDetailId.cannot.be.empty=generateDetailId cannot be empty.

View File

@@ -110,6 +110,7 @@ waistbandRight.cannot.be.empty=waistbandRight不能为空。
handLeft.cannot.be.empty=handLeft不能为空。
handRight.cannot.be.empty=handRight不能为空。
id.cannot.be.empty=id不能为空。
url.cannot.be.empty=url不能为空。
type.cannot.be.empty=type不能为空。
color.cannot.be.empty=color不能为空。
generateDetailId.cannot.be.empty=generateDetailId不能为空。