Rumah  >  Artikel  >  hujung hadapan web  >  Reka Bentuk Aras Rendah: Sistem Pengundian - Menggunakan Nodejs

Reka Bentuk Aras Rendah: Sistem Pengundian - Menggunakan Nodejs

WBOY
WBOYasal
2024-08-31 13:03:07380semak imbas

Low-Level Design: Polling System - Using Nodejs

Jadual Kandungan

  1. Persediaan Pangkalan Data
    • Skema Pangkalan Data MySQL
    • ERD untuk Sistem Pengundian
  2. Persediaan Bahagian Belakang
    • Langkah 1: Mulakan Projek
    • Langkah 2: Struktur Projek
  3. Pelaksanaan API
    • Langkah 1: Sambungan Pangkalan Data (db/db.js)
    • Langkah 2: Pembolehubah Persekitaran (.env)
    • Langkah 3: Pengawal Poll (pengawal/pollController.js)
    • Langkah 4: Laluan Tinjauan (laluan/pollRoutes.js)
    • Langkah 5: Titik Masuk Pelayan (index.js)
  4. Pengendalian Ralat
  5. Ujian
  6. Kesimpulan

Sila rujuk artikel Reka Bentuk Asas Sistem Pengundian Aras Rendah - I

Mari kita pecahkan keseluruhan proses kepada langkah terperinci, termasuk persediaan pangkalan data, pelaksanaan API menggunakan Node.js dengan Express dan interaksi dengan MySQL. Kami akan meliputi:

Persediaan Pangkalan Data

Pertama, kami akan mentakrifkan skema untuk pangkalan data MySQL dan mencipta jadual yang diperlukan.

Skema Pangkalan Data MySQL

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
);
  • jadual tinjauan pendapat: Menyimpan maklumat tinjauan pendapat dengan pengecam unik, soalan dan cap masa penciptaan.

  • jadual pilihan: Menyimpan pilihan yang dikaitkan dengan tinjauan pendapat, dipautkan melalui poll_id.

  • jadual undian: Merekod setiap undian, memaut kepada tinjauan pendapat, pilihan dan pengguna.

    ERD untuk Sistem Pengundian

Entiti:

  1. Tinjauan pendapat: Mewakili tinjauan pendapat itu sendiri, dengan atribut seperti poll_id dan soalan.
  2. Pilihan: Mewakili pilihan yang tersedia untuk setiap tinjauan pendapat, dengan atribut seperti option_id dan option_text.
  3. Undi: Mewakili undian yang diberikan oleh pengguna, dengan atribut seperti vote_id, user_id dan cap masa.

Hubungan:

  1. Satu-ke-Banyak antara Undian dan Pilihan: Setiap tinjauan pendapat boleh mempunyai berbilang pilihan.
  2. Many-to-One antara Undi dan Pilihan: Setiap undian dikaitkan dengan satu pilihan.
  3. Many-to-One antara Undi dan Tinjauan: Setiap undian dipautkan kepada tinjauan pendapat tertentu.

Berikut ialah perihalan ERD:

  1. Jadual Undian:

    • poll_id (Kunci Utama)
    • soalan
    • dicipta_di
  2. Jadual Pilihan:

    • option_id (Kunci Utama)
    • poll_id (Kunci Asing merujuk polls.poll_id)
    • teks_pilihan
  3. Jadual Undian:

    • vote_id (Kunci Utama)
    • poll_id (Kunci Asing merujuk polls.poll_id)
    • option_id (Pilihan rujukan Kunci Asing.option_id)
    • id_pengguna
    • dicipta_di

Perhubungan akan diwakili dengan garisan antara entiti:

  • Tinjauan pendapatPilihan: Satu tinjauan pendapat boleh mempunyai banyak pilihan.
  • PilihanUndi: Satu pilihan boleh mempunyai banyak undi.
  • Tinjauan pendapatUndi: Satu tinjauan pendapat boleh mendapat banyak undian.

Persediaan Bahagian Belakang

Mari kita sediakan projek Node.js menggunakan Express dan MySQL.

Langkah 1: Mulakan Projek

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

  • ekspresi: Rangka kerja web untuk Node.js.
  • mysql2: Pelanggan MySQL untuk Node.js.
  • dotenv: Untuk mengurus pembolehubah persekitaran.

Langkah 2: Struktur Projek

Buat struktur asas untuk projek:

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

Pelaksanaan API

Langkah 1: Sambungan Pangkalan Data

Fail - 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;

Langkah 2: Pembolehubah Persekitaran

Fail - .env

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

Langkah 3: Pengawal Undian

Fail - pengawal/pollController.js

Fail ini akan melaksanakan semua operasi CRUD yang diperlukan untuk sistem pengundian.

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." });
    }
};

Langkah 4: Laluan Tinjauan

Fail - route/pollRoutes.js
Tentukan laluan untuk setiap titik akhir API:

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;

Langkah 5: Titik Masuk Pelayan

Fail - index.js
Akhir sekali, sediakan pelayan:

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}`);
});

Pengendalian Ralat

Setiap kaedah termasuk pengendalian ralat untuk isu biasa seperti input tidak sah, undian pendua, tinjauan pendapat atau pilihan yang hilang dan ralat pelayan.

  • Pengesahan Input: Semakan dilakukan untuk memastikan bahawa input adalah sah, seperti menyemak sama ada medan yang diperlukan ada dan diformat dengan betul.
  • Pengurusan Transaksi: Untuk operasi yang melibatkan berbilang pertanyaan (cth., mencipta atau mengemas kini tinjauan pendapat), transaksi digunakan untuk memastikan konsistensi.

Menguji

Uji setiap titik akhir menggunakan alatan seperti Posmen atau curl.

  • Buat Tinjauan: POST /api/polls dengan badan JSON yang mengandungi soalan dan pelbagai pilihan.
  • Kemas kini Tinjauan: PUT /api/polls/:pollId dengan soalan dan pilihan yang dikemas kini.
  • Padam Pungutan Suara: PADAM /api/polls/:pollId.
  • Undi dalam Tinjauan: POST /api/polls/:pollId/undi dengan userId dan pilihan.
  • Lihat Keputusan Pungutan Suara: DAPATKAN /api/polls/:pollId/results.

Kesimpulan

Ini ialah pelaksanaan modular komprehensif sistem pengundian dalam talian menggunakan Node.js, Express dan MySQL. Ia mengendalikan operasi asas CRUD dan memastikan konsistensi data dengan transaksi. Ia juga termasuk pengendalian ralat asas untuk menjadikan API lebih mantap dan mesra pengguna.

Sila rujuk artikel Reka Bentuk Aras Rendah Asas Sistem Mengundi - Saya

Lagi Butiran:

Dapatkan semua artikel berkaitan reka bentuk sistem
Hastag: SystemDesignWithZeeshanAli

reka bentuk sistemdenganzeeshanali

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

Atas ialah kandungan terperinci Reka Bentuk Aras Rendah: Sistem Pengundian - Menggunakan Nodejs. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn