Heim  >  Artikel  >  Web-Frontend  >  Low-Level-Design: Polling-System – Verwendung von Nodejs

Low-Level-Design: Polling-System – Verwendung von Nodejs

WBOY
WBOYOriginal
2024-08-31 13:03:07351Durchsuche

Low-Level Design: Polling System - Using Nodejs

Inhaltsverzeichnis

  1. Datenbank-Setup
    • MySQL-Datenbankschema
    • ERD für das Wahlsystem
  2. Backend-Setup
    • Schritt 1: Initialisieren Sie das Projekt
    • Schritt 2: Projektstruktur
  3. API-Implementierung
    • Schritt 1: Datenbankverbindung (db/db.js)
    • Schritt 2: Umgebungsvariablen (.env)
    • Schritt 3: Poll Controller (controllers/pollController.js)
    • Schritt 4: Routen abfragen (routes/pollRoutes.js)
    • Schritt 5: Server-Einstiegspunkt (index.js)
  4. Fehlerbehandlung
  5. Testen
  6. Fazit

Bitte lesen Sie den Artikel Polling System Basic Low-Level Design – I

Lassen Sie uns den gesamten Prozess in detaillierte Schritte unterteilen, einschließlich der Datenbankeinrichtung, der API-Implementierung mit Node.js mit Express und der Interaktion mit MySQL. Wir werden Folgendes behandeln:

Datenbank-Setup

Zuerst definieren wir das Schema für die MySQL-Datenbank und erstellen die erforderlichen Tabellen.

MySQL-Datenbankschema

CREATE DATABASE polling_system;

USE polling_system;

CREATE TABLE polls (
    poll_id INT AUTO_INCREMENT PRIMARY KEY,
    question VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE options (
    option_id INT AUTO_INCREMENT PRIMARY KEY,
    poll_id INT,
    option_text VARCHAR(255) NOT NULL,
    FOREIGN KEY (poll_id) REFERENCES polls(poll_id) ON DELETE CASCADE
);

CREATE TABLE votes (
    vote_id INT AUTO_INCREMENT PRIMARY KEY,
    poll_id INT,
    user_id VARCHAR(255) NOT NULL,
    option_id INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (poll_id) REFERENCES polls(poll_id) ON DELETE CASCADE,
    FOREIGN KEY (option_id) REFERENCES options(option_id) ON DELETE CASCADE
);
  • Umfragetabelle: Speichert Umfrageinformationen mit einer eindeutigen Kennung, Frage und Erstellungszeitstempel.

  • Optionstabelle: Speichert die mit einer Umfrage verknüpften Optionen, verknüpft über poll_id.

  • Abstimmungstabelle: Zeichnet jede Abstimmung auf und verknüpft sie mit der Umfrage, der Option und dem Benutzer.

    ERD für das Wahlsystem

Entitäten:

  1. Umfragen: Stellt die Umfrage selbst dar, mit Attributen wie poll_id und Frage.
  2. Optionen: Stellt die für jede Umfrage verfügbaren Optionen dar, mit Attributen wie option_id und option_text.
  3. Stimmen: Stellt die von Benutzern abgegebenen Stimmen dar, mit Attributen wie vote_id, user_id und Zeitstempeln.

Beziehungen:

  1. Eins-zu-viele zwischen Umfragen und Optionen: Jede Umfrage kann mehrere Optionen haben.
  2. Many-to-One zwischen Stimmen und Optionen: Jede Stimme ist einer Option zugeordnet.
  3. Many-to-One zwischen Abstimmungen und Umfragen: Jede Abstimmung ist mit einer bestimmten Umfrage verknüpft.

Hier ist eine Beschreibung des ERD:

  1. Umfragetabelle:

    • poll_id (Primärschlüssel)
    • Frage
    • erstellt_at
  2. Optionstabelle:

    • option_id (Primärschlüssel)
    • poll_id (Fremdschlüssel, der auf polls.poll_id verweist)
    • Optionstext
  3. Abstimmungstabelle:

    • vote_id (Primärschlüssel)
    • poll_id (Fremdschlüssel, der auf polls.poll_id verweist)
    • option_id (Fremdschlüssel, der auf options.option_id verweist)
    • Benutzer-ID
    • erstellt_at

Die Beziehungen würden durch Linien zwischen den Entitäten dargestellt:

  • UmfragenOptionen: Eine Umfrage kann viele Optionen haben.
  • OptionenStimmen: Eine Option kann viele Stimmen haben.
  • UmfragenStimmen: Eine Umfrage kann viele Stimmen haben.

Backend-Setup

Lassen Sie uns ein Node.js-Projekt mit Express und MySQL einrichten.

Schritt 1: Initialisieren Sie das Projekt

mkdir polling-system
cd polling-system
npm init -y
npm install express mysql2 dotenv

  • Express: Ein Web-Framework für Node.js.
  • mysql2: Ein MySQL-Client für Node.js.
  • dotenv: Zur Verwaltung von Umgebungsvariablen.

Schritt 2: Projektstruktur

Erstellen Sie eine Grundstruktur für das Projekt:

polling-system/
│
├── .env
├── index.js
├── db/
│   └── db.js
├── routes/
│   └── pollRoutes.js
└── controllers/
    └── pollController.js

API-Implementierung

Schritt 1: Datenbankverbindung

Datei – db/db.js

const mysql = require('mysql2/promise');
require('dotenv').config();

const pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    waitForConnections: true,
    connectionLimit: 10,
    queueLimit: 0
});

module.exports = pool;

Schritt 2: Umgebungsvariablen

Datei – .env

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=yourpassword
DB_NAME=polling_system
PORT=3000

Schritt 3: Poll-Controller

Datei – controllers/pollController.js

Diese Datei implementiert alle notwendigen CRUD-Vorgänge für das Abfragesystem.

const pool = require('../db/db');

// Create Poll
exports.createPoll = async (req, res) => {
    const { question, options } = req.body;

    if (!question || !options || !Array.isArray(options) || options.length < 2) {
        return res.status(400).json({ message: "Invalid input data. Question and at least two options are required." });
    }

    try {
        const connection = await pool.getConnection();
        await connection.beginTransaction();

        const [result] = await connection.execute(
            'INSERT INTO polls (question) VALUES (?)',
            [question]
        );

        const pollId = result.insertId;

        const optionQueries = options.map(option => {
            return connection.execute(
                'INSERT INTO options (poll_id, option_text) VALUES (?, ?)',
                [pollId, option]
            );
        });

        await Promise.all(optionQueries);

        await connection.commit();
        connection.release();

        res.status(201).json({ pollId, message: "Poll created successfully." });

    } catch (error) {
        console.error("Error creating poll:", error.message);
        res.status(500).json({ message: "Error creating poll." });
    }
};

// Update Poll
exports.updatePoll = async (req, res) => {
    const { pollId } = req.params;
    const { question, options } = req.body;

    if (!pollId || !question || !options || !Array.isArray(options) || options.length < 2) {
        return res.status(400).json({ message: "Invalid input data. Question and at least two options are required." });
    }

    try {
        const connection = await pool.getConnection();
        await connection.beginTransaction();

        const [pollResult] = await connection.execute(
            'UPDATE polls SET question = ? WHERE poll_id = ?',
            [question, pollId]
        );

        if (pollResult.affectedRows === 0) {
            await connection.rollback();
            connection.release();
            return res.status(404).json({ message: "Poll not found." });
        }

        await connection.execute('DELETE FROM options WHERE poll_id = ?', [pollId]);

        const optionQueries = options.map(option => {
            return connection.execute(
                'INSERT INTO options (poll_id, option_text) VALUES (?, ?)',
                [pollId, option]
            );
        });

        await Promise.all(optionQueries);

        await connection.commit();
        connection.release();

        res.status(200).json({ message: "Poll updated successfully." });

    } catch (error) {
        console.error("Error updating poll:", error.message);
        res.status(500).json({ message: "Error updating poll." });
    }
};

// Delete Poll
exports.deletePoll = async (req, res) => {
    const { pollId } = req.params;

    try {
        const connection = await pool.getConnection();

        const [result] = await connection.execute(
            'DELETE FROM polls WHERE poll_id = ?',
            [pollId]
        );

        connection.release();

        if (result.affectedRows === 0) {
            return res.status(404).json({ message: "Poll not found." });
        }

        res.status(200).json({ message: "Poll deleted successfully." });

    } catch (error) {
        console.error("Error deleting poll:", error.message);
        res.status(500).json({ message: "Error deleting poll." });
    }
};

// Vote in Poll
exports.voteInPoll = async (req, res) => {
    const { pollId } = req.params;
    const { userId, option } = req.body;

    if (!userId || !option) {
        return res.status(400).json({ message: "User ID and option are required." });
    }

    try {
        const connection = await pool.getConnection();

        const [userVote] = await connection.execute(
            'SELECT * FROM votes WHERE poll_id = ? AND user_id = ?',
            [pollId, userId]
        );

        if (userVote.length > 0) {
            connection.release();
            return res.status(400).json({ message: "User has already voted." });
        }

        const [optionResult] = await connection.execute(
            'SELECT option_id FROM options WHERE poll_id = ? AND option_text = ?',
            [pollId, option]
        );

        if (optionResult.length === 0) {
            connection.release();
            return res.status(404).json({ message: "Option not found." });
        }

        const optionId = optionResult[0].option_id;

        await connection.execute(
            'INSERT INTO votes (poll_id, user_id, option_id) VALUES (?, ?, ?)',
            [pollId, userId, optionId]
        );

        connection.release();

        res.status(200).json({ message: "Vote cast successfully." });

    } catch (error) {
        console.error("Error casting vote:", error.message);
        res.status(500).json({ message: "Error casting vote." });
    }
};

// View Poll Results
exports.viewPollResults = async (req, res) => {
    const { pollId } = req.params;

    try {
        const connection = await pool.getConnection();

        const [poll] = await connection.execute(
            'SELECT * FROM polls WHERE poll_id = ?',
            [pollId]
        );

        if (poll.length === 0) {
            connection.release();
            return res.status(404).json({ message: "Poll not found." });
        }

        const [options] = await connection.execute(
            'SELECT option_text, COUNT(votes.option_id) as vote_count FROM options ' +
            'LEFT JOIN votes ON options.option_id = votes.option_id ' +
            'WHERE options.poll_id = ? GROUP BY options.option_id',
            [pollId]
        );

        connection.release();

        res.status(200).json({
            pollId: poll[0].poll_id,
            question: poll[0].question,
            results: options.reduce((acc, option) => {
                acc[option.option_text] = option.vote_count;
                return acc;
            }, {})
        });

    } catch (error) {
        console.error("Error viewing poll results:", error.message);
        res.status(500).json({ message: "Error viewing poll results." });
    }
};

Schritt 4: Routen abfragen

Datei – Routen/pollRoutes.js
Definieren Sie die Routen für jeden API-Endpunkt:

const express = require('express');
const router = express.Router();
const pollController = require('../controllers/pollController');

//

 Routes
router.post('/polls', pollController.createPoll);
router.put('/polls/:pollId', pollController.updatePoll);
router.delete('/polls/:pollId', pollController.deletePoll);
router.post('/polls/:pollId/vote', pollController.voteInPoll);
router.get('/polls/:pollId/results', pollController.viewPollResults);

module.exports = router;

Schritt 5: Server-Einstiegspunkt

Datei – index.js
Richten Sie abschließend den Server ein:

const express = require('express');
const pollRoutes = require('./routes/pollRoutes');
require('dotenv').config();

const app = express();
app.use(express.json());

// Routes
app.use('/api', pollRoutes);

// Error Handling Middleware
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ message: "Internal server error" });
});

// Start Server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Fehlerbehandlung

Jede Methode umfasst die Fehlerbehandlung für häufige Probleme wie ungültige Eingaben, doppelte Stimmen, fehlende Umfragen oder Optionen und Serverfehler.

  • Eingabevalidierung: Es werden Prüfungen durchgeführt, um sicherzustellen, dass die Eingaben gültig sind, z. B. wird überprüft, ob die erforderlichen Felder vorhanden und ordnungsgemäß formatiert sind.
  • Transaktionsverwaltung: Für Vorgänge mit mehreren Abfragen (z. B. das Erstellen oder Aktualisieren von Umfragen) werden Transaktionen verwendet, um die Konsistenz sicherzustellen.

Testen

Testen Sie jeden Endpunkt mit Tools wie Postman oder Curl.

  • Umfrage erstellen: POST /api/polls mit einem JSON-Text, der die Frage und eine Reihe von Optionen enthält.
  • Umfrage aktualisieren: PUT /api/polls/:pollId mit aktualisierter Frage und Optionen.
  • Umfrage löschen: DELETE /api/polls/:pollId.
  • In der Umfrage abstimmen: POST /api/polls/:pollId/vote mit Benutzer-ID und Option.
  • Umfrageergebnisse anzeigen: GET /api/polls/:pollId/results.

Abschluss

Dies ist eine umfassende modulare Implementierung eines Online-Umfragesystems mit Node.js, Express und MySQL. Es verwaltet die grundlegenden CRUD-Operationen und stellt die Datenkonsistenz bei Transaktionen sicher. Es umfasst auch eine grundlegende Fehlerbehandlung, um die API robuster und benutzerfreundlicher zu machen.

Bitte lesen Sie den Artikel Polling System Basic Low-Level Design – I

Weitere Details:

Holen Sie sich alle Artikel zum Thema Systemdesign
Hastag: SystemDesignWithZeeshanAli

systemdesignwithzeeshanali

Git: https://github.com/ZeeshanAli-0704/SystemDesignWithZeeshanAli

Das obige ist der detaillierte Inhalt vonLow-Level-Design: Polling-System – Verwendung von Nodejs. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn