Skip to content

AditShh-git/delivery-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

21 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🚚 Delivery Platform β€” Multi-Tenant Logistics Backend

A production-style multi-tenant logistics backend built with Spring Boot, PostgreSQL, Redis, and Docker.
Not a tutorial project. Built to solve real delivery problems.


πŸ’‘ Why I Built This

I've personally faced three frustrating problems as a customer:

  1. Too many apps β€” food delivery, courier, electronics returns all use different platforms. Why can't one backend handle all of them?
  2. The introvert problem β€” when an order is out for delivery, the rider randomly calls. Some customers don't pick up because they're anxious about unexpected calls. The order fails β€” not because the customer was unavailable, but because they weren't prepared. Scheduled slots fix this.
  3. False no-show claims β€” riders sometimes mark an order as attempted when they never showed up. Customers get charged. There's no proof either way. OTP verification and GPS-photo proof solve this.

This platform is my attempt to solve all three in one system.


πŸ— Architecture Overview

Controller β†’ Service β†’ Repository
     ↓
Policy Enforcement Layer (CompanyPolicy per product + delivery type)
     ↓
State Machine Lifecycle (10 statuses, enforced transitions)
     ↓
Domain Rule Layer (OTP guard β€” platform invariant, not configurable)
     ↓
Capacity-Safe Rider Dispatch (zone match, duty check, concurrency limit)
     ↓
Scheduler Layer (SLA automation, confirmation flow, auto-cancel)
     ↓
Analytics + Caching Layer (Redis, native SQL, projections)

Controllers are thin HTTP adapters β€” all business logic lives in service classes.
OTP enforcement is a platform-level invariant: no configuration can bypass it.


βœ… Current Status

Week Focus Status
1–2 Foundation β€” JWT, roles, entities, migrations βœ… Done
3 Order Engine β€” state machine, slots, WhatsApp, SLA βœ… Done
4 Analytics β€” dashboards, Redis caching, projections βœ… Done
5 SLA Automation β€” scheduler, trend API, zone heatmap βœ… Done
5+ Clean Architecture β€” thin controllers, refactored services βœ… Done
6 Delivery Verification β€” OTP system + platform-mandatory enforcement βœ… Done
7 Photo Proof Enforcement β€” GPS + photo on every FAILED attempt βœ… Done
8 Auto-Dispatch + RunSheet πŸ”œ Planned
9–10 Intelligence β€” incentive engine, confidence scoring πŸ”œ Future
11–12 Scale β€” partner API, webhooks, rate limiting, K8s πŸ”œ Future

πŸ”‘ Key Features

Auth & Roles

  • JWT authentication with BCrypt password hashing
  • 4 roles: ADMIN, COMPANY, RIDER, CUSTOMER
  • Role-scoped data isolation β€” COMPANY sees only its own data
  • @PreAuthorize on all endpoints, custom 401/403 handlers

4 Delivery Models

Model SLA Deadline Slot Required Dispatch Style
INSTANT 30 minutes No Zone-based, auto-dispatched
PARCEL 48 hours Date only WhatsApp-confirmed
SCHEDULED 24 hours Date + time window WhatsApp-confirmed
PICKUP_RETURN 48 hours No WhatsApp-confirmed

🏎 INSTANT (Zone-Based Dispatch)

For time-critical deliveries like food or same-hour courier. The order is assigned to the nearest available rider in the same zone and must be delivered within 30 minutes. No customer confirmation step β€” the order goes live immediately after creation.

πŸ“¦ PARCEL & πŸ”„ PICKUP_RETURN (WhatsApp-Driven)

These models use a two-step WhatsApp confirmation flow before dispatch:

Step 1 β€” 6AM Booking Confirmation:
At 6AM on the delivery date, ConfirmationScheduler sets the order to CONFIRMATION_PENDING and simulates sending a WhatsApp message to the customer:

"Your order is scheduled for delivery today. Please choose:
1️⃣ CONFIRM β€” Yes, deliver today
2️⃣ CANCEL β€” Cancel this order"

  • CONFIRM β†’ Customer is prompted to pick a time slot next
  • CANCEL β†’ Order cancelled immediately

Step 2 β€” Slot Selection (after CONFIRM):
Once the customer confirms, they must choose a delivery window:

"Please select time slot:
1️⃣ SLOT_9_12 β€” 9AM to 12PM
2️⃣ SLOT_12_3 β€” 12PM to 3PM
3️⃣ SLOT_3_6 β€” 3PM to 6PM"

  • Slot availability is checked against SlotCapacity (company + zone + date)
  • On success β†’ order moves to CONFIRMED, slot bookedCount incremented

If No Response β€” Auto-Retry & Auto-Reschedule:

  • After 12 hours with no reply β†’ a same-day reminder is sent
  • After a full day ignored β†’ order is automatically rescheduled to the next day (slotDate + 1)
  • After 3 consecutive ignored days β†’ order is auto-cancelled (autoCancelled = true)

Order State Machine

CREATED β†’ CONFIRMATION_PENDING β†’ CONFIRMED β†’ ASSIGNED β†’ IN_TRANSIT
                                                              ↓
                                    DELIVERED ← (OTP verified) ← IN_TRANSIT
                                    COLLECTED ← (OTP verified) ← IN_TRANSIT
                                    FAILED β†’ (GPS + photo required) β†’ retry
                                    DISPUTED β†’ (OPEN_BOX only)

Invalid transitions are blocked at the service layer. An order cannot jump to ASSIGNED before CONFIRMED. This keeps rider analytics and KPI dashboards accurate.

πŸ” OTP Delivery Verification

Every DELIVERED and COLLECTED transition requires a verified OTP β€” with no config flag to skip it.

Flow:

  1. Rider calls POST /api/orders/{id}/otp/send β†’ 6-digit OTP generated, BCrypt-hashed, stored
  2. Customer shares the OTP with the rider
  3. Rider calls POST /api/orders/{id}/otp/verify with the raw OTP β†’ BCrypt compared
  4. Only after verified β†’ PATCH /api/orders/{id}/status with DELIVERED/COLLECTED is accepted

Guard implementation:

  • ensureOtpVerified() is a private domain-rule method called before order.setStatus() β€” the transition is atomic
  • 3 wrong attempts β†’ OTP permanently locked; rider must re-send
  • @Transactional(noRollbackFor = ApiException.class) on verifyOtp ensures wrong_attempts increments persist even when the exception is thrown
  • OTP hash stored in delivery_otps table; verified_at recorded as audit trail

This eliminates the "fake delivery" problem β€” no completion without proof.

πŸ“ GPS + Photo Proof on Every Failed Attempt

  • Every FAILED status update requires latitude, longitude, and photoUrl
  • Stored in attempt_history alongside failure_reason, rider_id, and attempt_number
  • Prevents false no-show claims β€” every failed delivery is GPS-verified and photo-evidenced

Smart Risk Rule

  • On order creation, if a customer had β‰₯ 2 failed deliveries in the last 30 days, the order is immediately placed in CONFIRMATION_PENDING
  • The ConfirmationScheduler picks it up and re-sends confirmation β€” no silent dispatch for high-risk customers

SLA Automation

  • SlaBreachScheduler runs every 5 minutes via @Scheduled
  • Finds orders past their SLA deadline with slaBreached = false
  • Auto-flags them and evicts the Redis dashboard cache

Analytics Dashboards

  • Admin KPI β€” 9 executive metrics: total orders, delivered, failed, success rate, SLA breached, active riders, companies
  • Company KPI β€” same metrics, isolated per company (multi-tenant)
  • Rider Analytics β€” success rate, failure grouped by reason, zone + date filtered
  • Rider Ranking β€” sorted by success rate
  • Order Trend API β€” GET /api/admin/reports/order-trend?interval=DAY|WEEK|MONTH
  • Zone Heatmap β€” GET /api/admin/reports/zone-heatmap β€” which zones are failing, which are overloaded

All dashboards use native SQL aggregation + typed Spring projections (no Object[] raw queries).

Redis Caching

  • @Cacheable on admin and company dashboards
  • @CacheEvict on every order create, status update, and rider assignment
  • All cache names registered in CacheConfig to prevent Cannot find cache named errors

WhatsApp Webhook Engine

  • Inbound replies handled by WhatsAppWebhookService (interface + WhatsAppWebhookServiceImpl)
  • Webhook endpoint: POST /api/webhook/whatsapp?orderId={id}&action={action} β€” secured with X-Webhook-Secret header
  • Supported actions: CONFIRM β†’ slot prompt, SLOT_9_12 / SLOT_12_3 / SLOT_3_6 β†’ book slot, CANCEL β†’ cancel order
  • ConfirmationScheduler fires at 6AM daily to send booking confirmations; retries every hour; auto-reschedules after 1 ignored day; auto-cancels after 3 ignored days
  • All outbound messaging is currently logged (real WhatsApp API integration is wired in as the next step)

Slot Capacity System

  • Per-date, per-window slot capacity management via SlotCapacity entity
  • SlotController exposes slots management for admins
  • Capacity enforced at order confirmation time

Zone Enforcement

  • Riders can only be assigned to orders in their zone (hard constraint)
  • Null-safe zone check in assignRider() β€” orders without a zone skip enforcement
  • Zone heatmap shows failure rates and order volume per zone
  • DB indexes on zone, status, SLA breach for fast analytics queries

Company Onboarding

  • Companies onboard with full policy setup per product + delivery type
  • Email-based user resolution for company linking
  • Company status tracking (active/inactive)

πŸ—„ Database

  • 22 Flyway migrations β€” full schema versioning from V1 to V22
  • Indexes on: zone, status, sla_breached, company_id, created_at
  • Key tables: orders, riders, users, companies, attempt_history, delivery_otps, slot_capacities, zones

πŸ›  Tech Stack

Layer Technology
Language Java 17
Framework Spring Boot 3
Security Spring Security + JWT
Database PostgreSQL
Caching Redis
Migrations Flyway
Containerization Docker + docker-compose
Build Maven

▢️ How To Run

Prerequisites: Docker and Docker Compose installed.

# 1. Clone the repository
git clone https://github.com/AditShh-git/delivery-platform

# 2. Create a .env file
cp .env.example .env
# Fill in your values

# 3. Start everything
docker-compose up --build

The app will start on http://localhost:8080.
PostgreSQL on 5432, Redis on 6379.


πŸ“ Project Structure

src/
β”œβ”€β”€ controller/       # Thin REST adapters β€” routing, auth, request/response mapping only
β”œβ”€β”€ service/          # Business logic, state machine, domain rule enforcement
β”‚   └── impl/         # Service implementations
β”œβ”€β”€ slot/             # Slot capacity entity, repository, service, and controller
β”œβ”€β”€ repository/       # Spring Data JPA + native SQL queries
β”œβ”€β”€ scheduler/        # SlaBreachScheduler, ConfirmationScheduler
β”œβ”€β”€ entity/           # JPA entities
β”œβ”€β”€ dto/              # Request/response records
β”‚   β”œβ”€β”€ request/
β”‚   └── response/
β”œβ”€β”€ projection/       # Typed Spring projection interfaces
β”œβ”€β”€ config/           # CacheConfig, SecurityConfig, custom auth entry points
β”œβ”€β”€ security/         # JWT filter, token provider
β”œβ”€β”€ utils/            # Shared utilities
└── exception/        # Global exception handler
db/migration/         # V1–V22 Flyway SQL migrations

πŸ”œ Coming Next

  • AutoDispatchScheduler β€” INSTANT orders self-assign to the best available rider every 5 seconds
  • RunSheet System β€” admin creates a sorted delivery list for PARCEL riders, route ordered by nearest-neighbor GPS sort, exportable as CSV

Built independently to simulate production SaaS backend architecture.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors