From b6a068ebcdabf14678b9b166f5d371b5e4567ddf Mon Sep 17 00:00:00 2001 From: litianxiang Date: Mon, 23 Mar 2026 11:50:24 +0800 Subject: [PATCH 01/22] =?UTF-8?q?SKETCHBOARD=E4=BC=A0=E5=85=A5=E7=9A=84tex?= =?UTF-8?q?t=E6=94=B9=E4=B8=BA=E8=8E=B7=E5=8F=96=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E4=B8=AA,=E4=B8=BA=E5=88=86=E5=89=B2=E8=8E=B7=E5=8F=96style?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../da/service/impl/GenerateServiceImpl.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) 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 4a40fa2b..30173584 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -1260,14 +1260,31 @@ public class GenerateServiceImpl extends ServiceImpl i modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); } } else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) { - String[] split = generateDTO.getText().split(","); - String style = split[0].trim(); - if ("Lolita".equals( style)){ - style = "洛丽塔"; + String style = ""; + String userPrompt = ""; + // 找到第一个逗号的位置 + int firstCommaIndex = generateDTO.getText().indexOf(","); + if (firstCommaIndex != -1) { + // 截取第一个逗号前的内容作为style + style = generateDTO.getText().substring(0, firstCommaIndex).trim(); + // 截取第一个逗号后的所有内容作为userPrompt(去除首尾空格) + userPrompt = generateDTO.getText().substring(firstCommaIndex + 1).trim(); + + if ("Lolita".equals(style)) { + style = "洛丽塔"; + } + } else { + // 兼容无逗号的情况:style为空,全部内容作为userPrompt + userPrompt = generateDTO.getText().trim(); } - String userPrompt = split[1]; + String prompt = userPrompt + "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.sketch style:"+ style; + " 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."; + + if (!style.trim().isEmpty() && !"all".equalsIgnoreCase(style)) { + prompt += ".sketch style:" + style.trim(); + } + modelAndPromptMap.put(ModelConstants.PROMPT, prompt); if (isUseImage) { if (ModelConstants.ADVANCED.equals(modelName)) { From f02c0930a669c16ca0446c1faae4abd671353a16 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Mon, 23 Mar 2026 13:56:47 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=88=87=E9=9D=A2?= =?UTF-8?q?=EF=BC=88controller=E5=B1=82=E6=8A=A5=E9=94=99=E6=89=93?= =?UTF-8?q?=E5=8D=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 + .../aspect/ControllerLoggingAspect.java | 170 ++++++++++++++++++ .../da/service/impl/GenerateServiceImpl.java | 11 +- 3 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/ai/da/common/aspect/ControllerLoggingAspect.java diff --git a/pom.xml b/pom.xml index db8b8b4d..9c14ce4b 100644 --- a/pom.xml +++ b/pom.xml @@ -427,6 +427,11 @@ bcpkix-jdk18on 1.78.1 + + + org.springframework.boot + spring-boot-starter-aop + diff --git a/src/main/java/com/ai/da/common/aspect/ControllerLoggingAspect.java b/src/main/java/com/ai/da/common/aspect/ControllerLoggingAspect.java new file mode 100644 index 00000000..391a0dda --- /dev/null +++ b/src/main/java/com/ai/da/common/aspect/ControllerLoggingAspect.java @@ -0,0 +1,170 @@ +package com.ai.da.common.aspect; + +import com.ai.da.common.context.UserContext; +import com.ai.da.model.vo.AuthPrincipalVo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import java.util.HashMap; +import java.util.Map; + +/** + * Controller日志切面 + * 记录所有Controller接口的请求参数和用户信息 + */ +@Aspect +@Component +public class ControllerLoggingAspect { + + private static final Logger logger = LoggerFactory.getLogger(ControllerLoggingAspect.class); + + /** + * 定义切点:所有Controller方法 + */ + @Pointcut("execution(* com.ai.da.controller..*(..))") + public void controllerMethods() { + } + + /** + * Controller方法执行前记录日志 + */ +// @Before("controllerMethods()") + public void logControllerBefore(JoinPoint joinPoint) { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes != null) { + HttpServletRequest request = attributes.getRequest(); + + // 获取当前用户ID + Long userId = null; + AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder(); + if (authPrincipalVo != null) { + userId = authPrincipalVo.getId(); + } + + // 获取请求参数 + Map params = getRequestParams(joinPoint, request); + + logger.info("=== 请求开始 ==="); + logger.info("用户ID: {}", userId); + logger.info("请求URL: {}", request.getRequestURL().toString()); + logger.info("请求方法: {}", request.getMethod()); + logger.info("请求IP: {}", getClientIpAddress(request)); + logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName()); + logger.info("请求参数: {}", params); + } + } + + /** + * 获取请求参数 + */ + private Map getRequestParams(JoinPoint joinPoint, HttpServletRequest request) { + Map params = new HashMap<>(); + + // 1. 获取Query String参数 + String queryString = request.getQueryString(); + if (queryString != null && !queryString.isEmpty()) { + params.put("queryString", queryString); + } + + // 2. 获取方法参数(包含 @PathVariable, @RequestParam, @RequestBody 等) + Object[] args = joinPoint.getArgs(); + + if (args != null && args.length > 0) { + Map methodParams = new HashMap<>(); + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + // 过滤掉不可序列化的参数 + if (arg != null) { + if (isIgnorable(arg)) { + // 对于可忽略的类型,记录类型名 + methodParams.put("arg" + i, "[" + arg.getClass().getSimpleName() + "]"); + } else { + try { + methodParams.put("arg" + i, arg); + } catch (Exception e) { + methodParams.put("arg" + i, arg.toString()); + } + } + } + } + if (!methodParams.isEmpty()) { + params.put("methodParams", methodParams); + } + } + + return params; + } + + /** + * 判断是否需要过滤的参数类型 + */ + private boolean isIgnorable(Object obj) { + return obj instanceof HttpServletRequest + || obj instanceof HttpServletResponse + || obj instanceof MultipartFile + || obj instanceof MultipartFile[]; + } + + /** + * Controller方法抛出异常时记录日志 + */ + @AfterThrowing(pointcut = "controllerMethods()", throwing = "exception") + public void logControllerAfterThrowing(JoinPoint joinPoint, Throwable exception) { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + + Long userId = null; + AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder(); + if (authPrincipalVo != null) { + userId = authPrincipalVo.getId(); + } + + // 获取请求参数 + Map params = new HashMap<>(); + if (attributes != null) { + HttpServletRequest request = attributes.getRequest(); + params = getRequestParams(joinPoint, request); + } + + logger.error("=== 请求异常 ==="); + logger.error("用户ID: {}", userId); + logger.error("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName()); + logger.error("请求参数: {}", params); + logger.error("异常信息: ", exception); + logger.error("=== 异常结束 ==="); + } + + /** + * 获取客户端真实IP地址 + */ + private String getClientIpAddress(HttpServletRequest request) { + String xForwardedFor = request.getHeader("X-Forwarded-For"); + if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) { + return xForwardedFor.split(",")[0]; + } + + String xRealIp = request.getHeader("X-Real-IP"); + if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) { + return xRealIp; + } + + String proxyClientIp = request.getHeader("Proxy-Client-IP"); + if (proxyClientIp != null && !proxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(proxyClientIp)) { + return proxyClientIp; + } + + String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP"); + if (wlProxyClientIp != null && !wlProxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(wlProxyClientIp)) { + return wlProxyClientIp; + } + + return request.getRemoteAddr(); + } +} 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 30173584..33ab60de 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -785,8 +785,9 @@ public class GenerateServiceImpl extends ServiceImpl i long requestEndTime = System.currentTimeMillis(); log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}", response.code(), (requestEndTime - requestStartTime), taskId); + String result = response.body().string(); if (!response.isSuccessful()) { - log.warn("Google API响应失败,状态码: {} for taskId: {}", response.code(), taskId); + log.warn("Google API响应失败,状态码: {} for taskId: {},结果:{}", response.code(), taskId,result); if (attempt < maxRetries) { Thread.sleep(retryDelay * attempt); // 递增延迟 continue; @@ -795,7 +796,7 @@ public class GenerateServiceImpl extends ServiceImpl i } } - String result = response.body().string(); + // log.info("Google 响应结果:{}", result); com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); @@ -1065,6 +1066,12 @@ public class GenerateServiceImpl extends ServiceImpl i String result = response.body().string(); + if (response.code() != 200) { + log.error("Google API 请求失败 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 响应结果: {}", + taskId, attempt, endpoint, response.code(), result); + throw new BusinessException("system.error"); + } + // log.info("Google 响应结果:{}", result); com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); From 93429839c0438f4d6cbe56f268c6fb06e3d029e5 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 13:39:38 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E6=9C=AC=E5=9C=B0flux=E6=94=B9=E4=B8=BAf?= =?UTF-8?q?lux2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/da/common/RabbitMQ/MQPublisher.java | 5 ++ .../ai/da/common/constant/CommonConstant.java | 2 +- .../ai/da/model/dto/ImageProcessRequest.java | 55 +++++++++++++++++++ .../java/com/ai/da/python/PythonService.java | 27 ++++++++- .../com/ai/da/service/RabbitMQService.java | 2 + .../da/service/impl/GenerateServiceImpl.java | 13 +++-- .../da/service/impl/RabbitMQServiceImpl.java | 5 ++ 7 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/ai/da/model/dto/ImageProcessRequest.java diff --git a/src/main/java/com/ai/da/common/RabbitMQ/MQPublisher.java b/src/main/java/com/ai/da/common/RabbitMQ/MQPublisher.java index 1312ba83..833df8ca 100644 --- a/src/main/java/com/ai/da/common/RabbitMQ/MQPublisher.java +++ b/src/main/java/com/ai/da/common/RabbitMQ/MQPublisher.java @@ -28,6 +28,11 @@ public class MQPublisher { amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm); } + public void sendGenerateResultMessage(String mm) { + log.info("send generate result message: {}", mm); + amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerateResult(), mm); + } + /** * * @param mailParams 含有的字段 diff --git a/src/main/java/com/ai/da/common/constant/CommonConstant.java b/src/main/java/com/ai/da/common/constant/CommonConstant.java index 4fb6ec16..90d4144b 100644 --- a/src/main/java/com/ai/da/common/constant/CommonConstant.java +++ b/src/main/java/com/ai/da/common/constant/CommonConstant.java @@ -22,7 +22,7 @@ public class CommonConstant { public static final Integer NUMBER_10080 = 10080; } - public static final String GENERATE_PATH = "/api/generate_image"; + public static final String GENERATE_PATH = "/api/generate_image_flux2_klein"; public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo"; diff --git a/src/main/java/com/ai/da/model/dto/ImageProcessRequest.java b/src/main/java/com/ai/da/model/dto/ImageProcessRequest.java new file mode 100644 index 00000000..79380472 --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/ImageProcessRequest.java @@ -0,0 +1,55 @@ +package com.ai.da.model.dto; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * 图片处理请求体 + */ +@Data +@Builder +public class ImageProcessRequest { + + /** + * OSS桶名(bucket_name) + */ + private String bucket_name; + + /** + * OSS对象名(object_name) + */ + private String object_name; + + /** + * 输入图片路径列表(input_image_paths) + */ + private List input_image_paths; + + /** + * 图像宽度(width) + */ + private Integer width; + + /** + * 图像高度(height) + */ + private Integer height; + + /** + * 文本提示(prompt) + */ + private String prompt; + + /** + * 推理步数(steps) + */ + private Integer steps; + + /** + * 引导系数(guidance) + */ + private Double guidance; + +} \ No newline at end of file diff --git a/src/main/java/com/ai/da/python/PythonService.java b/src/main/java/com/ai/da/python/PythonService.java index b95a2804..9645f62f 100644 --- a/src/main/java/com/ai/da/python/PythonService.java +++ b/src/main/java/com/ai/da/python/PythonService.java @@ -2,8 +2,10 @@ package com.ai.da.python; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.exceptions.ExceptionUtil; +import com.ai.da.common.RabbitMQ.RabbitMQProperties; import com.ai.da.common.config.FileProperties; import com.ai.da.common.config.exception.BusinessException; +import com.ai.da.common.constant.CommonConstant; import com.ai.da.common.context.UserContext; import com.ai.da.common.enums.*; import com.ai.da.common.utils.*; @@ -20,6 +22,7 @@ import com.ai.da.model.vo.*; import com.ai.da.python.vo.*; import com.ai.da.service.DesignHistoryService; import com.ai.da.service.PythonTAllInfoService; +import com.ai.da.service.RabbitMQService; import com.ai.da.service.SysFileService; import com.alibaba.fastjson.*; import com.alibaba.fastjson.serializer.SerializerFeature; @@ -84,6 +87,12 @@ public class PythonService { @Resource private RedisUtil redisUtil; + @Resource + private RabbitMQService rabbitMQService; + + @Resource + private RabbitMQProperties rabbitMQProperties; + /** * 生成打印的图片 二合一 (废弃于2024/01/02) * @@ -3334,7 +3343,7 @@ public class PythonService { throw new BusinessException("system error!"); } - public Boolean generateSketchOrPrint(String params, String port, String servicePath) { + public Boolean generateSketchOrPrint(String params, String port, String servicePath,String taskId) { //限流校验 // AccessLimitUtils.validate("generateSketchOrPrint", 5); OkHttpClient client = new OkHttpClient().newBuilder() @@ -3396,6 +3405,22 @@ public class PythonService { if (result && jsonObject.get("code").equals(200)) { log.info("Generate##responseObject###{}", jsonObject); // return setGenerateImageList(jsonObject.getJSONObject("data")); + if (servicePath== CommonConstant.GENERATE_PATH){ + //放入结果到mq + JSONObject data = jsonObject.getJSONObject("data"); + String outputPath = data.getString("output_path"); + + + Map mqMessage = new HashMap<>(); + mqMessage.put("tasks_id", taskId); + mqMessage.put("status", "SUCCESS"); + mqMessage.put("message", "success"); + mqMessage.put("image_url", outputPath); + mqMessage.put("category", ""); + + String mqMessageBody = JSON.toJSONString(mqMessage); + rabbitMQService.publishMessageToGenerateResult(mqMessageBody); + } return Boolean.TRUE; } else { log.info("generateSketchOrPrintPrint失败###{}", jsonObject); diff --git a/src/main/java/com/ai/da/service/RabbitMQService.java b/src/main/java/com/ai/da/service/RabbitMQService.java index 7797c905..bf18b6eb 100644 --- a/src/main/java/com/ai/da/service/RabbitMQService.java +++ b/src/main/java/com/ai/da/service/RabbitMQService.java @@ -7,6 +7,8 @@ public interface RabbitMQService { void publishMessageToGenerate(String message); + void publishMessageToGenerateResult(String message); + void publishMessageToSR(String message); Integer getMessageCount(String queueUrl); 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 6d7907cf..91ef6282 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -243,12 +243,17 @@ public class GenerateServiceImpl extends ServiceImpl i 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); + // 构建object_name: {userId}/{category}/{uuid}.png + String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png"; + + ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder() + .object_name(objectName) + .bucket_name(userBucket) + .prompt(text).build(); + jsonString = JSON.toJSONString(imageProcessRequest, SerializerFeature.WriteMapNullValue); } - Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path); + Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path,generateThroughImageTextDTO.getUniqueId()); // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 save(generate); diff --git a/src/main/java/com/ai/da/service/impl/RabbitMQServiceImpl.java b/src/main/java/com/ai/da/service/impl/RabbitMQServiceImpl.java index 0f0e6dc4..42e920bf 100644 --- a/src/main/java/com/ai/da/service/impl/RabbitMQServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/RabbitMQServiceImpl.java @@ -31,6 +31,11 @@ public class RabbitMQServiceImpl implements RabbitMQService { mqPublisher.sendGenerateMessage(message); } + @Override + public void publishMessageToGenerateResult(String message) { + mqPublisher.sendGenerateResultMessage(message); + } + @Override public void publishMessageToSR(String message) { mqPublisher.sendSRMessage(message); From 0c9d5404c60b26cc8c094c34723d07ef6d14b732 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 14:05:27 +0800 Subject: [PATCH 04/22] =?UTF-8?q?flux2=E5=A4=B1=E8=B4=A5=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ai/da/python/PythonService.java | 10 +++++++++- .../com/ai/da/service/impl/GenerateServiceImpl.java | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ai/da/python/PythonService.java b/src/main/java/com/ai/da/python/PythonService.java index 9645f62f..f20b0ae2 100644 --- a/src/main/java/com/ai/da/python/PythonService.java +++ b/src/main/java/com/ai/da/python/PythonService.java @@ -3417,7 +3417,6 @@ public class PythonService { mqMessage.put("message", "success"); mqMessage.put("image_url", outputPath); mqMessage.put("category", ""); - String mqMessageBody = JSON.toJSONString(mqMessage); rabbitMQService.publishMessageToGenerateResult(mqMessageBody); } @@ -3425,8 +3424,17 @@ public class PythonService { } else { log.info("generateSketchOrPrintPrint失败###{}", jsonObject); log.info("Generate Exception! Code : " + jsonObject.get("code")); + Map mqMessage = new HashMap<>(); + mqMessage.put("tasks_id", taskId); + mqMessage.put("status", "ERROR"); + mqMessage.put("message", ""); + mqMessage.put("image_url", ""); + mqMessage.put("category", ""); + String mqMessageBody = JSON.toJSONString(mqMessage); + rabbitMQService.publishMessageToGenerateResult(mqMessageBody); return Boolean.FALSE; } + } public Response sendPostToModel(String content, String portAndRoute, String functionName) { 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 91ef6282..daed7a7d 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -250,7 +250,7 @@ public class GenerateServiceImpl extends ServiceImpl i .object_name(objectName) .bucket_name(userBucket) .prompt(text).build(); - jsonString = JSON.toJSONString(imageProcessRequest, SerializerFeature.WriteMapNullValue); + jsonString = JSON.toJSONString(imageProcessRequest); } Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path,generateThroughImageTextDTO.getUniqueId()); From bcf51aea231f29fee4c72bd5e478d03c0702e8e8 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 14:20:39 +0800 Subject: [PATCH 05/22] debug --- src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) 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 daed7a7d..f97d489e 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -289,6 +289,8 @@ public class GenerateServiceImpl extends ServiceImpl i @Override @Transactional(rollbackFor = Exception.class) public void processGenerateResult(String taskId, String url, String category) { + log.info("============ProcessGenerateResult listening=========="); + log.debug("taskId: " + taskId); // 1、处理模型返回的数据 GenerateDetail generateDetail = new GenerateDetail(); GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); @@ -331,6 +333,7 @@ public class GenerateServiceImpl extends ServiceImpl i GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category); // 更新redis redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + log.debug("generateResultVO: " + generateResultVO.toString()); // 执行积分扣除 // ** 注:如果生成的图片都是空白 则不扣积分 From dea2b3be424b4f2665ad0931d701adb57cd146fa Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 14:29:08 +0800 Subject: [PATCH 06/22] debug --- .../da/service/impl/GenerateServiceImpl.java | 81 ++++++++++--------- 1 file changed, 44 insertions(+), 37 deletions(-) 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 f97d489e..76026f32 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -291,49 +291,56 @@ public class GenerateServiceImpl extends ServiceImpl i public void processGenerateResult(String taskId, String url, String category) { log.info("============ProcessGenerateResult listening=========="); log.debug("taskId: " + taskId); - // 1、处理模型返回的数据 - GenerateDetail generateDetail = new GenerateDetail(); - GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); - Generate generate; + String status = null; 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; + // 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 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); + log.debug("generateDetail: " + generateDetail.toString()); // 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(); + String key = generateResultKey + ":" + taskId; + String imageName = url.substring(url.lastIndexOf("/") + 1); + 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); + log.debug("generateResultVO: " + generateResultVO.toString()); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); } - GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category); - // 更新redis - redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - log.debug("generateResultVO: " + generateResultVO.toString()); // 执行积分扣除 // ** 注:如果生成的图片都是空白 则不扣积分 From 6e060000835b8fd4ca25814ab4a445500d111243 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 14:46:01 +0800 Subject: [PATCH 07/22] debug --- src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 76026f32..2e7fdaff 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -2050,7 +2050,9 @@ public class GenerateServiceImpl extends ServiceImpl i public Generate selectByUniqueId(String uniqueId) { QueryWrapper qw = new QueryWrapper<>(); qw.eq("unique_id", uniqueId); - + log.debug("selectByUniqueId: " + uniqueId); + Generate one = getOne(qw); + log.debug("Generate: " + one); return getOne(qw); } From 1c5a3a12b913e8904edbb010c68f3de5017b036a Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 15:04:40 +0800 Subject: [PATCH 08/22] debug --- src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2e7fdaff..29ff0987 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -252,11 +252,11 @@ public class GenerateServiceImpl extends ServiceImpl i .prompt(text).build(); jsonString = JSON.toJSONString(imageProcessRequest); } + // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 + save(generate); Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path,generateThroughImageTextDTO.getUniqueId()); - // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 - save(generate); // 5、将本次请求存入redis String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId(); From c970ebe691b2c116439f11b7aaeb0e4fd80a5a34 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 15:15:59 +0800 Subject: [PATCH 09/22] debug --- .../da/service/impl/GenerateServiceImpl.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) 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 29ff0987..675f9c12 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -292,7 +292,6 @@ public class GenerateServiceImpl extends ServiceImpl i log.info("============ProcessGenerateResult listening=========="); log.debug("taskId: " + taskId); String status = null; - try { // 1、处理模型返回的数据 GenerateDetail generateDetail = new GenerateDetail(); GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); @@ -309,18 +308,18 @@ public class GenerateServiceImpl extends ServiceImpl i } } // 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); - } +// 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); + generateDetail.setMd5(""); // 将相应的url保存到数据库 generateDetailMapper.insert(generateDetail); log.debug("generateDetail: " + generateDetail.toString()); @@ -337,10 +336,7 @@ public class GenerateServiceImpl extends ServiceImpl i // 更新redis redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); log.debug("generateResultVO: " + generateResultVO.toString()); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } + // 执行积分扣除 // ** 注:如果生成的图片都是空白 则不扣积分 From 54466b935d7fd6853b8ff0bed58865f526d2a881 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 15:23:33 +0800 Subject: [PATCH 10/22] debug --- .../da/service/impl/GenerateServiceImpl.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) 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 675f9c12..e2f6cfcb 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -59,6 +59,7 @@ 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.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; @@ -253,7 +254,7 @@ public class GenerateServiceImpl extends ServiceImpl i jsonString = JSON.toJSONString(imageProcessRequest); } // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 - save(generate); + saveGenerateImmediately(generate); Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path,generateThroughImageTextDTO.getUniqueId()); @@ -270,6 +271,10 @@ public class GenerateServiceImpl extends ServiceImpl i redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); } + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) + public void saveGenerateImmediately(Generate generate) { + save(generate); + } public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) { if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) { @@ -308,14 +313,14 @@ public class GenerateServiceImpl extends ServiceImpl i } } // 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); -// } + 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()); From 2168978f61aeb7e8539a80f68ec9dfaa71295337 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 15:32:06 +0800 Subject: [PATCH 11/22] =?UTF-8?q?print=20pattern=E4=B9=9F=E6=94=B9?= =?UTF-8?q?=E4=B8=BAflux2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ai/da/service/impl/GenerateServiceImpl.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 e2f6cfcb..4cef593a 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -239,9 +239,14 @@ public class GenerateServiceImpl extends ServiceImpl i 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); + // 构建object_name: {userId}/{category}/{uuid}.png + String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png"; + + ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder() + .object_name(objectName) + .bucket_name(userBucket) + .prompt(text).build(); + jsonString = JSON.toJSONString(imageProcessRequest, SerializerFeature.WriteMapNullValue); } } else { // 构建object_name: {userId}/{category}/{uuid}.png From 9be1a1e30728b39fce17b3a8c738894ba39882a3 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 15:49:16 +0800 Subject: [PATCH 12/22] =?UTF-8?q?=E5=8A=A0=E9=94=81=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E7=BA=BF=E7=A8=8B=E8=AF=BB=E5=8F=96=E5=89=8D?= =?UTF-8?q?=E8=BF=98=E6=9C=AA=E4=BF=9D=E5=AD=98=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../da/service/impl/GenerateServiceImpl.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) 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 4cef593a..87bc3a48 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -219,6 +219,8 @@ public class GenerateServiceImpl extends ServiceImpl i version = "fast"; params.put("version", "fast"); } + // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 + saveGenerateImmediately(generate); // 3.1 确定不同类型的印花分别调哪个接口 if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) { switch (generateThroughImageTextDTO.getLevel2Type()) { @@ -258,8 +260,6 @@ public class GenerateServiceImpl extends ServiceImpl i .prompt(text).build(); jsonString = JSON.toJSONString(imageProcessRequest); } - // 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中 - saveGenerateImmediately(generate); Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path,generateThroughImageTextDTO.getUniqueId()); @@ -276,9 +276,31 @@ public class GenerateServiceImpl extends ServiceImpl i redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); } - @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void saveGenerateImmediately(Generate generate) { save(generate); + // 写入完成后设锁,通知 MQ 消费者可以安全读取 + String lockKey = "generate:lock:" + generate.getUniqueId(); + redisUtil.addToString(lockKey, "1", 60L); + } + + private void waitForSaveLock(String uniqueId) { + String lockKey = "generate:lock:" + uniqueId; + int maxRetries = 30; + int retryIntervalMs = 200; + for (int i = 0; i < maxRetries; i++) { + if (Boolean.TRUE.equals(redisUtil.hasKey(lockKey))) { + log.debug("Save lock acquired for uniqueId: {} after {} retries", uniqueId, i); + return; + } + try { + Thread.sleep(retryIntervalMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.warn("Interrupted while waiting for save lock: {}", uniqueId); + return; + } + } + log.warn("Save lock timeout for uniqueId: {}, proceeding anyway", uniqueId); } public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) { @@ -307,6 +329,8 @@ public class GenerateServiceImpl extends ServiceImpl i GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); Generate generate; try { + // 等待 HTTP 线程写入完成后再查库 + waitForSaveLock(taskId); generate = selectByUniqueId(taskId); } catch (MybatisPlusException e) { log.error(e.getMessage()); From 6757a89d046af0318758e7b26a5dd72111388e0b Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 15:54:53 +0800 Subject: [PATCH 13/22] =?UTF-8?q?Pattern=E6=A8=A1=E5=BC=8F=E5=8F=82?= =?UTF-8?q?=E6=95=B0fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 87bc3a48..3d443151 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -248,7 +248,7 @@ public class GenerateServiceImpl extends ServiceImpl i .object_name(objectName) .bucket_name(userBucket) .prompt(text).build(); - jsonString = JSON.toJSONString(imageProcessRequest, SerializerFeature.WriteMapNullValue); + jsonString = JSON.toJSONString(imageProcessRequest); } } else { // 构建object_name: {userId}/{category}/{uuid}.png From 28656c44c826e5fa94807a46dc8bc64d5a495743 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 24 Mar 2026 16:24:43 +0800 Subject: [PATCH 14/22] FIX FLUX2 --- .../ai/da/common/constant/CommonConstant.java | 3 +- .../com/ai/da/common/utils/RedisUtil.java | 1316 ++++++++--------- .../java/com/ai/da/python/PythonService.java | 2 +- .../da/service/impl/GenerateServiceImpl.java | 27 +- 4 files changed, 677 insertions(+), 671 deletions(-) diff --git a/src/main/java/com/ai/da/common/constant/CommonConstant.java b/src/main/java/com/ai/da/common/constant/CommonConstant.java index 90d4144b..dcd2c51f 100644 --- a/src/main/java/com/ai/da/common/constant/CommonConstant.java +++ b/src/main/java/com/ai/da/common/constant/CommonConstant.java @@ -22,7 +22,8 @@ public class CommonConstant { public static final Integer NUMBER_10080 = 10080; } - public static final String GENERATE_PATH = "/api/generate_image_flux2_klein"; + public static final String GENERATE_PATH = "api/generate_image"; + public static final String GENERATE_PATH_FLUX2_KLEIN = "/api/generate_image_flux2_klein"; public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo"; diff --git a/src/main/java/com/ai/da/common/utils/RedisUtil.java b/src/main/java/com/ai/da/common/utils/RedisUtil.java index 1264a1e4..c91411fa 100644 --- a/src/main/java/com/ai/da/common/utils/RedisUtil.java +++ b/src/main/java/com/ai/da/common/utils/RedisUtil.java @@ -1,659 +1,659 @@ -package com.ai.da.common.utils; - -import com.ai.da.model.dto.ProgressDTO; -import com.ai.da.python.vo.DesignPythonObject; -import com.alibaba.fastjson.JSON; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.netty.util.internal.StringUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ValueOperations; -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.data.redis.core.script.DefaultRedisScript; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import jakarta.annotation.Resource; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -@Slf4j -@Component -public class RedisUtil { - - @Resource - private RedisTemplate redisTemplate; - - public final static String FLUX_POLLING_URL = "Flux:"; - /** - * 登录 token 在 Redis 中的前缀: - * 最终 key 结构为 login:token:{userId} - */ - public final static String LOGIN_TOKEN_KEY = "login:token:"; - - public Boolean hasKey(String key){ - return redisTemplate.hasKey(key); - } - - //- - - - - - - - - - - - - - - - - - - - - ZSet类型 - - - - - - - - - - - - - - - - - - - - - - /** - * 向ZSet中添加元素 - */ - public void addToZSet(String key, String value, Double score) { - redisTemplate.opsForZSet().add(key, value, score); - } - - /** - * 从ZSet中删除元素 - */ - public void removeFromZSet(String key, String value) { - redisTemplate.opsForZSet().remove(key, value); - } - - /** - * 获取指定元素的当前排列顺序 - */ - public Long getRank(String key, String value) { - return redisTemplate.opsForZSet().rank(key, value); - } - - /** - * 获取当前ZSet中的最大score - */ - public Double getMaxScore(String key) { - Set> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, 0); - - if (!CollectionUtils.isEmpty(set)) { - Double score = set.iterator().next().getScore(); - return score + 1.0; - } else { - return 1.0; - } - } - - /** - * 判断元素是否存在 - */ - public Boolean isElementExistsInZSet(String key, String value) { - return redisTemplate.opsForZSet().score(key, value) != null; - } - - /** - * 获取当前ZSet中数据量的总和 - */ - public Long getZSetTotalCount(String key) { - return redisTemplate.opsForZSet().zCard(key); - } - - - public Set getZSetTotalData(String key){ - return redisTemplate.opsForZSet().range(key, 0, -1); - } - - //- - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - - - - - - - - - public final static String VIDEO_FINISHED_TASKS = "VideoFinishedTasks"; - - /** - * 将数据放入set缓存 - */ - public void addToSet(String key, String value, Long expiresIn) { - redisTemplate.opsForSet().add(key, value); - // 设置过期时间 - redisTemplate.expire(key, expiresIn, TimeUnit.SECONDS); - } - - /** - * 弹出变量中的元素 - */ - public void removeFromSet(String key, String value) { - redisTemplate.opsForSet().remove(key, value); - } - - /** - * 检查给定的元素是否在变量中。 - */ - public Boolean isElementExistsInSet(String key, String obj) { - return redisTemplate.opsForSet().isMember(key, obj); - } - - - //- - - - - - - - - - - - - - - - - - - - - hash类型 - - - - - - - - - - - - - - - - - - - - - - /** - * 加入缓存 - */ - public void addToMap(String key, Map map) { - redisTemplate.opsForHash().putAll(key, map); - } - - /** - * 验证指定 key 下 有没有指定的 hashkey - */ - public Boolean isElementExistsInMap(String key, String hashKey) { - return redisTemplate.opsForHash().hasKey(key, hashKey); - } - - /** - * 获取指定key的值string - */ - public String getMapValue(String key1, String key2) { - return String.valueOf(redisTemplate.opsForHash().get(key1, key2)); - } - - /** - * 删除指定 hash 的 HashKey - * - * @return 删除成功的 数量 - */ - public Long removeFromMap(String key, String hashKeys) { - return redisTemplate.opsForHash().delete(key, hashKeys); - } - - //- - - - - - - - - - - - - - - - - - - - - String类型 - - - - - - - - - - - - - - - - - - - - - public void addToString(String key, String value){ - redisTemplate.opsForValue().set(key,value); - } - - public void addToString(String key, String value, Long expiresIn){ - redisTemplate.opsForValue().set(key,value,expiresIn, TimeUnit.SECONDS); - } - - public String getFromString(String key){ - return redisTemplate.opsForValue().get(key); - } - - public Set getKeysFromString(String key){ - return redisTemplate.keys(key); - } - - public Long getSize(String key){return redisTemplate.opsForSet().size(key);} - - public List getMultiValue(Set keys){ - return redisTemplate.opsForValue().multiGet(keys); - } - - public Long getExpire(String key){ - return redisTemplate.getExpire(key); - } - - public void removeFromString(String key){ - redisTemplate.delete(key); - } - - /** - * 保存登录 token - * - * @param userId 用户 ID - * @param token token 字符串 - * @param expireMillis 过期时间(毫秒,通常与 JWT 保持一致) - */ - public void setLoginToken(Long userId, String token, long expireMillis) { - if (expireMillis <= 0) { - // 不设置过期时间,直到手动删除(不推荐) - addToString(LOGIN_TOKEN_KEY + userId, token); - return; - } - long expireSeconds = expireMillis / 1000; - if (expireSeconds <= 0) { - expireSeconds = 1; - } - addToString(LOGIN_TOKEN_KEY + userId, token, expireSeconds); - } - - /** - * 获取登录 token - */ - public String getLoginToken(Long userId) { - return getFromString(LOGIN_TOKEN_KEY + userId); - } - - /** - * 删除登录 token - */ - public void deleteLoginToken(Long userId) { - removeFromString(LOGIN_TOKEN_KEY + userId); - } - - public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:"; - - public void likePost(Long portfolioId, Long userId) { - redisTemplate.opsForSet().add(PORTFOLIO_LIKE_KEY + portfolioId, String.valueOf(userId)); - } - - public Long getLikeCount(Long portfolioId) { - String key = PORTFOLIO_LIKE_KEY + portfolioId; - return redisTemplate.opsForSet().size(key); - } - - public List getLikedPortfolios(Long userId) { - // 获取所有包含PORTFOLIO_LIKE_KEY的键 - Set likedPortfolios = redisTemplate.keys(PORTFOLIO_LIKE_KEY + "*"); - - // 如果没有喜欢的,返回空列表 - if (likedPortfolios == null || likedPortfolios.isEmpty()) { - return new ArrayList<>(); - } - - // 过滤出包含指定用户ID的键,并提取投资组合ID - return likedPortfolios.stream() - .filter(key -> redisTemplate.opsForSet().isMember(key, String.valueOf(userId))) - .map(key -> Long.valueOf(key.replace(PORTFOLIO_LIKE_KEY, ""))) - .collect(Collectors.toList()); - } - - public void unLikePost(Long portfolioId, Long userId) { - redisTemplate.opsForSet().remove(PORTFOLIO_LIKE_KEY + portfolioId, userId.toString()); - } - - // 检查用户是否喜欢某个作品 - public boolean isPostLikedByUser(Long portfolioId, Long userId) { - String key = PORTFOLIO_LIKE_KEY + portfolioId; - Boolean isMember = redisTemplate.opsForSet().isMember(key, userId.toString()); - return isMember != null && isMember; - } - - public final static String PORTFOLIO_VIEW_KEY = "portfolio:view:"; - - public void increaseViewCount(Long portfolioId) { - String key = PORTFOLIO_VIEW_KEY + portfolioId; - redisTemplate.opsForValue().increment(key); - } - - public Long getViewCount(Long portfolioId) { - String key = PORTFOLIO_VIEW_KEY + portfolioId; - return redisTemplate.opsForValue().increment(key, 0); - } - - public Long getViewCount(String key) { - Object value = redisTemplate.opsForValue().get(key); - if (value instanceof Integer) { - return Long.valueOf((Integer) value); - } else if (value instanceof Long) { - return (Long) value; - } else if (value instanceof String) { - return Long.valueOf((String) value); - } else { - throw new IllegalArgumentException("Unexpected value type"); - } - } - - public final static String PERSONAL_HOMEPAGE_VIEW_KEY = "PersonalHomepage:view:"; - - public void increasePersonalHomepageViewCount(Long accountId) { - String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId; - redisTemplate.opsForValue().increment(key); - } - - public Long getPersonalHomepageViewCount(Long accountId) { - String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId; - return redisTemplate.opsForValue().increment(key, 0); - } - - public final static String MOODBOARD_POSITION_KEY = "moodboard:position:"; - - public void saveMoodboardPosition(Long id, String moodboardPosition) { - addToString(MOODBOARD_POSITION_KEY + id, moodboardPosition); - } - - public String getMoodboardPosition(Long id) { - return getFromString(MOODBOARD_POSITION_KEY + id); - } - public final static String NICKNAME_MODIFY_TIMES = "NicknameModifyTimes:"; - public final static String UNNAMED_PROJECT_SEQ = "Project:UnnamedProjectSeq:"; - public Long increaseCount(String key) { - return redisTemplate.opsForValue().increment(key); - } - - public Long getIncrementCount(String key) { - return redisTemplate.opsForValue().increment(key, 0); - } - - public void setKeyExpire(String key, Long expire) { - redisTemplate.expire(key, expire, TimeUnit.DAYS); - } - - public final static String CHANGE_MAILBOX = "ChangeMailbox:"; - - // 每天允许通知3次 - public final static String UPLOAD_TIMEOUT_REMINDER_COUNTER = "UploadTimeoutReminderCounter"; - - public void addProcessId(String processId, int progress) { - // Redis 中的键,可以通过 processId 来唯一标识 - String redisKey = "process:progress:" + processId; - - // 将当前进度存储到 Redis - redisTemplate.opsForValue().set(redisKey, String.valueOf(progress)); - - // 设置过期时间为 5 分钟(300 秒) - redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES); - } - - public void addPathToCache(Long collectionId, Long userId, String path) { - // Redis 中的键,唯一标识由 collectionId 和 userId 组成 - String redisKey = "path:cache:" + collectionId + ":" + userId; - - // 增加路径的计数 - redisTemplate.opsForHash().increment(redisKey, path, 1); - - // 设置过期时间为 2 小时(7200 秒) - redisTemplate.expire(redisKey, 8, TimeUnit.HOURS); - } - - public int getPathUsageCount(Long collectionId, Long userId, String path) { - String redisKey = "path:cache:" + collectionId + ":" + userId; - - // 获取路径的使用次数 - Object count = redisTemplate.opsForHash().get(redisKey, path); - return count != null ? Integer.parseInt(count.toString()) : 0; - } - - public void addAssembledObjects(Long collectionId, Set assembledObjects) { - // Redis 中的键,使用 collectionId 来唯一标识 - String redisKey = "collection:assembledObjects:" + collectionId; - - // 将 assembledObjects 转换为 JSON 格式存储,避免直接存储对象 - String assembledObjectsJson = convertToJson(assembledObjects); - - // 使用 Redis 的 set 操作更新集合 - redisTemplate.opsForValue().set(redisKey, assembledObjectsJson); - - // 设置过期时间为 5 分钟(300 秒) - redisTemplate.expire(redisKey, 30, TimeUnit.MINUTES); - } - - // 将 Set 转换为 JSON 格式 - private String convertToJson(Set assembledObjects) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.writeValueAsString(assembledObjects); - } catch (JsonProcessingException e) { - e.printStackTrace(); - return null; - } - } - - public Set getAssembledObjects(Long collectionId) { - // Redis 中的键,使用 collectionId 来唯一标识 - String redisKey = "collection:assembledObjects:" + collectionId; - - // 从 Redis 获取存储的 JSON 字符串 - String assembledObjectsJson = (String) redisTemplate.opsForValue().get(redisKey); - - if (assembledObjectsJson == null) { - return new HashSet<>(); // 如果没有找到数据,返回一个空的 Set - } - - // 将 JSON 字符串转换为 Set - return convertFromJson(assembledObjectsJson); - } - - // 将 JSON 字符串转换为 Set - private Set convertFromJson(String json) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - // 使用 TypeReference 来指定目标类型是 Set - return objectMapper.readValue(json, new TypeReference>() {}); - } catch (JsonProcessingException e) { - e.printStackTrace(); - return new HashSet<>(); // 如果转换失败,返回空的 Set - } - } - - public final static String PAYMENT_INFO_LAST_SCAN_TIME = "PaymentInfoLastScanTime"; - - public final static String AFFILIATE_LINK_VIEW_KEY = "AffiliateLink:view:"; - - public void increaseAffiliateLinkViewCount(Long accountId) { - String key = AFFILIATE_LINK_VIEW_KEY + accountId; - redisTemplate.opsForValue().increment(key); - } - - public Long getAffiliateLinkViewCount(Long accountId) { - String key = AFFILIATE_LINK_VIEW_KEY + accountId; - return redisTemplate.opsForValue().increment(key, 0); - } - - /** - * 记录任务的耗时到Redis - * @param taskKey 任务标识,如 "taskA" - * @param elapsedTime 本次耗时,单位为毫秒 - */ - public void recordTaskElapsedTime(String taskKey, long elapsedTime) { - String hashKey = "task:stats"; - - // 累加总耗时 - redisTemplate.opsForHash().increment(hashKey, taskKey + ":totalTime", elapsedTime); - - // 增加计数器 - redisTemplate.opsForHash().increment(hashKey, taskKey + ":count", 1); - } - - /** - * 获取任务的平均耗时 - * @param taskKey 任务标识,如 "taskA" - * @return 平均耗时(毫秒) - */ - public double getTaskAverageTime(String taskKey) { - String hashKey = "task:stats"; - - // 获取总耗时和计数 - Object totalTime = redisTemplate.opsForHash().get(hashKey, taskKey + ":totalTime"); - Object count = redisTemplate.opsForHash().get(hashKey, taskKey + ":count"); - - // 计算平均值 - if (totalTime == null || count == null) { - return 0; - } - return Double.parseDouble(totalTime.toString()) / Long.parseLong(count.toString()); - } - - /** - * 清除指定任务的统计数据 - * @param taskKey 任务标识,如 "taskA" - */ - public void clearTaskStats(String taskKey) { - String hashKey = "task:stats"; - - // 删除总耗时和计数器 - redisTemplate.opsForHash().delete(hashKey, taskKey + ":totalTime", taskKey + ":count"); - } - - public void recordTaskElapsedTime(String taskKey, double elapsedTimeInSeconds) { - // 将耗时转换为 BigDecimal,并四舍五入保留四位小数 - BigDecimal elapsedTime = new BigDecimal(elapsedTimeInSeconds).setScale(4, RoundingMode.HALF_UP); - - // 累加总耗时(以毫秒为单位) - redisTemplate.opsForHash().increment("task:stats", taskKey + ":totalTime", elapsedTime.doubleValue()); - - // 增加计数器 - redisTemplate.opsForHash().increment("task:stats", taskKey + ":count", 1); - } - - // 获取第一部分(Sketch)耗时 - public double getFirstSketchTime() { - // 获取 "firstSketchTime:totalTime" 对应的值,并返回(单位为秒) - Object time = redisTemplate.opsForHash().get("task:stats", "firstSketchTime:totalTime"); - return time != null ? (double) time : 0.0; - } - - // 获取第二部分(获取特征值)耗时 - public double getGetAttributeRecognitionTime() { - // 获取 "getAttributeRecognitionTime:totalTime" 对应的值,并返回(单位为秒) - Object time = redisTemplate.opsForHash().get("task:stats", "getAttributeRecognitionTime:totalTime"); - return time != null ? (double) time : 0.0; - } - - // 获取第三部分(搭配 Sketch)耗时 - public double getOtherSketchTime() { - // 获取 "otherSketchTime:totalTime" 对应的值,并返回(单位为秒) - Object time = redisTemplate.opsForHash().get("task:stats", "otherSketchTime:totalTime"); - return time != null ? (double) time : 0.0; - } - - // 清理三部分的缓存 - public void clearTaskElapsedTimeCache() { - // 删除第一部分的缓存 - redisTemplate.opsForHash().delete("task:stats", "firstSketchTime:totalTime"); - redisTemplate.opsForHash().delete("task:stats", "firstSketchTime:count"); - - // 删除第二部分的缓存 - redisTemplate.opsForHash().delete("task:stats", "getAttributeRecognitionTime:totalTime"); - redisTemplate.opsForHash().delete("task:stats", "getAttributeRecognitionTime:count"); - - // 删除第三部分的缓存 - redisTemplate.opsForHash().delete("task:stats", "otherSketchTime:totalTime"); - redisTemplate.opsForHash().delete("task:stats", "otherSketchTime:count"); - } - - public boolean incrementLikeCount(Long userId, String sketchPath) { - String redisKey = "user_like_count:" + userId; - try { - redisTemplate.opsForHash().increment(redisKey, sketchPath, 1); - return true; - } catch (Exception e) { - log.error("Error incrementing like count for userId {} and sketchPath {}: {}", userId, sketchPath, e.getMessage()); - return false; - } - } - - public int getLikeCount(Long userId, String sketchPath) { - String redisKey = "user_like_count:" + userId; - Object count = redisTemplate.opsForHash().get(redisKey, sketchPath); - return count != null ? Integer.parseInt(count.toString()) : 0; - } - - public void storeMaxLikeCount(Long userId, int maxLikeCount) { - String redisKey = "user_max_like_count:" + userId; - redisTemplate.opsForValue().set(redisKey, String.valueOf(maxLikeCount)); - } - - public int getMaxLikeCount(Long userId) { - String redisKey = "user_max_like_count:" + userId; - String maxLikeCount = redisTemplate.opsForValue().get(redisKey); - return maxLikeCount != null ? Integer.parseInt(maxLikeCount) : 0; - } - - public final static String IMAGE_SEGMENTATION = "ImageSegmentation:"; - - public final static String STRIPE_EXCEPTION_LOG = "StripeException:"; - public final static String SUBSCRIPTION_SENT_EMAIL_TYPE = "SubscriptionEmailSentType:"; - - public void batchDeleteKeysWithSamePrefix(String prefix){ - Set keys = redisTemplate.keys(prefix + "*"); - assert keys != null; - if (!keys.isEmpty()){ - redisTemplate.delete(keys); - } - } - - public void setTaskProgressDTO(String taskId, ProgressDTO dto) { - String key = "task:progress:" + taskId; - redisTemplate.opsForValue().set(key, JSON.toJSONString(dto), Duration.ofDays(1)); - } - - public ProgressDTO getTaskProgressDTO(String taskId) { - String key = "task:progress:" + taskId; - String json = redisTemplate.opsForValue().get(key); - if (StringUtils.isBlank(json)) { -// return new ProgressDTO(0, 0, false); - return null; - } - try { - return JSON.parseObject(json, ProgressDTO.class); - } catch (Exception e) { - log.warn("任务进度解析失败 key={}, json={}", key, json); - return new ProgressDTO(0, 0, false, null); - } - } - - // Lua脚本(原子化操作) - /*private static final String RATE_LIMIT_SCRIPT = - "local current = redis.call('INCR', KEYS[1])\n" + - "if tonumber(current) == 1 then\n" + - " redis.call('EXPIRE', KEYS[1], ARGV[1])\n" + - "end\n" + - "return tonumber(current) <= tonumber(ARGV[2])";*/ - private static final String RATE_LIMIT_SCRIPT = - "local current = redis.call('INCR', KEYS[1])\n" + - "local ttl = redis.call('TTL', KEYS[1])\n" + - "if tonumber(current) == 1 or tonumber(ttl) == -1 then\n" + - " redis.call('EXPIRE', KEYS[1], ARGV[1])\n" + - "end\n" + - "return tonumber(current) <= tonumber(ARGV[2])"; - - /** - * 检查是否允许发送 - * @param userId 用户ID - * @return true-允许发送,false-已超限 - */ - public boolean allowSend(Long userId) { - String hourKey = getCurrentHourKey(userId); - - // 执行Lua脚本 - List keys = Collections.singletonList(hourKey); - List args = Arrays.asList( - 3600L, // 1小时过期 - 10L // 限制数量 一小时只能向普通用户发10封 - ); - - Boolean result = redisTemplate.execute( - new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Boolean.class), - keys, - args.toArray() - ); - - return Boolean.TRUE.equals(result); - } - - /** - * 获取当前小时的Key - * 格式:email_limit:{userId}:{yyyyMMddHH} - */ - private String getCurrentHourKey(Long userId) { - String hour = LocalDateTime.now() - .format(DateTimeFormatter.ofPattern("yyyyMMddHH")); - return String.format("email_limit:%s:%s", userId, hour); - } - - /** - * 获取当前已发送数量 - */ - public int getCurrentCount(Long userId) { - String key = getCurrentHourKey(userId); - String val = redisTemplate.opsForValue().get(key); - int count; - if (StringUtils.isBlank(val)){ - count = 0; - }else { - count = Integer.parseInt(val); - } - return count; - } - - public boolean allowRequest(String apiKey) { - String key = "rate_limit:" + apiKey; - ValueOperations ops = redisTemplate.opsForValue(); - - // 使用Redis的INCR命令 - Long count = ops.increment(key, 1); - - if (count == 1) { - // 第一次调用,设置过期时间 - redisTemplate.expire(key, 1, TimeUnit.MINUTES); - } - - return count <= 3; - } - +package com.ai.da.common.utils; + +import com.ai.da.model.dto.ProgressDTO; +import com.ai.da.python.vo.DesignPythonObject; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.util.internal.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import jakarta.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class RedisUtil { + + @Resource + private RedisTemplate redisTemplate; + + public final static String FLUX_POLLING_URL = "Flux:"; + /** + * 登录 token 在 Redis 中的前缀: + * 最终 key 结构为 login:token:{userId} + */ + public final static String LOGIN_TOKEN_KEY = "login:token:"; + + public Boolean hasKey(String key){ + return redisTemplate.hasKey(key); + } + + //- - - - - - - - - - - - - - - - - - - - - ZSet类型 - - - - - - - - - - - - - - - - - - - - + + /** + * 向ZSet中添加元素 + */ + public void addToZSet(String key, String value, Double score) { + redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * 从ZSet中删除元素 + */ + public void removeFromZSet(String key, String value) { + redisTemplate.opsForZSet().remove(key, value); + } + + /** + * 获取指定元素的当前排列顺序 + */ + public Long getRank(String key, String value) { + return redisTemplate.opsForZSet().rank(key, value); + } + + /** + * 获取当前ZSet中的最大score + */ + public Double getMaxScore(String key) { + Set> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, 0); + + if (!CollectionUtils.isEmpty(set)) { + Double score = set.iterator().next().getScore(); + return score + 1.0; + } else { + return 1.0; + } + } + + /** + * 判断元素是否存在 + */ + public Boolean isElementExistsInZSet(String key, String value) { + return redisTemplate.opsForZSet().score(key, value) != null; + } + + /** + * 获取当前ZSet中数据量的总和 + */ + public Long getZSetTotalCount(String key) { + return redisTemplate.opsForZSet().zCard(key); + } + + + public Set getZSetTotalData(String key){ + return redisTemplate.opsForZSet().range(key, 0, -1); + } + + //- - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - - - - - - - + + public final static String VIDEO_FINISHED_TASKS = "VideoFinishedTasks"; + + /** + * 将数据放入set缓存 + */ + public void addToSet(String key, String value, Long expiresIn) { + redisTemplate.opsForSet().add(key, value); + // 设置过期时间 + redisTemplate.expire(key, expiresIn, TimeUnit.SECONDS); + } + + /** + * 弹出变量中的元素 + */ + public void removeFromSet(String key, String value) { + redisTemplate.opsForSet().remove(key, value); + } + + /** + * 检查给定的元素是否在变量中。 + */ + public Boolean isElementExistsInSet(String key, String obj) { + return redisTemplate.opsForSet().isMember(key, obj); + } + + + //- - - - - - - - - - - - - - - - - - - - - hash类型 - - - - - - - - - - - - - - - - - - - - + + /** + * 加入缓存 + */ + public void addToMap(String key, Map map) { + redisTemplate.opsForHash().putAll(key, map); + } + + /** + * 验证指定 key 下 有没有指定的 hashkey + */ + public Boolean isElementExistsInMap(String key, String hashKey) { + return redisTemplate.opsForHash().hasKey(key, hashKey); + } + + /** + * 获取指定key的值string + */ + public String getMapValue(String key1, String key2) { + return String.valueOf(redisTemplate.opsForHash().get(key1, key2)); + } + + /** + * 删除指定 hash 的 HashKey + * + * @return 删除成功的 数量 + */ + public Long removeFromMap(String key, String hashKeys) { + return redisTemplate.opsForHash().delete(key, hashKeys); + } + + //- - - - - - - - - - - - - - - - - - - - - String类型 - - - - - - - - - - - - - - - - - - - - + public void addToString(String key, String value){ + redisTemplate.opsForValue().set(key,value); + } + + public void addToString(String key, String value, Long expiresIn){ + redisTemplate.opsForValue().set(key,value,expiresIn, TimeUnit.SECONDS); + } + + public String getFromString(String key){ + return redisTemplate.opsForValue().get(key); + } + + public Set getKeysFromString(String key){ + return redisTemplate.keys(key); + } + + public Long getSize(String key){return redisTemplate.opsForSet().size(key);} + + public List getMultiValue(Set keys){ + return redisTemplate.opsForValue().multiGet(keys); + } + + public Long getExpire(String key){ + return redisTemplate.getExpire(key); + } + + public void removeFromString(String key){ + redisTemplate.delete(key); + } + + /** + * 保存登录 token + * + * @param userId 用户 ID + * @param token token 字符串 + * @param expireMillis 过期时间(毫秒,通常与 JWT 保持一致) + */ + public void setLoginToken(Long userId, String token, long expireMillis) { + if (expireMillis <= 0) { + // 不设置过期时间,直到手动删除(不推荐) + addToString(LOGIN_TOKEN_KEY + userId, token); + return; + } + long expireSeconds = expireMillis / 1000; + if (expireSeconds <= 0) { + expireSeconds = 1; + } + addToString(LOGIN_TOKEN_KEY + userId, token, expireSeconds); + } + + /** + * 获取登录 token + */ + public String getLoginToken(Long userId) { + return getFromString(LOGIN_TOKEN_KEY + userId); + } + + /** + * 删除登录 token + */ + public void deleteLoginToken(Long userId) { + removeFromString(LOGIN_TOKEN_KEY + userId); + } + + public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:"; + + public void likePost(Long portfolioId, Long userId) { + redisTemplate.opsForSet().add(PORTFOLIO_LIKE_KEY + portfolioId, String.valueOf(userId)); + } + + public Long getLikeCount(Long portfolioId) { + String key = PORTFOLIO_LIKE_KEY + portfolioId; + return redisTemplate.opsForSet().size(key); + } + + public List getLikedPortfolios(Long userId) { + // 获取所有包含PORTFOLIO_LIKE_KEY的键 + Set likedPortfolios = redisTemplate.keys(PORTFOLIO_LIKE_KEY + "*"); + + // 如果没有喜欢的,返回空列表 + if (likedPortfolios == null || likedPortfolios.isEmpty()) { + return new ArrayList<>(); + } + + // 过滤出包含指定用户ID的键,并提取投资组合ID + return likedPortfolios.stream() + .filter(key -> redisTemplate.opsForSet().isMember(key, String.valueOf(userId))) + .map(key -> Long.valueOf(key.replace(PORTFOLIO_LIKE_KEY, ""))) + .collect(Collectors.toList()); + } + + public void unLikePost(Long portfolioId, Long userId) { + redisTemplate.opsForSet().remove(PORTFOLIO_LIKE_KEY + portfolioId, userId.toString()); + } + + // 检查用户是否喜欢某个作品 + public boolean isPostLikedByUser(Long portfolioId, Long userId) { + String key = PORTFOLIO_LIKE_KEY + portfolioId; + Boolean isMember = redisTemplate.opsForSet().isMember(key, userId.toString()); + return isMember != null && isMember; + } + + public final static String PORTFOLIO_VIEW_KEY = "portfolio:view:"; + + public void increaseViewCount(Long portfolioId) { + String key = PORTFOLIO_VIEW_KEY + portfolioId; + redisTemplate.opsForValue().increment(key); + } + + public Long getViewCount(Long portfolioId) { + String key = PORTFOLIO_VIEW_KEY + portfolioId; + return redisTemplate.opsForValue().increment(key, 0); + } + + public Long getViewCount(String key) { + Object value = redisTemplate.opsForValue().get(key); + if (value instanceof Integer) { + return Long.valueOf((Integer) value); + } else if (value instanceof Long) { + return (Long) value; + } else if (value instanceof String) { + return Long.valueOf((String) value); + } else { + throw new IllegalArgumentException("Unexpected value type"); + } + } + + public final static String PERSONAL_HOMEPAGE_VIEW_KEY = "PersonalHomepage:view:"; + + public void increasePersonalHomepageViewCount(Long accountId) { + String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId; + redisTemplate.opsForValue().increment(key); + } + + public Long getPersonalHomepageViewCount(Long accountId) { + String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId; + return redisTemplate.opsForValue().increment(key, 0); + } + + public final static String MOODBOARD_POSITION_KEY = "moodboard:position:"; + + public void saveMoodboardPosition(Long id, String moodboardPosition) { + addToString(MOODBOARD_POSITION_KEY + id, moodboardPosition); + } + + public String getMoodboardPosition(Long id) { + return getFromString(MOODBOARD_POSITION_KEY + id); + } + public final static String NICKNAME_MODIFY_TIMES = "NicknameModifyTimes:"; + public final static String UNNAMED_PROJECT_SEQ = "Project:UnnamedProjectSeq:"; + public Long increaseCount(String key) { + return redisTemplate.opsForValue().increment(key); + } + + public Long getIncrementCount(String key) { + return redisTemplate.opsForValue().increment(key, 0); + } + + public void setKeyExpire(String key, Long expire) { + redisTemplate.expire(key, expire, TimeUnit.DAYS); + } + + public final static String CHANGE_MAILBOX = "ChangeMailbox:"; + + // 每天允许通知3次 + public final static String UPLOAD_TIMEOUT_REMINDER_COUNTER = "UploadTimeoutReminderCounter"; + + public void addProcessId(String processId, int progress) { + // Redis 中的键,可以通过 processId 来唯一标识 + String redisKey = "process:progress:" + processId; + + // 将当前进度存储到 Redis + redisTemplate.opsForValue().set(redisKey, String.valueOf(progress)); + + // 设置过期时间为 5 分钟(300 秒) + redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES); + } + + public void addPathToCache(Long collectionId, Long userId, String path) { + // Redis 中的键,唯一标识由 collectionId 和 userId 组成 + String redisKey = "path:cache:" + collectionId + ":" + userId; + + // 增加路径的计数 + redisTemplate.opsForHash().increment(redisKey, path, 1); + + // 设置过期时间为 2 小时(7200 秒) + redisTemplate.expire(redisKey, 8, TimeUnit.HOURS); + } + + public int getPathUsageCount(Long collectionId, Long userId, String path) { + String redisKey = "path:cache:" + collectionId + ":" + userId; + + // 获取路径的使用次数 + Object count = redisTemplate.opsForHash().get(redisKey, path); + return count != null ? Integer.parseInt(count.toString()) : 0; + } + + public void addAssembledObjects(Long collectionId, Set assembledObjects) { + // Redis 中的键,使用 collectionId 来唯一标识 + String redisKey = "collection:assembledObjects:" + collectionId; + + // 将 assembledObjects 转换为 JSON 格式存储,避免直接存储对象 + String assembledObjectsJson = convertToJson(assembledObjects); + + // 使用 Redis 的 set 操作更新集合 + redisTemplate.opsForValue().set(redisKey, assembledObjectsJson); + + // 设置过期时间为 5 分钟(300 秒) + redisTemplate.expire(redisKey, 30, TimeUnit.MINUTES); + } + + // 将 Set 转换为 JSON 格式 + private String convertToJson(Set assembledObjects) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(assembledObjects); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } + + public Set getAssembledObjects(Long collectionId) { + // Redis 中的键,使用 collectionId 来唯一标识 + String redisKey = "collection:assembledObjects:" + collectionId; + + // 从 Redis 获取存储的 JSON 字符串 + String assembledObjectsJson = (String) redisTemplate.opsForValue().get(redisKey); + + if (assembledObjectsJson == null) { + return new HashSet<>(); // 如果没有找到数据,返回一个空的 Set + } + + // 将 JSON 字符串转换为 Set + return convertFromJson(assembledObjectsJson); + } + + // 将 JSON 字符串转换为 Set + private Set convertFromJson(String json) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + // 使用 TypeReference 来指定目标类型是 Set + return objectMapper.readValue(json, new TypeReference>() {}); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new HashSet<>(); // 如果转换失败,返回空的 Set + } + } + + public final static String PAYMENT_INFO_LAST_SCAN_TIME = "PaymentInfoLastScanTime"; + + public final static String AFFILIATE_LINK_VIEW_KEY = "AffiliateLink:view:"; + + public void increaseAffiliateLinkViewCount(Long accountId) { + String key = AFFILIATE_LINK_VIEW_KEY + accountId; + redisTemplate.opsForValue().increment(key); + } + + public Long getAffiliateLinkViewCount(Long accountId) { + String key = AFFILIATE_LINK_VIEW_KEY + accountId; + return redisTemplate.opsForValue().increment(key, 0); + } + + /** + * 记录任务的耗时到Redis + * @param taskKey 任务标识,如 "taskA" + * @param elapsedTime 本次耗时,单位为毫秒 + */ + public void recordTaskElapsedTime(String taskKey, long elapsedTime) { + String hashKey = "task:stats"; + + // 累加总耗时 + redisTemplate.opsForHash().increment(hashKey, taskKey + ":totalTime", elapsedTime); + + // 增加计数器 + redisTemplate.opsForHash().increment(hashKey, taskKey + ":count", 1); + } + + /** + * 获取任务的平均耗时 + * @param taskKey 任务标识,如 "taskA" + * @return 平均耗时(毫秒) + */ + public double getTaskAverageTime(String taskKey) { + String hashKey = "task:stats"; + + // 获取总耗时和计数 + Object totalTime = redisTemplate.opsForHash().get(hashKey, taskKey + ":totalTime"); + Object count = redisTemplate.opsForHash().get(hashKey, taskKey + ":count"); + + // 计算平均值 + if (totalTime == null || count == null) { + return 0; + } + return Double.parseDouble(totalTime.toString()) / Long.parseLong(count.toString()); + } + + /** + * 清除指定任务的统计数据 + * @param taskKey 任务标识,如 "taskA" + */ + public void clearTaskStats(String taskKey) { + String hashKey = "task:stats"; + + // 删除总耗时和计数器 + redisTemplate.opsForHash().delete(hashKey, taskKey + ":totalTime", taskKey + ":count"); + } + + public void recordTaskElapsedTime(String taskKey, double elapsedTimeInSeconds) { + // 将耗时转换为 BigDecimal,并四舍五入保留四位小数 + BigDecimal elapsedTime = new BigDecimal(elapsedTimeInSeconds).setScale(4, RoundingMode.HALF_UP); + + // 累加总耗时(以毫秒为单位) + redisTemplate.opsForHash().increment("task:stats", taskKey + ":totalTime", elapsedTime.doubleValue()); + + // 增加计数器 + redisTemplate.opsForHash().increment("task:stats", taskKey + ":count", 1); + } + + // 获取第一部分(Sketch)耗时 + public double getFirstSketchTime() { + // 获取 "firstSketchTime:totalTime" 对应的值,并返回(单位为秒) + Object time = redisTemplate.opsForHash().get("task:stats", "firstSketchTime:totalTime"); + return time != null ? (double) time : 0.0; + } + + // 获取第二部分(获取特征值)耗时 + public double getGetAttributeRecognitionTime() { + // 获取 "getAttributeRecognitionTime:totalTime" 对应的值,并返回(单位为秒) + Object time = redisTemplate.opsForHash().get("task:stats", "getAttributeRecognitionTime:totalTime"); + return time != null ? (double) time : 0.0; + } + + // 获取第三部分(搭配 Sketch)耗时 + public double getOtherSketchTime() { + // 获取 "otherSketchTime:totalTime" 对应的值,并返回(单位为秒) + Object time = redisTemplate.opsForHash().get("task:stats", "otherSketchTime:totalTime"); + return time != null ? (double) time : 0.0; + } + + // 清理三部分的缓存 + public void clearTaskElapsedTimeCache() { + // 删除第一部分的缓存 + redisTemplate.opsForHash().delete("task:stats", "firstSketchTime:totalTime"); + redisTemplate.opsForHash().delete("task:stats", "firstSketchTime:count"); + + // 删除第二部分的缓存 + redisTemplate.opsForHash().delete("task:stats", "getAttributeRecognitionTime:totalTime"); + redisTemplate.opsForHash().delete("task:stats", "getAttributeRecognitionTime:count"); + + // 删除第三部分的缓存 + redisTemplate.opsForHash().delete("task:stats", "otherSketchTime:totalTime"); + redisTemplate.opsForHash().delete("task:stats", "otherSketchTime:count"); + } + + public boolean incrementLikeCount(Long userId, String sketchPath) { + String redisKey = "user_like_count:" + userId; + try { + redisTemplate.opsForHash().increment(redisKey, sketchPath, 1); + return true; + } catch (Exception e) { + log.error("Error incrementing like count for userId {} and sketchPath {}: {}", userId, sketchPath, e.getMessage()); + return false; + } + } + + public int getLikeCount(Long userId, String sketchPath) { + String redisKey = "user_like_count:" + userId; + Object count = redisTemplate.opsForHash().get(redisKey, sketchPath); + return count != null ? Integer.parseInt(count.toString()) : 0; + } + + public void storeMaxLikeCount(Long userId, int maxLikeCount) { + String redisKey = "user_max_like_count:" + userId; + redisTemplate.opsForValue().set(redisKey, String.valueOf(maxLikeCount)); + } + + public int getMaxLikeCount(Long userId) { + String redisKey = "user_max_like_count:" + userId; + String maxLikeCount = redisTemplate.opsForValue().get(redisKey); + return maxLikeCount != null ? Integer.parseInt(maxLikeCount) : 0; + } + + public final static String IMAGE_SEGMENTATION = "ImageSegmentation:"; + + public final static String STRIPE_EXCEPTION_LOG = "StripeException:"; + public final static String SUBSCRIPTION_SENT_EMAIL_TYPE = "SubscriptionEmailSentType:"; + + public void batchDeleteKeysWithSamePrefix(String prefix){ + Set keys = redisTemplate.keys(prefix + "*"); + assert keys != null; + if (!keys.isEmpty()){ + redisTemplate.delete(keys); + } + } + + public void setTaskProgressDTO(String taskId, ProgressDTO dto) { + String key = "task:progress:" + taskId; + redisTemplate.opsForValue().set(key, JSON.toJSONString(dto), Duration.ofDays(1)); + } + + public ProgressDTO getTaskProgressDTO(String taskId) { + String key = "task:progress:" + taskId; + String json = redisTemplate.opsForValue().get(key); + if (StringUtils.isBlank(json)) { +// return new ProgressDTO(0, 0, false); + return null; + } + try { + return JSON.parseObject(json, ProgressDTO.class); + } catch (Exception e) { + log.warn("任务进度解析失败 key={}, json={}", key, json); + return new ProgressDTO(0, 0, false, null); + } + } + + // Lua脚本(原子化操作) + /*private static final String RATE_LIMIT_SCRIPT = + "local current = redis.call('INCR', KEYS[1])\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('EXPIRE', KEYS[1], ARGV[1])\n" + + "end\n" + + "return tonumber(current) <= tonumber(ARGV[2])";*/ + private static final String RATE_LIMIT_SCRIPT = + "local current = redis.call('INCR', KEYS[1])\n" + + "local ttl = redis.call('TTL', KEYS[1])\n" + + "if tonumber(current) == 1 or tonumber(ttl) == -1 then\n" + + " redis.call('EXPIRE', KEYS[1], ARGV[1])\n" + + "end\n" + + "return tonumber(current) <= tonumber(ARGV[2])"; + + /** + * 检查是否允许发送 + * @param userId 用户ID + * @return true-允许发送,false-已超限 + */ + public boolean allowSend(Long userId) { + String hourKey = getCurrentHourKey(userId); + + // 执行Lua脚本 + List keys = Collections.singletonList(hourKey); + List args = Arrays.asList( + 3600L, // 1小时过期 + 10L // 限制数量 一小时只能向普通用户发10封 + ); + + Boolean result = redisTemplate.execute( + new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Boolean.class), + keys, + args.toArray() + ); + + return Boolean.TRUE.equals(result); + } + + /** + * 获取当前小时的Key + * 格式:email_limit:{userId}:{yyyyMMddHH} + */ + private String getCurrentHourKey(Long userId) { + String hour = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("yyyyMMddHH")); + return String.format("email_limit:%s:%s", userId, hour); + } + + /** + * 获取当前已发送数量 + */ + public int getCurrentCount(Long userId) { + String key = getCurrentHourKey(userId); + String val = redisTemplate.opsForValue().get(key); + int count; + if (StringUtils.isBlank(val)){ + count = 0; + }else { + count = Integer.parseInt(val); + } + return count; + } + + public boolean allowRequest(String apiKey) { + String key = "rate_limit:" + apiKey; + ValueOperations ops = redisTemplate.opsForValue(); + + // 使用Redis的INCR命令 + Long count = ops.increment(key, 1); + + if (count == 1) { + // 第一次调用,设置过期时间 + redisTemplate.expire(key, 1, TimeUnit.MINUTES); + } + + return count <= 3; + } + } \ No newline at end of file diff --git a/src/main/java/com/ai/da/python/PythonService.java b/src/main/java/com/ai/da/python/PythonService.java index f20b0ae2..3ffad287 100644 --- a/src/main/java/com/ai/da/python/PythonService.java +++ b/src/main/java/com/ai/da/python/PythonService.java @@ -3405,7 +3405,7 @@ public class PythonService { if (result && jsonObject.get("code").equals(200)) { log.info("Generate##responseObject###{}", jsonObject); // return setGenerateImageList(jsonObject.getJSONObject("data")); - if (servicePath== CommonConstant.GENERATE_PATH){ + if (servicePath== CommonConstant.GENERATE_PATH_FLUX2_KLEIN){ //放入结果到mq JSONObject data = jsonObject.getJSONObject("data"); String outputPath = data.getString("output_path"); 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 3d443151..42f7a5c7 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -48,6 +48,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.apache.commons.io.IOUtils; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -241,16 +243,12 @@ public class GenerateServiceImpl extends ServiceImpl i jsonString = JSON.toJSONString(params, SerializerFeature.WriteMapNullValue); break; case "Pattern": - // 构建object_name: {userId}/{category}/{uuid}.png - String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png"; - - ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder() - .object_name(objectName) - .bucket_name(userBucket) - .prompt(text).build(); - jsonString = JSON.toJSONString(imageProcessRequest); + GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), + mode, category, generateThroughImageTextDTO.getGender(), version); + jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); } } else { + path = CommonConstant.GENERATE_PATH_FLUX2_KLEIN; // 构建object_name: {userId}/{category}/{uuid}.png String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png"; @@ -278,9 +276,16 @@ public class GenerateServiceImpl extends ServiceImpl i } public void saveGenerateImmediately(Generate generate) { save(generate); - // 写入完成后设锁,通知 MQ 消费者可以安全读取 - String lockKey = "generate:lock:" + generate.getUniqueId(); - redisUtil.addToString(lockKey, "1", 60L); + // 使用 TransactionSynchronizationManager 在事务真正提交后再设锁 + // 否则 save() 完成后事务尚未 commit,MQ 消费者立即读到 null + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + String lockKey = "generate:lock:" + generate.getUniqueId(); + redisUtil.addToString(lockKey, "1", 60L); + log.debug("Save lock set after commit for uniqueId: {}", generate.getUniqueId()); + } + }); } private void waitForSaveLock(String uniqueId) { From cb6f94d2d491249e3f7350d77b13068622ea3244 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Wed, 25 Mar 2026 10:19:06 +0800 Subject: [PATCH 15/22] py api fix --- src/main/java/com/ai/da/common/constant/CommonConstant.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ai/da/common/constant/CommonConstant.java b/src/main/java/com/ai/da/common/constant/CommonConstant.java index dcd2c51f..9f7ac8bc 100644 --- a/src/main/java/com/ai/da/common/constant/CommonConstant.java +++ b/src/main/java/com/ai/da/common/constant/CommonConstant.java @@ -22,7 +22,7 @@ public class CommonConstant { public static final Integer NUMBER_10080 = 10080; } - public static final String GENERATE_PATH = "api/generate_image"; + public static final String GENERATE_PATH = "/api/generate_image"; public static final String GENERATE_PATH_FLUX2_KLEIN = "/api/generate_image_flux2_klein"; public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo"; From 76eeb2be534b3e00bc85e50f89bba4b46a7806f7 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Wed, 25 Mar 2026 10:39:22 +0800 Subject: [PATCH 16/22] =?UTF-8?q?moodboard=E5=9F=BA=E7=A1=80=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../da/service/impl/GenerateServiceImpl.java | 133 ++++++++++-------- 1 file changed, 72 insertions(+), 61 deletions(-) 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 42f7a5c7..2bab0e43 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -66,6 +66,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import jakarta.annotation.Resource; + import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; @@ -248,18 +249,27 @@ public class GenerateServiceImpl extends ServiceImpl i jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); } } else { - path = CommonConstant.GENERATE_PATH_FLUX2_KLEIN; - // 构建object_name: {userId}/{category}/{uuid}.png - String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png"; + if (Objects.equals(version, "fast")) { + GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), + mode, category, generateThroughImageTextDTO.getGender(), version); + jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); + } else { + + + path = CommonConstant.GENERATE_PATH_FLUX2_KLEIN; + // 构建object_name: {userId}/{category}/{uuid}.png + String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png"; + + ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder() + .object_name(objectName) + .bucket_name(userBucket) + .prompt(text).build(); + jsonString = JSON.toJSONString(imageProcessRequest); + } - ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder() - .object_name(objectName) - .bucket_name(userBucket) - .prompt(text).build(); - jsonString = JSON.toJSONString(imageProcessRequest); } - Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path,generateThroughImageTextDTO.getUniqueId()); + Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path, generateThroughImageTextDTO.getUniqueId()); // 5、将本次请求存入redis @@ -274,6 +284,7 @@ public class GenerateServiceImpl extends ServiceImpl i redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); } + public void saveGenerateImmediately(Generate generate) { save(generate); // 使用 TransactionSynchronizationManager 在事务真正提交后再设锁 @@ -329,52 +340,52 @@ public class GenerateServiceImpl extends ServiceImpl i log.info("============ProcessGenerateResult listening=========="); log.debug("taskId: " + taskId); String status = null; - // 1、处理模型返回的数据 - GenerateDetail generateDetail = new GenerateDetail(); - GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); - Generate generate; - try { - // 等待 HTTP 线程写入完成后再查库 - waitForSaveLock(taskId); - 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; - } + // 1、处理模型返回的数据 + GenerateDetail generateDetail = new GenerateDetail(); + GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); + Generate generate; + try { + // 等待 HTTP 线程写入完成后再查库 + waitForSaveLock(taskId); + 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(""); - // 将相应的url保存到数据库 - generateDetailMapper.insert(generateDetail); - log.debug("generateDetail: " + generateDetail.toString()); + 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(""); + // 将相应的url保存到数据库 + generateDetailMapper.insert(generateDetail); + log.debug("generateDetail: " + generateDetail.toString()); // String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-")); - String key = generateResultKey + ":" + taskId; - String imageName = url.substring(url.lastIndexOf("/") + 1); - 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); - log.debug("generateResultVO: " + generateResultVO.toString()); + String key = generateResultKey + ":" + taskId; + String imageName = url.substring(url.lastIndexOf("/") + 1); + 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); + log.debug("generateResultVO: " + generateResultVO.toString()); // 执行积分扣除 @@ -837,7 +848,7 @@ public class GenerateServiceImpl extends ServiceImpl i response.code(), (requestEndTime - requestStartTime), taskId); String result = response.body().string(); if (!response.isSuccessful()) { - log.warn("Google API响应失败,状态码: {} for taskId: {},结果:{}", response.code(), taskId,result); + log.warn("Google API响应失败,状态码: {} for taskId: {},结果:{}", response.code(), taskId, result); if (attempt < maxRetries) { Thread.sleep(retryDelay * attempt); // 递增延迟 continue; @@ -1260,7 +1271,7 @@ public class GenerateServiceImpl extends ServiceImpl i * @param modelName advanced high normal */ private HashMap chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) { - if (StringUtil.isNullOrEmpty(modelName)){ + if (StringUtil.isNullOrEmpty(modelName)) { throw new BusinessException("system error"); } HashMap modelAndPromptMap = new HashMap<>(); @@ -1278,7 +1289,7 @@ public class GenerateServiceImpl extends ServiceImpl i String style = generateDTO.getText().substring(0, firstCommaIndex).trim(); String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim(); - prompt = getPrintboardPrompt(style, prompt,modelName,isUseImage); + prompt = getPrintboardPrompt(style, prompt, modelName, isUseImage); modelAndPromptMap.put(ModelConstants.PROMPT, prompt); @@ -1676,14 +1687,14 @@ public class GenerateServiceImpl extends ServiceImpl i "Flat textile pattern printed directly on fabric surface, no three-dimensional objects, no items placed on cloth. \n" + "Real style: fabric print, realistic woven/printed pattern, detailed surface pattern only"; } - }else { - throw new BusinessException("style error:"+ style); + } else { + throw new BusinessException("style error:" + style); } if (userInput == null || userInput.trim().isEmpty()) { - if (isUseImage){ + if (isUseImage) { prompt = "Theme: Image content" + "\nRequirement: " + systemPrompt; - }else { + } else { throw new BusinessException("prompt null"); } } else { @@ -4258,11 +4269,11 @@ public class GenerateServiceImpl extends ServiceImpl i // 处理不同状态 switch (statusEnum) { case TASK_NOT_FOUND: - // 审核没过 + // 审核没过 case REQUEST_MODERATED: - // 审核没过 + // 审核没过 case CONTENT_MODERATED: - // 出错 + // 出错 case ERROR: return "Fail"; case PENDING_F: From edaec9884d9256ed8110a87fe28465e5ebc1b9cc Mon Sep 17 00:00:00 2001 From: litianxiang Date: Wed, 25 Mar 2026 22:28:44 +0800 Subject: [PATCH 17/22] TO PROD --- .../java/com/ai/da/common/config/MyTaskScheduler.java | 2 +- src/main/java/com/ai/da/common/task/AccountTask.java | 2 +- src/main/java/com/ai/da/common/task/PaymentTask.java | 6 +++--- .../com/ai/da/common/task/SubscriptionReminderTask.java | 4 ++-- src/main/java/com/ai/da/common/utils/SendEmailUtil.java | 4 ++-- .../java/com/ai/da/service/impl/AffiliateServiceImpl.java | 4 ++-- .../java/com/ai/da/service/impl/EmailServiceImpl.java | 4 ++-- src/main/resources/application.properties | 4 ++-- src/main/resources/payment.properties | 8 ++++---- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/ai/da/common/config/MyTaskScheduler.java b/src/main/java/com/ai/da/common/config/MyTaskScheduler.java index be6bd63b..84f7d1ee 100644 --- a/src/main/java/com/ai/da/common/config/MyTaskScheduler.java +++ b/src/main/java/com/ai/da/common/config/MyTaskScheduler.java @@ -202,7 +202,7 @@ public class MyTaskScheduler { } } -// @Scheduled(cron = "0 0 9 * * ?") + @Scheduled(cron = "0 0 9 * * ?") public void sendTrialOrderExcelToManagements() { // 获取前一天日期 LocalDate yesterday = LocalDate.now().minusDays(1); diff --git a/src/main/java/com/ai/da/common/task/AccountTask.java b/src/main/java/com/ai/da/common/task/AccountTask.java index ee573eaa..cb14a867 100644 --- a/src/main/java/com/ai/da/common/task/AccountTask.java +++ b/src/main/java/com/ai/da/common/task/AccountTask.java @@ -34,7 +34,7 @@ public class AccountTask { accountService.refreshCreditsMonthly(); } -// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes + @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes public void getPaidUser() { // 获取code-create 表中 指定日期之后 订单状态为wc-processing的订单 accountService.extendValidityForCC(); diff --git a/src/main/java/com/ai/da/common/task/PaymentTask.java b/src/main/java/com/ai/da/common/task/PaymentTask.java index 52a6e2d6..4c4d3828 100644 --- a/src/main/java/com/ai/da/common/task/PaymentTask.java +++ b/src/main/java/com/ai/da/common/task/PaymentTask.java @@ -45,7 +45,7 @@ public class PaymentTask { @Resource private PayPalCheckoutService payPalCheckoutService; -// @Scheduled(cron = "0/30 * * * * ?") + @Scheduled(cron = "0/30 * * * * ?") public void orderConfirmForPaypal() throws SerializeException { // log.info("PayPal orderConfirm 被执行......"); @@ -97,7 +97,7 @@ public class PaymentTask { // } -// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes + @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes public void updateAffiliateInfoWithPayment(){ // log.info("佣金计算定时器"); affiliateService.updateAffiliateInfoWithPayment(); @@ -109,7 +109,7 @@ public class PaymentTask { affiliateService.syncLinkViewCountToDB(); } -// @Scheduled(cron = "0 0 8 28-31 * ?") + @Scheduled(cron = "0 0 8 28-31 * ?") public void commissionSummaryReminder(){ // 每个月末的最后一天的早上八点执行 LocalDate today = LocalDate.now(); diff --git a/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java b/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java index 0c6ada03..07af58db 100644 --- a/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java +++ b/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java @@ -40,7 +40,7 @@ public class SubscriptionReminderTask { REMINDER_DAYS_CONFIG.put("year", 14); } -// @Scheduled(cron = "0 0 9 * * ?") + @Scheduled(cron = "0 0 9 * * ?") public void subscriptionReminder() { // 获取所有需要通知的订阅 List subscriptionInfos = getDueSubscriptions(); @@ -97,7 +97,7 @@ public class SubscriptionReminderTask { return subscriptionInfoMapper.selectList(qw); } -// @Scheduled(cron = "0 0 9 * * ?") + @Scheduled(cron = "0 0 9 * * ?") public void trialReminder() { // 今天的 00:00:00 和 23:59:59 LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay(); diff --git a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java index 2d7a9e74..dcdeb4bd 100644 --- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java +++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java @@ -767,7 +767,7 @@ public class SendEmailUtil { try { String merchantEmail = "kimwong@code-create.com.hk"; String developer = "xupei3360@163.com"; - String[] receiverEmail = {/*merchantEmail,*/ developer}; + String[] receiverEmail = {merchantEmail, developer}; Credential cred = new Credential(SECRET_ID, SECRET_KEy); // 实例化一个http选项,可选的,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); @@ -968,7 +968,7 @@ public class SendEmailUtil { req.setFromEmailAddress(SEND_ADDRESS); String merchantEmail = "kimwong@code-create.com.hk"; String developerEmail = "xupei@code-create.com.hk"; - req.setDestination(new String[]{/*merchantEmail,*/ developerEmail}); + req.setDestination(new String[]{merchantEmail, developerEmail}); Template template = new Template(); req.setSubject("New Credit Purchase Order"); template.setTemplateID(CREDITS_PURCHASE_MERCHANT); diff --git a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java index 853f7824..c80a0f5d 100644 --- a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java @@ -82,7 +82,7 @@ public class AffiliateServiceImpl extends ServiceImpl merchantReceiver = Arrays.asList(/*merchantEmail,*/ developer); + List merchantReceiver = Arrays.asList(merchantEmail, developer); String merchantSubject = null; String merchantTemplate = null; @@ -731,7 +731,7 @@ public class EmailServiceImpl implements EmailService { jsonObject.put("quantity", quantity); jsonObject.put("totalFee", amount); - sendEmail(Arrays.asList(/*merchantEmail,*/ developerEmail), jsonObject, CREDITS_PURCHASE_MERCHANT, "New Credit Purchase Order", null, null); + sendEmail(Arrays.asList(merchantEmail, developerEmail), jsonObject, CREDITS_PURCHASE_MERCHANT, "New Credit Purchase Order", null, null); } private final static String COMMON_EXCEPTION_REMINDER = "135279_common-exception-reminder.html"; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 974fee23..8550475d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,7 +2,7 @@ #spring.profiles.active=test #����application-prod�ļ�(��������) -#spring.profiles.active=prod +spring.profiles.active=prod #����application-dev�ļ�(��������) -spring.profiles.active=dev +#spring.profiles.active=dev diff --git a/src/main/resources/payment.properties b/src/main/resources/payment.properties index 0f2800d6..f4b98130 100644 --- a/src/main/resources/payment.properties +++ b/src/main/resources/payment.properties @@ -27,9 +27,9 @@ paypal.webhook_id=1D107312EX592781K ##### Stripe # developer -stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 +#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 # dev 端点 -stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w +#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w # local 端点 #stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm @@ -43,8 +43,8 @@ stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w #stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL # kim - live -#stripe.private-key=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 +stripe.private-key=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 # prod 端点 -#stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11 +stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11 # dev 端点 #stripe.webhook-sign-secret=whsec_cFUtjUOo8wnrIKZmt4GNvt7ZY1bOfrYr From 5dd862ff792c5dadc5433adc397db5a063e63a41 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Thu, 26 Mar 2026 16:13:43 +0800 Subject: [PATCH 18/22] =?UTF-8?q?MOOD=5FBOARD=20high=20=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ai/da/service/impl/GenerateServiceImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 2bab0e43..ff860531 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -198,10 +198,13 @@ public class GenerateServiceImpl extends ServiceImpl i generate.setText(text); Long elementId = generateThroughImageTextDTO.getCollectionElementId(); // validateGeneraType(generate, text, elementId); - if (!StringUtil.isNullOrEmpty(text)) { - text = modifyPrompt(text, generate, generateThroughImageTextDTO.getLevel1Type(), generateThroughImageTextDTO.getAgeGroup()); + if (!(generateThroughImageTextDTO.getLevel1Type().equals(MOOD_BOARD.getRealName())&&generateThroughImageTextDTO.getModelName().equals("high"))){ + 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()); From 0da66ff210c12748746816a4a9ce319eda50a10a Mon Sep 17 00:00:00 2001 From: litianxiang Date: Fri, 27 Mar 2026 15:16:33 +0800 Subject: [PATCH 19/22] =?UTF-8?q?print=20t2i=E6=A8=A1=E5=9E=8B=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ai/da/common/constant/ModelConstants.java | 2 +- .../java/com/ai/da/service/impl/GenerateServiceImpl.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ai/da/common/constant/ModelConstants.java b/src/main/java/com/ai/da/common/constant/ModelConstants.java index bc0b1745..6ae98a81 100644 --- a/src/main/java/com/ai/da/common/constant/ModelConstants.java +++ b/src/main/java/com/ai/da/common/constant/ModelConstants.java @@ -20,7 +20,7 @@ public class ModelConstants { public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image"; public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415"; public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415"; - public static final String PRINTBOARD_HIGH_I2I = "doubao-seededit-3-0-i2i-250628"; + public static final String PRINTBOARD_HIGH_I2I = "doubao-seedream-4-0-250828"; public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828"; public static final String IMAGEN_MODEL = "imagen-4.0-generate-001"; public static final String NANO_BANANA = "gemini-2.5-flash-image"; 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 2bab0e43..a309e4b6 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -1550,6 +1550,11 @@ public class GenerateServiceImpl extends ServiceImpl i if (imagePath != null) { requestBuilder.image(finalImagePath1); } + if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_I2I)) { + GenerateImagesRequest.OptimizePromptOptions optimizePromptOptions = new GenerateImagesRequest.OptimizePromptOptions(); + optimizePromptOptions.setMode("fast"); + requestBuilder.optimizePromptOptions(optimizePromptOptions); + } // 保存生成记录到数据库 Generate generate = new Generate( From e4940019bf1cccb9e3bac99e87902fb177e03af1 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Fri, 27 Mar 2026 15:19:38 +0800 Subject: [PATCH 20/22] =?UTF-8?q?=E6=A1=86=E9=80=89=E9=80=82=E9=85=8Dpy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ai/da/python/PythonService.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ai/da/python/PythonService.java b/src/main/java/com/ai/da/python/PythonService.java index 3ffad287..4ba86ce3 100644 --- a/src/main/java/com/ai/da/python/PythonService.java +++ b/src/main/java/com/ai/da/python/PythonService.java @@ -72,6 +72,8 @@ public class PythonService { private String accessPythonPort; @Value("${minio.bucketName.gradient}") private String gradientBucketName; + @Value("${minio.bucketName.users}") + private String userBucketName; @Value("${access.python.generate_sr_port}") private String srServicePort; @@ -3343,7 +3345,7 @@ public class PythonService { throw new BusinessException("system error!"); } - public Boolean generateSketchOrPrint(String params, String port, String servicePath,String taskId) { + public Boolean generateSketchOrPrint(String params, String port, String servicePath, String taskId) { //限流校验 // AccessLimitUtils.validate("generateSketchOrPrint", 5); OkHttpClient client = new OkHttpClient().newBuilder() @@ -3405,7 +3407,7 @@ public class PythonService { if (result && jsonObject.get("code").equals(200)) { log.info("Generate##responseObject###{}", jsonObject); // return setGenerateImageList(jsonObject.getJSONObject("data")); - if (servicePath== CommonConstant.GENERATE_PATH_FLUX2_KLEIN){ + if (servicePath == CommonConstant.GENERATE_PATH_FLUX2_KLEIN) { //放入结果到mq JSONObject data = jsonObject.getJSONObject("data"); String outputPath = data.getString("output_path"); @@ -4172,6 +4174,9 @@ public class PythonService { .writeTimeout(60, TimeUnit.SECONDS) .build(); MediaType mediaType = MediaType.parse("application/json"); + content.put("bucket", userBucketName); + content.put("object_name", content.get("user_id") + "/" + "segment" + "/" + UUID.randomUUID() + ".png"); + content.remove("user_id"); RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content)); String url = accessPythonIp + ":" + accessPythonPort + "/api/seg_anything"; From 0faf77899b17776b240abda46bfa12a9cbe8b7e5 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Fri, 27 Mar 2026 16:49:10 +0800 Subject: [PATCH 21/22] =?UTF-8?q?fix:PRINTBOARD=5FHIGH=5FI2I=E4=B8=8EPRINT?= =?UTF-8?q?BOARD=5FADVANCED=5FI2I=E4=BD=BF=E7=94=A8=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E5=AF=BC=E8=87=B4=E7=A7=AF=E5=88=86=E6=89=A3?= =?UTF-8?q?=E9=99=A4=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ai/da/common/constant/ModelConstants.java | 2 +- src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ai/da/common/constant/ModelConstants.java b/src/main/java/com/ai/da/common/constant/ModelConstants.java index 6ae98a81..fab503bb 100644 --- a/src/main/java/com/ai/da/common/constant/ModelConstants.java +++ b/src/main/java/com/ai/da/common/constant/ModelConstants.java @@ -20,7 +20,7 @@ public class ModelConstants { public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image"; public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415"; public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415"; - public static final String PRINTBOARD_HIGH_I2I = "doubao-seedream-4-0-250828"; + public static final String PRINTBOARD_HIGH_I2I = "doubao-seedream-4-0-250828-fast"; public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828"; public static final String IMAGEN_MODEL = "imagen-4.0-generate-001"; public static final String NANO_BANANA = "gemini-2.5-flash-image"; 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 a309e4b6..672e31f0 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -1554,6 +1554,8 @@ public class GenerateServiceImpl extends ServiceImpl i GenerateImagesRequest.OptimizePromptOptions optimizePromptOptions = new GenerateImagesRequest.OptimizePromptOptions(); optimizePromptOptions.setMode("fast"); requestBuilder.optimizePromptOptions(optimizePromptOptions); + //由于PRINTBOARD_HIGH_I2I与PRINTBOARD_ADVANCED_I2I使用模型一致,为了区别积分扣除,PRINTBOARD_HIGH_I2I加入了-fast,但传入模型时需要去掉-fast,用PRINTBOARD_ADVANCED_I2I的常量做替代 + requestBuilder.model(ModelConstants.PRINTBOARD_ADVANCED_I2I); } // 保存生成记录到数据库 From c4d2780f0e2aae355ccf24db0974807173d1b1aa Mon Sep 17 00:00:00 2001 From: litianxiang Date: Tue, 31 Mar 2026 13:55:32 +0800 Subject: [PATCH 22/22] TO DEV --- .../java/com/ai/da/common/config/MyTaskScheduler.java | 2 +- src/main/java/com/ai/da/common/task/AccountTask.java | 2 +- src/main/java/com/ai/da/common/task/PaymentTask.java | 6 +++--- .../com/ai/da/common/task/SubscriptionReminderTask.java | 4 ++-- src/main/java/com/ai/da/common/utils/SendEmailUtil.java | 4 ++-- .../java/com/ai/da/service/impl/AffiliateServiceImpl.java | 4 ++-- .../java/com/ai/da/service/impl/EmailServiceImpl.java | 4 ++-- src/main/resources/application.properties | 4 ++-- src/main/resources/payment.properties | 8 ++++---- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/ai/da/common/config/MyTaskScheduler.java b/src/main/java/com/ai/da/common/config/MyTaskScheduler.java index 84f7d1ee..f7c10bcc 100644 --- a/src/main/java/com/ai/da/common/config/MyTaskScheduler.java +++ b/src/main/java/com/ai/da/common/config/MyTaskScheduler.java @@ -202,7 +202,7 @@ public class MyTaskScheduler { } } - @Scheduled(cron = "0 0 9 * * ?") + // @Scheduled(cron = "0 0 9 * * ?") public void sendTrialOrderExcelToManagements() { // 获取前一天日期 LocalDate yesterday = LocalDate.now().minusDays(1); diff --git a/src/main/java/com/ai/da/common/task/AccountTask.java b/src/main/java/com/ai/da/common/task/AccountTask.java index cb14a867..e0d90480 100644 --- a/src/main/java/com/ai/da/common/task/AccountTask.java +++ b/src/main/java/com/ai/da/common/task/AccountTask.java @@ -34,7 +34,7 @@ public class AccountTask { accountService.refreshCreditsMonthly(); } - @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes + // @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes public void getPaidUser() { // 获取code-create 表中 指定日期之后 订单状态为wc-processing的订单 accountService.extendValidityForCC(); diff --git a/src/main/java/com/ai/da/common/task/PaymentTask.java b/src/main/java/com/ai/da/common/task/PaymentTask.java index 4c4d3828..e63e9388 100644 --- a/src/main/java/com/ai/da/common/task/PaymentTask.java +++ b/src/main/java/com/ai/da/common/task/PaymentTask.java @@ -45,7 +45,7 @@ public class PaymentTask { @Resource private PayPalCheckoutService payPalCheckoutService; - @Scheduled(cron = "0/30 * * * * ?") + // @Scheduled(cron = "0/30 * * * * ?") public void orderConfirmForPaypal() throws SerializeException { // log.info("PayPal orderConfirm 被执行......"); @@ -97,7 +97,7 @@ public class PaymentTask { // } - @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes + // @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes public void updateAffiliateInfoWithPayment(){ // log.info("佣金计算定时器"); affiliateService.updateAffiliateInfoWithPayment(); @@ -109,7 +109,7 @@ public class PaymentTask { affiliateService.syncLinkViewCountToDB(); } - @Scheduled(cron = "0 0 8 28-31 * ?") + // @Scheduled(cron = "0 0 8 28-31 * ?") public void commissionSummaryReminder(){ // 每个月末的最后一天的早上八点执行 LocalDate today = LocalDate.now(); diff --git a/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java b/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java index 07af58db..26478270 100644 --- a/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java +++ b/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java @@ -40,7 +40,7 @@ public class SubscriptionReminderTask { REMINDER_DAYS_CONFIG.put("year", 14); } - @Scheduled(cron = "0 0 9 * * ?") + // @Scheduled(cron = "0 0 9 * * ?") public void subscriptionReminder() { // 获取所有需要通知的订阅 List subscriptionInfos = getDueSubscriptions(); @@ -97,7 +97,7 @@ public class SubscriptionReminderTask { return subscriptionInfoMapper.selectList(qw); } - @Scheduled(cron = "0 0 9 * * ?") + // @Scheduled(cron = "0 0 9 * * ?") public void trialReminder() { // 今天的 00:00:00 和 23:59:59 LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay(); diff --git a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java index dcdeb4bd..2d7a9e74 100644 --- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java +++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java @@ -767,7 +767,7 @@ public class SendEmailUtil { try { String merchantEmail = "kimwong@code-create.com.hk"; String developer = "xupei3360@163.com"; - String[] receiverEmail = {merchantEmail, developer}; + String[] receiverEmail = {/*merchantEmail,*/ developer}; Credential cred = new Credential(SECRET_ID, SECRET_KEy); // 实例化一个http选项,可选的,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); @@ -968,7 +968,7 @@ public class SendEmailUtil { req.setFromEmailAddress(SEND_ADDRESS); String merchantEmail = "kimwong@code-create.com.hk"; String developerEmail = "xupei@code-create.com.hk"; - req.setDestination(new String[]{merchantEmail, developerEmail}); + req.setDestination(new String[]{/*merchantEmail,*/ developerEmail}); Template template = new Template(); req.setSubject("New Credit Purchase Order"); template.setTemplateID(CREDITS_PURCHASE_MERCHANT); diff --git a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java index c80a0f5d..853f7824 100644 --- a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java @@ -82,7 +82,7 @@ public class AffiliateServiceImpl extends ServiceImpl merchantReceiver = Arrays.asList(merchantEmail, developer); + List merchantReceiver = Arrays.asList(/*merchantEmail,*/ developer); String merchantSubject = null; String merchantTemplate = null; @@ -731,7 +731,7 @@ public class EmailServiceImpl implements EmailService { jsonObject.put("quantity", quantity); jsonObject.put("totalFee", amount); - sendEmail(Arrays.asList(merchantEmail, developerEmail), jsonObject, CREDITS_PURCHASE_MERCHANT, "New Credit Purchase Order", null, null); + sendEmail(Arrays.asList(/*merchantEmail,*/ developerEmail), jsonObject, CREDITS_PURCHASE_MERCHANT, "New Credit Purchase Order", null, null); } private final static String COMMON_EXCEPTION_REMINDER = "135279_common-exception-reminder.html"; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8550475d..974fee23 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,7 +2,7 @@ #spring.profiles.active=test #����application-prod�ļ�(��������) -spring.profiles.active=prod +#spring.profiles.active=prod #����application-dev�ļ�(��������) -#spring.profiles.active=dev +spring.profiles.active=dev diff --git a/src/main/resources/payment.properties b/src/main/resources/payment.properties index f4b98130..0f2800d6 100644 --- a/src/main/resources/payment.properties +++ b/src/main/resources/payment.properties @@ -27,9 +27,9 @@ paypal.webhook_id=1D107312EX592781K ##### Stripe # developer -#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 +stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 # dev 端点 -#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w +stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w # local 端点 #stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm @@ -43,8 +43,8 @@ paypal.webhook_id=1D107312EX592781K #stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL # kim - live -stripe.private-key=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 +#stripe.private-key=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 # prod 端点 -stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11 +#stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11 # dev 端点 #stripe.webhook-sign-secret=whsec_cFUtjUOo8wnrIKZmt4GNvt7ZY1bOfrYr