尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+Vue+MyBatis+MySQL个人理财系统企业级开发实战

SpringBoot+Vue+MyBatis+MySQL个人理财系统企业级开发实战 1. 项目整体设计与技术选型拆解1.1 为什么这套技术栈是企业级个人理财系统的首选做个人理财系统用SpringBootVueMyBatisMySQL这套组合是目前中小企业级项目里最稳的搭配。这个项目不是简单的增删改查而是涵盖了账目管理、预算控制、报表分析、权限管理等多个模块的完整落地实现。很多人一听到“个人理财”就觉得这是个小玩具但真把它往“企业级”标准靠拢时你会发现需要思考的东西远比写几个接口复杂。先说说技术栈选型。SpringBoot解决了传统Spring配置繁琐的问题内嵌Tomcat改完代码不用重启容器配合Maven或Gradle可以快速构建项目骨架。Vue作为前端框架双向绑定和组件化让页面开发效率成倍提升特别是配合Element-UI这类组件库后台管理界面一周就能搭出来。MyBatis则把SQL控制权完全交还给你复杂报表查询、多表关联、动态条件拼接都很好用比JPA那种自动生成的SQL更容易掌控。MySQL作为关系型数据库对于个人理财这种数据量级——即使百万级流水也完全能扛住加上InnoDB事务支持能保证账目数据的一致性。之所以说“企业级”不是说要做成分布式微服务而是按照工程化标准去组织代码数据层、服务层、控制层分层清晰有统一异常处理、统一响应格式、权限校验、参数校验、日志记录。这些看似繁琐的规矩恰恰是项目能持续迭代不烂掉的关键。很多自学项目做到后面改一个需求要动十几个文件就是因为一开始没定好结构。1.2 核心功能模块与系统边界划分个人理财系统听起来简单但真正落地需要拆成几个相互独立的模块。我从实际项目里总结至少要覆盖这六块用户中心负责注册、登录、密码加密、token鉴权账目管理记录收入、支出流水支持分类、备注、多账户预算管理按月度设置分类预算超出时预警统计分析按时间、分类汇总收支生成报表资产管理维护银行卡、现金、支付宝等账户余额基础数据维护收支分类、支付方式等字典项。模块划分的意义在于边界清晰。比如账目管理只负责流水数据写入和查询预算管理则监听流水变化或通过服务调用来判断是否触发预警这样不会出现一个Service类写了几千行、后面完全不敢动的局面。我在最初设计时就没有把预算逻辑塞进账目Service里而是单独抽出BudgetService通过事件或者直接调用的方式联动实测下来扩展新功能时改动量少很多。1.3 数据库表结构设计从收支流水到预算报表数据库设计是这类系统的地基。我的核心表设计如下user用户表字段包括id、username、passwordBCrypt加密、nickname、avatar、create_time。account账户表关联用户字段包括id、user_id、account_name、account_type、balance、currency。category分类表类型为income或expense支持父级分类形成树形。transaction流水表这是最核心的表字段包括id、user_id、account_id、category_id、amount保留两位小数、type0支出/1收入、transaction_time、remark。budget预算表关联用户和分类字段包括id、user_id、category_id、budget_amount、month格式YYYY-MM。budget_alert预算预警记录表记录触发预警时间。流水表一定要加索引我一般在user_id transaction_time上建联合索引因为查询基本都是按用户和时间段过滤。另外金额字段用DECIMAL(10,2)千万别用FLOAT或DOUBLE否则浮点误差会让你对账对到怀疑人生。MyBatis生成代码时如果需要避免字段映射错误记得开启驼峰命名转换mapUnderscoreToCamelCase设为true否则下划线字段映射不到Java属性。2. 从零搭建SpringBootVueMyBatis项目2.1 后端脚手架搭建SpringBootMyBatis配置详解创建SpringBoot项目我用的是Spring Initializr选择合适的SpringBoot版本。这里有个坑SpringBoot 3.x要求JDK17如果你的环境是JDK8最好用SpringBoot 2.7.x别盲目追新导致本地环境配不起来。依赖需要引入spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java、lombok、spring-boot-starter-validation。application.yml里需要配置数据源以MySQL 8.0为例spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/personal_finance?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl注意MyBatis的mapper-locations指向XML文件这样SQL和Java代码分离后期维护SQL更清晰。StdOutImpl是控制台打印SQL测试阶段强烈建议打开方便排查问题。生产环境记得关掉。实体类可以直接用Lombok的Data简化但要注意MyBatis映射时不关心Lombok它用的是底层setter所以字段命名规范一定要正确。我习惯在Mapper接口里写Mapper注解并在启动类上扫描两个都可以二选一即可。2.2 前端Vue工程初始化与路由/状态管理规划前端用Vue2还是Vue3新项目我建议Vue3Composition API配合Vite构建速度快很多。但如果是跟着老教程学Vue2Element-UI也可以毕竟生态成熟。个人理财系统的典型页面包括登录注册页、仪表盘、流水记录页、报表页、预算页、账户页。用Vue Router管理路由需要做好路由守卫未登录用户强制跳转登录页。创建Vue3项目npm create vitelatest finance-web -- --template vue cd finance-web npm install npm install vue-router4 pinia axios element-plus路由配置示例import { createRouter, createWebHistory } from vue-router const router createRouter({ history: createWebHistory(), routes: [ { path: /, component: () import(/views/Home.vue), meta: { requiresAuth: true } }, { path: /login, component: () import(/views/Login.vue) } ] }) router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })状态管理用Pinia比Vuex更简单适合个人项目。我在全局store里保存用户信息、登录状态、菜单权限。封装axios实例时统一拦截器请求头带上token响应拦截器处理401跳登录、错误消息提示。2.3 前后端联调与统一响应体设计前后端分离开发最怕接口格式不统一。我在后端定义统一返回体ResultT包含code、message、data三个字段。枚举状态码里200成功、500业务错误、401未认证。SpringBoot中通过RestControllerAdvice全局捕获异常保证任何情况返回的都是标准格式。前端axios封装如下axios.interceptors.response.use( response response.data, error { if (error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )联调阶段通过Vite的proxy配置代理转发到后端地址避免跨域问题。vite.config.js里设置server.proxy把/api前缀请求转发到localhost:8080。后端接口统一加/api前缀这样前端代理和路由都干净。3. 核心业务模块的落地实现3.1 用户认证与权限拦截从JWT到方法级权限企业级系统的第一步是身份认证。我用JWT实现无状态登录用户登录成功后生成token前端存储到localStorage每次请求放到Authorization头。后端写一个JwtUtil工具类使用io.jsonwebtoken库生成和解析token密钥放在配置文件里。登录ControllerPostMapping(/login) public Result login(RequestBody Valid LoginDTO dto) { User user userService.findByUsername(dto.getUsername()); if (user null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) { throw new BizException(用户名或密码错误); } String token jwtUtil.createToken(user.getId(), user.getUsername()); return Result.success(Collections.singletonMap(token, token)); }密码存储一定用BCrypt加密Spring Security的BCryptPasswordEncoder可以直接用不要自己搞MD5。在拦截器里校验token解析用户id放到ThreadLocal供业务使用。权限拦截除了登录验证个人理财系统其实没有太多角色区分但为了“企业级”面子可以加一个简单的RequirePermission注解在预算删除等敏感操作上校验管理员角色。踩过的坑JWT的token有效期不宜太长我给的是2小时过期后前端用刷新接口获取新token。刷新token用一个refreshToken字段存到httpOnly Cookie里降低XSS风险。3.2 账目流水管理收支记录与分页查询的MyBatis实践流水管理是核心关键是增删改查但查要查得爽。列表查询支持按时间范围、分类、类型、账户筛选还要支持分页和导出。用MyBatis动态SQL处理条件拼接非常方便。Mapper XML写法select idselectPage resultTypecom.example.entity.TransactionVO SELECT t.id, t.amount, t.type, t.transaction_time, c.name as categoryName, a.account_name as accountName FROM transaction t LEFT JOIN category c ON t.category_id c.id LEFT JOIN account a ON t.account_id a.id where t.user_id #{userId} if teststartDate ! null AND t.transaction_time gt; #{startDate} /if if testendDate ! null AND t.transaction_time lt; #{endDate} /if if testtype ! null AND t.type #{type} /if if testcategoryId ! null AND t.category_id #{categoryId} /if /where ORDER BY t.transaction_time DESC /select注意日期格式转换前端传字符串后端用DateTimeFormat接收。MyBatis的if能很好处理空条件但别忘了user_id这个必带条件不然后台用户能看到别人数据。我在拦截器里统一设置userId到ThreadLocalMapper参数动态获取防止越权。新增流水时会涉及账户余额更新这里要开事务。在Service方法上加Transactional先插入流水再更新对应账户的余额。如果后续还有预算预警可以在提交后调用BudgetService检查本月该分类是否超过预算如果超过就生成预警记录。这里我用Spring的事件发布机制解耦ApplicationEventPublisher发布一个TransactionCreatedEvent预算监听器去处理这样代码不会耦合太重。3.3 预算预警与统计报表的SQL聚合实现预算模块的核心是月度分类预算用户设定某分类本月预算金额系统统计当月该分类支出合计超过阈值就提醒。统计SQL用SUM和GROUP BYSELECT category_id, SUM(amount) as total FROM transaction WHERE user_id #{userId} AND type 0 -- 支出 AND DATE_FORMAT(transaction_time, %Y-%m) #{month} GROUP BY category_id报表模块需要按天/周/月汇总收支柱状图的数据用SQL按日期分组比如近7天支出折线图SELECT DATE(transaction_time) as date, SUM(amount) as amount FROM transaction WHERE user_id #{userId} AND type 0 AND transaction_time #{startDate} GROUP BY DATE(transaction_time)这里注意时区问题MySQL的DATE方法会根据数据库时区转换如果服务器和客户端时区不一致可能导致数据偏差。我在配置连接时加了serverTimezoneAsia/Shanghai并在JVM中设置时区配合。报表前端用ECharts后端返回JSON数组Vue组件直接渲染。饼图展示分类占比时SQL查询每个分类的总金额再按金额大小排序。这里有个性能点统计表查询如果数据量大可以考虑增加一个每日汇总表通过定时任务或触发器维护查询直接读汇总表。但个人理财系统数据量不大直接聚合完全够用没必要过度设计。4. 常见坑点、性能优化与部署实践4.1 开发中踩过的高频问题与排查方法在开发中我整理了一份踩坑清单希望对你有帮助。问题1数据库日期时间字段变成当前时间这是MySQL驱动传参时区导致的。解决方案是JDBC URL里加serverTimezoneAsia/Shanghai实体字段用LocalDateTime配合JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。问题2MyBatis查询数据库能查到接口返回null。大概率是字段映射问题。检查mapUnderscoreToCamelCase是否开启或者实体类字段名是否和列名一一对应。另外resultType如果是VO类里面要有对应的setter。问题3Vue打包部署后刷新404。Vue Router处于history模式刷新请求路径找不到后端映射。解决方式是在SpringBoot中配置ForwardController把所有未匹配地址转发到index.html或者后端WebMvcConfigurer添加路由兜底。问题4跨域请求失败。前后端不在同端口需要后端开启CorsFilter允许前端origin和携带凭证。注意如果用了Spring Security还要在SecurityConfig里放行OPTIONS请求。问题5多用户数据串号。一定是查询条件漏了user_id。我在开发中强制要求所有查询Mapper都包含userId并且禁止在Mapper接口中未带userId就访问流水。审查代码时优先检查这一类问题。4.2 数据库索引优化与MyBatis缓存使用细节流水表的数据量增长快索引设计直接影响查询速度。除了在user_id和transaction_time上建联合索引还需要注意分页查询的深翻页问题。当页码很大时LIMIT 10000, 20效率低可以使用子查询延后聚合先查id列表再关联。MyBatis的二级缓存在多表查询时容易踩坑因为一个表的更新会影响多个表的缓存如果缓存配置不当会产生脏数据。个人理财系统业务场景中我不建议开启二级缓存就用默认的一级缓存SqlSession级别配合Redis做业务缓存反而更可靠。预算统计结果可以缓存到Redis 30分钟减少数据库压力。数据库层面定期检查慢查询日志MySQL的EXPLAIN是排查SQL执行计划的利器。如果某个统计SQL出现文件排序Using filesort考虑增加排序字段索引。金额字段不要做字符串处理保持DECIMAL类型。4.3 Vue项目打包集成到SpringBoot的部署方案完成后端开发后把Vue前端打包成静态文件放到SpringBoot的src/main/resources/static目录这样打出一个jar包就能部署省去Nginx配置的麻烦。具体步骤前端执行npm run build生成dist目录。把dist里的文件复制到后端src/main/resources/static。编写一个WebConfig实现WebMvcConfigurer重写addViewControllers把根路径和未匹配路径转发到index.html。后端打jar包java -jar finance-system.jar启动。启动后访问http://ip:8080/就能看到系统。打包时注意前端请求路径如果接口是/api开头后端API Controller带上这个前缀打包后的静态资源请求就不会冲突。生产环境我建议把静态资源和后端分开部署用Nginx托管静态文件后端只提供API。这样前端资源更新可以单独发布不用重新打jar包。但学习项目或小规模部署合并jar包是最省事的我最初用这种方式租台最便宜的云服务器就能上线。最后再分享一个小技巧开发阶段Vite的代理和后端地址关联时把proxy配置里的target改成局域网IP手机连同一WiFi就能通过电脑IP测试移动端页面不用每次打生产包验证。整个项目从零搭建到部署我大概用了两周其中数据库设计和联调最耗时间如果你照着这个思路动手踩坑点少一大半。真实的项目就是在这些问题里磨出来的多写几遍自然就熟了。
返回列表