底层设计:轮询系统 – 边缘情况

底层设计:轮询系统 - 边缘情况

目录

案例 1 – 处理更新的版本控制
情况 2 – pollid 作为 uuid 而不是主键
情况 3 – 选项为空或无效
案例 4 – 重复选项
案例 5 – 问题长度限制
案例 6 – 投票过期

请先参考以下文章:

底层设计:投票系统:基本

底层设计:轮询系统 – 使用 node.js 和 sql

边缘情况处理

案例1

要管理投票问题和选项的更新,同时保留与同一投票 id 关联的先前详细信息,您可以实现版本控制系统。这种方法允许您跟踪每次民意调查的历史数据,确保即使在更新后也保留旧的详细信息。

第 1 步:数据库架构更改

更新投票表

将 current_version_id 列添加到 polls 表中以跟踪投票的最新版本。

创建投票版本表

创建一个新表来存储民意调查的历史版本。

更新的数据库架构

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

第 2 步:api 实施变更

更新轮询控制器

修改 updatepoll 方法,在创建新版本之前检查问题是否发生变化。

文件:controllers/pollcontroller.js
const pool = require('../db/db');// create pollexports.createpoll = async (req, res) => {    const { question, options } = req.body;    if (!question || !options || !array.isarray(options) || options.length  {            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 pollexports.updatepoll = async (req, res) => {    const { pollid } = req.params;    const { question, options } = req.body;    if (!pollid || !options || !array.isarray(options) || options.length  {            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 pollexports.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 pollexports.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 resultsexports.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." });    }};

第 3 步:更新投票路线

确保在 pollroutes.js 中正确定义路由。

文件:routes/pollroutes.js
const express = require('express');const router = express.Router();const pollController = require('../controllers/pollController');// Routesrouter.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;

变更摘要

数据库:

更新了投票表以包含 current_version_id。创建了 poll_versions 表来跟踪问题版本。选项和投票表保持不变。

api:

创建了一个新的 createpoll 方法来初始化民意调查和版本。更新了 updatepoll 方法以在创建新版本之前检查问题更改。添加了投票和查看投票结果的方法。

路线:

确保定义所有必要的路由来处理民意调查创建、更新、投票和结果。

案例2

处理 pollid 需要是 uuid(通用唯一标识符)的场景。

以下是在轮询系统中为 thepollid 实现 uuid 的步骤,无需提供代码:

为投票 id 实施 uuid 的步骤

** 数据库架构更新:**

修改 polls、poll_versions、options 和 votes 表,以使用 char(36) 作为 poll_id 而不是整数。创建一个新的 poll_versions 表来存储由 uuid 链接的投票问题和选项的历史版本。

** uuid 生成:**

决定生成uuid的方法。您可以使用应用程序环境中的库或内置函数来创建uuid。

** 创建投票逻辑:**

创建新投票时,生成一个 uuid 并将其用作 poll_id。将新的投票记录插入投票表中。将初始问题插入 poll_versions 表并将其与生成的 uuid 链接。

** 更新投票逻辑:**

更新投票时:

检查问题是否已更改。

如果问题已更改,请在poll_versions 表中创建一个新版本条目来存储旧问题和选项。根据需要使用新问题和选项更新投票表。

** 投票逻辑:**

更新投票机制,确保使用uuid作为poll_id。

验证投票请求中提供的 uuid 是否存在于 polls 表中。

** api 更新:**

修改 api 端点以接受并返回 poll_id 的 uuid。确保所有 api 操作(创建、更新、删除、投票)一致引用 uuid 格式。

** 测试:**

彻底测试应用程序,确保在所有场景(创建、更新、投票和检索投票结果)中正确处理 uuid。

** 文档:**

更新您的 api 文档以反映 poll_id 格式的更改以及与版本控制和 uuid 使用相关的任何新行为。

按照以下步骤,您可以在轮询系统中成功实现 pollid 的 uuid,同时确保数据完整性和历史跟踪。

案例3

空或无效选项

验证方法:

api 输入验证: 在 api 端点中实施检查,以验证请求正文中提供的选项不为空并满足特定条件(例如,如果不允许,则不得使用特殊字符)。反馈机制:如果选项无效或为空,向用户提供清晰的错误消息,指导他们纠正输入。

案例4

重复选项

唯一性检查:

预插入验证: 在将选项添加到投票之前,检查数据库中现有选项是否有重复项。这可以通过使用轮询 id 查询选项表并将其与新选项进行比较来完成。用户反馈:如果检测到重复选项,则返回一条有意义的错误消息,通知用户哪些选项是重复的,从而允许他们相应地修改其输入。

案例5

问题长度限制

字符限制:

api 验证: 设置 api 中投票问题和选项的最大字符限制。这可以通过在创建和更新过程中检查问题的长度和每个选项来完成。用户界面反馈:实施客户端验证,以便在用户输入时超出字符限制时向用户提供即时反馈,增强用户体验。

案例6

投票过期

过期机制:

时间戳管理: 将时间戳字段添加到投票表中,以记录每个投票的创建时间,还可以选择另一个字段来记录到期日期。计划检查: 实施后台作业或 cron 任务,定期检查过期的轮询并将其在数据库中标记为非活动状态。这还可以包括阻止对过期民意调查进行投票。用户通知:(可选)通知投票创建者和参与者即将到期的日期,以便他们在投票变为非活动状态之前参与投票。

请先参考以下文章:

底层设计:投票系统:基本

底层设计:轮询系统 – 使用 node.js 和 sql

更多详情:

获取所有与系统设计相关的文章
标签:systemdesignwithzeeshanali

系统设计与zeeshanali

git:https://github.com/zeeshanali-0704/systemdesignwithzeeshanali

以上就是底层设计:轮询系统 – 边缘情况的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1491019.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月19日 13:37:59
下一篇 2025年12月19日 13:38:14

相关推荐

  • HTML、CSS 和 JavaScript 中的简单侧边栏菜单

    构建一个简单的侧边栏菜单是一个很好的主意,它可以为您的网站添加有价值的功能和令人惊叹的外观。 侧边栏菜单对于客户找到不同项目的方式很有用,而不会让他们觉得自己有太多选择,从而创造了简单性和秩序。 今天,我将分享一个简单的 HTML、CSS 和 JavaScript 源代码来创建一个简单的侧边栏菜单。…

    2025年12月24日
    200
  • 前端代码辅助工具:如何选择最可靠的AI工具?

    前端代码辅助工具:可靠性探讨 对于前端工程师来说,在HTML、CSS和JavaScript开发中借助AI工具是司空见惯的事情。然而,并非所有工具都能提供同等的可靠性。 个性化需求 关于哪个AI工具最可靠,这个问题没有一刀切的答案。每个人的使用习惯和项目需求各不相同。以下是一些影响选择的重要因素: 立…

    2025年12月24日
    300
  • 带有 HTML、CSS 和 JavaScript 工具提示的响应式侧边导航栏

    响应式侧边导航栏不仅有助于改善网站的导航,还可以解决整齐放置链接的问题,从而增强用户体验。通过使用工具提示,可以让用户了解每个链接的功能,包括设计紧凑的情况。 在本教程中,我将解释使用 html、css、javascript 创建带有工具提示的响应式侧栏导航的完整代码。 对于那些一直想要一个干净、简…

    2025年12月24日
    000
  • 布局 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在这里查看视觉效果: 固定导航 – 布局 – codesandbox两列 – 布局 – codesandbox三列 – 布局 – codesandbox圣杯 &#8…

    2025年12月24日
    000
  • 隐藏元素 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在此处查看隐藏元素的视觉效果 – codesandbox 隐藏元素 hiding elements hiding elements hiding elements hiding elements hiding element…

    2025年12月24日
    400
  • 居中 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在此处查看垂直中心 – codesandbox 和水平中心的视觉效果。 通过 css 居中 垂直居中 centering centering centering centering centering centering立即…

    2025年12月24日 好文分享
    300
  • 如何在 Laravel 框架中轻松集成微信支付和支付宝支付?

    如何用 laravel 框架集成微信支付和支付宝支付 问题:如何在 laravel 框架中集成微信支付和支付宝支付? 回答: 建议使用 easywechat 的 laravel 版,easywechat 是一个由腾讯工程师开发的高质量微信开放平台 sdk,已被广泛地应用于许多 laravel 项目中…

    2025年12月24日
    000
  • 如何在移动端实现子 div 在父 div 内任意滑动查看?

    如何在移动端中实现让子 div 在父 div 内任意滑动查看 在移动端开发中,有时我们需要让子 div 在父 div 内任意滑动查看。然而,使用滚动条无法实现负值移动,因此需要采用其他方法。 解决方案: 使用绝对布局(absolute)或相对布局(relative):将子 div 设置为绝对或相对定…

    2025年12月24日
    000
  • 移动端嵌套 DIV 中子 DIV 如何水平滑动?

    移动端嵌套 DIV 中子 DIV 滑动 在移动端开发中,遇到这样的问题:当子 DIV 的高度小于父 DIV 时,无法在父 DIV 中水平滚动子 DIV。 无限画布 要实现子 DIV 在父 DIV 中任意滑动,需要创建一个无限画布。使用滚动无法达到负值,因此需要使用其他方法。 相对定位 一种方法是将子…

    2025年12月24日
    000
  • 移动端项目中,如何消除rem字体大小计算带来的CSS扭曲?

    移动端项目中消除rem字体大小计算带来的css扭曲 在移动端项目中,使用rem计算根节点字体大小可以实现自适应布局。但是,此方法可能会导致页面打开时出现css扭曲,这是因为页面内容在根节点字体大小赋值后重新渲染造成的。 解决方案: 要避免这种情况,将计算根节点字体大小的js脚本移动到页面的最前面,即…

    2025年12月24日
    000
  • Nuxt 移动端项目中 rem 计算导致 CSS 变形,如何解决?

    Nuxt 移动端项目中解决 rem 计算导致 CSS 变形 在 Nuxt 移动端项目中使用 rem 计算根节点字体大小时,可能会遇到一个问题:页面内容在字体大小发生变化时会重绘,导致 CSS 变形。 解决方案: 可将计算根节点字体大小的 JS 代码块置于页面最前端的 标签内,确保在其他资源加载之前执…

    2025年12月24日
    200
  • Nuxt 移动端项目使用 rem 计算字体大小导致页面变形,如何解决?

    rem 计算导致移动端页面变形的解决方法 在 nuxt 移动端项目中使用 rem 计算根节点字体大小时,页面会发生内容重绘,导致页面打开时出现样式变形。如何避免这种现象? 解决方案: 移动根节点字体大小计算代码到页面顶部,即 head 中。 原理: flexível.js 也遇到了类似问题,它的解决…

    2025年12月24日
    000
  • 形状 – CSS 挑战

    您可以在 github 仓库中找到这篇文章中的所有代码。 您可以在此处查看 codesandbox 的视觉效果。 通过css绘制各种形状 如何在 css 中绘制正方形、梯形、三角形、异形三角形、扇形、圆形、半圆、固定宽高比、0.5px 线? shapes 0.5px line .square { w…

    2025年12月24日
    000
  • 有哪些美观的开源数字大屏驾驶舱框架?

    开源数字大屏驾驶舱框架推荐 问题:有哪些美观的开源数字大屏驾驶舱框架? 答案: 资源包 [弗若恩智能大屏驾驶舱开发资源包](https://www.fanruan.com/resource/152) 软件 [弗若恩报表 – 数字大屏可视化组件](https://www.fanruan.c…

    2025年12月24日
    000
  • 网站底部如何实现飘彩带效果?

    网站底部飘彩带效果的 js 库实现 许多网站都会在特殊节日或活动中添加一些趣味性的视觉效果,例如点击按钮后散发的五彩缤纷的彩带。对于一个特定的网站来说,其飘彩带效果的实现方式可能有以下几个方面: 以 https://dub.sh/ 网站为例,它底部按钮点击后的彩带效果是由 javascript 库实…

    2025年12月24日
    000
  • 网站彩带效果背后是哪个JS库?

    网站彩带效果背后是哪个js库? 当你访问某些网站时,点击按钮后,屏幕上会飘出五颜六色的彩带,营造出庆祝的氛围。这些效果是通过使用javascript库实现的。 问题: 哪个javascript库能够实现网站上点击按钮散发彩带的效果? 答案: 根据给定网站的源代码分析: 可以发现,该网站使用了以下js…

    好文分享 2025年12月24日
    100
  • 产品预览卡项目

    这个项目最初是来自 Frontend Mentor 的挑战,旨在使用 HTML 和 CSS 创建响应式产品预览卡。最初的任务是设计一张具有视觉吸引力和功能性的产品卡,能够无缝适应各种屏幕尺寸。这涉及使用 CSS 媒体查询来确保布局在不同设备上保持一致且用户友好。产品卡包含产品图像、标签、标题、描述和…

    2025年12月24日
    100
  • 如何利用 echarts-gl 绘制带发光的 3D 图表?

    如何绘制带发光的 3d 图表,类似于 echarts 中的示例? 为了实现类似的 3d 图表效果,需要引入 echarts-gl 库:https://github.com/ecomfe/echarts-gl。 echarts-gl 专用于在 webgl 环境中渲染 3d 图形。它提供了各种 3d 图…

    2025年12月24日
    000
  • 如何在 Element UI 的 el-rate 组件中实现 5 颗星 5 分制与百分制之间的转换?

    如何在el-rate中将5颗星5分制的分值显示为5颗星百分制? 要实现该效果,只需使用 el-rate 组件的 allow-half 属性。在设置 allow-half 属性后,获得的结果乘以 20 即可得到0-100之间的百分制分数。如下所示: score = score * 20; 动态显示鼠标…

    2025年12月24日
    100
  • CSS 最佳实践:后端程序员重温 CSS 时常见的三个疑问?

    CSS 最佳实践:提升代码质量 作为后端程序员,在重温 CSS/HTML 时,你可能会遇到一些关于最佳实践的问题。以下将解答三个常见问题,帮助你编写更规范、清晰的 CSS 代码。 1. margin 设置策略 当相邻元素都设置了 margin 时,通常情况下应为上一个元素设置 margin-bott…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信