plane_cycle.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package plane_v1
  2. import (
  3. "fmt"
  4. "go.uber.org/zap"
  5. )
  6. // ==================== Cycle API ====================
  7. // ListCycles 列出迭代
  8. func ListCycles() ([]byte, error) {
  9. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/", planeWorkspaceSlug, planeProjectId)
  10. resp, err := planeHttpGet(path)
  11. if err != nil {
  12. zap.L().Error("[Plane] ListCycles 请求失败", zap.Error(err))
  13. return nil, err
  14. }
  15. if !isSuccess(resp.StatusCode) {
  16. zap.L().Error("[Plane] ListCycles 返回错误", zap.String("status", resp.Status))
  17. return nil, fmt.Errorf("ListCycles 失败: %s", resp.Status)
  18. }
  19. return resp.Body, nil
  20. }
  21. // CreateCycle 创建迭代
  22. func CreateCycle(data map[string]interface{}) ([]byte, error) {
  23. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/", planeWorkspaceSlug, planeProjectId)
  24. resp, err := planeHttpPost(path, data)
  25. if err != nil {
  26. zap.L().Error("[Plane] CreateCycle 请求失败", zap.Error(err))
  27. return nil, err
  28. }
  29. if resp.StatusCode != 201 {
  30. zap.L().Error("[Plane] CreateCycle 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body)))
  31. return nil, fmt.Errorf("CreateCycle 失败: %s", resp.Status)
  32. }
  33. return resp.Body, nil
  34. }
  35. // GetCycle 获取迭代详情
  36. func GetCycle(cycleID string) ([]byte, error) {
  37. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/", planeWorkspaceSlug, planeProjectId, cycleID)
  38. resp, err := planeHttpGet(path)
  39. if err != nil {
  40. zap.L().Error("[Plane] GetCycle 请求失败", zap.Error(err))
  41. return nil, err
  42. }
  43. if !isSuccess(resp.StatusCode) {
  44. zap.L().Error("[Plane] GetCycle 返回错误", zap.String("status", resp.Status))
  45. return nil, fmt.Errorf("GetCycle 失败: %s", resp.Status)
  46. }
  47. return resp.Body, nil
  48. }
  49. // UpdateCycle 更新迭代
  50. func UpdateCycle(cycleID string, data map[string]interface{}) ([]byte, error) {
  51. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/", planeWorkspaceSlug, planeProjectId, cycleID)
  52. resp, err := planeHttpPatch(path, data)
  53. if err != nil {
  54. zap.L().Error("[Plane] UpdateCycle 请求失败", zap.Error(err))
  55. return nil, err
  56. }
  57. if !isSuccess(resp.StatusCode) {
  58. zap.L().Error("[Plane] UpdateCycle 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body)))
  59. return nil, fmt.Errorf("UpdateCycle 失败: %s", resp.Status)
  60. }
  61. return resp.Body, nil
  62. }
  63. // DeleteCycle 删除迭代
  64. func DeleteCycle(cycleID string) error {
  65. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/", planeWorkspaceSlug, planeProjectId, cycleID)
  66. resp, err := planeHttpDelete(path)
  67. if err != nil {
  68. zap.L().Error("[Plane] DeleteCycle 请求失败", zap.Error(err))
  69. return err
  70. }
  71. if resp.StatusCode != 204 {
  72. zap.L().Error("[Plane] DeleteCycle 返回错误", zap.String("status", resp.Status))
  73. return fmt.Errorf("DeleteCycle 失败: %s", resp.Status)
  74. }
  75. return nil
  76. }
  77. // ListCycleIssues 列出迭代中的工作项
  78. func ListCycleIssues(cycleID string) ([]byte, error) {
  79. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", planeWorkspaceSlug, planeProjectId, cycleID)
  80. resp, err := planeHttpGet(path)
  81. if err != nil {
  82. zap.L().Error("[Plane] ListCycleIssues 请求失败", zap.Error(err))
  83. return nil, err
  84. }
  85. if !isSuccess(resp.StatusCode) {
  86. zap.L().Error("[Plane] ListCycleIssues 返回错误", zap.String("status", resp.Status))
  87. return nil, fmt.Errorf("ListCycleIssues 失败: %s", resp.Status)
  88. }
  89. return resp.Body, nil
  90. }
  91. // AddIssuesToCycle 添加工作项到迭代
  92. func AddIssuesToCycle(cycleID string, issueIDs []string) ([]byte, error) {
  93. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", planeWorkspaceSlug, planeProjectId, cycleID)
  94. payload := map[string]interface{}{"issues": issueIDs}
  95. resp, err := planeHttpPost(path, payload)
  96. if err != nil {
  97. zap.L().Error("[Plane] AddIssuesToCycle 请求失败", zap.Error(err))
  98. return nil, err
  99. }
  100. if !isSuccess(resp.StatusCode) {
  101. zap.L().Error("[Plane] AddIssuesToCycle 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body)))
  102. return nil, fmt.Errorf("AddIssuesToCycle 失败: %s", resp.Status)
  103. }
  104. return resp.Body, nil
  105. }
  106. // RemoveIssueFromCycle 从迭代移除工作项
  107. func RemoveIssueFromCycle(cycleID, issueID string) error {
  108. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/cycle-issues/%s/", planeWorkspaceSlug, planeProjectId, cycleID, issueID)
  109. resp, err := planeHttpDelete(path)
  110. if err != nil {
  111. zap.L().Error("[Plane] RemoveIssueFromCycle 请求失败", zap.Error(err))
  112. return err
  113. }
  114. if resp.StatusCode != 204 {
  115. zap.L().Error("[Plane] RemoveIssueFromCycle 返回错误", zap.String("status", resp.Status))
  116. return fmt.Errorf("RemoveIssueFromCycle 失败: %s", resp.Status)
  117. }
  118. return nil
  119. }
  120. // TransferCycleIssues 转移未完成工作项到另一迭代
  121. func TransferCycleIssues(cycleID, newCycleID string) ([]byte, error) {
  122. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/transfer-issues/", planeWorkspaceSlug, planeProjectId, cycleID)
  123. payload := map[string]interface{}{"new_cycle_id": newCycleID}
  124. resp, err := planeHttpPost(path, payload)
  125. if err != nil {
  126. zap.L().Error("[Plane] TransferCycleIssues 请求失败", zap.Error(err))
  127. return nil, err
  128. }
  129. if !isSuccess(resp.StatusCode) {
  130. zap.L().Error("[Plane] TransferCycleIssues 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body)))
  131. return nil, fmt.Errorf("TransferCycleIssues 失败: %s", resp.Status)
  132. }
  133. return resp.Body, nil
  134. }
  135. // ArchiveCycle 归档迭代
  136. func ArchiveCycle(cycleID string) error {
  137. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/cycles/%s/archive/", planeWorkspaceSlug, planeProjectId, cycleID)
  138. resp, err := planeHttpPost(path, map[string]interface{}{})
  139. if err != nil {
  140. zap.L().Error("[Plane] ArchiveCycle 请求失败", zap.Error(err))
  141. return err
  142. }
  143. if !isSuccess(resp.StatusCode) {
  144. zap.L().Error("[Plane] ArchiveCycle 返回错误", zap.String("status", resp.Status))
  145. return fmt.Errorf("ArchiveCycle 失败: %s", resp.Status)
  146. }
  147. return nil
  148. }