1.初始化版本,2.引入thirdpart模块,引入canteen模块,初始化菜品分类,spu

This commit is contained in:
zjf committed 2024-12-06 16:54:30 +08:00
commit aff77150d6
1595 files changed
+168833

No files matched your search

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sentinel;
import com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.RequestOriginParser;
import com.bsd.pjcloud.common.sentinel.feign.PjcloudSentinelFeign;
import com.bsd.pjcloud.common.sentinel.handle.GlobalBizExceptionHandler;
import com.bsd.pjcloud.common.sentinel.handle.PjcloudUrlBlockHandler;
import com.bsd.pjcloud.common.sentinel.parser.PjcloudHeaderRequestOriginParser;
import feign.Feign;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* @author pjcloud
* @date 2020-02-12
* <p>
* sentinel 配置
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
public class PjcloudSentinelAutoConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.cloud.openfeign.sentinel.enabled",matchIfMissing = true)
public Feign.Builder feignSentinelBuilder() {
return PjcloudSentinelFeign.builder();
}
@Bean
@ConditionalOnMissingBean
/**
* 创建一个块异常处理器实例
* 如果没有定义块异常处理器,则创建一个PjcloudUrlBlockHandler实例
*/
public BlockExceptionHandler blockExceptionHandler() {
return new PjcloudUrlBlockHandler();
}
@Bean
@ConditionalOnMissingBean
/**
* 创建一个请求源解析器实例
* 如果没有定义请求源解析器,则创建一个PjcloudHeaderRequestOriginParser实例
*/
public RequestOriginParser requestOriginParser() {
return new PjcloudHeaderRequestOriginParser();
}
/**
* 创建一个全局业务异常处理器实例
* 用于处理全局的业务异常
*/
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public GlobalBizExceptionHandler globalBizExceptionHandler() {
return new GlobalBizExceptionHandler();
}
}
@@ -0,0 +1,129 @@
package com.bsd.pjcloud.common.sentinel.feign;
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
import feign.Contract;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Target;
import org.springframework.beans.BeansException;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 支持自动降级注入 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelFeign}
*
* @author pjcloud
* @date 2020/6/9
*/
public final class PjcloudSentinelFeign {
private PjcloudSentinelFeign() {
}
public static PjcloudSentinelFeign.Builder builder() {
return new PjcloudSentinelFeign.Builder();
}
public static final class Builder extends Feign.Builder implements ApplicationContextAware {
private Contract contract = new Contract.Default();
private ApplicationContext applicationContext;
private FeignClientFactory feignClientFactory;
@Override
public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
throw new UnsupportedOperationException();
}
@Override
public PjcloudSentinelFeign.Builder contract(Contract contract) {
this.contract = contract;
return this;
}
@Override
public Feign internalBuild() {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
// 查找 FeignClient 上的 降级策略
FeignClient feignClient = AnnotationUtils.findAnnotation(target.type(), FeignClient.class);
Class<?> fallback = feignClient.fallback();
Class<?> fallbackFactory = feignClient.fallbackFactory();
String beanName = feignClient.contextId();
if (!StringUtils.hasText(beanName)) {
beanName = feignClient.name();
}
Object fallbackInstance;
FallbackFactory<?> fallbackFactoryInstance;
if (void.class != fallback) {
fallbackInstance = getFromContext(beanName, "fallback", fallback, target.type());
return new PjcloudSentinelInvocationHandler(target, dispatch,
new FallbackFactory.Default(fallbackInstance));
}
if (void.class != fallbackFactory) {
fallbackFactoryInstance = (FallbackFactory<?>) getFromContext(beanName, "fallbackFactory",
fallbackFactory, FallbackFactory.class);
return new PjcloudSentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);
}
return new PjcloudSentinelInvocationHandler(target, dispatch);
}
private Object getFromContext(String name, String type, Class<?> fallbackType, Class<?> targetType) {
Object fallbackInstance = feignClientFactory.getInstance(name, fallbackType);
if (fallbackInstance == null) {
throw new IllegalStateException(String
.format("No %s instance of type %s found for feign client %s", type, fallbackType, name));
}
if (!targetType.isAssignableFrom(fallbackType)) {
throw new IllegalStateException(String.format(
"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
type, fallbackType, targetType, name));
}
return fallbackInstance;
}
});
super.contract(new SentinelContractHolder(contract));
return super.internalBuild();
}
private Object getFieldValue(Object instance, String fieldName) {
Field field = ReflectionUtils.findField(instance.getClass(), fieldName);
field.setAccessible(true);
try {
return field.get(instance);
}
catch (IllegalAccessException e) {
// ignore
}
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.feignClientFactory = this.applicationContext.getBean(FeignClientFactory.class);
}
}
}
@@ -0,0 +1,174 @@
package com.bsd.pjcloud.common.sentinel.feign;
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
import com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.bsd.pjcloud.common.core.util.R;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.MethodMetadata;
import feign.Target;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import static feign.Util.checkNotNull;
/**
* 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler} 支持自动降级注入
*
* @author pjcloud
* @date 2020/6/9
*/
@Slf4j
public class PjcloudSentinelInvocationHandler implements InvocationHandler {
public static final String EQUALS = "equals";
public static final String HASH_CODE = "hashCode";
public static final String TO_STRING = "toString";
private final Target<?> target;
private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
private FallbackFactory fallbackFactory;
private Map<Method, Method> fallbackMethodMap;
PjcloudSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
FallbackFactory fallbackFactory) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
this.fallbackMethodMap = toFallbackMethod(dispatch);
}
PjcloudSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (EQUALS.equals(method.getName())) {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
}
catch (IllegalArgumentException e) {
return false;
}
}
else if (HASH_CODE.equals(method.getName())) {
return hashCode();
}
else if (TO_STRING.equals(method.getName())) {
return toString();
}
Object result;
InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);
// only handle by HardCodedTarget
if (target instanceof Target.HardCodedTarget) {
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
.get(hardCodedTarget.type().getName() + Feign.configKey(hardCodedTarget.type(), method));
// resource default is HttpMethod:protocol://url
if (methodMetadata == null) {
result = methodHandler.invoke(args);
}
else {
String resourceName = methodMetadata.template().method().toUpperCase() + ":" + hardCodedTarget.url()
+ methodMetadata.template().path();
Entry entry = null;
try {
ContextUtil.enter(resourceName);
entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
result = methodHandler.invoke(args);
}
catch (Throwable ex) {
// fallback handle
if (!BlockException.isBlockException(ex)) {
Tracer.trace(ex);
}
if (fallbackFactory != null) {
try {
Object fallbackResult = fallbackMethodMap.get(method)
.invoke(fallbackFactory.create(ex), args);
return fallbackResult;
}
catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an
// interface
throw new AssertionError(e);
}
catch (InvocationTargetException e) {
throw new AssertionError(e.getCause());
}
}
else if (R.class == method.getReturnType()) {
log.error("feign 服务间调用异常", ex);
return R.failed(ex.getLocalizedMessage());
}
else {
throw ex;
}
}
finally {
if (entry != null) {
entry.exit(1, args);
}
ContextUtil.exit();
}
}
}
else {
// other target type using default strategy
result = methodHandler.invoke(args);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SentinelInvocationHandler) {
PjcloudSentinelInvocationHandler other = (PjcloudSentinelInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
Map<Method, Method> result = new LinkedHashMap<>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
}
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sentinel.handle;
import com.alibaba.csp.sentinel.Tracer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.bsd.pjcloud.common.core.util.R;
import feign.FeignException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import java.sql.SQLException;
import java.util.List;
/**
* @author pjcloud
* @date 2020-06-29
*/
@Slf4j
@RestControllerAdvice
public class GlobalBizExceptionHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* 全局异常.
*
* @param e the e
* @return R
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleGlobalException(Exception e) {
log.error("全局异常信息 ex={}", e.getMessage(), e);
// 业务异常交由 sentinel 记录
Tracer.trace(e);
return R.failed(e.getLocalizedMessage());
}
/**
* SQLException Exception
*
* @param exception 数据库调用异常
* @return R
*/
@ExceptionHandler({SQLException.class})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleSQLException(SQLException exception) {
log.error("数据库调用异常 ex={}", exception.getMessage(), exception);
return R.failed("数据库调用异常,请联系管理员处理");
}
@SneakyThrows
@ExceptionHandler(FeignException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleGlobalException(FeignException e) {
log.error("全局异常信息 ex={}", e.getMessage(), e);
// 业务异常交由 sentinel 记录
Tracer.trace(e);
if (e.responseBody().isPresent()) {
return objectMapper.readValue(e.responseBody().get().array(), R.class);
}
return R.failed(e.getLocalizedMessage());
}
/**
* AccessDeniedException
*
* @param e the e
* @return R
*/
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public R handleAccessDeniedException(AccessDeniedException e) {
log.error("拒绝授权异常信息 ex={}", e.getMessage());
return R.failed("权限不足,不允许访问");
}
/**
* validation Exception
*
* @param exception
* @return R
*/
@ExceptionHandler({BindException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleBodyValidException(BindException exception) {
List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
// 插入log 的逻辑
return R.failed(String.format("%s %s", fieldErrors.get(0).getField(), fieldErrors.get(0).getDefaultMessage()));
}
/**
* 避免 404 重定向到 /error 导致NPE ,ignore-url 需要配置对应端点
*
* @return R
*/
@DeleteMapping("/error")
@ResponseStatus(HttpStatus.NOT_FOUND)
public R noHandlerFoundException() {
return R.failed(HttpStatus.NOT_FOUND.getReasonPhrase());
}
/**
* 保持和低版本请求路径不存在的行为一致
* <p>
* <a href="https://github.com/spring-projects/spring-boot/issues/38733">[Spring Boot
* 3.2.0] 404 Not Found behavior #38733</a>
*
* @param exception
* @return R
*/
@ExceptionHandler({NoResourceFoundException.class})
@ResponseStatus(HttpStatus.NOT_FOUND)
public R noResourceFoundException(NoResourceFoundException exception) {
log.debug("请求路径 404 {}", exception.getMessage());
return R.failed(exception.getMessage());
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sentinel.handle;
import cn.hutool.json.JSONUtil;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.bsd.pjcloud.common.core.util.R;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
/**
* @author pjcloud
* @date 2019-10-11
* <p>
* 降级 限流策略
*/
@Slf4j
public class PjcloudUrlBlockHandler implements BlockExceptionHandler {
/**
* Handle the request when blocked.
*
* @param request Servlet request
* @param response Servlet response
* @param resourceName resource name
* @param e the block exception
* @throws Exception users may throw out the BlockException or other error occurs
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, String resourceName, BlockException e) throws Exception {
log.error("sentinel 降级 资源名称{}", resourceName, e);
response.setContentType("application/json");
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().print(JSONUtil.toJsonStr(R.failed(e.getMessage())));
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sentinel.parser;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.RequestOriginParser;
import jakarta.servlet.http.HttpServletRequest;
/**
* @author pjcloud
* @date 2019-10-11
* <p>
* sentinel 请求头解析判断
*/
public class PjcloudHeaderRequestOriginParser implements RequestOriginParser {
/**
* 请求头获取allow
*/
private static final String ALLOW = "Allow";
/**
* Parse the origin from given HTTP request.
* @param request HTTP request
* @return parsed origin
*/
@Override
public String parseOrigin(HttpServletRequest request) {
return request.getHeader(ALLOW);
}
}
@@ -0,0 +1 @@
com.bsd.pjcloud.common.sentinel.PjcloudSentinelAutoConfiguration