Set up project files and add .gitignore to exclude local build/runtime artifacts. Made-with: Cursor
760 lines
19 KiB
Go
760 lines
19 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"carrot_bbs/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ==================== 内容审核服务接口和实现 ====================
|
|
|
|
// AuditServiceProvider 内容审核服务提供商接口
|
|
type AuditServiceProvider interface {
|
|
// AuditText 审核文本
|
|
AuditText(ctx context.Context, text string, scene string) (*AuditResult, error)
|
|
// AuditImage 审核图片
|
|
AuditImage(ctx context.Context, imageURL string) (*AuditResult, error)
|
|
// GetName 获取提供商名称
|
|
GetName() string
|
|
}
|
|
|
|
// AuditResult 审核结果
|
|
type AuditResult struct {
|
|
Pass bool `json:"pass"` // 是否通过
|
|
Risk string `json:"risk"` // 风险等级: low, medium, high
|
|
Labels []string `json:"labels"` // 标签列表
|
|
Suggest string `json:"suggest"` // 建议: pass, review, block
|
|
Detail string `json:"detail"` // 详细说明
|
|
Provider string `json:"provider"` // 服务提供商
|
|
}
|
|
|
|
// AuditService 内容审核服务接口
|
|
type AuditService interface {
|
|
// AuditText 审核文本
|
|
AuditText(ctx context.Context, text string, auditType string) (*AuditResult, error)
|
|
// AuditImage 审核图片
|
|
AuditImage(ctx context.Context, imageURL string) (*AuditResult, error)
|
|
// GetAuditResult 获取审核结果
|
|
GetAuditResult(ctx context.Context, auditID string) (*AuditResult, error)
|
|
// SetProvider 设置审核服务提供商
|
|
SetProvider(provider AuditServiceProvider)
|
|
// GetProvider 获取当前审核服务提供商
|
|
GetProvider() AuditServiceProvider
|
|
}
|
|
|
|
// auditServiceImpl 内容审核服务实现
|
|
type auditServiceImpl struct {
|
|
db *gorm.DB
|
|
provider AuditServiceProvider
|
|
config *AuditConfig
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// AuditConfig 内容审核服务配置
|
|
type AuditConfig struct {
|
|
Enabled bool `mapstructure:"enabled" yaml:"enabled"`
|
|
// 审核服务提供商: local, aliyun, tencent, baidu
|
|
Provider string `mapstructure:"provider" yaml:"provider"`
|
|
// 阿里云配置
|
|
AliyunAccessKey string `mapstructure:"aliyun_access_key" yaml:"aliyun_access_key"`
|
|
AliyunSecretKey string `mapstructure:"aliyun_secret_key" yaml:"aliyun_secret_key"`
|
|
AliyunRegion string `mapstructure:"aliyun_region" yaml:"aliyun_region"`
|
|
// 腾讯云配置
|
|
TencentSecretID string `mapstructure:"tencent_secret_id" yaml:"tencent_secret_id"`
|
|
TencentSecretKey string `mapstructure:"tencent_secret_key" yaml:"tencent_secret_key"`
|
|
// 百度云配置
|
|
BaiduAPIKey string `mapstructure:"baidu_api_key" yaml:"baidu_api_key"`
|
|
BaiduSecretKey string `mapstructure:"baidu_secret_key" yaml:"baidu_secret_key"`
|
|
// 是否自动审核
|
|
AutoAudit bool `mapstructure:"auto_audit" yaml:"auto_audit"`
|
|
// 审核超时时间(秒)
|
|
Timeout int `mapstructure:"timeout" yaml:"timeout"`
|
|
}
|
|
|
|
// NewAuditService 创建内容审核服务
|
|
func NewAuditService(db *gorm.DB, config *AuditConfig) AuditService {
|
|
s := &auditServiceImpl{
|
|
db: db,
|
|
config: config,
|
|
}
|
|
|
|
// 根据配置初始化提供商
|
|
if config.Enabled {
|
|
provider := s.initProvider(config.Provider)
|
|
s.provider = provider
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// initProvider 根据配置初始化审核服务提供商
|
|
func (s *auditServiceImpl) initProvider(providerType string) AuditServiceProvider {
|
|
switch strings.ToLower(providerType) {
|
|
case "aliyun":
|
|
return NewAliyunAuditProvider(s.config.AliyunAccessKey, s.config.AliyunSecretKey, s.config.AliyunRegion)
|
|
case "tencent":
|
|
return NewTencentAuditProvider(s.config.TencentSecretID, s.config.TencentSecretKey)
|
|
case "baidu":
|
|
return NewBaiduAuditProvider(s.config.BaiduAPIKey, s.config.BaiduSecretKey)
|
|
case "local":
|
|
fallthrough
|
|
default:
|
|
// 默认使用本地审核服务
|
|
return NewLocalAuditProvider()
|
|
}
|
|
}
|
|
|
|
// AuditText 审核文本
|
|
func (s *auditServiceImpl) AuditText(ctx context.Context, text string, auditType string) (*AuditResult, error) {
|
|
if !s.config.Enabled {
|
|
// 如果审核服务未启用,直接返回通过
|
|
return &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Detail: "Audit service disabled",
|
|
}, nil
|
|
}
|
|
|
|
if text == "" {
|
|
return &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Detail: "Empty text",
|
|
}, nil
|
|
}
|
|
|
|
var result *AuditResult
|
|
var err error
|
|
|
|
// 使用提供商审核
|
|
if s.provider != nil {
|
|
result, err = s.provider.AuditText(ctx, text, auditType)
|
|
} else {
|
|
// 如果没有设置提供商,使用本地审核
|
|
localProvider := NewLocalAuditProvider()
|
|
result, err = localProvider.AuditText(ctx, text, auditType)
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("Audit text error: %v", err)
|
|
return &AuditResult{
|
|
Pass: false,
|
|
Risk: "high",
|
|
Suggest: "review",
|
|
Detail: fmt.Sprintf("Audit error: %v", err),
|
|
}, err
|
|
}
|
|
|
|
// 记录审核日志
|
|
go s.saveAuditLog(ctx, "text", "", text, auditType, result)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AuditImage 审核图片
|
|
func (s *auditServiceImpl) AuditImage(ctx context.Context, imageURL string) (*AuditResult, error) {
|
|
if !s.config.Enabled {
|
|
return &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Detail: "Audit service disabled",
|
|
}, nil
|
|
}
|
|
|
|
if imageURL == "" {
|
|
return &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Detail: "Empty image URL",
|
|
}, nil
|
|
}
|
|
|
|
var result *AuditResult
|
|
var err error
|
|
|
|
// 使用提供商审核
|
|
if s.provider != nil {
|
|
result, err = s.provider.AuditImage(ctx, imageURL)
|
|
} else {
|
|
// 如果没有设置提供商,使用本地审核
|
|
localProvider := NewLocalAuditProvider()
|
|
result, err = localProvider.AuditImage(ctx, imageURL)
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("Audit image error: %v", err)
|
|
return &AuditResult{
|
|
Pass: false,
|
|
Risk: "high",
|
|
Suggest: "review",
|
|
Detail: fmt.Sprintf("Audit error: %v", err),
|
|
}, err
|
|
}
|
|
|
|
// 记录审核日志
|
|
go s.saveAuditLog(ctx, "image", "", "", "image", result)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetAuditResult 获取审核结果
|
|
func (s *auditServiceImpl) GetAuditResult(ctx context.Context, auditID string) (*AuditResult, error) {
|
|
if s.db == nil || auditID == "" {
|
|
return nil, fmt.Errorf("invalid audit ID")
|
|
}
|
|
|
|
var auditLog model.AuditLog
|
|
if err := s.db.Where("id = ?", auditID).First(&auditLog).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &AuditResult{
|
|
Pass: auditLog.Result == model.AuditResultPass,
|
|
Risk: string(auditLog.RiskLevel),
|
|
Suggest: auditLog.Suggestion,
|
|
Detail: auditLog.Detail,
|
|
}
|
|
|
|
// 解析标签
|
|
if auditLog.Labels != "" {
|
|
json.Unmarshal([]byte(auditLog.Labels), &result.Labels)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// SetProvider 设置审核服务提供商
|
|
func (s *auditServiceImpl) SetProvider(provider AuditServiceProvider) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.provider = provider
|
|
}
|
|
|
|
// GetProvider 获取当前审核服务提供商
|
|
func (s *auditServiceImpl) GetProvider() AuditServiceProvider {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.provider
|
|
}
|
|
|
|
// saveAuditLog 保存审核日志
|
|
func (s *auditServiceImpl) saveAuditLog(ctx context.Context, contentType, content, imageURL, auditType string, result *AuditResult) {
|
|
if s.db == nil {
|
|
return
|
|
}
|
|
|
|
auditLog := model.AuditLog{
|
|
ContentType: contentType,
|
|
Content: content,
|
|
ContentURL: imageURL,
|
|
AuditType: auditType,
|
|
Labels: strings.Join(result.Labels, ","),
|
|
Suggestion: result.Suggest,
|
|
Detail: result.Detail,
|
|
Source: model.AuditSourceAuto,
|
|
Status: "completed",
|
|
}
|
|
|
|
if result.Pass {
|
|
auditLog.Result = model.AuditResultPass
|
|
} else if result.Suggest == "review" {
|
|
auditLog.Result = model.AuditResultReview
|
|
} else {
|
|
auditLog.Result = model.AuditResultBlock
|
|
}
|
|
|
|
switch result.Risk {
|
|
case "low":
|
|
auditLog.RiskLevel = model.AuditRiskLevelLow
|
|
case "medium":
|
|
auditLog.RiskLevel = model.AuditRiskLevelMedium
|
|
case "high":
|
|
auditLog.RiskLevel = model.AuditRiskLevelHigh
|
|
default:
|
|
auditLog.RiskLevel = model.AuditRiskLevelLow
|
|
}
|
|
|
|
if err := s.db.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Failed to save audit log: %v", err)
|
|
}
|
|
}
|
|
|
|
// ==================== 本地审核服务提供商 ====================
|
|
|
|
// localAuditProvider 本地审核服务提供商
|
|
type localAuditProvider struct {
|
|
// 可以注入敏感词服务进行本地审核
|
|
sensitiveService SensitiveService
|
|
}
|
|
|
|
// NewLocalAuditProvider 创建本地审核服务提供商
|
|
func NewLocalAuditProvider() AuditServiceProvider {
|
|
return &localAuditProvider{
|
|
sensitiveService: nil,
|
|
}
|
|
}
|
|
|
|
// GetName 获取提供商名称
|
|
func (p *localAuditProvider) GetName() string {
|
|
return "local"
|
|
}
|
|
|
|
// AuditText 审核文本
|
|
func (p *localAuditProvider) AuditText(ctx context.Context, text string, scene string) (*AuditResult, error) {
|
|
// 本地审核逻辑
|
|
// 1. 敏感词检查
|
|
// 2. 规则匹配
|
|
// 3. 简单的关键词检测
|
|
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "local",
|
|
}
|
|
|
|
// 如果有敏感词服务,使用它进行检测
|
|
if p.sensitiveService != nil {
|
|
hasSensitive, words := p.sensitiveService.Check(ctx, text)
|
|
if hasSensitive {
|
|
result.Pass = false
|
|
result.Risk = "high"
|
|
result.Suggest = "block"
|
|
result.Detail = fmt.Sprintf("包含敏感词: %s", strings.Join(words, ","))
|
|
result.Labels = append(result.Labels, "sensitive")
|
|
}
|
|
}
|
|
|
|
// 简单的关键词检测规则
|
|
// 实际项目中应该从数据库加载
|
|
suspiciousPatterns := []string{
|
|
"诈骗",
|
|
"钓鱼",
|
|
"木马",
|
|
"病毒",
|
|
}
|
|
|
|
for _, pattern := range suspiciousPatterns {
|
|
if strings.Contains(text, pattern) {
|
|
result.Pass = false
|
|
result.Risk = "high"
|
|
result.Suggest = "block"
|
|
result.Labels = append(result.Labels, "suspicious")
|
|
if result.Detail == "" {
|
|
result.Detail = fmt.Sprintf("包含可疑内容: %s", pattern)
|
|
} else {
|
|
result.Detail += fmt.Sprintf(", %s", pattern)
|
|
}
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AuditImage 审核图片
|
|
func (p *localAuditProvider) AuditImage(ctx context.Context, imageURL string) (*AuditResult, error) {
|
|
// 本地图片审核逻辑
|
|
// 1. 图片URL合法性检查
|
|
// 2. 图片格式检查
|
|
// 3. 可以扩展接入本地图片识别服务
|
|
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "local",
|
|
}
|
|
|
|
// 检查URL是否为空
|
|
if imageURL == "" {
|
|
result.Detail = "Empty image URL"
|
|
return result, nil
|
|
}
|
|
|
|
// 检查是否为支持的图片URL格式
|
|
validPrefixes := []string{"http://", "https://", "s3://", "oss://", "cos://"}
|
|
isValid := false
|
|
for _, prefix := range validPrefixes {
|
|
if strings.HasPrefix(strings.ToLower(imageURL), prefix) {
|
|
isValid = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isValid {
|
|
result.Pass = false
|
|
result.Risk = "medium"
|
|
result.Suggest = "review"
|
|
result.Detail = "Invalid image URL format"
|
|
result.Labels = append(result.Labels, "invalid_url")
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// SetSensitiveService 设置敏感词服务
|
|
func (p *localAuditProvider) SetSensitiveService(ss SensitiveService) {
|
|
p.sensitiveService = ss
|
|
}
|
|
|
|
// ==================== 阿里云审核服务提供商 ====================
|
|
|
|
// aliyunAuditProvider 阿里云审核服务提供商
|
|
type aliyunAuditProvider struct {
|
|
accessKey string
|
|
secretKey string
|
|
region string
|
|
}
|
|
|
|
// NewAliyunAuditProvider 创建阿里云审核服务提供商
|
|
func NewAliyunAuditProvider(accessKey, secretKey, region string) AuditServiceProvider {
|
|
return &aliyunAuditProvider{
|
|
accessKey: accessKey,
|
|
secretKey: secretKey,
|
|
region: region,
|
|
}
|
|
}
|
|
|
|
// GetName 获取提供商名称
|
|
func (p *aliyunAuditProvider) GetName() string {
|
|
return "aliyun"
|
|
}
|
|
|
|
// AuditText 审核文本
|
|
func (p *aliyunAuditProvider) AuditText(ctx context.Context, text string, scene string) (*AuditResult, error) {
|
|
// 阿里云内容安全API调用
|
|
// 实际项目中需要实现阿里云SDK调用
|
|
// 这里预留接口
|
|
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "aliyun",
|
|
Detail: "Aliyun audit not implemented, using pass",
|
|
}
|
|
|
|
// TODO: 实现阿里云内容安全API调用
|
|
// 具体参考: https://help.aliyun.com/document_detail/28417.html
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AuditImage 审核图片
|
|
func (p *aliyunAuditProvider) AuditImage(ctx context.Context, imageURL string) (*AuditResult, error) {
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "aliyun",
|
|
Detail: "Aliyun image audit not implemented, using pass",
|
|
}
|
|
|
|
// TODO: 实现阿里云图片审核API调用
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// ==================== 腾讯云审核服务提供商 ====================
|
|
|
|
// tencentAuditProvider 腾讯云审核服务提供商
|
|
type tencentAuditProvider struct {
|
|
secretID string
|
|
secretKey string
|
|
}
|
|
|
|
// NewTencentAuditProvider 创建腾讯云审核服务提供商
|
|
func NewTencentAuditProvider(secretID, secretKey string) AuditServiceProvider {
|
|
return &tencentAuditProvider{
|
|
secretID: secretID,
|
|
secretKey: secretKey,
|
|
}
|
|
}
|
|
|
|
// GetName 获取提供商名称
|
|
func (p *tencentAuditProvider) GetName() string {
|
|
return "tencent"
|
|
}
|
|
|
|
// AuditText 审核文本
|
|
func (p *tencentAuditProvider) AuditText(ctx context.Context, text string, scene string) (*AuditResult, error) {
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "tencent",
|
|
Detail: "Tencent audit not implemented, using pass",
|
|
}
|
|
|
|
// TODO: 实现腾讯云内容审核API调用
|
|
// 具体参考: https://cloud.tencent.com/document/product/1124/64508
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AuditImage 审核图片
|
|
func (p *tencentAuditProvider) AuditImage(ctx context.Context, imageURL string) (*AuditResult, error) {
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "tencent",
|
|
Detail: "Tencent image audit not implemented, using pass",
|
|
}
|
|
|
|
// TODO: 实现腾讯云图片审核API调用
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// ==================== 百度云审核服务提供商 ====================
|
|
|
|
// baiduAuditProvider 百度云审核服务提供商
|
|
type baiduAuditProvider struct {
|
|
apiKey string
|
|
secretKey string
|
|
}
|
|
|
|
// NewBaiduAuditProvider 创建百度云审核服务提供商
|
|
func NewBaiduAuditProvider(apiKey, secretKey string) AuditServiceProvider {
|
|
return &baiduAuditProvider{
|
|
apiKey: apiKey,
|
|
secretKey: secretKey,
|
|
}
|
|
}
|
|
|
|
// GetName 获取提供商名称
|
|
func (p *baiduAuditProvider) GetName() string {
|
|
return "baidu"
|
|
}
|
|
|
|
// AuditText 审核文本
|
|
func (p *baiduAuditProvider) AuditText(ctx context.Context, text string, scene string) (*AuditResult, error) {
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "baidu",
|
|
Detail: "Baidu audit not implemented, using pass",
|
|
}
|
|
|
|
// TODO: 实现百度云内容审核API调用
|
|
// 具体参考: https://cloud.baidu.com/doc/ANTISPAM/s/Jjw0r1iF6
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AuditImage 审核图片
|
|
func (p *baiduAuditProvider) AuditImage(ctx context.Context, imageURL string) (*AuditResult, error) {
|
|
result := &AuditResult{
|
|
Pass: true,
|
|
Risk: "low",
|
|
Suggest: "pass",
|
|
Labels: []string{},
|
|
Provider: "baidu",
|
|
Detail: "Baidu image audit not implemented, using pass",
|
|
}
|
|
|
|
// TODO: 实现百度云图片审核API调用
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// ==================== 审核结果回调处理 ====================
|
|
|
|
// AuditCallback 审核回调处理
|
|
type AuditCallback struct {
|
|
service AuditService
|
|
}
|
|
|
|
// NewAuditCallback 创建审核回调处理
|
|
func NewAuditCallback(service AuditService) *AuditCallback {
|
|
return &AuditCallback{
|
|
service: service,
|
|
}
|
|
}
|
|
|
|
// HandleTextCallback 处理文本审核回调
|
|
func (c *AuditCallback) HandleTextCallback(ctx context.Context, auditID string, result *AuditResult) error {
|
|
if c.service == nil || auditID == "" || result == nil {
|
|
return fmt.Errorf("invalid parameters")
|
|
}
|
|
|
|
log.Printf("Processing text audit callback: auditID=%s, result=%+v", auditID, result)
|
|
|
|
// 根据审核结果执行相应操作
|
|
// 例如: 更新帖子状态、发送通知等
|
|
|
|
return nil
|
|
}
|
|
|
|
// HandleImageCallback 处理图片审核回调
|
|
func (c *AuditCallback) HandleImageCallback(ctx context.Context, auditID string, result *AuditResult) error {
|
|
if c.service == nil || auditID == "" || result == nil {
|
|
return fmt.Errorf("invalid parameters")
|
|
}
|
|
|
|
log.Printf("Processing image audit callback: auditID=%s, result=%+v", auditID, result)
|
|
|
|
// 根据审核结果执行相应操作
|
|
// 例如: 更新图片状态、删除违规图片等
|
|
|
|
return nil
|
|
}
|
|
|
|
// ==================== 辅助函数 ====================
|
|
|
|
// IsContentSafe 判断内容是否安全
|
|
func IsContentSafe(result *AuditResult) bool {
|
|
if result == nil {
|
|
return true
|
|
}
|
|
return result.Pass && result.Suggest != "block"
|
|
}
|
|
|
|
// NeedReview 判断内容是否需要人工复审
|
|
func NeedReview(result *AuditResult) bool {
|
|
if result == nil {
|
|
return false
|
|
}
|
|
return result.Suggest == "review"
|
|
}
|
|
|
|
// GetRiskLevel 获取风险等级
|
|
func GetRiskLevel(result *AuditResult) string {
|
|
if result == nil {
|
|
return "low"
|
|
}
|
|
return result.Risk
|
|
}
|
|
|
|
// FormatAuditResult 格式化审核结果为字符串
|
|
func FormatAuditResult(result *AuditResult) string {
|
|
if result == nil {
|
|
return "{}"
|
|
}
|
|
data, _ := json.Marshal(result)
|
|
return string(data)
|
|
}
|
|
|
|
// ParseAuditResult 从字符串解析审核结果
|
|
func ParseAuditResult(data string) (*AuditResult, error) {
|
|
if data == "" {
|
|
return nil, fmt.Errorf("empty data")
|
|
}
|
|
var result AuditResult
|
|
if err := json.Unmarshal([]byte(data), &result); err != nil {
|
|
return nil, err
|
|
}
|
|
return &result, nil
|
|
}
|
|
|
|
// ==================== 审核日志查询 ====================
|
|
|
|
// GetAuditLogs 获取审核日志列表
|
|
func GetAuditLogs(db *gorm.DB, targetType string, targetID string, result string, page, pageSize int) ([]model.AuditLog, int64, error) {
|
|
query := db.Model(&model.AuditLog{})
|
|
|
|
if targetType != "" {
|
|
query = query.Where("target_type = ?", targetType)
|
|
}
|
|
if targetID != "" {
|
|
query = query.Where("target_id = ?", targetID)
|
|
}
|
|
if result != "" {
|
|
query = query.Where("result = ?", result)
|
|
}
|
|
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
var logs []model.AuditLog
|
|
offset := (page - 1) * pageSize
|
|
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&logs).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return logs, total, nil
|
|
}
|
|
|
|
// ==================== 定时任务 ====================
|
|
|
|
// AuditScheduler 审核调度器
|
|
type AuditScheduler struct {
|
|
db *gorm.DB
|
|
service AuditService
|
|
interval time.Duration
|
|
stopCh chan bool
|
|
}
|
|
|
|
// NewAuditScheduler 创建审核调度器
|
|
func NewAuditScheduler(db *gorm.DB, service AuditService, interval time.Duration) *AuditScheduler {
|
|
return &AuditScheduler{
|
|
db: db,
|
|
service: service,
|
|
interval: interval,
|
|
stopCh: make(chan bool),
|
|
}
|
|
}
|
|
|
|
// Start 启动调度器
|
|
func (s *AuditScheduler) Start() {
|
|
go func() {
|
|
ticker := time.NewTicker(s.interval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
s.processPendingAudits()
|
|
case <-s.stopCh:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Stop 停止调度器
|
|
func (s *AuditScheduler) Stop() {
|
|
s.stopCh <- true
|
|
}
|
|
|
|
// processPendingAudits 处理待审核内容
|
|
func (s *AuditScheduler) processPendingAudits() {
|
|
// 查询待审核的内容
|
|
// 1. 查询审核状态为 pending 的记录
|
|
// 2. 调用审核服务
|
|
// 3. 更新审核状态
|
|
|
|
// 示例逻辑,实际需要根据业务需求实现
|
|
log.Println("Processing pending audits...")
|
|
}
|
|
|
|
// CleanupOldLogs 清理旧的审核日志
|
|
func CleanupOldLogs(db *gorm.DB, days int) error {
|
|
// 清理指定天数之前的审核日志
|
|
cutoffTime := time.Now().AddDate(0, 0, -days)
|
|
return db.Where("created_at < ? AND result = ?", cutoffTime, model.AuditResultPass).Delete(&model.AuditLog{}).Error
|
|
}
|