Compare commits
192 Commits
eb3826927d
...
dev-ltx
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4940019bf | ||
|
|
0da66ff210 | ||
|
|
76eeb2be53 | ||
|
|
cb6f94d2d4 | ||
|
|
28656c44c8 | ||
|
|
6757a89d04 | ||
|
|
9be1a1e307 | ||
|
|
2168978f61 | ||
|
|
54466b935d | ||
|
|
c970ebe691 | ||
|
|
1c5a3a12b9 | ||
|
|
6e06000083 | ||
|
|
dea2b3be42 | ||
|
|
bcf51aea23 | ||
|
|
0c9d5404c6 | ||
|
|
93429839c0 | ||
|
|
27859c3e28 | ||
|
|
f02c0930a6 | ||
|
|
d57bb83b25 | ||
| 731e34f133 | |||
| 75eca8d6ba | |||
| 3e53401f76 | |||
|
|
b6a068ebcd | ||
|
|
dc291ea086 | ||
|
|
2e846e671a | ||
|
|
a5093311f9 | ||
|
|
aed338a6d7 | ||
|
|
8bdb49d25c | ||
|
|
5d53a8cd42 | ||
|
|
61b7f3072f | ||
|
|
a1f489f3a1 | ||
|
|
fc3fd877a8 | ||
|
|
fc72d2c430 | ||
|
|
1ac01dd090 | ||
|
|
3bbdf7c672 | ||
|
|
0646484fba | ||
|
|
96b8613741 | ||
|
|
cf30226a51 | ||
|
|
3c15a3ff68 | ||
|
|
0c904be227 | ||
|
|
7759b56123 | ||
|
|
d5bfaa8822 | ||
| 967c0cbc01 | |||
| 417e34b41a | |||
|
|
d51aa84647 | ||
|
|
5895bc6ab6 | ||
|
|
3301869f20 | ||
|
|
1ec42f4ad5 | ||
| cc506ff7e9 | |||
|
|
f2d43f06f4 | ||
|
|
9251df49f8 | ||
| 430156f4e8 | |||
|
|
d1123aedcc | ||
| 8c007077a3 | |||
|
|
d63b4b4e63 | ||
|
|
b826f0bf39 | ||
|
|
1decd8e258 | ||
|
|
1286e84488 | ||
| a252fdf7f9 | |||
| 807d802178 | |||
| 53f1b548be | |||
| 45dd78032a | |||
| c160da5132 | |||
| b23faeeee2 | |||
| 67789abca4 | |||
| 1c78d66aab | |||
| 528bc69923 | |||
|
|
22880d128d | ||
| 9c56a102cc | |||
| 2f59fe074f | |||
| 9c61b1c8fe | |||
| e30fdf7401 | |||
| ba2d10afbc | |||
| 6146112d04 | |||
| 412550df27 | |||
| 497421e7fe | |||
| 891527426c | |||
|
|
3e334d7956 | ||
|
|
8f0d0953b2 | ||
| f5c3621a5d | |||
|
|
9a1a0045e0 | ||
| 6223c8e994 | |||
| 67bbee49fd | |||
| ad62ceb32a | |||
| 082afe9e94 | |||
| 49288c3a31 | |||
| 81624e36db | |||
| a526b122d1 | |||
|
|
d882b2e817 | ||
|
|
ebf6427d42 | ||
| 77fe03d361 | |||
| 7a44d67dbf | |||
| 55ce2c6c7e | |||
| a426caaca3 | |||
| 7cb7ce2836 | |||
| 8e075f1da4 | |||
|
|
0f0fde2a3e | ||
|
|
8c6389a1f6 | ||
| 652f82b6a4 | |||
| 7ca2528dcf | |||
|
|
a7800913d2 | ||
|
|
1eaec64ff4 | ||
| e603952332 | |||
| 2bc8b8ef96 | |||
| 0ce968b919 | |||
|
|
dfc9ae4db2 | ||
|
|
a3505c6d95 | ||
|
|
6db0afd515 | ||
|
|
b1e6183dd1 | ||
| 30d08356c0 | |||
| 64cc29f456 | |||
|
|
2b3e12a11c | ||
|
|
d4a4724f61 | ||
|
|
ba6e2bd24c | ||
|
|
a38895b028 | ||
|
|
69a95e66ca | ||
|
|
40518cab37 | ||
|
|
46d61cb73f | ||
| 08f20fd1fe | |||
| d7edc166b3 | |||
|
|
79ad02f66b | ||
|
|
5e261b55c7 | ||
|
|
bc92fcbaf4 | ||
|
|
c6aec917c2 | ||
| 6bc500e78f | |||
| 4c43b98c02 | |||
|
|
5bae785a9f | ||
|
|
7b619aa4cb | ||
| c93ad6daa9 | |||
| 0047be7a03 | |||
| 4ef209cfd4 | |||
| a19751b4b7 | |||
|
|
bb0e5a4263 | ||
|
|
9e9df5367d | ||
| ba8a2c52de | |||
| 39d8c7efcf | |||
|
|
401910901a | ||
|
|
3f5ce6e0e7 | ||
| 0787025151 | |||
| 08b26872ff | |||
| 5bbf1326bb | |||
|
|
c5e27cd220 | ||
|
|
112e9c3bc9 | ||
|
|
ce95cb5080 | ||
|
|
71211bfbc3 | ||
| 72ad977dcb | |||
|
|
6400e79929 | ||
|
|
dd8c72f7d7 | ||
| 13151b65f5 | |||
| 9f523d5953 | |||
| 4879cfeb60 | |||
| 9e252b16ef | |||
| e64add14af | |||
| 3beb27e491 | |||
| 501032ef17 | |||
|
|
cb25bdd2e0 | ||
|
|
7a9fb0213b | ||
| cd767dce6f | |||
| bf95b85841 | |||
| 9e58bd9e7d | |||
| d0ec5c5c26 | |||
| ab8aa5ea5c | |||
| aefcd2fdb0 | |||
| e74eab1070 | |||
|
|
34da437a26 | ||
|
|
f84935d0bd | ||
| 35edaa0f27 | |||
| f43099e19e | |||
| 8079877734 | |||
| ef686e38ac | |||
| 100019d2a4 | |||
|
|
12af237d76 | ||
|
|
44dbbb2a4b | ||
| 9f42e153a4 | |||
| 4fa70a1c90 | |||
| dfb34916e7 | |||
| 9f7987306c | |||
|
|
d3fc70fbf2 | ||
|
|
17645d17e5 | ||
|
|
258eea5277 | ||
|
|
bb1d3bd359 | ||
|
|
6a8c87ed95 | ||
| 6cd42b799a | |||
| 6e1ed7f9b8 | |||
| b7be16738b | |||
| 6da5e91ec1 | |||
| a710fdd432 | |||
| d598f53d3c | |||
| 96170a9956 | |||
| 8205fb5290 | |||
| fcbe4762b3 | |||
| e750adcc94 |
@@ -99,6 +99,8 @@ jobs:
|
|||||||
volumes:
|
volumes:
|
||||||
# 数据挂载
|
# 数据挂载
|
||||||
- ./log:/log
|
- ./log:/log
|
||||||
|
- ./temp:/temp
|
||||||
|
- ./uploads:/temp/uploads
|
||||||
ports:
|
ports:
|
||||||
- '10090:5567'
|
- '10090:5567'
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
5
pom.xml
5
pom.xml
@@ -427,6 +427,11 @@
|
|||||||
<artifactId>bcpkix-jdk18on</artifactId>
|
<artifactId>bcpkix-jdk18on</artifactId>
|
||||||
<version>1.78.1</version>
|
<version>1.78.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- AOP -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -636,26 +636,26 @@ public class GenerateConsumer {
|
|||||||
public void getPoseTransformationResult(Message msg, Channel channel) {
|
public void getPoseTransformationResult(Message msg, Channel channel) {
|
||||||
processPoseTransformResult(msg, channel);
|
processPoseTransformResult(msg, channel);
|
||||||
}
|
}
|
||||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}")
|
// @RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}")
|
||||||
@RabbitHandler
|
// @RabbitHandler
|
||||||
public void getDesignBatchResult(Message msg, Channel channel) {
|
// public void getDesignBatchResult(Message msg, Channel channel) {
|
||||||
processDesignBatchResult(msg, channel);
|
// processDesignBatchResult(msg, channel);
|
||||||
}
|
// }
|
||||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageBatch}")
|
// @RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageBatch}")
|
||||||
@RabbitHandler
|
// @RabbitHandler
|
||||||
public void getToProductImageBatchResult(Message msg, Channel channel) {
|
// public void getToProductImageBatchResult(Message msg, Channel channel) {
|
||||||
processToProductImageBatchResult(msg, channel);
|
// processToProductImageBatchResult(msg, channel);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.relightBatch}")
|
// @RabbitListener(queues = "#{rabbitMQProperties.queues.relightBatch}")
|
||||||
@RabbitHandler
|
// @RabbitHandler
|
||||||
public void getRelightBatchResult(Message msg, Channel channel) {
|
// public void getRelightBatchResult(Message msg, Channel channel) {
|
||||||
processRelightBatchResult(msg, channel);
|
// processRelightBatchResult(msg, channel);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransformBatch}")
|
// @RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransformBatch}")
|
||||||
@RabbitHandler
|
// @RabbitHandler
|
||||||
public void getPoseTransformBatchResult(Message msg, Channel channel) {
|
// public void getPoseTransformBatchResult(Message msg, Channel channel) {
|
||||||
processPoseTransformBatchResult(msg, channel);
|
// processPoseTransformBatchResult(msg, channel);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ public class MQPublisher {
|
|||||||
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm);
|
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 含有的字段
|
* @param mailParams 含有的字段
|
||||||
|
|||||||
@@ -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<String, Object> 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<String, Object> getRequestParams(JoinPoint joinPoint, HttpServletRequest request) {
|
||||||
|
Map<String, Object> 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<String, Object> 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<String, Object> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ public class CommonConstant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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";
|
public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo";
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class ModelConstants {
|
|||||||
public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image";
|
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 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_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 PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828";
|
||||||
public static final String IMAGEN_MODEL = "imagen-4.0-generate-001";
|
public static final String IMAGEN_MODEL = "imagen-4.0-generate-001";
|
||||||
public static final String NANO_BANANA = "gemini-2.5-flash-image";
|
public static final String NANO_BANANA = "gemini-2.5-flash-image";
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ public enum AuthenticationOperationTypeEnum {
|
|||||||
*/
|
*/
|
||||||
UPDATE_USERINFO,
|
UPDATE_USERINFO,
|
||||||
|
|
||||||
REGISTER;
|
REGISTER,
|
||||||
|
/**
|
||||||
|
* Global_Award 活动验证
|
||||||
|
*/
|
||||||
|
GLOBAL_AWARD;
|
||||||
|
|
||||||
public static AuthenticationOperationTypeEnum of(String name) {
|
public static AuthenticationOperationTypeEnum of(String name) {
|
||||||
return Stream.of(AuthenticationOperationTypeEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null);
|
return Stream.of(AuthenticationOperationTypeEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.ai.da.common.context.UserContext;
|
|||||||
import com.ai.da.common.security.config.SecurityProperties;
|
import com.ai.da.common.security.config.SecurityProperties;
|
||||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||||
import com.ai.da.common.utils.LocalCacheUtils;
|
import com.ai.da.common.utils.LocalCacheUtils;
|
||||||
|
import com.ai.da.common.utils.RedisUtil;
|
||||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||||
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
||||||
import com.ai.da.common.utils.RequestInfoUtil;
|
import com.ai.da.common.utils.RequestInfoUtil;
|
||||||
@@ -40,6 +41,8 @@ public class AuthenticationFilter extends OncePerRequestFilter {
|
|||||||
private JWTTokenHelper jwtTokenHelper;
|
private JWTTokenHelper jwtTokenHelper;
|
||||||
@Resource
|
@Resource
|
||||||
private SecurityProperties properties;
|
private SecurityProperties properties;
|
||||||
|
@Resource
|
||||||
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
private static final List<String> FILTER_URL =
|
private static final List<String> FILTER_URL =
|
||||||
Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html",
|
Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html",
|
||||||
@@ -56,7 +59,12 @@ public class AuthenticationFilter extends OncePerRequestFilter {
|
|||||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
|
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
|
||||||
"/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode","/api/third/party/receiveDesignParams"
|
"/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode","/api/third/party/receiveDesignParams"
|
||||||
, "/api/account/schoolLogin", "/api/account/enterpriseLogin", "/api/account/organizationNameSearch",
|
, "/api/account/schoolLogin", "/api/account/enterpriseLogin", "/api/account/organizationNameSearch",
|
||||||
"/api/llm/stream"
|
"/api/llm/stream",
|
||||||
|
//GlobalAwardController
|
||||||
|
"/api/global-award/uploads/pdf/init", "/api/global-award/uploads/pdf/chunk", "/api/global-award/uploads/pdf/complete", "/api/global-award/uploads/pdf/status",
|
||||||
|
"/api/global-award/uploads/video/init", "/api/global-award/uploads/video/chunk", "/api/global-award/uploads/video/complete", "/api/global-award/uploads/video/status",
|
||||||
|
"/api/global-award/contestants/save", "/api/global-award/contestants/by-email", "/api/global-award/checkEmail", "/api/global-award/checkCode","/api/global-award/contestants/export",
|
||||||
|
"/api/global-award/contestants/export/files"
|
||||||
);
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,12 +140,19 @@ public class AuthenticationFilter extends OncePerRequestFilter {
|
|||||||
UserContext.delete();
|
UserContext.delete();
|
||||||
//存取用户信息到缓存
|
//存取用户信息到缓存
|
||||||
UserContext.setUserHolder(principal);
|
UserContext.setUserHolder(principal);
|
||||||
//校验token
|
// 校验 token:先查本地缓存,再查 Redis,保证服务重启后仍然有效
|
||||||
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
|
String userIdStr = String.valueOf(principal.getId());
|
||||||
|
String cacheToken = LocalCacheUtils.getTokenCache(userIdStr);
|
||||||
|
|
||||||
if(StringUtils.isEmpty(cacheToken)){
|
if (StringUtils.isEmpty(cacheToken)) {
|
||||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
// 本地缓存为空时,尝试从 Redis 读取
|
||||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(local cache empty)");
|
cacheToken = redisUtil.getLoginToken(principal.getId());
|
||||||
|
if (StringUtils.isEmpty(cacheToken)) {
|
||||||
|
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||||
|
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(cache & redis empty)");
|
||||||
|
}
|
||||||
|
// 将 Redis 中的 token 回填到本地缓存,减少后续 Redis 访问
|
||||||
|
LocalCacheUtils.setTokenCache(userIdStr, cacheToken);
|
||||||
}
|
}
|
||||||
if(!cacheToken.equals(jwtToken) ){
|
if(!cacheToken.equals(jwtToken) ){
|
||||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ public class AccountTask {
|
|||||||
accountService.extendValidityForCC();
|
accountService.extendValidityForCC();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每天凌晨0点执行一次
|
// 每天凌晨0点执行一次 目前已没有角色类型为4的用户
|
||||||
@Scheduled(cron = "0 0 0 * * ?")
|
/*@Scheduled(cron = "0 0 0 * * ?")
|
||||||
public void cancelActivityBenefits() {
|
public void cancelActivityBenefits() {
|
||||||
// 1、查询当前所有参与了活动且过期的用户
|
// 1、查询当前所有参与了活动且过期的用户
|
||||||
List<Account> accountList = accountService.getExpiredUserBySystemUser(4);
|
List<Account> accountList = accountService.getExpiredUserBySystemUser(4);
|
||||||
@@ -51,7 +51,7 @@ public class AccountTask {
|
|||||||
log.info("参与活动的用户{} : {} 于 {} 账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime());
|
log.info("参与活动的用户{} : {} 于 {} 账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime());
|
||||||
accountService.toVisitor(account);
|
accountService.toVisitor(account);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// 每天检测正式用户到期情况,每天凌晨0点执行
|
// 每天检测正式用户到期情况,每天凌晨0点执行
|
||||||
@Scheduled(cron = "0 0 0 * * ?")
|
@Scheduled(cron = "0 0 0 * * ?")
|
||||||
@@ -92,7 +92,7 @@ public class AccountTask {
|
|||||||
|
|
||||||
@Scheduled(cron = "0 5 0 * * ?")
|
@Scheduled(cron = "0 5 0 * * ?")
|
||||||
public void activeSubscriptionPlan() {
|
public void activeSubscriptionPlan() {
|
||||||
subscriptionPlanService.activeSubscriptionPlan();
|
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||||
|
|||||||
@@ -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(){
|
public void updateAffiliateInfoWithPayment(){
|
||||||
// log.info("佣金计算定时器");
|
// log.info("佣金计算定时器");
|
||||||
affiliateService.updateAffiliateInfoWithPayment();
|
affiliateService.updateAffiliateInfoWithPayment();
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class SubscriptionReminderTask {
|
|||||||
REMINDER_DAYS_CONFIG.put("year", 14);
|
REMINDER_DAYS_CONFIG.put("year", 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(cron = "0 0 9 * * ?")
|
// @Scheduled(cron = "0 0 9 * * ?")
|
||||||
public void subscriptionReminder() {
|
public void subscriptionReminder() {
|
||||||
// 获取所有需要通知的订阅
|
// 获取所有需要通知的订阅
|
||||||
List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions();
|
List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions();
|
||||||
@@ -97,7 +97,7 @@ public class SubscriptionReminderTask {
|
|||||||
return subscriptionInfoMapper.selectList(qw);
|
return subscriptionInfoMapper.selectList(qw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(cron = "0 0 9 * * ?")
|
// @Scheduled(cron = "0 0 9 * * ?")
|
||||||
public void trialReminder() {
|
public void trialReminder() {
|
||||||
// 今天的 00:00:00 和 23:59:59
|
// 今天的 00:00:00 和 23:59:59
|
||||||
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
|
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
|
||||||
|
|||||||
@@ -41,6 +41,13 @@ public class MinioUtil {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MinioClient minioClient;
|
private MinioClient minioClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取MinIO客户端实例
|
||||||
|
*/
|
||||||
|
public MinioClient getMinioClient() {
|
||||||
|
return minioClient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* description: 判断bucket是否存在,不存在则创建
|
* description: 判断bucket是否存在,不存在则创建
|
||||||
*
|
*
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1024,7 +1024,7 @@ public class SendEmailUtil {
|
|||||||
log.info("邮件发送结果res###{}", SendEmailResponse.toJsonString(resp));
|
log.info("邮件发送结果res###{}", SendEmailResponse.toJsonString(resp));
|
||||||
} catch (TencentCloudSDKException e) {
|
} catch (TencentCloudSDKException e) {
|
||||||
log.info("邮件发送失败###{}", e.toString());
|
log.info("邮件发送失败###{}", e.toString());
|
||||||
throw new BusinessException("failed.to.send.mail");
|
// throw new BusinessException("failed.to.send.mail");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,8 +114,8 @@ public class SendRequestUtil {
|
|||||||
|
|
||||||
public String sendFluxPost(String url, String requestBodyStr) {
|
public String sendFluxPost(String url, String requestBodyStr) {
|
||||||
// 尝试两个API key
|
// 尝试两个API key
|
||||||
String[] apiKeys = {"d447a0ac-2291-4f1c-9a36-f7614c385989",
|
String[] apiKeys = {"84e8f5d5-b0b3-49aa-b244-ab7ba27e7ae7",
|
||||||
"84e8f5d5-b0b3-49aa-b244-ab7ba27e7ae7"};
|
"d447a0ac-2291-4f1c-9a36-f7614c385989"};
|
||||||
boolean[] notified = {false, false}; // 记录是否已发送过不足提醒
|
boolean[] notified = {false, false}; // 记录是否已发送过不足提醒
|
||||||
|
|
||||||
for (int i = 0; i < apiKeys.length; i++) {
|
for (int i = 0; i < apiKeys.length; i++) {
|
||||||
@@ -140,8 +140,8 @@ public class SendRequestUtil {
|
|||||||
if (status == 402 || status == 403) {
|
if (status == 402 || status == 403) {
|
||||||
if (!notified[i]) {
|
if (!notified[i]) {
|
||||||
SendEmailUtil.commonExceptionReminder(
|
SendEmailUtil.commonExceptionReminder(
|
||||||
"Flux账户积分不足,flux生成任务失败",
|
"Flux账户积分不足,flux生成任务失败。(key:)" + apiKeys[i],
|
||||||
new String[]{"xupei3360@163.com, fangjianliao@aidlab.hk, investigation@aidlab.hk"}
|
new String[]{"xupei3360@163.com", "fangjianliao@aidlab.hk", "investigation@aidlab.hk"}
|
||||||
);
|
);
|
||||||
notified[i] = true;
|
notified[i] = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ import com.ai.da.model.vo.PersonalHomepageVO;
|
|||||||
import com.ai.da.service.AccountService;
|
import com.ai.da.service.AccountService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -139,21 +139,21 @@ public class AccountController {
|
|||||||
@Operation(summary = "aws状态检测")
|
@Operation(summary = "aws状态检测")
|
||||||
@GetMapping("/healthy")
|
@GetMapping("/healthy")
|
||||||
@ResponseStatus(HttpStatus.OK)
|
@ResponseStatus(HttpStatus.OK)
|
||||||
public Response<Map<String,Integer>> checkStatus(){
|
public Response<Map<String, Integer>> checkStatus() {
|
||||||
Map<String,Integer> returnMap = new HashMap<>();
|
Map<String, Integer> returnMap = new HashMap<>();
|
||||||
returnMap.put("code",200);
|
returnMap.put("code", 200);
|
||||||
return Response.success(returnMap);
|
return Response.success(returnMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "查询账号到期时间")
|
@Operation(summary = "查询账号到期时间")
|
||||||
@PostMapping("/getExpiredTime")
|
@PostMapping("/getExpiredTime")
|
||||||
public Response<Long> getExpiredTime(){
|
public Response<Long> getExpiredTime() {
|
||||||
return Response.success(accountService.getExpiredTime());
|
return Response.success(accountService.getExpiredTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "免密登录")
|
@Operation(summary = "免密登录")
|
||||||
@PostMapping("/noLoginRequired")
|
@PostMapping("/noLoginRequired")
|
||||||
public Response<AccountLoginVO> noLoginRequired(@RequestBody NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request){
|
public Response<AccountLoginVO> noLoginRequired(@RequestBody NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
|
||||||
return Response.success(accountService.noLoginRequired(noLoginRequiredDTO, request));
|
return Response.success(accountService.noLoginRequired(noLoginRequiredDTO, request));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +191,7 @@ public class AccountController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 参与活动 获取福利
|
* 参与活动 获取福利
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
/* @Operation(summary = "参与活动 获取福利")
|
/* @Operation(summary = "参与活动 获取福利")
|
||||||
@@ -201,7 +202,7 @@ public class AccountController {
|
|||||||
|
|
||||||
@Operation(summary = "将用户账号过期时间设置为过期当天的23:59:59")
|
@Operation(summary = "将用户账号过期时间设置为过期当天的23:59:59")
|
||||||
@GetMapping("/setUserValidToDayEnd")
|
@GetMapping("/setUserValidToDayEnd")
|
||||||
public Response<List<Long>> setUserValidToDayEnd(){
|
public Response<List<Long>> setUserValidToDayEnd() {
|
||||||
return Response.success(accountService.setUserValidToDayEnd());
|
return Response.success(accountService.setUserValidToDayEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,19 +224,19 @@ public class AccountController {
|
|||||||
|
|
||||||
@Operation(summary = "获取个人主页信息")
|
@Operation(summary = "获取个人主页信息")
|
||||||
@GetMapping("/personalHomepage")
|
@GetMapping("/personalHomepage")
|
||||||
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id){
|
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id) {
|
||||||
return Response.success(accountService.getPersonalHomepage(id));
|
return Response.success(accountService.getPersonalHomepage(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "getUsernameModifyTimes")
|
@Operation(summary = "getUsernameModifyTimes")
|
||||||
@GetMapping("/getNicknameModifyTimes")
|
@GetMapping("/getNicknameModifyTimes")
|
||||||
public Response<Long> getNicknameModifyTimes(){
|
public Response<Long> getNicknameModifyTimes() {
|
||||||
return Response.success(accountService.getNicknameModifyTimes());
|
return Response.success(accountService.getNicknameModifyTimes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "editUserName")
|
@Operation(summary = "editUserName")
|
||||||
@GetMapping("/editUserName")
|
@GetMapping("/editUserName")
|
||||||
public Response<String> editUserName(@RequestParam("newUserName") String newUserName){
|
public Response<String> editUserName(@RequestParam("newUserName") String newUserName) {
|
||||||
accountService.editUserName(newUserName);
|
accountService.editUserName(newUserName);
|
||||||
return Response.success("success");
|
return Response.success("success");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class ConvenientInquiryController {
|
|||||||
@GetMapping("/recentNewUserChart")
|
@GetMapping("/recentNewUserChart")
|
||||||
public Response<Map<String, Object>> recentNewUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
|
public Response<Map<String, Object>> recentNewUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
|
||||||
@Parameter(description = "endTime") @RequestParam @Nullable String endTime,
|
@Parameter(description = "endTime") @RequestParam @Nullable String endTime,
|
||||||
@Parameter(description = "userType") @RequestParam Integer userType) {
|
@Parameter(description = "userType") @RequestParam @Nullable Integer userType) {
|
||||||
return Response.success(convenientInquiryService.recentNewUserChart(startTime, endTime, userType));
|
return Response.success(convenientInquiryService.recentNewUserChart(startTime, endTime, userType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +179,10 @@ public class ConvenientInquiryController {
|
|||||||
|
|
||||||
@Operation(summary = "获取所有用户id")
|
@Operation(summary = "获取所有用户id")
|
||||||
@GetMapping("/getAllUserId")
|
@GetMapping("/getAllUserId")
|
||||||
public Response<List<Map<String, Object>>> getAllUsrIdList() {
|
public Response<IPage<Map<String, Object>>> getAllUserIdList(@Parameter(description = "page") @RequestParam Integer page,
|
||||||
return Response.success(convenientInquiryService.getAllUserIdList());
|
@Parameter(description = "size") @RequestParam Integer size,
|
||||||
|
@Parameter(description = "email 模糊查询") @RequestParam(required = false) String email) {
|
||||||
|
return Response.success(convenientInquiryService.getAllUserIdList(page, size, email));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取所有交易信息")
|
@Operation(summary = "获取所有交易信息")
|
||||||
|
|||||||
187
src/main/java/com/ai/da/controller/GlobalAwardController.java
Normal file
187
src/main/java/com/ai/da/controller/GlobalAwardController.java
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package com.ai.da.controller;
|
||||||
|
|
||||||
|
import com.ai.da.common.response.Response;
|
||||||
|
import com.ai.da.model.dto.*;
|
||||||
|
import com.ai.da.model.dto.ContestantDTO;
|
||||||
|
import com.ai.da.model.vo.CheckOTPVO;
|
||||||
|
import com.ai.da.service.GlobalAwardService;
|
||||||
|
import com.ai.da.service.upload.UploadService;
|
||||||
|
import com.ai.da.service.upload.UploadTask;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiParam;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/global-award")
|
||||||
|
@Api(tags = "全球奖项API", description = "全球奖项大赛管理和文件上传")
|
||||||
|
public class GlobalAwardController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private GlobalAwardService globalAwardService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UploadService uploadService;
|
||||||
|
|
||||||
|
// @PostMapping("/uploads/pdf")
|
||||||
|
// public Response<String> uploadPdf(@RequestParam("file") MultipartFile file,
|
||||||
|
// @RequestParam(value = "email", required = false) String email) throws Exception {
|
||||||
|
// return Response.success(globalAwardService.uploadPdf(file, email));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @PostMapping("/uploads/video")
|
||||||
|
// public Response<String> uploadVideo(@RequestParam("file") MultipartFile file,
|
||||||
|
// @RequestParam(value = "email", required = false) String email) throws Exception {
|
||||||
|
// return Response.success(globalAwardService.uploadVideo(file, email));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ===== 新增分片上传接口 =====
|
||||||
|
|
||||||
|
// ===== PDF分片上传接口 =====
|
||||||
|
|
||||||
|
/** 初始化PDF上传任务 */
|
||||||
|
@PostMapping("/uploads/pdf/init")
|
||||||
|
@ApiOperation(value = "初始化PDF上传", notes = "创建新的PDF上传任务并返回上传参数")
|
||||||
|
public Response<UploadInitResponse> initPdfUpload(@ApiParam(value = "PDF上传初始化请求", required = true) @RequestBody UploadInitRequest request) {
|
||||||
|
UploadTask uploadTask = uploadService.initPdfUpload(request);
|
||||||
|
return Response.success(UploadInitResponse.builder()
|
||||||
|
.uploadId(uploadTask.getUploadId())
|
||||||
|
.chunkSize(uploadTask.getChunkSize())
|
||||||
|
.totalChunks(uploadTask.getTotalChunks())
|
||||||
|
.expiresAt(uploadTask.getExpiresAt())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 上传PDF分片 */
|
||||||
|
@PostMapping("/uploads/pdf/chunk")
|
||||||
|
@ApiOperation(value = "上传PDF分片", notes = "上传PDF文件的单个分片")
|
||||||
|
public Response<UploadChunkResponse> uploadPdfChunk(
|
||||||
|
@ApiParam(value = "PDF文件分片", required = true) @RequestParam("chunk") MultipartFile chunk,
|
||||||
|
@ApiParam(value = "上传任务ID", required = true) @RequestParam("uploadId") String uploadId,
|
||||||
|
@ApiParam(value = "分片索引(从0开始)", required = true) @RequestParam("chunkIndex") int chunkIndex,
|
||||||
|
@ApiParam(value = "分片总数", required = true) @RequestParam("totalChunks") int totalChunks) {
|
||||||
|
|
||||||
|
UploadChunkResponse uploadChunkResponse = uploadService.uploadPdfChunk(uploadId, chunk, chunkIndex, totalChunks);
|
||||||
|
return Response.success(uploadChunkResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 完成PDF上传 */
|
||||||
|
@PostMapping("/uploads/pdf/complete")
|
||||||
|
@ApiOperation(value = "完成PDF上传", notes = "完成PDF上传并合并所有分片")
|
||||||
|
public Response<UploadCompleteResponse> completePdfUpload(@ApiParam(value = "PDF上传完成请求", required = true) @RequestBody UploadCompleteRequest request) {
|
||||||
|
UploadCompleteResponse uploadCompleteResponse = uploadService.completePdfUpload(
|
||||||
|
request.getUploadId(),
|
||||||
|
request.getFileName(),
|
||||||
|
request.getTotalSize(),
|
||||||
|
request.getEmail(),
|
||||||
|
request.getSecureToken());
|
||||||
|
return Response.success(uploadCompleteResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询PDF上传状态 */
|
||||||
|
@GetMapping("/uploads/pdf/status/{uploadId}")
|
||||||
|
@ApiOperation(value = "查询PDF上传状态", notes = "获取PDF上传任务的当前状态")
|
||||||
|
public Response<UploadStatusResponse> getPdfUploadStatus(@ApiParam(value = "上传任务ID", required = true) @PathVariable String uploadId) {
|
||||||
|
UploadStatusResponse pdfUploadStatus = uploadService.getPdfUploadStatus(uploadId);
|
||||||
|
return Response.success(pdfUploadStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 视频分片上传接口 =====
|
||||||
|
|
||||||
|
/** 初始化视频上传任务 */
|
||||||
|
@PostMapping("/uploads/video/init")
|
||||||
|
@ApiOperation(value = "初始化视频上传", notes = "创建新的视频上传任务并返回上传参数")
|
||||||
|
public Response<UploadInitResponse> initVideoUpload(@ApiParam(value = "视频上传初始化请求", required = true) @RequestBody UploadInitRequest request) {
|
||||||
|
UploadTask uploadTask = uploadService.initVideoUpload(request);
|
||||||
|
return Response.success(UploadInitResponse.builder()
|
||||||
|
.uploadId(uploadTask.getUploadId())
|
||||||
|
.chunkSize(uploadTask.getChunkSize())
|
||||||
|
.totalChunks(uploadTask.getTotalChunks())
|
||||||
|
.expiresAt(uploadTask.getExpiresAt())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 上传视频分片 */
|
||||||
|
@PostMapping("/uploads/video/chunk")
|
||||||
|
@ApiOperation(value = "上传视频分片", notes = "上传视频文件的单个分片")
|
||||||
|
public Response<UploadChunkResponse> uploadVideoChunk(
|
||||||
|
@ApiParam(value = "视频文件分片", required = true) @RequestParam("chunk") MultipartFile chunk,
|
||||||
|
@ApiParam(value = "上传任务ID", required = true) @RequestParam("uploadId") String uploadId,
|
||||||
|
@ApiParam(value = "分片索引(从0开始)", required = true) @RequestParam("chunkIndex") int chunkIndex,
|
||||||
|
@ApiParam(value = "分片总数", required = true) @RequestParam("totalChunks") int totalChunks) {
|
||||||
|
|
||||||
|
UploadChunkResponse uploadChunkResponse = uploadService.uploadVideoChunk(uploadId, chunk, chunkIndex, totalChunks);
|
||||||
|
return Response.success(uploadChunkResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 完成视频上传 */
|
||||||
|
@PostMapping("/uploads/video/complete")
|
||||||
|
@ApiOperation(value = "完成视频上传", notes = "完成视频上传并合并所有分片")
|
||||||
|
public Response<UploadCompleteResponse> completeVideoUpload(@ApiParam(value = "视频上传完成请求", required = true) @RequestBody UploadCompleteRequest request) {
|
||||||
|
UploadCompleteResponse uploadCompleteResponse = uploadService.completeVideoUpload(
|
||||||
|
request.getUploadId(),
|
||||||
|
request.getFileName(),
|
||||||
|
request.getTotalSize(),
|
||||||
|
request.getEmail(),
|
||||||
|
request.getSecureToken());
|
||||||
|
return Response.success(uploadCompleteResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询视频上传状态 */
|
||||||
|
@GetMapping("/uploads/video/status/{uploadId}")
|
||||||
|
@ApiOperation(value = "查询视频上传状态", notes = "获取视频上传任务的当前状态")
|
||||||
|
public Response<UploadStatusResponse> getVideoUploadStatus(@ApiParam(value = "上传任务ID", required = true) @PathVariable String uploadId) {
|
||||||
|
UploadStatusResponse videoUploadStatus = uploadService.getVideoUploadStatus(uploadId);
|
||||||
|
return Response.success(videoUploadStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/contestants/save")
|
||||||
|
@ApiOperation(value = "保存参赛者信息", notes = "保存或更新参赛者信息及已上传的文件")
|
||||||
|
public Response<Map<String,Object>> submit(@ApiParam(value = "参赛者信息", required = true) @RequestBody ContestantDTO request) {
|
||||||
|
return Response.success(globalAwardService.saveContestant(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contestants/{id}")
|
||||||
|
@ApiOperation(value = "根据id查询参赛者", notes = "根据id获取参赛者信息")
|
||||||
|
public Response<ContestantDTO> getContestantByID(@ApiParam(value = "参赛者id", required = true) @PathVariable("id") String id) {
|
||||||
|
ContestantDTO dto = globalAwardService.getContestantByID(id);
|
||||||
|
return Response.success(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/checkEmail")
|
||||||
|
public Response<String> checkEmail(@RequestParam("email") String email) {
|
||||||
|
globalAwardService.checkEmail(email);
|
||||||
|
return Response.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/checkCode")
|
||||||
|
public Response<CheckOTPVO> checkCode(@RequestParam("email") String email, @RequestParam("code") String code) {
|
||||||
|
return Response.success(globalAwardService.checkCode(email, code));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contestants/export")
|
||||||
|
@ApiOperation(value = "导出参赛者列表为Excel", notes = "导出所有参赛者信息为xlsx并触发下载")
|
||||||
|
public void exportContestants(HttpServletResponse response) throws Exception {
|
||||||
|
byte[] data = globalAwardService.exportContestants();
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.xlsx\"");
|
||||||
|
response.setContentLength(data.length);
|
||||||
|
response.getOutputStream().write(data);
|
||||||
|
response.getOutputStream().flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/contestants/export/files")
|
||||||
|
@ApiOperation(value = "导出参赛者文件到本地", notes = "根据参赛者编号范围导出PDF和视频文件到本地temp/uploads/contestants目录")
|
||||||
|
public Response<Integer> exportContestantFiles(@ApiParam(value = "参赛者文件导出请求", required = true) @RequestBody ContestantExportRequest request) throws Exception {
|
||||||
|
int exportedCount = globalAwardService.exportContestantFiles(request.getMinContestantNumber(), request.getMaxContestantNumber());
|
||||||
|
return Response.success(exportedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +23,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -119,4 +122,14 @@ public class PythonController {
|
|||||||
return Response.success(superResolutionService.prepareForSR(superResolutionDTO));
|
return Response.success(superResolutionService.prepareForSR(superResolutionDTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CrossOrigin
|
||||||
|
@Operation(summary = "Seg Anything 转发接口")
|
||||||
|
@PostMapping("/segAnything")
|
||||||
|
public Response<String> segAnything(@RequestBody Map<String, Object> payload) {
|
||||||
|
// 将前端传来的 Map 转为 fastjson JSONObject 并转发给 python 服务
|
||||||
|
JSONObject requestJson = (JSONObject) JSON.toJSON(payload);
|
||||||
|
String url = pythonService.segAnything(requestJson);
|
||||||
|
return Response.success(url);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class SubscriptionPlanController {
|
|||||||
@Operation(summary = "activeSubscriptionPlan")
|
@Operation(summary = "activeSubscriptionPlan")
|
||||||
@GetMapping("/activeSubscriptionPlan")
|
@GetMapping("/activeSubscriptionPlan")
|
||||||
public Response<String> activeSubscriptionPlan() {
|
public Response<String> activeSubscriptionPlan() {
|
||||||
subscriptionPlanService.activeSubscriptionPlan();
|
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||||
return Response.success();
|
return Response.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
src/main/java/com/ai/da/mapper/primary/ContestantMapper.java
Normal file
12
src/main/java/com/ai/da/mapper/primary/ContestantMapper.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package com.ai.da.mapper.primary;
|
||||||
|
|
||||||
|
import com.ai.da.mapper.primary.entity.Contestant;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestantMapper extends BaseMapper<Contestant> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ public interface DesignMapper extends CommonMapper<Design> {
|
|||||||
Long insertDesign(Design design);
|
Long insertDesign(Design design);
|
||||||
|
|
||||||
List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime, List<Long> ids, String email,
|
List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime, List<Long> ids, String email,
|
||||||
String role, String organizationName);
|
String role, String organizationName, boolean filterBySecond);
|
||||||
|
|
||||||
List<Design> selectDeleteList();
|
List<Design> selectDeleteList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.ai.da.common.config.mybatis.plus.CommonMapper;
|
|||||||
import com.ai.da.mapper.primary.entity.Notification;
|
import com.ai.da.mapper.primary.entity.Notification;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -20,5 +21,5 @@ public interface NotificationMapper extends CommonMapper<Notification> {
|
|||||||
|
|
||||||
void setPersonalNotificationAllRead(String type, Long receiverId, LocalDateTime time);
|
void setPersonalNotificationAllRead(String type, Long receiverId, LocalDateTime time);
|
||||||
|
|
||||||
List<Long> getUnreadSysNotification(Long accountId);
|
List<Long> getUnreadSysNotification(Long accountId, Date createTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.ai.da.mapper.primary;
|
||||||
|
|
||||||
|
import com.ai.da.common.config.mybatis.plus.CommonMapper;
|
||||||
|
import com.ai.da.mapper.primary.entity.UserPreference;
|
||||||
|
|
||||||
|
public interface UserPreferenceMapper extends CommonMapper<UserPreference> {
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package com.ai.da.mapper.primary;
|
|||||||
import com.ai.da.common.config.mybatis.plus.CommonMapper;
|
import com.ai.da.common.config.mybatis.plus.CommonMapper;
|
||||||
import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
|
import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapper 接口
|
* Mapper 接口
|
||||||
*
|
*
|
||||||
@@ -11,5 +13,11 @@ import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
|
|||||||
*/
|
*/
|
||||||
public interface WorkspaceRelStyleMapper extends CommonMapper<WorkspaceRelStyle> {
|
public interface WorkspaceRelStyleMapper extends CommonMapper<WorkspaceRelStyle> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据projectId查询workspaceRelStyles
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @return workspaceRelStyles列表
|
||||||
|
*/
|
||||||
|
List<WorkspaceRelStyle> selectByProjectId(Long projectId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@@ -76,13 +77,13 @@ public class Account implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*/
|
*/
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private Date createDate;
|
private Date createDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新时间
|
* 更新时间
|
||||||
*/
|
*/
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private Date updateDate;
|
private Date updateDate;
|
||||||
|
|
||||||
private Integer isTrial;
|
private Integer isTrial;
|
||||||
@@ -142,4 +143,26 @@ public class Account implements Serializable {
|
|||||||
private String givenName;
|
private String givenName;
|
||||||
|
|
||||||
private Long subscriptionPlanId;
|
private Long subscriptionPlanId;
|
||||||
|
|
||||||
|
// 在类内部定义的枚举
|
||||||
|
@Getter
|
||||||
|
public enum SystemRole {
|
||||||
|
VISITOR("游客", 0),
|
||||||
|
YEARLY("年付用户", 1),
|
||||||
|
MONTHLY("月付用户", 2),
|
||||||
|
TRIAL("试用用户", 3),
|
||||||
|
EVENT_USER("参加活动获取30天有效期和6000个积分的用户", 4),
|
||||||
|
ENTERPRISE_ADMIN("企业管理员账号", 5),
|
||||||
|
ENTERPRISE_SUB("企业子账号", 6),
|
||||||
|
EDUCATION_ADMIN("学校管理员", 7),
|
||||||
|
EDUCATION_SUB("学校子账号", 8);
|
||||||
|
|
||||||
|
private final String desc;
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
SystemRole(String desc, int code) {
|
||||||
|
this.desc = desc;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.ai.da.mapper.primary.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* submissions 表对应实体 — 参赛选手信息 (Contestant)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@TableName("contestants")
|
||||||
|
public class Contestant {
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@TableField("contestant_number")
|
||||||
|
private Integer contestantNumber;
|
||||||
|
|
||||||
|
@TableField("first_name")
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
@TableField("last_name")
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
private String gender;
|
||||||
|
|
||||||
|
private String occupation;
|
||||||
|
|
||||||
|
private Integer age;
|
||||||
|
|
||||||
|
@TableField("country_region_city")
|
||||||
|
private String countryRegionCity;
|
||||||
|
|
||||||
|
@TableField("phone_number")
|
||||||
|
private String phoneNumber;
|
||||||
|
|
||||||
|
@TableField("design_title")
|
||||||
|
private String designTitle;
|
||||||
|
|
||||||
|
@TableField("design_description")
|
||||||
|
private String designDescription;
|
||||||
|
|
||||||
|
@TableField("pdf_path")
|
||||||
|
private String pdfPath;
|
||||||
|
|
||||||
|
@TableField("video_path")
|
||||||
|
private String videoPath;
|
||||||
|
|
||||||
|
@TableField("video_duration")
|
||||||
|
private Integer videoDuration;
|
||||||
|
|
||||||
|
@TableField("video_size")
|
||||||
|
private Long videoSize;
|
||||||
|
|
||||||
|
@TableField("pdf_size")
|
||||||
|
private Long pdfSize;
|
||||||
|
|
||||||
|
@TableField("created_at")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@TableField("updated_at")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -62,4 +62,9 @@ public class DesignItemDetailPrint {
|
|||||||
* 更新时间
|
* 更新时间
|
||||||
*/
|
*/
|
||||||
private LocalDateTime updateDate;
|
private LocalDateTime updateDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象信息(JSON格式)
|
||||||
|
*/
|
||||||
|
private String object;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ public class SubscriptionPlan extends BaseEntity{
|
|||||||
*/
|
*/
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国家或地区
|
||||||
|
*/
|
||||||
|
private String countryOrRegion;
|
||||||
|
|
||||||
// 在类内部定义的枚举
|
// 在类内部定义的枚举
|
||||||
@Getter
|
@Getter
|
||||||
public enum SubscriptionStatus {
|
public enum SubscriptionStatus {
|
||||||
|
|||||||
@@ -90,6 +90,19 @@ public class TDesignPythonOutfitDetail implements Serializable {
|
|||||||
*/
|
*/
|
||||||
@Schema(description = "图层优先级")
|
@Schema(description = "图层优先级")
|
||||||
private Integer priority;
|
private Integer priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 镜像模式
|
||||||
|
*/
|
||||||
|
@Schema(description = "镜像模式")
|
||||||
|
private String transpose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旋转角度
|
||||||
|
*/
|
||||||
|
@Schema(description = "旋转角度")
|
||||||
|
private Double rotate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.ai.da.mapper.primary.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName("user_preference")
|
||||||
|
public class UserPreference implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
private Long accountId;
|
||||||
|
private String path;
|
||||||
|
private LocalDateTime dataTime;
|
||||||
|
private String category;
|
||||||
|
private String style;
|
||||||
|
private Long workspaceRelStyleId;
|
||||||
|
private Long projectId;
|
||||||
|
private Long designItemId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,5 +23,8 @@ public class UserPreferenceLogTest implements Serializable {
|
|||||||
private Long accountId;
|
private Long accountId;
|
||||||
private String path;
|
private String path;
|
||||||
private LocalDateTime dataTime;
|
private LocalDateTime dataTime;
|
||||||
|
private String category;
|
||||||
|
private String style;
|
||||||
|
private Long sysFileId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
74
src/main/java/com/ai/da/model/dto/ContestantDTO.java
Normal file
74
src/main/java/com/ai/da/model/dto/ContestantDTO.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contestant request DTO for Global Award
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "参赛者信息", description = "全球奖项大赛参赛者信息数据传输对象")
|
||||||
|
public class ContestantDTO {
|
||||||
|
@ApiModelProperty(value = "邮箱地址", required = true, example = "user@example.com")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "名字", required = true, example = "John")
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "姓氏", required = true, example = "Doe")
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "性别", required = true, example = "Male", allowableValues = "Male,Female,Other")
|
||||||
|
private String gender;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "职业", required = true, example = "Designer")
|
||||||
|
private String occupation;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "年龄", required = true, example = "25")
|
||||||
|
private Integer age;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "国家/地区/城市", required = true, example = "China/Shanghai/Shanghai")
|
||||||
|
private String countryRegionCity;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "电话号码", required = true, example = "+86 138 0000 0000")
|
||||||
|
private String phoneNumber;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "作品集链接", required = false, example = "https://portfolio.example.com")
|
||||||
|
private String portfolioUrl;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "设计作品标题", required = true, example = "Modern Office Building Design")
|
||||||
|
private String designTitle;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "设计作品描述", required = true, example = "A modern office building design featuring sustainable materials...")
|
||||||
|
private String designDescription;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "PDF文件路径", required = false, example = "contestants/user@example.com/2024/01/design_1234567890.pdf")
|
||||||
|
private String pdfPath;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "视频文件路径", required = false, example = "contestants/user@example.com/2024/01/video_1234567890.mp4")
|
||||||
|
private String videoPath;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "视频时长(秒)", required = false, example = "120")
|
||||||
|
private Integer videoDuration;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "视频大小(字节)", required = false, example = "10485760")
|
||||||
|
private Long videoSize;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "PDF 文件大小(字节)", required = false, example = "524288")
|
||||||
|
private Long pdfSize;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 是否确认覆盖已存在记录(false 表示发现已有记录时仅返回 existingRecord,不覆盖)
|
||||||
|
// */
|
||||||
|
// @ApiModelProperty(value = "是否确认覆盖已存在记录", required = false, example = "false")
|
||||||
|
// private Boolean confirm = false;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String secureToken;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参赛者文件导出请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "参赛者文件导出请求", description = "用于导出指定范围的参赛者文件")
|
||||||
|
public class ContestantExportRequest {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "最小参赛者编号", required = true, example = "10000")
|
||||||
|
private Integer minContestantNumber;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "最大参赛者编号", required = true, example = "10010")
|
||||||
|
private Integer maxContestantNumber;
|
||||||
|
}
|
||||||
@@ -43,6 +43,10 @@ public class DesignSingleIncludeLayersDTO implements Serializable {
|
|||||||
@Schema(description = "项目id")
|
@Schema(description = "项目id")
|
||||||
private Long projectId;
|
private Long projectId;
|
||||||
|
|
||||||
|
@NotBlank(message = "designType cannot be empty")
|
||||||
|
@Schema(description = "default -> 新增sketch || merge")
|
||||||
|
private String designType;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DesignSingleIncludeLayersDTO{" +
|
return "DesignSingleIncludeLayersDTO{" +
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ai.da.model.dto;
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import com.ai.da.mapper.primary.entity.Gradient;
|
import com.ai.da.mapper.primary.entity.Gradient;
|
||||||
@@ -67,4 +68,18 @@ public class DesignSingleItemDTO implements Serializable {
|
|||||||
|
|
||||||
private PartialDesignDTO partialDesign;
|
private PartialDesignDTO partialDesign;
|
||||||
|
|
||||||
|
@Schema(description = "镜像模式 ")
|
||||||
|
private int[] transpose;
|
||||||
|
|
||||||
|
@Schema(description = "45")
|
||||||
|
private double rotate;
|
||||||
|
|
||||||
|
@Hidden
|
||||||
|
@Schema(description = "带overall印花未分割图片")
|
||||||
|
private String undividedLayerBase64;
|
||||||
|
|
||||||
|
@Hidden
|
||||||
|
@Schema(description = "带overall/single印花未分割图片")
|
||||||
|
private String undividedLayerWithSinglePrintBase64;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/main/java/com/ai/da/model/dto/ImageProcessRequest.java
Normal file
55
src/main/java/com/ai/da/model/dto/ImageProcessRequest.java
Normal file
@@ -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<String> input_image_paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像宽度(width)
|
||||||
|
*/
|
||||||
|
private Integer width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像高度(height)
|
||||||
|
*/
|
||||||
|
private Integer height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本提示(prompt)
|
||||||
|
*/
|
||||||
|
private String prompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推理步数(steps)
|
||||||
|
*/
|
||||||
|
private Integer steps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引导系数(guidance)
|
||||||
|
*/
|
||||||
|
private Double guidance;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,17 +20,17 @@ public class PartialDesignDTO implements Serializable {
|
|||||||
@Schema(description = "图片的base64格式")
|
@Schema(description = "图片的base64格式")
|
||||||
private String partialDesignBase64;
|
private String partialDesignBase64;
|
||||||
|
|
||||||
@Schema(description = "图层信息")
|
/* @Schema(description = "图层信息")
|
||||||
private List<String> layers;
|
private List<String> layers;*/
|
||||||
|
|
||||||
public PartialDesignDTO(String partialDesignMinioPath) {
|
public PartialDesignDTO(String partialDesignMinioPath) {
|
||||||
this.partialDesignMinioPath = partialDesignMinioPath;
|
this.partialDesignMinioPath = partialDesignMinioPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PartialDesignDTO(String partialDesignMinioPath, List<String> layers) {
|
/* public PartialDesignDTO(String partialDesignMinioPath, List<String> layers) {
|
||||||
this.partialDesignMinioPath = partialDesignMinioPath;
|
this.partialDesignMinioPath = partialDesignMinioPath;
|
||||||
this.layers = layers;
|
this.layers = layers;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public PartialDesignDTO(String partialDesignMinioPath, String partialDesignPath) {
|
public PartialDesignDTO(String partialDesignMinioPath, String partialDesignPath) {
|
||||||
this.partialDesignMinioPath = partialDesignMinioPath;
|
this.partialDesignMinioPath = partialDesignMinioPath;
|
||||||
|
|||||||
@@ -48,4 +48,7 @@ public class SubscriptionPlanDTO {
|
|||||||
@Schema(description = "订阅计划状态")
|
@Schema(description = "订阅计划状态")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "国家或地区")
|
||||||
|
private String CountryOrRegion;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ public class SubscriptionPlanPageQuery extends QueryPageByTimeDTO {
|
|||||||
@Schema(description = "组织id")
|
@Schema(description = "组织id")
|
||||||
private Long organizationId;
|
private Long organizationId;
|
||||||
|
|
||||||
@Schema(description = "管理id")
|
@Schema(description = "管理员id")
|
||||||
private Long adminAccId;
|
private Long adminAccId;
|
||||||
|
|
||||||
@Schema(description = "状态 PENDING||ACTIVE||EXPIRED")
|
@Schema(description = "状态 PENDING||ACTIVE||EXPIRED")
|
||||||
private List<String> status;
|
private List<String> status;
|
||||||
|
|
||||||
|
@Schema(description = "国家或地区")
|
||||||
|
private String countryOrRegion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,4 +32,7 @@ public class UpdateSubscriptionPlanDTO {
|
|||||||
@Schema(description = "订阅重命名")
|
@Schema(description = "订阅重命名")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "国家或地区")
|
||||||
|
private String countryOrRegion;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/main/java/com/ai/da/model/dto/UploadChunkResponse.java
Normal file
33
src/main/java/com/ai/da/model/dto/UploadChunkResponse.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片上传响应DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@ApiModel(value = "分片上传响应", description = "单个文件分片上传成功的响应数据")
|
||||||
|
public class UploadChunkResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片索引
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "分片索引(从0开始)", required = true, example = "0")
|
||||||
|
private Integer chunkIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否上传成功
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "是否上传成功", required = true, example = "true")
|
||||||
|
private Boolean uploaded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片大小(字节)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "分片大小(字节)", required = true, example = "1048576")
|
||||||
|
private Long size;
|
||||||
|
}
|
||||||
53
src/main/java/com/ai/da/model/dto/UploadCompleteRequest.java
Normal file
53
src/main/java/com/ai/da/model/dto/UploadCompleteRequest.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成上传请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "完成上传请求", description = "文件上传完成时使用的请求参数")
|
||||||
|
public class UploadCompleteRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务ID
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "上传任务ID不能为空")
|
||||||
|
@ApiModelProperty(value = "上传任务唯一标识", required = true, example = "550e8400-e29b-41d4-a716-446655440000")
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "文件名不能为空")
|
||||||
|
@ApiModelProperty(value = "原始文件名", required = true, example = "design.pdf")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件总大小(字节)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "文件大小不能为空")
|
||||||
|
@Positive(message = "文件大小必须大于0")
|
||||||
|
@ApiModelProperty(value = "文件总大小(字节)", required = true, example = "10485760")
|
||||||
|
private Long totalSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户邮箱
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "用户邮箱不能为空")
|
||||||
|
@ApiModelProperty(value = "用户邮箱", required = true, example = "user@example.com")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全令牌(邮箱验证令牌)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "安全令牌不能为空")
|
||||||
|
@ApiModelProperty(value = "安全令牌", required = true, example = "abc123def456")
|
||||||
|
private String secureToken;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成上传响应DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@ApiModel(value = "完成上传响应", description = "文件上传完成并合并成功的响应数据")
|
||||||
|
public class UploadCompleteResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件在MinIO中的路径
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件在MinIO中的存储路径", required = true, example = "contestants/user@example.com/2024/01/design_1234567890.pdf")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件的完整URL
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件的完整访问URL", required = true, example = "https://minio.example.com/contestants/user@example.com/2024/01/design_1234567890.pdf")
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件大小(字节)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件大小(字节)", required = true, example = "10485760")
|
||||||
|
private Long fileSize;
|
||||||
|
}
|
||||||
53
src/main/java/com/ai/da/model/dto/UploadInitRequest.java
Normal file
53
src/main/java/com/ai/da/model/dto/UploadInitRequest.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化上传请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "初始化上传请求", description = "文件上传初始化时使用的请求参数")
|
||||||
|
public class UploadInitRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "文件名不能为空")
|
||||||
|
@ApiModelProperty(value = "文件名", required = true, example = "design.pdf")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件大小(字节)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "文件大小不能为空")
|
||||||
|
@Positive(message = "文件大小必须大于0")
|
||||||
|
@ApiModelProperty(value = "文件大小(字节)", required = true, example = "10485760")
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型(MIME类型)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "文件类型不能为空")
|
||||||
|
@ApiModelProperty(value = "文件类型(MIME类型)", required = true, example = "application/pdf")
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户邮箱
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "用户邮箱", required = true, example = "user@example.com")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全令牌(邮箱验证令牌)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "安全令牌不能为空")
|
||||||
|
@ApiModelProperty(value = "安全令牌", required = true, example = "abc123def456")
|
||||||
|
private String secureToken;
|
||||||
|
}
|
||||||
41
src/main/java/com/ai/da/model/dto/UploadInitResponse.java
Normal file
41
src/main/java/com/ai/da/model/dto/UploadInitResponse.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化上传响应DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@ApiModel(value = "初始化上传响应", description = "文件上传初始化成功的响应数据")
|
||||||
|
public class UploadInitResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务ID
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "上传任务唯一标识", required = true, example = "550e8400-e29b-41d4-a716-446655440000")
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片大小(字节)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "每个分片的大小(字节)", required = true, example = "1048576")
|
||||||
|
private Integer chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总分片数
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件被分成多少个分片", required = true, example = "10")
|
||||||
|
private Integer totalChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务过期时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "上传任务过期时间", required = true, example = "2024-01-20T10:30:00")
|
||||||
|
private LocalDateTime expiresAt;
|
||||||
|
}
|
||||||
59
src/main/java/com/ai/da/model/dto/UploadStatusResponse.java
Normal file
59
src/main/java/com/ai/da/model/dto/UploadStatusResponse.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.ai.da.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传状态响应DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@ApiModel(value = "上传状态响应", description = "查询上传任务当前状态的响应数据")
|
||||||
|
public class UploadStatusResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务ID
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "上传任务唯一标识", required = true, example = "550e8400-e29b-41d4-a716-446655440000")
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传状态
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "上传任务状态", required = true, example = "uploading", allowableValues = "initiated,uploading,completed,failed,expired")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传进度百分比 (0-100)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "上传进度百分比(0-100)", required = true, example = "60.0")
|
||||||
|
private Double progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已上传分片索引集合
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "已上传分片的索引集合", required = true, example = "[0,1,2,3,4]")
|
||||||
|
private Set<Integer> uploadedChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总分片数
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件被分成多少个分片", required = true, example = "10")
|
||||||
|
private Integer totalChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件总大小(字节)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "文件总大小(字节)", required = true, example = "10485760")
|
||||||
|
private Long totalSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已上传大小(字节)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "已上传的数据大小(字节)", required = true, example = "6291456")
|
||||||
|
private Long uploadedSize;
|
||||||
|
}
|
||||||
16
src/main/java/com/ai/da/model/vo/CheckOTPVO.java
Normal file
16
src/main/java/com/ai/da/model/vo/CheckOTPVO.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package com.ai.da.model.vo;
|
||||||
|
|
||||||
|
import com.ai.da.model.dto.ContestantDTO;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CheckOTPVO {
|
||||||
|
|
||||||
|
private String secureToken;
|
||||||
|
|
||||||
|
private ContestantDTO contestantDTO;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
package com.ai.da.model.vo;
|
package com.ai.da.model.vo;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import com.ai.da.mapper.primary.entity.Gradient;
|
import com.ai.da.mapper.primary.entity.Gradient;
|
||||||
@@ -62,11 +62,11 @@ public class DesignItemClothesDetailVO {
|
|||||||
@Schema(description = "渐变色信息")
|
@Schema(description = "渐变色信息")
|
||||||
private Gradient gradient;
|
private Gradient gradient;
|
||||||
|
|
||||||
@Schema(description = "未分割的图层")
|
/* @Schema(description = "未分割的图层")
|
||||||
private String undividedLayer;
|
private String undividedLayer;
|
||||||
|
|
||||||
@Schema(description = "添加single印花的未分割的图层")
|
@Schema(description = "添加single印花的未分割的图层")
|
||||||
private String undividedLayerWithSinglePrint;
|
private String undividedLayerWithSinglePrint;*/
|
||||||
|
|
||||||
@Schema(description = "局部design")
|
@Schema(description = "局部design")
|
||||||
private PartialDesignDTO partialDesign;
|
private PartialDesignDTO partialDesign;
|
||||||
|
|||||||
@@ -59,4 +59,16 @@ public class DesignPythonOutfitVO {
|
|||||||
* 图层优先级 从10开始,优先级数字越大越靠近上层
|
* 图层优先级 从10开始,优先级数字越大越靠近上层
|
||||||
*/
|
*/
|
||||||
private Integer priority;
|
private Integer priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 镜像模式
|
||||||
|
*/
|
||||||
|
@Schema(description = "镜像模式")
|
||||||
|
private int[] transpose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旋转角度
|
||||||
|
*/
|
||||||
|
@Schema(description = "旋转角度")
|
||||||
|
private Double rotate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ public class DesignSinglePrint implements Serializable {
|
|||||||
@Schema(description = "印花优先级")
|
@Schema(description = "印花优先级")
|
||||||
private Integer priority;
|
private Integer priority;
|
||||||
|
|
||||||
|
private String object;
|
||||||
|
|
||||||
public DesignSinglePrint() {
|
public DesignSinglePrint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,4 +45,7 @@ public class SubscriptionPlanVO {
|
|||||||
@Schema(description = "命名")
|
@Schema(description = "命名")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "国家或地区")
|
||||||
|
private String countryOrRegion;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ public class DesignPythonBasic {
|
|||||||
|
|
||||||
private String single_overall;
|
private String single_overall;
|
||||||
|
|
||||||
private String preview_submit;
|
// private String preview_submit;
|
||||||
|
|
||||||
private String switch_category;
|
private String switch_category;
|
||||||
/**
|
/**
|
||||||
@@ -40,4 +40,7 @@ public class DesignPythonBasic {
|
|||||||
|
|
||||||
private Boolean layer_order = Boolean.FALSE;
|
private Boolean layer_order = Boolean.FALSE;
|
||||||
|
|
||||||
|
// default | merge
|
||||||
|
private String design_type = "default";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,22 @@ public class DesignPythonItem {
|
|||||||
*/
|
*/
|
||||||
private String seg_mask_url;
|
private String seg_mask_url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 镜像模式
|
||||||
|
*/
|
||||||
|
private int[] transpose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旋转角度
|
||||||
|
*/
|
||||||
|
private double rotate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前端处理了print之后的结果图,python对该图进行分割
|
||||||
|
* designType为merge时,该字段必须有值,否则会导致python端没有数据返回
|
||||||
|
*/
|
||||||
|
private String merge_image_path;
|
||||||
|
|
||||||
public static List<String> OUTWEAR_DRESS_BLOUSE = Arrays.asList(CollectionLevel2TypeEnum.OUTWEAR.getRealName(),
|
public static List<String> OUTWEAR_DRESS_BLOUSE = Arrays.asList(CollectionLevel2TypeEnum.OUTWEAR.getRealName(),
|
||||||
CollectionLevel2TypeEnum.DRESS.getRealName(), CollectionLevel2TypeEnum.BLOUSE.getRealName());
|
CollectionLevel2TypeEnum.DRESS.getRealName(), CollectionLevel2TypeEnum.BLOUSE.getRealName());
|
||||||
|
|
||||||
@@ -143,7 +159,8 @@ public class DesignPythonItem {
|
|||||||
|
|
||||||
public DesignPythonItem(String type, String path, String color, PrintToPython print, Long businessId,
|
public DesignPythonItem(String type, String path, String color, PrintToPython print, Long businessId,
|
||||||
Long image_id, List<Long> offset, Float[] resize_scale, Integer priority, String gradient,
|
Long image_id, List<Long> offset, Float[] resize_scale, Integer priority, String gradient,
|
||||||
String gradientString, String seg_mask_url) {
|
String gradientString, String seg_mask_url, int[] transpose, double rotate,
|
||||||
|
String merge_image_path) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
@@ -157,6 +174,9 @@ public class DesignPythonItem {
|
|||||||
this.gradient = gradient;
|
this.gradient = gradient;
|
||||||
this.gradientString = gradientString;
|
this.gradientString = gradientString;
|
||||||
this.seg_mask_url = seg_mask_url;
|
this.seg_mask_url = seg_mask_url;
|
||||||
|
this.transpose = transpose;
|
||||||
|
this.rotate = rotate;
|
||||||
|
this.merge_image_path = merge_image_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DesignPythonItem(String type, String path, String color, PrintToPython print, String icon, Long businessId, Long image_id) {
|
public DesignPythonItem(String type, String path, String color, PrintToPython print, String icon, Long businessId, Long image_id) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.ai.da.python.vo;
|
package com.ai.da.python.vo;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class DesignPythonItemPrint {
|
public class DesignPythonItemPrint {
|
||||||
@@ -53,7 +53,7 @@ public class DesignPythonItemPrint {
|
|||||||
if (ifDesign){
|
if (ifDesign){
|
||||||
this.print_path_list = print_path_list;
|
this.print_path_list = print_path_list;
|
||||||
this.location = Collections.singletonList(Arrays.asList(0.0f, 0.0f));
|
this.location = Collections.singletonList(Arrays.asList(0.0f, 0.0f));
|
||||||
this.print_scale_list = Collections.singletonList(Arrays.asList(0.0f, 0.0f));
|
this.print_scale_list = Collections.singletonList(Arrays.asList(1.0f, 1.0f));
|
||||||
this.print_angle_list = Arrays.asList(0.0, 0.0);
|
this.print_angle_list = Arrays.asList(0.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,4 +246,6 @@ public interface AccountService extends IService<Account> {
|
|||||||
void setEduAdminToExpire(Account adminAccount);
|
void setEduAdminToExpire(Account adminAccount);
|
||||||
|
|
||||||
String getOrganizationTypeByRole(Integer roleNum);
|
String getOrganizationTypeByRole(Integer roleNum);
|
||||||
|
|
||||||
|
void validateUserValidaExpire(Account account);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public interface ConvenientInquiryService extends IService<Questionnaire> {
|
|||||||
|
|
||||||
IPage<Account> getUserInfo(QueryUserConditionsVO queryUserConditionsVO);
|
IPage<Account> getUserInfo(QueryUserConditionsVO queryUserConditionsVO);
|
||||||
|
|
||||||
List<Map<String, Object>> getAllUserIdList();
|
IPage<Map<String, Object>> getAllUserIdList(Integer pageNum, Integer pageSize, String email);
|
||||||
|
|
||||||
PageBaseResponse<PaymentInfoVO> queryTransactionRecords(QueryPaymentInfoDTO queryPaymentInfoDTO);
|
PageBaseResponse<PaymentInfoVO> queryTransactionRecords(QueryPaymentInfoDTO queryPaymentInfoDTO);
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public interface DesignItemService extends IService<DesignItem> {
|
|||||||
|
|
||||||
DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO);
|
DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO);
|
||||||
|
|
||||||
Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers);
|
Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO);
|
||||||
|
|
||||||
Map<String, String> setTypeAndUndividedLayer(JSONArray layers);
|
Map<String, String> setTypeAndUndividedLayer(JSONArray layers);
|
||||||
|
|
||||||
|
|||||||
44
src/main/java/com/ai/da/service/GlobalAwardService.java
Normal file
44
src/main/java/com/ai/da/service/GlobalAwardService.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package com.ai.da.service;
|
||||||
|
|
||||||
|
import com.ai.da.model.dto.ContestantDTO;
|
||||||
|
import com.ai.da.model.vo.CheckOTPVO;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface GlobalAwardService {
|
||||||
|
String uploadPdf(MultipartFile file, String email) throws Exception;
|
||||||
|
|
||||||
|
String uploadVideo(MultipartFile file, String email) throws Exception;
|
||||||
|
|
||||||
|
Map<String, Object> saveContestant(ContestantDTO request);
|
||||||
|
|
||||||
|
com.ai.da.model.dto.ContestantDTO getContestantByID(String email);
|
||||||
|
|
||||||
|
void checkEmail(String email);
|
||||||
|
|
||||||
|
CheckOTPVO checkCode(String email, String otp);
|
||||||
|
|
||||||
|
void checkSecurityToken(String email, String securityToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出参赛者列表为 Excel(二进制)
|
||||||
|
* @return Excel 文件的字节数组
|
||||||
|
*/
|
||||||
|
byte[] exportContestants() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将参赛者列表导出并保存到服务端本地目录(使用服务配置的 uploadDir/exports)
|
||||||
|
*/
|
||||||
|
void saveContestantsToLocal() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参赛者编号范围导出参赛者文件到本地目录
|
||||||
|
* @param minContestantNumber 最小参赛者编号
|
||||||
|
* @param maxContestantNumber 最大参赛者编号
|
||||||
|
* @return 导出的参赛者数量
|
||||||
|
*/
|
||||||
|
int exportContestantFiles(Integer minContestantNumber, Integer maxContestantNumber) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -7,6 +7,8 @@ public interface RabbitMQService {
|
|||||||
|
|
||||||
void publishMessageToGenerate(String message);
|
void publishMessageToGenerate(String message);
|
||||||
|
|
||||||
|
void publishMessageToGenerateResult(String message);
|
||||||
|
|
||||||
void publishMessageToSR(String message);
|
void publishMessageToSR(String message);
|
||||||
|
|
||||||
Integer getMessageCount(String queueUrl);
|
Integer getMessageCount(String queueUrl);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public interface SubscriptionPlanService extends IService<SubscriptionPlan> {
|
|||||||
|
|
||||||
void switchSubAccSubscriptionPlan(Long subscriptionPlanId, Long subAccId);
|
void switchSubAccSubscriptionPlan(Long subscriptionPlanId, Long subAccId);
|
||||||
|
|
||||||
void activeSubscriptionPlan();
|
void activeSubscriptionPlan(Long planId);
|
||||||
|
|
||||||
void expireSubscription();
|
void expireSubscription();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.usermodel.*;
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
@@ -83,6 +84,9 @@ import java.util.stream.Collectors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
|
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
|
||||||
|
@Resource
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AccountMapper accountMapper;
|
private AccountMapper accountMapper;
|
||||||
|
|
||||||
@@ -132,6 +136,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
@Resource
|
@Resource
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.ai.da.common.security.config.SecurityProperties securityProperties;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private UserFollowService userFollowService;
|
private UserFollowService userFollowService;
|
||||||
|
|
||||||
@@ -237,12 +244,13 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
|
AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
|
||||||
response.setEmail(account.getUserEmail());
|
response.setEmail(account.getUserEmail());
|
||||||
String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
|
String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
|
||||||
if (StringUtils.isNotBlank(token)) {
|
/*if (StringUtils.isNotBlank(token)) {
|
||||||
//用户已登入
|
//用户已登入
|
||||||
response.setToken(token);
|
response.setToken(token);
|
||||||
} else {
|
} else {
|
||||||
response.setToken(createAccountToken(account));
|
response.setToken(createAccountToken(account));
|
||||||
}
|
}*/
|
||||||
|
response.setToken(createAccountToken(account));
|
||||||
response.setUserId(account.getId());
|
response.setUserId(account.getId());
|
||||||
response.setSystemUser(account.getSystemUser());
|
response.setSystemUser(account.getSystemUser());
|
||||||
// 设置头像
|
// 设置头像
|
||||||
@@ -276,7 +284,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
// 定义常量(临时)
|
// 定义常量(临时)
|
||||||
private static final Integer SYSTEM_USER_TYPE_EDU_ADMIN = 7;
|
private static final Integer SYSTEM_USER_TYPE_EDU_ADMIN = 7;
|
||||||
|
|
||||||
private void validateUserValidaExpire(Account account) {
|
public void validateUserValidaExpire(Account account) {
|
||||||
Long currentTime = new Date().getTime();
|
Long currentTime = new Date().getTime();
|
||||||
if (account.getSystemUser().equals(0)) {
|
if (account.getSystemUser().equals(0)) {
|
||||||
return;
|
return;
|
||||||
@@ -294,7 +302,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
if (isEduAdmin) {
|
if (isEduAdmin) {
|
||||||
setEduAdminToExpire(account);
|
setEduAdminToExpire(account);
|
||||||
} else {
|
} else {
|
||||||
toVisitor(account);
|
// 这里调用代理的 toVisitor 方法
|
||||||
|
AccountService proxy = applicationContext.getBean(AccountService.class);
|
||||||
|
proxy.toVisitor(account);
|
||||||
// return;
|
// return;
|
||||||
throw new BusinessException("account.expired", ResultEnum.PROMPT.getCode());
|
throw new BusinessException("account.expired", ResultEnum.PROMPT.getCode());
|
||||||
}
|
}
|
||||||
@@ -344,7 +354,11 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
principal.setLanguage(account.getLanguage());
|
principal.setLanguage(account.getLanguage());
|
||||||
principal.setCountry(account.getCountry());
|
principal.setCountry(account.getCountry());
|
||||||
String token2 = jwtTokenHelper.createToken(principal);
|
String token2 = jwtTokenHelper.createToken(principal);
|
||||||
|
// 本地 JVM 缓存(适配旧逻辑)
|
||||||
LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);
|
LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);
|
||||||
|
// 同步写入 Redis,重启后仍然可用
|
||||||
|
long jwtExpiration = securityProperties.getJwtExpiration();
|
||||||
|
redisUtil.setLoginToken(account.getId(), token2, jwtExpiration);
|
||||||
return token2;
|
return token2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,21 +614,25 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean logout(AccountLogoutDTO accountLogoutDTO) {
|
public Boolean logout(AccountLogoutDTO accountLogoutDTO) {
|
||||||
//jwt本身失效比较难做 统一用缓存实现 删除缓存就失效
|
// jwt 本身失效比较难做,统一用缓存实现:删除缓存即失效
|
||||||
String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId()));
|
String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
|
||||||
if (StringUtils.isNotBlank(token)) {
|
LocalCacheUtils.delTokenCache(userIdStr);
|
||||||
LocalCacheUtils.delTokenCache(String.valueOf(accountLogoutDTO.getUserId()));
|
// 同时删除 Redis 中的 token,防止服务重启后仍然有效
|
||||||
}
|
redisUtil.deleteLoginToken(accountLogoutDTO.getUserId());
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) {
|
public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) {
|
||||||
String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId()));
|
String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
|
||||||
|
// 先查本地缓存
|
||||||
|
String token = LocalCacheUtils.getTokenCache(userIdStr);
|
||||||
if (StringUtils.isNotBlank(token)) {
|
if (StringUtils.isNotBlank(token)) {
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
return Boolean.FALSE;
|
// 本地没有时,再查 Redis,保证服务重启后也能判断登录状态
|
||||||
|
String redisToken = redisUtil.getLoginToken(accountLogoutDTO.getUserId());
|
||||||
|
return StringUtils.isNotBlank(redisToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -812,6 +830,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
if (StringUtils.isNotBlank(token)) {
|
if (StringUtils.isNotBlank(token)) {
|
||||||
LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId()));
|
LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId()));
|
||||||
}
|
}
|
||||||
|
// 删除 Redis 中对应的登录 token
|
||||||
|
redisUtil.deleteLoginToken(accountDelete.getId());
|
||||||
if (!userName.equals(userToBeUpdate.getUserName())) {
|
if (!userName.equals(userToBeUpdate.getUserName())) {
|
||||||
// accountMapper.deleteById(accountDelete);
|
// accountMapper.deleteById(accountDelete);
|
||||||
log.info("排查用户被删除原因:deleteNoLoginRequired,true, 删除用户(改为降为游客)");
|
log.info("排查用户被删除原因:deleteNoLoginRequired,true, 删除用户(改为降为游客)");
|
||||||
@@ -1065,6 +1085,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
if (StringUtils.isNotBlank(token)) {
|
if (StringUtils.isNotBlank(token)) {
|
||||||
LocalCacheUtils.delTokenCache(String.valueOf(account.getId()));
|
LocalCacheUtils.delTokenCache(String.valueOf(account.getId()));
|
||||||
}
|
}
|
||||||
|
// 删除 Redis 中对应的登录 token
|
||||||
|
redisUtil.deleteLoginToken(account.getId());
|
||||||
// accountMapper.deleteById(account.getId());
|
// accountMapper.deleteById(account.getId());
|
||||||
log.info("排查用户被删除原因:deleteNoLoginRequiredNew,删除用户(改为降为游客)");
|
log.info("排查用户被删除原因:deleteNoLoginRequiredNew,删除用户(改为降为游客)");
|
||||||
accountMapper.toVisitor(account.getId());
|
accountMapper.toVisitor(account.getId());
|
||||||
@@ -1269,7 +1291,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
account.setSystemUser(3);
|
account.setSystemUser(3);
|
||||||
account.setIsTrial(1);
|
account.setIsTrial(1);
|
||||||
account.setCredits(BigDecimal.valueOf(50));
|
account.setCredits(BigDecimal.valueOf(50));
|
||||||
account.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
|
// 广场用户试用延长至7天
|
||||||
|
account.setValidEndTime(toDayEnd(Instant.now().plus(7, ChronoUnit.DAYS).toEpochMilli()));
|
||||||
account.setIsBeginner(1);
|
account.setIsBeginner(1);
|
||||||
account.setValidStartTime(Instant.now().toEpochMilli());
|
account.setValidStartTime(Instant.now().toEpochMilli());
|
||||||
account.setCreateDate(new Date());
|
account.setCreateDate(new Date());
|
||||||
@@ -1933,6 +1956,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
return baseMapper.selectList(queryWrapper);
|
return baseMapper.selectList(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
public void toVisitor(Account account) {
|
public void toVisitor(Account account) {
|
||||||
accountMapper.toVisitor(account.getId());
|
accountMapper.toVisitor(account.getId());
|
||||||
}
|
}
|
||||||
@@ -2491,7 +2515,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 判断当前账号的有效期是否与管理员同步
|
// 判断当前账号的有效期是否与管理员同步
|
||||||
if (subAccount.getValidEndTime() < adminAcc.getValidEndTime()){
|
if (Objects.isNull(subAccount.getValidEndTime()) || subAccount.getValidEndTime() < adminAcc.getValidEndTime()){
|
||||||
subAccount.setValidEndTime(adminAcc.getValidEndTime());
|
subAccount.setValidEndTime(adminAcc.getValidEndTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3553,7 +3577,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
|||||||
// 只有当前子账号数量为0时允许批量上传
|
// 只有当前子账号数量为0时允许批量上传
|
||||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||||
qw.lambda().eq(Account::getSystemUser, 8)
|
qw.lambda().eq(Account::getSystemUser, 8)
|
||||||
.eq(Account::getOrganizationId, parent.getOrganizationId());
|
.eq(Account::getOrganizationId, parent.getOrganizationId())
|
||||||
|
.eq(Account::getSubscriptionPlanId, parent.getSubscriptionPlanId());
|
||||||
List<Account> accounts = accountMapper.selectList(qw);
|
List<Account> accounts = accountMapper.selectList(qw);
|
||||||
if (!accounts.isEmpty()) {
|
if (!accounts.isEmpty()) {
|
||||||
throw new BusinessException("permit.bulk.creation", ResultEnum.PROMPT.getCode());
|
throw new BusinessException("permit.bulk.creation", ResultEnum.PROMPT.getCode());
|
||||||
|
|||||||
@@ -520,9 +520,10 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
|
|||||||
.filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
|
.filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
|
||||||
.map(DesignCollectionPrintElementDTO::getId)
|
.map(DesignCollectionPrintElementDTO::getId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
List<CollectionElement> printBoardElements = new ArrayList<>();
|
||||||
if (!CollectionUtils.isEmpty(printBoardIds)) {
|
if (!CollectionUtils.isEmpty(printBoardIds)) {
|
||||||
// 从数据库批量查询printBoard元素
|
// 从数据库批量查询printBoard元素
|
||||||
List<CollectionElement> printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
|
printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
|
||||||
// 验证查询结果的完整性
|
// 验证查询结果的完整性
|
||||||
if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) {
|
if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) {
|
||||||
throw new BusinessException("get.printBoards.data.is.mismatch");
|
throw new BusinessException("get.printBoards.data.is.mismatch");
|
||||||
@@ -543,7 +544,8 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
|
|||||||
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
|
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
|
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
|
||||||
libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
|
printBoardElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
|
||||||
|
// libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,7 +561,8 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
|
|||||||
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
|
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
|
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
|
||||||
generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
|
printBoardElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
|
||||||
|
// generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import io.netty.util.internal.StringUtil;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.poi.xssf.usermodel.*;
|
import org.apache.poi.xssf.usermodel.*;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -149,7 +148,17 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
|||||||
|| ADMIN_IDS.contains(account.getId())
|
|| ADMIN_IDS.contains(account.getId())
|
||||||
|| ADMIN_IDS_READ_ONLY.contains(account.getId())
|
|| ADMIN_IDS_READ_ONLY.contains(account.getId())
|
||||||
)) {
|
)) {
|
||||||
if (StringUtil.isNullOrEmpty(startTime)) startTime = "2024-02-01 00:00:00";
|
boolean filterBySecond ;
|
||||||
|
if (StringUtil.isNullOrEmpty(startTime)) {
|
||||||
|
startTime = "2024-02-01 00:00:00";
|
||||||
|
filterBySecond = true;
|
||||||
|
} else {
|
||||||
|
LocalDateTime thresholdTime = LocalDateTime.of(2024, 5, 1, 0, 0, 0);
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
LocalDateTime startDateTime = LocalDateTime.parse(startTime, formatter);
|
||||||
|
filterBySecond = startDateTime.isBefore(thresholdTime);
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtil.isNullOrEmpty(endTime)) {
|
if (StringUtil.isNullOrEmpty(endTime)) {
|
||||||
// yyyy-MM-dd HH:mm:ss "HH"表示24小时制 "hh"表示12小时制
|
// yyyy-MM-dd HH:mm:ss "HH"表示24小时制 "hh"表示12小时制
|
||||||
endTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
endTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
@@ -173,7 +182,7 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
|||||||
default:
|
default:
|
||||||
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
||||||
}
|
}
|
||||||
return designMapper.getDesignStatistic(startTime, endTime, ids, email, role, account.getOrganizationName());
|
return designMapper.getDesignStatistic(startTime, endTime, ids, email, role, account.getOrganizationName(), filterBySecond);
|
||||||
} else {
|
} else {
|
||||||
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
||||||
}
|
}
|
||||||
@@ -695,14 +704,19 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
|||||||
|
|
||||||
String username = UserContext.getUserHolder().getUsername();
|
String username = UserContext.getUserHolder().getUsername();
|
||||||
Account account = new Account();
|
Account account = new Account();
|
||||||
|
account.setId(accountId);
|
||||||
// 修改用户有效期截止日期、用户类型、积分
|
// 修改用户有效期截止日期、用户类型、积分
|
||||||
if (!Objects.isNull(validEndTime)) {
|
if (!Objects.isNull(validEndTime)) {
|
||||||
account.setValidEndTime(validEndTime);
|
account.setValidEndTime(validEndTime);
|
||||||
log.info("管理员:{},修改用户 {} 信息,将账号到期时间置为:{}", username, accountId, validEndTime);
|
log.info("管理员:{},修改用户 {} 信息,将账号到期时间置为:{}", username, accountId, validEndTime);
|
||||||
}
|
}
|
||||||
if (!Objects.isNull(systemUser)) {
|
if (!Objects.isNull(systemUser) && !systemUser.equals(0)) {
|
||||||
account.setSystemUser(systemUser);
|
account.setSystemUser(systemUser);
|
||||||
log.info("管理员:{},修改用户 {} 信息,将账号身份置为:{}", username, accountId, systemUser);
|
log.info("管理员:{},修改用户 {} 信息,将账号身份置为:{}", username, accountId, systemUser);
|
||||||
|
} else if (systemUser.equals(0)){
|
||||||
|
// 将用户身份设置为游客
|
||||||
|
accountService.toVisitor(account);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
/*if (!StringUtils.isNullOrEmpty(systemUser)) {
|
/*if (!StringUtils.isNullOrEmpty(systemUser)) {
|
||||||
int systemUser = 0;
|
int systemUser = 0;
|
||||||
@@ -728,7 +742,6 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// todo 如果修改管理员账号的积分上限或子账号数量,则其所有子账号的积分上限需要重新计算
|
// todo 如果修改管理员账号的积分上限或子账号数量,则其所有子账号的积分上限需要重新计算
|
||||||
account.setId(accountId);
|
|
||||||
account.setUpdateDate(new Date());
|
account.setUpdateDate(new Date());
|
||||||
return accountMapper.updateById(account) == 1;
|
return accountMapper.updateById(account) == 1;
|
||||||
// accountService.update(account,null);
|
// accountService.update(account,null);
|
||||||
@@ -798,27 +811,46 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
|||||||
return accountMapper.selectPage(new Page<>(queryUserConditionsVO.getPage(), queryUserConditionsVO.getSize()), queryWrapper);
|
return accountMapper.selectPage(new Page<>(queryUserConditionsVO.getPage(), queryUserConditionsVO.getSize()), queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Map<String, Object>> getAllUserIdList() {
|
public IPage<Map<String, Object>> getAllUserIdList(Integer pageNum, Integer pageSize, String email) {
|
||||||
Long accountId = UserContext.getUserHolder().getId();
|
Long accountId = UserContext.getUserHolder().getId();
|
||||||
Account account = accountMapper.selectById(accountId);
|
Account account = accountMapper.selectById(accountId);
|
||||||
// 允许查看数据的用户id
|
|
||||||
if (Objects.nonNull(account.getSystemUser())
|
// 权限校验
|
||||||
&& (account.getSystemUser().equals(5)
|
if (Objects.isNull(account.getSystemUser())
|
||||||
|| account.getSystemUser().equals(7)
|
|| (!account.getSystemUser().equals(5)
|
||||||
|| ADMIN_IDS.contains(account.getId())
|
&& !account.getSystemUser().equals(7)
|
||||||
|| ADMIN_IDS_READ_ONLY.contains(account.getId())
|
&& !ADMIN_IDS.contains(account.getId())
|
||||||
)) {
|
&& !ADMIN_IDS_READ_ONLY.contains(account.getId()))) {
|
||||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
|
||||||
queryWrapper.select("id as value, user_name as label");
|
|
||||||
if ((account.getSystemUser().equals(7) || account.getSystemUser().equals(5))
|
|
||||||
&& !StringUtil.isNullOrEmpty(account.getOrganizationName())) {
|
|
||||||
queryWrapper.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
|
|
||||||
}
|
|
||||||
return accountMapper.selectMaps(queryWrapper);
|
|
||||||
} else {
|
|
||||||
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
||||||
}
|
}
|
||||||
// return maps.stream().map(map -> (Long)map.get("id")).collect(Collectors.toList());
|
|
||||||
|
// 创建分页对象
|
||||||
|
Page<Account> page = new Page<>(pageNum, pageSize);
|
||||||
|
|
||||||
|
// 构建查询条件
|
||||||
|
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.select("id", "user_name", "user_email");
|
||||||
|
|
||||||
|
if ((account.getSystemUser().equals(7) || account.getSystemUser().equals(5))
|
||||||
|
&& !StringUtil.isNullOrEmpty(account.getOrganizationName())) {
|
||||||
|
queryWrapper.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtil.isNullOrEmpty(email)) {
|
||||||
|
queryWrapper.lambda().like(Account::getUserEmail, email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行分页查询
|
||||||
|
IPage<Account> accountPage = accountMapper.selectPage(page, queryWrapper);
|
||||||
|
|
||||||
|
// 转换为 IPage<Map> 并重命名字段
|
||||||
|
return accountPage.convert(acc -> {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("value", acc.getId());
|
||||||
|
map.put("label", acc.getUserName());
|
||||||
|
map.put("email", acc.getUserEmail());
|
||||||
|
return map;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -838,19 +870,20 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
|||||||
if (!StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getOrder()) && queryPaymentInfoDTO.getOrder().equals("ASC")) {
|
if (!StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getOrder()) && queryPaymentInfoDTO.getOrder().equals("ASC")) {
|
||||||
order = "ASC";
|
order = "ASC";
|
||||||
}
|
}
|
||||||
|
String status = StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getStatus()) ? "Success" : queryPaymentInfoDTO.getStatus();
|
||||||
List<PaymentInfoVO> paymentInfoVOS = paymentInfoMapper.queryPaymentInfo(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
List<PaymentInfoVO> paymentInfoVOS = paymentInfoMapper.queryPaymentInfo(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
||||||
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(),
|
queryPaymentInfoDTO.getType(), status,
|
||||||
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
||||||
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(),
|
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(),
|
||||||
size, offset, order, queryPaymentInfoDTO.getPayer());
|
size, offset, order, queryPaymentInfoDTO.getPayer());
|
||||||
// 查询数据总量
|
// 查询数据总量
|
||||||
Long total = paymentInfoMapper.queryPaymentInfoCount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
Long total = paymentInfoMapper.queryPaymentInfoCount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
||||||
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(),
|
queryPaymentInfoDTO.getType(), status,
|
||||||
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
||||||
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
|
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
|
||||||
// 查询符合查询条件的总金额
|
// 查询符合查询条件的总金额
|
||||||
BigDecimal payerTotal = paymentInfoMapper.queryTotalPaymentAmount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
BigDecimal payerTotal = paymentInfoMapper.queryTotalPaymentAmount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
||||||
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(),
|
queryPaymentInfoDTO.getType(), status,
|
||||||
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
||||||
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
|
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
|
||||||
// 总页数
|
// 总页数
|
||||||
|
|||||||
@@ -363,9 +363,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
public List<TDesignPythonOutfitDetail> saveDesignSingleItemDetailAndLayers(DesignPythonObjects pythonObjects
|
public List<TDesignPythonOutfitDetail> saveDesignSingleItemDetailAndLayers(DesignPythonObjects pythonObjects
|
||||||
, Long designId, Long designItemId, Long userId
|
, Long designId, Long designItemId, Long userId
|
||||||
, JSONObject outfit, String timeZone, List<DesignSingleItemDTO> designSingleItemDTOList
|
, JSONObject outfit, String timeZone, List<DesignSingleItemDTO> designSingleItemDTOList
|
||||||
, Map<String, List<String>> priorityAndUndividedLayer
|
/*, Map<String, List<String>> priorityAndUndividedLayer*/
|
||||||
, boolean changeModelFlag
|
, boolean changeModelFlag
|
||||||
, Long modelId, String modelType, boolean isSingleCollectionFlag) {
|
, Long modelId, String modelType, boolean isSingleCollectionFlag, String designType) {
|
||||||
|
|
||||||
DesignItem designItem = new DesignItem();
|
DesignItem designItem = new DesignItem();
|
||||||
// String url = pythonObjects.getObjects().get(0).getBasic().getSave_name();
|
// String url = pythonObjects.getObjects().get(0).getBasic().getSave_name();
|
||||||
@@ -424,11 +424,15 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
designItemDetail.setColor(detail.getColor());
|
designItemDetail.setColor(detail.getColor());
|
||||||
designItemDetail.setIconPath(detail.getIcon());
|
designItemDetail.setIconPath(detail.getIcon());
|
||||||
// designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getType().toLowerCase()));
|
// designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getType().toLowerCase()));
|
||||||
|
/*// 取消存储UndividedLayer和UndividedLayerWithSinglePrint字段
|
||||||
if (!detail.getType().equals("Body")) {
|
if (!detail.getType().equals("Body")) {
|
||||||
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0));
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0))) {
|
||||||
designItemDetail.setUndividedLayerWithSinglePrint(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1));
|
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).getFirst());
|
||||||
|
}
|
||||||
}
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1))) {
|
||||||
|
designItemDetail.setUndividedLayerWithSinglePrint(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
// 印花存储在design_item_detail_print表中 这里还要存吗?
|
// 印花存储在design_item_detail_print表中 这里还要存吗?
|
||||||
// DesignPythonItemPrint printObject = detail.getPrintToPython();
|
// DesignPythonItemPrint printObject = detail.getPrintToPython();
|
||||||
// designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
|
// designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
|
||||||
@@ -436,7 +440,18 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
// designItemDetail.setPrintJson(JSON.toJSONString(printObject));
|
// designItemDetail.setPrintJson(JSON.toJSONString(printObject));
|
||||||
|
|
||||||
designItemDetail.setPartialDesign(Objects.isNull(detail.getPrint()) ? null : detail.getPrint().getPartial());
|
designItemDetail.setPartialDesign(Objects.isNull(detail.getPrint()) ? null : detail.getPrint().getPartial());
|
||||||
|
designItemDetail.setGradientString(detail.getGradientString());
|
||||||
|
|
||||||
designItemDetails.add(designItemDetail);
|
designItemDetails.add(designItemDetail);
|
||||||
|
|
||||||
|
// 处理gradientString为null的情况,强制更新为null
|
||||||
|
if (Objects.isNull(detail.getGradientString()) && Objects.nonNull(designItemDetail.getId())) {
|
||||||
|
UpdateWrapper<DesignItemDetail> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.eq("id", designItemDetail.getId());
|
||||||
|
updateWrapper.set("gradient_string", null);
|
||||||
|
updateWrapper.set("update_date", DateUtil.getByTimeZone(timeZone));
|
||||||
|
designItemDetailService.update(null, updateWrapper);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 逻辑删除未复用的旧记录
|
// 逻辑删除未复用的旧记录
|
||||||
@@ -481,6 +496,11 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
priorityOffset = designSingleItemDTOList.stream()
|
priorityOffset = designSingleItemDTOList.stream()
|
||||||
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
|
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建 priority 到 DesignSingleItemDTO 的映射,用于获取 transpose 和 rotate
|
||||||
|
Map<Integer, DesignSingleItemDTO> priorityToItemDTOMap = designSingleItemDTOList.stream()
|
||||||
|
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, dto -> dto, (old, newVal) -> old));
|
||||||
|
|
||||||
List<TDesignPythonOutfitDetail> list = new ArrayList<>();
|
List<TDesignPythonOutfitDetail> list = new ArrayList<>();
|
||||||
for (int i = 0; i < layers.size(); i++) {
|
for (int i = 0; i < layers.size(); i++) {
|
||||||
JSONObject jsonObject = layers.getJSONObject(i);
|
JSONObject jsonObject = layers.getJSONObject(i);
|
||||||
@@ -509,6 +529,12 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
|
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
|
||||||
}
|
}
|
||||||
designPythonOutfitDetail.setPriority(priority);
|
designPythonOutfitDetail.setPriority(priority);
|
||||||
|
// 从前端传入的 DesignSingleItemDTO 中获取 transpose 和 rotate,不再从 Python 返回的数据获取
|
||||||
|
DesignSingleItemDTO itemDTO = priorityToItemDTOMap.get(Math.abs(priority));
|
||||||
|
if (itemDTO != null) {
|
||||||
|
designPythonOutfitDetail.setTranspose(itemDTO.getTranspose() != null ? Arrays.toString(itemDTO.getTranspose()) : null);
|
||||||
|
designPythonOutfitDetail.setRotate(itemDTO.getRotate());
|
||||||
|
}
|
||||||
|
|
||||||
list.add(designPythonOutfitDetail);
|
list.add(designPythonOutfitDetail);
|
||||||
}
|
}
|
||||||
@@ -683,6 +709,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
public DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||||
|
if (!designSingleIncludeLayersDTO.getDesignType().equals("merge") && !designSingleIncludeLayersDTO.getDesignType().equals("default")) {
|
||||||
|
throw new BusinessException("The value of DesignType can only be 'default' or 'merge' ");
|
||||||
|
}
|
||||||
// 记录入参 base64数据太长,所以这里去掉
|
// 记录入参 base64数据太长,所以这里去掉
|
||||||
DesignSingleIncludeLayersDTO clone = SerializationUtils.clone(designSingleIncludeLayersDTO);
|
DesignSingleIncludeLayersDTO clone = SerializationUtils.clone(designSingleIncludeLayersDTO);
|
||||||
clone.getDesignSingleItemDTOList().forEach(i -> {
|
clone.getDesignSingleItemDTOList().forEach(i -> {
|
||||||
@@ -706,6 +735,17 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
log.info("set partialDesignBase64为空,便于日志打印");
|
log.info("set partialDesignBase64为空,便于日志打印");
|
||||||
i.getPartialDesign().setPartialDesignBase64(null);
|
i.getPartialDesign().setPartialDesignBase64(null);
|
||||||
}
|
}
|
||||||
|
// undividedLayerBase64 undividedLayerWithSinglePrintBase64 置空
|
||||||
|
/*// 前端合成的未分割的图
|
||||||
|
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerBase64())) {
|
||||||
|
log.info("set UndividedLayerBase64为空,便于日志打印");
|
||||||
|
i.setUndividedLayerBase64(null);
|
||||||
|
}
|
||||||
|
// 前端合成的未分割的图
|
||||||
|
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerWithSinglePrintBase64())) {
|
||||||
|
log.info("set UndividedLayerWithSinglePrintBase64为空,便于日志打印");
|
||||||
|
i.setUndividedLayerWithSinglePrintBase64(null);
|
||||||
|
}*/
|
||||||
});
|
});
|
||||||
|
|
||||||
log.info("designSingle request入参 ==> " + JSONObject.toJSONString(clone));
|
log.info("designSingle request入参 ==> " + JSONObject.toJSONString(clone));
|
||||||
@@ -800,7 +840,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
maskBase64ToPath(designSingleIncludeLayersDTO, setNull);
|
maskBase64ToPath(designSingleIncludeLayersDTO, setNull);
|
||||||
// maskBase64ToPath(designSingleIncludeLayersDTO, Boolean.TRUE);
|
// maskBase64ToPath(designSingleIncludeLayersDTO, Boolean.TRUE);
|
||||||
|
|
||||||
partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview());
|
partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview(), designSingleIncludeLayersDTO.getDesignType());
|
||||||
|
|
||||||
// 组装入参
|
// 组装入参
|
||||||
DesignPythonObjects objects = pythonService.covertDesignSingleParam(
|
DesignPythonObjects objects = pythonService.covertDesignSingleParam(
|
||||||
@@ -818,13 +858,13 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
JSONObject outfit = data.getJSONObject("0");
|
JSONObject outfit = data.getJSONObject("0");
|
||||||
|
|
||||||
JSONArray layers = outfit.getJSONArray("layers");
|
JSONArray layers = outfit.getJSONArray("layers");
|
||||||
Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers);
|
// Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers, designSingleIncludeLayersDTO);
|
||||||
if (!designSingleIncludeLayersDTO.getIsPreview()) {
|
if (!designSingleIncludeLayersDTO.getIsPreview()) {
|
||||||
// 更新及保存图层信息
|
// 更新及保存图层信息
|
||||||
tDesignPythonOutfitDetails = saveDesignSingleItemDetailAndLayers(objects, design.getId(), designSingleIncludeLayersDTO.getDesignItemId()
|
tDesignPythonOutfitDetails = saveDesignSingleItemDetailAndLayers(objects, design.getId(), designSingleIncludeLayersDTO.getDesignItemId()
|
||||||
, userId, outfit, designSingleIncludeLayersDTO.getTimeZone()
|
, userId, outfit, designSingleIncludeLayersDTO.getTimeZone()
|
||||||
, designSingleIncludeLayersDTO.getDesignSingleItemDTOList()
|
, designSingleIncludeLayersDTO.getDesignSingleItemDTOList()
|
||||||
, priorityAndUndividedLayer, changeModelFlag, modelId, modelType, isSingleCollectionFlag);
|
/*, priorityAndUndividedLayer*/, changeModelFlag, modelId, modelType, isSingleCollectionFlag, designSingleIncludeLayersDTO.getDesignType());
|
||||||
|
|
||||||
saveCollectionElement(designSingleIncludeLayersDTO);
|
saveCollectionElement(designSingleIncludeLayersDTO);
|
||||||
} else {
|
} else {
|
||||||
@@ -858,8 +898,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
outfit.getString("synthesis_url"),
|
outfit.getString("synthesis_url"),
|
||||||
designSingleIncludeLayersDTO.getDesignSingleItemDTOList(),
|
designSingleIncludeLayersDTO.getDesignSingleItemDTOList(),
|
||||||
detailsVO,
|
detailsVO,
|
||||||
design.getSingleOverall(),
|
design.getSingleOverall()/*,
|
||||||
priorityAndUndividedLayer);
|
priorityAndUndividedLayer*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方法1:仅查询(无事务)
|
// 方法1:仅查询(无事务)
|
||||||
@@ -919,12 +959,12 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
item.setMaskUrl(path);
|
item.setMaskUrl(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("服装{} 的maskUrl为null", item.getType());
|
// log.info("服装{} 的maskUrl为null", item.getType());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void partialDesignBase64ToImage(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Long accountId, boolean preview) {
|
private void partialDesignBase64ToImage(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Long accountId, boolean preview, String designType) {
|
||||||
designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
|
designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
|
||||||
PartialDesignDTO partialDesignDTO = item.getPartialDesign();
|
PartialDesignDTO partialDesignDTO = item.getPartialDesign();
|
||||||
if (!Objects.isNull(item.getPartialDesign())
|
if (!Objects.isNull(item.getPartialDesign())
|
||||||
@@ -946,21 +986,90 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
item.getPartialDesign().setPartialDesignMinioPath(newPath);
|
item.getPartialDesign().setPartialDesignMinioPath(newPath);
|
||||||
} else if (Objects.isNull(item.getPartialDesign())
|
} else if (Objects.isNull(item.getPartialDesign())
|
||||||
|| StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
|
|| StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
|
||||||
item.setPartialDesign(new PartialDesignDTO(null));
|
if (designType.equals("merge")) {
|
||||||
|
// 先去数据库进行查找,如果数据库中也是空,则提示需要提供,否则无法生成
|
||||||
|
DesignItemDetail designItemDetail = designItemDetailService.getById(item.getId());
|
||||||
|
if (Objects.isNull(designItemDetail)){
|
||||||
|
log.error("未知designItemDetailId: {}", item.getId());
|
||||||
|
throw new BusinessException("designItemDetails.not.found");
|
||||||
|
} else if (StringUtil.isNullOrEmpty(designItemDetail.getPartialDesign())) {
|
||||||
|
item.setPartialDesign(new PartialDesignDTO(designItemDetail.getUndividedLayer()));
|
||||||
|
/*log.error("merge模式下,必须提供partialDesign");
|
||||||
|
throw new BusinessException("required.partialDesign");*/
|
||||||
|
} else {
|
||||||
|
item.setPartialDesign(new PartialDesignDTO(designItemDetail.getPartialDesign()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.setPartialDesign(new PartialDesignDTO(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void undividedLayerBase64ToImage(List<DesignSingleItemDTO> designSingleItemDTOS) {
|
||||||
|
designSingleItemDTOS.forEach(item -> {
|
||||||
|
if (!StringUtil.isNullOrEmpty(item.getUndividedLayerBase64())) {
|
||||||
|
if (item.getUndividedLayerBase64().startsWith("data:image") && !item.getUndividedLayerBase64().startsWith("https://")) {
|
||||||
|
// 将原图地址作为修改后的图片地址,放在不同的桶
|
||||||
|
String filename = "image/image_" + UUID.randomUUID();
|
||||||
|
String path = minioUtil.base64UploadToPath(item.getUndividedLayerBase64(), clothingBucket, filename);
|
||||||
|
log.info("undividedLayer, 新的path为{}", path);
|
||||||
|
if (StringUtil.isNullOrEmpty(path)) {
|
||||||
|
log.error("undividedLayer图片base64上传失败");
|
||||||
|
throw new BusinessException("file.upload.fail");
|
||||||
|
}
|
||||||
|
item.setUndividedLayerBase64(path);
|
||||||
|
} else {
|
||||||
|
item.setUndividedLayerBase64(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtil.isNullOrEmpty(item.getUndividedLayerWithSinglePrintBase64())) {
|
||||||
|
if (item.getUndividedLayerWithSinglePrintBase64().startsWith("data:image") && !item.getUndividedLayerWithSinglePrintBase64().startsWith("https://")) {
|
||||||
|
// 将原图地址作为修改后的图片地址,放在不同的桶
|
||||||
|
String filename = "image/image_" + UUID.randomUUID();
|
||||||
|
String path = minioUtil.base64UploadToPath(item.getUndividedLayerWithSinglePrintBase64(), clothingBucket, filename);
|
||||||
|
log.info("getUndividedLayerWithSinglePrint, 新的path为{}", path);
|
||||||
|
if (StringUtil.isNullOrEmpty(path)) {
|
||||||
|
log.error("getUndividedLayerWithSinglePrintBase64图片base64上传失败");
|
||||||
|
throw new BusinessException("file.upload.fail");
|
||||||
|
}
|
||||||
|
item.setUndividedLayerWithSinglePrintBase64(path);
|
||||||
|
} else {
|
||||||
|
item.setUndividedLayerWithSinglePrintBase64(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers) {
|
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||||
HashMap<String, List<String>> priorityAndLayer = new HashMap<>();
|
String designType = "default";
|
||||||
for (int i = 0; i < layers.size(); i++) {
|
if (Objects.nonNull(designSingleIncludeLayersDTO)) {
|
||||||
JSONObject jsonObject = layers.getJSONObject(i);
|
designType = designSingleIncludeLayersDTO.getDesignType();
|
||||||
String priority = jsonObject.getString("priority");
|
|
||||||
String category = jsonObject.getString("image_category").split("_")[0];
|
|
||||||
if (!category.equals("body") && !priorityAndLayer.containsKey(priority))
|
|
||||||
priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url")));
|
|
||||||
}
|
}
|
||||||
|
HashMap<String, List<String>> priorityAndLayer = new HashMap<>();
|
||||||
|
if (designType.equals("default")) {
|
||||||
|
for (int i = 0; i < layers.size(); i++) {
|
||||||
|
JSONObject jsonObject = layers.getJSONObject(i);
|
||||||
|
String priority = jsonObject.getString("priority");
|
||||||
|
String category = jsonObject.getString("image_category").split("_")[0];
|
||||||
|
if (!category.equals("body") && !priorityAndLayer.containsKey(priority)) {
|
||||||
|
// pattern_overall_image_url | pattern_print_image_url 这俩字段来源有俩,merge模式下,来自前端,default模式下,来自python
|
||||||
|
priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (designSingleIncludeLayersDTO.getIsPreview()) {
|
||||||
|
// 如果是预览,则不处理、不存储前端传过来的数据
|
||||||
|
return priorityAndLayer;
|
||||||
|
}
|
||||||
|
undividedLayerBase64ToImage(designSingleIncludeLayersDTO.getDesignSingleItemDTOList());
|
||||||
|
for (DesignSingleItemDTO designSingleItemDTO : designSingleIncludeLayersDTO.getDesignSingleItemDTOList()) {
|
||||||
|
priorityAndLayer.put(designSingleItemDTO.getPriority().toString(), Arrays.asList(designSingleItemDTO.getUndividedLayerBase64(), designSingleItemDTO.getUndividedLayerWithSinglePrintBase64()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return priorityAndLayer;
|
return priorityAndLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,8 +1184,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
String currentFullBodyView,
|
String currentFullBodyView,
|
||||||
List<DesignSingleItemDTO> designSingleItemDTOList,
|
List<DesignSingleItemDTO> designSingleItemDTOList,
|
||||||
List<DesignPythonOutfitVO> layersObject,
|
List<DesignPythonOutfitVO> layersObject,
|
||||||
String singleOrOverall,
|
String singleOrOverall/*,
|
||||||
Map<String, List<String>> priorityAndUndividedLayer) {
|
Map<String, List<String>> priorityAndUndividedLayer*/) {
|
||||||
|
|
||||||
DesignSingleVO designSingleVO = new DesignSingleVO();
|
DesignSingleVO designSingleVO = new DesignSingleVO();
|
||||||
ArrayList<DesignItemClothesDetailVO> clothes = new ArrayList<>();
|
ArrayList<DesignItemClothesDetailVO> clothes = new ArrayList<>();
|
||||||
@@ -1116,10 +1225,15 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
String preSignedUrl = StringUtil.isNullOrEmpty(partialDesignMinioPath) ? null : minioUtil.getPreSignedUrl(partialDesignMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true);
|
String preSignedUrl = StringUtil.isNullOrEmpty(partialDesignMinioPath) ? null : minioUtil.getPreSignedUrl(partialDesignMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true);
|
||||||
designItemClothesDetailVO.setPartialDesign(new PartialDesignDTO(partialDesignMinioPath, preSignedUrl));
|
designItemClothesDetailVO.setPartialDesign(new PartialDesignDTO(partialDesignMinioPath, preSignedUrl));
|
||||||
|
|
||||||
|
/*// 取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段
|
||||||
if (priorityAndUndividedLayer.containsKey(singleItem.getPriority().toString())) {
|
if (priorityAndUndividedLayer.containsKey(singleItem.getPriority().toString())) {
|
||||||
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0))) {
|
||||||
designItemClothesDetailVO.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).getFirst(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||||
}
|
}
|
||||||
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1))) {
|
||||||
|
designItemClothesDetailVO.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
body.setLayersObject(layersObject.stream().filter(layers -> layers.getImageCategory().equals("body")).collect(Collectors.toList()));
|
body.setLayersObject(layersObject.stream().filter(layers -> layers.getImageCategory().equals("body")).collect(Collectors.toList()));
|
||||||
|
|
||||||
clothes.add(designItemClothesDetailVO);
|
clothes.add(designItemClothesDetailVO);
|
||||||
@@ -1201,6 +1315,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
designItemDetailPrint.setPosition(print.getLocation().toString());
|
designItemDetailPrint.setPosition(print.getLocation().toString());
|
||||||
designItemDetailPrint.setAngle(print.getAngle());
|
designItemDetailPrint.setAngle(print.getAngle());
|
||||||
designItemDetailPrint.setPriority(priority);
|
designItemDetailPrint.setPriority(priority);
|
||||||
|
designItemDetailPrint.setObject(print.getObject());
|
||||||
|
|
||||||
designItemDetailPrints.add(designItemDetailPrint);
|
designItemDetailPrints.add(designItemDetailPrint);
|
||||||
});
|
});
|
||||||
@@ -1294,6 +1409,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Objects.isNull(designSingleItem.getPrintObject()) || Objects.isNull(designSingleItem.getPrintObject().getPrints())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 添加print到library
|
// 添加print到library
|
||||||
designSingleItem.getPrintObject().getPrints().forEach(print -> {
|
designSingleItem.getPrintObject().getPrints().forEach(print -> {
|
||||||
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) {
|
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.alibaba.fastjson.JSONArray;
|
|||||||
import com.alibaba.fastjson.JSONException;
|
import com.alibaba.fastjson.JSONException;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
@@ -45,6 +46,7 @@ import java.math.BigDecimal;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
@@ -100,8 +102,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
private final UserLikeGroupService userLikeGroupService;
|
private final UserLikeGroupService userLikeGroupService;
|
||||||
private final UserLikeService userLikeService;
|
private final UserLikeService userLikeService;
|
||||||
private final UserBehaviorMapper userBehaviorMapper;
|
private final UserBehaviorMapper userBehaviorMapper;
|
||||||
private final UserPreferenceLogMapper userPreferenceLogMapper;
|
private final UserPreferenceMapper userPreferenceMapper;
|
||||||
private final WorkspaceService workspaceService;
|
private final WorkspaceService workspaceService;
|
||||||
|
private final WorkspaceRelStyleMapper workspaceRelStyleMapper;
|
||||||
|
|
||||||
@Value("${minio.endpoint}")
|
@Value("${minio.endpoint}")
|
||||||
private String endpoint;
|
private String endpoint;
|
||||||
@@ -713,7 +716,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
(existing, replacement) -> replacement));
|
(existing, replacement) -> replacement));
|
||||||
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||||
log.info("all typeLayers Map:{}", typeAndUndividedLayer);
|
log.info("all typeLayers Map:{}", typeAndUndividedLayer);
|
||||||
Map<String, List<String>> priorityAndUndividedLayer = designItemService.setPriorityAndUndividedLayer(layers);
|
Map<String, List<String>> priorityAndUndividedLayer = designItemService.setPriorityAndUndividedLayer(layers, null);
|
||||||
for (DesignPythonItem detail : item.getItems()) {
|
for (DesignPythonItem detail : item.getItems()) {
|
||||||
if (null == detail) {
|
if (null == detail) {
|
||||||
continue;
|
continue;
|
||||||
@@ -851,7 +854,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
|
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
|
||||||
d -> Math.abs(d.getPriority()),
|
d -> Math.abs(d.getPriority()),
|
||||||
(existing, replacement) -> replacement));
|
(existing, replacement) -> replacement));
|
||||||
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
// Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||||
for (DesignPythonItem detail : item.getItems()) {
|
for (DesignPythonItem detail : item.getItems()) {
|
||||||
if (null == detail) {
|
if (null == detail) {
|
||||||
continue;
|
continue;
|
||||||
@@ -862,9 +865,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
designItemDetail.setDesignItemId(designItemId);
|
designItemDetail.setDesignItemId(designItemId);
|
||||||
designItemDetail.setCollectionElementId(detail.getElementId());
|
designItemDetail.setCollectionElementId(detail.getElementId());
|
||||||
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
|
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
|
||||||
if (!detail.getType().equals("Body")) {
|
/*if (!detail.getType().equals("Body")) {
|
||||||
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
|
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
|
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
|
||||||
designItemDetail.setPath(detail.getBody_path());
|
designItemDetail.setPath(detail.getBody_path());
|
||||||
@@ -1108,18 +1111,20 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
//修改designItem为like状态
|
//修改designItem为like状态
|
||||||
designItemService.updateLikeStatus(designLikeDTO.getDesignItemId(), (byte) 1);
|
designItemService.updateLikeStatus(designLikeDTO.getDesignItemId(), (byte) 1);
|
||||||
// 记录喜欢的系统sketch
|
// 记录喜欢的系统sketch
|
||||||
addSystemLikeSketch(designItem);
|
addSystemLikeSketch(designItem, designLikeDTO.getProjectId());
|
||||||
// 更新项目更新时间
|
// 更新项目更新时间
|
||||||
projectService.modifyProjectUpdateTime(designLikeDTO.getProjectId());
|
projectService.modifyProjectUpdateTime(designLikeDTO.getProjectId());
|
||||||
return new DesignLikeVO(userLikeSortId, userGroupId, groupDetailId, pictureName, userLike.getId(), userLikeSort.getSort());
|
return new DesignLikeVO(userLikeSortId, userGroupId, groupDetailId, pictureName, userLike.getId(), userLikeSort.getSort());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSystemLikeSketch(DesignItem designItem) {
|
public void addSystemLikeSketch(DesignItem designItem, Long projectId) {
|
||||||
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
||||||
QueryWrapper<DesignItemDetail> qw = new QueryWrapper<>();
|
QueryWrapper<DesignItemDetail> qw = new QueryWrapper<>();
|
||||||
qw.lambda().eq(DesignItemDetail::getDesignItemId, designItem.getId());
|
qw.lambda().eq(DesignItemDetail::getDesignItemId, designItem.getId());
|
||||||
qw.lambda().ne(DesignItemDetail::getType, "Body");
|
qw.lambda().ne(DesignItemDetail::getType, "Body");
|
||||||
List<DesignItemDetail> designItemDetails = designItemDetailMapper.selectList(qw);
|
List<DesignItemDetail> designItemDetails = designItemDetailMapper.selectList(qw);
|
||||||
|
List<WorkspaceRelStyle> workspaceRelStyles = workspaceRelStyleMapper.selectByProjectId(projectId);
|
||||||
|
|
||||||
for (DesignItemDetail designItemDetail : designItemDetails) {
|
for (DesignItemDetail designItemDetail : designItemDetails) {
|
||||||
if (designItemDetail.getPath().startsWith("aida-sys-image")) {
|
if (designItemDetail.getPath().startsWith("aida-sys-image")) {
|
||||||
String[] split = designItemDetail.getPath().split("/");
|
String[] split = designItemDetail.getPath().split("/");
|
||||||
@@ -1132,16 +1137,34 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
userBehavior.setCreateTime(LocalDateTime.now());
|
userBehavior.setCreateTime(LocalDateTime.now());
|
||||||
userBehaviorMapper.insert(userBehavior);
|
userBehaviorMapper.insert(userBehavior);
|
||||||
|
|
||||||
UserPreferenceLogTest userPreferenceLogTest = new UserPreferenceLogTest();
|
UserPreference userPreference = new UserPreference();
|
||||||
userPreferenceLogTest.setPath(designItemDetail.getPath());
|
userPreference.setPath(designItemDetail.getPath());
|
||||||
userPreferenceLogTest.setAccountId(userHolder.getId());
|
SysFile sysFile = sysFileService.getOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUrl, designItemDetail.getPath()));
|
||||||
// userPreferenceLogTest.setUserLikeGroupId(userLike.getUserLikeGroupId());
|
userPreference.setAccountId(userHolder.getId());
|
||||||
userPreferenceLogTest.setDataTime(designItemDetail.getCreateDate().toInstant()
|
if (sysFile != null) {
|
||||||
|
userPreference.setCategory(sysFile.getLevel3Type().toLowerCase() + "_" + sysFile.getLevel2Type().toLowerCase());
|
||||||
|
userPreference.setStyle(sysFile.getStyle());
|
||||||
|
} else {
|
||||||
|
log.error("sysFile not found:{}", designItemDetail.getPath());
|
||||||
|
SendEmailUtil.commonExceptionReminder("url在sysFile里找不到" + designItemDetail.getPath(), new String[]{"litianxiangxtt@163.com"});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
userPreference.setDataTime(new Date().toInstant()
|
||||||
.atZone(ZoneId.systemDefault())
|
.atZone(ZoneId.systemDefault())
|
||||||
.toLocalDateTime());
|
.toLocalDateTime());
|
||||||
userPreferenceLogMapper.insert(userPreferenceLogTest);
|
userPreference.setDesignItemId(designItem.getId());
|
||||||
|
userPreference.setProjectId(projectId);
|
||||||
|
if (workspaceRelStyles == null||workspaceRelStyles.isEmpty()) {
|
||||||
|
//查不到记录,style应该是all
|
||||||
|
userPreference.setWorkspaceRelStyleId(0L);
|
||||||
|
} else {
|
||||||
|
userPreference.setWorkspaceRelStyleId(workspaceRelStyles.get(0).getStyleId());
|
||||||
|
}
|
||||||
|
userPreferenceMapper.insert(userPreference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Long> validateMergeElement(List<CollectionElement> oldElements, List<DesignItemDetail> designItemDetails) {
|
private List<Long> validateMergeElement(List<CollectionElement> oldElements, List<DesignItemDetail> designItemDetails) {
|
||||||
@@ -1309,12 +1332,13 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
d.setScope(o.getPath().startsWith("aida-sys-image") ? "sys" : "user");
|
d.setScope(o.getPath().startsWith("aida-sys-image") ? "sys" : "user");
|
||||||
d.setLevel1Type(converTypeToLevel1(o.getType()));
|
d.setLevel1Type(converTypeToLevel1(o.getType()));
|
||||||
d.setGradient(JSONObject.parseObject(o.getGradientString(), Gradient.class));
|
d.setGradient(JSONObject.parseObject(o.getGradientString(), Gradient.class));
|
||||||
|
/*// 取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段
|
||||||
if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())) {
|
if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())) {
|
||||||
d.setUndividedLayer(minioUtil.getPreSignedUrl(o.getUndividedLayer(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
|
d.setUndividedLayer(minioUtil.getPreSignedUrl(o.getUndividedLayer(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
|
||||||
}
|
}
|
||||||
if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())) {
|
if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())) {
|
||||||
d.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(o.getUndividedLayerWithSinglePrint(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
|
d.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(o.getUndividedLayerWithSinglePrint(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
|
||||||
}
|
}*/
|
||||||
// 根据designItemDetailId获取印花
|
// 根据designItemDetailId获取印花
|
||||||
List<DesignItemDetailPrint> prints = designItemDetailPrintService.getByDesignItemDetailId(o.getId(), "print");
|
List<DesignItemDetailPrint> prints = designItemDetailPrintService.getByDesignItemDetailId(o.getId(), "print");
|
||||||
prints.removeIf(print -> !minioUtil.doesObjectExist(print.getPath()));
|
prints.removeIf(print -> !minioUtil.doesObjectExist(print.getPath()));
|
||||||
@@ -1563,6 +1587,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
designSinglePrint.setScale(scales);
|
designSinglePrint.setScale(scales);
|
||||||
|
designSinglePrint.setObject(detailPrint.getObject());
|
||||||
prints.add(designSinglePrint);
|
prints.add(designSinglePrint);
|
||||||
} else {
|
} else {
|
||||||
// 多个印花
|
// 多个印花
|
||||||
@@ -1583,7 +1608,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
scales = Arrays.asList(scale, scale);
|
scales = Arrays.asList(scale, scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prints.add(new DesignSinglePrint(
|
DesignSinglePrint designSinglePrint = new DesignSinglePrint(
|
||||||
print.getLevel2Type(),
|
print.getLevel2Type(),
|
||||||
minioUtil.getPreSignedUrl(print.getPath(), 24 * 60),
|
minioUtil.getPreSignedUrl(print.getPath(), 24 * 60),
|
||||||
print.getPath(),
|
print.getPath(),
|
||||||
@@ -1591,7 +1616,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
scales,
|
scales,
|
||||||
print.getAngle(),
|
print.getAngle(),
|
||||||
print.getPriority(),
|
print.getPriority(),
|
||||||
ifSingle));
|
ifSingle);
|
||||||
|
designSinglePrint.setObject(print.getObject());
|
||||||
|
prints.add(designSinglePrint);
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1643,6 +1670,24 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
.lt("create_date", endTime)
|
.lt("create_date", endTime)
|
||||||
.select("count(id) as count");
|
.select("count(id) as count");
|
||||||
|
|
||||||
|
// 如果startTime早于2024-05-01 00:00:00,添加额外的筛选条件
|
||||||
|
LocalDateTime thresholdTime = LocalDateTime.of(2024, 5, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
LocalDateTime startDateTime = LocalDateTime.parse(startTime, formatter);
|
||||||
|
|
||||||
|
if (startDateTime.isBefore(thresholdTime)) {
|
||||||
|
// 使用 notLike 来排除以 ":01" 或 ":02" 结尾的时间
|
||||||
|
// 方案2.1:使用 apply 方法执行数据库函数 (create_date 字段的实际存储格式(可能包含毫秒),所以取最后三个字符进行匹配)
|
||||||
|
queryWrapper.apply("DATE_FORMAT(create_date, '%s') NOT IN ('01', '02')");
|
||||||
|
/*queryWrapper.notLike("create_date", "%:01")
|
||||||
|
.notLike("create_date", "%:02");*/
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to parse startTime: {}, skip time-based filtering", startTime, e);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Objects.isNull(accountIds) && !accountIds.isEmpty()) {
|
if (!Objects.isNull(accountIds) && !accountIds.isEmpty()) {
|
||||||
queryWrapper.in("account_id", accountIds);
|
queryWrapper.in("account_id", accountIds);
|
||||||
}
|
}
|
||||||
@@ -1650,7 +1695,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
List<Map<String, Object>> result = baseMapper.selectMaps(queryWrapper);
|
List<Map<String, Object>> result = baseMapper.selectMaps(queryWrapper);
|
||||||
if (result != null && !result.isEmpty()) {
|
if (result != null && !result.isEmpty()) {
|
||||||
Object countObj = result.get(0).get("count");
|
Object countObj = result.get(0).get("count");
|
||||||
return (Long) countObj;
|
return countObj != null ? ((Number) countObj).longValue() : 0L;
|
||||||
} else {
|
} else {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
@@ -2519,7 +2564,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
|
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
|
||||||
d -> Math.abs(d.getPriority()),
|
d -> Math.abs(d.getPriority()),
|
||||||
(existing, replacement) -> replacement));
|
(existing, replacement) -> replacement));
|
||||||
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
// Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||||
for (DesignPythonItem detail : item.getItems()) {
|
for (DesignPythonItem detail : item.getItems()) {
|
||||||
if (null == detail) {
|
if (null == detail) {
|
||||||
continue;
|
continue;
|
||||||
@@ -2530,9 +2575,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
designItemDetail.setDesignItemId(designItemId);
|
designItemDetail.setDesignItemId(designItemId);
|
||||||
designItemDetail.setCollectionElementId(detail.getElementId());
|
designItemDetail.setCollectionElementId(detail.getElementId());
|
||||||
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
|
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
|
||||||
if (!detail.getType().equals("Body")) {
|
/*if (!detail.getType().equals("Body")) {
|
||||||
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
|
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
|
||||||
}
|
}*/
|
||||||
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
|
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
|
||||||
designItemDetail.setPath(detail.getBody_path());
|
designItemDetail.setPath(detail.getBody_path());
|
||||||
//BODY不关联businessId
|
//BODY不关联businessId
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
import org.apache.commons.io.IOUtils;
|
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.HttpHost;
|
||||||
import org.apache.http.client.config.RequestConfig;
|
import org.apache.http.client.config.RequestConfig;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
@@ -59,10 +61,12 @@ import org.bytedeco.javacv.Java2DFrameConverter;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -218,6 +222,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
version = "fast";
|
version = "fast";
|
||||||
params.put("version", "fast");
|
params.put("version", "fast");
|
||||||
}
|
}
|
||||||
|
// 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中
|
||||||
|
saveGenerateImmediately(generate);
|
||||||
// 3.1 确定不同类型的印花分别调哪个接口
|
// 3.1 确定不同类型的印花分别调哪个接口
|
||||||
if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) {
|
if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) {
|
||||||
switch (generateThroughImageTextDTO.getLevel2Type()) {
|
switch (generateThroughImageTextDTO.getLevel2Type()) {
|
||||||
@@ -243,15 +249,28 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
|
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(),
|
if (Objects.equals(version, "fast")) {
|
||||||
mode, category, generateThroughImageTextDTO.getGender(), version);
|
GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(),
|
||||||
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path);
|
Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path, generateThroughImageTextDTO.getUniqueId());
|
||||||
|
|
||||||
// 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中
|
|
||||||
save(generate);
|
|
||||||
|
|
||||||
// 5、将本次请求存入redis
|
// 5、将本次请求存入redis
|
||||||
String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId();
|
String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId();
|
||||||
@@ -266,6 +285,40 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveGenerateImmediately(Generate generate) {
|
||||||
|
save(generate);
|
||||||
|
// 使用 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) {
|
||||||
|
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) {
|
public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) {
|
||||||
if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) {
|
if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) {
|
||||||
if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) {
|
if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) {
|
||||||
@@ -284,11 +337,16 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void processGenerateResult(String taskId, String url, String category) {
|
public void processGenerateResult(String taskId, String url, String category) {
|
||||||
|
log.info("============ProcessGenerateResult listening==========");
|
||||||
|
log.debug("taskId: " + taskId);
|
||||||
|
String status = null;
|
||||||
// 1、处理模型返回的数据
|
// 1、处理模型返回的数据
|
||||||
GenerateDetail generateDetail = new GenerateDetail();
|
GenerateDetail generateDetail = new GenerateDetail();
|
||||||
GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO();
|
GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO();
|
||||||
Generate generate;
|
Generate generate;
|
||||||
try {
|
try {
|
||||||
|
// 等待 HTTP 线程写入完成后再查库
|
||||||
|
waitForSaveLock(taskId);
|
||||||
generate = selectByUniqueId(taskId);
|
generate = selectByUniqueId(taskId);
|
||||||
} catch (MybatisPlusException e) {
|
} catch (MybatisPlusException e) {
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
@@ -311,14 +369,15 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
generateDetail.setUrl(url);
|
generateDetail.setUrl(url);
|
||||||
generateDetail.setGenerateId(generate.getId());
|
generateDetail.setGenerateId(generate.getId());
|
||||||
generateDetail.setCreateDate(LocalDateTime.now());
|
generateDetail.setCreateDate(LocalDateTime.now());
|
||||||
generateDetail.setMd5(md5);
|
generateDetail.setMd5("");
|
||||||
// 将相应的url保存到数据库
|
// 将相应的url保存到数据库
|
||||||
generateDetailMapper.insert(generateDetail);
|
generateDetailMapper.insert(generateDetail);
|
||||||
|
log.debug("generateDetail: " + generateDetail.toString());
|
||||||
|
|
||||||
// String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-"));
|
// String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-"));
|
||||||
String key = generateResultKey + ":" + taskId;
|
String key = generateResultKey + ":" + taskId;
|
||||||
String imageName = url.substring(url.lastIndexOf("/") + 1);
|
String imageName = url.substring(url.lastIndexOf("/") + 1);
|
||||||
String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success";
|
status = imageName.equals("white_image.jpg") ? "Invalid" : "Success";
|
||||||
if (StringUtil.isNullOrEmpty(category)) {
|
if (StringUtil.isNullOrEmpty(category)) {
|
||||||
Generate generateRecord = selectByUniqueId(taskId);
|
Generate generateRecord = selectByUniqueId(taskId);
|
||||||
category = generateRecord.getLevel2Type();
|
category = generateRecord.getLevel2Type();
|
||||||
@@ -326,6 +385,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category);
|
GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category);
|
||||||
// 更新redis
|
// 更新redis
|
||||||
redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||||
|
log.debug("generateResultVO: " + generateResultVO.toString());
|
||||||
|
|
||||||
|
|
||||||
// 执行积分扣除
|
// 执行积分扣除
|
||||||
// ** 注:如果生成的图片都是空白 则不扣积分
|
// ** 注:如果生成的图片都是空白 则不扣积分
|
||||||
@@ -785,8 +846,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
long requestEndTime = System.currentTimeMillis();
|
long requestEndTime = System.currentTimeMillis();
|
||||||
log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}",
|
log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}",
|
||||||
response.code(), (requestEndTime - requestStartTime), taskId);
|
response.code(), (requestEndTime - requestStartTime), taskId);
|
||||||
|
String result = response.body().string();
|
||||||
if (!response.isSuccessful()) {
|
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) {
|
if (attempt < maxRetries) {
|
||||||
Thread.sleep(retryDelay * attempt); // 递增延迟
|
Thread.sleep(retryDelay * attempt); // 递增延迟
|
||||||
continue;
|
continue;
|
||||||
@@ -795,7 +857,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = response.body().string();
|
|
||||||
// log.info("Google 响应结果:{}", result);
|
// log.info("Google 响应结果:{}", result);
|
||||||
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
|
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
|
||||||
|
|
||||||
@@ -1065,6 +1127,12 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
|
|
||||||
String result = response.body().string();
|
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);
|
// log.info("Google 响应结果:{}", result);
|
||||||
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
|
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
|
||||||
|
|
||||||
@@ -1203,6 +1271,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
* @param modelName advanced high normal
|
* @param modelName advanced high normal
|
||||||
*/
|
*/
|
||||||
private HashMap<String, String> chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) {
|
private HashMap<String, String> chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) {
|
||||||
|
if (StringUtil.isNullOrEmpty(modelName)) {
|
||||||
|
throw new BusinessException("system error");
|
||||||
|
}
|
||||||
HashMap<String, String> modelAndPromptMap = new HashMap<>();
|
HashMap<String, String> modelAndPromptMap = new HashMap<>();
|
||||||
boolean isUseImage;
|
boolean isUseImage;
|
||||||
if (Objects.isNull(generateDTO.getCollectionElementId())
|
if (Objects.isNull(generateDTO.getCollectionElementId())
|
||||||
@@ -1218,7 +1289,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
String style = generateDTO.getText().substring(0, firstCommaIndex).trim();
|
String style = generateDTO.getText().substring(0, firstCommaIndex).trim();
|
||||||
|
|
||||||
String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim();
|
String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim();
|
||||||
prompt = getPrintboardPrompt(style, prompt);
|
prompt = getPrintboardPrompt(style, prompt, modelName, isUseImage);
|
||||||
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
||||||
|
|
||||||
|
|
||||||
@@ -1239,7 +1310,14 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (ModelConstants.MOODBOARD.equals(generateDTO.getLevel1Type())) {
|
} else if (ModelConstants.MOODBOARD.equals(generateDTO.getLevel1Type())) {
|
||||||
String prompt = generateDTO.getText() + "high-resolution, ultra-detailed, realistic textures, perfect anatomy, cinematic lighting, 8k render, editorial photography style";
|
String userInput = generateDTO.getText();
|
||||||
|
String systemPrompt = "high-resolution, ultra-detailed, realistic textures, cinematic lighting, 8k render, editorial photography style";
|
||||||
|
String prompt;
|
||||||
|
if (userInput == null || userInput.trim().isEmpty()) {
|
||||||
|
throw new BusinessException("prompt null");
|
||||||
|
} else {
|
||||||
|
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
|
||||||
|
}
|
||||||
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
||||||
|
|
||||||
if (ModelConstants.ADVANCED.equals(modelName)) {
|
if (ModelConstants.ADVANCED.equals(modelName)) {
|
||||||
@@ -1250,8 +1328,31 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL);
|
modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL);
|
||||||
}
|
}
|
||||||
} else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) {
|
} else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) {
|
||||||
String prompt = generateDTO.getText() + "rules:front view sketch only,plain white background, single garment only, orthographic, centered on white background, borderless canvas, thin monochrome black line art.\n" +
|
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 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.";
|
" 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);
|
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
||||||
if (isUseImage) {
|
if (isUseImage) {
|
||||||
if (ModelConstants.ADVANCED.equals(modelName)) {
|
if (ModelConstants.ADVANCED.equals(modelName)) {
|
||||||
@@ -1449,6 +1550,11 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
if (imagePath != null) {
|
if (imagePath != null) {
|
||||||
requestBuilder.image(finalImagePath1);
|
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(
|
Generate generate = new Generate(
|
||||||
@@ -1560,19 +1666,44 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getPrintboardPrompt(String style, String prompt) {
|
private String getPrintboardPrompt(String style, String userInput, String modelName, boolean isUseImage) {
|
||||||
|
String systemPrompt = null;
|
||||||
|
String prompt;
|
||||||
|
|
||||||
if ("Painting Style".equals(style)) {
|
if ("Painting Style".equals(style)) {
|
||||||
prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" +
|
if (ModelConstants.ADVANCED.equals(modelName)) {
|
||||||
"2.Core Theme: " + prompt + "\n" +
|
systemPrompt = "Tileable seamless pattern, elegant composition, visually balanced, Light watercolor, Giplie Studio (style) pattern with even color field background, high-quality digital print, zero perspective depth, harmonious visual balance, consistent color tone.";
|
||||||
"3.Style: painting_style-The painting style refers to the overall approach, techniques, and artistic philosophy used in the artwork. For fashion designs that will be applied to printboards, it is important to define the unique stylistic elements that would translate well into wearable patterns.";
|
} else if (ModelConstants.HIGH.equals(modelName)) {
|
||||||
|
systemPrompt = "Design pattern, seamless, highly detailed, elegant composition, visually balanced. \n" +
|
||||||
|
"Painting style: traditional painting, hand-painted, brush strokes.";
|
||||||
|
}
|
||||||
} else if ("Illustration Style".equals(style)) {
|
} else if ("Illustration Style".equals(style)) {
|
||||||
prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" +
|
if (ModelConstants.ADVANCED.equals(modelName)) {
|
||||||
"2.Core Theme: " + prompt + "\n" +
|
systemPrompt = "Tileable seamless pattern, elegant composition, visually balanced, flat graphic, clean lines pattern with even color field background, high-quality digital print, zero perspective depth, harmonious visual balance, consistent color tone. ";
|
||||||
"3.Style: illustration_style-Illustration style focuses on the visual storytellingaspect, often used to depict narratives, characters, or thematic concepts. Forfashion, this style can introduce vivid and artistic interpretations, often aligned with specific themes.";
|
} else if (ModelConstants.HIGH.equals(modelName)) {
|
||||||
|
systemPrompt = "Design pattern, seamless, highly detailed, elegant composition, visually balanced. \n" +
|
||||||
|
"Illustration Style: flat graphic, clean lines.";
|
||||||
|
}
|
||||||
} else if ("Real Style".equals(style)) {
|
} else if ("Real Style".equals(style)) {
|
||||||
prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" +
|
if (ModelConstants.ADVANCED.equals(modelName)) {
|
||||||
"2.Core Theme: " + prompt + "\n" +
|
systemPrompt = "Tileable seamless pattern, even color field background, photorealistic style pattern, high-quality digital print, zero perspective depth, harmonious visual balance, consistent color tone. ";
|
||||||
"3.Style: real_style-Real style in fashion is all about authenticity. It featuresnatural fabrics, simple cuts that mirror real life silhouettes, and colors inspired bythe everyday world, exuding a down-to-earth and genuine charm.";
|
} else if (ModelConstants.HIGH.equals(modelName)) {
|
||||||
|
systemPrompt = "Design pattern, seamless, highly detailed, elegant composition, visually balanced. \n" +
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInput == null || userInput.trim().isEmpty()) {
|
||||||
|
if (isUseImage) {
|
||||||
|
prompt = "Theme: Image content" + "\nRequirement: " + systemPrompt;
|
||||||
|
} else {
|
||||||
|
throw new BusinessException("prompt null");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
|
||||||
}
|
}
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
@@ -1970,7 +2101,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
public Generate selectByUniqueId(String uniqueId) {
|
public Generate selectByUniqueId(String uniqueId) {
|
||||||
QueryWrapper<Generate> qw = new QueryWrapper<>();
|
QueryWrapper<Generate> qw = new QueryWrapper<>();
|
||||||
qw.eq("unique_id", uniqueId);
|
qw.eq("unique_id", uniqueId);
|
||||||
|
log.debug("selectByUniqueId: " + uniqueId);
|
||||||
|
Generate one = getOne(qw);
|
||||||
|
log.debug("Generate: " + one);
|
||||||
return getOne(qw);
|
return getOne(qw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4141,11 +4274,11 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
// 处理不同状态
|
// 处理不同状态
|
||||||
switch (statusEnum) {
|
switch (statusEnum) {
|
||||||
case TASK_NOT_FOUND:
|
case TASK_NOT_FOUND:
|
||||||
// 审核没过
|
// 审核没过
|
||||||
case REQUEST_MODERATED:
|
case REQUEST_MODERATED:
|
||||||
// 审核没过
|
// 审核没过
|
||||||
case CONTENT_MODERATED:
|
case CONTENT_MODERATED:
|
||||||
// 出错
|
// 出错
|
||||||
case ERROR:
|
case ERROR:
|
||||||
return "Fail";
|
return "Fail";
|
||||||
case PENDING_F:
|
case PENDING_F:
|
||||||
@@ -4264,7 +4397,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode());
|
MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode());
|
||||||
switch (motionModeEnum) {
|
switch (motionModeEnum) {
|
||||||
case POSE_TO_VIDEO:
|
case POSE_TO_VIDEO:
|
||||||
params.put("pose_id", poseTransformDTO.getPoseId());
|
params.put("pose_id", poseTransformDTO.getPoseId().toString());
|
||||||
params.put("image_url", poseTransformDTO.getProductImage());
|
params.put("image_url", poseTransformDTO.getProductImage());
|
||||||
break;
|
break;
|
||||||
case PROMPT_TO_VIDEO:
|
case PROMPT_TO_VIDEO:
|
||||||
|
|||||||
588
src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java
Normal file
588
src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
package com.ai.da.service.impl;
|
||||||
|
|
||||||
|
import com.ai.da.common.config.exception.BusinessException;
|
||||||
|
import com.ai.da.common.enums.AuthenticationOperationTypeEnum;
|
||||||
|
import com.ai.da.common.utils.*;
|
||||||
|
import com.ai.da.mapper.primary.AccountMapper;
|
||||||
|
import com.ai.da.mapper.primary.ContestantMapper;
|
||||||
|
import com.ai.da.mapper.primary.NotificationMapper;
|
||||||
|
import com.ai.da.mapper.primary.entity.Account;
|
||||||
|
import com.ai.da.mapper.primary.entity.Contestant;
|
||||||
|
import com.ai.da.mapper.primary.entity.Notification;
|
||||||
|
import com.ai.da.model.dto.ContestantDTO;
|
||||||
|
import com.ai.da.model.dto.PublishSysNotificationDTO;
|
||||||
|
import com.ai.da.model.vo.CheckOTPVO;
|
||||||
|
import com.ai.da.service.GlobalAwardService;
|
||||||
|
import com.ai.da.service.MessageCenterService;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
|
import org.apache.poi.ss.usermodel.Row;
|
||||||
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ContestantMapper contestantMapper;
|
||||||
|
|
||||||
|
private final AccountMapper accountMapper;
|
||||||
|
|
||||||
|
private final MessageCenterService messageCenterService;
|
||||||
|
|
||||||
|
private final NotificationMapper notificationMapper;
|
||||||
|
|
||||||
|
private final RedisUtil redisUtil;
|
||||||
|
|
||||||
|
@Value("${file.upload.temp.dir}")
|
||||||
|
private String uploadDir;
|
||||||
|
|
||||||
|
private static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy/MM");
|
||||||
|
|
||||||
|
private static final String tokenCacheKey = AuthenticationOperationTypeEnum.GLOBAL_AWARD.name() + ":";
|
||||||
|
|
||||||
|
@Value("${minio.bucket:contestants}")
|
||||||
|
private String minioBucket;
|
||||||
|
|
||||||
|
@Value("${global.award.link}")
|
||||||
|
private String link;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MinioUtil minioUtil;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String uploadPdf(MultipartFile file, String email) throws Exception {
|
||||||
|
validatePdf(file);
|
||||||
|
String path = storeFile(file, email, "pdf");
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String uploadVideo(MultipartFile file, String email) throws Exception {
|
||||||
|
validateVideo(file);
|
||||||
|
String path = storeFile(file, email, "video");
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validatePdf(MultipartFile file) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new BusinessException("File is empty.");
|
||||||
|
}
|
||||||
|
String ct = file.getContentType();
|
||||||
|
if (ct == null || !ct.toLowerCase().contains("pdf")) {
|
||||||
|
throw new BusinessException("Only PDF files are allowed.");
|
||||||
|
}
|
||||||
|
// size limit example 20MB
|
||||||
|
if (file.getSize() > 20L * 1024 * 1024) {
|
||||||
|
throw new BusinessException("PDF file size exceeds the limit.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateVideo(MultipartFile file) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new BusinessException("File is empty.");
|
||||||
|
}
|
||||||
|
String ct = file.getContentType();
|
||||||
|
if (ct == null || !(ct.toLowerCase().contains("mp4") || ct.toLowerCase().contains("video") )) {
|
||||||
|
throw new BusinessException("Invalid video file type.");
|
||||||
|
}
|
||||||
|
// size limit example 100MB
|
||||||
|
if (file.getSize() > 100L * 1024 * 1024) {
|
||||||
|
throw new BusinessException("Video file size exceeds the limit.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeEmail(String email) {
|
||||||
|
if (email == null) {
|
||||||
|
return "anonymous";
|
||||||
|
}
|
||||||
|
return email.replaceAll("[^a-zA-Z0-9]", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String storeFile(MultipartFile file, String email, String kind) throws IOException {
|
||||||
|
String normalized = normalizeEmail(email);
|
||||||
|
String datePart = LocalDateTime.now().format(YYYY_MM_DD);
|
||||||
|
String ext = "";
|
||||||
|
String original = file.getOriginalFilename();
|
||||||
|
if (original != null && original.contains(".")) {
|
||||||
|
ext = original.substring(original.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
String filename = System.currentTimeMillis() + "_" + UUID.randomUUID().toString() + ext;
|
||||||
|
String relativePath = "contestants/" + normalized + "/" + datePart + "/" + filename;
|
||||||
|
|
||||||
|
String uploadedPath = minioUtil.upload(minioBucket, relativePath, file, null);
|
||||||
|
log.info("uploaded via MinioUtil: {}", uploadedPath);
|
||||||
|
return uploadedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Map<String, Object> saveContestant(ContestantDTO request) {
|
||||||
|
Map<String,Object> resp = new HashMap<>();
|
||||||
|
if (request.getEmail() == null) {
|
||||||
|
throw new BusinessException("Email is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSecurityToken(request.getEmail(), request.getSecureToken());
|
||||||
|
|
||||||
|
QueryWrapper<Contestant> qw = new QueryWrapper<>();
|
||||||
|
qw.eq("email", request.getEmail());
|
||||||
|
Contestant existing = contestantMapper.selectOne(qw);
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
if (existing == null) {
|
||||||
|
// 通过行锁 + 重试机制保证 contestant_number 在并发下自增分配
|
||||||
|
final int maxAttempts = 5;
|
||||||
|
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
|
try {
|
||||||
|
// 获取当前最大 contestant_number 并加行锁(LIMIT 1 FOR UPDATE)
|
||||||
|
QueryWrapper<Contestant> qMax = new QueryWrapper<>();
|
||||||
|
qMax.isNotNull("contestant_number");
|
||||||
|
qMax.orderByDesc("contestant_number");
|
||||||
|
qMax.last("LIMIT 1 FOR UPDATE");
|
||||||
|
Contestant last = contestantMapper.selectOne(qMax);
|
||||||
|
Integer nextNumber = (last == null || last.getContestantNumber() == null) ? 10000 : last.getContestantNumber() + 1;
|
||||||
|
|
||||||
|
Contestant toInsert = Contestant.builder()
|
||||||
|
.email(request.getEmail())
|
||||||
|
.firstName(request.getFirstName())
|
||||||
|
.lastName(request.getLastName())
|
||||||
|
.gender(request.getGender())
|
||||||
|
.occupation(request.getOccupation())
|
||||||
|
.age(request.getAge())
|
||||||
|
.countryRegionCity(request.getCountryRegionCity())
|
||||||
|
.phoneNumber(request.getPhoneNumber())
|
||||||
|
.designTitle(request.getDesignTitle())
|
||||||
|
.designDescription(request.getDesignDescription())
|
||||||
|
.pdfPath(request.getPdfPath())
|
||||||
|
.videoPath(request.getVideoPath())
|
||||||
|
.videoDuration(request.getVideoDuration())
|
||||||
|
.videoSize(request.getVideoSize())
|
||||||
|
.pdfSize(request.getPdfSize())
|
||||||
|
.contestantNumber(nextNumber)
|
||||||
|
.createdAt(now)
|
||||||
|
.updatedAt(now)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
contestantMapper.insert(toInsert);
|
||||||
|
|
||||||
|
resp.put("success", true);
|
||||||
|
sendSiteMsg(toInsert.getId(), toInsert.getEmail());
|
||||||
|
return resp;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Attempt {} to assign contestant_number failed", attempt, e);
|
||||||
|
String msg = e.getMessage() == null ? "" : e.getMessage().toLowerCase();
|
||||||
|
if ((msg.contains("duplicate") || msg.contains("uniq_contestant_number") || msg.contains("contestant_number")) && attempt < maxAttempts) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100L);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new BusinessException("Failed to assign contestant number after retries.");
|
||||||
|
} else {
|
||||||
|
// update existing contestant
|
||||||
|
existing.setFirstName(request.getFirstName());
|
||||||
|
existing.setLastName(request.getLastName());
|
||||||
|
existing.setGender(request.getGender());
|
||||||
|
existing.setOccupation(request.getOccupation());
|
||||||
|
existing.setAge(request.getAge());
|
||||||
|
existing.setCountryRegionCity(request.getCountryRegionCity());
|
||||||
|
existing.setPhoneNumber(request.getPhoneNumber());
|
||||||
|
existing.setDesignTitle(request.getDesignTitle());
|
||||||
|
existing.setDesignDescription(request.getDesignDescription());
|
||||||
|
existing.setPdfPath(request.getPdfPath());
|
||||||
|
existing.setVideoPath(request.getVideoPath());
|
||||||
|
existing.setVideoDuration(request.getVideoDuration());
|
||||||
|
existing.setVideoSize(request.getVideoSize());
|
||||||
|
existing.setPdfSize(request.getPdfSize());
|
||||||
|
existing.setUpdatedAt(now);
|
||||||
|
contestantMapper.updateById(existing);
|
||||||
|
resp.put("success", true);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] exportContestants() throws Exception {
|
||||||
|
List<Contestant> list = contestantMapper.selectList(new QueryWrapper<>());
|
||||||
|
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
|
Sheet sheet = workbook.createSheet("contestants");
|
||||||
|
int rowIdx = 0;
|
||||||
|
Row header = sheet.createRow(rowIdx++);
|
||||||
|
String[] headers = new String[] {
|
||||||
|
"contestantNumber", "email", "firstName", "lastName", "gender", "occupation",
|
||||||
|
"age", "countryRegionCity", "phoneNumber", "designTitle", "designDescription",
|
||||||
|
"pdfPath", "videoPath", "videoDuration", "videoSizeMB", "pdfSizeMB", "createdAt", "updatedAt"
|
||||||
|
};
|
||||||
|
for (int i = 0; i < headers.length; i++) {
|
||||||
|
Cell c = header.createCell(i);
|
||||||
|
c.setCellValue(headers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Contestant cst : list) {
|
||||||
|
Row r = sheet.createRow(rowIdx++);
|
||||||
|
int ci = 0;
|
||||||
|
r.createCell(ci++).setCellValue(cst.getContestantNumber() == null ? "" : cst.getContestantNumber().toString());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getEmail() == null ? "" : cst.getEmail());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getFirstName() == null ? "" : cst.getFirstName());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getLastName() == null ? "" : cst.getLastName());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getGender() == null ? "" : cst.getGender());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getOccupation() == null ? "" : cst.getOccupation());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getAge() == null ? "" : cst.getAge().toString());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getCountryRegionCity() == null ? "" : cst.getCountryRegionCity());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
|
||||||
|
// r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
|
||||||
|
// r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
|
||||||
|
// 视频时长(秒)
|
||||||
|
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
|
||||||
|
// 视频大小、PDF 大小:以 MB 导出,保留两位小数
|
||||||
|
if (cst.getVideoSize() == null) {
|
||||||
|
r.createCell(ci++).setCellValue("");
|
||||||
|
} else {
|
||||||
|
double vMb = cst.getVideoSize() / 1024.0 / 1024.0;
|
||||||
|
r.createCell(ci++).setCellValue(String.format("%.2f", vMb));
|
||||||
|
}
|
||||||
|
if (cst.getPdfSize() == null) {
|
||||||
|
r.createCell(ci++).setCellValue("");
|
||||||
|
} else {
|
||||||
|
double pMb = cst.getPdfSize() / 1024.0 / 1024.0;
|
||||||
|
r.createCell(ci++).setCellValue(String.format("%.2f", pMb));
|
||||||
|
}
|
||||||
|
r.createCell(ci++).setCellValue(cst.getCreatedAt() == null ? "" : cst.getCreatedAt().toString());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getUpdatedAt() == null ? "" : cst.getUpdatedAt().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
workbook.write(out);
|
||||||
|
out.flush();
|
||||||
|
return out.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("export contestants failed", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveContestantsToLocal() throws Exception {
|
||||||
|
List<Contestant> list = contestantMapper.selectList(new QueryWrapper<>());
|
||||||
|
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
||||||
|
String ts = LocalDateTime.now().format(fmt);
|
||||||
|
Path exportDir = Paths.get(uploadDir == null ? "uploads" : uploadDir, "exports");
|
||||||
|
Files.createDirectories(exportDir);
|
||||||
|
Path outPath = exportDir.resolve("contestants_" + ts + ".xlsx");
|
||||||
|
|
||||||
|
try (XSSFWorkbook workbook = new XSSFWorkbook(); FileOutputStream fos = new FileOutputStream(outPath.toFile())) {
|
||||||
|
Sheet sheet = workbook.createSheet("contestants");
|
||||||
|
int rowIdx = 0;
|
||||||
|
Row header = sheet.createRow(rowIdx++);
|
||||||
|
String[] headers = new String[] {
|
||||||
|
"contestantNumber", "email", "firstName", "lastName", "gender", "occupation",
|
||||||
|
"age", "countryRegionCity", "phoneNumber", "designTitle", "designDescription",
|
||||||
|
"pdfPath", "videoPath", "videoDuration", "videoSizeMB", "pdfSizeMB", "createdAt", "updatedAt"
|
||||||
|
};
|
||||||
|
for (int i = 0; i < headers.length; i++) {
|
||||||
|
Cell c = header.createCell(i);
|
||||||
|
c.setCellValue(headers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Contestant cst : list) {
|
||||||
|
Row r = sheet.createRow(rowIdx++);
|
||||||
|
int ci = 0;
|
||||||
|
r.createCell(ci++).setCellValue(cst.getContestantNumber() == null ? "" : cst.getContestantNumber().toString());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getEmail() == null ? "" : cst.getEmail());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getFirstName() == null ? "" : cst.getFirstName());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getLastName() == null ? "" : cst.getLastName());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getGender() == null ? "" : cst.getGender());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getOccupation() == null ? "" : cst.getOccupation());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getAge() == null ? "" : cst.getAge().toString());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getCountryRegionCity() == null ? "" : cst.getCountryRegionCity());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
|
||||||
|
// r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
|
||||||
|
// r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
|
||||||
|
// 视频时长(秒)
|
||||||
|
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
|
||||||
|
// 视频大小、PDF 大小:以 MB 导出,保留两位小数
|
||||||
|
if (cst.getVideoSize() == null) {
|
||||||
|
r.createCell(ci++).setCellValue("");
|
||||||
|
} else {
|
||||||
|
double vMb = cst.getVideoSize() / 1024.0 / 1024.0;
|
||||||
|
r.createCell(ci++).setCellValue(String.format("%.2f", vMb));
|
||||||
|
}
|
||||||
|
if (cst.getPdfSize() == null) {
|
||||||
|
r.createCell(ci++).setCellValue("");
|
||||||
|
} else {
|
||||||
|
double pMb = cst.getPdfSize() / 1024.0 / 1024.0;
|
||||||
|
r.createCell(ci++).setCellValue(String.format("%.2f", pMb));
|
||||||
|
}
|
||||||
|
r.createCell(ci++).setCellValue(cst.getCreatedAt() == null ? "" : cst.getCreatedAt().toString());
|
||||||
|
r.createCell(ci++).setCellValue(cst.getUpdatedAt() == null ? "" : cst.getUpdatedAt().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
workbook.write(fos);
|
||||||
|
fos.flush();
|
||||||
|
log.info("Exported contestants to local file: {}", outPath.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("save contestants to local failed", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContestantDTO getContestantByID(String id) {
|
||||||
|
if (id == null) {
|
||||||
|
throw new BusinessException("id is required.");
|
||||||
|
}
|
||||||
|
Contestant existing = contestantMapper.selectById(id);
|
||||||
|
if (existing == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ContestantDTO dto = new ContestantDTO();
|
||||||
|
// dto.setEmail(existing.getEmail());
|
||||||
|
dto.setFirstName(existing.getFirstName());
|
||||||
|
dto.setLastName(existing.getLastName());
|
||||||
|
dto.setGender(existing.getGender());
|
||||||
|
dto.setOccupation(existing.getOccupation());
|
||||||
|
dto.setAge(existing.getAge());
|
||||||
|
dto.setCountryRegionCity(existing.getCountryRegionCity());
|
||||||
|
dto.setPhoneNumber(existing.getPhoneNumber());
|
||||||
|
dto.setDesignTitle(existing.getDesignTitle());
|
||||||
|
dto.setDesignDescription(existing.getDesignDescription());
|
||||||
|
dto.setPdfPath(existing.getPdfPath());
|
||||||
|
dto.setVideoPath(existing.getVideoPath());
|
||||||
|
dto.setVideoDuration(existing.getVideoDuration());
|
||||||
|
dto.setPdfSize(existing.getPdfSize());
|
||||||
|
dto.setVideoSize(existing.getVideoSize());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查邮箱是否符合申请要求,发送验证码
|
||||||
|
* @param email AiDA邮箱
|
||||||
|
*/
|
||||||
|
public void checkEmail(String email) {
|
||||||
|
List<Integer> validRole = Arrays.asList(1, 2, 7, 8);
|
||||||
|
// 1. 验证邮箱在aida中有无账号
|
||||||
|
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.lambda().eq(Account::getUserEmail, email);
|
||||||
|
List<Account> accounts = accountMapper.selectList(queryWrapper);
|
||||||
|
if (accounts.isEmpty()) {
|
||||||
|
throw new BusinessException("Please register and subscribe to AiDA, then resubmit your application.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 验证账号是否是付费用户(如果首次提交是,但是修改的时候已经不是了,how?不允许修改吗)
|
||||||
|
if (validRole.contains(accounts.getFirst().getSystemUser())) {
|
||||||
|
String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
|
||||||
|
LocalCacheUtils.setVerifyCodeCache(
|
||||||
|
AuthenticationOperationTypeEnum.GLOBAL_AWARD.name() + "_" + email, randomVerifyCode);
|
||||||
|
SendEmailUtil.send(email, null,
|
||||||
|
SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode);
|
||||||
|
} else {
|
||||||
|
throw new BusinessException("Please subscribe to AiDA, then resubmit your application.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证验证码是否正确
|
||||||
|
* @param email 邮箱
|
||||||
|
* @param otp 一次性验证码
|
||||||
|
* @return 临时token和之前提交的表单内容
|
||||||
|
*/
|
||||||
|
public CheckOTPVO checkCode(String email, String otp) {
|
||||||
|
String otpCache = LocalCacheUtils.getVerifyCodeCache(AuthenticationOperationTypeEnum.GLOBAL_AWARD.name() + "_" + email);
|
||||||
|
assert otpCache != null;
|
||||||
|
if (otpCache.equals(otp)) {
|
||||||
|
// 1. 生成唯一token
|
||||||
|
String secureToken = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
redisUtil.addToString(tokenCacheKey + email, secureToken, 3 * 24 * 60 * 60L);
|
||||||
|
|
||||||
|
return new CheckOTPVO(secureToken, getContestantByID(email));
|
||||||
|
} else {
|
||||||
|
throw new BusinessException("Verification code is incorrect. Please try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkSecurityToken(String email, String securityToken) {
|
||||||
|
String key = tokenCacheKey + email;
|
||||||
|
if (StringUtils.isBlank(securityToken)) {
|
||||||
|
log.error("security token 缺失");
|
||||||
|
throw new BusinessException("Please complete email verification first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String tokenCache = redisUtil.getFromString(key);
|
||||||
|
if (StringUtils.isBlank(tokenCache)) {
|
||||||
|
log.error("security token 过期");
|
||||||
|
throw new BusinessException("Email verification has expired. Please verify again.");
|
||||||
|
} else if (!tokenCache.equals(securityToken)){
|
||||||
|
log.error("security token 与缓存不符");
|
||||||
|
throw new BusinessException("Identity verification failed. Please complete email verification first.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送站内信
|
||||||
|
public void sendSiteMsg(String applicationId, String email) {
|
||||||
|
Long userId;
|
||||||
|
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.lambda().eq(Account::getUserEmail, email);
|
||||||
|
List<Account> accounts = accountMapper.selectList(queryWrapper);
|
||||||
|
if (accounts.isEmpty()) {
|
||||||
|
throw new BusinessException("Please register and subscribe to AiDA, then resubmit your application.");
|
||||||
|
}else {
|
||||||
|
userId = accounts.get(0).getId();
|
||||||
|
}
|
||||||
|
PublishSysNotificationDTO sysNotificationDTO = new PublishSysNotificationDTO();
|
||||||
|
Notification notification = new Notification();
|
||||||
|
notification.setType("system");
|
||||||
|
notification.setReceiverId(userId);
|
||||||
|
sysNotificationDTO.setTitle("System Notification 系统通知");
|
||||||
|
// todo
|
||||||
|
sysNotificationDTO.setContent(link + applicationId);
|
||||||
|
notification.setContent(JSON.toJSONString(sysNotificationDTO));
|
||||||
|
notification.setIsRead(0);
|
||||||
|
notification.setCreateTime(LocalDateTime.now());
|
||||||
|
notificationMapper.insert(notification);
|
||||||
|
// 这里推送消息是在接受到视频生成结束后发生的,所以UserContext中没有用户信息
|
||||||
|
messageCenterService.pushMessage("system", userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int exportContestantFiles(Integer minContestantNumber, Integer maxContestantNumber) throws Exception {
|
||||||
|
if (minContestantNumber == null || maxContestantNumber == null) {
|
||||||
|
throw new BusinessException("minContestantNumber and maxContestantNumber are required.");
|
||||||
|
}
|
||||||
|
if (minContestantNumber > maxContestantNumber) {
|
||||||
|
throw new BusinessException("minContestantNumber cannot be greater than maxContestantNumber.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 根据contestantNumber范围查询参赛者
|
||||||
|
QueryWrapper<Contestant> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.lambda()
|
||||||
|
.ge(Contestant::getContestantNumber, minContestantNumber)
|
||||||
|
.le(Contestant::getContestantNumber, maxContestantNumber)
|
||||||
|
.orderByAsc(Contestant::getContestantNumber);
|
||||||
|
List<Contestant> contestants = contestantMapper.selectList(queryWrapper);
|
||||||
|
|
||||||
|
if (contestants.isEmpty()) {
|
||||||
|
log.info("No contestants found in range [{}, {}]", minContestantNumber, maxContestantNumber);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 创建基础目录
|
||||||
|
String baseDir = uploadDir + "/contestants";
|
||||||
|
Path basePath = Paths.get(baseDir).toAbsolutePath();
|
||||||
|
Files.createDirectories(basePath);
|
||||||
|
log.info("Base directory created: {}", basePath);
|
||||||
|
|
||||||
|
int exportedCount = 0;
|
||||||
|
|
||||||
|
// 3. 遍历每个参赛者,下载文件
|
||||||
|
for (Contestant contestant : contestants) {
|
||||||
|
Integer contestantNumber = contestant.getContestantNumber();
|
||||||
|
if (contestantNumber == null) {
|
||||||
|
log.warn("Contestant {} has no contestantNumber, skipping", contestant.getId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建参赛者文件夹
|
||||||
|
String contestantDir = baseDir + "/" + contestantNumber;
|
||||||
|
Path contestantPath = Paths.get(contestantDir);
|
||||||
|
Files.createDirectories(contestantPath);
|
||||||
|
|
||||||
|
// 下载PDF文件
|
||||||
|
String pdfPath = contestant.getPdfPath();
|
||||||
|
if (StringUtils.isNotBlank(pdfPath)) {
|
||||||
|
try {
|
||||||
|
String fileName = pdfPath.contains("/") ?
|
||||||
|
pdfPath.substring(pdfPath.lastIndexOf("/") + 1) : "design.pdf";
|
||||||
|
downloadFileFromMinio(pdfPath, contestantPath.toString(), "design.pdf");
|
||||||
|
log.info("Downloaded PDF for contestant {}", fileName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to download PDF for contestant {}: {}", contestantNumber, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载视频文件
|
||||||
|
String videoPath = contestant.getVideoPath();
|
||||||
|
if (StringUtils.isNotBlank(videoPath)) {
|
||||||
|
try {
|
||||||
|
// 根据路径判断视频格式
|
||||||
|
String fileName = videoPath.contains("/") ?
|
||||||
|
videoPath.substring(videoPath.lastIndexOf("/") + 1) : "video.mp4";
|
||||||
|
downloadFileFromMinio(videoPath, contestantPath.toString(), fileName);
|
||||||
|
log.info("Downloaded video for contestant {}", contestantNumber);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to download video for contestant {}: {}", contestantNumber, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Exported {} contestants' files to {}", exportedCount, basePath);
|
||||||
|
return exportedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从MinIO下载文件到本地
|
||||||
|
* @param minioPath MinIO路径 (格式: bucketName/objectPath)
|
||||||
|
* @param localDir 本地目录
|
||||||
|
* @param fileName 本地文件名
|
||||||
|
*/
|
||||||
|
private void downloadFileFromMinio(String minioPath, String localDir, String fileName) {
|
||||||
|
if (StringUtils.isBlank(minioPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从路径中提取bucket名称和对象名称
|
||||||
|
int index = minioPath.indexOf("/");
|
||||||
|
if (index == -1) {
|
||||||
|
log.warn("Invalid MinIO path: {}", minioPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String bucketName = minioPath.substring(0, index);
|
||||||
|
String objectName = minioPath.substring(index + 1);
|
||||||
|
|
||||||
|
// 构建本地文件完整路径
|
||||||
|
Path localFilePath = Paths.get(localDir, fileName);
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
minioUtil.downloadMinioObjectToLocal(bucketName, objectName, localFilePath.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ import io.netty.util.internal.StringUtil;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
@@ -53,7 +54,11 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
|||||||
private final PythonTAllInfoService pythonTAllInfoService;
|
private final PythonTAllInfoService pythonTAllInfoService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public LibraryModelPointVO saveOrEditTemplatePoint(LibraryModelPointDTO libraryModelPointDTO) {
|
public LibraryModelPointVO saveOrEditTemplatePoint(LibraryModelPointDTO libraryModelPointDTO) {
|
||||||
|
// 参数校验
|
||||||
|
validateInputParams(libraryModelPointDTO);
|
||||||
|
|
||||||
LibraryModelPointVO libraryModelPointVO = CopyUtil.copyObject(libraryModelPointDTO, LibraryModelPointVO.class);
|
LibraryModelPointVO libraryModelPointVO = CopyUtil.copyObject(libraryModelPointDTO, LibraryModelPointVO.class);
|
||||||
|
|
||||||
// 不管是保存还是另存为,都需要传模特的libraryId
|
// 不管是保存还是另存为,都需要传模特的libraryId
|
||||||
@@ -71,7 +76,8 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
|||||||
// 更新模特图片
|
// 更新模特图片
|
||||||
if (flag) {
|
if (flag) {
|
||||||
libModel.setUrl(url);
|
libModel.setUrl(url);
|
||||||
libModel.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false));
|
String preSignedUrl = minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
|
||||||
|
libModel.setMd5(MD5Utils.encryptFile(preSignedUrl, false));
|
||||||
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
|
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
|
||||||
libModel.setWidth(imagesWidthAndHeight.get(0));
|
libModel.setWidth(imagesWidthAndHeight.get(0));
|
||||||
libModel.setHigh(imagesWidthAndHeight.get(1));
|
libModel.setHigh(imagesWidthAndHeight.get(1));
|
||||||
@@ -104,25 +110,10 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 不覆盖,即另存为
|
// 不覆盖,即另存为
|
||||||
// 新增模特library信息
|
Library saveAsModel = createNewLibraryCopy(libModel, libraryModelPointDTO);
|
||||||
Library saveAsModel = new Library();
|
|
||||||
saveAsModel.setAccountId(libModel.getAccountId());
|
|
||||||
saveAsModel.setLevel1Type(libModel.getLevel1Type());
|
|
||||||
saveAsModel.setLevel2Type(libModel.getLevel2Type());
|
|
||||||
String ageGroup = StringUtil.isNullOrEmpty(libModel.getLevel3Type()) ? "Adult" : libModel.getLevel3Type();
|
|
||||||
saveAsModel.setLevel3Type(ageGroup);
|
|
||||||
saveAsModel.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
|
|
||||||
saveAsModel.setUrl(url);
|
|
||||||
saveAsModel.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false));
|
|
||||||
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
|
|
||||||
saveAsModel.setWidth(imagesWidthAndHeight.get(0));
|
|
||||||
saveAsModel.setHigh(imagesWidthAndHeight.get(1));
|
|
||||||
saveAsModel.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
|
||||||
libraryService.save(saveAsModel);
|
|
||||||
// 更新新的模特在library中的id,用于后面新建模特点位信息用
|
|
||||||
libraryModelPointDTO.setLibraryId(saveAsModel.getId());
|
|
||||||
|
|
||||||
// 新增模特点位信息
|
// 新增模特点位信息
|
||||||
|
libraryModelPointDTO.setLibraryId(saveAsModel.getId()); // 更新libraryId为新创建的模型ID
|
||||||
LibraryModelPoint libraryModelPoint = resolvePoint(libraryModelPointDTO);
|
LibraryModelPoint libraryModelPoint = resolvePoint(libraryModelPointDTO);
|
||||||
libraryModelPoint.setModelType("Library");
|
libraryModelPoint.setModelType("Library");
|
||||||
libraryModelPoint.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
libraryModelPoint.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
||||||
@@ -130,22 +121,50 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
|||||||
libraryModelPointVO.setTemplateId(libraryModelPoint.getId());
|
libraryModelPointVO.setTemplateId(libraryModelPoint.getId());
|
||||||
libraryModelPointVO.setRelationId(libraryModelPoint.getRelationId());
|
libraryModelPointVO.setRelationId(libraryModelPoint.getRelationId());
|
||||||
}
|
}
|
||||||
//编辑
|
|
||||||
/*if (!StringUtils.isEmpty(libraryModelPointDTO.getModelSex())) {
|
|
||||||
Library byId = libraryService.getById(libraryModelPointDTO.getLibraryId());
|
|
||||||
if (!byId.getLevel2Type().equals(libraryModelPointDTO.getModelSex())) {
|
|
||||||
if (byId.getLevel2Type().equals(Sex.FEMALE.getValue())) {
|
|
||||||
libraryService.checkModel(Sex.FEMALE.getValue(), Collections.singletonList(byId.getId()), 1);
|
|
||||||
}else {
|
|
||||||
libraryService.checkModel(Sex.MALE.getValue(), Collections.singletonList(byId.getId()), 1);
|
|
||||||
}
|
|
||||||
byId.setLevel2Type(libraryModelPointDTO.getModelSex());
|
|
||||||
libraryService.updateById(byId);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
return libraryModelPointVO;
|
return libraryModelPointVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证输入参数
|
||||||
|
*/
|
||||||
|
private void validateInputParams(LibraryModelPointDTO libraryModelPointDTO) {
|
||||||
|
if (libraryModelPointDTO == null) {
|
||||||
|
throw new BusinessException("libraryModelPointDTO cannot be null");
|
||||||
|
}
|
||||||
|
if (libraryModelPointDTO.getLibraryId() == null || libraryModelPointDTO.getLibraryId() <= 0) {
|
||||||
|
throw new BusinessException("libraryId is required");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(libraryModelPointDTO.getModelPath())) {
|
||||||
|
throw new BusinessException("modelPath is required");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(libraryModelPointDTO.getTimeZone())) {
|
||||||
|
throw new BusinessException("timeZone is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的库模型副本
|
||||||
|
*/
|
||||||
|
private Library createNewLibraryCopy(Library originalModel, LibraryModelPointDTO libraryModelPointDTO) {
|
||||||
|
// 新增模特library信息
|
||||||
|
Library saveAsModel = new Library();
|
||||||
|
saveAsModel.setAccountId(originalModel.getAccountId());
|
||||||
|
saveAsModel.setLevel1Type(originalModel.getLevel1Type());
|
||||||
|
saveAsModel.setLevel2Type(originalModel.getLevel2Type());
|
||||||
|
String ageGroup = StringUtil.isNullOrEmpty(originalModel.getLevel3Type()) ? "Adult" : originalModel.getLevel3Type();
|
||||||
|
saveAsModel.setLevel3Type(ageGroup);
|
||||||
|
saveAsModel.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
|
||||||
|
saveAsModel.setUrl(libraryModelPointDTO.getModelPath());
|
||||||
|
String preSignedUrl = minioUtil.getPreSignedUrl(libraryModelPointDTO.getModelPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
|
||||||
|
saveAsModel.setMd5(MD5Utils.encryptFile(preSignedUrl, false));
|
||||||
|
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(libraryModelPointDTO.getModelPath());
|
||||||
|
saveAsModel.setWidth(imagesWidthAndHeight.get(0));
|
||||||
|
saveAsModel.setHigh(imagesWidthAndHeight.get(1));
|
||||||
|
saveAsModel.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
||||||
|
libraryService.save(saveAsModel);
|
||||||
|
return saveAsModel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LibraryModelPointVO saveOrEditTemplatePointOld(LibraryModelPointDTO libraryModelPointDTO) {
|
public LibraryModelPointVO saveOrEditTemplatePointOld(LibraryModelPointDTO libraryModelPointDTO) {
|
||||||
// Library library = libraryService.getById(libraryModelPointDTO.getLibraryId());
|
// Library library = libraryService.getById(libraryModelPointDTO.getLibraryId());
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
throw new BusinessException("type.cannot.be.empty");
|
throw new BusinessException("type.cannot.be.empty");
|
||||||
}
|
}
|
||||||
Long accountId = UserContext.getUserHolder().getId();
|
Long accountId = UserContext.getUserHolder().getId();
|
||||||
|
Account account = accountService.getById(accountId);
|
||||||
// 查动态
|
// 查动态
|
||||||
if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")) {
|
if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")) {
|
||||||
return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize());
|
return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize());
|
||||||
@@ -92,6 +93,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
|
|
||||||
if (getNotificationDTO.getType().equals("system")) {
|
if (getNotificationDTO.getType().equals("system")) {
|
||||||
queryWrapper.lambda().eq(Notification::getType, "system")
|
queryWrapper.lambda().eq(Notification::getType, "system")
|
||||||
|
.gt(Notification::getCreateTime, account.getCreateDate())
|
||||||
.and(wrapper -> wrapper
|
.and(wrapper -> wrapper
|
||||||
.isNull(Notification::getReceiverId)
|
.isNull(Notification::getReceiverId)
|
||||||
.or()
|
.or()
|
||||||
@@ -103,7 +105,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
|
|
||||||
Page<Notification> notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper);
|
Page<Notification> notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper);
|
||||||
|
|
||||||
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId);
|
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId, account.getCreateDate());
|
||||||
IPage<NotificationVO> convert = notificationPage.convert(o -> {
|
IPage<NotificationVO> convert = notificationPage.convert(o -> {
|
||||||
NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
|
NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
|
||||||
Account senderAccount = accountService.getById(notificationVO.getSenderId());
|
Account senderAccount = accountService.getById(notificationVO.getSenderId());
|
||||||
@@ -192,6 +194,8 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
if (!type.equals("system")) {
|
if (!type.equals("system")) {
|
||||||
// 个人未读消息
|
// 个人未读消息
|
||||||
count = getUnreadCountByType(type, receiverId);
|
count = getUnreadCountByType(type, receiverId);
|
||||||
|
} else if (Objects.isNull(receiverId)) {
|
||||||
|
count = 1L;
|
||||||
} else {
|
} else {
|
||||||
// 系统未读消息
|
// 系统未读消息
|
||||||
count = getUnreadSystemNotification(receiverId);
|
count = getUnreadSystemNotification(receiverId);
|
||||||
@@ -247,6 +251,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
|
|
||||||
private Long getUnreadSystemNotification(Long receiverId) {
|
private Long getUnreadSystemNotification(Long receiverId) {
|
||||||
// Long accountId = UserContext.getUserHolder().getId();
|
// Long accountId = UserContext.getUserHolder().getId();
|
||||||
|
Account account = accountService.getById(receiverId);
|
||||||
// 计算总的系统通知数量
|
// 计算总的系统通知数量
|
||||||
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.lambda().eq(Notification::getType, "system")
|
queryWrapper.lambda().eq(Notification::getType, "system")
|
||||||
@@ -255,6 +260,9 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
.or()
|
.or()
|
||||||
.eq(Notification::getReceiverId, receiverId)
|
.eq(Notification::getReceiverId, receiverId)
|
||||||
);
|
);
|
||||||
|
if (Objects.nonNull(account)) {
|
||||||
|
queryWrapper.lambda().gt(Notification::getCreateTime, account.getCreateDate());
|
||||||
|
}
|
||||||
Long totalSysCount = baseMapper.selectCount(queryWrapper);
|
Long totalSysCount = baseMapper.selectCount(queryWrapper);
|
||||||
|
|
||||||
// 计算单个用户读了多少条系统数据
|
// 计算单个用户读了多少条系统数据
|
||||||
@@ -302,6 +310,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
// 一键已读
|
// 一键已读
|
||||||
public void setReadAll(String type) {
|
public void setReadAll(String type) {
|
||||||
Long accountId = UserContext.getUserHolder().getId();
|
Long accountId = UserContext.getUserHolder().getId();
|
||||||
|
Account account = accountService.getById(accountId);
|
||||||
// 指定某个用户的某种类型的数据,将未读数据全部已读
|
// 指定某个用户的某种类型的数据,将未读数据全部已读
|
||||||
if (!type.equals("system")) {
|
if (!type.equals("system")) {
|
||||||
// 个人消息已读
|
// 个人消息已读
|
||||||
@@ -309,7 +318,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
} else {
|
} else {
|
||||||
// 系统消息已读
|
// 系统消息已读
|
||||||
// 1、先确定当前用户未读的系统消息有哪些
|
// 1、先确定当前用户未读的系统消息有哪些
|
||||||
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId);
|
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId, account.getCreateDate());
|
||||||
// 2、将未读的设为已读
|
// 2、将未读的设为已读
|
||||||
if (!unreadSysNotificationIds.isEmpty()) setReadStatusSystem(unreadSysNotificationIds);
|
if (!unreadSysNotificationIds.isEmpty()) setReadStatusSystem(unreadSysNotificationIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,6 +258,11 @@ public class PanToneServiceImpl extends ServiceImpl<PanToneMapper, PanTone> impl
|
|||||||
d.setH(getRgbByHsvBatchDTO.getH());
|
d.setH(getRgbByHsvBatchDTO.getH());
|
||||||
d.setS(getRgbByHsvBatchDTO.getS());
|
d.setS(getRgbByHsvBatchDTO.getS());
|
||||||
d.setV(getRgbByHsvBatchDTO.getV());
|
d.setV(getRgbByHsvBatchDTO.getV());
|
||||||
|
// 不使用数据库中存储的RGB值,使用通过hsv计算得到的RGB值
|
||||||
|
int[] rgb = PantoneUtils.hsvToRgb(d.getH(), d.getS(), d.getV());
|
||||||
|
d.setR(rgb[0]);
|
||||||
|
d.setG(rgb[1]);
|
||||||
|
d.setB(rgb[2]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Map<Integer, PantoneVO> valueToPantoneVo = templateResposne.stream().collect(Collectors.toMap(
|
Map<Integer, PantoneVO> valueToPantoneVo = templateResposne.stream().collect(Collectors.toMap(
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ public class RabbitMQServiceImpl implements RabbitMQService {
|
|||||||
mqPublisher.sendGenerateMessage(message);
|
mqPublisher.sendGenerateMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishMessageToGenerateResult(String message) {
|
||||||
|
mqPublisher.sendGenerateResultMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publishMessageToSR(String message) {
|
public void publishMessageToSR(String message) {
|
||||||
mqPublisher.sendSRMessage(message);
|
mqPublisher.sendSRMessage(message);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.ai.da.service.SubscriptionPlanService;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@@ -28,6 +29,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -37,9 +39,11 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import static com.ai.da.mapper.primary.entity.Account.SystemRole.EDUCATION_SUB;
|
||||||
|
import static com.ai.da.mapper.primary.entity.SubscriptionPlan.SubscriptionStatus.ACTIVE;
|
||||||
|
import static com.ai.da.mapper.primary.entity.SubscriptionPlan.SubscriptionStatus.PENDING;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@@ -62,7 +66,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
|
|
||||||
SubscriptionPlan subscriptionPlan = CopyUtil.copyObject(subscriptionPlanDTO, SubscriptionPlan.class);
|
SubscriptionPlan subscriptionPlan = CopyUtil.copyObject(subscriptionPlanDTO, SubscriptionPlan.class);
|
||||||
if (StringUtils.isBlank(subscriptionPlanDTO.getStatus())) {
|
if (StringUtils.isBlank(subscriptionPlanDTO.getStatus())) {
|
||||||
subscriptionPlan.setStatus(SubscriptionPlan.SubscriptionStatus.PENDING.name());
|
subscriptionPlan.setStatus(PENDING.name());
|
||||||
}
|
}
|
||||||
if (Objects.isNull(subscriptionPlan.getName())) {
|
if (Objects.isNull(subscriptionPlan.getName())) {
|
||||||
subscriptionPlan.setName("DEFAULT_NAME");
|
subscriptionPlan.setName("DEFAULT_NAME");
|
||||||
@@ -74,6 +78,10 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
baseMapper.insert(subscriptionPlan);
|
baseMapper.insert(subscriptionPlan);
|
||||||
|
if (subscriptionPlan.getStatus().equals(SubscriptionPlan.SubscriptionStatus.ACTIVE.name())) {
|
||||||
|
// 执行一次激活扫描器
|
||||||
|
activeSubscriptionPlan(subscriptionPlan.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCreatePlanParams(SubscriptionPlanDTO subscriptionPlanDTO) {
|
private void validateCreatePlanParams(SubscriptionPlanDTO subscriptionPlanDTO) {
|
||||||
@@ -93,52 +101,230 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
if (Objects.isNull(account)) {
|
if (Objects.isNull(account)) {
|
||||||
throw new BusinessException("unknown.administrator.user");
|
throw new BusinessException("unknown.administrator.user");
|
||||||
}
|
}
|
||||||
|
if (account.getSystemUser().equals(8) || account.getSystemUser().equals(6)) {
|
||||||
|
throw new BusinessException("Sub-accounts.cannot.be.admins");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保证订阅计划绑定的管理员所属组织的唯一性
|
||||||
|
checkAdminCrossOrg(subscriptionPlanDTO.getAdminAccId(), subscriptionPlanDTO.getOrganizationId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断指定的管理员是否已绑定其他组织的订阅计划
|
||||||
|
private void checkAdminCrossOrg(Long adminAccId, Long organizationId) {
|
||||||
|
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.lambda().eq(SubscriptionPlan::getAdminAccId, adminAccId)
|
||||||
|
.ne(SubscriptionPlan::getOrganizationId, organizationId);
|
||||||
|
Long count = baseMapper.selectCount(queryWrapper);
|
||||||
|
if (count > 0) {
|
||||||
|
throw new BusinessException("administrator.user.is.already.bound.to.different.organization");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新 到期时间、积分总量、已使用积分量
|
// 更新 到期时间、积分总量、已使用积分量
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@Override
|
@Override
|
||||||
public void updatePlan(UpdateSubscriptionPlanDTO updateDTO) {
|
public void updatePlan(UpdateSubscriptionPlanDTO dto) {
|
||||||
if (Objects.isNull(updateDTO.getId())) {
|
if (dto.getId() == null) {
|
||||||
throw new BusinessException("id.cannot.be.empty");
|
throw new BusinessException("id.cannot.be.empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
SubscriptionPlan subscriptionPlan = baseMapper.selectById(updateDTO.getId());
|
SubscriptionPlan plan = baseMapper.selectById(dto.getId());
|
||||||
if (Objects.isNull(subscriptionPlan)) {
|
if (plan == null) {
|
||||||
throw new BusinessException("unknown.subscription.plan");
|
throw new BusinessException("unknown.subscription.plan");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.nonNull(updateDTO.getCurrentPeriodStart()) && !updateDTO.getCurrentPeriodStart().equals(subscriptionPlan.getCurrentPeriodStart())) {
|
boolean activateToday = false;
|
||||||
subscriptionPlan.setCurrentPeriodStart(updateDTO.getCurrentPeriodStart());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Objects.nonNull(updateDTO.getCurrentPeriodEnd()) && !updateDTO.getCurrentPeriodEnd().equals(subscriptionPlan.getCurrentPeriodEnd())) {
|
activateToday = handlePeriodStart(dto, plan);
|
||||||
subscriptionPlan.setCurrentPeriodEnd(updateDTO.getCurrentPeriodEnd());
|
handlePeriodEnd(dto, plan);
|
||||||
}
|
handleAccountNum(dto, plan);
|
||||||
|
handleCreditLimit(dto, plan);
|
||||||
|
handleBasicInfo(dto, plan);
|
||||||
|
|
||||||
if (Objects.nonNull(updateDTO.getAccountNum()) && !updateDTO.getAccountNum().equals(subscriptionPlan.getAccountNum())) {
|
plan.setUpdateTime(LocalDateTime.now());
|
||||||
subscriptionPlan.setAccountNum(updateDTO.getAccountNum());
|
updateById(plan);
|
||||||
}
|
|
||||||
|
|
||||||
if (Objects.nonNull(updateDTO.getCreditLimit()) && !updateDTO.getCreditLimit().equals(subscriptionPlan.getCreditLimit())) {
|
|
||||||
subscriptionPlan.setCreditLimit(updateDTO.getCreditLimit());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Objects.nonNull(updateDTO.getAdminAccId()) && !updateDTO.getAdminAccId().equals(subscriptionPlan.getAdminAccId())) {
|
|
||||||
subscriptionPlan.setAdminAccId(updateDTO.getAdminAccId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(updateDTO.getName()) && !updateDTO.getName().equals(subscriptionPlan.getName())) {
|
|
||||||
subscriptionPlan.setName(updateDTO.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptionPlan.setUpdateTime(LocalDateTime.now());
|
|
||||||
updateById(subscriptionPlan);
|
|
||||||
|
|
||||||
|
postUpdateProcess(plan, activateToday);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePlan() {
|
// ===================== 字段处理 =====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理开始时间,返回是否需要当天激活
|
||||||
|
*/
|
||||||
|
private boolean handlePeriodStart(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||||
|
Long newStart = dto.getCurrentPeriodStart();
|
||||||
|
if (newStart == null || newStart.equals(plan.getCurrentPeriodStart())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACTIVE.name().equals(plan.getStatus())) {
|
||||||
|
throw new BusinessException(
|
||||||
|
"only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.setCurrentPeriodStart(newStart);
|
||||||
|
return isToday(newStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理结束时间(只能延长)
|
||||||
|
*/
|
||||||
|
private void handlePeriodEnd(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||||
|
Long newEnd = dto.getCurrentPeriodEnd();
|
||||||
|
if (newEnd == null || newEnd.equals(plan.getCurrentPeriodEnd())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newEnd < plan.getCurrentPeriodEnd()) {
|
||||||
|
throw new BusinessException(
|
||||||
|
"the.subscription.end.date.can.be.extended.only.not.reduced"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.setCurrentPeriodEnd(newEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理账号数量
|
||||||
|
*/
|
||||||
|
private void handleAccountNum(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||||
|
Integer newAccountNum = dto.getAccountNum();
|
||||||
|
if (newAccountNum == null || newAccountNum.equals(plan.getAccountNum())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newAccountNum < plan.getAccountNum()) {
|
||||||
|
long usedSubAccounts = countExistingSubAccounts(plan.getId());
|
||||||
|
if (newAccountNum < usedSubAccounts + 1) {
|
||||||
|
throw new BusinessException(
|
||||||
|
"total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.setAccountNum(newAccountNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理积分上限
|
||||||
|
*/
|
||||||
|
private void handleCreditLimit(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||||
|
BigDecimal newLimit = dto.getCreditLimit();
|
||||||
|
if (newLimit == null || newLimit.equals(plan.getCreditLimit())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLimit.compareTo(plan.getCreditUsage()) < 0) {
|
||||||
|
throw new BusinessException(
|
||||||
|
"the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.setCreditLimit(newLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础字段
|
||||||
|
*/
|
||||||
|
private void handleBasicInfo(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||||
|
if (dto.getAdminAccId() != null
|
||||||
|
&& !dto.getAdminAccId().equals(plan.getAdminAccId())) {
|
||||||
|
// 保证订阅计划绑定的管理员所属组织的唯一性
|
||||||
|
checkAdminCrossOrg(dto.getAdminAccId(), plan.getOrganizationId());
|
||||||
|
plan.setAdminAccId(dto.getAdminAccId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(dto.getName())
|
||||||
|
&& !dto.getName().equals(plan.getName())) {
|
||||||
|
plan.setName(dto.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(dto.getCountryOrRegion())
|
||||||
|
&& !dto.getCountryOrRegion().equals(plan.getCountryOrRegion())) {
|
||||||
|
plan.setCountryOrRegion(dto.getCountryOrRegion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== 更新后处理 =====================
|
||||||
|
|
||||||
|
private void postUpdateProcess(SubscriptionPlan plan, boolean activateToday) {
|
||||||
|
if (ACTIVE.name().equals(plan.getStatus())) {
|
||||||
|
syncAdminAndSubAccounts(plan);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activateToday) {
|
||||||
|
activeSubscriptionPlan(plan.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== 账号同步 =====================
|
||||||
|
|
||||||
|
private void syncAdminAndSubAccounts(SubscriptionPlan plan) {
|
||||||
|
Account admin = findActiveAdmin(plan);
|
||||||
|
if (admin != null) {
|
||||||
|
syncAdminAccount(admin, plan);
|
||||||
|
}
|
||||||
|
syncSubAccounts(plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Account findActiveAdmin(SubscriptionPlan plan) {
|
||||||
|
return accountMapper.selectOne(
|
||||||
|
new QueryWrapper<Account>().lambda()
|
||||||
|
.eq(Account::getId, plan.getAdminAccId())
|
||||||
|
.eq(Account::getSubscriptionPlanId, plan.getId())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncAdminAccount(Account admin, SubscriptionPlan plan) {
|
||||||
|
long planEndMillis = toMillis(plan.getCurrentPeriodEnd());
|
||||||
|
|
||||||
|
if (!Objects.equals(admin.getValidEndTime(), planEndMillis)) {
|
||||||
|
admin.setValidEndTime(planEndMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin.getCreditsUsageLimit().compareTo(plan.getCreditLimit()) != 0) {
|
||||||
|
// 这里计算修改前后的差值,上限增长,则差为正,上限下降,则差为负;
|
||||||
|
BigDecimal delta = plan.getCreditLimit()
|
||||||
|
.subtract(admin.getCreditsUsageLimit());
|
||||||
|
|
||||||
|
// 因为管理员的积分中可能包含自己购买的积分,所以这里直接将差值添加到管理员的credit中
|
||||||
|
admin.setCredits(admin.getCredits().add(delta));
|
||||||
|
admin.setCreditsUsageLimit(plan.getCreditLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
accountMapper.updateById(admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncSubAccounts(SubscriptionPlan plan) {
|
||||||
|
accountMapper.update(
|
||||||
|
null,
|
||||||
|
new UpdateWrapper<Account>().lambda()
|
||||||
|
.set(Account::getValidEndTime, toMillis(plan.getCurrentPeriodEnd()))
|
||||||
|
.eq(Account::getSubscriptionPlanId, plan.getId())
|
||||||
|
.eq(Account::getSystemUser, EDUCATION_SUB.getCode())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== 辅助方法 =====================
|
||||||
|
|
||||||
|
private long countExistingSubAccounts(Long planId) {
|
||||||
|
return accountMapper.selectCount(
|
||||||
|
new QueryWrapper<Account>().lambda()
|
||||||
|
.eq(Account::getSubscriptionPlanId, planId)
|
||||||
|
.eq(Account::getSystemUser, EDUCATION_SUB.getCode())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isToday(Long timestampSeconds) {
|
||||||
|
return timestampSeconds >= getTodayStartTimestamp()
|
||||||
|
&& timestampSeconds < getTodayEndTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long toMillis(Long seconds) {
|
||||||
|
return seconds * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,6 +342,12 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
queryWrapper.lambda().in(SubscriptionPlan::getStatus, subscriptionPlanPageQuery.getStatus());
|
queryWrapper.lambda().in(SubscriptionPlan::getStatus, subscriptionPlanPageQuery.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(subscriptionPlanPageQuery.getCountryOrRegion())){
|
||||||
|
queryWrapper.lambda().like(SubscriptionPlan::getCountryOrRegion, subscriptionPlanPageQuery.getCountryOrRegion());
|
||||||
|
}
|
||||||
|
|
||||||
|
queryWrapper.lambda().orderByAsc(SubscriptionPlan::getCurrentPeriodStart);
|
||||||
|
|
||||||
return baseMapper.selectList(queryWrapper);
|
return baseMapper.selectList(queryWrapper);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -210,6 +402,9 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
wrapper.in("sp.status", query.getStatus());
|
wrapper.in("sp.status", query.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(query.getCountryOrRegion())) {
|
||||||
|
wrapper.like("sp.country_or_region", query.getCountryOrRegion());
|
||||||
|
}
|
||||||
// 按创建时间倒序排序
|
// 按创建时间倒序排序
|
||||||
wrapper.ne("sp.is_deleted", 1)
|
wrapper.ne("sp.is_deleted", 1)
|
||||||
.orderByDesc("sp.create_time");
|
.orderByDesc("sp.create_time");
|
||||||
@@ -387,7 +582,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否在有效期内
|
// 检查是否在有效期内
|
||||||
if (plan.getCurrentPeriodEnd() != null && isExpired(plan.getCurrentPeriodEnd())) {
|
if (plan.getCurrentPeriodEnd() != null && !isExpired(plan.getCurrentPeriodEnd())) {
|
||||||
throw new BusinessException("valid.subscription.period");
|
throw new BusinessException("valid.subscription.period");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -407,18 +602,28 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
return currentPeriodEnd < currentTimestamp;
|
return currentPeriodEnd < currentTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void activeSubscriptionPlan() {
|
public void activeSubscriptionPlan(Long planId) {
|
||||||
log.info("开始执行订阅计划生效检查...");
|
log.info("开始执行订阅计划生效检查...");
|
||||||
|
|
||||||
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart,找出今天开始生效的计划
|
// 支持按id激活
|
||||||
List<SubscriptionPlan> todayActivePlans = findTodayActivePlans();
|
List<SubscriptionPlan> todayActivePlans = new ArrayList<>();
|
||||||
|
if (Objects.nonNull(planId)) {
|
||||||
|
SubscriptionPlan subscriptionPlan = baseMapper.selectById(planId);
|
||||||
|
if (Objects.nonNull(subscriptionPlan)){
|
||||||
|
todayActivePlans.add(subscriptionPlan);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart,找出今天开始生效的计划
|
||||||
|
todayActivePlans = findTodayActivePlans();
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(todayActivePlans)) {
|
if (CollectionUtils.isEmpty(todayActivePlans)) {
|
||||||
log.info("今日没有需要生效的订阅计划");
|
log.info("今日没有需要生效的订阅计划");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
|
|
||||||
|
|
||||||
// 2. 处理每个今天开始生效的订阅计划
|
// 2. 处理每个今天开始生效的订阅计划
|
||||||
for (SubscriptionPlan plan : todayActivePlans) {
|
for (SubscriptionPlan plan : todayActivePlans) {
|
||||||
@@ -449,7 +654,8 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
|
|
||||||
// 查询今天开始生效的订阅计划
|
// 查询今天开始生效的订阅计划
|
||||||
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.eq("is_deleted", 0) // 未删除
|
queryWrapper.eq("is_deleted", 0) // 未删除
|
||||||
|
.in("status", Arrays.asList(PENDING.name(), ACTIVE.name())) // 还未被激活的,或者设置为激活状态但是未被实际激活的
|
||||||
.between("current_period_start", todayStart, todayEnd) // 今天开始生效
|
.between("current_period_start", todayStart, todayEnd) // 今天开始生效
|
||||||
.orderByAsc("current_period_start"); // 按开始时间排序
|
.orderByAsc("current_period_start"); // 按开始时间排序
|
||||||
|
|
||||||
@@ -487,7 +693,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
|||||||
*/
|
*/
|
||||||
private void updateAccount(Account account, SubscriptionPlan plan, boolean isAdmin) {
|
private void updateAccount(Account account, SubscriptionPlan plan, boolean isAdmin) {
|
||||||
// 如果是管理员的切换,先再次记录一下已使用的积分
|
// 如果是管理员的切换,先再次记录一下已使用的积分
|
||||||
if (isAdmin) {
|
if (isAdmin && Objects.nonNull(account.getSubscriptionPlanId())) {
|
||||||
SubscriptionPlan currentPlan = baseMapper.selectById(account.getSubscriptionPlanId());
|
SubscriptionPlan currentPlan = baseMapper.selectById(account.getSubscriptionPlanId());
|
||||||
if (currentPlan.getCreditUsage().compareTo(account.getCreditsUsage()) < 0) {
|
if (currentPlan.getCreditUsage().compareTo(account.getCreditsUsage()) < 0) {
|
||||||
updateSubscriptionPlanUsage(currentPlan, account.getCreditsUsage());
|
updateSubscriptionPlanUsage(currentPlan, account.getCreditsUsage());
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.ai.da.model.vo.DesignPythonOutfitVO;
|
|||||||
import com.ai.da.model.vo.TDesignPythonOutfitDetailVO;
|
import com.ai.da.model.vo.TDesignPythonOutfitDetailVO;
|
||||||
import com.ai.da.service.ITDesignPythonOutfitDetailService;
|
import com.ai.da.service.ITDesignPythonOutfitDetailService;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@@ -67,6 +68,13 @@ public class TDesignPythonOutfitDetailServiceImpl extends ServiceImpl<TDesignPyt
|
|||||||
designPythonOutfitVO.setScale(modifyScale(detail.getScale()));
|
designPythonOutfitVO.setScale(modifyScale(detail.getScale()));
|
||||||
designPythonOutfitVO.setOffset(StringUtil.isNullOrEmpty(detail.getOffset()) ? Arrays.asList(0L, 0L) : parseLongList(detail.getOffset()));
|
designPythonOutfitVO.setOffset(StringUtil.isNullOrEmpty(detail.getOffset()) ? Arrays.asList(0L, 0L) : parseLongList(detail.getOffset()));
|
||||||
designPythonOutfitVO.setPriority(Math.abs(detail.getPriority()));
|
designPythonOutfitVO.setPriority(Math.abs(detail.getPriority()));
|
||||||
|
if (detail.getTranspose() != null) {
|
||||||
|
List<Integer> transposeList = JSONArray.parseArray(detail.getTranspose(), Integer.class);
|
||||||
|
designPythonOutfitVO.setTranspose(transposeList.stream().mapToInt(Integer::intValue).toArray());
|
||||||
|
} else {
|
||||||
|
designPythonOutfitVO.setTranspose(null);
|
||||||
|
}
|
||||||
|
designPythonOutfitVO.setRotate(detail.getRotate());
|
||||||
// designPythonOutfitVO.setOffset(CollectionUtil.isEmpty(offset) ? Arrays.asList(0L, 0L) : offset);
|
// designPythonOutfitVO.setOffset(CollectionUtil.isEmpty(offset) ? Arrays.asList(0L, 0L) : offset);
|
||||||
|
|
||||||
/*if (!StringUtil.isNullOrEmpty(detail.getImageSize())){
|
/*if (!StringUtil.isNullOrEmpty(detail.getImageSize())){
|
||||||
|
|||||||
@@ -1469,15 +1469,10 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
|
|||||||
sortRank(toProductImageResult);
|
sortRank(toProductImageResult);
|
||||||
results.add(new MagicToolResultVO(taskId, fluxResult));
|
results.add(new MagicToolResultVO(taskId, fluxResult));
|
||||||
} else {
|
} else {
|
||||||
fluxResult="Fail";
|
results.add(processFluxResult(fluxResult, toProductImageResult, taskId, toProductImageRecord.getPrompt()));
|
||||||
toProductImageResult.setStatus(fluxResult);
|
// 扣积分
|
||||||
toProductImageResultMapper.updateById(toProductImageResult);
|
Boolean flag = creditsService.taskCreditsDeduction(project.getAccountId(), taskId);
|
||||||
sortRank(toProductImageResult);
|
if (flag) creditsService.updateChangedCredits(String.valueOf(project.getAccountId()), taskId);
|
||||||
results.add(new MagicToolResultVO(taskId, fluxResult));
|
|
||||||
// results.add(processFluxResult(fluxResult, toProductImageResult, taskId, toProductImageRecord.getPrompt()));
|
|
||||||
// // 扣积分
|
|
||||||
// Boolean flag = creditsService.taskCreditsDeduction(project.getAccountId(), taskId);
|
|
||||||
// if (flag) creditsService.updateChangedCredits(String.valueOf(project.getAccountId()), taskId);
|
|
||||||
}
|
}
|
||||||
// 将积分暂扣区的积分移除
|
// 将积分暂扣区的积分移除
|
||||||
if (toProductImageResult.getStatus().equals("Fail")) {
|
if (toProductImageResult.getStatus().equals("Fail")) {
|
||||||
@@ -1534,11 +1529,12 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
|
|||||||
if (!collectionSorts.isEmpty()) {
|
if (!collectionSorts.isEmpty()) {
|
||||||
|
|
||||||
collectionSortMapper.deleteById(collectionSorts.get(0));
|
collectionSortMapper.deleteById(collectionSorts.get(0));
|
||||||
List<CollectionSort> collectionSorts1 = collectionSortMapper.selectList(new LambdaQueryWrapper<CollectionSort>().eq(CollectionSort::getParentId, collectionSorts.get(0).getParentId()).orderByDesc(CollectionSort::getSort).last("LIMIT 1"));
|
List<CollectionSort> collectionSorts1 = collectionSortMapper.selectList(new LambdaQueryWrapper<CollectionSort>().eq(CollectionSort::getParentId, collectionSorts.get(0).getParentId()).gt(CollectionSort::getSort, collectionSorts.get(0).getSort()));
|
||||||
|
|
||||||
if (!collectionSorts1.isEmpty()) {
|
if (!collectionSorts1.isEmpty()) {
|
||||||
collectionSorts1.get(0).setSort(collectionSorts1.get(0).getSort() - 1);
|
for (CollectionSort collectionSort : collectionSorts1) {
|
||||||
collectionSortMapper.updateById(collectionSorts1.get(0));
|
collectionSort.setSort(collectionSort.getSort() - 1);
|
||||||
|
collectionSortMapper.updateById(collectionSort);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/main/java/com/ai/da/service/upload/UploadService.java
Normal file
93
src/main/java/com/ai/da/service/upload/UploadService.java
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package com.ai.da.service.upload;
|
||||||
|
|
||||||
|
import com.ai.da.model.dto.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片上传服务接口
|
||||||
|
* 提供PDF和视频文件的分片上传、断点续传功能
|
||||||
|
*/
|
||||||
|
public interface UploadService {
|
||||||
|
|
||||||
|
// ===== PDF上传相关 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化PDF上传任务
|
||||||
|
* @param request 初始化请求
|
||||||
|
* @return 上传任务信息
|
||||||
|
*/
|
||||||
|
UploadTask initPdfUpload(UploadInitRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传PDF分片
|
||||||
|
* @param uploadId 上传任务ID
|
||||||
|
* @param chunk 分片文件
|
||||||
|
* @param chunkIndex 分片索引
|
||||||
|
* @param totalChunks 总分片数
|
||||||
|
* @return 分片上传结果
|
||||||
|
*/
|
||||||
|
UploadChunkResponse uploadPdfChunk(String uploadId, MultipartFile chunk,
|
||||||
|
int chunkIndex, int totalChunks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成PDF上传(异步合并并上传到MinIO)
|
||||||
|
* @param uploadId 上传任务ID
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param totalSize 文件总大小
|
||||||
|
* @return 完成上传结果
|
||||||
|
*/
|
||||||
|
UploadCompleteResponse completePdfUpload(String uploadId, String fileName, long totalSize, String email, String secureToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询PDF上传状态
|
||||||
|
* @param uploadId 上传任务ID
|
||||||
|
* @return 上传状态信息
|
||||||
|
*/
|
||||||
|
UploadStatusResponse getPdfUploadStatus(String uploadId);
|
||||||
|
|
||||||
|
|
||||||
|
// ===== 视频上传相关 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视频上传任务
|
||||||
|
* @param request 初始化请求
|
||||||
|
* @return 上传任务信息
|
||||||
|
*/
|
||||||
|
UploadTask initVideoUpload(UploadInitRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传视频分片
|
||||||
|
* @param uploadId 上传任务ID
|
||||||
|
* @param chunk 分片文件
|
||||||
|
* @param chunkIndex 分片索引
|
||||||
|
* @param totalChunks 总分片数
|
||||||
|
* @return 分片上传结果
|
||||||
|
*/
|
||||||
|
UploadChunkResponse uploadVideoChunk(String uploadId, MultipartFile chunk,
|
||||||
|
int chunkIndex, int totalChunks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成视频上传(异步合并并上传到MinIO)
|
||||||
|
* @param uploadId 上传任务ID
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param totalSize 文件总大小
|
||||||
|
* @return 完成上传结果
|
||||||
|
*/
|
||||||
|
UploadCompleteResponse completeVideoUpload(String uploadId, String fileName, long totalSize, String email, String secureToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询视频上传状态
|
||||||
|
* @param uploadId 上传任务ID
|
||||||
|
* @return 上传状态信息
|
||||||
|
*/
|
||||||
|
UploadStatusResponse getVideoUploadStatus(String uploadId);
|
||||||
|
|
||||||
|
|
||||||
|
// ===== 通用功能 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期上传任务
|
||||||
|
*/
|
||||||
|
void cleanupExpiredUploads();
|
||||||
|
}
|
||||||
111
src/main/java/com/ai/da/service/upload/UploadTask.java
Normal file
111
src/main/java/com/ai/da/service/upload/UploadTask.java
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package com.ai.da.service.upload;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务实体类
|
||||||
|
* 用于管理分片上传的状态和元数据
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class UploadTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务唯一标识
|
||||||
|
*/
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名
|
||||||
|
*/
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型 (pdf/video)
|
||||||
|
*/
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件总大小(字节)
|
||||||
|
*/
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户邮箱
|
||||||
|
*/
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总分片数
|
||||||
|
*/
|
||||||
|
private Integer totalChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片大小(字节)
|
||||||
|
*/
|
||||||
|
private Integer chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已上传分片索引集合
|
||||||
|
*/
|
||||||
|
private Set<Integer> uploadedChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传状态
|
||||||
|
*/
|
||||||
|
private UploadStatus status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务过期时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime expiresAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最终文件在MinIO中的路径
|
||||||
|
*/
|
||||||
|
private String finalPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传状态枚举
|
||||||
|
*/
|
||||||
|
public enum UploadStatus {
|
||||||
|
/**
|
||||||
|
* 已初始化,等待上传分片
|
||||||
|
*/
|
||||||
|
INITIATED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正在上传分片
|
||||||
|
*/
|
||||||
|
UPLOADING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正在处理文件(合并分片、上传到存储)
|
||||||
|
*/
|
||||||
|
PROCESSING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传完成
|
||||||
|
*/
|
||||||
|
COMPLETED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传失败
|
||||||
|
*/
|
||||||
|
FAILED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务过期
|
||||||
|
*/
|
||||||
|
EXPIRED
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,598 @@
|
|||||||
|
package com.ai.da.service.upload.impl;
|
||||||
|
|
||||||
|
import com.ai.da.common.config.exception.BusinessException;
|
||||||
|
import com.ai.da.common.utils.MinioUtil;
|
||||||
|
import com.ai.da.model.dto.*;
|
||||||
|
import com.ai.da.service.upload.UploadService;
|
||||||
|
import com.ai.da.service.upload.UploadTask;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.minio.PutObjectArgs;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分片上传服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class UploadServiceImpl implements UploadService {
|
||||||
|
|
||||||
|
// ===== 配置参数 =====
|
||||||
|
|
||||||
|
@Value("${file.upload.temp.dir:temp/uploads}")
|
||||||
|
private String tempDir;
|
||||||
|
|
||||||
|
// PDF分片大小:1MB
|
||||||
|
@Value("${file.upload.chunk.size.pdf:1048576}")
|
||||||
|
private int pdfChunkSize;
|
||||||
|
|
||||||
|
// 视频分片大小:2MB
|
||||||
|
@Value("${file.upload.chunk.size.video:2097152}")
|
||||||
|
private int videoChunkSize;
|
||||||
|
|
||||||
|
// 文件大小限制
|
||||||
|
@Value("${file.upload.max.size.pdf:20971520}") // PDF: 20MB
|
||||||
|
private long maxPdfSize;
|
||||||
|
|
||||||
|
@Value("${file.upload.max.size.video:104857600}") // 视频: 100MB
|
||||||
|
private long maxVideoSize;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MinioUtil minioUtil;
|
||||||
|
@Value("${minio.bucketName.globalAward:global-award}")
|
||||||
|
private String minioBucket;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.ai.da.service.GlobalAwardService globalAwardService;
|
||||||
|
|
||||||
|
// 内存存储上传任务状态
|
||||||
|
private final ConcurrentHashMap<String, UploadTask> uploadTasks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// JSON序列化工具
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用启动时加载现有上传任务
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
public void loadExistingTasks() {
|
||||||
|
try {
|
||||||
|
Path tempPath = Paths.get(tempDir);
|
||||||
|
if (!Files.exists(tempPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.list(tempPath)
|
||||||
|
.filter(Files::isDirectory)
|
||||||
|
.forEach(uploadDir -> {
|
||||||
|
try {
|
||||||
|
String uploadId = uploadDir.getFileName().toString();
|
||||||
|
Path metadataPath = uploadDir.resolve("metadata.json");
|
||||||
|
|
||||||
|
if (Files.exists(metadataPath)) {
|
||||||
|
String json = Files.readString(metadataPath);
|
||||||
|
UploadTask task = objectMapper.readValue(json, UploadTask.class);
|
||||||
|
|
||||||
|
// 检查任务是否已过期
|
||||||
|
if (task.getExpiresAt().isAfter(LocalDateTime.now())) {
|
||||||
|
uploadTasks.put(uploadId, task);
|
||||||
|
log.info("加载现有上传任务: uploadId={}, status={}", uploadId, task.getStatus());
|
||||||
|
} else {
|
||||||
|
// 清理过期任务
|
||||||
|
cleanupTempFiles(uploadId);
|
||||||
|
log.info("清理过期上传任务: uploadId={}", uploadId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("加载上传任务失败: {}", uploadDir.getFileName(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("成功加载 {} 个现有上传任务", uploadTasks.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("加载现有上传任务时发生错误", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== PDF上传实现 =====
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadTask initPdfUpload(UploadInitRequest request) {
|
||||||
|
// 验证安全令牌
|
||||||
|
globalAwardService.checkSecurityToken(request.getEmail(), request.getSecureToken());
|
||||||
|
|
||||||
|
// 验证PDF文件
|
||||||
|
validatePdfFile(request);
|
||||||
|
|
||||||
|
// 创建上传任务
|
||||||
|
String uploadId = UUID.randomUUID().toString();
|
||||||
|
int totalChunks = (int) Math.ceil((double) request.getFileSize() / pdfChunkSize);
|
||||||
|
|
||||||
|
UploadTask task = createUploadTask(request, uploadId, totalChunks, pdfChunkSize, "pdf");
|
||||||
|
|
||||||
|
// 创建临时目录并保存任务状态
|
||||||
|
createTempDirectory(uploadId);
|
||||||
|
uploadTasks.put(uploadId, task);
|
||||||
|
saveTaskMetadata(task);
|
||||||
|
|
||||||
|
log.info("PDF上传任务初始化完成: uploadId={}, totalChunks={}, fileSize={}",
|
||||||
|
uploadId, totalChunks, request.getFileSize());
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadChunkResponse uploadPdfChunk(String uploadId, MultipartFile chunk,
|
||||||
|
int chunkIndex, int totalChunks) {
|
||||||
|
// 验证任务状态
|
||||||
|
UploadTask task = validateAndGetTask(uploadId, "pdf");
|
||||||
|
|
||||||
|
// 保存分片到本地
|
||||||
|
saveChunkToLocal(chunk, uploadId, chunkIndex);
|
||||||
|
|
||||||
|
// 更新任务进度
|
||||||
|
updateTaskProgress(task, chunkIndex);
|
||||||
|
|
||||||
|
log.debug("PDF分片上传完成: uploadId={}, chunkIndex={}, size={}",
|
||||||
|
uploadId, chunkIndex, chunk.getSize());
|
||||||
|
|
||||||
|
return UploadChunkResponse.builder()
|
||||||
|
.chunkIndex(chunkIndex)
|
||||||
|
.uploaded(true)
|
||||||
|
.size(chunk.getSize())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadCompleteResponse completePdfUpload(String uploadId, String fileName, long totalSize, String email, String secureToken) {
|
||||||
|
// 验证安全令牌
|
||||||
|
globalAwardService.checkSecurityToken(email, secureToken);
|
||||||
|
|
||||||
|
UploadTask task = validateAndGetTask(uploadId, "pdf");
|
||||||
|
|
||||||
|
log.info("开始PDF文件合并: uploadId={}, fileName={}", uploadId, fileName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 合并所有分片
|
||||||
|
Path mergedFile = mergeChunks(task);
|
||||||
|
|
||||||
|
// 2. 上传到MinIO
|
||||||
|
String finalPath = uploadToMinio(task, mergedFile, "pdf");
|
||||||
|
|
||||||
|
// 3. 更新任务状态并清理
|
||||||
|
completeTask(task, finalPath);
|
||||||
|
cleanupTempFiles(uploadId);
|
||||||
|
|
||||||
|
log.info("PDF上传完成: uploadId={}, finalPath={}", uploadId, finalPath);
|
||||||
|
|
||||||
|
return buildCompleteResponse(task, finalPath, totalSize);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("PDF上传失败: uploadId={}", uploadId, e);
|
||||||
|
task.setStatus(UploadTask.UploadStatus.FAILED);
|
||||||
|
saveTaskMetadata(task);
|
||||||
|
throw new BusinessException("File merge failed. Please try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadStatusResponse getPdfUploadStatus(String uploadId) {
|
||||||
|
UploadTask task = uploadTasks.get(uploadId);
|
||||||
|
if (task == null) {
|
||||||
|
throw new BusinessException("Upload task not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"pdf".equals(task.getFileType())) {
|
||||||
|
throw new BusinessException("Task type mismatch.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算上传进度
|
||||||
|
double progress = task.getTotalChunks() > 0 ?
|
||||||
|
(double) task.getUploadedChunks().size() / task.getTotalChunks() * 100 : 0;
|
||||||
|
|
||||||
|
long uploadedSize = task.getUploadedChunks().size() * task.getChunkSize();
|
||||||
|
|
||||||
|
return UploadStatusResponse.builder()
|
||||||
|
.uploadId(uploadId)
|
||||||
|
.status(task.getStatus().name().toLowerCase())
|
||||||
|
.progress(Math.min(progress, 100.0))
|
||||||
|
.uploadedChunks(new HashSet<>(task.getUploadedChunks()))
|
||||||
|
.totalChunks(task.getTotalChunks())
|
||||||
|
.totalSize(task.getFileSize())
|
||||||
|
.uploadedSize(Math.min(uploadedSize, task.getFileSize()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 视频上传实现 =====
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadTask initVideoUpload(UploadInitRequest request) {
|
||||||
|
// 验证安全令牌
|
||||||
|
globalAwardService.checkSecurityToken(request.getEmail(), request.getSecureToken());
|
||||||
|
|
||||||
|
// 验证视频文件
|
||||||
|
validateVideoFile(request);
|
||||||
|
|
||||||
|
// 创建上传任务
|
||||||
|
String uploadId = UUID.randomUUID().toString();
|
||||||
|
int totalChunks = (int) Math.ceil((double) request.getFileSize() / videoChunkSize);
|
||||||
|
|
||||||
|
UploadTask task = createUploadTask(request, uploadId, totalChunks, videoChunkSize, "video");
|
||||||
|
|
||||||
|
// 创建临时目录并保存任务状态
|
||||||
|
createTempDirectory(uploadId);
|
||||||
|
uploadTasks.put(uploadId, task);
|
||||||
|
saveTaskMetadata(task);
|
||||||
|
|
||||||
|
log.info("视频上传任务初始化完成: uploadId={}, totalChunks={}, fileSize={}",
|
||||||
|
uploadId, totalChunks, request.getFileSize());
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadChunkResponse uploadVideoChunk(String uploadId, MultipartFile chunk,
|
||||||
|
int chunkIndex, int totalChunks) {
|
||||||
|
// 验证任务状态
|
||||||
|
UploadTask task = validateAndGetTask(uploadId, "video");
|
||||||
|
|
||||||
|
// 保存分片到本地
|
||||||
|
saveChunkToLocal(chunk, uploadId, chunkIndex);
|
||||||
|
|
||||||
|
// 更新任务进度
|
||||||
|
updateTaskProgress(task, chunkIndex);
|
||||||
|
|
||||||
|
log.debug("视频分片上传完成: uploadId={}, chunkIndex={}, size={}",
|
||||||
|
uploadId, chunkIndex, chunk.getSize());
|
||||||
|
|
||||||
|
return UploadChunkResponse.builder()
|
||||||
|
.chunkIndex(chunkIndex)
|
||||||
|
.uploaded(true)
|
||||||
|
.size(chunk.getSize())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadCompleteResponse completeVideoUpload(String uploadId, String fileName, long totalSize, String email, String secureToken) {
|
||||||
|
// 验证安全令牌
|
||||||
|
globalAwardService.checkSecurityToken(email, secureToken);
|
||||||
|
|
||||||
|
UploadTask task = validateAndGetTask(uploadId, "video");
|
||||||
|
|
||||||
|
log.info("开始视频文件合并: uploadId={}, fileName={}", uploadId, fileName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 合并所有分片
|
||||||
|
Path mergedFile = mergeChunks(task);
|
||||||
|
|
||||||
|
// 2. 上传到MinIO
|
||||||
|
String finalPath = uploadToMinio(task, mergedFile, "video");
|
||||||
|
|
||||||
|
// 3. 更新任务状态并清理
|
||||||
|
completeTask(task, finalPath);
|
||||||
|
cleanupTempFiles(uploadId);
|
||||||
|
|
||||||
|
log.info("视频上传完成: uploadId={}, finalPath={}", uploadId, finalPath);
|
||||||
|
|
||||||
|
return buildCompleteResponse(task, finalPath, totalSize);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("视频上传失败: uploadId={}", uploadId, e);
|
||||||
|
task.setStatus(UploadTask.UploadStatus.FAILED);
|
||||||
|
saveTaskMetadata(task);
|
||||||
|
throw new BusinessException("File merge failed. Please try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadStatusResponse getVideoUploadStatus(String uploadId) {
|
||||||
|
UploadTask task = uploadTasks.get(uploadId);
|
||||||
|
if (task == null) {
|
||||||
|
throw new BusinessException("Upload task not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"video".equals(task.getFileType())) {
|
||||||
|
throw new BusinessException("Task type mismatch.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算上传进度
|
||||||
|
double progress = task.getTotalChunks() > 0 ?
|
||||||
|
(double) task.getUploadedChunks().size() / task.getTotalChunks() * 100 : 0;
|
||||||
|
|
||||||
|
long uploadedSize = task.getUploadedChunks().size() * task.getChunkSize();
|
||||||
|
|
||||||
|
return UploadStatusResponse.builder()
|
||||||
|
.uploadId(uploadId)
|
||||||
|
.status(task.getStatus().name().toLowerCase())
|
||||||
|
.progress(Math.min(progress, 100.0))
|
||||||
|
.uploadedChunks(new HashSet<>(task.getUploadedChunks()))
|
||||||
|
.totalChunks(task.getTotalChunks())
|
||||||
|
.totalSize(task.getFileSize())
|
||||||
|
.uploadedSize(Math.min(uploadedSize, task.getFileSize()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 核心辅助方法 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证PDF文件
|
||||||
|
*/
|
||||||
|
private void validatePdfFile(UploadInitRequest request) {
|
||||||
|
if (!"application/pdf".equals(request.getFileType())) {
|
||||||
|
throw new BusinessException("Only PDF files are allowed.");
|
||||||
|
}
|
||||||
|
if (request.getFileSize() > maxPdfSize) {
|
||||||
|
throw new BusinessException("PDF file size cannot exceed " + (maxPdfSize / 1024 / 1024) + "MB.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证视频文件
|
||||||
|
*/
|
||||||
|
private void validateVideoFile(UploadInitRequest request) {
|
||||||
|
if (request.getFileType() == null ||
|
||||||
|
(!request.getFileType().contains("mp4") &&
|
||||||
|
!request.getFileType().contains("video") &&
|
||||||
|
!request.getFileType().contains("avi"))) {
|
||||||
|
throw new BusinessException("Unsupported video format.");
|
||||||
|
}
|
||||||
|
if (request.getFileSize() > maxVideoSize) {
|
||||||
|
throw new BusinessException("Video file size cannot exceed " + (maxVideoSize / 1024 / 1024) + "MB.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建上传任务
|
||||||
|
*/
|
||||||
|
private UploadTask createUploadTask(UploadInitRequest request, String uploadId,
|
||||||
|
int totalChunks, int chunkSize, String fileType) {
|
||||||
|
return UploadTask.builder()
|
||||||
|
.uploadId(uploadId)
|
||||||
|
.fileName(request.getFileName())
|
||||||
|
.fileType(fileType)
|
||||||
|
.fileSize(request.getFileSize())
|
||||||
|
.email(request.getEmail())
|
||||||
|
.totalChunks(totalChunks)
|
||||||
|
.chunkSize(chunkSize)
|
||||||
|
.uploadedChunks(new HashSet<>())
|
||||||
|
.status(UploadTask.UploadStatus.INITIATED)
|
||||||
|
.createdAt(LocalDateTime.now())
|
||||||
|
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时目录
|
||||||
|
*/
|
||||||
|
private void createTempDirectory(String uploadId) {
|
||||||
|
try {
|
||||||
|
Path uploadDir = Paths.get(tempDir, uploadId, "chunks");
|
||||||
|
Files.createDirectories(uploadDir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BusinessException("Failed to create temporary directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证并获取上传任务
|
||||||
|
*/
|
||||||
|
private UploadTask validateAndGetTask(String uploadId, String expectedType) {
|
||||||
|
UploadTask task = uploadTasks.get(uploadId);
|
||||||
|
if (task == null) {
|
||||||
|
throw new BusinessException("Upload task not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.getExpiresAt().isBefore(LocalDateTime.now())) {
|
||||||
|
task.setStatus(UploadTask.UploadStatus.EXPIRED);
|
||||||
|
throw new BusinessException("Upload task has expired.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expectedType.equals(task.getFileType())) {
|
||||||
|
throw new BusinessException("Task type mismatch.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.getStatus() == UploadTask.UploadStatus.COMPLETED ||
|
||||||
|
task.getStatus() == UploadTask.UploadStatus.FAILED) {
|
||||||
|
throw new BusinessException("Task has already been completed or failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存分片到本地
|
||||||
|
*/
|
||||||
|
private void saveChunkToLocal(MultipartFile chunk, String uploadId, int chunkIndex) {
|
||||||
|
try {
|
||||||
|
Path chunkPath = Paths.get(tempDir, uploadId, "chunks", "chunk_" + chunkIndex);
|
||||||
|
Files.copy(chunk.getInputStream(), chunkPath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BusinessException("Failed to save file chunk.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新任务进度
|
||||||
|
*/
|
||||||
|
private void updateTaskProgress(UploadTask task, int chunkIndex) {
|
||||||
|
task.getUploadedChunks().add(chunkIndex);
|
||||||
|
task.setStatus(UploadTask.UploadStatus.UPLOADING);
|
||||||
|
saveTaskMetadata(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并分片文件(零拷贝方式)
|
||||||
|
*/
|
||||||
|
private Path mergeChunks(UploadTask task) throws IOException {
|
||||||
|
Path mergedFile = Files.createTempFile("merge_", "_" + task.getFileName());
|
||||||
|
|
||||||
|
try (FileChannel outputChannel = FileChannel.open(mergedFile, StandardOpenOption.WRITE)) {
|
||||||
|
for (int i = 0; i < task.getTotalChunks(); i++) {
|
||||||
|
Path chunkPath = Paths.get(tempDir, task.getUploadId(), "chunks", "chunk_" + i);
|
||||||
|
try (FileChannel inputChannel = FileChannel.open(chunkPath, StandardOpenOption.READ)) {
|
||||||
|
// 零拷贝传输,提升性能
|
||||||
|
inputChannel.transferTo(0, inputChannel.size(), outputChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件到MinIO
|
||||||
|
*/
|
||||||
|
private String uploadToMinio(UploadTask task, Path mergedFile, String fileType) throws Exception {
|
||||||
|
// 生成MinIO路径: contestants/{email}/{date}/{filename}
|
||||||
|
String normalizedEmail = normalizeEmail(task.getEmail());
|
||||||
|
String datePart = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
|
||||||
|
String ext = getFileExtension(task.getFileName());
|
||||||
|
String filename = System.currentTimeMillis() + "_" + UUID.randomUUID().toString() + ext;
|
||||||
|
String relativePath = "contestants/" + normalizedEmail + "/" + datePart + "/" + filename;
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
try (FileInputStream fis = new FileInputStream(mergedFile.toFile())) {
|
||||||
|
minioUtil.getMinioClient().putObject(
|
||||||
|
PutObjectArgs.builder()
|
||||||
|
.bucket(minioBucket)
|
||||||
|
.object(relativePath)
|
||||||
|
.stream(fis, mergedFile.toFile().length(), -1)
|
||||||
|
.contentType(getContentType(fileType))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成任务
|
||||||
|
*/
|
||||||
|
private void completeTask(UploadTask task, String finalPath) {
|
||||||
|
task.setStatus(UploadTask.UploadStatus.COMPLETED);
|
||||||
|
task.setFinalPath(finalPath);
|
||||||
|
saveTaskMetadata(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完成响应
|
||||||
|
*/
|
||||||
|
private UploadCompleteResponse buildCompleteResponse(UploadTask task, String finalPath, long totalSize) {
|
||||||
|
// todo:URL是逻辑url
|
||||||
|
String fileUrl = minioBucket + "/" + finalPath;
|
||||||
|
|
||||||
|
return UploadCompleteResponse.builder()
|
||||||
|
.filePath(finalPath)
|
||||||
|
.fileUrl(fileUrl)
|
||||||
|
.fileSize(totalSize)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存任务元数据
|
||||||
|
*/
|
||||||
|
private void saveTaskMetadata(UploadTask task) {
|
||||||
|
try {
|
||||||
|
Path metadataPath = Paths.get(tempDir, task.getUploadId(), "metadata.json");
|
||||||
|
String json = objectMapper.writeValueAsString(task);
|
||||||
|
Files.writeString(metadataPath, json);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("保存任务元数据失败: uploadId={}", task.getUploadId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理临时文件
|
||||||
|
*/
|
||||||
|
private void cleanupTempFiles(String uploadId) {
|
||||||
|
try {
|
||||||
|
Path uploadDir = Paths.get(tempDir, uploadId);
|
||||||
|
if (Files.exists(uploadDir)) {
|
||||||
|
Files.walk(uploadDir)
|
||||||
|
.sorted(Comparator.reverseOrder())
|
||||||
|
.forEach(path -> {
|
||||||
|
try {
|
||||||
|
Files.delete(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("删除临时文件失败: {}", path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("清理临时文件失败: uploadId={}", uploadId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期上传任务(每小时执行一次)
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedDelay = 3600000) // 1小时
|
||||||
|
public void cleanupExpiredUploads() {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
uploadTasks.entrySet().removeIf(entry -> {
|
||||||
|
UploadTask task = entry.getValue();
|
||||||
|
if (task.getExpiresAt().isBefore(now)) {
|
||||||
|
cleanupTempFiles(task.getUploadId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 工具方法 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准化邮箱地址
|
||||||
|
*/
|
||||||
|
private String normalizeEmail(String email) {
|
||||||
|
if (email == null) {
|
||||||
|
return "anonymous";
|
||||||
|
}
|
||||||
|
return email.replaceAll("[^a-zA-Z0-9]", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件扩展名
|
||||||
|
*/
|
||||||
|
private String getFileExtension(String fileName) {
|
||||||
|
if (fileName != null && fileName.contains(".")) {
|
||||||
|
return fileName.substring(fileName.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件MIME类型
|
||||||
|
*/
|
||||||
|
private String getContentType(String fileType) {
|
||||||
|
switch (fileType.toLowerCase()) {
|
||||||
|
case "pdf":
|
||||||
|
return "application/pdf";
|
||||||
|
case "video":
|
||||||
|
return "video/mp4";
|
||||||
|
default:
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ spring.security.jwtExpiration=8640000000
|
|||||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**
|
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
|
||||||
spring.security.authApi=/auth/login
|
spring.security.authApi=/auth/login
|
||||||
|
|
||||||
|
|
||||||
@@ -63,6 +63,7 @@ minio.bucketName.gradient=aida-gradient
|
|||||||
minio.bucketName.modifiedSketch=aida-modified-sketch
|
minio.bucketName.modifiedSketch=aida-modified-sketch
|
||||||
minio.bucketName.slogan=aida-slogan
|
minio.bucketName.slogan=aida-slogan
|
||||||
minio.bucketName.partialDesign=aida-partial-design
|
minio.bucketName.partialDesign=aida-partial-design
|
||||||
|
minio.bucketName.globalAward=global-award
|
||||||
redirect_url=http://18.167.251.121:7788
|
redirect_url=http://18.167.251.121:7788
|
||||||
|
|
||||||
spring.rabbitmq.host=18.167.251.121
|
spring.rabbitmq.host=18.167.251.121
|
||||||
@@ -124,9 +125,9 @@ orderList.link=https://develop.aida.com.hk/home/homePage?order=
|
|||||||
# 0 不发送邮件通知 1 发送邮件通知
|
# 0 不发送邮件通知 1 发送邮件通知
|
||||||
stripe.webhook.fail.reminder=0
|
stripe.webhook.fail.reminder=0
|
||||||
# kim test
|
# kim test
|
||||||
stripe.paymentMethodConfiguration=pmc_1LywTWH7nPZ8bkrN6FvdCUWG
|
#stripe.paymentMethodConfiguration=pmc_1LywTWH7nPZ8bkrN6FvdCUWG
|
||||||
# developer test
|
# developer test
|
||||||
#stripe.paymentMethodConfiguration=pmc_1QIKyq02n1TEydyNKVEYvhW7
|
stripe.paymentMethodConfiguration=pmc_1QIKyq02n1TEydyNKVEYvhW7
|
||||||
#thymelea模板配置
|
#thymelea模板配置
|
||||||
#控制 Thymeleaf 是否启用模板缓存 生产环境用true,以提高性能
|
#控制 Thymeleaf 是否启用模板缓存 生产环境用true,以提高性能
|
||||||
spring.thymeleaf.cache=false
|
spring.thymeleaf.cache=false
|
||||||
@@ -158,4 +159,26 @@ google.client.id=157095842121-kdd1fdf8m8nudvj9sprstb2k2prnf9e4.apps.googleuserco
|
|||||||
#google.client.secret=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
|
#google.client.secret=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
|
||||||
google.client.secret=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
|
google.client.secret=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
|
||||||
google.redirect.uri=https://develop.api.aida.com.hk/api/third/party/auth/google_callback
|
google.redirect.uri=https://develop.api.aida.com.hk/api/third/party/auth/google_callback
|
||||||
design.callback.url=https://develop.api.aida.com.hk/api/third/party/receiveDesignResults
|
design.callback.url=https://develop.api.aida.com.hk/api/third/party/receiveDesignResults
|
||||||
|
|
||||||
|
# ===== 分片上传配置 =====
|
||||||
|
|
||||||
|
# 临时文件目录
|
||||||
|
file.upload.temp.dir=temp/uploads
|
||||||
|
|
||||||
|
# 分片大小配置
|
||||||
|
# PDF分片大小:1MB
|
||||||
|
file.upload.chunk.size.pdf=1048576
|
||||||
|
# 视频分片大小:2MB
|
||||||
|
file.upload.chunk.size.video=2097152
|
||||||
|
|
||||||
|
# 文件大小限制
|
||||||
|
# PDF最大文件大小:20MB
|
||||||
|
file.upload.max.size.pdf=20971520
|
||||||
|
# 视频最大文件大小:100MB
|
||||||
|
file.upload.max.size.video=104857600
|
||||||
|
|
||||||
|
# 上传任务过期时间(小时)
|
||||||
|
file.upload.task.expiry.hours=24
|
||||||
|
|
||||||
|
global.award.link=https://aida-global-design-awards.com.hk/contestants?id=
|
||||||
@@ -22,7 +22,7 @@ spring.security.jwtExpiration=604800000
|
|||||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**
|
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
|
||||||
spring.security.authApi=/auth/login
|
spring.security.authApi=/auth/login
|
||||||
|
|
||||||
|
|
||||||
@@ -63,6 +63,7 @@ minio.bucketName.gradient=aida-gradient
|
|||||||
minio.bucketName.modifiedSketch=aida-modified-sketch
|
minio.bucketName.modifiedSketch=aida-modified-sketch
|
||||||
minio.bucketName.slogan=aida-slogan
|
minio.bucketName.slogan=aida-slogan
|
||||||
minio.bucketName.partialDesign=aida-partial-design
|
minio.bucketName.partialDesign=aida-partial-design
|
||||||
|
minio.bucketName.globalAward=global-award
|
||||||
redirect_url=http://18.167.251.121:7788
|
redirect_url=http://18.167.251.121:7788
|
||||||
|
|
||||||
spring.rabbitmq.host=18.167.251.121
|
spring.rabbitmq.host=18.167.251.121
|
||||||
@@ -71,15 +72,15 @@ spring.rabbitmq.username=rabbit
|
|||||||
spring.rabbitmq.password=123456
|
spring.rabbitmq.password=123456
|
||||||
spring.rabbitmq.virtual-host=/
|
spring.rabbitmq.virtual-host=/
|
||||||
|
|
||||||
spring.redis.host=172.31.11.32
|
spring.data.redis.host=172.31.11.32
|
||||||
#spring.redis.host=18.167.251.121
|
#spring.data.redis.host=18.167.251.121
|
||||||
spring.redis.port=6379
|
spring.data.redis.port=6379
|
||||||
spring.redis.database=2
|
spring.data.redis.database=2
|
||||||
spring.redis.password=Aidlab
|
spring.data.redis.password=Aidlab
|
||||||
spring.redis.lettuce.pool.max-active=8
|
spring.data.redis.lettuce.pool.max-active=8
|
||||||
spring.redis.lettuce.pool.max-idle=8
|
spring.data.redis.lettuce.pool.max-idle=8
|
||||||
spring.redis.lettuce.pool.min-idle=0
|
spring.data.redis.lettuce.pool.min-idle=0
|
||||||
spring.redis.lettuce.pool.max-wait=5
|
spring.data.redis.lettuce.pool.max-wait=5
|
||||||
|
|
||||||
redis.key.orderForGenerate=OrderForGenerate
|
redis.key.orderForGenerate=OrderForGenerate
|
||||||
redis.key.generateCancelSet=GenerateCancelSet
|
redis.key.generateCancelSet=GenerateCancelSet
|
||||||
@@ -156,4 +157,26 @@ FREEPIK_API_KEY=FPSX94e5917d376a4facb87dabbaa0319c72
|
|||||||
google.client.id=29310152396-nnsd3h533fld665oguu8ovrt1nukmt46.apps.googleusercontent.com
|
google.client.id=29310152396-nnsd3h533fld665oguu8ovrt1nukmt46.apps.googleusercontent.com
|
||||||
google.client.secret=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
|
google.client.secret=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
|
||||||
google.redirect.uri=https://www.api.aida.com.hk/api/third/party/auth/google_callback
|
google.redirect.uri=https://www.api.aida.com.hk/api/third/party/auth/google_callback
|
||||||
design.callback.url=https://api.aida.com.hk/api/third/party/receiveDesignResults
|
design.callback.url=https://api.aida.com.hk/api/third/party/receiveDesignResults
|
||||||
|
|
||||||
|
# ===== 分片上传配置 =====
|
||||||
|
|
||||||
|
# 临时文件目录
|
||||||
|
file.upload.temp.dir=temp/uploads
|
||||||
|
|
||||||
|
# 分片大小配置
|
||||||
|
# PDF分片大小:1MB
|
||||||
|
file.upload.chunk.size.pdf=1048576
|
||||||
|
# 视频分片大小:2MB
|
||||||
|
file.upload.chunk.size.video=2097152
|
||||||
|
|
||||||
|
# 文件大小限制
|
||||||
|
# PDF最大文件大小:20MB
|
||||||
|
file.upload.max.size.pdf=20971520
|
||||||
|
# 视频最大文件大小:100MB
|
||||||
|
file.upload.max.size.video=104857600
|
||||||
|
|
||||||
|
# 上传任务过期时间(小时)
|
||||||
|
file.upload.task.expiry.hours=24
|
||||||
|
|
||||||
|
global.award.link=https://aida-global-design-awards.com.hk/contestants?id=
|
||||||
@@ -61,10 +61,10 @@
|
|||||||
</if>
|
</if>
|
||||||
<!-- 添加时间区间查询条件 -->
|
<!-- 添加时间区间查询条件 -->
|
||||||
<if test="startTime != null and startTime != ''">
|
<if test="startTime != null and startTime != ''">
|
||||||
AND cd.create_time >= #{startTime}
|
AND create_time >= #{startTime}
|
||||||
</if>
|
</if>
|
||||||
<if test="endTime != null and endTime != ''">
|
<if test="endTime != null and endTime != ''">
|
||||||
AND cd.create_time <= #{endTime}
|
AND create_time <= #{endTime}
|
||||||
</if>
|
</if>
|
||||||
GROUP BY
|
GROUP BY
|
||||||
account_id
|
account_id
|
||||||
|
|||||||
@@ -52,8 +52,10 @@
|
|||||||
AND b.create_date between #{startTime} and #{endTime}
|
AND b.create_date between #{startTime} and #{endTime}
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
and b.create_date not like '%:01'
|
<if test="filterBySecond">
|
||||||
and b.create_date not like '%:02'
|
and b.create_date not like '%:01'
|
||||||
|
and b.create_date not like '%:02'
|
||||||
|
</if>
|
||||||
<if test="ids != null and ids.size() > 0">
|
<if test="ids != null and ids.size() > 0">
|
||||||
and a.id in
|
and a.id in
|
||||||
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
||||||
|
|||||||
@@ -60,7 +60,8 @@
|
|||||||
SELECT system_notification_id
|
SELECT system_notification_id
|
||||||
FROM `t_sys_notification_read_status`
|
FROM `t_sys_notification_read_status`
|
||||||
WHERE account_id = #{accountId}
|
WHERE account_id = #{accountId}
|
||||||
)
|
)
|
||||||
|
AND create_time > #{createTime}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.ai.da.mapper.primary.WorkspaceRelStyleMapper">
|
||||||
|
|
||||||
|
<select id="selectByProjectId" resultType="com.ai.da.mapper.primary.entity.WorkspaceRelStyle">
|
||||||
|
SELECT
|
||||||
|
wrs.workspace_id,
|
||||||
|
wrs.style_id
|
||||||
|
FROM
|
||||||
|
workspace_rel_style wrs
|
||||||
|
INNER JOIN
|
||||||
|
workspace w ON wrs.workspace_id = w.id
|
||||||
|
WHERE
|
||||||
|
w.project_id = #{projectId}
|
||||||
|
AND w.is_deleted = 0
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -208,6 +208,13 @@ page.num.limit=The page number must be greater than 0.
|
|||||||
end.time.must.be.later.than.the.start.time=The subscription end time must be later than the start time.
|
end.time.must.be.later.than.the.start.time=The subscription end time must be later than the start time.
|
||||||
please.specify.the.organizationId=Please specify the organizationId.
|
please.specify.the.organizationId=Please specify the organizationId.
|
||||||
switch.failed.sub-account.not.under.your.active.subscription=Switch failed. Sub-account not under your active subscription.
|
switch.failed.sub-account.not.under.your.active.subscription=Switch failed. Sub-account not under your active subscription.
|
||||||
|
Sub-accounts.cannot.be.admins=Sub-accounts in a subscription cannot be designated as admins.
|
||||||
|
only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified=Only subscription plans with a PENDING status can have their start time modified.
|
||||||
|
the.subscription.end.date.can.be.extended.only.not.reduced=The subscription end date can be extended only, not reduced.
|
||||||
|
total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts=Total sub-account quota cannot be lower than existing sub-accounts.
|
||||||
|
the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used=The credit limit set cannot be lower than the amount of credits already used.
|
||||||
|
administrator.user.is.already.bound.to.different.organization=This administrator user is already bound to a subscription plan of a different organization.
|
||||||
|
required.partialDesign='partialDesign' (base64 or path) is required when updating an individual outfit.
|
||||||
|
|
||||||
# 可能会报异常
|
# 可能会报异常
|
||||||
# Informative:
|
# Informative:
|
||||||
|
|||||||
@@ -204,6 +204,13 @@ page.num.limit=页码必须大于0
|
|||||||
end.time.must.be.later.than.the.start.time=订阅结束时间必须晚于开始时间
|
end.time.must.be.later.than.the.start.time=订阅结束时间必须晚于开始时间
|
||||||
please.specify.the.organizationId=请指定organizationId
|
please.specify.the.organizationId=请指定organizationId
|
||||||
switch.failed.sub-account.not.under.your.active.subscription=切换失败,该子账号不属于您当前管理的订阅计划
|
switch.failed.sub-account.not.under.your.active.subscription=切换失败,该子账号不属于您当前管理的订阅计划
|
||||||
|
Sub-accounts.cannot.be.admins=在订阅中的子账号不能被指定为管理员
|
||||||
|
only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified=只有PENDING状态的订阅计划可以修改订阅开始时间
|
||||||
|
the.subscription.end.date.can.be.extended.only.not.reduced=订阅的到期时间不能缩短,只能延长
|
||||||
|
total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts=设置的子账号总数量不能低于现存已添加的子账号数量
|
||||||
|
the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used=设置的积分上限不能低于已使用的积分量
|
||||||
|
administrator.user.is.already.bound.to.different.organization=该管理员用户已与其他组织的订阅计划绑定
|
||||||
|
required.partialDesign=修改单套搭配必须提供'partialDesign'(base64 或 path)
|
||||||
|
|
||||||
# 可能会报异常
|
# 可能会报异常
|
||||||
# Informative:
|
# Informative:
|
||||||
|
|||||||
@@ -27,20 +27,20 @@ paypal.webhook_id=1D107312EX592781K
|
|||||||
##### Stripe
|
##### Stripe
|
||||||
|
|
||||||
# developer
|
# developer
|
||||||
#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
|
stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
|
||||||
# dev 端点
|
# dev 端点
|
||||||
#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
|
stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
|
||||||
# local 端点
|
# local 端点
|
||||||
#stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm
|
#stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm
|
||||||
|
|
||||||
# kim - test
|
# kim - test
|
||||||
stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0
|
#stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0
|
||||||
# prod 端点
|
# prod 端点
|
||||||
#stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u
|
#stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u
|
||||||
# local 端点
|
# local 端点
|
||||||
#stripe.webhook-sign-secret=whsec_NvwM3hDQiN5GXclYOYekE9IKHLjmROF8
|
#stripe.webhook-sign-secret=whsec_NvwM3hDQiN5GXclYOYekE9IKHLjmROF8
|
||||||
# dev 端点
|
# dev 端点
|
||||||
stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL
|
#stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL
|
||||||
|
|
||||||
# kim - live
|
# kim - live
|
||||||
#stripe.private-key=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
|
#stripe.private-key=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
|
||||||
|
|||||||
Reference in New Issue
Block a user