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

Reka Bentuk Aras Rendah: Sistem Pengundian - Kes Tepi

Aug 31, 2024 pm 02:43 PM

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

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

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;

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!

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

Alat AI Hot

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Stock Market GPT

Stock Market GPT

Penyelidikan pelaburan dikuasakan AI untuk keputusan yang lebih bijak

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Penapisan Multi-Condition JavaScript: Melaksanakan penapisan produk dinamik berdasarkan dan/atau logik Penapisan Multi-Condition JavaScript: Melaksanakan penapisan produk dinamik berdasarkan dan/atau logik Aug 22, 2025 am 10:00 AM

Tutorial ini memperkenalkan secara terperinci bagaimana menggunakan JavaScript untuk melaksanakan fungsi penapisan dinamik pelbagai syarat, yang membolehkan pengguna menapis produk berdasarkan pelbagai atribut seperti warna dan saiz. Melalui struktur HTML yang jelas dan contoh kod JavaScript, artikel menunjukkan cara mengendalikan dan atau logik secara fleksibel untuk memenuhi keperluan penapisan pengguna yang kompleks dan memberikan cadangan pengoptimuman.

Bagaimana anda memilih elemen dengan atribut data dalam JavaScript? Bagaimana anda memilih elemen dengan atribut data dalam JavaScript? Aug 30, 2025 am 01:57 AM

Anda boleh memilih elemen dengan atribut data dalam JavaScript melalui pemilih atribut CSS, dan gunakan document.QuerySelector () atau document.QuerySelectorAll () kaedah untuk mencapai matlamat ini. 1. Gunakan [data-attribute] untuk memilih elemen dengan atribut data yang ditentukan (sebarang nilai); 2. Gunakan [data-attribute = "nilai"] untuk memilih elemen yang nilai atributnya sesuai; 3. Akses atribut data melalui elemen.dataset, di mana data-us-id sepadan dengan dataset.userid (ganti

Pytest dan Selenium: Strategi pelaksanaan untuk ujian yang didorong oleh data dinamik Pytest dan Selenium: Strategi pelaksanaan untuk ujian yang didorong oleh data dinamik Aug 30, 2025 am 06:00 AM

Artikel ini bertujuan untuk menyelesaikan masalah yang @pytest.mark.parametrize decorator tidak dapat secara langsung mengendalikan data yang dijana pada runtime apabila menggunakan pytest dan selenium untuk ujian yang didorong data dinamik. Kami akan meneroka batasan pytest.mark.parametrize secara mendalam, dan memperkenalkan secara terperinci bagaimana untuk melaksanakan ujian parameter secara anggun berdasarkan pemerolehan data dinamik selenium melalui Pytest's PYTest_Generate_Tests Hook berfungsi untuk memastikan fleksibiliti dan kecekapan kes ujian.

Mengoptimumkan pengendalian acara lompat luaran dinamik di tetingkap pop timbul jQuery Mengoptimumkan pengendalian acara lompat luaran dinamik di tetingkap pop timbul jQuery Sep 01, 2025 am 11:48 AM

Artikel ini bertujuan untuk menyelesaikan masalah mengalihkan butang redirect pautan luaran dalam tetingkap pop-up jQuery menyebabkan kesilapan lompat. Apabila pengguna mengklik pelbagai pautan luaran dalam penggantian, butang lompat di pop timbul mungkin selalu menunjuk pada pautan pertama yang diklik. Penyelesaian teras adalah dengan menggunakan kaedah off ('klik') untuk membatalkan pengendali acara lama sebelum setiap mengikat peristiwa baru, memastikan bahawa tingkah laku lompat sentiasa menunjuk kepada URL sasaran terkini, dengan itu mencapai pengalihan pautan yang tepat dan terkawal.

Bina kaunter JavaScript berjalan dengan hari bekerja dan waktu bekerja Bina kaunter JavaScript berjalan dengan hari bekerja dan waktu bekerja Aug 31, 2025 am 06:30 AM

Artikel ini memperincikan bagaimana untuk membina kaunter masa yang tepat menggunakan JavaScript. Kaunter itu bertambah sekali seminit, tetapi hanya berjalan dalam hari kerja pratetap (Isnin hingga Jumaat) dan jam kerja (seperti 6 pagi hingga 8 malam). Ia boleh menjeda kenaikan semasa waktu tidak bekerja tetapi memaparkan nilai semasa dan menetapkan semula secara automatik pada hari pertama setiap bulan, memastikan ketepatan dan fleksibiliti logik pengiraan.

Mengoptimumkan Rendering Komponen React: Selesaikan masalah yang disebabkan oleh tetikus yang melayang Mengoptimumkan Rendering Komponen React: Selesaikan masalah yang disebabkan oleh tetikus yang melayang Aug 22, 2025 pm 01:36 PM

Artikel ini bertujuan untuk menyelesaikan isu-isu yang lebih baik yang dicetuskan oleh Onmouseover dalam aplikasi React. Dengan menggantikan OnMouseOver dengan OnMouseEnter dan digabungkan dengan OnMouseout dengan OnMouseleave, anda boleh mengurangkan komponen yang tidak perlu membuat semula dan meningkatkan prestasi aplikasi, terutamanya apabila berurusan dengan banyak komponen. Artikel ini akan menyediakan kod sampel dan penjelasan terperinci untuk membantu pemaju memahami dan menggunakan teknik pengoptimuman ini.

js mendapat ketinggian dan lebar elemen js mendapat ketinggian dan lebar elemen Aug 22, 2025 pm 04:16 PM

Useclientwidth/clientheightforvisiblecontentareaincludingpadding; 2.useOffsetWidth/offsetheightfortotalrenderedSizeIncludingContent, danborder;

Bagaimana unsur -unsur DOM yang dibuat secara dinamik diakses dan dikendalikan oleh skrip yang dimuatkan Bagaimana unsur -unsur DOM yang dibuat secara dinamik diakses dan dikendalikan oleh skrip yang dimuatkan Aug 30, 2025 am 11:57 AM

Artikel ini menerangkan bagaimana skrip JavaScript dapat diakses dengan berkesan dan dimanipulasi apabila ia dimuatkan dan dilaksanakan sebelum penciptaan elemen DOM dalam pembangunan web. Kami akan memperkenalkan tiga strategi teras: secara langsung lulus rujukan elemen melalui nilai pulangan fungsi, menggunakan peristiwa tersuai untuk mencapai komunikasi antara modul, dan menggunakan MutationObserver untuk mendengar perubahan struktur DOM. Kaedah ini dapat membantu pemaju menyelesaikan cabaran antara masa pelaksanaan JavaScript dan pemuatan kandungan dinamik, memastikan skrip dapat mengendalikan unsur-unsur dengan betul, seperti menjadikannya drag-mampu.

See all articles