Merge branch 'dev/dev-xp' into dev/dev
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
package com.aida.lanecarford.common.constant;
|
||||
|
||||
public class CommonConstants {
|
||||
|
||||
public static final String REQUEST_OUTFIT = "http://18.167.251.121:10004/api/v1/chatbot";
|
||||
|
||||
public static final String CHAT = "http://18.167.251.121:10004/api/v1/agent";
|
||||
|
||||
public static final int MINIO_PATH_TIMEOUT = 7 * 24 * 60 * 60; // minio图片临时访问地址 7 天过期(second)
|
||||
|
||||
|
||||
}
|
||||
@@ -8,5 +8,9 @@ public class RedisURIConstants {
|
||||
// 验证码 10分钟过期
|
||||
public static final Long verifyCodeTimeout = 10 * 60L;
|
||||
|
||||
public static final String outfitResultCache = "OutfitResultCache:";
|
||||
|
||||
public static final String minioPathCache = "MinioPathCache:";
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.aida.lanecarford.common.enums;
|
||||
|
||||
|
||||
public enum StatusEnum {
|
||||
SUCCEEDED,
|
||||
|
||||
FAILED,
|
||||
|
||||
PENDING,
|
||||
|
||||
RUNNING
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.aida.lanecarford.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum StylistPathEnum {
|
||||
|
||||
STYLIST_ONE("crystal", "lanecarford/stylist_guide/crystal_en.md"),
|
||||
|
||||
STYLIST_TWO("mini", "lanecarford/stylist_guide/mini_en.md");
|
||||
|
||||
private String name;
|
||||
|
||||
private String path;
|
||||
|
||||
public static StylistPathEnum of(String name) {
|
||||
return Stream.of(StylistPathEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -30,17 +30,18 @@ public class JwtUtil {
|
||||
|
||||
// 生成JWT token
|
||||
public String generateToken(AuthPrincipalVO principal) {
|
||||
return Jwts.builder()
|
||||
String token = Jwts.builder()
|
||||
.subject(JSONObject.toJSONString(principal))
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + jwtProperties.getJwtExpiration()))
|
||||
.signWith(getSigningKey())
|
||||
.compact();
|
||||
return jwtProperties.getJwtTokenPrefix() + token;
|
||||
}
|
||||
|
||||
// 从token中提取用户信息
|
||||
public String extractUserinfo(String token) {
|
||||
return parseClaims(token).getSubject();
|
||||
return parser(token).getSubject();
|
||||
}
|
||||
|
||||
// 验证token是否有效
|
||||
@@ -62,11 +63,7 @@ public class JwtUtil {
|
||||
token = token.substring(jwtProperties.getJwtTokenPrefix().length()).trim();
|
||||
}
|
||||
|
||||
return Jwts.parser()
|
||||
.verifyWith(getSigningKey())
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
return parseClaims(token);
|
||||
|
||||
} catch (ExpiredJwtException e) {
|
||||
log.error("Token已过期: {}", e.getMessage());
|
||||
|
||||
@@ -38,9 +38,9 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(jwtInterceptor)
|
||||
.addPathPatterns("/api/protected/**") // 保护这些路径
|
||||
.addPathPatterns("/api/**/**") // 保护这些路径
|
||||
.excludePathPatterns(Arrays.asList("/api/auth/precheckAndSendEmail", "/api/auth/register",
|
||||
"/api/auth/login", "/api/auth/forgotPwd")); // 排除登录接口
|
||||
"/api/auth/login", "/api/auth/forgotPwd", "/api/style/callback")); // 排除登录接口
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package com.aida.lanecarford.controller;
|
||||
|
||||
import com.aida.lanecarford.common.ApiResponse;
|
||||
import com.aida.lanecarford.dto.BaseRequest;
|
||||
import com.aida.lanecarford.service.CustomerService;
|
||||
import com.aida.lanecarford.vo.CustomerCheckInVO;
|
||||
import com.aida.lanecarford.vo.CustomerVO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 顾客控制器
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/customers")
|
||||
@@ -18,4 +20,14 @@ public class CustomerController {
|
||||
|
||||
private final CustomerService customerService;
|
||||
|
||||
@GetMapping("/checkIn")
|
||||
public ApiResponse<CustomerCheckInVO> customerCheckIn(@RequestParam String name, @RequestParam String email) {
|
||||
return ApiResponse.success(customerService.customerCheckIn(name, email));
|
||||
}
|
||||
|
||||
@PostMapping("/getAllCustomer")
|
||||
public ApiResponse<IPage<CustomerVO>> getAllCustomer(@Valid @RequestBody BaseRequest request) {
|
||||
return ApiResponse.success(customerService.getAllCustomer(request));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,7 @@ import com.aida.lanecarford.service.LoginService;
|
||||
import com.aida.lanecarford.vo.LoginVO;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
@@ -24,14 +21,15 @@ public class LoginController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ApiResponse<LoginVO> register(@Valid @RequestBody LoginRequest loginRequest) {
|
||||
return ApiResponse.success(loginService.register(loginRequest));
|
||||
@PostMapping("/registerOrLogin")
|
||||
public ApiResponse<LoginVO> registerOrLogin(@Valid @RequestBody LoginRequest loginRequest) {
|
||||
return ApiResponse.success(loginService.registerOrLogin(loginRequest));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ApiResponse<LoginVO> login(@Valid @RequestBody LoginRequest loginRequest) {
|
||||
return ApiResponse.success(loginService.login(loginRequest));
|
||||
@GetMapping("/logout")
|
||||
public ApiResponse<String> logout() {
|
||||
loginService.logout();
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/forgotPwd")
|
||||
@@ -40,5 +38,14 @@ public class LoginController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@GetMapping("/checkLoginStatus")
|
||||
public ApiResponse<String> checkLoginStatus() {
|
||||
boolean isLogin = loginService.checkLoginStatus();
|
||||
if (isLogin){
|
||||
return ApiResponse.success();
|
||||
} else {
|
||||
return ApiResponse.error("Please log in again.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.aida.lanecarford.controller;
|
||||
|
||||
import com.aida.lanecarford.dto.OutfitCallbackDTO;
|
||||
import com.aida.lanecarford.service.StyleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -11,11 +15,17 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/styles")
|
||||
@RequestMapping("/api/style")
|
||||
@RequiredArgsConstructor
|
||||
public class StyleController {
|
||||
|
||||
private final StyleService styleService;
|
||||
|
||||
@PostMapping("/callback")
|
||||
public void callback(@RequestBody OutfitCallbackDTO callbackDTO) {
|
||||
styleService.callback(callbackDTO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,14 +9,15 @@ import lombok.Data;
|
||||
public class LoginRequest {
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@NotBlank(message = "email cannot be empty")
|
||||
@Email(message = "Email format is incorrect.")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, message = "密码至少6位")
|
||||
@NotBlank(message = "password cannot be empty")
|
||||
@Size(min = 6, message = "Password must be at least 6 characters.")
|
||||
private String password;
|
||||
|
||||
@NotBlank(message = "operation type cannot be empty")
|
||||
private String operationType;
|
||||
|
||||
private String verifyCode;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.aida.lanecarford.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OutfitCallbackDTO {
|
||||
|
||||
private String outfit_id;
|
||||
|
||||
// 取值范围:ok || failed || stop
|
||||
private String status;
|
||||
|
||||
private String path;
|
||||
|
||||
private List<String> items;
|
||||
}
|
||||
28
src/main/java/com/aida/lanecarford/dto/RequestOutfitDTO.java
Normal file
28
src/main/java/com/aida/lanecarford/dto/RequestOutfitDTO.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.aida.lanecarford.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
public class RequestOutfitDTO {
|
||||
|
||||
// 顾客id
|
||||
@NotNull(message = "customer id cannot be empty")
|
||||
private Long customerId;
|
||||
|
||||
// 进店id
|
||||
@NotNull(message = "customer check-in id cannot be empty")
|
||||
private Long checkInId;
|
||||
|
||||
// 选择的设计师
|
||||
@NotNull(message = "please select a stylist")
|
||||
private String stylist;
|
||||
|
||||
@NotNull(message = "please select gender")
|
||||
private String gender;
|
||||
|
||||
// 生成数量
|
||||
private int num;
|
||||
|
||||
}
|
||||
@@ -11,9 +11,6 @@ import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 顾客实体类
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
|
||||
32
src/main/java/com/aida/lanecarford/entity/OutfitRequest.java
Normal file
32
src/main/java/com/aida/lanecarford/entity/OutfitRequest.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.aida.lanecarford.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OutfitRequest extends BaseEntity{
|
||||
|
||||
/**
|
||||
* 顾客id
|
||||
*/
|
||||
private Long customerId;
|
||||
|
||||
/**
|
||||
* 进店记录id
|
||||
*/
|
||||
private Long visitRecordId;
|
||||
|
||||
/**
|
||||
* 选择的设计师风格
|
||||
*/
|
||||
private String stylist;
|
||||
|
||||
/**
|
||||
* 选择的性别
|
||||
*/
|
||||
private String gender;
|
||||
|
||||
/**
|
||||
* 当前任务状态
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 风格配置实体类
|
||||
@@ -35,6 +35,11 @@ public class Style extends BaseEntity {
|
||||
@TableField("visit_record_id")
|
||||
private Long visitRecordId;
|
||||
|
||||
/**
|
||||
* 请求搭配id
|
||||
*/
|
||||
private Long outfitRequestId;
|
||||
|
||||
/**
|
||||
* 是否选中(0-未选中,1-已选中)
|
||||
*/
|
||||
@@ -53,6 +58,11 @@ public class Style extends BaseEntity {
|
||||
@TableField("python_request_id")
|
||||
private String pythonRequestId;
|
||||
|
||||
/**
|
||||
* 单品的唯一id
|
||||
*/
|
||||
private List<String> items;
|
||||
|
||||
/**
|
||||
* 生成状态(pending-等待中,processing-处理中,completed-已完成,failed-失败)
|
||||
*/
|
||||
|
||||
@@ -33,8 +33,8 @@ public class VisitRecord extends BaseEntity {
|
||||
/**
|
||||
* 导购ID
|
||||
*/
|
||||
@TableField("sales_id")
|
||||
private Long salesId;
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 进店日期
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aida.lanecarford.mapper;
|
||||
|
||||
import com.aida.lanecarford.entity.OutfitRequest;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface OutfitRequestMapper extends BaseMapper<OutfitRequest> {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.aida.lanecarford.service;
|
||||
|
||||
public interface ChatService {
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.aida.lanecarford.service;
|
||||
|
||||
import com.aida.lanecarford.dto.BaseRequest;
|
||||
import com.aida.lanecarford.entity.Customer;
|
||||
import com.aida.lanecarford.vo.CustomerCheckInVO;
|
||||
import com.aida.lanecarford.vo.CustomerVO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* 顾客服务接口
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface CustomerService extends IService<Customer> {
|
||||
|
||||
CustomerCheckInVO customerCheckIn(String name, String email);
|
||||
|
||||
IPage<CustomerVO> getAllCustomer(BaseRequest request);
|
||||
|
||||
}
|
||||
@@ -17,9 +17,11 @@ public interface LoginService extends IService<User> {
|
||||
|
||||
void preCheckAndSendEmail(LoginRequest loginRequest);
|
||||
|
||||
LoginVO register(LoginRequest loginRequest);
|
||||
LoginVO registerOrLogin(LoginRequest loginRequest);
|
||||
|
||||
LoginVO login(LoginRequest loginRequest);
|
||||
void logout();
|
||||
|
||||
void forgotPwd(LoginRequest loginRequest);
|
||||
|
||||
boolean checkLoginStatus();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.aida.lanecarford.service;
|
||||
|
||||
import com.aida.lanecarford.dto.OutfitCallbackDTO;
|
||||
import com.aida.lanecarford.entity.Style;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
@@ -11,4 +12,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
*/
|
||||
public interface StyleService extends IService<Style> {
|
||||
|
||||
void callback(OutfitCallbackDTO callbackDTO);
|
||||
|
||||
}
|
||||
@@ -8,13 +8,12 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 进店记录服务接口
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
public interface VisitRecordService extends IService<VisitRecord> {
|
||||
|
||||
Boolean delete(Long id);
|
||||
|
||||
List<LibraryVo> getByCustomerId(Long customerId);
|
||||
|
||||
VisitRecord addRecord(Long customerId, Long userId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.aida.lanecarford.service.impl;
|
||||
|
||||
import com.aida.lanecarford.service.ChatService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +1,78 @@
|
||||
package com.aida.lanecarford.service.impl;
|
||||
|
||||
import com.aida.lanecarford.common.security.context.UserContext;
|
||||
import com.aida.lanecarford.dto.BaseRequest;
|
||||
import com.aida.lanecarford.entity.Customer;
|
||||
import com.aida.lanecarford.entity.VisitRecord;
|
||||
import com.aida.lanecarford.mapper.CustomerMapper;
|
||||
import com.aida.lanecarford.service.CustomerService;
|
||||
import com.aida.lanecarford.service.VisitRecordService;
|
||||
import com.aida.lanecarford.util.CopyUtil;
|
||||
import com.aida.lanecarford.vo.CustomerCheckInVO;
|
||||
import com.aida.lanecarford.vo.CustomerVO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 顾客服务实现类
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements CustomerService {
|
||||
|
||||
@Resource
|
||||
private VisitRecordService visitRecordService;
|
||||
|
||||
// 选择顾客登录并添加入店记录
|
||||
public CustomerCheckInVO customerCheckIn(String name, String email) {
|
||||
// 1. 判断当前顾客信息在数据库中是否有存储
|
||||
LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Customer::getName, name).eq(Customer::getEmail, email);
|
||||
|
||||
Customer customer = getOne(queryWrapper);
|
||||
|
||||
// 2. 没有,获取连卡佛数据库中的顾客信息,查询有没有当前用户
|
||||
if (Objects.isNull(customer)) {
|
||||
// todo 从连卡佛数据库查数据
|
||||
// 先假设都找不到
|
||||
// throw new BusinessException("This customer does not currently have a registered VIP account.");
|
||||
// 如果找到了,则添加到数据库
|
||||
// 3. 添加当前顾客到本系统数据库
|
||||
customer = new Customer();
|
||||
customer.setName(name);
|
||||
customer.setEmail(email);
|
||||
customer.setCreatedTime(LocalDateTime.now());
|
||||
|
||||
save(customer);
|
||||
}
|
||||
|
||||
// 4. 添加入店记录
|
||||
VisitRecord visitRecord = visitRecordService.addRecord(customer.getId(), UserContext.getUserHolder().getId());
|
||||
|
||||
return new CustomerCheckInVO(customer.getId(), visitRecord.getId());
|
||||
}
|
||||
|
||||
// 获取所有的顾客名单
|
||||
public IPage<CustomerVO> getAllCustomer(BaseRequest request) {
|
||||
LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.select(Customer::getName, Customer::getEmail);
|
||||
|
||||
Page<Customer> page = page(new Page<>(request.getCurrent(), request.getSize()), queryWrapper);
|
||||
|
||||
return page.convert(customer -> {
|
||||
if (customer != null) {
|
||||
return CopyUtil.copyObject(customer, CustomerVO.class);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.aida.lanecarford.common.constant.RedisURIConstants;
|
||||
import com.aida.lanecarford.common.enums.AuthenticationOperationTypeEnum;
|
||||
import com.aida.lanecarford.common.enums.LanguageEnum;
|
||||
import com.aida.lanecarford.common.security.JwtUtil;
|
||||
import com.aida.lanecarford.common.security.context.UserContext;
|
||||
import com.aida.lanecarford.dto.LoginRequest;
|
||||
import com.aida.lanecarford.entity.User;
|
||||
import com.aida.lanecarford.exception.BusinessException;
|
||||
@@ -135,9 +136,25 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements L
|
||||
}
|
||||
}
|
||||
|
||||
// 注册
|
||||
@Override
|
||||
public LoginVO register(LoginRequest loginRequest) {
|
||||
public LoginVO registerOrLogin(LoginRequest loginRequest) {
|
||||
LoginVO loginVO;
|
||||
|
||||
// 2. 获取当前的操作类型
|
||||
AuthenticationOperationTypeEnum operationTypeEnum = AuthenticationOperationTypeEnum.of(loginRequest.getOperationType());
|
||||
|
||||
// 3. 根据操作类型进行后续处理
|
||||
loginVO = switch (operationTypeEnum) {
|
||||
case REGISTER -> register(loginRequest);
|
||||
case LOGIN -> login(loginRequest);
|
||||
default -> throw new BusinessException("Unknown authentication operation type.");
|
||||
};
|
||||
|
||||
return loginVO;
|
||||
}
|
||||
|
||||
// 注册
|
||||
private LoginVO register(LoginRequest loginRequest) {
|
||||
// 1. 验证邮箱
|
||||
checkVerifyCode(loginRequest.getVerifyCode(), REGISTER.name(), loginRequest.getEmail());
|
||||
|
||||
@@ -146,7 +163,7 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements L
|
||||
queryWrapper.lambda().eq(User::getEmail, loginRequest.getEmail());
|
||||
|
||||
User user = getOne(queryWrapper);
|
||||
if (Objects.isNull(user)){
|
||||
if (Objects.isNull(user)) {
|
||||
user = new User();
|
||||
user.setUsername(loginRequest.getName());
|
||||
user.setEmail(loginRequest.getEmail());
|
||||
@@ -157,15 +174,14 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements L
|
||||
}
|
||||
|
||||
// 3. 生成token,添加到缓存,返回
|
||||
String token = jwtUtil.generateToken(new AuthPrincipalVO(user.getId(), user.getUsername(), user.getLanguage()));
|
||||
String token = jwtUtil.generateToken(new AuthPrincipalVO(user.getId(), user.getUsername()));
|
||||
cacheUtil.setToken(user.getId(), token);
|
||||
|
||||
return new LoginVO(token, user, null);
|
||||
}
|
||||
|
||||
// 登录
|
||||
@Override
|
||||
public LoginVO login(LoginRequest loginRequest) {
|
||||
private LoginVO login(LoginRequest loginRequest) {
|
||||
// 1. 验证邮箱
|
||||
checkVerifyCode(loginRequest.getVerifyCode(), LOGIN.name(), loginRequest.getEmail());
|
||||
|
||||
@@ -176,12 +192,25 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements L
|
||||
User user = getOne(queryWrapper);
|
||||
|
||||
// 3. 生成token,添加到缓存,返回
|
||||
String token = jwtUtil.generateToken(new AuthPrincipalVO(user.getId(), user.getUsername(), user.getLanguage()));
|
||||
String token = jwtUtil.generateToken(new AuthPrincipalVO(user.getId(), user.getUsername()));
|
||||
cacheUtil.setToken(user.getId(), token);
|
||||
|
||||
return new LoginVO(token, user, null);
|
||||
}
|
||||
|
||||
// 登出
|
||||
@Override
|
||||
public void logout() {
|
||||
AuthPrincipalVO userHolder = UserContext.getUserHolder();
|
||||
|
||||
if (Objects.nonNull(userHolder)) {
|
||||
Object token = cacheUtil.getToken(userHolder.getId());
|
||||
if (Objects.nonNull(token)) {
|
||||
cacheUtil.deleteToken(userHolder.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 忘记密码
|
||||
@Override
|
||||
public void forgotPwd(LoginRequest loginRequest) {
|
||||
@@ -196,5 +225,22 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements L
|
||||
update(updateWrapper);
|
||||
}
|
||||
|
||||
// 检查登录状态
|
||||
public boolean checkLoginStatus() {
|
||||
AuthPrincipalVO userHolder = UserContext.getUserHolder();
|
||||
|
||||
if (Objects.nonNull(userHolder)) {
|
||||
Object token = cacheUtil.getToken(userHolder.getId());
|
||||
return Objects.nonNull(token);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 谷歌登录
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,192 @@
|
||||
package com.aida.lanecarford.service.impl;
|
||||
|
||||
import com.aida.lanecarford.common.constant.CommonConstants;
|
||||
import com.aida.lanecarford.common.constant.RedisURIConstants;
|
||||
import com.aida.lanecarford.common.enums.StatusEnum;
|
||||
import com.aida.lanecarford.common.enums.StylistPathEnum;
|
||||
import com.aida.lanecarford.dto.OutfitCallbackDTO;
|
||||
import com.aida.lanecarford.dto.RequestOutfitDTO;
|
||||
import com.aida.lanecarford.entity.OutfitRequest;
|
||||
import com.aida.lanecarford.entity.Style;
|
||||
import com.aida.lanecarford.exception.BusinessException;
|
||||
import com.aida.lanecarford.mapper.OutfitRequestMapper;
|
||||
import com.aida.lanecarford.mapper.StyleMapper;
|
||||
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.vo.OutfitResultVO;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 风格配置服务实现类
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements StyleService {
|
||||
|
||||
@Resource
|
||||
private CacheUtil cacheUtil;
|
||||
@Resource
|
||||
private MinioUtil minioUtil;
|
||||
@Resource
|
||||
private OutfitRequestMapper outfitRequestMapper;
|
||||
|
||||
|
||||
// 请求获取搭配
|
||||
public void requestOutfit(RequestOutfitDTO requestOutfitDTO) {
|
||||
// 请求需要顾客id, 生成的数量,风格
|
||||
|
||||
StylistPathEnum stylistPathEnum = StylistPathEnum.of(requestOutfitDTO.getStylist());
|
||||
Map<String, Object> params = setRequestOutfitParams(requestOutfitDTO.getCustomerId(), requestOutfitDTO.getNum(), stylistPathEnum.getPath());
|
||||
|
||||
OutfitRequest outfitRequest = new OutfitRequest();
|
||||
outfitRequest.setCustomerId(requestOutfitDTO.getCustomerId());
|
||||
outfitRequest.setVisitRecordId(requestOutfitDTO.getCheckInId());
|
||||
outfitRequest.setStylist(requestOutfitDTO.getStylist());
|
||||
outfitRequest.setGender(requestOutfitDTO.getGender());
|
||||
outfitRequestMapper.insert(outfitRequest);
|
||||
|
||||
|
||||
String response = SendRequestUtil.sendPost(CommonConstants.REQUEST_OUTFIT, JSON.toJSONString(params));
|
||||
if (Objects.isNull(response)) {
|
||||
outfitRequest.setStatus(StatusEnum.FAILED.name());
|
||||
outfitRequestMapper.updateById(outfitRequest);
|
||||
throw new BusinessException("System error (External interface failure)");
|
||||
}
|
||||
// todo 接收返回的requestID
|
||||
// JSONObject.parse(response, )
|
||||
|
||||
List<String> requestIds = new ArrayList<>();
|
||||
|
||||
for(String requestId : requestIds) {
|
||||
// 生成需要 6~8s, 所以这里可以先请求再存储
|
||||
Style style = new Style();
|
||||
style.setCustomerId(requestOutfitDTO.getCustomerId());
|
||||
style.setVisitRecordId(requestOutfitDTO.getCheckInId());
|
||||
style.setOutfitRequestId(outfitRequest.getId());
|
||||
style.setIsSelected(0);
|
||||
style.setPythonRequestId(requestId);
|
||||
style.setGenerationStatus(StatusEnum.PENDING.name());
|
||||
style.setCreatedTime(LocalDateTime.now());
|
||||
|
||||
save(style);
|
||||
|
||||
String key = RedisURIConstants.outfitResultCache + requestId;
|
||||
OutfitResultVO outfitResultVO = new OutfitResultVO(style.getId(), requestId, StatusEnum.PENDING.name());
|
||||
cacheUtil.setCache(key, outfitResultVO, RedisURIConstants.verifyCodeTimeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Map<String, Object> setRequestOutfitParams(Long customerId, int num, String stylistPath) {
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("user_id", customerId);
|
||||
params.put("num_outfits", num);
|
||||
params.put("stylist_path", stylistPath);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// 搭配完成后的回调通知处理
|
||||
public void callback(OutfitCallbackDTO callbackDTO) {
|
||||
if (Objects.isNull(callbackDTO.getStatus())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("ok".equals(callbackDTO.getStatus()) || "stop".equals(callbackDTO.getStatus())) {
|
||||
// 1. 判断path是否为空,是 -> 不做任何处理
|
||||
if (StringUtil.isNullOrEmpty(callbackDTO.getPath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(callbackDTO.getOutfit_id())) {
|
||||
log.error("回调参数中,outfit_id为空");
|
||||
return;
|
||||
}
|
||||
|
||||
String requestId = callbackDTO.getOutfit_id();
|
||||
|
||||
// 2. 获取outfit_id,查询数据库
|
||||
String key = RedisURIConstants.outfitResultCache + requestId;
|
||||
Object outfitResult = cacheUtil.getCache(key);
|
||||
if (Objects.isNull(outfitResult)) {
|
||||
log.error("未知搭配请求");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3.更新path, items, 状态
|
||||
// 由于数据变化较频繁,考虑存到redis
|
||||
if (outfitResult instanceof OutfitResultVO) {
|
||||
((OutfitResultVO) outfitResult).setPath(minioUtil.getPresignedUrl(callbackDTO.getPath(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
((OutfitResultVO) outfitResult).setStatus(StatusEnum.SUCCEEDED.name());
|
||||
cacheUtil.setCache(key, outfitResult, RedisURIConstants.verifyCodeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成结束或失败时更新数据库
|
||||
if ("stop".equals(callbackDTO.getStatus()) || "failed".equals(callbackDTO.getStatus())) {
|
||||
String requestId = callbackDTO.getOutfit_id();
|
||||
|
||||
LambdaQueryWrapper<Style> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Style::getPythonRequestId, requestId);
|
||||
Style outfit = getOne(queryWrapper);
|
||||
if (Objects.isNull(outfit)) {
|
||||
log.error("未知搭配请求");
|
||||
return;
|
||||
}
|
||||
|
||||
String status = "stop".equals(callbackDTO.getStatus()) ? StatusEnum.SUCCEEDED.name() : StatusEnum.FAILED.name();
|
||||
outfit.setGenerationStatus(status);
|
||||
outfit.setStyleImageUrl(callbackDTO.getPath());
|
||||
outfit.setItems(callbackDTO.getItems());
|
||||
outfit.setUpdatedTime(LocalDateTime.now());
|
||||
|
||||
updateById(outfit);
|
||||
}
|
||||
}
|
||||
|
||||
public List<OutfitResultVO> getOutfitResult(List<String> requestIDs) {
|
||||
ArrayList<OutfitResultVO> resultVOS = new ArrayList<>();
|
||||
// 优先从redis中获取结果,没有再从数据库中查询
|
||||
for (String requestID : requestIDs) {
|
||||
String key = RedisURIConstants.outfitResultCache + requestID;
|
||||
Object outfit = cacheUtil.getCache(key);
|
||||
|
||||
if (Objects.isNull(outfit)){
|
||||
break;
|
||||
}
|
||||
resultVOS.add((OutfitResultVO) outfit);
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<Style> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(Style::getPythonRequestId, requestIDs);
|
||||
|
||||
List<Style> list = list(queryWrapper);
|
||||
if (!list.isEmpty()) {
|
||||
for (Style style : list) {
|
||||
OutfitResultVO outfitResultVO = new OutfitResultVO();
|
||||
outfitResultVO.setId(style.getId());
|
||||
outfitResultVO.setRequestId(style.getPythonRequestId());
|
||||
outfitResultVO.setStatus(style.getGenerationStatus());
|
||||
if (!StringUtil.isNullOrEmpty(style.getStyleImageUrl())) {
|
||||
outfitResultVO.setPath(minioUtil.getPresignedUrl(style.getStyleImageUrl(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
}
|
||||
resultVOS.add(outfitResultVO);
|
||||
}
|
||||
}
|
||||
return resultVOS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,17 +13,19 @@ import com.aida.lanecarford.vo.LibraryVo;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 进店记录服务实现类
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @since 2024-01-01
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VisitRecordServiceImpl extends ServiceImpl<VisitRecordMapper, VisitRecord> implements VisitRecordService {
|
||||
@@ -87,4 +89,36 @@ public class VisitRecordServiceImpl extends ServiceImpl<VisitRecordMapper, Visit
|
||||
}
|
||||
return libraryVos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitRecord addRecord(Long customerId, Long userId) {
|
||||
// 参数验证
|
||||
if (customerId == null) {
|
||||
throw new IllegalArgumentException("customer ID cannot be empty");
|
||||
}
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("user ID cannot be empty");
|
||||
}
|
||||
|
||||
try {
|
||||
VisitRecord visitRecord = new VisitRecord();
|
||||
visitRecord.setCustomerId(customerId);
|
||||
visitRecord.setUserId(userId);
|
||||
visitRecord.setVisitDate(LocalDate.now());
|
||||
visitRecord.setVisitTime(LocalDateTime.now());
|
||||
visitRecord.setCreatedTime(LocalDateTime.now());
|
||||
|
||||
save(visitRecord);
|
||||
|
||||
log.info("顾客进店记录保存成功 recordId={}, customerId={}", visitRecord.getId(), customerId);
|
||||
return visitRecord;
|
||||
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
log.error("数据完整性约束违反 customerId={}, userId={}", customerId, userId, e);
|
||||
throw new BusinessException("Save failed: Data constraint violation.");
|
||||
} catch (Exception e) {
|
||||
log.error("保存进店记录失败 customerId={}, userId={}", customerId, userId, e);
|
||||
throw new BusinessException("Save failed: System exception.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,10 @@ public class CacheUtil {
|
||||
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void setCache(String key, Object value, Long timeout) {
|
||||
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Object getCache(String key) {
|
||||
return redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
58
src/main/java/com/aida/lanecarford/util/SendRequestUtil.java
Normal file
58
src/main/java/com/aida/lanecarford/util/SendRequestUtil.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.aida.lanecarford.util;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.aida.lanecarford.exception.BusinessException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SendRequestUtil {
|
||||
|
||||
public static String sendPost(String url, String requestBodyStr){
|
||||
int status;
|
||||
String body;
|
||||
try (HttpResponse execute = HttpRequest.post(url)
|
||||
.header("Content-Type", "application/json") // 必须设置 Content-Type
|
||||
.body(requestBodyStr) // Hutool 会自动处理 JSON 序列化
|
||||
.timeout(180000) // 设置超时(毫秒)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.warn("请求失败,接口地址:{}, 返回状态码为 : {}, body: {}", url, status, body);
|
||||
// throw new BusinessException("System error (External interface failure)");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String sendGet(String url, Map<String, Object> params) {
|
||||
int status;
|
||||
String body;
|
||||
try (HttpResponse execute = HttpRequest.get(url)
|
||||
.form(params) // 直接传入Map,Hutool会正确处理
|
||||
.timeout(180000)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("请求发生异常: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
log.warn("请求失败,接口地址:{}, 返回状态码为 : {}, body: {}", url, status, body);
|
||||
throw new BusinessException("System error (External interface failure)");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -9,9 +9,9 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class AuthPrincipalVO {
|
||||
|
||||
// 用户id
|
||||
private Long id;
|
||||
|
||||
// 用户名
|
||||
private String username;
|
||||
|
||||
private String language;
|
||||
}
|
||||
|
||||
13
src/main/java/com/aida/lanecarford/vo/CustomerCheckInVO.java
Normal file
13
src/main/java/com/aida/lanecarford/vo/CustomerCheckInVO.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.aida.lanecarford.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CustomerCheckInVO {
|
||||
|
||||
private Long customerId;
|
||||
|
||||
private Long checkInId;
|
||||
}
|
||||
11
src/main/java/com/aida/lanecarford/vo/CustomerVO.java
Normal file
11
src/main/java/com/aida/lanecarford/vo/CustomerVO.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.aida.lanecarford.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CustomerVO {
|
||||
|
||||
private String name;
|
||||
|
||||
private String email;
|
||||
}
|
||||
23
src/main/java/com/aida/lanecarford/vo/OutfitResultVO.java
Normal file
23
src/main/java/com/aida/lanecarford/vo/OutfitResultVO.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.aida.lanecarford.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class OutfitResultVO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String requestId;
|
||||
|
||||
private String path;
|
||||
|
||||
private String status;
|
||||
|
||||
public OutfitResultVO(Long id, String requestId, String status) {
|
||||
this.id = id;
|
||||
this.requestId = requestId;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user