feat(ik3cloud): 初始化项目基础配置与部门模块
- 添加 .gitignore 忽略规则 - 实现应用配置加载与管理逻辑 - 添加默认配置文件 app.ini - 配置 Gitea CI/CD 工作流用于构建和部署 - 实现金蝶云客户端初始化功能 - 添加 RPC 插件支持 Consul 注册中心 - 实现部门数据获取及树形结构处理逻辑 - 添加通用工具函数库 - 初始化 Go 模块依赖管理 - 创建 Dockerfile 用于服务容器化部署
This commit is contained in:
338
app/common/function.go
Normal file
338
app/common/function.go
Normal file
@@ -0,0 +1,338 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// @Title 随机数种子
|
||||
func init() {
|
||||
// 随机数种子
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// MD5 @Title md5加密
|
||||
func MD5(str string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// RandStr @Title 生成随机数
|
||||
func RandStr(n int, str ...string) string {
|
||||
s := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||
if len(str) > 0 {
|
||||
s = str[0]
|
||||
}
|
||||
res := ""
|
||||
for i := 0; i < n; i++ {
|
||||
res += string(s[rand.Intn(len(s))])
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// InArray @Title 是否在数组中
|
||||
func InArray[T comparable](need T, array []T) bool {
|
||||
for _, item := range array {
|
||||
if item == need {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CreateDateDir 根据当前日期来创建文件夹
|
||||
func CreateDateDir(Path string) string {
|
||||
folderName := time.Now().Format("20060102")
|
||||
folderPath := filepath.Join(Path, folderName)
|
||||
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
|
||||
os.MkdirAll(folderPath, 0755)
|
||||
}
|
||||
return folderName
|
||||
}
|
||||
|
||||
// DownloadFormUrl @Title 下载文件
|
||||
func DownloadFormUrl(src string, filename string) error {
|
||||
res, err := http.Get(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
// 获得get请求响应的reader对象
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(out, bytes.NewReader(body)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utf8ToGbk @Title utf8转gbk
|
||||
func Utf8ToGbk(source string) string {
|
||||
result, _, _ := transform.String(simplifiedchinese.GBK.NewEncoder(), source)
|
||||
return result
|
||||
}
|
||||
|
||||
// GbkToUtf8 @Title gbk转utf8
|
||||
func GbkToUtf8(source string) string {
|
||||
result, _, _ := transform.String(simplifiedchinese.GBK.NewDecoder(), source)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPassword @Title 密码加密
|
||||
func GetPassword(password, salt string) string {
|
||||
return MD5(salt + MD5(password+salt))
|
||||
}
|
||||
|
||||
// IsPhone @Title 是否手机号
|
||||
func IsPhone(phone string) bool {
|
||||
return regexp.MustCompile(`^1\d{10}$`).MatchString(phone)
|
||||
}
|
||||
|
||||
// Equal @TITLE 数据比较
|
||||
func Equal[T any](a, b T, fun func(a, b T) bool) bool {
|
||||
return fun(a, b)
|
||||
}
|
||||
|
||||
// Ptr @TITLE 返回指针
|
||||
func Ptr[T any](data T) *T {
|
||||
return &data
|
||||
}
|
||||
|
||||
// Split @TITLE 字符串分隔
|
||||
func Split(s, sep string) []string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(s, sep)
|
||||
}
|
||||
|
||||
// PtrData @TITLE 指针数据
|
||||
func PtrData[T any](data *T) T {
|
||||
if data != nil {
|
||||
return *data
|
||||
}
|
||||
return *new(T)
|
||||
}
|
||||
|
||||
// PtrDecimal @TITLE Decimal比较数据
|
||||
func PtrDecimal(a *decimal.Decimal, b *decimal.Decimal) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
return a.Cmp(*b) == 0
|
||||
}
|
||||
|
||||
// PtrCmp @TITLE 客户比较数据引用比较
|
||||
func PtrCmp[T comparable](a *T, b *T) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
return *a == *b
|
||||
}
|
||||
|
||||
// DefaultData @TITLE 反射创建默认值
|
||||
func DefaultData(data any) any {
|
||||
var value reflect.Value
|
||||
if reflect.TypeOf(data).Kind() != reflect.Ptr {
|
||||
value = reflect.ValueOf(&data).Elem()
|
||||
} else {
|
||||
value = reflect.ValueOf(data).Elem()
|
||||
}
|
||||
if value.Kind() == reflect.Interface {
|
||||
value = reflect.New(value.Elem().Type()).Elem()
|
||||
}
|
||||
|
||||
labels := map[string]string{}
|
||||
|
||||
typeOf := value.Type()
|
||||
for i := 0; i < typeOf.NumField(); i++ {
|
||||
field := typeOf.Field(i)
|
||||
name := field.Name
|
||||
if field.IsExported() {
|
||||
if !field.Anonymous {
|
||||
labels[name] = field.Tag.Get("label")
|
||||
} else {
|
||||
name = ""
|
||||
}
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
_, valueLabels := DefaultValue(value.Field(i), name)
|
||||
for valueKey, valueLabel := range valueLabels {
|
||||
labels[valueKey] = valueLabel
|
||||
}
|
||||
}
|
||||
if field.Type.Kind() == reflect.Slice {
|
||||
newValue := reflect.New(field.Type.Elem()).Elem()
|
||||
defaultValue, valueLabels := DefaultValue(newValue, name)
|
||||
for valueKey, valueLabel := range valueLabels {
|
||||
labels[valueKey] = valueLabel
|
||||
}
|
||||
value.Field(i).Set(reflect.Append(value.Field(i), defaultValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"Data": value.Interface(),
|
||||
"Label": labels,
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultValue(value reflect.Value, name string) (reflect.Value, map[string]string) {
|
||||
typeOf := value.Type()
|
||||
labels := map[string]string{}
|
||||
if typeOf.Kind() == reflect.Struct {
|
||||
for i := 0; i < typeOf.NumField(); i++ {
|
||||
field := typeOf.Field(i)
|
||||
fieldName := field.Name
|
||||
if field.IsExported() {
|
||||
if !field.Anonymous {
|
||||
labels[strings.Trim(name+"."+fieldName, ".")] = field.Tag.Get("label")
|
||||
} else {
|
||||
fieldName = ""
|
||||
}
|
||||
labels[strings.Trim(name+"."+fieldName, ".")] = field.Tag.Get("label")
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
_, valueLabels := DefaultValue(value.Field(i), fieldName)
|
||||
for valueKey, valueLabel := range valueLabels {
|
||||
labels[strings.Trim(name+"."+valueKey, ".")] = valueLabel
|
||||
}
|
||||
}
|
||||
if field.Type.Kind() == reflect.Slice {
|
||||
newValue := reflect.New(field.Type.Elem()).Elem()
|
||||
defaultValue, valueLabels := DefaultValue(newValue, fieldName)
|
||||
for valueKey, valueLabel := range valueLabels {
|
||||
labels[strings.Trim(name+"."+valueKey, ".")] = valueLabel
|
||||
}
|
||||
value.Field(i).Set(reflect.Append(value.Field(i), defaultValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value, labels
|
||||
}
|
||||
|
||||
func wrapIfStruct[T any](data T) T {
|
||||
v := reflect.ValueOf(data)
|
||||
if v.Kind() == reflect.Struct {
|
||||
ptr := reflect.New(v.Type())
|
||||
ptr.Elem().Set(v)
|
||||
return ptr.Interface().(T)
|
||||
}
|
||||
return data
|
||||
}
|
||||
func DefaultSlice[T any](data T) T {
|
||||
data = wrapIfStruct(data)
|
||||
value := reflect.ValueOf(data).Elem()
|
||||
typeOf := value.Type()
|
||||
|
||||
switch typeOf.Kind() {
|
||||
case reflect.Struct:
|
||||
DefaultSliceValue(value)
|
||||
case reflect.Slice:
|
||||
if value.IsNil() {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
}
|
||||
for j := 0; j < value.Len(); j++ {
|
||||
DefaultSliceValue(value.Index(j))
|
||||
}
|
||||
}
|
||||
|
||||
if reflect.TypeOf(data).Kind() != reflect.Ptr {
|
||||
return value.Interface().(T)
|
||||
} else {
|
||||
return value.Addr().Interface().(T)
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultSliceValue(value reflect.Value) reflect.Value {
|
||||
typeOf := value.Type()
|
||||
if typeOf.Kind() == reflect.Struct {
|
||||
for i := 0; i < typeOf.NumField(); i++ {
|
||||
DefaultSliceType(value.Field(i))
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
func DefaultSliceType(value reflect.Value) reflect.Value {
|
||||
switch value.Type().Kind() {
|
||||
case reflect.Ptr:
|
||||
if !value.IsNil() {
|
||||
DefaultSliceType(value.Elem())
|
||||
} else {
|
||||
valueType := value.Type().Elem()
|
||||
if valueType.Kind() == reflect.Slice {
|
||||
slice := reflect.MakeSlice(valueType, 0, 0)
|
||||
ptr := reflect.New(valueType)
|
||||
ptr.Elem().Set(slice)
|
||||
value.Set(ptr)
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
DefaultSliceValue(value)
|
||||
case reflect.Slice:
|
||||
if value.IsNil() {
|
||||
if value.CanSet() {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
}
|
||||
}
|
||||
for j := 0; j < value.Len(); j++ {
|
||||
DefaultSliceValue(value.Index(j))
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Unique @Title 去重
|
||||
func Unique[T comparable](array []T) (result []T) {
|
||||
for _, item := range array {
|
||||
if !InArray(item, result) {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// InsertSorted @TITLE array插入排序
|
||||
func InsertSorted[T any](array []T, value T, comp func(int) bool) []T {
|
||||
i, j := 0, len(array)
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if !comp(h) {
|
||||
i = h + 1 // preserves f(i-1) == false
|
||||
} else {
|
||||
j = h // preserves f(j) == true
|
||||
}
|
||||
}
|
||||
// 插入:保持有序
|
||||
array = append(array, value) // 增加一个空位
|
||||
copy(array[i+1:], array[i:]) // 后移
|
||||
array[i] = value // 插入值
|
||||
return array
|
||||
}
|
||||
Reference in New Issue
Block a user