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

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

目录

案例 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)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
通过构建具有依赖关系的后端框架来学习 Nodejs
上一篇 2025年12月19日 13:37:59
什么是 Cloudflare? Web 性能和安全公司概述
下一篇 2025年12月19日 13:38:14

相关推荐

  • 开源免费PHP工具 PHP开发效率提升利器

    推荐开源免费PHP开发工具以提升效率:VS Code、Sublime Text轻量高效,PhpStorm专业强大;调试用Xdebug、Kint、Ray;依赖管理选Composer;代码质量工具包括PHPStan、Psalm、PHP_CodeSniffer;数据库管理可用%ignore_a_1%MyA…

    2026年5月10日
    000
  • 谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    使用谷歌浏览器的开发者工具截图步骤:1. 按ctrl+shift+i(windows/linux)或cmd+option+i(mac)打开开发者工具。2. 点击右上角三个点,选择”更多工具”,再选择”截图”。3. 选择截取整个页面。推荐的谷歌浏览器扩展…

    2026年5月10日 用户投稿
    100
  • JavaScript计算器开发:解决数值显示与初始化问题

    本教程深入探讨了使用JavaScript构建计算器时常见的数值显示异常问题,特别是由于类属性未初始化导致的`Cannot read properties of undefined`错误。我们将详细分析问题根源,并通过在构造函数中调用初始化方法来解决该问题,同时优化显示逻辑,确保计算器功能稳定且界面显…

    2026年5月10日
    000
  • NextAuth getToken 在服务端返回 null 的问题排查与解决

    问题描述 在使用 Next.js 和 NextAuth 构建应用程序时,有时需要在服务端获取用户的身份验证信息。getToken 函数是 NextAuth 提供的一个便捷方法,用于从请求中提取 JWT (JSON Web Token)。然而,在某些情况下,尤其是在使用 getServerSidePr…

    2026年5月10日
    000
  • HTML文档如何工作?如何编辑HTML格式文件?

    HTML文档如何工作?如何编辑HTML格式文件?HTML文档如何工作?如何编辑HTML格式文件?HTML文档如何工作?如何编辑HTML格式文件?HTML文档如何工作?如何编辑HTML格式文件?

    浏览器解析和渲染html的过程包括:1. 解析html构建dom树;2. 结合css构建渲染树;3. 布局计算元素位置;4. 绘制像素到屏幕。编辑html可使用记事本、vs code、sublime text等文本或代码编辑器,其中vs code因语法高亮、自动补全和插件生态成为主流选择。标准htm…

    2026年5月10日 用户投稿
    100
  • GolangWeb项目异常捕获与日志记录

    答案:通过中间件使用defer和recover捕获panic,结合zap等结构化日志库记录请求链路信息,为每个请求生成trace ID,实现异常捕获与可追踪日志,提升系统稳定性与可观测性。 在Go语言Web项目中,异常捕获与日志记录是保障系统稳定性和可维护性的关键环节。Go本身没有像其他语言那样的t…

    2026年5月10日
    000
  • Python官网用户调查的参与方式_Python官网反馈提交详细教程

    答案是通过访问Python官网新闻页面、邮件邀请链接或GitHub仓库提交反馈。具体为:访问官网查找用户调查公告,或点击邮件中的专属链接参与,在GitHub的cpython仓库提交技术建议,并注意如实填写问卷与保护隐私。 如果您希望参与Python官网的用户调查并提交反馈,可以通过官方指定的渠道完成…

    2026年5月10日
    000
  • Go语言连接外部MySQL数据库:DSN配置与常见错误解析

    本文详细阐述了go语言使用`go-sql-driver/mysql`驱动连接外部mysql数据库的正确方法。重点介绍了数据源名称(dsn)的规范格式,特别是主机地址部分的配置,以避免常见的“getaddrinfow: the specified class was not found.”等网络解析错…

    2026年5月10日
    000
  • Tensorflow 音乐预测

    在本文中,我展示了如何使用张量流来预测音乐风格。在我的示例中,我比较了电子音乐和古典音乐。 你可以在我的github上找到代码:https://github.com/victordalet/sound_to_partition i – 数据集 第一步,您需要创建一个数据集文件夹,并在里面…

    2026年5月10日
    000
  • 学习了Python的Flask后,Go语言的Web框架该选Gin还是Beego?

    学习编程时,选择合适的框架至关重要。许多开发者在掌握Python Flask后,转向Go语言Web开发时,常常在Gin和Beego之间难以抉择。本文将深入分析,助您做出明智选择。 虽然网上搜索结果多建议使用Go原生标准库http,但实际上所有框架都是对http的封装。虽然使用http开发灵活,但工作…

    2026年5月10日
    000
  • JavaScript动态下拉菜单:实现日期选项与价格计算关联

    在现代web应用中,动态生成表单元素并使其具备交互逻辑是常见的需求。特别是在需要根据用户选择调整价格或服务参数的场景下,下拉菜单()常被用来展示一系列选项。本教程将指导您如何利用javascript动态生成一个包含日期选项的下拉菜单,并为每个选项关联一个具体的数值(如剩余天数),进而实现一个基于用户…

    2026年5月10日
    000
  • 如何在不暴露密钥的情况下,在客户端创建 Stripe Payment Link

    本文介绍了在纯静态网站环境下,如何利用 Stripe Payment Link 实现商品售卖,并着重讨论了在不暴露 Stripe 密钥的前提下,客户端创建 Payment Link 的可行性。分析了直接在客户端使用密钥的风险,并提出了预先生成 Payment Link 或使用后端服务动态生成 Pay…

    2026年5月10日
    000
  • 解决Go语言中GOPATH未设置错误及工作区配置指南

    本文旨在解决go语言开发中常见的“gopath not set”错误,并提供详细的go工作区配置指南。内容涵盖`gopath`环境变量的设置、go项目目录结构、`path`变量的扩展,以及一些高级配置技巧,旨在帮助开发者建立一个高效、规范的go开发环境,确保包的下载、编译和运行顺利进行。 Go语言在…

    2026年5月10日
    000
  • 掌握 JavaScript 中的高阶函数

    现代 javascript 开发严重依赖函数式编程,掌握其基本思想将极大提高你的编码能力。 高阶函数是这个范式最有力的武器之一。为了帮助您掌握它们,本文将介绍它们的定义、应用程序和独特的实现。 1. 函数式编程 函数式编程是一种编程范式,强调: 纯函数:没有副作用的函数,对于相同的输入返回相同的输出…

    2026年5月10日
    000
  • Golang使用assert库简化测试断言

    使用testify/assert库可提升Go测试代码的可读性和效率,通过go get github.com/stretchr/testify/assert安装后导入包,用assert.Equal等函数替代冗长的手动判断,支持丰富断言方法如Equal、True、Nil、Contains等,并可添加自定…

    2026年5月10日
    100
  • 如何处理在线编辑HTML时外部链接验证的处理方法

    在线编辑HTML时需验证外部链接以保障安全与可用性,可通过自动检测标记外链并添加rel属性提升安全性;2. 实时验证链接有效性,利用HEAD请求检查状态码并在编辑界面提示结果;3. 配置可信域名白名单控制高风险链接输入,适用于合规要求高的场景;4. 提供友好反馈机制,对无效或可疑链接弹出提示并支持新…

    2026年5月10日
    000
  • 怎样为C++配置嵌入式AI开发环境 TensorFlow Lite Micro移植指南

    怎样为C++配置嵌入式AI开发环境 TensorFlow Lite Micro移植指南怎样为C++配置嵌入式AI开发环境 TensorFlow Lite Micro移植指南怎样为C++配置嵌入式AI开发环境 TensorFlow Lite Micro移植指南怎样为C++配置嵌入式AI开发环境 TensorFlow Lite Micro移植指南

    要在c++++项目中使用tensorflow lite micro进行嵌入式ai开发,关键步骤包括:1. 确定mcu平台并安装对应的交叉编译工具链;2. 配置python环境并安装必要的依赖包;3. 获取并裁剪tflm源码,保留核心模块;4. 将tflm静态库集成到c++工程中;5. 按照模型加载、…

    2026年5月10日 用户投稿
    000
  • Golang图片处理技巧 imaging库裁剪缩放

    答案:使用Go语言的imaging库可高效实现图片裁剪与缩放,其API简洁易用,支持多种缩放算法(如Lanczos、CatmullRom)以平衡质量与性能,提供Crop和CropAnchor两种裁剪方式实现精确区域控制,并建议通过算法选择、内存管理、并发处理和错误校验等策略优化性能与稳定性。 在Go…

    2026年5月10日
    000
  • 如何通过GitHub API高效获取超过100个用户列表(分页教程)

    本教程旨在解决使用GitHub API获取用户列表时遇到的默认100个用户限制问题。我们将详细介绍两种主要的分页策略:利用Octokit库内置的paginate方法实现自动化分页,以及手动实现基于since参数的循环分页逻辑。文章将提供清晰的代码示例,并强调在不同场景下选择合适方法的注意事项,特别是…

    2026年5月10日
    100
  • c语言里面字符是什么意思

    字符在 C 语言中以单个字节存储于 char 变量中,用单引号括起表示常量,例如 ‘A’。字符变量用于存储字符值,可使用函数如 putchar() 输出、getchar() 输入、toupper() 转换大小写。字符数组存储多个字符,如 char name[10]。字符串是带…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信