yidun送检新增账户下拉和导出优化
This commit is contained in:
@@ -5,4 +5,5 @@ const (
|
|||||||
TencentImageTable = "tencent_image" // 图片送检表
|
TencentImageTable = "tencent_image" // 图片送检表
|
||||||
TencentVideoTable = "tencent_video" // 视频送检表
|
TencentVideoTable = "tencent_video" // 视频送检表
|
||||||
TencentContentCheckLogTable = "tencent_content_check_log" // 送检日志表
|
TencentContentCheckLogTable = "tencent_content_check_log" // 送检日志表
|
||||||
|
TencentAccountRelationTable = "tencent_account_relation" // 腾讯广告账户关系表
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -436,6 +436,103 @@ func (c *MaterialVerifyController) BatchVerifyVideo(ctx context.Context, req *Ba
|
|||||||
}, nil
|
}, 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
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// 回调处理接口
|
// 回调处理接口
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
@@ -273,6 +273,26 @@ func (d *MaterialVerifyLogDAO) GetPendingResults(ctx context.Context, limit int)
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLastRejectedLogByMaterialID 根据素材ID获取最后一条失败的校验日志
|
||||||
|
func (d *MaterialVerifyLogDAO) GetLastRejectedLogByMaterialID(ctx context.Context, materialID string, verifyStatus string) (*daoEntity.MaterialVerifyLog, error) {
|
||||||
|
var result daoEntity.MaterialVerifyLog
|
||||||
|
r, err := g.DB("default").Model(MaterialVerifyLogTable).
|
||||||
|
Where(daoEntity.MaterialVerifyLogCols.MaterialID, materialID).
|
||||||
|
Where(daoEntity.MaterialVerifyLogCols.VerifyStatus, verifyStatus).
|
||||||
|
OrderDesc(daoEntity.MaterialVerifyLogCols.CreatedAt).
|
||||||
|
One()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.IsEmpty() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err = r.Struct(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CountPendingResults 统计待查询结果的数量
|
// CountPendingResults 统计待查询结果的数量
|
||||||
func (d *MaterialVerifyLogDAO) CountPendingResults(ctx context.Context) (int, error) {
|
func (d *MaterialVerifyLogDAO) CountPendingResults(ctx context.Context) (int, error) {
|
||||||
count, err := g.DB("default").Model(MaterialVerifyLogTable).
|
count, err := g.DB("default").Model(MaterialVerifyLogTable).
|
||||||
|
|||||||
33
dao/dataengine/tencent_account_relation_dao.go
Normal file
33
dao/dataengine/tencent_account_relation_dao.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package dataengine
|
||||||
|
|
||||||
|
import (
|
||||||
|
consts "cid/consts/dataengine"
|
||||||
|
entity "cid/model/entity/dataengine"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TencentAccountRelationDAO 腾讯广告账户关系数据访问层
|
||||||
|
type TencentAccountRelationDAO struct{}
|
||||||
|
|
||||||
|
// TencentAccountRelation DAO单例
|
||||||
|
var TencentAccountRelation = new(TencentAccountRelationDAO)
|
||||||
|
|
||||||
|
// GetAll 获取所有启用的账户列表
|
||||||
|
func (d *TencentAccountRelationDAO) GetAll(ctx context.Context) ([]entity.TencentAccountRelation, error) {
|
||||||
|
var result []entity.TencentAccountRelation
|
||||||
|
r, err := Model(consts.TencentAccountRelationTable).
|
||||||
|
WhereNull("deleted_at").
|
||||||
|
OrderAsc(entity.TencentAccountRelationCols.AccountID).
|
||||||
|
All()
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Errorf(ctx, "查询账户关系表失败: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = r.Structs(&result); err != nil {
|
||||||
|
g.Log().Errorf(ctx, "转换账户关系数据失败: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
27
model/entity/dataengine/tencent_account_relation.go
Normal file
27
model/entity/dataengine/tencent_account_relation.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package dataengine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.com/red-future/common/beans"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TencentAccountRelation 腾讯广告账户关系实体(来源:data-engine.tencent_account_relation)
|
||||||
|
type TencentAccountRelation struct {
|
||||||
|
beans.SQLBaseDO `orm:",inherit"`
|
||||||
|
// 业务字段
|
||||||
|
AccountID int64 `orm:"account_id" json:"accountId" description:"账户ID"`
|
||||||
|
CorporationName string `orm:"corporation_name" json:"corporationName" description:"公司名称"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TencentAccountRelationCol 账户关系表字段定义
|
||||||
|
type TencentAccountRelationCol struct {
|
||||||
|
beans.SQLBaseCol
|
||||||
|
AccountID string
|
||||||
|
CorporationName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TencentAccountRelationCols 账户关系表字段常量
|
||||||
|
var TencentAccountRelationCols = TencentAccountRelationCol{
|
||||||
|
SQLBaseCol: beans.DefSQLBaseCol,
|
||||||
|
AccountID: "account_id",
|
||||||
|
CorporationName: "corporation_name",
|
||||||
|
}
|
||||||
@@ -255,8 +255,15 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-item">
|
<div class="filter-item">
|
||||||
<label>账户ID:</label>
|
<label>账户:</label>
|
||||||
<el-input v-model="imageFilters.accountId" placeholder="账户ID" style="width: 120px;" clearable @keyup.enter.native="searchImage"></el-input>
|
<el-select v-model="imageFilters.accountId" placeholder="全部账户" clearable filterable style="width: 200px;" @change="searchImage">
|
||||||
|
<el-option
|
||||||
|
v-for="item in accounts"
|
||||||
|
:key="item.accountId"
|
||||||
|
:label="item.accountId + ' - ' + item.corporationName"
|
||||||
|
:value="item.accountId">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" @click="searchImage">搜索</el-button>
|
<el-button type="primary" @click="searchImage">搜索</el-button>
|
||||||
<el-button @click="resetImageFilter">重置</el-button>
|
<el-button @click="resetImageFilter">重置</el-button>
|
||||||
@@ -334,8 +341,15 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-item">
|
<div class="filter-item">
|
||||||
<label>账户ID:</label>
|
<label>账户:</label>
|
||||||
<el-input v-model="videoFilters.accountId" placeholder="账户ID" style="width: 120px;" clearable @keyup.enter.native="searchVideo"></el-input>
|
<el-select v-model="videoFilters.accountId" placeholder="全部账户" clearable filterable style="width: 200px;" @change="searchVideo">
|
||||||
|
<el-option
|
||||||
|
v-for="item in accounts"
|
||||||
|
:key="item.accountId"
|
||||||
|
:label="item.accountId + ' - ' + item.corporationName"
|
||||||
|
:value="item.accountId">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" @click="searchVideo">搜索</el-button>
|
<el-button type="primary" @click="searchVideo">搜索</el-button>
|
||||||
<el-button @click="resetVideoFilter">重置</el-button>
|
<el-button @click="resetVideoFilter">重置</el-button>
|
||||||
@@ -601,11 +615,14 @@
|
|||||||
previewUrl: '',
|
previewUrl: '',
|
||||||
previewType: 'image',
|
previewType: 'image',
|
||||||
materialLogs: [],
|
materialLogs: [],
|
||||||
batchLoading: false
|
batchLoading: false,
|
||||||
|
// 账户列表(下拉筛选用)
|
||||||
|
accounts: []
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loadStats();
|
this.loadStats();
|
||||||
this.loadImageList();
|
this.loadImageList();
|
||||||
|
this.loadAccounts();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// API 请求
|
// API 请求
|
||||||
@@ -639,6 +656,21 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 加载账户列表(下拉筛选用)
|
||||||
|
loadAccounts() {
|
||||||
|
this.apiPost('/material/verify/controller/list-accounts', {}).then(res => {
|
||||||
|
this.accounts = res.data.data?.list || [];
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('加载账户列表失败', err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取账户名称(显示用)
|
||||||
|
getAccountName(accountId) {
|
||||||
|
const account = this.accounts.find(a => a.accountId === accountId);
|
||||||
|
return account ? account.corporationName : '';
|
||||||
|
},
|
||||||
|
|
||||||
// 图片列表
|
// 图片列表
|
||||||
loadImageList() {
|
loadImageList() {
|
||||||
this.imageLoading = true;
|
this.imageLoading = true;
|
||||||
@@ -813,36 +845,50 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出
|
// 导出(不通过数据,含失败原因)
|
||||||
exportImageUrls() {
|
exportImageUrls() {
|
||||||
if (!this.imageList.length) {
|
this.apiPost('/material/verify/controller/export-rejected', { materialType: 'IMAGE' }).then(res => {
|
||||||
this.$message.warning('没有可导出的图片');
|
const items = res.data.data?.items || [];
|
||||||
return;
|
if (!items.length) {
|
||||||
}
|
this.$message.warning('没有不通过的图片数据');
|
||||||
const rows = [['ID', '图片ID', '账户ID', '用途', '预览URL', '状态', '描述']];
|
return;
|
||||||
this.imageList.forEach(item => {
|
}
|
||||||
rows.push([
|
const rows = [['序号', '账户(名称)', '预览URL', '描述', '失败原因', '检测时间']];
|
||||||
item.id, item.imageId, item.accountId, item.imageUsage,
|
items.forEach((item, index) => {
|
||||||
item.previewUrl || '-',
|
const accountLabel = item.corporationName ? item.accountId + ' - ' + item.corporationName : item.accountId;
|
||||||
this.getStatusText(item.verifyStatus), item.description || '-'
|
rows.push([
|
||||||
]);
|
index + 1, accountLabel,
|
||||||
|
item.previewUrl || '-', item.description || '-',
|
||||||
|
item.errorMsg || '-', item.createdAt || '-'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
this.downloadCsv('图片不通过数据.csv', rows);
|
||||||
|
this.$message.success('导出成功,共 ' + items.length + ' 条');
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error('导出失败: ' + (err.response?.data?.msg || err.message));
|
||||||
});
|
});
|
||||||
this.downloadCsv('图片列表.csv', rows);
|
|
||||||
},
|
},
|
||||||
exportVideoUrls() {
|
exportVideoUrls() {
|
||||||
if (!this.videoList.length) {
|
this.apiPost('/material/verify/controller/export-rejected', { materialType: 'VIDEO' }).then(res => {
|
||||||
this.$message.warning('没有可导出的视频');
|
const items = res.data.data?.items || [];
|
||||||
return;
|
if (!items.length) {
|
||||||
}
|
this.$message.warning('没有不通过的视频数据');
|
||||||
const rows = [['ID', '视频ID', '账户ID', '预览URL', '状态', '描述']];
|
return;
|
||||||
this.videoList.forEach(item => {
|
}
|
||||||
rows.push([
|
const rows = [['序号', '账户(名称)', '预览URL', '描述', '失败原因', '检测时间']];
|
||||||
item.id, item.videoId, item.accountId,
|
items.forEach((item, index) => {
|
||||||
item.previewUrl || '-',
|
const accountLabel = item.corporationName ? item.accountId + ' - '+ item.corporationName : item.accountId;
|
||||||
this.getStatusText(item.verifyStatus), item.description || '-'
|
rows.push([
|
||||||
]);
|
index + 1, accountLabel,
|
||||||
|
item.previewUrl || '-', item.description || '-',
|
||||||
|
item.errorMsg || '-', item.createdAt || '-'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
this.downloadCsv('视频不通过数据.csv', rows);
|
||||||
|
this.$message.success('导出成功,共 ' + items.length + ' 条');
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error('导出失败: ' + (err.response?.data?.msg || err.message));
|
||||||
});
|
});
|
||||||
this.downloadCsv('视频列表.csv', rows);
|
|
||||||
},
|
},
|
||||||
downloadCsv(filename, rows) {
|
downloadCsv(filename, rows) {
|
||||||
const csv = rows.map(r => r.map(v => `"${String(v).replace(/"/g, '""')}"`).join(',')).join('\n');
|
const csv = rows.map(r => r.map(v => `"${String(v).replace(/"/g, '""')}"`).join(',')).join('\n');
|
||||||
|
|||||||
@@ -656,6 +656,140 @@ func (s *MaterialVerifyService) PollPendingVideoResults(ctx context.Context) (in
|
|||||||
return s.PollPendingResultsByType(ctx, consts.SourceTableTencentVideo)
|
return s.PollPendingResultsByType(ctx, consts.SourceTableTencentVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 导出服务 - 不通过数据导出
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// ExportRejectedItem 导出的不通过数据项
|
||||||
|
type ExportRejectedItem struct {
|
||||||
|
ID int64 `json:"id"` // 素材表主键ID
|
||||||
|
MaterialID string `json:"materialId"` // 素材ID(imageId/videoId)
|
||||||
|
AccountID int64 `json:"accountId"` // 账户ID
|
||||||
|
CorporationName string `json:"corporationName"` // 公司名称
|
||||||
|
PreviewURL string `json:"previewUrl"` // 预览URL
|
||||||
|
Description string `json:"description"` // 描述
|
||||||
|
ErrorMsg string `json:"errorMsg"` // 失败原因(最后一条失败日志的error_msg)
|
||||||
|
MaterialType string `json:"materialType"` // 素材类型 IMAGE/VIDEO
|
||||||
|
ImageUsage string `json:"imageUsage"` // 图片用途(仅图片)
|
||||||
|
CreatedAt string `json:"createdAt"` // 检测时间(日志创建时间)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFailureReason 获取失败原因
|
||||||
|
func getFailureReason(log *entity.MaterialVerifyLog) string {
|
||||||
|
if log == nil {
|
||||||
|
return "无校验日志"
|
||||||
|
}
|
||||||
|
// 优先使用 error_msg
|
||||||
|
if log.ErrorMsg != "" {
|
||||||
|
return log.ErrorMsg
|
||||||
|
}
|
||||||
|
// 根据 suggestion 和 label 生成原因
|
||||||
|
reasonMap := map[int]string{
|
||||||
|
0: "内容检测通过",
|
||||||
|
1: "内容嫌疑(需人工审核)",
|
||||||
|
2: "内容不通过",
|
||||||
|
}
|
||||||
|
suggestionText := reasonMap[log.Suggestion]
|
||||||
|
if suggestionText == "" {
|
||||||
|
suggestionText = fmt.Sprintf("未知(suggestion=%d)", log.Suggestion)
|
||||||
|
}
|
||||||
|
// 如果有 response_result,尝试提取更多信息
|
||||||
|
if log.ResponseResult != "" {
|
||||||
|
var resultMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(log.ResponseResult), &resultMap); err == nil {
|
||||||
|
if labels, ok := resultMap["labels"]; ok {
|
||||||
|
return fmt.Sprintf("%s (labels: %v)", suggestionText, labels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suggestionText
|
||||||
|
}
|
||||||
|
return suggestionText
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportRejectedData 导出不通过数据
|
||||||
|
func (s *MaterialVerifyService) ExportRejectedData(ctx context.Context, materialType string) ([]ExportRejectedItem, error) {
|
||||||
|
var items []ExportRejectedItem
|
||||||
|
|
||||||
|
// 加载账户名称映射
|
||||||
|
accountMap := make(map[int64]string)
|
||||||
|
if accounts, err := dao.TencentAccountRelation.GetAll(ctx); err == nil {
|
||||||
|
for _, acc := range accounts {
|
||||||
|
if acc.CorporationName != "" {
|
||||||
|
accountMap[acc.AccountID] = acc.CorporationName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理图片
|
||||||
|
if materialType == "" || materialType == entity.MaterialTypeImage {
|
||||||
|
condition := map[string]interface{}{
|
||||||
|
entity.TencentImageCols.VerifyStatus: entity.VerifyStatusRejected,
|
||||||
|
}
|
||||||
|
images, total, err := dao.TencentImage.GetByCondition(ctx, condition, 1, 100000)
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Errorf(ctx, "查询不通过图片失败: %v", err)
|
||||||
|
return nil, fmt.Errorf("查询不通过图片失败: %w", err)
|
||||||
|
}
|
||||||
|
g.Log().Infof(ctx, "导出不通过图片: total=%d, got=%d", total, len(images))
|
||||||
|
|
||||||
|
for _, img := range images {
|
||||||
|
// 查询最后一条失败的校验日志
|
||||||
|
log, _ := dao.MaterialVerifyLog.GetLastRejectedLogByMaterialID(ctx, img.ImageID, entity.VerifyStatusRejected)
|
||||||
|
var createdAtStr string
|
||||||
|
if log != nil && log.CreatedAt != nil {
|
||||||
|
createdAtStr = log.CreatedAt.Format("Y-m-d H:i:s")
|
||||||
|
}
|
||||||
|
items = append(items, ExportRejectedItem{
|
||||||
|
ID: img.Id,
|
||||||
|
MaterialID: img.ImageID,
|
||||||
|
AccountID: img.AccountID,
|
||||||
|
CorporationName: accountMap[img.AccountID],
|
||||||
|
PreviewURL: img.PreviewURL,
|
||||||
|
Description: img.Description,
|
||||||
|
ErrorMsg: getFailureReason(log),
|
||||||
|
MaterialType: entity.MaterialTypeImage,
|
||||||
|
ImageUsage: img.ImageUsage,
|
||||||
|
CreatedAt: createdAtStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理视频
|
||||||
|
if materialType == "" || materialType == entity.MaterialTypeVideo {
|
||||||
|
condition := map[string]interface{}{
|
||||||
|
entity.TencentVideoCols.VerifyStatus: entity.VerifyStatusRejected,
|
||||||
|
}
|
||||||
|
videos, total, err := dao.TencentVideo.GetByCondition(ctx, condition, 1, 100000)
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Errorf(ctx, "查询不通过视频失败: %v", err)
|
||||||
|
return nil, fmt.Errorf("查询不通过视频失败: %w", err)
|
||||||
|
}
|
||||||
|
g.Log().Infof(ctx, "导出不通过视频: total=%d, got=%d", total, len(videos))
|
||||||
|
|
||||||
|
for _, vid := range videos {
|
||||||
|
// 查询最后一条失败的校验日志
|
||||||
|
log, _ := dao.MaterialVerifyLog.GetLastRejectedLogByMaterialID(ctx, vid.VideoID, entity.VerifyStatusRejected)
|
||||||
|
var createdAtStr string
|
||||||
|
if log != nil && log.CreatedAt != nil {
|
||||||
|
createdAtStr = log.CreatedAt.Format("Y-m-d H:i:s")
|
||||||
|
}
|
||||||
|
items = append(items, ExportRejectedItem{
|
||||||
|
ID: vid.Id,
|
||||||
|
MaterialID: vid.VideoID,
|
||||||
|
AccountID: vid.AccountID,
|
||||||
|
CorporationName: accountMap[vid.AccountID],
|
||||||
|
PreviewURL: vid.PreviewURL,
|
||||||
|
Description: vid.Description,
|
||||||
|
ErrorMsg: getFailureReason(log),
|
||||||
|
MaterialType: entity.MaterialTypeVideo,
|
||||||
|
CreatedAt: createdAtStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetPendingResultsCount 获取待查询结果的数量
|
// GetPendingResultsCount 获取待查询结果的数量
|
||||||
func (s *MaterialVerifyService) GetPendingResultsCount(ctx context.Context) (int, error) {
|
func (s *MaterialVerifyService) GetPendingResultsCount(ctx context.Context) (int, error) {
|
||||||
return dao.MaterialVerifyLog.CountPendingResults(ctx)
|
return dao.MaterialVerifyLog.CountPendingResults(ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user