You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:基于PeriodicTask实现user_club延迟删除及撤销功能失败

问题描述

我需要实现以下业务逻辑:

  • 用户点击删除user_club时,弹出允许撤销该删除请求的提示;
  • 若用户在5秒内未进行任何操作,则执行删除操作;
  • 若用户执行撤销操作,则取消数据库中的删除请求。

我尝试用PeriodicTask实现该功能,但未成功。以下是我的代码,麻烦帮忙排查问题,谢谢。

代码实现

models.py

class UserClub(ItemBase):
    user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_club')
    club = models.ForeignKey(Club, on_delete=models.CASCADE)
    roles = models.ManyToManyField(Role)
    membership_id = models.CharField(max_length=255, blank=True, null=True)
    membership_expiry_date = models.DateField(max_length=255, blank=True, null=True)
    subscription = models.ForeignKey(to=Subscription, on_delete=models.SET_NULL, blank=True, null=True, related_name='user_club_subscription')
    default = models.BooleanField(default=False)
    reputation_point = models.PositiveIntegerField(default=0)

    class Meta:
        db_table = "sport_user_club"
        verbose_name = 'User Club'
        verbose_name_plural = 'User Club'

views.py

def destroy(self, request, *args, **kwargs):
        user_club = self.get_object()
        task = task_schedule_del_user_club.apply_async(args=[user_club.uuid], countdown=10)
        self.response_format['data'] = {
            "task_id": task.id
        }
        self.response_format['message'] = "Club will be deleted after 5 seconds"
        return JsonResponse(data=self.response_format, status=status.HTTP_200_OK)

    @swagger_auto_schema(methods=['post'], request_body=UndoDeleteClubProcessSerializer)
    @action(methods=['post'], detail=False, url_path="undo-request", url_name='undo-request')
    def undo_request_remove_club(self, request, *args, **kwargs):
        serializer = UndoDeleteClubProcessSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer_data = serializer.validated_data
        if serializer_data['is_undo_request']:
            try:
                task_schedule_del_user_club.AsyncResult(serializer_data['task_uuid'])
                self.response_format['message'] = "Undo request successfully"
                return JsonResponse(self.response_format, status=status.HTTP_200_OK)
            except Exception as e:
                logger.error(e)
                raise BaseError(_("Cancel an unsuccessful idea or proposal. Please try again!"))

tasks.py

@app.task
def task_schedule_del_user_club(user_club_uuid):
    try:
        user_club = UserClub.objects.get(uuid=user_club_uuid)
        user_club.delete()
    except UserClub.DoesNotExist:
        logger.warning(f"UserClub with id {user_club_uuid} does not exist.")
    except Exception as ex:
        logger.error(f"An error occurred while deleting UserClub: {ex}")
问题排查与修复方案

你的代码存在3个核心问题,直接导致功能失效:

  1. 撤销逻辑未实际取消任务:撤销方法中仅获取了任务的AsyncResult,但未调用revoke()方法终止任务,导致任务仍会执行删除操作;
  2. 倒计时与提示信息不一致:destroy方法设置的是10秒倒计时,但提示信息显示5秒,前后逻辑矛盾;
  3. 无任务执行兜底校验:若用户在任务即将执行时点击撤销,任务可能已开始运行,此时仅靠revoke()无法完全终止,会导致数据不一致。

修复后的代码调整

1. 修正撤销方法(views.py)

@swagger_auto_schema(methods=['post'], request_body=UndoDeleteClubProcessSerializer)
@action(methods=['post'], detail=False, url_path="undo-request", url_name='undo-request')
def undo_request_remove_club(self, request, *args, **kwargs):
    serializer = UndoDeleteClubProcessSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer_data = serializer.validated_data
    if serializer_data['is_undo_request']:
        try:
            task_result = task_schedule_del_user_club.AsyncResult(serializer_data['task_uuid'])
            # 取消队列中的任务,terminate=True强制终止已启动的任务
            task_result.revoke(terminate=True)
            self.response_format['message'] = "撤销请求成功"
            return JsonResponse(self.response_format, status=status.HTTP_200_OK)
        except Exception as e:
            logger.error(e)
            raise BaseError(_("撤销失败,请重试!"))

2. 统一倒计时与提示信息(views.py)

def destroy(self, request, *args, **kwargs):
    user_club = self.get_object()
    # 改为5秒倒计时,与提示信息一致
    task = task_schedule_del_user_club.apply_async(args=[user_club.uuid], countdown=5)
    self.response_format['data'] = {
        "task_id": task.id
    }
    self.response_format['message'] = "俱乐部将在5秒后删除,可点击撤销取消操作"
    return JsonResponse(data=self.response_format, status=status.HTTP_200_OK)

3. 增加数据库状态兜底校验(可选但推荐)

给UserClub模型增加一个待删除标记,避免任务已启动时无法终止的情况:

models.py 修改
class UserClub(ItemBase):
    # ... 原有字段
    pending_delete = models.BooleanField(default=False)

    class Meta:
        db_table = "sport_user_club"
        verbose_name = '用户俱乐部'
        verbose_name_plural = '用户俱乐部'
views.py 的destroy方法更新
def destroy(self, request, *args, **kwargs):
    user_club = self.get_object()
    user_club.pending_delete = True
    user_club.save()
    task = task_schedule_del_user_club.apply_async(args=[user_club.uuid], countdown=5)
    self.response_format['data'] = {
        "task_id": task.id
    }
    self.response_format['message'] = "俱乐部将在5秒后删除,可点击撤销取消操作"
    return JsonResponse(data=self.response_format, status=status.HTTP_200_OK)
tasks.py 更新
@app.task
def task_schedule_del_user_club(user_club_uuid):
    try:
        user_club = UserClub.objects.get(uuid=user_club_uuid)
        # 校验是否已撤销删除
        if user_club.pending_delete:
            user_club.delete()
        else:
            logger.info(f"UserClub {user_club_uuid} 删除已撤销")
    except UserClub.DoesNotExist:
        logger.warning(f"UserClub with id {user_club_uuid} 不存在。")
    except Exception as ex:
        logger.error(f"删除UserClub时出错: {ex}")
撤销方法新增状态更新
try:
    task_result = task_schedule_del_user_club.AsyncResult(serializer_data['task_uuid'])
    task_result.revoke(terminate=True)
    # 更新数据库标记,确保任务即使启动也不会执行删除
    UserClub.objects.filter(uuid=serializer_data.get('club_uuid')).update(pending_delete=False)
    self.response_format['message'] = "撤销请求成功"
    return JsonResponse(self.response_format, status=status.HTTP_200_OK)

额外注意事项

  • 确保Celery服务正常运行,任务队列配置正确,否则revoke()无法生效;
  • terminate=True依赖Celery的工作进程支持,若使用Windows系统可能无法正常终止任务,建议用数据库状态标记的方式兜底。

内容的提问来源于stack exchange,提问作者Hưng Yi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 20:12:35