Compare commits
24 Commits
13ae5016e8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 53b8c4c23f | |||
|
|
b49c1dd515 | ||
| f8beb03362 | |||
|
|
237f6d3890 | ||
| f5528fbfad | |||
|
|
31bb990d82 | ||
|
|
8a6e148d6c | ||
|
|
b94849d69c | ||
|
|
3f97bb1356 | ||
| 90c97f211b | |||
|
|
8d89c60a02 | ||
| 42dee2e4ad | |||
|
|
d77cf10b1b | ||
| 66eb6108cd | |||
|
|
3a343724a8 | ||
| 82f950e71d | |||
|
|
83b1265c0a | ||
| cceabb0c8d | |||
|
|
b16656af12 | ||
| 62de8e4885 | |||
|
|
a13795703e | ||
|
|
66e379a0bd | ||
| 9f7460e959 | |||
| 90d4c5e5f6 |
69
pom.xml
69
pom.xml
@@ -7,22 +7,22 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.5.13</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.imyeyu.spring</groupId>
|
||||
<artifactId>timi-spring</artifactId>
|
||||
<version>0.0.3</version>
|
||||
<version>0.0.11</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<springboot.version>3.4.0</springboot.version>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<springboot.version>3.5.13</springboot.version>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -37,10 +37,42 @@
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</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>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<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>
|
||||
</plugins>
|
||||
</build>
|
||||
@@ -70,20 +102,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -113,22 +131,21 @@
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>2.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<version>2.13.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.imyeyu.io</groupId>
|
||||
<artifactId>timi-io</artifactId>
|
||||
<version>0.0.2</version>
|
||||
<version>0.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package com.imyeyu.spring;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.imyeyu.io.IO;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.java.bean.timi.TimiResponse;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.spring.bean.RequestRange;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
@@ -21,8 +18,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -39,56 +35,12 @@ import java.util.Locale;
|
||||
*/
|
||||
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() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调数据
|
||||
*
|
||||
* @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 请求属性
|
||||
*
|
||||
@@ -380,9 +332,9 @@ public class TimiSpring {
|
||||
if (cookies == null) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < cookies.length; i++) {
|
||||
if (cookies[i].getName().equals(key)) {
|
||||
return cookies[i];
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals(key)) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -469,11 +421,11 @@ public class TimiSpring {
|
||||
/**
|
||||
* 解析 Range 请求范围
|
||||
*
|
||||
* @param fileLength 文件长度
|
||||
* @param total 总数据量
|
||||
* @return 请求范围
|
||||
* @throws IOException IO 异常
|
||||
*/
|
||||
public static RequestRange requestRange(long fileLength) throws IOException {
|
||||
public static RequestRange getRequestRange(long total) throws IOException {
|
||||
HttpServletResponse resp = getResponse();
|
||||
|
||||
String range = getRequestAttrAsString("Range");
|
||||
@@ -485,14 +437,14 @@ public class TimiSpring {
|
||||
String[] ranges = rangeValue.split("-");
|
||||
TimiException.requiredTrue(2 == ranges.length, "Invalid Range format");
|
||||
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) {
|
||||
resp.setHeader("Content-Range", "bytes */" + fileLength);
|
||||
if (start < 0 || total <= end || end < start) {
|
||||
resp.setHeader("Content-Range", "bytes */" + total);
|
||||
resp.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
return null;
|
||||
}
|
||||
return new RequestRange(start, end);
|
||||
return new RequestRange(start, end, total);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class AOPLogInterceptor {
|
||||
}
|
||||
|
||||
/** 注入注解 */
|
||||
@Pointcut("@annotation(annotation.com.imyeyu.spring.AOPLog)")
|
||||
@Pointcut("@annotation(com.imyeyu.spring.annotation.AOPLog)")
|
||||
public void logPointCut() {
|
||||
}
|
||||
|
||||
@@ -63,22 +63,24 @@ public class AOPLogInterceptor {
|
||||
@AfterReturning(returning = "response", pointcut = "logPointCut()")
|
||||
public void doAfterReturning(Object response) throws Throwable {
|
||||
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) {
|
||||
case PageResult<?> pageResult -> {
|
||||
// 返回数组
|
||||
if (pageResult.getList().isEmpty()) {
|
||||
msg += "PageResult<?> Empty";
|
||||
} else {
|
||||
if (pageResult.getList().get(0) == null) {
|
||||
if (pageResult.getList().getFirst() == null) {
|
||||
msg += "PageResult<?>." + pageResult.getList().size();
|
||||
} else {
|
||||
msg += "PageResult<" + pageResult.getList().get(0).getClass().getSimpleName() + ">[" + pageResult.getList().size() + "]";
|
||||
msg += "PageResult<%s>[%s]".formatted(pageResult.getList().getFirst().getClass().getSimpleName(), pageResult.getList().size());
|
||||
}
|
||||
}
|
||||
// 返回数据页
|
||||
} else if (response instanceof String string) {
|
||||
}
|
||||
case String string -> {
|
||||
// 返回字符串
|
||||
if (string.length() < 64) {
|
||||
msg += string;
|
||||
@@ -86,13 +88,14 @@ public class AOPLogInterceptor {
|
||||
msg += string.substring(0, 64) + "..";
|
||||
}
|
||||
msg = msg.replaceAll("[\\r\\n]+", "");
|
||||
} else if (response instanceof Boolean bool) {
|
||||
}
|
||||
case Boolean bool ->
|
||||
// 返回布尔值
|
||||
msg += bool;
|
||||
} else if (response instanceof Number number) {
|
||||
case Number number ->
|
||||
// 返回数字
|
||||
msg += response.getClass().getSimpleName() + ".[" + number.doubleValue() + "]";
|
||||
} else {
|
||||
case null, default -> {
|
||||
// 其他对象
|
||||
if (TimiJava.isNotEmpty(response)) {
|
||||
msg += response.getClass().getSimpleName();
|
||||
@@ -100,6 +103,7 @@ public class AOPLogInterceptor {
|
||||
msg += "NULL";
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info(msg, TimiSpring.getSessionAttr(REQUEST_ID));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ public abstract class CaptchaValidAbstractInterceptor {
|
||||
}
|
||||
if (joinPoint.getSignature() instanceof MethodSignature ms) {
|
||||
Object[] args = joinPoint.getArgs();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (args[i] instanceof CaptchaData<?> captchaData) {
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof CaptchaData<?> captchaData) {
|
||||
// 校验请求参数的验证码
|
||||
verify(captchaData.getCaptchaId(), captchaData.getCaptcha());
|
||||
break;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.imyeyu.spring.annotation;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
@@ -12,14 +13,9 @@ import org.springframework.web.servlet.HandlerInterceptor;
|
||||
* @author 夜雨
|
||||
* @version 2021-08-16 18:07
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
public abstract class RequestRateLimitAbstractInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 创建访问频率限制拦截器
|
||||
*/
|
||||
protected RequestRateLimitAbstractInterceptor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建接口标识
|
||||
*
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.imyeyu.spring.bean;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 含验证码数据实体
|
||||
@@ -9,6 +10,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||
* @author 夜雨
|
||||
* @version 2021-03-01 17:10
|
||||
*/
|
||||
@Data
|
||||
public class CaptchaData<T> {
|
||||
|
||||
/** 来源 */
|
||||
@@ -21,64 +23,4 @@ public class CaptchaData<T> {
|
||||
|
||||
/** 数据体 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.BasePage;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.utils.Text;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@@ -14,6 +17,9 @@ import java.util.LinkedHashMap;
|
||||
* @author 夜雨
|
||||
* @version 2023-06-02 14:47
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Page<T> extends BasePage {
|
||||
|
||||
/** 精确匹配示例 */
|
||||
@@ -31,12 +37,6 @@ public class Page<T> extends BasePage {
|
||||
/** 排序字段映射 */
|
||||
protected LinkedHashMap<String, BaseMapper.OrderType> orderMap;
|
||||
|
||||
/**
|
||||
* 创建分页参数
|
||||
*/
|
||||
public Page() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分页参数
|
||||
*
|
||||
@@ -65,96 +65,6 @@ public class Page<T> extends BasePage {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加排序字段
|
||||
*
|
||||
|
||||
@@ -11,9 +11,4 @@ import com.imyeyu.java.bean.BasePageResult;
|
||||
*/
|
||||
public class PageResult<T> extends BasePageResult<T> {
|
||||
|
||||
/**
|
||||
* 创建分页结果
|
||||
*/
|
||||
public PageResult() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package com.imyeyu.spring.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* RedisConfig 配置参数
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-11-21 10:02
|
||||
*/
|
||||
@Data
|
||||
public class RedisConfigParams {
|
||||
|
||||
/**
|
||||
* 创建 Redis 配置参数
|
||||
*/
|
||||
public RedisConfigParams() {
|
||||
}
|
||||
|
||||
/** 地址 */
|
||||
private String host;
|
||||
|
||||
@@ -35,129 +32,4 @@ public class RedisConfigParams {
|
||||
/** 最大空闲连接 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package com.imyeyu.spring.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 请求范围参数
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-07-14 17:09
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RequestRange {
|
||||
|
||||
/** 起始值 */
|
||||
@@ -14,52 +21,8 @@ public class RequestRange {
|
||||
/** 结束值 */
|
||||
private long end;
|
||||
|
||||
/**
|
||||
* 创建请求范围
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
/** 总数据量 */
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 获取范围长度
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.imyeyu.spring.config;
|
||||
|
||||
import com.imyeyu.spring.bean.RedisConfigParams;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import io.lettuce.core.api.StatefulConnection;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.springframework.cache.annotation.CachingConfigurer;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
@@ -39,7 +40,7 @@ public abstract class AbstractRedisConfig implements CachingConfigurer {
|
||||
* 连接池配置
|
||||
* <p>参考:
|
||||
* <pre>
|
||||
* GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
|
||||
* GenericObjectPoolConfig<StatefulConnection<?, ?>> config = new GenericObjectPoolConfig<>();
|
||||
* config.setMaxTotal(config.getMaxActive());
|
||||
* config.setMinIdle(config.getMinIdle());
|
||||
* config.setMaxIdle(config.getMaxIdle());
|
||||
@@ -48,7 +49,7 @@ public abstract class AbstractRedisConfig implements CachingConfigurer {
|
||||
*
|
||||
* @return GenericObjectPoolConfig
|
||||
*/
|
||||
public abstract GenericObjectPoolConfig<?> getPoolConfig();
|
||||
public abstract GenericObjectPoolConfig<StatefulConnection<?, ?>> getPoolConfig();
|
||||
|
||||
/**
|
||||
* Redis key 生成策略
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.imyeyu.spring.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.DeleteColumn;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -11,14 +12,9 @@ import java.io.Serializable;
|
||||
* @author 夜雨
|
||||
* @version 2021-11-20 17:45
|
||||
*/
|
||||
@Data
|
||||
public class BaseEntity implements Serializable, Creatable, Updatable, Deletable {
|
||||
|
||||
/**
|
||||
* 创建基础实体
|
||||
*/
|
||||
public BaseEntity() {
|
||||
}
|
||||
|
||||
/** 创建时间 */
|
||||
protected Long createdAt;
|
||||
|
||||
@@ -29,60 +25,6 @@ public class BaseEntity implements Serializable, Creatable, Updatable, Deletable
|
||||
@DeleteColumn
|
||||
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
|
||||
public boolean isDeleted() {
|
||||
return deletedAt != null && deletedAt < Time.now();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.imyeyu.spring.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 基本长整型 ID 实体
|
||||
@@ -8,33 +10,11 @@ import com.imyeyu.spring.annotation.table.Id;
|
||||
* @author 夜雨
|
||||
* @since 2025-02-07 12:51
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Entity extends BaseEntity implements IDEntity<Long> {
|
||||
|
||||
/** ID */
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
47
src/main/java/com/imyeyu/spring/entity/Multilingual.java
Normal file
47
src/main/java/com/imyeyu/spring/entity/Multilingual.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.imyeyu.spring.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 基本 UUID 实体
|
||||
@@ -9,24 +11,12 @@ import com.imyeyu.spring.annotation.table.Id;
|
||||
* @author 夜雨
|
||||
* @since 2025-02-07 12:07
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UUIDEntity extends BaseEntity implements IDEntity<String> {
|
||||
|
||||
/** ID */
|
||||
@Id
|
||||
@AutoUUID
|
||||
protected String id;
|
||||
|
||||
/**
|
||||
* 创建 UUID 实体
|
||||
*/
|
||||
public UUIDEntity() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,6 @@ public abstract class AbstractEntityService<T, P> implements BaseService<T, P> {
|
||||
/** 基本 Mapper */
|
||||
protected BaseMapper<T, P> baseMapper;
|
||||
|
||||
/**
|
||||
* 创建实体服务
|
||||
*/
|
||||
protected AbstractEntityService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Mapper 实例
|
||||
*
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.imyeyu.spring.util;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
|
||||
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> {
|
||||
|
||||
/**
|
||||
* 创建校验器
|
||||
*/
|
||||
protected AbstractValidator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证处理器,入参验证数据,返回错误消息语言映射,返回 null 时表示通过验证
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.imyeyu.spring.entity.Updatable;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.utils.Text;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.Getter;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -443,6 +444,7 @@ public abstract class BaseSQLProvider {
|
||||
* @author 夜雨
|
||||
* @since 2025-02-05 23:47
|
||||
*/
|
||||
@Getter
|
||||
protected static class EntityMeta {
|
||||
|
||||
/** 实体类 */
|
||||
@@ -541,51 +543,6 @@ public abstract class BaseSQLProvider {
|
||||
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 夜雨
|
||||
* @since 2025-02-07 09:54
|
||||
*/
|
||||
@Getter
|
||||
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 字段
|
||||
*
|
||||
@@ -789,23 +711,5 @@ public abstract class BaseSQLProvider {
|
||||
public boolean isNotId() {
|
||||
return !isId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自动 UUID
|
||||
*
|
||||
* @return true 为自动 UUID
|
||||
*/
|
||||
public boolean isAutoUUID() {
|
||||
return isAutoUUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自动大写 UUID
|
||||
*
|
||||
* @return true 为自动大写 UUID
|
||||
*/
|
||||
public boolean isAutoUpperUUID() {
|
||||
return isAutoUpperUUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
private static final String DEV_LANG_CONFIG = "dev.lang";
|
||||
|
||||
/**
|
||||
* 创建全局异常处理器
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.imyeyu.java.bean.timi.TimiResponse;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.annotation.AOPLogInterceptor;
|
||||
import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
|
||||
import lombok.Data;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -27,6 +28,7 @@ import java.util.Objects;
|
||||
* @author 夜雨
|
||||
* @version 2023-04-30 00:59
|
||||
*/
|
||||
@Data
|
||||
@RestControllerAdvice
|
||||
public class GlobalReturnHandler implements ResponseBodyAdvice<Object> {
|
||||
|
||||
@@ -77,22 +79,4 @@ public class GlobalReturnHandler implements ResponseBodyAdvice<Object> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多语言头处理回调
|
||||
*
|
||||
* @return 处理回调
|
||||
*/
|
||||
public CallbackArgReturn<LanguageMsgMapping<?>, String> getMultilingualHeader() {
|
||||
return multilingualHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置多语言头处理回调
|
||||
*
|
||||
* @param multilingualHeader 处理回调
|
||||
*/
|
||||
public void setMultilingualHeader(CallbackArgReturn<LanguageMsgMapping<?>, String> multilingualHeader) {
|
||||
this.multilingualHeader = multilingualHeader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.imyeyu.spring.util;
|
||||
|
||||
import com.imyeyu.spring.config.AbstractRedisConfig;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
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.core.Cursor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@@ -27,30 +30,13 @@ import java.util.function.Consumer;
|
||||
* @author 夜雨
|
||||
* @version 2021-11-21 09:58
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Redis<K, V> {
|
||||
|
||||
private final RedisSerializer<K> serializer;
|
||||
private final RedisTemplate<K, V> redis;
|
||||
|
||||
/**
|
||||
* 创建 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;
|
||||
}
|
||||
private RedisTemplate<K, V> redis;
|
||||
private RedisSerializer<K> serializer;
|
||||
|
||||
/**
|
||||
* 加锁
|
||||
@@ -101,8 +87,8 @@ public class Redis<K, V> {
|
||||
* @param value 值
|
||||
*/
|
||||
public void setAndKeepTTL(K key, V value) {
|
||||
Long expire = redis.getExpire(key, TimeUnit.MILLISECONDS);
|
||||
if (expire == null || expire <= 0) {
|
||||
long expire = redis.getExpire(key, TimeUnit.MILLISECONDS);
|
||||
if (expire <= 0) {
|
||||
// 判死
|
||||
destroy(key);
|
||||
} else {
|
||||
@@ -210,8 +196,8 @@ public class Redis<K, V> {
|
||||
public Map<K, List<V>> getAllList() {
|
||||
Map<K, List<V>> r = new HashMap<>();
|
||||
List<K> ks = keys("*");
|
||||
for (int i = 0; i < ks.size(); i++) {
|
||||
r.put(ks.get(i), getList(ks.get(i)));
|
||||
for (K k : ks) {
|
||||
r.put(k, getList(k));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -235,9 +221,9 @@ public class Redis<K, V> {
|
||||
*/
|
||||
public List<V> values() {
|
||||
List<V> r = new ArrayList<>();
|
||||
List<K> keys = keys("*");
|
||||
for (K key : keys) {
|
||||
r.add(get(key));
|
||||
List<K> ks = keys("*");
|
||||
for (K k : ks) {
|
||||
r.add(get(k));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -250,8 +236,8 @@ public class Redis<K, V> {
|
||||
public Map<K, V> map() {
|
||||
Map<K, V> r = new HashMap<>();
|
||||
List<K> ks = keys("*");
|
||||
for (int i = 0; i < ks.size(); i++) {
|
||||
r.put(ks.get(i), get(ks.get(i)));
|
||||
for (K k : ks) {
|
||||
r.put(k, get(k));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -280,8 +266,7 @@ public class Redis<K, V> {
|
||||
*/
|
||||
public boolean destroy(K key) {
|
||||
if (TimiJava.isNotEmpty(key) && has(key)) {
|
||||
Boolean isSucceed = redis.delete(key);
|
||||
return isSucceed != null && isSucceed;
|
||||
return redis.delete(key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
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.SerializationException;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
@@ -83,20 +85,34 @@ public class RedisSerializers {
|
||||
};
|
||||
|
||||
/**
|
||||
* Gson 序列化
|
||||
* Json 序列化
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
* @param clazz 数据类型
|
||||
* @return Redis 序列化器
|
||||
*/
|
||||
public static <T> RedisSerializer<T> gsonSerializer(Class<T> clazz) {
|
||||
return new RedisSerializer<>() {
|
||||
public static <T> RedisSerializer<T> jacksonSerializer(Class<T> clazz) {
|
||||
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
|
||||
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
|
||||
@@ -104,7 +120,11 @@ public class RedisSerializers {
|
||||
if (bytes == 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@ public class YamlPropertySourceFactory implements PropertySourceFactory {
|
||||
@Override
|
||||
public @org.springframework.lang.NonNull PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
|
||||
List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
|
||||
return sources.get(0);
|
||||
return sources.getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user