@@ -48,8 +48,21 @@ type Node struct {
4848 IsBroadcast bool `gorm:"default:false"` // IP来源:true=广播IP false=原生IP
4949 IsResidential bool `gorm:"default:false"` // 是否住宅IP
5050 FraudScore int `gorm:"default:-1"` // 欺诈评分(0-100,-1表示未检测)
51+ QualityStatus string `gorm:"size:32;default:'untested'"`
52+ QualityFamily string `gorm:"size:16;default:''"`
5153}
5254
55+ const (
56+ QualityStatusUntested = "untested"
57+ QualityStatusSuccess = "success"
58+ QualityStatusPartial = "partial"
59+ QualityStatusFailed = "failed"
60+ QualityStatusDisabled = "disabled"
61+
62+ QualityFamilyIPv4 = "ipv4"
63+ QualityFamilyIPv6 = "ipv6"
64+ )
65+
5366// nodeCache 使用新的泛型缓存,支持二级索引
5467var nodeCache * cache.MapCache [int , Node ]
5568
@@ -121,6 +134,23 @@ func NormalizeNodeForImport(node *Node) {
121134 if node .UpdatedAt .IsZero () {
122135 node .UpdatedAt = node .CreatedAt
123136 }
137+
138+ if node .QualityStatus == "" {
139+ switch {
140+ case node .FraudScore >= 0 :
141+ node .QualityStatus = QualityStatusSuccess
142+ case node .FraudScore < 0 :
143+ node .QualityStatus = QualityStatusUntested
144+ }
145+ }
146+
147+ if node .QualityFamily == "" && node .LandingIP != "" {
148+ if strings .Contains (node .LandingIP , ":" ) {
149+ node .QualityFamily = QualityFamilyIPv6
150+ } else {
151+ node .QualityFamily = QualityFamilyIPv4
152+ }
153+ }
124154}
125155
126156// InitNodeCache 初始化节点缓存
@@ -198,7 +228,7 @@ func (node *Node) Update() error {
198228
199229// UpdateSpeed 更新节点测速结果
200230func (node * Node ) UpdateSpeed () error {
201- err := database .DB .Model (node ).Select ("Speed" , "SpeedStatus" , "LinkCountry" , "LandingIP" , "DelayTime" , "DelayStatus" , "LatencyCheckAt" , "SpeedCheckAt" , "IsBroadcast" , "IsResidential" , "FraudScore" ).Updates (node ).Error
231+ err := database .DB .Model (node ).Select ("Speed" , "SpeedStatus" , "LinkCountry" , "LandingIP" , "DelayTime" , "DelayStatus" , "LatencyCheckAt" , "SpeedCheckAt" , "IsBroadcast" , "IsResidential" , "FraudScore" , "QualityStatus" , "QualityFamily" ).Updates (node ).Error
202232 if err != nil {
203233 return err
204234 }
@@ -215,6 +245,8 @@ func (node *Node) UpdateSpeed() error {
215245 cachedNode .IsBroadcast = node .IsBroadcast
216246 cachedNode .IsResidential = node .IsResidential
217247 cachedNode .FraudScore = node .FraudScore
248+ cachedNode .QualityStatus = node .QualityStatus
249+ cachedNode .QualityFamily = node .QualityFamily
218250 nodeCache .Set (node .ID , cachedNode )
219251 }
220252 return nil
@@ -235,6 +267,8 @@ type SpeedTestResult struct {
235267 IsBroadcast bool // IP来源:true=广播IP
236268 IsResidential bool // 是否住宅IP
237269 FraudScore int // 欺诈评分(0-100,-1=未检测)
270+ QualityStatus string
271+ QualityFamily string
238272}
239273
240274// BatchAddNodes 批量添加节点(高效 + 容错)
@@ -400,6 +434,8 @@ var speedResultFields = []speedResultField{
400434 return "0"
401435 }},
402436 {"fraud_score" , func (r SpeedTestResult ) string { return fmt .Sprintf ("%d" , r .FraudScore ) }},
437+ {"quality_status" , func (r SpeedTestResult ) string { return fmt .Sprintf ("'%s'" , escapeSQL (r .QualityStatus )) }},
438+ {"quality_family" , func (r SpeedTestResult ) string { return fmt .Sprintf ("'%s'" , escapeSQL (r .QualityFamily )) }},
403439}
404440
405441// tryBatchUpdateWithCaseWhen 使用 CASE WHEN 批量更新(高效)
@@ -475,6 +511,8 @@ func batchUpdateNodeCache(chunk []SpeedTestResult, skipSpeed bool) {
475511 cachedNode .IsBroadcast = r .IsBroadcast
476512 cachedNode .IsResidential = r .IsResidential
477513 cachedNode .FraudScore = r .FraudScore
514+ cachedNode .QualityStatus = r .QualityStatus
515+ cachedNode .QualityFamily = r .QualityFamily
478516 nodeCache .Set (r .NodeID , cachedNode )
479517 }
480518 }
@@ -494,6 +532,8 @@ func fallbackToIndividualSpeedUpdate(chunk []SpeedTestResult, skipSpeed bool) in
494532 "is_broadcast" : r .IsBroadcast ,
495533 "is_residential" : r .IsResidential ,
496534 "fraud_score" : r .FraudScore ,
535+ "quality_status" : r .QualityStatus ,
536+ "quality_family" : r .QualityFamily ,
497537 }
498538 if ! skipSpeed {
499539 updates ["speed" ] = r .Speed
@@ -524,6 +564,8 @@ func fallbackToIndividualSpeedUpdate(chunk []SpeedTestResult, skipSpeed bool) in
524564 cachedNode .IsBroadcast = r .IsBroadcast
525565 cachedNode .IsResidential = r .IsResidential
526566 cachedNode .FraudScore = r .FraudScore
567+ cachedNode .QualityStatus = r .QualityStatus
568+ cachedNode .QualityFamily = r .QualityFamily
527569 nodeCache .Set (r .NodeID , cachedNode )
528570 }
529571 }
@@ -705,10 +747,21 @@ type NodeFilter struct {
705747 MaxFraudScore int // 最大欺诈评分(0=不限制)
706748 ResidentialType string // 住宅属性过滤: residential/datacenter/untested
707749 IPType string // IP类型过滤: native/broadcast/untested
750+ QualityStatus string
708751}
709752
710753func hasNodeQualityData (n Node ) bool {
711- return n .FraudScore >= 0
754+ return n .QualityStatus == QualityStatusSuccess
755+ }
756+
757+ func getNodeQualityStatusValue (n Node ) string {
758+ if n .QualityStatus != "" {
759+ return n .QualityStatus
760+ }
761+ if n .FraudScore >= 0 {
762+ return QualityStatusSuccess
763+ }
764+ return QualityStatusUntested
712765}
713766
714767func getNodeResidentialTypeValue (n Node ) string {
@@ -761,6 +814,17 @@ func matchNodeIPType(n Node, ipType string) bool {
761814 }
762815}
763816
817+ func matchNodeQualityStatus (n Node , qualityStatus string ) bool {
818+ switch qualityStatus {
819+ case "" , "all" :
820+ return true
821+ case QualityStatusUntested , QualityStatusSuccess , QualityStatusPartial , QualityStatusFailed , QualityStatusDisabled :
822+ return getNodeQualityStatusValue (n ) == qualityStatus
823+ default :
824+ return true
825+ }
826+ }
827+
764828// ListWithFilters 根据过滤条件获取节点列表
765829func (node * Node ) ListWithFilters (filter NodeFilter ) ([]Node , error ) {
766830 // 预处理搜索关键词
@@ -877,11 +941,15 @@ func (node *Node) ListWithFilters(filter NodeFilter) ([]Node, error) {
877941
878942 // 最大欺诈评分过滤
879943 if filter .MaxFraudScore > 0 {
880- if n .FraudScore < 0 || n .FraudScore > filter .MaxFraudScore {
944+ if getNodeQualityStatusValue ( n ) != QualityStatusSuccess || n .FraudScore < 0 || n .FraudScore > filter .MaxFraudScore {
881945 return false
882946 }
883947 }
884948
949+ if ! matchNodeQualityStatus (n , filter .QualityStatus ) {
950+ return false
951+ }
952+
885953 // 住宅属性过滤
886954 if ! matchNodeResidentialType (n , filter .ResidentialType ) {
887955 return false
0 commit comments