This commit is contained in:
zyj
2025-09-13 10:22:49 +08:00
commit 002419a7ed
28 changed files with 1888 additions and 0 deletions

94
utils/ai_request.go Normal file
View File

@@ -0,0 +1,94 @@
package utils
import (
"encoding/json"
"fmt"
"github.com/go-resty/resty/v2"
)
type AiReqeust struct{}
type DeepseekResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []struct {
Index int `json:"index"`
Message struct {
Role string `json:"role"`
Content string `json:"content"`
} `json:"message"`
Logprobs interface{} `json:"logprobs"` // 可能是null或复杂结构
FinishReason string `json:"finish_reason"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
PromptTokensDetails struct {
CachedTokens int `json:"cached_tokens"`
} `json:"prompt_tokens_details"`
PromptCacheHitTokens int `json:"prompt_cache_hit_tokens"`
PromptCacheMissTokens int `json:"prompt_cache_miss_tokens"`
} `json:"usage"`
SystemFingerprint string `json:"system_fingerprint"`
}
func (*AiReqeust) Deepseek(text string) string {
// 定义结构体
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
}
// 创建 resty 客户端
client := resty.New()
// 目标URL
url := "https://api.deepseek.com/v1/chat/completions"
request := ChatRequest{
Model: "deepseek-chat",
Messages: []Message{
{
Role: "user",
Content: text,
},
},
}
// 发送 POST 请求
resp, err := client.R().
SetHeader("Content-Type", "application/json"). // 必填头
SetHeader("Authorization", "Bearer sk-9ca769cb45c249559276b979a2e1a2cd"). // 认证头
SetBody(request). // 设置JSON体
Post(url)
if err != nil {
fmt.Println("请求失败:", err)
return ""
}
// 解析响应
var response DeepseekResponse
if err := json.Unmarshal(resp.Body(), &response); err != nil {
fmt.Println("解析失败:", err)
return ""
}
// 提取关键信息
if len(response.Choices) > 0 {
firstChoice := response.Choices[0]
// fmt.Printf("\nAI回复 (%s):\n%s\n",
// firstChoice.Message.Role,
// firstChoice.Message.Content)
return firstChoice.Message.Content
}
return ""
}

60
utils/db.go Normal file
View File

@@ -0,0 +1,60 @@
package utils
import (
"fmt"
"go-account-register/config"
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func Connect() (*gorm.DB, error) {
appConfig, dberr := config.LoadConfig()
if dberr != nil {
fmt.Println("读取配置失败")
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
appConfig.Database.Username,
appConfig.Database.Password,
appConfig.Database.Host,
appConfig.Database.Port,
appConfig.Database.Database)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("failed to get sql.DB: %w", err)
}
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
log.Println("Database connection established")
DB = db
return db, nil
}
func GetDB() *gorm.DB {
if DB == nil {
panic("database connection is not initialized")
}
return DB
}
func Close() error {
sqlDB, err := DB.DB()
if err != nil {
return err
}
return sqlDB.Close()
}

69
utils/external/index.go vendored Normal file
View File

@@ -0,0 +1,69 @@
package external
import (
"fmt"
"go-account-register/models"
"go-account-register/utils"
"time"
)
func GetIpInfo(ip string) models.Ip {
db := utils.GetDB()
var ipInfo models.Ip
if ip == "" {
var ipRequest utils.IpRequest
res, err := ipRequest.IpApi(ip)
if err != nil {
}
resIp := db.Model(&models.Ip{}).Where("query = ?", res.Query).First(&ipInfo)
if resIp.Error != nil {
createIp(res)
db.Model(&models.Ip{}).Where("query = ?", res.Query).First(&ipInfo)
return ipInfo
}
return ipInfo
}
resIp := db.Model(&models.Ip{}).Where("query = ?", ip).First(&ipInfo)
if resIp.Error != nil {
var ipRequest utils.IpRequest
res, err := ipRequest.IpApi(ip)
if err != nil {
}
createIp(res)
db.Model(&models.Ip{}).Where("query = ?", ip).First(&ipInfo)
return ipInfo
}
return ipInfo
}
func createIp(res utils.IPAPIResponse) {
db := utils.GetDB()
db.Model(&models.Ip{}).Create(&models.Ip{
As: res.As,
Asname: res.Asname,
City: res.City,
Continent: res.Continent,
ContinentCode: res.ContinentCode,
Country: res.Country,
CountryCode: res.CountryCode,
Currency: res.Currency,
District: res.District,
Hosting: utils.BoolToInt(res.Hosting),
Isp: res.Isp,
Lat: fmt.Sprint(res.Lat),
Lon: fmt.Sprint(res.Lon),
Mobile: utils.BoolToInt(res.Mobile),
Offset: fmt.Sprint(res.Offset),
Org: res.Org,
Proxy: utils.BoolToInt(res.Proxy),
Query: res.Query,
Region: res.Region,
RegionName: res.RegionName,
Timezone: res.Timezone,
Zip: res.Zip,
UpdateTime: time.Now().Unix(),
CreateTime: time.Now().Unix(),
})
}

75
utils/index.go Normal file
View File

@@ -0,0 +1,75 @@
package utils
import (
"math/rand"
"time"
)
type RandDateParams struct {
Year int
Month int
Day int
}
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const (
letterBits = 6 // 52个字母需6位2^6=64
letterMask = 1<<letterBits - 1 // 二进制掩码63个1
)
func BoolToInt(b bool) int64 {
if b {
return 1
}
return 0
}
func RandString(n int) string {
b := make([]byte, n)
for i := 0; i < n; {
if idx := int(rand.Int63() & letterMask); idx < len(letters) {
b[i] = letters[idx]
i++
} // 丢弃无效索引,重试
}
return string(b)
}
func RandDate(minAge, maxAge int) RandDateParams {
// 创建本地随机生成器(避免全局 rand.Seed 弃用问题)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// 计算年份范围(当前年-100岁 到 当前年-18岁
currentTime := time.Now()
currentYear := currentTime.Year()
minYear := currentYear - maxAge
maxYear := currentYear - minAge
// 生成随机年份
year := minYear + r.Intn(maxYear-minYear+1)
// 生成随机月份1-12
month := r.Intn(12) + 1
// 动态计算当月最大天数(考虑闰年)
daysInMonth := 31
switch month {
case 2: // 二月特殊处理
if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
daysInMonth = 29 // 闰年
} else {
daysInMonth = 28 // 平年
}
case 4, 6, 9, 11: // 30 天的月份
daysInMonth = 30
}
// 生成随机日期1 ~ daysInMonth
day := r.Intn(daysInMonth) + 1
return RandDateParams{
Year: year,
Month: month,
Day: day,
}
}

84
utils/ip_request.go Normal file
View File

@@ -0,0 +1,84 @@
package utils
import (
"fmt"
"log"
"github.com/go-resty/resty/v2"
)
type IpRequest struct {
}
type IPAPIResponse struct {
As string `json:"as"`
Asname string `json:"asname"`
City string `json:"city"`
Continent string `json:"continent"`
ContinentCode string `json:"continentCode"`
Country string `json:"country"`
CountryCode string `json:"countryCode"`
Currency string `json:"currency"`
District string `json:"district"`
Hosting bool `json:"hosting"`
Isp string `json:"isp"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
Mobile bool `json:"mobile"`
Offset float64 `json:"offset"`
Org string `json:"org"`
Proxy bool `json:"proxy"`
Query string `json:"query"`
Region string `json:"region"`
RegionName string `json:"regionName"`
Timezone string `json:"timezone"`
Zip string `json:"zip"`
}
func (*IpRequest) IpApi(targetIP string) (IPAPIResponse, error) {
// 创建Resty客户端
client := resty.New()
// 构建请求URL
// targetIP := "103.142.140.235"
var url string = "http://demo.ip-api.com/json?fields=66842623&lang=en"
if targetIP != "" {
url = fmt.Sprintf("http://demo.ip-api.com/json/%s?fields=66842623&lang=en", targetIP)
}
// 创建响应结构体实例
var result IPAPIResponse
// 发送GET请求不设置任何请求头
resp, err := client.R().
SetResult(&result). // 自动解析JSON到结构体
Get(url)
// 错误处理
if err != nil {
fmt.Printf("请求失败: %v\n", err)
return result, err
}
// 检查HTTP状态码
if resp.StatusCode() != 200 {
fmt.Printf("API返回错误状态码: %d\n", resp.StatusCode())
return result, err
}
return result, nil
}
func (*IpRequest) GetIPAddress() {
// 创建Resty客户端
client := resty.New()
var url string = "http://icanhazip.com/"
// 发送GET请求不设置任何请求头
resp, err := client.R().
Get(url)
// 错误处理
if err != nil {
fmt.Printf("请求失败: %v\n", err)
}
log.Println(resp)
}

42
utils/proxy_handle.go Normal file
View File

@@ -0,0 +1,42 @@
package utils
import (
"net/url"
"strings"
)
type ProxyHandle struct {
}
func (ProxyHandle) ParseProxy(proxyStr string) (protocol, username, password, ip, port string, err error) {
// 清理字符串中的多余字符(如末尾的 \n
proxyStr = strings.TrimSpace(proxyStr)
// 解析 URL
proxyURL, err := url.Parse(proxyStr)
if err != nil {
return "", "", "", "", "", err
}
// 提取协议http/https
protocol = proxyURL.Scheme
// 提取 IP 和端口
host := proxyURL.Host
if strings.Contains(host, ":") {
parts := strings.Split(host, ":")
ip = parts[0]
port = parts[1]
} else {
ip = host
port = "8080" // 默认端口
}
// 提取用户名和密码
if proxyURL.User != nil {
username = proxyURL.User.Username()
password, _ = proxyURL.User.Password()
}
return protocol, username, password, ip, port, nil
}

34
utils/redis.go Normal file
View File

@@ -0,0 +1,34 @@
package utils
import (
"context"
"fmt"
"log"
"github.com/redis/go-redis/v9"
)
// Redis 封装结构体
type Redis struct {
rdb *redis.Client
}
// 初始化Redis连接
func InitRedis() (*Redis, error) {
ctx := context.Background()
// 创建 Redis 客户端
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis 服务器地址
Password: "", // 密码,没有则为空
DB: 0, // 默认数据库
})
// 测试连接
if _, err := rdb.Ping(ctx).Result(); err != nil {
return nil, fmt.Errorf("redis连接失败: %w", err)
}
log.Println("✅ Redis连接成功")
return &Redis{rdb: rdb}, nil
}

View File

@@ -0,0 +1,85 @@
package utils
import (
"image"
"image/color"
"image/draw"
_ "image/jpeg"
"image/png"
"log"
"os"
"github.com/golang/freetype"
"golang.org/x/image/font"
)
type VerifyImagePlugin struct{}
func (*VerifyImagePlugin) DrawText(originalImage, newImage, text, fontPath string) (string, error) {
srcFile, err := os.Open(originalImage) // 替换为你的图片路径
if err != nil {
log.Fatal("图片打开失败:", err)
return "", err
}
defer srcFile.Close()
// 解码原始图片
img, _, err := image.Decode(srcFile)
if err != nil {
log.Fatal("图片解码失败只支持jpg、png:", err)
return "", err
}
rgba := image.NewRGBA(img.Bounds())
draw.Draw(rgba, rgba.Bounds(), img, image.Point{}, draw.Src)
var fontStr string = fontPath
if fontPath == "" {
fontStr = "fonts/AlibabaPuHuiTi-3-115-Black.ttf"
}
fontBytes, err := os.ReadFile(fontStr) // 替换为你的字体路径
if err != nil {
log.Fatal("字体加载失败:", err)
return "", err
}
fontObj, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Fatal("字体解析失败:", err)
return "", err
}
c := freetype.NewContext()
c.SetDPI(72) // 屏幕分辨率
c.SetFont(fontObj) // 设置中文字体
c.SetFontSize(40) // 字体大小(磅)
c.SetClip(rgba.Bounds()) // 绘制区域
c.SetDst(rgba) // 目标画布
c.SetSrc(image.NewUniform(color.RGBA{ // 文字颜色
R: 255,
G: 255,
B: 255,
A: 255,
}))
c.SetHinting(font.HintingFull) // 增强小字清晰度
x := 150 // 水平起始位置
y := 250 // 垂直起始位置
yBase := y + int(c.PointToFixed(40)>>6) // 关键:修正垂直偏移
pt := freetype.Pt(x, yBase)
if _, err := c.DrawString(text, pt); err != nil {
log.Fatal("文字绘制失败:", err)
}
outFile, err := os.Create(newImage)
if err != nil {
log.Fatal("输出文件创建失败:", err)
return "", err
}
defer outFile.Close()
if err := png.Encode(outFile, rgba); err != nil {
log.Fatal("图片保存失败:", err)
return "", err
}
log.Println("成功生成output.png")
return newImage, nil
}