package plane_v2_api import ( "encoding/json" "fmt" plane "fwh-toolbox-server/repositories/api/plane_v2" ) // CreateProjectAsset 创建项目资源(获取预签名 URL) func CreateProjectAsset(c *plane.PlaneClient, slug, projectID, fileName, mimeType string, size int64, entityType string) (*plane.AssetUploadResult, error) { url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/", slug, projectID) payload := map[string]interface{}{ "name": fileName, "type": mimeType, "size": size, "entity_type": entityType, "entity_identifier": "", } resp, err := c.Post(url, payload) if err != nil { return nil, err } if !plane.IsSuccess(resp.StatusCode) { return nil, fmt.Errorf("CreateProjectAsset 失败: %s", resp.Status) } var result plane.AssetUploadResult if err := json.Unmarshal(resp.Body, &result); err != nil { return nil, fmt.Errorf("解析响应失败: %w", err) } return &result, nil } // ConfirmProjectAssetUpload 确认项目资源上传完成 func ConfirmProjectAssetUpload(c *plane.PlaneClient, slug, projectID, assetID string) error { url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", slug, projectID, assetID) resp, err := c.Patch(url, map[string]interface{}{}) if err != nil { return err } if resp.StatusCode != 204 && !plane.IsSuccess(resp.StatusCode) { return fmt.Errorf("ConfirmProjectAssetUpload 失败: %s", resp.Status) } return nil } // GetProjectAsset 获取项目资源 func GetProjectAsset(c *plane.PlaneClient, slug, projectID, assetID string) ([]byte, error) { url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", slug, projectID, assetID) resp, err := c.Get(url) if err != nil { return nil, err } return resp.Body, nil } // GetProjectAssetURL 获取项目资源的预签名访问 URL func GetProjectAssetURL(c *plane.PlaneClient, slug, projectID, assetID string) (string, error) { url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", slug, projectID, assetID) return c.GetRedirectLocation(url) } // DeleteProjectAsset 删除项目资源 func DeleteProjectAsset(c *plane.PlaneClient, slug, projectID, assetID string) error { url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/", slug, projectID, assetID) resp, err := c.Delete(url) if err != nil { return err } if resp.StatusCode != 204 { return fmt.Errorf("DeleteProjectAsset 失败: %s", resp.Status) } return nil } // BindAssetsToEntity 批量绑定资源到实体 func BindAssetsToEntity(c *plane.PlaneClient, slug, projectID, entityID string, assetIDs []string) error { url := fmt.Sprintf("/api/assets/v2/workspaces/%s/projects/%s/%s/bulk/", slug, projectID, entityID) payload := map[string]interface{}{ "asset_ids": assetIDs, } resp, err := c.Post(url, payload) if err != nil { return err } if resp.StatusCode != 204 && !plane.IsSuccess(resp.StatusCode) { return fmt.Errorf("BindAssetsToEntity 失败: %s", resp.Status) } return nil } // UploadToS3 上传文件到 S3(使用预签名 POST) func UploadToS3(c *plane.PlaneClient, uploadURL string, fields map[string]string, fileData []byte, fileName string) error { return c.PostS3(uploadURL, fields, fileData, fileName) } // UploadProjectAsset 完整上传项目资源流程:创建 → S3 上传 → 确认 func UploadProjectAsset(c *plane.PlaneClient, slug, projectID, fileName, mimeType string, fileData []byte, entityType string) (*plane.AssetUploadResult, error) { // Step 1: 创建资源 result, err := CreateProjectAsset(c, slug, projectID, fileName, mimeType, int64(len(fileData)), entityType) if err != nil { return nil, fmt.Errorf("创建资源失败: %w", err) } // Step 2: 上传到 S3 if err := UploadToS3(c, result.UploadData.URL, result.UploadData.Fields, fileData, fileName); err != nil { return nil, fmt.Errorf("上传文件到S3失败: %w", err) } // Step 3: 确认上传完成 if err := ConfirmProjectAssetUpload(c, slug, projectID, result.AssetID); err != nil { return nil, fmt.Errorf("确认上传失败: %w", err) } return result, nil }