Files
backend/internal/service/trade_service.go

222 lines
6.7 KiB
Go
Raw Normal View History

package service
import (
"context"
"errors"
"fmt"
"time"
"with_you/internal/dto"
"with_you/internal/model"
"with_you/internal/pkg/cursor"
"with_you/internal/repository"
"gorm.io/gorm"
)
type TradeService interface {
Create(ctx context.Context, userID string, req dto.CreateTradeItemRequest) (*model.TradeItem, error)
GetByID(ctx context.Context, id string, currentUserID *string) (*dto.TradeItemDetailResponse, error)
Update(ctx context.Context, userID string, id string, req dto.UpdateTradeItemRequest) error
Delete(ctx context.Context, userID string, id string) error
UpdateStatus(ctx context.Context, userID string, id string, status model.TradeItemStatus) error
List(ctx context.Context, tradeType *model.TradeType, category *model.TradeCategory, cursorStr string, pageSize int, currentUserID *string) (*dto.TradeCursorPageResponse, error)
Search(ctx context.Context, keyword string, tradeType *model.TradeType, category *model.TradeCategory, cursorStr string, pageSize int, currentUserID *string) (*dto.TradeCursorPageResponse, error)
RecordView(ctx context.Context, id string) error
Favorite(ctx context.Context, userID string, id string) error
Unfavorite(ctx context.Context, userID string, id string) error
}
type tradeService struct {
tradeRepo repository.TradeRepository
}
func NewTradeService(tradeRepo repository.TradeRepository) TradeService {
return &tradeService{tradeRepo: tradeRepo}
}
func (s *tradeService) Create(ctx context.Context, userID string, req dto.CreateTradeItemRequest) (*model.TradeItem, error) {
item := &model.TradeItem{
UserID: userID,
TradeType: model.TradeType(req.TradeType),
Category: model.TradeCategory(req.Category),
Title: req.Title,
Content: req.Content,
Segments: req.Segments,
Price: req.Price,
Condition: req.Condition,
Status: model.TradeItemActive,
}
if item.Content == "" {
item.Content = item.Title
}
images := req.Images
if images == nil {
images = []string{}
}
if err := s.tradeRepo.Create(ctx, item, images); err != nil {
return nil, fmt.Errorf("create trade item: %w", err)
}
return item, nil
}
func (s *tradeService) GetByID(ctx context.Context, id string, currentUserID *string) (*dto.TradeItemDetailResponse, error) {
item, err := s.tradeRepo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("trade item not found")
}
return nil, err
}
isFavorited := false
if currentUserID != nil {
fav, err := s.tradeRepo.IsFavorited(ctx, id, *currentUserID)
if err == nil {
isFavorited = fav
}
}
_ = s.tradeRepo.IncrementViews(ctx, id)
return dto.ConvertTradeItemToDetailResponse(item, isFavorited), nil
}
func (s *tradeService) Update(ctx context.Context, userID string, id string, req dto.UpdateTradeItemRequest) error {
item, err := s.tradeRepo.GetByID(ctx, id)
if err != nil {
return fmt.Errorf("trade item not found")
}
if item.UserID != userID {
return fmt.Errorf("not authorized")
}
if req.TradeType != nil {
item.TradeType = model.TradeType(*req.TradeType)
}
if req.Category != nil {
item.Category = model.TradeCategory(*req.Category)
}
if req.Title != nil {
item.Title = *req.Title
}
if req.Content != nil {
item.Content = *req.Content
}
if req.Segments != nil {
item.Segments = req.Segments
}
if req.Price != nil {
item.Price = req.Price
}
if req.Condition != nil {
item.Condition = *req.Condition
}
item.UpdatedAt = time.Now()
if req.Images != nil {
return s.tradeRepo.UpdateWithImages(ctx, item, &req.Images)
}
return s.tradeRepo.Update(ctx, item)
}
func (s *tradeService) Delete(ctx context.Context, userID string, id string) error {
item, err := s.tradeRepo.GetByID(ctx, id)
if err != nil {
return fmt.Errorf("trade item not found")
}
if item.UserID != userID {
return fmt.Errorf("not authorized")
}
return s.tradeRepo.Delete(ctx, id)
}
func (s *tradeService) UpdateStatus(ctx context.Context, userID string, id string, status model.TradeItemStatus) error {
item, err := s.tradeRepo.GetByID(ctx, id)
if err != nil {
return fmt.Errorf("trade item not found")
}
if item.UserID != userID {
return fmt.Errorf("not authorized")
}
return s.tradeRepo.UpdateStatus(ctx, id, status)
}
func (s *tradeService) List(ctx context.Context, tradeType *model.TradeType, category *model.TradeCategory, cursorStr string, pageSize int, currentUserID *string) (*dto.TradeCursorPageResponse, error) {
req := cursor.NewPageRequest(cursorStr, cursor.Forward, pageSize)
result, err := s.tradeRepo.ListByCursor(ctx, tradeType, category, nil, req)
if err != nil {
return nil, err
}
return s.buildCursorResponse(ctx, result, currentUserID)
}
func (s *tradeService) Search(ctx context.Context, keyword string, tradeType *model.TradeType, category *model.TradeCategory, cursorStr string, pageSize int, currentUserID *string) (*dto.TradeCursorPageResponse, error) {
req := cursor.NewPageRequest(cursorStr, cursor.Forward, pageSize)
result, err := s.tradeRepo.SearchByCursor(ctx, keyword, tradeType, category, req)
if err != nil {
return nil, err
}
return s.buildCursorResponse(ctx, result, currentUserID)
}
func (s *tradeService) buildCursorResponse(ctx context.Context, result *cursor.CursorPageResult[*model.TradeItem], currentUserID *string) (*dto.TradeCursorPageResponse, error) {
favoriteMap := make(map[string]bool)
if currentUserID != nil && len(result.Items) > 0 {
ids := make([]string, 0, len(result.Items))
for _, item := range result.Items {
ids = append(ids, item.ID)
}
fm, err := s.tradeRepo.IsFavoritedBatch(ctx, ids, *currentUserID)
if err == nil {
favoriteMap = fm
}
}
resp := &dto.TradeCursorPageResponse{
Items: dto.ConvertTradeItemsToResponse(result.Items, favoriteMap),
NextCursor: result.NextCursor,
PrevCursor: result.PrevCursor,
HasMore: result.HasMore,
}
return resp, nil
}
func (s *tradeService) RecordView(ctx context.Context, id string) error {
return s.tradeRepo.IncrementViews(ctx, id)
}
func (s *tradeService) Favorite(ctx context.Context, userID string, id string) error {
_, err := s.tradeRepo.GetByID(ctx, id)
if err != nil {
return fmt.Errorf("trade item not found")
}
exists, err := s.tradeRepo.IsFavorited(ctx, id, userID)
if err != nil {
return err
}
if exists {
return nil
}
if err := s.tradeRepo.CreateFavorite(ctx, id, userID); err != nil {
return err
}
_ = s.tradeRepo.IncrementFavorites(ctx, id)
return nil
}
func (s *tradeService) Unfavorite(ctx context.Context, userID string, id string) error {
exists, err := s.tradeRepo.IsFavorited(ctx, id, userID)
if err != nil {
return err
}
if !exists {
return nil
}
if err := s.tradeRepo.DeleteFavorite(ctx, id, userID); err != nil {
return err
}
_ = s.tradeRepo.DecrementFavorites(ctx, id)
return nil
}