18 Commits

Author SHA1 Message Date
f5528fbfad Merge pull request 'v0.0.9' (#9) from dev into master
Reviewed-on: #9
2026-04-08 12:34:13 +00:00
Timi
31bb990d82 v0.0.9
All checks were successful
CI/CD / build-deploy (pull_request) Successful in 6m12s
2026-04-08 20:33:33 +08:00
Timi
8a6e148d6c add ObjectMapper arg for RedisSerializers.jacksonSerializer 2026-04-08 11:50:27 +08:00
Timi
b94849d69c add @RequestBodyValue 2026-04-08 11:22:22 +08:00
Timi
3f97bb1356 fix Redis instance 2026-04-08 11:22:04 +08:00
90c97f211b Merge pull request 'v0.0.8' (#8) from dev into master
Reviewed-on: #8
2026-04-01 11:18:22 +00:00
Timi
8d89c60a02 v0.0.8
All checks were successful
CI/CD / build-deploy (pull_request) Successful in 55s
2026-04-01 19:17:55 +08:00
42dee2e4ad Merge pull request 'v0.0.7' (#7) from dev into master
Reviewed-on: #7
2026-03-27 15:10:56 +00:00
Timi
d77cf10b1b v0.0.7
All checks were successful
CI/CD / build-deploy (pull_request) Successful in 1m40s
2026-03-27 23:10:20 +08:00
66eb6108cd Merge pull request 'v0.0.6' (#6) from dev into master
Reviewed-on: #6
2026-03-21 10:16:59 +00:00
Timi
3a343724a8 v0.0.6
All checks were successful
CI/CD / build-deploy (pull_request) Successful in 1m55s
2026-03-21 18:16:20 +08:00
82f950e71d Merge pull request 'v0.0.5' (#5) from dev into master
Reviewed-on: #5
2026-03-17 03:24:06 +00:00
Timi
83b1265c0a v0.0.5
All checks were successful
CI/CD / build-deploy (pull_request) Successful in 1m24s
2026-03-17 11:23:45 +08:00
cceabb0c8d Merge pull request 'v0.0.5' (#4) from dev into master
Reviewed-on: #4
2026-03-17 03:07:34 +00:00
Timi
b16656af12 v0.0.5
Some checks failed
CI/CD / build-deploy (pull_request) Failing after 5m26s
2026-03-17 11:06:53 +08:00
62de8e4885 Merge pull request 'v0.0.4' (#3) from dev into master
Reviewed-on: #3
2026-03-16 09:18:09 +00:00
Timi
a13795703e v0.0.4
All checks were successful
CI/CD / build-deploy (pull_request) Successful in 21m29s
2026-03-16 17:14:18 +08:00
Timi
66e379a0bd add lombok 2026-03-16 15:42:25 +08:00
30 changed files with 431 additions and 980 deletions

65
pom.xml
View File

@@ -7,22 +7,22 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version> <version>3.5.11</version>
<relativePath/> <relativePath/>
</parent> </parent>
<groupId>com.imyeyu.spring</groupId> <groupId>com.imyeyu.spring</groupId>
<artifactId>timi-spring</artifactId> <artifactId>timi-spring</artifactId>
<version>0.0.3</version> <version>0.0.9</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
<springboot.version>3.4.0</springboot.version> <maven.test.skip>true</maven.test.skip>
<springboot.version>3.5.11</springboot.version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.test.skip>true</maven.test.skip>
</properties> </properties>
<build> <build>
@@ -37,10 +37,42 @@
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version> <version>3.3.1</version>
</plugin> </plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.20.0</version>
<configuration>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<outputDirectory>${project.build.directory}/delombok</outputDirectory>
<addOutputDirectory>false</addOutputDirectory>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
</dependency>
</dependencies>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.2</version> <version>3.11.2</version>
<configuration>
<sourcepath>${project.build.directory}/delombok</sourcepath>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
@@ -70,20 +102,6 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.version}</version> <version>${springboot.version}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -120,15 +138,14 @@
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
<version>2.12.0</version> <version>2.12.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency> <dependency>
<groupId>com.imyeyu.io</groupId> <groupId>com.imyeyu.io</groupId>
<artifactId>timi-io</artifactId> <artifactId>timi-io</artifactId>
<version>0.0.2</version> <version>0.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,19 +1,16 @@
package com.imyeyu.spring; package com.imyeyu.spring;
import com.google.gson.Gson; import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.Language; import com.imyeyu.java.bean.Language;
import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.java.bean.timi.TimiResponse;
import com.imyeyu.java.ref.Ref; import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.bean.RequestRange; import com.imyeyu.spring.bean.RequestRange;
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeanWrapperImpl;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
@@ -21,8 +18,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -39,56 +35,12 @@ import java.util.Locale;
*/ */
public class TimiSpring { public class TimiSpring {
/** 版本号 */
public static final String VERSION = "0.0.1";
private static final Logger log = LoggerFactory.getLogger(TimiSpring.class);
private static final Gson GSON = new Gson();
/** /**
* 工具类禁止实例化 * 工具类禁止实例化
*/ */
private TimiSpring() { private TimiSpring() {
} }
/**
* 回调数据
*
* @param response 返回
* @param resp 返回结果
*/
public static void render(HttpServletResponse response, TimiResponse<?> resp) {
try {
HttpSession session = getSession();
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
out.write(GSON.toJson(resp).getBytes(StandardCharsets.UTF_8));
out.flush();
out.close();
} catch (Exception e) {
log.error("TimiSpring.render Error", e);
}
}
/**
* 回调错误
*
* @param response 返回
* @param code 代码
* @param msgKey 消息映射键
*/
public static void renderError(HttpServletResponse response, TimiCode code, String msgKey) {
try {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
out.write(GSON.toJson(code.toResponse().msg(msgKey)).getBytes(StandardCharsets.UTF_8));
out.flush();
out.close();
} catch (Exception e) {
log.error("TimiSpring.renderError error", e);
}
}
/** /**
* 获取 Servlet 请求属性 * 获取 Servlet 请求属性
* *
@@ -380,9 +332,9 @@ public class TimiSpring {
if (cookies == null) { if (cookies == null) {
return null; return null;
} }
for (int i = 0; i < cookies.length; i++) { for (Cookie cookie : cookies) {
if (cookies[i].getName().equals(key)) { if (cookie.getName().equals(key)) {
return cookies[i]; return cookie;
} }
} }
return null; return null;
@@ -469,11 +421,11 @@ public class TimiSpring {
/** /**
* 解析 Range 请求范围 * 解析 Range 请求范围
* *
* @param fileLength 文件长度 * @param total 总数据量
* @return 请求范围 * @return 请求范围
* @throws IOException IO 异常 * @throws IOException IO 异常
*/ */
public static RequestRange requestRange(long fileLength) throws IOException { public static RequestRange getRequestRange(long total) throws IOException {
HttpServletResponse resp = getResponse(); HttpServletResponse resp = getResponse();
String range = getRequestAttrAsString("Range"); String range = getRequestAttrAsString("Range");
@@ -485,14 +437,14 @@ public class TimiSpring {
String[] ranges = rangeValue.split("-"); String[] ranges = rangeValue.split("-");
TimiException.requiredTrue(2 == ranges.length, "Invalid Range format"); TimiException.requiredTrue(2 == ranges.length, "Invalid Range format");
long start = Long.parseLong(ranges[0]); long start = Long.parseLong(ranges[0]);
long end = ranges[1].isEmpty() ? fileLength - 1 : Long.parseLong(ranges[1]); long end = ranges[1].isEmpty() ? total - 1 : Long.parseLong(ranges[1]);
// 验证范围有效性 // 验证范围有效性
if (start < 0 || fileLength <= end || end < start) { if (start < 0 || total <= end || end < start) {
resp.setHeader("Content-Range", "bytes */" + fileLength); resp.setHeader("Content-Range", "bytes */" + total);
resp.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); resp.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null; return null;
} }
return new RequestRange(start, end); return new RequestRange(start, end, total);
} }
public static void copyPropertiesNotNull(Object source, Object target) { public static void copyPropertiesNotNull(Object source, Object target) {
@@ -507,4 +459,22 @@ public class TimiSpring {
} }
} }
} }
public static void responseRangeStream(InputStream stream, long total) throws IOException {
HttpServletResponse resp = getResponse();
RequestRange range = getRequestRange(total);
if (range == null) {
// 完整文件
resp.setContentLengthLong(total);
resp.setStatus(HttpServletResponse.SC_OK);
IO.toOutputStream(stream, resp.getOutputStream());
} else {
// 分片文件
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
resp.setHeader("Content-Range", "bytes %s-%s/%s".formatted(range.getStart(), range.getEnd(), range.getTotal()));
resp.setContentLengthLong(range.getLength());
IO.toOutputStream(stream, resp.getOutputStream(), range.getStart(), range.getEnd());
resp.flushBuffer();
}
}
} }

View File

@@ -38,7 +38,7 @@ public class AOPLogInterceptor {
} }
/** 注入注解 */ /** 注入注解 */
@Pointcut("@annotation(annotation.com.imyeyu.spring.AOPLog)") @Pointcut("@annotation(com.imyeyu.spring.annotation.AOPLog)")
public void logPointCut() { public void logPointCut() {
} }
@@ -63,41 +63,45 @@ public class AOPLogInterceptor {
@AfterReturning(returning = "response", pointcut = "logPointCut()") @AfterReturning(returning = "response", pointcut = "logPointCut()")
public void doAfterReturning(Object response) throws Throwable { public void doAfterReturning(Object response) throws Throwable {
String msg = "ID: {} Response <- Return."; String msg = "ID: {} Response <- Return.";
if (response instanceof IDEntity<?> entity) { switch (response) {
// 返回实体 case IDEntity<?> entity ->
msg += entity.getClass().getSimpleName() + "." + entity.getId(); // 返回实体
} else if (response instanceof PageResult<?> pageResult) { msg += entity.getClass().getSimpleName() + "." + entity.getId();
// 返回数组 case PageResult<?> pageResult -> {
if (pageResult.getList().isEmpty()) { // 返回数组
msg += "PageResult<?> Empty"; if (pageResult.getList().isEmpty()) {
} else { msg += "PageResult<?> Empty";
if (pageResult.getList().get(0) == null) {
msg += "PageResult<?>." + pageResult.getList().size();
} else { } else {
msg += "PageResult<" + pageResult.getList().get(0).getClass().getSimpleName() + ">[" + pageResult.getList().size() + "]"; if (pageResult.getList().getFirst() == null) {
msg += "PageResult<?>." + pageResult.getList().size();
} else {
msg += "PageResult<%s>[%s]".formatted(pageResult.getList().getFirst().getClass().getSimpleName(), pageResult.getList().size());
}
} }
// 返回数据页
} }
// 返回数据页 case String string -> {
} else if (response instanceof String string) { // 返回字符串
// 返回字符串 if (string.length() < 64) {
if (string.length() < 64) { msg += string;
msg += string; } else {
} else { msg += string.substring(0, 64) + "..";
msg += string.substring(0, 64) + ".."; }
msg = msg.replaceAll("[\\r\\n]+", "");
} }
msg = msg.replaceAll("[\\r\\n]+", ""); case Boolean bool ->
} else if (response instanceof Boolean bool) { // 返回布尔值
// 返回布尔值 msg += bool;
msg += bool; case Number number ->
} else if (response instanceof Number number) { // 返回数字
// 返回数字 msg += response.getClass().getSimpleName() + ".[" + number.doubleValue() + "]";
msg += response.getClass().getSimpleName() + ".[" + number.doubleValue() + "]"; case null, default -> {
} else { // 其他对象
// 其他对象 if (TimiJava.isNotEmpty(response)) {
if (TimiJava.isNotEmpty(response)) { msg += response.getClass().getSimpleName();
msg += response.getClass().getSimpleName(); } else {
} else { msg += "NULL";
msg += "NULL"; }
} }
} }
log.info(msg, TimiSpring.getSessionAttr(REQUEST_ID)); log.info(msg, TimiSpring.getSessionAttr(REQUEST_ID));

View File

@@ -41,8 +41,8 @@ public abstract class CaptchaValidAbstractInterceptor {
} }
if (joinPoint.getSignature() instanceof MethodSignature ms) { if (joinPoint.getSignature() instanceof MethodSignature ms) {
Object[] args = joinPoint.getArgs(); Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) { for (Object arg : args) {
if (args[i] instanceof CaptchaData<?> captchaData) { if (arg instanceof CaptchaData<?> captchaData) {
// 校验请求参数的验证码 // 校验请求参数的验证码
verify(captchaData.getCaptchaId(), captchaData.getCaptcha()); verify(captchaData.getCaptchaId(), captchaData.getCaptcha());
break; break;

View File

@@ -0,0 +1,43 @@
package com.imyeyu.spring.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 读取 JSON 请求体中的单个字段并绑定到接口参数。
*
* <p>默认使用方法参数名作为 JSON 字段名,也可以手动指定字段名。
*
* <pre>
* public void run(@RequestBodyValue String data) {
* }
*
* public void run(@RequestBodyValue("value") Long id) {
* }
* </pre>
*
* @author 夜雨
* @version 2026-04-08 11:00
*/
@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestBodyValue {
/**
* JSON 字段名,为空时使用方法参数名。
*
* @return JSON 字段名
*/
String value() default "";
/**
* 是否必须存在该字段。
*
* @return true 表示必须存在
*/
boolean required() default true;
}

View File

@@ -0,0 +1,82 @@
package com.imyeyu.spring.annotation;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* {@link RequestBodyValue} 参数解析器。
*
* @author 夜雨
* @version 2026-04-08 11:00
*/
@Component
public class RequestBodyValueArgumentResolver implements HandlerMethodArgumentResolver {
private static final String REQUEST_BODY_JSON_NODE_ATTR = RequestBodyValueArgumentResolver.class.getName() + ".REQUEST_BODY_JSON_NODE";
private final ObjectMapper objectMapper;
/**
* 创建 {@link RequestBodyValue} 参数解析器。
*
* @param objectMapper Jackson 对象映射器
*/
public RequestBodyValueArgumentResolver(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public boolean supportsParameter(@NonNull MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBodyValue.class);
}
@Override
public Object resolveArgument(@NonNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
RequestBodyValue bodyValue = parameter.getParameterAnnotation(RequestBodyValue.class);
if (bodyValue == null) {
return null;
}
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
TimiException.required(request, "not found request");
assert request != null;
JsonNode requestBody;
{
Object cached = request.getAttribute(REQUEST_BODY_JSON_NODE_ATTR);
if (cached instanceof JsonNode jsonNode) {
return jsonNode;
}
byte[] bodyBytes = request.getInputStream().readAllBytes();
TimiException.requiredTrue(1 < bodyBytes.length, "empty request body");
requestBody = objectMapper.readTree(bodyBytes);
TimiException.requiredTrue(requestBody != null && requestBody.isObject(), "not object request body");
request.setAttribute(REQUEST_BODY_JSON_NODE_ATTR, requestBody);
}
String fieldName = bodyValue.value();
{
if (TimiJava.isEmpty(fieldName)) {
fieldName = parameter.getParameterName();
}
TimiException.required(fieldName, "not found @RequestBodyValue parameter name");
}
JsonNode fieldNode = requestBody.get(fieldName);
if (fieldNode == null || fieldNode.isMissingNode() || fieldNode.isNull()) {
if (!bodyValue.required()) {
return null;
}
throw new TimiException(TimiCode.ARG_MISS, "not found json field: %s".formatted(fieldName));
}
return objectMapper.treeToValue(fieldNode, parameter.getParameterType());
}
}

View File

@@ -2,6 +2,7 @@ package com.imyeyu.spring.annotation;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.NoArgsConstructor;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
@@ -12,14 +13,9 @@ import org.springframework.web.servlet.HandlerInterceptor;
* @author 夜雨 * @author 夜雨
* @version 2021-08-16 18:07 * @version 2021-08-16 18:07
*/ */
@NoArgsConstructor
public abstract class RequestRateLimitAbstractInterceptor implements HandlerInterceptor { public abstract class RequestRateLimitAbstractInterceptor implements HandlerInterceptor {
/**
* 创建访问频率限制拦截器
*/
protected RequestRateLimitAbstractInterceptor() {
}
/** /**
* 构建接口标识 * 构建接口标识
* *

View File

@@ -1,22 +0,0 @@
package com.imyeyu.spring.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 单字段 Json 数据体
*
* @author 夜雨
* @version 2023-08-09 10:36
* @deprecated 0.0.3 过时0.0.5 移除,单参数建议 url 传参
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Deprecated
public @interface RequestSingleParam {
}

View File

@@ -1,67 +0,0 @@
package com.imyeyu.spring.annotation;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import jakarta.annotation.Nonnull;
import jakarta.servlet.http.HttpServletRequest;
import com.imyeyu.io.IO;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* 单参数请求解析器
*
* @author 夜雨
* @since 2025-10-13 16:29
* @deprecated 0.0.3 过时0.0.5 移除,单参数建议 url 传参
*/
@Component
@Deprecated
public class RequestSingleParamResolver implements HandlerMethodArgumentResolver {
/**
* 创建单参数解析器
*/
public RequestSingleParamResolver() {
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestSingleParam.class);
}
@Override
public Object resolveArgument(@Nonnull MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request == null) {
throw new TimiException(TimiCode.REQUEST_BAD, "request illegal");
}
JsonElement element = JsonParser.parseString(IO.toString(request.getInputStream()));
if (!element.isJsonObject()) {
throw new TimiException(TimiCode.ARG_BAD, "not json object");
}
JsonObject object = element.getAsJsonObject();
String parameterName = parameter.getParameterName();
if (!object.has(parameterName)) {
throw new TimiException(TimiCode.ARG_MISS, "not found " + parameterName + " param");
}
JsonElement el = object.get(parameterName);
if (parameter.getParameterType().isAssignableFrom(Long.class)) {
return el.getAsLong();
}
if (parameter.getParameterType().isAssignableFrom(Integer.class)) {
return el.getAsInt();
}
if (parameter.getParameterType().isAssignableFrom(String.class)) {
return el.getAsString();
}
throw new TimiException(TimiCode.ERROR, "not support parameter type");
}
}

View File

@@ -1,6 +1,7 @@
package com.imyeyu.spring.bean; package com.imyeyu.spring.bean;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/** /**
* 含验证码数据实体 * 含验证码数据实体
@@ -9,6 +10,7 @@ import jakarta.validation.constraints.NotBlank;
* @author 夜雨 * @author 夜雨
* @version 2021-03-01 17:10 * @version 2021-03-01 17:10
*/ */
@Data
public class CaptchaData<T> { public class CaptchaData<T> {
/** 来源 */ /** 来源 */
@@ -21,64 +23,4 @@ public class CaptchaData<T> {
/** 数据体 */ /** 数据体 */
protected T data; protected T data;
/**
* 创建验证码数据实体
*/
public CaptchaData() {
}
/**
* 获取验证码 ID
*
* @return 验证码 ID
*/
public String getCaptchaId() {
return captchaId;
}
/**
* 设置验证码 ID
*
* @param captchaId 验证码 ID
*/
public void setCaptchaId(String captchaId) {
this.captchaId = captchaId;
}
/**
* 获取验证码
*
* @return 验证码
*/
public String getCaptcha() {
return captcha;
}
/**
* 设置验证码
*
* @param captcha 验证码
*/
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
/**
* 获取数据体
*
* @return 数据体
*/
public T getData() {
return data;
}
/**
* 设置数据体
*
* @param data 数据体
*/
public void setData(T data) {
this.data = data;
}
} }

View File

@@ -1,93 +0,0 @@
package com.imyeyu.spring.bean;
import com.imyeyu.java.bean.Language;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.annotation.table.AutoUUID;
import com.imyeyu.spring.annotation.table.Id;
import com.imyeyu.spring.entity.Creatable;
import com.imyeyu.spring.entity.Deletable;
import com.imyeyu.spring.entity.IDEntity;
import com.imyeyu.spring.entity.Updatable;
/**
* 多语言实体基类
*
* @author 夜雨
* @since 2025-10-17 15:21
*/
public class Multilingual extends Language implements IDEntity<String>, Creatable, Updatable, Deletable {
/** 唯一标识 */
@Id
@AutoUUID
protected String id;
/** 创建时间 */
protected Long createdAt;
/** 更新时间 */
protected Long updatedAt;
/** 删除时间 */
protected Long deletedAt;
/**
* 创建多语言实体
*/
public Multilingual() {
}
/**
* 获取指定语言值
*
* @param language 指定语言
* @return 值
*/
public String getValue(Language.Enum language) {
try {
return Ref.getFieldValue(this, language.toString().replace("_", ""), String.class);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public Long getCreatedAt() {
return createdAt;
}
@Override
public void setCreatedAt(Long createdAt) {
this.createdAt = createdAt;
}
@Override
public Long getUpdatedAt() {
return updatedAt;
}
@Override
public void setUpdatedAt(Long updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public Long getDeletedAt() {
return deletedAt;
}
@Override
public void setDeletedAt(Long deletedAt) {
this.deletedAt = deletedAt;
}
}

View File

@@ -4,6 +4,9 @@ import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.BasePage; import com.imyeyu.java.bean.BasePage;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.utils.Text; import com.imyeyu.utils.Text;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -14,6 +17,9 @@ import java.util.LinkedHashMap;
* @author 夜雨 * @author 夜雨
* @version 2023-06-02 14:47 * @version 2023-06-02 14:47
*/ */
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Page<T> extends BasePage { public class Page<T> extends BasePage {
/** 精确匹配示例 */ /** 精确匹配示例 */
@@ -31,12 +37,6 @@ public class Page<T> extends BasePage {
/** 排序字段映射 */ /** 排序字段映射 */
protected LinkedHashMap<String, BaseMapper.OrderType> orderMap; protected LinkedHashMap<String, BaseMapper.OrderType> orderMap;
/**
* 创建分页参数
*/
public Page() {
}
/** /**
* 创建分页参数 * 创建分页参数
* *
@@ -65,96 +65,6 @@ public class Page<T> extends BasePage {
return size; return size;
} }
/**
* 获取精确匹配示例
*
* @return 精确匹配示例
*/
public T getEqualsExample() {
return equalsExample;
}
/**
* 设置精确匹配示例
*
* @param equalsExample 精确匹配示例
*/
public void setEqualsExample(T equalsExample) {
this.equalsExample = equalsExample;
}
/**
* 获取模糊匹配示例
*
* @return 模糊匹配示例
*/
public T getLikesExample() {
return likesExample;
}
/**
* 设置模糊匹配示例
*
* @param likesExample 模糊匹配示例
*/
public void setLikesExample(T likesExample) {
this.likesExample = likesExample;
}
/**
* 获取精确匹配连接逻辑
*
* @return 连接逻辑
*/
public Logic getEqualsLogic() {
return equalsLogic;
}
/**
* 设置精确匹配连接逻辑
*
* @param equalsLogic 连接逻辑
*/
public void setEqualsLogic(Logic equalsLogic) {
this.equalsLogic = equalsLogic;
}
/**
* 获取模糊匹配连接逻辑
*
* @return 连接逻辑
*/
public Logic getLikesLogic() {
return likesLogic;
}
/**
* 设置模糊匹配连接逻辑
*
* @param likesLogic 连接逻辑
*/
public void setLikesLogic(Logic likesLogic) {
this.likesLogic = likesLogic;
}
/**
* 获取排序映射
*
* @return 排序映射
*/
public LinkedHashMap<String, BaseMapper.OrderType> getOrderMap() {
return orderMap;
}
/**
* 设置排序映射
*
* @param orderMap 排序映射
*/
public void setOrderMap(LinkedHashMap<String, BaseMapper.OrderType> orderMap) {
this.orderMap = orderMap;
}
/** /**
* 添加排序字段 * 添加排序字段
* *

View File

@@ -11,9 +11,4 @@ import com.imyeyu.java.bean.BasePageResult;
*/ */
public class PageResult<T> extends BasePageResult<T> { public class PageResult<T> extends BasePageResult<T> {
/**
* 创建分页结果
*/
public PageResult() {
}
} }

View File

@@ -1,19 +1,16 @@
package com.imyeyu.spring.bean; package com.imyeyu.spring.bean;
import lombok.Data;
/** /**
* RedisConfig 配置参数 * RedisConfig 配置参数
* *
* @author 夜雨 * @author 夜雨
* @version 2021-11-21 10:02 * @version 2021-11-21 10:02
*/ */
@Data
public class RedisConfigParams { public class RedisConfigParams {
/**
* 创建 Redis 配置参数
*/
public RedisConfigParams() {
}
/** 地址 */ /** 地址 */
private String host; private String host;
@@ -35,129 +32,4 @@ public class RedisConfigParams {
/** 最大空闲连接 */ /** 最大空闲连接 */
private int maxIdle; private int maxIdle;
/**
* 获取地址
*
* @return 地址
*/
public String getHost() {
return host;
}
/**
* 设置地址
*
* @param host 地址
*/
public void setHost(String host) {
this.host = host;
}
/**
* 获取端口
*
* @return 端口
*/
public int getPort() {
return port;
}
/**
* 设置端口
*
* @param port 端口
*/
public void setPort(int port) {
this.port = port;
}
/**
* 获取密码
*
* @return 密码
*/
public String getPassword() {
return password;
}
/**
* 设置密码
*
* @param password 密码
*/
public void setPassword(String password) {
this.password = password;
}
/**
* 获取超时(毫秒)
*
* @return 超时(毫秒)
*/
public int getTimeout() {
return timeout;
}
/**
* 设置超时(毫秒)
*
* @param timeout 超时(毫秒)
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* 获取最大活跃连接
*
* @return 最大活跃连接
*/
public int getMaxActive() {
return maxActive;
}
/**
* 设置最大活跃连接
*
* @param maxActive 最大活跃连接
*/
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
/**
* 获取最小空闲连接
*
* @return 最小空闲连接
*/
public int getMinIdle() {
return minIdle;
}
/**
* 设置最小空闲连接
*
* @param minIdle 最小空闲连接
*/
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
/**
* 获取最大空闲连接
*
* @return 最大空闲连接
*/
public int getMaxIdle() {
return maxIdle;
}
/**
* 设置最大空闲连接
*
* @param maxIdle 最大空闲连接
*/
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
} }

View File

@@ -1,11 +1,18 @@
package com.imyeyu.spring.bean; package com.imyeyu.spring.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/** /**
* 请求范围参数 * 请求范围参数
* *
* @author 夜雨 * @author 夜雨
* @since 2025-07-14 17:09 * @since 2025-07-14 17:09
*/ */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RequestRange { public class RequestRange {
/** 起始值 */ /** 起始值 */
@@ -14,52 +21,8 @@ public class RequestRange {
/** 结束值 */ /** 结束值 */
private long end; private long end;
/** /** 总数据量 */
* 创建请求范围 private long total;
*
* @param start 起始值
* @param end 结束值
*/
public RequestRange(long start, long end) {
this.start = start;
this.end = end;
}
/**
* 获取起始值
*
* @return 起始值
*/
public long getStart() {
return start;
}
/**
* 设置起始值
*
* @param start 起始值
*/
public void setStart(long start) {
this.start = start;
}
/**
* 获取结束值
*
* @return 结束值
*/
public long getEnd() {
return end;
}
/**
* 设置结束值
*
* @param end 结束值
*/
public void setEnd(long end) {
this.end = end;
}
/** /**
* 获取范围长度 * 获取范围长度

View File

@@ -2,6 +2,7 @@ package com.imyeyu.spring.config;
import com.imyeyu.spring.bean.RedisConfigParams; import com.imyeyu.spring.bean.RedisConfigParams;
import com.imyeyu.spring.util.Redis; import com.imyeyu.spring.util.Redis;
import io.lettuce.core.api.StatefulConnection;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.KeyGenerator;
@@ -39,7 +40,7 @@ public abstract class AbstractRedisConfig implements CachingConfigurer {
* 连接池配置 * 连接池配置
* <p>参考: * <p>参考:
* <pre> * <pre>
* GenericObjectPoolConfig&lt;?&gt; config = new GenericObjectPoolConfig&lt;&gt;(); * GenericObjectPoolConfig&lt;StatefulConnection&lt;?, ?&gt;&gt; config = new GenericObjectPoolConfig&lt;&gt;();
* config.setMaxTotal(config.getMaxActive()); * config.setMaxTotal(config.getMaxActive());
* config.setMinIdle(config.getMinIdle()); * config.setMinIdle(config.getMinIdle());
* config.setMaxIdle(config.getMaxIdle()); * config.setMaxIdle(config.getMaxIdle());
@@ -48,7 +49,7 @@ public abstract class AbstractRedisConfig implements CachingConfigurer {
* *
* @return GenericObjectPoolConfig * @return GenericObjectPoolConfig
*/ */
public abstract GenericObjectPoolConfig<?> getPoolConfig(); public abstract GenericObjectPoolConfig<StatefulConnection<?, ?>> getPoolConfig();
/** /**
* Redis key 生成策略 * Redis key 生成策略

View File

@@ -2,6 +2,7 @@ package com.imyeyu.spring.entity;
import com.imyeyu.spring.annotation.table.DeleteColumn; import com.imyeyu.spring.annotation.table.DeleteColumn;
import com.imyeyu.utils.Time; import com.imyeyu.utils.Time;
import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@@ -11,14 +12,9 @@ import java.io.Serializable;
* @author 夜雨 * @author 夜雨
* @version 2021-11-20 17:45 * @version 2021-11-20 17:45
*/ */
@Data
public class BaseEntity implements Serializable, Creatable, Updatable, Deletable { public class BaseEntity implements Serializable, Creatable, Updatable, Deletable {
/**
* 创建基础实体
*/
public BaseEntity() {
}
/** 创建时间 */ /** 创建时间 */
protected Long createdAt; protected Long createdAt;
@@ -29,60 +25,6 @@ public class BaseEntity implements Serializable, Creatable, Updatable, Deletable
@DeleteColumn @DeleteColumn
protected Long deletedAt; protected Long deletedAt;
/**
* 获取创建时间
*
* @return 创建时间
*/
public Long getCreatedAt() {
return createdAt;
}
/**
* 设置创建时间
*
* @param createdAt 创建时间
*/
public void setCreatedAt(Long createdAt) {
this.createdAt = createdAt;
}
/**
* 获取更新时间
*
* @return 更新时间
*/
public Long getUpdatedAt() {
return updatedAt;
}
/**
* 设置更新时间
*
* @param updatedAt 更新时间
*/
public void setUpdatedAt(Long updatedAt) {
this.updatedAt = updatedAt;
}
/**
* 获取删除时间
*
* @return 删除时间
*/
public Long getDeletedAt() {
return deletedAt;
}
/**
* 设置删除时间
*
* @param deletedAt 删除时间
*/
public void setDeletedAt(Long deletedAt) {
this.deletedAt = deletedAt;
}
@Override @Override
public boolean isDeleted() { public boolean isDeleted() {
return deletedAt != null && deletedAt < Time.now(); return deletedAt != null && deletedAt < Time.now();

View File

@@ -1,6 +1,8 @@
package com.imyeyu.spring.entity; package com.imyeyu.spring.entity;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** /**
* 基本长整型 ID 实体 * 基本长整型 ID 实体
@@ -8,33 +10,11 @@ import com.imyeyu.spring.annotation.table.Id;
* @author 夜雨 * @author 夜雨
* @since 2025-02-07 12:51 * @since 2025-02-07 12:51
*/ */
@Data
@EqualsAndHashCode(callSuper = true)
public class Entity extends BaseEntity implements IDEntity<Long> { public class Entity extends BaseEntity implements IDEntity<Long> {
/** ID */ /** ID */
@Id @Id
protected Long id; protected Long id;
/**
* 创建基础 ID 实体
*/
public Entity() {
}
/**
* 获取 ID
*
* @return ID
*/
public Long getId() {
return id;
}
/**
* 设置 ID
*
* @param id ID
*/
public void setId(Long id) {
this.id = id;
}
} }

View File

@@ -0,0 +1,47 @@
package com.imyeyu.spring.entity;
import com.imyeyu.java.bean.Language;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.annotation.table.AutoUUID;
import com.imyeyu.spring.annotation.table.Id;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 多语言实体基类
*
* @author 夜雨
* @since 2025-10-17 15:21
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Multilingual extends Language implements IDEntity<String>, Creatable, Updatable, Deletable, Destroyable {
/** 唯一标识 */
@Id
@AutoUUID
protected String id;
/** 创建时间 */
protected Long createdAt;
/** 更新时间 */
protected Long updatedAt;
/** 删除时间 */
protected Long deletedAt;
/**
* 获取指定语言值
*
* @param language 指定语言
* @return 值
*/
public String getValue(Language.Enum language) {
try {
return Ref.getFieldValue(this, language.toString().replace("_", ""), String.class);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -2,6 +2,8 @@ package com.imyeyu.spring.entity;
import com.imyeyu.spring.annotation.table.AutoUUID; import com.imyeyu.spring.annotation.table.AutoUUID;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** /**
* 基本 UUID 实体 * 基本 UUID 实体
@@ -9,24 +11,12 @@ import com.imyeyu.spring.annotation.table.Id;
* @author 夜雨 * @author 夜雨
* @since 2025-02-07 12:07 * @since 2025-02-07 12:07
*/ */
@Data
@EqualsAndHashCode(callSuper = true)
public class UUIDEntity extends BaseEntity implements IDEntity<String> { public class UUIDEntity extends BaseEntity implements IDEntity<String> {
/** ID */ /** ID */
@Id @Id
@AutoUUID @AutoUUID
protected String id; protected String id;
/**
* 创建 UUID 实体
*/
public UUIDEntity() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
} }

View File

@@ -1,63 +0,0 @@
package com.imyeyu.spring.handler;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.imyeyu.java.TimiJava;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* MySQL JSON 数据类型处理器
*
* @author 夜雨
* @since 2021-07-04 09:36
*/
public class GsonHandler extends BaseTypeHandler<JsonElement> {
/**
* 创建 Gson 类型处理器
*/
public GsonHandler() {
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, JsonElement parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.toString());
}
@Override
public JsonElement getNullableResult(ResultSet rs, String columnName) throws SQLException {
return toElement(rs.getString(columnName));
}
@Override
public JsonElement getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return toElement(rs.getString(columnIndex));
}
@Override
public JsonElement getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return toElement(cs.getNString(columnIndex));
}
private JsonElement toElement(String json) {
if (TimiJava.isNotEmpty(json)) {
JsonElement el = JsonParser.parseString(json);
if (el.isJsonObject()) {
return el.getAsJsonObject();
}
if (el.isJsonArray()) {
return el.getAsJsonArray();
}
if (el.isJsonPrimitive()) {
return el.getAsJsonPrimitive();
}
}
return null;
}
}

View File

@@ -0,0 +1,62 @@
package com.imyeyu.spring.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*
*
* @author 夜雨
* @since 2026-03-16 15:42
*/
@MappedTypes(JsonNode.class)
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CLOB, JdbcType.LONGVARCHAR})
public class JsonNodeTypeHandler extends BaseTypeHandler<JsonNode> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, JsonNode parameter, JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, MAPPER.writeValueAsString(parameter));
} catch (JsonProcessingException e) {
throw new SQLException("Failed to serialize JsonNode to JSON", e);
}
}
@Override
public JsonNode getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parseJson(rs.getString(columnName));
}
@Override
public JsonNode getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parseJson(rs.getString(columnIndex));
}
@Override
public JsonNode getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parseJson(cs.getString(columnIndex));
}
private JsonNode parseJson(String json) throws SQLException {
if (json == null || json.isEmpty()) {
return MAPPER.createObjectNode();
}
try {
return MAPPER.readTree(json);
} catch (JsonProcessingException e) {
throw new SQLException("Failed to parse JSON to JsonNode", e);
}
}
}

View File

@@ -20,12 +20,6 @@ public abstract class AbstractEntityService<T, P> implements BaseService<T, P> {
/** 基本 Mapper */ /** 基本 Mapper */
protected BaseMapper<T, P> baseMapper; protected BaseMapper<T, P> baseMapper;
/**
* 创建实体服务
*/
protected AbstractEntityService() {
}
/** /**
* 获取 Mapper 实例 * 获取 Mapper 实例
* *

View File

@@ -1,8 +1,8 @@
package com.imyeyu.spring.util; package com.imyeyu.spring.util;
import com.imyeyu.java.TimiJava;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;
import com.imyeyu.java.TimiJava;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
@@ -16,12 +16,6 @@ import java.lang.annotation.Annotation;
*/ */
public abstract class AbstractValidator<A extends Annotation, T> implements ConstraintValidator<A, T> { public abstract class AbstractValidator<A extends Annotation, T> implements ConstraintValidator<A, T> {
/**
* 创建校验器
*/
protected AbstractValidator() {
}
/** /**
* 验证处理器,入参验证数据,返回错误消息语言映射,返回 null 时表示通过验证 * 验证处理器,入参验证数据,返回错误消息语言映射,返回 null 时表示通过验证
* *

View File

@@ -21,6 +21,7 @@ import com.imyeyu.spring.entity.Updatable;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.utils.Text; import com.imyeyu.utils.Text;
import com.imyeyu.utils.Time; import com.imyeyu.utils.Time;
import lombok.Getter;
import org.apache.ibatis.builder.annotation.ProviderContext; import org.apache.ibatis.builder.annotation.ProviderContext;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -443,6 +444,7 @@ public abstract class BaseSQLProvider {
* @author 夜雨 * @author 夜雨
* @since 2025-02-05 23:47 * @since 2025-02-05 23:47
*/ */
@Getter
protected static class EntityMeta { protected static class EntityMeta {
/** 实体类 */ /** 实体类 */
@@ -541,51 +543,6 @@ public abstract class BaseSQLProvider {
return sb.substring(0, sb.length() - 1); return sb.substring(0, sb.length() - 1);
} }
/**
* 获取实体类型
*
* @return 实体类型
*/
public Class<?> getEntityClass() {
return entityClass;
}
/**
* 获取表名
*
* @return 表名
*/
public String getTable() {
return table;
}
/**
* 获取查询字段映射
*
* @return 查询字段映射
*/
public String getSelectAllClause() {
return selectAllClause;
}
/**
* 获取 ID 字段映射
*
* @return ID 字段映射
*/
public FieldColumn getIdFieldColumn() {
return idFieldColumn;
}
/**
* 获取字段映射列表
*
* @return 字段映射列表
*/
public List<FieldColumn> getFieldColumnList() {
return fieldColumnList;
}
/** /**
* 是否可创建 * 是否可创建
* *
@@ -629,6 +586,7 @@ public abstract class BaseSQLProvider {
* @author 夜雨 * @author 夜雨
* @since 2025-02-07 09:54 * @since 2025-02-07 09:54
*/ */
@Getter
protected static class FieldColumn { protected static class FieldColumn {
/** 字段 */ /** 字段 */
@@ -745,42 +703,6 @@ public abstract class BaseSQLProvider {
} }
} }
/**
* 获取字段
*
* @return 字段
*/
public Field getField() {
return field;
}
/**
* 获取字段名
*
* @return 字段名
*/
public String getFieldName() {
return fieldName;
}
/**
* 获取列名
*
* @return 列名
*/
public String getColumnName() {
return columnName;
}
/**
* 是否为 ID 字段
*
* @return true 为 ID 字段
*/
public boolean isId() {
return isId;
}
/** /**
* 是否非 ID 字段 * 是否非 ID 字段
* *
@@ -789,23 +711,5 @@ public abstract class BaseSQLProvider {
public boolean isNotId() { public boolean isNotId() {
return !isId(); return !isId();
} }
/**
* 是否自动 UUID
*
* @return true 为自动 UUID
*/
public boolean isAutoUUID() {
return isAutoUUID;
}
/**
* 是否自动大写 UUID
*
* @return true 为自动大写 UUID
*/
public boolean isAutoUpperUUID() {
return isAutoUpperUUID;
}
} }
} }

View File

@@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private static final String DEV_LANG_CONFIG = "dev.lang";
/** /**
* 创建全局异常处理器 * 创建全局异常处理器

View File

@@ -8,6 +8,7 @@ import com.imyeyu.java.bean.timi.TimiResponse;
import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.annotation.AOPLogInterceptor; import com.imyeyu.spring.annotation.AOPLogInterceptor;
import com.imyeyu.spring.annotation.IgnoreGlobalReturn; import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
import lombok.Data;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@@ -27,6 +28,7 @@ import java.util.Objects;
* @author 夜雨 * @author 夜雨
* @version 2023-04-30 00:59 * @version 2023-04-30 00:59
*/ */
@Data
@RestControllerAdvice @RestControllerAdvice
public class GlobalReturnHandler implements ResponseBodyAdvice<Object> { public class GlobalReturnHandler implements ResponseBodyAdvice<Object> {
@@ -77,22 +79,4 @@ public class GlobalReturnHandler implements ResponseBodyAdvice<Object> {
} }
return result; return result;
} }
/**
* 获取多语言头处理回调
*
* @return 处理回调
*/
public CallbackArgReturn<LanguageMsgMapping<?>, String> getMultilingualHeader() {
return multilingualHeader;
}
/**
* 设置多语言头处理回调
*
* @param multilingualHeader 处理回调
*/
public void setMultilingualHeader(CallbackArgReturn<LanguageMsgMapping<?>, String> multilingualHeader) {
this.multilingualHeader = multilingualHeader;
}
} }

View File

@@ -1,8 +1,11 @@
package com.imyeyu.spring.util; package com.imyeyu.spring.util;
import com.imyeyu.spring.config.AbstractRedisConfig;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.config.AbstractRedisConfig;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
@@ -27,30 +30,13 @@ import java.util.function.Consumer;
* @author 夜雨 * @author 夜雨
* @version 2021-11-21 09:58 * @version 2021-11-21 09:58
*/ */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Redis<K, V> { public class Redis<K, V> {
private final RedisSerializer<K> serializer; private RedisTemplate<K, V> redis;
private final RedisTemplate<K, V> redis; private RedisSerializer<K> serializer;
/**
* 创建 Redis 操作封装
*
* @param redis RedisTemplate 实例
* @param serializer 键序列化器
*/
public Redis(RedisTemplate<K, V> redis, RedisSerializer<K> serializer) {
this.redis = redis;
this.serializer = serializer;
}
/**
* 获取 Redis 模板对象
*
* @return Redis 模板对象
*/
public RedisTemplate<?, ?> getRedis() {
return redis;
}
/** /**
* 加锁 * 加锁
@@ -101,8 +87,8 @@ public class Redis<K, V> {
* @param value 值 * @param value 值
*/ */
public void setAndKeepTTL(K key, V value) { public void setAndKeepTTL(K key, V value) {
Long expire = redis.getExpire(key, TimeUnit.MILLISECONDS); long expire = redis.getExpire(key, TimeUnit.MILLISECONDS);
if (expire == null || expire <= 0) { if (expire <= 0) {
// 判死 // 判死
destroy(key); destroy(key);
} else { } else {
@@ -210,8 +196,8 @@ public class Redis<K, V> {
public Map<K, List<V>> getAllList() { public Map<K, List<V>> getAllList() {
Map<K, List<V>> r = new HashMap<>(); Map<K, List<V>> r = new HashMap<>();
List<K> ks = keys("*"); List<K> ks = keys("*");
for (int i = 0; i < ks.size(); i++) { for (K k : ks) {
r.put(ks.get(i), getList(ks.get(i))); r.put(k, getList(k));
} }
return r; return r;
} }
@@ -235,9 +221,9 @@ public class Redis<K, V> {
*/ */
public List<V> values() { public List<V> values() {
List<V> r = new ArrayList<>(); List<V> r = new ArrayList<>();
List<K> keys = keys("*"); List<K> ks = keys("*");
for (K key : keys) { for (K k : ks) {
r.add(get(key)); r.add(get(k));
} }
return r; return r;
} }
@@ -250,8 +236,8 @@ public class Redis<K, V> {
public Map<K, V> map() { public Map<K, V> map() {
Map<K, V> r = new HashMap<>(); Map<K, V> r = new HashMap<>();
List<K> ks = keys("*"); List<K> ks = keys("*");
for (int i = 0; i < ks.size(); i++) { for (K k : ks) {
r.put(ks.get(i), get(ks.get(i))); r.put(k, get(k));
} }
return r; return r;
} }
@@ -280,8 +266,7 @@ public class Redis<K, V> {
*/ */
public boolean destroy(K key) { public boolean destroy(K key) {
if (TimiJava.isNotEmpty(key) && has(key)) { if (TimiJava.isNotEmpty(key) && has(key)) {
Boolean isSucceed = redis.delete(key); return redis.delete(key);
return isSucceed != null && isSucceed;
} }
return false; return false;
} }

View File

@@ -1,10 +1,12 @@
package com.imyeyu.spring.util; package com.imyeyu.spring.util;
import com.google.gson.Gson; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException; import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
/** /**
@@ -83,20 +85,34 @@ public class RedisSerializers {
}; };
/** /**
* Gson 序列化 * Json 序列化
* *
* @param <T> 数据类型 * @param <T> 数据类型
* @param clazz 数据类型 * @param clazz 数据类型
* @return Redis 序列化器 * @return Redis 序列化器
*/ */
public static <T> RedisSerializer<T> gsonSerializer(Class<T> clazz) { public static <T> RedisSerializer<T> jacksonSerializer(Class<T> clazz) {
return new RedisSerializer<>() { return jacksonSerializer(new ObjectMapper(), clazz);
}
private static final Gson GSON = new Gson(); /**
* Json 序列化
*
* @param <T> 数据类型
* @param mapper 序列化对象
* @param clazz 数据类型
* @return Redis 序列化器
*/
public static <T> RedisSerializer<T> jacksonSerializer(ObjectMapper mapper, Class<T> clazz) {
return new RedisSerializer<>() {
@Override @Override
public byte[] serialize(T object) throws SerializationException { public byte[] serialize(T object) throws SerializationException {
return GSON.toJson(object).getBytes(StandardCharsets.UTF_8); try {
return mapper.writeValueAsString(object).getBytes(StandardCharsets.UTF_8);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
} }
@Override @Override
@@ -104,7 +120,11 @@ public class RedisSerializers {
if (bytes == null) { if (bytes == null) {
return null; return null;
} }
return GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), clazz); try {
return mapper.readValue(new String(bytes, StandardCharsets.UTF_8), clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
}; };
} }

View File

@@ -25,6 +25,6 @@ public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override @Override
public @org.springframework.lang.NonNull PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { public @org.springframework.lang.NonNull PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()); List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
return sources.get(0); return sources.getFirst();
} }
} }