diff --git a/pom.xml b/pom.xml
index 448dcf0..e8faec2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,6 +90,11 @@
jackson-databind
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
diff --git a/src/main/java/com/aida/lanecarford/common/constant/RedisURIConstants.java b/src/main/java/com/aida/lanecarford/common/constant/RedisURIConstants.java
index 551c13d..6a95f2c 100644
--- a/src/main/java/com/aida/lanecarford/common/constant/RedisURIConstants.java
+++ b/src/main/java/com/aida/lanecarford/common/constant/RedisURIConstants.java
@@ -7,6 +7,8 @@ public class RedisURIConstants {
public static final String verifyCodeCache = "VerifyCodeCache:";
// 验证码 10分钟过期
public static final Long verifyCodeTimeout = 10 * 60L;
+ // outfit result 结果30分钟过期
+ public static final Long outfitResultTimeout = 30 * 60L;
public static final String outfitResultCache = "OutfitResultCache:";
diff --git a/src/main/java/com/aida/lanecarford/config/SwaggerConfig.java b/src/main/java/com/aida/lanecarford/config/SwaggerConfig.java
index cc6acc0..3363158 100644
--- a/src/main/java/com/aida/lanecarford/config/SwaggerConfig.java
+++ b/src/main/java/com/aida/lanecarford/config/SwaggerConfig.java
@@ -20,7 +20,7 @@ import java.util.List;
/**
* Swagger配置类
* 提供完整的API文档配置,包括安全认证、服务器信息和标签分类
- *
+ *
* @author AI Assistant
* @since 2024-01-01
*/
@@ -116,9 +116,10 @@ public class SwaggerConfig {
.in(SecurityScheme.In.COOKIE)
.name("JSESSIONID")
.description("Session认证,通过登录接口获取"))
- .addSecuritySchemes("basicAuth", new SecurityScheme()
- .type(SecurityScheme.Type.HTTP)
- .scheme("basic")
+ .addSecuritySchemes("CustomAuth", new SecurityScheme()
+ .type(SecurityScheme.Type.APIKEY)
+ .name("Authorization")
+ .in(SecurityScheme.In.HEADER)
.description("基础认证(仅用于开发测试)"));
}
@@ -128,7 +129,7 @@ public class SwaggerConfig {
private List createSecurityRequirements() {
return Arrays.asList(
new SecurityRequirement().addList("sessionAuth"),
- new SecurityRequirement().addList("basicAuth")
+ new SecurityRequirement().addList("CustomAuth")
);
}
diff --git a/src/main/java/com/aida/lanecarford/config/WebConfig.java b/src/main/java/com/aida/lanecarford/config/WebConfig.java
index 6417d72..c034a95 100644
--- a/src/main/java/com/aida/lanecarford/config/WebConfig.java
+++ b/src/main/java/com/aida/lanecarford/config/WebConfig.java
@@ -39,8 +39,8 @@ public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/api/**/**") // 保护这些路径
- .excludePathPatterns(Arrays.asList("/api/auth/precheckAndSendEmail", "/api/auth/register",
- "/api/auth/login", "/api/auth/forgotPwd", "/api/style/callback")); // 排除登录接口
+ .excludePathPatterns(Arrays.asList("/api/auth/precheckEmail", "/api/auth/registerOrLogin",
+ "/api/auth/forgotPwd", "/api/style/callback")); // 排除登录接口
}
/**
diff --git a/src/main/java/com/aida/lanecarford/controller/ChatController.java b/src/main/java/com/aida/lanecarford/controller/ChatController.java
index fff3509..c5ff0c2 100644
--- a/src/main/java/com/aida/lanecarford/controller/ChatController.java
+++ b/src/main/java/com/aida/lanecarford/controller/ChatController.java
@@ -1,6 +1,9 @@
package com.aida.lanecarford.controller;
import com.aida.lanecarford.service.ChatService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
@@ -11,17 +14,27 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@Slf4j
@RestController
@RequestMapping("/api/llm")
+@Tag(name = "LLM对话管理", description = "大语言模型流式对话相关API接口")
public class ChatController {
@Resource
private ChatService chatService;
@CrossOrigin
+ @Operation(
+ summary = "流式对话",
+ description = "与大语言模型进行流式对话,返回Server-Sent Events数据流"
+ )
@GetMapping(value = "/streamChat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public SseEmitter streamChat(@RequestParam(required = false) String message,
- @RequestParam Long sessionId,
- @RequestParam String gender) {
- return chatService.streamChat(message, sessionId, gender );
- }
+ public SseEmitter streamChat(
+ @Parameter(description = "用户输入的消息内容", example = "你好,请介绍一下自己")
+ @RequestParam(required = false) String message,
+ @Parameter(description = "会话ID", example = "123456", required = true)
+ @RequestParam Long sessionId,
+
+ @Parameter(description = "性别", example = "male | female", required = true)
+ @RequestParam String gender) {
+ return chatService.streamChat(message, sessionId, gender);
+ }
}
diff --git a/src/main/java/com/aida/lanecarford/controller/LoginController.java b/src/main/java/com/aida/lanecarford/controller/LoginController.java
index c4de5e4..6fb3f34 100644
--- a/src/main/java/com/aida/lanecarford/controller/LoginController.java
+++ b/src/main/java/com/aida/lanecarford/controller/LoginController.java
@@ -4,9 +4,9 @@ import com.aida.lanecarford.common.ApiResponse;
import com.aida.lanecarford.dto.LoginRequest;
import com.aida.lanecarford.service.LoginService;
import com.aida.lanecarford.vo.LoginVO;
+import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@@ -27,11 +27,22 @@ public class LoginController {
description = "根据操作类型验证邮箱有效性并发送验证码。支持注册、登录、忘记密码三种操作类型。"
)
@PostMapping("/precheckAndSendEmail")
+ @Hidden
public ApiResponse preCheckAndSendEmail(@Valid @RequestBody LoginRequest loginRequest) {
loginService.preCheckAndSendEmail(loginRequest);
return ApiResponse.success("验证码已发送到您的邮箱");
}
+ @Operation(
+ summary = "检查邮箱",
+ description = "根据操作类型验证邮箱有效性并发送验证码。仅支持忘记密码。"
+ )
+ @GetMapping("/precheckEmail")
+ public ApiResponse precheckForgotPwdAndSendEmail(@Valid @RequestParam String email) {
+ loginService.precheckForgotPwdAndSendEmail(email);
+ return ApiResponse.success("验证码已发送到您的邮箱");
+ }
+
@Operation(
summary = "用户注册或登录",
description = "通过验证码完成用户注册或登录,返回JWT令牌和用户信息。"
diff --git a/src/main/java/com/aida/lanecarford/controller/StyleController.java b/src/main/java/com/aida/lanecarford/controller/StyleController.java
index 93eb876..d11a982 100644
--- a/src/main/java/com/aida/lanecarford/controller/StyleController.java
+++ b/src/main/java/com/aida/lanecarford/controller/StyleController.java
@@ -5,6 +5,10 @@ import com.aida.lanecarford.dto.OutfitCallbackDTO;
import com.aida.lanecarford.dto.RequestOutfitDTO;
import com.aida.lanecarford.service.StyleService;
import com.aida.lanecarford.vo.OutfitResultVO;
+import io.swagger.v3.oas.annotations.Hidden;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -22,22 +26,55 @@ import java.util.List;
@RestController
@RequestMapping("/api/style")
@RequiredArgsConstructor
+@Tag(name = "穿搭风格管理", description = "AI穿搭推荐和结果查询相关API接口")
public class StyleController {
private final StyleService styleService;
+ /**
+ * 请求AI穿搭推荐
+ * 提交穿搭需求给AI模型,异步生成穿搭方案
+ *
+ * @param requestOutfitDTO 穿搭请求参数DTO
+ * @return 包含请求ID列表的响应结果
+ */
+ @Operation(
+ summary = "请求AI穿搭推荐",
+ description = "提交用户的穿搭需求给AI模型进行异步处理,返回请求ID用于后续结果查询"
+ )
@PostMapping("/requestOutfit")
public ApiResponse> requestOutfit(@Valid @RequestBody RequestOutfitDTO requestOutfitDTO) {
return ApiResponse.success(styleService.requestOutfit(requestOutfitDTO));
}
+ /**
+ * AI服务回调接口
+ * 接收AI服务处理完成后的回调通知,更新穿搭结果状态
+ * 注意:此接口为内部接口,供AI服务调用,不对外暴露文档
+ *
+ * @param callbackDTO AI回调数据DTO
+ */
+ @Hidden
@PostMapping("/callback")
public void callback(@RequestBody OutfitCallbackDTO callbackDTO) {
styleService.callback(callbackDTO);
}
+ /**
+ * 获取穿搭结果
+ * 根据请求ID列表查询AI生成的穿搭方案结果
+ *
+ * @param requestIDs 请求ID列表,通过requestOutfit接口获取
+ * @return 穿搭结果视图对象列表
+ */
+ @Operation(
+ summary = "获取穿搭结果",
+ description = "根据请求ID列表查询AI生成的穿搭方案结果,支持批量查询"
+ )
@GetMapping("/getOutfitResult")
- public ApiResponse> getOutfitResult(@RequestParam List requestIDs) {
+ public ApiResponse> getOutfitResult(
+ @Parameter(description = "请求ID列表", required = true, example = "[a22019ac-9db2-4076-9953-42d65e8120ec, 20217a09-435d-4b34-962a-3af59881c6d9]")
+ @RequestParam List requestIDs) {
return ApiResponse.success(styleService.getOutfitResult(requestIDs));
}
diff --git a/src/main/java/com/aida/lanecarford/dto/RequestOutfitDTO.java b/src/main/java/com/aida/lanecarford/dto/RequestOutfitDTO.java
index 633fdfb..6c1d450 100644
--- a/src/main/java/com/aida/lanecarford/dto/RequestOutfitDTO.java
+++ b/src/main/java/com/aida/lanecarford/dto/RequestOutfitDTO.java
@@ -1,28 +1,55 @@
package com.aida.lanecarford.dto;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
+@Schema(description = "AI穿搭推荐请求参数")
public class RequestOutfitDTO {
- // 顾客id
+ @Schema(
+ description = "顾客ID",
+ example = "1",
+ requiredMode = Schema.RequiredMode.REQUIRED
+ )
@NotNull(message = "customer id cannot be empty")
private Long customerId;
- // 进店id
+ @Schema(
+ description = "顾客进店记录ID",
+ example = "1",
+ requiredMode = Schema.RequiredMode.REQUIRED
+ )
@NotNull(message = "customer check-in id cannot be empty")
private Long checkInId;
- // 选择的设计师
+ @Schema(
+ description = "选择的设计师风格",
+ example = "mini",
+ requiredMode = Schema.RequiredMode.REQUIRED,
+ allowableValues = {"mini", " crystal"}
+ )
@NotNull(message = "please select a stylist")
private String stylist;
+ @Schema(
+ description = "性别",
+ example = "female",
+ requiredMode = Schema.RequiredMode.REQUIRED,
+ allowableValues = {"male", "female"/*, "unisex"*/}
+ )
@NotNull(message = "please select gender")
private String gender;
- // 生成数量
- private int num;
+ @Schema(
+ description = "生成穿搭方案的数量",
+ example = "4",
+ defaultValue = "1",
+ minimum = "1",
+ maximum = "4"
+ )
+ private int num = 1;
}
diff --git a/src/main/java/com/aida/lanecarford/entity/Style.java b/src/main/java/com/aida/lanecarford/entity/Style.java
index b90fc12..3f170c8 100644
--- a/src/main/java/com/aida/lanecarford/entity/Style.java
+++ b/src/main/java/com/aida/lanecarford/entity/Style.java
@@ -61,7 +61,7 @@ public class Style extends BaseEntity {
/**
* 单品的唯一id
*/
- private List items;
+ private String items;
/**
* 生成状态(pending-等待中,processing-处理中,completed-已完成,failed-失败)
diff --git a/src/main/java/com/aida/lanecarford/service/LoginService.java b/src/main/java/com/aida/lanecarford/service/LoginService.java
index f25ca9b..148c7ec 100644
--- a/src/main/java/com/aida/lanecarford/service/LoginService.java
+++ b/src/main/java/com/aida/lanecarford/service/LoginService.java
@@ -17,6 +17,8 @@ public interface LoginService extends IService {
void preCheckAndSendEmail(LoginRequest loginRequest);
+ void precheckForgotPwdAndSendEmail(String email);
+
LoginVO registerOrLogin(LoginRequest loginRequest);
void logout();
diff --git a/src/main/java/com/aida/lanecarford/service/impl/ChatServiceImpl.java b/src/main/java/com/aida/lanecarford/service/impl/ChatServiceImpl.java
index ac36d92..95627f5 100644
--- a/src/main/java/com/aida/lanecarford/service/impl/ChatServiceImpl.java
+++ b/src/main/java/com/aida/lanecarford/service/impl/ChatServiceImpl.java
@@ -155,7 +155,7 @@ public class ChatServiceImpl implements ChatService {
}
private void sendRawData(String rawData, SseEmitter emitter, String sessionId) {
- Map response = createResponse("raw", rawData, sessionId);
+ Map response = createResponse("text", rawData, sessionId);
sendToClient(emitter, response);
}
diff --git a/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java b/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java
index cf7be4c..50d559d 100644
--- a/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java
+++ b/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java
@@ -64,9 +64,6 @@ public class LoginServiceImpl extends ServiceImpl implements L
case LOGIN:
precheckLogin(user, loginRequest);
break;
- case FORGET_PWD:
- precheckForgotPwd(user, loginRequest);
- break;
default:
throw new BusinessException("Unknown authentication operation type.");
@@ -77,11 +74,11 @@ public class LoginServiceImpl extends ServiceImpl implements L
if (Objects.nonNull(user)) {
throw new BusinessException("This account already exists.");
}
- String verifyCode = getCodeAndSetCache(REGISTER.name(), email);
+ /*String verifyCode = getCodeAndSetCache(REGISTER.name(), email);
Boolean sent = sendEmailUtil.send(email, REGISTER.name(), verifyCode);
if (!sent) {
throw new BusinessException("Failed to send verification code");
- }
+ }*/
}
private void precheckLogin(User user, LoginRequest loginRequest) {
@@ -91,35 +88,41 @@ public class LoginServiceImpl extends ServiceImpl implements L
if (!user.getPassword().equals(loginRequest.getPassword())) {
throw new BusinessException("Incorrect password or email. Please try again.");
}
- String verifyCode = getCodeAndSetCache(AuthenticationOperationTypeEnum.LOGIN.name(), loginRequest.getEmail());
+ /*String verifyCode = getCodeAndSetCache(AuthenticationOperationTypeEnum.LOGIN.name(), loginRequest.getEmail());
Boolean sent = sendEmailUtil.send(loginRequest.getEmail(), LOGIN.name(), verifyCode);
if (!sent) {
throw new BusinessException("Failed to send verification code");
- }
+ }*/
}
- private void precheckForgotPwd(User user, LoginRequest loginRequest) {
+ @Override
+ public void precheckForgotPwdAndSendEmail(String email) {
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.lambda().eq(User::getEmail, email);
+
+ User user = getOne(queryWrapper);
if (Objects.isNull(user)) {
throw new BusinessException("Account does not exist. Please register.");
}
- if (StringUtil.isNullOrEmpty(loginRequest.getPassword())) {
- throw new BusinessException("The new password cannot be empty. Please enter a new password.");
- }
- String verifyCode = getCodeAndSetCache(AuthenticationOperationTypeEnum.FORGET_PWD.name(), loginRequest.getEmail());
- Boolean sent = sendEmailUtil.send(loginRequest.getEmail(), FORGET_PWD.name(), verifyCode);
+
+ String verifyCode = getCodeAndSetCache(AuthenticationOperationTypeEnum.FORGET_PWD.name(), email);
+ Boolean sent = sendEmailUtil.send(email, FORGET_PWD.name(), verifyCode);
if (!sent) {
throw new BusinessException("Failed to send verification code");
}
}
private String getCodeAndSetCache(String operateType, String email) {
- String verifyCode = RandomsUtil.generateSecureSixDigitRandom();
+ String verifyCode = RandomsUtil.generateSecureFiveDigitRandom();
String key = RedisURIConstants.verifyCodeCache + operateType + "_" + email;
cacheUtil.setCache(key, verifyCode, RedisURIConstants.verifyCodeTimeout);
return verifyCode;
}
private void checkVerifyCode(String verifyCode, String operateType, String email) {
+ if (StringUtil.isNullOrEmpty(verifyCode)) {
+ throw new BusinessException("Verification code cannot be empty.");
+ }
String key = RedisURIConstants.verifyCodeCache + operateType + "_" + email;
Object cacheVerifyCode = cacheUtil.getCache(key);
if (Objects.isNull(cacheVerifyCode)) {
@@ -140,6 +143,8 @@ public class LoginServiceImpl extends ServiceImpl implements L
public LoginVO registerOrLogin(LoginRequest loginRequest) {
LoginVO loginVO;
+ preCheckAndSendEmail(loginRequest);
+
// 2. 获取当前的操作类型
AuthenticationOperationTypeEnum operationTypeEnum = AuthenticationOperationTypeEnum.of(loginRequest.getOperationType());
@@ -156,7 +161,7 @@ public class LoginServiceImpl extends ServiceImpl implements L
// 注册
private LoginVO register(LoginRequest loginRequest) {
// 1. 验证邮箱
- checkVerifyCode(loginRequest.getVerifyCode(), REGISTER.name(), loginRequest.getEmail());
+// checkVerifyCode(loginRequest.getVerifyCode(), REGISTER.name(), loginRequest.getEmail());
// 2. 通过验证,添加账号
QueryWrapper queryWrapper = new QueryWrapper<>();
@@ -183,7 +188,7 @@ public class LoginServiceImpl extends ServiceImpl implements L
// 登录
private LoginVO login(LoginRequest loginRequest) {
// 1. 验证邮箱
- checkVerifyCode(loginRequest.getVerifyCode(), LOGIN.name(), loginRequest.getEmail());
+// checkVerifyCode(loginRequest.getVerifyCode(), LOGIN.name(), loginRequest.getEmail());
// 2. 获取用户信息
QueryWrapper queryWrapper = new QueryWrapper<>();
@@ -217,7 +222,12 @@ public class LoginServiceImpl extends ServiceImpl implements L
// 1. 验证邮箱
checkVerifyCode(loginRequest.getVerifyCode(), FORGET_PWD.name(), loginRequest.getEmail());
- // 2. 重置密码
+ // 2. 验证新密码是否为空
+ if (StringUtil.isNullOrEmpty(loginRequest.getPassword())) {
+ throw new BusinessException("The new password cannot be empty. Please enter a new password.");
+ }
+
+ // 3. 重置密码
UpdateWrapper updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda()
.set(User::getPassword, loginRequest.getPassword())
diff --git a/src/main/java/com/aida/lanecarford/service/impl/StyleServiceImpl.java b/src/main/java/com/aida/lanecarford/service/impl/StyleServiceImpl.java
index 404f1cf..d819b25 100644
--- a/src/main/java/com/aida/lanecarford/service/impl/StyleServiceImpl.java
+++ b/src/main/java/com/aida/lanecarford/service/impl/StyleServiceImpl.java
@@ -15,6 +15,7 @@ import com.aida.lanecarford.service.StyleService;
import com.aida.lanecarford.util.CacheUtil;
import com.aida.lanecarford.util.MinioUtil;
import com.aida.lanecarford.util.SendRequestUtil;
+import com.aida.lanecarford.util.StringListConverter;
import com.aida.lanecarford.vo.OutfitResultVO;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
@@ -24,6 +25,7 @@ import io.netty.util.internal.StringUtil;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@@ -44,6 +46,9 @@ public class StyleServiceImpl extends ServiceImpl implements
@Resource
private OutfitRequestMapper outfitRequestMapper;
+ @Value("${webhook.domain}")
+ private String webhookDomain;
+
// 请求获取搭配
public List requestOutfit(RequestOutfitDTO requestOutfitDTO) {
@@ -96,6 +101,7 @@ public class StyleServiceImpl extends ServiceImpl implements
params.put("user_id", customerId.toString());
params.put("num_outfits", num);
params.put("stylist_path", stylistPath);
+ params.put("callback_url", webhookDomain);
return params;
}
@@ -130,9 +136,10 @@ public class StyleServiceImpl extends ServiceImpl implements
// 3.更新path, items, 状态
// 由于数据变化较频繁,考虑存到redis
if (outfitResult instanceof OutfitResultVO) {
- ((OutfitResultVO) outfitResult).setPath(minioUtil.getPresignedUrl(callbackDTO.getPath(), CommonConstants.MINIO_PATH_TIMEOUT, null));
- ((OutfitResultVO) outfitResult).setStatus(StatusEnum.SUCCEEDED.name());
- cacheUtil.setCache(key, outfitResult, RedisURIConstants.verifyCodeTimeout);
+ ((OutfitResultVO) outfitResult).setPath(minioUtil.getPresignedUrl(callbackDTO.getPath(), CommonConstants.MINIO_PATH_TIMEOUT));
+ String status = "ok".equals(callbackDTO.getStatus()) ? StatusEnum.RUNNING.name() : StatusEnum.SUCCEEDED.name();
+ ((OutfitResultVO) outfitResult).setStatus(status);
+ cacheUtil.setCache(key, outfitResult, RedisURIConstants.outfitResultTimeout);
}
}
@@ -151,9 +158,11 @@ public class StyleServiceImpl extends ServiceImpl implements
int status = "stop".equals(callbackDTO.getStatus()) ? StatusEnum.SUCCEEDED.getCode() : StatusEnum.FAILED.getCode();
outfit.setGenerationStatus(status);
outfit.setStyleImageUrl(callbackDTO.getPath());
- outfit.setItems(callbackDTO.getItems());
outfit.setUpdatedTime(LocalDateTime.now());
+ String itemsJson = StringListConverter.listToJson(callbackDTO.getItems());
+ outfit.setItems(itemsJson);
+
updateById(outfit);
}
}
@@ -185,7 +194,7 @@ public class StyleServiceImpl extends ServiceImpl implements
outfitResultVO.setRequestId(style.getPythonRequestId());
outfitResultVO.setStatus(StatusEnum.of(style.getGenerationStatus()).name());
if (!StringUtil.isNullOrEmpty(style.getStyleImageUrl())) {
- outfitResultVO.setPath(minioUtil.getPresignedUrl(style.getStyleImageUrl(), CommonConstants.MINIO_PATH_TIMEOUT, null));
+ outfitResultVO.setPath(minioUtil.getPresignedUrl(style.getStyleImageUrl(), CommonConstants.MINIO_PATH_TIMEOUT));
}
resultVOS.add(outfitResultVO);
}
diff --git a/src/main/java/com/aida/lanecarford/util/MinioUtil.java b/src/main/java/com/aida/lanecarford/util/MinioUtil.java
index 756024d..94e8033 100644
--- a/src/main/java/com/aida/lanecarford/util/MinioUtil.java
+++ b/src/main/java/com/aida/lanecarford/util/MinioUtil.java
@@ -2,6 +2,7 @@ package com.aida.lanecarford.util;
import com.aida.lanecarford.config.MinioConfig;
import com.aida.lanecarford.common.constant.MinioFileConstants;
+import com.aida.lanecarford.exception.BusinessException;
import com.aida.lanecarford.exception.MinioException;
import io.minio.*;
import io.minio.http.Method;
@@ -298,6 +299,23 @@ public class MinioUtil {
}
}
+ /**
+ * 获取预签名URL(路径中包含了桶名)
+ *
+ * @param path 对象名称(逻辑路径)
+ * @param expires 过期时间(秒)
+ * @return 预签名URL
+ */
+ public String getPresignedUrl(String path, int expires) {
+ if (!path.contains("/")) {
+ throw new BusinessException("unknown path");
+ }
+ int index = path.indexOf("/");
+ String bucketName = path.substring(0, index);
+ String fileName = path.substring(index + 1);
+ return getPresignedUrl(fileName, expires, bucketName);
+ }
+
/**
* 批量获取预签名URL
*
diff --git a/src/main/java/com/aida/lanecarford/util/RandomsUtil.java b/src/main/java/com/aida/lanecarford/util/RandomsUtil.java
index 4fac22d..967c352 100644
--- a/src/main/java/com/aida/lanecarford/util/RandomsUtil.java
+++ b/src/main/java/com/aida/lanecarford/util/RandomsUtil.java
@@ -9,11 +9,11 @@ import java.security.SecureRandom;
public class RandomsUtil {
/**
- * 使用ThreadLocalRandom生成6位随机数
+ * 使用ThreadLocalRandom生成5位随机数
*/
- public static String generateSecureSixDigitRandom() {
+ public static String generateSecureFiveDigitRandom() {
SecureRandom secureRandom = new SecureRandom();
- return String.format("%06d", secureRandom.nextInt(1000000));
+ return String.format("%05d", secureRandom.nextInt(100000));
}
}
diff --git a/src/main/java/com/aida/lanecarford/util/StringListConverter.java b/src/main/java/com/aida/lanecarford/util/StringListConverter.java
new file mode 100644
index 0000000..d487ab5
--- /dev/null
+++ b/src/main/java/com/aida/lanecarford/util/StringListConverter.java
@@ -0,0 +1,45 @@
+package com.aida.lanecarford.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+public class StringListConverter {
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * 将 List 转换为 JSON 字符串
+ */
+ public static String listToJson(List list) {
+ if (list == null || list.isEmpty()) {
+ return "[]";
+ }
+ try {
+ return objectMapper.writeValueAsString(list);
+ } catch (JsonProcessingException e) {
+ log.error("List转JSON失败: {}", list, e);
+ throw new RuntimeException("List转JSON失败", e);
+ }
+ }
+
+ /**
+ * 将 JSON 字符串转换为 List
+ */
+ public static List jsonToList(String json) {
+ if (json == null || json.trim().isEmpty()) {
+ return new ArrayList<>();
+ }
+ try {
+ return objectMapper.readValue(json, new TypeReference>() {});
+ } catch (JsonProcessingException e) {
+ log.error("JSON转List失败: {}", json, e);
+ throw new RuntimeException("JSON转List失败", e);
+ }
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index ee0159b..0a1f38a 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -80,3 +80,5 @@ tencent:
secret-key: XqujLlywhHfrqcCYfYVHtNgmeIiwxkKf
sender: info@aida.com.hk
+webhook:
+ domain: https://0dd6f6504aff.ngrok-free.app
diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql
index 082161c..2e51b2a 100644
--- a/src/main/resources/sql/schema.sql
+++ b/src/main/resources/sql/schema.sql
@@ -1,157 +1,169 @@
-- Lane Carford AI系统基础架构数据库表结构
-- 创建数据库
CREATE DATABASE IF NOT EXISTS lanecarford CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-USE lanecarford;
+USE lanecrawford;
--- 1. 导购表
-CREATE TABLE sales (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '导购ID',
- username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
- password VARCHAR(255) NOT NULL COMMENT '密码(加密后)',
- real_name VARCHAR(100) NOT NULL COMMENT '真实姓名',
- employee_id VARCHAR(50) UNIQUE COMMENT '员工编号',
- store_id VARCHAR(50) COMMENT '门店ID',
- store_name VARCHAR(100) COMMENT '门店名称',
- phone VARCHAR(20) COMMENT '手机号',
- email VARCHAR(100) COMMENT '邮箱',
- is_active TINYINT DEFAULT 1 COMMENT '是否启用(0-禁用,1-启用)',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- INDEX idx_username (username),
- INDEX idx_employee_id (employee_id),
- INDEX idx_store_id (store_id),
- INDEX idx_deleted (deleted)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='导购表';
+-- 1. 用户表
+CREATE TABLE `user` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
+ `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名',
+ `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱',
+ `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码(加密后)',
+ `gender` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '性别',
+ `employee_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '员工编号',
+ `store_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '门店ID',
+ `store_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '门店名称',
+ `phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号',
+ `avatar` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像地址',
+ `language` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '系统语言',
+ `is_active` tinyint DEFAULT '1' COMMENT '是否启用(0-禁用,1-启用)',
+ `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`),
+ UNIQUE KEY `employee_id` (`employee_id`),
+ KEY `idx_email` (`email`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- 2. 顾客表
-CREATE TABLE customers (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '顾客ID',
- name VARCHAR(100) NOT NULL COMMENT '顾客姓名',
- email VARCHAR(100) NOT NULL COMMENT '顾客邮箱',
- phone VARCHAR(20) COMMENT '手机号',
- gender VARCHAR(10) COMMENT '性别',
- age_range VARCHAR(20) COMMENT '年龄段',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- INDEX idx_email (email),
- INDEX idx_phone (phone),
- INDEX idx_name (name),
- INDEX idx_deleted (deleted)
+CREATE TABLE `customers` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '顾客ID',
+ `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '顾客姓名',
+ `email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '顾客邮箱',
+ `phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号',
+ `gender` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '性别',
+ `age_range` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '年龄段',
+ `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ KEY `idx_email` (`email`),
+ KEY `idx_phone` (`phone`),
+ KEY `idx_name` (`name`),
+ KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='顾客表';
-- 3. 进店记录表
-CREATE TABLE visit_records (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '进店记录ID',
- customer_id BIGINT NOT NULL COMMENT '顾客ID',
- sales_id BIGINT NOT NULL COMMENT '导购ID',
- visit_date DATE NOT NULL COMMENT '进店日期',
- visit_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '进店时间',
- notes TEXT COMMENT '备注',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
- FOREIGN KEY (sales_id) REFERENCES sales(id) ON DELETE CASCADE,
- INDEX idx_customer_id (customer_id),
- INDEX idx_sales_id (sales_id),
- INDEX idx_deleted (deleted)
+CREATE TABLE `visit_records` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '进店记录ID',
+ `customer_id` bigint NOT NULL COMMENT '顾客ID',
+ `user_id` bigint NOT NULL COMMENT '导购ID',
+ `visit_date` date NOT NULL COMMENT '进店日期',
+ `visit_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '进店时间',
+ `notes` text COLLATE utf8mb4_unicode_ci COMMENT '备注',
+ `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ KEY `idx_customer_id` (`customer_id`),
+ KEY `idx_user_id` (`user_id`),
+ KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='进店记录表';
-- 4. 风格配置表
-CREATE TABLE styles (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '风格配置ID',
- customer_id BIGINT NOT NULL COMMENT '顾客ID',
- visit_record_id BIGINT NOT NULL COMMENT '进店记录ID',
- is_selected TINYINT DEFAULT 0 COMMENT '是否选中(0-未选中,1-已选中)',
- style_image_url VARCHAR(500) COMMENT '风格图片URL',
- python_request_id VARCHAR(100) COMMENT 'Python请求ID',
- generation_status TINYINT DEFAULT 0 COMMENT '生成状态(0-处理中,1-已完成,2-失败)',
- error_message TEXT COMMENT '错误信息',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
- FOREIGN KEY (visit_record_id) REFERENCES visit_records(id) ON DELETE CASCADE,
- INDEX idx_customer_id (customer_id),
- INDEX idx_visit_record_id (visit_record_id),
- INDEX idx_python_request_id (python_request_id),
- INDEX idx_is_selected (is_selected),
- INDEX idx_generation_status (generation_status),
- INDEX idx_deleted (deleted)
+CREATE TABLE `styles` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '风格配置ID',
+ `customer_id` bigint NOT NULL COMMENT '顾客ID',
+ `visit_record_id` bigint NOT NULL COMMENT '进店记录ID',
+ `outfit_request_id` bigint NOT NULL COMMENT '请求id',
+ `is_selected` tinyint DEFAULT '0' COMMENT '是否选中(0-未选中,1-已选中)',
+ `style_image_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '风格图片URL',
+ `python_request_id` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Python请求ID',
+ `generation_status` tinyint DEFAULT '0' COMMENT '生成状态(0-处理中,1-已完成,2-失败)',
+ `items` json DEFAULT NULL COMMENT '单品唯一标识',
+ `error_message` text COLLATE utf8mb4_unicode_ci COMMENT '错误信息',
+ `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ KEY `idx_customer_id` (`customer_id`),
+ KEY `idx_visit_record_id` (`visit_record_id`),
+ KEY `idx_python_request_id` (`python_request_id`),
+ KEY `idx_is_selected` (`is_selected`),
+ KEY `idx_generation_status` (`generation_status`),
+ KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='风格配置表';
-- 5. 模特照片表
-CREATE TABLE model_photos (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '模特照片ID',
- photo_url VARCHAR(500) NOT NULL COMMENT '模特照片URL',
- photo_name VARCHAR(200) COMMENT '照片名称',
- gender VARCHAR(10) NOT NULL COMMENT '性别',
- is_active TINYINT DEFAULT 1 COMMENT '是否启用(0-禁用,1-启用)',
- sort_order INT DEFAULT 0 COMMENT '排序权重',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- INDEX idx_gender (gender),
- INDEX idx_is_active (is_active),
- INDEX idx_sort_order (sort_order),
- INDEX idx_deleted (deleted)
+CREATE TABLE `model_photos` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '模特照片ID',
+ `photo_url` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模特照片URL',
+ `photo_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '照片名称',
+ `gender` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '性别',
+ `is_active` tinyint DEFAULT '1' COMMENT '是否启用(0-禁用,1-启用)',
+ `sort_order` int DEFAULT '0' COMMENT '排序权重',
+ `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ KEY `idx_gender` (`gender`),
+ KEY `idx_is_active` (`is_active`),
+ KEY `idx_sort_order` (`sort_order`),
+ KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='模特照片表';
-- 6. 顾客照片表
-CREATE TABLE customer_photos (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '顾客照片ID',
- customer_id BIGINT NOT NULL COMMENT '顾客ID',
- visit_record_id BIGINT NOT NULL COMMENT '进店记录ID',
- photo_url VARCHAR(500) NOT NULL COMMENT '照片URL',
- is_primary TINYINT DEFAULT 0 COMMENT '是否为主照片(0-否,1-是)',
- upload_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
- FOREIGN KEY (visit_record_id) REFERENCES visit_records(id) ON DELETE CASCADE,
- INDEX idx_customer_id (customer_id),
- INDEX idx_visit_record_id (visit_record_id),
- INDEX idx_is_primary (is_primary),
- INDEX idx_deleted (deleted)
+CREATE TABLE `customer_photos` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '顾客照片ID',
+ `customer_id` bigint NOT NULL COMMENT '顾客ID',
+ `visit_record_id` bigint NOT NULL COMMENT '进店记录ID',
+ `photo_url` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '照片URL',
+ `is_primary` tinyint DEFAULT '0' COMMENT '是否为主照片(0-否,1-是)',
+ `upload_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间',
+ `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ KEY `idx_customer_id` (`customer_id`),
+ KEY `idx_visit_record_id` (`visit_record_id`),
+ KEY `idx_is_primary` (`is_primary`),
+ KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='顾客照片表';
--- 8. 试穿效果表
-CREATE TABLE try_on_effects (
- id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '试穿效果ID',
- customer_id BIGINT NOT NULL COMMENT '顾客ID',
- visit_record_id BIGINT NOT NULL COMMENT '进店记录ID',
- style_id BIGINT NOT NULL COMMENT '风格ID',
- model_photo_id BIGINT COMMENT '模特照片ID',
- customer_photo_id BIGINT COMMENT '顾客照片ID',
- prompt VARCHAR(500) COMMENT '提示词,当is_regenerated为1时才会有值',
- original_try_on_id BIGINT COMMENT '原试穿效果ID,当is_regenerated为1时才会有值',
- is_regenerated TINYINT DEFAULT 0 COMMENT '是否由生成结果重新生成(0-否,1-是)',
- result_image_url VARCHAR(500) COMMENT '试穿结果图片URL',
- request_id VARCHAR(100) COMMENT '请求ID',
- generation_status VARCHAR(20) DEFAULT 'pending' COMMENT '生成状态(pending-等待中,processing-处理中,completed-已完成,failed-失败)',
- error_message TEXT COMMENT '错误信息',
- is_favorite TINYINT DEFAULT 0 COMMENT '是否喜欢的最终造型(0-否,1-是)',
- created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标志(0-未删除,1-已删除)',
- FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
- FOREIGN KEY (visit_record_id) REFERENCES visit_records(id) ON DELETE CASCADE,
- FOREIGN KEY (style_id) REFERENCES styles(id) ON DELETE CASCADE,
- FOREIGN KEY (model_photo_id) REFERENCES model_photos(id) ON DELETE SET NULL,
- FOREIGN KEY (customer_photo_id) REFERENCES customer_photos(id) ON DELETE SET NULL,
- INDEX idx_customer_id (customer_id),
- INDEX idx_visit_record_id (visit_record_id),
- INDEX idx_style_id (style_id),
- INDEX idx_request_id (request_id),
- INDEX idx_generation_status (generation_status),
- INDEX idx_is_favorite (is_favorite),
- INDEX idx_deleted (deleted)
+-- 7. 试穿效果表
+CREATE TABLE `try_on_effects` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '试穿效果ID',
+ `customer_id` bigint NOT NULL COMMENT '顾客ID',
+ `visit_record_id` bigint NOT NULL COMMENT '进店记录ID',
+ `style_id` bigint NOT NULL COMMENT '风格ID',
+ `model_photo_id` bigint DEFAULT NULL COMMENT '模特照片ID',
+ `customer_photo_id` bigint DEFAULT NULL COMMENT '顾客照片ID',
+ `prompt` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提示词,当is_regenerated为1时才会有值',
+ `original_try_on_id` bigint DEFAULT NULL COMMENT '原试穿效果ID,当is_regenerated为1时才会有值',
+ `is_regenerated` tinyint DEFAULT '0' COMMENT '是否由生成结果重新生成(0-否,1-是)',
+ `result_image_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '试穿结果图片URL',
+ `request_id` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '请求ID',
+ `generation_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'pending' COMMENT '生成状态(pending-等待中,processing-处理中,completed-已完成,failed-失败)',
+ `error_message` text COLLATE utf8mb4_unicode_ci COMMENT '错误信息',
+ `is_favorite` tinyint DEFAULT '0' COMMENT '是否喜欢的最终造型(0-否,1-是)',
+ `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+ PRIMARY KEY (`id`),
+ KEY `idx_customer_id` (`customer_id`),
+ KEY `idx_visit_record_id` (`visit_record_id`),
+ KEY `idx_style_id` (`style_id`),
+ KEY `idx_request_id` (`request_id`),
+ KEY `idx_generation_status` (`generation_status`),
+ KEY `idx_is_favorite` (`is_favorite`),
+ KEY `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='试穿效果表';
+-- 8. 穿搭请求表
+CREATE TABLE `outfit_request` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `customer_id` bigint NOT NULL COMMENT '顾客id',
+ `visit_record_id` bigint NOT NULL COMMENT '进店记录id',
+ `stylist` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '选择的设计师风格',
+ `gender` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '选择的性别',
+ `status` tinyint(1) DEFAULT '0' COMMENT '当前任务状态 0-处理中 1-成功 2-失败',
+ `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='穿搭请求表';