package logic import ( "encoding/json" "fmt" "net/http" errCommon "micro-ayrshare/pkg/err" "micro-ayrshare/pkg/msg" "micro-ayrshare/internal/dto" "micro-ayrshare/pkg/app" "micro-ayrshare/pkg/utils" ) type IAryshareComment interface { PostComment(req *dto.PostCommentRequest) (res *dto.PostCommentResponse, err error) GetComment(req *dto.GetCommentRequest) (res *dto.GetCommentResponse, err error) DeleteComment(req *dto.DeleteCommentRequest) (res *dto.DeleteCommentResponse, err error) ReplyComment(req *dto.ReplyCommentRequest) (res *dto.ReplyCommentResponse, err error) } type AyrshareComment struct { } // PostComment 发布评论到社交媒体平台 func (a *AyrshareComment) PostComment(req *dto.PostCommentRequest) (res *dto.PostCommentResponse, err error) { errCommon.NoReturnInfo(req, "发布评论 参数信息: ") res = new(dto.PostCommentResponse) // 验证必填参数(这里只校验是否为空,平台合法性交由 Ayrshare 接口本身校验,方便支持更多平台) if req.ID == "" { return nil, errCommon.ReturnError(fmt.Errorf("id不能为空"), "id不能为空", "发布评论 失败: ") } if req.Comment == "" { return nil, errCommon.ReturnError(fmt.Errorf("comment不能为空"), "comment不能为空", "发布评论 失败: ") } if len(req.Platforms) == 0 { return nil, errCommon.ReturnError(fmt.Errorf("platforms不能为空"), "platforms不能为空", "发布评论 失败: ") } // 构建请求URL url := fmt.Sprintf("%s/api/comments", app.ModuleClients.AryshareClient.Config.Endpoint) // 准备请求体(排除 ProfileKey) reqBody := map[string]interface{}{ "id": req.ID, "comment": req.Comment, "platforms": req.Platforms, } if req.SearchPlatformId != nil { reqBody["searchPlatformId"] = *req.SearchPlatformId } if len(req.MediaUrls) > 0 { reqBody["mediaUrls"] = req.MediaUrls } reqBodyBytes, err := json.Marshal(reqBody) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONMarshal, "发布评论失败: ") } // 准备请求头 headers := make(map[string]string) headers["Authorization"] = fmt.Sprintf("Bearer %s", app.ModuleClients.AryshareClient.Config.ApiKey) if req.ProfileKey != "" { headers["Profile-Key"] = req.ProfileKey } // 调用 HTTP 方法 statusCode, result, err := utils.PostWithHeaders(url, reqBodyBytes, headers) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorPostCommentFailed, "发布评论失败: ") } // 解析响应 // 注意:API 可能返回数组格式 [{...}] 或对象格式 {...},需要兼容处理 var postCommentResp dto.PostCommentResponse // 先尝试判断响应是否为数组格式 var rawResponse interface{} if err := json.Unmarshal([]byte(result), &rawResponse); err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "发布评论失败: ") } // 如果是数组,取第一个元素 if arr, ok := rawResponse.([]interface{}); ok { if len(arr) == 0 { return nil, errCommon.ReturnError(fmt.Errorf("响应数组为空"), "发布评论失败:响应数组为空", "发布评论 失败: ") } // 将第一个元素转换为 JSON 字符串再解析 firstElementBytes, err := json.Marshal(arr[0]) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "发布评论失败: ") } if err := json.Unmarshal(firstElementBytes, &postCommentResp); err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "发布评论失败: ") } } else { // 如果是对象,直接解析 if err := json.Unmarshal([]byte(result), &postCommentResp); err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "发布评论失败: ") } } // 检查 HTTP 状态码 // 根据文档: // - 200: Success // - 404: Not Linked & Success (部分成功) // - 400: Duplicate comment if statusCode != http.StatusOK && statusCode != http.StatusNotFound { // 400 状态码可能是重复评论错误 if statusCode == http.StatusBadRequest { errMsg := postCommentResp.Message if errMsg == "" { errMsg = "发布评论失败:重复评论" } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "发布评论 失败: ") } return nil, errCommon.ReturnError( fmt.Errorf("接口返回状态码: %d, 响应结果: %s", statusCode, result), msg.ErrorPostCommentFailed, "发布评论 失败: ", ) } // 检查响应状态 // 根据文档,200/404 状态码可能返回 success 或 error // 注意:某些错误情况下,响应可能没有顶层 status 字段,错误信息在平台字段中(如 instagram.status = "error") if postCommentResp.Status == "error" { // 构建错误消息 errMsg := postCommentResp.Message if errMsg == "" { errMsg = msg.ErrorPostCommentFailed } if postCommentResp.Code != 0 { errMsg = fmt.Sprintf("错误代码: %d, %s", postCommentResp.Code, errMsg) } if len(postCommentResp.Errors) > 0 { errDetails := "" for _, e := range postCommentResp.Errors { if errDetails != "" { errDetails += "; " } errDetails += fmt.Sprintf("%s: %s", e.Platform, e.Message) } if errDetails != "" { errMsg += " (" + errDetails + ")" } } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "发布评论 失败: ") } // 检查平台字段中是否有错误信息(某些情况下错误信息在平台字段中,而不是顶层) // 例如:{"instagram": {"status": "error", "code": 215, "message": "..."}} if postCommentResp.Status == "" { // 检查各平台响应中是否有错误 platformErrors := make([]string, 0) if postCommentResp.Instagram != nil && postCommentResp.Instagram.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("Instagram: %s", postCommentResp.Instagram.Status)) } if postCommentResp.Facebook != nil && postCommentResp.Facebook.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("Facebook: %s", postCommentResp.Facebook.Status)) } if postCommentResp.Twitter != nil && postCommentResp.Twitter.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("Twitter: %s", postCommentResp.Twitter.Status)) } if postCommentResp.TikTok != nil && postCommentResp.TikTok.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("TikTok: %s", postCommentResp.TikTok.Status)) } if postCommentResp.LinkedIn != nil && postCommentResp.LinkedIn.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("LinkedIn: %s", postCommentResp.LinkedIn.Status)) } if postCommentResp.YouTube != nil && postCommentResp.YouTube.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("YouTube: %s", postCommentResp.YouTube.Status)) } if postCommentResp.Bluesky != nil && postCommentResp.Bluesky.Status == "error" { platformErrors = append(platformErrors, fmt.Sprintf("Bluesky: %s", postCommentResp.Bluesky.Status)) } // 如果发现平台错误,需要从原始响应中提取错误信息 if len(platformErrors) > 0 { // 尝试从原始响应中提取错误信息 var rawResponse interface{} if err := json.Unmarshal([]byte(result), &rawResponse); err == nil { var rawObj map[string]interface{} // 判断响应是数组还是对象 if arr, ok := rawResponse.([]interface{}); ok && len(arr) > 0 { // 如果是数组,取第一个元素 if firstObj, ok := arr[0].(map[string]interface{}); ok { rawObj = firstObj } } else if obj, ok := rawResponse.(map[string]interface{}); ok { // 如果是对象,直接使用 rawObj = obj } // 检查各平台字段中的错误信息 if rawObj != nil { for platform, value := range rawObj { if platformMap, ok := value.(map[string]interface{}); ok { if status, ok := platformMap["status"].(string); ok && status == "error" { errMsg := msg.ErrorPostCommentFailed if msg, ok := platformMap["message"].(string); ok && msg != "" { errMsg = msg } if code, ok := platformMap["code"].(float64); ok && code != 0 { errMsg = fmt.Sprintf("错误代码: %.0f, %s", code, errMsg) } return nil, errCommon.ReturnError(fmt.Errorf("%s平台: %s", platform, errMsg), errMsg, "发布评论 失败: ") } } } } } } } errCommon.NoReturnInfo(&postCommentResp, "发布评论 成功: ") res = &postCommentResp return } // GetComment 获取评论列表 func (a *AyrshareComment) GetComment(req *dto.GetCommentRequest) (res *dto.GetCommentResponse, err error) { errCommon.NoReturnInfo(req, "获取评论 参数信息: ") res = new(dto.GetCommentResponse) // 验证必填参数 if req.ID == "" { return nil, errCommon.ReturnError(fmt.Errorf("id不能为空"), "id不能为空", "获取评论 失败: ") } // 构建请求URL url := fmt.Sprintf("%s/api/comments/%s", app.ModuleClients.AryshareClient.Config.Endpoint, req.ID) // 准备请求头 headers := make(map[string]string) headers["Authorization"] = fmt.Sprintf("Bearer %s", app.ModuleClients.AryshareClient.Config.ApiKey) if req.ProfileKey != "" { headers["Profile-Key"] = req.ProfileKey } // 调用 HTTP 方法 statusCode, result, err := utils.GetWithHeaders(url, headers) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorGetCommentFailed, "获取评论失败: ") } // 解析响应 var getCommentResp dto.GetCommentResponse if err := json.Unmarshal([]byte(result), &getCommentResp); err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "获取评论失败: ") } // 检查响应状态 if getCommentResp.Status == "error" { // 构建错误消息 errMsg := getCommentResp.Message if errMsg == "" { errMsg = msg.ErrorGetCommentFailed } if getCommentResp.Code != 0 { errMsg = fmt.Sprintf("错误代码: %d, %s", getCommentResp.Code, errMsg) } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "获取评论 失败: ") } // 检查 HTTP 状态码 if statusCode != http.StatusOK { return nil, errCommon.ReturnError( fmt.Errorf("接口返回状态码: %d, 响应结果: %s", statusCode, result), msg.ErrorGetCommentFailed, "获取评论 失败: ", ) } errCommon.NoReturnInfo(&getCommentResp, "获取评论 成功: ") res = &getCommentResp return } // DeleteComment 删除评论 func (a *AyrshareComment) DeleteComment(req *dto.DeleteCommentRequest) (res *dto.DeleteCommentResponse, err error) { errCommon.NoReturnInfo(req, "删除评论 参数信息: ") res = new(dto.DeleteCommentResponse) // 验证必填参数(这里只校验是否为空,平台合法性交由 Ayrshare 接口本身校验,方便支持更多平台) if req.ID == "" { return nil, errCommon.ReturnError(fmt.Errorf("id不能为空"), "id不能为空", "删除评论 失败: ") } // 验证参数组合 // 如果使用 Social Comment ID,需要 searchPlatformId=true 和 platform // 如果使用 Ayrshare ID,需要 platforms if req.SearchPlatformId != nil && *req.SearchPlatformId { if req.Platform == "" { return nil, errCommon.ReturnError(fmt.Errorf("使用 Social Comment ID 时 platform 不能为空"), "使用 Social Comment ID 时 platform 不能为空", "删除评论 失败: ") } // 不在本地限制 platform 的具体取值,交由 Ayrshare 接口本身校验,便于支持更多平台 } else { if len(req.Platforms) == 0 { return nil, errCommon.ReturnError(fmt.Errorf("platforms不能为空"), "platforms不能为空", "删除评论 失败: ") } // 不在本地限制 platforms 的具体取值,交由 Ayrshare 接口本身校验,便于支持更多平台 } // 构建请求URL url := fmt.Sprintf("%s/api/comments/%s", app.ModuleClients.AryshareClient.Config.Endpoint, req.ID) // 准备请求体(排除 ProfileKey) var reqBody map[string]interface{} if req.SearchPlatformId != nil && *req.SearchPlatformId { // 使用 Social Comment ID reqBody = map[string]interface{}{ "searchPlatformId": true, "platform": req.Platform, } } else { // 使用 Ayrshare ID reqBody = map[string]interface{}{ "platforms": req.Platforms, } } reqBodyBytes, err := json.Marshal(reqBody) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONMarshal, "删除评论失败: ") } // 准备请求头 headers := make(map[string]string) headers["Authorization"] = fmt.Sprintf("Bearer %s", app.ModuleClients.AryshareClient.Config.ApiKey) if req.ProfileKey != "" { headers["Profile-Key"] = req.ProfileKey } // 调用 HTTP 方法 statusCode, result, err := utils.DeleteWithHeaders(url, reqBodyBytes, headers) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorDeleteCommentFailed, "删除评论失败: ") } // 解析响应 var deleteCommentResp dto.DeleteCommentResponse if err := json.Unmarshal([]byte(result), &deleteCommentResp); err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "删除评论失败: ") } // 检查 HTTP 状态码 // 根据文档: // - 200: Success (单条或多条) // - 400: Comment id not found if statusCode == http.StatusBadRequest { errMsg := deleteCommentResp.Message if errMsg == "" { errMsg = "删除评论失败:评论ID未找到" } if deleteCommentResp.Code != 0 { errMsg = fmt.Sprintf("错误代码: %d, %s", deleteCommentResp.Code, errMsg) } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "删除评论 失败: ") } if statusCode != http.StatusOK { return nil, errCommon.ReturnError( fmt.Errorf("接口返回状态码: %d, 响应结果: %s", statusCode, result), msg.ErrorDeleteCommentFailed, "删除评论 失败: ", ) } // 检查响应状态 if deleteCommentResp.Status == "error" { errMsg := deleteCommentResp.Message if errMsg == "" { errMsg = msg.ErrorDeleteCommentFailed } if deleteCommentResp.Code != 0 { errMsg = fmt.Sprintf("错误代码: %d, %s", deleteCommentResp.Code, errMsg) } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "删除评论 失败: ") } errCommon.NoReturnInfo(&deleteCommentResp, "删除评论 成功: ") res = &deleteCommentResp return } // ReplyComment 回复评论 func (a *AyrshareComment) ReplyComment(req *dto.ReplyCommentRequest) (res *dto.ReplyCommentResponse, err error) { errCommon.NoReturnInfo(req, "回复评论 参数信息: ") res = new(dto.ReplyCommentResponse) // 验证必填参数(这里只校验是否为空,平台合法性交由 Ayrshare 接口本身校验,方便支持更多平台) if req.ID == "" { return nil, errCommon.ReturnError(fmt.Errorf("id不能为空"), "id不能为空", "回复评论 失败: ") } if req.Comment == "" { return nil, errCommon.ReturnError(fmt.Errorf("comment不能为空"), "comment不能为空", "回复评论 失败: ") } if len(req.Platforms) == 0 { return nil, errCommon.ReturnError(fmt.Errorf("platforms不能为空"), "platforms不能为空", "回复评论 失败: ") } // 构建请求URL url := fmt.Sprintf("%s/api/comments/reply/%s", app.ModuleClients.AryshareClient.Config.Endpoint, req.ID) // 准备请求体(排除 ProfileKey) reqBody := map[string]interface{}{ "comment": req.Comment, "platforms": req.Platforms, } reqBodyBytes, err := json.Marshal(reqBody) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONMarshal, "回复评论失败: ") } // 准备请求头 headers := make(map[string]string) headers["Authorization"] = fmt.Sprintf("Bearer %s", app.ModuleClients.AryshareClient.Config.ApiKey) if req.ProfileKey != "" { headers["Profile-Key"] = req.ProfileKey } // 调用 HTTP 方法 statusCode, result, err := utils.PostWithHeaders(url, reqBodyBytes, headers) if err != nil { return nil, errCommon.ReturnError(err, msg.ErrorReplyCommentFailed, "回复评论失败: ") } // 解析响应 var replyCommentResp dto.ReplyCommentResponse if err := json.Unmarshal([]byte(result), &replyCommentResp); err != nil { return nil, errCommon.ReturnError(err, msg.ErrorJSONParse, "回复评论失败: ") } // 检查 HTTP 状态码 // 根据文档: // - 200: Success // - 400: Unknown commentId if statusCode == http.StatusBadRequest { errMsg := replyCommentResp.Message if errMsg == "" { errMsg = "回复评论失败:未知的评论ID" } if replyCommentResp.Code != 0 { errMsg = fmt.Sprintf("错误代码: %d, %s", replyCommentResp.Code, errMsg) } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "回复评论 失败: ") } if statusCode != http.StatusOK { return nil, errCommon.ReturnError( fmt.Errorf("接口返回状态码: %d, 响应结果: %s", statusCode, result), msg.ErrorReplyCommentFailed, "回复评论 失败: ", ) } // 检查响应状态 if replyCommentResp.Status == "error" { errMsg := replyCommentResp.Message if errMsg == "" { errMsg = msg.ErrorReplyCommentFailed } if replyCommentResp.Code != 0 { errMsg = fmt.Sprintf("错误代码: %d, %s", replyCommentResp.Code, errMsg) } return nil, errCommon.ReturnError(fmt.Errorf(errMsg), errMsg, "回复评论 失败: ") } errCommon.NoReturnInfo(&replyCommentResp, "回复评论 成功: ") res = &replyCommentResp return }