Maison > interface Web > js tutoriel > Conception de bas niveau : système d'interrogation - Cas extrêmes

Conception de bas niveau : système d'interrogation - Cas extrêmes

WBOY
Libérer: 2024-08-31 14:43:33
original
560 Les gens l'ont consulté

Low-Level Design: Polling System - Edge Cases

Table des matières

Cas 1 – Gérer la gestion des versions pour la mise à jour
Cas 2 - PollID doit être comme UUID et non comme clé primaire
Cas 3 - Options vides ou invalides
Cas 4 - Options de duplication
Cas 5 - Limite de longueur des questions
Cas 6 - Expiration du sondage

Veuillez vous référer d'abord aux articles suivants :

  1. Conception de bas niveau : système d'interrogation : de base

  2. Conception de bas niveau : système de sondage - Utilisation de Node.js et SQL

Traitement des cas extrêmes

Cas 1

Pour gérer les mises à jour de la question et des options d'un sondage tout en conservant les détails précédents associés au même identifiant de sondage, vous pouvez implémenter un système de versionnage. Cette approche vous permet de garder une trace des données historiques pour chaque sondage, garantissant ainsi que les anciens détails sont préservés même après les mises à jour.

Étape 1 : Modifications du schéma de base de données

  1. Mettre à jour le tableau des sondages

    • Ajoutez une colonne current_version_id à la table des sondages pour suivre la dernière version du sondage.
  2. Créer le tableau des versions de sondage

    • Créez un nouveau tableau pour stocker les versions historiques des sondages.

Schéma de base de données mis à jour

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
);
Copier après la connexion

Étape 2 : Modifications de la mise en œuvre de l'API

Mettre à jour le contrôleur de sondage

Modifiez la méthode updatePoll pour vérifier si la question a changé avant de créer une nouvelle version.

Fichier : 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." });
    }
};
Copier après la connexion

Étape 3 : Mettre à jour les itinéraires de sondage

Assurez-vous que les itinéraires sont correctement définis dans votre pollRoutes.js.

Fichier : routes/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;
Copier après la connexion

Résumé des modifications

  1. Base de données :

    • Mise à jour du tableau des sondages pour inclure current_version_id.
    • Création de la table poll_versions pour suivre les versions des questions.
    • Les tableaux d'options et de votes restent inchangés.
  2. API :

    • Création d'une nouvelle méthode createPoll pour initialiser les sondages et les versions.
    • Mise à jour de la méthode updatePoll pour vérifier les modifications apportées aux questions avant de créer une nouvelle version.
    • Ajout de méthodes pour voter et afficher les résultats du sondage.
  3. Routage :

    • Veiller à ce que tous les itinéraires nécessaires soient définis pour gérer la création, les mises à jour, le vote et les résultats des sondages.

Cas 2

Pour gérer un scénario dans lequel le pollId doit être un UUID (Universally Unique Identifier).

Voici les étapes pour implémenter les UUID pour thepollId dans votre système de sondage sans fournir de code :

Étapes pour implémenter l'UUID pour l'ID de sondage

  1. ** Mise à jour du schéma de base de données :**

    • Modifiez les tables polls, poll_versions, options et votes pour utiliser CHAR(36) pour poll_id au lieu d'un entier.
    • Créez une nouvelle table poll_versions pour stocker les versions historiques des questions et options de sondage liées par l'UUID.
  2. ** Génération UUID :**

    • Décidez d'une méthode de génération d'UUID. Vous pouvez utiliser une bibliothèque ou des fonctions intégrées dans votre environnement d'application pour créer des UUID.
  3. ** Créer une logique de sondage :**

    • Lors de la création d'un nouveau sondage, générez un UUID et utilisez-le comme poll_id.
    • Insérez le nouvel enregistrement de sondage dans le tableau des sondages.
    • Insérez la question initiale dans la table poll_versions et liez-la à l'UUID généré.
  4. ** Mettre à jour la logique du sondage :**

    • Lors de la mise à jour d'un sondage :
  5. Vérifiez si la question a changé.

    • Si la question a changé, créez une nouvelle entrée de version dans la table poll_versions pour stocker l'ancienne question et les options.
    • Mettez à jour le tableau des sondages avec la nouvelle question et les nouvelles options si nécessaire.
  6. ** Logique de vote :**

    • Mettez à jour le mécanisme de vote pour vous assurer qu'il utilise l'UUID comme poll_id.
  7. Vérifiez que l'UUID fourni dans la demande de vote existe dans le tableau des sondages.

  8. ** Mises à jour de l'API :**

    • Modifiez les points de terminaison de l'API pour accepter et renvoyer les UUID pour poll_id.
    • Assurez-vous que toutes les opérations de l'API (créer, mettre à jour, supprimer, voter) font référence au format UUID de manière cohérente.
  9. ** Test :**

    • Testez minutieusement l'application pour vous assurer que les UUID sont gérés correctement dans tous les scénarios (création, mises à jour, vote et récupération des résultats du sondage).
  10. **Documentations :**

    • Mettez à jour la documentation de votre API pour refléter les modifications apportées au format poll_id et tout nouveau comportement lié au contrôle de version et à l'utilisation de l'UUID.

En suivant ces étapes, vous pouvez implémenter avec succès les UUID pour pollId dans votre système de sondage tout en garantissant l'intégrité des données et le suivi historique.


Cas 3

Options vides ou invalides

Approche de validation :

  • Validation des entrées API : Mettez en œuvre des vérifications dans vos points de terminaison d'API pour vérifier que les options fournies dans le corps de la demande ne sont pas vides et répondent à des critères spécifiques (par exemple, aucun caractère spécial s'il n'est pas autorisé).
  • Mécanisme de rétroaction : Fournissez des messages d'erreur clairs à l'utilisateur si les options sont invalides ou vides, en le guidant pour corriger sa saisie.

Cas 4

Options de duplication

Contrôle d'unicité :

  • Validation pré-insertion : Avant d'ajouter des options à un sondage, vérifiez les options existantes dans la base de données pour les doublons. Cela peut être fait en interrogeant la table d'options à l'aide de l'ID du sondage et en la comparant aux nouvelles options.
  • Commentaires de l'utilisateur : Si une option en double est détectée, renvoie un message d'erreur significatif pour informer l'utilisateur quelles options sont des doublons, lui permettant ainsi de modifier sa saisie en conséquence.

Cas 5

Limite de longueur des questions

Limitation des caractères :

  • Validation de l'API : Définissez une limite maximale de caractères pour les questions et les options de sondage au sein de votre API. Cela peut être fait en vérifiant la longueur de la question et chaque option lors des processus de création et de mise à jour.
  • Commentaires sur l'interface utilisateur : Implémentez la validation côté client pour fournir un retour instantané aux utilisateurs lorsqu'ils dépassent la limite de caractères lors de la saisie, améliorant ainsi l'expérience utilisateur.

Cas 6

Expiration du sondage

Mécanisme d'expiration :

  • Gestion de l'horodatage : Ajoutez un champ d'horodatage à la table des sondages pour enregistrer la date de création de chaque sondage et éventuellement un autre champ pour la date d'expiration.
  • Vérifications programmées : Implémentez un travail en arrière-plan ou une tâche cron qui vérifie périodiquement les sondages expirés et les marque comme inactifs dans la base de données. Cela peut également inclure l’interdiction des votes sur les sondages expirés.
  • Notifications utilisateur : Vous pouvez éventuellement informer les créateurs du sondage et les participants des dates d'expiration imminentes, leur permettant ainsi de participer au sondage avant qu'il ne devienne inactif.

Veuillez vous référer d'abord aux articles suivants :

  1. Conception de bas niveau : système d'interrogation : de base

  2. Conception de bas niveau : système de sondage - Utilisation de Node.js et SQL

Plus de détails :

Obtenez tous les articles liés à la conception de systèmes
Hastag : SystemDesignWithZeeshanAli

conception du système aveczeeshanali

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal