Sfoglia il codice sorgente

refactor:提取feedback专用接口

Jerry Liang 2 mesi fa
parent
commit
568dc5a36a

+ 230 - 0
service/repositories/api/plane_v2/feedback_service.go

@@ -0,0 +1,230 @@
+package plane_v2
+
+// FeedbackPlaneService Feedback 专用的 Plane 服务(绑定固定的 workspace/project/labels)
+// 封装通用 PlaneService,调用方无需重复传入 workspace/project 参数
+type FeedbackPlaneService struct {
+	*PlaneService
+	WorkspaceSlug   string
+	ProjectId       string
+	LabelBugId      string
+	LabelFeatureId  string
+	LabelFeedbackId string
+}
+
+// NewFeedbackPlaneService 创建 Feedback 专用 PlaneService(须先调用 InitPlaneClient)
+func NewFeedbackPlaneService(workspaceSlug, projectId, labelBugId, labelFeatureId, labelFeedbackId string) *FeedbackPlaneService {
+	return &FeedbackPlaneService{
+		PlaneService:    NewPlaneService(),
+		WorkspaceSlug:   workspaceSlug,
+		ProjectId:       projectId,
+		LabelBugId:      labelBugId,
+		LabelFeatureId:  labelFeatureId,
+		LabelFeedbackId: labelFeedbackId,
+	}
+}
+
+// ============ Intake 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) CreateIntakeIssue(issue IntakeIssueRequest) (*IntakeIssueResponse, error) {
+	return f.PlaneService.CreateIntakeIssue(f.WorkspaceSlug, f.ProjectId, issue)
+}
+
+func (f *FeedbackPlaneService) ListIntakeIssues(status string) ([]byte, error) {
+	return f.PlaneService.ListIntakeIssues(f.WorkspaceSlug, f.ProjectId, status)
+}
+
+func (f *FeedbackPlaneService) GetIntakeIssue(issueID string) ([]byte, error) {
+	return f.PlaneService.GetIntakeIssue(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+func (f *FeedbackPlaneService) UpdateIntakeIssue(issueID string, data map[string]any) ([]byte, error) {
+	return f.PlaneService.UpdateIntakeIssue(f.WorkspaceSlug, f.ProjectId, issueID, data)
+}
+
+func (f *FeedbackPlaneService) AcceptIntakeIssue(issueID string) ([]byte, error) {
+	return f.PlaneService.AcceptIntakeIssue(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+func (f *FeedbackPlaneService) RejectIntakeIssue(issueID string) ([]byte, error) {
+	return f.PlaneService.RejectIntakeIssue(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+func (f *FeedbackPlaneService) DeleteIntakeIssue(issueID string) error {
+	return f.PlaneService.DeleteIntakeIssue(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+// ============ Asset 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) UploadProjectAsset(fileName, mimeType string, fileData []byte, entityType string) (*AssetUploadResult, error) {
+	return f.PlaneService.UploadProjectAsset(f.WorkspaceSlug, f.ProjectId, fileName, mimeType, fileData, entityType)
+}
+
+func (f *FeedbackPlaneService) CreateProjectAsset(fileName, mimeType string, size int64, entityType string) (*AssetUploadResult, error) {
+	return f.PlaneService.CreateProjectAsset(f.WorkspaceSlug, f.ProjectId, fileName, mimeType, size, entityType)
+}
+
+func (f *FeedbackPlaneService) ConfirmProjectAssetUpload(assetID string) error {
+	return f.PlaneService.ConfirmProjectAssetUpload(f.WorkspaceSlug, f.ProjectId, assetID)
+}
+
+func (f *FeedbackPlaneService) GetProjectAssetURL(assetID string) (string, error) {
+	return f.PlaneService.GetProjectAssetURL(f.WorkspaceSlug, f.ProjectId, assetID)
+}
+
+func (f *FeedbackPlaneService) GetProjectAsset(assetID string) ([]byte, error) {
+	return f.PlaneService.GetProjectAsset(f.WorkspaceSlug, f.ProjectId, assetID)
+}
+
+func (f *FeedbackPlaneService) DeleteProjectAsset(assetID string) error {
+	return f.PlaneService.DeleteProjectAsset(f.WorkspaceSlug, f.ProjectId, assetID)
+}
+
+func (f *FeedbackPlaneService) BindAssetsToEntity(entityID string, assetIDs []string) error {
+	return f.PlaneService.BindAssetsToEntity(f.WorkspaceSlug, f.ProjectId, entityID, assetIDs)
+}
+
+// ============ Workspace 便捷方法(绑定 workspace)============
+
+func (f *FeedbackPlaneService) GetWorkspace() ([]byte, error) {
+	return f.PlaneService.GetWorkspace(f.WorkspaceSlug)
+}
+
+// ============ Project 便捷方法(绑定 workspace)============
+
+func (f *FeedbackPlaneService) ListProjects() ([]byte, error) {
+	return f.PlaneService.ListProjects(f.WorkspaceSlug)
+}
+
+func (f *FeedbackPlaneService) GetProject(projectID string) ([]byte, error) {
+	return f.PlaneService.GetProject(f.WorkspaceSlug, projectID)
+}
+
+// ============ Issue 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListIssues() ([]byte, error) {
+	return f.PlaneService.ListIssues(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetIssue(issueID string) ([]byte, error) {
+	return f.PlaneService.GetIssue(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+func (f *FeedbackPlaneService) GetSubIssues(issueID string) ([]byte, error) {
+	return f.PlaneService.GetSubIssues(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+func (f *FeedbackPlaneService) GetIssueActivity(issueID string) ([]byte, error) {
+	return f.PlaneService.GetIssueActivity(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+// ============ Cycle 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListCycles() ([]byte, error) {
+	return f.PlaneService.ListCycles(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetCycle(cycleID string) ([]byte, error) {
+	return f.PlaneService.GetCycle(f.WorkspaceSlug, f.ProjectId, cycleID)
+}
+
+func (f *FeedbackPlaneService) ListCycleIssues(cycleID string) ([]byte, error) {
+	return f.PlaneService.ListCycleIssues(f.WorkspaceSlug, f.ProjectId, cycleID)
+}
+
+// ============ Module 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListModules() ([]byte, error) {
+	return f.PlaneService.ListModules(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetModule(moduleID string) ([]byte, error) {
+	return f.PlaneService.GetModule(f.WorkspaceSlug, f.ProjectId, moduleID)
+}
+
+func (f *FeedbackPlaneService) ListModuleIssues(moduleID string) ([]byte, error) {
+	return f.PlaneService.ListModuleIssues(f.WorkspaceSlug, f.ProjectId, moduleID)
+}
+
+// ============ Label 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListLabels() ([]byte, error) {
+	return f.PlaneService.ListLabels(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetLabel(labelID string) ([]byte, error) {
+	return f.PlaneService.GetLabel(f.WorkspaceSlug, f.ProjectId, labelID)
+}
+
+// ============ State 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListStates() ([]byte, error) {
+	return f.PlaneService.ListStates(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetState(stateID string) ([]byte, error) {
+	return f.PlaneService.GetState(f.WorkspaceSlug, f.ProjectId, stateID)
+}
+
+// ============ Comment 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListComments(issueID string) ([]byte, error) {
+	return f.PlaneService.ListComments(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+func (f *FeedbackPlaneService) GetComment(issueID, commentID string) ([]byte, error) {
+	return f.PlaneService.GetComment(f.WorkspaceSlug, f.ProjectId, issueID, commentID)
+}
+
+// ============ Link 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListIssueLinks(issueID string) ([]byte, error) {
+	return f.PlaneService.ListIssueLinks(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+// ============ Relation 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListIssueRelations(issueID string) ([]byte, error) {
+	return f.PlaneService.ListIssueRelations(f.WorkspaceSlug, f.ProjectId, issueID)
+}
+
+// ============ Sticky 便捷方法(绑定 workspace)============
+
+func (f *FeedbackPlaneService) ListStickies() ([]byte, error) {
+	return f.PlaneService.ListStickies(f.WorkspaceSlug)
+}
+
+func (f *FeedbackPlaneService) GetSticky(stickyID string) ([]byte, error) {
+	return f.PlaneService.GetSticky(f.WorkspaceSlug, stickyID)
+}
+
+// ============ Page 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListPages() ([]byte, error) {
+	return f.PlaneService.ListPages(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetPage(pageID string) ([]byte, error) {
+	return f.PlaneService.GetPage(f.WorkspaceSlug, f.ProjectId, pageID)
+}
+
+func (f *FeedbackPlaneService) GetPageDescription(pageID string) ([]byte, error) {
+	return f.PlaneService.GetPageDescription(f.WorkspaceSlug, f.ProjectId, pageID)
+}
+
+// ============ Member 便捷方法(绑定 workspace/project)============
+
+func (f *FeedbackPlaneService) ListWorkspaceMembers() ([]byte, error) {
+	return f.PlaneService.ListWorkspaceMembers(f.WorkspaceSlug)
+}
+
+func (f *FeedbackPlaneService) GetWorkspaceMember(memberID string) ([]byte, error) {
+	return f.PlaneService.GetWorkspaceMember(f.WorkspaceSlug, memberID)
+}
+
+func (f *FeedbackPlaneService) ListProjectMembers() ([]byte, error) {
+	return f.PlaneService.ListProjectMembers(f.WorkspaceSlug, f.ProjectId)
+}
+
+func (f *FeedbackPlaneService) GetProjectMember(memberID string) ([]byte, error) {
+	return f.PlaneService.GetProjectMember(f.WorkspaceSlug, f.ProjectId, memberID)
+}

+ 437 - 456
service/repositories/api/plane_v2/service.go

@@ -5,440 +5,462 @@ import (
 	"fmt"
 )
 
-// PlaneService 便捷封装(绑定固定的 workspace/project,共享全局 PlaneClient)
+// PlaneService 通用 Plane API 封装(无绑定配置,workspace/project 通过参数传入)
 type PlaneService struct {
-	Client          *PlaneClient
-	WorkspaceSlug   string
-	ProjectId       string
-	LabelBugId      string
-	LabelFeatureId  string
-	LabelFeedbackId string
+	Client *PlaneClient
 }
 
-// NewPlaneService 创建 PlaneService(须先调用 InitPlaneClient)
-func NewPlaneService(workspaceSlug, projectId, labelBugId, labelFeatureId, labelFeedbackId string) *PlaneService {
+// NewPlaneService 创建通用 PlaneService(须先调用 InitPlaneClient)
+func NewPlaneService() *PlaneService {
 	if globalClient == nil {
 		panic("[PlaneService] PlaneClient 未初始化,请先调用 InitPlaneClient")
 	}
-
-	return &PlaneService{
-		Client:          globalClient,
-		WorkspaceSlug:   workspaceSlug,
-		ProjectId:       projectId,
-		LabelBugId:      labelBugId,
-		LabelFeatureId:  labelFeatureId,
-		LabelFeedbackId: labelFeedbackId,
-	}
+	return &PlaneService{Client: globalClient}
 }
 
-// ============ Intake 便捷方法 ============
-
-func (s *PlaneService) CreateIntakeIssue(issue IntakeIssueRequest) (*IntakeIssueResponse, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/", s.WorkspaceSlug, s.ProjectId)
-	payload := map[string]any{"source": "IN_APP", "issue": issue}
+// ============ Workspace 方法 ============
 
-	resp, err := s.Client.Post(url, payload)
+func (s *PlaneService) ListWorkspaces() ([]byte, error) {
+	resp, err := s.Client.Get("/api/workspaces/")
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateIntakeIssue 失败: %s", resp.Status)
-	}
-	var result IntakeIssueResponse
-	if err := json.Unmarshal(resp.Body, &result); err != nil {
-		return nil, err
+		return nil, fmt.Errorf("ListWorkspaces 失败: %s", resp.Status)
 	}
-	return &result, nil
+	return resp.Body, nil
 }
 
-func (s *PlaneService) ListIntakeIssues(status string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/", s.WorkspaceSlug, s.ProjectId)
-	if status != "" {
-		url += "?status=" + status
-	}
+func (s *PlaneService) GetWorkspace(workspaceSlug string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/", workspaceSlug)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListIntakeIssues 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetWorkspace 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetIntakeIssue(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/%s/?expand=issue_inbox", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Get(url)
+func (s *PlaneService) CreateWorkspace(name, slug string) ([]byte, error) {
+	payload := map[string]interface{}{"name": name, "slug": slug}
+	resp, err := s.Client.Post("/api/workspaces/", payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetIntakeIssue 失败: %s", resp.Status)
+		return nil, fmt.Errorf("CreateWorkspace 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-// ============ Asset 便捷方法 ============
-
-func (s *PlaneService) UploadProjectAsset(fileName, mimeType string, fileData []byte, entityType string) (*AssetUploadResult, error) {
-	// Step 1: 创建资源
-	createURL := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/", s.WorkspaceSlug, s.ProjectId)
-	createPayload := map[string]any{
-		"name": fileName, "type": mimeType, "size": len(fileData),
-		"entity_type": entityType, "entity_identifier": "",
-	}
-	resp, err := s.Client.Post(createURL, createPayload)
+func (s *PlaneService) UpdateWorkspace(workspaceSlug string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/", workspaceSlug)
+	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateProjectAsset 失败: %s", resp.Status)
-	}
-	var result AssetUploadResult
-	if err := json.Unmarshal(resp.Body, &result); err != nil {
-		return nil, err
-	}
-
-	// Step 2: 上传到 S3
-	if err := s.Client.PostS3(result.UploadData.URL, result.UploadData.Fields, fileData, fileName); err != nil {
-		return nil, err
+		return nil, fmt.Errorf("UpdateWorkspace 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
-
-	// Step 3: 确认
-	confirmURL := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", s.WorkspaceSlug, s.ProjectId, result.AssetID)
-	s.Client.Patch(confirmURL, map[string]any{})
-
-	return &result, nil
-}
-
-func (s *PlaneService) GetProjectAssetURL(assetID string) (string, error) {
-	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", s.WorkspaceSlug, s.ProjectId, assetID)
-	return s.Client.GetRedirectLocation(url)
+	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteProjectAsset(assetID string) error {
-	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", s.WorkspaceSlug, s.ProjectId, assetID)
+func (s *PlaneService) DeleteWorkspace(workspaceSlug string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/", workspaceSlug)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteProjectAsset 失败: %s", resp.Status)
-	}
-	return nil
-}
-
-func (s *PlaneService) BindAssetsToEntity(entityID string, assetIDs []string) error {
-	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/bulk/", s.WorkspaceSlug, s.ProjectId, entityID)
-	resp, err := s.Client.Post(url, map[string]any{"asset_ids": assetIDs})
-	if err != nil {
-		return err
-	}
-	if resp.StatusCode != 204 && !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("BindAssetsToEntity 失败: %s", resp.Status)
+		return fmt.Errorf("DeleteWorkspace 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-// ============ Page 便捷方法 ============
-
-func (s *PlaneService) ListPages() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/", s.WorkspaceSlug, s.ProjectId)
-	resp, err := s.Client.Get(url)
-	if err != nil {
-		return nil, err
-	}
-	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListPages 失败: %s", resp.Status)
-	}
-	return resp.Body, nil
-}
+// ============ Project 方法 ============
 
-func (s *PlaneService) GetPage(pageID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/", s.WorkspaceSlug, s.ProjectId, pageID)
+func (s *PlaneService) ListProjects(workspaceSlug string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/", workspaceSlug)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetPage 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListProjects 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetPageDescription(pageID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/description/", s.WorkspaceSlug, s.ProjectId, pageID)
+func (s *PlaneService) GetProject(workspaceSlug, projectID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/", workspaceSlug, projectID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetPageDescription 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetProject 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-// ============ Workspace 便捷方法 ============
-
-func (s *PlaneService) ListWorkspaces() ([]byte, error) {
-	resp, err := s.Client.Get("/api/workspaces/")
+func (s *PlaneService) CreateProject(workspaceSlug string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/", workspaceSlug)
+	resp, err := s.Client.Post(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListWorkspaces 失败: %s", resp.Status)
+		return nil, fmt.Errorf("CreateProject 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetWorkspace() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/", s.WorkspaceSlug)
-	resp, err := s.Client.Get(url)
+func (s *PlaneService) UpdateProject(workspaceSlug, projectID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/", workspaceSlug, projectID)
+	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetWorkspace 失败: %s", resp.Status)
+		return nil, fmt.Errorf("UpdateProject 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateWorkspace(name, slug string) ([]byte, error) {
-	payload := map[string]interface{}{"name": name, "slug": slug}
-	resp, err := s.Client.Post("/api/workspaces/", payload)
+func (s *PlaneService) DeleteProject(workspaceSlug, projectID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/", workspaceSlug, projectID)
+	resp, err := s.Client.Delete(url)
 	if err != nil {
-		return nil, err
+		return err
 	}
-	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateWorkspace 失败: %s, body=%s", resp.Status, string(resp.Body))
+	if resp.StatusCode != 204 {
+		return fmt.Errorf("DeleteProject 失败: %s", resp.Status)
 	}
-	return resp.Body, nil
+	return nil
 }
 
-func (s *PlaneService) UpdateWorkspace(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/", s.WorkspaceSlug)
-	resp, err := s.Client.Patch(url, data)
+func (s *PlaneService) ArchiveProject(workspaceSlug, projectID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/archive/", workspaceSlug, projectID)
+	resp, err := s.Client.Post(url, nil)
 	if err != nil {
-		return nil, err
+		return err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateWorkspace 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return fmt.Errorf("ArchiveProject 失败: %s", resp.Status)
 	}
-	return resp.Body, nil
+	return nil
 }
 
-func (s *PlaneService) DeleteWorkspace() error {
-	url := fmt.Sprintf("/api/workspaces/%s/", s.WorkspaceSlug)
+func (s *PlaneService) UnarchiveProject(workspaceSlug, projectID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/archive/", workspaceSlug, projectID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
-	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteWorkspace 失败: %s", resp.Status)
+	if !IsSuccess(resp.StatusCode) {
+		return fmt.Errorf("UnarchiveProject 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-// ============ Project 便捷方法 ============
+// ============ Issue 方法 ============
 
-func (s *PlaneService) ListProjects() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/", s.WorkspaceSlug)
+func (s *PlaneService) ListIssues(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/", workspaceSlug, projectId)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListProjects 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListIssues 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetProject(projectID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/", s.WorkspaceSlug, projectID)
+func (s *PlaneService) GetIssue(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetProject 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetIssue 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateProject(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/", s.WorkspaceSlug)
+func (s *PlaneService) CreateIssue(workspaceSlug, projectId string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/", workspaceSlug, projectId)
 	resp, err := s.Client.Post(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateProject 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("CreateIssue 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateProject(projectID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/", s.WorkspaceSlug, projectID)
+func (s *PlaneService) UpdateIssue(workspaceSlug, projectId, issueID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateProject 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateIssue 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteProject(projectID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/", s.WorkspaceSlug, projectID)
+func (s *PlaneService) DeleteIssue(workspaceSlug, projectId, issueID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteProject 失败: %s", resp.Status)
+		return fmt.Errorf("DeleteIssue 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) ArchiveProject(projectID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/archive/", s.WorkspaceSlug, projectID)
+func (s *PlaneService) GetSubIssues(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/sub-issues/", workspaceSlug, projectId, issueID)
+	resp, err := s.Client.Get(url)
+	if err != nil {
+		return nil, err
+	}
+	if !IsSuccess(resp.StatusCode) {
+		return nil, fmt.Errorf("GetSubIssues 失败: %s", resp.Status)
+	}
+	return resp.Body, nil
+}
+
+func (s *PlaneService) ArchiveIssue(workspaceSlug, projectId, issueID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/archive/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Post(url, nil)
 	if err != nil {
 		return err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("ArchiveProject 失败: %s", resp.Status)
+		return fmt.Errorf("ArchiveIssue 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) UnarchiveProject(projectID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/archive/", s.WorkspaceSlug, projectID)
+func (s *PlaneService) UnarchiveIssue(workspaceSlug, projectId, issueID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/archive/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("UnarchiveProject 失败: %s", resp.Status)
+		return fmt.Errorf("UnarchiveIssue 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-// ============ Issue 便捷方法 ============
-
-func (s *PlaneService) ListIssues() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) GetIssueActivity(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/history/?activity_type=issue-property", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListIssues 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetIssueActivity 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetIssue(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/", s.WorkspaceSlug, s.ProjectId, issueID)
+// ============ Intake 方法 ============
+
+func (s *PlaneService) CreateIntakeIssue(workspaceSlug, projectId string, issue IntakeIssueRequest) (*IntakeIssueResponse, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/", workspaceSlug, projectId)
+	payload := map[string]any{"source": "IN_APP", "issue": issue}
+
+	resp, err := s.Client.Post(url, payload)
+	if err != nil {
+		return nil, err
+	}
+	if !IsSuccess(resp.StatusCode) {
+		return nil, fmt.Errorf("CreateIntakeIssue 失败: %s", resp.Status)
+	}
+	var result IntakeIssueResponse
+	if err := json.Unmarshal(resp.Body, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (s *PlaneService) ListIntakeIssues(workspaceSlug, projectId, status string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/", workspaceSlug, projectId)
+	if status != "" {
+		url += "?status=" + status
+	}
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetIssue 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListIntakeIssues 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateIssue(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/", s.WorkspaceSlug, s.ProjectId)
-	resp, err := s.Client.Post(url, data)
+func (s *PlaneService) GetIntakeIssue(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/%s/?expand=issue_inbox", workspaceSlug, projectId, issueID)
+	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateIssue 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("GetIntakeIssue 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateIssue(issueID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/", s.WorkspaceSlug, s.ProjectId, issueID)
+func (s *PlaneService) UpdateIntakeIssue(workspaceSlug, projectId, issueID string, data map[string]any) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/%s/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateIssue 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateIntakeIssue 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteIssue(issueID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/", s.WorkspaceSlug, s.ProjectId, issueID)
+func (s *PlaneService) AcceptIntakeIssue(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	return s.UpdateIntakeIssue(workspaceSlug, projectId, issueID, map[string]any{"status": 1})
+}
+
+func (s *PlaneService) RejectIntakeIssue(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	return s.UpdateIntakeIssue(workspaceSlug, projectId, issueID, map[string]any{"status": -1})
+}
+
+func (s *PlaneService) DeleteIntakeIssue(workspaceSlug, projectId, issueID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/%s/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteIssue 失败: %s", resp.Status)
+		return fmt.Errorf("DeleteIntakeIssue 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) GetSubIssues(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/sub-issues/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Get(url)
+// ============ Asset 方法 ============
+
+func (s *PlaneService) UploadProjectAsset(workspaceSlug, projectId, fileName, mimeType string, fileData []byte, entityType string) (*AssetUploadResult, error) {
+	// Step 1: 创建资源
+	createURL := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/", workspaceSlug, projectId)
+	createPayload := map[string]any{
+		"name": fileName, "type": mimeType, "size": len(fileData),
+		"entity_type": entityType, "entity_identifier": "",
+	}
+	resp, err := s.Client.Post(createURL, createPayload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetSubIssues 失败: %s", resp.Status)
+		return nil, fmt.Errorf("CreateProjectAsset 失败: %s", resp.Status)
 	}
-	return resp.Body, nil
+	var result AssetUploadResult
+	if err := json.Unmarshal(resp.Body, &result); err != nil {
+		return nil, err
+	}
+
+	// Step 2: 上传到 S3
+	if err := s.Client.PostS3(result.UploadData.URL, result.UploadData.Fields, fileData, fileName); err != nil {
+		return nil, err
+	}
+
+	// Step 3: 确认
+	confirmURL := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", workspaceSlug, projectId, result.AssetID)
+	s.Client.Patch(confirmURL, map[string]any{})
+
+	return &result, nil
 }
 
-func (s *PlaneService) ArchiveIssue(issueID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/archive/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Post(url, nil)
+func (s *PlaneService) CreateProjectAsset(workspaceSlug, projectId, fileName, mimeType string, size int64, entityType string) (*AssetUploadResult, error) {
+	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/", workspaceSlug, projectId)
+	payload := map[string]interface{}{
+		"name": fileName, "type": mimeType, "size": size,
+		"entity_type": entityType, "entity_identifier": "",
+	}
+	resp, err := s.Client.Post(url, payload)
 	if err != nil {
-		return err
+		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("ArchiveIssue 失败: %s", resp.Status)
+		return nil, fmt.Errorf("CreateProjectAsset 失败: %s", resp.Status)
 	}
-	return nil
+	var result AssetUploadResult
+	if err := json.Unmarshal(resp.Body, &result); err != nil {
+		return nil, fmt.Errorf("解析响应失败: %w", err)
+	}
+	return &result, nil
 }
 
-func (s *PlaneService) UnarchiveIssue(issueID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/archive/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Delete(url)
+func (s *PlaneService) ConfirmProjectAssetUpload(workspaceSlug, projectId, assetID string) error {
+	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", workspaceSlug, projectId, assetID)
+	resp, err := s.Client.Patch(url, map[string]interface{}{})
 	if err != nil {
 		return err
 	}
-	if !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("UnarchiveIssue 失败: %s", resp.Status)
+	if resp.StatusCode != 204 && !IsSuccess(resp.StatusCode) {
+		return fmt.Errorf("ConfirmProjectAssetUpload 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) GetIssueActivity(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/history/?activity_type=issue-property", s.WorkspaceSlug, s.ProjectId, issueID)
+func (s *PlaneService) GetProjectAssetURL(workspaceSlug, projectId, assetID string) (string, error) {
+	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", workspaceSlug, projectId, assetID)
+	return s.Client.GetRedirectLocation(url)
+}
+
+func (s *PlaneService) GetProjectAsset(workspaceSlug, projectId, assetID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", workspaceSlug, projectId, assetID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
-	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetIssueActivity 失败: %s", resp.Status)
-	}
 	return resp.Body, nil
 }
 
-// ============ Cycle 便捷方法 ============
+func (s *PlaneService) DeleteProjectAsset(workspaceSlug, projectId, assetID string) error {
+	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", workspaceSlug, projectId, assetID)
+	resp, err := s.Client.Delete(url)
+	if err != nil {
+		return err
+	}
+	if resp.StatusCode != 204 {
+		return fmt.Errorf("DeleteProjectAsset 失败: %s", resp.Status)
+	}
+	return nil
+}
+
+func (s *PlaneService) BindAssetsToEntity(workspaceSlug, projectId, entityID string, assetIDs []string) error {
+	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/bulk/", workspaceSlug, projectId, entityID)
+	resp, err := s.Client.Post(url, map[string]any{"asset_ids": assetIDs})
+	if err != nil {
+		return err
+	}
+	if resp.StatusCode != 204 && !IsSuccess(resp.StatusCode) {
+		return fmt.Errorf("BindAssetsToEntity 失败: %s", resp.Status)
+	}
+	return nil
+}
+
+// ============ Cycle 方法 ============
 
-func (s *PlaneService) ListCycles() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) ListCycles(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/", workspaceSlug, projectId)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -449,8 +471,8 @@ func (s *PlaneService) ListCycles() ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetCycle(cycleID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) GetCycle(workspaceSlug, projectId, cycleID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/", workspaceSlug, projectId, cycleID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -461,8 +483,8 @@ func (s *PlaneService) GetCycle(cycleID string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateCycle(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) CreateCycle(workspaceSlug, projectId string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/", workspaceSlug, projectId)
 	resp, err := s.Client.Post(url, data)
 	if err != nil {
 		return nil, err
@@ -473,8 +495,8 @@ func (s *PlaneService) CreateCycle(data map[string]interface{}) ([]byte, error)
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateCycle(cycleID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) UpdateCycle(workspaceSlug, projectId, cycleID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/", workspaceSlug, projectId, cycleID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
@@ -485,8 +507,8 @@ func (s *PlaneService) UpdateCycle(cycleID string, data map[string]interface{})
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteCycle(cycleID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) DeleteCycle(workspaceSlug, projectId, cycleID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/", workspaceSlug, projectId, cycleID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -497,8 +519,8 @@ func (s *PlaneService) DeleteCycle(cycleID string) error {
 	return nil
 }
 
-func (s *PlaneService) ListCycleIssues(cycleID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) ListCycleIssues(workspaceSlug, projectId, cycleID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", workspaceSlug, projectId, cycleID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -509,8 +531,8 @@ func (s *PlaneService) ListCycleIssues(cycleID string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) AddIssuesToCycle(cycleID string, issueIDs []string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) AddIssuesToCycle(workspaceSlug, projectId, cycleID string, issueIDs []string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/cycle-issues/", workspaceSlug, projectId, cycleID)
 	payload := map[string]interface{}{"issues": issueIDs}
 	resp, err := s.Client.Post(url, payload)
 	if err != nil {
@@ -522,8 +544,8 @@ func (s *PlaneService) AddIssuesToCycle(cycleID string, issueIDs []string) ([]by
 	return resp.Body, nil
 }
 
-func (s *PlaneService) RemoveIssueFromCycle(cycleID, issueID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/cycle-issues/%s/", s.WorkspaceSlug, s.ProjectId, cycleID, issueID)
+func (s *PlaneService) RemoveIssueFromCycle(workspaceSlug, projectId, cycleID, issueID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/cycle-issues/%s/", workspaceSlug, projectId, cycleID, issueID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -534,8 +556,8 @@ func (s *PlaneService) RemoveIssueFromCycle(cycleID, issueID string) error {
 	return nil
 }
 
-func (s *PlaneService) ArchiveCycle(cycleID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/archive/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) ArchiveCycle(workspaceSlug, projectId, cycleID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/archive/", workspaceSlug, projectId, cycleID)
 	resp, err := s.Client.Post(url, nil)
 	if err != nil {
 		return err
@@ -546,8 +568,8 @@ func (s *PlaneService) ArchiveCycle(cycleID string) error {
 	return nil
 }
 
-func (s *PlaneService) UnarchiveCycle(cycleID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/archive/", s.WorkspaceSlug, s.ProjectId, cycleID)
+func (s *PlaneService) UnarchiveCycle(workspaceSlug, projectId, cycleID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/cycles/%s/archive/", workspaceSlug, projectId, cycleID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -558,10 +580,10 @@ func (s *PlaneService) UnarchiveCycle(cycleID string) error {
 	return nil
 }
 
-// ============ Module 便捷方法 ============
+// ============ Module 方法 ============
 
-func (s *PlaneService) ListModules() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) ListModules(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/", workspaceSlug, projectId)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -572,8 +594,8 @@ func (s *PlaneService) ListModules() ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetModule(moduleID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) GetModule(workspaceSlug, projectId, moduleID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/", workspaceSlug, projectId, moduleID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -584,8 +606,8 @@ func (s *PlaneService) GetModule(moduleID string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateModule(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) CreateModule(workspaceSlug, projectId string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/", workspaceSlug, projectId)
 	resp, err := s.Client.Post(url, data)
 	if err != nil {
 		return nil, err
@@ -596,8 +618,8 @@ func (s *PlaneService) CreateModule(data map[string]interface{}) ([]byte, error)
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateModule(moduleID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) UpdateModule(workspaceSlug, projectId, moduleID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/", workspaceSlug, projectId, moduleID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
@@ -608,8 +630,8 @@ func (s *PlaneService) UpdateModule(moduleID string, data map[string]interface{}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteModule(moduleID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) DeleteModule(workspaceSlug, projectId, moduleID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/", workspaceSlug, projectId, moduleID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -620,8 +642,8 @@ func (s *PlaneService) DeleteModule(moduleID string) error {
 	return nil
 }
 
-func (s *PlaneService) ListModuleIssues(moduleID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/module-issues/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) ListModuleIssues(workspaceSlug, projectId, moduleID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/module-issues/", workspaceSlug, projectId, moduleID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -632,8 +654,8 @@ func (s *PlaneService) ListModuleIssues(moduleID string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) AddIssuesToModule(moduleID string, issueIDs []string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/module-issues/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) AddIssuesToModule(workspaceSlug, projectId, moduleID string, issueIDs []string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/module-issues/", workspaceSlug, projectId, moduleID)
 	payload := map[string]interface{}{"issues": issueIDs}
 	resp, err := s.Client.Post(url, payload)
 	if err != nil {
@@ -645,8 +667,8 @@ func (s *PlaneService) AddIssuesToModule(moduleID string, issueIDs []string) ([]
 	return resp.Body, nil
 }
 
-func (s *PlaneService) RemoveIssueFromModule(moduleID, issueID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/module-issues/%s/", s.WorkspaceSlug, s.ProjectId, moduleID, issueID)
+func (s *PlaneService) RemoveIssueFromModule(workspaceSlug, projectId, moduleID, issueID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/module-issues/%s/", workspaceSlug, projectId, moduleID, issueID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -657,8 +679,8 @@ func (s *PlaneService) RemoveIssueFromModule(moduleID, issueID string) error {
 	return nil
 }
 
-func (s *PlaneService) ArchiveModule(moduleID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/archive/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) ArchiveModule(workspaceSlug, projectId, moduleID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/archive/", workspaceSlug, projectId, moduleID)
 	resp, err := s.Client.Post(url, nil)
 	if err != nil {
 		return err
@@ -669,8 +691,8 @@ func (s *PlaneService) ArchiveModule(moduleID string) error {
 	return nil
 }
 
-func (s *PlaneService) UnarchiveModule(moduleID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/archive/", s.WorkspaceSlug, s.ProjectId, moduleID)
+func (s *PlaneService) UnarchiveModule(workspaceSlug, projectId, moduleID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/modules/%s/archive/", workspaceSlug, projectId, moduleID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -681,10 +703,10 @@ func (s *PlaneService) UnarchiveModule(moduleID string) error {
 	return nil
 }
 
-// ============ Label 便捷方法 ============
+// ============ Label 方法 ============
 
-func (s *PlaneService) ListLabels() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) ListLabels(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/", workspaceSlug, projectId)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -695,8 +717,8 @@ func (s *PlaneService) ListLabels() ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetLabel(labelID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/%s/", s.WorkspaceSlug, s.ProjectId, labelID)
+func (s *PlaneService) GetLabel(workspaceSlug, projectId, labelID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/%s/", workspaceSlug, projectId, labelID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -707,8 +729,8 @@ func (s *PlaneService) GetLabel(labelID string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateLabel(name, color string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) CreateLabel(workspaceSlug, projectId, name, color string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/", workspaceSlug, projectId)
 	payload := map[string]interface{}{"name": name, "color": color}
 	resp, err := s.Client.Post(url, payload)
 	if err != nil {
@@ -720,8 +742,8 @@ func (s *PlaneService) CreateLabel(name, color string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateLabel(labelID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/%s/", s.WorkspaceSlug, s.ProjectId, labelID)
+func (s *PlaneService) UpdateLabel(workspaceSlug, projectId, labelID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/%s/", workspaceSlug, projectId, labelID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
@@ -732,8 +754,8 @@ func (s *PlaneService) UpdateLabel(labelID string, data map[string]interface{})
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteLabel(labelID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/%s/", s.WorkspaceSlug, s.ProjectId, labelID)
+func (s *PlaneService) DeleteLabel(workspaceSlug, projectId, labelID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/labels/%s/", workspaceSlug, projectId, labelID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -744,10 +766,10 @@ func (s *PlaneService) DeleteLabel(labelID string) error {
 	return nil
 }
 
-// ============ State 便捷方法 ============
+// ============ State 方法 ============
 
-func (s *PlaneService) ListStates() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) ListStates(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/", workspaceSlug, projectId)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -758,8 +780,8 @@ func (s *PlaneService) ListStates() ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetState(stateID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/", s.WorkspaceSlug, s.ProjectId, stateID)
+func (s *PlaneService) GetState(workspaceSlug, projectId, stateID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/", workspaceSlug, projectId, stateID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
@@ -770,8 +792,8 @@ func (s *PlaneService) GetState(stateID string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateState(name, color, group string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/", s.WorkspaceSlug, s.ProjectId)
+func (s *PlaneService) CreateState(workspaceSlug, projectId, name, color, group string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/", workspaceSlug, projectId)
 	payload := map[string]interface{}{"name": name, "color": color, "group": group}
 	resp, err := s.Client.Post(url, payload)
 	if err != nil {
@@ -783,8 +805,8 @@ func (s *PlaneService) CreateState(name, color, group string) ([]byte, error) {
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateState(stateID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/", s.WorkspaceSlug, s.ProjectId, stateID)
+func (s *PlaneService) UpdateState(workspaceSlug, projectId, stateID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/", workspaceSlug, projectId, stateID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
@@ -795,8 +817,8 @@ func (s *PlaneService) UpdateState(stateID string, data map[string]interface{})
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteState(stateID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/", s.WorkspaceSlug, s.ProjectId, stateID)
+func (s *PlaneService) DeleteState(workspaceSlug, projectId, stateID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/", workspaceSlug, projectId, stateID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
@@ -807,8 +829,8 @@ func (s *PlaneService) DeleteState(stateID string) error {
 	return nil
 }
 
-func (s *PlaneService) MarkStateDefault(stateID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/default/", s.WorkspaceSlug, s.ProjectId, stateID)
+func (s *PlaneService) MarkStateDefault(workspaceSlug, projectId, stateID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/states/%s/default/", workspaceSlug, projectId, stateID)
 	resp, err := s.Client.Post(url, nil)
 	if err != nil {
 		return err
@@ -819,470 +841,429 @@ func (s *PlaneService) MarkStateDefault(stateID string) error {
 	return nil
 }
 
-// ============ Comment 便捷方法 ============
+// ============ Page 方法 ============
 
-func (s *PlaneService) ListComments(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/", s.WorkspaceSlug, s.ProjectId, issueID)
+func (s *PlaneService) ListPages(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/", workspaceSlug, projectId)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListComments 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListPages 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetComment(issueID, commentID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/%s/", s.WorkspaceSlug, s.ProjectId, issueID, commentID)
+func (s *PlaneService) GetPage(workspaceSlug, projectId, pageID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/", workspaceSlug, projectId, pageID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetComment 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetPage 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateComment(issueID, commentHTML string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/", s.WorkspaceSlug, s.ProjectId, issueID)
-	payload := map[string]interface{}{"comment_html": commentHTML}
-	resp, err := s.Client.Post(url, payload)
+func (s *PlaneService) GetPageDescription(workspaceSlug, projectId, pageID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/description/", workspaceSlug, projectId, pageID)
+	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateComment 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("GetPageDescription 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateComment(issueID, commentID, commentHTML string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/%s/", s.WorkspaceSlug, s.ProjectId, issueID, commentID)
-	payload := map[string]interface{}{"comment_html": commentHTML}
-	resp, err := s.Client.Patch(url, payload)
+func (s *PlaneService) CreatePage(workspaceSlug, projectId string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/", workspaceSlug, projectId)
+	resp, err := s.Client.Post(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateComment 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("CreatePage 失败: %s, body=%s", resp.Status, string(resp.Body))
+	}
+	return resp.Body, nil
+}
+
+func (s *PlaneService) UpdatePage(workspaceSlug, projectId, pageID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/", workspaceSlug, projectId, pageID)
+	resp, err := s.Client.Patch(url, data)
+	if err != nil {
+		return nil, err
+	}
+	if !IsSuccess(resp.StatusCode) {
+		return nil, fmt.Errorf("UpdatePage 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteComment(issueID, commentID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/%s/", s.WorkspaceSlug, s.ProjectId, issueID, commentID)
+func (s *PlaneService) DeletePage(workspaceSlug, projectId, pageID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/", workspaceSlug, projectId, pageID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteComment 失败: %s", resp.Status)
+		return fmt.Errorf("DeletePage 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-// ============ Link 便捷方法 ============
-
-func (s *PlaneService) ListIssueLinks(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Get(url)
+func (s *PlaneService) ArchivePage(workspaceSlug, projectId, pageID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/archive/", workspaceSlug, projectId, pageID)
+	resp, err := s.Client.Post(url, nil)
 	if err != nil {
-		return nil, err
+		return err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListIssueLinks 失败: %s", resp.Status)
+		return fmt.Errorf("ArchivePage 失败: %s", resp.Status)
 	}
-	return resp.Body, nil
+	return nil
 }
 
-func (s *PlaneService) CreateIssueLink(issueID, linkURL, title string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/", s.WorkspaceSlug, s.ProjectId, issueID)
-	payload := map[string]interface{}{"url": linkURL, "title": title}
-	resp, err := s.Client.Post(url, payload)
+func (s *PlaneService) UnarchivePage(workspaceSlug, projectId, pageID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/archive/", workspaceSlug, projectId, pageID)
+	resp, err := s.Client.Delete(url)
 	if err != nil {
-		return nil, err
+		return err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateIssueLink 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return fmt.Errorf("UnarchivePage 失败: %s", resp.Status)
 	}
-	return resp.Body, nil
+	return nil
 }
 
-func (s *PlaneService) UpdateIssueLink(issueID, linkID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/%s/", s.WorkspaceSlug, s.ProjectId, issueID, linkID)
-	resp, err := s.Client.Patch(url, data)
+// ============ Comment 方法 ============
+
+func (s *PlaneService) ListComments(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/", workspaceSlug, projectId, issueID)
+	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateIssueLink 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("ListComments 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteIssueLink(issueID, linkID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/%s/", s.WorkspaceSlug, s.ProjectId, issueID, linkID)
-	resp, err := s.Client.Delete(url)
+func (s *PlaneService) GetComment(workspaceSlug, projectId, issueID, commentID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/%s/", workspaceSlug, projectId, issueID, commentID)
+	resp, err := s.Client.Get(url)
 	if err != nil {
-		return err
+		return nil, err
 	}
-	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteIssueLink 失败: %s", resp.Status)
+	if !IsSuccess(resp.StatusCode) {
+		return nil, fmt.Errorf("GetComment 失败: %s", resp.Status)
 	}
-	return nil
+	return resp.Body, nil
 }
 
-// ============ Relation 便捷方法 ============
-
-func (s *PlaneService) ListIssueRelations(issueID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/relations/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Get(url)
+func (s *PlaneService) CreateComment(workspaceSlug, projectId, issueID, commentHTML string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/", workspaceSlug, projectId, issueID)
+	payload := map[string]interface{}{"comment_html": commentHTML}
+	resp, err := s.Client.Post(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListIssueRelations 失败: %s", resp.Status)
+		return nil, fmt.Errorf("CreateComment 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateIssueRelation(issueID, relationType string, relatedIDs []string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/relations/", s.WorkspaceSlug, s.ProjectId, issueID)
-	payload := map[string]interface{}{"relation_type": relationType, "related_issues": relatedIDs}
-	resp, err := s.Client.Post(url, payload)
+func (s *PlaneService) UpdateComment(workspaceSlug, projectId, issueID, commentID, commentHTML string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/%s/", workspaceSlug, projectId, issueID, commentID)
+	payload := map[string]interface{}{"comment_html": commentHTML}
+	resp, err := s.Client.Patch(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateIssueRelation 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateComment 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) RemoveIssueRelation(issueID, relationType, relatedID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/relations/%s/%s/", s.WorkspaceSlug, s.ProjectId, issueID, relationType, relatedID)
+func (s *PlaneService) DeleteComment(workspaceSlug, projectId, issueID, commentID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/comments/%s/", workspaceSlug, projectId, issueID, commentID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("RemoveIssueRelation 失败: %s", resp.Status)
+		return fmt.Errorf("DeleteComment 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-// ============ Sticky 便捷方法 ============
+// ============ Link 方法 ============
 
-func (s *PlaneService) ListStickies() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/stickies/", s.WorkspaceSlug)
+func (s *PlaneService) ListIssueLinks(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/", workspaceSlug, projectId, issueID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListStickies 失败: %s", resp.Status)
-	}
-	return resp.Body, nil
-}
-
-func (s *PlaneService) GetSticky(stickyID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/stickies/%s/", s.WorkspaceSlug, stickyID)
-	resp, err := s.Client.Get(url)
-	if err != nil {
-		return nil, err
-	}
-	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetSticky 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListIssueLinks 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) CreateSticky(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/stickies/", s.WorkspaceSlug)
-	resp, err := s.Client.Post(url, data)
+func (s *PlaneService) CreateIssueLink(workspaceSlug, projectId, issueID, linkURL, title string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/", workspaceSlug, projectId, issueID)
+	payload := map[string]interface{}{"url": linkURL, "title": title}
+	resp, err := s.Client.Post(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateSticky 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("CreateIssueLink 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateSticky(stickyID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/stickies/%s/", s.WorkspaceSlug, stickyID)
+func (s *PlaneService) UpdateIssueLink(workspaceSlug, projectId, issueID, linkID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/%s/", workspaceSlug, projectId, issueID, linkID)
 	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateSticky 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateIssueLink 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeleteSticky(stickyID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/stickies/%s/", s.WorkspaceSlug, stickyID)
+func (s *PlaneService) DeleteIssueLink(workspaceSlug, projectId, issueID, linkID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/links/%s/", workspaceSlug, projectId, issueID, linkID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteSticky 失败: %s", resp.Status)
+		return fmt.Errorf("DeleteIssueLink 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-// ============ Intake 补充方法 ============
+// ============ Relation 方法 ============
 
-func (s *PlaneService) UpdateIntakeIssue(issueID string, data map[string]any) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/%s/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Patch(url, data)
+func (s *PlaneService) ListIssueRelations(workspaceSlug, projectId, issueID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/relations/", workspaceSlug, projectId, issueID)
+	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateIntakeIssue 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListIssueRelations 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) AcceptIntakeIssue(issueID string) ([]byte, error) {
-	return s.UpdateIntakeIssue(issueID, map[string]any{"status": 1})
-}
-
-func (s *PlaneService) RejectIntakeIssue(issueID string) ([]byte, error) {
-	return s.UpdateIntakeIssue(issueID, map[string]any{"status": -1})
-}
-
-func (s *PlaneService) DeleteIntakeIssue(issueID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/intake-issues/%s/", s.WorkspaceSlug, s.ProjectId, issueID)
-	resp, err := s.Client.Delete(url)
-	if err != nil {
-		return err
-	}
-	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeleteIntakeIssue 失败: %s", resp.Status)
-	}
-	return nil
-}
-
-// ============ Asset 补充方法 ============
-
-func (s *PlaneService) CreateProjectAsset(fileName, mimeType string, size int64, entityType string) (*AssetUploadResult, error) {
-	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/", s.WorkspaceSlug, s.ProjectId)
-	payload := map[string]interface{}{
-		"name": fileName, "type": mimeType, "size": size,
-		"entity_type": entityType, "entity_identifier": "",
-	}
+func (s *PlaneService) CreateIssueRelation(workspaceSlug, projectId, issueID, relationType string, relatedIDs []string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/relations/", workspaceSlug, projectId, issueID)
+	payload := map[string]interface{}{"relation_type": relationType, "related_issues": relatedIDs}
 	resp, err := s.Client.Post(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreateProjectAsset 失败: %s", resp.Status)
-	}
-	var result AssetUploadResult
-	if err := json.Unmarshal(resp.Body, &result); err != nil {
-		return nil, fmt.Errorf("解析响应失败: %w", err)
+		return nil, fmt.Errorf("CreateIssueRelation 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
-	return &result, nil
+	return resp.Body, nil
 }
 
-func (s *PlaneService) ConfirmProjectAssetUpload(assetID string) error {
-	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", s.WorkspaceSlug, s.ProjectId, assetID)
-	resp, err := s.Client.Patch(url, map[string]interface{}{})
+func (s *PlaneService) RemoveIssueRelation(workspaceSlug, projectId, issueID, relationType, relatedID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/issues/%s/relations/%s/%s/", workspaceSlug, projectId, issueID, relationType, relatedID)
+	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
-	if resp.StatusCode != 204 && !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("ConfirmProjectAssetUpload 失败: %s", resp.Status)
+	if resp.StatusCode != 204 {
+		return fmt.Errorf("RemoveIssueRelation 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) GetProjectAsset(assetID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", s.WorkspaceSlug, s.ProjectId, assetID)
+// ============ Member 方法 ============
+
+func (s *PlaneService) ListWorkspaceMembers(workspaceSlug string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/members/", workspaceSlug)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
+	if !IsSuccess(resp.StatusCode) {
+		return nil, fmt.Errorf("ListWorkspaceMembers 失败: %s", resp.Status)
+	}
 	return resp.Body, nil
 }
 
-// ============ Page 补充方法 ============
-
-func (s *PlaneService) CreatePage(data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/", s.WorkspaceSlug, s.ProjectId)
-	resp, err := s.Client.Post(url, data)
+func (s *PlaneService) GetWorkspaceMember(workspaceSlug, memberID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/members/%s/", workspaceSlug, memberID)
+	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("CreatePage 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("GetWorkspaceMember 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdatePage(pageID string, data map[string]interface{}) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/", s.WorkspaceSlug, s.ProjectId, pageID)
-	resp, err := s.Client.Patch(url, data)
+func (s *PlaneService) UpdateWorkspaceMemberRole(workspaceSlug, memberID string, role int) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/members/%s/", workspaceSlug, memberID)
+	payload := map[string]interface{}{"role": role}
+	resp, err := s.Client.Patch(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdatePage 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateWorkspaceMemberRole 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) DeletePage(pageID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/", s.WorkspaceSlug, s.ProjectId, pageID)
+func (s *PlaneService) RemoveWorkspaceMember(workspaceSlug, memberID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/members/%s/", workspaceSlug, memberID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("DeletePage 失败: %s", resp.Status)
-	}
-	return nil
-}
-
-func (s *PlaneService) ArchivePage(pageID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/archive/", s.WorkspaceSlug, s.ProjectId, pageID)
-	resp, err := s.Client.Post(url, nil)
-	if err != nil {
-		return err
-	}
-	if !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("ArchivePage 失败: %s", resp.Status)
+		return fmt.Errorf("RemoveWorkspaceMember 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) UnarchivePage(pageID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/pages/%s/archive/", s.WorkspaceSlug, s.ProjectId, pageID)
-	resp, err := s.Client.Delete(url)
+func (s *PlaneService) ListProjectMembers(workspaceSlug, projectId string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/", workspaceSlug, projectId)
+	resp, err := s.Client.Get(url)
 	if err != nil {
-		return err
+		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return fmt.Errorf("UnarchivePage 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListProjectMembers 失败: %s", resp.Status)
 	}
-	return nil
+	return resp.Body, nil
 }
 
-// ============ Member 便捷方法 ============
-
-func (s *PlaneService) ListWorkspaceMembers() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/members/", s.WorkspaceSlug)
+func (s *PlaneService) GetProjectMember(workspaceSlug, projectId, memberID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/%s/", workspaceSlug, projectId, memberID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListWorkspaceMembers 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetProjectMember 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetWorkspaceMember(memberID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/members/%s/", s.WorkspaceSlug, memberID)
-	resp, err := s.Client.Get(url)
+func (s *PlaneService) AddProjectMember(workspaceSlug, projectId, memberID string, role int) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/", workspaceSlug, projectId)
+	payload := map[string]interface{}{"member": memberID, "role": role}
+	resp, err := s.Client.Post(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetWorkspaceMember 失败: %s", resp.Status)
+		return nil, fmt.Errorf("AddProjectMember 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateWorkspaceMemberRole(memberID string, role int) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/members/%s/", s.WorkspaceSlug, memberID)
+func (s *PlaneService) UpdateProjectMemberRole(workspaceSlug, projectId, memberID string, role int) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/%s/", workspaceSlug, projectId, memberID)
 	payload := map[string]interface{}{"role": role}
 	resp, err := s.Client.Patch(url, payload)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateWorkspaceMemberRole 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateProjectMemberRole 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) RemoveWorkspaceMember(memberID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/members/%s/", s.WorkspaceSlug, memberID)
+func (s *PlaneService) RemoveProjectMember(workspaceSlug, projectId, memberID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/%s/", workspaceSlug, projectId, memberID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("RemoveWorkspaceMember 失败: %s", resp.Status)
+		return fmt.Errorf("RemoveProjectMember 失败: %s", resp.Status)
 	}
 	return nil
 }
 
-func (s *PlaneService) ListProjectMembers() ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/", s.WorkspaceSlug, s.ProjectId)
+// ============ Sticky 方法 ============
+
+func (s *PlaneService) ListStickies(workspaceSlug string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/stickies/", workspaceSlug)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("ListProjectMembers 失败: %s", resp.Status)
+		return nil, fmt.Errorf("ListStickies 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) GetProjectMember(memberID string) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/%s/", s.WorkspaceSlug, s.ProjectId, memberID)
+func (s *PlaneService) GetSticky(workspaceSlug, stickyID string) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/stickies/%s/", workspaceSlug, stickyID)
 	resp, err := s.Client.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("GetProjectMember 失败: %s", resp.Status)
+		return nil, fmt.Errorf("GetSticky 失败: %s", resp.Status)
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) AddProjectMember(memberID string, role int) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/", s.WorkspaceSlug, s.ProjectId)
-	payload := map[string]interface{}{"member": memberID, "role": role}
-	resp, err := s.Client.Post(url, payload)
+func (s *PlaneService) CreateSticky(workspaceSlug string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/stickies/", workspaceSlug)
+	resp, err := s.Client.Post(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("AddProjectMember 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("CreateSticky 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) UpdateProjectMemberRole(memberID string, role int) ([]byte, error) {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/%s/", s.WorkspaceSlug, s.ProjectId, memberID)
-	payload := map[string]interface{}{"role": role}
-	resp, err := s.Client.Patch(url, payload)
+func (s *PlaneService) UpdateSticky(workspaceSlug, stickyID string, data map[string]interface{}) ([]byte, error) {
+	url := fmt.Sprintf("/api/workspaces/%s/stickies/%s/", workspaceSlug, stickyID)
+	resp, err := s.Client.Patch(url, data)
 	if err != nil {
 		return nil, err
 	}
 	if !IsSuccess(resp.StatusCode) {
-		return nil, fmt.Errorf("UpdateProjectMemberRole 失败: %s, body=%s", resp.Status, string(resp.Body))
+		return nil, fmt.Errorf("UpdateSticky 失败: %s, body=%s", resp.Status, string(resp.Body))
 	}
 	return resp.Body, nil
 }
 
-func (s *PlaneService) RemoveProjectMember(memberID string) error {
-	url := fmt.Sprintf("/api/workspaces/%s/projects/%s/members/%s/", s.WorkspaceSlug, s.ProjectId, memberID)
+func (s *PlaneService) DeleteSticky(workspaceSlug, stickyID string) error {
+	url := fmt.Sprintf("/api/workspaces/%s/stickies/%s/", workspaceSlug, stickyID)
 	resp, err := s.Client.Delete(url)
 	if err != nil {
 		return err
 	}
 	if resp.StatusCode != 204 {
-		return fmt.Errorf("RemoveProjectMember 失败: %s", resp.Status)
+		return fmt.Errorf("DeleteSticky 失败: %s", resp.Status)
 	}
 	return nil
-}
+}

+ 4 - 4
service/repositories/repositories.go

@@ -18,16 +18,16 @@ import (
 
 // ============ Plane V2 新架构(PlaneService)============
 
-// Plane 全局 PlaneService 实例(新架构入口) - 仅用于 fwh-toolbox 的 Feedback 模块
-var Plane *plane_v2.PlaneService
+// Plane Feedback 专用实例(绑定固定的 workspace/project/labels)
+var Plane *plane_v2.FeedbackPlaneService
 
 // InitPlane 初始化新架构 Plane(启动时调用) - 仅用于 fwh-toolbox 的 Feedback 模块
 func InitPlane(cfg *config.PlaneWebApiConfig) {
 	plane_v2.InitPlaneClient(cfg.Host, cfg.Email, cfg.Password)
-	Plane = plane_v2.NewPlaneService(cfg.WorkspaceSlug, cfg.ProjectId, cfg.LabelBugId, cfg.LabelFeatureId, cfg.LabelFeedbackId)
+	Plane = plane_v2.NewFeedbackPlaneService(cfg.WorkspaceSlug, cfg.ProjectId, cfg.LabelBugId, cfg.LabelFeatureId, cfg.LabelFeedbackId)
 }
 
-// InitPlane 初始化接口(给MCP Server使用)
+// 通用初始化接口(给MCP Server等外部模块使用)
 var InitPlaneClient = plane_v2.InitPlaneClient
 var NewPlaneService = plane_v2.NewPlaneService