BuildLinks 为用户生成完整订阅链接列表,自动按命名分组编号。 同组(country+region+network+entry 相同)≥2 个 host 时追加 01/02… 编号。 excluded 为用户已排除的 host ID 集合(nil 表示不过滤)。
(accesses []users.UserInbound, ibStore inbounds.InboundStore, user users.User, excluded map[string]bool)
| 93 | // 同组(country+region+network+entry 相同)≥2 个 host 时追加 01/02… 编号。 |
| 94 | // excluded 为用户已排除的 host ID 集合(nil 表示不过滤)。 |
| 95 | func BuildLinks(accesses []users.UserInbound, ibStore inbounds.InboundStore, user users.User, excluded map[string]bool) []string { |
| 96 | var entries []hostEntry |
| 97 | seenInbound := make(map[string]bool) |
| 98 | for _, acc := range accesses { |
| 99 | if acc.InboundID == "" { |
| 100 | nodeIbs, err := ibStore.ListInboundsByNode(acc.NodeID) |
| 101 | if err != nil { |
| 102 | continue |
| 103 | } |
| 104 | for _, ib := range nodeIbs { |
| 105 | hosts, err := ibStore.ListHostsByInbound(ib.ID) |
| 106 | if err != nil { |
| 107 | continue |
| 108 | } |
| 109 | for _, h := range hosts { |
| 110 | if excluded[h.ID] { |
| 111 | continue |
| 112 | } |
| 113 | entries = append(entries, hostEntry{ib, h, acc}) |
| 114 | } |
| 115 | } |
| 116 | continue |
| 117 | } |
| 118 | // 同一个 inbound 可能同时有直接分配和组分配,只取第一条(凭据已复用,内容相同) |
| 119 | if seenInbound[acc.InboundID] { |
| 120 | continue |
| 121 | } |
| 122 | seenInbound[acc.InboundID] = true |
| 123 | ib, err := ibStore.GetInbound(acc.InboundID) |
| 124 | if err != nil { |
| 125 | continue |
| 126 | } |
| 127 | hosts, err := ibStore.ListHostsByInbound(ib.ID) |
| 128 | if err != nil { |
| 129 | continue |
| 130 | } |
| 131 | for _, h := range hosts { |
| 132 | if excluded[h.ID] { |
| 133 | continue |
| 134 | } |
| 135 | entries = append(entries, hostEntry{ib, h, acc}) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | // 按 country → region → network → entry 排序,同组内保持原顺序 |
| 140 | sort.SliceStable(entries, func(i, j int) bool { |
| 141 | a, b := entries[i].host, entries[j].host |
| 142 | if a.Country != b.Country { |
| 143 | return a.Country < b.Country |
| 144 | } |
| 145 | if a.Region != b.Region { |
| 146 | return a.Region < b.Region |
| 147 | } |
| 148 | if a.Network != b.Network { |
| 149 | return a.Network < b.Network |
| 150 | } |
| 151 | return a.Entry < b.Entry |
| 152 | }) |
no test coverage detected