') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - BlackBossX/BusTicketGeneratingSystem: A Java-based QR Bus Ticket Booking System designed to modernize public transportation in Sri Lanka by enabling online ticket booking, QR code generation, and digital ticket verification. · GitHub
Skip to content

Repository files navigation

QR Bus Ticket Generating System

🚍 Welcome to the QR Bus Ticket Generating System! This project streamlines bus ticket booking with modern technologies like Google Cloud APIs and QR codes.


🚀 Features

  1. 🔒 Secure User Management

    • User sign-up and login with bcrypt for password hashing.
    • Unique User IDs generated via database triggers.
  2. 📍 Location-Based Ticketing

    • Calculates distance and travel duration using Google Cloud Distance Matrix API.
    • Dynamic fare calculation based on distance.
  3. 🎟️ QR Code Tickets

    • QR codes generated for tickets using the goqr.me API.
    • Tickets can be scanned for validation.
  4. 💾 Database Management

    • MySQL database to store user and trip information.
    • Automated ID generation for users and trips using SQL triggers.
  5. 🛠️ Modular and Scalable

    • Built with Maven for dependency management.
    • Organized into clear, reusable components.

🛠️ Technologies Used

  • Java
  • Maven
  • Google Cloud Distance Matrix API
  • goqr.me API
  • bcrypt
  • MySQL

⚙️ Setup Instructions

  1. Clone the repository and navigate to the project directory.

  2. Set up a MySQL database and execute the provided schema and triggers.

  3. Configure API keys for:

  4. Configure the .env file

    • GOOGLE_MAPS_API_KEY
    • DB_URL
    • DB_USERNAME
    • DB_PASSWORD
    • QR_API_URL
  5. Build the project with Maven:

    mvn clean install

Demo Video - Link

📊 Database Schema

🧑‍🤝‍🧑Users Table

CREATETABLEUsers (
user_id VARCHAR(10) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(75) UNIQUE NOT NULL,
password VARCHAR(60) NOT NULL,
phone VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Trigger for User ID Generation:

DELIMITER $$
CREATETRIGGERbefore_insert_users BEFORE INSERT ON Users FOR EACH ROW BEGIN
DECLARE next_id INT;
SELECT COALESCE(MAX(CAST(SUBSTRING(user_id, 2) AS UNSIGNED)), 0) +1 INTO next_id FROM Users;
SETNEW.user_id= CONCAT('U', LPAD(next_id, 4, '0'));
END$$;
DELIMITER ;

🛣️Trips Table

CREATETABLETrips (
trip_id VARCHAR(10) PRIMARY KEY,
start_location VARCHAR(100) NOT NULL,
end_location VARCHAR(100) NOT NULL,
distance VARCHAR(20) NOT NULL,
duration VARCHAR(20) NOT NULL,
fare DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Trigger for Trip ID Generation:

DELIMITER $$
CREATETRIGGERbefore_insert_trips BEFORE INSERT ON Trips FOR EACH ROW BEGIN
DECLARE next_id INT;
SELECT COALESCE(MAX(CAST(SUBSTRING(trip_id, 2) AS UNSIGNED)), 0) +1 INTO next_id FROM Trips;
SETNEW.trip_id= CONCAT('R', LPAD(next_id, 4, '0'));
END$$;
DELIMITER ;

🎟️Tickets Table

CREATETABLETickets (
ticket_id VARCHAR(10) PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
start_location VARCHAR(100) NOT NULL,
end_location VARCHAR(100) NOT NULL,
distance VARCHAR(20) NOT NULL,
duration VARCHAR(20) NOT NULL,
total_fare DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Trigger for Ticket ID Generation:

DELIMITER $$
CREATETRIGGERbefore_insert_tickets BEFORE INSERT ON Tickets FOR EACH ROW BEGIN
DECLARE next_id INT;
SELECT COALESCE(MAX(CAST(SUBSTRING(ticket_id, 2) AS UNSIGNED)), 0) +1 INTO next_id FROM Tickets;
SETNEW.ticket_id= CONCAT('T', LPAD(next_id, 4, '0'));
END$$
DELIMITER ;

💺Seats Table

CREATETABLESeats (
seat_id VARCHAR(10) NOT NULLPRIMARY KEY,
ticket_id VARCHAR(10) NOT NULL,
user_id VARCHAR(10) NOT NULL,
availability TINYINT(1) NOT NULL DEFAULT 1
);

Trigger for Seat ID Generation:

DELIMITER $$
CREATETRIGGERbefore_insert_seats
BEFORE INSERT ON Seats
FOR EACH ROW
BEGIN
DECLARE next_id INT;
SELECT COALESCE(MAX(CAST(SUBSTRING(seat_id, 2) AS UNSIGNED)), 0) +1 INTO next_id
FROM Seats;
SETNEW.seat_id= CONCAT('S', LPAD(next_id, 4, '0'));
END;
DELIMITER ;

Trigger to Limit the Seat Count to 50:

DELIMITER $$
CREATETRIGGERlimit_seats_before_insert
BEFORE INSERT ON Seats
FOR EACH ROW
BEGIN
DECLARE row_count INT;
-- Count the current number of rows in the tableSELECTCOUNT(*) INTO row_count FROM Seats;
-- Check if the row count exceeds 50
IF row_count >=50 THEN
SIGNAL SQLSTATE '45000'SET MESSAGE_TEXT ='Cannot add more rows. Maximum seat limit (50) reached.';
END IF;
END;
DELIMITER ;

🌟 Future Enhancements

  • Integrate a payment gateway for online payments.
  • Add analytics for trip data.
  • Upgrade the user interface with modern frameworks.

👤 Support

For support, email malandealwis@gmail.com

About

A Java-based QR Bus Ticket Booking System designed to modernize public transportation in Sri Lanka by enabling online ticket booking, QR code generation, and digital ticket verification.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages