diff --git a/internal/app/controller/plan/plan_controller.go b/internal/app/controller/plan/plan_controller.go index abc76f7..4f9782f 100644 --- a/internal/app/controller/plan/plan_controller.go +++ b/internal/app/controller/plan/plan_controller.go @@ -186,9 +186,26 @@ func (c *Controller) GetPlan(ctx *gin.Context) { // @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体(例如400, 500)" // @Router /plans [get] func (c *Controller) ListPlans(ctx *gin.Context) { - // 占位符:此处应调用服务层或仓库层来获取计划列表 - c.logger.Infof("收到获取计划列表请求 (占位符)") - controller.SendResponse(ctx, controller.CodeSuccess, "获取计划列表接口占位符", ListPlansResponse{Plans: []PlanResponse{}, Total: 0}) + // 1. 调用仓库层获取所有计划 + plans, err := c.planRepo.ListBasicPlans() + if err != nil { + c.logger.Errorf("获取计划列表失败: %v", err) + controller.SendErrorResponse(ctx, controller.CodeInternalError, "获取计划列表时发生内部错误") + return + } + + // 2. 将模型转换为响应 DTO + planResponses := make([]PlanResponse, 0, len(plans)) + for _, p := range plans { + planResponses = append(planResponses, *PlanToResponse(&p)) + } + + // 3. 构造并发送成功响应 + resp := ListPlansResponse{ + Plans: planResponses, + Total: len(planResponses), + } + controller.SendResponse(ctx, controller.CodeSuccess, "获取计划列表成功", resp) } // UpdatePlan godoc diff --git a/internal/app/controller/plan/plan_controller_test.go b/internal/app/controller/plan/plan_controller_test.go index eda6e13..b20a7af 100644 --- a/internal/app/controller/plan/plan_controller_test.go +++ b/internal/app/controller/plan/plan_controller_test.go @@ -18,15 +18,14 @@ import ( ) // MockPlanRepository 是 repository.PlanRepository 的一个模拟实现,用于测试 -// [保持原样,不做任何修改] type MockPlanRepository struct { - CreatePlanFunc func(plan *models.Plan) error - GetPlanByIDFunc func(id uint) (*models.Plan, error) - // ... 可以根据需要模拟其他接口方法 + CreatePlanFunc func(plan *models.Plan) error + GetPlanByIDFunc func(id uint) (*models.Plan, error) + ListBasicPlansFunc func() ([]models.Plan, error) } func (m *MockPlanRepository) ListBasicPlans() ([]models.Plan, error) { - panic("implement me") + return m.ListBasicPlansFunc() } func (m *MockPlanRepository) GetBasicPlanByID(id uint) (*models.Plan, error) { @@ -60,6 +59,7 @@ func setupTestRouter(repo repository.PlanRepository) *gin.Engine { planController := NewController(logger, repo) router.POST("/plans", planController.CreatePlan) router.GET("/plans/:id", planController.GetPlan) + router.GET("/plans", planController.ListPlans) return router } @@ -257,3 +257,105 @@ func TestController_GetPlan(t *testing.T) { assert.Equal(t, "获取计划详情时发生内部错误", resp.Message) }) } + +// TestController_ListPlans tests the ListPlans method +func TestController_ListPlans(t *testing.T) { + t.Run("成功-获取计划列表", func(t *testing.T) { + // Arrange + mockPlans := []models.Plan{ + {Model: gorm.Model{ID: 1}, Name: "Plan 1", ContentType: models.PlanContentTypeTasks}, + {Model: gorm.Model{ID: 2}, Name: "Plan 2", ContentType: models.PlanContentTypeTasks}, + } + mockRepo := &MockPlanRepository{ + ListBasicPlansFunc: func() ([]models.Plan, error) { + return mockPlans, nil + }, + } + router := setupTestRouter(mockRepo) + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/plans", nil) + + // Act + router.ServeHTTP(w, req) + + // Assert + assert.Equal(t, http.StatusOK, w.Code) + + var resp controller.Response + err := json.Unmarshal(w.Body.Bytes(), &resp) + assert.NoError(t, err) + + assert.Equal(t, controller.CodeSuccess, resp.Code) + assert.Equal(t, "获取计划列表成功", resp.Message) + + dataBytes, err := json.Marshal(resp.Data) + assert.NoError(t, err) + var listResp ListPlansResponse + err = json.Unmarshal(dataBytes, &listResp) + assert.NoError(t, err) + + assert.Equal(t, 2, listResp.Total) + assert.Len(t, listResp.Plans, 2) + assert.Equal(t, uint(1), listResp.Plans[0].ID) + assert.Equal(t, "Plan 1", listResp.Plans[0].Name) + }) + + t.Run("成功-返回空列表", func(t *testing.T) { + // Arrange + mockRepo := &MockPlanRepository{ + ListBasicPlansFunc: func() ([]models.Plan, error) { + return []models.Plan{}, nil + }, + } + router := setupTestRouter(mockRepo) + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/plans", nil) + + // Act + router.ServeHTTP(w, req) + + // Assert + assert.Equal(t, http.StatusOK, w.Code) + + var resp controller.Response + err := json.Unmarshal(w.Body.Bytes(), &resp) + assert.NoError(t, err) + + assert.Equal(t, controller.CodeSuccess, resp.Code) + + dataBytes, err := json.Marshal(resp.Data) + assert.NoError(t, err) + var listResp ListPlansResponse + err = json.Unmarshal(dataBytes, &listResp) + assert.NoError(t, err) + + assert.Equal(t, 0, listResp.Total) + assert.Len(t, listResp.Plans, 0) + }) + + t.Run("失败-仓库层返回错误", func(t *testing.T) { + // Arrange + dbErr := errors.New("db error") + mockRepo := &MockPlanRepository{ + ListBasicPlansFunc: func() ([]models.Plan, error) { + return nil, dbErr + }, + } + router := setupTestRouter(mockRepo) + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/plans", nil) + + // Act + router.ServeHTTP(w, req) + + // Assert + assert.Equal(t, http.StatusOK, w.Code) + + var resp controller.Response + err := json.Unmarshal(w.Body.Bytes(), &resp) + assert.NoError(t, err) + + assert.Equal(t, controller.CodeInternalError, resp.Code) + assert.Equal(t, "获取计划列表时发生内部错误", resp.Message) + }) +}