diff --git a/agileboot-system/wol-auth/pom.xml b/agileboot-system/wol-auth/pom.xml
index d379eca..75cd831 100644
--- a/agileboot-system/wol-auth/pom.xml
+++ b/agileboot-system/wol-auth/pom.xml
@@ -24,6 +24,11 @@
com.agileboot
agileboot-system-base
+
+ com.agileboot
+ wol-codegenerator
+ 1.0.0
+
diff --git a/agileboot-system/wol-codegenerator/pom.xml b/agileboot-system/wol-codegenerator/pom.xml
index 146ee5b..4648c07 100644
--- a/agileboot-system/wol-codegenerator/pom.xml
+++ b/agileboot-system/wol-codegenerator/pom.xml
@@ -22,5 +22,11 @@
com.agileboot
wol-common-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-freemarker
+
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/controller/GeneratorController.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/controller/GeneratorController.java
index ea51bb3..ef28207 100644
--- a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/controller/GeneratorController.java
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/controller/GeneratorController.java
@@ -1,15 +1,15 @@
package com.agileboot.codegen.controller;
+import com.agileboot.codegen.dto.CodegenRequest;
import com.agileboot.codegen.service.IGeneratorService;
import com.agileboot.common.core.core.R;
import com.alibaba.fastjson2.JSONArray;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
+import java.util.Map;
/**
* @Author cuiJiaWang
@@ -24,9 +24,42 @@ public class GeneratorController {
private final IGeneratorService generatorService;
@GetMapping("/template/all")
- public R> getAllTemplates() {
+ public R> getAllTemplates() {
String templates = generatorService.getTemplateConfig();
JSONArray jsonArray = JSONArray.parseArray(templates);
return R.ok(jsonArray);
}
+
+ @PostMapping("/code/generate")
+ public R> generateCode(@RequestBody CodegenRequest request) {
+ try {
+ log.info("开始生成代码,表SQL: {}", request.getTableSql());
+
+ // 合并请求参数
+ Map options = new HashMap<>();
+ if (request.getOptions() != null) {
+ options.putAll(request.getOptions());
+ }
+ options.put("tableSql", request.getTableSql());
+
+ // 调用服务生成代码
+ Map result = generatorService.getResultByParams(options);
+
+ // 检查是否有错误
+ if (result.containsKey("error")) {
+ return R.fail(result.get("error"));
+ }
+
+ Map response = new HashMap<>();
+ response.put("outputJson", result);
+ response.put("tableName", result.get("tableName"));
+
+ log.info("代码生成完成,表名: {}", result.get("tableName"));
+ return R.ok(response);
+
+ } catch (Exception e) {
+ log.error("生成代码失败", e);
+ return R.fail("生成代码失败: " + e.getMessage());
+ }
+ }
}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/dto/CodegenRequest.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/dto/CodegenRequest.java
new file mode 100644
index 0000000..6a38f2d
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/dto/CodegenRequest.java
@@ -0,0 +1,22 @@
+package com.agileboot.codegen.dto;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 代码生成请求DTO
+ */
+@Data
+public class CodegenRequest {
+
+ /**
+ * 表SQL语句
+ */
+ private String tableSql;
+
+ /**
+ * 生成选项
+ */
+ private Map options;
+}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/ClassInfo.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/ClassInfo.java
new file mode 100644
index 0000000..5466652
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/ClassInfo.java
@@ -0,0 +1,37 @@
+package com.agileboot.codegen.entity;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 类信息实体
+ */
+@Data
+public class ClassInfo {
+
+ /**
+ * 表名
+ */
+ private String tableName;
+
+ /**
+ * 原始表名
+ */
+ private String originTableName;
+
+ /**
+ * 类名
+ */
+ private String className;
+
+ /**
+ * 类注释
+ */
+ private String classComment;
+
+ /**
+ * 字段列表
+ */
+ private List fieldList;
+}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/FieldInfo.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/FieldInfo.java
new file mode 100644
index 0000000..99edd9f
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/FieldInfo.java
@@ -0,0 +1,35 @@
+package com.agileboot.codegen.entity;
+
+import lombok.Data;
+
+/**
+ * 字段信息实体
+ */
+@Data
+public class FieldInfo {
+
+ /**
+ * 数据库列名
+ */
+ private String columnName;
+
+ /**
+ * Java字段名
+ */
+ private String fieldName;
+
+ /**
+ * Java字段类型
+ */
+ private String fieldClass;
+
+ /**
+ * Swagger类型
+ */
+ private String swaggerClass;
+
+ /**
+ * 字段注释
+ */
+ private String fieldComment;
+}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/ParamInfo.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/ParamInfo.java
new file mode 100644
index 0000000..3db3208
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/entity/ParamInfo.java
@@ -0,0 +1,22 @@
+package com.agileboot.codegen.entity;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 参数信息实体
+ */
+@Data
+public class ParamInfo {
+
+ /**
+ * 表SQL语句
+ */
+ private String tableSql;
+
+ /**
+ * 生成选项
+ */
+ private Map options;
+}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/GeneratorServiceImpl.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/GeneratorServiceImpl.java
index 7863f5a..f02b4ae 100644
--- a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/GeneratorServiceImpl.java
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/GeneratorServiceImpl.java
@@ -1,19 +1,31 @@
package com.agileboot.codegen.service;
+import com.agileboot.codegen.entity.ClassInfo;
+import com.agileboot.codegen.util.FreemarkerUtil;
+import com.agileboot.codegen.util.MapUtil;
+import com.agileboot.codegen.util.TableParseUtil;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
import java.util.stream.Collectors;
/**
* @Author cuiJiaWang
* @Create 2025-09-24 14:31
*/
+@Slf4j
@Service
public class GeneratorServiceImpl implements IGeneratorService {
+
@Override
public String getTemplateConfig() {
String templateConfig = "";
@@ -24,9 +36,73 @@ public class GeneratorServiceImpl implements IGeneratorService {
inputStream.close();
}
} catch (IOException e) {
- e.printStackTrace();
+ log.error("读取模板配置失败", e);
}
-
return templateConfig;
}
+
+ @Override
+ public Map getResultByParams(Map options) {
+ Map result = new HashMap<>();
+
+ try {
+ // 1. 解析SQL获取表结构信息
+ String tableSql = MapUtil.getString(options, "tableSql");
+ String ignorePrefix = MapUtil.getString(options, "ignorePrefix", "");
+
+ if (StringUtils.isBlank(tableSql)) {
+ log.error("SQL语句为空");
+ result.put("error", "SQL语句不能为空");
+ return result;
+ }
+
+ ClassInfo classInfo = TableParseUtil.parseTableSql(tableSql, ignorePrefix);
+ if (classInfo == null) {
+ log.error("解析SQL失败");
+ result.put("error", "解析SQL失败");
+ return result;
+ }
+
+ // 2. 设置生成参数
+ options.put("classInfo", classInfo);
+ options.put("tableName", classInfo.getTableName());
+ options.put("authorName", MapUtil.getString(options, "authorName", "AgileBoot"));
+
+ // 3. 根据模板配置生成代码
+ String templateConfig = getTemplateConfig();
+ JSONArray templateGroups = JSONArray.parseArray(templateConfig);
+
+ for (Object groupObj : templateGroups) {
+ JSONObject group = (JSONObject) groupObj;
+ String groupName = group.getString("group");
+ JSONArray templates = group.getJSONArray("templates");
+
+ for (Object templateObj : templates) {
+ JSONObject template = (JSONObject) templateObj;
+ String templateName = template.getString("name");
+
+ // 构建模板文件路径,按组别分目录
+ String templatePath = groupName + "/" + templateName + ".ftl";
+
+ // 生成代码
+ if (FreemarkerUtil.templateExists(templatePath)) {
+ String generatedCode = FreemarkerUtil.generateByTemplate(templatePath, options);
+ result.put(templateName, generatedCode);
+ } else {
+ log.warn("模板文件不存在: {}", templatePath);
+ result.put(templateName, "// 模板文件不存在: " + templatePath);
+ }
+ }
+ }
+
+ // 添加表名信息
+ result.put("tableName", classInfo.getTableName());
+
+ } catch (Exception e) {
+ log.error("生成代码失败", e);
+ result.put("error", "生成代码失败: " + e.getMessage());
+ }
+
+ return result;
+ }
}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/IGeneratorService.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/IGeneratorService.java
index db51814..54e7bca 100644
--- a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/IGeneratorService.java
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/service/IGeneratorService.java
@@ -1,5 +1,7 @@
package com.agileboot.codegen.service;
+import java.util.Map;
+
/**
* @Author cuiJiaWang
* @Create 2025-09-24 14:31
@@ -7,4 +9,11 @@ package com.agileboot.codegen.service;
public interface IGeneratorService {
String getTemplateConfig();
+ /**
+ * 根据参数生成代码
+ *
+ * @param options 生成参数
+ * @return 生成的代码Map
+ */
+ Map getResultByParams(Map options);
}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/FreemarkerUtil.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/FreemarkerUtil.java
new file mode 100644
index 0000000..d0b82cb
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/FreemarkerUtil.java
@@ -0,0 +1,59 @@
+package com.agileboot.codegen.util;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Freemarker工具类
+ */
+@Slf4j
+public class FreemarkerUtil {
+
+ private static Configuration configuration;
+
+ static {
+ configuration = new Configuration(Configuration.VERSION_2_3_31);
+ configuration.setClassForTemplateLoading(FreemarkerUtil.class, "/templates/code-generator");
+ configuration.setDefaultEncoding("UTF-8");
+ }
+
+ /**
+ * 根据模板生成代码
+ *
+ * @param templateName 模板名称
+ * @param dataModel 数据模型
+ * @return 生成的代码
+ */
+ public static String generateByTemplate(String templateName, Map dataModel) {
+ try {
+ Template template = configuration.getTemplate(templateName);
+ StringWriter writer = new StringWriter();
+ template.process(dataModel, writer);
+ return writer.toString();
+ } catch (IOException | TemplateException e) {
+ log.error("生成代码失败,模板: {}, 错误: {}", templateName, e.getMessage(), e);
+ return "// 生成失败: " + e.getMessage();
+ }
+ }
+
+ /**
+ * 检查模板是否存在
+ *
+ * @param templateName 模板名称
+ * @return 是否存在
+ */
+ public static boolean templateExists(String templateName) {
+ try {
+ configuration.getTemplate(templateName);
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/MapUtil.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/MapUtil.java
new file mode 100644
index 0000000..7620b36
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/MapUtil.java
@@ -0,0 +1,71 @@
+package com.agileboot.codegen.util;
+
+import java.util.Map;
+
+/**
+ * Map工具类
+ */
+public class MapUtil {
+
+ /**
+ * 从Map中获取String值
+ *
+ * @param map Map对象
+ * @param key 键
+ * @return String值
+ */
+ public static String getString(Map map, String key) {
+ if (map == null || key == null) {
+ return null;
+ }
+ Object value = map.get(key);
+ return value != null ? value.toString() : null;
+ }
+
+ /**
+ * 从Map中获取String值,带默认值
+ *
+ * @param map Map对象
+ * @param key 键
+ * @param defaultValue 默认值
+ * @return String值
+ */
+ public static String getString(Map map, String key, String defaultValue) {
+ String value = getString(map, key);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * 从Map中获取Boolean值
+ *
+ * @param map Map对象
+ * @param key 键
+ * @return Boolean值
+ */
+ public static Boolean getBoolean(Map map, String key) {
+ if (map == null || key == null) {
+ return null;
+ }
+ Object value = map.get(key);
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+ if (value instanceof String) {
+ return Boolean.parseBoolean((String) value);
+ }
+ return null;
+ }
+
+ /**
+ * 从Map中获取Boolean值,带默认值
+ *
+ * @param map Map对象
+ * @param key 键
+ * @param defaultValue 默认值
+ * @return Boolean值
+ */
+ public static Boolean getBoolean(Map map, String key, Boolean defaultValue) {
+ Boolean value = getBoolean(map, key);
+ return value != null ? value : defaultValue;
+ }
+}
diff --git a/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/TableParseUtil.java b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/TableParseUtil.java
new file mode 100644
index 0000000..ebdc4fd
--- /dev/null
+++ b/agileboot-system/wol-codegenerator/src/main/java/com/agileboot/codegen/util/TableParseUtil.java
@@ -0,0 +1,179 @@
+package com.agileboot.codegen.util;
+
+import com.agileboot.codegen.entity.ClassInfo;
+import com.agileboot.codegen.entity.FieldInfo;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 表解析工具类
+ */
+public class TableParseUtil {
+
+ /**
+ * 解析DDL SQL语句获取ClassInfo
+ *
+ * @param sql DDL SQL语句
+ * @param ignorePrefix 忽略前缀
+ * @return ClassInfo对象
+ */
+ public static ClassInfo parseTableSql(String sql, String ignorePrefix) {
+ if (StringUtils.isBlank(sql)) {
+ return null;
+ }
+
+ ClassInfo classInfo = new ClassInfo();
+ List fieldList = new ArrayList<>();
+
+ // 解析表名和注释
+ parseTableInfo(sql, classInfo, ignorePrefix);
+
+ // 解析字段信息
+ parseFieldInfo(sql, fieldList);
+
+ classInfo.setFieldList(fieldList);
+ return classInfo;
+ }
+
+ /**
+ * 解析表信息
+ */
+ private static void parseTableInfo(String sql, ClassInfo classInfo, String ignorePrefix) {
+ // 匹配表名的正则表达式
+ Pattern tablePattern = Pattern.compile("CREATE\\s+TABLE\\s+[`'\"]*([\\w_]+)[`'\"]*", Pattern.CASE_INSENSITIVE);
+ Matcher tableMatcher = tablePattern.matcher(sql);
+
+ if (tableMatcher.find()) {
+ String tableName = tableMatcher.group(1);
+ classInfo.setOriginTableName(tableName);
+
+ // 去掉前缀
+ String processedTableName = tableName;
+ if (StringUtils.isNotBlank(ignorePrefix) && tableName.startsWith(ignorePrefix)) {
+ processedTableName = tableName.substring(ignorePrefix.length());
+ }
+
+ classInfo.setTableName(processedTableName);
+ classInfo.setClassName(toCamelCase(processedTableName, true));
+ }
+
+ // 匹配表注释
+ Pattern commentPattern = Pattern.compile("COMMENT\\s*=\\s*['\"]([^'\"]*)['\"]", Pattern.CASE_INSENSITIVE);
+ Matcher commentMatcher = commentPattern.matcher(sql);
+ if (commentMatcher.find()) {
+ classInfo.setClassComment(commentMatcher.group(1));
+ }
+ }
+
+ /**
+ * 解析字段信息
+ */
+ private static void parseFieldInfo(String sql, List fieldList) {
+ // 匹配字段定义的正则表达式
+ Pattern fieldPattern = Pattern.compile(
+ "[`'\"]*([\\w_]+)[`'\"]*\\s+([\\w()]+).*?(?:COMMENT\\s*['\"]([^'\"]*)['\"])?",
+ Pattern.CASE_INSENSITIVE
+ );
+
+ String[] lines = sql.split("\n");
+ for (String line : lines) {
+ line = line.trim();
+ // 跳过非字段行
+ if (line.startsWith("CREATE") || line.startsWith("PRIMARY") ||
+ line.startsWith("KEY") || line.startsWith("INDEX") ||
+ line.startsWith(")") || line.isEmpty()) {
+ continue;
+ }
+
+ Matcher fieldMatcher = fieldPattern.matcher(line);
+ if (fieldMatcher.find()) {
+ FieldInfo fieldInfo = new FieldInfo();
+ String columnName = fieldMatcher.group(1);
+ String columnType = fieldMatcher.group(2);
+ String comment = fieldMatcher.group(3);
+
+ fieldInfo.setColumnName(columnName);
+ fieldInfo.setFieldName(toCamelCase(columnName, false));
+ fieldInfo.setFieldClass(getJavaType(columnType));
+ fieldInfo.setSwaggerClass(getSwaggerType(columnType));
+ fieldInfo.setFieldComment(comment != null ? comment : "");
+
+ fieldList.add(fieldInfo);
+ }
+ }
+ }
+
+ /**
+ * 转换为驼峰命名
+ *
+ * @param str 原字符串
+ * @param firstUpper 首字母是否大写
+ * @return 驼峰命名字符串
+ */
+ private static String toCamelCase(String str, boolean firstUpper) {
+ if (StringUtils.isBlank(str)) {
+ return str;
+ }
+
+ StringBuilder result = new StringBuilder();
+ String[] parts = str.toLowerCase().split("_");
+
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+ if (StringUtils.isNotBlank(part)) {
+ if (i == 0 && !firstUpper) {
+ result.append(part);
+ } else {
+ result.append(part.substring(0, 1).toUpperCase())
+ .append(part.substring(1));
+ }
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * 获取Java类型
+ */
+ private static String getJavaType(String mysqlType) {
+ String lowerType = mysqlType.toLowerCase();
+ if (lowerType.contains("bigint")) {
+ return "Long";
+ } else if (lowerType.contains("int") || lowerType.contains("tinyint")) {
+ return "Integer";
+ } else if (lowerType.contains("varchar") || lowerType.contains("text") || lowerType.contains("char")) {
+ return "String";
+ } else if (lowerType.contains("decimal") || lowerType.contains("double")) {
+ return "Double";
+ } else if (lowerType.contains("float")) {
+ return "Float";
+ } else if (lowerType.contains("date") || lowerType.contains("time")) {
+ return "Date";
+ } else {
+ return "String";
+ }
+ }
+
+ /**
+ * 获取Swagger类型
+ */
+ private static String getSwaggerType(String mysqlType) {
+ String lowerType = mysqlType.toLowerCase();
+ if (lowerType.contains("int") || lowerType.contains("tinyint")) {
+ return "integer";
+ } else if (lowerType.contains("varchar") || lowerType.contains("text") || lowerType.contains("char")) {
+ return "string";
+ } else if (lowerType.contains("decimal") || lowerType.contains("double") || lowerType.contains("float")) {
+ return "number";
+ } else if (lowerType.contains("date") || lowerType.contains("time")) {
+ return "string";
+ } else {
+ return "string";
+ }
+ }
+}