diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go index a11d3c1f8e9..cce1e861304 100644 --- a/drivers/189pc/driver.go +++ b/drivers/189pc/driver.go @@ -4,9 +4,11 @@ import ( "context" "net/http" "strings" + "time" "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/pkg/utils" "github.com/go-resty/resty/v2" @@ -135,13 +137,14 @@ func (y *Cloud189PC) Link(ctx context.Context, file model.Obj, args model.LinkAr return like, nil } -func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { +func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) { fullUrl := API_URL if y.isFamily() { fullUrl += "/family/file" } fullUrl += "/createFolder.action" + var newFolder Cloud189Folder _, err := y.post(fullUrl, func(req *resty.Request) { req.SetContext(ctx) req.SetQueryParams(map[string]string{ @@ -158,11 +161,15 @@ func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName s "parentFolderId": parentDir.GetID(), }) } - }, nil) - return err + }, &newFolder) + if err != nil { + return nil, err + } + return &newFolder, nil } -func (y *Cloud189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error { +func (y *Cloud189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) { + var resp CreateBatchTaskResp _, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) { req.SetContext(ctx) req.SetFormData(map[string]string{ @@ -182,11 +189,17 @@ func (y *Cloud189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error { "familyId": y.FamilyID, }) } - }, nil) - return err + }, &resp) + if err != nil { + return nil, err + } + if err = y.WaitBatchTask("MOVE", resp.TaskID, time.Millisecond*400); err != nil { + return nil, err + } + return srcObj, nil } -func (y *Cloud189PC) Rename(ctx context.Context, srcObj model.Obj, newName string) error { +func (y *Cloud189PC) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) { queryParam := make(map[string]string) fullUrl := API_URL method := http.MethodPost @@ -195,23 +208,34 @@ func (y *Cloud189PC) Rename(ctx context.Context, srcObj model.Obj, newName strin method = http.MethodGet queryParam["familyId"] = y.FamilyID } - if srcObj.IsDir() { - fullUrl += "/renameFolder.action" - queryParam["folderId"] = srcObj.GetID() - queryParam["destFolderName"] = newName - } else { + + var newObj model.Obj + switch f := srcObj.(type) { + case *Cloud189File: fullUrl += "/renameFile.action" queryParam["fileId"] = srcObj.GetID() queryParam["destFileName"] = newName + newObj = &Cloud189File{Icon: f.Icon} // 复用预览 + case *Cloud189Folder: + fullUrl += "/renameFolder.action" + queryParam["folderId"] = srcObj.GetID() + queryParam["destFolderName"] = newName + newObj = &Cloud189Folder{} + default: + return nil, errs.NotSupport } + _, err := y.request(fullUrl, method, func(req *resty.Request) { - req.SetContext(ctx) - req.SetQueryParams(queryParam) - }, nil, nil) - return err + req.SetContext(ctx).SetQueryParams(queryParam) + }, nil, newObj) + if err != nil { + return nil, err + } + return newObj, nil } func (y *Cloud189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { + var resp CreateBatchTaskResp _, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) { req.SetContext(ctx) req.SetFormData(map[string]string{ @@ -232,11 +256,15 @@ func (y *Cloud189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { "familyId": y.FamilyID, }) } - }, nil) - return err + }, &resp) + if err != nil { + return err + } + return y.WaitBatchTask("COPY", resp.TaskID, time.Second) } func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error { + var resp CreateBatchTaskResp _, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) { req.SetContext(ctx) req.SetFormData(map[string]string{ @@ -256,19 +284,26 @@ func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error { "familyId": y.FamilyID, }) } - }, nil) - return err + }, &resp) + if err != nil { + return err + } + // 批量任务数量限制,过快会导致无法删除 + return y.WaitBatchTask("DELETE", resp.TaskID, time.Millisecond*200) } -func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { +func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) { switch y.UploadMethod { - case "stream": - return y.CommonUpload(ctx, dstDir, stream, up) case "old": return y.OldUpload(ctx, dstDir, stream, up) case "rapid": return y.FastUpload(ctx, dstDir, stream, up) + case "stream": + if stream.GetSize() == 0 { + return y.FastUpload(ctx, dstDir, stream, up) + } + fallthrough default: - return y.CommonUpload(ctx, dstDir, stream, up) + return y.StreamUpload(ctx, dstDir, stream, up) } } diff --git a/drivers/189pc/help.go b/drivers/189pc/help.go index 6cc6facc9f9..c7b2e12a6ca 100644 --- a/drivers/189pc/help.go +++ b/drivers/189pc/help.go @@ -10,6 +10,7 @@ import ( "crypto/x509" "encoding/hex" "encoding/pem" + "encoding/xml" "fmt" "math" "net/http" @@ -83,6 +84,55 @@ func MustParseTime(str string) *time.Time { return &lastOpTime } +type Time time.Time + +func (t *Time) UnmarshalJSON(b []byte) error { return t.Unmarshal(b) } +func (t *Time) UnmarshalXML(e *xml.Decoder, ee xml.StartElement) error { + b, err := e.Token() + if err != nil { + return err + } + if b, ok := b.(xml.CharData); ok { + if err = t.Unmarshal(b); err != nil { + return err + } + } + return e.Skip() +} +func (t *Time) Unmarshal(b []byte) error { + bs := strings.Trim(string(b), "\"") + var v time.Time + var err error + for _, f := range []string{"2006-01-02 15:04:05 -07", "Jan 2, 2006 15:04:05 PM -07"} { + v, err = time.ParseInLocation(f, bs+" +08", time.Local) + if err == nil { + break + } + } + *t = Time(v) + return err +} + +type String string + +func (t *String) UnmarshalJSON(b []byte) error { return t.Unmarshal(b) } +func (t *String) UnmarshalXML(e *xml.Decoder, ee xml.StartElement) error { + b, err := e.Token() + if err != nil { + return err + } + if b, ok := b.(xml.CharData); ok { + if err = t.Unmarshal(b); err != nil { + return err + } + } + return e.Skip() +} +func (s *String) Unmarshal(b []byte) error { + *s = String(bytes.Trim(b, "\"")) + return nil +} + func toFamilyOrderBy(o string) string { switch o { case "filename": @@ -122,10 +172,6 @@ func MustString(str string, err error) string { return str } -func MustToBytes(b []byte, err error) []byte { - return b -} - func BoolToNumber(b bool) int { if b { return 1 diff --git a/drivers/189pc/types.go b/drivers/189pc/types.go index 5459c0d475d..a3ce014509c 100644 --- a/drivers/189pc/types.go +++ b/drivers/189pc/types.go @@ -151,8 +151,13 @@ type FamilyInfoResp struct { /*文件部分*/ // 文件 type Cloud189File struct { - CreateDate string `json:"createDate"` - FileCata int64 `json:"fileCata"` + ID String `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + Md5 string `json:"md5"` + + LastOpTime Time `json:"lastOpTime"` + CreateDate Time `json:"createDate"` Icon struct { //iconOption 5 SmallUrl string `json:"smallUrl"` @@ -162,62 +167,44 @@ type Cloud189File struct { Max600 string `json:"max600"` MediumURL string `json:"mediumUrl"` } `json:"icon"` - ID int64 `json:"id"` - LastOpTime string `json:"lastOpTime"` - Md5 string `json:"md5"` - MediaType int `json:"mediaType"` - Name string `json:"name"` - Orientation int64 `json:"orientation"` - Rev string `json:"rev"` - Size int64 `json:"size"` - StarLabel int64 `json:"starLabel"` - - parseTime *time.Time -} -func (c *Cloud189File) GetSize() int64 { return c.Size } -func (c *Cloud189File) GetName() string { return c.Name } -func (c *Cloud189File) ModTime() time.Time { - if c.parseTime == nil { - c.parseTime = MustParseTime(c.LastOpTime) - } - return *c.parseTime + // Orientation int64 `json:"orientation"` + // FileCata int64 `json:"fileCata"` + // MediaType int `json:"mediaType"` + // Rev string `json:"rev"` + // StarLabel int64 `json:"starLabel"` } -func (c *Cloud189File) IsDir() bool { return false } -func (c *Cloud189File) GetID() string { return fmt.Sprint(c.ID) } -func (c *Cloud189File) GetPath() string { return "" } -func (c *Cloud189File) Thumb() string { return c.Icon.SmallUrl } + +func (c *Cloud189File) GetSize() int64 { return c.Size } +func (c *Cloud189File) GetName() string { return c.Name } +func (c *Cloud189File) ModTime() time.Time { return time.Time(c.LastOpTime) } +func (c *Cloud189File) IsDir() bool { return false } +func (c *Cloud189File) GetID() string { return string(c.ID) } +func (c *Cloud189File) GetPath() string { return "" } +func (c *Cloud189File) Thumb() string { return c.Icon.SmallUrl } // 文件夹 type Cloud189Folder struct { - ID int64 `json:"id"` + ID String `json:"id"` ParentID int64 `json:"parentId"` Name string `json:"name"` - FileCata int64 `json:"fileCata"` - FileCount int64 `json:"fileCount"` - - LastOpTime string `json:"lastOpTime"` - CreateDate string `json:"createDate"` + LastOpTime Time `json:"lastOpTime"` + CreateDate Time `json:"createDate"` - FileListSize int64 `json:"fileListSize"` - Rev string `json:"rev"` - StarLabel int64 `json:"starLabel"` - - parseTime *time.Time + // FileListSize int64 `json:"fileListSize"` + // FileCount int64 `json:"fileCount"` + // FileCata int64 `json:"fileCata"` + // Rev string `json:"rev"` + // StarLabel int64 `json:"starLabel"` } -func (c *Cloud189Folder) GetSize() int64 { return 0 } -func (c *Cloud189Folder) GetName() string { return c.Name } -func (c *Cloud189Folder) ModTime() time.Time { - if c.parseTime == nil { - c.parseTime = MustParseTime(c.LastOpTime) - } - return *c.parseTime -} -func (c *Cloud189Folder) IsDir() bool { return true } -func (c *Cloud189Folder) GetID() string { return fmt.Sprint(c.ID) } -func (c *Cloud189Folder) GetPath() string { return "" } +func (c *Cloud189Folder) GetSize() int64 { return 0 } +func (c *Cloud189Folder) GetName() string { return c.Name } +func (c *Cloud189Folder) ModTime() time.Time { return time.Time(c.LastOpTime) } +func (c *Cloud189Folder) IsDir() bool { return true } +func (c *Cloud189Folder) GetID() string { return string(c.ID) } +func (c *Cloud189Folder) GetPath() string { return "" } type Cloud189FilesResp struct { //ResCode int `json:"res_code"` @@ -284,15 +271,60 @@ func (r *GetUploadFileStatusResp) GetSize() int64 { return r.DataSize + r.Size } -type CommitUploadFileResp struct { +type CommitMultiUploadFileResp struct { + File struct { + UserFileID String `json:"userFileId"` + FileName string `json:"fileName"` + FileSize int64 `json:"fileSize"` + FileMd5 string `json:"fileMd5"` + CreateDate Time `json:"createDate"` + } `json:"file"` +} + +func (f *CommitMultiUploadFileResp) toFile() *Cloud189File { + return &Cloud189File{ + ID: f.File.UserFileID, + Name: f.File.FileName, + Size: f.File.FileSize, + Md5: f.File.FileMd5, + LastOpTime: f.File.CreateDate, + CreateDate: f.File.CreateDate, + } +} + +type OldCommitUploadFileResp struct { XMLName xml.Name `xml:"file"` - Id string `xml:"id"` + ID String `xml:"id"` Name string `xml:"name"` - Size string `xml:"size"` + Size int64 `xml:"size"` Md5 string `xml:"md5"` - CreateDate string `xml:"createDate"` - Rev string `xml:"rev"` - UserId string `xml:"userId"` + CreateDate Time `xml:"createDate"` +} + +func (f *OldCommitUploadFileResp) toFile() *Cloud189File { + return &Cloud189File{ + ID: f.ID, + Name: f.Name, + Size: f.Size, + Md5: f.Md5, + CreateDate: f.CreateDate, + LastOpTime: f.CreateDate, + } +} + +type CreateBatchTaskResp struct { + TaskID string `json:"taskId"` +} + +type BatchTaskStateResp struct { + FailedCount int `json:"failedCount"` + Process int `json:"process"` + SkipCount int `json:"skipCount"` + SubTaskCount int `json:"subTaskCount"` + SuccessedCount int `json:"successedCount"` + SuccessedFileIDList []int64 `json:"successedFileIdList"` + TaskID string `json:"taskId"` + TaskStatus int `json:"taskStatus"` //1 初始化 2 存在冲突 3 执行中,4 完成 } /* query 加密参数*/ diff --git a/drivers/189pc/utils.go b/drivers/189pc/utils.go index 0e01a298a80..5de99e2c031 100644 --- a/drivers/189pc/utils.go +++ b/drivers/189pc/utils.go @@ -268,7 +268,7 @@ func (y *Cloud189PC) login() (err error) { "validateCode": y.VCode, "captchaToken": param.CaptchaToken, "returnUrl": RETURN_URL, - "mailSuffix": "@189.cn", + // "mailSuffix": "@189.cn", "dynamicCheck": "FALSE", "clientType": CLIENT_TYPE, "cb_SaveName": "1", @@ -434,7 +434,8 @@ func (y *Cloud189PC) refreshSession() (err error) { } // 普通上传 -func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) { +// 无法上传大小为0的文件 +func (y *Cloud189PC) StreamUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) { var DEFAULT = partSize(file.GetSize()) var count = int(math.Ceil(float64(file.GetSize()) / float64(DEFAULT))) @@ -457,11 +458,11 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo // 初始化上传 var initMultiUpload InitMultiUploadResp - _, err = y.request(fullUrl+"/initMultiUpload", http.MethodGet, func(req *resty.Request) { + _, err := y.request(fullUrl+"/initMultiUpload", http.MethodGet, func(req *resty.Request) { req.SetContext(ctx) }, params, &initMultiUpload) if err != nil { - return err + return nil, err } fileMd5 := md5.New() @@ -470,7 +471,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo byteData := bytes.NewBuffer(make([]byte, DEFAULT)) for i := 1; i <= count; i++ { if utils.IsCanceled(ctx) { - return ctx.Err() + return nil, ctx.Err() } // 读取块 @@ -478,7 +479,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo silceMd5.Reset() _, err := io.CopyN(io.MultiWriter(fileMd5, silceMd5, byteData), file, DEFAULT) if err != io.EOF && err != io.ErrUnexpectedEOF && err != nil { - return err + return nil, err } // 计算块md5并进行hex和base64编码 @@ -496,7 +497,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo "uploadFileId": initMultiUpload.Data.UploadFileID, }, &uploadUrl) if err != nil { - return err + return nil, err } // 开始上传 @@ -511,7 +512,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo retry.Delay(time.Second), retry.MaxDelay(5*time.Second)) if err != nil { - return err + return nil, err } up(int(i * 100 / count)) } @@ -523,6 +524,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo } // 提交上传 + var resp CommitMultiUploadFileResp _, err = y.request(fullUrl+"/commitMultiUploadFile", http.MethodGet, func(req *resty.Request) { req.SetContext(ctx) @@ -533,16 +535,19 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo "lazyCheck": "1", "isLog": "0", "opertype": "3", - }, nil) - return err + }, &resp) + if err != nil { + return nil, err + } + return resp.toFile(), nil } // 快传 -func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) { +func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) { // 需要获取完整文件md5,必须支持 io.Seek tempFile, err := utils.CreateTempFile(file.GetReadCloser()) if err != nil { - return err + return nil, err } defer func() { _ = tempFile.Close() @@ -559,19 +564,19 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode silceMd5Base64s := make([]string, 0, count) for i := 1; i <= count; i++ { if utils.IsCanceled(ctx) { - return ctx.Err() + return nil, ctx.Err() } silceMd5.Reset() if _, err := io.CopyN(io.MultiWriter(fileMd5, silceMd5), tempFile, DEFAULT); err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { - return err + return nil, err } md5Byte := silceMd5.Sum(nil) silceMd5Hexs = append(silceMd5Hexs, strings.ToUpper(hex.EncodeToString(md5Byte))) silceMd5Base64s = append(silceMd5Base64s, fmt.Sprint(i, "-", base64.StdEncoding.EncodeToString(md5Byte))) } if _, err = tempFile.Seek(0, io.SeekStart); err != nil { - return err + return nil, err } fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil))) @@ -604,7 +609,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode req.SetContext(ctx) }, params, &uploadInfo) if err != nil { - return err + return nil, err } // 网盘中不存在该文件,开始上传 @@ -618,18 +623,18 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode "partInfo": strings.Join(silceMd5Base64s, ","), }, &uploadUrls) if err != nil { - return err + return nil, err } buf := make([]byte, DEFAULT) for i := 1; i <= count; i++ { if utils.IsCanceled(ctx) { - return ctx.Err() + return nil, ctx.Err() } n, err := io.ReadFull(tempFile, buf) if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { - return err + return nil, err } uploadData := uploadUrls.UploadUrls[fmt.Sprint("partNumber_", i)] err = retry.Do(func() error { @@ -641,7 +646,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode retry.Delay(time.Second), retry.MaxDelay(5*time.Second)) if err != nil { - return err + return nil, err } up(int(i * 100 / count)) @@ -649,6 +654,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode } // 提交 + var resp CommitMultiUploadFileResp _, err = y.request(fullUrl+"/commitMultiUploadFile", http.MethodGet, func(req *resty.Request) { req.SetContext(ctx) @@ -656,15 +662,19 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode "uploadFileId": uploadInfo.Data.UploadFileID, "isLog": "0", "opertype": "3", - }, nil) - return err + }, &resp) + if err != nil { + return nil, err + } + return resp.toFile(), nil } -func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) { +// 旧版本上传,家庭云不支持覆盖 +func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) { // 需要获取完整文件md5,必须支持 io.Seek tempFile, err := utils.CreateTempFile(file.GetReadCloser()) if err != nil { - return err + return nil, err } defer func() { _ = tempFile.Close() @@ -674,10 +684,10 @@ func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model // 计算md5 fileMd5 := md5.New() if _, err := io.Copy(fileMd5, tempFile); err != nil { - return err + return nil, err } if _, err = tempFile.Seek(0, io.SeekStart); err != nil { - return err + return nil, err } fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil))) @@ -718,14 +728,14 @@ func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model }, &uploadInfo) if err != nil { - return err + return nil, err } // 网盘中不存在该文件,开始上传 status := GetUploadFileStatusResp{CreateUploadFileResp: uploadInfo} for status.Size < file.GetSize() && status.FileDataExists != 1 { if utils.IsCanceled(ctx) { - return ctx.Err() + return nil, ctx.Err() } header := map[string]string{ @@ -742,7 +752,7 @@ func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model _, err := y.put(ctx, status.FileUploadUrl, header, true, io.NopCloser(tempFile)) if err, ok := err.(*RespErr); ok && err.Code != "InputStreamReadError" { - return err + return nil, err } // 获取断点状态 @@ -760,17 +770,17 @@ func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model } }, &status) if err != nil { - return err + return nil, err } if _, err := tempFile.Seek(status.GetSize(), io.SeekStart); err != nil { - return err + return nil, err } up(int(status.Size / file.GetSize())) } // 提交 - var resp CommitUploadFileResp + var resp OldCommitUploadFileResp _, err = y.post(status.FileCommitUrl, func(req *resty.Request) { req.SetContext(ctx) if y.isFamily() { @@ -788,7 +798,10 @@ func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model }) } }, &resp) - return err + if err != nil { + return nil, err + } + return resp.toFile(), nil } func (y *Cloud189PC) isFamily() bool { @@ -829,3 +842,33 @@ func (y *Cloud189PC) getFamilyID() (string, error) { } return fmt.Sprint(infos[0].FamilyID), nil } + +func (y *Cloud189PC) CheckBatchTask(aType string, taskID string) (*BatchTaskStateResp, error) { + var resp BatchTaskStateResp + _, err := y.post(API_URL+"/batch/checkBatchTask.action", func(req *resty.Request) { + req.SetFormData(map[string]string{ + "type": aType, + "taskId": taskID, + }) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (y *Cloud189PC) WaitBatchTask(aType string, taskID string, t time.Duration) error { + for { + state, err := y.CheckBatchTask(aType, taskID) + if err != nil { + return err + } + switch state.TaskStatus { + case 2: + return errors.New("there is a conflict with the target object") + case 4: + return nil + } + time.Sleep(t) + } +}