forked from erp-dev/erp
feat: notifier beta
This commit is contained in:
189
mission/models.py
Normal file
189
mission/models.py
Normal file
@@ -0,0 +1,189 @@
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from flower.common import ModelBase
|
||||
|
||||
|
||||
class MissionCategory(ModelBase):
|
||||
"""任务分类字典,按商户隔离。"""
|
||||
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
merchant = models.ForeignKey(
|
||||
"basic_info.Merchant",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="mission_categories",
|
||||
verbose_name="所属商户",
|
||||
)
|
||||
name = models.CharField(max_length=50, verbose_name="分类名称")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "任务分类"
|
||||
verbose_name_plural = "任务分类"
|
||||
ordering = ["id"]
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["merchant", "name"], name="unique_mission_category_name_per_merchant"),
|
||||
]
|
||||
|
||||
|
||||
class Mission(ModelBase):
|
||||
"""中立任务,可选关联到系统中的任意业务对象。"""
|
||||
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
merchant = models.ForeignKey(
|
||||
"basic_info.Merchant",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="missions",
|
||||
verbose_name="所属商户",
|
||||
)
|
||||
description = models.TextField(verbose_name="任务描述")
|
||||
category = models.ForeignKey(
|
||||
MissionCategory,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="missions",
|
||||
verbose_name="任务类型",
|
||||
)
|
||||
is_urgent = models.BooleanField(default=False, db_index=True, verbose_name="是否紧急")
|
||||
is_completed = models.BooleanField(default=False, db_index=True, verbose_name="是否完成")
|
||||
is_cancelled = models.BooleanField(default=False, db_index=True, verbose_name="是否取消")
|
||||
cancelled_at = models.DateTimeField(null=True, blank=True, verbose_name="取消时间")
|
||||
creator = models.ForeignKey(
|
||||
"basic_info.Employee",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="created_missions",
|
||||
verbose_name="任务创建者",
|
||||
)
|
||||
cancelled_by = models.ForeignKey(
|
||||
"basic_info.Employee",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="cancelled_missions",
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="取消人",
|
||||
)
|
||||
content_type = models.ForeignKey(
|
||||
ContentType,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="missions",
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="关联对象类型",
|
||||
)
|
||||
content_id = models.PositiveBigIntegerField(null=True, blank=True, db_index=True, verbose_name="关联对象ID")
|
||||
content_object = GenericForeignKey("content_type", "content_id")
|
||||
|
||||
@property
|
||||
def has_ending_reply(self) -> bool:
|
||||
return self.replies.filter(ends_task=True, is_rejected=False).exists()
|
||||
|
||||
@property
|
||||
def can_reply(self) -> bool:
|
||||
return not self.is_cancelled and not self.has_ending_reply
|
||||
|
||||
def get_participants(self):
|
||||
return self.participants.select_related("employee")
|
||||
|
||||
def filter_participants(self, **filters):
|
||||
return self.get_participants().filter(**filters)
|
||||
|
||||
@property
|
||||
def category_name(self) -> str:
|
||||
return self.category.name
|
||||
|
||||
def __str__(self):
|
||||
return f"Mission #{self.id}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "任务"
|
||||
verbose_name_plural = "任务"
|
||||
permissions = [
|
||||
("reopen_mission", "Can reopen mission"),
|
||||
("reject_mission_reply", "Can reject mission reply"),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=["merchant", "content_type", "content_id"]),
|
||||
models.Index(fields=["merchant", "is_completed", "is_cancelled"]),
|
||||
]
|
||||
|
||||
|
||||
class MissionParticipant(ModelBase):
|
||||
"""任务参与员工。"""
|
||||
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
merchant = models.ForeignKey(
|
||||
"basic_info.Merchant",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="mission_participants",
|
||||
verbose_name="所属商户",
|
||||
)
|
||||
mission = models.ForeignKey(
|
||||
Mission,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="participants",
|
||||
verbose_name="任务",
|
||||
)
|
||||
employee = models.ForeignKey(
|
||||
"basic_info.Employee",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="mission_participations",
|
||||
verbose_name="参与者",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.mission_id} - {self.employee_id}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "任务参与者"
|
||||
verbose_name_plural = "任务参与者"
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["mission", "employee"], name="unique_mission_participant"),
|
||||
]
|
||||
|
||||
|
||||
class MissionReply(ModelBase):
|
||||
"""任务回应记录。"""
|
||||
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
merchant = models.ForeignKey(
|
||||
"basic_info.Merchant",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="mission_replies",
|
||||
verbose_name="所属商户",
|
||||
)
|
||||
mission = models.ForeignKey(
|
||||
Mission,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="replies",
|
||||
verbose_name="任务",
|
||||
)
|
||||
responder = models.ForeignKey(
|
||||
"basic_info.Employee",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="mission_replies",
|
||||
verbose_name="回应者",
|
||||
)
|
||||
content = models.TextField(verbose_name="回应内容")
|
||||
replied_at = models.DateTimeField(default=timezone.now, db_index=True, verbose_name="回应时间")
|
||||
ends_task = models.BooleanField(default=False, db_index=True, verbose_name="是否结束任务")
|
||||
is_rejected = models.BooleanField(default=False, db_index=True, verbose_name="是否被撤销")
|
||||
rejected_by = models.ForeignKey(
|
||||
"basic_info.Employee",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="rejected_mission_replies",
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="撤销人",
|
||||
)
|
||||
rejected_at = models.DateTimeField(null=True, blank=True, verbose_name="撤销时间")
|
||||
|
||||
def __str__(self):
|
||||
return f"MissionReply #{self.id}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "任务回应"
|
||||
verbose_name_plural = "任务回应"
|
||||
ordering = ["replied_at", "id"]
|
||||
Reference in New Issue
Block a user