Maison> interface Web> js tutoriel> le corps du texte

Conception de bas niveau : système d'interrogation - Utilisation de Nodejs

WBOY
Libérer: 2024-08-31 13:03:07
original
194 Les gens l'ont consulté

Low-Level Design: Polling System - Using Nodejs

Table des matières

  1. Configuration de la base de données
    • Schéma de base de données MySQL
    • ERD pour le système de vote
  2. Configuration du back-end
    • Étape 1 : Initialiser le projet
    • Étape 2 : Structure du projet
  3. Implémentation de l'API
    • Étape 1 : Connexion à la base de données (db/db.js)
    • Étape 2 : Variables d'environnement (.env)
    • Étape 3 : Contrôleur de sondage (controllers/pollController.js)
    • Étape 4 : interroger les itinéraires (routes/pollRoutes.js)
    • Étape 5 : Point d'entrée du serveur (index.js)
  4. Gestion des erreurs
  5. Tests
  6. Conclusion

Veuillez vous référer à l'article Conception de base de bas niveau du système d'interrogation - I

Décomposons l'ensemble du processus en étapes détaillées, y compris la configuration de la base de données, la mise en œuvre de l'API à l'aide de Node.js avec Express et l'interaction avec MySQL. Nous couvrirons :

Configuration de la base de données

Tout d'abord, nous allons définir le schéma de la base de données MySQL et créer les tables nécessaires.

Schéma de base de données 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 );
Copier après la connexion
  • tableau des sondages: stocke les informations du sondage avec un identifiant unique, une question et un horodatage de création.

  • table d'options: stocke les options associées à un sondage, liées via poll_id.

  • tableau des votes: enregistre chaque vote, avec un lien vers le sondage, l'option et l'utilisateur.

    ERD pour le système de vote

Entités:

  1. Polls: représente le sondage lui-même, avec des attributs tels que poll_id et question.
  2. Options: représente les options disponibles pour chaque sondage, avec des attributs tels que option_id et option_text.
  3. Votes: représente les votes exprimés par les utilisateurs, avec des attributs tels que vote_id, user_id et timestamps.

Relations:

  1. Un à plusieursentre sondages et options : chaque sondage peut avoir plusieurs options.
  2. Plusieurs à unentre votes et options : chaque vote est associé à une option.
  3. Many-to-Oneentre Votes et Sondages : Chaque vote est lié à un sondage spécifique.

Voici une description de l’ERD :

  1. Tableau des sondages:

    • poll_id(Clé primaire)
    • question
    • created_at
  2. Tableau des options:

    • option_id(Clé primaire)
    • poll_id(clé étrangère faisant référence à polls.poll_id)
    • option_text
  3. Tableau des votes:

    • vote_id(Clé primaire)
    • poll_id(clé étrangère faisant référence à polls.poll_id)
    • option_id(Clé étrangère faisant référence à options.option_id)
    • user_id
    • created_at

Les relations seraient représentées par des lignes entre les entités :

  • SondagesOptions: Un sondage peut avoir plusieurs options.
  • OptionsVotes: Une option peut avoir plusieurs votes.
  • SondagesVotes: Un sondage peut avoir plusieurs votes.

Configuration du back-end

Configurons un projet Node.js en utilisant Express et MySQL.

Étape 1 : initialiser le projet

mkdir polling-system cd polling-system npm init -y npm install express mysql2 dotenv
Copier après la connexion
  • express: Un framework web pour Node.js.
  • mysql2: Un client MySQL pour Node.js.
  • dotenv: Pour gérer les variables d'environnement.

Étape 2 : Structure du projet

Créer une structure de base pour le projet :

polling-system/ │ ├── .env ├── index.js ├── db/ │ └── db.js ├── routes/ │ └── pollRoutes.js └── controllers/ └── pollController.js
Copier après la connexion

Implémentation de l'API

Étape 1 : connexion à la base de données

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

Étape 2 : Variables d'environnement

Fichier - .env

DB_HOST=localhost DB_USER=root DB_PASSWORD=yourpassword DB_NAME=polling_system PORT=3000
Copier après la connexion

Étape 3 : Contrôleur de sondage

Fichier - contrôleurs/pollController.js

Ce fichier implémentera toutes les opérations CRUD nécessaires au système de sondage.

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

Étape 4 : Itinéraires de sondage

Fichier - routes/pollRoutes.js
Définissez les routes pour chaque point de terminaison de l'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;
Copier après la connexion

Étape 5 : Point d'entrée du serveur

Fichier - index.js
Enfin, configurez le serveur :

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

Gestion des erreurs

Chaque méthode inclut la gestion des erreurs pour les problèmes courants tels que les entrées invalides, les votes en double, les sondages ou options manquants et les erreurs de serveur.

  • 輸入驗證:執行檢查以確保輸入有效,例如檢查必填欄位是否存在且格式正確。
  • 事務管理:對於涉及多個查詢的操作(例如,建立或更新投票),事務用於確保一致性。

測試

使用Postman或curl等工具測試每個端點。

  • 建立投票:使用包含問題和選項陣列的 JSON 正文來 POST /api/polls。
  • 更新投票:PUT /api/polls/:pollId 包含更新的問題和選項。
  • 刪除投票: DELETE /api/polls/:pollId.
  • 投票投票:使用 userId 和選項 POST /api/polls/:pollId/vote。
  • 查看投票結果:GET /api/polls/:pollId/results。

結論

這是一個使用 Node.js、Express 和 MySQL 的線上投票系統的全面模組化實作。它處理基本的CRUD操作並確保資料與交易的一致性。它還包括基本的錯誤處理,以使 API 更加健壯和用戶友好。

請參考文章輪詢系統基礎底層設計-I

更多詳情:

取得所有與系統設計相關的文章
標籤:SystemDesignWithZeeshanAli

系統設計與zeeshanali

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
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!