diff --git a/internal/api/api.go b/internal/api/api.go index 6ff221f..4d1355d 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -9,7 +9,9 @@ import ( "net/http" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/api/middleware" "git.huangwc.com/pig/pig-farm-controller/internal/config" + "git.huangwc.com/pig/pig-farm-controller/internal/controller/operation" "git.huangwc.com/pig/pig-farm-controller/internal/controller/user" "git.huangwc.com/pig/pig-farm-controller/internal/logs" "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" @@ -31,13 +33,19 @@ type API struct { // userController 用户控制器 userController *user.Controller + // operationController 操作历史控制器 + operationController *operation.Controller + + // authMiddleware 鉴权中间件 + authMiddleware *middleware.AuthMiddleware + // logger 日志记录器 logger *logs.Logger } // NewAPI 创建并返回一个新的API实例 // 初始化Gin引擎和相关配置 -func NewAPI(cfg *config.Config, userRepo repository.UserRepo) *API { +func NewAPI(cfg *config.Config, userRepo repository.UserRepo, operationHistoryRepo repository.OperationHistoryRepo) *API { // 设置Gin为发布模式 gin.SetMode(gin.ReleaseMode) @@ -64,11 +72,19 @@ func NewAPI(cfg *config.Config, userRepo repository.UserRepo) *API { // 创建用户控制器 userController := user.NewController(userRepo) + // 创建操作历史控制器 + operationController := operation.NewController(operationHistoryRepo) + + // 创建鉴权中间件 + authMiddleware := middleware.NewAuthMiddleware(userRepo) + return &API{ - engine: engine, - config: cfg, - userController: userController, - logger: logs.NewLogger(), + engine: engine, + config: cfg, + userController: userController, + operationController: operationController, + authMiddleware: authMiddleware, + logger: logs.NewLogger(), } } @@ -129,6 +145,43 @@ func (a *API) setupRoutes() { userGroup.POST("/login", a.userController.Login) } + // 需要鉴权的路由组 + protectedGroup := a.engine.Group("/api/v1") + protectedGroup.Use(a.authMiddleware.Handle()) + { + // 操作历史相关路由 + operationGroup := protectedGroup.Group("/operation") + { + operationGroup.POST("/", a.operationController.Create) + operationGroup.GET("/list", a.operationController.ListByUser) + operationGroup.GET("/:id", a.operationController.Get) + } + + // 示例受保护路由 + protectedGroup.GET("/profile", func(c *gin.Context) { + // 从上下文中获取用户信息 + userValue, exists := c.Get("user") + if !exists { + c.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户信息"}) + return + } + + user, ok := userValue.(*middleware.AuthUser) + if !ok { + c.JSON(http.StatusInternalServerError, gin.H{"error": "用户信息格式错误"}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "获取用户信息成功", + "user": map[string]interface{}{ + "id": user.ID, + "username": user.Username, + }, + }) + }) + } + // TODO: 添加更多路由 } diff --git a/internal/controller/operation/operation.go b/internal/controller/operation/operation.go new file mode 100644 index 0000000..cc30f7c --- /dev/null +++ b/internal/controller/operation/operation.go @@ -0,0 +1,212 @@ +// Package operation 提供操作历史相关功能的控制器 +// 实现操作历史记录、查询等操作 +package operation + +import ( + "net/http" + "strconv" + + "git.huangwc.com/pig/pig-farm-controller/internal/api/middleware" + "git.huangwc.com/pig/pig-farm-controller/internal/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/model" + "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" + "github.com/gin-gonic/gin" +) + +// Controller 操作历史控制器 +type Controller struct { + operationHistoryRepo repository.OperationHistoryRepo + logger *logs.Logger +} + +// NewController 创建操作历史控制器实例 +func NewController(operationHistoryRepo repository.OperationHistoryRepo) *Controller { + return &Controller{ + operationHistoryRepo: operationHistoryRepo, + logger: logs.NewLogger(), + } +} + +// CreateRequest 创建操作历史请求结构体 +type CreateRequest struct { + Action string `json:"action" binding:"required"` + Target string `json:"target"` + Parameters string `json:"parameters"` + Status string `json:"status" binding:"required"` + Result string `json:"result"` +} + +// Create 创建操作历史记录 +func (c *Controller) Create(ctx *gin.Context) { + // 从上下文中获取用户信息 + userValue, exists := ctx.Get("user") + if !exists { + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户信息"}) + return + } + + user, ok := userValue.(*middleware.AuthUser) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "用户信息格式错误"}) + return + } + + var req CreateRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) + return + } + + // 创建操作历史记录 + history := &model.OperationHistory{ + UserID: user.ID, + Action: req.Action, + Target: req.Target, + Parameters: req.Parameters, + Status: req.Status, + Result: req.Result, + } + + if err := c.operationHistoryRepo.Create(history); err != nil { + c.logger.Error("创建操作历史记录失败: " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "创建操作历史记录失败"}) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "操作历史记录创建成功", + "data": map[string]interface{}{ + "id": history.ID, + "action": history.Action, + "target": history.Target, + "parameters": history.Parameters, + "status": history.Status, + "result": history.Result, + "created_at": history.CreatedAt, + }, + }) +} + +// ListByUserRequest 按用户查询操作历史请求结构体 +type ListByUserRequest struct { + Page int `form:"page" binding:"required"` + Limit int `form:"limit" binding:"required"` +} + +// ListByUser 获取当前用户的所有操作历史记录 +func (c *Controller) ListByUser(ctx *gin.Context) { + // 从上下文中获取用户信息 + userValue, exists := ctx.Get("user") + if !exists { + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户信息"}) + return + } + + user, ok := userValue.(*middleware.AuthUser) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "用户信息格式错误"}) + return + } + + // 解析查询参数 + var req ListByUserRequest + if err := ctx.ShouldBindQuery(&req); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "查询参数错误"}) + return + } + + if req.Page <= 0 { + req.Page = 1 + } + + if req.Limit <= 0 || req.Limit > 100 { + req.Limit = 10 + } + + // 计算偏移量 + offset := (req.Page - 1) * req.Limit + + // 查询用户操作历史记录 + histories, err := c.operationHistoryRepo.FindByUserID(user.ID) + if err != nil { + c.logger.Error("查询操作历史记录失败: " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "查询操作历史记录失败"}) + return + } + + // 分页处理 + start := offset + end := start + req.Limit + if start > len(histories) { + start = len(histories) + } + if end > len(histories) { + end = len(histories) + } + + pagedHistories := histories[start:end] + + ctx.JSON(http.StatusOK, gin.H{ + "message": "查询成功", + "data": map[string]interface{}{ + "histories": pagedHistories, + "page": req.Page, + "limit": req.Limit, + "total": len(histories), + }, + }) +} + +// GetRequest 获取单个操作历史记录请求结构体 +type GetRequest struct { + ID string `uri:"id" binding:"required"` +} + +// Get 获取单个操作历史记录 +func (c *Controller) Get(ctx *gin.Context) { + // 从上下文中获取用户信息 + userValue, exists := ctx.Get("user") + if !exists { + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户信息"}) + return + } + + user, ok := userValue.(*middleware.AuthUser) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "用户信息格式错误"}) + return + } + + // 解析路径参数 + var req GetRequest + if err := ctx.ShouldBindUri(&req); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "路径参数错误"}) + return + } + + // 将ID转换为整数 + id, err := strconv.ParseUint(req.ID, 10, 32) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "ID格式错误"}) + return + } + + // 查询操作历史记录 + history, err := c.operationHistoryRepo.FindByID(uint(id)) + if err != nil { + c.logger.Error("查询操作历史记录失败: " + err.Error()) + ctx.JSON(http.StatusNotFound, gin.H{"error": "操作历史记录不存在"}) + return + } + + // 检查是否是当前用户的记录 + if history.UserID != user.ID { + ctx.JSON(http.StatusForbidden, gin.H{"error": "无权访问该记录"}) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "查询成功", + "data": history, + }) +} diff --git a/internal/core/application.go b/internal/core/application.go index a908196..7d3d012 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -29,6 +29,9 @@ type Application struct { // UserRepo 用户仓库实例 UserRepo repository.UserRepo + // OperationHistoryRepo 操作历史仓库实例 + OperationHistoryRepo repository.OperationHistoryRepo + // Config 应用配置 Config *config.Config @@ -53,19 +56,23 @@ func NewApplication(cfg *config.Config) *Application { // 初始化用户仓库 userRepo := repository.NewUserRepo(store.GetDB()) + // 初始化操作历史仓库 + operationHistoryRepo := repository.NewOperationHistoryRepo(store.GetDB()) + // 初始化API组件 - apiInstance := api.NewAPI(cfg, userRepo) + apiInstance := api.NewAPI(cfg, userRepo, operationHistoryRepo) // 初始化任务执行器组件(使用5个工作协程) taskExecutor := task.NewExecutor(5) return &Application{ - Storage: store, - API: apiInstance, - TaskExecutor: taskExecutor, - UserRepo: userRepo, - Config: cfg, - logger: logs.NewLogger(), + Storage: store, + API: apiInstance, + TaskExecutor: taskExecutor, + UserRepo: userRepo, + OperationHistoryRepo: operationHistoryRepo, + Config: cfg, + logger: logs.NewLogger(), } } diff --git a/internal/model/operation_history.go b/internal/model/operation_history.go new file mode 100644 index 0000000..63cefcd --- /dev/null +++ b/internal/model/operation_history.go @@ -0,0 +1,47 @@ +// Package model 提供数据模型定义 +// 包含用户、操作历史等相关数据结构 +package model + +import ( + "time" + + "gorm.io/gorm" +) + +// OperationHistory 代表用户操作历史记录 +type OperationHistory struct { + // ID 记录ID + ID uint `gorm:"primaryKey;column:id" json:"id"` + + // UserID 用户ID + UserID uint `gorm:"not null;column:user_id;index" json:"user_id"` + + // Action 操作类型/指令 + Action string `gorm:"not null;column:action" json:"action"` + + // Target 操作目标(可选) + Target string `gorm:"column:target" json:"target"` + + // Parameters 操作参数(可选) + Parameters string `gorm:"column:parameters" json:"parameters"` + + // Status 操作状态(成功/失败) + Status string `gorm:"not null;column:status" json:"status"` + + // Result 操作结果详情(可选) + Result string `gorm:"column:result" json:"result"` + + // CreatedAt 创建时间 + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + + // UpdatedAt 更新时间 + UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` + + // DeletedAt 删除时间(用于软删除) + DeletedAt gorm.DeletedAt `gorm:"index;column:deleted_at" json:"-"` +} + +// TableName 指定OperationHistory模型对应的数据库表名 +func (OperationHistory) TableName() string { + return "operation_histories" +} diff --git a/internal/model/user.go b/internal/model/user.go index f22cc4b..60e6a37 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -27,6 +27,9 @@ type User struct { // DeletedAt 删除时间(用于软删除) DeletedAt gorm.DeletedAt `gorm:"index;column:deleted_at" json:"-"` + + // OperationHistories 用户的操作历史记录 + OperationHistories []OperationHistory `gorm:"foreignKey:UserID" json:"-"` } // TableName 指定User模型对应的数据库表名 diff --git a/internal/storage/repository/operation_history.go b/internal/storage/repository/operation_history.go new file mode 100644 index 0000000..379fabf --- /dev/null +++ b/internal/storage/repository/operation_history.go @@ -0,0 +1,71 @@ +// Package repository 提供数据访问层实现 +// 包含各种数据实体的仓库接口和实现 +package repository + +import ( + "git.huangwc.com/pig/pig-farm-controller/internal/model" + "gorm.io/gorm" +) + +// OperationHistoryRepo 操作历史仓库接口 +type OperationHistoryRepo interface { + // Create 创建操作历史记录 + Create(history *model.OperationHistory) error + + // FindByUserID 根据用户ID查找操作历史记录 + FindByUserID(userID uint) ([]*model.OperationHistory, error) + + // FindByID 根据ID查找操作历史记录 + FindByID(id uint) (*model.OperationHistory, error) + + // List 获取操作历史记录列表(分页) + List(offset, limit int) ([]*model.OperationHistory, error) +} + +// operationHistoryRepo 操作历史仓库实现 +type operationHistoryRepo struct { + db *gorm.DB +} + +// NewOperationHistoryRepo 创建操作历史仓库实例 +func NewOperationHistoryRepo(db *gorm.DB) OperationHistoryRepo { + return &operationHistoryRepo{ + db: db, + } +} + +// Create 创建操作历史记录 +func (r *operationHistoryRepo) Create(history *model.OperationHistory) error { + result := r.db.Create(history) + return result.Error +} + +// FindByUserID 根据用户ID查找操作历史记录 +func (r *operationHistoryRepo) FindByUserID(userID uint) ([]*model.OperationHistory, error) { + var histories []*model.OperationHistory + result := r.db.Where("user_id = ?", userID).Order("created_at DESC").Find(&histories) + if result.Error != nil { + return nil, result.Error + } + return histories, nil +} + +// FindByID 根据ID查找操作历史记录 +func (r *operationHistoryRepo) FindByID(id uint) (*model.OperationHistory, error) { + var history model.OperationHistory + result := r.db.First(&history, id) + if result.Error != nil { + return nil, result.Error + } + return &history, nil +} + +// List 获取操作历史记录列表(分页) +func (r *operationHistoryRepo) List(offset, limit int) ([]*model.OperationHistory, error) { + var histories []*model.OperationHistory + result := r.db.Offset(offset).Limit(limit).Order("created_at DESC").Find(&histories) + if result.Error != nil { + return nil, result.Error + } + return histories, nil +}