5.24 网关 登录校验
This commit is contained in:
parent
5698a1d597
commit
bf50a915d7
@ -50,6 +50,21 @@
|
|||||||
<artifactId>hm-api</artifactId>
|
<artifactId>hm-api</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--nacos 服务注册发现-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--nacos配置管理-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--读取bootstrap文件-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.hmall.cart.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "hm.cart")
|
||||||
|
public class CartProperties {
|
||||||
|
private Integer maxAmount;
|
||||||
|
}
|
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
|
|
||||||
import com.hmall.api.client.ItemClient;
|
import com.hmall.api.client.ItemClient;
|
||||||
import com.hmall.api.dto.ItemDTO;
|
import com.hmall.api.dto.ItemDTO;
|
||||||
|
import com.hmall.cart.config.CartProperties;
|
||||||
import com.hmall.cart.domain.dto.CartFormDTO;
|
import com.hmall.cart.domain.dto.CartFormDTO;
|
||||||
import com.hmall.cart.domain.po.Cart;
|
import com.hmall.cart.domain.po.Cart;
|
||||||
import com.hmall.cart.domain.vo.CartVO;
|
import com.hmall.cart.domain.vo.CartVO;
|
||||||
@ -38,7 +39,7 @@ import java.util.stream.Collectors;
|
|||||||
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
|
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
|
||||||
|
|
||||||
private final ItemClient itemClient; //@RequiredArgsConstructor会为final字段生成构造器
|
private final ItemClient itemClient; //@RequiredArgsConstructor会为final字段生成构造器
|
||||||
|
private final CartProperties cartProperties;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addItem2Cart(CartFormDTO cartFormDTO) {
|
public void addItem2Cart(CartFormDTO cartFormDTO) {
|
||||||
@ -66,7 +67,7 @@ public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements IC
|
|||||||
@Override
|
@Override
|
||||||
public List<CartVO> queryMyCarts() {
|
public List<CartVO> queryMyCarts() {
|
||||||
// 1.查询我的购物车列表
|
// 1.查询我的购物车列表
|
||||||
List<Cart> carts = lambdaQuery().eq(Cart::getUserId,1L /*UserContext.getUser()*/).list();
|
List<Cart> carts = lambdaQuery().eq(Cart::getUserId,UserContext.getUser()).list();
|
||||||
if (CollUtils.isEmpty(carts)) {
|
if (CollUtils.isEmpty(carts)) {
|
||||||
return CollUtils.emptyList();
|
return CollUtils.emptyList();
|
||||||
}
|
}
|
||||||
@ -118,9 +119,9 @@ public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements IC
|
|||||||
long count = lambdaQuery()
|
long count = lambdaQuery()
|
||||||
.eq(Cart::getUserId, userId)
|
.eq(Cart::getUserId, userId)
|
||||||
.count(); // ← 这里返回 long
|
.count(); // ← 这里返回 long
|
||||||
if (count >= 10) {
|
if (count >= cartProperties.getMaxAmount()) {
|
||||||
throw new BizIllegalException(
|
throw new BizIllegalException(
|
||||||
StrUtil.format("用户购物车课程不能超过{}", 10)
|
StrUtil.format("用户购物车课程不能超过{}", cartProperties.getMaxAmount())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,14 @@
|
|||||||
server:
|
server:
|
||||||
port: 8082
|
port: 8082
|
||||||
spring:
|
|
||||||
application:
|
|
||||||
name: cart-service
|
|
||||||
profiles:
|
|
||||||
active: local
|
|
||||||
datasource:
|
|
||||||
url: jdbc:mysql://${hm.db.host}:3307/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
username: root
|
|
||||||
password: ${hm.db.pw}
|
|
||||||
mybatis-plus:
|
|
||||||
configuration:
|
|
||||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
|
||||||
global-config:
|
|
||||||
db-config:
|
|
||||||
update-strategy: not_null
|
|
||||||
id-type: auto
|
|
||||||
logging:
|
|
||||||
level:
|
|
||||||
com.hmall: debug
|
|
||||||
pattern:
|
|
||||||
dateformat: HH:mm:ss:SSS
|
|
||||||
file:
|
|
||||||
path: "logs/${spring.application.name}"
|
|
||||||
knife4j:
|
|
||||||
enable: true
|
|
||||||
openapi:
|
|
||||||
title: 黑马商城购物车接口文档
|
|
||||||
description: "黑马商城购物车接口文档"
|
|
||||||
email: zhangsan@itcast.cn
|
|
||||||
concat: 宇哥
|
|
||||||
url: https://www.itcast.cn
|
|
||||||
version: v1.0.0
|
|
||||||
group:
|
|
||||||
default:
|
|
||||||
group-name: default
|
|
||||||
api-rule: package
|
|
||||||
api-rule-resources:
|
|
||||||
- com.hmall.cart.controller
|
|
||||||
|
|
||||||
feign:
|
feign:
|
||||||
httpclient:
|
httpclient:
|
||||||
enabled: true # 使用 Apache HttpClient(默认关闭)
|
enabled: true # 使用 Apache HttpClient(默认关闭)
|
||||||
|
|
||||||
|
hm:
|
||||||
|
swagger:
|
||||||
|
title: 购物车服务接口文档
|
||||||
|
package: com.hmall.cart.controller
|
||||||
|
db:
|
||||||
|
database: hm-cart
|
||||||
|
|
||||||
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
|
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
|
14
cart-service/src/main/resources/bootstrap.yaml
Normal file
14
cart-service/src/main/resources/bootstrap.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: cart-service # 服务名称
|
||||||
|
profiles:
|
||||||
|
active: local
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
server-addr: 124.71.159.195:8848 # nacos地址
|
||||||
|
config:
|
||||||
|
file-extension: yaml # 文件后缀名
|
||||||
|
shared-configs: # 共享配置
|
||||||
|
- dataId: shared_jdbc.yaml # 共享mybatis配置
|
||||||
|
- dataId: shared_log.yaml # 共享日志配置
|
||||||
|
- dataId: shared_swagger.yaml # 共享日志配置
|
@ -11,7 +11,11 @@
|
|||||||
|
|
||||||
<artifactId>hm-api</artifactId>
|
<artifactId>hm-api</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.heima</groupId>
|
||||||
|
<artifactId>hm-common</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
<!--openFeign-->
|
<!--openFeign-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.hmall.api.config;
|
||||||
|
|
||||||
|
import com.hmall.common.utils.UserContext;
|
||||||
|
import feign.RequestInterceptor;
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
public class DefaultFeignConfig {
|
||||||
|
@Bean
|
||||||
|
public RequestInterceptor userInfoRequestInterceptor(){
|
||||||
|
return new RequestInterceptor() {
|
||||||
|
@Override
|
||||||
|
public void apply(RequestTemplate template) {
|
||||||
|
// 获取登录用户
|
||||||
|
Long userId = UserContext.getUser();
|
||||||
|
if(userId == null) {
|
||||||
|
// 如果为空则直接跳过
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 如果不为空则放入请求头中,传递给下游微服务
|
||||||
|
template.header("user-info", userId.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.hmall.common.config;
|
||||||
|
|
||||||
|
import com.hmall.common.interceptors.UserInfoInterceptor;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(DispatcherServlet.class)
|
||||||
|
public class MvcConfig implements WebMvcConfigurer {
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(new UserInfoInterceptor());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.hmall.common.interceptors;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.hmall.common.utils.UserContext;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
public class UserInfoInterceptor implements HandlerInterceptor {
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
// 1.获取请求头中的用户信息
|
||||||
|
String userInfo = request.getHeader("user-info");
|
||||||
|
// 2.判断是否为空
|
||||||
|
if (StrUtil.isNotBlank(userInfo)) {
|
||||||
|
// 不为空,保存到ThreadLocal
|
||||||
|
UserContext.setUser(Long.valueOf(userInfo));
|
||||||
|
}
|
||||||
|
// 3.放行
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
// 移除用户
|
||||||
|
UserContext.removeUser();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
com.hmall.common.config.MyBatisConfig,\
|
com.hmall.common.config.MyBatisConfig,\
|
||||||
com.hmall.common.config.JsonConfig
|
com.hmall.common.config.JsonConfig,\
|
||||||
|
com.hmall.common.config.MvcConfig
|
56
hm-gateway/pom.xml
Normal file
56
hm-gateway/pom.xml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>hmall</artifactId>
|
||||||
|
<groupId>com.heima</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>hm-gateway</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<!--common-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.heima</groupId>
|
||||||
|
<artifactId>hm-common</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!--网关-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--nacos discovery-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--负载均衡-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.hmall.gateway;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class GateWayApplication {
|
||||||
|
public static void main(String[] args){
|
||||||
|
SpringApplication.run(GateWayApplication.class,args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.hmall.gateway.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "hm.auth")
|
||||||
|
public class AuthProperties {
|
||||||
|
private List<String> includePaths;
|
||||||
|
private List<String> excludePaths;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.hmall.gateway.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "hm.jwt")
|
||||||
|
public class JwtProperties {
|
||||||
|
private Resource location;
|
||||||
|
private String password;
|
||||||
|
private String alias;
|
||||||
|
private Duration tokenTTL = Duration.ofMinutes(10);
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.hmall.gateway.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(JwtProperties.class)
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder(){
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KeyPair keyPair(JwtProperties properties){
|
||||||
|
// 获取秘钥工厂
|
||||||
|
KeyStoreKeyFactory keyStoreKeyFactory =
|
||||||
|
new KeyStoreKeyFactory(
|
||||||
|
properties.getLocation(),
|
||||||
|
properties.getPassword().toCharArray());
|
||||||
|
//读取钥匙对
|
||||||
|
return keyStoreKeyFactory.getKeyPair(
|
||||||
|
properties.getAlias(),
|
||||||
|
properties.getPassword().toCharArray());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.hmall.gateway.filters;
|
||||||
|
import com.hmall.common.exception.UnauthorizedException;
|
||||||
|
import com.hmall.common.utils.CollUtils;
|
||||||
|
import com.hmall.gateway.config.AuthProperties;
|
||||||
|
import com.hmall.gateway.utils.JwtTool;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@EnableConfigurationProperties(AuthProperties.class)
|
||||||
|
public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
private final JwtTool jwtTool;
|
||||||
|
|
||||||
|
private final AuthProperties authProperties;
|
||||||
|
|
||||||
|
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
// 1.获取Request
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
// 2.判断是否不需要拦截
|
||||||
|
if(isExclude(request.getPath().toString())){
|
||||||
|
// 无需拦截,直接放行
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
// 3.获取请求头中的token
|
||||||
|
String token = null;
|
||||||
|
List<String> headers = request.getHeaders().get("authorization");
|
||||||
|
if (!CollUtils.isEmpty(headers)) {
|
||||||
|
token = headers.get(0);
|
||||||
|
}
|
||||||
|
// 4.校验并解析token
|
||||||
|
Long userId = null;
|
||||||
|
try {
|
||||||
|
userId = jwtTool.parseToken(token);
|
||||||
|
} catch (UnauthorizedException e) {
|
||||||
|
// 如果无效,拦截
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
response.setRawStatusCode(401);
|
||||||
|
return response.setComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.如果有效,传递用户信息
|
||||||
|
String userInfo = userId.toString();
|
||||||
|
ServerWebExchange modifiedExchange = exchange.mutate()
|
||||||
|
.request(builder -> builder.header("user-info", userInfo))
|
||||||
|
.build();
|
||||||
|
// 6.放行
|
||||||
|
return chain.filter(modifiedExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExclude(String antPath) {
|
||||||
|
for (String pathPattern : authProperties.getExcludePaths()) {
|
||||||
|
if(antPathMatcher.match(pathPattern, antPath)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.hmall.gateway.filters;
|
||||||
|
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MyGlobalFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
// TODO 模拟登录校验逻辑
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
HttpHeaders headers = request.getHeaders();
|
||||||
|
System.out.println("headers = " + headers);
|
||||||
|
// 放行
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 0; // 这里图中缺少order值,暂设为0,可根据实际需求调整
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package com.hmall.gateway.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.exceptions.ValidateException;
|
||||||
|
import cn.hutool.jwt.JWT;
|
||||||
|
import cn.hutool.jwt.JWTValidator;
|
||||||
|
import cn.hutool.jwt.signers.JWTSigner;
|
||||||
|
import cn.hutool.jwt.signers.JWTSignerUtil;
|
||||||
|
import com.hmall.common.exception.UnauthorizedException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtTool {
|
||||||
|
private final JWTSigner jwtSigner;
|
||||||
|
|
||||||
|
public JwtTool(KeyPair keyPair) {
|
||||||
|
this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 access-token
|
||||||
|
*
|
||||||
|
* @param userId 用户信息
|
||||||
|
* @return access-token
|
||||||
|
*/
|
||||||
|
public String createToken(Long userId, Duration ttl) {
|
||||||
|
// 1.生成jws
|
||||||
|
return JWT.create()
|
||||||
|
.setPayload("user", userId)
|
||||||
|
.setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))
|
||||||
|
.setSigner(jwtSigner)
|
||||||
|
.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析token
|
||||||
|
*
|
||||||
|
* @param token token
|
||||||
|
* @return 解析刷新token得到的用户信息
|
||||||
|
*/
|
||||||
|
public Long parseToken(String token) {
|
||||||
|
// 1.校验token是否为空
|
||||||
|
if (token == null) {
|
||||||
|
throw new UnauthorizedException("未登录");
|
||||||
|
}
|
||||||
|
// 2.校验并解析jwt
|
||||||
|
JWT jwt;
|
||||||
|
try {
|
||||||
|
jwt = JWT.of(token).setSigner(jwtSigner);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new UnauthorizedException("无效的token", e);
|
||||||
|
}
|
||||||
|
// 2.校验jwt是否有效
|
||||||
|
if (!jwt.verify()) {
|
||||||
|
// 验证失败
|
||||||
|
throw new UnauthorizedException("无效的token");
|
||||||
|
}
|
||||||
|
// 3.校验是否过期
|
||||||
|
try {
|
||||||
|
JWTValidator.of(jwt).validateDate();
|
||||||
|
} catch (ValidateException e) {
|
||||||
|
throw new UnauthorizedException("token已经过期");
|
||||||
|
}
|
||||||
|
// 4.数据格式校验
|
||||||
|
Object userPayload = jwt.getPayload("user");
|
||||||
|
if (userPayload == null) {
|
||||||
|
// 数据为空
|
||||||
|
throw new UnauthorizedException("无效的token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.数据解析
|
||||||
|
try {
|
||||||
|
return Long.valueOf(userPayload.toString());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// 数据格式有误
|
||||||
|
throw new UnauthorizedException("无效的token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
hm-gateway/src/main/resources/application.yml
Normal file
42
hm-gateway/src/main/resources/application.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: gateway
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
server-addr: 124.71.159.195:8848
|
||||||
|
gateway:
|
||||||
|
routes:
|
||||||
|
- id: item # 路由规则id,自定义,唯一
|
||||||
|
uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
|
||||||
|
predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
|
||||||
|
- Path=/items/**,/search/** # 这里是以请求路径作为判断规则
|
||||||
|
- id: cart
|
||||||
|
uri: lb://cart-service
|
||||||
|
predicates:
|
||||||
|
- Path=/carts/**
|
||||||
|
- id: user
|
||||||
|
uri: lb://user-service
|
||||||
|
predicates:
|
||||||
|
- Path=/users/**,/addresses/**
|
||||||
|
- id: trade
|
||||||
|
uri: lb://trade-service
|
||||||
|
predicates:
|
||||||
|
- Path=/orders/**
|
||||||
|
- id: pay
|
||||||
|
uri: lb://pay-service
|
||||||
|
predicates:
|
||||||
|
- Path=/pay-orders/**
|
||||||
|
hm:
|
||||||
|
jwt:
|
||||||
|
location: classpath:hmall.jks
|
||||||
|
alias: hmall
|
||||||
|
password: hmall123
|
||||||
|
tokenTTL: 30m
|
||||||
|
auth:
|
||||||
|
excludePaths:
|
||||||
|
- /search/**
|
||||||
|
- /users/login
|
||||||
|
- /items/**
|
||||||
|
- /hi
|
BIN
hm-gateway/src/main/resources/hmall.jks
Normal file
BIN
hm-gateway/src/main/resources/hmall.jks
Normal file
Binary file not shown.
@ -6,7 +6,7 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://${hm.db.host}:3307/hmall?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
url: jdbc:mysql://${hm.db.host}:3307/hmall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
username: root
|
username: root
|
||||||
password: ${hm.db.pw}
|
password: ${hm.db.pw}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.hmall.item.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.hmall.api.dto.ItemDTO;
|
||||||
|
import com.hmall.common.domain.PageDTO;
|
||||||
|
|
||||||
|
import com.hmall.item.domain.po.Item;
|
||||||
|
import com.hmall.item.domain.query.ItemPageQuery;
|
||||||
|
import com.hmall.item.service.IItemService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@Api(tags = "搜索相关接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/search")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SearchController {
|
||||||
|
|
||||||
|
private final IItemService itemService;
|
||||||
|
|
||||||
|
@ApiOperation("搜索商品")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public PageDTO<ItemDTO> search(ItemPageQuery query) {
|
||||||
|
// 分页查询
|
||||||
|
Page<Item> result = itemService.lambdaQuery()
|
||||||
|
.like(StrUtil.isNotBlank(query.getKey()), Item::getName, query.getKey())
|
||||||
|
.eq(StrUtil.isNotBlank(query.getBrand()), Item::getBrand, query.getBrand())
|
||||||
|
.eq(StrUtil.isNotBlank(query.getCategory()), Item::getCategory, query.getCategory())
|
||||||
|
.eq(Item::getStatus, 1)
|
||||||
|
.between(query.getMaxPrice() != null, Item::getPrice, query.getMinPrice(), query.getMaxPrice())
|
||||||
|
.page(query.toMpPage("update_time", false));
|
||||||
|
// 封装并返回
|
||||||
|
return PageDTO.of(result, ItemDTO.class);
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,8 @@ package com.hmall.item.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
import com.hmall.api.dto.OrderDetailDTO;
|
||||||
import com.hmall.item.domain.po.Item;
|
import com.hmall.item.domain.po.Item;
|
||||||
import com.hmall.item.domain.dto.OrderDetailDTO;
|
|
||||||
import org.apache.ibatis.annotations.Update;
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,7 @@ import com.hmall.item.service.IItemService;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +42,27 @@ public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements II
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
|
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
|
||||||
return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
|
// 1. 防空:参数为 null 或 空集合,直接返回空列表
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Item> items;
|
||||||
|
try {
|
||||||
|
// 2. 真正去查
|
||||||
|
items = listByIds(ids);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 3. 捕获底层任何异常,记录并返回空列表
|
||||||
|
System.out.println("no");
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 再防空:数据库里一个都没查到,也返回空
|
||||||
|
if (items == null || items.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 正常转换
|
||||||
|
return BeanUtils.copyList(items, ItemDTO.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,20 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://${hm.db.host}:3307/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
url: jdbc:mysql://${hm.db.host}:3307/hm-item?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
username: root
|
username: root
|
||||||
password: ${hm.db.pw}
|
password: ${hm.db.pw}
|
||||||
|
hikari:
|
||||||
|
# 比你数据库空闲断连时间再小一点,这里示例用 20 分钟
|
||||||
|
max-lifetime: 1200000 # 20 分钟,单位 ms
|
||||||
|
idle-timeout: 600000 # 10 分钟,长时间不用就回收
|
||||||
|
connection-timeout: 30000 # 30 秒 等待可用连接
|
||||||
|
minimum-idle: 5 # 保持最少 5 个空闲
|
||||||
|
maximum-pool-size: 10 # 池中最大连接数,根据并发量调整
|
||||||
|
validation-timeout: 5000 # 验证一个连接可用时的超时时间
|
||||||
|
# (可选)强制每次从池里拿连接前跑一下简单检查:
|
||||||
|
connection-test-query: SELECT 1
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
configuration:
|
configuration:
|
||||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package com.hmall.pay;
|
package com.hmall.pay;
|
||||||
|
|
||||||
|
import com.hmall.api.config.DefaultFeignConfig;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
@MapperScan("com.hmall.pay.mapper")
|
@MapperScan("com.hmall.pay.mapper")
|
||||||
@EnableFeignClients(basePackages= "com.hmall.api.client")
|
@EnableFeignClients(basePackages= "com.hmall.api.client",defaultConfiguration = DefaultFeignConfig.class)
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class PayApplication {
|
public class PayApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -9,10 +9,20 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://${hm.db.host}:3307/hm-pay?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
url: jdbc:mysql://${hm.db.host}:3307/hm-pay?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
username: root
|
username: root
|
||||||
password: ${hm.db.pw}
|
password: ${hm.db.pw}
|
||||||
|
hikari:
|
||||||
|
# 比你数据库空闲断连时间再小一点,这里示例用 20 分钟
|
||||||
|
max-lifetime: 1200000 # 20 分钟,单位 ms
|
||||||
|
idle-timeout: 600000 # 10 分钟,长时间不用就回收
|
||||||
|
connection-timeout: 30000 # 30 秒 等待可用连接
|
||||||
|
minimum-idle: 5 # 保持最少 5 个空闲
|
||||||
|
maximum-pool-size: 10 # 池中最大连接数,根据并发量调整
|
||||||
|
validation-timeout: 5000 # 验证一个连接可用时的超时时间
|
||||||
|
# (可选)强制每次从池里拿连接前跑一下简单检查:
|
||||||
|
connection-test-query: SELECT 1
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
configuration:
|
configuration:
|
||||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
||||||
|
3
pom.xml
3
pom.xml
@ -17,6 +17,7 @@
|
|||||||
<module>hm-api</module>
|
<module>hm-api</module>
|
||||||
<module>trade-service</module>
|
<module>trade-service</module>
|
||||||
<module>pay-service</module>
|
<module>pay-service</module>
|
||||||
|
<module>hm-gateway</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
@ -36,7 +37,7 @@
|
|||||||
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
|
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
|
||||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
||||||
<hutool.version>5.8.11</hutool.version>
|
<hutool.version>5.8.11</hutool.version>
|
||||||
<mysql.version>8.0.23</mysql.version>
|
<mysql.version>8.0.24</mysql.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<!-- 对依赖包进行管理 -->
|
<!-- 对依赖包进行管理 -->
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package com.hmall.trade;
|
package com.hmall.trade;
|
||||||
|
|
||||||
|
import com.hmall.api.config.DefaultFeignConfig;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
@MapperScan("com.hmall.trade.mapper")
|
@MapperScan("com.hmall.trade.mapper")
|
||||||
@EnableFeignClients(basePackages= "com.hmall.api.client")
|
@EnableFeignClients(
|
||||||
|
basePackages = "com.hmall.api.client",
|
||||||
|
defaultConfiguration = DefaultFeignConfig.class
|
||||||
|
)
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class TradeApplication {
|
public class TradeApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -93,6 +93,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
order.setId(orderId);
|
order.setId(orderId);
|
||||||
order.setStatus(2);
|
order.setStatus(2);
|
||||||
order.setPayTime(LocalDateTime.now());
|
order.setPayTime(LocalDateTime.now());
|
||||||
|
order.setCreateTime(LocalDateTime.now());
|
||||||
updateById(order);
|
updateById(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,10 +9,20 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://${hm.db.host}:3307/hm-trade?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
url: jdbc:mysql://${hm.db.host}:3307/hm-trade?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
username: root
|
username: root
|
||||||
password: ${hm.db.pw}
|
password: ${hm.db.pw}
|
||||||
|
hikari:
|
||||||
|
# 比你数据库空闲断连时间再小一点,这里示例用 20 分钟
|
||||||
|
max-lifetime: 1200000 # 20 分钟,单位 ms
|
||||||
|
idle-timeout: 600000 # 10 分钟,长时间不用就回收
|
||||||
|
connection-timeout: 30000 # 30 秒 等待可用连接
|
||||||
|
minimum-idle: 5 # 保持最少 5 个空闲
|
||||||
|
maximum-pool-size: 10 # 池中最大连接数,根据并发量调整
|
||||||
|
validation-timeout: 5000 # 验证一个连接可用时的超时时间
|
||||||
|
# (可选)强制每次从池里拿连接前跑一下简单检查:
|
||||||
|
connection-test-query: SELECT 1
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
configuration:
|
configuration:
|
||||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
||||||
|
@ -9,10 +9,20 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://${hm.db.host}:3307/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
url: jdbc:mysql://${hm.db.host}:3307/hm-user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
username: root
|
username: root
|
||||||
password: ${hm.db.pw}
|
password: ${hm.db.pw}
|
||||||
|
hikari:
|
||||||
|
# 比你数据库空闲断连时间再小一点,这里示例用 20 分钟
|
||||||
|
max-lifetime: 1200000 # 20 分钟,单位 ms
|
||||||
|
idle-timeout: 600000 # 10 分钟,长时间不用就回收
|
||||||
|
connection-timeout: 30000 # 30 秒 等待可用连接
|
||||||
|
minimum-idle: 5 # 保持最少 5 个空闲
|
||||||
|
maximum-pool-size: 10 # 池中最大连接数,根据并发量调整
|
||||||
|
validation-timeout: 5000 # 验证一个连接可用时的超时时间
|
||||||
|
# (可选)强制每次从池里拿连接前跑一下简单检查:
|
||||||
|
connection-test-query: SELECT 1
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
configuration:
|
configuration:
|
||||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
||||||
|
Loading…
x
Reference in New Issue
Block a user