design pkg again

This commit is contained in:
Hoang 2021-11-10 08:44:22 +07:00
parent efa28749de
commit c082388d64
21 changed files with 707 additions and 624 deletions

30
action.go Normal file
View File

@ -0,0 +1,30 @@
package usermngmt
import "github.com/Selly-Modules/usermngmt/internal"
// Create ...
func (s Service) Create(payload internal.CreateOptions) error {
return s.userHandle().Create(payload)
}
// Update ...
func (s Service) Update(userID string, payload internal.UpdateOptions) error {
return s.userHandle().UpdateByUserID(userID, payload)
}
// ChangeUserPassword ...
func (s Service) ChangeUserPassword(userID string, payload internal.ChangePasswordOptions) error {
return s.userHandle().ChangeUserPassword(userID, payload)
}
func (s Service) ChangeUserStatus(userID, newStatus string) error {
return s.userHandle().ChangeUserStatus(userID, newStatus)
}
func (s Service) All(query internal.AllQuery) internal.UserAll {
return s.userHandle().All(query)
}
func (s Service) RoleCreate(payload internal.RoleCreateOptions) error {
return s.roleHandle().Create(payload)
}

View File

@ -1,61 +0,0 @@
package usermngmt
import (
"context"
"github.com/Selly-Modules/mongodb"
)
// CreateOptions ...
type CreateOptions struct {
Name string
Phone string
Email string
Password string
Status string
RoleID string
Other string
}
// Create ...
func (s Service) Create(payload CreateOptions) error {
var (
ctx = context.Background()
)
// Validate payload
if err := payload.validate(ctx); err != nil {
return err
}
// New user data from payload
doc, err := payload.newUser()
if err != nil {
return err
}
// Create user
if err = s.userCreate(ctx, doc); err != nil {
return err
}
return nil
}
func (payload CreateOptions) newUser() (result dbUser, err error) {
timeNow := now()
roleID, _ := mongodb.NewIDFromString(payload.RoleID)
return dbUser{
ID: mongodb.NewObjectID(),
Name: payload.Name,
SearchString: getSearchString(payload.Name, payload.Phone, payload.Email),
Phone: payload.Phone,
Email: payload.Email,
HashedPassword: hashPassword(payload.Password),
Status: payload.Status,
RoleID: roleID,
Other: payload.Other,
CreatedAt: timeNow,
UpdatedAt: timeNow,
}, nil
}

View File

@ -1,82 +0,0 @@
package usermngmt
import (
"context"
"sync"
"go.mongodb.org/mongo-driver/bson"
)
// AllQuery ...
type AllQuery struct {
Page int64
Limit int64
Keyword string
RoleID string
Status string
}
// All ...
func (s Service) All(queryParams AllQuery) (r UserAll) {
var (
ctx = context.Background()
wg sync.WaitGroup
cond = bson.M{}
)
query := commonQuery{
Page: queryParams.Page,
Limit: queryParams.Limit,
Keyword: queryParams.Keyword,
RoleID: queryParams.RoleID,
Status: queryParams.Status,
Sort: bson.M{"createdAt": -1},
}
// Assign condition
query.SetDefaultLimit()
query.AssignKeyword(cond)
query.AssignRoleID(cond)
query.AssignStatus(cond)
wg.Add(1)
go func() {
defer wg.Done()
docs := s.userFindByCondition(ctx, cond, query.GetFindOptionsUsingPage())
r.List = getResponseList(ctx, docs)
}()
wg.Add(1)
go func() {
defer wg.Done()
r.Total = s.userCountByCondition(ctx, cond)
}()
wg.Wait()
return
}
func getResponseList(ctx context.Context, users []dbUser) []User {
res := make([]User, 0)
for _, user := range users {
role, _ := s.roleFindByID(ctx, user.RoleID)
res = append(res, User{
ID: user.ID.Hex(),
Name: user.Name,
Phone: user.Phone,
Email: user.Email,
Status: user.Status,
Role: RoleShort{
ID: role.ID.Hex(),
Name: role.Name,
IsAdmin: role.IsAdmin,
},
Other: user.Other,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
return res
}

View File

@ -1,125 +0,0 @@
package usermngmt
import (
"context"
"errors"
"github.com/Selly-Modules/mongodb"
"go.mongodb.org/mongo-driver/bson"
)
// UpdateOptions ...
type UpdateOptions struct {
Name string
Phone string
Email string
RoleID string
Other string
}
// ChangePasswordOptions ...
type ChangePasswordOptions struct {
OldPassword string
NewPassword string
}
// UpdateByUserID ...
func (s Service) UpdateByUserID(userID string, payload UpdateOptions) error {
var (
ctx = context.Background()
)
// Validate payload
if err := payload.validate(ctx); err != nil {
return err
}
// Setup condition
id, _ := mongodb.NewIDFromString(userID)
cond := bson.M{
"_id": id,
}
// Setup update data
roleID, _ := mongodb.NewIDFromString(payload.RoleID)
updateData := bson.M{
"$set": bson.M{
"name": payload.Name,
"searchString": getSearchString(payload.Name, payload.Phone, payload.Email),
"phone": payload.Phone,
"email": payload.Email,
"roleId": roleID,
"other": payload.Other,
"updatedAt": now(),
},
}
// Update
if err := s.userUpdateOneByCondition(ctx, cond, updateData); err != nil {
return err
}
return nil
}
// ChangeUserPassword ...
func (s Service) ChangeUserPassword(userID string, opt ChangePasswordOptions) error {
var (
ctx = context.Background()
)
// Validate payload
err := opt.validate(userID)
if err != nil {
return err
}
// Find user
id, _ := mongodb.NewIDFromString(userID)
user, _ := s.userFindByID(ctx, id)
if user.ID.IsZero() {
return errors.New("user not found")
}
// Check old password
if isValid := checkPasswordHash(opt.OldPassword, user.HashedPassword); !isValid {
return errors.New("the password is incorrect")
}
// Update password
if err = s.userUpdateOneByCondition(ctx, bson.M{"_id": user.ID}, bson.M{
"$set": bson.M{
"hashedPassword": hashPassword(opt.NewPassword),
"updatedAt": now(),
},
}); err != nil {
return err
}
return nil
}
// ChangeUserStatus ...
func (s Service) ChangeUserStatus(userID, newStatus string) error {
var (
ctx = context.Background()
)
// Validate userID
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
return errors.New("invalid user id data")
}
// Update status
if err := s.userUpdateOneByCondition(ctx, bson.M{"_id": id}, bson.M{
"$set": bson.M{
"status": newStatus,
"updatedAt": now(),
},
}); err != nil {
return err
}
return nil
}

View File

@ -5,8 +5,4 @@ const (
tableUser = "users" tableUser = "users"
tableRole = "roles" tableRole = "roles"
tablePrefixDefault = "usermngmt" tablePrefixDefault = "usermngmt"
timezoneHCM = "Asia/Ho_Chi_Minh"
passwordHashingCost = 14
) )

148
db.go
View File

@ -1,14 +1,9 @@
package usermngmt package usermngmt
import ( import (
"context"
"fmt" "fmt"
"github.com/Selly-Modules/logger"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
) )
// getUserCollection ... // getUserCollection ...
@ -20,146 +15,3 @@ func (s Service) getUserCollection() *mongo.Collection {
func (s Service) getRoleCollection() *mongo.Collection { func (s Service) getRoleCollection() *mongo.Collection {
return s.DB.Collection(fmt.Sprintf("%s-%s", s.TablePrefix, tableRole)) return s.DB.Collection(fmt.Sprintf("%s-%s", s.TablePrefix, tableRole))
} }
func (s Service) isPhoneNumberOrEmailExisted(ctx context.Context, phone, email string) bool {
var (
col = s.getUserCollection()
)
// Find
cond := bson.M{
"$or": []bson.M{
{
"phone": phone,
},
{
"email": email,
},
},
}
total, err := col.CountDocuments(ctx, cond)
if err != nil {
logger.Error("usermngmt - countUserByCondition", logger.LogData{
"condition": cond,
"err": err.Error(),
})
return true
}
return total != 0
}
func (s Service) isRoleIDExisted(ctx context.Context, roleID primitive.ObjectID) bool {
var (
col = s.getRoleCollection()
)
// Find
cond := bson.M{
"_id": roleID,
}
total, err := col.CountDocuments(ctx, cond)
if err != nil {
logger.Error("usermngmt - countRoleByCondition", logger.LogData{
"condition": cond,
"err": err.Error(),
})
return false
}
return total != 0
}
func (s Service) userCreate(ctx context.Context, doc dbUser) error {
var (
col = s.getUserCollection()
)
_, err := col.InsertOne(ctx, doc)
if err != nil {
logger.Error("usermngmt - Create", logger.LogData{
"doc": doc,
"err": err.Error(),
})
return fmt.Errorf("error when create user: %s", err.Error())
}
return nil
}
func (s Service) userUpdateOneByCondition(ctx context.Context, cond interface{}, payload interface{}) error {
var (
col = s.getUserCollection()
)
_, err := col.UpdateOne(ctx, cond, payload)
if err != nil {
logger.Error("usermngmt - Update", logger.LogData{
"cond": cond,
"payload": payload,
"err": err.Error(),
})
return fmt.Errorf("error when update user: %s", err.Error())
}
return err
}
func (s Service) userFindByID(ctx context.Context, id primitive.ObjectID) (dbUser, error) {
var (
col = s.getUserCollection()
doc dbUser
)
err := col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
return doc, err
}
func (s Service) userFindByCondition(ctx context.Context, cond interface{}, opts ...*options.FindOptions) (docs []dbUser) {
var (
col = s.getUserCollection()
)
docs = make([]dbUser, 0)
cursor, err := col.Find(ctx, cond, opts...)
if err != nil {
logger.Error("usermngmt - All", logger.LogData{
"cond": cond,
"opts": opts,
"err": err.Error(),
})
return
}
defer cursor.Close(ctx)
if err = cursor.All(ctx, &docs); err != nil {
logger.Error("usermngmt - All - decode", logger.LogData{
"cond": cond,
"opts": opts,
"err": err.Error(),
})
return
}
return
}
// userCountByCondition ...
func (s Service) userCountByCondition(ctx context.Context, cond interface{}) int64 {
var (
col = s.getUserCollection()
)
total, err := col.CountDocuments(ctx, cond)
if err != nil {
logger.Error("usermngmt - Count", logger.LogData{
"err": err.Error(),
"cond": cond,
})
}
return total
}
func (s Service) roleFindByID(ctx context.Context, id primitive.ObjectID) (dbRole, error) {
var (
col = s.getRoleCollection()
doc dbRole
)
err := col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
return doc, err
}

2
go.mod
View File

@ -1,4 +1,4 @@
module github.com/selly-Modules/usermngmt module github.com/Selly-Modules/usermngmt
go 1.17 go 1.17

21
init_handle.go Normal file
View File

@ -0,0 +1,21 @@
package usermngmt
import (
"github.com/Selly-Modules/usermngmt/role"
"github.com/Selly-Modules/usermngmt/user"
)
// userHandle ...
func (s Service) userHandle() user.Handle {
return user.Handle{
Col: s.getUserCollection(),
RoleCol: s.getRoleCollection(),
}
}
// roleHandle ...
func (s Service) roleHandle() role.Handle {
return role.Handle{
Col: s.getRoleCollection(),
}
}

9
internal/constant.go Normal file
View File

@ -0,0 +1,9 @@
package internal
// Constant ...
const (
timezoneHCM = "Asia/Ho_Chi_Minh"
passwordHashingCost = 14
)

View File

@ -1,4 +1,4 @@
package usermngmt package internal
import ( import (
"time" "time"
@ -6,8 +6,18 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
) )
// dbUser ... // DBRole ...
type dbUser struct { type DBRole struct {
ID primitive.ObjectID `bson:"_id" json:"_id"`
Name string `bson:"name" json:"name"`
Code string `bson:"code" json:"code"`
IsAdmin bool `bson:"isAdmin" json:"isAdmin"`
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}
// DBUser ...
type DBUser struct {
ID primitive.ObjectID `bson:"_id"` ID primitive.ObjectID `bson:"_id"`
Name string `bson:"name"` Name string `bson:"name"`
SearchString string `bson:"searchString"` SearchString string `bson:"searchString"`
@ -21,12 +31,3 @@ type dbUser struct {
UpdatedAt time.Time `bson:"updatedAt"` UpdatedAt time.Time `bson:"updatedAt"`
} }
// dbRole ...
type dbRole struct {
ID primitive.ObjectID `bson:"_id" json:"_id"`
Name string `bson:"name" json:"name"`
Code string `bson:"code" json:"code"`
IsAdmin bool `bson:"isAdmin" json:"isAdmin"`
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}

View File

@ -1,4 +1,4 @@
package usermngmt package internal
import ( import (
"fmt" "fmt"
@ -7,17 +7,17 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
func hashPassword(password string) string { func HashPassword(password string) string {
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), passwordHashingCost) bytes, _ := bcrypt.GenerateFromPassword([]byte(password), passwordHashingCost)
return string(bytes) return string(bytes)
} }
func checkPasswordHash(password, hash string) bool { func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil return err == nil
} }
func getSearchString(fieldList ...string) string { func GetSearchString(fieldList ...string) string {
var ( var (
searchList = make([]interface{}, 0) searchList = make([]interface{}, 0)
format = "" format = ""
@ -33,3 +33,4 @@ func getSearchString(fieldList ...string) string {
} }
return fmt.Sprintf(format, searchList...) return fmt.Sprintf(format, searchList...)
} }

View File

@ -1,4 +1,4 @@
package usermngmt package internal
import ( import (
"github.com/Selly-Modules/mongodb" "github.com/Selly-Modules/mongodb"
@ -6,7 +6,7 @@ import (
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
) )
type commonQuery struct { type CommonQuery struct {
Page int64 Page int64
Limit int64 Limit int64
Keyword string Keyword string
@ -16,14 +16,14 @@ type commonQuery struct {
} }
// AssignKeyword ... // AssignKeyword ...
func (q *commonQuery) AssignKeyword(cond bson.M) { func (q *CommonQuery) AssignKeyword(cond bson.M) {
if q.Keyword != "" { if q.Keyword != "" {
cond["searchString"] = mongodb.GenerateQuerySearchString(q.Keyword) cond["searchString"] = mongodb.GenerateQuerySearchString(q.Keyword)
} }
} }
// AssignRoleID ... // AssignRoleID ...
func (q *commonQuery) AssignRoleID(cond bson.M) { func (q *CommonQuery) AssignRoleID(cond bson.M) {
if q.RoleID != "" { if q.RoleID != "" {
if id, isValid := mongodb.NewIDFromString(q.RoleID); isValid { if id, isValid := mongodb.NewIDFromString(q.RoleID); isValid {
cond["roleId"] = id cond["roleId"] = id
@ -32,14 +32,14 @@ func (q *commonQuery) AssignRoleID(cond bson.M) {
} }
// AssignStatus ... // AssignStatus ...
func (q *commonQuery) AssignStatus(cond bson.M) { func (q *CommonQuery) AssignStatus(cond bson.M) {
if q.Status != "" { if q.Status != "" {
cond["status"] = q.Status cond["status"] = q.Status
} }
} }
// GetFindOptionsUsingPage ... // GetFindOptionsUsingPage ...
func (q *commonQuery) GetFindOptionsUsingPage() *options.FindOptions { func (q *CommonQuery) GetFindOptionsUsingPage() *options.FindOptions {
opts := options.Find() opts := options.Find()
if q.Limit > 0 { if q.Limit > 0 {
opts.SetLimit(q.Limit).SetSkip(q.Limit * q.Page) opts.SetLimit(q.Limit).SetSkip(q.Limit * q.Page)
@ -51,8 +51,9 @@ func (q *commonQuery) GetFindOptionsUsingPage() *options.FindOptions {
} }
// SetDefaultLimit ... // SetDefaultLimit ...
func (q *commonQuery) SetDefaultLimit() { func (q *CommonQuery) SetDefaultLimit() {
if q.Limit <= 0 || q.Limit > 20 { if q.Limit <= 0 || q.Limit > 20 {
q.Limit = 20 q.Limit = 20
} }
} }

12
internal/role_model.go Normal file
View File

@ -0,0 +1,12 @@
package internal
type RoleShort struct {
ID string `json:"_id"`
Name string `json:"name"`
IsAdmin bool `json:"isAdmin"`
}
// RoleCreateOptions ...
type RoleCreateOptions struct {
Name string
}

View File

@ -1,4 +1,4 @@
package usermngmt package internal
import "time" import "time"
@ -14,7 +14,8 @@ func getHCMLocation() *time.Location {
return l return l
} }
// now ... // Now ...
func now() time.Time { func Now() time.Time {
return time.Now().In(getHCMLocation()) return time.Now().In(getHCMLocation())
} }

187
internal/user_model.go Normal file
View File

@ -0,0 +1,187 @@
package internal
import (
"errors"
"time"
"github.com/Selly-Modules/logger"
"github.com/Selly-Modules/mongodb"
)
// CreateOptions ...
type CreateOptions struct {
Name string
Phone string
Email string
Password string
Status string
RoleID string
Other string
}
// UpdateOptions ...
type UpdateOptions struct {
Name string
Phone string
Email string
RoleID string
Other string
}
// ChangePasswordOptions ...
type ChangePasswordOptions struct {
OldPassword string
NewPassword string
}
// AllQuery ...
type AllQuery struct {
Page int64
Limit int64
Keyword string
RoleID string
Status string
}
// User ...
type User struct {
ID string `json:"_id"`
Name string `json:"name"`
Phone string `json:"phone"`
Email string `json:"email"`
Status string `json:"status"`
Role RoleShort `json:"role"`
Other string `json:"other"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type (
// UserAll ...
UserAll struct {
List []User `json:"list"`
Total int64 `json:"total"`
}
)
// NewUser ...
func (payload CreateOptions) NewUser() (result DBUser, err error) {
timeNow := Now()
roleID, _ := mongodb.NewIDFromString(payload.RoleID)
return DBUser{
ID: mongodb.NewObjectID(),
Name: payload.Name,
SearchString: GetSearchString(payload.Name, payload.Phone, payload.Email),
Phone: payload.Phone,
Email: payload.Email,
HashedPassword: HashPassword(payload.Password),
Status: payload.Status,
RoleID: roleID,
Other: payload.Other,
CreatedAt: timeNow,
UpdatedAt: timeNow,
}, nil
}
// Validate ...
func (co CreateOptions) Validate() error {
// Name
if co.Name == "" {
logger.Error("usermngmt - Create: no Name data", logger.LogData{
"payload": co,
})
return errors.New("no name data")
}
// Phone
if co.Phone == "" {
logger.Error("usermngmt - Create: no phone data", logger.LogData{
"payload": co,
})
return errors.New("no phone data")
}
// Email
if co.Email == "" {
logger.Error("usermngmt - Create: no email data", logger.LogData{
"payload": co,
})
return errors.New("no email data")
}
// Password
if co.Password == "" {
logger.Error("usermngmt - Create: no password data", logger.LogData{
"payload": co,
})
return errors.New("no password data")
}
// Status
if co.Status == "" {
logger.Error("usermngmt - Create: no status data", logger.LogData{
"payload": co,
})
return errors.New("no status data")
}
// RoleID
if co.RoleID == "" {
logger.Error("usermngmt - Create: no roleID data", logger.LogData{
"payload": co,
})
return errors.New("no role id data")
}
return nil
}
// Validate ...
func (uo UpdateOptions) Validate() error {
// Name
if uo.Name == "" {
logger.Error("usermngmt - Update: no name data", logger.LogData{
"payload": uo,
})
return errors.New("no name data")
}
// Phone
if uo.Phone == "" {
logger.Error("usermngmt - Update: no phone data", logger.LogData{
"payload": uo,
})
return errors.New("no phone data")
}
// Email
if uo.Email == "" {
logger.Error("usermngmt - Update: no email data", logger.LogData{
"payload": uo,
})
return errors.New("no email data")
}
// RoleID
if uo.RoleID == "" {
logger.Error("usermngmt - Update: no roleID data", logger.LogData{
"payload": uo,
})
return errors.New("no role id data")
}
return nil
}
// Validate ...
func (co ChangePasswordOptions) Validate() error {
// OldPassword, NewPassword
if co.OldPassword == "" || co.NewPassword == "" {
logger.Error("usermngmt - ChangePassword: old or new password cannot be empty", logger.LogData{
"payload": co,
})
return errors.New("old or new password cannot be empty")
}
return nil
}

View File

@ -1,32 +0,0 @@
package usermngmt
import (
"time"
)
// User ...
type User struct {
ID string `json:"_id"`
Name string `json:"name"`
Phone string `json:"phone"`
Email string `json:"email"`
Status string `json:"status"`
Role RoleShort `json:"role"`
Other string `json:"other"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type RoleShort struct {
ID string `json:"_id"`
Name string `json:"name"`
IsAdmin bool `json:"isAdmin"`
}
type (
// UserAll ...
UserAll struct {
List []User `json:"list"`
Total int64 `json:"total"`
}
)

17
role/db.go Normal file
View File

@ -0,0 +1,17 @@
package role
import (
"context"
"github.com/Selly-Modules/usermngmt/internal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func (h Handle) findByID(ctx context.Context, id primitive.ObjectID) (internal.DBRole, error) {
var (
doc internal.DBRole
)
err := h.Col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
return doc, err
}

25
role/handle.go Normal file
View File

@ -0,0 +1,25 @@
package role
import (
"context"
"github.com/Selly-Modules/usermngmt/internal"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
type Handle struct {
Col *mongo.Collection
}
// FindByID ...
func (h Handle) FindByID(ctx context.Context, id primitive.ObjectID) (internal.DBRole, error) {
role, err := h.findByID(ctx, id)
return role, err
}
// Create ...
func (h Handle) Create(payload internal.RoleCreateOptions) error {
// TODO later
return nil
}

130
user/db.go Normal file
View File

@ -0,0 +1,130 @@
package user
import (
"context"
"fmt"
"github.com/Selly-Modules/logger"
"github.com/Selly-Modules/usermngmt/internal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
func (h Handle) isPhoneNumberOrEmailExisted(ctx context.Context, phone, email string) bool {
// Find
cond := bson.M{
"$or": []bson.M{
{
"phone": phone,
},
{
"email": email,
},
},
}
total, err := h.Col.CountDocuments(ctx, cond)
if err != nil {
logger.Error("usermngmt - countUserByCondition", logger.LogData{
"condition": cond,
"err": err.Error(),
})
return true
}
return total != 0
}
func (h Handle) isRoleIDExisted(ctx context.Context, roleID primitive.ObjectID) bool {
// Find
cond := bson.M{
"_id": roleID,
}
total, err := h.RoleCol.CountDocuments(ctx, cond)
if err != nil {
logger.Error("usermngmt - countRoleByCondition", logger.LogData{
"condition": cond,
"err": err.Error(),
})
return false
}
return total != 0
}
func (h Handle) roleFindByID(ctx context.Context, id primitive.ObjectID) (internal.DBRole, error) {
var (
doc internal.DBRole
)
err := h.RoleCol.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
return doc, err
}
func (h Handle) create(ctx context.Context, doc internal.DBUser) error {
_, err := h.Col.InsertOne(ctx, doc)
if err != nil {
logger.Error("usermngmt - Create", logger.LogData{
"doc": doc,
"err": err.Error(),
})
return fmt.Errorf("error when create user: %s", err.Error())
}
return nil
}
func (h Handle) updateOneByCondition(ctx context.Context, cond interface{}, payload interface{}) error {
_, err := h.Col.UpdateOne(ctx, cond, payload)
if err != nil {
logger.Error("usermngmt - Update", logger.LogData{
"cond": cond,
"payload": payload,
"err": err.Error(),
})
return fmt.Errorf("error when update user: %s", err.Error())
}
return err
}
func (h Handle) findByID(ctx context.Context, id primitive.ObjectID) (internal.DBUser, error) {
var (
doc internal.DBUser
)
err := h.Col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
return doc, err
}
func (h Handle) findByCondition(ctx context.Context, cond interface{}, opts ...*options.FindOptions) (docs []internal.DBUser) {
docs = make([]internal.DBUser, 0)
cursor, err := h.Col.Find(ctx, cond, opts...)
if err != nil {
logger.Error("usermngmt - All", logger.LogData{
"cond": cond,
"opts": opts,
"err": err.Error(),
})
return
}
defer cursor.Close(ctx)
if err = cursor.All(ctx, &docs); err != nil {
logger.Error("usermngmt - All - decode", logger.LogData{
"cond": cond,
"opts": opts,
"err": err.Error(),
})
return
}
return
}
// countByCondition ...
func (h Handle) countByCondition(ctx context.Context, cond interface{}) int64 {
total, err := h.Col.CountDocuments(ctx, cond)
if err != nil {
logger.Error("usermngmt - Count", logger.LogData{
"err": err.Error(),
"cond": cond,
})
}
return total
}

245
user/handle.go Normal file
View File

@ -0,0 +1,245 @@
package user
import (
"context"
"errors"
"sync"
"github.com/Selly-Modules/logger"
"github.com/Selly-Modules/mongodb"
"github.com/Selly-Modules/usermngmt/internal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
type Handle struct {
Col *mongo.Collection
RoleCol *mongo.Collection
}
// Create ...
func (h Handle) Create(payload internal.CreateOptions) error {
var (
ctx = context.Background()
)
// Validate payload
if err := payload.Validate(); err != nil {
return err
}
// Find roleID exists or not
roleID, isValid := mongodb.NewIDFromString(payload.RoleID)
if !isValid {
return errors.New("invalid role id data")
}
if !h.isRoleIDExisted(ctx, roleID) {
return errors.New("role id does not exist")
}
// Find phone number,email exists or not
if h.isPhoneNumberOrEmailExisted(ctx, payload.Phone, payload.Email) {
return errors.New("phone number or email already existed")
}
// New user data from payload
doc, err := payload.NewUser()
if err != nil {
return err
}
// Create user
if err = h.create(ctx, doc); err != nil {
return err
}
return nil
}
// All ...
func (h Handle) All(queryParams internal.AllQuery) (r internal.UserAll) {
var (
ctx = context.Background()
wg sync.WaitGroup
cond = bson.M{}
)
query := internal.CommonQuery{
Page: queryParams.Page,
Limit: queryParams.Limit,
Keyword: queryParams.Keyword,
RoleID: queryParams.RoleID,
Status: queryParams.Status,
Sort: bson.M{"createdAt": -1},
}
// Assign condition
query.SetDefaultLimit()
query.AssignKeyword(cond)
query.AssignRoleID(cond)
query.AssignStatus(cond)
wg.Add(1)
go func() {
defer wg.Done()
docs := h.findByCondition(ctx, cond, query.GetFindOptionsUsingPage())
r.List = h.getResponseList(ctx, docs)
}()
wg.Add(1)
go func() {
defer wg.Done()
r.Total = h.countByCondition(ctx, cond)
}()
wg.Wait()
return
}
func (h Handle) getResponseList(ctx context.Context, users []internal.DBUser) []internal.User {
res := make([]internal.User, 0)
for _, user := range users {
roleRaw, _ := h.roleFindByID(ctx, user.RoleID)
res = append(res, internal.User{
ID: user.ID.Hex(),
Name: user.Name,
Phone: user.Phone,
Email: user.Email,
Status: user.Status,
Role: internal.RoleShort{
ID: roleRaw.ID.Hex(),
Name: roleRaw.Name,
IsAdmin: roleRaw.IsAdmin,
},
Other: user.Other,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
return res
}
// UpdateByUserID ...
func (h Handle) UpdateByUserID(userID string, payload internal.UpdateOptions) error {
var (
ctx = context.Background()
)
// Validate payload
if err := payload.Validate(); err != nil {
return err
}
// Find roleID exists or not
roleID, isValid := mongodb.NewIDFromString(payload.RoleID)
if !isValid {
return errors.New("invalid role id data")
}
if !h.isRoleIDExisted(ctx, roleID) {
return errors.New("role id does not exist")
}
// Find phone number,email exists or not
if h.isPhoneNumberOrEmailExisted(ctx, payload.Phone, payload.Email) {
return errors.New("phone number or email already existed")
}
// Setup condition
id, _ := mongodb.NewIDFromString(userID)
cond := bson.M{
"_id": id,
}
// Setup update data
updateData := bson.M{
"$set": bson.M{
"name": payload.Name,
"searchString": internal.GetSearchString(payload.Name, payload.Phone, payload.Email),
"phone": payload.Phone,
"email": payload.Email,
"roleId": roleID,
"other": payload.Other,
"updatedAt": internal.Now(),
},
}
// Update
if err := h.updateOneByCondition(ctx, cond, updateData); err != nil {
return err
}
return nil
}
// ChangeUserPassword ...
func (h Handle) ChangeUserPassword(userID string, opt internal.ChangePasswordOptions) error {
var (
ctx = context.Background()
)
// Validate payload
err := opt.Validate()
if err != nil {
return err
}
// Validate userID
if _, isValid := mongodb.NewIDFromString(userID); !isValid {
logger.Error("usermngmt - ChangePassword: invalid userID data", logger.LogData{
"payload": opt,
"userID": userID,
})
return errors.New("invalid user id data")
}
// Find user
id, _ := mongodb.NewIDFromString(userID)
user, _ := h.findByID(ctx, id)
if user.ID.IsZero() {
return errors.New("user not found")
}
// Check old password
if isValid := internal.CheckPasswordHash(opt.OldPassword, user.HashedPassword); !isValid {
return errors.New("the password is incorrect")
}
// Update password
if err = h.updateOneByCondition(ctx, bson.M{"_id": user.ID}, bson.M{
"$set": bson.M{
"hashedPassword": internal.HashPassword(opt.NewPassword),
"updatedAt": internal.Now(),
},
}); err != nil {
return err
}
return nil
}
// ChangeUserStatus ...
func (h Handle) ChangeUserStatus(userID, newStatus string) error {
var (
ctx = context.Background()
)
// Validate userID
id, isValid := mongodb.NewIDFromString(userID)
if !isValid {
return errors.New("invalid user id data")
}
// Update status
if err := h.updateOneByCondition(ctx, bson.M{"_id": id}, bson.M{
"$set": bson.M{
"status": newStatus,
"updatedAt": internal.Now(),
},
}); err != nil {
return err
}
return nil
}

View File

@ -1,145 +0,0 @@
package usermngmt
import (
"context"
"errors"
"github.com/Selly-Modules/logger"
"github.com/Selly-Modules/mongodb"
)
func (co CreateOptions) validate(ctx context.Context) error {
// Name
if co.Name == "" {
logger.Error("usermngmt - Create: no Name data", logger.LogData{
"payload": co,
})
return errors.New("no name data")
}
// Phone
if co.Phone == "" {
logger.Error("usermngmt - Create: no phone data", logger.LogData{
"payload": co,
})
return errors.New("no phone data")
}
// Email
if co.Email == "" {
logger.Error("usermngmt - Create: no email data", logger.LogData{
"payload": co,
})
return errors.New("no email data")
}
// Password
if co.Password == "" {
logger.Error("usermngmt - Create: no password data", logger.LogData{
"payload": co,
})
return errors.New("no password data")
}
// Status
if co.Status == "" {
logger.Error("usermngmt - Create: no status data", logger.LogData{
"payload": co,
})
return errors.New("no status data")
}
// RoleID
if co.RoleID == "" {
logger.Error("usermngmt - Create: no roleID data", logger.LogData{
"payload": co,
})
return errors.New("no role id data")
}
// Find roleID exists or not
roleID, isValid := mongodb.NewIDFromString(co.RoleID)
if !isValid {
return errors.New("invalid role id data")
}
if !s.isRoleIDExisted(ctx, roleID) {
return errors.New("role id does not exist")
}
// Find phone number,email exists or not
if s.isPhoneNumberOrEmailExisted(ctx, co.Phone, co.Email) {
return errors.New("phone number or email already existed")
}
return nil
}
func (co UpdateOptions) validate(ctx context.Context) error {
// Name
if co.Name == "" {
logger.Error("usermngmt - Update: no name data", logger.LogData{
"payload": co,
})
return errors.New("no name data")
}
// Phone
if co.Phone == "" {
logger.Error("usermngmt - Update: no phone data", logger.LogData{
"payload": co,
})
return errors.New("no phone data")
}
// Email
if co.Email == "" {
logger.Error("usermngmt - Update: no email data", logger.LogData{
"payload": co,
})
return errors.New("no email data")
}
// RoleID
if co.RoleID == "" {
logger.Error("usermngmt - Update: no roleID data", logger.LogData{
"payload": co,
})
return errors.New("no role id data")
}
// Find roleID exists or not
roleID, isValid := mongodb.NewIDFromString(co.RoleID)
if !isValid {
return errors.New("invalid role id data")
}
if !s.isRoleIDExisted(ctx, roleID) {
return errors.New("role id does not exist")
}
// Find phone number,email exists or not
if s.isPhoneNumberOrEmailExisted(ctx, co.Phone, co.Email) {
return errors.New("phone number or email already existed")
}
return nil
}
func (co ChangePasswordOptions) validate(userID string) error {
// OldPassword, NewPassword
if co.OldPassword == "" || co.NewPassword == "" {
logger.Error("usermngmt - ChangePassword: old or new password cannot be empty", logger.LogData{
"payload": co,
})
return errors.New("old or new password cannot be empty")
}
// UserID
if _, isValid := mongodb.NewIDFromString(userID); !isValid {
logger.Error("usermngmt - ChangePassword: invalid userID data", logger.LogData{
"payload": co,
})
return errors.New("invalid user id data")
}
return nil
}