代码生成
This commit is contained in:
261
doc/模版.md
Normal file
261
doc/模版.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# stringtemplate4
|
||||
|
||||
设置 $
|
||||
|
||||
```
|
||||
STGroup group = new STGroupString("db_template",template, '$', '$');
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 模版的定义
|
||||
|
||||
|
||||
|
||||
基础
|
||||
|
||||
```
|
||||
classTemplate(item, module, operationList, className) ::= <<
|
||||
>>
|
||||
```
|
||||
|
||||
> 注意 <<内容>> 定义的方法
|
||||
>
|
||||
> classTemplate 为 group.getInstanceOf("classTemplate"); 定义
|
||||
|
||||
例子:
|
||||
|
||||
```java
|
||||
classTemplate(item, module, operationList, className) ::= <<
|
||||
package $module.packageName$;
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/10
|
||||
* @version 0.0.1
|
||||
*/
|
||||
public class $className$ {
|
||||
|
||||
}
|
||||
>>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
映射字典
|
||||
|
||||
```ts
|
||||
// 定义一个映射字典
|
||||
requestAnnotations ::= [
|
||||
"POST": "@PostMapping",
|
||||
"GET": "@GetMapping",
|
||||
"PUT": "@PutMapping",
|
||||
"DELETE": "@DeleteMapping",
|
||||
default: "@RequestMapping"
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
|
||||
|
||||
```java
|
||||
requestAnnotations ::= [
|
||||
"POST": "@PostMapping",
|
||||
"GET": "@GetMapping",
|
||||
"PUT": "@PutMapping",
|
||||
"DELETE": "@DeleteMapping",
|
||||
default: "@RequestMapping"
|
||||
]
|
||||
|
||||
classTemplate(module,item,operationList,basics) ::= <<
|
||||
package $module.packageName$.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* $item.itemName$ 控制器
|
||||
* @author xia
|
||||
*/
|
||||
@Tag(name = "$item.itemName$")
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/$item.itemCode$")
|
||||
public class $basics.itemCodeUp$Controller {
|
||||
|
||||
private final $basics.itemCodeUp$Service $item.itemCode$Service;
|
||||
|
||||
$operationList:{op |
|
||||
|
||||
@OperLog
|
||||
@SecureAudit(id = $op.id$,$if(op.isGreenLight)$isGreenLight = true,$endif$ $if(!op.isTenant)$isTenant = false,$endif$
|
||||
funType = FunTypeEnum.$op.funType$, funName = "$op.funName$", funCode = "$module.moduleCode$:$item.itemCode$:$op.operationCode$")
|
||||
@Operation(summary = "$op.funName$", description = "$op.describe$")
|
||||
$requestAnnotations.(op.requestType)$("$op.url$$op.pathParamsUrl$")
|
||||
public XResult<$if(op.isPage)$Page<$basics.itemCodeUp$$op.methodNamePascalCase$Res>$else$$if(op.isResParams)$$basics.itemCodeUp$$op.methodNamePascalCase$Res$else$Void$endif$$endif$> $op.methodName$($if(op.isReqParams)$@RequestBody @Valid $basics.itemCodeUp$$op.methodNamePascalCase$Req req$endif$$op.pathParamsLong$) {
|
||||
return $item.itemCode$Service.$op.methodName$($if(op.isPathParams)$$op.pathParams$$endif$$if(op.isReqParams)$req$endif$);
|
||||
\}
|
||||
|
||||
}$
|
||||
|
||||
}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 请求参数
|
||||
|
||||
|
||||
|
||||
```java
|
||||
classTemplate(module,item,operationList,basics) ::= <<
|
||||
package $module.apiPackageName$.request.$item.itemCode$;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* $item.itemName$
|
||||
* $basics.oper.funName$ 请求参数
|
||||
* @author xia
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "$basics.oper.funName$ 参数")
|
||||
public class $basics.itemCodeUp$Req $if(basics.oper.isPage)$extends XhPage$endif$ implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 响应参数
|
||||
|
||||
|
||||
|
||||
```java
|
||||
classTemplate(module,item,operationList,basics) ::= <<
|
||||
package $module.apiPackageName$.response.$item.itemCode$;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* $item.itemName$
|
||||
* $basics.oper.funName$ 响应
|
||||
* @author xia
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "$basics.oper.funName$ 响应")
|
||||
public class $basics.itemCodeUp$Res implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 服务接口
|
||||
|
||||
|
||||
|
||||
```java
|
||||
classTemplate(module,item,operationList,basics) ::= <<
|
||||
package $module.packageName$.service;
|
||||
|
||||
/**
|
||||
* $item.itemName$ 服务层接口
|
||||
* @author xia
|
||||
*/
|
||||
public interface $basics.itemCodeUp$Service {
|
||||
|
||||
$operationList:{op |
|
||||
|
||||
/**
|
||||
* $op.funName$
|
||||
* $op.describe$
|
||||
*/
|
||||
XResult<$if(op.isPage)$Page<$basics.itemCodeUp$$op.methodNamePascalCase$Res>$else$$if(op.isResParams)$$basics.itemCodeUp$$op.methodNamePascalCase$Res$else$Void$endif$$endif$> $op.methodName$($if(op.isReqParams)$$basics.itemCodeUp$$op.methodNamePascalCase$Req req$endif$$if(op.isPathParams)$$op.pathParamsLongReq$$endif$);
|
||||
|
||||
}$
|
||||
|
||||
}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 服务接口实现
|
||||
|
||||
|
||||
|
||||
```java
|
||||
classTemplate(module,item,operationList,basics) ::= <<
|
||||
package $module.packageName$.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import $module.packageName$.service.$basics.itemCodeUp$Service;
|
||||
|
||||
/**
|
||||
* $item.itemName$ 服务层接口
|
||||
* @author xia
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class $basics.itemCodeUp$ServiceImpl implements $basics.itemCodeUp$Service{
|
||||
|
||||
$operationList:{op |
|
||||
|
||||
/**
|
||||
* $op.funName$
|
||||
* $op.describe$
|
||||
*/
|
||||
@Override
|
||||
public XResult<$if(op.isPage)$Page<$basics.itemCodeUp$$op.methodNamePascalCase$Res>$else$$if(op.isResParams)$$basics.itemCodeUp$$op.methodNamePascalCase$Res$else$Void$endif$$endif$> $op.methodName$($if(op.isReqParams)$$basics.itemCodeUp$$op.methodNamePascalCase$Req req$endif$$if(op.isPathParams)$$op.pathParamsLongReq$$endif$){
|
||||
// TODO $op.funName$ 实现
|
||||
return null;
|
||||
\}
|
||||
|
||||
}$
|
||||
|
||||
}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
generated_code.zip
Normal file
BIN
generated_code.zip
Normal file
Binary file not shown.
10
pom.xml
10
pom.xml
@@ -23,6 +23,7 @@
|
||||
<hibernate-validator.version>9.1.0.Final</hibernate-validator.version>
|
||||
<springdoc-openapi-starter-webmvc-ui.version>2.8.15</springdoc-openapi-starter-webmvc-ui.version>
|
||||
<ST4.version>4.3.4</ST4.version>
|
||||
<liteflow.version>2.15.2</liteflow.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -102,8 +103,17 @@
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>ST4</artifactId>
|
||||
<version>${ST4.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- liteflow -->
|
||||
<dependency>
|
||||
<groupId>com.yomahub</groupId>
|
||||
<artifactId>liteflow-spring-boot-starter</artifactId>
|
||||
<version>${liteflow.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.cczsa.xinghe.codegen;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
public class CodegenApplication {
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.cczsa.xinghe.codegen.controller;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.domain.template.CodeGen;
|
||||
import com.cczsa.xinghe.codegen.service.CodeGenService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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;
|
||||
|
||||
@@ -22,9 +25,9 @@ public class CodeGenController {
|
||||
private final CodeGenService codeGenService;
|
||||
|
||||
@PostMapping("/download")
|
||||
public ResponseEntity<byte[]> downloadCode() {
|
||||
public ResponseEntity<byte[]> downloadCode(@RequestBody @Valid CodeGen req) {
|
||||
try {
|
||||
byte[] zipData = codeGenService.generateCodeZip();
|
||||
byte[] zipData = codeGenService.generateCodeZip(req);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
// 设置下载文件名
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
package com.cczsa.xinghe.codegen.entity.domain;
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.cczsa.xinghe.codegen.entity.domain.template;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "代码生成-参数")
|
||||
public class CodeGen implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotNull(message = "代码生成类型不能为空")
|
||||
@Schema(description = "代码生成类型")
|
||||
private CodeGenType codeGenType;
|
||||
|
||||
@Schema(description = "功能ID")
|
||||
private Long itemId;
|
||||
|
||||
|
||||
@Hidden
|
||||
@AssertTrue(message = "当代码生成类型为 CONTROLLER 时,功能ID不能为空")
|
||||
public boolean isItemIdValid() {
|
||||
if (codeGenType == CodeGenType.CONTROLLER) {
|
||||
return itemId != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.cczsa.xinghe.codegen.entity.domain.template;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.mybatisflex.annotation.EnumValue;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
@Schema(description = "代码生成类型")
|
||||
@Getter
|
||||
public enum CodeGenType {
|
||||
|
||||
CONTROLLER(0, "controller");
|
||||
|
||||
private final int code;
|
||||
private final String desc;
|
||||
|
||||
CodeGenType(int code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.cczsa.xinghe.codegen.entity.domain.template;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.FunOperationEntity;
|
||||
import com.cczsa.xinghe.codegen.util.StringUtils;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/19
|
||||
* @version 0.0.1
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class FunOperationTemp extends FunOperationEntity {
|
||||
|
||||
/**
|
||||
* URL路径
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
private String methodName;
|
||||
/**
|
||||
* 方法名 驼峰
|
||||
*/
|
||||
private String methodNamePascalCase;
|
||||
/**
|
||||
* 路径参数是否为空
|
||||
*/
|
||||
private Boolean isPathParams;
|
||||
/**
|
||||
* 路径参数 Long入参
|
||||
*/
|
||||
private String pathParamsLong;
|
||||
/**
|
||||
* 路径参数 Long入参
|
||||
*/
|
||||
private String pathParamsLongReq;
|
||||
/**
|
||||
* 路径参数 url入参
|
||||
*/
|
||||
private String pathParamsUrl;
|
||||
|
||||
|
||||
public void info(){
|
||||
url = StringUtils.toPath(this.getOperationCode());
|
||||
methodName = StringUtils.toCamelCase(this.getOperationCode());
|
||||
methodNamePascalCase = StringUtils.toPascalCase(this.getOperationCode());
|
||||
isPathParams= StringUtils.isNotEmpty(this.getPathParams());
|
||||
if (isPathParams){
|
||||
pathParamsLongReq = "Long "+this.getPathParams();
|
||||
pathParamsLong = "@PathVariable(\""+this.getPathParams()+"\") Long "+this.getPathParams();
|
||||
pathParamsUrl = "/{"+this.getPathParams()+"}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.cczsa.xinghe.codegen.entity.domain.template;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.FunItemEntity;
|
||||
import com.cczsa.xinghe.codegen.entity.FunModuleEntity;
|
||||
import com.cczsa.xinghe.codegen.entity.FunOperationEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class FunctionCode extends TemplateObj{
|
||||
|
||||
// 模块信息
|
||||
private FunModuleEntity moduleEntity;
|
||||
|
||||
// 功能信息
|
||||
private FunItemEntity itemEntity;
|
||||
|
||||
// 操作信息
|
||||
private List<FunOperationEntity> operationEntityList;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.cczsa.xinghe.codegen.entity.domain.template;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
public class TemplateObj {
|
||||
|
||||
}
|
||||
@@ -16,13 +16,15 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author My
|
||||
*/
|
||||
@Schema(description = "从接口:/templateType 获取列表",
|
||||
example = "0",
|
||||
allowableValues = {"0: controller"})
|
||||
@Schema(description = "从接口:/templateType 获取列表")
|
||||
@Getter
|
||||
public enum TemplateTypeEnum {
|
||||
|
||||
CONTROLLER(0, "controller");
|
||||
CONTROLLER(0, "controller"),
|
||||
REQUEST_PARAM(1, "请求参数"),
|
||||
RESPONSE_PARAM(2, "响应参数"),
|
||||
SERVICE(3, "服务接口"),
|
||||
SERVICE_IMPL(4, "服务实现");
|
||||
|
||||
private final int code;
|
||||
private final String desc;
|
||||
|
||||
@@ -8,6 +8,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -41,7 +42,7 @@ public class RoleBindFunReq implements Serializable {
|
||||
private List<Long> userDataScope;
|
||||
|
||||
@Schema(description = "排除的字段 根据 fieldConfig 进行设置")
|
||||
private List<String> excludeField;
|
||||
private List<String> excludeField = new ArrayList<>();
|
||||
|
||||
@NotNull(message = "客户端类型不能为空")
|
||||
@Schema(description = "客户端类型:0 PC端,1 小程序端,2 H5端")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.cczsa.xinghe.codegen.entity.res.role;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.enums.ClientTypeEnum;
|
||||
import com.cczsa.xinghe.codegen.entity.enums.MenuTypeEnum;
|
||||
import com.cczsa.xinghe.codegen.entity.enums.UsableConfigEnum;
|
||||
import com.cczsa.xinghe.codegen.handler.PostgreSQLJsonTypeHandler;
|
||||
import com.mybatisflex.annotation.Column;
|
||||
@@ -38,6 +39,8 @@ public class RoleQueryFunRes implements Serializable {
|
||||
@Schema(description = "父菜单ID")
|
||||
private Long parentId = 0L;
|
||||
|
||||
private MenuTypeEnum menuType;
|
||||
|
||||
@Schema(description = "路由路径")
|
||||
private String path;
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.cczsa.xinghe.codegen.service;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.domain.template.CodeGen;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/10
|
||||
* @version 0.0.1
|
||||
*/
|
||||
public interface CodeGenService {
|
||||
byte[] generateCodeZip();
|
||||
byte[] generateCodeZip(CodeGen req);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.cczsa.xinghe.codegen.service;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.enums.TemplateTypeEnum;
|
||||
import com.cczsa.xinghe.codegen.entity.req.template.TemplateAddReq;
|
||||
import com.cczsa.xinghe.codegen.entity.req.template.TemplateEditReq;
|
||||
import com.cczsa.xinghe.codegen.entity.req.template.TemplateQueryReq;
|
||||
@@ -42,4 +43,11 @@ public interface TemplateService {
|
||||
*/
|
||||
XResult<List<Map<Integer, String>>> getTemplateTypeList();
|
||||
|
||||
|
||||
/**
|
||||
* 根据模板类型获取模板内容的方法
|
||||
* @return 返回对应模板类型的字符串内容
|
||||
*/
|
||||
String getTemplateTypeContent(TemplateTypeEnum templateType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
package com.cczsa.xinghe.codegen.service.flow;
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.cczsa.xinghe.codegen.service.flow.template;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.FunItemEntity;
|
||||
import com.cczsa.xinghe.codegen.entity.FunModuleEntity;
|
||||
import com.cczsa.xinghe.codegen.entity.domain.template.FunOperationTemp;
|
||||
import com.cczsa.xinghe.codegen.entity.enums.TemplateTypeEnum;
|
||||
import com.cczsa.xinghe.codegen.mapper.FunItemMapper;
|
||||
import com.cczsa.xinghe.codegen.mapper.FunModuleMapper;
|
||||
import com.cczsa.xinghe.codegen.mapper.FunOperationMapper;
|
||||
import com.cczsa.xinghe.codegen.mapper.def.FunOperationDef;
|
||||
import com.cczsa.xinghe.codegen.service.TemplateService;
|
||||
import com.cczsa.xinghe.codegen.util.StringUtils;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.stringtemplate.v4.ST;
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
import org.stringtemplate.v4.STGroupString;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @author xia
|
||||
* @date 2026/1/17
|
||||
* @version 0.0.1
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@LiteflowComponent(id = "controllerCodeCreate", name = "controller 代码生成")
|
||||
public class ControllerCodeCreate extends NodeComponent {
|
||||
|
||||
private final FunModuleMapper funModuleMapper;
|
||||
private final FunItemMapper funItemMapper;
|
||||
private final FunOperationMapper funOperationMapper;
|
||||
private final TemplateService templateService;
|
||||
|
||||
|
||||
|
||||
// 模块信息
|
||||
private FunModuleEntity moduleEntity;
|
||||
// 功能信息
|
||||
private FunItemEntity itemEntity;
|
||||
// 操作信息
|
||||
private List<FunOperationTemp> operationEntityList;
|
||||
// 压缩流
|
||||
private ZipOutputStream zos;
|
||||
|
||||
|
||||
/**
|
||||
* setResponseData()/getResponseData():用于整个流程的输入输出
|
||||
* setInput()/getInput():用于节点间的输入数据
|
||||
* setOutput()/getOutput():用于节点间的输出数据
|
||||
* setChainReqData()/getChainReqData():用于链间数据传递
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void process() throws Exception {
|
||||
Long itemId = this.getContextBean(Long.class);
|
||||
|
||||
itemEntity = funItemMapper.selectOneById(itemId);
|
||||
if (itemEntity == null){
|
||||
return;
|
||||
}
|
||||
|
||||
moduleEntity = funModuleMapper.selectOneById(itemEntity.getModuleId());
|
||||
if (itemEntity == null){
|
||||
return;
|
||||
}
|
||||
FunOperationDef def = FunOperationDef.FUN_OPERATION_ENTITY;
|
||||
QueryWrapper query = QueryWrapper.create()
|
||||
.select(def.ALL_COLUMNS)
|
||||
.from(def)
|
||||
.where(def.MODULE_ID.eq(moduleEntity.getId()))
|
||||
.and(def.ITEM_ID.eq(itemId))
|
||||
.orderBy(def.ID,true);
|
||||
operationEntityList = funOperationMapper.selectListByQueryAs(query,FunOperationTemp.class);
|
||||
if (operationEntityList == null){
|
||||
operationEntityList = List.of();
|
||||
}
|
||||
operationEntityList.forEach(FunOperationTemp::info);
|
||||
|
||||
// 流
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
zos = new ZipOutputStream(outputStream);
|
||||
// 生成controller代码
|
||||
generateControllerCode(TemplateTypeEnum.CONTROLLER);
|
||||
// 生成请求参数代码
|
||||
generateControllerCode(TemplateTypeEnum.REQUEST_PARAM);
|
||||
// 生成响应参数代码
|
||||
generateControllerCode(TemplateTypeEnum.RESPONSE_PARAM);
|
||||
// 生成服务接口代码
|
||||
generateControllerCode(TemplateTypeEnum.SERVICE);
|
||||
// 生成服务接口实现代码
|
||||
generateControllerCode(TemplateTypeEnum.SERVICE_IMPL);
|
||||
|
||||
// 确保 ZIP 流结束
|
||||
zos.finish();
|
||||
// 返回数据
|
||||
this.getSlot().setResponseData(outputStream.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Controller代码
|
||||
*/
|
||||
public void generateControllerCode(TemplateTypeEnum templateType) throws IOException {
|
||||
// 获取
|
||||
String template = templateService.getTemplateTypeContent(templateType);
|
||||
if (template == null){
|
||||
return;
|
||||
}
|
||||
// 生成
|
||||
STGroup group = new STGroupString("db_template",template, '$', '$');
|
||||
ST classTemplate = group.getInstanceOf("classTemplate"); // 使用正确的模板名称
|
||||
|
||||
if (classTemplate == null) {
|
||||
System.out.println(template);
|
||||
throw new RuntimeException("Template 'classTemplate' not found");
|
||||
}
|
||||
|
||||
// 设置模板数据
|
||||
Map<String, Object> basics = new HashMap<>();
|
||||
setTemplateData(classTemplate,basics);
|
||||
|
||||
if(templateType == TemplateTypeEnum.REQUEST_PARAM || templateType == TemplateTypeEnum.RESPONSE_PARAM){
|
||||
for (FunOperationTemp item : operationEntityList){
|
||||
if (!isValidItem(templateType, item)) {
|
||||
continue;
|
||||
}
|
||||
// 生成代码并写入ZIP
|
||||
String itemStr = StringUtils.capitalizeFirstLetter(itemEntity.getItemCode());
|
||||
String operStr = StringUtils.toPascalCase(item.getOperationCode());
|
||||
basics.put("itemCodeUp", itemStr+operStr);
|
||||
basics.put("oper", item);
|
||||
writeCodeToZip(classTemplate, templateType, itemStr+ operStr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成代码并写入ZIP
|
||||
writeCodeToZip(classTemplate, templateType,StringUtils.capitalizeFirstLetter(itemEntity.getItemCode()));
|
||||
}
|
||||
|
||||
private boolean isValidItem(TemplateTypeEnum templateType, FunOperationTemp item) {
|
||||
return switch (templateType) {
|
||||
case REQUEST_PARAM -> item.getIsReqParams();
|
||||
case RESPONSE_PARAM -> item.getIsResParams();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 TemplateTypeEnum 获取文件后缀名
|
||||
* @param templateType 模板类型
|
||||
* @return 文件后缀名
|
||||
*/
|
||||
private String getFileSuffix(TemplateTypeEnum templateType) {
|
||||
return switch (templateType) {
|
||||
case CONTROLLER -> "Controller.java";
|
||||
case REQUEST_PARAM -> "Req.java";
|
||||
case RESPONSE_PARAM -> "Res.java";
|
||||
case SERVICE -> "Service.java";
|
||||
case SERVICE_IMPL -> "ServiceImpl.java";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模板数据
|
||||
*
|
||||
* @param classTemplate 模板对象
|
||||
* @param basics
|
||||
*/
|
||||
private void setTemplateData(ST classTemplate, Map<String, Object> basics) {
|
||||
classTemplate.add("item", itemEntity);
|
||||
classTemplate.add("module", moduleEntity);
|
||||
classTemplate.add("operationList", operationEntityList);
|
||||
|
||||
// 基础信息
|
||||
|
||||
String itemCodeUp = StringUtils.capitalizeFirstLetter(itemEntity.getItemCode());
|
||||
basics.put("itemCodeUp", itemCodeUp);
|
||||
basics.put("path", StringUtils.toPath(itemEntity.getItemCode()));
|
||||
|
||||
classTemplate.add("basics", basics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码并写入ZIP
|
||||
*/
|
||||
private void writeCodeToZip(ST classTemplate, TemplateTypeEnum templateType,String fileName) throws IOException {
|
||||
// 生成代码
|
||||
String result = classTemplate.render();
|
||||
|
||||
ZipEntry entry = new ZipEntry(fileName + getFileSuffix(templateType));
|
||||
zos.putNextEntry(entry);
|
||||
zos.write(result.getBytes(StandardCharsets.UTF_8));
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
package com.cczsa.xinghe.codegen.service.impl;
|
||||
|
||||
import com.cczsa.xinghe.codegen.entity.FunItemEntity;
|
||||
import com.cczsa.xinghe.codegen.entity.domain.template.CodeGen;
|
||||
import com.cczsa.xinghe.codegen.entity.domain.template.CodeGenType;
|
||||
import com.cczsa.xinghe.codegen.mapper.FunItemMapper;
|
||||
import com.cczsa.xinghe.codegen.mapper.FunModuleMapper;
|
||||
import com.cczsa.xinghe.codegen.mapper.FunOperationMapper;
|
||||
import com.cczsa.xinghe.codegen.service.CodeGenService;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -15,8 +24,26 @@ import org.springframework.stereotype.Service;
|
||||
@RequiredArgsConstructor
|
||||
public class CodeGenServiceImpl implements CodeGenService {
|
||||
|
||||
private final FunModuleMapper funModuleMapper;
|
||||
private final FunItemMapper funItemMapper;
|
||||
private final FunOperationMapper funOperationMapper;
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Override
|
||||
public byte[] generateCodeZip() {
|
||||
public byte[] generateCodeZip(CodeGen req) {
|
||||
|
||||
|
||||
FunItemEntity funItemEntity = funItemMapper.selectOneById(req.getItemId());
|
||||
if(req.getCodeGenType() == CodeGenType.CONTROLLER && funItemEntity != null){
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("controller",null,req.getItemId());
|
||||
log.info("执行结果:{}",response);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
return (byte[]) response.getSlot().getResponseData();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,14 @@ public class RoleServiceImpl implements RoleService {
|
||||
*/
|
||||
@Override
|
||||
public XResult<Void> bindFun(RoleBindFunReq req) {
|
||||
// ID = 操作id+ 角色ID + 客户端类型
|
||||
String id = req.getFunId().toString() + req.getRoleId().toString()+req.getClientType().getCode();
|
||||
|
||||
FunOperationEntity funOperationEntity1 = funOperationMapper.selectOneById(Long.parseLong(id));
|
||||
if (funOperationEntity1 != null) {
|
||||
return XResult.ok();
|
||||
}
|
||||
|
||||
FunOperationEntity funOperationEntity = funOperationMapper.selectOneById(req.getFunId());
|
||||
if (funOperationEntity == null) {
|
||||
return XResult.failed("权限不存在");
|
||||
@@ -175,27 +183,14 @@ public class RoleServiceImpl implements RoleService {
|
||||
roleFunEntity.setDataScope(req.getDataScope());
|
||||
roleFunEntity.setExcludeField(req.getExcludeField());
|
||||
roleFunEntity.setClientType(req.getClientType());
|
||||
if (req.getExcludeField() == null || !req.getExcludeField().isEmpty()) {
|
||||
roleFunEntity.setExcludeField(new ArrayList<>());
|
||||
}
|
||||
// 删除旧权限
|
||||
QueryWrapper deleteRoleFun = new QueryWrapper();
|
||||
deleteRoleFun.eq(RoleFunEntity::getClientType, req.getClientType());
|
||||
deleteRoleFun.eq(RoleFunEntity::getRoleId, req.getRoleId());
|
||||
deleteRoleFun.eq(RoleFunEntity::getFunId, req.getFunId());
|
||||
roleFunMapper.deleteByQuery(deleteRoleFun);
|
||||
// 获取最大ID
|
||||
RoleFunDef roleFunDef = RoleFunDef.ROLE_FUN_ENTITY;
|
||||
QueryWrapper queryRoleFun = new QueryWrapper();
|
||||
queryRoleFun.select(roleFunDef.ID)
|
||||
.from(roleFunDef)
|
||||
.orderBy(roleFunDef.ID, false);
|
||||
Long maxId = roleFunMapper.selectOneByQueryAs(queryRoleFun, Long.class);
|
||||
if (maxId == null) {
|
||||
maxId = 10000L;
|
||||
} else {
|
||||
maxId++;
|
||||
}
|
||||
roleFunEntity.setId(maxId);
|
||||
|
||||
roleFunEntity.setId(Long.parseLong(id));
|
||||
// 新增权限
|
||||
roleFunMapper.insertSelective(roleFunEntity);
|
||||
return XResult.ok();
|
||||
@@ -232,13 +227,15 @@ public class RoleServiceImpl implements RoleService {
|
||||
)
|
||||
.from(menuDef)
|
||||
.leftJoin(funOperationDef).on(menuDef.FUN_ID.eq(funOperationDef.ID))
|
||||
.eq(MenuEntity::getClientType, req.getClientType());
|
||||
.eq(MenuEntity::getClientType, req.getClientType())
|
||||
.orderBy(MenuEntity::getSortOrder, false);
|
||||
List<RoleQueryFunRes> roleQueryFunList = menuMapper.selectListByQueryAs(queryMenu, RoleQueryFunRes.class);
|
||||
// 获取角色功能权限
|
||||
RoleFunDef roleFunDef = RoleFunDef.ROLE_FUN_ENTITY;
|
||||
QueryWrapper queryRoleFun = new QueryWrapper();
|
||||
queryRoleFun.select(roleFunDef.ALL_COLUMNS)
|
||||
.from(roleFunDef)
|
||||
.eq(RoleFunEntity::getClientType,req.getClientType())
|
||||
.eq(RoleFunEntity::getRoleId, req.getRoleId());
|
||||
List<RoleFunEntity> roleFunEntities = roleFunMapper.selectListByQuery(queryRoleFun);
|
||||
for (RoleQueryFunRes roleQueryFunRes : roleQueryFunList) {
|
||||
|
||||
@@ -63,9 +63,43 @@ public class TemplateServiceImpl implements TemplateService {
|
||||
TemplateEntity templateEntity = new TemplateEntity();
|
||||
BeanUtils.copyProperties(req, templateEntity);
|
||||
templateMapper.insert(templateEntity);
|
||||
// 其它的设置为未使用
|
||||
if(req.getIsUse()){
|
||||
setCurrentTemplate(templateEntity.getId());
|
||||
}
|
||||
return XResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模板为当前使用
|
||||
* 同一类型下只能有一个模板被标记为使用中
|
||||
* @param templateId 模板ID
|
||||
*/
|
||||
public void setCurrentTemplate(Long templateId) {
|
||||
// 获取模板
|
||||
TemplateEntity templateEntity = templateMapper.selectOneById(templateId);
|
||||
if (templateEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取模板类型
|
||||
TemplateTypeEnum templateType = templateEntity.getTemplateType();
|
||||
|
||||
// 查询同一类型下的所有模板
|
||||
QueryWrapper queryWrapper = new QueryWrapper();
|
||||
queryWrapper.eq(TemplateEntity::getTemplateType, templateType);
|
||||
|
||||
// 将同一类型下除当前模板外的所有模板设置为未使用
|
||||
TemplateEntity entity = new TemplateEntity();
|
||||
entity.setIsUse(false);
|
||||
templateMapper.updateByQuery(entity,true,queryWrapper);
|
||||
|
||||
// 将当前模板设置为使用中
|
||||
templateEntity.setIsUse(true);
|
||||
templateMapper.update(templateEntity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改模板
|
||||
*/
|
||||
@@ -86,6 +120,10 @@ public class TemplateServiceImpl implements TemplateService {
|
||||
}
|
||||
BeanUtils.copyProperties(req, templateEntity);
|
||||
templateMapper.update(templateEntity);
|
||||
// 其它的设置为未使用
|
||||
if(req.getIsUse()){
|
||||
setCurrentTemplate(templateEntity.getId());
|
||||
}
|
||||
return XResult.ok();
|
||||
}
|
||||
|
||||
@@ -106,4 +144,15 @@ public class TemplateServiceImpl implements TemplateService {
|
||||
return XResult.ok(TemplateTypeEnum.getEnumList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateTypeContent(TemplateTypeEnum templateType) {
|
||||
TemplateDef def = TemplateDef.TEMPLATE_ENTITY;
|
||||
QueryWrapper query = new QueryWrapper();
|
||||
|
||||
query.select(def.CONTENT)
|
||||
.from(def)
|
||||
.where(def.TEMPLATE_TYPE.eq(templateType))
|
||||
.and(def.IS_USE.eq(true));
|
||||
return templateMapper.selectOneByQueryAs(query, String.class);
|
||||
}
|
||||
}
|
||||
105
src/main/java/com/cczsa/xinghe/codegen/util/StringUtils.java
Normal file
105
src/main/java/com/cczsa/xinghe/codegen/util/StringUtils.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.cczsa.xinghe.codegen.util;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author xia
|
||||
* @date 2026/1/19
|
||||
* @version 0.0.1
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
/**
|
||||
* 将字符串首字母大写
|
||||
* @param str 输入字符串
|
||||
* @return 首字母大写的字符串
|
||||
*/
|
||||
public static String capitalizeFirstLetter(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转为路径
|
||||
* 例子: user-add -> /user/add
|
||||
* @param str 输入字符串
|
||||
* @return 转换后的路径字符串
|
||||
*/
|
||||
public static String toPath(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
// 将连字符替换为斜杠,并在开头添加斜杠
|
||||
return "/" + str.replace("-", "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将连字符分隔的字符串转为驼峰命名
|
||||
* 例子:test-user -> testUser
|
||||
* @param str 输入字符串
|
||||
* @return 驼峰命名字符串
|
||||
*/
|
||||
public static String toCamelCase(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
String[] parts = str.split("-");
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
if (i == 0) {
|
||||
// 第一部分保持原样(小写)
|
||||
result.append(parts[i]);
|
||||
} else {
|
||||
// 后续部分首字母大写
|
||||
if (!parts[i].isEmpty()) {
|
||||
result.append(parts[i].substring(0, 1).toUpperCase())
|
||||
.append(parts[i].substring(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将连字符分隔的字符串转为大驼峰命名(PascalCase)
|
||||
* 例子:test-user -> TestUser
|
||||
* @param str 输入字符串
|
||||
* @return 大驼峰命名字符串
|
||||
*/
|
||||
public static String toPascalCase(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str;
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
String[] parts = str.split("-");
|
||||
for (String part : parts) {
|
||||
if (!part.isEmpty()) {
|
||||
result.append(part.substring(0, 1).toUpperCase())
|
||||
.append(part.substring(1));
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为空
|
||||
* 包括 null、空字符串 ""、"null"(不区分大小写)
|
||||
* @param str 输入字符串
|
||||
* @return 如果为空返回 true,否则返回 false
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return str == null || str.isEmpty() || str.trim().equalsIgnoreCase("null");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否不为空
|
||||
* @param str 输入字符串
|
||||
* @return 如果不为空返回 true,否则返回 false
|
||||
*/
|
||||
public static boolean isNotEmpty(String str) {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/resources/application-dev.yml
Normal file
22
src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
server:
|
||||
port: 7011
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://192.168.1.26:5432/xinghe-codegen
|
||||
username: postgres
|
||||
password: root
|
||||
driver-class-name: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource # 使用 HikariCP 作为连接池
|
||||
hikari:
|
||||
maximum-pool-size: 20
|
||||
minimum-idle: 5
|
||||
connection-timeout: 60000
|
||||
idle-timeout: 300000
|
||||
max-lifetime: 1200000
|
||||
validation-timeout: 5000
|
||||
leak-detection-threshold: 60000
|
||||
|
||||
liteflow:
|
||||
rule-source: liteflow/*.el.xml
|
||||
enable: true
|
||||
@@ -1,18 +1,4 @@
|
||||
server:
|
||||
port: 7011
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://192.168.1.26:5432/xinghe-codegen
|
||||
username: postgres
|
||||
password: root
|
||||
driver-class-name: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource # 使用 HikariCP 作为连接池
|
||||
hikari:
|
||||
maximum-pool-size: 20
|
||||
minimum-idle: 5
|
||||
connection-timeout: 60000
|
||||
idle-timeout: 300000
|
||||
max-lifetime: 1200000
|
||||
validation-timeout: 5000
|
||||
leak-detection-threshold: 60000
|
||||
# 使用不同模块的数据库配置
|
||||
profiles:
|
||||
active: dev
|
||||
7
src/main/resources/liteflow/controller.el.xml
Normal file
7
src/main/resources/liteflow/controller.el.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<!-- 主流程:用户下单 -->
|
||||
<chain name="controller">
|
||||
THEN(controllerCodeCreate);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -22,15 +22,15 @@ public class CodeGenTest {
|
||||
@Test
|
||||
@DisplayName("直接运行代码生成逻辑并输出到本地文件")
|
||||
void executeCodeGen() {
|
||||
// 1. 模拟 CodeGenServiceImpl.java 中的逻辑
|
||||
// 创建字节输出流,用于在内存中构建 ZIP 文件
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
|
||||
// 定义模板语法
|
||||
// 定义 StringTemplate 模板语法,用于生成 Java 类代码
|
||||
String template = """
|
||||
classTemplate(packageName, className, description, author) ::= <<
|
||||
package <packageName>;
|
||||
|
||||
|
||||
/**
|
||||
* <description>
|
||||
* @author <author>
|
||||
@@ -41,31 +41,36 @@ public class CodeGenTest {
|
||||
>>
|
||||
""";
|
||||
|
||||
// 初始化 StringTemplate 组
|
||||
// 初始化 StringTemplate 组,加载模板定义
|
||||
STGroup group = new STGroupString(template);
|
||||
// 获取模板实例
|
||||
ST st = group.getInstanceOf("classTemplate");
|
||||
|
||||
if (st == null) {
|
||||
throw new RuntimeException("Template 'classTemplate' not found");
|
||||
}
|
||||
|
||||
// 填充数据
|
||||
// 向模板实例填充数据
|
||||
st.add("packageName", "com.example.generated");
|
||||
st.add("className", "HelloWorld");
|
||||
st.add("description", "这是一个通过测试类直接生成的示例");
|
||||
st.add("author", "Gemini_Test");
|
||||
|
||||
// 渲染模板,生成最终的 Java 代码字符串
|
||||
String result = st.render();
|
||||
|
||||
// 写入 ZIP 流
|
||||
// 创建 ZIP 条目,代表 ZIP 文件中的一个文件
|
||||
ZipEntry entry = new ZipEntry("com/example/generated/HelloWorld.java");
|
||||
// 将条目写入 ZIP 输出流
|
||||
zos.putNextEntry(entry);
|
||||
// 将生成的代码写入 ZIP 条目
|
||||
zos.write(result.getBytes(StandardCharsets.UTF_8));
|
||||
// 关闭当前 ZIP 条目
|
||||
zos.closeEntry();
|
||||
|
||||
zos.finish(); // 确保 ZIP 流结束
|
||||
|
||||
// 2. 将结果保存到本地磁盘(方便查看结果)
|
||||
// 将内存中的 ZIP 内容写入本地文件,方便查看结果
|
||||
try (FileOutputStream fos = new FileOutputStream("generated_code.zip")) {
|
||||
baos.writeTo(fos);
|
||||
System.out.println("代码生成成功!请检查项目根目录下的 generated_code.zip");
|
||||
@@ -90,7 +95,7 @@ public class CodeGenTest {
|
||||
String template = """
|
||||
classTemplate(packageName, className, description, author) ::= <<
|
||||
package <packageName>;
|
||||
|
||||
|
||||
/**
|
||||
* <description>
|
||||
* @author <author>
|
||||
|
||||
Reference in New Issue
Block a user