当前位置: 首页
AI教程
程序员转型:PHP转AI+Golang,目录结构、Git与配置系统

程序员转型:PHP转AI+Golang,目录结构、Git与配置系统

时间:2026-07-24
转载

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

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-mall(github | gitee)开源项目的制作,全程记录分享。

从 PHP 到 AI + Golang,程序员自救转型手记(二):目录结构、初始化 GIT、设计并开发配置系统

上期聊了必须学什么、怎么快速上手,以及立项前的准备工作。这期直接动手,把 ai-go-mall 的目录结构搞出来,顺便初始化 Git,再设计并开发一套配置系统。

目录结构

首先创建 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零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。

时间:2026-09-01 16:53
CAD从入门到项目交付:绘图、标注、图块与实战工作流

CAD从入门到项目交付:绘图、标注、图块与实战工作流

掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。

时间:2026-09-01 16:52
Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤

Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤

本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。

时间:2026-09-01 14:27
Claude Code 文件修改前的权限模式配置与命令审批指南

Claude Code 文件修改前的权限模式配置与命令审批指南

本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。

时间:2026-09-01 14:12
Claude Code接入VS Code后先测扩展和终端命令

Claude Code接入VS Code后先测扩展和终端命令

在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。

时间:2026-09-01 14:10
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全