From d1201d6923415c987a7ca60214b8401073d177c7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Apr 2026 09:36:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor(material):=20=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=98=BE=E5=BC=8F=E5=A4=B1=E6=95=88=E7=BC=93=E5=AD=98=EF=BC=8C?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=20signals=EF=BC=9BTTL=2030min=20=E2=86=92=20?= =?UTF-8?q?5min?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 signals.py,改在 approve / reject / perform_destroy / import_excel 四个会影响 已审核材料集合的入口显式调用 invalidate_category_tree_cache(),调用栈可见、易追踪 - reject 与 perform_destroy 仅当原状态为 approved 时才失效,避免无效缓存抖动 - TTL 由 30 分钟降为 5 分钟,作为兜底防止遗漏路径 Co-Authored-By: Claude Opus 4.7 --- backend/apps/material/apps.py | 3 --- backend/apps/material/signals.py | 21 --------------------- backend/apps/material/views.py | 18 ++++++++++++++++-- 3 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 backend/apps/material/signals.py diff --git a/backend/apps/material/apps.py b/backend/apps/material/apps.py index 40305c1..3eca49d 100644 --- a/backend/apps/material/apps.py +++ b/backend/apps/material/apps.py @@ -4,6 +4,3 @@ from django.apps import AppConfig class MaterialConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'apps.material' - - def ready(self): - from . import signals # noqa: F401 diff --git a/backend/apps/material/signals.py b/backend/apps/material/signals.py deleted file mode 100644 index 6ce182e..0000000 --- a/backend/apps/material/signals.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.core.cache import cache -from django.db.models.signals import post_delete, post_save -from django.dispatch import receiver - -from .models import Material - -CATEGORY_TREE_CACHE_KEY = 'material:category_tree:approved' - - -def invalidate_category_tree_cache(): - cache.delete(CATEGORY_TREE_CACHE_KEY) - - -@receiver(post_save, sender=Material) -def _on_material_saved(sender, instance, **kwargs): - invalidate_category_tree_cache() - - -@receiver(post_delete, sender=Material) -def _on_material_deleted(sender, instance, **kwargs): - invalidate_category_tree_cache() diff --git a/backend/apps/material/views.py b/backend/apps/material/views.py index 17c2fd6..5a244b2 100644 --- a/backend/apps/material/views.py +++ b/backend/apps/material/views.py @@ -19,10 +19,16 @@ from django.core.cache import cache from .models import Material, MaterialCategory, MaterialSubcategory from .serializers import MaterialSerializer, MaterialListSerializer, MaterialCategorySerializer, MaterialSubcategorySerializer from .importers import import_materials_plan_excel -from .signals import CATEGORY_TREE_CACHE_KEY from apps.factory.models import COOPERATION_MODE_CHOICES +CATEGORY_TREE_CACHE_KEY = 'material:category_tree:approved' + + +def invalidate_category_tree_cache(): + cache.delete(CATEGORY_TREE_CACHE_KEY) + + def _join_choice_values(values, choices): if not values: return "" @@ -296,7 +302,10 @@ class MaterialViewSet(ModelViewSet): # 普通用户只能删除创建中的材料 if self.request.user.role != 'admin' and instance.status != 'draft': raise PermissionDenied("只有创建中的材料可以删除") + was_approved = instance.status == 'approved' instance.delete() + if was_approved: + invalidate_category_tree_cache() @action(detail=True, methods=['post']) def submit(self, request, pk=None): @@ -344,6 +353,7 @@ class MaterialViewSet(ModelViewSet): material.status = 'approved' material.save() + invalidate_category_tree_cache() return Response({"status": "审核通过"}) @action(detail=True, methods=['post']) @@ -365,8 +375,11 @@ class MaterialViewSet(ModelViewSet): status=status.HTTP_400_BAD_REQUEST ) + was_approved = material.status == 'approved' material.status = 'draft' material.save() + if was_approved: + invalidate_category_tree_cache() return Response({"status": "审核拒绝"}) @action(detail=False, methods=['get']) @@ -484,6 +497,7 @@ class MaterialViewSet(ModelViewSet): except Exception as exc: return Response({"detail": f"导入失败: {exc}"}, status=status.HTTP_400_BAD_REQUEST) + invalidate_category_tree_cache() return Response(result) @action(detail=False, methods=['get'], url_path='category-tree', permission_classes=[IsAuthenticated]) @@ -536,7 +550,7 @@ class MaterialViewSet(ModelViewSet): ], }) - cache.set(CATEGORY_TREE_CACHE_KEY, data, timeout=60 * 30) + cache.set(CATEGORY_TREE_CACHE_KEY, data, timeout=60 * 5) return Response(data) @action(detail=False, methods=['get'], url_path='categories-by-major')