1. 项目概述
这个基于SpringBoot的匿名提案系统是我在开发过程中遇到的一个非常有意思的项目。它主要解决了企业内部提案管理效率低下、信息易泄露的问题。作为一名长期从事企业级应用开发的工程师,我发现很多企业在员工提案管理方面确实存在不少痛点。
传统的人工提案管理方式存在几个明显缺陷:首先是效率问题,纸质提案需要层层传递,耗时耗力;其次是匿名性无法保证,员工担心被"秋后算账";最后是反馈机制缺失,很多有价值的建议石沉大海。这个系统就是为了解决这些问题而设计的。
系统采用前后端分离架构,前端使用Vue.js实现响应式界面,后端基于SpringBoot框架构建RESTful API,数据库选用MySQL 8.0。这种技术栈组合在当前企业级应用中非常主流,既保证了开发效率,又能满足性能需求。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型分析
选择SpringBoot作为后端框架主要基于以下几个考虑:
- 快速开发:SpringBoot的自动配置和起步依赖大大减少了样板代码
- 生态丰富:可以方便地集成Spring Security、MyBatis等常用组件
- 易于部署:内嵌Tomcat,打包成jar即可运行
- 社区支持:遇到问题可以快速找到解决方案
前端选择Vue.js是因为:
- 渐进式框架,学习曲线平缓
- 组件化开发,便于维护和复用
- 响应式数据绑定,开发体验好
- 丰富的生态系统(Vuex、Vue Router等)
数据库选用MySQL 8.0主要看中:
- 事务支持完善,ACID特性有保障
- JSON数据类型支持,便于存储半结构化数据
- 性能优异,特别是对读多写少的场景
- 成熟的集群方案,便于后期扩展
2.2 系统分层架构
系统采用经典的三层架构:
code复制表现层(View)
↓
业务逻辑层(Controller/Service)
↓
数据访问层(DAO)
↓
数据库(MySQL)
表现层使用Vue.js构建SPA应用,通过axios与后端交互。这里我特别设计了响应式布局,确保在PC和移动设备上都有良好的体验。
业务逻辑层采用SpringBoot实现,包含:
- Controller:处理HTTP请求,参数校验
- Service:核心业务逻辑
- DTO:数据传输对象
- Exception:统一异常处理
数据访问层使用MyBatis-Plus,相比原生MyBatis,它提供了很多开箱即用的功能,比如:
- 通用CRUD操作
- 条件构造器
- 分页插件
- 性能分析插件
3. 核心功能实现
3.1 匿名提案机制
匿名性是本系统的核心特性,我采用了多层次的匿名保护方案:
- 用户标识处理:
java复制// 使用UUID替代用户ID
String anonymousId = UUID.randomUUID().toString().replace("-","");
proposal.setSubmitterId(anonymousId);
- 数据脱敏:
java复制// 敏感信息加密存储
public String encrypt(String content) {
return DigestUtils.md5DigestAsHex(
(content + salt).getBytes()
);
}
- 访问控制:
java复制@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/proposals/{id}")
public ProposalDTO getProposalDetails(@PathVariable Long id) {
// 管理员也只能看到脱敏后的信息
return proposalService.getById(id);
}
3.2 提案管理流程
提案的生命周期管理是系统的另一个核心功能:
- 提案提交:
java复制public Result submitProposal(ProposalSubmitDTO dto) {
// 1. 参数校验
validateSubmitParams(dto);
// 2. 生成匿名标识
String anonymousId = generateAnonymousId();
// 3. 保存提案
Proposal proposal = convertToEntity(dto);
proposal.setStatus(ProposalStatus.PENDING);
proposal.setSubmitterId(anonymousId);
proposalMapper.insert(proposal);
// 4. 发送通知
notificationService.notifyAdmins(proposal);
return Result.success(proposal.getId());
}
- 提案审核:
java复制@Transactional
public Result reviewProposal(ProposalReviewDTO dto) {
Proposal proposal = getById(dto.getId());
if (proposal == null) {
throw new BusinessException("提案不存在");
}
// 更新状态
proposal.setStatus(dto.getApproved() ?
ProposalStatus.APPROVED : ProposalStatus.REJECTED);
proposal.setReviewComment(dto.getComment());
updateById(proposal);
// 记录审核日志
auditLogService.l
