Compare commits
140 Commits
08b26872ff
...
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 | |||
| 72ad977dcb |
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -59,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
|
||||||
|
|||||||
@@ -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 * * ?")
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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是否存在,不存在则创建
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -125,11 +125,11 @@ public class PythonController {
|
|||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@Operation(summary = "Seg Anything 转发接口")
|
@Operation(summary = "Seg Anything 转发接口")
|
||||||
@PostMapping("/segAnything")
|
@PostMapping("/segAnything")
|
||||||
public Response<JSONObject> segAnything(@RequestBody Map<String, Object> payload) {
|
public Response<String> segAnything(@RequestBody Map<String, Object> payload) {
|
||||||
// 将前端传来的 Map 转为 fastjson JSONObject 并转发给 python 服务
|
// 将前端传来的 Map 转为 fastjson JSONObject 并转发给 python 服务
|
||||||
JSONObject requestJson = (JSONObject) JSON.toJSON(payload);
|
JSONObject requestJson = (JSONObject) JSON.toJSON(payload);
|
||||||
JSONObject result = pythonService.segAnything(requestJson);
|
String url = pythonService.segAnything(requestJson);
|
||||||
return Response.success(result);
|
return Response.success(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -73,9 +74,11 @@ public class DesignSingleItemDTO implements Serializable {
|
|||||||
@Schema(description = "45")
|
@Schema(description = "45")
|
||||||
private double rotate;
|
private double rotate;
|
||||||
|
|
||||||
|
@Hidden
|
||||||
@Schema(description = "带overall印花未分割图片")
|
@Schema(description = "带overall印花未分割图片")
|
||||||
private String undividedLayerBase64;
|
private String undividedLayerBase64;
|
||||||
|
|
||||||
|
@Hidden
|
||||||
@Schema(description = "带overall/single印花未分割图片")
|
@Schema(description = "带overall/single印花未分割图片")
|
||||||
private String undividedLayerWithSinglePrintBase64;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package com.ai.da.python;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||||
|
import com.ai.da.common.RabbitMQ.RabbitMQProperties;
|
||||||
import com.ai.da.common.config.FileProperties;
|
import com.ai.da.common.config.FileProperties;
|
||||||
import com.ai.da.common.config.exception.BusinessException;
|
import com.ai.da.common.config.exception.BusinessException;
|
||||||
|
import com.ai.da.common.constant.CommonConstant;
|
||||||
import com.ai.da.common.context.UserContext;
|
import com.ai.da.common.context.UserContext;
|
||||||
import com.ai.da.common.enums.*;
|
import com.ai.da.common.enums.*;
|
||||||
import com.ai.da.common.utils.*;
|
import com.ai.da.common.utils.*;
|
||||||
@@ -20,6 +22,7 @@ import com.ai.da.model.vo.*;
|
|||||||
import com.ai.da.python.vo.*;
|
import com.ai.da.python.vo.*;
|
||||||
import com.ai.da.service.DesignHistoryService;
|
import com.ai.da.service.DesignHistoryService;
|
||||||
import com.ai.da.service.PythonTAllInfoService;
|
import com.ai.da.service.PythonTAllInfoService;
|
||||||
|
import com.ai.da.service.RabbitMQService;
|
||||||
import com.ai.da.service.SysFileService;
|
import com.ai.da.service.SysFileService;
|
||||||
import com.alibaba.fastjson.*;
|
import com.alibaba.fastjson.*;
|
||||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||||
@@ -39,6 +42,7 @@ import org.springframework.util.StringUtils;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -68,6 +72,8 @@ public class PythonService {
|
|||||||
private String accessPythonPort;
|
private String accessPythonPort;
|
||||||
@Value("${minio.bucketName.gradient}")
|
@Value("${minio.bucketName.gradient}")
|
||||||
private String gradientBucketName;
|
private String gradientBucketName;
|
||||||
|
@Value("${minio.bucketName.users}")
|
||||||
|
private String userBucketName;
|
||||||
@Value("${access.python.generate_sr_port}")
|
@Value("${access.python.generate_sr_port}")
|
||||||
private String srServicePort;
|
private String srServicePort;
|
||||||
|
|
||||||
@@ -83,6 +89,12 @@ public class PythonService {
|
|||||||
@Resource
|
@Resource
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RabbitMQService rabbitMQService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RabbitMQProperties rabbitMQProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成打印的图片 二合一 (废弃于2024/01/02)
|
* 生成打印的图片 二合一 (废弃于2024/01/02)
|
||||||
*
|
*
|
||||||
@@ -279,7 +291,7 @@ public class PythonService {
|
|||||||
|
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
elementVO.setHasUseMd5List(beforeAssemblyHasUseMd5List);
|
elementVO.setHasUseMd5List(beforeAssemblyHasUseMd5List);
|
||||||
i --;
|
i--;
|
||||||
|
|
||||||
switch (designPrintPictureType) {
|
switch (designPrintPictureType) {
|
||||||
case PIN:
|
case PIN:
|
||||||
@@ -406,13 +418,14 @@ public class PythonService {
|
|||||||
// if (CollectionUtil.isEmpty(pinData)) {
|
// if (CollectionUtil.isEmpty(pinData)) {
|
||||||
// return 0;
|
// return 0;
|
||||||
// }
|
// }
|
||||||
//// long topNum = sketchBoardElements.stream()
|
|
||||||
//// .filter(skecth -> skecth.getHasPin() == 1
|
/// / long topNum = sketchBoardElements.stream()
|
||||||
//// && DesignPythonItem.OUTWEAR_DRESS_BLOUSE.contains(skecth.getLevel2Type())).count();
|
/// / .filter(skecth -> skecth.getHasPin() == 1
|
||||||
//// long bottomNum = sketchBoardElements.stream()
|
/// / && DesignPythonItem.OUTWEAR_DRESS_BLOUSE.contains(skecth.getLevel2Type())).count();
|
||||||
//// .filter(skecth -> skecth.getHasPin() == 1
|
/// / long bottomNum = sketchBoardElements.stream()
|
||||||
//// && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count();
|
/// / .filter(skecth -> skecth.getHasPin() == 1
|
||||||
//// int num = Arrays.asList(topNum, bottomNum).stream().max(Comparator.comparing(Long::valueOf)).get().intValue();
|
/// / && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count();
|
||||||
|
/// / int num = Arrays.asList(topNum, bottomNum).stream().max(Comparator.comparing(Long::valueOf)).get().intValue();
|
||||||
// int num = pinData.size();
|
// int num = pinData.size();
|
||||||
// return Math.min(num, 8);
|
// return Math.min(num, 8);
|
||||||
// }
|
// }
|
||||||
@@ -560,12 +573,12 @@ public class PythonService {
|
|||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
long pinNum = printBoardElements.stream().filter(f -> f.getHasPin() == 1).count();
|
long pinNum = printBoardElements.stream().filter(f -> f.getHasPin() == 1).count();
|
||||||
if (designNum - pinNum < 0){
|
if (designNum - pinNum < 0) {
|
||||||
return RandomsUtil.randomSysFile(0L, (long) (pinNum/2 + 1));
|
return RandomsUtil.randomSysFile(0L, (long) (pinNum / 2 + 1));
|
||||||
} else if (designNum - pinNum < designNum/2) {
|
} else if (designNum - pinNum < designNum / 2) {
|
||||||
return RandomsUtil.randomSysFile(0L, designNum - pinNum + 1);
|
return RandomsUtil.randomSysFile(0L, designNum - pinNum + 1);
|
||||||
} else {
|
} else {
|
||||||
return RandomsUtil.randomSysFile(0L, (long) (designNum/2 + 1));
|
return RandomsUtil.randomSysFile(0L, (long) (designNum / 2 + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -680,8 +693,7 @@ public class PythonService {
|
|||||||
|
|
||||||
// 其他所有情况,都回退到使用系统推荐
|
// 其他所有情况,都回退到使用系统推荐
|
||||||
String categoryParam = elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase();
|
String categoryParam = elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase();
|
||||||
List<String> recommentdUrlList = getSystemSketchByCategory(categoryParam, elementVO.getBrandId(), elementVO.getBrandScale(),elementVO.getStyle());
|
List<String> recommentdUrlList = getSystemSketchByCategory(categoryParam, elementVO.getBrandId(), elementVO.getBrandScale(), elementVO.getStyle());
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(recommentdUrlList)) {
|
if (CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1051,7 @@ public class PythonService {
|
|||||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||||
}else {
|
} else {
|
||||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||||
}
|
}
|
||||||
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
|
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
|
||||||
@@ -1062,7 +1074,7 @@ public class PythonService {
|
|||||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||||
}else {
|
} else {
|
||||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1098,7 +1110,7 @@ public class PythonService {
|
|||||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||||
}else {
|
} else {
|
||||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||||
}
|
}
|
||||||
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
|
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
|
||||||
@@ -1122,7 +1134,7 @@ public class PythonService {
|
|||||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||||
}else {
|
} else {
|
||||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||||
}
|
}
|
||||||
// String tableName = getTableName(validateElementVO.getModelSex(), category);
|
// String tableName = getTableName(validateElementVO.getModelSex(), category);
|
||||||
@@ -2190,7 +2202,7 @@ public class PythonService {
|
|||||||
basic.setScale_earrings(0.16);
|
basic.setScale_earrings(0.16);
|
||||||
if (Objects.nonNull(designLibraryModelPointVO)) {
|
if (Objects.nonNull(designLibraryModelPointVO)) {
|
||||||
basic.setBody_point_test(getMap(designLibraryModelPointVO));
|
basic.setBody_point_test(getMap(designLibraryModelPointVO));
|
||||||
}else {
|
} else {
|
||||||
basic.setBody_point_test(getMap(designLibraryModelPoint));
|
basic.setBody_point_test(getMap(designLibraryModelPoint));
|
||||||
}
|
}
|
||||||
return basic;
|
return basic;
|
||||||
@@ -2851,13 +2863,17 @@ public class PythonService {
|
|||||||
gradientString = JSONObject.toJSONString(designSingleItem.getGradient());
|
gradientString = JSONObject.toJSONString(designSingleItem.getGradient());
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintToPython printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
|
PrintToPython printToPython;
|
||||||
|
printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
|
||||||
designSingleItem.getPartialDesign().getPartialDesignMinioPath());
|
designSingleItem.getPartialDesign().getPartialDesignMinioPath());
|
||||||
resolveDesignElement(designSingleItem.getTrims(), printToPython);
|
/*PrintToPython printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
|
||||||
log.info("组装参数【服装:{}的maskUrl: {}】",designSingleItem.getType(), designSingleItem.getMaskUrl());
|
designSingleItem.getPartialDesign().getPartialDesignMinioPath());*/
|
||||||
|
// resolveDesignElement(designSingleItem.getTrims(), printToPython);
|
||||||
|
log.info("组装参数【服装:{}的maskUrl: {}】", designSingleItem.getType(), designSingleItem.getMaskUrl());
|
||||||
|
|
||||||
String mergeImagePath = !StringUtil.isNullOrEmpty(printToPython.getPartial())
|
String partialDesign = designSingleItem.getPartialDesign().getPartialDesignMinioPath();
|
||||||
? printToPython.getPartial() : designSingleItem.getPath();
|
String mergeImagePath = !StringUtil.isNullOrEmpty(partialDesign)
|
||||||
|
? partialDesign : designSingleItem.getPath();
|
||||||
response.add(new DesignPythonItem(
|
response.add(new DesignPythonItem(
|
||||||
designSingleItem.getType(),
|
designSingleItem.getType(),
|
||||||
designSingleItem.getPath(),
|
designSingleItem.getPath(),
|
||||||
@@ -2880,7 +2896,7 @@ public class PythonService {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (singleOverall.equals("overall")){
|
if (singleOverall.equals("overall")) {
|
||||||
String bodyPath;
|
String bodyPath;
|
||||||
if (Objects.nonNull(designLibraryModelPoint)) {
|
if (Objects.nonNull(designLibraryModelPoint)) {
|
||||||
bodyPath = designLibraryModelPoint.getTemplateUrl();
|
bodyPath = designLibraryModelPoint.getTemplateUrl();
|
||||||
@@ -2896,12 +2912,12 @@ public class PythonService {
|
|||||||
|
|
||||||
private PrintToPython resolveDesignSinglePrint(List<DesignSinglePrint> printObject, String partialDesign) {
|
private PrintToPython resolveDesignSinglePrint(List<DesignSinglePrint> printObject, String partialDesign) {
|
||||||
PrintToPython printToPython = new PrintToPython();
|
PrintToPython printToPython = new PrintToPython();
|
||||||
DesignPythonItemPrint printSingle = new DesignPythonItemPrint();
|
// DesignPythonItemPrint printSingle = new DesignPythonItemPrint();
|
||||||
DesignPythonItemPrint printOverall = new DesignPythonItemPrint();
|
DesignPythonItemPrint printOverall = new DesignPythonItemPrint();
|
||||||
printToPython.setSingle(printSingle);
|
// printToPython.setSingle(printSingle);
|
||||||
printToPython.setOverall(printOverall);
|
printToPython.setOverall(printOverall);
|
||||||
printToPython.setPartial(StringUtil.isNullOrEmpty(partialDesign) ? null : partialDesign);
|
printToPython.setPartial(StringUtil.isNullOrEmpty(partialDesign) ? null : partialDesign);
|
||||||
if (Objects.isNull(printObject) || printObject.isEmpty()){
|
if (Objects.isNull(printObject) || printObject.isEmpty()) {
|
||||||
return printToPython;
|
return printToPython;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2932,12 +2948,12 @@ public class PythonService {
|
|||||||
Integer priority = p.getPriority();
|
Integer priority = p.getPriority();
|
||||||
setUriToMinioPath(p);
|
setUriToMinioPath(p);
|
||||||
// todo 下标越界问题
|
// todo 下标越界问题
|
||||||
if (p.getIfSingle()){
|
if (p.getIfSingle()) {
|
||||||
locationS.set(priority - 1, p.getLocation());
|
locationS.set(priority - 1, p.getLocation());
|
||||||
scaleS.set(priority - 1, p.getScale());
|
scaleS.set(priority - 1, p.getScale());
|
||||||
angleS.set( priority - 1, p.getAngle());
|
angleS.set(priority - 1, p.getAngle());
|
||||||
pathsS.set(priority - 1, p.getMinIOPath());
|
pathsS.set(priority - 1, p.getMinIOPath());
|
||||||
}else {
|
} else {
|
||||||
locationO.set(priority - 1, p.getLocation());
|
locationO.set(priority - 1, p.getLocation());
|
||||||
scaleO.set(priority - 1, p.getScale());
|
scaleO.set(priority - 1, p.getScale());
|
||||||
angleO.set(priority - 1, p.getAngle());
|
angleO.set(priority - 1, p.getAngle());
|
||||||
@@ -2945,14 +2961,14 @@ public class PythonService {
|
|||||||
}
|
}
|
||||||
// log.info("本次print打点locations###{}###fileVO{}", p.getLocation(), JSON.toJSONString(fileVO));
|
// log.info("本次print打点locations###{}###fileVO{}", p.getLocation(), JSON.toJSONString(fileVO));
|
||||||
});
|
});
|
||||||
locationS.removeAll(Collections.singleton(null));
|
/*locationS.removeAll(Collections.singleton(null));
|
||||||
scaleS.removeAll(Collections.singleton(null));
|
scaleS.removeAll(Collections.singleton(null));
|
||||||
angleS.removeAll(Collections.singleton(null));
|
angleS.removeAll(Collections.singleton(null));
|
||||||
pathsS.removeAll(Collections.singleton(null));
|
pathsS.removeAll(Collections.singleton(null));
|
||||||
printSingle.setLocation(locationS);
|
printSingle.setLocation(locationS);
|
||||||
printSingle.setPrint_scale_list(scaleS);
|
printSingle.setPrint_scale_list(scaleS);
|
||||||
printSingle.setPrint_angle_list(angleS);
|
printSingle.setPrint_angle_list(angleS);
|
||||||
printSingle.setPrint_path_list(pathsS);
|
printSingle.setPrint_path_list(pathsS);*/
|
||||||
|
|
||||||
locationO.removeAll(Collections.singleton(null));
|
locationO.removeAll(Collections.singleton(null));
|
||||||
scaleO.removeAll(Collections.singleton(null));
|
scaleO.removeAll(Collections.singleton(null));
|
||||||
@@ -2967,9 +2983,9 @@ public class PythonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 对印花类型为Generate的图片路径进行特殊处理
|
// 对印花类型为Generate的图片路径进行特殊处理
|
||||||
private void setUriToMinioPath(DesignSinglePrint print){
|
private void setUriToMinioPath(DesignSinglePrint print) {
|
||||||
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")){
|
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")) {
|
||||||
if (!StringUtil.isNullOrEmpty(print.getPath())){
|
if (!StringUtil.isNullOrEmpty(print.getPath())) {
|
||||||
try {
|
try {
|
||||||
URI uri = new URI(print.getPath());
|
URI uri = new URI(print.getPath());
|
||||||
String path = uri.getPath(); // 获取路径部分: /aida-users/87/print/9ac32f65-6043-424d-a146-92c9c6d204ee-4-87.png
|
String path = uri.getPath(); // 获取路径部分: /aida-users/87/print/9ac32f65-6043-424d-a146-92c9c6d204ee-4-87.png
|
||||||
@@ -3329,7 +3345,7 @@ public class PythonService {
|
|||||||
throw new BusinessException("system error!");
|
throw new BusinessException("system error!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean generateSketchOrPrint(String params, String port, String servicePath) {
|
public Boolean generateSketchOrPrint(String params, String port, String servicePath, String taskId) {
|
||||||
//限流校验
|
//限流校验
|
||||||
// AccessLimitUtils.validate("generateSketchOrPrint", 5);
|
// AccessLimitUtils.validate("generateSketchOrPrint", 5);
|
||||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||||
@@ -3391,12 +3407,36 @@ public class PythonService {
|
|||||||
if (result && jsonObject.get("code").equals(200)) {
|
if (result && jsonObject.get("code").equals(200)) {
|
||||||
log.info("Generate##responseObject###{}", jsonObject);
|
log.info("Generate##responseObject###{}", jsonObject);
|
||||||
// return setGenerateImageList(jsonObject.getJSONObject("data"));
|
// return setGenerateImageList(jsonObject.getJSONObject("data"));
|
||||||
|
if (servicePath == CommonConstant.GENERATE_PATH_FLUX2_KLEIN) {
|
||||||
|
//放入结果到mq
|
||||||
|
JSONObject data = jsonObject.getJSONObject("data");
|
||||||
|
String outputPath = data.getString("output_path");
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, String> mqMessage = new HashMap<>();
|
||||||
|
mqMessage.put("tasks_id", taskId);
|
||||||
|
mqMessage.put("status", "SUCCESS");
|
||||||
|
mqMessage.put("message", "success");
|
||||||
|
mqMessage.put("image_url", outputPath);
|
||||||
|
mqMessage.put("category", "");
|
||||||
|
String mqMessageBody = JSON.toJSONString(mqMessage);
|
||||||
|
rabbitMQService.publishMessageToGenerateResult(mqMessageBody);
|
||||||
|
}
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
} else {
|
} else {
|
||||||
log.info("generateSketchOrPrintPrint失败###{}", jsonObject);
|
log.info("generateSketchOrPrintPrint失败###{}", jsonObject);
|
||||||
log.info("Generate Exception! Code : " + jsonObject.get("code"));
|
log.info("Generate Exception! Code : " + jsonObject.get("code"));
|
||||||
|
Map<String, String> mqMessage = new HashMap<>();
|
||||||
|
mqMessage.put("tasks_id", taskId);
|
||||||
|
mqMessage.put("status", "ERROR");
|
||||||
|
mqMessage.put("message", "");
|
||||||
|
mqMessage.put("image_url", "");
|
||||||
|
mqMessage.put("category", "");
|
||||||
|
String mqMessageBody = JSON.toJSONString(mqMessage);
|
||||||
|
rabbitMQService.publishMessageToGenerateResult(mqMessageBody);
|
||||||
return Boolean.FALSE;
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response sendPostToModel(String content, String portAndRoute, String functionName) {
|
public Response sendPostToModel(String content, String portAndRoute, String functionName) {
|
||||||
@@ -3438,7 +3478,10 @@ public class PythonService {
|
|||||||
|
|
||||||
return imageUrlList;
|
return imageUrlList;
|
||||||
}*/
|
}*/
|
||||||
/** 废弃状态 */
|
|
||||||
|
/**
|
||||||
|
* 废弃状态
|
||||||
|
*/
|
||||||
public String composeLayers(List<OutfitDetailPythonItem> layersDetail) {
|
public String composeLayers(List<OutfitDetailPythonItem> layersDetail) {
|
||||||
HashMap<String, List<OutfitDetailPythonItem>> layers = new HashMap<>();
|
HashMap<String, List<OutfitDetailPythonItem>> layers = new HashMap<>();
|
||||||
HashMap<String, HashMap<String, List<OutfitDetailPythonItem>>> content = new HashMap<>();
|
HashMap<String, HashMap<String, List<OutfitDetailPythonItem>>> content = new HashMap<>();
|
||||||
@@ -3749,7 +3792,7 @@ public class PythonService {
|
|||||||
throw new BusinessException("relightImage.interface.exception");
|
throw new BusinessException("relightImage.interface.exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String imageToSketch(String imagePath, String bucket, String objectName, String styleCode, String styleImageUrl){
|
public String imageToSketch(String imagePath, String bucket, String objectName, String styleCode, String styleImageUrl) {
|
||||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
|
.pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
|
||||||
@@ -3796,7 +3839,7 @@ public class PythonService {
|
|||||||
String sketchResult = jsonObject.get("data").toString();
|
String sketchResult = jsonObject.get("data").toString();
|
||||||
log.info("ImageToSketch 结果 : {}", sketchResult);
|
log.info("ImageToSketch 结果 : {}", sketchResult);
|
||||||
return sketchResult;
|
return sketchResult;
|
||||||
}else {
|
} else {
|
||||||
log.info("ImageToSketch 失败。 Response code {}", responseCode);
|
log.info("ImageToSketch 失败。 Response code {}", responseCode);
|
||||||
throw new BusinessException("ImageToSketch 失败。 Response code " + responseCode);
|
throw new BusinessException("ImageToSketch 失败。 Response code " + responseCode);
|
||||||
}
|
}
|
||||||
@@ -3849,7 +3892,7 @@ public class PythonService {
|
|||||||
throw new BusinessException("bright.interface.exception");
|
throw new BusinessException("bright.interface.exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject attributeRecognition(List<String> pictureUrls,List<String> ids, List<String> category) {
|
public JSONObject attributeRecognition(List<String> pictureUrls, List<String> ids, List<String> category) {
|
||||||
//限流校验
|
//限流校验
|
||||||
AccessLimitUtils.validate("attributeRecognition", 20);
|
AccessLimitUtils.validate("attributeRecognition", 20);
|
||||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||||
@@ -3881,7 +3924,7 @@ public class PythonService {
|
|||||||
} catch (IOException ioException) {
|
} catch (IOException ioException) {
|
||||||
log.error("PythonService###attributeRecognition异常##{}", ExceptionUtil.getThrowableList(ioException));
|
log.error("PythonService###attributeRecognition异常##{}", ExceptionUtil.getThrowableList(ioException));
|
||||||
}
|
}
|
||||||
log.info("识别python对应的属性标签值结果###{}",bodyStr.trim());
|
log.info("识别python对应的属性标签值结果###{}", bodyStr.trim());
|
||||||
//去除限流
|
//去除限流
|
||||||
AccessLimitUtils.validateOut("attributeRecognition");
|
AccessLimitUtils.validateOut("attributeRecognition");
|
||||||
if (Objects.isNull(response)) {
|
if (Objects.isNull(response)) {
|
||||||
@@ -3965,7 +4008,7 @@ public class PythonService {
|
|||||||
throw new BusinessException("design.interface.exception");
|
throw new BusinessException("design.interface.exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getSystemSketchByCategory(String category, Long brandId, Double brandScale,String style) {
|
public List<String> getSystemSketchByCategory(String category, Long brandId, Double brandScale, String style) {
|
||||||
//******3.1.2版本临时使用java推荐方案去解决style未使用的问题**********
|
//******3.1.2版本临时使用java推荐方案去解决style未使用的问题**********
|
||||||
// try {
|
// try {
|
||||||
// //使用新库attribute_retrieval_style,表命名修改为elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase()比如female_skirt,与传入的category保持一致
|
// //使用新库attribute_retrieval_style,表命名修改为elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase()比如female_skirt,与传入的category保持一致
|
||||||
@@ -3989,6 +4032,14 @@ public class PythonService {
|
|||||||
// throw new BusinessException("system.error");
|
// throw new BusinessException("system.error");
|
||||||
// }
|
// }
|
||||||
//**********************end***********************************
|
//**********************end***********************************
|
||||||
|
//临时补丁
|
||||||
|
if (category != null && style != null) {
|
||||||
|
String categoryPrefix = category.split("_")[0];
|
||||||
|
if ("male".equals(categoryPrefix) &&
|
||||||
|
(style.equalsIgnoreCase("lolita") || style.equalsIgnoreCase("romantic"))) {
|
||||||
|
style = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
||||||
|
|
||||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||||
@@ -4110,11 +4161,12 @@ public class PythonService {
|
|||||||
//生成失败
|
//生成失败
|
||||||
throw new BusinessException("segProduct.interface.exception");
|
throw new BusinessException("segProduct.interface.exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转发 seg_anything 请求到 python 服务
|
* 转发 seg_anything 请求到 python 服务
|
||||||
* 请求 body 由调用方组装(包含 user_id, image_path, type, points, labels, box 等)
|
* 请求 body 由调用方组装(包含 user_id, image_path, type, points, labels, box 等)
|
||||||
*/
|
*/
|
||||||
public JSONObject segAnything(JSONObject content) {
|
public String segAnything(JSONObject content) {
|
||||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.pingInterval(5, TimeUnit.SECONDS)
|
.pingInterval(5, TimeUnit.SECONDS)
|
||||||
@@ -4122,6 +4174,9 @@ public class PythonService {
|
|||||||
.writeTimeout(60, TimeUnit.SECONDS)
|
.writeTimeout(60, TimeUnit.SECONDS)
|
||||||
.build();
|
.build();
|
||||||
MediaType mediaType = MediaType.parse("application/json");
|
MediaType mediaType = MediaType.parse("application/json");
|
||||||
|
content.put("bucket", userBucketName);
|
||||||
|
content.put("object_name", content.get("user_id") + "/" + "segment" + "/" + UUID.randomUUID() + ".png");
|
||||||
|
content.remove("user_id");
|
||||||
RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
|
RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
|
||||||
|
|
||||||
String url = accessPythonIp + ":" + accessPythonPort + "/api/seg_anything";
|
String url = accessPythonIp + ":" + accessPythonPort + "/api/seg_anything";
|
||||||
@@ -4147,7 +4202,30 @@ public class PythonService {
|
|||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
JSONObject responseObject = JSON.parseObject(responseBody);
|
JSONObject responseObject = JSON.parseObject(responseBody);
|
||||||
log.info("PythonService##segAnything response###{}", responseObject);
|
log.info("PythonService##segAnything response###{}", responseObject);
|
||||||
return responseObject;
|
// Extract nested output path and convert to presigned MinIO URL for frontend, return as String
|
||||||
|
try {
|
||||||
|
if (responseObject != null) {
|
||||||
|
JSONObject dataObj = responseObject.getJSONObject("data");
|
||||||
|
if (dataObj != null) {
|
||||||
|
String output = dataObj.getString("output");
|
||||||
|
if (!StringUtil.isNullOrEmpty(output)) {
|
||||||
|
try {
|
||||||
|
String presigned = minioUtil.getPreSignedUrl(output, 24 * 60);
|
||||||
|
return presigned;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to generate presigned url for {}: {}", output, e.getMessage());
|
||||||
|
throw new BusinessException("segAnything.presign.failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("PythonService##segAnything post-process error###{}", ex.getMessage());
|
||||||
|
throw new BusinessException("segAnything.interface.exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BusinessException("segAnything.missing.output");
|
||||||
}
|
}
|
||||||
throw new BusinessException("segAnything.interface.exception");
|
throw new BusinessException("segAnything.interface.exception");
|
||||||
} catch (IOException | JSONException e) {
|
} catch (IOException | JSONException e) {
|
||||||
@@ -4158,6 +4236,7 @@ public class PythonService {
|
|||||||
log.error("PythonService##segAnything异常response###{}", response);
|
log.error("PythonService##segAnything异常response###{}", response);
|
||||||
throw new BusinessException("segAnything.interface.exception");
|
throw new BusinessException("segAnything.interface.exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean poseTransformation(JSONObject content, String apiUri) {
|
public Boolean poseTransformation(JSONObject content, String apiUri) {
|
||||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
@@ -4261,7 +4340,7 @@ public class PythonService {
|
|||||||
String modifiedModel = jsonObject.get("data").toString();
|
String modifiedModel = jsonObject.get("data").toString();
|
||||||
log.info("modifyModelProportion 结果 : {}", modifiedModel);
|
log.info("modifyModelProportion 结果 : {}", modifiedModel);
|
||||||
return modifiedModel;
|
return modifiedModel;
|
||||||
}else {
|
} else {
|
||||||
log.info("modifyModelProportion 失败。 Response code {}", responseCode);
|
log.info("modifyModelProportion 失败。 Response code {}", responseCode);
|
||||||
throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode);
|
throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode);
|
||||||
}
|
}
|
||||||
@@ -4286,7 +4365,7 @@ public class PythonService {
|
|||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
map.put("prompt", prompt);
|
map.put("prompt", prompt);
|
||||||
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
||||||
map.put("user_id", userHolder.getId());
|
map.put("user_id", userHolder.getId().toString());
|
||||||
log.info("brandDNAGenerate请求python 参数:####{}", map);
|
log.info("brandDNAGenerate请求python 参数:####{}", map);
|
||||||
String param = JSON.toJSONString(map, SerializerFeature.WriteNullStringAsEmpty);
|
String param = JSON.toJSONString(map, SerializerFeature.WriteNullStringAsEmpty);
|
||||||
log.info(param);
|
log.info(param);
|
||||||
@@ -4364,10 +4443,11 @@ public class PythonService {
|
|||||||
log.info("imageSegmentation 结果 : {}", jsonObject.get("data").toString());
|
log.info("imageSegmentation 结果 : {}", jsonObject.get("data").toString());
|
||||||
List<ImageSegmentation.ImageDate> seg = JSONObject.parseObject(
|
List<ImageSegmentation.ImageDate> seg = JSONObject.parseObject(
|
||||||
jsonObject.get("data").toString(),
|
jsonObject.get("data").toString(),
|
||||||
new TypeReference<List<ImageSegmentation.ImageDate>>() {}
|
new TypeReference<List<ImageSegmentation.ImageDate>>() {
|
||||||
|
}
|
||||||
);
|
);
|
||||||
return seg;
|
return seg;
|
||||||
}else {
|
} else {
|
||||||
log.info("imageSegmentation 失败。 Response code {}", responseCode);
|
log.info("imageSegmentation 失败。 Response code {}", responseCode);
|
||||||
throw new BusinessException("imageSegmentation 失败。 Response code " + responseCode);
|
throw new BusinessException("imageSegmentation 失败。 Response code " + responseCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO);
|
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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -240,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());
|
||||||
// 设置头像
|
// 设置头像
|
||||||
@@ -279,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;
|
||||||
@@ -297,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());
|
||||||
}
|
}
|
||||||
@@ -1949,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());
|
||||||
}
|
}
|
||||||
@@ -2507,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -857,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,7 +363,7 @@ 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, String designType) {
|
, Long modelId, String modelType, boolean isSingleCollectionFlag, String designType) {
|
||||||
|
|
||||||
@@ -424,6 +424,7 @@ 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")) {
|
||||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0))) {
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0))) {
|
||||||
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).getFirst());
|
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).getFirst());
|
||||||
@@ -431,7 +432,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1))) {
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1))) {
|
||||||
designItemDetail.setUndividedLayerWithSinglePrint(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());
|
||||||
@@ -439,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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 逻辑删除未复用的旧记录
|
// 逻辑删除未复用的旧记录
|
||||||
@@ -485,13 +497,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
|
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图层与mask对应关系,衣服前后片的mask是相同的
|
// 创建 priority 到 DesignSingleItemDTO 的映射,用于获取 transpose 和 rotate
|
||||||
Map<Integer, String> priorityMask = null;
|
Map<Integer, DesignSingleItemDTO> priorityToItemDTOMap = designSingleItemDTOList.stream()
|
||||||
// 如果是merge模式 则使用入参中的mask
|
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, dto -> dto, (old, newVal) -> old));
|
||||||
if (designType.equals("merge")) {
|
|
||||||
priorityMask = designSingleItemDTOList.stream()
|
|
||||||
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getMaskMinioUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
||||||
@@ -514,20 +522,19 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
designPythonOutfitDetail.setImageUrl(jsonObject.getString("image_url"));
|
designPythonOutfitDetail.setImageUrl(jsonObject.getString("image_url"));
|
||||||
designPythonOutfitDetail.setImageSize(jsonObject.getString("image_size"));
|
designPythonOutfitDetail.setImageSize(jsonObject.getString("image_size"));
|
||||||
designPythonOutfitDetail.setImageCategory(jsonObject.getString("image_category"));
|
designPythonOutfitDetail.setImageCategory(jsonObject.getString("image_category"));
|
||||||
if (designType.equals("merge")) {
|
designPythonOutfitDetail.setMaskUrl(jsonObject.getString("mask_url"));
|
||||||
designPythonOutfitDetail.setMaskUrl(priorityMask.get(Math.abs(priority)));
|
|
||||||
} else {
|
|
||||||
designPythonOutfitDetail.setMaskUrl(jsonObject.getString("mask_url"));
|
|
||||||
}
|
|
||||||
|
|
||||||
designPythonOutfitDetail.setScale(Objects.isNull(jsonObject.getString("resize_scale")) ? "1.0" : jsonObject.getString("resize_scale"));
|
designPythonOutfitDetail.setScale(Objects.isNull(jsonObject.getString("resize_scale")) ? "1.0" : jsonObject.getString("resize_scale"));
|
||||||
designPythonOutfitDetail.setUserId(userId);
|
designPythonOutfitDetail.setUserId(userId);
|
||||||
if (priorityOffset != null && !priorityOffset.isEmpty()){
|
if (priorityOffset != null && !priorityOffset.isEmpty()){
|
||||||
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
|
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
|
||||||
}
|
}
|
||||||
designPythonOutfitDetail.setPriority(priority);
|
designPythonOutfitDetail.setPriority(priority);
|
||||||
designPythonOutfitDetail.setTranspose(jsonObject.getString("transpose"));
|
// 从前端传入的 DesignSingleItemDTO 中获取 transpose 和 rotate,不再从 Python 返回的数据获取
|
||||||
designPythonOutfitDetail.setRotate(jsonObject.getDouble("rotate"));
|
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);
|
||||||
}
|
}
|
||||||
@@ -702,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 -> {
|
||||||
@@ -726,7 +736,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
i.getPartialDesign().setPartialDesignBase64(null);
|
i.getPartialDesign().setPartialDesignBase64(null);
|
||||||
}
|
}
|
||||||
// undividedLayerBase64 undividedLayerWithSinglePrintBase64 置空
|
// undividedLayerBase64 undividedLayerWithSinglePrintBase64 置空
|
||||||
// 前端合成的未分割的图
|
/*// 前端合成的未分割的图
|
||||||
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerBase64())) {
|
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerBase64())) {
|
||||||
log.info("set UndividedLayerBase64为空,便于日志打印");
|
log.info("set UndividedLayerBase64为空,便于日志打印");
|
||||||
i.setUndividedLayerBase64(null);
|
i.setUndividedLayerBase64(null);
|
||||||
@@ -735,7 +745,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerWithSinglePrintBase64())) {
|
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerWithSinglePrintBase64())) {
|
||||||
log.info("set UndividedLayerWithSinglePrintBase64为空,便于日志打印");
|
log.info("set UndividedLayerWithSinglePrintBase64为空,便于日志打印");
|
||||||
i.setUndividedLayerWithSinglePrintBase64(null);
|
i.setUndividedLayerWithSinglePrintBase64(null);
|
||||||
}
|
}*/
|
||||||
});
|
});
|
||||||
|
|
||||||
log.info("designSingle request入参 ==> " + JSONObject.toJSONString(clone));
|
log.info("designSingle request入参 ==> " + JSONObject.toJSONString(clone));
|
||||||
@@ -848,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, designSingleIncludeLayersDTO);
|
// 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, designSingleIncludeLayersDTO.getDesignType());
|
/*, priorityAndUndividedLayer*/, changeModelFlag, modelId, modelType, isSingleCollectionFlag, designSingleIncludeLayersDTO.getDesignType());
|
||||||
|
|
||||||
saveCollectionElement(designSingleIncludeLayersDTO);
|
saveCollectionElement(designSingleIncludeLayersDTO);
|
||||||
} else {
|
} else {
|
||||||
@@ -888,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:仅查询(无事务)
|
||||||
@@ -977,10 +987,21 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
} else if (Objects.isNull(item.getPartialDesign())
|
} else if (Objects.isNull(item.getPartialDesign())
|
||||||
|| StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
|
|| StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
|
||||||
if (designType.equals("merge")) {
|
if (designType.equals("merge")) {
|
||||||
log.error("merge模式下,必须提供partialDesign");
|
// 先去数据库进行查找,如果数据库中也是空,则提示需要提供,否则无法生成
|
||||||
throw new BusinessException("required.partialDesign");
|
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));
|
||||||
}
|
}
|
||||||
item.setPartialDesign(new PartialDesignDTO(null));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1023,7 +1044,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||||
String designType = "default";
|
String designType = "default";
|
||||||
if (Objects.nonNull(designSingleIncludeLayersDTO)) {
|
if (Objects.nonNull(designSingleIncludeLayersDTO)) {
|
||||||
designType = designSingleIncludeLayersDTO.getDesignType();
|
designType = designSingleIncludeLayersDTO.getDesignType();
|
||||||
}
|
}
|
||||||
@@ -1163,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<>();
|
||||||
@@ -1204,6 +1225,7 @@ 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())) {
|
||||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0))) {
|
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0))) {
|
||||||
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).getFirst(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).getFirst(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||||
@@ -1211,7 +1233,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
|||||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1))) {
|
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));
|
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);
|
||||||
@@ -1387,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")) {
|
||||||
|
|||||||
@@ -46,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;
|
||||||
@@ -853,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;
|
||||||
@@ -864,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());
|
||||||
@@ -1140,12 +1141,12 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
userPreference.setPath(designItemDetail.getPath());
|
userPreference.setPath(designItemDetail.getPath());
|
||||||
SysFile sysFile = sysFileService.getOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUrl, designItemDetail.getPath()));
|
SysFile sysFile = sysFileService.getOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUrl, designItemDetail.getPath()));
|
||||||
userPreference.setAccountId(userHolder.getId());
|
userPreference.setAccountId(userHolder.getId());
|
||||||
if (sysFile != null){
|
if (sysFile != null) {
|
||||||
userPreference.setCategory(sysFile.getLevel3Type().toLowerCase()+"_"+sysFile.getLevel2Type().toLowerCase());
|
userPreference.setCategory(sysFile.getLevel3Type().toLowerCase() + "_" + sysFile.getLevel2Type().toLowerCase());
|
||||||
userPreference.setStyle(sysFile.getStyle());
|
userPreference.setStyle(sysFile.getStyle());
|
||||||
}else {
|
} else {
|
||||||
log.error("sysFile not found:{}",designItemDetail.getPath());
|
log.error("sysFile not found:{}", designItemDetail.getPath());
|
||||||
SendEmailUtil.commonExceptionReminder("url在sysFile里找不到"+designItemDetail.getPath(), new String[]{"litianxiangxtt@163.com"});
|
SendEmailUtil.commonExceptionReminder("url在sysFile里找不到" + designItemDetail.getPath(), new String[]{"litianxiangxtt@163.com"});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1154,7 +1155,12 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
|||||||
.toLocalDateTime());
|
.toLocalDateTime());
|
||||||
userPreference.setDesignItemId(designItem.getId());
|
userPreference.setDesignItemId(designItem.getId());
|
||||||
userPreference.setProjectId(projectId);
|
userPreference.setProjectId(projectId);
|
||||||
userPreference.setWorkspaceRelStyleId(workspaceRelStyles.get(0).getStyleId());
|
if (workspaceRelStyles == null||workspaceRelStyles.isEmpty()) {
|
||||||
|
//查不到记录,style应该是all
|
||||||
|
userPreference.setWorkspaceRelStyleId(0L);
|
||||||
|
} else {
|
||||||
|
userPreference.setWorkspaceRelStyleId(workspaceRelStyles.get(0).getStyleId());
|
||||||
|
}
|
||||||
userPreferenceMapper.insert(userPreference);
|
userPreferenceMapper.insert(userPreference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1326,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()));
|
||||||
@@ -1663,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);
|
||||||
}
|
}
|
||||||
@@ -1670,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;
|
||||||
}
|
}
|
||||||
@@ -2539,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;
|
||||||
@@ -2550,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,7 +1271,7 @@ 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)){
|
if (StringUtil.isNullOrEmpty(modelName)) {
|
||||||
throw new BusinessException("system error");
|
throw new BusinessException("system error");
|
||||||
}
|
}
|
||||||
HashMap<String, String> modelAndPromptMap = new HashMap<>();
|
HashMap<String, String> modelAndPromptMap = new HashMap<>();
|
||||||
@@ -1221,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,modelName);
|
prompt = getPrintboardPrompt(style, prompt, modelName, isUseImage);
|
||||||
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
||||||
|
|
||||||
|
|
||||||
@@ -1260,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)) {
|
||||||
@@ -1459,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(
|
||||||
@@ -1570,7 +1666,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getPrintboardPrompt(String style, String userInput, String modelName) {
|
private String getPrintboardPrompt(String style, String userInput, String modelName, boolean isUseImage) {
|
||||||
String systemPrompt = null;
|
String systemPrompt = null;
|
||||||
String prompt;
|
String prompt;
|
||||||
|
|
||||||
@@ -1596,12 +1692,16 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
|||||||
"Flat textile pattern printed directly on fabric surface, no three-dimensional objects, no items placed on cloth. \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";
|
"Real style: fabric print, realistic woven/printed pattern, detailed surface pattern only";
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
throw new BusinessException("style error:"+ style);
|
throw new BusinessException("style error:" + style);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userInput == null || userInput.trim().isEmpty()) {
|
if (userInput == null || userInput.trim().isEmpty()) {
|
||||||
throw new BusinessException("prompt null");
|
if (isUseImage) {
|
||||||
|
prompt = "Theme: Image content" + "\nRequirement: " + systemPrompt;
|
||||||
|
} else {
|
||||||
|
throw new BusinessException("prompt null");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
|
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
|
||||||
}
|
}
|
||||||
@@ -2001,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4172,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:
|
||||||
@@ -4295,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());
|
||||||
|
|||||||
@@ -194,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);
|
||||||
@@ -253,12 +255,14 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
|||||||
// 计算总的系统通知数量
|
// 计算总的系统通知数量
|
||||||
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
|
||||||
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()
|
||||||
.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);
|
||||||
|
|
||||||
// 计算单个用户读了多少条系统数据
|
// 计算单个用户读了多少条系统数据
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
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
|
||||||
@@ -159,3 +160,25 @@ google.client.id=157095842121-kdd1fdf8m8nudvj9sprstb2k2prnf9e4.apps.googleuserco
|
|||||||
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/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
|
||||||
@@ -157,3 +158,25 @@ google.client.id=29310152396-nnsd3h533fld665oguu8ovrt1nukmt46.apps.googleusercon
|
|||||||
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=")">
|
||||||
|
|||||||
@@ -17,3 +17,5 @@
|
|||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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