607 lines
18 KiB
Go
607 lines
18 KiB
Go
package dataengine
|
||
|
||
import (
|
||
consts "cid/consts/dataengine"
|
||
dao "cid/dao/dataengine"
|
||
entity "cid/model/entity/dataengine"
|
||
serviceDataengine "cid/service/dataengine"
|
||
"context"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
)
|
||
|
||
// MaterialVerifyController 素材校验控制器
|
||
type MaterialVerifyController struct{}
|
||
|
||
// MaterialVerify 控制器单例
|
||
var MaterialVerify = new(MaterialVerifyController)
|
||
|
||
// =============================================================================
|
||
// 请求/响应结构体
|
||
// =============================================================================
|
||
|
||
// ImageListReq 图片列表请求
|
||
type ImageListReq struct {
|
||
Status string `json:"status"`
|
||
AccountID int64 `json:"accountId"`
|
||
Page int `json:"page"`
|
||
PageSize int `json:"pageSize"`
|
||
StartTime int64 `json:"startTime"`
|
||
EndTime int64 `json:"endTime"`
|
||
}
|
||
|
||
// ImageListRes 图片列表响应
|
||
type ImageListRes struct {
|
||
List interface{} `json:"list"`
|
||
Total int `json:"total"`
|
||
}
|
||
|
||
// StatsRes 统计响应
|
||
type StatsRes struct {
|
||
Pending int `json:"pending"`
|
||
Verified int `json:"verified"`
|
||
Rejected int `json:"rejected"`
|
||
}
|
||
|
||
// VideoListReq 视频列表请求
|
||
type VideoListReq struct {
|
||
Status string `json:"status"`
|
||
AccountID int64 `json:"accountId"`
|
||
Page int `json:"page"`
|
||
PageSize int `json:"pageSize"`
|
||
StartTime int64 `json:"startTime"`
|
||
EndTime int64 `json:"endTime"`
|
||
}
|
||
|
||
// VideoListRes 视频列表响应
|
||
type VideoListRes struct {
|
||
List interface{} `json:"list"`
|
||
Total int `json:"total"`
|
||
}
|
||
|
||
// LogListReq 日志列表请求
|
||
type LogListReq struct {
|
||
MaterialType string `json:"materialType"`
|
||
MaterialID string `json:"materialId"`
|
||
VerifyStatus string `json:"verifyStatus"`
|
||
AccountID int64 `json:"accountId"`
|
||
Page int `json:"page"`
|
||
PageSize int `json:"pageSize"`
|
||
StartTime int64 `json:"startTime"`
|
||
EndTime int64 `json:"endTime"`
|
||
}
|
||
|
||
// ManualVerifyReq 手动校验请求
|
||
type ManualVerifyReq struct {
|
||
MaterialID string `json:"materialId" v:"required#素材ID不能为空"`
|
||
}
|
||
|
||
// TaskIDReq 任务ID请求
|
||
type TaskIDReq struct {
|
||
TaskID string `json:"taskId" v:"required#任务ID不能为空"`
|
||
}
|
||
|
||
// ImageCallbackReq 图片回调请求
|
||
type ImageCallbackReq struct {
|
||
CallbackData string `json:"callbackData"`
|
||
}
|
||
|
||
// VideoCallbackReq 视频回调请求
|
||
type VideoCallbackReq struct {
|
||
CallbackData string `json:"callbackData"`
|
||
}
|
||
|
||
// BatchVerifyReq 批量校验请求
|
||
type BatchVerifyReq struct {
|
||
Limit int `json:"limit"`
|
||
}
|
||
|
||
// =============================================================================
|
||
// 图片素材接口
|
||
// =============================================================================
|
||
|
||
// ListImage 图片素材列表
|
||
func (c *MaterialVerifyController) ListImage(ctx context.Context, req *ImageListReq) (res *ImageListRes, err error) {
|
||
if req.Page == 0 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize == 0 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
condition := make(map[string]interface{})
|
||
if req.Status != "" {
|
||
condition[entity.TencentImageCols.VerifyStatus] = req.Status
|
||
}
|
||
if req.AccountID > 0 {
|
||
condition[entity.TencentImageCols.AccountID] = req.AccountID
|
||
}
|
||
|
||
data, total, err := dao.TencentImage.GetByCondition(ctx, condition, req.Page, req.PageSize)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &ImageListRes{
|
||
List: data,
|
||
Total: total,
|
||
}, nil
|
||
}
|
||
|
||
// StatsImage 图片素材统计
|
||
func (c *MaterialVerifyController) StatsImage(ctx context.Context, req *ImageListReq) (res *StatsRes, err error) {
|
||
// 使用实体中定义的正确状态值:PENDING=待校验, VERIFIED=校验通过, REJECTED=校验不通过
|
||
pending, _ := dao.TencentImage.CountByStatus(ctx, entity.VerifyStatusPending)
|
||
verified, _ := dao.TencentImage.CountByStatus(ctx, entity.VerifyStatusVerified)
|
||
rejected, _ := dao.TencentImage.CountByStatus(ctx, entity.VerifyStatusRejected)
|
||
|
||
return &StatsRes{
|
||
Pending: pending,
|
||
Verified: verified,
|
||
Rejected: rejected,
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 视频素材接口
|
||
// =============================================================================
|
||
|
||
// ListVideo 视频素材列表
|
||
func (c *MaterialVerifyController) ListVideo(ctx context.Context, req *VideoListReq) (res *VideoListRes, err error) {
|
||
if req.Page == 0 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize == 0 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
condition := make(map[string]interface{})
|
||
if req.Status != "" {
|
||
condition[entity.TencentVideoCols.VerifyStatus] = req.Status
|
||
}
|
||
if req.AccountID > 0 {
|
||
condition[entity.TencentVideoCols.AccountID] = req.AccountID
|
||
}
|
||
|
||
data, total, err := dao.TencentVideo.GetByCondition(ctx, condition, req.Page, req.PageSize)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &VideoListRes{
|
||
List: data,
|
||
Total: total,
|
||
}, nil
|
||
}
|
||
|
||
// StatsVideo 视频素材统计
|
||
func (c *MaterialVerifyController) StatsVideo(ctx context.Context, req *VideoListReq) (res *StatsRes, err error) {
|
||
// 使用实体中定义的正确状态值:PENDING=待校验, VERIFIED=校验通过, REJECTED=校验不通过
|
||
pending, _ := dao.TencentVideo.CountByStatus(ctx, entity.VerifyStatusPending)
|
||
verified, _ := dao.TencentVideo.CountByStatus(ctx, entity.VerifyStatusVerified)
|
||
rejected, _ := dao.TencentVideo.CountByStatus(ctx, entity.VerifyStatusRejected)
|
||
|
||
return &StatsRes{
|
||
Pending: pending,
|
||
Verified: verified,
|
||
Rejected: rejected,
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 校验日志接口
|
||
// =============================================================================
|
||
|
||
// ListLogRes 日志列表响应
|
||
type ListLogRes struct {
|
||
List interface{} `json:"list"`
|
||
Total int `json:"total"`
|
||
}
|
||
|
||
// ListLog 日志列表
|
||
func (c *MaterialVerifyController) ListLog(ctx context.Context, req *LogListReq) (res *ListLogRes, err error) {
|
||
if req.Page == 0 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize == 0 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
condition := make(map[string]interface{})
|
||
if req.MaterialType != "" {
|
||
condition[entity.MaterialVerifyLogCols.MaterialType] = req.MaterialType
|
||
}
|
||
if req.MaterialID != "" {
|
||
condition[entity.MaterialVerifyLogCols.MaterialID] = req.MaterialID
|
||
}
|
||
if req.VerifyStatus != "" {
|
||
condition[entity.MaterialVerifyLogCols.VerifyStatus] = req.VerifyStatus
|
||
}
|
||
if req.AccountID > 0 {
|
||
condition[entity.MaterialVerifyLogCols.AccountID] = req.AccountID
|
||
}
|
||
|
||
data, total, err := serviceDataengine.MaterialVerify.GetLogsByCondition(ctx, condition, req.Page, req.PageSize)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &ListLogRes{
|
||
List: data,
|
||
Total: total,
|
||
}, nil
|
||
}
|
||
|
||
// LogDetailRes 日志详情响应
|
||
type LogDetailRes struct {
|
||
*entity.MaterialVerifyLog
|
||
PreviewURL string `json:"previewURL"`
|
||
}
|
||
|
||
// GetLogDetailReq 日志详情请求
|
||
type GetLogDetailReq struct {
|
||
Id int64 `json:"id" v:"required#日志ID不能为空"`
|
||
}
|
||
|
||
// GetLogDetail 日志详情
|
||
func (c *MaterialVerifyController) GetLogDetail(ctx context.Context, req *GetLogDetailReq) (res *LogDetailRes, err error) {
|
||
log, err := serviceDataengine.MaterialVerify.GetLogByID(ctx, req.Id)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if log == nil {
|
||
return nil, fmt.Errorf("日志不存在")
|
||
}
|
||
|
||
// 获取来源数据预览
|
||
res = &LogDetailRes{
|
||
MaterialVerifyLog: log,
|
||
}
|
||
if log.SourceTable == consts.SourceTableTencentImage {
|
||
image, _ := dao.TencentImage.GetByID(ctx, log.SourceID)
|
||
if image != nil {
|
||
res.PreviewURL = image.PreviewURL
|
||
}
|
||
} else if log.SourceTable == consts.SourceTableTencentVideo {
|
||
video, _ := dao.TencentVideo.GetByID(ctx, log.SourceID)
|
||
if video != nil {
|
||
res.PreviewURL = video.PreviewURL
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// StatsLogRes 日志统计响应
|
||
type StatsLogRes struct {
|
||
Total int `json:"total"`
|
||
Pending int `json:"pending"`
|
||
Verified int `json:"verified"`
|
||
Rejected int `json:"rejected"`
|
||
}
|
||
|
||
// StatsLog 日志统计
|
||
func (c *MaterialVerifyController) StatsLog(ctx context.Context, req *LogListReq) (res *StatsLogRes, err error) {
|
||
stats, err := serviceDataengine.MaterialVerify.GetStats(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &StatsLogRes{
|
||
Total: stats["total"],
|
||
Pending: stats["pending"],
|
||
Verified: stats["verified"],
|
||
Rejected: stats["rejected"],
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 手动校验接口
|
||
// =============================================================================
|
||
|
||
// ManualVerifyImageRes 手动校验响应
|
||
type ManualVerifyImageRes struct {
|
||
Id int64 `json:"id"`
|
||
TaskID string `json:"taskId"`
|
||
SourceID string `json:"sourceId"`
|
||
}
|
||
|
||
// ManualVerifyImage 手动校验图片
|
||
func (c *MaterialVerifyController) ManualVerifyImage(ctx context.Context, req *ManualVerifyReq) (res *ManualVerifyImageRes, err error) {
|
||
log, err := serviceDataengine.MaterialVerify.VerifyImageByID(ctx, req.MaterialID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &ManualVerifyImageRes{
|
||
Id: log.Id,
|
||
TaskID: log.TaskID,
|
||
SourceID: fmt.Sprintf("%d", log.SourceID),
|
||
}, nil
|
||
}
|
||
|
||
// ManualVerifyVideo 手动校验视频
|
||
func (c *MaterialVerifyController) ManualVerifyVideo(ctx context.Context, req *ManualVerifyReq) (res *ManualVerifyImageRes, err error) {
|
||
log, err := serviceDataengine.MaterialVerify.VerifyVideoByID(ctx, req.MaterialID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &ManualVerifyImageRes{
|
||
Id: log.Id,
|
||
TaskID: log.TaskID,
|
||
SourceID: fmt.Sprintf("%d", log.SourceID),
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 批量校验接口
|
||
// =============================================================================
|
||
|
||
// BatchVerifyRes 批量校验响应
|
||
type BatchVerifyRes struct {
|
||
Success int `json:"success"`
|
||
Fail int `json:"fail"`
|
||
Total int `json:"total"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// BatchVerifyImage 批量校验图片
|
||
func (c *MaterialVerifyController) BatchVerifyImage(ctx context.Context, req *BatchVerifyReq) (res *BatchVerifyRes, err error) {
|
||
if req.Limit <= 0 {
|
||
req.Limit = 100
|
||
}
|
||
|
||
images, err := dao.TencentImage.GetPendingList(ctx, req.Limit)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
successCount := 0
|
||
failCount := 0
|
||
|
||
for _, image := range images {
|
||
log, err := serviceDataengine.MaterialVerify.VerifyImageByID(ctx, image.ImageID)
|
||
if err != nil {
|
||
failCount++
|
||
g.Log().Errorf(ctx, "图片校验失败: %s, error: %v", image.ImageID, err)
|
||
} else {
|
||
successCount++
|
||
g.Log().Infof(ctx, "图片校验已提交: %s, logId: %d", image.ImageID, log.Id)
|
||
}
|
||
time.Sleep(100 * time.Millisecond)
|
||
}
|
||
|
||
// 等待易盾处理,然后自动查询结果
|
||
msg := fmt.Sprintf("批量校验完成,成功: %d,失败: %d", successCount, failCount)
|
||
if successCount > 0 {
|
||
g.Log().Infof(ctx, "提交完成,等待2秒后自动查询结果...")
|
||
time.Sleep(2 * time.Second)
|
||
pollSuccess, pollFail, _ := serviceDataengine.MaterialVerify.PollPendingResults(ctx)
|
||
msg = fmt.Sprintf("批量校验完成,提交成功: %d,提交失败: %d,自动查询成功: %d,未就绪: %d",
|
||
successCount, failCount, pollSuccess, pollFail)
|
||
}
|
||
|
||
return &BatchVerifyRes{
|
||
Success: successCount,
|
||
Fail: failCount,
|
||
Total: len(images),
|
||
Message: msg,
|
||
}, nil
|
||
}
|
||
|
||
// BatchVerifyVideo 批量校验视频
|
||
func (c *MaterialVerifyController) BatchVerifyVideo(ctx context.Context, req *BatchVerifyReq) (res *BatchVerifyRes, err error) {
|
||
if req.Limit <= 0 {
|
||
req.Limit = 100
|
||
}
|
||
|
||
videos, err := dao.TencentVideo.GetPendingList(ctx, req.Limit)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
successCount := 0
|
||
failCount := 0
|
||
|
||
for _, video := range videos {
|
||
log, err := serviceDataengine.MaterialVerify.VerifyVideoByID(ctx, video.VideoID)
|
||
if err != nil {
|
||
failCount++
|
||
g.Log().Errorf(ctx, "视频校验失败: %s, error: %v", video.VideoID, err)
|
||
} else {
|
||
successCount++
|
||
g.Log().Infof(ctx, "视频校验已提交: %s, logId: %d", video.VideoID, log.Id)
|
||
}
|
||
time.Sleep(100 * time.Millisecond)
|
||
}
|
||
|
||
// 等待易盾处理,然后自动查询结果
|
||
msg := fmt.Sprintf("批量校验完成,成功: %d,失败: %d", successCount, failCount)
|
||
if successCount > 0 {
|
||
g.Log().Infof(ctx, "提交完成,等待2秒后自动查询结果...")
|
||
time.Sleep(2 * time.Second)
|
||
pollSuccess, pollFail, _ := serviceDataengine.MaterialVerify.PollPendingResults(ctx)
|
||
msg = fmt.Sprintf("批量校验完成,提交成功: %d,提交失败: %d,自动查询成功: %d,未就绪: %d",
|
||
successCount, failCount, pollSuccess, pollFail)
|
||
}
|
||
|
||
return &BatchVerifyRes{
|
||
Success: successCount,
|
||
Fail: failCount,
|
||
Total: len(videos),
|
||
Message: msg,
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 账户列表接口
|
||
// =============================================================================
|
||
|
||
// ListAccountsReq 账户列表请求
|
||
type ListAccountsReq struct{}
|
||
|
||
// AccountItem 账户列表项
|
||
type AccountItem struct {
|
||
AccountID int64 `json:"accountId"`
|
||
CorporationName string `json:"corporationName"`
|
||
}
|
||
|
||
// ListAccountsRes 账户列表响应
|
||
type ListAccountsRes struct {
|
||
List []AccountItem `json:"list"`
|
||
}
|
||
|
||
// ListAccounts 获取所有启用的广告账户列表(用于前端下拉筛选)
|
||
func (c *MaterialVerifyController) ListAccounts(ctx context.Context, req *ListAccountsReq) (res *ListAccountsRes, err error) {
|
||
accounts, err := dao.TencentAccountRelation.GetAll(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var items []AccountItem
|
||
for _, acc := range accounts {
|
||
items = append(items, AccountItem{
|
||
AccountID: acc.AccountID,
|
||
CorporationName: acc.CorporationName,
|
||
})
|
||
}
|
||
|
||
return &ListAccountsRes{
|
||
List: items,
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 导出接口 - 不通过数据
|
||
// =============================================================================
|
||
|
||
// ExportRejectedReq 导出不通过数据请求
|
||
type ExportRejectedReq struct {
|
||
MaterialType string `json:"materialType"` // IMAGE/VIDEO,为空则导出全部
|
||
}
|
||
|
||
// ExportRejectedItem 导出的不通过数据项
|
||
type ExportRejectedItem struct {
|
||
ID int64 `json:"id"`
|
||
MaterialID string `json:"materialId"`
|
||
AccountID int64 `json:"accountId"`
|
||
CorporationName string `json:"corporationName"`
|
||
PreviewURL string `json:"previewUrl"`
|
||
Description string `json:"description"`
|
||
ErrorMsg string `json:"errorMsg"`
|
||
MaterialType string `json:"materialType"`
|
||
ImageUsage string `json:"imageUsage,omitempty"`
|
||
CreatedAt string `json:"createdAt"`
|
||
}
|
||
|
||
// ExportRejectedRes 导出不通过数据响应
|
||
type ExportRejectedRes struct {
|
||
Items []ExportRejectedItem `json:"items"`
|
||
Total int `json:"total"`
|
||
}
|
||
|
||
// ExportRejected 导出不通过的图片/视频数据(含失败原因)
|
||
func (c *MaterialVerifyController) ExportRejected(ctx context.Context, req *ExportRejectedReq) (res *ExportRejectedRes, err error) {
|
||
items, err := serviceDataengine.MaterialVerify.ExportRejectedData(ctx, req.MaterialType)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 转换为响应结构
|
||
var respItems []ExportRejectedItem
|
||
for _, item := range items {
|
||
respItems = append(respItems, ExportRejectedItem{
|
||
ID: item.ID,
|
||
MaterialID: item.MaterialID,
|
||
AccountID: item.AccountID,
|
||
CorporationName: item.CorporationName,
|
||
PreviewURL: item.PreviewURL,
|
||
Description: item.Description,
|
||
ErrorMsg: item.ErrorMsg,
|
||
MaterialType: item.MaterialType,
|
||
ImageUsage: item.ImageUsage,
|
||
CreatedAt: item.CreatedAt,
|
||
})
|
||
}
|
||
|
||
return &ExportRejectedRes{
|
||
Items: respItems,
|
||
Total: len(respItems),
|
||
}, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 回调处理接口
|
||
// =============================================================================
|
||
|
||
// CallbackRes 回调响应
|
||
type CallbackRes struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
}
|
||
|
||
// ImageCallback 图片校验回调
|
||
func (c *MaterialVerifyController) ImageCallback(ctx context.Context, req *ImageCallbackReq) (res *CallbackRes, err error) {
|
||
if req.CallbackData == "" {
|
||
return &CallbackRes{Code: 400, Msg: "callbackData不能为空"}, nil
|
||
}
|
||
|
||
err = serviceDataengine.MaterialVerify.ProcessImageCallback(ctx, req.CallbackData)
|
||
if err != nil {
|
||
return &CallbackRes{Code: 500, Msg: err.Error()}, nil
|
||
}
|
||
|
||
return &CallbackRes{Code: 200, Msg: "处理成功"}, nil
|
||
}
|
||
|
||
// VideoCallback 视频校验回调
|
||
func (c *MaterialVerifyController) VideoCallback(ctx context.Context, req *VideoCallbackReq) (res *CallbackRes, err error) {
|
||
if req.CallbackData == "" {
|
||
return &CallbackRes{Code: 400, Msg: "callbackData不能为空"}, nil
|
||
}
|
||
|
||
err = serviceDataengine.MaterialVerify.ProcessVideoCallback(ctx, req.CallbackData)
|
||
if err != nil {
|
||
return &CallbackRes{Code: 500, Msg: err.Error()}, nil
|
||
}
|
||
|
||
return &CallbackRes{Code: 200, Msg: "处理成功"}, nil
|
||
}
|
||
|
||
// ResultRes 结果查询响应
|
||
type ResultRes struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
}
|
||
|
||
// ImageResult 图片校验结果查询(轮询模式)
|
||
func (c *MaterialVerifyController) ImageResult(ctx context.Context, req *TaskIDReq) (res *ResultRes, err error) {
|
||
if req.TaskID == "" {
|
||
return &ResultRes{Code: 400, Msg: "taskId不能为空"}, nil
|
||
}
|
||
|
||
err = serviceDataengine.MaterialVerify.ProcessImageResultByTask(ctx, req.TaskID)
|
||
if err != nil {
|
||
return &ResultRes{Code: 500, Msg: err.Error()}, nil
|
||
}
|
||
|
||
return &ResultRes{Code: 200, Msg: "处理成功"}, nil
|
||
}
|
||
|
||
// VideoResult 视频校验结果查询(轮询模式)
|
||
func (c *MaterialVerifyController) VideoResult(ctx context.Context, req *TaskIDReq) (res *ResultRes, err error) {
|
||
if req.TaskID == "" {
|
||
return &ResultRes{Code: 400, Msg: "taskId不能为空"}, nil
|
||
}
|
||
|
||
err = serviceDataengine.MaterialVerify.ProcessVideoResultByTask(ctx, req.TaskID)
|
||
if err != nil {
|
||
return &ResultRes{Code: 500, Msg: err.Error()}, nil
|
||
}
|
||
|
||
return &ResultRes{Code: 200, Msg: "处理成功"}, nil
|
||
}
|