|
@@ -3,12 +3,15 @@ package fwhQueue
|
|
|
import (
|
|
import (
|
|
|
"context"
|
|
"context"
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"fwh-toolbox-server/domain"
|
|
"fwh-toolbox-server/domain"
|
|
|
"fwh-toolbox-server/dto"
|
|
"fwh-toolbox-server/dto"
|
|
|
"fwh-toolbox-server/repositories"
|
|
"fwh-toolbox-server/repositories"
|
|
|
|
|
+ "time"
|
|
|
|
|
|
|
|
"github.com/hibiken/asynq"
|
|
"github.com/hibiken/asynq"
|
|
|
|
|
+ "go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
@@ -29,6 +32,12 @@ func handleSiteInfoUpdate(ctx context.Context, task *asynq.Task) error {
|
|
|
|
|
|
|
|
// 执行任务
|
|
// 执行任务
|
|
|
total, err := executeSiteInfoUpdate(ctx, payload)
|
|
total, err := executeSiteInfoUpdate(ctx, payload)
|
|
|
|
|
+ if errors.Is(err, context.Canceled) {
|
|
|
|
|
+ // CancelSiteInfoUpdate 发出的取消(asynq 超时为 DeadlineExceeded,按失败处理)
|
|
|
|
|
+ zap.L().Info("[SiteInfoUpdate] 任务已取消", zap.String("id", taskId))
|
|
|
|
|
+ repositories.UpdateSiteInfoUpdateResult(payload.Id, domain.ProgressCancelled, 0)
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
zap.L().Error("[SiteInfoUpdate] 任务执行失败", zap.String("id", taskId), zap.String("err", err.Error()))
|
|
zap.L().Error("[SiteInfoUpdate] 任务执行失败", zap.String("id", taskId), zap.String("err", err.Error()))
|
|
|
repositories.UpdateSiteInfoUpdateResult(payload.Id, domain.ProgressError, 0)
|
|
repositories.UpdateSiteInfoUpdateResult(payload.Id, domain.ProgressError, 0)
|
|
@@ -48,6 +57,10 @@ func executeSiteInfoUpdate(ctx context.Context, payload domain.TaskSiteInfoUpdat
|
|
|
var realTotal uint64 = 0
|
|
var realTotal uint64 = 0
|
|
|
|
|
|
|
|
for {
|
|
for {
|
|
|
|
|
+ // 每页检查一次:取消(CancelSiteInfoUpdate)或超时(SiteInfoUpdateTimeout)时停止
|
|
|
|
|
+ if err := ctx.Err(); err != nil {
|
|
|
|
|
+ return 0, err
|
|
|
|
|
+ }
|
|
|
resp, err := repositories.QuerySiteInfoList(dto.SiteInfoPara{
|
|
resp, err := repositories.QuerySiteInfoList(dto.SiteInfoPara{
|
|
|
PageSize: pageSize,
|
|
PageSize: pageSize,
|
|
|
PageNum: pageNum,
|
|
PageNum: pageNum,
|
|
@@ -72,3 +85,33 @@ func executeSiteInfoUpdate(ctx context.Context, payload domain.TaskSiteInfoUpdat
|
|
|
|
|
|
|
|
return realTotal, nil
|
|
return realTotal, nil
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+// SiteInfoUpdateTimeout 站点信息同步任务的执行超时
|
|
|
|
|
+const SiteInfoUpdateTimeout = 4 * time.Hour
|
|
|
|
|
+
|
|
|
|
|
+// CancelSiteInfoUpdate 取消一次站点信息同步:
|
|
|
|
|
+// 正在执行的通知 worker 停止(worker 停下后把记录标记为 cancelled);还在排队或已不在队列中的直接删除并标记。
|
|
|
|
|
+// 可在主服务或只入队的独立进程(fwh-agent-mcp)中调用,需要 inspector 已初始化。
|
|
|
|
|
+func CancelSiteInfoUpdate(id primitive.ObjectID) error {
|
|
|
|
|
+ if inspector == nil {
|
|
|
|
|
+ return fmt.Errorf("任务队列未初始化")
|
|
|
|
|
+ }
|
|
|
|
|
+ taskId := newTaskID(TaskTypeSiteInfoUpdate, id.Hex())
|
|
|
|
|
+ info, err := inspector.GetTaskInfo(QueueMain, taskId)
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case errors.Is(err, asynq.ErrTaskNotFound) || errors.Is(err, asynq.ErrQueueNotFound):
|
|
|
|
|
+ // 队列中已没有这个任务(如主服务重启时丢失):只更新记录
|
|
|
|
|
+ case err != nil:
|
|
|
|
|
+ return err
|
|
|
|
|
+ case info.State == asynq.TaskStateActive:
|
|
|
|
|
+ return inspector.CancelProcessing(taskId)
|
|
|
|
|
+ case info.State == asynq.TaskStateCompleted || info.State == asynq.TaskStateArchived:
|
|
|
|
|
+ // 任务已经结束,worker 已写入最终状态,不覆盖
|
|
|
|
|
+ return nil
|
|
|
|
|
+ case info.State == asynq.TaskStatePending || info.State == asynq.TaskStateScheduled || info.State == asynq.TaskStateRetry:
|
|
|
|
|
+ if err := inspector.DeleteTask(QueueMain, taskId); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return repositories.UpdateSiteInfoUpdateResult(id, domain.ProgressCancelled, 0)
|
|
|
|
|
+}
|