This commit is contained in:
Hsy
2025-02-20 17:32:53 +08:00
commit 144c1788f7
57 changed files with 2827 additions and 0 deletions

View File

@ -0,0 +1,82 @@
package middleware
import (
"errors"
"net"
"net/http"
"strings"
)
// GetRealIP 提取客户端真实 IP 地址
func GetRealIP(r *http.Request) string {
headers := []string{"x-forwarded-for", "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "X-Real-IP"}
// 遍历头信息,找到第一个有效 IP
for _, header := range headers {
ip := extractIP(r.Header.Get(header))
if ip != "" {
return ip
}
}
// 获取 RemoteAddr如果经过代理则是代理 IP
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil && ip != "" {
return ip
}
// 检查是否是本地地址
if strings.HasPrefix(ip, "127.0.0.1") || strings.HasPrefix(ip, "[::1]") {
if externalIP, err := getExternalIP(); err == nil {
return externalIP.String()
}
}
return ""
}
// 提取 IP 地址并返回第一个非空部分
func extractIP(ips string) string {
if ips == "" || strings.EqualFold(ips, "unknown") {
return ""
}
// 返回第一个有效 IP
return strings.TrimSpace(strings.Split(ips, ",")[0])
}
// 获取非 127.0.0.1 的局域网 IP
func getExternalIP() (net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
ip := getIPFromAddr(addr)
if ip != nil {
return ip, nil
}
}
}
return nil, errors.New("not connected to the network")
}
func getIPFromAddr(addr net.Addr) net.IP {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip != nil && !ip.IsLoopback() {
return ip.To4()
}
return nil
}

72
pkg/util/172/172.go Normal file
View File

@ -0,0 +1,72 @@
package _72
import (
"encoding/json"
"fmt"
"hk/pkg/util/httpUtil"
"time"
)
const (
productsUrl = "https://haokaopenapi.lot-ml.com/api/order/GetProducts" // 商品列表查询Url
productsV2Url = "https://haokaopenapi.lot-ml.com/api/order/GetProductsV2" // 商品信息查询Url
pickNumberUrl = "https://haokaopenapi.lot-ml.com/api/order/PickNumber" // 商品选号Url
blackUserUrl = "https://haokaopenapi.lot-ml.com/api/black/BlackCheckCus" // 用户黑名单Url
blackAgentUrl = "https://haokaopenapi.lot-ml.com/api/black/BlackCheckAgent" // 代理黑名单Url
upFileUrl = "https://haokaopenapi.lot-ml.com/api/order/UpPicFile" // 证件照上传Url
addOrderUrl = "https://haokaopenapi.lot-ml.com/api/order/ApiToOrder" // 下单Url
orderInfoUrl = "https://haokaopenapi.lot-ml.com/api/order/GetOrderInfo" // 订单查询Url
)
type BaseParams struct {
Timestamp string `json:"Timestamp"` // Y 时间戳
UserID string `json:"user_id"` // Y 用户ID
UserSign string `json:"user_sign"` // Y 密钥
}
// GoodsReq 商品请求参数
type GoodsReq struct {
ProductID string `json:"ProductID"`
BaseParams
// Timestamp string `json:"Timestamp"` // Y 时间戳
// UserID string `json:"user_id"` // Y 用户ID
// UserSign string `json:"user_sign"` // Y 密钥
}
// ProductResp 定义产品信息的结构体
type ProductResp struct {
ProductID int64 `json:"productID"`
ProductName string `json:"productName"`
Flag string `json:"flag"` // 上架中
}
// GetProduct 获取上架中商品列表
func GetProduct(params GoodsReq) (ApiResponse[[]ProductResp], error) {
// 设置时间戳
timestamp := fmt.Sprintf("%d", time.Now().Unix()) // 当前时间戳长度为10位
params.Timestamp = timestamp
// 生成 MD5 签名
userSign := generateMD5SignV2(params, params.UserSign)
formParams := map[string]string{
"ProductID": params.ProductID,
"Timestamp": params.Timestamp,
"user_sign": userSign,
"user_id": params.UserID,
}
body, err := httpUtil.NewRequest().SendFormData(productsUrl, nil, formParams)
if err != nil {
return ApiResponse[[]ProductResp]{}, err
}
// 解析 JSON 响应
var apiResponse ApiResponse[[]ProductResp]
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil {
fmt.Println("解析响应体时发生错误:", err)
return ApiResponse[[]ProductResp]{}, err
}
return apiResponse, nil
}

198
pkg/util/172/172_test.go Normal file
View File

@ -0,0 +1,198 @@
package _72
import (
"fmt"
"testing"
"time"
)
func TestGetProduct(t *testing.T) {
key := "devttl"
secret := "2eed544990ff898cf4fe47ef90b1ce3a"
productID := ""
timestamp := fmt.Sprintf("%d", time.Now().Unix()) // 当前时间戳长度为10位
// 创建请求参数
params := GoodsReq{
ProductID: productID,
BaseParams: BaseParams{
Timestamp: timestamp,
UserID: key,
UserSign: secret,
},
}
// 发送 POST 请求
apiResponse, err := GetProduct(params)
if err != nil {
return
}
// 如果成功,处理 data 中的数据
if apiResponse.Code == 0 {
for index, item := range apiResponse.Data {
fmt.Printf("------------------%d\n", index)
fmt.Printf("产品ID: %v\n", item.ProductID)
fmt.Printf("产品名称: %v\n", item.ProductName)
fmt.Printf("产品状态: %v\n", item.Flag)
}
}
}
func TestGetProductV2(t *testing.T) {
key := "devttl"
secret := "2eed544990ff898cf4fe47ef90b1ce3a"
productID := "1128"
timestamp := fmt.Sprintf("%d", time.Now().Unix()) // 当前时间戳长度为10位
// 创建请求参数
params := GoodsReq{
ProductID: productID,
BaseParams: BaseParams{
Timestamp: timestamp,
UserID: key,
},
}
// 生成 MD5 签名
userSign := generateMD5SignV2(params, secret)
params.UserSign = userSign
fmt.Print(params)
// 发送 POST 请求
apiResponse, err := getProductV2(params)
if err != nil {
return
}
// 如果成功,处理 data 中的数据
if apiResponse.Code == 0 {
for index, item := range apiResponse.Data {
fmt.Printf("------------------%d\n", index)
fmt.Printf("产品ID: %v\n", item.ProductID)
fmt.Printf("产品名称: %v\n", item.ProductName)
fmt.Printf("产品主图: %v\n", item.MainPic)
fmt.Printf("发货地区: %v\n", item.Area)
fmt.Printf("禁发地区: %v\n", item.DisableArea)
fmt.Printf("详情图片: %v\n", item.LittlePicture)
fmt.Printf("详情链接: %v\n", item.NetAddr)
fmt.Printf("产品状态: %v\n", item.Flag)
fmt.Printf("是否选号: %v\n", item.NumberSel)
}
}
}
func TestGetPickNumber(t *testing.T) {
key := "devttl"
secret := "2eed544990ff898cf4fe47ef90b1ce3a"
productID := "1128"
timestamp := fmt.Sprintf("%d", time.Now().Unix()) // 当前时间戳长度为10位
// 创建请求参数
params := PickNumberReq{
ProductID: productID,
SearchCategory: "3",
BaseParams: BaseParams{
UserID: key,
Timestamp: timestamp,
},
}
// 生成 MD5 签名
userSign := generateMD5SignV2(params, secret)
params.UserSign = userSign
fmt.Println(params)
// 发送 POST 请求
apiResponse, err := GetPickNumber(params)
if err != nil {
return
}
// 如果成功,处理 data 中的数据
if apiResponse.Code == 0 {
for index, item := range apiResponse.Data {
fmt.Printf("------------------%d\n", index)
fmt.Printf("号码: %v\n", item.Number)
fmt.Printf("号码类型: %v\n", item.Type)
fmt.Printf("号码ID: %v\n", item.NumberId)
fmt.Printf("号码池ID: %v\n", item.NumberPoolId)
}
}
}
func TestCheckUserBlack(t *testing.T) {
key := "devttl"
secret := "2eed544990ff898cf4fe47ef90b1ce3a"
number := "15538654901"
numberType := "1"
timestamp := fmt.Sprintf("%d", time.Now().Unix()) // 当前时间戳长度为10位
// 创建请求参数
params := BlackReq{
Number: number,
Type: numberType,
Timestamp: timestamp,
UserID: key,
UserSign: secret,
}
// 发送 POST 请求
apiResponse, err := CheckBlackUser(params)
if err != nil {
return
}
// 如果成功,处理 data 中的数据
if apiResponse.Code == 0 {
fmt.Println("此用户是黑名单")
}
if apiResponse.Code == 1 {
fmt.Println("此用户不是黑名单")
}
if apiResponse.Code == -1 {
fmt.Println("请输入正确的手机号")
}
}
func TestCheckAgentBlack(t *testing.T) {
key := "devttl"
secret := "2eed544990ff898cf4fe47ef90b1ce3a"
number := "15538654901"
numberType := "1"
// 创建请求参数
params := BlackReq{
Number: number,
Type: numberType,
UserID: key,
}
// 生成 MD5 签名
userSign := generateMD5SignV2(params, secret)
params.UserSign = userSign
fmt.Println(params)
// 发送 POST 请求
apiResponse, err := CheckBlackAgent(params)
if err != nil {
return
}
// 如果成功,处理 data 中的数据
if apiResponse.Code == 0 {
fmt.Println("此用户是黑名单代理")
}
if apiResponse.Code == 1 {
fmt.Println("此用户不是黑名单代理")
}
if apiResponse.Code == -1 {
fmt.Println("请输入正确的代理手机号")
}
}

View File

@ -0,0 +1,70 @@
package _72
import (
"crypto/md5"
"encoding/hex"
"fmt"
"reflect"
"strings"
)
// ApiResponse 定义响应体的结构体
type ApiResponse[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Errs *string `json:"errs"`
Data T `json:"data"`
}
// 优化后的 generateMD5Sign 方法
func generateMD5SignV2(params interface{}, secret string) string {
// 获取结构体中的字段信息
var strBuilder strings.Builder
// 获取参数类型
v := reflect.ValueOf(params)
// 遍历结构体中的字段
for i := 0; i < v.NumField(); i++ {
// 获取字段名和值
field := v.Type().Field(i)
jsonTag := field.Tag.Get("json") // 获取 json 标签
value := v.Field(i)
// 如果字段是嵌套结构体,跳过
if value.Kind() == reflect.Struct {
continue
}
// 获取字段值
fieldValue := value.String()
// 排除 user_id 和空字符串字段
if field.Name != "UserID" && field.Name != "UserSign" {
// 拼接字段名和值
strBuilder.WriteString(fmt.Sprintf("%s=%s&", jsonTag, fieldValue))
}
}
// 使用反射获取字段并拼接
timestamp := v.FieldByName("Timestamp")
if timestamp.IsValid() {
timestampTag, _ := v.Type().FieldByName("Timestamp")
strBuilder.WriteString(fmt.Sprintf("%s=%s&", timestampTag.Tag.Get("json"), timestamp.String()))
}
userID := v.FieldByName("UserID")
if userID.IsValid() {
userIDTag, _ := v.Type().FieldByName("UserID")
strBuilder.WriteString(fmt.Sprintf("%s=%s", userIDTag.Tag.Get("json"), userID.String()))
}
// 拼接 secret 到最后
strBuilder.WriteString(secret)
// 获取拼接后的字符串
concatenatedStr := strBuilder.String()
fmt.Println(concatenatedStr)
// 计算 MD5 哈希
hash := md5.New()
hash.Write([]byte(concatenatedStr))
return hex.EncodeToString(hash.Sum(nil))
}

View File

@ -0,0 +1,69 @@
package _72
import (
"encoding/json"
"fmt"
"hk/pkg/util/httpUtil"
)
// BlackReq 黑名单请求
type BlackReq struct {
Number string `json:"number"` // Y 手机号或身份证号
Type string `json:"type"` // Y 查询类别 1手机号 2身份证号
Timestamp string `json:"Timestamp"` // Y 时间戳
UserID string `json:"username"` // Y 用户ID
UserSign string `json:"sign"` // Y 密钥
}
// CheckBlackUser 判断用户是否为黑名单用户
func CheckBlackUser(params BlackReq) (ApiResponse[any], error) {
formParams := map[string]string{
"number": params.Number,
"type": params.Type,
"Timestamp": params.Timestamp,
"username": params.UserID,
"sign": params.UserSign,
}
body, err := httpUtil.NewRequest().SendFormData(blackUserUrl, nil, formParams)
if err != nil {
return ApiResponse[any]{}, err
}
// 解析 JSON 响应
var apiResponse ApiResponse[any]
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil {
fmt.Println("解析响应体时发生错误:", err)
return ApiResponse[any]{}, err
}
return apiResponse, nil
}
// CheckBlackAgent 判断用户是否为黑名单代理
func CheckBlackAgent(params BlackReq) (ApiResponse[any], error) {
formParams := map[string]string{
"number": params.Number,
"type": params.Type,
"username": params.UserID,
"sign": params.UserSign,
}
body, err := httpUtil.NewRequest().SendFormData(blackAgentUrl, nil, formParams)
if err != nil {
return ApiResponse[any]{}, err
}
// 解析 JSON 响应
var apiResponse ApiResponse[any]
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil {
fmt.Println("解析响应体时发生错误:", err)
return ApiResponse[any]{}, err
}
return apiResponse, nil
}

50
pkg/util/172/goods_v2.go Normal file
View File

@ -0,0 +1,50 @@
package _72
import (
"encoding/json"
"fmt"
"hk/pkg/util/httpUtil"
"time"
)
const ()
// ProductV2Resp 定义商品套餐的结构
type ProductV2Resp struct {
ProductID int64 `json:"productID"`
ProductName string `json:"productName"`
MainPic string `json:"mainPic"`
Area string `json:"area"`
DisableArea string `json:"disableArea"`
LittlePicture string `json:"littlepicture"`
NetAddr string `json:"netAddr"`
Flag bool `json:"flag"`
NumberSel int `json:"numberSel"`
}
// GetProductV2 获取单个商品信息
func GetProductV2(params GoodsReq) (ApiResponse[[]ProductV2Resp], error) {
// 设置时间戳
timestamp := fmt.Sprintf("%d", time.Now().Unix()) // 当前时间戳长度为10位
params.Timestamp = timestamp
// 生成 MD5 签名
userSign := generateMD5SignV2(params, params.UserSign)
formParams := map[string]string{
"ProductID": params.ProductID,
"user_id": params.UserID,
"Timestamp": params.Timestamp,
"user_sign": userSign,
}
body, err := httpUtil.NewRequest().SendFormData(productsV2Url, nil, formParams)
// 解析 JSON 响应
var apiResponse ApiResponse[[]ProductV2Resp]
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil {
return ApiResponse[[]ProductV2Resp]{}, fmt.Errorf("解析响应体时发生错误: %v", err)
}
// 返回解析后的响应
return apiResponse, nil
}

View File

@ -0,0 +1,53 @@
package _72
import (
"encoding/json"
"fmt"
"hk/pkg/util/httpUtil"
)
const ()
type PickNumberReq struct {
City string `json:"city"` // 市
ProductID string `json:"prodID"` // Y 商品ID
Province string `json:"province"` // 省
SearchCategory string `json:"searchCategory"` // 查询分类 默认3
SearchValue string `json:"searchValue"` // 查询关键字2-4位
BaseParams
}
type PickNumberResp struct {
Type string `json:"type"` // 号码类型 普通
Number string `json:"number"` // 号码
NumberId string `json:"numberId"` // 号码ID
NumberPoolId string `json:"numberPoolId"` // 号码池ID
}
// GetPickNumber 商品选号
func GetPickNumber(params PickNumberReq) (ApiResponse[[]PickNumberResp], error) {
formParams := map[string]string{
"city": params.City,
"prodID": params.ProductID,
"province": params.Province,
"searchCategory": "3",
"searchValue": params.SearchValue,
"Timestamp": params.Timestamp,
"user_id": params.UserID,
"user_sign": params.UserSign,
}
body, err := httpUtil.NewRequest().SendFormData(pickNumberUrl, nil, formParams)
// 解析 JSON 响应
var apiResponse ApiResponse[[]PickNumberResp]
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil {
return ApiResponse[[]PickNumberResp]{}, fmt.Errorf("解析响应体时发生错误: %v", err)
}
// 返回解析后的响应
return apiResponse, nil
}

View File

@ -0,0 +1,140 @@
package httpUtil
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
// Request 封装的 POST 请求工具类
type Request struct {
client *http.Client
}
// NewRequest 创建一个新的 PostRequest 实例
func NewRequest() *Request {
return &Request{
client: &http.Client{
Timeout: 10 * time.Second, // 设置请求超时时间
},
}
}
// SendJSON 发送一个 JSON 格式的 POST 请求
func (p *Request) SendJSON(urlPath string, headers map[string]string, body interface{}) (string, error) {
// 将请求体转为 JSON
requestBody, err := json.Marshal(body)
if err != nil {
return "", fmt.Errorf("无法编码 JSON 请求体: %v", err)
}
// 创建 HTTP 请求
req, err := http.NewRequest("POST", urlPath, bytes.NewBuffer(requestBody))
if err != nil {
return "", fmt.Errorf("无法创建请求: %v", err)
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
for key, value := range headers {
req.Header.Set(key, value)
}
// 发送请求
resp, err := p.client.Do(req)
if err != nil {
return "", fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应内容
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应体失败: %v", err)
}
// 返回响应内容
return string(respBody), nil
}
// SendFormData 发送一个 form-data 格式的 POST 请求
func (p *Request) SendFormData(urlPath string, headers map[string]string, params map[string]string) (string, error) {
// 将参数拼接为表单数据
formData := url.Values{}
for key, value := range params {
formData.Set(key, value)
}
// 创建 HTTP 请求
req, err := http.NewRequest("POST", urlPath, strings.NewReader(formData.Encode()))
if err != nil {
return "", fmt.Errorf("无法创建请求: %v", err)
}
// 设置请求头
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for key, value := range headers {
req.Header.Set(key, value)
}
// 发送请求
resp, err := p.client.Do(req)
if err != nil {
return "", fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应内容
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应体失败: %v", err)
}
// 返回响应内容
return string(respBody), nil
}
// SendGET 发送一个 GET 请求
func (p *Request) SendGET(urlPath string, headers map[string]string, params map[string]string) (string, error) {
// 如果有查询参数,拼接到 URL 中
if len(params) > 0 {
urlValues := url.Values{}
for key, value := range params {
urlValues.Set(key, value)
}
urlPath = fmt.Sprintf("%s?%s", urlPath, urlValues.Encode())
}
// 创建 HTTP GET 请求
req, err := http.NewRequest("GET", urlPath, nil)
if err != nil {
return "", fmt.Errorf("无法创建请求: %v", err)
}
// 设置请求头
for key, value := range headers {
req.Header.Set(key, value)
}
// 发送请求
resp, err := p.client.Do(req)
if err != nil {
return "", fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应内容
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应体失败: %v", err)
}
// 返回响应内容
return string(respBody), nil
}

View File

@ -0,0 +1,43 @@
package httpUtil
import (
"fmt"
"testing"
)
func TestRequest(t *testing.T) {
// 创建 PostRequest 实例
postRequest := NewRequest()
// 示例:发送 JSON 请求
url := "https://example.com/api"
headers := map[string]string{
"Authorization": "Bearer your_token",
}
body := map[string]interface{}{
"key1": "value1",
"key2": "value2",
}
// 发送 JSON 请求
resp, err := postRequest.SendJSON(url, headers, body)
if err != nil {
fmt.Println("发送请求失败:", err)
return
}
fmt.Println("响应内容:", resp)
// 示例:发送 form-data 请求
formParams := map[string]string{
"user_id": "your_user_id",
"product_id": "12345",
}
resp2, err := postRequest.SendFormData(url, headers, formParams)
if err != nil {
fmt.Println("发送 form-data 请求失败:", err)
return
}
fmt.Println("响应内容:", resp2)
}

View File

@ -0,0 +1,58 @@
package strUtil
import (
"database/sql"
"regexp"
"strings"
)
// JoinStrings 字符串拼接
func JoinStrings(separator string, elems ...string) string {
return strings.Join(elems, separator)
}
// ValidString sql.NullString 转 string
func ValidString(sqlString sql.NullString) string {
if sqlString.Valid {
return sqlString.String
}
return ""
}
// StringToNullString string 转 sql.NullString
func StringToNullString(str string) sql.NullString {
return sql.NullString{
String: str,
Valid: str != "",
}
}
// ValidInt64 sql.NullInt64 转 int64
func ValidInt64(sqlInt sql.NullInt64) int64 {
if sqlInt.Valid {
return sqlInt.Int64
}
return 0
}
// Int64ToNullInt64 StringToNullString int64 转 sql.NullInt64
func Int64ToNullInt64(value int64) sql.NullInt64 {
return sql.NullInt64{
Int64: value,
Valid: value >= 0,
}
}
// ValidBool sql.NullBool 转 bool
func ValidBool(sqlBool sql.NullBool) bool {
if sqlBool.Valid {
return sqlBool.Bool
}
return false
}
// MatchRegexp 正则匹配
func MatchRegexp(pattern string, value string) bool {
r := regexp.MustCompile(pattern)
return r.MatchString(value)
}

151
pkg/util/tiktok/tiktok.go Normal file
View File

@ -0,0 +1,151 @@
package tiktok
import (
"encoding/json"
"fmt"
"hk/pkg/util/httpUtil"
"strings"
)
type tiktokResp struct {
Code int64 `json:"code"`
Data tiktokData `json:"data"`
}
type tiktokData struct {
ID string `json:"id"`
AwesomeID string `json:"aweme_id"`
Region string `json:"region"`
Title string `json:"title"`
Cover string `json:"cover"`
AiDynamicCover string `json:"ai_dynamic_cover"` // 动态封面
OriginCover string `json:"origin_cover"` // 静态封面
Duration int `json:"duration"`
Play string `json:"play"` // 无水印视频
Wmplay string `json:"wmplay"` // 有水印视频
Size int `json:"size"`
WmSize int `json:"wm_size"`
Music string `json:"music"`
MusicInfo musicInfo `json:"music_info"`
PlayCount int `json:"play_count"`
DiggCount int `json:"digg_count"`
CommentCount int `json:"comment_count"`
ShareCount int `json:"share_count"`
DownloadCount int `json:"download_count"`
CollectCount int `json:"collect_count"`
CreateTime int `json:"create_time"`
AnchorsExtras string `json:"anchors_extras"`
IsAd bool `json:"is_ad"`
CommercialVideoInfo string `json:"commercial_video_info"`
ItemCommentSettings int `json:"item_comment_settings"`
Author authorDTO `json:"author"`
}
type musicInfo struct {
ID string `json:"id"`
Title string `json:"title"`
Play string `json:"play"` // 背景音乐
Cover string `json:"cover"`
Author string `json:"author"`
Original bool `json:"original"`
Duration int `json:"duration"`
Album string `json:"album"`
}
type authorDTO struct {
ID string `json:"id"`
UniqueID string `json:"unique_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
}
type VideoInfoResponse struct {
AuthorID string `json:"authorId"` // 作者ID
AuthorUniqueID string `json:"authorUniqueId"` // 作者账号
AuthorAvatar string `json:"authorAvatar"` // 作者头像
AuthorNickname string `json:"authorNickname"` // 作者昵称
VideoID string `json:"videoId"` // 视频ID
VideoTitle string `json:"videoTitle"` // 作品描述
OriginCover string `json:"originCover"` // 静态封面
DynamicCover string `json:"dynamicCover"` // 动态封面
DestinationURL string `json:"destinationUrl"` // 无水印下载地址
WatermarkVideoURL string `json:"watermarkVideoUrl"` // 有水印下载地址
MusicURL string `json:"musicUrl"` // 背景音乐链接
OriginalURL string `json:"originalUrl"` // 原始下载链接
DownloaderURL string `json:"downloaderUrl"` // 解析后下载地址
CreateTime string `json:"createTime"` // 创建时间
Result bool `json:"result"` // 结果
}
func GetTiktokVideoInfo(videoUrl string) (*VideoInfoResponse, error) {
if len(videoUrl) <= 0 {
return nil, nil
}
// 解析视频地址
// 定义需要匹配的前缀
prefixes := []string{
"https://www.tiktok.com/t/",
"https://m.tiktok.com/v/",
"https://vm.tiktok.com/",
"https://vt.tiktok.com/",
"https://dltik.com/",
}
// 检查是否有匹配的前缀
for _, prefix := range prefixes {
if strings.HasPrefix(videoUrl, prefix) {
return nil, nil
}
}
// 获取视频信息
var url = "https://tiktok-download-without-watermark.p.rapidapi.com/analysis?url=" + videoUrl
headers := map[string]string{
"x-rapidapi-host": "tiktok-download-without-watermark.p.rapidapi.com",
"x-rapidapi-key": "4a5bbe21bfmshbd98e7404b74063p19ba22jsne87a37e3b675",
}
body, err := httpUtil.NewRequest().SendGET(url, headers, nil)
if err != nil || len(body) <= 0 {
return nil, err
}
var tiktokResp tiktokResp
err = json.Unmarshal([]byte(body), &tiktokResp)
if err != nil {
fmt.Println("解析响应体时发生错误:", err)
return nil, err
}
// 比对 statusCode 是否等于0
if tiktokResp.Code != 0 {
return nil, nil
}
var videoInfo = VideoInfoResponse{}
// 作者信息
videoInfo.AuthorID = tiktokResp.Data.Author.ID
videoInfo.AuthorAvatar = tiktokResp.Data.Author.Avatar
videoInfo.AuthorUniqueID = tiktokResp.Data.Author.UniqueID
videoInfo.AuthorNickname = tiktokResp.Data.Author.Nickname
videoInfo.VideoID = tiktokResp.Data.ID
videoInfo.VideoTitle = tiktokResp.Data.Title
videoInfo.OriginCover = tiktokResp.Data.OriginCover
videoInfo.DynamicCover = tiktokResp.Data.AiDynamicCover
videoInfo.DestinationURL = tiktokResp.Data.Play
videoInfo.WatermarkVideoURL = tiktokResp.Data.Wmplay
videoInfo.MusicURL = tiktokResp.Data.MusicInfo.Play
videoInfo.OriginalURL = videoUrl
videoInfo.DownloaderURL = videoUrl
//videoInfo.CreateTime = tiktokResp.Data.CreateTime
videoInfo.Result = true
return &videoInfo, nil
}

View File

@ -0,0 +1,7 @@
package tiktok
import "testing"
func TestTikTok(test *testing.T) {
GetTiktokVideoInfo("https://www.tiktok.com/@baotramthieu/video/6887554215598689538")
}