package util import ( "context" "net" "strings" "github.com/gogf/gf/v2/frame/g" ) // GetLocalIP 获取本机有效的局域网 IPv4 地址 func GetLocalIP() string { addrs, err := net.InterfaceAddrs() if err != nil { return "127.0.0.1" } var validIPs []string for _, addr := range addrs { ipnet, ok := addr.(*net.IPNet) if !ok { continue } ip := ipnet.IP if isIPValid(ip) { validIPs = append(validIPs, ip.String()) } } // 优先返回非 169.254.x.x 的 IP for _, ip := range validIPs { if !strings.HasPrefix(ip, "169.254.") { return ip } } // 其次返回 169.254.x.x(最后的选择) if len(validIPs) > 0 { return validIPs[0] } return "127.0.0.1" } // isIPValid 判断 IP 是否有效 func isIPValid(ip net.IP) bool { // 不是 loopback (127.0.0.1) if ip.IsLoopback() { return false } // 是 IPv4 if ip.To4() == nil { return false } // 不是链路本地地址 (169.254.0.0/16) if ip[0] == 169 && ip[1] == 254 { return false } // 不是组播地址 if ip.IsMulticast() { return false } // 不是未指定地址 (0.0.0.0) if ip.IsUnspecified() { return false } return true } // GetLocalAddress 获取局域网地址(IP:端口) func GetLocalAddress(ctx context.Context) string { ip := GetLocalIP() port := GetServerPort(ctx) if port == "80" || port == "443" { return ip } return ip + ":" + port } // GetSchemaFromRequest 从当前请求中获取协议(http/https) func GetSchemaFromRequest(ctx context.Context) string { r := g.RequestFromCtx(ctx) if r == nil { return "http" } // 1. 代理场景:X-Forwarded-Proto if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { return proto } // 2. 代理场景:X-Forwarded-Scheme if proto := r.Header.Get("X-Forwarded-Scheme"); proto != "" { return proto } // 3. TLS 连接(直接 HTTPS) if r.TLS != nil { return "https" } // 4. 默认 HTTP(这行很重要!) return "http" // ← 确保有这行 } // GetLocalBaseURL 获取局域网基础 URL(动态协议 + IP + 端口) func GetLocalBaseURL(ctx context.Context) string { schema := GetSchemaFromRequest(ctx) addr := GetLocalAddress(ctx) return schema + "://" + addr } // GetCallbackURL 获取回调地址(完整 URL) func GetCallbackURL(ctx context.Context, path string) string { baseURL := GetLocalBaseURL(ctx) // 确保 path 以 / 开头 if !strings.HasPrefix(path, "/") { path = "/" + path } return baseURL + path } // GetServerPort 从配置获取服务端口 func GetServerPort(ctx context.Context) string { address := g.Cfg().MustGet(ctx, "server.address", ":8080").String() // address 格式如 ":3009",去掉冒号 if strings.HasPrefix(address, ":") { return address[1:] } return "8080" }