first commit
This commit is contained in:
145
pkg/utils/http.go
Normal file
145
pkg/utils/http.go
Normal file
@ -0,0 +1,145 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 调用POST请求
|
||||
func HttpPost(url string, body string) (remoteResponse string, err error) {
|
||||
|
||||
bodyReader := strings.NewReader(body)
|
||||
//application/x-www-form-urlencoded
|
||||
//application/json
|
||||
_, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * 20, // 设置客户端超时为5秒
|
||||
}
|
||||
|
||||
response, err1 := client.Post(url, "application/x-www-form-urlencoded", bodyReader)
|
||||
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
resBody, err2 := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
}
|
||||
|
||||
remoteResponse = string(resBody)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 调用Get请求
|
||||
func HttpGet(url string) (remoteResponse string, err error) {
|
||||
|
||||
response, err1 := http.Get(url)
|
||||
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
resBody, err2 := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
}
|
||||
|
||||
remoteResponse = string(resBody)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 复杂http请求
|
||||
func HttpDo(httpMethod string, url string, headerMap map[string]string, rawBody string) (remoteResponse string, err error) {
|
||||
|
||||
_, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * 20, // 设置客户端超时为5秒
|
||||
}
|
||||
|
||||
req, err0 := http.NewRequest(httpMethod, url, strings.NewReader(rawBody))
|
||||
|
||||
if err0 != nil {
|
||||
|
||||
err = err0
|
||||
return
|
||||
}
|
||||
|
||||
if len(headerMap) > 0 {
|
||||
for k, v := range headerMap {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err1 := client.Do(req)
|
||||
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err2 := ioutil.ReadAll(resp.Body)
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
}
|
||||
|
||||
remoteResponse = string(body)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func CreateApiSign(params map[string]string, secret string) string {
|
||||
|
||||
keys := make([]string, 20)
|
||||
for key, _ := range params {
|
||||
if key == "Signature" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
//按key升序
|
||||
sort.Strings(keys)
|
||||
//把KEY值合并为字符串
|
||||
waitSignString := ""
|
||||
for _, value := range keys {
|
||||
waitSignString += base64.StdEncoding.EncodeToString([]byte(params[value]))
|
||||
}
|
||||
//加上密钥
|
||||
appSecret := secret
|
||||
waitSignString += appSecret
|
||||
|
||||
// log.InfoF("waitSignString: %s", waitSignString)
|
||||
//sha1加密
|
||||
t := sha1.New()
|
||||
io.WriteString(t, waitSignString)
|
||||
sign := t.Sum(nil)
|
||||
|
||||
return hex.EncodeToString(sign)
|
||||
|
||||
}
|
||||
88
pkg/utils/limit.go
Normal file
88
pkg/utils/limit.go
Normal file
@ -0,0 +1,88 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/anxpp/beego/context"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// Global Rate Limiter Configuration
|
||||
// 使用 rate.NewLimiter 创建一个全局限流器。
|
||||
// rate.Every(5*time.Second/500) 设置了令牌的生成速率,每 5 秒生成 500 个令牌,即每秒 100 个。500 是桶的容量,允许在短时间内处理 500 个突发请求。
|
||||
const (
|
||||
globalBurst = 500 // Example: A burst capacity of 500
|
||||
globalDuration = 5 * time.Second
|
||||
)
|
||||
|
||||
// User Rate Limiter Configuration
|
||||
// 使用 sync.Map 存储每个用户的限流器。sync.Map 是并发安全的,适合在多个 goroutine 中存取数据。getUserLimiter 函数负责获取或为新用户创建限流器。
|
||||
const (
|
||||
userBurst = 20 // Example: A burst capacity of 20
|
||||
userDuration = 3 * time.Second
|
||||
)
|
||||
|
||||
// Global Limiter
|
||||
var globalLimiter *rate.Limiter
|
||||
|
||||
func init() {
|
||||
// Initialize global rate limiter with a 5-second interval
|
||||
globalLimiter = rate.NewLimiter(rate.Every(globalDuration/time.Duration(globalBurst)), globalBurst)
|
||||
}
|
||||
|
||||
// User-specific limiters
|
||||
var (
|
||||
userLimiters sync.Map
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
// getUserLimiter retrieves or creates a user-specific rate limiter.
|
||||
func getUserLimiter(userID string) *rate.Limiter {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
limiter, ok := userLimiters.Load(userID)
|
||||
if !ok {
|
||||
// Create a new limiter for the user
|
||||
newLimiter := rate.NewLimiter(rate.Every(userDuration/time.Duration(userBurst)), userBurst)
|
||||
userLimiters.Store(userID, newLimiter)
|
||||
return newLimiter
|
||||
}
|
||||
|
||||
return limiter.(*rate.Limiter)
|
||||
}
|
||||
|
||||
// RateLimitFilter is a Beego filter for rate limiting.
|
||||
// 过滤器逻辑 (RateLimitFilter):
|
||||
// 首先调用 globalLimiter.Allow() 检查是否超过了全局限制。如果超出,直接返回 429 错误。
|
||||
// 然后,获取用户 ID。在实际应用中,你可能从 ctx.Input.Header、ctx.Input.Session 或其他地方获取。
|
||||
// 通过 getUserLimiter 获取该用户的限流器,并调用 userLimiter.Allow() 检查是否超过了用户限制。如果超出,返回 429 错误。
|
||||
func RateLimitFilter(ctx *context.Context) {
|
||||
// --- Global Rate Limiting ---
|
||||
if !globalLimiter.Allow() {
|
||||
ctx.ResponseWriter.WriteHeader(429) // 429 Too Many Requests
|
||||
// ctx.ResponseWriter.Write([]byte("429 Too Many Requests - Global Limit Exceeded"))
|
||||
ctx.ResponseWriter.Write([]byte("429 当前服务器请求过多"))
|
||||
return
|
||||
}
|
||||
|
||||
// --- Per-User Rate Limiting ---
|
||||
// In a real application, you would get the user ID from the session, JWT, or request header.
|
||||
// For this example, we'll use a hardcoded value or a placeholder.
|
||||
// You can replace this with your actual logic to get the user ID.
|
||||
userID := ctx.Input.Param("x-token") // Example: Get user ID from URL parameter
|
||||
if userID == "" {
|
||||
// Handle cases where the user ID is not available.
|
||||
userID = "anonymous"
|
||||
}
|
||||
|
||||
userLimiter := getUserLimiter(userID)
|
||||
if !userLimiter.Allow() {
|
||||
ctx.ResponseWriter.WriteHeader(429) // 429 Too Many Requests
|
||||
// ctx.ResponseWriter.Write([]byte(fmt.Sprintf("429 Too Many Requests - User %s Limit Exceeded", userID)))
|
||||
ctx.ResponseWriter.Write([]byte("429 当前用户请求过多"))
|
||||
return
|
||||
}
|
||||
}
|
||||
29
pkg/utils/p_tools.go
Normal file
29
pkg/utils/p_tools.go
Normal file
@ -0,0 +1,29 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// 获取ticket
|
||||
func GetJxApiTicket() (result string) {
|
||||
|
||||
ticket := viper.GetString("tuilan_ticket")
|
||||
result = ticket
|
||||
return
|
||||
}
|
||||
|
||||
// 是否在白名单
|
||||
func IsWhiteIp(ip string) (result bool) {
|
||||
|
||||
result = false
|
||||
|
||||
whiteList := []string{
|
||||
"120.53.99.84",
|
||||
"175.11.144.242",
|
||||
}
|
||||
|
||||
result = IsInStringList(ip, whiteList)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
37
pkg/utils/time_tool.go
Normal file
37
pkg/utils/time_tool.go
Normal file
@ -0,0 +1,37 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"github.com/anxpp/common-utils/str"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (t *Time) UnmarshalJSON(data []byte) (e error) {
|
||||
t.Time, e = time.ParseInLocation(str.TimeLayout, string(data), time.Local)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t Time) MarshalJSON() ([]byte, error) {
|
||||
return ([]byte)(fmt.Sprintf("\"%s\"", t.Time.Format(str.TimeLayout))), nil
|
||||
}
|
||||
func (t *Time) Scan(value interface{}) error {
|
||||
bytes, _ := value.([]byte)
|
||||
tt, _ := time.ParseInLocation(str.TimeLayout, string(bytes), time.Local)
|
||||
*t = Time{
|
||||
Time: tt,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t Time) Value() (driver.Value, error) {
|
||||
return t.Time.Format(str.TimeLayout), nil
|
||||
}
|
||||
|
||||
func (t Time) String() string {
|
||||
return str.TimeToString(t.Time)
|
||||
}
|
||||
356
pkg/utils/utils.go
Normal file
356
pkg/utils/utils.go
Normal file
@ -0,0 +1,356 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"servicebase/pkg/constant"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/anxpp/common-utils/str"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func FullPhotoUrl(path string) string {
|
||||
if len(path) == 0 {
|
||||
return ""
|
||||
}
|
||||
if strings.Index(path, "http") >= 0 {
|
||||
return path
|
||||
}
|
||||
return fmt.Sprintf("%s%s", constant.PhotoDomainUrl, path)
|
||||
}
|
||||
|
||||
// 从gin Context获取AppUserId
|
||||
func AppUserIdByHeader(c *gin.Context) (staffId string) {
|
||||
|
||||
inputToken := c.Request.Header.Get("X-Token")
|
||||
if len(inputToken) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
//从缓存获取
|
||||
// redisClient := cache.GetCommonRedisInstance().RedisClient
|
||||
// accessTokenKey := cache.ACCESSTOKEN_KEY_PREV + inputToken
|
||||
// staffId = redisClient.Get(cache.Ctx(), accessTokenKey).Val()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取客户端真实IP
|
||||
func GetClientRealIp(c *gin.Context) (ip string) {
|
||||
ip = c.ClientIP()
|
||||
xForwardedFor := c.Request.Header.Get("X-Forwarded-For")
|
||||
if xForwardedFor != "" {
|
||||
ip = xForwardedFor
|
||||
// 可能有多个IP,第一个是客户端的真实IP
|
||||
if i := strings.Index(xForwardedFor, ","); i >= 0 {
|
||||
ip = xForwardedFor[:i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 移出int数组重复项
|
||||
func RemoveDuplicatesInt(nums []int) []int {
|
||||
result := make([]int, 0, len(nums))
|
||||
temp := make(map[int]int, len(nums))
|
||||
for _, item := range nums {
|
||||
if _, ok := temp[item]; !ok {
|
||||
temp[item] = item
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 移出string数组重复项
|
||||
func RemoveDuplicatesString(nums []string) []string {
|
||||
result := make([]string, 0, len(nums))
|
||||
temp := make(map[string]string, len(nums))
|
||||
for _, item := range nums {
|
||||
if _, ok := temp[item]; !ok {
|
||||
temp[item] = item
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 判断数组是否包含某个数
|
||||
func IsInIntList(v int, list []int) bool {
|
||||
if len(list) == 0 {
|
||||
return false
|
||||
}
|
||||
isIn := false
|
||||
for _, item := range list {
|
||||
if v == item {
|
||||
isIn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isIn
|
||||
|
||||
}
|
||||
|
||||
// 判断数组是否包含某个字符串
|
||||
func IsInStringList(v string, list []string) bool {
|
||||
if len(list) == 0 {
|
||||
return false
|
||||
}
|
||||
isIn := false
|
||||
for _, item := range list {
|
||||
if v == item {
|
||||
isIn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isIn
|
||||
}
|
||||
|
||||
// 根据生日获得年龄
|
||||
func AgeByBirthday(birthday string) (age string) {
|
||||
age = ""
|
||||
if len(birthday) < 10 {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := time.Parse(str.DateLayout, birthday)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
birthdayYear := t.Year()
|
||||
nowYear := time.Now().Year()
|
||||
|
||||
ageInt := nowYear - birthdayYear
|
||||
|
||||
if ageInt < 0 {
|
||||
ageInt = 0
|
||||
}
|
||||
|
||||
age = strconv.Itoa(ageInt)
|
||||
return
|
||||
}
|
||||
|
||||
// 根据年龄获取生日
|
||||
func BirthdayByAge(age int) (birthday string) {
|
||||
birthday = ""
|
||||
if age < 0 || age > 100 {
|
||||
return
|
||||
}
|
||||
nowYear := time.Now().Year()
|
||||
birthYear := nowYear - age
|
||||
|
||||
birthday = strconv.Itoa(birthYear) + "-01-01"
|
||||
return
|
||||
}
|
||||
|
||||
// 获取日期是周几
|
||||
func GetWeekDayByDate(d string) string {
|
||||
|
||||
dayTime, err := time.Parse(str.DateLayout, d)
|
||||
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
week := int(dayTime.Weekday())
|
||||
return strconv.Itoa(week)
|
||||
}
|
||||
|
||||
// stringList转intList
|
||||
func StringListToIntList(stringList []string) (intList []int) {
|
||||
|
||||
if len(stringList) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range stringList {
|
||||
intList = append(intList, str.StringToInt(item))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// float32转string
|
||||
func Float32ToString(f float32, prec int) string {
|
||||
return strconv.FormatFloat(float64(f), 'f', prec, 32)
|
||||
}
|
||||
|
||||
// float64转string
|
||||
func Float64ToString(f float64, prec int) string {
|
||||
return strconv.FormatFloat(f, 'f', prec, 64)
|
||||
}
|
||||
|
||||
func Decimal(num float64) float64 {
|
||||
num, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", num), 64)
|
||||
return num
|
||||
}
|
||||
|
||||
// 获取周几的名字
|
||||
func GetWeekNameByIndex(weekIndex int) (weekName string) {
|
||||
if weekIndex < 0 || weekIndex > 6 {
|
||||
return
|
||||
}
|
||||
names := []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"}
|
||||
return names[weekIndex]
|
||||
}
|
||||
|
||||
// 获取周几的名字
|
||||
func GetWeekNameListByIndexList(weekIndexList string) (weekName string) {
|
||||
if len(weekIndexList) == 0 {
|
||||
return
|
||||
}
|
||||
var indexList []string
|
||||
_ = json.Unmarshal([]byte(weekIndexList), &indexList)
|
||||
|
||||
if len(indexList) == 0 {
|
||||
return
|
||||
}
|
||||
var nameList []string
|
||||
for _, item := range indexList {
|
||||
name := GetWeekNameByIndex(str.StringToInt(item))
|
||||
nameList = append(nameList, name)
|
||||
}
|
||||
weekName = strings.Join(nameList, ",")
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// 获取某时间的当月第一天
|
||||
func GetFirstDateOfMonth(d time.Time) time.Time {
|
||||
d = d.AddDate(0, 0, -d.Day()+1)
|
||||
return GetZeroTime(d)
|
||||
}
|
||||
|
||||
// 获取某时间的当月最后一天
|
||||
func GetLastDateOfMonth(d time.Time) time.Time {
|
||||
return GetFirstDateOfMonth(d).AddDate(0, 1, -1)
|
||||
}
|
||||
|
||||
// 获取某一天的0点时间
|
||||
func GetZeroTime(d time.Time) time.Time {
|
||||
return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location())
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
获取本周周一的日期
|
||||
*/
|
||||
func GetCurrentWeekFirstDate() (weekMonday string) {
|
||||
now := time.Now()
|
||||
|
||||
offset := int(time.Monday - now.Weekday())
|
||||
if offset > 0 {
|
||||
offset = -6
|
||||
}
|
||||
|
||||
weekStartDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset)
|
||||
weekMonday = weekStartDate.Format("2006-01-02")
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
日期加天数
|
||||
*/
|
||||
func DateAddDays(dateStr string, days int) (resultDate string) {
|
||||
dateTime, _ := time.Parse(str.DateLayout, dateStr)
|
||||
weekStartDate := dateTime.AddDate(0, 0, days)
|
||||
resultDate = str.DateToString(weekStartDate)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
获取上周的周一日期
|
||||
*/
|
||||
func GetLastWeekFirstDate() (resultDate string) {
|
||||
thisWeekMonday := GetCurrentWeekFirstDate()
|
||||
TimeMonday, _ := time.Parse(str.DateLayout, thisWeekMonday)
|
||||
lastWeekMonday := TimeMonday.AddDate(0, 0, -7)
|
||||
resultDate = lastWeekMonday.Format("2006-01-02")
|
||||
return
|
||||
}
|
||||
|
||||
// SHA1 加密
|
||||
func Sha1Str(str string) (result string) {
|
||||
t := sha1.New()
|
||||
io.WriteString(t, str)
|
||||
|
||||
result = fmt.Sprintf("%x", t.Sum(nil))
|
||||
return
|
||||
}
|
||||
|
||||
func GetAgeFromIDCard(idCard string) (int, error) {
|
||||
// 验证身份证号码长度
|
||||
if len(idCard) != 18 {
|
||||
return 0, fmt.Errorf("身份证号码必须是18位")
|
||||
}
|
||||
|
||||
// 提取出生日期部分 (第7-14位)
|
||||
birthStr := idCard[6:14]
|
||||
|
||||
// 解析出生日期
|
||||
birthDate, err := time.Parse("20060102", birthStr)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("解析出生日期失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取当前日期
|
||||
now := time.Now()
|
||||
|
||||
// 计算年龄
|
||||
age := now.Year() - birthDate.Year()
|
||||
|
||||
// 考虑月份和日期,如果还没过生日则减1岁
|
||||
birthDay := time.Date(now.Year(), birthDate.Month(), birthDate.Day(), 0, 0, 0, 0, now.Location())
|
||||
if now.Before(birthDay) {
|
||||
age--
|
||||
}
|
||||
|
||||
return age, nil
|
||||
}
|
||||
|
||||
// IDCardInfo 存储从身份证提取的信息
|
||||
type IDCardInfo struct {
|
||||
BirthYear int // 出生年份
|
||||
BirthMonth int // 出生月份
|
||||
BirthDay int // 出生日
|
||||
Gender string // 性别:"男" 或 "女"
|
||||
}
|
||||
|
||||
// ParseIDCard 从身份证号码提取出生日期和性别
|
||||
func ParseIDCard(idCard string) (birthday, gender string) {
|
||||
// 验证身份证号码长度
|
||||
if len(idCard) != 18 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// 提取出生日期部分 (第7-14位)
|
||||
birthStr := idCard[6:14]
|
||||
birthDate, err := time.Parse("20060102", birthStr)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
birthday = str.DateToString(birthDate)
|
||||
|
||||
// 提取性别标识 (第17位)
|
||||
genderChar := idCard[16:17]
|
||||
genderNum, err := strconv.Atoi(genderChar)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
gender = "0"
|
||||
if genderNum%2 == 1 {
|
||||
gender = "1"
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user