From cef0488a88572725b956c22ec9760ce23c41397e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moshow=E9=83=91=E9=94=B4?= Date: Wed, 9 Sep 2026 23:37:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=A4=E6=B3=95=E7=BC=96=E7=A8=8B=C2=B7?= =?UTF-8?q?=E9=9D=9E=E9=81=97=E4=BC=A0=E6=89=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 20 ++- .../controller/CodeGenController.java | 10 +- .../service/impl/CodeGenServiceImpl.java | 5 + .../service/impl/ZipServiceImpl.java | 24 +++- .../system/generator/util/HeritageUtil.java | 131 ++++++++++++++++++ src/main/resources/application-dev.yml | 4 +- src/main/resources/template.json | 10 +- 7 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/softdev/system/generator/util/HeritageUtil.java diff --git a/README.md b/README.md index 90447fa..e4bba80 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ ![image](https://img.shields.io/badge/SpringBoot3-%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-blue.svg) ![image](https://img.shields.io/badge/Freemarker-%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-blue.svg) ![image](https://img.shields.io/badge/CodeGenerator-%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-blue.svg) +[![非遗](https://img.shields.io/badge/%E9%9D%9E%E9%81%97-%E5%8F%A4%E6%B3%95%E7%BC%96%E7%A8%8B%E4%BC%A0%E6%89%BF%E8%80%85-8B4513.svg)](#description) [![Java CI with Maven](https://github.com/moshowgame/SpringBootCodeGenerator/actions/workflows/maven.yml/badge.svg)](https://github.com/moshowgame/SpringBootCodeGenerator/actions/workflows/maven.yml) # Author @@ -34,11 +35,20 @@ > 📦 **One-click ZIP download** — package all generated code into a structured ZIP in one click\ > 📦 **一键 ZIP 打包下载** — 按模板分组自动归档,告别逐个文件复制 -> 🙏 Thanks for your continued support! BeJSON once peaked at 1.5K daily PV 👀, and now maintains a steady flow of around 600 visits — plus 2K+ GitHub Stars ✨. Your feedback remains our greatest motivation to keep improving! -> ❤️ 感谢大家一直以来的支持!BeJSON 曾创下日均访问量 1.5K 👀 的高峰,目前稳定在约 600 左右,GitHub Star 数也已突破 2K ✨。你们的反馈始终是我们不断前进的最大动力! +> 🏺 In the age of AI vibe coding, every line of these templates is still handcrafted — traditional code craft, certified intangible cultural heritage\ +> 🏺 AI vibe coding 时代,本项目的模板依然坚持手搓:开启「🏺 古法印章」,生成的每份代码都会带上一枚非遗传承手作印章,ZIP 包内附《手工代码认证书》——致敬每一位古法编程非遗传承者! -> 🌈 Wishing everyone balance, health, and success — may your code be bug-free and your coffee strong ☕\ -> 💬 祝大家工作顺利,生活平衡,身体健康,步步高升,代码无 bug,咖啡够劲! +> ### 🐺 写在 AI 时代 +> +> 大狼狗代码生成器诞生于 2018 年,彼时 AI 写代码还是科幻。数万开发者使用过它,数千颗 Star 见证了它的成长。 +> +> 如今 AI 浪潮滚滚而来,我们选择不追逐风口,而是回归本质: +> +> - **工具的价值**,在于服务人,而非替代人; +> - **代码的价值**,在于解决问题,而非堆砌行数; +> - **开发者的价值**,在于思考与判断,而非手速与复制。 +> +> 感谢每一位使用过大狼狗的朋友。愿你在 AI 时代,既有工具的效率,也有匠人的定力。🙏 > 📬 Feel free to submit issues, share useful templates, or contribute your brilliant ideas via PR\ > 🤝 欢迎提交问题、分享常用模板,或将你的灵感通过 PR 实现! @@ -309,7 +319,7 @@ ResultVo.error(message); | 更新日期 | 更新内容 | | :--------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 2026.09.09 | 🎨 前端全面重构为 jQuery3 + Bootstrap 5.3 + CodeMirror 5:全新 `newui3` 现代化界面,`data-bs-theme` 原生深/浅色模式(CodeMirror 双主题跟随),输出区按模板类型动态切换语法高亮(Java/XML/SQL/JSON/JS);新增 `app.js` 独立命名空间 `CodeGenApp`(与旧版 main.js 零冲突),补齐本地 mode 文件(sql/clike/xml/javascript);新增🏺古法印章开关(isHeritage,致敬古法编程非遗传承者);模板选择区改为组内联布局,`template.json` 支持 `tier` 字段分级(main 常驻 / legacy 折叠),折叠与选中状态 localStorage 记忆;修复 JDK23+ 下 Lombok 注解处理不生效的编译问题; | +| 2026.09.09 | 🎨 前端全面重构为 jQuery3 + Bootstrap 5.3 + CodeMirror 5:全新 `newui3` 现代化界面,`data-bs-theme` 原生深/浅色模式(CodeMirror 双主题跟随),输出区按模板类型动态切换语法高亮(Java/XML/SQL/JSON/JS);新增 `app.js` 独立命名空间 `CodeGenApp`(与旧版 main.js 零冲突),补齐本地 mode 文件(sql/clike/xml/javascript);新增🏺古法印章开关(isHeritage,致敬古法编程非遗传承者);模板选择区改为组内联布局,`template.json` 支持 `tier` 字段分级(main 常驻 / legacy 折叠),折叠与选中状态 localStorage 记忆;修复 JDK23+ 下 Lombok 注解处理不生效的编译问题;🏺非遗传承后端落地:`HeritageUtil` 按内容嗅探+后缀适配注释语法盖章(Java/JS/QVS 用 `//`,SQL 用 `--`,YAML 用 `#`,XML/Vue/HTML 在声明后插 ``,JSON 不盖章),ZIP 首条目附《手工代码认证书》;修正 `template.json` 中 `swagger-ui`/`beanutil`/`qliksense` 三个模板 fileName 后缀与实际内容类型不符的问题,并修复 ZIP 包误将 `tableName` 字段打成伪文件的 bug; | | 2026.06.02 | 📦 新增"一键 ZIP 打包下载"功能:在生成按钮旁新增独立下载按钮,按模板 group 自动分目录打包成 ZIP;
支持 `template.json` 中 `fileName` 字段(含 `${className}` 占位符),智能推断文件后缀; | | 2025.12.09 | 优化Mybatis和Mybatis-Plus模板 | | 2025.12.08 | 引入单元测试和JaCoCo测试覆盖率,优化代码覆盖率 [UNIT\_TEST\_DOCUMENT.md](UNIT_TEST_DOCUMENT.md) | diff --git a/src/main/java/com/softdev/system/generator/controller/CodeGenController.java b/src/main/java/com/softdev/system/generator/controller/CodeGenController.java index 636e349..98fbebc 100644 --- a/src/main/java/com/softdev/system/generator/controller/CodeGenController.java +++ b/src/main/java/com/softdev/system/generator/controller/CodeGenController.java @@ -6,6 +6,8 @@ import com.softdev.system.generator.entity.dto.ParamInfo; import com.softdev.system.generator.entity.vo.ResultVo; import com.softdev.system.generator.service.CodeGenService; import com.softdev.system.generator.service.ZipService; +import com.softdev.system.generator.util.HeritageUtil; +import com.softdev.system.generator.util.MapUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -66,7 +68,7 @@ public class CodeGenController { result.getFileNameTemplates(), result.getGroupByTemplate(), zipName, - buildContext(result) + buildContext(result, paramInfo.getOptions()) ); String encoded = URLEncoder.encode(zipName + ".zip", StandardCharsets.UTF_8) @@ -95,12 +97,14 @@ public class CodeGenController { } /** - * 构造 ZIP 解析占位符所需的上下文 + * 构造 ZIP 解析占位符所需的上下文(含非遗传承印章开关与作者) */ - private Map buildContext(CodeGenResult result) { + private Map buildContext(CodeGenResult result, Map options) { Map ctx = new HashMap<>(); ctx.put("className", result.getClassName() == null ? "" : result.getClassName()); ctx.put("tableName", result.getTableName() == null ? "" : result.getTableName()); + ctx.put("isHeritage", HeritageUtil.isEnabled(options)); + ctx.put("author", MapUtil.getString(options, "authorName")); return ctx; } } diff --git a/src/main/java/com/softdev/system/generator/service/impl/CodeGenServiceImpl.java b/src/main/java/com/softdev/system/generator/service/impl/CodeGenServiceImpl.java index 311d19f..73ffa8d 100644 --- a/src/main/java/com/softdev/system/generator/service/impl/CodeGenServiceImpl.java +++ b/src/main/java/com/softdev/system/generator/service/impl/CodeGenServiceImpl.java @@ -12,6 +12,7 @@ import com.softdev.system.generator.service.TemplateService; import com.softdev.system.generator.service.parser.JsonParserService; import com.softdev.system.generator.service.parser.SqlParserService; import com.softdev.system.generator.util.FreemarkerUtil; +import com.softdev.system.generator.util.HeritageUtil; import com.softdev.system.generator.util.MapUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -70,6 +71,7 @@ public class CodeGenServiceImpl implements CodeGenService { generatedCode.put("tableName", MapUtil.getString(params, "tableName")); + boolean heritage = HeritageUtil.isEnabled(params); JSONArray parentTemplates = templateService.getAllTemplates(); for (int i = 0; i < parentTemplates.size(); i++) { JSONObject parentTemplateObj = parentTemplates.getJSONObject(i); @@ -81,6 +83,9 @@ public class CodeGenServiceImpl implements CodeGenService { String templateName = childTemplate.getString("name"); String templatePath = group + "/" + templateName + ".ftl"; String generatedText = FreemarkerUtil.processString(templatePath, params); + if (heritage) { + generatedText = HeritageUtil.stamp(childTemplate.getString("fileName"), generatedText); + } generatedCode.put(templateName, generatedText); fileNameTemplates.put(templateName, childTemplate.getString("fileName")); groupByTemplate.put(templateName, group); diff --git a/src/main/java/com/softdev/system/generator/service/impl/ZipServiceImpl.java b/src/main/java/com/softdev/system/generator/service/impl/ZipServiceImpl.java index 08023a4..8606de0 100644 --- a/src/main/java/com/softdev/system/generator/service/impl/ZipServiceImpl.java +++ b/src/main/java/com/softdev/system/generator/service/impl/ZipServiceImpl.java @@ -1,6 +1,7 @@ package com.softdev.system.generator.service.impl; import com.softdev.system.generator.service.ZipService; +import com.softdev.system.generator.util.HeritageUtil; import com.softdev.system.generator.util.ZipFileNameResolver; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -9,8 +10,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -40,10 +43,21 @@ public class ZipServiceImpl implements ZipService { Set usedPaths = new HashSet<>(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { zos.setLevel(java.util.zip.Deflater.BEST_SPEED); + // 🏺 非遗传承:手工代码认证书作为 ZIP 首个条目 + if (HeritageUtil.isEnabled(context)) { + List templateNames = generatedCode.keySet().stream() + .filter(name -> !"tableName".equals(name)) + .collect(Collectors.toList()); + String certificate = HeritageUtil.buildCertificate( + stringOf(context, "className"), stringOf(context, "tableName"), + stringOf(context, "author"), templateNames); + writeEntry(zos, "手工代码认证书.txt", certificate); + } for (Map.Entry entry : generatedCode.entrySet()) { String templateName = entry.getKey(); String content = entry.getValue(); - if (content == null) { + // tableName 是返回给前端的结构化字段,不是代码文件 + if (content == null || "tableName".equals(templateName)) { continue; } String fileNameTpl = fileNameTemplates == null ? null : fileNameTemplates.get(templateName); @@ -67,6 +81,14 @@ public class ZipServiceImpl implements ZipService { zos.closeEntry(); } + private String stringOf(Map context, String key) { + if (context == null || key == null) { + return ""; + } + Object value = context.get(key); + return value == null ? "" : value.toString(); + } + /** * 构造 entry 路径;遇到重名时加 _1 / _2 ... */ diff --git a/src/main/java/com/softdev/system/generator/util/HeritageUtil.java b/src/main/java/com/softdev/system/generator/util/HeritageUtil.java new file mode 100644 index 0000000..0c515f5 --- /dev/null +++ b/src/main/java/com/softdev/system/generator/util/HeritageUtil.java @@ -0,0 +1,131 @@ +package com.softdev.system.generator.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +/** + * 🏺 非遗传承工具:为生成的代码加盖手作印章,并生成手工代码认证书 + *

+ * 在 AI vibe coding 的时代,每一行模板都坚持手搓,致敬古法编程非遗传承者。 + * + * @author zhengkai.blog.csdn.net + */ +public final class HeritageUtil { + + private static final String LINE_1 = "🏺 古法编程·非遗传承 | Traditional Code Craft since 2018"; + private static final String LINE_2 = "本代码由手作模板工艺生成,非 AI vibe coding 之作 | Handcrafted, not AI-generated."; + + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private HeritageUtil() { + } + + /** + * isHeritage 是否启用;未显式关闭(false)时默认开启 + */ + public static boolean isEnabled(Map params) { + Object flag = params == null ? null : params.get("isHeritage"); + return flag == null || Boolean.TRUE.equals(flag) || "true".equalsIgnoreCase(String.valueOf(flag)); + } + + /** + * 在文件头部盖手作印章;JSON 等不支持注释的文件类型原样返回 + * + * @param fileName 模板配置中的目标文件名(用于推断注释语法,可能不准,仅作兜底) + * @param content 生成的代码文本 + */ + public static String stamp(String fileName, String content) { + if (content == null || content.isEmpty()) { + return content; + } + // 内容嗅探优先:模板配置的 fileName 可能与实际内容类型不符(如 mapper 标为 .java 实为 XML) + String trimmed = content.trim(); + if (trimmed.startsWith("<")) { + return stampAsXmlComment(content); + } + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + return content; + } + switch (extensionOf(fileName)) { + case "java": + case "jjs": + case "js": + case "qvs": + return "// " + LINE_1 + "\n// " + LINE_2 + "\n" + content; + case "sql": + return "-- " + LINE_1 + "\n-- " + LINE_2 + "\n" + content; + case "yml": + case "yaml": + case "properties": + case "txt": + return "# " + LINE_1 + "\n# " + LINE_2 + "\n" + content; + case "xml": + case "html": + case "vue": + case "md": + return stampAsXmlComment(content); + default: + return content; + } + } + + /** + * 手工代码认证书(ZIP 彩蛋) + */ + public static String buildCertificate(String className, String tableName, String author, List templateNames) { + StringBuilder sb = new StringBuilder(); + sb.append("🏺 手工代码认证书\n"); + sb.append("CERTIFICATE OF HANDCRAFTED CODE\n"); + sb.append("================================================================\n\n"); + sb.append("兹证明\n\n"); + sb.append(" ").append(safe(className)).append("(表 ").append(safe(tableName)).append(")的 ") + .append(templateNames == null ? 0 : templateNames.size()).append(" 份代码\n"); + sb.append(" 均由「古法编程·非遗传承」工艺生成。\n\n"); + sb.append("模板清单:\n"); + if (templateNames != null) { + for (String name : templateNames) { + sb.append(" · ").append(name).append('\n'); + } + } + sb.append('\n'); + sb.append("工匠:").append(author == null || author.trim().isEmpty() ? "Anonymous" : author.trim()).append('\n'); + sb.append("时间:").append(LocalDateTime.now().format(TIME_FORMATTER)).append('\n'); + sb.append("工艺:Freemarker 手作模板 + 自研/JSqlParser 双解析引擎\n\n"); + sb.append("在 AI vibe coding 的时代,\n"); + sb.append("每一行模板都坚持手搓。\n"); + sb.append("—— 致敬所有古法编程非遗传承者 🙏\n\n"); + sb.append("Powered by SpringBootCodeGenerator (2018 - 至今)\n"); + sb.append("https://github.com/moshowgame/SpringBootCodeGenerator/\n"); + return sb.toString(); + } + + /** + * XML 声明必须位于文档最前,印章插在声明之后 + */ + private static String stampAsXmlComment(String content) { + if (content.startsWith(""); + if (end > 0) { + String prolog = content.substring(0, end + 2) + "\n"; + return prolog + "\n" + content.substring(end + 2); + } + } + return "\n" + content; + } + + private static String extensionOf(String fileName) { + if (fileName == null) { + return ""; + } + String lower = fileName.toLowerCase(); + int dot = lower.lastIndexOf('.'); + return dot < 0 ? "" : lower.substring(dot + 1); + } + + private static String safe(String value) { + return value == null ? "" : value; + } + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index fdcf712..3a34549 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -51,11 +51,11 @@ OEM: header: SQL转Java JPA、MYBATIS实现类代码生成平台 keywords: sql转实体类,sql转DAO,SQL转service,SQL转JPA实现,SQL转MYBATIS实现 title: 大狼狗代码生成器 - slogan: 👐 Free your hands from boring CRUD—let the code write itself! ⚡ + slogan: 🏺 古法编程·非遗传承 | Free your hands from boring CRUD! ⚡ description:

💻 SpringBootCodeGenerator,又名 `大狼狗代码生成器`🐺🐶,支持 SQL 一键转 JAVA/JPA/Mybatis/MybatisPlus 等多种模板,轻松搞定 CRUD,彻底解放双手 ✋!只需提供 DDL、SELECT SQL 或简单 JSON 👉 即可生成 生成JPA/JdbcTemplate/Mybatis/MybatisPlus/BeetlSQL/CommonMapper 等代码模板。

🔥🔥🔥 全新 JSqlParser 引擎上线,强力支持 DDL CREATE SQL与 SELECT SQL 解析!欢迎体验 & 反馈 💬!

👨‍💻 面向开发者的高效利器,已服务数万工程师,欢迎 Star ⭐、Fork 🍴、提 Issue 💬,一起打造更强大的代码生成平台!

author: zhengkai.blog.csdn.net packageName: com.software.system - copyright: ✨ Powered by Moshow郑锴 — may the holy light guide your code, your coffee, and your commits! ⚡🧙‍♂️💻 + copyright: 🏺 Powered by Moshow郑锴 — 愿你在 AI 时代,既有工具的效率,也有匠人的定力 🙏 returnUtilSuccess: ResponseUtil.success returnUtilFailure: ResponseUtil.error outputStr: http://zhengkai.blog.csdn.net diff --git a/src/main/resources/template.json b/src/main/resources/template.json index 7667f83..8770e99 100644 --- a/src/main/resources/template.json +++ b/src/main/resources/template.json @@ -3,9 +3,9 @@ "tier": "main", "templates": [{ "id": "10", - "name": "swagger-ui", - "description": "swagger-ui", - "fileName": "swagger-ui.json" + "name": "swagger-ui", + "description": "swagger-ui", + "fileName": "swagger-ui.java" }, { "id": "11", @@ -183,7 +183,7 @@ "id": "70", "name": "beanutil", "description": "beanutil", - "fileName": "${className}BeanUtil.txt" + "fileName": "${className}BeanUtil.java" }, { "id": "71", @@ -313,7 +313,7 @@ "id": "201", "name": "qliksense", "description": "qlik sense", - "fileName": "qliksense.txt" + "fileName": "qliksense.qvs" }] }, {