package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

/*
CREATE TABLE `address` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `ip` char(15) NOT NULL DEFAULT '',
  `city` varchar(32) NOT NULL DEFAULT '',
  `region` varchar(32) NOT NULL DEFAULT '',
  `country` varchar(32) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/

// Address structure for table address
type Address struct {
	ID      int64  `db:"id"`
	IP      string `db:"ip"`
	City    string `db:"city"`
	Region  string `db:"region"`
	Country string `db:"country"`
}

// TableName for address
func (a *Address) TableName() string {
	return "address"
}

const (
	dbUser   = "root"
	dbPasswd = "123456"
	dbHost   = "localhost"
	dbPort   = 3306
	dbName   = "go_test"
)

var db *sqlx.DB

func init() {
	var err error
	db, err = sqlx.Connect("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4,utf8", dbUser, dbPasswd, dbHost, dbPort, dbName))
	if nil != err {
		log.Fatalln(err)
	}
}

func main() {
	testHTTPResp()
	processIP()
}

func processIP() {
	var totalCount int64

	err := db.Get(&totalCount, "SELECT COUNT(*) FROM address;")
	if nil != err {
		log.Fatalln(err)
		os.Exit(1)
	}

	var pageSize int64
	var offset int64

	pageSize = 500

	var wg sync.WaitGroup

	for offset = 0; offset <= totalCount; offset += pageSize {

		dataList := []Address{}
		sql := fmt.Sprintf("SELECT id, ip FROM address ORDER BY id ASC LIMIT %d, %d;", offset, pageSize)
		err = db.Select(&dataList, sql)
		if nil != err {
			log.Fatalln(err)
			os.Exit(1)
		}

		wg.Add(len(dataList))
		for _, item := range dataList {
			go func(ip string, id int64) {
				defer wg.Done()

				if ip != "" {
					var data HTTPResp
					err = sendHTTPRequest(ip, &data)
					if err == nil {
						sql := fmt.Sprintf("UPDATE address SET city = '%s', region = '%s', country = '%s' WHERE id = %d;", data.City, data.Region, data.CountryName, id)
						db.MustExec(sql)
					}
				}
			}(item.IP, item.ID)
		}
		wg.Wait()
	}
}

/*
https://ipapi.co/112.24.40.51/json/ 响应结构

{
    "ip": "112.24.40.51",
    "city": "Zhenjiang",
    "region": "Jiangsu",
    "region_code": "JS",
    "country": "CN",
    "country_name": "China",
    "continent_code": "AS",
    "in_eu": false,
    "postal": null,
    "latitude": 32.2109,
    "longitude": 119.4551,
    "timezone": "Asia/Shanghai",
    "utc_offset": "+0800",
    "country_calling_code": "+86",
    "currency": "CNY",
    "languages": "zh-CN,yue,wuu,dta,ug,za",
    "asn": "AS56046",
    "org": "China Mobile communications corporation"
}
*/

// HTTPResp structrue for response of ip query
type HTTPResp struct {
	City        string `json:"city"`
	Region      string `json:"region"`
	CountryName string `json:"country_name"`
}

func sendHTTPRequest(ip string, data *HTTPResp) error {
	req, _ := http.NewRequest("GET", fmt.Sprintf("https://ipapi.co/%s/json/", ip), nil)
	client := http.Client{
		Timeout: 10 * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return err
	}

	return nil
}

// 测试输出
func testHTTPResp() {
	var data HTTPResp
	err := sendHTTPRequest("112.24.40.51", &data)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("test\ncity: %s, region: %s, country_name: %s\n", data.City, data.Region, data.CountryName)
}
go build -o ./getIPDetail ./main.go
./getIPDetail
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐