From 0f9fff2f41d16a32c3c95606ca192ad2c33b810e Mon Sep 17 00:00:00 2001 From: fangshunjian Date: Fri, 21 Jun 2024 11:00:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 220 +++ .../geedge/asw/AppSketchWokrsApplication.java | 24 + .../asw/common/config/MagicApiConfig.java | 104 ++ .../asw/common/config/MybatisPlusConfig.java | 22 + .../asw/common/config/RestTemplateConfig.java | 131 ++ .../asw/common/config/SaTokenConfigure.java | 53 + .../geedge/asw/common/config/WebConfig.java | 46 + .../config/exception/ASWExceptionHandler.java | 65 + .../geedge/asw/common/util/ASWException.java | 103 ++ .../net/geedge/asw/common/util/Constants.java | 19 + .../geedge/asw/common/util/JasyptUtil.java | 51 + .../java/net/geedge/asw/common/util/R.java | 82 + .../net/geedge/asw/common/util/RCode.java | 71 + .../geedge/asw/common/util/ResponseUtil.java | 64 + .../java/net/geedge/asw/common/util/T.java | 1529 +++++++++++++++++ .../geedge/asw/common/util/VerifyUtil.java | 426 +++++ .../file/controller/FileController.java | 110 ++ .../asw/module/file/dao/FileContentDao.java | 12 + .../geedge/asw/module/file/dao/FileDao.java | 17 + .../module/file/entity/FileContentEntity.java | 17 + .../asw/module/file/entity/FileEntity.java | 28 + .../asw/module/file/service/IFileService.java | 42 + .../file/service/impl/FileServiceImpl.java | 90 + .../module/sys/controller/BaseController.java | 11 + .../sys/controller/SysAuthController.java | 50 + .../sys/controller/SysRoleController.java | 78 + .../sys/controller/SysUserController.java | 103 ++ .../asw/module/sys/dao/SysConfigDao.java | 12 + .../geedge/asw/module/sys/dao/SysRoleDao.java | 21 + .../asw/module/sys/dao/SysRoleMenuDao.java | 12 + .../geedge/asw/module/sys/dao/SysUserDao.java | 12 + .../asw/module/sys/dao/SysUserRoleDao.java | 12 + .../module/sys/entity/SysConfigEntity.java | 45 + .../asw/module/sys/entity/SysMenuEntity.java | 30 + .../asw/module/sys/entity/SysRoleEntity.java | 23 + .../module/sys/entity/SysRoleMenuEntity.java | 15 + .../asw/module/sys/entity/SysUserEntity.java | 27 + .../module/sys/entity/SysUserRoleEntity.java | 15 + .../module/sys/service/ISysAuthService.java | 15 + .../module/sys/service/ISysConfigService.java | 9 + .../sys/service/ISysRoleMenuService.java | 9 + .../module/sys/service/ISysRoleService.java | 10 + .../sys/service/ISysUserRoleService.java | 9 + .../module/sys/service/ISysUserService.java | 10 + .../sys/service/impl/SysAuthServiceImpl.java | 81 + .../service/impl/SysConfigServiceImpl.java | 14 + .../service/impl/SysRoleMenuServiceImpl.java | 15 + .../sys/service/impl/SysRoleServiceImpl.java | 43 + .../service/impl/SysUserRoleServiceImpl.java | 15 + .../sys/service/impl/SysUserServiceImpl.java | 41 + src/main/resources/application-magic-api.yml | 77 + src/main/resources/application.yml | 61 + src/main/resources/config/asw.properties | 10 + src/main/resources/config/logback-spring.xml | 67 + .../db/migration/V1.0.01__INIT_TABLES.sql | 136 ++ src/main/resources/static/banner.txt | 8 + src/main/resources/static/magic-editor.js | 10 + .../asw/AppSketchWorksApplicationTests.java | 13 + 58 files changed, 4445 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/net/geedge/asw/AppSketchWokrsApplication.java create mode 100644 src/main/java/net/geedge/asw/common/config/MagicApiConfig.java create mode 100644 src/main/java/net/geedge/asw/common/config/MybatisPlusConfig.java create mode 100644 src/main/java/net/geedge/asw/common/config/RestTemplateConfig.java create mode 100644 src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java create mode 100644 src/main/java/net/geedge/asw/common/config/WebConfig.java create mode 100644 src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java create mode 100644 src/main/java/net/geedge/asw/common/util/ASWException.java create mode 100644 src/main/java/net/geedge/asw/common/util/Constants.java create mode 100644 src/main/java/net/geedge/asw/common/util/JasyptUtil.java create mode 100644 src/main/java/net/geedge/asw/common/util/R.java create mode 100644 src/main/java/net/geedge/asw/common/util/RCode.java create mode 100644 src/main/java/net/geedge/asw/common/util/ResponseUtil.java create mode 100644 src/main/java/net/geedge/asw/common/util/T.java create mode 100644 src/main/java/net/geedge/asw/common/util/VerifyUtil.java create mode 100644 src/main/java/net/geedge/asw/module/file/controller/FileController.java create mode 100644 src/main/java/net/geedge/asw/module/file/dao/FileContentDao.java create mode 100644 src/main/java/net/geedge/asw/module/file/dao/FileDao.java create mode 100644 src/main/java/net/geedge/asw/module/file/entity/FileContentEntity.java create mode 100644 src/main/java/net/geedge/asw/module/file/entity/FileEntity.java create mode 100644 src/main/java/net/geedge/asw/module/file/service/IFileService.java create mode 100644 src/main/java/net/geedge/asw/module/file/service/impl/FileServiceImpl.java create mode 100644 src/main/java/net/geedge/asw/module/sys/controller/BaseController.java create mode 100644 src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java create mode 100644 src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java create mode 100644 src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java create mode 100644 src/main/java/net/geedge/asw/module/sys/dao/SysConfigDao.java create mode 100644 src/main/java/net/geedge/asw/module/sys/dao/SysRoleDao.java create mode 100644 src/main/java/net/geedge/asw/module/sys/dao/SysRoleMenuDao.java create mode 100644 src/main/java/net/geedge/asw/module/sys/dao/SysUserDao.java create mode 100644 src/main/java/net/geedge/asw/module/sys/dao/SysUserRoleDao.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysConfigEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysMenuEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysRoleEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysRoleMenuEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/entity/SysUserRoleEntity.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysAuthService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysRoleMenuService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysRoleService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysUserRoleService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/ISysUserService.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleMenuServiceImpl.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleServiceImpl.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysUserRoleServiceImpl.java create mode 100644 src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java create mode 100644 src/main/resources/application-magic-api.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/config/asw.properties create mode 100644 src/main/resources/config/logback-spring.xml create mode 100644 src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql create mode 100644 src/main/resources/static/banner.txt create mode 100644 src/main/resources/static/magic-editor.js create mode 100644 src/test/java/net/geedge/asw/AppSketchWorksApplicationTests.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..72215a6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,220 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.1.5 + + + net.geedge + asw-controller + 1.0 + asw-controller + AppSketch Works Controller + + 21 + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-quartz + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.flywaydb + flyway-core + + + org.flywaydb + flyway-mysql + + + + io.micrometer + micrometer-registry-prometheus + runtime + + + org.mariadb.jdbc + mariadb-java-client + + + + org.ssssssss + magic-api-spring-boot-starter + 2.1.1 + + + org.ssssssss + magic-script + + + + + org.springframework.restdocs + spring-restdocs-mockmvc + test + + + cn.dev33 + sa-token-spring-boot3-starter + 1.37.0 + + + cn.hutool + hutool-all + 5.8.16 + + + com.baomidou + mybatis-plus-boot-starter + 3.5.4.1 + + + org.projectlombok + lombok + + + org.graalvm.sdk + graal-sdk + 22.3.1 + provided + + + com.github.ulisesbocchio + jasypt-spring-boot-starter + 3.0.5 + + + org.ssssssss + magic-api-plugin-task + 2.1.1 + + + + org.apache.poi + poi-ooxml + 5.2.3 + + + + xerces + xercesImpl + 2.12.2 + + + + + com.github.ulisesbocchio + jasypt-spring-boot-starter + 3.0.5 + + + + org.apache.httpcomponents.client5 + httpclient5 + + + org.ssssssss + magic-script + 1.8.9 + + + com.j256.simplemagic + simplemagic + 1.16 + + + + + ${project.artifactId} + + + src/main/resources + true + + **/logback-spring.xml + **/asw.properties + + + + **/*.* + + + + + + org.graalvm.buildtools + native-maven-plugin + + true + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + + + nexus + Team Nexus Repository + http://192.168.40.153:8099/content/groups/public/ + + + nexus-releases + Team Nexus Repository + http://192.168.40.153:8099/content/repositories/releases/ + + + nexus-geedge + Team Nexus Repository + http://192.168.40.153:8099/content/repositories/geedge/ + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + false + + + + + diff --git a/src/main/java/net/geedge/asw/AppSketchWokrsApplication.java b/src/main/java/net/geedge/asw/AppSketchWokrsApplication.java new file mode 100644 index 0000000..03f39fa --- /dev/null +++ b/src/main/java/net/geedge/asw/AppSketchWokrsApplication.java @@ -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); + } + +} diff --git a/src/main/java/net/geedge/asw/common/config/MagicApiConfig.java b/src/main/java/net/geedge/asw/common/config/MagicApiConfig.java new file mode 100644 index 0000000..37dc0ca --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/MagicApiConfig.java @@ -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 magicResourceStorage, + TaskScheduler taskScheduler, boolean showLog) { + super(magicResourceStorage, taskScheduler, showLog); + this.taskScheduler = taskScheduler; + this.showLog = showLog; + } + + @Override + protected boolean register(MappingNode 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; + } + } + +} diff --git a/src/main/java/net/geedge/asw/common/config/MybatisPlusConfig.java b/src/main/java/net/geedge/asw/common/config/MybatisPlusConfig.java new file mode 100644 index 0000000..68dfa32 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/MybatisPlusConfig.java @@ -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; + } +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/config/RestTemplateConfig.java b/src/main/java/net/geedge/asw/common/config/RestTemplateConfig.java new file mode 100644 index 0000000..57bba8a --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/RestTemplateConfig.java @@ -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 registry = RegistryBuilder.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(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java b/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java new file mode 100644 index 0000000..958c36c --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/SaTokenConfigure.java @@ -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("/**"); + } +} diff --git a/src/main/java/net/geedge/asw/common/config/WebConfig.java b/src/main/java/net/geedge/asw/common/config/WebConfig.java new file mode 100644 index 0000000..5322ff8 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/WebConfig.java @@ -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() { + + 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 bean = new FilterRegistrationBean(new CorsFilter(source)); + // 设置监听器的优先级 + bean.setOrder(0); + return bean; + } +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java b/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java new file mode 100644 index 0000000..c91c9db --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/exception/ASWExceptionHandler.java @@ -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()); + } + +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/util/ASWException.java b/src/main/java/net/geedge/asw/common/util/ASWException.java new file mode 100644 index 0000000..47d56d9 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/ASWException.java @@ -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); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/util/Constants.java b/src/main/java/net/geedge/asw/common/util/Constants.java new file mode 100644 index 0000000..d2e343f --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/Constants.java @@ -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"; + + + +} diff --git a/src/main/java/net/geedge/asw/common/util/JasyptUtil.java b/src/main/java/net/geedge/asw/common/util/JasyptUtil.java new file mode 100644 index 0000000..3cce23a --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/JasyptUtil.java @@ -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; + } + +} diff --git a/src/main/java/net/geedge/asw/common/util/R.java b/src/main/java/net/geedge/asw/common/util/R.java new file mode 100644 index 0000000..dfb401d --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/R.java @@ -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 { + 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()); + if (!(data instanceof Map)) { + throw new ASWException("data put error"); + } + ((Map) data).put(key, value); + super.put("data", data); + return this; + } + + @SuppressWarnings("all") + public R putAllData(Map m) { + super.putAll(m); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/common/util/RCode.java b/src/main/java/net/geedge/asw/common/util/RCode.java new file mode 100644 index 0000000..f242b15 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/RCode.java @@ -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); + } +} diff --git a/src/main/java/net/geedge/asw/common/util/ResponseUtil.java b/src/main/java/net/geedge/asw/common/util/ResponseUtil.java new file mode 100644 index 0000000..5d29954 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/ResponseUtil.java @@ -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(); + } + +} diff --git a/src/main/java/net/geedge/asw/common/util/T.java b/src/main/java/net/geedge/asw/common/util/T.java new file mode 100644 index 0000000..23011ac --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/T.java @@ -0,0 +1,1529 @@ +package net.geedge.asw.common.util; + +import java.awt.Graphics; +import java.awt.Robot; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.time.LocalDateTime; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Collection; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; + +import com.baomidou.mybatisplus.core.metadata.OrderItem; + +import cn.hutool.core.date.DateTime; + +public class T { + + public static final Pattern REGEX_SPECIAL_DATE = Pattern + .compile("(\\d{4}-\\d{1,2}-\\d{1,2}[T]\\d{1,2}:\\d{1,2}:\\d{1,2})(.\\d{1,10})((\\+\\d{1,2}:\\d{1,2})|(Z))"); + /** + * 数据库操作工具类 + */ + public final static cn.hutool.db.DbUtil DbUtil = new cn.hutool.db.DbUtil(); + /** + * 安全相关工具类
+ * 加密分为三种:
+ * 1、对称加密(symmetric),例如:AES、DES等
+ * 2、非对称加密(asymmetric),例如:RSA、DSA等
+ * 3、摘要加密(digest),例如:MD5、SHA-1、SHA-256、HMAC等
+ */ + public final static cn.hutool.crypto.SecureUtil SecureUtil = new cn.hutool.crypto.SecureUtil(); + /** + * 代理工具类 + */ + public final static cn.hutool.aop.ProxyUtil ProxyUtil = new cn.hutool.aop.ProxyUtil(); + /** + * 敏感词工具类 + */ + public final static cn.hutool.dfa.SensitiveUtil SensitiveUtil = new cn.hutool.dfa.SensitiveUtil(); + + /** + * json 工具类 + */ + public static class JSONUtil extends cn.hutool.json.JSONUtil { + } + + /** + * 数据库元数据信息工具类 + * + *

+ * 需要注意的是,此工具类在某些数据库(比如Oracle)下无效,此时需要手动在数据库配置中增加: + * + *

+	 *  remarks = true
+	 *  useInformationSchema = true
+	 * 
+ * + * @author looly + */ + public static class MetaUtil extends cn.hutool.db.meta.MetaUtil { + } + + /** + * 异常工具类 + * + * @author Looly + */ + public static class ExceptionUtil extends cn.hutool.core.exceptions.ExceptionUtil { + } + + /** + * 正则相关工具类
+ * 常用正则请见 {@link cn.hutool.core.lang.Validator} + * + * @author xiaoleilu + */ + public static class ReUtil extends cn.hutool.core.util.ReUtil { + } + + /** + * SOAP相关工具类 + * + * @author looly + * @since 4.5.7 + */ + public static class SoapUtil extends cn.hutool.http.webservice.SoapUtil { + } + + /** + * 脚本工具类 + * + * @author Looly + */ + public static class ScriptUtil extends cn.hutool.script.ScriptUtil { + } + + /** + * 提供Unicode字符串和普通字符串之间的转换 + * + * @author 兜兜毛毛, looly + * @since 4.0.0 + */ + public static class UnicodeUtil extends cn.hutool.core.text.UnicodeUtil { + } + + /** + * 时间工具类 + * + * @author xiaoleilu + */ + public static class DateUtil extends cn.hutool.core.date.DateUtil { + /** + * 为了处理 2021-05-21T12:06:29.272986526+08:00 格式时间 + * + * @param utcString + * @return + */ + public static DateTime parseUTC(String utcString) { + if (StrUtil.isBlank(utcString)) { + return null; + } + if (utcString.length() > 29) { + Matcher matcher = REGEX_SPECIAL_DATE.matcher(utcString); + if (matcher.matches()) { + utcString = T.StrUtil.concat(true, matcher.group(1), matcher.group(3)); + } + } + return cn.hutool.core.date.DateUtil.parseUTC(utcString); + } + + } + + /** + * EC密钥参数相关工具类封装 + * + * @author looly + * @since 5.4.3 + */ + public static class ECKeyUtil extends cn.hutool.crypto.ECKeyUtil { + } + + /** + * 枚举工具类 + * + * @author looly + * @since 3.3.0 + */ + public static class EnumUtil extends cn.hutool.core.util.EnumUtil { + } + + /** + * 数学相关方法工具类
+ * 此工具类与{@link cn.hutool.core.util.NumberUtil}属于一类工具,NumberUtil偏向于简单数学计算的封装,MathUtil偏向复杂数学计算 + * + * @author looly + * @since 4.0.7 + */ + public static class MathUtil extends cn.hutool.core.math.MathUtil { + } + + /** + * Sax方式读取Excel相关工具类 + * + * @author looly + */ + public static class ExcelSaxUtil extends cn.hutool.poi.excel.sax.ExcelSaxUtil { + } + + /** + * 数字工具类
+ * 对于精确值计算应该使用 {@link BigDecimal}
+ * JDK7中BigDecimal(double val)构造方法的结果有一定的不可预知性,例如: + * + *
+	 * new BigDecimal(0.1)
+	 * 
+ *

+ * 表示的不是0.1而是0.1000000000000000055511151231257827021181583404541015625 + * + *

+ * 这是因为0.1无法准确的表示为double。因此应该使用new BigDecimal(String)。 + *

+ * 相关介绍: + *
    + *
  • http://www.oschina.net/code/snippet_563112_25237
  • + *
  • https://github.com/venusdrogon/feilong-core/wiki/one-jdk7-bug-thinking
  • + *
+ * + * @author Looly + */ + public static class NumberUtil extends cn.hutool.core.util.NumberUtil { + } + + /** + * Jsch工具类
+ * Jsch是Java Secure Channel的缩写。JSch是一个SSH2的纯Java实现。
+ * 它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等。
+ * + * @author Looly + * @since 4.0.0 + */ + public static class JschUtil extends cn.hutool.extra.ssh.JschUtil { + } + + /** + * 线程池工具 + * + * @author luxiaolei + */ + public static class ThreadUtil extends cn.hutool.core.thread.ThreadUtil { + } + + /** + * Oshi库封装的工具类,通过此工具类,可获取系统、硬件相关信息 + * + *
+	 * 1、系统信息
+	 * 2、硬件信息
+	 * 
+ * + * 相关内容见:https://github.com/oshi/oshi + * + * @author Looly + * @since 4.6.4 + */ + public static class OshiUtil extends cn.hutool.system.oshi.OshiUtil { + } + + /** + * URL(Uniform Resource Locator)统一资源定位符相关工具类 + * + *

+ * 统一资源定位符,描述了一台特定服务器上某资源的特定位置。 + *

+ * URL组成: + * + *
+	 *   协议://主机名[:端口]/ 路径/[:参数] [?查询]#Fragment
+	 *   protocol :// hostname[:port] / path / [:parameters][?query]#fragment
+	 * 
+ * + * @author xiaoleilu + */ + public static class URLUtil extends cn.hutool.core.util.URLUtil { + } + + /** + * ID生成器工具类,此工具类中主要封装: + * + *
+	 * 1. 唯一性ID生成器:UUID、ObjectId(MongoDB)、Snowflake
+	 * 
+ * + *

+ * ID相关文章见:http://calvin1978.blogcn.com/articles/uuid.html + * + * @author looly + * @since 4.1.13 + */ + public static class IdUtil extends cn.hutool.core.util.IdUtil { + } + + /** + * Http请求工具类 + * + * @author xiaoleilu + */ + public static class HttpUtil extends cn.hutool.http.HttpUtil { + } + + /** + * 桌面相关工具(平台相关)
+ * Desktop 类允许 Java 应用程序启动已在本机桌面上注册的关联应用程序,以处理 URI 或文件。 + * + * @author looly + * @since 4.5.7 + */ + public static class DesktopUtil extends cn.hutool.core.swing.DesktopUtil { + } + + /** + * 图片处理工具类:
+ * 功能:缩放图像、切割图像、旋转、图像类型转换、彩色转黑白、文字水印、图片水印等
+ * 参考:http://blog.csdn.net/zhangzhikaixinya/article/details/8459400 + * + * @author Looly + */ + public static class ImgUtil extends cn.hutool.core.img.ImgUtil { + } + + /** + * 分页工具类 + * + * @author xiaoleilu + */ + public static class PageUtil extends cn.hutool.core.util.PageUtil { + public static OrderItem decodeOrderByStr(String orderBy) { + if (cn.hutool.core.util.StrUtil.isBlank(orderBy)) { + return null; + } + if (orderBy.startsWith("-")) { + return OrderItem.desc(orderBy.substring(1)); + } + return OrderItem.asc(orderBy); + } + } + + /** + * HTML工具类 + * + *

+ * 比如我们在使用爬虫爬取HTML页面后,需要对返回页面的HTML内容做一定处理,
+ * 比如去掉指定标签(例如广告栏等)、去除JS、去掉样式等等,这些操作都可以使用此工具类完成。 + * + * @author xiaoleilu + * + */ + public static class HtmlUtil extends cn.hutool.http.HtmlUtil { + } + + /** + * PEM(Privacy Enhanced Mail)格式相关工具类。(基于Bouncy Castle) + * + *

+ * PEM一般为文本格式,以 -----BEGIN... 开头,以 -----END... 结尾,中间的内容是 BASE64 编码。 + *

+ * 这种格式可以保存证书和私钥,有时我们也把PEM格式的私钥的后缀改为 .key 以区别证书与私钥。 + * + * @author looly + * @since 5.1.6 + */ + public static class PemUtil extends cn.hutool.crypto.PemUtil { + } + + /** + * 分词工具类 + * + * @author looly + * @since 4.3.3 + */ + public static class TokenizerUtil extends cn.hutool.extra.tokenizer.TokenizerUtil { + } + + /** + * ExcelExtractor工具封装 + * + * @author looly + * @since 5.4.4 + */ + public static class ExcelExtractorUtil extends cn.hutool.poi.excel.ExcelExtractorUtil { + } + + /** + * 邮件工具类,基于javax.mail封装 + * + * @author looly + * @since 3.1.2 + */ + public static class MailUtil extends cn.hutool.extra.mail.MailUtil { + } + + /** + * NIO中Path对象操作封装 + * + * @author looly + * @since 5.4.1 + */ + public static class PathUtil extends cn.hutool.core.io.file.PathUtil { + } + + /** + * 邮件内部工具类 + * + * @author looly + * @since 3.2.3 + */ + public static class InternalMailUtil extends cn.hutool.extra.mail.InternalMailUtil { + } + + /** + * 定时任务工具类
+ * 此工具持有一个全局{@link cn.hutool.cron.Scheduler},所有定时任务在同一个调度器中执行
+ * {@link #setMatchSecond(boolean)} + * 方法用于定义是否使用秒匹配模式,如果为true,则定时任务表达式中的第一位为秒,否则为分,默认是分 + * + * @author xiaoleilu + * + */ + public static class CronUtil extends cn.hutool.cron.CronUtil { + } + + /** + * Java的System类封装工具类。
+ * 参考:http://blog.csdn.net/zhongweijian/article/details/7619383 + * + * @author Looly + */ + public static class SystemUtil extends cn.hutool.system.SystemUtil { + } + + /** + * 压缩工具类
+ * 基于commons-compress的压缩解压封装 + * + * @author looly + * @since 5.5.0 + */ + public static class CompressUtil extends cn.hutool.extra.compress.CompressUtil { + } + + /** + * {@link ClassLoader}工具类 + * + * @author Looly + * @since 3.0.9 + */ + public static class ClassLoaderUtil extends cn.hutool.core.util.ClassLoaderUtil { + } + + /** + * {@link Spliterator}相关工具类 + * + * @author looly + * @since 5.4.3 + */ + public static class SpliteratorUtil extends cn.hutool.core.collection.SpliteratorUtil { + } + + /** + * 监听工具类
+ * 主要负责文件监听器的快捷创建 + * + * @author Looly + * @since 3.1.0 + */ + public static class WatchUtil extends cn.hutool.core.io.watch.WatchUtil { + } + + /** + * Excel样式工具类 + * + * @author looly + * @since 4.0.0 + */ + public static class StyleUtil extends cn.hutool.poi.excel.style.StyleUtil { + } + + /** + * 系统剪贴板工具类 + * + * @author looly + * @since 3.2.0 + */ + public static class ClipboardUtil extends cn.hutool.core.swing.clipboard.ClipboardUtil { + } + + /** + * 文件类型判断工具类 + * + *

+ * 此工具根据文件的前几位bytes猜测文件类型,对于文本、zip判断不准确,对于视频、图片类型判断准确 + *

+ * + *

+ * 需要注意的是,xlsx、docx等Office2007格式,全部识别为zip,因为新版采用了OpenXML格式,这些格式本质上是XML文件打包为zip + *

+ * + * @author Looly + */ + public static class FileTypeUtil extends cn.hutool.core.io.FileTypeUtil { + } + + /** + * 引用工具类,主要针对{@link Reference} 工具化封装
+ * 主要封装包括: + * + *
+	 * 1. {@link SoftReference} 软引用,在GC报告内存不足时会被GC回收
+	 * 2. {@link WeakReference} 弱引用,在GC时发现弱引用会回收其对象
+	 * 3. {@link PhantomReference} 虚引用,在GC时发现虚引用对象,会将{@link PhantomReference}插入{@link ReferenceQueue}。 此时对象未被真正回收,要等到{@link ReferenceQueue}被真正处理后才会被回收。
+	 * 
+ * + * @author looly + * @since 3.1.2 + */ + public static class ReferenceUtil extends cn.hutool.core.util.ReferenceUtil { + } + + /** + * 布隆过滤器工具 + * + * @author looly + * @since 4.1.5 + */ + public static class BloomFilterUtil extends cn.hutool.bloomfilter.BloomFilterUtil { + } + + /** + * 修饰符工具类 + * + * @author looly + * @since 4.0.5 + */ + public static class ModifierUtil extends cn.hutool.core.util.ModifierUtil { + } + + /** + * 集合的stream操作封装 + * + * @author 528910437@QQ.COM + * @since 5.5.2 + */ + public static class CollStreamUtil extends cn.hutool.core.collection.CollStreamUtil { + } + + /** + * 摘要算法工具类 + * + * @author Looly + */ + public static class DigestUtil extends cn.hutool.crypto.digest.DigestUtil { + } + + /** + * 对象工具类,包括判空、克隆、序列化等操作 + * + * @author Looly + */ + public static class ObjectUtil extends cn.hutool.core.util.ObjectUtil { + } + + /** + * Hash算法大全
+ * 推荐使用FNV1算法 + * + * @author Goodzzp, Looly + */ + public static class HashUtil extends cn.hutool.core.util.HashUtil { + } + + /** + * Bouncy Castle相关工具类封装 + * + * @author looly + * @since 4.5.0 + */ + public static class BCUtil extends cn.hutool.crypto.BCUtil { + } + + /** + * 针对 {@link Type} 的工具类封装
+ * 最主要功能包括: + * + *
+	 * 1. 获取方法的参数和返回值类型(包括Type和Class)
+	 * 2. 获取泛型参数类型(包括对象的泛型参数或集合元素的泛型类型)
+	 * 
+ * + * @author Looly + * @since 3.0.8 + */ + public static class TypeUtil extends cn.hutool.core.util.TypeUtil { + } + + /** + * Excel文件工具类 + * + * @author looly + * @since 4.2.1 + */ + public static class ExcelFileUtil extends cn.hutool.poi.excel.ExcelFileUtil { + } + + /** + * NIO工具类 + * + * @since 5.4.0 + */ + public static class NioUtil extends cn.hutool.socket.nio.NioUtil { + } + + /** + * 类工具类
+ * + * @author xiaoleilu + */ + public static class ClassUtil extends cn.hutool.core.util.ClassUtil { + } + + /** + * 针对{@link Calendar} 对象封装工具类 + * + * @author looly + * @since 5.3.0 + */ + public static class CalendarUtil extends cn.hutool.core.date.CalendarUtil { + } + + /** + * 网络相关工具 + * + */ + public static class NetUtil extends cn.hutool.core.net.NetUtil { + + public static String getLocalIPBySocket(String host, Integer port) { + Socket socket = null; + try { + socket = new Socket(host, port); + return socket.getLocalAddress().getHostAddress(); + } catch (Exception e) { + return T.NetUtil.getLocalhostStr(); + } finally { + T.IoUtil.close(socket); + } + } + + } + + /** + * 源码编译工具类,主要封装{@link JavaCompiler} 相关功能 + * + * @author looly + * @since 5.5.2 + */ + public static class CompilerUtil extends cn.hutool.core.compiler.CompilerUtil { + } + + /** + * 锁相关工具 + * + * @author looly + * @since 5.2.5 + */ + public static class LockUtil extends cn.hutool.core.thread.lock.LockUtil { + } + + /** + * 文件名相关工具类 + * + * @author looly + * @since 5.4.1 + */ + public static class FileNameUtil extends cn.hutool.core.io.file.FileNameUtil { + } + + /** + * SM国密算法工具类
+ * 此工具类依赖org.bouncycastle:bcpkix-jdk15on + * + * @author looly + * @since 4.3.2 + */ + public static class SmUtil extends cn.hutool.crypto.SmUtil { + } + + /** + * Props工具类
+ * 提供静态方法获取配置文件 + * + * @author looly + * @since 5.1.3 + */ + public static class PropsUtil extends cn.hutool.setting.dialect.PropsUtil { + } + + /** + * XML工具类
+ * 此工具使用w3c dom工具,不需要依赖第三方包。
+ * 工具类封装了XML文档的创建、读取、写出和部分XML操作 + * + * @author xiaoleilu + */ + public static class XmlUtil extends cn.hutool.core.util.XmlUtil { + } + + /** + * Velocity模板引擎工具类
+ * 使用前必须初始化工具类 + * + * @author xiaoleilu + * @deprecated 使用TemplateUtil替代 + */ + public static class VelocityEngine extends cn.hutool.extra.template.engine.velocity.VelocityEngine { + } + + /** + * {@link ByteBuffer} 工具类
+ * 此工具来自于 t-io 项目以及其它项目的相关部分收集
+ * ByteBuffer的相关介绍见:https://www.cnblogs.com/ruber/p/6857159.html + * + * @author tanyaowu, looly + * @since 4.0.0 + * + */ + public static class BufferUtil extends cn.hutool.core.io.BufferUtil { + } + + /** + * 表达式引擎工具类 + * + * @author looly + * @since 5.5.0 + */ + public static class ExpressionUtil extends cn.hutool.extra.expression.ExpressionUtil { + } + + /** + * 驱动相关工具类,包括自动获取驱动类名 + * + * @author looly + * @since 4.0.10 + */ + public static class DriverUtil extends cn.hutool.db.dialect.DriverUtil { + } + + /** + * IO工具类
+ * IO工具类只是辅助流的读写,并不负责关闭流。原因是流可能被多次读写,读写关闭后容易造成问题。 + * + * @author xiaoleilu + */ + public static class IoUtil extends cn.hutool.core.io.IoUtil { + } + + /** + * JDK8+中的{@link LocalDateTime} 工具类封装 + * + * @author looly + * @since 5.3.9 + */ + public static class LocalDateTimeUtil extends cn.hutool.core.date.LocalDateTimeUtil { + } + + /** + * 规范化对象生成工具 + * + * @author looly + * @since 5.4.3 + */ + public static class InternUtil extends cn.hutool.core.lang.intern.InternUtil { + } + + /** + * 压缩工具类 + * + * @author Looly + */ + public static class ZipUtil extends cn.hutool.core.util.ZipUtil { + } + + /** + * 十六进制(简写为hex或下标16)在数学中是一种逢16进1的进位制,一般用数字0到9和字母A到F表示(其中:A~F即10~15)。
+ * 例如十进制数57,在二进制写作111001,在16进制写作39。
+ * 像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20
+ *

+ * 参考:https://my.oschina.net/xinxingegeya/blog/287476 + * + * @author Looly + */ + public static class HexUtil extends cn.hutool.core.util.HexUtil { + } + + /** + * 数据大小工具类 + * + * @author looly + * @since 5.3.10 + */ + public static class DataSizeUtil extends cn.hutool.core.io.unit.DataSizeUtil { + } + + /** + * User-Agent工具类 + * + * @author looly + * + */ + public static class UserAgentUtil extends cn.hutool.http.useragent.UserAgentUtil { + } + + /** + * 统一社会信用代码工具类 + * + *

+	 * 第一部分:登记管理部门代码1位 (数字或大写英文字母)
+	 * 第二部分:机构类别代码1位 (数字或大写英文字母)
+	 * 第三部分:登记管理机关行政区划码6位 (数字)
+	 * 第四部分:主体标识码(组织机构代码)9位 (数字或大写英文字母)
+	 * 第五部分:校验码1位 (数字或大写英文字母)
+	 * 
+ * + * @author looly + * @since 5.2.4 + */ + public static class CreditCodeUtil extends cn.hutool.core.util.CreditCodeUtil { + } + + /** + * {@link Temporal} 工具类封装 + * + * @author looly + * @since 5.4.5 + */ + public static class TemporalUtil extends cn.hutool.core.date.TemporalUtil { + } + + /** + * Setting工具类
+ * 提供静态方法获取配置文件 + * + * @author looly + */ + public static class SettingUtil extends cn.hutool.setting.SettingUtil { + } + + /** + * Statement和PreparedStatement工具类 + * + * @author looly + * @since 4.0.10 + */ + public static class StatementUtil extends cn.hutool.db.StatementUtil { + } + + /** + * 基于https://github.com/vdurmont/emoji-java的Emoji表情工具类 + *

+ * emoji-java文档以及别名列表见:https://github.com/vdurmont/emoji-java + * + * @author looly + * @since 4.2.1 + */ + public static class EmojiUtil extends cn.hutool.extra.emoji.EmojiUtil { + } + + /** + * Bean工具类 + * + *

+ * 把一个拥有对属性进行set和get方法的类,我们就可以称之为JavaBean。 + *

+ * + * @author Looly + * @since 3.1.2 + */ + public static class BeanUtil extends cn.hutool.core.bean.BeanUtil { + } + + /** + * Servlet相关工具类封装 + * + * @author looly + * @since 3.2.0 + */ + public static class ServletUtil extends cn.hutool.extra.servlet.ServletUtil { + } + + /** + * java bean 校验工具类,此工具类基于validation-api(jakarta.validation-api)封装 + * + *

+ * 在实际使用中,用户需引入validation-api的实现,如:hibernate-validator + *

+ *

+ * 注意:hibernate-validator还依赖了javax.el,需自行引入。 + *

+ * + * @author chengqiang + * @since 5.5.0 + */ + public static class ValidationUtil extends cn.hutool.extra.validation.ValidationUtil { + } + + /** + * Excel工作簿相关工具类 + * + * @author looly + * @since 4.0.7 + * + */ + public static class WorkbookUtil extends cn.hutool.poi.excel.WorkbookUtil { + } + + /** + * 拼音工具类,封装了TinyPinyin、JPinyin、Pinyin4j,通过SPI自动识别。 + * + * @author looly + */ + public static class PinyinUtil extends cn.hutool.extra.pinyin.PinyinUtil { + } + + /** + * 调用者。可以通过此类的方法获取调用者、多级调用者以及判断是否被调用 + * + * @author Looly + * @since 4.1.6 + */ + public static class CallerUtil extends cn.hutool.core.lang.caller.CallerUtil { + } + + /** + * 定时任务表达式工具类 + * + * @author looly + * + */ + public static class CronPatternUtil extends cn.hutool.cron.pattern.CronPatternUtil { + } + + /** + * 系统运行时工具类,用于执行系统命令的工具 + * + * @author Looly + * @since 3.1.1 + */ + public static class RuntimeUtil extends cn.hutool.core.util.RuntimeUtil { + } + + /** + * {@link TemporalAccessor} 工具类封装 + * + * @author looly + * @since 5.3.9 + */ + public static class TemporalAccessorUtil extends cn.hutool.core.date.TemporalAccessorUtil { + } + + /** + * 图形验证码工具 + * + * @author looly + * @since 3.1.2 + */ + public static class CaptchaUtil extends cn.hutool.captcha.CaptchaUtil { + } + + /** + * 反射工具类 + * + * @author Looly + * @since 3.0.9 + */ + public static class ReflectUtil extends cn.hutool.core.util.ReflectUtil { + } + + /** + * IPV4地址工具类 + * + *

+ * pr自:https://gitee.com/loolly/hutool/pulls/161 + *

+ * + * @author ZhuKun + * @since 5.4.1 + */ + public static class Ipv4Util extends cn.hutool.core.net.Ipv4Util { + } + + /** + * {@link Robot} 封装工具类,提供截屏等工具 + * + * @author looly + * @since 4.1.14 + */ + public static class RobotUtil extends cn.hutool.core.swing.RobotUtil { + } + + /** + * {@link Graphics}相关工具类 + * + * @author looly + * @since 4.5.2 + */ + public static class GraphicsUtil extends cn.hutool.core.img.GraphicsUtil { + } + + /** + * Cglib工具类 + * + * @author looly + * @since 5.4.1 + */ + public static class CglibUtil extends cn.hutool.extra.cglib.CglibUtil { + } + + /** + * 转义和反转义工具类Escape / Unescape
+ * escape采用ISO Latin字符集对指定的字符串进行编码。
+ * 所有的空格符、标点符号、特殊字符以及其他非ASCII字符都将被转化成%xx格式的字符编码(xx等于该字符在字符集表里面的编码的16进制数字)。 + * + * @author xiaoleilu + */ + public static class EscapeUtil extends cn.hutool.core.util.EscapeUtil { + } + + /** + * 诊断工具类 + * + * @author looly + * @since 5.5.2 + */ + public static class DiagnosticUtil extends cn.hutool.core.compiler.DiagnosticUtil { + } + + /** + * Excel中的行封装工具类 + * + * @author looly + * @since 4.0.7 + */ + public static class RowUtil extends cn.hutool.poi.excel.RowUtil { + } + + /** + * 密钥工具类 + * + *

+ * 包括: + * + *

+	 * 1、生成密钥(单密钥、密钥对)
+	 * 2、读取密钥文件
+	 * 
+ * + * @author looly, Gsealy + * @since 4.4.1 + */ + public static class KeyUtil extends cn.hutool.crypto.KeyUtil { + } + + /** + * 基于Zxing的二维码工具类 + * + * @author looly + * @since 4.0.2 + */ + public static class QrCodeUtil extends cn.hutool.extra.qrcode.QrCodeUtil { + } + + /** + * SQL相关工具类,包括相关SQL语句拼接等 + * + * @author looly + * @since 4.0.10 + */ + public static class SqlUtil extends cn.hutool.db.sql.SqlUtil { + } + + /** + * Excel工具类,不建议直接使用index直接操作sheet,在wps/excel中sheet显示顺序与index无关,还有隐藏sheet + * + * @author Looly + * + */ + public static class ExcelUtil extends cn.hutool.poi.excel.ExcelUtil { + } + + /** + * 缓存工具类 + * + * @author Looly + * @since 3.0.1 + */ + public static class CacheUtil extends cn.hutool.cache.CacheUtil { + } + + /** + * Word工具类 + * + * @author Looly + * @since 4.5.16 + */ + public static class WordUtil extends cn.hutool.poi.word.WordUtil { + } + + /** + * 注解工具类
+ * 快速获取注解对象、注解值等工具封装 + * + * @author looly + * @since 4.0.9 + */ + public static class AnnotationUtil extends cn.hutool.core.annotation.AnnotationUtil { + } + + /** + * Excel图片工具类 + * + * @author looly + * @since 4.0.7 + */ + public static class ExcelPicUtil extends cn.hutool.poi.excel.ExcelPicUtil { + } + + /** + * 屏幕相关(当前显示设置)工具类 + * + * @author looly + * @since 4.1.14 + */ + public static class ScreenUtil extends cn.hutool.core.swing.ScreenUtil { + } + + /** + * 文件工具类 + * + * @author looly + */ + public static class FileUtil extends cn.hutool.core.io.FileUtil { + } + + /** + * Word Document工具 + * + * @author looly + * @since 4.4.1 + */ + public static class DocUtil extends cn.hutool.poi.word.DocUtil { + } + + /** + * 比较工具类 + * + * @author looly + */ + public static class CompareUtil extends cn.hutool.core.comparator.CompareUtil { + } + + /** + * Boolean类型相关工具类 + * + * @author looly + * @since 4.1.16 + */ + public static class BooleanUtil extends cn.hutool.core.util.BooleanUtil { + } + + /** + * {@link Iterable} 和 {@link Iterator} 相关工具类 + * + * @author Looly + * @since 3.1.0 + */ + public static class IterUtil extends cn.hutool.core.collection.IterUtil { + } + + /** + * 树工具类 + * + * @author liangbaikai + */ + public static class TreeUtil extends cn.hutool.core.lang.tree.TreeUtil { + } + + /** + * 身份证相关工具类
+ * see https://www.oschina.net/code/snippet_1611_2881 + * + *

+ * 本工具并没有对行政区划代码做校验,如有需求,请参阅(2018年10月): + * http://www.mca.gov.cn/article/sj/xzqh/2018/201804-12/20181011221630.html + *

+ * + * @author Looly + * @since 3.0.4 + */ + public static class IdcardUtil extends cn.hutool.core.util.IdcardUtil { + } + + /** + * 字符工具类
+ * 部分工具来自于Apache Commons系列 + * + * @author looly + * @since 4.0.1 + */ + public static class CharUtil extends cn.hutool.core.util.CharUtil { + } + + /** + * Socket相关工具类 + * + * @author looly + * @since 4.5.0 + */ + public static class SocketUtil extends cn.hutool.socket.SocketUtil { + } + + /** + * 手机号工具类 + * + * @author dahuoyzs + * @since 5.3.11 + */ + public static class PhoneUtil extends cn.hutool.core.util.PhoneUtil { + } + + /** + * 模板工具类 + * + * @author looly + * @since 4.1.0 + */ + public static class TemplateUtil extends cn.hutool.extra.template.TemplateUtil { + } + + /** + * 集合相关工具类 + *

+ * 此工具方法针对{@link Collection}及其实现类封装的工具。 + *

+ * 由于{@link Collection} 实现了{@link Iterable}接口,因此部分工具此类不提供,而是在{@link IterUtil} + * 中提供 + * + * @author xiaoleilu + * @see IterUtil + * @since 3.1.1 + */ + public static class CollUtil extends cn.hutool.core.collection.CollUtil { + } + + /** + * {@link JavaFileObject} 相关工具类封装 + * + * @author lzpeng, looly + * @since 5.5.2 + */ + public static class JavaFileObjectUtil extends cn.hutool.core.compiler.JavaFileObjectUtil { + } + + /** + * 数组工具类 + * + * @author Looly + */ + public static class ArrayUtil extends cn.hutool.core.util.ArrayUtil { + } + + /** + * SSL(Secure Sockets Layer 安全套接字协议)相关工具封装 + * + * @author looly + * @since 5.5.2 + */ + public static class SSLUtil extends cn.hutool.core.net.SSLUtil { + } + + /** + * 集合相关工具类,包括数组,是{@link CollUtil} 的别名工具类类 + * + * @author xiaoleilu + * @see CollUtil + */ + public static class CollectionUtil extends cn.hutool.core.collection.CollectionUtil { + } + + /** + * Excel表格中单元格工具类 + * + * @author looly + * @since 4.0.7 + */ + public static class CellUtil extends cn.hutool.poi.excel.cell.CellUtil { + } + + /** + * 字符集工具类 + * + * @author xiaoleilu + */ + public static class CharsetUtil extends cn.hutool.core.util.CharsetUtil { + } + + /** + * 字符串工具类 + * + * @author xiaoleilu + */ + public static class StrUtil extends cn.hutool.core.util.StrUtil { + + /** + * 将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world=》helloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @param symbol 连接符 + * @return 转换后的驼峰式命名的字符串 + */ + public static String toCamelCase(CharSequence name, char symbol) { + if (null == name) { + return null; + } + String symbolStr = symbol + ""; + String name2 = name.toString(); + if (name2.contains(symbolStr)) { + final StringBuilder sb = new StringBuilder(name2.length()); + boolean upperCase = false; + for (int i = 0; i < name2.length(); i++) { + char c = name2.charAt(i); + + if (c == symbol) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(Character.toLowerCase(c)); + } + } + return sb.toString(); + } else { + return name2; + } + } + + private static final String[] LIKE_SPECIAL_CHAR = { "%", "_" }; + + /** + * 转义 msyql like 特殊字符 + * + * @param cs + * @return + */ + public static String likeEscape(String cs) { + if (StrUtil.isBlank(cs)) { + return null; + } + cs = StrUtil.replace(cs, "\\", "\\\\"); + for (String s : LIKE_SPECIAL_CHAR) { + cs = StrUtil.replace(cs, s, "\\" + s); + } + return cs; + } + + } + + /** + * Map相关工具类 + * + * @author Looly + * @since 3.1.1 + */ + public static class MapUtil extends cn.hutool.core.map.MapUtil { + } + + /** + * SPI机制中的服务加载工具类,流程如下 + * + *

+	 *     1、创建接口,并创建实现类
+	 *     2、ClassPath/META-INF/services下创建与接口全限定类名相同的文件
+	 *     3、文件内容填写实现类的全限定类名
+	 * 
+ * + * 相关介绍见:https://www.jianshu.com/p/3a3edbcd8f24 + * + * @author looly + * @since 5.1.6 + */ + public static class ServiceLoaderUtil extends cn.hutool.core.util.ServiceLoaderUtil { + } + + /** + * Spring(Spring boot)工具封装,包括: + * + *
+	 *     1、Spring IOC容器中的bean对象获取
+	 * 
+ * + * @author loolly + * @since 5.1.0 + */ + public static class SpringUtil extends cn.hutool.extra.spring.SpringUtil { + } + + /** + * 随机工具类 + * + * @author xiaoleilu + */ + public static class RandomUtil extends cn.hutool.core.util.RandomUtil { + } + + public static class ListUtil extends cn.hutool.core.collection.ListUtil { + } + + /** + * Resource资源工具类 + * + * @author Looly + * + */ + public static class ResourceUtil extends cn.hutool.core.io.resource.ResourceUtil { + } + + /** + * AWT中字体相关工具类 + * + * @author looly + * @since 5.3.6 + */ + public static class FontUtil extends cn.hutool.core.img.FontUtil { + } + + /** + * CSV工具 + * + * @author looly + * @since 4.0.5 + */ + public static class CsvUtil extends cn.hutool.core.text.csv.CsvUtil { + } + + /** + * Word中表格相关工具 + * + * @author Looly + * @since 4.5.14 + */ + public static class TableUtil extends cn.hutool.poi.word.TableUtil { + } + + /** + * http response 工具类 + * + * @author Looly + * @since 4.5.14 + */ + public static class ResponseUtil extends net.geedge.asw.common.util.ResponseUtil { + } + + /** + * 原始类型数组工具类 + * + * @author looly + * @since 5.5.2 + */ + public static class PrimitiveArrayUtil extends cn.hutool.core.util.PrimitiveArrayUtil { + } + + public static class AesUtil { + + private static final String KEY_ALGORITHM = "AES"; + private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";// 默认的加密算法 + + public static String encrypt(String content, byte[] key) { + try { + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器 + + byte[] byteContent = content.getBytes("utf-8"); + + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器 + + byte[] result = cipher.doFinal(byteContent);// 加密 + + return cn.hutool.core.util.HexUtil.encodeHexStr(result); + } catch (Exception ex) { + ex.printStackTrace(); + } + + return null; + } + + public static String decrypt(String content, byte[] key) { + + try { + // 实例化 + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); + + // 使用密钥初始化,设置为解密模式 + cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key)); + + // 执行操作 + byte[] result = cipher.doFinal(cn.hutool.core.util.HexUtil.decodeHex(content)); + String s = new String(result, "utf-8"); + return s; + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } + + private static SecretKeySpec getSecretKey(byte[] key) { + // 返回生成指定算法密钥生成器的 KeyGenerator 对象 + KeyGenerator kg = null; + try { + kg = KeyGenerator.getInstance(KEY_ALGORITHM); + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + random.setSeed(key); + // AES 要求密钥长度为 128 + kg.init(128, random); + // 生成一个密钥 + SecretKey secretKey = kg.generateKey(); + return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥 + } catch (NoSuchAlgorithmException ex) { + ex.printStackTrace(); + } + return null; + } + + } + + public static class VerifyUtil extends net.geedge.asw.common.util.VerifyUtil { + private VerifyUtil(Object value) { + super(value); + } + } +} diff --git a/src/main/java/net/geedge/asw/common/util/VerifyUtil.java b/src/main/java/net/geedge/asw/common/util/VerifyUtil.java new file mode 100644 index 0000000..1963051 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/util/VerifyUtil.java @@ -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); + } + +} diff --git a/src/main/java/net/geedge/asw/module/file/controller/FileController.java b/src/main/java/net/geedge/asw/module/file/controller/FileController.java new file mode 100644 index 0000000..d4e9ef7 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/controller/FileController.java @@ -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 queryWrapper = new QueryWrapper(); + queryWrapper.and(T.StrUtil.isNotBlank(q), wrapper -> { + wrapper.like("name", q).or().like("remark", q).or().like("path", q); + }); + Page 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(); + } + } + +} diff --git a/src/main/java/net/geedge/asw/module/file/dao/FileContentDao.java b/src/main/java/net/geedge/asw/module/file/dao/FileContentDao.java new file mode 100644 index 0000000..842fa7f --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/dao/FileContentDao.java @@ -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 { + +} diff --git a/src/main/java/net/geedge/asw/module/file/dao/FileDao.java b/src/main/java/net/geedge/asw/module/file/dao/FileDao.java new file mode 100644 index 0000000..42bc20e --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/dao/FileDao.java @@ -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 { + +// public void saveFileContent(FileContentEntity entity); +// +// public FileContentEntity getFileContentById(String id); +// +// public boolean deleteFileContentById(List ids); +} diff --git a/src/main/java/net/geedge/asw/module/file/entity/FileContentEntity.java b/src/main/java/net/geedge/asw/module/file/entity/FileContentEntity.java new file mode 100644 index 0000000..a4973c5 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/entity/FileContentEntity.java @@ -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; +} diff --git a/src/main/java/net/geedge/asw/module/file/entity/FileEntity.java b/src/main/java/net/geedge/asw/module/file/entity/FileEntity.java new file mode 100644 index 0000000..330b9c3 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/entity/FileEntity.java @@ -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; +} diff --git a/src/main/java/net/geedge/asw/module/file/service/IFileService.java b/src/main/java/net/geedge/asw/module/file/service/IFileService.java new file mode 100644 index 0000000..1645885 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/service/IFileService.java @@ -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 { + + /** + * 保存文件信息及内容 + * + * @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; + +} diff --git a/src/main/java/net/geedge/asw/module/file/service/impl/FileServiceImpl.java b/src/main/java/net/geedge/asw/module/file/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..3805d4a --- /dev/null +++ b/src/main/java/net/geedge/asw/module/file/service/impl/FileServiceImpl.java @@ -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 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().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 = 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 list = T.ListUtil.of(ids); + this.removeByIds(list); + fileContentDao.deleteBatchIds(list); + } + + @Override + public FileContentEntity getFileContentEntityById(String id) { + return fileContentDao.selectById(id); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/controller/BaseController.java b/src/main/java/net/geedge/asw/module/sys/controller/BaseController.java new file mode 100644 index 0000000..29ec813 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/controller/BaseController.java @@ -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(); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java new file mode 100644 index 0000000..0fb8979 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysAuthController.java @@ -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 permissions = authService.userPermissions(); + return R.ok(permissions); + } +} diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java new file mode 100644 index 0000000..80c00e5 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysRoleController.java @@ -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 queryWrapper = new QueryWrapper(); + queryWrapper.like(T.StrUtil.isNotBlank(name), "name", name).in(T.StrUtil.isNotBlank(ids), "id", ids.split(",")); + Page 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().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(); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java b/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java new file mode 100644 index 0000000..328b8a5 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/controller/SysUserController.java @@ -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 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 queryWrapper = new QueryWrapper(); + // 不查询 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 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().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(); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/dao/SysConfigDao.java b/src/main/java/net/geedge/asw/module/sys/dao/SysConfigDao.java new file mode 100644 index 0000000..ee867c6 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/dao/SysConfigDao.java @@ -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{ + +} diff --git a/src/main/java/net/geedge/asw/module/sys/dao/SysRoleDao.java b/src/main/java/net/geedge/asw/module/sys/dao/SysRoleDao.java new file mode 100644 index 0000000..a0b8d82 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/dao/SysRoleDao.java @@ -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 { + + @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 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 findMenuByUserId(String userId); +} diff --git a/src/main/java/net/geedge/asw/module/sys/dao/SysRoleMenuDao.java b/src/main/java/net/geedge/asw/module/sys/dao/SysRoleMenuDao.java new file mode 100644 index 0000000..874ffc4 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/dao/SysRoleMenuDao.java @@ -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{ + +} diff --git a/src/main/java/net/geedge/asw/module/sys/dao/SysUserDao.java b/src/main/java/net/geedge/asw/module/sys/dao/SysUserDao.java new file mode 100644 index 0000000..fa2dbc4 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/dao/SysUserDao.java @@ -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{ + +} diff --git a/src/main/java/net/geedge/asw/module/sys/dao/SysUserRoleDao.java b/src/main/java/net/geedge/asw/module/sys/dao/SysUserRoleDao.java new file mode 100644 index 0000000..0de7db4 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/dao/SysUserRoleDao.java @@ -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{ + +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysConfigEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysConfigEntity.java new file mode 100644 index 0000000..e61b95e --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysConfigEntity.java @@ -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); + } +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysMenuEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysMenuEntity.java new file mode 100644 index 0000000..5be0027 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysMenuEntity.java @@ -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 children; +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysRoleEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysRoleEntity.java new file mode 100644 index 0000000..9e67029 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysRoleEntity.java @@ -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; +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysRoleMenuEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysRoleMenuEntity.java new file mode 100644 index 0000000..2c7d9b9 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysRoleMenuEntity.java @@ -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; +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java new file mode 100644 index 0000000..d9ad678 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysUserEntity.java @@ -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 roles; + private Long createTimestamp; +} diff --git a/src/main/java/net/geedge/asw/module/sys/entity/SysUserRoleEntity.java b/src/main/java/net/geedge/asw/module/sys/entity/SysUserRoleEntity.java new file mode 100644 index 0000000..a87d771 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/entity/SysUserRoleEntity.java @@ -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; +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysAuthService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysAuthService.java new file mode 100644 index 0000000..5749b79 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysAuthService.java @@ -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 userPermissions(); + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java new file mode 100644 index 0000000..a9241fe --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java @@ -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 { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysRoleMenuService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysRoleMenuService.java new file mode 100644 index 0000000..8b4c1c8 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysRoleMenuService.java @@ -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 { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysRoleService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysRoleService.java new file mode 100644 index 0000000..7e25c23 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysRoleService.java @@ -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{ + + public void saveOrUpdateRole(SysRoleEntity entity); +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysUserRoleService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysUserRoleService.java new file mode 100644 index 0000000..5fdeaf6 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysUserRoleService.java @@ -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 { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysUserService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysUserService.java new file mode 100644 index 0000000..9dfed51 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysUserService.java @@ -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{ + + public void saveOrUpdateUser(SysUserEntity entity); +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java new file mode 100644 index 0000000..5de1c45 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysAuthServiceImpl.java @@ -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().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 userPermissions() { + Map result = T.MapUtil.newHashMap(); + String userId = StpUtil.getLoginIdAsString(); + List roleList = roleDao.findRoleByUserId(userId); + result.put("roles", roleList); + // 组织 menu数据 + List menuList = roleDao.findMenuByUserId(userId); + List 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> 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 collect = menuList.stream().filter(menu -> T.StrUtil.isBlank(menu.getPid())) + .collect(Collectors.toList()); + result.put("menus", collect); + + return result; + } +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..cd7a39d --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java @@ -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 implements ISysConfigService { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleMenuServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleMenuServiceImpl.java new file mode 100644 index 0000000..7f405ac --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleMenuServiceImpl.java @@ -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 + implements ISysRoleMenuService { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..4a283a2 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysRoleServiceImpl.java @@ -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 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().eq("role_id", entity.getId())); + } + // 保存 role + this.saveOrUpdate(entity); + // 保存 role 和 menu关联关系 + List rmList = T.ListUtil.list(false); + Stream.of(entity.getMenuIds()).forEach(menuId -> { + rmList.add(SysRoleMenuEntity.builder().roleId(entity.getId()).menuId(menuId).build()); + }); + roleMenuService.saveBatch(rmList); + } + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserRoleServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserRoleServiceImpl.java new file mode 100644 index 0000000..9aba5d0 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserRoleServiceImpl.java @@ -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 + implements ISysUserRoleService { + +} diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..a89aa44 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysUserServiceImpl.java @@ -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 implements ISysUserService { + + @Autowired + private SysUserRoleServiceImpl userRoleService; + + @Override + @Transactional + public void saveOrUpdateUser(SysUserEntity entity) { + if (T.StrUtil.isNotBlank(entity.getId())) { + userRoleService.remove(new QueryWrapper().eq("user_id", entity.getId())); + } + // 保存 user + this.saveOrUpdate(entity); + // 保存 user role关系 + List urList = T.ListUtil.list(false); + Stream.of(entity.getRoleIds()).forEach(roleId -> { + urList.add(SysUserRoleEntity.builder().roleId(roleId).userId(entity.getId()).build()); + }); + userRoleService.saveBatch(urList); + } + +} diff --git a/src/main/resources/application-magic-api.yml b/src/main/resources/application-magic-api.yml new file mode 100644 index 0000000..64266ce --- /dev/null +++ b/src/main/resources/application-magic-api.yml @@ -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 # 关闭时最多等待任务执行完毕的时间 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..1300f30 --- /dev/null +++ b/src/main/resources/application.yml @@ -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 \ No newline at end of file diff --git a/src/main/resources/config/asw.properties b/src/main/resources/config/asw.properties new file mode 100644 index 0000000..d388294 --- /dev/null +++ b/src/main/resources/config/asw.properties @@ -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 \ No newline at end of file diff --git a/src/main/resources/config/logback-spring.xml b/src/main/resources/config/logback-spring.xml new file mode 100644 index 0000000..1dbdc8a --- /dev/null +++ b/src/main/resources/config/logback-spring.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + ${log.path}/dh-web.log + + + ${out.pattern} + UTF-8 + + + + ${log.path}/dh-web-%d{yyyy-MM-dd}.%i.log + ${max.file.size} + ${max.history} + ${total.size.cap} + + + + + + + ${log.path}/dh-web-error.log + + + ${out.pattern} + UTF-8 + + + + ${log.path}/dh-web-error-%d{yyyy-MM-dd}.%i.log + ${max.file.size} + ${max.history} + ${total.size.cap} + + + + error + ACCEPT + DENY + + + + + + + + + diff --git a/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql b/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql new file mode 100644 index 0000000..c46f34d --- /dev/null +++ b/src/main/resources/db/migration/V1.0.01__INIT_TABLES.sql @@ -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; \ No newline at end of file diff --git a/src/main/resources/static/banner.txt b/src/main/resources/static/banner.txt new file mode 100644 index 0000000..17eb02b --- /dev/null +++ b/src/main/resources/static/banner.txt @@ -0,0 +1,8 @@ + _____ _ _ _ __ __ _ + /\ / ____| | | | | | \ \ / / | | + / \ _ __ _ __| (___ | | _____| |_ ___| |__ \ \ /\ / /__ _ __| | _____ + / /\ \ | '_ \| '_ \\___ \| |/ / _ \ __/ __| '_ \ \ \/ \/ / _ \| '__| |/ / __| + / ____ \| |_) | |_) |___) | < __/ || (__| | | | \ /\ / (_) | | | <\__ \ + /_/ \_\ .__/| .__/_____/|_|\_\___|\__\___|_| |_| \/ \/ \___/|_| |_|\_\___/ + | | | | + |_| |_| Powered by GeedgeNetworks \ No newline at end of file diff --git a/src/main/resources/static/magic-editor.js b/src/main/resources/static/magic-editor.js new file mode 100644 index 0000000..17c6152 --- /dev/null +++ b/src/main/resources/static/magic-editor.js @@ -0,0 +1,10 @@ +var MAGIC_EDITOR_CONFIG = { + title: 'digital horizon api', + header: { + skin: true, // 屏蔽皮肤按钮 + document: true, // 屏蔽文档按钮 + repo: false, // 屏蔽gitee和github + qqGroup: false // 屏蔽加入QQ群 + } + // 其它配置参考本页中其它配置项 +} \ No newline at end of file diff --git a/src/test/java/net/geedge/asw/AppSketchWorksApplicationTests.java b/src/test/java/net/geedge/asw/AppSketchWorksApplicationTests.java new file mode 100644 index 0000000..14e16bc --- /dev/null +++ b/src/test/java/net/geedge/asw/AppSketchWorksApplicationTests.java @@ -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() { + } + +}