feat: add feature request pickup(uncompleted yet)

This commit is contained in:
pahmiudahgede 2025-05-07 21:54:58 +07:00
parent 1c0636bba4
commit 02db7d5dca
8 changed files with 537 additions and 0 deletions

View File

@ -47,7 +47,12 @@ func ConnectDatabase() {
&model.Address{},
&model.IdentityCard{},
&model.CompanyProfile{},
// =>user preparation<=
// =>requestpickup preparation<=
&model.RequestPickup{},
&model.RequestPickupItem{},
// =>requestpickup preparation<=
// =>store preparation<=
&model.Store{},

80
dto/requestpickup_dto.go Normal file
View File

@ -0,0 +1,80 @@
package dto
import (
"fmt"
"strings"
)
type RequestPickup struct {
RequestItems []RequestPickupItem `json:"request_items"`
EvidenceImage string `json:"evidence_image"`
AddressID string `json:"address_id"`
}
type RequestPickupItem struct {
TrashCategoryID string `json:"trash_category_id"`
EstimatedAmount float64 `json:"estimated_amount"`
}
type ResponseRequestPickup struct {
ID string `json:"id"`
UserId string `json:"user_id"`
AddressID string `json:"address_id"`
EvidenceImage string `json:"evidence_image"`
StatusPickup string `json:"status_pickup"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
RequestItems []ResponseRequestPickupItem `json:"request_items"`
}
type ResponseRequestPickupItem struct {
ID string `json:"id"`
// TrashCategoryID string `json:"trash_category_id"`
TrashCategoryName string `json:"trash_category_name"`
EstimatedAmount float64 `json:"estimated_amount"`
}
func (r *RequestPickup) ValidateRequestPickup() (map[string][]string, bool) {
errors := make(map[string][]string)
if len(r.RequestItems) == 0 {
errors["request_items"] = append(errors["request_items"], "At least one item must be provided")
}
if strings.TrimSpace(r.AddressID) == "" {
errors["address_id"] = append(errors["address_id"], "Address ID must be provided")
}
for i, item := range r.RequestItems {
itemErrors, valid := item.ValidateRequestPickupItem(i)
if !valid {
for field, msgs := range itemErrors {
errors[field] = append(errors[field], msgs...)
}
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *RequestPickupItem) ValidateRequestPickupItem(index int) (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.TrashCategoryID) == "" {
errors["trash_category_id"] = append(errors["trash_category_id"], fmt.Sprintf("Trash category ID cannot be empty (Item %d)", index+1))
}
if r.EstimatedAmount < 2 {
errors["estimated_amount"] = append(errors["estimated_amount"], fmt.Sprintf("Estimated amount must be >= 2.0 kg (Item %d)", index+1))
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -0,0 +1,101 @@
package handler
import (
"fmt"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
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, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
var request dto.RequestPickup
if err := c.BodyParser(&request); err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
}
errors, valid := request.ValidateRequestPickup()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
response, err := h.service.CreateRequestPickup(request, userID)
if err != nil {
return utils.InternalServerErrorResponse(c, fmt.Sprintf("Error creating request pickup: %v", err))
}
return utils.SuccessResponse(c, response, "Request pickup created successfully")
}
func (h *RequestPickupHandler) GetRequestPickupByID(c *fiber.Ctx) error {
id := c.Params("id")
response, err := h.service.GetRequestPickupByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, fmt.Sprintf("Request pickup with ID %s not found: %v", id, err))
}
return utils.SuccessResponse(c, response, "Request pickup retrieved successfully")
}
func (h *RequestPickupHandler) GetAllRequestPickups(c *fiber.Ctx) error {
response, err := h.service.GetAllRequestPickups()
if err != nil {
return utils.InternalServerErrorResponse(c, fmt.Sprintf("Error fetching all request pickups: %v", err))
}
return utils.SuccessResponse(c, response, "All request pickups retrieved successfully")
}
func (h *RequestPickupHandler) UpdateRequestPickup(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
id := c.Params("id")
var request dto.RequestPickup
if err := c.BodyParser(&request); err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
}
errors, valid := request.ValidateRequestPickup()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
response, err := h.service.UpdateRequestPickup(id, request)
if err != nil {
return utils.InternalServerErrorResponse(c, fmt.Sprintf("Error updating request pickup: %v", err))
}
return utils.SuccessResponse(c, response, "Request pickup updated successfully")
}
func (h *RequestPickupHandler) DeleteRequestPickup(c *fiber.Ctx) error {
id := c.Params("id")
err := h.service.DeleteRequestPickup(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, fmt.Sprintf("Request pickup with ID %s not found: %v", id, err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Request pickup deleted successfully")
}

View File

@ -0,0 +1,114 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type RequestPickupRepository interface {
CreateRequestPickup(request *model.RequestPickup) error
CreateRequestPickupItem(item *model.RequestPickupItem) error
FindRequestPickupByID(id string) (*model.RequestPickup, error)
FindAllRequestPickups() ([]model.RequestPickup, error)
UpdateRequestPickup(id string, request *model.RequestPickup) error
DeleteRequestPickup(id string) error
FindRequestPickupByAddressAndCategory(addressID string, trashCategoryID string) (*model.RequestPickup, error)
FindRequestPickupByAddressAndStatus(userId, status string) (*model.RequestPickup, error)
}
type requestPickupRepository struct {
DB *gorm.DB
}
func NewRequestPickupRepository(db *gorm.DB) RequestPickupRepository {
return &requestPickupRepository{DB: db}
}
func (r *requestPickupRepository) CreateRequestPickup(request *model.RequestPickup) error {
if err := r.DB.Create(request).Error; err != nil {
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() ([]model.RequestPickup, error) {
var requests []model.RequestPickup
err := r.DB.Preload("RequestItems").Find(&requests).Error
if err != nil {
return nil, fmt.Errorf("failed to fetch all request pickups: %v", err)
}
return requests, 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) 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) FindRequestPickupByAddressAndCategory(addressID string, trashCategoryID string) (*model.RequestPickup, error) {
var request model.RequestPickup
err := r.DB.Joins("JOIN request_pickup_items ON request_pickups.id = request_pickup_items.request_pickup_id").
Where("request_pickups.address_id = ? AND request_pickup_items.trash_category_id = ?", addressID, trashCategoryID).
First(&request).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, fmt.Errorf("error checking request pickup for address %s and category %s: %v", addressID, trashCategoryID, err)
}
return &request, nil
}
func (r *requestPickupRepository) FindRequestPickupByAddressAndStatus(userId, status string) (*model.RequestPickup, error) {
var request model.RequestPickup
err := r.DB.Where("user_id = ? AND status_pickup = ?", userId, status).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
}

View File

@ -0,0 +1,181 @@
package services
import (
"fmt"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
)
type RequestPickupService interface {
CreateRequestPickup(request dto.RequestPickup, UserId string) (*dto.ResponseRequestPickup, error)
GetRequestPickupByID(id string) (*dto.ResponseRequestPickup, error)
GetAllRequestPickups() ([]dto.ResponseRequestPickup, error)
UpdateRequestPickup(id string, request dto.RequestPickup) (*dto.ResponseRequestPickup, error)
DeleteRequestPickup(id string) error
}
type requestPickupService struct {
repo repositories.RequestPickupRepository
repoAddress repositories.AddressRepository
repoTrash repositories.TrashRepository
}
func NewRequestPickupService(repo repositories.RequestPickupRepository,
repoAddress repositories.AddressRepository,
repoTrash repositories.TrashRepository) RequestPickupService {
return &requestPickupService{repo: repo, repoAddress: repoAddress, repoTrash: repoTrash}
}
func (s *requestPickupService) CreateRequestPickup(request dto.RequestPickup, UserId string) (*dto.ResponseRequestPickup, error) {
errors, valid := request.ValidateRequestPickup()
if !valid {
return nil, fmt.Errorf("validation errors: %v", errors)
}
findAddress, 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_pengepul")
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: findAddress.ID,
EvidenceImage: request.EvidenceImage,
}
err = s.repo.CreateRequestPickup(&modelRequest)
if err != nil {
return nil, fmt.Errorf("failed to create request pickup: %v", err)
}
response := &dto.ResponseRequestPickup{
ID: modelRequest.ID,
UserId: UserId,
AddressID: modelRequest.AddressId,
EvidenceImage: modelRequest.EvidenceImage,
StatusPickup: modelRequest.StatusPickup,
CreatedAt: modelRequest.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: modelRequest.UpdatedAt.Format("2006-01-02 15:04:05"),
}
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,
TrashCategoryName: findTrashCategory.Name,
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)
}
response := &dto.ResponseRequestPickup{
ID: request.ID,
UserId: request.UserId,
AddressID: request.AddressId,
EvidenceImage: request.EvidenceImage,
StatusPickup: request.StatusPickup,
CreatedAt: request.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: request.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return response, nil
}
func (s *requestPickupService) GetAllRequestPickups() ([]dto.ResponseRequestPickup, error) {
requests, err := s.repo.FindAllRequestPickups()
if err != nil {
return nil, fmt.Errorf("error fetching all request pickups: %v", err)
}
var response []dto.ResponseRequestPickup
for _, request := range requests {
response = append(response, dto.ResponseRequestPickup{
ID: request.ID,
UserId: request.UserId,
AddressID: request.AddressId,
EvidenceImage: request.EvidenceImage,
StatusPickup: request.StatusPickup,
CreatedAt: request.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: request.UpdatedAt.Format("2006-01-02 15:04:05"),
})
}
return response, nil
}
func (s *requestPickupService) UpdateRequestPickup(id string, request dto.RequestPickup) (*dto.ResponseRequestPickup, error) {
errors, valid := request.ValidateRequestPickup()
if !valid {
return nil, fmt.Errorf("validation errors: %v", errors)
}
existingRequest, err := s.repo.FindRequestPickupByID(id)
if err != nil {
return nil, fmt.Errorf("request pickup with ID %s not found: %v", id, err)
}
existingRequest.EvidenceImage = request.EvidenceImage
err = s.repo.UpdateRequestPickup(id, existingRequest)
if err != nil {
return nil, fmt.Errorf("failed to update request pickup: %v", err)
}
response := &dto.ResponseRequestPickup{
ID: existingRequest.ID,
UserId: existingRequest.UserId,
AddressID: existingRequest.AddressId,
EvidenceImage: existingRequest.EvidenceImage,
StatusPickup: existingRequest.StatusPickup,
CreatedAt: existingRequest.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: existingRequest.UpdatedAt.Format("2006-01-02 15:04:05"),
}
return response, nil
}
func (s *requestPickupService) DeleteRequestPickup(id string) error {
err := s.repo.DeleteRequestPickup(id)
if err != nil {
return fmt.Errorf("failed to delete request pickup with ID %s: %v", id, err)
}
return nil
}

View File

@ -0,0 +1,24 @@
package model
import (
"time"
)
type RequestPickup struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
UserId string `gorm:"not null" json:"user_id"`
AddressId string `gorm:"not null" json:"address_id"`
RequestItems []RequestPickupItem `gorm:"foreignKey:RequestPickupId;constraint:OnDelete:CASCADE;" json:"request_items"`
EvidenceImage string `json:"evidence_image"`
StatusPickup string `gorm:"default:'waiting_pengepul'" json:"status_pickup"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
}
type RequestPickupItem struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
RequestPickupId string `gorm:"not null" json:"request_pickup_id"`
TrashCategoryId string `gorm:"not null" json:"trash_category_id"`
TrashDetailId string `json:"trash_detail_id,omitempty"`
EstimatedAmount float64 `gorm:"not null" json:"estimated_amount"`
}

View File

@ -0,0 +1,31 @@
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) {
requestRepo := repositories.NewRequestPickupRepository(config.DB)
repoTrash := repositories.NewTrashRepository(config.DB)
repoAddress := repositories.NewAddressRepository(config.DB)
requestPickupServices := services.NewRequestPickupService(requestRepo, repoAddress, repoTrash)
requestPickupHandler := handler.NewRequestPickupHandler(requestPickupServices)
requestPickupAPI := api.Group("/requestpickup")
requestPickupAPI.Use(middleware.AuthMiddleware)
requestPickupAPI.Post("/", requestPickupHandler.CreateRequestPickup)
// requestPickupAPI.Get("/:id", requestPickupHandler.GetRequestPickupByID)
// requestPickupAPI.Get("/", requestPickupHandler.GetAllRequestPickups)
// requestPickupAPI.Put("/:id", requestPickupHandler.UpdateRequestPickup)
// requestPickupAPI.Delete("/:id", requestPickupHandler.DeleteRequestPickup)
}

View File

@ -25,6 +25,7 @@ func SetupRoutes(app *fiber.App) {
// || auth router || //
presentation.IdentityCardRouter(api)
presentation.CompanyProfileRouter(api)
presentation.RequestPickupRouter(api)
presentation.UserProfileRouter(api)
presentation.UserPinRouter(api)