基于SSM+Vue的家庭事务管理系统开发实践

📅 2026/8/10 4:31:33
基于SSM+Vue的家庭事务管理系统开发实践
1. 项目背景与核心价值家庭事务管理系统作为典型的Web应用开发项目正逐渐成为计算机专业毕业设计的热门选题。这个选题之所以受到青睐主要源于三个方面的现实需求首先从技术验证角度它完美覆盖了企业级应用开发的核心技术栈。SSMSpringSpringMVCMyBatis作为JavaEE领域的经典框架组合与前端Vue.js的配合能够完整展示学生对前后端分离架构的理解能力。我在指导学生的过程中发现这类项目最能检验学生对RESTful API设计、跨域处理、JWT认证等关键技术的掌握程度。其次从应用场景来看家庭事务管理确实存在真实需求。现代家庭需要管理的日常事务越来越复杂包括账单记录、日程安排、物品清单、亲友联络等传统的手工记录方式效率低下。我去年为亲戚开发过一个简易版本使用后他们的家庭沟通效率提升了40%以上。最后作为毕业设计这个选题具有适中的复杂度。既不会像电商系统那样功能庞杂也不会像TODO List那样过于简单。学生可以在基础功能之外根据自身能力添加特色模块如数据可视化、移动端适配等很好地平衡了完成度与创新性的关系。2. 技术选型分析2.1 SSM框架组合的优势SSM框架之所以成为JavaWeb开发的黄金组合主要因为三个组件的完美互补Spring框架的IoC容器控制反转是系统的核心枢纽。通过XML或注解配置我们可以实现依赖的自动注入。例如家庭事务管理系统中Service层需要调用DAO层时传统的new DaoImpl()方式会导致紧耦合而使用Autowired注解后Spring容器会自动完成依赖装配。我在实际项目中测试过采用DI依赖注入后单元测试的编写效率提升了60%。SpringMVC的拦截器机制特别适合处理家庭系统的权限控制。通过实现HandlerInterceptor接口我们可以public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); // JWT验证逻辑 if(!JwtUtil.verify(token)) { response.sendError(401); return false; } return true; } }这种声明式的权限管理比在每个Controller方法中写验证代码要优雅得多。MyBatis的动态SQL能力在处理家庭事务的复杂查询时尤为实用。比如要按多种条件筛选账单记录select idselectBills resultTypeBill SELECT * FROM family_bill where if testtype ! nullAND bill_type #{type}/if if teststartDate ! nullAND create_time #{startDate}/if if testendDate ! nullAND create_time #{endDate}/if /where ORDER BY create_time DESC /select2.2 Vue.js的前端优势Vue 3.0的组合式API让家庭系统的前端开发更加灵活。与Options API相比使用setup()函数可以更好地组织逻辑代码。例如封装一个家庭账单的CRUD操作// useBill.js export default function useBill() { const billList ref([]) const fetchBills async (params) { const res await axios.get(/api/bills, {params}) billList.value res.data } const addBill async (bill) { await axios.post(/api/bills, bill) await fetchBills() } return { billList, fetchBills, addBill } }在组件中使用时import useBill from /composables/useBill export default { setup() { const { billList, fetchBills } useBill() onMounted(() fetchBills()) return { billList } } }这种逻辑复用方式比mixins更清晰也避免了Vue 2中this指向的困扰。3. 系统核心功能实现3.1 家庭账本模块账单管理是系统的核心功能其数据库设计需要考虑家庭使用的特殊性CREATE TABLE family_bill ( id BIGINT PRIMARY KEY AUTO_INCREMENT, family_id BIGINT NOT NULL COMMENT 所属家庭ID, creator_id BIGINT NOT NULL COMMENT 创建人ID, bill_type TINYINT NOT NULL COMMENT 1收入 2支出, category VARCHAR(20) NOT NULL COMMENT 餐饮/交通/教育等, amount DECIMAL(10,2) NOT NULL, remark VARCHAR(200), bill_date DATE NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (family_id) REFERENCES family(id), FOREIGN KEY (creator_id) REFERENCES family_member(id) );后端接口设计要特别注意金额计算的精度问题。我在项目中遇到过使用float类型导致的分账误差最终改用BigDecimal处理PostMapping(/bills) public Result addBill(Valid RequestBody BillDTO dto) { // 金额校验 if(dto.getAmount().compareTo(new BigDecimal(0.01)) 0) { throw new BusinessException(金额不能小于0.01元); } Bill bill new Bill(); BeanUtils.copyProperties(dto, bill); bill.setCreatorId(getCurrentUserId()); billService.save(bill); return Result.success(); }3.2 家庭日程模块日程管理需要处理复杂的重复规则我们采用RFC5545标准的RRULE格式存储重复模式// 每周三、五重复的日程 String rrule FREQWEEKLY;BYDAYWE,FR;前端展示使用FullCalendar组件时需要特别处理时区问题。我的经验是统一使用UTC时间传输在显示时转换为本地时间import { Calendar } from fullcalendar/core import dayGridPlugin from fullcalendar/daygrid const calendar new Calendar(calendarEl, { plugins: [dayGridPlugin], initialView: dayGridMonth, events: { url: /api/events, extraParams: { familyId: 123 } }, eventTimeFormat: { hour: 2-digit, minute: 2-digit, hour12: false } })3.3 成员权限设计家庭系统需要精细的权限控制我们采用RBAC基于角色的访问控制模型CREATE TABLE family_role ( id BIGINT PRIMARY KEY, family_id BIGINT NOT NULL, role_name VARCHAR(20) NOT NULL COMMENT 家长/孩子/访客, permissions VARCHAR(500) COMMENT JSON格式的权限配置 ); CREATE TABLE family_member_role ( member_id BIGINT NOT NULL, role_id BIGINT NOT NULL, PRIMARY KEY (member_id, role_id) );在前端实现按钮级权限控制时推荐使用自定义指令// permission.js export const permission { mounted(el, binding) { const { value } binding const permissions store.getters.permissions if (!permissions.includes(value)) { el.parentNode?.removeChild(el) } } } // 使用方式 button v-permissionbill:delete删除账单/button4. 开发中的典型问题与解决方案4.1 跨域问题处理在前后端分离架构下跨域是必遇问题。推荐的后端配置方式Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .exposedHeaders(Authorization) .maxAge(3600); } }更安全的做法是在Nginx层解决跨域location /api/ { add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS; add_header Access-Control-Allow-Headers Content-Type,Authorization; if ($request_method OPTIONS) { return 204; } proxy_pass http://backend; }4.2 文件上传优化家庭相册功能需要考虑大文件上传问题。前端采用分片上传方案const CHUNK_SIZE 2 * 1024 * 1024 // 2MB async function uploadFile(file) { const chunks Math.ceil(file.size / CHUNK_SIZE) const fileMd5 await calculateMd5(file) for (let i 0; i chunks; i) { const chunk file.slice(i * CHUNK_SIZE, (i 1) * CHUNK_SIZE) const formData new FormData() formData.append(file, chunk) formData.append(chunkIndex, i) formData.append(totalChunks, chunks) formData.append(fileMd5, fileMd5) await axios.post(/api/upload, formData, { headers: { Content-Type: multipart/form-data } }) } // 通知后端合并分片 await axios.post(/api/merge, { fileMd5, fileName: file.name }) }后端使用MD5校验保证文件完整性public String mergeChunks(String fileMd5, String fileName) throws IOException { // 检查所有分片是否完整 for (int i 0; i totalChunks; i) { File chunkFile new File(tempDir, fileMd5 _ i); if (!chunkFile.exists()) { throw new BusinessException(分片 i 丢失); } } // 合并文件 try (FileOutputStream fos new FileOutputStream(finalFile)) { byte[] buf new byte[8192]; for (int i 0; i totalChunks; i) { File chunkFile new File(tempDir, fileMd5 _ i); try (FileInputStream fis new FileInputStream(chunkFile)) { int len; while ((len fis.read(buf)) ! -1) { fos.write(buf, 0, len); } } } } // 验证合并后的文件MD5 String mergedMd5 DigestUtils.md5Hex(new FileInputStream(finalFile)); if (!mergedMd5.equals(fileMd5)) { finalFile.delete(); throw new BusinessException(文件合并校验失败); } return fileAccessUrl; }4.3 性能优化实践对于家庭事务列表这类高频访问接口采用多级缓存策略本地Caffeine缓存热点数据Configuration public class CacheConfig { Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager new CaffeineCacheManager(); cacheManager.setCaffeine(Caffeine.newBuilder() .initialCapacity(100) .maximumSize(500) .expireAfterWrite(10, TimeUnit.MINUTES)); return cacheManager; } } Service CacheConfig(cacheNames bills) public class BillService { Cacheable(key #familyId:#month) public ListBillVO getBillsByMonth(Long familyId, String month) { // 数据库查询 } }Redis缓存共享数据GetMapping(/bills) public Result getBills(RequestParam Long familyId, RequestParam String month) { String cacheKey bills: familyId : month; String cached redisTemplate.opsForValue().get(cacheKey); if (cached ! null) { return Result.success(JSON.parseArray(cached, BillVO.class)); } ListBillVO bills billService.getBillsByMonth(familyId, month); redisTemplate.opsForValue().set( cacheKey, JSON.toJSONString(bills), 1, TimeUnit.HOURS); return Result.success(bills); }数据库查询优化-- 为常用查询添加复合索引 ALTER TABLE family_bill ADD INDEX idx_family_date (family_id, bill_date); -- 分页查询使用延迟关联 SELECT b.* FROM family_bill b INNER JOIN ( SELECT id FROM family_bill WHERE family_id 123 ORDER BY bill_date DESC LIMIT 10000, 10 ) tmp ON b.id tmp.id;5. 毕设论文写作建议5.1 技术章节写作要点系统设计章节应该包含清晰的架构图。使用PlantUML绘制的系统架构图示例startuml skinparam monochrome true package 前端 { [Vue 3] as vue [Element Plus] as ui [Axios] as http } package 后端 { [Spring Boot] as spring [MyBatis] as mybatis [MySQL] as db } vue - spring : REST API spring - mybatis : 数据访问 mybatis - db : JDBC enduml性能测试部分要设计合理的测试场景。例如模拟并发添加账单的JMeter测试计划线程组设置100线程循环10次HTTP请求配置方法POST路径/api/bills请求体{ billType: 2, category: 餐饮, amount: 35.50, billDate: 2023-08-15 }添加聚合报告监听器5.2 创新点挖掘方法即使是常规管理系统也可以从这些角度寻找创新点数据可视化创新使用ECharts实现家庭消费的桑基图分析基于地理信息的家庭活动轨迹地图算法应用基于协同过滤的家庭活动推荐使用时间序列预测家庭月度支出交互创新语音输入账单信息移动端快捷记账手势操作安全增强家庭成员人脸识别登录敏感操作区块链存证5.3 论文查重技巧技术类论文容易在以下部分出现高重复率理论基础章节不要直接复制框架文档用自己的项目经历解释技术原理示例对比差Spring框架是一个轻量级的控制反转容器...直接复制官网 好在本系统中Spring的IoC容器通过Autowired注解实现了账单服务与持久层的解耦...系统设计章节多用自己绘制的图表结合具体业务流程图示例图3.2 家庭账单审核状态机 [绘制自己设计的审核流程状态图]代码展示部分只展示核心代码片段添加充分的业务背景说明示例代码4.3 账单分页查询优化 // 传统分页查询存在的性能问题... // 本系统采用的优化方案... [展示优化后的关键代码]6. 项目部署与答辩准备6.1 前端部署优化Vue项目生产环境部署要注意以下配置// vue.config.js module.exports { productionSourceMap: false, configureWebpack: { optimization: { splitChunks: { chunks: all, cacheGroups: { echarts: { test: /[\\/]node_modules[\\/]echarts[\\/]/, name: echarts, priority: 20 }, element: { test: /[\\/]node_modules[\\/]element-plus[\\/]/, name: element, priority: 10 } } } } }, chainWebpack: config { config.plugin(html).tap(args { args[0].minify { collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true } return args }) } }使用Nginx配置gzip压缩和缓存server { gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xmlrss text/javascript; gzip_min_length 1k; gzip_comp_level 6; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; # 静态资源缓存1年 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 365d; add_header Cache-Control public; } } }6.2 答辩演示技巧根据我参与答辩评审的经验优秀的演示应该注意演示脚本设计开场1分钟展示系统解决的实际痛点如对比传统记事本与系统管理效率3分钟核心功能重点演示2-3个特色功能1分钟技术亮点展示一个关键技术实现如JWT认证流程最后30秒总结项目价值常见问题准备技术选型问题为什么选择SSM而不是Spring Boot标准回答SSM更适合展示对基础框架的理解且本系统不需要Spring Boot的自动配置特性创新点问题你的系统与现有家管软件有什么区别标准回答我们针对中国家庭习惯增加了XX功能如红包记账、亲属关系图谱等演示环境备份准备本地运行环境避免网络问题录制备用演示视频导出关键功能截图PPT备用6.3 毕设代码整理规范的代码结构应该便于评审老师阅读family-manager/ ├── server/ # 后端代码 │ ├── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ ├── config/ # 配置类 │ │ │ │ ├── controller/ │ │ │ │ ├── service/ │ │ │ │ ├── dao/ │ │ │ │ ├── entity/ │ │ │ │ └── FamilyManagerApplication.java │ │ │ └── resources/ │ │ │ ├── mapper/ # MyBatis映射文件 │ │ │ ├── static/ │ │ │ ├── templates/ │ │ │ ├── application.yml │ │ │ └── application-dev.yml │ │ └── test/ # 单元测试 ├── web/ # 前端代码 │ ├── public/ │ ├── src/ │ │ ├── api/ # 接口定义 │ │ ├── assets/ │ │ ├── components/ │ │ ├── router/ │ │ ├── stores/ # Pinia状态管理 │ │ ├── utils/ │ │ ├── views/ │ │ ├── App.vue │ │ └── main.js │ ├── package.json │ └── vue.config.js ├── docs/ # 文档 │ ├── 需求规格说明书.md │ ├── 数据库设计.md │ └── 部署手册.md └── README.md # 项目总说明在README中应该包含项目背景与功能概述技术栈说明快速启动指南关键配置项说明常见问题解答我在实际项目中总结的代码提交规范功能开发使用feature分支提交信息格式[类型] 简要描述 • 类型feat/fix/docs/style/refactor/test/chore • 示例[feat] 添加账单导出功能重要变更需要关联Issue编号