plane_intake.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package plane_v1
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "go.uber.org/zap"
  6. )
  7. // ==================== Intake Issue 请求/响应结构 ====================
  8. // IntakeIssueRequest Intake Issue创建请求体
  9. type IntakeIssueRequest struct {
  10. Issue IntakeIssue `json:"issue"`
  11. Source string `json:"source"`
  12. }
  13. // IntakeIssue Issue详情
  14. type IntakeIssue struct {
  15. Name string `json:"name"`
  16. DescriptionHTML string `json:"description_html"`
  17. Priority string `json:"priority"`
  18. LabelIds []string `json:"label_ids,omitempty"`
  19. }
  20. // IntakeIssueResponse Intake Issue创建响应
  21. type IntakeIssueResponse struct {
  22. Id string `json:"id"`
  23. IssueId string `json:"issue"`
  24. Status int `json:"status"`
  25. Source string `json:"source"`
  26. CreatedAt string `json:"created_at"`
  27. }
  28. // IntakeListResponse Intake列表响应
  29. type IntakeListResponse struct {
  30. Results []IntakeListItem `json:"results"`
  31. TotalCount int `json:"total_count"`
  32. NextCursor string `json:"next_cursor"`
  33. }
  34. // IntakeListItem Intake列表项
  35. type IntakeListItem struct {
  36. Id string `json:"id"`
  37. Status int `json:"status"`
  38. Source string `json:"source"`
  39. CreatedAt string `json:"created_at"`
  40. Issue IntakeIssueInfo `json:"issue_detail"`
  41. }
  42. // IntakeIssueInfo Issue基础信息
  43. type IntakeIssueInfo struct {
  44. Id string `json:"id"`
  45. Name string `json:"name"`
  46. Priority string `json:"priority"`
  47. }
  48. // ==================== Intake Issue API操作 ====================
  49. // CreateIntakeIssue 创建Intake Issue
  50. func CreateIntakeIssue(req IntakeIssueRequest) (*IntakeIssueResponse, error) {
  51. url := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/intake-issues/",
  52. planeWorkspaceSlug,
  53. planeProjectId,
  54. )
  55. resp, err := planeHttpPost(url, req)
  56. if err != nil {
  57. zap.L().Error("[Plane] CreateIntakeIssue 请求失败", zap.String("err", err.Error()))
  58. return nil, fmt.Errorf("提交失败: %w", err)
  59. }
  60. if !isSuccess(resp.StatusCode) {
  61. zap.L().Error("[Plane] CreateIntakeIssue 返回错误",
  62. zap.String("status", resp.Status),
  63. zap.String("body", string(resp.Body)),
  64. )
  65. return nil, fmt.Errorf("提交失败: Plane 返回 %s", resp.Status)
  66. }
  67. var result IntakeIssueResponse
  68. if err := json.Unmarshal(resp.Body, &result); err != nil {
  69. zap.L().Error("[Plane] CreateIntakeIssue 响应解析失败", zap.String("err", err.Error()))
  70. return nil, fmt.Errorf("响应解析失败: %w", err)
  71. }
  72. zap.L().Info("[Plane] CreateIntakeIssue 成功", zap.String("id", result.Id))
  73. return &result, nil
  74. }
  75. // GetIntakeIssueList 查询Intake Issue列表
  76. func GetIntakeIssueList() (*IntakeListResponse, error) {
  77. url := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/intake-issues/?per_page=100&expand=issue_detail",
  78. planeWorkspaceSlug,
  79. planeProjectId,
  80. )
  81. resp, err := planeHttpGet(url)
  82. if err != nil {
  83. zap.L().Error("[Plane] GetIntakeIssueList 请求失败", zap.String("err", err.Error()))
  84. return nil, fmt.Errorf("查询失败: %w", err)
  85. }
  86. if !isSuccess(resp.StatusCode) {
  87. zap.L().Error("[Plane] GetIntakeIssueList 返回错误", zap.String("status", resp.Status))
  88. return nil, fmt.Errorf("查询失败: Plane 返回 %s", resp.Status)
  89. }
  90. var listResp IntakeListResponse
  91. if err := json.Unmarshal(resp.Body, &listResp); err != nil {
  92. zap.L().Error("[Plane] GetIntakeIssueList 响应解析失败", zap.String("err", err.Error()))
  93. return nil, fmt.Errorf("响应解析失败: %w", err)
  94. }
  95. zap.L().Debug("[Plane] GetIntakeIssueList", zap.Int("count", len(listResp.Results)))
  96. return &listResp, nil
  97. }
  98. // GetIntakeIssueDetail 获取收件箱工作项详情
  99. func GetIntakeIssueDetail(issueID string) ([]byte, error) {
  100. url := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/intake-issues/%s/",
  101. planeWorkspaceSlug, planeProjectId, issueID)
  102. resp, err := planeHttpGet(url)
  103. if err != nil {
  104. zap.L().Error("[Plane] GetIntakeIssueDetail 请求失败", zap.Error(err))
  105. return nil, err
  106. }
  107. if !isSuccess(resp.StatusCode) {
  108. zap.L().Error("[Plane] GetIntakeIssueDetail 返回错误", zap.String("status", resp.Status))
  109. return nil, fmt.Errorf("GetIntakeIssueDetail 失败: %s", resp.Status)
  110. }
  111. return resp.Body, nil
  112. }
  113. // UpdateIntakeIssue 更新收件箱工作项
  114. func UpdateIntakeIssue(issueID string, data map[string]interface{}) ([]byte, error) {
  115. url := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/intake-issues/%s/",
  116. planeWorkspaceSlug, planeProjectId, issueID)
  117. resp, err := planeHttpPatch(url, data)
  118. if err != nil {
  119. zap.L().Error("[Plane] UpdateIntakeIssue 请求失败", zap.Error(err))
  120. return nil, err
  121. }
  122. if !isSuccess(resp.StatusCode) {
  123. zap.L().Error("[Plane] UpdateIntakeIssue 返回错误",
  124. zap.String("status", resp.Status),
  125. zap.String("body", string(resp.Body)),
  126. )
  127. return nil, fmt.Errorf("UpdateIntakeIssue 失败: %s", resp.Status)
  128. }
  129. return resp.Body, nil
  130. }
  131. // AcceptIntakeIssue 接受收件箱工作项
  132. func AcceptIntakeIssue(issueID string) ([]byte, error) {
  133. return UpdateIntakeIssue(issueID, map[string]interface{}{"status": 1})
  134. }
  135. // RejectIntakeIssue 拒绝收件箱工作项
  136. func RejectIntakeIssue(issueID string) ([]byte, error) {
  137. return UpdateIntakeIssue(issueID, map[string]interface{}{"status": -1})
  138. }
  139. // DeleteIntakeIssue 删除收件箱工作项
  140. func DeleteIntakeIssue(issueID string) error {
  141. url := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/intake-issues/%s/",
  142. planeWorkspaceSlug, planeProjectId, issueID)
  143. resp, err := planeHttpDelete(url)
  144. if err != nil {
  145. zap.L().Error("[Plane] DeleteIntakeIssue 请求失败", zap.Error(err))
  146. return err
  147. }
  148. if resp.StatusCode != 204 {
  149. zap.L().Error("[Plane] DeleteIntakeIssue 返回错误", zap.String("status", resp.Status))
  150. return fmt.Errorf("DeleteIntakeIssue 失败: %s", resp.Status)
  151. }
  152. return nil
  153. }