package plane_v1 import ( "fmt" "go.uber.org/zap" ) // ==================== Cycle API ==================== // ListCycles 列出迭代 func ListCycles() ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/", planeWorkspaceSlug, planeProjectId) resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] ListCycles 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] ListCycles 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("ListCycles 失败: %s", resp.Status) } return resp.Body, nil } // CreateCycle 创建迭代 func CreateCycle(data map[string]interface{}) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/", planeWorkspaceSlug, planeProjectId) resp, err := planeHttpPost(path, data) if err != nil { zap.L().Error("[Plane] CreateCycle 请求失败", zap.Error(err)) return nil, err } if resp.StatusCode != 201 { zap.L().Error("[Plane] CreateCycle 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body))) return nil, fmt.Errorf("CreateCycle 失败: %s", resp.Status) } return resp.Body, nil } // GetCycle 获取迭代详情 func GetCycle(cycleID string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/", planeWorkspaceSlug, planeProjectId, cycleID) resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] GetCycle 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] GetCycle 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("GetCycle 失败: %s", resp.Status) } return resp.Body, nil } // UpdateCycle 更新迭代 func UpdateCycle(cycleID string, data map[string]interface{}) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/", planeWorkspaceSlug, planeProjectId, cycleID) resp, err := planeHttpPatch(path, data) if err != nil { zap.L().Error("[Plane] UpdateCycle 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] UpdateCycle 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body))) return nil, fmt.Errorf("UpdateCycle 失败: %s", resp.Status) } return resp.Body, nil } // DeleteCycle 删除迭代 func DeleteCycle(cycleID string) error { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/", planeWorkspaceSlug, planeProjectId, cycleID) resp, err := planeHttpDelete(path) if err != nil { zap.L().Error("[Plane] DeleteCycle 请求失败", zap.Error(err)) return err } if resp.StatusCode != 204 { zap.L().Error("[Plane] DeleteCycle 返回错误", zap.String("status", resp.Status)) return fmt.Errorf("DeleteCycle 失败: %s", resp.Status) } return nil } // ListCycleIssues 列出迭代中的工作项 func ListCycleIssues(cycleID string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", planeWorkspaceSlug, planeProjectId, cycleID) resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] ListCycleIssues 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] ListCycleIssues 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("ListCycleIssues 失败: %s", resp.Status) } return resp.Body, nil } // AddIssuesToCycle 添加工作项到迭代 func AddIssuesToCycle(cycleID string, issueIDs []string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", planeWorkspaceSlug, planeProjectId, cycleID) payload := map[string]interface{}{"issues": issueIDs} resp, err := planeHttpPost(path, payload) if err != nil { zap.L().Error("[Plane] AddIssuesToCycle 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] AddIssuesToCycle 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body))) return nil, fmt.Errorf("AddIssuesToCycle 失败: %s", resp.Status) } return resp.Body, nil } // RemoveIssueFromCycle 从迭代移除工作项 func RemoveIssueFromCycle(cycleID, issueID string) error { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/cycle-issues/%s/", planeWorkspaceSlug, planeProjectId, cycleID, issueID) resp, err := planeHttpDelete(path) if err != nil { zap.L().Error("[Plane] RemoveIssueFromCycle 请求失败", zap.Error(err)) return err } if resp.StatusCode != 204 { zap.L().Error("[Plane] RemoveIssueFromCycle 返回错误", zap.String("status", resp.Status)) return fmt.Errorf("RemoveIssueFromCycle 失败: %s", resp.Status) } return nil } // TransferCycleIssues 转移未完成工作项到另一迭代 func TransferCycleIssues(cycleID, newCycleID string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/transfer-issues/", planeWorkspaceSlug, planeProjectId, cycleID) payload := map[string]interface{}{"new_cycle_id": newCycleID} resp, err := planeHttpPost(path, payload) if err != nil { zap.L().Error("[Plane] TransferCycleIssues 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] TransferCycleIssues 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body))) return nil, fmt.Errorf("TransferCycleIssues 失败: %s", resp.Status) } return resp.Body, nil } // ArchiveCycle 归档迭代 func ArchiveCycle(cycleID string) error { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/archive/", planeWorkspaceSlug, planeProjectId, cycleID) resp, err := planeHttpPost(path, map[string]interface{}{}) if err != nil { zap.L().Error("[Plane] ArchiveCycle 请求失败", zap.Error(err)) return err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] ArchiveCycle 返回错误", zap.String("status", resp.Status)) return fmt.Errorf("ArchiveCycle 失败: %s", resp.Status) } return nil }