项目初始化
This commit is contained in:
119
README.md
Normal file
119
README.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# RelationshipManagement-backend
|
||||||
|
|
||||||
|
基于 Gin 框架的分层架构后端项目
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
RelationshipManagement-backend/
|
||||||
|
├── main.go # 应用入口
|
||||||
|
├── go.mod # Go 模块依赖
|
||||||
|
├── config/ # 配置层
|
||||||
|
│ └── config.go # 配置管理
|
||||||
|
├── model/ # 数据模型层
|
||||||
|
│ └── user.go # 用户模型
|
||||||
|
├── dao/ # 数据访问层 (Data Access Object)
|
||||||
|
│ ├── db.go # 数据库连接
|
||||||
|
│ └── user_dao.go # 用户数据访问
|
||||||
|
├── service/ # 业务逻辑层
|
||||||
|
│ └── user_service.go # 用户服务
|
||||||
|
├── handler/ # 处理器层 (Controller)
|
||||||
|
│ └── user_handler.go # 用户处理器
|
||||||
|
├── router/ # 路由层
|
||||||
|
│ └── router.go # 路由配置
|
||||||
|
└── middleware/ # 中间件
|
||||||
|
├── cors.go # 跨域中间件
|
||||||
|
├── logger.go # 日志中间件
|
||||||
|
└── recovery.go # 恢复中间件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 架构说明
|
||||||
|
|
||||||
|
### 分层架构
|
||||||
|
|
||||||
|
1. **Router 层** (`router/`)
|
||||||
|
- 负责路由配置和请求分发
|
||||||
|
- 将 HTTP 请求路由到对应的 Handler
|
||||||
|
|
||||||
|
2. **Handler 层** (`handler/`)
|
||||||
|
- 处理 HTTP 请求和响应
|
||||||
|
- 参数验证和绑定
|
||||||
|
- 调用 Service 层处理业务逻辑
|
||||||
|
|
||||||
|
3. **Service 层** (`service/`)
|
||||||
|
- 业务逻辑处理
|
||||||
|
- 数据验证和业务规则
|
||||||
|
- 调用 DAO 层进行数据操作
|
||||||
|
|
||||||
|
4. **DAO 层** (`dao/`)
|
||||||
|
- 数据访问对象
|
||||||
|
- 数据库 CRUD 操作
|
||||||
|
- 数据库连接管理
|
||||||
|
|
||||||
|
5. **Model 层** (`model/`)
|
||||||
|
- 数据模型定义
|
||||||
|
- 数据库表结构映射
|
||||||
|
|
||||||
|
6. **Config 层** (`config/`)
|
||||||
|
- 配置管理
|
||||||
|
- 环境变量读取
|
||||||
|
|
||||||
|
7. **Middleware 层** (`middleware/`)
|
||||||
|
- 全局中间件
|
||||||
|
- CORS、日志、错误恢复等
|
||||||
|
|
||||||
|
## 环境配置
|
||||||
|
|
||||||
|
通过环境变量配置(可选,有默认值):
|
||||||
|
|
||||||
|
- `SERVER_PORT`: 服务器端口(默认: 8080)
|
||||||
|
- `GIN_MODE`: Gin 模式(debug/release/test,默认: debug)
|
||||||
|
- `DB_HOST`: 数据库主机(默认: localhost)
|
||||||
|
- `DB_PORT`: 数据库端口(默认: 3306)
|
||||||
|
- `DB_USER`: 数据库用户(默认: root)
|
||||||
|
- `DB_PASSWORD`: 数据库密码(默认: 空)
|
||||||
|
- `DB_NAME`: 数据库名称(默认: relationship_db)
|
||||||
|
|
||||||
|
## 运行项目
|
||||||
|
|
||||||
|
1. 安装依赖:
|
||||||
|
```bash
|
||||||
|
go mod tidy
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 配置数据库(可选,修改环境变量或代码中的默认值)
|
||||||
|
|
||||||
|
3. 运行项目:
|
||||||
|
```bash
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 访问健康检查接口:
|
||||||
|
```
|
||||||
|
GET http://localhost:8080/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 接口
|
||||||
|
|
||||||
|
### 用户相关接口
|
||||||
|
|
||||||
|
- `POST /api/users` - 创建用户
|
||||||
|
- `GET /api/users` - 获取用户列表(支持分页)
|
||||||
|
- `GET /api/users/:id` - 根据ID获取用户
|
||||||
|
- `PUT /api/users/:id` - 更新用户
|
||||||
|
- `DELETE /api/users/:id` - 删除用户
|
||||||
|
|
||||||
|
## 依赖说明
|
||||||
|
|
||||||
|
- **gin**: Web 框架
|
||||||
|
- **gorm**: ORM 框架
|
||||||
|
- **mysql**: MySQL 数据库驱动
|
||||||
|
- **logrus**: 日志库
|
||||||
|
|
||||||
|
## 开发建议
|
||||||
|
|
||||||
|
1. 添加新的业务模块时,按照相同的分层结构创建文件
|
||||||
|
2. 在 `dao/db.go` 的 `AutoMigrate()` 函数中添加新模型的自动迁移
|
||||||
|
3. 根据实际需求调整中间件和配置
|
||||||
|
4. 生产环境建议使用配置文件(如 YAML、JSON)替代环境变量
|
||||||
|
|
||||||
52
config/config.go
Normal file
52
config/config.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config 应用配置
|
||||||
|
type Config struct {
|
||||||
|
Server ServerConfig
|
||||||
|
DB DBConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConfig 服务器配置
|
||||||
|
type ServerConfig struct {
|
||||||
|
Port string
|
||||||
|
Mode string // debug, release, test
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBConfig 数据库配置
|
||||||
|
type DBConfig struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
DBName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig 加载配置
|
||||||
|
func LoadConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
Server: ServerConfig{
|
||||||
|
Port: getEnv("SERVER_PORT", "8080"),
|
||||||
|
Mode: getEnv("GIN_MODE", "debug"),
|
||||||
|
},
|
||||||
|
DB: DBConfig{
|
||||||
|
Host: getEnv("DB_HOST", "localhost"),
|
||||||
|
Port: getEnv("DB_PORT", "3306"),
|
||||||
|
User: getEnv("DB_USER", "root"),
|
||||||
|
Password: getEnv("DB_PASSWORD", ""),
|
||||||
|
DBName: getEnv("DB_NAME", "relationship_db"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnv 获取环境变量,如果不存在则返回默认值
|
||||||
|
func getEnv(key, defaultValue string) string {
|
||||||
|
if value := os.Getenv(key); value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
53
dao/db.go
Normal file
53
dao/db.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"RelationshipManagement-backend/config"
|
||||||
|
"fmt"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *gorm.DB
|
||||||
|
|
||||||
|
// InitDB 初始化数据库连接
|
||||||
|
func InitDB(cfg *config.Config) error {
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
|
cfg.DB.User,
|
||||||
|
cfg.DB.Password,
|
||||||
|
cfg.DB.Host,
|
||||||
|
cfg.DB.Port,
|
||||||
|
cfg.DB.DBName,
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动迁移(开发环境使用,生产环境建议使用迁移工具)
|
||||||
|
// AutoMigrate()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoMigrate 自动迁移数据库表
|
||||||
|
func AutoMigrate() error {
|
||||||
|
// 在这里添加需要自动迁移的模型
|
||||||
|
// return DB.AutoMigrate(&model.User{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseDB 关闭数据库连接
|
||||||
|
func CloseDB() error {
|
||||||
|
sqlDB, err := DB.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sqlDB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
69
dao/user_dao.go
Normal file
69
dao/user_dao.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"RelationshipManagement-backend/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserDAO 用户数据访问对象
|
||||||
|
type UserDAO struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserDAO 创建用户DAO实例
|
||||||
|
func NewUserDAO(db *gorm.DB) *UserDAO {
|
||||||
|
return &UserDAO{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 创建用户
|
||||||
|
func (dao *UserDAO) Create(user *model.User) error {
|
||||||
|
return dao.db.Create(user).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID 根据ID获取用户
|
||||||
|
func (dao *UserDAO) GetByID(id uint) (*model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
err := dao.db.First(&user, id).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByUsername 根据用户名获取用户
|
||||||
|
func (dao *UserDAO) GetByUsername(username string) (*model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
err := dao.db.Where("username = ?", username).First(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByEmail 根据邮箱获取用户
|
||||||
|
func (dao *UserDAO) GetByEmail(email string) (*model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
err := dao.db.Where("email = ?", email).First(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 更新用户
|
||||||
|
func (dao *UserDAO) Update(user *model.User) error {
|
||||||
|
return dao.db.Save(user).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除用户
|
||||||
|
func (dao *UserDAO) Delete(id uint) error {
|
||||||
|
return dao.db.Delete(&model.User{}, id).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 获取用户列表
|
||||||
|
func (dao *UserDAO) List(offset, limit int) ([]*model.User, error) {
|
||||||
|
var users []*model.User
|
||||||
|
err := dao.db.Offset(offset).Limit(limit).Find(&users).Error
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
|
||||||
42
go.mod
Normal file
42
go.mod
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
module RelationshipManagement-backend
|
||||||
|
|
||||||
|
go 1.25.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gin-gonic/gin v1.10.0
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
gorm.io/driver/mysql v1.5.7
|
||||||
|
gorm.io/gorm v1.25.12
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
|
golang.org/x/net v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
|
golang.org/x/text v0.15.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
103
go.sum
Normal file
103
go.sum
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||||
|
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
217
handler/user_handler.go
Normal file
217
handler/user_handler.go
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"RelationshipManagement-backend/model"
|
||||||
|
"RelationshipManagement-backend/service"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserHandler 用户处理器
|
||||||
|
type UserHandler struct {
|
||||||
|
userService *service.UserService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserHandler 创建用户处理器实例
|
||||||
|
func NewUserHandler(userService *service.UserService) *UserHandler {
|
||||||
|
return &UserHandler{userService: userService}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser 创建用户
|
||||||
|
// @Summary 创建用户
|
||||||
|
// @Description 创建新用户
|
||||||
|
// @Tags 用户
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param user body model.User true "用户信息"
|
||||||
|
// @Success 200 {object} map[string]interface{}
|
||||||
|
// @Failure 400 {object} map[string]interface{}
|
||||||
|
// @Router /api/users [post]
|
||||||
|
func (h *UserHandler) CreateUser(c *gin.Context) {
|
||||||
|
var user model.User
|
||||||
|
if err := c.ShouldBindJSON(&user); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "参数错误",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.userService.CreateUser(&user); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "创建用户失败",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"message": "创建成功",
|
||||||
|
"data": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByID 根据ID获取用户
|
||||||
|
// @Summary 获取用户
|
||||||
|
// @Description 根据ID获取用户信息
|
||||||
|
// @Tags 用户
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "用户ID"
|
||||||
|
// @Success 200 {object} map[string]interface{}
|
||||||
|
// @Failure 400 {object} map[string]interface{}
|
||||||
|
// @Router /api/users/{id} [get]
|
||||||
|
func (h *UserHandler) GetUserByID(c *gin.Context) {
|
||||||
|
idStr := c.Param("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "无效的用户ID",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := h.userService.GetUserByID(uint(id))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{
|
||||||
|
"code": 404,
|
||||||
|
"message": "用户不存在",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"message": "获取成功",
|
||||||
|
"data": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUsers 获取用户列表
|
||||||
|
// @Summary 获取用户列表
|
||||||
|
// @Description 分页获取用户列表
|
||||||
|
// @Tags 用户
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "页码" default(1)
|
||||||
|
// @Param pageSize query int false "每页数量" default(10)
|
||||||
|
// @Success 200 {object} map[string]interface{}
|
||||||
|
// @Router /api/users [get]
|
||||||
|
func (h *UserHandler) ListUsers(c *gin.Context) {
|
||||||
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||||
|
|
||||||
|
users, total, err := h.userService.ListUsers(page, pageSize)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"code": 500,
|
||||||
|
"message": "获取用户列表失败",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"message": "获取成功",
|
||||||
|
"data": gin.H{
|
||||||
|
"list": users,
|
||||||
|
"total": total,
|
||||||
|
"page": page,
|
||||||
|
"pageSize": pageSize,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser 更新用户
|
||||||
|
// @Summary 更新用户
|
||||||
|
// @Description 更新用户信息
|
||||||
|
// @Tags 用户
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "用户ID"
|
||||||
|
// @Param user body model.User true "用户信息"
|
||||||
|
// @Success 200 {object} map[string]interface{}
|
||||||
|
// @Failure 400 {object} map[string]interface{}
|
||||||
|
// @Router /api/users/{id} [put]
|
||||||
|
func (h *UserHandler) UpdateUser(c *gin.Context) {
|
||||||
|
idStr := c.Param("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "无效的用户ID",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user model.User
|
||||||
|
if err := c.ShouldBindJSON(&user); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "参数错误",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.ID = uint(id)
|
||||||
|
if err := h.userService.UpdateUser(&user); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "更新用户失败",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"message": "更新成功",
|
||||||
|
"data": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser 删除用户
|
||||||
|
// @Summary 删除用户
|
||||||
|
// @Description 根据ID删除用户
|
||||||
|
// @Tags 用户
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "用户ID"
|
||||||
|
// @Success 200 {object} map[string]interface{}
|
||||||
|
// @Failure 400 {object} map[string]interface{}
|
||||||
|
// @Router /api/users/{id} [delete]
|
||||||
|
func (h *UserHandler) DeleteUser(c *gin.Context) {
|
||||||
|
idStr := c.Param("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "无效的用户ID",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.userService.DeleteUser(uint(id)); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": 400,
|
||||||
|
"message": "删除用户失败",
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"message": "删除成功",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
35
main.go
Normal file
35
main.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"RelationshipManagement-backend/config"
|
||||||
|
"RelationshipManagement-backend/dao"
|
||||||
|
"RelationshipManagement-backend/router"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 加载配置
|
||||||
|
cfg := config.LoadConfig()
|
||||||
|
|
||||||
|
// 设置Gin模式
|
||||||
|
gin.SetMode(cfg.Server.Mode)
|
||||||
|
|
||||||
|
// 初始化数据库
|
||||||
|
if err := dao.InitDB(cfg); err != nil {
|
||||||
|
log.Fatalf("数据库初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
defer dao.CloseDB()
|
||||||
|
|
||||||
|
// 设置路由
|
||||||
|
r := router.SetupRouter()
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
addr := fmt.Sprintf(":%s", cfg.Server.Port)
|
||||||
|
log.Printf("服务器启动在端口 %s", cfg.Server.Port)
|
||||||
|
if err := r.Run(addr); err != nil {
|
||||||
|
log.Fatalf("服务器启动失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
middleware/cors.go
Normal file
23
middleware/cors.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CORS 跨域中间件
|
||||||
|
func CORS() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
||||||
|
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
c.AbortWithStatus(204)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
37
middleware/logger.go
Normal file
37
middleware/logger.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger 日志中间件
|
||||||
|
func Logger() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
start := time.Now()
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
raw := c.Request.URL.RawQuery
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
latency := time.Since(start)
|
||||||
|
clientIP := c.ClientIP()
|
||||||
|
method := c.Request.Method
|
||||||
|
statusCode := c.Writer.Status()
|
||||||
|
|
||||||
|
if raw != "" {
|
||||||
|
path = path + "?" + raw
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"status_code": statusCode,
|
||||||
|
"latency": latency,
|
||||||
|
"client_ip": clientIP,
|
||||||
|
"method": method,
|
||||||
|
"path": path,
|
||||||
|
}).Info("HTTP Request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
middleware/recovery.go
Normal file
21
middleware/recovery.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recovery 恢复中间件
|
||||||
|
func Recovery() gin.HandlerFunc {
|
||||||
|
return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
||||||
|
logrus.Errorf("Panic recovered: %v", recovered)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"code": 500,
|
||||||
|
"message": "服务器内部错误",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
19
model/user.go
Normal file
19
model/user.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// User 用户模型
|
||||||
|
type User struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
|
Username string `json:"username" gorm:"uniqueIndex;not null"`
|
||||||
|
Email string `json:"email" gorm:"uniqueIndex;not null"`
|
||||||
|
Password string `json:"-" gorm:"not null"` // 密码不返回给前端
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName 指定表名
|
||||||
|
func (User) TableName() string {
|
||||||
|
return "users"
|
||||||
|
}
|
||||||
|
|
||||||
50
router/router.go
Normal file
50
router/router.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"RelationshipManagement-backend/handler"
|
||||||
|
"RelationshipManagement-backend/middleware"
|
||||||
|
"RelationshipManagement-backend/service"
|
||||||
|
"RelationshipManagement-backend/dao"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetupRouter 设置路由
|
||||||
|
func SetupRouter() *gin.Engine {
|
||||||
|
// 初始化依赖
|
||||||
|
userDAO := dao.NewUserDAO(dao.DB)
|
||||||
|
userService := service.NewUserService(userDAO)
|
||||||
|
userHandler := handler.NewUserHandler(userService)
|
||||||
|
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
// 全局中间件
|
||||||
|
r.Use(middleware.CORS())
|
||||||
|
r.Use(middleware.Logger())
|
||||||
|
r.Use(middleware.Recovery())
|
||||||
|
|
||||||
|
// 健康检查
|
||||||
|
r.GET("/health", func(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"status": "ok",
|
||||||
|
"message": "服务运行正常",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// API路由组
|
||||||
|
api := r.Group("/api")
|
||||||
|
{
|
||||||
|
// 用户相关路由
|
||||||
|
users := api.Group("/users")
|
||||||
|
{
|
||||||
|
users.POST("", userHandler.CreateUser)
|
||||||
|
users.GET("", userHandler.ListUsers)
|
||||||
|
users.GET("/:id", userHandler.GetUserByID)
|
||||||
|
users.PUT("/:id", userHandler.UpdateUser)
|
||||||
|
users.DELETE("/:id", userHandler.DeleteUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
69
service/user_service.go
Normal file
69
service/user_service.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"RelationshipManagement-backend/dao"
|
||||||
|
"RelationshipManagement-backend/model"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserService 用户服务
|
||||||
|
type UserService struct {
|
||||||
|
userDAO *dao.UserDAO
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserService 创建用户服务实例
|
||||||
|
func NewUserService(userDAO *dao.UserDAO) *UserService {
|
||||||
|
return &UserService{userDAO: userDAO}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser 创建用户
|
||||||
|
func (s *UserService) CreateUser(user *model.User) error {
|
||||||
|
// 检查用户名是否已存在
|
||||||
|
_, err := s.userDAO.GetByUsername(user.Username)
|
||||||
|
if err == nil {
|
||||||
|
return errors.New("用户名已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查邮箱是否已存在
|
||||||
|
_, err = s.userDAO.GetByEmail(user.Email)
|
||||||
|
if err == nil {
|
||||||
|
return errors.New("邮箱已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建用户
|
||||||
|
return s.userDAO.Create(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByID 根据ID获取用户
|
||||||
|
func (s *UserService) GetUserByID(id uint) (*model.User, error) {
|
||||||
|
return s.userDAO.GetByID(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByUsername 根据用户名获取用户
|
||||||
|
func (s *UserService) GetUserByUsername(username string) (*model.User, error) {
|
||||||
|
return s.userDAO.GetByUsername(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser 更新用户
|
||||||
|
func (s *UserService) UpdateUser(user *model.User) error {
|
||||||
|
return s.userDAO.Update(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser 删除用户
|
||||||
|
func (s *UserService) DeleteUser(id uint) error {
|
||||||
|
return s.userDAO.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUsers 获取用户列表
|
||||||
|
func (s *UserService) ListUsers(page, pageSize int) ([]*model.User, int64, error) {
|
||||||
|
offset := (page - 1) * pageSize
|
||||||
|
users, err := s.userDAO.List(offset, pageSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取总数(这里简化处理,实际应该从DAO获取)
|
||||||
|
total := int64(len(users))
|
||||||
|
return users, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user