diff --git a/README.md b/README.md index e20246b..9ff6133 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@

- Build Status - Coverage Status - Downloads + Build Status + Build Status + Coverage Status + Downloads

## SpringBlade微服务开发平台 diff --git a/blade-core-bom/pom.xml b/blade-core-bom/pom.xml index c7c145e..b712b64 100644 --- a/blade-core-bom/pom.xml +++ b/blade-core-bom/pom.xml @@ -19,7 +19,7 @@ org.codehaus.mojo flatten-maven-plugin - ${maven-flatten.version} + ${maven.flatten.version} true oss diff --git a/blade-core-boot/pom.xml b/blade-core-boot/pom.xml index 3ad7129..8de0bc5 100644 --- a/blade-core-boot/pom.xml +++ b/blade-core-boot/pom.xml @@ -62,38 +62,27 @@ com.baomidou - mybatis-plus-boot-starter - ${mybatis.plus.version} + mybatis-plus-spring-boot3-starter org.mybatis mybatis-typehandlers-jsr310 - 1.0.2 net.sf.ehcache ehcache - 2.10.5 com.alibaba - druid-spring-boot-starter - 1.2.19 + druid-spring-boot-3-starter - mysql - mysql-connector-java - 8.0.32 + com.mysql + mysql-connector-j - - diff --git a/blade-core-boot/src/main/java/org/springblade/core/boot/ctrl/BladeController.java b/blade-core-boot/src/main/java/org/springblade/core/boot/ctrl/BladeController.java index 0d8949b..ebd1a08 100644 --- a/blade-core-boot/src/main/java/org/springblade/core/boot/ctrl/BladeController.java +++ b/blade-core-boot/src/main/java/org/springblade/core/boot/ctrl/BladeController.java @@ -23,7 +23,7 @@ import org.springblade.core.tool.api.R; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.multipart.MultipartFile; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; /** diff --git a/blade-core-boot/src/main/java/org/springblade/core/boot/logger/RequestLogAspect.java b/blade-core-boot/src/main/java/org/springblade/core/boot/logger/RequestLogAspect.java index 8b33ced..d159023 100644 --- a/blade-core-boot/src/main/java/org/springblade/core/boot/logger/RequestLogAspect.java +++ b/blade-core-boot/src/main/java/org/springblade/core/boot/logger/RequestLogAspect.java @@ -21,8 +21,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartFile; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.InputStream; import java.lang.reflect.Method; import java.util.*; diff --git a/blade-core-cloud/pom.xml b/blade-core-cloud/pom.xml index acda291..003e712 100644 --- a/blade-core-cloud/pom.xml +++ b/blade-core-cloud/pom.xml @@ -69,7 +69,6 @@ com.alibaba.nacos nacos-client - ${alibaba.nacos.version} @@ -81,7 +80,6 @@ com.alibaba fastjson - 1.2.83_noneautotype diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignSentinel.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignSentinel.java index 9ef89cf..536b5af 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignSentinel.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignSentinel.java @@ -25,7 +25,7 @@ import org.springblade.core.cloud.sentinel.BladeSentinelInvocationHandler; import org.springframework.beans.BeansException; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.cloud.openfeign.FeignContext; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationUtils; @@ -50,11 +50,10 @@ public class BladeFeignSentinel { public static final class Builder extends Feign.Builder implements ApplicationContextAware { private Contract contract = new Contract.Default(); private ApplicationContext applicationContext; - private FeignContext feignContext; + private FeignClientFactory feignContext; @Override - public Feign.Builder invocationHandlerFactory( - InvocationHandlerFactory invocationHandlerFactory) { + public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) { throw new UnsupportedOperationException(); } @@ -65,7 +64,7 @@ public class BladeFeignSentinel { } @Override - public Feign build() { + public Feign internalBuild() { super.invocationHandlerFactory(new InvocationHandlerFactory() { @SneakyThrows @Override @@ -115,13 +114,13 @@ public class BladeFeignSentinel { } }); super.contract(new SentinelContractHolder(contract)); - return super.build(); + return super.internalBuild(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; - feignContext = this.applicationContext.getBean(FeignContext.class); + feignContext = this.applicationContext.getBean(FeignClientFactory.class); } } diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeAccountGetter.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeAccountGetter.java index 115cee2..3fe694e 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeAccountGetter.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeAccountGetter.java @@ -20,7 +20,7 @@ import org.springblade.core.secure.utils.SecureUtil; import org.springblade.core.tool.utils.Charsets; import org.springblade.core.tool.utils.UrlUtil; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; /** * 用户信息获取器 diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeFeignAccountGetter.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeFeignAccountGetter.java index 0f086a4..0919ddb 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeFeignAccountGetter.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeFeignAccountGetter.java @@ -18,7 +18,7 @@ package org.springblade.core.cloud.header; import org.springframework.lang.Nullable; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; /** * Blade 用户信息获取器,用于请求头传递 diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeHttpHeadersContextHolder.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeHttpHeadersContextHolder.java index d9e323c..c03231a 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeHttpHeadersContextHolder.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/header/BladeHttpHeadersContextHolder.java @@ -23,7 +23,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.lang.Nullable; import org.springframework.util.PatternMatchUtils; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java new file mode 100644 index 0000000..b4f913e --- /dev/null +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.cloud.http; + +import org.springblade.core.cloud.props.BladeFeignHeadersProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * http 配置 + * + * @author L.cm + */ +@AutoConfiguration +@EnableConfigurationProperties({BladeHttpProperties.class, BladeFeignHeadersProperties.class}) +public class BladeHttpConfiguration { +} diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpProperties.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpProperties.java new file mode 100644 index 0000000..48d9e48 --- /dev/null +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpProperties.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.cloud.http; + +import lombok.Getter; +import lombok.Setter; +import org.springblade.core.launch.log.BladeLogLevel; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; + +import java.util.concurrent.TimeUnit; + +/** + * http 配置 + * + * @author L.cm + */ +@Getter +@Setter +@RefreshScope +@ConfigurationProperties("blade.http") +public class BladeHttpProperties { + /** + * 最大连接数,默认:200 + */ + private int maxConnections = 200; + /** + * 连接存活时间,默认:900L + */ + private long timeToLive = 900L; + /** + * 连接池存活时间单位,默认:秒 + */ + private TimeUnit timeUnit = TimeUnit.SECONDS; + /** + * 链接超时,默认:2000毫秒 + */ + private int connectionTimeout = 2000; + /** + * 是否支持重定向,默认:true + */ + private boolean followRedirects = true; + /** + * 关闭证书校验 + */ + private boolean disableSslValidation = true; + /** + * 日志级别 + */ + private BladeLogLevel level = BladeLogLevel.NONE; +} diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/HttpLoggingInterceptor.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/HttpLoggingInterceptor.java index 67eeb8a..5008593 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/HttpLoggingInterceptor.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/HttpLoggingInterceptor.java @@ -17,10 +17,10 @@ package org.springblade.core.cloud.http; import okhttp3.*; import okhttp3.internal.http.HttpHeaders; -import okhttp3.internal.platform.Platform; import okio.Buffer; import okio.BufferedSource; import okio.GzipSource; +import org.springblade.core.launch.log.BladeLogLevel; import java.io.EOFException; import java.io.IOException; @@ -29,8 +29,6 @@ import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.concurrent.TimeUnit; -import static okhttp3.internal.platform.Platform.INFO; - /** * An OkHttp interceptor which logs request and response information. Can be applied as an * {@linkplain OkHttpClient#interceptors() application interceptor} or as a {@linkplain @@ -42,121 +40,46 @@ import static okhttp3.internal.platform.Platform.INFO; */ public final class HttpLoggingInterceptor implements Interceptor { private static final Charset UTF8 = StandardCharsets.UTF_8; - - public enum Level { - /** - * No logs. - */ - NONE, - /** - * Logs request and response lines. - * - *

Example: - *

{@code
-		 * --> POST /greeting http/1.1 (3-byte body)
-		 *
-		 * <-- 200 OK (22ms, 6-byte body)
-		 * }
- */ - BASIC, - /** - * Logs request and response lines and their respective headers. - * - *

Example: - *

{@code
-		 * --> POST /greeting http/1.1
-		 * Host: example.com
-		 * Content-Type: plain/text
-		 * Content-Length: 3
-		 * --> END POST
-		 *
-		 * <-- 200 OK (22ms)
-		 * Content-Type: plain/text
-		 * Content-Length: 6
-		 * <-- END HTTP
-		 * }
- */ - HEADERS, - /** - * Logs request and response lines and their respective headers and bodies (if present). - * - *

Example: - *

{@code
-		 * --> POST /greeting http/1.1
-		 * Host: example.com
-		 * Content-Type: plain/text
-		 * Content-Length: 3
-		 *
-		 * Hi?
-		 * --> END POST
-		 *
-		 * <-- 200 OK (22ms)
-		 * Content-Type: plain/text
-		 * Content-Length: 6
-		 *
-		 * Hello!
-		 * <-- END HTTP
-		 * }
- */ - BODY - } + private final Logger logger; + private volatile BladeLogLevel level = BladeLogLevel.NONE; public interface Logger { /** * log - * * @param message message */ void log(String message); - - /** - * A {@link Logger} defaults output appropriate for the current platform. - */ - Logger DEFAULT = message -> Platform.get().log(message, INFO, null); - } - - public HttpLoggingInterceptor() { - this(Logger.DEFAULT); } public HttpLoggingInterceptor(Logger logger) { this.logger = logger; } - private final Logger logger; - - private volatile Level level = Level.NONE; - /** * Change the level at which this interceptor logs. - * * @param level log Level * @return HttpLoggingInterceptor */ - public HttpLoggingInterceptor setLevel(Level level) { - Objects.requireNonNull(level, "level == null. Use Level.NONE instead."); - this.level = level; + public HttpLoggingInterceptor setLevel(BladeLogLevel level) { + this.level = Objects.requireNonNull(level, "level == null. Use Level.NONE instead."); return this; } - public Level getLevel() { + public BladeLogLevel getLevel() { return level; } - private String gzip = "gzip"; - private String contentEncoding = "Content-Encoding"; - @Override public Response intercept(Chain chain) throws IOException { - Level level = this.level; + BladeLogLevel level = this.level; Request request = chain.request(); - if (level == Level.NONE) { + if (level == BladeLogLevel.NONE) { return chain.proceed(request); } - boolean logBody = level == Level.BODY; - boolean logHeaders = logBody || level == Level.HEADERS; + boolean logBody = level == BladeLogLevel.BODY; + boolean logHeaders = logBody || level == BladeLogLevel.HEADERS; RequestBody requestBody = request.body(); boolean hasRequestBody = requestBody != null; @@ -239,7 +162,8 @@ public final class HttpLoggingInterceptor implements Interceptor { if (logHeaders) { Headers headers = response.headers(); - for (int i = 0, count = headers.size(); i < count; i++) { + int count = headers.size(); + for (int i = 0; i < count; i++) { logger.log(headers.name(i) + ": " + headers.value(i)); } @@ -251,10 +175,10 @@ public final class HttpLoggingInterceptor implements Interceptor { BufferedSource source = responseBody.source(); // Buffer the entire body. source.request(Long.MAX_VALUE); - Buffer buffer = source.buffer(); + Buffer buffer = source.getBuffer(); Long gzippedLength = null; - if (gzip.equalsIgnoreCase(headers.get(contentEncoding))) { + if ("gzip".equalsIgnoreCase(headers.get("Content-Encoding"))) { gzippedLength = buffer.size(); GzipSource gzippedResponseBody = null; try { @@ -301,14 +225,12 @@ public final class HttpLoggingInterceptor implements Interceptor { * Returns true if the body in question probably contains human readable text. Uses a small sample * of code points to detect unicode control characters commonly used in binary file signatures. */ - private static int plainCnt = 16; - private static boolean isPlaintext(Buffer buffer) { try { Buffer prefix = new Buffer(); long byteCount = buffer.size() < 64 ? buffer.size() : 64; buffer.copyTo(prefix, 0, byteCount); - for (int i = 0; i < plainCnt; i++) { + for (int i = 0; i < 16; i++) { if (prefix.exhausted()) { break; } diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java index 140343a..6dd2ede 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java @@ -17,29 +17,38 @@ package org.springblade.core.cloud.http; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; import org.springblade.core.cloud.header.BladeFeignAccountGetter; +import org.springblade.core.cloud.http.client.OkHttp3ClientHttpRequestFactory; import org.springblade.core.cloud.props.BladeFeignHeadersProperties; +import org.springblade.core.tool.ssl.DisableValidationTrustManager; +import org.springblade.core.tool.ssl.TrustAllHostNames; import org.springblade.core.tool.utils.Charsets; +import org.springblade.core.tool.utils.Holder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory; -import org.springframework.cloud.commons.httpclient.OkHttpClientFactory; -import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Profile; -import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.web.client.RestTemplate; -import java.util.Collections; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.concurrent.TimeUnit; @@ -48,84 +57,53 @@ import java.util.concurrent.TimeUnit; * * @author L.cm */ +@Slf4j +@RequiredArgsConstructor @AutoConfiguration -@AllArgsConstructor -@ConditionalOnClass(okhttp3.OkHttpClient.class) -@EnableConfigurationProperties(BladeFeignHeadersProperties.class) +@ConditionalOnClass(OkHttpClient.class) +@ConditionalOnProperty(value = "blade.http.enabled", matchIfMissing = true) public class RestTemplateConfiguration { - private final ObjectMapper objectMapper; + private final BladeHttpProperties properties; /** - * dev, test 环境打印出BODY + * okhttp3 请求日志拦截器 + * * @return HttpLoggingInterceptor */ - @Bean("httpLoggingInterceptor") - @Profile({"dev", "test"}) - public HttpLoggingInterceptor testLoggingInterceptor() { + @Bean + public HttpLoggingInterceptor loggingInterceptor() { HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new OkHttpSlf4jLogger()); - interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); - return interceptor; - } - - /** - * ontest 环境 打印 请求头 - * @return HttpLoggingInterceptor - */ - @Bean("httpLoggingInterceptor") - @Profile("ontest") - public HttpLoggingInterceptor onTestLoggingInterceptor() { - HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new OkHttpSlf4jLogger()); - interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); - return interceptor; - } - - /** - * prod 环境只打印请求url - * @return HttpLoggingInterceptor - */ - @Bean("httpLoggingInterceptor") - @Profile("prod") - public HttpLoggingInterceptor prodLoggingInterceptor() { - HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new OkHttpSlf4jLogger()); - interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC); + interceptor.setLevel(properties.getLevel()); return interceptor; } /** * okhttp3 链接池配置 - * @param connectionPoolFactory 链接池配置 - * @param httpClientProperties httpClient配置 + * * @return okhttp3.ConnectionPool */ @Bean - @ConditionalOnMissingBean(okhttp3.ConnectionPool.class) - public okhttp3.ConnectionPool httpClientConnectionPool( - FeignHttpClientProperties httpClientProperties, - OkHttpClientConnectionPoolFactory connectionPoolFactory) { - Integer maxTotalConnections = httpClientProperties.getMaxConnections(); - Long timeToLive = httpClientProperties.getTimeToLive(); - TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); - return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit); + @ConditionalOnMissingBean + public ConnectionPool httpClientConnectionPool() { + int maxTotalConnections = properties.getMaxConnections(); + long timeToLive = properties.getTimeToLive(); + TimeUnit ttlUnit = properties.getTimeUnit(); + return new ConnectionPool(maxTotalConnections, timeToLive, ttlUnit); } /** * 配置OkHttpClient - * @param httpClientFactory httpClient 工厂 + * * @param connectionPool 链接池配置 - * @param httpClientProperties httpClient配置 - * @param interceptor 拦截器 + * @param interceptor 拦截器 * @return OkHttpClient */ @Bean - @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) - public okhttp3.OkHttpClient httpClient( - OkHttpClientFactory httpClientFactory, - okhttp3.ConnectionPool connectionPool, - FeignHttpClientProperties httpClientProperties, - HttpLoggingInterceptor interceptor) { - Boolean followRedirects = httpClientProperties.isFollowRedirects(); - Integer connectTimeout = httpClientProperties.getConnectionTimeout(); - return httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()) + @ConditionalOnMissingBean + public OkHttpClient okHttpClient(ConnectionPool connectionPool, HttpLoggingInterceptor interceptor) { + boolean followRedirects = properties.isFollowRedirects(); + int connectTimeout = properties.getConnectionTimeout(); + return this.createBuilder(properties.isDisableSslValidation()) .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) .writeTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) @@ -135,6 +113,24 @@ public class RestTemplateConfiguration { .build(); } + private OkHttpClient.Builder createBuilder(boolean disableSslValidation) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + if (disableSslValidation) { + try { + X509TrustManager disabledTrustManager = DisableValidationTrustManager.INSTANCE; + TrustManager[] trustManagers = new TrustManager[]{disabledTrustManager}; + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustManagers, Holder.SECURE_RANDOM); + SSLSocketFactory disabledSslSocketFactory = sslContext.getSocketFactory(); + builder.sslSocketFactory(disabledSslSocketFactory, disabledTrustManager); + builder.hostnameVerifier(TrustAllHostNames.INSTANCE); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + log.warn("Error setting SSLSocketFactory in OKHttpClient", e); + } + } + return builder; + } + @Bean public RestTemplateHeaderInterceptor requestHeaderInterceptor( @Autowired(required = false) @Nullable BladeFeignAccountGetter accountGetter, @@ -142,38 +138,57 @@ public class RestTemplateConfiguration { return new RestTemplateHeaderInterceptor(accountGetter,properties); } - /** - * 普通的 RestTemplate,不透传请求头,一般只做外部 http 调用 - * @param httpClient OkHttpClient - * @return RestTemplate - */ - @Bean - @ConditionalOnMissingBean(RestTemplate.class) - public RestTemplate restTemplate(okhttp3.OkHttpClient httpClient) { - RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory(httpClient)); - configMessageConverters(restTemplate.getMessageConverters()); - return restTemplate; + @AutoConfiguration + @RequiredArgsConstructor + @ConditionalOnClass(OkHttpClient.class) + @ConditionalOnProperty(value = "blade.http.rest-template.enable") + public static class RestTemplateAutoConfiguration { + private final ApplicationContext context; + + /** + * 普通的 RestTemplate,不透传请求头,一般只做外部 http 调用 + * + * @param okHttpClient OkHttpClient + * @return RestTemplate + */ + @Bean + @ConditionalOnMissingBean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, OkHttpClient okHttpClient) { + restTemplateBuilder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient)); + RestTemplate restTemplate = restTemplateBuilder.build(); + configMessageConverters(context, restTemplate.getMessageConverters()); + return restTemplate; + } } - /** - * 支持负载均衡的 LbRestTemplate - * @param httpClient OkHttpClient - * @param interceptor RestTemplateHeaderInterceptor - * @return LbRestTemplate - */ - @Bean - @LoadBalanced - @ConditionalOnMissingBean(LbRestTemplate.class) - public LbRestTemplate lbRestTemplate(okhttp3.OkHttpClient httpClient, RestTemplateHeaderInterceptor interceptor) { - LbRestTemplate lbRestTemplate = new LbRestTemplate(new OkHttp3ClientHttpRequestFactory(httpClient)); - lbRestTemplate.setInterceptors(Collections.singletonList(interceptor)); - configMessageConverters(lbRestTemplate.getMessageConverters()); - return lbRestTemplate; + @AutoConfiguration + @RequiredArgsConstructor + @ConditionalOnClass(OkHttpClient.class) + @ConditionalOnProperty(value = "blade.http.lb-rest-template.enable") + public static class LbRestTemplateAutoConfiguration { + private final ApplicationContext context; + + /** + * 支持负载均衡的 LbRestTemplate + * + * @param okHttpClient OkHttpClient + * @return LbRestTemplate + */ + @Bean + @LoadBalanced + @ConditionalOnMissingBean + public LbRestTemplate lbRestTemplate(RestTemplateBuilder restTemplateBuilder, OkHttpClient okHttpClient) { + restTemplateBuilder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient)); + LbRestTemplate restTemplate = restTemplateBuilder.build(LbRestTemplate.class); + restTemplate.getInterceptors().add(context.getBean(RestTemplateHeaderInterceptor.class)); + configMessageConverters(context, restTemplate.getMessageConverters()); + return restTemplate; + } } - private void configMessageConverters(List> converters) { + private static void configMessageConverters(ApplicationContext context, List> converters) { converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof MappingJackson2HttpMessageConverter); converters.add(new StringHttpMessageConverter(Charsets.UTF_8)); - converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); + converters.add(new MappingJackson2HttpMessageConverter(context.getBean(ObjectMapper.class))); } } diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/AbstractStreamingClientHttpRequest.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/AbstractStreamingClientHttpRequest.java new file mode 100644 index 0000000..3b362c6 --- /dev/null +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/AbstractStreamingClientHttpRequest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.cloud.http.client; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.StreamingHttpOutputMessage; +import org.springframework.http.client.AbstractClientHttpRequest; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.FastByteArrayOutputStream; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Abstract base for {@link ClientHttpRequest} that also implement + * {@link StreamingHttpOutputMessage}. Ensures that headers and + * body are not written multiple times. + * + * @author Arjen Poutsma + * @since 6.1 + */ +public abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequest + implements StreamingHttpOutputMessage { + + @Nullable + private Body body; + + @Nullable + private FastByteArrayOutputStream bodyStream; + + + @Override + protected final OutputStream getBodyInternal(HttpHeaders headers) { + Assert.state(this.body == null, "Invoke either getBody or setBody; not both"); + + if (this.bodyStream == null) { + this.bodyStream = new FastByteArrayOutputStream(1024); + } + return this.bodyStream; + } + + @Override + public final void setBody(Body body) { + Assert.notNull(body, "Body must not be null"); + assertNotExecuted(); + Assert.state(this.bodyStream == null, "Invoke either getBody or setBody; not both"); + + this.body = body; + } + + @Override + protected final ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { + if (this.body == null && this.bodyStream != null) { + this.body = outputStream -> this.bodyStream.writeTo(outputStream); + } + return executeInternal(headers, this.body); + } + + + /** + * Abstract template method that writes the given headers and content to the HTTP request. + * + * @param headers the HTTP headers + * @param body the HTTP body, may be {@code null} if no body was {@linkplain #setBody(Body) set} + * @return the response object for the executed request + * @since 6.1 + */ + protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException; + +} diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpRequest.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpRequest.java new file mode 100644 index 0000000..3b682a2 --- /dev/null +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpRequest.java @@ -0,0 +1,137 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.cloud.http.client; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okio.BufferedSink; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.net.URI; + +/** + * {@link ClientHttpRequest} implementation based on OkHttp 3.x. + * + *

Created via the {@link OkHttp3ClientHttpRequestFactory}. + * + * @author Luciano Leggieri + * @author Arjen Poutsma + * @author Roy Clarkson + * @since 4.3 + */ +public class OkHttp3ClientHttpRequest extends AbstractStreamingClientHttpRequest { + + private final OkHttpClient client; + + private final URI uri; + + private final HttpMethod method; + + + public OkHttp3ClientHttpRequest(OkHttpClient client, URI uri, HttpMethod method) { + this.client = client; + this.uri = uri; + this.method = method; + } + + + @Override + public HttpMethod getMethod() { + return this.method; + } + + @Override + public URI getURI() { + return this.uri; + } + + @Override + @SuppressWarnings("removal") + protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException { + + RequestBody requestBody; + if (body != null) { + requestBody = new BodyRequestBody(headers, body); + } else if (okhttp3.internal.http.HttpMethod.requiresRequestBody(getMethod().name())) { + String header = headers.getFirst(HttpHeaders.CONTENT_TYPE); + MediaType contentType = (header != null) ? MediaType.parse(header) : null; + requestBody = RequestBody.create(contentType, new byte[0]); + } else { + requestBody = null; + } + Request.Builder builder = new Request.Builder() + .url(this.uri.toURL()); + builder.method(this.method.name(), requestBody); + headers.forEach((headerName, headerValues) -> { + for (String headerValue : headerValues) { + builder.addHeader(headerName, headerValue); + } + }); + Request request = builder.build(); + return new OkHttp3ClientHttpResponse(this.client.newCall(request).execute()); + } + + + private static class BodyRequestBody extends RequestBody { + + private final HttpHeaders headers; + + private final Body body; + + + public BodyRequestBody(HttpHeaders headers, Body body) { + this.headers = headers; + this.body = body; + } + + @Override + public long contentLength() { + return this.headers.getContentLength(); + } + + @Nullable + @Override + public MediaType contentType() { + String contentType = this.headers.getFirst(HttpHeaders.CONTENT_TYPE); + if (StringUtils.hasText(contentType)) { + return MediaType.parse(contentType); + } else { + return null; + } + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + this.body.writeTo(sink.outputStream()); + } + + @Override + public boolean isOneShot() { + return !this.body.repeatable(); + } + } + + +} diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpRequestFactory.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpRequestFactory.java new file mode 100644 index 0000000..ac162fb --- /dev/null +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpRequestFactory.java @@ -0,0 +1,156 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.cloud.http.client; + +import okhttp3.Cache; +import okhttp3.OkHttpClient; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** + * {@link ClientHttpRequestFactory} implementation that uses + * OkHttp 3.x to create requests. + * + * @author Luciano Leggieri + * @author Arjen Poutsma + * @author Roy Clarkson + * @since 4.3 + */ +public class OkHttp3ClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean { + + private OkHttpClient client; + + private final boolean defaultClient; + + + /** + * Create a factory with a default {@link OkHttpClient} instance. + */ + public OkHttp3ClientHttpRequestFactory() { + this.client = new OkHttpClient(); + this.defaultClient = true; + } + + /** + * Create a factory with the given {@link OkHttpClient} instance. + * + * @param client the client to use + */ + public OkHttp3ClientHttpRequestFactory(OkHttpClient client) { + Assert.notNull(client, "OkHttpClient must not be null"); + this.client = client; + this.defaultClient = false; + } + + + /** + * Set the underlying read timeout in milliseconds. + * A value of 0 specifies an infinite timeout. + */ + public void setReadTimeout(int readTimeout) { + this.client = this.client.newBuilder() + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .build(); + } + + /** + * Set the underlying read timeout in milliseconds. + * A value of 0 specifies an infinite timeout. + * + * @since 6.1 + */ + public void setReadTimeout(Duration readTimeout) { + this.client = this.client.newBuilder() + .readTimeout(readTimeout) + .build(); + } + + /** + * Set the underlying write timeout in milliseconds. + * A value of 0 specifies an infinite timeout. + */ + public void setWriteTimeout(int writeTimeout) { + this.client = this.client.newBuilder() + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) + .build(); + } + + /** + * Set the underlying write timeout in milliseconds. + * A value of 0 specifies an infinite timeout. + * + * @since 6.1 + */ + public void setWriteTimeout(Duration writeTimeout) { + this.client = this.client.newBuilder() + .writeTimeout(writeTimeout) + .build(); + } + + /** + * Set the underlying connect timeout in milliseconds. + * A value of 0 specifies an infinite timeout. + */ + public void setConnectTimeout(int connectTimeout) { + this.client = this.client.newBuilder() + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .build(); + } + + /** + * Set the underlying connect timeout in milliseconds. + * A value of 0 specifies an infinite timeout. + * + * @since 6.1 + */ + public void setConnectTimeout(Duration connectTimeout) { + this.client = this.client.newBuilder() + .connectTimeout(connectTimeout) + .build(); + } + + + @NotNull + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) { + return new OkHttp3ClientHttpRequest(this.client, uri, httpMethod); + } + + + @Override + public void destroy() throws IOException { + if (this.defaultClient) { + // Clean up the client if we created it in the constructor + Cache cache = this.client.cache(); + if (cache != null) { + cache.close(); + } + this.client.dispatcher().executorService().shutdown(); + this.client.connectionPool().evictAll(); + } + } + +} diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpResponse.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpResponse.java new file mode 100644 index 0000000..297cd35 --- /dev/null +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/client/OkHttp3ClientHttpResponse.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.cloud.http.client; + +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.io.InputStream; + +/** + * {@link ClientHttpResponse} implementation based on OkHttp 3.x. + * + * @author Luciano Leggieri + * @author Arjen Poutsma + * @author Roy Clarkson + * @since 4.3 + */ +public class OkHttp3ClientHttpResponse implements ClientHttpResponse { + + private final Response response; + + @Nullable + private volatile HttpHeaders headers; + + + public OkHttp3ClientHttpResponse(Response response) { + Assert.notNull(response, "Response must not be null"); + this.response = response; + } + + + @Override + public HttpStatusCode getStatusCode() throws IOException { + return HttpStatusCode.valueOf(this.response.code()); + } + + @Override + public String getStatusText() { + return this.response.message(); + } + + @Override + public InputStream getBody() throws IOException { + ResponseBody body = this.response.body(); + return (body != null ? body.byteStream() : InputStream.nullInputStream()); + } + + @Override + public HttpHeaders getHeaders() { + HttpHeaders headers = this.headers; + if (headers == null) { + headers = new HttpHeaders(); + for (String headerName : this.response.headers().names()) { + for (String headerValue : this.response.headers(headerName)) { + headers.add(headerName, headerValue); + } + } + this.headers = headers; + } + return headers; + } + + @Override + public void close() { + ResponseBody body = this.response.body(); + if (body != null) { + body.close(); + } + } + +} diff --git a/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeBlockExceptionHandler.java b/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeBlockExceptionHandler.java index 12ddec4..159373c 100644 --- a/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeBlockExceptionHandler.java +++ b/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeBlockExceptionHandler.java @@ -7,8 +7,8 @@ import org.springblade.core.tool.jackson.JsonUtil; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; /** * Sentinel统一限流策略 diff --git a/blade-core-datascope/src/main/java/org/springblade/core/datascope/model/DataScopeModel.java b/blade-core-datascope/src/main/java/org/springblade/core/datascope/model/DataScopeModel.java index 8859a21..9a03fa5 100644 --- a/blade-core-datascope/src/main/java/org/springblade/core/datascope/model/DataScopeModel.java +++ b/blade-core-datascope/src/main/java/org/springblade/core/datascope/model/DataScopeModel.java @@ -20,6 +20,7 @@ import lombok.NoArgsConstructor; import org.springblade.core.datascope.constant.DataScopeConstant; import org.springblade.core.datascope.enums.DataScopeEnum; +import java.io.Serial; import java.io.Serializable; /** @@ -31,6 +32,7 @@ import java.io.Serializable; @NoArgsConstructor public class DataScopeModel implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-develop/pom.xml b/blade-core-develop/pom.xml index 30bc426..ce6b870 100644 --- a/blade-core-develop/pom.xml +++ b/blade-core-develop/pom.xml @@ -23,12 +23,10 @@ com.baomidou mybatis-plus-generator - ${mybatis.plus.generator.version} com.baomidou mybatis-plus-extension - ${mybatis.plus.version} diff --git a/blade-core-develop/src/main/resources/templates/controller.java.vm b/blade-core-develop/src/main/resources/templates/controller.java.vm index 625090a..9471288 100644 --- a/blade-core-develop/src/main/resources/templates/controller.java.vm +++ b/blade-core-develop/src/main/resources/templates/controller.java.vm @@ -15,12 +15,12 @@ */ package $!{package.Controller}; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import lombok.AllArgsConstructor; -import javax.validation.Valid; +import jakarta.validation.Valid; import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; @@ -54,7 +54,7 @@ import $!{superControllerClassPackage}; @RestController @AllArgsConstructor @RequestMapping("#if($!{hasServiceName})/$!{serviceName}#end/$!{entityKey}") -@Api(value = "$!{table.comment}", tags = "$!{table.comment}接口") +@Tag(name = "$!{table.comment}", tags = "$!{table.comment}接口") #if($!{superControllerClass}) public class $!{table.controllerName} extends $!{superControllerClass} { #else @@ -69,7 +69,7 @@ public class $!{table.controllerName} { */ @GetMapping("/detail") @ApiOperationSupport(order = 1) - @ApiOperation(value = "详情", notes = "传入$!{table.entityPath}") + @Operation(summary = "详情", description = "传入$!{table.entityPath}") public R<$!{entity}VO> detail($!{entity} $!{table.entityPath}) { $!{entity} detail = $!{table.entityPath}Service.getOne(Condition.getQueryWrapper($!{table.entityPath})); return R.data($!{entity}Wrapper.build().entityVO(detail)); @@ -80,7 +80,7 @@ public class $!{table.controllerName} { */ @GetMapping("/list") @ApiOperationSupport(order = 2) - @ApiOperation(value = "分页", notes = "传入$!{table.entityPath}") + @Operation(summary = "分页", description = "传入$!{table.entityPath}") public R> list($!{entity} $!{table.entityPath}, Query query) { IPage<$!{entity}> pages = $!{table.entityPath}Service.page(Condition.getPage(query), Condition.getQueryWrapper($!{table.entityPath})); return R.data($!{entity}Wrapper.build().pageVO(pages)); @@ -92,7 +92,7 @@ public class $!{table.controllerName} { */ @GetMapping("/detail") @ApiOperationSupport(order = 1) - @ApiOperation(value = "详情", notes = "传入$!{table.entityPath}") + @Operation(summary = "详情", description = "传入$!{table.entityPath}") public R<$!{entity}> detail($!{entity} $!{table.entityPath}) { $!{entity} detail = $!{table.entityPath}Service.getOne(Condition.getQueryWrapper($!{table.entityPath})); return R.data(detail); @@ -103,7 +103,7 @@ public class $!{table.controllerName} { */ @GetMapping("/list") @ApiOperationSupport(order = 2) - @ApiOperation(value = "分页", notes = "传入$!{table.entityPath}") + @Operation(summary = "分页", description = "传入$!{table.entityPath}") public R> list($!{entity} $!{table.entityPath}, Query query) { IPage<$!{entity}> pages = $!{table.entityPath}Service.page(Condition.getPage(query), Condition.getQueryWrapper($!{table.entityPath})); return R.data(pages); @@ -115,7 +115,7 @@ public class $!{table.controllerName} { */ @GetMapping("/page") @ApiOperationSupport(order = 3) - @ApiOperation(value = "分页", notes = "传入$!{table.entityPath}") + @Operation(summary = "分页", description = "传入$!{table.entityPath}") public R> page($!{entity}VO $!{table.entityPath}, Query query) { IPage<$!{entity}VO> pages = $!{table.entityPath}Service.select$!{entity}Page(Condition.getPage(query), $!{table.entityPath}); return R.data(pages); @@ -126,7 +126,7 @@ public class $!{table.controllerName} { */ @PostMapping("/save") @ApiOperationSupport(order = 4) - @ApiOperation(value = "新增", notes = "传入$!{table.entityPath}") + @Operation(summary = "新增", description = "传入$!{table.entityPath}") public R save(@Valid @RequestBody $!{entity} $!{table.entityPath}) { return R.status($!{table.entityPath}Service.save($!{table.entityPath})); } @@ -136,7 +136,7 @@ public class $!{table.controllerName} { */ @PostMapping("/update") @ApiOperationSupport(order = 5) - @ApiOperation(value = "修改", notes = "传入$!{table.entityPath}") + @Operation(summary = "修改", description = "传入$!{table.entityPath}") public R update(@Valid @RequestBody $!{entity} $!{table.entityPath}) { return R.status($!{table.entityPath}Service.updateById($!{table.entityPath})); } @@ -146,7 +146,7 @@ public class $!{table.controllerName} { */ @PostMapping("/submit") @ApiOperationSupport(order = 6) - @ApiOperation(value = "新增或修改", notes = "传入$!{table.entityPath}") + @Operation(summary = "新增或修改", description = "传入$!{table.entityPath}") public R submit(@Valid @RequestBody $!{entity} $!{table.entityPath}) { return R.status($!{table.entityPath}Service.saveOrUpdate($!{table.entityPath})); } @@ -158,8 +158,8 @@ public class $!{table.controllerName} { */ @PostMapping("/remove") @ApiOperationSupport(order = 7) - @ApiOperation(value = "逻辑删除", notes = "传入ids") - public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) { + @Operation(summary = "逻辑删除", description = "传入ids") + public R remove(@Parameter(name = "主键集合", required = true) @RequestParam String ids) { return R.status($!{table.entityPath}Service.deleteLogic(Func.toLongList(ids))); } @@ -170,8 +170,8 @@ public class $!{table.controllerName} { */ @PostMapping("/remove") @ApiOperationSupport(order = 8) - @ApiOperation(value = "删除", notes = "传入ids") - public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) { + @Operation(summary = "删除", description = "传入ids") + public R remove(@Parameter(name = "主键集合", required = true) @RequestParam String ids) { return R.status($!{table.entityPath}Service.removeByIds(Func.toLongList(ids))); } diff --git a/blade-core-develop/src/main/resources/templates/entity.java.vm b/blade-core-develop/src/main/resources/templates/entity.java.vm index 08afc0b..fdc6772 100644 --- a/blade-core-develop/src/main/resources/templates/entity.java.vm +++ b/blade-core-develop/src/main/resources/templates/entity.java.vm @@ -23,9 +23,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; #end #if($!{swagger}) -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; #end +import java.io.Serial; /** * $!{table.comment}实体类 @@ -43,7 +43,7 @@ import io.swagger.annotations.ApiModelProperty; @EqualsAndHashCode(callSuper = true) #end #if($!{swagger}) -@ApiModel(value = "$!{entity}对象", description = #if ("$!{table.comment}"=="")"$!{entity}对象"#else"$!{table.comment}"#end) +@Schema(description = #if ("$!{table.comment}"=="")"$!{entity}对象"#else"$!{table.comment}"#end) #end #if($!{superEntityClass}) public class $!{entity} extends $!{superEntityClass}#if($!{activeRecord})<$!{entity}>#end { @@ -54,7 +54,8 @@ public class $!{entity} extends Model<$!{entity}> { public class $!{entity} implements Serializable { #end - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; ## ---------- BEGIN 字段循环遍历 ---------- #foreach($field in $!{table.fields}) @@ -67,7 +68,7 @@ public class $!{entity} implements Serializable { * $!{field.comment} */ #if($!{swagger}) - @ApiModelProperty(value = "$!{field.comment}") + @Schema(description = "$!{field.comment}") #end #end #if($!{field.keyFlag}) diff --git a/blade-core-develop/src/main/resources/templates/entityDTO.java.vm b/blade-core-develop/src/main/resources/templates/entityDTO.java.vm index a7f0512..8c31195 100644 --- a/blade-core-develop/src/main/resources/templates/entityDTO.java.vm +++ b/blade-core-develop/src/main/resources/templates/entityDTO.java.vm @@ -21,6 +21,7 @@ import $!{package.Entity}.$!{entity}; import lombok.Data; import lombok.EqualsAndHashCode; #end +import java.io.Serial; /** * $!{table.comment}数据传输对象实体类 @@ -33,6 +34,7 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) #end public class $!{entity}DTO extends $!{entity} { + @Serial private static final long serialVersionUID = 1L; } diff --git a/blade-core-develop/src/main/resources/templates/entityVO.java.vm b/blade-core-develop/src/main/resources/templates/entityVO.java.vm index ac0f812..e558e9a 100644 --- a/blade-core-develop/src/main/resources/templates/entityVO.java.vm +++ b/blade-core-develop/src/main/resources/templates/entityVO.java.vm @@ -22,8 +22,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; #end #if($!{swagger}) -import io.swagger.annotations.ApiModel; +import io.swagger.v3.oas.annotations.media.Schema; #end +import java.io.Serial; /** * $!{table.comment}视图实体类 @@ -36,9 +37,10 @@ import io.swagger.annotations.ApiModel; @EqualsAndHashCode(callSuper = true) #end #if($!{swagger}) -@ApiModel(value = "$!{entity}VO对象", description = #if ("$!{table.comment}"=="")"$!{entity}VO对象"#else"$!{table.comment}"#end) +@Schema(description = #if ("$!{table.comment}"=="")"$!{entity}VO对象"#else"$!{table.comment}"#end) #end public class $!{entity}VO extends $!{entity} { + @Serial private static final long serialVersionUID = 1L; } diff --git a/blade-core-develop/src/main/resources/templates/wrapper.java.vm b/blade-core-develop/src/main/resources/templates/wrapper.java.vm index 64a8b01..3729f01 100644 --- a/blade-core-develop/src/main/resources/templates/wrapper.java.vm +++ b/blade-core-develop/src/main/resources/templates/wrapper.java.vm @@ -37,7 +37,7 @@ public class $!{entity}Wrapper extends BaseEntityWrapper<$!{entity}, $!{entity}V @Override public $!{entity}VO entityVO($!{entity} $!{table.entityPath}) { - $!{entity}VO $!{table.entityPath}VO = BeanUtil.copy($!{table.entityPath}, $!{entity}VO.class); + $!{entity}VO $!{table.entityPath}VO = BeanUtil.copyProperties($!{table.entityPath}, $!{entity}VO.class); return $!{table.entityPath}VO; } diff --git a/blade-core-launch/pom.xml b/blade-core-launch/pom.xml index 064023b..b08338c 100644 --- a/blade-core-launch/pom.xml +++ b/blade-core-launch/pom.xml @@ -30,6 +30,28 @@ org.springframework.boot spring-boot-starter-undertow + + + jakarta.servlet + jakarta.servlet-api + + + + javax.xml.bind + jaxb-api + + + com.sun.xml.bind + jaxb-core + + + com.sun.xml.bind + jaxb-impl + + + javax.activation + activation + diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java b/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java new file mode 100644 index 0000000..98f37dc --- /dev/null +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2019-2029, DreamLu 卢春梦 (596392912@qq.com & www.dreamlu.net). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.launch.config; + +import org.springblade.core.launch.props.BladeProperties; +import org.springblade.core.launch.props.BladePropertySourcePostProcessor; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +/** + * blade property config + * + * @author L.cm + */ +@AutoConfiguration +@Order(Ordered.HIGHEST_PRECEDENCE) +@EnableConfigurationProperties(BladeProperties.class) +public class BladePropertyConfiguration { + + @Bean + public BladePropertySourcePostProcessor bladePropertySourcePostProcessor() { + return new BladePropertySourcePostProcessor(); + } + +} diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java index 141010b..c2e4dac 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java @@ -25,7 +25,7 @@ public interface AppConstant { /** * 应用版本 */ - String APPLICATION_VERSION = "3.7.2"; + String APPLICATION_VERSION = "4.0.0"; /** * 基础包 diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/log/BladeLogLevel.java b/blade-core-launch/src/main/java/org/springblade/core/launch/log/BladeLogLevel.java new file mode 100644 index 0000000..f3588b4 --- /dev/null +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/log/BladeLogLevel.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.launch.log; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 请求日志级别,来源 okHttp + * + * @author L.cm + */ +@Getter +@RequiredArgsConstructor +public enum BladeLogLevel { + /** + * No logs. + */ + NONE(0), + + /** + * Logs request and response lines. + * + *

Example: + *

{@code
+	 * --> POST /greeting http/1.1 (3-byte body)
+	 *
+	 * <-- 200 OK (22ms, 6-byte body)
+	 * }
+ */ + BASIC(1), + + /** + * Logs request and response lines and their respective headers. + * + *

Example: + *

{@code
+	 * --> POST /greeting http/1.1
+	 * Host: example.com
+	 * Content-Type: plain/text
+	 * Content-Length: 3
+	 * --> END POST
+	 *
+	 * <-- 200 OK (22ms)
+	 * Content-Type: plain/text
+	 * Content-Length: 6
+	 * <-- END HTTP
+	 * }
+ */ + HEADERS(2), + + /** + * Logs request and response lines and their respective headers and bodies (if present). + * + *

Example: + *

{@code
+	 * --> POST /greeting http/1.1
+	 * Host: example.com
+	 * Content-Type: plain/text
+	 * Content-Length: 3
+	 *
+	 * Hi?
+	 * --> END POST
+	 *
+	 * <-- 200 OK (22ms)
+	 * Content-Type: plain/text
+	 * Content-Length: 6
+	 *
+	 * Hello!
+	 * <-- END HTTP
+	 * }
+ */ + BODY(3); + + /** + * 请求日志配置前缀 + */ + public static final String REQ_LOG_PROPS_PREFIX = "blade.log.request"; + /** + * 控制台日志是否启用 + */ + public static final String CONSOLE_LOG_ENABLED_PROP = "blade.log.console.enabled"; + + /** + * 级别 + */ + private final int level; + + /** + * 当前版本 小于和等于 比较的版本 + * + * @param level LogLevel + * @return 是否小于和等于 + */ + public boolean lte(BladeLogLevel level) { + return this.level <= level.level; + } + +} diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladeProperties.java b/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladeProperties.java index 3ebc888..a3bd1fd 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladeProperties.java +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladeProperties.java @@ -17,6 +17,7 @@ package org.springblade.core.launch.props; import lombok.Getter; import lombok.Setter; +import org.springblade.core.launch.constant.AppConstant; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; @@ -200,6 +201,33 @@ public class BladeProperties implements EnvironmentAware, EnvironmentCapable { return defaultValue; } + /** + * 是否是开发环境 + * + * @return boolean + */ + public boolean isDev() { + return AppConstant.DEV_CODE.equals(getEnv()); + } + + /** + * 是否是生产环境 + * + * @return boolean + */ + public boolean isProd() { + return AppConstant.PROD_CODE.equals(getEnv()); + } + + /** + * 是否是测试环境 + * + * @return boolean + */ + public boolean isTest() { + return AppConstant.TEST_CODE.equals(getEnv()); + } + /** * 判断是否存在key * diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladePropertySource.java b/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladePropertySource.java new file mode 100644 index 0000000..38636b8 --- /dev/null +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladePropertySource.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2019-2029, DreamLu 卢春梦 (596392912@qq.com & www.dreamlu.net). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.launch.props; + +import org.springframework.core.Ordered; + +import java.lang.annotation.*; + +/** + * 自定义资源文件读取,优先级最低 + * + * @author L.cm + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface BladePropertySource { + + /** + * Indicate the resource location(s) of the properties file to be loaded. + * for example, {@code "classpath:/com/example/app.yml"} + * + * @return location(s) + */ + String value(); + + /** + * load app-{activeProfile}.yml + * + * @return {boolean} + */ + boolean loadActiveProfile() default true; + + /** + * Get the order value of this resource. + * + * @return order + */ + int order() default Ordered.LOWEST_PRECEDENCE; + +} diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladePropertySourcePostProcessor.java b/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladePropertySourcePostProcessor.java new file mode 100644 index 0000000..b85213b --- /dev/null +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/props/BladePropertySourcePostProcessor.java @@ -0,0 +1,178 @@ +/** + * Copyright (c) 2019-2029, DreamLu 卢春梦 (596392912@qq.com & www.dreamlu.net). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.launch.props; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.env.PropertySourceLoader; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.SpringFactoriesLoader; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 自定义资源文件读取,优先级最低 + * + * @author L.cm + */ +@Slf4j +public class BladePropertySourcePostProcessor implements BeanFactoryPostProcessor, InitializingBean, Ordered { + private final ResourceLoader resourceLoader; + private final List propertySourceLoaders; + + public BladePropertySourcePostProcessor() { + this.resourceLoader = new DefaultResourceLoader(); + this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + log.info("BladePropertySourcePostProcessor process @BladePropertySource bean."); + Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(BladePropertySource.class); + Set> beanEntrySet = beansWithAnnotation.entrySet(); + // 没有 @YmlPropertySource 注解,跳出 + if (beanEntrySet.isEmpty()) { + log.warn("Not found @BladePropertySource on spring bean class."); + return; + } + // 组装资源 + List propertyFileList = new ArrayList<>(); + for (Map.Entry entry : beanEntrySet) { + Class beanClass = ClassUtils.getUserClass(entry.getValue()); + BladePropertySource propertySource = AnnotationUtils.getAnnotation(beanClass, BladePropertySource.class); + if (propertySource == null) { + continue; + } + int order = propertySource.order(); + boolean loadActiveProfile = propertySource.loadActiveProfile(); + String location = propertySource.value(); + propertyFileList.add(new PropertyFile(order, location, loadActiveProfile)); + } + + // 装载 PropertySourceLoader + Map loaderMap = new HashMap<>(16); + for (PropertySourceLoader loader : propertySourceLoaders) { + String[] loaderExtensions = loader.getFileExtensions(); + for (String extension : loaderExtensions) { + loaderMap.put(extension, loader); + } + } + // 去重,排序 + List sortedPropertyList = propertyFileList.stream() + .distinct() + .sorted() + .collect(Collectors.toList()); + ConfigurableEnvironment environment = beanFactory.getBean(ConfigurableEnvironment.class); + MutablePropertySources propertySources = environment.getPropertySources(); + + // 只支持 activeProfiles,没有必要支持 spring.profiles.include。 + String[] activeProfiles = environment.getActiveProfiles(); + ArrayList propertySourceList = new ArrayList<>(); + for (String profile : activeProfiles) { + for (PropertyFile propertyFile : sortedPropertyList) { + // 不加载 ActiveProfile 的配置文件 + if (!propertyFile.loadActiveProfile) { + continue; + } + String extension = propertyFile.getExtension(); + PropertySourceLoader loader = loaderMap.get(extension); + if (loader == null) { + throw new IllegalArgumentException("Can't find PropertySourceLoader for PropertySource extension:" + extension); + } + String location = propertyFile.getLocation(); + String filePath = StringUtils.stripFilenameExtension(location); + String profiledLocation = filePath + "-" + profile + "." + extension; + Resource resource = resourceLoader.getResource(profiledLocation); + loadPropertySource(profiledLocation, resource, loader, propertySourceList); + } + } + // 本身的 Resource + for (PropertyFile propertyFile : sortedPropertyList) { + String extension = propertyFile.getExtension(); + PropertySourceLoader loader = loaderMap.get(extension); + String location = propertyFile.getLocation(); + Resource resource = resourceLoader.getResource(location); + loadPropertySource(location, resource, loader, propertySourceList); + } + // 转存 + for (PropertySource propertySource : propertySourceList) { + propertySources.addLast(propertySource); + } + } + + private static void loadPropertySource(String location, Resource resource, + PropertySourceLoader loader, + List sourceList) { + if (resource.exists()) { + String name = "bladePropertySource: [" + location + "]"; + try { + sourceList.addAll(loader.load(name, resource)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void afterPropertiesSet() throws Exception { + log.info("BladePropertySourcePostProcessor init."); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + @Getter + @ToString + @EqualsAndHashCode + private static class PropertyFile implements Comparable { + private final int order; + private final String location; + private final String extension; + private final boolean loadActiveProfile; + + PropertyFile(int order, String location, boolean loadActiveProfile) { + this.order = order; + this.location = location; + this.loadActiveProfile = loadActiveProfile; + this.extension = Objects.requireNonNull(StringUtils.getFilenameExtension(location)); + } + + @Override + public int compareTo(PropertyFile other) { + return Integer.compare(this.order, other.order); + } + } +} diff --git a/blade-core-loadbalancer/pom.xml b/blade-core-loadbalancer/pom.xml index 1b343ca..a38abbe 100644 --- a/blade-core-loadbalancer/pom.xml +++ b/blade-core-loadbalancer/pom.xml @@ -55,7 +55,6 @@ com.alibaba.nacos nacos-client - ${alibaba.nacos.version} diff --git a/blade-core-log/pom.xml b/blade-core-log/pom.xml index dd5e2a9..35a71cd 100644 --- a/blade-core-log/pom.xml +++ b/blade-core-log/pom.xml @@ -28,11 +28,15 @@ org.springblade blade-core-cloud + + + hibernate-validator + org.hibernate.validator + com.baomidou mybatis-plus - ${mybatis.plus.version} diff --git a/blade-core-log/src/main/java/org/springblade/core/log/config/BladeErrorMvcAutoConfiguration.java b/blade-core-log/src/main/java/org/springblade/core/log/config/BladeErrorMvcAutoConfiguration.java index fad5f36..de3bb50 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/config/BladeErrorMvcAutoConfiguration.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/config/BladeErrorMvcAutoConfiguration.java @@ -36,7 +36,7 @@ import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.DispatcherServlet; -import javax.servlet.Servlet; +import jakarta.servlet.Servlet; /** * 统一异常处理 diff --git a/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorAttributes.java b/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorAttributes.java index a3de7bd..b6d2057 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorAttributes.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorAttributes.java @@ -42,8 +42,8 @@ public class BladeErrorAttributes extends DefaultErrorAttributes { @Override public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { - String requestUri = this.getAttr(webRequest, "javax.servlet.error.request_uri"); - Integer status = this.getAttr(webRequest, "javax.servlet.error.status_code"); + String requestUri = this.getAttr(webRequest, "jakarta.servlet.error.request_uri"); + Integer status = this.getAttr(webRequest, "jakarta.servlet.error.status_code"); Throwable error = getError(webRequest); R result; if (error == null) { diff --git a/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorController.java b/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorController.java index 2daffea..f6ac6b5 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorController.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/error/BladeErrorController.java @@ -25,8 +25,8 @@ import org.springframework.http.MediaType; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.json.MappingJackson2JsonView; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Map; /** diff --git a/blade-core-log/src/main/java/org/springblade/core/log/error/BladeRestExceptionTranslator.java b/blade-core-log/src/main/java/org/springblade/core/log/error/BladeRestExceptionTranslator.java index 77294bd..cb692cf 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/error/BladeRestExceptionTranslator.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/error/BladeRestExceptionTranslator.java @@ -18,6 +18,7 @@ package org.springblade.core.log.error; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.springblade.core.launch.props.BladeProperties; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.log.props.BladeLogProperties; import org.springblade.core.log.publisher.ErrorLogPublisher; @@ -46,9 +47,9 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.NoHandlerFoundException; -import javax.servlet.Servlet; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; +import jakarta.servlet.Servlet; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; import java.util.Set; /** @@ -64,6 +65,7 @@ import java.util.Set; @RequiredArgsConstructor public class BladeRestExceptionTranslator { + private final BladeProperties bladeProperties; private final BladeLogProperties bladeLogProperties; @ExceptionHandler(MissingServletRequestParameterException.class) @@ -163,6 +165,10 @@ public class BladeRestExceptionTranslator { if (bladeLogProperties.getError()) { ErrorLogPublisher.publishEvent(e, UrlUtil.getPath(WebUtil.getRequest().getRequestURI())); } + // 生产环境屏蔽具体异常信息返回 + if (bladeProperties.isProd()) { + return R.fail(ResultCode.INTERNAL_SERVER_ERROR); + } return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage())); } diff --git a/blade-core-log/src/main/java/org/springblade/core/log/event/ErrorLogListener.java b/blade-core-log/src/main/java/org/springblade/core/log/event/ErrorLogListener.java index bbc2e01..6fabf31 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/event/ErrorLogListener.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/event/ErrorLogListener.java @@ -22,7 +22,6 @@ import org.springblade.core.launch.props.BladeProperties; import org.springblade.core.launch.server.ServerInfo; import org.springblade.core.log.constant.EventConstant; import org.springblade.core.log.feign.ILogClient; -import org.springblade.core.log.model.LogAbstract; import org.springblade.core.log.model.LogError; import org.springblade.core.log.utils.LogAbstractUtil; import org.springframework.context.event.EventListener; @@ -48,10 +47,14 @@ public class ErrorLogListener { @Order @EventListener(ErrorLogEvent.class) public void saveErrorLog(ErrorLogEvent event) { - Map source = (Map) event.getSource(); - LogError logError = (LogError) source.get(EventConstant.EVENT_LOG); - LogAbstractUtil.addOtherInfoToLog(logError, bladeProperties, serverInfo); - logService.saveErrorLog(logError); + try { + Map source = (Map) event.getSource(); + LogError logError = (LogError) source.get(EventConstant.EVENT_LOG); + LogAbstractUtil.addOtherInfoToLog(logError, bladeProperties, serverInfo); + logService.saveErrorLog(logError); + } catch (Exception e) { + log.error("保存错误日志失败", e); + } } } diff --git a/blade-core-log/src/main/java/org/springblade/core/log/model/LogApi.java b/blade-core-log/src/main/java/org/springblade/core/log/model/LogApi.java index d8a3de0..a18e220 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/model/LogApi.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/model/LogApi.java @@ -19,6 +19,7 @@ package org.springblade.core.log.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import java.io.Serial; import java.io.Serializable; /** @@ -30,6 +31,7 @@ import java.io.Serializable; @TableName("blade_log_api") public class LogApi extends LogAbstract implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-log/src/main/java/org/springblade/core/log/model/LogApiVo.java b/blade-core-log/src/main/java/org/springblade/core/log/model/LogApiVo.java index 72ac39a..90ea232 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/model/LogApiVo.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/model/LogApiVo.java @@ -18,6 +18,8 @@ package org.springblade.core.log.model; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serial; + /** * LogApi视图实体类 * @@ -26,6 +28,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class LogApiVo extends LogApi { + @Serial private static final long serialVersionUID = 1L; private String strId; diff --git a/blade-core-log/src/main/java/org/springblade/core/log/model/LogError.java b/blade-core-log/src/main/java/org/springblade/core/log/model/LogError.java index 448db85..74529b8 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/model/LogError.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/model/LogError.java @@ -19,6 +19,7 @@ package org.springblade.core.log.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import java.io.Serial; import java.io.Serializable; /** @@ -30,6 +31,7 @@ import java.io.Serializable; @TableName("blade_log_error") public class LogError extends LogAbstract implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-log/src/main/java/org/springblade/core/log/model/LogErrorVo.java b/blade-core-log/src/main/java/org/springblade/core/log/model/LogErrorVo.java index 4700105..5c8b0a2 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/model/LogErrorVo.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/model/LogErrorVo.java @@ -18,6 +18,8 @@ package org.springblade.core.log.model; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serial; + /** * LogError视图实体类 * @@ -26,6 +28,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class LogErrorVo extends LogError { + @Serial private static final long serialVersionUID = 1L; private String strId; diff --git a/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsual.java b/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsual.java index 2c15c4f..88f5bbb 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsual.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsual.java @@ -19,6 +19,7 @@ package org.springblade.core.log.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import java.io.Serial; import java.io.Serializable; /** @@ -31,6 +32,7 @@ import java.io.Serializable; @TableName("blade_log_usual") public class LogUsual extends LogAbstract implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsualVo.java b/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsualVo.java index 6a50f08..076c2fb 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsualVo.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/model/LogUsualVo.java @@ -18,6 +18,8 @@ package org.springblade.core.log.model; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serial; + /** * LogUsual视图实体类 * @@ -26,6 +28,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class LogUsualVo extends LogUsual { + @Serial private static final long serialVersionUID = 1L; private String strId; diff --git a/blade-core-log/src/main/java/org/springblade/core/log/publisher/ApiLogPublisher.java b/blade-core-log/src/main/java/org/springblade/core/log/publisher/ApiLogPublisher.java index ac9a3bc..3d6d87a 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/publisher/ApiLogPublisher.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/publisher/ApiLogPublisher.java @@ -28,7 +28,7 @@ import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.utils.SpringUtil; import org.springblade.core.tool.utils.WebUtil; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; diff --git a/blade-core-log/src/main/java/org/springblade/core/log/publisher/ErrorLogPublisher.java b/blade-core-log/src/main/java/org/springblade/core/log/publisher/ErrorLogPublisher.java index 3c5564b..f568cc4 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/publisher/ErrorLogPublisher.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/publisher/ErrorLogPublisher.java @@ -26,7 +26,7 @@ import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.SpringUtil; import org.springblade.core.tool.utils.WebUtil; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; diff --git a/blade-core-log/src/main/java/org/springblade/core/log/publisher/UsualLogPublisher.java b/blade-core-log/src/main/java/org/springblade/core/log/publisher/UsualLogPublisher.java index 7f0e39a..a3081dc 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/publisher/UsualLogPublisher.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/publisher/UsualLogPublisher.java @@ -24,7 +24,7 @@ import org.springblade.core.log.utils.LogAbstractUtil; import org.springblade.core.tool.utils.SpringUtil; import org.springblade.core.tool.utils.WebUtil; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; diff --git a/blade-core-log/src/main/java/org/springblade/core/log/utils/LogAbstractUtil.java b/blade-core-log/src/main/java/org/springblade/core/log/utils/LogAbstractUtil.java index de6192b..ff75e4d 100644 --- a/blade-core-log/src/main/java/org/springblade/core/log/utils/LogAbstractUtil.java +++ b/blade-core-log/src/main/java/org/springblade/core/log/utils/LogAbstractUtil.java @@ -22,7 +22,7 @@ import org.springblade.core.log.model.LogAbstract; import org.springblade.core.secure.utils.SecureUtil; import org.springblade.core.tool.utils.*; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; /** * Log 相关工具 diff --git a/blade-core-mybatis/pom.xml b/blade-core-mybatis/pom.xml index d699180..1f7444a 100644 --- a/blade-core-mybatis/pom.xml +++ b/blade-core-mybatis/pom.xml @@ -19,17 +19,18 @@ org.mybatis mybatis - ${mybatis.version} org.mybatis mybatis-spring - ${mybatis.spring.version} com.baomidou mybatis-plus - ${mybatis.plus.version} + + + org.mybatis + mybatis-typehandlers-jsr310 diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseEntity.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseEntity.java index 25f9c48..a276aed 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseEntity.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseEntity.java @@ -22,7 +22,7 @@ import com.baomidou.mybatisplus.annotation.TableLogic; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.springblade.core.tool.utils.DateUtil; import org.springframework.format.annotation.DateTimeFormat; @@ -41,7 +41,7 @@ public class BaseEntity implements Serializable { /** * 主键 */ - @ApiModelProperty(value = "主键") + @Schema(description = "主键") @TableId(value = "id", type = IdType.ASSIGN_ID) @JsonSerialize(using = ToStringSerializer.class) private Long id; @@ -50,14 +50,14 @@ public class BaseEntity implements Serializable { * 创建人 */ @JsonSerialize(using = ToStringSerializer.class) - @ApiModelProperty(value = "创建人") + @Schema(description = "创建人") private Long createUser; /** * 创建部门 */ @JsonSerialize(using = ToStringSerializer.class) - @ApiModelProperty(value = "创建部门") + @Schema(description = "创建部门") private Long createDept; /** @@ -65,14 +65,14 @@ public class BaseEntity implements Serializable { */ @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME) @JsonFormat(pattern = DateUtil.PATTERN_DATETIME) - @ApiModelProperty(value = "创建时间") + @Schema(description = "创建时间") private Date createTime; /** * 更新人 */ @JsonSerialize(using = ToStringSerializer.class) - @ApiModelProperty(value = "更新人") + @Schema(description = "更新人") private Long updateUser; /** @@ -80,19 +80,19 @@ public class BaseEntity implements Serializable { */ @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME) @JsonFormat(pattern = DateUtil.PATTERN_DATETIME) - @ApiModelProperty(value = "更新时间") + @Schema(description = "更新时间") private Date updateTime; /** * 状态[1:正常] */ - @ApiModelProperty(value = "业务状态") + @Schema(description = "业务状态") private Integer status; /** * 状态[0:未删除,1:删除] */ @TableLogic - @ApiModelProperty(value = "是否已删除") + @Schema(description = "是否已删除") private Integer isDeleted; } diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java index 8b4b5bd..8ca4a27 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java @@ -18,7 +18,7 @@ package org.springblade.core.mp.base; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.extension.service.IService; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotEmpty; import java.util.List; /** diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java index 7c02db2..2f61b44 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java @@ -28,7 +28,7 @@ import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.Func; import org.springframework.validation.annotation.Validated; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotEmpty; import java.util.Collection; import java.util.Date; import java.util.List; diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java index a53bf61..086ced5 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java @@ -16,7 +16,7 @@ package org.springblade.core.mp.base; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -32,7 +32,7 @@ public class TenantEntity extends BaseEntity { /** * 租户ID */ - @ApiModelProperty(value = "租户ID") + @Schema(description = "租户ID") private String tenantId; } diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Query.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Query.java index 10188bb..da1253f 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Query.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Query.java @@ -15,11 +15,12 @@ */ package org.springblade.core.mp.support; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; +import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY; + /** * 分页工具 * @@ -27,31 +28,31 @@ import lombok.experimental.Accessors; */ @Data @Accessors(chain = true) -@ApiModel(description = "查询条件") +@Schema(description = "查询条件") public class Query { /** * 当前页 */ - @ApiModelProperty(value = "当前页") + @Schema(description = "当前页") private Integer current; /** * 每页的数量 */ - @ApiModelProperty(value = "每页的数量") + @Schema(description = "每页的数量") private Integer size; /** * 排序的字段名 */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String ascs; /** * 排序方式 */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String descs; } diff --git a/blade-core-report/pom.xml b/blade-core-report/pom.xml index 00e77ba..a5aa445 100644 --- a/blade-core-report/pom.xml +++ b/blade-core-report/pom.xml @@ -21,8 +21,35 @@ com.bstek.ureport ureport2-console + + + commons-fileupload + commons-fileupload + + + javax.servlet + servlet-api + + 2.2.9 + + com.bstek.ureport + ureport2-core + + + javax.servlet + servlet-api + + + 2.2.9 + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + provided + diff --git a/blade-core-report/src/main/java/com/bstek/ureport/build/Splash.java b/blade-core-report/src/main/java/com/bstek/ureport/build/Splash.java new file mode 100644 index 0000000..54581d5 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/build/Splash.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.build; + +/** + * @author Jacky.gao + * @since 2017年6月19日 + */ +public class Splash { + public void doPrint() { + String sb = "\n" + + " ___ ___ ________ _______ ________ ________ ________ _________ ________ \n" + + "|\\ \\|\\ \\|\\ __ \\|\\ ___ \\ |\\ __ \\|\\ __ \\|\\ __ \\|\\___ ___\\ |\\_____ \\ \n" + + "\\ \\ \\\\\\ \\ \\ \\|\\ \\ \\ __/|\\ \\ \\|\\ \\ \\ \\|\\ \\ \\ \\|\\ \\|___ \\ \\_| \\|____|\\ /_ \n" + + " \\ \\ \\\\\\ \\ \\ _ _\\ \\ \\_|/_\\ \\ ____\\ \\ \\\\\\ \\ \\ _ _\\ \\ \\ \\ \\|\\ \\ \n" + + " \\ \\ \\\\\\ \\ \\ \\\\ \\\\ \\ \\_|\\ \\ \\ \\___|\\ \\ \\\\\\ \\ \\ \\\\ \\| \\ \\ \\ __\\_\\ \\ \n" + + " \\ \\_______\\ \\__\\\\ _\\\\ \\_______\\ \\__\\ \\ \\_______\\ \\__\\\\ _\\ \\ \\__\\ |\\_______\\\n" + + " \\|_______|\\|__|\\|__|\\|_______|\\|__| \\|_______|\\|__|\\|__| \\|__| \\|_______|\n" + + "........................................................................................................" + + "\n" + + ". uReport, is a Chinese style report engine" + + " licensed under the Apache License 2.0, ." + + "\n" + + ". which is opensource, easy to use,high-performance, with browser-based-designer, ." + + "\n" + + ". it has now been upgraded by BladeX to support jdk 17 and spring boot 3. ." + + "\n" + + "........................................................................................................" + + "\n"; + System.out.println(sb); + } + + +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/build/compute/ZxingValueCompute.java b/blade-core-report/src/main/java/com/bstek/ureport/build/compute/ZxingValueCompute.java new file mode 100644 index 0000000..528edc4 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/build/compute/ZxingValueCompute.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.build.compute; + +import com.bstek.ureport.build.BindData; +import com.bstek.ureport.build.Context; +import com.bstek.ureport.definition.value.Source; +import com.bstek.ureport.definition.value.ValueType; +import com.bstek.ureport.definition.value.ZxingValue; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.expression.model.Expression; +import com.bstek.ureport.expression.model.data.BindDataListExpressionData; +import com.bstek.ureport.expression.model.data.ExpressionData; +import com.bstek.ureport.expression.model.data.ObjectExpressionData; +import com.bstek.ureport.expression.model.data.ObjectListExpressionData; +import com.bstek.ureport.model.Cell; +import com.bstek.ureport.model.Image; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springblade.core.tool.utils.Base64Util; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年3月27日 + */ +public class ZxingValueCompute implements ValueCompute { + private static final int BLACK = 0xff000000; + private static final int WHITE = 0xFFFFFFFF; + @Override + public List compute(Cell cell, Context context) { + List list=new ArrayList(); + ZxingValue value=(ZxingValue)cell.getValue(); + String format=value.getFormat(); + BarcodeFormat barcodeForamt=BarcodeFormat.QR_CODE; + if(StringUtils.isNotBlank(format)){ + barcodeForamt=BarcodeFormat.valueOf(format); + } + int w=value.getWidth(); + int h=value.getHeight(); + Source source=value.getSource(); + if(source.equals(Source.text)){ + String data=value.getValue(); + Image image=buildImage(barcodeForamt,data,w,h); + list.add(new BindData(image)); + }else{ + Expression expression=value.getExpression(); + ExpressionData data=expression.execute(cell,cell, context); + if(data instanceof BindDataListExpressionData){ + BindDataListExpressionData listData=(BindDataListExpressionData)data; + List bindDataList=listData.getData(); + for(BindData bindData:bindDataList){ + Object obj=bindData.getValue(); + if(obj==null)obj=""; + Image image=buildImage(barcodeForamt,obj.toString(),w,h); + list.add(new BindData(image)); + } + }else if(data instanceof ObjectExpressionData){ + ObjectExpressionData exprData=(ObjectExpressionData)data; + Object obj=exprData.getData(); + if(obj==null){ + obj=""; + }else if(obj instanceof String){ + String text=obj.toString(); + if(text.startsWith("\"") && text.endsWith("\"")){ + text=text.substring(1,text.length()-1); + } + obj=text; + } + Image image=buildImage(barcodeForamt,obj.toString(),w,h); + list.add(new BindData(image)); + }else if(data instanceof ObjectListExpressionData){ + ObjectListExpressionData listExprData=(ObjectListExpressionData)data; + List listData=listExprData.getData(); + for(Object obj:listData){ + if(obj==null){ + obj=""; + }else if(obj instanceof String){ + String text=obj.toString(); + if(text.startsWith("\"") && text.endsWith("\"")){ + text=text.substring(1,text.length()-1); + } + obj=text; + } + Image image=buildImage(barcodeForamt,obj.toString(),w,h); + list.add(new BindData(image)); + } + } + } + return list; + } + + private Image buildImage(BarcodeFormat format,String data,int w,int h){ + try{ + Map hints = new Hashtable(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + hints.put(EncodeHintType.MARGIN,0); + if(format.equals(BarcodeFormat.QR_CODE)){ + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + } + BitMatrix matrix = new MultiFormatWriter().encode(data,format, w, h,hints); + int width = matrix.getWidth(); + int height = matrix.getHeight(); + BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); + } + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageIO.write(image, "png", outputStream); + byte[] bytes=outputStream.toByteArray(); + String base64Data= Base64Util.encodeToString(bytes); + IOUtils.closeQuietly(outputStream); + return new Image(base64Data,w,h); + }catch(Exception ex){ + throw new ReportComputeException(ex); + } + } + + @Override + public ValueType type() { + return ValueType.zxing; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/BaseServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/BaseServletAction.java new file mode 100644 index 0000000..e4a1eae --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/BaseServletAction.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Method; +import java.net.URLDecoder; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + + +/** + * @author Jacky.gao + * @since 2016年6月3日 + */ +public abstract class BaseServletAction implements ServletAction { + protected Throwable buildRootException(Throwable throwable) { + if (throwable.getCause() == null) { + return throwable; + } + return buildRootException(throwable.getCause()); + } + + protected String decode(String value) { + if (value == null) { + return value; + } + try { + value = URLDecoder.decode(value, "utf-8"); + value = URLDecoder.decode(value, "utf-8"); + return value; + } catch (Exception ex) { + return value; + } + } + + protected String decodeContent(String content) { + if (content == null) { + return content; + } + try { + content = URLDecoder.decode(content, "utf-8"); + return content; + } catch (Exception ex) { + return content; + } + } + + protected Map buildParameters(HttpServletRequest req) { + Map parameters = new HashMap(); + Enumeration enumeration = req.getParameterNames(); + while (enumeration.hasMoreElements()) { + Object obj = enumeration.nextElement(); + if (obj == null) { + continue; + } + String name = obj.toString(); + String value = req.getParameter(name); + if (name == null || value == null || name.startsWith("_")) { + continue; + } + parameters.put(name, decode(value)); + } + return parameters; + } + + protected void invokeMethod(String methodName, HttpServletRequest req, HttpServletResponse resp) throws ServletException { + try { + Method method = this.getClass().getMethod(methodName, new Class[]{HttpServletRequest.class, HttpServletResponse.class}); + method.invoke(this, new Object[]{req, resp}); + } catch (Exception ex) { + throw new ServletException(ex); + } + } + + protected String retriveMethod(HttpServletRequest req) throws ServletException { + String path = req.getContextPath() + UReportServlet.URL; + String uri = req.getRequestURI(); + String targetUrl = uri.substring(path.length()); + int slashPos = targetUrl.indexOf("/", 1); + if (slashPos > -1) { + String methodName = targetUrl.substring(slashPos + 1).trim(); + return methodName.length() > 0 ? methodName : null; + } + return null; + } + + protected String buildDownloadFileName(String reportFileName, String fileName, String extName) { + if (StringUtils.isNotBlank(fileName)) { + fileName = decode(fileName); + if (!fileName.toLowerCase().endsWith(extName)) { + fileName = fileName + extName; + } + return fileName; + } else { + int pos = reportFileName.indexOf(":"); + if (pos > 0) { + reportFileName = reportFileName.substring(pos + 1, reportFileName.length()); + } + pos = reportFileName.toLowerCase().indexOf(".ureport.xml"); + if (pos > 0) { + reportFileName = reportFileName.substring(0, pos); + } + return "ureport-" + reportFileName + extName; + } + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/MobileUtils.java b/blade-core-report/src/main/java/com/bstek/ureport/console/MobileUtils.java new file mode 100644 index 0000000..ac96dfb --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/MobileUtils.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console; + +import jakarta.servlet.http.HttpServletRequest; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Jacky.gao + * @since 2017年10月11日 + */ +public class MobileUtils { + private static String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i" + + "|windows (phone|ce)|blackberry" + + "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp" + + "|laystation portable)|nokia|fennec|htc[-_]" + + "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b"; + private static String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser" + + "|[1-4][0-9]{2}x[1-4][0-9]{2})\\b"; + private static Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE); + private static Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE); + + public static boolean isMobile(HttpServletRequest req) { + String userAgent = req.getHeader("USER-AGENT"); + if (userAgent == null) { + userAgent = ""; + } + userAgent = userAgent.toLowerCase(); + Matcher matcherPhone = phonePat.matcher(userAgent); + Matcher matcherTable = tablePat.matcher(userAgent); + if (matcherPhone.find() || matcherTable.find()) { + return true; + } else { + return false; + } + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/RequestHolder.java b/blade-core-report/src/main/java/com/bstek/ureport/console/RequestHolder.java new file mode 100644 index 0000000..c45ff57 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/RequestHolder.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * @author Jacky.gao + * @since 2017年3月8日 + */ +public class RequestHolder { + private static final ThreadLocal requestThreadLocal = new ThreadLocal(); + + public static void setRequest(HttpServletRequest request) { + requestThreadLocal.set(request); + } + + public static HttpServletRequest getRequest() { + return requestThreadLocal.get(); + } + + public static void clean() { + requestThreadLocal.remove(); + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/ServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/ServletAction.java new file mode 100644 index 0000000..3ddb64f --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/ServletAction.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +/** + * @author Jacky.gao + * @since 2017年1月25日 + */ +public interface ServletAction { + public static final String PREVIEW_KEY = "p"; + + void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException; + + String url(); +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/UReportServlet.java b/blade-core-report/src/main/java/com/bstek/ureport/console/UReportServlet.java new file mode 100644 index 0000000..c9670b4 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/UReportServlet.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年1月25日 + */ +public class UReportServlet extends HttpServlet { + private static final long serialVersionUID = 533049461276487971L; + public static final String URL = "/ureport"; + private Map actionMap = new HashMap(); + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + WebApplicationContext applicationContext = getWebApplicationContext(config); + Collection handlers = applicationContext.getBeansOfType(ServletAction.class).values(); + for (ServletAction handler : handlers) { + String url = handler.url(); + if (actionMap.containsKey(url)) { + throw new RuntimeException("Handler [" + url + "] already exist."); + } + actionMap.put(url, handler); + } + } + + protected WebApplicationContext getWebApplicationContext(ServletConfig config) { + return WebApplicationContextUtils.getWebApplicationContext(config.getServletContext()); + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = req.getContextPath() + URL; + String uri = req.getRequestURI(); + String targetUrl = uri.substring(path.length()); + if (targetUrl.length() < 1) { + outContent(resp, "Welcome to use ureport,please specify target url."); + return; + } + int slashPos = targetUrl.indexOf("/", 1); + if (slashPos > -1) { + targetUrl = targetUrl.substring(0, slashPos); + } + ServletAction targetHandler = actionMap.get(targetUrl); + if (targetHandler == null) { + outContent(resp, "Handler [" + targetUrl + "] not exist."); + return; + } + RequestHolder.setRequest(req); + try { + targetHandler.execute(req, resp); + } catch (Exception ex) { + resp.setCharacterEncoding("UTF-8"); + PrintWriter pw = resp.getWriter(); + Throwable e = buildRootException(ex); + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + String errorMsg = e.getMessage(); + if (StringUtils.isBlank(errorMsg)) { + errorMsg = e.getClass().getName(); + } + pw.write(errorMsg); + pw.close(); + throw new ServletException(ex); + } finally { + RequestHolder.clean(); + } + } + + private Throwable buildRootException(Throwable throwable) { + if (throwable.getCause() == null) { + return throwable; + } + return buildRootException(throwable.getCause()); + } + + private void outContent(HttpServletResponse resp, String msg) throws IOException { + resp.setContentType("text/html"); + PrintWriter pw = resp.getWriter(); + pw.write(""); + pw.write("

UReport Console
"); + pw.write(""); + pw.write(msg); + pw.write(""); + pw.write(""); + pw.flush(); + pw.close(); + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/WriteJsonServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/WriteJsonServletAction.java new file mode 100644 index 0000000..f94c4d2 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/WriteJsonServletAction.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletResponse; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; + + +/** + * @author Jacky.gao + * @since 2016年5月23日 + */ +public abstract class WriteJsonServletAction extends BaseServletAction { + protected void writeObjectToJson(HttpServletResponse resp, Object obj) throws ServletException, IOException { + resp.setContentType("text/json"); + resp.setCharacterEncoding("UTF-8"); + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Inclusion.NON_NULL); + mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); + mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + OutputStream out = resp.getOutputStream(); + try { + mapper.writeValue(out, obj); + } finally { + out.flush(); + out.close(); + } + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/cache/HttpSessionReportCache.java b/blade-core-report/src/main/java/com/bstek/ureport/console/cache/HttpSessionReportCache.java new file mode 100644 index 0000000..a905d5d --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/cache/HttpSessionReportCache.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.cache; + +import com.bstek.ureport.cache.ReportCache; +import com.bstek.ureport.console.RequestHolder; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年3月8日 + */ +public class HttpSessionReportCache implements ReportCache { + private Map sessionReportMap = new HashMap(); + private boolean disabled; + + @Override + public Object getObject(String file) { + HttpServletRequest req = RequestHolder.getRequest(); + if (req == null) { + return null; + } + ObjectMap objMap = getObjectMap(req); + return objMap.get(file); + } + + @Override + public void storeObject(String file, Object object) { + HttpServletRequest req = RequestHolder.getRequest(); + if (req == null) { + return; + } + ObjectMap map = getObjectMap(req); + map.put(file, object); + } + + @Override + public boolean disabled() { + return disabled; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + private ObjectMap getObjectMap(HttpServletRequest req) { + List expiredList = new ArrayList(); + for (String key : sessionReportMap.keySet()) { + ObjectMap reportObj = sessionReportMap.get(key); + if (reportObj.isExpired()) { + expiredList.add(key); + } + } + for (String key : expiredList) { + sessionReportMap.remove(key); + } + String sessionId = req.getSession().getId(); + ObjectMap obj = sessionReportMap.get(sessionId); + if (obj != null) { + return obj; + } else { + ObjectMap objMap = new ObjectMap(); + sessionReportMap.put(sessionId, objMap); + return objMap; + } + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/cache/TempObjectCache.java b/blade-core-report/src/main/java/com/bstek/ureport/console/cache/TempObjectCache.java new file mode 100644 index 0000000..8d3859d --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/cache/TempObjectCache.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.cache; + +import com.bstek.ureport.console.RequestHolder; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * @author Jacky.gao + * @since 2017年9月6日 + */ +public class TempObjectCache { + private static TempObjectCache tempObjectCache = new TempObjectCache(); + private Map sessionMap = new HashMap(); + + public static Object getObject(String key) { + return tempObjectCache.get(key); + } + + public static void putObject(String key, Object obj) { + tempObjectCache.store(key, obj); + } + + public static void removeObject(String key) { + tempObjectCache.remove(key); + } + + public void remove(String key) { + HttpServletRequest req = RequestHolder.getRequest(); + if (req == null) { + return; + } + ObjectMap mapObject = getReportMap(req); + if (mapObject != null) { + mapObject.remove(key); + } + } + + public Object get(String key) { + HttpServletRequest req = RequestHolder.getRequest(); + if (req == null) { + return null; + } + ObjectMap mapObject = getReportMap(req); + return mapObject.get(key); + } + + public void store(String key, Object obj) { + HttpServletRequest req = RequestHolder.getRequest(); + if (req == null) { + return; + } + ObjectMap mapObject = getReportMap(req); + mapObject.put(key, obj); + } + + private ObjectMap getReportMap(HttpServletRequest req) { + List expiredList = new ArrayList(); + for (String key : sessionMap.keySet()) { + ObjectMap reportObj = sessionMap.get(key); + if (reportObj.isExpired()) { + expiredList.add(key); + } + } + for (String key : expiredList) { + sessionMap.remove(key); + } + String sessionId = req.getSession().getId(); + ObjectMap obj = sessionMap.get(sessionId); + if (obj != null) { + return obj; + } else { + ObjectMap mapObject = new ObjectMap(); + sessionMap.put(sessionId, mapObject); + return mapObject; + } + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/chart/ChartServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/chart/ChartServletAction.java new file mode 100644 index 0000000..16117fd --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/chart/ChartServletAction.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.chart; + +import com.bstek.ureport.cache.CacheUtils; +import com.bstek.ureport.chart.ChartData; +import com.bstek.ureport.console.RenderPageServletAction; +import com.bstek.ureport.utils.UnitUtils; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +/** + * @author Jacky.gao + * @since 2017年6月30日 + */ +public class ChartServletAction extends RenderPageServletAction { + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } + } + + public void storeData(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String file = req.getParameter("_u"); + file = decode(file); + String chartId = req.getParameter("_chartId"); + ChartData chartData = CacheUtils.getChartData(chartId); + if (chartData == null) { + return; + } + String base64Data = req.getParameter("_base64Data"); + String prefix = "data:image/png;base64,"; + if (base64Data != null) { + if (base64Data.startsWith(prefix)) { + base64Data = base64Data.substring(prefix.length(), base64Data.length()); + } + } + chartData.setBase64Data(base64Data); + String width = req.getParameter("_width"); + String height = req.getParameter("_height"); + chartData.setHeight(UnitUtils.pixelToPoint(Integer.valueOf(height))); + chartData.setWidth(UnitUtils.pixelToPoint(Integer.valueOf(width))); + } + + @Override + public String url() { + return "/chart"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/designer/DatasourceServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/designer/DatasourceServletAction.java new file mode 100644 index 0000000..779c7b2 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/designer/DatasourceServletAction.java @@ -0,0 +1,385 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.designer; + +import com.bstek.ureport.Utils; +import com.bstek.ureport.build.Context; +import com.bstek.ureport.console.RenderPageServletAction; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.dataset.Field; +import com.bstek.ureport.definition.datasource.BuildinDatasource; +import com.bstek.ureport.definition.datasource.DataType; +import com.bstek.ureport.expression.ExpressionUtils; +import com.bstek.ureport.expression.model.Expression; +import com.bstek.ureport.expression.model.data.ExpressionData; +import com.bstek.ureport.expression.model.data.ObjectExpressionData; +import com.bstek.ureport.utils.ProcedureUtils; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.lang3.StringUtils; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.PreparedStatementCallback; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.PreparedStatementCreatorFactory; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.jdbc.core.namedparam.*; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.springframework.jdbc.support.JdbcUtils; + +import javax.sql.DataSource; +import java.beans.PropertyDescriptor; +import java.io.IOException; +import java.lang.reflect.Method; +import java.sql.*; +import java.util.Date; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Jacky.gao + * @since 2017年2月6日 + */ +public class DatasourceServletAction extends RenderPageServletAction { + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } + } + + public void loadBuildinDatasources(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + List datasources = new ArrayList(); + for (BuildinDatasource datasource : Utils.getBuildinDatasources()) { + datasources.add(datasource.name()); + } + writeObjectToJson(resp, datasources); + } + + public void loadMethods(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String beanId = req.getParameter("beanId"); + Object obj = applicationContext.getBean(beanId); + Class clazz = obj.getClass(); + Method[] methods = clazz.getMethods(); + List result = new ArrayList(); + for (Method method : methods) { + Class[] types = method.getParameterTypes(); + if (types.length != 3) { + continue; + } + Class typeClass1 = types[0]; + Class typeClass2 = types[1]; + Class typeClass3 = types[2]; + if (!String.class.isAssignableFrom(typeClass1)) { + continue; + } + if (!String.class.isAssignableFrom(typeClass2)) { + continue; + } + if (!Map.class.isAssignableFrom(typeClass3)) { + continue; + } + result.add(method.getName()); + } + writeObjectToJson(resp, result); + } + + public void buildClass(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String clazz = req.getParameter("clazz"); + List result = new ArrayList(); + try { + Class targetClass = Class.forName(clazz); + PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(targetClass); + for (PropertyDescriptor pd : propertyDescriptors) { + String name = pd.getName(); + if ("class".equals(name)) { + continue; + } + result.add(new Field(name)); + } + writeObjectToJson(resp, result); + } catch (Exception ex) { + throw new ReportDesignException(ex); + } + } + + public void buildDatabaseTables(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Connection conn = null; + ResultSet rs = null; + try { + conn = buildConnection(req); + DatabaseMetaData metaData = conn.getMetaData(); + String url = metaData.getURL(); + String schema = null; + if (url.toLowerCase().contains("oracle")) { + schema = metaData.getUserName(); + } + List> tables = new ArrayList>(); + rs = metaData.getTables(null, schema, "%", new String[]{"TABLE", "VIEW"}); + while (rs.next()) { + Map table = new HashMap(); + table.put("name", rs.getString("TABLE_NAME")); + table.put("type", rs.getString("TABLE_TYPE")); + tables.add(table); + } + writeObjectToJson(resp, tables); + } catch (Exception ex) { + throw new ServletException(ex); + } finally { + JdbcUtils.closeResultSet(rs); + JdbcUtils.closeConnection(conn); + } + } + + public void buildFields(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String sql = req.getParameter("sql"); + String parameters = req.getParameter("parameters"); + Connection conn = null; + final List fields = new ArrayList(); + try { + conn = buildConnection(req); + Map map = buildParameters(parameters); + sql = parseSql(sql, map); + if (ProcedureUtils.isProcedure(sql)) { + List fieldsList = ProcedureUtils.procedureColumnsQuery(sql, map, conn); + fields.addAll(fieldsList); + } else { + DataSource dataSource = new SingleConnectionDataSource(conn, false); + NamedParameterJdbcTemplate jdbc = new NamedParameterJdbcTemplate(dataSource); + PreparedStatementCreator statementCreator = getPreparedStatementCreator(sql, new MapSqlParameterSource(map)); + jdbc.getJdbcOperations().execute(statementCreator, new PreparedStatementCallback() { + @Override + public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException { + ResultSet rs = null; + try { + rs = ps.executeQuery(); + ResultSetMetaData metadata = rs.getMetaData(); + int columnCount = metadata.getColumnCount(); + for (int i = 0; i < columnCount; i++) { + String columnName = metadata.getColumnLabel(i + 1); + fields.add(new Field(columnName)); + } + return null; + } finally { + JdbcUtils.closeResultSet(rs); + } + } + }); + } + writeObjectToJson(resp, fields); + } catch (Exception ex) { + throw new ReportDesignException(ex); + } finally { + JdbcUtils.closeConnection(conn); + } + } + + protected PreparedStatementCreator getPreparedStatementCreator(String sql, SqlParameterSource paramSource) { + ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql); + String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource); + Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null); + List declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource); + PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters); + return pscf.newPreparedStatementCreator(params); + } + + public void previewData(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String sql = req.getParameter("sql"); + String parameters = req.getParameter("parameters"); + Map map = buildParameters(parameters); + sql = parseSql(sql, map); + Connection conn = null; + try { + conn = buildConnection(req); + List> list = null; + if (ProcedureUtils.isProcedure(sql)) { + list = ProcedureUtils.procedureQuery(sql, map, conn); + } else { + DataSource dataSource = new SingleConnectionDataSource(conn, false); + NamedParameterJdbcTemplate jdbc = new NamedParameterJdbcTemplate(dataSource); + list = jdbc.queryForList(sql, map); + } + int size = list.size(); + int currentTotal = size; + if (currentTotal > 500) { + currentTotal = 500; + } + List> ls = new ArrayList>(); + for (int i = 0; i < currentTotal; i++) { + ls.add(list.get(i)); + } + DataResult result = new DataResult(); + List fields = new ArrayList(); + if (size > 0) { + Map item = list.get(0); + for (String name : item.keySet()) { + fields.add(name); + } + } + result.setFields(fields); + result.setCurrentTotal(currentTotal); + result.setData(ls); + result.setTotal(size); + writeObjectToJson(resp, result); + } catch (Exception ex) { + throw new ServletException(ex); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } + + private String parseSql(String sql, Map parameters) { + sql = sql.trim(); + Context context = new Context(applicationContext, parameters); + if (sql.startsWith(ExpressionUtils.EXPR_PREFIX) && sql.endsWith(ExpressionUtils.EXPR_SUFFIX)) { + sql = sql.substring(2, sql.length() - 1); + Expression expr = ExpressionUtils.parseExpression(sql); + sql = executeSqlExpr(expr, context); + return sql; + } else { + String sqlForUse = sql; + Pattern pattern = Pattern.compile("\\$\\{.*?\\}"); + Matcher matcher = pattern.matcher(sqlForUse); + while (matcher.find()) { + String substr = matcher.group(); + String sqlExpr = substr.substring(2, substr.length() - 1); + Expression expr = ExpressionUtils.parseExpression(sqlExpr); + String result = executeSqlExpr(expr, context); + sqlForUse = sqlForUse.replace(substr, result); + } + Utils.logToConsole("DESIGN SQL:" + sqlForUse); + return sqlForUse; + } + } + + private String executeSqlExpr(Expression sqlExpr, Context context) { + String sqlForUse = null; + ExpressionData exprData = sqlExpr.execute(null, null, context); + if (exprData instanceof ObjectExpressionData) { + ObjectExpressionData data = (ObjectExpressionData) exprData; + Object obj = data.getData(); + if (obj != null) { + String s = obj.toString(); + s = s.replaceAll("\\\\", ""); + sqlForUse = s; + } + } + return sqlForUse; + } + + public void testConnection(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String username = req.getParameter("username"); + String password = req.getParameter("password"); + String driver = req.getParameter("driver"); + String url = req.getParameter("url"); + Connection conn = null; + Map map = new HashMap(); + try { + Class.forName(driver); + conn = DriverManager.getConnection(url, username, password); + map.put("result", true); + } catch (Exception ex) { + map.put("error", ex.toString()); + map.put("result", false); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + writeObjectToJson(resp, map); + } + + @SuppressWarnings("unchecked") + private Map buildParameters(String parameters) throws IOException, JsonParseException, JsonMappingException { + Map map = new HashMap(); + if (StringUtils.isBlank(parameters)) { + return map; + } + ObjectMapper mapper = new ObjectMapper(); + List> list = mapper.readValue(parameters, ArrayList.class); + for (Map param : list) { + String name = param.get("name").toString(); + DataType type = DataType.valueOf(param.get("type").toString()); + String defaultValue = (String) param.get("defaultValue"); + if (defaultValue == null || defaultValue.isEmpty()) { + switch (type) { + case Boolean: + map.put(name, false); + case Date: + map.put(name, new Date()); + case Float: + map.put(name, (float) 0); + case Integer: + map.put(name, 0); + case String: + if (defaultValue != null) { + map.put(name, ""); + } else { + map.put(name, "null"); + } + break; + case List: + map.put(name, new ArrayList<>()); + } + } else { + map.put(name, type.parse(defaultValue)); + } + } + return map; + } + + private Connection buildConnection(HttpServletRequest req) throws Exception { + String type = req.getParameter("type"); + if (type.equals("jdbc")) { + String username = req.getParameter("username"); + String password = req.getParameter("password"); + String driver = req.getParameter("driver"); + String url = req.getParameter("url"); + + Class.forName(driver); + return DriverManager.getConnection(url, username, password); + } else { + String name = req.getParameter("name"); + Connection conn = Utils.getBuildinConnection(name); + if (conn == null) { + throw new ReportDesignException("Buildin datasource [" + name + "] not exist."); + } + return conn; + } + } + + @Override + public String url() { + return "/datasource"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/designer/DesignerServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/designer/DesignerServletAction.java new file mode 100644 index 0000000..76fa50d --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/designer/DesignerServletAction.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.designer; + +import com.bstek.ureport.cache.CacheUtils; +import com.bstek.ureport.console.RenderPageServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.ReportDefinition; +import com.bstek.ureport.dsl.ReportParserLexer; +import com.bstek.ureport.dsl.ReportParserParser; +import com.bstek.ureport.dsl.ReportParserParser.DatasetContext; +import com.bstek.ureport.export.ReportRender; +import com.bstek.ureport.expression.ErrorInfo; +import com.bstek.ureport.expression.ScriptErrorListener; +import com.bstek.ureport.parser.ReportParser; +import com.bstek.ureport.provider.report.ReportProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.*; + +/** + * @author Jacky.gao + * @since 2017年1月25日 + */ +public class DesignerServletAction extends RenderPageServletAction { + private ReportRender reportRender; + private ReportParser reportParser; + private List reportProviders = new ArrayList(); + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } else { + VelocityContext context = new VelocityContext(); + context.put("contextPath", req.getContextPath()); + resp.setContentType("text/html"); + resp.setCharacterEncoding("utf-8"); + Template template = ve.getTemplate("ureport-html/designer.html", "utf-8"); + PrintWriter writer = resp.getWriter(); + template.merge(context, writer); + writer.close(); + } + } + + public void scriptValidation(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String content = req.getParameter("content"); + ANTLRInputStream antlrInputStream = new ANTLRInputStream(content); + ReportParserLexer lexer = new ReportParserLexer(antlrInputStream); + CommonTokenStream tokenStream = new CommonTokenStream(lexer); + ReportParserParser parser = new ReportParserParser(tokenStream); + ScriptErrorListener errorListener = new ScriptErrorListener(); + parser.removeErrorListeners(); + parser.addErrorListener(errorListener); + parser.expression(); + List infos = errorListener.getInfos(); + writeObjectToJson(resp, infos); + } + + public void conditionScriptValidation(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String content = req.getParameter("content"); + ANTLRInputStream antlrInputStream = new ANTLRInputStream(content); + ReportParserLexer lexer = new ReportParserLexer(antlrInputStream); + CommonTokenStream tokenStream = new CommonTokenStream(lexer); + ReportParserParser parser = new ReportParserParser(tokenStream); + ScriptErrorListener errorListener = new ScriptErrorListener(); + parser.removeErrorListeners(); + parser.addErrorListener(errorListener); + parser.expr(); + List infos = errorListener.getInfos(); + writeObjectToJson(resp, infos); + } + + + public void parseDatasetName(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String expr = req.getParameter("expr"); + ANTLRInputStream antlrInputStream = new ANTLRInputStream(expr); + ReportParserLexer lexer = new ReportParserLexer(antlrInputStream); + CommonTokenStream tokenStream = new CommonTokenStream(lexer); + ReportParserParser parser = new ReportParserParser(tokenStream); + parser.removeErrorListeners(); + DatasetContext ctx = parser.dataset(); + String datasetName = ctx.Identifier().getText(); + Map result = new HashMap(); + result.put("datasetName", datasetName); + writeObjectToJson(resp, result); + } + + public void savePreviewData(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String content = req.getParameter("content"); + content = decodeContent(content); + InputStream inputStream = IOUtils.toInputStream(content, "utf-8"); + ReportDefinition reportDef = reportParser.parse(inputStream, "p"); + reportRender.rebuildReportDefinition(reportDef); + IOUtils.closeQuietly(inputStream); + TempObjectCache.putObject(PREVIEW_KEY, reportDef); + } + + public void loadReport(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String file = req.getParameter("file"); + if (file == null) { + throw new ReportDesignException("Report file can not be null."); + } + file = ReportUtils.decodeFileName(file); + Object obj = TempObjectCache.getObject(file); + if (obj != null && obj instanceof ReportDefinition) { + ReportDefinition reportDef = (ReportDefinition) obj; + TempObjectCache.removeObject(file); + writeObjectToJson(resp, new ReportDefinitionWrapper(reportDef)); + } else { + ReportDefinition reportDef = reportRender.parseReport(file); + writeObjectToJson(resp, new ReportDefinitionWrapper(reportDef)); + } + } + + public void deleteReportFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String file = req.getParameter("file"); + if (file == null) { + throw new ReportDesignException("Report file can not be null."); + } + ReportProvider targetReportProvider = null; + for (ReportProvider provider : reportProviders) { + if (file.startsWith(provider.getPrefix())) { + targetReportProvider = provider; + break; + } + } + if (targetReportProvider == null) { + throw new ReportDesignException("File [" + file + "] not found available report provider."); + } + targetReportProvider.deleteReport(file); + } + + + public void saveReportFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String file = req.getParameter("file"); + file = ReportUtils.decodeFileName(file); + String content = req.getParameter("content"); + content = decodeContent(content); + ReportProvider targetReportProvider = null; + for (ReportProvider provider : reportProviders) { + if (file.startsWith(provider.getPrefix())) { + targetReportProvider = provider; + break; + } + } + if (targetReportProvider == null) { + throw new ReportDesignException("File [" + file + "] not found available report provider."); + } + targetReportProvider.saveReport(file, content); + InputStream inputStream = IOUtils.toInputStream(content, "utf-8"); + ReportDefinition reportDef = reportParser.parse(inputStream, file); + reportRender.rebuildReportDefinition(reportDef); + CacheUtils.cacheReportDefinition(file, reportDef); + IOUtils.closeQuietly(inputStream); + } + + public void loadReportProviders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + writeObjectToJson(resp, reportProviders); + } + + public void setReportRender(ReportRender reportRender) { + this.reportRender = reportRender; + } + + public void setReportParser(ReportParser reportParser) { + this.reportParser = reportParser; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + super.setApplicationContext(applicationContext); + Collection providers = applicationContext.getBeansOfType(ReportProvider.class).values(); + for (ReportProvider provider : providers) { + if (provider.disabled() || provider.getName() == null) { + continue; + } + reportProviders.add(provider); + } + } + + @Override + public String url() { + return "/designer"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/designer/SearchFormDesignerAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/designer/SearchFormDesignerAction.java new file mode 100644 index 0000000..be9d228 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/designer/SearchFormDesignerAction.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.designer; + +import com.bstek.ureport.console.RenderPageServletAction; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @author Jacky.gao + * @since 2017年10月24日 + */ +public class SearchFormDesignerAction extends RenderPageServletAction { + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + VelocityContext context = new VelocityContext(); + context.put("contextPath", req.getContextPath()); + resp.setContentType("text/html"); + resp.setCharacterEncoding("utf-8"); + Template template = ve.getTemplate("ureport-html/searchform.html", "utf-8"); + PrintWriter writer = resp.getWriter(); + template.merge(context, writer); + writer.close(); + } + + @Override + public String url() { + return "/searchFormDesigner"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/excel/ExportExcel97ServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/excel/ExportExcel97ServletAction.java new file mode 100644 index 0000000..be5b76e --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/excel/ExportExcel97ServletAction.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.excel; + +import com.bstek.ureport.build.ReportBuilder; +import com.bstek.ureport.console.BaseServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.ReportDefinition; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.export.ExportConfigure; +import com.bstek.ureport.export.ExportConfigureImpl; +import com.bstek.ureport.export.ExportManager; +import com.bstek.ureport.export.excel.low.Excel97Producer; +import com.bstek.ureport.model.Report; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年7月3日 + */ +public class ExportExcel97ServletAction extends BaseServletAction { + private ReportBuilder reportBuilder; + private ExportManager exportManager; + private Excel97Producer excelProducer = new Excel97Producer(); + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } else { + buildExcel(req, resp, false, false); + } + } + + public void paging(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + buildExcel(req, resp, true, false); + } + + public void sheet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + buildExcel(req, resp, false, true); + } + + public void buildExcel(HttpServletRequest req, HttpServletResponse resp, boolean withPage, boolean withSheet) throws IOException { + String file = req.getParameter("_u"); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + String fileName = req.getParameter("_n"); + if (StringUtils.isNotBlank(fileName)) { + fileName = decode(fileName); + } else { + fileName = "ureport.xls"; + } + resp.setContentType("application/octet-stream;charset=ISO8859-1"); + resp.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\""); + Map parameters = buildParameters(req); + OutputStream outputStream = resp.getOutputStream(); + if (file.equals(PREVIEW_KEY)) { + ReportDefinition reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do export excel."); + } + Report report = reportBuilder.buildReport(reportDefinition, parameters); + if (withPage) { + excelProducer.produceWithPaging(report, outputStream); + } else if (withSheet) { + excelProducer.produceWithSheet(report, outputStream); + } else { + excelProducer.produce(report, outputStream); + } + } else { + ExportConfigure configure = new ExportConfigureImpl(file, parameters, outputStream); + if (withPage) { + exportManager.exportExcelWithPaging(configure); + } else if (withSheet) { + exportManager.exportExcelWithPagingSheet(configure); + } else { + exportManager.exportExcel(configure); + } + } + outputStream.flush(); + outputStream.close(); + } + + public void setReportBuilder(ReportBuilder reportBuilder) { + this.reportBuilder = reportBuilder; + } + + public void setExportManager(ExportManager exportManager) { + this.exportManager = exportManager; + } + + @Override + public String url() { + return "/excel97"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/excel/ExportExcelServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/excel/ExportExcelServletAction.java new file mode 100644 index 0000000..772b8b4 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/excel/ExportExcelServletAction.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.excel; + +import com.bstek.ureport.build.ReportBuilder; +import com.bstek.ureport.console.BaseServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.ReportDefinition; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.exception.ReportException; +import com.bstek.ureport.export.ExportConfigure; +import com.bstek.ureport.export.ExportConfigureImpl; +import com.bstek.ureport.export.ExportManager; +import com.bstek.ureport.export.excel.high.ExcelProducer; +import com.bstek.ureport.model.Report; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年4月17日 + */ +public class ExportExcelServletAction extends BaseServletAction { + private ReportBuilder reportBuilder; + private ExportManager exportManager; + private ExcelProducer excelProducer = new ExcelProducer(); + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } else { + buildExcel(req, resp, false, false); + } + } + + public void paging(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + buildExcel(req, resp, true, false); + } + + public void sheet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + buildExcel(req, resp, false, true); + } + + public void buildExcel(HttpServletRequest req, HttpServletResponse resp, boolean withPage, boolean withSheet) throws IOException { + String file = req.getParameter("_u"); + file = decode(file); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + OutputStream outputStream = resp.getOutputStream(); + try { + String fileName = req.getParameter("_n"); + fileName = buildDownloadFileName(file, fileName, ".xlsx"); + resp.setContentType("application/octet-stream;charset=ISO8859-1"); + fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1"); + resp.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\""); + Map parameters = buildParameters(req); + if (file.equals(PREVIEW_KEY)) { + ReportDefinition reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do export excel."); + } + Report report = reportBuilder.buildReport(reportDefinition, parameters); + if (withPage) { + excelProducer.produceWithPaging(report, outputStream); + } else if (withSheet) { + excelProducer.produceWithSheet(report, outputStream); + } else { + excelProducer.produce(report, outputStream); + } + } else { + ExportConfigure configure = new ExportConfigureImpl(file, parameters, outputStream); + if (withPage) { + exportManager.exportExcelWithPaging(configure); + } else if (withSheet) { + exportManager.exportExcelWithPagingSheet(configure); + } else { + exportManager.exportExcel(configure); + } + } + } catch (Exception ex) { + throw new ReportException(ex); + } finally { + outputStream.flush(); + outputStream.close(); + } + } + + public void setReportBuilder(ReportBuilder reportBuilder) { + this.reportBuilder = reportBuilder; + } + + public void setExportManager(ExportManager exportManager) { + this.exportManager = exportManager; + } + + @Override + public String url() { + return "/excel"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/html/HtmlPreviewServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/html/HtmlPreviewServletAction.java new file mode 100644 index 0000000..85be661 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/html/HtmlPreviewServletAction.java @@ -0,0 +1,365 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.html; + +import com.bstek.ureport.build.Context; +import com.bstek.ureport.build.ReportBuilder; +import com.bstek.ureport.build.paging.Page; +import com.bstek.ureport.cache.CacheUtils; +import com.bstek.ureport.chart.ChartData; +import com.bstek.ureport.console.MobileUtils; +import com.bstek.ureport.console.RenderPageServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.Paper; +import com.bstek.ureport.definition.ReportDefinition; +import com.bstek.ureport.definition.searchform.FormPosition; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.export.*; +import com.bstek.ureport.export.html.HtmlProducer; +import com.bstek.ureport.export.html.HtmlReport; +import com.bstek.ureport.export.html.SearchFormData; +import com.bstek.ureport.model.Report; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.codehaus.jackson.map.ObjectMapper; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.*; + +/** + * @author Jacky.gao + * @since 2017年2月15日 + */ +public class HtmlPreviewServletAction extends RenderPageServletAction { + private ExportManager exportManager; + private ReportBuilder reportBuilder; + private ReportRender reportRender; + private HtmlProducer htmlProducer = new HtmlProducer(); + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } else { + VelocityContext context = new VelocityContext(); + HtmlReport htmlReport = null; + String errorMsg = null; + try { + htmlReport = loadReport(req); + } catch (Exception ex) { + if (!(ex instanceof ReportDesignException)) { + ex.printStackTrace(); + } + errorMsg = buildExceptionMessage(ex); + } + String title = buildTitle(req); + context.put("title", title); + if (htmlReport == null) { + context.put("content", "
报表计算出错,错误信息如下:
" + errorMsg + "
"); + context.put("error", true); + context.put("searchFormJs", ""); + context.put("downSearchFormHtml", ""); + context.put("upSearchFormHtml", ""); + } else { + SearchFormData formData = htmlReport.getSearchFormData(); + if (formData != null) { + context.put("searchFormJs", formData.getJs()); + if (formData.getFormPosition().equals(FormPosition.up)) { + context.put("upSearchFormHtml", formData.getHtml()); + context.put("downSearchFormHtml", ""); + } else { + context.put("downSearchFormHtml", formData.getHtml()); + context.put("upSearchFormHtml", ""); + } + } else { + context.put("searchFormJs", ""); + context.put("downSearchFormHtml", ""); + context.put("upSearchFormHtml", ""); + } + context.put("content", htmlReport.getContent()); + context.put("style", htmlReport.getStyle()); + context.put("reportAlign", htmlReport.getReportAlign()); + context.put("totalPage", htmlReport.getTotalPage()); + context.put("totalPageWithCol", htmlReport.getTotalPageWithCol()); + context.put("pageIndex", htmlReport.getPageIndex()); + context.put("chartDatas", convertJson(htmlReport.getChartDatas())); + context.put("error", false); + context.put("file", req.getParameter("_u")); + context.put("intervalRefreshValue", htmlReport.getHtmlIntervalRefreshValue()); + String customParameters = buildCustomParameters(req); + context.put("customParameters", customParameters); + context.put("_t", ""); + Tools tools = null; + if (MobileUtils.isMobile(req)) { + tools = new Tools(false); + tools.setShow(false); + } else { + String toolsInfo = req.getParameter("_t"); + if (StringUtils.isNotBlank(toolsInfo)) { + tools = new Tools(false); + if (toolsInfo.equals("0")) { + tools.setShow(false); + } else { + String[] infos = toolsInfo.split(","); + for (String name : infos) { + tools.doInit(name); + } + } + context.put("_t", toolsInfo); + context.put("hasTools", true); + } else { + tools = new Tools(true); + } + } + context.put("tools", tools); + } + context.put("contextPath", req.getContextPath()); + resp.setContentType("text/html"); + resp.setCharacterEncoding("utf-8"); + Template template = ve.getTemplate("ureport-html/html-preview.html", "utf-8"); + PrintWriter writer = resp.getWriter(); + template.merge(context, writer); + writer.close(); + } + } + + private String buildTitle(HttpServletRequest req) { + String title = req.getParameter("_title"); + if (StringUtils.isBlank(title)) { + title = req.getParameter("_u"); + title = decode(title); + int point = title.lastIndexOf(".ureport.xml"); + if (point > -1) { + title = title.substring(0, point); + } + if (title.equals("p")) { + title = "设计中报表"; + } + } else { + title = decode(title); + } + return title + "-ureport"; + } + + private String convertJson(Collection data) { + if (data == null || data.size() == 0) { + return ""; + } + ObjectMapper mapper = new ObjectMapper(); + try { + String json = mapper.writeValueAsString(data); + return json; + } catch (Exception e) { + throw new ReportComputeException(e); + } + } + + public void loadData(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + HtmlReport htmlReport = loadReport(req); + writeObjectToJson(resp, htmlReport); + } + + public void loadPrintPages(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String file = req.getParameter("_u"); + file = decode(file); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + Map parameters = buildParameters(req); + ReportDefinition reportDefinition = null; + if (file.equals(PREVIEW_KEY)) { + reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do export excel."); + } + } else { + reportDefinition = reportRender.getReportDefinition(file); + } + Report report = reportBuilder.buildReport(reportDefinition, parameters); + Map chartMap = report.getContext().getChartDataMap(); + if (chartMap.size() > 0) { + CacheUtils.storeChartDataMap(chartMap); + } + FullPageData pageData = PageBuilder.buildFullPageData(report); + StringBuilder sb = new StringBuilder(); + List> list = pageData.getPageList(); + Context context = report.getContext(); + if (list.size() > 0) { + for (int i = 0; i < list.size(); i++) { + List columnPages = list.get(i); + if (i == 0) { + String html = htmlProducer.produce(context, columnPages, pageData.getColumnMargin(), false); + sb.append(html); + } else { + String html = htmlProducer.produce(context, columnPages, pageData.getColumnMargin(), false); + sb.append(html); + } + } + } else { + List pages = report.getPages(); + for (int i = 0; i < pages.size(); i++) { + Page page = pages.get(i); + if (i == 0) { + String html = htmlProducer.produce(context, page, false); + sb.append(html); + } else { + String html = htmlProducer.produce(context, page, true); + sb.append(html); + } + } + } + Map map = new HashMap(); + map.put("html", sb.toString()); + writeObjectToJson(resp, map); + } + + public void loadPagePaper(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String file = req.getParameter("_u"); + file = decode(file); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + ReportDefinition report = null; + if (file.equals(PREVIEW_KEY)) { + report = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (report == null) { + throw new ReportDesignException("Report data has expired."); + } + } else { + report = reportRender.getReportDefinition(file); + } + Paper paper = report.getPaper(); + writeObjectToJson(resp, paper); + } + + private HtmlReport loadReport(HttpServletRequest req) { + Map parameters = buildParameters(req); + HtmlReport htmlReport = null; + String file = req.getParameter("_u"); + file = decode(file); + String pageIndex = req.getParameter("_i"); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + if (file.equals(PREVIEW_KEY)) { + ReportDefinition reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do preview."); + } + Report report = reportBuilder.buildReport(reportDefinition, parameters); + Map chartMap = report.getContext().getChartDataMap(); + if (chartMap.size() > 0) { + CacheUtils.storeChartDataMap(chartMap); + } + htmlReport = new HtmlReport(); + String html = null; + if (StringUtils.isNotBlank(pageIndex) && !pageIndex.equals("0")) { + Context context = report.getContext(); + int index = Integer.valueOf(pageIndex); + SinglePageData pageData = PageBuilder.buildSinglePageData(index, report); + List pages = pageData.getPages(); + if (pages.size() == 1) { + Page page = pages.get(0); + html = htmlProducer.produce(context, page, false); + } else { + html = htmlProducer.produce(context, pages, pageData.getColumnMargin(), false); + } + htmlReport.setTotalPage(pageData.getTotalPages()); + htmlReport.setPageIndex(index); + } else { + html = htmlProducer.produce(report); + } + if (report.getPaper().isColumnEnabled()) { + htmlReport.setColumn(report.getPaper().getColumnCount()); + } + htmlReport.setChartDatas(report.getContext().getChartDataMap().values()); + htmlReport.setContent(html); + htmlReport.setTotalPage(report.getPages().size()); + htmlReport.setStyle(reportDefinition.getStyle()); + htmlReport.setSearchFormData(reportDefinition.buildSearchFormData(report.getContext().getDatasetMap(), parameters)); + htmlReport.setReportAlign(report.getPaper().getHtmlReportAlign().name()); + htmlReport.setHtmlIntervalRefreshValue(report.getPaper().getHtmlIntervalRefreshValue()); + } else { + if (StringUtils.isNotBlank(pageIndex) && !pageIndex.equals("0")) { + int index = Integer.valueOf(pageIndex); + htmlReport = exportManager.exportHtml(file, req.getContextPath(), parameters, index); + } else { + htmlReport = exportManager.exportHtml(file, req.getContextPath(), parameters); + } + } + return htmlReport; + } + + + private String buildCustomParameters(HttpServletRequest req) { + StringBuilder sb = new StringBuilder(); + Enumeration enumeration = req.getParameterNames(); + while (enumeration.hasMoreElements()) { + Object obj = enumeration.nextElement(); + if (obj == null) { + continue; + } + String name = obj.toString(); + String value = req.getParameter(name); + if (name == null || value == null || (name.startsWith("_") && !name.equals("_n"))) { + continue; + } + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(name); + sb.append("="); + sb.append(value); + } + return sb.toString(); + } + + private String buildExceptionMessage(Throwable throwable) { + Throwable root = buildRootException(throwable); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + root.printStackTrace(pw); + String trace = sw.getBuffer().toString(); + trace = trace.replaceAll("\n", "
"); + pw.close(); + return trace; + } + + public void setExportManager(ExportManager exportManager) { + this.exportManager = exportManager; + } + + public void setReportBuilder(ReportBuilder reportBuilder) { + this.reportBuilder = reportBuilder; + } + + public void setReportRender(ReportRender reportRender) { + this.reportRender = reportRender; + } + + @Override + public String url() { + return "/preview"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/image/ImageServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/image/ImageServletAction.java new file mode 100644 index 0000000..25e7efd --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/image/ImageServletAction.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.image; + +import com.bstek.ureport.cache.ResourceCache; +import com.bstek.ureport.console.ServletAction; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author Jacky.gao + * @since 2016年6月6日 + */ +public class ImageServletAction implements ServletAction { + public static final String URL = "/image"; + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String key = req.getParameter("_key"); + if (StringUtils.isNotBlank(key)) { + byte[] bytes = (byte[]) ResourceCache.getObject(key); + InputStream input = new ByteArrayInputStream(bytes); + OutputStream output = resp.getOutputStream(); + resp.setContentType("image/png"); + try { + IOUtils.copy(input, output); + } finally { + IOUtils.closeQuietly(input); + IOUtils.closeQuietly(output); + } + } else { + //processImage(req, resp); + } + } + + @Override + public String url() { + return URL; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/importexcel/ImportExcelServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/importexcel/ImportExcelServletAction.java new file mode 100644 index 0000000..e1f4fa0 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/importexcel/ImportExcelServletAction.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.importexcel; + +import com.bstek.ureport.console.RenderPageServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.definition.ReportDefinition; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springblade.core.tool.utils.MultipartUtil; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +/** + * @author Jacky.gao + * @since 2017年5月25日 + */ +public class ImportExcelServletAction extends RenderPageServletAction { + private List excelParsers = new ArrayList(); + + public ImportExcelServletAction() { + excelParsers.add(new HSSFExcelParser()); + excelParsers.add(new XSSFExcelParser()); + } + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String errorInfo; + ReportDefinition report = null; + try { + List items = MultipartUtil.extractMultipartFiles(req); + for (MultipartFile item : items) { + String fieldName = item.getName(); + String name = Objects.requireNonNull(item.getOriginalFilename()).toLowerCase(); + if (fieldName.equals("_excel_file") && (name.endsWith(".xls") || name.endsWith(".xlsx"))) { + InputStream inputStream = item.getInputStream(); + for (ExcelParser parser : excelParsers) { + if (parser.support(name)) { + report = parser.parse(inputStream); + break; + } + } + inputStream.close(); + break; + } + } + errorInfo = "请选择一个合法的Excel导入"; + } catch (Exception e) { + e.printStackTrace(); + errorInfo = e.getMessage(); + } + + Map result = new HashMap(); + if (report != null) { + result.put("result", true); + TempObjectCache.putObject("classpath:template/template.ureport.xml", report); + } else { + result.put("result", false); + if (errorInfo != null) { + result.put("errorInfo", errorInfo); + } + } + writeObjectToJson(resp, result); + } + + @Override + public String url() { + return "/import"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/pdf/ExportPdfServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/pdf/ExportPdfServletAction.java new file mode 100644 index 0000000..d52f477 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/pdf/ExportPdfServletAction.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.pdf; + +import com.bstek.ureport.build.ReportBuilder; +import com.bstek.ureport.console.BaseServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.Paper; +import com.bstek.ureport.definition.ReportDefinition; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.exception.ReportException; +import com.bstek.ureport.export.ExportConfigure; +import com.bstek.ureport.export.ExportConfigureImpl; +import com.bstek.ureport.export.ExportManager; +import com.bstek.ureport.export.ReportRender; +import com.bstek.ureport.export.pdf.PdfProducer; +import com.bstek.ureport.model.Report; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; +import org.codehaus.jackson.map.ObjectMapper; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年3月20日 + */ +public class ExportPdfServletAction extends BaseServletAction { + private ReportBuilder reportBuilder; + private ExportManager exportManager; + private ReportRender reportRender; + private PdfProducer pdfProducer = new PdfProducer(); + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } else { + buildPdf(req, resp, false); + } + } + + public void show(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + buildPdf(req, resp, true); + } + + public void buildPdf(HttpServletRequest req, HttpServletResponse resp, boolean forPrint) throws IOException { + String file = req.getParameter("_u"); + file = decode(file); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + OutputStream outputStream = null; + try { + String fileName = req.getParameter("_n"); + fileName = buildDownloadFileName(file, fileName, ".pdf"); + fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1"); + if (forPrint) { + resp.setContentType("application/pdf"); + resp.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\""); + } else { + resp.setContentType("application/octet-stream;charset=ISO8859-1"); + resp.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\""); + } + outputStream = resp.getOutputStream(); + Map parameters = buildParameters(req); + if (file.equals(PREVIEW_KEY)) { + ReportDefinition reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do export pdf."); + } + Report report = reportBuilder.buildReport(reportDefinition, parameters); + pdfProducer.produce(report, outputStream); + } else { + ExportConfigure configure = new ExportConfigureImpl(file, parameters, outputStream); + exportManager.exportPdf(configure); + } + } catch (Exception ex) { + throw new ReportException(ex); + } finally { + outputStream.flush(); + outputStream.close(); + } + } + + public void newPaging(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String file = req.getParameter("_u"); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + Report report = null; + Map parameters = buildParameters(req); + if (file.equals(PREVIEW_KEY)) { + ReportDefinition reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do export pdf."); + } + report = reportBuilder.buildReport(reportDefinition, parameters); + } else { + ReportDefinition reportDefinition = reportRender.getReportDefinition(file); + report = reportRender.render(reportDefinition, parameters); + } + String paper = req.getParameter("_paper"); + ObjectMapper mapper = new ObjectMapper(); + Paper newPaper = mapper.readValue(paper, Paper.class); + report.rePaging(newPaper); + } + + public void setReportRender(ReportRender reportRender) { + this.reportRender = reportRender; + } + + public void setExportManager(ExportManager exportManager) { + this.exportManager = exportManager; + } + + public void setReportBuilder(ReportBuilder reportBuilder) { + this.reportBuilder = reportBuilder; + } + + @Override + public String url() { + return "/pdf"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/res/ResourceLoaderServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/res/ResourceLoaderServletAction.java new file mode 100644 index 0000000..c5f0324 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/res/ResourceLoaderServletAction.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.res; + +import com.bstek.ureport.console.ServletAction; +import com.bstek.ureport.console.UReportServlet; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author Jacky.gao + * @since 2016年6月6日 + */ +public class ResourceLoaderServletAction implements ServletAction, ApplicationContextAware { + public static final String URL = "/res"; + private ApplicationContext applicationContext; + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = req.getContextPath() + UReportServlet.URL + URL; + String uri = req.getRequestURI(); + String resPath = uri.substring(path.length() + 1); + String p = "classpath:" + resPath; + if (p.endsWith(".js")) { + resp.setContentType("text/javascript"); + } else if (p.endsWith(".css")) { + resp.setContentType("text/css"); + } else if (p.endsWith(".png")) { + resp.setContentType("image/png"); + } else if (p.endsWith(".jpg")) { + resp.setContentType("image/jpeg"); + } else if (p.endsWith(".svg")) { + resp.setContentType("image/svg+xml"); + } else { + resp.setContentType("application/octet-stream"); + } + InputStream input = applicationContext.getResource(p).getInputStream(); + OutputStream output = resp.getOutputStream(); + try { + IOUtils.copy(input, output); + } finally { + IOUtils.closeQuietly(input); + IOUtils.closeQuietly(output); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public String url() { + return URL; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/console/word/ExportWordServletAction.java b/blade-core-report/src/main/java/com/bstek/ureport/console/word/ExportWordServletAction.java new file mode 100644 index 0000000..e4ec473 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/console/word/ExportWordServletAction.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.console.word; + +import com.bstek.ureport.build.ReportBuilder; +import com.bstek.ureport.console.BaseServletAction; +import com.bstek.ureport.console.cache.TempObjectCache; +import com.bstek.ureport.console.exception.ReportDesignException; +import com.bstek.ureport.definition.ReportDefinition; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.exception.ReportException; +import com.bstek.ureport.export.ExportConfigure; +import com.bstek.ureport.export.ExportConfigureImpl; +import com.bstek.ureport.export.ExportManager; +import com.bstek.ureport.export.word.high.WordProducer; +import com.bstek.ureport.model.Report; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年4月17日 + */ +public class ExportWordServletAction extends BaseServletAction { + private ReportBuilder reportBuilder; + private ExportManager exportManager; + private WordProducer wordProducer = new WordProducer(); + + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String method = retriveMethod(req); + if (method != null) { + invokeMethod(method, req, resp); + } else { + buildWord(req, resp); + } + } + + public void buildWord(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String file = req.getParameter("_u"); + file = decode(file); + if (StringUtils.isBlank(file)) { + throw new ReportComputeException("Report file can not be null."); + } + OutputStream outputStream = resp.getOutputStream(); + try { + String fileName = req.getParameter("_n"); + fileName = buildDownloadFileName(file, fileName, ".docx"); + fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1"); + resp.setContentType("application/octet-stream;charset=ISO8859-1"); + resp.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\""); + Map parameters = buildParameters(req); + if (file.equals(PREVIEW_KEY)) { + ReportDefinition reportDefinition = (ReportDefinition) TempObjectCache.getObject(PREVIEW_KEY); + if (reportDefinition == null) { + throw new ReportDesignException("Report data has expired,can not do export word."); + } + Report report = reportBuilder.buildReport(reportDefinition, parameters); + wordProducer.produce(report, outputStream); + } else { + ExportConfigure configure = new ExportConfigureImpl(file, parameters, outputStream); + exportManager.exportWord(configure); + } + } catch (Exception ex) { + throw new ReportException(ex); + } finally { + outputStream.flush(); + outputStream.close(); + } + } + + public void setReportBuilder(ReportBuilder reportBuilder) { + this.reportBuilder = reportBuilder; + } + + public void setExportManager(ExportManager exportManager) { + this.exportManager = exportManager; + } + + @Override + public String url() { + return "/word"; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/definition/CellStyle.java b/blade-core-report/src/main/java/com/bstek/ureport/definition/CellStyle.java new file mode 100644 index 0000000..b2440b2 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/definition/CellStyle.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.definition; + +import com.bstek.ureport.export.pdf.font.FontBuilder; +import org.apache.commons.lang.StringUtils; +import org.codehaus.jackson.annotate.JsonIgnore; + +import java.awt.*; +import java.io.Serializable; + + +/** + * @author Jacky.gao + * @since 2017年1月18日 + */ +public class CellStyle implements Serializable { + private static final long serialVersionUID = 8327688051735343849L; + private String bgcolor; + private String forecolor; + private int fontSize; + private String fontFamily; + private String format; + private float lineHeight; + private Alignment align; + private Alignment valign; + private Boolean bold; + private Boolean italic; + private Boolean underline; + private Boolean wrapCompute; + private Border leftBorder; + private Border rightBorder; + private Border topBorder; + private Border bottomBorder; + + private Font font; + + public Border getLeftBorder() { + return leftBorder; + } + + public void setLeftBorder(Border leftBorder) { + this.leftBorder = leftBorder; + } + + public Border getRightBorder() { + return rightBorder; + } + + public void setRightBorder(Border rightBorder) { + this.rightBorder = rightBorder; + } + + public Border getTopBorder() { + return topBorder; + } + + public void setTopBorder(Border topBorder) { + this.topBorder = topBorder; + } + + public Border getBottomBorder() { + return bottomBorder; + } + + public void setBottomBorder(Border bottomBorder) { + this.bottomBorder = bottomBorder; + } + + public String getBgcolor() { + return bgcolor; + } + + public void setBgcolor(String bgcolor) { + this.bgcolor = bgcolor; + } + + public String getForecolor() { + return forecolor; + } + + public void setForecolor(String forecolor) { + this.forecolor = forecolor; + } + + public int getFontSize() { + return fontSize; + } + + public void setFontSize(int fontSize) { + this.fontSize = fontSize; + } + + public String getFontFamily() { + return fontFamily; + } + + public void setFontFamily(String fontFamily) { + this.fontFamily = fontFamily; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public Alignment getAlign() { + return align; + } + + public void setAlign(Alignment align) { + this.align = align; + } + + public Alignment getValign() { + return valign; + } + + public void setValign(Alignment valign) { + this.valign = valign; + } + + public Boolean getBold() { + return bold; + } + + public void setBold(Boolean bold) { + this.bold = bold; + } + + public Boolean getItalic() { + return italic; + } + + public void setItalic(Boolean italic) { + this.italic = italic; + } + + public Boolean getUnderline() { + return underline; + } + + public void setUnderline(Boolean underline) { + this.underline = underline; + } + + public Boolean getWrapCompute() { + return wrapCompute; + } + + public void setWrapCompute(Boolean wrapCompute) { + this.wrapCompute = wrapCompute; + } + + public void setFont(Font font) { + this.font = font; + } + + public float getLineHeight() { + return lineHeight; + } + + public void setLineHeight(float lineHeight) { + this.lineHeight = lineHeight; + } + + @JsonIgnore + public Font getFont() { + if (this.font == null) { + int fontStyle = Font.PLAIN; + if ((bold != null && bold) && (italic != null && italic)) { + fontStyle = Font.BOLD | Font.ITALIC; + } else if (bold != null && bold) { + fontStyle = Font.BOLD; + } else if (italic != null && italic) { + fontStyle = Font.ITALIC; + } + String fontName = fontFamily; + if (StringUtils.isBlank(fontName)) { + fontName = "宋体"; + } + this.font = FontBuilder.getAwtFont(fontName, fontStyle, (float) fontSize); + } + return this.font; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/font/FontBuilder.java b/blade-core-report/src/main/java/com/bstek/ureport/font/FontBuilder.java new file mode 100644 index 0000000..d378153 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/font/FontBuilder.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.font; + +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.exception.ReportException; +import com.bstek.ureport.export.pdf.font.FontRegister; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Font; +import com.itextpdf.text.FontFactory; +import com.itextpdf.text.pdf.BaseFont; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.*; + +/** + * @author Jacky.gao + * @since 2014年4月22日 + */ +public class FontBuilder implements ApplicationContextAware { + private static ApplicationContext applicationContext; + private static final Map fontMap = new HashMap(); + public static final Map fontPathMap = new HashMap(); + private static List systemFontNameList = new ArrayList(); + + public static Font getFont(String fontName, int fontSize, boolean fontBold, boolean fontItalic, boolean underLine) { + BaseFont baseFont = fontMap.get(fontName); + Font font = null; + if (baseFont != null) { + font = new Font(baseFont); + } else { + font = FontFactory.getFont(fontName); + } + font.setSize(fontSize); + int fontStyle = Font.NORMAL; + if (fontBold && fontItalic && underLine) { + fontStyle = Font.BOLD | Font.ITALIC | Font.UNDERLINE; + } else if (fontBold) { + if (fontItalic) { + fontStyle = Font.BOLD | Font.ITALIC; + } else if (underLine) { + fontStyle = Font.BOLD | Font.UNDERLINE; + } else { + fontStyle = Font.BOLD; + } + } else if (fontItalic) { + if (underLine) { + fontStyle = Font.ITALIC | Font.UNDERLINE; + } else if (fontBold) { + fontStyle = Font.ITALIC | Font.BOLD; + } else { + fontStyle = Font.ITALIC; + } + } else if (underLine) { + fontStyle = Font.UNDERLINE; + } + font.setStyle(fontStyle); + return font; + } + + public static java.awt.Font getAwtFont(String fontName, int fontStyle, float size) { + if (systemFontNameList.contains(fontName)) { + return new java.awt.Font(fontName, fontStyle, Integer.parseInt(String.valueOf(size))); + } + String fontPath = fontPathMap.get(fontName); + if (fontPath == null) { + fontName = "宋体"; + fontPath = fontPathMap.get(fontName); + if (fontPath == null) { + return null; + } + } + InputStream inputStream = null; + try { + inputStream = applicationContext.getResource(fontPath).getInputStream(); + java.awt.Font font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, inputStream); + return font.deriveFont(fontStyle, size); + } catch (Exception e) { + throw new ReportException(e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + FontBuilder.applicationContext = applicationContext; + GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String[] fontNames = environment.getAvailableFontFamilyNames(); + for (String name : fontNames) { + systemFontNameList.add(name); + } + Collection fontRegisters = applicationContext.getBeansOfType(FontRegister.class).values(); + for (FontRegister fontReg : fontRegisters) { + String fontName = fontReg.getFontName(); + String fontPath = fontReg.getFontPath(); + if (StringUtils.isEmpty(fontPath) || StringUtils.isEmpty(fontName)) { + continue; + } + try { + BaseFont baseFont = getIdentityFont(fontName, fontPath, applicationContext); + if (baseFont == null) { + throw new ReportComputeException("Font " + fontPath + " does not exist"); + } + fontMap.put(fontName, baseFont); + } catch (Exception e) { + e.printStackTrace(); + throw new ReportComputeException(e); + } + } + } + + private BaseFont getIdentityFont(String fontFamily, String fontPath, ApplicationContext applicationContext) throws DocumentException, IOException { + if (!fontPath.startsWith(ApplicationContext.CLASSPATH_URL_PREFIX)) { + fontPath = ApplicationContext.CLASSPATH_URL_PREFIX + fontPath; + } + String fontName = fontPath; + int lastSlashPos = fontPath.lastIndexOf("/"); + if (lastSlashPos != -1) { + fontName = fontPath.substring(lastSlashPos + 1, fontPath.length()); + } + if (fontName.toLowerCase().endsWith(".ttc")) { + fontName = fontName + ",0"; + } + InputStream inputStream = null; + try { + fontPathMap.put(fontFamily, fontPath); + inputStream = applicationContext.getResource(fontPath).getInputStream(); + byte[] bytes = IOUtils.toByteArray(inputStream); + BaseFont baseFont = BaseFont.createFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, bytes, null); + baseFont.setSubset(true); + return baseFont; + } finally { + if (inputStream != null) inputStream.close(); + } + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/parser/SlashBuilder.java b/blade-core-report/src/main/java/com/bstek/ureport/parser/SlashBuilder.java new file mode 100644 index 0000000..a191e84 --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/parser/SlashBuilder.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.parser; + +import com.bstek.ureport.cache.ResourceCache; +import com.bstek.ureport.definition.*; +import com.bstek.ureport.definition.value.Slash; +import com.bstek.ureport.definition.value.SlashValue; +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.utils.UnitUtils; +import org.springblade.core.tool.utils.Base64Util; + +import javax.imageio.ImageIO; +import javax.imageio.stream.MemoryCacheImageOutputStream; +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; + +/** + * @author Jacky.gao + * @since 2017年3月17日 + */ +public class SlashBuilder { + public void buildSlashImage(CellDefinition cell, ReportDefinition report) { + int rowNumber = cell.getRowNumber(); + int colNumber = cell.getColumnNumber(); + int rowSpan = cell.getRowSpan(); + int colSpan = cell.getColSpan(); + int verticalBorderWidth = 0, horizontalBorderWidth = 0; + CellStyle cellStyle = cell.getCellStyle(); + if (cellStyle.getLeftBorder() != null) { + verticalBorderWidth += cellStyle.getLeftBorder().getWidth(); + } + if (cellStyle.getRightBorder() != null) { + verticalBorderWidth += cellStyle.getRightBorder().getWidth(); + } + if (cellStyle.getTopBorder() != null) { + horizontalBorderWidth = cellStyle.getTopBorder().getWidth(); + } + if (cellStyle.getBottomBorder() != null) { + horizontalBorderWidth = cellStyle.getBottomBorder().getWidth(); + } + int width = 0; + int height = 0; + if (rowSpan == 0) { + rowSpan = 1; + } + if (colSpan == 0) { + colSpan = 1; + } + List columns = report.getColumns(); + List rows = report.getRows(); + for (int i = colNumber; i < (colNumber + colSpan); i++) { + ColumnDefinition col = columns.get(i - 1); + width += UnitUtils.pointToPixel(col.getWidth()); + } + for (int i = rowNumber; i < (rowNumber + rowSpan); i++) { + RowDefinition row = rows.get(i - 1); + height += UnitUtils.pointToPixel(row.getHeight()); + } + width -= horizontalBorderWidth; + height -= verticalBorderWidth; + SlashValue content = (SlashValue) cell.getValue(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); + Graphics2D g = (Graphics2D) image.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + Font font = cellStyle.getFont(); + g.setFont(font); + g.setStroke(new BasicStroke(1f)); + String bgColor = cellStyle.getBgcolor(); + if (bgColor == null) { + bgColor = "255,255,255"; + } + g.setColor(getColor(bgColor)); + g.fillRect(0, 0, width, height); + AffineTransform transform = g.getTransform(); + int allRowHeight = 0; + int index = 0; + String lc = cellStyle.getForecolor(); + if (lc == null) { + lc = "0,0,0"; + } + Color lineColor = getColor(lc); + String fc = cellStyle.getForecolor(); + if (fc == null) { + fc = "0,0,0"; + } + Color fontColor = getColor(fc); + for (int i = rowNumber; i < (rowNumber + rowSpan); i++) { + Slash slash = getSlash(content, index); + if (slash == null) { + break; + } + String text = slash.getText(); + if (text == null) { + break; + } + RowDefinition row = rows.get(i - 1); + int rowHeight = UnitUtils.pointToPixel(row.getHeight()); + g.setColor(fontColor); + int x = slash.getX(); + int y = slash.getY(); + g.rotate(Math.toRadians(slash.getDegree()), x, y); + g.drawString(text, x, y); + g.setTransform(transform); + g.setColor(lineColor); + int h = allRowHeight + rowHeight; + if (i == (rowNumber + rowSpan - 1)) { + h = allRowHeight + (rowHeight / 3) * 2; + } + g.drawLine(0, 0, width, h); + allRowHeight += rowHeight; + index++; + } + Slash slash = getSlash(content, index); + if (slash != null) { + String text = slash.getText(); + if (text != null) { + int x = slash.getX(); + int y = slash.getY(); + g.rotate(Math.toRadians(slash.getDegree()), x, y); + g.setColor(fontColor); + g.drawString(text, x, y); + g.setTransform(transform); + index++; + } + } + if (colSpan > 0) { + colSpan--; + } + int colNumberStart = colNumber + colSpan; + for (int i = colNumberStart; i > (colNumber - 1); i--) { + slash = getSlash(content, index); + if (slash == null) { + break; + } + String text = slash.getText(); + if (text == null) { + break; + } + int x = slash.getX(); + int y = slash.getY(); + g.rotate(Math.toRadians(slash.getDegree()), x, y); + g.setColor(fontColor); + g.drawString(text, x, y); + g.setTransform(transform); + ColumnDefinition col = columns.get(i - 1); + int colWidth = UnitUtils.pointToPixel(col.getWidth()); + int w = width; + if (i == colNumberStart) { + w = width - colWidth / 3; + } + g.setColor(lineColor); + g.drawLine(0, 0, w, height); + width -= colWidth; + index++; + } + byte[] imageBytes = null; + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + MemoryCacheImageOutputStream memoryImage = new MemoryCacheImageOutputStream(byteOutput); + try { + ImageIO.write(image, "png", memoryImage); + imageBytes = byteOutput.toByteArray(); + String base64Data = Base64Util.encodeToString(imageBytes); + content.setBase64Data(base64Data); + } catch (Exception ex) { + throw new ReportComputeException(ex); + } finally { + try { + if (memoryImage != null) { + memoryImage.close(); + } + if (byteOutput != null) { + byteOutput.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + g.dispose(); + String imageByteKey = buildKey(report.getReportFullName(), cell.getName()); + ResourceCache.putObject(imageByteKey, imageBytes); + } + + private Slash getSlash(SlashValue content, int index) { + List slashes = content.getSlashes(); + if (index < slashes.size()) { + return slashes.get(index); + } + return null; + } + + private Color getColor(String text) { + if (text == null) { + return null; + } + String[] str = text.split(","); + return new Color(Integer.valueOf(str[0]), Integer.valueOf(str[1]), Integer.valueOf(str[2])); + } + + public static String buildKey(String reportFullName, String cellName) { + return "slash-" + reportFullName + "-" + cellName; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/provider/image/DefaultImageProvider.java b/blade-core-report/src/main/java/com/bstek/ureport/provider/image/DefaultImageProvider.java new file mode 100644 index 0000000..d9261be --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/provider/image/DefaultImageProvider.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.provider.image; + +import com.bstek.ureport.exception.ReportComputeException; +import jakarta.servlet.ServletContext; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.util.ResourceUtils; +import org.springframework.web.context.ServletContextAware; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Jacky.gao + * @since 2017年3月6日 + */ +public class DefaultImageProvider implements ImageProvider, ApplicationContextAware, ServletContextAware { + private ApplicationContext applicationContext; + private String baseWebPath; + + @Override + public InputStream getImage(String path) { + try { + if (path.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX) || path.startsWith("/WEB-INF")) { + return applicationContext.getResource(path).getInputStream(); + } else { + path = baseWebPath + path; + return new FileInputStream(path); + } + } catch (IOException e) { + throw new ReportComputeException(e); + } + } + + @Override + public boolean support(String path) { + if (path.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) { + return true; + } else if (baseWebPath != null && (path.startsWith("/") || path.startsWith("/WEB-INF"))) { + return true; + } + return false; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void setServletContext(ServletContext servletContext) { + this.baseWebPath = servletContext.getRealPath("/"); + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/provider/report/file/FileReportProvider.java b/blade-core-report/src/main/java/com/bstek/ureport/provider/report/file/FileReportProvider.java new file mode 100644 index 0000000..c62ca5b --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/provider/report/file/FileReportProvider.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.provider.report.file; + +import com.bstek.ureport.exception.ReportException; +import com.bstek.ureport.provider.report.ReportFile; +import com.bstek.ureport.provider.report.ReportProvider; +import jakarta.servlet.ServletContext; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.web.context.WebApplicationContext; + +import java.io.*; +import java.util.*; + +/** + * @author Jacky.gao + * @since 2017年2月11日 + */ +public class FileReportProvider implements ReportProvider, ApplicationContextAware { + private String prefix = "file:"; + private String fileStoreDir; + private boolean disabled; + + @Override + public InputStream loadReport(String file) { + if (file.startsWith(prefix)) { + file = file.substring(prefix.length(), file.length()); + } + String fullPath = fileStoreDir + "/" + file; + try { + return new FileInputStream(fullPath); + } catch (FileNotFoundException e) { + throw new ReportException(e); + } + } + + @Override + public void deleteReport(String file) { + if (file.startsWith(prefix)) { + file = file.substring(prefix.length(), file.length()); + } + String fullPath = fileStoreDir + "/" + file; + File f = new File(fullPath); + if (f.exists()) { + f.delete(); + } + } + + @Override + public List getReportFiles() { + File file = new File(fileStoreDir); + List list = new ArrayList(); + for (File f : file.listFiles()) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(f.lastModified()); + list.add(new ReportFile(f.getName(), calendar.getTime())); + } + Collections.sort(list, new Comparator() { + @Override + public int compare(ReportFile f1, ReportFile f2) { + return f2.getUpdateDate().compareTo(f1.getUpdateDate()); + } + }); + return list; + } + + @Override + public String getName() { + return "服务器文件系统"; + } + + @Override + public void saveReport(String file, String content) { + if (file.startsWith(prefix)) { + file = file.substring(prefix.length(), file.length()); + } + String fullPath = fileStoreDir + "/" + file; + FileOutputStream outStream = null; + try { + outStream = new FileOutputStream(new File(fullPath)); + IOUtils.write(content, outStream, "utf-8"); + } catch (Exception ex) { + throw new ReportException(ex); + } finally { + if (outStream != null) { + try { + outStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + } + + @Override + public boolean disabled() { + return disabled; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public void setFileStoreDir(String fileStoreDir) { + this.fileStoreDir = fileStoreDir; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + File file = new File(fileStoreDir); + if (file.exists()) { + return; + } + if (applicationContext instanceof WebApplicationContext) { + WebApplicationContext context = (WebApplicationContext) applicationContext; + ServletContext servletContext = context.getServletContext(); + String basePath = servletContext.getRealPath("/"); + fileStoreDir = basePath + fileStoreDir; + file = new File(fileStoreDir); + if (!file.exists()) { + file.mkdirs(); + } + } + } + + @Override + public String getPrefix() { + return prefix; + } +} diff --git a/blade-core-report/src/main/java/com/bstek/ureport/utils/ImageUtils.java b/blade-core-report/src/main/java/com/bstek/ureport/utils/ImageUtils.java new file mode 100644 index 0000000..9ce9e6f --- /dev/null +++ b/blade-core-report/src/main/java/com/bstek/ureport/utils/ImageUtils.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright 2017 Bstek + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.bstek.ureport.utils; + +import com.bstek.ureport.exception.ReportComputeException; +import com.bstek.ureport.image.ChartImageProcessor; +import com.bstek.ureport.image.ImageProcessor; +import com.bstek.ureport.image.ImageType; +import com.bstek.ureport.image.StaticImageProcessor; +import org.apache.commons.io.IOUtils; +import org.springblade.core.tool.utils.Base64Util; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Jacky.gao + * @since 2017年3月20日 + */ +public class ImageUtils { + private static Map> imageProcessorMap = new HashMap>(); + + static { + StaticImageProcessor staticImageProcessor = new StaticImageProcessor(); + imageProcessorMap.put(ImageType.image, staticImageProcessor); + ChartImageProcessor chartImageProcessor = new ChartImageProcessor(); + imageProcessorMap.put(ImageType.chart, chartImageProcessor); + } + + public static InputStream base64DataToInputStream(String base64Data) { + byte[] bytes = Base64Util.decodeFromString(base64Data); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + return inputStream; + } + + @SuppressWarnings("unchecked") + public static String getImageBase64Data(ImageType type, Object data, int width, int height) { + ImageProcessor targetProcessor = (ImageProcessor) imageProcessorMap.get(type); + if (targetProcessor == null) { + throw new ReportComputeException("Unknow image type :" + type); + } + InputStream inputStream = targetProcessor.getImage(data); + try { + if (width > 0 && height > 0) { + BufferedImage inputImage = ImageIO.read(inputStream); + BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB); + Graphics2D g = outputImage.createGraphics(); + g.drawImage(inputImage, 0, 0, width, height, null); + g.dispose(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageIO.write(outputImage, "png", outputStream); + inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + } + byte[] bytes = IOUtils.toByteArray(inputStream); + return Base64Util.encodeToString(bytes); + } catch (Exception ex) { + throw new ReportComputeException(ex); + } finally { + IOUtils.closeQuietly(inputStream); + } + } +} diff --git a/blade-core-report/src/main/java/org/springblade/core/report/config/ReportConfiguration.java b/blade-core-report/src/main/java/org/springblade/core/report/config/ReportConfiguration.java index a2c7c03..ad526f7 100644 --- a/blade-core-report/src/main/java/org/springblade/core/report/config/ReportConfiguration.java +++ b/blade-core-report/src/main/java/org/springblade/core/report/config/ReportConfiguration.java @@ -32,7 +32,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ImportResource; import org.springframework.core.annotation.Order; -import javax.servlet.Servlet; +import jakarta.servlet.Servlet; /** * UReport配置类 diff --git a/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportBootEndpoint.java b/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportBootEndpoint.java index 2879ee3..2ea616b 100644 --- a/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportBootEndpoint.java +++ b/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportBootEndpoint.java @@ -19,14 +19,14 @@ import org.springblade.core.launch.constant.AppConstant; import org.springblade.core.report.service.IReportFileService; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import springfox.documentation.annotations.ApiIgnore; +import io.swagger.v3.oas.annotations.Hidden; /** * UReport Boot版 API端点 * * @author Chill */ -@ApiIgnore +@Hidden @RestController @RequestMapping(AppConstant.APPLICATION_REPORT_NAME + "/report/rest") public class ReportBootEndpoint extends ReportEndpoint { diff --git a/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportEndpoint.java b/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportEndpoint.java index 0ea59aa..80db92a 100644 --- a/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportEndpoint.java +++ b/blade-core-report/src/main/java/org/springblade/core/report/endpoint/ReportEndpoint.java @@ -24,7 +24,7 @@ import org.springblade.core.report.service.IReportFileService; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.Func; import org.springframework.web.bind.annotation.*; -import springfox.documentation.annotations.ApiIgnore; +import io.swagger.v3.oas.annotations.Hidden; import java.util.Map; @@ -33,7 +33,7 @@ import java.util.Map; * * @author Chill */ -@ApiIgnore +@Hidden @RestController @AllArgsConstructor @RequestMapping("/report/rest") diff --git a/blade-core-report/src/main/java/org/springblade/core/report/entity/ReportFileEntity.java b/blade-core-report/src/main/java/org/springblade/core/report/entity/ReportFileEntity.java index 1e60c61..ea8248d 100644 --- a/blade-core-report/src/main/java/org/springblade/core/report/entity/ReportFileEntity.java +++ b/blade-core-report/src/main/java/org/springblade/core/report/entity/ReportFileEntity.java @@ -21,6 +21,7 @@ import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import java.io.Serial; import java.io.Serializable; import java.util.Date; @@ -33,6 +34,7 @@ import java.util.Date; @TableName("blade_report_file") public class ReportFileEntity implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-secure/pom.xml b/blade-core-secure/pom.xml index 8b730b3..fdbfcd0 100644 --- a/blade-core-secure/pom.xml +++ b/blade-core-secure/pom.xml @@ -19,12 +19,10 @@ io.jsonwebtoken jjwt-impl - 0.11.2 io.jsonwebtoken jjwt-jackson - 0.11.2 diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java b/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java index 889bf0d..78875ea 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java @@ -17,8 +17,7 @@ package org.springblade.core.secure; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -27,31 +26,31 @@ import lombok.Data; * @author Chill */ @Data -@ApiModel(description = "认证信息") +@Schema(description = "认证信息") public class AuthInfo { - @ApiModelProperty(value = "令牌") + @Schema(description = "令牌") private String accessToken; - @ApiModelProperty(value = "令牌类型") + @Schema(description = "令牌类型") private String tokenType; - @ApiModelProperty(value = "刷新令牌") + @Schema(description = "刷新令牌") private String refreshToken; - @ApiModelProperty(value = "用户ID") + @Schema(description = "用户ID") @JsonSerialize(using = ToStringSerializer.class) private Long userId; - @ApiModelProperty(value = "租户ID") + @Schema(description = "租户ID") private String tenantId; - @ApiModelProperty(value = "第三方系统ID") + @Schema(description = "第三方系统ID") private String oauthId; - @ApiModelProperty(value = "头像") + @Schema(description = "头像") private String avatar = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"; - @ApiModelProperty(value = "角色名") + @Schema(description = "角色名") private String authority; - @ApiModelProperty(value = "用户名") + @Schema(description = "用户名") private String userName; - @ApiModelProperty(value = "账号名") + @Schema(description = "账号名") private String account; - @ApiModelProperty(value = "过期时间") + @Schema(description = "过期时间") private long expiresIn; - @ApiModelProperty(value = "许可证") + @Schema(description = "许可证") private String license = "powered by blade"; } diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java b/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java index d9958ea..3a23cf6 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java @@ -15,11 +15,14 @@ */ package org.springblade.core.secure; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.io.Serial; import java.io.Serializable; +import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY; + /** * 用户实体 * @@ -28,47 +31,48 @@ import java.io.Serializable; @Data public class BladeUser implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** * 客户端id */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String clientId; /** * 用户id */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private Long userId; /** * 租户ID */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String tenantId; /** * 部门id */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String deptId; /** * 昵称 */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String userName; /** * 账号 */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String account; /** * 角色id */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String roleId; /** * 角色名 */ - @ApiModelProperty(hidden = true) + @Schema(accessMode = READ_ONLY) private String roleName; } diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java b/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java index eec53d2..4389037 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java @@ -23,7 +23,7 @@ import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.StringUtil; import org.springblade.core.tool.utils.WebUtil; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Objects; /** diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java b/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java index d22fe3f..0ec5d52 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java @@ -28,8 +28,8 @@ import org.springblade.core.tool.utils.WebUtil; import org.springframework.http.MediaType; import org.springframework.web.servlet.AsyncHandlerInterceptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Objects; diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java b/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java index 74cd559..ae27527 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java @@ -26,8 +26,8 @@ import org.springblade.core.tool.utils.WebUtil; import org.springframework.http.MediaType; import org.springframework.web.servlet.AsyncHandlerInterceptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Objects; diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java b/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java index 6df96b1..d4a2c10 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java @@ -15,7 +15,7 @@ */ package org.springblade.core.secure.provider; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -29,23 +29,23 @@ public class ClientDetails implements IClientDetails { /** * 客户端id */ - @ApiModelProperty(value = "客户端id") + @Schema(description = "客户端id") private String clientId; /** * 客户端密钥 */ - @ApiModelProperty(value = "客户端密钥") + @Schema(description = "客户端密钥") private String clientSecret; /** * 令牌过期秒数 */ - @ApiModelProperty(value = "令牌过期秒数") + @Schema(description = "令牌过期秒数") private Integer accessTokenValidity; /** * 刷新令牌过期秒数 */ - @ApiModelProperty(value = "刷新令牌过期秒数") + @Schema(description = "刷新令牌过期秒数") private Integer refreshTokenValidity; } diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java b/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java index 978b0b7..e4b3530 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java @@ -37,10 +37,10 @@ public class SecureRegistry { public SecureRegistry() { this.defaultExcludePatterns.add("/actuator/health/**"); - this.defaultExcludePatterns.add("/v2/api-docs/**"); + this.defaultExcludePatterns.add("/v3/api-docs/**"); + this.defaultExcludePatterns.add("/swagger-ui/**"); this.defaultExcludePatterns.add("/auth/**"); this.defaultExcludePatterns.add("/token/**"); - this.defaultExcludePatterns.add("/log/**"); this.defaultExcludePatterns.add("/user/user-info"); this.defaultExcludePatterns.add("/user/user-info-by-id"); this.defaultExcludePatterns.add("/menu/auth-routes"); diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java b/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java index bef24e8..37887e5 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java +++ b/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java @@ -32,7 +32,7 @@ import org.springblade.core.tool.constant.RoleConstant; import org.springblade.core.tool.utils.*; import javax.crypto.spec.SecretKeySpec; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.security.Key; import java.util.*; diff --git a/blade-core-social/pom.xml b/blade-core-social/pom.xml index 21612c3..960d31f 100644 --- a/blade-core-social/pom.xml +++ b/blade-core-social/pom.xml @@ -23,7 +23,6 @@ me.zhyd.oauth JustAuth - 1.15.8 org.apache.httpcomponents diff --git a/blade-core-social/src/main/java/org/springblade/core/social/utils/SocialUtil.java b/blade-core-social/src/main/java/org/springblade/core/social/utils/SocialUtil.java index d3e6140..1187521 100644 --- a/blade-core-social/src/main/java/org/springblade/core/social/utils/SocialUtil.java +++ b/blade-core-social/src/main/java/org/springblade/core/social/utils/SocialUtil.java @@ -60,7 +60,10 @@ public class SocialUtil { authRequest = new AuthWeChatOpenRequest(authConfig); break; case WECHAT_ENTERPRISE: - authRequest = new AuthWeChatEnterpriseRequest(authConfig); + authRequest = new AuthWeChatEnterpriseQrcodeRequest(authConfig); + break; + case WECHAT_ENTERPRISE_WEB: + authRequest = new AuthWeChatEnterpriseWebRequest(authConfig); break; case WECHAT_MP: authRequest = new AuthWeChatMpRequest(authConfig); diff --git a/blade-core-swagger/pom.xml b/blade-core-swagger/pom.xml index 88d8529..50e5326 100644 --- a/blade-core-swagger/pom.xml +++ b/blade-core-swagger/pom.xml @@ -23,12 +23,7 @@ com.github.xiaoymin - knife4j-openapi2-spring-boot-starter - - - com.google.guava - guava - ${guava.version} + knife4j-openapi3-jakarta-spring-boot-starter diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/EnableSwagger.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/EnableSwagger.java index ab12ea7..ce62f5c 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/EnableSwagger.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/EnableSwagger.java @@ -15,7 +15,6 @@ */ package org.springblade.core.swagger; -import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; import java.lang.annotation.*; @@ -27,6 +26,5 @@ import java.lang.annotation.*; @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -@EnableSwagger2WebMvc public @interface EnableSwagger { } diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java index 9ea3871..eed0b43 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java @@ -16,26 +16,27 @@ package org.springblade.core.swagger; -import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver; -import com.google.common.collect.Lists; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import lombok.AllArgsConstructor; -import org.springblade.core.launch.props.BladeProperties; -import org.springframework.boot.autoconfigure.AutoConfiguration; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springdoc.core.configuration.SpringDocConfiguration; +import org.springdoc.core.customizers.GlobalOpenApiCustomizer; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.service.*; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spi.service.contexts.SecurityContext; -import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; -import springfox.documentation.spring.web.plugins.Docket; +import org.springframework.context.annotation.Configuration; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; /** @@ -43,107 +44,119 @@ import java.util.List; * * @author Chill */ +@Slf4j @EnableSwagger -@AutoConfiguration +@Configuration @AllArgsConstructor -@Import(BeanValidatorPluginsConfiguration.class) +@AutoConfigureBefore(SpringDocConfiguration.class) +@EnableConfigurationProperties(SwaggerProperties.class) +@ConditionalOnProperty(value = "swagger.enabled", havingValue = "true", matchIfMissing = true) public class SwaggerAutoConfiguration { private static final String DEFAULT_BASE_PATH = "/**"; private static final List DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**"); - /** - * 引入Knife4j扩展类 - */ - private final OpenApiExtensionResolver openApiExtensionResolver; + private static final String AUTHORIZATION_HEADER = "Authorization"; + private static final String TOKEN_HEADER = "Blade-Auth"; + private static final String TENANT_HEADER = "Tenant-Id"; /** - * 引入Blade环境变量 + * 引入Swagger配置类 */ - private final BladeProperties bladeProperties; + private final SwaggerProperties swaggerProperties; + /** + * 初始化OpenAPI对象 + */ @Bean - @ConditionalOnMissingBean - public SwaggerProperties swaggerProperties() { - return new SwaggerProperties(); + public OpenAPI openApi() { + // 初始化OpenAPI对象,并设置API的基本信息、安全策略、联系人信息、许可信息以及外部文档链接 + return new OpenAPI() + .components(new Components() + // 添加安全策略,配置API密钥(Token)和鉴权机制 + .addSecuritySchemes(TOKEN_HEADER, + new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .scheme("bearer") + .bearerFormat("JWT") + .name(TOKEN_HEADER) + ) + // 添加安全策略,配置API密钥(Authorization)和鉴权机制 + .addSecuritySchemes(AUTHORIZATION_HEADER, + new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name(AUTHORIZATION_HEADER) + ) + // 添加安全策略,配置租户ID(Tenant-Id)和鉴权机制 + .addSecuritySchemes(TENANT_HEADER, + new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name(TENANT_HEADER) + ) + ) + // 设置API文档的基本信息,包括标题、描述、联系方式和许可信息 + .info(new Info() + .title(swaggerProperties.getTitle()) + .description(swaggerProperties.getDescription()) + .termsOfService(swaggerProperties.getTermsOfServiceUrl()) + .contact(new Contact() + .name(swaggerProperties.getContact().getName()) + .email(swaggerProperties.getContact().getEmail()) + .url(swaggerProperties.getContact().getUrl()) + ) + .license(new License() + .name(swaggerProperties.getLicense()) + .url(swaggerProperties.getLicenseUrl()) + ) + .version(swaggerProperties.getVersion()) + ); } + /** + * 初始化GlobalOpenApiCustomizer对象 + */ @Bean @ConditionalOnMissingBean - public Docket api(SwaggerProperties swaggerProperties) { - if (swaggerProperties.getBasePath().size() == 0) { + public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() { + return openApi -> { + if (openApi.getPaths() != null) { + openApi.getPaths().forEach((s, pathItem) -> pathItem.readOperations().forEach(operation -> + operation.addSecurityItem(new SecurityRequirement() + .addList(AUTHORIZATION_HEADER) + .addList(TOKEN_HEADER) + .addList(TENANT_HEADER)))); + } + }; + } + + /** + * 初始化GroupedOpenApi对象 + */ + @Bean + @ConditionalOnMissingBean + public GroupedOpenApi defaultApi() { + // 如果Swagger配置中的基本路径和排除路径为空,则设置默认的基本路径和排除路径 + if (CollectionUtil.isEmpty(swaggerProperties.getBasePath())) { swaggerProperties.getBasePath().add(DEFAULT_BASE_PATH); } - if (swaggerProperties.getExcludePath().size() == 0) { + if (CollectionUtil.isEmpty(swaggerProperties.getExcludePath())) { swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH); } - ApiSelectorBuilder apis = new Docket(DocumentationType.SWAGGER_2) - .host(swaggerProperties.getHost()) - .apiInfo(apiInfo(swaggerProperties)).select() - .apis(SwaggerUtil.basePackages(swaggerProperties.getBasePackages())); - swaggerProperties.getBasePath().forEach(p -> apis.paths(PathSelectors.ant(p))); - swaggerProperties.getExcludePath().forEach(p -> apis.paths(PathSelectors.ant(p).negate())); - return apis.build().securityContexts(securityContexts(swaggerProperties)).securitySchemes(securitySchemas(swaggerProperties)) - .extensions(openApiExtensionResolver.buildExtensions(bladeProperties.getName())); - } - - /** - * 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL - */ - private List securityContexts(SwaggerProperties swaggerProperties) { - return Collections.singletonList(SecurityContext.builder() - .securityReferences(defaultAuth(swaggerProperties)) - .forPaths(PathSelectors.regex(swaggerProperties.getAuthorization().getAuthRegex())) - .build()); - } - - /** - * 默认的全局鉴权策略 - */ - private List defaultAuth(SwaggerProperties swaggerProperties) { - List authorizationScopeList = new ArrayList<>(); - List securityReferenceList = new ArrayList<>(); - List swaggerScopeList = swaggerProperties.getAuthorization().getAuthorizationScopeList(); - swaggerScopeList.forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))); - if (authorizationScopeList.size() == 0) { - authorizationScopeList.add(new AuthorizationScope("global", "accessEverywhere")); - } - AuthorizationScope[] authorizationScopes = authorizationScopeList.toArray(new AuthorizationScope[0]); - swaggerScopeList.forEach(authorizationScope -> securityReferenceList.add(new SecurityReference(authorizationScope.getName(), authorizationScopes))); - if (securityReferenceList.size() == 0) { - securityReferenceList.add(new SecurityReference(SwaggerUtil.clientInfo().getName(), authorizationScopes)); - securityReferenceList.add(new SecurityReference(SwaggerUtil.bladeAuth().getName(), authorizationScopes)); - securityReferenceList.add(new SecurityReference(SwaggerUtil.bladeTenant().getName(), authorizationScopes)); - } - return securityReferenceList; - } - - /** - * 配置安全策略 - */ - private List securitySchemas(SwaggerProperties swaggerProperties) { - List swaggerApiKeyList = swaggerProperties.getAuthorization().getAuthorizationApiKeyList(); - if (swaggerApiKeyList.size() == 0) { - return Lists.newArrayList(SwaggerUtil.clientInfo(), SwaggerUtil.bladeAuth(), SwaggerUtil.bladeTenant()); - } else { - List securitySchemeList = new ArrayList<>(); - swaggerApiKeyList.forEach(authorizationApiKey -> securitySchemeList.add(new ApiKey(authorizationApiKey.getName(), authorizationApiKey.getKeyName(), authorizationApiKey.getPassAs()))); - return securitySchemeList; - } - } - - /** - * 配置基本信息 - */ - private ApiInfo apiInfo(SwaggerProperties swaggerProperties) { - return new ApiInfoBuilder() - .title(swaggerProperties.getTitle()) - .description(swaggerProperties.getDescription()) - .license(swaggerProperties.getLicense()) - .licenseUrl(swaggerProperties.getLicenseUrl()) - .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()) - .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())) - .version(swaggerProperties.getVersion()) + // 获取Swagger配置中的基本路径、排除路径、基本包路径和排除包路径 + List basePath = swaggerProperties.getBasePath(); + List excludePath = swaggerProperties.getExcludePath(); + List basePackages = swaggerProperties.getBasePackages(); + List excludePackages = swaggerProperties.getExcludePackages(); + // 创建并返回GroupedOpenApi对象 + return GroupedOpenApi.builder() + .group("default") + .pathsToMatch(basePath.toArray(new String[0])) + .pathsToExclude(excludePath.toArray(new String[0])) + .packagesToScan(basePackages.toArray(new String[0])) + .packagesToExclude(excludePackages.toArray(new String[0])) .build(); } diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerHandlerConfiguration.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerHandlerConfiguration.java deleted file mode 100644 index 0cc081b..0000000 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerHandlerConfiguration.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2018-2028, lengleng (wangiegie@gmail.com). - *

- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.gnu.org/licenses/lgpl.html - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springblade.core.swagger; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; -import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; - -import java.lang.reflect.Field; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 解决swagger2与最新版springboot冲突的问题 - * - * @author Chill - */ -@AutoConfiguration -public class SwaggerHandlerConfiguration { - - @Bean - public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { - return new BeanPostProcessor() { - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof WebMvcRequestHandlerProvider) { - customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); - } - return bean; - } - - private void customizeSpringfoxHandlerMappings(List mappings) { - List copy = mappings.stream() - .filter(mapping -> mapping.getPatternParser() == null) - .collect(Collectors.toList()); - mappings.clear(); - mappings.addAll(copy); - } - }; - } - - private static List getHandlerMappings(Object bean) { - try { - Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); - field.setAccessible(true); - return (List) field.get(bean); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - -} diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerLauncherServiceImpl.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerLauncherServiceImpl.java index 3d732f7..d9f903c 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerLauncherServiceImpl.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerLauncherServiceImpl.java @@ -15,6 +15,7 @@ */ package org.springblade.core.swagger; +import net.dreamlu.mica.auto.annotation.AutoService; import org.springblade.core.launch.constant.AppConstant; import org.springblade.core.launch.service.LauncherService; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -27,12 +28,14 @@ import java.util.Properties; * * @author Chill */ +@AutoService(LauncherService.class) public class SwaggerLauncherServiceImpl implements LauncherService { @Override public void launcher(SpringApplicationBuilder builder, String appName, String profile) { Properties props = System.getProperties(); if (profile.equals(AppConstant.PROD_CODE)) { props.setProperty("knife4j.production", "true"); + props.setProperty("swagger.enabled", "false"); } props.setProperty("knife4j.enable", "true"); props.setProperty("spring.mvc.pathmatch.matching-strategy", "ANT_PATH_MATCHER"); diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java index c830264..9470b94 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2028, lengleng (wangiegie@gmail.com). + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). *

* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; * you may not use this file except in compliance with the License. @@ -32,10 +32,18 @@ import java.util.List; @Data @ConfigurationProperties("swagger") public class SwaggerProperties { + /** + * 是否开启swagger + */ + private boolean enabled = true; /** * swagger会解析的包路径 **/ private List basePackages = new ArrayList<>(Collections.singletonList(AppConstant.BASE_PACKAGES)); + /** + * swagger会排除解析的包路径 + **/ + private List excludePackages = new ArrayList<>(); /** * swagger会解析的url规则 **/ @@ -55,11 +63,11 @@ public class SwaggerProperties { /** * 版本 **/ - private String version = "3.7.2"; + private String version = AppConstant.APPLICATION_VERSION; /** * 许可证 **/ - private String license = ""; + private String license = "Powered By SpringBlade"; /** * 许可证URL **/ @@ -68,7 +76,6 @@ public class SwaggerProperties { * 服务条款URL **/ private String termsOfServiceUrl = ""; - /** * host信息 **/ @@ -89,15 +96,15 @@ public class SwaggerProperties { /** * 联系人 **/ - private String name = "chillzhuang"; - /** - * 联系人url - **/ - private String url = ""; + private String name = "翼宿"; /** * 联系人email **/ - private String email = "smallchill@163.com"; + private String email = "bladejava@qq.com"; + /** + * 联系人url + **/ + private String url = "https://gitee.com/smallc"; } @@ -115,60 +122,11 @@ public class SwaggerProperties { */ private String authRegex = "^.*$"; - /** - * 鉴权作用域列表 - */ - private List authorizationScopeList = new ArrayList<>(); - - /** - * 鉴权请求头参数列表 - */ - private List authorizationApiKeyList = new ArrayList<>(); - /** * 接口匹配地址 */ private List tokenUrlList = new ArrayList<>(); } - @Data - @NoArgsConstructor - public static class AuthorizationScope { - /** - * 鉴权策略名, 需要和ApiKey的name保持一致 - */ - private String name = ""; - /** - * 作用域名称 - */ - private String scope = ""; - - /** - * 作用域描述 - */ - private String description = ""; - - } - - @Data - @NoArgsConstructor - public static class AuthorizationApiKey { - - /** - * 参数名 - */ - private String name = ""; - - /** - * 参数值 - */ - private String keyName = ""; - - /** - * 参数作用域 - */ - private String passAs = ""; - - } } diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerUtil.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerUtil.java deleted file mode 100644 index 6171b5a..0000000 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerUtil.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). - *

- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.gnu.org/licenses/lgpl.html - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springblade.core.swagger; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import org.springblade.core.launch.constant.TokenConstant; -import springfox.documentation.RequestHandler; -import springfox.documentation.service.ApiKey; - -import java.util.List; -import java.util.function.Predicate; - -/** - * Swagger工具类 - * - * @author Chill - */ -public class SwaggerUtil { - - /** - * 获取包集合 - * - * @param basePackages 多个包名集合 - */ - public static Predicate basePackages(final List basePackages) { - return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true); - } - - private static Function, Boolean> handlerPackage(final List basePackages) { - return input -> { - // 循环判断匹配 - for (String strPackage : basePackages) { - boolean isMatch = input.getPackage().getName().startsWith(strPackage); - if (isMatch) { - return true; - } - } - return false; - }; - } - - private static Optional> declaringClass(RequestHandler input) { - return Optional.fromNullable(input.declaringClass()); - } - - - public static ApiKey clientInfo() { - return new ApiKey("ClientInfo", "Authorization", "header"); - } - - public static ApiKey bladeAuth() { - return new ApiKey("BladeAuth", TokenConstant.HEADER, "header"); - } - - public static ApiKey bladeTenant() { - return new ApiKey("TenantId", "Tenant-Id", "header"); - } - -} diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerWebConfiguration.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerWebConfiguration.java index 54c3995..b9a1587 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerWebConfiguration.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerWebConfiguration.java @@ -16,6 +16,7 @@ package org.springblade.core.swagger; +import org.springblade.core.launch.props.BladePropertySource; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; @@ -28,6 +29,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; */ @AutoConfiguration @EnableConfigurationProperties(SwaggerProperties.class) +@BladePropertySource(value = "classpath:/blade-swagger.yml") public class SwaggerWebConfiguration implements WebMvcConfigurer { @Override diff --git a/blade-core-swagger/src/main/resources/META-INF/services/org.springblade.core.launch.service.LauncherService b/blade-core-swagger/src/main/resources/META-INF/services/org.springblade.core.launch.service.LauncherService deleted file mode 100644 index 03ce0b3..0000000 --- a/blade-core-swagger/src/main/resources/META-INF/services/org.springblade.core.launch.service.LauncherService +++ /dev/null @@ -1 +0,0 @@ -org.springblade.core.swagger.SwaggerLauncherServiceImpl diff --git a/blade-core-swagger/src/main/resources/blade-swagger.yml b/blade-core-swagger/src/main/resources/blade-swagger.yml new file mode 100644 index 0000000..d621371 --- /dev/null +++ b/blade-core-swagger/src/main/resources/blade-swagger.yml @@ -0,0 +1,34 @@ +#springdoc-openapi项目配置 +springdoc: + swagger-ui: + path: /swagger-ui.html + tags-sorter: order + operations-sorter: order + persist-authorization: true + api-docs: + enabled: true + path: /v3/api-docs + +#knife4j配置 +knife4j: + #启用 + enable: true + #基础认证 + basic: + enable: false + username: blade + password: blade + #增强配置 + setting: + enable-swagger-models: true + enable-document-manage: true + enable-host: false + enable-host-text: http://localhost + enable-request-cache: true + enable-filter-multipart-apis: false + enable-filter-multipart-api-method-type: POST + enable-footer: false + enable-footer-custom: true + language: zh_cn + footer-custom-content: Copyright © 2024 SpringBlade All Rights Reserved + diff --git a/blade-core-tool/pom.xml b/blade-core-tool/pom.xml index 5181a52..eb787f4 100644 --- a/blade-core-tool/pom.xml +++ b/blade-core-tool/pom.xml @@ -28,33 +28,25 @@ com.google.guava guava - ${guava.version} com.squareup.okhttp3 okhttp - 3.11.0 - io.springfox - springfox-swagger2 - - - io.swagger - swagger-models + io.swagger.core.v3 + swagger-annotations io.protostuff protostuff-core - ${protostuff.version} io.protostuff protostuff-runtime - ${protostuff.version} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/api/R.java b/blade-core-tool/src/main/java/org/springblade/core/tool/api/R.java index 6a9e513..2ba9d70 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/api/R.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/api/R.java @@ -15,14 +15,17 @@ */ package org.springblade.core.tool.api; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.utils.ObjectUtil; import org.springframework.lang.Nullable; -import javax.servlet.http.HttpServletResponse; +import java.io.Serial; import java.io.Serializable; import java.util.Optional; @@ -34,19 +37,20 @@ import java.util.Optional; @Getter @Setter @ToString -@ApiModel(description = "返回信息") +@Schema(description = "返回信息") @NoArgsConstructor public class R implements Serializable { + @Serial private static final long serialVersionUID = 1L; - @ApiModelProperty(value = "状态码", required = true) + @Schema(description = "状态码") private int code; - @ApiModelProperty(value = "是否成功", required = true) + @Schema(description = "是否成功") private boolean success; - @ApiModelProperty(value = "承载数据") + @Schema(description = "承载数据") private T data; - @ApiModelProperty(value = "返回消息", required = true) + @Schema(description = "返回消息") private String msg; private R(IResultCode resultCode) { diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/api/ResultCode.java b/blade-core-tool/src/main/java/org/springblade/core/tool/api/ResultCode.java index 19e0287..ff69cb8 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/api/ResultCode.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/api/ResultCode.java @@ -18,7 +18,7 @@ package org.springblade.core.tool.api; import lombok.AllArgsConstructor; import lombok.Getter; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; /** * 业务代码枚举 diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/config/RequestConfiguration.java b/blade-core-tool/src/main/java/org/springblade/core/tool/config/RequestConfiguration.java index 553a981..a6a0b38 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/config/RequestConfiguration.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/config/RequestConfiguration.java @@ -25,7 +25,7 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; -import javax.servlet.DispatcherType; +import jakarta.servlet.DispatcherType; /** * 过滤器配置类 diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/node/BaseNode.java b/blade-core-tool/src/main/java/org/springblade/core/tool/node/BaseNode.java index 1a9105e..9f8b3f2 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/node/BaseNode.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/node/BaseNode.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; +import java.io.Serial; import java.util.ArrayList; import java.util.List; @@ -31,6 +32,7 @@ import java.util.List; @Data public class BaseNode implements INode { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/node/ForestNode.java b/blade-core-tool/src/main/java/org/springblade/core/tool/node/ForestNode.java index ff5b0dc..ec9fc15 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/node/ForestNode.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/node/ForestNode.java @@ -18,6 +18,8 @@ package org.springblade.core.tool.node; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serial; + /** * 森林节点类 @@ -28,6 +30,7 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = false) public class ForestNode extends BaseNode { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/node/TreeNode.java b/blade-core-tool/src/main/java/org/springblade/core/tool/node/TreeNode.java index 3c82fa2..d716683 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/node/TreeNode.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/node/TreeNode.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; import org.springblade.core.tool.utils.Func; +import java.io.Serial; import java.util.Objects; /** @@ -30,6 +31,7 @@ import java.util.Objects; @Data public class TreeNode extends BaseNode { + @Serial private static final long serialVersionUID = 1L; private String title; diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeHttpServletRequestWrapper.java b/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeHttpServletRequestWrapper.java index c2d47e2..d47173d 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeHttpServletRequestWrapper.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeHttpServletRequestWrapper.java @@ -19,10 +19,10 @@ import org.springblade.core.tool.utils.WebUtil; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeRequestFilter.java b/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeRequestFilter.java index f036d40..434e765 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeRequestFilter.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/request/BladeRequestFilter.java @@ -18,8 +18,8 @@ package org.springblade.core.tool.request; import lombok.AllArgsConstructor; import org.springframework.util.AntPathMatcher; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; /** diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/request/XssHttpServletRequestWrapper.java b/blade-core-tool/src/main/java/org/springblade/core/tool/request/XssHttpServletRequestWrapper.java index 62255ee..d9805e7 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/request/XssHttpServletRequestWrapper.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/request/XssHttpServletRequestWrapper.java @@ -20,10 +20,10 @@ import org.springblade.core.tool.utils.WebUtil; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/ssl/DisableValidationTrustManager.java b/blade-core-tool/src/main/java/org/springblade/core/tool/ssl/DisableValidationTrustManager.java new file mode 100644 index 0000000..5878490 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/ssl/DisableValidationTrustManager.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.tool.ssl; + +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * 不进行证书校验 + * + * @author L.cm + */ +public class DisableValidationTrustManager implements X509TrustManager { + + public static final X509TrustManager INSTANCE = new DisableValidationTrustManager(); + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/ssl/TrustAllHostNames.java b/blade-core-tool/src/main/java/org/springblade/core/tool/ssl/TrustAllHostNames.java new file mode 100644 index 0000000..19071a5 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/ssl/TrustAllHostNames.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.tool.ssl; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +/** + * 信任所有 host name + * + * @author L.cm + */ +public class TrustAllHostNames implements HostnameVerifier { + public static final TrustAllHostNames INSTANCE = new TrustAllHostNames(); + + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Base64Util.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Base64Util.java index 6f349b0..d6c206b 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Base64Util.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Base64Util.java @@ -15,12 +15,19 @@ */ package org.springblade.core.tool.utils; +import java.nio.charset.Charset; +import java.util.Base64; + /** * Base64工具 * * @author L.cm */ -public class Base64Util extends org.springframework.util.Base64Utils { +public class Base64Util { + public static final Base64.Encoder ENCODER = Base64.getEncoder(); + public static final Base64.Encoder URL_ENCODER = Base64.getUrlEncoder(); + public static final Base64.Decoder DECODER = Base64.getDecoder(); + public static final Base64.Decoder URL_DECODER = Base64.getUrlDecoder(); /** * 编码 @@ -29,7 +36,7 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @return {String} */ public static String encode(String value) { - return Base64Util.encode(value, Charsets.UTF_8); + return encode(value, Charsets.UTF_8); } /** @@ -39,9 +46,9 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @param charset 字符集 * @return {String} */ - public static String encode(String value, java.nio.charset.Charset charset) { + public static String encode(String value, Charset charset) { byte[] val = value.getBytes(charset); - return new String(Base64Util.encode(val), charset); + return new String(encode(val), charset); } /** @@ -51,7 +58,7 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @return {String} */ public static String encodeUrlSafe(String value) { - return Base64Util.encodeUrlSafe(value, Charsets.UTF_8); + return encodeUrlSafe(value, Charsets.UTF_8); } /** @@ -61,9 +68,9 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @param charset 字符集 * @return {String} */ - public static String encodeUrlSafe(String value, java.nio.charset.Charset charset) { + public static String encodeUrlSafe(String value, Charset charset) { byte[] val = value.getBytes(charset); - return new String(Base64Util.encodeUrlSafe(val), charset); + return new String(encodeUrlSafe(val), charset); } /** @@ -73,7 +80,7 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @return {String} */ public static String decode(String value) { - return Base64Util.decode(value, Charsets.UTF_8); + return decode(value, Charsets.UTF_8); } /** @@ -83,9 +90,9 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @param charset 字符集 * @return {String} */ - public static String decode(String value, java.nio.charset.Charset charset) { + public static String decode(String value, Charset charset) { byte[] val = value.getBytes(charset); - byte[] decodedValue = Base64Util.decode(val); + byte[] decodedValue = decode(val); return new String(decodedValue, charset); } @@ -96,7 +103,7 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @return {String} */ public static String decodeUrlSafe(String value) { - return Base64Util.decodeUrlSafe(value, Charsets.UTF_8); + return decodeUrlSafe(value, Charsets.UTF_8); } /** @@ -106,9 +113,113 @@ public class Base64Util extends org.springframework.util.Base64Utils { * @param charset 字符集 * @return {String} */ - public static String decodeUrlSafe(String value, java.nio.charset.Charset charset) { + public static String decodeUrlSafe(String value, Charset charset) { byte[] val = value.getBytes(charset); - byte[] decodedValue = Base64Util.decodeUrlSafe(val); + byte[] decodedValue = decodeUrlSafe(val); return new String(decodedValue, charset); } + + /** + * Base64-encode the given byte array. + * + * @param src the original byte array + * @return the encoded byte array + */ + public static byte[] encode(byte[] src) { + if (src.length == 0) { + return src; + } + return ENCODER.encode(src); + } + + /** + * Base64-decode the given byte array. + * + * @param src the encoded byte array + * @return the original byte array + */ + public static byte[] decode(byte[] src) { + if (src.length == 0) { + return src; + } + return DECODER.decode(src); + } + + /** + * Base64-encode the given byte array using the RFC 4648 + * "URL and Filename Safe Alphabet". + * + * @param src the original byte array + * @return the encoded byte array + */ + public static byte[] encodeUrlSafe(byte[] src) { + if (src.length == 0) { + return src; + } + return URL_ENCODER.encode(src); + } + + /** + * Base64-decode the given byte array using the RFC 4648 + * "URL and Filename Safe Alphabet". + * + * @param src the encoded byte array + * @return the original byte array + * @since 4.2.4 + */ + public static byte[] decodeUrlSafe(byte[] src) { + if (src.length == 0) { + return src; + } + return URL_DECODER.decode(src); + } + + /** + * Base64-encode the given byte array to a String. + * + * @param src the original byte array + * @return the encoded byte array as a UTF-8 String + */ + public static String encodeToString(byte[] src) { + if (src.length == 0) { + return ""; + } + return new String(encode(src), Charsets.UTF_8); + } + + /** + * Base64-decode the given byte array from a UTF-8 String. + * + * @param src the encoded UTF-8 String + * @return the original byte array + */ + public static byte[] decodeFromString(String src) { + if (src.isEmpty()) { + return new byte[0]; + } + return decode(src.getBytes(Charsets.UTF_8)); + } + + /** + * Base64-encode the given byte array to a String using the RFC 4648 + * "URL and Filename Safe Alphabet". + * + * @param src the original byte array + * @return the encoded byte array as a UTF-8 String + */ + public static String encodeToUrlSafeString(byte[] src) { + return new String(encodeUrlSafe(src), Charsets.UTF_8); + } + + /** + * Base64-decode the given byte array from a UTF-8 String using the RFC 4648 + * "URL and Filename Safe Alphabet". + * + * @param src the encoded UTF-8 String + * @return the original byte array + */ + public static byte[] decodeFromUrlSafeString(String src) { + return decodeUrlSafe(src.getBytes(Charsets.UTF_8)); + } + } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java index 9bdee2c..205e2f8 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java @@ -1517,7 +1517,7 @@ public class Func { * @return T */ public static T copy(Object source, Class clazz) { - return BeanUtil.copy(source, clazz); + return BeanUtil.copyProperties(source, clazz); } /** @@ -1529,7 +1529,7 @@ public class Func { * @param targetBean 需要赋值的对象 */ public static void copy(Object source, Object targetBean) { - BeanUtil.copy(source, targetBean); + BeanUtil.copyProperties(source, targetBean); } /** diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/MultipartUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/MultipartUtil.java new file mode 100644 index 0000000..89c83a1 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/MultipartUtil.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.tool.utils; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * MultipartUtils + * + * @author Chill + */ +public class MultipartUtil { + /** + * 从HttpServletRequest中解析并返回所有的MultipartFile + * + * @param request HttpServletRequest对象,应为MultipartHttpServletRequest类型 + * @return 包含所有MultipartFile的列表,如果没有文件或请求不是多部分请求,则返回空列表 + */ + public static List extractMultipartFiles(HttpServletRequest request) { + List files = new ArrayList<>(); + + if (request instanceof MultipartHttpServletRequest multipartRequest) { + + // 获取所有文件的映射 + Map fileMap = multipartRequest.getFileMap(); + + // 遍历映射,并将所有MultipartFile添加到列表中 + for (MultipartFile file : fileMap.values()) { + if (file != null && !file.isEmpty()) { + files.add(file); + } + } + } + + return files; + } +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/ValidationRule.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/ValidationRule.java index 057c9b9..5c101ab 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/ValidationRule.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/ValidationRule.java @@ -18,6 +18,7 @@ package org.springblade.core.tool.utils; import lombok.AllArgsConstructor; import lombok.Data; +import java.io.Serial; import java.io.Serializable; /** @@ -28,6 +29,7 @@ import java.io.Serializable; @Data @AllArgsConstructor public class ValidationRule implements Serializable { + @Serial private static final long serialVersionUID = 1L; /** * 校验值 diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/WebUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/WebUtil.java index 27163d6..6a5fa3a 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/WebUtil.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/WebUtil.java @@ -26,10 +26,10 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.method.HandlerMethod; -import javax.servlet.ServletInputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; diff --git a/blade-core-transaction/pom.xml b/blade-core-transaction/pom.xml index b608fae..a2107e9 100644 --- a/blade-core-transaction/pom.xml +++ b/blade-core-transaction/pom.xml @@ -32,7 +32,6 @@ com.alibaba.cloud spring-cloud-starter-alibaba-seata - ${alibaba.cloud.version} io.seata diff --git a/pom.xml b/pom.xml index c3b3849..2e8224e 100644 --- a/pom.xml +++ b/pom.xml @@ -36,31 +36,37 @@ - 3.7.2.1 + 4.0.0 - 1.8 - 3.8.1 - 1.3.0 - 4.3.0 - 3.5.13 - 2.1.1 - 3.5.4.1 - 3.5.4.1 + 17 + 3.11.0 + 1.3.0 + UTF-8 + UTF-8 + + 4.5.0 + + 3.5.16 + 3.0.3 + 3.5.6 + 3.5.6 + + 4.12.0 1.6.0 3.4.2 - 31.1-jre - 2.3.2 + 33.1.0-jre + 3.1.3 + 1.2.22 - 2021.0.5.0 - 2.1.2 - 5.3.31 - 2.7.18 - 2.7.14 - 2021.0.8 + 6.1.5 + 3.2.4 + 3.2.3 + 2023.0.1 + + 2022.0.0.0 + 2.3.1 - UTF-8 - UTF-8 @@ -213,6 +219,173 @@ blade-core-crypto ${revision} + + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + + + com.squareup.okhttp3 + logging-interceptor + ${okhttp.version} + + + + org.mybatis + mybatis + ${mybatis.version} + + + org.mybatis + mybatis-spring + ${mybatis.spring.version} + + + com.baomidou + mybatis-plus + ${mybatis.plus.version} + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis.plus.version} + + + com.baomidou + mybatis-plus-extension + ${mybatis.plus.version} + + + com.baomidou + mybatis-plus-generator + ${mybatis.plus.generator.version} + + + org.mybatis + mybatis-typehandlers-jsr310 + 1.0.2 + + + + com.alibaba.nacos + nacos-client + ${alibaba.nacos.version} + + + + com.alibaba + fastjson + 2.0.47 + + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + + + io.jsonwebtoken + jjwt-jackson + 0.11.2 + + + + me.zhyd.oauth + JustAuth + 1.16.6 + + + + com.google.guava + guava + ${guava.version} + + + + net.sf.ehcache + ehcache + 2.10.5 + + + + hibernate-validator + org.hibernate.validator + 8.0.1.Final + + + + com.alibaba + druid-spring-boot-3-starter + 1.2.22 + + + + com.mysql + mysql-connector-j + 8.3.0 + + + + io.swagger.core.v3 + swagger-annotations + 2.2.19 + + + org.springdoc + springdoc-openapi-starter-webflux-ui + 2.3.0 + + + + io.protostuff + protostuff-core + ${protostuff.version} + + + io.protostuff + protostuff-runtime + ${protostuff.version} + + + + org.antlr + antlr4-runtime + 4.9.2 + + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + com.sun.xml.bind + jaxb-core + 4.0.5 + + + com.sun.xml.bind + jaxb-impl + 4.0.5 + + + javax.activation + activation + 1.1.1 + + + + org.projectlombok + lombok + 1.18.30 + @@ -247,7 +420,6 @@ org.hibernate.validator hibernate-validator - 6.2.0.Final net.dreamlu @@ -286,7 +458,7 @@ org.codehaus.mojo flatten-maven-plugin - ${maven-flatten.version} + ${maven.flatten.version} true oss