This commit is contained in:
杭州明婳科技
2026-04-30 23:26:14 +08:00
commit 2dfcbbbb22
76 changed files with 17478 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
package main
import (
"fmt"
"os"
"strings"
"text/template"
"time"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
func createProject(name string) {
if _, err := os.Stat(name); !os.IsNotExist(err) {
fmt.Printf("目录 %s 已存在\n", name)
return
}
// 创建目录结构
dirs := []string{
name,
name + "/config",
name + "/handler",
name + "/model",
name + "/repository",
name + "/service",
name + "/middleware",
name + "/public",
name + "/logs",
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
fmt.Printf("创建目录失败: %s\n", err)
return
}
}
// 获取模块路径
module := name
if len(os.Args) > 3 && os.Args[3] == "--module" && len(os.Args) > 4 {
module = os.Args[4]
}
caser := cases.Title(language.English)
data := TemplateData{
Package: caser.String(name),
Name: caser.String(name),
NameLower: strings.ToLower(name),
Module: module,
Year: time.Now().Year(),
}
// 创建文件
files := map[string]string{
name + "/main.go": templates.Main,
name + "/config.yaml": templates.Config,
name + "/go.mod": fmt.Sprintf(templates.GoMod, module),
name + "/Makefile": templates.Makefile,
name + "/.gitignore": templates.Gitignore,
name + "/handler/home.go": templates.Handler,
}
for path, content := range files {
tmpl, err := template.New(path).Parse(content)
if err != nil {
fmt.Printf("解析模板失败: %s\n", err)
return
}
file, err := os.Create(path)
if err != nil {
fmt.Printf("创建文件失败: %s\n", err)
return
}
defer file.Close()
if err := tmpl.Execute(file, data); err != nil {
fmt.Printf("写入文件失败: %s\n", err)
return
}
}
fmt.Printf("✓ 项目 %s 创建成功\n", name)
fmt.Println("\n下一步:")
fmt.Printf(" cd %s\n", name)
fmt.Println(" go mod tidy")
fmt.Println(" go run main.go")
}
func makeFile(fileType, name string) {
name = strings.ToLower(name)
caser := cases.Title(language.English)
nameTitle := caser.String(name)
switch fileType {
case "handler":
createHandler(name, nameTitle)
case "repository":
createRepository(name, nameTitle)
case "model":
createModel(name, nameTitle)
case "service":
createService(name, nameTitle)
default:
fmt.Printf("未知类型: %s\n", fileType)
fmt.Println("可用类型: handler, repository, model, service")
}
}
func createHandler(name, nameTitle string) {
path := fmt.Sprintf("handler/%s.go", name)
if fileExists(path) {
fmt.Printf("文件 %s 已存在\n", path)
return
}
content := fmt.Sprintf(templates.HandlerMake,
nameTitle, name, nameTitle,
nameTitle, name, nameTitle, nameTitle, nameTitle,
name, nameTitle, name, nameTitle,
nameTitle, name, name, nameTitle,
nameTitle, name, name, nameTitle,
nameTitle, name, name, nameTitle,
nameTitle, name, name, nameTitle,
)
writeFile(path, content)
fmt.Printf("✓ 创建处理器: %s\n", path)
}
func createRepository(name, nameTitle string) {
path := fmt.Sprintf("repository/%s_repository.go", name)
if fileExists(path) {
fmt.Printf("文件 %s 已存在\n", path)
return
}
content := fmt.Sprintf(templates.RepositoryMake,
nameTitle, name, nameTitle, nameTitle,
nameTitle, name, nameTitle, nameTitle, nameTitle, nameTitle,
nameTitle, nameTitle, nameTitle,
)
writeFile(path, content)
fmt.Printf("✓ 创建仓库: %s\n", path)
}
func createModel(name, nameTitle string) {
path := fmt.Sprintf("model/%s.go", name)
if fileExists(path) {
fmt.Printf("文件 %s 已存在\n", path)
return
}
content := fmt.Sprintf(templates.ModelMake,
nameTitle, name, nameTitle, nameTitle, name,
)
writeFile(path, content)
fmt.Printf("✓ 创建模型: %s\n", path)
}
func createService(name, nameTitle string) {
path := fmt.Sprintf("service/%s_service.go", name)
if fileExists(path) {
fmt.Printf("文件 %s 已存在\n", path)
return
}
content := fmt.Sprintf(templates.ServiceMake,
nameTitle, name, nameTitle, nameTitle,
nameTitle, name, nameTitle, nameTitle, nameTitle, nameTitle, nameTitle,
nameTitle, nameTitle,
nameTitle, nameTitle,
nameTitle, nameTitle,
nameTitle, nameTitle,
)
writeFile(path, content)
fmt.Printf("✓ 创建服务: %s\n", path)
}
+55
View File
@@ -0,0 +1,55 @@
package main
import (
"fmt"
"os"
)
func printUsage() {
fmt.Println(`xlgo - Go Web 框架脚手架工具
用法:
xlgo new <项目名> 创建新项目
xlgo make handler <名称> 创建处理器
xlgo make repository <名称> 创建仓库
xlgo make model <名称> 创建模型
xlgo make service <名称> 创建服务
xlgo version 显示版本号
示例:
xlgo new myapp
xlgo make handler user
xlgo make repository user`)
}
func main() {
if len(os.Args) < 2 {
printUsage()
return
}
command := os.Args[1]
switch command {
case "new":
if len(os.Args) < 3 {
fmt.Println("用法: xlgo new <项目名>")
return
}
createProject(os.Args[2])
case "make":
if len(os.Args) < 4 {
fmt.Println("用法: xlgo make <类型> <名称>")
fmt.Println("类型: handler, repository, model, service")
return
}
makeFile(os.Args[2], os.Args[3])
case "version":
fmt.Println("xlgo v1.0.0")
default:
printUsage()
}
}
+483
View File
@@ -0,0 +1,483 @@
package main
// Templates 存放所有代码生成模板
var templates = struct {
Main string
Config string
GoMod string
Makefile string
Gitignore string
Handler string
HandlerMake string
RepositoryMake string
ModelMake string
ServiceMake string
}{
// Main 新项目主文件模板
Main: `package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"{{.Module}}/config"
"{{.Module}}/database"
"{{.Module}}/handler"
"{{.Module}}/logger"
"{{.Module}}/middleware"
"{{.Module}}/storage"
"{{.Module}}/validation"
)
var configPath string
func init() {
flag.StringVar(&configPath, "config", "./config.yaml", "配置文件路径")
}
func main() {
flag.Parse()
// 加载配置
cfg, err := config.Load(configPath)
if err != nil {
fmt.Printf("加载配置失败: %v\n", err)
os.Exit(1)
}
// 初始化日志
if err := logger.Init(cfg); err != nil {
fmt.Printf("初始化日志失败: %v\n", err)
os.Exit(1)
}
defer logger.Sync()
// 初始化数据库
if err := database.InitMySQL(cfg); err != nil {
logger.Fatalf("初始化 MySQL 失败: %v", err)
}
defer database.Close()
// 初始化 Redis
if err := database.InitRedis(cfg); err != nil {
logger.Fatalf("初始化 Redis 失败: %v", err)
}
defer database.CloseRedis()
// 初始化存储
if err := storage.Init(&cfg.Storage); err != nil {
logger.Fatalf("初始化存储失败: %v", err)
}
// 初始化验证器
validation.InitValidator()
// 设置 Gin 模式
if cfg.IsProduction() {
gin.SetMode(gin.ReleaseMode)
}
// 创建路由
r := gin.New()
r.Use(gin.Recovery())
r.Use(middleware.Logger())
r.Use(middleware.CORS())
// 静态文件服务
r.Static("/public", "./public")
// 注册路由
setupRoutes(r)
// 启动服务器
srv := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
Handler: r,
}
// 优雅关闭
go func() {
logger.Infof("服务器启动,监听端口 %d", cfg.Server.Port)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Fatalf("服务器启动失败: %v", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
logger.Info("正在关闭服务器...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
logger.Fatalf("服务器强制关闭: %v", err)
}
logger.Info("服务器已关闭")
}
func setupRoutes(r *gin.Engine) {
// 初始化限速器
middleware.InitRateLimiters()
// Swagger 文档
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// 健康检查
r.GET("/health", handler.HealthCheck)
// API 路由
api := r.Group("/api/v1")
{
// 首页
api.GET("/", handler.Home)
}
}
`,
// Config 配置文件模板
Config: `app:
name: "{{.Name}}"
site_name: "{{.NameLower}}" # 站点别名,用于缓存键前缀、日志标识、多站点区分
version: "1.0.0"
env: "dev" # dev/test/prod
debug: true
base_url: "http://localhost:8080"
token_expire: 86400 # Token过期时间(秒)
server:
port: 8080
mode: development
database:
host: localhost
port: 3306
user: root
password: your_password
name: {{.NameLower}}
max_idle_conns: 10
max_open_conns: 100
redis:
host: localhost
port: 6379
password: ""
db: 0
jwt:
secret: your_jwt_secret_key_here
expire: 86400
storage:
driver: local
local:
path: ./public
base_url: http://localhost:8080/public
log:
dir: ./logs
max_size: 100
max_backups: 30
max_age: 30
compress: true
`,
// GoMod go.mod 文件模板
GoMod: `module %s
go 1.25
require (
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/uuid v1.6.0
github.com/redis/go-redis/v9 v9.5.1
github.com/spf13/viper v1.18.2
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.21.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/driver/mysql v1.5.4
gorm.io/gorm v1.25.7
)
`,
// Makefile 模板
Makefile: `.PHONY: build run test clean tidy swagger
build:
go build -o bin/server .
run:
go run main.go
test:
go test ./...
clean:
rm -rf bin/
rm -rf logs/
tidy:
go mod tidy
swagger:
swag init -g main.go -o ./swagger
`,
// Gitignore 模板
Gitignore: `# Binaries
bin/
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test files
*.test
*.out
coverage.txt
# Go workspace file
go.work
# IDE
.idea/
.vscode/
*.swp
*.swo
# Environment
.env
.env.local
# Logs
logs/
*.log
# Config (keep example)
config.local.yaml
`,
// Handler 新项目默认处理器模板
Handler: `package handler
import (
"github.com/gin-gonic/gin"
"{{.Module}}/response"
)
// HealthCheck 健康检查
func HealthCheck(c *gin.Context) {
response.Success(c, gin.H{
"status": "ok",
})
}
// Home 首页
func Home(c *gin.Context) {
response.Success(c, gin.H{
"message": "Welcome to {{.Name}}!",
})
}
`,
// HandlerMake make handler 命令模板
HandlerMake: `package handler
import (
"github.com/gin-gonic/gin"
"xlgo/response"
)
// %sHandler %s 处理器
type %sHandler struct {
// 可以注入 service
}
// New%sHandler 创建 %s 处理器
func New%sHandler() *%sHandler {
return &%sHandler{}
}
// List 获取列表
// @Summary 获取%s列表
// @Tags %s
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/v1/%ss [get]
func (h *%sHandler) List(c *gin.Context) {
response.Success(c, gin.H{
"Items": []string{},
})
}
// Get 获取详情
// @Summary 获取%s详情
// @Tags %s
// @Accept json
// @Produce json
// @Param id path int true "ID"
// @Success 200 {object} response.Response
// @Router /api/v1/%ss/{id} [get]
func (h *%sHandler) Get(c *gin.Context) {
response.Success(c, gin.H{
"id": c.Param("id"),
})
}
// Create 创建
// @Summary 创建%s
// @Tags %s
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/v1/%ss [post]
func (h *%sHandler) Create(c *gin.Context) {
response.Success(c, nil)
}
// Update 更新
// @Summary 更新%s
// @Tags %s
// @Accept json
// @Produce json
// @Param id path int true "ID"
// @Success 200 {object} response.Response
// @Router /api/v1/%ss/{id} [put]
func (h *%sHandler) Update(c *gin.Context) {
response.Success(c, nil)
}
// Delete 删除
// @Summary 删除%s
// @Tags %s
// @Accept json
// @Produce json
// @Param id path int true "ID"
// @Success 200 {object} response.Response
// @Router /api/v1/%ss/{id} [delete]
func (h *%sHandler) Delete(c *gin.Context) {
response.Success(c, nil)
}
`,
// RepositoryMake make repository 命令模板
RepositoryMake: `package repository
import (
"context"
"xlgo/database"
"xlgo/model"
)
// %sRepository %s 仓库
type %sRepository struct {
*BaseRepo[model.%s]
}
// New%sRepository 创建 %s 仓库
func New%sRepository() *%sRepository {
return &%sRepository{
BaseRepo: NewBaseRepo[model.%s](database.GetDB()),
}
}
// FindByName 根据名称查询
func (r *%sRepository) FindByName(ctx context.Context, name string) (*model.%s, error) {
var m model.%s
err := r.GetDB().WithContext(ctx).Where("name = ?", name).First(&m).Error
if err != nil {
return nil, err
}
return &m, nil
}
`,
// ModelMake make model 命令模板
ModelMake: `package model
// %s %s 模型
type %s struct {
BaseModel
Name string ` + "`" + `gorm:"size:100;not null" json:"name"` + "`" + `
Description string ` + "`" + `gorm:"size:500" json:"description"` + "`" + `
Status int ` + "`" + `gorm:"default:1" json:"status"` + "`" + ` // 1: 启用, 0: 禁用
}
// TableName 表名
func (%s) TableName() string {
return "%ss"
}
`,
// ServiceMake make service 命令模板
ServiceMake: `package service
import (
"context"
"xlgo/model"
"xlgo/repository"
)
// %sService %s 服务
type %sService struct {
repo *repository.%sRepository
}
// New%sService 创建 %s 服务
func New%sService() *%sService {
return &%sService{
repo: repository.New%sRepository(),
}
}
// List 获取列表
func (s *%sService) List(ctx context.Context, page, pageSize int) ([]model.%s, int64, error) {
// TODO: 实现列表查询
return nil, 0, nil
}
// GetByID 根据 ID 获取
func (s *%sService) GetByID(ctx context.Context, id uint) (*model.%s, error) {
return s.repo.FindByID(ctx, id)
}
// Create 创建
func (s *%sService) Create(ctx context.Context, m *model.%s) error {
return s.repo.Create(ctx, m)
}
// Update 更新
func (s *%sService) Update(ctx context.Context, m *model.%s) error {
return s.repo.Update(ctx, m)
}
// Delete 删除
func (s *%sService) Delete(ctx context.Context, id uint) error {
return s.repo.Delete(ctx, id)
}
`,
}
+10
View File
@@ -0,0 +1,10 @@
package main
// TemplateData 模板数据
type TemplateData struct {
Package string
Name string
NameLower string
Module string
Year int
}
+24
View File
@@ -0,0 +1,24 @@
package main
import (
"fmt"
"os"
"path/filepath"
)
func writeFile(path, content string) {
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0755); err != nil {
fmt.Printf("创建目录失败: %s\n", err)
return
}
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
fmt.Printf("写入文件失败: %s\n", err)
}
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}