6 Commits

Author SHA1 Message Date
66eb6108cd Merge pull request 'v0.0.6' (#6) from dev into master
Reviewed-on: #6
2026-03-21 10:16:59 +00:00
82f950e71d Merge pull request 'v0.0.5' (#5) from dev into master
Reviewed-on: #5
2026-03-17 03:24:06 +00:00
cceabb0c8d Merge pull request 'v0.0.5' (#4) from dev into master
Reviewed-on: #4
2026-03-17 03:07:34 +00:00
62de8e4885 Merge pull request 'v0.0.4' (#3) from dev into master
Reviewed-on: #3
2026-03-16 09:18:09 +00:00
9f7460e959 Merge pull request 'v0.0.3' (#2) from dev into master
Reviewed-on: #2
2026-02-10 10:56:12 +00:00
90d4c5e5f6 Merge pull request 'v0.0.2' (#1) from dev into master
Reviewed-on: #1
2026-01-19 09:53:14 +00:00
6 changed files with 17 additions and 157 deletions

10
pom.xml
View File

@@ -7,19 +7,19 @@
<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.5.13</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.11</version> <version>0.0.6</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
<maven.test.skip>true</maven.test.skip> <maven.test.skip>true</maven.test.skip>
<springboot.version>3.5.13</springboot.version> <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>
@@ -131,12 +131,12 @@
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.5</version> <version>3.0.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
<version>2.13.1</version> <version>2.12.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.imyeyu.io</groupId> <groupId>com.imyeyu.io</groupId>

View File

@@ -1,43 +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 请求体中的单个字段并绑定到接口参数。
*
* <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

@@ -1,82 +0,0 @@
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

@@ -1,9 +1,13 @@
package com.imyeyu.spring.entity; package com.imyeyu.spring.bean;
import com.imyeyu.java.bean.Language; import com.imyeyu.java.bean.Language;
import com.imyeyu.java.ref.Ref; import com.imyeyu.java.ref.Ref;
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 com.imyeyu.spring.entity.Creatable;
import com.imyeyu.spring.entity.Deletable;
import com.imyeyu.spring.entity.IDEntity;
import com.imyeyu.spring.entity.Updatable;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -15,7 +19,7 @@ import lombok.EqualsAndHashCode;
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class Multilingual extends Language implements IDEntity<String>, Creatable, Updatable, Deletable, Destroyable { public class Multilingual extends Language implements IDEntity<String>, Creatable, Updatable, Deletable {
/** 唯一标识 */ /** 唯一标识 */
@Id @Id

View File

@@ -3,9 +3,6 @@ package com.imyeyu.spring.util;
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 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;
@@ -30,13 +27,7 @@ import java.util.function.Consumer;
* @author 夜雨 * @author 夜雨
* @version 2021-11-21 09:58 * @version 2021-11-21 09:58
*/ */
@Data public record Redis<K, V>(RedisTemplate<K, V> redis, RedisSerializer<K> serializer) {
@NoArgsConstructor
@AllArgsConstructor
public class Redis<K, V> {
private RedisTemplate<K, V> redis;
private RedisSerializer<K> serializer;
/** /**
* 加锁 * 加锁

View File

@@ -87,29 +87,19 @@ public class RedisSerializers {
/** /**
* Json 序列化 * Json 序列化
* *
* @param <T> 数据类型 * @param <T> 数据类型
* @param clazz 数据类型 * @param clazz 数据类型
* @return Redis 序列化器 * @return Redis 序列化器
*/ */
public static <T> RedisSerializer<T> jacksonSerializer(Class<T> clazz) { public static <T> RedisSerializer<T> jacksonSerializer(Class<T> clazz) {
return jacksonSerializer(new ObjectMapper(), clazz);
}
/**
* Json 序列化
*
* @param <T> 数据类型
* @param mapper 序列化对象
* @param clazz 数据类型
* @return Redis 序列化器
*/
public static <T> RedisSerializer<T> jacksonSerializer(ObjectMapper mapper, Class<T> clazz) {
return new RedisSerializer<>() { return new RedisSerializer<>() {
private static final ObjectMapper JACKSON = new ObjectMapper();
@Override @Override
public byte[] serialize(T object) throws SerializationException { public byte[] serialize(T object) throws SerializationException {
try { try {
return mapper.writeValueAsString(object).getBytes(StandardCharsets.UTF_8); return JACKSON.writeValueAsString(object).getBytes(StandardCharsets.UTF_8);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -121,7 +111,7 @@ public class RedisSerializers {
return null; return null;
} }
try { try {
return mapper.readValue(new String(bytes, StandardCharsets.UTF_8), clazz); return JACKSON.readValue(new String(bytes, StandardCharsets.UTF_8), clazz);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }