fc / apps /cases /models /log.py
jun2024's picture
Upload 2241 files
b4f2238 verified
Raw History Blame Contribute Delete
6.34 kB
"""Module for log."""
from __future__ import annotations
import logging
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar
from django.core.files.uploadedfile import UploadedFile
from django.db import models
from django.utils.translation import gettext_lazy as _
from apps.cases.utils import CASE_LOG_ALLOWED_EXTENSIONS, CASE_LOG_MAX_FILE_SIZE
from apps.core.filesystem.storage import KeepOriginalNameStorage
_SENTINEL = object()
from .case import Case
if TYPE_CHECKING:
from django.db.models.fields.related_descriptors import RelatedManager
# 案件日志附件存储
case_log_storage = KeepOriginalNameStorage()
logger = logging.getLogger(__name__)
def validate_log_attachment(file: UploadedFile) -> None:
"""验证日志附件"""
name = str(getattr(file, "name", ""))
size: int = int(getattr(file, "size", 0) or 0)
ext = Path(name).suffix.lower()
if ext not in CASE_LOG_ALLOWED_EXTENSIONS:
from django.core.exceptions import ValidationError
raise ValidationError(_("不支持的文件类型"))
if size and size > CASE_LOG_MAX_FILE_SIZE:
from django.core.exceptions import ValidationError
raise ValidationError(_("文件大小超过50MB限制"))
class CaseLog(models.Model):
id: int
case = models.ForeignKey(Case, on_delete=models.CASCADE, related_name="logs", verbose_name=_("案件"))
content = models.TextField(verbose_name=_("日志内容"))
actor = models.ForeignKey(
"organization.Lawyer", on_delete=models.PROTECT, related_name="case_logs", verbose_name=_("操作人")
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建日期"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("修改日期"))
if TYPE_CHECKING:
attachments: RelatedManager[CaseLogAttachment]
versions: RelatedManager[CaseLogVersion]
class Meta:
verbose_name = _("日志")
verbose_name_plural = _("日志")
indexes: ClassVar = [
models.Index(fields=["case", "-created_at"]),
models.Index(fields=["actor"]),
]
def __str__(self) -> str:
return f"{self.case_id}-{self.actor_id}-{self.created_at}"
def _exported_reminders(self) -> list[dict[str, Any]]:
cached = getattr(self, "_cached_exported_reminders", _SENTINEL)
if cached is not _SENTINEL:
return cached
if not getattr(self, "id", None):
self._cached_exported_reminders = []
return []
reminders: list[dict[str, Any]]
try:
from apps.core.interfaces import ServiceLocator
reminder_service = ServiceLocator.get_reminder_service()
reminders = reminder_service.export_case_log_reminders_internal(case_log_id=int(self.id))
except Exception:
logger.exception("case_log_export_reminders_failed", extra={"case_log_id": int(self.id)})
reminders = []
self._cached_exported_reminders = reminders
return reminders
@property
def reminder_entries(self) -> list[dict[str, Any]]:
return self._exported_reminders()
@property
def has_reminders(self) -> bool:
return bool(self._exported_reminders())
@property
def reminder_count(self) -> int:
return len(self._exported_reminders())
@property
def _latest_reminder(self) -> Any | None:
"""缓存最近的提醒记录,避免重复查询。"""
cached = getattr(self, "_cached_latest_reminder", _SENTINEL)
if cached is not _SENTINEL:
return cached
if not getattr(self, "id", None):
self._cached_latest_reminder = None
return None
reminder: dict[str, Any] | None = None
try:
from apps.core.interfaces import ServiceLocator
reminder_service = ServiceLocator.get_reminder_service()
reminder = reminder_service.get_latest_case_log_reminder_internal(case_log_id=int(self.id))
except Exception:
logger.exception("case_log_latest_reminder_failed", extra={"case_log_id": int(self.id)})
reminders = self._exported_reminders()
reminder = reminders[-1] if reminders else None
self._cached_latest_reminder = reminder
return reminder
@property
def reminder_type(self) -> str | None:
"""获取最近的提醒类型。"""
reminder = self._latest_reminder
if reminder is None:
return None
if isinstance(reminder, dict):
return str(reminder.get("reminder_type") or "")
return str(getattr(reminder, "reminder_type", ""))
@property
def reminder_time(self) -> datetime | None:
"""获取最近的提醒时间。"""
reminder = self._latest_reminder
if reminder is None:
return None
due_at = reminder.get("due_at") if isinstance(reminder, dict) else getattr(reminder, "due_at", None)
return due_at if isinstance(due_at, datetime) else None
class CaseLogAttachment(models.Model):
id: int
log = models.ForeignKey(CaseLog, on_delete=models.CASCADE, related_name="attachments", verbose_name=_("日志"))
file = models.FileField(
upload_to="case_logs/",
storage=case_log_storage,
validators=[validate_log_attachment],
verbose_name=_("相关文书"),
)
uploaded_at = models.DateTimeField(auto_now_add=True, verbose_name=_("上传时间"))
class Meta:
verbose_name = _("日志附件")
verbose_name_plural = _("日志附件")
class CaseLogVersion(models.Model):
id: int
log = models.ForeignKey(CaseLog, on_delete=models.CASCADE, related_name="versions", verbose_name=_("日志"))
content = models.TextField(verbose_name=_("历史内容"))
version_at = models.DateTimeField(auto_now_add=True, verbose_name=_("版本时间"))
actor = models.ForeignKey(
"organization.Lawyer", on_delete=models.PROTECT, related_name="case_log_versions", verbose_name=_("操作者")
)
class Meta:
verbose_name = _("案件日志版本")
verbose_name_plural = _("案件日志版本")
def __str__(self) -> str:
return f"{self.log_id}-{self.version_at}"