17 changed files with 0 additions and 842 deletions
@ -1,68 +0,0 @@ |
|||||
# Aurora Golang Clean Architecture Template |
|
||||
|
|
||||
这是一个基于 **Go + Gin + GORM** 的多数据库自动切换项目模板,遵循 Clean Architecture 分层思想。模板默认支持 SQLite、MySQL 与 PostgreSQL,使用配置文件即可切换数据库驱动。 |
|
||||
|
|
||||
## 结构说明 |
|
||||
|
|
||||
``` |
|
||||
. |
|
||||
├── cmd/ |
|
||||
│ └── server/ # 应用入口 |
|
||||
├── configs/ |
|
||||
│ └── config.yaml # 默认配置文件 |
|
||||
├── internal/ |
|
||||
│ ├── config/ # 配置加载 |
|
||||
│ ├── domain/ # 领域模型和接口 |
|
||||
│ ├── infrastructure/ # 数据库与仓储实现 |
|
||||
│ ├── interface/ # HTTP 适配器 |
|
||||
│ └── usecase/ # 应用服务 |
|
||||
├── pkg/ |
|
||||
│ └── logger/ # 日志封装 |
|
||||
└── go.mod |
|
||||
``` |
|
||||
|
|
||||
## 快速开始 |
|
||||
|
|
||||
1. 安装依赖 |
|
||||
```bash |
|
||||
go mod tidy |
|
||||
``` |
|
||||
|
|
||||
2. 根据需要调整 `configs/config.yaml` 或 `.env` 中的数据库驱动参数,可选值: |
|
||||
- `sqlite` |
|
||||
- `mysql` |
|
||||
- `postgres` / `postgresql` |
|
||||
|
|
||||
3. 运行服务 |
|
||||
```bash |
|
||||
go run ./cmd/server |
|
||||
``` |
|
||||
|
|
||||
4. 示例接口 |
|
||||
- `GET /api/v1/users` |
|
||||
- `GET /api/v1/users/:id` |
|
||||
- `POST /api/v1/users` |
|
||||
|
|
||||
## 数据库自动切换 |
|
||||
|
|
||||
通过配置文件中的 `database.driver` 自动选择对应的 GORM 驱动: |
|
||||
|
|
||||
- SQLite 会在 `data/aurora.db` 下生成数据库文件(路径可配置) |
|
||||
- MySQL、PostgreSQL 根据配置生成 DSN,支持额外连接参数 |
|
||||
|
|
||||
## 清晰的分层设计 |
|
||||
|
|
||||
- `domain`: 定义核心业务实体与仓储接口 |
|
||||
- `usecase`: 聚合业务逻辑,依赖仓储接口 |
|
||||
- `infrastructure`: 提供 GORM 仓储实现、数据库初始化等 |
|
||||
- `interface/http`: 提供 Gin HTTP 适配器 |
|
||||
- `cmd/server`: 组装依赖、启动服务 |
|
||||
|
|
||||
## 下一步 |
|
||||
|
|
||||
- 扩展更多领域模型与用例 |
|
||||
- 集成依赖注入框架 |
|
||||
- 增加测试与 CI/CD |
|
||||
|
|
||||
欢迎在此模板基础上继续拓展,构建您的业务服务。 |
|
||||
|
|
||||
@ -1,58 +0,0 @@ |
|||||
package main |
|
||||
|
|
||||
import ( |
|
||||
"fmt" |
|
||||
"log" |
|
||||
"path/filepath" |
|
||||
|
|
||||
"auroragolang/docs" |
|
||||
"auroragolang/internal/config" |
|
||||
"auroragolang/internal/domain/model" |
|
||||
"auroragolang/internal/infrastructure/database" |
|
||||
infraRepo "auroragolang/internal/infrastructure/repository" |
|
||||
httpInterface "auroragolang/internal/interface/http" |
|
||||
"auroragolang/internal/usecase" |
|
||||
"auroragolang/pkg/logger" |
|
||||
|
|
||||
"gorm.io/gorm" |
|
||||
) |
|
||||
|
|
||||
// @title Aurora Service API
|
|
||||
// @version 1.0
|
|
||||
// @description Aurora Golang 示例 REST API,基于 Gin + GORM。
|
|
||||
// @BasePath /api/v1
|
|
||||
func main() { |
|
||||
root := "." |
|
||||
cfg, err := config.LoadConfig(filepath.Join(root, "configs")) |
|
||||
if err != nil { |
|
||||
log.Fatalf("加载配置失败: %v", err) |
|
||||
} |
|
||||
|
|
||||
appLogger := logger.New(cfg.App.Name) |
|
||||
|
|
||||
docs.SwaggerInfo.Version = cfg.App.Env |
|
||||
docs.SwaggerInfo.BasePath = "/api/v1" |
|
||||
|
|
||||
db, err := database.Connect(cfg.Database) |
|
||||
if err != nil { |
|
||||
appLogger.Fatalf("数据库连接失败: %v", err) |
|
||||
} |
|
||||
|
|
||||
autoMigrate(db, appLogger) |
|
||||
|
|
||||
userRepo := infraRepo.NewGormUserRepository(db) |
|
||||
userService := usecase.NewUserService(userRepo) |
|
||||
router := httpInterface.NewRouter(userService) |
|
||||
|
|
||||
appLogger.Printf("服务启动: 端口 %d,环境 %s", cfg.App.Port, cfg.App.Env) |
|
||||
if err := router.Run(fmt.Sprintf(":%d", cfg.App.Port)); err != nil { |
|
||||
appLogger.Fatalf("服务器启动失败: %v", err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func autoMigrate(db *gorm.DB, log *logger.Logger) { |
|
||||
if err := db.AutoMigrate(&model.User{}); err != nil { |
|
||||
log.Fatalf("自动迁移失败: %v", err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@ -1,24 +0,0 @@ |
|||||
app: |
|
||||
name: AuroraService |
|
||||
env: development |
|
||||
port: 8080 |
|
||||
|
|
||||
database: |
|
||||
driver: sqlite |
|
||||
sqlite: |
|
||||
path: data/aurora.db |
|
||||
mysql: |
|
||||
host: localhost |
|
||||
port: 3306 |
|
||||
user: root |
|
||||
password: password |
|
||||
dbname: aurora |
|
||||
params: charset=utf8mb4&parseTime=True&loc=Local |
|
||||
postgres: |
|
||||
host: localhost |
|
||||
port: 5432 |
|
||||
user: postgres |
|
||||
password: password |
|
||||
dbname: aurora |
|
||||
sslmode: disable |
|
||||
|
|
||||
@ -1,40 +0,0 @@ |
|||||
## 项目架构说明 |
|
||||
|
|
||||
### 技术栈概览 |
|
||||
- Web 框架:Gin (`internal/interface/http`) |
|
||||
- ORM 层:GORM(`internal/infrastructure/database`、`internal/infrastructure/repository`) |
|
||||
- 接口文档:Swaggo(`docs/docs.go`、`/swagger` 路由) |
|
||||
- 配置管理:Viper + YAML (`internal/config`) |
|
||||
- 日志:自定义包装(`pkg/logger`) |
|
||||
|
|
||||
### 数据库自动切换 |
|
||||
- 配置文件 `configs/config.yaml` 通过 `database.driver` 控制当前使用的数据库(未设置时自动回退为 `sqlite`)。 |
|
||||
- 支持的驱动: |
|
||||
- `sqlite`:默认用于本地与轻量测试;路径可通过 `database.sqlite.path` 自定义。 |
|
||||
- `mysql`:提供账号、主机、端口、库名与参数配置。 |
|
||||
- `postgres`:提供账号、主机、端口、库名与 SSL 模式配置。 |
|
||||
- `internal/infrastructure/database/database.go` 根据配置动态选择驱动并建立连接,确保应用无需改动即可切换数据库。 |
|
||||
|
|
||||
### 自动迁移机制 |
|
||||
- `cmd/server/main.go` 中的 `autoMigrate` 函数会在启动时对 `internal/domain/model` 内的模型执行 `db.AutoMigrate`。 |
|
||||
- 当前示例模型为 `model.User`,可按需扩展更多模型。 |
|
||||
|
|
||||
### 分层结构(类 Clean Architecture) |
|
||||
- `internal/domain`:领域模型与仓储接口。 |
|
||||
- `internal/usecase`:业务用例与应用服务。 |
|
||||
- `internal/infrastructure`:具体实现(数据库、仓储等)。 |
|
||||
- `internal/interface/http`:Gin 路由与控制器。 |
|
||||
- `cmd/server`:程序入口,负责初始化配置、日志、数据库、依赖注入以及启动 HTTP 服务。 |
|
||||
|
|
||||
### 配置流程 |
|
||||
1. `cmd/server/main.go` 调用 `config.LoadConfig` 读取 `configs` 目录下的 `config.yaml`。 |
|
||||
2. 根据配置创建日志实例,初始化数据库连接。 |
|
||||
3. 执行自动迁移,注入仓储与服务,再初始化 Gin 路由并启动服务。 |
|
||||
4. 注册 `/swagger/*any` 路由,直接访问并查看自动生成的 Swagger UI。 |
|
||||
|
|
||||
### 快速验证 |
|
||||
1. 修改 `configs/config.yaml` 中的 `database.driver` 为 `sqlite`、`mysql` 或 `postgres`。 |
|
||||
2. 启动服务 `go run ./cmd/server`,确认可正常连接并迁移数据库。 |
|
||||
3. 访问 `GET /api/v1/users` 等 REST 接口验证路由与仓储是否正常工作。 |
|
||||
|
|
||||
|
|
||||
@ -1,41 +0,0 @@ |
|||||
## 项目代码目录 |
|
||||
|
|
||||
``` |
|
||||
AuroraGolang/ |
|
||||
├── cmd/ |
|
||||
│ └── server/ |
|
||||
│ └── main.go # 应用入口,加载配置、初始化依赖、启动 Gin |
|
||||
├── configs/ |
|
||||
│ └── config.yaml # 默认配置(App、数据库驱动与连接信息) |
|
||||
├── internal/ |
|
||||
│ ├── config/ |
|
||||
│ │ └── config.go # 配置加载与环境覆盖 |
|
||||
│ ├── domain/ |
|
||||
│ │ ├── model/ |
|
||||
│ │ │ └── user.go # 领域模型定义 |
|
||||
│ │ └── repository/ |
|
||||
│ │ └── user_repository.go# 仓储接口(领域层) |
|
||||
│ ├── infrastructure/ |
|
||||
│ │ ├── database/ |
|
||||
│ │ │ └── database.go # 数据库连接与驱动切换逻辑 |
|
||||
│ │ └── repository/ |
|
||||
│ │ └── user_repository_gorm.go |
|
||||
│ ├── interface/ |
|
||||
│ │ └── http/ |
|
||||
│ │ ├── router.go # Gin 路由注册 |
|
||||
│ │ └── user_handler.go # 控制器实现 |
|
||||
│ └── usecase/ |
|
||||
│ └── user_service.go # 应用服务/用例 |
|
||||
├── pkg/ |
|
||||
│ └── logger/ |
|
||||
│ └── logger.go # 日志封装 |
|
||||
├── docs/ |
|
||||
│ ├── architecture.md # 架构说明 |
|
||||
│ ├── code_structure.md # 代码目录概览 |
|
||||
│ └── docs.go # Swaggo 生成的 Swagger 模板(手动维护) |
|
||||
├── go.mod |
|
||||
├── README.md |
|
||||
└── modify.md |
|
||||
``` |
|
||||
|
|
||||
|
|
||||
@ -1,227 +0,0 @@ |
|||||
package docs |
|
||||
|
|
||||
import ( |
|
||||
"encoding/json" |
|
||||
"strings" |
|
||||
|
|
||||
"github.com/swaggo/swag" |
|
||||
) |
|
||||
|
|
||||
const docTemplate = `{ |
|
||||
"swagger": "2.0", |
|
||||
"info": { |
|
||||
"description": "{{.Description}}", |
|
||||
"title": "{{.Title}}", |
|
||||
"contact": {}, |
|
||||
"license": {}, |
|
||||
"version": "{{.Version}}" |
|
||||
}, |
|
||||
"host": "{{.Host}}", |
|
||||
"basePath": "{{.BasePath}}", |
|
||||
"schemes": {{ marshal .Schemes }}, |
|
||||
"paths": { |
|
||||
"/api/v1/users": { |
|
||||
"get": { |
|
||||
"tags": [ |
|
||||
"users" |
|
||||
], |
|
||||
"summary": "获取用户列表", |
|
||||
"description": "返回所有用户", |
|
||||
"produces": [ |
|
||||
"application/json" |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "OK", |
|
||||
"schema": { |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"$ref": "#/definitions/model.User" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"500": { |
|
||||
"description": "Internal Server Error", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/http.ErrorResponse" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"post": { |
|
||||
"tags": [ |
|
||||
"users" |
|
||||
], |
|
||||
"summary": "创建用户", |
|
||||
"description": "创建新的用户记录", |
|
||||
"consumes": [ |
|
||||
"application/json" |
|
||||
], |
|
||||
"produces": [ |
|
||||
"application/json" |
|
||||
], |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"description": "用户数据", |
|
||||
"name": "user", |
|
||||
"in": "body", |
|
||||
"required": true, |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/model.User" |
|
||||
} |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"201": { |
|
||||
"description": "Created", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/model.User" |
|
||||
} |
|
||||
}, |
|
||||
"400": { |
|
||||
"description": "Bad Request", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/http.ErrorResponse" |
|
||||
} |
|
||||
}, |
|
||||
"500": { |
|
||||
"description": "Internal Server Error", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/http.ErrorResponse" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"/api/v1/users/{id}": { |
|
||||
"get": { |
|
||||
"tags": [ |
|
||||
"users" |
|
||||
], |
|
||||
"summary": "获取指定用户", |
|
||||
"description": "根据用户 ID 获取详情", |
|
||||
"produces": [ |
|
||||
"application/json" |
|
||||
], |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"type": "integer", |
|
||||
"format": "int64", |
|
||||
"description": "用户 ID", |
|
||||
"name": "id", |
|
||||
"in": "path", |
|
||||
"required": true |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "OK", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/model.User" |
|
||||
} |
|
||||
}, |
|
||||
"400": { |
|
||||
"description": "Bad Request", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/http.ErrorResponse" |
|
||||
} |
|
||||
}, |
|
||||
"404": { |
|
||||
"description": "Not Found", |
|
||||
"schema": { |
|
||||
"$ref": "#/definitions/http.ErrorResponse" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"definitions": { |
|
||||
"http.ErrorResponse": { |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"error": { |
|
||||
"type": "string" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"model.User": { |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"ID": { |
|
||||
"type": "integer", |
|
||||
"format": "int64" |
|
||||
}, |
|
||||
"CreatedAt": { |
|
||||
"type": "string", |
|
||||
"format": "date-time" |
|
||||
}, |
|
||||
"UpdatedAt": { |
|
||||
"type": "string", |
|
||||
"format": "date-time" |
|
||||
}, |
|
||||
"DeletedAt": { |
|
||||
"type": "string", |
|
||||
"format": "date-time" |
|
||||
}, |
|
||||
"name": { |
|
||||
"type": "string" |
|
||||
}, |
|
||||
"email": { |
|
||||
"type": "string" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}` |
|
||||
|
|
||||
type swaggerInfo struct { |
|
||||
Version string |
|
||||
Host string |
|
||||
BasePath string |
|
||||
Schemes []string |
|
||||
Title string |
|
||||
Description string |
|
||||
} |
|
||||
|
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
|
||||
var SwaggerInfo = swaggerInfo{ |
|
||||
Version: "1.0", |
|
||||
Host: "", |
|
||||
BasePath: "/api/v1", |
|
||||
Schemes: []string{"http"}, |
|
||||
Title: "Aurora Service API", |
|
||||
Description: "Aurora Golang 示例 REST API,基于 Gin + GORM。", |
|
||||
} |
|
||||
|
|
||||
type s struct{} |
|
||||
|
|
||||
func (s *s) ReadDoc() string { |
|
||||
doc := docTemplate |
|
||||
doc = strings.Replace(doc, "{{.Version}}", SwaggerInfo.Version, -1) |
|
||||
doc = strings.Replace(doc, "{{.Host}}", SwaggerInfo.Host, -1) |
|
||||
doc = strings.Replace(doc, "{{.BasePath}}", SwaggerInfo.BasePath, -1) |
|
||||
doc = strings.Replace(doc, "{{.Title}}", SwaggerInfo.Title, -1) |
|
||||
doc = strings.Replace(doc, "{{.Description}}", SwaggerInfo.Description, -1) |
|
||||
doc = strings.Replace(doc, "{{ marshal .Schemes }}", marshal(SwaggerInfo.Schemes), -1) |
|
||||
return doc |
|
||||
} |
|
||||
|
|
||||
func marshal(v interface{}) string { |
|
||||
if v == nil { |
|
||||
return "[]" |
|
||||
} |
|
||||
|
|
||||
b, err := json.Marshal(v) |
|
||||
if err != nil { |
|
||||
return "[]" |
|
||||
} |
|
||||
|
|
||||
return strings.ReplaceAll(string(b), `\u0026`, "&") |
|
||||
} |
|
||||
|
|
||||
func init() { |
|
||||
swag.Register(swag.Name, &s{}) |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@ -1,17 +0,0 @@ |
|||||
module auroragolang |
|
||||
|
|
||||
go 1.22 |
|
||||
|
|
||||
require ( |
|
||||
github.com/gin-gonic/gin v1.10.0 |
|
||||
github.com/swaggo/files v1.1.3 |
|
||||
github.com/swaggo/gin-swagger v1.6.0 |
|
||||
github.com/swaggo/swag v1.16.3 |
|
||||
github.com/joho/godotenv v1.5.1 |
|
||||
github.com/spf13/viper v1.18.2 |
|
||||
gorm.io/driver/mysql v1.5.7 |
|
||||
gorm.io/driver/postgres v1.5.8 |
|
||||
gorm.io/driver/sqlite v1.5.6 |
|
||||
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde |
|
||||
) |
|
||||
|
|
||||
@ -1,80 +0,0 @@ |
|||||
package config |
|
||||
|
|
||||
import ( |
|
||||
"fmt" |
|
||||
"path/filepath" |
|
||||
"strings" |
|
||||
|
|
||||
"github.com/joho/godotenv" |
|
||||
"github.com/spf13/viper" |
|
||||
) |
|
||||
|
|
||||
type AppConfig struct { |
|
||||
Name string `mapstructure:"name"` |
|
||||
Env string `mapstructure:"env"` |
|
||||
Port int `mapstructure:"port"` |
|
||||
} |
|
||||
|
|
||||
type SQLiteConfig struct { |
|
||||
Path string `mapstructure:"path"` |
|
||||
} |
|
||||
|
|
||||
type MySQLConfig struct { |
|
||||
Host string `mapstructure:"host"` |
|
||||
Port int `mapstructure:"port"` |
|
||||
User string `mapstructure:"user"` |
|
||||
Password string `mapstructure:"password"` |
|
||||
DBName string `mapstructure:"dbname"` |
|
||||
Params string `mapstructure:"params"` |
|
||||
} |
|
||||
|
|
||||
type PostgresConfig struct { |
|
||||
Host string `mapstructure:"host"` |
|
||||
Port int `mapstructure:"port"` |
|
||||
User string `mapstructure:"user"` |
|
||||
Password string `mapstructure:"password"` |
|
||||
DBName string `mapstructure:"dbname"` |
|
||||
SSLMode string `mapstructure:"sslmode"` |
|
||||
} |
|
||||
|
|
||||
type DatabaseConfig struct { |
|
||||
Driver string `mapstructure:"driver"` |
|
||||
SQLite SQLiteConfig `mapstructure:"sqlite"` |
|
||||
MySQL MySQLConfig `mapstructure:"mysql"` |
|
||||
Postgres PostgresConfig `mapstructure:"postgres"` |
|
||||
} |
|
||||
|
|
||||
type Config struct { |
|
||||
App AppConfig `mapstructure:"app"` |
|
||||
Database DatabaseConfig `mapstructure:"database"` |
|
||||
} |
|
||||
|
|
||||
func LoadConfig(configPath string) (*Config, error) { |
|
||||
v := viper.New() |
|
||||
|
|
||||
envPath := filepath.Join(configPath, ".env") |
|
||||
_ = godotenv.Load(envPath) // ignore missing .env
|
|
||||
|
|
||||
v.AddConfigPath(configPath) |
|
||||
v.SetConfigName("config") |
|
||||
v.SetConfigType("yaml") |
|
||||
|
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) |
|
||||
v.AutomaticEnv() |
|
||||
|
|
||||
if err := v.ReadInConfig(); err != nil { |
|
||||
return nil, fmt.Errorf("读取配置失败: %w", err) |
|
||||
} |
|
||||
|
|
||||
cfg := new(Config) |
|
||||
if err := v.Unmarshal(cfg); err != nil { |
|
||||
return nil, fmt.Errorf("解析配置失败: %w", err) |
|
||||
} |
|
||||
|
|
||||
if strings.TrimSpace(cfg.Database.Driver) == "" { |
|
||||
cfg.Database.Driver = "sqlite" |
|
||||
} |
|
||||
|
|
||||
return cfg, nil |
|
||||
} |
|
||||
|
|
||||
@ -1,10 +0,0 @@ |
|||||
package model |
|
||||
|
|
||||
import "gorm.io/gorm" |
|
||||
|
|
||||
type User struct { |
|
||||
gorm.Model |
|
||||
Name string `json:"name" gorm:"size:255;not null"` |
|
||||
Email string `json:"email" gorm:"size:255;uniqueIndex;not null"` |
|
||||
} |
|
||||
|
|
||||
@ -1,10 +0,0 @@ |
|||||
package repository |
|
||||
|
|
||||
import "auroragolang/internal/domain/model" |
|
||||
|
|
||||
type UserRepository interface { |
|
||||
List() ([]model.User, error) |
|
||||
GetByID(id uint) (*model.User, error) |
|
||||
Create(user *model.User) error |
|
||||
} |
|
||||
|
|
||||
@ -1,55 +0,0 @@ |
|||||
package database |
|
||||
|
|
||||
import ( |
|
||||
"fmt" |
|
||||
"os" |
|
||||
"path/filepath" |
|
||||
"strings" |
|
||||
|
|
||||
"auroragolang/internal/config" |
|
||||
|
|
||||
"gorm.io/driver/mysql" |
|
||||
"gorm.io/driver/postgres" |
|
||||
"gorm.io/driver/sqlite" |
|
||||
"gorm.io/gorm" |
|
||||
) |
|
||||
|
|
||||
func Connect(cfg config.DatabaseConfig) (*gorm.DB, error) { |
|
||||
driver := strings.ToLower(cfg.Driver) |
|
||||
switch driver { |
|
||||
case "sqlite": |
|
||||
dbPath := cfg.SQLite.Path |
|
||||
if !filepath.IsAbs(dbPath) { |
|
||||
dbPath = filepath.Join(".", dbPath) |
|
||||
} |
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0o755); err != nil { |
|
||||
return nil, fmt.Errorf("创建SQLite目录失败: %w", err) |
|
||||
} |
|
||||
return gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) |
|
||||
case "mysql": |
|
||||
dsn := fmt.Sprintf( |
|
||||
"%s:%s@tcp(%s:%d)/%s?%s", |
|
||||
cfg.MySQL.User, |
|
||||
cfg.MySQL.Password, |
|
||||
cfg.MySQL.Host, |
|
||||
cfg.MySQL.Port, |
|
||||
cfg.MySQL.DBName, |
|
||||
cfg.MySQL.Params, |
|
||||
) |
|
||||
return gorm.Open(mysql.Open(dsn), &gorm.Config{}) |
|
||||
case "postgres", "postgresql": |
|
||||
dsn := fmt.Sprintf( |
|
||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", |
|
||||
cfg.Postgres.Host, |
|
||||
cfg.Postgres.Port, |
|
||||
cfg.Postgres.User, |
|
||||
cfg.Postgres.Password, |
|
||||
cfg.Postgres.DBName, |
|
||||
cfg.Postgres.SSLMode, |
|
||||
) |
|
||||
return gorm.Open(postgres.Open(dsn), &gorm.Config{}) |
|
||||
default: |
|
||||
return nil, fmt.Errorf("未知的数据库驱动: %s", cfg.Driver) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@ -1,37 +0,0 @@ |
|||||
package repository |
|
||||
|
|
||||
import ( |
|
||||
"auroragolang/internal/domain/model" |
|
||||
domainRepo "auroragolang/internal/domain/repository" |
|
||||
|
|
||||
"gorm.io/gorm" |
|
||||
) |
|
||||
|
|
||||
type GormUserRepository struct { |
|
||||
db *gorm.DB |
|
||||
} |
|
||||
|
|
||||
func NewGormUserRepository(db *gorm.DB) domainRepo.UserRepository { |
|
||||
return &GormUserRepository{db: db} |
|
||||
} |
|
||||
|
|
||||
func (r *GormUserRepository) List() ([]model.User, error) { |
|
||||
var users []model.User |
|
||||
if err := r.db.Find(&users).Error; err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
return users, nil |
|
||||
} |
|
||||
|
|
||||
func (r *GormUserRepository) GetByID(id uint) (*model.User, error) { |
|
||||
var user model.User |
|
||||
if err := r.db.First(&user, id).Error; err != nil { |
|
||||
return nil, err |
|
||||
} |
|
||||
return &user, nil |
|
||||
} |
|
||||
|
|
||||
func (r *GormUserRepository) Create(user *model.User) error { |
|
||||
return r.db.Create(user).Error |
|
||||
} |
|
||||
|
|
||||
@ -1,28 +0,0 @@ |
|||||
package http |
|
||||
|
|
||||
import ( |
|
||||
"auroragolang/internal/usecase" |
|
||||
|
|
||||
swaggerFiles "github.com/swaggo/files" |
|
||||
ginSwagger "github.com/swaggo/gin-swagger" |
|
||||
|
|
||||
"github.com/gin-gonic/gin" |
|
||||
) |
|
||||
|
|
||||
func NewRouter(userService *usecase.UserService) *gin.Engine { |
|
||||
router := gin.Default() |
|
||||
|
|
||||
userHandler := NewUserHandler(userService) |
|
||||
|
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) |
|
||||
|
|
||||
api := router.Group("/api/v1") |
|
||||
{ |
|
||||
api.GET("/users", userHandler.ListUsers) |
|
||||
api.GET("/users/:id", userHandler.GetUser) |
|
||||
api.POST("/users", userHandler.CreateUser) |
|
||||
} |
|
||||
|
|
||||
return router |
|
||||
} |
|
||||
|
|
||||
@ -1,94 +0,0 @@ |
|||||
package http |
|
||||
|
|
||||
import ( |
|
||||
"net/http" |
|
||||
"strconv" |
|
||||
|
|
||||
"auroragolang/internal/domain/model" |
|
||||
"auroragolang/internal/usecase" |
|
||||
|
|
||||
"github.com/gin-gonic/gin" |
|
||||
) |
|
||||
|
|
||||
type UserHandler struct { |
|
||||
service *usecase.UserService |
|
||||
} |
|
||||
|
|
||||
type ErrorResponse struct { |
|
||||
Error string `json:"error"` |
|
||||
} |
|
||||
|
|
||||
func NewUserHandler(service *usecase.UserService) *UserHandler { |
|
||||
return &UserHandler{service: service} |
|
||||
} |
|
||||
|
|
||||
// ListUsers godoc
|
|
||||
// @Summary 获取用户列表
|
|
||||
// @Description 返回所有用户
|
|
||||
// @Tags users
|
|
||||
// @Produce json
|
|
||||
// @Success 200 {array} model.User
|
|
||||
// @Failure 500 {object} ErrorResponse
|
|
||||
// @Router /api/v1/users [get]
|
|
||||
func (h *UserHandler) ListUsers(c *gin.Context) { |
|
||||
users, err := h.service.ListUsers() |
|
||||
if err != nil { |
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) |
|
||||
return |
|
||||
} |
|
||||
c.JSON(http.StatusOK, users) |
|
||||
} |
|
||||
|
|
||||
// GetUser godoc
|
|
||||
// @Summary 获取指定用户
|
|
||||
// @Description 根据用户 ID 获取详情
|
|
||||
// @Tags users
|
|
||||
// @Produce json
|
|
||||
// @Param id path int true "用户 ID"
|
|
||||
// @Success 200 {object} model.User
|
|
||||
// @Failure 400 {object} ErrorResponse
|
|
||||
// @Failure 404 {object} ErrorResponse
|
|
||||
// @Router /api/v1/users/{id} [get]
|
|
||||
func (h *UserHandler) GetUser(c *gin.Context) { |
|
||||
idParam := c.Param("id") |
|
||||
id, err := strconv.Atoi(idParam) |
|
||||
if err != nil { |
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的用户ID"}) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
user, err := h.service.GetUser(uint(id)) |
|
||||
if err != nil { |
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
c.JSON(http.StatusOK, user) |
|
||||
} |
|
||||
|
|
||||
// CreateUser godoc
|
|
||||
// @Summary 创建用户
|
|
||||
// @Description 创建新的用户记录
|
|
||||
// @Tags users
|
|
||||
// @Accept json
|
|
||||
// @Produce json
|
|
||||
// @Param user body model.User true "用户数据"
|
|
||||
// @Success 201 {object} model.User
|
|
||||
// @Failure 400 {object} ErrorResponse
|
|
||||
// @Failure 500 {object} ErrorResponse
|
|
||||
// @Router /api/v1/users [post]
|
|
||||
func (h *UserHandler) CreateUser(c *gin.Context) { |
|
||||
var payload model.User |
|
||||
if err := c.ShouldBindJSON(&payload); err != nil { |
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
if err := h.service.CreateUser(&payload); err != nil { |
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
c.JSON(http.StatusCreated, payload) |
|
||||
} |
|
||||
|
|
||||
@ -1,27 +0,0 @@ |
|||||
package usecase |
|
||||
|
|
||||
import ( |
|
||||
"auroragolang/internal/domain/model" |
|
||||
"auroragolang/internal/domain/repository" |
|
||||
) |
|
||||
|
|
||||
type UserService struct { |
|
||||
repo repository.UserRepository |
|
||||
} |
|
||||
|
|
||||
func NewUserService(repo repository.UserRepository) *UserService { |
|
||||
return &UserService{repo: repo} |
|
||||
} |
|
||||
|
|
||||
func (s *UserService) ListUsers() ([]model.User, error) { |
|
||||
return s.repo.List() |
|
||||
} |
|
||||
|
|
||||
func (s *UserService) GetUser(id uint) (*model.User, error) { |
|
||||
return s.repo.GetByID(id) |
|
||||
} |
|
||||
|
|
||||
func (s *UserService) CreateUser(user *model.User) error { |
|
||||
return s.repo.Create(user) |
|
||||
} |
|
||||
|
|
||||
@ -1,8 +0,0 @@ |
|||||
# 修改记录 |
|
||||
|
|
||||
- 2025-11-11:创建 Aurora Golang Clean 架构项目模板,新增多数据库配置、Gin/GORM 依赖注入、示例 User 用例以及 README 文档。 |
|
||||
- 2025-11-11:新增 `docs/architecture.md` 描述架构与数据库切换、自动迁移等能力,便于核对需求符合情况。 |
|
||||
- 2025-11-11:新增 `docs/code_structure.md` 输出项目代码目录树,方便快速浏览模块。 |
|
||||
- 2025-11-11:调整配置加载逻辑,默认使用 SQLite;同步更新架构文档说明。 |
|
||||
- 2025-11-11:集成 Swaggo,新增 `/swagger` 接口文档路由与 `docs/docs.go` 描述文件,并补充相关文档。 |
|
||||
|
|
||||
@ -1,18 +0,0 @@ |
|||||
package logger |
|
||||
|
|
||||
import ( |
|
||||
"log" |
|
||||
"os" |
|
||||
) |
|
||||
|
|
||||
type Logger struct { |
|
||||
*log.Logger |
|
||||
} |
|
||||
|
|
||||
func New(appName string) *Logger { |
|
||||
prefix := appName + " " |
|
||||
return &Logger{ |
|
||||
Logger: log.New(os.Stdout, prefix, log.LstdFlags|log.Lshortfile), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Loading…
Reference in new issue