From 8b4637aa3ac0d9f9ffe0acec62947f429e44fadb Mon Sep 17 00:00:00 2001 From: DacongDA Date: Sun, 25 May 2025 21:23:48 +0800 Subject: [PATCH] feat: provide a more complete Excel template for uploading users and fix any bugs (#3831) --- object/user_upload.go | 60 +++---------------------- object/user_util.go | 102 ++++++++++++++++++++++++++++++++++++++++++ xlsx/user_test.xlsx | Bin 10948 -> 13944 bytes 3 files changed, 107 insertions(+), 55 deletions(-) diff --git a/object/user_upload.go b/object/user_upload.go index c4df3d6e..fcbcb074 100644 --- a/object/user_upload.go +++ b/object/user_upload.go @@ -81,62 +81,12 @@ func UploadUsers(owner string, path string) (bool, error) { return false, err } + transUsers, err := StringArrayToUser(table) + if err != nil { + return false, err + } newUsers := []*User{} - for index, line := range table { - line := line - if index == 0 || parseLineItem(&line, 0) == "" { - continue - } - - user := &User{ - Owner: parseLineItem(&line, 0), - Name: parseLineItem(&line, 1), - CreatedTime: parseLineItem(&line, 2), - UpdatedTime: parseLineItem(&line, 3), - Id: parseLineItem(&line, 4), - Type: parseLineItem(&line, 5), - Password: parseLineItem(&line, 6), - PasswordSalt: parseLineItem(&line, 7), - DisplayName: parseLineItem(&line, 8), - FirstName: parseLineItem(&line, 9), - LastName: parseLineItem(&line, 10), - Avatar: parseLineItem(&line, 11), - PermanentAvatar: "", - Email: parseLineItem(&line, 12), - Phone: parseLineItem(&line, 13), - Location: parseLineItem(&line, 14), - Address: []string{parseLineItem(&line, 15)}, - Affiliation: parseLineItem(&line, 16), - Title: parseLineItem(&line, 17), - IdCardType: parseLineItem(&line, 18), - IdCard: parseLineItem(&line, 19), - Homepage: parseLineItem(&line, 20), - Bio: parseLineItem(&line, 21), - Tag: parseLineItem(&line, 22), - Region: parseLineItem(&line, 23), - Language: parseLineItem(&line, 24), - Gender: parseLineItem(&line, 25), - Birthday: parseLineItem(&line, 26), - Education: parseLineItem(&line, 27), - Score: parseLineItemInt(&line, 28), - Karma: parseLineItemInt(&line, 29), - Ranking: parseLineItemInt(&line, 30), - IsDefaultAvatar: false, - IsOnline: parseLineItemBool(&line, 31), - IsAdmin: parseLineItemBool(&line, 32), - IsForbidden: parseLineItemBool(&line, 33), - IsDeleted: parseLineItemBool(&line, 34), - SignupApplication: parseLineItem(&line, 35), - Hash: "", - PreHash: "", - CreatedIp: parseLineItem(&line, 36), - LastSigninTime: parseLineItem(&line, 37), - LastSigninIp: parseLineItem(&line, 38), - Ldap: "", - Properties: map[string]string{}, - DeletedTime: parseLineItem(&line, 39), - } - + for _, user := range transUsers { if _, ok := oldUserMap[user.GetId()]; !ok { newUsers = append(newUsers, user) } diff --git a/object/user_util.go b/object/user_util.go index f55f9715..938f1727 100644 --- a/object/user_util.go +++ b/object/user_util.go @@ -19,12 +19,14 @@ import ( "fmt" "reflect" "regexp" + "strconv" "strings" "github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/util" + "github.com/go-webauthn/webauthn/webauthn" jsoniter "github.com/json-iterator/go" "github.com/xorm-io/core" ) @@ -689,3 +691,103 @@ func IsAppUser(userId string) bool { } return false } + +func setReflectAttr[T any](fieldValue *reflect.Value, fieldString string) error { + unmarshalValue := new(T) + err := json.Unmarshal([]byte(fieldString), unmarshalValue) + if err != nil { + return err + } + + fvElem := fieldValue + fvElem.Set(reflect.ValueOf(*unmarshalValue)) + return nil +} + +func StringArrayToUser(stringArray [][]string) ([]*User, error) { + fieldNames := stringArray[0] + excelMap := []map[string]string{} + userFieldMap := map[string]int{} + + reflectedUser := reflect.TypeOf(User{}) + for i := 0; i < reflectedUser.NumField(); i++ { + userFieldMap[strings.ToLower(reflectedUser.Field(i).Name)] = i + } + + for idx, field := range stringArray { + if idx == 0 { + continue + } + + tempMap := map[string]string{} + for idx, val := range field { + tempMap[fieldNames[idx]] = val + } + excelMap = append(excelMap, tempMap) + } + + users := []*User{} + var err error + + for _, u := range excelMap { + user := User{} + reflectedUser := reflect.ValueOf(&user).Elem() + for k, v := range u { + if v == "" || v == "null" || v == "[]" || v == "{}" { + continue + } + fName := strings.ToLower(strings.ReplaceAll(k, "_", "")) + fieldIdx, ok := userFieldMap[fName] + if !ok { + continue + } + fv := reflectedUser.Field(fieldIdx) + if !fv.IsValid() { + continue + } + switch fv.Kind() { + case reflect.String: + fv.SetString(v) + continue + case reflect.Bool: + fv.SetBool(v == "1") + continue + case reflect.Int: + intVal, err := strconv.Atoi(v) + if err != nil { + return nil, err + } + fv.SetInt(int64(intVal)) + continue + } + + switch fv.Type() { + case reflect.TypeOf([]string{}): + err = setReflectAttr[[]string](&fv, v) + case reflect.TypeOf([]*string{}): + err = setReflectAttr[[]*string](&fv, v) + case reflect.TypeOf([]*FaceId{}): + err = setReflectAttr[[]*FaceId](&fv, v) + case reflect.TypeOf([]*MfaProps{}): + err = setReflectAttr[[]*MfaProps](&fv, v) + case reflect.TypeOf([]*Role{}): + err = setReflectAttr[[]*Role](&fv, v) + case reflect.TypeOf([]*Permission{}): + err = setReflectAttr[[]*Permission](&fv, v) + case reflect.TypeOf([]ManagedAccount{}): + err = setReflectAttr[[]ManagedAccount](&fv, v) + case reflect.TypeOf([]MfaAccount{}): + err = setReflectAttr[[]MfaAccount](&fv, v) + case reflect.TypeOf([]webauthn.Credential{}): + err = setReflectAttr[[]webauthn.Credential](&fv, v) + } + + if err != nil { + return nil, err + } + } + users = append(users, &user) + } + + return users, nil +} diff --git a/xlsx/user_test.xlsx b/xlsx/user_test.xlsx index 95d42962eb9d4bb990411857e996aef1af37017b..da2f435a02ebbc9f52967a37b5d0294f2f6eb7cf 100644 GIT binary patch literal 13944 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