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='穿搭请求表';