-- Runnable three-tier code generation templates -- Import this script after sql/db.sql. -- Template ids: 9201 backend, 9202 admin_frontend, 9203 frontend SET FOREIGN_KEY_CHECKS = 0; DELETE FROM sys_project_template WHERE template_id IN (9201, 9202, 9203); DELETE FROM sys_project_structure WHERE template_id IN (9201, 9202, 9203) OR node_id BETWEEN 920100 AND 920399; DELETE FROM sys_template_file WHERE template_id IN (9201, 9202, 9203) OR template_file_id BETWEEN 920100 AND 920399; DELETE FROM sys_template WHERE template_id IN (9201, 9202, 9203); INSERT INTO sys_template (template_id, template_name, template_path, template_desc, template_type, template_status, create_by, create_time, remark) VALUES (9201, 'Runnable Backend Template', '/runnable/backend', 'Spring Boot + MyBatis Plus runnable backend', 'backend', 0, 'admin', sysdate(), 'three-tier runnable template'), (9202, 'Runnable Admin Frontend Template', '/runnable/admin_frontend', 'Vue2 + Element UI runnable admin frontend', 'admin_frontend', 0, 'admin', sysdate(), 'three-tier runnable template'), (9203, 'Runnable Portal Frontend Template', '/runnable/frontend', 'Vue2 + Element UI runnable portal frontend', 'frontend', 0, 'admin', sysdate(), 'three-tier runnable template'); -- 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 (920101, 9201, 'pom.xml.vm', NULL, 'pom.xml.vm', ' 4.0.0 org.springframework.boot spring-boot-starter-parent 2.5.9 ${packageName} ${businessName}-service 1.0.0 ${functionName} ${functionName} generated by qing template 1.8 org.springframework.boot spring-boot-starter-web com.baomidou mybatis-plus-boot-starter 3.5.1 mysql mysql-connector-java runtime org.apache.commons commons-lang3 cn.hutool hutool-all 5.7.20 org.apache.poi poi-ooxml 4.1.2 io.swagger swagger-annotations 1.6.2 org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin ', 'admin', sysdate()), (920102, 9201, 'README.md.vm', NULL, 'README.md.vm', '# ${functionName} 这是由 qing 模板生成的单表 Spring Boot + Vue2 项目。 ## 后端启动 1. 创建 MySQL 数据库,默认库名为 `vip`。 2. 执行 `sql/${businessName}.sql` 中的建表语句。 3. 按需修改 `src/main/resources/application.yml` 里的数据库账号密码。 4. 在项目根目录运行: ```bash mvn spring-boot:run ``` The service starts at `http://localhost:8080`. ## 前端启动 ```bash cd vue npm install npm run dev ``` 前端开发服务默认把 `/api` 代理到 `http://localhost:8080`。 ', 'admin', sysdate()), (920103, 9201, 'Application.java.vm', NULL, 'Application.java.vm', 'package ${packageName}; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @MapperScan("${packageName}.mapper") @SpringBootApplication public class ${ClassName}Application { public static void main(String[] args) { SpringApplication.run(${ClassName}Application.class, args); } } ', 'admin', sysdate()), (920104, 9201, 'Result.java.vm', NULL, 'Result.java.vm', 'package ${packageName}.common; import java.io.Serializable; public class Result implements Serializable { private static final long serialVersionUID = 1L; private String code; private String msg; private T data; public Result() { } public Result(String code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public static Result success() { return new Result("200", "操作成功", null); } public static Result success(T data) { return new Result("200", "操作成功", data); } public static Result error(String msg) { return new Result("500", msg, null); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ', 'admin', sysdate()), (920105, 9201, 'MybatisPlusConfig.java.vm', NULL, 'MybatisPlusConfig.java.vm', 'package ${packageName}.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } ', 'admin', sysdate()), (920106, 9201, 'CorsConfig.java.vm', NULL, 'CorsConfig.java.vm', 'package ${packageName}.config; import ${packageName}.security.PortalAuthInterceptor; import javax.annotation.Resource; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { @Resource private PortalAuthInterceptor portalAuthInterceptor; @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(portalAuthInterceptor) .addPathPatterns("/**") .excludePathPatterns("/auth/login", "/auth/admin/login", "/business-blocks/**", "/files/**", "/error"); } } ', 'admin', sysdate()), (920107, 9201, 'FileController.java.vm', NULL, 'FileController.java.vm', 'package ${packageName}.controller; import ${packageName}.common.Result; import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; 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.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/files") public class FileController { private final Path uploadRoot = Paths.get("uploads"); @PostMapping("/upload") public Result> upload(@RequestParam("file") MultipartFile file) throws IOException { Files.createDirectories(uploadRoot); String originalName = StringUtils.cleanPath(file.getOriginalFilename() == null ? "file" : file.getOriginalFilename()); String suffix = ""; int dotIndex = originalName.lastIndexOf(''.''); if (dotIndex >= 0) { suffix = originalName.substring(dotIndex); } String fileName = UUID.randomUUID().toString().replace("-", "") + suffix; Files.copy(file.getInputStream(), uploadRoot.resolve(fileName), StandardCopyOption.REPLACE_EXISTING); Map data = new HashMap(); data.put("name", originalName); data.put("url", "/files/" + fileName); return Result.success(data); } @GetMapping("/{fileName:.+}") public ResponseEntity getFile(@PathVariable String fileName) throws MalformedURLException { Path filePath = uploadRoot.resolve(fileName).normalize(); Resource resource = new UrlResource(filePath.toUri()); if (!resource.exists()) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\\"" + resource.getFilename() + "\\"") .body(resource); } } ', 'admin', sysdate()), (920108, 9201, 'application.yml.vm', NULL, 'application.yml.vm', '#set($d = ''$'') server: port: ${d}{SERVER_PORT:8080} spring: application: name: ${businessName}-service datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: ${d}{DB_URL:jdbc:mysql://localhost:3306/vip?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8} username: ${d}{DB_USERNAME:root} password: ${d}{DB_PASSWORD:123456} servlet: multipart: max-file-size: 100MB max-request-size: 100MB mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ', 'admin', sysdate()), (920109, 9201, 'entity.java.vm', NULL, 'entity.java.vm', 'package ${packageName}.entity; #foreach ($import in $importList) import ${import}; #end import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableField; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import cn.hutool.core.annotation.Alias; /** * ${functionName}对象 ${tableName} * * @author ${author} * @date ${datetime} */ #if($table.crud || $table.sub) #set($Entity="BaseEntity") #elseif($table.tree) #set($Entity="TreeEntity") #end @Getter @Setter @Accessors(chain = true) @ApiModel(value = "${ClassName}对象", description = "${functionName}") @TableName("${tableName}") public class ${ClassName} { private static final long serialVersionUID = 1L; #foreach ($column in $columns) /** $column.columnComment */ #if($column.list) #set($parentheseIndex=$column.columnComment.indexOf("(")) #if($parentheseIndex != -1) #set($comment=$column.columnComment.substring(0, $parentheseIndex)) #else #set($comment=$column.columnComment) #end #if($column.javaType == ''Date'') @JsonFormat(pattern = "yyyy-MM-dd") #else #end #end @ApiModelProperty("${column.columnComment}") @Alias("${column.columnComment}") #if ($column.isPk == "1") @TableId(value = "${column.columnName}", type = IdType.AUTO) #else @TableField("${column.columnName}") #end private $column.javaType $column.javaField; #end #if($table.sub) /** $table.subTable.functionName信息 */ @ApiModelProperty("${table.subTable.functionName}信息") private List<${subClassName}> ${subclassName}List; #end @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) #foreach ($column in $columns) #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) #set($AttrName=$column.javaField) #else #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) #end .append("${column.javaField}", get${AttrName}()) #end #if($table.sub) .append("${subclassName}List", get${subClassName}List()) #end .toString(); } } ', 'admin', sysdate()), (920110, 9201, 'mapper.java.vm', NULL, 'mapper.java.vm', 'package ${packageName}.mapper; import ${packageName}.entity.${ClassName}; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * ${functionName}Mapper接口 * * @author ${author} * @date ${datetime} */ public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> { } ', 'admin', sysdate()), (920111, 9201, 'service.java.vm', NULL, 'service.java.vm', 'package ${packageName}.service; import ${packageName}.entity.${ClassName}; import com.baomidou.mybatisplus.extension.service.IService; #if($hasTableBusinessActions) import java.util.Map; #end /** * ${functionName}Service接口 * * @author ${author} * @date ${datetime} */ public interface I${ClassName}Service extends IService<${ClassName}> { #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) int ${action.code}(Map params); #end #end } ', 'admin', sysdate()), (920112, 9201, 'serviceImpl.java.vm', NULL, 'serviceImpl.java.vm', 'package ${packageName}.service.impl; import ${packageName}.entity.${ClassName}; import ${packageName}.mapper.${ClassName}Mapper; import ${packageName}.service.I${ClassName}Service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; #if($hasTableBusinessActions) 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.transaction.annotation.Transactional; #end import org.springframework.stereotype.Service; /** * ${functionName}Service业务层处理 * * @author ${author} * @date ${datetime} */ @Service public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { #if($hasTableBusinessActions) // STRICT_TARGET_TABLE_CONDITIONS @Resource private JdbcTemplate jdbcTemplate; #foreach($action in $tableBusinessActions) @Override @Transactional(rollbackFor = Exception.class) public int ${action.code}(Map params) { if (params == null) { throw new IllegalArgumentException("业务动作参数不能为空"); } #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) 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}"); #end #end #end return rows; } #end private void requireBusinessParam(Map params, String field) { if (!params.containsKey(field) || params.get(field) == null || String.valueOf(params.get(field)).trim().isEmpty()) { throw new IllegalArgumentException("缺少业务动作参数:" + field); } } private void assertExists(Map params, String tableName, String[] conditionFields, String message) { if (countRows(params, tableName, conditionFields) <= 0) { throw new IllegalArgumentException(defaultMessage(message, "业务记录不存在")); } } private void assertNotExists(Map params, String tableName, String[] conditionFields, String message) { if (countRows(params, tableName, conditionFields) > 0) { throw new IllegalArgumentException(defaultMessage(message, "业务记录已存在")); } } 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 IllegalArgumentException(defaultMessage(message, "业务规则校验失败")); } } 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 IllegalArgumentException(defaultMessage(message, "业务规则校验失败")); } } 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 IllegalArgumentException(defaultMessage(message, "业务规则校验失败")); } 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, "业务数值无效"); BigDecimal expected = resolveBusinessNumber(params, compareValue); if (current.compareTo(expected) < 0) { throw new IllegalArgumentException(defaultMessage(message, "业务数值校验失败")); } } private int insertRow(String tableName, Map values) { String safeTableName = requireSafeIdentifier(tableName, "业务动作目标表无效"); if (values == null || values.isEmpty()) { throw new IllegalArgumentException("业务新增字段不能为空"); } 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(), "业务新增字段无效")); 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, "业务动作目标表无效"); if (values == null || values.isEmpty()) { throw new IllegalArgumentException("业务更新字段不能为空"); } 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(), "业务更新字段无效")).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, "业务动作目标表无效"); String safeTargetField = requireSafeIdentifier(targetField, "业务动作目标字段无效"); 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, "业务动作目标表无效"); String safeTargetField = requireSafeIdentifier(targetField, "业务动作目标字段无效"); 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 IllegalArgumentException(defaultMessage(message, "业务数值扣减失败")); } 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); return jdbcTemplate.update("update " + safeTableName + " set " + safeTargetField + " = " + safeValueExpression + " where " + whereClause, conditionValues(params, conditionFields)); } private String buildWhereClause(Map params, String[] conditionFields) { if (conditionFields == null || conditionFields.length == 0) { throw new IllegalArgumentException("业务动作条件字段不能为空"); } 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, "业务规则目标表无效"); 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, "业务规则目标表无效"); String safeTargetField = requireSafeIdentifier(targetField, "业务规则目标字段无效"); String whereClause = buildWhereClause(params, conditionFields); List> rows = jdbcTemplate.queryForList("select " + safeTargetField + " from " + safeTableName + " where " + whereClause + " limit 1", conditionValues(params, conditionFields)); return rows.isEmpty() ? null : 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 ("#[[${current_user.id}]]#".equals(valueExpression) || "#[[${current_user_id}]]#".equals(valueExpression)) { requireBusinessParam(params, "__currentUserId"); return params.get("__currentUserId"); } 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), "业务数值表达式无效"); } private BigDecimal toBigDecimal(Object value, String message) { try { return new BigDecimal(String.valueOf(value)); } catch (RuntimeException e) { throw new IllegalArgumentException(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 IllegalArgumentException(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 IllegalArgumentException("业务动作表达式无效"); } return valueExpression; } #end } ', 'admin', sysdate()), (920113, 9201, 'controller.java.vm', NULL, 'controller.java.vm', 'package ${packageName}.controller; import cn.hutool.poi.excel.ExcelReader; import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelWriter; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ${packageName}.common.Result; import ${packageName}.entity.${ClassName}; #if($hasQingCurrentUserColumn || $hasTableBusinessActions) import ${packageName}.entity.PortalUser; import ${packageName}.security.PortalAuthTokenStore; #end import ${packageName}.service.I${ClassName}Service; import java.io.InputStream; import java.net.URLEncoder; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; #if($hasQingCurrentUserColumn || $hasTableBusinessActions) import javax.servlet.http.HttpServletRequest; #end import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; 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.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; /** * ${functionName}Controller * * @author ${author} * @date ${datetime} */ @RestController public class ${ClassName}Controller { @Resource private I${ClassName}Service ${className}Service; #if($hasQingCurrentUserColumn || $hasTableBusinessActions) @Resource private PortalAuthTokenStore tokenStore; #end #if($allowAdd == ''1'' || $allowEdit == ''1'') @PostMapping("/${moduleName}/${businessName}") public Result save(@RequestBody ${ClassName} ${className}) { #if($allowAdd == ''1'' && $allowEdit == ''1'') ${className}Service.saveOrUpdate(${className}); #elseif($allowAdd == ''1'') ${className}Service.save(${className}); #else ${className}Service.updateById(${className}); #end return Result.success(); } #end #if($allowRemove == ''1'') @DeleteMapping("/${moduleName}/${businessName}/{${pkColumn.javaField}}") public Result delete(@PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { ${className}Service.removeById(${pkColumn.javaField}); return Result.success(); } @PostMapping("/${moduleName}/${businessName}/del/batch") public Result deleteBatch(@RequestBody List<${pkColumn.javaType}> ${pkColumn.javaField}s) { ${className}Service.removeByIds(${pkColumn.javaField}s); return Result.success(); } #end @GetMapping("/${moduleName}/${businessName}") public Result> findAll() { return Result.success(${className}Service.list()); } @GetMapping("/${moduleName}/${businessName}/{${pkColumn.javaField}}") public Result<${ClassName}> findOne(@PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { return Result.success(${className}Service.getById(${pkColumn.javaField})); } @GetMapping("/${moduleName}/${businessName}/page") public Result> findPage(@RequestParam(required = false, defaultValue = "1") Integer pageNum, @RequestParam(required = false, defaultValue = "10") Integer pageSize, @RequestParam(required = false) Map params#if($hasQingCurrentUserColumn), HttpServletRequest request#end) { QueryWrapper<${ClassName}> queryWrapper = new QueryWrapper<${ClassName}>(); #if($hasQingCurrentUserColumn) if (isCurrentUserOnly(params)) { PortalUser currentUser = tokenStore.getPortalUser(resolveToken(request)); if (currentUser == null) { return Result.success(new Page<${ClassName}>(pageNum, pageSize)); } queryWrapper.eq("${qingCurrentUserColumn.columnName}", currentUser.getUserId()); } #end #foreach ($column in $columns) #if($column.isQuery == "1") #set($queryType=$column.queryType) #set($javaField=$column.javaField) #set($columnName=$column.columnName) #if($queryType == "EQ") queryWrapper.eq(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #elseif($queryType == "NE") queryWrapper.ne(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #elseif($queryType == "GT") queryWrapper.gt(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #elseif($queryType == "GTE") queryWrapper.ge(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #elseif($queryType == "LT") queryWrapper.lt(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #elseif($queryType == "LTE") queryWrapper.le(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #elseif($queryType == "LIKE") queryWrapper.like(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #else queryWrapper.eq(hasValue(params.get("${javaField}")), "${columnName}", params.get("${javaField}")); #end #end #end queryWrapper.orderByDesc("${pkColumn.columnName}"); return Result.success(${className}Service.page(new Page<${ClassName}>(pageNum, pageSize), queryWrapper)); } @GetMapping("/${moduleName}/${businessName}/export") public void export(HttpServletResponse response) throws Exception { List<${ClassName}> list = ${className}Service.list(); ExcelWriter writer = ExcelUtil.getWriter(true); writer.write(list, true); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); String fileName = URLEncoder.encode("${functionName}信息表", "UTF-8"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); ServletOutputStream out = response.getOutputStream(); writer.flush(out, true); out.close(); writer.close(); } #if($allowAdd == ''1'') @PostMapping("/${moduleName}/${businessName}/import") public Result imp(MultipartFile file) throws Exception { InputStream inputStream = file.getInputStream(); ExcelReader reader = ExcelUtil.getReader(inputStream); List<${ClassName}> list = reader.readAll(${ClassName}.class); ${className}Service.saveBatch(list); return Result.success(); } #end #if($hasTableBusinessActions) #foreach($action in $tableBusinessActions) #if($action.method == "GET") @GetMapping("${action.path}") public Result ${action.code}(@RequestParam(required = false) Map params, HttpServletRequest request) #elseif($action.method == "PUT") @org.springframework.web.bind.annotation.PutMapping("${action.path}") public Result ${action.code}(@RequestBody(required = false) Map params, HttpServletRequest request) #elseif($action.method == "DELETE") @DeleteMapping("${action.path}") public Result ${action.code}(@RequestBody(required = false) Map params, HttpServletRequest request) #else @PostMapping("${action.path}") public Result ${action.code}(@RequestBody(required = false) Map params, HttpServletRequest request) #end { try { Map actionParams = prepareBusinessActionParams(params, request, new String[] {#foreach($field in $action.currentUserFields)"${field}"#if($foreach.hasNext), #end#end}); return Result.success(${className}Service.${action.code}(actionParams)); } catch (RuntimeException e) { return Result.error(e.getMessage()); } } #end private Map prepareBusinessActionParams(Map params, HttpServletRequest request, String[] currentUserFields) { Map actionParams = params == null ? new LinkedHashMap() : new LinkedHashMap(params); PortalUser currentUser = tokenStore.getPortalUser(resolveToken(request)); if (currentUser == null) { throw new IllegalArgumentException("请先登录"); } actionParams.put("__currentUserId", currentUser.getUserId()); if (currentUserFields != null) { for (String field : currentUserFields) { if (StringUtils.isNotBlank(field)) { if ("username".equals(field) || "user_name".equals(field)) { actionParams.put(field, currentUser.getUsername()); } else { actionParams.put(field, currentUser.getUserId()); } } } } return actionParams; } #end private boolean hasValue(Object value) { return value != null && StringUtils.isNotBlank(String.valueOf(value)); } #if($hasQingCurrentUserColumn) private boolean isCurrentUserOnly(Map params) { return params != null && "true".equalsIgnoreCase(String.valueOf(params.get("__currentUserOnly"))); } #end #if($hasQingCurrentUserColumn || $hasTableBusinessActions) private String resolveToken(HttpServletRequest request) { String authorization = request.getHeader("Authorization"); if (authorization != null && authorization.startsWith("Bearer ")) { return authorization.substring("Bearer ".length()); } return request.getHeader("X-Portal-Token"); } #end } ', 'admin', sysdate()), (920114, 9201, 'mapper.xml.vm', NULL, 'mapper.xml.vm', ' #foreach ($column in $columns) #if ($column.isPk == "1") #else #end #end #foreach ($column in $columns) ${column.columnName}#if($foreach.count != $columns.size()),#end #end ', 'admin', sysdate()), (920115, 9201, 'sql.vm', NULL, 'sql.vm', '-- ${functionName} table -- Generated by qing template at ${datetime} ${table.createTableSql} create table if not exists t_admin ( admin_id bigint not null auto_increment comment ''Admin ID'', username varchar(64) not null comment ''Username'', password_hash varchar(64) not null comment ''SHA-256 password hash'', nickname varchar(64) default null comment ''Nickname'', status char(1) default ''0'' comment ''Status, 0 normal 1 disabled'', create_time datetime default current_timestamp comment ''Create time'', primary key (admin_id), unique key uk_t_admin_username (username) ) engine=innodb default charset=utf8mb4 comment=''Admin user table''; insert into t_admin (username, password_hash, nickname, status) select ''admin'', ''8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'', ''Administrator'', ''0'' where not exists (select 1 from t_admin where username = ''admin''); create table if not exists t_user ( user_id bigint not null auto_increment comment ''User ID'', username varchar(64) not null comment ''Username'', password_hash varchar(64) not null comment ''SHA-256 password hash'', nickname varchar(64) default null comment ''Nickname'', status char(1) default ''0'' comment ''Status, 0 normal 1 disabled'', create_time datetime default current_timestamp comment ''Create time'', primary key (user_id), unique key uk_t_user_username (username) ) engine=innodb default charset=utf8mb4 comment=''Portal user table''; insert into t_user (username, password_hash, nickname, status) select ''user'', ''8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'', ''User'', ''0'' where not exists (select 1 from t_user where username = ''user''); ', 'admin', sysdate()), (920116, 9201, 'PortalUser.java.vm', NULL, 'PortalUser.java.vm', 'package ${packageName}.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @Getter @Setter @Accessors(chain = true) @TableName("t_user") public class PortalUser { @TableId(value = "user_id", type = IdType.AUTO) private Long userId; private String username; private String passwordHash; private String nickname; private String status; } ', 'admin', sysdate()), (920117, 9201, 'PortalUserMapper.java.vm', NULL, 'PortalUserMapper.java.vm', 'package ${packageName}.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import ${packageName}.entity.PortalUser; public interface PortalUserMapper extends BaseMapper { } ', 'admin', sysdate()), (920118, 9201, 'PortalAuthTokenStore.java.vm', NULL, 'PortalAuthTokenStore.java.vm', 'package ${packageName}.security; import ${packageName}.entity.AdminUser; import ${packageName}.entity.PortalUser; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.springframework.stereotype.Component; @Component public class PortalAuthTokenStore { private final Map sessions = new ConcurrentHashMap(); public String issue(Object user) { String token = UUID.randomUUID().toString().replace("-", ""); sessions.put(token, user); return token; } public Object getUser(String token) { if (token == null || token.trim().isEmpty()) { return null; } return sessions.get(token); } public PortalUser getPortalUser(String token) { Object user = getUser(token); return user instanceof PortalUser ? (PortalUser) user : null; } public AdminUser getAdminUser(String token) { Object user = getUser(token); return user instanceof AdminUser ? (AdminUser) user : null; } public boolean validate(String token) { return getUser(token) != null; } public void revoke(String token) { if (token != null) { sessions.remove(token); } } } ', 'admin', sysdate()), (920119, 9201, 'PortalAuthInterceptor.java.vm', NULL, 'PortalAuthInterceptor.java.vm', 'package ${packageName}.security; import java.io.IOException; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @Component public class PortalAuthInterceptor implements HandlerInterceptor { @Resource private PortalAuthTokenStore tokenStore; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } String token = resolveToken(request); if (tokenStore.validate(token)) { return true; } response.setStatus(401); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\\"code\\":\\"401\\",\\"msg\\":\\"请先登录\\",\\"data\\":null}"); return false; } private String resolveToken(HttpServletRequest request) { String authorization = request.getHeader("Authorization"); if (authorization != null && authorization.startsWith("Bearer ")) { return authorization.substring("Bearer ".length()); } return request.getHeader("X-Portal-Token"); } } ', 'admin', sysdate()), (920120, 9201, 'PortalAuthController.java.vm', NULL, 'PortalAuthController.java.vm', 'package ${packageName}.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import ${packageName}.common.Result; import ${packageName}.entity.PortalUser; import ${packageName}.mapper.PortalUserMapper; import ${packageName}.security.PortalAuthTokenStore; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/auth") public class PortalAuthController { @Resource private PortalUserMapper portalUserMapper; @Resource private PortalAuthTokenStore tokenStore; @PostMapping("/login") public Result> login(@RequestBody LoginBody body) { if (body == null || isBlank(body.getUsername()) || isBlank(body.getPassword())) { return Result.error("请输入用户名和密码"); } PortalUser user = portalUserMapper.selectOne(new QueryWrapper() .eq("username", body.getUsername()) .eq("status", "0") .last("limit 1")); if (user == null || !passwordHash(body.getPassword()).equalsIgnoreCase(user.getPasswordHash())) { return Result.error("账号或密码错误"); } String token = tokenStore.issue(user); user.setPasswordHash(null); Map data = new HashMap(); data.put("token", token); data.put("user", user); return Result.success(data); } @GetMapping("/me") public Result me(HttpServletRequest request) { PortalUser user = tokenStore.getPortalUser(resolveToken(request)); if (user == null) { return Result.error("请先登录"); } user.setPasswordHash(null); return Result.success(user); } @PostMapping("/logout") public Result logout(HttpServletRequest request) { tokenStore.revoke(resolveToken(request)); return Result.success(); } private String resolveToken(HttpServletRequest request) { String authorization = request.getHeader("Authorization"); if (authorization != null && authorization.startsWith("Bearer ")) { return authorization.substring("Bearer ".length()); } return request.getHeader("X-Portal-Token"); } private String passwordHash(String password) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8)); StringBuilder builder = new StringBuilder(); for (byte item : hash) { builder.append(String.format("%02x", item)); } return builder.toString(); } catch (Exception e) { throw new IllegalStateException("Password hash failed", e); } } private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } public static class LoginBody { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } } ', 'admin', sysdate()), (920121, 9201, 'AdminUser.java.vm', NULL, 'AdminUser.java.vm', 'package ${packageName}.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @Getter @Setter @Accessors(chain = true) @TableName("t_admin") public class AdminUser { @TableId(value = "admin_id", type = IdType.AUTO) private Long adminId; private String username; private String passwordHash; private String nickname; private String status; } ', 'admin', sysdate()), (920122, 9201, 'AdminUserMapper.java.vm', NULL, 'AdminUserMapper.java.vm', 'package ${packageName}.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import ${packageName}.entity.AdminUser; public interface AdminUserMapper extends BaseMapper { } ', 'admin', sysdate()), (920123, 9201, 'AdminAuthController.java.vm', NULL, 'AdminAuthController.java.vm', 'package ${packageName}.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import ${packageName}.common.Result; import ${packageName}.entity.AdminUser; import ${packageName}.mapper.AdminUserMapper; import ${packageName}.security.PortalAuthTokenStore; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/auth/admin") public class AdminAuthController { @Resource private AdminUserMapper adminUserMapper; @Resource private PortalAuthTokenStore tokenStore; @PostMapping("/login") public Result> login(@RequestBody LoginBody body) { if (body == null || isBlank(body.getUsername()) || isBlank(body.getPassword())) { return Result.error("请输入用户名和密码"); } AdminUser user = adminUserMapper.selectOne(new QueryWrapper() .eq("username", body.getUsername()) .eq("status", "0") .last("limit 1")); if (user == null || !passwordHash(body.getPassword()).equalsIgnoreCase(user.getPasswordHash())) { return Result.error("账号或密码错误"); } String token = tokenStore.issue(user); user.setPasswordHash(null); Map data = new HashMap(); data.put("token", token); data.put("user", user); return Result.success(data); } @GetMapping("/me") public Result me(HttpServletRequest request) { AdminUser user = tokenStore.getAdminUser(resolveToken(request)); if (user == null) { return Result.error("请先登录"); } user.setPasswordHash(null); return Result.success(user); } @PostMapping("/logout") public Result logout(HttpServletRequest request) { tokenStore.revoke(resolveToken(request)); return Result.success(); } private String resolveToken(HttpServletRequest request) { String authorization = request.getHeader("Authorization"); if (authorization != null && authorization.startsWith("Bearer ")) { return authorization.substring("Bearer ".length()); } return request.getHeader("X-Admin-Token"); } private String passwordHash(String password) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8)); StringBuilder builder = new StringBuilder(); for (byte item : hash) { builder.append(String.format("%02x", item)); } return builder.toString(); } catch (Exception e) { throw new IllegalStateException("Password hash failed", e); } } private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } public static class LoginBody { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } } ', '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 (920201, 9202, 'package.json.vm', NULL, 'package.json.vm', '{ "name": "${projectFileName}-admin", "version": "1.0.0", "private": true, "scripts": { "dev": "vue-cli-service serve --host 0.0.0.0", "serve": "vue-cli-service serve --host 0.0.0.0", "build": "vue-cli-service build" }, "dependencies": { "axios": "^0.27.2", "core-js": "^3.8.3", "echarts": "5.4.0", "element-ui": "^2.15.14", "vue": "^2.6.14", "vue-router": "^3.6.5" }, "devDependencies": { "@vue/cli-service": "^4.5.19", "vue-template-compiler": "^2.6.14" } }', 'admin', sysdate()), (920202, 9202, 'babel.config.js.vm', NULL, 'babel.config.js.vm', 'module.exports = { presets: [ "@vue/cli-plugin-babel/preset" ] } ', 'admin', sysdate()), (920203, 9202, 'vue.config.js.vm', NULL, 'vue.config.js.vm', 'const port = process.env.PORT || 8081 const apiBaseUrl = process.env.VUE_APP_API_BASE_URL || "http://localhost:8080" module.exports = { transpileDependencies: [], devServer: { port, proxy: { "/api": { target: apiBaseUrl, changeOrigin: true, pathRewrite: { "^/api": "" } } } } } ', 'admin', sysdate()), (920204, 9202, 'index.html.vm', NULL, 'index.html.vm', ' ${projectName} Admin
', 'admin', sysdate()), (920205, 9202, 'admin-main.js.vm', NULL, 'admin-main.js.vm', 'import Vue from "vue" import ElementUI from "element-ui" import "element-ui/lib/theme-chalk/index.css" import App from "./App.vue" import router from "./router" import request from "./utils/request" import dict from "./utils/dict" Vue.config.productionTip = false Vue.use(ElementUI) Vue.prototype.request = request Vue.prototype._dict = dict new Vue({ router, render: h => h(App) }).#[[$mount]]#("#app")', 'admin', sysdate()), (920206, 9202, 'admin-App.vue.vm', NULL, 'admin-App.vue.vm', ' ', 'admin', sysdate()), (920207, 9202, 'admin-router.js.vm', NULL, 'admin-router.js.vm', 'import Vue from "vue" import Router from "vue-router" import Login from "@/views/login/index.vue" #if($hasAdminRouteItems) #foreach($component in $adminRouteComponents) import ${component.componentName} from "${component.componentPath}" #end #end Vue.use(Router) #if($hasAdminRouteItems) const firstPath = "${defaultAdminPath}" #else const firstPath = "/empty" const Empty = { template: "
暂无可管理数据
" } #end const routes = [ { path: "/login", name: "Login", component: Login, meta: { title: "登录" } }, { path: "/", redirect: firstPath }#if($hasAdminRouteItems)#else, { path: "/empty", name: "Empty", component: Empty, meta: { title: "系统首页" } }#end #foreach($item in $adminRouteItems), { path: "${item.path}", name: "${item.code}", component: ${item.componentName}, meta: { title: "${item.title}" } } #end ] const router = new Router({ mode: "hash", routes }) router.beforeEach((to, from, next) => { const hasToken = Boolean(window.localStorage.getItem("admin-token")) if (to.path !== "/login" && !hasToken) { next({ path: "/login", query: { redirect: to.fullPath } }) return } if (to.path === "/login" && hasToken) { next(to.query.redirect || firstPath) return } next() }) export default router', 'admin', sysdate()), (920211, 9202, 'admin-login.vue.vm', NULL, 'admin-login.vue.vm', ' ', 'admin', sysdate()), (920208, 9202, 'request.js.vm', NULL, 'request.js.vm', 'import axios from "axios" import { Message } from "element-ui" const request = axios.create({ baseURL: "/api", timeout: 30000 }) request.interceptors.request.use(config => { const token = window.localStorage.getItem("admin-token") if (token) { config.headers.Authorization = "Bearer " + token } return config }) request.interceptors.response.use( response => { const data = response.data if (data && data.code === "401") { clearAdminSession() Message.error(data.msg || "请先登录") return Promise.reject(new Error(data.msg || "Unauthorized")) } return data }, error => { if (error && error.response && error.response.status === 401) { clearAdminSession() Message.error("请先登录") } else { Message.error(error && error.message ? error.message : "请求失败") } return Promise.reject(error) } ) function clearAdminSession() { window.localStorage.removeItem("admin-token") window.localStorage.removeItem("admin-user") if (window.location.hash.indexOf("#/login") !== 0) { window.location.hash = "#/login" } } export default request ', 'admin', sysdate()), (920209, 9202, 'dict.js.vm', NULL, 'dict.js.vm', 'const emptyDict = [] export default { getDictDataByType() { return emptyDict }, getDictLabel(type, value) { return value === undefined || value === null || value === "" ? "-" : value } } ', 'admin', sysdate()), (920210, 9202, 'admin-index.vue.vm', NULL, 'admin-index.vue.vm', ' ', '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 (920301, 9203, 'package.json.vm', NULL, 'package.json.vm', '{ "name": "${projectFileName}-web", "version": "1.0.0", "private": true, "scripts": { "dev": "vue-cli-service serve --host 0.0.0.0", "serve": "vue-cli-service serve --host 0.0.0.0", "build": "vue-cli-service build" }, "dependencies": { "axios": "^0.27.2", "core-js": "^3.8.3", "echarts": "5.4.0", "element-ui": "^2.15.14", "vue": "^2.6.14", "vue-router": "^3.6.5" }, "devDependencies": { "@vue/cli-service": "^4.5.19", "vue-template-compiler": "^2.6.14" } } ', 'admin', sysdate()), (920302, 9203, 'babel.config.js.vm', NULL, 'babel.config.js.vm', 'module.exports = { presets: [ "@vue/cli-plugin-babel/preset" ] } ', 'admin', sysdate()), (920303, 9203, 'vue.config.js.vm', NULL, 'vue.config.js.vm', 'const port = process.env.PORT || 8082 const apiBaseUrl = process.env.VUE_APP_API_BASE_URL || "http://localhost:8080" module.exports = { transpileDependencies: [], devServer: { port, proxy: { "/api": { target: apiBaseUrl, changeOrigin: true, pathRewrite: { "^/api": "" } } } } } ', 'admin', sysdate()), (920304, 9203, 'index.html.vm', NULL, 'index.html.vm', ' ${functionName}
', 'admin', sysdate()), (920305, 9203, 'frontend-main.js.vm', NULL, 'frontend-main.js.vm', 'import Vue from "vue" import ElementUI from "element-ui" import "element-ui/lib/theme-chalk/index.css" import App from "./App.vue" import router from "./router" import request from "./api/request" import dict from "./utils/dict" import "./style.css" Vue.config.productionTip = false Vue.use(ElementUI) Vue.prototype.request = request Vue.prototype._dict = dict new Vue({ router, render: h => h(App) }).#[[$mount]]#("#app")', 'admin', sysdate()), (920306, 9203, 'frontend-App.vue.vm', NULL, 'frontend-App.vue.vm', '#set($portalProjectName = $projectName) #if(!$portalProjectName || $portalProjectName == "") #set($portalProjectName = $functionName) #end #if(!$portalProjectName || $portalProjectName == "") #set($portalProjectName = "Portal") #end #set($portalHomePath = $defaultFrontendPath) #if(!$portalHomePath || $portalHomePath == "") #set($portalHomePath = "/${moduleName}/${businessName}") #end ', 'admin', sysdate()), (920307, 9203, 'style.css.vm', NULL, 'style.css.vm', 'body { margin: 0; background: #f6f7fb; color: #1f2937; font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; } .el-button { border-radius: 4px; }', 'admin', sysdate()), (920308, 9203, 'frontend-router.js.vm', NULL, 'frontend-router.js.vm', 'import Vue from "vue" import Router from "vue-router" import Login from "@/views/login/index.vue" #set($portalDefaultPath = $defaultFrontendPath) #if(!$portalDefaultPath || $portalDefaultPath == "") #set($portalDefaultPath = "/${moduleName}/${businessName}") #end #if($hasFrontendRouteItems) #foreach($component in $frontendRouteComponents) import ${component.componentName} from "${component.componentPath}" #end #else import ${BusinessName} from "@/views/${moduleName}/${businessName}/index.vue" #end Vue.use(Router) const router = new Router({ mode: "hash", routes: [ { path: "/", redirect: "${portalDefaultPath}" }, { path: "/login", name: "Login", component: Login }, #if($hasFrontendRouteItems) #foreach($item in $frontendRouteItems) { path: "${item.path}", name: "${item.code}", component: ${item.componentName}, meta: { requiresAuth: ${item.requiresLogin} } }#if($foreach.hasNext),#end #end #else { path: "/${moduleName}/${businessName}", name: "${BusinessName}", component: ${BusinessName}, meta: { requiresAuth: true } } #end ] }) router.beforeEach((to, from, next) => { const requiresAuth = to.matched.some(record => record.meta && record.meta.requiresAuth) const hasToken = Boolean(localStorage.getItem("portal-token")) if (requiresAuth && !hasToken) { next({ path: "/login", query: { redirect: to.fullPath } }) return } next() }) export default router ', 'admin', sysdate()), (920309, 9203, 'frontend-request.js.vm', NULL, 'frontend-request.js.vm', 'import axios from "axios" import { Message } from "element-ui" const request = axios.create({ baseURL: "/api", timeout: 30000 }) request.interceptors.request.use(config => { const token = localStorage.getItem("portal-token") if (token) { config.headers.Authorization = "Bearer " + token } return config }) request.interceptors.response.use( response => { const data = response.data if (data && data.code === "401") { clearSession() Message.error(data.msg || "请先登录") return Promise.reject(new Error(data.msg || "Unauthorized")) } return data }, error => { if (error && error.response && error.response.status === 401) { clearSession() Message.error("请先登录") } else { Message.error(error && error.message ? error.message : "请求失败") } return Promise.reject(error) } ) function clearSession() { localStorage.removeItem("portal-token") localStorage.removeItem("portal-user") if (window.location.hash.indexOf("#/login") !== 0) { window.location.hash = "#/login" } } export default request ', 'admin', sysdate()), (920310, 9203, 'frontend-api.js.vm', NULL, 'frontend-api.js.vm', 'import request from "./request" export function list${ClassName}(query) { return request({ url: "/${moduleName}/${businessName}/page", method: "get", params: query }) } export function get${ClassName}(${pkColumn.javaField}) { return request({ url: "/${moduleName}/${businessName}/" + ${pkColumn.javaField}, method: "get" }) }', 'admin', sysdate()), (920311, 9203, 'frontend-index.vue.vm', NULL, 'frontend-index.vue.vm', ' ', 'admin', sysdate()), (920312, 9203, 'frontend-login.vue.vm', NULL, 'frontend-login.vue.vm', '#set($portalProjectName = $projectName) #if(!$portalProjectName || $portalProjectName == "") #set($portalProjectName = $functionName) #end #if(!$portalProjectName || $portalProjectName == "") #set($portalProjectName = "Portal") #end #set($portalDefaultPath = $defaultFrontendPath) #if(!$portalDefaultPath || $portalDefaultPath == "") #set($portalDefaultPath = "/${moduleName}/${businessName}") #end ', 'admin', sysdate()), (920313, 9203, 'frontend-dict.js.vm', NULL, 'frontend-dict.js.vm', 'const emptyDict = [] export default { getDictDataByType() { return emptyDict }, getDictLabel(type, value) { return value === undefined || value === null || value === "" ? "-" : value } } ', '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 (920100, 0, '{projectName}-backend', 'folder', NULL, 9201, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920101, 920100, 'pom.xml', 'file', NULL, 9201, -1, 'pom.xml.vm', 1, '0', 'admin', sysdate(), '', NULL), (920102, 920100, 'README.md', 'file', NULL, 9201, -1, 'README.md.vm', 2, '0', 'admin', sysdate(), '', NULL), (920103, 920100, 'sql', 'folder', NULL, 9201, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (920104, 920103, '{businessName}.sql', 'file', NULL, 9201, 0, 'sql.vm', 1, '0', 'admin', sysdate(), '', NULL), (920110, 920100, 'src', 'folder', NULL, 9201, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (920111, 920110, 'main', 'folder', NULL, 9201, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920112, 920111, 'java', 'folder', NULL, 9201, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920113, 920111, 'resources', 'folder', NULL, 9201, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (920114, 920112, '{packagePath}', 'folder', NULL, 9201, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920115, 920114, '{projectClassName}Application.java', 'file', NULL, 9201, -1, 'Application.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920116, 920114, 'common', 'folder', NULL, 9201, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (920117, 920116, 'Result.java', 'file', NULL, 9201, -1, 'Result.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920118, 920114, 'config', 'folder', NULL, 9201, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (920119, 920118, 'MybatisPlusConfig.java', 'file', NULL, 9201, -1, 'MybatisPlusConfig.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920120, 920118, 'CorsConfig.java', 'file', NULL, 9201, -1, 'CorsConfig.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920121, 920114, 'controller', 'folder', NULL, 9201, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (920122, 920121, 'FileController.java', 'file', NULL, 9201, -1, 'FileController.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920123, 920121, '{ClassName}Controller.java', 'file', NULL, 9201, 0, 'controller.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920124, 920114, 'entity', 'folder', NULL, 9201, NULL, NULL, 5, '0', 'admin', sysdate(), '', NULL), (920125, 920124, '{ClassName}.java', 'file', NULL, 9201, 0, 'entity.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920126, 920114, 'mapper', 'folder', NULL, 9201, NULL, NULL, 6, '0', 'admin', sysdate(), '', NULL), (920127, 920126, '{ClassName}Mapper.java', 'file', NULL, 9201, 0, 'mapper.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920128, 920114, 'service', 'folder', NULL, 9201, NULL, NULL, 7, '0', 'admin', sysdate(), '', NULL), (920129, 920128, 'I{ClassName}Service.java', 'file', NULL, 9201, 0, 'service.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920130, 920128, 'impl', 'folder', NULL, 9201, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (920131, 920130, '{ClassName}ServiceImpl.java', 'file', NULL, 9201, 0, 'serviceImpl.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920132, 920113, 'application.yml', 'file', NULL, 9201, -1, 'application.yml.vm', 1, '0', 'admin', sysdate(), '', NULL), (920133, 920113, 'mapper', 'folder', NULL, 9201, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (920134, 920133, '{moduleName}', 'folder', NULL, 9201, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920135, 920134, '{ClassName}Mapper.xml', 'file', NULL, 9201, 0, 'mapper.xml.vm', 1, '0', 'admin', sysdate(), '', NULL), (920136, 920124, 'PortalUser.java', 'file', NULL, 9201, -1, 'PortalUser.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920137, 920126, 'PortalUserMapper.java', 'file', NULL, 9201, -1, 'PortalUserMapper.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920138, 920114, 'security', 'folder', NULL, 9201, NULL, NULL, 8, '0', 'admin', sysdate(), '', NULL), (920139, 920138, 'PortalAuthTokenStore.java', 'file', NULL, 9201, -1, 'PortalAuthTokenStore.java.vm', 1, '0', 'admin', sysdate(), '', NULL), (920140, 920138, 'PortalAuthInterceptor.java', 'file', NULL, 9201, -1, 'PortalAuthInterceptor.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920141, 920121, 'PortalAuthController.java', 'file', NULL, 9201, -1, 'PortalAuthController.java.vm', 3, '0', 'admin', sysdate(), '', NULL), (920142, 920124, 'AdminUser.java', 'file', NULL, 9201, -1, 'AdminUser.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920143, 920126, 'AdminUserMapper.java', 'file', NULL, 9201, -1, 'AdminUserMapper.java.vm', 2, '0', 'admin', sysdate(), '', NULL), (920144, 920121, 'AdminAuthController.java', 'file', NULL, 9201, -1, 'AdminAuthController.java.vm', 4, '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 (920200, 0, '{projectName}-admin', 'folder', NULL, 9202, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920201, 920200, 'package.json', 'file', NULL, 9202, -1, 'package.json.vm', 1, '0', 'admin', sysdate(), '', NULL), (920202, 920200, 'babel.config.js', 'file', NULL, 9202, -1, 'babel.config.js.vm', 2, '0', 'admin', sysdate(), '', NULL), (920203, 920200, 'vue.config.js', 'file', NULL, 9202, -1, 'vue.config.js.vm', 3, '0', 'admin', sysdate(), '', NULL), (920204, 920200, 'public', 'folder', NULL, 9202, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (920205, 920204, 'index.html', 'file', NULL, 9202, -1, 'index.html.vm', 1, '0', 'admin', sysdate(), '', NULL), (920206, 920200, 'src', 'folder', NULL, 9202, NULL, NULL, 5, '0', 'admin', sysdate(), '', NULL), (920207, 920206, 'main.js', 'file', NULL, 9202, -1, 'admin-main.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920208, 920206, 'App.vue', 'file', NULL, 9202, -1, 'admin-App.vue.vm', 2, '0', 'admin', sysdate(), '', NULL), (920209, 920206, 'router', 'folder', NULL, 9202, NULL, NULL, 3, '0', 'admin', sysdate(), '', NULL), (920210, 920209, 'index.js', 'file', NULL, 9202, -1, 'admin-router.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920211, 920206, 'utils', 'folder', NULL, 9202, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (920212, 920211, 'request.js', 'file', NULL, 9202, -1, 'request.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920213, 920211, 'dict.js', 'file', NULL, 9202, -1, 'dict.js.vm', 2, '0', 'admin', sysdate(), '', NULL), (920214, 920206, 'views', 'folder', NULL, 9202, NULL, NULL, 5, '0', 'admin', sysdate(), '', NULL), (920218, 920214, 'login', 'folder', NULL, 9202, -1, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920219, 920218, 'index.vue', 'file', NULL, 9202, -1, 'admin-login.vue.vm', 1, '0', 'admin', sysdate(), '', NULL), (920215, 920214, '{moduleName}', 'folder', NULL, 9202, 0, NULL, 2, '0', 'admin', sysdate(), '', NULL), (920216, 920215, '{businessName}', 'folder', NULL, 9202, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920217, 920216, 'index.vue', 'file', NULL, 9202, 0, 'admin-index.vue.vm', 1, '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 (920300, 0, '{projectName}-web', 'folder', NULL, 9203, NULL, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920301, 920300, 'package.json', 'file', NULL, 9203, -1, 'package.json.vm', 1, '0', 'admin', sysdate(), '', NULL), (920302, 920300, 'babel.config.js', 'file', NULL, 9203, -1, 'babel.config.js.vm', 2, '0', 'admin', sysdate(), '', NULL), (920303, 920300, 'vue.config.js', 'file', NULL, 9203, -1, 'vue.config.js.vm', 3, '0', 'admin', sysdate(), '', NULL), (920304, 920300, 'public', 'folder', NULL, 9203, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (920305, 920304, 'index.html', 'file', NULL, 9203, -1, 'index.html.vm', 1, '0', 'admin', sysdate(), '', NULL), (920306, 920300, 'src', 'folder', NULL, 9203, NULL, NULL, 5, '0', 'admin', sysdate(), '', NULL), (920307, 920306, 'main.js', 'file', NULL, 9203, -1, 'frontend-main.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920308, 920306, 'App.vue', 'file', NULL, 9203, -1, 'frontend-App.vue.vm', 2, '0', 'admin', sysdate(), '', NULL), (920309, 920306, 'style.css', 'file', NULL, 9203, -1, 'style.css.vm', 3, '0', 'admin', sysdate(), '', NULL), (920310, 920306, 'router', 'folder', NULL, 9203, NULL, NULL, 4, '0', 'admin', sysdate(), '', NULL), (920311, 920310, 'index.js', 'file', NULL, 9203, -1, 'frontend-router.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920312, 920306, 'api', 'folder', NULL, 9203, NULL, NULL, 5, '0', 'admin', sysdate(), '', NULL), (920313, 920312, 'request.js', 'file', NULL, 9203, -1, 'frontend-request.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920314, 920312, '{businessName}.js', 'file', NULL, 9203, 0, 'frontend-api.js.vm', 2, '0', 'admin', sysdate(), '', NULL), (920321, 920306, 'utils', 'folder', NULL, 9203, NULL, NULL, 6, '0', 'admin', sysdate(), '', NULL), (920322, 920321, 'dict.js', 'file', NULL, 9203, -1, 'frontend-dict.js.vm', 1, '0', 'admin', sysdate(), '', NULL), (920315, 920306, 'views', 'folder', NULL, 9203, NULL, NULL, 7, '0', 'admin', sysdate(), '', NULL), (920316, 920315, '{moduleName}', 'folder', NULL, 9203, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920317, 920316, '{businessName}', 'folder', NULL, 9203, 0, NULL, 1, '0', 'admin', sysdate(), '', NULL), (920318, 920317, 'index.vue', 'file', NULL, 9203, 0, 'frontend-index.vue.vm', 1, '0', 'admin', sysdate(), '', NULL), (920319, 920315, 'login', 'folder', NULL, 9203, NULL, NULL, 2, '0', 'admin', sysdate(), '', NULL), (920320, 920319, 'index.vue', 'file', NULL, 9203, -1, 'frontend-login.vue.vm', 1, '0', 'admin', sysdate(), '', NULL); SET FOREIGN_KEY_CHECKS = 1;