75 lines
1.9 KiB
Go
75 lines
1.9 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"strings"
|
|
|
|
"kefu-cloud/server/internal/geoip"
|
|
"kefu-cloud/server/internal/model"
|
|
)
|
|
|
|
const visitorGeoBackfillBatch = 500
|
|
|
|
var visitorGeoLookup = geoip.Lookup
|
|
|
|
// BackfillVisitorGeoIP 使用历史会话保存的 IP 补齐旧数据中的国家/省市信息。
|
|
func BackfillVisitorGeoIP(ctx context.Context) (int, error) {
|
|
var lastID uint
|
|
updated := 0
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return updated, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
var sessions []model.Session
|
|
if err := model.DB.
|
|
Select("id", "visitor_ip").
|
|
Where("id > ? AND visitor_ip <> ''", lastID).
|
|
Where("visitor_country = '' AND (visitor_region = '' OR visitor_region = ?)", "未知").
|
|
Order("id asc").Limit(visitorGeoBackfillBatch).Find(&sessions).Error; err != nil {
|
|
return updated, err
|
|
}
|
|
if len(sessions) == 0 {
|
|
return updated, nil
|
|
}
|
|
|
|
for _, session := range sessions {
|
|
lastID = session.ID
|
|
location, ok := visitorGeoLookup(session.VisitorIP)
|
|
if !ok {
|
|
if isPrivateIP(session.VisitorIP) {
|
|
if err := model.DB.Model(&model.Session{}).Where("id = ?", session.ID).
|
|
Update("visitor_region", "内网").Error; err != nil {
|
|
return updated, err
|
|
}
|
|
updated++
|
|
}
|
|
continue
|
|
}
|
|
if strings.TrimSpace(location.Region) == "" {
|
|
continue
|
|
}
|
|
if err := model.DB.Model(&model.Session{}).Where("id = ?", session.ID).Updates(map[string]interface{}{
|
|
"visitor_region": location.Region,
|
|
"visitor_country": location.Country,
|
|
"visitor_province": location.Province,
|
|
"visitor_city": location.City,
|
|
}).Error; err != nil {
|
|
return updated, err
|
|
}
|
|
updated++
|
|
}
|
|
if len(sessions) < visitorGeoBackfillBatch {
|
|
return updated, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func isPrivateIP(raw string) bool {
|
|
ip := net.ParseIP(strings.TrimSpace(raw))
|
|
return ip != nil && (ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast())
|
|
}
|