本项目是一个基于 Gin 框架的 Go Web 应用,采用分层架构设计。
项目Fork后执行 ./init.sh 脚本进行初始化,脚本会自动检测远程仓库地址并替换模块路径。
./init.sh
go-web/ ├── main.go # 程序入口 ├── go.mod # Go模块定义 ├── go.sum # 依赖版本锁定 ├── README.md # 项目说明文档 ├── .gitignore # Git忽略文件 ├── .goreleaser.yaml # 发布配置 ├── config.yaml.example # 配置文件示例 ├── cmd/ # 命令行入口 │ └── run.go ├── app/ # 应用核心代码 │ ├── controller/ # 控制器层(处理HTTP请求) │ ├── service/ # 服务层(业务逻辑) │ ├── dao/ # 数据访问层 │ ├── model/ # 数据模型 │ ├── dto/ # 数据传输对象 │ └── middleware/ # 中间件 ├── router/ # 路由配置 ├── config/ # 配置管理 ├── helper/ # 基础支持服务 └── util/ # 工具函数
gofmt 格式化所有 Go 代码gofmt# 格式化单个文件
gofmt -w filename.go
# 格式化整个项目
gofmt -w .
# 或使用 go fmt
go fmt ./...
import (
"fmt"
"log"
"os"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"your-project/app/model"
"your-project/config"
)
// 好的包名
package user
package auth
package http
// 避免的包名
package users
package user_service
package userService
// 推荐的文件名
user_controller.go
auth_service.go
database_config.go
// 导出的标识符
func ProcessRequest() error { }
var DefaultTimeout = 30 * time.Second
// 未导出的标识符
func processInternal() error { }
var maxRetries = 3
// 常量定义
const (
StatusPending Status = iota
StatusProcessing
StatusCompleted
StatusFailed
)
type Reader interface {
Read([]byte) (int, error)
}
type Writer interface {
Write([]byte) (int, error)
}
type UserService interface {
CreateUser(user *User) error
GetUser(id int) (*User, error)
}
type User struct {
name string
age int
}
// Getter - 不使用 Get 前缀
func (u *User) Name() string {
return u.name
}
// Setter - 使用 Set 前缀
func (u *User) SetName(name string) {
u.name = name
}
HTTP请求 -> Controller -> Service -> Repository -> Database ↓ DTO/Model
// 小接口
type UserReader interface {
GetUser(id int) (*User, error)
}
type UserWriter interface {
CreateUser(user *User) error
UpdateUser(user *User) error
}
// 接口组合
type UserRepository interface {
UserReader
UserWriter
}
// 好的错误处理
func GetUser(id int) (*User, error) {
if id <= 0 {
return nil, fmt.Errorf("invalid user id: %d", id)
}
user, err := userRepo.FindByID(id)
if err != nil {
return nil, fmt.Errorf("failed to get user %d: %w", id, err)
}
return user, nil
}
// 调用时的错误处理
user, err := GetUser(123)
if err != nil {
log.Printf("get user failed: %v", err)
return err
}
// 定义错误类型
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on field %s: %s", e.Field, e.Message)
}
// 使用错误类型
func ValidateUser(user *User) error {
if user.Name == "" {
return &ValidationError{
Field: "name",
Message: "name cannot be empty",
}
}
return nil
}
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// 错误码定义使用 iota
const (
CodeSuccess = iota
CodeBadRequest
CodeUnauthorized
CodeNotFound
CodeInternalError
)
// 好的 goroutine 使用模式
func ProcessRequests(ctx context.Context, requests <-chan Request) {
for {
select {
case req := <-requests:
go func(r Request) {
defer recover() // 防止 panic 导致程序崩溃
processRequest(r)
}(req)
case <-ctx.Done():
return
}
}
}
// 工作池模式
func WorkerPool(jobs <-chan Job, results chan<- Result) {
const numWorkers = 3
for i := 0; i < numWorkers; i++ {
go func() {
for job := range jobs {
result := processJob(job)
results <- result
}
}()
}
}
// Package user provides user management functionality.
// It includes user creation, authentication, and profile management.
package user
// CreateUser creates a new user with the given information.
// It returns the created user and any error encountered.
// The user's password will be hashed before storage.
func CreateUser(name, email, password string) (*User, error) {
// implementation
}
// User represents a user in the system.
// It contains basic user information and authentication data.
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"`
}
// User represents a user entity in the database.
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"uniqueIndex;size:50" json:"username"`
Password string `gorm:"size:100" json:"-"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// TableName returns the table name for User model.
func (User) TableName() string {
return "users"
}
// UserRepository defines the interface for user data access.
type UserRepository interface {
Create(user *User) error
GetByID(id uint) (*User, error)
GetByUsername(username string) (*User, error)
Update(user *User) error
Delete(id uint) error
}
// userRepository implements UserRepository interface.
type userRepository struct {
db *gorm.DB
}
// NewUserRepository creates a new UserRepository instance.
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db: db}
}
/api/v1/users # 用户列表 /api/v1/users/{id} # 特定用户 /api/v1/users/{id}/posts # 用户的文章
// UserController handles user-related HTTP requests.
type UserController struct {
userService UserService
}
// NewUserController creates a new UserController instance.
func NewUserController(userService UserService) *UserController {
return &UserController{
userService: userService,
}
}
// GetUser handles GET /api/v1/users/{id} requests.
func (c *UserController) GetUser(ctx *gin.Context) {
id, err := strconv.Atoi(ctx.Param("id"))
if err != nil {
ctx.JSON(http.StatusBadRequest, Response{
Code: CodeBadRequest,
Message: "invalid user id",
})
return
}
user, err := c.userService.GetUser(id)
if err != nil {
ctx.JSON(http.StatusInternalError, Response{
Code: CodeInternalError,
Message: err.Error(),
})
return
}
ctx.JSON(http.StatusOK, Response{
Code: CodeSuccess,
Data: user,
})
}
_test.go 结尾Test 开头Benchmark 开头Example 开头func TestUserService_CreateUser(t *testing.T) {
tests := []struct {
name string
input *User
want *User
wantErr bool
}{
{
name: "valid user",
input: &User{
Username: "testuser",
Password: "password123",
},
want: &User{
ID: 1,
Username: "testuser",
},
wantErr: false,
},
// more test cases...
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test implementation
})
}
}
// Config represents the application configuration.
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
}
// ServerConfig represents server configuration.
type ServerConfig struct {
Port int `mapstructure:"port"`
Mode string `mapstructure:"mode"`
}
// 使用结构化日志记录关键信息
logger.Info("user created successfully",
zap.String("username", user.Username),
zap.Int("userID", user.ID),
zap.Duration("duration", time.Since(start)),
)
// 错误日志应包含足够的上下文
logger.Error("failed to create user",
zap.String("username", username),
zap.Error(err),
zap.String("trace_id", traceID),
)
// HealthController handles health check requests.
type HealthController struct {
db *gorm.DB
redis *redis.Client
}
// Check performs comprehensive health check.
func (h *HealthController) Check(ctx *gin.Context) {
status := gin.H{
"status": "ok",
"timestamp": time.Now(),
}
// 检查数据库连接
if err := h.checkDatabase(); err != nil {
status["database"] = "error: " + err.Error()
ctx.JSON(http.StatusServiceUnavailable, status)
return
}
status["database"] = "ok"
// 检查 Redis 连接
if err := h.checkRedis(); err != nil {
status["redis"] = "error: " + err.Error()
ctx.JSON(http.StatusServiceUnavailable, status)
return
}
status["redis"] = "ok"
ctx.JSON(http.StatusOK, status)
}
#!/bin/bash
# build.sh
# 设置构建变量
VERSION=$(git describe --tags --always)
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
COMMIT=$(git rev-parse HEAD)
# 构建二进制文件
go build -ldflags "-X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME} -X main.Commit=${COMMIT}" -o bin/app main.go
// 在 main.go 中定义版本信息
var (
Version = "dev"
BuildTime = "unknown"
Commit = "unknown"
)
func printVersion() {
fmt.Printf("Version: %s\n", Version)
fmt.Printf("Build Time: %s\n", BuildTime)
fmt.Printf("Commit: %s\n", Commit)
}