重构文件类型相关Service以支持国际化查询 添加findOneByLang辅助方法统一查询逻辑 实现StringConverter支持UI控件显示 优化缓存配置和查询性能 新增UnitStringConverter和CustomerCatalogStringConverter 完善文档和测试用例
280 lines
11 KiB
Java
280 lines
11 KiB
Java
package com.ecep.contract.service;
|
||
|
||
import com.ecep.contract.PageArgument;
|
||
import com.ecep.contract.PageContent;
|
||
import com.ecep.contract.WebSocketClientService;
|
||
import com.ecep.contract.model.IdentityEntity;
|
||
import com.ecep.contract.model.NamedEntity;
|
||
import com.ecep.contract.util.ParamUtils;
|
||
import com.ecep.contract.vm.IdentityViewModel;
|
||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||
import com.fasterxml.jackson.databind.JsonNode;
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
import javafx.util.StringConverter;
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.data.domain.Page;
|
||
import org.springframework.data.domain.Pageable;
|
||
|
||
import java.lang.reflect.Type;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.concurrent.CompletableFuture;
|
||
import java.util.function.Supplier;
|
||
|
||
public class QueryService<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
||
implements ViewModelService<T, TV> {
|
||
// 添加日志记录器
|
||
private static final Logger logger = LoggerFactory.getLogger(QueryService.class);
|
||
@Autowired
|
||
protected WebSocketClientService webSocketService;
|
||
@Autowired
|
||
protected ObjectMapper objectMapper;
|
||
|
||
@SuppressWarnings("unchecked")
|
||
public TV createNewViewModel() {
|
||
try {
|
||
Type genericSuperclass = getClass().getGenericSuperclass();
|
||
String typeName = genericSuperclass.getTypeName();
|
||
// System.out.println("typeName = " + typeName);
|
||
String clz = typeName.split("<")[1].split(">")[0].split(",")[1].trim();
|
||
// System.out.println("clz = " + clz);
|
||
Class<?> clazz = Class.forName(clz);
|
||
return (TV) clazz.getDeclaredConstructor().newInstance();
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("无法创建ViewModel实例", e);
|
||
}
|
||
}
|
||
|
||
@SuppressWarnings("unchecked")
|
||
public T createNewEntity() {
|
||
try {
|
||
Type genericSuperclass = getClass().getGenericSuperclass();
|
||
String typeName = genericSuperclass.getTypeName();
|
||
// System.out.println("typeName = " + typeName);
|
||
String clz = typeName.split("<")[1].split(">")[0].split(",")[0].trim();
|
||
// System.out.println("clz = " + clz);
|
||
Class<?> clazz = Class.forName(clz);
|
||
return (T) clazz.getDeclaredConstructor().newInstance();
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("无法创建Entity实例", e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public T save(T entity) {
|
||
try {
|
||
return async("save", entity, entity.getClass().getName()).handle((response, ex) -> {
|
||
if (ex != null) {
|
||
throw new RuntimeException("保存实体失败", ex);
|
||
}
|
||
if (response != null) {
|
||
try {
|
||
objectMapper.updateValue(entity, response);
|
||
} catch (JsonMappingException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
return entity;
|
||
}).get();
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("保存实体失败", e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void delete(T entity) {
|
||
try {
|
||
async("delete", entity, entity.getClass().getName()).handle((response, ex) -> {
|
||
if (ex != null) {
|
||
throw new RuntimeException("删除实体失败", ex);
|
||
}
|
||
if (response != null) {
|
||
return updateValue(entity, response);
|
||
}
|
||
return null;
|
||
}).get();
|
||
} catch (Exception e) {
|
||
logger.error("删除实体失败 #{}", entity.getId(), e);
|
||
throw new RuntimeException("删除实体失败", e);
|
||
}
|
||
}
|
||
|
||
public T updateValue(T entity, JsonNode node) {
|
||
try {
|
||
objectMapper.updateValue(entity, node);
|
||
} catch (JsonMappingException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
return entity;
|
||
}
|
||
|
||
public CompletableFuture<JsonNode> async(String method, Object... params) {
|
||
return webSocketService.invoke(getBeanName(), method, params).handle((response, ex) -> {
|
||
if (ex != null) {
|
||
throw new RuntimeException("远程方法+" + method + "+调用失败", ex);
|
||
}
|
||
return response;
|
||
});
|
||
}
|
||
|
||
public CompletableFuture<T> asyncFindById(Integer id) {
|
||
return async("findById", id, Integer.class).handle((response, ex) -> {
|
||
if (ex != null) {
|
||
throw new RuntimeException("查询实体失败", ex);
|
||
}
|
||
T newEntity = createNewEntity();
|
||
return updateValue(newEntity, response);
|
||
});
|
||
}
|
||
|
||
@Override
|
||
public T findById(Integer id) {
|
||
try {
|
||
return asyncFindById(id).get();
|
||
} catch (Exception e) {
|
||
logger.error("查询实体失败 #{}", id, e);
|
||
throw new RuntimeException("查询实体失败", e);
|
||
}
|
||
}
|
||
|
||
public List<T> findAll() {
|
||
return findAll(null, Pageable.unpaged()).getContent();
|
||
}
|
||
|
||
/**
|
||
* 异步查询所有实体数据,返回CompletableFuture对象
|
||
*
|
||
* @param params 查询参数映射表,可以包含过滤条件等信息
|
||
* @param pageable 分页参数,包含页码、每页条数、排序规则等
|
||
* @return 包含分页结果的CompletableFuture对象
|
||
*/
|
||
public CompletableFuture<Page<T>> asyncFindAll(Map<String, Object> params, Pageable pageable) {
|
||
// 调用async方法发送WebSocket请求,获取异步响应结果
|
||
return async("findAll", params, PageArgument.of(pageable)).handle((response, ex) -> {
|
||
if (ex != null) {
|
||
throw new RuntimeException("远程方法+findAll+调用失败", ex);
|
||
}
|
||
try {
|
||
// 将响应结果转换为PageContent对象,使用当前类的createNewEntity方法创建实体实例
|
||
PageContent<T> pageContent = of(response, objectMapper, this::createNewEntity);
|
||
// 将PageContent转换为Spring Data的Page对象并返回
|
||
return pageContent.toPage();
|
||
} catch (Exception e) {
|
||
// 处理转换过程中的异常,包装为RuntimeException并附带响应内容
|
||
throw new RuntimeException("响应结果转换失败, JSON = " + response.toString(), e);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 同步查询所有实体数据,实现了ViewModelService接口的findAll方法
|
||
*
|
||
* @param params 查询参数映射表,可以包含过滤条件等信息
|
||
* @param pageable 分页参数,包含页码、每页条数、排序规则等
|
||
* @return 包含查询结果的Page对象
|
||
* @throws RuntimeException 当查询失败时抛出异常
|
||
*/
|
||
@Override
|
||
public Page<T> findAll(Map<String, Object> params, Pageable pageable) {
|
||
try {
|
||
if (pageable == null) {
|
||
pageable = Pageable.unpaged();
|
||
}
|
||
// 调用异步方法并阻塞等待结果返回
|
||
return asyncFindAll(params, pageable).get();
|
||
} catch (Exception e) {
|
||
// 处理异步查询过程中的任何异常,转换为运行时异常抛出
|
||
throw new RuntimeException("查询失败, 参数:" + params, e);
|
||
}
|
||
}
|
||
|
||
public T findOneByProperty(String propertyName, Object propertyValue) {
|
||
return findAll(ParamUtils.builder().equals(propertyName, propertyValue).build(), Pageable.ofSize(1)).stream()
|
||
.findFirst().orElse(null);
|
||
}
|
||
|
||
/**
|
||
* 异步执行计数操作,返回CompletableFuture<Long>类型结果
|
||
*
|
||
* @param params 包含计数所需参数的Map集合
|
||
* @return 返回一个CompletableFuture<Long>对象,用于获取异步操作的结果
|
||
*/
|
||
public CompletableFuture<Long> asyncCount(Map<String, Object> params) {
|
||
// 调用async方法执行名为"count"的异步操作,传入参数params
|
||
// 使用handle方法处理异步操作的结果或异常
|
||
return async("count", params).handle((response, ex) -> {
|
||
if (ex != null) {
|
||
throw new RuntimeException("远程方法+count+调用失败", ex);
|
||
}
|
||
// 将响应结果转换为Long类型并返回
|
||
return response.asLong();
|
||
});
|
||
}
|
||
|
||
public long count(Map<String, Object> params) {
|
||
try {
|
||
return asyncCount(params).get();
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("计数失败", e);
|
||
}
|
||
}
|
||
|
||
public List<T> search(String searchText) {
|
||
ParamUtils.Builder params = getSpecification(searchText);
|
||
List<T> list = findAll(params.build(), Pageable.ofSize(10)).getContent();
|
||
return list;
|
||
}
|
||
|
||
public static <T> PageContent<T> of(JsonNode response, ObjectMapper objectMapper, Supplier<T> createNewEntity)
|
||
throws JsonProcessingException {
|
||
|
||
PageContent<T> pageContent = new PageContent<>();
|
||
List<T> content = new ArrayList<>();
|
||
if (response.has("content")) {
|
||
JsonNode contentNode = response.get("content");
|
||
if (contentNode != null && contentNode.isArray()) {
|
||
for (JsonNode node : contentNode) {
|
||
T newEntity = createNewEntity.get();
|
||
objectMapper.updateValue(newEntity, node);
|
||
content.add(newEntity);
|
||
}
|
||
}
|
||
}
|
||
pageContent.setContent(content);
|
||
if (response.has("page")) {
|
||
JsonNode pageNode = response.get("page");
|
||
PageArgument pageArgument = objectMapper.treeToValue(pageNode, PageArgument.class);
|
||
pageContent.setPage(pageArgument);
|
||
}
|
||
if (response.has("totalElements")) {
|
||
int totalElements = response.get("totalElements").asInt();
|
||
pageContent.setTotalElements(totalElements);
|
||
}
|
||
if (response.has("totalPages")) {
|
||
int totalPages = response.get("totalPages").asInt();
|
||
pageContent.setTotalPages(totalPages);
|
||
}
|
||
return pageContent;
|
||
}
|
||
|
||
public StringConverter<T> getStringConverter() {
|
||
return new StringConverter<>() {
|
||
@Override
|
||
public String toString(T object) {
|
||
if (object instanceof NamedEntity named) {
|
||
return named.getName();
|
||
}
|
||
return object.toString();
|
||
}
|
||
|
||
@Override
|
||
public T fromString(String string) {
|
||
return null;
|
||
}
|
||
};
|
||
}
|
||
} |