feat: update and delete product
This commit is contained in:
parent
89f0df69af
commit
f449b40270
|
@ -2,6 +2,33 @@ package dto
|
|||
|
||||
import "errors"
|
||||
|
||||
type ProductResponseWithSoldDTO struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []ProductImageDTO `json:"product_images"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe"`
|
||||
Sold int `json:"sold"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ProductResponseWithoutSoldDTO struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []ProductImageDTO `json:"product_images"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ProductResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
|
@ -11,7 +38,6 @@ type ProductResponseDTO struct {
|
|||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe"`
|
||||
Sold int `json:"sold"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
@ -40,10 +66,21 @@ type CreateProductResponseDTO struct {
|
|||
UserID string `json:"user_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []string `json:"product_images"`
|
||||
TrashDetailID string `json:"trash_detail_id"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type UpdateProductRequestDTO struct {
|
||||
ProductTitle string `json:"product_title" validate:"required,min=3,max=255"`
|
||||
ProductImages []string `json:"product_images" validate:"required,min=1,dive,url"`
|
||||
TrashDetailID string `json:"trash_detail_id" validate:"required,uuid"`
|
||||
SalePrice int64 `json:"sale_price" validate:"required,gt=0"`
|
||||
Quantity int `json:"quantity" validate:"required,gt=0"`
|
||||
ProductDescribe string `json:"product_describe,omitempty"`
|
||||
}
|
||||
|
||||
func ValidateSalePrice(marketPrice, salePrice int64) error {
|
||||
|
|
|
@ -110,4 +110,6 @@ func AppRouter(app *fiber.App) {
|
|||
api.Get("/post/products", middleware.RoleRequired(utils.RolePengepul), controllers.GetAllProducts)
|
||||
api.Get("/post/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.GetProductByID)
|
||||
api.Post("/post/addproduct", middleware.RoleRequired(utils.RolePengepul), controllers.CreateProduct)
|
||||
api.Put("/post/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.UpdateProduct)
|
||||
api.Delete("/delete/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.DeleteProduct)
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ func CreateProduct(c *fiber.Ctx) error {
|
|||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"lengkapi data dengan benar",
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
@ -113,3 +113,89 @@ func CreateProduct(c *fiber.Ctx) error {
|
|||
product,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateProduct(c *fiber.Ctx) error {
|
||||
var input dto.UpdateProductRequestDTO
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
productID := c.Params("productid")
|
||||
if productID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Product ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
product, err := services.UpdateProduct(productID, userID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnprocessableEntity).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnprocessableEntity,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Product updated successfully",
|
||||
product,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteProduct(c *fiber.Ctx) error {
|
||||
productID := c.Params("productid")
|
||||
if productID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Product ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
err := services.DeleteProduct(productID, userID)
|
||||
if err != nil {
|
||||
if err.Error() == "product not found or not authorized to delete" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Product not found: mungkin idnya salah",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete product: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Product deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -27,6 +27,13 @@ func GetProductByIDAndUserID(productID, userID string) (domain.Product, error) {
|
|||
return product, err
|
||||
}
|
||||
|
||||
func GetProductByID(productID string) (domain.Product, error) {
|
||||
var product domain.Product
|
||||
err := config.DB.Preload("ProductImages").Preload("TrashDetail").
|
||||
Where("id = ?", productID).First(&product).Error
|
||||
return product, err
|
||||
}
|
||||
|
||||
func CreateProduct(product *domain.Product, images []domain.ProductImage) error {
|
||||
err := config.DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
|
@ -46,3 +53,32 @@ func CreateProduct(product *domain.Product, images []domain.ProductImage) error
|
|||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateProduct(product *domain.Product, images []domain.ProductImage) error {
|
||||
return config.DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
if err := tx.Model(&domain.Product{}).Where("id = ?", product.ID).Updates(product).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Where("product_id = ?", product.ID).Delete(&domain.ProductImage{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(images) > 0 {
|
||||
for i := range images {
|
||||
images[i].ProductID = product.ID
|
||||
}
|
||||
if err := tx.Create(&images).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteProduct(productID, userID string) (int64, error) {
|
||||
result := config.DB.Where("id = ? AND user_id = ?", productID, userID).Delete(&domain.Product{})
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseDTO, error) {
|
||||
func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseWithSoldDTO, error) {
|
||||
offset := (page - 1) * limit
|
||||
products, err := repositories.GetProductsByUserID(userID, limit, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var productResponses []dto.ProductResponseDTO
|
||||
var productResponses []dto.ProductResponseWithSoldDTO
|
||||
for _, product := range products {
|
||||
var images []dto.ProductImageDTO
|
||||
for _, img := range product.ProductImages {
|
||||
images = append(images, dto.ProductImageDTO{ImageURL: img.ImageURL})
|
||||
}
|
||||
|
||||
productResponses = append(productResponses, dto.ProductResponseDTO{
|
||||
productResponses = append(productResponses, dto.ProductResponseWithSoldDTO{
|
||||
ID: product.ID,
|
||||
UserID: product.UserID,
|
||||
ProductTitle: product.ProductTitle,
|
||||
|
@ -43,10 +45,10 @@ func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseD
|
|||
return productResponses, nil
|
||||
}
|
||||
|
||||
func GetProductByIDAndUserID(productID, userID string) (dto.ProductResponseDTO, error) {
|
||||
func GetProductByIDAndUserID(productID, userID string) (dto.ProductResponseWithSoldDTO, error) {
|
||||
product, err := repositories.GetProductByIDAndUserID(productID, userID)
|
||||
if err != nil {
|
||||
return dto.ProductResponseDTO{}, err
|
||||
return dto.ProductResponseWithSoldDTO{}, err
|
||||
}
|
||||
|
||||
var images []dto.ProductImageDTO
|
||||
|
@ -54,7 +56,7 @@ func GetProductByIDAndUserID(productID, userID string) (dto.ProductResponseDTO,
|
|||
images = append(images, dto.ProductImageDTO{ImageURL: img.ImageURL})
|
||||
}
|
||||
|
||||
return dto.ProductResponseDTO{
|
||||
return dto.ProductResponseWithSoldDTO{
|
||||
ID: product.ID,
|
||||
UserID: product.UserID,
|
||||
ProductTitle: product.ProductTitle,
|
||||
|
@ -96,14 +98,88 @@ func CreateProduct(input dto.CreateProductRequestDTO, userID string) (dto.Create
|
|||
return dto.CreateProductResponseDTO{}, err
|
||||
}
|
||||
|
||||
trashDetail, err := repositories.GetTrashDetailByID(product.TrashDetailID)
|
||||
if err != nil {
|
||||
return dto.CreateProductResponseDTO{}, err
|
||||
}
|
||||
|
||||
return dto.CreateProductResponseDTO{
|
||||
ID: product.ID,
|
||||
UserID: product.UserID,
|
||||
ProductTitle: product.ProductTitle,
|
||||
ProductImages: input.ProductImages,
|
||||
TrashDetailID: product.TrashDetailID,
|
||||
TrashDetail: dto.TrashDetailResponseDTO{
|
||||
ID: trashDetail.ID,
|
||||
Description: trashDetail.Description,
|
||||
Price: trashDetail.Price,
|
||||
},
|
||||
SalePrice: product.SalePrice,
|
||||
Quantity: product.Quantity,
|
||||
ProductDescribe: product.ProductDescribe,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(product.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func UpdateProduct(productID, userID string, input dto.UpdateProductRequestDTO) (dto.ProductResponseDTO, error) {
|
||||
if err := dto.GetValidator().Struct(input); err != nil {
|
||||
return dto.ProductResponseDTO{}, err
|
||||
}
|
||||
|
||||
product := &domain.Product{
|
||||
ID: productID,
|
||||
UserID: userID,
|
||||
ProductTitle: input.ProductTitle,
|
||||
TrashDetailID: input.TrashDetailID,
|
||||
SalePrice: input.SalePrice,
|
||||
Quantity: input.Quantity,
|
||||
ProductDescribe: input.ProductDescribe,
|
||||
}
|
||||
|
||||
var images []domain.ProductImage
|
||||
for _, imageURL := range input.ProductImages {
|
||||
images = append(images, domain.ProductImage{ImageURL: imageURL})
|
||||
}
|
||||
|
||||
if err := repositories.UpdateProduct(product, images); err != nil {
|
||||
return dto.ProductResponseDTO{}, err
|
||||
}
|
||||
|
||||
updatedProduct, err := repositories.GetProductByID(productID)
|
||||
if err != nil {
|
||||
return dto.ProductResponseDTO{}, err
|
||||
}
|
||||
|
||||
var productImages []dto.ProductImageDTO
|
||||
for _, img := range updatedProduct.ProductImages {
|
||||
productImages = append(productImages, dto.ProductImageDTO{ImageURL: img.ImageURL})
|
||||
}
|
||||
|
||||
return dto.ProductResponseDTO{
|
||||
ID: updatedProduct.ID,
|
||||
UserID: updatedProduct.UserID,
|
||||
ProductTitle: updatedProduct.ProductTitle,
|
||||
ProductImages: productImages,
|
||||
TrashDetail: dto.TrashDetailResponseDTO{
|
||||
ID: updatedProduct.TrashDetail.ID,
|
||||
Description: updatedProduct.TrashDetail.Description,
|
||||
Price: updatedProduct.TrashDetail.Price,
|
||||
},
|
||||
SalePrice: updatedProduct.SalePrice,
|
||||
Quantity: updatedProduct.Quantity,
|
||||
ProductDescribe: updatedProduct.ProductDescribe,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(updatedProduct.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(updatedProduct.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DeleteProduct(productID, userID string) error {
|
||||
rowsAffected, err := repositories.DeleteProduct(productID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return errors.New("product not found or not authorized to delete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue