yidun送检功能
This commit is contained in:
175
controller/yidun/content_check_controller.go
Normal file
175
controller/yidun/content_check_controller.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package yidun
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/yidun"
|
||||
serviceDataengine "cid/service/dataengine"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ContentCheckController 内容送检控制器
|
||||
type ContentCheckController struct{}
|
||||
|
||||
// ContentCheck 内容送检控制器单例
|
||||
var ContentCheck = new(ContentCheckController)
|
||||
|
||||
// StatusRes 状态响应
|
||||
type StatusRes struct {
|
||||
Running bool `json:"running"`
|
||||
Config serviceDataengine.ContentCheckConfig `json:"config"`
|
||||
PendingStats map[string]int `json:"pending_stats"`
|
||||
}
|
||||
|
||||
// Start 启动送检服务
|
||||
func (c *ContentCheckController) Start(ctx context.Context, req *dto.StartCheckReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
if serviceDataengine.TencentContentCheck.IsRunning() {
|
||||
return &beans.ResponseEmpty{}, nil
|
||||
}
|
||||
|
||||
// 如果有配置参数,更新配置
|
||||
if req.BatchSize > 0 || req.IntervalSeconds > 0 {
|
||||
config := serviceDataengine.ContentCheckConfig{
|
||||
BatchSize: req.BatchSize,
|
||||
ImageEnabled: req.ImageEnabled,
|
||||
VideoEnabled: req.VideoEnabled,
|
||||
IntervalSeconds: req.IntervalSeconds,
|
||||
}
|
||||
serviceDataengine.TencentContentCheck.SetConfig(config)
|
||||
}
|
||||
|
||||
err = serviceDataengine.TencentContentCheck.Start(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &beans.ResponseEmpty{}, nil
|
||||
}
|
||||
|
||||
// Stop 停止送检服务
|
||||
func (c *ContentCheckController) Stop(ctx context.Context, req *dto.EmptyReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
serviceDataengine.TencentContentCheck.Stop(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// Status 获取送检服务状态
|
||||
func (c *ContentCheckController) Status(ctx context.Context, req *dto.EmptyReq) (res *StatusRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
res = &StatusRes{
|
||||
Running: serviceDataengine.TencentContentCheck.IsRunning(),
|
||||
Config: serviceDataengine.TencentContentCheck.GetConfig(),
|
||||
PendingStats: serviceDataengine.TencentContentCheck.GetPendingStats(ctx),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessImageCallback 处理图片检测回调
|
||||
func (c *ContentCheckController) ProcessImageCallback(ctx context.Context, req *dto.ProcessImageCallbackReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
if req.CallbackData == "" {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = serviceDataengine.TencentContentCallback.ProcessImageCallback(ctx, req.CallbackData)
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessVideoCallback 处理视频检测回调
|
||||
func (c *ContentCheckController) ProcessVideoCallback(ctx context.Context, req *dto.ProcessVideoCallbackReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
if req.CallbackData == "" {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = serviceDataengine.TencentContentCallback.ProcessVideoCallback(ctx, req.CallbackData)
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessImageResult 查询并处理图片检测结果(轮询模式)
|
||||
func (c *ContentCheckController) ProcessImageResult(ctx context.Context, req *dto.ProcessImageResultReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
if req.TaskID == "" {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = serviceDataengine.TencentContentCallback.ProcessImageResult(ctx, req.TaskID)
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessVideoResult 查询并处理视频检测结果(轮询模式)
|
||||
func (c *ContentCheckController) ProcessVideoResult(ctx context.Context, req *dto.ProcessVideoResultReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
if req.TaskID == "" {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = serviceDataengine.TencentContentCallback.ProcessVideoResult(ctx, req.TaskID)
|
||||
return
|
||||
}
|
||||
|
||||
// ManualSubmitImageByID 根据图片ID手动提交送检
|
||||
func (c *ContentCheckController) ManualSubmitImageByID(ctx context.Context, req *dto.ManualSubmitImageByIDReq) (res *dto.ManualSubmitRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
result, err := serviceDataengine.TencentContentCheck.SubmitImageByID(ctx, req.ImageID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.ManualSubmitRes{
|
||||
TaskID: result.TaskID,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ManualSubmitVideoByID 根据视频ID手动提交送检
|
||||
func (c *ContentCheckController) ManualSubmitVideoByID(ctx context.Context, req *dto.ManualSubmitVideoByIDReq) (res *dto.ManualSubmitRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
result, err := serviceDataengine.TencentContentCheck.SubmitVideoByID(ctx, req.VideoID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.ManualSubmitRes{
|
||||
TaskID: result.TaskID,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetImageCheckLogs 获取图片的送检日志
|
||||
func (c *ContentCheckController) GetImageCheckLogs(ctx context.Context, req *dto.GetImageCheckLogsReq) (res *dto.GetCheckLogsRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
logs, err := serviceDataengine.TencentContentCallback.GetCheckLogsByImageID(ctx, req.ImageID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.GetCheckLogsRes{
|
||||
List: logs,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetVideoCheckLogs 获取视频的送检日志
|
||||
func (c *ContentCheckController) GetVideoCheckLogs(ctx context.Context, req *dto.GetVideoCheckLogsReq) (res *dto.GetCheckLogsRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
|
||||
logs, err := serviceDataengine.TencentContentCallback.GetCheckLogsByVideoID(ctx, req.VideoID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.GetCheckLogsRes{
|
||||
List: logs,
|
||||
}
|
||||
return
|
||||
}
|
||||
340
controller/yidun/yidun_callback_controller.go
Normal file
340
controller/yidun/yidun_callback_controller.go
Normal file
@@ -0,0 +1,340 @@
|
||||
package yidun
|
||||
|
||||
import (
|
||||
dataengineService "cid/service/dataengine"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.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 ""
|
||||
}
|
||||
Reference in New Issue
Block a user