- JavaScript 95.2%
- HTML 2.4%
- Vue 1.3%
- Go 0.8%
- CSS 0.3%
| .gitea/workflows | ||
| .idea | ||
| build | ||
| frontend | ||
| frontend_dist | ||
| handler | ||
| router | ||
| service | ||
| static | ||
| templates | ||
| utils | ||
| .gitignore | ||
| a.txt | ||
| DataEngineerTools | ||
| DataEngineerTools-v1.60-c2c0101.7z | ||
| DataEngineerTools-v1.61-6b23b9d.7z | ||
| DataEngineerTools-v1.62-643482b.7z | ||
| DataEngineerTools-v1.63-a7e30f6.7z | ||
| DataEngineerTools.exe.gz | ||
| ddl.csv | ||
| go.mod | ||
| go.sum | ||
| HANDOVER.md | ||
| main.go | ||
| mock_server | ||
| mock_server.go | ||
| odps_projects.json | ||
| README.md | ||
DataEngineerTools 开发文档
本文档面向后续新增需求的开发者,用于统一开发规范、文件组织、前端风格和后端架构约定。 如果有复杂到不能遵守规范的情况,也可以详细说明后,经过我的判断可以超出规范。
一、项目概述
DataEngineerTools 是一个面向数据工程师的内部辅助工具集,基于 Go 1.25 + Gin 构建。
所有静态资源和 HTML 模板通过 embed.FS 打包进二进制,部署只需一个可执行文件,无需外部文件目录。
- 监听端口:
:8080 - 技术栈:Go + Gin(后端)+ Vue 3(CDN 引入,前端)+ HTML/CSS(原生)
- 部署方式:
go build产物单文件部署
二、核心约束(必须遵守)
⚠️ 以下是本项目最重要的硬性规范,新增任何需求都必须遵守。
一个需求 = 一套文件,命名一一对应
| 层次 | 文件数量 | 命名规则 | 示例 |
|---|---|---|---|
| 前端页面 | 1 个 HTML | templates/{name}.html |
templates/ddl.html |
| 后端 Handler | 1 个文件 | handler/{name}_handler.go |
handler/ddl_handler.go |
| 后端 Service | 1 个文件 | service/{name}_service.go |
service/ddl_service.go |
| 路由注册 | 在 router/router.go 中统一添加 |
— | — |
禁止 为同一个需求创建多个 Handler 文件或多个 Service 文件,也禁止拆分多个 HTML 页面。
简单需求可以纯前端实现
- 如果需求不涉及后端数据处理(如文本 Diff、纯计算工具),可以只创建 HTML,不创建 Handler 和 Service。
- 路由中仅注册页面 GET 路由即可:
r.GET("/diff", func(c *gin.Context) { c.HTML(http.StatusOK, "diff.html", nil) })
CSS / JS 可以多文件
static/css/和static/js/下可以创建多个文件,供各页面按需引入static/css/common.css是全局公共样式,不要在里面写页面专属样式- 页面专属样式写在对应 HTML 的
<style>标签内
三、目录结构
DataEngineerTools/
├── main.go # 入口:embed 打包并启动
├── go.mod
├── router/
│ └── router.go # 统一路由注册 + 依赖组装(唯一组装点)
├── handler/ # HTTP 层,每个需求一个文件
│ ├── check_handler.go
│ ├── format_handler.go
│ ├── ddl_handler.go
│ └── abcode_handler.go
├── service/ # 业务逻辑层,每个需求一个文件
│ ├── check_service.go
│ ├── format_service.go
│ ├── ddl_service.go
│ └── abcode_service.go
├── utils/
│ └── abcode/ # 独立算法包,不依赖 HTTP 层
│ ├── packet.go # 协议层
│ └── codec.go # 视觉层
├── templates/ # Gin HTML 模板(embed 打包)
│ ├── common.html # 公共组件(侧边栏 sidebar)
│ ├── index.html # 首页导航
│ └── {name}.html # 各需求页面
└── static/ # 前端静态资源(embed 打包)
├── css/
│ └── common.css # 全局公共样式
├── js/ # 各页面专属 JS(可选)
└── lib/ # 第三方库(只读,勿修改)
├── vue.global.js
├── highlight.min.js
├── all.min.css # Font Awesome 图标
└── ...
四、新增需求开发流程
步骤一:判断是否需要后端
| 需求类型 | 是否需要后端 |
|---|---|
| 纯文本处理、格式转换(无需外部数据) | ❌ 纯前端即可 |
| 需要调用外部 API / 访问文件系统 / 复杂算法 | ✅ 需要后端 |
步骤二:创建文件(仅有后端时)
# 创建 Service(业务逻辑)
touch service/{name}_service.go
# 创建 Handler(HTTP 层)
touch handler/{name}_handler.go
步骤三:创建前端页面
touch templates/{name}.html
参考 templates/format.html 作为最简单的模板起点。
步骤四:注册路由(router/router.go)
// 1. 页面路由
r.GET("/{name}", func(c *gin.Context) { c.HTML(http.StatusOK, "{name}.html", nil) })
// 2. 初始化 Service 和 Handler(如有后端)
{name}Service := service.New{Name}Service()
{name}Handler := handler.New{Name}Handler({name}Service)
// 3. API 路由(如有后端)
api.POST("/{name}/action", {name}Handler.SomeAction)
步骤五:更新侧边栏(templates/common.html)
在 sidebar-menu 区域添加导航项:
<a href="/{name}" class="menu-item" id="nav-{name}">
<div class="menu-icon"><i class="fas fa-icon-name"></i></div>
<div class="menu-text">功能名称</div>
</a>
同时在 common.html 底部的 JS 中添加高亮判断:
} else if (path.includes("{name}")) {
document.getElementById("nav-{name}").classList.add("active");
}
五、后端开发规范
5.1 Handler 职责边界
Handler 只负责:
- 绑定入参(
c.ShouldBindJSON) - 调用 Service 方法
- 返回 HTTP 响应(
c.JSON)
Handler 不允许包含任何业务逻辑。
func (h *DDLHandler) ParseDDL(c *gin.Context) {
var request map[string]string
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的JSON格式"})
return
}
// 业务逻辑全部委托 Service
csv := h.ddlService.ParseDDLToCSV(request["ddl"])
c.JSON(http.StatusOK, gin.H{"csv": csv})
}
5.2 Service 职责边界
Service 只负责:业务逻辑、算法、外部调用。
Service 不允许导入 github.com/gin-gonic/gin,入参出参均为纯 Go 类型。
5.3 构造函数 + 依赖注入
// Service
type {Name}Service struct { /* 依赖字段 */ }
func New{Name}Service() *{Name}Service { return &{Name}Service{...} }
// Handler
type {Name}Handler struct { {name}Service *service.{Name}Service }
func New{Name}Handler(s *service.{Name}Service) *{Name}Handler { return &{Name}Handler{{name}Service: s} }
5.4 错误处理规范
- 参数绑定失败 →
400 Bad Request+{"error": "描述"} - 业务处理失败 →
500 Internal Server Error+{"error": "描述"} - 特殊情况:若前端需要统一用响应体的字段判断结果(不依赖 HTTP 状态码),可返回
200 OK+{"message": "错误描述"},但需在代码注释中说明原因
5.5 并发安全
- Service 中若有内存共享状态(如历史记录缓存),必须使用
sync.RWMutex - 读操作用
RLock()/RUnlock(),写操作用Lock()/Unlock() - 对外暴露共享数据时,返回拷贝而非原始引用
5.6 请求体大小限制
接收文件或大数据时,必须在 Handler 中限制请求体大小:
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 50<<20) // 50MB
六、前端开发规范
6.1 整体页面结构模板
所有功能页面遵循统一的 HTML 骨架:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据服务工具集 - {功能名称}</title>
<!-- Vue 3(必须) -->
<script src="/lib/vue.global.js"></script>
<!-- 代码高亮(按需引入) -->
<link rel="stylesheet" href="/lib/atom-one-dark.min.css">
<script src="/lib/highlight.min.js"></script>
<!-- Font Awesome 图标(必须) -->
<link rel="stylesheet" href="/lib/all.min.css">
<!-- 公共样式(必须) -->
<link rel="stylesheet" href="/css/common.css">
<style>
/* 当前页面专属样式写这里 */
</style>
</head>
<body>
<div id="app" class="app-layout">
<!-- 公共侧边栏(必须) -->
[[template "sidebar"]]
<div class="main-content">
<!-- 页面主体内容 -->
</div>
</div>
<!-- 暗色模式切换按钮(必须复制此段) -->
<div class="theme-toggle" id="themeToggle">
<svg class="sun-icon" viewBox="0 0 24 24"><path d="M12 17C14.7614 17 17 14.7614 17 12C17 9.23858 14.7614 7 12 7C9.23858 7 7 9.23858 7 12C7 14.7614 9.23858 17 12 17Z"/><path d="M12 1V3M12 21V23M23 12H21M3 12H1M20.07 4.93L18.66 6.34M5.34 17.66L3.93 19.07M20.07 19.07L18.66 17.66M5.34 6.34L3.93 4.93"/></svg>
<svg class="moon-icon" viewBox="0 0 24 24"><path d="M21 12.79C20.8427 14.4922 20.2039 16.1144 19.1582 17.4668C18.1126 18.8192 16.7035 19.8458 15.0957 20.4265C13.4879 21.0073 11.7472 21.1181 10.0806 20.7461C8.41394 20.3741 6.88989 19.5345 5.67879 18.3234C4.46769 17.1123 3.62814 15.5883 3.25612 13.9216C2.8841 12.2549 2.99495 10.5143 3.57568 8.90647C4.15642 7.29864 5.18304 5.88953 6.53544 4.84389C7.88783 3.79826 9.51004 3.15941 11.212 3C9.81902 4.80958 9.15354 7.02094 9.3534 9.23622C9.55325 11.4515 10.6038 13.5001 12.2976 14.9111C13.9914 16.3221 16.1884 16.9999 18.412 16.8061C20.6356 16.6123 22.6903 15.5653 24 14"/></svg>
</div>
<script>
// 暗色模式逻辑(必须复制此段)
document.addEventListener('DOMContentLoaded', function() {
if (localStorage.getItem('darkMode') === 'true') {
document.body.classList.add('dark-mode');
}
document.getElementById('themeToggle').addEventListener('click', function() {
document.body.classList.toggle('dark-mode');
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
});
});
</script>
</body>
</html>
6.2 设计风格(Design Token)
所有颜色、间距必须使用 CSS 变量,禁止硬编码颜色值:
| CSS 变量 | 亮色值 | 暗色值 | 用途 |
|---|---|---|---|
--primary |
#6366F1 |
#818CF8 |
主色调(按钮、高亮、标题) |
--secondary |
#10B981 |
#34D399 |
辅助色 |
--background |
#F8FAFC |
0F172A |
页面背景 |
--surface |
#FFFFFF |
#1E293B |
卡片/面板背景 |
--text-primary |
#1E293B |
#F1F5F9 |
主要文字 |
--text-secondary |
#64748B |
#94A3B8 |
次要文字、说明 |
/* ✅ 正确 */
color: var(--primary);
background: var(--surface);
/* ❌ 错误 */
color: #6366F1;
background: #FFFFFF;
6.3 字体规范
| 用途 | 字体 |
|---|---|
| 界面文字 | 'Inter', system-ui, sans-serif |
| 代码 / 输入框 / 结果展示 | 'JetBrains Mono', monospace |
6.4 暗色模式适配
- 所有颜色使用 CSS 变量(自动适配)
- 暗色专属样式使用
.dark-mode前缀覆盖:
.dark-mode .panel {
box-shadow: 0 8px 24px -6px rgba(0, 0, 0, 0.2);
}
- 暗色偏好存储在
localStorage.getItem('darkMode'),页面初始化时读取
6.5 通用布局组件(直接复用 common.css)
| 类名 | 用途 |
|---|---|
.app-layout |
根容器,Flex 横向布局(侧边栏 + 主内容) |
.main-content |
主内容区,自动留出侧边栏宽度 |
.panel |
卡片容器,白色圆角阴影 |
.panel-header |
面板标题区(含 h1 和描述 p) |
.input-container |
带边框圆角的输入区域,focus 时显示主色边框 |
.btn .btn-primary |
主操作按钮 |
.btn-copy |
复制按钮(半透明主色背景) |
.history-list |
历史记录列表容器 |
.history-item |
历史记录单项 |
.loading-spinner |
圆形加载动画 |
.status-bar |
底部状态栏 |
6.6 布局模式
左右分栏(主要布局):左侧输入区 + 右侧结果区,参考 check.html / ddl.html:
.original-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 三列均分 */
gap: 24px;
height: calc(100vh - 48px);
}
单列居中(简单工具):参考 format.html:
.format-numbers-container {
max-width: 1000px;
margin: 0 auto;
}
6.7 Vue 3 使用规范
- 使用 CDN 引入的
vue.global.js(无需构建工具) - 使用 Options API 风格(与现有页面保持一致)
- 模板分隔符为
[[ ]](Gin 已配置,避免与 Vue{{ }}冲突)
<div id="app">
<!-- Vue 模板使用 {{ }} -->
<p>{{ message }}</p>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return { message: 'Hello' }
}
}).mount('#app');
</script>
注意:Gin 模板层(服务端渲染)使用
[[ ]],Vue 模板层(客户端渲染)使用{{ }},二者不冲突。
6.8 API 请求规范
前端统一使用 fetch 发送请求:
// POST JSON
const res = await fetch('/api/{name}/action', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await res.json();
6.9 图标库
使用 Font Awesome 6(已打包在 /lib/all.min.css),图标列表参考 fontawesome.com/icons:
<i class="fas fa-database"></i> <!-- 实心图标 -->
<i class="far fa-copy"></i> <!-- 线框图标 -->
七、现有功能模块一览
| 功能名称 | 路由 | 是否有后端 | 文件 |
|---|---|---|---|
| 首页导航 | / |
❌ | templates/index.html |
| 数据服务查询 | /check |
✅ | check_handler.go / check_service.go |
| SQL 格式化工具 | /format |
✅ | format_handler.go / format_service.go |
| DDL 解析工具 | /ddl |
✅ | ddl_handler.go / ddl_service.go |
| ABCode 生码 | /abcode-encode |
✅ | abcode_handler.go / abcode_service.go |
| ABCode 解码 | /abcode-decode |
✅ | 同上 |
| 文档比对(Diff) | /diff |
❌ | templates/diff.html |
八、主要依赖
| 依赖包 | 用途 |
|---|---|
github.com/gin-gonic/gin |
HTTP Web 框架 |
github.com/klauspost/compress/zstd |
Zstandard 压缩 |
github.com/klauspost/reedsolomon |
Reed-Solomon 纠错编码 |
github.com/pquerna/otp |
TOTP 动态口令 |
golang.org/x/crypto |
PBKDF2 密钥派生 |
九、待办 / 未完成功能
以下功能已在首页导航或侧边栏展示,但路由和实现尚未完成,后续开发时参考本文档规范补全:
| 功能 | 规划路由 | 说明 |
|---|---|---|
| 并发测试 | /concurrent |
模拟多线程并发请求数据服务 |
| 数据转换工具 | /insert |
数据转 INSERT 语句(MySQL/Hologres/MaxCompute) |
| SQL 查询工具 | /sql |
JDBC 直连执行 SQL(MySQL/PostgreSQL) |