first commit

This commit is contained in:
Yangtao
2025-11-18 17:48:20 +08:00
commit 6e56cab848
196 changed files with 65809 additions and 0 deletions

145
pkg/utils/http.go Normal file
View 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
View 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
View 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
View 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
View 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
}