初始化项目
This commit is contained in:
220
pom.xml
Normal file
220
pom.xml
Normal file
@@ -0,0 +1,220 @@
|
||||
<?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>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.5</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>net.geedge</groupId>
|
||||
<artifactId>asw-controller</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>asw-controller</name>
|
||||
<description>AppSketch Works Controller</description>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-spring-boot-starter</artifactId>
|
||||
<version>2.1.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-script</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-mockmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>1.37.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.sdk</groupId>
|
||||
<artifactId>graal-sdk</artifactId>
|
||||
<version>22.3.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-api-plugin-task</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>xerces</groupId>
|
||||
<artifactId>xercesImpl</artifactId>
|
||||
<version>2.12.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jasypt加密 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ssssssss</groupId>
|
||||
<artifactId>magic-script</artifactId>
|
||||
<version>1.8.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.j256.simplemagic</groupId>
|
||||
<artifactId>simplemagic</artifactId>
|
||||
<version>1.16</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<excludes>
|
||||
<exclude>**/logback-spring.xml</exclude>
|
||||
<exclude>**/asw.properties</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<!--包含文件夹以及子文件夹下所有资源 -->
|
||||
<include>**/*.*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.graalvm.buildtools</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>public</id>
|
||||
<name>aliyun nexus</name>
|
||||
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<!-- 配置私服地址 -->
|
||||
<repository>
|
||||
<id>nexus</id>
|
||||
<name>Team Nexus Repository</name>
|
||||
<url>http://192.168.40.153:8099/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>nexus-releases</id>
|
||||
<name>Team Nexus Repository</name>
|
||||
<url>http://192.168.40.153:8099/content/repositories/releases/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>nexus-geedge</id>
|
||||
<name>Team Nexus Repository</name>
|
||||
<url>http://192.168.40.153:8099/content/repositories/geedge/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>public</id>
|
||||
<name>aliyun nexus</name>
|
||||
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
</project>
|
||||
24
src/main/java/net/geedge/asw/AppSketchWokrsApplication.java
Normal file
24
src/main/java/net/geedge/asw/AppSketchWokrsApplication.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.geedge.asw;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import net.geedge.asw.common.util.Constants;
|
||||
import net.geedge.asw.common.util.T;
|
||||
|
||||
@SpringBootApplication(proxyBeanMethods = false)
|
||||
@EnableTransactionManagement
|
||||
@PropertySource(value = {"file:./config/asw.properties", "classpath:./config/asw.properties"}, encoding = "utf-8", ignoreResourceNotFound = true)
|
||||
public class AppSketchWokrsApplication {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
System.setProperty("jasypt.encryptor.password", T.HexUtil.encodeHexStr(Constants.AES_KEY));
|
||||
SpringApplication.run(AppSketchWokrsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
104
src/main/java/net/geedge/asw/common/config/MagicApiConfig.java
Normal file
104
src/main/java/net/geedge/asw/common/config/MagicApiConfig.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package net.geedge.asw.common.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.config.CronTask;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.ssssssss.magicapi.core.config.MagicConfiguration;
|
||||
import org.ssssssss.magicapi.core.service.MagicResourceStorage;
|
||||
import org.ssssssss.magicapi.modules.http.HttpModule;
|
||||
import org.ssssssss.magicapi.task.model.TaskInfo;
|
||||
import org.ssssssss.magicapi.task.service.TaskInfoMagicResourceStorage;
|
||||
import org.ssssssss.magicapi.task.service.TaskMagicDynamicRegistry;
|
||||
import org.ssssssss.magicapi.task.starter.MagicTaskConfig;
|
||||
import org.ssssssss.magicapi.utils.ScriptManager;
|
||||
import org.ssssssss.script.MagicScriptContext;
|
||||
|
||||
@Configuration
|
||||
public class MagicApiConfig {
|
||||
@Autowired
|
||||
private MagicTaskConfig config;
|
||||
|
||||
@Bean
|
||||
public HttpModule magicHttpModule(RestTemplate restTemplate) {
|
||||
return new HttpModule(restTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskMagicDynamicRegistry taskMagicDynamicRegistry(
|
||||
TaskInfoMagicResourceStorage taskInfoMagicResourceStorage) {
|
||||
MagicTaskConfig.Shutdown shutdown = config.getShutdown();
|
||||
ThreadPoolTaskScheduler poolTaskScheduler = null;
|
||||
if (config.isEnable()) {
|
||||
poolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
poolTaskScheduler.setPoolSize(config.getPool().getSize());
|
||||
poolTaskScheduler.setWaitForTasksToCompleteOnShutdown(shutdown.isAwaitTermination());
|
||||
if (shutdown.getAwaitTerminationPeriod() != null) {
|
||||
poolTaskScheduler.setAwaitTerminationSeconds((int) shutdown.getAwaitTerminationPeriod().getSeconds());
|
||||
}
|
||||
poolTaskScheduler.setThreadNamePrefix(config.getThreadNamePrefix());
|
||||
poolTaskScheduler.initialize();
|
||||
}
|
||||
return new CustomTaskMagicDynamicRegistry(taskInfoMagicResourceStorage, poolTaskScheduler, config.isLog());
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义任务动态注册,context注入taskinfo对象
|
||||
*/
|
||||
private class CustomTaskMagicDynamicRegistry extends TaskMagicDynamicRegistry {
|
||||
private final TaskScheduler taskScheduler;
|
||||
private static final Logger logger = LoggerFactory.getLogger(CustomTaskMagicDynamicRegistry.class);
|
||||
private final boolean showLog;
|
||||
|
||||
public CustomTaskMagicDynamicRegistry(MagicResourceStorage<TaskInfo> magicResourceStorage,
|
||||
TaskScheduler taskScheduler, boolean showLog) {
|
||||
super(magicResourceStorage, taskScheduler, showLog);
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.showLog = showLog;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean register(MappingNode<TaskInfo> mappingNode) {
|
||||
TaskInfo entity = mappingNode.getEntity();
|
||||
if (taskScheduler != null) {
|
||||
String scriptName = MagicConfiguration.getMagicResourceService().getScriptName(entity);
|
||||
try {
|
||||
CronTrigger trigger = new CronTrigger(entity.getCron());
|
||||
CronTask cronTask = new CronTask(() -> {
|
||||
if (entity.isEnabled()) {
|
||||
try {
|
||||
if (showLog) {
|
||||
logger.info("定时任务:[{}]开始执行", scriptName);
|
||||
}
|
||||
MagicScriptContext magicScriptContext = new MagicScriptContext();
|
||||
magicScriptContext.setScriptName(scriptName);
|
||||
magicScriptContext.set("taskinfo", entity);
|
||||
ScriptManager.executeScript(entity.getScript(), magicScriptContext);
|
||||
} catch (Exception e) {
|
||||
logger.error("scriptName:{}", scriptName);
|
||||
logger.error("定时任务执行出错", e);
|
||||
} finally {
|
||||
if (showLog) {
|
||||
logger.info("定时任务:[{}]执行完毕", scriptName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, trigger);
|
||||
mappingNode.setMappingData(taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()));
|
||||
} catch (Exception e) {
|
||||
logger.error("定时任务:[{}]注册失败", scriptName, e);
|
||||
}
|
||||
logger.debug("注册定时任务:[{},{}]", MagicConfiguration.getMagicResourceService().getScriptName(entity),
|
||||
entity.getCron());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.geedge.asw.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 添加分页插件
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));//如果配置多个插件,切记分页最后添加
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package net.geedge.asw.common.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.hc.client5.http.classic.HttpClient;
|
||||
import org.apache.hc.client5.http.config.RequestConfig;
|
||||
import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
|
||||
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
|
||||
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
|
||||
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.hc.core5.http.config.Registry;
|
||||
import org.apache.hc.core5.http.config.RegistryBuilder;
|
||||
import org.apache.hc.core5.ssl.SSLContexts;
|
||||
import org.apache.hc.core5.ssl.TrustStrategy;
|
||||
import org.apache.hc.core5.util.Timeout;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Http resttemplate 配置
|
||||
**/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
/**
|
||||
* # 从连接池获取连接的timeout,不宜过大,ms
|
||||
*/
|
||||
@Value("${http-pool.connection-request-timeout:200}")
|
||||
private int connectionRequestTimeout;
|
||||
/**
|
||||
* 指客户端和服务器建立连接的超时时间,ms , 最大约21秒,因为内部tcp在进行三次握手建立连接时,默认tcp超时时间是20秒
|
||||
*/
|
||||
@Value("${http-pool.connection-timeout:1000}")
|
||||
private int connectionTimeout;
|
||||
/**
|
||||
* 指客户端从服务器读取数据包的间隔超时时间,不是总读取时间,也就是socket timeout,ms
|
||||
*/
|
||||
@Value("${http-pool.socket-timeout:180000}")
|
||||
private int socketTimeout;
|
||||
/**
|
||||
* #每个路由的最大连接数,如果只调用一个地址,可以将其设置为最大连接数
|
||||
*/
|
||||
@Value("${http-pool.max-per-route:10000}")
|
||||
private int maxPerRoute;
|
||||
/**
|
||||
* #连接池的最大连接数,0代表不限;如果取0,需要考虑连接泄露导致系统崩溃的后果
|
||||
*/
|
||||
@Value("${http-pool.max-total:1000}")
|
||||
private int maxTotal;
|
||||
|
||||
/**
|
||||
* 长连接保持时间 单位s,不宜过长
|
||||
*/
|
||||
@Value("${http-pool.keep-alive-time:60}")
|
||||
private int keepAliveTime;
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder, ClientHttpRequestFactory requestFactory) {
|
||||
RestTemplate restTemplate = builder.build();
|
||||
restTemplate.setRequestFactory(requestFactory);
|
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
|
||||
@Override
|
||||
public boolean hasError(ClientHttpResponse response) throws IOException {
|
||||
int rawStatusCode = response.getRawStatusCode();
|
||||
HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
|
||||
return (statusCode != null ? statusCode.isError() : hasError(rawStatusCode));
|
||||
}
|
||||
|
||||
protected boolean hasError(int unknownStatusCode) {
|
||||
HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);
|
||||
return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
}
|
||||
});
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientHttpRequestFactory httpRequestFactory(HttpClient client) {
|
||||
return new HttpComponentsClientHttpRequestFactory(client);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpClient httpClient() throws Exception {
|
||||
// 配置不校验server 证书
|
||||
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
|
||||
@Override
|
||||
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
|
||||
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
|
||||
new NoopHostnameVerifier());
|
||||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("http", PlainConnectionSocketFactory.getSocketFactory())
|
||||
.register("https", sslConnectionSocketFactory).build();
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
|
||||
// 设置整个连接池最大连接数 根据自己的场景决定
|
||||
connectionManager.setMaxTotal(maxTotal);
|
||||
// 路由是对maxTotal的细分
|
||||
connectionManager.setDefaultMaxPerRoute(maxPerRoute);
|
||||
RequestConfig requestConfig = RequestConfig.custom().setResponseTimeout(Timeout.ofMilliseconds(socketTimeout))// 服务器返回数据(response)的时间,超过该时间抛出readtimeout
|
||||
.setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout))// 连接上服务器(握手成功)的时间,超出该时间抛出connect timeout
|
||||
.setConnectionRequestTimeout(Timeout.ofMilliseconds(connectionRequestTimeout))// 从连接池中获取连接的超时时间,超过该时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException:Timeout
|
||||
// waiting for connection from pool
|
||||
.build();
|
||||
return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
|
||||
.setConnectionManager(connectionManager)
|
||||
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package net.geedge.asw.common.config;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* satoken timeout
|
||||
* key: satoken.timeout
|
||||
* 单位:分钟
|
||||
* 默认:30天
|
||||
*/
|
||||
@Value("${satoken.timeout:#{24 * 60 * 30}}")
|
||||
private Long timeout;
|
||||
|
||||
// Sa-Token 参数配置,参考文档:https://sa-token.cc
|
||||
// 此配置会与 application.yml 中的配置合并 (代码配置优先)
|
||||
@Autowired
|
||||
public void configSaToken(SaTokenConfig config) {
|
||||
config.setTokenName("satoken"); // token 名称(同时也是 cookie 名称)
|
||||
Long satokenTimeout = this.timeout == -1L ? -1L : TimeUnit.MINUTES.toSeconds(this.timeout);
|
||||
config.setTimeout(satokenTimeout); // token 有效期(单位:秒),默认30天,-1代表永不过期
|
||||
config.setActiveTimeout(-1); // token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
config.setIsConcurrent(true); // 是否允许同一账号多地同时登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录)
|
||||
config.setIsShare(true); // 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token,为 false 时每次登录新建一个 token)
|
||||
config.setTokenStyle("simple-uuid"); // token 风格
|
||||
config.setIsLog(false); // 是否输出操作日志
|
||||
config.setIsPrint(false);
|
||||
// config.setIsReadCookie(false);
|
||||
}
|
||||
|
||||
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
||||
registry.addInterceptor(new SaInterceptor(handler -> {
|
||||
SaRouter.match("/file/**").notMatch("/file/content/*").check(r -> StpUtil.checkLogin());
|
||||
SaRouter.match("/sys/**").notMatch("/sys/login").check(r -> StpUtil.checkLogin());
|
||||
})).addPathPatterns("/**");
|
||||
}
|
||||
}
|
||||
46
src/main/java/net/geedge/asw/common/config/WebConfig.java
Normal file
46
src/main/java/net/geedge/asw/common/config/WebConfig.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package net.geedge.asw.common.config;
|
||||
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* CORS 基于Filter的全局配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
|
||||
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
final CorsConfiguration config = new CorsConfiguration();
|
||||
// 允许cookies跨域
|
||||
config.setAllowCredentials(true);
|
||||
// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
|
||||
config.addAllowedOrigin("*");
|
||||
// #允许访问的头信息,*表示全部
|
||||
config.addAllowedHeader("*");
|
||||
// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
|
||||
config.setMaxAge(18000L);
|
||||
// 允许提交请求的方法,*表示全部允许
|
||||
config.addAllowedMethod("OPTIONS");
|
||||
config.addAllowedMethod("HEAD");
|
||||
config.addAllowedMethod("GET");
|
||||
config.addAllowedMethod("PUT");
|
||||
config.addAllowedMethod("POST");
|
||||
config.addAllowedMethod("DELETE");
|
||||
config.addAllowedMethod("PATCH");
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
|
||||
// 设置监听器的优先级
|
||||
bean.setOrder(0);
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package net.geedge.asw.common.config.exception;
|
||||
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.hutool.log.Log;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import net.geedge.asw.common.util.ASWException;
|
||||
import net.geedge.asw.common.util.R;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
|
||||
/**
|
||||
* 异常处理器
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class ASWExceptionHandler {
|
||||
|
||||
private static final Log log = Log.get();
|
||||
|
||||
/**
|
||||
* 处理自定义异常
|
||||
*/
|
||||
@ExceptionHandler(ASWException.class)
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
|
||||
public R handleDHException(ASWException e, HttpServletRequest request) {
|
||||
log.warn(e, "Request uri: {}", request.getRequestURI());
|
||||
return R.error(e.getCode(), e.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户未登录
|
||||
* @param e
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
@ResponseStatus(value = HttpStatus.FORBIDDEN)
|
||||
public R handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
log.warn(e, "Request uri: {}", request.getRequestURI());
|
||||
return R.error(RCode.USER_NO_LOGIN);
|
||||
}
|
||||
|
||||
@ExceptionHandler(DuplicateKeyException.class)
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
|
||||
public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {
|
||||
log.error(e, "Request uri: {}", request.getRequestURI());
|
||||
return R.error(RCode.SYS_DUPLICATE_RECORD);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public R handleException(Exception e, HttpServletRequest request) {
|
||||
if (e instanceof ClientAbortException) {
|
||||
return null;
|
||||
}
|
||||
log.error(e, "Request uri: {}", request.getRequestURI());
|
||||
return R.error().put("msg", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
103
src/main/java/net/geedge/asw/common/util/ASWException.java
Normal file
103
src/main/java/net/geedge/asw/common/util/ASWException.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class ASWException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String msg = RCode.ERROR.getMsg();
|
||||
private int code = RCode.ERROR.getCode();
|
||||
private Object[] param = new Object[] {};
|
||||
|
||||
public ASWException(RCode rCode) {
|
||||
super(rCode.getMsg());
|
||||
this.code = rCode.getCode();
|
||||
this.msg = rCode.getMsg();
|
||||
this.param = rCode.getParam();
|
||||
}
|
||||
|
||||
public ASWException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ASWException(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ASWException(String msg, int code) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public ASWException(String msg, int code, Object[] param, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public ASWException(String msg, int code, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public void throwException() {
|
||||
throw this;
|
||||
}
|
||||
|
||||
public static ASWExceptionBuilder builder() {
|
||||
return new ASWExceptionBuilder();
|
||||
}
|
||||
|
||||
public static class ASWExceptionBuilder {
|
||||
private int code = RCode.ERROR.getCode();
|
||||
private String msg = RCode.ERROR.getMsg();
|
||||
private Throwable e;
|
||||
private Object[] param;
|
||||
|
||||
private ASWExceptionBuilder() {
|
||||
}
|
||||
|
||||
public ASWExceptionBuilder rcode(RCode rcode) {
|
||||
if (T.ObjectUtil.isNotNull(rcode)) {
|
||||
this.code = rcode.getCode();
|
||||
this.msg = rcode.getMsg();
|
||||
this.param = rcode.getParam();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ASWExceptionBuilder code(int code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ASWExceptionBuilder msg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ASWExceptionBuilder param(Object[] param) {
|
||||
this.param = param;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ASWExceptionBuilder throwable(Throwable e) {
|
||||
this.e = e;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ASWException build() {
|
||||
return new ASWException(this.msg, this.code, this.param, this.e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
19
src/main/java/net/geedge/asw/common/util/Constants.java
Normal file
19
src/main/java/net/geedge/asw/common/util/Constants.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Constants {
|
||||
|
||||
/**
|
||||
* AES 加密密钥
|
||||
*/
|
||||
public static final byte[] AES_KEY = T.HexUtil.decodeHex("bde5430614b21baf1c53bd6f616d1a39");
|
||||
|
||||
/**
|
||||
* 临时目录
|
||||
*/
|
||||
public static final String TEMP_PATH = System.getProperty("user.dir") + File.separator + "tmp";
|
||||
|
||||
|
||||
|
||||
}
|
||||
51
src/main/java/net/geedge/asw/common/util/JasyptUtil.java
Normal file
51
src/main/java/net/geedge/asw/common/util/JasyptUtil.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
|
||||
import org.jasypt.iv.RandomIvGenerator;
|
||||
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
|
||||
/**
|
||||
* Jasypt 密钥加密工具类
|
||||
* 默认使用 版本默认加密算法 PBEWithHMACSHA512AndAES_256
|
||||
*
|
||||
* @Link https://github.com/ulisesbocchio/jasypt-spring-boot
|
||||
*/
|
||||
public class JasyptUtil {
|
||||
|
||||
/**
|
||||
* encrypt
|
||||
*
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
public static String encrypt(String content) {
|
||||
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
|
||||
// 加密算法
|
||||
encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256");
|
||||
encryptor.setIvGenerator(new RandomIvGenerator());
|
||||
String password = HexUtil.encodeHexStr(Constants.AES_KEY);
|
||||
encryptor.setPassword(password);
|
||||
String encrypt = encryptor.encrypt(content);
|
||||
return encrypt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* decrypt
|
||||
*
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
public static String decrypt(String content) {
|
||||
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
|
||||
// 加密算法
|
||||
encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256");
|
||||
encryptor.setIvGenerator(new RandomIvGenerator());
|
||||
String password = HexUtil.encodeHexStr(Constants.AES_KEY);
|
||||
encryptor.setPassword(password);
|
||||
String decrypt = encryptor.decrypt(content);
|
||||
return decrypt;
|
||||
}
|
||||
|
||||
}
|
||||
82
src/main/java/net/geedge/asw/common/util/R.java
Normal file
82
src/main/java/net/geedge/asw/common/util/R.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 返回数据
|
||||
*
|
||||
* 错误码、错误内容统一在枚举类RCode中定义, 错误码格式见RCode注释,错误码内容必须用英文,作为国际化的code 自定义的错误类型必须加注释
|
||||
*/
|
||||
public class R extends HashMap<String, Object> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public R() {
|
||||
put("code", RCode.SUCCESS.getCode());
|
||||
put("msg", RCode.SUCCESS.getMsg());
|
||||
put("timestamp", T.DateUtil.current());
|
||||
}
|
||||
|
||||
public static R error() {
|
||||
return error(RCode.ERROR.getCode(), RCode.ERROR.getMsg());
|
||||
}
|
||||
|
||||
public static R error(RCode rCode) {
|
||||
R r = new R();
|
||||
r.put("code", rCode.getCode());
|
||||
r.put("msg", rCode.getMsg());
|
||||
return r;
|
||||
}
|
||||
|
||||
public static R error(Integer code, String msg) {
|
||||
R r = new R();
|
||||
r.put("code", code);
|
||||
r.put("msg", msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static R ok(String msg) {
|
||||
R r = new R();
|
||||
r.put("msg", msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static R ok() {
|
||||
return new R();
|
||||
}
|
||||
|
||||
public static R ok(Object data) {
|
||||
R r = new R();
|
||||
r.put("data", data);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R put(String key, Object value) {
|
||||
super.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public R putData(Object value) {
|
||||
this.put("data", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public R putData(String key, Object value) {
|
||||
Object data = super.getOrDefault("data", new LinkedHashMap<String, Object>());
|
||||
if (!(data instanceof Map)) {
|
||||
throw new ASWException("data put error");
|
||||
}
|
||||
((Map<String, Object>) data).put(key, value);
|
||||
super.put("data", data);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public R putAllData(Map m) {
|
||||
super.putAll(m);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
71
src/main/java/net/geedge/asw/common/util/RCode.java
Normal file
71
src/main/java/net/geedge/asw/common/util/RCode.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
public enum RCode {
|
||||
|
||||
/**
|
||||
* 10**** : 系统认证 或 通用错误提示 20**** : screen module
|
||||
*/
|
||||
|
||||
ERROR(999, "error"), // 通用错误/未知错误
|
||||
|
||||
SYS_USER_PWD_ERROR(100001, "username or password error"), // 用户名 或密码错误
|
||||
SYS_DUPLICATE_RECORD(100002, "duplicate record"), // 数据库中已存在该记录
|
||||
SYS_NO_AUTH(100003, "Permission denied"), // 没有权限
|
||||
ID_CANNOT_EMPTY(100004, "id cannot be empty"), // ID 不能为空
|
||||
NAME_CANNOT_EMPTY(100005, "name cannot be empty"), // name 不能为空
|
||||
PARAM_CANNOT_EMPTY(100006, "parameter cannot be empty"), // parameter 不能为空
|
||||
USER_NO_LOGIN(100007, "user not login"), // 用户未登录
|
||||
SYS_RECORD_NOT_FOUND(100008, "record not found"),// 未找到记录
|
||||
|
||||
|
||||
SCREEN_ID_CANNOT_EMPTY(200001, "id cannot be empty"),
|
||||
|
||||
SCREE_DATASOURCE_DEFAULT_CANNOT_BE_DELETE(300001,"The default data source cannot be deleted."),
|
||||
SCREE_DATASOURCE_REPEAT(300002,"Screen datasource name duplicate"),
|
||||
|
||||
/**
|
||||
* import
|
||||
*/
|
||||
EXCELFILE_TYPE_ERROR(400001, "The type can only be xlsx, json, csv"),
|
||||
EXCELFILE_PARSE_ERROR(400002, "Import file resolution failed"),
|
||||
EXCELFILE_HEADER_TEMPLATE_ERROR(400003,"The header row of the import template is inconsistent with the system template"),
|
||||
EXCELFILE_HEADER_LANGUAGE_ERROR(400004, "Language must be en, zh or ru"),
|
||||
EXCELFILE_IMPORT_FILE_ISNULL(400005, "Import file is null"),
|
||||
EXCELFILE_HEADER_LANGUAGE_ISNULL(400006, "Language can not be empty"),
|
||||
EXCELFILE_IMPORT_ERROR(400007, "File import error"),
|
||||
EXCELFILE_SCHEDULE_TASK_IS_NULL(400008, "Schedule task can not be empty"),
|
||||
EXCELFILE_SCHEDULE_CRON_IS_NULL(400009, "Schedule cron can not be empty"),
|
||||
EXCELFILE_SCHEDULE_ENABLE_IS_NULL(400010, "Schedule enable can not be empty"),
|
||||
EXCELFILE_SCHEDULE_SCRIPT_IS_NULL(400011, "Schedule script can not be empty"),
|
||||
|
||||
|
||||
SUCCESS(200, "success"); // 成功
|
||||
|
||||
private RCode(Integer code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
private Integer code;
|
||||
private String msg;
|
||||
private Object[] param;
|
||||
|
||||
public RCode setParam(Object... param) {
|
||||
this.param = param;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object[] getParam() {
|
||||
return param;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return MessageFormat.format(msg, param);
|
||||
}
|
||||
}
|
||||
64
src/main/java/net/geedge/asw/common/util/ResponseUtil.java
Normal file
64
src/main/java/net/geedge/asw/common/util/ResponseUtil.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import com.j256.simplemagic.ContentInfo;
|
||||
import com.j256.simplemagic.ContentInfoUtil;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
public class ResponseUtil {
|
||||
|
||||
/**
|
||||
* downloadFile
|
||||
*
|
||||
* @param response
|
||||
* @param contentType
|
||||
* @param filename
|
||||
* @param data
|
||||
* @throws IORuntimeException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void downloadFile(HttpServletResponse response, String contentType, String filename, byte[] data) throws IORuntimeException, IOException {
|
||||
String fileName = T.URLUtil.encode(filename, T.CharsetUtil.CHARSET_UTF_8);
|
||||
ReflectUtil.invoke(response, "addHeader", "Content-Disposition", "attachment; filename=" + fileName);
|
||||
ReflectUtil.invoke(response, "addHeader", "Content-Length", "" + data.length);
|
||||
ReflectUtil.invoke(response, "setHeader", "Access-Control-Expose-Headers", "Content-Disposition");
|
||||
ReflectUtil.invoke(response, "setContentType", StrUtil.emptyToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE));
|
||||
|
||||
T.IoUtil.write(response.getOutputStream(), false, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* reponse 下载 byte数据
|
||||
* @param response
|
||||
* @param filename
|
||||
* @param data
|
||||
* @throws IORuntimeException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void downloadFile(HttpServletResponse response, String filename, byte[] data)
|
||||
throws IORuntimeException, IOException {
|
||||
response.setContentType(ResponseUtil.getDownloadContentType(filename));
|
||||
String fileName = T.URLUtil.encode(filename, T.CharsetUtil.CHARSET_UTF_8);
|
||||
// response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
|
||||
// response.addHeader("Content-Length", "" + data.length);
|
||||
// response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
|
||||
ReflectUtil.invoke(response, "addHeader", "Content-Disposition", "attachment; filename=" + fileName);
|
||||
ReflectUtil.invoke(response, "addHeader", "Content-Length", "" + data.length);
|
||||
ReflectUtil.invoke(response, "setHeader", "Access-Control-Expose-Headers", "Content-Disposition");
|
||||
T.IoUtil.write(response.getOutputStream(), false, data);
|
||||
}
|
||||
|
||||
public static String getDownloadContentType(String filename){
|
||||
ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(filename);
|
||||
return T.ObjectUtil.isEmpty(extensionMatch) ? MediaType.APPLICATION_OCTET_STREAM_VALUE : extensionMatch.getMimeType();
|
||||
}
|
||||
|
||||
}
|
||||
1529
src/main/java/net/geedge/asw/common/util/T.java
Normal file
1529
src/main/java/net/geedge/asw/common/util/T.java
Normal file
File diff suppressed because it is too large
Load Diff
426
src/main/java/net/geedge/asw/common/util/VerifyUtil.java
Normal file
426
src/main/java/net/geedge/asw/common/util/VerifyUtil.java
Normal file
@@ -0,0 +1,426 @@
|
||||
package net.geedge.asw.common.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
|
||||
/**
|
||||
* ValidateUtil
|
||||
*/
|
||||
public class VerifyUtil {
|
||||
|
||||
private Object value;
|
||||
|
||||
protected VerifyUtil(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建校验实例,传入目标对象
|
||||
*
|
||||
* @param value 校验对象
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public static VerifyUtil is(Object value) {
|
||||
return new VerifyUtil(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换目标对象,不重新创建实例
|
||||
*
|
||||
* @param value 校验对象
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil and(Object value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 非空校验
|
||||
*
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil notNull() {
|
||||
return notNull(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 非空校验
|
||||
*
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil notNull(RCode code) {
|
||||
if (T.ObjectUtil.isNull(value)) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 非空校验
|
||||
*
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil notEmpty() {
|
||||
return notEmpty(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 非空校验
|
||||
*
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil notEmpty(RCode code) {
|
||||
notNull(code);
|
||||
switch(value) {
|
||||
case String v -> {
|
||||
if(T.StrUtil.isEmpty(v)) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Collection<?> v -> {
|
||||
if(T.CollUtil.isEmpty(v)) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Object[] v -> {
|
||||
if(T.ArrayUtil.length(v) == 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
default ->{}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public VerifyUtil equalsIgnoreCase(CharSequence sequence) {
|
||||
return equalsIgnoreCase(sequence, null);
|
||||
}
|
||||
|
||||
public VerifyUtil equalsIgnoreCase(CharSequence sequence, RCode code) {
|
||||
if (!T.StrUtil.equalsIgnoreCase(sequence, T.StrUtil.toStringOrNull(value))) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则校验
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil regex(String regex) {
|
||||
return regex(regex, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则校验
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil regex(String regex, RCode code) {
|
||||
if (!T.ReUtil.isMatch(regex, T.StrUtil.toStringOrNull(value))) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最大值校验
|
||||
*
|
||||
* @param max 最大值
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil max(Number max) {
|
||||
return max(max, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 最大值校验
|
||||
*
|
||||
* @param max 最大值
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil max(Number max, RCode code) {
|
||||
|
||||
switch (value) {
|
||||
case null -> {
|
||||
}
|
||||
case Integer v -> {
|
||||
if (T.NumberUtil.compare(v, max.intValue()) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Long v -> {
|
||||
if (T.NumberUtil.compare(v, max.longValue()) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Double v -> {
|
||||
if (T.NumberUtil.compare(v, max.doubleValue()) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Float v -> {
|
||||
if (T.NumberUtil.compare(v, max.floatValue()) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Short v -> {
|
||||
if (T.NumberUtil.compare(v, max.shortValue()) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Byte v -> {
|
||||
if (T.NumberUtil.compare(v, max.byteValue()) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小值校验
|
||||
*
|
||||
* @param min 最小值
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil min(Number min) {
|
||||
return min(min, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小值校验
|
||||
*
|
||||
* @param min 最小值
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil min(Number min, RCode code) {
|
||||
switch (value) {
|
||||
case null -> {
|
||||
}
|
||||
case Integer v -> {
|
||||
if (T.NumberUtil.compare(min.intValue(), v) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Long v -> {
|
||||
if (T.NumberUtil.compare(min.longValue(), v) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Double v -> {
|
||||
if (T.NumberUtil.compare(min.doubleValue(), v) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Float v -> {
|
||||
if (T.NumberUtil.compare(min.floatValue(), v) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Short v -> {
|
||||
if (T.NumberUtil.compare(min.shortValue(), v) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Byte v -> {
|
||||
if (T.NumberUtil.compare(min.byteValue(), v) > 0) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最大长度校验
|
||||
*
|
||||
* @param max 最大长度
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil maxLength(int max) {
|
||||
return maxLength(max, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 最大长度校验
|
||||
*
|
||||
* @param max 最大长度
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil maxLength(int max, RCode code) {
|
||||
switch (value) {
|
||||
case null -> {
|
||||
}
|
||||
case String v -> {
|
||||
if (T.StrUtil.length(v) > max) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Collection v -> {
|
||||
if (T.CollectionUtil.size(v) > max) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Object[] v -> {
|
||||
if (T.ArrayUtil.length(v) > max) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小长度校验
|
||||
*
|
||||
* @param min 最小长度
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil minLength(int min) {
|
||||
return minLength(min, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小长度校验
|
||||
*
|
||||
* @param min 最小长度
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil minLength(int min, RCode code) {
|
||||
switch (value) {
|
||||
case null -> {
|
||||
}
|
||||
case String v -> {
|
||||
if (T.StrUtil.length(v) < min) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Collection v -> {
|
||||
if (T.CollectionUtil.size(v) < min) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
case Object[] v -> {
|
||||
if (T.ArrayUtil.length(v) < min) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义日期格式校验
|
||||
*
|
||||
* @param format 格式
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil date(String format) {
|
||||
return date(format, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义日期格式校验
|
||||
*
|
||||
* @param format 格式
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil date(String format, RCode code) {
|
||||
try {
|
||||
T.DateUtil.parse(T.StrUtil.toStringOrNull(value), format);
|
||||
} catch (Exception e) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* IP地址校验
|
||||
*
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil ip() {
|
||||
return ip(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* IP地址校验
|
||||
*
|
||||
* @param msg 错误信息
|
||||
* @return ValidateUtils
|
||||
*/
|
||||
public VerifyUtil ip(RCode code) {
|
||||
if (!Validator.isIpv4(T.StrUtil.toStringOrNull(value)) && !Validator.isIpv6(T.StrUtil.toStringOrNull(value))) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public VerifyUtil port(RCode code) {
|
||||
if (!T.NetUtil.isValidPort(T.NumberUtil.parseInt(T.StrUtil.toStringOrNull(value)))) {
|
||||
throw ASWException.builder().rcode(code).build();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多参数校验,不能同时为空
|
||||
*
|
||||
* @param code
|
||||
* @param validates
|
||||
*/
|
||||
public static VerifyUtil notAllNull(RCode code, Object... validates) {
|
||||
boolean allNull = true;
|
||||
for (Object validate : validates) {
|
||||
if (validate != null) {
|
||||
if (validate instanceof String) {
|
||||
if (T.ObjectUtil.isNull((String) validate))
|
||||
continue;
|
||||
} else if (validate instanceof Number) {
|
||||
if (((Number) validate) == null)
|
||||
continue;
|
||||
} else if (validate instanceof Collection) {
|
||||
if (T.ObjectUtil.isNull((Collection) validate))
|
||||
continue;
|
||||
} else if (validate instanceof Map) {
|
||||
if (T.ObjectUtil.isNull((Map) validate))
|
||||
continue;
|
||||
} else if (validate instanceof Object[]) {
|
||||
if (T.ObjectUtil.isNull((Object[]) validate))
|
||||
continue;
|
||||
}
|
||||
allNull = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allNull) {
|
||||
throw new ASWException(code);
|
||||
}
|
||||
return new VerifyUtil(validates);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package net.geedge.asw.module.file.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import cn.hutool.log.Log;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import net.geedge.asw.common.util.ASWException;
|
||||
import net.geedge.asw.common.util.R;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.file.entity.FileContentEntity;
|
||||
import net.geedge.asw.module.file.entity.FileEntity;
|
||||
import net.geedge.asw.module.file.service.IFileService;
|
||||
import net.geedge.asw.module.sys.controller.BaseController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
public class FileController extends BaseController {
|
||||
private static final Log log = Log.get();
|
||||
@Autowired
|
||||
private IFileService fileService;
|
||||
// 单位: s,默认:1天
|
||||
@Value("${file.expires:86400}")
|
||||
private Integer fileExpires;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R detail(@PathVariable("id") String id) {
|
||||
FileEntity entity = fileService.getById(id);
|
||||
return R.ok().putData("record", entity);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public R list(String q, @RequestParam(defaultValue = "1") Integer current,
|
||||
@RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) {
|
||||
QueryWrapper<FileEntity> queryWrapper = new QueryWrapper<FileEntity>();
|
||||
queryWrapper.and(T.StrUtil.isNotBlank(q), wrapper -> {
|
||||
wrapper.like("name", q).or().like("remark", q).or().like("path", q);
|
||||
});
|
||||
Page<FileEntity> page = Page.of(current, size);
|
||||
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
|
||||
page = fileService.page(page, queryWrapper);
|
||||
page.getRecords().forEach(entity -> {
|
||||
entity.setUri(T.StrUtil.concat(true, "/file/content/", entity.getId(), ".", entity.getSuffix()));
|
||||
});
|
||||
return R.ok(page);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public R add(String remark, @RequestParam(value = "file", required = false) MultipartFile file) throws IOException {
|
||||
if (T.ObjectUtil.isNull(file)) {
|
||||
throw ASWException.builder().rcode(RCode.PARAM_CANNOT_EMPTY).build();
|
||||
}
|
||||
FileEntity savedFile = fileService.saveFile(remark, file);
|
||||
return R.ok().putData("record", savedFile);
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public R delete(String[] ids) {
|
||||
T.VerifyUtil.is(ids).notEmpty();
|
||||
fileService.deleteFile(ids);
|
||||
log.info("delete files, ids: {}", T.ArrayUtil.toString(ids));
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件内容
|
||||
*
|
||||
* @param name name = id + suffix , eq: adfadfadf.txt
|
||||
* @param request
|
||||
* @param response
|
||||
* @throws IOException
|
||||
*/
|
||||
@GetMapping("/content/{name}")
|
||||
public void download(@PathVariable("name") String name, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String id = T.FileNameUtil.getPrefix(name);
|
||||
FileEntity fileEntity = fileService.getById(id);
|
||||
if (T.ObjectUtil.isNull(fileEntity)) {
|
||||
response.sendError(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.getReasonPhrase());
|
||||
} else {
|
||||
FileContentEntity fileContentEntity = fileService.getFileContentEntityById(id);
|
||||
response.setStatus(200);
|
||||
response.setContentType(fileEntity.getContentType());
|
||||
response.setContentLength(fileEntity.getSize());
|
||||
response.setHeader("Content-disposition", "attachment; filename=" + fileEntity.getName());
|
||||
// 设置缓存响应头
|
||||
response.setDateHeader("Expires", T.DateUtil.current() + fileExpires * 1000);
|
||||
response.setHeader("Cache-Control", "public, max-age=" + fileExpires);
|
||||
response.getOutputStream().write(fileContentEntity.getContent());
|
||||
response.flushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.geedge.asw.module.file.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.file.entity.FileContentEntity;
|
||||
|
||||
@Mapper
|
||||
public interface FileContentDao extends BaseMapper<FileContentEntity> {
|
||||
|
||||
}
|
||||
17
src/main/java/net/geedge/asw/module/file/dao/FileDao.java
Normal file
17
src/main/java/net/geedge/asw/module/file/dao/FileDao.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.geedge.asw.module.file.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.file.entity.FileEntity;
|
||||
|
||||
@Mapper
|
||||
public interface FileDao extends BaseMapper<FileEntity> {
|
||||
|
||||
// public void saveFileContent(FileContentEntity entity);
|
||||
//
|
||||
// public FileContentEntity getFileContentById(String id);
|
||||
//
|
||||
// public boolean deleteFileContentById(List<String> ids);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.geedge.asw.module.file.entity;
|
||||
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_file_content")
|
||||
public class FileContentEntity {
|
||||
|
||||
private String id;
|
||||
@TableField(jdbcType = JdbcType.BLOB)
|
||||
private byte[] content;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.geedge.asw.module.file.entity;
|
||||
|
||||
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 lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_file")
|
||||
public class FileEntity {
|
||||
|
||||
/**
|
||||
* 以文件md5作为id
|
||||
*/
|
||||
@TableId(type = IdType.NONE)
|
||||
private String id;
|
||||
private String name;
|
||||
private String suffix;
|
||||
private String contentType;
|
||||
private Integer size;
|
||||
private String path;
|
||||
private String remark;
|
||||
private Long createTimestamp;
|
||||
@TableField(exist = false)
|
||||
private String uri;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.geedge.asw.module.file.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import net.geedge.asw.module.file.entity.FileContentEntity;
|
||||
import net.geedge.asw.module.file.entity.FileEntity;
|
||||
|
||||
public interface IFileService extends IService<FileEntity> {
|
||||
|
||||
/**
|
||||
* 保存文件信息及内容
|
||||
*
|
||||
* @param remark
|
||||
* @param file
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
FileEntity saveFile(String remark, MultipartFile file) throws IOException;
|
||||
|
||||
/**
|
||||
* 根据文件 id 获取内容
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public FileContentEntity getFileContentEntityById(String id);
|
||||
|
||||
/**
|
||||
* 删除文件和文件内容
|
||||
*
|
||||
* @param ids
|
||||
*/
|
||||
void deleteFile(String[] ids);
|
||||
|
||||
public void saveFileAndContent(FileEntity entity, File file) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package net.geedge.asw.module.file.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.MediaTypeFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.file.dao.FileContentDao;
|
||||
import net.geedge.asw.module.file.dao.FileDao;
|
||||
import net.geedge.asw.module.file.entity.FileContentEntity;
|
||||
import net.geedge.asw.module.file.entity.FileEntity;
|
||||
import net.geedge.asw.module.file.service.IFileService;
|
||||
|
||||
@Service
|
||||
public class FileServiceImpl extends ServiceImpl<FileDao, FileEntity> implements IFileService {
|
||||
|
||||
@Autowired
|
||||
private FileContentDao fileContentDao;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public FileEntity saveFile(String remark, MultipartFile file) throws IOException {
|
||||
String md5 = T.DigestUtil.md5Hex(file.getBytes());
|
||||
// 根据md5判断是否已经存在,存在直接响应
|
||||
FileEntity entity = this.getOne(new QueryWrapper<FileEntity>().lambda().eq(FileEntity::getId, md5));
|
||||
if (T.ObjectUtil.isNull(entity)) {
|
||||
entity = new FileEntity();
|
||||
entity.setId(md5);
|
||||
entity.setSize((int) file.getSize());
|
||||
entity.setName(file.getOriginalFilename());
|
||||
entity.setSuffix(T.FileNameUtil.getSuffix(entity.getName()));
|
||||
entity.setCreateTimestamp(T.DateUtil.current());
|
||||
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(entity.getName());
|
||||
if (mediaType.isPresent()) {
|
||||
entity.setContentType(mediaType.get().toString());
|
||||
} else {
|
||||
entity.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
}
|
||||
entity.setRemark(remark);
|
||||
this.saveFileAndContent(entity, file);
|
||||
}
|
||||
entity.setUri(T.StrUtil.concat(true, "/file/content/", entity.getId(), ".", entity.getSuffix()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void saveFileAndContent(FileEntity entity, MultipartFile file) throws IOException {
|
||||
// 默认统一保存到数据库中
|
||||
entity.setPath(T.StrUtil.concat(true, "db:", entity.getId()));
|
||||
this.save(entity);
|
||||
FileContentEntity content = new FileContentEntity();
|
||||
content.setId(entity.getId());
|
||||
content.setContent(file.getBytes());
|
||||
fileContentDao.insert(content);
|
||||
}
|
||||
@Override
|
||||
public void saveFileAndContent(FileEntity entity, File file) throws IOException {
|
||||
// 默认统一保存到数据库中
|
||||
entity.setPath(T.StrUtil.concat(true, "db:", entity.getId()));
|
||||
this.save(entity);
|
||||
FileContentEntity content = new FileContentEntity();
|
||||
content.setId(entity.getId());
|
||||
content.setContent(T.FileUtil.readBytes(file));
|
||||
fileContentDao.insert(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteFile(String[] ids) {
|
||||
List<String> list = T.ListUtil.of(ids);
|
||||
this.removeByIds(list);
|
||||
fileContentDao.deleteBatchIds(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileContentEntity getFileContentEntityById(String id) {
|
||||
return fileContentDao.selectById(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.geedge.asw.module.sys.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
public abstract class BaseController {
|
||||
|
||||
protected String getSysUserId() {
|
||||
return StpUtil.getLoginIdAsString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package net.geedge.asw.module.sys.controller;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import net.geedge.asw.common.util.R;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysAuthService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/sys")
|
||||
public class SysAuthController {
|
||||
|
||||
@Autowired
|
||||
private ISysAuthService authService;
|
||||
|
||||
record AuthRecord(String userName, String pwd) {}
|
||||
|
||||
@PostMapping("/login")
|
||||
public R login(@RequestBody AuthRecord record) {
|
||||
T.VerifyUtil.is(record.userName()).notEmpty(RCode.SYS_USER_PWD_ERROR).and(record.pwd())
|
||||
.notEmpty(RCode.SYS_USER_PWD_ERROR);
|
||||
SysUserEntity userEntity = authService.login(record.userName(), record.pwd());
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
userEntity.setPwd(null);
|
||||
return R.ok().putData("tokenInfo", tokenInfo).putData("user", userEntity);
|
||||
}
|
||||
|
||||
@RequestMapping("/logout")
|
||||
public R logout() {
|
||||
authService.logout();
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/permissions")
|
||||
public R permissions() {
|
||||
Map<String, Object> permissions = authService.userPermissions();
|
||||
return R.ok(permissions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.geedge.asw.module.sys.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import cn.hutool.log.Log;
|
||||
import net.geedge.asw.common.util.ASWException;
|
||||
import net.geedge.asw.common.util.R;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysRoleService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/sys/role")
|
||||
public class SysRoleController {
|
||||
|
||||
private static final Log log = Log.get();
|
||||
@Autowired
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R detail(@PathVariable("id") String id) {
|
||||
SysRoleEntity entity = roleService.getById(id);
|
||||
return R.ok().putData("record", entity);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public R list(String ids, String name, @RequestParam(defaultValue = "1") Integer current,
|
||||
@RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) {
|
||||
QueryWrapper<SysRoleEntity> queryWrapper = new QueryWrapper<SysRoleEntity>();
|
||||
queryWrapper.like(T.StrUtil.isNotBlank(name), "name", name).in(T.StrUtil.isNotBlank(ids), "id", ids.split(","));
|
||||
Page<SysRoleEntity> page = Page.of(current, size);
|
||||
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
|
||||
page = roleService.page(page, queryWrapper);
|
||||
return R.ok(page);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public R add(@RequestBody SysRoleEntity entity) {
|
||||
T.VerifyUtil.is(entity).notNull().and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY);
|
||||
SysRoleEntity one = roleService.getOne(new QueryWrapper<SysRoleEntity>().lambda().eq(SysRoleEntity::getName, entity.getName()));
|
||||
if (T.ObjectUtil.isNotNull(one)) {
|
||||
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
|
||||
}
|
||||
entity.setCreateTimestamp(T.DateUtil.current());
|
||||
roleService.saveOrUpdateRole(entity);
|
||||
return R.ok().putData("id", entity.getId());
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public R update(@RequestBody SysRoleEntity entity) {
|
||||
T.VerifyUtil.is(entity).notNull().and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY);
|
||||
entity.setCreateTimestamp(null);
|
||||
roleService.saveOrUpdateRole(entity);
|
||||
return R.ok().putData("id", entity.getId());
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public R delete(String[] ids) {
|
||||
T.VerifyUtil.is(ids).notEmpty();
|
||||
roleService.removeBatchByIds(T.ListUtil.of(ids));
|
||||
log.info("delete Role, ids: {}", T.ArrayUtil.toString(ids));
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.geedge.asw.module.sys.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import cn.hutool.log.Log;
|
||||
import net.geedge.asw.common.util.ASWException;
|
||||
import net.geedge.asw.common.util.Constants;
|
||||
import net.geedge.asw.common.util.R;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleEntity;
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysRoleService;
|
||||
import net.geedge.asw.module.sys.service.ISysUserService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/sys/user")
|
||||
public class SysUserController {
|
||||
|
||||
private static final Log log = Log.get();
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
@Autowired
|
||||
private ISysRoleService roleService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R detail(@PathVariable("id") String id) {
|
||||
SysUserEntity entity = userService.getById(id);
|
||||
entity.setPwd(null);
|
||||
List<SysRoleEntity> roleList = roleService.listByIds(T.ListUtil.of(entity.getRoleIds()));
|
||||
entity.setRoles(roleList);
|
||||
return R.ok().putData("record", entity);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public R list(String name, @RequestParam(defaultValue = "1") Integer current,
|
||||
@RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) {
|
||||
QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<SysUserEntity>();
|
||||
// 不查询 pwd 列
|
||||
queryWrapper.select(SysUserEntity.class, entity -> !entity.getColumn().equals("pwd"));
|
||||
if (T.StrUtil.isNotBlank(name)) {
|
||||
queryWrapper.and(wrapper -> wrapper.like("name", name).or().like("user_name", name));
|
||||
}
|
||||
Page<SysUserEntity> page = Page.of(current, size);
|
||||
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
|
||||
page = userService.page(page, queryWrapper);
|
||||
return R.ok(page);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public R add(@RequestBody SysUserEntity entity) {
|
||||
T.VerifyUtil.is(entity).notNull().and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
|
||||
.and(entity.getUserName()).notEmpty().and(entity.getPwd()).notEmpty();
|
||||
SysUserEntity one = userService.getOne(
|
||||
new QueryWrapper<SysUserEntity>().lambda().eq(SysUserEntity::getUserName, entity.getUserName()));
|
||||
if (T.ObjectUtil.isNotNull(one)) {
|
||||
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
|
||||
}
|
||||
// 密码加密
|
||||
entity.setPwd(T.AesUtil.encrypt(entity.getPwd(), Constants.AES_KEY));
|
||||
entity.setCreateTimestamp(T.DateUtil.current());
|
||||
userService.saveOrUpdateUser(entity);
|
||||
return R.ok().putData("id", entity.getId());
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public R update(@RequestBody SysUserEntity entity) {
|
||||
T.VerifyUtil.is(entity).notNull().and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY);
|
||||
if (T.StrUtil.isNotBlank(entity.getPwd())) {
|
||||
// 密码加密
|
||||
entity.setPwd(T.AesUtil.encrypt(entity.getPwd(), Constants.AES_KEY));
|
||||
} else {
|
||||
entity.setPwd(null);
|
||||
}
|
||||
entity.setUserName(null);// username 不允许修改
|
||||
entity.setCreateTimestamp(null);
|
||||
userService.saveOrUpdateUser(entity);
|
||||
return R.ok().putData("id", entity.getId());
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public R delete(String[] ids) {
|
||||
T.VerifyUtil.is(ids).notEmpty();
|
||||
userService.removeBatchByIds(T.ListUtil.of(ids));
|
||||
log.info("delete user, ids: {}", T.ArrayUtil.toString(ids));
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.geedge.asw.module.sys.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysConfigEntity;
|
||||
|
||||
@Mapper
|
||||
public interface SysConfigDao extends BaseMapper<SysConfigEntity>{
|
||||
|
||||
}
|
||||
21
src/main/java/net/geedge/asw/module/sys/dao/SysRoleDao.java
Normal file
21
src/main/java/net/geedge/asw/module/sys/dao/SysRoleDao.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package net.geedge.asw.module.sys.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysMenuEntity;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleEntity;
|
||||
|
||||
@Mapper
|
||||
public interface SysRoleDao extends BaseMapper<SysRoleEntity> {
|
||||
|
||||
@Select("select sr.* from sys_role sr left join sys_user_role sur on sr.id = sur.role_id where sur.user_id = #{userId}")
|
||||
public List<SysRoleEntity> findRoleByUserId(String userId);
|
||||
|
||||
@Select("select sm.* from sys_menu sm LEFT JOIN sys_role_menu srm on sm.id = srm.menu_id LEFT JOIN sys_user_role sur on srm.role_id = sur.role_id where sur.user_id = #{userId} and sm.state = 1 order by sm.order")
|
||||
public List<SysMenuEntity> findMenuByUserId(String userId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.geedge.asw.module.sys.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysRoleMenuEntity;
|
||||
|
||||
@Mapper
|
||||
public interface SysRoleMenuDao extends BaseMapper<SysRoleMenuEntity>{
|
||||
|
||||
}
|
||||
12
src/main/java/net/geedge/asw/module/sys/dao/SysUserDao.java
Normal file
12
src/main/java/net/geedge/asw/module/sys/dao/SysUserDao.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package net.geedge.asw.module.sys.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
|
||||
@Mapper
|
||||
public interface SysUserDao extends BaseMapper<SysUserEntity>{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.geedge.asw.module.sys.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysUserRoleEntity;
|
||||
|
||||
@Mapper
|
||||
public interface SysUserRoleDao extends BaseMapper<SysUserRoleEntity>{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.geedge.asw.module.sys.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import net.geedge.asw.common.util.T;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@TableName("sys_config")
|
||||
public class SysConfigEntity {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String paramKey;
|
||||
private String paramValue;
|
||||
private Integer status;
|
||||
private String remark;
|
||||
|
||||
public enum KeyEnum {
|
||||
SCREEN_PUBLISH_NUM("screen_publish_num");
|
||||
|
||||
private String key;
|
||||
|
||||
KeyEnum(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
System.out.println(SysConfigEntity.KeyEnum.SCREEN_PUBLISH_NUM.getKey());
|
||||
}
|
||||
|
||||
public Integer getParamValueAsInt() {
|
||||
return T.NumberUtil.parseInt(this.paramValue);
|
||||
}
|
||||
|
||||
public Long getParamValueAsLong() {
|
||||
return T.NumberUtil.parseLong(this.paramValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.geedge.asw.module.sys.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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 lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_menu")
|
||||
public class SysMenuEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
private String name;
|
||||
private String i18n;
|
||||
private String pid;
|
||||
private String type;
|
||||
private String perms;
|
||||
private String route;
|
||||
private String icon;
|
||||
private Integer order;
|
||||
private Integer state;
|
||||
private Long createTimestamp;
|
||||
@TableField(exist = false)
|
||||
private List<SysMenuEntity> children;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package net.geedge.asw.module.sys.entity;
|
||||
|
||||
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 lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_role")
|
||||
public class SysRoleEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
private String name;
|
||||
private String i18n;
|
||||
private String remark;
|
||||
private Integer buildIn;
|
||||
@TableField(exist = false)
|
||||
private String[] menuIds;
|
||||
private Long createTimestamp;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.geedge.asw.module.sys.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@TableName("sys_role_menu")
|
||||
public class SysRoleMenuEntity {
|
||||
|
||||
private String roleId;
|
||||
private String menuId;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.geedge.asw.module.sys.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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 lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("sys_user")
|
||||
public class SysUserEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
private String name;
|
||||
@TableField("user_name")
|
||||
private String userName;
|
||||
private String pwd;
|
||||
@TableField(exist = false)
|
||||
private String[] roleIds;
|
||||
@TableField(exist = false)
|
||||
private List<SysRoleEntity> roles;
|
||||
private Long createTimestamp;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.geedge.asw.module.sys.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@TableName("sys_user_role")
|
||||
public class SysUserRoleEntity {
|
||||
|
||||
private String userId;
|
||||
private String roleId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.geedge.asw.module.sys.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
|
||||
public interface ISysAuthService{
|
||||
|
||||
public SysUserEntity login(String userName, String pwd);
|
||||
|
||||
public void logout();
|
||||
|
||||
public Map<String, Object> userPermissions();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.geedge.asw.module.sys.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysConfigEntity;
|
||||
|
||||
public interface ISysConfigService extends IService<SysConfigEntity> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.geedge.asw.module.sys.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysRoleMenuEntity;
|
||||
|
||||
public interface ISysRoleMenuService extends IService<SysRoleMenuEntity> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.geedge.asw.module.sys.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysRoleEntity;
|
||||
|
||||
public interface ISysRoleService extends IService<SysRoleEntity>{
|
||||
|
||||
public void saveOrUpdateRole(SysRoleEntity entity);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.geedge.asw.module.sys.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysUserRoleEntity;
|
||||
|
||||
public interface ISysUserRoleService extends IService<SysUserRoleEntity> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.geedge.asw.module.sys.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
|
||||
public interface ISysUserService extends IService<SysUserEntity>{
|
||||
|
||||
public void saveOrUpdateUser(SysUserEntity entity);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package net.geedge.asw.module.sys.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import net.geedge.asw.common.util.ASWException;
|
||||
import net.geedge.asw.common.util.Constants;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.sys.dao.SysRoleDao;
|
||||
import net.geedge.asw.module.sys.dao.SysUserDao;
|
||||
import net.geedge.asw.module.sys.entity.SysMenuEntity;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleEntity;
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysAuthService;
|
||||
|
||||
@Service
|
||||
public class SysAuthServiceImpl implements ISysAuthService {
|
||||
|
||||
private static final Log log = Log.get();
|
||||
|
||||
@Autowired
|
||||
private SysUserDao userDao;
|
||||
@Autowired
|
||||
private SysRoleDao roleDao;
|
||||
|
||||
@Override
|
||||
public SysUserEntity login(String userName, String pwd) {
|
||||
SysUserEntity userEntity = userDao
|
||||
.selectOne(new QueryWrapper<SysUserEntity>().lambda().eq(SysUserEntity::getUserName, userName));
|
||||
if (T.ObjectUtil.isNull(userEntity)
|
||||
|| !T.StrUtil.equals(userEntity.getPwd(), T.AesUtil.encrypt(pwd, Constants.AES_KEY))) {
|
||||
log.warn("user login error, username: {}", userName);
|
||||
throw ASWException.builder().rcode(RCode.SYS_USER_PWD_ERROR).build();
|
||||
}
|
||||
StpUtil.login(userEntity.getId());
|
||||
log.info("user login success, userName: {}", userName);
|
||||
return userEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
StpUtil.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录用户权限
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> userPermissions() {
|
||||
Map<String, Object> result = T.MapUtil.newHashMap();
|
||||
String userId = StpUtil.getLoginIdAsString();
|
||||
List<SysRoleEntity> roleList = roleDao.findRoleByUserId(userId);
|
||||
result.put("roles", roleList);
|
||||
// 组织 menu数据
|
||||
List<SysMenuEntity> menuList = roleDao.findMenuByUserId(userId);
|
||||
List<String> buttonList = menuList.stream().filter(menu -> T.StrUtil.equalsIgnoreCase(menu.getType(), "button"))
|
||||
.map(menu -> menu.getName()).collect(Collectors.toList());
|
||||
result.put("buttons", buttonList);
|
||||
//生成 menu tree结构
|
||||
Map<String, List<SysMenuEntity>> groupMap = menuList.stream()
|
||||
.filter(menu -> T.StrUtil.equalsIgnoreCase(menu.getType(), "menu"))
|
||||
.collect(Collectors.groupingBy(SysMenuEntity::getPid));
|
||||
menuList.forEach(menu -> {
|
||||
menu.setChildren(groupMap.get(menu.getId()));
|
||||
});
|
||||
List<SysMenuEntity> collect = menuList.stream().filter(menu -> T.StrUtil.isBlank(menu.getPid()))
|
||||
.collect(Collectors.toList());
|
||||
result.put("menus", collect);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.geedge.asw.module.sys.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import net.geedge.asw.module.sys.dao.SysConfigDao;
|
||||
import net.geedge.asw.module.sys.entity.SysConfigEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysConfigService;
|
||||
|
||||
@Service
|
||||
public class SysConfigServiceImpl extends ServiceImpl<SysConfigDao, SysConfigEntity> implements ISysConfigService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.geedge.asw.module.sys.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import net.geedge.asw.module.sys.dao.SysRoleMenuDao;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleMenuEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysRoleMenuService;
|
||||
|
||||
@Service
|
||||
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuDao, SysRoleMenuEntity>
|
||||
implements ISysRoleMenuService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package net.geedge.asw.module.sys.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.sys.dao.SysRoleDao;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleEntity;
|
||||
import net.geedge.asw.module.sys.entity.SysRoleMenuEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysRoleMenuService;
|
||||
import net.geedge.asw.module.sys.service.ISysRoleService;
|
||||
|
||||
@Service
|
||||
public class SysRoleServiceImpl extends ServiceImpl<SysRoleDao, SysRoleEntity> implements ISysRoleService {
|
||||
|
||||
@Autowired
|
||||
private ISysRoleMenuService roleMenuService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveOrUpdateRole(SysRoleEntity entity) {
|
||||
// 删除 sys_role_menu 关联数据
|
||||
if (T.StrUtil.isBlank(entity.getId())) {
|
||||
roleMenuService.remove(new QueryWrapper<SysRoleMenuEntity>().eq("role_id", entity.getId()));
|
||||
}
|
||||
// 保存 role
|
||||
this.saveOrUpdate(entity);
|
||||
// 保存 role 和 menu关联关系
|
||||
List<SysRoleMenuEntity> rmList = T.ListUtil.list(false);
|
||||
Stream.of(entity.getMenuIds()).forEach(menuId -> {
|
||||
rmList.add(SysRoleMenuEntity.builder().roleId(entity.getId()).menuId(menuId).build());
|
||||
});
|
||||
roleMenuService.saveBatch(rmList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.geedge.asw.module.sys.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import net.geedge.asw.module.sys.dao.SysUserRoleDao;
|
||||
import net.geedge.asw.module.sys.entity.SysUserRoleEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysUserRoleService;
|
||||
|
||||
@Service
|
||||
public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleDao, SysUserRoleEntity>
|
||||
implements ISysUserRoleService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.geedge.asw.module.sys.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.sys.dao.SysUserDao;
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
import net.geedge.asw.module.sys.entity.SysUserRoleEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysUserService;
|
||||
|
||||
@Service
|
||||
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> implements ISysUserService {
|
||||
|
||||
@Autowired
|
||||
private SysUserRoleServiceImpl userRoleService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveOrUpdateUser(SysUserEntity entity) {
|
||||
if (T.StrUtil.isNotBlank(entity.getId())) {
|
||||
userRoleService.remove(new QueryWrapper<SysUserRoleEntity>().eq("user_id", entity.getId()));
|
||||
}
|
||||
// 保存 user
|
||||
this.saveOrUpdate(entity);
|
||||
// 保存 user role关系
|
||||
List<SysUserRoleEntity> urList = T.ListUtil.list(false);
|
||||
Stream.of(entity.getRoleIds()).forEach(roleId -> {
|
||||
urList.add(SysUserRoleEntity.builder().roleId(roleId).userId(entity.getId()).build());
|
||||
});
|
||||
userRoleService.saveBatch(urList);
|
||||
}
|
||||
|
||||
}
|
||||
77
src/main/resources/application-magic-api.yml
Normal file
77
src/main/resources/application-magic-api.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
magic-api:
|
||||
web: /magic/web # UI请求的界面以及UI服务地址
|
||||
resource: #配置存储方式
|
||||
type: database # 配置存储在数据库中
|
||||
tableName: magic_api_file # 数据库中的表名
|
||||
# datasource: magic #指定数据源(单数据源时无需配置,多数据源时默认使用主数据源,如果存在其他数据源中需要指定。)
|
||||
# prefix: /magic-api # key前缀
|
||||
readonly: false # 是否是只读模式
|
||||
prefix: / # 接口前缀,可以不配置
|
||||
auto-import-module: db,http,log # 自动导入的模块
|
||||
auto-import-package: java.lang.*,java.util.*,net.geedge.digitalhorizon.common.util.T #自动导包
|
||||
allow-override: false #禁止覆盖应用接口
|
||||
sql-column-case: camel #启用驼峰命名转换
|
||||
editor-config: classpath:./static/magic-editor.js #编辑器配置
|
||||
support-cross-domain: true # 跨域支持,默认开启
|
||||
# secret-key: 123456789 # 远程推送时的秘钥,未配置则不开启推送
|
||||
push-path: /_magic-api-sync #远程推送的路径,默认为/_magic-api-sync
|
||||
show-sql: true #配置打印SQL
|
||||
compile-cache-size: 500 #配置编译缓存容量
|
||||
persistence-response-body: true #是否持久化保存ResponseBody
|
||||
date-pattern: # 配置请求参数支持的日期格式
|
||||
- yyyy-MM-dd
|
||||
- yyyy-MM-dd HH:mm:ss
|
||||
- yyyyMMddHHmmss
|
||||
- yyyyMMdd
|
||||
response: |- #配置JSON格式,格式为magic-script中的表达式
|
||||
{
|
||||
code: code,
|
||||
message: message,
|
||||
data,
|
||||
timestamp,
|
||||
requestTime,
|
||||
executeTime,
|
||||
}
|
||||
response-code:
|
||||
success: 200 #执行成功的code值
|
||||
invalid: 400 #参数验证未通过的code值
|
||||
exception: 500 #执行出现异常的code值
|
||||
banner: false # 打印banner
|
||||
thread-pool-executor-size: 8 # async语句的线程池大小
|
||||
throw-exception: false #执行出错时是否抛出异常
|
||||
backup: #备份相关配置
|
||||
enable: true #是否启用
|
||||
max-history: -1 #备份保留天数,-1为永久保留
|
||||
# datasource: magic #指定数据源(单数据源时无需配置,多数据源时默认使用主数据源,如果存在其他数据源中需要指定。)
|
||||
table-name: magic_api_backup #使用数据库存储备份时的表名
|
||||
crud: # CRUD相关配置
|
||||
logic-delete-column: deleted #逻辑删除列
|
||||
logic-delete-value: 1 #逻辑删除值
|
||||
cache: # 缓存相关配置
|
||||
capacity: 10000 #缓存容量
|
||||
ttl: -1 # 永不过期
|
||||
enable: true # 启用缓存
|
||||
page:
|
||||
size: size # 页大小的参数名称
|
||||
page: current # 页码的参数名称
|
||||
default-page: 1 # 未传页码时的默认首页
|
||||
default-size: 20 # 未传页大小时的默认页大小
|
||||
security: # 安全配置
|
||||
username: admin # 登录用的用户名
|
||||
password: 123456 # 登录用的密码
|
||||
swagger:
|
||||
version: 1.0
|
||||
description: Digital Horizon API 接口信息
|
||||
title: DH API Swagger Docs
|
||||
name: DH API 接口
|
||||
location: /v2/api-docs/dh-api/swagger2.json
|
||||
debug:
|
||||
timeout: 60 # 断点超时时间,默认60s
|
||||
task:
|
||||
thread-name-prefix: dh-task- #线程池名字前缀
|
||||
log: true # 打印日志
|
||||
pool:
|
||||
size: 8 #线程池大小,默认值为CPU核心数
|
||||
shutdown:
|
||||
awaitTermination: false #关闭时是否等待任务执行完毕,默认为false
|
||||
awaitTerminationPeriod: 10s # 关闭时最多等待任务执行完毕的时间
|
||||
61
src/main/resources/application.yml
Normal file
61
src/main/resources/application.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
spring:
|
||||
banner:
|
||||
location: classpath:static/banner.txt
|
||||
web:
|
||||
resources:
|
||||
static-locations: ./public/
|
||||
cache:
|
||||
cachecontrol:
|
||||
no-cache: true
|
||||
# max-age: 30d
|
||||
# cache-public: true
|
||||
profiles:
|
||||
active: prod
|
||||
include: magic-api
|
||||
datasource:
|
||||
driver-class-name: org.mariadb.jdbc.Driver
|
||||
url: jdbc:mariadb://${database.host}:${database.port}/${database.name}?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
|
||||
username: ${database.user}
|
||||
password: ENC(${database.pin})
|
||||
flyway:
|
||||
enabled: true # 是否开启
|
||||
encoding: UTF-8 # 编码
|
||||
sql-migration-prefix: V # 脚本文件的前缀,默认为V
|
||||
sql-migration-separator: __ # 双下划线
|
||||
baseline-on-migrate: true # 连接数据库中存在表时设置为true
|
||||
locations: classpath:db/migration # 脚本路径
|
||||
clean-disabled: false # flyway 的 clean 命令会删除指定 schema 下的所有 table, 生产务必禁掉。这个默认值是 false 理论上作为默认配置是不科学的
|
||||
validate-on-migrate: true # 执行迁移时是否自动调用验证 当你的 版本不符合逻辑 比如 你先执行了 DML 而没有 对应的DDL 会抛出异常
|
||||
placeholder-replacement: false # 不做取值替换 默认替换为 ${} ,初始化sql中有sql语句存在freemarker替换,所以禁用此项
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 200MB
|
||||
max-request-size: 200MB
|
||||
enabled: true
|
||||
main:
|
||||
allow-circular-references: true
|
||||
|
||||
server:
|
||||
# port: 2023
|
||||
servlet:
|
||||
context-path: /
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
max-threads: 1000
|
||||
min-spare-threads: 5
|
||||
connection-timeout: 60000
|
||||
keep-alive-timeout: 600000
|
||||
compression:
|
||||
enabled: true
|
||||
mime-types: text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:db/mapper/*Mapper.xml
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
banner: false
|
||||
|
||||
logging:
|
||||
config: ./config/logback-spring.xml
|
||||
10
src/main/resources/config/asw.properties
Normal file
10
src/main/resources/config/asw.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
server.port=80
|
||||
database.port=3306
|
||||
#database.host=192.168.44.23
|
||||
#database.name=dh-test-f
|
||||
#database.user=nezha
|
||||
database.host=127.0.0.1
|
||||
database.name=asw
|
||||
database.user=user
|
||||
#database.pin=NTV/OauZb0eHc9tqzQzG0bbfmzH0VRILYhGbU2ssJ1fDr5gqJ+gd5ELnND3wZ6EB
|
||||
database.pin=TPe6maTtzzu0zD0n0H+UTqug9nnb6l+eESqvILQ1dy3e1bEVZHTXfsKjqKp3vAXQ
|
||||
67
src/main/resources/config/logback-spring.xml
Normal file
67
src/main/resources/config/logback-spring.xml
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds">
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
<logger name="org.springframework.web" level="info" />
|
||||
<logger name="org.springboot.sample" level="warn" />
|
||||
<logger name="org.apache" level="warn" />
|
||||
<logger name="org.springframework" level="info" />
|
||||
<logger name="druid.sql" level="info" />
|
||||
|
||||
<property name="log.path" value="/var/log/dh/" />
|
||||
<!-- 输出格式 -->
|
||||
<property name="out.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
|
||||
<!-- 活动文件的大小 -->
|
||||
<property name="max.file.size" value="100MB"/>
|
||||
<!-- 保留的归档文件的最大数量 -->
|
||||
<property name="max.history" value="30"/>
|
||||
<!-- 控制所有归档日志文件的总大小 -->
|
||||
<property name="total.size.cap" value="10GB"/>
|
||||
|
||||
<!-- 2.2 level为 INFO 日志,时间滚动输出 -->
|
||||
<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<!-- 正在记录的日志文档的路径及文档名 -->
|
||||
<file>${log.path}/dh-web.log</file>
|
||||
<!--日志文档输出格式 -->
|
||||
<encoder>
|
||||
<pattern>${out.pattern}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/dh-web-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>${max.file.size}</maxFileSize>
|
||||
<maxHistory>${max.history}</maxHistory>
|
||||
<totalSizeCap>${total.size.cap}</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<!-- 2.1 level为 ERROR 日志,时间滚动输出 -->
|
||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<!-- 正在记录的日志文档的路径及文档名 -->
|
||||
<file>${log.path}/dh-web-error.log</file>
|
||||
<!--日志文档输出格式 -->
|
||||
<encoder>
|
||||
<pattern>${out.pattern}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/dh-web-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>${max.file.size}</maxFileSize>
|
||||
<maxHistory>${max.history}</maxHistory>
|
||||
<totalSizeCap>${total.size.cap}</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<!-- 此日志文档只记录debug级别的 -->
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>error</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="LOG_FILE" />
|
||||
<appender-ref ref="ERROR_FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
136
src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql
Normal file
136
src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 1、新增 sys_user 表
|
||||
* 2、添加默认用户 admin/admin
|
||||
*/
|
||||
DROP TABLE IF EXISTS `sys_user`;
|
||||
CREATE TABLE `sys_user` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`user_name` varchar(255) NOT NULL,
|
||||
`pwd` varchar(255) NOT NULL,
|
||||
`create_timestamp` bigint(20) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_user_name` (`user_name`) USING BTREE,
|
||||
KEY `idx_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT INTO `sys_user`(`id`, `name`, `user_name`, `pwd`, `create_timestamp`) VALUES ('admin', 'admin', 'admin', 'ad9d757e620d5d9cd8e32c3dbcf37525', UNIX_TIMESTAMP(NOW())*1000);
|
||||
|
||||
/**
|
||||
* 1、新增 sys_role 表
|
||||
* 2、新增 sys_menu 表
|
||||
* 3、新增 sys_user_role 表
|
||||
* 4、新增 sys_role_menu 表
|
||||
* 5、添加初始化数据
|
||||
*/
|
||||
DROP TABLE IF EXISTS `sys_role`;
|
||||
CREATE TABLE `sys_role` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`i18n` varchar(255) NOT NULL,
|
||||
`remark` varchar(255) NOT NULL,
|
||||
`build_in` int(10) NOT NULL DEFAULT 0,
|
||||
`create_timestamp` bigint(20) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `sys_menu`;
|
||||
CREATE TABLE `sys_menu` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`i18n` varchar(255) NOT NULL,
|
||||
`pid` varchar(64) NOT NULL DEFAULT '',
|
||||
`type` varchar(64) NOT NULL,
|
||||
`perms` varchar(255) NOT NULL DEFAULT '',
|
||||
`route` varchar(255) NOT NULL DEFAULT '',
|
||||
`icon` varchar(255) NOT NULL DEFAULT '',
|
||||
`order` int(10) NOT NULL DEFAULT 1,
|
||||
`create_timestamp` bigint(20) NOT NULL,
|
||||
`state` int(10) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `sys_user_role`;
|
||||
CREATE TABLE `sys_user_role` (
|
||||
`user_id` varchar(64) NOT NULL,
|
||||
`role_id` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`user_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `sys_role_menu`;
|
||||
CREATE TABLE `sys_role_menu` (
|
||||
`menu_id` varchar(64) NOT NULL,
|
||||
`role_id` varchar(64) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 添加内置角色
|
||||
INSERT INTO `sys_role` (`id`, `name`, `i18n`, `remark`, `build_in`, `create_timestamp`) VALUES ('admin', 'admin', 'admin', 'admin', 1, UNIX_TIMESTAMP(NOW())*1000);
|
||||
|
||||
/**
|
||||
* 1、新增 sys_config 表
|
||||
*/
|
||||
DROP TABLE IF EXISTS `sys_config`;
|
||||
CREATE TABLE `sys_config` (
|
||||
`param_key` varchar(50) NOT NULL COMMENT 'key',
|
||||
`param_value` text DEFAULT NULL,
|
||||
`status` tinyint(4) DEFAULT 1 COMMENT '状态 0:隐藏 1:显示',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`param_key`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';
|
||||
|
||||
/**
|
||||
初始化 magic api 时区配置 ,默认 Asia/Shanghai
|
||||
*/
|
||||
|
||||
INSERT INTO `sys_config` (`param_key`, `param_value`, `status`, `remark`) VALUES ( 'timezone', 'Asia/Shanghai', 1, '时区配置');
|
||||
|
||||
/**
|
||||
* 新增 magic api file 表
|
||||
*/
|
||||
DROP TABLE IF EXISTS `magic_api_file`;
|
||||
|
||||
CREATE TABLE `magic_api_file` (
|
||||
`file_path` varchar(512) NOT NULL,
|
||||
`file_content` mediumtext,
|
||||
PRIMARY KEY (`file_path`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `magic_api_backup`;
|
||||
|
||||
CREATE TABLE `magic_api_backup` (
|
||||
`id` varchar(32) NOT NULL COMMENT '原对象ID',
|
||||
`create_date` bigint(13) NOT NULL COMMENT '备份时间',
|
||||
`tag` varchar(32) DEFAULT NULL COMMENT '标签',
|
||||
`type` varchar(32) DEFAULT NULL COMMENT '类型',
|
||||
`name` varchar(64) DEFAULT NULL COMMENT '原名称',
|
||||
`content` blob COMMENT '备份内容',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '操作人',
|
||||
PRIMARY KEY (`id`,`create_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
/**
|
||||
* 新增 sys_file sys_file_content 表
|
||||
*/
|
||||
DROP TABLE IF EXISTS `sys_file`;
|
||||
CREATE TABLE `sys_file` (
|
||||
`id` varchar(64) NOT NULL COMMENT '文件md5值',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`suffix` varchar(255) NOT NULL DEFAULT '' COMMENT '文件名后缀',
|
||||
`content_type` varchar(255) DEFAULT NULL,
|
||||
`size` bigint(20) NOT NULL,
|
||||
`path` varchar(512) NOT NULL COMMENT 'file:相对路径 或 db:md5',
|
||||
`create_timestamp` bigint(20) NOT NULL,
|
||||
`remark` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `sys_file_content`;
|
||||
CREATE TABLE `sys_file_content` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`content` longblob NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
8
src/main/resources/static/banner.txt
Normal file
8
src/main/resources/static/banner.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
_____ _ _ _ __ __ _
|
||||
/\ / ____| | | | | | \ \ / / | |
|
||||
/ \ _ __ _ __| (___ | | _____| |_ ___| |__ \ \ /\ / /__ _ __| | _____
|
||||
/ /\ \ | '_ \| '_ \\___ \| |/ / _ \ __/ __| '_ \ \ \/ \/ / _ \| '__| |/ / __|
|
||||
/ ____ \| |_) | |_) |___) | < __/ || (__| | | | \ /\ / (_) | | | <\__ \
|
||||
/_/ \_\ .__/| .__/_____/|_|\_\___|\__\___|_| |_| \/ \/ \___/|_| |_|\_\___/
|
||||
| | | |
|
||||
|_| |_| Powered by GeedgeNetworks
|
||||
10
src/main/resources/static/magic-editor.js
Normal file
10
src/main/resources/static/magic-editor.js
Normal file
@@ -0,0 +1,10 @@
|
||||
var MAGIC_EDITOR_CONFIG = {
|
||||
title: 'digital horizon api',
|
||||
header: {
|
||||
skin: true, // 屏蔽皮肤按钮
|
||||
document: true, // 屏蔽文档按钮
|
||||
repo: false, // 屏蔽gitee和github
|
||||
qqGroup: false // 屏蔽加入QQ群
|
||||
}
|
||||
// 其它配置参考本页中其它配置项
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.geedge.asw;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class AppSketchWorksApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user