-- Business blueprint Velocity templates -- Import this script after sql/db.sql. -- Template ids: -- 9101 backend: RuoYi Spring Boot + MyBatis -- 9102 admin_frontend: Vue2 + Element UI for RuoYi admin -- 9103 frontend: Vue3 + Element Plus portal SET FOREIGN_KEY_CHECKS = 0; DELETE FROM sys_project_template WHERE template_id IN (9101, 9102, 9103); DELETE FROM sys_project_structure WHERE template_id IN (9101, 9102, 9103) OR node_id BETWEEN 910100 AND 910399; DELETE FROM sys_template_file WHERE template_id IN (9101, 9102, 9103) OR template_file_id BETWEEN 910100 AND 910399; DELETE FROM sys_template WHERE template_id IN (9101, 9102, 9103); INSERT INTO sys_template (template_id, template_name, template_path, template_desc, template_type, template_status, create_by, create_time, remark) VALUES (9101, 'Business Blueprint Backend', '/blueprint/backend', 'RuoYi backend with CRUD and executable blueprint effects', 'backend', 0, 'admin', sysdate(), 'business blueprint'), (9102, 'Business Blueprint Admin Frontend', '/blueprint/admin_frontend', 'Vue2 Element UI admin pages with blueprint action buttons', 'admin_frontend', 0, 'admin', sysdate(), 'business blueprint'), (9103, 'Business Blueprint Portal Frontend', '/blueprint/frontend', 'Vue3 Element Plus portal pages with blueprint action buttons', 'frontend', 0, 'admin', sysdate(), 'business blueprint'); -- Backend template files INSERT INTO sys_template_file (template_file_id, template_id, file_name, module_id, file_path, file_content, create_by, create_time) VALUES (910101, 9101, 'pom.xml.vm', NULL, 'pom.xml.vm', ' 4.0.0 ${packageName} ${projectFileName}-backend 1.0.0 ${projectName} Generated backend with business blueprint actions com.ruoyi ruoyi 3.8.8 com.ruoyi ruoyi-common com.ruoyi ruoyi-framework ', 'admin', sysdate()), (910102, 9101, 'application.yml.vm', NULL, 'application.yml.vm', 'server: port: 8080 spring: application: name: ${projectFileName}-backend ', 'admin', sysdate()), (910103, 9101, 'README.md.vm', NULL, 'README.md.vm', '# ${projectName} Backend Generated from EasyCode business blueprint templates. Create table SQL: ```sql ${createTableSql} ``` # Business Actions #if($hasBusinessActions) #foreach($action in $businessActions) - ${action.name} `${action.code}` `${action.method} ${action.path}` owner `${action.ownerTable}` #end #else No business actions in blueprint. #end ', 'admin', sysdate()), (910104, 9101, 'domain.java.vm', NULL, 'domain.java.vm', 'package ${packageName}.domain; import java.math.BigDecimal; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.core.domain.BaseEntity; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; /** * ${functionName} object ${tableName} * * @author ${author} * @date ${datetime} */ public class ${ClassName} extends BaseEntity { private static final long serialVersionUID = 1L; #foreach($column in $columns) #if(!$table.isSuperColumn($column.javaField)) /** ${column.columnComment} */ #if($column.javaType == "Date") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") #end #if($column.isList == "1") @Excel(name = "${column.columnComment}") #end private ${column.javaType} ${column.javaField}; #end #end #foreach($column in $columns) #if(!$table.isSuperColumn($column.javaField)) public void set${column.capJavaField}(${column.javaType} ${column.javaField}) { this.${column.javaField} = ${column.javaField}; } public ${column.javaType} get${column.capJavaField}() { return ${column.javaField}; } #end #end @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) #foreach($column in $columns) #if(!$table.isSuperColumn($column.javaField)) .append("${column.javaField}", get${column.capJavaField}()) #end #end .toString(); } } ', 'admin', sysdate()), (910105, 9101, 'mapper.java.vm', NULL, 'mapper.java.vm', 'package ${packageName}.mapper; import java.util.List; import ${packageName}.domain.${ClassName}; /** * ${functionName} Mapper */ public interface ${ClassName}Mapper { public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); public int insert${ClassName}(${ClassName} ${className}); public int update${ClassName}(${ClassName} ${className}); public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); } ', 'admin', sysdate()), (910106, 9101, 'mapper.xml.vm', NULL, 'mapper.xml.vm', ' #foreach($column in $columns) #if($column.isPk == "1") #else #end #end select #foreach($column in $columns) ${column.columnName}#if($foreach.hasNext),#end #end from ${tableName} insert into ${tableName} #foreach($column in $columns) #if($column.isInsert == "1" && $column.isIncrement != "1") ${column.columnName}, #end #end #foreach($column in $columns) #if($column.isInsert == "1" && $column.isIncrement != "1") #{${column.javaField}}, #end #end update ${tableName} #foreach($column in $columns) #if($column.isEdit == "1" && $column.isPk != "1") ${column.columnName} = #{${column.javaField}}, #end #end where ${pkColumn.columnName} = #{${pkColumn.javaField}} delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} delete from ${tableName} where ${pkColumn.columnName} in #{${pkColumn.javaField}} ', 'admin', sysdate()), (910107, 9101, 'service.java.vm', NULL, 'service.java.vm', 'package ${packageName}.service; import java.util.List; import java.util.Map; import ${packageName}.domain.${ClassName}; /** * ${functionName} Service */ public interface I${ClassName}Service { public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); public int insert${ClassName}(${ClassName} ${className}); public int update${ClassName}(${ClassName} ${className}); public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) public int ${action.code}(Map params); #end #end } ', 'admin', sysdate()), (910108, 9101, 'serviceImpl.java.vm', NULL, 'serviceImpl.java.vm', 'package ${packageName}.service.impl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ruoyi.common.exception.ServiceException; import ${packageName}.domain.${ClassName}; import ${packageName}.mapper.${ClassName}Mapper; import ${packageName}.service.I${ClassName}Service; /** * ${functionName} Service implementation */ @Service public class ${ClassName}ServiceImpl implements I${ClassName}Service { @Resource private ${ClassName}Mapper ${className}Mapper; #if($hasTableBusinessActions) @Resource private JdbcTemplate jdbcTemplate; #end @Override public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) { return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); } @Override public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) { return ${className}Mapper.select${ClassName}List(${className}); } @Override public int insert${ClassName}(${ClassName} ${className}) { return ${className}Mapper.insert${ClassName}(${className}); } @Override public int update${ClassName}(${ClassName} ${className}) { return ${className}Mapper.update${ClassName}(${className}); } @Override public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) { return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); } @Override public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) { return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); } #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) /** * ${action.name} #if($action.requestFields) * Request fields: #foreach($field in $action.requestFields) * - ${field} #end #end #if($action.rules) * Rules: #foreach($rule in $action.rules) * - ${rule} #end #end #if($action.effects) * Effects: #foreach($effect in $action.effects) * - ${effect.type} ${effect.targetTable}.${effect.targetField} ${effect.value} ${effect.description} #end #end */ @Transactional(rollbackFor = Exception.class) @Override public int ${action.code}(Map params) { if (params == null) { throw new ServiceException("业务动作参数不能为空"); } #if($action.requestFields) #foreach($field in $action.requestFields) requireBusinessParam(params, "${field}"); #end #end #if($action.ruleChecks) #foreach($rule in $action.ruleChecks) #set($conditionFields = $rule.conditionFields) #if($rule.type == "EXISTS") assertExists(params, "${rule.targetTable}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}"); #elseif($rule.type == "NOT_EXISTS") assertNotExists(params, "${rule.targetTable}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}"); #elseif($rule.type == "FIELD_EQUALS") assertFieldEquals(params, "${rule.targetTable}", "${rule.targetField}", "${rule.expectedValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}"); #elseif($rule.type == "FIELD_NOT_EQUALS") assertFieldNotEquals(params, "${rule.targetTable}", "${rule.targetField}", "${rule.expectedValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}"); #elseif($rule.type == "FIELD_IN") assertFieldIn(params, "${rule.targetTable}", "${rule.targetField}", new String[] {#foreach($value in $rule.expectedValues)"${value}"#if($foreach.hasNext), #end#end}, new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}"); #elseif($rule.type == "NUMBER_GTE") assertNumberGte(params, "${rule.targetTable}", "${rule.targetField}", "${rule.compareValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}"); #end #end #end int rows = 0; #if($action.effects) #foreach($effect in $action.effects) #if($effect.type == "UPDATE_FIELD") #set($conditionFields = $effect.conditionFields) #if(!$conditionFields || $conditionFields.size() == 0) #set($conditionFields = $action.requestFields) #end rows += executeUpdateFieldEffect(params, "${effect.targetTable}", "${effect.targetField}", "${effect.value}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}); #elseif($effect.type == "INSERT_ROW") #set($effectIndex = $foreach.index) Map insertValues${effectIndex} = new java.util.LinkedHashMap(); #foreach($entry in $effect.values.entrySet()) insertValues${effectIndex}.put("${entry.key}", resolveBusinessValue(params, "${entry.value}")); #end rows += insertRow("${effect.targetTable}", insertValues${effectIndex}); #elseif($effect.type == "UPDATE_FIELDS") #set($conditionFields = $effect.conditionFields) #set($effectIndex = $foreach.index) Map updateValues${effectIndex} = new java.util.LinkedHashMap(); #foreach($entry in $effect.values.entrySet()) updateValues${effectIndex}.put("${entry.key}", resolveBusinessValue(params, "${entry.value}")); #end rows += updateFields(params, "${effect.targetTable}", updateValues${effectIndex}, new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}); #elseif($effect.type == "SET_STATUS") #set($conditionFields = $effect.conditionFields) rows += setStatus(params, "${effect.targetTable}", "${effect.targetField}", resolveBusinessValue(params, "${effect.value}"), new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}); #elseif($effect.type == "INCREASE_NUMBER") #set($conditionFields = $effect.conditionFields) rows += increaseNumberField(params, "${effect.targetTable}", "${effect.targetField}", "${effect.amount}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}); #elseif($effect.type == "DECREASE_NUMBER") #set($conditionFields = $effect.conditionFields) rows += decreaseNumberField(params, "${effect.targetTable}", "${effect.targetField}", "${effect.amount}", "${effect.minValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${effect.message}"); #else // Unsupported effect type ${effect.type}: ${effect.description} #end #end #end return rows; } #end private void requireBusinessParam(Map params, String field) { if (!params.containsKey(field) || params.get(field) == null) { throw new ServiceException("缺少业务动作参数:" + field); } } private void assertExists(Map params, String tableName, String[] conditionFields, String message) { if (countRows(params, tableName, conditionFields) <= 0) { throw new ServiceException(defaultMessage(message, "Business record does not exist")); } } private void assertNotExists(Map params, String tableName, String[] conditionFields, String message) { if (countRows(params, tableName, conditionFields) > 0) { throw new ServiceException(defaultMessage(message, "Business record already exists")); } } private void assertFieldEquals(Map params, String tableName, String targetField, String expectedValue, String[] conditionFields, String message) { Object value = querySingleField(params, tableName, targetField, conditionFields); if (value == null || !String.valueOf(resolveBusinessValue(params, expectedValue)).equals(String.valueOf(value))) { throw new ServiceException(defaultMessage(message, "Business rule check failed")); } } private void assertFieldNotEquals(Map params, String tableName, String targetField, String expectedValue, String[] conditionFields, String message) { Object value = querySingleField(params, tableName, targetField, conditionFields); if (value != null && String.valueOf(resolveBusinessValue(params, expectedValue)).equals(String.valueOf(value))) { throw new ServiceException(defaultMessage(message, "Business rule check failed")); } } private void assertFieldIn(Map params, String tableName, String targetField, String[] expectedValues, String[] conditionFields, String message) { Object value = querySingleField(params, tableName, targetField, conditionFields); String current = value == null ? "" : String.valueOf(value); for (String expectedValue : expectedValues) { if (String.valueOf(resolveBusinessValue(params, expectedValue)).equals(current)) { return; } } throw new ServiceException(defaultMessage(message, "Business rule check failed")); } private void assertNumberGte(Map params, String tableName, String targetField, String compareValue, String[] conditionFields, String message) { Object value = querySingleField(params, tableName, targetField, conditionFields); BigDecimal current = toBigDecimal(value, "Business numeric value is invalid"); BigDecimal expected = resolveBusinessNumber(params, compareValue); if (current.compareTo(expected) < 0) { throw new ServiceException(defaultMessage(message, "Business numeric rule check failed")); } } private int insertRow(String tableName, Map values) { String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid"); if (values == null || values.isEmpty()) { throw new ServiceException("Business insert values cannot be empty"); } StringBuilder fields = new StringBuilder(); StringBuilder placeholders = new StringBuilder(); List args = new ArrayList(); for (Map.Entry entry : values.entrySet()) { if (fields.length() > 0) { fields.append(", "); placeholders.append(", "); } fields.append(requireSafeIdentifier(entry.getKey(), "Business effect value field is invalid")); placeholders.append("?"); args.add(entry.getValue()); } return jdbcTemplate.update("insert into " + safeTableName + " (" + fields + ") values (" + placeholders + ")", args.toArray()); } private int updateFields(Map params, String tableName, Map values, String[] conditionFields) { String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid"); if (values == null || values.isEmpty()) { throw new ServiceException("Business update values cannot be empty"); } StringBuilder sets = new StringBuilder(); List args = new ArrayList(); for (Map.Entry entry : values.entrySet()) { if (sets.length() > 0) { sets.append(", "); } sets.append(requireSafeIdentifier(entry.getKey(), "Business effect value field is invalid")).append(" = ?"); args.add(entry.getValue()); } String whereClause = buildWhereClause(params, conditionFields); appendConditionValues(args, params, conditionFields); return jdbcTemplate.update("update " + safeTableName + " set " + sets + " where " + whereClause, args.toArray()); } private int setStatus(Map params, String tableName, String targetField, Object value, String[] conditionFields) { Map values = new java.util.LinkedHashMap(); values.put(targetField, value); return updateFields(params, tableName, values, conditionFields); } private int increaseNumberField(Map params, String tableName, String targetField, String amountExpression, String[] conditionFields) { String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid"); String safeTargetField = requireSafeIdentifier(targetField, "Business effect target field is invalid"); BigDecimal amount = resolveBusinessNumber(params, amountExpression); String whereClause = buildWhereClause(params, conditionFields); List args = new ArrayList(); args.add(amount); appendConditionValues(args, params, conditionFields); return jdbcTemplate.update("update " + safeTableName + " set " + safeTargetField + " = " + safeTargetField + " + ? where " + whereClause, args.toArray()); } private int decreaseNumberField(Map params, String tableName, String targetField, String amountExpression, String minValueExpression, String[] conditionFields, String message) { String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid"); String safeTargetField = requireSafeIdentifier(targetField, "Business effect target field is invalid"); BigDecimal amount = resolveBusinessNumber(params, amountExpression); BigDecimal minValue = resolveBusinessNumber(params, minValueExpression == null || minValueExpression.length() == 0 ? "0" : minValueExpression); String whereClause = buildWhereClause(params, conditionFields); List args = new ArrayList(); args.add(amount); appendConditionValues(args, params, conditionFields); args.add(amount); args.add(minValue); int rows = jdbcTemplate.update("update " + safeTableName + " set " + safeTargetField + " = " + safeTargetField + " - ? where " + whereClause + " and " + safeTargetField + " - ? >= ?", args.toArray()); if (rows == 0) { throw new ServiceException(defaultMessage(message, "Business numeric decrease failed")); } return rows; } private int executeUpdateFieldEffect(Map params, String tableName, String targetField, String valueExpression, String[] conditionFields) { String safeTableName = requireSafeIdentifier(tableName, "业务影响目标表不合法"); String safeTargetField = requireSafeIdentifier(targetField, "业务影响目标字段不合法"); String safeValueExpression = requireSafeExpression(valueExpression); String whereClause = buildWhereClause(params, conditionFields); Object[] values = conditionValues(params, conditionFields); String sql = "update " + safeTableName + " set " + safeTargetField + " = " + safeValueExpression + " where " + whereClause; return jdbcTemplate.update(sql, values); } private String buildWhereClause(Map params, String[] conditionFields) { if (conditionFields == null || conditionFields.length == 0) { throw new ServiceException("业务影响缺少条件字段"); } StringBuilder where = new StringBuilder(); for (String field : conditionFields) { String safeField = requireSafeIdentifier(field, "业务影响条件字段不合法"); requireBusinessParam(params, safeField); if (where.length() > 0) { where.append(" and "); } where.append(safeField).append(" = ?"); } return where.toString(); } private Object[] conditionValues(Map params, String[] conditionFields) { Object[] values = new Object[conditionFields.length]; for (int i = 0; i < conditionFields.length; i++) { values[i] = params.get(conditionFields[i]); } return values; } private int countRows(Map params, String tableName, String[] conditionFields) { String safeTableName = requireSafeIdentifier(tableName, "Business rule target table is invalid"); String whereClause = buildWhereClause(params, conditionFields); Integer count = jdbcTemplate.queryForObject("select count(1) from " + safeTableName + " where " + whereClause, conditionValues(params, conditionFields), Integer.class); return count == null ? 0 : count.intValue(); } private Object querySingleField(Map params, String tableName, String targetField, String[] conditionFields) { String safeTableName = requireSafeIdentifier(tableName, "Business rule target table is invalid"); String safeTargetField = requireSafeIdentifier(targetField, "Business rule target field is invalid"); String whereClause = buildWhereClause(params, conditionFields); List> rows = jdbcTemplate.queryForList("select " + safeTargetField + " from " + safeTableName + " where " + whereClause + " limit 1", conditionValues(params, conditionFields)); if (rows.isEmpty()) { return null; } return rows.get(0).get(safeTargetField); } private void appendConditionValues(List args, Map params, String[] conditionFields) { for (Object value : conditionValues(params, conditionFields)) { args.add(value); } } private Object resolveBusinessValue(Map params, String valueExpression) { if (valueExpression == null) { return null; } if ("#[[${now}]]#".equals(valueExpression)) { return new Date(); } if (valueExpression.startsWith("#[[${param.]]#") && valueExpression.endsWith("}")) { String field = valueExpression.substring(8, valueExpression.length() - 1); requireBusinessParam(params, field); return params.get(field); } return valueExpression; } private BigDecimal resolveBusinessNumber(Map params, String valueExpression) { return toBigDecimal(resolveBusinessValue(params, valueExpression), "Business numeric expression is invalid"); } private BigDecimal toBigDecimal(Object value, String message) { try { return new BigDecimal(String.valueOf(value)); } catch (RuntimeException e) { throw new ServiceException(message); } } private String defaultMessage(String message, String fallback) { return message == null || message.length() == 0 ? fallback : message; } private String requireSafeIdentifier(String value, String message) { if (value == null || !value.matches("[A-Za-z][A-Za-z0-9_]*")) { throw new ServiceException(message); } return value; } private String requireSafeExpression(String valueExpression) { if (valueExpression == null || !valueExpression.matches("[A-Za-z0-9_ +*/().-]{1,200}") || valueExpression.contains(";") || valueExpression.contains("--")) { throw new ServiceException("业务影响字段表达式不合法"); } return valueExpression; } #end } ', 'admin', sysdate()), (910109, 9101, 'controller.java.vm', NULL, 'controller.java.vm', 'package ${packageName}.controller; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; 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.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.poi.ExcelUtil; import ${packageName}.domain.${ClassName}; import ${packageName}.service.I${ClassName}Service; /** * ${functionName} Controller */ @RestController @RequestMapping("/${moduleName}/${businessName}") public class ${ClassName}Controller extends BaseController { @Resource private I${ClassName}Service ${className}Service; @GetMapping("/list") public TableDataInfo list(${ClassName} ${className}) { startPage(); List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); return getDataTable(list); } @PostMapping("/export") public void export(HttpServletResponse response, ${ClassName} ${className}) { List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); util.exportExcel(response, list, "${functionName}"); } @GetMapping(value = "/{${pkColumn.javaField}}") public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); } @Log(title = "${functionName}", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody ${ClassName} ${className}) { return toAjax(${className}Service.insert${ClassName}(${className})); } @Log(title = "${functionName}", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody ${ClassName} ${className}) { return toAjax(${className}Service.update${ClassName}(${className})); } @Log(title = "${functionName}", businessType = BusinessType.DELETE) @DeleteMapping("/{${pkColumn.javaField}s}") public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); } #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) @Log(title = "${action.name}", businessType = BusinessType.OTHER) #if($action.method == "GET") @GetMapping("${action.path}") public AjaxResult ${action.code}(@RequestParam Map params) { return toAjax(${className}Service.${action.code}(params)); } #elseif($action.method == "PUT") @PutMapping("${action.path}") public AjaxResult ${action.code}(@RequestBody Map params) { return toAjax(${className}Service.${action.code}(params)); } #elseif($action.method == "DELETE") @DeleteMapping("${action.path}") public AjaxResult ${action.code}(@RequestBody(required = false) Map params) { return toAjax(${className}Service.${action.code}(params)); } #else @PostMapping("${action.path}") public AjaxResult ${action.code}(@RequestBody Map params) { return toAjax(${className}Service.${action.code}(params)); } #end #end #end } ', 'admin', sysdate()); -- Admin frontend template files INSERT INTO sys_template_file (template_file_id, template_id, file_name, module_id, file_path, file_content, create_by, create_time) VALUES (910201, 9102, 'package.json.vm', NULL, 'package.json.vm', '{ "name": "${projectFileName}-admin", "version": "1.0.0", "private": true, "scripts": { "dev": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "element-ui": "2.15.14", "vue": "2.6.14", "vue-router": "3.6.5", "vuex": "3.6.2" } } ', 'admin', sysdate()), (910202, 9102, 'api.js.vm', NULL, 'api.js.vm', 'import request from "@/utils/request" const baseUrl = "/${moduleName}/${businessName}" export function list${ClassName}(query) { return request({ url: baseUrl + "/list", method: "get", params: query }) } export function get${ClassName}(${pkColumn.javaField}) { return request({ url: baseUrl + "/" + ${pkColumn.javaField}, method: "get" }) } export function add${ClassName}(data) { return request({ url: baseUrl, method: "post", data }) } export function update${ClassName}(data) { return request({ url: baseUrl, method: "put", data }) } export function del${ClassName}(${pkColumn.javaField}) { return request({ url: baseUrl + "/" + ${pkColumn.javaField}, method: "delete" }) } #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) export function ${action.code}(data) { return request({ url: baseUrl + "${action.path}", method: "${action.method.toLowerCase()}", #if($action.method == "GET") params: data #else data #end }) } #end #end ', 'admin', sysdate()), (910203, 9102, 'index.vue.vm', NULL, 'index.vue.vm', ' ', 'admin', sysdate()); INSERT INTO sys_template_file (template_file_id, template_id, file_name, module_id, file_path, file_content, create_by, create_time) VALUES (910204, 9102, 'adminMenus.js.vm', NULL, 'adminMenus.js.vm', 'export const appRoles = [ #foreach($role in $appRoles) { code: "${role.code}", name: "${role.name}", description: "${role.description}" }#if($foreach.hasNext),#end #end ] export const adminMenus = [ #foreach($menu in $adminMenus) { code: "${menu.code}", name: "${menu.name}", path: "${menu.path}", requiresLogin: ${menu.requiresLogin}, visibleRoles: [ #foreach($role in $menu.visibleRoles) "${role}"#if($foreach.hasNext),#end #end ], dataScope: "${menu.dataScope}" }#if($foreach.hasNext),#end #end ] export function requiresLogin(menu) { return Boolean(menu && menu.requiresLogin) } export function canShowMenu(menu, currentUser = {}) { if (!menu) { return false } if (menu.requiresLogin && !currentUser.id && !currentUser.userId) { return false } const userRoles = Array.isArray(currentUser.roles) ? currentUser.roles : [] if (!Array.isArray(menu.visibleRoles) || menu.visibleRoles.length === 0) { return true } return menu.visibleRoles.some(role => userRoles.includes(role)) } ', 'admin', sysdate()); INSERT INTO sys_template_file (template_file_id, template_id, file_name, module_id, file_path, file_content, create_by, create_time) VALUES (910205, 9102, 'adminPermission.js.vm', NULL, 'adminPermission.js.vm', 'import { adminMenus, canShowMenu, requiresLogin } from "./config/adminMenus" function defaultCurrentUser() { try { return JSON.parse(localStorage.getItem("currentUser") || "{}") } catch (error) { return {} } } function isLoggedIn(user) { return Boolean(user && (user.id || user.userId)) } export function installAdminGuard(router, getCurrentUser = defaultCurrentUser) { router.beforeEach((to, from, next) => { const menu = adminMenus.find(item => item.path === to.path) const user = getCurrentUser() || {} if (menu && !canShowMenu(menu, user)) { next(false) return } if (menu && requiresLogin(menu) && !isLoggedIn(user)) { next("/login") return } next() }) } ', 'admin', sysdate()); -- Portal frontend template files INSERT INTO sys_template_file (template_file_id, template_id, file_name, module_id, file_path, file_content, create_by, create_time) VALUES (910301, 9103, 'package.json.vm', NULL, 'package.json.vm', '{ "name": "${projectFileName}-portal", "version": "1.0.0", "private": true, "type": "module", "scripts": { "dev": "vite --host 0.0.0.0", "build": "vite build" }, "dependencies": { "@vitejs/plugin-vue": "5.0.5", "axios": "1.7.2", "element-plus": "2.7.6", "vite": "5.3.1", "vue": "3.4.29", "vue-router": "4.4.0" }, "devDependencies": {} } ', 'admin', sysdate()), (910302, 9103, 'index.html.vm', NULL, 'index.html.vm', ' ${projectName}
', 'admin', sysdate()), (910303, 9103, 'main.js.vm', NULL, 'main.js.vm', 'import { createApp } from "vue" import ElementPlus from "element-plus" import "element-plus/dist/index.css" import App from "./App.vue" import router from "./router" createApp(App).use(router).use(ElementPlus).mount("#app") ', 'admin', sysdate()), (910304, 9103, 'App.vue.vm', NULL, 'App.vue.vm', ' ', 'admin', sysdate()), (910305, 9103, 'router.js.vm', NULL, 'router.js.vm', 'import { createRouter, createWebHistory } from "vue-router" import { canShowMenu, frontendMenus, requiresLogin } from "../config/frontendMenus" #foreach($table in $tables) import ${table.className}Index from "../views/${table.businessName}/index.vue" import ${table.className}Detail from "../views/${table.businessName}/detail.vue" #end const routes = [ #if($hasTables) #set($firstTable = $tables.get(0)) { path: "/", redirect: "/${firstTable.businessName}" }, #else { path: "/", component: { template: "
" } } #end #foreach($table in $tables) { path: "/${table.businessName}", component: ${table.className}Index }, { path: "/${table.businessName}/:id", component: ${table.className}Detail }#if($foreach.hasNext),#end #end ] function currentUser() { try { return JSON.parse(localStorage.getItem("currentUser") || "{}") } catch (error) { return {} } } function isLoggedIn(user) { return Boolean(user && (user.id || user.userId)) } const router = createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, from, next) => { const menu = frontendMenus.find(item => item.path === to.path) const user = currentUser() if (menu && !canShowMenu(menu, user)) { next(false) return } if (menu && requiresLogin(menu) && !isLoggedIn(user)) { next("/login") return } next() }) export default router ', 'admin', sysdate()), (910306, 9103, 'request.js.vm', NULL, 'request.js.vm', 'import axios from "axios" import { ElMessage } from "element-plus" const request = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API || "", timeout: 15000 }) request.interceptors.response.use( response => response.data, error => { ElMessage.error(error.response?.data?.msg || error.message || "请求失败") return Promise.reject(error) } ) export default request ', 'admin', sysdate()), (910307, 9103, 'api.js.vm', NULL, 'api.js.vm', 'import request from "./request" const baseUrl = "/${moduleName}/${businessName}" export function list${ClassName}(query) { return request({ url: baseUrl + "/list", method: "get", params: query }) } export function get${ClassName}(${pkColumn.javaField}) { return request({ url: baseUrl + "/" + ${pkColumn.javaField}, method: "get" }) } #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) export function ${action.code}(data) { return request({ url: baseUrl + "${action.path}", method: "${action.method.toLowerCase()}", #if($action.method == "GET") params: data #else data #end }) } #end #end ', 'admin', sysdate()), (910308, 9103, 'index.vue.vm', NULL, 'index.vue.vm', ' ', 'admin', sysdate()), (910309, 9103, 'detail.vue.vm', NULL, 'detail.vue.vm', ' ', 'admin', sysdate()); INSERT INTO sys_template_file (template_file_id, template_id, file_name, module_id, file_path, file_content, create_by, create_time) VALUES (910310, 9103, 'frontendMenus.js.vm', NULL, 'frontendMenus.js.vm', 'export const appRoles = [ #foreach($role in $appRoles) { code: "${role.code}", name: "${role.name}", description: "${role.description}" }#if($foreach.hasNext),#end #end ] export const frontendMenus = [ #foreach($menu in $frontendMenus) { code: "${menu.code}", name: "${menu.name}", path: "${menu.path}", requiresLogin: ${menu.requiresLogin}, visibleRoles: [ #foreach($role in $menu.visibleRoles) "${role}"#if($foreach.hasNext),#end #end ], dataScope: "${menu.dataScope}" }#if($foreach.hasNext),#end #end ] export function requiresLogin(menu) { return Boolean(menu && menu.requiresLogin) } export function canShowMenu(menu, currentUser = {}) { if (!menu) { return false } if (menu.requiresLogin && !currentUser.id && !currentUser.userId) { return false } const userRoles = Array.isArray(currentUser.roles) ? currentUser.roles : [] if (!Array.isArray(menu.visibleRoles) || menu.visibleRoles.length === 0) { return true } return menu.visibleRoles.some(role => userRoles.includes(role)) } ', 'admin', sysdate()); -- Backend project structure INSERT INTO sys_project_structure (node_id, parent_id, node_name, node_type, module, template_id, table_id, category, sort_order, status, create_by, create_time, update_by, update_time) VALUES (910100, 0, '{projectName}-backend', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910101, 910100, 'pom.xml', 'file', NULL, 9101, -1, 'pom.xml.vm', 1, '0', 'admin', sysdate(), '', NULL), (910102, 910100, 'README.md', 'file', NULL, 9101, -1, 'README.md.vm', 2, '0', 'admin', sysdate(), '', NULL), (910103, 910100, 'src', 'folder', NULL, 9101, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (910104, 910103, 'main', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910105, 910104, 'java', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910106, 910105, '{packageName}', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910107, 910106, 'domain', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910108, 910106, 'mapper', 'folder', NULL, 9101, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (910109, 910106, 'service', 'folder', NULL, 9101, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (910110, 910109, 'impl', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910111, 910106, 'controller', 'folder', NULL, 9101, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (910112, 910104, 'resources', 'folder', NULL, 9101, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (910113, 910112, 'mapper', 'folder', NULL, 9101, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910114, 910113, '{moduleName}', 'folder', NULL, 9101, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910115, 910112, 'application.yml', 'file', NULL, 9101, -1, 'application.yml.vm', 2, '0', 'admin', sysdate(), '', NULL), (910116, 910107, '{ClassName}.java', 'file', NULL, 9101, 0, 'domain.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (910117, 910108, '{ClassName}Mapper.java', 'file', NULL, 9101, 0, 'mapper.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (910118, 910109, 'I{ClassName}Service.java', 'file', NULL, 9101, 0, 'service.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (910119, 910110, '{ClassName}ServiceImpl.java', 'file', NULL, 9101, 0, 'serviceImpl.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (910120, 910111, '{ClassName}Controller.java', 'file', NULL, 9101, 0, 'controller.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (910121, 910114, '{ClassName}Mapper.xml', 'file', NULL, 9101, 0, 'mapper.xml.vm', 1, '0', 'admin', sysdate(), '', NULL); -- Admin frontend project structure INSERT INTO sys_project_structure (node_id, parent_id, node_name, node_type, module, template_id, table_id, category, sort_order, status, create_by, create_time, update_by, update_time) VALUES (910200, 0, '{projectName}-admin', 'folder', NULL, 9102, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910201, 910200, 'package.json', 'file', NULL, 9102, -1, 'package.json.vm', 1, '0', 'admin', sysdate(), '', NULL), (910202, 910200, 'src', 'folder', NULL, 9102, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (910203, 910202, 'api', 'folder', NULL, 9102, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910204, 910203, '{moduleName}', 'folder', NULL, 9102, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910205, 910204, '{businessName}.js', 'file', NULL, 9102, 0, 'api.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (910206, 910202, 'views', 'folder', NULL, 9102, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (910207, 910206, '{moduleName}', 'folder', NULL, 9102, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910208, 910207, '{businessName}', 'folder', NULL, 9102, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910209, 910208, 'index.vue', 'file', NULL, 9102, 0, 'index.vue.vm', 1, '0', 'admin', sysdate(), '', NULL); INSERT INTO sys_project_structure (node_id, parent_id, node_name, node_type, module, template_id, table_id, category, sort_order, status, create_by, create_time, update_by, update_time) VALUES (910210, 910202, 'config', 'folder', NULL, 9102, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (910211, 910210, 'adminMenus.js', 'file', NULL, 9102, -1, 'adminMenus.js.vm', 1, '0', 'admin', sysdate(), '', NULL); INSERT INTO sys_project_structure (node_id, parent_id, node_name, node_type, module, template_id, table_id, category, sort_order, status, create_by, create_time, update_by, update_time) VALUES (910212, 910202, 'permission.js', 'file', NULL, 9102, -1, 'adminPermission.js.vm', 4, '0', 'admin', sysdate(), '', NULL); -- Portal frontend project structure INSERT INTO sys_project_structure (node_id, parent_id, node_name, node_type, module, template_id, table_id, category, sort_order, status, create_by, create_time, update_by, update_time) VALUES (910300, 0, '{projectName}-portal', 'folder', NULL, 9103, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910301, 910300, 'package.json', 'file', NULL, 9103, -1, 'package.json.vm', 1, '0', 'admin', sysdate(), '', NULL), (910302, 910300, 'index.html', 'file', NULL, 9103, -1, 'index.html.vm', 2, '0', 'admin', sysdate(), '', NULL), (910303, 910300, 'src', 'folder', NULL, 9103, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (910304, 910303, 'main.js', 'file', NULL, 9103, -1, 'main.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (910305, 910303, 'App.vue', 'file', NULL, 9103, -1, 'App.vue.vm', 2, '0', 'admin', sysdate(), '', NULL), (910306, 910303, 'router', 'folder', NULL, 9103, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (910307, 910306, 'index.js', 'file', NULL, 9103, -1, 'router.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (910308, 910303, 'api', 'folder', NULL, 9103, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (910309, 910308, 'request.js', 'file', NULL, 9103, -1, 'request.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (910310, 910308, '{businessName}.js', 'file', NULL, 9103, 0, 'api.js.vm', 2, '0', 'admin', sysdate(), '', NULL), (910311, 910303, 'views', 'folder', NULL, 9103, NULL, NULL, 5, '0', 'admin', sysdate(), '', NULL), (910312, 910311, '{businessName}', 'folder', NULL, 9103, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (910313, 910312, 'index.vue', 'file', NULL, 9103, 0, 'index.vue.vm', 1, '0', 'admin', sysdate(), '', NULL), (910314, 910312, 'detail.vue', 'file', NULL, 9103, 0, 'detail.vue.vm', 2, '0', 'admin', sysdate(), '', NULL); INSERT INTO sys_project_structure (node_id, parent_id, node_name, node_type, module, template_id, table_id, category, sort_order, status, create_by, create_time, update_by, update_time) VALUES (910315, 910303, 'config', 'folder', NULL, 9103, NULL, NULL, 6, '0', 'admin', sysdate(), '', NULL), (910316, 910315, 'frontendMenus.js', 'file', NULL, 9103, -1, 'frontendMenus.js.vm', 1, '0', 'admin', sysdate(), '', NULL); SET FOREIGN_KEY_CHECKS = 1;