Files
common/utils/utils.go

312 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"context"
"encoding/json"
"fmt"
"net"
"reflect"
"sort"
"strconv"
"strings"
"sync/atomic"
"time"
"gitee.com/red-future---jilin-g/common/beans"
"github.com/gogf/gf/v2/database/gredis"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/tiger1103/gfast-token/gftoken"
)
// ValidStructPtr 验证是否为结构体指针
func ValidStructPtr(req any) (err error) {
//验证请求参数必须为指针
var (
reflectValue reflect.Value
reflectKind reflect.Kind
)
if v, ok := req.(reflect.Value); ok {
reflectValue = v
} else {
reflectValue = reflect.ValueOf(req)
}
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Ptr {
err = gerror.NewCode(gcode.CodeInvalidParameter, `the parameter "req" for function Find should type of *struct/*[]struct`)
}
return
}
// GetMonthToday 获取N个月前的某日
func GetMonthToday(t time.Time, month int) time.Time {
// today
fmt.Printf("today: [%s]\n", t)
// 判断天数范围 小于等于28天的计算,覆盖大多数情况
if t.Day() <= 28 {
return t.AddDate(0, -month, 0)
}
// 月份的天数数组
monthDay := [13]int{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
// 计算目标所在日期
target := t.AddDate(0, 0, 1-t.Day()).AddDate(0, -month, 0)
// 计算当月最大天数
targetDay := monthDay[target.Month()]
// 计算闰年
if target.Month() == time.February && (target.Year()%400 == 0 || (target.Year()%100 != 0 && target.Year()%4 == 0)) {
targetDay++
}
if t.Day() > targetDay {
return target.AddDate(0, 0, targetDay-1)
}
return target.AddDate(0, 0, t.Day()-1)
}
func GetUserInfo(ctx context.Context) (user beans.User, err error) {
r := g.RequestFromCtx(ctx)
if r != nil {
redisAddr := g.Cfg().MustGet(ctx, "redis.default.address").String()
gft := gftoken.NewGfToken(
gftoken.WithCacheKey("gfToken:"),
gftoken.WithTimeout(20),
gftoken.WithMaxRefresh(10),
gftoken.WithMultiLogin(true),
//gftoken.WithExcludePaths(g.SliceStr{"/excludeDemo"}),
gftoken.WithGRedisConfig(&gredis.Config{
Address: redisAddr,
Db: 1,
}))
// 解析 token
data, err := gft.ParseToken(g.RequestFromCtx(ctx))
if err != nil {
return user, gerror.Wrap(err, "token 解析失败")
}
// 检查 data 是否为 nil
if data == nil {
return user, gerror.New("token 数据为空")
}
// 检查 data.Data 是否为 nil
if data.Data == nil {
return user, gerror.New("用户信息为空")
}
dataMap := gconv.Map(data.Data)
user.UserName = dataMap["userName"]
user.TenantId = dataMap["tenantId"]
} else {
user.TenantId = ctx.Value("tenantId")
user.UserName = ctx.Value("userName")
}
if user.TenantId == nil {
return user, gerror.New("租户信息为空")
}
return
}
func SetValue(ctx context.Context, result any, key string, value any) {
// 检查context是否已取消
select {
case <-ctx.Done():
return
default:
// 使用反射设置result的Total属性
resultValue := reflect.ValueOf(result)
if resultValue.Kind() == reflect.Ptr {
resultValue = resultValue.Elem()
totalField := resultValue.FieldByName(key)
if totalField.IsValid() && totalField.CanSet() {
totalField.Set(reflect.ValueOf(value))
}
}
return
}
}
func Struts(ctx context.Context, pointer any, mapping ...map[string]string) {
// 检查context是否已取消
select {
case <-ctx.Done():
return
default:
return
}
}
func OrderMap(m map[string]interface{}) map[string]interface{} {
// 提取所有key
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
// 使用标准排序算法对key进行排序
// 使用strings.Sort确保排序结果永远一致
sort.Strings(keys)
// 创建有序map
orderedMap := make(map[string]interface{}, len(m))
for _, k := range keys {
orderedMap[k] = m[k]
}
return orderedMap
}
// ParseIntSlice 解析整数切片 - 通用字符串处理工具
func ParseIntSlice(str string) []int {
parts := strings.Split(str, ",")
result := make([]int, 0, len(parts))
for _, part := range parts {
if val, err := strconv.Atoi(strings.TrimSpace(part)); err == nil {
result = append(result, val)
}
}
return result
}
// ParseStrings 解析字符串切片 - 通用字符串处理工具
func ParseStrings(str string) []string {
if str == "" {
return nil
}
parts := strings.Split(str, ",")
result := make([]string, 0, len(parts))
for _, part := range parts {
if trimmed := strings.TrimSpace(part); trimmed != "" {
result = append(result, trimmed)
}
}
return result
}
// FilterServiceNames 过滤服务名 - 通用映射处理工具
func FilterServiceNames(services map[string]interface{}, excludeKeys ...string) []string {
excludeMap := make(map[string]bool)
for _, key := range excludeKeys {
excludeMap[key] = true
}
result := make([]string, 0, len(services))
for key := range services {
if !excludeMap[key] {
result = append(result, key)
}
}
return result
}
// FormatUnixTime 格式化Unix时间戳 - 通用时间处理工具
func FormatUnixTime(timestamp int64) string {
if timestamp <= 0 {
return ""
}
return time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
}
// ParseDurationWithDefault 解析持续时间,失败时使用默认值 - 通用时间处理工具
func ParseDurationWithDefault(ctx context.Context, durationStr, defaultStr, fieldName string) (time.Duration, string) {
durationParsed, err := time.ParseDuration(durationStr)
if err != nil {
// 这里不能直接使用g.Log()因为这是utils包没有直接的日志访问
// 调用方应该处理日志
// g.Log().Warningf(ctx, "解析%s失败: %s, 使用默认值 %s, error: %v", fieldName, durationStr, defaultStr, err)
durationParsed, _ = time.ParseDuration(defaultStr)
return durationParsed, defaultStr
}
return durationParsed, durationStr
}
// AtomicUpdateMin 原子更新最小值 - 通用数值处理工具
func AtomicUpdateMin(minValue *atomic.Int64, newValue int64) {
for {
currentMin := minValue.Load()
if newValue >= currentMin {
break
}
if minValue.CompareAndSwap(currentMin, newValue) {
break
}
}
}
// AtomicUpdateMax 原子更新最大值 - 通用数值处理工具
func AtomicUpdateMax(maxValue *atomic.Int64, newValue int64) {
for {
currentMax := maxValue.Load()
if newValue <= currentMax {
break
}
if maxValue.CompareAndSwap(currentMax, newValue) {
break
}
}
}
// ParseCIDRs 解析CIDR列表 - 通用网络处理工具
func ParseCIDRs(strs []string) ([]*net.IPNet, error) {
nets := make([]*net.IPNet, 0, len(strs))
for _, s := range strs {
if s == "*" {
if _, ipv4Net, err := net.ParseCIDR("0.0.0.0/0"); err == nil {
nets = append(nets, ipv4Net)
}
if _, ipv6Net, err := net.ParseCIDR("::/0"); err == nil {
nets = append(nets, ipv6Net)
}
continue
}
if _, ipNet, err := net.ParseCIDR(s); err == nil {
nets = append(nets, ipNet)
}
}
return nets, nil
}
// UrlDecode 简单的URL解码 - 通用编码解码工具
func UrlDecode(s string) string {
result := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
if s[i] == '%' && i+2 < len(s) {
if high := HexDigit(s[i+1]); high != 0xFF {
if low := HexDigit(s[i+2]); low != 0xFF {
result = append(result, (high<<4)|low)
i += 2
continue
}
}
}
result = append(result, s[i])
}
return string(result)
}
// HexDigit 十六进制字符转数字 - 通用编码解码工具
func HexDigit(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
default:
return 0xFF
}
}
func Struct(params any, pointer any) error {
b, err := json.Marshal(params)
if err != nil {
return err
}
err = json.Unmarshal(b, &pointer)
if err != nil {
return err
}
return nil
}