feat: setup docker

This commit is contained in:
pahmiudahgede 2025-06-09 06:03:43 +07:00
parent e7c0675f8a
commit d8b43348b3
125 changed files with 2826 additions and 12794 deletions

71
.dockerignore Normal file
View File

@ -0,0 +1,71 @@
# Git
.git
.gitignore
README.md
.gitattributes
# Documentation
*.md
docs/
# Environment files (kecuali yang diperlukan)
.env
.env.local
.env.example
# Kita tetap include .env.dev dan .env.docker untuk development
# Logs
*.log
logs/
# Dependencies
vendor/
# Test files
*_test.go
testdata/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Temporary files
*.tmp
*.temp
tmp/
# Build artifacts (untuk production)
main
*.exe
# Docker
Dockerfile
docker-compose*.yml
.dockerignore
# Air specific
.air.toml
tmp/
*_templ.go
# Coverage
*.out
coverage.html
# Database
*.db
*.sqlite
*.sqlite3
# Public uploads (jika ada)
public/uploads/
# Makefile
Makefile

61
.gitignore vendored
View File

@ -1,6 +1,3 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
@ -14,17 +11,61 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Dependency directories
vendor/
# Go workspace file
go.work
go.work.sum
# env file
.env
.env.prod
.env.dev
# Environment files - ignore all variations
.env*
!.env.example
# Ignore public uploads
# Logs
*.log
logs/
# Temporary files
tmp/
*.tmp
*.temp
# IDE/Editor files
.vscode/
.idea/
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Air live reload tool
.air.toml
# Public uploads - user generated content
/public/apirijig/v2/uploads/
# Build outputs
/bin/
/build/
/dist/
# Coverage reports
coverage.txt
coverage.html
*.cover
# Debug files
debug
*.pprof
# Local development files
*.local

28
Dockerfile.dev Normal file
View File

@ -0,0 +1,28 @@
# Dockerfile untuk development environment dengan Air hot reload
FROM golang:1.23-alpine
# Install dependencies dan Air
RUN apk add --no-cache git ca-certificates curl && \
go install github.com/cosmtrek/air@latest
# Set working directory
WORKDIR /app
# Copy go mod files dan download dependencies
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Create tmp directory untuk Air
RUN mkdir -p tmp
# Set timezone (optional)
RUN cp /usr/share/zoneinfo/Asia/Jakarta /etc/localtime
# Expose port
EXPOSE 7000
# Run Air untuk hot reload
CMD ["air", "-c", ".air.toml"]

153
Makefile Normal file
View File

@ -0,0 +1,153 @@
# Makefile untuk mengelola Docker commands
.PHONY: help build up down restart logs clean dev prod dev-build dev-up dev-down dev-logs
# Color codes untuk output yang lebih menarik
GREEN := \033[0;32m
YELLOW := \033[1;33m
RED := \033[0;31m
NC := \033[0m # No Color
# Default target
help:
@echo "$(GREEN)Available commands:$(NC)"
@echo "$(YELLOW)Production:$(NC)"
@echo " build - Build all Docker images"
@echo " up - Start all services"
@echo " down - Stop all services"
@echo " restart - Restart all services"
@echo " logs - Show logs for all services"
@echo " clean - Remove all containers and volumes"
@echo " prod - Start production environment"
@echo ""
@echo "$(YELLOW)Development (dengan Air hot reload):$(NC)"
@echo " dev-build - Build development images"
@echo " dev-up - Start development environment dengan hot reload"
@echo " dev-down - Stop development environment"
@echo " dev-logs - Show development logs"
@echo " dev-clean - Clean development environment"
@echo " dev-restart- Restart development environment"
@echo ""
@echo "$(YELLOW)Utilities:$(NC)"
@echo " app-logs - Show only app logs"
@echo " db-logs - Show only database logs"
@echo " status - Check service status"
@echo " shell - Execute bash in app container"
@echo " psql - Execute psql in postgres container"
@echo " redis-cli - Execute redis-cli in redis container"
# Production Commands
build:
@echo "$(GREEN)Building production images...$(NC)"
docker compose build --no-cache
up:
@echo "$(GREEN)Starting production services...$(NC)"
docker compose up -d
down:
@echo "$(RED)Stopping production services...$(NC)"
docker compose down
restart:
@echo "$(YELLOW)Restarting production services...$(NC)"
docker compose restart
logs:
@echo "$(GREEN)Showing production logs...$(NC)"
docker compose logs -f
clean:
@echo "$(RED)Cleaning production environment...$(NC)"
docker compose down -v --remove-orphans
docker system prune -f
docker volume prune -f
prod:
@echo "$(GREEN)Starting production environment...$(NC)"
docker compose up -d
# Development Commands (dengan Air hot reload)
dev-build:
@echo "$(GREEN)Building development images dengan Air...$(NC)"
docker compose -f docker-compose.dev.yml build --no-cache
dev-up:
@echo "$(GREEN)Starting development environment dengan Air hot reload...$(NC)"
docker compose -f docker-compose.dev.yml up -d
@echo "$(GREEN)Development services started!$(NC)"
@echo "$(YELLOW)API Server: http://localhost:7000$(NC)"
@echo "$(YELLOW)PostgreSQL: localhost:5433$(NC)"
@echo "$(YELLOW)Redis: localhost:6378$(NC)"
@echo "$(YELLOW)pgAdmin: http://localhost:8080 (admin@rijig.com / admin123)$(NC)"
@echo "$(YELLOW)Redis Commander: http://localhost:8081$(NC)"
@echo ""
@echo "$(GREEN)✨ Hot reload is active! Edit your Go files and see changes automatically ✨$(NC)"
dev-down:
@echo "$(RED)Stopping development services...$(NC)"
docker compose -f docker-compose.dev.yml down
dev-logs:
@echo "$(GREEN)Showing development logs...$(NC)"
docker compose -f docker-compose.dev.yml logs -f
dev-clean:
@echo "$(RED)Cleaning development environment...$(NC)"
docker compose -f docker-compose.dev.yml down -v --remove-orphans
docker system prune -f
dev-restart:
@echo "$(YELLOW)Restarting development services...$(NC)"
docker compose -f docker-compose.dev.yml restart
# Development utilities
dev-app-logs:
@echo "$(GREEN)Showing development app logs...$(NC)"
docker compose -f docker-compose.dev.yml logs -f app
dev-db-logs:
@echo "$(GREEN)Showing development database logs...$(NC)"
docker compose -f docker-compose.dev.yml logs -f postgres
dev-shell:
@echo "$(GREEN)Accessing development app container...$(NC)"
docker compose -f docker-compose.dev.yml exec app sh
dev-status:
@echo "$(GREEN)Development service status:$(NC)"
docker compose -f docker-compose.dev.yml ps
# Shared utilities
app-logs:
docker compose logs -f app
db-logs:
docker compose logs -f postgres
status:
docker compose ps
shell:
docker compose exec app sh
psql:
docker compose exec postgres psql -U postgres -d apirijig_v2
redis-cli:
docker compose exec redis redis-cli
# Rebuild and restart app only
app-rebuild:
docker compose build app
docker compose up -d app
# View real-time resource usage
stats:
docker stats
# Quick development setup (recommended)
dev:
@echo "$(GREEN)Setting up complete development environment...$(NC)"
make dev-build
make dev-up

View File

@ -1,13 +1,11 @@
package main
import (
"log"
"rijig/config"
"rijig/internal/repositories"
"rijig/internal/services"
"rijig/internal/worker"
// "rijig/internal/repositories"
// "rijig/internal/services"
"rijig/router"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
@ -15,21 +13,21 @@ import (
func main() {
config.SetupConfig()
cartRepo := repositories.NewCartRepository()
trashRepo := repositories.NewTrashRepository(config.DB)
cartService := services.NewCartService(cartRepo, trashRepo)
worker := worker.NewCartWorker(cartService, cartRepo, trashRepo)
// cartRepo := repositories.NewCartRepository()
// trashRepo := repositories.NewTrashRepository(config.DB)
// cartService := services.NewCartService(cartRepo, trashRepo)
// worker := worker.NewCartWorker(cartService, cartRepo, trashRepo)
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
// go func() {
// ticker := time.NewTicker(30 * time.Second)
// defer ticker.Stop()
for range ticker.C {
if err := worker.AutoCommitExpiringCarts(); err != nil {
log.Printf("Auto-commit error: %v", err)
}
}
}()
// for range ticker.C {
// if err := worker.AutoCommitExpiringCarts(); err != nil {
// log.Printf("Auto-commit error: %v", err)
// }
// }
// }()
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
@ -45,7 +43,6 @@ func main() {
app.Use(cors.New())
router.SetupRoutes(app)
config.StartServer(app)
}

View File

@ -2,17 +2,29 @@ package config
import (
"log"
"os"
"github.com/joho/godotenv"
)
func SetupConfig() {
if _, exists := os.LookupEnv("DOCKER_ENV"); exists {
log.Println("Running in Docker container, using environment variables")
} else {
err := godotenv.Load(".env.dev")
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
log.Printf("Warning: Error loading .env file: %v", err)
log.Println("Trying to use system environment variables...")
} else {
log.Println("Loaded environment from .env.dev file")
}
}
ConnectDatabase()
ConnectRedis()
InitWhatsApp()
go func() {
InitWhatsApp() // Ini tidak akan blocking startup server
}()
}

View File

@ -6,7 +6,9 @@ import (
"log"
"os"
"os/signal"
"sync"
"syscall"
"time"
_ "github.com/lib/pq"
"github.com/mdp/qrterminal/v3"
@ -14,15 +16,64 @@ import (
"go.mau.fi/whatsmeow/proto/waE2E"
"go.mau.fi/whatsmeow/store/sqlstore"
"go.mau.fi/whatsmeow/types"
"go.mau.fi/whatsmeow/types/events"
waLog "go.mau.fi/whatsmeow/util/log"
"google.golang.org/protobuf/proto"
)
var WhatsAppClient *whatsmeow.Client
var container *sqlstore.Container
type WhatsAppManager struct {
Client *whatsmeow.Client
container *sqlstore.Container
isConnected bool
mu sync.RWMutex
ctx context.Context
cancel context.CancelFunc
shutdownCh chan struct{}
}
var (
waManager *WhatsAppManager
once sync.Once
)
func GetWhatsAppManager() *WhatsAppManager {
once.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
waManager = &WhatsAppManager{
ctx: ctx,
cancel: cancel,
shutdownCh: make(chan struct{}),
}
})
return waManager
}
func InitWhatsApp() {
dbLog := waLog.Stdout("Database", "DEBUG", true)
manager := GetWhatsAppManager()
log.Println("Initializing WhatsApp client...")
if err := manager.setupDatabase(); err != nil {
log.Fatalf("Failed to setup WhatsApp database: %v", err)
}
if err := manager.setupClient(); err != nil {
log.Fatalf("Failed to setup WhatsApp client: %v", err)
}
if err := manager.handleAuthentication(); err != nil {
log.Fatalf("Failed to authenticate WhatsApp: %v", err)
}
manager.setupEventHandlers()
go manager.handleShutdown()
log.Println("WhatsApp client initialized successfully and ready to send messages!")
}
func (w *WhatsAppManager) setupDatabase() error {
dbLog := waLog.Stdout("WhatsApp-DB", "ERROR", true)
dsn := fmt.Sprintf(
"postgres://%s:%s@%s:%s/%s?sslmode=disable",
@ -34,114 +85,291 @@ func InitWhatsApp() {
)
var err error
container, err = sqlstore.New("postgres", dsn, dbLog)
w.container, err = sqlstore.New("postgres", dsn, dbLog)
if err != nil {
log.Fatalf("Failed to connect to WhatsApp database: %v", err)
return fmt.Errorf("failed to connect to database: %v", err)
}
deviceStore, err := container.GetFirstDevice()
log.Println("WhatsApp database connection established")
return nil
}
func (w *WhatsAppManager) setupClient() error {
deviceStore, err := w.container.GetFirstDevice()
if err != nil {
log.Fatalf("Failed to get WhatsApp device: %v", err)
return fmt.Errorf("failed to get device store: %v", err)
}
clientLog := waLog.Stdout("Client", "DEBUG", true)
WhatsAppClient = whatsmeow.NewClient(deviceStore, clientLog)
clientLog := waLog.Stdout("WhatsApp-Client", "ERROR", true)
w.Client = whatsmeow.NewClient(deviceStore, clientLog)
if WhatsAppClient.Store.ID == nil {
fmt.Println("WhatsApp Client is not logged in, generating QR Code...")
return nil
}
qrChan, _ := WhatsAppClient.GetQRChannel(context.Background())
err = WhatsAppClient.Connect()
if err != nil {
log.Fatalf("Failed to connect WhatsApp client: %v", err)
func (w *WhatsAppManager) handleAuthentication() error {
if w.Client.Store.ID == nil {
log.Println("WhatsApp client not logged in, generating QR code...")
return w.authenticateWithQR()
}
for evt := range qrChan {
if evt.Event == "code" {
fmt.Println("QR Code untuk login:")
log.Println("WhatsApp client already logged in, connecting...")
return w.connect()
}
func (w *WhatsAppManager) authenticateWithQR() error {
qrChan, err := w.Client.GetQRChannel(w.ctx)
if err != nil {
return fmt.Errorf("failed to get QR channel: %v", err)
}
if err := w.Client.Connect(); err != nil {
return fmt.Errorf("failed to connect client: %v", err)
}
qrTimeout := time.NewTimer(3 * time.Minute)
defer qrTimeout.Stop()
for {
select {
case evt := <-qrChan:
switch evt.Event {
case "code":
fmt.Println("\n=== QR CODE UNTUK LOGIN WHATSAPP ===")
generateQRCode(evt.Code)
} else {
fmt.Println("Login event:", evt.Event)
fmt.Println("Scan QR code di atas dengan WhatsApp Anda")
fmt.Println("QR code akan expired dalam 3 menit")
case "success":
log.Println("✅ WhatsApp login successful!")
w.setConnected(true)
return nil
case "timeout":
return fmt.Errorf("QR code expired, please restart")
default:
log.Printf("Login status: %s", evt.Event)
}
case <-qrTimeout.C:
return fmt.Errorf("QR code authentication timeout after 3 minutes")
case <-w.ctx.Done():
return fmt.Errorf("authentication cancelled")
}
}
} else {
fmt.Println("WhatsApp Client sudah login, langsung terhubung...")
err = WhatsAppClient.Connect()
if err != nil {
log.Fatalf("Failed to connect WhatsApp client: %v", err)
}
}
func (w *WhatsAppManager) connect() error {
if err := w.Client.Connect(); err != nil {
return fmt.Errorf("failed to connect: %v", err)
}
log.Println("WhatsApp client connected successfully!")
go handleShutdown()
time.Sleep(2 * time.Second)
w.setConnected(true)
return nil
}
func (w *WhatsAppManager) setupEventHandlers() {
w.Client.AddEventHandler(func(evt interface{}) {
switch v := evt.(type) {
case *events.Connected:
log.Println("✅ WhatsApp client connected")
w.setConnected(true)
case *events.Disconnected:
log.Println("❌ WhatsApp client disconnected")
w.setConnected(false)
case *events.LoggedOut:
log.Println("🚪 WhatsApp client logged out")
w.setConnected(false)
case *events.Message:
log.Printf("📨 Message received from %s", v.Info.Sender)
}
})
}
func (w *WhatsAppManager) setConnected(status bool) {
w.mu.Lock()
defer w.mu.Unlock()
w.isConnected = status
}
func (w *WhatsAppManager) IsConnected() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.isConnected
}
func generateQRCode(qrString string) {
qrterminal.GenerateHalfBlock(qrString, qrterminal.M, os.Stdout)
}
func handleShutdown() {
func (w *WhatsAppManager) handleShutdown() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
select {
case <-sigChan:
log.Println("Received shutdown signal...")
case <-w.ctx.Done():
log.Println("Context cancelled...")
}
w.shutdown()
}
func (w *WhatsAppManager) shutdown() {
log.Println("Shutting down WhatsApp client...")
WhatsAppClient.Disconnect()
os.Exit(0)
w.cancel()
if w.Client != nil {
w.Client.Disconnect()
}
if w.container != nil {
w.container.Close()
}
close(w.shutdownCh)
log.Println("WhatsApp client shutdown completed")
}
func SendWhatsAppMessage(phone, message string) error {
if WhatsAppClient == nil {
manager := GetWhatsAppManager()
if manager.Client == nil {
return fmt.Errorf("WhatsApp client is not initialized")
}
targetJID, _ := types.ParseJID(phone + "@s.whatsapp.net")
msg := waE2E.Message{
if !manager.IsConnected() {
return fmt.Errorf("WhatsApp client is not connected")
}
if phone == "" || message == "" {
return fmt.Errorf("phone number and message cannot be empty")
}
if phone[0] == '0' {
phone = "62" + phone[1:] // Convert 08xx menjadi 628xx
}
if phone[:2] != "62" {
phone = "62" + phone // Tambahkan 62 jika belum ada
}
// Parse JID
targetJID, err := types.ParseJID(phone + "@s.whatsapp.net")
if err != nil {
return fmt.Errorf("invalid phone number format: %v", err)
}
// Buat pesan
msg := &waE2E.Message{
Conversation: proto.String(message),
}
_, err := WhatsAppClient.SendMessage(context.Background(), targetJID, &msg)
// Kirim dengan timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
resp, err := manager.Client.SendMessage(ctx, targetJID, msg)
if err != nil {
return fmt.Errorf("failed to send WhatsApp message: %v", err)
return fmt.Errorf("failed to send message: %v", err)
}
log.Printf("WhatsApp message sent successfully to: %s", phone)
log.Printf("✅ Message sent to %s (ID: %s)", phone, resp.ID)
return nil
}
// SendWhatsAppMessageBatch - Kirim pesan ke multiple nomor
func SendWhatsAppMessageBatch(phoneNumbers []string, message string) []error {
var errors []error
for _, phone := range phoneNumbers {
if err := SendWhatsAppMessage(phone, message); err != nil {
errors = append(errors, fmt.Errorf("failed to send to %s: %v", phone, err))
continue
}
// Delay untuk menghindari rate limit
time.Sleep(1 * time.Second)
}
return errors
}
// GetWhatsAppStatus - Cek status koneksi
func GetWhatsAppStatus() map[string]interface{} {
manager := GetWhatsAppManager()
status := map[string]interface{}{
"initialized": manager.Client != nil,
"connected": manager.IsConnected(),
"logged_in": false,
"jid": "",
}
if manager.Client != nil && manager.Client.Store.ID != nil {
status["logged_in"] = true
status["jid"] = manager.Client.Store.ID.String()
}
return status
}
// LogoutWhatsApp - Logout dan cleanup
func LogoutWhatsApp() error {
if WhatsAppClient == nil {
manager := GetWhatsAppManager()
if manager.Client == nil {
return fmt.Errorf("WhatsApp client is not initialized")
}
WhatsAppClient.Disconnect()
log.Println("Logging out WhatsApp...")
err := removeWhatsAppDeviceFromContainer()
// Logout
err := manager.Client.Logout()
if err != nil {
return fmt.Errorf("failed to remove device from container: %v", err)
log.Printf("Warning: Failed to logout properly: %v", err)
}
err = container.Close()
if err != nil {
return fmt.Errorf("failed to close database connection: %v", err)
// Disconnect
manager.Client.Disconnect()
manager.setConnected(false)
// Hapus device dari store
if err := manager.removeDeviceFromStore(); err != nil {
log.Printf("Warning: Failed to remove device: %v", err)
}
log.Println("WhatsApp client disconnected and session cleared successfully.")
// Close database
if manager.container != nil {
manager.container.Close()
}
log.Println("✅ WhatsApp logout completed")
return nil
}
func removeWhatsAppDeviceFromContainer() error {
deviceStore, err := container.GetFirstDevice()
func (w *WhatsAppManager) removeDeviceFromStore() error {
deviceStore, err := w.container.GetFirstDevice()
if err != nil {
return fmt.Errorf("failed to get WhatsApp device: %v", err)
return err
}
if deviceStore != nil {
err := deviceStore.Delete()
if err != nil {
return fmt.Errorf("failed to remove device from store: %v", err)
}
if deviceStore != nil && deviceStore.ID != nil {
return deviceStore.Delete()
}
return nil
}
// IsValidPhoneNumber - Validasi format nomor telepon Indonesia
func IsValidPhoneNumber(phone string) bool {
// Minimal validasi untuk nomor Indonesia
if len(phone) < 10 || len(phone) > 15 {
return false
}
// Cek awalan nomor Indonesia
if phone[:2] == "62" || phone[0] == '0' {
return true
}
return false
}

139
docker-compose.dev.yml Normal file
View File

@ -0,0 +1,139 @@
# docker-compose.dev.yml - Development environment dengan Air hot reload
services:
# PostgreSQL Database
postgres:
image: postgres:16-alpine
container_name: rijig_postgres_dev
restart: unless-stopped
environment:
POSTGRES_DB: apirijig_v2
POSTGRES_USER: postgres
POSTGRES_PASSWORD: pahmiadmin
PGDATA: /var/lib/postgresql/data/pgdata
ports:
- "5433:5432"
volumes:
- postgres_data_dev:/var/lib/postgresql/data
- ./init-db:/docker-entrypoint-initdb.d
networks:
- rijig_network_dev
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d apirijig_v2"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Redis Cache
redis:
image: redis:7-alpine
container_name: rijig_redis_dev
restart: unless-stopped
ports:
- "6378:6379"
volumes:
- redis_data_dev:/data
networks:
- rijig_network_dev
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
start_period: 15s
# Go Application dengan Air hot reload
app:
build:
context: .
dockerfile: Dockerfile.dev
container_name: rijig_app_dev
restart: unless-stopped
ports:
- "7000:7000"
environment:
# Docker Environment Flag
DOCKER_ENV: "true"
# Base URL
BASE_URL: /apirijig/v2
# Server Settings
SERVER_HOST: 0.0.0.0
SERVER_PORT: 7000
# Database Settings - menggunakan service name sebagai host
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: apirijig_v2
DB_USER: postgres
DB_PASSWORD: pahmiadmin
# Redis Settings - menggunakan service name sebagai host
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ""
REDIS_DB: 0
# Auth Keys
API_KEY: apirijikL0RH64wfkEpPqjAroLVPuFgT0EpsSLBPsmyUvIqZrUAi6X3HNPM7Vter
SECRET_KEY: TJ6h3vPMPlAuv7cbD27RU1/UyRctEih5k4H3+o7tZM1PSwTcoFETL6lqB54=
# TTL Settings
ACCESS_TOKEN_EXPIRY: 23*time.Hour
REFRESH_TOKEN_EXPIRY: 28*24*time.Hour
PARTIAL_TOKEN_EXPIRY: 2*time.Hour
volumes:
# Mount source code untuk hot reload
- .:/app
# Exclude node_modules dan vendor (jika ada)
- /app/tmp
- /app/vendor
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- rijig_network_dev
# pgAdmin (optional - untuk GUI database management)
pgadmin:
image: dpage/pgadmin4:latest
container_name: rijig_pgadmin_dev
restart: unless-stopped
environment:
PGADMIN_DEFAULT_EMAIL: admin@rijig.com
PGADMIN_DEFAULT_PASSWORD: admin123
PGADMIN_CONFIG_SERVER_MODE: "False"
ports:
- "8080:80"
volumes:
- pgadmin_data_dev:/var/lib/pgadmin
depends_on:
- postgres
networks:
- rijig_network_dev
# Redis Commander (optional - untuk GUI redis management)
redis-commander:
image: rediscommander/redis-commander:latest
container_name: rijig_redis_commander_dev
restart: unless-stopped
environment:
REDIS_HOSTS: local:redis:6379
ports:
- "8081:8081"
depends_on:
- redis
networks:
- rijig_network_dev
networks:
rijig_network_dev:
driver: bridge
volumes:
postgres_data_dev:
redis_data_dev:
pgadmin_data_dev:

View File

@ -38,7 +38,7 @@ type ResponseRequestPickup struct {
type ResponseRequestPickupItem struct {
ID string `json:"id,omitempty"`
TrashCategoryID string `json:"trash_category_id,omitempty"`
TrashCategory []ResponseTrashCategoryDTO `json:"trash_category,omitempty"`
// TrashCategory []ResponseTrashCategoryDTO `json:"trash_category,omitempty"`
EstimatedAmount float64 `json:"estimated_amount,omitempty"`
}

View File

@ -4,17 +4,16 @@ import (
"fmt"
"log"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AboutHandler struct {
AboutService services.AboutService
AboutService AboutService
}
func NewAboutHandler(aboutService services.AboutService) *AboutHandler {
func NewAboutHandler(aboutService AboutService) *AboutHandler {
return &AboutHandler{
AboutService: aboutService,
}
@ -36,7 +35,7 @@ func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error {
return utils.BadRequest(c, "Cover image is required")
}
response, err := h.AboutService.CreateAbout(request, aboutCoverImage)
response, err := h.AboutService.CreateAbout(c.Context(), request, aboutCoverImage)
if err != nil {
log.Printf("Error creating About: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to create About: %v", err))
@ -60,7 +59,7 @@ func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error {
return utils.BadRequest(c, "cover_image is required")
}
response, err := h.AboutService.UpdateAbout(id, request, aboutCoverImage)
response, err := h.AboutService.UpdateAbout(c.Context(), id, request, aboutCoverImage)
if err != nil {
log.Printf("Error updating About: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to update About: %v", err))
@ -70,7 +69,7 @@ func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error {
}
func (h *AboutHandler) GetAllAbout(c *fiber.Ctx) error {
response, err := h.AboutService.GetAllAbout()
response, err := h.AboutService.GetAllAbout(c.Context())
if err != nil {
log.Printf("Error fetching all About: %v", err)
return utils.InternalServerError(c, "Failed to fetch About list")
@ -82,7 +81,7 @@ func (h *AboutHandler) GetAllAbout(c *fiber.Ctx) error {
func (h *AboutHandler) GetAboutByID(c *fiber.Ctx) error {
id := c.Params("id")
response, err := h.AboutService.GetAboutByID(id)
response, err := h.AboutService.GetAboutByID(c.Context(), id)
if err != nil {
log.Printf("Error fetching About by ID: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to fetch About by ID: %v", err))
@ -94,7 +93,7 @@ func (h *AboutHandler) GetAboutByID(c *fiber.Ctx) error {
func (h *AboutHandler) GetAboutDetailById(c *fiber.Ctx) error {
id := c.Params("id")
response, err := h.AboutService.GetAboutDetailById(id)
response, err := h.AboutService.GetAboutDetailById(c.Context(), id)
if err != nil {
log.Printf("Error fetching About detail by ID: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to fetch About by ID: %v", err))
@ -106,7 +105,7 @@ func (h *AboutHandler) GetAboutDetailById(c *fiber.Ctx) error {
func (h *AboutHandler) DeleteAbout(c *fiber.Ctx) error {
id := c.Params("id")
if err := h.AboutService.DeleteAbout(id); err != nil {
if err := h.AboutService.DeleteAbout(c.Context(), id); err != nil {
log.Printf("Error deleting About: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to delete About: %v", err))
}
@ -132,7 +131,7 @@ func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error {
return utils.BadRequest(c, "image_detail is required")
}
response, err := h.AboutService.CreateAboutDetail(request, aboutDetailImage)
response, err := h.AboutService.CreateAboutDetail(c.Context(), request, aboutDetailImage)
if err != nil {
log.Printf("Error creating AboutDetail: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to create AboutDetail: %v", err))
@ -156,7 +155,7 @@ func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error {
return utils.BadRequest(c, "image_detail is required")
}
response, err := h.AboutService.UpdateAboutDetail(id, request, aboutDetailImage)
response, err := h.AboutService.UpdateAboutDetail(c.Context(), id, request, aboutDetailImage)
if err != nil {
log.Printf("Error updating AboutDetail: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to update AboutDetail: %v", err))
@ -168,7 +167,7 @@ func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error {
func (h *AboutHandler) DeleteAboutDetail(c *fiber.Ctx) error {
id := c.Params("id")
if err := h.AboutService.DeleteAboutDetail(id); err != nil {
if err := h.AboutService.DeleteAboutDetail(c.Context(), id); err != nil {
log.Printf("Error deleting AboutDetail: %v", err)
return utils.InternalServerError(c, fmt.Sprintf("Failed to delete AboutDetail: %v", err))
}

View File

@ -1 +1,32 @@
package about
import (
"rijig/config"
"rijig/middleware"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
func AboutRouter(api fiber.Router) {
aboutRepo := NewAboutRepository(config.DB)
aboutService := NewAboutService(aboutRepo)
aboutHandler := NewAboutHandler(aboutService)
aboutRoutes := api.Group("/about")
aboutRoutes.Use(middleware.AuthMiddleware())
aboutRoutes.Get("/", aboutHandler.GetAllAbout)
aboutRoutes.Get("/:id", aboutHandler.GetAboutByID)
aboutRoutes.Post("/", aboutHandler.CreateAbout)
aboutRoutes.Put("/:id", middleware.RequireRoles(utils.RoleAdministrator), aboutHandler.UpdateAbout)
aboutRoutes.Delete("/:id", aboutHandler.DeleteAbout)
aboutDetailRoutes := api.Group("/about-detail")
aboutDetailRoutes.Use(middleware.AuthMiddleware())
aboutDetailRoute := api.Group("/about-detail")
aboutDetailRoute.Get("/:id", aboutHandler.GetAboutDetailById)
aboutDetailRoutes.Post("/", aboutHandler.CreateAboutDetail)
aboutDetailRoutes.Put("/:id", middleware.RequireRoles(utils.RoleAdministrator), aboutHandler.UpdateAboutDetail)
aboutDetailRoutes.Delete("/:id", middleware.RequireRoles(utils.RoleAdministrator), aboutHandler.DeleteAboutDetail)
}

View File

@ -1 +1,113 @@
package address
import (
"rijig/dto"
"rijig/middleware"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AddressHandler struct {
AddressService AddressService
}
func NewAddressHandler(addressService AddressService) *AddressHandler {
return &AddressHandler{AddressService: addressService}
}
func (h *AddressHandler) CreateAddress(c *fiber.Ctx) error {
var request dto.CreateAddressDTO
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
if err := c.BodyParser(&request); err != nil {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Invalid request body", map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateAddress()
if !valid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", errors)
}
addressResponse, err := h.AddressService.CreateAddress(c.Context(), claims.UserID, request)
if err != nil {
return utils.InternalServerError(c, err.Error())
}
return utils.CreateSuccessWithData(c, "user address created successfully", addressResponse)
}
func (h *AddressHandler) GetAddressByUserID(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
addresses, err := h.AddressService.GetAddressByUserID(c.Context(), claims.UserID)
if err != nil {
return utils.NotFound(c, err.Error())
}
return utils.SuccessWithData(c, "User addresses fetched successfully", addresses)
}
func (h *AddressHandler) GetAddressByID(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
addressID := c.Params("address_id")
address, err := h.AddressService.GetAddressByID(c.Context(), claims.UserID, addressID)
if err != nil {
return utils.NotFound(c, err.Error())
}
return utils.SuccessWithData(c, "Address fetched successfully", address)
}
func (h *AddressHandler) UpdateAddress(c *fiber.Ctx) error {
addressID := c.Params("address_id")
var request dto.CreateAddressDTO
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
if err := c.BodyParser(&request); err != nil {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Invalid request body", map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateAddress()
if !valid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", errors)
}
updatedAddress, err := h.AddressService.UpdateAddress(c.Context(), claims.UserID, addressID, request)
if err != nil {
return utils.NotFound(c, err.Error())
}
return utils.SuccessWithData(c, "User address updated successfully", updatedAddress)
}
func (h *AddressHandler) DeleteAddress(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
addressID := c.Params("address_id")
err = h.AddressService.DeleteAddress(c.Context(), claims.UserID, addressID)
if err != nil {
return utils.Forbidden(c, err.Error())
}
return utils.Success(c, "Address deleted successfully")
}

View File

@ -1 +1,24 @@
package address
import (
"rijig/config"
"rijig/internal/wilayahindo"
"rijig/middleware"
"github.com/gofiber/fiber/v2"
)
func AddressRouter(api fiber.Router) {
addressRepo := NewAddressRepository(config.DB)
wilayahRepo := wilayahindo.NewWilayahIndonesiaRepository(config.DB)
addressService := NewAddressService(addressRepo, wilayahRepo)
addressHandler := NewAddressHandler(addressService)
adddressAPI := api.Group("/user/address")
adddressAPI.Post("/create-address", middleware.AuthMiddleware(), addressHandler.CreateAddress)
adddressAPI.Get("/get-address", middleware.AuthMiddleware(), addressHandler.GetAddressByUserID)
adddressAPI.Get("/get-address/:address_id", middleware.AuthMiddleware(), addressHandler.GetAddressByID)
adddressAPI.Put("/update-address/:address_id", middleware.AuthMiddleware(), addressHandler.UpdateAddress)
adddressAPI.Delete("/delete-address/:address_id", middleware.AuthMiddleware(), addressHandler.DeleteAddress)
}

View File

@ -2,9 +2,6 @@ package article
import (
"rijig/config"
"rijig/internal/handler"
"rijig/internal/repositories"
"rijig/internal/services"
"rijig/middleware"
"rijig/utils"
@ -12,9 +9,9 @@ import (
)
func ArticleRouter(api fiber.Router) {
articleRepo := repositories.NewArticleRepository(config.DB)
articleService := services.NewArticleService(articleRepo)
articleHandler := handler.NewArticleHandler(articleService)
articleRepo := NewArticleRepository(config.DB)
articleService := NewArticleService(articleRepo)
articleHandler := NewArticleHandler(articleService)
articleAPI := api.Group("/article")

View File

@ -1 +1,49 @@
package cart
import (
"fmt"
"strings"
)
type RequestCartItemDTO struct {
TrashID string `json:"trash_id"`
Amount float64 `json:"amount"`
}
type RequestCartDTO struct {
CartItems []RequestCartItemDTO `json:"cart_items"`
}
type CartResponse struct {
ID string `json:"id"`
UserID string `json:"user_id"`
TotalAmount float64 `json:"total_amount"`
EstimatedTotalPrice float64 `json:"estimated_total_price"`
CartItems []CartItemResponse `json:"cart_items"`
}
type CartItemResponse struct {
ID string `json:"id"`
TrashID string `json:"trash_id"`
TrashName string `json:"trash_name"`
TrashIcon string `json:"trash_icon"`
TrashPrice float64 `json:"trash_price"`
Amount float64 `json:"amount"`
SubTotalEstimatedPrice float64 `json:"subtotal_estimated_price"`
}
func (r *RequestCartDTO) ValidateRequestCartDTO() (map[string][]string, bool) {
errors := make(map[string][]string)
for i, item := range r.CartItems {
if strings.TrimSpace(item.TrashID) == "" {
errors[fmt.Sprintf("cart_items[%d].trash_id", i)] = append(errors[fmt.Sprintf("cart_items[%d].trash_id", i)], "trash_id tidak boleh kosong")
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1 +1,162 @@
package cart
import (
"context"
"errors"
"fmt"
"rijig/config"
"rijig/model"
"gorm.io/gorm"
)
type CartRepository interface {
FindOrCreateCart(ctx context.Context, userID string) (*model.Cart, error)
AddOrUpdateCartItem(ctx context.Context, cartID, trashCategoryID string, amount float64, estimatedPrice float64) error
DeleteCartItem(ctx context.Context, cartID, trashCategoryID string) error
GetCartByUser(ctx context.Context, userID string) (*model.Cart, error)
UpdateCartTotals(ctx context.Context, cartID string) error
DeleteCart(ctx context.Context, userID string) error
CreateCartWithItems(ctx context.Context, cart *model.Cart) error
HasExistingCart(ctx context.Context, userID string) (bool, error)
}
type cartRepository struct{}
func NewCartRepository() CartRepository {
return &cartRepository{}
}
func (r *cartRepository) FindOrCreateCart(ctx context.Context, userID string) (*model.Cart, error) {
var cart model.Cart
db := config.DB.WithContext(ctx)
err := db.
Preload("CartItems.TrashCategory").
Where("user_id = ?", userID).
First(&cart).Error
if err == nil {
return &cart, nil
}
if errors.Is(err, gorm.ErrRecordNotFound) {
newCart := model.Cart{
UserID: userID,
TotalAmount: 0,
EstimatedTotalPrice: 0,
}
if err := db.Create(&newCart).Error; err != nil {
return nil, err
}
return &newCart, nil
}
return nil, err
}
func (r *cartRepository) AddOrUpdateCartItem(ctx context.Context, cartID, trashCategoryID string, amount float64, estimatedPrice float64) error {
db := config.DB.WithContext(ctx)
var item model.CartItem
err := db.
Where("cart_id = ? AND trash_category_id = ?", cartID, trashCategoryID).
First(&item).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
newItem := model.CartItem{
CartID: cartID,
TrashCategoryID: trashCategoryID,
Amount: amount,
SubTotalEstimatedPrice: amount * estimatedPrice,
}
return db.Create(&newItem).Error
}
if err != nil {
return err
}
item.Amount = amount
item.SubTotalEstimatedPrice = amount * estimatedPrice
return db.Save(&item).Error
}
func (r *cartRepository) DeleteCartItem(ctx context.Context, cartID, trashCategoryID string) error {
db := config.DB.WithContext(ctx)
return db.Where("cart_id = ? AND trash_category_id = ?", cartID, trashCategoryID).
Delete(&model.CartItem{}).Error
}
func (r *cartRepository) GetCartByUser(ctx context.Context, userID string) (*model.Cart, error) {
var cart model.Cart
db := config.DB.WithContext(ctx)
err := db.
Preload("CartItems.TrashCategory").
Where("user_id = ?", userID).
First(&cart).Error
if err != nil {
return nil, err
}
return &cart, nil
}
func (r *cartRepository) UpdateCartTotals(ctx context.Context, cartID string) error {
db := config.DB.WithContext(ctx)
var items []model.CartItem
if err := db.Where("cart_id = ?", cartID).Find(&items).Error; err != nil {
return err
}
var totalAmount float64
var totalPrice float64
for _, item := range items {
totalAmount += item.Amount
totalPrice += item.SubTotalEstimatedPrice
}
return db.Model(&model.Cart{}).
Where("id = ?", cartID).
Updates(map[string]interface{}{
"total_amount": totalAmount,
"estimated_total_price": totalPrice,
}).Error
}
func (r *cartRepository) DeleteCart(ctx context.Context, userID string) error {
db := config.DB.WithContext(ctx)
var cart model.Cart
if err := db.Where("user_id = ?", userID).First(&cart).Error; err != nil {
return err
}
return db.Delete(&cart).Error
}
func (r *cartRepository) CreateCartWithItems(ctx context.Context, cart *model.Cart) error {
db := config.DB.WithContext(ctx)
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(cart).Error; err != nil {
return fmt.Errorf("failed to create cart: %w", err)
}
return nil
})
}
func (r *cartRepository) HasExistingCart(ctx context.Context, userID string) (bool, error) {
db := config.DB.WithContext(ctx)
var count int64
err := db.Model(&model.Cart{}).Where("user_id = ?", userID).Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}

View File

@ -1 +1,356 @@
package cart
import (
"context"
"fmt"
"log"
"time"
"rijig/internal/trash"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type CartService struct {
cartRepo CartRepository
trashRepo trash.TrashRepositoryInterface
}
func NewCartService(cartRepo CartRepository, trashRepo trash.TrashRepositoryInterface) *CartService {
return &CartService{
cartRepo: cartRepo,
trashRepo: trashRepo,
}
}
func (s *CartService) AddToCart(ctx context.Context, userID, trashCategoryID string, amount float64) error {
cartKey := fmt.Sprintf("cart:%s", userID)
var cartItems map[string]model.CartItem
err := utils.GetCache(cartKey, &cartItems)
if err != nil && err.Error() != "ErrCacheMiss" {
return fmt.Errorf("failed to get cart from cache: %w", err)
}
if cartItems == nil {
cartItems = make(map[string]model.CartItem)
}
trashCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, trashCategoryID)
if err != nil {
return fmt.Errorf("failed to get trash category: %w", err)
}
cartItems[trashCategoryID] = model.CartItem{
TrashCategoryID: trashCategoryID,
Amount: amount,
SubTotalEstimatedPrice: amount * float64(trashCategory.EstimatedPrice),
}
return utils.SetCache(cartKey, cartItems, 24*time.Hour)
}
func (s *CartService) RemoveFromCart(ctx context.Context, userID, trashCategoryID string) error {
cartKey := fmt.Sprintf("cart:%s", userID)
var cartItems map[string]model.CartItem
err := utils.GetCache(cartKey, &cartItems)
if err != nil {
if err.Error() == "ErrCacheMiss" {
return nil
}
return fmt.Errorf("failed to get cart from cache: %w", err)
}
delete(cartItems, trashCategoryID)
if len(cartItems) == 0 {
return utils.DeleteCache(cartKey)
}
return utils.SetCache(cartKey, cartItems, 24*time.Hour)
}
func (s *CartService) ClearCart(userID string) error {
cartKey := fmt.Sprintf("cart:%s", userID)
return utils.DeleteCache(cartKey)
}
func (s *CartService) GetCartFromRedis(ctx context.Context, userID string) (*CartResponse, error) {
cartKey := fmt.Sprintf("cart:%s", userID)
var cartItems map[string]model.CartItem
err := utils.GetCache(cartKey, &cartItems)
if err != nil {
if err.Error() == "ErrCacheMiss" {
return &CartResponse{
ID: "N/A",
UserID: userID,
TotalAmount: 0,
EstimatedTotalPrice: 0,
CartItems: []CartItemResponse{},
}, nil
}
return nil, fmt.Errorf("failed to get cart from cache: %w", err)
}
var totalAmount float64
var estimatedTotal float64
var cartItemDTOs []CartItemResponse
for _, item := range cartItems {
trashCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashCategoryID)
if err != nil {
log.Printf("Failed to get trash category %s: %v", item.TrashCategoryID, err)
continue
}
totalAmount += item.Amount
estimatedTotal += item.SubTotalEstimatedPrice
cartItemDTOs = append(cartItemDTOs, CartItemResponse{
ID: uuid.NewString(),
TrashID: trashCategory.ID,
TrashName: trashCategory.Name,
TrashIcon: trashCategory.IconTrash,
TrashPrice: float64(trashCategory.EstimatedPrice),
Amount: item.Amount,
SubTotalEstimatedPrice: item.SubTotalEstimatedPrice,
})
}
resp := &CartResponse{
ID: "N/A",
UserID: userID,
TotalAmount: totalAmount,
EstimatedTotalPrice: estimatedTotal,
CartItems: cartItemDTOs,
}
return resp, nil
}
func (s *CartService) CommitCartToDatabase(ctx context.Context, userID string) error {
cartKey := fmt.Sprintf("cart:%s", userID)
var cartItems map[string]model.CartItem
err := utils.GetCache(cartKey, &cartItems)
if err != nil {
if err.Error() == "ErrCacheMiss" {
log.Printf("No cart items found in Redis for user: %s", userID)
return fmt.Errorf("no cart items found")
}
return fmt.Errorf("failed to get cart from cache: %w", err)
}
if len(cartItems) == 0 {
log.Printf("No items to commit for user: %s", userID)
return fmt.Errorf("no items to commit")
}
hasCart, err := s.cartRepo.HasExistingCart(ctx, userID)
if err != nil {
return fmt.Errorf("failed to check existing cart: %w", err)
}
var cart *model.Cart
if hasCart {
cart, err = s.cartRepo.GetCartByUser(ctx, userID)
if err != nil {
return fmt.Errorf("failed to get existing cart: %w", err)
}
} else {
cart, err = s.cartRepo.FindOrCreateCart(ctx, userID)
if err != nil {
return fmt.Errorf("failed to create cart: %w", err)
}
}
for _, item := range cartItems {
trashCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashCategoryID)
if err != nil {
log.Printf("Trash category not found for trashID: %s", item.TrashCategoryID)
continue
}
err = s.cartRepo.AddOrUpdateCartItem(
ctx,
cart.ID,
item.TrashCategoryID,
item.Amount,
float64(trashCategory.EstimatedPrice),
)
if err != nil {
log.Printf("Failed to add/update cart item: %v", err)
continue
}
}
if err := s.cartRepo.UpdateCartTotals(ctx, cart.ID); err != nil {
return fmt.Errorf("failed to update cart totals: %w", err)
}
if err := utils.DeleteCache(cartKey); err != nil {
log.Printf("Failed to clear Redis cart: %v", err)
}
log.Printf("Cart committed successfully for user: %s", userID)
return nil
}
func (s *CartService) GetCart(ctx context.Context, userID string) (*CartResponse, error) {
cartRedis, err := s.GetCartFromRedis(ctx, userID)
if err == nil && len(cartRedis.CartItems) > 0 {
return cartRedis, nil
}
cartDB, err := s.cartRepo.GetCartByUser(ctx, userID)
if err != nil {
return &CartResponse{
ID: "N/A",
UserID: userID,
TotalAmount: 0,
EstimatedTotalPrice: 0,
CartItems: []CartItemResponse{},
}, nil
}
var items []CartItemResponse
for _, item := range cartDB.CartItems {
items = append(items, CartItemResponse{
ID: item.ID,
TrashID: item.TrashCategoryID,
TrashName: item.TrashCategory.Name,
TrashIcon: item.TrashCategory.IconTrash,
TrashPrice: float64(item.TrashCategory.EstimatedPrice),
Amount: item.Amount,
SubTotalEstimatedPrice: item.SubTotalEstimatedPrice,
})
}
resp := &CartResponse{
ID: cartDB.ID,
UserID: cartDB.UserID,
TotalAmount: cartDB.TotalAmount,
EstimatedTotalPrice: cartDB.EstimatedTotalPrice,
CartItems: items,
}
return resp, nil
}
func (s *CartService) SyncCartFromDatabaseToRedis(ctx context.Context, userID string) error {
cartDB, err := s.cartRepo.GetCartByUser(ctx, userID)
if err != nil {
return fmt.Errorf("failed to get cart from database: %w", err)
}
cartItems := make(map[string]model.CartItem)
for _, item := range cartDB.CartItems {
cartItems[item.TrashCategoryID] = model.CartItem{
TrashCategoryID: item.TrashCategoryID,
Amount: item.Amount,
SubTotalEstimatedPrice: item.SubTotalEstimatedPrice,
}
}
cartKey := fmt.Sprintf("cart:%s", userID)
return utils.SetCache(cartKey, cartItems, 24*time.Hour)
}
func (s *CartService) GetCartItemCount(userID string) (int, error) {
cartKey := fmt.Sprintf("cart:%s", userID)
var cartItems map[string]model.CartItem
err := utils.GetCache(cartKey, &cartItems)
if err != nil {
if err.Error() == "ErrCacheMiss" {
return 0, nil
}
return 0, fmt.Errorf("failed to get cart from cache: %w", err)
}
return len(cartItems), nil
}
func (s *CartService) DeleteCart(ctx context.Context, userID string) error {
cartKey := fmt.Sprintf("cart:%s", userID)
if err := utils.DeleteCache(cartKey); err != nil {
log.Printf("Failed to delete cart from Redis: %v", err)
}
return s.cartRepo.DeleteCart(ctx, userID)
}
func (s *CartService) UpdateCartWithDTO(ctx context.Context, userID string, cartDTO *RequestCartDTO) error {
if errors, valid := cartDTO.ValidateRequestCartDTO(); !valid {
return fmt.Errorf("validation failed: %v", errors)
}
cartKey := fmt.Sprintf("cart:%s", userID)
cartItems := make(map[string]model.CartItem)
for _, itemDTO := range cartDTO.CartItems {
trashCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, itemDTO.TrashID)
if err != nil {
log.Printf("Failed to get trash category %s: %v", itemDTO.TrashID, err)
continue
}
subtotal := itemDTO.Amount * float64(trashCategory.EstimatedPrice)
cartItems[itemDTO.TrashID] = model.CartItem{
TrashCategoryID: itemDTO.TrashID,
Amount: itemDTO.Amount,
SubTotalEstimatedPrice: subtotal,
}
}
return utils.SetCache(cartKey, cartItems, 24*time.Hour)
}
func (s *CartService) AddItemsToCart(ctx context.Context, userID string, items []RequestCartItemDTO) error {
cartKey := fmt.Sprintf("cart:%s", userID)
var cartItems map[string]model.CartItem
err := utils.GetCache(cartKey, &cartItems)
if err != nil && err.Error() != "ErrCacheMiss" {
return fmt.Errorf("failed to get cart from cache: %w", err)
}
if cartItems == nil {
cartItems = make(map[string]model.CartItem)
}
for _, itemDTO := range items {
if itemDTO.TrashID == "" {
continue
}
trashCategory, err := s.trashRepo.GetTrashCategoryByID(ctx, itemDTO.TrashID)
if err != nil {
log.Printf("Failed to get trash category %s: %v", itemDTO.TrashID, err)
continue
}
subtotal := itemDTO.Amount * float64(trashCategory.EstimatedPrice)
cartItems[itemDTO.TrashID] = model.CartItem{
TrashCategoryID: itemDTO.TrashID,
Amount: itemDTO.Amount,
SubTotalEstimatedPrice: subtotal,
}
}
return utils.SetCache(cartKey, cartItems, 24*time.Hour)
}

View File

@ -1 +1,252 @@
package collector
import (
"fmt"
"rijig/internal/address"
"rijig/internal/trash"
"strings"
"time"
)
type CreateCollectorRequest struct {
UserID string `json:"user_id" binding:"required"`
JobStatus string `json:"job_status,omitempty"`
AddressID string `json:"address_id" binding:"required"`
AvailableTrashItems []CreateAvailableTrashRequest `json:"available_trash_items,omitempty"`
}
type UpdateCollectorRequest struct {
JobStatus string `json:"job_status,omitempty"`
AddressID string `json:"address_id,omitempty"`
AvailableTrashItems []CreateAvailableTrashRequest `json:"available_trash_items,omitempty"`
}
type CreateAvailableTrashRequest struct {
TrashCategoryID string `json:"trash_category_id" binding:"required"`
Price float32 `json:"price" binding:"required"`
}
type UpdateAvailableTrashRequest struct {
ID string `json:"id,omitempty"`
TrashCategoryID string `json:"trash_category_id,omitempty"`
Price float32 `json:"price,omitempty"`
}
type CollectorResponse struct {
ID string `json:"id"`
UserID string `json:"user_id"`
JobStatus string `json:"job_status"`
Rating float32 `json:"rating"`
AddressID string `json:"address_id"`
Address *address.AddressResponseDTO `json:"address,omitempty"`
AvailableTrash []AvailableTrashResponse `json:"available_trash"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
type AvailableTrashResponse struct {
ID string `json:"id"`
CollectorID string `json:"collector_id"`
TrashCategoryID string `json:"trash_category_id"`
TrashCategory *trash.ResponseTrashCategoryDTO `json:"trash_category,omitempty"`
Price float32 `json:"price"`
}
func (r *CreateCollectorRequest) ValidateCreateCollectorRequest() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.UserID) == "" {
errors["user_id"] = append(errors["user_id"], "User ID tidak boleh kosong")
}
if strings.TrimSpace(r.AddressID) == "" {
errors["address_id"] = append(errors["address_id"], "Address ID tidak boleh kosong")
}
if r.JobStatus != "" {
r.JobStatus = strings.ToLower(strings.TrimSpace(r.JobStatus))
if r.JobStatus != "active" && r.JobStatus != "inactive" {
errors["job_status"] = append(errors["job_status"], "Job status hanya boleh 'active' atau 'inactive'")
}
} else {
r.JobStatus = "inactive"
}
if len(r.AvailableTrashItems) > 0 {
trashCategoryMap := make(map[string]bool)
for i, item := range r.AvailableTrashItems {
fieldPrefix := fmt.Sprintf("available_trash_items[%d]", i)
if strings.TrimSpace(item.TrashCategoryID) == "" {
errors[fieldPrefix+".trash_category_id"] = append(errors[fieldPrefix+".trash_category_id"], "Trash category ID tidak boleh kosong")
} else {
if trashCategoryMap[item.TrashCategoryID] {
errors[fieldPrefix+".trash_category_id"] = append(errors[fieldPrefix+".trash_category_id"], "Trash category ID sudah ada dalam daftar")
} else {
trashCategoryMap[item.TrashCategoryID] = true
}
}
if item.Price <= 0 {
errors[fieldPrefix+".price"] = append(errors[fieldPrefix+".price"], "Harga harus lebih dari 0")
}
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *UpdateCollectorRequest) ValidateUpdateCollectorRequest() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.JobStatus != "" {
r.JobStatus = strings.ToLower(strings.TrimSpace(r.JobStatus))
if r.JobStatus != "active" && r.JobStatus != "inactive" {
errors["job_status"] = append(errors["job_status"], "Job status hanya boleh 'active' atau 'inactive'")
}
}
if r.AddressID != "" && strings.TrimSpace(r.AddressID) == "" {
errors["address_id"] = append(errors["address_id"], "Address ID tidak boleh kosong jika disediakan")
}
if len(r.AvailableTrashItems) > 0 {
trashCategoryMap := make(map[string]bool)
for i, item := range r.AvailableTrashItems {
fieldPrefix := fmt.Sprintf("available_trash_items[%d]", i)
if strings.TrimSpace(item.TrashCategoryID) == "" {
errors[fieldPrefix+".trash_category_id"] = append(errors[fieldPrefix+".trash_category_id"], "Trash category ID tidak boleh kosong")
} else {
if trashCategoryMap[item.TrashCategoryID] {
errors[fieldPrefix+".trash_category_id"] = append(errors[fieldPrefix+".trash_category_id"], "Trash category ID sudah ada dalam daftar")
} else {
trashCategoryMap[item.TrashCategoryID] = true
}
}
if item.Price <= 0 {
errors[fieldPrefix+".price"] = append(errors[fieldPrefix+".price"], "Harga harus lebih dari 0")
}
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *CreateAvailableTrashRequest) ValidateCreateAvailableTrashRequest() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.TrashCategoryID) == "" {
errors["trash_category_id"] = append(errors["trash_category_id"], "Trash category ID tidak boleh kosong")
}
if r.Price <= 0 {
errors["price"] = append(errors["price"], "Harga harus lebih dari 0")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *UpdateAvailableTrashRequest) ValidateUpdateAvailableTrashRequest() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.TrashCategoryID != "" && strings.TrimSpace(r.TrashCategoryID) == "" {
errors["trash_category_id"] = append(errors["trash_category_id"], "Trash category ID tidak boleh kosong jika disediakan")
}
if r.Price != 0 && r.Price <= 0 {
errors["price"] = append(errors["price"], "Harga harus lebih dari 0 jika disediakan")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *CreateCollectorRequest) IsValidJobStatus(status string) bool {
status = strings.ToLower(strings.TrimSpace(status))
return status == "active" || status == "inactive"
}
func (r *UpdateCollectorRequest) IsValidJobStatus(status string) bool {
status = strings.ToLower(strings.TrimSpace(status))
return status == "active" || status == "inactive"
}
func (r *CollectorResponse) FormatTimestamp(t time.Time) string {
return t.Format(time.RFC3339)
}
func (r *CreateCollectorRequest) SetDefaults() {
if r.JobStatus == "" {
r.JobStatus = "inactive"
} else {
r.JobStatus = strings.ToLower(strings.TrimSpace(r.JobStatus))
}
}
func (r *UpdateCollectorRequest) NormalizeJobStatus() {
if r.JobStatus != "" {
r.JobStatus = strings.ToLower(strings.TrimSpace(r.JobStatus))
}
}
type BulkUpdateAvailableTrashRequest struct {
CollectorID string `json:"collector_id" binding:"required"`
AvailableTrashItems []CreateAvailableTrashRequest `json:"available_trash_items" binding:"required"`
}
func (r *BulkUpdateAvailableTrashRequest) ValidateBulkUpdateAvailableTrashRequest() (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(r.AvailableTrashItems) == 0 {
errors["available_trash_items"] = append(errors["available_trash_items"], "Minimal harus ada 1 item sampah")
} else {
trashCategoryMap := make(map[string]bool)
for i, item := range r.AvailableTrashItems {
fieldPrefix := fmt.Sprintf("available_trash_items[%d]", i)
if strings.TrimSpace(item.TrashCategoryID) == "" {
errors[fieldPrefix+".trash_category_id"] = append(errors[fieldPrefix+".trash_category_id"], "Trash category ID tidak boleh kosong")
} else {
if trashCategoryMap[item.TrashCategoryID] {
errors[fieldPrefix+".trash_category_id"] = append(errors[fieldPrefix+".trash_category_id"], "Trash category ID sudah ada dalam daftar")
} else {
trashCategoryMap[item.TrashCategoryID] = true
}
}
if item.Price <= 0 {
errors[fieldPrefix+".price"] = append(errors[fieldPrefix+".price"], "Harga harus lebih dari 0")
}
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1 +1,360 @@
package collector
import (
"rijig/middleware"
"rijig/utils"
"strconv"
"strings"
"github.com/gofiber/fiber/v2"
)
type CollectorHandler struct {
collectorService CollectorService
}
func NewCollectorHandler(collectorService CollectorService) *CollectorHandler {
return &CollectorHandler{
collectorService: collectorService,
}
}
func (h *CollectorHandler) CreateCollector(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
var req CreateCollectorRequest
if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request format")
}
errors, isValid := req.ValidateCreateCollectorRequest()
if !isValid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", errors)
}
req.SetDefaults()
collector, err := h.collectorService.CreateCollector(c.Context(), &req, claims.UserID)
if err != nil {
if strings.Contains(err.Error(), "already exists") {
return utils.BadRequest(c, err.Error())
}
return utils.InternalServerError(c, "Failed to create collector")
}
return utils.CreateSuccessWithData(c, "Collector created successfully", collector)
}
func (h *CollectorHandler) GetCollectorByID(c *fiber.Ctx) error {
id := c.Params("id")
if strings.TrimSpace(id) == "" {
return utils.BadRequest(c, "Collector ID is required")
}
collector, err := h.collectorService.GetCollectorByID(c.Context(), id)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found")
}
return utils.InternalServerError(c, "Failed to get collector")
}
return utils.SuccessWithData(c, "Collector retrieved successfully", collector)
}
func (h *CollectorHandler) GetCollectorByUserID(c *fiber.Ctx) error {
// userID := c.Params("userID")
// if strings.TrimSpace(userID) == "" {
// return utils.BadRequest(c, "User ID is required")
// }
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
collector, err := h.collectorService.GetCollectorByUserID(c.Context(), claims.UserID)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found for this user")
}
return utils.InternalServerError(c, "Failed to get collector")
}
return utils.SuccessWithData(c, "Collector retrieved successfully", collector)
}
func (h *CollectorHandler) UpdateCollector(c *fiber.Ctx) error {
/* id := c.Params("id")
if strings.TrimSpace(id) == "" {
return utils.BadRequest(c, "Collector ID is required")
} */
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
var req UpdateCollectorRequest
if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request format")
}
errors, isValid := req.ValidateUpdateCollectorRequest()
if !isValid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", errors)
}
req.NormalizeJobStatus()
collector, err := h.collectorService.UpdateCollector(c.Context(), claims.UserID, &req)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found")
}
return utils.InternalServerError(c, "Failed to update collector")
}
return utils.SuccessWithData(c, "Collector updated successfully", collector)
}
func (h *CollectorHandler) DeleteCollector(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
err = h.collectorService.DeleteCollector(c.Context(), claims.UserID)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found")
}
return utils.InternalServerError(c, "Failed to delete collector")
}
return utils.Success(c, "Collector deleted successfully")
}
func (h *CollectorHandler) ListCollectors(c *fiber.Ctx) error {
limit, offset, page := h.parsePaginationParams(c)
collectors, total, err := h.collectorService.ListCollectors(c.Context(), limit, offset)
if err != nil {
return utils.InternalServerError(c, "Failed to get collectors")
}
responseData := map[string]interface{}{
"collectors": collectors,
"total": total,
}
return utils.SuccessWithPagination(c, "Collectors retrieved successfully", responseData, page, limit)
}
func (h *CollectorHandler) GetActiveCollectors(c *fiber.Ctx) error {
limit, offset, page := h.parsePaginationParams(c)
collectors, total, err := h.collectorService.GetActiveCollectors(c.Context(), limit, offset)
if err != nil {
return utils.InternalServerError(c, "Failed to get active collectors")
}
responseData := map[string]interface{}{
"collectors": collectors,
"total": total,
}
return utils.SuccessWithPagination(c, "Active collectors retrieved successfully", responseData, page, limit)
}
func (h *CollectorHandler) GetCollectorsByAddress(c *fiber.Ctx) error {
addressID := c.Params("addressID")
if strings.TrimSpace(addressID) == "" {
return utils.BadRequest(c, "Address ID is required")
}
limit, offset, page := h.parsePaginationParams(c)
collectors, total, err := h.collectorService.GetCollectorsByAddress(c.Context(), addressID, limit, offset)
if err != nil {
return utils.InternalServerError(c, "Failed to get collectors by address")
}
responseData := map[string]interface{}{
"collectors": collectors,
"total": total,
"address_id": addressID,
}
return utils.SuccessWithPagination(c, "Collectors by address retrieved successfully", responseData, page, limit)
}
func (h *CollectorHandler) GetCollectorsByTrashCategory(c *fiber.Ctx) error {
trashCategoryID := c.Params("trashCategoryID")
if strings.TrimSpace(trashCategoryID) == "" {
return utils.BadRequest(c, "Trash category ID is required")
}
limit, offset, page := h.parsePaginationParams(c)
collectors, total, err := h.collectorService.GetCollectorsByTrashCategory(c.Context(), trashCategoryID, limit, offset)
if err != nil {
return utils.InternalServerError(c, "Failed to get collectors by trash category")
}
responseData := map[string]interface{}{
"collectors": collectors,
"total": total,
"trash_category_id": trashCategoryID,
}
return utils.SuccessWithPagination(c, "Collectors by trash category retrieved successfully", responseData, page, limit)
}
func (h *CollectorHandler) UpdateJobStatus(c *fiber.Ctx) error {
id := c.Params("id")
if strings.TrimSpace(id) == "" {
return utils.BadRequest(c, "Collector ID is required")
}
var req struct {
JobStatus string `json:"job_status" binding:"required"`
}
if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request format")
}
if strings.TrimSpace(req.JobStatus) == "" {
return utils.BadRequest(c, "Job status is required")
}
jobStatus := strings.ToLower(strings.TrimSpace(req.JobStatus))
validStatuses := []string{"active", "inactive", "busy"}
if !h.isValidJobStatus(jobStatus, validStatuses) {
return utils.BadRequest(c, "Invalid job status. Valid statuses: active, inactive, busy")
}
err := h.collectorService.UpdateJobStatus(c.Context(), id, jobStatus)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found")
}
return utils.InternalServerError(c, "Failed to update job status")
}
return utils.Success(c, "Job status updated successfully")
}
func (h *CollectorHandler) UpdateRating(c *fiber.Ctx) error {
id := c.Params("id")
if strings.TrimSpace(id) == "" {
return utils.BadRequest(c, "Collector ID is required")
}
var req struct {
Rating float32 `json:"rating" binding:"required"`
}
if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request format")
}
if req.Rating < 1.0 || req.Rating > 5.0 {
return utils.BadRequest(c, "Rating must be between 1.0 and 5.0")
}
err := h.collectorService.UpdateRating(c.Context(), id, req.Rating)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found")
}
return utils.InternalServerError(c, "Failed to update rating")
}
return utils.Success(c, "Rating updated successfully")
}
func (h *CollectorHandler) UpdateAvailableTrash(c *fiber.Ctx) error {
id := c.Params("id")
if strings.TrimSpace(id) == "" {
return utils.BadRequest(c, "Collector ID is required")
}
var req BulkUpdateAvailableTrashRequest
req.CollectorID = id
if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request format")
}
errors, isValid := req.ValidateBulkUpdateAvailableTrashRequest()
if !isValid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", errors)
}
err := h.collectorService.UpdateAvailableTrash(c.Context(), id, req.AvailableTrashItems)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Collector not found")
}
return utils.InternalServerError(c, "Failed to update available trash")
}
return utils.Success(c, "Available trash updated successfully")
}
func (h *CollectorHandler) parsePaginationParams(c *fiber.Ctx) (limit, offset, page int) {
limitStr := c.Query("limit", "10")
limit, err := strconv.Atoi(limitStr)
if err != nil || limit <= 0 {
limit = 10
}
if limit > 100 {
limit = 100
}
pageStr := c.Query("page", "1")
page, err = strconv.Atoi(pageStr)
if err != nil || page <= 0 {
page = 1
}
offset = (page - 1) * limit
return limit, offset, page
}
func (h *CollectorHandler) isValidJobStatus(status string, validStatuses []string) bool {
for _, validStatus := range validStatuses {
if status == validStatus {
return true
}
}
return false
}
func (h *CollectorHandler) RegisterRoutes(app *fiber.App) {
collectors := app.Group("/api/v1/collectors")
collectors.Post("/", h.CreateCollector)
collectors.Get("/:id", h.GetCollectorByID)
collectors.Put("/:id", h.UpdateCollector)
collectors.Delete("/:id", h.DeleteCollector)
collectors.Get("/", h.ListCollectors)
collectors.Get("/active", h.GetActiveCollectors)
collectors.Get("/user/:userID", h.GetCollectorByUserID)
collectors.Get("/address/:addressID", h.GetCollectorsByAddress)
collectors.Get("/trash-category/:trashCategoryID", h.GetCollectorsByTrashCategory)
collectors.Patch("/:id/job-status", h.UpdateJobStatus)
collectors.Patch("/:id/rating", h.UpdateRating)
collectors.Put("/:id/available-trash", h.UpdateAvailableTrash)
}

View File

@ -1 +1,337 @@
package collector
import (
"context"
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type CollectorRepository interface {
Create(ctx context.Context, collector *model.Collector) error
GetByID(ctx context.Context, id string) (*model.Collector, error)
GetByUserID(ctx context.Context, userID string) (*model.Collector, error)
Update(ctx context.Context, collector *model.Collector) error
Delete(ctx context.Context, UserID string) error
List(ctx context.Context, limit, offset int) ([]*model.Collector, int64, error)
GetActiveCollectors(ctx context.Context, limit, offset int) ([]*model.Collector, int64, error)
GetCollectorsByAddress(ctx context.Context, addressID string, limit, offset int) ([]*model.Collector, int64, error)
GetCollectorsByTrashCategory(ctx context.Context, trashCategoryID string, limit, offset int) ([]*model.Collector, int64, error)
UpdateJobStatus(ctx context.Context, id string, jobStatus string) error
UpdateRating(ctx context.Context, id string, rating float32) error
CreateAvailableTrash(ctx context.Context, availableTrash *model.AvaibleTrashByCollector) error
GetAvailableTrashByCollectorID(ctx context.Context, collectorID string) ([]*model.AvaibleTrashByCollector, error)
UpdateAvailableTrash(ctx context.Context, availableTrash *model.AvaibleTrashByCollector) error
DeleteAvailableTrash(ctx context.Context, id string) error
BulkCreateAvailableTrash(ctx context.Context, availableTrashList []*model.AvaibleTrashByCollector) error
BulkUpdateAvailableTrash(ctx context.Context, collectorID string, availableTrashList []*model.AvaibleTrashByCollector) error
DeleteAvailableTrashByCollectorID(ctx context.Context, collectorID string) error
WithTx(tx *gorm.DB) CollectorRepository
}
type collectorRepository struct {
db *gorm.DB
}
func NewCollectorRepository(db *gorm.DB) CollectorRepository {
return &collectorRepository{
db: db,
}
}
func (r *collectorRepository) WithTx(tx *gorm.DB) CollectorRepository {
return &collectorRepository{
db: tx,
}
}
func (r *collectorRepository) Create(ctx context.Context, collector *model.Collector) error {
if err := r.db.WithContext(ctx).Create(collector).Error; err != nil {
return fmt.Errorf("failed to create collector: %w", err)
}
return nil
}
func (r *collectorRepository) GetByID(ctx context.Context, id string) (*model.Collector, error) {
var collector model.Collector
err := r.db.WithContext(ctx).
Preload("Address").
Preload("AvaibleTrashByCollector").
Preload("AvaibleTrashByCollector.TrashCategory").
Where("id = ?", id).
First(&collector).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("collector with id %s not found", id)
}
return nil, fmt.Errorf("failed to get collector by id: %w", err)
}
return &collector, nil
}
func (r *collectorRepository) GetByUserID(ctx context.Context, userID string) (*model.Collector, error) {
var collector model.Collector
err := r.db.WithContext(ctx).
Preload("Address").
Preload("AvaibleTrashByCollector").
Preload("AvaibleTrashByCollector.TrashCategory").
Where("user_id = ?", userID).
First(&collector).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("collector with user_id %s not found", userID)
}
return nil, fmt.Errorf("failed to get collector by user_id: %w", err)
}
return &collector, nil
}
func (r *collectorRepository) Update(ctx context.Context, collector *model.Collector) error {
if err := r.db.WithContext(ctx).Save(collector).Error; err != nil {
return fmt.Errorf("failed to update collector: %w", err)
}
return nil
}
func (r *collectorRepository) Delete(ctx context.Context, UserID string) error {
result := r.db.WithContext(ctx).Delete(&model.Collector{}, "user_id = ?", UserID)
if result.Error != nil {
return fmt.Errorf("failed to delete collector: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("collector with user_id %s not found", UserID)
}
return nil
}
func (r *collectorRepository) List(ctx context.Context, limit, offset int) ([]*model.Collector, int64, error) {
var collectors []*model.Collector
var total int64
if err := r.db.WithContext(ctx).Model(&model.Collector{}).Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count collectors: %w", err)
}
err := r.db.WithContext(ctx).
Preload("Address").
Preload("AvaibleTrashByCollector").
Preload("AvaibleTrashByCollector.TrashCategory").
Limit(limit).
Offset(offset).
Find(&collectors).Error
if err != nil {
return nil, 0, fmt.Errorf("failed to list collectors: %w", err)
}
return collectors, total, nil
}
func (r *collectorRepository) GetActiveCollectors(ctx context.Context, limit, offset int) ([]*model.Collector, int64, error) {
var collectors []*model.Collector
var total int64
query := r.db.WithContext(ctx).Where("job_status = ?", "active")
if err := query.Model(&model.Collector{}).Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count active collectors: %w", err)
}
err := query.
Preload("Address").
Preload("AvaibleTrashByCollector").
Preload("AvaibleTrashByCollector.TrashCategory").
Limit(limit).
Offset(offset).
Find(&collectors).Error
if err != nil {
return nil, 0, fmt.Errorf("failed to get active collectors: %w", err)
}
return collectors, total, nil
}
func (r *collectorRepository) GetCollectorsByAddress(ctx context.Context, addressID string, limit, offset int) ([]*model.Collector, int64, error) {
var collectors []*model.Collector
var total int64
query := r.db.WithContext(ctx).Where("address_id = ?", addressID)
if err := query.Model(&model.Collector{}).Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count collectors by address: %w", err)
}
err := query.
Preload("Address").
Preload("AvaibleTrashByCollector").
Preload("AvaibleTrashByCollector.TrashCategory").
Limit(limit).
Offset(offset).
Find(&collectors).Error
if err != nil {
return nil, 0, fmt.Errorf("failed to get collectors by address: %w", err)
}
return collectors, total, nil
}
func (r *collectorRepository) GetCollectorsByTrashCategory(ctx context.Context, trashCategoryID string, limit, offset int) ([]*model.Collector, int64, error) {
var collectors []*model.Collector
var total int64
subQuery := r.db.WithContext(ctx).
Table("avaible_trash_by_collectors").
Select("collector_id").
Where("trash_category_id = ?", trashCategoryID)
query := r.db.WithContext(ctx).
Where("id IN (?)", subQuery)
if err := query.Model(&model.Collector{}).Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count collectors by trash category: %w", err)
}
err := query.
Preload("Address").
Preload("AvaibleTrashByCollector").
Preload("AvaibleTrashByCollector.TrashCategory").
Limit(limit).
Offset(offset).
Find(&collectors).Error
if err != nil {
return nil, 0, fmt.Errorf("failed to get collectors by trash category: %w", err)
}
return collectors, total, nil
}
func (r *collectorRepository) UpdateJobStatus(ctx context.Context, id string, jobStatus string) error {
result := r.db.WithContext(ctx).
Model(&model.Collector{}).
Where("id = ?", id).
Update("job_status", jobStatus)
if result.Error != nil {
return fmt.Errorf("failed to update job status: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("collector with id %s not found", id)
}
return nil
}
func (r *collectorRepository) UpdateRating(ctx context.Context, id string, rating float32) error {
result := r.db.WithContext(ctx).
Model(&model.Collector{}).
Where("id = ?", id).
Update("rating", rating)
if result.Error != nil {
return fmt.Errorf("failed to update rating: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("collector with id %s not found", id)
}
return nil
}
func (r *collectorRepository) CreateAvailableTrash(ctx context.Context, availableTrash *model.AvaibleTrashByCollector) error {
if err := r.db.WithContext(ctx).Create(availableTrash).Error; err != nil {
return fmt.Errorf("failed to create available trash: %w", err)
}
return nil
}
func (r *collectorRepository) GetAvailableTrashByCollectorID(ctx context.Context, collectorID string) ([]*model.AvaibleTrashByCollector, error) {
var availableTrash []*model.AvaibleTrashByCollector
err := r.db.WithContext(ctx).
Preload("TrashCategory").
Where("collector_id = ?", collectorID).
Find(&availableTrash).Error
if err != nil {
return nil, fmt.Errorf("failed to get available trash by collector id: %w", err)
}
return availableTrash, nil
}
func (r *collectorRepository) UpdateAvailableTrash(ctx context.Context, availableTrash *model.AvaibleTrashByCollector) error {
if err := r.db.WithContext(ctx).Save(availableTrash).Error; err != nil {
return fmt.Errorf("failed to update available trash: %w", err)
}
return nil
}
func (r *collectorRepository) DeleteAvailableTrash(ctx context.Context, id string) error {
result := r.db.WithContext(ctx).Delete(&model.AvaibleTrashByCollector{}, "id = ?", id)
if result.Error != nil {
return fmt.Errorf("failed to delete available trash: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("available trash with id %s not found", id)
}
return nil
}
func (r *collectorRepository) BulkCreateAvailableTrash(ctx context.Context, availableTrashList []*model.AvaibleTrashByCollector) error {
if len(availableTrashList) == 0 {
return nil
}
if err := r.db.WithContext(ctx).CreateInBatches(availableTrashList, 100).Error; err != nil {
return fmt.Errorf("failed to bulk create available trash: %w", err)
}
return nil
}
func (r *collectorRepository) BulkUpdateAvailableTrash(ctx context.Context, collectorID string, availableTrashList []*model.AvaibleTrashByCollector) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("collector_id = ?", collectorID).Delete(&model.AvaibleTrashByCollector{}).Error; err != nil {
return fmt.Errorf("failed to delete existing available trash: %w", err)
}
if len(availableTrashList) > 0 {
for _, item := range availableTrashList {
item.CollectorID = collectorID
}
if err := tx.CreateInBatches(availableTrashList, 100).Error; err != nil {
return fmt.Errorf("failed to create new available trash: %w", err)
}
}
return nil
})
}
func (r *collectorRepository) DeleteAvailableTrashByCollectorID(ctx context.Context, collectorID string) error {
if err := r.db.WithContext(ctx).Where("collector_id = ?", collectorID).Delete(&model.AvaibleTrashByCollector{}).Error; err != nil {
return fmt.Errorf("failed to delete available trash by collector id: %w", err)
}
return nil
}

View File

@ -1 +1,349 @@
package collector
import (
"context"
"fmt"
"rijig/internal/address"
"rijig/internal/trash"
"rijig/model"
"strings"
"time"
"gorm.io/gorm"
)
type CollectorService interface {
CreateCollector(ctx context.Context, req *CreateCollectorRequest, UserID string) (*CollectorResponse, error)
GetCollectorByID(ctx context.Context, id string) (*CollectorResponse, error)
GetCollectorByUserID(ctx context.Context, userID string) (*CollectorResponse, error)
UpdateCollector(ctx context.Context, UserID string, req *UpdateCollectorRequest) (*CollectorResponse, error)
DeleteCollector(ctx context.Context, UserID string) error
ListCollectors(ctx context.Context, limit, offset int) ([]*CollectorResponse, int64, error)
GetActiveCollectors(ctx context.Context, limit, offset int) ([]*CollectorResponse, int64, error)
GetCollectorsByAddress(ctx context.Context, addressID string, limit, offset int) ([]*CollectorResponse, int64, error)
GetCollectorsByTrashCategory(ctx context.Context, trashCategoryID string, limit, offset int) ([]*CollectorResponse, int64, error)
UpdateJobStatus(ctx context.Context, id string, jobStatus string) error
UpdateRating(ctx context.Context, id string, rating float32) error
UpdateAvailableTrash(ctx context.Context, collectorID string, availableTrashItems []CreateAvailableTrashRequest) error
}
type collectorService struct {
collectorRepo CollectorRepository
db *gorm.DB
}
func NewCollectorService(collectorRepo CollectorRepository, db *gorm.DB) CollectorService {
return &collectorService{
collectorRepo: collectorRepo,
db: db,
}
}
func (s *collectorService) CreateCollector(ctx context.Context, req *CreateCollectorRequest, UserID string) (*CollectorResponse, error) {
existingCollector, err := s.collectorRepo.GetByUserID(ctx, UserID)
if err != nil && !strings.Contains(err.Error(), "not found") {
return nil, fmt.Errorf("failed to check existing collector: %w", err)
}
if existingCollector != nil {
return nil, fmt.Errorf("collector already exists for user_id: %s", req.UserID)
}
collector := &model.Collector{
UserID: UserID,
JobStatus: "inactive",
AddressID: req.AddressID,
Rating: 5.0,
}
if req.JobStatus != "" {
collector.JobStatus = req.JobStatus
}
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
collectorRepoTx := s.collectorRepo.WithTx(tx)
if err := collectorRepoTx.Create(ctx, collector); err != nil {
return fmt.Errorf("failed to create collector: %w", err)
}
if len(req.AvailableTrashItems) > 0 {
availableTrashList := s.buildAvailableTrashList(collector.ID, req.AvailableTrashItems)
if err := collectorRepoTx.BulkCreateAvailableTrash(ctx, availableTrashList); err != nil {
return fmt.Errorf("failed to create available trash items: %w", err)
}
}
return nil
})
if err != nil {
return nil, err
}
createdCollector, err := s.collectorRepo.GetByID(ctx, collector.ID)
if err != nil {
return nil, fmt.Errorf("failed to fetch created collector: %w", err)
}
return s.toCollectorResponse(createdCollector), nil
}
func (s *collectorService) GetCollectorByID(ctx context.Context, id string) (*CollectorResponse, error) {
collector, err := s.collectorRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
return s.toCollectorResponse(collector), nil
}
func (s *collectorService) GetCollectorByUserID(ctx context.Context, userID string) (*CollectorResponse, error) {
collector, err := s.collectorRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, err
}
return s.toCollectorResponse(collector), nil
}
func (s *collectorService) UpdateCollector(ctx context.Context, UserID string, req *UpdateCollectorRequest) (*CollectorResponse, error) {
collector, err := s.collectorRepo.GetByUserID(ctx, UserID)
if err != nil {
return nil, fmt.Errorf("failed to get collector: %w", err)
}
needsUpdate := s.checkCollectorNeedsUpdate(collector, req)
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
collectorRepoTx := s.collectorRepo.WithTx(tx)
if needsUpdate {
s.applyCollectorUpdates(collector, req)
collector.UpdatedAt = time.Now()
if err := collectorRepoTx.Update(ctx, collector); err != nil {
return fmt.Errorf("failed to update collector: %w", err)
}
}
if len(req.AvailableTrashItems) > 0 {
availableTrashList := s.buildAvailableTrashList(collector.ID, req.AvailableTrashItems)
if err := collectorRepoTx.BulkUpdateAvailableTrash(ctx, collector.ID, availableTrashList); err != nil {
return fmt.Errorf("failed to update available trash items: %w", err)
}
}
return nil
})
if err != nil {
return nil, err
}
updatedCollector, err := s.collectorRepo.GetByUserID(ctx, UserID)
if err != nil {
return nil, fmt.Errorf("failed to fetch updated collector: %w", err)
}
return s.toCollectorResponse(updatedCollector), nil
}
func (s *collectorService) DeleteCollector(ctx context.Context, UserID string) error {
_, err := s.collectorRepo.GetByUserID(ctx, UserID)
if err != nil {
return fmt.Errorf("collector not found: %w", err)
}
if err := s.collectorRepo.Delete(ctx, UserID); err != nil {
return fmt.Errorf("failed to delete collector: %w", err)
}
return nil
}
func (s *collectorService) ListCollectors(ctx context.Context, limit, offset int) ([]*CollectorResponse, int64, error) {
limit, offset = s.normalizePagination(limit, offset)
collectors, total, err := s.collectorRepo.List(ctx, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to list collectors: %w", err)
}
return s.buildCollectorResponseList(collectors), total, nil
}
func (s *collectorService) GetActiveCollectors(ctx context.Context, limit, offset int) ([]*CollectorResponse, int64, error) {
limit, offset = s.normalizePagination(limit, offset)
collectors, total, err := s.collectorRepo.GetActiveCollectors(ctx, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to get active collectors: %w", err)
}
return s.buildCollectorResponseList(collectors), total, nil
}
func (s *collectorService) GetCollectorsByAddress(ctx context.Context, addressID string, limit, offset int) ([]*CollectorResponse, int64, error) {
limit, offset = s.normalizePagination(limit, offset)
collectors, total, err := s.collectorRepo.GetCollectorsByAddress(ctx, addressID, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to get collectors by address: %w", err)
}
return s.buildCollectorResponseList(collectors), total, nil
}
func (s *collectorService) GetCollectorsByTrashCategory(ctx context.Context, trashCategoryID string, limit, offset int) ([]*CollectorResponse, int64, error) {
limit, offset = s.normalizePagination(limit, offset)
collectors, total, err := s.collectorRepo.GetCollectorsByTrashCategory(ctx, trashCategoryID, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to get collectors by trash category: %w", err)
}
return s.buildCollectorResponseList(collectors), total, nil
}
func (s *collectorService) UpdateJobStatus(ctx context.Context, id string, jobStatus string) error {
if err := s.collectorRepo.UpdateJobStatus(ctx, id, jobStatus); err != nil {
return fmt.Errorf("failed to update job status: %w", err)
}
return nil
}
func (s *collectorService) UpdateRating(ctx context.Context, id string, rating float32) error {
if err := s.collectorRepo.UpdateRating(ctx, id, rating); err != nil {
return fmt.Errorf("failed to update rating: %w", err)
}
return nil
}
func (s *collectorService) UpdateAvailableTrash(ctx context.Context, collectorID string, availableTrashItems []CreateAvailableTrashRequest) error {
availableTrashList := s.buildAvailableTrashList(collectorID, availableTrashItems)
if err := s.collectorRepo.BulkUpdateAvailableTrash(ctx, collectorID, availableTrashList); err != nil {
return fmt.Errorf("failed to update available trash: %w", err)
}
return nil
}
func (s *collectorService) buildAvailableTrashList(collectorID string, items []CreateAvailableTrashRequest) []*model.AvaibleTrashByCollector {
availableTrashList := make([]*model.AvaibleTrashByCollector, 0, len(items))
for _, item := range items {
availableTrash := &model.AvaibleTrashByCollector{
CollectorID: collectorID,
TrashCategoryID: item.TrashCategoryID,
Price: item.Price,
}
availableTrashList = append(availableTrashList, availableTrash)
}
return availableTrashList
}
func (s *collectorService) checkCollectorNeedsUpdate(collector *model.Collector, req *UpdateCollectorRequest) bool {
if req.JobStatus != "" && req.JobStatus != collector.JobStatus {
return true
}
if req.AddressID != "" && req.AddressID != collector.AddressID {
return true
}
return false
}
func (s *collectorService) applyCollectorUpdates(collector *model.Collector, req *UpdateCollectorRequest) {
if req.JobStatus != "" {
collector.JobStatus = req.JobStatus
}
if req.AddressID != "" {
collector.AddressID = req.AddressID
}
}
func (s *collectorService) normalizePagination(limit, offset int) (int, int) {
if limit <= 0 {
limit = 10
}
if offset < 0 {
offset = 0
}
if limit > 100 {
limit = 100
}
return limit, offset
}
func (s *collectorService) buildCollectorResponseList(collectors []*model.Collector) []*CollectorResponse {
responses := make([]*CollectorResponse, 0, len(collectors))
for _, collector := range collectors {
responses = append(responses, s.toCollectorResponse(collector))
}
return responses
}
func (s *collectorService) toCollectorResponse(collector *model.Collector) *CollectorResponse {
response := &CollectorResponse{
ID: collector.ID,
UserID: collector.UserID,
JobStatus: collector.JobStatus,
Rating: collector.Rating,
AddressID: collector.AddressID,
AvailableTrash: make([]AvailableTrashResponse, 0),
CreatedAt: collector.CreatedAt.Format(time.RFC3339),
UpdatedAt: collector.UpdatedAt.Format(time.RFC3339),
}
if collector.Address.ID != "" {
response.Address = &address.AddressResponseDTO{
ID: collector.Address.ID,
UserID: collector.Address.UserID,
Province: collector.Address.Province,
Regency: collector.Address.Regency,
District: collector.Address.District,
Village: collector.Address.Village,
PostalCode: collector.Address.PostalCode,
Detail: collector.Address.Detail,
Latitude: collector.Address.Latitude,
Longitude: collector.Address.Longitude,
CreatedAt: collector.Address.CreatedAt.Format(time.RFC3339),
UpdatedAt: collector.Address.UpdatedAt.Format(time.RFC3339),
}
}
for _, availableTrash := range collector.AvaibleTrashByCollector {
trashResponse := AvailableTrashResponse{
ID: availableTrash.ID,
CollectorID: availableTrash.CollectorID,
TrashCategoryID: availableTrash.TrashCategoryID,
Price: availableTrash.Price,
}
if availableTrash.TrashCategory.ID != "" {
trashResponse.TrashCategory = &trash.ResponseTrashCategoryDTO{
ID: availableTrash.TrashCategory.ID,
TrashName: availableTrash.TrashCategory.Name,
TrashIcon: availableTrash.TrashCategory.IconTrash,
EstimatedPrice: availableTrash.TrashCategory.EstimatedPrice,
Variety: availableTrash.TrashCategory.Variety,
CreatedAt: availableTrash.TrashCategory.CreatedAt.Format(time.RFC3339),
UpdatedAt: availableTrash.TrashCategory.UpdatedAt.Format(time.RFC3339),
}
}
response.AvailableTrash = append(response.AvailableTrash, trashResponse)
}
return response
}

View File

@ -1,176 +0,0 @@
package handler
/*
import (
"fmt"
"log"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AboutHandler struct {
AboutService services.AboutService
}
func NewAboutHandler(aboutService services.AboutService) *AboutHandler {
return &AboutHandler{
AboutService: aboutService,
}
}
func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error {
var request dto.RequestAboutDTO
if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err)
return utils.ResponseErrorData(c, "Invalid input data")
}
aboutCoverImage, err := c.FormFile("cover_image")
if err != nil {
log.Printf("Error retrieving cover image about from request: %v", err)
return utils.ErrorResponse(c, "cover_iamge is required")
}
response, err := h.AboutService.CreateAbout(request, aboutCoverImage)
if err != nil {
log.Printf("Error creating About: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to create About: %v", err))
}
return utils.SuccessResponse(c, response, "Successfully created About")
}
func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error {
id := c.Params("id")
var request dto.RequestAboutDTO
if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err)
return utils.ErrorResponse(c, "Invalid input data")
}
aboutCoverImage, err := c.FormFile("cover_image")
if err != nil {
log.Printf("Error retrieving cover image about from request: %v", err)
return utils.ErrorResponse(c, "cover_iamge is required")
}
response, err := h.AboutService.UpdateAbout(id, request, aboutCoverImage)
if err != nil {
log.Printf("Error updating About: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to update About: %v", err))
}
return utils.SuccessResponse(c, response, "Successfully updated About")
}
func (h *AboutHandler) GetAllAbout(c *fiber.Ctx) error {
response, err := h.AboutService.GetAllAbout()
if err != nil {
log.Printf("Error fetching all About: %v", err)
return utils.ErrorResponse(c, "Failed to fetch About list")
}
return utils.PaginatedResponse(c, response, 1, len(response), len(response), "Successfully fetched About list")
}
func (h *AboutHandler) GetAboutByID(c *fiber.Ctx) error {
id := c.Params("id")
response, err := h.AboutService.GetAboutByID(id)
if err != nil {
log.Printf("Error fetching About by ID: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to fetch About by ID: %v", err))
}
return utils.SuccessResponse(c, response, "Successfully fetched About")
}
func (h *AboutHandler) GetAboutDetailById(c *fiber.Ctx) error {
id := c.Params("id")
response, err := h.AboutService.GetAboutDetailById(id)
if err != nil {
log.Printf("Error fetching About detail by ID: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to fetch About by ID: %v", err))
}
return utils.SuccessResponse(c, response, "Successfully fetched About")
}
func (h *AboutHandler) DeleteAbout(c *fiber.Ctx) error {
id := c.Params("id")
if err := h.AboutService.DeleteAbout(id); err != nil {
log.Printf("Error deleting About: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to delete About: %v", err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Successfully deleted About")
}
func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error {
var request dto.RequestAboutDetailDTO
if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err)
return utils.ErrorResponse(c, "Invalid input data")
}
errors, valid := request.ValidateAboutDetail()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
aboutDetailImage, err := c.FormFile("image_detail")
if err != nil {
log.Printf("Error retrieving image detail from request: %v", err)
return utils.ErrorResponse(c, "image_detail is required")
}
response, err := h.AboutService.CreateAboutDetail(request, aboutDetailImage)
if err != nil {
log.Printf("Error creating AboutDetail: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to create AboutDetail: %v", err))
}
return utils.SuccessResponse(c, response, "Successfully created AboutDetail")
}
func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error {
id := c.Params("id")
var request dto.RequestAboutDetailDTO
if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err)
return utils.ErrorResponse(c, "Invalid input data")
}
aboutDetailImage, err := c.FormFile("image_detail")
if err != nil {
log.Printf("Error retrieving image detail from request: %v", err)
return utils.ErrorResponse(c, "image_detail is required")
}
response, err := h.AboutService.UpdateAboutDetail(id, request, aboutDetailImage)
if err != nil {
log.Printf("Error updating AboutDetail: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to update AboutDetail: %v", err))
}
return utils.SuccessResponse(c, response, "Successfully updated AboutDetail")
}
func (h *AboutHandler) DeleteAboutDetail(c *fiber.Ctx) error {
id := c.Params("id")
if err := h.AboutService.DeleteAboutDetail(id); err != nil {
log.Printf("Error deleting AboutDetail: %v", err)
return utils.ErrorResponse(c, fmt.Sprintf("Failed to delete AboutDetail: %v", err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Successfully deleted AboutDetail")
}
*/

View File

@ -1,93 +0,0 @@
package handler
import (
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AddressHandler struct {
AddressService services.AddressService
}
func NewAddressHandler(addressService services.AddressService) *AddressHandler {
return &AddressHandler{AddressService: addressService}
}
func (h *AddressHandler) CreateAddress(c *fiber.Ctx) error {
var requestAddressDTO dto.CreateAddressDTO
if err := c.BodyParser(&requestAddressDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := requestAddressDTO.ValidateAddress()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
addressResponse, err := h.AddressService.CreateAddress(c.Locals("userID").(string), requestAddressDTO)
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
}
return utils.CreateResponse(c, addressResponse, "user address created successfully")
}
func (h *AddressHandler) GetAddressByUserID(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
addresses, err := h.AddressService.GetAddressByUserID(userID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, addresses, "User addresses fetched successfully")
}
func (h *AddressHandler) GetAddressByID(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
addressID := c.Params("address_id")
address, err := h.AddressService.GetAddressByID(userID, addressID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, address, "Address fetched successfully")
}
func (h *AddressHandler) UpdateAddress(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
addressID := c.Params("address_id")
var addressDTO dto.CreateAddressDTO
if err := c.BodyParser(&addressDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := addressDTO.ValidateAddress()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
updatedAddress, err := h.AddressService.UpdateAddress(userID, addressID, addressDTO)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, updatedAddress, "User address updated successfully")
}
func (h *AddressHandler) DeleteAddress(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
addressID := c.Params("address_id")
err := h.AddressService.DeleteAddress(userID, addressID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusForbidden, err.Error())
}
return utils.SuccessResponse(c, nil, "Address deleted successfully")
}

View File

@ -1,138 +0,0 @@
package handler
import (
"fmt"
"mime/multipart"
"strconv"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type ArticleHandler struct {
ArticleService services.ArticleService
}
func NewArticleHandler(articleService services.ArticleService) *ArticleHandler {
return &ArticleHandler{ArticleService: articleService}
}
func (h *ArticleHandler) CreateArticle(c *fiber.Ctx) error {
var request dto.RequestArticleDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
coverImage, err := c.FormFile("coverImage")
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Cover image is required")
}
articleResponse, err := h.ArticleService.CreateArticle(request, coverImage)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.CreateResponse(c, articleResponse, "Article created successfully")
}
func (h *ArticleHandler) GetAllArticles(c *fiber.Ctx) error {
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil || page < 1 {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil || limit < 1 {
limit = 0
}
articles, totalArticles, err := h.ArticleService.GetAllArticles(page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch articles")
}
fmt.Printf("Total Articles: %d\n", totalArticles)
if page == 0 && limit == 0 {
return utils.NonPaginatedResponse(c, articles, totalArticles, "Articles fetched successfully")
}
return utils.PaginatedResponse(c, articles, page, limit, totalArticles, "Articles fetched successfully")
}
func (h *ArticleHandler) GetArticleByID(c *fiber.Ctx) error {
id := c.Params("article_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Article ID is required")
}
article, err := h.ArticleService.GetArticleByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "Article not found")
}
return utils.SuccessResponse(c, article, "Article fetched successfully")
}
func (h *ArticleHandler) UpdateArticle(c *fiber.Ctx) error {
id := c.Params("article_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Article ID is required")
}
var request dto.RequestArticleDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
var coverImage *multipart.FileHeader
coverImage, err := c.FormFile("coverImage")
if err != nil && err.Error() != "no such file" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Cover image is required")
}
articleResponse, err := h.ArticleService.UpdateArticle(id, request, coverImage)
if err != nil {
if err.Error() == fmt.Sprintf("article with ID %s not found", id) {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, articleResponse, "Article updated successfully")
}
func (h *ArticleHandler) DeleteArticle(c *fiber.Ctx) error {
id := c.Params("article_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Article ID is required")
}
err := h.ArticleService.DeleteArticle(id)
if err != nil {
if err.Error() == fmt.Sprintf("article with ID %s not found", id) {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Article deleted successfully")
}

View File

@ -1,81 +0,0 @@
package handler
/*
import (
"log"
dto "rijig/dto/auth"
services "rijig/internal/services/auth"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AuthAdminHandler struct {
UserService services.AuthAdminService
}
func NewAuthAdminHandler(userService services.AuthAdminService) *AuthAdminHandler {
return &AuthAdminHandler{UserService: userService}
}
func (h *AuthAdminHandler) RegisterAdmin(c *fiber.Ctx) error {
var request dto.RegisterAdminRequest
if err := c.BodyParser(&request); err != nil {
return utils.InternalServerErrorResponse(c, "Failed to parse request body")
}
errors, valid := request.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
user, err := h.UserService.RegisterAdmin(&request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
}
return utils.SuccessResponse(c, user, "Admin registered successfully")
}
func (h *AuthAdminHandler) LoginAdmin(c *fiber.Ctx) error {
var request dto.LoginAdminRequest
if err := c.BodyParser(&request); err != nil {
return utils.InternalServerErrorResponse(c, "Failed to parse request body")
}
loginResponse, err := h.UserService.LoginAdmin(&request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusUnauthorized, err.Error())
}
return utils.SuccessResponse(c, loginResponse, "Login successful")
}
func (h *AuthAdminHandler) LogoutAdmin(c *fiber.Ctx) error {
// Ambil userID dari c.Locals
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
log.Println("Error: UserID is nil or empty")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User not authenticated")
}
// Ambil deviceID dari header atau c.Locals
deviceID, ok := c.Locals("device_id").(string)
if !ok || deviceID == "" {
log.Println("Error: DeviceID is nil or empty")
return utils.ErrorResponse(c, "DeviceID is required")
}
log.Printf("UserID: %s, DeviceID: %s", userID, deviceID)
err := h.UserService.LogoutAdmin(userID, deviceID)
if err != nil {
log.Printf("Error during logout process for user %s: %v", userID, err)
return utils.ErrorResponse(c, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Successfully logged out")
}
*/

View File

@ -1,82 +0,0 @@
package handler
/*
import (
"log"
"rijig/dto"
services "rijig/internal/services/auth"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AuthMasyarakatHandler struct {
authMasyarakatService services.AuthMasyarakatService
}
func NewAuthMasyarakatHandler(authMasyarakatService services.AuthMasyarakatService) *AuthMasyarakatHandler {
return &AuthMasyarakatHandler{authMasyarakatService}
}
func (h *AuthMasyarakatHandler) RegisterOrLoginHandler(c *fiber.Ctx) error {
var req dto.RegisterRequest
if err := c.BodyParser(&req); err != nil {
return utils.ErrorResponse(c, "Invalid request body")
}
if req.Phone == "" {
return utils.ErrorResponse(c, "Phone number is required")
}
if err := h.authMasyarakatService.RegisterOrLogin(&req); err != nil {
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "OTP sent successfully")
}
func (h *AuthMasyarakatHandler) VerifyOTPHandler(c *fiber.Ctx) error {
var req dto.VerifyOTPRequest
if err := c.BodyParser(&req); err != nil {
return utils.ErrorResponse(c, "Invalid request body")
}
if req.OTP == "" {
return utils.ErrorResponse(c, "OTP is required")
}
if req.DeviceID == "" {
return utils.ErrorResponse(c, "DeviceID is required")
}
response, err := h.authMasyarakatService.VerifyOTP(&req)
if err != nil {
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, response, "Registration/Login successful")
}
func (h *AuthMasyarakatHandler) LogoutHandler(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.ErrorResponse(c, "User is not logged in or invalid session")
}
deviceID, ok := c.Locals("device_id").(string)
if !ok || deviceID == "" {
log.Println("Error: DeviceID is nil or empty")
return utils.ErrorResponse(c, "DeviceID is required")
}
err := h.authMasyarakatService.Logout(userID, deviceID)
if err != nil {
log.Printf("Error during logout process for user %s: %v", userID, err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Logged out successfully")
}
*/

View File

@ -1,82 +0,0 @@
package handler
/*
import (
"log"
"rijig/dto"
services "rijig/internal/services/auth"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AuthPengepulHandler struct {
authPengepulService services.AuthMasyarakatService
}
func NewAuthPengepulHandler(authPengepulService services.AuthMasyarakatService) *AuthPengepulHandler {
return &AuthPengepulHandler{authPengepulService}
}
func (h *AuthPengepulHandler) RegisterOrLoginHandler(c *fiber.Ctx) error {
var req dto.RegisterRequest
if err := c.BodyParser(&req); err != nil {
return utils.ErrorResponse(c, "Invalid request body")
}
if req.Phone == "" {
return utils.ErrorResponse(c, "Phone number is required")
}
if err := h.authPengepulService.RegisterOrLogin(&req); err != nil {
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "OTP sent successfully")
}
func (h *AuthPengepulHandler) VerifyOTPHandler(c *fiber.Ctx) error {
var req dto.VerifyOTPRequest
if err := c.BodyParser(&req); err != nil {
return utils.ErrorResponse(c, "Invalid request body")
}
if req.OTP == "" {
return utils.ErrorResponse(c, "OTP is required")
}
if req.DeviceID == "" {
return utils.ErrorResponse(c, "DeviceID is required")
}
response, err := h.authPengepulService.VerifyOTP(&req)
if err != nil {
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, response, "Registration/Login successful")
}
func (h *AuthPengepulHandler) LogoutHandler(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.ErrorResponse(c, "User is not logged in or invalid session")
}
deviceID, ok := c.Locals("device_id").(string)
if !ok || deviceID == "" {
log.Println("Error: DeviceID is nil or empty")
return utils.ErrorResponse(c, "DeviceID is required")
}
err := h.authPengepulService.Logout(userID, deviceID)
if err != nil {
log.Printf("Error during logout process for user %s: %v", userID, err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Logged out successfully")
}
*/

View File

@ -1,82 +0,0 @@
package handler
/*
import (
"log"
"rijig/dto"
services "rijig/internal/services/auth"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type AuthPengelolaHandler struct {
authPengelolaService services.AuthMasyarakatService
}
func NewAuthPengelolaHandler(authPengelolaService services.AuthMasyarakatService) *AuthPengelolaHandler {
return &AuthPengelolaHandler{authPengelolaService}
}
func (h *AuthPengelolaHandler) RegisterOrLoginHandler(c *fiber.Ctx) error {
var req dto.RegisterRequest
if err := c.BodyParser(&req); err != nil {
return utils.ErrorResponse(c, "Invalid request body")
}
if req.Phone == "" {
return utils.ErrorResponse(c, "Phone number is required")
}
if err := h.authPengelolaService.RegisterOrLogin(&req); err != nil {
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "OTP sent successfully")
}
func (h *AuthPengelolaHandler) VerifyOTPHandler(c *fiber.Ctx) error {
var req dto.VerifyOTPRequest
if err := c.BodyParser(&req); err != nil {
return utils.ErrorResponse(c, "Invalid request body")
}
if req.OTP == "" {
return utils.ErrorResponse(c, "OTP is required")
}
if req.DeviceID == "" {
return utils.ErrorResponse(c, "DeviceID is required")
}
response, err := h.authPengelolaService.VerifyOTP(&req)
if err != nil {
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, response, "Registration/Login successful")
}
func (h *AuthPengelolaHandler) LogoutHandler(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.ErrorResponse(c, "User is not logged in or invalid session")
}
deviceID, ok := c.Locals("device_id").(string)
if !ok || deviceID == "" {
log.Println("Error: DeviceID is nil or empty")
return utils.ErrorResponse(c, "DeviceID is required")
}
err := h.authPengelolaService.Logout(userID, deviceID)
if err != nil {
log.Printf("Error during logout process for user %s: %v", userID, err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Logged out successfully")
}
*/

View File

@ -1,80 +0,0 @@
package handler
// import (
// "log"
// "rijig/dto"
// "rijig/internal/services"
// "rijig/utils"
// "github.com/gofiber/fiber/v2"
// )
// type AuthHandler struct {
// authService services.AuthService
// }
// func NewAuthHandler(authService services.AuthService) *AuthHandler {
// return &AuthHandler{authService}
// }
// func (h *AuthHandler) RegisterOrLoginHandler(c *fiber.Ctx) error {
// var req dto.RegisterRequest
// if err := c.BodyParser(&req); err != nil {
// return utils.ErrorResponse(c, "Invalid request body")
// }
// if req.Phone == "" || req.RoleID == "" {
// return utils.ErrorResponse(c, "Phone number and role ID are required")
// }
// if err := h.authService.RegisterOrLogin(&req); err != nil {
// return utils.ErrorResponse(c, err.Error())
// }
// return utils.SuccessResponse(c, nil, "OTP sent successfully")
// }
// func (h *AuthHandler) VerifyOTPHandler(c *fiber.Ctx) error {
// var req dto.VerifyOTPRequest
// if err := c.BodyParser(&req); err != nil {
// return utils.ErrorResponse(c, "Invalid request body")
// }
// if req.OTP == "" {
// return utils.ErrorResponse(c, "OTP is required")
// }
// response, err := h.authService.VerifyOTP(&req)
// if err != nil {
// return utils.ErrorResponse(c, err.Error())
// }
// return utils.SuccessResponse(c, response, "Registration/Login successful")
// }
// func (h *AuthHandler) LogoutHandler(c *fiber.Ctx) error {
// userID, ok := c.Locals("userID").(string)
// if !ok || userID == "" {
// return utils.ErrorResponse(c, "User is not logged in or invalid session")
// }
// phoneKey := "user_phone:" + userID
// phone, err := utils.GetStringData(phoneKey)
// if err != nil || phone == "" {
// log.Printf("Error retrieving phone from Redis for user %s: %v", userID, err)
// return utils.ErrorResponse(c, "Phone number is missing or invalid session data")
// }
// err = h.authService.Logout(userID, phone)
// if err != nil {
// log.Printf("Error during logout process for user %s: %v", userID, err)
// return utils.ErrorResponse(c, err.Error())
// }
// return utils.SuccessResponse(c, nil, "Logged out successfully")
// }

View File

@ -1,109 +0,0 @@
package handler
import (
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type BannerHandler struct {
BannerService services.BannerService
}
func NewBannerHandler(bannerService services.BannerService) *BannerHandler {
return &BannerHandler{BannerService: bannerService}
}
func (h *BannerHandler) CreateBanner(c *fiber.Ctx) error {
var request dto.RequestBannerDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateBannerInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
bannerImage, err := c.FormFile("bannerimage")
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner image is required")
}
bannerResponse, err := h.BannerService.CreateBanner(request, bannerImage)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.CreateResponse(c, bannerResponse, "Banner created successfully")
}
func (h *BannerHandler) GetAllBanners(c *fiber.Ctx) error {
banners, err := h.BannerService.GetAllBanners()
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch banners")
}
return utils.NonPaginatedResponse(c, banners, len(banners), "Banners fetched successfully")
}
func (h *BannerHandler) GetBannerByID(c *fiber.Ctx) error {
id := c.Params("banner_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner ID is required")
}
banner, err := h.BannerService.GetBannerByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "invalid banner id")
}
return utils.SuccessResponse(c, banner, "Banner fetched successfully")
}
func (h *BannerHandler) UpdateBanner(c *fiber.Ctx) error {
id := c.Params("banner_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner ID is required")
}
var request dto.RequestBannerDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateBannerInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
bannerImage, err := c.FormFile("bannerimage")
if err != nil && err.Error() != "no such file" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner image is required")
}
bannerResponse, err := h.BannerService.UpdateBanner(id, request, bannerImage)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, bannerResponse, "Banner updated successfully")
}
func (h *BannerHandler) DeleteBanner(c *fiber.Ctx) error {
id := c.Params("banner_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner ID is required")
}
err := h.BannerService.DeleteBanner(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Banner deleted successfully")
}

View File

@ -1,93 +0,0 @@
package handler
import (
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type CartHandler struct {
cartService services.CartService
}
func NewCartHandler(cartService services.CartService) *CartHandler {
return &CartHandler{cartService: cartService}
}
func (h *CartHandler) AddOrUpdateItem(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
var req dto.RequestCartItemDTO
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"request": {"Payload tidak valid"},
})
}
hasErrors, _ := req.Amount > 0 && req.TrashID != "", true
if !hasErrors {
errs := make(map[string][]string)
if req.Amount <= 0 {
errs["amount"] = append(errs["amount"], "Amount harus lebih dari 0")
}
if req.TrashID == "" {
errs["trash_id"] = append(errs["trash_id"], "Trash ID tidak boleh kosong")
}
return utils.ValidationErrorResponse(c, errs)
}
if err := h.cartService.AddOrUpdateItem(c.Context(), userID, req); err != nil {
return utils.InternalServerErrorResponse(c, "Gagal menambahkan item ke keranjang")
}
return utils.GenericResponse(c, fiber.StatusOK, "Item berhasil ditambahkan ke keranjang")
}
func (h *CartHandler) GetCart(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
cart, err := h.cartService.GetCart(c.Context(), userID)
if err != nil {
return utils.ErrorResponse(c, "Gagal mengambil data keranjang")
}
return utils.SuccessResponse(c, cart, "Berhasil mengambil data keranjang")
}
func (h *CartHandler) DeleteItem(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
trashID := c.Params("trash_id")
if trashID == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Trash ID tidak boleh kosong")
}
if err := h.cartService.DeleteItem(c.Context(), userID, trashID); err != nil {
return utils.InternalServerErrorResponse(c, "Gagal menghapus item dari keranjang")
}
return utils.GenericResponse(c, fiber.StatusOK, "Item berhasil dihapus dari keranjang")
}
func (h *CartHandler) Checkout(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
if err := h.cartService.Checkout(c.Context(), userID); err != nil {
return utils.InternalServerErrorResponse(c, "Gagal melakukan checkout keranjang")
}
return utils.GenericResponse(c, fiber.StatusOK, "Checkout berhasil. Permintaan pickup telah dibuat.")
}
func (h *CartHandler) ClearCart(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
err := h.cartService.ClearCart(c.Context(), userID)
if err != nil {
return utils.InternalServerErrorResponse(c, "Gagal menghapus keranjang")
}
return utils.GenericResponse(c, fiber.StatusOK, "Keranjang berhasil dikosongkan")
}

View File

@ -1,194 +0,0 @@
package handler
import (
"context"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type CollectorHandler interface {
CreateCollector(c *fiber.Ctx) error
AddTrashToCollector(c *fiber.Ctx) error
GetCollectorByID(c *fiber.Ctx) error
GetCollectorByUserID(c *fiber.Ctx) error
UpdateCollector(c *fiber.Ctx) error
UpdateJobStatus(c *fiber.Ctx) error
UpdateTrash(c *fiber.Ctx) error
DeleteTrash(c *fiber.Ctx) error
}
type collectorHandler struct {
service services.CollectorService
}
func NewCollectorHandler(service services.CollectorService) CollectorHandler {
return &collectorHandler{service: service}
}
func (h *collectorHandler) CreateCollector(c *fiber.Ctx) error {
var req dto.RequestCollectorDTO
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if errs, valid := req.ValidateRequestCollector(); !valid {
return utils.ValidationErrorResponse(c, errs)
}
userID := c.Locals("userID").(string)
err := h.service.CreateCollector(context.Background(), userID, req)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.CreateResponse(c, nil, "Collector berhasil dibuat")
}
func (h *collectorHandler) AddTrashToCollector(c *fiber.Ctx) error {
collectorID := c.Params("id")
var req dto.RequestAddAvaibleTrash
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if errs, valid := req.ValidateRequestAddAvaibleTrash(); !valid {
return utils.ValidationErrorResponse(c, errs)
}
err := h.service.AddTrashToCollector(context.Background(), collectorID, req)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Trash berhasil ditambahkan")
}
func (h *collectorHandler) GetCollectorByID(c *fiber.Ctx) error {
collectorID := c.Params("id")
result, err := h.service.GetCollectorByID(context.Background(), collectorID)
if err != nil {
return utils.ErrorResponse(c, "Collector tidak ditemukan")
}
return utils.SuccessResponse(c, result, "Data collector berhasil diambil")
}
func (h *collectorHandler) GetCollectorByUserID(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
result, err := h.service.GetCollectorByUserID(context.Background(), userID)
if err != nil {
return utils.ErrorResponse(c, "Collector tidak ditemukan")
}
return utils.SuccessResponse(c, result, "Data collector berhasil diambil")
}
func (h *collectorHandler) UpdateCollector(c *fiber.Ctx) error {
collectorID := c.Params("id")
var req struct {
JobStatus *string `json:"job_status"`
Rating float32 `json:"rating"`
AddressID string `json:"address_id"`
}
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if req.AddressID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"address_id": {"tidak boleh kosong"},
})
}
err := h.service.UpdateCollector(context.Background(), collectorID, req.JobStatus, req.Rating, req.AddressID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Collector berhasil diperbarui")
}
func (h *collectorHandler) UpdateJobStatus(c *fiber.Ctx) error {
collectorID := c.Params("id")
var req struct {
JobStatus string `json:"job_status"`
}
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
if req.JobStatus != "active" && req.JobStatus != "inactive" {
return utils.ValidationErrorResponse(c, map[string][]string{
"job_status": {"harus bernilai 'active' atau 'inactive'"},
})
}
err := h.service.UpdateCollector(c.Context(), collectorID, &req.JobStatus, 0, "")
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Status collector berhasil diperbarui")
}
func (h *collectorHandler) UpdateTrash(c *fiber.Ctx) error {
collectorID := c.Params("id")
var req []dto.RequestAvaibleTrashbyCollector
if err := c.BodyParser(&req); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"format JSON tidak valid"},
})
}
for i, t := range req {
if t.TrashId == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"trash_id": {t.TrashId, "trash_id tidak boleh kosong pada item ke " + string(rune(i))},
})
}
if t.TrashPrice <= 0 {
return utils.ValidationErrorResponse(c, map[string][]string{
"trash_price": {"trash_price harus lebih dari 0 pada item ke " + string(rune(i))},
})
}
}
err := h.service.UpdateAvaibleTrashByCollector(context.Background(), collectorID, req)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Trash berhasil diperbarui")
}
func (h *collectorHandler) DeleteTrash(c *fiber.Ctx) error {
trashID := c.Params("id")
if trashID == "" {
return utils.ValidationErrorResponse(c, map[string][]string{
"trash_id": {"tidak boleh kosong"},
})
}
err := h.service.DeleteAvaibleTrash(context.Background(), trashID)
if err != nil {
return utils.InternalServerErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Trash berhasil dihapus")
}

View File

@ -1,100 +0,0 @@
package handler
import (
"fmt"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type CompanyProfileHandler struct {
companyProfileService services.CompanyProfileService
}
func NewCompanyProfileHandler(service services.CompanyProfileService) *CompanyProfileHandler {
return &CompanyProfileHandler{
companyProfileService: service,
}
}
func (h *CompanyProfileHandler) CreateCompanyProfile(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 requestDTO dto.RequestCompanyProfileDTO
if err := c.BodyParser(&requestDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid input data"}})
}
companyProfileResponse, err := h.companyProfileService.CreateCompanyProfile(userID, &requestDTO)
if err != nil {
return utils.ErrorResponse(c, fmt.Sprintf("Failed to create company profile: %v", err))
}
return utils.SuccessResponse(c, companyProfileResponse, "Company profile created successfully")
}
func (h *CompanyProfileHandler) GetCompanyProfileByID(c *fiber.Ctx) error {
id := c.Params("company_id")
companyProfileResponse, err := h.companyProfileService.GetCompanyProfileByID(id)
if err != nil {
return utils.ErrorResponse(c, fmt.Sprintf("Failed to fetch company profile: %v", err))
}
return utils.SuccessResponse(c, companyProfileResponse, "Company profile fetched successfully")
}
func (h *CompanyProfileHandler) GetCompanyProfilesByUserID(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
companyProfilesResponse, err := h.companyProfileService.GetCompanyProfilesByUserID(userID)
if err != nil {
return utils.ErrorResponse(c, fmt.Sprintf("Failed to fetch company profiles: %v", err))
}
return utils.NonPaginatedResponse(c, companyProfilesResponse, len(companyProfilesResponse), "Company profiles fetched successfully")
}
func (h *CompanyProfileHandler) UpdateCompanyProfile(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("company_id")
var requestDTO dto.RequestCompanyProfileDTO
if err := c.BodyParser(&requestDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid input data"}})
}
companyProfileResponse, err := h.companyProfileService.UpdateCompanyProfile(id, &requestDTO)
if err != nil {
return utils.ErrorResponse(c, fmt.Sprintf("Failed to update company profile: %v", err))
}
return utils.SuccessResponse(c, companyProfileResponse, "Company profile updated successfully")
}
func (h *CompanyProfileHandler) DeleteCompanyProfile(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("company_id")
err := h.companyProfileService.DeleteCompanyProfile(id)
if err != nil {
return utils.ErrorResponse(c, fmt.Sprintf("Failed to delete company profile: %v", err))
}
return utils.SuccessResponse(c, nil, "Company profile deleted successfully")
}

View File

@ -1,93 +0,0 @@
package handler
import (
"fmt"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type CoverageAreaHandler struct {
service services.CoverageAreaService
}
func NewCoverageAreaHandler(service services.CoverageAreaService) *CoverageAreaHandler {
return &CoverageAreaHandler{service: service}
}
func (h *CoverageAreaHandler) CreateCoverageArea(c *fiber.Ctx) error {
var request dto.RequestCoverageArea
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"Invalid request body"},
})
}
errors, valid := request.ValidateCoverageArea()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
response, err := h.service.CreateCoverageArea(request)
if err != nil {
return utils.InternalServerErrorResponse(c, fmt.Sprintf("Error creating coverage area: %v", err))
}
return utils.SuccessResponse(c, response, "Coverage area created successfully")
}
func (h *CoverageAreaHandler) GetCoverageAreaByID(c *fiber.Ctx) error {
id := c.Params("id")
response, err := h.service.GetCoverageAreaByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, fmt.Sprintf("Coverage area with ID %s not found", id))
}
return utils.SuccessResponse(c, response, "Coverage area found")
}
func (h *CoverageAreaHandler) GetAllCoverageAreas(c *fiber.Ctx) error {
response, err := h.service.GetAllCoverageAreas()
if err != nil {
return utils.InternalServerErrorResponse(c, "Error fetching coverage areas")
}
return utils.SuccessResponse(c, response, "Coverage areas fetched successfully")
}
func (h *CoverageAreaHandler) UpdateCoverageArea(c *fiber.Ctx) error {
id := c.Params("id")
var request dto.RequestCoverageArea
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{
"body": {"Invalid request body"},
})
}
errors, valid := request.ValidateCoverageArea()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
response, err := h.service.UpdateCoverageArea(id, request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, fmt.Sprintf("Coverage area with ID %s not found", id))
}
return utils.SuccessResponse(c, response, "Coverage area updated successfully")
}
func (h *CoverageAreaHandler) DeleteCoverageArea(c *fiber.Ctx) error {
id := c.Params("id")
err := h.service.DeleteCoverageArea(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, fmt.Sprintf("Coverage area with ID %s not found", id))
}
return utils.GenericResponse(c, fiber.StatusOK, "Coverage area deleted successfully")
}

View File

@ -1,134 +0,0 @@
package handler
import (
"log"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type IdentityCardHandler struct {
IdentityCardService services.IdentityCardService
}
func NewIdentityCardHandler(identityCardService services.IdentityCardService) *IdentityCardHandler {
return &IdentityCardHandler{
IdentityCardService: identityCardService,
}
}
func (h *IdentityCardHandler) CreateIdentityCard(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User not authenticated")
}
var request dto.RequestIdentityCardDTO
if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing body: %v", err)
return utils.ErrorResponse(c, "Invalid request data")
}
cardPhoto, err := c.FormFile("cardphoto")
if err != nil {
log.Printf("Error retrieving card photo from request: %v", err)
return utils.ErrorResponse(c, "Card photo is required")
}
identityCard, err := h.IdentityCardService.CreateIdentityCard(userID, &request, cardPhoto)
if err != nil {
log.Printf("Error creating identity card: %v", err)
return utils.ErrorResponse(c, err.Error())
}
return utils.CreateResponse(c, identityCard, "Identity card created successfully")
}
func (h *IdentityCardHandler) UpdateIdentityCard(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User not authenticated")
}
id := c.Params("identity_id")
if id == "" {
return utils.ErrorResponse(c, "Identity card ID is required")
}
var request dto.RequestIdentityCardDTO
if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing body: %v", err)
return utils.ErrorResponse(c, "Invalid request data")
}
cardPhoto, err := c.FormFile("cardphoto")
if err != nil && err.Error() != "File not found" {
log.Printf("Error retrieving card photo: %v", err)
return utils.ErrorResponse(c, "Card photo is required")
}
updatedCard, err := h.IdentityCardService.UpdateIdentityCard(userID, id, &request, cardPhoto)
if err != nil {
log.Printf("Error updating identity card: %v", err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, updatedCard, "Identity card updated successfully")
}
func (h *IdentityCardHandler) GetIdentityCardById(c *fiber.Ctx) error {
id := c.Params("identity_id")
if id == "" {
return utils.ErrorResponse(c, "Identity card ID is required")
}
identityCard, err := h.IdentityCardService.GetIdentityCardByID(id)
if err != nil {
log.Printf("Error retrieving identity card: %v", err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, identityCard, "Identity card retrieved successfully")
}
func (h *IdentityCardHandler) GetIdentityCard(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
identityCard, err := h.IdentityCardService.GetIdentityCardsByUserID(userID)
if err != nil {
log.Printf("Error retrieving identity card: %v", err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, identityCard, "Identity card retrieved successfully")
}
func (h *IdentityCardHandler) DeleteIdentityCard(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User not authenticated")
}
id := c.Params("identity_id")
if id == "" {
return utils.ErrorResponse(c, "Identity card ID is required")
}
err := h.IdentityCardService.DeleteIdentityCard(id)
if err != nil {
log.Printf("Error deleting identity card: %v", err)
return utils.ErrorResponse(c, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Identity card deleted successfully")
}

View File

@ -1,99 +0,0 @@
package handler
import (
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type InitialCointHandler struct {
InitialCointService services.InitialCointService
}
func NewInitialCointHandler(initialCointService services.InitialCointService) *InitialCointHandler {
return &InitialCointHandler{InitialCointService: initialCointService}
}
func (h *InitialCointHandler) CreateInitialCoint(c *fiber.Ctx) error {
var request dto.RequestInitialCointDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateCointInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
initialCointResponse, err := h.InitialCointService.CreateInitialCoint(request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.CreateResponse(c, initialCointResponse, "Initial coint created successfully")
}
func (h *InitialCointHandler) GetAllInitialCoints(c *fiber.Ctx) error {
initialCoints, err := h.InitialCointService.GetAllInitialCoints()
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch initial coints")
}
return utils.NonPaginatedResponse(c, initialCoints, len(initialCoints), "Initial coints fetched successfully")
}
func (h *InitialCointHandler) GetInitialCointByID(c *fiber.Ctx) error {
id := c.Params("coin_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
}
initialCoint, err := h.InitialCointService.GetInitialCointByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "Invalid coin ID")
}
return utils.SuccessResponse(c, initialCoint, "Initial coint fetched successfully")
}
func (h *InitialCointHandler) UpdateInitialCoint(c *fiber.Ctx) error {
id := c.Params("coin_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
}
var request dto.RequestInitialCointDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateCointInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
initialCointResponse, err := h.InitialCointService.UpdateInitialCoint(id, request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, initialCointResponse, "Initial coint updated successfully")
}
func (h *InitialCointHandler) DeleteInitialCoint(c *fiber.Ctx) error {
id := c.Params("coin_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
}
err := h.InitialCointService.DeleteInitialCoint(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Initial coint deleted successfully")
}

View File

@ -1,37 +0,0 @@
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

@ -1,49 +0,0 @@
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

@ -1,227 +0,0 @@
package handler
import (
"fmt"
"log"
"strconv"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type ProductHandler struct {
ProductService services.ProductService
}
func NewProductHandler(productService services.ProductService) *ProductHandler {
return &ProductHandler{ProductService: productService}
}
func ConvertStringToInt(value string) (int, error) {
convertedValue, err := strconv.Atoi(value)
if err != nil {
return 0, fmt.Errorf("invalid integer format: %s", value)
}
return convertedValue, nil
}
func GetPaginationParams(c *fiber.Ctx) (int, int, error) {
pageStr := c.Query("page", "1")
limitStr := c.Query("limit", "50")
page, err := strconv.Atoi(pageStr)
if err != nil || page <= 0 {
return 0, 0, fmt.Errorf("invalid page value")
}
limit, err := strconv.Atoi(limitStr)
if err != nil || limit <= 0 {
return 0, 0, fmt.Errorf("invalid limit value")
}
return page, limit, nil
}
func (h *ProductHandler) CreateProduct(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok {
log.Println("User ID not found in Locals")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User ID not found")
}
productName := c.FormValue("product_name")
quantityStr := c.FormValue("quantity")
productImages, err := c.MultipartForm()
if err != nil {
log.Printf("Error parsing form data: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Error parsing form data")
}
quantity, err := ConvertStringToInt(quantityStr)
if err != nil {
log.Printf("Invalid quantity: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid quantity")
}
productDTO := dto.RequestProductDTO{
ProductName: productName,
Quantity: quantity,
ProductImages: productImages.File["product_image"],
}
product, err := h.ProductService.CreateProduct(userID, &productDTO)
if err != nil {
log.Printf("Error creating product: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
}
return utils.CreateResponse(c, product, "Product created successfully")
}
func (h *ProductHandler) GetAllProductsByStoreID(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok {
log.Println("User ID not found in Locals")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User ID not found")
}
page, limit, err := GetPaginationParams(c)
if err != nil {
log.Printf("Invalid pagination params: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid pagination parameters")
}
products, total, err := h.ProductService.GetAllProductsByStoreID(userID, page, limit)
if err != nil {
log.Printf("Error fetching products: %v", err)
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.PaginatedResponse(c, products, page, limit, int(total), "Products fetched successfully")
}
func (h *ProductHandler) GetProductByID(c *fiber.Ctx) error {
productID := c.Params("product_id")
if productID == "" {
log.Println("Product ID is required")
return utils.GenericResponse(c, fiber.StatusBadRequest, "Product ID is required")
}
product, err := h.ProductService.GetProductByID(productID)
if err != nil {
log.Printf("Error fetching product: %v", err)
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, product, "Product fetched successfully")
}
func (h *ProductHandler) UpdateProduct(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok {
log.Println("User ID not found in Locals")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User ID not found")
}
productID := c.Params("product_id")
if productID == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Product ID is required")
}
var productDTO dto.RequestProductDTO
if err := c.BodyParser(&productDTO); err != nil {
log.Printf("Error parsing body: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
}
productImages, err := c.MultipartForm()
if err != nil {
log.Printf("Error parsing form data: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Error parsing form data")
}
productDTO.ProductImages = productImages.File["product_images"]
product, err := h.ProductService.UpdateProduct(userID, productID, &productDTO)
if err != nil {
log.Printf("Error updating product: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
}
return utils.CreateResponse(c, product, "Product updated successfully")
}
func (h *ProductHandler) DeleteProduct(c *fiber.Ctx) error {
productID := c.Params("product_id")
if productID == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Product ID is required")
}
err := h.ProductService.DeleteProduct(productID)
if err != nil {
log.Printf("Error deleting product: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, fmt.Sprintf("Failed to delete product: %v", err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Product deleted successfully")
}
func (h *ProductHandler) DeleteProducts(c *fiber.Ctx) error {
var productIDs []string
if err := c.BodyParser(&productIDs); err != nil {
log.Printf("Error parsing product IDs: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid input for product IDs")
}
if len(productIDs) == 0 {
return utils.GenericResponse(c, fiber.StatusBadRequest, "No product IDs provided")
}
err := h.ProductService.DeleteProducts(productIDs)
if err != nil {
log.Printf("Error deleting products: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, fmt.Sprintf("Failed to delete products: %v", err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Products deleted successfully")
}
func (h *ProductHandler) DeleteProductImage(c *fiber.Ctx) error {
imageID := c.Params("image_id")
if imageID == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Image ID is required")
}
err := h.ProductService.DeleteProductImage(imageID)
if err != nil {
log.Printf("Error deleting product image: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, fmt.Sprintf("Failed to delete product image: %v", err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Product image deleted successfully")
}
func (h *ProductHandler) DeleteProductImages(c *fiber.Ctx) error {
var imageIDs []string
if err := c.BodyParser(&imageIDs); err != nil {
log.Printf("Error parsing image IDs: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid input for image IDs")
}
if len(imageIDs) == 0 {
return utils.GenericResponse(c, fiber.StatusBadRequest, "No image IDs provided")
}
err := h.ProductService.DeleteProductImages(imageIDs)
if err != nil {
log.Printf("Error deleting product images: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, fmt.Sprintf("Failed to delete product images: %v", err))
}
return utils.GenericResponse(c, fiber.StatusOK, "Product images deleted successfully")
}

View File

@ -1,66 +0,0 @@
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

@ -1,150 +0,0 @@
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

@ -1,47 +0,0 @@
package handler
import (
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type RoleHandler struct {
RoleService services.RoleService
}
func NewRoleHandler(roleService services.RoleService) *RoleHandler {
return &RoleHandler{RoleService: roleService}
}
func (h *RoleHandler) GetRoles(c *fiber.Ctx) error {
// roleID, ok := c.Locals("roleID").(string)
// if !ok || roleID != utils.RoleAdministrator {
// return utils.GenericResponse(c, fiber.StatusForbidden, "Forbidden: You don't have permission to access this resource")
// }
roles, err := h.RoleService.GetRoles(c.Context())
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, roles, "Roles fetched successfully")
}
func (h *RoleHandler) GetRoleByID(c *fiber.Ctx) error {
roleID := c.Params("role_id")
// roleIDFromSession, ok := c.Locals("roleID").(string)
// if !ok || roleIDFromSession != utils.RoleAdministrator {
// return utils.GenericResponse(c, fiber.StatusForbidden, "Forbidden: You don't have permission to access this resource")
// }
role, err := h.RoleService.GetRoleByID(c.Context(), roleID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "role id tidak ditemukan")
}
return utils.SuccessResponse(c, role, "Role fetched successfully")
}

View File

@ -1,159 +0,0 @@
package handler
import (
"log"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type StoreHandler struct {
StoreService services.StoreService
}
func NewStoreHandler(storeService services.StoreService) *StoreHandler {
return &StoreHandler{StoreService: storeService}
}
func (h *StoreHandler) CreateStore(c *fiber.Ctx) error {
storeName := c.FormValue("store_name")
storeInfo := c.FormValue("store_info")
storeAddressID := c.FormValue("store_address_id")
if storeName == "" || storeInfo == "" || storeAddressID == "" {
log.Println("Missing required fields")
return utils.GenericResponse(c, fiber.StatusBadRequest, "All fields are required")
}
storeLogo, err := c.FormFile("store_logo")
if err != nil {
log.Printf("Error parsing store logo: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Store logo is required")
}
storeBanner, err := c.FormFile("store_banner")
if err != nil {
log.Printf("Error parsing store banner: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Store banner is required")
}
requestStoreDTO := dto.RequestStoreDTO{
StoreName: storeName,
StoreLogo: storeLogo.Filename,
StoreBanner: storeBanner.Filename,
StoreInfo: storeInfo,
StoreAddressID: storeAddressID,
}
errors, valid := requestStoreDTO.ValidateStoreInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
userID, ok := c.Locals("userID").(string)
if !ok {
log.Println("User ID not found in Locals")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User ID not found")
}
store, err := h.StoreService.CreateStore(userID, requestStoreDTO, storeLogo, storeBanner)
if err != nil {
log.Printf("Error creating store: %v", err)
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
}
return utils.CreateResponse(c, store, "Store created successfully")
}
func (h *StoreHandler) GetStoreByUserID(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok {
log.Println("User ID not found in Locals")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User ID not found")
}
store, err := h.StoreService.GetStoreByUserID(userID)
if err != nil {
log.Printf("Error fetching store: %v", err)
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
log.Printf("Store fetched successfully: %v", store)
return utils.SuccessResponse(c, store, "Store fetched successfully")
}
func (h *StoreHandler) UpdateStore(c *fiber.Ctx) error {
storeID := c.Params("store_id")
if storeID == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Store ID is required")
}
storeName := c.FormValue("store_name")
storeInfo := c.FormValue("store_info")
storeAddressID := c.FormValue("store_address_id")
if storeName == "" || storeInfo == "" || storeAddressID == "" {
log.Println("Missing required fields")
return utils.GenericResponse(c, fiber.StatusBadRequest, "All fields are required")
}
storeLogo, err := c.FormFile("store_logo")
if err != nil && err.Error() != "missing file" {
log.Printf("Error parsing store logo: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Error parsing store logo")
}
storeBanner, err := c.FormFile("store_banner")
if err != nil && err.Error() != "missing file" {
log.Printf("Error parsing store banner: %v", err)
return utils.GenericResponse(c, fiber.StatusBadRequest, "Error parsing store banner")
}
requestStoreDTO := dto.RequestStoreDTO{
StoreName: storeName,
StoreLogo: storeLogo.Filename,
StoreBanner: storeBanner.Filename,
StoreInfo: storeInfo,
StoreAddressID: storeAddressID,
}
errors, valid := requestStoreDTO.ValidateStoreInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
userID, ok := c.Locals("userID").(string)
if !ok {
log.Println("User ID not found in Locals")
return utils.GenericResponse(c, fiber.StatusUnauthorized, "User ID not found")
}
store, err := h.StoreService.UpdateStore(storeID, &requestStoreDTO, storeLogo, storeBanner, userID)
if err != nil {
log.Printf("Error updating store: %v", err)
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
log.Printf("Store updated successfully: %v", store)
return utils.SuccessResponse(c, store, "Store updated successfully")
}
func (h *StoreHandler) DeleteStore(c *fiber.Ctx) error {
storeID := c.Params("store_id")
if storeID == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Store ID is required")
}
err := h.StoreService.DeleteStore(storeID)
if err != nil {
log.Printf("Error deleting store: %v", err)
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
log.Printf("Store deleted successfully: %v", storeID)
return utils.GenericResponse(c, fiber.StatusOK, "Store deleted successfully")
}

View File

@ -1,148 +0,0 @@
package handler
import (
"log"
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type TrashHandler struct {
TrashService services.TrashService
}
func NewTrashHandler(trashService services.TrashService) *TrashHandler {
return &TrashHandler{TrashService: trashService}
}
func (h *TrashHandler) CreateCategory(c *fiber.Ctx) error {
var request dto.RequestTrashCategoryDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateTrashCategoryInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
iconTrash, err := c.FormFile("icon")
if err != nil {
log.Printf("Error retrieving card photo from request: %v", err)
return utils.ErrorResponse(c, "Card photo is required")
}
categoryResponse, err := h.TrashService.CreateCategory(request, iconTrash)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to create category: "+err.Error())
}
return utils.CreateResponse(c, categoryResponse, "Category created successfully")
}
func (h *TrashHandler) AddDetailToCategory(c *fiber.Ctx) error {
var request dto.RequestTrashDetailDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
detailResponse, err := h.TrashService.AddDetailToCategory(request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to add detail to category: "+err.Error())
}
return utils.CreateResponse(c, detailResponse, "Trash detail added successfully")
}
func (h *TrashHandler) GetCategories(c *fiber.Ctx) error {
categories, err := h.TrashService.GetCategories()
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch categories: "+err.Error())
}
return utils.NonPaginatedResponse(c, categories, len(categories), "Categories retrieved successfully")
}
func (h *TrashHandler) GetCategoryByID(c *fiber.Ctx) error {
id := c.Params("category_id")
category, err := h.TrashService.GetCategoryByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "Category not found: "+err.Error())
}
return utils.SuccessResponse(c, category, "Category retrieved successfully")
}
func (h *TrashHandler) GetTrashDetailByID(c *fiber.Ctx) error {
id := c.Params("detail_id")
detail, err := h.TrashService.GetTrashDetailByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "Trash detail not found: "+err.Error())
}
return utils.SuccessResponse(c, detail, "Trash detail retrieved successfully")
}
func (h *TrashHandler) UpdateCategory(c *fiber.Ctx) error {
id := c.Params("category_id")
var request dto.RequestTrashCategoryDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
}
iconTrash, err := c.FormFile("icon")
if err != nil && err.Error() != "File not found" {
log.Printf("Error retrieving icon trash from request: %v", err)
return utils.ErrorResponse(c, "icon trash is required")
}
updatedCategory, err := h.TrashService.UpdateCategory(id, request, iconTrash)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error updating category: "+err.Error())
}
return utils.SuccessResponse(c, updatedCategory, "Category updated successfully")
}
func (h *TrashHandler) UpdateDetail(c *fiber.Ctx) error {
id := c.Params("detail_id")
var request dto.RequestTrashDetailDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
}
updatedDetail, err := h.TrashService.UpdateDetail(id, request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error updating detail: "+err.Error())
}
return utils.SuccessResponse(c, updatedDetail, "Trash detail updated successfully")
}
func (h *TrashHandler) DeleteCategory(c *fiber.Ctx) error {
id := c.Params("category_id")
if err := h.TrashService.DeleteCategory(id); err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error deleting category: "+err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Category deleted successfully")
}
func (h *TrashHandler) DeleteDetail(c *fiber.Ctx) error {
id := c.Params("detail_id")
if err := h.TrashService.DeleteDetail(id); err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error deleting detail: "+err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Trash detail deleted successfully")
}

View File

@ -1,101 +0,0 @@
package handler
import (
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"strconv"
"github.com/gofiber/fiber/v2"
)
type UserHandler struct {
userService services.UserService
}
func NewUserHandler(userService services.UserService) *UserHandler {
return &UserHandler{userService: userService}
}
func (h *UserHandler) UpdateUserAvatarHandler(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
avatar, err := c.FormFile("avatar")
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "No avatar file provided")
}
updatedUser, err := h.userService.UpdateUserAvatar(userID, avatar)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, updatedUser, "Avatar updated successfully")
}
func (h *UserHandler) GetUserByIDHandler(c *fiber.Ctx) error {
// userID := c.Params("id")
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
user, err := h.userService.GetUserByID(userID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, user, "User retrieved successfully")
}
func (h *UserHandler) GetAllUsersHandler(c *fiber.Ctx) error {
page := 1
limit := 10
if p := c.Query("page"); p != "" {
page, _ = strconv.Atoi(p)
}
if l := c.Query("limit"); l != "" {
limit, _ = strconv.Atoi(l)
}
users, err := h.userService.GetAllUsers(page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.PaginatedResponse(c, users, page, limit, len(users), "Users retrieved successfully")
}
func (h *UserHandler) UpdateUserHandler(c *fiber.Ctx) error {
var request dto.RequestUserDTO
if err := c.BodyParser(&request); err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
}
userID := c.Locals("userID").(string)
updatedUser, err := h.userService.UpdateUser(userID, &request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, updatedUser, "User profile updated successfully")
}
func (h *UserHandler) UpdateUserPasswordHandler(c *fiber.Ctx) error {
var request dto.UpdatePasswordDTO
if err := c.BodyParser(&request); err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Invalid request body")
}
userID := c.Locals("userID").(string)
err := h.userService.UpdateUserPassword(userID, request.OldPassword, request.NewPassword, request.ConfirmNewPassword)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, nil, "Password updated successfully")
}

View File

@ -1,102 +0,0 @@
package handler
import (
"rijig/dto"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type UserPinHandler struct {
UserPinService services.UserPinService
}
func NewUserPinHandler(userPinService services.UserPinService) *UserPinHandler {
return &UserPinHandler{UserPinService: userPinService}
}
func (h *UserPinHandler) VerifyUserPin(c *fiber.Ctx) error {
var requestUserPinDTO dto.RequestUserPinDTO
if err := c.BodyParser(&requestUserPinDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := requestUserPinDTO.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
message, err := h.UserPinService.VerifyUserPin(userID, requestUserPinDTO.Pin)
if err != nil {
return utils.GenericResponse(c, fiber.StatusUnauthorized, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, message)
}
func (h *UserPinHandler) CheckPinStatus(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
}
status, err := h.UserPinService.CheckPinStatus(userID)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
if status == "Pin not created" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Pin belum dibuat")
}
return utils.GenericResponse(c, fiber.StatusOK, "Pin sudah dibuat")
}
func (h *UserPinHandler) CreateUserPin(c *fiber.Ctx) error {
var requestUserPinDTO dto.RequestUserPinDTO
if err := c.BodyParser(&requestUserPinDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := requestUserPinDTO.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
userID := c.Locals("userID").(string)
message, err := h.UserPinService.CreateUserPin(userID, requestUserPinDTO.Pin)
if err != nil {
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
}
return utils.GenericResponse(c, fiber.StatusCreated, message)
}
func (h *UserPinHandler) UpdateUserPin(c *fiber.Ctx) error {
var requestUserPinDTO dto.UpdateUserPinDTO
if err := c.BodyParser(&requestUserPinDTO); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := requestUserPinDTO.Validate()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
userID := c.Locals("userID").(string)
message, err := h.UserPinService.UpdateUserPin(userID, requestUserPinDTO.OldPin, requestUserPinDTO.NewPin)
if err != nil {
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, message)
}

View File

@ -1,24 +0,0 @@
package handler
import (
"log"
"rijig/config"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
func WhatsAppHandler(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(string)
if !ok || userID == "" {
return utils.ErrorResponse(c, "User is not logged in or invalid session")
}
err := config.LogoutWhatsApp()
if err != nil {
log.Printf("Error during logout process for user %s: %v", userID, err)
return utils.ErrorResponse(c, err.Error())
}
return utils.SuccessResponse(c, nil, "Logged out successfully")
}

View File

@ -1,200 +0,0 @@
package handler
import (
"strconv"
"rijig/internal/services"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type WilayahIndonesiaHandler struct {
WilayahService services.WilayahIndonesiaService
}
func NewWilayahImportHandler(wilayahService services.WilayahIndonesiaService) *WilayahIndonesiaHandler {
return &WilayahIndonesiaHandler{WilayahService: wilayahService}
}
func (h *WilayahIndonesiaHandler) ImportWilayahData(c *fiber.Ctx) error {
err := h.WilayahService.ImportDataFromCSV()
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, fiber.StatusCreated, "Data imported successfully")
}
func (h *WilayahIndonesiaHandler) GetProvinces(c *fiber.Ctx) error {
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
provinces, totalProvinces, err := h.WilayahService.GetAllProvinces(page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch provinces")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, provinces, page, limit, totalProvinces, "Provinces fetched successfully")
}
return utils.NonPaginatedResponse(c, provinces, totalProvinces, "Provinces fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetProvinceByID(c *fiber.Ctx) error {
provinceID := c.Params("provinceid")
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
province, totalRegencies, err := h.WilayahService.GetProvinceByID(provinceID, page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch province")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, province, page, limit, totalRegencies, "Province fetched successfully")
}
return utils.NonPaginatedResponse(c, province, totalRegencies, "Province fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetAllRegencies(c *fiber.Ctx) error {
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
regencies, totalRegencies, err := h.WilayahService.GetAllRegencies(page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch regency")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, regencies, page, limit, totalRegencies, "regency fetched successfully")
}
return utils.NonPaginatedResponse(c, regencies, totalRegencies, "Provinces fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetRegencyByID(c *fiber.Ctx) error {
regencyId := c.Params("regencyid")
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
regency, totalDistrict, err := h.WilayahService.GetRegencyByID(regencyId, page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch regency")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, regency, page, limit, totalDistrict, "regency fetched successfully")
}
return utils.NonPaginatedResponse(c, regency, totalDistrict, "regency fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetAllDistricts(c *fiber.Ctx) error {
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
districts, totalDistricts, err := h.WilayahService.GetAllDistricts(page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch districts")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, districts, page, limit, totalDistricts, "districts fetched successfully")
}
return utils.NonPaginatedResponse(c, districts, totalDistricts, "districts fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetDistrictByID(c *fiber.Ctx) error {
districtId := c.Params("districtid")
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
district, totalVillages, err := h.WilayahService.GetDistrictByID(districtId, page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch district")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, district, page, limit, totalVillages, "district fetched successfully")
}
return utils.NonPaginatedResponse(c, district, totalVillages, "district fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetAllVillages(c *fiber.Ctx) error {
page, err := strconv.Atoi(c.Query("page", "0"))
if err != nil {
page = 0
}
limit, err := strconv.Atoi(c.Query("limit", "0"))
if err != nil {
limit = 0
}
villages, totalVillages, err := h.WilayahService.GetAllVillages(page, limit)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch villages")
}
if page > 0 && limit > 0 {
return utils.PaginatedResponse(c, villages, page, limit, totalVillages, "villages fetched successfully")
}
return utils.NonPaginatedResponse(c, villages, totalVillages, "villages fetched successfully")
}
func (h *WilayahIndonesiaHandler) GetVillageByID(c *fiber.Ctx) error {
id := c.Params("villageid")
village, err := h.WilayahService.GetVillageByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.SuccessResponse(c, village, "Village fetched successfully")
}

View File

@ -1,112 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type AboutRepository interface {
CreateAbout(about *model.About) error
CreateAboutDetail(aboutDetail *model.AboutDetail) error
GetAllAbout() ([]model.About, error)
GetAboutByID(id string) (*model.About, error)
GetAboutByIDWithoutPrel(id string) (*model.About, error)
GetAboutDetailByID(id string) (*model.AboutDetail, error)
UpdateAbout(id string, about *model.About) (*model.About, error)
UpdateAboutDetail(id string, aboutDetail *model.AboutDetail) (*model.AboutDetail, error)
DeleteAbout(id string) error
DeleteAboutDetail(id string) error
}
type aboutRepository struct {
DB *gorm.DB
}
func NewAboutRepository(db *gorm.DB) AboutRepository {
return &aboutRepository{DB: db}
}
func (r *aboutRepository) CreateAbout(about *model.About) error {
if err := r.DB.Create(&about).Error; err != nil {
return fmt.Errorf("failed to create About: %v", err)
}
return nil
}
func (r *aboutRepository) CreateAboutDetail(aboutDetail *model.AboutDetail) error {
if err := r.DB.Create(&aboutDetail).Error; err != nil {
return fmt.Errorf("failed to create AboutDetail: %v", err)
}
return nil
}
func (r *aboutRepository) GetAllAbout() ([]model.About, error) {
var abouts []model.About
if err := r.DB.Find(&abouts).Error; err != nil {
return nil, fmt.Errorf("failed to fetch all About records: %v", err)
}
return abouts, nil
}
func (r *aboutRepository) GetAboutByID(id string) (*model.About, error) {
var about model.About
if err := r.DB.Preload("AboutDetail").Where("id = ?", id).First(&about).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("about with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch About by ID: %v", err)
}
return &about, nil
}
func (r *aboutRepository) GetAboutByIDWithoutPrel(id string) (*model.About, error) {
var about model.About
if err := r.DB.Where("id = ?", id).First(&about).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("about with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch About by ID: %v", err)
}
return &about, nil
}
func (r *aboutRepository) GetAboutDetailByID(id string) (*model.AboutDetail, error) {
var aboutDetail model.AboutDetail
if err := r.DB.Where("id = ?", id).First(&aboutDetail).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("aboutdetail with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch About by ID: %v", err)
}
return &aboutDetail, nil
}
func (r *aboutRepository) UpdateAbout(id string, about *model.About) (*model.About, error) {
if err := r.DB.Model(&about).Where("id = ?", id).Updates(about).Error; err != nil {
return nil, fmt.Errorf("failed to update About: %v", err)
}
return about, nil
}
func (r *aboutRepository) UpdateAboutDetail(id string, aboutDetail *model.AboutDetail) (*model.AboutDetail, error) {
if err := r.DB.Model(&aboutDetail).Where("id = ?", id).Updates(aboutDetail).Error; err != nil {
return nil, fmt.Errorf("failed to update AboutDetail: %v", err)
}
return aboutDetail, nil
}
func (r *aboutRepository) DeleteAbout(id string) error {
if err := r.DB.Where("id = ?", id).Delete(&model.About{}).Error; err != nil {
return fmt.Errorf("failed to delete About: %v", err)
}
return nil
}
func (r *aboutRepository) DeleteAboutDetail(id string) error {
if err := r.DB.Where("id = ?", id).Delete(&model.AboutDetail{}).Error; err != nil {
return fmt.Errorf("failed to delete AboutDetail: %v", err)
}
return nil
}

View File

@ -1,61 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type AddressRepository interface {
CreateAddress(address *model.Address) error
FindAddressByUserID(userID string) ([]model.Address, error)
FindAddressByID(id string) (*model.Address, error)
UpdateAddress(address *model.Address) error
DeleteAddress(id string) error
}
type addressRepository struct {
DB *gorm.DB
}
func NewAddressRepository(db *gorm.DB) AddressRepository {
return &addressRepository{DB: db}
}
func (r *addressRepository) CreateAddress(address *model.Address) error {
return r.DB.Create(address).Error
}
func (r *addressRepository) FindAddressByUserID(userID string) ([]model.Address, error) {
var addresses []model.Address
err := r.DB.Where("user_id = ?", userID).Find(&addresses).Error
if err != nil {
return nil, err
}
return addresses, nil
}
func (r *addressRepository) FindAddressByID(id string) (*model.Address, error) {
var address model.Address
err := r.DB.Where("id = ?", id).First(&address).Error
if err != nil {
return nil, err
}
return &address, nil
}
func (r *addressRepository) UpdateAddress(address *model.Address) error {
err := r.DB.Save(address).Error
if err != nil {
return err
}
return nil
}
func (r *addressRepository) DeleteAddress(id string) error {
err := r.DB.Where("id = ?", id).Delete(&model.Address{}).Error
if err != nil {
return err
}
return nil
}

View File

@ -1,75 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type ArticleRepository interface {
CreateArticle(article *model.Article) error
FindArticleByID(id string) (*model.Article, error)
FindAllArticles(page, limit int) ([]model.Article, int, error)
UpdateArticle(id string, article *model.Article) error
DeleteArticle(id string) error
}
type articleRepository struct {
DB *gorm.DB
}
func NewArticleRepository(db *gorm.DB) ArticleRepository {
return &articleRepository{DB: db}
}
func (r *articleRepository) CreateArticle(article *model.Article) error {
return r.DB.Create(article).Error
}
func (r *articleRepository) FindArticleByID(id string) (*model.Article, error) {
var article model.Article
err := r.DB.Where("id = ?", id).First(&article).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("article with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch article: %v", err)
}
return &article, nil
}
func (r *articleRepository) FindAllArticles(page, limit int) ([]model.Article, int, error) {
var articles []model.Article
var total int64
if err := r.DB.Model(&model.Article{}).Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count articles: %v", err)
}
fmt.Printf("Total Articles Count: %d\n", total)
if page > 0 && limit > 0 {
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&articles).Error
if err != nil {
return nil, 0, fmt.Errorf("failed to fetch articles: %v", err)
}
} else {
err := r.DB.Find(&articles).Error
if err != nil {
return nil, 0, fmt.Errorf("failed to fetch articles: %v", err)
}
}
return articles, int(total), nil
}
func (r *articleRepository) UpdateArticle(id string, article *model.Article) error {
return r.DB.Model(&model.Article{}).Where("id = ?", id).Updates(article).Error
}
func (r *articleRepository) DeleteArticle(id string) error {
result := r.DB.Delete(&model.Article{}, "id = ?", id)
return result.Error
}

View File

@ -1,93 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type AuthAdminRepository interface {
FindByEmail(email string) (*model.User, error)
FindAdminByEmailandRoleid(email, roleId string) (*model.User, error)
FindAdminByPhoneandRoleid(phone, roleId string) (*model.User, error)
FindByPhone(phone string) (*model.User, error)
FindByEmailOrPhone(identifier string) (*model.User, error)
FindRoleByName(roleName string) (*model.Role, error)
CreateUser(user *model.User) (*model.User, error)
}
type authAdminRepository struct {
DB *gorm.DB
}
func NewAuthAdminRepository(db *gorm.DB) AuthAdminRepository {
return &authAdminRepository{DB: db}
}
func (r *authAdminRepository) FindByEmail(email string) (*model.User, error) {
var user model.User
err := r.DB.Preload("Role").Where("email = ?", email).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}
func (r *authAdminRepository) FindAdminByEmailandRoleid(email, roleId string) (*model.User, error) {
var user model.User
err := r.DB.Where("email = ? AND role_id = ?", email, roleId).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}
func (r *authAdminRepository) FindAdminByPhoneandRoleid(phone, roleId string) (*model.User, error) {
var user model.User
err := r.DB.Where("phone = ? AND role_id = ?", phone, roleId).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}
func (r *authAdminRepository) FindByPhone(phone string) (*model.User, error) {
var user model.User
err := r.DB.Where("phone = ?", phone).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}
func (r *authAdminRepository) FindByEmailOrPhone(identifier string) (*model.User, error) {
var user model.User
err := r.DB.Where("email = ? OR phone = ?", identifier, identifier).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}
func (r *authAdminRepository) CreateUser(user *model.User) (*model.User, error) {
err := r.DB.Create(user).Error
if err != nil {
return nil, err
}
return user, nil
}
func (r *authAdminRepository) FindRoleByName(roleName string) (*model.Role, error) {
var role model.Role
err := r.DB.Where("role_name = ?", roleName).First(&role).Error
if err != nil {
return nil, err
}
return &role, nil
}

View File

@ -1,48 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type AuthMasyarakatRepository interface {
CreateUser(user *model.User) (*model.User, error)
GetUserByPhone(phone string) (*model.User, error)
GetUserByPhoneAndRole(phone string, roleId string) (*model.User, error)
}
type authMasyarakatRepository struct {
db *gorm.DB
}
func NewAuthMasyarakatRepositories(db *gorm.DB) AuthMasyarakatRepository {
return &authMasyarakatRepository{db}
}
func (r *authMasyarakatRepository) CreateUser(user *model.User) (*model.User, error) {
if err := r.db.Create(user).Error; err != nil {
return nil, err
}
return user, nil
}
func (r *authMasyarakatRepository) GetUserByPhone(phone string) (*model.User, error) {
var user model.User
if err := r.db.Where("phone = ?", phone).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
func (r *authMasyarakatRepository) GetUserByPhoneAndRole(phone string, roleId string) (*model.User, error) {
var user model.User
err := r.db.Where("phone = ? AND role_id = ?", phone, roleId).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}

View File

@ -1,48 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type AuthPengelolaRepository interface {
CreateUser(user *model.User) (*model.User, error)
GetUserByPhone(phone string) (*model.User, error)
GetUserByPhoneAndRole(phone string, roleId string) (*model.User, error)
}
type authPengelolaRepository struct {
db *gorm.DB
}
func NewAuthPengelolaRepositories(db *gorm.DB) AuthPengelolaRepository {
return &authPengelolaRepository{db}
}
func (r *authPengelolaRepository) CreateUser(user *model.User) (*model.User, error) {
if err := r.db.Create(user).Error; err != nil {
return nil, err
}
return user, nil
}
func (r *authPengelolaRepository) GetUserByPhone(phone string) (*model.User, error) {
var user model.User
if err := r.db.Where("phone = ?", phone).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
func (r *authPengelolaRepository) GetUserByPhoneAndRole(phone string, roleId string) (*model.User, error) {
var user model.User
err := r.db.Where("phone = ? AND role_id = ?", phone, roleId).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}

View File

@ -1,48 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type AuthPengepulRepository interface {
CreateUser(user *model.User) (*model.User, error)
GetUserByPhone(phone string) (*model.User, error)
GetUserByPhoneAndRole(phone string, roleId string) (*model.User, error)
}
type authPengepulRepository struct {
db *gorm.DB
}
func NewAuthPengepulRepositories(db *gorm.DB) AuthPengepulRepository {
return &authPengepulRepository{db}
}
func (r *authPengepulRepository) CreateUser(user *model.User) (*model.User, error) {
if err := r.db.Create(user).Error; err != nil {
return nil, err
}
return user, nil
}
func (r *authPengepulRepository) GetUserByPhone(phone string) (*model.User, error) {
var user model.User
if err := r.db.Where("phone = ?", phone).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
func (r *authPengepulRepository) GetUserByPhoneAndRole(phone string, roleId string) (*model.User, error) {
var user model.User
err := r.db.Where("phone = ? AND role_id = ?", phone, roleId).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}

View File

@ -1,48 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type UserRepository interface {
CreateUser(user *model.User) (*model.User, error)
GetUserByPhone(phone string) (*model.User, error)
GetUserByPhoneAndRole(phone string, roleID string) (*model.User, error)
}
type userRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db}
}
func (r *userRepository) CreateUser(user *model.User) (*model.User, error) {
if err := r.db.Create(user).Error; err != nil {
return nil, err
}
return user, nil
}
func (r *userRepository) GetUserByPhone(phone string) (*model.User, error) {
var user model.User
if err := r.db.Where("phone = ?", phone).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
func (r *userRepository) GetUserByPhoneAndRole(phone string, roleID string) (*model.User, error) {
var user model.User
err := r.db.Where("phone = ? AND role_id = ?", phone, roleID).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}

View File

@ -1,70 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type BannerRepository interface {
CreateBanner(banner *model.Banner) error
FindBannerByID(id string) (*model.Banner, error)
FindAllBanners() ([]model.Banner, error)
UpdateBanner(id string, banner *model.Banner) error
DeleteBanner(id string) error
}
type bannerRepository struct {
DB *gorm.DB
}
func NewBannerRepository(db *gorm.DB) BannerRepository {
return &bannerRepository{DB: db}
}
func (r *bannerRepository) CreateBanner(banner *model.Banner) error {
if err := r.DB.Create(banner).Error; err != nil {
return fmt.Errorf("failed to create banner: %v", err)
}
return nil
}
func (r *bannerRepository) FindBannerByID(id string) (*model.Banner, error) {
var banner model.Banner
err := r.DB.Where("id = ?", id).First(&banner).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("banner with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch banner by ID: %v", err)
}
return &banner, nil
}
func (r *bannerRepository) FindAllBanners() ([]model.Banner, error) {
var banners []model.Banner
err := r.DB.Find(&banners).Error
if err != nil {
return nil, fmt.Errorf("failed to fetch banners: %v", err)
}
return banners, nil
}
func (r *bannerRepository) UpdateBanner(id string, banner *model.Banner) error {
err := r.DB.Model(&model.Banner{}).Where("id = ?", id).Updates(banner).Error
if err != nil {
return fmt.Errorf("failed to update banner: %v", err)
}
return nil
}
func (r *bannerRepository) DeleteBanner(id string) error {
result := r.DB.Delete(&model.Banner{}, "id = ?", id)
if result.Error != nil {
return fmt.Errorf("failed to delete banner: %v", result.Error)
}
return nil
}

View File

@ -1,135 +0,0 @@
package repositories
import (
"context"
"errors"
"rijig/config"
"rijig/model"
)
type CollectorRepository interface {
CreateCollector(ctx context.Context, collector *model.Collector) error
AddAvaibleTrash(ctx context.Context, trashItems []model.AvaibleTrashByCollector) error
GetCollectorByID(ctx context.Context, collectorID string) (*model.Collector, error)
GetCollectorByUserID(ctx context.Context, userID string) (*model.Collector, error)
GetTrashItemByID(ctx context.Context, id string) (*model.AvaibleTrashByCollector, error)
UpdateCollector(ctx context.Context, collector *model.Collector, updates map[string]interface{}) error
UpdateAvaibleTrashByCollector(ctx context.Context, collectorID string, updatedTrash []model.AvaibleTrashByCollector) 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 {
}
func NewCollectorRepository() CollectorRepository {
return &collectorRepository{}
}
func (r *collectorRepository) CreateCollector(ctx context.Context, collector *model.Collector) error {
return config.DB.WithContext(ctx).Create(collector).Error
}
func (r *collectorRepository) AddAvaibleTrash(ctx context.Context, trashItems []model.AvaibleTrashByCollector) error {
if len(trashItems) == 0 {
return nil
}
return config.DB.WithContext(ctx).Create(&trashItems).Error
}
func (r *collectorRepository) GetCollectorByID(ctx context.Context, collectorID string) (*model.Collector, error) {
var collector model.Collector
err := config.DB.WithContext(ctx).
Preload("User").
Preload("Address").
Preload("AvaibleTrashByCollector.TrashCategory").
First(&collector, "id = ?", collectorID).Error
if err != nil {
return nil, err
}
return &collector, nil
}
func (r *collectorRepository) GetCollectorByUserID(ctx context.Context, userID string) (*model.Collector, error) {
var collector model.Collector
err := config.DB.WithContext(ctx).
Preload("User").
Preload("Address").
Preload("AvaibleTrashByCollector.TrashCategory").
First(&collector, "user_id = ?", userID).Error
if err != nil {
return nil, err
}
return &collector, nil
}
func (r *collectorRepository) GetTrashItemByID(ctx context.Context, id string) (*model.AvaibleTrashByCollector, error) {
var item model.AvaibleTrashByCollector
if err := config.DB.WithContext(ctx).First(&item, "id = ?", id).Error; err != nil {
return nil, err
}
return &item, nil
}
func (r *collectorRepository) UpdateCollector(ctx context.Context, collector *model.Collector, updates map[string]interface{}) error {
return config.DB.WithContext(ctx).
Model(&model.Collector{}).
Where("id = ?", collector.ID).
Updates(updates).Error
}
func (r *collectorRepository) UpdateAvaibleTrashByCollector(ctx context.Context, collectorID string, updatedTrash []model.AvaibleTrashByCollector) error {
for _, trash := range updatedTrash {
err := config.DB.WithContext(ctx).
Model(&model.AvaibleTrashByCollector{}).
Where("collector_id = ? AND trash_category_id = ?", collectorID, trash.TrashCategoryID).
Update("price", trash.Price).Error
if err != nil {
return err
}
}
return nil
}
func (r *collectorRepository) DeleteAvaibleTrash(ctx context.Context, trashID string) error {
if trashID == "" {
return errors.New("trash_id cannot be empty")
}
return config.DB.WithContext(ctx).
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

@ -1,78 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type CompanyProfileRepository interface {
CreateCompanyProfile(companyProfile *model.CompanyProfile) (*model.CompanyProfile, error)
GetCompanyProfileByID(id string) (*model.CompanyProfile, error)
GetCompanyProfilesByUserID(userID string) ([]model.CompanyProfile, error)
UpdateCompanyProfile(id string, companyProfile *model.CompanyProfile) (*model.CompanyProfile, error)
DeleteCompanyProfile(id string) error
}
type companyProfileRepository struct {
DB *gorm.DB
}
func NewCompanyProfileRepository(db *gorm.DB) CompanyProfileRepository {
return &companyProfileRepository{
DB: db,
}
}
func (r *companyProfileRepository) CreateCompanyProfile(companyProfile *model.CompanyProfile) (*model.CompanyProfile, error) {
err := r.DB.Create(companyProfile).Error
if err != nil {
return nil, fmt.Errorf("failed to create company profile: %v", err)
}
return companyProfile, nil
}
func (r *companyProfileRepository) GetCompanyProfileByID(id string) (*model.CompanyProfile, error) {
var companyProfile model.CompanyProfile
err := r.DB.Preload("User").First(&companyProfile, "id = ?", id).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("company profile with ID %s not found", id)
}
return nil, fmt.Errorf("error fetching company profile: %v", err)
}
return &companyProfile, nil
}
func (r *companyProfileRepository) GetCompanyProfilesByUserID(userID string) ([]model.CompanyProfile, error) {
var companyProfiles []model.CompanyProfile
err := r.DB.Preload("User").Where("user_id = ?", userID).Find(&companyProfiles).Error
if err != nil {
return nil, fmt.Errorf("error fetching company profiles for userID %s: %v", userID, err)
}
return companyProfiles, nil
}
func (r *companyProfileRepository) UpdateCompanyProfile(id string, companyProfile *model.CompanyProfile) (*model.CompanyProfile, error) {
var existingProfile model.CompanyProfile
err := r.DB.First(&existingProfile, "id = ?", id).Error
if err != nil {
return nil, fmt.Errorf("company profile not found: %v", err)
}
err = r.DB.Model(&existingProfile).Updates(companyProfile).Error
if err != nil {
return nil, fmt.Errorf("failed to update company profile: %v", err)
}
return &existingProfile, nil
}
func (r *companyProfileRepository) DeleteCompanyProfile(id string) error {
err := r.DB.Delete(&model.CompanyProfile{}, "id = ?", id).Error
if err != nil {
return fmt.Errorf("failed to delete company profile: %v", err)
}
return nil
}

View File

@ -1,82 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type CoverageAreaRepository interface {
FindCoverageByProvinceAndRegency(province, regency string) (*model.CoverageArea, error)
CreateCoverage(coverage *model.CoverageArea) error
FindCoverageById(id string) (*model.CoverageArea, error)
FindAllCoverage() ([]model.CoverageArea, error)
UpdateCoverage(id string, coverage *model.CoverageArea) error
DeleteCoverage(id string) error
}
type coverageAreaRepository struct {
DB *gorm.DB
}
func NewCoverageAreaRepository(db *gorm.DB) CoverageAreaRepository {
return &coverageAreaRepository{DB: db}
}
func (r *coverageAreaRepository) FindCoverageByProvinceAndRegency(province, regency string) (*model.CoverageArea, error) {
var coverage model.CoverageArea
err := r.DB.Where("province = ? AND regency = ?", province, regency).First(&coverage).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &coverage, nil
}
func (r *coverageAreaRepository) CreateCoverage(coverage *model.CoverageArea) error {
if err := r.DB.Create(coverage).Error; err != nil {
return fmt.Errorf("failed to create coverage: %v", err)
}
return nil
}
func (r *coverageAreaRepository) FindCoverageById(id string) (*model.CoverageArea, error) {
var coverage model.CoverageArea
err := r.DB.Where("id = ?", id).First(&coverage).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("coverage with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch coverage by ID: %v", err)
}
return &coverage, nil
}
func (r *coverageAreaRepository) FindAllCoverage() ([]model.CoverageArea, error) {
var coverage []model.CoverageArea
err := r.DB.Find(&coverage).Error
if err != nil {
return nil, fmt.Errorf("failed to fetch coverage: %v", err)
}
return coverage, nil
}
func (r *coverageAreaRepository) UpdateCoverage(id string, coverage *model.CoverageArea) error {
err := r.DB.Model(&model.CoverageArea{}).Where("id = ?", id).Updates(coverage).Error
if err != nil {
return fmt.Errorf("failed to update coverage: %v", err)
}
return nil
}
func (r *coverageAreaRepository) DeleteCoverage(id string) error {
result := r.DB.Delete(&model.CoverageArea{}, "id = ?", id)
if result.Error != nil {
return fmt.Errorf("failed to delete coverage: %v", result.Error)
}
return nil
}

View File

@ -1,82 +0,0 @@
package repositories
import (
"errors"
"fmt"
"log"
"rijig/model"
"gorm.io/gorm"
)
type IdentityCardRepository interface {
CreateIdentityCard(identityCard *model.IdentityCard) (*model.IdentityCard, error)
GetIdentityCardByID(id string) (*model.IdentityCard, error)
GetIdentityCardsByUserID(userID string) ([]model.IdentityCard, error)
UpdateIdentityCard(id string, updatedCard *model.IdentityCard) (*model.IdentityCard, error)
DeleteIdentityCard(id string) error
}
type identityCardRepository struct {
db *gorm.DB
}
func NewIdentityCardRepository(db *gorm.DB) IdentityCardRepository {
return &identityCardRepository{
db: db,
}
}
func (r *identityCardRepository) CreateIdentityCard(identityCard *model.IdentityCard) (*model.IdentityCard, error) {
if err := r.db.Create(identityCard).Error; err != nil {
log.Printf("Error creating identity card: %v", err)
return nil, fmt.Errorf("failed to create identity card: %w", err)
}
return identityCard, nil
}
func (r *identityCardRepository) GetIdentityCardByID(id string) (*model.IdentityCard, error) {
var identityCard model.IdentityCard
if err := r.db.Where("id = ?", id).First(&identityCard).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("identity card not found with id %s", id)
}
log.Printf("Error fetching identity card by ID: %v", err)
return nil, fmt.Errorf("error fetching identity card by ID: %w", err)
}
return &identityCard, nil
}
func (r *identityCardRepository) GetIdentityCardsByUserID(userID string) ([]model.IdentityCard, error) {
var identityCards []model.IdentityCard
if err := r.db.Where("user_id = ?", userID).Find(&identityCards).Error; err != nil {
log.Printf("Error fetching identity cards by userID: %v", err)
return nil, fmt.Errorf("error fetching identity cards by userID: %w", err)
}
return identityCards, nil
}
func (r *identityCardRepository) UpdateIdentityCard(id string, updatedCard *model.IdentityCard) (*model.IdentityCard, error) {
var existingCard model.IdentityCard
if err := r.db.Where("id = ?", id).First(&existingCard).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("identity card with ID %s not found", id)
}
log.Printf("Error fetching identity card for update: %v", err)
return nil, fmt.Errorf("error fetching identity card for update: %w", err)
}
if err := r.db.Save(&existingCard).Error; err != nil {
log.Printf("Error updating identity card: %v", err)
return nil, fmt.Errorf("failed to update identity card: %w", err)
}
return &existingCard, nil
}
func (r *identityCardRepository) DeleteIdentityCard(id string) error {
if err := r.db.Where("id = ?", id).Delete(&model.IdentityCard{}).Error; err != nil {
log.Printf("Error deleting identity card: %v", err)
return fmt.Errorf("failed to delete identity card: %w", err)
}
return nil
}

View File

@ -1,68 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type InitialCointRepository interface {
CreateInitialCoint(coint *model.InitialCoint) error
FindInitialCointByID(id string) (*model.InitialCoint, error)
FindAllInitialCoints() ([]model.InitialCoint, error)
UpdateInitialCoint(id string, coint *model.InitialCoint) error
DeleteInitialCoint(id string) error
}
type initialCointRepository struct {
DB *gorm.DB
}
func NewInitialCointRepository(db *gorm.DB) InitialCointRepository {
return &initialCointRepository{DB: db}
}
func (r *initialCointRepository) CreateInitialCoint(coint *model.InitialCoint) error {
if err := r.DB.Create(coint).Error; err != nil {
return fmt.Errorf("failed to create initial coint: %v", err)
}
return nil
}
func (r *initialCointRepository) FindInitialCointByID(id string) (*model.InitialCoint, error) {
var coint model.InitialCoint
err := r.DB.Where("id = ?", id).First(&coint).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("initial coint with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch initial coint by ID: %v", err)
}
return &coint, nil
}
func (r *initialCointRepository) FindAllInitialCoints() ([]model.InitialCoint, error) {
var coints []model.InitialCoint
err := r.DB.Find(&coints).Error
if err != nil {
return nil, fmt.Errorf("failed to fetch initial coints: %v", err)
}
return coints, nil
}
func (r *initialCointRepository) UpdateInitialCoint(id string, coint *model.InitialCoint) error {
err := r.DB.Model(&model.InitialCoint{}).Where("id = ?", id).Updates(coint).Error
if err != nil {
return fmt.Errorf("failed to update initial coint: %v", err)
}
return nil
}
func (r *initialCointRepository) DeleteInitialCoint(id string) error {
result := r.DB.Delete(&model.InitialCoint{}, "id = ?", id)
if result.Error != nil {
return fmt.Errorf("failed to delete initial coint: %v", result.Error)
}
return nil
}

View File

@ -1,34 +0,0 @@
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

@ -1,139 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type ProductRepository interface {
CountProductsByStoreID(storeID string) (int64, error)
CreateProduct(product *model.Product) error
GetProductByID(productID string) (*model.Product, error)
GetProductsByStoreID(storeID string) ([]model.Product, error)
FindProductsByStoreID(storeID string, page, limit int) ([]model.Product, error)
FindProductImagesByProductID(productID string) ([]model.ProductImage, error)
GetProductImageByID(imageID string) (*model.ProductImage, error)
UpdateProduct(product *model.Product) error
DeleteProduct(productID string) error
DeleteProductsByID(productIDs []string) error
AddProductImages(images []model.ProductImage) error
DeleteProductImagesByProductID(productID string) error
DeleteProductImagesByID(imageIDs []string) error
DeleteProductImageByID(imageID string) error
}
type productRepository struct {
DB *gorm.DB
}
func NewProductRepository(DB *gorm.DB) ProductRepository {
return &productRepository{DB}
}
func (r *productRepository) CreateProduct(product *model.Product) error {
return r.DB.Create(product).Error
}
func (r *productRepository) CountProductsByStoreID(storeID string) (int64, error) {
var count int64
if err := r.DB.Model(&model.Product{}).Where("store_id = ?", storeID).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}
func (r *productRepository) GetProductByID(productID string) (*model.Product, error) {
var product model.Product
if err := r.DB.Preload("ProductImages").Where("id = ?", productID).First(&product).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &product, nil
}
func (r *productRepository) GetProductsByStoreID(storeID string) ([]model.Product, error) {
var products []model.Product
if err := r.DB.Where("store_id = ?", storeID).Preload("ProductImages").Find(&products).Error; err != nil {
return nil, err
}
return products, nil
}
func (r *productRepository) FindProductsByStoreID(storeID string, page, limit int) ([]model.Product, error) {
var products []model.Product
offset := (page - 1) * limit
if err := r.DB.
Where("store_id = ?", storeID).
Limit(limit).
Offset(offset).
Find(&products).Error; err != nil {
return nil, err
}
return products, nil
}
func (r *productRepository) FindProductImagesByProductID(productID string) ([]model.ProductImage, error) {
var productImages []model.ProductImage
if err := r.DB.Where("product_id = ?", productID).Find(&productImages).Error; err != nil {
return nil, err
}
return productImages, nil
}
func (r *productRepository) GetProductImageByID(imageID string) (*model.ProductImage, error) {
var productImage model.ProductImage
if err := r.DB.Where("id = ?", imageID).First(&productImage).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &productImage, nil
}
func (r *productRepository) UpdateProduct(product *model.Product) error {
return r.DB.Save(product).Error
}
func (r *productRepository) DeleteProduct(productID string) error {
return r.DB.Delete(&model.Product{}, "id = ?", productID).Error
}
func (r *productRepository) DeleteProductsByID(productIDs []string) error {
if err := r.DB.Where("id IN ?", productIDs).Delete(&model.Product{}).Error; err != nil {
return fmt.Errorf("failed to delete products: %v", err)
}
return nil
}
func (r *productRepository) AddProductImages(images []model.ProductImage) error {
if len(images) == 0 {
return nil
}
return r.DB.Create(&images).Error
}
func (r *productRepository) DeleteProductImagesByProductID(productID string) error {
return r.DB.Where("product_id = ?", productID).Delete(&model.ProductImage{}).Error
}
func (r *productRepository) DeleteProductImagesByID(imageIDs []string) error {
if err := r.DB.Where("id IN ?", imageIDs).Delete(&model.ProductImage{}).Error; err != nil {
return fmt.Errorf("failed to delete product images: %v", err)
}
return nil
}
func (r *productRepository) DeleteProductImageByID(imageID string) error {
if err := r.DB.Where("id = ?", imageID).Delete(&model.ProductImage{}).Error; err != nil {
return fmt.Errorf("failed to delete product image: %v", err)
}
return nil
}

View File

@ -1,48 +0,0 @@
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

@ -1,143 +0,0 @@
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,49 +0,0 @@
package repositories
import (
"context"
"rijig/model"
"gorm.io/gorm"
)
type RoleRepository interface {
FindByID(ctx context.Context, id string) (*model.Role, error)
FindRoleByName(ctx context.Context, roleName string) (*model.Role, error)
FindAll(ctx context.Context) ([]model.Role, error)
}
type roleRepository struct {
db *gorm.DB
}
func NewRoleRepository(db *gorm.DB) RoleRepository {
return &roleRepository{db}
}
func (r *roleRepository) FindByID(ctx context.Context, id string) (*model.Role, error) {
var role model.Role
err := r.db.WithContext(ctx).Where("id = ?", id).First(&role).Error
if err != nil {
return nil, err
}
return &role, nil
}
func (r *roleRepository) FindRoleByName(ctx context.Context, roleName string) (*model.Role, error) {
var role model.Role
err := r.db.WithContext(ctx).Where("role_name = ?", roleName).First(&role).Error
if err != nil {
return nil, err
}
return &role, nil
}
func (r *roleRepository) FindAll(ctx context.Context) ([]model.Role, error) {
var roles []model.Role
err := r.db.WithContext(ctx).Find(&roles).Error
if err != nil {
return nil, err
}
return roles, nil
}

View File

@ -1,88 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type StoreRepository interface {
FindStoreByUserID(userID string) (*model.Store, error)
FindStoreByID(storeID string) (*model.Store, error)
FindAddressByID(addressID string) (*model.Address, error)
CreateStore(store *model.Store) error
UpdateStore(store *model.Store) error
DeleteStore(storeID string) error
}
type storeRepository struct {
DB *gorm.DB
}
func NewStoreRepository(DB *gorm.DB) StoreRepository {
return &storeRepository{DB}
}
func (r *storeRepository) FindStoreByUserID(userID string) (*model.Store, error) {
var store model.Store
if err := r.DB.Where("user_id = ?", userID).First(&store).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &store, nil
}
func (r *storeRepository) FindStoreByID(storeID string) (*model.Store, error) {
var store model.Store
if err := r.DB.Where("id = ?", storeID).First(&store).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &store, nil
}
func (r *storeRepository) FindAddressByID(addressID string) (*model.Address, error) {
var address model.Address
if err := r.DB.Where("id = ?", addressID).First(&address).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &address, nil
}
func (r *storeRepository) CreateStore(store *model.Store) error {
if err := r.DB.Create(store).Error; err != nil {
return err
}
return nil
}
func (r *storeRepository) UpdateStore(store *model.Store) error {
if err := r.DB.Save(store).Error; err != nil {
return err
}
return nil
}
func (r *storeRepository) DeleteStore(storeID string) error {
if storeID == "" {
return fmt.Errorf("store ID cannot be empty")
}
if err := r.DB.Where("id = ?", storeID).Delete(&model.Store{}).Error; err != nil {
return fmt.Errorf("failed to delete store: %w", err)
}
return nil
}

View File

@ -1,175 +0,0 @@
package repositories
import (
"context"
"errors"
"fmt"
"log"
"rijig/config"
"rijig/model"
"gorm.io/gorm"
)
type TrashRepository interface {
CreateCategory(category *model.TrashCategory) error
AddDetailToCategory(detail *model.TrashDetail) error
GetCategories() ([]model.TrashCategory, error)
GetCategoryByID(id string) (*model.TrashCategory, error)
FindCategoryId(id string) (*model.TrashCategory, error)
GetTrashCategoryByName(name string) (*model.TrashCategory, error)
GetTrashDetailByID(id string) (*model.TrashDetail, error)
GetTrashCategoryByID(ctx context.Context, id string) (*model.TrashCategory, error)
GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error)
UpdateCategoryName(id string, newName string) error
UpdateCategory(id string, updateTrashCategory *model.TrashCategory) (*model.TrashCategory, error)
UpdateTrashDetail(id string, description string, price float64) error
UpdateEstimatedPrice(ctx context.Context, trashCategoryID string) error
DeleteCategory(id string) error
DeleteTrashDetail(id string) error
}
type trashRepository struct {
DB *gorm.DB
}
func NewTrashRepository(db *gorm.DB) TrashRepository {
return &trashRepository{DB: db}
}
func (r *trashRepository) CreateCategory(category *model.TrashCategory) error {
if err := r.DB.Create(category).Error; err != nil {
return fmt.Errorf("failed to create category: %v", err)
}
return nil
}
func (r *trashRepository) AddDetailToCategory(detail *model.TrashDetail) error {
if err := r.DB.Create(detail).Error; err != nil {
return fmt.Errorf("failed to add detail to category: %v", err)
}
return nil
}
func (r *trashRepository) GetCategories() ([]model.TrashCategory, error) {
var categories []model.TrashCategory
if err := r.DB.Preload("Details").Find(&categories).Error; err != nil {
return nil, fmt.Errorf("failed to fetch categories: %v", err)
}
return categories, nil
}
func (r *trashRepository) GetCategoryByID(id string) (*model.TrashCategory, error) {
var category model.TrashCategory
if err := r.DB.Preload("Details").First(&category, "id = ?", id).Error; err != nil {
return nil, fmt.Errorf("category not found: %v", err)
}
return &category, nil
}
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) {
var category model.TrashCategory
if err := r.DB.First(&category, "id = ?", id).Error; err != nil {
return nil, fmt.Errorf("category not found: %v", err)
}
return &category, nil
}
func (r *trashRepository) GetTrashCategoryByName(name string) (*model.TrashCategory, error) {
var category model.TrashCategory
if err := r.DB.Find(&category, "name = ?", name).Error; err != nil {
return nil, fmt.Errorf("category not found: %v", err)
}
return &category, nil
}
func (r *trashRepository) GetTrashDetailByID(id string) (*model.TrashDetail, error) {
var detail model.TrashDetail
if err := r.DB.First(&detail, "id = ?", id).Error; err != nil {
return nil, fmt.Errorf("trash detail not found: %v", err)
}
return &detail, nil
}
func (r *trashRepository) GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error) {
var details []model.TrashDetail
if err := r.DB.Where("category_id = ?", categoryID).Find(&details).Error; err != nil {
return nil, fmt.Errorf("failed to fetch details for category %s: %v", categoryID, err)
}
return details, nil
}
func (r *trashRepository) UpdateCategoryName(id string, newName string) error {
if err := r.DB.Model(&model.TrashCategory{}).Where("id = ?", id).Update("name", newName).Error; err != nil {
return fmt.Errorf("failed to update category name: %v", err)
}
return nil
}
func (r *trashRepository) UpdateCategory(id string, updateTrashCategory *model.TrashCategory) (*model.TrashCategory, error) {
var existingtrashCtgry model.TrashCategory
if err := r.DB.Where("id = ?", id).First(&existingtrashCtgry).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("trashCategory with ID %s not found", id)
}
log.Printf("Error fetching trash category for update: %v", err)
return nil, fmt.Errorf("error fetching trash category for update: %w", err)
}
if err := r.DB.Save(&existingtrashCtgry).Error; err != nil {
log.Printf("Error updating trash category: %v", err)
return nil, fmt.Errorf("failed to update trash category: %w", err)
}
return &existingtrashCtgry, nil
}
func (r *trashRepository) UpdateTrashDetail(id string, description string, price float64) error {
if err := r.DB.Model(&model.TrashDetail{}).Where("id = ?", id).Updates(model.TrashDetail{Description: description, Price: price}).Error; err != nil {
return fmt.Errorf("failed to update trash detail: %v", err)
}
return nil
}
func (r *trashRepository) UpdateEstimatedPrice(ctx context.Context, trashCategoryID string) error {
var avg float64
err := config.DB.WithContext(ctx).
Model(&model.AvaibleTrashByCollector{}).
Where("trash_category_id = ?", trashCategoryID).
Select("AVG(price)").Scan(&avg).Error
if err != nil {
return err
}
return config.DB.WithContext(ctx).
Model(&model.TrashCategory{}).
Where("id = ?", trashCategoryID).
Update("estimated_price", avg).Error
}
func (r *trashRepository) DeleteCategory(id string) error {
if err := r.DB.Delete(&model.TrashCategory{}, "id = ?", id).Error; err != nil {
return fmt.Errorf("failed to delete category: %v", err)
}
return nil
}
func (r *trashRepository) DeleteTrashDetail(id string) error {
if err := r.DB.Delete(&model.TrashDetail{}, "id = ?", id).Error; err != nil {
return fmt.Errorf("failed to delete trash detail: %v", err)
}
return nil
}

View File

@ -1,166 +0,0 @@
package repositories
import (
"context"
"errors"
"fmt"
"rijig/config"
"rijig/model"
"gorm.io/gorm"
)
type CartRepository interface {
FindOrCreateCart(ctx context.Context, userID string) (*model.Cart, error)
AddOrUpdateCartItem(ctx context.Context, cartID, trashCategoryID string, amount float64, estimatedPrice float64) error
DeleteCartItem(ctx context.Context, cartID, trashCategoryID string) error
GetCartByUser(ctx context.Context, userID string) (*model.Cart, error)
UpdateCartTotals(ctx context.Context, cartID string) error
DeleteCart(ctx context.Context, userID string) error
// New method for batch cart creation
CreateCartWithItems(ctx context.Context, cart *model.Cart) error
// Check if user already has a cart
HasExistingCart(ctx context.Context, userID string) (bool, error)
}
type cartRepository struct{}
func NewCartRepository() CartRepository {
return &cartRepository{}
}
func (r *cartRepository) FindOrCreateCart(ctx context.Context, userID string) (*model.Cart, error) {
var cart model.Cart
db := config.DB.WithContext(ctx)
err := db.
Preload("CartItems.TrashCategory").
Where("user_id = ?", userID).
First(&cart).Error
if err == nil {
return &cart, nil
}
if errors.Is(err, gorm.ErrRecordNotFound) {
newCart := model.Cart{
UserID: userID,
TotalAmount: 0,
EstimatedTotalPrice: 0,
}
if err := db.Create(&newCart).Error; err != nil {
return nil, err
}
return &newCart, nil
}
return nil, err
}
func (r *cartRepository) AddOrUpdateCartItem(ctx context.Context, cartID, trashCategoryID string, amount float64, estimatedPrice float64) error {
db := config.DB.WithContext(ctx)
var item model.CartItem
err := db.
Where("cart_id = ? AND trash_category_id = ?", cartID, trashCategoryID).
First(&item).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
newItem := model.CartItem{
CartID: cartID,
TrashCategoryID: trashCategoryID,
Amount: amount,
SubTotalEstimatedPrice: amount * estimatedPrice,
}
return db.Create(&newItem).Error
}
if err != nil {
return err
}
item.Amount = amount
item.SubTotalEstimatedPrice = amount * estimatedPrice
return db.Save(&item).Error
}
func (r *cartRepository) DeleteCartItem(ctx context.Context, cartID, trashCategoryID string) error {
db := config.DB.WithContext(ctx)
return db.Where("cart_id = ? AND trash_category_id = ?", cartID, trashCategoryID).
Delete(&model.CartItem{}).Error
}
func (r *cartRepository) GetCartByUser(ctx context.Context, userID string) (*model.Cart, error) {
var cart model.Cart
db := config.DB.WithContext(ctx)
err := db.
Preload("CartItems.TrashCategory").
Where("user_id = ?", userID).
First(&cart).Error
if err != nil {
return nil, err
}
return &cart, nil
}
func (r *cartRepository) UpdateCartTotals(ctx context.Context, cartID string) error {
db := config.DB.WithContext(ctx)
var items []model.CartItem
if err := db.Where("cart_id = ?", cartID).Find(&items).Error; err != nil {
return err
}
var totalAmount float64
var totalPrice float64
for _, item := range items {
totalAmount += item.Amount
totalPrice += item.SubTotalEstimatedPrice
}
return db.Model(&model.Cart{}).
Where("id = ?", cartID).
Updates(map[string]interface{}{
"total_amount": totalAmount,
"estimated_total_price": totalPrice,
}).Error
}
func (r *cartRepository) DeleteCart(ctx context.Context, userID string) error {
db := config.DB.WithContext(ctx)
var cart model.Cart
if err := db.Where("user_id = ?", userID).First(&cart).Error; err != nil {
return err
}
return db.Delete(&cart).Error
}
// New method for batch cart creation with transaction
func (r *cartRepository) CreateCartWithItems(ctx context.Context, cart *model.Cart) error {
db := config.DB.WithContext(ctx)
// Use transaction to ensure data consistency
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(cart).Error; err != nil {
return fmt.Errorf("failed to create cart: %w", err)
}
return nil
})
}
// Check if user already has a cart
func (r *cartRepository) HasExistingCart(ctx context.Context, userID string) (bool, error) {
db := config.DB.WithContext(ctx)
var count int64
err := db.Model(&model.Cart{}).Where("user_id = ?", userID).Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}

View File

@ -1,77 +0,0 @@
package repositories
import (
"fmt"
"rijig/model"
"gorm.io/gorm"
)
type UserProfilRepository interface {
FindByID(userID string) (*model.User, error)
FindAll(page, limit int) ([]model.User, error)
Update(user *model.User) error
UpdateAvatar(userID, avatarURL string) error
UpdatePassword(userID string, newPassword string) error
}
type userProfilRepository struct {
DB *gorm.DB
}
func NewUserProfilRepository(db *gorm.DB) UserProfilRepository {
return &userProfilRepository{DB: db}
}
func (r *userProfilRepository) FindByID(userID string) (*model.User, error) {
var user model.User
err := r.DB.Preload("Role").Where("id = ?", userID).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("user with ID %s not found", userID)
}
return nil, fmt.Errorf("error finding user with ID %s: %v", userID, err)
}
if user.Role == nil {
return nil, fmt.Errorf("role not found for user ID %s", userID)
}
return &user, nil
}
func (r *userProfilRepository) FindAll(page, limit int) ([]model.User, error) {
var users []model.User
offset := (page - 1) * limit
err := r.DB.Preload("Role").Offset(offset).Limit(limit).Find(&users).Error
if err != nil {
return nil, fmt.Errorf("error finding all users: %v", err)
}
return users, nil
}
func (r *userProfilRepository) Update(user *model.User) error {
err := r.DB.Save(user).Error
if err != nil {
return fmt.Errorf("error updating user: %v", err)
}
return nil
}
func (r *userProfilRepository) UpdateAvatar(userID, avatarURL string) error {
var user model.User
err := r.DB.Model(&user).Where("id = ?", userID).Update("avatar", avatarURL).Error
if err != nil {
return fmt.Errorf("error updating avatar for user ID %s: %v", userID, err)
}
return nil
}
func (r *userProfilRepository) UpdatePassword(userID string, newPassword string) error {
var user model.User
err := r.DB.Model(&user).Where("id = ?", userID).Update("password", newPassword).Error
if err != nil {
return fmt.Errorf("error updating password for user ID %s: %v", userID, err)
}
return nil
}

View File

@ -1,60 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type UserPinRepository interface {
FindByUserID(userID string) (*model.UserPin, error)
FindByPin(userPin string) (*model.UserPin, error)
Create(userPin *model.UserPin) error
Update(userPin *model.UserPin) error
}
type userPinRepository struct {
DB *gorm.DB
}
func NewUserPinRepository(db *gorm.DB) UserPinRepository {
return &userPinRepository{DB: db}
}
func (r *userPinRepository) FindByUserID(userID string) (*model.UserPin, error) {
var userPin model.UserPin
err := r.DB.Where("user_id = ?", userID).First(&userPin).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &userPin, nil
}
func (r *userPinRepository) FindByPin(pin string) (*model.UserPin, error) {
var userPin model.UserPin
err := r.DB.Where("pin = ?", pin).First(&userPin).Error
if err != nil {
return nil, err
}
return &userPin, nil
}
func (r *userPinRepository) Create(userPin *model.UserPin) error {
err := r.DB.Create(userPin).Error
if err != nil {
return err
}
return nil
}
func (r *userPinRepository) Update(userPin *model.UserPin) error {
err := r.DB.Save(userPin).Error
if err != nil {
return err
}
return nil
}

View File

@ -1,244 +0,0 @@
package repositories
import (
"rijig/model"
"gorm.io/gorm"
)
type WilayahIndonesiaRepository interface {
ImportProvinces(provinces []model.Province) error
ImportRegencies(regencies []model.Regency) error
ImportDistricts(districts []model.District) error
ImportVillages(villages []model.Village) error
FindAllProvinces(page, limit int) ([]model.Province, int, error)
FindProvinceByID(id string, page, limit int) (*model.Province, int, error)
FindAllRegencies(page, limit int) ([]model.Regency, int, error)
FindRegencyByID(id string, page, limit int) (*model.Regency, int, error)
FindAllDistricts(page, limit int) ([]model.District, int, error)
FindDistrictByID(id string, page, limit int) (*model.District, int, error)
FindAllVillages(page, limit int) ([]model.Village, int, error)
FindVillageByID(id string) (*model.Village, error)
}
type wilayahIndonesiaRepository struct {
DB *gorm.DB
}
func NewWilayahIndonesiaRepository(db *gorm.DB) WilayahIndonesiaRepository {
return &wilayahIndonesiaRepository{DB: db}
}
func (r *wilayahIndonesiaRepository) ImportProvinces(provinces []model.Province) error {
for _, province := range provinces {
if err := r.DB.Create(&province).Error; err != nil {
return err
}
}
return nil
}
func (r *wilayahIndonesiaRepository) ImportRegencies(regencies []model.Regency) error {
for _, regency := range regencies {
if err := r.DB.Create(&regency).Error; err != nil {
return err
}
}
return nil
}
func (r *wilayahIndonesiaRepository) ImportDistricts(districts []model.District) error {
for _, district := range districts {
if err := r.DB.Create(&district).Error; err != nil {
return err
}
}
return nil
}
func (r *wilayahIndonesiaRepository) ImportVillages(villages []model.Village) error {
for _, village := range villages {
if err := r.DB.Create(&village).Error; err != nil {
return err
}
}
return nil
}
func (r *wilayahIndonesiaRepository) FindAllProvinces(page, limit int) ([]model.Province, int, error) {
var provinces []model.Province
var total int64
err := r.DB.Model(&model.Province{}).Count(&total).Error
if err != nil {
return nil, 0, err
}
if page > 0 && limit > 0 {
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&provinces).Error
if err != nil {
return nil, 0, err
}
} else {
err := r.DB.Find(&provinces).Error
if err != nil {
return nil, 0, err
}
}
return provinces, int(total), nil
}
func (r *wilayahIndonesiaRepository) FindProvinceByID(id string, page, limit int) (*model.Province, int, error) {
var province model.Province
err := r.DB.Preload("Regencies", func(db *gorm.DB) *gorm.DB {
if page > 0 && limit > 0 {
return db.Offset((page - 1) * limit).Limit(limit)
}
return db
}).Where("id = ?", id).First(&province).Error
if err != nil {
return nil, 0, err
}
var totalRegencies int64
r.DB.Model(&model.Regency{}).Where("province_id = ?", id).Count(&totalRegencies)
return &province, int(totalRegencies), nil
}
func (r *wilayahIndonesiaRepository) FindAllRegencies(page, limit int) ([]model.Regency, int, error) {
var regencies []model.Regency
var total int64
err := r.DB.Model(&model.Regency{}).Count(&total).Error
if err != nil {
return nil, 0, err
}
if page > 0 && limit > 0 {
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&regencies).Error
if err != nil {
return nil, 0, err
}
} else {
err := r.DB.Find(&regencies).Error
if err != nil {
return nil, 0, err
}
}
return regencies, int(total), nil
}
func (r *wilayahIndonesiaRepository) FindRegencyByID(id string, page, limit int) (*model.Regency, int, error) {
var regency model.Regency
err := r.DB.Preload("Districts", func(db *gorm.DB) *gorm.DB {
if page > 0 && limit > 0 {
return db.Offset((page - 1) * limit).Limit(limit)
}
return db
}).Where("id = ?", id).First(&regency).Error
if err != nil {
return nil, 0, err
}
var totalDistricts int64
err = r.DB.Model(&model.District{}).Where("regency_id = ?", id).Count(&totalDistricts).Error
if err != nil {
return nil, 0, err
}
return &regency, int(totalDistricts), nil
}
func (r *wilayahIndonesiaRepository) FindAllDistricts(page, limit int) ([]model.District, int, error) {
var district []model.District
var total int64
err := r.DB.Model(&model.District{}).Count(&total).Error
if err != nil {
return nil, 0, err
}
if page > 0 && limit > 0 {
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&district).Error
if err != nil {
return nil, 0, err
}
} else {
err := r.DB.Find(&district).Error
if err != nil {
return nil, 0, err
}
}
return district, int(total), nil
}
func (r *wilayahIndonesiaRepository) FindDistrictByID(id string, page, limit int) (*model.District, int, error) {
var district model.District
err := r.DB.Preload("Villages", func(db *gorm.DB) *gorm.DB {
if page > 0 && limit > 0 {
return db.Offset((page - 1) * limit).Limit(limit)
}
return db
}).Where("id = ?", id).First(&district).Error
if err != nil {
return nil, 0, err
}
var totalVillage int64
r.DB.Model(&model.Village{}).Where("district_id = ?", id).Count(&totalVillage)
return &district, int(totalVillage), nil
}
func (r *wilayahIndonesiaRepository) FindAllVillages(page, limit int) ([]model.Village, int, error) {
var villages []model.Village
var total int64
err := r.DB.Model(&model.Village{}).Count(&total).Error
if err != nil {
return nil, 0, err
}
if page > 0 && limit > 0 {
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&villages).Error
if err != nil {
return nil, 0, err
}
} else {
err := r.DB.Find(&villages).Error
if err != nil {
return nil, 0, err
}
}
return villages, int(total), nil
}
func (r *wilayahIndonesiaRepository) FindVillageByID(id string) (*model.Village, error) {
var village model.Village
err := r.DB.Where("id = ?", id).First(&village).Error
if err != nil {
return nil, err
}
return &village, nil
}

View File

@ -1,436 +0,0 @@
package services
import (
"fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type AboutService interface {
CreateAbout(request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error)
UpdateAbout(id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error)
GetAllAbout() ([]dto.ResponseAboutDTO, error)
GetAboutByID(id string) (*dto.ResponseAboutDTO, error)
GetAboutDetailById(id string) (*dto.ResponseAboutDetailDTO, error)
DeleteAbout(id string) error
CreateAboutDetail(request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error)
UpdateAboutDetail(id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error)
DeleteAboutDetail(id string) error
}
type aboutService struct {
aboutRepo repositories.AboutRepository
}
func NewAboutService(aboutRepo repositories.AboutRepository) AboutService {
return &aboutService{aboutRepo: aboutRepo}
}
func formatResponseAboutDetailDTO(about *model.AboutDetail) (*dto.ResponseAboutDetailDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt)
response := &dto.ResponseAboutDetailDTO{
ID: about.ID,
AboutID: about.AboutID,
ImageDetail: about.ImageDetail,
Description: about.Description,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return response, nil
}
func formatResponseAboutDTO(about *model.About) (*dto.ResponseAboutDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt)
response := &dto.ResponseAboutDTO{
ID: about.ID,
Title: about.Title,
CoverImage: about.CoverImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return response, nil
}
func (s *aboutService) saveCoverImageAbout(coverImageAbout *multipart.FileHeader) (string, error) {
pathImage := "/uploads/coverabout/"
coverImageAboutDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(coverImageAboutDir); os.IsNotExist(err) {
if err := os.MkdirAll(coverImageAboutDir, os.ModePerm); err != nil {
return "", fmt.Errorf("gagal membuat direktori untuk cover image about: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := filepath.Ext(coverImageAbout.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
coverImageFileName := fmt.Sprintf("%s_coverabout%s", uuid.New().String(), extension)
coverImagePath := filepath.Join(coverImageAboutDir, coverImageFileName)
src, err := coverImageAbout.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(coverImagePath)
if err != nil {
return "", fmt.Errorf("failed to create cover image about file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save cover image about: %v", err)
}
coverImageAboutUrl := fmt.Sprintf("%s%s", pathImage, coverImageFileName)
return coverImageAboutUrl, nil
}
func (s *aboutService) saveCoverImageAboutDetail(coverImageAbout *multipart.FileHeader) (string, error) {
pathImage := "/uploads/coverabout/coveraboutdetail/"
coverImageAboutDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(coverImageAboutDir); os.IsNotExist(err) {
if err := os.MkdirAll(coverImageAboutDir, os.ModePerm); err != nil {
return "", fmt.Errorf("gagal membuat direktori untuk cover image about: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := filepath.Ext(coverImageAbout.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
coverImageFileName := fmt.Sprintf("%s_coveraboutdetail_%s", uuid.New().String(), extension)
coverImagePath := filepath.Join(coverImageAboutDir, coverImageFileName)
src, err := coverImageAbout.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(coverImagePath)
if err != nil {
return "", fmt.Errorf("failed to create cover image about file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save cover image about: %v", err)
}
coverImageAboutUrl := fmt.Sprintf("%s%s", pathImage, coverImageFileName)
return coverImageAboutUrl, nil
}
func deleteCoverImageAbout(coverimageAboutPath string) error {
if coverimageAboutPath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + coverimageAboutPath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Image deleted successfully: %s", absolutePath)
return nil
}
func (s *aboutService) CreateAbout(request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) {
errors, valid := request.ValidateAbout()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
coverImageAboutPath, err := s.saveCoverImageAbout(coverImageAbout)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan cover image about: %v ", err)
}
about := model.About{
Title: request.Title,
CoverImage: coverImageAboutPath,
}
if err := s.aboutRepo.CreateAbout(&about); err != nil {
return nil, fmt.Errorf("failed to create About: %v", err)
}
response, err := formatResponseAboutDTO(&about)
if err != nil {
return nil, fmt.Errorf("error formatting About response: %v", err)
}
return response, nil
}
func (s *aboutService) UpdateAbout(id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) {
errors, valid := request.ValidateAbout()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
about, err := s.aboutRepo.GetAboutByID(id)
if err != nil {
return nil, fmt.Errorf("about not found: %v", err)
}
if about.CoverImage != "" {
err := deleteCoverImageAbout(about.CoverImage)
if err != nil {
return nil, fmt.Errorf("gagal mengahpus gambar lama: %v", err)
}
}
var coverImageAboutPath string
if coverImageAbout != nil {
coverImageAboutPath, err = s.saveCoverImageAbout(coverImageAbout)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan gambar baru: %v", err)
}
}
about.Title = request.Title
if coverImageAboutPath != "" {
about.CoverImage = coverImageAboutPath
}
updatedAbout, err := s.aboutRepo.UpdateAbout(id, about)
if err != nil {
return nil, fmt.Errorf("failed to update About: %v", err)
}
response, err := formatResponseAboutDTO(updatedAbout)
if err != nil {
return nil, fmt.Errorf("error formatting About response: %v", err)
}
return response, nil
}
func (s *aboutService) GetAllAbout() ([]dto.ResponseAboutDTO, error) {
aboutList, err := s.aboutRepo.GetAllAbout()
if err != nil {
return nil, fmt.Errorf("failed to get About list: %v", err)
}
var aboutDTOList []dto.ResponseAboutDTO
for _, about := range aboutList {
response, err := formatResponseAboutDTO(&about)
if err != nil {
log.Printf("Error formatting About response: %v", err)
continue
}
aboutDTOList = append(aboutDTOList, *response)
}
return aboutDTOList, nil
}
func (s *aboutService) GetAboutByID(id string) (*dto.ResponseAboutDTO, error) {
about, err := s.aboutRepo.GetAboutByID(id)
if err != nil {
return nil, fmt.Errorf("about not found: %v", err)
}
response, err := formatResponseAboutDTO(about)
if err != nil {
return nil, fmt.Errorf("error formatting About response: %v", err)
}
var responseDetails []dto.ResponseAboutDetailDTO
for _, detail := range about.AboutDetail {
formattedDetail, err := formatResponseAboutDetailDTO(&detail)
if err != nil {
return nil, fmt.Errorf("error formatting AboutDetail response: %v", err)
}
responseDetails = append(responseDetails, *formattedDetail)
}
response.AboutDetail = &responseDetails
return response, nil
}
func (s *aboutService) GetAboutDetailById(id string) (*dto.ResponseAboutDetailDTO, error) {
about, err := s.aboutRepo.GetAboutDetailByID(id)
if err != nil {
return nil, fmt.Errorf("about not found: %v", err)
}
response, err := formatResponseAboutDetailDTO(about)
if err != nil {
return nil, fmt.Errorf("error formatting About response: %v", err)
}
return response, nil
}
func (s *aboutService) DeleteAbout(id string) error {
about, err := s.aboutRepo.GetAboutByID(id)
if err != nil {
return fmt.Errorf("about not found: %v", err)
}
if about.CoverImage != "" {
err := deleteCoverImageAbout(about.CoverImage)
if err != nil {
return fmt.Errorf("gagal mengahpus gambar lama: %v", err)
}
}
if err := s.aboutRepo.DeleteAbout(id); err != nil {
return fmt.Errorf("failed to delete About: %v", err)
}
return nil
}
func (s *aboutService) CreateAboutDetail(request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) {
errors, valid := request.ValidateAboutDetail()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
_, err := s.aboutRepo.GetAboutByIDWithoutPrel(request.AboutId)
if err != nil {
return nil, fmt.Errorf("about_id tidak ditemukan: %v", err)
}
coverImageAboutDetailPath, err := s.saveCoverImageAboutDetail(coverImageAboutDetail)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan cover image about detail: %v ", err)
}
aboutDetail := model.AboutDetail{
AboutID: request.AboutId,
ImageDetail: coverImageAboutDetailPath,
Description: request.Description,
}
if err := s.aboutRepo.CreateAboutDetail(&aboutDetail); err != nil {
return nil, fmt.Errorf("failed to create AboutDetail: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.UpdatedAt)
response := &dto.ResponseAboutDetailDTO{
ID: aboutDetail.ID,
AboutID: aboutDetail.AboutID,
ImageDetail: aboutDetail.ImageDetail,
Description: aboutDetail.Description,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return response, nil
}
func (s *aboutService) UpdateAboutDetail(id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) {
errors, valid := request.ValidateAboutDetail()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
aboutDetail, err := s.aboutRepo.GetAboutDetailByID(id)
if err != nil {
return nil, fmt.Errorf("about detail tidakck ditemukan: %v", err)
}
if aboutDetail.ImageDetail != "" {
err := deleteCoverImageAbout(aboutDetail.ImageDetail)
if err != nil {
return nil, fmt.Errorf("gagal menghapus gambar lama: %v", err)
}
}
var coverImageAboutDeatilPath string
if imageDetail != nil {
coverImageAboutDeatilPath, err = s.saveCoverImageAbout(imageDetail)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan gambar baru: %v", err)
}
}
aboutDetail.Description = request.Description
if coverImageAboutDeatilPath != "" {
aboutDetail.ImageDetail = coverImageAboutDeatilPath
}
aboutDetail, err = s.aboutRepo.UpdateAboutDetail(id, aboutDetail)
if err != nil {
log.Printf("Error updating about detail: %v", err)
return nil, fmt.Errorf("failed to update about detail: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.UpdatedAt)
response := &dto.ResponseAboutDetailDTO{
ID: aboutDetail.ID,
AboutID: aboutDetail.AboutID,
ImageDetail: aboutDetail.ImageDetail,
Description: aboutDetail.Description,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return response, nil
}
func (s *aboutService) DeleteAboutDetail(id string) error {
aboutDetail, err := s.aboutRepo.GetAboutDetailByID(id)
if err != nil {
return fmt.Errorf("about detail tidakck ditemukan: %v", err)
}
if aboutDetail.ImageDetail != "" {
err := deleteCoverImageAbout(aboutDetail.ImageDetail)
if err != nil {
return fmt.Errorf("gagal menghapus gambar lama: %v", err)
}
}
if err := s.aboutRepo.DeleteAboutDetail(id); err != nil {
return fmt.Errorf("failed to delete AboutDetail: %v", err)
}
return nil
}

View File

@ -1,412 +0,0 @@
package services
import (
"fmt"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
)
type AddressService interface {
CreateAddress(userID string, request dto.CreateAddressDTO) (*dto.AddressResponseDTO, error)
GetAddressByUserID(userID string) ([]dto.AddressResponseDTO, error)
GetAddressByID(userID, id string) (*dto.AddressResponseDTO, error)
UpdateAddress(userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error)
DeleteAddress(userID, id string) error
}
type addressService struct {
AddressRepo repositories.AddressRepository
WilayahRepo repositories.WilayahIndonesiaRepository
}
func NewAddressService(addressRepo repositories.AddressRepository, wilayahRepo repositories.WilayahIndonesiaRepository) AddressService {
return &addressService{
AddressRepo: addressRepo,
WilayahRepo: wilayahRepo,
}
}
func (s *addressService) CreateAddress(userID string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) {
province, _, err := s.WilayahRepo.FindProvinceByID(addressDTO.Province, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid province_id")
}
regency, _, err := s.WilayahRepo.FindRegencyByID(addressDTO.Regency, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid regency_id")
}
district, _, err := s.WilayahRepo.FindDistrictByID(addressDTO.District, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid district_id")
}
village, err := s.WilayahRepo.FindVillageByID(addressDTO.Village)
if err != nil {
return nil, fmt.Errorf("invalid village_id")
}
address := model.Address{
UserID: userID,
Province: province.Name,
Regency: regency.Name,
District: district.Name,
Village: village.Name,
PostalCode: addressDTO.PostalCode,
Detail: addressDTO.Detail,
Latitude: addressDTO.Latitude,
Longitude: addressDTO.Longitude,
}
err = s.AddressRepo.CreateAddress(&address)
if err != nil {
return nil, fmt.Errorf("failed to create address: %v", err)
}
userCacheKey := fmt.Sprintf("user:%s:addresses", userID)
utils.DeleteData(userCacheKey)
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
addressResponseDTO := &dto.AddressResponseDTO{
UserID: address.UserID,
ID: address.ID,
Province: address.Province,
Regency: address.Regency,
District: address.District,
Village: address.Village,
PostalCode: address.PostalCode,
Detail: address.Detail,
Latitude: address.Latitude,
Longitude: address.Longitude,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("address:%s", address.ID)
cacheData := map[string]interface{}{
"data": addressResponseDTO,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching new address to Redis: %v\n", err)
}
addresses, err := s.AddressRepo.FindAddressByUserID(userID)
if err != nil {
return nil, fmt.Errorf("failed to fetch updated addresses for user: %v", err)
}
var addressDTOs []dto.AddressResponseDTO
for _, addr := range addresses {
createdAt, _ := utils.FormatDateToIndonesianFormat(addr.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(addr.UpdatedAt)
addressDTOs = append(addressDTOs, dto.AddressResponseDTO{
UserID: addr.UserID,
ID: addr.ID,
Province: addr.Province,
Regency: addr.Regency,
District: addr.District,
Village: addr.Village,
PostalCode: addr.PostalCode,
Detail: addr.Detail,
Latitude: addr.Latitude,
Longitude: addr.Longitude,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData = map[string]interface{}{
"data": addressDTOs,
}
err = utils.SetJSONData(userCacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching updated user addresses to Redis: %v\n", err)
}
return addressResponseDTO, nil
}
func (s *addressService) GetAddressByUserID(userID string) ([]dto.AddressResponseDTO, error) {
cacheKey := fmt.Sprintf("user:%s:addresses", userID)
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
var addresses []dto.AddressResponseDTO
if data, ok := cachedData["data"].([]interface{}); ok {
for _, item := range data {
addressData, ok := item.(map[string]interface{})
if ok {
addresses = append(addresses, dto.AddressResponseDTO{
UserID: addressData["user_id"].(string),
ID: addressData["address_id"].(string),
Province: addressData["province"].(string),
Regency: addressData["regency"].(string),
District: addressData["district"].(string),
Village: addressData["village"].(string),
PostalCode: addressData["postalCode"].(string),
Detail: addressData["detail"].(string),
Latitude: addressData["latitude"].(float64),
Longitude: addressData["longitude"].(float64),
CreatedAt: addressData["createdAt"].(string),
UpdatedAt: addressData["updatedAt"].(string),
})
}
}
return addresses, nil
}
}
addresses, err := s.AddressRepo.FindAddressByUserID(userID)
if err != nil {
return nil, fmt.Errorf("failed to fetch addresses: %v", err)
}
var addressDTOs []dto.AddressResponseDTO
for _, address := range addresses {
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
addressDTOs = append(addressDTOs, dto.AddressResponseDTO{
UserID: address.UserID,
ID: address.ID,
Province: address.Province,
Regency: address.Regency,
District: address.District,
Village: address.Village,
PostalCode: address.PostalCode,
Detail: address.Detail,
Latitude: address.Latitude,
Longitude: address.Longitude,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": addressDTOs,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching addresses to Redis: %v\n", err)
}
return addressDTOs, nil
}
func (s *addressService) GetAddressByID(userID, id string) (*dto.AddressResponseDTO, error) {
address, err := s.AddressRepo.FindAddressByID(id)
if err != nil {
return nil, fmt.Errorf("address not found: %v", err)
}
if address.UserID != userID {
return nil, fmt.Errorf("you are not authorized to update this address")
}
cacheKey := fmt.Sprintf("address:%s", id)
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
addressData, ok := cachedData["data"].(map[string]interface{})
if ok {
address := dto.AddressResponseDTO{
UserID: addressData["user_id"].(string),
ID: addressData["address_id"].(string),
Province: addressData["province"].(string),
Regency: addressData["regency"].(string),
District: addressData["district"].(string),
Village: addressData["village"].(string),
PostalCode: addressData["postalCode"].(string),
Detail: addressData["detail"].(string),
Latitude: addressData["latitude"].(float64),
Longitude: addressData["longitude"].(float64),
CreatedAt: addressData["createdAt"].(string),
UpdatedAt: addressData["updatedAt"].(string),
}
return &address, nil
}
}
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
addressDTO := &dto.AddressResponseDTO{
UserID: address.UserID,
ID: address.ID,
Province: address.Province,
Regency: address.Regency,
District: address.District,
Village: address.Village,
PostalCode: address.PostalCode,
Detail: address.Detail,
Latitude: address.Latitude,
Longitude: address.Longitude,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": addressDTO,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching address to Redis: %v\n", err)
}
return addressDTO, nil
}
func (s *addressService) UpdateAddress(userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) {
address, err := s.AddressRepo.FindAddressByID(id)
if err != nil {
return nil, fmt.Errorf("address not found: %v", err)
}
if address.UserID != userID {
return nil, fmt.Errorf("you are not authorized to update this address")
}
province, _, err := s.WilayahRepo.FindProvinceByID(addressDTO.Province, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid province_id")
}
regency, _, err := s.WilayahRepo.FindRegencyByID(addressDTO.Regency, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid regency_id")
}
district, _, err := s.WilayahRepo.FindDistrictByID(addressDTO.District, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid district_id")
}
village, err := s.WilayahRepo.FindVillageByID(addressDTO.Village)
if err != nil {
return nil, fmt.Errorf("invalid village_id")
}
address.Province = province.Name
address.Regency = regency.Name
address.District = district.Name
address.Village = village.Name
address.PostalCode = addressDTO.PostalCode
address.Detail = addressDTO.Detail
address.Latitude = addressDTO.Latitude
address.Longitude = addressDTO.Longitude
// address.UpdatedAt = time.Now()
err = s.AddressRepo.UpdateAddress(address)
if err != nil {
return nil, fmt.Errorf("failed to update address: %v", err)
}
addressCacheKey := fmt.Sprintf("address:%s", id)
utils.DeleteData(addressCacheKey)
userAddressesCacheKey := fmt.Sprintf("user:%s:addresses", userID)
utils.DeleteData(userAddressesCacheKey)
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
addressResponseDTO := &dto.AddressResponseDTO{
UserID: address.UserID,
ID: address.ID,
Province: address.Province,
Regency: address.Regency,
District: address.District,
Village: address.Village,
PostalCode: address.PostalCode,
Detail: address.Detail,
Latitude: address.Latitude,
Longitude: address.Longitude,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": addressResponseDTO,
}
err = utils.SetJSONData(addressCacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching updated address to Redis: %v\n", err)
}
addresses, err := s.AddressRepo.FindAddressByUserID(address.UserID)
if err != nil {
return nil, fmt.Errorf("failed to fetch updated addresses for user: %v", err)
}
var addressDTOs []dto.AddressResponseDTO
for _, addr := range addresses {
createdAt, _ := utils.FormatDateToIndonesianFormat(addr.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(addr.UpdatedAt)
addressDTOs = append(addressDTOs, dto.AddressResponseDTO{
UserID: addr.UserID,
ID: addr.ID,
Province: addr.Province,
Regency: addr.Regency,
District: addr.District,
Village: addr.Village,
PostalCode: addr.PostalCode,
Detail: addr.Detail,
Latitude: addr.Latitude,
Longitude: addr.Longitude,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData = map[string]interface{}{
"data": addressDTOs,
}
err = utils.SetJSONData(userAddressesCacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching updated user addresses to Redis: %v\n", err)
}
return addressResponseDTO, nil
}
func (s *addressService) DeleteAddress(userID, addressID string) error {
address, err := s.AddressRepo.FindAddressByID(addressID)
if err != nil {
return fmt.Errorf("address not found: %v", err)
}
if address.UserID != userID {
return fmt.Errorf("you are not authorized to delete this address")
}
err = s.AddressRepo.DeleteAddress(addressID)
if err != nil {
return fmt.Errorf("failed to delete address: %v", err)
}
addressCacheKey := fmt.Sprintf("address:%s", addressID)
err = utils.DeleteData(addressCacheKey)
if err != nil {
fmt.Printf("Error deleting address cache: %v\n", err)
}
userAddressesCacheKey := fmt.Sprintf("user:%s:addresses", address.UserID)
err = utils.DeleteData(userAddressesCacheKey)
if err != nil {
fmt.Printf("Error deleting user addresses cache: %v\n", err)
}
return nil
}

View File

@ -1,415 +0,0 @@
package services
import (
"encoding/json"
"fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type ArticleService interface {
CreateArticle(request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error)
GetAllArticles(page, limit int) ([]dto.ArticleResponseDTO, int, error)
GetArticleByID(id string) (*dto.ArticleResponseDTO, error)
UpdateArticle(id string, request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error)
DeleteArticle(id string) error
}
type articleService struct {
ArticleRepo repositories.ArticleRepository
}
func NewArticleService(articleRepo repositories.ArticleRepository) ArticleService {
return &articleService{ArticleRepo: articleRepo}
}
func (s *articleService) saveCoverArticle(coverArticle *multipart.FileHeader) (string, error) {
pathImage := "/uploads/articles/"
coverArticleDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(coverArticleDir); os.IsNotExist(err) {
if err := os.MkdirAll(coverArticleDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for cover article: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := filepath.Ext(coverArticle.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
coverArticleFileName := fmt.Sprintf("%s_coverarticle%s", uuid.New().String(), extension)
coverArticlePath := filepath.Join(coverArticleDir, coverArticleFileName)
src, err := coverArticle.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(coverArticlePath)
if err != nil {
return "", fmt.Errorf("failed to create cover article file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save cover article: %v", err)
}
iconTrashUrl := fmt.Sprintf("%s%s", pathImage, coverArticleFileName)
return iconTrashUrl, nil
}
func deleteCoverArticle(imagePath string) error {
if imagePath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + imagePath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Image deleted successfully: %s", absolutePath)
return nil
}
func (s *articleService) CreateArticle(request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error) {
coverArticlePath, err := s.saveCoverArticle(coverImage)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan ikon sampah: %v", err)
}
article := model.Article{
Title: request.Title,
CoverImage: coverArticlePath,
Author: request.Author,
Heading: request.Heading,
Content: request.Content,
}
if err := s.ArticleRepo.CreateArticle(&article); err != nil {
return nil, fmt.Errorf("failed to create article: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
articleResponseDTO := &dto.ArticleResponseDTO{
ID: article.ID,
Title: article.Title,
CoverImage: article.CoverImage,
Author: article.Author,
Heading: article.Heading,
Content: article.Content,
PublishedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("article:%s", article.ID)
cacheData := map[string]interface{}{
"data": articleResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching article to Redis: %v\n", err)
}
articles, total, err := s.ArticleRepo.FindAllArticles(0, 0)
if err != nil {
fmt.Printf("Error fetching all articles: %v\n", err)
}
var articleDTOs []dto.ArticleResponseDTO
for _, a := range articles {
createdAt, _ := utils.FormatDateToIndonesianFormat(a.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(a.UpdatedAt)
articleDTOs = append(articleDTOs, dto.ArticleResponseDTO{
ID: a.ID,
Title: a.Title,
CoverImage: a.CoverImage,
Author: a.Author,
Heading: a.Heading,
Content: a.Content,
PublishedAt: createdAt,
UpdatedAt: updatedAt,
})
}
articlesCacheKey := "articles:all"
cacheData = map[string]interface{}{
"data": articleDTOs,
"total": total,
}
if err := utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching all articles to Redis: %v\n", err)
}
return articleResponseDTO, nil
}
func (s *articleService) GetAllArticles(page, limit int) ([]dto.ArticleResponseDTO, int, error) {
var cacheKey string
if page == 0 && limit == 0 {
cacheKey = "articles:all"
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
if data, ok := cachedData["data"].([]interface{}); ok {
var articles []dto.ArticleResponseDTO
for _, item := range data {
articleData, ok := item.(map[string]interface{})
if ok {
articles = append(articles, dto.ArticleResponseDTO{
ID: articleData["article_id"].(string),
Title: articleData["title"].(string),
CoverImage: articleData["coverImage"].(string),
Author: articleData["author"].(string),
Heading: articleData["heading"].(string),
Content: articleData["content"].(string),
PublishedAt: articleData["publishedAt"].(string),
UpdatedAt: articleData["updatedAt"].(string),
})
}
}
if total, ok := cachedData["total"].(float64); ok {
fmt.Printf("Cached Total Articles: %f\n", total)
return articles, int(total), nil
} else {
fmt.Println("Total articles not found in cache, using 0 as fallback.")
return articles, 0, nil
}
}
}
}
articles, total, err := s.ArticleRepo.FindAllArticles(page, limit)
if err != nil {
return nil, 0, fmt.Errorf("failed to fetch articles: %v", err)
}
fmt.Printf("Total Articles from Database: %d\n", total)
var articleDTOs []dto.ArticleResponseDTO
for _, article := range articles {
publishedAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
articleDTOs = append(articleDTOs, dto.ArticleResponseDTO{
ID: article.ID,
Title: article.Title,
CoverImage: article.CoverImage,
Author: article.Author,
Heading: article.Heading,
Content: article.Content,
PublishedAt: publishedAt,
UpdatedAt: updatedAt,
})
}
cacheKey = fmt.Sprintf("articles_page:%d_limit:%d", page, limit)
cacheData := map[string]interface{}{
"data": articleDTOs,
"total": total,
}
fmt.Printf("Setting cache with total: %d\n", total)
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching articles to Redis: %v\n", err)
}
return articleDTOs, total, nil
}
func (s *articleService) GetArticleByID(id string) (*dto.ArticleResponseDTO, error) {
cacheKey := fmt.Sprintf("article:%s", id)
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
articleResponse := &dto.ArticleResponseDTO{}
if data, ok := cachedData["data"].(string); ok {
if err := json.Unmarshal([]byte(data), articleResponse); err == nil {
return articleResponse, nil
}
}
}
article, err := s.ArticleRepo.FindArticleByID(id)
if err != nil {
return nil, fmt.Errorf("failed to fetch article by ID: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
articleResponseDTO := &dto.ArticleResponseDTO{
ID: article.ID,
Title: article.Title,
CoverImage: article.CoverImage,
Author: article.Author,
Heading: article.Heading,
Content: article.Content,
PublishedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": articleResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching article to Redis: %v\n", err)
}
return articleResponseDTO, nil
}
func (s *articleService) UpdateArticle(id string, request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error) {
article, err := s.ArticleRepo.FindArticleByID(id)
if err != nil {
return nil, fmt.Errorf("article not found: %v", id)
}
if article.CoverImage != "" {
err := deleteCoverArticle(article.CoverImage)
if err != nil {
return nil, fmt.Errorf("failed to delete old image: %v", err)
}
}
var coverArticlePath string
if coverImage != nil {
coverArticlePath, err = s.saveCoverArticle(coverImage)
if err != nil {
return nil, fmt.Errorf("failed to save card photo: %v", err)
}
}
if coverArticlePath != "" {
article.CoverImage = coverArticlePath
}
article.Title = request.Title
article.Heading = request.Heading
article.Content = request.Content
article.Author = request.Author
err = s.ArticleRepo.UpdateArticle(id, article)
if err != nil {
return nil, fmt.Errorf("failed to update article: %v", err)
}
updatedArticle, err := s.ArticleRepo.FindArticleByID(id)
if err != nil {
return nil, fmt.Errorf("failed to fetch updated article: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(updatedArticle.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(updatedArticle.UpdatedAt)
articleResponseDTO := &dto.ArticleResponseDTO{
ID: updatedArticle.ID,
Title: updatedArticle.Title,
CoverImage: updatedArticle.CoverImage,
Author: updatedArticle.Author,
Heading: updatedArticle.Heading,
Content: updatedArticle.Content,
PublishedAt: createdAt,
UpdatedAt: updatedAt,
}
articleCacheKey := fmt.Sprintf("article:%s", updatedArticle.ID)
err = utils.SetJSONData(articleCacheKey, map[string]interface{}{"data": articleResponseDTO}, time.Hour*24)
if err != nil {
fmt.Printf("Error caching updated article to Redis: %v\n", err)
}
articlesCacheKey := "articles:all"
err = utils.DeleteData(articlesCacheKey)
if err != nil {
fmt.Printf("Error deleting articles cache: %v\n", err)
}
articles, _, err := s.ArticleRepo.FindAllArticles(0, 0)
if err != nil {
fmt.Printf("Error fetching all articles: %v\n", err)
} else {
var articleDTOs []dto.ArticleResponseDTO
for _, a := range articles {
createdAt, _ := utils.FormatDateToIndonesianFormat(a.PublishedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(a.UpdatedAt)
articleDTOs = append(articleDTOs, dto.ArticleResponseDTO{
ID: a.ID,
Title: a.Title,
CoverImage: a.CoverImage,
Author: a.Author,
Heading: a.Heading,
Content: a.Content,
PublishedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": articleDTOs,
}
err = utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching updated articles to Redis: %v\n", err)
}
}
return articleResponseDTO, nil
}
func (s *articleService) DeleteArticle(id string) error {
article, err := s.ArticleRepo.FindArticleByID(id)
if err != nil {
return fmt.Errorf("failed to find article: %v", id)
}
if err := deleteCoverArticle(article.CoverImage); err != nil {
return fmt.Errorf("error waktu menghapus cover image article %s: %v", id, err)
}
err = s.ArticleRepo.DeleteArticle(id)
if err != nil {
return fmt.Errorf("failed to delete article: %v", err)
}
articleCacheKey := fmt.Sprintf("article:%s", id)
err = utils.DeleteData(articleCacheKey)
if err != nil {
fmt.Printf("Error deleting cache for article: %v\n", err)
}
articlesCacheKey := "articles:all"
err = utils.DeleteData(articlesCacheKey)
if err != nil {
fmt.Printf("Error deleting cache for all articles: %v\n", err)
}
return nil
}

View File

@ -1,192 +0,0 @@
package service
/*
import (
"errors"
"fmt"
"log"
"rijig/config"
dto "rijig/dto/auth"
"rijig/internal/repositories"
repository "rijig/internal/repositories/auth"
"rijig/model"
"rijig/utils"
"time"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
)
const (
ErrEmailTaken = "email is already used"
ErrPhoneTaken = "phone number is already used"
ErrInvalidPassword = "password does not match"
ErrRoleNotFound = "role not found"
ErrFailedToGenerateToken = "failed to generate token"
ErrFailedToHashPassword = "failed to hash password"
ErrFailedToCreateUser = "failed to create user"
ErrIncorrectPassword = "incorrect password"
ErrAccountNotFound = "account not found"
)
type AuthAdminService interface {
RegisterAdmin(request *dto.RegisterAdminRequest) (*model.User, error)
LoginAdmin(req *dto.LoginAdminRequest) (*dto.LoginResponse, error)
LogoutAdmin(userID, deviceID string) error
}
type authAdminService struct {
UserRepo repository.AuthAdminRepository
RoleRepo repositories.RoleRepository
SecretKey string
}
func NewAuthAdminService(userRepo repository.AuthAdminRepository, roleRepo repositories.RoleRepository, secretKey string) AuthAdminService {
return &authAdminService{UserRepo: userRepo, RoleRepo: roleRepo, SecretKey: secretKey}
}
func (s *authAdminService) RegisterAdmin(request *dto.RegisterAdminRequest) (*model.User, error) {
if existingUser, _ := s.UserRepo.FindByEmail(request.Email); existingUser != nil {
return nil, errors.New(ErrEmailTaken)
}
if existingUser, _ := s.UserRepo.FindByPhone(request.Phone); existingUser != nil {
return nil, errors.New(ErrPhoneTaken)
}
role, err := s.UserRepo.FindRoleByName("administrator")
if err != nil {
return nil, errors.New(ErrRoleNotFound)
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)
if err != nil {
log.Println("Error hashing password:", err)
return nil, errors.New(ErrFailedToHashPassword)
}
user := &model.User{
Name: request.Name,
Email: request.Email,
Phone: request.Phone,
Password: string(hashedPassword),
RoleID: role.ID,
Role: role,
Dateofbirth: request.Dateofbirth,
Placeofbirth: request.Placeofbirth,
Gender: request.Gender,
RegistrationStatus: "completed",
}
createdUser, err := s.UserRepo.CreateUser(user)
if err != nil {
log.Println("Error creating user:", err)
return nil, fmt.Errorf("%s: %v", ErrFailedToCreateUser, err)
}
return createdUser, nil
}
func (s *authAdminService) LoginAdmin(req *dto.LoginAdminRequest) (*dto.LoginResponse, error) {
user, err := s.UserRepo.FindByEmail(req.Email)
if err != nil {
log.Println("User not found:", err)
return nil, errors.New(ErrAccountNotFound)
}
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil {
log.Println("Incorrect password:", err)
return nil, errors.New(ErrIncorrectPassword)
}
existingUser, err := s.UserRepo.FindAdminByEmailandRoleid(req.Email, "42bdecce-f2ad-44ae-b3d6-883c1fbddaf7")
if err != nil {
return nil, fmt.Errorf("failed to check existing user: %w", err)
}
var adminUser *model.User
if existingUser != nil {
adminUser = existingUser
} else {
adminUser = &model.User{
Email: req.Email,
RoleID: "42bdecce-f2ad-44ae-b3d6-883c1fbddaf7",
}
createdUser, err := s.UserRepo.CreateUser(adminUser)
if err != nil {
return nil, err
}
adminUser = createdUser
}
token, err := s.generateJWTToken(adminUser.ID, req.Deviceid)
if err != nil {
return nil, err
}
role, err := s.RoleRepo.FindByID(user.RoleID)
if err != nil {
return nil, fmt.Errorf("failed to get role: %w", err)
}
deviceID := req.Deviceid
if err := s.saveSessionAdminData(user.ID, deviceID, user.RoleID, role.RoleName, token); err != nil {
return nil, err
}
return &dto.LoginResponse{
UserID: user.ID,
Role: user.Role.RoleName,
Token: token,
}, nil
}
func (s *authAdminService) saveSessionAdminData(userID string, deviceID string, roleID string, roleName string, token string) error {
sessionKey := fmt.Sprintf("session:%s:%s", userID, deviceID)
sessionData := map[string]interface{}{
"userID": userID,
"roleID": roleID,
"roleName": roleName,
}
if err := utils.SetJSONData(sessionKey, sessionData, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session data: %w", err)
}
if err := utils.SetStringData("session_token:"+userID+":"+deviceID, token, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session token: %w", err)
}
return nil
}
func (s *authAdminService) generateJWTToken(userID string, deviceID string) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour)
claims := jwt.MapClaims{
"sub": userID,
"exp": expirationTime.Unix(),
"device_id": deviceID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
secretKey := config.GetSecretKey()
return token.SignedString([]byte(secretKey))
}
func (s *authAdminService) LogoutAdmin(userID, deviceID string) error {
err := utils.DeleteSessionData(userID, deviceID)
if err != nil {
return fmt.Errorf("failed to delete session from Redis: %w", err)
}
return nil
}
*/

View File

@ -1,171 +0,0 @@
package service
/*
import (
"errors"
"fmt"
"rijig/config"
"rijig/dto"
"rijig/internal/repositories"
repository "rijig/internal/repositories/auth"
"rijig/model"
"rijig/utils"
"time"
"github.com/golang-jwt/jwt/v5"
)
type AuthMasyarakatService interface {
RegisterOrLogin(req *dto.RegisterRequest) error
VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error)
Logout(userID, deviceID string) error
}
type authMasyarakatService struct {
userRepo repository.AuthPengelolaRepository
roleRepo repositories.RoleRepository
}
func NewAuthMasyarakatService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository) AuthMasyarakatService {
return &authMasyarakatService{userRepo, roleRepo}
}
func (s *authMasyarakatService) generateJWTToken(userID string, deviceID string) (string, error) {
expirationTime := time.Now().Add(672 * time.Hour)
claims := jwt.MapClaims{
"sub": userID,
"exp": expirationTime.Unix(),
"device_id": deviceID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
secretKey := config.GetSecretKey()
return token.SignedString([]byte(secretKey))
}
func (s *authMasyarakatService) RegisterOrLogin(req *dto.RegisterRequest) error {
if err := s.checkOTPRequestCooldown(req.Phone); err != nil {
return err
}
return s.sendOTP(req.Phone)
}
func (s *authMasyarakatService) checkOTPRequestCooldown(phone string) error {
otpSentTime, err := utils.GetStringData("otp_sent:" + phone)
if err != nil || otpSentTime == "" {
return nil
}
lastSent, _ := time.Parse(time.RFC3339, otpSentTime)
if time.Since(lastSent) < otpCooldown {
return errors.New("please wait before requesting a new OTP")
}
return nil
}
func (s *authMasyarakatService) sendOTP(phone string) error {
otp := generateOTP()
if err := config.SendWhatsAppMessage(phone, fmt.Sprintf("Your OTP is: %s", otp)); err != nil {
return err
}
if err := utils.SetStringData("otp:"+phone, otp, 10*time.Minute); err != nil {
return err
}
return utils.SetStringData("otp_sent:"+phone, time.Now().Format(time.RFC3339), 10*time.Minute)
}
func (s *authMasyarakatService) VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error) {
storedOTP, err := utils.GetStringData("otp:" + req.Phone)
if err != nil || storedOTP == "" {
return nil, errors.New("OTP expired or not found")
}
if storedOTP != req.OTP {
return nil, errors.New("invalid OTP")
}
if err := utils.DeleteData("otp:" + req.Phone); err != nil {
return nil, fmt.Errorf("failed to remove OTP from Redis: %w", err)
}
if err := utils.DeleteData("otp_sent:" + req.Phone); err != nil {
return nil, fmt.Errorf("failed to remove otp_sent from Redis: %w", err)
}
existingUser, err := s.userRepo.GetUserByPhoneAndRole(req.Phone, "0e5684e4-b214-4bd0-972f-3be80c4649a0")
if err != nil {
return nil, fmt.Errorf("failed to check existing user: %w", err)
}
var user *model.User
if existingUser != nil {
user = existingUser
} else {
user = &model.User{
Phone: req.Phone,
RoleID: "0e5684e4-b214-4bd0-972f-3be80c4649a0",
PhoneVerified: true,
RegistrationStatus: "completed",
}
createdUser, err := s.userRepo.CreateUser(user)
if err != nil {
return nil, err
}
user = createdUser
}
token, err := s.generateJWTToken(user.ID, req.DeviceID)
if err != nil {
return nil, err
}
role, err := s.roleRepo.FindByID(user.RoleID)
if err != nil {
return nil, fmt.Errorf("failed to get role: %w", err)
}
deviceID := req.DeviceID
if err := s.saveSessionData(user.ID, deviceID, user.RoleID, role.RoleName, token); err != nil {
return nil, err
}
return &dto.UserDataResponse{
UserID: user.ID,
UserRole: role.RoleName,
Token: token,
}, nil
}
func (s *authMasyarakatService) saveSessionData(userID string, deviceID string, roleID string, roleName string, token string) error {
sessionKey := fmt.Sprintf("session:%s:%s", userID, deviceID)
sessionData := map[string]interface{}{
"userID": userID,
"roleID": roleID,
"roleName": roleName,
}
if err := utils.SetJSONData(sessionKey, sessionData, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session data: %w", err)
}
if err := utils.SetStringData("session_token:"+userID+":"+deviceID, token, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session token: %w", err)
}
return nil
}
func (s *authMasyarakatService) Logout(userID, deviceID string) error {
err := utils.DeleteSessionData(userID, deviceID)
if err != nil {
return fmt.Errorf("failed to delete session from Redis: %w", err)
}
return nil
}
*/

View File

@ -1,171 +0,0 @@
package service
/*
import (
"errors"
"fmt"
"rijig/config"
"rijig/dto"
"rijig/internal/repositories"
repository "rijig/internal/repositories/auth"
"rijig/model"
"rijig/utils"
"time"
"github.com/golang-jwt/jwt/v5"
)
type AuthPengelolaService interface {
RegisterOrLogin(req *dto.RegisterRequest) error
VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error)
Logout(userID, deviceID string) error
}
type authPengelolaService struct {
userRepo repository.AuthPengelolaRepository
roleRepo repositories.RoleRepository
}
func NewAuthPengelolaService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository) AuthPengelolaService {
return &authPengelolaService{userRepo, roleRepo}
}
func (s *authPengelolaService) generateJWTToken(userID string, deviceID string) (string, error) {
expirationTime := time.Now().Add(168 * time.Hour)
claims := jwt.MapClaims{
"sub": userID,
"exp": expirationTime.Unix(),
"device_id": deviceID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
secretKey := config.GetSecretKey()
return token.SignedString([]byte(secretKey))
}
func (s *authPengelolaService) RegisterOrLogin(req *dto.RegisterRequest) error {
if err := s.checkOTPRequestCooldown(req.Phone); err != nil {
return err
}
return s.sendOTP(req.Phone)
}
func (s *authPengelolaService) checkOTPRequestCooldown(phone string) error {
otpSentTime, err := utils.GetStringData("otp_sent:" + phone)
if err != nil || otpSentTime == "" {
return nil
}
lastSent, _ := time.Parse(time.RFC3339, otpSentTime)
if time.Since(lastSent) < otpCooldown {
return errors.New("please wait before requesting a new OTP")
}
return nil
}
func (s *authPengelolaService) sendOTP(phone string) error {
otp := generateOTP()
if err := config.SendWhatsAppMessage(phone, fmt.Sprintf("Your OTP is: %s", otp)); err != nil {
return err
}
if err := utils.SetStringData("otp:"+phone, otp, 10*time.Minute); err != nil {
return err
}
return utils.SetStringData("otp_sent:"+phone, time.Now().Format(time.RFC3339), 10*time.Minute)
}
func (s *authPengelolaService) VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error) {
storedOTP, err := utils.GetStringData("otp:" + req.Phone)
if err != nil || storedOTP == "" {
return nil, errors.New("OTP expired or not found")
}
if storedOTP != req.OTP {
return nil, errors.New("invalid OTP")
}
if err := utils.DeleteData("otp:" + req.Phone); err != nil {
return nil, fmt.Errorf("failed to remove OTP from Redis: %w", err)
}
if err := utils.DeleteData("otp_sent:" + req.Phone); err != nil {
return nil, fmt.Errorf("failed to remove otp_sent from Redis: %w", err)
}
existingUser, err := s.userRepo.GetUserByPhoneAndRole(req.Phone, "0bf86966-7042-410a-a88c-d01f70832348")
if err != nil {
return nil, fmt.Errorf("failed to check existing user: %w", err)
}
var user *model.User
if existingUser != nil {
user = existingUser
} else {
user = &model.User{
Phone: req.Phone,
RoleID: "0bf86966-7042-410a-a88c-d01f70832348",
PhoneVerified: true,
RegistrationStatus: "uncompleted",
}
createdUser, err := s.userRepo.CreateUser(user)
if err != nil {
return nil, err
}
user = createdUser
}
token, err := s.generateJWTToken(user.ID, req.DeviceID)
if err != nil {
return nil, err
}
role, err := s.roleRepo.FindByID(user.RoleID)
if err != nil {
return nil, fmt.Errorf("failed to get role: %w", err)
}
deviceID := req.DeviceID
if err := s.saveSessionData(user.ID, deviceID, user.RoleID, role.RoleName, token); err != nil {
return nil, err
}
return &dto.UserDataResponse{
UserID: user.ID,
UserRole: role.RoleName,
Token: token,
}, nil
}
func (s *authPengelolaService) saveSessionData(userID string, deviceID string, roleID string, roleName string, token string) error {
sessionKey := fmt.Sprintf("session:%s:%s", userID, deviceID)
sessionData := map[string]interface{}{
"userID": userID,
"roleID": roleID,
"roleName": roleName,
}
if err := utils.SetJSONData(sessionKey, sessionData, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session data: %w", err)
}
if err := utils.SetStringData("session_token:"+userID+":"+deviceID, token, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session token: %w", err)
}
return nil
}
func (s *authPengelolaService) Logout(userID, deviceID string) error {
err := utils.DeleteSessionData(userID, deviceID)
if err != nil {
return fmt.Errorf("failed to delete session from Redis: %w", err)
}
return nil
}
*/

View File

@ -1,172 +0,0 @@
package service
/*
import (
"errors"
"fmt"
"rijig/config"
"rijig/dto"
"rijig/internal/repositories"
repository "rijig/internal/repositories/auth"
"rijig/model"
"rijig/utils"
"time"
"github.com/golang-jwt/jwt/v5"
)
type AuthPengepulService interface {
RegisterOrLogin(req *dto.RegisterRequest) error
VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error)
Logout(userID, deviceID string) error
}
type authPengepulService struct {
userRepo repository.AuthPengelolaRepository
roleRepo repositories.RoleRepository
}
func NewAuthPengepulService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository) AuthPengepulService {
return &authPengepulService{userRepo, roleRepo}
}
func (s *authPengepulService) generateJWTToken(userID string, deviceID string) (string, error) {
expirationTime := time.Now().Add(480 * time.Hour)
claims := jwt.MapClaims{
"sub": userID,
"exp": expirationTime.Unix(),
"device_id": deviceID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
secretKey := config.GetSecretKey()
return token.SignedString([]byte(secretKey))
}
func (s *authPengepulService) RegisterOrLogin(req *dto.RegisterRequest) error {
if err := s.checkOTPRequestCooldown(req.Phone); err != nil {
return err
}
return s.sendOTP(req.Phone)
}
func (s *authPengepulService) checkOTPRequestCooldown(phone string) error {
otpSentTime, err := utils.GetStringData("otp_sent:" + phone)
if err != nil || otpSentTime == "" {
return nil
}
lastSent, _ := time.Parse(time.RFC3339, otpSentTime)
if time.Since(lastSent) < otpCooldown {
return errors.New("please wait before requesting a new OTP")
}
return nil
}
func (s *authPengepulService) sendOTP(phone string) error {
otp := generateOTP()
fmt.Printf("ur otp is:%s", otp)
// if err := config.SendWhatsAppMessage(phone, fmt.Sprintf("Your OTP is: %s", otp)); err != nil {
// return err
// }
if err := utils.SetStringData("otp:"+phone, otp, 10*time.Minute); err != nil {
return err
}
return utils.SetStringData("otp_sent:"+phone, time.Now().Format(time.RFC3339), 10*time.Minute)
}
func (s *authPengepulService) VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error) {
storedOTP, err := utils.GetStringData("otp:" + req.Phone)
if err != nil || storedOTP == "" {
return nil, errors.New("OTP expired or not found")
}
if storedOTP != req.OTP {
return nil, errors.New("invalid OTP")
}
if err := utils.DeleteData("otp:" + req.Phone); err != nil {
return nil, fmt.Errorf("failed to remove OTP from Redis: %w", err)
}
if err := utils.DeleteData("otp_sent:" + req.Phone); err != nil {
return nil, fmt.Errorf("failed to remove otp_sent from Redis: %w", err)
}
existingUser, err := s.userRepo.GetUserByPhoneAndRole(req.Phone, "d7245535-0e9e-4d35-ab39-baece5c10b3c")
if err != nil {
return nil, fmt.Errorf("failed to check existing user: %w", err)
}
var user *model.User
if existingUser != nil {
user = existingUser
} else {
user = &model.User{
Phone: req.Phone,
RoleID: "d7245535-0e9e-4d35-ab39-baece5c10b3c",
PhoneVerified: true,
RegistrationStatus: "uncompleted",
}
createdUser, err := s.userRepo.CreateUser(user)
if err != nil {
return nil, err
}
user = createdUser
}
token, err := s.generateJWTToken(user.ID, req.DeviceID)
if err != nil {
return nil, err
}
role, err := s.roleRepo.FindByID(user.RoleID)
if err != nil {
return nil, fmt.Errorf("failed to get role: %w", err)
}
deviceID := req.DeviceID
if err := s.saveSessionData(user.ID, deviceID, user.RoleID, role.RoleName, token); err != nil {
return nil, err
}
return &dto.UserDataResponse{
UserID: user.ID,
UserRole: role.RoleName,
Token: token,
}, nil
}
func (s *authPengepulService) saveSessionData(userID string, deviceID string, roleID string, roleName string, token string) error {
sessionKey := fmt.Sprintf("session:%s:%s", userID, deviceID)
sessionData := map[string]interface{}{
"userID": userID,
"roleID": roleID,
"roleName": roleName,
}
if err := utils.SetJSONData(sessionKey, sessionData, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session data: %w", err)
}
if err := utils.SetStringData("session_token:"+userID+":"+deviceID, token, 24*time.Hour); err != nil {
return fmt.Errorf("failed to set session token: %w", err)
}
return nil
}
func (s *authPengepulService) Logout(userID, deviceID string) error {
err := utils.DeleteSessionData(userID, deviceID)
if err != nil {
return fmt.Errorf("failed to delete session from Redis: %w", err)
}
return nil
}
*/

View File

@ -1,14 +0,0 @@
package service
import (
"fmt"
"math/rand"
"time"
)
func generateOTP() string {
randGenerator := rand.New(rand.NewSource(time.Now().UnixNano()))
return fmt.Sprintf("%04d", randGenerator.Intn(10000))
}
const otpCooldown = 50 * time.Second

View File

@ -1,213 +0,0 @@
package services
// import (
// "errors"
// "fmt"
// "math/rand"
// "rijig/config"
// "rijig/dto"
// "rijig/internal/repositories"
// "rijig/model"
// "rijig/utils"
// "time"
// "github.com/golang-jwt/jwt/v5"
// )
// const otpCooldown = 30 * time.Second
// type AuthService interface {
// RegisterOrLogin(req *dto.RegisterRequest) error
// VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error)
// Logout(userID, phone string) error
// }
// type authService struct {
// userRepo repositories.UserRepository
// roleRepo repositories.RoleRepository
// }
// func NewAuthService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository) AuthService {
// return &authService{userRepo, roleRepo}
// }
// func (s *authService) RegisterOrLogin(req *dto.RegisterRequest) error {
// if err := s.checkOTPRequestCooldown(req.Phone); err != nil {
// return err
// }
// user, err := s.userRepo.GetUserByPhoneAndRole(req.Phone, req.RoleID)
// if err != nil {
// return fmt.Errorf("failed to check existing user: %w", err)
// }
// if user != nil {
// return s.sendOTP(req.Phone)
// }
// user = &model.User{
// Phone: req.Phone,
// RoleID: req.RoleID,
// }
// createdUser, err := s.userRepo.CreateUser(user)
// if err != nil {
// return fmt.Errorf("failed to create new user: %w", err)
// }
// if err := s.saveUserToRedis(createdUser.ID, createdUser, req.Phone); err != nil {
// return err
// }
// return s.sendOTP(req.Phone)
// }
// func (s *authService) checkOTPRequestCooldown(phone string) error {
// otpSentTime, err := utils.GetStringData("otp_sent:" + phone)
// if err != nil || otpSentTime == "" {
// return nil
// }
// lastSent, _ := time.Parse(time.RFC3339, otpSentTime)
// if time.Since(lastSent) < otpCooldown {
// return errors.New("please wait before requesting a new OTP")
// }
// return nil
// }
// func (s *authService) sendOTP(phone string) error {
// otp := generateOTP()
// if err := config.SendWhatsAppMessage(phone, fmt.Sprintf("Your OTP is: %s", otp)); err != nil {
// return err
// }
// if err := utils.SetStringData("otp:"+phone, otp, 10*time.Minute); err != nil {
// return err
// }
// return utils.SetStringData("otp_sent:"+phone, time.Now().Format(time.RFC3339), 10*time.Minute)
// }
// func (s *authService) VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error) {
// storedOTP, err := utils.GetStringData("otp:" + req.Phone)
// if err != nil || storedOTP == "" {
// return nil, errors.New("OTP expired or not found")
// }
// if storedOTP != req.OTP {
// return nil, errors.New("invalid OTP")
// }
// if err := utils.DeleteData("otp:" + req.Phone); err != nil {
// return nil, fmt.Errorf("failed to remove OTP from Redis: %w", err)
// }
// existingUser, err := s.userRepo.GetUserByPhoneAndRole(req.Phone, req.RoleID)
// if err != nil {
// return nil, fmt.Errorf("failed to check existing user: %w", err)
// }
// var user *model.User
// if existingUser != nil {
// user = existingUser
// } else {
// user = &model.User{
// Phone: req.Phone,
// RoleID: req.RoleID,
// }
// createdUser, err := s.userRepo.CreateUser(user)
// if err != nil {
// return nil, err
// }
// user = createdUser
// }
// token, err := s.generateJWTToken(user.ID)
// if err != nil {
// return nil, err
// }
// role, err := s.roleRepo.FindByID(user.RoleID)
// if err != nil {
// return nil, fmt.Errorf("failed to get role: %w", err)
// }
// if err := s.saveSessionData(user.ID, user.RoleID, role.RoleName, token); err != nil {
// return nil, err
// }
// return &dto.UserDataResponse{
// UserID: user.ID,
// UserRole: role.RoleName,
// Token: token,
// }, nil
// }
// func (s *authService) saveUserToRedis(userID string, user *model.User, phone string) error {
// if err := utils.SetJSONData("user:"+userID, user, 10*time.Minute); err != nil {
// return fmt.Errorf("failed to store user data in Redis: %w", err)
// }
// if err := utils.SetStringData("user_phone:"+userID, phone, 10*time.Minute); err != nil {
// return fmt.Errorf("failed to store user phone in Redis: %w", err)
// }
// return nil
// }
// func (s *authService) generateJWTToken(userID string) (string, error) {
// expirationTime := time.Now().Add(24 * time.Hour)
// claims := &jwt.RegisteredClaims{
// Subject: userID,
// ExpiresAt: jwt.NewNumericDate(expirationTime),
// }
// token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// secretKey := config.GetSecretKey()
// return token.SignedString([]byte(secretKey))
// }
// func (s *authService) saveSessionData(userID string, roleID string, roleName string, token string) error {
// sessionKey := fmt.Sprintf("session:%s", userID)
// sessionData := map[string]interface{}{
// "userID": userID,
// "roleID": roleID,
// "roleName": roleName,
// }
// if err := utils.SetJSONData(sessionKey, sessionData, 24*time.Hour); err != nil {
// return fmt.Errorf("failed to set session data: %w", err)
// }
// if err := utils.SetStringData("session_token:"+userID, token, 24*time.Hour); err != nil {
// return fmt.Errorf("failed to set session token: %w", err)
// }
// return nil
// }
// func (s *authService) Logout(userID, phone string) error {
// keys := []string{
// "session:" + userID,
// "session_token:" + userID,
// "user_logged_in:" + userID,
// "user:" + userID,
// "user_phone:" + userID,
// "otp_sent:" + phone,
// }
// for _, key := range keys {
// if err := utils.DeleteData(key); err != nil {
// return fmt.Errorf("failed to delete key %s from Redis: %w", key, err)
// }
// }
// return nil
// }
// func generateOTP() string {
// randGenerator := rand.New(rand.NewSource(time.Now().UnixNano()))
// return fmt.Sprintf("%04d", randGenerator.Intn(10000))
// }

View File

@ -1,366 +0,0 @@
package services
import (
"fmt"
"mime/multipart"
"os"
"path/filepath"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type BannerService interface {
CreateBanner(request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error)
GetAllBanners() ([]dto.ResponseBannerDTO, error)
GetBannerByID(id string) (*dto.ResponseBannerDTO, error)
UpdateBanner(id string, request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error)
DeleteBanner(id string) error
}
type bannerService struct {
BannerRepo repositories.BannerRepository
}
func NewBannerService(bannerRepo repositories.BannerRepository) BannerService {
return &bannerService{BannerRepo: bannerRepo}
}
func (s *bannerService) saveBannerImage(bannerImage *multipart.FileHeader) (string, error) {
bannerImageDir := "./public" + os.Getenv("BASE_URL") + "/uploads/banners"
if _, err := os.Stat(bannerImageDir); os.IsNotExist(err) {
if err := os.MkdirAll(bannerImageDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for banner image: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
extension := filepath.Ext(bannerImage.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
bannerImageFileName := fmt.Sprintf("%s_banner%s", uuid.New().String(), extension)
bannerImagePath := filepath.Join(bannerImageDir, bannerImageFileName)
src, err := bannerImage.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(bannerImagePath)
if err != nil {
return "", fmt.Errorf("failed to create banner image file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save banner image: %v", err)
}
return bannerImagePath, nil
}
func (s *bannerService) CreateBanner(request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error) {
errors, valid := request.ValidateBannerInput()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
bannerImagePath, err := s.saveBannerImage(bannerImage)
if err != nil {
return nil, fmt.Errorf("failed to save banner image: %v", err)
}
banner := model.Banner{
BannerName: request.BannerName,
BannerImage: bannerImagePath,
}
if err := s.BannerRepo.CreateBanner(&banner); err != nil {
return nil, fmt.Errorf("failed to create banner: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(banner.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(banner.UpdatedAt)
bannerResponseDTO := &dto.ResponseBannerDTO{
ID: banner.ID,
BannerName: banner.BannerName,
BannerImage: banner.BannerImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
articlesCacheKey := "banners:all"
err = utils.DeleteData(articlesCacheKey)
if err != nil {
fmt.Printf("Error deleting cache for all banners: %v\n", err)
}
cacheKey := fmt.Sprintf("banner:%s", banner.ID)
cacheData := map[string]interface{}{
"data": bannerResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching banner: %v\n", err)
}
banners, err := s.BannerRepo.FindAllBanners()
if err == nil {
var bannersDTO []dto.ResponseBannerDTO
for _, b := range banners {
createdAt, _ := utils.FormatDateToIndonesianFormat(b.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(b.UpdatedAt)
bannersDTO = append(bannersDTO, dto.ResponseBannerDTO{
ID: b.ID,
BannerName: b.BannerName,
BannerImage: b.BannerImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData = map[string]interface{}{
"data": bannersDTO,
}
if err := utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching updated banners to Redis: %v\n", err)
}
} else {
fmt.Printf("Error fetching all banners: %v\n", err)
}
return bannerResponseDTO, nil
}
func (s *bannerService) GetAllBanners() ([]dto.ResponseBannerDTO, error) {
var banners []dto.ResponseBannerDTO
cacheKey := "banners:all"
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
if data, ok := cachedData["data"].([]interface{}); ok {
for _, item := range data {
if bannerData, ok := item.(map[string]interface{}); ok {
banners = append(banners, dto.ResponseBannerDTO{
ID: bannerData["id"].(string),
BannerName: bannerData["bannername"].(string),
BannerImage: bannerData["bannerimage"].(string),
CreatedAt: bannerData["createdAt"].(string),
UpdatedAt: bannerData["updatedAt"].(string),
})
}
}
return banners, nil
}
}
records, err := s.BannerRepo.FindAllBanners()
if err != nil {
return nil, fmt.Errorf("failed to fetch banners: %v", err)
}
for _, record := range records {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
banners = append(banners, dto.ResponseBannerDTO{
ID: record.ID,
BannerName: record.BannerName,
BannerImage: record.BannerImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": banners,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching banners: %v\n", err)
}
return banners, nil
}
func (s *bannerService) GetBannerByID(id string) (*dto.ResponseBannerDTO, error) {
cacheKey := fmt.Sprintf("banner:%s", id)
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
if data, ok := cachedData["data"].(map[string]interface{}); ok {
return &dto.ResponseBannerDTO{
ID: data["id"].(string),
BannerName: data["bannername"].(string),
BannerImage: data["bannerimage"].(string),
CreatedAt: data["createdAt"].(string),
UpdatedAt: data["updatedAt"].(string),
}, nil
}
}
banner, err := s.BannerRepo.FindBannerByID(id)
if err != nil {
return nil, fmt.Errorf("banner with ID %s not found", id)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(banner.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(banner.UpdatedAt)
bannerResponseDTO := &dto.ResponseBannerDTO{
ID: banner.ID,
BannerName: banner.BannerName,
BannerImage: banner.BannerImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": bannerResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching banner: %v\n", err)
}
return bannerResponseDTO, nil
}
func (s *bannerService) UpdateBanner(id string, request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error) {
banner, err := s.BannerRepo.FindBannerByID(id)
if err != nil {
return nil, fmt.Errorf("banner with ID %s not found", id)
}
var oldImagePath string
if bannerImage != nil {
bannerImagePath, err := s.saveBannerImage(bannerImage)
if err != nil {
return nil, fmt.Errorf("failed to save banner image: %v", err)
}
oldImagePath = banner.BannerImage
banner.BannerImage = bannerImagePath
}
banner.BannerName = request.BannerName
if err := s.BannerRepo.UpdateBanner(id, banner); err != nil {
return nil, fmt.Errorf("failed to update banner: %v", err)
}
if oldImagePath != "" {
err := os.Remove(oldImagePath)
if err != nil {
fmt.Printf("Failed to delete old banner image: %v\n", err)
}
}
createdAt, _ := utils.FormatDateToIndonesianFormat(banner.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(banner.UpdatedAt)
bannerResponseDTO := &dto.ResponseBannerDTO{
ID: banner.ID,
BannerName: banner.BannerName,
BannerImage: banner.BannerImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("banner:%s", id)
err = utils.DeleteData(cacheKey)
if err != nil {
fmt.Printf("Error deleting cache for banner: %v\n", err)
}
cacheData := map[string]interface{}{
"data": bannerResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching updated banner: %v\n", err)
}
articlesCacheKey := "banners:all"
err = utils.DeleteData(articlesCacheKey)
if err != nil {
fmt.Printf("Error deleting cache for all banners: %v\n", err)
}
banners, err := s.BannerRepo.FindAllBanners()
if err == nil {
var bannersDTO []dto.ResponseBannerDTO
for _, b := range banners {
createdAt, _ := utils.FormatDateToIndonesianFormat(b.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(b.UpdatedAt)
bannersDTO = append(bannersDTO, dto.ResponseBannerDTO{
ID: b.ID,
BannerName: b.BannerName,
BannerImage: b.BannerImage,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData = map[string]interface{}{
"data": bannersDTO,
}
if err := utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching updated banners to Redis: %v\n", err)
}
} else {
fmt.Printf("Error fetching all banners: %v\n", err)
}
return bannerResponseDTO, nil
}
func (s *bannerService) DeleteBanner(id string) error {
banner, err := s.BannerRepo.FindBannerByID(id)
if err != nil {
return fmt.Errorf("banner with ID %s not found", id)
}
if banner.BannerImage != "" {
err := os.Remove(banner.BannerImage)
if err != nil {
fmt.Printf("Failed to delete banner image: %v\n", err)
} else {
fmt.Printf("Successfully deleted banner image: %s\n", banner.BannerImage)
}
}
if err := s.BannerRepo.DeleteBanner(id); err != nil {
return fmt.Errorf("failed to delete banner from database: %v", err)
}
cacheKey := fmt.Sprintf("banner:%s", banner.ID)
err = utils.DeleteData(cacheKey)
if err != nil {
fmt.Printf("Error deleting cache for banner: %v\n", err)
}
articlesCacheKey := "banners:all"
err = utils.DeleteData(articlesCacheKey)
if err != nil {
fmt.Printf("Error deleting cache for all banners: %v\n", err)
}
return nil
}

View File

@ -1,73 +0,0 @@
package services
import (
"context"
"encoding/json"
"fmt"
"time"
"rijig/config"
"rijig/dto"
"github.com/go-redis/redis/v8"
)
const CartTTL = 30 * time.Minute
const CartKeyPrefix = "cart:"
func buildCartKey(userID string) string {
return fmt.Sprintf("%s%s", CartKeyPrefix, userID)
}
func SetCartToRedis(ctx context.Context, userID string, cart dto.RequestCartDTO) error {
data, err := json.Marshal(cart)
if err != nil {
return err
}
return config.RedisClient.Set(ctx, buildCartKey(userID), data, CartTTL).Err()
}
func RefreshCartTTL(ctx context.Context, userID string) error {
return config.RedisClient.Expire(ctx, buildCartKey(userID), CartTTL).Err()
}
func GetCartFromRedis(ctx context.Context, userID string) (*dto.RequestCartDTO, error) {
val, err := config.RedisClient.Get(ctx, buildCartKey(userID)).Result()
if err == redis.Nil {
return nil, nil
} else if err != nil {
return nil, err
}
var cart dto.RequestCartDTO
if err := json.Unmarshal([]byte(val), &cart); err != nil {
return nil, err
}
return &cart, nil
}
func DeleteCartFromRedis(ctx context.Context, userID string) error {
return config.RedisClient.Del(ctx, buildCartKey(userID)).Err()
}
func GetExpiringCartKeys(ctx context.Context, threshold time.Duration) ([]string, error) {
keys, err := config.RedisClient.Keys(ctx, CartKeyPrefix+"*").Result()
if err != nil {
return nil, err
}
var expiringKeys []string
for _, key := range keys {
ttl, err := config.RedisClient.TTL(ctx, key).Result()
if err != nil {
continue
}
if ttl > 0 && ttl <= threshold {
expiringKeys = append(expiringKeys, key)
}
}
return expiringKeys, nil
}

View File

@ -1,266 +0,0 @@
package services
import (
"context"
"errors"
"log"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
)
type CartService interface {
AddOrUpdateItem(ctx context.Context, userID string, req dto.RequestCartItemDTO) error
GetCart(ctx context.Context, userID string) (*dto.ResponseCartDTO, error)
DeleteItem(ctx context.Context, userID string, trashID string) error
ClearCart(ctx context.Context, userID string) error
Checkout(ctx context.Context, userID string) error
}
type cartService struct {
repo repositories.CartRepository
trashRepo repositories.TrashRepository
}
func NewCartService(repo repositories.CartRepository, trashRepo repositories.TrashRepository) CartService {
return &cartService{repo: repo, trashRepo: trashRepo}
}
func (s *cartService) AddOrUpdateItem(ctx context.Context, userID string, req dto.RequestCartItemDTO) error {
if req.Amount <= 0 {
return errors.New("amount harus lebih dari 0")
}
_, err := s.trashRepo.GetTrashCategoryByID(ctx, req.TrashID)
if err != nil {
return err
}
existingCart, err := GetCartFromRedis(ctx, userID)
if err != nil {
return err
}
if existingCart == nil {
existingCart = &dto.RequestCartDTO{
CartItems: []dto.RequestCartItemDTO{},
}
}
updated := false
for i, item := range existingCart.CartItems {
if item.TrashID == req.TrashID {
existingCart.CartItems[i].Amount = req.Amount
updated = true
break
}
}
if !updated {
existingCart.CartItems = append(existingCart.CartItems, dto.RequestCartItemDTO{
TrashID: req.TrashID,
Amount: req.Amount,
})
}
return SetCartToRedis(ctx, userID, *existingCart)
}
func (s *cartService) GetCart(ctx context.Context, userID string) (*dto.ResponseCartDTO, error) {
cached, err := GetCartFromRedis(ctx, userID)
if err != nil {
return nil, err
}
if cached != nil {
if err := RefreshCartTTL(ctx, userID); err != nil {
log.Printf("Warning: Failed to refresh cart TTL for user %s: %v", userID, err)
}
return s.buildResponseFromCache(ctx, userID, cached)
}
cart, err := s.repo.GetCartByUser(ctx, userID)
if err != nil {
return &dto.ResponseCartDTO{
ID: "",
UserID: userID,
TotalAmount: 0,
EstimatedTotalPrice: 0,
CartItems: []dto.ResponseCartItemDTO{},
}, nil
}
response := s.buildResponseFromDB(cart)
cacheData := dto.RequestCartDTO{CartItems: []dto.RequestCartItemDTO{}}
for _, item := range cart.CartItems {
cacheData.CartItems = append(cacheData.CartItems, dto.RequestCartItemDTO{
TrashID: item.TrashCategoryID,
Amount: item.Amount,
})
}
if err := SetCartToRedis(ctx, userID, cacheData); err != nil {
log.Printf("Warning: Failed to cache cart for user %s: %v", userID, err)
}
return response, nil
}
func (s *cartService) DeleteItem(ctx context.Context, userID string, trashID string) error {
existingCart, err := GetCartFromRedis(ctx, userID)
if err != nil {
return err
}
if existingCart == nil {
return errors.New("keranjang tidak ditemukan")
}
filtered := []dto.RequestCartItemDTO{}
for _, item := range existingCart.CartItems {
if item.TrashID != trashID {
filtered = append(filtered, item)
}
}
existingCart.CartItems = filtered
return SetCartToRedis(ctx, userID, *existingCart)
}
func (s *cartService) ClearCart(ctx context.Context, userID string) error {
if err := DeleteCartFromRedis(ctx, userID); err != nil {
return err
}
return s.repo.DeleteCart(ctx, userID)
}
func (s *cartService) Checkout(ctx context.Context, userID string) error {
cachedCart, err := GetCartFromRedis(ctx, userID)
if err != nil {
return err
}
if cachedCart != nil {
if err := s.commitCartFromRedis(ctx, userID, cachedCart); err != nil {
return err
}
}
_, err = s.repo.GetCartByUser(ctx, userID)
if err != nil {
return err
}
DeleteCartFromRedis(ctx, userID)
return s.repo.DeleteCart(ctx, userID)
}
func (s *cartService) buildResponseFromCache(ctx context.Context, userID string, cached *dto.RequestCartDTO) (*dto.ResponseCartDTO, error) {
totalQty := 0.0
totalPrice := 0.0
items := []dto.ResponseCartItemDTO{}
for _, item := range cached.CartItems {
trash, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashID)
if err != nil {
log.Printf("Warning: Trash category %s not found for cached cart item", item.TrashID)
continue
}
subtotal := item.Amount * trash.EstimatedPrice
totalQty += item.Amount
totalPrice += subtotal
items = append(items, dto.ResponseCartItemDTO{
ID: "",
TrashID: item.TrashID,
TrashName: trash.Name,
TrashIcon: trash.Icon,
TrashPrice: trash.EstimatedPrice,
Amount: item.Amount,
SubTotalEstimatedPrice: subtotal,
})
}
return &dto.ResponseCartDTO{
ID: "-",
UserID: userID,
TotalAmount: totalQty,
EstimatedTotalPrice: totalPrice,
CartItems: items,
}, nil
}
func (s *cartService) buildResponseFromDB(cart *model.Cart) *dto.ResponseCartDTO {
var items []dto.ResponseCartItemDTO
for _, item := range cart.CartItems {
items = append(items, dto.ResponseCartItemDTO{
ID: item.ID,
TrashID: item.TrashCategoryID,
TrashName: item.TrashCategory.Name,
TrashIcon: item.TrashCategory.Icon,
TrashPrice: item.TrashCategory.EstimatedPrice,
Amount: item.Amount,
SubTotalEstimatedPrice: item.SubTotalEstimatedPrice,
})
}
return &dto.ResponseCartDTO{
ID: cart.ID,
UserID: cart.UserID,
TotalAmount: cart.TotalAmount,
EstimatedTotalPrice: cart.EstimatedTotalPrice,
CartItems: items,
}
}
func (s *cartService) commitCartFromRedis(ctx context.Context, userID string, cachedCart *dto.RequestCartDTO) error {
if len(cachedCart.CartItems) == 0 {
return nil
}
totalAmount := 0.0
totalPrice := 0.0
var cartItems []model.CartItem
for _, item := range cachedCart.CartItems {
trash, err := s.trashRepo.GetTrashCategoryByID(ctx, item.TrashID)
if err != nil {
log.Printf("Warning: Skipping invalid trash category %s during commit", item.TrashID)
continue
}
subtotal := item.Amount * trash.EstimatedPrice
totalAmount += item.Amount
totalPrice += subtotal
cartItems = append(cartItems, model.CartItem{
TrashCategoryID: item.TrashID,
Amount: item.Amount,
SubTotalEstimatedPrice: subtotal,
})
}
if len(cartItems) == 0 {
return nil
}
newCart := &model.Cart{
UserID: userID,
TotalAmount: totalAmount,
EstimatedTotalPrice: totalPrice,
CartItems: cartItems,
}
return s.repo.CreateCartWithItems(ctx, newCart)
}

View File

@ -1,224 +0,0 @@
package services
import (
"context"
"errors"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
)
type CollectorService interface {
CreateCollector(ctx context.Context, userID string, req dto.RequestCollectorDTO) error
AddTrashToCollector(ctx context.Context, collectorID string, req dto.RequestAddAvaibleTrash) error
GetCollectorByID(ctx context.Context, collectorID string) (*dto.ResponseCollectorDTO, error)
GetCollectorByUserID(ctx context.Context, userID string) (*dto.ResponseCollectorDTO, error)
UpdateCollector(ctx context.Context, collectorID string, jobStatus *string, rating float32, addressID string) error
UpdateAvaibleTrashByCollector(ctx context.Context, collectorID string, updatedTrash []dto.RequestAvaibleTrashbyCollector) error
DeleteAvaibleTrash(ctx context.Context, trashID string) error
}
type collectorService struct {
repo repositories.CollectorRepository
trashRepo repositories.TrashRepository
}
func NewCollectorService(repo repositories.CollectorRepository, trashRepo repositories.TrashRepository,
) CollectorService {
return &collectorService{repo: repo, trashRepo: trashRepo}
}
func (s *collectorService) CreateCollector(ctx context.Context, userID string, req dto.RequestCollectorDTO) error {
collector := &model.Collector{
UserID: userID,
AddressID: req.AddressId,
JobStatus: "inactive",
Rating: 5,
}
if err := s.repo.CreateCollector(ctx, collector); err != nil {
return err
}
var trashItems []model.AvaibleTrashByCollector
for _, item := range req.AvaibleTrashbyCollector {
trashItems = append(trashItems, model.AvaibleTrashByCollector{
CollectorID: collector.ID,
TrashCategoryID: item.TrashId,
Price: item.TrashPrice,
})
}
if err := s.repo.AddAvaibleTrash(ctx, trashItems); err != nil {
return err
}
for _, t := range trashItems {
_ = s.trashRepo.UpdateEstimatedPrice(ctx, t.TrashCategoryID)
}
return nil
}
func (s *collectorService) AddTrashToCollector(ctx context.Context, collectorID string, req dto.RequestAddAvaibleTrash) error {
var trashItems []model.AvaibleTrashByCollector
for _, item := range req.AvaibleTrash {
trashItems = append(trashItems, model.AvaibleTrashByCollector{
CollectorID: collectorID,
TrashCategoryID: item.TrashId,
Price: item.TrashPrice,
})
}
if err := s.repo.AddAvaibleTrash(ctx, trashItems); err != nil {
return err
}
for _, t := range trashItems {
_ = s.trashRepo.UpdateEstimatedPrice(ctx, t.TrashCategoryID)
}
return nil
}
func (s *collectorService) GetCollectorByID(ctx context.Context, collectorID string) (*dto.ResponseCollectorDTO, error) {
collector, err := s.repo.GetCollectorByID(ctx, collectorID)
if err != nil {
return nil, err
}
response := &dto.ResponseCollectorDTO{
ID: collector.ID,
UserId: collector.UserID,
AddressId: collector.AddressID,
JobStatus: &collector.JobStatus,
Rating: collector.Rating,
User: &dto.UserResponseDTO{
ID: collector.User.ID,
Name: collector.User.Name,
Phone: collector.User.Phone,
},
Address: &dto.AddressResponseDTO{
Province: collector.Address.Province,
District: collector.Address.District,
Regency: collector.Address.Regency,
Village: collector.Address.Village,
PostalCode: collector.Address.PostalCode,
Latitude: collector.Address.Latitude,
Longitude: collector.Address.Longitude,
},
}
for _, item := range collector.AvaibleTrashByCollector {
response.AvaibleTrashbyCollector = append(response.AvaibleTrashbyCollector, dto.ResponseAvaibleTrashByCollector{
ID: item.ID,
TrashId: item.TrashCategory.ID,
TrashName: item.TrashCategory.Name,
TrashIcon: item.TrashCategory.Icon,
TrashPrice: item.Price,
})
}
return response, nil
}
func (s *collectorService) GetCollectorByUserID(ctx context.Context, userID string) (*dto.ResponseCollectorDTO, error) {
collector, err := s.repo.GetCollectorByUserID(ctx, userID)
if err != nil {
return nil, err
}
response := &dto.ResponseCollectorDTO{
ID: collector.ID,
UserId: collector.UserID,
AddressId: collector.AddressID,
JobStatus: &collector.JobStatus,
Rating: collector.Rating,
User: &dto.UserResponseDTO{
ID: collector.User.ID,
Name: collector.User.Name,
Phone: collector.User.Phone,
},
Address: &dto.AddressResponseDTO{
Province: collector.Address.Province,
District: collector.Address.District,
Regency: collector.Address.Regency,
Village: collector.Address.Village,
PostalCode: collector.Address.PostalCode,
Latitude: collector.Address.Latitude,
Longitude: collector.Address.Longitude,
},
}
for _, item := range collector.AvaibleTrashByCollector {
response.AvaibleTrashbyCollector = append(response.AvaibleTrashbyCollector, dto.ResponseAvaibleTrashByCollector{
ID: item.ID,
TrashId: item.TrashCategory.ID,
TrashName: item.TrashCategory.Name,
TrashIcon: item.TrashCategory.Icon,
TrashPrice: item.Price,
})
}
return response, nil
}
func (s *collectorService) UpdateCollector(ctx context.Context, collectorID string, jobStatus *string, rating float32, addressID string) error {
updates := make(map[string]interface{})
if jobStatus != nil {
updates["job_status"] = *jobStatus
}
if rating > 0 {
updates["rating"] = rating
}
if addressID != "" {
updates["address_id"] = addressID
}
if len(updates) == 0 {
return errors.New("tidak ada data yang diubah")
}
return s.repo.UpdateCollector(ctx, &model.Collector{ID: collectorID}, updates)
}
func (s *collectorService) UpdateAvaibleTrashByCollector(ctx context.Context, collectorID string, updatedTrash []dto.RequestAvaibleTrashbyCollector) error {
var updated []model.AvaibleTrashByCollector
for _, item := range updatedTrash {
updated = append(updated, model.AvaibleTrashByCollector{
CollectorID: collectorID,
TrashCategoryID: item.TrashId,
Price: item.TrashPrice,
})
}
if err := s.repo.UpdateAvaibleTrashByCollector(ctx, collectorID, updated); err != nil {
return err
}
for _, item := range updated {
_ = s.trashRepo.UpdateEstimatedPrice(ctx, item.TrashCategoryID)
}
return nil
}
func (s *collectorService) DeleteAvaibleTrash(ctx context.Context, trashID string) error {
if trashID == "" {
return errors.New("trash_id tidak boleh kosong")
}
item, err := s.repo.GetTrashItemByID(ctx, trashID)
if err != nil {
return err
}
if err := s.repo.DeleteAvaibleTrash(ctx, trashID); err != nil {
return err
}
return s.trashRepo.UpdateEstimatedPrice(ctx, item.TrashCategoryID)
}

View File

@ -1,163 +0,0 @@
package services
import (
"fmt"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
)
type CompanyProfileService interface {
CreateCompanyProfile(userID string, request *dto.RequestCompanyProfileDTO) (*dto.ResponseCompanyProfileDTO, error)
GetCompanyProfileByID(id string) (*dto.ResponseCompanyProfileDTO, error)
GetCompanyProfilesByUserID(userID string) ([]dto.ResponseCompanyProfileDTO, error)
UpdateCompanyProfile(id string, request *dto.RequestCompanyProfileDTO) (*dto.ResponseCompanyProfileDTO, error)
DeleteCompanyProfile(id string) error
}
type companyProfileService struct {
companyProfileRepo repositories.CompanyProfileRepository
}
func NewCompanyProfileService(companyProfileRepo repositories.CompanyProfileRepository) CompanyProfileService {
return &companyProfileService{
companyProfileRepo: companyProfileRepo,
}
}
func FormatResponseCompanyProfile(companyProfile *model.CompanyProfile) (*dto.ResponseCompanyProfileDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(companyProfile.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(companyProfile.UpdatedAt)
responseDTO := &dto.ResponseCompanyProfileDTO{
ID: companyProfile.ID,
UserID: companyProfile.UserID,
CompanyName: companyProfile.CompanyName,
CompanyAddress: companyProfile.CompanyAddress,
CompanyPhone: companyProfile.CompanyPhone,
CompanyEmail: companyProfile.CompanyEmail,
CompanyLogo: companyProfile.CompanyLogo,
CompanyWebsite: companyProfile.CompanyWebsite,
TaxID: companyProfile.TaxID,
FoundedDate: companyProfile.FoundedDate,
CompanyType: companyProfile.CompanyType,
CompanyDescription: companyProfile.CompanyDescription,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return responseDTO, nil
}
func (s *companyProfileService) CreateCompanyProfile(userID string, request *dto.RequestCompanyProfileDTO) (*dto.ResponseCompanyProfileDTO, error) {
errors, valid := request.ValidateCompanyProfileInput()
if !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
companyProfile := &model.CompanyProfile{
UserID: userID,
CompanyName: request.CompanyName,
CompanyAddress: request.CompanyAddress,
CompanyPhone: request.CompanyPhone,
CompanyEmail: request.CompanyEmail,
CompanyLogo: request.CompanyLogo,
CompanyWebsite: request.CompanyWebsite,
TaxID: request.TaxID,
FoundedDate: request.FoundedDate,
CompanyType: request.CompanyType,
CompanyDescription: request.CompanyDescription,
}
createdCompanyProfile, err := s.companyProfileRepo.CreateCompanyProfile(companyProfile)
if err != nil {
return nil, fmt.Errorf("failed to create company profile: %v", err)
}
responseDTO, err := FormatResponseCompanyProfile(createdCompanyProfile)
if err != nil {
return nil, fmt.Errorf("failed to format company profile response: %v", err)
}
return responseDTO, nil
}
func (s *companyProfileService) GetCompanyProfileByID(id string) (*dto.ResponseCompanyProfileDTO, error) {
companyProfile, err := s.companyProfileRepo.GetCompanyProfileByID(id)
if err != nil {
return nil, fmt.Errorf("error retrieving company profile by ID: %v", err)
}
responseDTO, err := FormatResponseCompanyProfile(companyProfile)
if err != nil {
return nil, fmt.Errorf("error formatting company profile response: %v", err)
}
return responseDTO, nil
}
func (s *companyProfileService) GetCompanyProfilesByUserID(userID string) ([]dto.ResponseCompanyProfileDTO, error) {
companyProfiles, err := s.companyProfileRepo.GetCompanyProfilesByUserID(userID)
if err != nil {
return nil, fmt.Errorf("error retrieving company profiles by userID: %v", err)
}
var responseDTOs []dto.ResponseCompanyProfileDTO
for _, companyProfile := range companyProfiles {
responseDTO, err := FormatResponseCompanyProfile(&companyProfile)
if err != nil {
return nil, fmt.Errorf("error formatting company profile response: %v", err)
}
responseDTOs = append(responseDTOs, *responseDTO)
}
return responseDTOs, nil
}
func (s *companyProfileService) UpdateCompanyProfile(id string, request *dto.RequestCompanyProfileDTO) (*dto.ResponseCompanyProfileDTO, error) {
errors, valid := request.ValidateCompanyProfileInput()
if !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
companyProfile := &model.CompanyProfile{
CompanyName: request.CompanyName,
CompanyAddress: request.CompanyAddress,
CompanyPhone: request.CompanyPhone,
CompanyEmail: request.CompanyEmail,
CompanyLogo: request.CompanyLogo,
CompanyWebsite: request.CompanyWebsite,
TaxID: request.TaxID,
FoundedDate: request.FoundedDate,
CompanyType: request.CompanyType,
CompanyDescription: request.CompanyDescription,
}
updatedCompanyProfile, err := s.companyProfileRepo.UpdateCompanyProfile(id, companyProfile)
if err != nil {
return nil, fmt.Errorf("failed to update company profile: %v", err)
}
responseDTO, err := FormatResponseCompanyProfile(updatedCompanyProfile)
if err != nil {
return nil, fmt.Errorf("failed to format company profile response: %v", err)
}
return responseDTO, nil
}
func (s *companyProfileService) DeleteCompanyProfile(id string) error {
err := s.companyProfileRepo.DeleteCompanyProfile(id)
if err != nil {
return fmt.Errorf("failed to delete company profile: %v", err)
}
return nil
}

View File

@ -1,155 +0,0 @@
package services
import (
"fmt"
"log"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
)
type CoverageAreaService interface {
CreateCoverageArea(request dto.RequestCoverageArea) (*dto.ResponseCoverageArea, error)
GetCoverageAreaByID(id string) (*dto.ResponseCoverageArea, error)
GetAllCoverageAreas() ([]dto.ResponseCoverageArea, error)
UpdateCoverageArea(id string, request dto.RequestCoverageArea) (*dto.ResponseCoverageArea, error)
DeleteCoverageArea(id string) error
}
type coverageAreaService struct {
repo repositories.CoverageAreaRepository
WilayahRepo repositories.WilayahIndonesiaRepository
}
func NewCoverageAreaService(repo repositories.CoverageAreaRepository, WilayahRepo repositories.WilayahIndonesiaRepository) CoverageAreaService {
return &coverageAreaService{repo: repo, WilayahRepo: WilayahRepo}
}
func ConvertCoverageAreaToResponse(coverage *model.CoverageArea) *dto.ResponseCoverageArea {
createdAt, _ := utils.FormatDateToIndonesianFormat(coverage.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coverage.UpdatedAt)
return &dto.ResponseCoverageArea{
ID: coverage.ID,
Province: coverage.Province,
Regency: coverage.Regency,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
}
func (s *coverageAreaService) CreateCoverageArea(request dto.RequestCoverageArea) (*dto.ResponseCoverageArea, error) {
errors, valid := request.ValidateCoverageArea()
if !valid {
return nil, fmt.Errorf("validation errors: %v", errors)
}
province, _, err := s.WilayahRepo.FindProvinceByID(request.Province, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid province_id")
}
regency, _, err := s.WilayahRepo.FindRegencyByID(request.Regency, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid regency_id")
}
existingCoverage, err := s.repo.FindCoverageByProvinceAndRegency(province.Name, regency.Name)
if err == nil && existingCoverage != nil {
return nil, fmt.Errorf("coverage area with province %s and regency %s already exists", province.Name, regency.Name)
}
coverage := model.CoverageArea{
Province: province.Name,
Regency: regency.Name,
}
if err := s.repo.CreateCoverage(&coverage); err != nil {
return nil, fmt.Errorf("failed to create coverage area: %v", err)
}
response := ConvertCoverageAreaToResponse(&coverage)
return response, nil
}
func (s *coverageAreaService) GetCoverageAreaByID(id string) (*dto.ResponseCoverageArea, error) {
coverage, err := s.repo.FindCoverageById(id)
if err != nil {
return nil, err
}
response := ConvertCoverageAreaToResponse(coverage)
return response, nil
}
func (s *coverageAreaService) GetAllCoverageAreas() ([]dto.ResponseCoverageArea, error) {
coverageAreas, err := s.repo.FindAllCoverage()
if err != nil {
return nil, err
}
var response []dto.ResponseCoverageArea
for _, coverage := range coverageAreas {
response = append(response, *ConvertCoverageAreaToResponse(&coverage))
}
return response, nil
}
func (s *coverageAreaService) UpdateCoverageArea(id string, request dto.RequestCoverageArea) (*dto.ResponseCoverageArea, error) {
errors, valid := request.ValidateCoverageArea()
if !valid {
return nil, fmt.Errorf("validation errors: %v", errors)
}
coverage, err := s.repo.FindCoverageById(id)
if err != nil {
return nil, fmt.Errorf("coverage area with ID %s not found: %v", id, err)
}
province, _, err := s.WilayahRepo.FindProvinceByID(request.Province, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid province_id")
}
regency, _, err := s.WilayahRepo.FindRegencyByID(request.Regency, 0, 0)
if err != nil {
return nil, fmt.Errorf("invalid regency_id")
}
existingCoverage, err := s.repo.FindCoverageByProvinceAndRegency(province.Name, regency.Name)
if err == nil && existingCoverage != nil {
return nil, fmt.Errorf("coverage area with province %s and regency %s already exists", province.Name, regency.Name)
}
coverage.Province = province.Name
coverage.Regency = regency.Name
if err := s.repo.UpdateCoverage(id, coverage); err != nil {
return nil, fmt.Errorf("failed to update coverage area: %v", err)
}
response := ConvertCoverageAreaToResponse(coverage)
return response, nil
}
func (s *coverageAreaService) DeleteCoverageArea(id string) error {
coverage, err := s.repo.FindCoverageById(id)
if err != nil {
return fmt.Errorf("coverage area with ID %s not found: %v", id, err)
}
if err := s.repo.DeleteCoverage(id); err != nil {
return fmt.Errorf("failed to delete coverage area: %v", err)
}
log.Printf("Coverage area with ID %s successfully deleted", coverage.ID)
return nil
}

View File

@ -1,289 +0,0 @@
package services
import (
"fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
)
type IdentityCardService interface {
CreateIdentityCard(userID string, request *dto.RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*dto.ResponseIdentityCardDTO, error)
GetIdentityCardByID(id string) (*dto.ResponseIdentityCardDTO, error)
GetIdentityCardsByUserID(userID string) ([]dto.ResponseIdentityCardDTO, error)
UpdateIdentityCard(userID string, id string, request *dto.RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*dto.ResponseIdentityCardDTO, error)
DeleteIdentityCard(id string) error
}
type identityCardService struct {
identityCardRepo repositories.IdentityCardRepository
userRepo repositories.UserProfilRepository
}
func NewIdentityCardService(identityCardRepo repositories.IdentityCardRepository, userRepo repositories.UserProfilRepository) IdentityCardService {
return &identityCardService{
identityCardRepo: identityCardRepo,
userRepo: userRepo,
}
}
func FormatResponseIdentityCars(identityCard *model.IdentityCard) (*dto.ResponseIdentityCardDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(identityCard.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(identityCard.UpdatedAt)
idcardResponseDTO := &dto.ResponseIdentityCardDTO{
ID: identityCard.ID,
UserID: identityCard.UserID,
Identificationumber: identityCard.Identificationumber,
Placeofbirth: identityCard.Placeofbirth,
Dateofbirth: identityCard.Dateofbirth,
Gender: identityCard.Gender,
BloodType: identityCard.BloodType,
District: identityCard.District,
Village: identityCard.Village,
Neighbourhood: identityCard.Neighbourhood,
Religion: identityCard.Religion,
Maritalstatus: identityCard.Maritalstatus,
Job: identityCard.Job,
Citizenship: identityCard.Citizenship,
Validuntil: identityCard.Validuntil,
Cardphoto: identityCard.Cardphoto,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return idcardResponseDTO, nil
}
func (s *identityCardService) saveIdentityCardImage(userID string, cardPhoto *multipart.FileHeader) (string, error) {
pathImage := "/uploads/identitycards/"
cardPhotoDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(cardPhotoDir); os.IsNotExist(err) {
if err := os.MkdirAll(cardPhotoDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for identity card photo: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
extension := filepath.Ext(cardPhoto.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
cardPhotoFileName := fmt.Sprintf("%s_cardphoto%s", userID, extension)
cardPhotoPath := filepath.Join(cardPhotoDir, cardPhotoFileName)
src, err := cardPhoto.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(cardPhotoPath)
if err != nil {
return "", fmt.Errorf("failed to create card photo file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save card photo: %v", err)
}
cardPhotoURL := fmt.Sprintf("%s%s", pathImage, cardPhotoFileName)
return cardPhotoURL, nil
}
func deleteIdentityCardImage(imagePath string) error {
if imagePath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + imagePath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Image deleted successfully: %s", absolutePath)
return nil
}
func (s *identityCardService) CreateIdentityCard(userID string, request *dto.RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*dto.ResponseIdentityCardDTO, error) {
errors, valid := request.ValidateIdentityCardInput()
if !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
cardPhotoPath, err := s.saveIdentityCardImage(userID, cardPhoto)
if err != nil {
return nil, fmt.Errorf("failed to save card photo: %v", err)
}
identityCard := &model.IdentityCard{
UserID: userID,
Identificationumber: request.Identificationumber,
Placeofbirth: request.Placeofbirth,
Dateofbirth: request.Dateofbirth,
Gender: request.Gender,
BloodType: request.BloodType,
District: request.District,
Village: request.Village,
Neighbourhood: request.Neighbourhood,
Religion: request.Religion,
Maritalstatus: request.Maritalstatus,
Job: request.Job,
Citizenship: request.Citizenship,
Validuntil: request.Validuntil,
Cardphoto: cardPhotoPath,
}
identityCard, err = s.identityCardRepo.CreateIdentityCard(identityCard)
if err != nil {
log.Printf("Error creating identity card: %v", err)
return nil, fmt.Errorf("failed to create identity card: %v", err)
}
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("failde to fint user: %v", err)
}
user.RegistrationStatus = "onreview"
err = s.userRepo.Update(user)
if err != nil {
return nil, fmt.Errorf("failed to update user: %v", err)
}
idcardResponseDTO, _ := FormatResponseIdentityCars(identityCard)
return idcardResponseDTO, nil
}
func (s *identityCardService) GetIdentityCardByID(id string) (*dto.ResponseIdentityCardDTO, error) {
identityCard, err := s.identityCardRepo.GetIdentityCardByID(id)
if err != nil {
log.Printf("Error fetching identity card: %v", err)
return nil, fmt.Errorf("failed to fetch identity card")
}
idcardResponseDTO, _ := FormatResponseIdentityCars(identityCard)
return idcardResponseDTO, nil
}
func (s *identityCardService) GetIdentityCardsByUserID(userID string) ([]dto.ResponseIdentityCardDTO, error) {
identityCards, err := s.identityCardRepo.GetIdentityCardsByUserID(userID)
if err != nil {
log.Printf("Error fetching identity cards by userID: %v", err)
return nil, fmt.Errorf("failed to fetch identity cards by userID")
}
var response []dto.ResponseIdentityCardDTO
for _, card := range identityCards {
idcardResponseDTO, err := FormatResponseIdentityCars(&card)
if err != nil {
log.Printf("Error creating response DTO for identity card ID %v: %v", card.ID, err)
continue
}
response = append(response, *idcardResponseDTO)
}
return response, nil
}
func (s *identityCardService) UpdateIdentityCard(userID string, id string, request *dto.RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*dto.ResponseIdentityCardDTO, error) {
errors, valid := request.ValidateIdentityCardInput()
if !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
identityCard, err := s.identityCardRepo.GetIdentityCardByID(id)
if err != nil {
return nil, fmt.Errorf("identity card not found: %v", err)
}
if identityCard.Cardphoto != "" {
err := deleteIdentityCardImage(identityCard.Cardphoto)
if err != nil {
return nil, fmt.Errorf("failed to delete old image: %v", err)
}
}
var cardPhotoPath string
if cardPhoto != nil {
cardPhotoPath, err = s.saveIdentityCardImage(userID, cardPhoto)
if err != nil {
return nil, fmt.Errorf("failed to save card photo: %v", err)
}
}
identityCard.Identificationumber = request.Identificationumber
identityCard.Placeofbirth = request.Placeofbirth
identityCard.Dateofbirth = request.Dateofbirth
identityCard.Gender = request.Gender
identityCard.BloodType = request.BloodType
identityCard.District = request.District
identityCard.Village = request.Village
identityCard.Neighbourhood = request.Neighbourhood
identityCard.Religion = request.Religion
identityCard.Maritalstatus = request.Maritalstatus
identityCard.Job = request.Job
identityCard.Citizenship = request.Citizenship
identityCard.Validuntil = request.Validuntil
if cardPhotoPath != "" {
identityCard.Cardphoto = cardPhotoPath
}
identityCard, err = s.identityCardRepo.UpdateIdentityCard(id, identityCard)
if err != nil {
log.Printf("Error updating identity card: %v", err)
return nil, fmt.Errorf("failed to update identity card: %v", err)
}
idcardResponseDTO, _ := FormatResponseIdentityCars(identityCard)
return idcardResponseDTO, nil
}
func (s *identityCardService) DeleteIdentityCard(id string) error {
identityCard, err := s.identityCardRepo.GetIdentityCardByID(id)
if err != nil {
return fmt.Errorf("identity card not found: %v", err)
}
if identityCard.Cardphoto != "" {
err := deleteIdentityCardImage(identityCard.Cardphoto)
if err != nil {
return fmt.Errorf("failed to delete card photo: %v", err)
}
}
err = s.identityCardRepo.DeleteIdentityCard(id)
if err != nil {
return fmt.Errorf("failed to delete identity card: %v", err)
}
return nil
}

View File

@ -1,299 +0,0 @@
package services
import (
"fmt"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
)
type InitialCointService interface {
CreateInitialCoint(request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error)
GetAllInitialCoints() ([]dto.ReponseInitialCointDTO, error)
GetInitialCointByID(id string) (*dto.ReponseInitialCointDTO, error)
UpdateInitialCoint(id string, request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error)
DeleteInitialCoint(id string) error
}
type initialCointService struct {
InitialCointRepo repositories.InitialCointRepository
}
func NewInitialCointService(repo repositories.InitialCointRepository) InitialCointService {
return &initialCointService{InitialCointRepo: repo}
}
func (s *initialCointService) CreateInitialCoint(request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error) {
errors, valid := request.ValidateCointInput()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
coint := model.InitialCoint{
CoinName: request.CoinName,
ValuePerUnit: request.ValuePerUnit,
}
if err := s.InitialCointRepo.CreateInitialCoint(&coint); err != nil {
return nil, fmt.Errorf("failed to create initial coint: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(coint.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coint.UpdatedAt)
responseDTO := &dto.ReponseInitialCointDTO{
ID: coint.ID,
CoinName: coint.CoinName,
ValuePerUnit: coint.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("initialcoint:%s", coint.ID)
cacheData := map[string]interface{}{
"data": responseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching new initial coint: %v\n", err)
}
err := s.updateAllCointCache()
if err != nil {
return nil, fmt.Errorf("error updating all initial coint cache: %v", err)
}
return responseDTO, nil
}
func (s *initialCointService) GetAllInitialCoints() ([]dto.ReponseInitialCointDTO, error) {
var cointsDTO []dto.ReponseInitialCointDTO
cacheKey := "initialcoints:all"
cachedData, err := utils.GetJSONData(cacheKey)
if err != nil {
fmt.Printf("Error fetching cache for initialcoints: %v\n", err)
}
if cachedData != nil {
if data, ok := cachedData["data"].([]interface{}); ok {
for _, item := range data {
if cointData, ok := item.(map[string]interface{}); ok {
if coinID, ok := cointData["coin_id"].(string); ok {
if coinName, ok := cointData["coin_name"].(string); ok {
if valuePerUnit, ok := cointData["value_perunit"].(float64); ok {
if createdAt, ok := cointData["createdAt"].(string); ok {
if updatedAt, ok := cointData["updatedAt"].(string); ok {
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: coinID,
CoinName: coinName,
ValuePerUnit: valuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
}
}
}
}
}
}
return cointsDTO, nil
}
}
records, err := s.InitialCointRepo.FindAllInitialCoints()
if err != nil {
return nil, fmt.Errorf("failed to fetch initial coints from database: %v", err)
}
if len(records) == 0 {
return cointsDTO, nil
}
for _, record := range records {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: record.ID,
CoinName: record.CoinName,
ValuePerUnit: record.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": cointsDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching all initial coints: %v\n", err)
}
return cointsDTO, nil
}
func (s *initialCointService) GetInitialCointByID(id string) (*dto.ReponseInitialCointDTO, error) {
cacheKey := fmt.Sprintf("initialcoint:%s", id)
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
if data, ok := cachedData["data"].(map[string]interface{}); ok {
return &dto.ReponseInitialCointDTO{
ID: data["coin_id"].(string),
CoinName: data["coin_name"].(string),
ValuePerUnit: data["value_perunit"].(float64),
CreatedAt: data["createdAt"].(string),
UpdatedAt: data["updatedAt"].(string),
}, nil
} else {
return nil, fmt.Errorf("error: cache data is not in the expected format for coin ID %s", id)
}
}
coint, err := s.InitialCointRepo.FindInitialCointByID(id)
if err != nil {
return nil, fmt.Errorf("failed to fetch initial coint by ID %s: %v", id, err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(coint.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coint.UpdatedAt)
cointDTO := &dto.ReponseInitialCointDTO{
ID: coint.ID,
CoinName: coint.CoinName,
ValuePerUnit: coint.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": cointDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching initial coint by ID: %v\n", err)
}
return cointDTO, nil
}
func (s *initialCointService) UpdateInitialCoint(id string, request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error) {
coint, err := s.InitialCointRepo.FindInitialCointByID(id)
if err != nil {
return nil, fmt.Errorf("initial coint with ID %s not found", id)
}
coint.CoinName = request.CoinName
coint.ValuePerUnit = request.ValuePerUnit
if err := s.InitialCointRepo.UpdateInitialCoint(id, coint); err != nil {
return nil, fmt.Errorf("failed to update initial coint: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(coint.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coint.UpdatedAt)
cointDTO := &dto.ReponseInitialCointDTO{
ID: coint.ID,
CoinName: coint.CoinName,
ValuePerUnit: coint.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("initialcoint:%s", id)
cacheData := map[string]interface{}{
"data": cointDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching updated initial coint: %v\n", err)
}
allCoints, err := s.InitialCointRepo.FindAllInitialCoints()
if err != nil {
return nil, fmt.Errorf("failed to fetch all initial coints from database: %v", err)
}
var cointsDTO []dto.ReponseInitialCointDTO
for _, record := range allCoints {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: record.ID,
CoinName: record.CoinName,
ValuePerUnit: record.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheAllKey := "initialcoints:all"
cacheAllData := map[string]interface{}{
"data": cointsDTO,
}
if err := utils.SetJSONData(cacheAllKey, cacheAllData, time.Hour*24); err != nil {
fmt.Printf("Error caching all initial coints: %v\n", err)
}
return cointDTO, nil
}
func (s *initialCointService) DeleteInitialCoint(id string) error {
coint, err := s.InitialCointRepo.FindInitialCointByID(id)
if err != nil {
return fmt.Errorf("initial coint with ID %s not found", id)
}
if err := s.InitialCointRepo.DeleteInitialCoint(id); err != nil {
return fmt.Errorf("failed to delete initial coint: %v", err)
}
cacheKey := fmt.Sprintf("initialcoint:%s", coint.ID)
if err := utils.DeleteData(cacheKey); err != nil {
fmt.Printf("Error deleting cache for initial coint: %v\n", err)
}
return s.updateAllCointCache()
}
func (s *initialCointService) updateAllCointCache() error {
records, err := s.InitialCointRepo.FindAllInitialCoints()
if err != nil {
return fmt.Errorf("failed to fetch all initial coints from database: %v", err)
}
var cointsDTO []dto.ReponseInitialCointDTO
for _, record := range records {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: record.ID,
CoinName: record.CoinName,
ValuePerUnit: record.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheAllKey := "initialcoints:all"
cacheAllData := map[string]interface{}{
"data": cointsDTO,
}
if err := utils.SetJSONData(cacheAllKey, cacheAllData, time.Hour*24); err != nil {
fmt.Printf("Error caching all initial coints: %v\n", err)
return err
}
return nil
}

View File

@ -1,36 +0,0 @@
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

@ -1,146 +0,0 @@
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

@ -1,404 +0,0 @@
package services
import (
"fmt"
"mime/multipart"
"os"
"path/filepath"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type ProductService interface {
SaveProductImage(file *multipart.FileHeader, imageType string) (string, error)
CreateProduct(userID string, productDTO *dto.RequestProductDTO) (*dto.ResponseProductDTO, error)
GetAllProductsByStoreID(userID string, page, limit int) ([]dto.ResponseProductDTO, int64, error)
GetProductByID(productID string) (*dto.ResponseProductDTO, error)
UpdateProduct(userID, productID string, productDTO *dto.RequestProductDTO) (*dto.ResponseProductDTO, error)
DeleteProduct(productID string) error
DeleteProducts(productIDs []string) error
DeleteProductImage(imageID string) error
DeleteProductImages(imageIDs []string) error
deleteImageFile(imageID string) error
}
type productService struct {
productRepo repositories.ProductRepository
storeRepo repositories.StoreRepository
}
func NewProductService(productRepo repositories.ProductRepository, storeRepo repositories.StoreRepository) ProductService {
return &productService{productRepo, storeRepo}
}
func (s *productService) CreateProduct(userID string, productDTO *dto.RequestProductDTO) (*dto.ResponseProductDTO, error) {
store, err := s.storeRepo.FindStoreByUserID(userID)
if err != nil {
return nil, fmt.Errorf("error retrieving store by user ID: %w", err)
}
if store == nil {
return nil, fmt.Errorf("store not found for user %s", userID)
}
var imagePaths []string
var productImages []model.ProductImage
for _, file := range productDTO.ProductImages {
imagePath, err := s.SaveProductImage(file, "product")
if err != nil {
return nil, fmt.Errorf("failed to save product image: %w", err)
}
imagePaths = append(imagePaths, imagePath)
productImages = append(productImages, model.ProductImage{
ImageURL: imagePath,
})
}
if len(imagePaths) == 0 {
return nil, fmt.Errorf("at least one image is required for the product")
}
product := model.Product{
StoreID: store.ID,
ProductName: productDTO.ProductName,
Quantity: productDTO.Quantity,
}
product.ProductImages = productImages
if err := s.productRepo.CreateProduct(&product); err != nil {
return nil, fmt.Errorf("failed to create product: %w", err)
}
createdAt, err := utils.FormatDateToIndonesianFormat(product.CreatedAt)
if err != nil {
return nil, fmt.Errorf("failed to format createdAt: %w", err)
}
updatedAt, err := utils.FormatDateToIndonesianFormat(product.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("failed to format updatedAt: %w", err)
}
var productImagesDTO []dto.ResponseProductImageDTO
for _, img := range product.ProductImages {
productImagesDTO = append(productImagesDTO, dto.ResponseProductImageDTO{
ID: img.ID,
ProductID: img.ProductID,
ImageURL: img.ImageURL,
})
}
productDTOResponse := &dto.ResponseProductDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductName: product.ProductName,
Quantity: product.Quantity,
ProductImages: productImagesDTO,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return productDTOResponse, nil
}
func (s *productService) GetAllProductsByStoreID(userID string, page, limit int) ([]dto.ResponseProductDTO, int64, error) {
store, err := s.storeRepo.FindStoreByUserID(userID)
if err != nil {
return nil, 0, fmt.Errorf("error retrieving store by user ID: %w", err)
}
if store == nil {
return nil, 0, fmt.Errorf("store not found for user %s", userID)
}
total, err := s.productRepo.CountProductsByStoreID(store.ID)
if err != nil {
return nil, 0, fmt.Errorf("error counting products: %w", err)
}
products, err := s.productRepo.FindProductsByStoreID(store.ID, page, limit)
if err != nil {
return nil, 0, fmt.Errorf("error fetching products: %w", err)
}
var productDTOs []dto.ResponseProductDTO
for _, product := range products {
productImages, err := s.productRepo.FindProductImagesByProductID(product.ID)
if err != nil {
return nil, 0, fmt.Errorf("error fetching product images: %w", err)
}
var productImagesDTO []dto.ResponseProductImageDTO
for _, img := range productImages {
productImagesDTO = append(productImagesDTO, dto.ResponseProductImageDTO{
ID: img.ID,
ProductID: img.ProductID,
ImageURL: img.ImageURL,
})
}
createdAt, _ := utils.FormatDateToIndonesianFormat(product.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(product.UpdatedAt)
productDTOs = append(productDTOs, dto.ResponseProductDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductName: product.ProductName,
Quantity: product.Quantity,
Saled: product.Saled,
ProductImages: productImagesDTO,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
return productDTOs, total, nil
}
func (s *productService) GetProductByID(productID string) (*dto.ResponseProductDTO, error) {
product, err := s.productRepo.GetProductByID(productID)
if err != nil {
return nil, fmt.Errorf("failed to retrieve product: %w", err)
}
if product == nil {
return nil, fmt.Errorf("product not found")
}
createdAt, _ := utils.FormatDateToIndonesianFormat(product.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(product.UpdatedAt)
productDTO := &dto.ResponseProductDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductName: product.ProductName,
Quantity: product.Quantity,
Saled: product.Saled,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
var productImagesDTO []dto.ResponseProductImageDTO
for _, image := range product.ProductImages {
productImagesDTO = append(productImagesDTO, dto.ResponseProductImageDTO{
ID: image.ID,
ProductID: image.ProductID,
ImageURL: image.ImageURL,
})
}
productDTO.ProductImages = productImagesDTO
return productDTO, nil
}
func (s *productService) UpdateProduct(userID, productID string, productDTO *dto.RequestProductDTO) (*dto.ResponseProductDTO, error) {
store, err := s.storeRepo.FindStoreByUserID(userID)
if err != nil {
return nil, fmt.Errorf("error retrieving store by user ID: %w", err)
}
if store == nil {
return nil, fmt.Errorf("store not found for user %s", userID)
}
product, err := s.productRepo.GetProductByID(productID)
if err != nil {
return nil, fmt.Errorf("failed to retrieve product: %v", err)
}
if product == nil {
return nil, fmt.Errorf("product not found")
}
if product.StoreID != store.ID {
return nil, fmt.Errorf("user does not own the store for this product")
}
if err := s.deleteProductImages(productID); err != nil {
return nil, fmt.Errorf("failed to delete old product images: %v", err)
}
var productImages []model.ProductImage
for _, file := range productDTO.ProductImages {
imagePath, err := s.SaveProductImage(file, "product")
if err != nil {
return nil, fmt.Errorf("failed to save product image: %w", err)
}
productImages = append(productImages, model.ProductImage{
ImageURL: imagePath,
})
}
product.ProductName = productDTO.ProductName
product.Quantity = productDTO.Quantity
product.ProductImages = productImages
if err := s.productRepo.UpdateProduct(product); err != nil {
return nil, fmt.Errorf("failed to update product: %w", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(product.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(product.UpdatedAt)
var productImagesDTO []dto.ResponseProductImageDTO
for _, img := range product.ProductImages {
productImagesDTO = append(productImagesDTO, dto.ResponseProductImageDTO{
ID: img.ID,
ProductID: img.ProductID,
ImageURL: img.ImageURL,
})
}
productDTOResponse := &dto.ResponseProductDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductName: product.ProductName,
Quantity: product.Quantity,
ProductImages: productImagesDTO,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return productDTOResponse, nil
}
func (s *productService) SaveProductImage(file *multipart.FileHeader, imageType string) (string, error) {
imageDir := fmt.Sprintf("./public%s/uploads/store/%s", os.Getenv("BASE_URL"), imageType)
if _, err := os.Stat(imageDir); os.IsNotExist(err) {
if err := os.MkdirAll(imageDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for %s image: %v", imageType, err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
extension := filepath.Ext(file.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed for %s", imageType)
}
fileName := fmt.Sprintf("%s_%s%s", imageType, uuid.New().String(), extension)
filePath := filepath.Join(imageDir, fileName)
fileData, err := file.Open()
if err != nil {
return "", fmt.Errorf("failed to open file: %w", err)
}
defer fileData.Close()
outFile, err := os.Create(filePath)
if err != nil {
return "", fmt.Errorf("failed to create %s image file: %v", imageType, err)
}
defer outFile.Close()
if _, err := outFile.ReadFrom(fileData); err != nil {
return "", fmt.Errorf("failed to save %s image: %v", imageType, err)
}
return filepath.Join("/uploads/store/", imageType, fileName), nil
}
func (s *productService) DeleteProduct(productID string) error {
if err := s.deleteProductImages(productID); err != nil {
return fmt.Errorf("failed to delete associated product images: %w", err)
}
if err := s.productRepo.DeleteProduct(productID); err != nil {
return fmt.Errorf("failed to delete product: %w", err)
}
return nil
}
func (s *productService) DeleteProducts(productIDs []string) error {
for _, productID := range productIDs {
if err := s.deleteProductImages(productID); err != nil {
return fmt.Errorf("failed to delete associated images for product %s: %w", productID, err)
}
}
if err := s.productRepo.DeleteProductsByID(productIDs); err != nil {
return fmt.Errorf("failed to delete products: %w", err)
}
return nil
}
func (s *productService) DeleteProductImage(imageID string) error {
if err := s.deleteImageFile(imageID); err != nil {
return fmt.Errorf("failed to delete image file with ID %s: %w", imageID, err)
}
if err := s.productRepo.DeleteProductImageByID(imageID); err != nil {
return fmt.Errorf("failed to delete product image from database: %w", err)
}
return nil
}
func (s *productService) DeleteProductImages(imageIDs []string) error {
for _, imageID := range imageIDs {
if err := s.deleteImageFile(imageID); err != nil {
return fmt.Errorf("failed to delete image file with ID %s: %w", imageID, err)
}
}
if err := s.productRepo.DeleteProductImagesByID(imageIDs); err != nil {
return fmt.Errorf("failed to delete product images from database: %w", err)
}
return nil
}
func (s *productService) deleteProductImages(productID string) error {
productImages, err := s.productRepo.FindProductImagesByProductID(productID)
if err != nil {
return fmt.Errorf("failed to fetch product images: %w", err)
}
for _, img := range productImages {
if err := s.deleteImageFile(img.ID); err != nil {
return fmt.Errorf("failed to delete image file: %w", err)
}
}
if err := s.productRepo.DeleteProductImagesByProductID(productID); err != nil {
return fmt.Errorf("failed to delete product images from database: %w", err)
}
return nil
}
func (s *productService) deleteImageFile(imageID string) error {
productImage, err := s.productRepo.GetProductImageByID(imageID)
if err != nil {
return fmt.Errorf("failed to fetch product image: %w", err)
}
if productImage == nil {
return fmt.Errorf("product image with ID %s not found", imageID)
}
baseURL := os.Getenv("BASE_URL")
imagePath := fmt.Sprintf("./public%s%s", baseURL, productImage.ImageURL)
if err := os.Remove(imagePath); err != nil {
return fmt.Errorf("failed to delete image file: %w", err)
}
return nil
}

View File

@ -1,43 +0,0 @@
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

@ -1,137 +0,0 @@
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,
}
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,103 +0,0 @@
package services
import (
"context"
"fmt"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/utils"
)
type RoleService interface {
GetRoles(ctx context.Context) ([]dto.RoleResponseDTO, error)
GetRoleByID(ctx context.Context, roleID string) (*dto.RoleResponseDTO, error)
}
type roleService struct {
RoleRepo repositories.RoleRepository
}
func NewRoleService(roleRepo repositories.RoleRepository) RoleService {
return &roleService{RoleRepo: roleRepo}
}
func (s *roleService) GetRoles(ctx context.Context) ([]dto.RoleResponseDTO, error) {
cacheKey := "roles_list"
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
var roles []dto.RoleResponseDTO
if data, ok := cachedData["data"].([]interface{}); ok {
for _, item := range data {
role, ok := item.(map[string]interface{})
if ok {
roles = append(roles, dto.RoleResponseDTO{
ID: role["role_id"].(string),
RoleName: role["role_name"].(string),
CreatedAt: role["createdAt"].(string),
UpdatedAt: role["updatedAt"].(string),
})
}
}
return roles, nil
}
}
roles, err := s.RoleRepo.FindAll(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch roles: %v", err)
}
var roleDTOs []dto.RoleResponseDTO
for _, role := range roles {
createdAt, _ := utils.FormatDateToIndonesianFormat(role.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(role.UpdatedAt)
roleDTOs = append(roleDTOs, dto.RoleResponseDTO{
ID: role.ID,
RoleName: role.RoleName,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": roleDTOs,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching roles data to Redis: %v\n", err)
}
return roleDTOs, nil
}
func (s *roleService) GetRoleByID(ctx context.Context, roleID string) (*dto.RoleResponseDTO, error) {
role, err := s.RoleRepo.FindByID(ctx, roleID)
if err != nil {
return nil, fmt.Errorf("role not found: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(role.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(role.UpdatedAt)
roleDTO := &dto.RoleResponseDTO{
ID: role.ID,
RoleName: role.RoleName,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("role:%s", roleID)
cacheData := map[string]interface{}{
"data": roleDTO,
}
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
if err != nil {
fmt.Printf("Error caching role data to Redis: %v\n", err)
}
return roleDTO, nil
}

View File

@ -1,294 +0,0 @@
package services
import (
"fmt"
"mime/multipart"
"os"
"path/filepath"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type StoreService interface {
CreateStore(userID string, storeDTO dto.RequestStoreDTO, storeLogo *multipart.FileHeader, storeBanner *multipart.FileHeader) (*dto.ResponseStoreDTO, error)
GetStoreByUserID(userID string) (*dto.ResponseStoreDTO, error)
UpdateStore(storeID string, storeDTO *dto.RequestStoreDTO, storeLogo *multipart.FileHeader, storeBanner *multipart.FileHeader, userID string) (*dto.ResponseStoreDTO, error)
DeleteStore(storeID string) error
}
type storeService struct {
storeRepo repositories.StoreRepository
}
func NewStoreService(storeRepo repositories.StoreRepository) StoreService {
return &storeService{storeRepo}
}
func (s *storeService) CreateStore(userID string, storeDTO dto.RequestStoreDTO, storeLogo, storeBanner *multipart.FileHeader) (*dto.ResponseStoreDTO, error) {
if errors, valid := storeDTO.ValidateStoreInput(); !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
existingStore, err := s.storeRepo.FindStoreByUserID(userID)
if err != nil {
return nil, fmt.Errorf("error checking if user already has a store: %w", err)
}
if existingStore != nil {
return nil, fmt.Errorf("user already has a store")
}
address, err := s.storeRepo.FindAddressByID(storeDTO.StoreAddressID)
if err != nil {
return nil, fmt.Errorf("error validating store address ID: %w", err)
}
if address == nil {
return nil, fmt.Errorf("store address ID not found")
}
storeLogoPath, err := s.saveStoreImage(storeLogo, "logo")
if err != nil {
return nil, fmt.Errorf("failed to save store logo: %w", err)
}
storeBannerPath, err := s.saveStoreImage(storeBanner, "banner")
if err != nil {
return nil, fmt.Errorf("failed to save store banner: %w", err)
}
store := model.Store{
UserID: userID,
StoreName: storeDTO.StoreName,
StoreLogo: storeLogoPath,
StoreBanner: storeBannerPath,
StoreInfo: storeDTO.StoreInfo,
StoreAddressID: storeDTO.StoreAddressID,
}
if err := s.storeRepo.CreateStore(&store); err != nil {
return nil, fmt.Errorf("failed to create store: %w", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(store.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(store.UpdatedAt)
storeResponseDTO := &dto.ResponseStoreDTO{
ID: store.ID,
UserID: store.UserID,
StoreName: store.StoreName,
StoreLogo: store.StoreLogo,
StoreBanner: store.StoreBanner,
StoreInfo: store.StoreInfo,
StoreAddressID: store.StoreAddressID,
TotalProduct: store.TotalProduct,
Followers: store.Followers,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return storeResponseDTO, nil
}
func (s *storeService) GetStoreByUserID(userID string) (*dto.ResponseStoreDTO, error) {
store, err := s.storeRepo.FindStoreByUserID(userID)
if err != nil {
return nil, fmt.Errorf("error retrieving store by user ID: %w", err)
}
if store == nil {
return nil, fmt.Errorf("store not found for user ID: %s", userID)
}
createdAt, err := utils.FormatDateToIndonesianFormat(store.CreatedAt)
if err != nil {
return nil, fmt.Errorf("failed to format createdAt: %w", err)
}
updatedAt, err := utils.FormatDateToIndonesianFormat(store.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("failed to format updatedAt: %w", err)
}
storeResponseDTO := &dto.ResponseStoreDTO{
ID: store.ID,
UserID: store.UserID,
StoreName: store.StoreName,
StoreLogo: store.StoreLogo,
StoreBanner: store.StoreBanner,
StoreInfo: store.StoreInfo,
StoreAddressID: store.StoreAddressID,
TotalProduct: store.TotalProduct,
Followers: store.Followers,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return storeResponseDTO, nil
}
func (s *storeService) UpdateStore(storeID string, storeDTO *dto.RequestStoreDTO, storeLogo, storeBanner *multipart.FileHeader, userID string) (*dto.ResponseStoreDTO, error) {
store, err := s.storeRepo.FindStoreByID(storeID)
if err != nil {
return nil, fmt.Errorf("error retrieving store by ID: %w", err)
}
if store == nil {
return nil, fmt.Errorf("store not found")
}
if storeDTO.StoreAddressID == "" {
return nil, fmt.Errorf("store address ID cannot be empty")
}
address, err := s.storeRepo.FindAddressByID(storeDTO.StoreAddressID)
if err != nil {
return nil, fmt.Errorf("error validating store address ID: %w", err)
}
if address == nil {
return nil, fmt.Errorf("store address ID not found")
}
if storeLogo != nil {
if err := s.deleteStoreImage(store.StoreLogo); err != nil {
return nil, fmt.Errorf("failed to delete old store logo: %w", err)
}
storeLogoPath, err := s.saveStoreImage(storeLogo, "logo")
if err != nil {
return nil, fmt.Errorf("failed to save store logo: %w", err)
}
store.StoreLogo = storeLogoPath
}
if storeBanner != nil {
if err := s.deleteStoreImage(store.StoreBanner); err != nil {
return nil, fmt.Errorf("failed to delete old store banner: %w", err)
}
storeBannerPath, err := s.saveStoreImage(storeBanner, "banner")
if err != nil {
return nil, fmt.Errorf("failed to save store banner: %w", err)
}
store.StoreBanner = storeBannerPath
}
store.StoreName = storeDTO.StoreName
store.StoreInfo = storeDTO.StoreInfo
store.StoreAddressID = storeDTO.StoreAddressID
if err := s.storeRepo.UpdateStore(store); err != nil {
return nil, fmt.Errorf("failed to update store: %w", err)
}
createdAt, err := utils.FormatDateToIndonesianFormat(store.CreatedAt)
if err != nil {
return nil, fmt.Errorf("failed to format createdAt: %w", err)
}
updatedAt, err := utils.FormatDateToIndonesianFormat(store.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("failed to format updatedAt: %w", err)
}
storeResponseDTO := &dto.ResponseStoreDTO{
ID: store.ID,
UserID: store.UserID,
StoreName: store.StoreName,
StoreLogo: store.StoreLogo,
StoreBanner: store.StoreBanner,
StoreInfo: store.StoreInfo,
StoreAddressID: store.StoreAddressID,
TotalProduct: store.TotalProduct,
Followers: store.Followers,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return storeResponseDTO, nil
}
func (s *storeService) DeleteStore(storeID string) error {
store, err := s.storeRepo.FindStoreByID(storeID)
if err != nil {
return fmt.Errorf("error retrieving store by ID: %w", err)
}
if store == nil {
return fmt.Errorf("store not found")
}
if store.StoreLogo != "" {
if err := s.deleteStoreImage(store.StoreLogo); err != nil {
return fmt.Errorf("failed to delete store logo: %w", err)
}
}
if store.StoreBanner != "" {
if err := s.deleteStoreImage(store.StoreBanner); err != nil {
return fmt.Errorf("failed to delete store banner: %w", err)
}
}
if err := s.storeRepo.DeleteStore(storeID); err != nil {
return fmt.Errorf("failed to delete store: %w", err)
}
return nil
}
func (s *storeService) saveStoreImage(file *multipart.FileHeader, imageType string) (string, error) {
imageDir := fmt.Sprintf("./public%s/uploads/store/%s", os.Getenv("BASE_URL"), imageType)
if _, err := os.Stat(imageDir); os.IsNotExist(err) {
if err := os.MkdirAll(imageDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for %s image: %v", imageType, err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
extension := filepath.Ext(file.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed for %s", imageType)
}
fileName := fmt.Sprintf("%s_%s%s", imageType, uuid.New().String(), extension)
filePath := filepath.Join(imageDir, fileName)
fileData, err := file.Open()
if err != nil {
return "", fmt.Errorf("failed to open file: %w", err)
}
defer fileData.Close()
outFile, err := os.Create(filePath)
if err != nil {
return "", fmt.Errorf("failed to create %s image file: %v", imageType, err)
}
defer outFile.Close()
if _, err := outFile.ReadFrom(fileData); err != nil {
return "", fmt.Errorf("failed to save %s image: %v", imageType, err)
}
return filepath.Join("/uploads/store", imageType, fileName), nil
}
func (s *storeService) deleteStoreImage(imagePath string) error {
if imagePath == "" {
return nil
}
filePath := filepath.Join("./public", imagePath)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil
}
err := os.Remove(filePath)
if err != nil {
return fmt.Errorf("failed to delete file at %s: %w", filePath, err)
}
return nil
}

View File

@ -1,631 +0,0 @@
package services
import (
"fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"strconv"
"time"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"github.com/google/uuid"
)
type TrashService interface {
CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error)
AddDetailToCategory(request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error)
GetCategories() ([]dto.ResponseTrashCategoryDTO, error)
GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO, error)
GetTrashDetailByID(id string) (*dto.ResponseTrashDetailDTO, error)
UpdateCategory(id string, request dto.RequestTrashCategoryDTO, iconPath *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error)
UpdateDetail(id string, request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error)
DeleteCategory(id string) error
DeleteDetail(id string) error
}
type trashService struct {
TrashRepo repositories.TrashRepository
}
func NewTrashService(trashRepo repositories.TrashRepository) TrashService {
return &trashService{TrashRepo: trashRepo}
}
func (s *trashService) saveIconOfTrash(iconTrash *multipart.FileHeader) (string, error) {
pathImage := "/uploads/icontrash/"
iconTrashDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(iconTrashDir); os.IsNotExist(err) {
if err := os.MkdirAll(iconTrashDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for icon trash: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true}
extension := filepath.Ext(iconTrash.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
iconTrashFIleName := fmt.Sprintf("%s_icontrash%s", uuid.New().String(), extension)
iconTrashPath := filepath.Join(iconTrashDir, iconTrashFIleName)
src, err := iconTrash.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(iconTrashPath)
if err != nil {
return "", fmt.Errorf("failed to create icon trash file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save icon trash: %v", err)
}
iconTrashUrl := fmt.Sprintf("%s%s", pathImage, iconTrashFIleName)
return iconTrashUrl, nil
}
func deleteIconTrashFIle(imagePath string) error {
if imagePath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + imagePath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete image: %v", err)
}
log.Printf("Image deleted successfully: %s", absolutePath)
return nil
}
func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) {
parsedPrice, err := strconv.ParseFloat(request.EstimatedPrice, 64)
fmt.Println("Received estimatedprice:", request.EstimatedPrice)
if err != nil {
return nil, fmt.Errorf("gagal memvalidasi harga: %v", err)
} else {
fmt.Printf("hasil parsing%v", parsedPrice)
}
icontrashPath, err := s.saveIconOfTrash(iconTrash)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan ikon sampah: %v", err)
}
category := model.TrashCategory{
Name: request.Name,
EstimatedPrice: parsedPrice,
Icon: icontrashPath,
}
if err := s.TrashRepo.CreateCategory(&category); err != nil {
return nil, fmt.Errorf("failed to create category: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID,
Name: category.Name,
EstimatedPrice: float64(category.EstimatedPrice),
Icon: category.Icon,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, nil, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching category: %v", err)
}
categories, err := s.TrashRepo.GetCategories()
if err == nil {
var categoriesDTO []dto.ResponseTrashCategoryDTO
for _, c := range categories {
ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt)
cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt)
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: c.ID,
Name: c.Name,
EstimatedPrice: float64(c.EstimatedPrice),
Icon: c.Icon,
CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt,
})
}
if err := s.CacheCategoryList(categoriesDTO, time.Hour*6); err != nil {
fmt.Printf("Error caching all categories: %v\n", err)
}
}
return categoryResponseDTO, nil
}
func (s *trashService) AddDetailToCategory(request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error) {
errors, valid := request.ValidateTrashDetailInput()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
detail := model.TrashDetail{
CategoryID: request.CategoryID,
Description: request.Description,
Price: request.Price,
}
if err := s.TrashRepo.AddDetailToCategory(&detail); err != nil {
return nil, fmt.Errorf("failed to add detail to category: %v", err)
}
dcreatedAt, _ := utils.FormatDateToIndonesianFormat(detail.CreatedAt)
dupdatedAt, _ := utils.FormatDateToIndonesianFormat(detail.UpdatedAt)
detailResponseDTO := &dto.ResponseTrashDetailDTO{
ID: detail.ID,
CategoryID: detail.CategoryID,
Description: detail.Description,
Price: detail.Price,
CreatedAt: dcreatedAt,
UpdatedAt: dupdatedAt,
}
cacheKey := fmt.Sprintf("detail:%s", detail.ID)
cacheData := map[string]interface{}{
"data": detailResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching detail: %v", err)
}
category, err := s.TrashRepo.GetCategoryByID(detail.CategoryID)
if err == nil {
ccreatedAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
cupdatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID,
Name: category.Name,
Icon: category.Icon,
CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt,
}
if err := s.CacheCategoryAndDetails(detail.CategoryID, categoryResponseDTO, category.Details, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching updated category: %v", err)
}
} else {
return nil, fmt.Errorf("error fetching category for cache update: %v", err)
}
return detailResponseDTO, nil
}
func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) {
cacheKey := "categories:all"
cachedCategories, err := utils.GetJSONData(cacheKey)
if err == nil && cachedCategories != nil {
var categoriesDTO []dto.ResponseTrashCategoryDTO
for _, category := range cachedCategories["data"].([]interface{}) {
categoryData := category.(map[string]interface{})
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: categoryData["id"].(string),
Name: categoryData["name"].(string),
EstimatedPrice: categoryData["estimatedprice"].(float64),
Icon: categoryData["icon"].(string),
CreatedAt: categoryData["createdAt"].(string),
UpdatedAt: categoryData["updatedAt"].(string),
})
}
return categoriesDTO, nil
}
categories, err := s.TrashRepo.GetCategories()
if err != nil {
return nil, fmt.Errorf("failed to fetch categories: %v", err)
}
var categoriesDTO []dto.ResponseTrashCategoryDTO
for _, category := range categories {
createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: category.ID,
Name: category.Name,
EstimatedPrice: category.EstimatedPrice,
Icon: category.Icon,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": categoriesDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*6); err != nil {
fmt.Printf("Error caching categories: %v\n", err)
}
return categoriesDTO, nil
}
func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO, error) {
cacheKey := fmt.Sprintf("category:%s", id)
cachedCategory, err := utils.GetJSONData(cacheKey)
if err == nil && cachedCategory != nil {
categoryData := cachedCategory["data"].(map[string]interface{})
details := mapDetails(cachedCategory["details"])
return &dto.ResponseTrashCategoryDTO{
ID: categoryData["id"].(string),
Name: categoryData["name"].(string),
EstimatedPrice: categoryData["estimatedprice"].(float64),
Icon: categoryData["icon"].(string),
CreatedAt: categoryData["createdAt"].(string),
UpdatedAt: categoryData["updatedAt"].(string),
Details: details,
}, nil
}
category, err := s.TrashRepo.GetCategoryByID(id)
if err != nil {
return nil, fmt.Errorf("category not found: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID,
Name: category.Name,
EstimatedPrice: category.EstimatedPrice,
Icon: category.Icon,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
if category.Details != nil {
var detailsDTO []dto.ResponseTrashDetailDTO
for _, detail := range category.Details {
createdAt, _ := utils.FormatDateToIndonesianFormat(detail.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(detail.UpdatedAt)
detailsDTO = append(detailsDTO, dto.ResponseTrashDetailDTO{
ID: detail.ID,
CategoryID: detail.CategoryID,
Description: detail.Description,
Price: detail.Price,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
categoryDTO.Details = detailsDTO
}
if err := s.CacheCategoryAndDetails(category.ID, categoryDTO, categoryDTO.Details, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching category and details: %v", err)
}
return categoryDTO, nil
}
func (s *trashService) GetTrashDetailByID(id string) (*dto.ResponseTrashDetailDTO, error) {
cacheKey := fmt.Sprintf("detail:%s", id)
cachedDetail, err := utils.GetJSONData(cacheKey)
if err == nil && cachedDetail != nil {
detailData := cachedDetail["data"].(map[string]interface{})
return &dto.ResponseTrashDetailDTO{
ID: detailData["id"].(string),
CategoryID: detailData["category_id"].(string),
Description: detailData["description"].(string),
Price: detailData["price"].(float64),
CreatedAt: detailData["createdAt"].(string),
UpdatedAt: detailData["updatedAt"].(string),
}, nil
}
detail, err := s.TrashRepo.GetTrashDetailByID(id)
if err != nil {
return nil, fmt.Errorf("trash detail not found: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(detail.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(detail.UpdatedAt)
detailDTO := &dto.ResponseTrashDetailDTO{
ID: detail.ID,
CategoryID: detail.CategoryID,
Description: detail.Description,
Price: detail.Price,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": detailDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
return nil, fmt.Errorf("error caching detail: %v", err)
}
return detailDTO, nil
}
func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategoryDTO, iconPath *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) {
errors, valid := request.ValidateTrashCategoryInput()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
category, err := s.TrashRepo.GetCategoryByID(id)
if err != nil {
return nil, fmt.Errorf("category not found: %v", err)
}
if category.Icon != "" {
err := deleteIconTrashFIle(category.Icon)
if err != nil {
return nil, fmt.Errorf("failed to delete old image: %v", err)
}
}
var iconTrashPath string
if iconPath != nil {
iconTrashPath, err = s.saveIconOfTrash(iconPath)
if err != nil {
return nil, fmt.Errorf("failed to save card photo: %v", err)
}
}
if iconTrashPath != "" {
category.Icon = iconTrashPath
}
category, err = s.TrashRepo.UpdateCategory(id, category)
if err != nil {
log.Printf("Error updating trash category: %v", err)
return nil, fmt.Errorf("failed to update category: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID,
Name: category.Name,
EstimatedPrice: category.EstimatedPrice,
Icon: category.Icon,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, category.Details, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching updated category: %v", err)
}
allCategories, err := s.TrashRepo.GetCategories()
if err == nil {
var categoriesDTO []dto.ResponseTrashCategoryDTO
for _, c := range allCategories {
ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt)
cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt)
categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{
ID: c.ID,
Name: c.Name,
EstimatedPrice: c.EstimatedPrice,
Icon: c.Icon,
CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt,
})
}
if err := s.CacheCategoryList(categoriesDTO, time.Hour*6); err != nil {
fmt.Printf("Error caching all categories: %v\n", err)
}
}
return categoryResponseDTO, nil
}
func (s *trashService) UpdateDetail(id string, request dto.RequestTrashDetailDTO) (*dto.ResponseTrashDetailDTO, error) {
errors, valid := request.ValidateTrashDetailInput()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
if err := s.TrashRepo.UpdateTrashDetail(id, request.Description, request.Price); err != nil {
return nil, fmt.Errorf("failed to update detail: %v", err)
}
detail, err := s.TrashRepo.GetTrashDetailByID(id)
if err != nil {
return nil, fmt.Errorf("trash detail not found: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(detail.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(detail.UpdatedAt)
detailResponseDTO := &dto.ResponseTrashDetailDTO{
ID: detail.ID,
CategoryID: detail.CategoryID,
Description: detail.Description,
Price: detail.Price,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("detail:%s", detail.ID)
cacheData := map[string]interface{}{
"data": detailResponseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching updated detail: %v", err)
}
category, err := s.TrashRepo.GetCategoryByID(detail.CategoryID)
if err == nil {
ccreatedAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt)
cupdatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt)
categoryResponseDTO := &dto.ResponseTrashCategoryDTO{
ID: category.ID,
Name: category.Name,
Icon: category.Icon,
CreatedAt: ccreatedAt,
UpdatedAt: cupdatedAt,
}
if err := s.CacheCategoryAndDetails(detail.CategoryID, categoryResponseDTO, category.Details, time.Hour*6); err != nil {
return nil, fmt.Errorf("error caching updated category: %v", err)
}
} else {
fmt.Printf("Error fetching category for cache update: %v\n", err)
}
return detailResponseDTO, nil
}
func (s *trashService) DeleteCategory(id string) error {
detailsCacheKeyPrefix := "detail:"
details, err := s.TrashRepo.GetDetailsByCategoryID(id)
if err != nil {
return fmt.Errorf("failed to fetch details for category %s: %v", id, err)
}
for _, detail := range details {
detailCacheKey := detailsCacheKeyPrefix + detail.ID
if err := s.deleteCache(detailCacheKey); err != nil {
return fmt.Errorf("error clearing cache for deleted detail: %v", err)
}
}
category, err := s.TrashRepo.GetCategoryByID(id)
if err != nil {
return fmt.Errorf("failed to fetch category for deletion: %v", err)
}
if err := deleteIconTrashFIle(category.Icon); err != nil {
return fmt.Errorf("error deleting icon for category %s: %v", id, err)
}
if err := s.TrashRepo.DeleteCategory(id); err != nil {
return fmt.Errorf("failed to delete category: %v", err)
}
if err := s.deleteCache("category:" + id); err != nil {
return fmt.Errorf("error clearing cache for deleted category: %v", err)
}
if err := s.deleteCache("categories:all"); err != nil {
return fmt.Errorf("error clearing categories list cache: %v", err)
}
return nil
}
func (s *trashService) DeleteDetail(id string) error {
detail, err := s.TrashRepo.GetTrashDetailByID(id)
if err != nil {
return fmt.Errorf("trash detail not found: %v", err)
}
if err := s.TrashRepo.DeleteTrashDetail(id); err != nil {
return fmt.Errorf("failed to delete detail: %v", err)
}
detailCacheKey := fmt.Sprintf("detail:%s", id)
if err := s.deleteCache(detailCacheKey); err != nil {
return fmt.Errorf("error clearing cache for deleted detail: %v", err)
}
categoryCacheKey := fmt.Sprintf("category:%s", detail.CategoryID)
if err := s.deleteCache(categoryCacheKey); err != nil {
return fmt.Errorf("error clearing cache for category after detail deletion: %v", err)
}
return nil
}
func mapDetails(details interface{}) []dto.ResponseTrashDetailDTO {
var detailsDTO []dto.ResponseTrashDetailDTO
if details != nil {
for _, detail := range details.([]interface{}) {
detailData := detail.(map[string]interface{})
detailsDTO = append(detailsDTO, dto.ResponseTrashDetailDTO{
ID: detailData["id"].(string),
CategoryID: detailData["category_id"].(string),
Description: detailData["description"].(string),
Price: detailData["price"].(float64),
CreatedAt: detailData["createdAt"].(string),
UpdatedAt: detailData["updatedAt"].(string),
})
}
}
return detailsDTO
}
func (s *trashService) CacheCategoryAndDetails(categoryID string, categoryData interface{}, detailsData interface{}, expiration time.Duration) error {
cacheKey := fmt.Sprintf("category:%s", categoryID)
cacheData := map[string]interface{}{
"data": categoryData,
"details": detailsData,
}
err := utils.SetJSONData(cacheKey, cacheData, expiration)
if err != nil {
return fmt.Errorf("error caching category and details: %v", err)
}
return nil
}
func (s *trashService) CacheCategoryList(categoriesData interface{}, expiration time.Duration) error {
cacheKey := "categories:all"
cacheData := map[string]interface{}{
"data": categoriesData,
}
err := utils.SetJSONData(cacheKey, cacheData, expiration)
if err != nil {
return fmt.Errorf("error caching categories list: %v", err)
}
return nil
}
func (s *trashService) deleteCache(cacheKey string) error {
if err := utils.DeleteData(cacheKey); err != nil {
fmt.Printf("Error clearing cache for key: %v\n", cacheKey)
return fmt.Errorf("error clearing cache for key %s: %v", cacheKey, err)
}
fmt.Printf("Deleted cache for key: %s\n", cacheKey)
return nil
}

View File

@ -1,154 +0,0 @@
package services
// import (
// "log"
// "time"
// "rijig/dto"
// "rijig/internal/repositories"
// "rijig/model"
// "github.com/google/uuid"
// )
// type CartService struct {
// Repo repositories.CartRepository
// }
// func NewCartService(repo repositories.CartRepository) *CartService {
// return &CartService{Repo: repo}
// }
// func (s *CartService) CommitCartToDatabase(userID string) error {
// items, err := GetCartItems(userID)
// if err != nil || len(items) == 0 {
// log.Printf("No items to commit for user: %s", userID)
// return err
// }
// var cartItems []model.CartItem
// var totalAmount float32
// var estimatedTotal float32
// for _, item := range items {
// trash, err := s.Repo.GetTrashCategoryByID(item.TrashCategoryID)
// if err != nil {
// log.Printf("Trash category not found for trashID: %s", item.TrashCategoryID)
// continue
// }
// subTotal := float32(trash.EstimatedPrice) * item.Amount
// totalAmount += item.Amount
// estimatedTotal += subTotal
// cartItems = append(cartItems, model.CartItem{
// ID: uuid.NewString(),
// TrashCategoryID: item.TrashCategoryID,
// Amount: item.Amount,
// SubTotalEstimatedPrice: subTotal,
// })
// }
// cart := &model.Cart{
// ID: uuid.NewString(),
// UserID: userID,
// CartItems: cartItems,
// TotalAmount: totalAmount,
// EstimatedTotalPrice: estimatedTotal,
// CreatedAt: time.Now(),
// UpdatedAt: time.Now(),
// }
// if err := s.Repo.DeleteCartByUserID(userID); err != nil {
// log.Printf("Failed to delete old cart: %v", err)
// }
// if err := s.Repo.CreateCart(cart); err != nil {
// log.Printf("Failed to create cart: %v", err)
// return err
// }
// if err := ClearCart(userID); err != nil {
// log.Printf("Failed to clear Redis cart: %v", err)
// }
// log.Printf("Cart committed successfully for user: %s", userID)
// return nil
// }
// func (s *CartService) GetCartFromRedis(userID string) (*dto.CartResponse, error) {
// items, err := GetCartItems(userID)
// if err != nil || len(items) == 0 {
// return nil, err
// }
// var totalAmount float32
// var estimatedTotal float32
// var cartItemDTOs []dto.CartItemResponse
// for _, item := range items {
// trash, err := s.Repo.GetTrashCategoryByID(item.TrashCategoryID)
// if err != nil {
// continue
// }
// subtotal := float32(trash.EstimatedPrice) * item.Amount
// totalAmount += item.Amount
// estimatedTotal += subtotal
// cartItemDTOs = append(cartItemDTOs, dto.CartItemResponse{
// TrashId: trash.ID,
// TrashIcon: trash.Icon,
// TrashName: trash.Name,
// Amount: item.Amount,
// EstimatedSubTotalPrice: subtotal,
// })
// }
// resp := &dto.CartResponse{
// ID: "N/A",
// UserID: userID,
// TotalAmount: totalAmount,
// EstimatedTotalPrice: estimatedTotal,
// CreatedAt: time.Now().Format(time.RFC3339),
// UpdatedAt: time.Now().Format(time.RFC3339),
// CartItems: cartItemDTOs,
// }
// return resp, nil
// }
// func (s *CartService) GetCart(userID string) (*dto.CartResponse, error) {
// cartRedis, err := s.GetCartFromRedis(userID)
// if err == nil && len(cartRedis.CartItems) > 0 {
// return cartRedis, nil
// }
// cartDB, err := s.Repo.GetCartByUserID(userID)
// if err != nil {
// return nil, err
// }
// var items []dto.CartItemResponse
// for _, item := range cartDB.CartItems {
// items = append(items, dto.CartItemResponse{
// ItemId: item.ID,
// TrashId: item.TrashCategoryID,
// TrashIcon: item.TrashCategory.Icon,
// TrashName: item.TrashCategory.Name,
// Amount: item.Amount,
// EstimatedSubTotalPrice: item.SubTotalEstimatedPrice,
// })
// }
// resp := &dto.CartResponse{
// ID: cartDB.ID,
// UserID: cartDB.UserID,
// TotalAmount: cartDB.TotalAmount,
// EstimatedTotalPrice: cartDB.EstimatedTotalPrice,
// CreatedAt: cartDB.CreatedAt.Format(time.RFC3339),
// UpdatedAt: cartDB.UpdatedAt.Format(time.RFC3339),
// CartItems: items,
// }
// return resp, nil
// }

View File

@ -1,231 +0,0 @@
package services
import (
"fmt"
"log"
"mime/multipart"
"os"
"path/filepath"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
)
type UserService interface {
GetUserByID(userID string) (*dto.UserResponseDTO, error)
GetAllUsers(page, limit int) ([]dto.UserResponseDTO, error)
UpdateUser(userID string, request *dto.RequestUserDTO) (*dto.UserResponseDTO, error)
UpdateUserAvatar(userID string, avatar *multipart.FileHeader) (*dto.UserResponseDTO, error)
UpdateUserPassword(userID, oldPassword, newPassword, confirmNewPassword string) error
}
type userService struct {
userRepo repositories.UserProfilRepository
}
func NewUserService(userRepo repositories.UserProfilRepository) UserService {
return &userService{userRepo: userRepo}
}
func (s *userService) GetUserByID(userID string) (*dto.UserResponseDTO, error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("error retrieving user by ID: %v", err)
}
userDTO, err := s.formatUserResponse(user)
if err != nil {
return nil, fmt.Errorf("error formatting user response: %v", err)
}
return userDTO, nil
}
func (s *userService) GetAllUsers(page, limit int) ([]dto.UserResponseDTO, error) {
users, err := s.userRepo.FindAll(page, limit)
if err != nil {
return nil, fmt.Errorf("error retrieving all users: %v", err)
}
var userDTOs []dto.UserResponseDTO
for _, user := range users {
userDTO, err := s.formatUserResponse(&user)
if err != nil {
log.Printf("Error formatting user response for userID %s: %v", user.ID, err)
continue
}
userDTOs = append(userDTOs, *userDTO)
}
return userDTOs, nil
}
func (s *userService) UpdateUser(userID string, request *dto.RequestUserDTO) (*dto.UserResponseDTO, error) {
errors, valid := request.Validate()
if !valid {
return nil, fmt.Errorf("validation failed: %v", errors)
}
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("user not found: %v", err)
}
user.Name = request.Name
user.Phone = request.Phone
user.Email = request.Email
err = s.userRepo.Update(user)
if err != nil {
return nil, fmt.Errorf("error updating user: %v", err)
}
userDTO, err := s.formatUserResponse(user)
if err != nil {
return nil, fmt.Errorf("error formatting updated user response: %v", err)
}
return userDTO, nil
}
func (s *userService) UpdateUserAvatar(userID string, avatar *multipart.FileHeader) (*dto.UserResponseDTO, error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return nil, fmt.Errorf("user not found: %v", err)
}
if *user.Avatar != "" {
err := s.deleteAvatarImage(*user.Avatar)
if err != nil {
return nil, fmt.Errorf("failed to delete old image: %v", err)
}
}
avatarURL, err := s.saveAvatarImage(userID, avatar)
if err != nil {
return nil, fmt.Errorf("failed to save avatar image: %v", err)
}
err = s.userRepo.UpdateAvatar(userID, avatarURL)
if err != nil {
return nil, fmt.Errorf("failed to update avatar in the database: %v", err)
}
userDTO, err := s.formatUserResponse(user)
if err != nil {
return nil, fmt.Errorf("failed to format user response: %v", err)
}
return userDTO, nil
}
func (s *userService) UpdateUserPassword(userID, oldPassword, newPassword, confirmNewPassword string) error {
// errors, valid := utils.ValidatePasswordUpdate(oldPassword, newPassword, confirmNewPassword)
// if !valid {
// return fmt.Errorf("password validation error: %v", errors)
// }
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("user not found: %v", err)
}
if user.Password != oldPassword {
return fmt.Errorf("old password is incorrect")
}
err = s.userRepo.UpdatePassword(userID, newPassword)
if err != nil {
return fmt.Errorf("error updating password: %v", err)
}
return nil
}
func (s *userService) formatUserResponse(user *model.User) (*dto.UserResponseDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
userDTO := &dto.UserResponseDTO{
ID: user.ID,
Username: user.Name,
Avatar: user.Avatar,
Name: user.Name,
Phone: user.Phone,
Email: user.Email,
EmailVerified: user.PhoneVerified,
RoleName: user.Role.RoleName,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
return userDTO, nil
}
func (s *userService) saveAvatarImage(userID string, avatar *multipart.FileHeader) (string, error) {
pathImage := "/uploads/avatars/"
avatarDir := "./public" + os.Getenv("BASE_URL") + pathImage
if _, err := os.Stat(avatarDir); os.IsNotExist(err) {
if err := os.MkdirAll(avatarDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create directory for avatar: %v", err)
}
}
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
extension := filepath.Ext(avatar.Filename)
if !allowedExtensions[extension] {
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
}
avatarFileName := fmt.Sprintf("%s_avatar%s", userID, extension)
avatarPath := filepath.Join(avatarDir, avatarFileName)
src, err := avatar.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %v", err)
}
defer src.Close()
dst, err := os.Create(avatarPath)
if err != nil {
return "", fmt.Errorf("failed to create avatar file: %v", err)
}
defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil {
return "", fmt.Errorf("failed to save avatar: %v", err)
}
avatarURL := fmt.Sprintf("%s%s", pathImage, avatarFileName)
return avatarURL, nil
}
func (s *userService) deleteAvatarImage(avatarPath string) error {
if avatarPath == "" {
return nil
}
baseDir := "./public/" + os.Getenv("BASE_URL")
absolutePath := baseDir + avatarPath
if _, err := os.Stat(absolutePath); os.IsNotExist(err) {
return fmt.Errorf("image file not found: %v", err)
}
err := os.Remove(absolutePath)
if err != nil {
return fmt.Errorf("failed to delete avatar image: %v", err)
}
log.Printf("Avatar image deleted successfully: %s", absolutePath)
return nil
}

Some files were not shown because too many files have changed in this diff Show More