This commit is contained in:
2025-02-06 11:14:33 +08:00
commit 07847a2d9e
535 changed files with 65131 additions and 0 deletions

View File

@ -0,0 +1,35 @@
package biginthelper
import (
"math/big"
"regexp"
"strconv"
)
// ParseBigInt 将字符串转换为 big.Int
func ParseBigInt(value string) *big.Int {
bi := new(big.Int)
i, ok := bi.SetString(value, 10)
if !ok {
return bi
}
return i
}
// IntToHex convert int to hexadecimal representation
// int64转换为16进制字符串
func IntToHex(i int64) string {
return strconv.FormatInt(i, 16)
}
// BigToHex 将bigint转化为16进制带 0x 的字符串
func BigToHex(bigInt big.Int) string {
return "0x" + IntToHex(bigInt.Int64())
}
// CheckIsAddress Check is a eth Address
// 检查是否是以太坊地址 正则匹配
func CheckIsAddress(addr string) bool {
re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$")
return re.MatchString(addr)
}

View File

@ -0,0 +1,55 @@
package biginthelper
import (
"fmt"
"math/rand"
"strconv"
"strings"
"testing"
)
func Test_ParseBigInt(t *testing.T) {
fmt.Println(ParseBigInt(`231513`))
fmt.Println(ParseBigInt(`654wdf16`))
fmt.Println(ParseBigInt(`5455_1655`))
fmt.Println(ParseBigInt(``))
fmt.Println(ParseBigInt(`af`))
}
func Test_IntToHex(t *testing.T) {
fmt.Println(strings.TrimLeft(`01615`, "0"))
fmt.Println(strings.TrimLeft(`1615`, "0"))
fmt.Println(strings.TrimLeft(`0x1615`, "0"))
}
// i := int64(32)
// s := strconv.FormatInt(i, 16)
// println(s)
// 对比strconv.FormatInt(i, 16)和fmt.Sprintf("0x%x", i)的性能消耗
// go test -bench=_QE_ -benchmem
// -benchtime 默认为1秒 -benchmem 获得内存分配的统计数据
func Benchmark_QE_strconv(b *testing.B) {
for i := 0; i < b.N; i++ {
strconv.FormatInt(getInt64(), 16)
}
}
func Benchmark_QE_fmt(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf("0x%x", getInt64())
}
}
func getInt64() int64 {
return int64(rand.Intn(10000))
}
// 结果
// Benchmark_QE_strconv-6 47142570 24.29 ns/op 5 B/op 1 allocs/op
// Benchmark_QE_fmt-6 14787649 82.41 ns/op 8 B/op 1 allocs/op
// 改为随机数后
// Benchmark_QE_strconv-6 27890760 42.48 ns/op 3 B/op 0 allocs/op
// Benchmark_QE_fmt-6 10595380 108.6 ns/op 15 B/op 1 allocs/op
// 结论 尽量使用 strconv.FormatInt(i, 16) 进行16进制的转换

View File

@ -0,0 +1,119 @@
package biginthelper
import (
"bytes"
"fmt"
"math/big"
"strings"
)
// BigIntString BigIntString
func BigIntString(balance *big.Int, decimals int64) string {
amount := BigIntFloat(balance, decimals)
deci := fmt.Sprintf("%%0.%vf", decimals)
return clean(fmt.Sprintf(deci, amount))
}
// BigIntFloat BigIntFloat
func BigIntFloat(balance *big.Int, decimals int64) *big.Float {
if balance.Sign() == 0 {
return big.NewFloat(0)
}
bal := new(big.Float)
bal.SetInt(balance)
pow := bigPow(10, decimals)
p := big.NewFloat(0)
p.SetInt(pow)
bal.Quo(bal, p)
return bal
}
func bigPow(a, b int64) *big.Int {
r := big.NewInt(a)
return r.Exp(r, big.NewInt(b), nil)
}
func clean(newNum string) string {
stringBytes := bytes.TrimRight([]byte(newNum), "0")
newNum = string(stringBytes)
if stringBytes[len(stringBytes)-1] == 46 {
newNum += "0"
}
if stringBytes[0] == 46 {
newNum = "0" + newNum
}
return newNum
}
// GetActualHex 获取真实的十六进制..
func GetActualHex(h string) string {
h = strings.TrimLeft(h, "0")
var hex string
if strings.Index(h, "0x") == 0 {
hex = h[2:]
} else {
hex = h
}
if len(h)%2 != 0 {
hex = "0" + hex
}
return "0x" + hex
}
// HexToBig HexToBig
func HexToBig(h string) *big.Int {
i := big.NewInt(0)
h = strings.Replace(h, "0x", "", -1)
if h == "" {
return i
}
if _, ok := i.SetString(h, 16); !ok {
return nil
}
return i
}
// ConvertNumToFloat ConvertNumToFloat
func ConvertNumToFloat(num int64) float64 {
switch num {
case 1:
return 10.0
case 2:
return 100.0
case 3:
return 1000.0
case 4:
return 10000.0
case 5:
return 100000.0
case 6:
return 1000000.0
case 7:
return 10000000.0
case 8:
return 100000000.0
case 9:
return 1000000000.0
case 10:
return 10000000000.0
case 11:
return 100000000000.0
case 12:
return 1000000000000.0
case 13:
return 10000000000000.0
case 14:
return 100000000000000.0
case 15:
return 1000000000000000.0
case 16:
return 10000000000000000.0
case 17:
return 100000000000000000.0
case 18:
return 1000000000000000000.0
default:
return 0.0
}
}

View File

@ -0,0 +1,197 @@
package cmap
import (
"sync"
)
var SHARD_COUNT = 32
// 一个分片的map存储器 可并发
const ShardCount = 31 // 分区数量
// ConcurrentMap A "thread" safe map of type string:Anything.
// To avoid lock bottlenecks this map is dived to several (ShardCount) map shards.
type ConcurrentMap []*ConcurrentMapShared // 分片存储map 可并发
// ConcurrentMapShared A "thread" safe string to anything map.
type ConcurrentMapShared struct {
items map[string]interface{}
sync.RWMutex // Read Write mutex, guards access to internal map.
}
// New Creates a new concurrent map.
func New() ConcurrentMap {
m := make(ConcurrentMap, SHARD_COUNT)
for i := 0; i < SHARD_COUNT; i++ {
m[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
}
return m
}
// GetNoLock retrieves an element from map under given key.
func (m ConcurrentMap) GetNoLock(shard *ConcurrentMapShared, key string) (interface{}, bool) {
// Get item from shard.
val, ok := shard.items[key]
return val, ok
}
// SetNoLock retrieves an element from map under given key.
func (m ConcurrentMap) SetNoLock(shard *ConcurrentMapShared, key string, value interface{}) {
shard.items[key] = value
}
// NewConcurrentMap 创建
func NewConcurrentMap() ConcurrentMap {
m := make(ConcurrentMap, ShardCount)
for i := 0; i < ShardCount; i++ {
m[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
}
return m
}
// GetShard 返回给定键下的分片
func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {
return m[fnv32(key)&ShardCount]
}
// MSet 存储一组map
func (m ConcurrentMap) MSet(data map[string]interface{}) {
for key, value := range data {
shard := m.GetShard(key)
shard.Lock()
shard.items[key] = value
shard.Unlock()
}
}
// Set the given value under the specified key.
// 在指定的键下设置给定的值。
func (m ConcurrentMap) Set(key string, value interface{}) {
// Get map shard.
shard := m.GetShard(key)
shard.Lock()
shard.items[key] = value
shard.Unlock()
}
// UpsertCb Callback to return new element to be inserted into the map
// It is called while lock is held, therefore it MUST NOT
// try to access other keys in same map, as it can lead to deadlock since
// Go sync.RWLock is not reentrant
// 回调函数返回新元素插入到映射中。它在锁被持有时被调用,因此它一定不要试图访问同一映射中的其他键,因为它可能导致死锁。 RWLock是不可重入的
type UpsertCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{}
// Upsert Insert or Update - updates existing element or inserts a new one using UpsertCb
// 插入或更新——使用UpsertCb更新现有元素或插入新元素
func (m ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res interface{}) {
shard := m.GetShard(key)
shard.Lock()
v, ok := shard.items[key]
res = cb(ok, v, value)
shard.items[key] = res
shard.Unlock()
return res
}
// SetIfAbsent Sets the given value under the specified key if no value was associated with it.
// 如果没有值与指定键关联,则在指定键下设置给定值。
func (m ConcurrentMap) SetIfAbsent(key string, value interface{}) bool {
// Get map shard.
shard := m.GetShard(key)
shard.Lock()
_, ok := shard.items[key]
if !ok {
shard.items[key] = value
}
shard.Unlock()
return !ok
}
// Get retrieves an element from map under given key.
func (m ConcurrentMap) Get(key string) (interface{}, bool) {
shard := m.GetShard(key)
shard.RLock()
val, ok := shard.items[key]
shard.RUnlock()
return val, ok
}
// Count returns the number of elements within the map.
func (m ConcurrentMap) Count() int {
count := 0
for i := 0; i < ShardCount; i++ {
shard := m[i]
shard.RLock()
count += len(shard.items)
shard.RUnlock()
}
return count
}
// Has Looks up an item under specified key 存在性
func (m ConcurrentMap) Has(key string) bool {
shard := m.GetShard(key)
shard.RLock()
_, ok := shard.items[key]
shard.RUnlock()
return ok
}
// Remove removes an element from the map. 移除
func (m ConcurrentMap) Remove(key string) {
// Try to get shard.
shard := m.GetShard(key)
shard.Lock()
delete(shard.items, key)
shard.Unlock()
}
// RemoveCb is a callback executed in a map.RemoveCb() call, while Lock is held
// If returns true, the element will be removed from the map
// 是一个在map.RemoveCb()调用中执行的回调函数当Lock被持有时如果返回true该元素将从map中移除
type RemoveCb func(key string, v interface{}, exists bool) bool
// RemoveCb locks the shard containing the key, retrieves its current value and calls the callback with those params
// If callback returns true and element exists, it will remove it from the map
// Returns the value returned by the callback (even if element was not present in the map)
// 如果callback返回true且element存在则将其从map中移除。返回callback返回的值(即使element不存在于map中)
func (m ConcurrentMap) RemoveCb(key string, cb RemoveCb) bool {
shard := m.GetShard(key)
shard.Lock()
v, ok := shard.items[key]
remove := cb(key, v, ok)
if remove && ok {
delete(shard.items, key)
}
shard.Unlock()
return remove
}
// Pop removes an element from the map and returns it
// 从映射中移除一个元素并返回它
func (m ConcurrentMap) Pop(key string) (v interface{}, exists bool) {
// Try to get shard.
shard := m.GetShard(key)
shard.Lock()
v, exists = shard.items[key]
delete(shard.items, key)
shard.Unlock()
return v, exists
}
// IsEmpty checks if map is empty. 是否是空的
func (m ConcurrentMap) IsEmpty() bool {
return m.Count() == 0
}
// 将键值映射为数字uint32
func fnv32(key string) uint32 {
const prime32 = uint32(16777619)
hash := uint32(2166136261)
for i := 0; i < len(key); i++ {
hash *= prime32
hash ^= uint32(key[i])
}
return hash
}

View File

@ -0,0 +1,60 @@
package cmap
import (
"fmt"
"go-admin/pkg/utility"
"hash/crc32"
"testing"
)
// 离散性测试
func Test_fnv32(t *testing.T) {
st := make(map[uint32]int)
for i := 0; i < 1000000; i++ {
fnv := crc32.ChecksumIEEE([]byte(utility.GenerateRandString(8)))
k := fnv & 15
count, ok := st[k]
if !ok {
st[k] = 1
}
st[k] = count + 1
}
for k, v := range st {
fmt.Println(k, "\t", float64(v)/1000000)
}
}
// go test -bench=_QE_ -benchmem -run=^$
// -benchtime 默认为1秒 -benchmem 获得内存分配的统计数据
// Benchmark_QE_1-6 146641 8192 ns/op 32 B/op 3 allocs/op
// Benchmark_QE_2-6 143118 8246 ns/op 40 B/op 4 allocs/op
//
// Benchmark_QE_1-6 146289 8212 ns/op 32 B/op 3 allocs/op
// Benchmark_QE_2-6 144918 8239 ns/op 40 B/op 4 allocs/op
func Benchmark_QE_1(b *testing.B) {
for i := 0; i < b.N; i++ {
fnv32(utility.GenerateRandString(8))
}
}
func Benchmark_QE_2(b *testing.B) {
for i := 0; i < b.N; i++ {
crc32.ChecksumIEEE([]byte(utility.GenerateRandString(8)))
}
}
// go test -bench=_QE2_ -benchmem -benchtime=5s -run=^$
// -benchtime 默认为1秒 -benchmem 获得内存分配的统计数据
// Benchmark_QE2_1-6 1000000000 0.2623 ns/op 0 B/op 0 allocs/op
// Benchmark_QE2_2-6 1000000000 0.2631 ns/op 0 B/op 0 allocs/op
func Benchmark_QE2_1(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = i & 31
}
}
func Benchmark_QE2_2(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = i % 31
}
}

View File

@ -0,0 +1,160 @@
package cmap
import (
"sync"
"github.com/bytedance/sonic"
)
// 迭代器部分
// Tuple Used by the Iter & IterBuffered functions to wrap two variables together over a channel
// 由Iter & IterBuffered函数使用在一个通道上封装两个变量
type Tuple struct {
Key string
Val interface{}
}
// Iter returns an iterator which could be used in a for range loop.
// 返回一个可用于for范围循环的迭代器。
// Deprecated: using IterBuffered() will get a better performence
// 使用IterBuffered()将获得更好的性能
func (m ConcurrentMap) Iter() <-chan Tuple {
chans := snapshot(m)
ch := make(chan Tuple) // 不带缓冲
go fanIn(chans, ch)
return ch
}
// IterBuffered returns a buffered iterator which could be used in a for range loop.
// 返回一个可用于for范围循环的缓冲迭代器。
func (m ConcurrentMap) IterBuffered() <-chan Tuple {
chans := snapshot(m)
total := 0
for _, c := range chans {
total += cap(c)
}
ch := make(chan Tuple, total) // 一次性写完到缓冲中
go fanIn(chans, ch)
return ch
}
// Returns a array of channels that contains elements in each shard,
// which likely takes a snapshot of `m`.
// It returns once the size of each buffered channel is determined,
// before all the channels are populated using goroutines.
// 返回一个通道数组其中包含每个shard中的元素它可能会获取' m '的快照。
// 一旦确定了每个缓冲通道的大小在使用goroutines填充所有通道之前它将返回。
func snapshot(m ConcurrentMap) (chans []chan Tuple) {
chans = make([]chan Tuple, ShardCount)
wg := sync.WaitGroup{}
wg.Add(ShardCount)
// Foreach shard.
for index, shard := range m {
go func(index int, shard *ConcurrentMapShared) {
shard.RLock()
chans[index] = make(chan Tuple, len(shard.items))
wg.Done() // 只要创建了通道就不用再阻塞了
for key, val := range shard.items {
chans[index] <- Tuple{key, val}
}
shard.RUnlock()
close(chans[index])
}(index, shard)
}
wg.Wait()
return chans
}
// fanIn reads elements from channels `chans` into channel `out`
// 从通道' chans '读取元素到通道' out '
func fanIn(chans []chan Tuple, out chan Tuple) {
wg := sync.WaitGroup{}
wg.Add(len(chans))
for _, ch := range chans {
go func(ch chan Tuple) {
for t := range ch {
out <- t
}
wg.Done()
}(ch)
}
wg.Wait()
close(out)
}
// Items returns all items as map[string]interface{}
// 返回所有条目作为map[string]interface{}
func (m ConcurrentMap) Items() map[string]interface{} {
tmp := make(map[string]interface{})
// Insert items to temporary map. 向临时映射中插入项目。
for item := range m.IterBuffered() {
tmp[item.Key] = item.Val
}
return tmp
}
// IterCb Iterator callback,called for every key,value found in
// maps. RLock is held for all calls for a given shard
// therefore callback sess consistent view of a shard,
// but not across the shards
// 迭代器回调函数在map中找到的每个键和值都会被调用。
// RLock对给定分片的所有调用都保持因此回调获得一个分片的一致视图但不跨分片
type IterCb func(key string, v interface{})
// IterCb Callback based iterator, cheapest way to read all elements in a map.
// 基于回调的迭代器,读取映射中所有元素的最便宜方法。
func (m ConcurrentMap) IterCb(fn IterCb) {
for idx := range m {
shard := (m)[idx]
shard.RLock()
for key, value := range shard.items {
fn(key, value)
}
shard.RUnlock()
}
}
// Keys returns all keys as []string
// 返回所有键为[]字符串
func (m ConcurrentMap) Keys() []string {
count := m.Count()
ch := make(chan string, count)
go func() {
// Foreach shard.
wg := sync.WaitGroup{}
wg.Add(ShardCount)
for _, shard := range m {
go func(shard *ConcurrentMapShared) {
// Foreach key, value pair.
shard.RLock()
for key := range shard.items {
ch <- key
}
shard.RUnlock()
wg.Done()
}(shard)
}
wg.Wait()
close(ch)
}()
// Generate keys
keys := make([]string, 0, count)
for k := range ch {
keys = append(keys, k)
}
return keys
}
// MarshalJSON Reviles ConcurrentMap "private" variables to json marshal.
// 将存储的所有数据json序列化输出
func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
tmp := make(map[string]interface{})
for item := range m.IterBuffered() {
tmp[item.Key] = item.Val
}
return sonic.Marshal(tmp)
}

View File

@ -0,0 +1,89 @@
package utility
import (
"math/rand"
"time"
"github.com/shopspring/decimal"
)
func StrToDecimal(data string) decimal.Decimal {
result, _ := decimal.NewFromString(data)
return result
}
// DecimalCutStr 按保留的小数点位数,去掉多余的小数 非四舍五入
func DecimalCutStr(num decimal.Decimal, size int32) string {
if num.Cmp(decimal.Zero) == 0 {
return `0`
}
str := num.Truncate(size)
result := str.String()
return result
}
// Random 生成一个在 [start, end] 范围内的随机数,保留一位小数
// start 开始数字
// end 结束数字
// floatNum 保留小数位数
func DecimalRandom(start, end decimal.Decimal, floatNum int) decimal.Decimal {
// 创建一个本地随机数生成器
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// 将 start 和 end 转换为浮点数
startFloat, _ := start.Float64()
endFloat, _ := end.Float64()
// 生成随机数
randomFloat := startFloat + r.Float64()*(endFloat-startFloat)
// 保留一位小数
randomDecimal := decimal.NewFromFloat(randomFloat).Round(int32(floatNum))
return randomDecimal
}
// DiscardDecimal 舍弃 decimal 类型的最后指定位数小数
// value: 输入的 decimal 值
// discardDigits: 需要舍弃的小数位数
func DiscardDecimal(value decimal.Decimal, discardDigits int32) decimal.Decimal {
// 如果 discardDigits 小于 0直接返回原值
if discardDigits < 0 {
return value
}
// 获取当前值的小数位数
currentPrecision := value.Exponent() * -1
// 获取整数部分
integerPart := value.Truncate(0)
// 如果小数位数超过一位且小于两位
if currentPrecision > 1 && currentPrecision < 2 {
// 如果有整数部分,截断一位小数
if !integerPart.IsZero() {
return value.Truncate(currentPrecision - 1)
}
// 如果没有整数部分,按正常流程处理
}
// 如果小数位数只有一位
if currentPrecision == 1 {
// 如果有整数部分,返回整数部分
if !integerPart.IsZero() {
return integerPart
}
// 如果没有整数部分,保持原数据不变
return value
}
// 如果小数位数超过两位,按正常流程处理
if currentPrecision > discardDigits {
precision := currentPrecision - discardDigits
return value.Truncate(precision)
}
return value
}

301
pkg/utility/floathelper.go Normal file
View File

@ -0,0 +1,301 @@
package utility
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/shopspring/decimal"
)
// Float64CutString 保留size位小数 不足则填充0
func Float64CutString(num float64, size int32) string {
de := decimal.NewFromFloat(num).Truncate(size)
return de.StringFixed(size)
}
func FloatAddCutFixStr(num1, num2 float64, size int32) string {
result := decimal.NewFromFloat(num1).Add(decimal.NewFromFloat(num2)).Truncate(size)
return result.StringFixed(size)
}
// FloatCut 按保留的小数点位数,去掉多余的小数
func FloatCut(num float64, size int32) float64 {
de := decimal.NewFromFloat(num)
str := de.Truncate(size)
result, _ := str.Float64()
return result
}
// FloatCutStr 按保留的小数点位数,去掉多余的小数 非四舍五入
func FloatCutStr(num float64, size int32) string {
if num == 0 {
return `0`
}
de := decimal.NewFromFloat(num)
str := de.Truncate(size)
result := str.String()
return result
}
// StringFloat64Cut 保留8为小数后边为0不截取
func StringFloat64Cut(num string, size int32) string {
// 清理输入字符串,去除空字符和其他非数字字符
if strings.Contains(num, "x00") {
fmt.Sprintf("打印信息", num)
}
cleanedNum := strings.TrimRight(num, "\x00") // 去除空字符
cleanedNum = strings.TrimSpace(cleanedNum) // 去除空格
cleanedNum = strings.ReplaceAll(strings.ReplaceAll(cleanedNum, ",", ""), "\x00", "") // 去除逗号
de, err := decimal.NewFromString(cleanedNum)
if err != nil {
return ""
}
return de.Truncate(size).String()
}
// StrToFloatCut 按保留的小数点位数,去掉多余的小数 非四舍五入
func StrToFloatCut(num string, size int32) float64 {
if num == "" {
return 0
}
de, _ := decimal.NewFromString(num)
str := de.Truncate(size)
result, _ := str.Float64()
return result
}
// FloatThousand 对float进行千分位处理返回字符串,比如2568965463.256545 => 2,568,965,463.256545
func FloatThousand(num float64) string {
if num <= 1000 {
return decimal.NewFromFloat(num).String()
}
n := decimal.NewFromFloat(num).String()
dec := ""
if strings.Index(n, ".") != -1 {
dec = n[strings.Index(n, ".")+1:]
n = n[0:strings.Index(n, ".")]
}
for i := 0; i <= len(n); i = i + 4 {
a := n[0 : len(n)-i]
b := n[len(n)-i:]
n = a + "," + b
}
if n[0:1] == "," {
n = n[1:]
}
if n[len(n)-1:] == "," {
n = n[0 : len(n)-1]
}
if dec != "" {
n = n + "." + dec
}
return n
}
// Float8ToString 按保留的小数点8位数,去掉多余的小数, return string
func Float8ToString(num float64) string {
return FloatToString(num, 8)
}
// FloatAdd float + float
func FloatAdd(num1, num2 float64) float64 {
result := decimal.NewFromFloat(num1).Add(decimal.NewFromFloat(num2))
f, _ := result.Float64()
return f
}
func FloatAddCutStr(num1, num2 float64, size int32) string {
result := decimal.NewFromFloat(num1).Add(decimal.NewFromFloat(num2))
return result.Truncate(size).String()
}
func FloatAddCut(num1, num2 float64, size int32) float64 {
result := decimal.NewFromFloat(num1).Add(decimal.NewFromFloat(num2))
f, _ := result.Truncate(size).Float64()
return f
}
// FloatSub float - float
func FloatSub(num1, num2 float64) float64 {
if num2 == 0 {
return num1
}
result := decimal.NewFromFloat(num1).Sub(decimal.NewFromFloat(num2))
f, _ := result.Float64()
return f
}
// FloatSubCut float - float
func FloatSubCut(num1, num2 float64, size int32) float64 {
if num2 == 0 {
return num1
}
result := decimal.NewFromFloat(num1).Sub(decimal.NewFromFloat(num2))
f, _ := result.Truncate(size).Float64()
return f
}
// FloatSubCutStr float - float
func FloatSubCutStr(num1, num2 float64, size int32) string {
if num2 == 0 {
return decimal.NewFromFloat(num1).Truncate(size).String()
}
result := decimal.NewFromFloat(num1).Sub(decimal.NewFromFloat(num2))
f := result.Truncate(size).String()
return f
}
// FloatDiv float / float 两数相除
func FloatDiv(num1, num2 float64) float64 {
result := decimal.NewFromFloat(num1).Div(decimal.NewFromFloat(num2))
f, _ := result.Float64()
return f
}
func FloatDivCutStr(num1, num2 float64, size int32) string {
result := decimal.NewFromFloat(num1).Div(decimal.NewFromFloat(num2))
result = result.Truncate(size)
s := result.String()
return s
}
func FloatDivCutFixStr(num1, num2 float64, size int32) string {
result := decimal.NewFromFloat(num1).Div(decimal.NewFromFloat(num2))
return result.Truncate(size).StringFixed(size)
}
func FloatDivCut(num1, num2 float64, size int32) float64 {
result := decimal.NewFromFloat(num1).Div(decimal.NewFromFloat(num2))
result = result.Truncate(size)
f, _ := result.Float64()
return f
}
// FloatMul float * float
func FloatMul(num1, num2 float64) float64 {
result := decimal.NewFromFloat(num1).Mul(decimal.NewFromFloat(num2))
f, _ := result.Float64()
return f
}
// FloatMulCut 两数相乘并返回小数点后size位的float64
func FloatMulCut(num1, num2 float64, size int32) float64 {
result := decimal.NewFromFloat(num1).Mul(decimal.NewFromFloat(num2))
result = result.Truncate(size)
f, _ := result.Float64()
return f
}
// FloatMulCutStr float * float 两数相乘并返回指定小数位数的float64 返回字符串
func FloatMulCutStr(num1, num2 float64, size int32) string {
result := decimal.NewFromFloat(num1).Mul(decimal.NewFromFloat(num2))
result = result.Truncate(size)
return result.String()
}
// FloatMulCutFixStr float * float 两数相乘并返回指定小数位数的float64 返回字符串
func FloatMulCutFixStr(num1, num2 float64, size int32) string {
result := decimal.NewFromFloat(num1).Mul(decimal.NewFromFloat(num2))
result = result.Truncate(size)
return result.StringFixed(size)
}
// GetTotalAmt 计算需要冻结的币 数量*??/价格
func GetTotalAmt(num int, price, contractVal float64, size int32) float64 {
de := decimal.NewFromInt(int64(num)).
Mul(decimal.NewFromFloat(contractVal)).
Div(decimal.NewFromFloat(price)).
Truncate(size)
result2, _ := de.Float64()
return result2
}
func GetNonce() string {
s := strconv.FormatInt(time.Now().UnixNano(), 10)[0:11]
return s
}
// IsEqual 比对2个float64 是否相等
func IsEqual(num1, num2 float64, size int32) bool {
n1 := decimal.NewFromFloat(num1).Truncate(size)
n2 := decimal.NewFromFloat(num2).Truncate(size)
return n1.Equal(n2)
}
// GetDealAmt 根据下单张数,下单总的冻结金额,计算本次成交金额
func GetDealAmt(num, totalNum int, totalAmt float64, size int32) float64 {
if num == totalNum {
return totalAmt
}
de := decimal.NewFromFloat(totalAmt).
Div(decimal.NewFromInt(int64(num))).
Mul(decimal.NewFromInt(int64(num))).
Truncate(size)
result2, _ := de.Float64()
return result2
}
func ToFloat64(v interface{}) float64 {
if v == nil {
return 0.0
}
switch v.(type) {
case float64:
return v.(float64)
case string:
vStr := v.(string)
vF, _ := strconv.ParseFloat(vStr, 64)
return vF
default:
panic("to float64 error.")
}
}
func ToInt(v interface{}) int {
if v == nil {
return 0
}
switch v.(type) {
case string:
vStr := v.(string)
vInt, _ := strconv.Atoi(vStr)
return vInt
case int:
return v.(int)
case float64:
vF := v.(float64)
return int(vF)
default:
panic("to int error.")
}
}
func ToInt64(v interface{}) int64 {
if v == nil {
return 0
}
switch v.(type) {
case float64:
return int64(v.(float64))
default:
vv := fmt.Sprint(v)
if vv == "" {
return 0
}
vvv, err := strconv.ParseInt(vv, 0, 64)
if err != nil {
return 0
}
return vvv
}
}

View File

@ -0,0 +1,14 @@
package utility
import (
"fmt"
"testing"
)
func Test_FloatCutStr(t *testing.T) {
fmt.Println(FloatCutStr(10, -1))
}
func TestFloat64CutString(t *testing.T) {
xx := Float64CutString(1002277.51198900, 3)
fmt.Println(xx)
}

76
pkg/utility/idhelper.go Normal file
View File

@ -0,0 +1,76 @@
package utility
import (
"github.com/rs/xid"
"go.uber.org/zap"
"fmt"
"math"
"math/rand"
"time"
log "github.com/go-admin-team/go-admin-core/logger"
)
// GetXid Package xid is a globally unique id generator library
// 包xid是一个全局唯一的id生成器库
func GetXid() string {
return xid.New().String()
}
// GetGuid 获取guid 基于时间戳和MAC地址的uuid 可以视为随机字符串
//func GetGuid() string {
// return uuid.NewV1().String()
//}
//var orderLock sync.Mutex
// GetOrderNo 获取订单id 看样子已经废弃 改用采用雪花算法获取了
//func GetOrderNo() string {
// orderLock.Lock()
// defer orderLock.Unlock()
// date := time.Now().Format("200601021504")
// code := fmt.Sprintf("%s%07d", date, time.Now().Nanosecond()/100)
// time.Sleep(30 * time.Millisecond)
// return code
//}
// GetRandIntStr 生成len位的随机数字
func GetRandIntStr(len int, prefix string) string {
rand.Seed(time.Now().UnixNano())
num := rand.Int31n(int32(math.Pow(10, float64(len))))
x := fmt.Sprintf("%s%0*d", prefix, len, num)
return x
}
// GenerateRandString 生成指定位数的字符串
// 虽然繁琐 但理解之后就觉得很精妙
func GenerateRandString(length int) string {
var chars = []byte(`ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`) // 长度:(1,256)
rand.Seed(time.Now().UnixNano())
clen := len(chars)
maxRb := 255 - (256 % clen) // [-1,255] 255 - (256%36) = 251 避免模偏倚 为了每个字符被取到的几率相等
b := make([]byte, length)
r := make([]byte, length+(length/4)) // storage for random bytes. 存储随机字节
for i := 0; ; {
// 将随机的byte值填充到byte数组中 以供使用
if _, err := rand.Read(r); err != nil {
log.Error(`GenerateRandString`, zap.Error(err))
return ``
}
for _, rb := range r {
c := int(rb)
if c > maxRb {
// Skip this number to avoid modulo bias.跳过这个数字以避免模偏倚
continue
}
b[i] = chars[c%clen]
i++
if i == length { // 直到取到合适的长度
return string(b)
}
}
}
}

View File

@ -0,0 +1,26 @@
package utility
import (
"fmt"
"testing"
"time"
)
func Test_GenerateRandString(t *testing.T) {
for i := 0; i < 10; i++ {
t.Log(GenerateRandString(6))
time.Sleep(time.Microsecond)
}
}
func TestName(t *testing.T) {
for i := 0; i < 10; i++ {
t.Log(GetRandIntStr(6, ""))
time.Sleep(time.Microsecond)
}
// t.Log(math.Pow(10, 5))
}
func TestGetXid(t *testing.T) {
data := GetXid()
fmt.Println(data)
}

234
pkg/utility/ipnet.go Normal file
View File

@ -0,0 +1,234 @@
package utility
import (
"errors"
"net"
"net/url"
"sort"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
func GetIp(ctx *gin.Context) string {
ip := ctx.ClientIP()
if len(ip) > 0 {
return ip
}
return ""
}
// ExternalIP get external ip. 获取外部的ip
func ExternalIP() (res []string) {
inters, err := net.Interfaces()
if err != nil {
return
}
for _, inter := range inters {
if !strings.HasPrefix(inter.Name, "lo") {
addrs, err := inter.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipNet, ok := addr.(*net.IPNet); ok {
if ipNet.IP.IsLoopback() || ipNet.IP.IsLinkLocalMulticast() || ipNet.IP.IsLinkLocalUnicast() {
continue
}
if ip4 := ipNet.IP.To4(); ip4 != nil {
switch true {
case ip4[0] == 10:
continue
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
continue
case ip4[0] == 192 && ip4[1] == 168:
continue
default:
res = append(res, ipNet.IP.String())
}
}
}
}
}
}
return
}
// InternalIP get internal ip. 获取内部的ip
func InternalIP() string {
inters, err := net.Interfaces()
if err != nil {
return ""
}
for _, inter := range inters {
if inter.Flags&net.FlagUp == net.FlagUp {
continue
}
if !strings.HasPrefix(inter.Name, "lo") {
addrs, err := inter.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
}
}
return ""
}
// GetFreePort gets a free port. 获得一个自由端口
func GetFreePort() (port int, err error) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
defer listener.Close()
addr := listener.Addr().String()
_, portString, err := net.SplitHostPort(addr)
if err != nil {
return 0, err
}
return strconv.Atoi(portString)
}
// ParseRpcAddress parses rpc address such as tcp@127.0.0.1:8972 quic@192.168.1.1:9981
func ParseRpcAddress(addr string) (network string, ip string, port int, err error) {
ati := strings.Index(addr, "@")
if ati <= 0 {
return "", "", 0, errors.New("invalid rpc address: " + addr)
}
network = addr[:ati]
addr = addr[ati+1:]
var portStr string
ip, portStr, err = net.SplitHostPort(addr)
if err != nil {
return "", "", 0, err
}
port, err = strconv.Atoi(portStr)
return network, ip, port, err
}
func ConvertMeta2Map(meta string) map[string]string {
var rt = make(map[string]string)
if meta == "" {
return rt
}
v, err := url.ParseQuery(meta)
if err != nil {
return rt
}
for key := range v {
rt[key] = v.Get(key)
}
return rt
}
func ConvertMap2String(meta map[string]string) string {
var buf strings.Builder
keys := make([]string, 0, len(meta))
for k := range meta {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
vs := meta[k]
keyEscaped := url.QueryEscape(k)
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(keyEscaped)
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(vs))
}
return buf.String()
}
// ExternalIPV4 gets external IPv4 address of this server.
func ExternalIPV4() (string, error) {
ifaces, err := net.Interfaces()
if err != nil {
return "", err
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := iface.Addrs()
if err != nil {
return "", err
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
ip = ip.To4()
if ip == nil {
continue // not an ipv4 address
}
return ip.String(), nil
}
}
return "", errors.New("are you connected to the network?")
}
// ExternalIPV6 gets external IPv6 address of this server.
func ExternalIPV6() (string, error) {
ifaces, err := net.Interfaces()
if err != nil {
return "", err
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := iface.Addrs()
if err != nil {
return "", err
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
ip = ip.To16()
if ip == nil {
continue // not an ipv4 address
}
return ip.String(), nil
}
}
return "", errors.New("are you connected to the network?")
}

View File

@ -0,0 +1,40 @@
package lockkey
import (
"fmt"
"sync"
)
type LockByKey struct {
m map[string]*sync.RWMutex
l sync.Mutex
}
func NewLockByKey() *LockByKey {
return &LockByKey{m: make(map[string]*sync.RWMutex)}
}
func (lk *LockByKey) Lock(key string) {
lk.l.Lock()
defer lk.l.Unlock()
mu, ok := lk.m[key]
if !ok {
mu = &sync.RWMutex{}
lk.m[key] = mu
}
mu.Lock()
}
func (lk *LockByKey) Unlock(key string) {
lk.l.Lock()
defer lk.l.Unlock()
mu, ok := lk.m[key]
if !ok {
panic(fmt.Sprintf("unlock non-existent key: %v", key))
}
mu.Unlock()
}

View File

@ -0,0 +1,34 @@
package lockkey
import (
"fmt"
"testing"
"time"
)
func TestLock(t *testing.T) {
lk := NewLockByKey()
// Lock and unlock the same key multiple times.
for i := 0; i < 5; i++ {
go func() {
lk.Lock("key")
fmt.Println("locked")
lk.Unlock("key")
fmt.Println("unlocked")
}()
}
// Lock and unlock different keys.
lk.Lock("key1")
fmt.Println("locked key1")
lk.Unlock("key1")
fmt.Println("unlocked key1")
lk.Lock("key2")
fmt.Println("locked key2")
lk.Unlock("key2")
fmt.Println("unlocked key2")
time.Sleep(3 * time.Second)
}

54
pkg/utility/maps.go Normal file
View File

@ -0,0 +1,54 @@
package utility
import (
"github.com/mitchellh/mapstructure"
"reflect"
"time"
)
// 映射;
type maps struct {
}
// 构建;
func Maps() *maps {
return &maps{}
}
// 转为结构体;
func (this *maps) Struct(src, dst interface{}) error {
config := &mapstructure.DecoderConfig{
WeaklyTypedInput: true,
DecodeHook: mapstructure.ComposeDecodeHookFunc(ToTimeHookFunc()),
Result: &dst,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
if err = decoder.Decode(src); err != nil {
return err
}
return nil
}
func ToTimeHookFunc() mapstructure.DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if t != reflect.TypeOf(time.Time{}) {
return data, nil
}
switch f.Kind() {
case reflect.String:
return time.Parse(time.RFC3339, data.(string))
case reflect.Float64:
return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
case reflect.Int64:
return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
default:
return data, nil
}
}
}

View File

@ -0,0 +1,47 @@
package ratecheck
import (
"github.com/juju/ratelimit"
gocache "github.com/patrickmn/go-cache"
"go-admin/pkg/utility"
"time"
)
var (
// Map of limiters with TTL
tokenBuckets = gocache.New(120*time.Minute, 1*time.Minute)
userDur = 1 * time.Second
userSize int64 = 1
orderDur = 20 * time.Second
)
// CheckRateLimit 根据key检测在规定时间内是否超过访问次数,限流
func CheckRateLimit(key string, duration time.Duration, size int64) bool {
if _, found := tokenBuckets.Get(key); !found {
tokenBuckets.Set(
key,
ratelimit.NewBucket(duration, size),
duration)
}
expiringMap, found := tokenBuckets.Get(key)
if !found {
return false
}
return expiringMap.(*ratelimit.Bucket).TakeAvailable(1) > 0
}
// CheckUserRateLimit 根据key检测在规定时间内单个用户是否超过访问次数,限流,默认5秒1次请求
func CheckUserRateLimit(userid int, methodName string) bool {
key := methodName + "-" + utility.IntTostring(userid)
return CheckRateLimit(key, userDur, userSize)
}
// 检测订单成交是否重复推送
func CheckOrderIdIsExist(tradeId string) bool {
_, found := tokenBuckets.Get(tradeId)
if !found {
tokenBuckets.Set(tradeId, true, orderDur)
}
return found
}

112
pkg/utility/regexhelper.go Normal file
View File

@ -0,0 +1,112 @@
package utility
import (
"regexp"
)
var (
emailRegexp, _ = regexp.Compile("(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)")
)
// ValidateEmail 验证邮箱
func ValidateEmail(email string) bool {
// userName := "[a-zA-Z0-9][a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]{0,62}[a-zA-Z0-9]"
// domainName := "[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?"
// topLevelDomainName := "(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*"
// pattern := "^" + userName + "@" + domainName + topLevelDomainName + "$"
// reg := regexp.MustCompile(pattern)
// match := reg.MatchString(email)
match := emailRegexp.MatchString(email)
return match
}
// ValidatePhoneNumber 手机号码验证,+91-9819882936
//
// 1. Mobile Number
// - Starts with 6,7,8,8
// - 10 digit
// - Prexix can be +91, 0
//
// 2. LandLine Number
// - {area-code}-{local number}
// - 10 digit number
// - area codes range from 2-digits to 4-digits
// - ex. 02321-238200
func ValidatePhoneNumber(num string) bool {
prefixMobileNum := `(?:(?:\+|0{0,2})91([\s-])?|[0]?)?`
mobileNum := `[6-9]\d{9}`
landLineNum := `((0)?(([1-9]\d{1}-\d{8})|([1-9]\d{2}-\d{7})|([1-9]\d{3}-\d{6})))`
pattern := "^(" + "(" + prefixMobileNum + mobileNum + ")" + "|" + landLineNum + ")$"
reg := regexp.MustCompile(pattern)
match := reg.MatchString(num)
return match
}
// ValidatePhoneForeign 外国手机
func ValidatePhoneForeign(phone string) bool {
reg, err := regexp.MatchString("^[0-9]{7}$", phone)
if err != nil {
return false
}
return reg
}
// IsMobileChina 中国手机验证
func IsMobileChina(phone string) bool {
// reg, err := regexp.MatchString("^[0-9]{11}$", phone)
reg, err := regexp.MatchString("^(13[0-9]|14[0-9]|15[0-9]|17[0-9]|18[0-9]|19[0-9])\\d{8}$", phone)
if err != nil {
return false
}
return reg
}
// ReturnEmail 替换邮箱中间几位为*号
func ReturnEmail(email string) string {
if len(email) == 0 {
return ""
}
re, _ := regexp.Compile("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)")
return re.ReplaceAllString(email, "$1****$3$4")
}
// ReturnPhoneNO 替换手机号中间四位为*
func ReturnPhoneNO(phone string) string {
if len(phone) == 0 {
return ""
}
re, _ := regexp.Compile("(\\d{3})(\\d{4})(\\d{4})")
return re.ReplaceAllString(phone, "$1****$3")
}
// CheckPasswordOk
// 密码长度minLength-maxLength位可使用字母、数字、符号组成区分大小写至少包含两种
// minLength: 指定密码的最小长度
// maxLength指定密码的最大长度
// pwd明文密码
func CheckPasswordOk(minLength, maxLength int, pwd string) bool {
if len(pwd) < minLength {
return false // fmt.Errorf("BAD PASSWORD: The password is shorter than %d characters", minLength)
}
if len(pwd) > maxLength {
return false // fmt.Errorf("BAD PASSWORD: The password is logner than %d characters", maxLength)
}
// patternList := []string{`[0-9]+`, `[a-z]+`, `[A-Z]+`, `[~!@#$%^&*?_-]+`}
isNum, _ := regexp.MatchString(`[0-9]+`, pwd)
isLower, _ := regexp.MatchString(`[a-z]+`, pwd)
isUpper, _ := regexp.MatchString(`[A-Z]+`, pwd)
//isSpe, _ := regexp.MatchString(`[~!@#$%^&*?_-]+`, pwd)
return isNum && isLower && isUpper
}
// IsAccountTwo 用户账号验证
func IsAccountTwo(phone string) bool {
// reg, err := regexp.MatchString("^[0-9]{11}$", phone)
reg, err := regexp.MatchString("^[A-Za-z\\d]{6,12}$", phone)
if err != nil {
return false
}
return reg
}

44
pkg/utility/safego.go Normal file
View File

@ -0,0 +1,44 @@
package utility
import (
"fmt"
"runtime"
"runtime/debug"
"strings"
"github.com/go-admin-team/go-admin-core/logger"
)
// SafeGo 安全地启动一个 goroutine捕获 panic
func SafeGo(fn func()) {
go func() {
defer func() {
if r := recover(); r != nil {
// 记录 Goroutine ID、panic 信息和堆栈
logger.Error(fmt.Sprintf("Recovered from panic in Goroutine %s: %v\nStack Trace:\n%s", getGoroutineID(), r, string(debug.Stack())))
}
}()
fn()
}()
}
// 获取 Goroutine ID
func getGoroutineID() string {
buf := make([]byte, 64)
n := runtime.Stack(buf, false)
stack := string(buf[:n])
// 提取 Goroutine ID
id := strings.Split(stack, " ")[1]
return id
}
func SafeGoParam[T any](fn func(T), param T) {
go func() {
defer func() {
if r := recover(); r != nil {
logger.Error(fmt.Sprintf(" SafeGoParam Recovered from panic in Goroutine %s: %v\nStack Trace:\n%s", getGoroutineID(), r, string(debug.Stack())))
}
}()
fn(param) // 执行传入的函数
}()
}

29
pkg/utility/search.go Normal file
View File

@ -0,0 +1,29 @@
package utility
import "sort"
// 二分查找(会将切片 a 排为升序)
//
// 找到返回 true未找到返回 false
func BinarySearch(a []int, x int) bool {
if !sort.IntsAreSorted(a) {
sort.Ints(a)
}
l, r := 0, len(a)-1
for l <= r {
m := (l + r) / 2
if a[m] == x {
return true
}
// x 在左边
if x < a[m] {
r = m - 1
} else {
l = m + 1
}
}
return false
}

View File

@ -0,0 +1,14 @@
package utility
import (
"fmt"
"testing"
)
func TestBinarySearch(t *testing.T) {
// a := []int{5, 4, 3, 2}
a := []int{7, 5, 3, 9, 2, 6}
dst := BinarySearch(a, 106)
fmt.Println(dst)
}

68
pkg/utility/seqs/rand.go Normal file
View File

@ -0,0 +1,68 @@
package seqs
import (
cr "crypto/rand"
"math/big"
mr "math/rand"
"time"
)
// 随机生成器;
type rand struct {
}
// 全局随机数;
var rnd *mr.Rand
func init() {
rnd = mr.New(mr.NewSource(time.Now().UnixNano()))
}
// 构建;
func Rand() *rand {
return &rand{}
}
// 随机数字;
func (rd *rand) DigitId(_len int) string {
return rd.newId([]byte("0123456789"), _len)
}
// 随机字母;
func (rd *rand) ChrtId(_len int) string {
return rd.newId([]byte("abcdefghijklmnopqrstuvwxyz"), _len)
}
// 随机混合(数字+字母);
func (rd *rand) BothId(_len int) string {
return rd.newId([]byte("0123456789abcdefghijklmnopqrstuvwxyz"), _len)
}
// 随机范围(长整型);
func (rd *rand) RandI64(min, max int64) int64 {
bi, _ := cr.Int(cr.Reader, big.NewInt(max-min))
return min + bi.Int64()
}
// 随机范围(0 ~ max);
func (rd *rand) RandInt(max int) int {
return rnd.Intn(max)
}
// 随机中文;
func (rd *rand) ChrtCn(_len int) string {
a := make([]rune, _len)
for i := range a {
a[i] = rune(rd.RandI64(19968, 40869))
}
return string(a)
}
// newId;
func (rd *rand) newId(tmpl []byte, _len int) string {
var r []byte
for i := 0; i < _len; i++ {
r = append(r, tmpl[rnd.Intn(len(tmpl))])
}
return string(r)
}

141
pkg/utility/slices.go Normal file
View File

@ -0,0 +1,141 @@
package utility
import "strings"
// ContainsStr []string 包含元素?
func ContainsStr(arr []string, v string) bool {
for _, a := range arr {
if a == v {
return true
}
}
return false
}
func RemoveByValue(slice []string, value string) []string {
for i, v := range slice {
if v == value {
// 找到值,删除对应索引
return append(slice[:i], slice[i+1:]...)
}
}
return slice // 未找到返回原切片
}
// ContainsInt []int 包含元素?
func ContainsInt(arr []int, v int) bool {
for _, a := range arr {
if a == v {
return true
}
}
return false
}
func HasSuffix(data string, suffixs []string) bool {
for _, suffix := range suffixs {
if strings.HasSuffix(data, suffix) {
return true
}
}
return false
}
// SplitSlice 将 []string 切片根据最大数量分割成二维数组
func SplitSlice(slice []string, maxSize int) [][]string {
var result [][]string
// 遍历切片,每次取 maxSize 个元素
for i := 0; i < len(slice); i += maxSize {
end := i + maxSize
// 如果 end 超出切片长度,则取到切片末尾
if end > len(slice) {
end = len(slice)
}
// 将当前段添加到结果中
result = append(result, slice[i:end])
}
return result
}
//// 切片;
//type slices struct {
//}
//
//// 构建;
//func Slices() *slices {
// return &slices{}
//}
//
//// 包含元素?
//func (this *slices) Contains(s interface{}, v interface{}) bool {
// // 相关定义;
// ss := reflect.Indirect(reflect.ValueOf(s))
// slen := ss.Len()
// // 遍历;
// for i := 0; i < slen; i++ {
// // 定位元素;
// sv := reflect.Indirect(ss.Index(i))
// if fmt.Sprint(sv.Interface()) == fmt.Sprint(v) {
// return true
// }
// }
// return false
//}
//
//// 转为切片;
//func (this *slices) Slice(s interface{}) []interface{} {
// // 相关定义;
// ss := reflect.Indirect(reflect.ValueOf(s))
// slen := ss.Len()
// // 遍历;
// out := make([]interface{}, slen)
// for i := 0; i < slen; i++ {
// // 追加;
// out[i] = ss.Index(i).Interface()
// }
// return out
//}
//
//// 校验为nil?
//func (this *slices) IsNil(v interface{}) bool {
// if v == nil {
// return true
// }
// vi := reflect.ValueOf(v)
// if vi.Kind() == reflect.Ptr {
// return vi.Elem().IsNil()
// }
// return vi.IsNil()
//}
//
//// StringSliceReflectEqual 判断 string和slice 是否相等
//// 因为使用了反射所以效率较低可以看benchmark结果
//func (this *slices) StringSliceReflectEqual(a, b []string) bool {
// return reflect.DeepEqual(a, b)
//}
//
//// StringSliceEqual 判断 string和slice 是否相等
//// 使用了传统的遍历方式
//func (this *slices) StringSliceEqual(a, b []string) bool {
// if len(a) != len(b) {
// return false
// }
//
// // reflect.DeepEqual的结果保持一致
// if (a == nil) != (b == nil) {
// return false
// }
//
// // bounds check 边界检查
// // 避免越界
// b = b[:len(a)]
// for i, v := range a {
// if v != b[i] {
// return false
// }
// }
//
// return true
//}

View File

@ -0,0 +1,30 @@
package snowflakehelper
// 雪花算法用于生成订单号
import (
"fmt"
"go-admin/config"
"github.com/bwmarrin/snowflake"
)
var (
snowNode *snowflake.Node
)
func init() {
snowflake.Epoch = 1649212361224 // time.Now().UnixMilli()
// nodeId := utility.StringAsInt64()
node, err := snowflake.NewNode(config.ExtConfig.ServiceId)
if err != nil {
fmt.Println("snowflake.NewNode err:", err)
return
}
snowNode = node
}
// GetOrderId 生成int64订单id
func GetOrderId() int64 {
return snowNode.Generate().Int64()
}

485
pkg/utility/stringhelper.go Normal file
View File

@ -0,0 +1,485 @@
package utility
import (
"fmt"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"unicode/utf8"
"unsafe"
"go.uber.org/zap"
log "github.com/go-admin-team/go-admin-core/logger"
"github.com/shopspring/decimal"
)
// 获取不超过指定长度的字段
func StringCut(s string, maxLength int) string {
if len(s) > maxLength {
return s[:maxLength] // 获取前 maxLength 个字符
}
return s // 如果长度不足,返回原字符串
}
// FloatToStringZero 去掉多余的小数0,比如0.0245000返回0.0245,return string
func FloatToStringZero(num string) string {
de, _ := decimal.NewFromString(num)
str := de.String()
return str
}
// IntTostring int to string
func IntTostring(num int) string {
if num == 0 {
return "0"
}
return strconv.Itoa(num)
}
// StringLen 获取字符串真实长度
func StringLen(s string) int {
return len([]rune(s))
}
// StringIsNil 检测字符串长度是否为0
func StringIsNil(str string) bool {
if len(str) == 0 {
return true
}
return len(strings.Trim(str, " ")) == 0
}
// StringTrim 去前后空格
func StringTrim(str string) string {
return strings.Trim(str, " ")
}
// StringAsFloat tries to convert a string to float, and if it can't, just returns zero
// 尝试将字符串转换为浮点数如果不能则返回0
func StringAsFloat(s string) float64 {
if len(s) == 0 {
return 0.0
}
if f, err := strconv.ParseFloat(s, 64); err == nil {
return f
}
return 0.0
}
func StringAsDecimal(s string) decimal.Decimal {
if len(s) == 0 {
return decimal.Zero
}
f, err := decimal.NewFromString(s)
if err != nil {
log.Error("字符串转化decimal失败: ", zap.Error(err))
return decimal.Zero
}
return f
}
func StringAsFloatCut(s string, size int32) float64 {
if len(s) == 0 {
return 0
}
f, err := decimal.NewFromString(s)
if err != nil {
log.Error("字符串转化decimal失败: ", zap.Error(err))
return 0
}
d, _ := f.Truncate(size).Float64()
return d
}
// FloatToString 按保留的小数点位数,去掉多余的小数,return string
func FloatToString(num float64, size int32) string {
if num == 0 {
return getZero(size)
}
de := decimal.NewFromFloat(num)
str := de.Truncate(size).String()
return str
}
func getZero(size int32) string {
switch size {
case 0:
return "0"
case 1:
return "0.0"
case 2:
return "0.00"
case 3:
return "0.000"
case 4:
return "0.0000"
case 5:
return "0.00000"
case 6:
return "0.000000"
}
return "0"
}
func GetGearNumStr(size int32) string {
switch size {
case 0:
return "1"
case 1:
return "0.1"
case 2:
return "0.01"
case 3:
return "0.001"
case 4:
return "0.0001"
case 5:
return "0.00001"
case 6:
return "0.000001"
case 7:
return "0.0000001"
case 8:
return "0.00000001"
}
return "0.1"
}
func FloatToStr(num float64) string {
de := decimal.NewFromFloat(num)
str := de.String()
return str
}
// StringAsInteger returns the integer value extracted from string, or zero
func StringAsInteger(s string) int {
if s == "" {
return 0
}
if i, err := strconv.ParseInt(s, 10, 32); err == nil {
return int(i)
}
return 0
}
// StringAsInt64 returns the int64 value extracted from string, or zero
func StringAsInt64(s string) int64 {
if s == "" {
return 0
}
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
return i
}
return 0
}
func StringAsInt32(s string) int32 {
if s == "" {
return 0
}
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
return int32(i)
}
return 0
}
// StringToDecimal String To Decimal
func StringToDecimal(val string) decimal.Decimal {
cleanedNum := strings.TrimRight(val, "\x00") // 去除空字符
cleanedNum = strings.TrimSpace(cleanedNum) // 去除空格
cleanedNum = strings.ReplaceAll(cleanedNum, ",", "") // 去除逗号
d, err := decimal.NewFromString(cleanedNum)
if err != nil {
return decimal.Zero
}
return d
}
// StringToInt String => int
func StringToInt(val string) int {
i, err := strconv.Atoi(val)
if err != nil {
return 0
}
return i
}
// StringToFloat64 String => Float64
func StringToFloat64(val string) float64 {
d, err := strconv.ParseFloat(val, 64)
if err != nil {
return 0
}
return d
}
// ToLower 返回小写字符串
func ToLower(str string) string {
return strings.ToLower(str)
}
// ToUpper 返回大写字符串
func ToUpper(str string) string {
return strings.ToUpper(str)
}
// IntToString int to string
func IntToString(num int) string {
if num == 0 {
return "0"
}
return strconv.Itoa(num)
}
// CheckPhone returns true if a given sequence has between 9 and 14 digits
func CheckPhone(phone string, acceptEmpty bool) bool {
phone = OnlyDigits(phone)
return (acceptEmpty && (phone == "")) || ((len([]rune(phone)) >= 9) && (len([]rune(phone)) <= 14))
}
// OnlyDigits returns only the numbers from the given string, after strip all the rest ( letters, spaces, etc. )
func OnlyDigits(sequence string) string {
if utf8.RuneCountInString(sequence) > 0 {
re, _ := regexp.Compile(`[\D]`)
sequence = re.ReplaceAllString(sequence, "")
}
return sequence
}
// CheckEmail returns true if the given sequence is a valid email address
// See https://tools.ietf.org/html/rfc2822#section-3.4.1 for details about email address anatomy
func CheckEmail(email string) bool {
if email == "" {
return false
}
re := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
return re.MatchString(email)
}
// IsNumericType checks if an interface's concrete type corresponds to some of golang native numeric types
func IsNumericType(x interface{}) bool {
switch x.(type) {
case uint:
return true
case uint8: // Or byte
return true
case uint16:
return true
case uint32:
return true
case uint64:
return true
case int:
return true
case int8:
return true
case int16:
return true
case int32:
return true
case float32:
return true
case float64:
return true
case complex64:
return true
case complex128:
return true
default:
return false
}
}
// B2S converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func B2S(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// S2B converts string to a byte slice without memory allocation.
// Note it may break if string and/or slice header will change
// in the future go versions.
func S2B(s string) (b []byte) {
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
bh.Data = sh.Data
bh.Len = sh.Len
bh.Cap = sh.Len
return b
}
// Int64ToString int64 转到string
func Int64ToString(num int64) string {
if num == 0 {
return ""
}
return strconv.FormatInt(num, 10)
}
// Float64ToString Float64 转 string
// prec: 小数位数 -1 为全保留 与 FloatCutStr 不同的是 这里会四舍五入
func Float64ToString(num float64, prec int32) string {
return strconv.FormatFloat(num, 'f', int(prec), 64)
}
// SliceRemoveDuplicates 除去[]string数组重复的数据
func SliceRemoveDuplicates(slice []string) []string {
sort.Strings(slice)
i := 0
var j int
for {
if i >= len(slice)-1 {
break
}
for j = i + 1; j < len(slice) && slice[i] == slice[j]; j++ {
}
slice = append(slice[:i+1], slice[j:]...)
i++
}
return slice
}
func GetSymbolIdStr(coinId, currId string) string {
return fmt.Sprintf("%v/%v", coinId, currId)
}
func GetSymbolCoinIdAndCurrId(symbolIdStr string) (string, string) {
l := strings.Split(symbolIdStr, "/")
return l[0], l[1]
}
// CheckIdCard 检验身份证
func CheckIdCard(card string) bool {
// 18位身份证 ^(\d{17})([0-9]|X)$
// 匹配规则
// (^\d{15}$) 15位身份证
// (^\d{18}$) 18位身份证
// (^\d{17}(\d|X|x)$) 18位身份证 最后一位为X的用户
regRuler := "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)"
// 正则调用规则
reg := regexp.MustCompile(regRuler)
// 返回 MatchString 是否匹配
return reg.MatchString(card)
}
func FilteredSQLInject(to_match_str string) bool {
//过滤
//ORACLE 注解 -- /**/
//关键字过滤 update ,delete
// 正则的字符串, 不能用 " " 因为" "里面的内容会转义
str := `(?:')|(?:--)|(/\*(?:.|[\n\r])*?\*/)|((select|update|and|or|delete|insert|trancate|char|chr|into|substr|ascii|declare|exec|count|master|into|drop|execute))`
re, err := regexp.Compile(str)
if err != nil {
return false
}
return re.MatchString(to_match_str)
}
func TrimHtml(src string) string {
//将HTML标签全转换成小写
re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
src = re.ReplaceAllStringFunc(src, strings.ToLower)
//去除STYLE
re, _ = regexp.Compile("\\<style[\\S\\s]+?\\</style\\>")
src = re.ReplaceAllString(src, "")
//去除SCRIPT
re, _ = regexp.Compile("\\<script[\\S\\s]+?\\</script\\>")
src = re.ReplaceAllString(src, "")
//去除所有尖括号内的HTML代码并换成换行符
re, _ = regexp.Compile("\\<[\\S\\s]+?\\>")
src = re.ReplaceAllString(src, "\n")
//去除连续的换行符
re, _ = regexp.Compile("\\s{2,}")
src = re.ReplaceAllString(src, "\n")
return strings.TrimSpace(src)
}
// ReplaceImages 过滤掉图片
func ReplaceImages(htmls string) string {
result := htmls
regx := `<img[^>]*src[="'s]+[^.]*/([^.]+).[^"']+["']?[^>]*>`
var imgRE = regexp.MustCompile(regx)
imgs := imgRE.FindAllStringSubmatch(htmls, -1)
for i := range imgs {
v := imgs[i]
result = strings.ReplaceAll(result, v[0], "")
}
return result
}
// KeySubString 文章搜索专用
func KeySubString(key, value string, leng int) string {
b := ReplaceImages(value)
pos := strings.Index(b, key)
if pos < 0 {
return ""
}
start := b[0:pos]
end := b[pos:]
out := Substr(end, leng)
return start + out
}
// Substr 截取字符串
func Substr(s string, l int) string {
if len(s) <= l {
return s
}
ss, sl, rl, rs := "", 0, 0, []rune(s)
for _, r := range rs {
rint := int(r)
if rint < 128 {
rl = 1
} else {
rl = 2
}
if sl+rl > l {
break
}
sl += rl
ss += string(r)
}
return ss
}
/*
根据字符串获取小数位数
*/
func GetPrecision(value string) int {
// 去掉末尾的多余零
value = strings.TrimRight(value, "0")
// 找到小数点的位置
decimalPos := strings.Index(value, ".")
if decimalPos == -1 {
// 如果没有小数点说明是整数精度为0
return 0
}
// 计算小数点后的位数
return len(value) - decimalPos - 1
}
// 替换字符串后缀
func ReplaceSuffix(text, oldSuffix, newSuffix string) string {
if strings.HasSuffix(text, oldSuffix) {
return text[:len(text)-len(oldSuffix)] + newSuffix
}
return text
}

View File

@ -0,0 +1,49 @@
package utility
import (
"fmt"
"math/rand"
"strconv"
"testing"
)
func Test_(t *testing.T) {
// t.Log(decimal.NewFromFloat(0))
t.Log(strconv.FormatFloat(1.04, 'f', 1, 64))
t.Log(strconv.FormatFloat(1.05, 'f', 1, 64))
t.Log(strconv.FormatFloat(1.06, 'f', 1, 64))
t.Log(strconv.FormatFloat(1.006, 'f', 2, 64))
// t.Log(strconv.FormatFloat(`1.006`, 64))
}
// i := int64(32)
// s := strconv.FormatInt(i, 16)
// println(s)
// 对比下 Float64ToString 和 FloatCutStr 的效率
// go test -bench=_QE_ -benchmem
// -benchtime 默认为1秒 -benchmem 获得内存分配的统计数据
func Benchmark_QE_1(b *testing.B) {
for i := 0; i < b.N; i++ {
Float64ToString(getRandData(), 2)
}
}
func Benchmark_QE_2(b *testing.B) {
for i := 0; i < b.N; i++ {
FloatCutStr(getRandData(), 2) // 已废弃
}
}
func getRandData() float64 {
return float64(rand.Intn(1000000)) / 1000
}
func TestTrimHtml(t *testing.T) {
a := `sff` // `<script>alert('ab')</script>`
x := TrimHtml(a)
fmt.Print(x)
}
// Benchmark_QE_1-6 4902826 243.3 ns/op 31 B/op 2 allocs/op
// Benchmark_QE_2-6 1275004 940.6 ns/op 137 B/op 11 allocs/op
// 故此将优先使用 Float64ToString 并将已使用的 FloatCutStr 进行替换

View File

@ -0,0 +1,306 @@
package timehelper
import (
"fmt"
"go-admin/pkg/utility"
"strings"
"time"
)
const (
DATEFORMAT = "2006-01-02"
TIMEFORMAT = "2006-01-02 15:04:05"
MONTHFORMAT = "2006-01"
DATETIMEFORMAT = "2006-01-02 15:04"
TimeNil = "1900-01-01 00:00:00"
DefaultUnix = -62135596800 // 时间戳默认初始值
)
// GetDateUnixDate 返回带毫秒的时间戳如果需要转化为时间类型time,
// 不带毫秒的time.Unix(1663315884651/1000,0)
// 带毫秒的time.Unix(1663315884651/1000, 1000000*(1663315884651%1000))
func GetDateUnixDate(date time.Time) int64 {
return date.UnixMilli()
}
func ConvertTimeLocalSec(date int64) time.Time {
d1 := time.Unix(date/1000, 1000000*(date%1000))
d2 := time.Date(d1.Year(), d1.Month(), d1.Day(), d1.Hour(), d1.Minute(), d1.Second(), d1.Nanosecond(), time.Local)
return d2
}
// TimeSubDays 时间间隔天数
func TimeSubDays(t1, t2 time.Time) int {
if t1.Location().String() != t2.Location().String() {
return -1
}
hours := t1.Sub(t2).Hours()
if hours <= 0 {
return -1
}
// sub hours less than 24
if hours < 24 {
// may same day
t1y, t1m, t1d := t1.Date()
t2y, t2m, t2d := t2.Date()
isSameDay := (t1y == t2y && t1m == t2m && t1d == t2d)
if isSameDay {
return 0
}
return 1
}
// equal or more than 24
if (hours/24)-float64(int(hours/24)) == 0 { // just 24's times
return int(hours / 24)
}
// more than 24 hours
return int(hours/24) + 1
}
// ConvertTimeLocal s
func ConvertTimeLocal(d1 time.Time) time.Time {
d2 := time.Date(d1.Year(), d1.Month(), d1.Day(), d1.Hour(), d1.Minute(), d1.Second(), 0, time.Local)
return d2
}
// ConvertTimeLocalLong 转本地时间戳输出
func ConvertTimeLocalLong(d1 time.Time) int64 {
d2 := time.Date(d1.Year(), d1.Month(), d1.Day(), d1.Hour(), d1.Minute(), d1.Second(), 0, time.Local)
return d2.Unix()
}
// ConvertTimeDayLocal s
func ConvertTimeDayLocal(d1 time.Time) time.Time {
d2 := time.Date(d1.Year(), d1.Month(), d1.Day(), 0, 0, 0, 0, time.Local)
return d2
}
// // ToTimeHour 转为时分秒
// func ToTimeHour(ltime int64) string {
// now := IntToTime(ltime)
// return now.Format("15:04:05")
// }
// ParseTimeStrInLocal 从时间字符串解析时间,默认 当前时间
func ParseTimeStrInLocal(timeStr string, defaultT ...time.Time) time.Time {
t, err := time.ParseInLocation(TIMEFORMAT, timeStr, time.Local)
if err != nil {
if len(defaultT) > 0 {
return defaultT[0]
}
return time.Now()
}
return t
}
// IntToTime 时间戳转为时间类型
func IntToTime(intime int64) time.Time {
return time.Unix(intime/1000, 0)
}
func Int64ToTime(in int64) time.Time {
return time.Unix(in, 0)
}
// GetPastDay 当前时间算起过去num天内的开始日期、结束日期
func GetPastDay(num int) (start, end time.Time) {
tn := time.Now()
// 当前时间,天数为单位
nowday := time.Date(tn.Year(), tn.Month(), tn.Day(), 0, 0, 0, 0, time.Local)
// 过去30天
oldTime := nowday.AddDate(0, 0, -num)
return oldTime, nowday
}
// ConvertTimeToString 格式时间返回前台yyyy-mm-dd hh:mm:ss
func ConvertTimeToString(t time.Time) string {
return t.Format("2006-01-02 15:04:05")
}
// ConvertTimeToStringMin 格式时间返回前台yyyy-mm-dd hh:mm:ss
func ConvertTimeToStringMin(t time.Time) string {
return t.Format("2006-01-02 15:04")
}
// ConvertTimeToStringDay 格式时间返回前台yyyy-mm-dd
func ConvertTimeToStringDay(t time.Time) string {
return t.Format("2006-01-02")
}
// ConvertTimeToString2 格式时间返回前台yyyy.mm.dd hh:mm 2020.06.02 17:50
func ConvertTimeToString2(t time.Time) string {
return t.Format("2006.01.02 15:04")
}
// ConvertTimeTostringYear 格式时间返回前台mm-dd yyyy
func ConvertTimeTostringYear(s time.Time) string {
return s.Format("01-02 2006")
}
func Time2TimeStampMilli(s time.Time) string {
return utility.Int64ToString(s.UnixMilli())
}
// GetWeeHours 返回若干天后的凌晨时间戳 若day=0 则返回当天凌晨的时间
// var cstSh, _ = time.LoadLocation("Asia/Shanghai") //上海时区
func GetWeeHours(day int) time.Time {
t := time.Now() // 当前时间
OnHour := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
return OnHour.AddDate(0, 0, day)
// 另一种写法
// return uint(time.Date(t.Year(), t.Month(), t.Day()+day, 0, 0, 0, 0, t.Location()).Unix())
}
// GetMonths 返回若干月后的凌晨时间戳 若months=0 则返回当月凌晨的时间
func GetMonths(months int) time.Time {
t := time.Now() // 当前时间
OnHour := time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, t.Location())
return OnHour.AddDate(0, months, 0)
}
func GetTimeFromStrDate(date string) (year, month, day int) {
d, err := time.Parse(DATEFORMAT, date)
if err != nil {
fmt.Println("出生日期解析错误!")
return 0, 0, 0
}
year = d.Year()
month = int(d.Month())
day = d.Day()
return
}
func GetAge(year int) (age int) {
if year <= 0 {
age = -1
}
nowyear := time.Now().Year()
age = nowyear - year
return
}
// GetWeekDayByNum 根据输入的数字日期返回周XX,字符串隔开的 (0,1,2,3)
func GetWeekDayByNum(weeks string, lang string) []string {
var result []string
if len(weeks) == 0 {
return result
}
weeks = strings.TrimRight(weeks, ",")
arr := strings.Split(weeks, ",")
weekMap := map[string]string{}
switch lang {
case "zh-CN":
weekMap = map[string]string{
"0": "周日",
"1": "周一",
"2": "周二",
"3": "周三",
"4": "周四",
"5": "周五",
"6": "周六",
}
case "zh-HK":
weekMap = map[string]string{
"0": "周日",
"1": "週一",
"2": "週二",
"3": "週三",
"4": "週四",
"5": "週五",
"6": "週六",
}
case "jp":
weekMap = map[string]string{
"0": "日曜日",
"1": "月曜日",
"2": "火曜日",
"3": "水曜日",
"4": "木曜日",
"5": "金曜日",
"6": "土曜日",
}
case "kr":
weekMap = map[string]string{
"0": "일요일",
"1": "월요일",
"2": "화요일",
"3": "수요일",
"4": "목요일",
"5": "금요일",
"6": "토요일",
}
default:
weekMap = map[string]string{
"0": "Sunday",
"1": "Monday",
"2": "Tuesday",
"3": "Wednesday",
"4": "Thursday",
"5": "Friday",
"6": "Saturday",
}
}
for _, a := range arr {
if value, ok := weekMap[a]; ok {
result = append(result, value)
}
}
return result
}
// 时间差计算
func GetDateSub(begin time.Time, end time.Time) time.Duration {
begin1 := GetDateFormat(begin)
end1 := GetDateFormat(end)
return end1.Sub(begin1)
}
// GetDateFormat 格式化日期用来计算时间差
func GetDateFormat(date time.Time) time.Time {
return time.Date(date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second(), 0, time.UTC)
}
// 本周一
func ThisWeek1() time.Time {
// w1 = today - (Weekday+6)%7
today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
factor := time.Duration((today.Weekday()+6)%7) * 24 * time.Hour
return today.Add(-factor)
}
// 本月 1号
func ThisMonthD1() time.Time {
// d1 = today - day - 1
today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
factor := time.Duration(today.Day()-1) * 24 * time.Hour
return today.Add(-factor)
}
// 近 xx 天
func Latest7Day(day int) time.Time {
today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
return today.Add(-(time.Duration(day)) * 24 * time.Hour)
}
func LatestMonth(num int) time.Time {
today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
return today.AddDate(0, -num, 0)
}
// 近 30 天
func Latest30Day() time.Time {
today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
return today.Add(-30 * 24 * time.Hour)
}
func YMDUnix(t time.Time) int64 {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()).Unix()
}

View File

@ -0,0 +1,18 @@
package timehelper
import (
"fmt"
"testing"
"time"
)
func Test_GetWeeHours(t *testing.T) {
fmt.Println(time.Unix(1672129500, 0))
fmt.Println(GetWeeHours(-1))
}
func TestGetTimeFromStrDate(t *testing.T) {
year, _, _ := GetTimeFromStrDate("2022-06-12")
age := GetAge(year)
fmt.Println(age)
}