diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..bf2c501 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..84a2485 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/restaurant_db + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..4158879 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f6a588b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..c3c8b8e --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/database.sql b/database.sql index 2169346..d777d05 100644 --- a/database.sql +++ b/database.sql @@ -138,4 +138,138 @@ -- SELECT * FROM ...; -- SELECT * FROM ...; -- SELECT * FROM ...; --- SELECT * FROM ...; \ No newline at end of file +-- SELECT * FROM ...; + + +-- ======================================================= +-- Restaurant Database Management System +-- PostgreSQL Schema +-- ======================================================= + +-- ------------------------------------------------------- +-- OPTIONAL CLEANUP +-- Uncomment if you want to reset the database completely +-- ------------------------------------------------------- +-- DROP TABLE IF EXISTS order_details CASCADE; +-- DROP TABLE IF EXISTS orders CASCADE; +-- DROP TABLE IF EXISTS menu_items CASCADE; +-- DROP TABLE IF EXISTS users CASCADE; + +-- ======================================================= +-- USER TABLE +-- ======================================================= +-- Represents customers using the system. +-- Requirements: +-- - Unique identifier +-- - Unique username +-- - Required username and password +-- - Passwords should not be stored in plain text +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + username VARCHAR(100) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + email VARCHAR(255) +); + +-- ======================================================= +-- MENU ITEM TABLE +-- ======================================================= +-- Represents available food and drink items. +-- Requirements: +-- - Unique identifier +-- - Name required +-- - Price positive +CREATE TABLE menu_items ( + id SERIAL PRIMARY KEY, + name VARCHAR(150) NOT NULL, + description TEXT, + price DOUBLE PRECISION NOT NULL CHECK (price > 0), + category VARCHAR(100) +); + +-- ======================================================= +-- ORDER TABLE +-- ======================================================= +-- Represents orders placed by customers. +-- Requirements: +-- - Each order belongs to exactly one user +-- - A user can have multiple orders +-- - Use non-reserved table name: orders +CREATE TABLE orders ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + total_price DOUBLE PRECISION NOT NULL DEFAULT 0 CHECK (total_price >= 0), + CONSTRAINT fk_orders_user + FOREIGN KEY (user_id) + REFERENCES users(id) + ON DELETE CASCADE +); + +-- ======================================================= +-- ORDER DETAIL TABLE +-- ======================================================= +-- Represents items inside an order. +-- Requirements: +-- - Each detail belongs to one order +-- - Each detail references one menu item +-- - Quantity > 0 +-- - Store item price at purchase time +CREATE TABLE order_details ( + id SERIAL PRIMARY KEY, + order_id INT NOT NULL, + menu_item_id INT NOT NULL, + quantity INT NOT NULL CHECK (quantity > 0), + price DOUBLE PRECISION NOT NULL CHECK (price > 0), + CONSTRAINT fk_order_details_order + FOREIGN KEY (order_id) + REFERENCES orders(id) + ON DELETE CASCADE, + CONSTRAINT fk_order_details_menu_item + FOREIGN KEY (menu_item_id) + REFERENCES menu_items(id) +); + +-- ======================================================= +-- INITIAL MENU DATA +-- ======================================================= +-- Insert at least 3 food/drink items +INSERT INTO menu_items (name, description, price, category) VALUES + ('Margherita Pizza', 'Classic pizza with tomato sauce, mozzarella, and basil', 8.99, 'Pizza'), + ('Pepperoni Pizza', 'Pizza topped with pepperoni and cheese', 10.99, 'Pizza'), + ('Cheeseburger', 'Beef burger with cheddar cheese and lettuce', 7.50, 'Burger'), + ('Pasta Carbonara', 'Creamy pasta with bacon and parmesan', 11.50, 'Pasta'), + ('Coca Cola', 'Chilled soft drink', 1.50, 'Drink'); + +-- ======================================================= +-- OPTIONAL TEST DATA +-- ======================================================= +-- Sample user for testing (password is hashed placeholder) +INSERT INTO users (username, password_hash, email) VALUES + ('testuser', '$2a$10$dummyhashforTestingOnly12345678901234567890', 'testuser@example.com'); + +-- Sample order for testing +INSERT INTO orders (user_id, created_at, total_price) +VALUES (1, CURRENT_TIMESTAMP, 0); + +-- Sample order details for testing +INSERT INTO order_details (order_id, menu_item_id, quantity, price) VALUES + (1, 1, 2, 8.99), + (1, 5, 2, 1.50); + +-- Update total price for the sample order +UPDATE orders +SET total_price = ( + SELECT SUM(quantity * price) + FROM order_details + WHERE order_id = 1 +) +WHERE id = 1; + +-- ======================================================= +-- VERIFICATION QUERIES +-- ======================================================= +SELECT * FROM users; +SELECT * FROM menu_items; +SELECT * FROM orders; +SELECT * FROM order_details; \ No newline at end of file diff --git a/pom.xml b/pom.xml index 028ca50..1b26cb2 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,12 @@ + + org.mindrot + jbcrypt + 0.4 + + org.postgresql postgresql diff --git a/restaurant_db.sql b/restaurant_db.sql new file mode 100644 index 0000000..2ce88b9 --- /dev/null +++ b/restaurant_db.sql @@ -0,0 +1,110 @@ +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + username VARCHAR(100) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + email VARCHAR(255) +); + +-- ======================================================= +-- MENU ITEM TABLE +-- ======================================================= +-- Represents available food and drink items. +-- Requirements: +-- - Unique identifier +-- - Name required +-- - Price positive +CREATE TABLE menu_items ( + id SERIAL PRIMARY KEY, + name VARCHAR(150) NOT NULL, + description TEXT, + price DOUBLE PRECISION NOT NULL CHECK (price > 0), + category VARCHAR(100) +); + +-- ======================================================= +-- ORDER TABLE +-- ======================================================= +-- Represents orders placed by customers. +-- Requirements: +-- - Each order belongs to exactly one user +-- - A user can have multiple orders +-- - Use non-reserved table name: orders +CREATE TABLE orders ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + total_price DOUBLE PRECISION NOT NULL DEFAULT 0 CHECK (total_price >= 0), + CONSTRAINT fk_orders_user + FOREIGN KEY (user_id) + REFERENCES users(id) + ON DELETE CASCADE +); + +-- ======================================================= +-- ORDER DETAIL TABLE +-- ======================================================= +-- Represents items inside an order. +-- Requirements: +-- - Each detail belongs to one order +-- - Each detail references one menu item +-- - Quantity > 0 +-- - Store item price at purchase time +CREATE TABLE order_details ( + id SERIAL PRIMARY KEY, + order_id INT NOT NULL, + menu_item_id INT NOT NULL, + quantity INT NOT NULL CHECK (quantity > 0), + price DOUBLE PRECISION NOT NULL CHECK (price > 0), + CONSTRAINT fk_order_details_order + FOREIGN KEY (order_id) + REFERENCES orders(id) + ON DELETE CASCADE, + CONSTRAINT fk_order_details_menu_item + FOREIGN KEY (menu_item_id) + REFERENCES menu_items(id) + ON DELETE RESTRICT +); + +-- ======================================================= +-- INITIAL MENU DATA +-- ======================================================= +-- Insert at least 3 food/drink items +INSERT INTO menu_items (name, description, price, category) VALUES + ('Margherita Pizza', 'Classic pizza with tomato sauce, mozzarella, and basil', 8.99, 'Pizza'), + ('Pepperoni Pizza', 'Pizza topped with pepperoni and cheese', 10.99, 'Pizza'), + ('Cheeseburger', 'Beef burger with cheddar cheese and lettuce', 7.50, 'Burger'), + ('Pasta Carbonara', 'Creamy pasta with bacon and parmesan', 11.50, 'Pasta'), + ('Coca Cola', 'Chilled soft drink', 1.50, 'Drink'); + +-- ======================================================= +-- OPTIONAL TEST DATA +-- ======================================================= +-- Sample user for testing (password is hashed placeholder) +INSERT INTO users (username, password_hash, email) VALUES + ('testuser', '$2a$10$dummyhashforTestingOnly12345678901234567890', 'testuser@example.com'); + +-- Sample order for testing +INSERT INTO orders (user_id, created_at, total_price) +VALUES (1, CURRENT_TIMESTAMP, 0); + +-- Sample order details for testing +INSERT INTO order_details (order_id, menu_item_id, quantity, price) VALUES + (1, 1, 2, 8.99), + (1, 5, 2, 1.50); + +-- Update total price for the sample order +UPDATE orders +SET total_price = ( + SELECT SUM(quantity * price) + FROM order_details + WHERE order_id = 1 +) +WHERE id = 1; + +-- ======================================================= +-- VERIFICATION QUERIES +-- ======================================================= +SELECT * FROM users; +SELECT * FROM menu_items; +SELECT * FROM orders; +SELECT * FROM order_details; diff --git a/src/main/java/dev/Main.java b/src/main/java/dev/Main.java index 3a93683..24432cd 100644 --- a/src/main/java/dev/Main.java +++ b/src/main/java/dev/Main.java @@ -1,12 +1,21 @@ package dev; import dev.ui.ConsoleMenu; +import dev.service.AuthService; +import dev.service.MenuService; +import dev.service.OrderService; -public class Main { +public class Main +{ - public static void main(String[] args) { + public static void main(String[] args) + { + + AuthService authService = new AuthService(); + MenuService menuService = new MenuService(); + OrderService orderService = new OrderService(); - ConsoleMenu menu = new ConsoleMenu(); + ConsoleMenu menu = new ConsoleMenu(authService, menuService, orderService); menu.start(); } diff --git a/src/main/java/dev/dao/MenuItemDao.java b/src/main/java/dev/dao/MenuItemDao.java index 3bbebe9..021da09 100644 --- a/src/main/java/dev/dao/MenuItemDao.java +++ b/src/main/java/dev/dao/MenuItemDao.java @@ -1,24 +1,73 @@ package dev.dao; +import dev.database.DatabaseConnection; import dev.model.MenuItem; - +import java.sql.*; +import java.util.ArrayList; import java.util.List; -public class MenuItemDao { +public class MenuItemDao +{ - public List findAll() { + public List findAll() + { - // TODO: - // Retrieve all menu items - return null; + String sql = "SELECT * FROM menu_items"; + List items = new ArrayList<>(); + + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql); + ResultSet rs = pstmt.executeQuery()) + { + + while (rs.next()) + { + items.add(new MenuItem( + rs.getInt("id"), + rs.getString("name"), + rs.getString("description"), + rs.getDouble("price"), + rs.getString("category") + )); + } + } + catch (SQLException e) + { + e.printStackTrace(); + } + + return items; } - public MenuItem findById(int id) { + public MenuItem findById(int id) + { + String sql = "SELECT * FROM menu_items WHERE id = ?"; - // TODO: - // Find menu item by id + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { + pstmt.setInt(1, id); + + try (ResultSet rs = pstmt.executeQuery()) + { + if (rs.next()) + { + return new MenuItem( + rs.getInt("id"), + rs.getString("name"), + rs.getString("description"), + rs.getDouble("price"), + rs.getString("category") + ); + } + } + } + catch (SQLException e) + { + e.printStackTrace(); + } return null; } diff --git a/src/main/java/dev/dao/OrderDao.java b/src/main/java/dev/dao/OrderDao.java index 00999a2..7a3930d 100644 --- a/src/main/java/dev/dao/OrderDao.java +++ b/src/main/java/dev/dao/OrderDao.java @@ -1,25 +1,92 @@ package dev.dao; +import dev.database.DatabaseConnection; import dev.model.Order; - +import java.sql.*; +import java.util.ArrayList; import java.util.List; -public class OrderDao { +public class OrderDao +{ - public int save(Order order) { + public int save(Order order) + { + String sql = "INSERT INTO orders (user_id, created_at, total_price) VALUES (?, ?, ?)"; - // TODO: - // Insert order and return generated id + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) + { + pstmt.setInt(1, order.getUserId()); + pstmt.setTimestamp(2, Timestamp.valueOf(order.getCreatedAt())); + pstmt.setDouble(3, order.getTotalPrice()); + + int affectedRows = pstmt.executeUpdate(); + if (affectedRows == 0) return -1; + + try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) + { + if (generatedKeys.next()) + { + return generatedKeys.getInt(1); + } + } + } + catch (SQLException e) + { + e.printStackTrace(); + } return -1; } - public List findByUserId(int userId) { + public List findByUserId(int userId) + { + List orders = new ArrayList<>(); + String sql = "SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC"; - // TODO: - // Retrieve all orders of a user + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { - return null; + pstmt.setInt(1, userId); + ResultSet rs = pstmt.executeQuery(); + + while (rs.next()) + { + Order order = new Order(); + order.setId(rs.getInt("id")); + order.setUserId(rs.getInt("user_id")); + + order.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime()); + order.setTotalPrice(rs.getDouble("total_price")); + + orders.add(order); + } + } + catch (SQLException e) + { + e.printStackTrace(); + } + return orders; } -} \ No newline at end of file + public void updateTotalPrice(int orderId, double totalPrice) + { + String sql = "UPDATE orders SET total_price = ? WHERE id = ?"; + + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { + + pstmt.setDouble(1, totalPrice); + pstmt.setInt(2, orderId); + pstmt.executeUpdate(); + + } + catch (SQLException e) + { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/dev/dao/OrderDetailDao.java b/src/main/java/dev/dao/OrderDetailDao.java index ed42f56..7a2098f 100644 --- a/src/main/java/dev/dao/OrderDetailDao.java +++ b/src/main/java/dev/dao/OrderDetailDao.java @@ -1,24 +1,82 @@ package dev.dao; import dev.model.OrderDetail; +import dev.database.DatabaseConnection; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; -public class OrderDetailDao { - public void save(OrderDetail detail) { +public class OrderDetailDao +{ + + public void save(OrderDetail detail) + { // TODO: // Insert order detail + String sql = "INSERT INTO order_details (order_id, menu_item_id, quantity, price) VALUES (?, ?, ?, ?)"; + + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { + + pstmt.setInt(1, detail.getOrderId()); + pstmt.setInt(2, detail.getMenuItemId()); + pstmt.setInt(3, detail.getQuantity()); + pstmt.setDouble(4, detail.getPrice()); + + pstmt.executeUpdate(); + } + catch (SQLException e) + { + System.err.println("Error saving order detail: " + e.getMessage()); + } + } - public List findByOrderId(int orderId) { + public List findByOrderId(int orderId) + { // TODO: + // Retrieve order details - return null; + String sql = "SELECT * FROM order_details WHERE order_id = ?"; + List details = new ArrayList<>(); + + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { + + pstmt.setInt(1, orderId); + + try (ResultSet rs = pstmt.executeQuery()) + { + while (rs.next()) + { + OrderDetail detail = new OrderDetail( + rs.getInt("id"), + rs.getInt("order_id"), + rs.getInt("menu_item_id"), + rs.getInt("quantity"), + rs.getDouble("price") + ); + details.add(detail); + } + } + } + catch (SQLException e) + { + System.err.println("Error finding order details: " + e.getMessage()); + } + + return details; } } \ No newline at end of file diff --git a/src/main/java/dev/dao/UserDao.java b/src/main/java/dev/dao/UserDao.java index af08106..244d342 100644 --- a/src/main/java/dev/dao/UserDao.java +++ b/src/main/java/dev/dao/UserDao.java @@ -2,21 +2,70 @@ package dev.dao; import dev.model.User; -public class UserDao { +import dev.database.DatabaseConnection; - public boolean save(User user) { +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; - // TODO: - // Insert user into database +public class UserDao +{ + + public boolean save(User user) + { + + String sql = "INSERT INTO users (username, password_hash) VALUES (?, ?)"; + + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { + + pstmt.setString(1, user.getUsername()); + pstmt.setString(2, user.getPasswordHash()); + + int affectedRows = pstmt.executeUpdate(); + return affectedRows > 0; + + } + catch (SQLException e) + { + e.printStackTrace(); + return false; + } - return false; } - public User findByUsername(String username) { + public User findByUsername(String username) + { + String sql = "SELECT * FROM users WHERE username = ?"; - // TODO: - // Find a user by username + try (Connection conn = DatabaseConnection.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) + { + pstmt.setString(1, username); + + try (ResultSet rs = pstmt.executeQuery()) + { + if (rs.next()) + { + User user = new User(); + + user.setId(rs.getInt("id")); + user.setUsername(rs.getString("username")); + user.setPasswordHash(rs.getString("password_hash")); + + user.setEmail(rs.getString("email")); + + return user; + } + } + } + catch (SQLException e) + { + e.printStackTrace(); + } return null; } diff --git a/src/main/java/dev/database/DatabaseConnection.java b/src/main/java/dev/database/DatabaseConnection.java index da45535..e441d5b 100644 --- a/src/main/java/dev/database/DatabaseConnection.java +++ b/src/main/java/dev/database/DatabaseConnection.java @@ -2,14 +2,16 @@ package dev.database; import java.sql.Connection; import java.sql.SQLException; +import java.sql.DriverManager; -public class DatabaseConnection { +public class DatabaseConnection +{ - private static final String URL = "jdbc:postgresql://localhost:5432/restaurant_db"; // DB Server + private static final String URL = "jdbc:postgresql://localhost:5432/restaurant_db"; - private static final String USER = "postgres"; // Your Username + private static final String USER = "postgres"; - private static final String PASSWORD = "password"; // Your Password + private static final String PASSWORD = "Sa123456*"; private DatabaseConnection() { @@ -18,10 +20,8 @@ public class DatabaseConnection { public static Connection getConnection() throws SQLException { - // TODO: - // Return a valid PostgreSQL connection - return null; + return DriverManager.getConnection(URL, USER, PASSWORD); + } - } \ No newline at end of file diff --git a/src/main/java/dev/model/MenuItem.java b/src/main/java/dev/model/MenuItem.java index 265ecc1..872a5fa 100644 --- a/src/main/java/dev/model/MenuItem.java +++ b/src/main/java/dev/model/MenuItem.java @@ -1,6 +1,7 @@ package dev.model; -public class MenuItem { +public class MenuItem +{ private int id; @@ -12,4 +13,75 @@ public class MenuItem { private String category; + + public MenuItem() {} + + public MenuItem(int id, String name, String description, double price, String category) + { + this.id = id; + this.name = name; + this.description = description; + this.price = price; + this.category = category; + } + + public int getId() + { + return id; + } + + public String getName() + { + return name; + } + + public String getDescription() + { + return description; + } + + public double getPrice() + { + return price; + } + + public String getCategory() + { + return category; + } + + public void setId(int id) + { + this.id = id; + } + + public void setName(String name) + { + this.name = name; + } + + public void setDescription(String description) + { + this.description = description; + } + + public void setPrice(double price) + { + this.price = price; + } + + public void setCategory(String category) + { + this.category = category; + } + + @Override + public String toString() + { + return "MenuItem{" + + "id=" + id + + ", name='" + name + '\'' + + ", price=" + price + + '}'; + } } \ No newline at end of file diff --git a/src/main/java/dev/model/Order.java b/src/main/java/dev/model/Order.java index 32049cb..d9c4dcf 100644 --- a/src/main/java/dev/model/Order.java +++ b/src/main/java/dev/model/Order.java @@ -2,14 +2,79 @@ package dev.model; import java.time.LocalDateTime; -public class Order { +public class Order +{ private int id; - private int userId; - private LocalDateTime createdAt; - private double totalPrice; + public Order() {} + + public Order(int userId, double totalPrice) + { + this.userId = userId; + this.createdAt = LocalDateTime.now(); + this.totalPrice = totalPrice; + } + + public Order(int id, int userId, LocalDateTime createdAt, double totalPrice) + { + this.id = id; + this.userId = userId; + this.createdAt = createdAt; + this.totalPrice = totalPrice; + } + + public int getId() + { + return id; + } + + public void setId(int id) + { + this.id = id; + } + + public int getUserId() + { + return userId; + } + + public void setUserId(int userId) + { + this.userId = userId; + } + + public LocalDateTime getCreatedAt() + { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) + { + this.createdAt = createdAt; + } + + public double getTotalPrice() + { + return totalPrice; + } + + public void setTotalPrice(double totalPrice) + { + this.totalPrice = totalPrice; + } + + @Override + public String toString() + { + return "Order{" + + "id=" + id + + ", userId=" + userId + + ", createdAt=" + createdAt + + ", totalPrice=" + totalPrice + + '}'; + } } \ No newline at end of file diff --git a/src/main/java/dev/model/OrderDetail.java b/src/main/java/dev/model/OrderDetail.java index b5b8bbe..34ffe2c 100644 --- a/src/main/java/dev/model/OrderDetail.java +++ b/src/main/java/dev/model/OrderDetail.java @@ -1,15 +1,92 @@ package dev.model; -public class OrderDetail { +public class OrderDetail +{ private int id; - private int orderId; - private int menuItemId; - private int quantity; - private double price; -} \ No newline at end of file + public OrderDetail() {} + + public OrderDetail(int orderId, int menuItemId, int quantity, double price) + { + this.orderId = orderId; + this.menuItemId = menuItemId; + this.quantity = quantity; + this.price = price; + } + + public OrderDetail(int id, int orderId, int menuItemId, int quantity, double price) + { + this.id = id; + this.orderId = orderId; + this.menuItemId = menuItemId; + this.quantity = quantity; + this.price = price; + } + + public int getId() + { + return id; + } + + public void setId(int id) + { + this.id = id; + } + + public int getOrderId() + { + return orderId; + } + + public void setOrderId(int orderId) + { + this.orderId = orderId; + } + + public int getMenuItemId() + { + return menuItemId; + } + + public void setMenuItemId(int menuItemId) + { + this.menuItemId = menuItemId; + } + + public int getQuantity() + { + return quantity; + } + + public void setQuantity(int quantity) + { + this.quantity = quantity; + } + + public double getPrice() + { + return price; + } + + public void setPrice(double price) + { + this.price = price; + } + + @Override + public String toString() + { + return "OrderDetail{" + + "id=" + id + + ", orderId=" + orderId + + ", menuItemId=" + menuItemId + + ", quantity=" + quantity + + ", price=" + price + + '}'; + } +} diff --git a/src/main/java/dev/model/User.java b/src/main/java/dev/model/User.java index 0ab60c6..a436f10 100644 --- a/src/main/java/dev/model/User.java +++ b/src/main/java/dev/model/User.java @@ -1,13 +1,82 @@ package dev.model; -public class User { +public class User +{ private int id; private String username; - private String password; + private String passwordHash; private String email; + public User() {} + + + public User(String username, String passwordHash, String email) + { + this.username = username; + this.passwordHash = passwordHash; + this.email = email; + } + + public User(int id, String username, String passwordHash, String email) + { + this.id = id; + this.username = username; + this.passwordHash = passwordHash; + this.email = email; + } + + public int getId() + { + return id; + } + + public void setId(int id) + { + this.id = id; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPasswordHash() + { + return passwordHash; + } + + public void setPasswordHash(String passwordHash) + { + this.passwordHash = passwordHash; + } + + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Override + public String toString() + { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", email='" + email + '\'' + + '}'; + } + } \ No newline at end of file diff --git a/src/main/java/dev/service/AuthService.java b/src/main/java/dev/service/AuthService.java index bee866a..b115465 100644 --- a/src/main/java/dev/service/AuthService.java +++ b/src/main/java/dev/service/AuthService.java @@ -1,23 +1,49 @@ package dev.service; +import dev.dao.UserDao; import dev.model.User; +import org.mindrot.jbcrypt.BCrypt; -public class AuthService { +public class AuthService +{ + private final UserDao userDao = new UserDao(); - public boolean register(String username, String password, String email) { + public boolean register(String username, String password, String email) + { - // TODO: - // Validate and register user - return false; + if (username == null || username.isBlank()) return false; + if (password == null || password.isBlank()) return false; + + if (email == null || email.isBlank()) return false; + + User existing = userDao.findByUsername(username.trim()); + if (existing != null) return false; + + String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt(12)); + + User user = new User(); + user.setUsername(username.trim()); + user.setPasswordHash(passwordHash); + + user.setEmail(email.trim()); + + return userDao.save(user); + } - public User login(String username, String password) { + public User login(String username, String password) + { - // TODO: - // Authenticate user + if (username == null || username.isBlank()) return null; + if (password == null || password.isBlank()) return null; + + User user = userDao.findByUsername(username.trim()); + if (user == null) return null; + + boolean ok = BCrypt.checkpw(password, user.getPasswordHash()); + return ok ? user : null; - return null; } } \ No newline at end of file diff --git a/src/main/java/dev/service/MenuService.java b/src/main/java/dev/service/MenuService.java index 6dbf4da..092c04e 100644 --- a/src/main/java/dev/service/MenuService.java +++ b/src/main/java/dev/service/MenuService.java @@ -1,12 +1,38 @@ package dev.service; -public class MenuService { +import dev.dao.MenuItemDao; +import dev.model.MenuItem; +import dev.ui.ConsoleStyle; +import java.util.List; - public void showMenu() { +public class MenuService +{ - // TODO: - // Display menu items + private final MenuItemDao menuItemDao = new MenuItemDao(); + public void showMenu() + { + List menuItems = menuItemDao.findAll(); + + if (menuItems == null || menuItems.isEmpty()) + { + ConsoleStyle.printError("No menu items available."); + return; + } + + ConsoleStyle.printHeader("๐Ÿ“‹ MENU ITEMS"); + System.out.printf("%-5s %-25s %-10s %-15s%n", "ID", "Name", "Price", "Category"); + ConsoleStyle.printLine(); + + for (MenuItem item : menuItems) + { + System.out.printf( + "%-5d %-25s $%-9.2f %-15s%n", + item.getId(), + item.getName(), + item.getPrice(), + item.getCategory() == null ? "-" : item.getCategory() + ); + } } - } \ No newline at end of file diff --git a/src/main/java/dev/service/OrderService.java b/src/main/java/dev/service/OrderService.java index 14708f8..5866010 100644 --- a/src/main/java/dev/service/OrderService.java +++ b/src/main/java/dev/service/OrderService.java @@ -1,26 +1,147 @@ package dev.service; -public class OrderService { +import dev.dao.MenuItemDao; +import dev.dao.OrderDao; +import dev.dao.OrderDetailDao; +import dev.model.Order; +import dev.model.OrderDetail; +import dev.model.MenuItem; +import dev.ui.ConsoleStyle; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Scanner; - public void placeOrder(int userId) { +public class OrderService +{ - // TODO: - // Create order + private final OrderDao orderDao = new OrderDao(); + private final OrderDetailDao orderDetailDao = new OrderDetailDao(); + private final MenuItemDao menuItemDao = new MenuItemDao(); + private final Scanner scanner = new Scanner(System.in); + public void placeOrder(int userId) + { + System.out.println("\n--- Create New Order ---"); + + Order order = new Order(); + order.setUserId(userId); + order.setCreatedAt(LocalDateTime.now()); + order.setTotalPrice(0.0); + + int orderId = orderDao.save(order); + + if (orderId == -1) + { + System.out.println("Failed to create order."); + return; + } + + double total = 0; + List details = new java.util.ArrayList<>(); + + while (true) + { + System.out.print("Enter Menu Item ID (or 0 to finish): "); + int itemId = scanner.nextInt(); + + if (itemId == 0) + { + break; + } + + MenuItem item = menuItemDao.findById(itemId); + if (item == null) + { + System.out.println("Item not found!"); + continue; + } + + System.out.print("Quantity for " + item.getName() + ": "); + int quantity = scanner.nextInt(); + + OrderDetail detail = new OrderDetail(); + detail.setOrderId(orderId); + detail.setMenuItemId(itemId); + detail.setQuantity(quantity); + detail.setPrice(item.getPrice()); + + orderDetailDao.save(detail); + details.add(detail); + + total += (item.getPrice() * quantity); + System.out.println("Added: " + item.getName() + " x" + quantity); + } + + order.setId(orderId); + order.setTotalPrice(total); + orderDao.updateTotalPrice(orderId, total); + + System.out.println("Order #" + orderId + " placed successfully. Total: $" + total); + + printReceipt(order, details); } - public void printReceipt(int orderId) { + private void printReceipt(Order order, List details) + { + ConsoleStyle.printHeader("๐Ÿงพ RECEIPT "); - // TODO: - // Print order receipt + System.out.println("Order ID: " + order.getId()); + System.out.println("Date : " + order.getCreatedAt().toString().replace("T", " ")); + ConsoleStyle.printLine(); + System.out.printf("%-20s %-8s %-12s %-12s%n", "Item Name", "Qty", "Unit Price", "Total"); + ConsoleStyle.printLine(); + + for (OrderDetail detail : details) + { + MenuItem item = menuItemDao.findById(detail.getMenuItemId()); + String itemName = (item != null) ? item.getName() : "Item #" + detail.getMenuItemId(); + + double lineTotal = detail.getQuantity() * detail.getPrice(); + + System.out.printf( + "%-20s %-8d $%-11.2f $%-11.2f%n", + itemName, + detail.getQuantity(), + detail.getPrice(), + lineTotal + ); + } + + ConsoleStyle.printLine(); + System.out.printf("%-42s $%.2f%n", "Grand Total:", order.getTotalPrice()); + ConsoleStyle.printLine(); } - public void showOrderHistory(int userId) { + public void showOrderHistory(int userId) + { + System.out.println("\n--- ๐Ÿ“œ Order History (Detailed) ---"); + List history = orderDao.findByUserId(userId); - // TODO: - // Display user's order history + if (history.isEmpty()) + { + System.out.println("You have no previous orders."); + return; + } + for (Order o : history) { + System.out.println("=========================================="); + System.out.printf("ORDER ID: %-5d | Date: %s\n", o.getId(), o.getCreatedAt().toString().replace("T", " ")); + System.out.println("Items:"); + + List details = orderDetailDao.findByOrderId(o.getId()); + + for (OrderDetail detail : details) + { + MenuItem item = menuItemDao.findById(detail.getMenuItemId()); + String itemName = (item != null) ? item.getName() : "Unknown Item"; + + System.out.printf(" - %-20s x%-3d ($%.2f)\n", itemName, detail.getQuantity(), detail.getPrice()); + } + + System.out.printf("TOTAL: $%.2f\n", o.getTotalPrice()); + } + System.out.println("=========================================="); } } \ No newline at end of file diff --git a/src/main/java/dev/ui/ConsoleMenu.java b/src/main/java/dev/ui/ConsoleMenu.java index 663dc0c..2a865e5 100644 --- a/src/main/java/dev/ui/ConsoleMenu.java +++ b/src/main/java/dev/ui/ConsoleMenu.java @@ -1,44 +1,155 @@ package dev.ui; +import dev.model.User; +import dev.service.AuthService; +import dev.service.OrderService; +import dev.service.MenuService; + import java.util.Scanner; -public class ConsoleMenu { +public class ConsoleMenu +{ - private final Scanner scanner = - new Scanner(System.in); - - public void start() { - - while (true) { - - System.out.println(); - System.out.println("===== JAVA PIZZERIA ====="); - System.out.println("1. Login"); - System.out.println("2. Register"); - System.out.println("3. Exit"); - - int choice = scanner.nextInt(); - - switch (choice) { - - case 1: - // TODO - break; - - case 2: - // TODO - break; - - case 3: - return; - - default: - System.out.println("Invalid choice"); - - } - - } + private final AuthService authService; + private final MenuService menuService; + private final OrderService orderService; + private final Scanner scanner; + private User currentUser; + public ConsoleMenu(AuthService authService, MenuService menuService, OrderService orderService) + { + this.authService = authService; + this.menuService = menuService; + this.orderService = orderService; + this.scanner = new Scanner(System.in); } -} \ No newline at end of file + public void start() + { + while (true) + { + if (currentUser == null) + { + showGuestMenu(); + } + else + { + showUserMenu(); + } + } + } + + private void showGuestMenu() + { + ConsoleStyle.printHeader("๐Ÿ• WELCOME TO JAVA PIZZERIA ๐Ÿ•"); + System.out.println("1. Login"); + System.out.println("2. Register New Account"); + System.out.println("3. Exit"); + ConsoleStyle.printLine(); + System.out.print("Choose an option: "); + + String input = scanner.nextLine().trim(); + + switch (input) + { + case "1": + handleLogin(); + break; + + case "2": + handleRegister(); + break; + + case "3": + ConsoleStyle.printInfo("Goodbye! See you soon."); + System.exit(0); + break; + + default: + System.out.println("Invalid choice."); + } + } + + private void showUserMenu() + { + ConsoleStyle.printHeader("๐Ÿฝ๏ธ MAIN MENU - Welcome, " + currentUser.getUsername() + " ๐Ÿฝ๏ธ"); + System.out.println("1. View Menu"); + System.out.println("2. Place New Order"); + System.out.println("3. View Order History"); + System.out.println("4. Logout"); + System.out.println("5. Exit"); + ConsoleStyle.printLine(); + System.out.print("Choose an option: "); + + String input = scanner.nextLine().trim(); + + switch (input) + { + case "1"-> + menuService.showMenu(); + + case "2"-> + orderService.placeOrder(currentUser.getId()); + + case "3" -> + orderService.showOrderHistory(currentUser.getId()); + case "4"->{ + currentUser = null; + ConsoleStyle.printSuccess("Logged out successfully."); + } + case "5"->{ + ConsoleStyle.printInfo("Goodbye! See you soon."); + System.exit(0); + } + default-> + ConsoleStyle.printError("Invalid choice."); + } + } + + private void handleLogin() + { + System.out.println("\n--- Login ---"); + System.out.print("Username: "); + String username = scanner.nextLine().trim(); + + System.out.print("Password: "); + String password = scanner.nextLine(); + + User user = authService.login(username, password); + + if (user != null) + { + currentUser = user; + ConsoleStyle.printSuccess("Login successful! Welcome back, " + user.getUsername() + "."); + } + else + { + ConsoleStyle.printError("Invalid username or password."); + } + } + + private void handleRegister() + { + System.out.println("\n--- Register ---"); + System.out.print("Enter username: "); + String username = scanner.nextLine().trim(); + + System.out.print("Enter password: "); + String password = scanner.nextLine(); + + System.out.print("Enter email: "); + String email = scanner.nextLine().trim(); + + boolean success = authService.register(username, password, email); + + if (success) + { + ConsoleStyle.printSuccess("Registration successful! You can now login."); + } + else + { + ConsoleStyle.printError("Registration failed. Username might be taken."); + } + } +} + diff --git a/src/main/java/dev/ui/ConsoleStyle.java b/src/main/java/dev/ui/ConsoleStyle.java new file mode 100644 index 0000000..f342f98 --- /dev/null +++ b/src/main/java/dev/ui/ConsoleStyle.java @@ -0,0 +1,38 @@ +package dev.ui; + +public class ConsoleStyle +{ + public static final String RESET = "\u001B[0m"; + public static final String BOLD = "\u001B[1m"; + public static final String RED = "\u001B[31m"; + public static final String GREEN = "\u001B[32m"; + public static final String YELLOW = "\u001B[33m"; + public static final String BLUE = "\u001B[34m"; + public static final String CYAN = "\u001B[36m"; + + public static void printHeader(String title) { + System.out.println(CYAN + "===============================================" + RESET); + System.out.println(CYAN + BOLD + " " + title + RESET); + System.out.println(CYAN + "===============================================" + RESET); + } + + + public static void printSuccess(String message) + { + System.out.println(GREEN + "โœ” " + message + RESET); + } + + public static void printError(String message) + { + System.out.println(RED + "โœ˜ " + message + RESET); + } + + public static void printInfo(String message) { + System.out.println(YELLOW + "โ„น " + message + RESET); + } + + public static void printLine() + { + System.out.println(CYAN + "-----------------------------------------------" + RESET); + } +} \ No newline at end of file