yidun检测、查询

This commit is contained in:
2026-05-08 09:33:40 +08:00
parent 22dd73c37f
commit 51d26aeee7
10 changed files with 2321 additions and 41 deletions

875
YIDUN_API_DOCUMENTATION.md Normal file
View File

@@ -0,0 +1,875 @@
# 易盾内容安全 API 调用文档
## 📋 目录
- [概述](#概述)
- [配置说明](#配置说明)
- [API接口列表](#api接口列表)
- [文本检测](#1-文本检测)
- [图片检测](#2-图片检测)
- [视频检测](#3-视频检测)
- [错误码说明](#错误码说明)
---
## 概述
本项目集成了网易易盾内容安全SDK提供文本、图片、视频的内容安全检测服务。
### 产品凭证
| 检测类型 | SecretID | SecretKey | BusinessID |
|---------|----------|-----------|------------|
| **音视频检测** | f58a38341ca6227014df7c3bf0e6f16f | 526aa631ba5d518aedeb70b5a3b67371 | 需从易盾控制台获取 |
| **图片检测** | 9a82f90bfec61eb40d1c95605b894817 | f73a78954417a3713c36ec2d14eb2b5f | 需从易盾控制台获取 |
| **文本检测** | 待配置 | 待配置 | 待配置 |
### 服务地址
```
http://localhost:3002
```
---
## 配置说明
配置文件位置:`config.yml`
```yaml
yidun:
# 音视频检测配置
video:
business_id: "YOUR_VIDEO_BUSINESS_ID"
secret_id: "f58a38341ca6227014df7c3bf0e6f16f"
secret_key: "526aa631ba5d518aedeb70b5a3b67371"
# 图片检测配置
image:
business_id: "YOUR_IMAGE_BUSINESS_ID"
secret_id: "9a82f90bfec61eb40d1c95605b894817"
secret_key: "f73a78954417a3713c36ec2d14eb2b5f"
# 文本检测配置
text:
business_id: "YOUR_TEXT_BUSINESS_ID"
secret_id: "YOUR_TEXT_SECRET_ID"
secret_key: "YOUR_TEXT_SECRET_KEY"
```
---
## API接口列表
| 接口名称 | 请求方法 | 路径 | 说明 |
|---------|---------|------|------|
| 文本检测提交 | POST | `/yidun/detect-text` | 提交文本进行异步检测 |
| 图片检测提交 | POST | `/yidun/detect-image` | 提交图片进行异步检测 |
| 视频检测提交 | POST | `/yidun/detect-video` | 提交视频进行检测 |
| 图片结果查询 | POST | `/yidun/GetImageResult` | 查询图片检测结果(轮询模式) |
| 图片检测回调 | POST | `/yidun/receive-image-callback` | 接收图片检测结果推送(推送模式) |
| 视频结果查询 | POST | `/yidun/GetVideoResult` | 查询视频检测结果(轮询模式) |
| 视频检测回调 | POST | `/yidun/receive-video-callback` | 接收视频检测结果推送(推送模式) |
**注意**:检测结果是异步的,提交接口只返回 `task_id`,需要通过回调或轮询获取结果。
---
## 1. 文本检测
### 提交文本检测任务
**接口地址**: `POST /yidun/detect-text`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| data_id | string | 是 | 数据唯一标识 |
| content | string | 是 | 待检测的文本内容 |
| ip | string | 否 | 用户IP地址 |
| token | string | 否 | 用户token反作弊 |
**请求示例**:
```bash
curl -X POST http://localhost:3002/yidun/detect-text \
-H "Content-Type: application/json" \
-d '{
"data_id": "text-001",
"content": "这是一段需要检测的文本内容",
"ip": "192.168.1.100"
}'
```
**响应示例**:
```json
{
"code": 0,
"message": "",
"data": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}
```
**响应说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| data | string | 任务ID (task_id),用于后续查询检测结果 |
---
## 2. 图片检测
### 2.1 提交图片检测任务
**接口地址**: `POST /yidun/detect-image`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| data_id | string | 是 | 数据唯一标识 |
| image_url | string | 是 | 图片URL地址 |
| callback_url | string | 否 | 回调地址 |
**请求示例**:
```bash
curl -X POST http://localhost:3002/yidun/detect-image \
-H "Content-Type: application/json" \
-d '{
"data_id": "img-001",
"image_url": "https://example.com/image.jpg"
}'
```
**响应示例**:
```json
{
"code": 0,
"message": "",
"data": {
"taskId": "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7",
"name": "img-001",
"dataId": "img-001",
"dealingCount": 0
}
}
```
**响应说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| taskId | string | 任务ID用于后续查询检测结果 |
| name | string | 图片唯一标识 |
| dataId | string | 客户图片唯一标识 |
| dealingCount | int64 | 缓冲池排队待处理数据量 |
### 2.2 查询图片检测结果(轮询模式)
**接口地址**: `POST /yidun/GetImageResult`
**SDK调用信息**:
- SDK方法: `ImageClient.ImageCallback(request)`
- 请求地址: `http://as.dun.163.com/v5/image/callback/results`
- 请求参数: `yidunRequestId` (taskId)
**请求示例**:
```bash
curl -X POST "http://localhost:3002/yidun/GetImageResult" \
-H "Content-Type: application/json" \
-d '{"taskId": "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7"}'
```
**响应示例**:
```json
{
"code": 0,
"message": "",
"data": {
"taskId": "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7",
"status": 2,
"suggestion": 0,
"label": 100,
"resultType": 2,
"dataId": "img-001",
"name": "img-001",
"censorTime": 1629426386079,
"url": "https://example.com/image.jpg",
"antispam": {
"taskId": "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7",
"name": "img-001",
"dataId": "img-001",
"status": 2,
"suggestion": 0,
"label": 100,
"resultType": 2,
"censorTime": 1629426386079,
"censorSource": 0,
"censorRound": 1,
"labels": [
{
"label": 400,
"level": 2,
"rate": 1.0
}
],
"censorLabels": [
{
"code": "审核标签编码",
"customCode": "自定义标签编码",
"name": "审核标签名称",
"desc": "风景"
}
],
"remark": "人审备注信息"
},
"ocr": { ... },
"face": { ... },
"quality": { ... },
"logo": { ... },
"discern": { ... },
"ad": { ... },
"userRisk": { ... },
"anticheat": { ... },
"riskControl": { ... },
"aigc": { ... },
"llmCheckInfo": [ ... ]
}
}
```
**antispam 字段完整说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| taskId | string | 任务ID |
| name | string | 图片名称 |
| dataId | string | 客户数据ID |
| status | int | 检测状态0=未开始1=检测中2=检测成功3=检测失败 |
| suggestion | int | 处置建议0=通过1=嫌疑2=不通过 |
| label | int | 一级分类 |
| secondLabel | string | 二级分类 |
| thirdLabel | string | 三级分类 |
| riskDescription | string | 风险描述 |
| resultType | int | 结果类型1=机器结果2=人审结果 |
| censorTime | int64 | 审核完成时间(毫秒时间戳) |
| censorSource | int | 审核来源 |
| censorRound | int | 审核轮数 |
| censorLabels | array | 审核标签数组 |
| remark | string | 审核备注 |
| overAllMarkDesc | string | 整体审核备注 |
| detailMarks | array | 细节标注 |
| labels | array | 分类标签详情 |
| url | string | 图片URL |
| imgMd5 | string | 图片MD5 |
| frameSize | int | 分帧数 |
| customLabels | array | 客户自定义标签 |
| censorExtension | object | 人审拓展字段 |
| strategyVersions | array | 策略版本 |
| hitType | int | 命中策略类型 |
| strategyType | int | 策略类型1=公有策略2=私有策略 |
| hitResult | string | 命中结果 |
| hitSource | int | 特征添加来源 |
| hidden | bool | 是否有隐藏文件 |
| hiddenFormat | string | 隐藏文件格式 |
| publicOpinionInfo | string | 舆情信息 |
**labels 数组结构**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| label | int | 标签类型 |
| level | int | 判断结果0=正常1=不确定2=确定 |
| rate | float | 置信度 |
| subLabels | array | 二级分类详情 |
| explain | string | LLM解释说明 |
| isLlmCheck | bool | 是否LLM检测命中 |
**censorLabels 数组结构**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| code | string | 审核标签编码 |
| customCode | string | 自定义标签编码 |
| name | string | 审核标签名称 |
| desc | string | 审核标签描述 |
| parentLabelId | string | 父标签ID |
| depth | int | 标签深度 |
### 2.3 接收图片检测回调(推送模式)
**接口地址**: `POST /yidun/receive-image-callback`
**易盾回调配置**:
- HTTP URL: 回调地址 `callbackUrl`
- HTTP Method: POST
- Content-Type: application/x-www-form-urlencoded
**请求参数** (form-data):
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| secretId | string | 是 | 产品密钥ID |
| businessId | string | 是 | 业务ID |
| signature | string | 是 | 请求签名 |
| callbackData | string | 是 | 回调数据(JSON字符串) |
**请求体示例**:
```
callbackData={"antispam":{...}}&signature=xxx&secretId=xxx&businessId=xxx
```
**回调数据格式**:
```json
{
"antispam": {
"taskId": "a2e53718f9c348d09206e4671304ca7c",
"suggestion": 2,
"remark": "人审备注信息,填写才会返回",
"dataId": "dataId",
"name": "name",
"labels": [
{
"label": 400,
"level": 2,
"rate": 1.0
},
{
"label": 500,
"level": 0,
"rate": 1.0
}
],
"censorLabels": [
{
"code": "审核标签编码",
"customCode": "自定义标签编码",
"name": "审核标签名称",
"desc": "风景"
}
],
"resultType": 2,
"censorTime": 1622036595467,
"censorSource": 0,
"censorRound": 1
}
}
```
**Suggestion值说明**:
| 值 | 含义 |
|----|------|
| 0 | 通过 |
| 1 | 嫌疑,需人工审核 |
| 2 | 不通过 |
**Status值说明**:
| 值 | 含义 |
|----|------|
| 0 | 未开始 |
| 1 | 检测中 |
| 2 | 检测成功 |
| 3 | 检测失败 |
---
## 3. 视频检测
### 提交视频检测任务
**接口地址**: `POST /yidun/detect-video`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| data_id | string | 是 | 数据唯一标识 |
| video_url | string | 是 | 视频URL地址 |
| callback_url | string | 否 | 回调地址 |
**请求示例**:
```bash
curl -X POST http://localhost:3002/yidun/detect-video \
-H "Content-Type: application/json" \
-d '{
"data_id": "video-001",
"video_url": "https://example.com/video.mp4"
}'
```
**响应示例**:
```json
{
"code": 0,
"message": "",
"data": {
"taskId": "c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8",
"dataId": "video-001",
"dealingCount": 0
}
}
```
**响应说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| taskId | string | 任务ID用于后续查询检测结果 |
| dataId | string | 调用检测时传递的数据ID |
| dealingCount | int64 | 缓冲池排队待处理数据量 |
**注意**: 视频检测是异步的,检测时间取决于视频长度(几分钟到几十分钟)。建议配置回调地址接收结果。
### 3.2 查询视频检测结果(轮询模式)
**接口地址**: `POST /yidun/GetVideoResult`
**SDK调用信息**:
- SDK方法: `VideoClient.Callback(request)`
- 请求地址: `http://as.dun.163.com/v2/videosolution/callback/results`
- 请求参数: `yidunRequestId` (taskId)
**请求示例**:
```bash
curl -X POST "http://localhost:3002/yidun/GetVideoResult" \
-H "Content-Type: application/json" \
-d '{"taskId": "c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8"}'
```
**响应示例**:
```json
{
"code": 0,
"message": "",
"data": {
"taskId": "18xrunm6fr35t49nnv506ukg02009q66",
"status": 2,
"suggestion": 2,
"label": 100,
"resultType": 1,
"dataId": "video-001",
"censorTime": 1633759853762,
"duration": 71339,
"antispam": {
"taskId": "18xrunm6fr35t49nnv506ukg02009q66",
"suggestion": 2,
"status": 2,
"resultType": 1,
"censorSource": 2,
"checkTime": 1633759861164,
"censorTime": 1633759853762,
"duration": 71339,
"censorLabels": [
{
"code": "101",
"desc": "自定义标签",
"customCode": "68684"
}
],
"evidences": {
"images": [...],
"text": {...},
"audio": {...},
"video": {...}
}
},
"language": { ... },
"voice": { ... },
"asr": { ... },
"ocr": { ... },
"discern": { ... },
"logo": { ... },
"face": { ... },
"aigc": { ... },
"quality": { ... }
}
}
```
**antispam 字段完整说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| taskId | string | 任务ID |
| dataId | string | 客户数据ID |
| callback | string | 回调参数 |
| suggestion | int | 处置建议0=通过1=嫌疑2=不通过 |
| status | int | 检测状态0=未开始1=检测中2=检测成功3=检测失败 |
| resultType | int | 结果类型1=机器结果2=人审结果 |
| censorRound | int | 人审轮次 |
| censor | string | 人审操作人 |
| censorSource | int | 审核来源0=易盾人审1=客户人审2=易盾机审 |
| checkTime | int64 | 机器检测结束时间 |
| censorTime | int64 | 人工审核完成时间 |
| duration | int64 | 音视频时长(毫秒) |
| durationMs | int64 | 音频时长(毫秒) |
| label | int | 一级垃圾类型 |
| secondLabel | string | 二级垃圾类型 |
| thirdLabel | string | 三级垃圾类型 |
| riskDescription | string | 风险描述 |
| picCount | int64 | 截图数量 |
| censorLabels | array | 审核标签数组 |
| censorExtension | object | 质检扩展结果 |
| evidences | object | 机器检测证据信息 |
| solutionExtra | object | 额外信息 |
| reviewEvidences | object | 人审证据信息 |
**evidences 对象说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| text | object | 文本证据(标题、简介等) |
| images | array | 图片证据(封面、截图中的图片) |
| audio | object | 音频证据(人声内容) |
| video | object | 视频证据(视频画面) |
**CensorSource值说明**:
| 值 | 含义 |
|----|------|
| 0 | 易盾人审 |
| 1 | 客户人审 |
| 2 | 易盾机审 |
### 3.3 接收视频检测回调(推送模式)
**接口地址**: `POST /yidun/receive-video-callback`
**易盾回调配置**:
- HTTP URL: 回调地址 `callbackUrl`
- HTTP Method: POST
- Content-Type: application/x-www-form-urlencoded
**请求参数** (form-data):
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| secretId | string | 是 | 产品密钥ID |
| signature | string | 是 | 请求签名 |
| callbackData | string | 是 | 回调数据(JSON字符串) |
**请求体示例**:
```
callbackData={"antispam":{...}}&signature=xxx&secretId=xxx
```
**回调数据格式**:
```json
{
"antispam": {
"taskId": "18xrunm6fr35t49nnv506ukg02009q66",
"suggestion": 2,
"status": 2,
"resultType": 1,
"censorSource": 2,
"checkTime": 1633759861164,
"censorTime": 1633759853762,
"duration": 71339,
"censorLabels": [
{
"code": "101",
"desc": "自定义标签",
"customCode": "68684"
}
],
"evidences": {
"images": [
{
"censorType": 0,
"labels": [
{
"label": 100,
"level": 2,
"rate": 0.9999,
"subLabels": [
{
"details": {
"hitInfos": [
{
"group": "色情部位分组",
"value": "女胸"
}
]
},
"rate": 1,
"subLabel": 10002
}
]
}
],
"name": "gpqis0e92czvfliemjpa3neg02009q6a",
"resultType": 1,
"status": 2,
"suggestion": 2,
"taskId": "0b2dd0e561a8457fbfcf7d79378eec37"
}
],
"text": {
"taskId": "44i3effo1j5w5for9l8xtdjg02009q6a",
"suggestion": 2,
"resultType": 1,
"censorType": 0,
"isRelatedHit": false,
"labels": [
{
"label": 200,
"level": 2,
"subLabels": [
{
"subLabel": "200009",
"details": {
"hitInfos": [
{
"value": "加我",
"positions": [
{
"fieldName": "content",
"startPos": 5,
"endPos": 8
}
]
}
]
}
}
]
}
]
},
"audio": {
"taskId": "18xrunm6fr35t49nnv506ukg02009q66",
"dataId": "",
"status": 2,
"suggestion": 2,
"label": 100,
"resultType": 1,
"callback": "",
"censorSource": 2,
"censorTime": 1633759861065,
"segments": [
{
"startTime": 0,
"endTime": 6,
"content": "第一次用这个软件上了半天,全是长得漂亮呢?",
"type": 0,
"labels": [
{
"label": 100,
"level": 2,
"subLabels": [
{
"subLabel": "100001",
"details": {
"hitInfos": [
{
"value": "120",
"startTime": 1684201,
"endTime": 1684201
}
],
"keywords": [
{
"word": "自定义敏感词"
}
],
"libInfos": [
{
"listType": 1,
"entity": "127.0.0.1"
}
]
}
}
]
}
]
}
],
"duration": 71
},
"video": {
"taskId": "18xrunm6fr35t49nnv506ukg02009q66",
"dataId": "",
"status": 2,
"suggestion": 2,
"resultType": 1,
"censorSource": 2,
"censorTime": 1633759858920,
"pictures": [
{
"type": 1,
"url": "https://xxx.nosdn.127.net/xxx.jpg",
"startTime": 5000,
"endTime": 5000,
"labels": [
{
"label": 100,
"level": 2,
"rate": 0.95157,
"subLabels": [
{
"subLabel": 10007,
"rate": 0.926,
"details": {
"hitInfos": [
{
"value": "卡通色情",
"group": "色情部位分组"
}
]
}
}
]
}
],
"censorSource": 2,
"frontPics": [
{
"url": "https://xxx.nosdn.127.net/xxx_0_0.jpg"
}
],
"backPics": [
{
"url": "https://xxx.nosdn.127.net/xxx_2_10000.jpg"
}
]
}
]
}
}
}
}
```
**Suggestion值说明**:
| 值 | 含义 |
|----|------|
| 0 | 通过 |
| 1 | 嫌疑,需人工审核 |
| 2 | 不通过 |
**Status值说明**:
| 值 | 含义 |
|----|------|
| 0 | 未开始 |
| 1 | 检测中 |
| 2 | 检测成功 |
| 3 | 检测失败 |
**images/images.labels 结构说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| label | int | 标签类型 |
| level | int | 级别0=正常1=嫌疑2=确定违规 |
| rate | float | 置信度 |
| subLabels | array | 二级分类详情 |
| subLabels.details.hitInfos | array | 命中信息 |
| subLabels.details.hitInfos.group | string | 分组名称 |
| subLabels.details.hitInfos.value | string | 命中值 |
**audio.segments 结构说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| startTime | int | 开始时间(秒) |
| endTime | int | 结束时间(秒) |
| content | string | 语音识别原文 |
| type | int | 片段类型0=语音识别1=声纹检测 |
| labels | array | 分类标签 |
| labels.details.keywords | array | 自定义敏感词 |
| labels.details.libInfos | array | 自定义名单 |
| labels.details.hitInfos | array | 命中内容 |
**video.pictures 结构说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| type | int | 截图类型 |
| url | string | 截图URL |
| startTime | int64 | 开始时间(毫秒) |
| endTime | int64 | 结束时间(毫秒) |
| labels | array | 分类标签 |
| frontPics | array | 关联前帧图片 |
| backPics | array | 关联后帧图片 |
---
## 错误码说明
| code | 说明 |
|------|------|
| 0 | 成功 |
| 400 | 请求参数错误 |
| 500 | 服务器内部错误 |
**易盾API错误码**:
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 401 | 认证失败SecretID/SecretKey错误 |
| 403 | 权限不足 |
| 429 | 请求频率超限 |
---
## Go代码调用示例
```go
// 文本检测
func detectText() (string, error) {
req := map[string]interface{}{
"data_id": "text-001",
"content": "这是一段测试文本",
}
jsonData, _ := json.Marshal(req)
resp, err := http.Post("http://localhost:3002/yidun/detect-text", "application/json", bytes.NewBuffer(jsonData))
// ...
}
// 图片检测
func detectImage() (string, error) {
req := map[string]interface{}{
"data_id": "img-001",
"image_url": "https://example.com/image.jpg",
}
jsonData, _ := json.Marshal(req)
resp, err := http.Post("http://localhost:3002/yidun/detect-image", "application/json", bytes.NewBuffer(jsonData))
// ...
}
// 视频检测
func detectVideo() (string, error) {
req := map[string]interface{}{
"data_id": "video-001",
"video_url": "https://example.com/video.mp4",
}
jsonData, _ := json.Marshal(req)
resp, err := http.Post("http://localhost:3002/yidun/detect-video", "application/json", bytes.NewBuffer(jsonData))
// ...
}
```
---
**最后更新**: 2026-05-07

View File

@@ -46,4 +46,31 @@ consul:
address: 116.204.74.41:8500
# pass: jiahui8888
jaeger: #链路追踪
addr: 116.204.74.41:4318
addr: 116.204.74.41:4318
yidun:
# 音视频检测配置
video:
secret_id: "f58a38341ca6227014df7c3bf0e6f16f"
secret_key: "526aa631ba5d518aedeb70b5a3b67371"
region: "cn-hangzhou"
protocol: "https"
max_retry_count: 3
# 图片检测配置
image:
business_id: "20acccc525cbc5cb6bed3d8f6f6b2f77"
secret_id: "9a82f90bfec61eb40d1c95605b894817"
secret_key: "f73a78954417a3713c36ec2d14eb2b5f"
region: "cn-hangzhou"
protocol: "https"
max_retry_count: 3
# 文本检测配置(如需要请补充)
text:
business_id: "YOUR_TEXT_BUSINESS_ID"
secret_id: "YOUR_TEXT_SECRET_ID"
secret_key: "YOUR_TEXT_SECRET_KEY"
region: "cn-hangzhou"
protocol: "https"
max_retry_count: 3

View File

@@ -0,0 +1,164 @@
package yidun
import (
"cid/service/yidun"
"context"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/text/v5/check/async/single"
)
type yidunController struct{}
// YidunController 易盾控制器
var YidunController = new(yidunController)
// DetectTextReq 文本检测请求
type DetectTextReq struct {
DataID string `json:"data_id"`
Content string `json:"content" v:"required#待检测文本不能为空"`
IP string `json:"ip"`
Token string `json:"token"`
}
// DetectImageReq 图片检测请求
type DetectImageReq struct {
DataID string `json:"data_id"`
ImageURL string `json:"image_url" v:"required#图片URL不能为空"`
CallbackURL string `json:"callback_url"`
}
// DetectVideoReq 视频检测请求
type DetectVideoReq struct {
DataID string `json:"data_id"`
VideoURL string `json:"video_url" v:"required#视频URL不能为空"`
CallbackURL string `json:"callback_url"`
}
// DetectText 文本检测
func (c *yidunController) DetectText(ctx context.Context, req *DetectTextReq) (string, error) {
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
businessId := g.Cfg().MustGet(ctx, "yidun.text.business_id").String()
sdkReq := single.NewTextAsyncCheckRequest(businessId)
sdkReq.SetDataID(req.DataID)
sdkReq.SetContent(req.Content)
if req.IP != "" {
sdkReq.SetIP(req.IP)
}
if req.Token != "" {
sdkReq.SetToken(req.Token)
}
return yidun.TextDetection.DetectText(ctx, sdkReq)
}
// DetectImage 图片检测
func (c *yidunController) DetectImage(ctx context.Context, req *DetectImageReq) (*yidun.ImageSubmitResult, error) {
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
return yidun.ImageDetection.DetectImage(ctx, req.ImageURL, req.DataID, req.CallbackURL)
}
// DetectVideo 视频检测
func (c *yidunController) DetectVideo(ctx context.Context, req *DetectVideoReq) (*yidun.VideoSubmitResult, error) {
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
return yidun.VideoDetection.DetectVideo(ctx, req.VideoURL, req.DataID, req.CallbackURL)
}
// ImageCallbackResult 图片检测回调响应
type ImageCallbackResult struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
// ReceiveImageCallback 接收图片检测结果推送
func (c *yidunController) ReceiveImageCallback(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
callbackData := r.GetForm("callbackData", "").String()
if callbackData == "" {
r.Response.WriteJson(ImageCallbackResult{Code: 400, Msg: "callbackData不能为空"})
return
}
err := yidun.ImageDetection.ProcessImageCallback(ctx, callbackData)
if err != nil {
g.Log().Errorf(ctx, "处理图片检测回调失败: %v", err)
r.Response.WriteJson(ImageCallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(ImageCallbackResult{Code: 200, Msg: "success"})
}
// VideoCallbackResult 视频检测回调响应
type VideoCallbackResult struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
// ReceiveVideoCallback 接收视频检测结果推送
func (c *yidunController) ReceiveVideoCallback(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
callbackData := r.GetForm("callbackData", "").String()
if callbackData == "" {
r.Response.WriteJson(VideoCallbackResult{Code: 400, Msg: "callbackData不能为空"})
return
}
err := yidun.VideoDetection.ProcessVideoCallback(ctx, callbackData)
if err != nil {
g.Log().Errorf(ctx, "处理视频检测回调失败: %v", err)
r.Response.WriteJson(VideoCallbackResult{Code: 500, Msg: err.Error()})
return
}
r.Response.WriteJson(VideoCallbackResult{Code: 200, Msg: "success"})
}
// GetVideoResult 获取视频检测结果
func (c *yidunController) GetVideoResult(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
taskId := r.Get("taskId", "").String()
if taskId == "" {
r.Response.WriteJson(g.Map{"code": 400, "msg": "taskId不能为空"})
return
}
result, err := yidun.VideoDetection.GetVideoResult(ctx, taskId)
if err != nil {
g.Log().Errorf(ctx, "查询视频检测结果失败: %v", err)
r.Response.WriteJson(g.Map{"code": 500, "msg": err.Error()})
return
}
r.Response.WriteJson(result)
}
// GetImageResult 获取图片检测结果
func (c *yidunController) GetImageResult(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
taskId := r.Get("taskId", "").String()
if taskId == "" {
r.Response.WriteJson(g.Map{"code": 400, "msg": "taskId不能为空"})
return
}
result, err := yidun.ImageDetection.GetImageResult(ctx, taskId)
if err != nil {
g.Log().Errorf(ctx, "查询图片检测结果失败: %v", err)
r.Response.WriteJson(g.Map{"code": 500, "msg": err.Error()})
return
}
r.Response.WriteJson(result)
}

14
go.mod
View File

@@ -7,6 +7,7 @@ require (
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.5
github.com/gogf/gf/v2 v2.9.5
github.com/yidun/yidun-golang-sdk v1.0.38
golang.org/x/net v0.47.0
)
@@ -26,6 +27,7 @@ require (
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-ego/gse v1.0.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
@@ -61,14 +63,15 @@ require (
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/r3labs/diff/v2 v2.15.1 // indirect
github.com/redis/go-redis/v9 v9.12.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/tiger1103/gfast-token v1.0.10 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/vcaesar/cedar v0.30.0 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
go.mongodb.org/mongo-driver/v2 v2.4.0 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
@@ -79,11 +82,10 @@ require (
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect

59
go.sum
View File

@@ -1,9 +1,4 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.com/red-future/common v0.0.6 h1:2Otksfcy5V5JCBcqd2eRKh4WwZ/iAiIhJZMr6uM1x+Q=
gitea.com/red-future/common v0.0.6/go.mod h1:UI9N5UUjilbMPF7+/lypZSnqDVHigt14300oSRrAyZg=
gitea.com/red-future/common v0.0.7/go.mod h1:JgBczlUF8xA8S0YbvDaMLxvvpmBGgjJA6po+EbodzZA=
gitea.com/red-future/common v0.0.9/go.mod h1:JgBczlUF8xA8S0YbvDaMLxvvpmBGgjJA6po+EbodzZA=
gitea.com/red-future/common v0.0.11/go.mod h1:B8syUI4XbLCDQSeRHURYxEwnWw8mEFgmqCxjC+lM+NU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -68,6 +63,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4=
github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -105,12 +102,14 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
@@ -133,8 +132,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
@@ -243,6 +241,8 @@ github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfB
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -264,6 +264,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg=
github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc=
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -283,24 +285,26 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y=
github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/yidun/yidun-golang-sdk v1.0.38 h1:4NjQdt2GGMgLToB2+zTA0L4YRpqY3ZQjVpl2ot1gwfk=
github.com/yidun/yidun-golang-sdk v1.0.38/go.mod h1:+JGdWbkUvLi9uKTtHI+nrxajulfZKA7BXDPlzt1RCsU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver/v2 v2.4.0 h1:Oq6BmUAAFTzMeh6AonuDlgZMuAuEiUxoAD1koK5MuFo=
go.mongodb.org/mongo-driver/v2 v2.4.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
@@ -330,19 +334,15 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae h1:COZdc9Ut6wLq7MO9GIYxfZl4n4ScmgqQLoHocKXrxco=
golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -351,15 +351,16 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -370,7 +371,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -391,25 +391,19 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -421,7 +415,6 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -432,6 +425,9 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
@@ -443,6 +439,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=

14
main.go
View File

@@ -4,12 +4,13 @@ import (
"cid/controller/app"
"cid/controller/data"
"cid/controller/mapping"
controllerYidun "cid/controller/yidun"
serviceYidun "cid/service/yidun"
"fmt"
_ "gitea.com/red-future/common/config"
_ "gitea.com/red-future/common/consul"
"gitea.com/red-future/common/http"
"gitea.com/red-future/common/jaeger"
_ "gitea.com/red-future/common/ragflow" // RAGFlow 客户端自动初始化
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
"golang.org/x/net/context"
@@ -19,6 +20,11 @@ func main() {
ctx := context.Background()
defer jaeger.ShutDown(ctx)
// 初始化易盾客户端
if err := serviceYidun.InitYidunClients(ctx); err != nil {
panic(fmt.Sprintf("初始化易盾客户端失败: %v", err))
}
http.RouteRegister([]interface{}{
// 平台管理
data.Platform,
@@ -30,6 +36,8 @@ func main() {
mapping.DataMapping,
// 应用管理
app.Application,
// 易盾内容安全
controllerYidun.YidunController,
})
select {}
}

View File

@@ -0,0 +1,389 @@
package yidun
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/image/v5/callback"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/image/v5/check"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/image/v5/check/async"
imagesync "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/image/v5/check/sync"
)
// ImageDetectionService 图片检测服务
type ImageDetectionService struct{}
var ImageDetection = new(ImageDetectionService)
var (
ErrImageStillProcessing = errors.New("图片仍在检测中,请稍后重试")
ErrImageResultNotFound = errors.New("未找到图片检测结果")
)
// ImageSubmitResult 图片检测提交结果
type ImageSubmitResult struct {
TaskID string `json:"taskId"` // 任务ID
Name string `json:"name"` // 图片唯一标识
DataID string `json:"dataId"` // 客户图片唯一标识
DealingCount int64 `json:"dealingCount"` // 缓冲池排队待处理数据量
}
// DetectImage 提交图片异步检测任务,返回完整响应
func (s *ImageDetectionService) DetectImage(ctx context.Context, imageURL, dataID string, callbackURL string) (*ImageSubmitResult, error) {
if DefaultClients == nil || DefaultClients.ImageClient == nil {
return nil, fmt.Errorf("易盾图片检测客户端未初始化")
}
if imageURL == "" {
return nil, fmt.Errorf("图片URL不能为空")
}
businessId := g.Cfg().MustGet(ctx, "yidun.image.business_id").String()
g.Log().Infof(ctx, "图片检测任务提交, url: %s, business_id: %s", imageURL, businessId)
// 创建请求
request := async.NewImageV5AsyncCheckRequest(businessId)
imageBean := check.NewImageBeanRequest()
imageBean.SetData(imageURL)
imageBean.SetName(dataID)
imageBean.SetType(1) // 1: 图片URL
if callbackURL != "" {
imageBean.SetCallbackUrl(callbackURL)
}
request.SetImages([]check.ImageBeanRequest{*imageBean})
// 调用API
response, err := DefaultClients.ImageClient.ImageAsyncCheck(request)
if err != nil {
g.Log().Errorf(ctx, "图片检测提交失败: %v", err)
return nil, fmt.Errorf("图片检测提交失败: %w", err)
}
if response.GetCode() != 200 {
g.Log().Errorf(ctx, "图片检测API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
return nil, fmt.Errorf("图片检测API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
}
result := &ImageSubmitResult{
DealingCount: 0,
}
if response.Result != nil {
if response.Result.DealingCount != nil {
result.DealingCount = *response.Result.DealingCount
}
if response.Result.CheckImages != nil && len(*response.Result.CheckImages) > 0 {
detail := (*response.Result.CheckImages)[0]
if detail.TaskId != nil {
result.TaskID = *detail.TaskId
}
if detail.Name != nil {
result.Name = *detail.Name
}
if detail.DataId != nil {
result.DataID = *detail.DataId
}
}
}
g.Log().Infof(ctx, "图片检测任务提交成功, taskID: %s, dealingCount: %d", result.TaskID, result.DealingCount)
return result, nil
}
// ImageResult 图片检测完整结果
type ImageResult struct {
TaskID string `json:"taskId"` // 任务ID
Status int `json:"status"` // 检测状态0=未开始1=检测中2=检测成功3=检测失败
Suggestion int `json:"suggestion"` // 处置建议0=通过1=嫌疑2=不通过
Label int `json:"label"` // 垃圾类型
ResultType int `json:"resultType"` // 结果类型1=机器结果2=人审结果
DataID string `json:"dataId"` // 数据ID
Name string `json:"name"` // 图片标识
CensorTime int64 `json:"censorTime"` // 审核完成时间(毫秒)
Url string `json:"url"` // 图片URL
// 完整证据信息
Antispam *imagesync.ImageV5AntispamResp `json:"antispam,omitempty"` // 反垃圾检测结果
Ocr *imagesync.ImageV5OcrResp `json:"ocr,omitempty"` // OCR文字识别结果
Face *imagesync.ImageV5FaceResp `json:"face,omitempty"` // 人脸检测结果
Quality *imagesync.ImageV5QualityResp `json:"quality,omitempty"` // 图片质量结果
Logo *imagesync.ImageV5LogoResp `json:"logo,omitempty"` // Logo识别结果
Discern *imagesync.ImageV5DiscernResp `json:"discern,omitempty"` // 图片识别结果
Ad *imagesync.ImageV5AdResp `json:"ad,omitempty"` // 广告识别结果
UserRisk *imagesync.ImageV5UserRiskResp `json:"userRisk,omitempty"` // 用户画像结果
Anticheat *imagesync.ImageAnticheatV5Resp `json:"anticheat,omitempty"` // 反作弊结果
RiskControl *imagesync.ImageRiskControlV5Resp `json:"riskControl,omitempty"` // 智能风控结果
Aigc *imagesync.ImageV5AigcResp `json:"aigc,omitempty"` // AIGC识别结果
LlmCheckInfo *[]imagesync.LlmCheckInfo `json:"llmCheckInfo,omitempty"` // 大模型检测结果
}
// GetImageResult 获取图片检测结果(轮询模式)
func (s *ImageDetectionService) GetImageResult(ctx context.Context, taskID string) (*ImageResult, error) {
if DefaultClients == nil || DefaultClients.ImageClient == nil {
return nil, fmt.Errorf("易盾图片检测客户端未初始化")
}
businessId := g.Cfg().MustGet(ctx, "yidun.image.business_id").String()
g.Log().Infof(ctx, "查询图片检测结果, taskID: %s", taskID)
req := callback.NewImageCallbackRequest(businessId)
req.SetYidunRequestId(taskID)
response, err := DefaultClients.ImageClient.ImageCallback(req)
if err != nil {
g.Log().Errorf(ctx, "查询图片检测结果失败: %v", err)
return nil, fmt.Errorf("查询图片检测结果失败: %w", err)
}
if response.GetCode() != 200 {
g.Log().Errorf(ctx, "查询图片检测结果API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
return nil, fmt.Errorf("查询图片检测结果API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
}
if response.Result == nil || len(*response.Result) == 0 {
g.Log().Warningf(ctx, "未找到图片检测结果, taskID: %s", taskID)
return nil, ErrImageStillProcessing
}
// 查找指定taskID的结果
for _, item := range *response.Result {
if item.Antispam != nil && item.Antispam.TaskId != nil && *item.Antispam.TaskId == taskID {
result := &ImageResult{
Antispam: item.Antispam,
Ocr: item.Ocr,
Face: item.Face,
Quality: item.Quality,
Logo: item.Logo,
Discern: item.Discern,
Ad: item.Ad,
UserRisk: item.UserRisk,
Anticheat: item.Anticheat,
RiskControl: item.RiskControl,
Aigc: item.Aigc,
LlmCheckInfo: item.LlmCheckInfo,
}
result.TaskID = taskID
if item.Antispam.Status != nil {
result.Status = *item.Antispam.Status
}
if item.Antispam.Suggestion != nil {
result.Suggestion = *item.Antispam.Suggestion
}
if item.Antispam.Label != nil {
result.Label = *item.Antispam.Label
}
if item.Antispam.ResultType != nil {
result.ResultType = *item.Antispam.ResultType
}
if item.Antispam.DataId != nil {
result.DataID = *item.Antispam.DataId
}
if item.Antispam.Name != nil {
result.Name = *item.Antispam.Name
}
if item.Antispam.CensorTime != nil {
result.CensorTime = *item.Antispam.CensorTime
}
if item.Antispam.Url != nil {
result.Url = *item.Antispam.Url
}
return result, nil
}
}
g.Log().Warningf(ctx, "未找到指定的taskID: %s", taskID)
return nil, ErrImageResultNotFound
}
// ImageCallbackData 推送模式回调数据完整结构
type ImageCallbackData struct {
Antispam *ImageCallbackAntispam `json:"antispam"` // 反垃圾检测结果
}
type ImageCallbackAntispam struct {
TaskId string `json:"taskId"` // 任务ID
Name string `json:"name"` // 图片名称
DataId string `json:"dataId"` // 客户数据ID
Suggestion int `json:"suggestion"` // 处置建议0=通过1=嫌疑2=不通过
Label int `json:"label"` // 一级分类
SecondLabel string `json:"secondLabel"` // 二级分类
ThirdLabel string `json:"thirdLabel"` // 三级分类
RiskDescription string `json:"riskDescription"` // 风险描述
Status int `json:"status"` // 检测状态0=未开始1=检测中2=检测成功3=检测失败
ResultType int `json:"resultType"` // 结果类型1=机器结果2=人审结果
CensorTime int64 `json:"censorTime"` // 审核完成时间
CensorSource int `json:"censorSource"` // 审核来源
CensorRound int `json:"censorRound"` // 审核轮数
CensorLabels []*CensorLabel `json:"censorLabels"` // 审核标签
Remark string `json:"remark"` // 审核备注
OverAllMarkDesc string `json:"overAllMarkDesc"` // 整体审核备注
DetailMarks []*DetailMarkInfo `json:"detailMarks"` // 细节标注
Labels []*ImageLabelInfo `json:"labels"` // 分类标签详情
Url string `json:"url"` // 图片URL
ImgMd5 string `json:"imgMd5"` // 图片MD5
FrameSize int `json:"frameSize"` // 分帧数
CustomLabels []*CustomLabelInfo `json:"customLabels"` // 客户自定义标签
CensorExtension *CensorExtensionInfo `json:"censorExtension"` // 人审拓展字段
StrategyVersions []*StrategyVersionInfo `json:"strategyVersions"` // 策略版本
HitType int `json:"hitType"` // 命中策略类型
StrategyType int `json:"strategyType"` // 策略类型1=公有策略2=私有策略
HitResult string `json:"hitResult"` // 命中结果
HitSource int `json:"hitSource"` // 特征添加来源
Hidden bool `json:"hidden"` // 是否有隐藏文件
HiddenFormat string `json:"hiddenFormat"` // 隐藏文件格式
PublicOpinionInfo string `json:"publicOpinionInfo"` // 舆情信息
}
// CensorLabel 审核标签信息
type CensorLabel struct {
Code string `json:"code"` // 审核标签编码
CustomCode string `json:"customCode"` // 自定义标签编码
Name string `json:"name"` // 审核标签名称
Desc string `json:"desc"` // 审核标签描述
ParentLabelId string `json:"parentLabelId"` // 父标签ID
Depth int `json:"depth"` // 标签深度
}
// ImageLabelInfo 分类标签详情
type ImageLabelInfo struct {
Label int `json:"label"` // 标签类型
Level int `json:"level"` // 判断结果0=正常1=不确定2=确定
Rate float32 `json:"rate"` // 置信度
SubLabels []*SubLabelDetailInfo `json:"subLabels"` // 二级分类详情
Explain string `json:"explain"` // LLM解释说明
IsLlmCheck bool `json:"isLlmCheck"` // 是否LLM检测命中
}
// SubLabelDetailInfo 二级分类详情
type SubLabelDetailInfo struct {
SubLabel string `json:"subLabel"` // 二级分类标签
Level int `json:"level"` // 级别
SubLabelDepth int `json:"subLabelDepth"` // 细分类层级
SecondLabel string `json:"secondLabel"` // 二级分类
ThirdLabel string `json:"thirdLabel"` // 三级分类
RiskDescription string `json:"riskDescription"` // 风险描述
HitStrategy int `json:"hitStrategy"` // 命中标识
Rate float32 `json:"rate"` // 置信度
Explain string `json:"explain"` // 解释说明
IsLlmCheck bool `json:"isLlmCheck"` // 是否LLM命中
Details *SubLabelHitDetails `json:"details"` // 命中详情
}
// SubLabelHitDetails 二级分类命中详情
type SubLabelHitDetails struct {
Keywords []*HitKeywordInfo `json:"keywords"` // 敏感词命中
LibInfos []*HitLibInfo `json:"libInfos"` // 图片名单命中
HitInfos []*HitInfo `json:"hitInfos"` // 其他命中信息
Anticheat *AnticheatHitInfo `json:"anticheat"` // 反作弊命中
Llm *LlmKeywordInfo `json:"llm"` // 大模型关键词
}
// HitKeywordInfo 敏感词命中信息
type HitKeywordInfo struct {
Type int `json:"type"` // 命中类型
Word string `json:"word"` // 敏感词
Entity string `json:"entity"` // 图片名单URL
HitCount int `json:"hitCount"` // 命中次数
Value string `json:"value"` // 值
Group string `json:"group"` // 分组
X1 float32 `json:"x1"` // 坐标
Y1 float32 `json:"y1"` // 坐标
X2 float32 `json:"x2"` // 坐标
Y2 float32 `json:"y2"` // 坐标
ReleaseTime int64 `json:"releaseTime"` // 释放时间
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
}
// HitLibInfo 图片名单命中信息
type HitLibInfo struct {
Type int `json:"type"` // 命中类型
Entity string `json:"entity"` // 图片名单URL
HitCount int `json:"hitCount"` // 命中次数
ReleaseTime int64 `json:"releaseTime"` // 释放时间
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
}
// HitInfo 其他命中信息
type HitInfo struct {
Type int `json:"type"` // 命中类型
Value string `json:"value"` // 值
Group string `json:"group"` // 分组
X1 float32 `json:"x1"` // 坐标
Y1 float32 `json:"y1"` // 坐标
X2 float32 `json:"x2"` // 坐标
Y2 float32 `json:"y2"` // 坐标
}
// AnticheatHitInfo 反作弊命中信息
type AnticheatHitInfo struct {
HitType int `json:"hitType"` // 命中类型
}
// LlmKeywordInfo 大模型关键词信息
type LlmKeywordInfo struct {
Keyword string `json:"keyword"` // 关键词
}
// DetailMarkInfo 细节标注信息
type DetailMarkInfo struct {
Position []*MarkPointInfo `json:"position"` // 标注位置
CensorLabels []*CensorLabel `json:"censorLabels"` // 标注标签
Desc string `json:"desc"` // 标注备注
}
// MarkPointInfo 标注点坐标
type MarkPointInfo struct {
X float32 `json:"x"` // X坐标
Y float32 `json:"y"` // Y坐标
}
// CustomLabelInfo 客户自定义标签
type CustomLabelInfo struct {
Name string `json:"name"` // 名称
Code string `json:"code"` // 编码
Depth int `json:"depth"` // 深度
}
// CensorExtensionInfo 人审拓展字段
type CensorExtensionInfo struct {
QualityInspectionTaskId string `json:"qualityInspectionTaskId"` // 质检任务ID
InspTaskCreateTime float64 `json:"inspTaskCreateTime"` // 质检任务创建时间
QualityInspectionType float64 `json:"qualityInspectionType"` // 质检类型
}
// StrategyVersionInfo 策略版本信息
type StrategyVersionInfo struct {
Label int `json:"label"` // 垃圾类别
Version string `json:"version"` // 版本号
}
// ProcessImageCallback 处理图片检测回调(推送模式)
func (s *ImageDetectionService) ProcessImageCallback(ctx context.Context, callbackData string) error {
if callbackData == "" {
return fmt.Errorf("回调数据不能为空")
}
var data ImageCallbackData
if err := json.Unmarshal([]byte(callbackData), &data); err != nil {
g.Log().Errorf(ctx, "解析回调数据失败: %v", err)
return fmt.Errorf("解析回调数据失败: %w", err)
}
if data.Antispam == nil {
return fmt.Errorf("回调数据格式错误缺少antispam字段")
}
g.Log().Infof(ctx, "处理图片检测结果 - taskId: %s, suggestion: %d, resultType: %d",
data.Antispam.TaskId, data.Antispam.Suggestion, data.Antispam.ResultType)
// TODO: 业务逻辑,如保存数据库、触发后续流程等
// 可使用完整字段data.Antispam.Labels, data.Antispam.CensorLabels, data.Antispam.Remark 等
return nil
}

View File

@@ -0,0 +1,51 @@
package yidun
import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/text/v5/check/async/single"
)
// TextDetectionService 文本检测服务
type TextDetectionService struct{}
var TextDetection = new(TextDetectionService)
// DetectText 提交文本异步检测任务
func (s *TextDetectionService) DetectText(ctx context.Context, req *single.TextAsyncCheckRequest) (string, error) {
if DefaultClients == nil || DefaultClients.TextClient == nil {
return "", fmt.Errorf("易盾文本检测客户端未初始化")
}
response, err := DefaultClients.TextClient.AsyncCheckText(req)
if err != nil {
g.Log().Errorf(ctx, "文本检测提交失败: %v", err)
return "", fmt.Errorf("文本检测提交失败: %w", err)
}
if response.GetCode() != 200 {
g.Log().Errorf(ctx, "文本检测API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
return "", fmt.Errorf("文本检测API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
}
var taskID string
if response.Result != nil && len(response.Result.CheckTexts) > 0 {
taskID = *response.Result.CheckTexts[0].TaskID
}
g.Log().Infof(ctx, "文本检测任务提交成功, taskID: %s", taskID)
return taskID, nil
}
// GetTextResult 获取文本检测结果
func (s *TextDetectionService) GetTextResult(ctx context.Context, taskID string) (interface{}, error) {
if DefaultClients == nil || DefaultClients.TextClient == nil {
return nil, fmt.Errorf("易盾文本检测客户端未初始化")
}
g.Log().Infof(ctx, "查询文本检测结果, taskID: %s", taskID)
// TODO: 根据实际业务需求实现查询逻辑
return nil, nil
}

View File

@@ -0,0 +1,714 @@
package yidun
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/gogf/gf/v2/frame/g"
audiocallback "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/audio/callback/v4/response"
videocallback "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/video/callback/v4/response"
callbackrequest "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/videosolution/callback/v2/request"
callbackresponse "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/videosolution/callback/v2/response"
vsrequest "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/videosolution/submit/v2/request"
submitresponse "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/videosolution/submit/v2/response"
)
// VideoDetectionService 视频检测服务
type VideoDetectionService struct{}
var VideoDetection = new(VideoDetectionService)
var (
ErrVideoStillProcessing = errors.New("视频仍在检测中,请稍后重试")
ErrVideoResultNotFound = errors.New("未找到视频检测结果")
)
// VideoSubmitResult 视频检测提交结果
type VideoSubmitResult struct {
TaskID string `json:"taskId"` // 任务ID
DataID string `json:"dataId"` // 数据ID
DealingCount int64 `json:"dealingCount"` // 缓冲池排队待处理数据量
}
// DetectVideo 提交视频检测任务,返回完整响应
func (s *VideoDetectionService) DetectVideo(ctx context.Context, videoURL, dataID string, callbackURL string) (*VideoSubmitResult, error) {
if DefaultClients == nil || DefaultClients.VideoClient == nil {
return nil, fmt.Errorf("易盾视频检测客户端未初始化")
}
if videoURL == "" {
return nil, fmt.Errorf("视频URL不能为空")
}
g.Log().Infof(ctx, "视频检测任务提交, url: %s", videoURL)
// 创建请求
req := vsrequest.NewVideoSolutionSubmitV2Req()
req.SetURL(videoURL)
req.SetDataID(dataID)
req.SetUniqueKey(dataID)
if callbackURL != "" {
req.SetCallbackURL(callbackURL)
}
// 调用API
response, err := DefaultClients.VideoClient.Submit(req)
if err != nil {
g.Log().Errorf(ctx, "视频检测提交失败: %v", err)
return nil, fmt.Errorf("视频检测提交失败: %w", err)
}
if response.GetCode() != 200 {
g.Log().Errorf(ctx, "视频检测API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
return nil, fmt.Errorf("视频检测API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
}
result := &VideoSubmitResult{}
if response.Result != nil {
if response.Result.TaskID != nil {
result.TaskID = *response.Result.TaskID
}
if response.Result.DataID != nil {
result.DataID = *response.Result.DataID
}
if response.Result.DealingCount != nil {
result.DealingCount = *response.Result.DealingCount
}
}
g.Log().Infof(ctx, "视频检测任务提交成功, taskID: %s, dealingCount: %d", result.TaskID, result.DealingCount)
return result, nil
}
// VideoResult 视频检测完整结果
// 包含易盾返回的完整检测信息文本、图片、音频、ASR等
type VideoResult struct {
TaskID string `json:"taskId"` // 任务ID
Status int `json:"status"` // 检测状态0=未开始1=检测中2=检测完成
Suggestion int `json:"suggestion"` // 处置建议0=通过1=嫌疑2=不通过
Label int `json:"label"` // 垃圾类型
ResultType int `json:"resultType"` // 结果类型1=机器结果2=人审结果
DataID string `json:"dataId"` // 数据ID
CensorTime int64 `json:"censorTime"` // 审核完成时间(毫秒)
Duration int64 `json:"duration"` // 视频时长(毫秒)
// 完整证据信息
Antispam *callbackresponse.VideoSolutionAntispamCallbackV2Response `json:"antispam,omitempty"` // 反垃圾检测结果
Language *audiocallback.AudioLanguageCallbackV4Response `json:"language,omitempty"` // 语言检测结果
Voice *audiocallback.AudioVoiceCallbackV4Response `json:"voice,omitempty"` // 语音识别结果
Asr *audiocallback.AudioAsrCallbackV4Response `json:"asr,omitempty"` // ASR语音转文字结果
Ocr *videocallback.VideoCallbackOcrV4Response `json:"ocr,omitempty"` // OCR文字识别结果
Discern *videocallback.VideoCallbackDiscernV4Response `json:"discern,omitempty"` // 画面识别结果
Logo *videocallback.VideoCallbackLogoV4Response `json:"logo,omitempty"` // Logo识别结果
Face *videocallback.VideoCallbackFaceV4Response `json:"face,omitempty"` // 人脸识别结果
Aigc *videocallback.VideoCallbackAigcV4Response `json:"aigc,omitempty"` // AIGC识别结果
Quality *callbackresponse.VideoSolutionQualityCallbackV2Response `json:"quality,omitempty"` // 音视频质量检测结果
}
// GetVideoResult 获取视频检测结果(轮询模式)
// 返回完整的检测详情包括文本、图片、音频、ASR等
func (s *VideoDetectionService) GetVideoResult(ctx context.Context, taskID string) (*VideoResult, error) {
if DefaultClients == nil || DefaultClients.VideoClient == nil {
return nil, fmt.Errorf("易盾视频检测客户端未初始化")
}
g.Log().Infof(ctx, "查询视频检测结果, taskID: %s", taskID)
req := callbackrequest.NewVideoSolutionCallbackV2Request()
req.SetYidunRequestId(taskID)
response, err := DefaultClients.VideoClient.Callback(req)
if err != nil {
g.Log().Errorf(ctx, "查询视频检测结果失败: %v", err)
return nil, fmt.Errorf("查询视频检测结果失败: %w", err)
}
if response.GetCode() != 200 {
g.Log().Errorf(ctx, "查询视频检测结果API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
return nil, fmt.Errorf("查询视频检测结果API错误: code=%d, msg=%s", response.GetCode(), response.GetMsg())
}
if response.Result == nil || len(*response.Result) == 0 {
g.Log().Warningf(ctx, "未找到视频检测结果, taskID: %s", taskID)
return nil, ErrVideoStillProcessing
}
// 查找指定taskID的结果
for _, item := range *response.Result {
if item.Antispam != nil && item.Antispam.TaskID != nil && *item.Antispam.TaskID == taskID {
result := &VideoResult{
Antispam: item.Antispam,
Language: item.Language,
Voice: item.Voice,
Asr: item.Asr,
Ocr: item.Ocr,
Discern: item.Discern,
Logo: item.Logo,
Face: item.Face,
Aigc: item.Aigc,
Quality: item.Quality,
}
result.TaskID = taskID
if item.Antispam.Status != nil {
result.Status = *item.Antispam.Status
}
if item.Antispam.Suggestion != nil {
result.Suggestion = *item.Antispam.Suggestion
}
if item.Antispam.Label != nil {
result.Label = *item.Antispam.Label
}
if item.Antispam.ResultType != nil {
result.ResultType = *item.Antispam.ResultType
}
if item.Antispam.DataID != nil {
result.DataID = *item.Antispam.DataID
}
if item.Antispam.CensorTime != nil {
result.CensorTime = *item.Antispam.CensorTime
}
if item.Antispam.Duration != nil {
result.Duration = *item.Antispam.Duration
}
return result, nil
}
}
g.Log().Warningf(ctx, "未找到指定的taskID: %s", taskID)
return nil, ErrVideoResultNotFound
}
// PollVideoResult 轮询获取视频检测结果
func (s *VideoDetectionService) PollVideoResult(ctx context.Context, taskID string, interval, maxWait time.Duration) (*VideoResult, error) {
if interval <= 0 {
interval = 1 * time.Second
}
if maxWait <= 0 {
maxWait = 5 * time.Minute
}
startTime := time.Now()
g.Log().Infof(ctx, "开始轮询视频检测结果, taskID: %s, interval: %v, maxWait: %v", taskID, interval, maxWait)
for time.Since(startTime) < maxWait {
select {
case <-ctx.Done():
return nil, fmt.Errorf("轮询取消: %w", ctx.Err())
default:
}
result, err := s.GetVideoResult(ctx, taskID)
if err == nil {
g.Log().Infof(ctx, "轮询成功, taskID: %s, elapsed: %v", taskID, time.Since(startTime))
return result, nil
}
if errors.Is(err, ErrVideoResultNotFound) {
g.Log().Infof(ctx, "视频仍在检测中,继续轮询, taskID: %s, elapsed: %v", taskID, time.Since(startTime))
} else {
g.Log().Warningf(ctx, "查询出错,继续重试, taskID: %s, error: %v", taskID, err)
}
time.Sleep(interval)
}
return nil, fmt.Errorf("轮询超时,已等待 %v", time.Since(startTime))
}
// GetSDKSubmitResponse 获取SDK原始提交响应包含完整易盾返回信息
func (s *VideoDetectionService) GetSDKSubmitResponse(ctx context.Context, videoURL, dataID string, callbackURL string) (*submitresponse.VideoSolutionSubmitV2Response, error) {
if DefaultClients == nil || DefaultClients.VideoClient == nil {
return nil, fmt.Errorf("易盾视频检测客户端未初始化")
}
req := vsrequest.NewVideoSolutionSubmitV2Req()
req.SetURL(videoURL)
req.SetDataID(dataID)
req.SetUniqueKey(dataID)
if callbackURL != "" {
req.SetCallbackURL(callbackURL)
}
response, err := DefaultClients.VideoClient.Submit(req)
if err != nil {
return nil, err
}
return response, nil
}
// GetSDKCallbackResponse 获取SDK原始回调响应包含完整易盾返回信息
func (s *VideoDetectionService) GetSDKCallbackResponse(ctx context.Context, taskID string) (*callbackresponse.VideoSolutionCallbackV2Response, error) {
if DefaultClients == nil || DefaultClients.VideoClient == nil {
return nil, fmt.Errorf("易盾视频检测客户端未初始化")
}
req := callbackrequest.NewVideoSolutionCallbackV2Request()
req.SetYidunRequestId(taskID)
response, err := DefaultClients.VideoClient.Callback(req)
if err != nil {
return nil, err
}
return response, nil
}
// =============================================================================
// 视频检测结果推送模式(易盾主动回调)
// =============================================================================
// VideoCallbackData 推送模式回调数据完整结构
type VideoCallbackData struct {
Antispam *VideoCallbackAntispam `json:"antispam"` // 反垃圾检测结果
}
type VideoCallbackAntispam struct {
TaskID string `json:"taskId"` // 任务ID
DataID string `json:"dataId"` // 客户数据ID
Callback string `json:"callback"` // 回调参数
Suggestion int `json:"suggestion"` // 处置建议0=通过1=嫌疑2=不通过
Status int `json:"status"` // 检测状态0=未开始1=检测中2=检测成功3=检测失败
ResultType int `json:"resultType"` // 结果类型1=机器结果2=人审结果
CensorRound int `json:"censorRound"` // 人审轮次
Censor string `json:"censor"` // 人审操作人
CensorSource int `json:"censorSource"` // 审核来源0=易盾人审1=客户人审2=易盾机审
CheckTime int64 `json:"checkTime"` // 机器检测结束时间
CensorTime int64 `json:"censorTime"` // 人工审核完成时间
Duration int64 `json:"duration"` // 音视频时长(毫秒)
DurationMs int64 `json:"durationMs"` // 音频时长(毫秒)
Label int `json:"label"` // 一级垃圾类型
SecondLabel string `json:"secondLabel"` // 二级垃圾类型
ThirdLabel string `json:"thirdLabel"` // 三级垃圾类型
RiskDescription string `json:"riskDescription"` // 风险描述
PicCount int64 `json:"picCount"` // 截图数量
CensorLabels []*VideoCensorLabel `json:"censorLabels"` // 审核标签
CensorExtension *VideoCensorExtension `json:"censorExtension"` // 质检扩展结果
Evidences *VideoCallbackEvidence `json:"evidences"` // 机器检测证据信息
SolutionExtra *VideoSolutionExtra `json:"solutionExtra"` // 额外信息
ReviewEvidences *VideoReviewEvidence `json:"reviewEvidences"` // 人审证据信息
}
// VideoCensorLabel 审核标签
type VideoCensorLabel struct {
Code string `json:"code"` // 审核标签编码
Desc string `json:"desc"` // 审核标签描述
Name string `json:"name"` // 审核标签名称
CustomCode string `json:"customCode"` // 自定义标签编码
ParentLabelId string `json:"parentLabelId"` // 父标签ID
Depth int `json:"depth"` // 标签深度
}
// VideoCensorExtension 质检扩展结果
type VideoCensorExtension struct {
QualityInspectionTaskId string `json:"qualityInspectionTaskId"` // 质检任务ID
QualityInspectionType int `json:"qualityInspectionType"` // 质检类型
InspTaskCreateTime int64 `json:"inspTaskCreateTime"` // 质检任务创建时间
}
// VideoCallbackEvidence 机器检测证据信息
type VideoCallbackEvidence struct {
Text *VideoTextEvidence `json:"text,omitempty"` // 文本证据
Images *[]VideoImageEvidence `json:"images,omitempty"` // 图片证据
Audio *VideoAudioEvidence `json:"audio,omitempty"` // 音频证据
Video *VideoVideoEvidence `json:"video,omitempty"` // 视频证据
}
// VideoTextEvidence 文本证据
type VideoTextEvidence struct {
TaskID string `json:"taskId"` // 任务ID
DataID string `json:"dataId"` // 数据ID
Suggestion int `json:"suggestion"` // 处置建议
ResultType int `json:"resultType"` // 结果类型
CensorType int `json:"censorType"` // 审核类型
IsRelatedHit bool `json:"isRelatedHit"` // 是否关联命中
Labels []*VideoTextLabel `json:"labels"` // 分类标签
}
// VideoTextLabel 文本分类标签
type VideoTextLabel struct {
Label int `json:"label"` // 标签类型
Level int `json:"level"` // 级别
SubLabels []*VideoTextSubLabel `json:"subLabels"` // 二级分类
}
// VideoTextSubLabel 文本二级分类
type VideoTextSubLabel struct {
SubLabel string `json:"subLabel"` // 二级分类
SubLabelDepth int `json:"subLabelDepth"` // 细分类层级
SecondLabel string `json:"secondLabel"` // 二级分类
ThirdLabel string `json:"thirdLabel"` // 三级分类
Details *VideoTextSubLabelDetail `json:"details"` // 命中详情
}
// VideoTextSubLabelDetail 文本二级分类命中详情
type VideoTextSubLabelDetail struct {
Keywords *[]VideoTextKeyword `json:"keywords,omitempty"` // 敏感词
LibInfos *[]VideoTextLibInfo `json:"libInfos,omitempty"` // 名单信息
Anticheat *VideoTextAnticheat `json:"anticheat,omitempty"` // 反作弊
HitInfos *[]VideoTextHitInfo `json:"hitInfos,omitempty"` // 其他命中
}
// VideoTextKeyword 敏感词信息
type VideoTextKeyword struct {
Word string `json:"word"` // 敏感词
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
}
// VideoTextLibInfo 名单信息
type VideoTextLibInfo struct {
Type int `json:"type"` // 名单类型
Entity string `json:"entity"` // 名单内容
ReleaseTime int64 `json:"releaseTime"` // 释放时间
}
// VideoTextAnticheat 反作弊信息
type VideoTextAnticheat struct {
HitType int `json:"hitType"` // 命中类型
}
// VideoTextHitInfo 其他命中信息
type VideoTextHitInfo struct {
Value string `json:"value"` // 命中值
Positions *[]VideoTextHitPosition `json:"positions"` // 命中位置
}
// VideoTextHitPosition 命中位置
type VideoTextHitPosition struct {
FieldName string `json:"fieldName"` // 字段名
StartPos int `json:"startPos"` // 开始位置
EndPos int `json:"endPos"` // 结束位置
}
// VideoImageEvidence 图片证据
type VideoImageEvidence struct {
Name string `json:"name"` // 图片名称
DataID string `json:"dataId"` // 数据ID
Suggestion int `json:"suggestion"` // 处置建议
ResultType int `json:"resultType"` // 结果类型
Status int `json:"status"` // 状态
CensorType int `json:"censorType"` // 审核类型
Labels []*VideoImageLabel `json:"labels"` // 分类标签
TaskId string `json:"taskId"` // 任务ID
}
// VideoImageLabel 图片分类标签
type VideoImageLabel struct {
Label int `json:"label"` // 标签类型
Level int `json:"level"` // 级别
Rate float32 `json:"rate"` // 置信度
SubLabels []*VideoImageSubLabel `json:"subLabels"` // 二级分类
}
// VideoImageSubLabel 图片二级分类
type VideoImageSubLabel struct {
SubLabel interface{} `json:"subLabel"` // 二级分类标签
SubLabelDepth int `json:"subLabelDepth"` // 细分类层级
SecondLabel string `json:"secondLabel"` // 二级分类
ThirdLabel string `json:"thirdLabel"` // 三级分类
HitStrategy int `json:"hitStrategy"` // 命中策略
Rate float32 `json:"rate"` // 置信度
Details *VideoImageSubDetail `json:"details"` // 命中详情
Explain string `json:"explain"` // 解释
IsLlmCheck bool `json:"isLlmCheck"` // 是否LLM命中
}
// VideoImageSubDetail 图片二级分类命中详情
type VideoImageSubDetail struct {
Keywords *[]VideoImageHitInfo `json:"keywords,omitempty"` // 敏感词
LibInfos *[]VideoImageLibInfo `json:"libInfos,omitempty"` // 名单信息
HitInfos *[]VideoImageHitInfo `json:"hitInfos,omitempty"` // 其他命中
Anticheat *VideoImageAnticheat `json:"anticheat,omitempty"` // 反作弊
Llm *VideoImageLlm `json:"llm,omitempty"` // 大模型
}
// VideoImageHitInfo 图片命中信息
type VideoImageHitInfo struct {
Type int `json:"type"` // 命中类型
Word string `json:"word"` // 敏感词
Entity string `json:"entity"` // 名单URL
HitCount int `json:"hitCount"` // 命中次数
Value string `json:"value"` // 值
Group string `json:"group"` // 分组
X1 float32 `json:"x1"` // 坐标
Y1 float32 `json:"y1"` // 坐标
X2 float32 `json:"x2"` // 坐标
Y2 float32 `json:"y2"` // 坐标
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
}
// VideoImageLibInfo 图片名单信息
type VideoImageLibInfo struct {
Type int `json:"type"` // 名单类型
Entity string `json:"entity"` // 名单URL
HitCount int `json:"hitCount"` // 命中次数
ReleaseTime int64 `json:"releaseTime"` // 释放时间
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
}
// VideoImageAnticheat 反作弊信息
type VideoImageAnticheat struct {
HitType int `json:"hitType"` // 命中类型
}
// VideoImageLlm 大模型信息
type VideoImageLlm struct {
Keyword string `json:"keyword"` // 关键词
}
// VideoAudioEvidence 音频证据
type VideoAudioEvidence struct {
TaskID string `json:"taskId"` // 任务ID
DataID string `json:"dataId"` // 数据ID
Status int `json:"status"` // 状态
Suggestion int `json:"suggestion"` // 处置建议
Label int `json:"label"` // 标签
ResultType int `json:"resultType"` // 结果类型
Callback string `json:"callback"` // 回调参数
CensorSource int `json:"censorSource"` // 审核来源
CensorTime int64 `json:"censorTime"` // 审核时间
Duration int64 `json:"duration"` // 音频时长
Segments []*VideoAudioSegment `json:"segments"` // 音频片段
}
// VideoAudioSegment 音频片段
type VideoAudioSegment struct {
StartTime int `json:"startTime"` // 开始时间(秒)
EndTime int `json:"endTime"` // 结束时间(秒)
StartTimeMillis int64 `json:"startTimeMillis"` // 开始时间(毫秒)
EndTimeMillis int64 `json:"endTimeMillis"` // 结束时间(毫秒)
Content string `json:"content"` // 语音识别原文
Type int `json:"type"` // 片段类型0=语音识别1=声纹检测
LeaderName string `json:"leaderName"` // 声纹检测人名
Labels []*VideoAudioLabel `json:"labels"` // 分类标签
Url string `json:"url"` // 音频URL
}
// VideoAudioLabel 音频分类标签
type VideoAudioLabel struct {
Label int `json:"label"` // 标签类型
Level int `json:"level"` // 级别
SubLabels []*VideoAudioSubLabel `json:"subLabels"` // 二级分类
}
// VideoAudioSubLabel 音频二级分类
type VideoAudioSubLabel struct {
SubLabel string `json:"subLabel"` // 细分类
SubLabelDepth int `json:"subLabelDepth"` // 细分类层级
SecondLabel string `json:"secondLabel"` // 二级分类
ThirdLabel string `json:"thirdLabel"` // 三级分类
SuggestionRiskLevel int `json:"suggestionRiskLevel"` // 嫌疑级别
Rate float64 `json:"rate"` // 置信度
RiskDescription string `json:"riskDescription"` // 风险描述
Details *VideoAudioSubLabelDetail `json:"details"` // 命中详情
}
// VideoAudioSubLabelDetail 音频二级分类命中详情
type VideoAudioSubLabelDetail struct {
HitInfos *[]VideoAudioHitInfo `json:"hitInfos,omitempty"` // 命中内容
Keywords *[]VideoAudioKeyword `json:"keywords,omitempty"` // 自定义敏感词
LibInfos *[]VideoAudioLibInfo `json:"libInfos,omitempty"` // 自定义名单
}
// VideoAudioHitInfo 音频命中内容
type VideoAudioHitInfo struct {
Value string `json:"value"` // 命中敏感词或声纹检测分值
StartTime int `json:"startTime"` // 开始时间
EndTime int `json:"endTime"` // 结束时间
}
// VideoAudioKeyword 自定义敏感词
type VideoAudioKeyword struct {
Word string `json:"word"` // 敏感词
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
}
// VideoAudioLibInfo 自定义名单
type VideoAudioLibInfo struct {
ListType int `json:"listType"` // 名单类型
Entity string `json:"entity"` // 名单内容
}
// VideoVideoEvidence 视频证据
type VideoVideoEvidence struct {
TaskID string `json:"taskId"` // 任务ID
DataID string `json:"dataId"` // 数据ID
Status int `json:"status"` // 状态
Suggestion int `json:"suggestion"` // 处置建议
ResultType int `json:"resultType"` // 结果类型
CensorSource int `json:"censorSource"` // 审核来源
CensorTime int64 `json:"censorTime"` // 审核时间
Pictures []*VideoPicture `json:"pictures"` // 截图列表
}
// VideoPicture 截图信息
type VideoPicture struct {
Type int `json:"type"` // 类型
URL string `json:"url"` // 图片URL
StartTime int64 `json:"startTime"` // 开始时间
EndTime int64 `json:"endTime"` // 结束时间
Labels []*VideoPictureLabel `json:"labels"` // 分类标签
CensorSource int `json:"censorSource"` // 审核来源
FrontPics []*VideoRelatedPic `json:"frontPics"` // 关联前帧
BackPics []*VideoRelatedPic `json:"backPics"` // 关联后帧
PictureID string `json:"pictureId"` // 图片ID
}
// VideoPictureLabel 截图分类标签
type VideoPictureLabel struct {
Label int `json:"label"` // 标签类型
Level int `json:"level"` // 级别
Rate float32 `json:"rate"` // 置信度
SubLabels []*VideoPictureSubLabel `json:"subLabels"` // 二级分类
}
// VideoPictureSubLabel 截图二级分类
type VideoPictureSubLabel struct {
SubLabel interface{} `json:"subLabel"` // 二级分类标签
SubLabelDepth int `json:"subLabelDepth"` // 细分类层级
SecondLabel string `json:"secondLabel"` // 二级分类
ThirdLabel string `json:"thirdLabel"` // 三级分类
HitStrategy int `json:"hitStrategy"` // 命中策略
Rate float32 `json:"rate"` // 置信度
Details *VideoPictureSubDetail `json:"details"` // 命中详情
Explain string `json:"explain"` // 解释
IsLlmCheck bool `json:"isLlmCheck"` // 是否LLM命中
}
// VideoPictureSubDetail 截图二级分类命中详情
type VideoPictureSubDetail struct {
Keywords *[]VideoPictureHitInfo `json:"keywords,omitempty"` // 敏感词
LibInfos *[]VideoPictureLibInfo `json:"libInfos,omitempty"` // 名单信息
HitInfos *[]VideoPictureHitInfo `json:"hitInfos,omitempty"` // 其他命中
Anticheat *VideoPictureAnticheat `json:"anticheat,omitempty"` // 反作弊
Llm *VideoPictureLlm `json:"llm,omitempty"` // 大模型
}
// VideoPictureHitInfo 截图命中信息
type VideoPictureHitInfo struct {
Value string `json:"value"` // 命中值
Group string `json:"group"` // 分组
Type int `json:"type"` // 类型
X1 float32 `json:"x1"` // 坐标
Y1 float32 `json:"y1"` // 坐标
X2 float32 `json:"x2"` // 坐标
Y2 float32 `json:"y2"` // 坐标
}
// VideoPictureLibInfo 截图名单信息
type VideoPictureLibInfo struct {
Type int `json:"type"` // 名单类型
Entity string `json:"entity"` // 名单URL
HitCount int `json:"hitCount"` // 命中次数
ReleaseTime int64 `json:"releaseTime"` // 释放时间
StrategyGroupName string `json:"strategyGroupName"` // 策略组名称
StrategyGroupId int64 `json:"strategyGroupId"` // 策略组ID
}
// VideoPictureAnticheat 反作弊信息
type VideoPictureAnticheat struct {
HitType int `json:"hitType"` // 命中类型
}
// VideoPictureLlm 大模型信息
type VideoPictureLlm struct {
Keyword string `json:"keyword"` // 关键词
}
// VideoRelatedPic 关联截图
type VideoRelatedPic struct {
URL string `json:"url"` // 图片URL
}
// VideoSolutionExtra 额外信息
type VideoSolutionExtra struct {
FailUnit *VideoFailUnit `json:"failUnit,omitempty"` // 失败单元
}
// VideoFailUnit 失败单元
type VideoFailUnit struct {
Images *[]VideoImageFailUnit `json:"images,omitempty"` // 图片失败单元
Audio *VideoTargetFailUnit `json:"audio,omitempty"` // 音频失败单元
Video *VideoTargetFailUnit `json:"video,omitempty"` // 视频失败单元
}
// VideoImageFailUnit 图片失败单元
type VideoImageFailUnit struct {
FailureReason int `json:"failureReason"` // 失败原因
Name string `json:"name"` // 名称
}
// VideoTargetFailUnit 目标失败单元
type VideoTargetFailUnit struct {
FailureReason int `json:"failureReason"` // 失败原因
}
// VideoReviewEvidence 人审证据信息
type VideoReviewEvidence struct {
Description string `json:"description"` // 描述
Detail string `json:"detail"` // 详情
Texts *[]VideoReviewText `json:"texts"` // 文本证据
Images *[]VideoReviewImage `json:"images"` // 图片证据
Audios *[]VideoReviewAudio `json:"audios"` // 音频证据
Videos *[]VideoReviewVideo `json:"videos"` // 视频证据
}
// VideoReviewText 人审文本证据
type VideoReviewText struct {
Snippet string `json:"snippet"` // 片段
Description string `json:"description"` // 描述
}
// VideoReviewImage 人审图片证据
type VideoReviewImage struct {
URL string `json:"url"` // 图片URL
Description string `json:"description"` // 描述
}
// VideoReviewAudio 人审音频证据
type VideoReviewAudio struct {
StartTime int64 `json:"startTime"` // 开始时间
EndTime int64 `json:"endTime"` // 结束时间
Description string `json:"description"` // 描述
}
// VideoReviewVideo 人审视频证据
type VideoReviewVideo struct {
StartTime int64 `json:"startTime"` // 开始时间
EndTime int64 `json:"endTime"` // 结束时间
URL string `json:"url"` // 视频URL
Description string `json:"description"` // 描述
}
// ProcessVideoCallback 处理视频检测回调(推送模式)
func (s *VideoDetectionService) ProcessVideoCallback(ctx context.Context, callbackData string) error {
if callbackData == "" {
return fmt.Errorf("回调数据不能为空")
}
var data VideoCallbackData
if err := json.Unmarshal([]byte(callbackData), &data); err != nil {
g.Log().Errorf(ctx, "解析视频回调数据失败: %v", err)
return fmt.Errorf("解析视频回调数据失败: %w", err)
}
if data.Antispam == nil {
return fmt.Errorf("视频回调数据格式错误缺少antispam字段")
}
g.Log().Infof(ctx, "处理视频检测结果 - taskId: %s, suggestion: %d, resultType: %d, status: %d",
data.Antispam.TaskID, data.Antispam.Suggestion, data.Antispam.ResultType, data.Antispam.Status)
// TODO: 业务逻辑,如保存数据库、触发后续流程等
// 可使用完整字段data.Antispam.Evidences, data.Antispam.CensorLabels, data.Antispam.Duration 等
return nil
}

View File

@@ -0,0 +1,53 @@
package yidun
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/image/v5"
text "github.com/yidun/yidun-golang-sdk/yidun/service/antispam/text"
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/videosolution"
)
// YidunClients 易盾客户端集合直接使用SDK客户端
type YidunClients struct {
TextClient *text.TextClient
ImageClient *image.ImageClient
VideoClient *videosolution.VideoSolutionClient
}
var DefaultClients *YidunClients
// InitYidunClients 初始化所有易盾客户端
func InitYidunClients(ctx context.Context) error {
clients := &YidunClients{}
// 文本检测
secretId := g.Cfg().MustGet(ctx, "yidun.text.secret_id").String()
secretKey := g.Cfg().MustGet(ctx, "yidun.text.secret_key").String()
if secretId != "" && secretKey != "" {
clients.TextClient = text.NewTextClientWithAccessKey(secretId, secretKey)
g.Log().Info(ctx, "文本检测客户端初始化成功")
}
// 图片检测
secretId = g.Cfg().MustGet(ctx, "yidun.image.secret_id").String()
secretKey = g.Cfg().MustGet(ctx, "yidun.image.secret_key").String()
businessId := g.Cfg().MustGet(ctx, "yidun.image.business_id").String()
if secretId != "" && secretKey != "" {
clients.ImageClient = image.NewImageClientWithAccessKey(secretId, secretKey)
g.Log().Infof(ctx, "图片检测客户端初始化成功, business_id: %s", businessId)
}
// 视频检测
secretId = g.Cfg().MustGet(ctx, "yidun.video.secret_id").String()
secretKey = g.Cfg().MustGet(ctx, "yidun.video.secret_key").String()
businessId = g.Cfg().MustGet(ctx, "yidun.video.business_id").String()
if secretId != "" && secretKey != "" {
clients.VideoClient = videosolution.NewVideoSolutionClientWithAccessKey(secretId, secretKey)
g.Log().Infof(ctx, "视频检测客户端初始化成功, business_id: %s", businessId)
}
DefaultClients = clients
return nil
}