267 lines
13 KiB
Go
267 lines
13 KiB
Go
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 int `gorm:"default:0" json:"usage_count"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
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"`
|
||
}
|