ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring Boot+MyBatis在线投票系统开发实践

Spring Boot+MyBatis在线投票系统开发实践

1. 项目背景与核心价值

去年参与某高校学生会选举系统开发时,我深刻体会到传统纸质投票的痛点:计票耗时易错、结果透明度低、异地参与困难。这个基于Spring Boot + MyBatis的在线投票系统,正是为解决这些实际问题而设计的轻量级解决方案。

系统采用前后端分离架构,后端提供RESTful API接口,前端可灵活适配Web或小程序。核心功能包括:多类型投票创建(单选/多选/评分)、实时结果可视化、防刷票机制等。相比市面常见投票工具,我们的特色在于:

  • 支持复杂投票逻辑(如条件选项显示)
  • 完善的权限控制体系
  • 投票数据可追溯审计

2. 技术架构解析

2.1 Spring Boot框架选型

选择Spring Boot 2.7.x版本主要基于:

  1. 内嵌Tomcat简化部署
  2. Starter依赖自动配置(特别是Spring Security和Redis)
  3. Actuator端点监控
  4. 与MyBatis的天然兼容性

关键配置示例:

@SpringBootApplication @MapperScan("com.vote.mapper") @EnableCaching public class VoteApplication { public static void main(String[] args) { SpringApplication.run(VoteApplication.class, args); } }

2.2 MyBatis持久层设计

采用MyBatis-Plus 3.5.x增强功能:

  • 动态表名处理器(按年份分表存储投票记录)
  • 逻辑删除支持
  • 性能分析插件

投票实体关系模型:

CREATE TABLE `vote` ( `id` bigint NOT NULL AUTO_INCREMENT, `title` varchar(100) NOT NULL, `type` tinyint COMMENT '1-单选 2-多选 3-评分', `start_time` datetime NOT NULL, `end_time` datetime NOT NULL, `creator_id` bigint NOT NULL, `is_anonymous` tinyint(1) DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. 核心功能实现

3.1 并发投票控制

采用Redis分布式锁防止重复提交:

public boolean submitVote(Long voteId, Long userId, List<Long> optionIds) { String lockKey = "vote:lock:" + voteId + ":" + userId; try { Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { // 执行投票逻辑 return voteService.processVote(voteId, userId, optionIds); } throw new RuntimeException("操作过于频繁"); } finally { redisTemplate.delete(lockKey); } }

3.2 实时统计方案

使用SSE(Server-Sent Events)推送结果更新:

@GetMapping("/results/{voteId}") public SseEmitter streamResults(@PathVariable Long voteId) { SseEmitter emitter = new SseEmitter(3600_000L); resultPushService.addEmitter(voteId, emitter); emitter.onCompletion(() -> resultPushService.removeEmitter(voteId, emitter)); return emitter; }

4. 安全防护体系

4.1 防刷票策略

  • IP限流(Guava RateLimiter)
  • 设备指纹校验
  • 验证码二次确认
  • 投票行为模式分析

安全配置类示例:

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/vote/create").hasRole("ADMIN") .antMatchers("/api/vote/submit").authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }

5. 性能优化实践

5.1 缓存策略

  • 使用Caffeine实现本地缓存(投票基础信息)
  • Redis缓存热门投票结果
  • 多级缓存更新策略:
graph LR A[客户端请求] --> B{本地缓存命中?} B -->|是| C[返回缓存] B -->|否| D{Redis缓存命中?} D -->|是| E[更新本地缓存并返回] D -->|否| F[查询数据库并写入缓存]

5.2 数据库优化

  • 投票记录表按用户ID哈希分片
  • 建立复合索引:
    ALTER TABLE vote_record ADD INDEX idx_search (vote_id, user_id, submit_time);
  • 使用MyBatis批量插入:
    <insert id="batchInsert" useGeneratedKeys="true"> INSERT INTO vote_record (...) VALUES <foreach collection="list" item="item" separator=","> (#{item.voteId}, #{item.userId}, ...) </foreach> </insert>

6. 部署方案

6.1 容器化部署

Docker Compose编排文件示例:

version: '3' services: vote-app: image: vote-service:1.0 ports: - "8080:8080" depends_on: - redis - mysql redis: image: redis:6-alpine ports: - "6379:6379" mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: rootpass ports: - "3306:3306"

6.2 监控配置

Prometheus监控指标示例:

- pattern: /api/vote/submit metrics: - name: vote_submit_total help: Total vote submissions labels: status: $http_status method: $http_method

7. 踩坑实录

  1. MyBatis枚举映射问题:

    • 现象:投票类型枚举值存入数据库后变成序号
    • 解决:配置typeHandlers并指定jdbcType
    <result column="type" property="type" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
  2. Redis缓存穿透:

    • 现象:高并发查询不存在的投票ID
    • 方案:布隆过滤器+空值缓存
    public Vote getVoteById(Long id) { if (!bloomFilter.mightContain(id)) { return null; } // ...正常查询逻辑 }
  3. SSE连接泄漏:

    • 现象:客户端异常断开导致服务端连接未释放
    • 修复:添加心跳检测和超时机制
    emitter.send(SseEmitter.event() .data("ping") .reconnectTime(30000));

8. 扩展方向

  1. 可视化搭建:

    • 拖拽式投票表单设计器
    • 自定义主题样式模板
  2. 智能分析:

    • 基于投票结果的用户画像
    • 选项关联性分析
  3. 多端适配:

    • 微信小程序SDK封装
    • 桌面端Electron应用

这个项目最让我惊喜的是MyBatis-Plus的动态表名功能,在处理历史投票数据归档时异常好用。建议在复杂查询场景下,可以结合QueryDSL进一步提升代码可读性。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进