mirror of
https://github.com/vran-dev/databasir.git
synced 2025-08-08 18:25:17 +08:00
feat: support version diff (#52)
* feat: implementation diff processor * fix: checkstyle * feat: ignore sync if without change * fix: checkstyle * feat:add databasir diff api * feat:update frontend resources
This commit is contained in:
@@ -32,6 +32,7 @@ public enum DomainErrors implements DatabasirErrors {
|
||||
MUST_NOT_MODIFY_SYSTEM_DEFAULT_DATABASE_TYPE("A_10017", "禁止修改系统默认数据库类型"),
|
||||
DOWNLOAD_DRIVER_ERROR("A_10018", "驱动下载失败"),
|
||||
INVALID_DATABASE_TYPE_URL_PATTERN("A_10019", "不合法的 url pattern"),
|
||||
DOCUMENT_VERSION_IS_INVALID("A_10020", "文档版本不合法"),
|
||||
;
|
||||
|
||||
private final String errCode;
|
||||
|
@@ -1,9 +0,0 @@
|
||||
package com.databasir.core.domain.document.converter;
|
||||
|
||||
public interface BaseConverter {
|
||||
|
||||
@NullToEmpty
|
||||
default String nullToEmpty(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package com.databasir.core.domain.document.converter;
|
||||
|
||||
import com.databasir.core.infrastructure.converter.JsonConverter;
|
||||
import com.databasir.core.meta.data.ColumnMeta;
|
||||
import com.databasir.core.meta.data.DatabaseMeta;
|
||||
import com.databasir.core.meta.data.IndexMeta;
|
||||
import com.databasir.core.meta.data.TableMeta;
|
||||
import com.databasir.dao.tables.pojos.*;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper(componentModel = "spring", uses = JsonConverter.class)
|
||||
public interface DatabaseMetaConverter {
|
||||
|
||||
default DatabaseMeta of(DatabaseDocumentPojo database,
|
||||
List<TableDocumentPojo> tables,
|
||||
List<TableColumnDocumentPojo> columns,
|
||||
List<TableIndexDocumentPojo> indexes,
|
||||
List<TableTriggerDocumentPojo> triggers,
|
||||
List<TableForeignKeyDocumentPojo> foreignKeys) {
|
||||
var columnMap = groupBy(columns, TableColumnDocumentPojo::getTableDocumentId);
|
||||
var indexMap = groupBy(indexes, TableIndexDocumentPojo::getTableDocumentId);
|
||||
var triggerMap = groupBy(triggers, TableTriggerDocumentPojo::getTableDocumentId);
|
||||
var fkMap = groupBy(foreignKeys, TableForeignKeyDocumentPojo::getTableDocumentId);
|
||||
return of(database, tables, columnMap, indexMap, triggerMap, fkMap);
|
||||
}
|
||||
|
||||
default DatabaseMeta of(DatabaseDocumentPojo database,
|
||||
List<TableDocumentPojo> tables,
|
||||
Map<Integer, List<TableColumnDocumentPojo>> columnGroupByTableId,
|
||||
Map<Integer, List<TableIndexDocumentPojo>> indexGroupByTableId,
|
||||
Map<Integer, List<TableTriggerDocumentPojo>> triggerGroupByTableId,
|
||||
Map<Integer, List<TableForeignKeyDocumentPojo>> fkGroupByTableId) {
|
||||
List<TableMeta> tableMetas = tables.stream()
|
||||
.map(table -> {
|
||||
Integer id = table.getId();
|
||||
var columns = columnGroupByTableId.getOrDefault(id, Collections.emptyList());
|
||||
var indexes = indexGroupByTableId.getOrDefault(id, Collections.emptyList());
|
||||
var triggers = triggerGroupByTableId.getOrDefault(id, Collections.emptyList());
|
||||
var foreignKeys = fkGroupByTableId.getOrDefault(id, Collections.emptyList());
|
||||
return of(table, columns, indexes, triggers, foreignKeys);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return of(database, tableMetas);
|
||||
}
|
||||
|
||||
DatabaseMeta of(DatabaseDocumentPojo database, List<TableMeta> tables);
|
||||
|
||||
TableMeta of(TableDocumentPojo table,
|
||||
List<TableColumnDocumentPojo> columns,
|
||||
List<TableIndexDocumentPojo> indexes,
|
||||
List<TableTriggerDocumentPojo> triggers,
|
||||
List<TableForeignKeyDocumentPojo> foreignKeys);
|
||||
|
||||
ColumnMeta of(TableColumnDocumentPojo pojo);
|
||||
|
||||
@Mapping(target = "isUniqueKey", source = "pojo.isUnique")
|
||||
@Mapping(target = "columnNames", source = "pojo.columnNameArray")
|
||||
IndexMeta of(TableIndexDocumentPojo pojo);
|
||||
|
||||
default <R> Map<Integer, List<R>> groupBy(List<R> content, Function<R, Integer> idMapping) {
|
||||
return content.stream()
|
||||
.collect(Collectors.groupingBy(idMapping));
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper(componentModel = "spring", uses = JsonConverter.class, unmappedTargetPolicy = ReportingPolicy.WARN)
|
||||
public interface DocumentPojoConverter extends BaseConverter {
|
||||
public interface DocumentPojoConverter {
|
||||
|
||||
@Mapping(target = "databaseName", source = "meta.databaseName")
|
||||
@Mapping(target = "schemaName", source = "meta.schemaName")
|
||||
@@ -23,7 +23,6 @@ public interface DocumentPojoConverter extends BaseConverter {
|
||||
com.databasir.core.meta.data.DatabaseMeta meta,
|
||||
Long version);
|
||||
|
||||
@Mapping(target = "comment", qualifiedBy = NullToEmpty.class)
|
||||
TableDocumentPojo toTablePojo(Integer databaseDocumentId,
|
||||
com.databasir.core.meta.data.TableMeta meta);
|
||||
|
||||
@@ -35,7 +34,6 @@ public interface DocumentPojoConverter extends BaseConverter {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Mapping(target = "comment", qualifiedBy = NullToEmpty.class)
|
||||
TableColumnDocumentPojo toColumnPojo(Integer databaseDocumentId,
|
||||
Integer tableDocumentId,
|
||||
ColumnMeta meta);
|
||||
|
@@ -1,14 +0,0 @@
|
||||
package com.databasir.core.domain.document.converter;
|
||||
|
||||
import org.mapstruct.Qualifier;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Qualifier
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface NullToEmpty {
|
||||
}
|
@@ -2,7 +2,11 @@ package com.databasir.core.domain.document.service;
|
||||
|
||||
import com.databasir.core.Databasir;
|
||||
import com.databasir.core.DatabasirConfig;
|
||||
import com.databasir.core.diff.Diffs;
|
||||
import com.databasir.core.diff.data.DiffType;
|
||||
import com.databasir.core.diff.data.RootDiff;
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.document.converter.DatabaseMetaConverter;
|
||||
import com.databasir.core.domain.document.converter.DocumentPojoConverter;
|
||||
import com.databasir.core.domain.document.converter.DocumentResponseConverter;
|
||||
import com.databasir.core.domain.document.converter.DocumentSimpleResponseConverter;
|
||||
@@ -74,6 +78,8 @@ public class DocumentService {
|
||||
|
||||
private final DocumentSimpleResponseConverter documentSimpleResponseConverter;
|
||||
|
||||
private final DatabaseMetaConverter databaseMetaConverter;
|
||||
|
||||
private final JsonConverter jsonConverter;
|
||||
|
||||
private final List<DocumentFileGenerator> documentFileGenerators;
|
||||
@@ -82,19 +88,37 @@ public class DocumentService {
|
||||
public void syncByProjectId(Integer projectId) {
|
||||
projectDao.selectOptionalById(projectId)
|
||||
.orElseThrow(DomainErrors.PROJECT_NOT_FOUND::exception);
|
||||
DatabaseMeta meta = retrieveDatabaseMeta(projectId);
|
||||
Optional<DatabaseDocumentPojo> latestDocumentOpt = databaseDocumentDao.selectNotArchivedByProjectId(projectId);
|
||||
if (latestDocumentOpt.isPresent()) {
|
||||
DatabaseDocumentPojo latestDocument = latestDocumentOpt.get();
|
||||
Integer previousDocumentId = latestDocument.getId();
|
||||
DatabaseMeta current = retrieveDatabaseMeta(projectId);
|
||||
Optional<DatabaseDocumentPojo> originalOption = databaseDocumentDao.selectNotArchivedByProjectId(projectId);
|
||||
if (originalOption.isPresent()) {
|
||||
DatabaseDocumentPojo original = originalOption.get();
|
||||
DatabaseMeta originalMeta = retrieveOriginalDatabaseMeta(original);
|
||||
RootDiff diff = Diffs.diff(originalMeta, current);
|
||||
if (diff.getDiffType() == DiffType.NONE) {
|
||||
log.info("ignore project {} {} sync data, because without change",
|
||||
projectId,
|
||||
original.getDatabaseName());
|
||||
return;
|
||||
}
|
||||
Integer previousDocumentId = original.getId();
|
||||
// archive old version
|
||||
databaseDocumentDao.updateIsArchiveById(previousDocumentId, true);
|
||||
saveNewDocument(meta, latestDocument.getVersion() + 1, latestDocument.getProjectId());
|
||||
saveNewDocument(current, original.getVersion() + 1, original.getProjectId());
|
||||
} else {
|
||||
saveNewDocument(meta, 1L, projectId);
|
||||
saveNewDocument(current, 1L, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
private DatabaseMeta retrieveOriginalDatabaseMeta(DatabaseDocumentPojo original) {
|
||||
Integer docId = original.getId();
|
||||
List<TableDocumentPojo> tables = tableDocumentDao.selectByDatabaseDocumentId(docId);
|
||||
List<TableColumnDocumentPojo> columns = tableColumnDocumentDao.selectByDatabaseDocumentId(docId);
|
||||
List<TableIndexDocumentPojo> indexes = tableIndexDocumentDao.selectByDatabaseMetaId(docId);
|
||||
List<TableTriggerDocumentPojo> triggers = tableTriggerDocumentDao.selectByDatabaseDocumentId(docId);
|
||||
List<TableForeignKeyDocumentPojo> fks = tableForeignKeyDocumentDao.selectByDatabaseDocumentId(docId);
|
||||
return databaseMetaConverter.of(original, tables, columns, indexes, triggers, fks);
|
||||
}
|
||||
|
||||
private DatabaseMeta retrieveDatabaseMeta(Integer projectId) {
|
||||
ProjectSyncRulePojo rule = projectSyncRuleDao.selectByProjectId(projectId);
|
||||
DataSourcePojo dataSource = dataSourceDao.selectByProjectId(projectId);
|
||||
@@ -325,4 +349,20 @@ public class DocumentService {
|
||||
.ifPresent(generator -> generator.generate(context, out));
|
||||
});
|
||||
}
|
||||
|
||||
public RootDiff diff(Integer projectId, Long originalVersion, Long currentVersion) {
|
||||
var original = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, originalVersion)
|
||||
.orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception);
|
||||
DatabaseDocumentPojo current;
|
||||
if (currentVersion == null) {
|
||||
current = databaseDocumentDao.selectNotArchivedByProjectId(projectId)
|
||||
.orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception);
|
||||
} else {
|
||||
current = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, currentVersion)
|
||||
.orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception);
|
||||
}
|
||||
DatabaseMeta currMeta = retrieveOriginalDatabaseMeta(current);
|
||||
DatabaseMeta originalMeta = retrieveOriginalDatabaseMeta(original);
|
||||
return Diffs.diff(originalMeta, currMeta);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user