package model import ( "time" "gorm.io/gorm" ) type Tenant struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:100;not null;uniqueIndex" json:"name"` PlanID *uint `json:"plan_id"` SeatCount int `gorm:"default:2" json:"seat_count"` ExpireAt time.Time `json:"expire_at"` Status string `gorm:"size:20;default:normal" json:"status"` ContactName string `gorm:"size:30" json:"contact_name"` ContactPhone string `gorm:"size:20" json:"contact_phone"` ContactEmail string `gorm:"size:100" json:"contact_email"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type User struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` Role string `gorm:"size:20;default:agent" json:"role"` Username string `gorm:"size:50;not null;uniqueIndex" json:"username"` PasswordHash string `gorm:"size:255;not null" json:"-"` Nickname string `gorm:"size:50" json:"nickname"` Status string `gorm:"size:20;default:online" json:"status"` LastOnlineAt *time.Time `json:"last_online_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type Channel struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` Type string `gorm:"size:30;not null" json:"type"` Name string `gorm:"size:50" json:"name"` Status string `gorm:"size:20;default:enabled" json:"status"` Config string `gorm:"type:text" json:"config"` ScriptCode string `gorm:"size:500" json:"script_code"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type Customer struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` Name string `gorm:"size:50;not null" json:"name"` Phone string `gorm:"size:20" json:"phone"` // 主手机号(兼容列表搜索) Tel string `gorm:"size:30" json:"tel"` // 联系电话/固话 Email string `gorm:"size:100" json:"email"` Wechat string `gorm:"size:50" json:"wechat"` // 微信号,默认可空 QQ string `gorm:"size:20;column:qq" json:"qq"` Remark string `gorm:"type:text" json:"remark"` // 客户备注 Tags string `gorm:"type:text" json:"tags"` Source string `gorm:"size:30" json:"source"` Status string `gorm:"size:20;default:online" json:"status"` ConversationCount int `gorm:"default:0" json:"conversation_count"` SatisfactionSum float64 `gorm:"default:0" json:"-"` SatisfactionCount int `gorm:"default:0" json:"-"` LastContactAt *time.Time `json:"last_contact_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // CustomerTag 租户级客户标签库,由管理员维护,坐席给客户打标时选择。 type CustomerTag struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"uniqueIndex:idx_tenant_tag_name;not null" json:"tenant_id"` Name string `gorm:"size:30;uniqueIndex:idx_tenant_tag_name;not null" json:"name"` Color string `gorm:"size:20" json:"color"` // 预设色键或 #hex SortOrder int `gorm:"default:0" json:"sort_order"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // BlacklistEntry 租户级访客黑名单(按 IP 或设备指纹拦截)。 // ExpiresAt 为空表示长期有效;允许多条历史记录,拦截时取未过期的最新一条。 type BlacklistEntry struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index:idx_bl_lookup;not null" json:"tenant_id"` Kind string `gorm:"size:20;index:idx_bl_lookup;not null" json:"kind"` // ip | device Value string `gorm:"size:200;index:idx_bl_lookup;not null" json:"value"` Reason string `gorm:"size:500" json:"reason"` ExpiresAt *time.Time `json:"expires_at"` // nil = 长期 OperatorID uint `gorm:"index" json:"operator_id"` SessionID *uint `json:"session_id"` CustomerID *uint `json:"customer_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type Session struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` ChannelID uint `json:"channel_id"` CustomerID uint `gorm:"index" json:"customer_id"` AgentID *uint `gorm:"index" json:"agent_id"` VisitorTokenHash string `gorm:"size:64;index" json:"-"` VisitorIP string `gorm:"size:64" json:"visitor_ip"` VisitorRegion string `gorm:"size:100" json:"visitor_region"` UserAgent string `gorm:"size:500" json:"user_agent"` // DeviceKey 访客端持久设备指纹(localStorage),用于设备级拉黑 DeviceKey string `gorm:"size:64;index" json:"device_key"` // 落地页 / 当前页(访客浏览轨迹) LandingURL string `gorm:"size:1000" json:"landing_url"` LandingTitle string `gorm:"size:200" json:"landing_title"` Referrer string `gorm:"size:1000" json:"referrer"` CurrentURL string `gorm:"size:1000" json:"current_url"` CurrentTitle string `gorm:"size:200" json:"current_title"` // LastSeenAt 访客最近活跃(心跳/换页),用于在线时长与在线状态 LastSeenAt *time.Time `json:"last_seen_at"` // DraftText 访客输入框未发送草稿(实时监控用,非聊天消息) DraftText string `gorm:"type:text" json:"draft_text"` DraftUpdatedAt *time.Time `json:"draft_updated_at"` LastReadSeq int `gorm:"default:0" json:"last_read_seq"` Status string `gorm:"size:20;default:waiting" json:"status"` Priority string `gorm:"size:20;default:normal" json:"priority"` SatisfactionScore *int `json:"satisfaction_score"` SatisfactionText string `gorm:"size:500" json:"satisfaction_text"` EndReason string `gorm:"size:50" json:"end_reason"` CreatedAt time.Time `json:"created_at"` EndedAt *time.Time `json:"ended_at"` UpdatedAt time.Time `json:"updated_at"` } // CustomerContact 客户联系方式(多条不覆盖,各自独立记录)。 type CustomerContact struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` CustomerID uint `gorm:"uniqueIndex:idx_cust_contact;not null" json:"customer_id"` Kind string `gorm:"size:20;uniqueIndex:idx_cust_contact;not null" json:"kind"` // phone|wechat|email|qq Value string `gorm:"size:100;uniqueIndex:idx_cust_contact;not null" json:"value"` Source string `gorm:"size:30" json:"source"` // draft|message|leave|agent CreatedAt time.Time `json:"created_at"` } // VisitorPageView 访客在宿主站的页面浏览记录(按会话)。 type VisitorPageView struct { ID uint `gorm:"primaryKey" json:"id"` SessionID uint `gorm:"index;not null" json:"session_id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` URL string `gorm:"size:1000;not null" json:"url"` Title string `gorm:"size:200" json:"title"` EnteredAt time.Time `json:"entered_at"` } type Message struct { ID uint `gorm:"primaryKey" json:"id"` SessionID uint `gorm:"not null;uniqueIndex:idx_message_session_seq" json:"session_id"` SenderType string `gorm:"size:20;not null" json:"sender_type"` SenderID *uint `json:"sender_id"` Content string `gorm:"type:text;not null" json:"content"` Type string `gorm:"size:20;default:text" json:"type"` Seq int `gorm:"not null;uniqueIndex:idx_message_session_seq" json:"seq"` SentAt time.Time `json:"sent_at"` } type SessionEvent struct { ID uint `gorm:"primaryKey" json:"id"` SessionID uint `gorm:"index;not null" json:"session_id"` OperatorID uint `json:"operator_id"` Action string `gorm:"size:50;not null" json:"action"` Detail string `gorm:"size:500" json:"detail"` CreatedAt time.Time `json:"created_at"` } type Category struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` ParentID *uint `json:"parent_id"` Name string `gorm:"size:30;not null" json:"name"` } type KnowledgeEntry struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` CategoryID uint `json:"category_id"` Title string `gorm:"size:100;not null" json:"title"` Content string `gorm:"type:text;not null" json:"content"` Status string `gorm:"size:20;default:draft" json:"status"` UsageCount int `gorm:"default:0" json:"usage_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // QuickReply 团队/个人快捷回复(与知识库独立)。 // Scope=team 时 OwnerUserID 为空,全租户共享;Scope=personal 时归属 OwnerUserID。 type QuickReply struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` Scope string `gorm:"size:20;index;not null" json:"scope"` // team | personal OwnerUserID *uint `gorm:"index" json:"owner_user_id,omitempty"` Title string `gorm:"size:100;not null" json:"title"` Content string `gorm:"type:text;not null" json:"content"` Shortcut string `gorm:"size:32;index" json:"shortcut"` // 输入码,如 nh → /nh GroupName string `gorm:"size:50" json:"group_name"` Status string `gorm:"size:20;default:draft;index" json:"status"` // draft | published SortOrder int `gorm:"default:0" json:"sort_order"` // UsageCount 全局累计(管理页展示);排序以个人用量为准见 QuickReplyUserUsage UsageCount int `gorm:"default:0" json:"usage_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // MyUsageCount 当前登录用户个人调用次数(接口动态填充,非库字段) MyUsageCount int `gorm:"-" json:"my_usage_count,omitempty"` } // QuickReplyUserUsage 坐席个人快捷回复调用统计(按账户独立,非全租户)。 type QuickReplyUserUsage struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"index;not null" json:"tenant_id"` UserID uint `gorm:"uniqueIndex:idx_qr_user_usage;not null" json:"user_id"` QuickReplyID uint `gorm:"uniqueIndex:idx_qr_user_usage;index;not null" json:"quick_reply_id"` UsageCount int `gorm:"default:0" json:"usage_count"` UpdatedAt time.Time `json:"updated_at"` } type Plan struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:30;not null" json:"name"` PriceMonthly int `json:"price_monthly"` Seats int `json:"seats"` StorageDays int `json:"storage_days"` KBLimit int `json:"kb_limit"` Features string `gorm:"type:text" json:"features"` Status string `gorm:"size:20;default:active" json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type OperationLog struct { ID uint `gorm:"primaryKey" json:"id"` OperatorID uint `json:"operator_id"` Action string `gorm:"size:50;not null" json:"action"` Detail string `gorm:"size:500" json:"detail"` TargetType string `gorm:"size:30" json:"target_type"` TargetID *uint `json:"target_id"` IP string `gorm:"size:50" json:"ip"` CreatedAt time.Time `json:"created_at"` } type Announcement struct { ID uint `gorm:"primaryKey" json:"id"` Title string `gorm:"size:100;not null" json:"title"` Content string `gorm:"type:text" json:"content"` Status string `gorm:"size:20;default:draft" json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // TenantSetting 租户级系统设置(欢迎语、工作时间、通知开关、分配策略等)。 type TenantSetting struct { ID uint `gorm:"primaryKey" json:"id"` TenantID uint `gorm:"uniqueIndex;not null" json:"tenant_id"` DisplayName string `gorm:"size:100" json:"display_name"` AgentNickname string `gorm:"size:50" json:"agent_nickname"` Timezone string `gorm:"size:50;default:Asia/Shanghai" json:"timezone"` WelcomeMessage string `gorm:"size:500" json:"welcome_message"` // WelcomeMessagesJSON 多段欢迎语 JSON:[{type:text|image, content:...}, ...] WelcomeMessagesJSON string `gorm:"type:text" json:"welcome_messages_json"` OfflinePrompt string `gorm:"size:500" json:"offline_prompt"` WorkHoursJSON string `gorm:"type:text" json:"work_hours_json"` WorktimePrompt string `gorm:"size:500" json:"worktime_prompt"` NotifyNewSession bool `gorm:"default:true" json:"notify_new_session"` NotifyOfflineLeave bool `gorm:"default:true" json:"notify_offline_leave"` NotifyDailyReport bool `gorm:"default:false" json:"notify_daily_report"` // AssignStrategy 自动分配策略:least_load(默认)| round_robin AssignStrategy string `gorm:"size:30;default:least_load" json:"assign_strategy"` // MaxActivePerAgent 每位坐席最大进行中会话数;0 表示不限制 MaxActivePerAgent int `gorm:"default:0" json:"max_active_per_agent"` // RRLastAgentID 轮询策略的上次分配坐席游标 RRLastAgentID *uint `json:"-"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` }