ListPlanExecutionLogs
This commit is contained in:
		| @@ -118,3 +118,51 @@ func (c *Controller) ListDeviceCommandLogs(ctx *gin.Context) { | |||||||
| 	c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total) | 	c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total) | ||||||
| 	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备命令日志成功", resp, actionType, "获取设备命令日志成功", req) | 	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备命令日志成功", resp, actionType, "获取设备命令日志成功", req) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListPlanExecutionLogs godoc | ||||||
|  | // @Summary      获取计划执行日志列表 | ||||||
|  | // @Description  根据提供的过滤条件,分页获取计划执行日志 | ||||||
|  | // @Tags         数据监控 | ||||||
|  | // @Security     BearerAuth | ||||||
|  | // @Produce      json | ||||||
|  | // @Param        query query dto.ListPlanExecutionLogRequest true "查询参数" | ||||||
|  | // @Success      200 {object} controller.Response{data=dto.ListPlanExecutionLogResponse} | ||||||
|  | // @Router       /api/v1/monitor/plan-execution-logs [get] | ||||||
|  | func (c *Controller) ListPlanExecutionLogs(ctx *gin.Context) { | ||||||
|  | 	const actionType = "获取计划执行日志列表" | ||||||
|  |  | ||||||
|  | 	var req dto.ListPlanExecutionLogRequest | ||||||
|  | 	if err := ctx.ShouldBindQuery(&req); err != nil { | ||||||
|  | 		c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err) | ||||||
|  | 		controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	opts := repository.PlanExecutionLogListOptions{ | ||||||
|  | 		PlanID:    req.PlanID, | ||||||
|  | 		OrderBy:   req.OrderBy, | ||||||
|  | 		StartTime: req.StartTime, | ||||||
|  | 		EndTime:   req.EndTime, | ||||||
|  | 	} | ||||||
|  | 	if req.Status != nil { | ||||||
|  | 		status := models.ExecutionStatus(*req.Status) | ||||||
|  | 		opts.Status = &status | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	data, total, err := c.monitorService.ListPlanExecutionLogs(opts, req.Page, req.PageSize) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if errors.Is(err, repository.ErrInvalidPagination) { | ||||||
|  | 			c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err) | ||||||
|  | 			controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err) | ||||||
|  | 		controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划执行日志失败: "+err.Error(), actionType, "服务层查询失败", req) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.NewListPlanExecutionLogResponse(data, total, req.Page, req.PageSize) | ||||||
|  | 	c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total) | ||||||
|  | 	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划执行日志成功", resp, actionType, "获取计划执行日志成功", req) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -117,3 +117,60 @@ func NewListDeviceCommandLogResponse(data []models.DeviceCommandLog, total int64 | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // --- PlanExecutionLog --- | ||||||
|  |  | ||||||
|  | // ListPlanExecutionLogRequest 定义了获取计划执行日志列表的请求参数 | ||||||
|  | type ListPlanExecutionLogRequest struct { | ||||||
|  | 	Page      int        `form:"page,default=1"` | ||||||
|  | 	PageSize  int        `form:"pageSize,default=10"` | ||||||
|  | 	PlanID    *uint      `form:"plan_id"` | ||||||
|  | 	Status    *string    `form:"status"` | ||||||
|  | 	StartTime *time.Time `form:"start_time" time_format:"rfc3339"` | ||||||
|  | 	EndTime   *time.Time `form:"end_time" time_format:"rfc3339"` | ||||||
|  | 	OrderBy   string     `form:"order_by"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PlanExecutionLogDTO 是用于API响应的计划执行日志结构 | ||||||
|  | type PlanExecutionLogDTO struct { | ||||||
|  | 	ID        uint                   `json:"id"` | ||||||
|  | 	CreatedAt time.Time              `json:"created_at"` | ||||||
|  | 	UpdatedAt time.Time              `json:"updated_at"` | ||||||
|  | 	PlanID    uint                   `json:"plan_id"` | ||||||
|  | 	Status    models.ExecutionStatus `json:"status"` | ||||||
|  | 	StartedAt time.Time              `json:"started_at"` | ||||||
|  | 	EndedAt   time.Time              `json:"ended_at"` | ||||||
|  | 	Error     string                 `json:"error"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListPlanExecutionLogResponse 是获取计划执行日志列表的响应结构 | ||||||
|  | type ListPlanExecutionLogResponse struct { | ||||||
|  | 	List       []PlanExecutionLogDTO `json:"list"` | ||||||
|  | 	Pagination PaginationDTO         `json:"pagination"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewListPlanExecutionLogResponse 从模型数据创建列表响应 DTO | ||||||
|  | func NewListPlanExecutionLogResponse(data []models.PlanExecutionLog, total int64, page, pageSize int) *ListPlanExecutionLogResponse { | ||||||
|  | 	dtos := make([]PlanExecutionLogDTO, len(data)) | ||||||
|  | 	for i, item := range data { | ||||||
|  | 		dtos[i] = PlanExecutionLogDTO{ | ||||||
|  | 			ID:        item.ID, | ||||||
|  | 			CreatedAt: item.CreatedAt, | ||||||
|  | 			UpdatedAt: item.UpdatedAt, | ||||||
|  | 			PlanID:    item.PlanID, | ||||||
|  | 			Status:    item.Status, | ||||||
|  | 			StartedAt: item.StartedAt, | ||||||
|  | 			EndedAt:   item.EndedAt, | ||||||
|  | 			Error:     item.Error, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &ListPlanExecutionLogResponse{ | ||||||
|  | 		List: dtos, | ||||||
|  | 		Pagination: PaginationDTO{ | ||||||
|  | 			Total:    total, | ||||||
|  | 			Page:     page, | ||||||
|  | 			PageSize: pageSize, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| type MonitorService struct { | type MonitorService struct { | ||||||
| 	sensorDataRepo       repository.SensorDataRepository | 	sensorDataRepo       repository.SensorDataRepository | ||||||
| 	deviceCommandLogRepo repository.DeviceCommandLogRepository | 	deviceCommandLogRepo repository.DeviceCommandLogRepository | ||||||
|  | 	executionLogRepo     repository.ExecutionLogRepository | ||||||
| 	// 在这里可以添加其他超表模型的仓库依赖 | 	// 在这里可以添加其他超表模型的仓库依赖 | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -16,10 +17,12 @@ type MonitorService struct { | |||||||
| func NewMonitorService( | func NewMonitorService( | ||||||
| 	sensorDataRepo repository.SensorDataRepository, | 	sensorDataRepo repository.SensorDataRepository, | ||||||
| 	deviceCommandLogRepo repository.DeviceCommandLogRepository, | 	deviceCommandLogRepo repository.DeviceCommandLogRepository, | ||||||
|  | 	executionLogRepo repository.ExecutionLogRepository, | ||||||
| ) *MonitorService { | ) *MonitorService { | ||||||
| 	return &MonitorService{ | 	return &MonitorService{ | ||||||
| 		sensorDataRepo:       sensorDataRepo, | 		sensorDataRepo:       sensorDataRepo, | ||||||
| 		deviceCommandLogRepo: deviceCommandLogRepo, | 		deviceCommandLogRepo: deviceCommandLogRepo, | ||||||
|  | 		executionLogRepo:     executionLogRepo, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -32,3 +35,8 @@ func (s *MonitorService) ListSensorData(opts repository.SensorDataListOptions, p | |||||||
| func (s *MonitorService) ListDeviceCommandLogs(opts repository.DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) { | func (s *MonitorService) ListDeviceCommandLogs(opts repository.DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) { | ||||||
| 	return s.deviceCommandLogRepo.List(opts, page, pageSize) | 	return s.deviceCommandLogRepo.List(opts, page, pageSize) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListPlanExecutionLogs 负责处理查询计划执行日志列表的业务逻辑 | ||||||
|  | func (s *MonitorService) ListPlanExecutionLogs(opts repository.PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) { | ||||||
|  | 	return s.executionLogRepo.ListPlanExecutionLogs(opts, page, pageSize) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -8,9 +8,18 @@ import ( | |||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // PlanExecutionLogListOptions 定义了查询计划执行日志时的可选参数 | ||||||
|  | type PlanExecutionLogListOptions struct { | ||||||
|  | 	PlanID    *uint | ||||||
|  | 	Status    *models.ExecutionStatus | ||||||
|  | 	StartTime *time.Time // 基于 created_at 字段 | ||||||
|  | 	EndTime   *time.Time // 基于 created_at 字段 | ||||||
|  | 	OrderBy   string     // 例如 "created_at asc" | ||||||
|  | } | ||||||
|  |  | ||||||
| // ExecutionLogRepository 定义了与执行日志交互的接口。 | // ExecutionLogRepository 定义了与执行日志交互的接口。 | ||||||
| // 这为服务层提供了一个清晰的契约,并允许在测试中轻松地进行模拟。 |  | ||||||
| type ExecutionLogRepository interface { | type ExecutionLogRepository interface { | ||||||
|  | 	// --- Existing methods --- | ||||||
| 	UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error | 	UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error | ||||||
| 	UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error | 	UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error | ||||||
| 	CreateTaskExecutionLog(log *models.TaskExecutionLog) error | 	CreateTaskExecutionLog(log *models.TaskExecutionLog) error | ||||||
| @@ -48,6 +57,9 @@ type ExecutionLogRepository interface { | |||||||
|  |  | ||||||
| 	// CancelIncompleteTasksByPlanLogID 取消一个计划执行中的所有未完成任务 | 	// CancelIncompleteTasksByPlanLogID 取消一个计划执行中的所有未完成任务 | ||||||
| 	CancelIncompleteTasksByPlanLogID(planLogID uint, reason string) error | 	CancelIncompleteTasksByPlanLogID(planLogID uint, reason string) error | ||||||
|  |  | ||||||
|  | 	// --- New method --- | ||||||
|  | 	ListPlanExecutionLogs(opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // gormExecutionLogRepository 是使用 GORM 的具体实现。 | // gormExecutionLogRepository 是使用 GORM 的具体实现。 | ||||||
| @@ -56,18 +68,57 @@ type gormExecutionLogRepository struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // NewGormExecutionLogRepository 创建一个新的执行日志仓库。 | // NewGormExecutionLogRepository 创建一个新的执行日志仓库。 | ||||||
| // 它接收一个 GORM DB 实例作为依赖。 |  | ||||||
| func NewGormExecutionLogRepository(db *gorm.DB) ExecutionLogRepository { | func NewGormExecutionLogRepository(db *gorm.DB) ExecutionLogRepository { | ||||||
| 	return &gormExecutionLogRepository{db: db} | 	return &gormExecutionLogRepository{db: db} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListPlanExecutionLogs 实现了分页和过滤查询计划执行日志的功能 | ||||||
|  | func (r *gormExecutionLogRepository) ListPlanExecutionLogs(opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) { | ||||||
|  | 	if page <= 0 || pageSize <= 0 { | ||||||
|  | 		return nil, 0, ErrInvalidPagination | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var results []models.PlanExecutionLog | ||||||
|  | 	var total int64 | ||||||
|  |  | ||||||
|  | 	query := r.db.Model(&models.PlanExecutionLog{}) | ||||||
|  |  | ||||||
|  | 	if opts.PlanID != nil { | ||||||
|  | 		query = query.Where("plan_id = ?", *opts.PlanID) | ||||||
|  | 	} | ||||||
|  | 	if opts.Status != nil { | ||||||
|  | 		query = query.Where("status = ?", *opts.Status) | ||||||
|  | 	} | ||||||
|  | 	if opts.StartTime != nil { | ||||||
|  | 		query = query.Where("created_at >= ?", *opts.StartTime) | ||||||
|  | 	} | ||||||
|  | 	if opts.EndTime != nil { | ||||||
|  | 		query = query.Where("created_at <= ?", *opts.EndTime) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := query.Count(&total).Error; err != nil { | ||||||
|  | 		return nil, 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	orderBy := "created_at DESC" | ||||||
|  | 	if opts.OrderBy != "" { | ||||||
|  | 		orderBy = opts.OrderBy | ||||||
|  | 	} | ||||||
|  | 	query = query.Order(orderBy) | ||||||
|  |  | ||||||
|  | 	offset := (page - 1) * pageSize | ||||||
|  | 	err := query.Limit(pageSize).Offset(offset).Find(&results).Error | ||||||
|  |  | ||||||
|  | 	return results, total, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // --- Existing method implementations --- | ||||||
|  |  | ||||||
| func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error { | func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error { | ||||||
| 	if len(logIDs) == 0 { | 	if len(logIDs) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	return r.db.Model(&models.TaskExecutionLog{}). | 	return r.db.Model(&models.TaskExecutionLog{}).Where("id IN ?", logIDs).Update("status", status).Error | ||||||
| 		Where("id IN ?", logIDs). |  | ||||||
| 		Update("status", status).Error |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error { | func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user