跪拜 Guibai
← Back to the summary

From PHP to Go + AI: Building an Admin Model and Hot-Reload in a GORM-Powered Project

This is a series blog where the author, as a PHP full-stack engineer, uses AI tools (Claude Code, Codex, DeepSeek, Doubao, etc.) to learn the Go language from scratch and ultimately complete the open-source project ai-go-mall (GitHub | Gitee), documenting the entire process.

In the previous installment, we completed "creating the CLAUDE.md file and organizing the directory structure." This time, we will: design the admin model and implement hot-reload configuration.

Design the Admin Model / Database Table

Time to start building tables. Using GORM, we design the model according to its conventions, and then use AutoMigrate to create the table. When the model is modified, the table structure is automatically updated accordingly.

I plan to implement some backend functionality first, and then consider features like anti-XSS, CORS, rate limiting, etc. We already implemented support for automatic migration earlier. New models just need to be created in the model directory and registered by calling the Register function; the table will be created automatically when the system starts.

AutoMigrate Optimization

Currently, the syntax for registering an auto-migration model is Register(&Admin{}), which only registers a single model at a time. However, multiple related models can be placed in one model file. For example, the admin.go model file can contain at least three model definitions: Admin, AdminGroup, and AdminRule. So we first modify the Register function to accept any number of parameters, enabling the syntax Register(&Admin{}, &AdminGroup{}, &AdminRule{}).

Directly ask Claude Code: Modify the Register function in @internal/model/model.go to support any number of parameters, just like db.AutoMigrate(&User{}, &Product{}, &Order{}).

# Large language models are probabilistic generators, not fixed lookup tables. Combined with sampling strategies, context, version differences, etc., the same question can produce different outputs. The AI output and generated code below are for reference only. Feel free to skip ahead to see how to find problems and complete the fixes.

Now I understand the current implementation. Modify the Register function to support variadic parameters:

● Update(internal\model\model.go)
  ⎿  Added 3 lines, removed 2 lines
       3  var models []any
       4  
       5  // Register models that need auto-migration. Each model file can use the init() mechanism for automatic invocation.
       6 -func Register(m any) {                                                                                      
       7 -  models = append(models, m)                                                                                
       6 +// Supports registering multiple models at once, e.g., Register(&User{}, &Product{}, &Order{})               
       7 +func Register(models ...any) {                                                                              
       8 +  registered = append(registered, models...)                                                                
       9  }
      10  
      11  // Returns all registered models for use by database.Init's AutoMigrate

● Also need to rename the variable `models` to `registered` to avoid conflict with the parameter name...

It's a very simple requirement, only a few lines changed. The correctness of the new code is immediately obvious. The core is:

func Register(models ...any) {
	registered = append(registered, models...)
}

Admin Model Design

You can also directly ask Claude Code to design the model by referencing the GORM documentation:

  1. In internal\model\admin.go, define the admin model using GORM's syntax conventions (https://gorm.io/docs/models.html), and register it for auto-migration.
  2. Do not embed gorm.Model; instead, define all fields individually. This is because gorm.Model lacks field comments and doesn't allow control over field ordering. Place the ID field first, and UpdatedAt, CreatedAt, DeletedAt fields last in order.
  3. Each field must have a Chinese comment (except the ID field, which uses ID as its comment).
  4. Fill in field tags in a standardized and ordered manner, referencing existing model definitions. The comment tag should always be first, since all fields will have a comment.
  5. The admin model fields are: ID, Username, Nickname, Avatar, Email, Mobile, LoginFailure, LastLoginAt, LastLoginIp, Password, Bio, Status, UpdatedAt, CreatedAt, DeletedAt.

PS:

  1. The author has designed admin tables many times before, so they directly copied a previous CREATE TABLE SQL and had another AI extract the field names. Generally, if there's no prior design, you can let AI design from scratch.
  2. This prompt for creating new models can be reused in the future. Whether it's fully comprehensive and reasonable right now is uncertain; we'll refine it as we go.

After manual fine-tuning, the design is very neat. The varchar lengths 64, 128, 255 have no special meaning; they just look good and are reasonably appropriate lengths. The Status field does not use an enum type, allowing developers to extend other status values on their own.

type Admin struct {
	ID           uint           `gorm:"comment:ID;primarykey;autoIncrement"`
	Username     string         `gorm:"comment:用户名;type:varchar(64)"`
	Nickname     string         `gorm:"comment:昵称;type:varchar(64)"`
	Avatar       string         `gorm:"comment:头像;type:varchar(255)"`
	Email        string         `gorm:"comment:邮箱;type:varchar(128)"`
	Mobile       string         `gorm:"comment:手机号;type:varchar(16)"`
	LoginFailure uint           `gorm:"comment:连续登录失败次数"`
	LastLoginAt  time.Time      `gorm:"comment:上次登录时间"`
	LastLoginIp  string         `gorm:"comment:上次登录IP;type:varchar(64)"`
	Password     string         `gorm:"comment:密码;type:varchar(255)"`
	Bio          string         `gorm:"comment:个人简介;type:varchar(255)"`
	Status       string         `gorm:"comment:状态:enable=启用,disable=禁用;type:varchar(64)"`
	UpdatedAt    time.Time      `gorm:"comment:更新时间"`
	CreatedAt    time.Time      `gorm:"comment:创建时间"`
	DeletedAt    gorm.DeletedAt `gorm:"comment:删除时间;index"`
}

Hot-Reload Configuration

After modifying code, you always need to re-run go run cmd/api/main.go. Configuring automatic hot-reload for code changes in the development environment can save a lot of effort. The Go language itself or its standard library doesn't have built-in support for this. The Gin framework recommends using Air to implement hot-reload.

Actually, installing and configuring Air is quite simple, but we still let Claude Code handle it. Just tell it: Install and configure Air (https://github.com/air-verse/air) to implement hot-reload functionality in the development environment.

PS: The author always includes the GitHub or other official link when installing dependencies. This is very important, mainly to prevent the AI from installing counterfeit dependency packages. For example, if a hacker created https://github.com/hacker/air-true and published various tutorials/promotional articles across the web, and our prompt doesn't include the official repository link, the AI might search the web for "golang air dependency" and be tricked by those articles. The counterfeit package would still run normally, but it would carry malicious code, potentially using your computer/server for cryptocurrency mining or as a botnet node in the middle of the night.

Using Claude Code for installation still has benefits: the generated configuration file adapts to the project's directory structure and includes comments. Note that Air is not installed as a dependency of the current project; instead, it's go installed into $GOPATH/bin. Only the .air.toml configuration file is placed in the project root directory. The author also researched whether the configuration file could use YAML format to avoid introducing another toml file format into the project, but unfortunately, the answer is no.