调整项目架构,让框架更优化
This commit is contained in:
@@ -1,40 +1,17 @@
|
||||
# 模板文件
|
||||
# 模板
|
||||
|
||||
这个目录包含了可以直接复制到你项目中使用的模板文件。
|
||||
复制到业务项目后,按注释中的挂载点扩展即可。
|
||||
|
||||
## 包含的模板
|
||||
|
||||
- `migrate/main.go` - 数据库迁移工具模板 ⭐
|
||||
- `Dockerfile.example` - Docker 构建示例
|
||||
- `docker-compose.example.yml` - Docker Compose 示例
|
||||
- `Makefile.example` - Makefile 常用命令示例
|
||||
|
||||
## 快速使用
|
||||
|
||||
### 迁移工具模板
|
||||
| 文件 | 默认已接好 | 主要改哪里 |
|
||||
|------|------------|------------|
|
||||
| `server/main.go` | MustInit、日志、Warmup、默认中间件链、NewHandler、Close;`-config`/`-addr` | `buildOptions` / `setupMiddleware` / `registerRoutes` |
|
||||
| `migrate/main.go` | MustInit、日志、Database Warmup、Migrator、Close;`-config`/`-dir` | `buildOptions`;SQL 放 `migrations/` |
|
||||
| `Dockerfile.example` 等 | 镜像 / Compose / Makefile 示例 | 按部署环境改 |
|
||||
|
||||
```bash
|
||||
# 1. 复制到你的项目
|
||||
mkdir -p cmd/migrate
|
||||
cp templates/migrate/main.go cmd/migrate/
|
||||
|
||||
# 2. 编译
|
||||
go build -o bin/migrate cmd/migrate/main.go
|
||||
|
||||
# 3. 使用
|
||||
./bin/migrate up
|
||||
./bin/migrate -help
|
||||
mkdir -p cmd/server cmd/migrate
|
||||
cp templates/server/main.go cmd/server/main.go
|
||||
cp templates/migrate/main.go cmd/migrate/main.go
|
||||
```
|
||||
|
||||
### Docker 模板
|
||||
|
||||
```bash
|
||||
# 复制到你的项目根目录
|
||||
cp templates/Dockerfile.example Dockerfile
|
||||
cp templates/docker-compose.example.yml docker-compose.yml
|
||||
cp templates/Makefile.example Makefile
|
||||
```
|
||||
|
||||
## 完整文档
|
||||
|
||||
详细使用说明请查看:[INTEGRATION.md](../INTEGRATION.md) 第 7 节「数据库迁移」
|
||||
对接说明:[INTEGRATION.md](../INTEGRATION.md)。
|
||||
|
||||
@@ -5,145 +5,153 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/factory"
|
||||
"git.toowon.com/jimmy/go-common/migration"
|
||||
)
|
||||
|
||||
// 数据库迁移工具(黑盒模式)
|
||||
// 数据库迁移 CLI 脚手架。
|
||||
//
|
||||
// 工作原理:
|
||||
// 此工具调用 migration.RunMigrationsFromConfigWithCommand() 方法,
|
||||
// 内部自动处理配置加载、数据库连接、迁移执行等所有细节。
|
||||
// 你只需要提供配置文件和SQL迁移文件即可。
|
||||
// mkdir -p cmd/migrate
|
||||
// cp templates/migrate/main.go cmd/migrate/main.go
|
||||
// go build -o bin/migrate cmd/migrate/main.go
|
||||
// ./bin/migrate up|status|down
|
||||
//
|
||||
// 使用方式:
|
||||
// 基本用法:
|
||||
// ./migrate up # 使用默认配置
|
||||
// ./migrate up -config /path/to/config.json # 指定配置文件
|
||||
// ./migrate up -config config.json -dir db/migrations # 指定配置和迁移目录
|
||||
// ./migrate status # 查看迁移状态
|
||||
// ./migrate down # 回滚最后一个迁移
|
||||
//
|
||||
// Docker 中使用:
|
||||
// # 方式1:挂载配置文件(推荐)
|
||||
// docker run -v /host/config.json:/app/config.json myapp ./migrate up
|
||||
//
|
||||
// # 方式2:使用环境变量指定配置文件路径
|
||||
// docker run -e CONFIG_FILE=/etc/app/config.json myapp ./migrate up
|
||||
//
|
||||
// # 方式3:指定容器内的配置文件路径
|
||||
// docker run myapp ./migrate up -config /etc/app/config.json
|
||||
//
|
||||
// 支持的命令:
|
||||
// up - 执行所有待执行的迁移
|
||||
// down - 回滚最后一个迁移
|
||||
// status - 查看迁移状态
|
||||
//
|
||||
// 配置优先级(从高到低):
|
||||
// 1. 命令行参数 -config 和 -dir
|
||||
// 2. 环境变量 CONFIG_FILE 和 MIGRATIONS_DIR
|
||||
// 3. 默认值(config.json 和 migrations)
|
||||
|
||||
var (
|
||||
configFile string
|
||||
migrationsDir string
|
||||
showHelp bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&configFile, "config", "", "配置文件路径(默认:config.json 或环境变量 CONFIG_FILE)")
|
||||
flag.StringVar(&configFile, "c", "", "配置文件路径(简写)")
|
||||
flag.StringVar(&migrationsDir, "dir", "", "迁移文件目录(默认:migrations 或环境变量 MIGRATIONS_DIR)")
|
||||
flag.StringVar(&migrationsDir, "d", "", "迁移文件目录(简写)")
|
||||
flag.BoolVar(&showHelp, "help", false, "显示帮助信息")
|
||||
flag.BoolVar(&showHelp, "h", false, "显示帮助信息(简写)")
|
||||
}
|
||||
// 默认已接好:Factory 初始化、日志、Database Warmup、Migrator、Close 收口。
|
||||
// 配置优先级:-config/-dir > 环境变量 CONFIG_FILE/MIGRATIONS_DIR > 默认值。
|
||||
|
||||
func main() {
|
||||
var (
|
||||
configFile string
|
||||
migrationsDir string
|
||||
showHelp bool
|
||||
)
|
||||
|
||||
flag.StringVar(&configFile, "config", "", "配置文件路径(默认 config.json)")
|
||||
flag.StringVar(&configFile, "c", "", "配置文件路径(简写)")
|
||||
flag.StringVar(&migrationsDir, "dir", "", "迁移目录(默认 migrations)")
|
||||
flag.StringVar(&migrationsDir, "d", "", "迁移目录(简写)")
|
||||
flag.BoolVar(&showHelp, "help", false, "显示帮助")
|
||||
flag.BoolVar(&showHelp, "h", false, "显示帮助(简写)")
|
||||
flag.Parse()
|
||||
|
||||
// 显示帮助
|
||||
if showHelp {
|
||||
printHelp()
|
||||
os.Exit(0)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取命令(默认up)
|
||||
// 支持两种方式:
|
||||
// 1. 位置参数:./migrate up
|
||||
// 2. 标志参数:./migrate -cmd=up(向后兼容)
|
||||
command := "up"
|
||||
args := flag.Args()
|
||||
if len(args) > 0 {
|
||||
if args := flag.Args(); len(args) > 0 {
|
||||
command = args[0]
|
||||
}
|
||||
|
||||
// 验证命令
|
||||
if command != "up" && command != "down" && command != "status" {
|
||||
fmt.Fprintf(os.Stderr, "错误:未知命令 '%s'\n\n", command)
|
||||
fmt.Fprintf(os.Stderr, "未知命令: %s\n\n", command)
|
||||
printHelp()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 获取配置文件路径(优先级:命令行 > 环境变量 > 默认值)
|
||||
// 如果未指定,RunMigrationsFromConfigWithCommand 会自动查找
|
||||
if configFile == "" {
|
||||
configFile = getEnv("CONFIG_FILE", "")
|
||||
configFile = envOr("CONFIG_FILE", "config.json")
|
||||
}
|
||||
|
||||
// 获取迁移目录(优先级:命令行 > 环境变量 > 默认值)
|
||||
// 如果未指定,RunMigrationsFromConfigWithCommand 会使用默认值 "migrations"
|
||||
if migrationsDir == "" {
|
||||
migrationsDir = getEnv("MIGRATIONS_DIR", "")
|
||||
migrationsDir = envOr("MIGRATIONS_DIR", "migrations")
|
||||
}
|
||||
|
||||
// 执行迁移(黑盒模式:内部自动处理所有细节)
|
||||
if err := migration.RunMigrationsFromConfigWithCommand(configFile, migrationsDir, command); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "错误: %v\n", err)
|
||||
app := factory.MustInit(configFile, buildOptions()...)
|
||||
defer app.Close()
|
||||
|
||||
log := app.MustLogger()
|
||||
|
||||
if err := app.Warmup(factory.ModuleLogger, factory.ModuleDatabase); err != nil {
|
||||
log.Error("warmup failed", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
m, err := app.Migrator(migrationsDir)
|
||||
if err != nil {
|
||||
log.Error("create migrator failed", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "up":
|
||||
if err := m.Up(); err != nil {
|
||||
log.Error("migrate up failed", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("migrate up ok", map[string]any{"dir": migrationsDir})
|
||||
fmt.Println("✓ 迁移执行成功")
|
||||
|
||||
case "down":
|
||||
if err := m.Down(); err != nil {
|
||||
log.Error("migrate down failed", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("migrate down ok", map[string]any{"dir": migrationsDir})
|
||||
fmt.Println("✓ 迁移回滚成功")
|
||||
|
||||
case "status":
|
||||
status, err := m.Status()
|
||||
if err != nil {
|
||||
log.Error("migrate status failed", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
printStatus(status)
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
// buildOptions 组装 Factory Option(自定义 DB / Logger 等在此注入)
|
||||
func buildOptions() []factory.Option {
|
||||
// 默认走 config.json。需要替换时例如:
|
||||
//
|
||||
// return []factory.Option{
|
||||
// factory.WithLogger(customLogger),
|
||||
// factory.WithDatabase(db),
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
func printStatus(status []migration.MigrationStatus) {
|
||||
if len(status) == 0 {
|
||||
fmt.Println("没有找到迁移")
|
||||
return
|
||||
}
|
||||
return defaultValue
|
||||
fmt.Println("\n迁移状态:")
|
||||
fmt.Printf("%-20s %-40s %-10s\n", "版本", "描述", "状态")
|
||||
for _, s := range status {
|
||||
st := "待执行"
|
||||
if s.Applied {
|
||||
st = "已应用"
|
||||
}
|
||||
fmt.Printf("%-20s %-40s %-10s\n", s.Version, s.Description, st)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func envOr(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func printHelp() {
|
||||
fmt.Println("数据库迁移工具")
|
||||
fmt.Println()
|
||||
fmt.Println("用法:")
|
||||
fmt.Println(" migrate [命令] [选项]")
|
||||
fmt.Println()
|
||||
fmt.Println("命令:")
|
||||
fmt.Println(" up 执行所有待执行的迁移(默认)")
|
||||
fmt.Println(" down 回滚最后一个迁移")
|
||||
fmt.Println(" status 查看迁移状态")
|
||||
fmt.Println()
|
||||
fmt.Println("选项:")
|
||||
fmt.Println(" -config, -c 配置文件路径(默认: config.json)")
|
||||
fmt.Println(" -dir, -d 迁移文件目录(默认: migrations)")
|
||||
fmt.Println(" -help, -h 显示帮助信息")
|
||||
fmt.Println()
|
||||
fmt.Println("示例:")
|
||||
fmt.Println(" # 使用默认配置")
|
||||
fmt.Println(" migrate up")
|
||||
fmt.Println()
|
||||
fmt.Println(" # 指定配置文件")
|
||||
fmt.Println(" migrate up -config /etc/app/config.json")
|
||||
fmt.Println()
|
||||
fmt.Println(" # 指定配置和迁移目录")
|
||||
fmt.Println(" migrate up -c config.json -d db/migrations")
|
||||
fmt.Println()
|
||||
fmt.Println(" # 使用环境变量指定配置文件路径")
|
||||
fmt.Println(" CONFIG_FILE=/etc/app/config.json migrate up")
|
||||
fmt.Println()
|
||||
fmt.Println(" # Docker 中使用(挂载配置文件)")
|
||||
fmt.Println(" docker run -v /host/config.json:/app/config.json myapp migrate up")
|
||||
fmt.Println()
|
||||
fmt.Println("配置优先级(从高到低):")
|
||||
fmt.Println(" 1. 命令行参数 -config 和 -dir")
|
||||
fmt.Println(" 2. 环境变量 CONFIG_FILE 和 MIGRATIONS_DIR")
|
||||
fmt.Println(" 3. 默认值(config.json 和 migrations)")
|
||||
fmt.Println(`数据库迁移工具
|
||||
|
||||
用法:
|
||||
migrate [命令] [选项]
|
||||
|
||||
命令:
|
||||
up 执行待执行迁移(默认)
|
||||
down 回滚最后一个迁移
|
||||
status 查看状态
|
||||
|
||||
选项:
|
||||
-config, -c 配置文件(默认: config.json,可用 CONFIG_FILE)
|
||||
-dir, -d 迁移目录(默认: migrations,可用 MIGRATIONS_DIR)
|
||||
-help, -h 帮助
|
||||
|
||||
示例:
|
||||
migrate up
|
||||
migrate up -config /etc/app/config.json -dir db/migrations
|
||||
CONFIG_FILE=config.json migrate status`)
|
||||
}
|
||||
|
||||
100
templates/server/main.go
Normal file
100
templates/server/main.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/factory"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
// 业务 HTTP 服务脚手架。
|
||||
//
|
||||
// mkdir -p cmd/server
|
||||
// cp templates/server/main.go cmd/server/main.go
|
||||
//
|
||||
// 默认已接好:Factory 初始化、日志、默认中间件链、NewHandler 出参、Close 收口。
|
||||
// 按需改:buildOptions / setupMiddleware / registerRoutes / Warmup 模块列表。
|
||||
|
||||
func main() {
|
||||
configPath := flag.String("config", envOr("CONFIG_FILE", "config.json"), "配置文件路径")
|
||||
addr := flag.String("addr", envOr("HTTP_ADDR", ":8080"), "监听地址")
|
||||
flag.Parse()
|
||||
|
||||
app := factory.MustInit(*configPath, buildOptions()...)
|
||||
defer app.Close()
|
||||
|
||||
log := app.MustLogger()
|
||||
|
||||
// 启动期预热:按业务实际使用的模块增删(未配置的模块 Warmup 会失败)
|
||||
if err := app.Warmup(
|
||||
factory.ModuleLogger,
|
||||
// factory.ModuleI18n,
|
||||
// factory.ModuleDatabase,
|
||||
// factory.ModuleRedis,
|
||||
); err != nil {
|
||||
log.Error("warmup failed", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
chain := setupMiddleware(app)
|
||||
mux := http.NewServeMux()
|
||||
registerRoutes(mux, chain, app)
|
||||
|
||||
log.Info("server starting", map[string]any{"addr": *addr, "config": *configPath})
|
||||
if err := http.ListenAndServe(*addr, mux); err != nil {
|
||||
log.Error("server stopped", map[string]any{"error": err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// buildOptions 组装 Factory Option(自定义实现 / 测试替身在此注入)
|
||||
func buildOptions() []factory.Option {
|
||||
// 默认不注入,全部走 config.json lazy 初始化。
|
||||
// 需要替换实现时取消注释,例如:
|
||||
//
|
||||
// return []factory.Option{
|
||||
// factory.WithLogger(customLogger),
|
||||
// factory.WithStorage(customStorage),
|
||||
// factory.WithDatabase(db),
|
||||
// factory.WithRedis(rds),
|
||||
// factory.WithI18n(i18nInst),
|
||||
// factory.WithMiddlewareChain(customChain),
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupMiddleware 基于 Factory 默认链追加业务中间件
|
||||
// 默认链:Recovery → RequestID → Logging → RateLimit? → CORS? → Language → Timezone
|
||||
func setupMiddleware(app *factory.Factory) *middleware.Chain {
|
||||
chain := app.MiddlewareChain()
|
||||
|
||||
// 业务鉴权等自行挂载(本库不提供登录体系)
|
||||
// chain.Append(authMiddleware)
|
||||
// chain.Append(permissionMiddleware)
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
// registerRoutes 注册路由;出参统一用 app.NewHandler
|
||||
func registerRoutes(mux *http.ServeMux, chain *middleware.Chain, app *factory.Factory) {
|
||||
mux.Handle("/api/ping", chain.ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h := app.NewHandler(w, r)
|
||||
h.Success(map[string]string{"status": "ok"})
|
||||
}))
|
||||
|
||||
// 示例:业务路由
|
||||
// mux.Handle("/api/users", chain.ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// h := app.NewHandler(w, r)
|
||||
// // ...
|
||||
// h.Success(nil)
|
||||
// }))
|
||||
}
|
||||
|
||||
func envOr(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
Reference in New Issue
Block a user