refactor(material): 改用显式失效缓存,移除 signals;TTL 30min → 5min

- 删除 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 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-27 09:36:11 +08:00
parent 25587dce21
commit d1201d6923
3 changed files with 16 additions and 26 deletions

View File

@ -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

View File

@ -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()

View File

@ -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')