Files
backend-go/AGENTS.md
2026-03-02 10:24:11 +08:00

9.1 KiB

AGENTS.md

This file provides guidance for agentic coding agents working on this repository.

Build/Lint/Test Commands

Building

make build              # Build executable (trace-backend.exe)
make run                # Run development server
go build -o bin/backend.exe .  # Build to custom location

Testing

make test                                    # Run all integration tests (./tests/...)
go test -v ./services/...                  # Run service layer unit tests
go test -v ./tests/...                      # Run integration tests
go test -v -run TestAuthService_ValidateUser_Success ./services/...  # Single test
go test -v ./services/... -run TestSerialsService  # Run test prefix
make test-coverage                          # Generate coverage report

Code Quality

make lint                 # Run golangci-lint (configured in .golangci.yml)
make fmt                  # Format code with gofmt
make imports              # Format imports with goimports
make quality              # fmt + imports + lint (full check)
golangci-lint run ./...  # Direct lint check

Swagger Documentation

make swagger             # Generate swagger docs to docs/ directory
swag init -g main.go     # Alternative command

Code Style Guidelines

Project Structure

backend-go/
├── config/                  # Configuration management
│   └── config.go            # Config loading (.env, config.yaml, env vars)
├── controllers/             # HTTP request handlers
│   ├── auth_controller.go   # Auth: login, profile, password change
│   ├── companies_controller.go  # Company CRUD
│   ├── employees_controller.go  # Employee serials: generate, query, update, revoke, qrcode
│   ├── helper.go            # Helper functions (GetCurrentUser, BindJSON, Response)
│   └── serials_controller.go    # Company serials: generate, query, update, revoke, qrcode
├── database/                # Database connection and migrations
│   └── database.go          # GORM init, AutoMigrate
├── docs/                    # Swagger documentation (auto-generated)
├── logger/                  # Structured logging
│   └── logger.go            # Zap logger wrapper
├── middleware/              # Middleware
│   └── auth.go              # JWT auth, Admin permission check
├── models/                  # Data models and DTOs
│   └── models.go            # User, Company, Serial, EmployeeSerial and DTOs
├── routes/                  # Route configuration
│   └── routes.go            # API route registration
├── services/                # Business logic layer
│   ├── auth_service.go      # Auth: validate user, generate token, password management
│   ├── companies_service.go # Company CRUD
│   ├── employees_service.go # Employee serials: generate, query, update, revoke, qrcode
│   ├── serials_service.go   # Company serials: generate, query, update, revoke, qrcode
│   └── services_test.go     # Unit tests
├── tests/                   # Integration tests
│   └── main_test.go         # End-to-end tests
├── data/                    # SQLite data directory
├── main.go                  # Application entry point
├── go.mod                   # Go dependencies
├── Makefile                 # Build tasks
└── .env.example             # Environment variable example

Layer Responsibilities

  • controllers/: Receive HTTP requests, validate params, call services, return responses
  • services/: Business logic, data processing, database interaction, return results or errors
  • models/: Data structure definitions, GORM models, API request/response DTOs
  • middleware/: Authentication and authorization
  • routes/: Route registration, connect controllers to router

API Surface (Current)

  • Auth: POST /api/auth/login, POST /api/auth/logout, GET /api/auth/profile, PUT /api/auth/profile, POST /api/auth/change-password
  • Serials: POST /api/serials/generate, POST /api/serials/generate-with-prefix, POST /api/serials/:serialNumber/qrcode, GET /api/serials/:serialNumber/query, GET /api/serials, PATCH /api/serials/:serialNumber, PUT /api/serials/:serialNumber, POST /api/serials/:serialNumber/revoke
  • Companies: GET /api/companies/stats/overview, GET /api/companies, GET /api/companies/:companyName, POST /api/companies, PATCH /api/companies/:companyName, PUT /api/companies/:companyName, POST /api/companies/:companyName/revoke, DELETE /api/companies/:companyName/serials/:serialNumber, DELETE /api/companies/:companyName
  • Employee Serials: POST /api/employee-serials/generate, POST /api/employee-serials/:serialNumber/qrcode, GET /api/employee-serials/:serialNumber/query, GET /api/employee-serials, PATCH /api/employee-serials/:serialNumber, PUT /api/employee-serials/:serialNumber, POST /api/employee-serials/:serialNumber/revoke

Import Organization

Standard imports followed by third-party imports, then project imports (sorted alphabetically):

import (
    "errors"
    "net/http"
    
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v5"
    
    "git.beifan.cn/trace-system/backend-go/config"
    "git.beifan.cn/trace-system/backend-go/models"
)

Naming Conventions

  • Controllers: AuthController, SerialsController, CompaniesController, EmployeeSerialsController
  • Services: AuthService, SerialsService, CompaniesService, EmployeeSerialsService
  • Models: User, Company, Serial (use PascalCase for exported structs)
  • DTOs: LoginDTO, ChangePasswordDTO, UpdateProfileDTO (DTO suffix)
  • Functions: ValidateUser, Generate, Query (PascalCase for exported)
  • Variables: camelCase for local variables, PascalCase for exported

Struct Tags

  • JSON tags: lowercase camelCase, json:"username", json:"accessToken"
  • GORM tags: PascalCase, gorm:"primaryKey", gorm:"uniqueIndex;size:255"
  • Validate tags: validate:"required,min=6", validate:"omitempty,email"
  • Omit empty: json:"omitempty" for optional fields
  • Ignore in JSON: json:"-" for sensitive fields (Password, DeletedAt)

Error Handling

Services: Return errors, don't handle them directly:

func (s *AuthService) ValidateUser(username, password string) (models.User, error) {
    var user models.User
    err := database.DB.Where("username = ?", username).First(&user).Error
    if err != nil {
        return models.User{}, errors.New("用户名或密码错误")
    }
    return user, nil
}

Controllers: Use helper functions for consistent responses:

func (c *AuthController) Login(ctx *gin.Context) {
    var loginData models.LoginDTO
    if !BindJSON(ctx, &loginData) {
        return
    }
    
    user, err := c.authService.ValidateUser(loginData.Username, loginData.Password)
    if err != nil {
        ErrorResponse(ctx, http.StatusUnauthorized, err.Error())
        return
    }
    
    SuccessResponse(ctx, "登录成功", gin.H{"user": user})
}

Response Format

Success response:

{
  "message": "操作成功",
  "data": { ... }
}

Error response:

{
  "message": "错误描述",
  "error": "详细错误信息"
}

Logging

Use structured logger from logger package:

logger.Info("用户登录", logger.String("username", user.Username))
logger.Error("数据库错误", logger.Err(err))
logger.Fatal("致命错误", logger.Err(err))

Database Operations

  • Use database.DB for GORM operations
  • Always check for errors: if err != nil { ... }
  • Use Unscoped for permanent deletion: database.DB.Unscoped().Delete(...)
  • Test cleanup: database.DB.Unscoped().Where("1 = 1").Delete(&models.User{})
  • During AutoMigrate(), default admin is seeded only when user table is empty

Testing

  • Use testify/assert for assertions
  • Use TestMain for setup/cleanup (init DB, create test data, cleanup on exit)
  • Test naming: Test{Service}_{Method}_{Scenario}
  • Clean up test data after tests: delete all records
  • Use bcrypt for password hashing in tests

Swagger Annotations

Add Swagger comments to controller methods:

// @Summary Brief description
// @Description Detailed description
// @Tags Category
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param name body models.DTO true "Description"
// @Success 200 {object} models.ResponseDTO
// @Failure 400 {object} models.ErrorResponse
// @Router /path [method]

After modifying Swagger annotations, run make swagger.

Configuration

  • Load with config.LoadConfig()
  • Access with config.GetAppConfig()
  • Environment variables must use APP_ prefix (e.g. APP_SERVER_PORT, APP_DATABASE_DRIVER, APP_DATABASE_SQLITE_PATH, APP_JWT_SECRET)
  • .env file for local development (not committed)

Middleware

  • JWTAuthMiddleware: Validates JWT tokens, sets user in context
  • AdminMiddleware: Checks if user has admin role
  • Access current user: user, ok := GetCurrentUser(ctx)

Git Hooks

Run make quality before committing to ensure code passes all checks.