Merge branch 'dev/dev' into dev/dev-ltx

This commit is contained in:
litianxiang
2025-10-28 17:23:31 +08:00
15 changed files with 158 additions and 61 deletions

1
.gitignore vendored
View File

@@ -31,3 +31,4 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
/logs/

View File

@@ -10,11 +10,12 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays; import java.util.Arrays;
/** /**
* 日志切面 * 日志切面
* *
* @author AI Assistant * @author AI Assistant
* @since 2024-01-01 * @since 2024-01-01
*/ */
@@ -28,13 +29,15 @@ public class LoggingAspect {
* 定义切点所有Controller方法 * 定义切点所有Controller方法
*/ */
@Pointcut("execution(* com.aida.lanecarford.controller..*(..))") @Pointcut("execution(* com.aida.lanecarford.controller..*(..))")
public void controllerMethods() {} public void controllerMethods() {
}
/** /**
* 定义切点所有Service方法 * 定义切点所有Service方法
*/ */
@Pointcut("execution(* com.aida.lanecarford.service..*(..))") @Pointcut("execution(* com.aida.lanecarford.service..*(..))")
public void serviceMethods() {} public void serviceMethods() {
}
/** /**
* Controller方法执行前记录日志 * Controller方法执行前记录日志
@@ -44,12 +47,13 @@ public class LoggingAspect {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) { if (attributes != null) {
HttpServletRequest request = attributes.getRequest(); HttpServletRequest request = attributes.getRequest();
logger.info("=== 请求开始 ==="); logger.info("=== 请求开始 ===");
logger.info("请求URL: {}", request.getRequestURL().toString()); logger.info("请求URL: {}", request.getRequestURL().toString());
logger.info("请求方法: {}", request.getMethod()); logger.info("请求方法: {}", request.getMethod());
logger.info("请求IP: {}", getClientIpAddress(request)); logger.info("请求IP: {}", getClientIpAddress(request));
logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); // logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
logger.info("请求参数: {}", Arrays.toString(joinPoint.getArgs())); logger.info("请求参数: {}", Arrays.toString(joinPoint.getArgs()));
} }
} }
@@ -59,7 +63,7 @@ public class LoggingAspect {
*/ */
@AfterReturning(pointcut = "controllerMethods()", returning = "result") @AfterReturning(pointcut = "controllerMethods()", returning = "result")
public void logControllerAfterReturning(JoinPoint joinPoint, Object result) { public void logControllerAfterReturning(JoinPoint joinPoint, Object result) {
logger.info("方法执行成功: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); logger.info("方法执行成功: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
logger.info("返回结果: {}", result); logger.info("返回结果: {}", result);
logger.info("=== 请求结束 ==="); logger.info("=== 请求结束 ===");
} }
@@ -69,7 +73,7 @@ public class LoggingAspect {
*/ */
@AfterThrowing(pointcut = "controllerMethods()", throwing = "exception") @AfterThrowing(pointcut = "controllerMethods()", throwing = "exception")
public void logControllerAfterThrowing(JoinPoint joinPoint, Throwable exception) { public void logControllerAfterThrowing(JoinPoint joinPoint, Throwable exception) {
logger.error("方法执行异常: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); logger.error("方法执行异常: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
logger.error("异常信息: ", exception); logger.error("异常信息: ", exception);
logger.info("=== 请求异常结束 ==="); logger.info("=== 请求异常结束 ===");
} }
@@ -80,15 +84,15 @@ public class LoggingAspect {
@Around("serviceMethods()") @Around("serviceMethods()")
public Object logServiceAround(ProceedingJoinPoint joinPoint) throws Throwable { public Object logServiceAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); // String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
String methodName = joinPoint.getSignature().getDeclaringType().getSimpleName() + "." + joinPoint.getSignature().getName();
try { try {
logger.debug("Service方法开始执行: {}", methodName); logger.debug("Service方法开始执行: {}", methodName);
Object result = joinPoint.proceed(); Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
logger.debug("Service方法执行成功: {}, 耗时: {}ms", methodName, (endTime - startTime)); logger.debug("Service方法执行成功: {}, 耗时: {}ms", methodName, (endTime - startTime));
return result; return result;
} catch (Exception e) { } catch (Exception e) {
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
@@ -106,22 +110,22 @@ public class LoggingAspect {
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) { if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
return xForwardedFor.split(",")[0]; return xForwardedFor.split(",")[0];
} }
String xRealIp = request.getHeader("X-Real-IP"); String xRealIp = request.getHeader("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) { if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
return xRealIp; return xRealIp;
} }
String proxyClientIp = request.getHeader("Proxy-Client-IP"); String proxyClientIp = request.getHeader("Proxy-Client-IP");
if (proxyClientIp != null && !proxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(proxyClientIp)) { if (proxyClientIp != null && !proxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(proxyClientIp)) {
return proxyClientIp; return proxyClientIp;
} }
String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP"); String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP");
if (wlProxyClientIp != null && !wlProxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(wlProxyClientIp)) { if (wlProxyClientIp != null && !wlProxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(wlProxyClientIp)) {
return wlProxyClientIp; return wlProxyClientIp;
} }
return request.getRemoteAddr(); return request.getRemoteAddr();
} }
} }

View File

@@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
/** /**
* 性能监控切面 * 性能监控切面
* *
* @author AI Assistant * @author AI Assistant
* @since 2024-01-01 * @since 2024-01-01
*/ */
@@ -22,7 +22,7 @@ public class PerformanceAspect {
/** /**
* 监控Controller方法性能 * 监控Controller方法性能
*/ */
@Around("execution(* com.aida.lanecarford.controller..*(..))") // @Around("execution(* com.aida.lanecarford.controller..*(..))")
public Object monitorControllerPerformance(ProceedingJoinPoint joinPoint) throws Throwable { public Object monitorControllerPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
return monitorMethodPerformance(joinPoint, "Controller"); return monitorMethodPerformance(joinPoint, "Controller");
} }
@@ -30,7 +30,7 @@ public class PerformanceAspect {
/** /**
* 监控Service方法性能 * 监控Service方法性能
*/ */
@Around("execution(* com.aida.lanecarford.service..*(..))") // @Around("execution(* com.aida.lanecarford.service..*(..))")
public Object monitorServicePerformance(ProceedingJoinPoint joinPoint) throws Throwable { public Object monitorServicePerformance(ProceedingJoinPoint joinPoint) throws Throwable {
return monitorMethodPerformance(joinPoint, "Service"); return monitorMethodPerformance(joinPoint, "Service");
} }
@@ -38,7 +38,7 @@ public class PerformanceAspect {
/** /**
* 监控数据库操作性能 * 监控数据库操作性能
*/ */
@Around("execution(* com.aida.lanecarford.mapper..*(..))") // @Around("execution(* com.aida.lanecarford.mapper..*(..))")
public Object monitorMapperPerformance(ProceedingJoinPoint joinPoint) throws Throwable { public Object monitorMapperPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
return monitorMethodPerformance(joinPoint, "Mapper"); return monitorMethodPerformance(joinPoint, "Mapper");
} }
@@ -49,28 +49,28 @@ public class PerformanceAspect {
private Object monitorMethodPerformance(ProceedingJoinPoint joinPoint, String layer) throws Throwable { private Object monitorMethodPerformance(ProceedingJoinPoint joinPoint, String layer) throws Throwable {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
try { try {
Object result = joinPoint.proceed(); Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime; long executionTime = endTime - startTime;
// 记录性能日志 // 记录性能日志
logPerformance(layer, methodName, executionTime, true); logPerformance(layer, methodName, executionTime, true);
// 如果执行时间过长,记录警告 // 如果执行时间过长,记录警告
if (executionTime > getWarningThreshold(layer)) { if (executionTime > getWarningThreshold(layer)) {
logger.warn("{}方法执行时间过长: {} - {}ms", layer, methodName, executionTime); logger.warn("{}方法执行时间过长: {} - {}ms", layer, methodName, executionTime);
} }
return result; return result;
} catch (Exception e) { } catch (Exception e) {
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime; long executionTime = endTime - startTime;
// 记录异常性能日志 // 记录异常性能日志
logPerformance(layer, methodName, executionTime, false); logPerformance(layer, methodName, executionTime, false);
throw e; throw e;
} }
} }
@@ -80,10 +80,10 @@ public class PerformanceAspect {
*/ */
private void logPerformance(String layer, String methodName, long executionTime, boolean success) { private void logPerformance(String layer, String methodName, long executionTime, boolean success) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("性能监控 - {}: {} - {}ms - {}", logger.debug("性能监控 - {}: {} - {}ms - {}",
layer, methodName, executionTime, success ? "成功" : "失败"); layer, methodName, executionTime, success ? "成功" : "失败");
} }
// 可以在这里添加性能数据收集逻辑,比如发送到监控系统 // 可以在这里添加性能数据收集逻辑,比如发送到监控系统
collectPerformanceMetrics(layer, methodName, executionTime, success); collectPerformanceMetrics(layer, methodName, executionTime, success);
} }
@@ -114,7 +114,7 @@ public class PerformanceAspect {
// - 发送到时序数据库 // - 发送到时序数据库
// - 更新内存中的统计信息 // - 更新内存中的统计信息
// - 发送到监控系统 // - 发送到监控系统
// 示例:简单的内存统计 // 示例:简单的内存统计
PerformanceMetrics.recordExecution(layer, methodName, executionTime, success); PerformanceMetrics.recordExecution(layer, methodName, executionTime, success);
} }
@@ -123,7 +123,7 @@ public class PerformanceAspect {
* 简单的性能指标收集器 * 简单的性能指标收集器
*/ */
private static class PerformanceMetrics { private static class PerformanceMetrics {
public static void recordExecution(String layer, String methodName, long executionTime, boolean success) { public static void recordExecution(String layer, String methodName, long executionTime, boolean success) {
// 简单的日志记录,实际项目中可以替换为更复杂的指标收集 // 简单的日志记录,实际项目中可以替换为更复杂的指标收集
if (executionTime > 1000) { // 超过1秒的操作 if (executionTime > 1000) { // 超过1秒的操作

View File

@@ -1,6 +1,7 @@
package com.aida.lanecarford.common.enums; package com.aida.lanecarford.common.enums;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@@ -8,13 +9,18 @@ import java.util.stream.Stream;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "生成状态枚举")
public enum StatusEnum { public enum StatusEnum {
@Schema(description = "等待中")
PENDING(0), PENDING(0),
@Schema(description = "成功")
SUCCEEDED(1), SUCCEEDED(1),
@Schema(description = "失败")
FAILED(2), FAILED(2),
@Schema(description = "运行中")
RUNNING(3); RUNNING(3);
private int code; private int code;
@@ -24,5 +30,4 @@ public enum StatusEnum {
} }
} }

View File

@@ -9,9 +9,9 @@ import java.util.stream.Stream;
@AllArgsConstructor @AllArgsConstructor
public enum StylistPathEnum { public enum StylistPathEnum {
STYLIST_ONE("crystal", "lanecarford/stylist_guide/crystal_en.md"), STYLIST_ONE("crystal", "lanecarford/stylist_guide/latest/crystal_en.md"),
STYLIST_TWO("mini", "lanecarford/stylist_guide/mini_en.md"); STYLIST_TWO("mini", "lanecarford/stylist_guide/latest/mini_en.md");
private String name; private String name;

View File

@@ -2,6 +2,7 @@ package com.aida.lanecarford.controller;
import com.aida.lanecarford.common.ApiResponse; import com.aida.lanecarford.common.ApiResponse;
import com.aida.lanecarford.dto.LoginRequest; import com.aida.lanecarford.dto.LoginRequest;
import com.aida.lanecarford.entity.User;
import com.aida.lanecarford.service.LoginService; import com.aida.lanecarford.service.LoginService;
import com.aida.lanecarford.vo.LoginVO; import com.aida.lanecarford.vo.LoginVO;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
@@ -23,8 +24,8 @@ public class LoginController {
private final LoginService loginService; private final LoginService loginService;
@Operation( @Operation(
summary = "预检查并发送邮箱验证码", summary = "预检查并发送邮箱验证码",
description = "根据操作类型验证邮箱有效性并发送验证码。支持注册、登录、忘记密码三种操作类型。" description = "根据操作类型验证邮箱有效性并发送验证码。支持注册、登录、忘记密码三种操作类型。"
) )
@PostMapping("/precheckAndSendEmail") @PostMapping("/precheckAndSendEmail")
@Hidden @Hidden
@@ -44,8 +45,8 @@ public class LoginController {
} }
@Operation( @Operation(
summary = "用户注册或登录", summary = "用户注册或登录",
description = "通过验证码完成用户注册或登录返回JWT令牌和用户信息。" description = "通过验证码完成用户注册或登录返回JWT令牌和用户信息。"
) )
@PostMapping("/registerOrLogin") @PostMapping("/registerOrLogin")
public ApiResponse<LoginVO> registerOrLogin(@Valid @RequestBody LoginRequest loginRequest) { public ApiResponse<LoginVO> registerOrLogin(@Valid @RequestBody LoginRequest loginRequest) {
@@ -53,8 +54,8 @@ public class LoginController {
} }
@Operation( @Operation(
summary = "用户登出", summary = "用户登出",
description = "清除用户登录状态使当前JWT令牌失效。" description = "清除用户登录状态使当前JWT令牌失效。"
) )
@GetMapping("/logout") @GetMapping("/logout")
public ApiResponse<String> logout() { public ApiResponse<String> logout() {
@@ -63,8 +64,8 @@ public class LoginController {
} }
@Operation( @Operation(
summary = "忘记密码", summary = "忘记密码",
description = "通过邮箱验证码重置用户密码。需要先获取验证码,然后提供新密码。" description = "通过邮箱验证码重置用户密码。需要先获取验证码,然后提供新密码。"
) )
@PostMapping("/forgotPwd") @PostMapping("/forgotPwd")
public ApiResponse<String> forgotPwd(@Valid @RequestBody LoginRequest loginRequest) { public ApiResponse<String> forgotPwd(@Valid @RequestBody LoginRequest loginRequest) {
@@ -73,8 +74,8 @@ public class LoginController {
} }
@Operation( @Operation(
summary = "检查登录状态", summary = "检查登录状态",
description = "验证当前用户的登录状态是否有效检查JWT令牌是否过期。" description = "验证当前用户的登录状态是否有效检查JWT令牌是否过期。"
) )
@GetMapping("/checkLoginStatus") @GetMapping("/checkLoginStatus")
public ApiResponse<String> checkLoginStatus() { public ApiResponse<String> checkLoginStatus() {
@@ -86,4 +87,13 @@ public class LoginController {
} }
} }
@Operation(
summary = "获取用户信息",
description = "通过token获取当前用户信息"
)
@GetMapping("/getUserInfo")
public ApiResponse<User> getUserInfo() {
return ApiResponse.success(loginService.getUserInfo());
}
} }

View File

@@ -5,17 +5,15 @@ import lombok.Data;
import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 请求参数基类 * 请求参数基类
*
* @author AI Assistant
* @since 2024-01-01
*/ */
@Data @Data
@Schema(description = "请求参数基类") @Schema(description = "请求参数基类")
public abstract class BaseRequest implements Serializable { public class BaseRequest implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -71,8 +69,8 @@ public abstract class BaseRequest implements Serializable {
* 是否有时间范围筛选 * 是否有时间范围筛选
*/ */
public boolean hasTimeRange() { public boolean hasTimeRange() {
return startTime != null && !startTime.trim().isEmpty() return startTime != null && !startTime.trim().isEmpty()
&& endTime != null && !endTime.trim().isEmpty(); && endTime != null && !endTime.trim().isEmpty();
} }
/** /**

View File

@@ -3,6 +3,7 @@ package com.aida.lanecarford.dto;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
import java.util.Map;
@Data @Data
public class OutfitCallbackDTO { public class OutfitCallbackDTO {
@@ -14,5 +15,5 @@ public class OutfitCallbackDTO {
private String path; private String path;
private List<String> items; private List<Map<String, String>> items;
} }

View File

@@ -26,4 +26,6 @@ public interface LoginService extends IService<User> {
void forgotPwd(LoginRequest loginRequest); void forgotPwd(LoginRequest loginRequest);
boolean checkLoginStatus(); boolean checkLoginStatus();
User getUserInfo();
} }

View File

@@ -3,6 +3,7 @@ package com.aida.lanecarford.service.impl;
import com.aida.lanecarford.common.constant.RedisURIConstants; import com.aida.lanecarford.common.constant.RedisURIConstants;
import com.aida.lanecarford.common.enums.AuthenticationOperationTypeEnum; import com.aida.lanecarford.common.enums.AuthenticationOperationTypeEnum;
import com.aida.lanecarford.common.enums.LanguageEnum; import com.aida.lanecarford.common.enums.LanguageEnum;
import com.aida.lanecarford.common.response.ResultEnum;
import com.aida.lanecarford.common.security.JwtUtil; import com.aida.lanecarford.common.security.JwtUtil;
import com.aida.lanecarford.common.security.context.UserContext; import com.aida.lanecarford.common.security.context.UserContext;
import com.aida.lanecarford.dto.LoginRequest; import com.aida.lanecarford.dto.LoginRequest;
@@ -246,11 +247,18 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements L
return false; return false;
} }
// 谷歌登录 // todo 谷歌登录
// 获取用户信息
public User getUserInfo() {
AuthPrincipalVO userHolder = UserContext.getUserHolder();
User user = getById(userHolder.getId());
if (Objects.isNull(user)) {
throw new BusinessException("User information cannot be found", ResultEnum.ERROR.getCode());
}
return user;
}
} }

View File

@@ -64,7 +64,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
outfitRequest.setGender(requestOutfitDTO.getGender()); outfitRequest.setGender(requestOutfitDTO.getGender());
outfitRequestMapper.insert(outfitRequest); outfitRequestMapper.insert(outfitRequest);
String response = SendRequestUtil.sendPost(CommonConstants.REQUEST_OUTFIT, JSON.toJSONString(params)); String response = SendRequestUtil.sendPostWithRetry(CommonConstants.REQUEST_OUTFIT, JSON.toJSONString(params));
JSONObject jsonObject = JSONObject.parseObject(response); // todo 确认这里的status的取值 JSONObject jsonObject = JSONObject.parseObject(response); // todo 确认这里的status的取值
if (Objects.isNull(response) /*|| !jsonObject.getString("status").equals("ok")*/) { if (Objects.isNull(response) /*|| !jsonObject.getString("status").equals("ok")*/) {
@@ -75,7 +75,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
List<String> requestIds = jsonObject.getJSONArray("outfit_ids").toJavaList(String.class); List<String> requestIds = jsonObject.getJSONArray("outfit_ids").toJavaList(String.class);
for(String requestId : requestIds) { for (String requestId : requestIds) {
// 生成需要 6~8s, 所以这里可以先请求再存储 // 生成需要 6~8s, 所以这里可以先请求再存储
Style style = new Style(); Style style = new Style();
style.setCustomerId(requestOutfitDTO.getCustomerId()); style.setCustomerId(requestOutfitDTO.getCustomerId());
@@ -175,7 +175,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
String key = RedisURIConstants.outfitResultCache + requestID; String key = RedisURIConstants.outfitResultCache + requestID;
Object outfit = cacheUtil.getCache(key); Object outfit = cacheUtil.getCache(key);
if (Objects.isNull(outfit)){ if (Objects.isNull(outfit)) {
reQueryIds.add(requestID); reQueryIds.add(requestID);
continue; continue;
} }

View File

@@ -12,7 +12,7 @@ import java.util.Map;
@Component @Component
public class SendRequestUtil { public class SendRequestUtil {
public static String sendPost(String url, String requestBodyStr){ public static String sendPost(String url, String requestBodyStr) {
int status; int status;
String body; String body;
try (HttpResponse execute = HttpRequest.post(url) try (HttpResponse execute = HttpRequest.post(url)
@@ -53,6 +53,61 @@ public class SendRequestUtil {
throw new BusinessException("System error (External interface failure)"); throw new BusinessException("System error (External interface failure)");
} }
public static String sendPostWithRetry(String url, String requestBodyStr) {
return sendPost(url, requestBodyStr, 3, 1000); // 默认重试3次间隔1秒
}
public static String sendPost(String url, String requestBodyStr, int maxRetries, long retryInterval) {
int status = 0;
String body = null;
int retryCount = 0;
while (retryCount <= maxRetries) {
try {
log.debug("发送POST请求URL: {}, 重试次数: {}/{}", url, retryCount, maxRetries);
HttpResponse execute = HttpRequest.post(url)
.header("Content-Type", "application/json")
.body(requestBodyStr)
.timeout(180000)
.execute();
status = execute.getStatus();
body = execute.body();
if (status == 200) {
log.debug("请求成功URL: {}, 状态码: {}", url, status);
return body;
} else {
log.warn("请求返回非200状态码URL: {}, 状态码: {}, Body: {}", url, status, body);
}
} catch (Exception e) {
log.warn("请求发生异常URL: {}, 异常信息: {}, 重试次数: {}/{}",
url, e.getMessage(), retryCount, maxRetries);
}
// 判断是否继续重试
if (retryCount < maxRetries) {
retryCount++;
try {
log.debug("等待 {}ms 后重试...", retryInterval);
Thread.sleep(retryInterval);
// 可选:递增重试间隔(指数退避)
retryInterval = (long) (retryInterval * 1.5);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
} else {
break;
}
}
log.error("请求最终失败URL: {}, 最大重试次数: {}, 最后状态码: {}, 最后响应: {}",
url, maxRetries, status, body);
return null;
}
} }

View File

@@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
public class StringListConverter { public class StringListConverter {
@@ -16,7 +17,7 @@ public class StringListConverter {
/** /**
* 将 List<String> 转换为 JSON 字符串 * 将 List<String> 转换为 JSON 字符串
*/ */
public static String listToJson(List<String> list) { public static String listToJson(List<Map<String, String>> list) {
if (list == null || list.isEmpty()) { if (list == null || list.isEmpty()) {
return "[]"; return "[]";
} }
@@ -31,12 +32,13 @@ public class StringListConverter {
/** /**
* 将 JSON 字符串转换为 List<String> * 将 JSON 字符串转换为 List<String>
*/ */
public static List<String> jsonToList(String json) { public static List<Map<String, String>> jsonToList(String json) {
if (json == null || json.trim().isEmpty()) { if (json == null || json.trim().isEmpty()) {
return new ArrayList<>(); return new ArrayList<>();
} }
try { try {
return objectMapper.readValue(json, new TypeReference<List<String>>() {}); return objectMapper.readValue(json, new TypeReference<>() {
});
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
log.error("JSON转List失败: {}", json, e); log.error("JSON转List失败: {}", json, e);
throw new RuntimeException("JSON转List失败", e); throw new RuntimeException("JSON转List失败", e);

View File

@@ -1,18 +1,29 @@
package com.aida.lanecarford.vo; package com.aida.lanecarford.vo;
import com.aida.lanecarford.common.enums.StatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@Schema(description = "AI穿搭推荐结果响应参数")
public class OutfitResultVO { public class OutfitResultVO {
@Schema(description = "记录ID", example = "21")
private Long id; private Long id;
@Schema(description = "请求ID", example = "7c30e8f6-fdc6-4699-9239-ae6a9d3cf948")
private String requestId; private String requestId;
@Schema(description = "图片路径", example = "https://example.com/images/outfit_123.jpg")
private String path; private String path;
@Schema(
description = "处理状态",
implementation = StatusEnum.class,
requiredMode = Schema.RequiredMode.REQUIRED
)
private String status; private String status;
public OutfitResultVO(Long id, String requestId, String status) { public OutfitResultVO(Long id, String requestId, String status) {

View File

@@ -45,7 +45,7 @@ spring:
mybatis-plus: mybatis-plus:
configuration: configuration:
map-underscore-to-camel-case: true map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config: global-config:
db-config: db-config:
logic-delete-field: deleted logic-delete-field: deleted