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:
parent
25587dce21
commit
d1201d6923
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
Loading…
Reference in New Issue