feat: setup docker
This commit is contained in:
parent
e7c0675f8a
commit
d8b43348b3
|
|
@ -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
|
||||||
|
|
@ -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
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
|
|
@ -14,17 +11,61 @@
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories
|
||||||
# vendor/
|
vendor/
|
||||||
|
|
||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
go.work.sum
|
go.work.sum
|
||||||
|
|
||||||
# env file
|
# Environment files - ignore all variations
|
||||||
.env
|
.env*
|
||||||
.env.prod
|
!.env.example
|
||||||
.env.dev
|
|
||||||
|
|
||||||
# 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/
|
/public/apirijig/v2/uploads/
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
/bin/
|
||||||
|
/build/
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
|
coverage.txt
|
||||||
|
coverage.html
|
||||||
|
*.cover
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
debug
|
||||||
|
*.pprof
|
||||||
|
|
||||||
|
# Local development files
|
||||||
|
*.local
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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
|
||||||
35
cmd/main.go
35
cmd/main.go
|
|
@ -1,13 +1,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"rijig/config"
|
"rijig/config"
|
||||||
"rijig/internal/repositories"
|
// "rijig/internal/repositories"
|
||||||
"rijig/internal/services"
|
// "rijig/internal/services"
|
||||||
"rijig/internal/worker"
|
|
||||||
"rijig/router"
|
"rijig/router"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
|
|
@ -15,21 +13,21 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.SetupConfig()
|
config.SetupConfig()
|
||||||
cartRepo := repositories.NewCartRepository()
|
// cartRepo := repositories.NewCartRepository()
|
||||||
trashRepo := repositories.NewTrashRepository(config.DB)
|
// trashRepo := repositories.NewTrashRepository(config.DB)
|
||||||
cartService := services.NewCartService(cartRepo, trashRepo)
|
// cartService := services.NewCartService(cartRepo, trashRepo)
|
||||||
worker := worker.NewCartWorker(cartService, cartRepo, trashRepo)
|
// worker := worker.NewCartWorker(cartService, cartRepo, trashRepo)
|
||||||
|
|
||||||
go func() {
|
// go func() {
|
||||||
ticker := time.NewTicker(30 * time.Second)
|
// ticker := time.NewTicker(30 * time.Second)
|
||||||
defer ticker.Stop()
|
// defer ticker.Stop()
|
||||||
|
|
||||||
for range ticker.C {
|
// for range ticker.C {
|
||||||
if err := worker.AutoCommitExpiringCarts(); err != nil {
|
// if err := worker.AutoCommitExpiringCarts(); err != nil {
|
||||||
log.Printf("Auto-commit error: %v", err)
|
// log.Printf("Auto-commit error: %v", err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}()
|
// }()
|
||||||
|
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
ErrorHandler: func(c *fiber.Ctx, err error) error {
|
ErrorHandler: func(c *fiber.Ctx, err error) error {
|
||||||
|
|
@ -45,7 +43,6 @@ func main() {
|
||||||
|
|
||||||
app.Use(cors.New())
|
app.Use(cors.New())
|
||||||
|
|
||||||
|
|
||||||
router.SetupRoutes(app)
|
router.SetupRoutes(app)
|
||||||
config.StartServer(app)
|
config.StartServer(app)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,29 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetupConfig() {
|
func SetupConfig() {
|
||||||
|
|
||||||
|
if _, exists := os.LookupEnv("DOCKER_ENV"); exists {
|
||||||
|
|
||||||
|
log.Println("Running in Docker container, using environment variables")
|
||||||
|
} else {
|
||||||
|
|
||||||
err := godotenv.Load(".env.dev")
|
err := godotenv.Load(".env.dev")
|
||||||
if err != nil {
|
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()
|
ConnectDatabase()
|
||||||
ConnectRedis()
|
ConnectRedis()
|
||||||
InitWhatsApp()
|
go func() {
|
||||||
|
InitWhatsApp() // Ini tidak akan blocking startup server
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"github.com/mdp/qrterminal/v3"
|
"github.com/mdp/qrterminal/v3"
|
||||||
|
|
@ -14,15 +16,64 @@ import (
|
||||||
"go.mau.fi/whatsmeow/proto/waE2E"
|
"go.mau.fi/whatsmeow/proto/waE2E"
|
||||||
"go.mau.fi/whatsmeow/store/sqlstore"
|
"go.mau.fi/whatsmeow/store/sqlstore"
|
||||||
"go.mau.fi/whatsmeow/types"
|
"go.mau.fi/whatsmeow/types"
|
||||||
|
"go.mau.fi/whatsmeow/types/events"
|
||||||
waLog "go.mau.fi/whatsmeow/util/log"
|
waLog "go.mau.fi/whatsmeow/util/log"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var WhatsAppClient *whatsmeow.Client
|
type WhatsAppManager struct {
|
||||||
var container *sqlstore.Container
|
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() {
|
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(
|
dsn := fmt.Sprintf(
|
||||||
"postgres://%s:%s@%s:%s/%s?sslmode=disable",
|
"postgres://%s:%s@%s:%s/%s?sslmode=disable",
|
||||||
|
|
@ -34,114 +85,291 @@ func InitWhatsApp() {
|
||||||
)
|
)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
container, err = sqlstore.New("postgres", dsn, dbLog)
|
w.container, err = sqlstore.New("postgres", dsn, dbLog)
|
||||||
if err != nil {
|
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")
|
||||||
if err != nil {
|
return nil
|
||||||
log.Fatalf("Failed to get WhatsApp device: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clientLog := waLog.Stdout("Client", "DEBUG", true)
|
func (w *WhatsAppManager) setupClient() error {
|
||||||
WhatsAppClient = whatsmeow.NewClient(deviceStore, clientLog)
|
deviceStore, err := w.container.GetFirstDevice()
|
||||||
|
|
||||||
if WhatsAppClient.Store.ID == nil {
|
|
||||||
fmt.Println("WhatsApp Client is not logged in, generating QR Code...")
|
|
||||||
|
|
||||||
qrChan, _ := WhatsAppClient.GetQRChannel(context.Background())
|
|
||||||
err = WhatsAppClient.Connect()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to connect WhatsApp client: %v", err)
|
return fmt.Errorf("failed to get device store: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for evt := range qrChan {
|
clientLog := waLog.Stdout("WhatsApp-Client", "ERROR", true)
|
||||||
if evt.Event == "code" {
|
w.Client = whatsmeow.NewClient(deviceStore, clientLog)
|
||||||
fmt.Println("QR Code untuk login:")
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WhatsAppManager) handleAuthentication() error {
|
||||||
|
if w.Client.Store.ID == nil {
|
||||||
|
log.Println("WhatsApp client not logged in, generating QR code...")
|
||||||
|
return w.authenticateWithQR()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
generateQRCode(evt.Code)
|
||||||
} else {
|
fmt.Println("Scan QR code di atas dengan WhatsApp Anda")
|
||||||
fmt.Println("Login event:", evt.Event)
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("WhatsApp client connected successfully!")
|
func (w *WhatsAppManager) connect() error {
|
||||||
go handleShutdown()
|
if err := w.Client.Connect(); err != nil {
|
||||||
|
return fmt.Errorf("failed to connect: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
func generateQRCode(qrString string) {
|
||||||
qrterminal.GenerateHalfBlock(qrString, qrterminal.M, os.Stdout)
|
qrterminal.GenerateHalfBlock(qrString, qrterminal.M, os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleShutdown() {
|
func (w *WhatsAppManager) handleShutdown() {
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
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...")
|
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 {
|
func SendWhatsAppMessage(phone, message string) error {
|
||||||
if WhatsAppClient == nil {
|
manager := GetWhatsAppManager()
|
||||||
|
|
||||||
|
if manager.Client == nil {
|
||||||
return fmt.Errorf("WhatsApp client is not initialized")
|
return fmt.Errorf("WhatsApp client is not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
targetJID, _ := types.ParseJID(phone + "@s.whatsapp.net")
|
if !manager.IsConnected() {
|
||||||
msg := waE2E.Message{
|
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),
|
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 {
|
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
|
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 {
|
func LogoutWhatsApp() error {
|
||||||
if WhatsAppClient == nil {
|
manager := GetWhatsAppManager()
|
||||||
|
|
||||||
|
if manager.Client == nil {
|
||||||
return fmt.Errorf("WhatsApp client is not initialized")
|
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 {
|
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()
|
// Disconnect
|
||||||
if err != nil {
|
manager.Client.Disconnect()
|
||||||
return fmt.Errorf("failed to close database connection: %v", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeWhatsAppDeviceFromContainer() error {
|
func (w *WhatsAppManager) removeDeviceFromStore() error {
|
||||||
deviceStore, err := container.GetFirstDevice()
|
deviceStore, err := w.container.GetFirstDevice()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get WhatsApp device: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if deviceStore != nil {
|
if deviceStore != nil && deviceStore.ID != nil {
|
||||||
err := deviceStore.Delete()
|
return deviceStore.Delete()
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to remove device from store: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -38,7 +38,7 @@ type ResponseRequestPickup struct {
|
||||||
type ResponseRequestPickupItem struct {
|
type ResponseRequestPickupItem struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
TrashCategoryID string `json:"trash_category_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"`
|
EstimatedAmount float64 `json:"estimated_amount,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,16 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"rijig/dto"
|
"rijig/dto"
|
||||||
"rijig/internal/services"
|
|
||||||
"rijig/utils"
|
"rijig/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AboutHandler struct {
|
type AboutHandler struct {
|
||||||
AboutService services.AboutService
|
AboutService AboutService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAboutHandler(aboutService services.AboutService) *AboutHandler {
|
func NewAboutHandler(aboutService AboutService) *AboutHandler {
|
||||||
return &AboutHandler{
|
return &AboutHandler{
|
||||||
AboutService: aboutService,
|
AboutService: aboutService,
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +35,7 @@ func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error {
|
||||||
return utils.BadRequest(c, "Cover image is required")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Error creating About: %v", err)
|
log.Printf("Error creating About: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to create 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")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Error updating About: %v", err)
|
log.Printf("Error updating About: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to update 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 {
|
func (h *AboutHandler) GetAllAbout(c *fiber.Ctx) error {
|
||||||
response, err := h.AboutService.GetAllAbout()
|
response, err := h.AboutService.GetAllAbout(c.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error fetching all About: %v", err)
|
log.Printf("Error fetching all About: %v", err)
|
||||||
return utils.InternalServerError(c, "Failed to fetch About list")
|
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 {
|
func (h *AboutHandler) GetAboutByID(c *fiber.Ctx) error {
|
||||||
id := c.Params("id")
|
id := c.Params("id")
|
||||||
|
|
||||||
response, err := h.AboutService.GetAboutByID(id)
|
response, err := h.AboutService.GetAboutByID(c.Context(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error fetching About by ID: %v", err)
|
log.Printf("Error fetching About by ID: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to fetch 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 {
|
func (h *AboutHandler) GetAboutDetailById(c *fiber.Ctx) error {
|
||||||
id := c.Params("id")
|
id := c.Params("id")
|
||||||
|
|
||||||
response, err := h.AboutService.GetAboutDetailById(id)
|
response, err := h.AboutService.GetAboutDetailById(c.Context(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error fetching About detail by ID: %v", err)
|
log.Printf("Error fetching About detail by ID: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to fetch About 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 {
|
func (h *AboutHandler) DeleteAbout(c *fiber.Ctx) error {
|
||||||
id := c.Params("id")
|
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)
|
log.Printf("Error deleting About: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to delete 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")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Error creating AboutDetail: %v", err)
|
log.Printf("Error creating AboutDetail: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to create 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")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Error updating AboutDetail: %v", err)
|
log.Printf("Error updating AboutDetail: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to update 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 {
|
func (h *AboutHandler) DeleteAboutDetail(c *fiber.Ctx) error {
|
||||||
id := c.Params("id")
|
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)
|
log.Printf("Error deleting AboutDetail: %v", err)
|
||||||
return utils.InternalServerError(c, fmt.Sprintf("Failed to delete AboutDetail: %v", err))
|
return utils.InternalServerError(c, fmt.Sprintf("Failed to delete AboutDetail: %v", err))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,32 @@
|
||||||
package about
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,113 @@
|
||||||
package address
|
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")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,24 @@
|
||||||
package address
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,6 @@ package article
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"rijig/config"
|
"rijig/config"
|
||||||
"rijig/internal/handler"
|
|
||||||
"rijig/internal/repositories"
|
|
||||||
"rijig/internal/services"
|
|
||||||
"rijig/middleware"
|
"rijig/middleware"
|
||||||
"rijig/utils"
|
"rijig/utils"
|
||||||
|
|
||||||
|
|
@ -12,9 +9,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func ArticleRouter(api fiber.Router) {
|
func ArticleRouter(api fiber.Router) {
|
||||||
articleRepo := repositories.NewArticleRepository(config.DB)
|
articleRepo := NewArticleRepository(config.DB)
|
||||||
articleService := services.NewArticleService(articleRepo)
|
articleService := NewArticleService(articleRepo)
|
||||||
articleHandler := handler.NewArticleHandler(articleService)
|
articleHandler := NewArticleHandler(articleService)
|
||||||
|
|
||||||
articleAPI := api.Group("/article")
|
articleAPI := api.Group("/article")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1,49 @@
|
||||||
package cart
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,162 @@
|
||||||
package cart
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,356 @@
|
||||||
package cart
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,252 @@
|
||||||
package collector
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,360 @@
|
||||||
package collector
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,337 @@
|
||||||
package collector
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,349 @@
|
||||||
package collector
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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")
|
|
||||||
// }
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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(®ency).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(®encies).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
err := r.DB.Find(®encies).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(®ency).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 ®ency, 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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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))
|
|
||||||
// }
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
// }
|
|
||||||
|
|
@ -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
Loading…
Reference in New Issue