fix: auto commit to DB
This commit is contained in:
parent
29b04db0cb
commit
58f843cac2
63
cmd/main.go
63
cmd/main.go
|
@ -2,58 +2,49 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"rijig/config"
|
||||
"rijig/internal/repositories"
|
||||
"rijig/internal/services"
|
||||
"rijig/internal/worker"
|
||||
"rijig/router"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/robfig/cron"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config.SetupConfig()
|
||||
logFile, _ := os.OpenFile("logs/cart_commit.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
log.SetOutput(logFile)
|
||||
cartRepo := repositories.NewCartRepository()
|
||||
trashRepo := repositories.NewTrashRepository(config.DB)
|
||||
cartService := services.NewCartService(cartRepo, trashRepo)
|
||||
worker := worker.NewCartWorker(cartService, cartRepo, trashRepo)
|
||||
|
||||
go func() {
|
||||
c := cron.New()
|
||||
c.AddFunc("@every 1m", func() {
|
||||
_ = worker.CommitExpiredCartsToDB()
|
||||
})
|
||||
c.Start()
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if err := worker.AutoCommitExpiringCarts(); err != nil {
|
||||
log.Printf("Auto-commit error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
app := fiber.New()
|
||||
|
||||
app.Use(cors.New(cors.Config{
|
||||
AllowOrigins: "*",
|
||||
AllowMethods: "GET,POST,PUT,PATCH,DELETE",
|
||||
AllowHeaders: "Content-Type,x-api-key",
|
||||
}))
|
||||
app := fiber.New(fiber.Config{
|
||||
ErrorHandler: func(c *fiber.Ctx, err error) error {
|
||||
code := fiber.StatusInternalServerError
|
||||
if e, ok := err.(*fiber.Error); ok {
|
||||
code = e.Code
|
||||
}
|
||||
return c.Status(code).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// app.Use(cors.New(cors.Config{
|
||||
// AllowOrigins: "http://localhost:3000",
|
||||
// AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
|
||||
// AllowHeaders: "Origin, Content-Type, Accept, Authorization, x-api-key",
|
||||
// AllowCredentials: true,
|
||||
// }))
|
||||
app.Use(cors.New())
|
||||
|
||||
// app.Use(func(c *fiber.Ctx) error {
|
||||
// c.Set("Access-Control-Allow-Origin", "http://localhost:3000")
|
||||
// c.Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
|
||||
// c.Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, x-api-key")
|
||||
// c.Set("Access-Control-Allow-Credentials", "true")
|
||||
// return c.Next()
|
||||
// })
|
||||
|
||||
// app.Options("*", func(c *fiber.Ctx) error {
|
||||
// c.Set("Access-Control-Allow-Origin", "http://localhost:3000")
|
||||
// c.Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
|
||||
// c.Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, x-api-key")
|
||||
// c.Set("Access-Control-Allow-Credentials", "true")
|
||||
// return c.SendStatus(fiber.StatusNoContent)
|
||||
// })
|
||||
|
||||
router.SetupRoutes(app)
|
||||
config.StartServer(app)
|
||||
|
|
|
@ -7,44 +7,38 @@ import (
|
|||
|
||||
type RequestCartItemDTO struct {
|
||||
TrashID string `json:"trash_id"`
|
||||
Amount float32 `json:"amount"`
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
type RequestCartDTO struct {
|
||||
CartItems []RequestCartItemDTO `json:"cart_items"`
|
||||
}
|
||||
|
||||
type ResponseCartDTO struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
EstimatedTotalPrice float64 `json:"estimated_total_price"`
|
||||
CartItems []ResponseCartItemDTO `json:"cart_items"`
|
||||
}
|
||||
|
||||
type ResponseCartItemDTO struct {
|
||||
ID string `json:"id"`
|
||||
TrashID string `json:"trash_id"`
|
||||
TrashName string `json:"trash_name"`
|
||||
TrashIcon string `json:"trash_icon"`
|
||||
Amount float32 `json:"amount"`
|
||||
SubTotalEstimatedPrice float32 `json:"subtotal_estimated_price"`
|
||||
}
|
||||
|
||||
type ResponseCartDTO struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
TotalAmount float32 `json:"total_amount"`
|
||||
EstimatedTotalPrice float32 `json:"estimated_total_price"`
|
||||
CartItems []ResponseCartItemDTO `json:"cart_items"`
|
||||
TrashPrice float64 `json:"trash_price"`
|
||||
Amount float64 `json:"amount"`
|
||||
SubTotalEstimatedPrice float64 `json:"subtotal_estimated_price"`
|
||||
}
|
||||
|
||||
func (r *RequestCartDTO) ValidateRequestCartDTO() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if len(r.CartItems) == 0 {
|
||||
errors["cart_items"] = append(errors["cart_items"], "minimal satu item harus dimasukkan")
|
||||
}
|
||||
|
||||
for i, item := range r.CartItems {
|
||||
if strings.TrimSpace(item.TrashID) == "" {
|
||||
errors[fmt.Sprintf("cart_items[%d].trash_id", i)] = append(errors[fmt.Sprintf("cart_items[%d].trash_id", i)], "trash_id tidak boleh kosong")
|
||||
}
|
||||
if item.Amount <= 0 {
|
||||
errors[fmt.Sprintf("cart_items[%d].amount", i)] = append(errors[fmt.Sprintf("cart_items[%d].amount", i)], "amount harus lebih dari 0")
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
|
|
1
go.mod
1
go.mod
|
@ -37,7 +37,6 @@ require (
|
|||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mdp/qrterminal/v3 v3.2.0
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"rijig/dto"
|
||||
"rijig/internal/services"
|
||||
"rijig/utils"
|
||||
|
@ -9,100 +8,86 @@ import (
|
|||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type CartHandler interface {
|
||||
GetCart(c *fiber.Ctx) error
|
||||
AddOrUpdateCartItem(c *fiber.Ctx) error
|
||||
AddMultipleCartItems(c *fiber.Ctx) error
|
||||
DeleteCartItem(c *fiber.Ctx) error
|
||||
ClearCart(c *fiber.Ctx) error
|
||||
type CartHandler struct {
|
||||
cartService services.CartService
|
||||
}
|
||||
|
||||
type cartHandler struct {
|
||||
service services.CartService
|
||||
func NewCartHandler(cartService services.CartService) *CartHandler {
|
||||
return &CartHandler{cartService: cartService}
|
||||
}
|
||||
|
||||
func NewCartHandler(service services.CartService) CartHandler {
|
||||
return &cartHandler{service: service}
|
||||
}
|
||||
|
||||
func (h *cartHandler) GetCart(c *fiber.Ctx) error {
|
||||
func (h *CartHandler) AddOrUpdateItem(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
var req dto.RequestCartItemDTO
|
||||
|
||||
cart, err := h.service.GetCart(context.Background(), userID)
|
||||
if err != nil {
|
||||
return utils.ErrorResponse(c, "Cart belum dibuat atau sudah kadaluarsa")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, cart, "Data cart berhasil diambil")
|
||||
}
|
||||
|
||||
func (h *cartHandler) AddOrUpdateCartItem(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
var item dto.RequestCartItemDTO
|
||||
if err := c.BodyParser(&item); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"format tidak valid"}})
|
||||
}
|
||||
|
||||
if item.TrashID == "" || item.Amount <= 0 {
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{
|
||||
"trash_id": {"harus diisi"},
|
||||
"amount": {"harus lebih dari 0"},
|
||||
"request": {"Payload tidak valid"},
|
||||
})
|
||||
}
|
||||
|
||||
if err := h.service.AddOrUpdateItem(context.Background(), userID, item); err != nil {
|
||||
return utils.InternalServerErrorResponse(c, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Item berhasil ditambahkan/diupdate di cart")
|
||||
}
|
||||
|
||||
func (h *cartHandler) AddMultipleCartItems(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
var payload dto.RequestCartDTO
|
||||
if err := c.BodyParser(&payload); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{
|
||||
"body": {"format tidak valid"},
|
||||
})
|
||||
}
|
||||
|
||||
if errs, ok := payload.ValidateRequestCartDTO(); !ok {
|
||||
hasErrors, _ := req.Amount > 0 && req.TrashID != "", true
|
||||
if !hasErrors {
|
||||
errs := make(map[string][]string)
|
||||
if req.Amount <= 0 {
|
||||
errs["amount"] = append(errs["amount"], "Amount harus lebih dari 0")
|
||||
}
|
||||
if req.TrashID == "" {
|
||||
errs["trash_id"] = append(errs["trash_id"], "Trash ID tidak boleh kosong")
|
||||
}
|
||||
return utils.ValidationErrorResponse(c, errs)
|
||||
}
|
||||
|
||||
for _, item := range payload.CartItems {
|
||||
if err := h.service.AddOrUpdateItem(context.Background(), userID, item); err != nil {
|
||||
return utils.InternalServerErrorResponse(c, err.Error())
|
||||
}
|
||||
if err := h.cartService.AddOrUpdateItem(c.Context(), userID, req); err != nil {
|
||||
return utils.InternalServerErrorResponse(c, "Gagal menambahkan item ke keranjang")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Semua item berhasil ditambahkan/diupdate ke cart")
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Item berhasil ditambahkan ke keranjang")
|
||||
}
|
||||
|
||||
func (h *cartHandler) DeleteCartItem(c *fiber.Ctx) error {
|
||||
func (h *CartHandler) GetCart(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
trashID := c.Params("trashID")
|
||||
|
||||
cart, err := h.cartService.GetCart(c.Context(), userID)
|
||||
if err != nil {
|
||||
return utils.ErrorResponse(c, "Gagal mengambil data keranjang")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, cart, "Berhasil mengambil data keranjang")
|
||||
}
|
||||
|
||||
func (h *CartHandler) DeleteItem(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
trashID := c.Params("trash_id")
|
||||
|
||||
if trashID == "" {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"trash_id": {"tidak boleh kosong"}})
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Trash ID tidak boleh kosong")
|
||||
}
|
||||
|
||||
err := h.service.DeleteItem(context.Background(), userID, trashID)
|
||||
if err != nil {
|
||||
return utils.InternalServerErrorResponse(c, err.Error())
|
||||
if err := h.cartService.DeleteItem(c.Context(), userID, trashID); err != nil {
|
||||
return utils.InternalServerErrorResponse(c, "Gagal menghapus item dari keranjang")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Item berhasil dihapus dari cart")
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Item berhasil dihapus dari keranjang")
|
||||
}
|
||||
|
||||
func (h *cartHandler) ClearCart(c *fiber.Ctx) error {
|
||||
func (h *CartHandler) Checkout(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
if err := h.service.ClearCart(context.Background(), userID); err != nil {
|
||||
return utils.InternalServerErrorResponse(c, err.Error())
|
||||
if err := h.cartService.Checkout(c.Context(), userID); err != nil {
|
||||
return utils.InternalServerErrorResponse(c, "Gagal melakukan checkout keranjang")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Seluruh cart berhasil dihapus")
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Checkout berhasil. Permintaan pickup telah dibuat.")
|
||||
}
|
||||
|
||||
func (h *CartHandler) ClearCart(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
err := h.cartService.ClearCart(c.Context(), userID)
|
||||
if err != nil {
|
||||
return utils.InternalServerErrorResponse(c, "Gagal menghapus keranjang")
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Keranjang berhasil dikosongkan")
|
||||
}
|
|
@ -1,15 +1,27 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"rijig/config"
|
||||
"rijig/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CartRepository interface {
|
||||
CreateCart(cart *model.Cart) error
|
||||
GetTrashCategoryByID(id string) (*model.TrashCategory, error)
|
||||
GetCartByUserID(userID string) (*model.Cart, error)
|
||||
DeleteCartByUserID(userID string) error
|
||||
FindOrCreateCart(ctx context.Context, userID string) (*model.Cart, error)
|
||||
AddOrUpdateCartItem(ctx context.Context, cartID, trashCategoryID string, amount float64, estimatedPrice float64) error
|
||||
DeleteCartItem(ctx context.Context, cartID, trashCategoryID string) error
|
||||
GetCartByUser(ctx context.Context, userID string) (*model.Cart, error)
|
||||
UpdateCartTotals(ctx context.Context, cartID string) error
|
||||
DeleteCart(ctx context.Context, userID string) error
|
||||
// New method for batch cart creation
|
||||
CreateCartWithItems(ctx context.Context, cart *model.Cart) error
|
||||
// Check if user already has a cart
|
||||
HasExistingCart(ctx context.Context, userID string) (bool, error)
|
||||
}
|
||||
|
||||
type cartRepository struct{}
|
||||
|
@ -18,29 +30,137 @@ func NewCartRepository() CartRepository {
|
|||
return &cartRepository{}
|
||||
}
|
||||
|
||||
func (r *cartRepository) CreateCart(cart *model.Cart) error {
|
||||
return config.DB.Create(cart).Error
|
||||
}
|
||||
|
||||
func (r *cartRepository) DeleteCartByUserID(userID string) error {
|
||||
return config.DB.Where("user_id = ?", userID).Delete(&model.Cart{}).Error
|
||||
}
|
||||
|
||||
func (r *cartRepository) GetTrashCategoryByID(id string) (*model.TrashCategory, error) {
|
||||
var trash model.TrashCategory
|
||||
if err := config.DB.First(&trash, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &trash, nil
|
||||
}
|
||||
|
||||
func (r *cartRepository) GetCartByUserID(userID string) (*model.Cart, error) {
|
||||
func (r *cartRepository) FindOrCreateCart(ctx context.Context, userID string) (*model.Cart, error) {
|
||||
var cart model.Cart
|
||||
err := config.DB.Preload("CartItems.TrashCategory").
|
||||
db := config.DB.WithContext(ctx)
|
||||
|
||||
err := db.
|
||||
Preload("CartItems.TrashCategory").
|
||||
Where("user_id = ?", userID).
|
||||
First(&cart).Error
|
||||
|
||||
if err == nil {
|
||||
return &cart, nil
|
||||
}
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
newCart := model.Cart{
|
||||
UserID: userID,
|
||||
TotalAmount: 0,
|
||||
EstimatedTotalPrice: 0,
|
||||
}
|
||||
if err := db.Create(&newCart).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &newCart, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (r *cartRepository) AddOrUpdateCartItem(ctx context.Context, cartID, trashCategoryID string, amount float64, estimatedPrice float64) error {
|
||||
db := config.DB.WithContext(ctx)
|
||||
|
||||
var item model.CartItem
|
||||
err := db.
|
||||
Where("cart_id = ? AND trash_category_id = ?", cartID, trashCategoryID).
|
||||
First(&item).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
newItem := model.CartItem{
|
||||
CartID: cartID,
|
||||
TrashCategoryID: trashCategoryID,
|
||||
Amount: amount,
|
||||
SubTotalEstimatedPrice: amount * estimatedPrice,
|
||||
}
|
||||
return db.Create(&newItem).Error
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
item.Amount = amount
|
||||
item.SubTotalEstimatedPrice = amount * estimatedPrice
|
||||
return db.Save(&item).Error
|
||||
}
|
||||
|
||||
func (r *cartRepository) DeleteCartItem(ctx context.Context, cartID, trashCategoryID string) error {
|
||||
db := config.DB.WithContext(ctx)
|
||||
return db.Where("cart_id = ? AND trash_category_id = ?", cartID, trashCategoryID).
|
||||
Delete(&model.CartItem{}).Error
|
||||
}
|
||||
|
||||
func (r *cartRepository) GetCartByUser(ctx context.Context, userID string) (*model.Cart, error) {
|
||||
var cart model.Cart
|
||||
db := config.DB.WithContext(ctx)
|
||||
|
||||
err := db.
|
||||
Preload("CartItems.TrashCategory").
|
||||
Where("user_id = ?", userID).
|
||||
First(&cart).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cart, nil
|
||||
}
|
||||
|
||||
func (r *cartRepository) UpdateCartTotals(ctx context.Context, cartID string) error {
|
||||
db := config.DB.WithContext(ctx)
|
||||
|
||||
var items []model.CartItem
|
||||
if err := db.Where("cart_id = ?", cartID).Find(&items).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var totalAmount float64
|
||||
var totalPrice float64
|
||||
|
||||
for _, item := range items {
|
||||
totalAmount += item.Amount
|
||||
totalPrice += item.SubTotalEstimatedPrice
|
||||
}
|
||||
|
||||
return db.Model(&model.Cart{}).
|
||||
Where("id = ?", cartID).
|
||||
Updates(map[string]interface{}{
|
||||
"total_amount": totalAmount,
|
||||
"estimated_total_price": totalPrice,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (r *cartRepository) DeleteCart(ctx context.Context, userID string) error {
|
||||
db := config.DB.WithContext(ctx)
|
||||
var cart model.Cart
|
||||
if err := db.Where("user_id = ?", userID).First(&cart).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Delete(&cart).Error
|
||||
}
|
||||
|
||||
// New method for batch cart creation with transaction
|
||||
func (r *cartRepository) CreateCartWithItems(ctx context.Context, cart *model.Cart) error {
|
||||
db := config.DB.WithContext(ctx)
|
||||
|
||||
// Use transaction to ensure data consistency
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(cart).Error; err != nil {
|
||||
return fmt.Errorf("failed to create cart: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Check if user already has a cart
|
||||
func (r *cartRepository) HasExistingCart(ctx context.Context, userID string) (bool, error) {
|
||||
db := config.DB.WithContext(ctx)
|
||||
|
||||
var count int64
|
||||
err := db.Model(&model.Cart{}).Where("user_id = ?", userID).Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func NewAuthMasyarakatService(userRepo repositories.UserRepository, roleRepo rep
|
|||
|
||||
func (s *authMasyarakatService) generateJWTToken(userID string, deviceID string) (string, error) {
|
||||
|
||||
expirationTime := time.Now().Add(24 * time.Hour)
|
||||
expirationTime := time.Now().Add(672 * time.Hour)
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID,
|
||||
|
|
|
@ -31,7 +31,7 @@ func NewAuthPengelolaService(userRepo repositories.UserRepository, roleRepo repo
|
|||
|
||||
func (s *authPengelolaService) generateJWTToken(userID string, deviceID string) (string, error) {
|
||||
|
||||
expirationTime := time.Now().Add(24 * time.Hour)
|
||||
expirationTime := time.Now().Add(168 * time.Hour)
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID,
|
||||
|
|
|
@ -31,7 +31,7 @@ func NewAuthPengepulService(userRepo repositories.UserRepository, roleRepo repos
|
|||
|
||||
func (s *authPengepulService) generateJWTToken(userID string, deviceID string) (string, error) {
|
||||
|
||||
expirationTime := time.Now().Add(24 * time.Hour)
|
||||
expirationTime := time.Now().Add(480 * time.Hour)
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID,
|
||||
|
|
|
@ -8,97 +8,66 @@ import (
|
|||
|
||||
"rijig/config"
|
||||
"rijig/dto"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var cartTTL = 30 * time.Minute
|
||||
const CartTTL = 30 * time.Minute
|
||||
const CartKeyPrefix = "cart:"
|
||||
|
||||
func getCartKey(userID string) string {
|
||||
return fmt.Sprintf("cart:user:%s", userID)
|
||||
func buildCartKey(userID string) string {
|
||||
return fmt.Sprintf("%s%s", CartKeyPrefix, userID)
|
||||
}
|
||||
|
||||
func SetCartToRedis(ctx context.Context, userID string, cart dto.RequestCartDTO) error {
|
||||
key := getCartKey(userID)
|
||||
|
||||
data, err := json.Marshal(cart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal cart: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = config.RedisClient.Set(ctx, key, data, cartTTL).Err()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save cart to redis: %w", err)
|
||||
}
|
||||
return config.RedisClient.Set(ctx, buildCartKey(userID), data, CartTTL).Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
func RefreshCartTTL(ctx context.Context, userID string) error {
|
||||
return config.RedisClient.Expire(ctx, buildCartKey(userID), CartTTL).Err()
|
||||
}
|
||||
|
||||
func GetCartFromRedis(ctx context.Context, userID string) (*dto.RequestCartDTO, error) {
|
||||
key := getCartKey(userID)
|
||||
val, err := config.RedisClient.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
val, err := config.RedisClient.Get(ctx, buildCartKey(userID)).Result()
|
||||
if err == redis.Nil {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cart dto.RequestCartDTO
|
||||
if err := json.Unmarshal([]byte(val), &cart); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal cart data: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cart, nil
|
||||
}
|
||||
|
||||
func DeleteCartFromRedis(ctx context.Context, userID string) error {
|
||||
key := getCartKey(userID)
|
||||
return config.RedisClient.Del(ctx, key).Err()
|
||||
return config.RedisClient.Del(ctx, buildCartKey(userID)).Err()
|
||||
}
|
||||
|
||||
func GetCartTTL(ctx context.Context, userID string) (time.Duration, error) {
|
||||
key := getCartKey(userID)
|
||||
return config.RedisClient.TTL(ctx, key).Result()
|
||||
}
|
||||
|
||||
func UpdateOrAddCartItemToRedis(ctx context.Context, userID string, item dto.RequestCartItemDTO) error {
|
||||
cart, err := GetCartFromRedis(ctx, userID)
|
||||
func GetExpiringCartKeys(ctx context.Context, threshold time.Duration) ([]string, error) {
|
||||
keys, err := config.RedisClient.Keys(ctx, CartKeyPrefix+"*").Result()
|
||||
if err != nil {
|
||||
|
||||
cart = &dto.RequestCartDTO{
|
||||
CartItems: []dto.RequestCartItemDTO{item},
|
||||
}
|
||||
return SetCartToRedis(ctx, userID, *cart)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updated := false
|
||||
for i, ci := range cart.CartItems {
|
||||
if ci.TrashID == item.TrashID {
|
||||
cart.CartItems[i].Amount = item.Amount
|
||||
updated = true
|
||||
break
|
||||
var expiringKeys []string
|
||||
for _, key := range keys {
|
||||
ttl, err := config.RedisClient.TTL(ctx, key).Result()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if ttl > 0 && ttl <= threshold {
|
||||
expiringKeys = append(expiringKeys, key)
|
||||
}
|
||||
}
|
||||
if !updated {
|
||||
cart.CartItems = append(cart.CartItems, item)
|
||||
}
|
||||
|
||||
return SetCartToRedis(ctx, userID, *cart)
|
||||
}
|
||||
|
||||
func RemoveCartItemFromRedis(ctx context.Context, userID, trashID string) error {
|
||||
cart, err := GetCartFromRedis(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updatedItems := make([]dto.RequestCartItemDTO, 0)
|
||||
for _, ci := range cart.CartItems {
|
||||
if ci.TrashID != trashID {
|
||||
updatedItems = append(updatedItems, ci)
|
||||
}
|
||||
}
|
||||
|
||||
if len(updatedItems) == 0 {
|
||||
return DeleteCartFromRedis(ctx, userID)
|
||||
}
|
||||
|
||||
cart.CartItems = updatedItems
|
||||
return SetCartToRedis(ctx, userID, *cart)
|
||||
return expiringKeys, nil
|
||||
}
|
||||
|
|
|
@ -1,35 +1,266 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"rijig/dto"
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"rijig/dto"
|
||||
"rijig/internal/repositories"
|
||||
"rijig/model"
|
||||
)
|
||||
|
||||
type CartService interface {
|
||||
GetCart(ctx context.Context, userID string) (*dto.RequestCartDTO, error)
|
||||
AddOrUpdateItem(ctx context.Context, userID string, item dto.RequestCartItemDTO) error
|
||||
AddOrUpdateItem(ctx context.Context, userID string, req dto.RequestCartItemDTO) error
|
||||
GetCart(ctx context.Context, userID string) (*dto.ResponseCartDTO, error)
|
||||
DeleteItem(ctx context.Context, userID string, trashID string) error
|
||||
ClearCart(ctx context.Context, userID string) error
|
||||
Checkout(ctx context.Context, userID string) error
|
||||
}
|
||||
|
||||
type cartService struct{}
|
||||
|
||||
func NewCartService() CartService {
|
||||
return &cartService{}
|
||||
type cartService struct {
|
||||
repo repositories.CartRepository
|
||||
trashRepo repositories.TrashRepository
|
||||
}
|
||||
|
||||
func (s *cartService) GetCart(ctx context.Context, userID string) (*dto.RequestCartDTO, error) {
|
||||
return GetCartFromRedis(ctx, userID)
|
||||
func NewCartService(repo repositories.CartRepository, trashRepo repositories.TrashRepository) CartService {
|
||||
return &cartService{repo: repo, trashRepo: trashRepo}
|
||||
}
|
||||
|
||||
func (s *cartService) AddOrUpdateItem(ctx context.Context, userID string, item dto.RequestCartItemDTO) error {
|
||||
return UpdateOrAddCartItemToRedis(ctx, userID, item)
|
||||
func (s *cartService) AddOrUpdateItem(ctx context.Context, userID string, req dto.RequestCartItemDTO) error {
|
||||
if req.Amount <= 0 {
|
||||
return errors.New("amount harus lebih dari 0")
|
||||
}
|
||||
|
||||
_, err := s.trashRepo.GetTrashCategoryByID(ctx, req.TrashID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingCart, err := GetCartFromRedis(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existingCart == nil {
|
||||
existingCart = &dto.RequestCartDTO{
|
||||
CartItems: []dto.RequestCartItemDTO{},
|
||||
}
|
||||
}
|
||||
|
||||
updated := false
|
||||
for i, item := range existingCart.CartItems {
|
||||
if item.TrashID == req.TrashID {
|
||||
existingCart.CartItems[i].Amount = req.Amount
|
||||
updated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !updated {
|
||||
existingCart.CartItems = append(existingCart.CartItems, dto.RequestCartItemDTO{
|
||||
TrashID: req.TrashID,
|
||||
Amount: req.Amount,
|
||||
})
|
||||
}
|
||||
|
||||
return SetCartToRedis(ctx, userID, *existingCart)
|
||||
}
|
||||
|
||||
func (s *cartService) GetCart(ctx context.Context, userID string) (*dto.ResponseCartDTO, error) {
|
||||
|
||||
cached, err := GetCartFromRedis(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cached != nil {
|
||||
|
||||
if err := RefreshCartTTL(ctx, userID); err != nil {
|
||||
log.Printf("Warning: Failed to refresh cart TTL for user %s: %v", userID, err)
|
||||
}
|
||||
|
||||
return s.buildResponseFromCache(ctx, userID, cached)
|
||||
}
|
||||
|
||||
cart, err := s.repo.GetCartByUser(ctx, userID)
|
||||
if err != nil {
|
||||
|
||||
return &dto.ResponseCartDTO{
|
||||
ID: "",
|
||||
UserID: userID,
|
||||
TotalAmount: 0,
|
||||
EstimatedTotalPrice: 0,
|
||||
CartItems: []dto.ResponseCartItemDTO{},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
response := s.buildResponseFromDB(cart)
|
||||
|
||||
cacheData := dto.RequestCartDTO{CartItems: []dto.RequestCartItemDTO{}}
|
||||
for _, item := range cart.CartItems {
|
||||
cacheData.CartItems = append(cacheData.CartItems, dto.RequestCartItemDTO{
|
||||
TrashID: item.TrashCategoryID,
|
||||
Amount: item.Amount,
|
||||
})
|
||||
}
|
||||
|
||||
if err := SetCartToRedis(ctx, userID, cacheData); err != nil {
|
||||
log.Printf("Warning: Failed to cache cart for user %s: %v", userID, err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *cartService) DeleteItem(ctx context.Context, userID string, trashID string) error {
|
||||
return RemoveCartItemFromRedis(ctx, userID, trashID)
|
||||
|
||||
existingCart, err := GetCartFromRedis(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingCart == nil {
|
||||
return errors.New("keranjang tidak ditemukan")
|
||||
}
|
||||
|
||||
filtered := []dto.RequestCartItemDTO{}
|
||||
for _, item := range existingCart.CartItems {
|
||||
if item.TrashID != trashID {
|
||||
filtered = append(filtered, item)
|
||||
}
|
||||
}
|
||||
existingCart.CartItems = filtered
|
||||
|
||||
return SetCartToRedis(ctx, userID, *existingCart)
|
||||
}
|
||||
|
||||
func (s *cartService) ClearCart(ctx context.Context, userID string) error {
|
||||
return DeleteCartFromRedis(ctx, userID)
|
||||
}
|
||||
|
||||
if err := DeleteCartFromRedis(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.repo.DeleteCart(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *cartService) Checkout(ctx context.Context, userID string) error {
|
||||
|
||||
cachedCart, err := GetCartFromRedis(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cachedCart != nil {
|
||||
if err := s.commitCartFromRedis(ctx, userID, cachedCart); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = s.repo.GetCartByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
DeleteCartFromRedis(ctx, userID)
|
||||
return s.repo.DeleteCart(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *cartService) buildResponseFromCache(ctx context.Context, userID string, cached *dto.RequestCartDTO) (*dto.ResponseCartDTO, error) {
|
||||
totalQty := 0.0
|
||||
totalPrice := 0.0
|
||||
items := []dto.ResponseCartItemDTO{}
|
||||
|
||||
for _, item := range cached.CartItems {
|
||||
trash, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashID)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Trash category %s not found for cached cart item", item.TrashID)
|
||||
continue
|
||||
}
|
||||
|
||||
subtotal := item.Amount * trash.EstimatedPrice
|
||||
totalQty += item.Amount
|
||||
totalPrice += subtotal
|
||||
|
||||
items = append(items, dto.ResponseCartItemDTO{
|
||||
ID: "",
|
||||
TrashID: item.TrashID,
|
||||
TrashName: trash.Name,
|
||||
TrashIcon: trash.Icon,
|
||||
TrashPrice: trash.EstimatedPrice,
|
||||
Amount: item.Amount,
|
||||
SubTotalEstimatedPrice: subtotal,
|
||||
})
|
||||
}
|
||||
|
||||
return &dto.ResponseCartDTO{
|
||||
ID: "-",
|
||||
UserID: userID,
|
||||
TotalAmount: totalQty,
|
||||
EstimatedTotalPrice: totalPrice,
|
||||
CartItems: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *cartService) buildResponseFromDB(cart *model.Cart) *dto.ResponseCartDTO {
|
||||
var items []dto.ResponseCartItemDTO
|
||||
for _, item := range cart.CartItems {
|
||||
items = append(items, dto.ResponseCartItemDTO{
|
||||
ID: item.ID,
|
||||
TrashID: item.TrashCategoryID,
|
||||
TrashName: item.TrashCategory.Name,
|
||||
TrashIcon: item.TrashCategory.Icon,
|
||||
TrashPrice: item.TrashCategory.EstimatedPrice,
|
||||
Amount: item.Amount,
|
||||
SubTotalEstimatedPrice: item.SubTotalEstimatedPrice,
|
||||
})
|
||||
}
|
||||
|
||||
return &dto.ResponseCartDTO{
|
||||
ID: cart.ID,
|
||||
UserID: cart.UserID,
|
||||
TotalAmount: cart.TotalAmount,
|
||||
EstimatedTotalPrice: cart.EstimatedTotalPrice,
|
||||
CartItems: items,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *cartService) commitCartFromRedis(ctx context.Context, userID string, cachedCart *dto.RequestCartDTO) error {
|
||||
if len(cachedCart.CartItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
totalAmount := 0.0
|
||||
totalPrice := 0.0
|
||||
var cartItems []model.CartItem
|
||||
|
||||
for _, item := range cachedCart.CartItems {
|
||||
trash, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashID)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Skipping invalid trash category %s during commit", item.TrashID)
|
||||
continue
|
||||
}
|
||||
|
||||
subtotal := item.Amount * trash.EstimatedPrice
|
||||
totalAmount += item.Amount
|
||||
totalPrice += subtotal
|
||||
|
||||
cartItems = append(cartItems, model.CartItem{
|
||||
TrashCategoryID: item.TrashID,
|
||||
Amount: item.Amount,
|
||||
SubTotalEstimatedPrice: subtotal,
|
||||
})
|
||||
}
|
||||
|
||||
if len(cartItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
newCart := &model.Cart{
|
||||
UserID: userID,
|
||||
TotalAmount: totalAmount,
|
||||
EstimatedTotalPrice: totalPrice,
|
||||
CartItems: cartItems,
|
||||
}
|
||||
|
||||
return s.repo.CreateCartWithItems(ctx, newCart)
|
||||
}
|
||||
|
|
|
@ -67,8 +67,6 @@ func (s *requestPickupService) ConvertCartToRequestPickup(ctx context.Context, u
|
|||
Notes: req.Notes,
|
||||
StatusPickup: "waiting_collector",
|
||||
RequestItems: requestItems,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.pickupRepo.CreateRequestPickup(ctx, &pickup); err != nil {
|
||||
|
|
|
@ -3,109 +3,156 @@ package worker
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"rijig/config"
|
||||
"rijig/dto"
|
||||
"rijig/internal/repositories"
|
||||
"rijig/internal/services"
|
||||
"rijig/model"
|
||||
)
|
||||
|
||||
func CommitExpiredCartsToDB() error {
|
||||
type CartWorker struct {
|
||||
cartService services.CartService
|
||||
cartRepo repositories.CartRepository
|
||||
trashRepo repositories.TrashRepository
|
||||
}
|
||||
|
||||
func NewCartWorker(cartService services.CartService, cartRepo repositories.CartRepository, trashRepo repositories.TrashRepository) *CartWorker {
|
||||
return &CartWorker{
|
||||
cartService: cartService,
|
||||
cartRepo: cartRepo,
|
||||
trashRepo: trashRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *CartWorker) AutoCommitExpiringCarts() error {
|
||||
ctx := context.Background()
|
||||
threshold := 1 * time.Minute
|
||||
|
||||
keys, err := config.RedisClient.Keys(ctx, "cart:user:*").Result()
|
||||
keys, err := services.GetExpiringCartKeys(ctx, threshold)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error fetching cart keys: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("[CART-WORKER] Found %d carts expiring within 1 minute", len(keys))
|
||||
|
||||
successCount := 0
|
||||
for _, key := range keys {
|
||||
ttl, err := config.RedisClient.TTL(ctx, key).Result()
|
||||
if err != nil || ttl > 30*time.Second {
|
||||
userID := w.extractUserIDFromKey(key)
|
||||
if userID == "" {
|
||||
log.Printf("[CART-WORKER] Invalid key format: %s", key)
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := config.RedisClient.Get(ctx, key).Result()
|
||||
hasCart, err := w.cartRepo.HasExistingCart(ctx, userID)
|
||||
if err != nil {
|
||||
log.Printf("[CART-WORKER] Error checking existing cart for user %s: %v", userID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var cart dto.RequestCartDTO
|
||||
if err := json.Unmarshal([]byte(val), &cart); err != nil {
|
||||
if hasCart {
|
||||
|
||||
if err := services.DeleteCartFromRedis(ctx, userID); err != nil {
|
||||
log.Printf("[CART-WORKER] Failed to delete Redis cache for user %s: %v", userID, err)
|
||||
} else {
|
||||
log.Printf("[CART-WORKER] Deleted Redis cache for user %s (already has DB cart)", userID)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
userID := extractUserIDFromKey(key)
|
||||
cartData, err := w.getCartFromRedis(ctx, key)
|
||||
if err != nil {
|
||||
log.Printf("[CART-WORKER] Failed to get cart data for key %s: %v", key, err)
|
||||
continue
|
||||
}
|
||||
|
||||
cartID := SaveCartToDB(ctx, userID, &cart)
|
||||
if err := w.commitCartToDB(ctx, userID, cartData); err != nil {
|
||||
log.Printf("[CART-WORKER] Failed to commit cart for user %s: %v", userID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_ = config.RedisClient.Del(ctx, key).Err()
|
||||
|
||||
fmt.Printf(
|
||||
"[AUTO-COMMIT] UserID: %s | CartID: %s | TotalItem: %d | EstimatedTotalPrice: %.2f | Committed at: %s\n",
|
||||
userID, cartID, len(cart.CartItems), calculateTotalEstimated(&cart), time.Now().Format(time.RFC3339),
|
||||
)
|
||||
if err := services.DeleteCartFromRedis(ctx, userID); err != nil {
|
||||
log.Printf("[CART-WORKER] Warning: Failed to delete Redis key after commit for user %s: %v", userID, err)
|
||||
}
|
||||
|
||||
successCount++
|
||||
log.Printf("[CART-WORKER] Successfully auto-committed cart for user %s", userID)
|
||||
}
|
||||
|
||||
log.Printf("[CART-WORKER] Auto-commit completed: %d successful commits", successCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractUserIDFromKey(key string) string {
|
||||
|
||||
func (w *CartWorker) extractUserIDFromKey(key string) string {
|
||||
parts := strings.Split(key, ":")
|
||||
if len(parts) == 3 {
|
||||
return parts[2]
|
||||
if len(parts) >= 2 {
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func SaveCartToDB(ctx context.Context, userID string, cart *dto.RequestCartDTO) string {
|
||||
totalAmount := float32(0)
|
||||
totalPrice := float32(0)
|
||||
func (w *CartWorker) getCartFromRedis(ctx context.Context, key string) (*dto.RequestCartDTO, error) {
|
||||
val, err := config.RedisClient.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cart dto.RequestCartDTO
|
||||
if err := json.Unmarshal([]byte(val), &cart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cart, nil
|
||||
}
|
||||
|
||||
func (w *CartWorker) commitCartToDB(ctx context.Context, userID string, cartData *dto.RequestCartDTO) error {
|
||||
if len(cartData.CartItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
totalAmount := 0.0
|
||||
totalPrice := 0.0
|
||||
var cartItems []model.CartItem
|
||||
for _, item := range cart.CartItems {
|
||||
|
||||
var trash model.TrashCategory
|
||||
if err := config.DB.First(&trash, "id = ?", item.TrashID).Error; err != nil {
|
||||
for _, item := range cartData.CartItems {
|
||||
if item.Amount <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
subtotal := trash.EstimatedPrice * float64(item.Amount)
|
||||
trash, err := w.trashRepo.GetTrashCategoryByID(ctx, item.TrashID)
|
||||
if err != nil {
|
||||
log.Printf("[CART-WORKER] Warning: Skipping invalid trash category %s", item.TrashID)
|
||||
continue
|
||||
}
|
||||
|
||||
subtotal := item.Amount * trash.EstimatedPrice
|
||||
totalAmount += item.Amount
|
||||
totalPrice += float32(subtotal)
|
||||
totalPrice += subtotal
|
||||
|
||||
cartItems = append(cartItems, model.CartItem{
|
||||
TrashCategoryID: item.TrashID,
|
||||
Amount: item.Amount,
|
||||
SubTotalEstimatedPrice: float32(subtotal),
|
||||
SubTotalEstimatedPrice: subtotal,
|
||||
})
|
||||
}
|
||||
|
||||
newCart := model.Cart{
|
||||
if len(cartItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
newCart := &model.Cart{
|
||||
UserID: userID,
|
||||
TotalAmount: totalAmount,
|
||||
EstimatedTotalPrice: totalPrice,
|
||||
CartItems: cartItems,
|
||||
}
|
||||
|
||||
if err := config.DB.WithContext(ctx).Create(&newCart).Error; err != nil {
|
||||
fmt.Printf("Error committing cart: %v\n", err)
|
||||
}
|
||||
|
||||
return newCart.ID
|
||||
}
|
||||
|
||||
func calculateTotalEstimated(cart *dto.RequestCartDTO) float32 {
|
||||
var total float32
|
||||
for _, item := range cart.CartItems {
|
||||
var trash model.TrashCategory
|
||||
if err := config.DB.First(&trash, "id = ?", item.TrashID).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
total += item.Amount * float32(trash.EstimatedPrice)
|
||||
}
|
||||
return total
|
||||
return w.cartRepo.CreateCartWithItems(ctx, newCart)
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ type Cart struct {
|
|||
UserID string `gorm:"not null" json:"user_id"`
|
||||
User User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE;" json:"-"`
|
||||
CartItems []CartItem `gorm:"foreignKey:CartID;constraint:OnDelete:CASCADE;" json:"cart_items"`
|
||||
TotalAmount float32 `json:"total_amount"`
|
||||
EstimatedTotalPrice float32 `json:"estimated_total_price"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
EstimatedTotalPrice float64 `json:"estimated_total_price"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ type CartItem struct {
|
|||
Cart *Cart `gorm:"foreignKey:CartID;constraint:OnDelete:CASCADE;" json:"-"`
|
||||
TrashCategoryID string `gorm:"not null" json:"trash_id"`
|
||||
TrashCategory TrashCategory `gorm:"foreignKey:TrashCategoryID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"trash_category"`
|
||||
Amount float32 `json:"amount"`
|
||||
SubTotalEstimatedPrice float32 `json:"subtotal_estimated_price"`
|
||||
Amount float64 `json:"amount"`
|
||||
SubTotalEstimatedPrice float64 `json:"subtotal_estimated_price"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package presentation
|
||||
|
||||
import (
|
||||
"rijig/config"
|
||||
"rijig/internal/handler"
|
||||
"rijig/internal/repositories"
|
||||
"rijig/internal/services"
|
||||
"rijig/middleware"
|
||||
|
||||
|
@ -9,16 +11,18 @@ import (
|
|||
)
|
||||
|
||||
func TrashCartRouter(api fiber.Router) {
|
||||
cartService := services.NewCartService()
|
||||
repo := repositories.NewCartRepository()
|
||||
trashRepo := repositories.NewTrashRepository(config.DB)
|
||||
cartService := services.NewCartService(repo, trashRepo)
|
||||
cartHandler := handler.NewCartHandler(cartService)
|
||||
|
||||
cart := api.Group("/cart")
|
||||
cart.Use(middleware.AuthMiddleware)
|
||||
|
||||
cart.Get("/", cartHandler.GetCart)
|
||||
cart.Post("/item", cartHandler.AddOrUpdateCartItem)
|
||||
cart.Post("/items", cartHandler.AddMultipleCartItems)
|
||||
cart.Delete("/item/:trashID", cartHandler.DeleteCartItem)
|
||||
cart.Delete("/", cartHandler.ClearCart)
|
||||
|
||||
cart.Post("/item", cartHandler.AddOrUpdateItem)
|
||||
cart.Delete("/item/:trash_id", cartHandler.DeleteItem)
|
||||
cart.Delete("/clear", cartHandler.ClearCart)
|
||||
}
|
||||
|
||||
// cart.Post("/items", cartHandler.AddMultipleCartItems)
|
||||
|
|
|
@ -11,11 +11,13 @@ import (
|
|||
)
|
||||
|
||||
func CollectorRouter(api fiber.Router) {
|
||||
cartRepo := repositories.NewCartRepository()
|
||||
// trashRepo repositories.TrashRepository
|
||||
|
||||
pickupRepo := repositories.NewRequestPickupRepository()
|
||||
trashRepo := repositories.NewTrashRepository(config.DB)
|
||||
historyRepo := repositories.NewPickupStatusHistoryRepository()
|
||||
cartService := services.NewCartService()
|
||||
cartService := services.NewCartService(cartRepo, trashRepo)
|
||||
|
||||
pickupService := services.NewRequestPickupService(trashRepo, pickupRepo, cartService, historyRepo)
|
||||
pickupHandler := handler.NewRequestPickupHandler(pickupService)
|
||||
|
|
|
@ -11,10 +11,12 @@ import (
|
|||
)
|
||||
|
||||
func RequestPickupRouter(api fiber.Router) {
|
||||
cartRepo := repositories.NewCartRepository()
|
||||
pickupRepo := repositories.NewRequestPickupRepository()
|
||||
historyRepo := repositories.NewPickupStatusHistoryRepository()
|
||||
trashRepo := repositories.NewTrashRepository(config.DB)
|
||||
cartService := services.NewCartService()
|
||||
|
||||
cartService := services.NewCartService(cartRepo, trashRepo)
|
||||
historyService := services.NewPickupStatusHistoryService(historyRepo)
|
||||
|
||||
pickupService := services.NewRequestPickupService(trashRepo, pickupRepo, cartService, historyRepo)
|
||||
|
|
|
@ -5,9 +5,8 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"rijig/config"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue