This commit is contained in:
litianxiang
2026-05-15 15:09:21 +08:00
commit ac7de27099
69 changed files with 2696 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package com.aida.buyer.common.constants;
public class BuyerConstants {
public static final Integer FAVORITE_STATUS_ACTIVE = 1;
public static final Integer FAVORITE_STATUS_REMOVED = 0;
public static final Integer CART_STATUS_ACTIVE = 1;
public static final Integer CART_STATUS_REMOVED = 0;
public static final Integer ADDRESS_STATUS_DEFAULT = 1;
public static final Integer ADDRESS_STATUS_NORMAL = 0;
}

View File

@@ -0,0 +1,8 @@
package com.aida.buyer.common.constants;
public class CommonConstants {
public static final int MINIO_PATH_TIMEOUT = 7 * 24 * 60 * 60; // minio图片临时访问地址 7 天过期second
public static final int TOKEN_EXPIRE_TIME = 7 * 24; // token 7 天过期Hour
}

View File

@@ -0,0 +1,19 @@
package com.aida.buyer.common.constants;
public class MinioFileConstants {
public static final String PATH_SEPARATOR = "/";
public static final String FILE_TYPE_PNG = "png";
public static final String FILE_TYPE_JPG = "jpg";
public static final String FILE_TYPE_JPEG = "jpeg";
public static final String FILE_TYPE_GIF = "gif";
public static final String FILE_TYPE_WEBP = "webp";
public static final String FILE_TYPE_PDF = "pdf";
public static final String CONTENT_TYPE_PNG = "image/png";
public static final String CONTENT_TYPE_JPG = "image/jpeg";
public static final String CONTENT_TYPE_GIF = "image/gif";
public static final String CONTENT_TYPE_WEBP = "image/webp";
public static final String CONTENT_TYPE_PDF = "application/pdf";
}

View File

@@ -0,0 +1,12 @@
package com.aida.buyer.common.constants;
public class StatusConstants {
public static final Integer ENABLE = 1;
public static final Integer DISABLE = 0;
public static final Integer DELETE = 1;
public static final Integer NOT_DELETE = 0;
public static final Integer AUDIT_PENDING = 0;
public static final Integer AUDIT_APPROVED = 1;
public static final Integer AUDIT_REJECTED = 2;
}

View File

@@ -0,0 +1,54 @@
package com.aida.buyer.common.context;
import com.aida.buyer.common.exception.UnauthorizedException;
import com.aida.buyer.model.vo.AuthPrincipalVo;
public class UserContext {
private static final ThreadLocal<AuthPrincipalVo> userHolder = new ThreadLocal<>();
private static final ThreadLocal<Boolean> optionalAuth = ThreadLocal.withInitial(() -> false);
public static void setUserHolder(AuthPrincipalVo authPrincipalVo) {
userHolder.set(authPrincipalVo);
}
public static void setOptionalAuth(boolean value) {
optionalAuth.set(value);
}
public static AuthPrincipalVo getUserHolder() {
AuthPrincipalVo holder = userHolder.get();
if (holder == null) {
if (optionalAuth.get()) {
return null;
}
throw new UnauthorizedException("Gateway token verification failed");
}
if (!"AIDA".equals(holder.getSource())) {
throw new UnauthorizedException("Gateway token verification failed");
}
return holder;
}
public static void delete() {
userHolder.remove();
optionalAuth.remove();
}
public static Long getUserId() {
return getUserHolder() == null ? null : getUserHolder().getId();
}
public static Long getBuyerId() {
AuthPrincipalVo holder = userHolder.get();
if (holder == null) {
if (optionalAuth.get()) {
return null;
}
throw new UnauthorizedException("Gateway token verification failed");
}
if (!"BUYER".equals(holder.getSource())) {
throw new UnauthorizedException("Gateway token verification failed");
}
return holder.getId();
}
}

View File

@@ -0,0 +1,37 @@
package com.aida.buyer.common.exception;
import com.aida.buyer.common.result.ResultEnum;
import lombok.Getter;
@Getter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final Integer code;
private final String msg;
public BusinessException(ResultEnum resultEnum) {
super(resultEnum.getMessage());
this.code = resultEnum.getCode();
this.msg = resultEnum.getMessage();
}
public BusinessException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
public BusinessException(String message) {
super(message);
this.code = -1;
this.msg = message;
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
this.code = -1;
this.msg = message;
}
}

View File

@@ -0,0 +1,53 @@
package com.aida.buyer.common.exception;
import com.aida.buyer.common.result.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<Response<Void>> handleUnauthorizedException(UnauthorizedException e) {
log.error("Unauthorized: {}", e.getMessage());
return new ResponseEntity<>(
Response.fail(401, e.getMessage()),
HttpStatus.UNAUTHORIZED
);
}
@ExceptionHandler(BusinessException.class)
public Response<Void> handleBusinessException(BusinessException e) {
return Response.fail(e.getCode(), e.getMsg());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.findFirst()
.orElse("validation error");
return Response.fail(-2, message);
}
@ExceptionHandler(BindException.class)
public Response<Void> handleBindException(BindException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.findFirst()
.orElse("bind error");
return Response.fail(-2, message);
}
@ExceptionHandler(Exception.class)
public Response<Void> handleException(Exception e) {
log.error("system error: ", e);
return Response.error("系统繁忙");
}
}

View File

@@ -0,0 +1,14 @@
package com.aida.buyer.common.exception;
public class UnauthorizedException extends RuntimeException {
private static final long serialVersionUID = 1L;
public UnauthorizedException(String message) {
super(message);
}
public UnauthorizedException() {
super("Gateway token verification failed");
}
}

View File

@@ -0,0 +1,79 @@
package com.aida.buyer.common.interceptor;
import com.aida.buyer.common.context.UserContext;
import com.aida.buyer.config.GatewayAuthProperties;
import com.aida.buyer.model.vo.AuthPrincipalVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Component
public class UserContextInterceptor implements HandlerInterceptor {
private static final String USER_ID_HEADER = "X-User-Id";
private static final String USER_INFO_HEADER = "X-User-Info";
private final ObjectMapper objectMapper = new ObjectMapper();
private final AntPathMatcher pathMatcher = new AntPathMatcher();
private final List<String> localOptionalAuthPaths;
public UserContextInterceptor(GatewayAuthProperties gatewayAuthProperties) {
this.localOptionalAuthPaths = gatewayAuthProperties.getOptionalAuthPaths().stream()
.map(this::toLocalPath)
.collect(Collectors.toList());
log.info("Local optional auth paths: {}", localOptionalAuthPaths);
}
private String toLocalPath(String gatewayPath) {
if (gatewayPath == null) {
return gatewayPath;
}
if (gatewayPath.startsWith("/buyer")) {
String local = gatewayPath.substring("/buyer".length());
return local.isEmpty() ? "/" : local;
}
return gatewayPath;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String userInfoJson = request.getHeader(USER_INFO_HEADER);
if (userInfoJson != null && !userInfoJson.isBlank()) {
try {
AuthPrincipalVo principal = objectMapper.readValue(userInfoJson, AuthPrincipalVo.class);
UserContext.setUserHolder(principal);
} catch (Exception e) {
log.warn("Failed to parse X-User-Info header: {}", e.getMessage());
}
} else if (isOptionalAuthPath(request.getRequestURI())) {
UserContext.setOptionalAuth(true);
}
return true;
}
private boolean isOptionalAuthPath(String requestUri) {
if (localOptionalAuthPaths == null || localOptionalAuthPaths.isEmpty()) {
return false;
}
for (String pattern : localOptionalAuthPaths) {
if (pathMatcher.match(pattern, requestUri)) {
return true;
}
}
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
UserContext.delete();
}
}

View File

@@ -0,0 +1,29 @@
package com.aida.buyer.common.result;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class PageResponse<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Long page;
private Long size;
private Long pages;
private Long total;
private List<T> content;
public static <T> PageResponse<T> success(IPage<T> page) {
PageResponse<T> response = new PageResponse<>();
response.setPage(page.getCurrent());
response.setSize(page.getSize());
response.setPages(page.getPages());
response.setTotal(page.getTotal());
response.setContent(page.getRecords());
return response;
}
}

View File

@@ -0,0 +1,48 @@
package com.aida.buyer.common.result;
import lombok.Data;
import java.io.Serializable;
@Data
public class Response<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Integer errCode;
private String errMsg;
private T data;
public static <T> Response<T> success() {
return success(null);
}
public static <T> Response<T> success(T data) {
Response<T> response = new Response<>();
response.setErrCode(ResultEnum.SUCCESS.getCode());
response.setErrMsg(ResultEnum.SUCCESS.getMessage());
response.setData(data);
return response;
}
public static <T> Response<T> fail(String errMsg) {
Response<T> response = new Response<>();
response.setErrCode(ResultEnum.FAIL.getCode());
response.setErrMsg(errMsg);
return response;
}
public static <T> Response<T> fail(Integer errCode, String errMsg) {
Response<T> response = new Response<>();
response.setErrCode(errCode);
response.setErrMsg(errMsg);
return response;
}
public static <T> Response<T> error(String errMsg) {
Response<T> response = new Response<>();
response.setErrCode(ResultEnum.FAIL.getCode());
response.setErrMsg(errMsg);
return response;
}
}

View File

@@ -0,0 +1,19 @@
package com.aida.buyer.common.result;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ResultEnum {
SUCCESS(0, "success"),
FAIL(-1, "fail"),
PARAMETER_ERROR(-2, "parameter error"),
NO_LOGIN(-100, "no login"),
NO_PERMISSION(-200, "no permission"),
ACCOUNT_LOCK(-300, "account locked");
private final Integer code;
private final String message;
}