refactor(service): 修改IEntityService泛型为VO类型并优化缓存策略

重构所有注解@CacheConfig的Service类,将IEntityService泛型从实体类改为VO类
实现实体与VO之间的转换逻辑,使用VO替代实体进行缓存以避免序列化问题
更新相关依赖组件和测试用例,确保功能完整性和系统兼容性
优化Redis缓存配置,清理旧缓存数据并验证新缓存策略有效性
This commit is contained in:
2025-09-28 18:19:00 +08:00
parent df6188db40
commit b03b5385a5
75 changed files with 3144 additions and 1377 deletions

View File

@@ -0,0 +1,389 @@
package com.ecep.contract.service;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import com.ecep.contract.IEntityService;
import com.ecep.contract.PageArgument;
import com.ecep.contract.PageContent;
import com.ecep.contract.QueryService;
import com.ecep.contract.SpringApp;
import com.ecep.contract.constant.WebSocketConstant;
import com.ecep.contract.handler.SessionInfo;
import com.ecep.contract.model.Voable;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@Service
public class WebSocketServerCallbackManager {
static final Logger logger = LoggerFactory.getLogger(WebSocketServerCallbackManager.class);
private final ObjectMapper objectMapper;
public WebSocketServerCallbackManager(@Autowired ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public void onMessage(SessionInfo session, JsonNode jsonNode) {
// 处理 messageId 的消息
String messageId = jsonNode.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME).asText();
try {
Object result = handleAsMessageCallback(session, messageId, jsonNode);
send(session, messageId, result);
} catch (Exception e) {
sendError(session, messageId, 500, e.getMessage());
logger.warn("处理消息回调失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
}
}
private void sendError(SessionInfo session, String messageId, int errorCode, String message) {
try {
session.sendError(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId, errorCode, message);
} catch (IOException e) {
logger.warn("发送错误消息失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
}
}
private void send(SessionInfo session, String messageId, Object data) {
Map<String, Object> map = new HashMap<>();
if (data instanceof Voable<?>) {
map.put("data", ((Voable<?>) data).toVo());
} else {
map.put("data", data);
}
map.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
map.put(WebSocketConstant.SUCCESS_FIELD_NAME, true);
try {
session.send(map);
} catch (IOException e) {
logger.warn("发送消息失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
}
}
private Object handleAsMessageCallback(SessionInfo session, String messageId, JsonNode jsonNode)
throws Exception {
if (!jsonNode.has(WebSocketConstant.SERVICE_FIELD_NAME)) {
throw new IllegalArgumentException("缺失 service 参数");
}
String serviceName = jsonNode.get(WebSocketConstant.SERVICE_FIELD_NAME).asText();
Object service = null;
try {
service = SpringApp.getBean(serviceName);
} catch (Exception e) {
throw new IllegalArgumentException("未找到服务: " + serviceName);
}
if (!jsonNode.has(WebSocketConstant.METHOD_FIELD_NAME)) {
throw new IllegalArgumentException("缺失 method 参数");
}
String methodName = jsonNode.get(WebSocketConstant.METHOD_FIELD_NAME).asText();
JsonNode argumentsNode = jsonNode.get(WebSocketConstant.ARGUMENTS_FIELD_NAME);
Object result = null;
if (methodName.equals("findAll")) {
result = invokerFindAllMethod(service, argumentsNode);
} else if (methodName.equals("findById")) {
result = invokerFindByIdMethod(service, argumentsNode);
} else if (methodName.equals("save")) {
result = invokerSaveMethod(service, argumentsNode);
} else if (methodName.equals("delete")) {
result = invokerDeleteMethod(service, argumentsNode);
} else if (methodName.equals("count")) {
result = invokerCountMethod(service, argumentsNode);
} else {
result = invokerOtherMethod(service, methodName, argumentsNode);
}
return result;
}
private Object invokerOtherMethod(Object service, String methodName, JsonNode argumentsNode)
throws NoSuchMethodException, SecurityException, InvocationTargetException, IllegalAccessException,
ClassNotFoundException, JsonProcessingException {
int size = argumentsNode.size();
Class<?> targetClass = getTargetClass(service.getClass());
if (size == 0) {
Method method = targetClass.getMethod(methodName);
return method.invoke(service);
}
if (!argumentsNode.get(0).isArray()) {
Class<?> parameterType = Class.forName(argumentsNode.get(1).asText());
Object arg = objectMapper.treeToValue(argumentsNode.get(0), parameterType);
Method method = targetClass.getMethod(methodName, parameterType);
return method.invoke(service, arg);
}
// 参数值
JsonNode paramsNode = argumentsNode.get(0);
// 参数类型
JsonNode typesNode = argumentsNode.get(1);
Class<?>[] parameterTypes = new Class<?>[typesNode.size()];
Object[] args = new Object[paramsNode.size()];
for (int i = 0; i < typesNode.size(); i++) {
String type = typesNode.get(i).asText();
parameterTypes[i] = Class.forName(type);
args[i] = objectMapper.treeToValue(paramsNode.get(i), parameterTypes[i]);
}
try {
Method method = targetClass.getMethod(methodName, parameterTypes);
return method.invoke(service, args);
} catch (NoSuchMethodException e) {
logger.error("NoSuchMethodException, targetClass: {}, Methods:{}", targetClass, targetClass.getMethods());
throw e;
}
}
private Object invokerDeleteMethod(Object service, JsonNode argumentsNode) {
JsonNode paramsNode = argumentsNode.get(0);
if (!paramsNode.has("id")) {
throw new IllegalArgumentException("缺失 id 参数");
}
int id = paramsNode.get("id").asInt();
IEntityService<Object> entityService = (IEntityService<Object>) service;
Object entity = entityService.findById(id);
if (entity == null) {
throw new NoSuchElementException("未找到实体: #" + id);
}
entityService.delete(entity);
return entity;
}
private Object invokerSaveMethod(Object service, JsonNode argumentsNode) throws JsonMappingException {
JsonNode paramsNode = argumentsNode.get(0);
if (service instanceof IEntityService<?> entityService) {
Object entity = null;
if (paramsNode.has("id") && !paramsNode.get("id").isNull()) {
int id = paramsNode.get("id").asInt();
entity = entityService.findById(id);
if (entity == null) {
throw new NoSuchElementException("未找到实体: #" + id);
}
} else {
entity = createNewEntity(entityService);
}
if (service instanceof VoableService<?, ?>) {
String typeClz = argumentsNode.get(1).asText();
Class<?> type = null;
try {
type = Class.forName(typeClz);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Object object = objectMapper.convertValue(paramsNode, type);
((VoableService<Object, Object>) service).updateByVo(entity, object);
} else {
objectMapper.updateValue(entity, paramsNode);
}
return ((IEntityService<Object>) entityService).save(entity);
}
return null;
}
private <T> T createNewEntity(IEntityService<T> entityService) {
try {
// 通过分析接口的泛型参数来获取实体类型
Class<?> serviceClass = entityService.getClass();
// 1. 直接检查接口
Class<T> entityClass = findEntityTypeInInterfaces(serviceClass);
if (entityClass != null) {
return entityClass.getDeclaredConstructor().newInstance();
}
// 2. 处理Spring代理类 - 获取原始类
Class<?> targetClass = getTargetClass(serviceClass);
if (targetClass != serviceClass) {
entityClass = findEntityTypeInInterfaces(targetClass);
if (entityClass != null) {
return entityClass.getDeclaredConstructor().newInstance();
}
}
// 3. 尝试查找父类
entityClass = findEntityTypeInSuperclass(serviceClass);
if (entityClass != null) {
return entityClass.getDeclaredConstructor().newInstance();
}
// 4. 如果上述方法都失败,尝试从参数类型推断
entityClass = findEntityTypeFromMethodParameters(serviceClass);
if (entityClass != null) {
return entityClass.getDeclaredConstructor().newInstance();
}
// 如果所有方法都失败,抛出更具描述性的异常
throw new UnsupportedOperationException("无法确定实体类型,请检查服务实现: " + serviceClass.getName());
} catch (Exception e) {
throw new RuntimeException("无法创建Entity实例: " + e.getMessage(), e);
}
}
/**
* 从接口中查找实体类型
*/
@SuppressWarnings("unchecked")
private <T> Class<T> findEntityTypeInInterfaces(Class<?> serviceClass) {
Type[] interfaces = serviceClass.getGenericInterfaces();
for (Type iface : interfaces) {
if (iface instanceof ParameterizedType paramType) {
if (IEntityService.class.isAssignableFrom((Class<?>) paramType.getRawType())) {
// 获取IEntityService的泛型参数类型
Type entityType = paramType.getActualTypeArguments()[0];
if (entityType instanceof Class<?>) {
return (Class<T>) entityType;
} else if (entityType instanceof ParameterizedType) {
// 处理参数化类型
Type rawType = ((ParameterizedType) entityType).getRawType();
if (rawType instanceof Class<?>) {
return (Class<T>) rawType;
}
}
}
}
}
return null;
}
/**
* 从父类中查找实体类型
*/
@SuppressWarnings("unchecked")
private <T> Class<T> findEntityTypeInSuperclass(Class<?> serviceClass) {
Type genericSuperclass = serviceClass.getGenericSuperclass();
while (genericSuperclass != null && genericSuperclass != Object.class) {
if (genericSuperclass instanceof ParameterizedType paramType) {
Type rawType = paramType.getRawType();
if (rawType instanceof Class<?> && IEntityService.class.isAssignableFrom((Class<?>) rawType)) {
Type entityType = paramType.getActualTypeArguments()[0];
if (entityType instanceof Class<?>) {
return (Class<T>) entityType;
}
}
}
// 继续查找父类的父类
if (genericSuperclass instanceof Class<?>) {
genericSuperclass = ((Class<?>) genericSuperclass).getGenericSuperclass();
} else {
break;
}
}
return null;
}
/**
* 尝试从方法参数类型推断实体类型
*/
@SuppressWarnings("unchecked")
private <T> Class<T> findEntityTypeFromMethodParameters(Class<?> serviceClass) {
try {
// 尝试通过findById方法推断实体类型
Method findByIdMethod = serviceClass.getMethod("findById", Integer.class);
if (findByIdMethod != null) {
return (Class<T>) findByIdMethod.getReturnType();
}
// 尝试通过findAll方法推断实体类型
Method[] methods = serviceClass.getMethods();
for (Method method : methods) {
if (method.getName().equals("findAll") && method.getParameterCount() > 0) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType paramType &&
paramType.getRawType() instanceof Class<?> &&
"org.springframework.data.domain.Page"
.equals(((Class<?>) paramType.getRawType()).getName())) {
Type[] actualTypeArguments = paramType.getActualTypeArguments();
if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class<?>) {
return (Class<T>) actualTypeArguments[0];
}
}
}
}
} catch (Exception e) {
// 忽略异常,继续尝试其他方法
}
return null;
}
/**
* 获取被代理的原始类
*/
private Class<?> getTargetClass(Class<?> proxyClass) {
// 处理CGLIB代理类
if (proxyClass.getName().contains("$$SpringCGLIB$$")) {
return proxyClass.getSuperclass();
}
return proxyClass;
}
/*
* see client QueryService#findById(Integer)
*/
private Object invokerFindByIdMethod(Object service, JsonNode argumentsNode) {
JsonNode paramsNode = argumentsNode.get(0);
if (service instanceof IEntityService<?> entityService) {
Integer id = paramsNode.asInt();
return entityService.findById(id);
}
try {
JsonNode typesNode = argumentsNode.get(1);
if (paramsNode.isInt()) {
Method method = service.getClass().getMethod("findById", Integer.class);
return method.invoke(service, paramsNode.asInt());
}
if (paramsNode.isTextual()) {
Method method = service.getClass().getMethod("findById", String.class);
return method.invoke(service, paramsNode.asText());
}
throw new IllegalArgumentException("unable to invoke findById method, paramsNode is not int or text");
} catch (Exception e) {
throw new RuntimeException("unable to invoke findById method", e);
}
}
private Object invokerFindAllMethod(Object service, JsonNode argumentsNode) throws JsonProcessingException {
JsonNode paramsNode = argumentsNode.get(0);
JsonNode pageableNode = argumentsNode.get(1);
PageArgument pageArgument = objectMapper.treeToValue(pageableNode, PageArgument.class);
QueryService<?> entityService = (QueryService<?>) service;
Page<?> page = entityService.findAll(paramsNode, pageArgument.toPageable());
return PageContent.of(page.map(entity -> {
if (entity instanceof Voable<?>) {
return ((Voable<?>) entity).toVo();
}
return entity;
}));
}
private Object invokerCountMethod(Object service, JsonNode argumentsNode) {
JsonNode paramsNode = argumentsNode.get(0);
if (service instanceof QueryService<?> entityService) {
return entityService.count(paramsNode);
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.ds.company.tasker;
package com.ecep.contract.service.tasker;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.cloud.rk.CloudRkService;
@@ -13,15 +13,12 @@ import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.model.CloudRk;
import com.ecep.contract.model.CloudYu;
import com.ecep.contract.model.Company;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.util.MyStringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.ds.customer.tasker;
package com.ecep.contract.service.tasker;
import static com.ecep.contract.util.ExcelUtils.setCellValue;
@@ -32,7 +32,6 @@ import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.util.CompanyUtils;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.ds.customer.tasker;
package com.ecep.contract.service.tasker;
import java.time.LocalDate;
import java.util.Comparator;
@@ -12,7 +12,6 @@ import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.ds.customer.tasker;
package com.ecep.contract.service.tasker;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
@@ -8,7 +8,6 @@ import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -1,18 +1,14 @@
package com.ecep.contract.ds.company.tasker;
package com.ecep.contract.service.tasker;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.tasker.ContractVerifyComm;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -1,10 +1,10 @@
package com.ecep.contract.ds.contract.tasker;
package com.ecep.contract.service.tasker;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ds.contract.tasker.AbstContractRepairTasker;
import com.ecep.contract.ds.contract.tasker.ContractRepairComm;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.util.StringUtils;
@@ -13,12 +13,11 @@ import com.ecep.contract.MessageHolder;
import com.ecep.contract.model.Contract;
import lombok.Getter;
import lombok.Setter;
/**
* 合同修复任务
*/
public class ContractRepairTask extends AbstContractRepairTasker implements WebSocketServerTasker {
public class ContractRepairTasker extends AbstContractRepairTasker implements WebSocketServerTasker {
@Getter
private Contract contract;
@Getter
@@ -27,7 +26,7 @@ public class ContractRepairTask extends AbstContractRepairTasker implements WebS
private final ContractRepairComm comm = new ContractRepairComm();
public ContractRepairTask() {
public ContractRepairTasker() {
}
public void init(JsonNode argsNode) {

View File

@@ -1,11 +1,11 @@
package com.ecep.contract.ds.contract.tasker;
package com.ecep.contract.service.tasker;
import java.util.Locale;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.tasker.ContractVerifyComm;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
@@ -24,15 +24,18 @@ public class ContractVerifyTasker extends Tasker<Object> implements WebSocketSer
@Override
protected Object execute(MessageHolder holder) {
updateTitle("验证合同 " + contract.getCode() + " 及其子合同是否符合合规要求");
updateProgress(1, 10);
if (!comm.verify(contract, holder)) {
passed = false;
}
updateProgress(9, 10);
if (passed) {
holder.info("合规验证通过");
} else {
holder.error("合规验证不通过");
}
updateProgress(10, 10);
updateProperty("passed", passed);
return null;
}

View File

@@ -1,13 +1,11 @@
package com.ecep.contract.ds.customer.tasker;
package com.ecep.contract.service.tasker;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.ds.project;
package com.ecep.contract.service.tasker;
import java.time.LocalDateTime;
import java.util.ArrayList;
@@ -26,7 +26,6 @@ import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectCost;
import com.ecep.contract.model.ProjectCostItem;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.util.NumberUtils;
import com.ecep.contract.util.TaxRateUtils;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.service;
package com.ecep.contract.service.tasker;
import java.io.IOException;
import java.io.InputStream;
@@ -42,11 +42,17 @@ public class WebSocketServerTaskManager implements InitializingBean {
Resource resource = resourceLoader.getResource("classpath:tasker_mapper.json");
try (InputStream inputStream = resource.getInputStream()) {
JsonNode rootNode = objectMapper.readTree(inputStream);
JsonNode taskersNode = rootNode.get("taskers");
if (taskersNode != null && taskersNode.isObject()) {
JsonNode tasksNode = rootNode.get("tasks");
if (tasksNode != null && tasksNode.isObject()) {
Map<String, String> taskMap = new java.util.HashMap<>();
taskersNode.fields().forEachRemaining(entry -> {
tasksNode.fields().forEachRemaining(entry -> {
taskMap.put(entry.getKey(), entry.getValue().asText());
try {
Class.forName(entry.getValue().asText());
} catch (ClassNotFoundException e) {
logger.error("Failed to load task class: {}", entry.getValue().asText(), e);
}
});
taskClzMap = taskMap;
}

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.service;
package com.ecep.contract.service.tasker;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;