fix: fixing data type and validation also fixing deleting icon in dir

This commit is contained in:
pahmiudahgede 2025-05-16 00:13:45 +07:00
parent fc54cbd118
commit 7fb899ac8b
5 changed files with 125 additions and 65 deletions

View File

@ -1,19 +1,23 @@
package dto package dto
import "strings" import (
"strings"
)
type RequestTrashCategoryDTO struct { type RequestTrashCategoryDTO struct {
Name string `json:"name"` Name string `json:"name"`
Icon string `json:"icon"` EstimatedPrice string `json:"estimatedprice"`
Icon string `json:"icon"`
} }
type ResponseTrashCategoryDTO struct { type ResponseTrashCategoryDTO struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
CreatedAt string `json:"createdAt,omitempty"` EstimatedPrice float64 `json:"estimatedprice"`
UpdatedAt string `json:"updatedAt,omitempty"` CreatedAt string `json:"createdAt,omitempty"`
Details []ResponseTrashDetailDTO `json:"details,omitempty"` UpdatedAt string `json:"updatedAt,omitempty"`
Details []ResponseTrashDetailDTO `json:"details,omitempty"`
} }
type ResponseTrashDetailDTO struct { type ResponseTrashDetailDTO struct {
@ -37,6 +41,11 @@ func (r *RequestTrashCategoryDTO) ValidateTrashCategoryInput() (map[string][]str
if strings.TrimSpace(r.Name) == "" { if strings.TrimSpace(r.Name) == "" {
errors["name"] = append(errors["name"], "name is required") errors["name"] = append(errors["name"], "name is required")
} }
// if valid, msg := utils.ValidateFloatPrice(fmt.Sprintf("%f", r.EstimatedPrice)); !valid {
// errors["estimated_price"] = append(errors["estimated_price"], msg)
// }
if len(errors) > 0 { if len(errors) > 0 {
return errors, false return errors, false
} }
@ -50,9 +59,11 @@ func (r *RequestTrashDetailDTO) ValidateTrashDetailInput() (map[string][]string,
if strings.TrimSpace(r.Description) == "" { if strings.TrimSpace(r.Description) == "" {
errors["description"] = append(errors["description"], "description is required") errors["description"] = append(errors["description"], "description is required")
} }
if r.Price <= 0 {
errors["price"] = append(errors["price"], "price must be greater than 0") // if valid, msg := utils.ValidateFloatPrice(fmt.Sprintf("%f", r.Price)); !valid {
} // errors["price"] = append(errors["price"], msg)
// }
if len(errors) > 0 { if len(errors) > 0 {
return errors, false return errors, false
} }

View File

@ -19,10 +19,16 @@ func NewTrashHandler(trashService services.TrashService) *TrashHandler {
func (h *TrashHandler) CreateCategory(c *fiber.Ctx) error { func (h *TrashHandler) CreateCategory(c *fiber.Ctx) error {
var request dto.RequestTrashCategoryDTO var request dto.RequestTrashCategoryDTO
if err := c.BodyParser(&request); err != nil { if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}}) return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
} }
errors, valid := request.ValidateTrashCategoryInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
iconTrash, err := c.FormFile("icon") iconTrash, err := c.FormFile("icon")
if err != nil { if err != nil {
log.Printf("Error retrieving card photo from request: %v", err) log.Printf("Error retrieving card photo from request: %v", err)

View File

@ -6,6 +6,7 @@ import (
"mime/multipart" "mime/multipart"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"time" "time"
"rijig/dto" "rijig/dto"
@ -101,9 +102,13 @@ func deleteIconTrashFIle(imagePath string) error {
} }
func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) { func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) {
errors, valid := request.ValidateTrashCategoryInput()
if !valid { parsedPrice, err := strconv.ParseFloat(request.EstimatedPrice, 64)
return nil, fmt.Errorf("validation error: %v", errors) fmt.Println("Received estimatedprice:", request.EstimatedPrice)
if err != nil {
return nil, fmt.Errorf("gagal memvalidasi harga: %v", err)
} else {
fmt.Printf("hasil parsing%v", parsedPrice)
} }
icontrashPath, err := s.saveIconOfTrash(iconTrash) icontrashPath, err := s.saveIconOfTrash(iconTrash)
@ -113,7 +118,9 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconT
category := model.TrashCategory{ category := model.TrashCategory{
Name: request.Name, Name: request.Name,
Icon: icontrashPath,
EstimatedPrice: parsedPrice,
Icon: icontrashPath,
} }
if err := s.TrashRepo.CreateCategory(&category); err != nil { if err := s.TrashRepo.CreateCategory(&category); err != nil {
@ -124,11 +131,12 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconT
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon, EstimatedPrice: float64(category.EstimatedPrice),
CreatedAt: createdAt, Icon: category.Icon,
UpdatedAt: updatedAt, CreatedAt: createdAt,
UpdatedAt: updatedAt,
} }
if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, nil, time.Hour*6); err != nil { if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, nil, time.Hour*6); err != nil {
@ -142,11 +150,12 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconT
ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt) ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt)
cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt) cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt)
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: c.ID, ID: c.ID,
Name: c.Name, Name: c.Name,
Icon: c.Icon, EstimatedPrice: float64(c.EstimatedPrice),
CreatedAt: ccreatedAt, Icon: c.Icon,
UpdatedAt: cupdatedAt, CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt,
}) })
} }
@ -227,11 +236,12 @@ func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) {
for _, category := range cachedCategories["data"].([]interface{}) { for _, category := range cachedCategories["data"].([]interface{}) {
categoryData := category.(map[string]interface{}) categoryData := category.(map[string]interface{})
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: categoryData["id"].(string), ID: categoryData["id"].(string),
Name: categoryData["name"].(string), Name: categoryData["name"].(string),
Icon: categoryData["icon"].(string), EstimatedPrice: categoryData["estimatedprice"].(float64),
CreatedAt: categoryData["createdAt"].(string), Icon: categoryData["icon"].(string),
UpdatedAt: categoryData["updatedAt"].(string), CreatedAt: categoryData["createdAt"].(string),
UpdatedAt: categoryData["updatedAt"].(string),
}) })
} }
return categoriesDTO, nil return categoriesDTO, nil
@ -244,19 +254,19 @@ func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) {
var categoriesDTO []dto.ResponseTrashCategoryDTO var categoriesDTO []dto.ResponseTrashCategoryDTO
for _, category := range categories { for _, category := range categories {
// path := os.Getenv("BASE_URL")
createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon, EstimatedPrice: category.EstimatedPrice,
CreatedAt: createdAt, Icon: category.Icon,
UpdatedAt: updatedAt, CreatedAt: createdAt,
UpdatedAt: updatedAt,
}) })
} }
cacheData := map[string]interface{}{ cacheData := map[string]interface{}{
"data": categoriesDTO, "data": categoriesDTO,
} }
@ -274,12 +284,13 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO
categoryData := cachedCategory["data"].(map[string]interface{}) categoryData := cachedCategory["data"].(map[string]interface{})
details := mapDetails(cachedCategory["details"]) details := mapDetails(cachedCategory["details"])
return &dto.ResponseTrashCategoryDTO{ return &dto.ResponseTrashCategoryDTO{
ID: categoryData["id"].(string), ID: categoryData["id"].(string),
Name: categoryData["name"].(string), Name: categoryData["name"].(string),
Icon: categoryData["icon"].(string), EstimatedPrice: categoryData["estimatedprice"].(float64),
CreatedAt: categoryData["createdAt"].(string), Icon: categoryData["icon"].(string),
UpdatedAt: categoryData["updatedAt"].(string), CreatedAt: categoryData["createdAt"].(string),
Details: details, UpdatedAt: categoryData["updatedAt"].(string),
Details: details,
}, nil }, nil
} }
@ -292,11 +303,12 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryDTO := &dto.ResponseTrashCategoryDTO{ categoryDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon, EstimatedPrice: category.EstimatedPrice,
CreatedAt: createdAt, Icon: category.Icon,
UpdatedAt: updatedAt, CreatedAt: createdAt,
UpdatedAt: updatedAt,
} }
if category.Details != nil { if category.Details != nil {
@ -405,11 +417,12 @@ func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategor
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID, ID: category.ID,
Name: category.Name, Name: category.Name,
Icon: category.Icon, EstimatedPrice: category.EstimatedPrice,
CreatedAt: createdAt, Icon: category.Icon,
UpdatedAt: updatedAt, CreatedAt: createdAt,
UpdatedAt: updatedAt,
} }
if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, category.Details, time.Hour*6); err != nil { if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, category.Details, time.Hour*6); err != nil {
@ -423,11 +436,12 @@ func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategor
ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt) ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt)
cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt) cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt)
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: c.ID, ID: c.ID,
Name: c.Name, Name: c.Name,
Icon: c.Icon, EstimatedPrice: c.EstimatedPrice,
CreatedAt: ccreatedAt, Icon: c.Icon,
UpdatedAt: cupdatedAt, CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt,
}) })
} }
@ -512,6 +526,15 @@ func (s *trashService) DeleteCategory(id string) error {
} }
} }
category, err := s.TrashRepo.GetCategoryByID(id)
if err != nil {
return fmt.Errorf("failed to fetch category for deletion: %v", err)
}
if err := deleteIconTrashFIle(category.Icon); err != nil {
return fmt.Errorf("error deleting icon for category %s: %v", id, err)
}
if err := s.TrashRepo.DeleteCategory(id); err != nil { if err := s.TrashRepo.DeleteCategory(id); err != nil {
return fmt.Errorf("failed to delete category: %v", err) return fmt.Errorf("failed to delete category: %v", err)
} }

View File

@ -3,12 +3,13 @@ package model
import "time" import "time"
type TrashCategory struct { type TrashCategory struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
Details []TrashDetail `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE;" json:"details"` EstimatedPrice float64 `gorm:"not null" json:"estimated_price"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"` Details []TrashDetail `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE;" json:"details"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"` CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
} }
type TrashDetail struct { type TrashDetail struct {

View File

@ -1,7 +1,9 @@
package utils package utils
import ( import (
"fmt"
"regexp" "regexp"
"strconv"
"strings" "strings"
) )
@ -42,3 +44,20 @@ func isSpecialCharacter(char rune) bool {
specialChars := "!@#$%^&*()-_=+[]{}|;:'\",.<>?/`~" specialChars := "!@#$%^&*()-_=+[]{}|;:'\",.<>?/`~"
return strings.ContainsRune(specialChars, char) return strings.ContainsRune(specialChars, char)
} }
func ValidateFloatPrice(price string) (float64, error) {
// price = strings.Trim(price, `"`)
// price = strings.TrimSpace(price)
parsedPrice, err := strconv.ParseFloat(price, 64)
if err != nil {
return 0, fmt.Errorf("harga tidak valid. Format harga harus angka desimal.")
}
if parsedPrice <= 0 {
return 0, fmt.Errorf("harga harus lebih besar dari 0.")
}
return parsedPrice, nil
}