diff --git a/models/download.go b/models/download.go index f0c2b74..207f560 100644 --- a/models/download.go +++ b/models/download.go @@ -29,6 +29,7 @@ type Download struct { // 数据库忽略字段 StatusInfo rpc.StatusInfo `gorm:"-"` + Task *Task `gorm:"-"` } // AfterFind 找到下载任务后的钩子,处理Status结构 @@ -38,6 +39,10 @@ func (task *Download) AfterFind() (err error) { err = json.Unmarshal([]byte(task.Attrs), &task.StatusInfo) } + if task.TaskID != 0 { + task.Task, _ = GetTasksByID(task.TaskID) + } + return err } @@ -72,10 +77,15 @@ func GetDownloadsByStatus(status ...int) []Download { } // GetDownloadsByStatusAndUser 根据状态检索和用户ID下载 +// page 为 0 表示列出所有,非零时分页 // TODO 测试 -func GetDownloadsByStatusAndUser(uid uint, status ...int) []Download { +func GetDownloadsByStatusAndUser(page, uid uint, status ...int) []Download { var tasks []Download - DB.Where("user_id = ? and status in (?)", uid, status).Find(&tasks) + dbChain := DB + if page > 0 { + dbChain = dbChain.Limit(10).Offset((page - 1) * 10).Order("updated_at DESC") + } + dbChain.Where("user_id = ? and status in (?)", uid, status).Find(&tasks) return tasks } diff --git a/models/task.go b/models/task.go index 1efbada..bfeb52f 100644 --- a/models/task.go +++ b/models/task.go @@ -41,8 +41,16 @@ func (task *Task) SetError(err string) error { } // GetTasksByStatus 根据状态检索任务 -func GetTasksByStatus(status int) []Task { +func GetTasksByStatus(status ...int) []Task { var tasks []Task - DB.Where("status = ?", status).Find(&tasks) + DB.Where("status in (?)", status).Find(&tasks) return tasks } + +// GetTasksByID 根据ID检索任务 +// TODO 测试 +func GetTasksByID(id interface{}) (*Task, error) { + task := &Task{} + result := DB.Where("id = ?", id).First(task) + return task, result.Error +} diff --git a/pkg/aria2/monitor.go b/pkg/aria2/monitor.go index fe6a409..9579051 100644 --- a/pkg/aria2/monitor.go +++ b/pkg/aria2/monitor.go @@ -23,6 +23,7 @@ type Monitor struct { Interval time.Duration notifier chan StatusEvent + retried int } // StatusEvent 状态改变事件 @@ -68,11 +69,20 @@ func (monitor *Monitor) Loop() { func (monitor *Monitor) Update() bool { status, err := Instance.Status(monitor.Task) if err != nil { + monitor.retried++ util.Log().Warning("无法获取下载任务[%s]的状态,%s", monitor.Task.GID, err) - monitor.setErrorStatus(err) - monitor.RemoveTempFolder() - return true + + // 十次重试后认定为任务失败 + if monitor.retried > 10 { + util.Log().Warning("无法获取下载任务[%s]的状态,超过最大重试次数限制,%s", monitor.Task.GID, err) + monitor.setErrorStatus(err) + monitor.RemoveTempFolder() + return true + } + + return false } + monitor.retried = 0 // 磁力链下载需要跟随 if len(status.FollowedBy) > 0 { diff --git a/pkg/filesystem/manage.go b/pkg/filesystem/manage.go index 6e82c00..fded098 100644 --- a/pkg/filesystem/manage.go +++ b/pkg/filesystem/manage.go @@ -253,9 +253,8 @@ func (fs *FileSystem) ListDeleteFiles(ctx context.Context, ids []uint) error { func (fs *FileSystem) List(ctx context.Context, dirPath string, pathProcessor func(string) string) ([]Object, error) { // 获取父目录 isExist, folder := fs.IsPathExist(dirPath) - // 不存在时返回空的结果 if !isExist { - return []Object{}, nil + return nil, ErrPathNotExist } fs.SetTargetDir(&[]model.Folder{*folder}) diff --git a/pkg/serializer/aria2.go b/pkg/serializer/aria2.go index a366cad..613f0be 100644 --- a/pkg/serializer/aria2.go +++ b/pkg/serializer/aria2.go @@ -12,8 +12,6 @@ type DownloadListResponse struct { UpdateInterval int `json:"interval"` Name string `json:"name"` Status int `json:"status"` - UserID uint `json:"uid"` - Error string `json:"error"` Dst string `json:"dst"` Total uint64 `json:"total"` Downloaded uint64 `json:"downloaded"` @@ -21,6 +19,58 @@ type DownloadListResponse struct { Info rpc.StatusInfo `json:"info"` } +// FinishedListResponse 已完成任务条目 +type FinishedListResponse struct { + Name string `json:"name"` + Status int `json:"status"` + Dst string `json:"dst"` + Error string `json:"error"` + Total uint64 `json:"total"` + Files []rpc.FileInfo `json:"files"` + TaskStatus int `json:"task_status"` + TaskError string `json:"task_error"` + CreateTime string `json:"create"` + UpdateTime string `json:"update"` +} + +// BuildFinishedListResponse 构建已完成任务条目 +func BuildFinishedListResponse(tasks []model.Download) Response { + resp := make([]FinishedListResponse, 0, len(tasks)) + + for i := 0; i < len(tasks); i++ { + fileName := tasks[i].StatusInfo.BitTorrent.Info.Name + if len(tasks[i].StatusInfo.Files) == 1 { + fileName = path.Base(tasks[i].StatusInfo.Files[0].Path) + } + + // 过滤敏感信息 + for i2 := 0; i2 < len(tasks[i].StatusInfo.Files); i2++ { + tasks[i].StatusInfo.Files[i2].Path = path.Base(tasks[i].StatusInfo.Files[i2].Path) + } + + download := FinishedListResponse{ + Name: fileName, + Status: tasks[i].Status, + Error: tasks[i].Error, + Dst: tasks[i].Dst, + Total: tasks[i].TotalSize, + Files: tasks[i].StatusInfo.Files, + TaskStatus: -1, + UpdateTime: tasks[i].UpdatedAt.Format("2006-01-02 15:04:05"), + CreateTime: tasks[i].CreatedAt.Format("2006-01-02 15:04:05"), + } + + if tasks[i].Task != nil { + download.TaskError = tasks[i].Task.Error + download.TaskStatus = tasks[i].Task.Status + } + + resp = append(resp, download) + } + + return Response{Data: resp} +} + // BuildDownloadingResponse 构建正在下载的列表响应 func BuildDownloadingResponse(tasks []model.Download) Response { resp := make([]DownloadListResponse, 0, len(tasks)) @@ -43,8 +93,6 @@ func BuildDownloadingResponse(tasks []model.Download) Response { UpdateInterval: interval, Name: fileName, Status: tasks[i].Status, - UserID: tasks[i].UserID, - Error: tasks[i].Error, Dst: tasks[i].Dst, Total: tasks[i].TotalSize, Downloaded: tasks[i].DownloadedSize, diff --git a/pkg/task/job.go b/pkg/task/job.go index 2379ec3..caac69f 100644 --- a/pkg/task/job.go +++ b/pkg/task/job.go @@ -77,7 +77,7 @@ func Record(job Job) (*model.Task, error) { // Resume 从数据库中恢复未完成任务 func Resume() { - tasks := model.GetTasksByStatus(Queued) + tasks := model.GetTasksByStatus(Queued, Processing) if len(tasks) == 0 { return } diff --git a/routers/controllers/aria2.go b/routers/controllers/aria2.go index 30ed8b4..f26ecf5 100644 --- a/routers/controllers/aria2.go +++ b/routers/controllers/aria2.go @@ -92,3 +92,14 @@ func ListDownloading(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// ListFinished 获取已完成的任务 +func ListFinished(c *gin.Context) { + var service aria2.DownloadListService + if err := c.ShouldBindQuery(&service); err == nil { + res := service.Finished(c, CurrentUser(c)) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} diff --git a/routers/controllers/file.go b/routers/controllers/file.go index f18738a..3942466 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -341,3 +341,14 @@ func GetUploadCredential(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// SearchFile 搜索文件 +func SearchFile(c *gin.Context) { + var service explorer.ItemDecompressService + if err := c.ShouldBindJSON(&service); err == nil { + res := service.CreateDecompressTask(c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} diff --git a/routers/router.go b/routers/router.go index 56b8fb4..26958eb 100644 --- a/routers/router.go +++ b/routers/router.go @@ -271,6 +271,8 @@ func InitMasterRouter() *gin.Engine { file.POST("compress", controllers.Compress) // 创建文件解压缩任务 file.POST("decompress", controllers.Decompress) + // 创建文件解压缩任务 + file.GET("search/:type/:keywords", controllers.SearchFile) } // 离线下载任务 @@ -286,6 +288,8 @@ func InitMasterRouter() *gin.Engine { aria2.DELETE("task/:gid", controllers.CancelAria2Download) // 获取正在下载中的任务 aria2.GET("downloading", controllers.ListDownloading) + // 获取已完成的任务 + aria2.GET("finished", controllers.ListFinished) } // 目录 diff --git a/service/aria2/manage.go b/service/aria2/manage.go index c087903..f36ab30 100644 --- a/service/aria2/manage.go +++ b/service/aria2/manage.go @@ -19,12 +19,20 @@ type DownloadTaskService struct { // DownloadListService 下载列表服务 type DownloadListService struct { + Page uint `form:"page"` +} + +// Finished 获取已完成的任务 +func (service *DownloadListService) Finished(c *gin.Context, user *model.User) serializer.Response { + // 查找下载记录 + downloads := model.GetDownloadsByStatusAndUser(service.Page, user.ID, aria2.Error, aria2.Complete, aria2.Canceled, aria2.Unknown) + return serializer.BuildFinishedListResponse(downloads) } // Downloading 获取正在下载中的任务 func (service *DownloadListService) Downloading(c *gin.Context, user *model.User) serializer.Response { // 查找下载记录 - downloads := model.GetDownloadsByStatusAndUser(user.ID, aria2.Downloading, aria2.Paused, aria2.Ready) + downloads := model.GetDownloadsByStatusAndUser(service.Page, user.ID, aria2.Downloading, aria2.Paused, aria2.Ready) return serializer.BuildDownloadingResponse(downloads) } diff --git a/service/explorer/directory.go b/service/explorer/directory.go index f603bea..f981536 100644 --- a/service/explorer/directory.go +++ b/service/explorer/directory.go @@ -28,7 +28,7 @@ func (service *DirectoryService) ListDirectory(c *gin.Context) serializer.Respon // 获取子项目 objects, err := fs.List(ctx, service.Path, nil) if err != nil { - return serializer.Err(serializer.CodeCreateFolderFailed, err.Error(), err) + return serializer.Err(serializer.CodeNotSet, err.Error(), err) } var parentID uint