【数据库课程设计】网上投票管理系统
目录
前言:
一,系统需求分析
1,需求概述
2,系统功能图
3,业务流程图
业务流程分析
业务流程图
4,数据流程图
5,数据字典
二,概念结构设计
1,实体分析
2,属性分析
3,联系分析
4,概念模型分析(CDM图)
三,逻辑结构设计
逻辑结构模型
逻辑结构图 (LDM)
四,物理结构设计
1,表设计
2,建表sql语句
3,物理结构(PDM图)
五,数据库功能实现及界面展示
1,确定数据库需要创建的对象
视图
自定义函数
存储过程
触发器
2,界面布局及实现
遇到的问题:
源码:
前言:
开发环境:SQL Server 2022,Qt Creator 4.11.1 (Community)
源码在文章末尾
结果预览:
屏幕录制 2025-05-20 183909
一,系统需求分析
1,需求概述
(1)应用系统背景
随着互联网技术的发展,传统线下投票方式(如纸质投票、会议举手表决等)逐渐暴露出效率低、成本高、统计复杂、安全性差等问题。网上投票管理系统旨在通过数字化手段实现投票流程的自动化,支持多场景投票需求(如企业决策、校园活动、社会调研等),提供高效、安全、透明的投票服务。
系统目标:
-
提高投票效率和准确性,减少人工统计错误;
-
支持匿名/实名投票、单选/多选、限时投票等多种模式;
-
保障数据安全性,防止刷票、篡改等恶意行为;
-
提供友好的用户界面和实时结果可视化功能。
(2)组织结构情况
-
普通用户:参与投票、查看投票结果。
-
管理员:创建/管理投票活动、审核用户身份、设置投票规则。
-
审核员:对敏感投票内容进行人工审核。
-
系统管理员:维护系统安全与数据库。
(3)需求分析过程
1,需求收集方法:
-
问卷调查:面向企业、学校等潜在用户,收集投票场景需求。
-
竞品分析:研究现有投票系统(如腾讯投票、问卷星)的功能优缺点。
-
专家访谈:与信息安全专家讨论防刷票、数据加密方案。
2,核心需求整理
-
功能性需求:
-
用户注册/登录(支持手机号、邮箱、第三方登录);
-
投票创建(设置标题、选项、截止时间、投票规则);
-
投票参与(匿名/实名模式、防重复提交);
-
结果统计(实时图表展示、数据导出为Excel/PDF);
-
审核机制(敏感词过滤、人工审核流程)。
-
-
非功能性需求:
-
性能:支持1000人同时在线投票,响应时间<2秒;
-
安全性:HTTPS传输、数据库加密、IP限制防刷票;
-
兼容性:适配PC、手机浏览器及微信小程序。
-
2,系统功能图
3,业务流程图
业务流程分析
1,用户注册与身份验证流程
- 新用户通过邮箱/手机号注册,系统验证唯一性后创建账户。
- 管理员可对用户信息进行审核。
2,投票创建与发布流程
- 管理员填写投票基本信息(标题,选项,截止时间等等)
- 审核通过后 ,投票进入“已发布”状态,用户可见。
3,用户参与投票流程
- 用户选择投票活动,根据规则(实名/匿名)提交选择。
- 系统校验用户权限。
- 投票实时记录,并写入数据库。
4,投票处理流程
- 投票时间到后,系统自动关闭并锁定数据。
- 支持查看投票结果。
业务流程图


4,数据流程图

5,数据字典
1,用户表(User)
表说明:
存储系统用户信息,支持角色权限管理。用户名和邮箱需唯一,密码加密存储。
字段名 | 数据类型 | 长度 | 是否允许为空 | 默认值 | 键/索引 | 说明 |
UserID | INT | 11 | 否 | 无 | 主键 | 用户唯一标识,自增 |
Username | NVARCHAR | 50 | 否 | 无 | 唯一索引 | 用户名 |
Password | NVARCHAR | 128 | 否 | 无 | 无 | 密码 |
NVARCHAR | 100 | 否 | 无 | 无 | 用户邮箱 | |
Role | NVARCHAR | 10 | 否 | 'user' | 索引 | 用户角色 |
CreatedAt | DATETIME | - | 否 | GETDATE() | 无 | 用户注册时间 |
2,投票主表(Vote)
表说明:
存储投票活动的基本信息,通过外键关联创建者。状态字段控制投票生命周期。
字段名 | 数据类型 | 长度 | 是否允许为空 | 默认值 | 键/索引 | 说明 |
VoteID | INT | 11 | 否 | 无 | 主键 | 投票唯一标识,自增 |
Title | NAVRCHAR | 100 | 否 | 无 | 无 | 投票标题 |
CreatorID | INT | 11 | 否 | 无 | 外键User | 投票创建者 ID |
IsAnonymous | BIT | 否 | 0 | 无 | 是否匿名投票:0(实名),1(匿名) | |
MaxChoices | INT | 11 | 否 | 1 | 无 | 用户最多可选选项数(单选/多选) |
EndTime | DaATETIME | 否 | 索引 | 投票结束时间 | ||
Status | NVARCHAR | 10 | 否 | 'draft' | 联合索引 | 状态:draft(草稿),published(已发布),closed(已关闭) |
3,投票选项表(VoteOption)
表说明:
存储投票的选项信息,每个投票可包含多个选项,通过外键关联投票主表。
字段名 | 数据类型 | 长度 | 是否允许为空 | 默认值 | 键/索引 | 说明 |
OptionID | INT | 11 | 否 | 主键 | 选项唯一标识,自增 | |
VoteID | INT | 11 | 否 | 外键->Vote | 关联的投票ID | |
OptionText | NVARCHAR | 200 | 否 | 选项文本内容 |
4,投票记录表(VoteRecord)
表说明:
记录用户投票行为,匿名投票时UserID为NULL。通过唯一约束(UserID+VoteID)防止重复投票。
字段名 | 数据类型 | 长度 | 是否允许为空 | 默认值 | 键/索引 | 说明 |
RecordID | INT | 11 | 否 | 主键 | 投票记录唯一标识,自增 | |
VoteID | INT | 11 | 否 | 外键->Vote | 关联投票ID | |
UserID | INT | 11 | 是 | NULL | 外键->User | 投票用户ID,匿名时为NULL |
OptionID | INT | 11 | 否 | 外键->VoteOption | 用户选择的选项ID | |
VoteTime | DATETIME | 否 | GETDATE() | 索引 | 投票提交时间 | |
IPAddress | NVARCHAR | 45 | 否 | 用户投票时的投票IP |
5,审核日志表 (AuditLog)
表说明:
记录投票审核的操作日志,确保投票内容的合规性。
字段名 | 数据类型 | 长度 | 是否允许为空 | 默认值 | 键/索引 | 说明 |
LogID | INT | 11 | 否 | 主键 | 唯一标识 ,自增 | |
VoteID | INT | 11 | 否 | 外键->Vote | 被审核的投票ID | |
AuditorLog | INT | 11 | 否 | 外键->User | 审核员用户ID | |
Action | NVARCHAR | 10 | 否 | 审核操作:通过,驳回 | ||
Comment | NVARCHAR(MAX) | 是 | NULL | 审核意见(文本) | ||
AuditTime | DATETIME | 否 | GETTIME() | 审核时间 |
二,概念结构设计
数据库概念结构设计是指将系统需求转化为一个抽象的信息结构,即概念模型。
1,实体分析
实体识别,核心实体:
User(用户),Vote(投票),VoteOpston(投票选项),VoteRecord(投票记录),AyditLog(审核日志)
2,属性分析
实体特征:
为每个实体定义唯一标识符(主键)和描述性属性。
为每个实体定义唯一标识(主键约束),然后根据业务需求确立其他字段。(如非空、唯一性、默认值)。
-
User 实体:
UserID
(主键)、Username
、Password
、Role
。 -
Vote 实体:
VoteID
(主键)、Title
、EndTime
、Status
。
3,联系分析
-
根据业务流程分析实体间交互(如用户创建投票、用户选择选项)。
-
确定关系基数(1:1、1:N、M:N)。
实体与实体之间的关系:
确定关系基数,用户:投票=1:n(一个用户可以创建多个投票活动)
投票:选项=1:n(一个投票包含多个选项)等等。
实体关系模型:E-R图如下
4,概念模型分析(CDM图)
三,逻辑结构设计
数据库的逻辑设计的任务是把概念设计阶段设计好的基本E-R图转换为与选用的具体机器上的DBMS所支持的数据模型相符合的逻辑结构。根据ER图 写出关系模式,并标出主键外键。
1,实体到表的转换
每个实体映射为一张表,属性转换为表的字段。关系模型的基本映射规则(实体-表,属性-列)。示例:User实体->User表;Vote实体->Vote表。
2,关系处理
1:n关系:通过外键在“多”端表中引用“一”端表中的主键。
3,主键与唯一键约束
主键(Primary Key):每个表定义唯一标识符。如自增(auto increment)
唯一性约束:防止数据重复,如用户名唯一。
逻辑结构模型
1,用户表(User)
字段名 | 数据类型 | 约束 | 说明 |
UserID | INT | PRIMARY KEY IDENTITY | 用户唯一标识 |
Username | NVARCHAR(50) | UNIQUE NOT NULL | 用户名(唯一) |
Password | NVARCHAR(128) | NOT NULL | 密码(加密) |
NVARCHAR(100) | NOT NULL | 用户邮箱 | |
Role | NVARCHAR(10) | CHECK (Role IN ('user','admin','auditor')) DEFAULT 'user' | 用户角色 |
CreatorAt | DATETIME | DEFAULT GETDATE() | 注册时间 |
2,投票主表(Vote)
字段名 | 数据类型 | 约束 | 说明 |
VoteID | INT | PRIMARY KEY IDENTITY | 投票唯一 |
Title | NVARCHAR(100) | NOT NULL | 投票标题 |
CreatorID | INT | FOREIGN KEY REFERENCES User(UserID) | 创建者ID |
IsAnonymous | BIT | DEFAULT 0 | 0实名, 1匿名 |
MaxChoices | INT | DEFAULT 1 | 最多可选数 |
Endtime | DATETIME | NOT NULL | 截止时间 |
Status | NVARCHAR(10) | CHECK (Status IN ('draft','published','closed')) DEFAULT 'draft' | 投票状态 |
3,投票选项表(VoteOption)
字段名 | 数据类型 | 约束 | 说明 |
OptionID | INT | PRIMARY KEY IDENTITY | 选项唯一标识 |
VoteID | INT | FOREIGN KEY REFERENCES Vote(VoteID) | 所属投票ID |
OptionText | NVARCHAR(200) | NOT NULL | 选项文本内容 |
4,投票记录表(OptionRecord)
字段名 | 数据类型 | 约束 | 说明 |
RecordID | INT | PRIMARY KEY INENTITY | 记录唯一标识 |
VoteID | INT | FOREIGN KEY REFERENCES Vote(VoteID) | 关联投票ID |
UserID | INT | FOREIGN KEY REFERENCES User(UserID) | 投票用户ID |
OptionID | INT | FOREIGN KEY REFERENCES VoteOption(OptionID) | 用户选择的选项ID |
VoteTime | DATETIME | DEFAULT GETDATE() | 投票时间 |
IPAddress | NVARCHAR(45) | NOT NULL | 用户IP地址 |
5,审核日志表 (AuditLog)
字段名 | 数据类型 | 约束 | 说明 |
LogID | INT | PRIMARY KEY IDENTITY | 日志唯一标识 |
VoteID | INT | FOREIGN KEY REFERENCES Vote(VoteID) | 被审核的投票ID |
AuditorID | INT | FOREIGN KEY REFERENCES User(UserID) | 审核员ID |
Action | NVARCHAR(10) | CHECK (Action IN ('approve','reject')) | 审核操作 |
Comment | NVARCHAR(200) | NULL | 审核意见 |
AuditTime | DATETIME | DEFAULT GETDATE() | 审核时间 |
逻辑结构图 (LDM)
四,物理结构设计
1,表设计
存储优化:
使用NVARCHAR存储多语言文本(如中文,符号等等)。
自增主键IEDNTITY(1,1)替身插入性能。
完整性约束:
级联删除:删除用户时自动清理其生成的投票(ON DELETE CASCADE)
索引策略:
高频查询字段:Vote表中的Status和EndTime联合索引,加速查询。
时间范围查询:VoteRecord表中的VoteTime创建索引。
安全设计:
密码字段使用SHA2_256(哈希算法)。
匿名投票时,强制IPAddress非空。
2,建表sql语句
注:下面 的建表语句中,有的表的名称 加上了[ ],目的是避免你创建的表,或查询与一些SQL SERVER自带的关键字发生冲突。
比如下面创建了一个的User表,而系统中也包含这一张表,所以需要加上[ ],避免冲突。
-- 1. 用户表
CREATE TABLE [User](UserID INT IDENTITY(1,1) PRIMARY KEY,Username NVARCHAR(50) NOT NULL UNIQUE,Password NVARCHAR(128) NOT NULL,Email NVARCHAR(100),Role NVARCHAR(10) CHECK (Role IN ('user', 'admin', 'auditor')) DEFAULT 'user',CreatedAt DATETIME NOT NULL DEFAULT GETDATE()
);
GO-- 2. 投票主表
CREATE TABLE Vote (VoteID INT IDENTITY(1,1) PRIMARY KEY,Title NVARCHAR(100) NOT NULL,CreatorID INT NOT NULL FOREIGN KEY REFERENCES [User](UserID) ON DELETE CASCADE,IsAnonymous BIT NOT NULL DEFAULT 0,MaxChoices INT NOT NULL DEFAULT 1 CHECK (MaxChoices > 0),EndTime DATETIME NOT NULL,
);
GO-- 3. 投票选项表
CREATE TABLE VoteOption (OptionID INT IDENTITY(1,1) PRIMARY KEY,VoteID INT NOT NULL FOREIGN KEY REFERENCES Vote(VoteID) ON DELETE CASCADE,OptionText NVARCHAR(200) NOT NULL
);
GO-- 4. 投票记录表
CREATE TABLE VoteRecord (RecordID INT IDENTITY(1,1) PRIMARY KEY,VoteID INT NOT NULL FOREIGN KEY REFERENCES Vote(VoteID) ON DELETE NO ACTION ,UserID INT NULL FOREIGN KEY REFERENCES [User](UserID) ON DELETE SET NULL,OptionID INT NOT NULL FOREIGN KEY REFERENCES VoteOption(OptionID) ON DELETE NO ACTION ,VoteTime DATETIME NOT NULL DEFAULT GETDATE(),IPAddress NVARCHAR(45) NOT NULL,-- 唯一约束防止重复投票(匿名投票需程序补充IP校验)CONSTRAINT UK_User_Vote UNIQUE (UserID, VoteID)
);
GO-- 5. 审核日志表
CREATE TABLE AuditLog (LogID INT IDENTITY(1,1) PRIMARY KEY,VoteID INT NOT NULL FOREIGN KEY REFERENCES Vote(VoteID) ON DELETE CASCADE,AuditorID INT NOT NULL FOREIGN KEY REFERENCES [User](UserID),Action NVARCHAR(10) NOT NULL CHECK (Action IN ('approve', 'reject')),Comment NVARCHAR(MAX) NULL,AuditTime DATETIME NOT NULL DEFAULT GETDATE()
);
GO-- 6. 索引设计
CREATE INDEX idx_User_Role ON [User] (Role);
CREATE INDEX idx_Vote_Status_EndTime ON Vote (Status, EndTime);
CREATE INDEX idx_VoteRecord_VoteTime ON VoteRecord (VoteTime);
CREATE INDEX idx_AuditLog_AuditTime ON AuditLog (AuditTime);
3,物理结构(PDM图)
五,数据库功能实现及界面展示
1,确定数据库需要创建的对象
视图
视图(views):简化复杂查询,如统计投票结果,显示待审核的投票。
投票结果视图:展示已发布投票的每个选项得票数
CREATE VIEW vw_VoteResults AS
SELECT v.VoteID,v.Title,vo.OptionID,vo.OptionText,COUNT(vr.OptionID) AS VoteCount
FROM Vote v
JOIN VoteOption vo ON v.VoteID = vo.VoteID
LEFT JOIN VoteRecord vr ON vo.OptionID = vr.OptionID
GROUP BY v.VoteID, v.Title, vo.OptionID, vo.OptionText;
待审核投票视图:列出所有状态为"草稿"的 视图,包含创建者及截止时间。
CREATE VIEW vw_PendingAudits AS
SELECT v.VoteID,v.Title,u.Username AS Creator,v.CreatedAt,v.EndTime
FROM Vote v
JOIN [User] u ON v.CreatorID = u.UserID
WHERE v.Status = 'draft';
用户投票记录视图:仅显示实名投票记录,包含用户,投票及选项信息。
CREATE VIEW vw_UserVoteHistory AS
SELECT u.UserID,u.Username,v.VoteID,v.Title,vo.OptionText AS VotedOption,vr.VoteTime
FROM VoteRecord vr
JOIN [User] u ON vr.UserID = u.UserID
JOIN Vote v ON vr.VoteID = v.VoteID
JOIN VoteOption vo ON vr.OptionID = vo.OptionID
WHERE v.IsAnonymous = 0;
删除投票视图
--CREATE PROCEDURE sp_DeleteVote
-- @VoteID INT,
-- @RequesterUserID INT
--AS
--BEGIN
-- BEGIN TRY
-- BEGIN TRANSACTION-- -- 验证权限:仅管理员或创建者可删除
-- IF NOT EXISTS (
-- SELECT 1
-- FROM Vote
-- WHERE VoteID = @VoteID
-- AND (
-- CreatorID = @RequesterUserID
-- OR EXISTS (
-- SELECT 1
-- FROM [User]
-- WHERE UserID = @RequesterUserID
-- AND Role = 'admin'
-- )
-- )
-- )
-- BEGIN
-- RAISERROR('无权删除该投票', 16, 1)
-- RETURN
-- END-- -- 执行删除(触发器处理级联删除)
-- DELETE FROM Vote
-- WHERE VoteID = @VoteID-- COMMIT TRANSACTION
-- END TRY
-- BEGIN CATCH
-- IF @@TRANCOUNT > 0
-- ROLLBACK TRANSACTION;
-- THROW; -- 抛出原始错误
-- END CATCH
--END
自定义函数
自定义函数::用于业务逻辑,如检查用户是否已投票、计算选项得票数。
检查用户是否已投票(防止重复投票)
CREATE FUNCTION dbo.fn_HasUserVoted (@UserID INT,@VoteID INT
)
RETURNS BIT
AS
BEGINDECLARE @Result BIT = 0;IF EXISTS (SELECT 1 FROM VoteRecord WHERE UserID = @UserID AND VoteID = @VoteID)SET @Result = 1;RETURN @Result;
END;
存储过程
存储过程:封装业务操作,如创建投票、提交投票、审核投票。
创建投票:
CREATE PROCEDURE sp_CreateVote@Title NVARCHAR(100),@CreatorID INT,@IsAnonymous BIT,@MaxChoices INT,@EndTime DATETIME,@Options NVARCHAR(MAX) -- JSON格式: ["Option1", "Option2"]
AS
BEGINBEGIN TRANSACTION;-- 插入投票主表INSERT INTO Vote (Title, CreatorID, IsAnonymous, MaxChoices, EndTime)VALUES (@Title, @CreatorID, @IsAnonymous, @MaxChoices, @EndTime);DECLARE @VoteID INT = SCOPE_IDENTITY();-- 解析并插入选项INSERT INTO VoteOption (VoteID, OptionText)SELECT @VoteID, [value]FROM OPENJSON(@Options);-- 自动触发敏感词检测IF EXISTS (SELECT 1 FROM VoteOption WHERE VoteID = @VoteIDAND dbo.fn_CheckSensitiveWords(OptionText) = 1)BEGIN-- 存在敏感词,状态设为待审核UPDATE Vote SET Status = 'draft' WHERE VoteID = @VoteID;ENDELSEBEGIN-- 无敏感词,直接发布UPDATE Vote SET Status = 'published' WHERE VoteID = @VoteID;ENDCOMMIT TRANSACTION;
END;
用户提交投票:
CREATE PROCEDURE sp_SubmitVote@VoteID INT,@UserID INT, -- 匿名时为NULL@OptionIDs NVARCHAR(MAX), -- JSON格式: [1, 2]@IPAddress NVARCHAR(45)
AS
BEGINBEGIN TRYBEGIN TRANSACTION;-- 检查投票是否有效IF NOT EXISTS (SELECT 1 FROM Vote WHERE VoteID = @VoteID AND Status = 'published'AND EndTime > GETDATE())RAISERROR('投票不存在或已截止!', 16, 1);-- 检查选项数量DECLARE @OptionCount INT = (SELECT COUNT(*) FROM OPENJSON(@OptionIDs));IF @OptionCount > (SELECT MaxChoices FROM Vote WHERE VoteID = @VoteID)RAISERROR('超出最大可选数量!', 16, 1);-- 实名投票检查重复IF @UserID IS NOT NULL AND dbo.fn_HasUserVoted(@UserID, @VoteID) = 1RAISERROR('您已参与过本次投票!', 16, 1);-- 匿名投票检查IP限制IF @UserID IS NULL AND EXISTS (SELECT 1 FROM VoteRecord WHERE VoteID = @VoteID AND IPAddress = @IPAddressAND VoteTime >= DATEADD(HOUR, -24, GETDATE()))RAISERROR('同一IP24小时内仅能投票一次!', 16, 1);-- 插入投票记录INSERT INTO VoteRecord (VoteID, UserID, OptionID, IPAddress)SELECT @VoteID, @UserID, [value], @IPAddressFROM OPENJSON(@OptionIDs);COMMIT TRANSACTION;END TRYBEGIN CATCHROLLBACK TRANSACTION;THROW;END CATCH
END;
触发器
触发器:触发器是一种特殊的存储过程,它会在特定的数据库事件(如插入,删除,更新等等)发生时自动执行。触发器与表直接关联,用于在数据变动时,完成某些业务逻辑,从而保证数据的一致性,完整性和自动化处理。
自动更新选项的票数:每次插入投票记录时,对应选项的 VoteCount字段自动+1
CREATE TRIGGER trg_UpdateVoteCount
ON VoteRecord
AFTER INSERT
AS
BEGINUPDATE voSET vo.VoteCount = vo.VoteCount + 1FROM VoteOption voJOIN inserted i ON vo.OptionID = i.OptionID;
END;
防止修改已结束的投票:拦截对已截止投票的修改操作,确保数据不可变性。
CREATE TRIGGER trg_PreventModifyClosedVote
ON Vote
INSTEAD OF UPDATE
AS
BEGINIF UPDATE(EndTime) OR UPDATE(Status)BEGINIF EXISTS (SELECT 1 FROM inserted iJOIN deleted d ON i.VoteID = d.VoteIDWHERE d.EndTime < GETDATE())RAISERROR('投票已截止,禁止修改!', 16, 1);ELSEUPDATE vSET Title = i.Title,EndTime = i.EndTime,Status = i.StatusFROM Vote vJOIN inserted i ON v.VoteID = i.VoteID;END
END;
2,界面布局及实现
程序开始界面
登录/注册界面
主页面
遇到的问题:
在进行新建投票的操作过程中,出现userID冲突的过程。找了半天是在用户登录注册代码部分,获取userID时,由于通过map查找userID时,字符串大小写搞错了,一直获取的是同一个userID。
还有就是对于选项表,起初的想法是按照数组的特点,每个选项对应一个下标,运行后才发现这样做使得OptionID发生冲突,也就是发生主键冲突。解决办法是将每个选项的文本和它的选项ID拼接在一起,获取选项ID的时候,直接取选项的第一个字符然后转为整数(先这么做,这里看到会存在问题的)。
源码:
网上投票系统: 这是一个数据库课程设计,关于网上投票管理系统的,使用到的技术QT,使用的数据库SQL-Server - Gitee.com