mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
update: 日志优化
This commit is contained in:
@@ -128,6 +128,11 @@ PORT=8080
|
||||
|
||||
# 时区配置
|
||||
TIMEZONE=Asia/Shanghai
|
||||
|
||||
# 日志配置
|
||||
LOG_LEVEL=INFO # 日志级别 (DEBUG, INFO, WARN, ERROR, FATAL)
|
||||
DEBUG=false # 调试模式开关
|
||||
STRUCTURED_LOG=false # 结构化日志开关 (JSON格式)
|
||||
```
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -66,20 +69,28 @@ func (r *CksRepositoryImpl) FindAll() ([]entity.Cks, error) {
|
||||
|
||||
// FindByID 根据ID查找Cks,预加载Pan关联数据
|
||||
func (r *CksRepositoryImpl) FindByID(id uint) (*entity.Cks, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var cks entity.Cks
|
||||
err := r.db.Preload("Pan").First(&cks, id).Error
|
||||
queryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Debug("FindByID失败: ID=%d, 错误=%v, 查询耗时=%v", id, err, queryDuration)
|
||||
return nil, err
|
||||
}
|
||||
utils.Debug("FindByID成功: ID=%d, 查询耗时=%v", id, queryDuration)
|
||||
return &cks, nil
|
||||
}
|
||||
|
||||
func (r *CksRepositoryImpl) FindByIds(ids []uint) ([]*entity.Cks, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var cks []*entity.Cks
|
||||
err := r.db.Preload("Pan").Where("id IN ?", ids).Find(&cks).Error
|
||||
queryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Debug("FindByIds失败: IDs数量=%d, 错误=%v, 查询耗时=%v", len(ids), err, queryDuration)
|
||||
return nil, err
|
||||
}
|
||||
utils.Debug("FindByIds成功: 找到%d个账号,查询耗时=%v", len(cks), queryDuration)
|
||||
return cks, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -201,6 +202,7 @@ func (r *ResourceRepositoryImpl) SearchByPanID(query string, panID uint, page, l
|
||||
|
||||
// SearchWithFilters 根据参数进行搜索
|
||||
func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}) ([]entity.Resource, int64, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var resources []entity.Resource
|
||||
var total int64
|
||||
|
||||
@@ -318,8 +320,11 @@ func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 获取分页数据,按更新时间倒序
|
||||
queryStart := utils.GetCurrentTime()
|
||||
err := db.Order("updated_at DESC").Offset(offset).Limit(pageSize).Find(&resources).Error
|
||||
fmt.Printf("查询结果: 总数=%d, 当前页数据量=%d, pageSize=%d\n", total, len(resources), pageSize)
|
||||
queryDuration := time.Since(queryStart)
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("SearchWithFilters完成: 总数=%d, 当前页数据量=%d, 查询耗时=%v, 总耗时=%v", total, len(resources), queryDuration, totalDuration)
|
||||
return resources, total, err
|
||||
}
|
||||
|
||||
@@ -452,11 +457,15 @@ func (r *ResourceRepositoryImpl) GetResourcesForTransfer(panID uint, sinceTime t
|
||||
|
||||
// GetByURL 根据URL获取资源
|
||||
func (r *ResourceRepositoryImpl) GetByURL(url string) (*entity.Resource, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var resource entity.Resource
|
||||
err := r.db.Where("url = ?", url).First(&resource).Error
|
||||
queryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Debug("GetByURL失败: URL=%s, 错误=%v, 查询耗时=%v", url, err, queryDuration)
|
||||
return nil, err
|
||||
}
|
||||
utils.Debug("GetByURL成功: URL=%s, 查询耗时=%v", url, queryDuration)
|
||||
return &resource, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package repo
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
@@ -100,8 +101,11 @@ func (r *SystemConfigRepositoryImpl) UpsertConfigs(configs []entity.SystemConfig
|
||||
|
||||
// GetOrCreateDefault 获取配置或创建默认配置
|
||||
func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
configs, err := r.FindAll()
|
||||
initialQueryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("获取所有系统配置失败: %v,耗时: %v", err, initialQueryDuration)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -141,11 +145,16 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
|
||||
{Key: entity.ConfigKeyQrCodeStyle, Value: entity.ConfigDefaultQrCodeStyle, Type: entity.ConfigTypeString},
|
||||
}
|
||||
|
||||
createStart := utils.GetCurrentTime()
|
||||
err = r.UpsertConfigs(defaultConfigs)
|
||||
createDuration := time.Since(createStart)
|
||||
if err != nil {
|
||||
utils.Error("创建默认系统配置失败: %v,耗时: %v", err, createDuration)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Info("创建默认系统配置成功,数量: %d,总耗时: %v", len(defaultConfigs), totalDuration)
|
||||
return defaultConfigs, nil
|
||||
}
|
||||
|
||||
@@ -198,17 +207,24 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
|
||||
|
||||
// 如果有缺失的配置项,则添加它们
|
||||
if len(missingConfigs) > 0 {
|
||||
upsertStart := utils.GetCurrentTime()
|
||||
err = r.UpsertConfigs(missingConfigs)
|
||||
upsertDuration := time.Since(upsertStart)
|
||||
if err != nil {
|
||||
utils.Error("添加缺失的系统配置失败: %v,耗时: %v", err, upsertDuration)
|
||||
return nil, err
|
||||
}
|
||||
utils.Debug("添加缺失的系统配置完成,数量: %d,耗时: %v", len(missingConfigs), upsertDuration)
|
||||
// 重新获取所有配置
|
||||
configs, err = r.FindAll()
|
||||
if err != nil {
|
||||
utils.Error("重新获取所有系统配置失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("GetOrCreateDefault完成,总数: %d,总耗时: %v", len(configs), totalDuration)
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -58,8 +61,15 @@ func (r *TaskItemRepositoryImpl) DeleteByTaskID(taskID uint) error {
|
||||
|
||||
// GetByTaskIDAndStatus 根据任务ID和状态获取任务项
|
||||
func (r *TaskItemRepositoryImpl) GetByTaskIDAndStatus(taskID uint, status string) ([]*entity.TaskItem, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var items []*entity.TaskItem
|
||||
err := r.db.Where("task_id = ? AND status = ?", taskID, status).Order("id ASC").Find(&items).Error
|
||||
queryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("GetByTaskIDAndStatus失败: 任务ID=%d, 状态=%s, 错误=%v, 查询耗时=%v", taskID, status, err, queryDuration)
|
||||
return nil, err
|
||||
}
|
||||
utils.Debug("GetByTaskIDAndStatus成功: 任务ID=%d, 状态=%s, 数量=%d, 查询耗时=%v", taskID, status, len(items), queryDuration)
|
||||
return items, err
|
||||
}
|
||||
|
||||
@@ -93,19 +103,36 @@ func (r *TaskItemRepositoryImpl) GetListByTaskID(taskID uint, page, pageSize int
|
||||
|
||||
// UpdateStatus 更新任务项状态
|
||||
func (r *TaskItemRepositoryImpl) UpdateStatus(id uint, status string) error {
|
||||
return r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Update("status", status).Error
|
||||
startTime := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Update("status", status).Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStatus失败: ID=%d, 状态=%s, 错误=%v, 更新耗时=%v", id, status, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStatus成功: ID=%d, 状态=%s, 更新耗时=%v", id, status, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStatusAndOutput 更新任务项状态和输出数据
|
||||
func (r *TaskItemRepositoryImpl) UpdateStatusAndOutput(id uint, status, outputData string) error {
|
||||
return r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
startTime := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"output_data": outputData,
|
||||
}).Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStatusAndOutput失败: ID=%d, 状态=%s, 错误=%v, 更新耗时=%v", id, status, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStatusAndOutput成功: ID=%d, 状态=%s, 更新耗时=%v", id, status, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStatsByTaskID 获取任务项统计信息
|
||||
func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var results []struct {
|
||||
Status string
|
||||
Count int
|
||||
@@ -117,7 +144,9 @@ func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int,
|
||||
Group("status").
|
||||
Find(&results).Error
|
||||
|
||||
queryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("GetStatsByTaskID失败: 任务ID=%d, 错误=%v, 查询耗时=%v", taskID, err, queryDuration)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -134,12 +163,22 @@ func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int,
|
||||
stats["total"] += result.Count
|
||||
}
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("GetStatsByTaskID成功: 任务ID=%d, 统计信息=%v, 查询耗时=%v, 总耗时=%v", taskID, stats, queryDuration, totalDuration)
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// ResetProcessingItems 重置处理中的任务项为pending状态
|
||||
func (r *TaskItemRepositoryImpl) ResetProcessingItems(taskID uint) error {
|
||||
return r.db.Model(&entity.TaskItem{}).
|
||||
startTime := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.TaskItem{}).
|
||||
Where("task_id = ? AND status = ?", taskID, "processing").
|
||||
Update("status", "pending").Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("ResetProcessingItems失败: 任务ID=%d, 错误=%v, 更新耗时=%v", taskID, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("ResetProcessingItems成功: 任务ID=%d, 更新耗时=%v", taskID, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -35,11 +36,15 @@ func NewTaskRepository(db *gorm.DB) TaskRepository {
|
||||
|
||||
// GetByID 根据ID获取任务
|
||||
func (r *TaskRepositoryImpl) GetByID(id uint) (*entity.Task, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var task entity.Task
|
||||
err := r.db.First(&task, id).Error
|
||||
queryDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Debug("GetByID失败: ID=%d, 错误=%v, 查询耗时=%v", id, err, queryDuration)
|
||||
return nil, err
|
||||
}
|
||||
utils.Debug("GetByID成功: ID=%d, 查询耗时=%v", id, queryDuration)
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
@@ -55,6 +60,7 @@ func (r *TaskRepositoryImpl) Delete(id uint) error {
|
||||
|
||||
// GetList 获取任务列表
|
||||
func (r *TaskRepositoryImpl) GetList(page, pageSize int, taskType, status string) ([]*entity.Task, int64, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
var tasks []*entity.Task
|
||||
var total int64
|
||||
|
||||
@@ -69,84 +75,171 @@ func (r *TaskRepositoryImpl) GetList(page, pageSize int, taskType, status string
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
countStart := utils.GetCurrentTime()
|
||||
err := query.Count(&total).Error
|
||||
countDuration := time.Since(countStart)
|
||||
if err != nil {
|
||||
utils.Error("GetList获取总数失败: 错误=%v, 查询耗时=%v", err, countDuration)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
queryStart := utils.GetCurrentTime()
|
||||
err = query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&tasks).Error
|
||||
queryDuration := time.Since(queryStart)
|
||||
if err != nil {
|
||||
utils.Error("GetList查询失败: 错误=%v, 查询耗时=%v", err, queryDuration)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("GetList完成: 任务类型=%s, 状态=%s, 页码=%d, 页面大小=%d, 总数=%d, 结果数=%d, 总耗时=%v", taskType, status, page, pageSize, total, len(tasks), totalDuration)
|
||||
return tasks, total, nil
|
||||
}
|
||||
|
||||
// UpdateStatus 更新任务状态
|
||||
func (r *TaskRepositoryImpl) UpdateStatus(id uint, status string) error {
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
|
||||
startTime := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStatus失败: ID=%d, 状态=%s, 错误=%v, 更新耗时=%v", id, status, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStatus成功: ID=%d, 状态=%s, 更新耗时=%v", id, status, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProgress 更新任务进度
|
||||
func (r *TaskRepositoryImpl) UpdateProgress(id uint, progress float64, progressData string) error {
|
||||
startTime := utils.GetCurrentTime()
|
||||
// 检查progress和progress_data字段是否存在
|
||||
var count int64
|
||||
err := r.db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'tasks' AND column_name = 'progress'").Count(&count).Error
|
||||
if err != nil || count == 0 {
|
||||
// 如果检查失败或字段不存在,只更新processed_items等现有字段
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
updateStart := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"processed_items": progress, // 使用progress作为processed_items的近似值
|
||||
}).Error
|
||||
updateDuration := time.Since(updateStart)
|
||||
totalDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateProgress失败(字段不存在): ID=%d, 进度=%f, 错误=%v, 更新耗时=%v, 总耗时=%v", id, progress, err, updateDuration, totalDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateProgress成功(字段不存在): ID=%d, 进度=%f, 更新耗时=%v, 总耗时=%v", id, progress, updateDuration, totalDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 字段存在,正常更新
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
updateStart := utils.GetCurrentTime()
|
||||
err = r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"progress": progress,
|
||||
"progress_data": progressData,
|
||||
}).Error
|
||||
updateDuration := time.Since(updateStart)
|
||||
totalDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateProgress失败: ID=%d, 进度=%f, 错误=%v, 更新耗时=%v, 总耗时=%v", id, progress, err, updateDuration, totalDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateProgress成功: ID=%d, 进度=%f, 更新耗时=%v, 总耗时=%v", id, progress, updateDuration, totalDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStatusAndMessage 更新任务状态和消息
|
||||
func (r *TaskRepositoryImpl) UpdateStatusAndMessage(id uint, status, message string) error {
|
||||
startTime := utils.GetCurrentTime()
|
||||
// 检查message字段是否存在
|
||||
var count int64
|
||||
err := r.db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'tasks' AND column_name = 'message'").Count(&count).Error
|
||||
if err != nil {
|
||||
// 如果检查失败,只更新状态
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
|
||||
updateStart := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
|
||||
updateDuration := time.Since(updateStart)
|
||||
totalDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStatusAndMessage失败(检查失败): ID=%d, 状态=%s, 错误=%v, 更新耗时=%v, 总耗时=%v", id, status, err, updateDuration, totalDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStatusAndMessage成功(检查失败): ID=%d, 状态=%s, 更新耗时=%v, 总耗时=%v", id, status, updateDuration, totalDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
// message字段存在,更新状态和消息
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
updateStart := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"message": message,
|
||||
}).Error
|
||||
updateDuration := time.Since(updateStart)
|
||||
totalDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStatusAndMessage失败(字段存在): ID=%d, 状态=%s, 错误=%v, 更新耗时=%v, 总耗时=%v", id, status, err, updateDuration, totalDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStatusAndMessage成功(字段存在): ID=%d, 状态=%s, 更新耗时=%v, 总耗时=%v", id, status, updateDuration, totalDuration)
|
||||
return nil
|
||||
} else {
|
||||
// message字段不存在,只更新状态
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
|
||||
updateStart := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
|
||||
updateDuration := time.Since(updateStart)
|
||||
totalDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStatusAndMessage失败(字段不存在): ID=%d, 状态=%s, 错误=%v, 更新耗时=%v, 总耗时=%v", id, status, err, updateDuration, totalDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStatusAndMessage成功(字段不存在): ID=%d, 状态=%s, 更新耗时=%v, 总耗时=%v", id, status, updateDuration, totalDuration)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTaskStats 更新任务统计信息
|
||||
func (r *TaskRepositoryImpl) UpdateTaskStats(id uint, processed, success, failed int) error {
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
startTime := utils.GetCurrentTime()
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"processed_items": processed,
|
||||
"success_items": success,
|
||||
"failed_items": failed,
|
||||
}).Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateTaskStats失败: ID=%d, 处理数=%d, 成功数=%d, 失败数=%d, 错误=%v, 更新耗时=%v", id, processed, success, failed, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateTaskStats成功: ID=%d, 处理数=%d, 成功数=%d, 失败数=%d, 更新耗时=%v", id, processed, success, failed, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStartedAt 更新任务开始时间
|
||||
func (r *TaskRepositoryImpl) UpdateStartedAt(id uint) error {
|
||||
startTime := utils.GetCurrentTime()
|
||||
now := time.Now()
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("started_at", now).Error
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("started_at", now).Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateStartedAt失败: ID=%d, 错误=%v, 更新耗时=%v", id, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateStartedAt成功: ID=%d, 更新耗时=%v", id, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateCompletedAt 更新任务完成时间
|
||||
func (r *TaskRepositoryImpl) UpdateCompletedAt(id uint) error {
|
||||
startTime := utils.GetCurrentTime()
|
||||
now := time.Now()
|
||||
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("completed_at", now).Error
|
||||
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("completed_at", now).Error
|
||||
updateDuration := time.Since(startTime)
|
||||
if err != nil {
|
||||
utils.Error("UpdateCompletedAt失败: ID=%d, 错误=%v, 更新耗时=%v", id, err, updateDuration)
|
||||
return err
|
||||
}
|
||||
utils.Debug("UpdateCompletedAt成功: ID=%d, 更新耗时=%v", id, updateDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
132
docs/logging.md
Normal file
132
docs/logging.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# 日志系统说明
|
||||
|
||||
## 概述
|
||||
|
||||
本项目使用自定义的日志系统,支持多种日志级别、环境差异化配置和结构化日志记录。
|
||||
|
||||
## 日志级别
|
||||
|
||||
日志系统支持以下级别(按严重程度递增):
|
||||
|
||||
1. **DEBUG** - 调试信息,用于开发和故障排除
|
||||
2. **INFO** - 一般信息,记录系统正常运行状态
|
||||
3. **WARN** - 警告信息,表示可能的问题但不影响系统运行
|
||||
4. **ERROR** - 错误信息,表示系统错误但可以继续运行
|
||||
5. **FATAL** - 致命错误,系统将退出
|
||||
|
||||
## 环境配置
|
||||
|
||||
### 日志级别配置
|
||||
|
||||
可以通过环境变量配置日志级别:
|
||||
|
||||
```bash
|
||||
# 设置日志级别(DEBUG, INFO, WARN, ERROR, FATAL)
|
||||
LOG_LEVEL=DEBUG
|
||||
|
||||
# 或者启用调试模式(等同于DEBUG级别)
|
||||
DEBUG=true
|
||||
```
|
||||
|
||||
默认情况下,开发环境使用DEBUG级别,生产环境使用INFO级别。
|
||||
|
||||
### 结构化日志
|
||||
|
||||
可以通过环境变量启用结构化日志(JSON格式):
|
||||
|
||||
```bash
|
||||
# 启用结构化日志
|
||||
STRUCTURED_LOG=true
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本日志记录
|
||||
|
||||
```go
|
||||
import "github.com/ctwj/urldb/utils"
|
||||
|
||||
// 基本日志记录
|
||||
utils.Debug("调试信息: %s", debugInfo)
|
||||
utils.Info("一般信息: %s", info)
|
||||
utils.Warn("警告信息: %s", warning)
|
||||
utils.Error("错误信息: %s", err)
|
||||
utils.Fatal("致命错误: %s", fatalErr) // 程序将退出
|
||||
```
|
||||
|
||||
### 结构化日志记录
|
||||
|
||||
结构化日志允许添加额外的字段信息,便于日志分析:
|
||||
|
||||
```go
|
||||
// 带字段的结构化日志
|
||||
utils.DebugWithFields(map[string]interface{}{
|
||||
"user_id": 123,
|
||||
"action": "login",
|
||||
"ip": "192.168.1.1",
|
||||
}, "用户登录调试信息")
|
||||
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_id": 456,
|
||||
"status": "completed",
|
||||
"duration_ms": 1250,
|
||||
}, "任务处理完成")
|
||||
|
||||
utils.ErrorWithFields(map[string]interface{}{
|
||||
"error_code": 500,
|
||||
"error": "database connection failed",
|
||||
"component": "database",
|
||||
}, "数据库连接失败: %v", err)
|
||||
```
|
||||
|
||||
## 日志输出
|
||||
|
||||
日志默认输出到:
|
||||
- 控制台(标准输出)
|
||||
- 文件(logs目录下的app_日期.log文件)
|
||||
|
||||
日志文件支持轮转,单个文件最大100MB,最多保留5个备份文件,日志文件最长保留30天。
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **选择合适的日志级别**:
|
||||
- DEBUG:详细的调试信息,仅在开发和故障排除时使用
|
||||
- INFO:重要的业务流程和状态变更
|
||||
- WARN:可预期的问题和异常情况
|
||||
- ERROR:系统错误和异常
|
||||
- FATAL:系统无法继续运行的致命错误
|
||||
|
||||
2. **使用结构化日志**:
|
||||
- 对于需要后续分析的日志,使用结构化日志
|
||||
- 添加有意义的字段,如用户ID、任务ID、请求ID等
|
||||
- 避免在字段中包含敏感信息
|
||||
|
||||
3. **性能监控**:
|
||||
- 记录关键操作的执行时间
|
||||
- 使用duration_ms字段记录毫秒级耗时
|
||||
|
||||
4. **安全日志**:
|
||||
- 记录所有认证和授权相关的操作
|
||||
- 包含客户端IP和用户信息
|
||||
- 记录失败的访问尝试
|
||||
|
||||
## 示例
|
||||
|
||||
```go
|
||||
// 性能监控示例
|
||||
startTime := time.Now()
|
||||
// 执行操作...
|
||||
duration := time.Since(startTime)
|
||||
utils.DebugWithFields(map[string]interface{}{
|
||||
"operation": "database_query",
|
||||
"duration_ms": duration.Milliseconds(),
|
||||
}, "数据库查询完成,耗时: %v", duration)
|
||||
|
||||
// 安全日志示例
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"ip": clientIP,
|
||||
"action": "login",
|
||||
"status": "success",
|
||||
}, "用户登录成功 - 用户ID: %d, IP: %s", userID, clientIP)
|
||||
```
|
||||
@@ -15,3 +15,8 @@ TIMEZONE=Asia/Shanghai
|
||||
# 文件上传配置
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=5MB
|
||||
|
||||
# 日志配置
|
||||
LOG_LEVEL=INFO # 日志级别 (DEBUG, INFO, WARN, ERROR, FATAL)
|
||||
DEBUG=false # 调试模式开关
|
||||
STRUCTURED_LOG=false
|
||||
@@ -148,6 +148,11 @@ func (h *PublicAPIHandler) AddBatchResources(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 记录API访问安全日志
|
||||
clientIP := c.ClientIP()
|
||||
userAgent := c.GetHeader("User-Agent")
|
||||
utils.Info("PublicAPI.AddBatchResources - API访问 - IP: %s, UserAgent: %s, 资源数量: %d", clientIP, userAgent, len(req.Resources))
|
||||
|
||||
// 收集所有待提交的URL,去重
|
||||
urlSet := make(map[string]struct{})
|
||||
for _, resource := range req.Resources {
|
||||
@@ -238,7 +243,9 @@ func (h *PublicAPIHandler) AddBatchResources(c *gin.Context) {
|
||||
func (h *PublicAPIHandler) SearchResources(c *gin.Context) {
|
||||
startTime := time.Now()
|
||||
|
||||
// 获取查询参数
|
||||
// 记录API访问安全日志
|
||||
clientIP := c.ClientIP()
|
||||
userAgent := c.GetHeader("User-Agent")
|
||||
keyword := c.Query("keyword")
|
||||
tag := c.Query("tag")
|
||||
category := c.Query("category")
|
||||
@@ -246,6 +253,9 @@ func (h *PublicAPIHandler) SearchResources(c *gin.Context) {
|
||||
pageStr := c.DefaultQuery("page", "1")
|
||||
pageSizeStr := c.DefaultQuery("page_size", "20")
|
||||
|
||||
utils.Info("PublicAPI.SearchResources - API访问 - IP: %s, UserAgent: %s, Keyword: %s, Tag: %s, Category: %s, PanID: %s",
|
||||
clientIP, userAgent, keyword, tag, category, panID)
|
||||
|
||||
page, err := strconv.Atoi(pageStr)
|
||||
if err != nil || page < 1 {
|
||||
page = 1
|
||||
@@ -402,9 +412,14 @@ func (h *PublicAPIHandler) SearchResources(c *gin.Context) {
|
||||
func (h *PublicAPIHandler) GetHotDramas(c *gin.Context) {
|
||||
startTime := time.Now()
|
||||
|
||||
// 记录API访问安全日志
|
||||
clientIP := c.ClientIP()
|
||||
userAgent := c.GetHeader("User-Agent")
|
||||
pageStr := c.DefaultQuery("page", "1")
|
||||
pageSizeStr := c.DefaultQuery("page_size", "20")
|
||||
|
||||
utils.Info("PublicAPI.GetHotDramas - API访问 - IP: %s, UserAgent: %s", clientIP, userAgent)
|
||||
|
||||
page, err := strconv.Atoi(pageStr)
|
||||
if err != nil || page < 1 {
|
||||
page = 1
|
||||
|
||||
@@ -130,6 +130,10 @@ func UpdateSystemConfig(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("UpdateSystemConfig - 管理员更新系统配置 - 管理员: %s, IP: %s", adminUsername, clientIP)
|
||||
|
||||
// 调试信息
|
||||
utils.Info("接收到的配置请求: %+v", req)
|
||||
|
||||
@@ -320,6 +324,10 @@ func ToggleAutoProcess(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("ToggleAutoProcess - 管理员切换自动处理配置 - 管理员: %s, 启用: %t, IP: %s", adminUsername, req.AutoProcessReadyResources, clientIP)
|
||||
|
||||
// 获取当前配置
|
||||
configs, err := repoManager.SystemConfigRepository.GetOrCreateDefault()
|
||||
if err != nil {
|
||||
|
||||
@@ -51,6 +51,10 @@ func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("CreateBatchTransferTask - 用户创建批量转存任务 - 用户: %s, 任务标题: %s, 资源数量: %d, IP: %s", username, req.Title, len(req.Resources), clientIP)
|
||||
|
||||
utils.Debug("创建批量转存任务: %s,资源数量: %d,选择账号数量: %d", req.Title, len(req.Resources), len(req.SelectedAccounts))
|
||||
|
||||
// 构建任务配置
|
||||
@@ -124,6 +128,10 @@ func (h *TaskHandler) StartTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("StartTask - 用户启动任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
|
||||
|
||||
err = h.taskManager.StartTask(uint(taskID))
|
||||
if err != nil {
|
||||
utils.Error("启动任务失败: %v", err)
|
||||
@@ -147,6 +155,10 @@ func (h *TaskHandler) StopTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("StopTask - 用户停止任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
|
||||
|
||||
err = h.taskManager.StopTask(uint(taskID))
|
||||
if err != nil {
|
||||
utils.Error("停止任务失败: %v", err)
|
||||
@@ -170,6 +182,10 @@ func (h *TaskHandler) PauseTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("PauseTask - 用户暂停任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
|
||||
|
||||
err = h.taskManager.PauseTask(uint(taskID))
|
||||
if err != nil {
|
||||
utils.Error("暂停任务失败: %v", err)
|
||||
@@ -360,8 +376,13 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("DeleteTask - 用户删除任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
|
||||
|
||||
// 检查任务是否在运行
|
||||
if h.taskManager.IsTaskRunning(uint(taskID)) {
|
||||
utils.Warn("DeleteTask - 尝试删除正在运行的任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
|
||||
ErrorResponse(c, "任务正在运行中,无法删除", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -383,6 +404,7 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
|
||||
}
|
||||
|
||||
utils.Debug("任务删除成功: %d", taskID)
|
||||
utils.Info("DeleteTask - 任务删除成功 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"message": "任务删除成功",
|
||||
@@ -402,6 +424,10 @@ func (h *TaskHandler) CreateExpansionTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("CreateExpansionTask - 用户创建扩容任务 - 用户: %s, 账号ID: %d, IP: %s", username, req.PanAccountID, clientIP)
|
||||
|
||||
utils.Debug("创建扩容任务: 账号ID %d", req.PanAccountID)
|
||||
|
||||
// 获取账号信息,用于构建任务标题
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"github.com/ctwj/urldb/db/dto"
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/middleware"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -20,18 +22,24 @@ func Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("Login - 尝试登录 - 用户名: %s, IP: %s", req.Username, clientIP)
|
||||
|
||||
user, err := repoManager.UserRepository.FindByUsername(req.Username)
|
||||
if err != nil {
|
||||
utils.Warn("Login - 用户不存在或密码错误 - 用户名: %s, IP: %s", req.Username, clientIP)
|
||||
ErrorResponse(c, "用户名或密码错误", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsActive {
|
||||
utils.Warn("Login - 账户已被禁用 - 用户名: %s, IP: %s", req.Username, clientIP)
|
||||
ErrorResponse(c, "账户已被禁用", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if !middleware.CheckPassword(req.Password, user.Password) {
|
||||
utils.Warn("Login - 密码错误 - 用户名: %s, IP: %s", req.Username, clientIP)
|
||||
ErrorResponse(c, "用户名或密码错误", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
@@ -42,10 +50,13 @@ func Login(c *gin.Context) {
|
||||
// 生成JWT令牌
|
||||
token, err := middleware.GenerateToken(user)
|
||||
if err != nil {
|
||||
utils.Error("Login - 生成令牌失败 - 用户名: %s, IP: %s, Error: %v", req.Username, clientIP, err)
|
||||
ErrorResponse(c, "生成令牌失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("Login - 登录成功 - 用户名: %s(ID:%d), IP: %s", req.Username, user.ID, clientIP)
|
||||
|
||||
response := dto.LoginResponse{
|
||||
Token: token,
|
||||
User: converter.ToUserResponse(user),
|
||||
@@ -62,9 +73,13 @@ func Register(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("Register - 尝试注册 - 用户名: %s, 邮箱: %s, IP: %s", req.Username, req.Email, clientIP)
|
||||
|
||||
// 检查用户名是否已存在
|
||||
existingUser, _ := repoManager.UserRepository.FindByUsername(req.Username)
|
||||
if existingUser != nil {
|
||||
utils.Warn("Register - 用户名已存在 - 用户名: %s, IP: %s", req.Username, clientIP)
|
||||
ErrorResponse(c, "用户名已存在", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -72,6 +87,7 @@ func Register(c *gin.Context) {
|
||||
// 检查邮箱是否已存在
|
||||
existingEmail, _ := repoManager.UserRepository.FindByEmail(req.Email)
|
||||
if existingEmail != nil {
|
||||
utils.Warn("Register - 邮箱已存在 - 邮箱: %s, IP: %s", req.Email, clientIP)
|
||||
ErrorResponse(c, "邮箱已存在", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -79,6 +95,7 @@ func Register(c *gin.Context) {
|
||||
// 哈希密码
|
||||
hashedPassword, err := middleware.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
utils.Error("Register - 密码加密失败 - 用户名: %s, IP: %s, Error: %v", req.Username, clientIP, err)
|
||||
ErrorResponse(c, "密码加密失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -93,10 +110,13 @@ func Register(c *gin.Context) {
|
||||
|
||||
err = repoManager.UserRepository.Create(user)
|
||||
if err != nil {
|
||||
utils.Error("Register - 创建用户失败 - 用户名: %s, IP: %s, Error: %v", req.Username, clientIP, err)
|
||||
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("Register - 注册成功 - 用户名: %s(ID:%d), 邮箱: %s, IP: %s", req.Username, user.ID, req.Email, clientIP)
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"message": "注册成功",
|
||||
"user": converter.ToUserResponse(user),
|
||||
@@ -123,9 +143,14 @@ func CreateUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("CreateUser - 管理员创建用户 - 管理员: %s, 新用户名: %s, IP: %s", adminUsername, req.Username, clientIP)
|
||||
|
||||
// 检查用户名是否已存在
|
||||
existingUser, _ := repoManager.UserRepository.FindByUsername(req.Username)
|
||||
if existingUser != nil {
|
||||
utils.Warn("CreateUser - 用户名已存在 - 管理员: %s, 用户名: %s, IP: %s", adminUsername, req.Username, clientIP)
|
||||
ErrorResponse(c, "用户名已存在", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -133,6 +158,7 @@ func CreateUser(c *gin.Context) {
|
||||
// 检查邮箱是否已存在
|
||||
existingEmail, _ := repoManager.UserRepository.FindByEmail(req.Email)
|
||||
if existingEmail != nil {
|
||||
utils.Warn("CreateUser - 邮箱已存在 - 管理员: %s, 邮箱: %s, IP: %s", adminUsername, req.Email, clientIP)
|
||||
ErrorResponse(c, "邮箱已存在", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -140,6 +166,7 @@ func CreateUser(c *gin.Context) {
|
||||
// 哈希密码
|
||||
hashedPassword, err := middleware.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
utils.Error("CreateUser - 密码加密失败 - 管理员: %s, 用户名: %s, IP: %s, Error: %v", adminUsername, req.Username, clientIP, err)
|
||||
ErrorResponse(c, "密码加密失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -154,10 +181,13 @@ func CreateUser(c *gin.Context) {
|
||||
|
||||
err = repoManager.UserRepository.Create(user)
|
||||
if err != nil {
|
||||
utils.Error("CreateUser - 创建用户失败 - 管理员: %s, 用户名: %s, IP: %s, Error: %v", adminUsername, req.Username, clientIP, err)
|
||||
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("CreateUser - 用户创建成功 - 管理员: %s, 用户名: %s(ID:%d), 角色: %s, IP: %s", adminUsername, req.Username, user.ID, req.Role, clientIP)
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"message": "用户创建成功",
|
||||
"user": converter.ToUserResponse(user),
|
||||
@@ -179,12 +209,21 @@ func UpdateUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("UpdateUser - 管理员更新用户 - 管理员: %s, 目标用户ID: %d, IP: %s", adminUsername, id, clientIP)
|
||||
|
||||
user, err := repoManager.UserRepository.FindByID(uint(id))
|
||||
if err != nil {
|
||||
utils.Warn("UpdateUser - 目标用户不存在 - 管理员: %s, 用户ID: %d, IP: %s", adminUsername, id, clientIP)
|
||||
ErrorResponse(c, "用户不存在", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 记录变更前的信息
|
||||
oldInfo := fmt.Sprintf("用户名:%s,邮箱:%s,角色:%s,状态:%t", user.Username, user.Email, user.Role, user.IsActive)
|
||||
utils.Debug("UpdateUser - 更新前用户信息 - 管理员: %s, 用户ID: %d, 信息: %s", adminUsername, id, oldInfo)
|
||||
|
||||
if req.Username != "" {
|
||||
user.Username = req.Username
|
||||
}
|
||||
@@ -198,10 +237,15 @@ func UpdateUser(c *gin.Context) {
|
||||
|
||||
err = repoManager.UserRepository.Update(user)
|
||||
if err != nil {
|
||||
utils.Error("UpdateUser - 更新用户失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
|
||||
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 记录变更后信息
|
||||
newInfo := fmt.Sprintf("用户名:%s,邮箱:%s,角色:%s,状态:%t", user.Username, user.Email, user.Role, user.IsActive)
|
||||
utils.Info("UpdateUser - 用户更新成功 - 管理员: %s, 用户ID: %d, 更新前: %s, 更新后: %s, IP: %s", adminUsername, id, oldInfo, newInfo, clientIP)
|
||||
|
||||
SuccessResponse(c, gin.H{"message": "用户更新成功"})
|
||||
}
|
||||
|
||||
@@ -220,8 +264,13 @@ func ChangePassword(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("ChangePassword - 管理员修改用户密码 - 管理员: %s, 目标用户ID: %d, IP: %s", adminUsername, id, clientIP)
|
||||
|
||||
user, err := repoManager.UserRepository.FindByID(uint(id))
|
||||
if err != nil {
|
||||
utils.Warn("ChangePassword - 目标用户不存在 - 管理员: %s, 用户ID: %d, IP: %s", adminUsername, id, clientIP)
|
||||
ErrorResponse(c, "用户不存在", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
@@ -229,6 +278,7 @@ func ChangePassword(c *gin.Context) {
|
||||
// 哈希新密码
|
||||
hashedPassword, err := middleware.HashPassword(req.NewPassword)
|
||||
if err != nil {
|
||||
utils.Error("ChangePassword - 密码加密失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
|
||||
ErrorResponse(c, "密码加密失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -236,10 +286,13 @@ func ChangePassword(c *gin.Context) {
|
||||
user.Password = hashedPassword
|
||||
err = repoManager.UserRepository.Update(user)
|
||||
if err != nil {
|
||||
utils.Error("ChangePassword - 更新密码失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
|
||||
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("ChangePassword - 密码修改成功 - 管理员: %s, 用户名: %s(ID:%d), IP: %s", adminUsername, user.Username, id, clientIP)
|
||||
|
||||
SuccessResponse(c, gin.H{"message": "密码修改成功"})
|
||||
}
|
||||
|
||||
@@ -252,12 +305,27 @@ func DeleteUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
adminUsername, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("DeleteUser - 管理员删除用户 - 管理员: %s, 目标用户ID: %d, IP: %s", adminUsername, id, clientIP)
|
||||
|
||||
// 先获取用户信息用于日志记录
|
||||
user, err := repoManager.UserRepository.FindByID(uint(id))
|
||||
if err != nil {
|
||||
utils.Warn("DeleteUser - 目标用户不存在 - 管理员: %s, 用户ID: %d, IP: %s", adminUsername, id, clientIP)
|
||||
ErrorResponse(c, "用户不存在", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err = repoManager.UserRepository.Delete(uint(id))
|
||||
if err != nil {
|
||||
utils.Error("DeleteUser - 删除用户失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
|
||||
ErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("DeleteUser - 用户删除成功 - 管理员: %s, 用户名: %s(ID:%d), IP: %s", adminUsername, user.Username, id, clientIP)
|
||||
|
||||
SuccessResponse(c, gin.H{"message": "用户删除成功"})
|
||||
}
|
||||
|
||||
@@ -269,12 +337,18 @@ func GetProfile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
utils.Info("GetProfile - 用户获取个人资料 - 用户名: %s(ID:%d), IP: %s", username, userID, clientIP)
|
||||
|
||||
user, err := repoManager.UserRepository.FindByID(userID.(uint))
|
||||
if err != nil {
|
||||
utils.Warn("GetProfile - 用户不存在 - 用户名: %s(ID:%d), IP: %s", username, userID, clientIP)
|
||||
ErrorResponse(c, "用户不存在", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
response := converter.ToUserResponse(user)
|
||||
utils.Debug("GetProfile - 成功获取个人资料 - 用户名: %s(ID:%d), IP: %s", username, userID, clientIP)
|
||||
SuccessResponse(c, response)
|
||||
}
|
||||
|
||||
1
main.go
1
main.go
@@ -41,7 +41,6 @@ func main() {
|
||||
if err := utils.InitLogger(nil); err != nil {
|
||||
log.Fatal("初始化日志系统失败:", err)
|
||||
}
|
||||
defer utils.GetLogger().Close()
|
||||
|
||||
// 加载环境变量
|
||||
if err := godotenv.Load(); err != nil {
|
||||
|
||||
@@ -27,11 +27,14 @@ type Claims struct {
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
// utils.Info("AuthMiddleware - 收到请求: %s %s", c.Request.Method, c.Request.URL.Path)
|
||||
// utils.Info("AuthMiddleware - Authorization头: %s", authHeader)
|
||||
clientIP := c.ClientIP()
|
||||
userAgent := c.Request.UserAgent()
|
||||
|
||||
utils.Debug("AuthMiddleware - 认证请求: %s %s, IP: %s, UserAgent: %s",
|
||||
c.Request.Method, c.Request.URL.Path, clientIP, userAgent)
|
||||
|
||||
if authHeader == "" {
|
||||
utils.Error("AuthMiddleware - 未提供认证令牌")
|
||||
utils.Warn("AuthMiddleware - 未提供认证令牌 - IP: %s, Path: %s", clientIP, c.Request.URL.Path)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未提供认证令牌"})
|
||||
c.Abort()
|
||||
return
|
||||
@@ -39,29 +42,31 @@ func AuthMiddleware() gin.HandlerFunc {
|
||||
|
||||
// 检查Bearer前缀
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
// utils.Error("AuthMiddleware - 无效的认证格式: %s", authHeader)
|
||||
utils.Warn("AuthMiddleware - 无效的认证格式 - IP: %s, Header: %s", clientIP, authHeader)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证格式"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
// utils.Info("AuthMiddleware - 解析令牌: %s", tokenString[:10]+"...")
|
||||
utils.Debug("AuthMiddleware - 解析令牌: %s...", tokenString[:utils.Min(len(tokenString), 10)])
|
||||
|
||||
claims, err := parseToken(tokenString)
|
||||
if err != nil {
|
||||
// utils.Error("AuthMiddleware - 令牌解析失败: %v", err)
|
||||
utils.Warn("AuthMiddleware - 令牌解析失败 - IP: %s, Error: %v", clientIP, err)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的令牌"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// utils.Info("AuthMiddleware - 令牌验证成功,用户: %s, 角色: %s", claims.Username, claims.Role)
|
||||
utils.Info("AuthMiddleware - 认证成功 - 用户: %s(ID:%d), 角色: %s, IP: %s",
|
||||
claims.Username, claims.UserID, claims.Role, clientIP)
|
||||
|
||||
// 将用户信息存储到上下文中
|
||||
c.Set("user_id", claims.UserID)
|
||||
c.Set("username", claims.Username)
|
||||
c.Set("role", claims.Role)
|
||||
c.Set("client_ip", clientIP)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
@@ -71,18 +76,23 @@ func AuthMiddleware() gin.HandlerFunc {
|
||||
func AdminMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
role, exists := c.Get("role")
|
||||
username, _ := c.Get("username")
|
||||
clientIP, _ := c.Get("client_ip")
|
||||
|
||||
if !exists {
|
||||
// c.JSON(http.StatusUnauthorized, gin.H{"error": "未认证"})
|
||||
utils.Warn("AdminMiddleware - 未认证访问管理员接口 - IP: %s, Path: %s", clientIP, c.Request.URL.Path)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if role != "admin" {
|
||||
// c.JSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"})
|
||||
utils.Warn("AdminMiddleware - 非管理员用户尝试访问管理员接口 - 用户: %s, 角色: %s, IP: %s, Path: %s",
|
||||
username, role, clientIP, c.Request.URL.Path)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
utils.Debug("AdminMiddleware - 管理员访问接口 - 用户: %s, IP: %s, Path: %s", username, clientIP, c.Request.URL.Path)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,25 +53,47 @@ type ExpansionOutput struct {
|
||||
|
||||
// Process 处理扩容任务项
|
||||
func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error {
|
||||
utils.Info("开始处理扩容任务项: %d", item.ID)
|
||||
startTime := utils.GetCurrentTime()
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_item_id": item.ID,
|
||||
"task_id": taskID,
|
||||
}, "开始处理扩容任务项: %d", item.ID)
|
||||
|
||||
// 解析输入数据
|
||||
parseStart := utils.GetCurrentTime()
|
||||
var input ExpansionInput
|
||||
if err := json.Unmarshal([]byte(item.InputData), &input); err != nil {
|
||||
parseDuration := time.Since(parseStart)
|
||||
utils.ErrorWithFields(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"duration_ms": parseDuration.Milliseconds(),
|
||||
}, "解析输入数据失败: %v,耗时: %v", err, parseDuration)
|
||||
return fmt.Errorf("解析输入数据失败: %v", err)
|
||||
}
|
||||
parseDuration := time.Since(parseStart)
|
||||
utils.DebugWithFields(map[string]interface{}{
|
||||
"duration_ms": parseDuration.Milliseconds(),
|
||||
}, "解析输入数据完成,耗时: %v", parseDuration)
|
||||
|
||||
// 验证输入数据
|
||||
validateStart := utils.GetCurrentTime()
|
||||
if err := ep.validateInput(&input); err != nil {
|
||||
validateDuration := time.Since(validateStart)
|
||||
utils.Error("输入数据验证失败: %v,耗时: %v", err, validateDuration)
|
||||
return fmt.Errorf("输入数据验证失败: %v", err)
|
||||
}
|
||||
validateDuration := time.Since(validateStart)
|
||||
utils.Debug("输入数据验证完成,耗时: %v", validateDuration)
|
||||
|
||||
// 检查账号是否已经扩容过
|
||||
checkExpansionStart := utils.GetCurrentTime()
|
||||
exists, err := ep.checkExpansionExists(input.PanAccountID)
|
||||
checkExpansionDuration := time.Since(checkExpansionStart)
|
||||
if err != nil {
|
||||
utils.Error("检查扩容记录失败: %v", err)
|
||||
utils.Error("检查扩容记录失败: %v,耗时: %v", err, checkExpansionDuration)
|
||||
return fmt.Errorf("检查扩容记录失败: %v", err)
|
||||
}
|
||||
utils.Debug("检查扩容记录完成,耗时: %v", checkExpansionDuration)
|
||||
|
||||
if exists {
|
||||
output := ExpansionOutput{
|
||||
@@ -89,7 +111,9 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
|
||||
}
|
||||
|
||||
// 检查账号类型(只支持quark账号)
|
||||
checkAccountTypeStart := utils.GetCurrentTime()
|
||||
if err := ep.checkAccountType(input.PanAccountID); err != nil {
|
||||
checkAccountTypeDuration := time.Since(checkAccountTypeStart)
|
||||
output := ExpansionOutput{
|
||||
Success: false,
|
||||
Message: "账号类型不支持扩容",
|
||||
@@ -100,12 +124,16 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Error("账号类型不支持扩容: %v", err)
|
||||
utils.Error("账号类型不支持扩容: %v,耗时: %v", err, checkAccountTypeDuration)
|
||||
return err
|
||||
}
|
||||
checkAccountTypeDuration := time.Since(checkAccountTypeStart)
|
||||
utils.Debug("检查账号类型完成,耗时: %v", checkAccountTypeDuration)
|
||||
|
||||
// 执行扩容操作(传入数据源)
|
||||
expansionStart := utils.GetCurrentTime()
|
||||
transferred, err := ep.performExpansion(ctx, input.PanAccountID, input.DataSource)
|
||||
expansionDuration := time.Since(expansionStart)
|
||||
if err != nil {
|
||||
output := ExpansionOutput{
|
||||
Success: false,
|
||||
@@ -117,9 +145,10 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Error("扩容任务项处理失败: %d, 错误: %v", item.ID, err)
|
||||
utils.Error("扩容任务项处理失败: %d, 错误: %v,总耗时: %v", item.ID, err, expansionDuration)
|
||||
return fmt.Errorf("扩容失败: %v", err)
|
||||
}
|
||||
utils.Debug("扩容操作完成,耗时: %v", expansionDuration)
|
||||
|
||||
// 扩容成功
|
||||
output := ExpansionOutput{
|
||||
@@ -132,27 +161,44 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Info("扩容任务项处理完成: %d, 账号ID: %d", item.ID, input.PanAccountID)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_item_id": item.ID,
|
||||
"account_id": input.PanAccountID,
|
||||
"duration_ms": elapsedTime.Milliseconds(),
|
||||
}, "扩容任务项处理完成: %d, 账号ID: %d, 总耗时: %v", item.ID, input.PanAccountID, elapsedTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateInput 验证输入数据
|
||||
func (ep *ExpansionProcessor) validateInput(input *ExpansionInput) error {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
if input.PanAccountID == 0 {
|
||||
utils.Error("账号ID验证失败,账号ID不能为空,耗时: %v", time.Since(startTime))
|
||||
return fmt.Errorf("账号ID不能为空")
|
||||
}
|
||||
|
||||
utils.Debug("输入数据验证完成,耗时: %v", time.Since(startTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkExpansionExists 检查账号是否已经扩容过
|
||||
func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
// 查询所有expansion类型的任务
|
||||
tasksStart := utils.GetCurrentTime()
|
||||
tasks, _, err := ep.repoMgr.TaskRepository.GetList(1, 1000, "expansion", "completed")
|
||||
tasksDuration := time.Since(tasksStart)
|
||||
if err != nil {
|
||||
utils.Error("获取扩容任务列表失败: %v,耗时: %v", err, tasksDuration)
|
||||
return false, fmt.Errorf("获取扩容任务列表失败: %v", err)
|
||||
}
|
||||
utils.Debug("获取扩容任务列表完成,找到 %d 个任务,耗时: %v", len(tasks), tasksDuration)
|
||||
|
||||
// 检查每个任务的配置中是否包含该账号ID
|
||||
checkStart := utils.GetCurrentTime()
|
||||
for _, task := range tasks {
|
||||
if task.Config != "" {
|
||||
var taskConfig map[string]interface{}
|
||||
@@ -162,6 +208,8 @@ func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, err
|
||||
// 找到了该账号的扩容任务,检查任务状态
|
||||
if task.Status == "completed" {
|
||||
// 如果任务已完成,说明已经扩容过
|
||||
checkDuration := time.Since(checkStart)
|
||||
utils.Debug("检查扩容记录完成,账号已扩容,耗时: %v", checkDuration)
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
@@ -169,40 +217,63 @@ func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, err
|
||||
}
|
||||
}
|
||||
}
|
||||
checkDuration := time.Since(checkStart)
|
||||
utils.Debug("检查扩容记录完成,账号未扩容,耗时: %v", checkDuration)
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("检查扩容记录完成,账号未扩容,总耗时: %v", totalDuration)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// checkAccountType 检查账号类型(只支持quark账号)
|
||||
func (ep *ExpansionProcessor) checkAccountType(panAccountID uint) error {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
// 获取账号信息
|
||||
accountStart := utils.GetCurrentTime()
|
||||
cks, err := ep.repoMgr.CksRepository.FindByID(panAccountID)
|
||||
accountDuration := time.Since(accountStart)
|
||||
if err != nil {
|
||||
utils.Error("获取账号信息失败: %v,耗时: %v", err, accountDuration)
|
||||
return fmt.Errorf("获取账号信息失败: %v", err)
|
||||
}
|
||||
utils.Debug("获取账号信息完成,耗时: %v", accountDuration)
|
||||
|
||||
// 检查是否为quark账号
|
||||
serviceCheckStart := utils.GetCurrentTime()
|
||||
if cks.ServiceType != "quark" {
|
||||
serviceCheckDuration := time.Since(serviceCheckStart)
|
||||
utils.Error("账号类型检查失败,当前账号类型: %s,耗时: %v", cks.ServiceType, serviceCheckDuration)
|
||||
return fmt.Errorf("只支持quark账号扩容,当前账号类型: %s", cks.ServiceType)
|
||||
}
|
||||
serviceCheckDuration := time.Since(serviceCheckStart)
|
||||
utils.Debug("账号类型检查完成,为quark账号,耗时: %v", serviceCheckDuration)
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("账号类型检查完成,总耗时: %v", totalDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// performExpansion 执行扩容操作
|
||||
func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID uint, dataSource map[string]interface{}) ([]TransferredResource, error) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
startTime := utils.GetCurrentTime()
|
||||
utils.Info("执行扩容操作,账号ID: %d, 数据源: %v", panAccountID, dataSource)
|
||||
|
||||
transferred := []TransferredResource{}
|
||||
|
||||
// 获取账号信息
|
||||
accountStart := utils.GetCurrentTime()
|
||||
account, err := ep.repoMgr.CksRepository.FindByID(panAccountID)
|
||||
accountDuration := time.Since(accountStart)
|
||||
if err != nil {
|
||||
utils.Error("获取账号信息失败: %v,耗时: %v", err, accountDuration)
|
||||
return nil, fmt.Errorf("获取账号信息失败: %v", err)
|
||||
}
|
||||
utils.Debug("获取账号信息完成,耗时: %v", accountDuration)
|
||||
|
||||
// 创建网盘服务工厂
|
||||
serviceStart := utils.GetCurrentTime()
|
||||
factory := pan.NewPanFactory()
|
||||
service, err := factory.CreatePanServiceByType(pan.Quark, &pan.PanConfig{
|
||||
URL: "",
|
||||
@@ -210,10 +281,13 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
|
||||
IsType: 0,
|
||||
Cookie: account.Ck,
|
||||
})
|
||||
serviceDuration := time.Since(serviceStart)
|
||||
if err != nil {
|
||||
utils.Error("创建网盘服务失败: %v,耗时: %v", err, serviceDuration)
|
||||
return nil, fmt.Errorf("创建网盘服务失败: %v", err)
|
||||
}
|
||||
service.SetCKSRepository(ep.repoMgr.CksRepository, *account)
|
||||
utils.Debug("创建网盘服务完成,耗时: %v", serviceDuration)
|
||||
|
||||
// 定义扩容分类列表(按优先级排序)
|
||||
categories := []string{
|
||||
@@ -246,11 +320,14 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
|
||||
utils.Info("开始处理分类: %s", category)
|
||||
|
||||
// 获取该分类的资源
|
||||
resourcesStart := utils.GetCurrentTime()
|
||||
resources, err := ep.getHotResources(category)
|
||||
resourcesDuration := time.Since(resourcesStart)
|
||||
if err != nil {
|
||||
utils.Error("获取分类 %s 的资源失败: %v", category, err)
|
||||
utils.Error("获取分类 %s 的资源失败: %v,耗时: %v", category, err, resourcesDuration)
|
||||
continue
|
||||
}
|
||||
utils.Debug("获取分类 %s 的资源完成,耗时: %v", category, resourcesDuration)
|
||||
|
||||
if len(resources) == 0 {
|
||||
utils.Info("分类 %s 没有可用资源,跳过", category)
|
||||
@@ -269,11 +346,14 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
|
||||
}
|
||||
|
||||
// 检查是否还有存储空间
|
||||
storageCheckStart := utils.GetCurrentTime()
|
||||
hasSpace, err := ep.checkStorageSpace(service, &account.Ck)
|
||||
storageCheckDuration := time.Since(storageCheckStart)
|
||||
if err != nil {
|
||||
utils.Error("检查存储空间失败: %v", err)
|
||||
utils.Error("检查存储空间失败: %v,耗时: %v", err, storageCheckDuration)
|
||||
return transferred, fmt.Errorf("检查存储空间失败: %v", err)
|
||||
}
|
||||
utils.Debug("检查存储空间完成,耗时: %v", storageCheckDuration)
|
||||
|
||||
if !hasSpace {
|
||||
utils.Info("存储空间不足,停止扩容,但保存已转存的资源")
|
||||
@@ -282,24 +362,30 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
|
||||
}
|
||||
|
||||
// 获取资源 , dataSourceType, thirdPartyURL
|
||||
resourceGetStart := utils.GetCurrentTime()
|
||||
resource, err := ep.getResourcesByHot(resource, dataSourceType, thirdPartyURL, *account, service)
|
||||
resourceGetDuration := time.Since(resourceGetStart)
|
||||
if resource == nil || err != nil {
|
||||
if resource != nil {
|
||||
utils.Error("获取资源失败: %s, 错误: %v", resource.Title, err)
|
||||
utils.Error("获取资源失败: %s, 错误: %v,耗时: %v", resource.Title, err, resourceGetDuration)
|
||||
} else {
|
||||
utils.Error("获取资源失败, 错误: %v", err)
|
||||
utils.Error("获取资源失败, 错误: %v,耗时: %v", err, resourceGetDuration)
|
||||
}
|
||||
totalFailed++
|
||||
continue
|
||||
}
|
||||
utils.Debug("获取资源完成,耗时: %v", resourceGetDuration)
|
||||
|
||||
// 执行转存
|
||||
transferStart := utils.GetCurrentTime()
|
||||
saveURL, err := ep.transferResource(ctx, service, resource, *account)
|
||||
transferDuration := time.Since(transferStart)
|
||||
if err != nil {
|
||||
utils.Error("转存资源失败: %s, 错误: %v", resource.Title, err)
|
||||
utils.Error("转存资源失败: %s, 错误: %v,耗时: %v", resource.Title, err, transferDuration)
|
||||
totalFailed++
|
||||
continue
|
||||
}
|
||||
utils.Debug("转存资源完成,耗时: %v", transferDuration)
|
||||
|
||||
// 随机休眠1-3秒,避免请求过于频繁
|
||||
sleepDuration := time.Duration(rand.Intn(3)+1) * time.Second
|
||||
@@ -324,7 +410,8 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
|
||||
utils.Info("分类 %s 处理完成,转存 %d 个资源", category, transferredCount)
|
||||
}
|
||||
|
||||
utils.Info("扩容完成,总共转存: %d 个资源,失败: %d 个资源", totalTransferred, totalFailed)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.Info("扩容完成,总共转存: %d 个资源,失败: %d 个资源,总耗时: %v", totalTransferred, totalFailed, elapsedTime)
|
||||
return transferred, nil
|
||||
}
|
||||
|
||||
@@ -335,27 +422,45 @@ func (ep *ExpansionProcessor) getResourcesByHot(
|
||||
entity entity.Cks,
|
||||
service pan.PanService,
|
||||
) (*entity.Resource, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
if dataSourceType == "third-party" && thirdPartyURL != "" {
|
||||
// 从第三方API获取资源
|
||||
return ep.getResourcesFromThirdPartyAPI(resource, thirdPartyURL)
|
||||
thirdPartyStart := utils.GetCurrentTime()
|
||||
result, err := ep.getResourcesFromThirdPartyAPI(resource, thirdPartyURL)
|
||||
thirdPartyDuration := time.Since(thirdPartyStart)
|
||||
utils.Debug("从第三方API获取资源完成,耗时: %v", thirdPartyDuration)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 从内部数据库获取资源
|
||||
return ep.getResourcesFromInternalDB(resource, entity, service)
|
||||
internalStart := utils.GetCurrentTime()
|
||||
result, err := ep.getResourcesFromInternalDB(resource, entity, service)
|
||||
internalDuration := time.Since(internalStart)
|
||||
utils.Debug("从内部数据库获取资源完成,耗时: %v", internalDuration)
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("获取资源完成: %s,总耗时: %v", resource.Title, totalDuration)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// getResourcesFromInternalDB 根据 HotDrama 的title 获取数据库中资源,并且资源的类型和 account 的资源类型一致
|
||||
func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDrama, account entity.Cks, service pan.PanService) (*entity.Resource, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
// 修改配置 isType = 1 只检测,不转存
|
||||
configStart := utils.GetCurrentTime()
|
||||
service.UpdateConfig(&pan.PanConfig{
|
||||
URL: "",
|
||||
ExpiredType: 0,
|
||||
IsType: 1,
|
||||
Cookie: account.Ck,
|
||||
})
|
||||
utils.Debug("更新服务配置完成,耗时: %v", time.Since(configStart))
|
||||
panID := account.PanID
|
||||
|
||||
// 1. 搜索标题
|
||||
searchStart := utils.GetCurrentTime()
|
||||
params := map[string]interface{}{
|
||||
"search": HotDrama.Title,
|
||||
"pan_id": panID,
|
||||
@@ -364,11 +469,15 @@ func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDra
|
||||
"page_size": 10,
|
||||
}
|
||||
resources, _, err := ep.repoMgr.ResourceRepository.SearchWithFilters(params)
|
||||
searchDuration := time.Since(searchStart)
|
||||
if err != nil {
|
||||
utils.Error("搜索资源失败: %v,耗时: %v", err, searchDuration)
|
||||
return nil, fmt.Errorf("搜索资源失败: %v", err)
|
||||
}
|
||||
utils.Debug("搜索资源完成,找到 %d 个资源,耗时: %v", len(resources), searchDuration)
|
||||
|
||||
// 检查结果是否有效,通过服务验证
|
||||
validateStart := utils.GetCurrentTime()
|
||||
for _, res := range resources {
|
||||
if res.IsValid && res.URL != "" {
|
||||
// 使用服务验证资源是否可转存
|
||||
@@ -376,38 +485,59 @@ func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDra
|
||||
if shareID != "" {
|
||||
result, err := service.Transfer(shareID)
|
||||
if err == nil && result != nil && result.Success {
|
||||
validateDuration := time.Since(validateStart)
|
||||
utils.Debug("验证资源成功: %s,耗时: %v", res.Title, validateDuration)
|
||||
return &res, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
validateDuration := time.Since(validateStart)
|
||||
utils.Debug("验证资源完成,未找到有效资源,耗时: %v", validateDuration)
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("从内部数据库获取资源完成: %s,总耗时: %v", HotDrama.Title, totalDuration)
|
||||
// 3. 没有有效资源,返回错误信息
|
||||
return nil, fmt.Errorf("未找到有效的资源")
|
||||
}
|
||||
|
||||
// getResourcesFromInternalDB 从内部数据库获取资源
|
||||
func (ep *ExpansionProcessor) getHotResources(category string) ([]*entity.HotDrama, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
// 获取该分类下sub_type为"排行"的资源
|
||||
rankedStart := utils.GetCurrentTime()
|
||||
dramas, _, err := ep.repoMgr.HotDramaRepository.FindByCategoryAndSubType(category, "排行", 1, 20)
|
||||
rankedDuration := time.Since(rankedStart)
|
||||
if err != nil {
|
||||
utils.Error("获取分类 %s 的排行资源失败: %v,耗时: %v", category, err, rankedDuration)
|
||||
return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err)
|
||||
}
|
||||
utils.Debug("获取分类 %s 的排行资源完成,找到 %d 个资源,耗时: %v", category, len(dramas), rankedDuration)
|
||||
|
||||
// 如果没有找到"排行"类型的资源,尝试获取该分类下的所有资源
|
||||
if len(dramas) == 0 {
|
||||
allStart := utils.GetCurrentTime()
|
||||
dramas, _, err = ep.repoMgr.HotDramaRepository.FindByCategory(category, 1, 20)
|
||||
allDuration := time.Since(allStart)
|
||||
if err != nil {
|
||||
utils.Error("获取分类 %s 的所有资源失败: %v,耗时: %v", category, err, allDuration)
|
||||
return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err)
|
||||
}
|
||||
utils.Debug("获取分类 %s 的所有资源完成,找到 %d 个资源,耗时: %v", category, len(dramas), allDuration)
|
||||
}
|
||||
|
||||
// 转换为指针数组
|
||||
convertStart := utils.GetCurrentTime()
|
||||
result := make([]*entity.HotDrama, len(dramas))
|
||||
for i := range dramas {
|
||||
result[i] = &dramas[i]
|
||||
}
|
||||
convertDuration := time.Since(convertStart)
|
||||
utils.Debug("转换资源数组完成,耗时: %v", convertDuration)
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("获取热门资源完成: 分类 %s,总数 %d,总耗时: %v", category, len(result), totalDuration)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -423,50 +553,69 @@ func (ep *ExpansionProcessor) getResourcesFromThirdPartyAPI(resource *entity.Hot
|
||||
|
||||
// checkStorageSpace 检查存储空间是否足够
|
||||
func (ep *ExpansionProcessor) checkStorageSpace(service pan.PanService, ck *string) (bool, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
userInfoStart := utils.GetCurrentTime()
|
||||
userInfo, err := service.GetUserInfo(ck)
|
||||
userInfoDuration := time.Since(userInfoStart)
|
||||
if err != nil {
|
||||
utils.Error("获取用户信息失败: %v", err)
|
||||
utils.Error("获取用户信息失败: %v,耗时: %v", err, userInfoDuration)
|
||||
// 如果无法获取用户信息,假设还有空间继续
|
||||
return true, nil
|
||||
}
|
||||
utils.Debug("获取用户信息完成,耗时: %v", userInfoDuration)
|
||||
|
||||
// 检查是否还有足够的空间(保留至少10GB空间)
|
||||
const reservedSpaceGB = 100
|
||||
reservedSpaceBytes := int64(reservedSpaceGB * 1024 * 1024 * 1024)
|
||||
|
||||
if userInfo.TotalSpace-userInfo.UsedSpace <= reservedSpaceBytes {
|
||||
utils.Info("存储空间不足,已使用: %d bytes,总容量: %d bytes",
|
||||
userInfo.UsedSpace, userInfo.TotalSpace)
|
||||
utils.Info("存储空间不足,已使用: %d bytes,总容量: %d bytes,检查耗时: %v",
|
||||
userInfo.UsedSpace, userInfo.TotalSpace, time.Since(startTime))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("存储空间检查完成,有足够空间,耗时: %v", totalDuration)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// transferResource 执行单个资源的转存
|
||||
func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.PanService, res *entity.Resource, account entity.Cks) (string, error) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
|
||||
// 修改配置 isType = 0 转存
|
||||
configStart := utils.GetCurrentTime()
|
||||
service.UpdateConfig(&pan.PanConfig{
|
||||
URL: "",
|
||||
ExpiredType: 0,
|
||||
IsType: 0,
|
||||
Cookie: account.Ck,
|
||||
})
|
||||
utils.Debug("更新服务配置完成,耗时: %v", time.Since(configStart))
|
||||
|
||||
// 如果没有URL,跳过转存
|
||||
if res.URL == "" {
|
||||
utils.Error("资源 %s 没有有效的URL", res.URL)
|
||||
return "", fmt.Errorf("资源 %s 没有有效的URL", res.URL)
|
||||
}
|
||||
|
||||
// 提取分享ID
|
||||
extractStart := utils.GetCurrentTime()
|
||||
shareID, _ := pan.ExtractShareId(res.URL)
|
||||
extractDuration := time.Since(extractStart)
|
||||
if shareID == "" {
|
||||
utils.Error("无法从URL %s 提取分享ID,耗时: %v", res.URL, extractDuration)
|
||||
return "", fmt.Errorf("无法从URL %s 提取分享ID", res.URL)
|
||||
}
|
||||
utils.Debug("提取分享ID完成: %s,耗时: %v", shareID, extractDuration)
|
||||
|
||||
// 执行转存
|
||||
transferStart := utils.GetCurrentTime()
|
||||
result, err := service.Transfer(shareID)
|
||||
transferDuration := time.Since(transferStart)
|
||||
if err != nil {
|
||||
utils.Error("转存失败: %v,耗时: %v", err, transferDuration)
|
||||
return "", fmt.Errorf("转存失败: %v", err)
|
||||
}
|
||||
|
||||
@@ -475,10 +624,12 @@ func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.
|
||||
if result != nil {
|
||||
errorMsg = result.Message
|
||||
}
|
||||
utils.Error("转存结果失败: %s,耗时: %v", errorMsg, time.Since(transferStart))
|
||||
return "", fmt.Errorf("转存失败: %s", errorMsg)
|
||||
}
|
||||
|
||||
// 提取转存链接
|
||||
extractURLStart := utils.GetCurrentTime()
|
||||
var saveURL string
|
||||
if result.Data != nil {
|
||||
if data, ok := result.Data.(map[string]interface{}); ok {
|
||||
@@ -490,11 +641,14 @@ func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.
|
||||
if saveURL == "" {
|
||||
saveURL = result.ShareURL
|
||||
}
|
||||
|
||||
if saveURL == "" {
|
||||
extractURLDuration := time.Since(extractURLStart)
|
||||
utils.Error("转存成功但未获取到分享链接,耗时: %v", extractURLDuration)
|
||||
return "", fmt.Errorf("转存成功但未获取到分享链接")
|
||||
}
|
||||
|
||||
totalDuration := time.Since(startTime)
|
||||
utils.Debug("转存资源完成: %s -> %s,总耗时: %v", res.Title, saveURL, totalDuration)
|
||||
return saveURL, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/db/repo"
|
||||
@@ -90,7 +91,9 @@ func (tm *TaskManager) StartTask(taskID uint) error {
|
||||
// 启动后台任务
|
||||
go tm.processTask(ctx, task, processor)
|
||||
|
||||
utils.Info("StartTask: 任务 %d 启动成功", taskID)
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_id": taskID,
|
||||
}, "StartTask: 任务 %d 启动成功", taskID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -185,14 +188,19 @@ func (tm *TaskManager) StopTask(taskID uint) error {
|
||||
|
||||
// processTask 处理任务
|
||||
func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, processor TaskProcessor) {
|
||||
startTime := utils.GetCurrentTime()
|
||||
defer func() {
|
||||
tm.mu.Lock()
|
||||
delete(tm.running, task.ID)
|
||||
tm.mu.Unlock()
|
||||
utils.Debug("processTask: 任务 %d 处理完成,清理资源", task.ID)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.Info("processTask: 任务 %d 处理完成,耗时: %v,清理资源", task.ID, elapsedTime)
|
||||
}()
|
||||
|
||||
utils.Debug("processTask: 开始处理任务: %d, 类型: %s", task.ID, task.Type)
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_id": task.ID,
|
||||
"task_type": task.Type,
|
||||
}, "processTask: 开始处理任务: %d, 类型: %s", task.ID, task.Type)
|
||||
|
||||
// 更新任务状态为运行中
|
||||
err := tm.repoMgr.TaskRepository.UpdateStatus(task.ID, "running")
|
||||
@@ -208,7 +216,9 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
|
||||
}
|
||||
|
||||
// 获取任务项统计信息,用于计算正确的进度
|
||||
statsStart := utils.GetCurrentTime()
|
||||
stats, err := tm.repoMgr.TaskItemRepository.GetStatsByTaskID(task.ID)
|
||||
statsDuration := time.Since(statsStart)
|
||||
if err != nil {
|
||||
utils.Error("获取任务项统计失败: %v", err)
|
||||
stats = map[string]int{
|
||||
@@ -218,14 +228,20 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
}
|
||||
} else {
|
||||
utils.Debug("获取任务项统计完成,耗时: %v", statsDuration)
|
||||
}
|
||||
|
||||
// 获取待处理的任务项
|
||||
itemsStart := utils.GetCurrentTime()
|
||||
items, err := tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending")
|
||||
itemsDuration := time.Since(itemsStart)
|
||||
if err != nil {
|
||||
utils.Error("获取任务项失败: %v", err)
|
||||
tm.markTaskFailed(task.ID, fmt.Sprintf("获取任务项失败: %v", err))
|
||||
return
|
||||
} else {
|
||||
utils.Debug("获取任务项完成,数量: %d,耗时: %v", len(items), itemsDuration)
|
||||
}
|
||||
|
||||
// 计算总任务项数和已完成的项数
|
||||
@@ -236,10 +252,14 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
|
||||
|
||||
// 如果当前批次有处理中的任务项,重置它们为pending状态(服务器重启恢复)
|
||||
if processingItems > 0 {
|
||||
utils.Debug("任务 %d 发现 %d 个处理中的任务项,重置为pending状态", task.ID, processingItems)
|
||||
utils.Info("任务 %d 发现 %d 个处理中的任务项,重置为pending状态", task.ID, processingItems)
|
||||
resetStart := utils.GetCurrentTime()
|
||||
err = tm.repoMgr.TaskItemRepository.ResetProcessingItems(task.ID)
|
||||
resetDuration := time.Since(resetStart)
|
||||
if err != nil {
|
||||
utils.Error("重置处理中任务项失败: %v", err)
|
||||
} else {
|
||||
utils.Debug("重置处理中任务项完成,耗时: %v", resetDuration)
|
||||
}
|
||||
// 重新获取待处理的任务项
|
||||
items, err = tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending")
|
||||
@@ -258,21 +278,35 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
|
||||
utils.Debug("任务 %d 统计信息: 总计=%d, 已完成=%d, 已失败=%d, 待处理=%d",
|
||||
task.ID, totalItems, completedItems, failedItems, currentBatchItems)
|
||||
|
||||
for _, item := range items {
|
||||
// 记录处理开始时间
|
||||
batchStartTime := utils.GetCurrentTime()
|
||||
|
||||
for i, item := range items {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
utils.Debug("任务 %d 被取消", task.ID)
|
||||
return
|
||||
default:
|
||||
// 记录单个任务项处理开始时间
|
||||
itemStartTime := utils.GetCurrentTime()
|
||||
|
||||
// 处理单个任务项
|
||||
err := tm.processTaskItem(ctx, task.ID, item, processor)
|
||||
processedItems++
|
||||
|
||||
// 记录单个任务项处理耗时
|
||||
itemDuration := time.Since(itemStartTime)
|
||||
|
||||
if err != nil {
|
||||
failedItems++
|
||||
utils.Error("处理任务项 %d 失败: %v", item.ID, err)
|
||||
utils.ErrorWithFields(map[string]interface{}{
|
||||
"task_item_id": item.ID,
|
||||
"error": err.Error(),
|
||||
"duration_ms": itemDuration.Milliseconds(),
|
||||
}, "处理任务项 %d 失败: %v,耗时: %v", item.ID, err, itemDuration)
|
||||
} else {
|
||||
successItems++
|
||||
utils.Info("处理任务项 %d 成功,耗时: %v", item.ID, itemDuration)
|
||||
}
|
||||
|
||||
// 更新任务进度(基于总任务项数)
|
||||
@@ -280,9 +314,21 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
|
||||
progress := float64(processedItems) / float64(totalItems) * 100
|
||||
tm.updateTaskProgress(task.ID, progress, processedItems, successItems, failedItems)
|
||||
}
|
||||
|
||||
// 每处理10个任务项记录一次批处理进度
|
||||
if (i+1)%10 == 0 || i == len(items)-1 {
|
||||
batchDuration := time.Since(batchStartTime)
|
||||
utils.Info("任务 %d 批处理进度: 已处理 %d/%d 项,成功 %d 项,失败 %d 项,当前批处理耗时: %v",
|
||||
task.ID, processedItems, totalItems, successItems, failedItems, batchDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 记录整个批处理耗时
|
||||
batchDuration := time.Since(batchStartTime)
|
||||
utils.Info("任务 %d 批处理完成: 总计 %d 项,成功 %d 项,失败 %d 项,总耗时: %v",
|
||||
task.ID, len(items), successItems, failedItems, batchDuration)
|
||||
|
||||
// 任务完成
|
||||
status := "completed"
|
||||
message := fmt.Sprintf("任务完成,共处理 %d 项,成功 %d 项,失败 %d 项", processedItems, successItems, failedItems)
|
||||
@@ -308,25 +354,41 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
|
||||
}
|
||||
}
|
||||
|
||||
utils.Info("任务 %d 处理完成: %s", task.ID, message)
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_id": task.ID,
|
||||
"message": message,
|
||||
}, "任务 %d 处理完成: %s", task.ID, message)
|
||||
}
|
||||
|
||||
// processTaskItem 处理单个任务项
|
||||
func (tm *TaskManager) processTaskItem(ctx context.Context, taskID uint, item *entity.TaskItem, processor TaskProcessor) error {
|
||||
itemStartTime := utils.GetCurrentTime()
|
||||
utils.Debug("开始处理任务项: %d (任务ID: %d)", item.ID, taskID)
|
||||
|
||||
// 更新任务项状态为处理中
|
||||
updateStart := utils.GetCurrentTime()
|
||||
err := tm.repoMgr.TaskItemRepository.UpdateStatus(item.ID, "processing")
|
||||
updateDuration := time.Since(updateStart)
|
||||
if err != nil {
|
||||
utils.Error("更新任务项状态失败: %v,耗时: %v", err, updateDuration)
|
||||
return fmt.Errorf("更新任务项状态失败: %v", err)
|
||||
} else {
|
||||
utils.Debug("更新任务项状态为处理中完成,耗时: %v", updateDuration)
|
||||
}
|
||||
|
||||
// 处理任务项
|
||||
processStart := utils.GetCurrentTime()
|
||||
err = processor.Process(ctx, taskID, item)
|
||||
processDuration := time.Since(processStart)
|
||||
|
||||
if err != nil {
|
||||
// 处理失败
|
||||
utils.Error("处理任务项 %d 失败: %v,处理耗时: %v", item.ID, err, processDuration)
|
||||
|
||||
outputData := map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"time": utils.GetCurrentTime(),
|
||||
"duration_ms": processDuration.Milliseconds(),
|
||||
}
|
||||
outputJSON, _ := json.Marshal(outputData)
|
||||
|
||||
@@ -338,25 +400,49 @@ func (tm *TaskManager) processTaskItem(ctx context.Context, taskID uint, item *e
|
||||
}
|
||||
|
||||
// 处理成功
|
||||
utils.Info("处理任务项 %d 成功,处理耗时: %v", item.ID, processDuration)
|
||||
|
||||
// 如果处理器已经设置了 output_data(比如 ExpansionProcessor),则不覆盖
|
||||
var outputJSON string
|
||||
if item.OutputData == "" {
|
||||
outputData := map[string]interface{}{
|
||||
"success": true,
|
||||
"time": utils.GetCurrentTime(),
|
||||
"duration_ms": processDuration.Milliseconds(),
|
||||
}
|
||||
outputBytes, _ := json.Marshal(outputData)
|
||||
outputJSON = string(outputBytes)
|
||||
} else {
|
||||
// 使用处理器设置的 output_data
|
||||
outputJSON = item.OutputData
|
||||
// 使用处理器设置的 output_data,并添加处理时间信息
|
||||
var existingOutput map[string]interface{}
|
||||
if json.Unmarshal([]byte(item.OutputData), &existingOutput) == nil {
|
||||
existingOutput["duration_ms"] = processDuration.Milliseconds()
|
||||
outputBytes, _ := json.Marshal(existingOutput)
|
||||
outputJSON = string(outputBytes)
|
||||
} else {
|
||||
// 如果无法解析现有输出,保留原样并添加时间信息
|
||||
outputData := map[string]interface{}{
|
||||
"original_output": item.OutputData,
|
||||
"success": true,
|
||||
"time": utils.GetCurrentTime(),
|
||||
"duration_ms": processDuration.Milliseconds(),
|
||||
}
|
||||
outputBytes, _ := json.Marshal(outputData)
|
||||
outputJSON = string(outputBytes)
|
||||
}
|
||||
}
|
||||
|
||||
updateSuccessStart := utils.GetCurrentTime()
|
||||
err = tm.repoMgr.TaskItemRepository.UpdateStatusAndOutput(item.ID, "completed", outputJSON)
|
||||
updateSuccessDuration := time.Since(updateSuccessStart)
|
||||
if err != nil {
|
||||
utils.Error("更新成功任务项状态失败: %v", err)
|
||||
utils.Error("更新成功任务项状态失败: %v,耗时: %v", err, updateSuccessDuration)
|
||||
} else {
|
||||
utils.Debug("更新成功任务项状态完成,耗时: %v", updateSuccessDuration)
|
||||
}
|
||||
|
||||
itemDuration := time.Since(itemStartTime)
|
||||
utils.Debug("任务项 %d 处理完成,总耗时: %v", item.ID, itemDuration)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -51,20 +51,42 @@ type TransferOutput struct {
|
||||
|
||||
// Process 处理转存任务项
|
||||
func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error {
|
||||
utils.Info("开始处理转存任务项: %d", item.ID)
|
||||
startTime := utils.GetCurrentTime()
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_item_id": item.ID,
|
||||
"task_id": taskID,
|
||||
}, "开始处理转存任务项: %d", item.ID)
|
||||
|
||||
// 解析输入数据
|
||||
parseStart := utils.GetCurrentTime()
|
||||
var input TransferInput
|
||||
if err := json.Unmarshal([]byte(item.InputData), &input); err != nil {
|
||||
parseDuration := time.Since(parseStart)
|
||||
utils.ErrorWithFields(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"duration_ms": parseDuration.Milliseconds(),
|
||||
}, "解析输入数据失败: %v,耗时: %v", err, parseDuration)
|
||||
return fmt.Errorf("解析输入数据失败: %v", err)
|
||||
}
|
||||
parseDuration := time.Since(parseStart)
|
||||
utils.DebugWithFields(map[string]interface{}{
|
||||
"duration_ms": parseDuration.Milliseconds(),
|
||||
}, "解析输入数据完成,耗时: %v", parseDuration)
|
||||
|
||||
// 验证输入数据
|
||||
validateStart := utils.GetCurrentTime()
|
||||
if err := tp.validateInput(&input); err != nil {
|
||||
validateDuration := time.Since(validateStart)
|
||||
utils.Error("输入数据验证失败: %v,耗时: %v", err, validateDuration)
|
||||
return fmt.Errorf("输入数据验证失败: %v", err)
|
||||
}
|
||||
validateDuration := time.Since(validateStart)
|
||||
utils.DebugWithFields(map[string]interface{}{
|
||||
"duration_ms": validateDuration.Milliseconds(),
|
||||
}, "输入数据验证完成,耗时: %v", validateDuration)
|
||||
|
||||
// 获取任务配置中的账号信息
|
||||
configStart := utils.GetCurrentTime()
|
||||
var selectedAccounts []uint
|
||||
task, err := tp.repoMgr.TaskRepository.GetByID(taskID)
|
||||
if err == nil && task.Config != "" {
|
||||
@@ -79,15 +101,21 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
|
||||
}
|
||||
}
|
||||
}
|
||||
configDuration := time.Since(configStart)
|
||||
utils.Debug("获取任务配置完成,耗时: %v", configDuration)
|
||||
|
||||
if len(selectedAccounts) == 0 {
|
||||
utils.Error("失败: %v", "没有指定转存账号")
|
||||
}
|
||||
|
||||
// 检查资源是否已存在
|
||||
checkStart := utils.GetCurrentTime()
|
||||
exists, existingResource, err := tp.checkResourceExists(input.URL)
|
||||
checkDuration := time.Since(checkStart)
|
||||
if err != nil {
|
||||
utils.Error("检查资源是否存在失败: %v", err)
|
||||
utils.Error("检查资源是否存在失败: %v,耗时: %v", err, checkDuration)
|
||||
} else {
|
||||
utils.Debug("检查资源是否存在完成,耗时: %v", checkDuration)
|
||||
}
|
||||
|
||||
if exists {
|
||||
@@ -107,19 +135,26 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Info("资源已存在且有转存链接,跳过转存: %s", input.Title)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.Info("资源已存在且有转存链接,跳过转存: %s,总耗时: %v", input.Title, elapsedTime)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 查询出 账号列表
|
||||
cksStart := utils.GetCurrentTime()
|
||||
cks, err := tp.repoMgr.CksRepository.FindByIds(selectedAccounts)
|
||||
cksDuration := time.Since(cksStart)
|
||||
if err != nil {
|
||||
utils.Error("读取账号失败: %v", err)
|
||||
utils.Error("读取账号失败: %v,耗时: %v", err, cksDuration)
|
||||
} else {
|
||||
utils.Debug("读取账号完成,账号数量: %d,耗时: %v", len(cks), cksDuration)
|
||||
}
|
||||
|
||||
// 执行转存操作
|
||||
transferStart := utils.GetCurrentTime()
|
||||
resourceID, saveURL, err := tp.performTransfer(ctx, &input, cks)
|
||||
transferDuration := time.Since(transferStart)
|
||||
if err != nil {
|
||||
// 转存失败,更新输出数据
|
||||
output := TransferOutput{
|
||||
@@ -131,7 +166,13 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Error("转存任务项处理失败: %d, 错误: %v", item.ID, err)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.ErrorWithFields(map[string]interface{}{
|
||||
"task_item_id": item.ID,
|
||||
"error": err.Error(),
|
||||
"duration_ms": transferDuration.Milliseconds(),
|
||||
"total_ms": elapsedTime.Milliseconds(),
|
||||
}, "转存任务项处理失败: %d, 错误: %v,转存耗时: %v,总耗时: %v", item.ID, err, transferDuration, elapsedTime)
|
||||
return fmt.Errorf("转存失败: %v", err)
|
||||
}
|
||||
|
||||
@@ -146,7 +187,8 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Error("转存任务项处理失败: %d, 未获取到分享链接", item.ID)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.Error("转存任务项处理失败: %d, 未获取到分享链接,总耗时: %v", item.ID, elapsedTime)
|
||||
return fmt.Errorf("转存成功但未获取到分享链接")
|
||||
}
|
||||
|
||||
@@ -161,7 +203,14 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
|
||||
outputJSON, _ := json.Marshal(output)
|
||||
item.OutputData = string(outputJSON)
|
||||
|
||||
utils.Info("转存任务项处理完成: %d, 资源ID: %d, 转存链接: %s", item.ID, resourceID, saveURL)
|
||||
elapsedTime := time.Since(startTime)
|
||||
utils.InfoWithFields(map[string]interface{}{
|
||||
"task_item_id": item.ID,
|
||||
"resource_id": resourceID,
|
||||
"save_url": saveURL,
|
||||
"transfer_duration_ms": transferDuration.Milliseconds(),
|
||||
"total_duration_ms": elapsedTime.Milliseconds(),
|
||||
}, "转存任务项处理完成: %d, 资源ID: %d, 转存链接: %s,转存耗时: %v,总耗时: %v", item.ID, resourceID, saveURL, transferDuration, elapsedTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
217
utils/logger.go
217
utils/logger.go
@@ -1,12 +1,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -40,6 +42,16 @@ func (l LogLevel) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredLogEntry 结构化日志条目
|
||||
type StructuredLogEntry struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Caller string `json:"caller"`
|
||||
Module string `json:"module"`
|
||||
Fields map[string]interface{} `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// Logger 统一日志器
|
||||
type Logger struct {
|
||||
debugLogger *log.Logger
|
||||
@@ -63,22 +75,72 @@ type LogConfig struct {
|
||||
EnableConsole bool // 是否启用控制台输出
|
||||
EnableFile bool // 是否启用文件输出
|
||||
EnableRotation bool // 是否启用日志轮转
|
||||
StructuredLog bool // 是否启用结构化日志格式
|
||||
}
|
||||
|
||||
// DefaultConfig 默认配置
|
||||
func DefaultConfig() *LogConfig {
|
||||
// 从环境变量获取日志级别,默认为INFO
|
||||
logLevel := getLogLevelFromEnv()
|
||||
|
||||
return &LogConfig{
|
||||
LogDir: "logs",
|
||||
LogLevel: INFO,
|
||||
LogLevel: logLevel,
|
||||
MaxFileSize: 100, // 100MB
|
||||
MaxBackups: 5,
|
||||
MaxAge: 30, // 30天
|
||||
EnableConsole: true,
|
||||
EnableFile: true,
|
||||
EnableRotation: true,
|
||||
StructuredLog: os.Getenv("STRUCTURED_LOG") == "true", // 从环境变量控制结构化日志
|
||||
}
|
||||
}
|
||||
|
||||
// getLogLevelFromEnv 从环境变量获取日志级别
|
||||
func getLogLevelFromEnv() LogLevel {
|
||||
envLogLevel := os.Getenv("LOG_LEVEL")
|
||||
envDebug := os.Getenv("DEBUG")
|
||||
|
||||
// 如果设置了DEBUG环境变量为true,则使用DEBUG级别
|
||||
if envDebug == "true" || envDebug == "1" {
|
||||
return DEBUG
|
||||
}
|
||||
|
||||
// 根据LOG_LEVEL环境变量设置日志级别
|
||||
switch strings.ToUpper(envLogLevel) {
|
||||
case "DEBUG":
|
||||
return DEBUG
|
||||
case "INFO":
|
||||
return INFO
|
||||
case "WARN", "WARNING":
|
||||
return WARN
|
||||
case "ERROR":
|
||||
return ERROR
|
||||
case "FATAL":
|
||||
return FATAL
|
||||
default:
|
||||
// 根据运行环境设置默认级别:开发环境DEBUG,生产环境INFO
|
||||
if isDevelopment() {
|
||||
return DEBUG
|
||||
}
|
||||
return INFO
|
||||
}
|
||||
}
|
||||
|
||||
// isDevelopment 判断是否为开发环境
|
||||
func isDevelopment() bool {
|
||||
env := os.Getenv("GO_ENV")
|
||||
return env == "development" || env == "dev" || env == "local" || env == "test"
|
||||
}
|
||||
|
||||
// getEnvironment 获取当前环境类型
|
||||
func (l *Logger) getEnvironment() string {
|
||||
if isDevelopment() {
|
||||
return "development"
|
||||
}
|
||||
return "production"
|
||||
}
|
||||
|
||||
var (
|
||||
globalLogger *Logger
|
||||
onceLogger sync.Once
|
||||
@@ -135,6 +197,11 @@ func NewLogger(config *LogConfig) (*Logger, error) {
|
||||
go logger.startRotationCheck()
|
||||
}
|
||||
|
||||
// 打印日志配置信息
|
||||
logger.Info("日志系统初始化完成 - 级别: %s, 环境: %s",
|
||||
config.LogLevel.String(),
|
||||
logger.getEnvironment())
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
@@ -200,26 +267,55 @@ func (l *Logger) log(level LogLevel, format string, args ...interface{}) {
|
||||
line = 0
|
||||
}
|
||||
|
||||
// 提取文件名
|
||||
// 提取文件名作为模块名
|
||||
fileName := filepath.Base(file)
|
||||
moduleName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
|
||||
|
||||
// 格式化消息
|
||||
message := fmt.Sprintf(format, args...)
|
||||
|
||||
// 添加调用位置信息
|
||||
fullMessage := fmt.Sprintf("[%s:%d] %s", fileName, line, message)
|
||||
caller := fmt.Sprintf("%s:%d", fileName, line)
|
||||
|
||||
if l.config.StructuredLog {
|
||||
// 结构化日志格式
|
||||
entry := StructuredLogEntry{
|
||||
Timestamp: GetCurrentTime(),
|
||||
Level: level.String(),
|
||||
Message: message,
|
||||
Caller: caller,
|
||||
Module: moduleName,
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
// 如果JSON序列化失败,回退到普通格式
|
||||
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s", level.String(), fileName, line, message)
|
||||
l.logToLevel(level, fullMessage)
|
||||
return
|
||||
}
|
||||
|
||||
l.logToLevel(level, string(jsonBytes))
|
||||
} else {
|
||||
// 普通文本格式
|
||||
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s", level.String(), fileName, line, message)
|
||||
l.logToLevel(level, fullMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// logToLevel 根据级别输出日志
|
||||
func (l *Logger) logToLevel(level LogLevel, message string) {
|
||||
switch level {
|
||||
case DEBUG:
|
||||
l.debugLogger.Println(fullMessage)
|
||||
l.debugLogger.Println(message)
|
||||
case INFO:
|
||||
l.infoLogger.Println(fullMessage)
|
||||
l.infoLogger.Println(message)
|
||||
case WARN:
|
||||
l.warnLogger.Println(fullMessage)
|
||||
l.warnLogger.Println(message)
|
||||
case ERROR:
|
||||
l.errorLogger.Println(fullMessage)
|
||||
l.errorLogger.Println(message)
|
||||
case FATAL:
|
||||
l.fatalLogger.Println(fullMessage)
|
||||
l.fatalLogger.Println(message)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -328,15 +424,83 @@ func (l *Logger) cleanOldLogs() {
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭日志器
|
||||
func (l *Logger) Close() error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
if l.file != nil {
|
||||
return l.file.Close()
|
||||
// Min 返回两个整数中的较小值
|
||||
func Min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 结构化日志方法
|
||||
func (l *Logger) DebugWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
l.logWithFields(DEBUG, fields, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) InfoWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
l.logWithFields(INFO, fields, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) WarnWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
l.logWithFields(WARN, fields, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) ErrorWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
l.logWithFields(ERROR, fields, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) FatalWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
l.logWithFields(FATAL, fields, format, args...)
|
||||
}
|
||||
|
||||
// logWithFields 带字段的结构化日志方法
|
||||
func (l *Logger) logWithFields(level LogLevel, fields map[string]interface{}, format string, args ...interface{}) {
|
||||
if level < l.config.LogLevel {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取调用者信息
|
||||
_, file, line, ok := runtime.Caller(2)
|
||||
if !ok {
|
||||
file = "unknown"
|
||||
line = 0
|
||||
}
|
||||
|
||||
// 提取文件名作为模块名
|
||||
fileName := filepath.Base(file)
|
||||
moduleName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
|
||||
|
||||
// 格式化消息
|
||||
message := fmt.Sprintf(format, args...)
|
||||
|
||||
// 添加调用位置信息
|
||||
caller := fmt.Sprintf("%s:%d", fileName, line)
|
||||
|
||||
if l.config.StructuredLog {
|
||||
// 结构化日志格式
|
||||
entry := StructuredLogEntry{
|
||||
Timestamp: GetCurrentTime(),
|
||||
Level: level.String(),
|
||||
Message: message,
|
||||
Caller: caller,
|
||||
Module: moduleName,
|
||||
Fields: fields,
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
// 如果JSON序列化失败,回退到普通格式
|
||||
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s - Fields: %v", level.String(), fileName, line, message, fields)
|
||||
l.logToLevel(level, fullMessage)
|
||||
return
|
||||
}
|
||||
|
||||
l.logToLevel(level, string(jsonBytes))
|
||||
} else {
|
||||
// 普通文本格式
|
||||
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s - Fields: %v", level.String(), fileName, line, message, fields)
|
||||
l.logToLevel(level, fullMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 全局便捷函数
|
||||
@@ -359,3 +523,24 @@ func Error(format string, args ...interface{}) {
|
||||
func Fatal(format string, args ...interface{}) {
|
||||
GetLogger().Fatal(format, args...)
|
||||
}
|
||||
|
||||
// 全局结构化日志便捷函数
|
||||
func DebugWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
GetLogger().DebugWithFields(fields, format, args...)
|
||||
}
|
||||
|
||||
func InfoWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
GetLogger().InfoWithFields(fields, format, args...)
|
||||
}
|
||||
|
||||
func WarnWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
GetLogger().WarnWithFields(fields, format, args...)
|
||||
}
|
||||
|
||||
func ErrorWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
GetLogger().ErrorWithFields(fields, format, args...)
|
||||
}
|
||||
|
||||
func FatalWithFields(fields map[string]interface{}, format string, args ...interface{}) {
|
||||
GetLogger().FatalWithFields(fields, format, args...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user