1 Star 3 Fork 0

gm/mingo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
helper.go 14.96 KB
一键复制 编辑 原始数据 按行查看 历史
gm 提交于 2025-07-12 01:04 +08:00 . 支持指针类型
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
package mingo
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"math"
"math/rand/v2"
"net"
"reflect"
"regexp"
"slices"
"sort"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
)
// 限制数字在指定范围
func Clamp[T Number](value T, min T, max T) T {
if value < min {
return min
} else if value > max {
return max
} else {
return value
}
}
// 随机数
func Random(min int, max int) int {
return rand.IntN(max-min) + min
}
// 随机字符串,不含特殊字符
func RandomString(length int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
dictLen := len(letters)
b := make([]rune, length)
for i := 0; i < length; i++ {
b[i] = letters[rand.IntN(dictLen)]
}
return string(b)
}
// 获取 slice 第一项
func First[T any](list []T) *T {
if len(list) == 0 {
return nil
} else {
return &list[0]
}
}
// 获取 slice 最后一项
func Last[T any](list []T) *T {
if len(list) == 0 {
return nil
} else {
return &list[0]
}
}
// 排序,直接修改原数组
// switch pos when compare return true
func Sort[T any](list []T, compare func(a *T, b *T) bool) {
sort.Slice(list, func(i int, j int) bool {
return compare(&list[i], &list[j])
})
}
// 获取列表中最小的一项,支持 struct slice
func Min[T any, V Number](list []T, getValue func(item *T) V) *T {
length := len(list)
if length == 0 {
return nil
} else {
tmp := list[0]
for i := 1; i < length; i++ {
if getValue(&list[i]) < getValue(&tmp) {
tmp = list[i]
}
}
return &tmp
}
}
// 获取列表中最大的一项,支持 struct slice
func Max[T any, V Number](list []T, getValue func(item *T) V) *T {
length := len(list)
if length == 0 {
return nil
} else {
tmp := list[0]
for i := 1; i < length; i++ {
if getValue(&list[i]) > getValue(&tmp) {
tmp = list[i]
}
}
return &tmp
}
}
// 查找 slice 某项
// @return 1:Item pointer,nil will be not find
// @return 2:item index,-1 will be not find
func Find[T any](list []T, match func(item *T) bool) (*T, int) {
for i := 0; i < len(list); i++ {
if match(&list[i]) {
return &list[i], i
}
}
return nil, -1
}
// 判断 slice 是否含有某项
func Includes[T any](list []T, match func(item *T) bool) bool {
ln := len(list)
for i := 0; i < ln; i++ {
if match(&list[i]) {
return true
}
}
return false
}
// 遍历 slice 并重新组织结构
func ForEach[T any, V any](list []T, getValue func(item *T) V) []V {
res := make([]V, len(list))
ln := len(list)
for index := 0; index < ln; index++ {
res[index] = getValue(&list[index])
}
return res
}
// 移除数组下标为 index 的项
func Remove[T any](list []T, index int) []T {
if index < 0 || index >= len(list) {
return list
}
res := make([]T, len(list)-1)
copy(res, list[:index])
copy(res[index:], list[index+1:])
return res
}
// 筛选 slice
func Filter[T any](list []T, match func(item *T) bool) []T {
res := make([]T, 0)
ln := len(list)
for index := 0; index < ln; index++ {
if match(&list[index]) {
res = append(res, list[index])
}
}
return res
}
// slice 转 map
func Objectify[T any, V any](list []T, getKey func(item *T) string, getValue func(item *T) V) map[string]V {
res := make(map[string]V)
ln := len(list)
for i := 0; i < ln; i++ {
res[getKey(&list[i])] = getValue(&list[i])
}
return res
}
// map 转 slice
func Listify[T, V any](obj map[string]T, toValue func(key string, value *T) V) []V {
res := make([]V, len(obj))
index := 0
for key, value := range obj {
res[index] = toValue(key, &value)
index++
}
return res
}
// sum slice 内某项数据
func Sum[T any, V Number](list []T, getValue func(item *T) V) V {
var res V
ln := len(list)
for i := 0; i < ln; i++ {
res += getValue(&list[i])
}
return res
}
// 向下取整到某位小数点
func Floor(value float64, len int) float64 {
pow := math.Pow10(len)
return math.Floor(value*pow) / pow
}
// 4舍5入到某位小数点
func Round(value float64, len int) float64 {
pow := math.Pow10(len)
return math.Round(value*pow) / pow
}
// 获取 value 内 block 的最大倍数
func Trunc[T Number](value T, block T) T {
quotient := value / block
return quotient * block
}
// 遍历 list 并折合成 initValue 类型
func Reduce[T any, V any](list []T, inter func(prev *V, cur *T, index int) *V, initValue *V) *V {
ln := len(list)
for i := 0; i < ln; i++ {
initValue = inter(initValue, &list[i], i)
}
return initValue
}
// 获取 slice 交集
func Intersection[T any](listA []T, listB []T, compare func(a *T, b *T) bool) []T {
res := make([]T, 0)
aln := len(listA)
bln := len(listB)
for i := 0; i < aln; i++ {
for j := 0; j < bln; j++ {
if compare(&listA[i], &listB[j]) {
res = append(res, listA[i])
}
}
}
return res
}
func Merge[T any](a1 []T, a2 []T) []T {
buff := make([]T, len(a1)+len(a2))
copy(buff, a1)
copy(buff[len(a1):], a2)
return buff
}
// 循环定时,暂时启动后不能停止
// @return cancel signal:cancel <- false
func SetInterval(duration time.Duration, cb func()) chan bool {
ticker := time.NewTicker(duration)
cancel := make(chan bool)
go func() {
for {
select {
case <-ticker.C:
cb()
case <-cancel:
return
}
}
}()
return cancel
}
func Join(list []string, separator string) string {
if len(list) == 0 {
return ""
}
res := ""
ln := len(list)
for i := 0; i < ln; i++ {
res += separator + list[i]
}
return res[len(separator):]
}
// object 排序后,转查询参数,不带?返回
func QueryString(params map[string]string) string {
list := Listify(params, func(key string, value *string) string {
if len(*value) > 0 {
return key + "=" + *value
} else {
return ""
}
})
sort.Slice(list, func(i, j int) bool {
return strings.Compare(list[i], list[j]) < 0
})
return Join(list, "&")
}
// get address of value
func Ptr[T any](val T) *T {
return &val
}
func Md5(buff []byte) string {
var val = md5.Sum(buff)
return hex.EncodeToString(val[:])
}
func Sha1(buff []byte) string {
var val = sha1.Sum(buff)
return hex.EncodeToString(val[:])
}
func TruncateDay(t time.Time) time.Time {
return t.Add(-time.Hour * time.Duration(t.Hour())).Truncate(time.Hour)
}
func GetLocalIp() (string, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, address := range addrs {
// 检查ip地址判断是否回环地址
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}
return "", errors.New("get ip fail")
}
type ValidateError struct {
Key string
Label string // 如果不提供,则会使用Key 名称
ErrMsg string // 错误信息
}
func (t ValidateError) Error() string {
return t.Label + t.ErrMsg
}
func (t *ValidateError) Is(target error) bool {
_, ok := target.(*ValidateError)
return ok
}
var requiredReg *regexp.Regexp = regexp.MustCompile("^required$")
var minReg *regexp.Regexp = regexp.MustCompile("^min=")
var maxReg *regexp.Regexp = regexp.MustCompile("^max=")
var lenReg *regexp.Regexp = regexp.MustCompile("^len=")
var inReg *regexp.Regexp = regexp.MustCompile("^in=")
func validateInt(expr string, value int64, bitSize int) error {
if len(expr) == 0 {
return nil
}
for _, ruleStr := range strings.Split(expr, ";") {
rule := []byte(ruleStr)
if requiredReg.Match(rule) {
if value == 0 {
return errors.New("不能为空")
}
} else if minReg.Match(rule) {
if min, err := strconv.ParseInt(ruleStr[4:], 10, bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if value < min {
return errors.New("不能小于" + ruleStr[4:])
}
} else if maxReg.Match(rule) {
if max, err := strconv.ParseInt(ruleStr[4:], 10, bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if value > max {
return errors.New("不能大于" + ruleStr[4:])
}
} else if inReg.Match(rule) {
items := strings.Split(ruleStr[3:], ",")
find := false
for _, item := range items {
if num, err := strconv.ParseInt(item, 10, bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if num == value {
find = true
break
}
}
if !find {
return errors.New("无效值")
}
} else {
return errors.New("unsupported rule")
}
}
return nil
}
func validateUInt(expr string, value uint64, bitSize int) error {
if len(expr) == 0 {
return nil
}
for _, ruleStr := range strings.Split(expr, ";") {
rule := []byte(ruleStr)
if requiredReg.Match(rule) {
if value == 0 {
return errors.New("不能为空")
}
} else if minReg.Match(rule) {
if min, err := strconv.ParseUint(ruleStr[4:], 10, bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if value < min {
return errors.New("不能小于" + ruleStr[4:])
}
} else if maxReg.Match(rule) {
if max, err := strconv.ParseUint(ruleStr[4:], 10, bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if value > max {
return errors.New("不能大于" + ruleStr[4:])
}
} else if inReg.Match(rule) {
items := strings.Split(ruleStr[3:], ",")
find := false
for _, item := range items {
if num, err := strconv.ParseUint(item, 10, bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if num == value {
find = true
break
}
}
if !find {
return errors.New("无效值")
}
} else {
return errors.New("unsupported rule")
}
}
return nil
}
func validateFloat(expr string, value float64, bitSize int) error {
if len(expr) == 0 {
return nil
}
for _, ruleStr := range strings.Split(expr, ";") {
rule := []byte(ruleStr)
if requiredReg.Match(rule) {
if value == 0 {
return errors.New("不能为空")
}
} else if minReg.Match(rule) {
if min, err := strconv.ParseFloat(ruleStr[4:], bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if value < min {
return errors.New("不能小于" + ruleStr[4:])
}
} else if maxReg.Match(rule) {
if max, err := strconv.ParseFloat(ruleStr[4:], bitSize); err != nil {
return errors.Wrap(err, "atoi")
} else if value > max {
return errors.New("不能大于" + ruleStr[4:])
}
} else if inReg.Match(rule) {
items := strings.Split(ruleStr[3:], ",")
find := false
var num float64
var err error
for _, item := range items {
if num, err = strconv.ParseFloat(item, bitSize); err != nil {
return errors.Wrap(err, "parsefloat")
} else if num == value {
find = true
break
}
}
if !find {
return errors.New("无效值")
}
} else {
return errors.New("unsupported rule")
}
}
return nil
}
func validateString(expr string, value string) error {
if len(expr) == 0 {
return nil
}
for _, ruleStr := range strings.Split(expr, ";") {
rule := []byte(ruleStr)
if requiredReg.Match(rule) {
if len(value) == 0 {
return errors.New("不能为空")
}
} else if minReg.Match(rule) {
if min, err := strconv.Atoi(ruleStr[4:]); err != nil {
return errors.Wrap(err, "atoi")
} else if len(value) < min {
return errors.New("长度不能小于" + ruleStr[4:])
}
} else if maxReg.Match(rule) {
if max, err := strconv.Atoi(ruleStr[4:]); err != nil {
return errors.Wrap(err, "atoi")
} else if len(value) > max {
return errors.New("长度不能大于" + ruleStr[4:])
}
} else if lenReg.Match(rule) {
if lenght, err := strconv.Atoi(ruleStr[4:]); err != nil {
return errors.Wrap(err, "atoi")
} else if len(value) != lenght {
return errors.New("长度应为" + ruleStr[4:])
}
} else if inReg.Match(rule) {
items := strings.Split(ruleStr[3:], ",")
if slices.Index(items, value) < 0 {
return errors.New("非有效值")
}
} else {
return errors.New("unsupported rule")
}
}
return nil
}
func validateSlice(expr string, items reflect.Value) error {
var err error
for i1 := range items.Len() {
item := items.Index(i1)
var itemKind reflect.Kind
var itemValue reflect.Value
var itemType reflect.Type
if item.Kind() == reflect.Pointer {
// 当为空指针时,只检查 required 属性
if item.IsNil() {
if strings.Contains(expr, "required") {
return errors.New("不能为空")
} else {
continue
}
}
itemValue = item.Elem()
itemKind = item.Elem().Kind()
itemType = itemValue.Type()
} else {
itemKind = item.Kind()
itemValue = item
itemType = itemValue.Type()
}
switch itemKind {
case reflect.String:
if err = validateString(expr, itemValue.String()); err != nil {
return err
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if err = validateInt(expr, itemValue.Int(), itemType.Bits()); err != nil {
return err
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if err = validateUInt(expr, itemValue.Uint(), itemType.Bits()); err != nil {
return err
}
case reflect.Float32, reflect.Float64:
if err = validateFloat(expr, itemValue.Float(), itemType.Bits()); err != nil {
return err
}
case reflect.Struct:
if child := Validate(itemValue.Addr().Interface()); child != nil {
return child
}
}
}
return nil
}
func Validate(data any) error {
var err error
res := ValidateError{}
if reflect.TypeOf(data).Kind() != reflect.Pointer {
res.Key = "Body"
res.ErrMsg = "data must be a pointer"
return &res
}
types := reflect.TypeOf(data).Elem()
values := reflect.ValueOf(data).Elem()
for i := 0; i < types.NumField(); i++ {
field := types.Field(i)
var value reflect.Value
var kind reflect.Kind
var type_ reflect.Type
res.Key = field.Name
res.Label = field.Tag.Get("label")
if len(res.Label) == 0 {
res.Label = field.Name
}
expr := field.Tag.Get("validate")
if field.Type.Kind() == reflect.Pointer {
// 当为空指针时,只检查 required 属性
if values.Field(i).IsNil() {
if strings.Contains(expr, "required") {
res.ErrMsg = "不能为空"
return res
} else {
continue
}
}
value = values.Field(i).Elem()
kind = value.Kind()
type_ = value.Type() // 如果是空指针,不能取类型todo
} else {
value = values.Field(i)
kind = value.Kind()
type_ = value.Type()
}
switch kind {
case reflect.String:
if err = validateString(expr, value.String()); err != nil {
res.ErrMsg = err.Error()
return &res
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if err = validateInt(expr, value.Int(), type_.Bits()); err != nil {
res.ErrMsg = err.Error()
return &res
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if err = validateUInt(expr, value.Uint(), type_.Bits()); err != nil {
res.ErrMsg = err.Error()
return &res
}
case reflect.Float32, reflect.Float64:
if err = validateFloat(expr, value.Float(), type_.Bits()); err != nil {
res.ErrMsg = err.Error()
return &res
}
case reflect.Struct:
if child := Validate(value.Addr().Interface()); child != nil {
return child
}
case reflect.Slice, reflect.Array:
if err = validateSlice(expr, value); err != nil {
res.ErrMsg = err.Error()
return &res
}
}
}
return nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/liangguoming/mingo.git
git@gitee.com:liangguoming/mingo.git
liangguoming
mingo
mingo
v0.13.2

搜索帮助