Files
contract-manager/client/src/main/java/com/ecep/contract/service/QueryService.java
songqq 09b0da498b feat(service): 实现国际化支持并优化Service层
重构文件类型相关Service以支持国际化查询
添加findOneByLang辅助方法统一查询逻辑
实现StringConverter支持UI控件显示
优化缓存配置和查询性能
新增UnitStringConverter和CustomerCatalogStringConverter
完善文档和测试用例
2025-09-24 16:20:49 +08:00

280 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
};
}
}