Rumah > hujung hadapan web > tutorial js > Reka Bentuk Aras Rendah: Sistem Pengundian - Kes Tepi

Reka Bentuk Aras Rendah: Sistem Pengundian - Kes Tepi

WBOY
Lepaskan: 2024-08-31 14:43:33
asal
536 orang telah melayarinya

Low-Level Design: Polling System - Edge Cases

Jadual kandungan

Kes 1 - Mengendalikan Versi untuk Kemas Kini
Kes 2 - PollID menjadi UUID & bukan Kunci Utama
Kes 3 - Pilihan Kosong atau Tidak Sah
Kes 4 - Pilihan Pendua
Kes 5 - Had Panjang Soalan
Kes 6 - Tamat Pungutan Suara

Sila rujuk artikel berikut terlebih dahulu:

  1. Reka Bentuk Peringkat Rendah: Sistem Pengundian: Asas

  2. Reka Bentuk Peringkat Rendah: Sistem Pengundian - Menggunakan Node.js & SQL

Pengendalian Kes Tepi

Kes 1

Untuk mengurus kemas kini kepada soalan dan pilihan tinjauan pendapat sambil mengekalkan butiran sebelumnya yang dikaitkan dengan ID tinjauan pendapat yang sama, anda boleh melaksanakan sistem versi. Pendekatan ini membolehkan anda menjejaki data sejarah untuk setiap tinjauan pendapat, memastikan butiran lama dikekalkan walaupun selepas kemas kini.

Langkah 1: Perubahan Skema Pangkalan Data

  1. Kemas kini Jadual Undian

    • Tambahkan lajur current_version_id pada jadual tinjauan pendapat untuk menjejaki versi terbaru tinjauan pendapat.
  2. Buat Jadual Versi Pungutan Suara

    • Buat jadual baharu untuk menyimpan versi sejarah tinjauan pendapat.

Skema Pangkalan Data dikemas kini

CREATE DATABASE polling_system;

USE polling_system;

CREATE TABLE polls (
    poll_id INT AUTO_INCREMENT PRIMARY KEY,
    current_version_id INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (current_version_id) REFERENCES poll_versions(version_id) ON DELETE SET NULL
);

CREATE TABLE poll_versions (
    version_id INT AUTO_INCREMENT PRIMARY KEY,
    poll_id INT,
    question VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (poll_id) REFERENCES polls(poll_id) ON DELETE CASCADE
);

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
);
Salin selepas log masuk

Langkah 2: Perubahan Pelaksanaan API

Kemas kini Pengawal Undian

Ubah suai kaedah updatePoll untuk menyemak sama ada soalan telah berubah sebelum membuat versi baharu.

Fail: controllers/pollController.js
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 (current_version_id) VALUES (NULL)'
        );

        const pollId = result.insertId;

        const [versionResult] = await connection.execute(
            'INSERT INTO poll_versions (poll_id, question) VALUES (?, ?)',
            [pollId, question]
        );

        const versionId = versionResult.insertId;

        // Update the current version in the polls table
        await connection.execute(
            'UPDATE polls SET current_version_id = ? WHERE poll_id = ?',
            [versionId, 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(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 || !options || !Array.isArray(options) || options.length < 2) {
        return res.status(400).json({ message: "Invalid input data. At least two options are required." });
    }

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

        // Fetch the existing poll
        const [existingPoll] = await connection.execute(
            'SELECT question FROM poll_versions WHERE poll_id = (SELECT current_version_id FROM polls WHERE poll_id = ?)',
            [pollId]
        );

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

        const currentQuestion = existingPoll[0].question;

        // Check if the question has changed
        if (currentQuestion !== question) {
            // Create a new version since the question has changed
            const [versionResult] = await connection.execute(
                'INSERT INTO poll_versions (poll_id, question) VALUES (?, ?)',
                [pollId, question]
            );

            const versionId = versionResult.insertId;

            // Update the current version in the polls table
            await connection.execute(
                'UPDATE polls SET current_version_id = ? WHERE poll_id = ?',
                [versionId, pollId]
            );
        }

        // Remove old options and insert new ones
        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);
        await connection.rollback();
        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." });
    }
};
Salin selepas log masuk

Langkah 3: Kemas kini Laluan Undian

Pastikan laluan ditakrifkan dengan betul dalam pollRoutes.js anda.

Fail: route/pollRoutes.js
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;
Salin selepas log masuk

Ringkasan Perubahan

  1. Pangkalan Data:

    • Mengemas kini jadual tinjauan pendapat untuk memasukkan current_version_id.
    • Mencipta jadual poll_versions untuk menjejak versi soalan.
    • Jadual pilihan dan undian kekal tidak berubah.
  2. API:

    • Mencipta kaedah createPoll baharu untuk memulakan tinjauan pendapat dan versi.
    • Mengemas kini kaedah updatePoll untuk menyemak perubahan soalan sebelum membuat versi baharu.
    • Kaedah tambahan untuk mengundi dan melihat keputusan tinjauan pendapat.
  3. Penghalaan:

    • Memastikan semua laluan yang diperlukan ditakrifkan untuk mengendalikan penciptaan tinjauan pendapat, kemas kini, pengundian dan keputusan.

Kes 2

Untuk mengendalikan senario di mana pollId diperlukan untuk menjadi UUID (Pengecam Unik Sejagat).

Berikut ialah langkah untuk melaksanakan UUID untuk thepollId dalam sistem pengundian anda tanpa memberikan kod:

Langkah-langkah untuk Melaksanakan UUID untuk ID Tinjauan

  1. ** Kemas Kini Skema Pangkalan Data:**

    • Ubah suai jadual tinjauan pendapat, versi_pungutan suara, pilihan dan undian untuk menggunakan CHAR(36) untuk id_pungutan suara dan bukannya integer.
    • Buat jadual poll_versions baharu untuk menyimpan versi sejarah soalan tinjauan pendapat dan pilihan yang dipautkan oleh UUID.
  2. ** Generasi UUID:**

    • Tentukan kaedah untuk menjana UUID. Anda boleh menggunakan perpustakaan atau fungsi terbina dalam dalam persekitaran aplikasi anda untuk mencipta UUID.
  3. ** Cipta Logik Tinjauan:**

    • Apabila membuat tinjauan pendapat baharu, hasilkan UUID dan gunakannya sebagai poll_id.
    • Masukkan rekod tinjauan pendapat baharu ke dalam jadual tinjauan pendapat.
    • Masukkan soalan awal ke dalam jadual poll_versions dan pautkannya dengan UUID yang dijana.
  4. ** Kemas kini Logik Undian:**

    • Apabila mengemas kini tinjauan pendapat:
  5. Semak sama ada soalan telah berubah.

    • Jika soalan telah berubah, buat entri versi baharu dalam jadual thepoll_versions untuk menyimpan soalan dan pilihan lama.
    • Kemas kini jadual tinjauan pendapat dengan soalan baharu dan pilihan yang perlu.
  6. ** Logik Pengundian:**

    • Kemas kini mekanisme pengundian untuk memastikan ia menggunakan UUID sebagai poll_id.
  7. Sahkan bahawa UUID yang disediakan dalam permintaan undian wujud dalam jadual tinjauan pendapat.

  8. ** Kemas Kini API:**

    • Ubah suai titik akhir API untuk menerima dan mengembalikan UUID untuk poll_id.
    • Pastikan semua operasi API (buat, kemas kini, padam, undi) merujuk format UUID secara konsisten.
  9. ** Ujian:**

    • Uji aplikasi dengan teliti untuk memastikan UUID dikendalikan dengan betul dalam semua senario (penciptaan, kemas kini, pengundian dan mendapatkan semula keputusan tinjauan pendapat).
  10. ** Dokumentasi:**

    • Kemas kini dokumentasi API anda untuk mencerminkan perubahan dalam format thepoll_id dan sebarang kelakuan baharu yang berkaitan dengan versi dan penggunaan UUID.

Dengan mengikut langkah ini, anda boleh berjaya melaksanakan UUID untuk pollId dalam sistem pengundian anda sambil memastikan integriti data dan penjejakan sejarah.


Kes 3

Pilihan Kosong atau Tidak Sah

Pendekatan Pengesahan:

  • Pengesahan Input API: Laksanakan semakan dalam titik akhir API anda untuk mengesahkan bahawa pilihan yang disediakan dalam kandungan permintaan tidak kosong dan memenuhi kriteria tertentu (cth., tiada aksara khas jika tidak dibenarkan).
  • Mekanisme Maklum Balas: Berikan mesej ralat yang jelas kepada pengguna jika pilihan tidak sah atau kosong, membimbing mereka untuk membetulkan input mereka.

Kes 4

Pilihan Pendua

Semakan Keunikan:

  • Pengesahan Pra-Sisip: Sebelum menambah pilihan pada tinjauan pendapat, semak pilihan sedia ada dalam pangkalan data untuk pendua. Ini boleh dilakukan dengan menanyakan jadual pilihan menggunakan ID tinjauan pendapat dan membandingkannya dengan pilihan baharu.
  • Maklum Balas Pengguna: Jika pilihan pendua dikesan, kembalikan mesej ralat yang bermakna untuk memaklumkan pengguna pilihan yang menjadi pendua, membolehkan mereka mengubah suai input mereka dengan sewajarnya.

Kes 5

Had Panjang Soalan

Had Perwatakan:

  • Pengesahan API: Tetapkan had aksara maksimum untuk soalan tinjauan pendapat dan pilihan dalam API anda. Ini boleh dilakukan dengan menyemak panjang soalan dan setiap pilihan semasa proses penciptaan dan kemas kini.
  • Maklum Balas Antara Muka Pengguna: Laksanakan pengesahan pihak pelanggan untuk memberikan maklum balas segera kepada pengguna apabila mereka melebihi had aksara semasa menaip, meningkatkan pengalaman pengguna.

Kes 6

Tamat Pungutan Suara

Mekanisme Tamat Tempoh:

  • Pengurusan Cap Masa: Tambahkan medan cap masa pada jadual tinjauan pendapat untuk merekodkan apabila setiap tinjauan pendapat dibuat dan secara pilihan medan lain untuk tarikh tamat tempoh.
  • Semakan Berjadual: Laksanakan kerja latar belakang atau tugas cron yang menyemak secara berkala untuk tinjauan yang telah tamat tempoh dan menandakannya sebagai tidak aktif dalam pangkalan data. Ini juga boleh termasuk menghalang undian pada tinjauan pendapat yang telah tamat tempoh.
  • Pemberitahuan Pengguna: Secara pilihan, maklumkan kepada pencipta tinjauan pendapat dan peserta tentang tarikh tamat tempoh yang akan berlaku, membenarkan mereka terlibat dengan tinjauan pendapat sebelum ia menjadi tidak aktif.

Sila rujuk artikel berikut terlebih dahulu:

  1. Reka Bentuk Peringkat Rendah: Sistem Pengundian: Asas

  2. Reka Bentuk Peringkat Rendah: Sistem Pengundian - Menggunakan Node.js & SQL

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 - Kes Tepi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
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
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan