Files
cid/controller/yidun/yidun_callback_controller.go
2026-06-10 15:41:58 +08:00

341 lines
10 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package yidun
import (
dataengineService "cid/service/dataengine"
"context"
"fmt"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
// YidunCallbackController 易盾回调控制器
// 用于接收易盾检测结果的主动推送或手动轮询查询
type YidunCallbackController struct{}
// YidunCallback 易盾回调控制器单例
var YidunCallback = new(YidunCallbackController)
// CallbackResult 通用回调响应
type CallbackResult struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data,omitempty"`
}
// PollResult 轮询结果
type PollResult struct {
SuccessCount int `json:"success_count"`
FailCount int `json:"fail_count"`
PendingCount int `json:"pending_count"`
}
// =============================================================================
// 易盾主动推送模式回调接口
// 易盾会在检测完成后主动 POST 数据到这些接口
// =============================================================================
// ReceiveImageCallback 接收易盾图片检测结果推送
// 易盾回调格式: POST /yidun/callback/receiveImage
// Body: callbackData={"antispam":{...}}
func (c *YidunCallbackController) ReceiveImageCallback(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "yidun_callback"})
// 易盾推送的数据在请求体中
var callbackData string
// 尝试从表单数据获取
callbackData = r.GetForm("callbackData", "").String()
if callbackData == "" {
// 尝试从请求体JSON获取
var reqBody map[string]interface{}
if err := r.Parse(&reqBody); err == nil {
if v, ok := reqBody["callbackData"]; ok {
callbackData = toString(v)
}
}
}
// 尝试直接从请求体获取原始数据
if callbackData == "" {
callbackData = string(r.GetBody())
}
if callbackData == "" {
g.Log().Warningf(ctx, "图片回调数据为空")
r.Response.WriteJson(CallbackResult{Code: 400, Msg: "callbackData不能为空"})
return
}
g.Log().Infof(ctx, "收到易盾图片回调, data长度: %d", len(callbackData))
// 处理回调 - 更新 material_verify_log 和 tencent_image 表
err := dataengineService.MaterialVerify.ProcessImageCallback(ctx, callbackData)
if err != nil {
g.Log().Errorf(ctx, "处理易盾图片回调失败: %v", err)
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(CallbackResult{Code: 200, Msg: "success"})
}
// ReceiveVideoCallback 接收易盾视频检测结果推送
// 易盾回调格式: POST /yidun/callback/receiveVideo
// Body: callbackData={"antispam":{...}}
func (c *YidunCallbackController) ReceiveVideoCallback(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "yidun_callback"})
// 易盾推送的数据在请求体中
var callbackData string
// 尝试从表单数据获取
callbackData = r.GetForm("callbackData", "").String()
if callbackData == "" {
// 尝试从请求体JSON获取
var reqBody map[string]interface{}
if err := r.Parse(&reqBody); err == nil {
if v, ok := reqBody["callbackData"]; ok {
callbackData = toString(v)
}
}
}
// 尝试直接从请求体获取原始数据
if callbackData == "" {
callbackData = string(r.GetBody())
}
if callbackData == "" {
g.Log().Warningf(ctx, "视频回调数据为空")
r.Response.WriteJson(CallbackResult{Code: 400, Msg: "callbackData不能为空"})
return
}
g.Log().Infof(ctx, "收到易盾视频回调, data长度: %d", len(callbackData))
// 处理回调 - 更新 material_verify_log 和 tencent_video 表
err := dataengineService.MaterialVerify.ProcessVideoCallback(ctx, callbackData)
if err != nil {
g.Log().Errorf(ctx, "处理易盾视频回调失败: %v", err)
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(CallbackResult{Code: 200, Msg: "success"})
}
// =============================================================================
// 轮询模式 - 手动查询检测结果
// =============================================================================
// PollAllResults 轮询所有待查询的检测结果(图片+视频)
// 格式: POST /yidun/callback/poll
func (c *YidunCallbackController) PollAllResults(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
g.Log().Info(ctx, "开始轮询所有待查询的检测结果...")
// 先获取待处理数量
pendingCount, _ := dataengineService.MaterialVerify.GetPendingResultsCount(ctx)
// 执行轮询
successCount, failCount, err := dataengineService.MaterialVerify.PollPendingResults(ctx)
result := PollResult{
SuccessCount: successCount,
FailCount: failCount,
PendingCount: pendingCount - successCount,
}
if err != nil {
r.Response.WriteJson(CallbackResult{
Code: 500,
Msg: fmt.Sprintf("轮询完成但有错误: %v", err),
Data: result,
})
return
}
r.Response.WriteJson(CallbackResult{
Code: 200,
Msg: fmt.Sprintf("轮询完成,成功处理 %d 条,失败 %d 条", successCount, failCount),
Data: result,
})
}
// PollImageResults 轮询图片待查询的检测结果
// 格式: POST /yidun/callback/pollImage
func (c *YidunCallbackController) PollImageResults(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
g.Log().Info(ctx, "开始轮询图片待查询的检测结果...")
successCount, failCount, err := dataengineService.MaterialVerify.PollPendingImageResults(ctx)
if err != nil {
r.Response.WriteJson(CallbackResult{
Code: 500,
Msg: fmt.Sprintf("轮询失败: %v", err),
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
})
return
}
r.Response.WriteJson(CallbackResult{
Code: 200,
Msg: fmt.Sprintf("轮询完成,成功处理 %d 条,失败 %d 条", successCount, failCount),
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
})
}
// PollVideoResults 轮询视频待查询的检测结果
// 格式: POST /yidun/callback/pollVideo
func (c *YidunCallbackController) PollVideoResults(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
g.Log().Info(ctx, "开始轮询视频待查询的检测结果...")
successCount, failCount, err := dataengineService.MaterialVerify.PollPendingVideoResults(ctx)
if err != nil {
r.Response.WriteJson(CallbackResult{
Code: 500,
Msg: fmt.Sprintf("轮询失败: %v", err),
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
})
return
}
r.Response.WriteJson(CallbackResult{
Code: 200,
Msg: fmt.Sprintf("轮询完成,成功处理 %d 条,失败 %d 条", successCount, failCount),
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
})
}
// PollByTaskID 根据任务ID查询单个检测结果
// 格式: POST /yidun/callback/pollTask
func (c *YidunCallbackController) PollByTaskID(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
taskID := r.Get("taskId", "").String()
taskType := r.Get("type", "").String() // image 或 video
if taskID == "" {
r.Response.WriteJson(CallbackResult{Code: 400, Msg: "taskId不能为空"})
return
}
g.Log().Infof(ctx, "查询单个检测结果, taskId=%s, type=%s", taskID, taskType)
var err error
if taskType == "video" || taskType == "" {
// 尝试视频
err = dataengineService.MaterialVerify.ProcessVideoResultByTask(ctx, taskID)
if err != nil {
// 如果失败且没有指定类型,尝试图片
if taskType == "" {
err = dataengineService.MaterialVerify.ProcessImageResultByTask(ctx, taskID)
}
}
} else if taskType == "image" {
err = dataengineService.MaterialVerify.ProcessImageResultByTask(ctx, taskID)
}
if err != nil {
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(CallbackResult{Code: 200, Msg: "查询并处理成功"})
}
// GetPendingCount 获取待查询结果的数量
// 格式: GET /yidun/callback/pendingCount
func (c *YidunCallbackController) GetPendingCount(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
count, err := dataengineService.MaterialVerify.GetPendingResultsCount(ctx)
if err != nil {
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(g.Map{
"code": 200,
"data": g.Map{
"pending_count": count,
"description": "待查询结果的日志数量状态为pending且有taskID",
},
})
}
// =============================================================================
// 兼容旧接口(手动触发回调处理)
// =============================================================================
// ProcessImageCallback 手动处理图片回调(兼容旧接口)
// 格式: POST /yidun/callback/processImage
func (c *YidunCallbackController) ProcessImageCallback(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
var req struct {
CallbackData string `json:"callbackData" v:"required#回调数据不能为空"`
}
if err := r.Parse(&req); err != nil {
r.Response.WriteJson(CallbackResult{Code: 400, Msg: err.Error()})
return
}
err := dataengineService.MaterialVerify.ProcessImageCallback(ctx, req.CallbackData)
if err != nil {
g.Log().Errorf(ctx, "处理图片回调失败: %v", err)
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(CallbackResult{Code: 200, Msg: "success"})
}
// ProcessVideoCallback 手动处理视频回调(兼容旧接口)
// 格式: POST /yidun/callback/processVideo
func (c *YidunCallbackController) ProcessVideoCallback(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
var req struct {
CallbackData string `json:"callbackData" v:"required#回调数据不能为空"`
}
if err := r.Parse(&req); err != nil {
r.Response.WriteJson(CallbackResult{Code: 400, Msg: err.Error()})
return
}
err := dataengineService.MaterialVerify.ProcessVideoCallback(ctx, req.CallbackData)
if err != nil {
g.Log().Errorf(ctx, "处理视频回调失败: %v", err)
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(CallbackResult{Code: 200, Msg: "success"})
}
// toString 转换interface{}为string
func toString(v interface{}) string {
if s, ok := v.(string); ok {
return s
}
return ""
}