feat: add all feature are maybe needed in pickup bussiness

This commit is contained in:
pahmiudahgede 2025-05-22 12:36:27 +07:00
parent b7a1d10898
commit ab6f282235
27 changed files with 1667 additions and 562 deletions

View File

@ -54,6 +54,8 @@ func ConnectDatabase() {
// =>requestpickup preparation<= // =>requestpickup preparation<=
&model.RequestPickup{}, &model.RequestPickup{},
&model.RequestPickupItem{}, &model.RequestPickupItem{},
&model.PickupStatusHistory{},
&model.PickupRating{},
&model.Cart{}, &model.Cart{},
&model.CartItem{}, &model.CartItem{},

View File

@ -5,6 +5,17 @@ import (
"strings" "strings"
) )
type NearbyCollectorDTO struct {
CollectorID string `json:"collector_id"`
Name string `json:"name"`
Phone string `json:"phone"`
Rating float32 `json:"rating"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
DistanceKm float64 `json:"distance_km"`
MatchedTrash []string `json:"matched_trash_ids"`
}
type RequestCollectorDTO struct { type RequestCollectorDTO struct {
AddressId string `json:"address_id"` AddressId string `json:"address_id"`
AvaibleTrashbyCollector []RequestAvaibleTrashbyCollector `json:"avaible_trash"` AvaibleTrashbyCollector []RequestAvaibleTrashbyCollector `json:"avaible_trash"`

25
dto/rating_dto.go Normal file
View File

@ -0,0 +1,25 @@
package dto
import "strings"
type CreatePickupRatingDTO struct {
Rating float32 `json:"rating"`
Feedback string `json:"feedback"`
}
func (r *CreatePickupRatingDTO) ValidateCreatePickupRatingDTO() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.Rating < 1.0 || r.Rating > 5.0 {
errors["rating"] = append(errors["rating"], "Rating harus antara 1.0 sampai 5.0")
}
if len(strings.TrimSpace(r.Feedback)) > 255 {
errors["feedback"] = append(errors["feedback"], "Feedback tidak boleh lebih dari 255 karakter")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

85
dto/request_pickup_dto.go Normal file
View File

@ -0,0 +1,85 @@
package dto
import (
"strings"
)
// type NearbyCollectorDTO struct {
// CollectorID string `json:"collector_id"`
// Name string `json:"name"`
// Phone string `json:"phone"`
// Rating float32 `json:"rating"`
// Latitude float64 `json:"latitude"`
// Longitude float64 `json:"longitude"`
// DistanceKm float64 `json:"distance_km"`
// MatchedTrash []string `json:"matched_trash_ids"`
// }
type SelectCollectorDTO struct {
CollectorID string `json:"collector_id"`
}
type UpdateRequestPickupItemDTO struct {
ItemID string `json:"item_id"`
Amount float64 `json:"actual_amount"`
}
type UpdatePickupItemsRequest struct {
Items []UpdateRequestPickupItemDTO `json:"items"`
}
func (r *SelectCollectorDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.CollectorID) == "" {
errors["collector_id"] = append(errors["collector_id"], "collector_id tidak boleh kosong")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type AssignedPickupDTO struct {
PickupID string `json:"pickup_id"`
UserID string `json:"user_id"`
UserName string `json:"user_name"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Notes string `json:"notes"`
MatchedTrash []string `json:"matched_trash"`
}
type PickupRequestForCollectorDTO struct {
PickupID string `json:"pickup_id"`
UserID string `json:"user_id"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
DistanceKm float64 `json:"distance_km"`
MatchedTrash []string `json:"matched_trash"`
}
type RequestPickupDTO struct {
AddressID string `json:"address_id"`
RequestMethod string `json:"request_method"` // "manual" atau "otomatis"
Notes string `json:"notes,omitempty"`
}
func (r *RequestPickupDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.AddressID) == "" {
errors["address_id"] = append(errors["address_id"], "alamat harus dipilih")
}
method := strings.ToLower(strings.TrimSpace(r.RequestMethod))
if method != "manual" && method != "otomatis" {
errors["request_method"] = append(errors["request_method"], "harus manual atau otomatis")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -0,0 +1,37 @@
package handler
import (
"context"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type PickupStatusHistoryHandler interface {
GetStatusHistory(c *fiber.Ctx) error
}
type pickupStatusHistoryHandler struct {
service services.PickupStatusHistoryService
}
func NewPickupStatusHistoryHandler(service services.PickupStatusHistoryService) PickupStatusHistoryHandler {
return &pickupStatusHistoryHandler{service: service}
}
func (h *pickupStatusHistoryHandler) GetStatusHistory(c *fiber.Ctx) error {
pickupID := c.Params("id")
if pickupID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"pickup_id": {"pickup ID tidak boleh kosong"},
})
}
histories, err := h.service.GetStatusHistory(context.Background(), pickupID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, histories, "Riwayat status pickup berhasil diambil")
}

View File

@ -0,0 +1,49 @@
package handler
import (
"context"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type PickupMatchingHandler interface {
GetNearbyCollectorsForPickup(c *fiber.Ctx) error
GetAvailablePickupForCollector(c *fiber.Ctx) error
}
type pickupMatchingHandler struct {
service services.PickupMatchingService
}
func NewPickupMatchingHandler(service services.PickupMatchingService) PickupMatchingHandler {
return &pickupMatchingHandler{service: service}
}
func (h *pickupMatchingHandler) GetNearbyCollectorsForPickup(c *fiber.Ctx) error {
pickupID := c.Params("pickupID")
if pickupID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"pickup_id": {"pickup ID harus disertakan"},
})
}
collectors, err := h.service.FindNearbyCollectorsForPickup(context.Background(), pickupID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, collectors, "Data collector terdekat berhasil diambil")
}
func (h *pickupMatchingHandler) GetAvailablePickupForCollector(c *fiber.Ctx) error {
collectorID := c.Locals("userID").(string)
pickups, err := h.service.FindAvailableRequestsForCollector(context.Background(), collectorID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, pickups, "Data request pickup otomatis berhasil diambil")
}

View File

@ -0,0 +1,66 @@
package handler
import (
"context"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type PickupRatingHandler interface {
CreateRating(c *fiber.Ctx) error
GetRatingsByCollector(c *fiber.Ctx) error
GetAverageRating(c *fiber.Ctx) error
}
type pickupRatingHandler struct {
service services.PickupRatingService
}
func NewPickupRatingHandler(service services.PickupRatingService) PickupRatingHandler {
return &pickupRatingHandler{service: service}
}
func (h *pickupRatingHandler) CreateRating(c *fiber.Ctx) error {
pickupID := c.Params("id")
userID := c.Locals("userID").(string)
collectorID := c.Query("collector_id")
var req dto.CreatePickupRatingDTO
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"Format JSON tidak valid"},
})
}
if errs, ok := req.ValidateCreatePickupRatingDTO(); !ok {
return utils.ValidationErrorResponse(c, errs)
}
err := h.service.CreateRating(context.Background(), userID, pickupID, collectorID, req)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Rating berhasil dikirim")
}
func (h *pickupRatingHandler) GetRatingsByCollector(c *fiber.Ctx) error {
collectorID := c.Params("id")
ratings, err := h.service.GetRatingsByCollector(context.Background(), collectorID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, ratings, "Daftar rating collector berhasil diambil")
}
func (h *pickupRatingHandler) GetAverageRating(c *fiber.Ctx) error {
collectorID := c.Params("id")
avg, err := h.service.GetAverageRating(context.Background(), collectorID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, fiber.Map{"average_rating": avg}, "Rata-rata rating collector")
}

View File

@ -0,0 +1,150 @@
package handler
import (
"context"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"time"
"github.com/gofiber/fiber/v2"
)
type RequestPickupHandler interface {
CreateRequestPickup(c *fiber.Ctx) error
SelectCollector(c *fiber.Ctx) error
GetAssignedPickup(c *fiber.Ctx) error
ConfirmPickup(c *fiber.Ctx) error
UpdatePickupStatus(c *fiber.Ctx) error
UpdatePickupItemActualAmount(c *fiber.Ctx) error
}
type requestPickupHandler struct {
service services.RequestPickupService
}
func NewRequestPickupHandler(service services.RequestPickupService) RequestPickupHandler {
return &requestPickupHandler{service: service}
}
func (h *requestPickupHandler) CreateRequestPickup(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
var req dto.RequestPickupDTO
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if errs, ok := req.Validate(); !ok {
return utils.ValidationErrorResponse(c, errs)
}
if err := h.service.ConvertCartToRequestPickup(context.Background(), userID, req); err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Request pickup berhasil dibuat")
}
func (h *requestPickupHandler) SelectCollector(c *fiber.Ctx) error {
pickupID := c.Params("id")
if pickupID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"pickup_id": {"pickup ID harus disertakan"},
})
}
var req dto.SelectCollectorDTO
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if errs, ok := req.Validate(); !ok {
return utils.ValidationErrorResponse(c, errs)
}
if err := h.service.AssignCollectorToRequest(context.Background(), pickupID, req); err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Collector berhasil dipilih untuk pickup")
}
func (h *requestPickupHandler) GetAssignedPickup(c *fiber.Ctx) error {
collectorID := c.Locals("userID").(string)
result, err := h.service.FindRequestsAssignedToCollector(context.Background(), collectorID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, result, "Data pickup yang ditugaskan berhasil diambil")
}
func (h *requestPickupHandler) ConfirmPickup(c *fiber.Ctx) error {
pickupID := c.Params("id")
if pickupID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"pickup_id": {"pickup ID wajib diisi"},
})
}
err := h.service.ConfirmPickupByCollector(context.Background(), pickupID, time.Now())
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Pickup berhasil dikonfirmasi oleh collector")
}
func (h *requestPickupHandler) UpdatePickupStatus(c *fiber.Ctx) error {
pickupID := c.Params("id")
if pickupID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"pickup_id": {"pickup ID tidak boleh kosong"},
})
}
if err := h.service.UpdatePickupStatusToPickingUp(context.Background(), pickupID); err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Status pickup berhasil diperbarui menjadi 'collector_are_picking_up'")
}
func (h *requestPickupHandler) UpdatePickupItemActualAmount(c *fiber.Ctx) error {
pickupID := c.Params("id")
if pickupID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"pickup_id": {"pickup ID tidak boleh kosong"},
})
}
var req dto.UpdatePickupItemsRequest
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if len(req.Items) == 0 {
return utils.ValidationErrorResponse(c, map[string][]string{
"items": {"daftar item tidak boleh kosong"},
})
}
for _, item := range req.Items {
if item.ItemID == "" || item.Amount <= 0 {
return utils.ValidationErrorResponse(c, map[string][]string{
"item": {"item_id harus valid dan amount > 0"},
})
}
}
if err := h.service.UpdateActualPickupItems(context.Background(), pickupID, req.Items); err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Berat aktual dan harga berhasil diperbarui")
}

View File

@ -4,21 +4,11 @@ import (
"context" "context"
"errors" "errors"
// "fmt"
// "log"
"rijig/config" "rijig/config"
"rijig/model" "rijig/model"
// "gorm.io/gorm"
) )
type CollectorRepository interface { type CollectorRepository interface {
// FindActiveCollectors() ([]model.Collector, error)
// FindCollectorById(collector_id string) (*model.Collector, error)
// FindCollectorByIdWithoutAddr(collector_id string) (*model.Collector, error)
// CreateCollector(collector *model.Collector) error
// UpdateCollector(userId string, jobStatus string) (*model.Collector, error)
CreateCollector(ctx context.Context, collector *model.Collector) error CreateCollector(ctx context.Context, collector *model.Collector) error
AddAvaibleTrash(ctx context.Context, trashItems []model.AvaibleTrashByCollector) error AddAvaibleTrash(ctx context.Context, trashItems []model.AvaibleTrashByCollector) error
GetCollectorByID(ctx context.Context, collectorID string) (*model.Collector, error) GetCollectorByID(ctx context.Context, collectorID string) (*model.Collector, error)
@ -27,80 +17,18 @@ type CollectorRepository interface {
UpdateCollector(ctx context.Context, collector *model.Collector, updates map[string]interface{}) error UpdateCollector(ctx context.Context, collector *model.Collector, updates map[string]interface{}) error
UpdateAvaibleTrashByCollector(ctx context.Context, collectorID string, updatedTrash []model.AvaibleTrashByCollector) error UpdateAvaibleTrashByCollector(ctx context.Context, collectorID string, updatedTrash []model.AvaibleTrashByCollector) error
DeleteAvaibleTrash(ctx context.Context, trashID string) error DeleteAvaibleTrash(ctx context.Context, trashID string) error
GetActiveCollectorsWithTrashAndAddress(ctx context.Context) ([]model.Collector, error)
GetCollectorWithAddressAndTrash(ctx context.Context, collectorID string) (*model.Collector, error)
} }
type collectorRepository struct { type collectorRepository struct {
// DB *gorm.DB
} }
// func NewCollectorRepository(db *gorm.DB) CollectorRepository {
// return &collectorRepository{DB: db}
// }
func NewCollectorRepository() CollectorRepository { func NewCollectorRepository() CollectorRepository {
return &collectorRepository{} return &collectorRepository{}
} }
// func (r *collectorRepository) FindActiveCollectors() ([]model.Collector, error) {
// var collectors []model.Collector
// err := r.DB.Preload("Address").Where("job_status = ?", "active").First(&collectors).Error
// if err != nil {
// return nil, fmt.Errorf("failed to fetch active collectors: %v", err)
// }
// return collectors, nil
// }
// func (r *collectorRepository) FindCollectorById(collector_id string) (*model.Collector, error) {
// var collector model.Collector
// err := r.DB.Preload("Address").Where("user_id = ?", collector_id).First(&collector).Error
// if err != nil {
// return nil, fmt.Errorf("error fetching collector: %v", err)
// }
// fmt.Printf("menampilkan data collector %v", &collector)
// return &collector, nil
// }
// func (r *collectorRepository) FindCollectorByIdWithoutAddr(collector_id string) (*model.Collector, error) {
// var collector model.Collector
// err := r.DB.Where("user_id = ?", collector_id).First(&collector).Error
// if err != nil {
// return nil, fmt.Errorf("error fetching collector: %v", err)
// }
// fmt.Printf("menampilkan data collector %v", &collector)
// return &collector, nil
// }
// func (r *collectorRepository) CreateCollector(collector *model.Collector) error {
// if err := r.DB.Create(collector).Error; err != nil {
// return fmt.Errorf("failed to create collector: %v", err)
// }
// return nil
// }
// func (r *collectorRepository) UpdateCollector(userId string, jobStatus string) (*model.Collector, error) {
// var existingCollector model.Collector
// if err := r.DB.Where("user_id = ?", userId).First(&existingCollector).Error; err != nil {
// if errors.Is(err, gorm.ErrRecordNotFound) {
// return nil, fmt.Errorf("collector dengan user_id %s tidak ditemukan", userId)
// }
// log.Printf("Gagal mencari collector: %v", err)
// return nil, fmt.Errorf("gagal fetching collector: %w", err)
// }
// if jobStatus != "active" && jobStatus != "nonactive" {
// return nil, fmt.Errorf("invalid job status: %v", jobStatus)
// }
// if err := r.DB.Model(&existingCollector).Update("jobstatus", jobStatus).Error; err != nil {
// log.Printf("Gagal mengupdate data collector: %v", err)
// return nil, fmt.Errorf("gagal mengupdate job status untuk collector: %w", err)
// }
// return &existingCollector, nil
// }
func (r *collectorRepository) CreateCollector(ctx context.Context, collector *model.Collector) error { func (r *collectorRepository) CreateCollector(ctx context.Context, collector *model.Collector) error {
return config.DB.WithContext(ctx).Create(collector).Error return config.DB.WithContext(ctx).Create(collector).Error
} }
@ -175,3 +103,35 @@ func (r *collectorRepository) DeleteAvaibleTrash(ctx context.Context, trashID st
return config.DB.WithContext(ctx). return config.DB.WithContext(ctx).
Delete(&model.AvaibleTrashByCollector{}, "id = ?", trashID).Error Delete(&model.AvaibleTrashByCollector{}, "id = ?", trashID).Error
} }
//
func (r *collectorRepository) GetActiveCollectorsWithTrashAndAddress(ctx context.Context) ([]model.Collector, error) {
var collectors []model.Collector
err := config.DB.WithContext(ctx).
Preload("User").
Preload("Address").
Preload("AvaibleTrashbyCollector.TrashCategory").
Where("job_status = ?", "active").
Find(&collectors).Error
if err != nil {
return nil, err
}
return collectors, nil
}
func (r *collectorRepository) GetCollectorWithAddressAndTrash(ctx context.Context, collectorID string) (*model.Collector, error) {
var collector model.Collector
err := config.DB.WithContext(ctx).
Preload("Address").
Preload("AvaibleTrashbyCollector").
Where("id = ?", collectorID).
First(&collector).Error
if err != nil {
return nil, err
}
return &collector, nil
}

View File

@ -0,0 +1,34 @@
package repositories
import (
"context"
"rijig/config"
"rijig/model"
)
type PickupStatusHistoryRepository interface {
CreateStatusHistory(ctx context.Context, history model.PickupStatusHistory) error
GetStatusHistoryByRequestID(ctx context.Context, requestID string) ([]model.PickupStatusHistory, error)
}
type pickupStatusHistoryRepository struct{}
func NewPickupStatusHistoryRepository() PickupStatusHistoryRepository {
return &pickupStatusHistoryRepository{}
}
func (r *pickupStatusHistoryRepository) CreateStatusHistory(ctx context.Context, history model.PickupStatusHistory) error {
return config.DB.WithContext(ctx).Create(&history).Error
}
func (r *pickupStatusHistoryRepository) GetStatusHistoryByRequestID(ctx context.Context, requestID string) ([]model.PickupStatusHistory, error) {
var histories []model.PickupStatusHistory
err := config.DB.WithContext(ctx).
Where("request_id = ?", requestID).
Order("changed_at asc").
Find(&histories).Error
if err != nil {
return nil, err
}
return histories, nil
}

View File

@ -0,0 +1,48 @@
package repositories
import (
"context"
"rijig/config"
"rijig/model"
)
type PickupRatingRepository interface {
CreateRating(ctx context.Context, rating model.PickupRating) error
GetRatingsByCollector(ctx context.Context, collectorID string) ([]model.PickupRating, error)
CalculateAverageRating(ctx context.Context, collectorID string) (float32, error)
}
type pickupRatingRepository struct{}
func NewPickupRatingRepository() PickupRatingRepository {
return &pickupRatingRepository{}
}
func (r *pickupRatingRepository) CreateRating(ctx context.Context, rating model.PickupRating) error {
return config.DB.WithContext(ctx).Create(&rating).Error
}
func (r *pickupRatingRepository) GetRatingsByCollector(ctx context.Context, collectorID string) ([]model.PickupRating, error) {
var ratings []model.PickupRating
err := config.DB.WithContext(ctx).
Where("collector_id = ?", collectorID).
Order("created_at desc").
Find(&ratings).Error
if err != nil {
return nil, err
}
return ratings, nil
}
func (r *pickupRatingRepository) CalculateAverageRating(ctx context.Context, collectorID string) (float32, error) {
var avg float32
err := config.DB.WithContext(ctx).
Model(&model.PickupRating{}).
Select("AVG(rating)").
Where("collector_id = ?", collectorID).
Scan(&avg).Error
if err != nil {
return 0, err
}
return avg, nil
}

View File

@ -0,0 +1,143 @@
package repositories
import (
"context"
"rijig/config"
"rijig/dto"
"rijig/model"
"time"
)
type RequestPickupRepository interface {
CreateRequestPickup(ctx context.Context, pickup *model.RequestPickup) error
GetPickupWithItemsAndAddress(ctx context.Context, id string) (*model.RequestPickup, error)
GetAllAutomaticRequestsWithAddress(ctx context.Context) ([]model.RequestPickup, error)
UpdateCollectorID(ctx context.Context, pickupID, collectorID string) error
GetRequestsAssignedToCollector(ctx context.Context, collectorID string) ([]model.RequestPickup, error)
UpdatePickupStatusAndConfirmationTime(ctx context.Context, pickupID string, status string, confirmedAt time.Time) error
UpdatePickupStatus(ctx context.Context, pickupID string, status string) error
UpdateRequestPickupItemsAmountAndPrice(ctx context.Context, pickupID string, items []dto.UpdateRequestPickupItemDTO) error
}
type requestPickupRepository struct{}
func NewRequestPickupRepository() RequestPickupRepository {
return &requestPickupRepository{}
}
func (r *requestPickupRepository) CreateRequestPickup(ctx context.Context, pickup *model.RequestPickup) error {
return config.DB.WithContext(ctx).Create(pickup).Error
}
func (r *requestPickupRepository) GetPickupWithItemsAndAddress(ctx context.Context, id string) (*model.RequestPickup, error) {
var pickup model.RequestPickup
err := config.DB.WithContext(ctx).
Preload("RequestItems").
Preload("Address").
Where("id = ?", id).
First(&pickup).Error
if err != nil {
return nil, err
}
return &pickup, nil
}
func (r *requestPickupRepository) UpdateCollectorID(ctx context.Context, pickupID, collectorID string) error {
return config.DB.WithContext(ctx).
Model(&model.RequestPickup{}).
Where("id = ?", pickupID).
Update("collector_id", collectorID).
Error
}
func (r *requestPickupRepository) GetAllAutomaticRequestsWithAddress(ctx context.Context) ([]model.RequestPickup, error) {
var pickups []model.RequestPickup
err := config.DB.WithContext(ctx).
Preload("RequestItems").
Preload("Address").
Where("request_method = ?", "otomatis").
Find(&pickups).Error
if err != nil {
return nil, err
}
return pickups, nil
}
func (r *requestPickupRepository) GetRequestsAssignedToCollector(ctx context.Context, collectorID string) ([]model.RequestPickup, error) {
var pickups []model.RequestPickup
err := config.DB.WithContext(ctx).
Preload("User").
Preload("Address").
Preload("RequestItems").
Where("collector_id = ? AND status_pickup = ?", collectorID, "waiting_collector").
Find(&pickups).Error
if err != nil {
return nil, err
}
return pickups, nil
}
func (r *requestPickupRepository) UpdatePickupStatusAndConfirmationTime(ctx context.Context, pickupID string, status string, confirmedAt time.Time) error {
return config.DB.WithContext(ctx).
Model(&model.RequestPickup{}).
Where("id = ?", pickupID).
Updates(map[string]interface{}{
"status_pickup": status,
"confirmed_by_collector_at": confirmedAt,
}).Error
}
func (r *requestPickupRepository) UpdatePickupStatus(ctx context.Context, pickupID string, status string) error {
return config.DB.WithContext(ctx).
Model(&model.RequestPickup{}).
Where("id = ?", pickupID).
Update("status_pickup", status).
Error
}
func (r *requestPickupRepository) UpdateRequestPickupItemsAmountAndPrice(ctx context.Context, pickupID string, items []dto.UpdateRequestPickupItemDTO) error {
// ambil collector_id dulu dari pickup
var pickup model.RequestPickup
if err := config.DB.WithContext(ctx).
Select("collector_id").
Where("id = ?", pickupID).
First(&pickup).Error; err != nil {
return err
}
for _, item := range items {
var pickupItem model.RequestPickupItem
err := config.DB.WithContext(ctx).
Where("id = ? AND request_pickup_id = ?", item.ItemID, pickupID).
First(&pickupItem).Error
if err != nil {
return err
}
var price float64
err = config.DB.WithContext(ctx).
Model(&model.AvaibleTrashByCollector{}).
Where("collector_id = ? AND trash_category_id = ?", pickup.CollectorID, pickupItem.TrashCategoryId).
Select("price").
Scan(&price).Error
if err != nil {
return err
}
finalPrice := item.Amount * price
err = config.DB.WithContext(ctx).
Model(&model.RequestPickupItem{}).
Where("id = ?", item.ItemID).
Updates(map[string]interface{}{
"estimated_amount": item.Amount,
"final_price": finalPrice,
}).Error
if err != nil {
return err
}
}
return nil
}

View File

@ -1,181 +1,181 @@
package repositories package repositories
import ( // import (
"fmt" // "fmt"
"rijig/model" // "rijig/model"
"gorm.io/gorm" // "gorm.io/gorm"
) // )
type RequestPickupRepository interface { // type RequestPickupRepository interface {
CreateRequestPickup(request *model.RequestPickup) error // CreateRequestPickup(request *model.RequestPickup) error
CreateRequestPickupItem(item *model.RequestPickupItem) error // CreateRequestPickupItem(item *model.RequestPickupItem) error
FindRequestPickupByID(id string) (*model.RequestPickup, error) // FindRequestPickupByID(id string) (*model.RequestPickup, error)
FindAllRequestPickups(userId string) ([]model.RequestPickup, error) // FindAllRequestPickups(userId string) ([]model.RequestPickup, error)
FindAllAutomaticMethodRequest(requestMethod, statuspickup string) ([]model.RequestPickup, error) // FindAllAutomaticMethodRequest(requestMethod, statuspickup string) ([]model.RequestPickup, error)
FindRequestPickupByAddressAndStatus(userId, status, method string) (*model.RequestPickup, error) // FindRequestPickupByAddressAndStatus(userId, status, method string) (*model.RequestPickup, error)
FindRequestPickupByStatus(userId, status, method string) (*model.RequestPickup, error) // FindRequestPickupByStatus(userId, status, method string) (*model.RequestPickup, error)
GetRequestPickupItems(requestPickupId string) ([]model.RequestPickupItem, error) // GetRequestPickupItems(requestPickupId string) ([]model.RequestPickupItem, error)
GetAutomaticRequestPickupsForCollector() ([]model.RequestPickup, error) // GetAutomaticRequestPickupsForCollector() ([]model.RequestPickup, error)
GetManualReqMethodforCollect(collector_id string) ([]model.RequestPickup, error) // GetManualReqMethodforCollect(collector_id string) ([]model.RequestPickup, error)
// SelectCollectorInRequest(userId string, collectorId string) error // // SelectCollectorInRequest(userId string, collectorId string) error
UpdateRequestPickup(id string, request *model.RequestPickup) error // UpdateRequestPickup(id string, request *model.RequestPickup) error
DeleteRequestPickup(id string) error // DeleteRequestPickup(id string) error
} // }
type requestPickupRepository struct { // type requestPickupRepository struct {
DB *gorm.DB // DB *gorm.DB
} // }
func NewRequestPickupRepository(db *gorm.DB) RequestPickupRepository { // func NewRequestPickupRepository(db *gorm.DB) RequestPickupRepository {
return &requestPickupRepository{DB: db} // return &requestPickupRepository{DB: db}
} // }
func (r *requestPickupRepository) CreateRequestPickup(request *model.RequestPickup) error { // func (r *requestPickupRepository) CreateRequestPickup(request *model.RequestPickup) error {
if err := r.DB.Create(request).Error; err != nil { // if err := r.DB.Create(request).Error; err != nil {
return fmt.Errorf("failed to create request pickup: %v", err) // return fmt.Errorf("failed to create request pickup: %v", err)
}
for _, item := range request.RequestItems {
item.RequestPickupId = request.ID
if err := r.DB.Create(&item).Error; err != nil {
return fmt.Errorf("failed to create request pickup item: %v", err)
}
}
return nil
}
func (r *requestPickupRepository) CreateRequestPickupItem(item *model.RequestPickupItem) error {
if err := r.DB.Create(item).Error; err != nil {
return fmt.Errorf("failed to create request pickup item: %v", err)
}
return nil
}
func (r *requestPickupRepository) FindRequestPickupByID(id string) (*model.RequestPickup, error) {
var request model.RequestPickup
err := r.DB.Preload("RequestItems").First(&request, "id = ?", id).Error
if err != nil {
return nil, fmt.Errorf("request pickup with ID %s not found: %v", id, err)
}
return &request, nil
}
func (r *requestPickupRepository) FindAllRequestPickups(userId string) ([]model.RequestPickup, error) {
var requests []model.RequestPickup
err := r.DB.Preload("RequestItems").Where("user_id = ?", userId).Find(&requests).Error
if err != nil {
return nil, fmt.Errorf("failed to fetch all request pickups: %v", err)
}
return requests, nil
}
func (r *requestPickupRepository) FindAllAutomaticMethodRequest(requestMethod, statuspickup string) ([]model.RequestPickup, error) {
var requests []model.RequestPickup
err := r.DB.Preload("RequestItems").Where("request_method = ? AND status_pickup = ?", requestMethod, statuspickup).Find(&requests).Error
if err != nil {
return nil, fmt.Errorf("error fetching request pickups with request_method %s: %v", requestMethod, err)
}
return requests, nil
}
func (r *requestPickupRepository) FindRequestPickupByAddressAndStatus(userId, status, method string) (*model.RequestPickup, error) {
var request model.RequestPickup
err := r.DB.Preload("Address").Where("user_id = ? AND status_pickup = ? AND request_method =?", userId, status, method).First(&request).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, fmt.Errorf("failed to check existing request pickup: %v", err)
}
return &request, nil
}
func (r *requestPickupRepository) FindRequestPickupByStatus(userId, status, method string) (*model.RequestPickup, error) {
var request model.RequestPickup
err := r.DB.Where("user_id = ? AND status_pickup = ? AND request_method =?", userId, status, method).First(&request).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, fmt.Errorf("failed to check existing request pickup: %v", err)
}
return &request, nil
}
func (r *requestPickupRepository) UpdateRequestPickup(id string, request *model.RequestPickup) error {
err := r.DB.Model(&model.RequestPickup{}).Where("id = ?", id).Updates(request).Error
if err != nil {
return fmt.Errorf("failed to update request pickup: %v", err)
}
return nil
}
// func (r *requestPickupRepository) SelectCollectorInRequest(userId string, collectorId string) error {
// var request model.RequestPickup
// err := r.DB.Model(&model.RequestPickup{}).
// Where("user_id = ? AND status_pickup = ? AND request_method = ? AND collector_id IS NULL", userId, "waiting_collector", "manual").
// First(&request).Error
// if err != nil {
// if err == gorm.ErrRecordNotFound {
// return fmt.Errorf("no matching request pickup found for user %s", userId)
// }
// return fmt.Errorf("failed to find request pickup: %v", err)
// } // }
// err = r.DB.Model(&model.RequestPickup{}). // for _, item := range request.RequestItems {
// Where("id = ?", request.ID). // item.RequestPickupId = request.ID
// Update("collector_id", collectorId). // if err := r.DB.Create(&item).Error; err != nil {
// Error // return fmt.Errorf("failed to create request pickup item: %v", err)
// if err != nil { // }
// return fmt.Errorf("failed to update collector_id: %v", err) // }
// return nil
// }
// func (r *requestPickupRepository) CreateRequestPickupItem(item *model.RequestPickupItem) error {
// if err := r.DB.Create(item).Error; err != nil {
// return fmt.Errorf("failed to create request pickup item: %v", err)
// } // }
// return nil // return nil
// } // }
func (r *requestPickupRepository) DeleteRequestPickup(id string) error { // func (r *requestPickupRepository) FindRequestPickupByID(id string) (*model.RequestPickup, error) {
// var request model.RequestPickup
// err := r.DB.Preload("RequestItems").First(&request, "id = ?", id).Error
// if err != nil {
// return nil, fmt.Errorf("request pickup with ID %s not found: %v", id, err)
// }
// return &request, nil
// }
if err := r.DB.Where("request_pickup_id = ?", id).Delete(&model.RequestPickupItem{}).Error; err != nil { // func (r *requestPickupRepository) FindAllRequestPickups(userId string) ([]model.RequestPickup, error) {
return fmt.Errorf("failed to delete request pickup items: %v", err) // var requests []model.RequestPickup
} // err := r.DB.Preload("RequestItems").Where("user_id = ?", userId).Find(&requests).Error
// if err != nil {
// return nil, fmt.Errorf("failed to fetch all request pickups: %v", err)
// }
// return requests, nil
// }
err := r.DB.Delete(&model.RequestPickup{}, "id = ?", id).Error // func (r *requestPickupRepository) FindAllAutomaticMethodRequest(requestMethod, statuspickup string) ([]model.RequestPickup, error) {
if err != nil { // var requests []model.RequestPickup
return fmt.Errorf("failed to delete request pickup: %v", err) // err := r.DB.Preload("RequestItems").Where("request_method = ? AND status_pickup = ?", requestMethod, statuspickup).Find(&requests).Error
} // if err != nil {
return nil // return nil, fmt.Errorf("error fetching request pickups with request_method %s: %v", requestMethod, err)
} // }
func (r *requestPickupRepository) GetAutomaticRequestPickupsForCollector() ([]model.RequestPickup, error) { // return requests, nil
var requests []model.RequestPickup // }
err := r.DB.Preload("Address").
Where("request_method = ? AND status_pickup = ? AND collector_id = ?", "otomatis", "waiting_collector", nil).
Find(&requests).Error
if err != nil {
return nil, fmt.Errorf("error fetching pickup requests: %v", err)
}
return requests, nil
}
func (r *requestPickupRepository) GetManualReqMethodforCollect(collector_id string) ([]model.RequestPickup, error) { // func (r *requestPickupRepository) FindRequestPickupByAddressAndStatus(userId, status, method string) (*model.RequestPickup, error) {
var requests []model.RequestPickup // var request model.RequestPickup
err := r.DB.Where("request_method = ? AND status_pickup = ? AND collector_id = ?", "otomatis", "waiting_collector", collector_id). // err := r.DB.Preload("Address").Where("user_id = ? AND status_pickup = ? AND request_method =?", userId, status, method).First(&request).Error
Find(&requests).Error // if err != nil {
if err != nil { // if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("error fetching pickup requests: %v", err) // return nil, nil
} // }
return requests, nil // return nil, fmt.Errorf("failed to check existing request pickup: %v", err)
} // }
// return &request, nil
// }
func (r *requestPickupRepository) GetRequestPickupItems(requestPickupId string) ([]model.RequestPickupItem, error) { // func (r *requestPickupRepository) FindRequestPickupByStatus(userId, status, method string) (*model.RequestPickup, error) {
var items []model.RequestPickupItem // var request model.RequestPickup
// err := r.DB.Where("user_id = ? AND status_pickup = ? AND request_method =?", userId, status, method).First(&request).Error
// if err != nil {
// if err == gorm.ErrRecordNotFound {
// return nil, nil
// }
// return nil, fmt.Errorf("failed to check existing request pickup: %v", err)
// }
// return &request, nil
// }
err := r.DB.Preload("TrashCategory").Where("request_pickup_id = ?", requestPickupId).Find(&items).Error // func (r *requestPickupRepository) UpdateRequestPickup(id string, request *model.RequestPickup) error {
if err != nil { // err := r.DB.Model(&model.RequestPickup{}).Where("id = ?", id).Updates(request).Error
return nil, fmt.Errorf("error fetching request pickup items: %v", err) // if err != nil {
} // return fmt.Errorf("failed to update request pickup: %v", err)
return items, nil // }
}
// return nil
// }
// // func (r *requestPickupRepository) SelectCollectorInRequest(userId string, collectorId string) error {
// // var request model.RequestPickup
// // err := r.DB.Model(&model.RequestPickup{}).
// // Where("user_id = ? AND status_pickup = ? AND request_method = ? AND collector_id IS NULL", userId, "waiting_collector", "manual").
// // First(&request).Error
// // if err != nil {
// // if err == gorm.ErrRecordNotFound {
// // return fmt.Errorf("no matching request pickup found for user %s", userId)
// // }
// // return fmt.Errorf("failed to find request pickup: %v", err)
// // }
// // err = r.DB.Model(&model.RequestPickup{}).
// // Where("id = ?", request.ID).
// // Update("collector_id", collectorId).
// // Error
// // if err != nil {
// // return fmt.Errorf("failed to update collector_id: %v", err)
// // }
// // return nil
// // }
// func (r *requestPickupRepository) DeleteRequestPickup(id string) error {
// if err := r.DB.Where("request_pickup_id = ?", id).Delete(&model.RequestPickupItem{}).Error; err != nil {
// return fmt.Errorf("failed to delete request pickup items: %v", err)
// }
// err := r.DB.Delete(&model.RequestPickup{}, "id = ?", id).Error
// if err != nil {
// return fmt.Errorf("failed to delete request pickup: %v", err)
// }
// return nil
// }
// func (r *requestPickupRepository) GetAutomaticRequestPickupsForCollector() ([]model.RequestPickup, error) {
// var requests []model.RequestPickup
// err := r.DB.Preload("Address").
// Where("request_method = ? AND status_pickup = ? AND collector_id = ?", "otomatis", "waiting_collector", nil).
// Find(&requests).Error
// if err != nil {
// return nil, fmt.Errorf("error fetching pickup requests: %v", err)
// }
// return requests, nil
// }
// func (r *requestPickupRepository) GetManualReqMethodforCollect(collector_id string) ([]model.RequestPickup, error) {
// var requests []model.RequestPickup
// err := r.DB.Where("request_method = ? AND status_pickup = ? AND collector_id = ?", "otomatis", "waiting_collector", collector_id).
// Find(&requests).Error
// if err != nil {
// return nil, fmt.Errorf("error fetching pickup requests: %v", err)
// }
// return requests, nil
// }
// func (r *requestPickupRepository) GetRequestPickupItems(requestPickupId string) ([]model.RequestPickupItem, error) {
// var items []model.RequestPickupItem
// err := r.DB.Preload("TrashCategory").Where("request_pickup_id = ?", requestPickupId).Find(&items).Error
// if err != nil {
// return nil, fmt.Errorf("error fetching request pickup items: %v", err)
// }
// return items, nil
// }

View File

@ -20,6 +20,7 @@ type TrashRepository interface {
FindCategoryId(id string) (*model.TrashCategory, error) FindCategoryId(id string) (*model.TrashCategory, error)
GetTrashCategoryByName(name string) (*model.TrashCategory, error) GetTrashCategoryByName(name string) (*model.TrashCategory, error)
GetTrashDetailByID(id string) (*model.TrashDetail, error) GetTrashDetailByID(id string) (*model.TrashDetail, error)
GetTrashCategoryByID(ctx context.Context, id string) (*model.TrashCategory, error)
GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error) GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error)
UpdateCategoryName(id string, newName string) error UpdateCategoryName(id string, newName string) error
UpdateCategory(id string, updateTrashCategory *model.TrashCategory) (*model.TrashCategory, error) UpdateCategory(id string, updateTrashCategory *model.TrashCategory) (*model.TrashCategory, error)
@ -68,6 +69,15 @@ func (r *trashRepository) GetCategoryByID(id string) (*model.TrashCategory, erro
return &category, nil return &category, nil
} }
// spesial code
func (r *trashRepository) GetTrashCategoryByID(ctx context.Context, id string) (*model.TrashCategory, error) {
var trash model.TrashCategory
if err := config.DB.WithContext(ctx).First(&trash, "id = ?", id).Error; err != nil {
return nil, err
}
return &trash, nil
}
func (r *trashRepository) FindCategoryId(id string) (*model.TrashCategory, error) { func (r *trashRepository) FindCategoryId(id string) (*model.TrashCategory, error) {
var category model.TrashCategory var category model.TrashCategory

View File

@ -0,0 +1,36 @@
package services
import (
"context"
"time"
"rijig/model"
"rijig/internal/repositories"
)
type PickupStatusHistoryService interface {
LogStatusChange(ctx context.Context, requestID, status, changedByID, changedByRole string) error
GetStatusHistory(ctx context.Context, requestID string) ([]model.PickupStatusHistory, error)
}
type pickupStatusHistoryService struct {
repo repositories.PickupStatusHistoryRepository
}
func NewPickupStatusHistoryService(repo repositories.PickupStatusHistoryRepository) PickupStatusHistoryService {
return &pickupStatusHistoryService{repo: repo}
}
func (s *pickupStatusHistoryService) LogStatusChange(ctx context.Context, requestID, status, changedByID, changedByRole string) error {
history := model.PickupStatusHistory{
RequestID: requestID,
Status: status,
ChangedAt: time.Now(),
ChangedByID: changedByID,
ChangedByRole: changedByRole,
}
return s.repo.CreateStatusHistory(ctx, history)
}
func (s *pickupStatusHistoryService) GetStatusHistory(ctx context.Context, requestID string) ([]model.PickupStatusHistory, error) {
return s.repo.GetStatusHistoryByRequestID(ctx, requestID)
}

View File

@ -0,0 +1,146 @@
package services
import (
"context"
"fmt"
"rijig/dto"
"rijig/internal/repositories"
"rijig/utils"
)
type PickupMatchingService interface {
FindNearbyCollectorsForPickup(ctx context.Context, pickupID string) ([]dto.NearbyCollectorDTO, error)
FindAvailableRequestsForCollector(ctx context.Context, collectorID string) ([]dto.PickupRequestForCollectorDTO, error)
}
type pickupMatchingService struct {
pickupRepo repositories.RequestPickupRepository
collectorRepo repositories.CollectorRepository
}
func NewPickupMatchingService(pickupRepo repositories.RequestPickupRepository, collectorRepo repositories.CollectorRepository) PickupMatchingService {
return &pickupMatchingService{
pickupRepo: pickupRepo,
collectorRepo: collectorRepo,
}
}
func (s *pickupMatchingService) FindNearbyCollectorsForPickup(ctx context.Context, pickupID string) ([]dto.NearbyCollectorDTO, error) {
pickup, err := s.pickupRepo.GetPickupWithItemsAndAddress(ctx, pickupID)
if err != nil {
return nil, fmt.Errorf("pickup tidak ditemukan: %w", err)
}
userCoord := utils.Coord{
Lat: pickup.Address.Latitude,
Lon: pickup.Address.Longitude,
}
requestedTrash := make(map[string]bool)
for _, item := range pickup.RequestItems {
requestedTrash[item.TrashCategoryId] = true
}
collectors, err := s.collectorRepo.GetActiveCollectorsWithTrashAndAddress(ctx)
if err != nil {
return nil, fmt.Errorf("gagal mengambil data collector: %w", err)
}
var result []dto.NearbyCollectorDTO
for _, col := range collectors {
coord := utils.Coord{
Lat: col.Address.Latitude,
Lon: col.Address.Longitude,
}
_, km := utils.Distance(userCoord, coord)
if km > 10 {
continue
}
var matchedTrash []string
for _, item := range col.AvaibleTrashByCollector {
if requestedTrash[item.TrashCategoryID] {
matchedTrash = append(matchedTrash, item.TrashCategoryID)
}
}
if len(matchedTrash) == 0 {
continue
}
result = append(result, dto.NearbyCollectorDTO{
CollectorID: col.ID,
Name: col.User.Name,
Phone: col.User.Phone,
Rating: col.Rating,
Latitude: col.Address.Latitude,
Longitude: col.Address.Longitude,
DistanceKm: km,
MatchedTrash: matchedTrash,
})
}
return result, nil
}
// terdpaat error seperti ini: "undefined: dto.PickupRequestForCollectorDTO" dan seprti ini: s.collectorRepo.GetCollectorWithAddressAndTrash undefined (type repositories.CollectorRepository has no field or method GetCollectorWithAddressAndTrash) pada kode berikut:
func (s *pickupMatchingService) FindAvailableRequestsForCollector(ctx context.Context, collectorID string) ([]dto.PickupRequestForCollectorDTO, error) {
collector, err := s.collectorRepo.GetCollectorWithAddressAndTrash(ctx, collectorID)
if err != nil {
return nil, fmt.Errorf("collector tidak ditemukan: %w", err)
}
pickupList, err := s.pickupRepo.GetAllAutomaticRequestsWithAddress(ctx)
if err != nil {
return nil, fmt.Errorf("gagal mengambil pickup otomatis: %w", err)
}
collectorCoord := utils.Coord{
Lat: collector.Address.Latitude,
Lon: collector.Address.Longitude,
}
// map trash collector
collectorTrash := make(map[string]bool)
for _, t := range collector.AvaibleTrashByCollector {
collectorTrash[t.TrashCategoryID] = true
}
var results []dto.PickupRequestForCollectorDTO
for _, p := range pickupList {
if p.StatusPickup != "waiting_collector" {
continue
}
coord := utils.Coord{
Lat: p.Address.Latitude,
Lon: p.Address.Longitude,
}
_, km := utils.Distance(collectorCoord, coord)
if km > 10 {
continue
}
match := false
var matchedTrash []string
for _, item := range p.RequestItems {
if collectorTrash[item.TrashCategoryId] {
match = true
matchedTrash = append(matchedTrash, item.TrashCategoryId)
}
}
if match {
results = append(results, dto.PickupRequestForCollectorDTO{
PickupID: p.ID,
UserID: p.UserId,
Latitude: p.Address.Latitude,
Longitude: p.Address.Longitude,
DistanceKm: km,
MatchedTrash: matchedTrash,
})
}
}
return results, nil
}

View File

@ -0,0 +1,43 @@
package services
import (
"context"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
)
type PickupRatingService interface {
CreateRating(ctx context.Context, userID, pickupID, collectorID string, input dto.CreatePickupRatingDTO) error
GetRatingsByCollector(ctx context.Context, collectorID string) ([]model.PickupRating, error)
GetAverageRating(ctx context.Context, collectorID string) (float32, error)
}
type pickupRatingService struct {
repo repositories.PickupRatingRepository
}
func NewPickupRatingService(repo repositories.PickupRatingRepository) PickupRatingService {
return &pickupRatingService{repo: repo}
}
func (s *pickupRatingService) CreateRating(ctx context.Context, userID, pickupID, collectorID string, input dto.CreatePickupRatingDTO) error {
rating := model.PickupRating{
RequestID: pickupID,
UserID: userID,
CollectorID: collectorID,
Rating: input.Rating,
Feedback: input.Feedback,
CreatedAt: time.Now(),
}
return s.repo.CreateRating(ctx, rating)
}
func (s *pickupRatingService) GetRatingsByCollector(ctx context.Context, collectorID string) ([]model.PickupRating, error) {
return s.repo.GetRatingsByCollector(ctx, collectorID)
}
func (s *pickupRatingService) GetAverageRating(ctx context.Context, collectorID string) (float32, error) {
return s.repo.CalculateAverageRating(ctx, collectorID)
}

View File

@ -0,0 +1,139 @@
package services
import (
"context"
"fmt"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"time"
)
type RequestPickupService interface {
ConvertCartToRequestPickup(ctx context.Context, userID string, req dto.RequestPickupDTO) error
AssignCollectorToRequest(ctx context.Context, pickupID string, req dto.SelectCollectorDTO) error
FindRequestsAssignedToCollector(ctx context.Context, collectorID string) ([]dto.AssignedPickupDTO, error)
ConfirmPickupByCollector(ctx context.Context, pickupID string, confirmedAt time.Time) error
UpdatePickupStatusToPickingUp(ctx context.Context, pickupID string) error
UpdateActualPickupItems(ctx context.Context, pickupID string, items []dto.UpdateRequestPickupItemDTO) error
}
type requestPickupService struct {
trashRepo repositories.TrashRepository
pickupRepo repositories.RequestPickupRepository
cartService CartService
historyRepo repositories.PickupStatusHistoryRepository
}
func NewRequestPickupService(trashRepo repositories.TrashRepository, pickupRepo repositories.RequestPickupRepository, cartService CartService, historyRepo repositories.PickupStatusHistoryRepository) RequestPickupService {
return &requestPickupService{
trashRepo: trashRepo,
pickupRepo: pickupRepo,
cartService: cartService,
historyRepo: historyRepo,
}
}
func (s *requestPickupService) ConvertCartToRequestPickup(ctx context.Context, userID string, req dto.RequestPickupDTO) error {
cart, err := s.cartService.GetCart(ctx, userID)
if err != nil || len(cart.CartItems) == 0 {
return fmt.Errorf("cart kosong atau tidak ditemukan")
}
var requestItems []model.RequestPickupItem
for _, item := range cart.CartItems {
trash, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashID)
if err != nil {
continue
}
subtotal := float64(item.Amount) * trash.EstimatedPrice
requestItems = append(requestItems, model.RequestPickupItem{
TrashCategoryId: item.TrashID,
EstimatedAmount: float64(item.Amount),
EstimatedPricePerKg: trash.EstimatedPrice,
EstimatedSubtotalPrice: subtotal,
})
}
if len(requestItems) == 0 {
return fmt.Errorf("tidak ada item valid dalam cart")
}
pickup := model.RequestPickup{
UserId: userID,
AddressId: req.AddressID,
RequestMethod: req.RequestMethod,
Notes: req.Notes,
StatusPickup: "waiting_collector",
RequestItems: requestItems,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := s.pickupRepo.CreateRequestPickup(ctx, &pickup); err != nil {
return fmt.Errorf("gagal menyimpan request pickup: %w", err)
}
if err := s.cartService.ClearCart(ctx, userID); err != nil {
return fmt.Errorf("request berhasil, tapi gagal hapus cart: %w", err)
}
return nil
}
func (s *requestPickupService) AssignCollectorToRequest(ctx context.Context, pickupID string, req dto.SelectCollectorDTO) error {
if req.CollectorID == "" {
return fmt.Errorf("collector_id tidak boleh kosong")
}
return s.pickupRepo.UpdateCollectorID(ctx, pickupID, req.CollectorID)
}
func (s *requestPickupService) FindRequestsAssignedToCollector(ctx context.Context, collectorID string) ([]dto.AssignedPickupDTO, error) {
pickups, err := s.pickupRepo.GetRequestsAssignedToCollector(ctx, collectorID)
if err != nil {
return nil, err
}
var result []dto.AssignedPickupDTO
for _, p := range pickups {
var matchedTrash []string
for _, item := range p.RequestItems {
matchedTrash = append(matchedTrash, item.TrashCategoryId)
}
result = append(result, dto.AssignedPickupDTO{
PickupID: p.ID,
UserID: p.UserId,
UserName: p.User.Name,
Latitude: p.Address.Latitude,
Longitude: p.Address.Longitude,
Notes: p.Notes,
MatchedTrash: matchedTrash,
})
}
return result, nil
}
func (s *requestPickupService) ConfirmPickupByCollector(ctx context.Context, pickupID string, confirmedAt time.Time) error {
return s.pickupRepo.UpdatePickupStatusAndConfirmationTime(ctx, pickupID, "confirmed_by_collector", confirmedAt)
}
func (s *requestPickupService) UpdatePickupStatusToPickingUp(ctx context.Context, pickupID string) error {
err := s.pickupRepo.UpdatePickupStatus(ctx, pickupID, "collector_are_picking_up")
if err != nil {
return err
}
return s.historyRepo.CreateStatusHistory(ctx, model.PickupStatusHistory{
RequestID: pickupID,
Status: "collector_are_picking_up",
ChangedAt: time.Now(),
ChangedByID: "collector",
ChangedByRole: "collector",
})
}
func (s *requestPickupService) UpdateActualPickupItems(ctx context.Context, pickupID string, items []dto.UpdateRequestPickupItemDTO) error {
return s.pickupRepo.UpdateRequestPickupItemsAmountAndPrice(ctx, pickupID, items)
}

View File

@ -1,349 +1,349 @@
package services package services
import ( // import (
"fmt" // "fmt"
"rijig/dto" // "rijig/dto"
"rijig/internal/repositories" // "rijig/internal/repositories"
"rijig/model" // "rijig/model"
"rijig/utils" // "rijig/utils"
) // )
type RequestPickupService interface { // type RequestPickupService interface {
// CreateRequestPickup(request dto.RequestPickup, UserId string) (*dto.ResponseRequestPickup, error) // // CreateRequestPickup(request dto.RequestPickup, UserId string) (*dto.ResponseRequestPickup, error)
// GetRequestPickupByID(id string) (*dto.ResponseRequestPickup, error) // // GetRequestPickupByID(id string) (*dto.ResponseRequestPickup, error)
// GetAllRequestPickups(userid string) ([]dto.ResponseRequestPickup, error) // // GetAllRequestPickups(userid string) ([]dto.ResponseRequestPickup, error)
// GetRequestPickupsForCollector(collectorId string) ([]dto.ResponseRequestPickup, error) // // GetRequestPickupsForCollector(collectorId string) ([]dto.ResponseRequestPickup, error)
// SelectCollectorInRequest(userId, collectorId string) error // // SelectCollectorInRequest(userId, collectorId string) error
} // }
type requestPickupService struct { // type requestPickupService struct {
repo repositories.RequestPickupRepository // repo repositories.RequestPickupRepository
repoColl repositories.CollectorRepository // repoColl repositories.CollectorRepository
repoAddress repositories.AddressRepository // repoAddress repositories.AddressRepository
repoTrash repositories.TrashRepository // repoTrash repositories.TrashRepository
repoUser repositories.UserProfilRepository // repoUser repositories.UserProfilRepository
} // }
func NewRequestPickupService(repo repositories.RequestPickupRepository, // func NewRequestPickupService(repo repositories.RequestPickupRepository,
repoColl repositories.CollectorRepository, // repoColl repositories.CollectorRepository,
repoAddress repositories.AddressRepository, // repoAddress repositories.AddressRepository,
repoTrash repositories.TrashRepository, // repoTrash repositories.TrashRepository,
repoUser repositories.UserProfilRepository) RequestPickupService { // repoUser repositories.UserProfilRepository) RequestPickupService {
return &requestPickupService{repo: repo, repoColl: repoColl, repoAddress: repoAddress, repoTrash: repoTrash, repoUser: repoUser} // return &requestPickupService{repo: repo, repoColl: repoColl, repoAddress: repoAddress, repoTrash: repoTrash, repoUser: repoUser}
} // }
func (s *requestPickupService) CreateRequestPickup(request dto.RequestPickup, UserId string) (*dto.ResponseRequestPickup, error) { // func (s *requestPickupService) CreateRequestPickup(request dto.RequestPickup, UserId string) (*dto.ResponseRequestPickup, error) {
errors, valid := request.ValidateRequestPickup() // errors, valid := request.ValidateRequestPickup()
if !valid { // if !valid {
return nil, fmt.Errorf("validation errors: %v", errors) // return nil, fmt.Errorf("validation errors: %v", errors)
}
_, err := s.repoAddress.FindAddressByID(request.AddressID)
if err != nil {
return nil, fmt.Errorf("address with ID %s not found", request.AddressID)
}
existingRequest, err := s.repo.FindRequestPickupByAddressAndStatus(UserId, "waiting_collector", "otomatis")
if err != nil {
return nil, fmt.Errorf("error checking for existing request pickup: %v", err)
}
if existingRequest != nil {
return nil, fmt.Errorf("there is already a pending pickup request for address %s", request.AddressID)
}
modelRequest := model.RequestPickup{
UserId: UserId,
AddressId: request.AddressID,
EvidenceImage: request.EvidenceImage,
RequestMethod: request.RequestMethod,
}
err = s.repo.CreateRequestPickup(&modelRequest)
if err != nil {
return nil, fmt.Errorf("failed to create request pickup: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(modelRequest.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(modelRequest.UpdatedAt)
response := &dto.ResponseRequestPickup{
ID: modelRequest.ID,
UserId: UserId,
AddressID: modelRequest.AddressId,
EvidenceImage: modelRequest.EvidenceImage,
StatusPickup: modelRequest.StatusPickup,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
for _, item := range request.RequestItems {
findTrashCategory, err := s.repoTrash.GetCategoryByID(item.TrashCategoryID)
if err != nil {
return nil, fmt.Errorf("trash category with ID %s not found", item.TrashCategoryID)
}
modelItem := model.RequestPickupItem{
RequestPickupId: modelRequest.ID,
TrashCategoryId: findTrashCategory.ID,
EstimatedAmount: item.EstimatedAmount,
}
err = s.repo.CreateRequestPickupItem(&modelItem)
if err != nil {
return nil, fmt.Errorf("failed to create request pickup item: %v", err)
}
response.RequestItems = append(response.RequestItems, dto.ResponseRequestPickupItem{
ID: modelItem.ID,
TrashCategory: []dto.ResponseTrashCategoryDTO{{Name: findTrashCategory.Name, Icon: findTrashCategory.Icon}},
EstimatedAmount: modelItem.EstimatedAmount,
})
}
return response, nil
}
func (s *requestPickupService) GetRequestPickupByID(id string) (*dto.ResponseRequestPickup, error) {
request, err := s.repo.FindRequestPickupByID(id)
if err != nil {
return nil, fmt.Errorf("error fetching request pickup with ID %s: %v", id, err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(request.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(request.UpdatedAt)
response := &dto.ResponseRequestPickup{
ID: request.ID,
UserId: request.UserId,
AddressID: request.AddressId,
EvidenceImage: request.EvidenceImage,
StatusPickup: request.StatusPickup,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return response, nil
}
func (s *requestPickupService) GetAllRequestPickups(userid string) ([]dto.ResponseRequestPickup, error) {
requests, err := s.repo.FindAllRequestPickups(userid)
if err != nil {
return nil, fmt.Errorf("error fetching all request pickups: %v", err)
}
var response []dto.ResponseRequestPickup
for _, request := range requests {
createdAt, _ := utils.FormatDateToIndonesianFormat(request.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(request.UpdatedAt)
response = append(response, dto.ResponseRequestPickup{
ID: request.ID,
UserId: request.UserId,
AddressID: request.AddressId,
EvidenceImage: request.EvidenceImage,
StatusPickup: request.StatusPickup,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
return response, nil
}
// func (s *requestPickupService) GetRequestPickupsForCollector(collectorId string) ([]dto.ResponseRequestPickup, error) {
// requests, err := s.repo.GetAutomaticRequestPickupsForCollector()
// if err != nil {
// return nil, fmt.Errorf("error retrieving automatic pickup requests: %v", err)
// } // }
// var response []dto.ResponseRequestPickup // _, err := s.repoAddress.FindAddressByID(request.AddressID)
// if err != nil {
// return nil, fmt.Errorf("address with ID %s not found", request.AddressID)
// }
// for _, req := range requests { // existingRequest, err := s.repo.FindRequestPickupByAddressAndStatus(UserId, "waiting_collector", "otomatis")
// if err != nil {
// return nil, fmt.Errorf("error checking for existing request pickup: %v", err)
// }
// if existingRequest != nil {
// return nil, fmt.Errorf("there is already a pending pickup request for address %s", request.AddressID)
// }
// collector, err := s.repoColl.FindCollectorById(collectorId) // modelRequest := model.RequestPickup{
// UserId: UserId,
// AddressId: request.AddressID,
// EvidenceImage: request.EvidenceImage,
// RequestMethod: request.RequestMethod,
// }
// err = s.repo.CreateRequestPickup(&modelRequest)
// if err != nil {
// return nil, fmt.Errorf("failed to create request pickup: %v", err)
// }
// createdAt, _ := utils.FormatDateToIndonesianFormat(modelRequest.CreatedAt)
// updatedAt, _ := utils.FormatDateToIndonesianFormat(modelRequest.UpdatedAt)
// response := &dto.ResponseRequestPickup{
// ID: modelRequest.ID,
// UserId: UserId,
// AddressID: modelRequest.AddressId,
// EvidenceImage: modelRequest.EvidenceImage,
// StatusPickup: modelRequest.StatusPickup,
// CreatedAt: createdAt,
// UpdatedAt: updatedAt,
// }
// for _, item := range request.RequestItems {
// findTrashCategory, err := s.repoTrash.GetCategoryByID(item.TrashCategoryID)
// if err != nil { // if err != nil {
// return nil, fmt.Errorf("error fetching collector data: %v", err) // return nil, fmt.Errorf("trash category with ID %s not found", item.TrashCategoryID)
// } // }
// _, distance := utils.Distance( // modelItem := model.RequestPickupItem{
// utils.Coord{Lat: collector.Address.Latitude, Lon: collector.Address.Longitude}, // RequestPickupId: modelRequest.ID,
// utils.Coord{Lat: req.Address.Latitude, Lon: req.Address.Longitude}, // TrashCategoryId: findTrashCategory.ID,
// ) // EstimatedAmount: item.EstimatedAmount,
// if distance <= 20 {
// mappedRequest := dto.ResponseRequestPickup{
// ID: req.ID,
// UserId: req.UserId,
// AddressID: req.AddressId,
// EvidenceImage: req.EvidenceImage,
// StatusPickup: req.StatusPickup,
// CreatedAt: req.CreatedAt.Format("2006-01-02 15:04:05"),
// UpdatedAt: req.UpdatedAt.Format("2006-01-02 15:04:05"),
// }
// user, err := s.repoUser.FindByID(req.UserId)
// if err != nil {
// return nil, fmt.Errorf("error fetching user data: %v", err)
// }
// mappedRequest.User = []dto.UserResponseDTO{
// {
// Name: user.Name,
// Phone: user.Phone,
// },
// }
// address, err := s.repoAddress.FindAddressByID(req.AddressId)
// if err != nil {
// return nil, fmt.Errorf("error fetching address data: %v", err)
// }
// mappedRequest.Address = []dto.AddressResponseDTO{
// {
// District: address.District,
// Village: address.Village,
// Detail: address.Detail,
// },
// }
// requestItems, err := s.repo.GetRequestPickupItems(req.ID)
// if err != nil {
// return nil, fmt.Errorf("error fetching request items: %v", err)
// }
// var mappedRequestItems []dto.ResponseRequestPickupItem
// for _, item := range requestItems {
// trashCategory, err := s.repoTrash.GetCategoryByID(item.TrashCategoryId)
// if err != nil {
// return nil, fmt.Errorf("error fetching trash category: %v", err)
// }
// mappedRequestItems = append(mappedRequestItems, dto.ResponseRequestPickupItem{
// ID: item.ID,
// TrashCategory: []dto.ResponseTrashCategoryDTO{{
// Name: trashCategory.Name,
// Icon: trashCategory.Icon,
// }},
// EstimatedAmount: item.EstimatedAmount,
// })
// }
// mappedRequest.RequestItems = mappedRequestItems
// response = append(response, mappedRequest)
// } // }
// err = s.repo.CreateRequestPickupItem(&modelItem)
// if err != nil {
// return nil, fmt.Errorf("failed to create request pickup item: %v", err)
// }
// response.RequestItems = append(response.RequestItems, dto.ResponseRequestPickupItem{
// ID: modelItem.ID,
// TrashCategory: []dto.ResponseTrashCategoryDTO{{Name: findTrashCategory.Name, Icon: findTrashCategory.Icon}},
// EstimatedAmount: modelItem.EstimatedAmount,
// })
// } // }
// return response, nil // return response, nil
// } // }
// func (s *requestPickupService) GetManualRequestPickupsForCollector(collectorId string) ([]dto.ResponseRequestPickup, error) { // func (s *requestPickupService) GetRequestPickupByID(id string) (*dto.ResponseRequestPickup, error) {
// collector, err := s.repoColl.FindCollectorById(collectorId) // request, err := s.repo.FindRequestPickupByID(id)
// if err != nil { // if err != nil {
// return nil, fmt.Errorf("error fetching collector data: %v", err) // return nil, fmt.Errorf("error fetching request pickup with ID %s: %v", id, err)
// } // }
// requests, err := s.repo.GetManualReqMethodforCollect(collector.ID)
// createdAt, _ := utils.FormatDateToIndonesianFormat(request.CreatedAt)
// updatedAt, _ := utils.FormatDateToIndonesianFormat(request.UpdatedAt)
// response := &dto.ResponseRequestPickup{
// ID: request.ID,
// UserId: request.UserId,
// AddressID: request.AddressId,
// EvidenceImage: request.EvidenceImage,
// StatusPickup: request.StatusPickup,
// CreatedAt: createdAt,
// UpdatedAt: updatedAt,
// }
// return response, nil
// }
// func (s *requestPickupService) GetAllRequestPickups(userid string) ([]dto.ResponseRequestPickup, error) {
// requests, err := s.repo.FindAllRequestPickups(userid)
// if err != nil { // if err != nil {
// return nil, fmt.Errorf("error retrieving manual pickup requests: %v", err) // return nil, fmt.Errorf("error fetching all request pickups: %v", err)
// } // }
// var response []dto.ResponseRequestPickup // var response []dto.ResponseRequestPickup
// for _, request := range requests {
// for _, req := range requests { // createdAt, _ := utils.FormatDateToIndonesianFormat(request.CreatedAt)
// updatedAt, _ := utils.FormatDateToIndonesianFormat(request.UpdatedAt)
// createdAt, _ := utils.FormatDateToIndonesianFormat(req.CreatedAt) // response = append(response, dto.ResponseRequestPickup{
// updatedAt, _ := utils.FormatDateToIndonesianFormat(req.UpdatedAt) // ID: request.ID,
// UserId: request.UserId,
// mappedRequest := dto.ResponseRequestPickup{ // AddressID: request.AddressId,
// ID: req.ID, // EvidenceImage: request.EvidenceImage,
// UserId: req.UserId, // StatusPickup: request.StatusPickup,
// AddressID: req.AddressId,
// EvidenceImage: req.EvidenceImage,
// StatusPickup: req.StatusPickup,
// CreatedAt: createdAt, // CreatedAt: createdAt,
// UpdatedAt: updatedAt, // UpdatedAt: updatedAt,
// } // })
// user, err := s.repoUser.FindByID(req.UserId)
// if err != nil {
// return nil, fmt.Errorf("error fetching user data: %v", err)
// }
// mappedRequest.User = []dto.UserResponseDTO{
// {
// Name: user.Name,
// Phone: user.Phone,
// },
// }
// address, err := s.repoAddress.FindAddressByID(req.AddressId)
// if err != nil {
// return nil, fmt.Errorf("error fetching address data: %v", err)
// }
// mappedRequest.Address = []dto.AddressResponseDTO{
// {
// District: address.District,
// Village: address.Village,
// Detail: address.Detail,
// },
// }
// requestItems, err := s.repo.GetRequestPickupItems(req.ID)
// if err != nil {
// return nil, fmt.Errorf("error fetching request items: %v", err)
// }
// var mappedRequestItems []dto.ResponseRequestPickupItem
// for _, item := range requestItems {
// trashCategory, err := s.repoTrash.GetCategoryByID(item.TrashCategoryId)
// if err != nil {
// return nil, fmt.Errorf("error fetching trash category: %v", err)
// }
// mappedRequestItems = append(mappedRequestItems, dto.ResponseRequestPickupItem{
// ID: item.ID,
// TrashCategory: []dto.ResponseTrashCategoryDTO{{
// Name: trashCategory.Name,
// Icon: trashCategory.Icon,
// }},
// EstimatedAmount: item.EstimatedAmount,
// })
// }
// mappedRequest.RequestItems = mappedRequestItems
// response = append(response, mappedRequest)
// } // }
// return response, nil // return response, nil
// } // }
// func (s *requestPickupService) SelectCollectorInRequest(userId, collectorId string) error { // // func (s *requestPickupService) GetRequestPickupsForCollector(collectorId string) ([]dto.ResponseRequestPickup, error) {
// // requests, err := s.repo.GetAutomaticRequestPickupsForCollector()
// // if err != nil {
// // return nil, fmt.Errorf("error retrieving automatic pickup requests: %v", err)
// // }
// request, err := s.repo.FindRequestPickupByStatus(userId, "waiting_collector", "manual") // // var response []dto.ResponseRequestPickup
// if err != nil {
// return fmt.Errorf("request pickup not found: %v", err)
// }
// if request.StatusPickup != "waiting_collector" && request.RequestMethod != "manual" { // // for _, req := range requests {
// return fmt.Errorf("pickup request is not in 'waiting_collector' status and not 'manual' method")
// }
// collector, err := s.repoColl.FindCollectorByIdWithoutAddr(collectorId) // // collector, err := s.repoColl.FindCollectorById(collectorId)
// if err != nil { // // if err != nil {
// return fmt.Errorf("collector tidak ditemukan: %v", err) // // return nil, fmt.Errorf("error fetching collector data: %v", err)
// } // // }
// request.CollectorID = &collector.ID // // _, distance := utils.Distance(
// // utils.Coord{Lat: collector.Address.Latitude, Lon: collector.Address.Longitude},
// // utils.Coord{Lat: req.Address.Latitude, Lon: req.Address.Longitude},
// // )
// err = s.repo.UpdateRequestPickup(request.ID, request) // // if distance <= 20 {
// if err != nil {
// return fmt.Errorf("failed to update request pickup: %v", err)
// }
// return nil // // mappedRequest := dto.ResponseRequestPickup{
// } // // ID: req.ID,
// // UserId: req.UserId,
// // AddressID: req.AddressId,
// // EvidenceImage: req.EvidenceImage,
// // StatusPickup: req.StatusPickup,
// // CreatedAt: req.CreatedAt.Format("2006-01-02 15:04:05"),
// // UpdatedAt: req.UpdatedAt.Format("2006-01-02 15:04:05"),
// // }
// // user, err := s.repoUser.FindByID(req.UserId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching user data: %v", err)
// // }
// // mappedRequest.User = []dto.UserResponseDTO{
// // {
// // Name: user.Name,
// // Phone: user.Phone,
// // },
// // }
// // address, err := s.repoAddress.FindAddressByID(req.AddressId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching address data: %v", err)
// // }
// // mappedRequest.Address = []dto.AddressResponseDTO{
// // {
// // District: address.District,
// // Village: address.Village,
// // Detail: address.Detail,
// // },
// // }
// // requestItems, err := s.repo.GetRequestPickupItems(req.ID)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching request items: %v", err)
// // }
// // var mappedRequestItems []dto.ResponseRequestPickupItem
// // for _, item := range requestItems {
// // trashCategory, err := s.repoTrash.GetCategoryByID(item.TrashCategoryId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching trash category: %v", err)
// // }
// // mappedRequestItems = append(mappedRequestItems, dto.ResponseRequestPickupItem{
// // ID: item.ID,
// // TrashCategory: []dto.ResponseTrashCategoryDTO{{
// // Name: trashCategory.Name,
// // Icon: trashCategory.Icon,
// // }},
// // EstimatedAmount: item.EstimatedAmount,
// // })
// // }
// // mappedRequest.RequestItems = mappedRequestItems
// // response = append(response, mappedRequest)
// // }
// // }
// // return response, nil
// // }
// // func (s *requestPickupService) GetManualRequestPickupsForCollector(collectorId string) ([]dto.ResponseRequestPickup, error) {
// // collector, err := s.repoColl.FindCollectorById(collectorId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching collector data: %v", err)
// // }
// // requests, err := s.repo.GetManualReqMethodforCollect(collector.ID)
// // if err != nil {
// // return nil, fmt.Errorf("error retrieving manual pickup requests: %v", err)
// // }
// // var response []dto.ResponseRequestPickup
// // for _, req := range requests {
// // createdAt, _ := utils.FormatDateToIndonesianFormat(req.CreatedAt)
// // updatedAt, _ := utils.FormatDateToIndonesianFormat(req.UpdatedAt)
// // mappedRequest := dto.ResponseRequestPickup{
// // ID: req.ID,
// // UserId: req.UserId,
// // AddressID: req.AddressId,
// // EvidenceImage: req.EvidenceImage,
// // StatusPickup: req.StatusPickup,
// // CreatedAt: createdAt,
// // UpdatedAt: updatedAt,
// // }
// // user, err := s.repoUser.FindByID(req.UserId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching user data: %v", err)
// // }
// // mappedRequest.User = []dto.UserResponseDTO{
// // {
// // Name: user.Name,
// // Phone: user.Phone,
// // },
// // }
// // address, err := s.repoAddress.FindAddressByID(req.AddressId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching address data: %v", err)
// // }
// // mappedRequest.Address = []dto.AddressResponseDTO{
// // {
// // District: address.District,
// // Village: address.Village,
// // Detail: address.Detail,
// // },
// // }
// // requestItems, err := s.repo.GetRequestPickupItems(req.ID)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching request items: %v", err)
// // }
// // var mappedRequestItems []dto.ResponseRequestPickupItem
// // for _, item := range requestItems {
// // trashCategory, err := s.repoTrash.GetCategoryByID(item.TrashCategoryId)
// // if err != nil {
// // return nil, fmt.Errorf("error fetching trash category: %v", err)
// // }
// // mappedRequestItems = append(mappedRequestItems, dto.ResponseRequestPickupItem{
// // ID: item.ID,
// // TrashCategory: []dto.ResponseTrashCategoryDTO{{
// // Name: trashCategory.Name,
// // Icon: trashCategory.Icon,
// // }},
// // EstimatedAmount: item.EstimatedAmount,
// // })
// // }
// // mappedRequest.RequestItems = mappedRequestItems
// // response = append(response, mappedRequest)
// // }
// // return response, nil
// // }
// // func (s *requestPickupService) SelectCollectorInRequest(userId, collectorId string) error {
// // request, err := s.repo.FindRequestPickupByStatus(userId, "waiting_collector", "manual")
// // if err != nil {
// // return fmt.Errorf("request pickup not found: %v", err)
// // }
// // if request.StatusPickup != "waiting_collector" && request.RequestMethod != "manual" {
// // return fmt.Errorf("pickup request is not in 'waiting_collector' status and not 'manual' method")
// // }
// // collector, err := s.repoColl.FindCollectorByIdWithoutAddr(collectorId)
// // if err != nil {
// // return fmt.Errorf("collector tidak ditemukan: %v", err)
// // }
// // request.CollectorID = &collector.ID
// // err = s.repo.UpdateRequestPickup(request.ID, request)
// // if err != nil {
// // return fmt.Errorf("failed to update request pickup: %v", err)
// // }
// // return nil
// // }

View File

@ -0,0 +1,12 @@
package model
import "time"
type PickupStatusHistory struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
RequestID string `gorm:"not null" json:"request_id"`
Status string `gorm:"not null" json:"status"`
ChangedAt time.Time `gorm:"not null" json:"changed_at"`
ChangedByID string `gorm:"not null" json:"changed_by_id"`
ChangedByRole string `gorm:"not null" json:"changed_by_role"`
}

13
model/rating_model.go Normal file
View File

@ -0,0 +1,13 @@
package model
import "time"
type PickupRating struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
RequestID string `gorm:"not null;unique" json:"request_id"`
UserID string `gorm:"not null" json:"user_id"`
CollectorID string `gorm:"not null" json:"collector_id"`
Rating float32 `gorm:"not null" json:"rating"`
Feedback string `json:"feedback,omitempty"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
}

View File

@ -7,28 +7,30 @@ import (
type RequestPickup struct { type RequestPickup struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"` ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
UserId string `gorm:"not null" json:"user_id"` UserId string `gorm:"not null" json:"user_id"`
User User `gorm:"foreignKey:UserId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"user"` User *User `gorm:"foreignKey:UserId" json:"user"`
AddressId string `gorm:"not null" json:"address_id"` AddressId string `gorm:"not null" json:"address_id"`
Address Address `gorm:"foreignKey:AddressId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"address"` Address *Address `gorm:"foreignKey:AddressId" json:"address"`
RequestItems []RequestPickupItem `gorm:"foreignKey:RequestPickupId;constraint:OnDelete:CASCADE;" json:"request_items"` RequestItems []RequestPickupItem `gorm:"foreignKey:RequestPickupId;constraint:OnDelete:CASCADE;" json:"request_items"`
EvidenceImage string `json:"evidence_image"`
Notes string `json:"notes"` Notes string `json:"notes"`
StatusPickup string `gorm:"default:'waiting_collector'" json:"status_pickup"` StatusPickup string `gorm:"default:'waiting_collector'" json:"status_pickup"`
CollectorID *string `gorm:"type:uuid" json:"collector_id,omitempty"` CollectorID *string `gorm:"type:uuid" json:"collector_id,omitempty"`
Collector Collector `gorm:"foreignKey:CollectorID;constraint:OnDelete:CASCADE;" json:"collector"` Collector *Collector `gorm:"foreignKey:CollectorID;references:ID" json:"collector,omitempty"`
ConfirmedByCollectorAt *time.Time `json:"confirmed_by_collector_at,omitempty"` ConfirmedByCollectorAt *time.Time `json:"confirmed_by_collector_at,omitempty"`
RequestMethod string `gorm:"not null" json:"request_method"` RequestMethod string `gorm:"not null" json:"request_method"` // manual / otomatis
CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` FinalPrice float64 `json:"final_price"` // diisi setelah collector update berat
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
} }
type RequestPickupItem struct { type RequestPickupItem struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"` ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
RequestPickupId string `gorm:"not null" json:"request_pickup_id"` RequestPickupId string `gorm:"not null" json:"request_pickup_id"`
RequestPickup RequestPickup `gorm:"foreignKey:RequestPickupId;constraint:OnDelete:CASCADE;"` RequestPickup *RequestPickup `gorm:"foreignKey:RequestPickupId" json:"-"`
TrashCategoryId string `gorm:"not null" json:"trash_category_id"` TrashCategoryId string `gorm:"not null" json:"trash_category_id"`
TrashCategory TrashCategory `gorm:"foreignKey:TrashCategoryId;constraint:OnDelete:CASCADE;" json:"trash_category"` TrashCategory *TrashCategory `gorm:"foreignKey:TrashCategoryId" json:"trash_category"`
EstimatedAmount float64 `gorm:"not null" json:"estimated_amount"` EstimatedAmount float64 `gorm:"not null" json:"estimated_amount"`
EstimatedPricePerKg float64 `gorm:"not null" json:"estimated_price_per_kg"` // harga pada saat itu
EstimatedSubtotalPrice float64 `gorm:"not null" json:"estimated_subtotal_price"` // amount * price per kg
} }
// request_method { // request_method {

View File

@ -30,8 +30,15 @@ func CollectorRouter(api fiber.Router) {
// Middleware Auth dan Role // Middleware Auth dan Role
// Inisialisasi repository dan service // Inisialisasi repository dan service
collectorRepo := repositories.NewCollectorRepository() pickupRepo := repositories.NewRequestPickupRepository()
trashRepo := repositories.NewTrashRepository(config.DB) trashRepo := repositories.NewTrashRepository(config.DB)
historyRepo := repositories.NewPickupStatusHistoryRepository()
cartService := services.NewCartService()
pickupService := services.NewRequestPickupService(trashRepo, pickupRepo, cartService, historyRepo)
pickupHandler := handler.NewRequestPickupHandler(pickupService)
collectorRepo := repositories.NewCollectorRepository()
// trashRepo := repositories.NewTrashRepository(config.DB)
collectorService := services.NewCollectorService(collectorRepo, trashRepo) collectorService := services.NewCollectorService(collectorRepo, trashRepo)
collectorHandler := handler.NewCollectorHandler(collectorService) collectorHandler := handler.NewCollectorHandler(collectorService)
@ -43,7 +50,9 @@ func CollectorRouter(api fiber.Router) {
collectors.Post("/", collectorHandler.CreateCollector) // Create collector collectors.Post("/", collectorHandler.CreateCollector) // Create collector
collectors.Post("/:id/trash", collectorHandler.AddTrashToCollector) // Add trash to collector collectors.Post("/:id/trash", collectorHandler.AddTrashToCollector) // Add trash to collector
collectors.Get("/:id", collectorHandler.GetCollectorByID) // Get collector by ID collectors.Get("/:id", collectorHandler.GetCollectorByID) // Get collector by ID
collectors.Get("/", collectorHandler.GetCollectorByUserID) // Get collector by ID collectors.Get("/", collectorHandler.GetCollectorByUserID)
collectors.Get("/pickup/assigned-to-me", pickupHandler.GetAssignedPickup)
// Get collector by ID
collectors.Patch("/:id", collectorHandler.UpdateCollector) // Update collector fields collectors.Patch("/:id", collectorHandler.UpdateCollector) // Update collector fields
collectors.Patch("/:id/trash", collectorHandler.UpdateTrash) collectors.Patch("/:id/trash", collectorHandler.UpdateTrash)
collectors.Patch("/:id/job-status", collectorHandler.UpdateJobStatus) collectors.Patch("/:id/job-status", collectorHandler.UpdateJobStatus)

View File

@ -0,0 +1,25 @@
package presentation
import (
"rijig/internal/handler"
"rijig/internal/repositories"
"rijig/internal/services"
"rijig/middleware"
"github.com/gofiber/fiber/v2"
)
func PickupMatchingRouter(api fiber.Router) {
pickupRepo := repositories.NewRequestPickupRepository()
collectorRepo := repositories.NewCollectorRepository()
service := services.NewPickupMatchingService(pickupRepo, collectorRepo)
handler := handler.NewPickupMatchingHandler(service)
manual := api.Group("/pickup/manual")
manual.Use(middleware.AuthMiddleware)
manual.Get("/:pickupID/nearby-collectors", handler.GetNearbyCollectorsForPickup)
auto := api.Group("/pickup/otomatis")
auto.Use(middleware.AuthMiddleware)
auto.Get("/available-requests", handler.GetAvailablePickupForCollector)
}

View File

@ -0,0 +1,24 @@
package presentation
import (
"rijig/internal/handler"
"rijig/internal/repositories"
"rijig/internal/services"
"rijig/middleware"
"github.com/gofiber/fiber/v2"
)
func PickupRatingRouter(api fiber.Router) {
ratingRepo := repositories.NewPickupRatingRepository()
ratingService := services.NewPickupRatingService(ratingRepo)
ratingHandler := handler.NewPickupRatingHandler(ratingService)
rating := api.Group("/pickup")
rating.Use(middleware.AuthMiddleware)
rating.Post("/:id/rating", ratingHandler.CreateRating)
collector := api.Group("/collector")
collector.Get("/:id/ratings", ratingHandler.GetRatingsByCollector)
collector.Get("/:id/ratings/average", ratingHandler.GetAverageRating)
}

View File

@ -0,0 +1,33 @@
package presentation
import (
"rijig/config"
"rijig/internal/handler"
"rijig/internal/repositories"
"rijig/internal/services"
"rijig/middleware"
"github.com/gofiber/fiber/v2"
)
func RequestPickupRouter(api fiber.Router) {
pickupRepo := repositories.NewRequestPickupRepository()
historyRepo := repositories.NewPickupStatusHistoryRepository()
trashRepo := repositories.NewTrashRepository(config.DB)
cartService := services.NewCartService()
historyService := services.NewPickupStatusHistoryService(historyRepo)
pickupService := services.NewRequestPickupService(trashRepo, pickupRepo, cartService, historyRepo)
pickupHandler := handler.NewRequestPickupHandler(pickupService)
statuspickupHandler := handler.NewPickupStatusHistoryHandler(historyService)
reqpickup := api.Group("/reqpickup")
reqpickup.Use(middleware.AuthMiddleware)
reqpickup.Post("/manual", pickupHandler.CreateRequestPickup)
reqpickup.Get("/pickup/:id/history", statuspickupHandler.GetStatusHistory)
reqpickup.Post("/otomatis", pickupHandler.CreateRequestPickup)
reqpickup.Put("/:id/select-collector", pickupHandler.SelectCollector)
reqpickup.Put("/pickup/:id/status", pickupHandler.UpdatePickupStatus)
reqpickup.Put("/pickup/:id/item/update-actual", pickupHandler.UpdatePickupItemActualAmount)
}

View File

@ -26,7 +26,10 @@ func SetupRoutes(app *fiber.App) {
// || auth router || // // || auth router || //
presentation.IdentityCardRouter(api) presentation.IdentityCardRouter(api)
presentation.CompanyProfileRouter(api) presentation.CompanyProfileRouter(api)
// presentation.RequestPickupRouter(api) presentation.RequestPickupRouter(api)
presentation.PickupMatchingRouter(api)
presentation.PickupRatingRouter(api)
presentation.CollectorRouter(api) presentation.CollectorRouter(api)
presentation.TrashCartRouter(api) presentation.TrashCartRouter(api)