241 lines
6.3 KiB
Go
241 lines
6.3 KiB
Go
package services
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"rijig/dto"
|
|
"rijig/internal/repositories"
|
|
"rijig/model"
|
|
"rijig/utils"
|
|
)
|
|
|
|
type CartService interface {
|
|
CreateCartFromDTO(userID string, items []dto.RequestCartItems) error
|
|
GetCartByUserID(userID string) (*dto.CartResponse, error)
|
|
CommitCartFromRedis(userID string) error
|
|
DeleteCart(cartID string) error
|
|
}
|
|
|
|
type cartService struct {
|
|
repo repositories.CartRepository
|
|
repoTrash repositories.TrashRepository
|
|
}
|
|
|
|
func NewCartService(repo repositories.CartRepository, repoTrash repositories.TrashRepository) CartService {
|
|
return &cartService{repo: repo, repoTrash: repoTrash}
|
|
}
|
|
|
|
func redisCartKey(userID string) string {
|
|
return fmt.Sprintf("cart:user:%s", userID)
|
|
}
|
|
|
|
func (s *cartService) CreateCartFromDTO(userID string, items []dto.RequestCartItems) error {
|
|
// Validasi semua item
|
|
for _, item := range items {
|
|
if errMap, valid := item.ValidateRequestCartItem(); !valid {
|
|
return dto.ValidationErrors{Errors: errMap}
|
|
}
|
|
}
|
|
|
|
// Ambil cart yang sudah ada dari Redis (jika ada)
|
|
var existingCart dto.CartResponse
|
|
val, err := utils.GetData(redisCartKey(userID))
|
|
if err == nil && val != "" {
|
|
if err := json.Unmarshal([]byte(val), &existingCart); err != nil {
|
|
log.Printf("Failed to unmarshal existing cart: %v", err)
|
|
}
|
|
}
|
|
|
|
// Buat map dari existing items untuk mempermudah update
|
|
itemMap := make(map[string]dto.CartItemResponse)
|
|
for _, item := range existingCart.CartItems {
|
|
itemMap[item.TrashName] = item
|
|
}
|
|
|
|
// Proses input baru
|
|
for _, input := range items {
|
|
trash, err := s.repoTrash.GetCategoryByID(input.TrashID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to retrieve trash category for id %s: %v", input.TrashID, err)
|
|
}
|
|
|
|
if input.Amount == 0 {
|
|
delete(itemMap, trash.Name) // hapus item
|
|
continue
|
|
}
|
|
|
|
subtotal := float32(trash.EstimatedPrice) * input.Amount
|
|
|
|
itemMap[trash.Name] = dto.CartItemResponse{
|
|
TrashIcon: trash.Icon,
|
|
TrashName: trash.Name,
|
|
Amount: input.Amount,
|
|
EstimatedSubTotalPrice: subtotal,
|
|
}
|
|
}
|
|
|
|
// Rekonstruksi cart
|
|
var finalItems []dto.CartItemResponse
|
|
var totalAmount float32
|
|
var totalPrice float32
|
|
for _, item := range itemMap {
|
|
finalItems = append(finalItems, item)
|
|
totalAmount += item.Amount
|
|
totalPrice += item.EstimatedSubTotalPrice
|
|
}
|
|
|
|
cart := dto.CartResponse{
|
|
ID: existingCart.ID,
|
|
UserID: userID,
|
|
TotalAmount: totalAmount,
|
|
EstimatedTotalPrice: totalPrice,
|
|
CartItems: finalItems,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
// Simpan ulang ke Redis dengan TTL 10 menit
|
|
return utils.SetData(redisCartKey(userID), cart, 1*time.Minute)
|
|
}
|
|
|
|
|
|
func (s *cartService) GetCartByUserID(userID string) (*dto.CartResponse, error) {
|
|
val, err := utils.GetData(redisCartKey(userID))
|
|
if err != nil {
|
|
log.Printf("Redis get error: %v", err)
|
|
}
|
|
if val != "" {
|
|
var cached dto.CartResponse
|
|
if err := json.Unmarshal([]byte(val), &cached); err == nil {
|
|
return &cached, nil
|
|
}
|
|
}
|
|
|
|
cart, err := s.repo.GetByUserID(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cart == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var items []dto.CartItemResponse
|
|
for _, item := range cart.CartItems {
|
|
items = append(items, dto.CartItemResponse{
|
|
TrashIcon: item.TrashCategory.Icon,
|
|
TrashName: item.TrashCategory.Name,
|
|
Amount: item.Amount,
|
|
EstimatedSubTotalPrice: item.SubTotalEstimatedPrice,
|
|
})
|
|
}
|
|
|
|
response := &dto.CartResponse{
|
|
ID: cart.ID,
|
|
UserID: cart.UserID,
|
|
TotalAmount: cart.TotalAmount,
|
|
EstimatedTotalPrice: cart.EstimatedTotalPrice,
|
|
CartItems: items,
|
|
CreatedAt: cart.CreatedAt,
|
|
UpdatedAt: cart.UpdatedAt,
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (s *cartService) CommitCartFromRedis(userID string) error {
|
|
val, err := utils.GetData(redisCartKey(userID))
|
|
if err != nil || val == "" {
|
|
return errors.New("no cart found in redis")
|
|
}
|
|
|
|
var cartDTO dto.CartResponse
|
|
if err := json.Unmarshal([]byte(val), &cartDTO); err != nil {
|
|
return errors.New("invalid cart data in Redis")
|
|
}
|
|
|
|
existingCart, err := s.repo.GetByUserID(userID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get cart from db: %v", err)
|
|
}
|
|
|
|
if existingCart == nil {
|
|
// buat cart baru jika belum ada
|
|
var items []model.CartItem
|
|
for _, item := range cartDTO.CartItems {
|
|
trash, err := s.repoTrash.GetTrashCategoryByName(item.TrashName)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
items = append(items, model.CartItem{
|
|
TrashID: trash.ID,
|
|
Amount: item.Amount,
|
|
SubTotalEstimatedPrice: item.EstimatedSubTotalPrice,
|
|
})
|
|
}
|
|
|
|
newCart := model.Cart{
|
|
UserID: userID,
|
|
TotalAmount: cartDTO.TotalAmount,
|
|
EstimatedTotalPrice: cartDTO.EstimatedTotalPrice,
|
|
CartItems: items,
|
|
}
|
|
|
|
return s.repo.Create(&newCart)
|
|
}
|
|
|
|
// buat map item lama (by trash_name)
|
|
existingItemMap := make(map[string]*model.CartItem)
|
|
for i := range existingCart.CartItems {
|
|
trashName := existingCart.CartItems[i].TrashCategory.Name
|
|
existingItemMap[trashName] = &existingCart.CartItems[i]
|
|
}
|
|
|
|
// proses update/hapus/tambah
|
|
for _, newItem := range cartDTO.CartItems {
|
|
if newItem.Amount == 0 {
|
|
if existing, ok := existingItemMap[newItem.TrashName]; ok {
|
|
_ = s.repo.DeleteCartItemByID(existing.ID)
|
|
}
|
|
continue
|
|
}
|
|
|
|
trash, err := s.repoTrash.GetTrashCategoryByName(newItem.TrashName)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if existing, ok := existingItemMap[newItem.TrashName]; ok {
|
|
existing.Amount = newItem.Amount
|
|
existing.SubTotalEstimatedPrice = newItem.EstimatedSubTotalPrice
|
|
_ = s.repo.UpdateCartItem(existing)
|
|
} else {
|
|
newModelItem := model.CartItem{
|
|
CartID: existingCart.ID,
|
|
TrashID: trash.ID,
|
|
Amount: newItem.Amount,
|
|
SubTotalEstimatedPrice: newItem.EstimatedSubTotalPrice,
|
|
}
|
|
_ = s.repo.InsertCartItem(&newModelItem)
|
|
}
|
|
}
|
|
|
|
// update cart total amount & price
|
|
existingCart.TotalAmount = cartDTO.TotalAmount
|
|
existingCart.EstimatedTotalPrice = cartDTO.EstimatedTotalPrice
|
|
if err := s.repo.Update(existingCart); err != nil {
|
|
return err
|
|
}
|
|
|
|
return utils.DeleteData(redisCartKey(userID))
|
|
}
|
|
|
|
|
|
func (s *cartService) DeleteCart(cartID string) error {
|
|
return s.repo.Delete(cartID)
|
|
}
|