feat:增加代码逻辑

This commit is contained in:
jiaji.H 2025-10-15 14:52:37 +08:00
parent 8aee1f4641
commit 8d3115d613
17 changed files with 1578 additions and 2 deletions

34
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,34 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"envFile": "${workspaceFolder}/conf/alibabacloud.env",
"env": {
"GOPATH":"C:\\Users\\lenovo\\go",
"GOOS":"windows"
},
"program": "${workspaceFolder}/cmd",
"args":[]
},
{
"name": "Run main.go",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd",
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/conf/alibabacloud.env",
"env": {
"DEBUG": "true"
},
"dlvFlags": ["--check-go-version=false"]
}
]
}

22
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#fa1b49",
"activityBar.background": "#fa1b49",
"activityBar.foreground": "#e7e7e7",
"activityBar.inactiveForeground": "#e7e7e799",
"activityBarBadge.background": "#155e02",
"activityBarBadge.foreground": "#e7e7e7",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#fa1b49",
"statusBar.background": "#dd0531",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#fa1b49",
"statusBarItem.remoteBackground": "#dd0531",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#dd0531",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#dd053199",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.color": "#dd0531"
}

174
README.md
View File

@ -1,3 +1,173 @@
# contentSecurity
# 阿里云内容安全2.0 Demo
阿里云内容安全2.0服务demo支持对视频、图片和文字进行内容安全审核。
这是一个使用Go语言编写的阿里云内容安全2.0服务演示程序,支持对视频、图片和文字进行内容安全审核。
## 功能特性
- 🖼️ **图片内容安全审核**
- 支持通过URL或本地文件进行图片审核
- 支持多种图片格式JPG、PNG、BMP、GIF、WEBP
- 检测场景色情、暴恐、广告、直播、Logo等
- 📝 **文本内容安全审核**
- 支持单条文本和批量文本审核
- 检测场景:反垃圾信息
- 提供详细的违规内容定位信息
- 🎥 **视频内容安全审核**
- 支持异步和同步视频审核
- 自动截帧分析
- 提供帧级别的违规内容定位
## 环境要求
- Go 1.21 或更高版本
- 阿里云账号并开通内容安全2.0服务
- 有效的阿里云AccessKey
## 安装和配置
### 1. 克隆项目
```bash
git clone <your-repo-url>
cd content-security-demo
```
### 2. 安装依赖
```bash
go mod tidy
```
### 3. 配置阿里云AccessKey
有两种方式配置AccessKey
#### 方式一:使用环境变量
```bash
export ALIBABA_CLOUD_ACCESS_KEY_ID="your_access_key_id"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your_access_key_secret"
```
#### 方式二:使用.env文件
1. 复制配置文件:
```bash
cp env.example .env
```
2. 编辑`.env`文件填入您的AccessKey信息
```env
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id_here
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret_here
ALIBABA_CLOUD_REGION=cn-shanghai
ALIBABA_CLOUD_ENDPOINT=green.cn-shanghai.aliyuncs.com
```
### 4. 运行程序
```bash
go run .
```
## 使用说明
运行程序后,会显示交互式菜单:
```
=== 阿里云内容安全2.0 Demo ===
支持视频、图片、文字内容安全审核
请选择要测试的功能:
1. 图片内容安全审核
2. 文本内容安全审核
3. 视频内容安全审核
4. 退出
```
### 图片审核
- **通过URL审核**输入图片的公开URL地址
- **通过文件审核**:输入本地图片文件的完整路径
支持的图片格式JPG、JPEG、PNG、BMP、GIF、WEBP
文件大小限制最大9MB
### 文本审核
- **单条文本**:输入一段文本进行审核
- **批量文本**:输入多行文本,每行一条,空行结束
### 视频审核
- **异步扫描**提交任务后立即返回任务ID可稍后查询结果
- **同步扫描**:等待审核完成并返回结果(可能需要几分钟)
## 审核结果说明
### 建议类型
- `pass`:通过
- `review`:需要人工审核
- `block`:拒绝
### 检测场景
- `porn`:色情内容
- `terrorism`:暴恐内容
- `ad`:广告内容
- `live`:直播内容
- `logo`Logo识别
- `antispam`:反垃圾信息
## 项目结构
```
.
├── main.go # 主程序入口
├── config.go # 配置管理
├── image_scanner.go # 图片审核功能
├── text_scanner.go # 文本审核功能
├── video_scanner.go # 视频审核功能
├── go.mod # Go模块文件
├── env.example # 环境变量示例
└── README.md # 项目说明
```
## 注意事项
1. **费用说明**:阿里云内容安全服务按调用次数计费,请查看[官方定价](https://www.alibabacloud.com/help/zh/content-moderation/latest/billing-overview)
2. **权限配置**建议为RAM用户创建AccessKey并授予`AliyunYundunGreenWebFullAccess`权限
3. **文件限制**
- 图片最大9MB建议分辨率大于256×256像素
- 视频:支持多种格式,建议使用异步审核
4. **网络要求**:需要能够访问阿里云服务端点
## 错误处理
程序包含完善的错误处理机制:
- 配置验证
- 网络请求错误处理
- 文件格式和大小验证
- API响应解析错误处理
## 扩展功能
可以根据需要扩展以下功能:
- 添加更多检测场景
- 实现结果缓存机制
- 添加批量处理功能
- 集成到Web服务中
## 技术支持
如有问题,请参考:
- [阿里云内容安全官方文档](https://www.alibabacloud.com/help/zh/content-moderation/)
- [Go SDK文档](https://github.com/aliyun/alibaba-cloud-sdk-go)
## 许可证
MIT License

257
cmd/main.go Normal file
View File

@ -0,0 +1,257 @@
package main
import (
"bufio"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/api"
"fmt"
"os"
"strings"
"time"
)
func main() {
fmt.Println("=== 阿里云内容安全2.0 Demo ===")
fmt.Println("支持视频、图片、文字内容安全审核")
fmt.Println()
// 加载配置
fmt.Println("正在加载配置...")
config, err := conf.LoadConfigFromFile("conf/alibabacloud.env")
if err != nil {
fmt.Printf("❌ 加载配置失败: %v\n", err)
return
}
// 显示配置信息(隐藏敏感信息)
fmt.Printf("✅ 配置加载成功\n")
fmt.Printf(" 区域: %s\n", config.Region)
fmt.Printf(" 端点: %s\n", config.Endpoint)
if config.AccessKeyID != "" {
fmt.Printf(" AccessKey ID: %s...\n", config.AccessKeyID[:8])
}
if config.AccessKeySecret != "" {
fmt.Printf(" AccessKey Secret: %s...\n", config.AccessKeySecret[:8])
}
fmt.Println()
// 检查必要的配置
if config.AccessKeyID == "" || config.AccessKeySecret == "" {
fmt.Println("❌ 缺少必要的配置信息:")
if config.AccessKeyID == "" {
fmt.Println(" - ALIBABA_CLOUD_ACCESS_KEY_ID 未设置")
}
if config.AccessKeySecret == "" {
fmt.Println(" - ALIBABA_CLOUD_ACCESS_KEY_SECRET 未设置")
}
fmt.Println()
fmt.Println("请检查以下配置文件:")
fmt.Println(" - .env")
fmt.Println(" - conf/alibabacloud.env")
fmt.Println(" - 环境变量")
return
}
// 显示菜单
for {
showMenu()
choice := getUserInput("请选择操作 (1-4): ")
switch choice {
case "1":
handleImageScan(config)
case "2":
handleTextScan(config)
case "3":
handleVideoScan(config)
case "4":
fmt.Println("感谢使用!")
return
default:
fmt.Println("❌ 无效选择,请重新输入")
}
fmt.Println()
}
}
func showMenu() {
fmt.Println("请选择要测试的功能:")
fmt.Println("1. 图片内容安全审核")
fmt.Println("2. 文本内容安全审核")
fmt.Println("3. 视频内容安全审核")
fmt.Println("4. 退出")
fmt.Println()
}
func handleImageScan(config *conf.Config) {
fmt.Println("\n=== 图片内容安全审核 ===")
scanner, err := api.NewImageScanner(config)
if err != nil {
fmt.Printf("❌ 创建图片扫描器失败: %v\n", err)
return
}
fmt.Println("请选择图片输入方式:")
fmt.Println("1. 通过URL")
fmt.Println("2. 通过本地文件")
choice := getUserInput("请选择 (1-2): ")
switch choice {
case "1":
url := getUserInput("请输入图片URL: ")
if url == "" {
fmt.Println("❌ URL不能为空")
return
}
fmt.Println("正在扫描图片...")
result, err := scanner.ScanImageByURL(url, "image_"+time.Now().Format("20060102150405"))
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
}
scanner.PrintResult(result)
case "2":
filePath := getUserInput("请输入图片文件路径: ")
if filePath == "" {
fmt.Println("❌ 文件路径不能为空")
return
}
fmt.Println("正在扫描图片...")
result, err := scanner.ScanImageByFile(filePath, "image_"+time.Now().Format("20060102150405"))
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
}
scanner.PrintResult(result)
default:
fmt.Println("❌ 无效选择")
}
}
func handleTextScan(config *conf.Config) {
fmt.Println("\n=== 文本内容安全审核 ===")
scanner, err := api.NewTextScanner(config)
if err != nil {
fmt.Printf("❌ 创建文本扫描器失败: %v\n", err)
return
}
fmt.Println("请选择文本输入方式:")
fmt.Println("1. 单条文本")
fmt.Println("2. 批量文本")
choice := getUserInput("请选择 (1-2): ")
switch choice {
case "1":
text := getUserInput("请输入要审核的文本: ")
if text == "" {
fmt.Println("❌ 文本不能为空")
return
}
fmt.Println("正在扫描文本...")
result, err := scanner.ScanText(text, "text_"+time.Now().Format("20060102150405"))
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
}
scanner.PrintResult(result)
case "2":
fmt.Println("请输入多条文本,每行一条,输入空行结束:")
var texts []string
var dataIDs []string
for i := 1; ; i++ {
text := getUserInput(fmt.Sprintf("文本%d: ", i))
if text == "" {
break
}
texts = append(texts, text)
dataIDs = append(dataIDs, fmt.Sprintf("text_%d_%s", i, time.Now().Format("20060102150405")))
}
if len(texts) == 0 {
fmt.Println("❌ 没有输入任何文本")
return
}
fmt.Printf("正在批量扫描 %d 条文本...\n", len(texts))
result, err := scanner.ScanTextBatch(texts, dataIDs)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
}
scanner.PrintResult(result)
default:
fmt.Println("❌ 无效选择")
}
}
func handleVideoScan(config *conf.Config) {
fmt.Println("\n=== 视频内容安全审核 ===")
scanner, err := api.NewVideoScanner(config)
if err != nil {
fmt.Printf("❌ 创建视频扫描器失败: %v\n", err)
return
}
fmt.Println("请选择视频审核方式:")
fmt.Println("1. 异步扫描(提交任务后立即返回)")
fmt.Println("2. 同步扫描(等待结果返回)")
choice := getUserInput("请选择 (1-2): ")
videoURL := getUserInput("请输入视频URL: ")
if videoURL == "" {
fmt.Println("❌ 视频URL不能为空")
return
}
dataID := "video_" + time.Now().Format("20060102150405")
switch choice {
case "1":
fmt.Println("正在提交视频扫描任务...")
result, err := scanner.ScanVideoAsync(videoURL, dataID)
if err != nil {
fmt.Printf("❌ 提交任务失败: %v\n", err)
return
}
scanner.PrintScanResult(result)
if len(result.Data) > 0 && result.Data[0].TaskID != "" {
fmt.Printf("\n💡 提示: 使用任务ID %s 可以稍后查询结果\n", result.Data[0].TaskID)
}
case "2":
fmt.Println("正在扫描视频(这可能需要几分钟)...")
result, err := scanner.ScanVideoAndWait(videoURL, dataID, 10*time.Minute)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
}
scanner.PrintResult(result)
default:
fmt.Println("❌ 无效选择")
}
}
func getUserInput(prompt string) string {
fmt.Print(prompt)
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
return strings.TrimSpace(input)
}

12
conf/alibabacloud.env Normal file
View File

@ -0,0 +1,12 @@
#=========== 阿里云内容安全配置 ===========
# 阿里云AccessKey ID
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id_here
# 阿里云AccessKey Secret
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret_here
# 阿里云区域可选默认为cn-shanghai
ALIBABA_CLOUD_REGION=cn-shanghai
# 阿里云端点可选默认为green.cn-shanghai.aliyuncs.com
ALIBABA_CLOUD_ENDPOINT=green.cn-shanghai.aliyuncs.com

81
conf/config.go Normal file
View File

@ -0,0 +1,81 @@
package conf
import (
"fmt"
"os"
"strconv"
"github.com/joho/godotenv"
)
// Config 配置结构体
type Config struct {
AccessKeyID string
AccessKeySecret string
Region string
Endpoint string
}
// LoadConfig 加载配置
func LoadConfig() (*Config, error) {
// 尝试加载多个可能的配置文件
configFiles := []string{
".env", // 根目录的.env文件
"conf/alibabacloud.env", // 阿里云配置文件
"alibabacloud.env", // 根目录的阿里云配置文件
}
// 按顺序尝试加载配置文件
for _, configFile := range configFiles {
if err := godotenv.Load(configFile); err == nil {
fmt.Printf("成功加载配置文件: %s\n", configFile)
break
}
}
config := &Config{
AccessKeyID: getEnv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""),
Region: getEnv("ALIBABA_CLOUD_REGION", "cn-shanghai"),
Endpoint: getEnv("ALIBABA_CLOUD_ENDPOINT", "green.cn-shanghai.aliyuncs.com"),
}
return config, nil
}
// LoadConfigFromFile 从指定文件加载配置
func LoadConfigFromFile(configFile string) (*Config, error) {
// 加载指定的配置文件
if err := godotenv.Load(configFile); err != nil {
return nil, fmt.Errorf("加载配置文件失败 %s: %v", configFile, err)
}
fmt.Printf("成功加载配置文件: %s\n", configFile)
config := &Config{
AccessKeyID: getEnv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""),
Region: getEnv("ALIBABA_CLOUD_REGION", "cn-shanghai"),
Endpoint: getEnv("ALIBABA_CLOUD_ENDPOINT", "green.cn-shanghai.aliyuncs.com"),
}
return config, nil
}
// getEnv 获取环境变量,如果不存在则返回默认值
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
// getEnvAsInt 获取环境变量并转换为整数
func getEnvAsInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
}
return defaultValue
}

137
examples.md Normal file
View File

@ -0,0 +1,137 @@
# 使用示例
## 1. 图片审核示例
### 通过URL审核图片
```
请输入图片URL: https://example.com/test-image.jpg
```
### 通过本地文件审核图片
```
请输入图片文件路径: C:\Users\Desktop\test-image.png
```
## 2. 文本审核示例
### 单条文本审核
```
请输入要审核的文本: 这是一段测试文本,包含一些敏感词汇
```
### 批量文本审核
```
文本1: 第一条测试文本
文本2: 第二条测试文本
文本3: 第三条测试文本
文本4: [空行结束输入]
```
## 3. 视频审核示例
### 异步视频审核
```
请输入视频URL: https://example.com/test-video.mp4
```
### 同步视频审核
```
请输入视频URL: https://example.com/test-video.mp4
```
## 4. 审核结果示例
### 图片审核结果
```
=== 图片内容安全审核结果 ===
状态码: 200
消息: OK
数据ID: image_20231201120000
处理状态: 200 - OK
✅ 未检测到违规内容
```
### 文本审核结果
```
=== 文本内容安全审核结果 ===
状态码: 200
消息: OK
数据ID: text_20231201120000
处理状态: 200 - OK
场景: antispam
标签: spam
建议: block
置信度: 0.95
违规详情:
内容: 敏感词汇
位置: [10, 15]
---
```
### 视频审核结果
```
=== 视频内容安全审核结果 ===
状态码: 200
消息: OK
数据ID: video_20231201120000
任务ID: task_123456789
处理状态: 200 - OK
审核状态: success
场景: porn
标签: porn
建议: block
置信度: 0.88
违规帧详情:
时间偏移: 30秒
帧URL: https://example.com/frame.jpg
场景: porn, 标签: porn, 建议: block, 置信度: 0.88
---
```
## 5. 环境变量配置示例
### Windows (PowerShell)
```powershell
$env:ALIBABA_CLOUD_ACCESS_KEY_ID="your_access_key_id"
$env:ALIBABA_CLOUD_ACCESS_KEY_SECRET="your_access_key_secret"
```
### Windows (CMD)
```cmd
set ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id
set ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret
```
### Linux/macOS
```bash
export ALIBABA_CLOUD_ACCESS_KEY_ID="your_access_key_id"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your_access_key_secret"
```
### .env文件
```env
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret
ALIBABA_CLOUD_REGION=cn-shanghai
ALIBABA_CLOUD_ENDPOINT=green.cn-shanghai.aliyuncs.com
```
## 6. 常见问题
### Q: 如何获取阿里云AccessKey
A: 登录阿里云控制台,进入"访问控制" -> "用户" -> "创建AccessKey"
### Q: 支持哪些图片格式?
A: 支持JPG、JPEG、PNG、BMP、GIF、WEBP格式文件大小不超过9MB
### Q: 视频审核需要多长时间?
A: 视频审核时间取决于视频长度,通常需要几分钟到十几分钟
### Q: 如何查看审核历史?
A: 可以通过任务ID查询历史审核结果
### Q: 审核费用如何计算?
A: 按调用次数计费,具体价格请查看阿里云官方定价页面

17
go.mod Normal file
View File

@ -0,0 +1,17 @@
module contentSecurityDemo
go 1.23
require (
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706
github.com/joho/godotenv v1.5.1
)
require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

89
go.sum Normal file
View File

@ -0,0 +1,89 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706 h1:5qi9iWE+e6XoobqYHdg6rQj1o+ygAWhM4AzyvHgk4fA=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -0,0 +1,182 @@
package api
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/model"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// ImageScanner 图片内容安全扫描器
type ImageScanner struct {
Client *green.Client
Config *conf.Config
}
// NewImageScanner 创建图片扫描器
func NewImageScanner(config *conf.Config) (*ImageScanner, error) {
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("创建客户端失败: %v", err)
}
return &ImageScanner{
Client: client,
Config: config,
}, nil
}
// ScanImageByURL 通过URL扫描图片
func (s *ImageScanner) ScanImageByURL(imageURL string, dataID string) (*model.ImageScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.ApiName = "ImageSyncScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.ImageScanRequest{
Tasks: []model.ImageTask{
{
DataID: dataID,
URL: imageURL,
},
},
Scenes: []string{"porn", "terrorism", "ad", "live", "logo"},
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.ImageScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// ScanImageByFile 通过文件扫描图片
func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*model.ImageScanResponse, error) {
// 读取文件
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("打开文件失败: %v", err)
}
defer file.Close()
// 检查文件大小限制为9MB
fileInfo, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("获取文件信息失败: %v", err)
}
if fileInfo.Size() > 9*1024*1024 {
return nil, fmt.Errorf("文件大小超过9MB限制")
}
// 读取文件内容并转换为base64
fileContent, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("读取文件失败: %v", err)
}
// 检查文件类型
ext := strings.ToLower(filepath.Ext(filePath))
supportedExts := []string{".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp"}
isSupported := false
for _, supportedExt := range supportedExts {
if ext == supportedExt {
isSupported = true
break
}
}
if !isSupported {
return nil, fmt.Errorf("不支持的文件格式: %s", ext)
}
// 将文件内容转换为base64
base64Content := fmt.Sprintf("data:image/%s;base64,%s", ext[1:],
strings.ReplaceAll(string(fileContent), "\n", ""))
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.ApiName = "ImageSyncScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.ImageScanRequest{
Tasks: []model.ImageTask{
{
DataID: dataID,
Content: base64Content,
},
},
Scenes: []string{"porn", "terrorism", "ad", "live", "logo"},
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.ImageScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// PrintResult 打印扫描结果
func (s *ImageScanner) PrintResult(response *model.ImageScanResponse) {
fmt.Println("=== 图片内容安全审核结果 ===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("\n数据ID: %s\n", data.DataID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
if len(data.Results) == 0 {
fmt.Println("✅ 未检测到违规内容")
continue
}
for _, result := range data.Results {
fmt.Printf("场景: %s\n", result.Scene)
fmt.Printf("标签: %s\n", result.Label)
fmt.Printf("建议: %s\n", result.Suggestion)
fmt.Printf("置信度: %.2f\n", result.Rate)
fmt.Println("---")
}
}
}

View File

@ -0,0 +1,166 @@
package api
import (
"encoding/json"
"fmt"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/model"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// TextScanner 文本内容安全扫描器
type TextScanner struct {
Client *green.Client
Config *conf.Config
}
// NewTextScanner 创建文本扫描器
func NewTextScanner(config *conf.Config) (*TextScanner, error) {
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("创建客户端失败: %v", err)
}
return &TextScanner{
Client: client,
Config: config,
}, nil
}
// ScanText 扫描文本内容
func (s *TextScanner) ScanText(content string, dataID string) (*model.TextScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.ApiName = "TextScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.TextScanRequest{
Tasks: []model.TextTask{
{
DataID: dataID,
Content: content,
},
},
Scenes: []string{"antispam"},
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.TextScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// ScanTextBatch 批量扫描文本内容
func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string) (*model.TextScanResponse, error) {
if len(texts) != len(dataIDs) {
return nil, fmt.Errorf("文本数量和ID数量不匹配")
}
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.ApiName = "TextScan"
request.QueryParams["RegionId"] = s.Config.Region
var tasks []model.TextTask
for i, text := range texts {
tasks = append(tasks, model.TextTask{
DataID: dataIDs[i],
Content: text,
})
}
scanRequest := model.TextScanRequest{
Tasks: tasks,
Scenes: []string{"antispam"},
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.TextScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// PrintResult 打印扫描结果
func (s *TextScanner) PrintResult(response *model.TextScanResponse) {
fmt.Println("=== 文本内容安全审核结果 ===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("\n数据ID: %s\n", data.DataID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
if len(data.Results) == 0 {
fmt.Println("✅ 未检测到违规内容")
continue
}
for _, result := range data.Results {
fmt.Printf("场景: %s\n", result.Scene)
fmt.Printf("标签: %s\n", result.Label)
fmt.Printf("建议: %s\n", result.Suggestion)
fmt.Printf("置信度: %.2f\n", result.Rate)
// 打印详细信息
if len(result.Details) > 0 {
fmt.Println("违规详情:")
for _, detail := range result.Details {
fmt.Printf(" 内容: %s\n", detail.Context.Context)
fmt.Printf(" 位置: %v\n", detail.Context.Pos)
}
}
fmt.Println("---")
}
}
}
// GetSuggestionText 获取建议文本
func GetSuggestionText(suggestion string) string {
switch suggestion {
case "pass":
return "通过"
case "review":
return "需要人工审核"
case "block":
return "拒绝"
default:
return suggestion
}
}

View File

@ -0,0 +1,200 @@
package api
import (
"encoding/json"
"fmt"
"time"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/model"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// VideoScanner 视频内容安全扫描器
type VideoScanner struct {
Client *green.Client
Config *conf.Config
}
// NewVideoScanner 创建视频扫描器
func NewVideoScanner(config *conf.Config) (*VideoScanner, error) {
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("创建客户端失败: %v", err)
}
return &VideoScanner{
Client: client,
Config: config,
}, nil
}
// ScanVideoAsync 异步扫描视频
func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string) (*model.VideoScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.ApiName = "VideoAsyncScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.VideoScanRequest{
Tasks: []model.VideoTask{
{
DataID: dataID,
URL: videoURL,
Interval: 1, // 每秒截取一帧
MaxFrames: 100, // 最多截取100帧
},
},
Scenes: []string{"porn", "terrorism", "ad", "live"},
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.VideoScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// GetVideoResult 获取视频审核结果
func (s *VideoScanner) GetVideoResult(taskID string) (*model.VideoResultResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.ApiName = "VideoAsyncScanResults"
request.QueryParams["RegionId"] = s.Config.Region
requestBody := fmt.Sprintf(`{"taskId": "%s"}`, taskID)
request.Content = []byte(requestBody)
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var resultResponse model.VideoResultResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &resultResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &resultResponse, nil
}
// ScanVideoAndWait 扫描视频并等待结果
func (s *VideoScanner) ScanVideoAndWait(videoURL string, dataID string, maxWaitTime time.Duration) (*model.VideoResultResponse, error) {
// 提交扫描任务
scanResponse, err := s.ScanVideoAsync(videoURL, dataID)
if err != nil {
return nil, fmt.Errorf("提交扫描任务失败: %v", err)
}
if len(scanResponse.Data) == 0 {
return nil, fmt.Errorf("未获取到任务ID")
}
taskID := scanResponse.Data[0].TaskID
fmt.Printf("视频扫描任务已提交任务ID: %s\n", taskID)
// 轮询获取结果
startTime := time.Now()
for time.Since(startTime) < maxWaitTime {
result, err := s.GetVideoResult(taskID)
if err != nil {
return nil, fmt.Errorf("获取结果失败: %v", err)
}
if len(result.Data) > 0 {
status := result.Data[0].Status
fmt.Printf("当前状态: %s\n", status)
switch status {
case "success":
return result, nil
case "failure":
return nil, fmt.Errorf("视频审核失败: %s", result.Data[0].Message)
case "running", "pending":
// 继续等待
time.Sleep(5 * time.Second)
default:
return nil, fmt.Errorf("未知状态: %s", status)
}
} else {
time.Sleep(5 * time.Second)
}
}
return nil, fmt.Errorf("等待超时请稍后手动查询结果任务ID: %s", taskID)
}
// PrintScanResult 打印扫描提交结果
func (s *VideoScanner) PrintScanResult(response *model.VideoScanResponse) {
fmt.Println("=== 视频扫描任务提交结果 ===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("数据ID: %s\n", data.DataID)
fmt.Printf("任务ID: %s\n", data.TaskID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
fmt.Printf("视频URL: %s\n", data.URL)
}
}
// PrintResult 打印审核结果
func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
fmt.Println("=== 视频内容安全审核结果 ===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("\n数据ID: %s\n", data.DataID)
fmt.Printf("任务ID: %s\n", data.TaskID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
fmt.Printf("审核状态: %s\n", data.Status)
if len(data.Results) == 0 {
fmt.Println("✅ 未检测到违规内容")
continue
}
for _, result := range data.Results {
fmt.Printf("场景: %s\n", result.Scene)
fmt.Printf("标签: %s\n", result.Label)
fmt.Printf("建议: %s\n", result.Suggestion)
fmt.Printf("置信度: %.2f\n", result.Rate)
// 打印帧级别的结果
if len(result.Frames) > 0 {
fmt.Println("违规帧详情:")
for _, frame := range result.Frames {
fmt.Printf(" 时间偏移: %d秒\n", frame.Offset)
fmt.Printf(" 帧URL: %s\n", frame.URL)
for _, frameResult := range frame.Results {
fmt.Printf(" 场景: %s, 标签: %s, 建议: %s, 置信度: %.2f\n",
frameResult.Scene, frameResult.Label, frameResult.Suggestion, frameResult.Rate)
}
}
}
fmt.Println("---")
}
}
}

31
internal/model/image.go Normal file
View File

@ -0,0 +1,31 @@
package model
// ImageScanRequest 图片审核请求结构
type ImageScanRequest struct {
Tasks []ImageTask `json:"tasks"`
Scenes []string `json:"scenes"`
}
// ImageTask 图片任务结构
type ImageTask struct {
DataID string `json:"dataId"`
URL string `json:"url,omitempty"`
Content string `json:"content,omitempty"` // base64编码的图片内容
}
// ImageScanResponse 图片审核响应结构
type ImageScanResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data []struct {
Code int `json:"code"`
Message string `json:"message"`
DataID string `json:"dataId"`
Results []struct {
Scene string `json:"scene"`
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
} `json:"results"`
} `json:"data"`
}

36
internal/model/text.go Normal file
View File

@ -0,0 +1,36 @@
package model
// TextScanRequest 文本审核请求结构
type TextScanRequest struct {
Tasks []TextTask `json:"tasks"`
Scenes []string `json:"scenes"`
}
// TextTask 文本任务结构
type TextTask struct {
DataID string `json:"dataId"`
Content string `json:"content"`
}
// TextScanResponse 文本审核响应结构
type TextScanResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data []struct {
Code int `json:"code"`
Message string `json:"message"`
DataID string `json:"dataId"`
Results []struct {
Scene string `json:"scene"`
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
Details []struct {
Context struct {
Context string `json:"context"`
Pos []int `json:"pos"`
} `json:"context"`
} `json:"details"`
} `json:"results"`
} `json:"data"`
}

57
internal/model/video.go Normal file
View File

@ -0,0 +1,57 @@
package model
// VideoScanRequest 视频审核请求结构
type VideoScanRequest struct {
Tasks []VideoTask `json:"tasks"`
Scenes []string `json:"scenes"`
}
// VideoTask 视频任务结构
type VideoTask struct {
DataID string `json:"dataId"`
URL string `json:"url"`
Interval int `json:"interval,omitempty"` // 截帧间隔,单位秒
MaxFrames int `json:"maxFrames,omitempty"` // 最大截帧数
}
// VideoScanResponse 视频审核响应结构
type VideoScanResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data []struct {
Code int `json:"code"`
Message string `json:"message"`
DataID string `json:"dataId"`
TaskID string `json:"taskId"`
URL string `json:"url"`
} `json:"data"`
}
// VideoResultResponse 视频审核结果响应结构
type VideoResultResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data []struct {
Code int `json:"code"`
Message string `json:"message"`
DataID string `json:"dataId"`
TaskID string `json:"taskId"`
Status string `json:"status"`
Results []struct {
Scene string `json:"scene"`
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
Frames []struct {
Offset int `json:"offset"`
URL string `json:"url"`
Results []struct {
Scene string `json:"scene"`
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
} `json:"results"`
} `json:"frames"`
} `json:"results"`
} `json:"data"`
}

44
run.bat Normal file
View File

@ -0,0 +1,44 @@
@echo off
echo === 阿里云内容安全2.0 Demo ===
echo.
REM 检查Go是否安装
go version >nul 2>&1
if errorlevel 1 (
echo ❌ 错误: 未找到Go请先安装Go 1.21或更高版本
pause
exit /b 1
)
echo ✅ Go环境检查通过
echo.
REM 下载依赖
echo 正在下载依赖...
go mod tidy
if errorlevel 1 (
echo ❌ 依赖下载失败
pause
exit /b 1
)
echo ✅ 依赖下载完成
echo.
REM 检查环境变量
if "%ALIBABA_CLOUD_ACCESS_KEY_ID%"=="" (
echo ⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量
echo 请设置环境变量或创建.env文件
echo.
)
if "%ALIBABA_CLOUD_ACCESS_KEY_SECRET%"=="" (
echo ⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量
echo 请设置环境变量或创建.env文件
echo.
)
REM 运行程序
echo 启动程序...
go run cmd/main.go
pause

41
run.sh Normal file
View File

@ -0,0 +1,41 @@
#!/bin/bash
echo "=== 阿里云内容安全2.0 Demo ==="
echo
# 检查Go是否安装
if ! command -v go &> /dev/null; then
echo "❌ 错误: 未找到Go请先安装Go 1.21或更高版本"
exit 1
fi
echo "✅ Go环境检查通过"
echo
# 下载依赖
echo "正在下载依赖..."
go mod tidy
if [ $? -ne 0 ]; then
echo "❌ 依赖下载失败"
exit 1
fi
echo "✅ 依赖下载完成"
echo
# 检查环境变量
if [ -z "$ALIBABA_CLOUD_ACCESS_KEY_ID" ]; then
echo "⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量"
echo "请设置环境变量或创建.env文件"
echo
fi
if [ -z "$ALIBABA_CLOUD_ACCESS_KEY_SECRET" ]; then
echo "⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量"
echo "请设置环境变量或创建.env文件"
echo
fi
# 运行程序
echo "启动程序..."
go run cmd/main.go