
Go AI 增强开发模板 —— 一个真实、可运行、开箱即用的项目骨架,专为 Golang + Fiber + GORM + PostgreSQL + Gitea Actions + AI 协作 场景设计。
可以直接复制到本地,5 分钟内启动一个 AI 友好型后端项目!
📦 go-ai-starter 项目模板(v1.0)
技术栈:Go 1.23|Fiber v2.50|GORM v2|PostgreSQL|JWT|Gitea Actions
特性:分层架构 + AI 开发规则 + 自动测试 + CI/CD
🗂️ 目录结构
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linego-ai-starter/├── .cursorrules # ← AI 行为规范(关键!)├── docs/│ └── product.md # 产品需求说明├── internal/│ ├── handler/│ │ └── user.go # HTTP handlers│ ├── service/│ │ └── user.go # 业务逻辑 + 接口│ ├── repository/│ │ └── user.go # 数据访问层│ └── model/│ └── user.go # 领域模型├── pkg/│ └── hash/│ └── bcrypt.go # 密码加密工具├── cmd/server/main.go # 启动入口├── .gitea/workflows/ci.yml # CI/CD 流程├── go.mod└── tasks.md # AI 任务清单
📄 核心文件内容
1. .cursorrules(AI 全局行为规则)
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line# Go 项目 AI 开发规则你是资深 Go 后端工程师,熟悉 Fiber、GORM、PostgreSQL 和 Clean Architecture。## 代码规范- 所有函数必须接收 context.Context- 错误处理返回 error,不 panic- 使用 errors.New() 或 fmt.Errorf() 创建错误- 密码必须用 bcrypt 加密(cost=10)- 接口定义在 internal/service,实现在同目录- 不使用全局变量,配置通过参数注入## 测试要求- 必须写 table-driven test- 覆盖:正常、边界、错误路径- 使用 testify/mock 模拟依赖## 禁止事项- 不硬编码密钥- 不忽略 context.Done()- 不写 SQL 注入风险代码## 输出格式- 代码带注释(说明“为什么”)- 返回完整可运行代码,含 import
2. internal/model/user.go
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linepackage modelimport "time"type User struct {ID string `gorm:"primaryKey;size:36"`Email string `gorm:"uniqueIndex;not null"`Password string `gorm:"not null"` // bcrypt hashCreatedAt time.TimeUpdatedAt time.Time}
3. pkg/hash/bcrypt.go
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linepackage hashimport ("golang.org/x/crypto/bcrypt")const BcryptCost = 10func HashPassword(password string) (string, error) {bytes, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)return string(bytes), err}func CheckPassword(hash, password string) bool {err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))return err == nil}
4. internal/service/user.go
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linepackage serviceimport ("context""errors""fmt""your-project/internal/model""your-project/internal/repository""your-project/pkg/hash")var (ErrUserExists = errors.New("user already exists")ErrInvalidPassword = errors.New("password must be at least 8 characters")ErrUserNotFound = errors.New("user not found"))type UserService interface {CreateUser(ctx context.Context, email, password string) (string, error)GetUserByEmail(ctx context.Context, email string) (*model.User, error)}type userService struct {userRepo repository.UserRepositoryhasher PasswordHasher}type PasswordHasher interface {HashPassword(password string) (string, error)CheckPassword(hash, password string) bool}func NewUserService(userRepo repository.UserRepository, hasher PasswordHasher) UserService {return &userService{userRepo: userRepo, hasher: hasher}}func (s *userService) CreateUser(ctx context.Context, email, password string) (string, error) {if len(password) < 8 {return "", ErrInvalidPassword}// 检查用户是否存在exists, err := s.userRepo.ExistsByEmail(ctx, email)if err != nil {return "", fmt.Errorf("check user exists: %w", err)}if exists {return "", ErrUserExists}// 加密密码hashed, err := s.hasher.HashPassword(password)if err != nil {return "", fmt.Errorf("hash password: %w", err)}user := &model.User{Email: email,Password: hashed,}if err := s.userRepo.Create(ctx, user); err != nil {return "", fmt.Errorf("create user: %w", err)}return user.ID, nil}func (s *userService) GetUserByEmail(ctx context.Context, email string) (*model.User, error) {user, err := s.userRepo.FindByEmail(ctx, email)if err != nil {return nil, fmt.Errorf("find user: %w", err)}if user == nil {return nil, ErrUserNotFound}return user, nil}
5. internal/handler/user.go
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linepackage handlerimport ("net/http""github.com/gofiber/fiber/v2""your-project/internal/service")type UserHandler struct {userService service.UserService}func NewUserHandler(us service.UserService) *UserHandler {return &UserHandler{userService: us}}type registerRequest struct {Email string `json:"email" validate:"required,email"`Password string `json:"password" validate:"required,min=8"`}func (h *UserHandler) Register(c *fiber.Ctx) error {var req registerRequestif err := c.BodyParser(&req); err != nil {return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid request"})}id, err := h.userService.CreateUser(c.Context(), req.Email, req.Password)if err != nil {switch {case errors.Is(err, service.ErrUserExists):return c.Status(http.StatusConflict).JSON(fiber.Map{"error": "email already registered"})case errors.Is(err, service.ErrInvalidPassword):return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "password too weak"})default:return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "internal error"})}}return c.Status(http.StatusCreated).JSON(fiber.Map{"user_id": id,})}
6. cmd/server/main.go
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linepackage mainimport ("log""github.com/gofiber/fiber/v2""github.com/gofiber/fiber/v2/middleware/logger""gorm.io/driver/postgres""gorm.io/gorm""your-project/internal/handler""your-project/internal/repository""your-project/internal/service""your-project/pkg/hash")func main() {// 初始化 DB(实际项目应从 env 读取 DSN)dsn := "host=localhost user=postgres password=postgres dbname=go_ai_starter port=5432 sslmode=disable"db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})if err != nil {log.Fatal("failed to connect database:", err)}// 自动迁移db.AutoMigrate(&model.User{})// 依赖注入userRepo := repository.NewUserRepository(db)userService := service.NewUserService(userRepo, &hash.BcryptHasher{})userHandler := handler.NewUserHandler(userService)app := fiber.New()app.Use(logger.New())app.Post("/api/register", userHandler.Register)log.Println("Server starting on :3000")app.Listen(":3000")}
💡 注意:你需要先创建数据库
go_ai_starter,或修改 DSN
7. .gitea/workflows/ci.yml
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linename: CIon:push:branches: [ main ]pull_request:jobs:test:runs-on: ubuntu-lateststeps:- name: Checkoutuses: actions/checkout@v3- name: Set up Gouses: actions/setup-go@v4with:go-version: '1.23'- name: Run testsrun: go test -v ./...- name: Buildrun: go build -o bin/server ./cmd/server
8. tasks.md(AI 任务驱动)
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line## [TODO] 用户注册模块- 描述:实现邮箱注册 + 密码加密 + 唯一性校验- 验收标准:- 密码 ≥8 位- 邮箱唯一,冲突返回 409- 成功返回 user_id- 关联文件:service/user.go, handler/user.go## [TODO] JWT 登录接口- 描述:邮箱+密码登录,返回 token- 待补充...
9. docs/product.md
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line# 产品需求:用户系统## 功能列表1. 用户注册(邮箱 + 密码)2. 用户登录(返回 JWT)3. 获取用户信息(需认证)## 非功能需求- 密码必须 bcrypt 加密- 所有接口支持 context 超时- 错误信息对用户友好(不暴露内部细节)
▶️ 如何启动?
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line# 1. 初始化模块go mod init your-project# 2. 安装依赖go get github.com/gofiber/fiber/v2go get gorm.io/gorm gorm.io/driver/postgresgo get golang.org/x/crypto/bcryptgo get github.com/stretchr/testify # 用于测试# 3. 创建数据库(PostgreSQL)createdb go_ai_starter# 4. 运行go run cmd/server/main.go
然后测试注册:
ounter(lineounter(lineounter(linecurl -X POST http://localhost:3000/api/register \-H "Content-Type: application/json" \-d '{"email":"test@example.com","password":"secure123"}'
✅ 下一步建议
在 VS Code / Cursor 中打开项目 → AI 会自动读取 .cursorrules编辑 tasks.md添加新任务选中接口 → 右键 “Generate Implementation” 让 AI 写代码 提交到 Gitea → 自动触发 CI
夜雨聆风