From 1593db9189465e611e7a53a9f909d59a98ab3572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Fri, 22 May 2026 15:46:15 +0800 Subject: [PATCH] Support front draft project preview --- .../front/FrontProjectController.java | 44 ++++ .../service/GenProjectServiceImpl.java | 246 ++++++++---------- .../generator/service/IGenProjectService.java | 8 +- .../front/FrontProjectPreviewServiceImpl.java | 86 ++++++ .../front/IFrontProjectPreviewService.java | 17 ++ .../service/GenProjectServiceImplTest.java | 47 ++++ .../FrontProjectPreviewServiceImplTest.java | 92 +++++++ 7 files changed, 407 insertions(+), 133 deletions(-) create mode 100644 RuoYi-Vue/ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/FrontProjectPreviewServiceImpl.java create mode 100644 RuoYi-Vue/ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IFrontProjectPreviewService.java create mode 100644 RuoYi-Vue/ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/FrontProjectPreviewServiceImplTest.java diff --git a/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java b/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java index 04b50af..894b5b6 100644 --- a/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java +++ b/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java @@ -1,5 +1,8 @@ package com.ruoyi.web.controller.front; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -8,6 +11,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -16,7 +20,9 @@ import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse; import com.ruoyi.generator.domain.front.dto.FrontProjectCreateRequest; import com.ruoyi.generator.domain.front.dto.FrontProjectUpdateRequest; import com.ruoyi.generator.domain.front.dto.GenerateDatabaseRequest; +import com.ruoyi.generator.domain.front.dto.PreviewFileRequest; import com.ruoyi.generator.service.front.IAiGenerateService; +import com.ruoyi.generator.service.front.IFrontProjectPreviewService; import com.ruoyi.generator.service.front.IFrontProjectService; @RestController @@ -27,6 +33,8 @@ public class FrontProjectController extends BaseController private IFrontProjectService frontProjectService; @Autowired private IAiGenerateService aiGenerateService; + @Autowired + private IFrontProjectPreviewService frontProjectPreviewService; @PostMapping("/create") public AjaxResult create(@RequestBody FrontProjectCreateRequest request) @@ -83,4 +91,40 @@ public class FrontProjectController extends BaseController Long userId = SecurityUtils.getUserId(); return success(aiGenerateService.generateDatabase(userId, projectId, request)); } + + @PostMapping("/{projectId}/preview") + public AjaxResult preview(@PathVariable Long projectId) + { + Long userId = SecurityUtils.getUserId(); + frontProjectPreviewService.getStructure(userId, projectId, "backend"); + frontProjectPreviewService.getStructure(userId, projectId, "frontend"); + frontProjectPreviewService.getStructure(userId, projectId, "admin_frontend"); + return toAjax(frontProjectPreviewService.markPreviewReady(userId, projectId)); + } + + @GetMapping("/{projectId}/structure") + public AjaxResult structure(@PathVariable Long projectId, @RequestParam String templateType) + { + Long userId = SecurityUtils.getUserId(); + return success(frontProjectPreviewService.getStructure(userId, projectId, templateType)); + } + + @GetMapping("/{projectId}/file-content") + public AjaxResult fileContent(@PathVariable Long projectId, PreviewFileRequest request) + { + Long userId = SecurityUtils.getUserId(); + return success(frontProjectPreviewService.getFileContent(userId, projectId, request)); + } + + @GetMapping("/{projectId}/download") + public void download(@PathVariable Long projectId, @RequestParam String templateType, HttpServletResponse response) throws IOException + { + Long userId = SecurityUtils.getUserId(); + byte[] data = frontProjectPreviewService.download(userId, projectId, templateType); + response.reset(); + response.setHeader("Content-Disposition", "attachment; filename=\"project.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } } diff --git a/RuoYi-Vue/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java b/RuoYi-Vue/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java index 6b9c63b..67b5eb2 100644 --- a/RuoYi-Vue/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java +++ b/RuoYi-Vue/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java @@ -2,28 +2,36 @@ package com.ruoyi.generator.service; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.generator.domain.*; +import com.ruoyi.generator.domain.GenProject; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.domain.SysCodeSnippet; +import com.ruoyi.generator.domain.SysProjectModule; +import com.ruoyi.generator.domain.SysProjectStructure; +import com.ruoyi.generator.domain.SysProjectTemplate; +import com.ruoyi.generator.domain.Template; +import com.ruoyi.generator.domain.TemplateFile; import com.ruoyi.generator.mapper.GenProjectMapper; import com.ruoyi.generator.mapper.GenTableMapper; import com.ruoyi.generator.util.VelocityInitializer; import com.ruoyi.generator.util.VelocityUtils; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.apache.velocity.VelocityContext; -import org.apache.velocity.app.Velocity; -/** - * 项目表 服务层实现 - * - * @author ruoyi - */ @Service public class GenProjectServiceImpl implements IGenProjectService { @Autowired @@ -43,96 +51,60 @@ public class GenProjectServiceImpl implements IGenProjectService { @Autowired private ISysProjectStructureService sysProjectStructureService; - /** - * 查询项目信息 - * - * @param projectId 项目ID - * @return 项目信息 - */ @Override public GenProject selectGenProjectById(Long projectId) { return genProjectMapper.selectGenProjectById(projectId); } - /** - * 查询项目列表 - * - * @param genProject 项目信息 - * @return 项目集合 - */ @Override public List selectGenProjectList(GenProject genProject) { return genProjectMapper.selectGenProjectList(genProject); } - /** - * 新增项目 - * - * @param genProject 项目信息 - * @return 结果 - */ @Override public int insertGenProject(GenProject genProject) { return genProjectMapper.insertGenProject(genProject); } - /** - * 修改项目 - * - * @param genProject 项目信息 - * @return 结果 - */ @Override public int updateGenProject(GenProject genProject) { return genProjectMapper.updateGenProject(genProject); } - /** - * 删除项目对象 - * - * @param projectId 项目ID - * @return 结果 - */ @Override public int deleteGenProjectById(Long projectId) { return genProjectMapper.deleteGenProjectById(projectId); } - /** - * 批量删除项目信息 - * - * @param projectIds 需要删除的项目ID - * @return 结果 - */ @Override public int deleteGenProjectByIds(Long[] projectIds) { return genProjectMapper.deleteGenProjectByIds(projectIds); } - /** - * 获取项目结构 - * - * @param projectId 项目ID - * @return 项目结构 - */ @Override public List> getProjectStructure(Long projectId, String type) { GenProject project = getRequiredProject(projectId); - - List projectTables = genTableMapper.selectGenTablesByProjectId(projectId); - project.setTables(projectTables); + project.setTables(genTableMapper.selectGenTablesByProjectId(projectId)); + return getProjectStructure(project, type); + } - List> structure = new ArrayList<>(); + @Override + public List> getProjectStructure(GenProject project, String type) { + if (project == null) { + throw new ServiceException("项目信息不能为空"); + } + List projectTables = project.getTables(); + if (projectTables == null) { + projectTables = new ArrayList(); + project.setTables(projectTables); + } - // 获取项目模板 - SysProjectTemplate projectTemplate = getRequiredProjectTemplate(project, type); + List> structure = new ArrayList>(); + SysProjectTemplate projectTemplate = resolveProjectTemplate(project, type); + List structureNodes = selectStructureNodes(project, projectTemplate); - // 获取模板关联的项目结构节点 - List structureNodes = sysProjectStructureService.selectProjectStructureListByModule(project.getProjectId(), projectTemplate.getTemplateId()); - - // 构建树形结构 - Map sourceNodeMap = new HashMap<>(); - Map> nodeMap = new LinkedHashMap<>(); + Map sourceNodeMap = new HashMap(); + Map> nodeMap = new LinkedHashMap>(); for (SysProjectStructure node : structureNodes) { sourceNodeMap.put(node.getNodeId(), node); if (isTableScopedNode(node)) { @@ -146,41 +118,36 @@ public class GenProjectServiceImpl implements IGenProjectService { } } - // 构建父子关系 for (SysProjectStructure node : structureNodes) { if (isTableScopedNode(node)) { - // 处理表特定的节点 for (GenTable table : projectTables) { Map nodeData = nodeMap.get(getNodeKey(node.getNodeId(), table.getTableId())); addNodeToParent(structure, nodeData, node, nodeMap, sourceNodeMap, table); } } else { - // 处理普通节点 Map nodeData = nodeMap.get(getNodeKey(node.getNodeId(), null)); addNodeToParent(structure, nodeData, node, nodeMap, sourceNodeMap, null); } } - return structure; } @Override public byte[] downloadStructure(Long projectId, String type) { GenProject project = getRequiredProject(projectId); + project.setTables(genTableMapper.selectGenTablesByProjectId(projectId)); + return downloadStructure(project, type); + } - List projectTables = genTableMapper.selectGenTablesByProjectId(projectId); - project.setTables(projectTables); - + @Override + public byte[] downloadStructure(GenProject project, String type) { + if (project == null) { + throw new ServiceException("项目信息不能为空"); + } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(outputStream); - try { - // 获取项目结构 - List> structure = getProjectStructure(projectId, type); - - // 递归处理项目结构 - processStructure(structure, zip, project, type); - + processStructure(getProjectStructure(project, type), zip, project, type); zip.close(); return outputStream.toByteArray(); } catch (IOException e) { @@ -201,19 +168,16 @@ public class GenProjectServiceImpl implements IGenProjectService { String fullPath = currentPath + name; if ("folder".equals(nodeType)) { - // 创建目录,确保以/结尾 String folderPath = fullPath + "/"; zip.putNextEntry(new ZipEntry(folderPath)); zip.closeEntry(); - // 处理子节点,传递当前完整路径 @SuppressWarnings("unchecked") List> children = (List>) node.get("children"); if (children != null && !children.isEmpty()) { processStructureWithPath(children, zip, project, folderPath, type); } } else if ("file".equals(nodeType)) { - // 生成文件内容 String content = generateFileContent(project, category, (Long) tableId, type); if (content != null) { zip.putNextEntry(new ZipEntry(fullPath)); @@ -224,33 +188,26 @@ public class GenProjectServiceImpl implements IGenProjectService { } } - /** - * 预览代码 - * - * @param tableId 表编号 - * @return 预览数据列表 - */ @Override - public Map previewCode(Long projectId, Long tableId, String keyword, String type) - { - Map dataMap = new LinkedHashMap<>(); + public Map previewCode(Long projectId, Long tableId, String keyword, String type) { GenProject project = getRequiredProject(projectId); - List projectTables = genTableMapper.selectGenTablesByProjectId(projectId); - project.setTables(projectTables); + project.setTables(genTableMapper.selectGenTablesByProjectId(projectId)); + return previewCode(project, tableId, keyword, type); + } - SysProjectModule projectModuleFilter = new SysProjectModule(); - projectModuleFilter.setProjectId(project.getProjectId()); - List modules = sysProjectModuleService.selectSysProjectModuleList(projectModuleFilter); + @Override + public Map previewCode(GenProject project, Long tableId, String keyword, String type) { + if (project == null) { + throw new ServiceException("项目信息不能为空"); + } + Map dataMap = new LinkedHashMap(); + List modules = getProjectModules(project); VelocityInitializer.initVelocity(); - - // 获取模板列表 - SysProjectTemplate projectTemplate = getRequiredProjectTemplate(project, type); + SysProjectTemplate projectTemplate = resolveProjectTemplate(project, type); List templateFileList = getRequiredTemplateFiles(projectTemplate.getTemplateId()); - for (TemplateFile templateFile : templateFileList) - { - if(matchesTemplateFile(templateFile, keyword)){ - // 渲染模板 + for (TemplateFile templateFile : templateFileList) { + if (matchesTemplateFile(templateFile, keyword)) { dataMap.put(templateFile.getFileName(), renderTemplate(project, tableId, modules, templateFile)); } } @@ -261,22 +218,16 @@ public class GenProjectServiceImpl implements IGenProjectService { if (project == null) { throw new ServiceException("项目信息不能为空"); } - SysProjectModule projectModuleFilter = new SysProjectModule(); - projectModuleFilter.setProjectId(project.getProjectId()); - List modules = sysProjectModuleService.selectSysProjectModuleList(projectModuleFilter); + List modules = getProjectModules(project); VelocityInitializer.initVelocity(); - - // 获取模板列表 - SysProjectTemplate projectTemplate = getRequiredProjectTemplate(project, type); + SysProjectTemplate projectTemplate = resolveProjectTemplate(project, type); List templateFileList = getRequiredTemplateFiles(projectTemplate.getTemplateId()); - for (TemplateFile templateFile : templateFileList) - { - if(matchesTemplateFile(templateFile, category)){ + for (TemplateFile templateFile : templateFileList) { + if (matchesTemplateFile(templateFile, category)) { try { return renderTemplate(project, tableId, modules, templateFile); } catch (Exception e) { - e.printStackTrace(); throw new ServiceException("渲染模板失败,表名:" + tableId); } } @@ -284,6 +235,12 @@ public class GenProjectServiceImpl implements IGenProjectService { throw new ServiceException("未找到模板文件:" + category); } + private List getProjectModules(GenProject project) { + SysProjectModule projectModuleFilter = new SysProjectModule(); + projectModuleFilter.setProjectId(project.getProjectId()); + return sysProjectModuleService.selectSysProjectModuleList(projectModuleFilter); + } + private GenProject getRequiredProject(Long projectId) { GenProject project = genProjectMapper.selectGenProjectById(projectId); if (project == null) { @@ -292,12 +249,39 @@ public class GenProjectServiceImpl implements IGenProjectService { return project; } - private SysProjectTemplate getRequiredProjectTemplate(GenProject project, String type) { + private SysProjectTemplate resolveProjectTemplate(GenProject project, String type) { SysProjectTemplate projectTemplate = sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(project.getProjectId(), type); - if (projectTemplate == null || projectTemplate.getTemplateId() == null) { - throw new ServiceException("项目未配置" + type + "模板"); + if (projectTemplate != null && projectTemplate.getTemplateId() != null) { + return projectTemplate; } - return projectTemplate; + + Template templateFilter = new Template(); + templateFilter.setTemplateType(type); + List