package model import ( "encoding/json" "time" ) // Consultation 专家会诊单 type Consultation struct { ID string `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"` RoomID *string `gorm:"column:room_id;type:uuid;index" json:"roomId,omitempty"` BatchID *string `gorm:"column:batch_id;type:uuid;index" json:"batchId,omitempty"` LampTestID *string `gorm:"column:lamp_test_id;type:uuid;index" json:"lampTestId,omitempty"` Title string `gorm:"size:128" json:"title"` Summary *string `gorm:"type:text" json:"summary,omitempty"` Snapshot json.RawMessage `gorm:"type:jsonb" json:"snapshot,omitempty"` Status string `gorm:"size:16;default:pending" json:"status"` // pending/consulting/resolved/archived ExpertID *string `gorm:"column:expert_id;type:uuid" json:"expertId,omitempty"` AssigneeID *string `gorm:"column:assignee_id;type:uuid" json:"assigneeId,omitempty"` AssignedAt *time.Time `gorm:"column:assigned_at;type:timestamptz" json:"assignedAt,omitempty"` AcceptedAt *time.Time `gorm:"column:accepted_at;type:timestamptz" json:"acceptedAt,omitempty"` NeedsInfoAt *time.Time `gorm:"column:needs_info_at;type:timestamptz" json:"needsInfoAt,omitempty"` SLADeadline *time.Time `gorm:"column:sla_deadline;type:timestamptz" json:"slaDeadline,omitempty"` OverdueAt *time.Time `gorm:"column:overdue_at;type:timestamptz" json:"overdueAt,omitempty"` LastSLAEventAt *time.Time `gorm:"column:last_sla_event_at;type:timestamptz" json:"lastSlaEventAt,omitempty"` Opinion *string `gorm:"type:text" json:"opinion,omitempty"` Plan *string `gorm:"type:text" json:"plan,omitempty"` ResolvedAt *time.Time `gorm:"column:resolved_at;type:timestamptz" json:"resolvedAt,omitempty"` ArchivedAt *time.Time `gorm:"column:archived_at;type:timestamptz" json:"archivedAt,omitempty"` CreatedAt time.Time `gorm:"type:timestamptz" json:"createdAt"` UpdatedAt time.Time `gorm:"type:timestamptz" json:"updatedAt"` RoomName *string `gorm:"-" json:"roomName,omitempty"` } func (Consultation) TableName() string { return "consultations" } // ValidConsultationTransition 状态流转规则:unassigned/assigned/accepted/needs_info/resolved/archived。 func ValidConsultationTransition(from, to string) bool { switch from { case "unassigned": return to == "assigned" case "assigned": return to == "accepted" || to == "needs_info" case "accepted": return to == "needs_info" || to == "resolved" case "needs_info": return to == "accepted" || to == "resolved" case "resolved": return to == "archived" || to == "needs_info" default: return false } } // ConsultationSLAState 返回会诊 SLA 状态;超时只标记,不自动伪造专家结论。 func ConsultationSLAState(t Consultation, now time.Time) string { if t.SLADeadline == nil { return "unscheduled" } if now.After(*t.SLADeadline) { return "overdue" } return "on_time" } // ConsultationSnapshot 病例快照(会诊时打包,避免引用数据变化) type ConsultationSnapshot struct { RoomName string `json:"roomName,omitempty"` LampTest *LampTest `json:"lampTest,omitempty"` Inspection *InspectionRecord `json:"inspection,omitempty"` Batch *Batch `json:"batch,omitempty"` WeatherAlerts []WeatherAlert `json:"weatherAlerts,omitempty"` CreatedAt time.Time `json:"createdAt"` }