feat(widget): 优化访客在线轨迹、GeoIP与多站点渠道
CI / Frontend (typecheck + lint + build) (push) Successful in 2m5s
CI / Backend (go vet + test) (push) Failing after 5m20s

This commit is contained in:
yml2213
2026-11-06 18:21:30 +08:00
parent 24fac1bcf1
commit 38d6fc1684
37 changed files with 1071 additions and 123 deletions
+17 -6
View File
@@ -39,6 +39,7 @@ type Channel struct {
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"`
SiteDomain string `gorm:"size:255" json:"site_domain"`
Status string `gorm:"size:20;default:enabled" json:"status"`
Config string `gorm:"type:text" json:"config"`
ScriptCode string `gorm:"size:500" json:"script_code"`
@@ -103,6 +104,10 @@ type Session struct {
VisitorTokenHash string `gorm:"size:64;index" json:"-"`
VisitorIP string `gorm:"size:64" json:"visitor_ip"`
VisitorRegion string `gorm:"size:100" json:"visitor_region"`
VisitorCountry string `gorm:"size:100" json:"visitor_country"`
VisitorProvince string `gorm:"size:100" json:"visitor_province"`
VisitorCity string `gorm:"size:100" json:"visitor_city"`
SiteHost string `gorm:"size:255" json:"site_host"`
UserAgent string `gorm:"size:500" json:"user_agent"`
// DeviceKey 访客端持久设备指纹(localStorage),用于设备级拉黑
DeviceKey string `gorm:"size:64;index" json:"device_key"`
@@ -114,6 +119,10 @@ type Session struct {
CurrentTitle string `gorm:"size:200" json:"current_title"`
// LastSeenAt 访客最近活跃(心跳/换页),用于在线时长与在线状态
LastSeenAt *time.Time `json:"last_seen_at"`
// VisitorLeftAt 显式离开时间;再次心跳会清除并恢复在线。
VisitorLeftAt *time.Time `json:"visitor_left_at"`
// VisitorOnline 不入库,由 LastSeenAt/VisitorLeftAt 动态计算。
VisitorOnline bool `gorm:"-" json:"visitor_online"`
// DraftText 访客输入框未发送草稿(实时监控用,非聊天消息)
DraftText string `gorm:"type:text" json:"draft_text"`
DraftUpdatedAt *time.Time `json:"draft_updated_at"`
@@ -141,12 +150,14 @@ type CustomerContact struct {
// 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"`
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"`
ExitedAt *time.Time `json:"exited_at"`
Duration int `gorm:"column:duration_seconds" json:"duration_seconds"`
}
type Message struct {