Feature/role create #5
|
@ -0,0 +1,49 @@
|
|||
package usermngmt
|
||||
|
||||
import (
|
||||
"github.com/Selly-Modules/usermngmt/model"
|
||||
"github.com/Selly-Modules/usermngmt/role"
|
||||
"github.com/Selly-Modules/usermngmt/user"
|
||||
)
|
||||
|
||||
//
|
||||
// User
|
||||
//
|
||||
|
||||
// user methods
|
||||
|
||||
// CreateUser ...
|
||||
func (s Service) CreateUser(payload model.UserCreateOptions) error {
|
||||
return user.Create(payload)
|
||||
}
|
||||
|
||||
// UpdateUser ...
|
||||
func (s Service) UpdateUser(userID string, payload model.UserUpdateOptions) error {
|
||||
return user.UpdateByUserID(userID, payload)
|
||||
}
|
||||
|
||||
// ChangeUserPassword ...
|
||||
func (s Service) ChangeUserPassword(userID string, payload model.ChangePasswordOptions) error {
|
||||
return user.ChangeUserPassword(userID, payload)
|
||||
}
|
||||
|
||||
// ChangeUserStatus ...
|
||||
func (s Service) ChangeUserStatus(userID, newStatus string) error {
|
||||
return user.ChangeUserStatus(userID, newStatus)
|
||||
}
|
||||
|
||||
// GetAllUser ...
|
||||
func (s Service) GetAllUser(query model.UserAllQuery) model.UserAll {
|
||||
return user.All(query)
|
||||
}
|
||||
|
||||
//
|
||||
// Role
|
||||
//
|
||||
|
||||
// role methods
|
||||
|
||||
// CreateRole ...
|
||||
func (s Service) CreateRole(payload model.RoleCreateOptions) error {
|
||||
return role.Create(payload)
|
||||
}
|
|
@ -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 User, err error) {
|
||||
timeNow := now()
|
||||
roleID, _ := mongodb.NewIDFromString(payload.RoleID)
|
||||
return User{
|
||||
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
|
||||
}
|
|
@ -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 ResponseUserAll) {
|
||||
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 []User) []ResponseUser {
|
||||
res := make([]ResponseUser, 0)
|
||||
|
||||
for _, user := range users {
|
||||
role, _ := s.roleFindByID(ctx, user.RoleID)
|
||||
res = append(res, ResponseUser{
|
||||
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
|
||||
}
|
125
action_update.go
125
action_update.go
|
@ -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
|
||||
}
|
12
constant.go
12
constant.go
|
@ -1,12 +0,0 @@
|
|||
package usermngmt
|
||||
|
||||
// Constant ...
|
||||
const (
|
||||
tableUser = "users"
|
||||
tableRole = "roles"
|
||||
tablePrefixDefault = "usermngmt"
|
||||
|
||||
timezoneHCM = "Asia/Ho_Chi_Minh"
|
||||
|
||||
passwordHashingCost = 14
|
||||
)
|
|
@ -0,0 +1,34 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
// Table
|
||||
var (
|
||||
tableUser = "users"
|
||||
tableRole = "roles"
|
||||
)
|
||||
|
||||
var (
|
||||
db *mongo.Database
|
||||
prefix string
|
||||
)
|
||||
|
||||
// Set ...
|
||||
func Set(instance *mongo.Database, tablePrefix string) {
|
||||
db = instance
|
||||
prefix = tablePrefix
|
||||
}
|
||||
|
||||
// GetUserCol ...
|
||||
func GetUserCol() *mongo.Collection {
|
||||
return db.Collection(fmt.Sprintf("%s-%s", prefix, tableUser))
|
||||
}
|
||||
|
||||
// GetRoleCol ...
|
||||
func GetRoleCol() *mongo.Collection {
|
||||
return db.Collection(fmt.Sprintf("%s-%s", prefix, tableRole))
|
||||
}
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
|||
module github.com/selly-Modules/usermngmt
|
||||
module github.com/Selly-Modules/usermngmt
|
||||
|
||||
go 1.17
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package internal
|
||||
|
||||
// Constant ...
|
||||
const (
|
||||
timezoneHCM = "Asia/Ho_Chi_Minh"
|
||||
|
||||
passwordHashingCost = 14
|
||||
|
||||
TablePrefixDefault = "usermngmt"
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package usermngmt
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -7,17 +7,20 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func hashPassword(password string) string {
|
||||
// HashPassword ...
|
||||
func HashPassword(password string) string {
|
||||
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), passwordHashingCost)
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func checkPasswordHash(password, hash string) bool {
|
||||
// CheckPasswordHash ...
|
||||
func CheckPasswordHash(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func getSearchString(fieldList ...string) string {
|
||||
// GetSearchString ...
|
||||
func GetSearchString(fieldList ...string) string {
|
||||
var (
|
||||
searchList = make([]interface{}, 0)
|
||||
format = ""
|
|
@ -1,4 +1,4 @@
|
|||
package usermngmt
|
||||
package internal
|
||||
|
||||
import "time"
|
||||
|
||||
|
@ -14,7 +14,7 @@ func getHCMLocation() *time.Location {
|
|||
return l
|
||||
}
|
||||
|
||||
// now ...
|
||||
func now() time.Time {
|
||||
// Now ...
|
||||
func Now() time.Time {
|
||||
return time.Now().In(getHCMLocation())
|
||||
}
|
59
model.go
59
model.go
|
@ -1,59 +0,0 @@
|
|||
package usermngmt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// User ...
|
||||
type User struct {
|
||||
ID primitive.ObjectID `bson:"_id" json:"_id"`
|
||||
Name string `bson:"name" json:"name"`
|
||||
SearchString string `bson:"searchString" json:"-"`
|
||||
Phone string `bson:"phone" json:"phone"` // unique
|
||||
Email string `bson:"email" json:"email"` // unique
|
||||
HashedPassword string `bson:"hashedPassword" json:"-"`
|
||||
Status string `bson:"status" json:"status"`
|
||||
RoleID primitive.ObjectID `bson:"roleId" json:"roleId"`
|
||||
Other string `bson:"other" json:"other"`
|
||||
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
|
||||
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
|
||||
}
|
||||
|
||||
// ResponseUser ...
|
||||
type ResponseUser 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"`
|
||||
}
|
||||
|
||||
// Role ...
|
||||
type Role 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"`
|
||||
}
|
||||
|
||||
type RoleShort struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
}
|
||||
|
||||
type (
|
||||
// ResponseUserAll ...
|
||||
ResponseUserAll struct {
|
||||
List []ResponseUser `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// DBUser ...
|
||||
type DBUser struct {
|
||||
ID primitive.ObjectID `bson:"_id"`
|
||||
Name string `bson:"name"`
|
||||
SearchString string `bson:"searchString"`
|
||||
Phone string `bson:"phone"` // unique
|
||||
Email string `bson:"email"` // unique
|
||||
HashedPassword string `bson:"hashedPassword"`
|
||||
Status string `bson:"status"`
|
||||
RoleID primitive.ObjectID `bson:"roleId"`
|
||||
Other string `bson:"other"`
|
||||
CreatedAt time.Time `bson:"createdAt"`
|
||||
UpdatedAt time.Time `bson:"updatedAt"`
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package usermngmt
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/Selly-Modules/mongodb"
|
||||
|
@ -6,7 +6,8 @@ import (
|
|||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type commonQuery struct {
|
||||
// CommonQuery ...
|
||||
type CommonQuery struct {
|
||||
Page int64
|
||||
Limit int64
|
||||
Keyword string
|
||||
|
@ -16,14 +17,14 @@ type commonQuery struct {
|
|||
}
|
||||
|
||||
// AssignKeyword ...
|
||||
func (q *commonQuery) AssignKeyword(cond bson.M) {
|
||||
func (q *CommonQuery) AssignKeyword(cond bson.M) {
|
||||
if q.Keyword != "" {
|
||||
cond["searchString"] = mongodb.GenerateQuerySearchString(q.Keyword)
|
||||
}
|
||||
}
|
||||
|
||||
// AssignRoleID ...
|
||||
func (q *commonQuery) AssignRoleID(cond bson.M) {
|
||||
func (q *CommonQuery) AssignRoleID(cond bson.M) {
|
||||
if q.RoleID != "" {
|
||||
if id, isValid := mongodb.NewIDFromString(q.RoleID); isValid {
|
||||
cond["roleId"] = id
|
||||
|
@ -32,14 +33,14 @@ func (q *commonQuery) AssignRoleID(cond bson.M) {
|
|||
}
|
||||
|
||||
// AssignStatus ...
|
||||
func (q *commonQuery) AssignStatus(cond bson.M) {
|
||||
func (q *CommonQuery) AssignStatus(cond bson.M) {
|
||||
if q.Status != "" {
|
||||
cond["status"] = q.Status
|
||||
}
|
||||
}
|
||||
|
||||
// GetFindOptionsUsingPage ...
|
||||
func (q *commonQuery) GetFindOptionsUsingPage() *options.FindOptions {
|
||||
func (q *CommonQuery) GetFindOptionsUsingPage() *options.FindOptions {
|
||||
opts := options.Find()
|
||||
if q.Limit > 0 {
|
||||
opts.SetLimit(q.Limit).SetSkip(q.Limit * q.Page)
|
||||
|
@ -51,7 +52,7 @@ func (q *commonQuery) GetFindOptionsUsingPage() *options.FindOptions {
|
|||
}
|
||||
|
||||
// SetDefaultLimit ...
|
||||
func (q *commonQuery) SetDefaultLimit() {
|
||||
func (q *CommonQuery) SetDefaultLimit() {
|
||||
if q.Limit <= 0 || q.Limit > 20 {
|
||||
q.Limit = 20
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package model
|
||||
|
||||
// RoleCreateOptions ...
|
||||
type RoleCreateOptions struct {
|
||||
Name string
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package model
|
||||
|
||||
// RoleShort ...
|
||||
type RoleShort struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
}
|
|
@ -1,14 +1,48 @@
|
|||
package usermngmt
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/Selly-Modules/logger"
|
||||
"github.com/Selly-Modules/mongodb"
|
||||
)
|
||||
|
||||
func (co CreateOptions) validate(ctx context.Context) error {
|
||||
// UserCreateOptions ...
|
||||
type UserCreateOptions struct {
|
||||
Name string
|
||||
Phone string
|
||||
Email string
|
||||
Password string
|
||||
Status string
|
||||
RoleID string
|
||||
Other string
|
||||
}
|
||||
|
||||
// UserUpdateOptions ...
|
||||
type UserUpdateOptions struct {
|
||||
Name string
|
||||
Phone string
|
||||
Email string
|
||||
RoleID string
|
||||
Other string
|
||||
}
|
||||
|
||||
// ChangePasswordOptions ...
|
||||
type ChangePasswordOptions struct {
|
||||
OldPassword string
|
||||
NewPassword string
|
||||
}
|
||||
|
||||
// UserAllQuery ...
|
||||
type UserAllQuery struct {
|
||||
Page int64
|
||||
Limit int64
|
||||
Keyword string
|
||||
RoleID string
|
||||
Status string
|
||||
}
|
||||
|
||||
// Validate ...
|
||||
func (co UserCreateOptions) Validate() error {
|
||||
// Name
|
||||
if co.Name == "" {
|
||||
logger.Error("usermngmt - Create: no Name data", logger.LogData{
|
||||
|
@ -57,74 +91,48 @@ func (co CreateOptions) validate(ctx context.Context) error {
|
|||
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 {
|
||||
// Validate ...
|
||||
func (uo UserUpdateOptions) Validate() error {
|
||||
// Name
|
||||
if co.Name == "" {
|
||||
if uo.Name == "" {
|
||||
logger.Error("usermngmt - Update: no name data", logger.LogData{
|
||||
"payload": co,
|
||||
"payload": uo,
|
||||
})
|
||||
return errors.New("no name data")
|
||||
}
|
||||
|
||||
// Phone
|
||||
if co.Phone == "" {
|
||||
if uo.Phone == "" {
|
||||
logger.Error("usermngmt - Update: no phone data", logger.LogData{
|
||||
"payload": co,
|
||||
"payload": uo,
|
||||
})
|
||||
return errors.New("no phone data")
|
||||
}
|
||||
|
||||
// Email
|
||||
if co.Email == "" {
|
||||
if uo.Email == "" {
|
||||
logger.Error("usermngmt - Update: no email data", logger.LogData{
|
||||
"payload": co,
|
||||
"payload": uo,
|
||||
})
|
||||
return errors.New("no email data")
|
||||
}
|
||||
|
||||
// RoleID
|
||||
if co.RoleID == "" {
|
||||
if uo.RoleID == "" {
|
||||
logger.Error("usermngmt - Update: no roleID data", logger.LogData{
|
||||
"payload": co,
|
||||
"payload": uo,
|
||||
})
|
||||
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 {
|
||||
// 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{
|
||||
|
@ -133,13 +141,5 @@ func (co ChangePasswordOptions) validate(userID string) error {
|
|||
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
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package model
|
||||
|
||||
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 (
|
||||
// UserAll ...
|
||||
UserAll struct {
|
||||
List []User `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
package role
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Selly-Modules/usermngmt/database"
|
||||
"github.com/Selly-Modules/usermngmt/model"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func findByID(ctx context.Context, id primitive.ObjectID) (model.DBRole, error) {
|
||||
var (
|
||||
doc model.DBRole
|
||||
col = database.GetRoleCol()
|
||||
)
|
||||
err := col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
|
||||
return doc, err
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package role
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Selly-Modules/usermngmt/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// FindByID ...
|
||||
func FindByID(ctx context.Context, id primitive.ObjectID) (model.DBRole, error) {
|
||||
role, err := findByID(ctx, id)
|
||||
return role, err
|
||||
}
|
||||
|
||||
// Create ...
|
||||
func Create(payload model.RoleCreateOptions) error {
|
||||
// TODO later
|
||||
return nil
|
||||
}
|
|
@ -1,31 +1,21 @@
|
|||
package usermngmt
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Selly-Modules/logger"
|
||||
"github.com/Selly-Modules/usermngmt/database"
|
||||
"github.com/Selly-Modules/usermngmt/model"
|
||||
"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/options"
|
||||
)
|
||||
|
||||
// getUserCollection ...
|
||||
func (s Service) getUserCollection() *mongo.Collection {
|
||||
return s.DB.Collection(fmt.Sprintf("%s-%s", s.TablePrefix, tableUser))
|
||||
}
|
||||
|
||||
// getRoleCollection ...
|
||||
func (s Service) getRoleCollection() *mongo.Collection {
|
||||
return s.DB.Collection(fmt.Sprintf("%s-%s", s.TablePrefix, tableRole))
|
||||
}
|
||||
|
||||
func (s Service) isPhoneNumberOrEmailExisted(ctx context.Context, phone, email string) bool {
|
||||
func isPhoneNumberOrEmailExisted(ctx context.Context, phone, email string) bool {
|
||||
var (
|
||||
col = s.getUserCollection()
|
||||
col = database.GetUserCol()
|
||||
)
|
||||
|
||||
// Find
|
||||
cond := bson.M{
|
||||
"$or": []bson.M{
|
||||
|
@ -48,11 +38,10 @@ func (s Service) isPhoneNumberOrEmailExisted(ctx context.Context, phone, email s
|
|||
return total != 0
|
||||
}
|
||||
|
||||
func (s Service) isRoleIDExisted(ctx context.Context, roleID primitive.ObjectID) bool {
|
||||
func isRoleIDExisted(ctx context.Context, roleID primitive.ObjectID) bool {
|
||||
var (
|
||||
col = s.getRoleCollection()
|
||||
col = database.GetRoleCol()
|
||||
)
|
||||
|
||||
// Find
|
||||
cond := bson.M{
|
||||
"_id": roleID,
|
||||
|
@ -68,11 +57,19 @@ func (s Service) isRoleIDExisted(ctx context.Context, roleID primitive.ObjectID)
|
|||
return total != 0
|
||||
}
|
||||
|
||||
func (s Service) userCreate(ctx context.Context, doc User) error {
|
||||
func roleFindByID(ctx context.Context, id primitive.ObjectID) (model.DBRole, error) {
|
||||
var (
|
||||
col = s.getUserCollection()
|
||||
doc model.DBRole
|
||||
col = database.GetRoleCol()
|
||||
)
|
||||
err := col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
|
||||
return doc, err
|
||||
}
|
||||
|
||||
func create(ctx context.Context, doc model.DBUser) error {
|
||||
var (
|
||||
col = database.GetUserCol()
|
||||
)
|
||||
_, err := col.InsertOne(ctx, doc)
|
||||
if err != nil {
|
||||
logger.Error("usermngmt - Create", logger.LogData{
|
||||
|
@ -85,11 +82,10 @@ func (s Service) userCreate(ctx context.Context, doc User) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s Service) userUpdateOneByCondition(ctx context.Context, cond interface{}, payload interface{}) error {
|
||||
func updateOneByCondition(ctx context.Context, cond interface{}, payload interface{}) error {
|
||||
var (
|
||||
col = s.getUserCollection()
|
||||
col = database.GetUserCol()
|
||||
)
|
||||
|
||||
_, err := col.UpdateOne(ctx, cond, payload)
|
||||
if err != nil {
|
||||
logger.Error("usermngmt - Update", logger.LogData{
|
||||
|
@ -103,20 +99,20 @@ func (s Service) userUpdateOneByCondition(ctx context.Context, cond interface{},
|
|||
return err
|
||||
}
|
||||
|
||||
func (s Service) userFindByID(ctx context.Context, id primitive.ObjectID) (User, error) {
|
||||
func findByID(ctx context.Context, id primitive.ObjectID) (model.DBUser, error) {
|
||||
var (
|
||||
col = s.getUserCollection()
|
||||
doc User
|
||||
doc model.DBUser
|
||||
col = database.GetUserCol()
|
||||
)
|
||||
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 []User) {
|
||||
func findByCondition(ctx context.Context, cond interface{}, opts ...*options.FindOptions) (docs []model.DBUser) {
|
||||
var (
|
||||
col = s.getUserCollection()
|
||||
col = database.GetUserCol()
|
||||
)
|
||||
docs = make([]User, 0)
|
||||
docs = make([]model.DBUser, 0)
|
||||
|
||||
cursor, err := col.Find(ctx, cond, opts...)
|
||||
if err != nil {
|
||||
|
@ -139,12 +135,11 @@ func (s Service) userFindByCondition(ctx context.Context, cond interface{}, opts
|
|||
return
|
||||
}
|
||||
|
||||
// userCountByCondition ...
|
||||
func (s Service) userCountByCondition(ctx context.Context, cond interface{}) int64 {
|
||||
// countByCondition ...
|
||||
func countByCondition(ctx context.Context, cond interface{}) int64 {
|
||||
var (
|
||||
col = s.getUserCollection()
|
||||
col = database.GetUserCol()
|
||||
)
|
||||
|
||||
total, err := col.CountDocuments(ctx, cond)
|
||||
if err != nil {
|
||||
logger.Error("usermngmt - Count", logger.LogData{
|
||||
|
@ -154,12 +149,3 @@ func (s Service) userCountByCondition(ctx context.Context, cond interface{}) int
|
|||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (s Service) roleFindByID(ctx context.Context, id primitive.ObjectID) (Role, error) {
|
||||
var (
|
||||
col = s.getRoleCollection()
|
||||
doc Role
|
||||
)
|
||||
err := col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc)
|
||||
return doc, err
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/Selly-Modules/logger"
|
||||
"github.com/Selly-Modules/mongodb"
|
||||
"github.com/Selly-Modules/usermngmt/internal"
|
||||
"github.com/Selly-Modules/usermngmt/model"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// Create ...
|
||||
func Create(payload model.UserCreateOptions) 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 !isRoleIDExisted(ctx, roleID) {
|
||||
return errors.New("role id does not exist")
|
||||
}
|
||||
|
||||
// Find phone number,email exists or not
|
||||
if isPhoneNumberOrEmailExisted(ctx, payload.Phone, payload.Email) {
|
||||
return errors.New("phone number or email already existed")
|
||||
}
|
||||
|
||||
// New user data from payload
|
||||
doc, err := newUser(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create user
|
||||
if err = create(ctx, doc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newUser ...
|
||||
func newUser(payload model.UserCreateOptions) (result model.DBUser, err error) {
|
||||
timeNow := internal.Now()
|
||||
roleID, _ := mongodb.NewIDFromString(payload.RoleID)
|
||||
return model.DBUser{
|
||||
ID: mongodb.NewObjectID(),
|
||||
Name: payload.Name,
|
||||
SearchString: internal.GetSearchString(payload.Name, payload.Phone, payload.Email),
|
||||
Phone: payload.Phone,
|
||||
Email: payload.Email,
|
||||
HashedPassword: internal.HashPassword(payload.Password),
|
||||
Status: payload.Status,
|
||||
RoleID: roleID,
|
||||
Other: payload.Other,
|
||||
CreatedAt: timeNow,
|
||||
UpdatedAt: timeNow,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// All ...
|
||||
func All(queryParams model.UserAllQuery) (r model.UserAll) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
wg sync.WaitGroup
|
||||
cond = bson.M{}
|
||||
)
|
||||
query := model.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 := findByCondition(ctx, cond, query.GetFindOptionsUsingPage())
|
||||
r.List = getResponseList(ctx, docs)
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r.Total = countByCondition(ctx, cond)
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getResponseList(ctx context.Context, users []model.DBUser) []model.User {
|
||||
res := make([]model.User, 0)
|
||||
|
||||
for _, user := range users {
|
||||
roleRaw, _ := roleFindByID(ctx, user.RoleID)
|
||||
res = append(res, model.User{
|
||||
ID: user.ID.Hex(),
|
||||
Name: user.Name,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Status: user.Status,
|
||||
Role: model.RoleShort{
|
||||
ID: roleRaw.ID.Hex(),
|
||||
Name: roleRaw.Name,
|
||||
IsAdmin: roleRaw.IsAdmin,
|
||||
},
|
||||
Other: user.Other,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// UpdateByUserID ...
|
||||
func UpdateByUserID(userID string, payload model.UserUpdateOptions) 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 !isRoleIDExisted(ctx, roleID) {
|
||||
return errors.New("role id does not exist")
|
||||
}
|
||||
|
||||
// Find phone number,email exists or not
|
||||
if 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 := updateOneByCondition(ctx, cond, updateData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeUserPassword ...
|
||||
func ChangeUserPassword(userID string, opt model.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, _ := 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 = 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 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 := updateOneByCondition(ctx, bson.M{"_id": id}, bson.M{
|
||||
"$set": bson.M{
|
||||
"status": newStatus,
|
||||
"updatedAt": internal.Now(),
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
14
usermngmt.go
14
usermngmt.go
|
@ -5,7 +5,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/Selly-Modules/mongodb"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"github.com/Selly-Modules/usermngmt/database"
|
||||
"github.com/Selly-Modules/usermngmt/internal"
|
||||
)
|
||||
|
||||
// MongoDBConfig ...
|
||||
|
@ -23,8 +24,7 @@ type Config struct {
|
|||
|
||||
// Service ...
|
||||
type Service struct {
|
||||
Config
|
||||
DB *mongo.Database
|
||||
config Config
|
||||
}
|
||||
|
||||
var s *Service
|
||||
|
@ -37,7 +37,7 @@ func Init(config Config) (*Service, error) {
|
|||
|
||||
// If prefixTable is empty then it is usermngmt
|
||||
if config.TablePrefix == "" {
|
||||
config.TablePrefix = tablePrefixDefault
|
||||
config.TablePrefix = internal.TablePrefixDefault
|
||||
}
|
||||
|
||||
// Connect MongoDB
|
||||
|
@ -54,9 +54,11 @@ func Init(config Config) (*Service, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Set database
|
||||
database.Set(db, config.TablePrefix)
|
||||
|
||||
s = &Service{
|
||||
Config: config,
|
||||
DB: db,
|
||||
config: config,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
|
Loading…
Reference in New Issue