plane_workitem.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package plane_v1
  2. import (
  3. "fmt"
  4. "net/url"
  5. "strings"
  6. "go.uber.org/zap"
  7. )
  8. // ==================== Work Item API ====================
  9. // ListWorkItems 列出工作项
  10. func ListWorkItems(params map[string]string) ([]byte, error) {
  11. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/", planeWorkspaceSlug, planeProjectId)
  12. if len(params) > 0 {
  13. parts := []string{}
  14. for k, v := range params {
  15. parts = append(parts, url.QueryEscape(k)+"="+url.QueryEscape(v))
  16. }
  17. path += "?" + strings.Join(parts, "&")
  18. }
  19. resp, err := planeHttpGet(path)
  20. if err != nil {
  21. zap.L().Error("[Plane] ListWorkItems 请求失败", zap.Error(err))
  22. return nil, err
  23. }
  24. if !isSuccess(resp.StatusCode) {
  25. zap.L().Error("[Plane] ListWorkItems 返回错误", zap.String("status", resp.Status))
  26. return nil, fmt.Errorf("ListWorkItems 失败: %s", resp.Status)
  27. }
  28. return resp.Body, nil
  29. }
  30. // CreateWorkItem 创建工作项
  31. func CreateWorkItem(data map[string]interface{}) ([]byte, error) {
  32. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/", planeWorkspaceSlug, planeProjectId)
  33. resp, err := planeHttpPost(path, data)
  34. if err != nil {
  35. zap.L().Error("[Plane] CreateWorkItem 请求失败", zap.Error(err))
  36. return nil, err
  37. }
  38. if resp.StatusCode != 201 {
  39. zap.L().Error("[Plane] CreateWorkItem 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body)))
  40. return nil, fmt.Errorf("CreateWorkItem 失败: %s", resp.Status)
  41. }
  42. return resp.Body, nil
  43. }
  44. // GetWorkItem 获取工作项详情
  45. func GetWorkItem(workItemID string) ([]byte, error) {
  46. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/%s/", planeWorkspaceSlug, planeProjectId, workItemID)
  47. resp, err := planeHttpGet(path)
  48. if err != nil {
  49. zap.L().Error("[Plane] GetWorkItem 请求失败", zap.Error(err))
  50. return nil, err
  51. }
  52. if !isSuccess(resp.StatusCode) {
  53. zap.L().Error("[Plane] GetWorkItem 返回错误", zap.String("status", resp.Status))
  54. return nil, fmt.Errorf("GetWorkItem 失败: %s", resp.Status)
  55. }
  56. return resp.Body, nil
  57. }
  58. // UpdateWorkItem 更新工作项
  59. func UpdateWorkItem(workItemID string, data map[string]interface{}) ([]byte, error) {
  60. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/%s/", planeWorkspaceSlug, planeProjectId, workItemID)
  61. resp, err := planeHttpPatch(path, data)
  62. if err != nil {
  63. zap.L().Error("[Plane] UpdateWorkItem 请求失败", zap.Error(err))
  64. return nil, err
  65. }
  66. if !isSuccess(resp.StatusCode) {
  67. zap.L().Error("[Plane] UpdateWorkItem 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body)))
  68. return nil, fmt.Errorf("UpdateWorkItem 失败: %s", resp.Status)
  69. }
  70. return resp.Body, nil
  71. }
  72. // DeleteWorkItem 删除工作项
  73. func DeleteWorkItem(workItemID string) error {
  74. path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/%s/", planeWorkspaceSlug, planeProjectId, workItemID)
  75. resp, err := planeHttpDelete(path)
  76. if err != nil {
  77. zap.L().Error("[Plane] DeleteWorkItem 请求失败", zap.Error(err))
  78. return err
  79. }
  80. if resp.StatusCode != 204 {
  81. zap.L().Error("[Plane] DeleteWorkItem 返回错误", zap.String("status", resp.Status))
  82. return fmt.Errorf("DeleteWorkItem 失败: %s", resp.Status)
  83. }
  84. return nil
  85. }
  86. // GetWorkItemByIdentifier 通过标识符获取工作项(如 PROJ-42)
  87. func GetWorkItemByIdentifier(projectIdentifier string, sequenceID int) ([]byte, error) {
  88. path := fmt.Sprintf("/api/v1/workspaces/%s/work-items/%s-%d/", planeWorkspaceSlug, projectIdentifier, sequenceID)
  89. resp, err := planeHttpGet(path)
  90. if err != nil {
  91. zap.L().Error("[Plane] GetWorkItemByIdentifier 请求失败", zap.Error(err))
  92. return nil, err
  93. }
  94. if !isSuccess(resp.StatusCode) {
  95. zap.L().Error("[Plane] GetWorkItemByIdentifier 返回错误", zap.String("status", resp.Status))
  96. return nil, fmt.Errorf("GetWorkItemByIdentifier 失败: %s", resp.Status)
  97. }
  98. return resp.Body, nil
  99. }
  100. // SearchWorkItems 搜索工作项
  101. func SearchWorkItems(query string, limit int, projectID string) ([]byte, error) {
  102. path := fmt.Sprintf("/api/v1/workspaces/%s/work-items/search/?search=%s&limit=%d",
  103. planeWorkspaceSlug, url.QueryEscape(query), limit)
  104. if projectID != "" {
  105. path += "&project_id=" + projectID
  106. }
  107. resp, err := planeHttpGet(path)
  108. if err != nil {
  109. zap.L().Error("[Plane] SearchWorkItems 请求失败", zap.Error(err))
  110. return nil, err
  111. }
  112. if !isSuccess(resp.StatusCode) {
  113. zap.L().Error("[Plane] SearchWorkItems 返回错误", zap.String("status", resp.Status))
  114. return nil, fmt.Errorf("SearchWorkItems 失败: %s", resp.Status)
  115. }
  116. return resp.Body, nil
  117. }