| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- package fwhQueue
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "fwh-toolbox-server/domain"
- "fwh-toolbox-server/dto"
- "fwh-toolbox-server/repositories"
- "time"
- "github.com/hibiken/asynq"
- "go.mongodb.org/mongo-driver/bson/primitive"
- "go.uber.org/zap"
- )
- // handleSiteInfoUpdate 是站点信息更新任务的 Asynq handler
- func handleSiteInfoUpdate(ctx context.Context, task *asynq.Task) error {
- var payload domain.TaskSiteInfoUpdate
- if err := json.Unmarshal(task.Payload(), &payload); err != nil {
- return fmt.Errorf("unmarshal SiteInfoUpdate payload: %w", err)
- }
- taskId := payload.Id.Hex()
- // 更新运行状态
- if err := repositories.UpdateSiteInfoUpdateResult(payload.Id, domain.ProgressRunning, 0); err != nil {
- zap.L().Error("[SiteInfoUpdate] 更新状态失败(Running)", zap.String("id", taskId))
- return err
- }
- // 执行任务
- 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 {
- zap.L().Error("[SiteInfoUpdate] 任务执行失败", zap.String("id", taskId), zap.String("err", err.Error()))
- repositories.UpdateSiteInfoUpdateResult(payload.Id, domain.ProgressError, 0)
- return nil // 业务错误不重试
- }
- zap.L().Info("[SiteInfoUpdate] 任务执行成功", zap.String("id", taskId), zap.Uint64("total", total))
- repositories.UpdateSiteInfoUpdateResult(payload.Id, domain.ProgressComplete, total)
- return nil
- }
- // executeSiteInfoUpdate 执行站点信息更新
- func executeSiteInfoUpdate(ctx context.Context, payload domain.TaskSiteInfoUpdate) (uint64, error) {
- var pageSize uint = 50
- var pageNum uint = 1
- var total uint64 = 0
- var realTotal uint64 = 0
- for {
- // 每页检查一次:取消(CancelSiteInfoUpdate)或超时(SiteInfoUpdateTimeout)时停止
- if err := ctx.Err(); err != nil {
- return 0, err
- }
- resp, err := repositories.QuerySiteInfoList(dto.SiteInfoPara{
- PageSize: pageSize,
- PageNum: pageNum,
- })
- if err != nil {
- return 0, fmt.Errorf("查询站点信息失败(page=%d): %w", pageNum, err)
- }
- for _, info := range *resp.Result {
- if err := repositories.InsertSiteInfoData(*domain.FwhSiteInfoToSiteInfoDomain(&info)); err != nil {
- return 0, fmt.Errorf("保存站点信息失败(page=%d): %w", pageNum, err)
- }
- realTotal++
- }
- pageNum++
- total = resp.Total
- if (int64(pageNum)-1)*int64(pageSize) >= int64(total) {
- break
- }
- }
- 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)
- }
|