古法编程·非遗传承

This commit is contained in:
Moshow郑锴
2026-09-09 23:37:24 +08:00
parent d6a09de78b
commit cef0488a88
7 changed files with 188 additions and 16 deletions

View File

@@ -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;<br>支持 `template.json` 中 `fileName` 字段(含 `${className}` 占位符),智能推断文件后缀; |
| 2025.12.09 | 优化Mybatis和Mybatis-Plus模板 |
| 2025.12.08 | 引入单元测试和JaCoCo测试覆盖率,优化代码覆盖率 [UNIT\_TEST\_DOCUMENT.md](UNIT_TEST_DOCUMENT.md) |

View File

@@ -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<String, Object> buildContext(CodeGenResult result) {
private Map<String, Object> buildContext(CodeGenResult result, Map<String, Object> options) {
Map<String, Object> 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;
}
}

View File

@@ -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);

View File

@@ -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<String> usedPaths = new HashSet<>();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
zos.setLevel(java.util.zip.Deflater.BEST_SPEED);
// 🏺 非遗传承:手工代码认证书作为 ZIP 首个条目
if (HeritageUtil.isEnabled(context)) {
List<String> 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<String, String> 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<String, Object> context, String key) {
if (context == null || key == null) {
return "";
}
Object value = context.get(key);
return value == null ? "" : value.toString();
}
/**
* 构造 entry 路径;遇到重名时加 _1 / _2 ...
*/

View File

@@ -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;
/**
* 🏺 非遗传承工具:为生成的代码加盖手作印章,并生成手工代码认证书
* <p>
* 在 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<String, Object> 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<String> 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("<?xml")) {
int end = content.indexOf("?>");
if (end > 0) {
String prolog = content.substring(0, end + 2) + "\n";
return prolog + "<!--\n " + LINE_1 + "\n " + LINE_2 + "\n-->\n" + content.substring(end + 2);
}
}
return "<!--\n " + LINE_1 + "\n " + LINE_2 + "\n-->\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;
}
}

View File

@@ -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: <p>💻 SpringBootCodeGenerator,又名 `大狼狗代码生成器`🐺🐶,支持 SQL 一键转 JAVA/JPA/Mybatis/MybatisPlus 等多种模板,轻松搞定 CRUD,彻底解放双手 ✋!只需提供 DDL、SELECT SQL 或简单 JSON 👉 即可生成 生成JPA/JdbcTemplate/Mybatis/MybatisPlus/BeetlSQL/CommonMapper 等代码模板。</p><p>🔥🔥🔥 全新 JSqlParser 引擎上线,强力支持 DDL CREATE SQL与 SELECT SQL 解析!欢迎体验 & 反馈 💬!</p><p>👨‍💻 面向开发者的高效利器,已服务数万工程师,欢迎 Star ⭐、Fork 🍴、提 Issue 💬,一起打造更强大的代码生成平台!</p>
author: zhengkai.blog.csdn.net
packageName: com.software.system
copyright: ✨ Powered by <a href="https://zhengkai.blog.csdn.net" target="_blank">Moshow郑锴</a> — may the holy light guide your code, your coffee, and your commits! ⚡🧙‍♂️💻
copyright: 🏺 Powered by <a href="https://zhengkai.blog.csdn.net" target="_blank">Moshow郑锴</a> — 愿你在 AI 时代,既有工具的效率,也有匠人的定力 🙏
returnUtilSuccess: ResponseUtil.success
returnUtilFailure: ResponseUtil.error
outputStr: http://zhengkai.blog.csdn.net

View File

@@ -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"
}]
},
{