mirror of
				https://github.com/vran-dev/databasir.git
				synced 2025-11-04 16:26:10 +08:00 
			
		
		
		
	refactor: redesign file export api
This commit is contained in:
		@@ -1,11 +1,11 @@
 | 
				
			|||||||
package com.databasir.api;
 | 
					package com.databasir.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.databasir.common.JsonData;
 | 
					import com.databasir.common.JsonData;
 | 
				
			||||||
import com.databasir.common.SystemException;
 | 
					 | 
				
			||||||
import com.databasir.core.domain.document.data.DatabaseDocumentResponse;
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentResponse;
 | 
				
			||||||
import com.databasir.core.domain.document.data.DatabaseDocumentSimpleResponse;
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentSimpleResponse;
 | 
				
			||||||
import com.databasir.core.domain.document.data.DatabaseDocumentVersionResponse;
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentVersionResponse;
 | 
				
			||||||
import com.databasir.core.domain.document.data.TableDocumentResponse;
 | 
					import com.databasir.core.domain.document.data.TableDocumentResponse;
 | 
				
			||||||
 | 
					import com.databasir.core.domain.document.generator.DocumentFileType;
 | 
				
			||||||
import com.databasir.core.domain.document.service.DocumentService;
 | 
					import com.databasir.core.domain.document.service.DocumentService;
 | 
				
			||||||
import com.databasir.core.domain.log.annotation.Operation;
 | 
					import com.databasir.core.domain.log.annotation.Operation;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
@@ -20,13 +20,8 @@ import org.springframework.validation.annotation.Validated;
 | 
				
			|||||||
import org.springframework.web.bind.annotation.*;
 | 
					import org.springframework.web.bind.annotation.*;
 | 
				
			||||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
 | 
					import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.IOException;
 | 
					 | 
				
			||||||
import java.nio.charset.StandardCharsets;
 | 
					import java.nio.charset.StandardCharsets;
 | 
				
			||||||
import java.nio.file.Files;
 | 
					 | 
				
			||||||
import java.nio.file.Path;
 | 
					 | 
				
			||||||
import java.nio.file.Paths;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.UUID;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.springframework.data.domain.Sort.Direction.DESC;
 | 
					import static org.springframework.data.domain.Sort.Direction.DESC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,24 +57,18 @@ public class DocumentController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @GetMapping(Routes.Document.EXPORT)
 | 
					    @GetMapping(Routes.Document.EXPORT)
 | 
				
			||||||
    public ResponseEntity<StreamingResponseBody> getDocumentFiles(@PathVariable Integer projectId,
 | 
					    public ResponseEntity<StreamingResponseBody> getDocumentFiles(@PathVariable Integer projectId,
 | 
				
			||||||
                                                                  @RequestParam(required = false) Long version) {
 | 
					                                                                  @RequestParam(required = false)
 | 
				
			||||||
        String data = documentService.toMarkdown(projectId, version).get();
 | 
					                                                                          Long version,
 | 
				
			||||||
        try {
 | 
					                                                                  @RequestParam DocumentFileType fileType) {
 | 
				
			||||||
            Path path = Files.writeString(Paths.get(UUID.randomUUID().toString() + ".md"), data,
 | 
					        HttpHeaders headers = new HttpHeaders();
 | 
				
			||||||
                    StandardCharsets.UTF_8);
 | 
					        String fileName = "project[" + projectId + "]." + fileType.getFileExtension();
 | 
				
			||||||
            HttpHeaders headers = new HttpHeaders();
 | 
					        headers.setContentDisposition(ContentDisposition.attachment()
 | 
				
			||||||
            headers.setContentDisposition(ContentDisposition.attachment()
 | 
					                .filename("demo.md", StandardCharsets.UTF_8)
 | 
				
			||||||
                    .filename("demo.md", StandardCharsets.UTF_8)
 | 
					                .build());
 | 
				
			||||||
                    .build());
 | 
					        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
 | 
				
			||||||
            byte[] bytes = Files.readAllBytes(path);
 | 
					        return ResponseEntity.ok()
 | 
				
			||||||
            Files.deleteIfExists(path);
 | 
					                .headers(headers)
 | 
				
			||||||
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
 | 
					                .body(out -> documentService.export(projectId, version, fileType, out));
 | 
				
			||||||
            return ResponseEntity.ok()
 | 
					 | 
				
			||||||
                    .headers(headers)
 | 
					 | 
				
			||||||
                    .body(out -> out.write(bytes));
 | 
					 | 
				
			||||||
        } catch (IOException e) {
 | 
					 | 
				
			||||||
            throw new SystemException("System error");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @GetMapping(Routes.Document.GET_SIMPLE_ONE)
 | 
					    @GetMapping(Routes.Document.GET_SIMPLE_ONE)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ subprojects {
 | 
				
			|||||||
        postgresqlConnectorVersion = '42.3.1'
 | 
					        postgresqlConnectorVersion = '42.3.1'
 | 
				
			||||||
        hikariVersion = '5.0.0'
 | 
					        hikariVersion = '5.0.0'
 | 
				
			||||||
        jacksonVersion = '2.13.1'
 | 
					        jacksonVersion = '2.13.1'
 | 
				
			||||||
 | 
					        easyExcelVersion = '3.0.5'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dependencies {
 | 
					    dependencies {
 | 
				
			||||||
@@ -47,6 +48,8 @@ subprojects {
 | 
				
			|||||||
        annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
 | 
					        annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        implementation "org.slf4j:slf4j-api:${slf4jVersion}"
 | 
					        implementation "org.slf4j:slf4j-api:${slf4jVersion}"
 | 
				
			||||||
 | 
					        implementation "com.alibaba:easyexcel:${easyExcelVersion}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test {
 | 
					    test {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,7 @@ dependencies {
 | 
				
			|||||||
    implementation 'com.auth0:java-jwt:3.18.3'
 | 
					    implementation 'com.auth0:java-jwt:3.18.3'
 | 
				
			||||||
    implementation 'org.commonmark:commonmark:0.18.1'
 | 
					    implementation 'org.commonmark:commonmark:0.18.1'
 | 
				
			||||||
    implementation 'org.freemarker:freemarker:2.3.31'
 | 
					    implementation 'org.freemarker:freemarker:2.3.31'
 | 
				
			||||||
 | 
					    implementation 'com.alibaba:easyexcel'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
 | 
					    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
 | 
				
			||||||
    implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
 | 
					    implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					package com.databasir.core.domain.document.generator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentResponse;
 | 
				
			||||||
 | 
					import lombok.Builder;
 | 
				
			||||||
 | 
					import lombok.Getter;
 | 
				
			||||||
 | 
					import lombok.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface DocumentFileGenerator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean support(DocumentFileType type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void generate(DocumentFileGenerateContext context, OutputStream outputStream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Getter
 | 
				
			||||||
 | 
					    @Builder
 | 
				
			||||||
 | 
					    class DocumentFileGenerateContext {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @NonNull
 | 
				
			||||||
 | 
					        private DocumentFileType documentFileType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @NonNull
 | 
				
			||||||
 | 
					        private DatabaseDocumentResponse databaseDocument;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					package com.databasir.core.domain.document.generator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.Getter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@AllArgsConstructor
 | 
				
			||||||
 | 
					@Getter
 | 
				
			||||||
 | 
					public enum DocumentFileType {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MARKDOWN("md"), EXCEL("xlsx");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String fileExtension;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package com.databasir.core.domain.document.generator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.stereotype.Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component
 | 
				
			||||||
 | 
					public class ExcelDocumentFileGenerator implements DocumentFileGenerator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean support(DocumentFileType type) {
 | 
				
			||||||
 | 
					        return type == DocumentFileType.EXCEL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void generate(DocumentFileGenerateContext context, OutputStream outputStream) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void buildTableWithSheet() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					package com.databasir.core.domain.document.generator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.databasir.common.SystemException;
 | 
				
			||||||
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentResponse;
 | 
				
			||||||
 | 
					import com.databasir.core.domain.document.data.TableDocumentResponse;
 | 
				
			||||||
 | 
					import com.databasir.core.render.markdown.MarkdownBuilder;
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
 | 
					import org.springframework.stereotype.Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets;
 | 
				
			||||||
 | 
					import java.nio.file.Files;
 | 
				
			||||||
 | 
					import java.nio.file.Path;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component
 | 
				
			||||||
 | 
					@Slf4j
 | 
				
			||||||
 | 
					public class MarkdownDocumentFileGenerator implements DocumentFileGenerator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean support(DocumentFileType type) {
 | 
				
			||||||
 | 
					        return type == DocumentFileType.MARKDOWN;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void generate(DocumentFileGenerateContext context, OutputStream outputStream) {
 | 
				
			||||||
 | 
					        String fileName = context.getDatabaseDocument().getDatabaseName() + "-" + UUID.randomUUID().toString();
 | 
				
			||||||
 | 
					        String data = markdownData(context);
 | 
				
			||||||
 | 
					        Path tempFile = null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            tempFile = Files.createTempFile(fileName, ".md");
 | 
				
			||||||
 | 
					            Path path = Files.writeString(tempFile, data, StandardCharsets.UTF_8);
 | 
				
			||||||
 | 
					            byte[] bytes = Files.readAllBytes(path);
 | 
				
			||||||
 | 
					            outputStream.write(bytes);
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            if (tempFile != null) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    Files.deleteIfExists(tempFile);
 | 
				
			||||||
 | 
					                } catch (IOException ex) {
 | 
				
			||||||
 | 
					                    log.warn("delete temp file error", ex);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            throw new SystemException("System error");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String markdownData(DocumentFileGenerateContext context) {
 | 
				
			||||||
 | 
					        DatabaseDocumentResponse doc = context.getDatabaseDocument();
 | 
				
			||||||
 | 
					        MarkdownBuilder builder = MarkdownBuilder.builder();
 | 
				
			||||||
 | 
					        builder.primaryTitle(doc.getDatabaseName());
 | 
				
			||||||
 | 
					        // overview
 | 
				
			||||||
 | 
					        overviewBuild(builder, doc);
 | 
				
			||||||
 | 
					        // tables
 | 
				
			||||||
 | 
					        doc.getTables().forEach(table -> tableBuild(builder, table));
 | 
				
			||||||
 | 
					        return builder.build();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void overviewBuild(MarkdownBuilder builder, DatabaseDocumentResponse doc) {
 | 
				
			||||||
 | 
					        builder.secondTitle("overview");
 | 
				
			||||||
 | 
					        List<List<String>> overviewContent = new ArrayList<>();
 | 
				
			||||||
 | 
					        for (int i = 0; i < doc.getTables().size(); i++) {
 | 
				
			||||||
 | 
					            TableDocumentResponse table = doc.getTables().get(i);
 | 
				
			||||||
 | 
					            overviewContent.add(List.of((i + 1) + "", table.getName(), table.getType(),
 | 
				
			||||||
 | 
					                    table.getComment()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        builder.table(List.of("", "表名", "类型", "备注"), overviewContent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void tableBuild(MarkdownBuilder builder, TableDocumentResponse table) {
 | 
				
			||||||
 | 
					        builder.secondTitle(table.getName());
 | 
				
			||||||
 | 
					        columnBuild(builder, table);
 | 
				
			||||||
 | 
					        indexBuild(builder, table);
 | 
				
			||||||
 | 
					        foreignKeyBuild(builder, table);
 | 
				
			||||||
 | 
					        triggerBuild(builder, table);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void columnBuild(MarkdownBuilder builder, TableDocumentResponse table) {
 | 
				
			||||||
 | 
					        Function<TableDocumentResponse.ColumnDocumentResponse, String>
 | 
				
			||||||
 | 
					                columnDefaultValueMapping = column -> {
 | 
				
			||||||
 | 
					            if (Objects.equals(column.getNullable(), "YES")) {
 | 
				
			||||||
 | 
					                return Objects.requireNonNullElse(column.getDefaultValue(), "null");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return Objects.requireNonNullElse(column.getDefaultValue(), "");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        builder.thirdTitle("Columns");
 | 
				
			||||||
 | 
					        List<List<String>> columnContent = new ArrayList<>();
 | 
				
			||||||
 | 
					        for (int i = 0; i < table.getColumns().size(); i++) {
 | 
				
			||||||
 | 
					            var column = table.getColumns().get(i);
 | 
				
			||||||
 | 
					            String type;
 | 
				
			||||||
 | 
					            if (column.getDecimalDigits() == null || column.getDecimalDigits() == 0) {
 | 
				
			||||||
 | 
					                type = column.getType() + "(" + column.getSize() + ")";
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                type = column.getType() + "(" + column.getSize() + "," + column.getDecimalDigits() + ")";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            columnContent.add(List.of((i + 1) + "",
 | 
				
			||||||
 | 
					                    column.getName(),
 | 
				
			||||||
 | 
					                    type,
 | 
				
			||||||
 | 
					                    column.getIsPrimaryKey() ? "YES" : "NO",
 | 
				
			||||||
 | 
					                    column.getNullable(),
 | 
				
			||||||
 | 
					                    column.getAutoIncrement(),
 | 
				
			||||||
 | 
					                    columnDefaultValueMapping.apply(column),
 | 
				
			||||||
 | 
					                    column.getComment()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        builder.table(List.of("", "名称", "类型", "是否为主键", "可为空", "自增", "默认值", "备注"),
 | 
				
			||||||
 | 
					                columnContent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void indexBuild(MarkdownBuilder builder, TableDocumentResponse table) {
 | 
				
			||||||
 | 
					        builder.thirdTitle("Indexes");
 | 
				
			||||||
 | 
					        List<List<String>> indexContent = new ArrayList<>();
 | 
				
			||||||
 | 
					        for (int i = 0; i < table.getIndexes().size(); i++) {
 | 
				
			||||||
 | 
					            var index = table.getIndexes().get(i);
 | 
				
			||||||
 | 
					            String columnNames = String.join(", ", index.getColumnNames());
 | 
				
			||||||
 | 
					            String isUnique = index.getIsUnique() ? "YES" : "NO";
 | 
				
			||||||
 | 
					            indexContent.add(List.of((i + 1) + "", index.getName(), isUnique, columnNames));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        builder.table(List.of("", "名称", "是否唯一", "关联列"), indexContent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void foreignKeyBuild(MarkdownBuilder builder, TableDocumentResponse table) {
 | 
				
			||||||
 | 
					        if (!table.getForeignKeys().isEmpty()) {
 | 
				
			||||||
 | 
					            List<List<String>> foreignKeys = new ArrayList<>();
 | 
				
			||||||
 | 
					            builder.thirdTitle("Foreign Keys");
 | 
				
			||||||
 | 
					            for (int i = 0; i < table.getForeignKeys().size(); i++) {
 | 
				
			||||||
 | 
					                TableDocumentResponse.ForeignKeyDocumentResponse fk = table.getForeignKeys().get(i);
 | 
				
			||||||
 | 
					                List<String> item = List.of(
 | 
				
			||||||
 | 
					                        (i + 1) + "",
 | 
				
			||||||
 | 
					                        fk.getFkName(), fk.getFkColumnName(),
 | 
				
			||||||
 | 
					                        fk.getPkName(), fk.getPkTableName(), fk.getPkColumnName(),
 | 
				
			||||||
 | 
					                        fk.getUpdateRule(), fk.getDeleteRule()
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                foreignKeys.add(item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            builder.table(
 | 
				
			||||||
 | 
					                    List.of("", "FK Name", "FK Column", "PK Name", "PK Table", "PK Column",
 | 
				
			||||||
 | 
					                            "Update Rule", "Delete Rule"),
 | 
				
			||||||
 | 
					                    foreignKeys
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void triggerBuild(MarkdownBuilder builder, TableDocumentResponse table) {
 | 
				
			||||||
 | 
					        if (!table.getTriggers().isEmpty()) {
 | 
				
			||||||
 | 
					            List<List<String>> triggerContent = new ArrayList<>();
 | 
				
			||||||
 | 
					            for (int i = 0; i < table.getTriggers().size(); i++) {
 | 
				
			||||||
 | 
					                var trigger = table.getTriggers().get(i);
 | 
				
			||||||
 | 
					                triggerContent.add(List.of((i + 1) + "",
 | 
				
			||||||
 | 
					                        trigger.getName(),
 | 
				
			||||||
 | 
					                        trigger.getTiming(),
 | 
				
			||||||
 | 
					                        trigger.getManipulation(),
 | 
				
			||||||
 | 
					                        trigger.getStatement()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            builder.thirdTitle("Triggers");
 | 
				
			||||||
 | 
					            builder.table(List.of("", "名称", "timing", "manipulation", "statement"), triggerContent);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,10 +10,11 @@ import com.databasir.core.domain.document.data.DatabaseDocumentResponse;
 | 
				
			|||||||
import com.databasir.core.domain.document.data.DatabaseDocumentSimpleResponse;
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentSimpleResponse;
 | 
				
			||||||
import com.databasir.core.domain.document.data.DatabaseDocumentVersionResponse;
 | 
					import com.databasir.core.domain.document.data.DatabaseDocumentVersionResponse;
 | 
				
			||||||
import com.databasir.core.domain.document.data.TableDocumentResponse;
 | 
					import com.databasir.core.domain.document.data.TableDocumentResponse;
 | 
				
			||||||
 | 
					import com.databasir.core.domain.document.generator.DocumentFileGenerator;
 | 
				
			||||||
 | 
					import com.databasir.core.domain.document.generator.DocumentFileType;
 | 
				
			||||||
import com.databasir.core.infrastructure.connection.DatabaseConnectionService;
 | 
					import com.databasir.core.infrastructure.connection.DatabaseConnectionService;
 | 
				
			||||||
import com.databasir.core.infrastructure.converter.JsonConverter;
 | 
					import com.databasir.core.infrastructure.converter.JsonConverter;
 | 
				
			||||||
import com.databasir.core.meta.data.DatabaseMeta;
 | 
					import com.databasir.core.meta.data.DatabaseMeta;
 | 
				
			||||||
import com.databasir.core.render.markdown.MarkdownBuilder;
 | 
					 | 
				
			||||||
import com.databasir.dao.impl.*;
 | 
					import com.databasir.dao.impl.*;
 | 
				
			||||||
import com.databasir.dao.tables.pojos.*;
 | 
					import com.databasir.dao.tables.pojos.*;
 | 
				
			||||||
import com.databasir.dao.value.DocumentDiscussionCountPojo;
 | 
					import com.databasir.dao.value.DocumentDiscussionCountPojo;
 | 
				
			||||||
@@ -26,9 +27,12 @@ import org.springframework.stereotype.Service;
 | 
				
			|||||||
import org.springframework.transaction.annotation.Transactional;
 | 
					import org.springframework.transaction.annotation.Transactional;
 | 
				
			||||||
import org.springframework.util.CollectionUtils;
 | 
					import org.springframework.util.CollectionUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
import java.sql.Connection;
 | 
					import java.sql.Connection;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.function.Function;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service
 | 
					@Service
 | 
				
			||||||
@@ -72,6 +76,8 @@ public class DocumentService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private final JsonConverter jsonConverter;
 | 
					    private final JsonConverter jsonConverter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final List<DocumentFileGenerator> documentFileGenerators;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Transactional
 | 
					    @Transactional
 | 
				
			||||||
    public void syncByProjectId(Integer projectId) {
 | 
					    public void syncByProjectId(Integer projectId) {
 | 
				
			||||||
        projectDao.selectOptionalById(projectId)
 | 
					        projectDao.selectOptionalById(projectId)
 | 
				
			||||||
@@ -303,82 +309,20 @@ public class DocumentService {
 | 
				
			|||||||
                .collect(Collectors.toList());
 | 
					                .collect(Collectors.toList());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Optional<String> toMarkdown(Integer projectId, Long version) {
 | 
					    public void export(Integer projectId,
 | 
				
			||||||
        return getOneByProjectId(projectId, version)
 | 
					                       Long version,
 | 
				
			||||||
                .map(doc -> {
 | 
					                       DocumentFileType type,
 | 
				
			||||||
                    MarkdownBuilder builder = MarkdownBuilder.builder();
 | 
					                       OutputStream out) {
 | 
				
			||||||
                    builder.primaryTitle(doc.getDatabaseName());
 | 
					        getOneByProjectId(projectId, version)
 | 
				
			||||||
                    // overview
 | 
					                .ifPresent(doc -> {
 | 
				
			||||||
                    builder.secondTitle("overview");
 | 
					                    var context = DocumentFileGenerator.DocumentFileGenerateContext.builder()
 | 
				
			||||||
                    List<List<String>> overviewContent = new ArrayList<>();
 | 
					                            .documentFileType(type)
 | 
				
			||||||
                    for (int i = 0; i < doc.getTables().size(); i++) {
 | 
					                            .databaseDocument(doc)
 | 
				
			||||||
                        TableDocumentResponse table = doc.getTables().get(i);
 | 
					                            .build();
 | 
				
			||||||
                        overviewContent.add(List.of((i + 1) + "", table.getName(), table.getType(),
 | 
					                    documentFileGenerators.stream()
 | 
				
			||||||
                                table.getComment()));
 | 
					                            .filter(g -> g.support(type))
 | 
				
			||||||
                    }
 | 
					                            .findFirst()
 | 
				
			||||||
                    builder.table(List.of("", "表名", "类型", "备注"), overviewContent);
 | 
					                            .ifPresent(generator -> generator.generate(context, out));
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Function<TableDocumentResponse.ColumnDocumentResponse, String>
 | 
					 | 
				
			||||||
                            columnDefaultValueMapping = column -> {
 | 
					 | 
				
			||||||
                        if (Objects.equals(column.getNullable(), "YES")) {
 | 
					 | 
				
			||||||
                            return Objects.requireNonNullElse(column.getDefaultValue(), "null");
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            return Objects.requireNonNullElse(column.getDefaultValue(), "");
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                    // tables
 | 
					 | 
				
			||||||
                    doc.getTables().forEach(table -> {
 | 
					 | 
				
			||||||
                        builder.secondTitle(table.getName());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // columns
 | 
					 | 
				
			||||||
                        List<List<String>> columnContent = new ArrayList<>();
 | 
					 | 
				
			||||||
                        for (int i = 0; i < table.getColumns().size(); i++) {
 | 
					 | 
				
			||||||
                            var column = table.getColumns().get(i);
 | 
					 | 
				
			||||||
                            String type;
 | 
					 | 
				
			||||||
                            if (column.getDecimalDigits() == null || column.getDecimalDigits() == 0) {
 | 
					 | 
				
			||||||
                                type = table.getType() + "(" + column.getSize() + ")";
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                type = table.getType() + "(" + column.getSize() + "," + column.getDecimalDigits() + ")";
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            columnContent.add(List.of((i + 1) + "",
 | 
					 | 
				
			||||||
                                    column.getName(),
 | 
					 | 
				
			||||||
                                    type,
 | 
					 | 
				
			||||||
                                    column.getIsPrimaryKey() ? "YES" : "NO",
 | 
					 | 
				
			||||||
                                    column.getNullable(),
 | 
					 | 
				
			||||||
                                    column.getAutoIncrement(),
 | 
					 | 
				
			||||||
                                    columnDefaultValueMapping.apply(column),
 | 
					 | 
				
			||||||
                                    column.getComment()));
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        builder.thirdTitle("columns");
 | 
					 | 
				
			||||||
                        builder.table(List.of("", "名称", "类型", "是否为主键", "可为空", "自增", "默认值", "备注"),
 | 
					 | 
				
			||||||
                                columnContent);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // indexes
 | 
					 | 
				
			||||||
                        List<List<String>> indexContent = new ArrayList<>();
 | 
					 | 
				
			||||||
                        for (int i = 0; i < table.getIndexes().size(); i++) {
 | 
					 | 
				
			||||||
                            var index = table.getIndexes().get(i);
 | 
					 | 
				
			||||||
                            String columnNames = String.join(", ", index.getColumnNames());
 | 
					 | 
				
			||||||
                            String isUnique = index.getIsUnique() ? "YES" : "NO";
 | 
					 | 
				
			||||||
                            indexContent.add(List.of((i + 1) + "", index.getName(), isUnique, columnNames));
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        builder.thirdTitle("indexes");
 | 
					 | 
				
			||||||
                        builder.table(List.of("", "名称", "是否唯一", "关联列"), indexContent);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (!table.getTriggers().isEmpty()) {
 | 
					 | 
				
			||||||
                            List<List<String>> triggerContent = new ArrayList<>();
 | 
					 | 
				
			||||||
                            for (int i = 0; i < table.getTriggers().size(); i++) {
 | 
					 | 
				
			||||||
                                var trigger = table.getTriggers().get(i);
 | 
					 | 
				
			||||||
                                triggerContent.add(List.of((i + 1) + "",
 | 
					 | 
				
			||||||
                                        trigger.getName(),
 | 
					 | 
				
			||||||
                                        trigger.getTiming(),
 | 
					 | 
				
			||||||
                                        trigger.getManipulation(),
 | 
					 | 
				
			||||||
                                        trigger.getStatement()));
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            builder.thirdTitle("triggers");
 | 
					 | 
				
			||||||
                            builder.table(List.of("", "名称", "timing", "manipulation", "statement"), triggerContent);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                    return builder.build();
 | 
					 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user