投票系统
创建于 2025 年 6 月 22 日
项目介绍
投票系统是基于若依 RuoYi 3.9.0 后台框架二次开发的歌手大赛投票平台,业务代码集中在 com.ruoyi.vote 包,前端在若依标准 ruoyi-ui(Vue 2 + Element UI)上新增 views/vote 业务页面,并配套一套基于 uni-app 开发的微信小程序(toupiao-wx)。系统服务一场歌手大赛的完整链路:管理员创建投票活动并设定起止时间;选手通过小程序报名,提交姓名、身份证、手机号、微博用户名、抖音 ID 与微博截图,经管理端审核通过后进入选手列表;管理员在管理端点击”开始投票”,投票窗口进入 1 分钟倒计时,用户在小程序上为心仪选手投票;倒计时结束投票自动停止,票数实时统计并生成并列排名,管理端与小程序排行榜同步展示。
数据库脚本 250622-toupiao.sql 一次性导入若依基础表、app 用户表(member_user)与三张业务表(vote_activity / vote_user / vote_record),并预置”歌手大赛”活动、多名歌手选手与数百条真实投票记录,导入即可走通”建活动 → 报名 → 审核 → 开始投票 → 限时抢票 → 查看排名”的完整业务流程。
项目架构
后端为若依标准多模块 Maven 工程(ruoyi-admin / ruoyi-common / ruoyi-framework / ruoyi-system,外加 ruoyi-member app 用户模块、ruoyi-quartz 定时任务模块),Spring Boot 2.5.15 + Java 8。持久层没有沿用若依的 Mapper XML 写法,而是引入 easy-query 的代理模式:实体类标注 @Table / @EntityProxy 并实现 ProxyEntityAvailable,编译期生成 VoteActivityProxy / VoteUserProxy / VoteRecordProxy 代理类,业务代码在 Controller 内注入 EasyEntityQuery,以类型安全的 lambda 表达式链式完成查询、分页、插入、更新与删除——整个投票模块零 Mapper XML、零手写 SQL。以 VoteActivityController.buildQuery() 为例,把活动名称模糊、创建人精确等筛选条件逐个追加到 where 并 orderBy(createTime().desc()),分页通过 toPageResult() 直接产出若依标准的 TableDataInfo;删除时显式 disableLogicDelete() 走物理删除。
接口按访问端拆分为两套:/vote/admin/**(管理端,走 SecurityUtils.getUsername() 后台用户体系)与 /vote/app/**(小程序端,走 SecurityUtils.getAppUserId() app 用户体系),均以 Swagger 注解(@Api / @ApiOperation)生成接口文档。投票记录的 Excel 导出在 Controller 内完成多表关联:leftJoin(MemberUser.class) 以 operaUserId 关联 app 用户表,用 Select.of(v1.FETCHER.allFields(), m2.nickName().as(v1.operaUserName())) 把操作人昵称拼进导出结果。Redis 缓存、Druid 连接池、JWT 鉴权、短信验证码登录(SmsCodeAuthenticationProvider + AppTokenService)、POI 导出(ExcelUtil)等若依基础设施均可用。
数据模型围绕活动展开,三张业务表职责分明:vote_activity 记录活动名称、开始/结束时间与富文本内容;vote_user(投票对象)保存姓名、身份证、手机号、微博用户名、抖音 ID、微博截图,并携带 audit_status(0 待审核 / 1 通过 / 2 拒绝)、activity_id(所属活动)、is_run(投票开关);vote_record(投票记录)以 vote_user_id 指向选手并冗余选手姓名/身份证/手机号快照,opera_user_id 记录投票人,remark 留痕”为歌手 XX 投票”。投票对象类上还声明了三个非表字段(voteCount / rank / records),用于承载统计结果。
核心功能
- 活动管理与实时排名(
views/vote/activity/index.vue+VoteActivityController):活动的增删改查与 Excel 导出之外,getInfo在返回活动详情时自动查询该活动下的全部选手挂到users字段;排名接口GET /vote/admin/activity/rank/{id}对活动内选手逐个统计vote_record票数,按票数降序排序,再对去重后的票数集合按序编号生成”票数 → 名次”映射——票数相同名次并列,管理端”查看投票排名”弹窗以 🥇🥈🥉 图标展示前三名与全部名次。 - 选手报名与批量审核(
views/vote/user/index.vue+VoteUserController+AppVoteUserController):小程序端报名接口校验活动 ID 非空,并检查同一活动下该用户是否已报名(“报名信息已存在”),报名表单通过u-upload上传微博截图(仅 jpg/png、不超过 5M,走若依common/upload);管理端选手列表以el-tag展示审核状态(待审核/通过/不通过),提供”批量通过""批量拒绝”一键流转,同时支持按姓名、活动筛选与 Excel 导出。 - 限时投票与倒计时(
AppVoteRecordController.add+views/vote/user/index.vue):管理端点击”开始投票”将选手isRun置 1,前端随即显示”剩余 01:00”倒计时,startCountdown在 60 秒后自动调用更新接口把isRun置 0 完成关闭;后端投票接口同样校验isRun != "1"时直接返回”投票已停止”,前后端双重保障投票窗口严格受控。 - 防重复投票(
AppVoteRecordController.add):投票前先校验选手存在,再以operaUserId + voteUserId + createTime 按天(YYYY-MM-DD)组合统计当天记录,命中即返回”您已经投过票了”——每个用户每天对同一选手只能投一票;通过校验后自动把选手的姓名/身份证/手机号冗余进投票记录,并写入操作人 ID 与创建时间,保证后续统计与导出不依赖选手表的实时状态。 - 投票记录管理(
views/vote/record/index.vue+VoteRecordController):管理端按投票对象、姓名、操作用户、关联活动(活动下拉实时显示状态)多维筛选,支持新增/修改/删除与 Excel 导出,导出时 leftJoin app 用户表带出操作人昵称;小程序端pages/my/myEnroll.vue通过listByToken查询自己报名过的活动,pages/my/certificate.vue展示电子证书,pages/vote/leaderboard.vue直接复用排名接口呈现实时榜单。
功能截图
暂无运行截图。
快速上手
项目为若依标准多模块工程(后端 20250622-投票系统-api,管理端 20250622-投票系统-web,小程序端 toupiao-wx)。数据库直接导入根目录 250622-toupiao.sql(内含若依基础表、member_user 表、三张投票业务表与”歌手大赛”种子数据),修改 ruoyi-admin/src/main/resources/application-druid.yml 中的 MySQL 连接与 Redis 地址,启动 RuoYiApplication 即完成后端部署。管理端进入 20250622-投票系统-web 执行 npm install 与 npm run dev,使用 admin/admin123 登录后即可看到活动管理、投票对象、投票记录三个业务菜单,可为选手”开始投票”体验 1 分钟限时窗口与倒计时自动停止。小程序端用 HBuilderX 导入 toupiao-wx 运行(微信登录上传头像昵称),即可体验选手列表投票、报名上传微博截图、排行榜与我的报名;投票接口的权限标识(system:record:list 等)已按若依规范预置在菜单权限中。
总结
本项目是一套”管理后台 + 微信小程序”双端完整的投票平台:后端在若依 3.9.0 之上以 easy-query 代理模式实现了零 Mapper XML 的业务开发,前端管理端用 Element UI 完成活动、选手、记录三个模块的常规 CRUD 与批量审核、Excel 导出,小程序端则覆盖微信登录、报名、投票、排行榜、证书的完整用户链路。业务设计上有几个值得借鉴的点:一是冗余快照——投票记录冗余选手姓名/身份证/手机号,使统计与导出不依赖选手表的实时状态;二是并列排名算法——先按票数排序,再对去重票数集合生成名次映射,天然处理并列情况;三是前后端双重投票控制——管理端倒计时自动关停 + 后端 isRun 校验 + 按天防重复投组合拳,兼顾运营节奏与公平性。项目 SQL 中保留了数百条真实投票记录,说明系统已在真实活动中跑通全流程,具备直接复用于各类投票评选场景的能力。