mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-09-03 06:35:25 +08:00
refactor: 优化拓展逻辑
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user