"""消息通知与智能预警模型: 1. Notification 站内消息/通知 2. AlertRule 业务预警规则(库存低限预警、应收账款逾期催收预警等) """ from django.conf import settings from django.db import models from apps.core.base_models import TenantScopedModel class Notification(TenantScopedModel): """消息通知。""" CATEGORY_CHOICES = [ ("info", "系统通知"), ("warning", "业务预警"), ("error", "异常提醒"), ("audit", "审批待办"), ] recipient = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.CASCADE, related_name="notifications", help_text="接收人(为空表示租户内广播)", ) title = models.CharField(max_length=128) content = models.TextField() category = models.CharField(max_length=16, choices=CATEGORY_CHOICES, default="info") link = models.CharField(max_length=255, blank=True, default="", help_text="跳转路由") is_read = models.BooleanField(default=False) read_at = models.DateTimeField(null=True, blank=True) extra_data = models.JSONField(default=dict, blank=True) class Meta: db_table = "notify_notification" indexes = [ models.Index(fields=["tenant", "recipient", "is_read"]), models.Index(fields=["tenant", "-created_at"]), ] ordering = ["-created_at"] def __str__(self): return f"[{self.get_category_display()}] {self.title} (已读: {self.is_read})" class AlertRule(TenantScopedModel): """业务预警规则配置。""" RULE_TYPE_CHOICES = [ ("inventory_low", "库存缺货/下限预警"), ("receivable_overdue", "应收账款逾期催款预警"), ("stock_backlog", "商品呆滞积压预警"), ("batch_expiry", "批次近效期预警"), ("risk_score", "AI 应收风险预警(threshold=风险分数线)"), ] code = models.CharField(max_length=64, help_text="规则代码") name = models.CharField(max_length=128, help_text="预警规则名称") rule_type = models.CharField(max_length=32, choices=RULE_TYPE_CHOICES) threshold = models.DecimalField(max_digits=18, decimal_places=4, default=10, help_text="预警阈值(如天数或数量)") is_enabled = models.BooleanField(default=True) channels = models.JSONField(default=list, blank=True, help_text="通知渠道 ['in_app', 'webhook']") class Meta: db_table = "notify_alert_rule" unique_together = [("tenant", "code")] verbose_name_plural = "业务预警规则" ordering = ["code"] def __str__(self): return f"{self.name} (阈值: {self.threshold}) [{ '启用' if self.is_enabled else '停用' }]"