Files
backend-go/AGENTS.md
T
2026-06-06 13:50:56 +08:00

13 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 TestEmployeeSerialsService  # 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
│   ├── aftersales_controller.go # Aftersales orders: create, query, submit, confirm, qrcode
│   ├── auth_controller.go   # Auth: login, profile, password change
│   ├── dashboard_controller.go  # Dashboard work-order statistics
│   ├── employees_controller.go  # Employee permission codes: generate, query, update, revoke, qrcode
│   ├── helper.go            # Helper functions (GetCurrentUser, BindJSON, Response)
│   ├── product_traces_controller.go # Product traceability
│   ├── project_orders_controller.go # Project work orders
│   └── users_controller.go      # Employee master data / backend account management
├── 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 / Technician permission checks
├── models/                  # Data models and DTOs
│   └── models.go            # User, Company, EmployeeSerial, ProductTrace, AftersalesOrder, ProjectOrder and DTOs
├── routes/                  # Route configuration
│   └── routes.go            # API route registration
├── services/                # Business logic layer
│   ├── aftersales_service.go # Aftersales orders: create, list, update, submit, customer confirm, qrcode
│   ├── auth_service.go      # Auth: validate user, generate token, password management
│   ├── dashboard_service.go # Dashboard work-order statistics
│   ├── employees_service.go # Employee permission codes: generate, query, update, revoke, qrcode
│   ├── product_traces_service.go # Product traceability
│   ├── project_orders_service.go # Project work orders
│   ├── aftersales_service_test.go # Aftersales unit tests
│   ├── services_test.go     # Auth / employee permission-code unit tests
│   ├── users_service.go     # Employee CRUD, role management, password reset (admin)
│   └── users_service_test.go # Employee/user account 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
  • Dashboard: GET /api/dashboard/stats
  • Employee Permission Codes: 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, DELETE /api/employee-serials/:serialNumber
  • Product Traces: GET /api/product-traces/:serialNumber/query, POST /api/product-traces, GET /api/product-traces, GET /api/product-traces/:serialNumber, PATCH /api/product-traces/:serialNumber, POST /api/product-traces/:serialNumber/qrcode, POST /api/product-traces/:serialNumber/wechat-qrcode, POST /api/product-traces/:serialNumber/revoke, DELETE /api/product-traces/:serialNumber
  • Aftersales (公开): GET /api/aftersales/:serialNumber/query, POST /api/aftersales/:serialNumber/confirm
  • Aftersales (工单角色+管理员): GET /api/aftersales, GET /api/aftersales/:serialNumber, PATCH /api/aftersales/:serialNumber, POST /api/aftersales/:serialNumber/qrcode, POST /api/aftersales/:serialNumber/submit
  • Aftersales (仅管理员): POST /api/aftersales, POST /api/aftersales/:serialNumber/reassign, POST /api/aftersales/:serialNumber/force-close, DELETE /api/aftersales/:serialNumber
  • Project Orders (公开): GET /api/project-orders/:serialNumber/query, POST /api/project-orders/:serialNumber/site-images, POST /api/project-orders/:serialNumber/complete
  • Project Orders (工单角色+管理员): GET /api/project-orders, GET /api/project-orders/:serialNumber, PATCH /api/project-orders/:serialNumber, POST /api/project-orders/:serialNumber/qrcode, POST /api/project-orders/:serialNumber/submit
  • Project Orders (仅管理员): POST /api/project-orders, POST /api/project-orders/:serialNumber/reassign, POST /api/project-orders/:serialNumber/force-close, DELETE /api/project-orders/:serialNumber
  • Users (仅管理员): GET /api/users/assignable
  • Employees (仅管理员): POST /api/employees, GET /api/employees, PATCH /api/employees/:id, POST /api/employees/:id/reset-password, DELETE /api/employees/:id

Roles and permissions

  • admin is the system administrator role and has full backend access.
  • Permission management may create/edit only four equal work-order roles: software_engineer, hardware_engineer, business_manager, project_manager.
  • The four work-order roles can log in only to process aftersales/project orders assigned to themselves.
  • The four work-order roles must not access dashboard, permission management, product traces, assignable user lists, creation/deletion/reassign/force-close endpoints, or other admin-only resources.
  • technician is legacy-compatible and may keep work-order access, but must not be offered as a new role.
  • employee is legacy/no backend login access and must not be offered as a new role.
  • Creating an employee through /api/employees creates employee master data and automatically generates one employee permission code bound by employeeId.
  • Creating managed work-order roles requires an initial password; creating admin or employee through permission management is forbidden.

Business Boundaries

  • Enterprise/company-code management was removed. Do not reintroduce /api/companies, /api/serials, CompaniesService, SerialsService, or a company-management UI.
  • The Company model remains only as internal compatibility for employee permission-code ownership; it is not an exposed management feature.
  • Aftersales/project order companyName is customer information stored on the order only; creating or updating a work order must not create a managed-company feature.

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, DashboardController, EmployeeSerialsController, ProductTracesController, ProjectOrdersController
  • Services: AuthService, DashboardService, EmployeeSerialsService, ProductTracesService, ProjectOrdersService
  • Models: User, Company, EmployeeSerial, ProductTrace, AftersalesOrder, ProjectOrder (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})
}
  • Use ErrorResponse for API errors so messages are user-friendly and consistent.
  • Do not leak raw DB/stack errors to clients; log internal details and return safe messages.

Response Format

Success response:

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

Some endpoints also return Node-compatible top-level keys (e.g. serial, serials, pagination) to preserve frontend compatibility.

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
  • TechnicianMiddleware: Allows admin, the four managed work-order roles, and legacy technician (used for work-order processing endpoints)
  • Access current user: user, ok := GetCurrentUser(ctx)

Git Hooks

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