IP接口
This commit is contained in:
parent
e952657166
commit
43f78fb688
11
api/network/model/ip_info.go
Normal file
11
api/network/model/ip_info.go
Normal file
@ -0,0 +1,11 @@
|
||||
package model
|
||||
|
||||
// IpInfo // IP信息
|
||||
type IpInfo struct {
|
||||
IP string `json:"ip"` // IP地址
|
||||
Country string `json:"country"` // 国家
|
||||
Region string `json:"region"` // 区域
|
||||
Province string `json:"province"` // 省份
|
||||
City string `json:"city"` // 城市
|
||||
ISP string `json:"ISP"` // ISP
|
||||
}
|
6
api/network/model/req/ip.go
Normal file
6
api/network/model/req/ip.go
Normal file
@ -0,0 +1,6 @@
|
||||
package req
|
||||
|
||||
// GetIpInfoRequest 获取IP信息请求
|
||||
type GetIpInfoRequest struct {
|
||||
IP string `json:"ip" validate:"required"` // IP 地址
|
||||
}
|
47
api/network/server/ip_api.go
Normal file
47
api/network/server/ip_api.go
Normal file
@ -0,0 +1,47 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"api/api/network/model"
|
||||
"api/api/network/model/req"
|
||||
"api/utils"
|
||||
"api/utils/r"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// IpApi ip接口
|
||||
type IpApi struct {
|
||||
}
|
||||
|
||||
// GetIpInfo 获取IP信息
|
||||
func (*IpApi) GetIpInfo(c *gin.Context) {
|
||||
data := utils.BindValidJson[req.GetIpInfoRequest](c)
|
||||
ipInfo, err := ipService.GetIpInfo(data.IP)
|
||||
if err != nil {
|
||||
r.Error(c)
|
||||
}
|
||||
r.SuccessData(c, ipInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIpInfoList 获取IP信息
|
||||
func (*IpApi) GetIpInfoList(c *gin.Context) {
|
||||
scanner := bufio.NewScanner(c.Request.Body)
|
||||
var ipInfoList []model.IpInfo
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
ipInfo, err := ipService.GetIpInfo(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if ipInfo.ISP == "谷歌" {
|
||||
ipInfoList = append(ipInfoList, ipInfo)
|
||||
fmt.Println(ipInfo.IP)
|
||||
}
|
||||
// 在这里你可以处理每一行的数据,执行你的业务逻辑
|
||||
}
|
||||
r.SuccessData(c, ipInfoList)
|
||||
return
|
||||
}
|
@ -4,4 +4,5 @@ import "api/api/network/service"
|
||||
|
||||
var (
|
||||
networkService = service.NetWorkService{}
|
||||
ipService = service.IpService{}
|
||||
)
|
||||
|
54
api/network/service/ip_service.go
Normal file
54
api/network/service/ip_service.go
Normal file
@ -0,0 +1,54 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"api/api/network/model"
|
||||
"fmt"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
searcher *xdb.Searcher
|
||||
)
|
||||
|
||||
type IpService struct{}
|
||||
|
||||
func init() {
|
||||
var dbPath = "config/google.xdb"
|
||||
// 1、从 dbPath 加载整个 xdb 到内存
|
||||
cBuff, err := xdb.LoadContentFromFile(dbPath)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to load content from `%s`: %s\n", dbPath, err)
|
||||
if err != nil {
|
||||
log.Panic("dbPath初始化失败: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 2、用全局的 cBuff 创建完全基于内存的查询对象。
|
||||
searcher, err = xdb.NewWithBuffer(cBuff)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create searcher with content: %s\n", err)
|
||||
if err != nil {
|
||||
log.Panic("searcher初始化失败: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetIpInfo 获取IP信息
|
||||
func (*IpService) GetIpInfo(ip string) (model.IpInfo, error) {
|
||||
region, err := searcher.SearchByStr(ip)
|
||||
if err != nil {
|
||||
return model.IpInfo{}, err
|
||||
}
|
||||
info := strings.Split(region, "|")
|
||||
var ipInfo = model.IpInfo{
|
||||
IP: ip,
|
||||
Country: info[0],
|
||||
Region: info[1],
|
||||
Province: info[2],
|
||||
City: info[3],
|
||||
ISP: info[4],
|
||||
}
|
||||
return ipInfo, nil
|
||||
}
|
BIN
config/google.xdb
Normal file
BIN
config/google.xdb
Normal file
Binary file not shown.
1
go.mod
1
go.mod
@ -25,6 +25,7 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20240510055607-89e20ab7b6c6 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -49,6 +49,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20240510055607-89e20ab7b6c6 h1:YeIGErDiB/fhmNsJy0cfjoT8XnRNT9hb19xZ4MvWQDU=
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20240510055607-89e20ab7b6c6/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
|
@ -18,6 +18,8 @@ func ServerRouter() http.Handler {
|
||||
base := r.Group("/api")
|
||||
{
|
||||
base.GET("/getHeaders", networkApi.GetHeaders)
|
||||
base.POST("/getIpInfo", ipApi.GetIpInfo)
|
||||
base.POST("/getIpInfoList", ipApi.GetIpInfoList)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -6,4 +6,5 @@ import (
|
||||
|
||||
var (
|
||||
networkApi server.NetWorkApi
|
||||
ipApi server.IpApi
|
||||
)
|
||||
|
134
utils/gin_context.go
Normal file
134
utils/gin_context.go
Normal file
@ -0,0 +1,134 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"api/utils/model/req"
|
||||
"api/utils/r"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 在内部 panic 会被 middleware 捕获到并返回错误信息
|
||||
|
||||
// Validate 参数合法性校验
|
||||
func Validate(c *gin.Context, data any) {
|
||||
validMsg := Validator.Validate(data)
|
||||
if validMsg != "" {
|
||||
r.ReturnJson(c, http.StatusOK, r.ERROR_INVALID_PARAM, validMsg, nil)
|
||||
panic(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind 绑定
|
||||
func Bind[T any](c *gin.Context) (data T) {
|
||||
if err := c.ShouldBind(&data); err != nil {
|
||||
// Logger.Error("Bind", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BindValid 绑定验证 + 合法性校验
|
||||
func BindValid[T any](c *gin.Context) (data T) {
|
||||
// Json 绑定
|
||||
if err := c.ShouldBind(&data); err != nil {
|
||||
// Logger.Error("BindValid", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
// 参数合法性校验
|
||||
Validate(c, &data)
|
||||
return data
|
||||
}
|
||||
|
||||
// BindJson Json 绑定
|
||||
func BindJson[T any](c *gin.Context) (data T) {
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
// Logger.Error("BindJson", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BindValidJson Json 绑定验证 + 合法性校验
|
||||
func BindValidJson[T any](c *gin.Context) (data T) {
|
||||
// Json 绑定
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
// Logger.Error("BindValidJson", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
// 参数合法性校验
|
||||
Validate(c, &data)
|
||||
return data
|
||||
}
|
||||
|
||||
// BindQuery Param 绑定
|
||||
func BindQuery[T any](c *gin.Context) (data T) {
|
||||
if err := c.ShouldBindQuery(&data); err != nil {
|
||||
// Logger.Error("BindQuery", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
|
||||
// TODO: 检查是否有 PageSize 或 PageQuery 字段,并处理其值
|
||||
// val := reflect.ValueOf(data)
|
||||
// pageSize := val.FieldByName("PageSize").Int()
|
||||
// fmt.Println("pageSize: ", pageSize)
|
||||
// val.FieldByName("PageSize").Elem().SetInt(12)
|
||||
return
|
||||
}
|
||||
|
||||
// BindPageQuery Param 分页绑定(处理了 PageSize 和 PageQuery)
|
||||
func BindPageQuery(c *gin.Context) (data req.PageQuery) {
|
||||
if err := c.ShouldBindQuery(&data); err != nil {
|
||||
// Logger.Error("BindQuery", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
// 检查分页参数
|
||||
CheckQueryPage(&data.PageSize, &data.PageNum)
|
||||
return
|
||||
}
|
||||
|
||||
// BindValidQuery Param 绑定验证 + 合法性校验
|
||||
func BindValidQuery[T any](c *gin.Context) (data T) {
|
||||
// Query 绑定
|
||||
if err := c.ShouldBindQuery(&data); err != nil {
|
||||
// Logger.Error("BindValidQuery", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
// 参数合法性校验
|
||||
Validate(c, &data)
|
||||
return data
|
||||
}
|
||||
|
||||
// CheckQueryPage 检查分页参数
|
||||
func CheckQueryPage(pageSize, pageNum *int) {
|
||||
switch {
|
||||
case *pageSize >= 100:
|
||||
*pageSize = 100
|
||||
case *pageSize <= 0:
|
||||
*pageSize = 10
|
||||
}
|
||||
if *pageNum <= 0 {
|
||||
*pageNum = 1
|
||||
}
|
||||
}
|
||||
|
||||
// GetFromContext 从 Gin Context 上获取值, 该值是 JWT middleware 解析 Token 后设置的
|
||||
// 如果该值不存在, 说明 Token 有问题
|
||||
func GetFromContext[T any](c *gin.Context, key string) T {
|
||||
val, exist := c.Get(key)
|
||||
if !exist {
|
||||
panic(r.ERROR_TOKEN_RUNTIME)
|
||||
}
|
||||
return val.(T)
|
||||
}
|
||||
|
||||
// GetIntParam 从 Context 获取 Int 类型 Param 参数
|
||||
func GetIntParam(c *gin.Context, key string) int {
|
||||
val, err := strconv.Atoi(c.Param(key))
|
||||
if err != nil {
|
||||
// Logger.Error("GetIntParam", zap.Error(err))
|
||||
panic(r.ERROR_REQUEST_PARAM)
|
||||
}
|
||||
return val
|
||||
}
|
25
utils/model/req/universal.go
Normal file
25
utils/model/req/universal.go
Normal file
@ -0,0 +1,25 @@
|
||||
package req
|
||||
|
||||
// KeywordQuery 关键字查询
|
||||
type KeywordQuery struct {
|
||||
Keyword string `form:"keyword"`
|
||||
}
|
||||
|
||||
// PageQuery 获取数据(需要分页)
|
||||
type PageQuery struct {
|
||||
PageSize int `form:"page_size"`
|
||||
PageNum int `form:"page_num"`
|
||||
Keyword string `form:"keyword"`
|
||||
}
|
||||
|
||||
// SoftDelete 软删除请求(批量)
|
||||
type SoftDelete struct {
|
||||
Ids []int `json:"ids"`
|
||||
IsDelete *int8 `json:"is_delete" validate:"required,min=0,max=1"` // 软删除到回收站, 没有的字段不使用
|
||||
}
|
||||
|
||||
// UpdateReview 修改审核(批量)
|
||||
type UpdateReview struct {
|
||||
Ids []int `json:"ids"`
|
||||
IsReview *int8 `json:"is_review" validate:"required,min=0,max=1"`
|
||||
}
|
@ -47,3 +47,13 @@ func SuccessData(c *gin.Context, data any) {
|
||||
func Success(c *gin.Context) {
|
||||
Send(c, http.StatusOK, OK, nil)
|
||||
}
|
||||
|
||||
// Error 失败
|
||||
func Error(c *gin.Context) {
|
||||
Send(c, http.StatusOK, FAIL, nil)
|
||||
}
|
||||
|
||||
// ErrorData 数据
|
||||
func ErrorData(c *gin.Context, data any) {
|
||||
Send(c, http.StatusOK, FAIL, data)
|
||||
}
|
||||
|
53
utils/validator.go
Normal file
53
utils/validator.go
Normal file
@ -0,0 +1,53 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/go-playground/locales/zh_Hans_CN"
|
||||
unTrans "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
zhTrans "github.com/go-playground/validator/v10/translations/zh"
|
||||
)
|
||||
|
||||
var Validator = new(ValidateUtil)
|
||||
|
||||
type ValidateUtil struct{}
|
||||
|
||||
var (
|
||||
uni *unTrans.UniversalTranslator
|
||||
validate *validator.Validate
|
||||
)
|
||||
|
||||
// Validate 返回验证错误信息, 为 "" 则无错误
|
||||
func (v *ValidateUtil) Validate(data any) string {
|
||||
// 验证对象
|
||||
validate = validator.New()
|
||||
// 翻译器
|
||||
trans := v.validateTransInit(validate)
|
||||
|
||||
err := validate.Struct(data)
|
||||
if err != nil {
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
return e.Translate(trans)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// validateTransInit 数据验证翻译器
|
||||
func (*ValidateUtil) validateTransInit(validate *validator.Validate) unTrans.Translator {
|
||||
// 万能翻译器,保存所有的语言环境和翻译数据
|
||||
uni = unTrans.New(zh_Hans_CN.New())
|
||||
// 翻译器
|
||||
trans, _ := uni.GetTranslator("zh_Hans_CN")
|
||||
// 验证器注册翻译器
|
||||
_ = zhTrans.RegisterDefaultTranslations(validate, trans)
|
||||
|
||||
// 读取 Tag 中的 label 标签为字段的翻译
|
||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
return field.Tag.Get("label")
|
||||
})
|
||||
|
||||
return trans
|
||||
}
|
Loading…
Reference in New Issue
Block a user