forked from erp-dev/erp
147 lines
4.0 KiB
Plaintext
147 lines
4.0 KiB
Plaintext
@startuml
|
||
title 中立 mission 及跟进模块类图
|
||
|
||
skinparam shadowing false
|
||
skinparam class {
|
||
BackgroundColor #F8FBFF
|
||
BorderColor #4C6A92
|
||
ArrowColor #4C6A92
|
||
FontName Noto Sans CJK SC
|
||
}
|
||
skinparam note {
|
||
BackgroundColor #FFF7E6
|
||
BorderColor #B7791F
|
||
FontName Noto Sans CJK SC
|
||
}
|
||
skinparam defaultFontName Noto Sans CJK SC
|
||
|
||
class "Mission\n任务" as Mission {
|
||
+id: BigAutoField
|
||
+merchant: Merchant
|
||
+description: Text
|
||
+category: CharField
|
||
+created_at: DateTime
|
||
+is_urgent: Boolean = false
|
||
+is_completed: Boolean = false
|
||
+is_cancelled: Boolean = false
|
||
+cancelled_at: DateTime? = null
|
||
+content_type: ContentType? = null
|
||
+content_id: PositiveBigInteger? = null
|
||
+content_object: GenericForeignKey?
|
||
+creator: Employee
|
||
+cancelled_by: Employee? = null
|
||
--
|
||
+participants: QuerySet<Employee>
|
||
+has_ending_reply: Boolean <<computed>>
|
||
+can_reply: Boolean <<computed>>
|
||
+get_participants(): QuerySet<Employee>
|
||
+filter_participants(...): QuerySet<Employee>
|
||
+reopen(): Mission
|
||
}
|
||
|
||
class "MissionParticipant\n任务参与者" as MissionParticipant {
|
||
+id: BigAutoField
|
||
+merchant: Merchant
|
||
+mission: Mission
|
||
+employee: Employee
|
||
+created_at: DateTime
|
||
}
|
||
|
||
class "MissionReply\n任务回应" as MissionReply {
|
||
+id: BigAutoField
|
||
+merchant: Merchant
|
||
+mission: Mission
|
||
+responder: Employee
|
||
+content: Text
|
||
+replied_at: DateTime
|
||
+ends_task: Boolean = false
|
||
+is_rejected: Boolean = false
|
||
+rejected_by: Employee? = null
|
||
+rejected_at: DateTime? = null
|
||
}
|
||
|
||
class "Merchant\nbasic_info.Merchant" as Merchant {
|
||
+id: BigAutoField
|
||
+name: CharField
|
||
}
|
||
|
||
class "Employee\nbasic_info.Employee" as Employee {
|
||
+id: BigAutoField
|
||
+name: CharField
|
||
+merchant: Merchant
|
||
}
|
||
|
||
class "ContentType\ndjango.contrib.contenttypes" as ContentType {
|
||
+id: AutoField
|
||
+app_label: CharField
|
||
+model: CharField
|
||
}
|
||
|
||
class "Any Business Model\n任意业务模型" as AnyBusinessModel {
|
||
+id: ...
|
||
}
|
||
|
||
Mission "1" o-- "0..*" MissionParticipant : participants
|
||
Mission "0..*" --> "1" Merchant : merchant
|
||
MissionParticipant "0..*" --> "1" Merchant : merchant
|
||
MissionReply "0..*" --> "1" Merchant : merchant
|
||
MissionParticipant "0..*" --> "1" Employee : employee
|
||
Mission "1" --> "1" Employee : creator
|
||
Mission "0..*" --> "0..1" Employee : cancelled_by
|
||
Mission "1" o-- "0..*" MissionReply : replies
|
||
MissionReply "0..*" --> "1" Employee : responder
|
||
MissionReply "0..*" --> "0..1" Employee : rejected_by
|
||
Mission "0..*" --> "0..1" ContentType : content_type
|
||
Mission ..> AnyBusinessModel : content_object\nGenericForeignKey
|
||
|
||
note right of Mission
|
||
content_type 与 content_id 均可为空:
|
||
- 为空:任务不绑定具体业务对象
|
||
- 非空:任务可关联系统中任意业务模型
|
||
|
||
所有人物字段均关联 basic_info.Employee,
|
||
不直接关联 request.user / auth.User。
|
||
|
||
Mission / MissionParticipant / MissionReply
|
||
都冗余保存 merchant,用于严格多商户隔离。
|
||
end note
|
||
|
||
note bottom of Mission
|
||
参与者通过 MissionParticipant 中间表维护。
|
||
Mission 模型应提供便捷方法用于获取和筛选参与者,
|
||
但关系本身不直接写成裸 ManyToMany 字段,
|
||
方便后续扩展参与者状态、角色、加入时间等信息。
|
||
end note
|
||
|
||
note right of MissionReply
|
||
当任意关联回应 ends_task = true 时:
|
||
- Mission.has_ending_reply = true
|
||
- Mission.can_reply = false
|
||
- 后续不允许继续创建 MissionReply
|
||
|
||
创建 ends_task = true 的回应时,
|
||
本模块 service 同步设置:
|
||
Mission.is_completed = true
|
||
end note
|
||
|
||
note left of Mission
|
||
is_cancelled 默认为 false。
|
||
取消任务时写入:
|
||
- cancelled_by
|
||
- cancelled_at
|
||
end note
|
||
|
||
note bottom of MissionReply
|
||
reopen 独立业务函数的前置条件:
|
||
- Mission.is_cancelled = false
|
||
- Mission.is_completed = true
|
||
- 能查询到 ends_task = true 的 MissionReply
|
||
|
||
reopen 执行效果:
|
||
- Mission.is_completed = false
|
||
- 对 ends_task = true 的回应设置 ends_task = false
|
||
- 同时记录该回应 is_rejected = true、
|
||
rejected_by = reopened_by、rejected_at = now
|
||
end note
|
||
@enduml
|