删除多余类
This commit is contained in:
parent
464266e455
commit
4a49d27766
@ -1,499 +0,0 @@
|
|||||||
package com.zhgd.xmgl.modules.validator;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
|
||||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.DatabaseMetaData;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class BulkEntityValidator {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("db1DataSource")
|
|
||||||
private DataSource dataSource;
|
|
||||||
|
|
||||||
private final Map<String, List<String>> tableColumnsCache = new ConcurrentHashMap<>();
|
|
||||||
private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
|
||||||
private final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用启动时自动验证所有实体类
|
|
||||||
*/
|
|
||||||
public void autoValidateOnStartup() {
|
|
||||||
log.info("开始自动验证实体类与数据库表映射...");
|
|
||||||
try {
|
|
||||||
Map<String, ValidationResult> results = validateAllEntitiesInProject();
|
|
||||||
printValidationSummary(results);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("实体类映射验证失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证项目中所有实体类 - 返回验证结果
|
|
||||||
*/
|
|
||||||
public Map<String, ValidationResult> validateAllEntitiesInProject() {
|
|
||||||
// 常见的实体类包路径,可以根据实际情况调整
|
|
||||||
String[] commonEntityPackages = {
|
|
||||||
"com.zhgd.xmgl.modules.**.entity"
|
|
||||||
};
|
|
||||||
|
|
||||||
Map<String, ValidationResult> allResults = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
for (String basePackage : commonEntityPackages) {
|
|
||||||
try {
|
|
||||||
Map<String, ValidationResult> packageResults = validatePackageEntities(basePackage);
|
|
||||||
allResults.putAll(packageResults);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("扫描包 {} 失败: {}", basePackage, e.getMessage());
|
|
||||||
allResults.put(basePackage, ValidationResult.error(basePackage, "扫描包失败: " + e.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证指定包下的所有实体类
|
|
||||||
*/
|
|
||||||
public Map<String, ValidationResult> validatePackageEntities(String basePackage) {
|
|
||||||
log.info("开始扫描包: {}", basePackage);
|
|
||||||
Map<String, ValidationResult> results = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
Set<Class<?>> entityClasses = findEntitiesInPackage(basePackage);
|
|
||||||
log.info("在包 {} 中找到 {} 个实体类", basePackage, entityClasses.size());
|
|
||||||
|
|
||||||
for (Class<?> entityClass : entityClasses) {
|
|
||||||
try {
|
|
||||||
ValidationResult result = validateSingleEntity(entityClass);
|
|
||||||
results.put(entityClass.getSimpleName(), result);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("验证实体类 {} 失败: {}", entityClass.getSimpleName(), e.getMessage());
|
|
||||||
results.put(entityClass.getSimpleName(),
|
|
||||||
ValidationResult.error(entityClass.getSimpleName(), e.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证单个实体类
|
|
||||||
*/
|
|
||||||
public ValidationResult validateSingleEntity(Class<?> entityClass) {
|
|
||||||
TableName tableAnnotation = entityClass.getAnnotation(TableName.class);
|
|
||||||
if (tableAnnotation == null) {
|
|
||||||
return ValidationResult.error(entityClass.getSimpleName(), "缺少 @TableName 注解");
|
|
||||||
}
|
|
||||||
|
|
||||||
String tableName = tableAnnotation.value();
|
|
||||||
if (tableName.isEmpty()) {
|
|
||||||
return ValidationResult.error(entityClass.getSimpleName(), "@TableName 注解值为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查表是否存在
|
|
||||||
if (!isTableExists(tableName)) {
|
|
||||||
return ValidationResult.error(entityClass.getSimpleName(),
|
|
||||||
"数据库表不存在: " + tableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> dbColumns = getTableColumns(tableName);
|
|
||||||
List<EntityFieldInfo> entityFields = getEntityFields(entityClass);
|
|
||||||
|
|
||||||
// 对比字段映射
|
|
||||||
List<String> missingInEntity = findMissingInEntity(dbColumns, entityFields);
|
|
||||||
List<String> missingInDb = findMissingInDatabase(entityFields, dbColumns);
|
|
||||||
List<String> typeMismatches = findTypeMismatches(entityClass, tableName, entityFields, dbColumns);
|
|
||||||
|
|
||||||
return ValidationResult.of(entityClass.getSimpleName(), tableName,
|
|
||||||
missingInEntity, missingInDb, typeMismatches);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扫描包下的所有实体类
|
|
||||||
*/
|
|
||||||
private Set<Class<?>> findEntitiesInPackage(String basePackage) {
|
|
||||||
Set<Class<?>> entities = new LinkedHashSet<>();
|
|
||||||
try {
|
|
||||||
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
|
||||||
ClassUtils.convertClassNameToResourcePath(basePackage) + "/**/*.class";
|
|
||||||
|
|
||||||
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
|
|
||||||
|
|
||||||
for (Resource resource : resources) {
|
|
||||||
if (resource.isReadable()) {
|
|
||||||
try {
|
|
||||||
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
|
|
||||||
String className = metadataReader.getClassMetadata().getClassName();
|
|
||||||
|
|
||||||
// 过滤内部类、匿名类等
|
|
||||||
if (className.contains("$")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> clazz = Class.forName(className);
|
|
||||||
if (clazz.isAnnotationPresent(TableName.class)) {
|
|
||||||
entities.add(clazz);
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
log.warn("无法加载类: {}", resource.getFilename());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("扫描包 {} 失败: {}", basePackage, e.getMessage());
|
|
||||||
}
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取数据库表的所有字段
|
|
||||||
*/
|
|
||||||
private List<String> getTableColumns(String tableName) {
|
|
||||||
return tableColumnsCache.computeIfAbsent(tableName, this::fetchTableColumnsFromDb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> fetchTableColumnsFromDb(String tableName) {
|
|
||||||
List<String> columns = new ArrayList<>();
|
|
||||||
try (Connection conn = dataSource.getConnection()) {
|
|
||||||
DatabaseMetaData metaData = conn.getMetaData();
|
|
||||||
|
|
||||||
// 尝试不同的表名匹配策略
|
|
||||||
ResultSet rs = metaData.getColumns(null, null, tableName, null);
|
|
||||||
if (!rs.next()) {
|
|
||||||
// 尝试小写
|
|
||||||
rs = metaData.getColumns(null, null, tableName.toLowerCase(), null);
|
|
||||||
}
|
|
||||||
if (!rs.next()) {
|
|
||||||
// 尝试大写
|
|
||||||
rs = metaData.getColumns(null, null, tableName.toUpperCase(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置游标
|
|
||||||
rs.beforeFirst();
|
|
||||||
|
|
||||||
while (rs.next()) {
|
|
||||||
String columnName = rs.getString("COLUMN_NAME");
|
|
||||||
// 清理列名中的特殊字符
|
|
||||||
columnName = cleanColumnName(columnName);
|
|
||||||
columns.add(columnName.toLowerCase());
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
log.error("获取表 {} 字段失败: {}", tableName, e.getMessage());
|
|
||||||
}
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查表是否存在
|
|
||||||
*/
|
|
||||||
private boolean isTableExists(String tableName) {
|
|
||||||
try (Connection conn = dataSource.getConnection()) {
|
|
||||||
DatabaseMetaData metaData = conn.getMetaData();
|
|
||||||
ResultSet rs = metaData.getTables(null, null, tableName, null);
|
|
||||||
if (rs.next()) return true;
|
|
||||||
|
|
||||||
// 尝试小写
|
|
||||||
rs = metaData.getTables(null, null, tableName.toLowerCase(), null);
|
|
||||||
if (rs.next()) return true;
|
|
||||||
|
|
||||||
// 尝试大写
|
|
||||||
rs = metaData.getTables(null, null, tableName.toUpperCase(), null);
|
|
||||||
return rs.next();
|
|
||||||
|
|
||||||
} catch (SQLException e) {
|
|
||||||
log.error("检查表是否存在失败: {}", e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取实体类的所有字段信息
|
|
||||||
*/
|
|
||||||
private List<EntityFieldInfo> getEntityFields(Class<?> entityClass) {
|
|
||||||
List<EntityFieldInfo> fields = new ArrayList<>();
|
|
||||||
Class<?> currentClass = entityClass;
|
|
||||||
|
|
||||||
// 遍历所有父类直到 Object
|
|
||||||
while (currentClass != null && currentClass != Object.class) {
|
|
||||||
Field[] declaredFields = currentClass.getDeclaredFields();
|
|
||||||
|
|
||||||
for (Field field : declaredFields) {
|
|
||||||
// 忽略静态字段、序列化字段等
|
|
||||||
if (shouldIgnoreField(field)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityFieldInfo fieldInfo = extractFieldInfo(field);
|
|
||||||
if (fieldInfo != null) {
|
|
||||||
fields.add(fieldInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentClass = currentClass.getSuperclass();
|
|
||||||
}
|
|
||||||
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否应该忽略该字段
|
|
||||||
*/
|
|
||||||
private boolean shouldIgnoreField(Field field) {
|
|
||||||
int modifiers = field.getModifiers();
|
|
||||||
|
|
||||||
// 忽略静态字段
|
|
||||||
if (Modifier.isStatic(modifiers)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 忽略序列化字段
|
|
||||||
if ("serialVersionUID".equals(field.getName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 忽略被 @TableField(exist = false) 标记的字段
|
|
||||||
TableField tableField = field.getAnnotation(TableField.class);
|
|
||||||
if (tableField != null && !tableField.exist()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提取字段信息
|
|
||||||
*/
|
|
||||||
private EntityFieldInfo extractFieldInfo(Field field) {
|
|
||||||
String fieldName = field.getName();
|
|
||||||
String columnName;
|
|
||||||
|
|
||||||
TableField tableField = field.getAnnotation(TableField.class);
|
|
||||||
if (tableField != null && !tableField.value().isEmpty()) {
|
|
||||||
columnName = tableField.value();
|
|
||||||
} else {
|
|
||||||
columnName = StrUtil.toUnderlineCase(fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理列名中的特殊字符
|
|
||||||
columnName = cleanColumnName(columnName);
|
|
||||||
|
|
||||||
return new EntityFieldInfo(
|
|
||||||
fieldName,
|
|
||||||
columnName.toLowerCase(),
|
|
||||||
field.getType().getSimpleName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 清理列名,处理各种特殊情况
|
|
||||||
*/
|
|
||||||
private String cleanColumnName(String columnName) {
|
|
||||||
if (columnName == null || columnName.isEmpty()) {
|
|
||||||
return columnName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 去掉反引号、单引号、双引号等
|
|
||||||
columnName = columnName.replace("`", "")
|
|
||||||
.replace("'", "")
|
|
||||||
.replace("\"", "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// 如果列名被方括号包围(SQL Server风格),去掉方括号
|
|
||||||
if (columnName.startsWith("[") && columnName.endsWith("]")) {
|
|
||||||
columnName = columnName.substring(1, columnName.length() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return columnName;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 找出数据库中有的但实体类缺少的字段
|
|
||||||
*/
|
|
||||||
private List<String> findMissingInEntity(List<String> dbColumns, List<EntityFieldInfo> entityFields) {
|
|
||||||
Set<String> entityColumnNames = entityFields.stream()
|
|
||||||
.map(EntityFieldInfo::getColumnName)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
return dbColumns.stream()
|
|
||||||
.filter(column -> !entityColumnNames.contains(column))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 找出实体类有的但数据库缺少的字段
|
|
||||||
*/
|
|
||||||
private List<String> findMissingInDatabase(List<EntityFieldInfo> entityFields, List<String> dbColumns) {
|
|
||||||
Set<String> dbColumnSet = new HashSet<>(dbColumns);
|
|
||||||
|
|
||||||
return entityFields.stream()
|
|
||||||
.map(EntityFieldInfo::getColumnName)
|
|
||||||
.filter(columnName -> !dbColumnSet.contains(columnName))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 找出类型不匹配的字段(基础实现)
|
|
||||||
*/
|
|
||||||
private List<String> findTypeMismatches(Class<?> entityClass, String tableName,
|
|
||||||
List<EntityFieldInfo> entityFields, List<String> dbColumns) {
|
|
||||||
// 这里可以实现更复杂的类型匹配检查
|
|
||||||
// 目前返回空列表,可以根据需要扩展
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打印验证摘要
|
|
||||||
*/
|
|
||||||
public void printValidationSummary(Map<String, ValidationResult> results) {
|
|
||||||
if (results.isEmpty()) {
|
|
||||||
log.info("🎉 没有找到需要验证的实体类!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long errorCount = results.values().stream()
|
|
||||||
.filter(ValidationResult::hasIssues)
|
|
||||||
.count();
|
|
||||||
long successCount = results.size() - errorCount;
|
|
||||||
|
|
||||||
log.info("验证完成!总计验证 {} 个实体类,成功: {},存在问题: {}",
|
|
||||||
results.size(), successCount, errorCount);
|
|
||||||
|
|
||||||
if (errorCount == 0) {
|
|
||||||
log.info("🎉 所有实体类映射验证通过!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.warn("⚠️ 发现 {} 个实体类存在映射问题:", errorCount);
|
|
||||||
|
|
||||||
results.forEach((className, result) -> {
|
|
||||||
if (result.hasIssues()) {
|
|
||||||
log.warn("\n==========================================");
|
|
||||||
log.warn("实体类: {} -> 表: {}", className, result.getTableName());
|
|
||||||
|
|
||||||
if (!result.getMissingInEntity().isEmpty()) {
|
|
||||||
log.warn("❌ 数据库有但实体缺少: {}", result.getMissingInEntity());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.getMissingInDb().isEmpty()) {
|
|
||||||
log.warn("❌ 实体有但数据库缺少: {}", result.getMissingInDb());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.getTypeMismatches().isEmpty()) {
|
|
||||||
log.warn("⚠️ 类型不匹配: {}", result.getTypeMismatches());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.getError() != null) {
|
|
||||||
log.warn("💥 验证错误: {}", result.getError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log.warn("\n==========================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成详细的验证报告
|
|
||||||
*/
|
|
||||||
public String generateDetailedReport() {
|
|
||||||
Map<String, ValidationResult> results = validateAllEntitiesInProject();
|
|
||||||
|
|
||||||
StringBuilder report = new StringBuilder();
|
|
||||||
report.append("实体类映射验证报告\n");
|
|
||||||
report.append("生成时间: ").append(new Date()).append("\n\n");
|
|
||||||
|
|
||||||
if (results.isEmpty()) {
|
|
||||||
report.append("✅ 没有找到需要验证的实体类\n");
|
|
||||||
return report.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
long errorCount = results.values().stream()
|
|
||||||
.filter(ValidationResult::hasIssues)
|
|
||||||
.count();
|
|
||||||
long successCount = results.size() - errorCount;
|
|
||||||
|
|
||||||
report.append("验证统计: 总计 ").append(results.size())
|
|
||||||
.append(" 个实体类,成功: ").append(successCount)
|
|
||||||
.append(",存在问题: ").append(errorCount).append("\n\n");
|
|
||||||
|
|
||||||
if (errorCount == 0) {
|
|
||||||
report.append("✅ 所有实体类映射正确\n");
|
|
||||||
return report.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
report.append("发现 ").append(errorCount).append(" 个问题:\n\n");
|
|
||||||
|
|
||||||
results.forEach((className, result) -> {
|
|
||||||
if (result.hasIssues()) {
|
|
||||||
report.append("实体类: ").append(className)
|
|
||||||
.append(" -> 表: ").append(result.getTableName()).append("\n");
|
|
||||||
|
|
||||||
if (!result.getMissingInEntity().isEmpty()) {
|
|
||||||
report.append(" 数据库有但实体缺少: ").append(result.getMissingInEntity()).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.getMissingInDb().isEmpty()) {
|
|
||||||
report.append(" 实体有但数据库缺少: ").append(result.getMissingInDb()).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.getTypeMismatches().isEmpty()) {
|
|
||||||
report.append(" 类型不匹配: ").append(result.getTypeMismatches()).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.getError() != null) {
|
|
||||||
report.append(" 错误: ").append(result.getError()).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
report.append("\n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return report.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取验证统计信息
|
|
||||||
*/
|
|
||||||
public Map<String, Object> getValidationStatistics() {
|
|
||||||
Map<String, ValidationResult> results = validateAllEntitiesInProject();
|
|
||||||
|
|
||||||
long total = results.size();
|
|
||||||
long errorCount = results.values().stream()
|
|
||||||
.filter(ValidationResult::hasIssues)
|
|
||||||
.count();
|
|
||||||
long successCount = total - errorCount;
|
|
||||||
|
|
||||||
Map<String, Object> stats = new HashMap<>();
|
|
||||||
stats.put("totalEntities", total);
|
|
||||||
stats.put("successCount", successCount);
|
|
||||||
stats.put("errorCount", errorCount);
|
|
||||||
stats.put("successRate", total > 0 ? (successCount * 100.0 / total) : 0);
|
|
||||||
stats.put("lastValidated", new Date());
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package com.zhgd.xmgl.modules.validator;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 实体字段信息类
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
class EntityFieldInfo {
|
|
||||||
private String fieldName;
|
|
||||||
private String columnName;
|
|
||||||
private String fieldType;
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
package com.zhgd.xmgl.modules.validator;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/xmgl/entity-validator")
|
|
||||||
public class EntityValidatorController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private BulkEntityValidator validator;
|
|
||||||
|
|
||||||
@GetMapping("/validate-all")
|
|
||||||
public Map<String, Object> validateAll() {
|
|
||||||
Map<String, ValidationResult> results = validator.validateAllEntitiesInProject();
|
|
||||||
|
|
||||||
long errorCount = results.values().stream()
|
|
||||||
.filter(ValidationResult::hasIssues)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
|
||||||
response.put("totalEntities", results.size());
|
|
||||||
response.put("errorCount", errorCount);
|
|
||||||
response.put("successCount", results.size() - errorCount);
|
|
||||||
response.put("results", results);
|
|
||||||
response.put("report", validator.generateDetailedReport());
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/validate-package")
|
|
||||||
public Map<String, Object> validatePackage(@RequestParam String packageName) {
|
|
||||||
Map<String, ValidationResult> results = validator.validatePackageEntities(packageName);
|
|
||||||
|
|
||||||
long errorCount = results.values().stream()
|
|
||||||
.filter(ValidationResult::hasIssues)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
|
||||||
response.put("package", packageName);
|
|
||||||
response.put("totalEntities", results.size());
|
|
||||||
response.put("errorCount", errorCount);
|
|
||||||
response.put("successCount", results.size() - errorCount);
|
|
||||||
response.put("results", results);
|
|
||||||
response.put("hasIssues", errorCount > 0);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/status")
|
|
||||||
public Map<String, Object> getValidationStatus() {
|
|
||||||
return validator.getValidationStatistics();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/report")
|
|
||||||
public String getDetailedReport() {
|
|
||||||
return validator.generateDetailedReport();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
package com.zhgd.xmgl.modules.validator;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证结果类
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
class ValidationResult {
|
|
||||||
private String className;
|
|
||||||
private String tableName;
|
|
||||||
private List<String> missingInEntity;
|
|
||||||
private List<String> missingInDb;
|
|
||||||
private List<String> typeMismatches;
|
|
||||||
private String error;
|
|
||||||
|
|
||||||
public static ValidationResult of(String className, String tableName,
|
|
||||||
List<String> missingInEntity, List<String> missingInDb,
|
|
||||||
List<String> typeMismatches) {
|
|
||||||
return new ValidationResult(className, tableName, missingInEntity, missingInDb, typeMismatches, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValidationResult error(String className, String error) {
|
|
||||||
return new ValidationResult(className, null, Collections.emptyList(),
|
|
||||||
Collections.emptyList(), Collections.emptyList(), error);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasIssues() {
|
|
||||||
return error != null ||
|
|
||||||
!missingInEntity.isEmpty() ||
|
|
||||||
!missingInDb.isEmpty() ||
|
|
||||||
!typeMismatches.isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package com.zhgd.xmgl.modules.validator;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class ValidatorConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public BulkEntityValidator bulkEntityValidator() {
|
|
||||||
return new BulkEntityValidator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -21,7 +21,7 @@ import com.zhgd.xmgl.util.AsyncTaskUtil;
|
|||||||
import com.zhgd.xmgl.util.HikVideoUtil;
|
import com.zhgd.xmgl.util.HikVideoUtil;
|
||||||
import com.zhgd.xmgl.util.PathUtil;
|
import com.zhgd.xmgl.util.PathUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.javacrumbs.shedlock.core.SchedulerLock;
|
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -68,7 +68,7 @@ public class Mcs8Task {
|
|||||||
/**
|
/**
|
||||||
* 定时从Mcs8获取执法记录仪文件
|
* 定时从Mcs8获取执法记录仪文件
|
||||||
*/
|
*/
|
||||||
@SchedulerLock(name = "getPoliceCameraItemFile", lockAtMostFor = 1000 * 60 * 5, lockAtLeastFor = 1000 * 60 * 2)
|
@SchedulerLock(name = "getPoliceCameraItemFile", lockAtMostFor = "PT300S", lockAtLeastFor = "PT120S")
|
||||||
@Scheduled(cron = "0 */5 * * * ?")
|
@Scheduled(cron = "0 */5 * * * ?")
|
||||||
@RequestMapping("getPoliceCameraItemFile")
|
@RequestMapping("getPoliceCameraItemFile")
|
||||||
public void getPoliceCameraItemFile() {
|
public void getPoliceCameraItemFile() {
|
||||||
@ -91,7 +91,7 @@ public class Mcs8Task {
|
|||||||
/**
|
/**
|
||||||
* 更新执法记录仪的封面
|
* 更新执法记录仪的封面
|
||||||
*/
|
*/
|
||||||
@SchedulerLock(name = "updatePoliceCameraItemCoverUrl", lockAtMostFor = 1000 * 60 * 10, lockAtLeastFor = 1000 * 60 * 5)
|
@SchedulerLock(name = "updatePoliceCameraItemCoverUrl", lockAtMostFor = "PT600S", lockAtLeastFor = "PT300S")
|
||||||
@Scheduled(cron = "* */30 * * * ?")
|
@Scheduled(cron = "* */30 * * * ?")
|
||||||
@RequestMapping("updatePoliceCameraItemCoverUrl")
|
@RequestMapping("updatePoliceCameraItemCoverUrl")
|
||||||
public void updatePoliceCameraItemCoverUrl() {
|
public void updatePoliceCameraItemCoverUrl() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user