@@ -43,6 +43,13 @@ func NewrefererStatsManager(userRepoPtr *store.Repository) *ClientStatsManager {
4343 }
4444}
4545
46+ func NewRefererIPStatsManager (userRepoPtr * store.Repository ) * ClientStatsManager {
47+ return & ClientStatsManager {
48+ repo : userRepoPtr ,
49+ statsType : "referer_ip" ,
50+ }
51+ }
52+
4653func NewBrowserStatsManager (userRepoPtr * store.Repository ) * ClientStatsManager {
4754 return & ClientStatsManager {
4855 repo : userRepoPtr ,
@@ -141,6 +148,18 @@ func (s *ClientStatsManager) Query(query StatsQuery) (StatsResult, error) {
141148 groupExpr = "u.url"
142149 case "referer" :
143150 joinClause = fmt .Sprintf (`JOIN "%s_dim_referer" r ON r.id = l.referer_id` , query .WebsiteID )
151+ case "referer_ip" :
152+ joinClause = fmt .Sprintf (
153+ `JOIN "%s_dim_referer" r ON r.id = l.referer_id JOIN "%s_dim_ip" ip ON ip.id = l.ip_id` ,
154+ query .WebsiteID ,
155+ query .WebsiteID ,
156+ )
157+ selectExpr = "ip.ip"
158+ groupExpr = "ip.ip"
159+ sourceKind , _ := query .ExtraParam ["sourceKind" ].(string )
160+ if sourceCondition := buildRefererSourceCondition (sourceKind , "r.referer" ); sourceCondition != "" {
161+ extraCondition += " AND " + sourceCondition
162+ }
144163 case "user_browser" :
145164 joinClause = fmt .Sprintf (`JOIN "%s_dim_ua" ua ON ua.id = l.ua_id` , query .WebsiteID )
146165 selectExpr = "ua.browser"
@@ -183,7 +202,7 @@ func (s *ClientStatsManager) Query(query StatsQuery) (StatsResult, error) {
183202
184203 rows , err := s .repo .GetDB ().Query (dbQueryStr , startTime .Unix (), endTime .Unix (), limit )
185204 if err != nil {
186- return result , fmt .Errorf ("查询URL统计失败 : %v" , err )
205+ return result , fmt .Errorf ("查询客户端统计失败 : %v" , err )
187206 }
188207 defer rows .Close ()
189208
@@ -194,7 +213,7 @@ func (s *ClientStatsManager) Query(query StatsQuery) (StatsResult, error) {
194213 var url string
195214 var pv , uv int
196215 if err := rows .Scan (& url , & pv , & uv ); err != nil {
197- return result , fmt .Errorf ("解析URL统计结果失败 : %v" , err )
216+ return result , fmt .Errorf ("解析客户端统计结果失败 : %v" , err )
198217 }
199218 result .Key = append (result .Key , url )
200219 result .PV = append (result .PV , pv )
@@ -204,7 +223,7 @@ func (s *ClientStatsManager) Query(query StatsQuery) (StatsResult, error) {
204223 }
205224
206225 if err := rows .Err (); err != nil {
207- return result , fmt .Errorf ("遍历URL统计结果失败 : %v" , err )
226+ return result , fmt .Errorf ("遍历客户端统计结果失败 : %v" , err )
208227 }
209228
210229 if totalPV > 0 && totalUV > 0 {
@@ -258,3 +277,46 @@ func normalizeDomain(raw string) string {
258277 raw = strings .TrimSuffix (raw , "/" )
259278 return raw
260279}
280+
281+ func buildRefererSourceCondition (sourceKind string , refererColumn string ) string {
282+ directCondition := fmt .Sprintf ("(%s = '-' OR %s = '')" , refererColumn , refererColumn )
283+ searchCondition := buildSearchEngineRefererCondition (refererColumn )
284+
285+ switch sourceKind {
286+ case "search" :
287+ return searchCondition
288+ case "direct" :
289+ return directCondition
290+ case "external" :
291+ if searchCondition == "" {
292+ return fmt .Sprintf ("NOT %s" , directCondition )
293+ }
294+ return fmt .Sprintf ("(NOT %s AND NOT %s)" , directCondition , searchCondition )
295+ default :
296+ return ""
297+ }
298+ }
299+
300+ func buildSearchEngineRefererCondition (refererColumn string ) string {
301+ patterns := []string {
302+ "baidu." ,
303+ "google." ,
304+ "bing." ,
305+ "sogou." ,
306+ "360." ,
307+ "so.com" ,
308+ "yahoo." ,
309+ "duckduckgo." ,
310+ "yandex." ,
311+ }
312+
313+ conditions := make ([]string , 0 , len (patterns ))
314+ for _ , pattern := range patterns {
315+ safePattern := strings .ReplaceAll (strings .ToLower (pattern ), "'" , "''" )
316+ conditions = append (conditions , fmt .Sprintf ("LOWER(%s) LIKE '%%%s%%'" , refererColumn , safePattern ))
317+ }
318+ if len (conditions ) == 0 {
319+ return ""
320+ }
321+ return fmt .Sprintf ("(%s)" , strings .Join (conditions , " OR " ))
322+ }
0 commit comments