程序员转型:PHP转AI+Golang,目录结构、Git与配置系统
使用AI工具从零学习Golang并开发ai-go-mall开源项目,第二期完成目录结构搭建、Git初始化,基于Viper设计开发配置系统,支持YAML文件自动加载和环境变量覆盖,实现配置监听与热更新,为项目后续扩展打下基础。

目录结构
首先创建ai-go-mall 目录,然后执行 go mod init ai-go-mall 完成初始化,这是 Go 项目的基础操作。
把之前准备好的 项目目录结构.md 和 编码风格最佳实践.md 放进 /doc 目录。接着切到 ai-go-mall 目录,执行 claude 命令,让它根据 @doc项目目录结构.md 创建需要的文件夹。
AI 申请授权时,只要不涉及越权执行命令、安装盗版依赖,或者使用不靠谱的中转站(防止中间人攻击),一般都可以同意。不过话说回来,最后每一行代码还得咱们自己过一遍。
.
├── go.mod
├── .claude/
│ └── settings.local.json
├── cmd/
│ └── api/
├── config/
├── doc/
│ ├── 项目目录结构.md
│ └── 编码风格最佳实践.md
├── internal/
│ ├── handler/
│ ├── model/
│ ├── repository/
│ └── service/
├── pkg/
│ ├── logger/
│ └── validator/
└── script/
生成完发现,有些目录短期内用不上,比如 pkg 和 script,直接删掉。同时手动创建 cmd/api/main.go 文件,这是入口文件,必不可少。
初始化 GIT
Git 是全世界最流行的版本控制系统,没什么好说的,必备技能,不会的去学一下,很快就能上手。git init
git config user.name "yang"
git config user.email "1094963513@qq.com"
如果不上传远程仓库,名字和邮箱随便写就行。接下来让 Claude 帮忙建个 .gitignore 文件,把 .DS_Store、*.css.map、*.local.* 这类常见忽略项都加进去。
生成的 .gitignore 内容如下:
# OS
.DS_Store
Thumbs.db
# IDE
.idea/
.vscode/
*.swp
*.swo
# Local config
*.local.*
.env
# Go
/bin/
/vendor/
*.exe
*.test
*.out
# Frontend
*.css.map
*.js.map
node_modules/
/dist/
# Build output
/tmp/
/log/
手动把 *.swo、*.swp 改成 *.sw?,同时去掉了 .env 这条规则,因为后面会用到 .env.yaml,不能一起忽略。
设计并开发配置系统
对比了几种方案,最终选了 Viper 来管理配置。它是 Go 生态里最完整的配置解决方案,GitHub 上 30k 的 star 数就是最好的背书。 规划配置系统的核心思路如下: - 用github.com/spf13/viper 管理配置
- 初版包含 应用名称、服务端口号、数据库相关 配置项,一个配置文件搞定
- 所有配置文件用 YAML 格式,存放在 /config 目录下,配置系统能自动加载所有 YAML 文件,新增配置不需要改代码
- 额外加一个 .env.yaml 环境配置文件,优先级最高,用于开发环境自定义数据库配置,Git 需要忽略
- 再配一个 .env.yaml.example 文件,Git 不忽略,方便开发者快速创建自己的环境配置
- 数据库配置带上 type 字段,默认用 PostgreSQL,同时准备好连接池配置项
把这个规划扔给 AI 后,它很快给出了输出:
配置系统已实现完毕。以下是最终文件结构和验证结果:
config/
├── config.yaml # 主配置文件(Git 跟踪)
├── .env.yaml # 本地环境配置(Git 忽略)
└── .env.yaml.example # 环境配置示例(Git 跟踪)
pkg/config/
└── config.go # 配置加载逻辑
cmd/api/
└── main.go # 入口,调用配置系统
核心设计:
- config.Load("config") 自动扫描 /config 目录下所有 .yaml 文件,按文件名排序后逐个 Merge,.env.yaml 排最后加载,值覆盖前面的文件
- 新增配置文件无需改代码,放入 /config 目录即可
- 数据库默认类型为 PostgreSQL,连接池默认值 max_open=25 / max_idle=10 / max_lifetime=5m
- 提供 ConnMaxLifetimeDuration() 方法将字符串转为 time.Duration
验证结果:
┌─────────────────┬─────────────┬─────────────────────┐
│ 配置项 │ config.yaml │ .env.yaml(覆盖后) │
├─────────────────┼─────────────┼─────────────────────┤
│ app.name │ ai-go-mall │ ai-go-mall │
├─────────────────┼─────────────┼─────────────────────┤
│ server.port │ 8080 │ 8080 │
├─────────────────┼─────────────┼─────────────────────┤
│ database.host │ localhost │ 192.168.1.100 │
├─────────────────┼─────────────┼─────────────────────┤
│ database.port │ 5432 │ 5433 │
├─────────────────┼─────────────┼─────────────────────┤
│ database.dbname │ ai-go-mall │ ai-go-mall-dev │
└─────────────────┴─────────────┴─────────────────────┘
✻ Churned for 2m 50s
检查一下目录结构,发现几个问题:
1. AI 把 .env.yaml 和 .env.yaml.example 放在了 /config 目录下。其实本意是让环境配置直接放在根目录。
2. 配置加载逻辑放在了 pkg/config.go,虽然合理,但更希望配置加载直接在 /config 目录下,最终结构是 /config/config.go + 多个 .yaml 文件。未来会迁到 internal 目录,那时 /config 只存 YAML 文件。
3. .env.yaml 里只有数据库配置,没有 server.port 和 app.name 配置项。
把这些问题发给 AI,让它继续改:
三个问题已全部修正,最终结构如下:
.
├── .env.yaml # 根目录,Git 忽略 ✓
├── .env.yaml.example # 根目录,Git 跟踪,含全部配置项 ✓
├── .gitignore
├── config/
│ ├── config.go # 配置加载逻辑 ✓
│ └── config.yaml # 主配置文件
└── cmd/api/main.go
改动要点:
┌──────────────────────────────────┬───────────────────────────────────────────────────────────┐
│ 问题 │ 修正 │
├──────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ .env.yaml 放入 /config │ 移至根目录 │
├──────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ config.go 放入 /pkg/config │ 移至 /config/config.go,import 路径变为 ai-go-mall/config │
├──────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ .env.yaml.example 仅含数据库配置 │ 补充了 app.name、server.port 及数据库全套配置 │
└──────────────────────────────────┴───────────────────────────────────────────────────────────┘
加载顺序: config/*.yaml → 根目录 .env.yaml(最后加载,覆盖前面的值)。.env.yaml 不存在时优雅降级,不报错。
✻ Worked for 1m 24s
接下来是关键步骤——逐行理解代码,找出不足,或者按照自己的偏好做调整。找到的问题如下:
1. config.Load 函数需要传递 config 目录路径,但当前项目直接用 /config 目录即可,没必要传参。
2. config.Load 有“加载”的意思,在 main.go 调用,但改为 config.Init 函数更合理。
3. 没加载到任何配置文件时,不需要报错。
4. 数据库配置缺少 timezone 和 prefix(往期项目习惯加个 prefix 配置项,可以不用,但不能没有)。
5. 不需要对数据库配置做任何预处理,后续在连接数据库的函数里处理。
6. 研究后,要求 AI 提供一个 config.Get 函数,用于获取配置项(现在是 cfg.App.Name,改为 config.Get().App.Name),前者配置可能被外部修改,包装一个方法扩展性更好。
7. 对环境变量进行加载,并覆盖已有配置项,即调用 viper.AutomaticEnv()(不是加载项目的 .env.yaml 环境配置文件)。环境变量可以通过以下方式动态设定:
# Linux / Mac
SERVER_PORT=8080 APP_NAME=myapp go run main.go
# Windows CMD
set SERVER_PORT=8080&& set APP_NAME=myapp&& go run main.go
# Windows PowerShell
$env:SERVER_PORT="8080"; $env:APP_NAME="myapp"; go run main.go
动态设定的 APP_NAME 会覆盖 app.name 配置项。AI 还修改了 go.mod 和 .gitignore 等文件,其中 .gitignore 的修改如下:
# .gitignore
- .env.*
+ .env.yaml
+ !.env.yaml.example
修改很合理,但 + !.env.yaml.example 是多余的,直接删除即可。
继续让 AI 按需求修改,经过几轮对话后,配置系统开发完成。完整代码开源于:github | gitee,核心代码如下:
1. 让 AI 增加了监听配置改变,以及配置数据的读写锁。
2. 监听配置改变时,当新配置为空可能不会覆盖,研究后添加了 c.ZeroFields = true 选项。
3. 同时确认了:结构体定义代码在最上面,是 Golang 项目的通用做法。
// main.go
package main
import (
"fmt"
"log"
"ai-go-mall/config"
)
func main() {
// 初始化配置
if err := config.Init(); err != nil {
log.Fatalf("config init: %v", err)
}
cfg := config.Get()
fmt.Printf("应用名称: %s\n", cfg.App.Name)
fmt.Printf("服务端口: %d\n", cfg.Server.Port)
fmt.Printf("数据库类型: %s\n", cfg.Database.Type)
fmt.Printf("数据库地址: %s:%d\n", cfg.Database.Host, cfg.Database.Port)
fmt.Printf("数据库名称: %s\n", cfg.Database.DBName)
fmt.Printf("连接池配置: max_open=%d max_idle=%d max_lifetime=%d\n",
cfg.Database.MaxOpenConns,
cfg.Database.MaxIdleConns,
cfg.Database.ConnMaxLifetime,
)
}
app:
name: ai-go-mall
server:
port: 8080
database:
type: postgres
host: 127.0.0.1
port: 5432
user: postgres
password: "123456"
dbname: aigo
prefix:
sslmode: disable
timezone: Asia/Shanghai
max_open_conns: 25
max_idle_conns: 10
conn_max_lifetime: 300 # 秒
// config.go
package config
import (
"fmt"
"log"
"os"
"path/filepath"
"sync"
"github.com/fsnotify/fsnotify"
"github.com/go-viper/mapstructure/v2"
"github.com/spf13/viper"
)
// App 应用配置
type App struct {
Name string
}
// Server 服务配置
type Server struct {
Port int
}
// Database 数据库配置
type Database struct {
Type string
Host string
Port int
User string
Password string
Prefix string
DBName string `mapstructure:"dbname"`
SSLMode string `mapstructure:"sslmode"`
Timezone string `mapstructure:"timezone"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
ConnMaxLifetime int `mapstructure:"conn_max_lifetime"`
}
// Config 聚合配置
type Config struct {
App App
Server Server
Database Database
}
var (
vip *viper.Viper
config = new(Config)
mu sync.RWMutex
)
// 初始化配置
func Init() error {
if vip == nil {
vip = viper.New()
// 读取 config 目录下所有 yaml 文件
files, err := filepath.Glob(filepath.Join("./config", "*.yaml"))
if err != nil {
return fmt.Errorf("get config files: %w", err)
}
for _, file := range files {
vip.SetConfigFile(file)
if err := vip.MergeInConfig(); err != nil {
return fmt.Errorf("merge config file %s: %w", file, err)
}
}
// 加载根目录 .env.yaml 文件(覆盖从 config 文件夹读取的配置)
if _, err := os.Stat(".env.yaml"); err == nil {
vip.SetConfigFile(".env.yaml")
if err := vip.MergeInConfig(); err != nil {
return fmt.Errorf("merge config file .env.yaml: %w", err)
}
}
// 支持环境变量覆盖
vip.AutomaticEnv()
// 解析到结构体
if err := vip.Unmarshal(config); err != nil {
return fmt.Errorf("unmarshal config: %w", err)
}
// 监听配置改变
vip.OnConfigChange(func(e fsnotify.Event) {
mu.Lock()
defer mu.Unlock()
if err := vip.Unmarshal(config, func(c *mapstructure.DecoderConfig) {
c.ZeroFields = true
}); err != nil {
log.Printf("config reload failed: %v", err)
}
})
vip.WatchConfig()
}
return nil
}
// 返回全局配置副本。
func Get() Config {
mu.RLock()
defer mu.RUnlock()
return *config
}
// 获取 Viper 全局单例
func GetViper() *viper.Viper {
return vip
}
// 是否已经初始化
func IsInit() bool {
return vip != nil
}
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
CAD零基础入门教程:坐标输入、图层管理与基础绘图命令
本文面向CAD零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。
CAD从入门到项目交付:绘图、标注、图块与实战工作流
掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。
Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤
本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。
Claude Code 文件修改前的权限模式配置与命令审批指南
本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。
Claude Code接入VS Code后先测扩展和终端命令
在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。
- 热门数据榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2026-09-01 16:53
2026-09-01 16:52
2026-09-01 14:27
2026-09-01 14:12
2026-09-01 14:10
2026-09-01 14:07
2026-09-01 13:55
2026-09-01 13:47
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

