add utils to pkg
This commit is contained in:
@@ -0,0 +1,676 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
randGen *rand.Rand
|
||||
once sync.Once // 保证全局只初始化一次,并发安全
|
||||
)
|
||||
|
||||
func IsMobile(mobile string) bool {
|
||||
// 手机号验证正则表达式(支持国内手机号)
|
||||
phoneRegex := regexp.MustCompile(`^1[3-9]\d{9}$`)
|
||||
return phoneRegex.MatchString(mobile)
|
||||
}
|
||||
|
||||
// 初始化随机数生成器(内部方法,外部无需调用)
|
||||
func initRandGenerator() {
|
||||
// 创建随机数源
|
||||
source := rand.NewSource(time.Now().UnixNano())
|
||||
// 基于源创建生成器
|
||||
randGen = rand.New(source)
|
||||
}
|
||||
|
||||
// GetRandNum 工程化生成四位随机数(0000-9999)
|
||||
func GetRandNum(number int) int {
|
||||
// 首次调用时初始化,后续调用直接复用,无重复初始化开销
|
||||
once.Do(initRandGenerator)
|
||||
// 生成0-9999随机数,格式化补零为4位
|
||||
return randGen.Intn(number)
|
||||
}
|
||||
|
||||
func UniqueId(typeStr string, id int) string {
|
||||
// 格式化补零:%04d 表示数字占4位,不足左侧补0
|
||||
return fmt.Sprintf("%s%010d%s%s", typeStr, id, time.Now().Format("20060102150405"), fmt.Sprintf("%04d", GetRandNum(10000)))
|
||||
}
|
||||
|
||||
// GenerateNickname 生成风格1:形容词+名词+随机数字(例:帅气小猫668)
|
||||
func GenerateNickname() string {
|
||||
var (
|
||||
// 形容词(修饰昵称)
|
||||
adjectives = []string{
|
||||
"帅气", "可爱", "温柔", "霸气", "呆萌", "高冷", "暖心", "潇洒",
|
||||
"元气", "佛系", "硬核", "软萌", "酷飒", "慵懒", "清爽",
|
||||
}
|
||||
// 名词(核心昵称)
|
||||
nouns = []string{
|
||||
"小猫", "小狗", "星辰", "大海", "清风", "明月", "云朵", "小鹿",
|
||||
"枫叶", "晚风", "暖阳", "星辰", "鲸鱼", "兔子", "小熊",
|
||||
}
|
||||
)
|
||||
adj := adjectives[rand.Intn(len(adjectives))]
|
||||
noun := nouns[rand.Intn(len(nouns))]
|
||||
var numStr strings.Builder
|
||||
for i := 0; i < 3; i++ {
|
||||
numStr.WriteByte(byte('0' + rand.Intn(10)))
|
||||
}
|
||||
num := numStr.String() // 2位随机数字
|
||||
return fmt.Sprintf("%s%s%s", adj, noun, num)
|
||||
}
|
||||
|
||||
// MD5Encrypt 封装MD5加密函数:输入字符串,返回32位小写MD5哈希值
|
||||
func MD5Encrypt(str string) string {
|
||||
// 1. 创建MD5哈希对象
|
||||
hash := md5.New()
|
||||
// 2. 写入需要加密的字节流(MD5处理的是字节,需将字符串转[]byte)
|
||||
hash.Write([]byte(str))
|
||||
// 3. 计算哈希值(返回[]byte类型的二进制结果)
|
||||
sum := hash.Sum(nil)
|
||||
// 4. 将二进制结果转为16进制字符串(32位小写)
|
||||
return hex.EncodeToString(sum)
|
||||
}
|
||||
|
||||
func GetEncrypt(key string, nums ...int8) string {
|
||||
original := GetConfigString(key)
|
||||
str := original
|
||||
//TODO 后台切换到新版时开启
|
||||
//for _, num := range nums {
|
||||
// str = str[num:] + str[:num]
|
||||
//}
|
||||
return str
|
||||
}
|
||||
|
||||
func InArray[K int | int8 | uint8 | int64 | string, V any](val K, arr map[K]V) bool {
|
||||
_, ok := arr[val]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ConvertTo 通用类型转换方法
|
||||
// 入参:val 任意类型的值
|
||||
// 出参:T 指定类型的结果,error 转换失败原因(成功时为nil)
|
||||
// 支持类型:int/int64/uint/string/float64/bool 所有基础类型双向转换
|
||||
func ConvertTo[T int | int64 | uint | string | float64 | bool](val interface{}) (T, error) {
|
||||
// 定义返回值的零值,用于转换失败时返回
|
||||
var zero T
|
||||
|
||||
// 入参为nil,直接返回错误
|
||||
if val == nil {
|
||||
return zero, errors.New("convert failed: val is nil")
|
||||
}
|
||||
|
||||
// 核心:类型断言+匹配,实现任意类型转指定类型T
|
||||
switch t := val.(type) {
|
||||
// 入参类型与返回类型一致,直接返回
|
||||
case T:
|
||||
return t, nil
|
||||
|
||||
// 入参为int,转换为目标类型T
|
||||
case int:
|
||||
return convertIntTo[T](t)
|
||||
// 入参为int64,转换为目标类型T
|
||||
case int64:
|
||||
return convertInt64To[T](t)
|
||||
// 入参为string,转换为目标类型T
|
||||
case string:
|
||||
return convertStringTo[T](t)
|
||||
// 入参为float64,转换为目标类型T
|
||||
case float64:
|
||||
return convertFloat64To[T](t)
|
||||
// 入参为bool,转换为目标类型T
|
||||
case bool:
|
||||
return convertBoolTo[T](t)
|
||||
// 入参为uint,转换为目标类型T
|
||||
case uint:
|
||||
return convertUintTo[T](t)
|
||||
|
||||
// 不支持的入参类型
|
||||
default:
|
||||
return zero, fmt.Errorf("convert failed: unsupport input type %T, target type %T", val, zero)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------ 基础类型转换子方法 ------------------------------
|
||||
// convertIntTo int转指定类型T
|
||||
func convertIntTo[T int | int64 | uint | string | float64 | bool](val int) (T, error) {
|
||||
var zero T
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
return any(val).(T), nil
|
||||
case int64:
|
||||
return any(int64(val)).(T), nil
|
||||
case uint:
|
||||
return any(uint(val)).(T), nil
|
||||
case string:
|
||||
return any(strconv.Itoa(val)).(T), nil
|
||||
case float64:
|
||||
return any(float64(val)).(T), nil
|
||||
case bool:
|
||||
return any(val != 0).(T), nil
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupport convert int to %T", zero)
|
||||
}
|
||||
}
|
||||
|
||||
// convertInt64To int64转指定类型T
|
||||
func convertInt64To[T int | int64 | uint | string | float64 | bool](val int64) (T, error) {
|
||||
var zero T
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
return any(int(val)).(T), nil
|
||||
case int64:
|
||||
return any(val).(T), nil
|
||||
case uint:
|
||||
return any(uint(val)).(T), nil
|
||||
case string:
|
||||
return any(strconv.FormatInt(val, 10)).(T), nil
|
||||
case float64:
|
||||
return any(float64(val)).(T), nil
|
||||
case bool:
|
||||
return any(val != 0).(T), nil
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupport convert int64 to %T", zero)
|
||||
}
|
||||
}
|
||||
|
||||
// convertUintTo uint转指定类型T
|
||||
func convertUintTo[T int | int64 | uint | string | float64 | bool](val uint) (T, error) {
|
||||
var zero T
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
return any(int(val)).(T), nil
|
||||
case int64:
|
||||
return any(int64(val)).(T), nil
|
||||
case uint:
|
||||
return any(val).(T), nil
|
||||
case string:
|
||||
return any(strconv.FormatUint(uint64(val), 10)).(T), nil
|
||||
case float64:
|
||||
return any(float64(val)).(T), nil
|
||||
case bool:
|
||||
return any(val != 0).(T), nil
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupport convert uint to %T", zero)
|
||||
}
|
||||
}
|
||||
|
||||
// convertFloat64To float64转指定类型T(注意:转整型会截断小数)
|
||||
func convertFloat64To[T int | int64 | uint | string | float64 | bool](val float64) (T, error) {
|
||||
var zero T
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
return any(int(val)).(T), nil
|
||||
case int64:
|
||||
return any(int64(val)).(T), nil
|
||||
case uint:
|
||||
return any(uint(val)).(T), nil
|
||||
case string:
|
||||
return any(strconv.FormatFloat(val, 'f', -1, 64)).(T), nil
|
||||
case float64:
|
||||
return any(val).(T), nil
|
||||
case bool:
|
||||
return any(val != 0).(T), nil
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupport convert float64 to %T", zero)
|
||||
}
|
||||
}
|
||||
|
||||
// convertBoolTo bool转指定类型T(true→1/"true",false→0/"false")
|
||||
func convertBoolTo[T int | int64 | uint | string | float64 | bool](val bool) (T, error) {
|
||||
var zero T
|
||||
var intVal int = 0
|
||||
var strVal string = "false"
|
||||
if val {
|
||||
intVal = 1
|
||||
strVal = "true"
|
||||
}
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
return any(intVal).(T), nil
|
||||
case int64:
|
||||
return any(int64(intVal)).(T), nil
|
||||
case uint:
|
||||
return any(uint(intVal)).(T), nil
|
||||
case string:
|
||||
return any(strVal).(T), nil
|
||||
case float64:
|
||||
return any(float64(intVal)).(T), nil
|
||||
case bool:
|
||||
return any(val).(T), nil
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupport convert bool to %T", zero)
|
||||
}
|
||||
}
|
||||
|
||||
// convertStringTo string转指定类型T(需保证字符串格式合法,否则转换失败)
|
||||
func convertStringTo[T int | int64 | uint | string | float64 | bool](val string) (T, error) {
|
||||
var zero T
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
num, err := strconv.Atoi(val)
|
||||
return any(num).(T), err
|
||||
case int64:
|
||||
num, err := strconv.ParseInt(val, 10, 64)
|
||||
return any(num).(T), err
|
||||
case uint:
|
||||
num, err := strconv.ParseUint(val, 10, 64)
|
||||
return any(uint(num)).(T), err
|
||||
case string:
|
||||
return any(val).(T), nil
|
||||
case float64:
|
||||
num, err := strconv.ParseFloat(val, 64)
|
||||
return any(num).(T), err
|
||||
case bool:
|
||||
b, err := strconv.ParseBool(val)
|
||||
return any(b).(T), err
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupport convert string to %T", zero)
|
||||
}
|
||||
}
|
||||
|
||||
func StringToFloat64(val string) float64 {
|
||||
n, _ := convertStringTo[float64](strings.TrimSpace(val))
|
||||
return n
|
||||
}
|
||||
|
||||
func RoundMoney(amount float64) float64 {
|
||||
return math.Round(amount*100) / 100
|
||||
}
|
||||
|
||||
func Decrypt(str string) (string, Error) {
|
||||
str, err := Crypto{}.AESDecryptECB(str)
|
||||
if err != nil {
|
||||
Logger.Error("decrypt str error", str, err)
|
||||
return str, err
|
||||
}
|
||||
|
||||
// 去掉乱码
|
||||
str = strings.TrimRight(str, "\x00\x01\x02\x03\x04\x05\x06\x07\x08")
|
||||
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func DecryptMobile(mobile string) string {
|
||||
|
||||
mobile, err := Decrypt(mobile)
|
||||
if err != nil {
|
||||
Logger.Error("decrypt mobile error", mobile, err)
|
||||
return mobile
|
||||
}
|
||||
|
||||
if len(mobile) < 11 {
|
||||
return mobile
|
||||
}
|
||||
|
||||
return mobile[:3] + "****" + mobile[7:11]
|
||||
}
|
||||
|
||||
// 接收 [startDate, endDate](格式 "YYYY-MM-DD"),
|
||||
// 返回 [2006-01-02 00:00:00, 2006-01-02 23:59:59]
|
||||
func DateRangeWithTime(dates []string) [2]string {
|
||||
var result [2]string
|
||||
if len(dates) != 2 {
|
||||
return result
|
||||
}
|
||||
|
||||
layout := "2006-01-02"
|
||||
startDate, err1 := time.Parse(layout, dates[0])
|
||||
endDate, err2 := time.Parse(layout, dates[1])
|
||||
if err1 != nil || err2 != nil {
|
||||
// 出错就原样返回前端输入
|
||||
result[0] = dates[0]
|
||||
result[1] = dates[1]
|
||||
return result
|
||||
}
|
||||
|
||||
result[0] = startDate.Format("2006-01-02") + " 00:00:00"
|
||||
result[1] = endDate.Format("2006-01-02") + " 23:59:59"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// CalcAge 根据出生日期计算年龄
|
||||
func CalcAgeStr(birthday string) string {
|
||||
|
||||
if birthday == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
layout := "2006-01-02"
|
||||
birth, err := time.Parse(layout, birthday)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
age := now.Year() - birth.Year()
|
||||
|
||||
if now.Month() < birth.Month() ||
|
||||
(now.Month() == birth.Month() && now.Day() < birth.Day()) {
|
||||
age--
|
||||
}
|
||||
|
||||
if age < 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strconv.Itoa(age)
|
||||
}
|
||||
|
||||
func MaskData(data string, first, end uint8) string {
|
||||
// 2. 截取前3位 + **** + 后4位
|
||||
// Go字符串按字节截取,数字字符单字节,直接切片即可
|
||||
return data[:first] + "****" + data[end:]
|
||||
}
|
||||
|
||||
func RandString(num uint8) string {
|
||||
chars := "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ"
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
res := make([]byte, num)
|
||||
for i := range res {
|
||||
res[i] = chars[rand.Intn(len(chars))]
|
||||
}
|
||||
return string(res)
|
||||
}
|
||||
|
||||
// ExcelColumn 定义导出的列:表头 + 对应结构体字段
|
||||
type ExcelColumn struct {
|
||||
Header string // 表头显示名称
|
||||
Field string // 结构体字段名(必须大写)
|
||||
Width float64 //宽
|
||||
IsImage bool //是否是图片
|
||||
}
|
||||
|
||||
func ExcelExport(c *gin.Context, fileName string, columns []ExcelColumn, dataList interface{}) error {
|
||||
f := excelize.NewFile()
|
||||
sheet := "Sheet1"
|
||||
f.SetSheetName("Sheet1", sheet)
|
||||
|
||||
// 自动换行样式
|
||||
wrapStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Alignment: &excelize.Alignment{
|
||||
WrapText: true,
|
||||
Vertical: "top",
|
||||
},
|
||||
})
|
||||
|
||||
// 写入表头
|
||||
for i, col := range columns {
|
||||
colName, _ := excelize.ColumnNumberToName(i + 1)
|
||||
_ = f.SetCellValue(sheet, colName+"1", col.Header)
|
||||
}
|
||||
|
||||
items := reflect.ValueOf(dataList)
|
||||
if items.Kind() != reflect.Slice {
|
||||
return f.Write(c.Writer)
|
||||
}
|
||||
|
||||
// 遍历行
|
||||
for rowIdx := 0; rowIdx < items.Len(); rowIdx++ {
|
||||
rowNum := rowIdx + 2
|
||||
item := items.Index(rowIdx).Interface()
|
||||
val := reflect.ValueOf(item)
|
||||
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
// 遍历列
|
||||
for colIdx, col := range columns {
|
||||
fieldVal := getFieldValue(val, col.Field)
|
||||
if !fieldVal.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
cellName, _ := excelize.ColumnNumberToName(colIdx + 1)
|
||||
cellPos := cellName + strconv.Itoa(rowNum)
|
||||
|
||||
// ======================================
|
||||
// 图片URL → 改为【点击查看图片】链接
|
||||
// ======================================
|
||||
if col.IsImage {
|
||||
imgURL := toStr(fieldVal.Interface())
|
||||
if imgURL != "" {
|
||||
// 设置超链接(显示文字:点击查看图片,真正链接:imgURL)
|
||||
_ = f.SetCellHyperLink(sheet, cellPos, imgURL, "External")
|
||||
// 单元格显示文字
|
||||
_ = f.SetCellValue(sheet, cellPos, "点击查看")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// ======================================
|
||||
// 文本/数组 自动换行
|
||||
// ======================================
|
||||
var cellValue interface{}
|
||||
kind := fieldVal.Kind()
|
||||
if kind == reflect.Slice || kind == reflect.Array {
|
||||
var lines []string
|
||||
for i := 0; i < fieldVal.Len(); i++ {
|
||||
lines = append(lines, toStr(fieldVal.Index(i).Interface()))
|
||||
}
|
||||
cellValue = strings.Join(lines, "\n")
|
||||
} else {
|
||||
cellValue = fieldVal.Interface()
|
||||
}
|
||||
|
||||
_ = f.SetCellValue(sheet, cellPos, cellValue)
|
||||
_ = f.SetCellStyle(sheet, cellPos, cellPos, wrapStyle)
|
||||
}
|
||||
|
||||
_ = f.SetRowHeight(sheet, rowNum, 30)
|
||||
}
|
||||
|
||||
// 设置列宽
|
||||
for colIdx, col := range columns {
|
||||
if col.Width <= 0 {
|
||||
continue
|
||||
}
|
||||
colName, _ := excelize.ColumnNumberToName(colIdx + 1)
|
||||
_ = f.SetColWidth(sheet, colName, colName, col.Width)
|
||||
}
|
||||
|
||||
// 下载
|
||||
c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
c.Header("Content-Disposition", "attachment; filename="+fileName+".xlsx")
|
||||
return f.Write(c.Writer)
|
||||
}
|
||||
|
||||
// 嵌套字段取值
|
||||
func getFieldValue(val reflect.Value, field string) reflect.Value {
|
||||
fields := strings.Split(field, ".")
|
||||
current := val
|
||||
for _, f := range fields {
|
||||
if current.Kind() == reflect.Ptr {
|
||||
current = current.Elem()
|
||||
}
|
||||
if !current.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
current = current.FieldByName(f)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// 通用转字符串
|
||||
func toStr(v interface{}) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
// Unique 去重函数:传入切片,返回去重后的新切片
|
||||
func Unique(s []string) []string {
|
||||
tempMap := make(map[string]bool) // 用map记录是否存在
|
||||
res := []string{}
|
||||
|
||||
for _, item := range s {
|
||||
if !tempMap[item] { // 不存在才添加
|
||||
tempMap[item] = true
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// MapToStruct 将 map 转换为 struct(支持 map[string]interface{} 转任意 struct)
|
||||
// 参数:
|
||||
//
|
||||
// data: 源 map(key 为字符串,value 为任意类型)
|
||||
// target: 目标 struct 指针(必须传指针,否则无法赋值
|
||||
func MapToStruct(data map[string]interface{}, target interface{}) error {
|
||||
jsonData, err := json.Marshal(data) // 将 map 转换为 JSON 字节切片
|
||||
if err != nil {
|
||||
Logger.Error("Error marshalling map to JSON:", err)
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(jsonData, &target) // 将 JSON 字节切片解析到结构体中
|
||||
if err != nil {
|
||||
Logger.Error("Error unmarshalling JSON:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func JsonStringToStruct(jsonStr string, v interface{}) bool {
|
||||
// 1. 校验入参:确保v是指针类型(否则json.Unmarshal无法工作)
|
||||
if v == nil {
|
||||
Logger.Error("传入的结构体指针不能为nil")
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. 将JSON字符串转为字节数组,执行解析
|
||||
err := jsoniter.Unmarshal([]byte(jsonStr), v)
|
||||
if err != nil {
|
||||
Logger.Error("JSON解析失败: ", err, jsonStr)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TimeToStartAndEnd(timeString string, day int) (string, string) {
|
||||
if len(timeString) > 0 {
|
||||
tmpStr := strings.Split(timeString, " - ")
|
||||
if len(tmpStr) > 1 {
|
||||
startTime := TimeStart(day, tmpStr[0])
|
||||
endTime := TimeEnd(day, tmpStr[1])
|
||||
if len(startTime) > 0 && len(endTime) > 0 {
|
||||
return startTime, endTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func StructToJson(data interface{}) string {
|
||||
bytes, err := jsoniter.Marshal(data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func GetStringByMap(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func BuildImageURL(data interface{}) interface{} {
|
||||
switch v := data.(type) {
|
||||
|
||||
case map[string]interface{}:
|
||||
for key, val := range v {
|
||||
v[key] = BuildImageURL(val)
|
||||
}
|
||||
return v
|
||||
|
||||
case []interface{}:
|
||||
for i, val := range v {
|
||||
v[i] = BuildImageURL(val)
|
||||
}
|
||||
return v
|
||||
|
||||
case string:
|
||||
if v == "" {
|
||||
return v
|
||||
}
|
||||
base := GetConfigString("upload.image_base_url")
|
||||
return fmt.Sprintf("%s/%s", strings.TrimRight(base, "/"), strings.TrimLeft(v, "/"))
|
||||
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func ParseIntArray(value string) ([]int, error) {
|
||||
|
||||
var result []int
|
||||
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
if value == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(
|
||||
[]byte(value),
|
||||
&result,
|
||||
)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func ParseStringArray(value string) ([]string, error) {
|
||||
|
||||
var result []string
|
||||
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
if value == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(
|
||||
[]byte(value),
|
||||
&result,
|
||||
)
|
||||
|
||||
return result, err
|
||||
}
|
||||
func UniqueStr(arr []string) []string {
|
||||
m := make(map[string]struct{})
|
||||
res := make([]string, 0, len(arr))
|
||||
for _, v := range arr {
|
||||
// 修正这里:用ok接收是否存在
|
||||
if _, ok := m[v]; !ok {
|
||||
m[v] = struct{}{}
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user