Merge branch 'development' of https://gitlab.com/mbugroup/lti-api into dev/teguh

This commit is contained in:
aguhh18
2025-11-17 13:52:13 +07:00
87 changed files with 3284 additions and 582 deletions
@@ -71,70 +71,70 @@ func (u *UserController) GetOne(c *fiber.Ctx) error {
})
}
func (u *UserController) CreateOne(c *fiber.Ctx) error {
req := new(validation.Create)
// func (u *UserController) CreateOne(c *fiber.Ctx) error {
// req := new(validation.Create)
if err := c.BodyParser(req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
// if err := c.BodyParser(req); err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
// }
result, err := u.UserService.CreateOne(c, req)
if err != nil {
return err
}
// result, err := u.UserService.CreateOne(c, req)
// if err != nil {
// return err
// }
return c.Status(fiber.StatusCreated).
JSON(response.Success{
Code: fiber.StatusCreated,
Status: "success",
Message: "Create user successfully",
Data: dto.ToUserListDTO(*result),
})
}
// return c.Status(fiber.StatusCreated).
// JSON(response.Success{
// Code: fiber.StatusCreated,
// Status: "success",
// Message: "Create user successfully",
// Data: dto.ToUserListDTO(*result),
// })
// }
func (u *UserController) UpdateOne(c *fiber.Ctx) error {
req := new(validation.Update)
param := c.Params("id")
// func (u *UserController) UpdateOne(c *fiber.Ctx) error {
// req := new(validation.Update)
// param := c.Params("id")
id, err := strconv.Atoi(param)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
}
// id, err := strconv.Atoi(param)
// if err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
// }
if err := c.BodyParser(req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
// if err := c.BodyParser(req); err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
// }
result, err := u.UserService.UpdateOne(c, req, uint(id))
if err != nil {
return err
}
// result, err := u.UserService.UpdateOne(c, req, uint(id))
// if err != nil {
// return err
// }
return c.Status(fiber.StatusOK).
JSON(response.Success{
Code: fiber.StatusOK,
Status: "success",
Message: "Update user successfully",
Data: dto.ToUserListDTO(*result),
})
}
// return c.Status(fiber.StatusOK).
// JSON(response.Success{
// Code: fiber.StatusOK,
// Status: "success",
// Message: "Update user successfully",
// Data: dto.ToUserListDTO(*result),
// })
// }
func (u *UserController) DeleteOne(c *fiber.Ctx) error {
param := c.Params("id")
// func (u *UserController) DeleteOne(c *fiber.Ctx) error {
// param := c.Params("id")
id, err := strconv.Atoi(param)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
}
// id, err := strconv.Atoi(param)
// if err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
// }
if err := u.UserService.DeleteOne(c, uint(id)); err != nil {
return err
}
// if err := u.UserService.DeleteOne(c, uint(id)); err != nil {
// return err
// }
return c.Status(fiber.StatusOK).
JSON(response.Common{
Code: fiber.StatusOK,
Status: "success",
Message: "Delete user successfully",
})
}
// return c.Status(fiber.StatusOK).
// JSON(response.Common{
// Code: fiber.StatusOK,
// Status: "success",
// Message: "Delete user successfully",
// })
// }
@@ -2,29 +2,118 @@ package repository
import (
"context"
"errors"
"time"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
"github.com/jackc/pgconn"
commonrepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type UserRepository interface {
repository.BaseRepository[entity.User]
commonrepo.BaseRepository[entity.User]
IdExists(ctx context.Context, id uint) (bool, error)
GetByIdUser(ctx context.Context, idUser int64, modifier func(*gorm.DB) *gorm.DB) (*entity.User, error)
UpsertByIdUser(ctx context.Context, user *entity.User) error
SoftDeleteByIdUser(ctx context.Context, idUser int64) error
}
type UserRepositoryImpl struct {
*repository.BaseRepositoryImpl[entity.User]
*commonrepo.BaseRepositoryImpl[entity.User]
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &UserRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.User](db),
BaseRepositoryImpl: commonrepo.NewBaseRepository[entity.User](db),
db: db,
}
}
func (r *UserRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
return repository.Exists[entity.User](ctx, r.db, id)
return commonrepo.Exists[entity.User](ctx, r.db, id)
}
func (r *UserRepositoryImpl) GetByIdUser(
ctx context.Context,
idUser int64,
modifier func(*gorm.DB) *gorm.DB,
) (*entity.User, error) {
return r.BaseRepositoryImpl.First(ctx, func(db *gorm.DB) *gorm.DB {
return db.Where("id_user = ?", idUser)
})
}
func (r *UserRepositoryImpl) UpsertByIdUser(ctx context.Context, user *entity.User) error {
if user == nil {
return gorm.ErrInvalidData
}
return r.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
now := time.Now()
user.DeletedAt = gorm.DeletedAt{}
user.UpdatedAt = now
err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id_user"}},
UpdateAll: true,
}).Omit("id", "created_at").Create(user).Error
if err == nil {
return nil
}
if !isUniqueViolation(err, "users_email_unique") {
return err
}
var existing entity.User
lockQuery := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("email = ?", user.Email)
if err := lockQuery.First(&existing).Error; err != nil {
return err
}
user.Id = existing.Id
updates := map[string]any{
"id_user": user.IdUser,
"email": user.Email,
"name": user.Name,
"updated_at": now,
"deleted_at": gorm.DeletedAt{},
}
if err := tx.Model(&entity.User{}).Where("id = ?", existing.Id).Updates(updates).Error; err != nil {
return err
}
return nil
})
}
func (r *UserRepositoryImpl) SoftDeleteByIdUser(ctx context.Context, idUser int64) error {
query := r.DB().WithContext(ctx).Where("id_user = ?", idUser)
result := query.Delete(&entity.User{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
}
func isUniqueViolation(err error, constraint string) bool {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
return false
}
if pgErr.Code != "23505" {
return false
}
if constraint == "" {
return true
}
return pgErr.ConstraintName == constraint
}
+7 -5
View File
@@ -1,20 +1,22 @@
package users
import (
"github.com/gofiber/fiber/v2"
"gitlab.com/mbugroup/lti-api.git/internal/middleware"
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/users/controllers"
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
"github.com/gofiber/fiber/v2"
)
func UserRoutes(v1 fiber.Router, s user.UserService) {
ctrl := controller.NewUserController(s)
route := v1.Group("/users")
route.Use(middleware.Auth(s))
route.Get("/", ctrl.GetAll)
route.Post("/", ctrl.CreateOne)
// route.Post("/", ctrl.CreateOne)
route.Get("/:id", ctrl.GetOne)
route.Patch("/:id", ctrl.UpdateOne)
route.Delete("/:id", ctrl.DeleteOne)
// route.Patch("/:id", ctrl.UpdateOne)
// route.Delete("/:id", ctrl.DeleteOne)
}
@@ -20,6 +20,7 @@ type UserService interface {
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.User, error)
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.User, error)
DeleteOne(ctx *fiber.Ctx, id uint) error
GetBySSOUserID(ctx *fiber.Ctx, ssoUserID uint) (*entity.User, error)
}
type userService struct {
@@ -68,6 +69,18 @@ func (s userService) GetOne(c *fiber.Ctx, id uint) (*entity.User, error) {
return user, nil
}
func (s userService) GetBySSOUserID(c *fiber.Ctx, ssoUserID uint) (*entity.User, error) {
user, err := s.Repository.GetByIdUser(c.Context(), int64(ssoUserID), nil)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "User not found")
}
if err != nil {
s.Log.Errorf("Failed get user by SSO id: %+v", err)
return nil, err
}
return user, nil
}
func (s *userService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.User, error) {
if err := s.Validate.Struct(req); err != nil {
return nil, err