|
|
package utils
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
"encoding/json"
|
|
|
"fmt"
|
|
|
"github.com/beego/beego/v2/core/validation"
|
|
|
"github.com/bwmarrin/snowflake"
|
|
|
jsonlib "github.com/linmadan/egglib-go/utils/json"
|
|
|
"github.com/shopspring/decimal"
|
|
|
"golang.org/x/text/encoding/simplifiedchinese"
|
|
|
"golang.org/x/text/transform"
|
|
|
"io"
|
|
|
"io/ioutil"
|
|
|
"reflect"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
func CamelCase(name string, firstUpper bool) string {
|
|
|
array := []byte(name)
|
|
|
if len(array) == 0 {
|
|
|
return ""
|
|
|
}
|
|
|
rspArray := make([]byte, len(array))
|
|
|
if firstUpper {
|
|
|
copy(rspArray[:1], strings.ToUpper(string(array[:1])))
|
|
|
} else {
|
|
|
copy(rspArray[:1], strings.ToLower(string(array[:1])))
|
|
|
}
|
|
|
copy(rspArray[1:], array[1:])
|
|
|
return string(rspArray)
|
|
|
}
|
|
|
|
|
|
func ObjectToMap(o interface{}) map[string]interface{} {
|
|
|
if o == nil {
|
|
|
return nil
|
|
|
}
|
|
|
value := reflect.ValueOf(o)
|
|
|
if value.Kind() != reflect.Ptr {
|
|
|
return nil
|
|
|
}
|
|
|
elem := value.Elem()
|
|
|
relType := elem.Type()
|
|
|
m := make(map[string]interface{})
|
|
|
for i := 0; i < relType.NumField(); i++ {
|
|
|
field := relType.Field(i)
|
|
|
if elem.Field(i).IsZero() {
|
|
|
continue
|
|
|
}
|
|
|
m[CamelCase(field.Name, false)] = elem.Field(i).Interface()
|
|
|
}
|
|
|
return m
|
|
|
}
|
|
|
|
|
|
func ToMap(o interface{}) map[string]interface{} {
|
|
|
if o == nil {
|
|
|
return nil
|
|
|
}
|
|
|
m := make(map[string]interface{})
|
|
|
data, _ := json.Marshal(o)
|
|
|
json.Unmarshal(data, &m)
|
|
|
return m
|
|
|
}
|
|
|
|
|
|
func DeleteMapKeys(options map[string]interface{}, keys ...string) map[string]interface{} {
|
|
|
for i := range keys {
|
|
|
if _, ok := options[keys[i]]; ok {
|
|
|
delete(options, keys[i])
|
|
|
}
|
|
|
}
|
|
|
return options
|
|
|
}
|
|
|
|
|
|
// AssertString convert v to string value
|
|
|
func AssertString(v interface{}) string {
|
|
|
if v == nil {
|
|
|
return ""
|
|
|
}
|
|
|
|
|
|
// if func (v *Type) String() string, we can't use Elem()
|
|
|
switch vt := v.(type) {
|
|
|
case fmt.Stringer:
|
|
|
return vt.String()
|
|
|
}
|
|
|
|
|
|
val := reflect.ValueOf(v)
|
|
|
if val.Kind() == reflect.Ptr && !val.IsNil() {
|
|
|
val = val.Elem()
|
|
|
}
|
|
|
|
|
|
switch vt := val.Interface().(type) {
|
|
|
case bool:
|
|
|
return strconv.FormatBool(vt)
|
|
|
case error:
|
|
|
return vt.Error()
|
|
|
case float32:
|
|
|
return strconv.FormatFloat(float64(vt), 'f', -1, 32)
|
|
|
case float64:
|
|
|
return strconv.FormatFloat(vt, 'f', -1, 64)
|
|
|
case fmt.Stringer:
|
|
|
return vt.String()
|
|
|
case int:
|
|
|
return strconv.Itoa(vt)
|
|
|
case int8:
|
|
|
return strconv.Itoa(int(vt))
|
|
|
case int16:
|
|
|
return strconv.Itoa(int(vt))
|
|
|
case int32:
|
|
|
return strconv.Itoa(int(vt))
|
|
|
case int64:
|
|
|
return strconv.FormatInt(vt, 10)
|
|
|
case string:
|
|
|
return vt
|
|
|
case uint:
|
|
|
return strconv.FormatUint(uint64(vt), 10)
|
|
|
case uint8:
|
|
|
return strconv.FormatUint(uint64(vt), 10)
|
|
|
case uint16:
|
|
|
return strconv.FormatUint(uint64(vt), 10)
|
|
|
case uint32:
|
|
|
return strconv.FormatUint(uint64(vt), 10)
|
|
|
case uint64:
|
|
|
return strconv.FormatUint(vt, 10)
|
|
|
case []byte:
|
|
|
return string(vt)
|
|
|
default:
|
|
|
return fmt.Sprint(val.Interface())
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ValidatePtr validate v is a ptr value
|
|
|
func ValidatePtr(v *reflect.Value) error {
|
|
|
// sequence is very important, IsNil must be called after checking Kind() with reflect.Ptr,
|
|
|
// panic otherwise
|
|
|
if !v.IsValid() || v.Kind() != reflect.Ptr || v.IsNil() {
|
|
|
return fmt.Errorf("not a valid pointer: %v", v)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func LoadCustomFieldToMap(src interface{}, fields ...string) map[string]interface{} {
|
|
|
rsp := LoadCustomField(src, fields...)
|
|
|
if rsp == nil {
|
|
|
return map[string]interface{}{}
|
|
|
}
|
|
|
return rsp.(map[string]interface{})
|
|
|
}
|
|
|
|
|
|
func LoadCustomField(src interface{}, fields ...string) interface{} {
|
|
|
typeSrc := reflect.TypeOf(src)
|
|
|
valueSrc := reflect.ValueOf(src)
|
|
|
|
|
|
if v, ok := src.(reflect.Value); ok {
|
|
|
valueSrc = v
|
|
|
typeSrc = v.Type()
|
|
|
}
|
|
|
if typeSrc.Kind() == reflect.Ptr {
|
|
|
valueSrc = valueSrc.Elem()
|
|
|
}
|
|
|
k := valueSrc.Kind()
|
|
|
switch k {
|
|
|
case reflect.Array, reflect.Slice:
|
|
|
len := valueSrc.Len()
|
|
|
retSliceMap := make([]map[string]interface{}, 0)
|
|
|
if len == 0 {
|
|
|
return retSliceMap
|
|
|
}
|
|
|
for i := 0; i < len; i++ {
|
|
|
v := valueSrc.Index(i)
|
|
|
retSliceMap = append(retSliceMap, (LoadCustomField(v, fields...)).(map[string]interface{}))
|
|
|
}
|
|
|
return retSliceMap
|
|
|
case reflect.Struct:
|
|
|
retSliceMap := make(map[string]interface{})
|
|
|
for _, filed := range fields {
|
|
|
f := valueSrc.FieldByName(filed)
|
|
|
if !f.IsValid() {
|
|
|
continue
|
|
|
}
|
|
|
v := f.Interface()
|
|
|
if t, ok := v.(time.Time); ok {
|
|
|
v = t.Local().Format("2006-01-02 15:04:05")
|
|
|
}
|
|
|
retSliceMap[CamelCase(filed, false)] = v
|
|
|
}
|
|
|
return retSliceMap
|
|
|
default:
|
|
|
return src
|
|
|
}
|
|
|
return src
|
|
|
}
|
|
|
|
|
|
func AppendCustomField(src interface{}, options map[string]interface{}) interface{} {
|
|
|
var mapSrc map[string]interface{}
|
|
|
var ok bool
|
|
|
mapSrc, ok = src.(map[string]interface{})
|
|
|
if !ok {
|
|
|
jsonlib.Unmarshal([]byte(jsonlib.MarshalToString(src)), &mapSrc)
|
|
|
}
|
|
|
for field, value := range options {
|
|
|
mapSrc[CamelCase(field, false)] = value
|
|
|
}
|
|
|
return mapSrc
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
|
|
json 格式化
|
|
|
|
|
|
*/
|
|
|
|
|
|
func Marshal(v interface{}) ([]byte, error) {
|
|
|
return json.Marshal(v)
|
|
|
}
|
|
|
|
|
|
func Unmarshal(data []byte, v interface{}) error {
|
|
|
decoder := json.NewDecoder(bytes.NewReader(data))
|
|
|
if err := unmarshalUseNumber(decoder, v); err != nil {
|
|
|
return formatError(string(data), err)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func UnmarshalFromString(str string, v interface{}) error {
|
|
|
decoder := json.NewDecoder(strings.NewReader(str))
|
|
|
if err := unmarshalUseNumber(decoder, v); err != nil {
|
|
|
return formatError(str, err)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func UnmarshalFromReader(reader io.Reader, v interface{}) error {
|
|
|
var buf strings.Builder
|
|
|
teeReader := io.TeeReader(reader, &buf)
|
|
|
decoder := json.NewDecoder(teeReader)
|
|
|
if err := unmarshalUseNumber(decoder, v); err != nil {
|
|
|
return formatError(buf.String(), err)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func unmarshalUseNumber(decoder *json.Decoder, v interface{}) error {
|
|
|
decoder.UseNumber()
|
|
|
return decoder.Decode(v)
|
|
|
}
|
|
|
|
|
|
func formatError(v string, err error) error {
|
|
|
return fmt.Errorf("string: `%s`, error: `%s`", v, err.Error())
|
|
|
}
|
|
|
|
|
|
type ReflectVal struct {
|
|
|
T reflect.Type
|
|
|
V reflect.Value
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
拷贝当前对象到目标对象,具有相同属性的值
|
|
|
*/
|
|
|
func CopyObject(src, dst interface{}) {
|
|
|
var srcMap = make(map[string]ReflectVal)
|
|
|
|
|
|
vs := reflect.ValueOf(src)
|
|
|
ts := reflect.TypeOf(src)
|
|
|
vd := reflect.ValueOf(dst)
|
|
|
td := reflect.TypeOf(dst)
|
|
|
|
|
|
ls := vs.Elem().NumField()
|
|
|
for i := 0; i < ls; i++ {
|
|
|
srcMap[ts.Elem().Field(i).Name] = ReflectVal{
|
|
|
T: vs.Elem().Field(i).Type(),
|
|
|
V: vs.Elem().Field(i),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
ld := vd.Elem().NumField()
|
|
|
for i := 0; i < ld; i++ {
|
|
|
n := td.Elem().Field(i).Name
|
|
|
t := vd.Elem().Field(i).Type()
|
|
|
if v, ok := srcMap[n]; ok && v.T == t && vd.Elem().Field(i).CanSet() {
|
|
|
vd.Elem().Field(i).Set(v.V)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
时间计算
|
|
|
*/
|
|
|
|
|
|
func ValidWorkTime(t string) error {
|
|
|
ts := strings.Split(t, ":")
|
|
|
if len(ts) != 2 {
|
|
|
return fmt.Errorf("时间格式有误")
|
|
|
}
|
|
|
ts1, err := strconv.Atoi(ts[0])
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("小时格式有误")
|
|
|
}
|
|
|
if !(ts1 < 24 && ts1 >= 0) {
|
|
|
return fmt.Errorf("小时格式有误")
|
|
|
}
|
|
|
ts2, err := strconv.Atoi(ts[1])
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("分钟格式有误")
|
|
|
}
|
|
|
if !(ts2 < 60 && ts2 >= 0) {
|
|
|
return fmt.Errorf("分钟格式有误")
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// 计算两个时间间隔的时间
|
|
|
func ComputeTimeDuration(t1, t2 string) (result time.Duration, err error) {
|
|
|
if err = ValidWorkTime(t1); err != nil {
|
|
|
return
|
|
|
}
|
|
|
if err = ValidWorkTime(t2); err != nil {
|
|
|
return
|
|
|
}
|
|
|
t1s := strings.Split(t1, ":")
|
|
|
t1sHour, _ := strconv.Atoi(t1s[0])
|
|
|
t1sMin, _ := strconv.Atoi(t1s[1])
|
|
|
|
|
|
t2s := strings.Split(t2, ":")
|
|
|
t2sHour, _ := strconv.Atoi(t2s[0])
|
|
|
t2sMin, _ := strconv.Atoi(t2s[1])
|
|
|
var t1t, t2t time.Time
|
|
|
if t1sHour < t2sHour {
|
|
|
t1t = time.Date(2006, 1, 1, t1sHour, t1sMin, 0, 0, time.Local)
|
|
|
t2t = time.Date(2006, 1, 1, t2sHour, t2sMin, 0, 0, time.Local)
|
|
|
} else {
|
|
|
t1t = time.Date(2006, 1, 1, t1sHour, t1sMin, 0, 0, time.Local)
|
|
|
t2t = time.Date(2006, 1, 2, t2sHour, t2sMin, 0, 0, time.Local)
|
|
|
}
|
|
|
ts := t2t.Sub(t1t)
|
|
|
return ts, nil
|
|
|
}
|
|
|
|
|
|
func ToArrayString(inputs []int) []string {
|
|
|
result := make([]string, 0)
|
|
|
for i := range inputs {
|
|
|
result = append(result, strconv.Itoa(inputs[i]))
|
|
|
}
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
func ToArrayInt(inputs []string) []int {
|
|
|
result := make([]int, 0)
|
|
|
for i := range inputs {
|
|
|
v, _ := strconv.Atoi(inputs[i])
|
|
|
result = append(result, v)
|
|
|
}
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
func LoadQueryObject(queryOption map[string]interface{}, obj interface{}) error {
|
|
|
jsonlib.UnmarshalFromString(jsonlib.MarshalToString(queryOption), obj)
|
|
|
validation := validation.Validation{}
|
|
|
result, err := validation.Valid(obj)
|
|
|
if !result && len(validation.Errors) > 0 {
|
|
|
return validation.Errors[0]
|
|
|
}
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// 字符串截取
|
|
|
func SubStr(str string, start, length int) string {
|
|
|
rs := []rune(str)
|
|
|
rl := len(rs)
|
|
|
end := 0
|
|
|
if start < 0 {
|
|
|
start = rl - 1 + start
|
|
|
}
|
|
|
end = start + length
|
|
|
if start > end {
|
|
|
start, end = end, start
|
|
|
}
|
|
|
if start < 0 {
|
|
|
start = 0
|
|
|
}
|
|
|
if start > rl {
|
|
|
start = rl
|
|
|
}
|
|
|
if end < 0 {
|
|
|
end = 0
|
|
|
}
|
|
|
if end > rl {
|
|
|
end = rl
|
|
|
}
|
|
|
return string(rs[start:end])
|
|
|
}
|
|
|
|
|
|
//生成新ID
|
|
|
var snowFlakeNode *snowflake.Node
|
|
|
|
|
|
func NewSnowflakeId() (int64, error) {
|
|
|
if snowFlakeNode == nil {
|
|
|
node, err := snowflake.NewNode(1)
|
|
|
if err != nil {
|
|
|
return 0, err
|
|
|
}
|
|
|
snowFlakeNode = node
|
|
|
}
|
|
|
// Generate a snowflake ID.
|
|
|
id := snowFlakeNode.Generate()
|
|
|
return id.Int64(), nil
|
|
|
}
|
|
|
|
|
|
// Round 保留数值的精度位 四舍五入
|
|
|
func Round(value float64, places int32) float64 {
|
|
|
quantity := decimal.NewFromFloat(value)
|
|
|
d := quantity.Round(places)
|
|
|
rsp, _ := d.Float64()
|
|
|
return rsp
|
|
|
}
|
|
|
|
|
|
// Truncate 截取数值固定长度的 eg:Truncate(99.99,1) Result: 99.9
|
|
|
func Truncate(value float64, places int32) float64 {
|
|
|
quantity := decimal.NewFromFloat(value).Truncate(places)
|
|
|
rsp, _ := quantity.Float64()
|
|
|
return rsp
|
|
|
}
|
|
|
|
|
|
func Utf8ToGbk(s []byte) ([]byte, error) {
|
|
|
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
|
|
|
d, e := ioutil.ReadAll(reader)
|
|
|
if e != nil {
|
|
|
return nil, e
|
|
|
}
|
|
|
return d, nil
|
|
|
}
|
|
|
|
|
|
func GbkToUtf8(s []byte) ([]byte, error) {
|
|
|
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
|
|
|
d, e := ioutil.ReadAll(reader)
|
|
|
if e != nil {
|
|
|
return nil, e
|
|
|
}
|
|
|
return d, nil
|
|
|
} |
...
|
...
|
|