Verify chart business block generation integration

This commit is contained in:
王鹏
2026-06-22 20:32:29 +08:00
parent 7b305c3ab1
commit 5dd928ad6b
2 changed files with 487 additions and 2 deletions

View File

@@ -42,6 +42,7 @@ public class BusinessBlockGenerationServiceTest
assertTrue(containsPath(files, "src/views/pages/cart_page/index.vue")); assertTrue(containsPath(files, "src/views/pages/cart_page/index.vue"));
assertTrue(containsPath(files, "src/views/pages/cart_page/blocks/cart_001/CartBlock.vue")); assertTrue(containsPath(files, "src/views/pages/cart_page/blocks/cart_001/CartBlock.vue"));
assertTrue(containsPath(files, "src/api/business-blocks/cart_001.js")); assertTrue(containsPath(files, "src/api/business-blocks/cart_001.js"));
assertFalse(containsPath(files, "src/utils/chartRuntime.js"));
} }
@Test @Test

View File

@@ -13,6 +13,8 @@ import com.ruoyi.generator.domain.TemplateFile;
import com.ruoyi.generator.domain.GeneratedBusinessBlockFile; import com.ruoyi.generator.domain.GeneratedBusinessBlockFile;
import com.ruoyi.generator.domain.front.FrontProjectPageDesign; import com.ruoyi.generator.domain.front.FrontProjectPageDesign;
import com.ruoyi.generator.domain.front.dto.BusinessActionDesign; import com.ruoyi.generator.domain.front.dto.BusinessActionDesign;
import com.ruoyi.generator.domain.front.dto.BusinessActionEffectDesign;
import com.ruoyi.generator.domain.front.dto.BusinessActionRuleDesign;
import com.ruoyi.generator.domain.front.dto.BusinessActionUiBindingDesign; import com.ruoyi.generator.domain.front.dto.BusinessActionUiBindingDesign;
import com.ruoyi.generator.mapper.GenProjectMapper; import com.ruoyi.generator.mapper.GenProjectMapper;
import com.ruoyi.generator.mapper.GenTableMapper; import com.ruoyi.generator.mapper.GenTableMapper;
@@ -37,6 +39,7 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -46,6 +49,7 @@ import java.util.zip.ZipInputStream;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@@ -142,6 +146,40 @@ public class GenProjectServiceImplTest {
assertFalse(preview.containsKey("my-controller.java.vm")); assertFalse(preview.containsKey("my-controller.java.vm"));
} }
@Test
public void previewCodeImportsDateTypesForRunnableBackendEntityAuditColumns() {
GenProject project = project();
GenTable table = table(11L, "sys_user", "User");
GenTableColumn createTime = new GenTableColumn();
createTime.setTableId(11L);
createTime.setColumnName("create_time");
createTime.setColumnType("datetime");
createTime.setJavaType("Date");
createTime.setJavaField("createTime");
createTime.setIsPk("0");
table.setColumns(Arrays.asList(pkColumn(11L), createTime));
TemplateFile entityTemplate = templateFile("entity.java.vm",
"#foreach ($import in $importList)\nimport ${import};\n#end\n"
+ "@JsonFormat(pattern = \"yyyy-MM-dd\")\nprivate Date createTime;");
entityTemplate.setTemplateId(9201L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "backend"))
.thenReturn(projectTemplate(9201L));
when(templateFileService.selectTemplateFilesByTemplateId(9201L)).thenReturn(Arrays.asList(entityTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "entity.java.vm", "backend");
String content = preview.get("entity.java.vm");
assertTrue(content.contains("import java.util.Date;"));
assertTrue(content.contains("import com.fasterxml.jackson.annotation.JsonFormat;"));
}
@Test @Test
public void previewCodeAppliesOnlyMatchingActiveSnippetToTemplateInsertPoint() { public void previewCodeAppliesOnlyMatchingActiveSnippetToTemplateInsertPoint() {
GenProject project = project(); GenProject project = project();
@@ -166,6 +204,36 @@ public class GenProjectServiceImplTest {
assertFalse(content.contains("public void wrong() {}")); assertFalse(content.contains("public void wrong() {}"));
} }
@Test
public void previewCodeNormalizesResourceUrlProtocolChecks() {
GenProject project = project();
GenTable table = table(11L, "sys_user", "User");
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList(projectModule(1L)));
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "backend")).thenReturn(projectTemplate(100L));
when(templateFileService.selectTemplateFilesByTemplateId(100L)).thenReturn(Arrays.asList(
templateFile("index.vue.vm",
"if (/^(https?:)?\\/\\//.test(url) || url.indexOf(\"data:\") === 0 || url.indexOf(\"blob:\") === 0) {\n"
+ " return url\n"
+ "}\n"
+ "if (/^(https?:)?///.test(url) || url.indexOf(\"data:\") === 0 || url.indexOf(\"blob:\") === 0) {\n"
+ " return url\n"
+ "}")
));
Map<String, String> preview = service.previewCode(1L, 11L, "index.vue.vm", "backend");
String content = preview.get("index.vue.vm");
assertFalse(content.contains("/^(https?:)?\\/\\//"));
assertFalse(content.contains("/^(https?:)?///"));
assertTrue(content.contains("url.indexOf(\"http://\") === 0"));
assertTrue(content.contains("url.indexOf(\"//\") === 0"));
}
@Test @Test
public void previewCodeExposesBusinessActionsForCurrentTableOnly() { public void previewCodeExposesBusinessActionsForCurrentTableOnly() {
GenProject project = project(); GenProject project = project();
@@ -257,6 +325,95 @@ public class GenProjectServiceImplTest {
assertFalse(content.contains("sync_role")); assertFalse(content.contains("sync_role"));
} }
@Test
public void previewCodeEnrichesPageDesignedBusinessActionsWithRenderMetadata() {
GenProject project = project();
BusinessActionDesign action = businessAction("borrow_book", "sys_user");
action.setRequestFields(Arrays.asList("id", "user_id", "due_date"));
BusinessActionEffectDesign effect = new BusinessActionEffectDesign();
effect.setType("INSERT_ROW");
effect.setTargetTable("sys_user");
Map<String, String> values = new LinkedHashMap<String, String>();
values.put("user_id", "${current_user.id}");
effect.setValues(values);
action.setEffects(Arrays.asList(effect));
project.setBusinessActions(Arrays.asList(action));
project.setPageDesigns(Arrays.asList(pageDesign("user_page", "sys_user",
"{\"canvas\":\"frontend-list-v1\",\"regions\":["
+ "{\"id\":\"toolbar\",\"type\":\"toolbar\",\"actions\":[\"business:borrow_book\"]},"
+ "{\"id\":\"table\",\"type\":\"table\",\"fields\":[\"id\"],\"rowActions\":[]}"
+ "]}")));
GenTable table = table(11L, "sys_user", "User");
GenTableColumn dueDate = column(11L, "due_date", "dueDate");
dueDate.setColumnComment("Due date");
dueDate.setHtmlType("datetime");
table.setColumns(Arrays.asList(pkColumn(11L), column(11L, "user_id", "userId"), dueDate));
project.setTables(Arrays.asList(table));
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "frontend")).thenReturn(projectTemplate(100L));
when(templateFileService.selectTemplateFilesByTemplateId(100L)).thenReturn(Arrays.asList(
templateFile("list.vue.vm",
"#foreach($action in $pageDesignBusinessActions)"
+ "trusted:#foreach($field in $action.currentUserFields)${field};#end"
+ "#foreach($field in $action.inputFields)"
+ "input:${field.field}:${field.label}:${field.htmlType};#end"
+ "#end")
));
Map<String, String> preview = service.previewCode(1L, 11L, "list.vue.vm", "frontend");
String content = preview.get("list.vue.vm");
assertTrue(content.contains("trusted:user_id;"));
assertTrue(content.contains("input:due_date:Due date:datetime;"));
}
@Test
public void previewCodeAppliesPageDesignBusinessButtonPresentation() {
GenProject project = project();
project.setBusinessActions(Arrays.asList(
businessAction("approve_book", "sys_user")
));
project.setPageDesigns(Arrays.asList(pageDesign("user_page", "sys_user",
"{\"canvas\":\"frontend-list-v1\","
+ "\"businessButtons\":{\"approve_book\":{"
+ "\"label\":\"审核通过\","
+ "\"type\":\"success\","
+ "\"icon\":\"el-icon-check\","
+ "\"confirmMessage\":\"确认审核通过?\","
+ "\"successMessage\":\"已审核\","
+ "\"refreshTarget\":\"list\""
+ "}},"
+ "\"regions\":["
+ "{\"id\":\"toolbar\",\"type\":\"toolbar\",\"actions\":[\"business:approve_book\"]},"
+ "{\"id\":\"table\",\"type\":\"table\",\"fields\":[\"id\"],\"rowActions\":[]}"
+ "]}")));
GenTable table = table(11L, "sys_user", "User");
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList(projectModule(1L)));
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "frontend")).thenReturn(projectTemplate(100L));
when(templateFileService.selectTemplateFilesByTemplateId(100L)).thenReturn(Arrays.asList(
templateFile("list.vue.vm",
"#foreach($action in $pageDesignToolbarBusinessActions)"
+ "${action.code}:${action.buttonLabel}:${action.buttonType}:${action.buttonIcon}:"
+ "${action.confirmMessage}:${action.successMessage}:${action.refreshTarget};"
+ "#end")
));
Map<String, String> preview = service.previewCode(1L, 11L, "list.vue.vm", "frontend");
String content = preview.get("list.vue.vm");
assertTrue(content.contains("approve_book:审核通过:success:el-icon-check:确认审核通过?:已审核:list;"));
}
@Test @Test
public void previewCodeUsesPageDesignedBusinessActionsFromOtherOwnerTables() { public void previewCodeUsesPageDesignedBusinessActionsFromOtherOwnerTables() {
GenProject project = project(); GenProject project = project();
@@ -316,16 +473,151 @@ public class GenProjectServiceImplTest {
assertTrue(content.contains("portal-page")); assertTrue(content.contains("portal-page"));
assertTrue(content.contains("content-board")); assertTrue(content.contains("content-board"));
assertTrue(content.contains("businessActionMap")); assertTrue(content.contains("businessActionMap"));
assertTrue(content.contains("routeActionMap"));
assertTrue(content.contains("handleConfiguredRoute"));
}
@Test
public void previewCodeUsesBundledFrontendIndexWhenDefaultRunnableTemplateLacksBusinessButtonConfig() {
GenProject project = project();
GenTable table = table(11L, "sys_user", "User");
TemplateFile staleTemplate = templateFile("frontend-index.vue.vm",
"#set($designed = $hasCurrentFrontendPageDesign)\n"
+ "#set($renderForm = true)\n"
+ "<template><div>old business button template</div></template>\n"
+ "<script>export default { data() { return { businessActionMap: {}, routeActionMap: {}, __currentUserOnly: false } },"
+ " methods: { handleConfiguredRoute() {}, handleView(row) {}, resolveResourceUrl(url) { return url } } }</script>");
staleTemplate.setTemplateId(9203L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "frontend")).thenReturn(projectTemplate(9203L));
when(templateFileService.selectTemplateFilesByTemplateId(9203L)).thenReturn(Arrays.asList(staleTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "frontend-index.vue.vm", "frontend");
String content = preview.get("frontend-index.vue.vm");
assertFalse(content.contains("old business button template"));
assertTrue(content.contains("executeBusinessAction"));
assertTrue(content.contains("refreshBusinessActionTarget"));
assertTrue(content.contains("businessActionDialogVisible"));
assertTrue(content.contains("submitBusinessAction"));
assertTrue(content.contains("currentUserFields"));
assertFalse(content.contains("this.$prompt"));
}
@Test
public void previewCodeUsesBundledFrontendIndexWhenDefaultRunnableTemplateHasSqlFragileRouteRegex() {
GenProject project = project();
GenTable table = table(11L, "sys_user", "User");
TemplateFile staleTemplate = templateFile("frontend-index.vue.vm",
"#set($designed = $hasCurrentFrontendPageDesign)\n"
+ "#set($renderForm = true)\n"
+ "<template><div>old route regex template</div></template>\n"
+ "<script>export default { data() { return { businessActionMap: {}, routeActionMap: {}, __currentUserOnly: false } },"
+ " methods: { handleConfiguredRoute() { return /(^|\\/):[^/]+/.test(path) ? \"\" : path },"
+ " handleView(row) {}, resolveResourceUrl(url) { return url }, executeBusinessAction() {}, refreshBusinessActionTarget() {} } }</script>");
staleTemplate.setTemplateId(9203L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "frontend")).thenReturn(projectTemplate(9203L));
when(templateFileService.selectTemplateFilesByTemplateId(9203L)).thenReturn(Arrays.asList(staleTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "frontend-index.vue.vm", "frontend");
String content = preview.get("frontend-index.vue.vm");
assertFalse(content.contains("old route regex template"));
assertFalse(content.contains("return /(^|\\/):[^/]+/.test(path) ? \"\" : path"));
assertTrue(content.contains("hasUnresolvedRouteParam(path)"));
}
@Test
public void previewCodeUsesBundledFrontendIndexWhenDefaultRunnableTemplateCannotResolveUploadedResourceUrls() {
GenProject project = project();
GenTable table = table(11L, "sys_user", "User");
TemplateFile staleTemplate = templateFile("frontend-index.vue.vm",
"#set($designed = $hasCurrentFrontendPageDesign)\n"
+ "#set($renderForm = true)\n"
+ "<template><el-image :src=\"scope.row.avatar\" /></template>\n"
+ "<script>export default { data() { return { businessActionMap: {}, routeActionMap: {}, __currentUserOnly: false } },"
+ " methods: { handleConfiguredRoute() {}, handleView(row) {} } }</script>");
staleTemplate.setTemplateId(9203L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "frontend")).thenReturn(projectTemplate(9203L));
when(templateFileService.selectTemplateFilesByTemplateId(9203L)).thenReturn(Arrays.asList(staleTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "frontend-index.vue.vm", "frontend");
String content = preview.get("frontend-index.vue.vm");
assertFalse(content.contains("<el-image :src=\"scope.row.avatar\" />"));
assertTrue(content.contains("resolveResourceUrl(url)"));
}
@Test
public void previewCodeUsesBundledFrontendAppWhenDefaultRunnableTemplateHasBrokenNavBinding() {
GenProject project = project();
GenTable table = table(11L, "sys_user", "User");
FrontProjectPageDesign home = new FrontProjectPageDesign();
home.setPageCode("home");
home.setPageName("Home");
home.setRoutePath("/home");
home.setPageScope("frontend");
home.setPageType("list");
home.setTableName("sys_user");
home.setLayoutJson("{\"navigation\":{\"showInMenu\":true,\"menuLevel\":\"primary\",\"menuCode\":\"home\",\"menuName\":\"Home\",\"menuOrder\":1},\"regions\":[]}");
project.setPageDesigns(Arrays.asList(home));
TemplateFile staleTemplate = templateFile("frontend-App.vue.vm",
"<template><nav><div class=\"portal-nav-group\" "
+ ":class=\"{ active: isNavGroupActive(${group.activePathsLiteral}) }\"></div></nav></template>");
staleTemplate.setTemplateId(9203L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "frontend")).thenReturn(projectTemplate(9203L));
when(templateFileService.selectTemplateFilesByTemplateId(9203L)).thenReturn(Arrays.asList(staleTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "frontend-App.vue.vm", "frontend");
String content = preview.get("frontend-App.vue.vm");
assertTrue(content.contains("portal-shell"));
assertFalse(content.contains(":class=\"{ active: isNavGroupActive("));
} }
@Test @Test
public void previewCodeUsesBundledControllerWhenDefaultRunnableTemplateIsStale() { public void previewCodeUsesBundledControllerWhenDefaultRunnableTemplateIsStale() {
GenProject project = project(); GenProject project = project();
BusinessActionDesign action = businessAction("borrow_book", "sys_user");
action.setRequestFields(Arrays.asList("user_id"));
BusinessActionEffectDesign effect = new BusinessActionEffectDesign();
effect.setType("INSERT_ROW");
effect.setTargetTable("sys_user");
Map<String, String> values = new LinkedHashMap<String, String>();
values.put("user_id", "${current_user.id}");
effect.setValues(values);
action.setEffects(Arrays.asList(effect));
project.setBusinessActions(Arrays.asList(action));
GenTable table = table(11L, "sys_user", "User"); GenTable table = table(11L, "sys_user", "User");
table.setColumns(Arrays.asList(pkColumn(11L), column(11L, "user_id", "userId"))); table.setColumns(Arrays.asList(pkColumn(11L), column(11L, "user_id", "userId")));
table.setPkColumn(table.getColumns().get(0)); table.setPkColumn(table.getColumns().get(0));
TemplateFile staleTemplate = templateFile("controller.java.vm", TemplateFile staleTemplate = templateFile("controller.java.vm",
"package ${packageName}.controller; public class ${ClassName}Controller {}"); "package ${packageName}.controller; "
+ "// __currentUserOnly prepareBusinessActionParams\n"
+ "public class ${ClassName}Controller {}");
staleTemplate.setTemplateId(9201L); staleTemplate.setTemplateId(9201L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project); when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
@@ -340,8 +632,99 @@ public class GenProjectServiceImplTest {
String content = preview.get("controller.java.vm"); String content = preview.get("controller.java.vm");
assertFalse(content.contains("public class UserController {}")); assertFalse(content.contains("public class UserController {}"));
assertTrue(content.contains("@GetMapping(\"/page\")")); assertTrue(content.contains("@GetMapping(\"/system/user/page\")"));
assertTrue(content.contains("__currentUserOnly")); assertTrue(content.contains("__currentUserOnly"));
assertTrue(content.contains("new String[] {\"user_id\"}"));
assertTrue(content.contains("actionParams.put(field, currentUser.getUserId())"));
}
@Test
public void previewCodeUsesBundledBusinessServiceWhenDefaultRunnableTemplateIsStale() {
GenProject project = project();
project.setBusinessActions(Arrays.asList(businessAction("borrow_book", "sys_user")));
GenTable table = table(11L, "sys_user", "User");
table.setColumns(Arrays.asList(pkColumn(11L), column(11L, "user_id", "userId")));
table.setPkColumn(table.getColumns().get(0));
TemplateFile staleTemplate = templateFile("service.java.vm",
"package ${packageName}.service; public interface I${ClassName}Service {}");
staleTemplate.setTemplateId(9201L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "backend")).thenReturn(projectTemplate(9201L));
when(templateFileService.selectTemplateFilesByTemplateId(9201L)).thenReturn(Arrays.asList(staleTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "service.java.vm", "backend");
String content = preview.get("service.java.vm");
assertTrue(content.contains("int borrow_book(Map<String, Object> params)"));
}
@Test
public void previewCodeUsesBundledBusinessServiceImplWhenDefaultRunnableTemplateIsStale() {
GenProject project = project();
project.setBusinessActions(Arrays.asList(businessAction("borrow_book", "sys_user")));
GenTable table = table(11L, "sys_user", "User");
table.setColumns(Arrays.asList(pkColumn(11L), column(11L, "user_id", "userId")));
table.setPkColumn(table.getColumns().get(0));
TemplateFile staleTemplate = templateFile("serviceImpl.java.vm",
"package ${packageName}.service.impl; "
+ "// @Transactional resolveBusinessValue\n"
+ "public class ${ClassName}ServiceImpl {}");
staleTemplate.setTemplateId(9201L);
when(genProjectMapper.selectGenProjectById(1L)).thenReturn(project);
when(genTableMapper.selectGenTablesByProjectId(1L)).thenReturn(Arrays.asList(table));
when(genTableMapper.selectGenTableById(11L)).thenReturn(table);
when(sysProjectModuleService.selectSysProjectModuleList(any(SysProjectModule.class))).thenReturn(Arrays.asList());
when(codeSnippetService.selectSysCodeSnippetList(any(SysCodeSnippet.class))).thenReturn(Arrays.asList());
when(sysProjectTemplateService.selectSysProjectTemplateByProjectAndType(1L, "backend")).thenReturn(projectTemplate(9201L));
when(templateFileService.selectTemplateFilesByTemplateId(9201L)).thenReturn(Arrays.asList(staleTemplate));
Map<String, String> preview = service.previewCode(1L, 11L, "serviceImpl.java.vm", "backend");
String content = preview.get("serviceImpl.java.vm");
assertTrue(content.contains("@Transactional(rollbackFor = Exception.class)"));
assertTrue(content.contains("resolveBusinessValue"));
assertTrue(content.contains("STRICT_TARGET_TABLE_CONDITIONS"));
}
@Test
public void previewCodeRejectsSavedBusinessActionConditionOutsideTargetTable() {
GenProject project = project();
GenTable bookTable = table(21L, "book_info", "Book");
bookTable.setColumns(Arrays.asList(
pkColumn(21L),
column(21L, "available_quantity", "availableQuantity")));
bookTable.setPkColumn(bookTable.getColumns().get(0));
GenTable borrowTable = table(22L, "borrow_info", "Borrow");
borrowTable.setColumns(Arrays.asList(
pkColumn(22L),
column(22L, "book_id", "bookId")));
borrowTable.setPkColumn(borrowTable.getColumns().get(0));
project.setTables(Arrays.asList(bookTable, borrowTable));
BusinessActionDesign action = businessAction("borrow_book", "book_info");
action.setRequestFields(Arrays.asList("book_id"));
BusinessActionRuleDesign rule = new BusinessActionRuleDesign();
rule.setType("NUMBER_GTE");
rule.setTargetTable("book_info");
rule.setTargetField("available_quantity");
rule.setCompareValue("1");
rule.setConditionFields(Arrays.asList("book_id"));
action.setRuleChecks(Arrays.asList(rule));
project.setBusinessActions(Arrays.asList(action));
try {
service.previewCode(project, 21L, "serviceImpl.java.vm", "backend");
fail("Expected invalid saved business action to be rejected");
} catch (ServiceException e) {
assertEquals("Business rule condition field does not exist on target table book_info: book_id",
e.getMessage());
}
} }
@Test @Test
@@ -737,6 +1120,53 @@ public class GenProjectServiceImplTest {
assertTrue(paths.contains("demo-app/src/api/business-blocks/cart_001.js")); assertTrue(paths.contains("demo-app/src/api/business-blocks/cart_001.js"));
} }
@Test
public void getProjectStructureAddsAdminBusinessBlockPageDesignFiles() {
GenProject project = project();
project.setFrontDraft(true);
FrontProjectPageDesign home = pageDesign("admin_home", "",
"{\"canvas\":\"business-blocks-v1\",\"blocks\":[],\"navigation\":{\"showInMenu\":true,\"menuOrder\":1}}");
home.setPageScope("admin");
home.setPageName("Home");
home.setRoutePath("/admin/home");
home.setPageType("business_block");
project.setPageDesigns(Arrays.asList(home));
when(templateService.selectTemplateList(any(Template.class))).thenReturn(Arrays.asList(template(9202L, 0L)));
when(sysProjectStructureService.selectSysProjectStructureList(any(SysProjectStructure.class))).thenReturn(Arrays.asList(
structure(1L, 0L, "{projectName}-admin", "folder", null, null),
structure(2L, 1L, "src", "folder", null, null)
));
List<Map<String, Object>> structure = service.getProjectStructure(project, "admin_frontend");
Set<String> paths = structurePaths(structure);
assertTrue(paths.contains("demo-app-admin/src/views/pages/admin_home/index.vue"));
}
@Test
public void adminChartFilesAppearInPreviewAndDownloadStructure() {
GenProject project = chartBusinessBlockProject();
project.setFrontDraft(true);
when(templateService.selectTemplateList(any(Template.class))).thenReturn(Arrays.asList(
template(9202L, 0L),
template(9201L, 0L)));
when(sysProjectStructureService.selectSysProjectStructureList(any(SysProjectStructure.class)))
.thenReturn(Arrays.asList(
structure(1L, 0L, "{projectName}-admin", "folder", null, null),
structure(2L, 1L, "src", "folder", null, null)));
Set<String> frontendPaths = structurePaths(service.getProjectStructure(project, "admin_frontend"));
Set<String> backendPaths = structurePaths(service.getProjectStructure(project, "backend"));
assertTrue(frontendPaths.contains("demo-app-admin/src/utils/chartRuntime.js"));
assertTrue(frontendPaths.contains(
"demo-app-admin/src/views/pages/admin_home/blocks/admin_line_chart_001/AdminLineChart001Block.vue"));
assertTrue(backendPaths.contains(
"demo-app-admin/src/main/resources/mapper/block/AdminLineChart001BlockMapper.xml"));
}
@Test @Test
public void getProjectStructureAddsNormalFrontendPageDesignFiles() { public void getProjectStructureAddsNormalFrontendPageDesignFiles() {
GenProject project = project(); GenProject project = project();
@@ -795,6 +1225,29 @@ public class GenProjectServiceImplTest {
assertTrue(entries.contains("demo-app/src/api/business-blocks/cart_001.js")); assertTrue(entries.contains("demo-app/src/api/business-blocks/cart_001.js"));
} }
@Test
public void downloadStructureSkipsDuplicateDirectoryEntriesFromEquivalentPaths() throws Exception {
GenProject project = project();
project.setFrontDraft(true);
project.setTables(Arrays.<GenTable>asList());
when(templateService.selectTemplateList(any(Template.class))).thenReturn(Arrays.asList(template(9201L, 0L)));
when(sysProjectStructureService.selectSysProjectStructureList(any(SysProjectStructure.class))).thenReturn(Arrays.asList(
structure(1L, 0L, "demo", "folder", null, null),
structure(2L, 1L, "src", "folder", null, null),
structure(3L, 2L, "com/example", "folder", null, null),
structure(4L, 3L, "controller", "folder", null, null),
structure(5L, 2L, "com", "folder", null, null),
structure(6L, 5L, "example", "folder", null, null),
structure(7L, 6L, "controller", "folder", null, null)
));
byte[] data = service.downloadStructure(project, "backend");
Set<String> entries = zipEntries(data);
assertTrue(entries.contains("demo/src/com/example/controller/"));
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<String, Object> child(Map<String, Object> node, int index) { private Map<String, Object> child(Map<String, Object> node, int index) {
return children(node).get(index); return children(node).get(index);
@@ -980,6 +1433,37 @@ public class GenProjectServiceImplTest {
return project; return project;
} }
private GenProject chartBusinessBlockProject() {
GenProject project = project();
GenTable orders = table(203L, "shop_order", "ShopOrder");
orders.setColumns(Arrays.asList(
pkColumn(203L),
column(203L, "amount", "amount"),
column(203L, "status", "status"),
column(203L, "create_time", "createTime")));
orders.getColumns().get(1).setJavaType("BigDecimal");
orders.getColumns().get(1).setColumnType("decimal(10,2)");
orders.getColumns().get(3).setJavaType("Date");
orders.getColumns().get(3).setColumnType("datetime");
orders.setPkColumn(orders.getColumns().get(0));
project.setTables(Arrays.asList(orders));
FrontProjectPageDesign design = pageDesign("admin_home", "",
"{\"canvas\":\"business-blocks-v1\",\"blocks\":["
+ "{\"id\":\"admin_line_chart_001\",\"blockCode\":\"admin_line_chart\",\"span\":6,"
+ "\"dataset\":{\"schema\":\"single-table-aggregate-v1\",\"table\":\"shop_order\","
+ "\"dimension\":{\"field\":\"createTime\",\"timeUnit\":\"day\"},"
+ "\"metrics\":[{\"field\":\"amount\",\"aggregate\":\"sum\",\"alias\":\"salesAmount\",\"label\":\"Sales\"}],"
+ "\"filters\":[{\"field\":\"status\",\"operator\":\"eq\",\"value\":\"PAID\"}],"
+ "\"sort\":{\"by\":\"dimension\",\"order\":\"asc\"},\"limit\":100},"
+ "\"display\":{\"title\":\"Sales Trend\"}}]}");
design.setPageScope("admin");
design.setPageType("business_block");
design.setRoutePath("/admin/home");
project.setPageDesigns(Arrays.asList(design));
return project;
}
private FrontProjectPageDesign businessBlockPageDesign() { private FrontProjectPageDesign businessBlockPageDesign() {
FrontProjectPageDesign design = pageDesign("cart_page", "shop_cart", FrontProjectPageDesign design = pageDesign("cart_page", "shop_cart",
"{\"canvas\":\"business-blocks-v1\",\"blocks\":[{\"id\":\"cart_001\",\"blockCode\":\"cart\",\"config\":{" "{\"canvas\":\"business-blocks-v1\",\"blocks\":[{\"id\":\"cart_001\",\"blockCode\":\"cart\",\"config\":{"