This commit is contained in:
Administrator 2024-12-13 09:50:15 +08:00
commit 7011024e46
388 changed files with 20094 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.factorypath
.classpath
.project
*.class
.settings
pom.properties
**/target/**
mvnw
mvnw.cmd
maven-wrapper.properties

33
admin-server/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

68
admin-server/pom.xml Normal file
View File

@ -0,0 +1,68 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.evotech.hd</groupId>
<artifactId>battery-cloud</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>admin-server</artifactId>
<version>1.0.0</version>
<name>admin-server</name>
<description>监控和日志</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,18 @@
package com.evotech.hd.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
@SpringBootApplication
@EnableDiscoveryClient
@EnableAdminServer
public class AdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(AdminServerApplication.class, args);
}
}

View File

@ -0,0 +1,54 @@
package com.evotech.hd.admin.config;
import org.springframework.stereotype.Component;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractStatusChangeNotifier;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
/**
* 自定义通知
* 继承 AbstractStatusChangeNotifier 实现了 doNotify 方法
*/
@Component
@Slf4j
public class NotifierConfig extends AbstractStatusChangeNotifier {
public NotifierConfig(InstanceRepository repository) {
super(repository);
}
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
if (event instanceof InstanceStatusChangedEvent) {
log.info("===>>>实例名称:"+instance.getRegistration().getName());
log.info("===>>>实例服务地址:"+instance.getRegistration().getServiceUrl());
String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
switch (status) {
case "DOWN":
log.info("健康检查没通过!");
break;
case "OFFLINE":
log.info("服务离线!");
break;
case "UP":
log.info("服务上线!");
break;
case "UNKNOWN":
log.info("服务未知异常!");
break;
default:
log.info(status);
break;
}
}
});
}
}

View File

@ -0,0 +1,43 @@
package com.evotech.hd.admin.config;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@Component
@Order(value = 100)
@Slf4j
public class PrintConfig implements ApplicationRunner {
@Resource
private Environment env;
@Override
public void run(ApplicationArguments args) {
int port = env.getProperty("server.port", Integer.class) == null?8080:env.getProperty("server.port", Integer.class);
String address = "127.0.0.1";
try {
address = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
address = "192.168.5.213";
e.printStackTrace();
}
String url = "http://" + address + ":" + port;
String path = env.getProperty("server.servlet.context-path");
if (StringUtils.hasText(path)) {
url += path;
}
log.info("\r\n===>>>项目已启动,访问地址:{}", url);
}
}

View File

@ -0,0 +1,58 @@
# 1.server
server:
port: 9099
servlet:
context-path: /admonitor
# 2. log
logging:
config: classpath:logback-spring.xml
# 3. spring
spring:
application:
name: admin-monitor
boot:
admin:
ui:
# 登陆页面标题
title: 微服务监控
# 登陆后,页面左上标题
brand: <span>服务监控和日志</span>
# 配置一个账号和密码
# security:
# user:
# name: zrb
# password: zrb123
cloud:
nacos:
serverAddr: 192.168.5.213:8848
username: nacos
password: nacos
discovery:
register-enabled: true
#server-addr: 10.10.1.6:8848
#ip: 10.10.1.2
# metadata:
# user:
# name: ${spring.security.user.name}
# password: ${spring.security.user.password}
# 因添加了context-pathadmin-server要想发现正确路径需要加这个
metadata:
management:
context-path: ${server.servlet.context-path}/actuator
#开放指定信息给服务器看
management:
httpexchanges:
recording:
enabled: true
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
# xml文件中配置的日志文件的名称
logfile:
external-file: /logs/admin/admin-server.log

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL如果设置为WARN则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时配置文件如果发生改变将会被重新加载默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔如果没有给出时间单位默认单位是毫秒。当scan为true时此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<property name="CONTEXT_NAME" value="admin-server" />
<property name="LOG_PATH" value="/logs/admin" />
<property name="MAX_FILE_SIZE" value="100MB" />
<property name="MAX_HISTORY" value="30" />
<!--读取配置中心的属性 -->
<!-- <springProperty scope="context" name="LOG_PATH_NAME" source="logging.file.name"/> -->
<contextName>${CONTEXT_NAME}</contextName>
<!--输出到控制台 -->
<!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%i索引【从数字0开始递增】 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<!--%logger{50}:表示logger名字最长50个字符否则按照句点分割 -->
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''} -- %msg%n</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--所有日志输出到文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志名,指定最新的文件名其他文件名使用FileNamePattern -->
<File>${LOG_PATH}/${CONTEXT_NAME}.log</File>
<!--日志文件输出格式 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{50}.%M\(%line\) -- %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 后面可以加.zip -->
<fileNamePattern>${LOG_PATH}/${CONTEXT_NAME}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--日志文件保留天数 -->
<maxHistory>${MAX_HISTORY}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!--文件达到 最大时会被压缩和切割 -->
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- 异步日志输出看情况配置 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE" />
</appender>
<!-- 统一配置日志输出root标签和logger标签如果这开启了就把不同环境的关上 -->
<!-- root标签相当于开关只有把定义的appender添加到这里 才会生效有日志 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
<!-- <appender-ref ref="ASYNC_ERROR_FILE" /> -->
</root>
<!-- logger标签用来设置某一个包或者具体的某一个类的日志打印级别 -->
<logger name="org.springframework.jdbc.core.JdbcTemplate" level="INFO" />
<!-- 配置不同环境的日志输出root标签和logger标签 -->
<!-- <springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ASYNC_ERROR_FILE" />
</root>
<logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG" />
</springProfile> -->
<!-- <springProfile name="prod">
<root level="INFO">
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ASYNC_ERROR_FILE" />
</root>
</springProfile> -->
</configuration>

33
authorization-server/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.evotech.hd</groupId>
<artifactId>battery-cloud</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>authorization-server</artifactId>
<name>authorization-server</name>
<description>认证服务</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
<!-- openfein -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.evotech.hd</groupId>
<artifactId>common-web</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.evotech.hd</groupId>
<artifactId>common-redis</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.evotech.hd</groupId>
<artifactId>common-mybatis</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-jwt</artifactId>
</dependency>
<!-- 不加这个打包有warning -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,19 @@
package com.evotech.hd.authorization;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan("com.evotech.hd.**")
@EnableFeignClients
public class AuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServerApplication.class, args);
}
}

View File

@ -0,0 +1,262 @@
package com.evotech.hd.authorization.config.oauth2;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.StringUtils;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import com.evotech.hd.authorization.config.oauth2.passwordmode.OAuth2PasswordAuthenticationConverter;
import com.evotech.hd.authorization.config.oauth2.passwordmode.OAuth2PasswordAuthenticationProvider;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import jakarta.annotation.Resource;
@Configuration
public class AuthorizationServerConfig {
Logger log = LoggerFactory.getLogger(this.getClass());
private static final String KEY_ID = "jnGZxjHC54hP4ZnXrrEedtNweQ6aK29w";
@Resource
private MyOAuth2TokenJwtCustomizer jwtCustomizer;
@Resource
private RSAKeyPair rsaKeyPair;
/**
* 授权服务器端点配置
*/
@Bean
@Order(1)
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<?> tokenGenerator) throws Exception {
// 这是 http 的默认配置可以点进去看一下我们为了加入自己的密码模式需要把
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
// RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
// http.securityMatcher(endpointsMatcher)
// .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
// .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
// .apply(authorizationServerConfigurer);
// 添加password模式
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).tokenEndpoint(tokenEndpoint ->
tokenEndpoint
// 添加授权模式转换器(Converter)
.accessTokenRequestConverter(new OAuth2PasswordAuthenticationConverter())
// 添加 授权模式提供者(Provider)
.authenticationProvider(new OAuth2PasswordAuthenticationProvider(authenticationManager, authorizationService, tokenGenerator))
// 成功响应
.accessTokenResponseHandler(new MyAuthenticationSuccessHandler())
// 失败响应
.errorResponseHandler(new MyAuthenticationFailureHandler()));
// 开启OpenID Connect 1.0协议相关端点
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
return http.build();
}
/**
* 配置认证服务器请求地址
*/
@Bean
@Order(2)
AuthorizationServerSettings authorizationServerSettings() {
// 什么都不配置则使用默认地址
return AuthorizationServerSettings.builder().build();
}
// 客户端信息表oauth2_registered_client
@Bean
@Order(3)
RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
return registeredClientRepository;
}
// token表交互数据用的oauth2_authorization
@Bean
OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
rowMapper.setLobHandler(new DefaultLobHandler());
ObjectMapper objectMapper = new ObjectMapper();
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
List<Module> modules = SecurityJackson2Modules.getModules(classLoader);
objectMapper.registerModules(modules);
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
// 使用刷新模式需要从 oauth2_authorization 表反序列化attributes字段得到用户信息(SysUserDetails)
// objectMapper.addMixIn(SysUserDetails.class, SysUserMixin.class);
objectMapper.addMixIn(Long.class, Object.class);
rowMapper.setObjectMapper(objectMapper);
service.setAuthorizationRowMapper(rowMapper);
return service;
}
// 和授权记录表交互数据用的oauth2_authorization_consent
@Bean
OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
/**
* ========= Token部分 ========
*/
/**
* 配置 JWK为JWT(id_token)提供加密密钥用于加密/解密或签名/验签
* JWK详细见https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key-41
*/
@Bean
JWKSource<SecurityContext> jwkSource() {
RSAPublicKey publicKey = null;
RSAPrivateKey privateKey = null;
String publicKeyBase64 = rsaKeyPair.getPublicKeyBase64();
String privateKeyBase64 = rsaKeyPair.getPrivateKeyBase64();
if (StringUtils.hasText(publicKeyBase64) && StringUtils.hasText(privateKeyBase64)) {
publicKey = getPublicKey(publicKeyBase64);
privateKey = getPrivateKey(privateKeyBase64);
} else {
KeyPair keyPair = generateRsaKey();
publicKey = (RSAPublicKey) keyPair.getPublic();
privateKey = (RSAPrivateKey) keyPair.getPrivate();
log.warn("未设置生成token的秘钥");
log.info("生成临时秘钥:");
log.info("\r\n===publicKey===" +
"\r\n" +
Base64.getEncoder().encodeToString(publicKey.getEncoded()) +
"\r\n===privateKey===" +
"\r\n" +
Base64.getEncoder().encodeToString(privateKey.getEncoded()));
}
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(KEY_ID)
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private RSAPublicKey getPublicKey(String publicKeyBase64) {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64));
RSAPublicKey rsaPublicKey = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
rsaPublicKey = (RSAPublicKey)keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
}
return rsaPublicKey;
}
private RSAPrivateKey getPrivateKey(String privateKeyBase64) {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64));
RSAPrivateKey rsaPrivateKey = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
rsaPrivateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
}
return rsaPrivateKey;
}
/**
* 生成RSA密钥对给上面jwkSource() 方法的提供密钥对
*/
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
/**
* 配置jwt解析器
*/
@Bean
JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
/**
* 配置token生成器
*/
@Bean
OAuth2TokenGenerator<?> tokenGenerator(JWKSource<SecurityContext> jwkSource) {
JwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource));
// token 中加入自定义的字段内容
jwtGenerator.setJwtCustomizer(jwtCustomizer);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
}

View File

@ -0,0 +1,18 @@
package com.evotech.hd.authorization.config.oauth2;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MyAccessToken {
private String accessToken;
private String refreshToken;
private String tokenType;
private long expiresIn;
}

View File

@ -0,0 +1,42 @@
package com.evotech.hd.authorization.config.oauth2;
import java.io.IOException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.enums.CodeMsg;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final HttpMessageConverter<Object> httpResponseConverter = new MappingJackson2HttpMessageConverter();
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
Result<String> res = new Result<String>();
if (exception instanceof UsernameNotFoundException) {
res.error(CodeMsg.USERNAME_OR_PASSWORD_ERROR);
} else if (exception instanceof OAuth2AuthenticationException) {
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
res.error(error.getErrorCode());
}
System.out.println("error走这个了 MyAuthenticationFailureHandler");
httpResponseConverter.write(res, null, httpResponse);
}
}

View File

@ -0,0 +1,63 @@
package com.evotech.hd.authorization.config.oauth2;
import java.io.IOException;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.DefaultOAuth2AccessTokenResponseMapConverter;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.CollectionUtils;
import com.evotech.hd.common.core.entity.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
/**
* MappingJackson2HttpMessageConverter Spring 框架提供的一个 HTTP 消息转换器用于将 HTTP 请求和响应的 JSON 数据与 Java 对象之间进行转换
*/
private final HttpMessageConverter<Object> accessTokenHttpResponseConverter = new MappingJackson2HttpMessageConverter();
private Converter<OAuth2AccessTokenResponse, Map<String, Object>> accessTokenResponseParametersConverter = new DefaultOAuth2AccessTokenResponseMapConverter();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
OAuth2AccessTokenResponse.Builder builder =OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()).tokenType(accessToken.getTokenType());
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
}
if (refreshToken != null) {
builder.refreshToken(refreshToken.getTokenValue());
}
if (!CollectionUtils.isEmpty(additionalParameters)) {
builder.additionalParameters(additionalParameters);
}
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
Map<String, Object> tokenResponseParameters = accessTokenResponseParametersConverter.convert(accessTokenResponse);
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
this.accessTokenHttpResponseConverter.write(new Result<String>().success(tokenResponseParameters), null, httpResponse);
}
}

View File

@ -0,0 +1,32 @@
package com.evotech.hd.authorization.config.oauth2;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import com.evotech.hd.authorization.config.security.userdetail.MyUserDetail;
/**
* 自定义Token包含的字段信息
* 通过context拿到authentication和其他信息然后再拿到PrincipaluserDetails数据或者其他
*/
@Configuration
public class MyOAuth2TokenJwtCustomizer implements OAuth2TokenCustomizer<JwtEncodingContext> {
@Override
public void customize(JwtEncodingContext context) {
JwtClaimsSet.Builder claims = context.getClaims();
System.out.println("自定义token字段");
Authentication authentication = context.getPrincipal();
MyUserDetail detail = (MyUserDetail) authentication.getPrincipal();
claims.claim("uid", detail.getUid());
claims.claim("rcodes", detail.getRcodes());
}
}

View File

@ -0,0 +1,25 @@
package com.evotech.hd.authorization.config.oauth2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class RSAKeyPair {
@Value("${security.token.public_key_base64:null}")
private String publicKeyBase64;
@Value("${security.token.private_key_base64:null}")
private String privateKeyBase64;
public String getPublicKeyBase64() {
return publicKeyBase64;
}
public String getPrivateKeyBase64() {
return privateKeyBase64;
}
}

View File

@ -0,0 +1,79 @@
package com.evotech.hd.authorization.config.oauth2.passwordmode;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
public class OAuth2AuthenticationProviderUtils {
private OAuth2AuthenticationProviderUtils() {
}
public static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {
OAuth2ClientAuthenticationToken clientPrincipal = null;
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
}
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
return clientPrincipal;
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
}
public static <T extends OAuth2Token> OAuth2Authorization invalidate(OAuth2Authorization authorization, T token) {
// @formatter:off
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization)
.token(token,
(metadata) ->
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true));
if (OAuth2RefreshToken.class.isAssignableFrom(token.getClass())) {
authorizationBuilder.token(
authorization.getAccessToken().getToken(),
(metadata) ->
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true));
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode =
authorization.getToken(OAuth2AuthorizationCode.class);
if (authorizationCode != null && !authorizationCode.isInvalidated()) {
authorizationBuilder.token(
authorizationCode.getToken(),
(metadata) ->
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true));
}
}
// @formatter:on
return authorizationBuilder.build();
}
public static <T extends OAuth2Token> OAuth2AccessToken accessToken(OAuth2Authorization.Builder builder, T token,
OAuth2TokenContext accessTokenContext) {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token.getTokenValue(),
token.getIssuedAt(), token.getExpiresAt(), accessTokenContext.getAuthorizedScopes());
OAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient()
.getTokenSettings()
.getAccessTokenFormat();
builder.token(accessToken, (metadata) -> {
if (token instanceof ClaimAccessor claimAccessor) {
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, claimAccessor.getClaims());
}
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false);
metadata.put(OAuth2TokenFormat.class.getName(), accessTokenFormat.getValue());
});
return accessToken;
}
}

View File

@ -0,0 +1,101 @@
package com.evotech.hd.authorization.config.oauth2.passwordmode;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
public class OAuth2EndpointUtils {
public static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private OAuth2EndpointUtils() {
}
public static MultiValueMap<String, String> getFormParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameterMap.forEach((key, values) -> {
String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : "";
// If not query parameter then it's a form parameter
if (!queryString.contains(key) && values.length > 0) {
for (String value : values) {
parameters.add(key, value);
}
}
});
return parameters;
}
public static MultiValueMap<String, String> getQueryParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameterMap.forEach((key, values) -> {
String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : "";
if (queryString.contains(key) && values.length > 0) {
for (String value : values) {
parameters.add(key, value);
}
}
});
return parameters;
}
public static Map<String, Object> getParametersIfMatchesAuthorizationCodeGrantRequest(HttpServletRequest request,
String... exclusions) {
if (!matchesAuthorizationCodeGrantRequest(request)) {
return Collections.emptyMap();
}
MultiValueMap<String, String> multiValueParameters = "GET".equals(request.getMethod())
? getQueryParameters(request) : getFormParameters(request);
for (String exclusion : exclusions) {
multiValueParameters.remove(exclusion);
}
Map<String, Object> parameters = new HashMap<>();
multiValueParameters.forEach(
(key, value) -> parameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])));
return parameters;
}
public static boolean matchesAuthorizationCodeGrantRequest(HttpServletRequest request) {
return AuthorizationGrantType.AUTHORIZATION_CODE.getValue()
.equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))
&& request.getParameter(OAuth2ParameterNames.CODE) != null;
}
public static boolean matchesPkceTokenRequest(HttpServletRequest request) {
return matchesAuthorizationCodeGrantRequest(request)
&& request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
}
public static void throwError(String errorCode, String parameterName, String errorUri) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
throw new OAuth2AuthenticationException(error);
}
public static String normalizeUserCode(String userCode) {
Assert.hasText(userCode, "userCode cannot be empty");
StringBuilder sb = new StringBuilder(userCode.toUpperCase().replaceAll("[^A-Z\\d]+", ""));
Assert.isTrue(sb.length() == 8, "userCode must be exactly 8 alpha/numeric characters");
sb.insert(4, '-');
return sb.toString();
}
public static boolean validateUserCode(String userCode) {
return (userCode != null && userCode.toUpperCase().replaceAll("[^A-Z\\d]+", "").length() == 8);
}
}

View File

@ -0,0 +1,82 @@
package com.evotech.hd.authorization.config.oauth2.passwordmode;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.lang.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
/**
* 限定必须要传的参数
* 只是要这些参数并没有对参数值校验
*/
public class OAuth2PasswordAuthenticationConverter implements AuthenticationConverter {
@Nullable
@Override
public Authentication convert(HttpServletRequest request) {
// 1. 提取表单参数准备校验用用的这个方法就是复制的sas自带的
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);
// 2. 授权类型参数 (必须)
// String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
String grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);
if (!OAuth2PasswordAuthenticationToken.PASSWORD.getValue().equals(grantType)) {
return null;
}
// 3. 令牌申请访问范围参数 (可选)
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.SCOPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
Set<String> requestedScopes = null;
if (StringUtils.hasText(scope)) {
requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
}
// 4. 账号密码参数校验
// 4.1 用户名参数 (必须)
String username = parameters.getFirst(OAuth2ParameterNames.USERNAME);
if (!StringUtils.hasText(username) || parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.USERNAME,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
// 4.2 密码参数 (必须)
String password = parameters.getFirst(OAuth2ParameterNames.PASSWORD);
if (!StringUtils.hasText(password) || parameters.get(OAuth2ParameterNames.PASSWORD).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.PASSWORD,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
// 5. 客户端凭据信息在header 中填写的那个
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
// 6. 参数封装成additionalParameters放到OAuth2PasswordAuthenticationToken中传给 PasswordAuthenticationProvider 用于验证值
Map<String, Object> additionalParameters = new HashMap<>();
parameters.forEach((key, value) -> {
if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.SCOPE)) {
additionalParameters.put(key, value.get(0));
}
});
return new OAuth2PasswordAuthenticationToken(clientPrincipal, requestedScopes, additionalParameters);
}
}

View File

@ -0,0 +1,186 @@
package com.evotech.hd.authorization.config.oauth2.passwordmode;
import java.security.Principal;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext.Builder;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import jakarta.annotation.Resource;
/**
* 参考授权码(主要)"OAuth2AuthorizationCodeAuthenticationProvider"和客户端"OAuth2ClientCredentialsAuthenticationProvider"
*/
public class OAuth2PasswordAuthenticationProvider implements AuthenticationProvider {
@Resource
private PasswordEncoder passwordEncoder;
// 这部分代码和OAuth2ClientCredentialsAuthenticationProvider类似只是添加了AuthenticationManager
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private final AuthenticationManager authenticationManager;
private final OAuth2AuthorizationService authorizationService;
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
public OAuth2PasswordAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
this.authenticationManager = authenticationManager;
this.authorizationService = authorizationService;
this.tokenGenerator = tokenGenerator;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// PasswordAuthenticationToken
OAuth2PasswordAuthenticationToken passwordAuthenticationToken = (OAuth2PasswordAuthenticationToken) authentication;
// coverter中最后生成的token包含3部分内容在这里拿出来下面用
Map<String, Object> additionalParameters = passwordAuthenticationToken.getAdditionalParameters();
OAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient(passwordAuthenticationToken);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
// 1. 验证客户端是否支持密码模式类型(grant_type=password)
if (!registeredClient.getAuthorizationGrantTypes().contains(passwordAuthenticationToken.getGrantType())) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE);
}
// 2. 校验范围scope
Set<String> authorizedScopes = registeredClient.getScopes();
Set<String> requestedScopes = passwordAuthenticationToken.getScopes();
if (!CollectionUtils.isEmpty(requestedScopes )) {
Set<String> unauthorizedScopes = requestedScopes.stream()
.filter(scope -> !registeredClient.getScopes().contains(scope))
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(unauthorizedScopes)) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
}
authorizedScopes = new LinkedHashSet<>(requestedScopes);
}
// 3 用户名密码校验
String username = (String) additionalParameters.get(OAuth2ParameterNames.USERNAME);
String password = (String) additionalParameters.get(OAuth2ParameterNames.PASSWORD);
// 我们不自己校验了用oauth2的方法校验
// MyUserDetail userDetail = userDetailsService.loadUserByUsername(username);
// if (userDetail == null) {
// throw new OAuth2AuthenticationException("用户不存在!");
// }
// if (!passwordEncoder.matches(password, userDetail.getPassword())) {
// throw new OAuth2AuthenticationException("密码不正确!");
// }
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
Authentication usernamePasswordAuthentication = null;
try {
usernamePasswordAuthentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
} catch (AuthenticationException e) {
e.printStackTrace();
throw new OAuth2AuthenticationException("账号或密码错误");
}
// 处理userDetails的时候我们没有添加权限信息这拿不到数据
// Collection<? extends GrantedAuthority> authorities = usernamePasswordAuthentication.getAuthorities();
// for (GrantedAuthority authoriti : authorities) {
// }
// 4. 生成token
// 4.1 填充token需要的上下文数据按照授权码模式来的
Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
.registeredClient(registeredClient)
// 身份验证成功的认证信息(用户名权限等信息)
.principal(usernamePasswordAuthentication)
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
.authorizedScopes(authorizedScopes)
// 授权类型
.authorizationGrantType(passwordAuthenticationToken.getGrantType())
// 授权具体对象
.authorizationGrant(passwordAuthenticationToken)
;
// 4.2 生成访问令牌(Access Token)
DefaultOAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
if (generatedAccessToken == null) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the access token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
// 4. 生成刷新令牌(Refresh Token)
OAuth2RefreshToken refreshToken = null;
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
// Do not issue refresh token to public client
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the refresh token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
}
// 5. 组装数据入库
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName(clientPrincipal.getName())
.authorizationGrantType(passwordAuthenticationToken.getGrantType())
.authorizedScopes(authorizedScopes)
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
if (generatedAccessToken instanceof ClaimAccessor) {
authorizationBuilder.token(accessToken, (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
} else {
authorizationBuilder.accessToken(accessToken);
authorizationBuilder.refreshToken(refreshToken);
}
OAuth2Authorization authorization = authorizationBuilder.build();
// 入库
this.authorizationService.save(authorization);
additionalParameters = Collections.emptyMap();
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
}
@Override
public boolean supports(Class<?> authentication) {
return OAuth2PasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}

View File

@ -0,0 +1,55 @@
package com.evotech.hd.authorization.config.oauth2.passwordmode;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.lang.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
/**
* 这个token就是传递数据用的一个实体想要什么数据可以写成变量
* 在converter生成时赋值在provider中拿出来用
*/
public class OAuth2PasswordAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
private static final long serialVersionUID = -7029686994815546552L;
public static final AuthorizationGrantType PASSWORD = new AuthorizationGrantType("password");
private final Set<String> scopes;
/**
* 密码模式身份验证令牌
*
* @param clientPrincipal 客户端信息
* @param scopes 令牌申请访问范围
* @param additionalParameters 自定义额外参数(用户名和密码等)
*/
protected OAuth2PasswordAuthenticationToken(Authentication clientPrincipal, @Nullable Set<String> scopes,
@Nullable Map<String, Object> additionalParameters) {
super(PASSWORD, clientPrincipal, additionalParameters);
this.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());
}
/**
* 这个方法 父类中直接返回了空字符串
* 我们可以根据自己的需要重写可以返回密码
*/
@Override
public Object getCredentials() {
return this.getAdditionalParameters().get(OAuth2ParameterNames.PASSWORD);
}
public Set<String> getScopes() {
return this.scopes;
}
}

View File

@ -0,0 +1,36 @@
package com.evotech.hd.authorization.config.security;
import java.io.IOException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import com.evotech.hd.common.core.entity.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 登陆了没有权限时触发异常 返回信息
*/
public class MyAccessDeniedHandler implements AccessDeniedHandler {
private final HttpMessageConverter<Object> httpResponseConverter = new MappingJackson2HttpMessageConverter();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("=====无权限的异常处理");
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
Result<Integer> res = new Result<Integer>().error(accessDeniedException.getLocalizedMessage());
httpResponseConverter.write(res, null, httpResponse);
}
}

View File

@ -0,0 +1,40 @@
package com.evotech.hd.authorization.config.security;
import java.io.IOException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.web.AuthenticationEntryPoint;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.enums.CodeMsg;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 未认证没有登录返回异常 信息
*/
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final HttpMessageConverter<Object> httpResponseConverter = new MappingJackson2HttpMessageConverter();
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
if (authException instanceof InvalidBearerTokenException) {
System.out.println(authException.getMessage());
}
authException.printStackTrace();
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
Result<String> res = new Result<String>().error(CodeMsg.AUTHENTICATION_FAILED);
httpResponseConverter.write(res, null, httpResponse);
}
}

View File

@ -0,0 +1,104 @@
package com.evotech.hd.authorization.config.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import com.evotech.hd.authorization.config.security.userdetail.UserDetailService;
@Configuration
public class WebSecutiryConfig {
@Bean
UserDetailsService userDetailsService() {
UserDetailService userDetail = new UserDetailService();
return userDetail;
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Spring Security 安全过滤器链配置
*/
@Bean
@Order(1)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
// 1. 开启认证提前排除 不需要认证的
http.authorizeHttpRequests(authorize -> {
authorize
// 路径不用加context-path
.requestMatchers("/oauth2/**").permitAll()
// 登陆图形验证码
.requestMatchers("/captcha/**").permitAll()
// 登陆
.requestMatchers("/login/**").permitAll()
.anyRequest().authenticated();
})
;
// 2. 登陆方式默认是使用security提供表单的登陆页面和方式我们这里关闭
http.formLogin(Customizer.withDefaults());
// http.formLogin(AbstractHttpConfigurer::disable);
// 3. 登出配置
// http.logout(logout -> logout.logoutSuccessHandler(new MyLogoutSuccessHandler()));
// 4. security异常错误配置
http.exceptionHandling(exception -> {
// 未认证时 访问接口返回错误
exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());
// 未授权时返回错误一般资源服务器 才会用到这个
exception.accessDeniedHandler(new MyAccessDeniedHandler());
});
// 5. csrf是默认开启的此时对于post请求会需要一个"_csrf"的隐藏字段传递为了前端方便这个关了
http.csrf(csrf -> csrf.disable());
// 6. 跨域处理
http.cors(Customizer.withDefaults());
// 7. session管理设置同一个账号只能登陆一次.单体服务这个管用微服务 不能用
// http.sessionManagement(session -> session.maximumSessions(1).expiredSessionStrategy(new MySessionInformationExpiredStrategy()));
return http.build();
}
/**
* Spring Security 排除路径
*/
@Bean
WebSecurityCustomizer webSecurityCustomizer() {
return (web) ->
// 不走过滤器链(swagger和静态资源jscsshtml)
web.ignoring().requestMatchers(
"/webjars/**",
"/doc.html",
"/swagger-resources/**",
"/v3/api-docs/**",
"/swagger-ui/**",
// admin监控
"/actuator/**",
"/instances/**"
);
}
}

View File

@ -0,0 +1,52 @@
package com.evotech.hd.authorization.config.security.userdetail;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.evotech.hd.common.core.entity.resource.auth.AuthUser;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class MyUserDetail extends AuthUser implements UserDetails {
private static final long serialVersionUID = 786868339462173799L;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getUsername() {
return this.getUname();
}
@Override
public boolean isAccountNonExpired() {
return UserDetails.super.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return UserDetails.super.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return UserDetails.super.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return this.getStatus() == 1;
}
}

View File

@ -0,0 +1,38 @@
package com.evotech.hd.authorization.config.security.userdetail;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.evotech.hd.authorization.service.ResourceService;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.resource.auth.AuthUser;
import com.evotech.hd.common.core.enums.CodeMsg;
import cn.hutool.core.bean.BeanUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class UserDetailService implements UserDetailsService {
@Resource
private ResourceService resourceService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Result<AuthUser> result = resourceService.loadUserByName(username);
if(!CodeMsg.SUCCESS.getCode().equals(result.getCode())) {
throw new UsernameNotFoundException("账号或密码错误!");
}
AuthUser user = BeanUtil.toBean(result.getData(), AuthUser.class) ;
MyUserDetail userDetail = new MyUserDetail();
BeanUtil.copyProperties(user, userDetail, false);
if (!userDetail.isEnabled()) {
throw new DisabledException("账号状态异常!");
}
return userDetail;
}
}

View File

@ -0,0 +1,36 @@
package com.evotech.hd.authorization.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.authorization.service.CaptchaService;
import com.evotech.hd.common.core.entity.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
@Tag(name = "验证码")
@RestController
@RequestMapping("/captcha")
public class CaptchaController {
@Resource
private CaptchaService captchaService;
@Operation(summary = "获取")
@GetMapping("/get")
public Result<String> getCaptcha() {
return captchaService.getCaptcha();
}
@Operation(summary = "校验", description = "code格式 -- captchaId:code")
@PostMapping("/check")
public Result<Boolean> checkCaptcha(String code) {
return captchaService.checkCaptcha(code);
}
}

View File

@ -0,0 +1,56 @@
package com.evotech.hd.authorization.controller;
import org.apache.http.auth.AuthenticationException;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evotech.hd.authorization.entity.LoginRequest;
import com.evotech.hd.authorization.entity.UserVo;
import com.evotech.hd.authorization.service.LoginService;
import com.evotech.hd.common.core.entity.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
@Tag(name = "登陆")
@RestController
@RequestMapping("/login")
public class LoginController {
@Resource
private LoginService loginService;
@Operation(summary = "登陆")
@PostMapping("/oauthlogin")
@Parameters(
)
@Parameter(name="Authorization", example = "Basic eXRoZDpZVEhEMTIz")
public Result<UserVo> oauthLogin(@RequestHeader String Authorization, @Valid @ParameterObject LoginRequest lr, HttpServletRequest request) throws AuthenticationException {
return loginService.oauthLogin(lr, request);
}
@Operation(summary = "登出")
@DeleteMapping("/oauthlogout")
public Result<String> AuthLogout(HttpServletRequest request) {
return loginService.logout(request);
}
@Operation(summary = "检验token")
@PostMapping("/checktoken")
public Result<Boolean> checkToken(String token) throws AuthenticationException {
return loginService.checkToken(token);
}
}

View File

@ -0,0 +1,25 @@
package com.evotech.hd.authorization.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class LoginRequest {
@Schema(description = "账号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "账号不能为空")
private String username;
@Schema(description = "密码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "密码不能为空")
private String password;
@Schema(description = "验证码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "验证码不能为空")
private String code;
}

View File

@ -0,0 +1,55 @@
package com.evotech.hd.authorization.entity;
import java.io.Serializable;
import java.util.List;
import com.evotech.hd.authorization.config.oauth2.MyAccessToken;
import com.evotech.hd.common.core.entity.resource.auth.AuthRole;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.Data;
@Data
@Schema(name = "登陆时返回前端的对象")
@ApiResponse
public class UserVo implements Serializable {
private static final long serialVersionUID = 1L;
private String uid;
@Schema(name = "账号")
private String uname;
@Schema(name = "姓名")
private String name;
@Schema(description = "账号类型1-开发者2-运营方3-客户")
private Integer type;
@Schema(description = "关联方代码")
private String typeRelateCode;
@Schema(name = "邮箱")
private String email;
@Schema(name = "手机")
private String mobile;
@Schema(name = "性别")
private String sex;
@Schema(name = "头像")
private String avatar;
@Schema(name = "token")
private MyAccessToken token;
@Schema(name = "角色")
List<AuthRole> roleList;
@Schema(name = "权限")
List<String> permCodeList;
}

View File

@ -0,0 +1,11 @@
package com.evotech.hd.authorization.service;
import com.evotech.hd.common.core.entity.Result;
public interface CaptchaService {
public Result<String> getCaptcha();
public Result<Boolean> checkCaptcha(String code);
}

View File

@ -0,0 +1,17 @@
package com.evotech.hd.authorization.service;
import org.apache.http.auth.AuthenticationException;
import com.evotech.hd.authorization.entity.LoginRequest;
import com.evotech.hd.authorization.entity.UserVo;
import com.evotech.hd.common.core.entity.Result;
import jakarta.servlet.http.HttpServletRequest;
public interface LoginService {
public Result<UserVo> oauthLogin(LoginRequest lr, HttpServletRequest request) throws AuthenticationException;
public Result<String> logout(HttpServletRequest request);
public Result<Boolean> checkToken(String token) throws AuthenticationException;
}

View File

@ -0,0 +1,36 @@
package com.evotech.hd.authorization.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import com.evotech.hd.common.core.entity.LoginCacheInfo;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.resource.LogLogin;
import com.evotech.hd.common.core.entity.resource.auth.AuthUser;
import jakarta.validation.constraints.NotNull;
@FeignClient(value = "resource-server")
public interface ResourceService {
@GetMapping(value = "/resource/user/userbyname",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<AuthUser> loadUserByName(@RequestParam("uname") String userName);
@PostMapping(value = "/resource/user/userpermbyid",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<String> userPermById(@NotNull @RequestParam("uid") String userId, @RequestParam("type")Integer type);
@PostMapping(value = "/resource/loginlog/add",
consumes = {MediaType.APPLICATION_JSON_VALUE})
public Result<Integer> addLoginLog(@RequestBody LogLogin log);
@GetMapping(value = "/resource/logininfo/get",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public Result<LoginCacheInfo> loginInfo(@RequestParam("uid") String uid);
}

View File

@ -0,0 +1,55 @@
package com.evotech.hd.authorization.service.impl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.evotech.hd.authorization.service.CaptchaService;
import com.evotech.hd.common.core.constant.HDConstant;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.redis.utils.RedisUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.IdUtil;
import jakarta.annotation.Resource;
@Service
public class CaptchaServiceImpl implements CaptchaService {
@Resource
private RedisUtil redisUtil;
@Value("${yt.captcha.expire:300}")
private Integer captchaExpire;
@Override
public Result<String> getCaptcha() {
RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 30);
captcha.setGenerator(randomGenerator);
captcha.createCode();
String code = captcha.getCode();
String imageBase64Data = captcha.getImageBase64Data();
String captchaId = IdUtil.fastSimpleUUID();
redisUtil.set(HDConstant.CAPTCHA_REDIS_PREFIX + captchaId, code, captchaExpire);
return new Result<String>().success("OK", captchaId + ":" + imageBase64Data);
}
@Override
public Result<Boolean> checkCaptcha(String code) {
String[] codeArr = code.split(":");
if (codeArr.length != 2) {
return new Result<Boolean>().error("验证码格式错误!");
}
String captchaId = codeArr[0];
code = codeArr[1];
if (!redisUtil.hasKey(HDConstant.CAPTCHA_REDIS_PREFIX + captchaId)) {
return new Result<Boolean>().error("验证码错误或已过期");
}
if (code.equals(redisUtil.get(HDConstant.CAPTCHA_REDIS_PREFIX + captchaId).toString())) {
return new Result<Boolean>().success(true);
}
return new Result<Boolean>().error("验证码错误!");
}
}

View File

@ -0,0 +1,203 @@
package com.evotech.hd.authorization.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.http.auth.AuthenticationException;
import org.springframework.beans.BeanUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.evotech.hd.authorization.config.oauth2.MyAccessToken;
import com.evotech.hd.authorization.entity.LoginRequest;
import com.evotech.hd.authorization.entity.UserVo;
import com.evotech.hd.authorization.service.ResourceService;
import com.evotech.hd.authorization.service.CaptchaService;
import com.evotech.hd.authorization.service.LoginService;
import com.evotech.hd.authorization.utils.LoginRequesHeadertUtil;
import com.evotech.hd.authorization.utils.Oauth2AccessTokenUtil;
import com.evotech.hd.authorization.utils.TokenUtil;
import com.evotech.hd.common.core.constant.HDConstant;
import com.evotech.hd.common.core.entity.LoginCacheInfo;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.entity.resource.LogLogin;
import com.evotech.hd.common.core.entity.resource.auth.AuthPermission;
import com.evotech.hd.common.core.entity.resource.auth.AuthRole;
import com.evotech.hd.common.core.entity.resource.auth.AuthUser;
import com.evotech.hd.common.core.enums.CodeMsg;
import com.evotech.hd.common.redis.utils.RedisUtil;
import com.evotech.hd.common.web.util.IpUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
@Service
public class LoginServiceImpl implements LoginService {
public static final Long DEFAULT_TOKEN_EXPIRATION = 7200L;
public static final String TOKEN_END_POINT = "/oauth2/token";
@Resource
private CaptchaService captchaService;
@Resource
private ResourceService resourceService;
@Resource
private RedisUtil redisUtil;
@Resource
private Environment env;
@Override
public Result<UserVo> oauthLogin(LoginRequest lr, HttpServletRequest request) throws AuthenticationException {
// 1. 校验验证码
Result<Boolean> res = captchaService.checkCaptcha(lr.getCode());
if (res.getStatus() != 1) {
return new Result<UserVo>().error(res.getCode(), res.getMsg());
}
// 2. 从请求头获取client信息
Map<String, String> oAuth2Client = LoginRequesHeadertUtil.getOAuth2Client(request);
if (oAuth2Client == null) {
return new Result<UserVo>().error("无客户端Client信息");
}
// 3. 获取token
String url = getTokenUrl();
MyAccessToken token = Oauth2AccessTokenUtil.getToken(url, oAuth2Client.get("clientId"), oAuth2Client.get("clientSecret"), lr.getUsername(), lr.getPassword());
// 4.添加缓存
UserVo uv = new UserVo();
uv.setToken(token);
uv = addRedisCache(uv);
// 5. 添加登录记录
Boolean loginLogFlag = true;
if (loginLogFlag) {
try {
writeLoginLog(request, uv);
} catch (Exception e) {
e.printStackTrace();
}
}
return new Result<UserVo>().success(uv);
}
/**
* 拼接获取token的地址
*/
private String getTokenUrl() {
int port = env.getProperty("server.port", Integer.class) == null?8080:env.getProperty("server.port", Integer.class);
String url = "http://127.0.0.1" + ":" + port;
String path = env.getProperty("server.servlet.context-path");
url = StringUtils.hasText(path) ? url + path : url;
url = url + TOKEN_END_POINT;
return url;
}
private void writeLoginLog(HttpServletRequest request, UserVo uv) {
LogLogin loginLog = new LogLogin();
Date d = new Date();
loginLog.setUid(uv.getUid());
loginLog.setUname(uv.getUname());
loginLog.setName(uv.getName());
loginLog.setLoginTime(d);
loginLog.setCtime(d);
loginLog.setRequestIp(IpUtil.getRemoteIP(request));
String uaStr = request.getHeader("User-Agent");
UserAgent ua = UserAgentUtil.parse(uaStr);
loginLog.setUa(uaStr);
loginLog.setBrowser(ua.getBrowser().toString());
loginLog.setBrowserVersion(ua.getVersion());
loginLog.setOperatingSystem(ua.getOs().toString());
loginLog.setPlatForm(ua.getPlatform().toString());
loginLog.setCreater("SYSTEM");
resourceService.addLoginLog(loginLog);
}
/**
* 登陆缓存内容暂定
* key:
* hd:login:jti:token
* hd:login:jti:user
* hd:login:jti:rcodes
* hd:login:jti:perms
*
*/
private UserVo addRedisCache(UserVo uv) {
String token = uv.getToken().getAccessToken();
String uid = TokenUtil.getUserId(token);
// 1. 请求数据
Result<LoginCacheInfo> res = resourceService.loginInfo(uid);
if (!CodeMsg.SUCCESS.getCode().equals(res.getCode())) {
throw new RuntimeException(res.getMsg());
}
// 2. 处理数据
String jti = TokenUtil.getJti(token);
Object o = redisUtil.get(HDConstant.HD_CACHE_TOKEN_EXP_KEY);
Long tokenExp = o == null ? DEFAULT_TOKEN_EXPIRATION : Long.valueOf(o.toString());
redisUtil.set(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":token", token, tokenExp);
JSONObject jo = JSONUtil.parseObj(res.getData());
AuthUser user = JSONUtil.toBean(jo.getJSONObject("user"), AuthUser.class);
BeanUtils.copyProperties(user, uv);
redisUtil.set(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":user", user, tokenExp);
List<AuthRole> roleList = JSONUtil.toList(jo.getJSONArray("roleList"), AuthRole.class);
uv.setRoleList(roleList);
String rcodes = roleList.stream().map(i -> i.getRcode()).collect(Collectors.joining(","));
redisUtil.set(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":rcodes", rcodes, tokenExp);
List<String> rcodeList = roleList.stream().map(i -> i.getRcode()).toList();
if (!rcodeList.contains(HDConstant.SYSTEM_MANAGER_ROLE_CODE)) {
List<AuthPermission> permissionList = JSONUtil.toList(jo.getJSONArray("permissionList"), AuthPermission.class);
List<String> permUriList = permissionList.stream().map(i -> i.getUri()).toList();
List<String> permCodeList = permissionList.stream().map(i -> i.getCode()).toList();
uv.setPermCodeList(permCodeList);
redisUtil.lSet(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":perms", permUriList.toArray(new String[permUriList.size()]), tokenExp);
}
return uv;
}
@Override
public Result<String> logout(HttpServletRequest request) {
String authorization = request.getHeader(HDConstant.AUTHORIZATION_KEY);
if (authorization != null && authorization.contains(HDConstant.JWT_PREFIX)) {
String token = authorization.substring(HDConstant.JWT_PREFIX.length());
String jti = TokenUtil.getJti(token);
redisUtil.del(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":token");
redisUtil.del(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":user");
redisUtil.del(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":rcodes");
if (redisUtil.hasKey(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":perms")) {
redisUtil.del(HDConstant.LOGIN_CACHE_KEY_PREFIX + jti + ":perms");
}
return new Result<String>().success("退出成功");
} else {
return new Result<String>().error("无token");
}
}
@Override
public Result<Boolean> checkToken(String token) throws AuthenticationException {
// TODO 发送到自带的token验证接口 验证
return new Result<Boolean>().success(true);
}
}

View File

@ -0,0 +1,33 @@
package com.evotech.hd.authorization.utils;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.util.Strings;
import com.evotech.hd.common.core.constant.HDConstant;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletRequest;
public class LoginRequesHeadertUtil {
public static Map<String, String> getOAuth2Client(HttpServletRequest request) {
// 从请求头获取
String basic = request.getHeader(HDConstant.AUTHORIZATION_KEY);
if (StrUtil.isBlank(basic) || !basic.startsWith(HDConstant.BASIC_PREFIX)) {
return null;
}
basic = basic.replace(HDConstant.BASIC_PREFIX, Strings.EMPTY);
//client:secret
String basicPlainText = new String(Base64.getDecoder().decode(basic.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
Map<String, String> m = new HashMap<>();
m.put("clientId", basicPlainText.split(":")[0]);
m.put("clientSecret", basicPlainText.split(":")[1]);
return m;
}
}

View File

@ -0,0 +1,42 @@
package com.evotech.hd.authorization.utils;
import java.util.HashMap;
import java.util.Map;
import com.evotech.hd.authorization.config.oauth2.MyAccessToken;
import com.evotech.hd.common.core.enums.CodeMsg;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
public class Oauth2AccessTokenUtil {
public static MyAccessToken getToken(String url, String clientId, String clientSecret, String username, String password) {
Map<String, Object> m = new HashMap<String, Object>();
m.put("grant_type", "password");
m.put("username", username);
m.put("password", password);
m.put("scope", "ALL");
String body = HttpUtil
.createPost(url)
.basicAuth(clientId, clientSecret)
.form(m)
.execute()
.body();
JSONObject jo = JSONUtil.parseObj(body);
if (CodeMsg.SUCCESS.getCode().equals(jo.getStr("code"))) {
JSONObject jo1 = jo.getJSONObject("data");
MyAccessToken tokenRes = new MyAccessToken();
tokenRes.setAccessToken(jo1.getStr("access_token"));
tokenRes.setExpiresIn(jo1.getLong("expires_in"));
tokenRes.setRefreshToken(jo1.getStr("refresh_token"));
tokenRes.setTokenType(jo1.getStr("token_type"));
return tokenRes;
} else {
throw new RuntimeException("生成token出错");
}
}
}

View File

@ -0,0 +1,49 @@
package com.evotech.hd.authorization.utils;
import java.util.Date;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import cn.hutool.core.date.DateUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
/**
* token解析工具类
*/
public class TokenUtil {
public static JWT parseToJwt(String token) {
JWT parseToken = JWTUtil.parseToken(token);
return parseToken;
}
/**
* 从token中获取userId
*/
public static String getUserId(String token) {
String uid = parseToJwt(token).getPayloads().getStr("uid");
return uid;
}
/**
* 从token中获取jti
*/
public static String getJti(String token) {
String jti = parseToJwt(token).getPayloads().getStr(JwtClaimNames.JTI).replaceAll("-", "");
return jti;
}
/**
* 从token中获取过期时间
*/
public static Date getExp(String token) {
String exp = parseToJwt(token).getPayloads().getStr(JwtClaimNames.EXP).toString();
return DateUtil.date(Long.valueOf(exp) * 1000);
}
}

View File

@ -0,0 +1,12 @@
{"properties": [
{
"name": "security.token.public_key_base64",
"type": "java.lang.String",
"description": "A description for 'security.token.public_key_base64'"
},
{
"name": "security.token.private_key_base64",
"type": "java.lang.String",
"description": "A description for 'security.token.private_key_base64'"
}
]}

View File

@ -0,0 +1,41 @@
security.token.public_key_base64: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA23qzsZ/51EA9PAYvGyGwlSYEZxnGmF3FxiraUvLyeiBawyGFCHprnmG+fr+80KmhGh1UMbNwuwIRgn8tEY9TjlfK7iNhsX1QGEjrL6LggDJeXlA/XN4kRPY9sW7+VQpr1MPJjB5tQYVkPLvv3L8v/7k5hcPEoHIFwQoOnYBuQqLkj38EQ/NlOQLKzhyDRFWSm+6WzQF46fTMRLzRpfuFhUBk9oF3B8Y/vAajY90QDw5xdV0uGreK6CgESldCzx6hh1AHiLLICINRKTNL1t+uhRaz/f0xm8baxaKzusv2relz69GADCQl/GrTArcfWXVXqvetFzsQOt35EBOpxl3nRwIDAQAB
security.token.private_key_base64: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDberOxn/nUQD08Bi8bIbCVJgRnGcaYXcXGKtpS8vJ6IFrDIYUIemueYb5+v7zQqaEaHVQxs3C7AhGCfy0Rj1OOV8ruI2GxfVAYSOsvouCAMl5eUD9c3iRE9j2xbv5VCmvUw8mMHm1BhWQ8u+/cvy//uTmFw8SgcgXBCg6dgG5CouSPfwRD82U5AsrOHINEVZKb7pbNAXjp9MxEvNGl+4WFQGT2gXcHxj+8BqNj3RAPDnF1XS4at4roKARKV0LPHqGHUAeIssgIg1EpM0vW366FFrP9/TGbxtrForO6y/at6XPr0YAMJCX8atMCtx9ZdVeq960XOxA63fkQE6nGXedHAgMBAAECggEAGU5mDrp/31nC1btuzgWN6zyVcF/X9rSFO8qwHrVVgQBfBrXENYyMARiLEuk/t51/blfoX8ytWFquMyo/w5EPlM+JnfilaIYm6I3r1DPHB/EG0YAWNjqE2xC7X0yJdbu8YC1s/UN63H2pZ5lR2FWRDr7II6TFdKyA/lePBNkMnZEc6SueoCFxUVNjalE9XNwaBqTvS1omxh11m/I6y+V3YW3ymwFN4tXdIWv1NsM4l3HfAbAVreqSRPL9Z1HHPpAD06aWKZD4BqA1umHs31IkbgQ4RvsUu2XeXOI5a/T+7kXHItNk0u2ozM7H92M4VrEvMXAso7woqs9bHxBouaQVnQKBgQDiD3Iz2r/PRghARoZdO8vzzph4/p7DscjJmv3V/SqwWSVAkLVJbjEDB6omuar2EW7nxpW7yc9hwRLB5fEYIVUeUbkhTCY33WAikKYAkdGqAV9N/nI2euSavdluo9LB1FwuTz4yLXydPP+c97EiwAY4Xle0Zbg41g1DhAz6rqrnswKBgQD4jCBnDvb/GQROip0FDLSu63STLySwka/bP8OIC8x4UN6eI+heT9sPoxBBsUdHwSKUfOP1oKoW+fLYGyX/2Gn64M/5L6ds5e7yaIZFykNVU0ysItTuAFt9tsOEZtxhRZlaQb9F3RRkzd10rZB0s0Pueg9X19ncUvpHqn+aIyO4HQKBgFA+CVystk893anrHsCzfBbj661vPC3cypf9g0LVJbUJP7bmZuNU0OLxco1idHP6BjMRg47v+MQLQ7w+AdF963firGNMY3iLBRff3nzvRcwhxpGp0yLRhpoC785dKm+RENODX2FyUfyCbX1rUp4yKUMTAfDP9o9+M6EWm4DURWgPAoGAcxr6GqBclTFxxCn/pAoJV4TlvRA1LqyZw7EZDdVhAUt6fcRlZeAXtHsxGStPtpRkPl5EeEnK288vvxN7mqwQEMayqlV+dTlbWto7bPDKFCb6uDF9aSezN1o/2/6DC21fIuSV/3XubuzEJbgH0XP//t56YpUtaRLoo+37IFgrv6UCgYAsTEQkg8la0BgKL9maiubnqdbl9nwS6l4lJqtlNNLaWIkiePmsY1dlkY91oQbc+YH3IJFexX/viuzZBXBJm9Hnf/MG+VeUz0f6ldZ2tI8fYbXIu0YadNmh0nnEAsUMaG7wYK6xnXiklh0LSjZUfWGmC4x/s2s1ixQme5ZIO2IPKQ==
# 1.server
server:
port: 9101
servlet:
context-path: /oauth2
# 2. log
logging:
config: classpath:logback-spring.xml
# 3.spring
spring:
application:
name: oauth2-server
config:
import:
- nacos:${spring.application.name}.yaml?refreshEnabled=true
cloud:
nacos:
serverAddr: 192.168.5.213:8848
username: nacos
password: nacos
discovery:
register-enabled: true
#server-addr: 10.10.1.6:8848
#ip: 192.168.5.200
metadata:
management:
context-path: ${server.servlet.context-path}/actuator
# 5.为springbootadmin开启所有检查
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
# xml文件中配置的日志文件的名称
logfile:
external-file: /logs/oauth2/oauth2-server.log

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL如果设置为WARN则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时配置文件如果发生改变将会被重新加载默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔如果没有给出时间单位默认单位是毫秒。当scan为true时此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<property name="CONTEXT_NAME" value="oauth2-server" />
<property name="LOG_PATH" value="/logs/oauth2" />
<property name="MAX_FILE_SIZE" value="100MB" />
<property name="MAX_HISTORY" value="30" />
<!--读取配置中心的属性 -->
<!-- <springProperty scope="context" name="LOG_PATH_NAME" source="logging.file.name"/> -->
<contextName>${CONTEXT_NAME}</contextName>
<!--输出到控制台 -->
<!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%i索引【从数字0开始递增】 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<!--%logger{50}:表示logger名字最长50个字符否则按照句点分割 -->
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''} -- %msg%n</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--所有日志输出到文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志名,指定最新的文件名其他文件名使用FileNamePattern -->
<File>${LOG_PATH}/${CONTEXT_NAME}.log</File>
<!--日志文件输出格式 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{50}.%M\(%line\) -- %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 后面可以加.zip -->
<fileNamePattern>${LOG_PATH}/${CONTEXT_NAME}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--日志文件保留天数 -->
<maxHistory>${MAX_HISTORY}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!--文件达到 最大时会被压缩和切割 -->
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- ERROR 日志 -->
<!-- 暂时不用了,输出到一个文件 -->
<!-- 异步日志输出看情况配置 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE" />
</appender>
<!-- 统一配置日志输出root标签和logger标签如果这开启了就把不同环境的关上 -->
<!-- root标签相当于开关只有把定义的appender添加到这里 才会生效有日志 -->
<root level="Info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
</root>
<!-- logger标签用来设置某一个包或者具体的某一个类的日志打印级别 -->
<logger name="org.springframework.jdbc.core.JdbcTemplate" level="INFO" />
<!-- 配置不同环境的日志输出root标签和logger标签 -->
<!-- <springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
</root>
<logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG" />
</springProfile> -->
<!-- <springProfile name="prod">
<root level="INFO">
<appender-ref ref="ASYNC_FILE" />
</root>
</springProfile> -->
</configuration>

View File

@ -0,0 +1,13 @@
package com.evotech.hd.authorization;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AuthorizationServerApplicationTests {
@Test
void contextLoads() {
}
}

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.evotech.hd</groupId>
<artifactId>base-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>common-core</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
<!-- 注解校验参数需要的2个包2个都需要 -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
</dependencies>
<build>
</build>
</project>

View File

@ -0,0 +1,74 @@
package com.evotech.hd.common.core.constant;
public interface HDConstant {
/**
* 系统管理员角色
*/
String SYSTEM_MANAGER_ROLE_CODE = "SYSADMIN";
/**
* 认证请求头key
*/
String AUTHORIZATION_KEY = "Authorization";
/**
* JWT令牌前缀
*/
String JWT_PREFIX = "Bearer ";
/**
* Basic认证前缀
*/
String BASIC_PREFIX = "Basic ";
/**
* JWT载体key
*/
String JWT_PAYLOAD_KEY = "payload";
/**
* jwt中添加的属性
*/
String USER_ID_KEY = "uid";
String ROLE_CODE_KEY = "rcodes";
/**
* 登陆验证码缓存key
* hd:login:captcha: + captchaId
*/
String CAPTCHA_REDIS_PREFIX = "hd:login:captcha:";
/**
* 登陆缓存内容前缀
* key: hd:login:jti:token
*/
String LOGIN_CACHE_KEY_PREFIX = "hd:login:";
/**
* 缓存系统token有效时间s
*/
String HD_CACHE_TOKEN_EXP_KEY = "hd:cache:tokenExp";
/**
* 资源权限类型
*/
String RESOURCE_TYPE_PERM = "PER";
String RESOURCE_TYPE_MENU = "MENU";
/**
* 交换数据非对称加密RSA秘钥前缀
*/
String HD_STATION_SECRET_KEY_RSA_PREFIX = "hd:station:secretKey:rsa:";
/**
* 交换数据对称加密AES秘钥前缀
*/
String HD_STATION_SECRET_KEY_AES_PREFIX = "hd:station:secretKey:aes:";
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.ProxyOperater;
/**
* @author zrb
* @since 2024-10-15
*/
public interface ProxyOperaterDao extends BaseMapper<ProxyOperater> {
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.auth;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.auth.AuthMenu;
/**
* @author zrb
* @since 2024-09-04
*/
public interface AuthMenuDao extends BaseMapper<AuthMenu> {
}

View File

@ -0,0 +1,18 @@
package com.evotech.hd.common.core.dao.resource.auth;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.auth.AuthPermission;
/**
* @author zrb
* @since 2024-09-04
*/
public interface AuthPermissionDao extends BaseMapper<AuthPermission> {
List<AuthPermission> listPermByUid(@Param("uid") String uid);
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.auth;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.auth.AuthRole;
/**
* @author zrb
* @since 2024-09-04
*/
public interface AuthRoleDao extends BaseMapper<AuthRole> {
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.auth;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.auth.AuthRoleResource;
/**
* @author zrb
* @since 2024-09-04
*/
public interface AuthRoleResourceDao extends BaseMapper<AuthRoleResource> {
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.auth;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.auth.AuthUser;
/**
* @author zrb
* @since 2024-09-04
*/
public interface AuthUserDao extends BaseMapper<AuthUser> {
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.auth;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.auth.AuthUserRole;
/**
* @author zrb
* @since 2024-09-04
*/
public interface AuthUserRoleDao extends BaseMapper<AuthUserRole> {
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.dict;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.dict.AdmdvsInfo;
/**
* @author zrb
* @since 2024-09-05
*/
public interface AdmdvsInfoDao extends BaseMapper<AdmdvsInfo> {
}

View File

@ -0,0 +1,19 @@
package com.evotech.hd.common.core.dao.resource.dict;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.request.ListDictRequest;
import com.evotech.hd.common.core.entity.resource.dict.Dict;
/**
* @author zrb
* @since 2024-09-05
*/
public interface DictDao extends BaseMapper<Dict> {
List<Dict> listDict(@Param("ldr") ListDictRequest ldr);
}

View File

@ -0,0 +1,12 @@
package com.evotech.hd.common.core.dao.resource.dict;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evotech.hd.common.core.entity.resource.dict.DictType;
/**
* @author zrb
* @since 2024-09-05
*/
public interface DictTypeDao extends BaseMapper<DictType> {
}

View File

@ -0,0 +1,21 @@
package com.evotech.hd.common.core.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.Min;
import lombok.Data;
@Data
@Schema(name = "分页基类")
public class BasePageRequest {
@Schema(description = "每页条数", requiredMode = RequiredMode.REQUIRED, example = "10" )
@Min(value = 1)
private Integer pageSize = 10;
@Schema(description = "页数", requiredMode = RequiredMode.REQUIRED, example = "1")
@Min(1)
private Integer pageNo = 1;
}

View File

@ -0,0 +1,28 @@
package com.evotech.hd.common.core.entity;
import java.util.List;
import com.evotech.hd.common.core.entity.resource.auth.AuthPermission;
import com.evotech.hd.common.core.entity.resource.auth.AuthRole;
import com.evotech.hd.common.core.entity.resource.auth.AuthUser;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "登陆时缓存数据")
public class LoginCacheInfo {
@Schema(description = "token")
private String token;
@Schema(description = "权限")
private AuthUser user;
@Schema(description = "角色")
List<AuthRole> roleList;
@Schema(description = "权限")
List<AuthPermission> permissionList;
}

View File

@ -0,0 +1,164 @@
package com.evotech.hd.common.core.entity;
import java.io.Serializable;
import com.evotech.hd.common.core.enums.CodeMsg;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author zrb
* @date 2024年9月2日17:25:54
*/
@Data
@Schema(name = "请求返回对象")
public class Result<T> implements Serializable {
private static final long serialVersionUID = -7806513009135956518L;
@Schema(description = "请求处理状态", example = "1")
private Integer status;
@Schema(description = "状态码", example = "1000")
private String code;
@Schema(description = "返回消息")
private String msg;
@Schema(description = "返回数据")
private Object data;
private T obj;
public Result(Integer status, String code, String msg, Object data) {
this.status = status;
this.code = code;
this.msg = msg;
this.data = data;
}
public Result(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public Result(Integer status) {
this.status = status;
}
public Result() {
}
public Result(Integer status, String code, String msg) {
this.status = status;
this.code = code;
this.msg = msg;
}
public Result(Integer status, String msg) {
this.status = status;
this.msg = msg;
}
public Result<T> success(Object o) {
this.status = 1;
this.code = CodeMsg.SUCCESS.getCode();
this.msg = CodeMsg.SUCCESS.getMsg();
this.data = o;
return this;
}
public Result<T> success(String msg, Object o) {
this.status = 1;
this.code = CodeMsg.SUCCESS.getCode();
this.msg = msg;
this.data = o;
return this;
}
public Result<T> success(String msg) {
this.status = 1;
this.code = CodeMsg.SUCCESS.getCode();
this.msg = msg;
this.data = CodeMsg.SUCCESS.getMsg();
return this;
}
public Result<T> success(String code, String msg, Object o) {
this.status = 1;
this.code = code;
this.msg = msg;
this.data = o;
return this;
}
public Result<T> success(CodeMsg cm, Object o) {
this.status = 0;
this.code = cm.getCode();
this.msg = cm.getMsg();
this.data = o;
return this;
}
public Result<T> error(String msg) {
this.status = 0;
this.code = CodeMsg.ERROR.getCode();
this.msg = msg;
this.data = "ERROR";
return this;
}
public Result<T> error(String code, String msg) {
this.status = 0;
this.code = code;
this.msg = msg;
this.data = "ERROR";
return this;
}
public Result<T> error(CodeMsg cm) {
this.status = 0;
this.code = cm.getCode();
this.msg = cm.getMsg();
return this;
}
public Result<T> error(CodeMsg cm, Object o) {
return error(cm.getCode(), cm.getMsg(), o);
}
public Result<T> error(String code, String msg, Object o) {
this.status = 0;
this.code = code;
this.msg = msg;
this.data = o;
return this;
}
public Result<T> error(String msg, Object o) {
this.status = 0;
this.code = CodeMsg.ERROR.getCode();
this.msg = msg;
this.data = o;
return this;
}
public Result<T> exception(Object data) {
this.status = -1;
this.code = CodeMsg.ERROR.getCode();
this.msg = CodeMsg.ERROR.getMsg();
this.data = data==null?"未知异常":data;
return this;
}
public Result<T> bussinessException(String code, String msg, Object o) {
this.status = -1;
this.code = code;
this.msg = msg;
this.data = o;
return this;
}
}

View File

@ -0,0 +1,139 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* @author zrb
* @since 2024-10-15
*/
@Data
@TableName("yt_t_battery_station")
@Schema(name = "BatteryStation", description = "换电站")
public class BatteryStation implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Hidden
private Integer pkId;
@Schema(description = "归属运营商ID", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "无关联运营商信息")
private String proxyId;
@Schema(description = "站点名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "站点名称不能为空")
private String name;
@Schema(description = "站点编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "站点编码不能为空")
private String code;
@Schema(description = "状态1-正常营业2-正常停运3-故障停运4-指令停运9-其它", requiredMode = RequiredMode.REQUIRED)
private Integer status;
@Schema(description = "站点类型ID")
private Integer type;
@Schema(description = "地址")
private String address;
@Schema(description = "地址-省")
private String addressProvince;
@Schema(description = "地址-市")
private String addressCity;
@Schema(description = "地址-区县")
private String addressArea;
@Schema(description = "注册日期")
private String registerDate;
@Schema(description = "联系人")
private String contacts;
@Schema(description = "联系电话")
private String phone;
@Schema(description = "删除标识1-已删除0-未删除")
private Integer delFlag;
@Schema(description = "激活日期")
private String activeDate;
@Schema(description = "经纬度信息")
private String locationPoint;
@Schema(description = "全天营业1-是0-否")
private Integer openAllDay;
@Schema(description = "通道数")
private Integer tdQuantity;
@Schema(description = "机器人数量")
private Integer jqrQuantity;
@Schema(description = "充电机数量")
private Integer cdjQuantity;
@Schema(description = "电池仓数量")
private Integer dccQuantity;
@Schema(description = "电池数量")
private Integer dcQuantity;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
@Schema(description = "电池列表", hidden = true)
@TableField(exist = false)
private List<BatteryStationDc> dcList;
@Schema(description = "电池仓列表", hidden = true)
@TableField(exist = false)
private List<BatteryStationDcc> dccList;
@Schema(description = "电机列表", hidden = true)
@TableField(exist = false)
private List<BatteryStationDj> djList;
@Schema(description = "机器人列表", hidden = true)
@TableField(exist = false)
private List<BatteryStationRobot> robotList;
@Schema(description = "换电费用标准", hidden = true)
@TableField(exist = false)
private List<BatteryStationHdFeeStandard> feeStandardList;
}

View File

@ -0,0 +1,88 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-17
*/
@Getter
@Setter
@TableName("yt_t_battery_station_dc")
@Schema(name = "电站-电池")
public class BatteryStationDc implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "电池型号编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "型号编码不能为空")
private String typeCode;
@Schema(description = "电池编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "电池编码不能为空")
private String batCode;
@Schema(description = "生产日期", example = "yyyyMMdd")
private String productionDate;
@Schema(description = "注册时间", example = "yyyyMMdd")
private String registrationDate;
@Schema(description = "初始来源1-站2-车", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "初始来源不能为空")
private Integer sourceFrom;
@Schema(description = "站码或车牌照")
private String sourceCode;
@Schema(description = "状态1-出租中2-充电中3-充电完毕4-故障5-其它", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "换电站编码")
private String stationCode;
@Schema(description = "换电站编码")
private String stationName;
@Schema(description = "当前电量")
private Integer soc;
@Schema(description = "删除标志1-已删除0-未删除", hidden = true)
private Integer delFlag;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,66 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-17
*/
@Getter
@Setter
@TableName("yt_t_battery_station_dcc")
@Schema(name = "电站-电池仓")
public class BatteryStationDcc implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站编码不能为空")
private String stationCode;
@Schema(description = "换电站编码")
private String stationName;
@Schema(description = "电池仓序号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "电池仓序号不能为空")
private String dccNo;
@Schema(description = "状态1-正常2-检修, 3-坏", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,80 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-17
*/
@Getter
@Setter
@TableName("yt_t_battery_station_dj")
@Schema(name = "电站-电机")
public class BatteryStationDj implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站编码不能为空")
private String stationCode;
@Schema(description = "换电站编码")
private String stationName;
@Schema(description = "充电机编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "充电机编码不能为空")
private String code;
@Schema(description = "充电机软件版本", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "充电机软件版本不能为空")
private String version;
@Schema(description = "电池仓序号")
private String dccNo;
@Schema(description = "充电连接类型1-连接器2-充电枪", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "充电连接类型不能为空")
private Integer switchType;
@Schema(description = "状态1-正常2-检修, 3-坏", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "充电枪数量")
private Integer gunNum;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,95 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.hutool.core.date.DatePattern;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-17
*/
@Getter
@Setter
@TableName("yt_t_battery_station_hd_fee_standard")
@Schema(name = "电站-换电费用标准")
public class BatteryStationHdFeeStandard implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "费用标准名称")
private String name;
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站编码不能为空")
private String stationCode;
@Schema(description = "换电站编码")
private String stationName;
@Schema(description = "开始日期", requiredMode = RequiredMode.REQUIRED, example = DatePattern.PURE_DATE_PATTERN)
@NotBlank(message = "开始时间不能为空")
private String dayBegin;
@Schema(description = "结束日期", requiredMode = RequiredMode.REQUIRED, example = DatePattern.PURE_DATE_PATTERN)
@NotBlank(message = "结束时间不能为空")
private String dayEnd;
@Schema(description = "正常换电服务费")
private BigDecimal commonRemainFee;
@Schema(description = "换电时正常电量范围")
private String commonRemainSocRange;
@Schema(description = "换电时电量剩余过多的soc界定")
private Integer moreRemainSoc;
@Schema(description = "换电时电量剩余过多的服务费")
private BigDecimal moreRemainFee;
@Schema(description = "换电时电量剩余一般的soc界定")
private Integer fewRemainSoc;
@Schema(description = "换电时电量剩余一般的服务费")
private BigDecimal fewRemainFee;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
@Schema(description = "换电费用标准细节", hidden = true)
@TableField(exist = false)
private List<BatteryStationHdFeeStandardDetail> detailList;
}

View File

@ -0,0 +1,85 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.hutool.core.date.DatePattern;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-17
*/
@Getter
@Setter
@TableName("yt_t_battery_station_hd_fee_standard_detail")
@Schema(name = "电站-换电费用标准细节")
public class BatteryStationHdFeeStandardDetail implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站编码不能为空")
private String stationCode;
@Schema(description = "收费规则ID", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer standardId;
@Schema(description = "每公里收费")
private BigDecimal eachKmFee;
@Schema(description = "每SOC收费")
private BigDecimal eachSocFee;
@Schema(description = "每度电收费")
private BigDecimal eachKwhFee;
@Schema(description = "开始时间", requiredMode = RequiredMode.REQUIRED, example = DatePattern.NORM_TIME_PATTERN)
@NotNull
@DateTimeFormat(pattern = DatePattern.NORM_TIME_PATTERN)
private LocalTime timeBegin;
@Schema(description = "结束时间", requiredMode = RequiredMode.REQUIRED, example = DatePattern.NORM_TIME_PATTERN)
@NotNull
@DateTimeFormat(pattern = DatePattern.NORM_TIME_PATTERN)
private LocalTime timeEnd;
@Schema(description = "谷段服务费")
private BigDecimal timeServiceFee;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,69 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-17
*/
@Getter
@Setter
@TableName("yt_t_battery_station_robot")
@Schema(name = "电站-机器人")
public class BatteryStationRobot implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站编码不能为空")
private String stationCode;
@Schema(description = "换电站编码")
private String stationName;
@Schema(description = "机器人编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "机器人编码不能为空")
private String code;
@Schema(description = "状态1-正常", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "运行模式1-就地2-远程")
private Integer runMode;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,75 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-12-06
*/
@Data
@TableName("yt_t_battery_trace")
@Schema(name = "电池追溯表")
public class BatteryTrace implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "电池编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String batCode;
@Schema(description = "轨迹点类型1-电站2-车辆", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer pointType;
@Schema(description = "开始时间", example = "yyyy-MM-dd HH:mm:ss", requiredMode = RequiredMode.REQUIRED)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date beginTime;
@Schema(description = "轨迹点编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String pointCode;
@Schema(description = "轨迹点名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String pointName;
@Schema(description = "结束时间", example = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date endTime;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,74 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-11-22
*/
@Getter
@Setter
@TableName("yt_t_order_recharge")
@Schema(name = "充值订单")
public class OrderRecharge implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "订单时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date orderTime;
@Schema(description = "订单编码", hidden = true)
private String orderNo;
@Schema(description = "换电站编码")
private String stationCode;
@Schema(description = "换电站名称")
private String stationName;
@Schema(description = "钱包账户账号")
private String accountCode;
@Schema(description = "充值账号ID")
private String userId;
@Schema(description = "交易编码")
private String tradeNo;
@Schema(description = "删除状态1-已删除0-未删除", hidden = true)
private Integer delFlag;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,178 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-11-22
*/
@Data
@TableName("yt_t_order_swap_battery")
@Schema(name = "换电订单信息表")
public class OrderSwapBattery implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "预约订单ID")
private Integer orderPreId;
@Schema(description = "预约用户")
private String orderPreUid;
@Schema(description = "预约用户名称")
private String orderPreUname;
@Schema(description = "预约用户手机")
private String orderPrePhone;
@Schema(description = "类型1-换电2-充电", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "订单类型不能为空")
private Integer orderType;
@Schema(description = "订单编码", hidden = true)
private String orderNo;
@Schema(description = "车牌号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "车牌号不能为空")
private String plateNum;
@Schema(description = "订单时间", example = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date orderTime;
@Schema(description = "换电站编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站编码不能为空")
private String stationCode;
@Schema(description = "换电站名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "换电站名称不能为空")
private String stationName;
@Schema(description = "金额账号")
private String accountCode;
@Schema(description = "订单金额")
private Integer amount;
@Schema(description = "订单状态1-已创建2-换电中3-换电完成4-充电中5-充电完成6-待结算7-已完成9-已取消")
private Integer status;
@Schema(description = "计算费用方式1-ODO2-SOC3-按电量")
private Integer feeType;
@Schema(description = "基础费用")
private Integer basicFee;
@Schema(description = "服务开始时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date serviceTimeBegin;
@Schema(description = "服务结束时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date serviceTimeEnd;
@Schema(description = "服务费")
private Integer serviceFee;
@Schema(description = "上次租赁电池时车辆里程")
private BigDecimal lastRentBatCarOdo;
@Schema(description = "归还电池时车辆里程")
private BigDecimal nowReturnBatCarOdo;
@Schema(description = "按ODO换电费")
private BigDecimal odoAmount;
@Schema(description = "充电开始时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date chargeTimeBegin;
@Schema(description = "充电结束时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date chargeTimeEnd;
@Schema(description = "总充电量")
private BigDecimal electAmount;
@Schema(description = "租借电池包仓位")
private Integer rentBatNo;
@Schema(description = "租用电池包编码")
private String rentBatCode;
@Schema(description = "租用电池包SOC")
private Integer rentBatSoc;
@Schema(description = "归还电池包编码")
private String returnBatCode;
@Schema(description = "归还电池包仓位")
private Integer returnBatNo;
@Schema(description = "归还电池包SOC")
private Integer returnBatSoc;
@Schema(description = "归还电池租出时soc")
private Integer returnBatRentSoc;
@Schema(description = "归还电池租出的换电站编码")
private String returnBatRentStationCode;
@Schema(description = "归还电池租出的换电站")
private String returnBatRentStationName;
@Schema(description = "换电模式1-全自动2-半自动3-人工干预 ")
private Integer changeMode;
@Schema(description = "换电车道 1-A 车道;2-B 车道")
private Integer changeLane;
@Schema(description = "删除状态1-已删除0-未删除", hidden = true)
private Integer delFlag;
@Schema(description = "交易编码", hidden = true)
private String tradeNo;
@Schema(description = "备注信息")
private String remark;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,84 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import lombok.Data;
/**
* @author zrb
* @since 2024-12-04
*/
@Data
@TableName("yt_t_order_swap_battery_pre")
@Schema(name = "换电预约订单")
public class OrderSwapBatteryPre implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(description = "ID", hidden = true)
private Integer pkId;
@Schema(description = "来源1-小程序2-云端3-站端", requiredMode = RequiredMode.REQUIRED)
private Integer from;
@Schema(description = "来源是站端时记录发送Id其他来源不需要", hidden = true)
private String sourceId;
@Schema(description = "预约人编码")
private String ucode;
@Schema(description = "预约人姓名")
private String uname;
@Schema(description = "手机号码")
private String phone;
@Schema(description = "车牌号")
private String plateNum;
@Schema(description = "换电站编码")
private String stationCode;
@Schema(description = "换电站名称")
private String stationName;
@Schema(description = "预约时间", hidden = true)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date reservationTime;
@Schema(description = "预约换电日期", example = "yyyyMMdd", requiredMode = RequiredMode.REQUIRED)
private String swapDay;
@Schema(description = "预约换电时间段", example = "8:00-10:00")
private String swapDuration;
@Schema(description = "状态1-预约成功2-到店使用3-取消4-过期", hidden = true)
private Integer status;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,56 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author zrb
* @since 2024-12-11
*/
@Data
@TableName("yt_t_order_swap_battery_step")
@Schema(name = "换电步骤记录")
public class OrderSwapBatteryStep implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(description = "ID", hidden = true)
private Integer pkId;
@Schema(description = "订单编码")
private String orderNo;
@Schema(description = "步骤1-车辆进站2-车辆到达指定位置3-对中机构4-取新电5-拆旧电6-装新电7-放旧电8-完成")
private Integer step;
@Schema(description = "步骤时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date stepTime;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,106 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author zrb
* @since 2024-11-22
*/
@Data
@TableName("yt_t_trade_detail")
@Schema(name = "交易信息表")
public class TradeDetail implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "换电站编码")
private String stationCode;
@Schema(description = "账号编码")
private String accountCode;
@Schema(description = "账号")
private String accountName;
@Schema(description = "交易编码")
private String outTradeNo;
@Schema(description = "交易类型1-充值2-订单消费3-提现")
private Integer tradeType;
@Schema(description = "支付方式1-账户余额2-微信3-支付宝4-网银5 -充电补偿")
private Integer payType;
@Schema(description = "订单数量")
private Integer orderCount;
@Schema(description = "订单编码")
private String orderNums;
@Schema(description = "交易金额")
private Integer tradeAmount;
@Schema(description = "交易前账户余额")
private Integer preAccountAmount;
@Schema(description = "交易后账户余额")
private Integer afterAccountAmount;
@Schema(description = "优惠券编码")
private String couponCode;
@Schema(description = "优惠金额")
private Integer giftAmount;
@Schema(description = "微信支付订单号")
private String transactionId;
@Schema(description = "支付结果: 0-未支付, 1-已支付, 2-支付失败")
private Integer payResult;
@Schema(description = "在微信等支付网关创建支付订单时失败,返回的失败原因")
private String payMsg;
@Schema(description = "删除标识1-已删除0-未删除")
private Integer delFlag;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
@Schema(description = "退款结果: 0-未退款, 1-退款中2-已退款, 3-退款失败")
private Integer refundResult;
@Schema(description = "在微信等退款网关创建退款订单时失败,返回的失败原因")
private String refundMsg;
@Schema(description = "退款返回数据")
private String refundReturn;
}

View File

@ -0,0 +1,121 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-11-22
*/
@Data
@TableName("yt_t_vehicle_info")
@Schema(name = "车辆信息表")
public class VehicleInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "型号编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "型号编码不能为空")
private String typeCode;
@Schema(description = "车辆识别代码VIN号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "VIN号不能为空")
private String vinNo;
@Schema(description = "车架号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "车架号不能为空")
private String frameworkNo;
@Schema(description = "车主类型1-个人2-企业", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "车主类型不能为空")
private Integer ownerType;
@Schema(description = "车主ID")
private String ownerId;
@Schema(description = "车主名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "车主名称不能为空")
private String ownerName;
@Schema(description = "车牌号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "车牌号不能为空")
private String plateNum;
@Schema(description = "引擎号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "引擎号不能为空")
private String engineNo;
@Schema(description = "座位数")
private Integer seatsCount;
@Schema(description = "车身颜色")
private String carColor;
@Schema(description = "车籍/归属地")
private String carArea;
@Schema(description = "出厂日期")
private String productionDate;
@Schema(description = "购车日期")
private String purchaseDate;
@Schema(description = "上牌日期")
private String boardDate;
@Schema(description = "首次登记日期")
private String registrationDate;
@Schema(description = "消费方式1-电量2-里程", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "消费方式不能为空")
private Integer usageType;
@Schema(description = "总里程")
private BigDecimal totalMileage;
@Schema(description = "联系电话")
private String phone;
@Schema(description = "删除状态1-已删除0-未删除", hidden = true)
private Integer delFlag;
@Schema(description = "公司名称")
private String cname;
@Schema(description = "组织机构代码")
private String ccode;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,69 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-11-22
*/
@Data
@TableName("yt_t_vehicle_wechat_user_relation")
@Schema(name = "车辆和用户关系")
public class VehicleWechatUserRelation implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "用户编码", requiredMode = RequiredMode.REQUIRED)
private String ucode;
@Schema(description = "用户名称", requiredMode = RequiredMode.REQUIRED)
private String uname;
@Schema(description = "微信用户标识")
private String openid;
@Schema(description = "车牌号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "车牌号不能为空")
private String plateNum;
@Schema(description = "车主类型1-个人2-企业", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "车主类型不能为空")
private Integer ownerType;
@Schema(description = "关联人手机号", requiredMode = RequiredMode.REQUIRED)
private String phone;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,88 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-11-22
*/
@Data
@TableName("yt_t_wallet_account")
@Schema(name = "资金钱包账户表")
public class WalletAccount implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "户主类型1-个人2-企业", requiredMode = RequiredMode.REQUIRED)
@NotNull(message = "类型不能为空")
private Integer ownerType;
@Schema(description = "户主ID", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "用户编码不能为空")
private String ownerId;
@Schema(description = "编码", hidden = true)
private String code;
@Schema(description = "账户总金额,分:总金额=充值金额+赠送金额")
private Integer totalAmount;
@Schema(description = "充值余额,分")
private Integer rechargeAmount;
@Schema(description = "赠送金额,分")
private Integer giftAmount;
@Schema(description = "积分余额")
private Integer point;
@Schema(description = "押金")
private Integer deposit;
@Schema(description = "SN码")
private String snCode;
@Schema(description = "租金")
private Integer rent;
@Schema(description = "状态")
private Boolean status;
@Schema(description = "引入站点", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "站点编码不能为空")
private String stationCode;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,93 @@
package com.evotech.hd.common.core.entity.cloud;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-11-22
*/
@Data
@TableName("yt_t_wallet_account_detail")
@Schema(name = "资金账户明细")
public class WalletAccountDetail implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
@Schema(hidden = true)
private Integer pkId;
@Schema(description = "编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String code;
@Schema(description = "交易编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank
private String tradeNo;
@Schema(description = "交易前账户总金额,分:总金额=充值金额+赠送金额", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer preTotalAmount;
@Schema(description = "交易前充值余额,分", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer preRechargeAmount;
@Schema(description = "交易前赠送金额,分", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer preGiftAmount;
@Schema(description = "交易总金额,分:总金额=充值金额+赠送金额", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer tradeTotalAmount;
@Schema(description = "交易充值余额,分", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer tradeRechargeAmount;
@Schema(description = "交易赠送金额,分", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer tradeGiftAmount;
@Schema(description = "交易后总金额,分:总金额=充值金额+赠送金额", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer afterTotalAmount;
@Schema(description = "交易后充值余额,分", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer afterRechargeAmount;
@Schema(description = "交易后赠送金额,分", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer afterGiftAmount;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,32 @@
package com.evotech.hd.common.core.entity.request;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "查询字典参数")
public class ListDictRequest {
@Schema(description = "字典类型id")
private String typeId;
@Schema(description = "字典类型名")
private String typeName;
@Schema(description = "字典类型编码")
private String typeCode;
@Schema(description = "字典id")
private String dictId;
@Schema(description = "字典名")
private String dictName;
@Schema(description = "字典编码")
private String dictCode;
@Schema(description = "字典值")
private String dictValue;
}

View File

@ -0,0 +1,73 @@
package com.evotech.hd.common.core.entity.resource;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
/**
* @author zrb
* @date 2024年9月6日08:35:28
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("yt_log_login")
@Schema(name = "登录日志", description = "登录日志")
public class LogLogin implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "登录人ID")
private String uid;
@Schema(description = "操作IP")
private String requestIp;
@Schema(description = "登录人姓名")
private String name;
@Schema(description = "账号")
private String uname;
@Schema(description = "登录描述")
private String description;
@Schema(description = "登录时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date loginTime;
@Schema(description = "浏览器请求头")
private String ua;
@Schema(description = "浏览器名称")
private String browser;
@Schema(description = "浏览器版本")
private String browserVersion;
@Schema(description = "操作系统")
private String operatingSystem;
@Schema(description = "平台")
private String platForm;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "创建者")
private String creater;
}

View File

@ -0,0 +1,62 @@
package com.evotech.hd.common.core.entity.resource;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-11-01
*/
@Getter
@Setter
@TableName("hd_resource.yt_log_upload")
@Schema(name = "上传记录表")
public class LogUpload implements Serializable {
private static final long serialVersionUID = 1L;
@TableId("pk_id")
private String pkId;
@Schema(description = "公司编码")
private String ccode;
@Schema(description = "运营商编码")
private String pocode;
@Schema(description = "自定义类型,会新建此文件夹,默认值 hd")
private String type;
@Schema(description = "本次上传内容,不填取第一个文件名字")
private String name;
@Schema(description = "数量")
private Integer amount;
@Schema(description = "状态1-成功")
private Integer status;
@Schema(description = "用户")
private String user;
@Schema(description = "文件路径")
private String filePath;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "创建者")
private String creater;
}

View File

@ -0,0 +1,96 @@
package com.evotech.hd.common.core.entity.resource;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-10-15
*/
@Getter
@Setter
@TableName("yt_t_proxy_operater")
@Schema(name = "代理运营商信息")
public class ProxyOperater implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "区划名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "区划名称不能为空")
private String division;
@Schema(description = "区划编码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "区划编码不能为空")
private String divisionNo;
@Schema(description = "运营商名称", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "运营商名称不能为空")
private String poname;
@Schema(description = "运营商组织机构代码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "运营商组织机构代码不能为空")
private String pocode;
@Schema(description = "地址")
private String address;
@Schema(description = "地址-省")
private String addressProvince;
@Schema(description = "地址-市")
private String addressCity;
@Schema(description = "地址-区县")
private String addressArea;
@Schema(description = "联系人")
private String contacts;
@Schema(description = "联系电话")
private String phone;
@Schema(description = "状态1-启用0-禁用")
private Integer status;
@Schema(description = "只读1-是0-否")
private Integer readonly;
@Schema(description = "删除标识1-已删除0-未删除", hidden = true)
private Integer delFlag;
@Schema(description = "换电站数量")
private Integer stationCount;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,72 @@
package com.evotech.hd.common.core.entity.resource;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-11-01
*/
@Getter
@Setter
@TableName("hd_resource.yt_t_upload_file")
@Schema(name = "上传文件表")
public class UploadFile implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "公司编码")
private String ccode;
@Schema(description = "运营商编码")
private String pocode;
@Schema(description = "数据类型")
private String type;
@Schema(description = "上传ID")
private String uploadId;
@Schema(description = "文件原名")
private String fileOriginalName;
@Schema(description = "文件类型")
private String fileType;
@Schema(description = "文件大小")
private Long fileSize;
@Schema(description = "文件最终名字")
private String fileFinalName;
@Schema(description = "文件路径")
private String filePath;
@Schema(description = "路径+名称")
private String pathName;
@Schema(description = "地址")
private String fileUrl;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "创建者")
private String creater;
}

View File

@ -0,0 +1,81 @@
package com.evotech.hd.common.core.entity.resource.auth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-04
*/
@Getter
@Setter
@TableName("yt_auth_menu")
@Schema(name = "菜单")
public class AuthMenu implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "菜单名称")
private String name;
@Schema(description = "功能描述")
private String mark;
@Schema(description = "对应路由组件component")
private String component;
@Schema(description = "对应路由path")
private String path;
@Schema(description = "状态")
private Integer status;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "菜单图标")
private String icon;
@Schema(description = "菜单类型:系统-1目录-2页面-3")
private String type;
@Schema(description = "父级菜单id")
private Integer parentId;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
@Schema(description = "子目录", hidden = true)
@TableField(exist = false)
private List<AuthMenu> subMenuList;
}

View File

@ -0,0 +1,69 @@
package com.evotech.hd.common.core.entity.resource.auth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-04
*/
@Getter
@Setter
@TableName("yt_auth_permission")
@Schema(name = "资源权限")
public class AuthPermission implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "资源编码:页面:操作")
private String code;
@Schema(description = "接口名称")
private String name;
@Schema(description = "菜单ID")
private Integer menuId;
@Schema(description = "允许访问路径gateway-允许网关访问private-不允许网关访问")
private String allow;
@Schema(description = "uri路径")
private String uri;
@Schema(description = "请求方式GET/POST/DEL/PUSH")
private String requestType;
@Schema(description = "接口描述")
private String mark;
@Schema(description = "创建人id")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,66 @@
package com.evotech.hd.common.core.entity.resource.auth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-04
*/
@Getter
@Setter
@TableName("yt_auth_role")
@Schema(name = "角色")
public class AuthRole implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "id", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "角色名称")
private String name;
@Schema(description = "角色编码")
private String rcode;
@Schema(description = "功能描述")
private String mark;
@Schema(description = "状态")
private Integer status;
@Schema(description = "角色类型1-开发者2-运营方3-客户")
private Integer type;
@Schema(description = "是否内置角色,内置角色不允许修改")
private Integer readonly;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,49 @@
package com.evotech.hd.common.core.entity.resource.auth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-04
*/
@Getter
@Setter
@TableName("yt_auth_role_resource")
@Schema(name = "角色的权限")
public class AuthRoleResource implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键")
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "角色编码")
private String rcode;
@Schema(description = "权限类型:SYS-系统MENU-菜单PER-资源权限")
private String resourceType;
@Schema(description = "权限id")
private Integer resourceId;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "创建人", hidden = true)
private String creater;
}

View File

@ -0,0 +1,114 @@
package com.evotech.hd.common.core.entity.resource.auth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author zrb
* @since 2024-09-04
*/
@Data
@TableName("yt_auth_user")
@Schema(name = "账号")
public class AuthUser implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "用户ID")
private String uid;
@Schema(description = "账号", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "账号不能为空")
private String uname;
@Schema(description = "账号类型1-开发者2-运营方3-客户", requiredMode = RequiredMode.REQUIRED)
private Integer type;
@Schema(description = "关联方代码")
private String typeRelateCode;
@Schema(description = "姓名")
private String name;
@Schema(description = "密码", requiredMode = RequiredMode.REQUIRED)
@NotBlank(message = "密码不能为空")
private String password;
@Schema(description = "邮箱")
private String email;
@Schema(description = "手机")
private String phone;
@Schema(description = "性别")
private String sex;
@Schema(description = "状态1-启用0-禁用", requiredMode = RequiredMode.REQUIRED)
@NotNull
private Integer status;
@Schema(description = "是否内置账号,内置账号不允许动")
private Integer readonly;
@Schema(description = "删除标识1-已删除0-未删除", hidden = true)
private Integer delFlag;
@Schema(description = "头像")
private String avatar;
@Schema(description = "最后一次输错密码时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date passwordErrorLastTime;
@Schema(description = "密码错误次数", hidden = true)
private Integer passwordErrorNum;
@Schema(description = "密码过期时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date passwordExpireTime;
@Schema(description = "最后登录时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date lastLoginTime;
@Schema(description = "创建人", hidden = true)
private String creater;
@Schema(description = "创建时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新人", hidden = true)
private String updater;
@Schema(description = "更新时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
@Schema(description = "角色编码", hidden = true)
@TableField(exist = false)
private String rcodes;
}

View File

@ -0,0 +1,53 @@
package com.evotech.hd.common.core.entity.resource.auth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-04
*/
@Getter
@Setter
@TableName("yt_auth_user_role")
@Schema(name = "账号角色关系")
public class AuthUserRole implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "用户ID")
@NotBlank
private String uid;
@Schema(description = "角色编码")
@NotBlank
private String rcode;
@Schema(description = "创建人")
private String creater;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "修改时间", hidden = true)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,58 @@
package com.evotech.hd.common.core.entity.resource.dict;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-05
*/
@Getter
@Setter
@TableName("yt_d_admdvs_info")
@Schema(name = "行政区划信息表")
public class AdmdvsInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId("pk_id")
private Integer pkId;
@Schema(description = "区划代码")
private String admdvsNo;
@Schema(description = "区划名称")
private String admdvsName;
@Schema(description = "父级代码")
private String pno;
@Schema(description = "等级1-省2-市3-区县")
private Integer level;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
@Schema(description = "子区划", hidden = true)
@TableField(exist = false)
private List<AdmdvsInfo> subAdmdvsInfoList;
}

View File

@ -0,0 +1,59 @@
package com.evotech.hd.common.core.entity.resource.dict;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-05
*/
@Getter
@Setter
@TableName("hd_resource.yt_d_dict")
@Schema(name = "字典")
public class Dict implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "字典名称")
private String dictName;
@Schema(description = "字典编码")
private String dictCode;
@Schema(description = "字典值")
private String dictValue;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "类型ID")
private Integer typeId;
private String typeCode;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,51 @@
package com.evotech.hd.common.core.entity.resource.dict;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author zrb
* @since 2024-09-05
*/
@Getter
@Setter
@TableName("hd_resource.yt_d_dict_type")
@Schema(name = "字典类型")
public class DictType implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键", hidden = true)
@TableId(value = "pk_id", type = IdType.AUTO)
private Integer pkId;
@Schema(description = "类型名称")
private String typeName;
@Schema(description = "类型编码")
private String typeCode;
@Schema(description = "描述")
private String mark;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date ctime;
@Schema(description = "更新时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date uptime;
}

View File

@ -0,0 +1,100 @@
package com.evotech.hd.common.core.enums;
/**
* 100 Continue 继续客户端应继续其请求
101 Switching Protocols 切换协议服务器根据客户端的请求切换协议只能切换到更高级的协议例如切换到HTTP的新版本协议
200 OK 请求成功一般用于GET与POST请求
201 Created 已创建成功请求并创建了新的资源
202 Accepted 已接受已经接受请求但未处理完成
203 Non-Authoritative Information 非授权信息请求成功但返回的meta信息不在原始的服务器而是一个副本
204 No Content 无内容服务器成功处理但未返回内容在未更新网页的情况下可确保浏览器继续显示当前文档
205 Reset Content 重置内容服务器处理成功用户终端例如浏览器应重置文档视图可通过此返回码清除浏览器的表单域
206 Partial Content 部分内容服务器成功处理了部分GET请求
300 Multiple Choices 多种选择请求的资源可包括多个位置相应可返回一个资源特征与地址的列表用于用户终端例如浏览器选择
301 Moved Permanently 永久移动请求的资源已被永久的移动到新URI返回信息会包括新的URI浏览器会自动定向到新URI今后任何新的请求都应使用新的URI代替
302 Found 临时移动与301类似但资源只是临时被移动客户端应继续使用原有URI
303 See Other 查看其它地址与301类似使用GET和POST请求查看
304 Not Modified 未修改所请求的资源未修改服务器返回此状态码时不会返回任何资源客户端通常会缓存访问过的资源通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 Use Proxy 使用代理所请求的资源必须通过代理访问
306 Unused 已经被废弃的HTTP状态码
307 Temporary Redirect 临时重定向与302类似使用GET请求重定向
400 Bad Request 客户端请求的语法错误服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留将来使用
403 Forbidden 服务器理解请求客户端的请求但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源网页通过此代码网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
407 Proxy Authentication Required 请求要求代理的身份认证与401类似但请求者应当使用代理进行授权
408 Request Time-out 服务器等待客户端发送的请求时间过长超时
409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码服务器处理请求时发生了冲突
410 Gone 客户端请求的资源已经不存在410不同于404如果资源以前有现在被永久删除了可使用410代码网站设计人员可通过301代码指定资源的新位置
411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息
412 Precondition Failed 客户端请求信息的先决条件错误
413 Request Entity Too Large 由于请求的实体过大服务器无法处理因此拒绝请求为防止客户端的连续请求服务器可能会关闭连接如果只是服务器暂时无法处理则会包含一个Retry-After的响应信息
414 Request-URI Too Large 请求的URI过长URI通常为网址服务器无法处理
415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
416 Requested range not satisfiable 客户端请求的范围无效
417 Expectation Failed 服务器无法满足Expect的请求头信息
500 Internal Server Error 服务器内部错误无法完成请求
501 Not Implemented 服务器不支持请求的功能无法完成请求
502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时从远程服务器接收到了一个无效的响应
503 Service Unavailable 由于超载或系统维护服务器暂时的无法处理客户端的请求延时的长度可包含在服务器的Retry-After头信息中
504 Gateway Time-out 充当网关或代理的服务器未及时从远端服务器获取请求
505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本无法完成处理
*/
public enum CodeMsg {
// 统一回复
SUCCESS("1000", "OK"),
ERROR("4000", "ERROR"),
// 系统错误 S
USERNAME_OR_PASSWORD_ERROR("S0400", "用户名或密码错误!"),
PASSWORD_ENTER_EXCEED_LIMIT("S0401", "用户输入密码次数超限!"),
USER_NOT_LOGIN("S0402", "用户未登陆!"),
AUTHENTICATION_FAILED("S0403", "认证失败!"),
TOKEN_INVALID("S0404", "无效token"),
TOKEN_EXPIRED("S0405", "token已过期"),
ACCESS_DENY("S0406", "访问未授权!"),
ACCESS_SCOPE_ERROR("S0407", "客户端权限范围出错"),
GATEWAY_EXECUTION_TIMEOUT("S0408", "网关执行超时"),
GATEWAY_EXECUTION_ERROR("S0409", "网关执行出错"),
// 业务错误 B
BUSSINESS_ERROR("B0400", "业务出错"),
PARAM_IS_NULL("B0401", "请求参数为空"),
PARAM_ERROR("B0402", "用户请求参数错误"),
UPLOAD_FILE_ERROR("B0403", "用户上传文件异常"),
UPLOAD_FILE_TYPE_NOT_MATCH("B0404", "用户上传文件类型不匹配"),
UPLOAD_FILE_SIZE_EXCEEDS("B0405", "用户上传文件太大"),
// 数据库错误 D
DATABASE_ERROR("D0400", "数据库服务出错"),
DATA_EXIST("D0401", "此数据已存在!"),
DATABASE_RESULT_NULL("D0402", "查询结果为空"),
DATABASE_TABLE_NOT_EXIST("D0403", "表不存在"),
DATABASE_COLUMN_NOT_EXIST("D0404", "列不存在"),
SQL_RUN_ERROR("D0405", "sql运行异常"),
REDIS_ERROR("D0410", "REDIS数据出错"),
// 小程序 w
WECHAT_LOGIN_ERROR("W0400", "小程序登录出错"),
WECHAT_SERRION_ERROR("W0401", "小程序登录态错误"),
WECHAT_API_ERROR("W0402", "小程序接口调用异常");
String code;
String msg;
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
CodeMsg(String code, String msg) {
this.code = code;
this.msg = msg;
}
}

View File

@ -0,0 +1,22 @@
package com.evotech.hd.common.core.utils;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
/**
* @author zrb
* @date 2024年9月5日10:25:58
*/
public class SnowflakeUtil {
public static final Snowflake sf = IdUtil.getSnowflake(1, 1);
public static Long getId() {
return sf.nextId();
}
public static String getIdStr() {
return sf.nextIdStr();
}
}

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.evotech.hd</groupId>
<artifactId>base-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>common-mybatis</artifactId>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
</dependencies>
<!-- 原来有代码生成器,现在移出去了,这个打包配置先不用了 -->
<!--<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</exclude>
<exclude></exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>templates/**</exclude>
</excludes>
</resource>
</resources>
</build>-->
</project>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.evotech.hd</groupId>
<artifactId>base-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>common-redis</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,71 @@
package com.evotech.hd.common.redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author zrb
* @date 2024年9月3日17:13:42
*/
@Configuration
public class RedisConfig {
/**
* 其实SpringBoot自动帮我们在容器中生成了一个RedisTemplate和一个StringRedisTemplate
* 但是这个RedisTemplate的泛型是<Object,Object>写代码不方便需要写好多类型转换的代码我们需要一个泛型为<String,Object>形式的RedisTemplate
* 同时设置key-value的序列化方式
*/
@Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
/*
* 设置序列化参数
*/
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域field,get和set,以及修饰符范围ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型类必须是非final修饰的final修饰的类比如String,Integer等会跑出异常
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(om, Object.class);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value序列化方式
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
// RedisTemplate的初始化我们上面没有设置到的参数会在这个方法中设置成默认的若没有这个缺少设置有时会报错
template.afterPropertiesSet();
return template;
}
/***
* 添加了这个类SpringBoot不会帮我们自动生成StringRedisTemplate为了方便程序中使用我们自己生成一个放到容器中
* stringRedisTemplate默认采用的是String的序列化策略
* @param redisConnectionFactory
* @return
*/
@Bean
StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
return stringRedisTemplate;
}
}

View File

@ -0,0 +1,663 @@
package com.evotech.hd.common.redis.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @author zrb
* @date 2024年9月3日17:15:36
*/
@Component
public class RedisUtil {
/**
* 注入redisTemplate bean
*/
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定缓存失效时间
*
* @param key
* @param time 时间()
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key获取过期时间
*
* @param key 不能为null
* @return 时间() 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((List<String>) CollectionUtils.arrayToList(key));
}
}
}
// ============================String(字符串)=============================
/**
* 普通缓存获取
*
* @param key
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key
* @param value
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key
* @param value
* @param time 时间() time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Hash(哈希)=================================
/**
* HashGet
*
* @param key 不能为null
* @param item 不能为null
* @return
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key
* @param map 对应多个键值
* @param time 时间()
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key
* @param item
* @param value
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key
* @param item
* @param value
* @param time 时间() 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 不能为null
* @param item 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 不能为null
* @param item 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key
* @param item
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key
* @param item
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================Set(集合)=============================
/**
* 根据key获取Set中的所有值
*
* @param key
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key
* @param value
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key
* @param values 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key
* @param time 时间()
* @param values 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key
* @param values 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================List(列表)=================================
/**
* 获取list缓存的内容
*
* @param key
* @param start 开始
* @param end 结束 0 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key
* @param index 索引 index>=0时 0 表头1 第二个元素依次类推index<0时-1表尾-2倒数第二个元素依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key
* @param value
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key
* @param value
* @param time 时间()
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* 要放数组放list就是个buglist先转数组
* @param key
* @param value
* @return
*/
public boolean lSet(String key, Object[] value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* 要放数组放list就是个buglist先转数组
* @param key
* @param value
* @param time 时间()
* @return
*/
public boolean lSet(String key, Object[] value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移出并获取列表的第一个元素
*
* @param key
* @return 删除的元素
*/
public Object lLeftPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
/**
* 移出并获取列表的第一个元素 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
*
* @param key
* @param timeout
* 等待时间
* @param unit
* 时间单位
* @return
*/
public Object lBLeftPop(String key, long timeout, TimeUnit unit) {
return redisTemplate.opsForList().leftPop(key, timeout, unit);
}
/**
* 移除并获取列表最后一个元素
*
* @param key
* @return 删除的元素
*/
public Object lRightPop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
/**
* 移出并获取列表的最后一个元素 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
*
* @param key
* @param timeout
* 等待时间
* @param unit
* 时间单位
* @return
*/
public Object lBRightPop(String key, long timeout, TimeUnit unit) {
return redisTemplate.opsForList().rightPop(key, timeout, unit);
}
/**
* 移除列表的最后一个元素并将该元素添加到另一个列表并返回
*
* @param sourceKey
* @param destinationKey
* @return
*/
public Object lRightPopAndLeftPush(String sourceKey, String destinationKey) {
return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
destinationKey);
}
/**
* 从列表中弹出一个值将弹出的元素插入到另外一个列表中并返回它 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
*
* @param sourceKey
* @param destinationKey
* @param timeout
* @param unit
* @return
*/
public Object lBRightPopAndLeftPush(String sourceKey, String destinationKey,
long timeout, TimeUnit unit) {
return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
destinationKey, timeout, unit);
}
/**
* 根据索引修改list中的某条数据
*
* @param key
* @param index 索引
* @param value
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key
* @param count 移除多少个
* @param value
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 根据正则表达式获取key列表
*
* @param patternKey 正则表达式
* @return 匹配key列表
*/
public Set<String> keys(String patternKey) {
try {
Set<String> keys = redisTemplate.keys(patternKey);
return keys;
} catch (Exception e) {
e.printStackTrace();
return new HashSet<>();
}
}
}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.evotech.hd</groupId>
<artifactId>base-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>common-web</artifactId>
<dependencies>
<dependency>
<groupId>com.evotech.hd</groupId>
<artifactId>common-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</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>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,84 @@
package com.evotech.hd.common.web.exception;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.evotech.hd.common.core.entity.Result;
import com.evotech.hd.common.core.enums.CodeMsg;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
/**
* 全局异常处理
* @author zrb
*/
/**
* @RestControllerAdvice注解和controller相对应
* 若用controller则此处用ControllerAdvice
* 若用RestController此处用@RestControllerAdvice
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理空指针的异常
* @param req
* @param e
*/
@ExceptionHandler(value = NullPointerException.class)
public Result<String> exceptionHandler(HttpServletRequest req, NullPointerException e) {
log.error("空指针异常:", e);
return new Result<String>().bussinessException(CodeMsg.ERROR.getCode(), e.getMessage(), null);
}
/**
* Http请求消息异常
*/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public Result<String> exceptionHandler(HttpServletRequest req, HttpMessageNotReadableException e) {
log.error("http请求参数解析异常", e);
return new Result<String>().bussinessException(CodeMsg.PARAM_ERROR.getCode(), "http请求参数解析异常", e.getMessage());
}
/**
* 参数校验异常
* @param req
* @param e
*/
@ExceptionHandler(value = MyArgumentException.class)
public Result<String> exceptionHandler(HttpServletRequest req, MyArgumentException e) {
log.error("请求参数异常:", e);
return new Result<String>().bussinessException(CodeMsg.PARAM_ERROR.getCode(), CodeMsg.PARAM_ERROR.getMsg(), e.getParamArr());
}
/**
* 这个异常的意思就是在更新update或insert数据库时新的数据违反了完整性
* 例如主键重复我这里的问题是数据库的id字段未设置自增默认值也没设在插入的时候就出现了这个异常
* @param req
* @param e
*/
@ExceptionHandler(value = DataIntegrityViolationException.class)
public Result<String> exceptionHandler(HttpServletRequest req, DataIntegrityViolationException e) {
log.error("sql语句异常", e);
return new Result<String>().bussinessException(CodeMsg.SQL_RUN_ERROR.getCode(), e.getCause().toString(), null);
}
/**
* 自己抛出的运行异常
* @param req
* @param e
*/
@ExceptionHandler(value = Exception.class)
public Result<String> exceptionHandler(HttpServletRequest req, RuntimeException e) {
log.error("捕获运行异常:", e);
return new Result<String>().bussinessException(CodeMsg.ERROR.getCode(), e instanceof RuntimeException?e.getMessage():e.toString(), null);
}
}

View File

@ -0,0 +1,41 @@
package com.evotech.hd.common.web.exception;
public class MyArgumentException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 请求的参数数据若参数校验不通过此字段存储请求参数返回前端
*/
private String[] paramArr;
public String[] getParamArr() {
return paramArr;
}
public void setParamArr(String[] paramArr) {
this.paramArr = paramArr;
}
public MyArgumentException() {
super();
}
public MyArgumentException(String message) {
super(message);
}
public MyArgumentException(String message, String params) {
super(message);
this.paramArr = params.split(",");
}
public MyArgumentException(String message, Throwable cause) {
super(message, cause);
}
public MyArgumentException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,29 @@
package com.evotech.hd.common.web.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.servlet.Filter;
@Configuration
public class MyFilterConfig {
@Autowired
private RequestBodyFilter requestBodyFilter;
/**
* 将过滤器注册到FilterRegistrationBean中
* 进行简单配置
*/
@Bean
FilterRegistrationBean<Filter> myFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(requestBodyFilter);
registration.addUrlPatterns("/*");
registration.setName("requestBodyFilter");
return registration;
}
}

View File

@ -0,0 +1,106 @@
package com.evotech.hd.common.web.filter;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
/**
* 自定义request对象将body处理后放到这个对象里面
* 一定要重写getReader()和getInputStream()方法将body放进去
* 这样后面的controller才能从这个request的getReader()和getInputStream()
* 方法中拿到新的body对象
* @author zrb
*/
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
public MyHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
final StringBuilder builder = new StringBuilder();
// 缓存按 8192 大小
final CharBuffer buffer = CharBuffer.allocate(2 << 12);
BufferedReader reader = null;
try {
reader = request.getReader();
while (-1 != reader.read(buffer)) {
builder.append(buffer.flip());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
String bodyStr = builder.toString();
try {
bodyStr = URLDecoder.decode(bodyStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException("body解码出错");
}
body = bodyStr.getBytes(Charset.defaultCharset());
}
public byte[] getBody() {
return body;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return bais.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
}

Some files were not shown because too many files have changed in this diff Show More