From c1bc259c103c292f57fe848d9b5998b6ec284dfe Mon Sep 17 00:00:00 2001 From: liaozan <378024053@qq.com> Date: Sun, 20 Mar 2022 21:48:25 +0800 Subject: [PATCH] feat: support preview --- .../controller/InitializerController.java | 46 ++++----- .../initializer/maven/MavenUtils.java | 33 ++++--- .../param/ArchetypeGenerateParam.java | 10 ++ .../initializer/response/PreviewFileTree.java | 27 ++++++ .../initializer/service/ArchetypeService.java | 93 +++++++++++++++++++ .../src/components/BackendStarter.vue | 74 ++++++++++++--- initializer-ui/src/util/request.js | 5 +- 7 files changed, 228 insertions(+), 60 deletions(-) create mode 100644 initializer-backend/src/main/java/com/schbrain/archetype/initializer/response/PreviewFileTree.java create mode 100644 initializer-backend/src/main/java/com/schbrain/archetype/initializer/service/ArchetypeService.java diff --git a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/controller/InitializerController.java b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/controller/InitializerController.java index e1bb75c..d87f346 100644 --- a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/controller/InitializerController.java +++ b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/controller/InitializerController.java @@ -1,21 +1,16 @@ package com.schbrain.archetype.initializer.controller; -import com.schbrain.archetype.initializer.maven.MavenUtils; import com.schbrain.archetype.initializer.param.ArchetypeGenerateParam; -import com.schbrain.common.util.ServletUtils; -import org.springframework.http.ContentDisposition; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.util.StreamUtils; +import com.schbrain.archetype.initializer.response.PreviewFileTree; +import com.schbrain.archetype.initializer.service.ArchetypeService; +import com.schbrain.common.support.result.ResponseDTO; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.util.List; /** * @author liaozan @@ -24,25 +19,22 @@ import java.io.IOException; @RestController public class InitializerController { - @PostMapping("/archetype/generate") - public void generateArchetype(@RequestBody @Validated ArchetypeGenerateParam param) throws IOException { - File generatedProject = MavenUtils.generate(param); - - FileInputStream inputStream = new FileInputStream(generatedProject); + @Autowired + private ArchetypeService archetypeService; - HttpServletResponse response = ServletUtils.getResponse(); - response.addHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition(generatedProject)); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + @PostMapping("/archetype/generate") + public ResponseDTO generateArchetype(@RequestBody @Validated ArchetypeGenerateParam param) { + return ResponseDTO.success(archetypeService.generate(param)); + } - StreamUtils.copy(inputStream, response.getOutputStream()); + @GetMapping("/archetype/preview/{id}") + public ResponseDTO> previewArchetype(@PathVariable String id) throws FileNotFoundException { + return ResponseDTO.success(archetypeService.preview(id)); } - private String contentDisposition(File file) { - return ContentDisposition - .attachment() - .filename(file.getName()) - .build() - .toString(); + @GetMapping("/archetype/download/{id}") + public void downloadArchetype(@PathVariable String id) throws IOException { + archetypeService.download(id); } } \ No newline at end of file diff --git a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/maven/MavenUtils.java b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/maven/MavenUtils.java index efb572c..2e259c0 100644 --- a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/maven/MavenUtils.java +++ b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/maven/MavenUtils.java @@ -1,8 +1,8 @@ package com.schbrain.archetype.initializer.maven; import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.ZipUtil; import com.schbrain.archetype.initializer.param.ArchetypeGenerateParam; +import com.schbrain.common.util.IdWorker; import com.schbrain.common.util.JacksonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.maven.cli.MavenCli; @@ -10,6 +10,7 @@ import org.apache.maven.settings.Mirror; import org.apache.maven.settings.Server; import org.apache.maven.settings.Settings; import org.apache.maven.settings.io.DefaultSettingsWriter; +import org.springframework.util.StringUtils; import java.io.File; import java.io.IOException; @@ -38,20 +39,21 @@ public class MavenUtils { log.info("Success install archive of : {}", workDirectory); } - public static File generate(ArchetypeGenerateParam param) { + public static String generate(ArchetypeGenerateParam param) { log.info("Prepare to generate archetype project: {}", JacksonUtils.toJsonString(param, true)); - MavenCli mavenCli = new MavenCli(); - - File archetype = getArchetypeDirectory(param.getArtifactId()); + String id = IdWorker.getIdStr(); + File archetype = getArchetypeDirectory(id); String outputDirectory = archetype.getAbsolutePath(); - String[] args = getArchetypeGenerateArgs(param, getSettingsFile().getAbsolutePath(), outputDirectory); - mavenCli.doMain(args, null, System.out, System.err); - log.info("Generate archetype project at {}", outputDirectory); - return ZipUtil.zip(archetype); + return id; + } + + public static File getArchetypeDirectory(String id) { + String tmpDirPath = FileUtil.getTmpDirPath(); + return new File(tmpDirPath, id); } private static File getSettingsFile() { @@ -88,14 +90,6 @@ public class MavenUtils { return SETTINGS_FILE; } - private static File getArchetypeDirectory(String artifactId) { - String tmpDirPath = FileUtil.getTmpDirPath(); - File archetype = new File(tmpDirPath, artifactId); - // clean cache - FileUtil.del(archetype); - return archetype; - } - private static String[] getInstallArgs(String settingsFile) { return new String[]{ "-B", @@ -107,6 +101,10 @@ public class MavenUtils { @SuppressWarnings("SpellCheckingInspection") private static String[] getArchetypeGenerateArgs(ArchetypeGenerateParam param, String settingsFile, String outputDirectory) { + String subModuleNamePrefix = param.getSubModuleNamePrefix(); + if (!StringUtils.hasText(subModuleNamePrefix)) { + subModuleNamePrefix = param.getArtifactId(); + } return new String[]{ "-B", "archetype:generate", @@ -117,6 +115,7 @@ public class MavenUtils { String.format("-DoutputDirectory=%s", outputDirectory), String.format("-DgroupId=%s", param.getGroupId()), String.format("-DartifactId=%s", param.getArtifactId()), + String.format("-DsubModuleNamePrefix=%s", subModuleNamePrefix), }; } diff --git a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/param/ArchetypeGenerateParam.java b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/param/ArchetypeGenerateParam.java index 0ce7402..a99d94d 100644 --- a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/param/ArchetypeGenerateParam.java +++ b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/param/ArchetypeGenerateParam.java @@ -11,9 +11,19 @@ import javax.validation.constraints.NotBlank; @Data public class ArchetypeGenerateParam { + /** + * groupId + */ @NotBlank private String groupId; + /** + * artifactId + */ @NotBlank private String artifactId; + /** + * 子模块前缀 + */ + private String subModuleNamePrefix; } \ No newline at end of file diff --git a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/response/PreviewFileTree.java b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/response/PreviewFileTree.java new file mode 100644 index 0000000..5ecbd2e --- /dev/null +++ b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/response/PreviewFileTree.java @@ -0,0 +1,27 @@ +package com.schbrain.archetype.initializer.response; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +/** + * @author liaozan + * @since 2022/3/20 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PreviewFileTree { + + private String fileName; + + private List children; + + public boolean isFile() { + return CollectionUtils.isEmpty(children); + } + +} \ No newline at end of file diff --git a/initializer-backend/src/main/java/com/schbrain/archetype/initializer/service/ArchetypeService.java b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/service/ArchetypeService.java new file mode 100644 index 0000000..9833d84 --- /dev/null +++ b/initializer-backend/src/main/java/com/schbrain/archetype/initializer/service/ArchetypeService.java @@ -0,0 +1,93 @@ +package com.schbrain.archetype.initializer.service; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ZipUtil; +import com.schbrain.archetype.initializer.maven.MavenUtils; +import com.schbrain.archetype.initializer.param.ArchetypeGenerateParam; +import com.schbrain.archetype.initializer.response.PreviewFileTree; +import com.schbrain.common.util.ServletUtils; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.util.StreamUtils; + +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * @author liaozan + * @since 2022/3/20 + */ +@Service +public class ArchetypeService { + + private final Map archetypeNameCache = new ConcurrentHashMap<>(); + + public String generate(ArchetypeGenerateParam param) { + String generateId = MavenUtils.generate(param); + archetypeNameCache.put(generateId, param.getArtifactId()); + return generateId; + } + + public List preview(String id) throws FileNotFoundException { + File generatedFiles = getGeneratedFiles(id); + return List.of(buildFileTree(generatedFiles)); + } + + public void download(String id) throws IOException { + File generatedFiles = ZipUtil.zip(getGeneratedFiles(id)); + FileInputStream inputStream = new FileInputStream(generatedFiles); + HttpServletResponse response = ServletUtils.getResponse(); + response.addHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition(generatedFiles)); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + StreamUtils.copy(inputStream, response.getOutputStream()); + } + + private PreviewFileTree buildFileTree(File root) { + File[] fileItems = root.listFiles(); + if (ArrayUtil.isEmpty(fileItems)) { + return new PreviewFileTree(root.getName(), Collections.emptyList()); + } + List children = Arrays.stream(fileItems) + .map(fileItem -> { + PreviewFileTree childrenFileTree; + if (fileItem.isDirectory()) { + childrenFileTree = buildFileTree(fileItem); + } else { + childrenFileTree = new PreviewFileTree(fileItem.getName(), Collections.emptyList()); + } + return childrenFileTree; + }) + .sorted(Comparator.comparing(PreviewFileTree::isFile)) + .collect(Collectors.toList()); + PreviewFileTree fileTree = new PreviewFileTree(); + fileTree.setFileName(root.getName()); + fileTree.setChildren(children); + return fileTree; + } + + private File getGeneratedFiles(String id) throws FileNotFoundException { + File archetypeDirectory = MavenUtils.getArchetypeDirectory(id); + if (!archetypeDirectory.exists()) { + throw new FileNotFoundException(archetypeDirectory.getAbsolutePath()); + } + String artifactId = archetypeNameCache.get(id); + return new File(archetypeDirectory, artifactId); + } + + private String contentDisposition(File file) { + return ContentDisposition + .attachment() + .filename(file.getName()) + .build() + .toString(); + } + +} \ No newline at end of file diff --git a/initializer-ui/src/components/BackendStarter.vue b/initializer-ui/src/components/BackendStarter.vue index 6d4fccf..24dfbf0 100644 --- a/initializer-ui/src/components/BackendStarter.vue +++ b/initializer-ui/src/components/BackendStarter.vue @@ -15,7 +15,18 @@ - + + + + + + + + @@ -28,6 +39,13 @@ export default { name: "BackendStarter", data() { return { + id: null, + dialogVisible: false, + defaultProps: { + children: 'children', + label: 'fileName', + }, + treeData: [], form: { groupId: '', artifactId: '', @@ -36,20 +54,27 @@ export default { }, methods: { submit: function () { - axios.post('/archetype/generate', this.form, { + axios.post('/archetype/generate', this.form).then(res => { + this.id = res.data.result + }, error => { + ElMessageBox.alert(error.data.message) + }) + }, + preview: function () { + axios.get(`/archetype/preview/${this.id}`, this.form).then(res => { + this.treeData = res.data.result + this.dialogVisible = true + }, error => { + ElMessageBox.alert(error.data.message) + }) + }, + download: function () { + axios.get(`/archetype/download/${this.id}`, { responseType: 'blob' }).then(res => { const {headers, data} = res const fileName = headers['content-disposition'].replace(/\w+;\sfilename="(.*)"/, '$1') - const blob = new Blob([data]) - const link = document.createElement('a'); - link.style.display = "none"; - link.href = URL.createObjectURL(blob); - link.download = fileName; - document.body.appendChild(link); - link.click(); - URL.revokeObjectURL(link.href); - document.body.removeChild(link); + this.downloadGeneratedProject(data, fileName); }, error => { const reader = new FileReader(); reader.readAsText(error.data, 'utf-8'); @@ -60,6 +85,20 @@ export default { ) } }) + }, + handleNodeClick: function (data) { + console.log(data) + }, + downloadGeneratedProject: function (data, fileName) { + const blob = new Blob([data]) + const link = document.createElement('a'); + link.style.display = "none"; + link.href = URL.createObjectURL(blob); + link.download = fileName; + document.body.appendChild(link); + link.click(); + URL.revokeObjectURL(link.href); + document.body.removeChild(link); } } } @@ -113,7 +152,7 @@ export default { } .form-submit { - margin-top: 10px; + margin: 10px 5px 0 5px; background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ff5bb0), color-stop(1, #ef027d)); border-radius: 9px; -webkit-border-radius: 9px; @@ -123,11 +162,18 @@ export default { font-size: 15px; font-weight: bold; font-style: normal; - height: 40px; + height: 35px; line-height: 30px; - width: 100px; + width: 60px; text-decoration: none; text-shadow: 1px 1px 0 #c70067; cursor: pointer; } + +.form-submit:disabled { + background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #000000), color-stop(1, #000000)); + border: 1px solid #000000; + text-shadow: 1px 1px 0 #000000; + cursor: not-allowed; +} \ No newline at end of file diff --git a/initializer-ui/src/util/request.js b/initializer-ui/src/util/request.js index 0fc5eac..062bc3c 100644 --- a/initializer-ui/src/util/request.js +++ b/initializer-ui/src/util/request.js @@ -18,14 +18,15 @@ service.interceptors.request.use(config => { service.interceptors.response.use(response => { hideLoading() - if (response.data.type === 'application/json') { - if (response.data.code !== 200) { + if (response.headers['content-type'] === 'application/json') { + if (response.data.code !== 0) { return Promise.reject(response); } } return response }, error => { hideLoading() + return Promise.reject(error) }) export default service \ No newline at end of file -- GitLab