package plane_v1 import ( "fmt" "net/url" "strings" "go.uber.org/zap" ) // ==================== Work Item API ==================== // ListWorkItems 列出工作项 func ListWorkItems(params map[string]string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/", planeWorkspaceSlug, planeProjectId) if len(params) > 0 { parts := []string{} for k, v := range params { parts = append(parts, url.QueryEscape(k)+"="+url.QueryEscape(v)) } path += "?" + strings.Join(parts, "&") } resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] ListWorkItems 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] ListWorkItems 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("ListWorkItems 失败: %s", resp.Status) } return resp.Body, nil } // CreateWorkItem 创建工作项 func CreateWorkItem(data map[string]interface{}) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/", planeWorkspaceSlug, planeProjectId) resp, err := planeHttpPost(path, data) if err != nil { zap.L().Error("[Plane] CreateWorkItem 请求失败", zap.Error(err)) return nil, err } if resp.StatusCode != 201 { zap.L().Error("[Plane] CreateWorkItem 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body))) return nil, fmt.Errorf("CreateWorkItem 失败: %s", resp.Status) } return resp.Body, nil } // GetWorkItem 获取工作项详情 func GetWorkItem(workItemID string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/%s/", planeWorkspaceSlug, planeProjectId, workItemID) resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] GetWorkItem 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] GetWorkItem 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("GetWorkItem 失败: %s", resp.Status) } return resp.Body, nil } // UpdateWorkItem 更新工作项 func UpdateWorkItem(workItemID string, data map[string]interface{}) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/%s/", planeWorkspaceSlug, planeProjectId, workItemID) resp, err := planeHttpPatch(path, data) if err != nil { zap.L().Error("[Plane] UpdateWorkItem 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] UpdateWorkItem 返回错误", zap.String("status", resp.Status), zap.String("body", string(resp.Body))) return nil, fmt.Errorf("UpdateWorkItem 失败: %s", resp.Status) } return resp.Body, nil } // DeleteWorkItem 删除工作项 func DeleteWorkItem(workItemID string) error { path := fmt.Sprintf("/api/v1/workspaces/%s/projects/%s/work-items/%s/", planeWorkspaceSlug, planeProjectId, workItemID) resp, err := planeHttpDelete(path) if err != nil { zap.L().Error("[Plane] DeleteWorkItem 请求失败", zap.Error(err)) return err } if resp.StatusCode != 204 { zap.L().Error("[Plane] DeleteWorkItem 返回错误", zap.String("status", resp.Status)) return fmt.Errorf("DeleteWorkItem 失败: %s", resp.Status) } return nil } // GetWorkItemByIdentifier 通过标识符获取工作项(如 PROJ-42) func GetWorkItemByIdentifier(projectIdentifier string, sequenceID int) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/work-items/%s-%d/", planeWorkspaceSlug, projectIdentifier, sequenceID) resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] GetWorkItemByIdentifier 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] GetWorkItemByIdentifier 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("GetWorkItemByIdentifier 失败: %s", resp.Status) } return resp.Body, nil } // SearchWorkItems 搜索工作项 func SearchWorkItems(query string, limit int, projectID string) ([]byte, error) { path := fmt.Sprintf("/api/v1/workspaces/%s/work-items/search/?search=%s&limit=%d", planeWorkspaceSlug, url.QueryEscape(query), limit) if projectID != "" { path += "&project_id=" + projectID } resp, err := planeHttpGet(path) if err != nil { zap.L().Error("[Plane] SearchWorkItems 请求失败", zap.Error(err)) return nil, err } if !isSuccess(resp.StatusCode) { zap.L().Error("[Plane] SearchWorkItems 返回错误", zap.String("status", resp.Status)) return nil, fmt.Errorf("SearchWorkItems 失败: %s", resp.Status) } return resp.Body, nil }