2026-05-23 13:43:00 +08:00
-- 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
2026-05-23 14:12:09 +08:00
( 9101 , ' Business Blueprint Backend ' , ' /blueprint/backend ' , ' RuoYi backend with CRUD and executable blueprint effects ' , ' backend ' , 0 , ' admin ' , sysdate ( ) , ' business blueprint ' ) ,
2026-05-23 13:43:00 +08:00
( 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 ' ,
' <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns : xsi = " http://www.w3.org/2001/XMLSchema-instance "
xsi : schemaLocation = " http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd " >
< modelVersion > 4 . 0 . 0 < / modelVersion >
< groupId > $ { packageName } < / groupId >
< artifactId > $ { projectFileName } - backend < / artifactId >
< version > 1 . 0 . 0 < / version >
< name > $ { projectName } < / name >
< description > Generated backend with business blueprint actions < / description >
< parent >
< groupId > com . ruoyi < / groupId >
< artifactId > ruoyi < / artifactId >
< version > 3 . 8 . 8 < / version >
< / parent >
< dependencies >
< dependency >
< groupId > com . ruoyi < / groupId >
< artifactId > ruoyi - common < / artifactId >
< / dependency >
< dependency >
< groupId > com . ruoyi < / groupId >
< artifactId > ruoyi - framework < / artifactId >
< / dependency >
< / dependencies >
< / project >
' , ' 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 = 1 L ;
#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 ' ,
' <?xml version="1.0" encoding="UTF-8" ?>
< ! DOCTYPE mapper
PUBLIC " -//mybatis.org//DTD Mapper 3.0//EN "
" http://mybatis.org/dtd/mybatis-3-mapper.dtd " >
< mapper namespace = " ${packageName}.mapper.${ClassName}Mapper " >
< resultMap type = " ${packageName}.domain.${ClassName} " id = " ${ClassName}Result " >
#foreach($column in $columns)
#if($column.isPk == "1")
< id property = " ${column.javaField} " column = " ${column.columnName} " / >
#else
< result property = " ${column.javaField} " column = " ${column.columnName} " / >
#end
#end
< / resultMap >
< sql id = " select${ClassName}Vo " >
select
#foreach($column in $columns)
$ { column . columnName } #if($foreach.hasNext),#end
#end
from $ { tableName }
< / sql >
< select id = " select${ClassName}List " parameterType = " ${packageName}.domain.${ClassName} " resultMap = " ${ClassName}Result " >
< include refid = " select${ClassName}Vo " / >
< where >
#foreach($column in $columns)
#if($column.isQuery == "1")
< if test = " ${column.javaField} != null " > and $ { column . columnName } = #{${column.javaField}}</if>
#end
#end
< / where >
< / select >
< select id = " select${ClassName}By${pkColumn.capJavaField} " parameterType = " ${pkColumn.javaType} " resultMap = " ${ClassName}Result " >
< include refid = " select${ClassName}Vo " / >
where $ { pkColumn . columnName } = #{${pkColumn.javaField}}
< / select >
< insert id = " insert${ClassName} " parameterType = " ${packageName}.domain.${ClassName} " #if($pkColumn.increment) useGeneratedKeys="true" keyProperty="${pkColumn.javaField}"#end>
insert into $ { tableName }
< trim prefix = " ( " suffix = " ) " suffixOverrides = " , " >
#foreach($column in $columns)
#if($column.isInsert == "1" && $column.isIncrement != "1")
< if test = " ${column.javaField} != null " > $ { column . columnName } , < / if >
#end
#end
< / trim >
< trim prefix = " values ( " suffix = " ) " suffixOverrides = " , " >
#foreach($column in $columns)
#if($column.isInsert == "1" && $column.isIncrement != "1")
< if test = " ${column.javaField} != null " > #{${column.javaField}},</if>
#end
#end
< / trim >
< / insert >
< update id = " update${ClassName} " parameterType = " ${packageName}.domain.${ClassName} " >
update $ { tableName }
< trim prefix = " SET " suffixOverrides = " , " >
#foreach($column in $columns)
#if($column.isEdit == "1" && $column.isPk != "1")
< if test = " ${column.javaField} != null " > $ { column . columnName } = #{${column.javaField}},</if>
#end
#end
< / trim >
where $ { pkColumn . columnName } = #{${pkColumn.javaField}}
< / update >
< delete id = " delete${ClassName}By${pkColumn.capJavaField} " parameterType = " ${pkColumn.javaType} " >
delete from $ { tableName } where $ { pkColumn . columnName } = #{${pkColumn.javaField}}
< / delete >
< delete id = " delete${ClassName}By${pkColumn.capJavaField}s " parameterType = " String " >
delete from $ { tableName } where $ { pkColumn . columnName } in
< foreach item = " ${pkColumn.javaField} " collection = " array " open = " ( " separator = " , " close = " ) " >
#{${pkColumn.javaField}}
< / foreach >
< / delete >
< / mapper >
' , ' 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 < String , Object > params ) ;
#end
#end
}
' , ' admin ' , sysdate()),
( 910108 , 9101 , ' serviceImpl.java.vm ' , NULL , ' serviceImpl.java.vm ' ,
' package ${packageName}.service.impl;
2026-05-24 13:29:42 +08:00
import java . math . BigDecimal ;
import java . util . ArrayList ;
import java . util . Date ;
2026-05-23 13:43:00 +08:00
import java . util . List ;
import java . util . Map ;
import javax . annotation . Resource ;
2026-05-23 14:12:09 +08:00
import org . springframework . jdbc . core . JdbcTemplate ;
2026-05-23 13:43:00 +08:00
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 ;
2026-05-23 14:12:09 +08:00
#if($hasTableBusinessActions)
@ Resource
private JdbcTemplate jdbcTemplate ;
#end
2026-05-23 13:43:00 +08:00
@ 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 < String , Object > params )
{
2026-05-23 14:12:09 +08:00
if ( params = = null ) {
throw new ServiceException ( " 业务动作参数不能为空 " ) ;
}
#if($action.requestFields)
#foreach($field in $action.requestFields)
requireBusinessParam ( params , " ${field} " ) ;
#end
2026-05-24 13:29:42 +08:00
#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
2026-05-23 14:12:09 +08:00
#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});
2026-05-24 13:29:42 +08:00
#elseif($effect.type == "INSERT_ROW")
#set($effectIndex = $foreach.index)
Map < String , Object > insertValues $ { effectIndex } = new java . util . LinkedHashMap < String , Object > ( ) ;
#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 < String , Object > updateValues $ { effectIndex } = new java . util . LinkedHashMap < String , Object > ( ) ;
#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}");
2026-05-23 14:12:09 +08:00
#else
/ / Unsupported effect type $ { effect . type } : $ { effect . description }
#end
#end
#end
return rows ;
2026-05-23 13:43:00 +08:00
}
#end
2026-05-23 14:12:09 +08:00
private void requireBusinessParam ( Map < String , Object > params , String field )
{
if ( ! params . containsKey ( field ) | | params . get ( field ) = = null ) {
throw new ServiceException ( " 缺少业务动作参数: " + field ) ;
}
}
2026-05-24 13:29:42 +08:00
private void assertExists ( Map < String , Object > 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 < String , Object > 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 < String , Object > 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 < String , Object > 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 < String , Object > 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 < String , Object > 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 < String , Object > 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 < Object > args = new ArrayList < Object > ( ) ;
for ( Map . Entry < String , Object > 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 < String , Object > params , String tableName , Map < String , Object > 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 < Object > args = new ArrayList < Object > ( ) ;
for ( Map . Entry < String , Object > 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 < String , Object > params , String tableName , String targetField , Object value , String [ ] conditionFields )
{
Map < String , Object > values = new java . util . LinkedHashMap < String , Object > ( ) ;
values . put ( targetField , value ) ;
return updateFields ( params , tableName , values , conditionFields ) ;
}
private int increaseNumberField ( Map < String , Object > 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 < Object > args = new ArrayList < Object > ( ) ;
args . add ( amount ) ;
appendConditionValues ( args , params , conditionFields ) ;
return jdbcTemplate . update ( " update " + safeTableName + " set " + safeTargetField + " = " + safeTargetField + " + ? where " + whereClause , args . toArray ( ) ) ;
}
private int decreaseNumberField ( Map < String , Object > 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 < Object > args = new ArrayList < Object > ( ) ;
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 ;
}
2026-05-23 14:12:09 +08:00
private int executeUpdateFieldEffect ( Map < String , Object > 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 < String , Object > 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 < String , Object > 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 ;
}
2026-05-24 13:29:42 +08:00
private int countRows ( Map < String , Object > 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 < String , Object > 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 < Map < String , Object > > 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 < Object > args , Map < String , Object > params , String [ ] conditionFields )
{
for ( Object value : conditionValues ( params , conditionFields ) ) {
args . add ( value ) ;
}
}
private Object resolveBusinessValue ( Map < String , Object > 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 < String , Object > 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 ;
}
2026-05-23 14:12:09 +08:00
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 ;
}
2026-05-23 13:43:00 +08:00
#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 < String , Object > params )
{
return toAjax ( $ { className } Service . $ { action . code } ( params ) ) ;
}
#elseif($action.method == "PUT")
@ PutMapping ( " ${action.path} " )
public AjaxResult $ { action . code } ( @ RequestBody Map < String , Object > params )
{
return toAjax ( $ { className } Service . $ { action . code } ( params ) ) ;
}
#elseif($action.method == "DELETE")
@ DeleteMapping ( " ${action.path} " )
public AjaxResult $ { action . code } ( @ RequestBody ( required = false ) Map < String , Object > params )
{
return toAjax ( $ { className } Service . $ { action . code } ( params ) ) ;
}
#else
@ PostMapping ( " ${action.path} " )
public AjaxResult $ { action . code } ( @ RequestBody Map < String , Object > 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 ' ,
' <template>
< div class = " app-container " >
< el - form : model = " queryParams " ref = " queryForm " size = " small " : inline = " true " label - width = " 88px " >
#foreach($column in $columns)
#if($column.isQuery == "1")
< el - form - item label = " ${column.columnComment} " prop = " ${column.javaField} " >
< el - input
v - model = " queryParams.${column.javaField} "
placeholder = " 请输入${column.columnComment} "
clearable
@ keyup . enter . native = " handleQuery "
/ >
< / el - form - item >
#end
#end
< el - form - item >
< el - button type = " primary " icon = " el-icon-search " size = " mini " @ click = " handleQuery " > 搜 索 < / el - button >
< el - button icon = " el-icon-refresh " size = " mini " @ click = " resetQuery " > 重 置 < / el - button >
< / el - form - item >
< / el - form >
< el - row : gutter = " 10 " class = " mb8 " >
< el - col : span = " 1.5 " >
< el - button type = " primary " plain icon = " el-icon-plus " size = " mini " @ click = " handleAdd " > 新 增 < / el - button >
< / el - col >
< / el - row >
< el - table v - loading = " loading " : data = " rows " @ selection - change = " handleSelectionChange " >
< el - table - column type = " selection " width = " 55 " align = " center " / >
#foreach($column in $columns)
#if($column.isList == "1")
< el - table - column label = " ${column.columnComment} " align = " center " prop = " ${column.javaField} " / >
#end
#end
< el - table - column label = " 操作 " align = " center " class - name = " small-padding fixed-width " width = " 260 " >
< template slot - scope = " scope " >
< el - button size = " mini " type = " text " icon = " el-icon-edit " @ click = " handleUpdate(scope.row) " > 修 改 < / el - button >
< el - button size = " mini " type = " text " icon = " el-icon-delete " @ click = " handleDelete(scope.row) " > 删 除 < / el - button >
#if($hasTableBusinessActions)
< el - dropdown size = " mini " @ command = " command => handleBusinessAction(scope.row, command) " >
< el - button type = " text " size = " mini " > 业 务 < i class = " el-icon-arrow-down el-icon--right " > < / i > < / el - button >
< el - dropdown - menu slot = " dropdown " >
#foreach($action in $tableBusinessActions)
< el - dropdown - item command = " ${action.code} " > $ { action . name } < / el - dropdown - item >
#end
< / el - dropdown - menu >
< / el - dropdown >
#end
< / template >
< / el - table - column >
< / el - table >
< pagination
v - show = " total > 0 "
: total = " total "
: page . sync = " queryParams.pageNum "
: limit . sync = " queryParams.pageSize "
@ pagination = " getList "
/ >
< el - dialog : title = " title " : visible . sync = " open " width = " 560px " append - to - body >
< el - form ref = " form " : model = " form " label - width = " 100px " >
#foreach($column in $columns)
#if($column.isEdit == "1" && $column.isPk != "1")
< el - form - item label = " ${column.columnComment} " prop = " ${column.javaField} " >
< el - input v - model = " form.${column.javaField} " placeholder = " 请输入${column.columnComment} " / >
< / el - form - item >
#end
#end
< / el - form >
< div slot = " footer " class = " dialog-footer " >
< el - button type = " primary " @ click = " submitForm " > 确 定 < / el - button >
< el - button @ click = " cancel " > 取 消 < / el - button >
< / div >
< / el - dialog >
< / div >
< / template >
< script >
import * as $ { className } Api from " @/api/${moduleName}/${businessName} "
export default {
name : " ${ClassName} " ,
data ( ) {
return {
loading : false ,
rows : [ ] ,
ids : [ ] ,
total : 0 ,
title : " " ,
open : false ,
queryParams : {
pageNum : 1 ,
pageSize : 10
} ,
form : { } ,
businessActions : [
#if($hasTableBusinessActions)
#foreach($action in $tableBusinessActions)
{ code : " ${action.code} " , name : " ${action.name} " } #if($foreach.hasNext),#end
#end
#end
]
}
} ,
created ( ) {
this . getList ( )
} ,
methods : {
getList ( ) {
this . loading = true
$ { className } Api . list $ { ClassName } ( this . queryParams ) . then ( response = > {
this . rows = response . rows | | response . data | | [ ]
this . total = response . total | | this . rows . length
this . loading = false
} )
} ,
handleQuery ( ) {
this . queryParams . pageNum = 1
this . getList ( )
} ,
resetQuery ( ) {
this . resetForm ( " queryForm " )
this . handleQuery ( )
} ,
handleSelectionChange ( selection ) {
this . ids = selection . map ( item = > item . $ { pkColumn . javaField } )
} ,
handleAdd ( ) {
this . form = { }
this . open = true
this . title = " 新增${functionName} "
} ,
handleUpdate ( row ) {
const id = row . $ { pkColumn . javaField }
$ { className } Api . get $ { ClassName } ( id ) . then ( response = > {
this . form = response . data | | { }
this . open = true
this . title = " 修改${functionName} "
} )
} ,
submitForm ( ) {
const request = this . form . $ { pkColumn . javaField } ? $ { className } Api . update $ { ClassName } ( this . form ) : $ { className } Api . add $ { ClassName } ( this . form )
request . then ( ( ) = > {
2026-05-24 13:29:42 +08:00
this . #[[$modal]]#.msgSuccess("保存成功")
2026-05-23 13:43:00 +08:00
this . open = false
this . getList ( )
} )
} ,
handleDelete ( row ) {
const id = row . $ { pkColumn . javaField }
$ { className } Api . del $ { ClassName } ( id ) . then ( ( ) = > {
2026-05-24 13:29:42 +08:00
this . #[[$modal]]#.msgSuccess("删除成功")
2026-05-23 13:43:00 +08:00
this . getList ( )
} )
} ,
handleBusinessAction ( row , actionCode ) {
const action = this . businessActions . find ( item = > item . code = = = actionCode )
$ { className } Api [ actionCode ] ( { . . . row } ) . then ( ( ) = > {
2026-05-24 13:29:42 +08:00
this . #[[$modal]]#.msgSuccess((action ? action.name : "业务动作") + "成功")
2026-05-23 13:43:00 +08:00
this . getList ( )
} )
} ,
cancel ( ) {
this . open = false
}
}
}
< / script >
' , ' admin ' , sysdate());
2026-05-23 16:09:44 +08:00
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());
2026-05-23 13:43:00 +08:00
-- 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 ' ,
' <!doctype html>
< html lang = " zh-CN " >
< head >
< meta charset = " UTF-8 " / >
< meta name = " viewport " content = " width=device-width, initial-scale=1.0 " / >
< title > $ { projectName } < / title >
< / head >
< body >
< div id = " app " > < / div >
< script type = " module " src = " /src/main.js " > < / script >
< / body >
< / html >
' , ' 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 ' ,
' <template>
< el - container class = " shell " >
< el - header class = " topbar " >
< strong > $ { projectName } < / strong >
< el - menu mode = " horizontal " router : default - active = " $route.path " >
< el - menu - item index = " / " > 工 作 台 < / el - menu - item >
2026-05-23 16:09:44 +08:00
< el - menu - item v - for = " menu in visibleMenus " : key = " menu.code " : index = " menu.path " >
{ { menu . name } }
< / el - menu - item >
2026-05-23 13:43:00 +08:00
< / el - menu >
< / el - header >
< el - main >
< router - view / >
< / el - main >
< / el - container >
< / template >
2026-05-23 16:09:44 +08:00
< script setup >
import { computed , ref } from " vue "
import { canShowMenu , frontendMenus } from " ./config/frontendMenus "
function loadCurrentUser ( ) {
try {
return JSON . parse ( localStorage . getItem ( " currentUser " ) | | " {} " )
} catch ( error ) {
return { }
}
}
const currentUser = ref ( loadCurrentUser ( ) )
const visibleMenus = computed ( ( ) = > frontendMenus . filter ( menu = > canShowMenu ( menu , currentUser . value ) ) )
< / script >
2026-05-23 13:43:00 +08:00
< style scoped >
. shell {
min - height : 100 vh ;
background : #f6f8fb;
}
. topbar {
display : flex ;
align - items : center ;
gap : 24 px ;
background : #ffffff;
border - bottom : 1 px solid #e5e7eb;
}
< / style >
' , ' admin ' , sysdate()),
( 910305 , 9103 , ' router.js.vm ' , NULL , ' router.js.vm ' ,
' import { createRouter, createWebHistory } from "vue-router"
2026-05-23 16:09:44 +08:00
import { canShowMenu , frontendMenus , requiresLogin } from " ../config/frontendMenus "
2026-05-23 13:43:00 +08:00
#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 : " <main /> " }
}
#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
]
2026-05-23 16:09:44 +08:00
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 ( {
2026-05-23 13:43:00 +08:00
history : createWebHistory ( ) ,
routes
} )
2026-05-23 16:09:44 +08:00
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
2026-05-23 13:43:00 +08:00
' , ' 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 ' ,
' <template>
< section class = " page " >
< div class = " toolbar " >
< el - input v - model = " keyword " placeholder = " 搜索${functionName} " clearable @ keyup . enter = " loadData " / >
< el - button type = " primary " @ click = " loadData " > 搜 索 < / el - button >
< / div >
< el - table v - loading = " loading " : data = " rows " @ row - click = " openDetail " >
#foreach($column in $columns)
#if($column.isList == "1")
< el - table - column prop = " ${column.javaField} " label = " ${column.columnComment} " / >
#end
#end
< el - table - column label = " 业务 " width = " 220 " >
< template #default="{ row }">
#if($hasTableBusinessActions)
#foreach($action in $tableBusinessActions)
< el - button size = " small " @ click . stop = " runAction(row, ${foreach.index}) " > $ { action . name } < / el - button >
#end
#else
< span class = " muted " > 无 业 务 动 作 < / span >
#end
< / template >
< / el - table - column >
< / el - table >
< / section >
< / template >
< script setup >
import { onMounted , ref } from " vue "
import { useRouter } from " vue-router "
import { ElMessage } from " element-plus "
import * as api from " ../../api/${businessName} "
const router = useRouter ( )
const loading = ref ( false )
const rows = ref ( [ ] )
const keyword = ref ( " " )
const actions = [
#if($hasTableBusinessActions)
#foreach($action in $tableBusinessActions)
{ code : " ${action.code} " , name : " ${action.name} " } #if($foreach.hasNext),#end
#end
#end
]
function loadData ( ) {
loading . value = true
api . list $ { ClassName } ( { keyword : keyword . value } ) . then ( res = > {
rows . value = res . rows | | res . data | | [ ]
} ) . finally ( ( ) = > {
loading . value = false
} )
}
function openDetail ( row ) {
router . push ( " /${businessName}/ " + row . $ { pkColumn . javaField } )
}
function runAction ( row , index ) {
const action = actions [ index ]
if ( ! action | | ! api [ action . code ] ) {
return
}
api [ action . code ] ( { . . . row } ) . then ( ( ) = > {
ElMessage . success ( action . name + " 成功 " )
loadData ( )
} )
}
onMounted ( loadData )
< / script >
< style scoped >
. page {
max - width : 1180 px ;
margin : 0 auto ;
}
. toolbar {
display : flex ;
gap : 12 px ;
margin - bottom : 16 px ;
}
. muted {
color : #909399;
}
< / style >
' , ' admin ' , sysdate()),
( 910309 , 9103 , ' detail.vue.vm ' , NULL , ' detail.vue.vm ' ,
' <template>
< section class = " detail " >
< el - page - header @ back = " router.back() " content = " ${functionName}详情 " / >
< el - descriptions : column = " 2 " border >
#foreach($column in $columns)
< el - descriptions - item label = " ${column.columnComment} " > { { detail . $ { column . javaField } } } < / el - descriptions - item >
#end
< / el - descriptions >
< div class = " actions " >
#if($hasTableBusinessActions)
#foreach($action in $tableBusinessActions)
< el - button type = " primary " @ click = " runAction(${foreach.index}) " > $ { action . name } < / el - button >
#end
#end
< / div >
< / section >
< / template >
< script setup >
import { onMounted , ref } from " vue "
import { useRoute , useRouter } from " vue-router "
import { ElMessage } from " element-plus "
import * as api from " ../../api/${businessName} "
const route = useRoute ( )
const router = useRouter ( )
const detail = ref ( { } )
const actions = [
#if($hasTableBusinessActions)
#foreach($action in $tableBusinessActions)
{ code : " ${action.code} " , name : " ${action.name} " } #if($foreach.hasNext),#end
#end
#end
]
function loadDetail ( ) {
api . get $ { ClassName } ( route . params . id ) . then ( res = > {
detail . value = res . data | | res
} )
}
function runAction ( index ) {
const action = actions [ index ]
if ( ! action | | ! api [ action . code ] ) {
return
}
api [ action . code ] ( { . . . detail . value } ) . then ( ( ) = > {
ElMessage . success ( action . name + " 成功 " )
loadDetail ( )
} )
}
onMounted ( loadDetail )
< / script >
< style scoped >
. detail {
max - width : 960 px ;
margin : 0 auto ;
}
. actions {
margin - top : 16 px ;
display : flex ;
gap : 12 px ;
}
< / style >
' , ' admin ' , sysdate());
2026-05-23 16:09:44 +08:00
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());
2026-05-23 13:43:00 +08:00
-- 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 ) ;
2026-05-23 16:09:44 +08:00
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 ) ;
2026-05-23 13:43:00 +08:00
-- 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 ) ;
2026-05-23 16:09:44 +08:00
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 ) ;
2026-05-23 13:43:00 +08:00
SET FOREIGN_KEY_CHECKS = 1 ;