MIF_E31222379_BE/internal/trash/trash_service.go

751 lines
25 KiB
Go

package trash
import (
"context"
"errors"
"fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"rijig/model"
"strings"
"time"
"github.com/google/uuid"
)
type TrashServiceInterface interface {
CreateTrashCategory(ctx context.Context, req RequestTrashCategoryDTO) (*ResponseTrashCategoryDTO, error)
CreateTrashCategoryWithDetails(ctx context.Context, categoryReq RequestTrashCategoryDTO, detailsReq []RequestTrashDetailDTO) (*ResponseTrashCategoryDTO, error)
CreateTrashCategoryWithIcon(ctx context.Context, req RequestTrashCategoryDTO, iconFile *multipart.FileHeader) (*ResponseTrashCategoryDTO, error)
UpdateTrashCategory(ctx context.Context, id string, req RequestTrashCategoryDTO) (*ResponseTrashCategoryDTO, error)
UpdateTrashCategoryWithIcon(ctx context.Context, id string, req RequestTrashCategoryDTO, iconFile *multipart.FileHeader) (*ResponseTrashCategoryDTO, error)
GetAllTrashCategories(ctx context.Context) ([]ResponseTrashCategoryDTO, error)
GetAllTrashCategoriesWithDetails(ctx context.Context) ([]ResponseTrashCategoryDTO, error)
GetTrashCategoryByID(ctx context.Context, id string) (*ResponseTrashCategoryDTO, error)
GetTrashCategoryByIDWithDetails(ctx context.Context, id string) (*ResponseTrashCategoryDTO, error)
DeleteTrashCategory(ctx context.Context, id string) error
CreateTrashDetail(ctx context.Context, req RequestTrashDetailDTO) (*ResponseTrashDetailDTO, error)
CreateTrashDetailWithIcon(ctx context.Context, req RequestTrashDetailDTO, iconFile *multipart.FileHeader) (*ResponseTrashDetailDTO, error)
AddTrashDetailToCategory(ctx context.Context, categoryID string, req RequestTrashDetailDTO) (*ResponseTrashDetailDTO, error)
AddTrashDetailToCategoryWithIcon(ctx context.Context, categoryID string, req RequestTrashDetailDTO, iconFile *multipart.FileHeader) (*ResponseTrashDetailDTO, error)
UpdateTrashDetail(ctx context.Context, id string, req RequestTrashDetailDTO) (*ResponseTrashDetailDTO, error)
UpdateTrashDetailWithIcon(ctx context.Context, id string, req RequestTrashDetailDTO, iconFile *multipart.FileHeader) (*ResponseTrashDetailDTO, error)
GetTrashDetailsByCategory(ctx context.Context, categoryID string) ([]ResponseTrashDetailDTO, error)
GetTrashDetailByID(ctx context.Context, id string) (*ResponseTrashDetailDTO, error)
DeleteTrashDetail(ctx context.Context, id string) error
BulkCreateTrashDetails(ctx context.Context, categoryID string, detailsReq []RequestTrashDetailDTO) ([]ResponseTrashDetailDTO, error)
BulkDeleteTrashDetails(ctx context.Context, detailIDs []string) error
ReorderTrashDetails(ctx context.Context, categoryID string, orderedDetailIDs []string) error
}
type TrashService struct {
trashRepo TrashRepositoryInterface
}
func NewTrashService(trashRepo TrashRepositoryInterface) TrashServiceInterface {
return &TrashService{
trashRepo: trashRepo,
}
}
func (s *TrashService) saveIconOfTrash(iconTrash *multipart.FileHeader) (string, error) {
pathImage := "/uploads/icontrash/"
iconTrashDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(iconTrashDir); os.IsNotExist(err) {
if err := os.MkdirAll(iconTrashDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for icon trash: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := strings.ToLower(filepath.Ext(iconTrash.Filename))
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, .png, and .svg are allowed")
}
iconTrashFileName := fmt.Sprintf("%s_icontrash%s", uuid.New().String(), extension)
iconTrashPath := filepath.Join(iconTrashDir, iconTrashFileName)
src, err := iconTrash.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(iconTrashPath)
if err != nil {
return "", fmt.Errorf("failed to create icon trash file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save icon trash: %v", err)
}
iconTrashUrl := fmt.Sprintf("%s%s", pathImage, iconTrashFileName)
return iconTrashUrl, nil
}
func (s *TrashService) saveIconOfTrashDetail(iconTrashDetail *multipart.FileHeader) (string, error) {
pathImage := "/uploads/icontrashdetail/"
iconTrashDetailDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(iconTrashDetailDir); os.IsNotExist(err) {
if err := os.MkdirAll(iconTrashDetailDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for icon trash detail: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := strings.ToLower(filepath.Ext(iconTrashDetail.Filename))
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, .png, and .svg are allowed")
}
iconTrashDetailFileName := fmt.Sprintf("%s_icontrashdetail%s", uuid.New().String(), extension)
iconTrashDetailPath := filepath.Join(iconTrashDetailDir, iconTrashDetailFileName)
src, err := iconTrashDetail.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(iconTrashDetailPath)
if err != nil {
return "", fmt.Errorf("failed to create icon trash detail file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save icon trash detail: %v", err)
}
iconTrashDetailUrl := fmt.Sprintf("%s%s", pathImage, iconTrashDetailFileName)
return iconTrashDetailUrl, nil
}
func (s *TrashService) deleteIconTrashFile(imagePath string) error {
if imagePath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + imagePath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Image deleted successfully: %s", absolutePath)
return nil
}
func (s *TrashService) deleteIconTrashDetailFile(imagePath string) error {
if imagePath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + imagePath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Trash detail image deleted successfully: %s", absolutePath)
return nil
}
func (s *TrashService) CreateTrashCategoryWithIcon(ctx context.Context, req RequestTrashCategoryDTO, iconFile *multipart.FileHeader) (*ResponseTrashCategoryDTO, error) {
if errors, valid := req.ValidateRequestTrashCategoryDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
var iconUrl string
var err error
if iconFile != nil {
iconUrl, err = s.saveIconOfTrash(iconFile)
if err != nil {
return nil, fmt.Errorf("failed to save icon: %w", err)
}
}
category := &model.TrashCategory{
Name: req.Name,
IconTrash: iconUrl,
EstimatedPrice: req.EstimatedPrice,
Variety: req.Variety,
}
if err := s.trashRepo.CreateTrashCategory(ctx, category); err != nil {
if iconUrl != "" {
s.deleteIconTrashFile(iconUrl)
}
return nil, fmt.Errorf("failed to create trash category: %w", err)
}
response := s.convertTrashCategoryToResponseDTO(category)
return response, nil
}
func (s *TrashService) UpdateTrashCategoryWithIcon(ctx context.Context, id string, req RequestTrashCategoryDTO, iconFile *multipart.FileHeader) (*ResponseTrashCategoryDTO, error) {
if errors, valid := req.ValidateRequestTrashCategoryDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
existingCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get existing category: %w", err)
}
var iconUrl string = existingCategory.IconTrash
if iconFile != nil {
newIconUrl, err := s.saveIconOfTrash(iconFile)
if err != nil {
return nil, fmt.Errorf("failed to save new icon: %w", err)
}
iconUrl = newIconUrl
}
updates := map[string]interface{}{
"name": req.Name,
"icon_trash": iconUrl,
"estimated_price": req.EstimatedPrice,
"variety": req.Variety,
}
if err := s.trashRepo.UpdateTrashCategory(ctx, id, updates); err != nil {
if iconFile != nil && iconUrl != existingCategory.IconTrash {
s.deleteIconTrashFile(iconUrl)
}
return nil, fmt.Errorf("failed to update trash category: %w", err)
}
if iconFile != nil && existingCategory.IconTrash != "" && iconUrl != existingCategory.IconTrash {
if err := s.deleteIconTrashFile(existingCategory.IconTrash); err != nil {
log.Printf("Warning: failed to delete old icon: %v", err)
}
}
updatedCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to retrieve updated category: %w", err)
}
response := s.convertTrashCategoryToResponseDTO(updatedCategory)
return response, nil
}
func (s *TrashService) CreateTrashDetailWithIcon(ctx context.Context, req RequestTrashDetailDTO, iconFile *multipart.FileHeader) (*ResponseTrashDetailDTO, error) {
if errors, valid := req.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
var iconUrl string
var err error
if iconFile != nil {
iconUrl, err = s.saveIconOfTrashDetail(iconFile)
if err != nil {
return nil, fmt.Errorf("failed to save icon: %w", err)
}
}
detail := &model.TrashDetail{
TrashCategoryID: req.CategoryID,
IconTrashDetail: iconUrl,
Description: req.Description,
StepOrder: req.StepOrder,
}
if err := s.trashRepo.CreateTrashDetail(ctx, detail); err != nil {
if iconUrl != "" {
s.deleteIconTrashDetailFile(iconUrl)
}
return nil, fmt.Errorf("failed to create trash detail: %w", err)
}
response := s.convertTrashDetailToResponseDTO(detail)
return response, nil
}
func (s *TrashService) AddTrashDetailToCategoryWithIcon(ctx context.Context, categoryID string, req RequestTrashDetailDTO, iconFile *multipart.FileHeader) (*ResponseTrashDetailDTO, error) {
if errors, valid := req.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
var iconUrl string
var err error
if iconFile != nil {
iconUrl, err = s.saveIconOfTrashDetail(iconFile)
if err != nil {
return nil, fmt.Errorf("failed to save icon: %w", err)
}
}
detail := &model.TrashDetail{
IconTrashDetail: iconUrl,
Description: req.Description,
StepOrder: req.StepOrder,
}
if err := s.trashRepo.AddTrashDetailToCategory(ctx, categoryID, detail); err != nil {
if iconUrl != "" {
s.deleteIconTrashDetailFile(iconUrl)
}
return nil, fmt.Errorf("failed to add trash detail to category: %w", err)
}
response := s.convertTrashDetailToResponseDTO(detail)
return response, nil
}
func (s *TrashService) UpdateTrashDetailWithIcon(ctx context.Context, id string, req RequestTrashDetailDTO, iconFile *multipart.FileHeader) (*ResponseTrashDetailDTO, error) {
if errors, valid := req.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
existingDetail, err := s.trashRepo.GetTrashDetailByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get existing detail: %w", err)
}
var iconUrl string = existingDetail.IconTrashDetail
if iconFile != nil {
newIconUrl, err := s.saveIconOfTrashDetail(iconFile)
if err != nil {
return nil, fmt.Errorf("failed to save new icon: %w", err)
}
iconUrl = newIconUrl
}
updates := map[string]interface{}{
"icon_trash_detail": iconUrl,
"description": req.Description,
"step_order": req.StepOrder,
}
if err := s.trashRepo.UpdateTrashDetail(ctx, id, updates); err != nil {
if iconFile != nil && iconUrl != existingDetail.IconTrashDetail {
s.deleteIconTrashDetailFile(iconUrl)
}
return nil, fmt.Errorf("failed to update trash detail: %w", err)
}
if iconFile != nil && existingDetail.IconTrashDetail != "" && iconUrl != existingDetail.IconTrashDetail {
if err := s.deleteIconTrashDetailFile(existingDetail.IconTrashDetail); err != nil {
log.Printf("Warning: failed to delete old icon: %v", err)
}
}
updatedDetail, err := s.trashRepo.GetTrashDetailByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to retrieve updated detail: %w", err)
}
response := s.convertTrashDetailToResponseDTO(updatedDetail)
return response, nil
}
func (s *TrashService) CreateTrashCategory(ctx context.Context, req RequestTrashCategoryDTO) (*ResponseTrashCategoryDTO, error) {
if errors, valid := req.ValidateRequestTrashCategoryDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
category := &model.TrashCategory{
Name: req.Name,
IconTrash: req.IconTrash,
EstimatedPrice: req.EstimatedPrice,
Variety: req.Variety,
}
if err := s.trashRepo.CreateTrashCategory(ctx, category); err != nil {
return nil, fmt.Errorf("failed to create trash category: %w", err)
}
response := s.convertTrashCategoryToResponseDTO(category)
return response, nil
}
func (s *TrashService) CreateTrashCategoryWithDetails(ctx context.Context, categoryReq RequestTrashCategoryDTO, detailsReq []RequestTrashDetailDTO) (*ResponseTrashCategoryDTO, error) {
if errors, valid := categoryReq.ValidateRequestTrashCategoryDTO(); !valid {
return nil, fmt.Errorf("category validation failed: %v", errors)
}
for i, detailReq := range detailsReq {
if errors, valid := detailReq.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("detail %d validation failed: %v", i+1, errors)
}
}
category := &model.TrashCategory{
Name: categoryReq.Name,
IconTrash: categoryReq.IconTrash,
EstimatedPrice: categoryReq.EstimatedPrice,
Variety: categoryReq.Variety,
}
details := make([]model.TrashDetail, len(detailsReq))
for i, detailReq := range detailsReq {
details[i] = model.TrashDetail{
IconTrashDetail: detailReq.IconTrashDetail,
Description: detailReq.Description,
StepOrder: detailReq.StepOrder,
}
}
if err := s.trashRepo.CreateTrashCategoryWithDetails(ctx, category, details); err != nil {
return nil, fmt.Errorf("failed to create trash category with details: %w", err)
}
createdCategory, err := s.trashRepo.GetTrashCategoryByIDWithDetails(ctx, category.ID)
if err != nil {
return nil, fmt.Errorf("failed to retrieve created category: %w", err)
}
response := s.convertTrashCategoryToResponseDTOWithDetails(createdCategory)
return response, nil
}
func (s *TrashService) UpdateTrashCategory(ctx context.Context, id string, req RequestTrashCategoryDTO) (*ResponseTrashCategoryDTO, error) {
if errors, valid := req.ValidateRequestTrashCategoryDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
exists, err := s.trashRepo.CheckTrashCategoryExists(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to check category existence: %w", err)
}
if !exists {
return nil, errors.New("trash category not found")
}
updates := map[string]interface{}{
"name": req.Name,
"icon_trash": req.IconTrash,
"estimated_price": req.EstimatedPrice,
"variety": req.Variety,
}
if err := s.trashRepo.UpdateTrashCategory(ctx, id, updates); err != nil {
return nil, fmt.Errorf("failed to update trash category: %w", err)
}
updatedCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to retrieve updated category: %w", err)
}
response := s.convertTrashCategoryToResponseDTO(updatedCategory)
return response, nil
}
func (s *TrashService) GetAllTrashCategories(ctx context.Context) ([]ResponseTrashCategoryDTO, error) {
categories, err := s.trashRepo.GetAllTrashCategories(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get trash categories: %w", err)
}
responses := make([]ResponseTrashCategoryDTO, len(categories))
for i, category := range categories {
responses[i] = *s.convertTrashCategoryToResponseDTO(&category)
}
return responses, nil
}
func (s *TrashService) GetAllTrashCategoriesWithDetails(ctx context.Context) ([]ResponseTrashCategoryDTO, error) {
categories, err := s.trashRepo.GetAllTrashCategoriesWithDetails(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get trash categories with details: %w", err)
}
responses := make([]ResponseTrashCategoryDTO, len(categories))
for i, category := range categories {
responses[i] = *s.convertTrashCategoryToResponseDTOWithDetails(&category)
}
return responses, nil
}
func (s *TrashService) GetTrashCategoryByID(ctx context.Context, id string) (*ResponseTrashCategoryDTO, error) {
category, err := s.trashRepo.GetTrashCategoryByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get trash category: %w", err)
}
response := s.convertTrashCategoryToResponseDTO(category)
return response, nil
}
func (s *TrashService) GetTrashCategoryByIDWithDetails(ctx context.Context, id string) (*ResponseTrashCategoryDTO, error) {
category, err := s.trashRepo.GetTrashCategoryByIDWithDetails(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get trash category with details: %w", err)
}
response := s.convertTrashCategoryToResponseDTOWithDetails(category)
return response, nil
}
func (s *TrashService) DeleteTrashCategory(ctx context.Context, id string) error {
category, err := s.trashRepo.GetTrashCategoryByID(ctx, id)
if err != nil {
return fmt.Errorf("failed to get category: %w", err)
}
if err := s.trashRepo.DeleteTrashCategory(ctx, id); err != nil {
return fmt.Errorf("failed to delete trash category: %w", err)
}
if category.IconTrash != "" {
if err := s.deleteIconTrashFile(category.IconTrash); err != nil {
log.Printf("Warning: failed to delete category icon: %v", err)
}
}
return nil
}
func (s *TrashService) CreateTrashDetail(ctx context.Context, req RequestTrashDetailDTO) (*ResponseTrashDetailDTO, error) {
if errors, valid := req.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
detail := &model.TrashDetail{
TrashCategoryID: req.CategoryID,
IconTrashDetail: req.IconTrashDetail,
Description: req.Description,
StepOrder: req.StepOrder,
}
if err := s.trashRepo.CreateTrashDetail(ctx, detail); err != nil {
return nil, fmt.Errorf("failed to create trash detail: %w", err)
}
response := s.convertTrashDetailToResponseDTO(detail)
return response, nil
}
func (s *TrashService) AddTrashDetailToCategory(ctx context.Context, categoryID string, req RequestTrashDetailDTO) (*ResponseTrashDetailDTO, error) {
if errors, valid := req.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
detail := &model.TrashDetail{
IconTrashDetail: req.IconTrashDetail,
Description: req.Description,
StepOrder: req.StepOrder,
}
if err := s.trashRepo.AddTrashDetailToCategory(ctx, categoryID, detail); err != nil {
return nil, fmt.Errorf("failed to add trash detail to category: %w", err)
}
response := s.convertTrashDetailToResponseDTO(detail)
return response, nil
}
func (s *TrashService) UpdateTrashDetail(ctx context.Context, id string, req RequestTrashDetailDTO) (*ResponseTrashDetailDTO, error) {
if errors, valid := req.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
exists, err := s.trashRepo.CheckTrashDetailExists(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to check detail existence: %w", err)
}
if !exists {
return nil, errors.New("trash detail not found")
}
updates := map[string]interface{}{
"icon_trash_detail": req.IconTrashDetail,
"description": req.Description,
"step_order": req.StepOrder,
}
if err := s.trashRepo.UpdateTrashDetail(ctx, id, updates); err != nil {
return nil, fmt.Errorf("failed to update trash detail: %w", err)
}
updatedDetail, err := s.trashRepo.GetTrashDetailByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to retrieve updated detail: %w", err)
}
response := s.convertTrashDetailToResponseDTO(updatedDetail)
return response, nil
}
func (s *TrashService) GetTrashDetailsByCategory(ctx context.Context, categoryID string) ([]ResponseTrashDetailDTO, error) {
exists, err := s.trashRepo.CheckTrashCategoryExists(ctx, categoryID)
if err != nil {
return nil, fmt.Errorf("failed to check category existence: %w", err)
}
if !exists {
return nil, errors.New("trash category not found")
}
details, err := s.trashRepo.GetTrashDetailsByCategory(ctx, categoryID)
if err != nil {
return nil, fmt.Errorf("failed to get trash details: %w", err)
}
responses := make([]ResponseTrashDetailDTO, len(details))
for i, detail := range details {
responses[i] = *s.convertTrashDetailToResponseDTO(&detail)
}
return responses, nil
}
func (s *TrashService) GetTrashDetailByID(ctx context.Context, id string) (*ResponseTrashDetailDTO, error) {
detail, err := s.trashRepo.GetTrashDetailByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get trash detail: %w", err)
}
response := s.convertTrashDetailToResponseDTO(detail)
return response, nil
}
func (s *TrashService) DeleteTrashDetail(ctx context.Context, id string) error {
detail, err := s.trashRepo.GetTrashDetailByID(ctx, id)
if err != nil {
return fmt.Errorf("failed to get detail: %w", err)
}
if err := s.trashRepo.DeleteTrashDetail(ctx, id); err != nil {
return fmt.Errorf("failed to delete trash detail: %w", err)
}
if detail.IconTrashDetail != "" {
if err := s.deleteIconTrashDetailFile(detail.IconTrashDetail); err != nil {
log.Printf("Warning: failed to delete detail icon: %v", err)
}
}
return nil
}
func (s *TrashService) BulkCreateTrashDetails(ctx context.Context, categoryID string, detailsReq []RequestTrashDetailDTO) ([]ResponseTrashDetailDTO, error) {
exists, err := s.trashRepo.CheckTrashCategoryExists(ctx, categoryID)
if err != nil {
return nil, fmt.Errorf("failed to check category existence: %w", err)
}
if !exists {
return nil, errors.New("trash category not found")
}
for i, detailReq := range detailsReq {
if errors, valid := detailReq.ValidateRequestTrashDetailDTO(); !valid {
return nil, fmt.Errorf("detail %d validation failed: %v", i+1, errors)
}
}
responses := make([]ResponseTrashDetailDTO, len(detailsReq))
for i, detailReq := range detailsReq {
response, err := s.AddTrashDetailToCategory(ctx, categoryID, detailReq)
if err != nil {
return nil, fmt.Errorf("failed to create detail %d: %w", i+1, err)
}
responses[i] = *response
}
return responses, nil
}
func (s *TrashService) BulkDeleteTrashDetails(ctx context.Context, detailIDs []string) error {
for _, id := range detailIDs {
if err := s.DeleteTrashDetail(ctx, id); err != nil {
return fmt.Errorf("failed to delete detail %s: %w", id, err)
}
}
return nil
}
func (s *TrashService) ReorderTrashDetails(ctx context.Context, categoryID string, orderedDetailIDs []string) error {
exists, err := s.trashRepo.CheckTrashCategoryExists(ctx, categoryID)
if err != nil {
return fmt.Errorf("failed to check category existence: %w", err)
}
if !exists {
return errors.New("trash category not found")
}
for i, detailID := range orderedDetailIDs {
updates := map[string]interface{}{
"step_order": i + 1,
}
if err := s.trashRepo.UpdateTrashDetail(ctx, detailID, updates); err != nil {
return fmt.Errorf("failed to reorder detail %s: %w", detailID, err)
}
}
return nil
}
func (s *TrashService) convertTrashCategoryToResponseDTO(category *model.TrashCategory) *ResponseTrashCategoryDTO {
return &ResponseTrashCategoryDTO{
ID: category.ID,
TrashName: category.Name,
TrashIcon: category.IconTrash,
EstimatedPrice: category.EstimatedPrice,
Variety: category.Variety,
CreatedAt: category.CreatedAt.Format(time.RFC3339),
UpdatedAt: category.UpdatedAt.Format(time.RFC3339),
}
}
func (s *TrashService) convertTrashCategoryToResponseDTOWithDetails(category *model.TrashCategory) *ResponseTrashCategoryDTO {
response := s.convertTrashCategoryToResponseDTO(category)
details := make([]ResponseTrashDetailDTO, len(category.Details))
for i, detail := range category.Details {
details[i] = *s.convertTrashDetailToResponseDTO(&detail)
}
response.TrashDetail = details
return response
}
func (s *TrashService) convertTrashDetailToResponseDTO(detail *model.TrashDetail) *ResponseTrashDetailDTO {
return &ResponseTrashDetailDTO{
ID: detail.ID,
CategoryID: detail.TrashCategoryID,
IconTrashDetail: detail.IconTrashDetail,
Description: detail.Description,
StepOrder: detail.StepOrder,
CreatedAt: detail.CreatedAt.Format(time.RFC3339),
UpdatedAt: detail.UpdatedAt.Format(time.RFC3339),
}
}