From 3cf4841fddc3288589d3c5b92fbfb016f3d7a4f0 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Sat, 14 Feb 2026 13:07:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ugc=E5=86=85=E5=AE=B9=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/wechat/safety.go | 156 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 utils/wechat/safety.go diff --git a/utils/wechat/safety.go b/utils/wechat/safety.go new file mode 100644 index 0000000..07fe0f4 --- /dev/null +++ b/utils/wechat/safety.go @@ -0,0 +1,156 @@ +package wechat + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "sundynix-go/global" + + "go.uber.org/zap" +) + +// WeChatCommonResponse 微信通用响应 +type WeChatCommonResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +// MsgSecCheckDetail 文本检测结果详情 +type MsgSecCheckDetail struct { + Strategy string `json:"strategy"` + Errcode int `json:"errcode"` + Suggest string `json:"suggest"` + Label int `json:"label"` + Prob int `json:"prob"` +} + +// MsgSecCheckResult 文本检测结果 +type MsgSecCheckResult struct { + Suggest string `json:"suggest"` + Label int `json:"label"` +} + +// MsgSecCheckResponse 文本检测响应 +type MsgSecCheckResponse struct { + WeChatCommonResponse + Result MsgSecCheckResult `json:"result"` + Detail []MsgSecCheckDetail `json:"detail"` +} + +// MediaCheckAsyncResponse 媒体检测异步响应 +type MediaCheckAsyncResponse struct { + WeChatCommonResponse + TraceId string `json:"trace_id"` +} + +// MsgSecCheck 文本内容安全识别 +// content: 需检测的文本内容 +// openid: 用户的openid(用户需在近两小时访问过小程序) +// return: true-通过, false-不通过 +func MsgSecCheck(content string, openid string) bool { + // 2024-05-15: 微信api调整 + // https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/sec-center/sec-check/msgSecCheck.html + accessToken := GetMiniAccessToken() + url := "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=" + accessToken + + payload := map[string]interface{}{ + "version": 2, + "openid": openid, + "scene": 2, // 2-资料说明 + "content": content, + } + + jsonData, err := json.Marshal(payload) + if err != nil { + global.Logger.Error("MsgSecCheck json marshal error", zap.Error(err)) + return false + } + + resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + global.Logger.Error("MsgSecCheck http post error", zap.Error(err)) + return false + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + global.Logger.Error("MsgSecCheck read body error", zap.Error(err)) + return false + } + + var response MsgSecCheckResponse + if err := json.Unmarshal(body, &response); err != nil { + global.Logger.Error("MsgSecCheck json unmarshal error", zap.Error(err)) + return false + } + + if response.Errcode != 0 { + global.Logger.Error("MsgSecCheck api error", zap.Int("errcode", response.Errcode), zap.String("errmsg", response.Errmsg)) + return false + } + + // 检查 result.suggest + if response.Result.Suggest == "pass" { + return true + } + + global.Logger.Warn("MsgSecCheck risky content", + zap.String("content", content), + zap.String("suggest", response.Result.Suggest), + zap.Int("label", response.Result.Label), + ) + return false +} + +// MediaCheckAsync 多媒体内容安全识别(异步) +// mediaUrl: 需检测的多媒体url +// mediaType: 1:音频; 2:图片 +// openid: 用户的openid +// return: trace_id, error +func MediaCheckAsync(mediaUrl string, mediaType int, openid string) (string, error) { + accessToken := GetMiniAccessToken() + url := "https://api.weixin.qq.com/wxa/media_check_async?access_token=" + accessToken + + payload := map[string]interface{}{ + "media_url": mediaUrl, + "media_type": mediaType, + "version": 2, + "openid": openid, + "scene": 2, + } + + jsonData, err := json.Marshal(payload) + if err != nil { + return "", err + } + + resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + var response MediaCheckAsyncResponse + if err := json.Unmarshal(body, &response); err != nil { + return "", err + } + + if response.Errcode != 0 { + global.Logger.Error("MediaCheckAsync api error", zap.Int("errcode", response.Errcode), zap.String("errmsg", response.Errmsg)) + return "", fmt.Errorf("errcode: %d, errmsg: %s", response.Errcode, response.Errmsg) + } + + if response.TraceId != "" { + return response.TraceId, nil + } + + return "", fmt.Errorf("no trace_id returned") +}