package util import ( "crypto/md5" "encoding/json" "fmt" "io" "math" "math/rand" "reflect" "service/api/consts" "service/api/message" "service/library/logger" "sort" "strconv" "strings" "time" "unsafe" "github.com/olivere/elastic/v7" "github.com/qiniu/qmgo" ) func ToJson(v any) string { str, err := json.Marshal(v) if err != nil { logger.Info("%v marsha failed, err: %v", v, err) } return string(str) } func StringToMd5(s string) string { m := md5.New() _, _ = io.WriteString(m, s) return fmt.Sprintf("%x", m.Sum(nil)) } // 将实体类结构体指针转为bson.M,采用反射 func EntityToM(p any) qmgo.M { set := qmgo.M{} pType := reflect.TypeOf(p).Elem() pVal := reflect.ValueOf(p).Elem() for i := 0; i < pType.NumField(); i++ { name := pType.Field(i).Tag.Get("bson") if name != "" { field := pVal.Field(i) if name != "_id" && !field.IsNil() { value := field.Elem().Interface() set[name] = value } } } return set } func DeepEntityToM(p any) qmgo.M { set := qmgo.M{} pType := reflect.TypeOf(p).Elem() pVal := reflect.ValueOf(p).Elem() for i := 0; i < pType.NumField(); i++ { name := pType.Field(i).Tag.Get("bson") if name != "" { field := pVal.Field(i) if name != "_id" && !field.IsNil() { AddFieldToM(field, name, set) } } } return set } func AddFieldToM(field reflect.Value, parentNodeName string, set qmgo.M) { if IsTypeStructPtr(field.Type()) { p := field.Interface() pType := reflect.TypeOf(p).Elem() pVal := reflect.ValueOf(p).Elem() for i := 0; i < pType.NumField(); i++ { name := pType.Field(i).Tag.Get("bson") if name != "" { field := pVal.Field(i) if name != "_id" && !field.IsNil() { AddFieldToM(field, parentNodeName+"."+name, set) } } } } else { value := field.Elem().Interface() set[parentNodeName] = value } } func StringsContains(elems []string, v string) bool { for _, s := range elems { if s == v { return true } } return false } func Int64Contains(elems []int64, v int64) bool { for _, s := range elems { if s == v { return true } } return false } // 下划线转大写驼峰 func UderscoreToUpperCamelCase(s string) string { s = strings.Replace(s, "_", " ", -1) s = strings.Title(s) return strings.Replace(s, " ", "", -1) } // 数组转sql数组 []int{1, 2, 3) --> 1,2,3 func Convert2SqlArr(a ...any) string { return strings.Replace(strings.Trim(fmt.Sprint(a), "[]"), " ", ",", -1) } // 获取整点时间戳 func GetHourStartTimeStamp(t time.Time) int64 { loc, err := time.LoadLocation("Asia/Shanghai") if err != nil { loc = time.FixedZone("CST", 8*3600) } timeStr := fmt.Sprintf("%02d-%02d-%02d %02d:00:00", t.Year(), t.Month(), t.Day(), t.Hour()) duetimecst, err := time.ParseInLocation("2006-1-2 15:04:05", timeStr, loc) if err != nil { logger.Error("parse error : %v", err) } return duetimecst.Unix() } // 获取30分时间戳 func GetHourHalfTimeStamp(t time.Time) int64 { loc, err := time.LoadLocation("Asia/Shanghai") if err != nil { loc = time.FixedZone("CST", 8*3600) } timeStr := fmt.Sprintf("%02d-%02d-%02d %02d:30:00", t.Year(), t.Month(), t.Day(), t.Hour()) duetimecst, err := time.ParseInLocation("2006-1-2 15:04:05", timeStr, loc) if err != nil { logger.Error("parse error : %v", err) } return duetimecst.Unix() } // 获取整分时间戳 func GetMinuteStartTimeStamp(t time.Time) int64 { loc, err := time.LoadLocation("Asia/Shanghai") if err != nil { loc = time.FixedZone("CST", 8*3600) } timeStr := fmt.Sprintf("%02d-%02d-%02d %02d:%02d:00", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) duetimecst, err := time.ParseInLocation("2006-1-2 15:04:05", timeStr, loc) if err != nil { logger.Error("parse error : %v", err) } return duetimecst.Unix() } // 获取今天0点 func GetTodayZeroTime() time.Time { currentTime := time.Now() zeroTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 0, 0, 0, 0, currentTime.Location()) return zeroTime } // 获取0点时间戳 func GetDayStartTimeStamp(t time.Time) int64 { loc, err := time.LoadLocation("Asia/Shanghai") if err != nil { loc = time.FixedZone("CST", 8*3600) } timeStr := fmt.Sprintf("%02d-%02d-%02d 00:00:00", t.Year(), t.Month(), t.Day()) duetimecst, err := time.ParseInLocation("2006-1-2 15:04:05", timeStr, loc) if err != nil { logger.Error("parse error : %v", err) } return duetimecst.Unix() } // 获取当日该时间时间戳 func GetTimestampAtThisMomentForTime(t time.Time, layout string) int64 { loc, err := time.LoadLocation("Asia/Shanghai") if err != nil { loc = time.FixedZone("CST", 8*3600) } timeStr := fmt.Sprintf("%02d-%02d-%02d %s", t.Year(), t.Month(), t.Day(), layout) duetimecst, err := time.ParseInLocation("2006-1-2 15:04:05", timeStr, loc) if err != nil { logger.Error("parse error : %v", err) } return duetimecst.Unix() } // 随机生成字符串 func RandomString(l int) string { str := "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" bytes := []byte(str) var result []byte = make([]byte, 0, l) r := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < l; i++ { result = append(result, bytes[r.Intn(len(bytes))]) } return BytesToString(result) } // BytesToString 0 拷贝转换 slice byte 为 string func BytesToString(b []byte) (s string) { _bptr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) _sptr := (*reflect.StringHeader)(unsafe.Pointer(&s)) _sptr.Data = _bptr.Data _sptr.Len = _bptr.Len return s } func UnescapeJsonStr(s string) string { s = strings.ReplaceAll(s, "\\u003c", "<") s = strings.ReplaceAll(s, "\\u003e", ">") s = strings.ReplaceAll(s, "\\u0026", "&") return s } func AbsInt64(x int64) int64 { return int64(math.Abs(float64(x))) } func String2Int(s string) int { n, _ := strconv.Atoi(s) return n } func String2Int32(s string) int32 { return int32(String2Int(s)) } func String2Int64(s string) int64 { return int64(String2Int(s)) } func VerisonCompare(ver1 string, ver2 string) (bool, error) { ver1SeqNos := strings.Split(ver1, ".") ver2SeqNos := strings.Split(ver2, ".") if len(ver1SeqNos) != len(ver2SeqNos) { logger.Error("version format error") return false, fmt.Errorf("version format error") } for i := range ver1SeqNos { ver1SeqNo, err := strconv.Atoi(ver1SeqNos[i]) if err != nil { logger.Error("ver1 version format error:%v", err) return false, err } ver2SeqNo, err := strconv.Atoi(ver2SeqNos[i]) if err != nil { logger.Error("ver2 version format error:%v", err) return false, err } if ver1SeqNo > ver2SeqNo { return true, nil } else if ver1SeqNo < ver2SeqNo { return false, nil } } return false, nil } func RoundUp(num float64) int64 { return int64(math.Ceil(num)) } func SortParam(argList []*message.JsonParamEntry) string { var argsortable message.JsonParamEntrySortable = argList sort.Sort(argsortable) args := make([]string, 0) for _, arg := range argsortable { args = append(args, fmt.Sprintf("%s=%v", arg.Name, arg.Value)) } return strings.Join(args, "&") } func TransferInt64SliceIntoValueCountMap(s []int64) map[int64]int { mp := make(map[int64]int) for _, integer := range s { mp[integer] = mp[integer] + 1 } return mp } func IsInt64SliceEqualAsSet(s1 []int64, s2 []int64) bool { if len(s1) != len(s2) { return false } mp1 := TransferInt64SliceIntoValueCountMap(s1) mp2 := TransferInt64SliceIntoValueCountMap(s2) for _, integer := range s1 { if mp1[integer] != mp2[integer] { return false } } return true } func InInt64Slice(slice []int64, item int64) bool { for _, s := range slice { if s == item { return true } } return false } func InInt32Slice(slice []int32, item int32) bool { for _, s := range slice { if s == item { return true } } return false } func InIntSlice(slice []int, item int) bool { for _, s := range slice { if s == item { return true } } return false } func InStrSlice(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false } func GetLastLessOrEqualForInt64(arr []int64, target int64) int { low, high := 0, len(arr)-1 result := -1 for low <= high { mid := (low + high) / 2 if arr[mid] <= target { result = mid // 记录当前找到的索引 low = mid + 1 // 继续在右半部分查找 } else { high = mid - 1 // 目标值在左半部分,缩小搜索范围 } } return result } func GetLastLessOrEqualForFloat64(arr []float64, target float64) int { low, high := 0, len(arr)-1 result := -1 for low <= high { mid := (low + high) / 2 if arr[mid] <= target { result = mid // 记录当前找到的索引 low = mid + 1 // 继续在右半部分查找 } else { high = mid - 1 // 目标值在左半部分,缩小搜索范围 } } return result } type Int64Filter struct { LowerBound *int64 `json:"lower_bound" bson:"lower_bound"` UpperBound *int64 `json:"upper_bound" bson:"upper_bound"` ScaleCoeff float64 `json:"scale_coeff" bson:"scale_coeff"` Decay float64 `json:"decay" bson:"decay"` Weight float64 `json:"weight" bson:"weight"` } func CreateEsInt64RangeQuery(name string, filter *Int64Filter) *elastic.RangeQuery { if filter == nil { return nil } query := elastic.NewRangeQuery(name) if filter.LowerBound != nil { query.Gte(DerefInt64(filter.LowerBound)) } if filter.UpperBound != nil { query.Lte(DerefInt64(filter.UpperBound)) } return query } func CreateGaussDecayFunction(name string, filter *Int64Filter, refFilter *Int64Filter) *elastic.GaussDecayFunction { if filter == nil { return nil } lb := DerefInt64(filter.LowerBound) ub := DerefInt64(filter.UpperBound) if filter.LowerBound == nil { lb = DerefInt64(refFilter.LowerBound) } if filter.UpperBound == nil { ub = DerefInt64(refFilter.UpperBound) } origin := float64(lb+ub) / 2 offset := float64(-lb+ub) / 2 scale := (origin - offset) * refFilter.ScaleCoeff if scale == 0 { scale = 1 } return elastic.NewGaussDecayFunction().FieldName(name).Origin(origin).Offset(offset).Scale(scale).Decay(refFilter.Decay).Weight(refFilter.Weight) } func CreateKeywordScriptScoreFunction(name string, value string, weight float64) *elastic.ScriptFunction { scriptStr := "doc['%s'].value == '%s' ? %f : %f" script := elastic.NewScript(fmt.Sprintf(scriptStr, name, value, 1.0, 0.0)) return elastic.NewScriptFunction(script).Weight(weight) } func TrimZeroWidthUnicode(str string) string { str = strings.Trim(str, "\xe2\x80\x8b") str = strings.Trim(str, "\xe2\x80\x8c") str = strings.Trim(str, "\xe2\x80\x8d") return str } func FormatTs(timestamp int64, format string) string { return time.Unix(timestamp, 0).Local().Format(format) } func FormatTsAsNotifT(timestamp int64) string { return FormatTs(timestamp, "2006年1月2日 15时04分") } func GetNotifScene(key string, option int64) int64 { return consts.AudNotifTempKeyMap[key][option] }