diff --git a/config/connection.go b/config/connection.go index 842d180..934ad96 100644 --- a/config/connection.go +++ b/config/connection.go @@ -69,6 +69,7 @@ func InitDatabase() { &domain.RequestItem{}, &domain.Product{}, &domain.ProductImage{}, + &domain.Store{}, ) if err != nil { log.Fatal("Error: Failed to auto migrate domain:", err) diff --git a/domain/product.go b/domain/product.go index a921304..8c7e025 100644 --- a/domain/product.go +++ b/domain/product.go @@ -5,6 +5,8 @@ import "time" type Product struct { ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` UserID string `gorm:"type:uuid;not null" json:"user_id"` + StoreID string `gorm:"type:uuid;not null" json:"store_id"` + Store Store `gorm:"foreignKey:StoreID" json:"store"` ProductTitle string `gorm:"not null" json:"product_title"` ProductImages []ProductImage `gorm:"foreignKey:ProductID;constraint:OnDelete:CASCADE;" json:"product_images"` TrashDetailID string `gorm:"type:uuid;not null" json:"trash_detail_id"` diff --git a/domain/store.go b/domain/store.go new file mode 100644 index 0000000..851a619 --- /dev/null +++ b/domain/store.go @@ -0,0 +1,80 @@ +package domain + +import "time" + +type Store struct { + ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` + UserID string `gorm:"type:uuid;not null" json:"user_id"` + User User `gorm:"foreignKey:UserID" json:"user"` + StoreName string `gorm:"not null;unique" json:"store_name"` + StoreLogo string `json:"store_logo"` + StoreBanner string `json:"store_banner"` + StoreDesc string `gorm:"type:text" json:"store_desc"` + Follower int `gorm:"default:0" json:"follower"` + StoreRating float64 `gorm:"default:0" json:"store_rating"` + CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` + UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +} + +// type StoreFinance struct { +// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` +// StoreID string `gorm:"type:uuid;not null" json:"store_id"` +// TotalRevenue int64 `gorm:"default:0" json:"total_revenue"` +// TotalExpenses int64 `gorm:"default:0" json:"total_expenses"` +// NetProfit int64 `gorm:"default:0" json:"net_profit"` +// OrdersCount int `gorm:"default:0" json:"orders_count"` +// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` +// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +// } + +// type Order struct { +// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` +// StoreID string `gorm:"type:uuid;not null" json:"store_id"` +// UserID string `gorm:"type:uuid;not null" json:"user_id"` +// TotalPrice int64 `gorm:"not null" json:"total_price"` +// OrderStatus string `gorm:"not null" json:"order_status"` +// ShippingStatus string `gorm:"not null" json:"shipping_status"` +// PaymentStatus string `gorm:"not null" json:"payment_status"` +// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` +// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +// } + +// type OrderDetail struct { +// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` +// OrderID string `gorm:"type:uuid;not null" json:"order_id"` +// ProductID string `gorm:"type:uuid;not null" json:"product_id"` +// Quantity int `gorm:"not null" json:"quantity"` +// UnitPrice int64 `gorm:"not null" json:"unit_price"` +// TotalPrice int64 `gorm:"not null" json:"total_price"` +// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` +// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +// } + +// type Shipping struct { +// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` +// OrderID string `gorm:"type:uuid;not null" json:"order_id"` +// ShippingDate time.Time `gorm:"default:current_timestamp" json:"shipping_date"` +// ShippingCost int64 `gorm:"not null" json:"shipping_cost"` +// TrackingNo string `gorm:"not null" json:"tracking_no"` +// ShippingStatus string `gorm:"not null" json:"shipping_status"` +// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` +// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +// } + +// type Cancellation struct { +// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` +// OrderID string `gorm:"type:uuid;not null" json:"order_id"` +// Reason string `gorm:"type:text" json:"reason"` +// CancelledAt time.Time `gorm:"default:current_timestamp" json:"cancelled_at"` +// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` +// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +// } + +// type Return struct { +// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` +// OrderID string `gorm:"type:uuid;not null" json:"order_id"` +// Reason string `gorm:"type:text" json:"reason"` +// ReturnedAt time.Time `gorm:"default:current_timestamp" json:"returned_at"` +// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` +// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +// } diff --git a/dto/product.go b/dto/product.go index 6a50b29..6a64851 100644 --- a/dto/product.go +++ b/dto/product.go @@ -4,7 +4,7 @@ import "errors" type ProductResponseWithSoldDTO struct { ID string `json:"id"` - UserID string `json:"user_id"` + StoreID string `json:"store_id"` ProductTitle string `json:"product_title"` ProductImages []ProductImageDTO `json:"product_images"` TrashDetail TrashDetailResponseDTO `json:"trash_detail"` @@ -18,7 +18,7 @@ type ProductResponseWithSoldDTO struct { type ProductResponseWithoutSoldDTO struct { ID string `json:"id"` - UserID string `json:"user_id"` + StoreID string `json:"store_id"` ProductTitle string `json:"product_title"` ProductImages []ProductImageDTO `json:"product_images"` TrashDetail TrashDetailResponseDTO `json:"trash_detail"` @@ -31,7 +31,7 @@ type ProductResponseWithoutSoldDTO struct { type ProductResponseDTO struct { ID string `json:"id"` - UserID string `json:"user_id"` + StoreID string `json:"store_id"` ProductTitle string `json:"product_title"` ProductImages []ProductImageDTO `json:"product_images"` TrashDetail TrashDetailResponseDTO `json:"trash_detail"` @@ -53,6 +53,7 @@ type TrashDetailResponseDTO struct { } type CreateProductRequestDTO struct { + StoreID string `json:"storeid" validate:"required,uuid"` 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"` @@ -63,7 +64,7 @@ type CreateProductRequestDTO struct { type CreateProductResponseDTO struct { ID string `json:"id"` - UserID string `json:"user_id"` + StoreID string `json:"store_id"` ProductTitle string `json:"product_title"` ProductImages []string `json:"product_images"` TrashDetail TrashDetailResponseDTO `json:"trash_detail"` @@ -84,8 +85,9 @@ type UpdateProductRequestDTO struct { } func ValidateSalePrice(marketPrice, salePrice int64) error { - if salePrice > marketPrice { - return errors.New("sale price cannot be greater than market price") + + if salePrice > marketPrice*2 { + return errors.New("sale price cannot be more than twice the market price") } return nil } diff --git a/dto/store.go b/dto/store.go new file mode 100644 index 0000000..e3990b0 --- /dev/null +++ b/dto/store.go @@ -0,0 +1,14 @@ +package dto + +type StoreResponseDTO struct { + ID string `json:"id"` + UserID string `json:"user_id"` + StoreName string `json:"store_name"` + StoreLogo string `json:"store_logo"` + StoreBanner string `json:"store_banner"` + StoreDesc string `json:"store_desc"` + Follower int `json:"follower"` + StoreRating float64 `json:"store_rating"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/internal/api/routes.go b/internal/api/routes.go index 3ca8424..6a833d0 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -109,7 +109,12 @@ func AppRouter(app *fiber.App) { // # product post by pengepul api.Get("/post/products", middleware.RoleRequired(utils.RolePengepul), controllers.GetAllProducts) api.Get("/post/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.GetProductByID) + api.Get("/view/product/:storeid", middleware.RoleRequired(utils.RolePengepul), controllers.GetProductsByStore) 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) + + // # marketplace + api.Get("/store/marketplace", middleware.RoleRequired(utils.RolePengepul), controllers.GetStoresByUserID) + api.Get("/store/marketplace/:storeid", middleware.RoleRequired(utils.RolePengepul), controllers.GetStoreByID) } diff --git a/internal/controllers/product.go b/internal/controllers/product.go index 0be53ab..a985015 100644 --- a/internal/controllers/product.go +++ b/internal/controllers/product.go @@ -17,10 +17,12 @@ func GetAllProducts(c *fiber.Ctx) error { )) } - limit := c.QueryInt("limit", 0) + storeID := c.Query("storeID", "") + + limit := c.QueryInt("limit", 10) page := c.QueryInt("page", 1) - if limit < 0 || page <= 0 { + if limit <= 0 || page <= 0 { return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( fiber.StatusBadRequest, "Invalid pagination parameters", @@ -28,7 +30,17 @@ func GetAllProducts(c *fiber.Ctx) error { )) } - products, err := services.GetProductsByUserID(userID, limit, page) + var products []dto.ProductResponseWithSoldDTO + var err error + + if storeID != "" { + + products, err = services.GetProductsByStoreID(storeID, limit, page) + } else { + + products, err = services.GetProductsByUserID(userID, limit, page) + } + if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( fiber.StatusInternalServerError, @@ -45,11 +57,11 @@ func GetAllProducts(c *fiber.Ctx) error { } func GetProductByID(c *fiber.Ctx) error { - userID, ok := c.Locals("userID").(string) - if !ok || userID == "" { + storeID, ok := c.Locals("userID").(string) + if !ok || storeID == "" { return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse( fiber.StatusUnauthorized, - "Unauthorized: user ID is missing", + "Unauthorized: store ID is missing", nil, )) } @@ -63,7 +75,7 @@ func GetProductByID(c *fiber.Ctx) error { )) } - product, err := services.GetProductByIDAndUserID(productID, userID) + product, err := services.GetProductByIDAndStoreID(productID, storeID) if err != nil { return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( fiber.StatusNotFound, @@ -79,8 +91,46 @@ func GetProductByID(c *fiber.Ctx) error { )) } +func GetProductsByStore(c *fiber.Ctx) error { + storeID := c.Params("storeid") + if storeID == "" { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Store ID is required", + nil, + )) + } + + limit := c.QueryInt("limit", 10) + page := c.QueryInt("page", 1) + + if limit <= 0 || page <= 0 { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Invalid pagination parameters", + nil, + )) + } + + products, err := services.GetProductsByStoreID(storeID, limit, page) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( + fiber.StatusNotFound, + "Store not found", + nil, + )) + } + + return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( + fiber.StatusOK, + "Products fetched successfully", + products, + )) +} + func CreateProduct(c *fiber.Ctx) error { var input dto.CreateProductRequestDTO + if err := c.BodyParser(&input); err != nil { return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( fiber.StatusBadRequest, @@ -98,6 +148,14 @@ func CreateProduct(c *fiber.Ctx) error { )) } + if input.StoreID == "" { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Store ID is required in the body", + nil, + )) + } + product, err := services.CreateProduct(input, userID) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(utils.FormatResponse( @@ -115,14 +173,7 @@ func CreateProduct(c *fiber.Ctx) error { } 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, - )) - } + productID := c.Params("productid") userID, ok := c.Locals("userID").(string) if !ok || userID == "" { @@ -133,7 +184,6 @@ func UpdateProduct(c *fiber.Ctx) error { )) } - productID := c.Params("productid") if productID == "" { return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( fiber.StatusBadRequest, @@ -142,7 +192,24 @@ func UpdateProduct(c *fiber.Ctx) error { )) } - product, err := services.UpdateProduct(productID, userID, input) + 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, + )) + } + + if err := dto.GetValidator().Struct(input); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Invalid product data", + nil, + )) + } + + updatedProduct, err := services.UpdateProduct(productID, input) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(utils.FormatResponse( fiber.StatusUnprocessableEntity, @@ -154,19 +221,12 @@ func UpdateProduct(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( fiber.StatusOK, "Product updated successfully", - product, + updatedProduct, )) } 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 == "" { @@ -177,18 +237,19 @@ func DeleteProduct(c *fiber.Ctx) error { )) } - err := services.DeleteProduct(productID, userID) + if productID == "" { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Product ID is required", + nil, + )) + } + + err := services.DeleteProduct(productID) 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(), + return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( + fiber.StatusNotFound, + "Product not found or unable to delete", nil, )) } diff --git a/internal/controllers/store.go b/internal/controllers/store.go new file mode 100644 index 0000000..e50d905 --- /dev/null +++ b/internal/controllers/store.go @@ -0,0 +1,70 @@ +package controllers + +import ( + "github.com/gofiber/fiber/v2" + "github.com/pahmiudahgede/senggoldong/internal/services" + "github.com/pahmiudahgede/senggoldong/utils" +) + +func GetStoreByID(c *fiber.Ctx) error { + storeID := c.Params("storeid") + if storeID == "" { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Store ID is required", + nil, + )) + } + + store, err := services.GetStoreByID(storeID) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( + fiber.StatusNotFound, + "Store not found", + nil, + )) + } + + return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( + fiber.StatusOK, + "Store fetched successfully", + store, + )) +} + +func GetStoresByUserID(c *fiber.Ctx) error { + 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, + )) + } + + limit := c.QueryInt("limit", 0) + page := c.QueryInt("page", 1) + + if limit < 0 || page <= 0 { + return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( + fiber.StatusBadRequest, + "Invalid pagination parameters", + nil, + )) + } + + stores, err := services.GetStoresByUserID(userID, limit, page) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( + fiber.StatusInternalServerError, + "Failed to fetch stores", + nil, + )) + } + + return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( + fiber.StatusOK, + "Stores fetched successfully", + stores, + )) +} diff --git a/internal/repositories/product.go b/internal/repositories/product.go index c7d391c..703a437 100644 --- a/internal/repositories/product.go +++ b/internal/repositories/product.go @@ -6,6 +6,18 @@ import ( "gorm.io/gorm" ) +func GetProductsByStoreID(storeID string, limit, offset int) ([]domain.Product, error) { + var products []domain.Product + query := config.DB.Preload("ProductImages").Preload("TrashDetail").Where("store_id = ?", storeID) + + if limit > 0 { + query = query.Limit(limit).Offset(offset) + } + + err := query.Find(&products).Error + return products, err +} + func GetProductsByUserID(userID string, limit, offset int) ([]domain.Product, error) { var products []domain.Product query := config.DB.Preload("ProductImages").Preload("TrashDetail").Where("user_id = ?", userID) @@ -18,10 +30,10 @@ func GetProductsByUserID(userID string, limit, offset int) ([]domain.Product, er return products, err } -func GetProductByIDAndUserID(productID, userID string) (domain.Product, error) { +func GetProductByIDAndStoreID(productID, storeID string) (domain.Product, error) { var product domain.Product err := config.DB.Preload("ProductImages").Preload("TrashDetail"). - Where("id = ? AND user_id = ?", productID, userID). + Where("id = ? AND store_id = ?", productID, storeID). First(&product).Error return product, err @@ -34,8 +46,18 @@ func GetProductByID(productID string) (domain.Product, error) { return product, err } +func IsValidStoreID(storeID string) bool { + var count int64 + err := config.DB.Model(&domain.Store{}).Where("id = ?", storeID).Count(&count).Error + if err != nil || count == 0 { + return false + } + return true +} + func CreateProduct(product *domain.Product, images []domain.ProductImage) error { - err := config.DB.Transaction(func(tx *gorm.DB) error { + + return config.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Create(product).Error; err != nil { return err @@ -45,23 +67,20 @@ func CreateProduct(product *domain.Product, images []domain.ProductImage) error for i := range images { images[i].ProductID = product.ID } + if err := tx.Create(&images).Error; err != nil { return err } } + return nil }) - 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 { + if err := tx.Save(product).Error; err != nil { return err } @@ -69,6 +88,11 @@ func UpdateProduct(product *domain.Product, images []domain.ProductImage) error for i := range images { images[i].ProductID = product.ID } + + if err := tx.Where("product_id = ?", product.ID).Delete(&domain.ProductImage{}).Error; err != nil { + return err + } + if err := tx.Create(&images).Error; err != nil { return err } @@ -78,7 +102,7 @@ func UpdateProduct(product *domain.Product, images []domain.ProductImage) error }) } -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 +func DeleteProduct(productID string) error { + + return config.DB.Where("id = ?", productID).Delete(&domain.Product{}).Error } diff --git a/internal/repositories/store.go b/internal/repositories/store.go new file mode 100644 index 0000000..9e32c02 --- /dev/null +++ b/internal/repositories/store.go @@ -0,0 +1,24 @@ +package repositories + +import ( + "github.com/pahmiudahgede/senggoldong/config" + "github.com/pahmiudahgede/senggoldong/domain" +) + +func GetStoreByID(storeID string) (domain.Store, error) { + var store domain.Store + err := config.DB.Where("id = ?", storeID).First(&store).Error + return store, err +} + +func GetStoresByUserID(userID string, limit, offset int) ([]domain.Store, error) { + var stores []domain.Store + query := config.DB.Where("user_id = ?", userID) + + if limit > 0 { + query = query.Limit(limit).Offset(offset) + } + + err := query.Find(&stores).Error + return stores, err +} diff --git a/internal/services/product.go b/internal/services/product.go index 38dc4f1..c70727f 100644 --- a/internal/services/product.go +++ b/internal/services/product.go @@ -9,6 +9,18 @@ import ( "github.com/pahmiudahgede/senggoldong/utils" ) +func GetProductsByStoreID(storeID string, limit, page int) ([]dto.ProductResponseWithSoldDTO, error) { + + offset := (page - 1) * limit + + products, err := repositories.GetProductsByStoreID(storeID, limit, offset) + if err != nil { + return nil, err + } + + return mapProductsToDTO(products), nil +} + func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseWithSoldDTO, error) { offset := (page - 1) * limit products, err := repositories.GetProductsByUserID(userID, limit, offset) @@ -16,6 +28,10 @@ func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseW return nil, err } + return mapProductsToDTO(products), nil +} + +func mapProductsToDTO(products []domain.Product) []dto.ProductResponseWithSoldDTO { var productResponses []dto.ProductResponseWithSoldDTO for _, product := range products { var images []dto.ProductImageDTO @@ -25,7 +41,7 @@ func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseW productResponses = append(productResponses, dto.ProductResponseWithSoldDTO{ ID: product.ID, - UserID: product.UserID, + StoreID: product.StoreID, ProductTitle: product.ProductTitle, ProductImages: images, TrashDetail: dto.TrashDetailResponseDTO{ @@ -41,12 +57,11 @@ func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseW UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt), }) } - - return productResponses, nil + return productResponses } -func GetProductByIDAndUserID(productID, userID string) (dto.ProductResponseWithSoldDTO, error) { - product, err := repositories.GetProductByIDAndUserID(productID, userID) +func GetProductByIDAndStoreID(productID, storeID string) (dto.ProductResponseWithSoldDTO, error) { + product, err := repositories.GetProductByIDAndStoreID(productID, storeID) if err != nil { return dto.ProductResponseWithSoldDTO{}, err } @@ -58,7 +73,7 @@ func GetProductByIDAndUserID(productID, userID string) (dto.ProductResponseWithS return dto.ProductResponseWithSoldDTO{ ID: product.ID, - UserID: product.UserID, + StoreID: product.StoreID, ProductTitle: product.ProductTitle, ProductImages: images, TrashDetail: dto.TrashDetailResponseDTO{ @@ -80,8 +95,20 @@ func CreateProduct(input dto.CreateProductRequestDTO, userID string) (dto.Create return dto.CreateProductResponseDTO{}, err } + trashDetail, err := repositories.GetTrashDetailByID(input.TrashDetailID) + if err != nil { + return dto.CreateProductResponseDTO{}, err + } + + marketPrice := int64(trashDetail.Price) + + if err := dto.ValidateSalePrice(marketPrice, input.SalePrice); err != nil { + return dto.CreateProductResponseDTO{}, err + } + product := &domain.Product{ UserID: userID, + StoreID: input.StoreID, ProductTitle: input.ProductTitle, TrashDetailID: input.TrashDetailID, SalePrice: input.SalePrice, @@ -98,14 +125,14 @@ func CreateProduct(input dto.CreateProductRequestDTO, userID string) (dto.Create return dto.CreateProductResponseDTO{}, err } - trashDetail, err := repositories.GetTrashDetailByID(product.TrashDetailID) + trashDetail, err = repositories.GetTrashDetailByID(product.TrashDetailID) if err != nil { return dto.CreateProductResponseDTO{}, err } return dto.CreateProductResponseDTO{ ID: product.ID, - UserID: product.UserID, + StoreID: product.StoreID, ProductTitle: product.ProductTitle, ProductImages: input.ProductImages, TrashDetail: dto.TrashDetailResponseDTO{ @@ -121,65 +148,61 @@ func CreateProduct(input dto.CreateProductRequestDTO, userID string) (dto.Create }, nil } -func UpdateProduct(productID, userID string, input dto.UpdateProductRequestDTO) (dto.ProductResponseDTO, error) { - if err := dto.GetValidator().Struct(input); err != nil { - return dto.ProductResponseDTO{}, err +func UpdateProduct(productID string, input dto.UpdateProductRequestDTO) (dto.CreateProductResponseDTO, error) { + + product, err := repositories.GetProductByID(productID) + if err != nil { + return dto.CreateProductResponseDTO{}, errors.New("product not found") } - product := &domain.Product{ - ID: productID, - UserID: userID, - ProductTitle: input.ProductTitle, - TrashDetailID: input.TrashDetailID, - SalePrice: input.SalePrice, - Quantity: input.Quantity, - ProductDescribe: input.ProductDescribe, - } + product.ProductTitle = input.ProductTitle + product.TrashDetailID = input.TrashDetailID + product.SalePrice = input.SalePrice + product.Quantity = input.Quantity + product.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 + if err := repositories.UpdateProduct(&product, images); err != nil { + return dto.CreateProductResponseDTO{}, err } - updatedProduct, err := repositories.GetProductByID(productID) + trashDetail, err := repositories.GetTrashDetailByID(product.TrashDetailID) if err != nil { - return dto.ProductResponseDTO{}, err + return dto.CreateProductResponseDTO{}, 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, + return dto.CreateProductResponseDTO{ + ID: product.ID, + StoreID: product.StoreID, + ProductTitle: product.ProductTitle, + ProductImages: input.ProductImages, TrashDetail: dto.TrashDetailResponseDTO{ - ID: updatedProduct.TrashDetail.ID, - Description: updatedProduct.TrashDetail.Description, - Price: updatedProduct.TrashDetail.Price, + ID: trashDetail.ID, + Description: trashDetail.Description, + Price: trashDetail.Price, }, - SalePrice: updatedProduct.SalePrice, - Quantity: updatedProduct.Quantity, - ProductDescribe: updatedProduct.ProductDescribe, - CreatedAt: utils.FormatDateToIndonesianFormat(updatedProduct.CreatedAt), - UpdatedAt: utils.FormatDateToIndonesianFormat(updatedProduct.UpdatedAt), + SalePrice: product.SalePrice, + Quantity: product.Quantity, + ProductDescribe: product.ProductDescribe, + CreatedAt: utils.FormatDateToIndonesianFormat(product.CreatedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt), }, nil } -func DeleteProduct(productID, userID string) error { - rowsAffected, err := repositories.DeleteProduct(productID, userID) +func DeleteProduct(productID string) error { + + _, err := repositories.GetProductByID(productID) if err != nil { + return errors.New("product not found") + } + + if err := repositories.DeleteProduct(productID); err != nil { return err } - if rowsAffected == 0 { - return errors.New("product not found or not authorized to delete") - } + return nil } diff --git a/internal/services/store.go b/internal/services/store.go new file mode 100644 index 0000000..f9648a9 --- /dev/null +++ b/internal/services/store.go @@ -0,0 +1,53 @@ +package services + +import ( + "github.com/pahmiudahgede/senggoldong/dto" + "github.com/pahmiudahgede/senggoldong/internal/repositories" + "github.com/pahmiudahgede/senggoldong/utils" +) + +func GetStoreByID(storeID string) (dto.StoreResponseDTO, error) { + store, err := repositories.GetStoreByID(storeID) + if err != nil { + return dto.StoreResponseDTO{}, err + } + + return dto.StoreResponseDTO{ + ID: store.ID, + UserID: store.UserID, + StoreName: store.StoreName, + StoreLogo: store.StoreLogo, + StoreBanner: store.StoreBanner, + StoreDesc: store.StoreDesc, + Follower: store.Follower, + StoreRating: store.StoreRating, + CreatedAt: utils.FormatDateToIndonesianFormat(store.CreatedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(store.UpdatedAt), + }, nil +} + +func GetStoresByUserID(userID string, limit, page int) ([]dto.StoreResponseDTO, error) { + offset := (page - 1) * limit + stores, err := repositories.GetStoresByUserID(userID, limit, offset) + if err != nil { + return nil, err + } + + var storeResponses []dto.StoreResponseDTO + for _, store := range stores { + storeResponses = append(storeResponses, dto.StoreResponseDTO{ + ID: store.ID, + UserID: store.UserID, + StoreName: store.StoreName, + StoreLogo: store.StoreLogo, + StoreBanner: store.StoreBanner, + StoreDesc: store.StoreDesc, + Follower: store.Follower, + StoreRating: store.StoreRating, + CreatedAt: utils.FormatDateToIndonesianFormat(store.CreatedAt), + UpdatedAt: utils.FormatDateToIndonesianFormat(store.UpdatedAt), + }) + } + + return storeResponses, nil +}