refactor: 优化拓展逻辑

This commit is contained in:
zhouhao
2024-12-20 11:37:38 +08:00
parent 022e94131c
commit 737e387df9
8 changed files with 198 additions and 50 deletions

View File

@@ -0,0 +1,69 @@
package org.hswebframework.web.api.crud.entity;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.ezorm.core.Extensible;
import java.util.Collections;
import java.util.Map;
/**
* 可扩展的实体类
* <p>
* <ul>
* <li>
* 实体类继承此类,或者实现{@link Extensible}接口.
* </li>
* <li>
* 使用{@link org.hswebframework.web.crud.configuration.TableMetadataCustomizer}自定义表结构
* </li>
* <li>
* json序列化时,默认会将拓展字段平铺到json中.
* </li>
* </ul>
*
* @param <PK> 主键类型
* @see JsonAnySetter
* @see JsonAnyGetter
* @since 4.0.18
*/
@Getter
@Setter
public class ExtensibleEntity<PK> extends GenericEntity<PK> implements Extensible {
private Map<String, Object> extensions;
/**
* 默认不序列化扩展属性,会由{@link ExtensibleEntity#extensions()},{@link JsonAnyGetter}平铺到json中.
*
* @return 扩展属性
*/
@JsonIgnore
public Map<String, Object> getExtensions() {
return extensions;
}
@Override
@JsonAnyGetter
public Map<String, Object> extensions() {
return extensions == null ? Collections.emptyMap() : extensions;
}
@Override
public Object getExtension(String property) {
Map<String, Object> ext = this.extensions;
return ext == null ? null : ext.get(property);
}
@Override
@JsonAnySetter
public synchronized void setExtension(String property, Object value) {
if (extensions == null) {
extensions = new java.util.HashMap<>();
}
extensions.put(property, value);
}
}

View File

@@ -26,6 +26,7 @@ import org.hswebframework.web.bean.ToString;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Map;
/**

View File

@@ -0,0 +1,33 @@
package org.hswebframework.web.api.crud.entity;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.junit.Test;
import static org.junit.Assert.*;
public class ExtensibleEntityTest {
@Test
@SneakyThrows
public void testJson() {
ExtensibleEntity<String> entity = new ExtensibleEntity<>();
entity.setId("test");
entity.setExtension("extName", "test");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerFor(ExtensibleEntity.class).writeValueAsString(entity);
System.out.println(json);
ExtensibleEntity<String> decoded = mapper.readerFor(ExtensibleEntity.class).readValue(json);
assertNotNull(decoded.getId());
assertEquals(entity.getId(), decoded.getId());
assertNotNull(decoded.getExtension("extName"));
assertEquals(entity.getExtension("extName"), decoded.getExtension("extName"));
}
}

View File

@@ -30,15 +30,16 @@ public class CrudTests {
entity.setAge(1);
entity.setName("test");
Mono.just(entity)
.cast(TestEntity.class)
.as(service::insert)
.as(StepVerifier::create)
.expectNext(1)
.verifyComplete();
entity.setExtension("extName", "test");
service.insert(entity)
.as(StepVerifier::create)
.expectNext(1)
.verifyComplete();
Assert.assertNotNull(entity.getId());
service.findById(entity.getId())
.doOnNext(System.out::println)
.as(StepVerifier::create)
.expectNextMatches(e -> e instanceof CustomTestEntity)
.verifyComplete();

View File

@@ -4,6 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hswebframework.web.api.crud.entity.ExtensibleEntity;
import org.hswebframework.web.api.crud.entity.GenericEntity;
import org.hswebframework.web.crud.annotation.EnableEntityEvent;
import org.hswebframework.web.crud.generator.Generators;
@@ -18,7 +19,7 @@ import javax.persistence.Table;
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
@EnableEntityEvent
public class TestEntity extends GenericEntity<String> {
public class TestEntity extends ExtensibleEntity<String> {
@Column(length = 32)
private String name;

View File

@@ -1,15 +1,48 @@
package org.hswebframework.web.crud.service;
import org.hswebframework.ezorm.rdb.metadata.DataType;
import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata;
import org.hswebframework.web.crud.configuration.TableMetadataCustomizer;
import org.hswebframework.web.crud.entity.CustomTestEntity;
import org.hswebframework.web.crud.entity.TestEntity;
import org.hswebframework.web.crud.entity.factory.EntityMappingCustomizer;
import org.hswebframework.web.crud.entity.factory.MapperEntityFactory;
import org.springframework.stereotype.Component;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.sql.JDBCType;
import java.util.Set;
@Component
public class CustomTestCustom implements EntityMappingCustomizer {
public class CustomTestCustom implements EntityMappingCustomizer, TableMetadataCustomizer {
@Override
public void custom(MapperEntityFactory factory) {
factory.addMapping(TestEntity.class, new MapperEntityFactory.Mapper<>(CustomTestEntity.class,CustomTestEntity::new));
factory.addMapping(TestEntity.class, new MapperEntityFactory.Mapper<>(CustomTestEntity.class, CustomTestEntity::new));
}
@Override
public void customColumn(Class<?> entityType,
PropertyDescriptor descriptor,
Field field,
Set<Annotation> annotations,
RDBColumnMetadata column) {
}
@Override
public void customTable(Class<?> entityType, RDBTableMetadata table) {
if (TestEntity.class.isAssignableFrom(entityType)) {
RDBColumnMetadata col = table.newColumn();
col.setName("ext_name");
col.setAlias("extName");
col.setLength(32);
col.setType(DataType.jdbc(JDBCType.VARCHAR, String.class));
table.addColumn(col);
}
}
}

View File

@@ -65,14 +65,7 @@ public interface I18nSupportEntity {
return defaultMessage;
}
String msg = entries.get(locale.toString());
if (msg != null) {
return msg;
}
return entries
.getOrDefault(locale.getCountry(), defaultMessage);
return LocaleUtils.getMessage(entries::get, locale, () -> defaultMessage);
}
}

View File

@@ -13,10 +13,7 @@ import reactor.util.context.Context;
import javax.annotation.Nonnull;
import java.util.Locale;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.*;
/**
* 用于进行国际化消息转换
@@ -39,6 +36,25 @@ public final class LocaleUtils {
static MessageSource messageSource = UnsupportedMessageSource.instance();
/**
* 从指定数据源中获取国际化消息
*
* @param messageSource 消息源
* @param locale 语言地区
* @param defaultMessage 默认消息
*/
public static String getMessage(Function<String, String> messageSource,
Locale locale,
Supplier<String> defaultMessage) {
String str = locale.toString();
String msg = messageSource.apply(str);
if (msg == null) {
msg = messageSource.apply(locale.getLanguage());
}
return msg == null ? defaultMessage.get() : msg;
}
/**
* 获取当前的语言地区,如果没有设置则返回系统默认语言
*
@@ -113,24 +129,25 @@ public final class LocaleUtils {
@SuppressWarnings("all")
public static Mono<Locale> currentReactive() {
return Mono
.deferContextual(ctx -> Mono.just(ctx.getOrDefault(Locale.class, DEFAULT_LOCALE)));
.deferContextual(ctx -> Mono.just(ctx.getOrDefault(Locale.class, DEFAULT_LOCALE)));
}
public static <T> Mono<T> doInReactive(Callable<T> call) {
return currentReactive()
.handle((locale, sink) -> {
Locale old = CONTEXT_THREAD_LOCAL.get();
try {
CONTEXT_THREAD_LOCAL.set(locale);
T data = call.call();
if (data != null) {
sink.next(data);
}
} catch (Throwable e) {
sink.error(e);
} finally {
CONTEXT_THREAD_LOCAL.set(old);
.handle((locale, sink) -> {
Locale old = CONTEXT_THREAD_LOCAL.get();
try {
CONTEXT_THREAD_LOCAL.set(locale);
T data = call.call();
if (data != null) {
sink.next(data);
}
});
} catch (Throwable e) {
sink.error(e);
} finally {
CONTEXT_THREAD_LOCAL.set(old);
}
});
}
/**
@@ -271,11 +288,11 @@ public final class LocaleUtils {
BiFunction<S, String, R> mapper,
Object... args) {
return currentReactive()
.map(locale -> {
String msg = message.apply(source);
String newMsg = resolveMessage(messageSource, locale, msg, msg, args);
return mapper.apply(source, newMsg);
});
.map(locale -> {
String msg = message.apply(source);
String newMsg = resolveMessage(messageSource, locale, msg, msg, args);
return mapper.apply(source, newMsg);
});
}
/**
@@ -288,7 +305,7 @@ public final class LocaleUtils {
public static Mono<String> resolveMessageReactive(String code,
Object... args) {
return currentReactive()
.map(locale -> resolveMessage(messageSource, locale, code, code, args));
.map(locale -> resolveMessage(messageSource, locale, code, code, args));
}
/**
@@ -303,7 +320,7 @@ public final class LocaleUtils {
String code,
Object... args) {
return currentReactive()
.map(locale -> resolveMessage(messageSource, locale, code, code, args));
.map(locale -> resolveMessage(messageSource, locale, code, code, args));
}
/**
@@ -417,12 +434,12 @@ public final class LocaleUtils {
return publisher -> {
if (publisher instanceof Mono) {
return (T) Mono
.from(publisher)
.doOnEach(on(type, operation));
}
return (T) Flux
.from(publisher)
.doOnEach(on(type, operation));
}
return (T) Flux
.from(publisher)
.doOnEach(on(type, operation));
};
}
@@ -488,7 +505,7 @@ public final class LocaleUtils {
actual.currentContext().getOrDefault(Locale.class, DEFAULT_LOCALE),
(a, l) -> {
source.subscribe(
new LocaleSwitchSubscriber<>(a)
new LocaleSwitchSubscriber<>(a)
);
return null;
}
@@ -506,7 +523,7 @@ public final class LocaleUtils {
actual.currentContext().getOrDefault(Locale.class, DEFAULT_LOCALE),
(a, l) -> {
source.subscribe(
new LocaleSwitchSubscriber<>(a)
new LocaleSwitchSubscriber<>(a)
);
return null;
}
@@ -522,7 +539,7 @@ public final class LocaleUtils {
@Nonnull
public Context currentContext() {
return actual
.currentContext();
.currentContext();
}
@Override
@@ -532,7 +549,7 @@ public final class LocaleUtils {
private Locale current() {
return currentContext()
.getOrDefault(Locale.class, DEFAULT_LOCALE);
.getOrDefault(Locale.class, DEFAULT_LOCALE);
}
@Override