ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SSM+Vue银行信贷项目实战:从环境搭建到部署避坑指南

SSM+Vue银行信贷项目实战:从环境搭建到部署避坑指南 简介这是一套基于SSM框架与Vue技术栈的银行贷款管理系统完整项目资源面向Java Web方向的课程设计、毕业设计或入门实战开发者可帮助理解前后端分离开发与银行信贷业务的数据管理流程。压缩包共7个文件大小仅4.55MB其中包含txt使用说明与说明文档、docx/doc格式的任务书与论文报告、sql数据库脚本以及ppt演示文稿覆盖从环境配置、代码阅读到项目答辩的全过程材料。已有63人浏览学习属于轻量但结构完整的实战样例。通过完整源码与配套文档读者既能对照学习SSM整合Vue的接口调用方式也能利用现成数据库脚本快速跑通项目并参考开题报告和任务书完善自己的文档写作适合快速上手或作为二次开发基础。1. 为什么拿到的ssm vue银行信贷项目别急着跑起来很多人拿到一个“ssm银行贷款管理系统vue.ZIP”第一反应是解压、导进IDEA、启动Tomcat然后等着浏览器弹出一个漂亮的登录页。我这个做过几套信贷类管理系统的老油条要泼一盆冷水这类带Vue的SSM整合项目十有八九不是能直接跑的反而是最容易让你在环境配置上浪费一整天的主儿。为什么因为SSM是SpringSpringMVCMyBatis的三层老搭档而Vue是独立的SPA前端工程两者通过JSON接口通信中间隔着“谁去启动前端页面”“接口跨域怎么放行”“打包后前端文件放哪”这些最容易出幺蛾子的环节。这个方向解决的是什么问题典型场景是银行或小贷公司的贷款申请、审批、签约、放款、还款计划查询。后端管业务数据和流程前端做操作界面。它适合谁一是做毕业设计的计算机学生二是公司里需要快速搭一个内部信贷管理原型、后期打算换成SpringBoot微服务架构的初级工程师。你可以把它当成一个“最新的SSM前后端分离参考模板”但前提是你得先弄懂它的骨架而不是双击Start按钮。2. SSM后端从applicationContext到Mapper的调用链拆解2.1 三层架构里的贷款核心模块该放哪一层拿到任何SSM项目的ZIP我建议先不看Controller先找WEB-INF/web.xml和applicationContext.xml因为SSM的“零配置”其实还是要配置。在这个信贷系统里典型的三层是Controller层接收Vue发来的JSON请求Service层写贷款审批、还款计划等业务规则MapperDao层用MyBatis直接跟MySQL打交道。不少新手容易把SQL直接写在Service里然后发现后期改个字段名要翻遍所有业务代码。我在这类项目里习惯强制规定Service层只做事务边界和业务判断不出现SqlSession或JdbcTemplate数据访问全部走Mapper接口。这个信贷系统的关键“贷款申请”模块我会拆成LoanApplyController、LoanApplyService、LoanApplyMapper三个成品类每个类对应一件事。因为银行贷款业务有很强的状态机属性待审批、已通过、已拒绝、已放款如果状态流转逻辑散落在Controller里后面接征信接口时会改到怀疑人生。2.2 手写一个贷款申请的Controller-Service-Mapper链路假设这个ZIP里已经给了实体类和Mapper接口我们要想新增一个“贷款申请提交”接口核心代码大致是这样。注意我写的不是项目里必然存在的代码而是这类系统里最常见、最可靠的写法你拿到后可以照着核对。RestController RequestMapping(/loan) public class LoanApplyController { Autowired private LoanApplyService loanApplyService; PostMapping(/apply) public Result apply(RequestBody LoanApplyRequest request) { // 把前端传来的JSON直接绑定到请求对象 LoanApply apply new LoanApply(); BeanUtils.copyProperties(request, apply); apply.setApplyTime(new Date()); apply.setStatus(PENDING); // 调用Service执行申请逻辑包括校验额度、生成申请编号 Long applyId loanApplyService.createLoanApply(apply); return Result.success(applyId); } }这段代码说明几个关键参数点RestController让接口直接返回JSONVue那边接收时不需要像JSP那样解析视图LoanApplyRequest里的字段名要和前端表单的键名一模一样比如applyAmount、termMonths、customerId否则JSON绑定会得到全nullstatus字段初始为PENDING后续审批人操作时改成APPROVED或REJECTED。再看Service层这个类的重点在事务注解和业务校验Service public class LoanApplyServiceImpl implements LoanApplyService { Autowired private LoanApplyMapper loanApplyMapper; Transactional(rollbackFor Exception.class) Override public Long createLoanApply(LoanApply apply) { // 校验贷款金额不能超过风控上限比如30万 if (apply.getApplyAmount() 300000) { throw new BusinessException(单笔贷款金额超限); } // 生成业务编号比如 L20250101 序列号 apply.setApplyNo(generateApplyNo()); loanApplyMapper.insert(apply); return apply.getId(); } }Transactional在这里很关键。信贷类操作一旦插入申请后又去写入审批记录要么都成功要么都失败否则会出现申请在、审批记录丢失的脏数据。这个教训是我在真实项目里踩过的。参数上特别注意rollbackFor Exception.class是所有SSM项目里该有的默认选择如果漏写Spring只对运行时异常回滚受检异常插入一半就完蛋了。2.3 MyBatis映射里最容易卡壳的动态SQL怎么写这个系统的Mapper层几乎全是动态SQL因为查询条件经常是“客户名、贷款状态、日期范围”多个条件可选。if标签的紧密度决定了SQL拼接对不对。select idselectLoanList resultTypeLoanApply SELECT * FROM loan_apply where if testcustomerName ! null and customerName ! AND customer_name LIKE CONCAT(%, #{customerName}, %) /if if teststatus ! null and status ! AND status #{status} /if if teststartDate ! null AND apply_time gt; #{startDate} /if /where ORDER BY apply_time DESC /selectwhere标签会自动去掉第一个多余的AND或OR这是MyBatis最优雅的处理方式不要自己写WHERE 11硬拼看着恶心而且有注入风险。这里有个容易翻车的地方日期区间查询#{}会传字符串别把gt;写成XML里会直接报错。还有LIKE CONCAT绝对优先于LIKE %${customerName}%后者虽然能跑但那是字符串拼接数据库里等着你的是SQL注入。3. Vue前端登录态与路由守卫是信贷系统的第一道闸3.1 用Vue Router做基于角色的页面路由前端工程翻开后你会看到src/router/index.js这个信贷系统必须有登录拦截不然审批页面裸奔在网上就是一个重大事故。Vue Router的beforeEach钩子就是干这个的。很多这种ZIP里路由配置用的还是Vue.use(Router)的经典写法配合动态路由参数比如{ path: /approve/:applyId, name: ApproveDetail, meta: { requiresAuth: true } }这样点击列表项跳转详情时能带上申请单ID。关键在守卫里做角色判断——信贷员和审批经理看到的菜单不一样router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.meta.requiresAuth !token) { next(/login); } else if (to.path /approve localStorage.getItem(role) ! MANAGER) { // 只有经理角色才能进审批页普通柜员直接弹回首页 next(/dashboard); } else { next(); } });这段逻辑在真实项目里会被骂“太简单”但作为基础的路由权限控制是够了。注意localStorage.getItem(role)这种方法在刷新页面时依然有效比放在Vuex里永远不重新加载要方便缺点是xss脚本能摸走它所以生产环境更推荐用sessionStorage或令牌里解码角色。这个项目的ZIP里如果只在某个页面里校验登录而没放在全局钩子里那你要自己迁移过来这是最常见的改造点。3.2 封装axios请求并带上token的必要性信贷系统里每个操作几乎都要知道“是谁在做”。如果直接在每个页面里this.$http.get(...)那token和错误处理会重复一百遍。常见做法是在src/utils/request.js里封一个axios实例import axios from axios; const service axios.create({ baseURL: /api, // 通过代理转发到后端SpringMVC timeout: 10000 }); service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token); window.location.href /login; } return Promise.reject(error); } ); export default service;baseURL这里写成/api是配合开发环境的Vue代理和线上Nginx转发。如果ZIP里后端接口没有统一/api前缀你需要在前端开发服务器里做proxy或者在后端SpringMVC里加RequestMapping(/api)的controller前缀。这个取舍决定了你项目将来能不能直接部署到同一个端口下。还有一个容易被忽略的坑response拦截器里返回的是response.data而不是response如果在代码里你还写res.data.code那就会变成response.data.data.code这等于自己给自己挖坑。我见到过至少三个项目因为这里多包或少包装一层联调时对不上字段。3.3 列表页与审批动作的交互状态设计贷款列表页在Vue里一般是表格组件加分页器。审批动作是典型的“状态改变”操作不是单纯的增删改查。比如审批通过后列表里那行的状态按钮要立刻变成“已通过”并且不能再点第二次。handleApprove(row) { this.$confirm(确认审批通过, 提示).then(() { approveLoan(row.applyId).then(res { // 不整页刷新只更新当前行状态 row.status APPROVED; this.$message.success(审批通过); }); }); }这里最值钱的细节是row.status APPROVED直接修改当前行对象而不重新调用列表接口响应会快很多。但要注意如果后端审批接口里附带更新了可用额度那列表里显示的借款额度也要一起回显所以稳妥做法是审批接口返回最新对象前端用Object.assign(row, res.data)覆盖整行。关于“按钮不能再点第二次”最好在后端接口里用状态作为乐观锁校验否则用户快速双击会创建两条审批记录。4. 数据库初始化与SSM联调信贷数据的边界坑4.1 建库建表前先补完的金额与状态字段银行贷款系统ZIP里通常附带一个.sql文件但直接运行它未必适合你的MySQL版本。我一般是先打开这个脚本检查三类字段金额字段是否用了DECIMAL(18,2)而不是FLOAT贷款状态字段是否用VARCHAR(20)加上CHECK约束客户手机号是否为唯一索引。金额、利率这些但凡跟钱沾边的一律用DECIMAL。MySQL里的FLOAT和DOUBLE是IEEE浮点数算十万元级的贷款利息能精确到分吗不能一分两分的差距在银行对账里就是雪崩。状态字段用VARCHAR存“PENDING”“APPROVED”“REJECTED”这样的英文枚举比中文“待审批”更安全因为前端判断status APPROVED和判断status 已通过编码出错概率完全不一样。另外还款计划表repayment_plan里每一个期的due_date要加索引因为用户查看“我的还款计划”时都是按日期范围查的没有索引的表到几万条数据就会慢到让你怀疑MySQL。4.2 后端接口返回的JSON字段与前端Vue对象如何对齐联调最烦的问题不是接口报错而是接口返回了前端字段全是undefined。原因很简单后端实体类用驼峰命名customerName数据库字段用下划线customer_name如果MyBatis的mapUnderscoreToCamelCase没开返回的JSON里键名就是customer_name而Vue组件里写的是row.customerName这能不undefined吗解决办法是在applicationContext-mybatis.xml或mybatis-config.xml里设这个属性settings setting namemapUnderscoreToCamelCase valuetrue/ /settings而前端这边我习惯在数组请求之后做一层“字段归一化”比如把后端返回的applyNo直接对应到表格的applyNo列不强行改成applyNumber。因为改动JSON字段名比改动后端实体类麻烦多了你得在每个用到的地方都改。真正职业做法是在后端写VO。但作为二手ZIP项目你拿到手后大概率不想动后端结构那就记住后端字段名是什么前端就写什么万不得已时用计算属性映射。4.3 跨域与代理配置在dev环境下的三种做法开发环境下前端跑在localhost:8080node环境后端SSM跑在Tomcat的8080端口这就跨域了。常见的三种解决法我按稳定性排个序第一前端开发服务器开代理。在vue.config.js里写module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这个方案的优点是浏览器无感不需要后端启用CORS适合前后端本地并行开发。注意pathRewrite如果你的后端Controller路径本来就是/loan/apply而前端请求写的是/api/loan/apply这里就必须把^/api干掉不然请求会变成/api/loan/apply后端没有映射直接404。第二后端过滤器统一加CORS头。如果你懒得配代理可以在SpringMVC的web.xml里加一个CorsFilter不过要小心allowMethods里没写PUT或DELETE前端会直接报Access-Control-Allow-Methods错误。第三干脆不用代理把Vue打包后的文件扔进SpringMVC的webapp目录下前后端同端口部署。这种方式最省事但你就别想着开发时热更新了改一行Vue代码就得重新npm run build一次。我平时是开发用代理上线用第三种。5. 打包部署避坑为什么你的ZIP项目启动就报404或连不上库5.1 现象后端war包放进Tomcat后启动正常访问却404你把这套系统后端打成war包扔进tomcat/webapps/下面Tomcat启动日志没报错但浏览器输入http://localhost:8080/loan/apply直接404。原因很常见war包在Tomcat里会解压成一个和war包同名的目录也就是说你的项目上下文路径是/ssm-loan-0.1而不是根路径/。SpringMVC的RequestMapping(/loan/apply)是相对于web应用的上下文路径的所以完整访问地址应该是http://localhost:8080/ssm-loan-0.1/loan/apply。解决如果你就是要根路径访问把war包改名为ROOT.war再放进去或者出包时在pom.xml里配置finalNameROOT/finalName。还有另一种可能是SpringMVC的前端控制器DispatcherServlet的映射url-pattern写的是*.do那你请求路径里就得带.do后缀。拿到ZIP先看web.xml里这四个参数就能省下半小时排查时间。5.2 现象前端npm run build产物挂上Nginx后刷新页面404Vue用npm run build生成dist目录你把它拷到Nginx的html目录下打开首页一切正常但一按F5刷新或者访问/approve/123这种路由Nginx直接给你404。这就是典型的前端路由模式问题。信贷系统的Vue Router如果你用了history模式刷新时Nginx会把/approve/123当成一个真实路径去找对应的静态文件结果当然找不到。解决是在Nginx配置里加一个try_fileslocation / { alias /opt/loan-frontend/; index index.html; try_files $uri $uri/ /index.html; }推荐你在开发环境就用history模式并且搭配这个Nginx配置因为hash模式虽然刷新不404但URL里带个#号在给银行演示的时候显得极不专业。这类ZIP里的前端一般默认是history模式如果你看到的却是hash多半是上一个开发者为了省事改的。5.3 现象数据库连接池报Communications link failureSpring和MyBatis整合的项目里启动时数据库连接池通常不会真正建立连接所以应用能启动成功但接口一查库就报这个错。原因千奇百怪最常见的是连接MySQL的url配置里没加useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai。MySQL 8.0以上对时区要求很严格serverTimezone少了直接报通讯链路失败。另一个坑是防火墙没放行3306端口尤其你在云服务器上部署安全组规则忘了配置本地能连服务器连不上。解决顺序先用命令行在服务器上试试mysql -h localhost -u loan_user -p排除MySQL自身问题再检查jdbc.properties里的url和密码最后在Tomcat的bin/catalina.sh加一行JAVA_OPTS 设置时区不过现在一般人都是加url参数了。这里要强调改完jdbc.properties一定要重新打包war或重启TomcatSSM项目不像SpringBoot有devtools改配置文件不会热生效。5.4 现象SSM项目里Vue打包文件放不进webapp的路径问题很多人想把Vue打包后的dist直接放进SSM项目的src/main/webapp里但IDEA里Maven构建时老是不能自动带上这些静态资源。原因在于Maven的resources默认只打包src/main/resources下的文件webapp目录由war插件处理。解决是确保你的打包方式是war并且把src/main/webapp配置为warSourceDirectory。如果你用的是SpringBoot内嵌Tomcat的方式那还要注意静态资源放在static而不是webapp。这个ZIP里如果是传统SSM你可以在IDEA里直接看Project Structure里的Web Facet资源目录是不是指向了webapp很多人在这一步导错了。另外前端打包后资源路径可能带/assets/...绝对路径如果整个站在根路径下没问题但如果你部署在/loan/子目录下则需要把vue.config.js里的publicPath改成./让资源相对路径加载否则CSS和JS全部404。这是导致“页面白屏但控制台不报错”的头号原因。6. 把系统改造成真有落地价值三个进阶技巧6.1 用拦截器注解把审批权限从Controller里抽出来这套ZIP里权限判断大概率写在每个Controller方法开头比如if(!isManager()) return ...。真上线就会面临一个问题审批角色一变代码要改好几个方法。我一般会顺手引入一个RequireRole(MANAGER)注解再写一个HandlerInterceptor拦截没有权限的请求。改造后Controller只写业务参数维护成本立刻降一个等级。这个技巧对二手项目尤其友好不会动SpringMVC核心只是加个拦截器注册。另外用拦截器统一打印请求日志也很值钱。信贷系统出问题全靠日志。你可以先粗粒度地打出“谁在什么时间调用了哪个接口”后面再逐步加参数级别日志。没有这一步将来征信对接或者放款故障排查时你连从哪几台服务器查日志都不知道。6.2 还款计划生成的两种算法与边界校验很多二手项目里还款计划是直接循环for month生成等额本息。这里有个财务上的坑等额本息的月还款额公式是R P * i * (1i)^n / ((1i)^n - 1)最后一个月要加上一个修正值因为每期利息四舍五入到分会导致最后一期剩余本金不为0。我在合规的信贷系统里看到的最稳写法是计算每期应还本金P / n应还利息剩余本金*月利率并把每期利息四舍五入到分最后一期再倒挤本金。这样做虽然前几期和等额本息的标准数值略微差几分钱但能保证整个还款计划表彻底平账符合银行的借贷记账原则。还有边界校验贷款期限n不能是0利率必须大于0放款日期必须是工作日因为计息起始日涉到节假日顺延。这部分我建议先写进Service层的校验规则里而不是留在前端表单里。原因很简单银行贷款流程可能由柜员录入客户经理审批系统很多入口都能触发业务后端不校验等于建了条独木桥。6.3 从ZIP到可维护工程补充单元测试与迁移文档最后说句经验之谈。ZIP里如果是毕设级项目没有单元测试很正常但我拿到手后会做的第一件事是给LoanApplyServiceImpl补上几个JUnit测试尤其是状态转换和金额校验方法。这类老SSM项目没有SpringBoot方便需要手动加载applicationContext.xmlRunWith(SpringJUnit4ClassRunner.class) ContextConfiguration(classpath:applicationContext.xml) public class LoanApplyServiceTest { Test public void testCreateLoanApplyIfAmountOverLimit() { // 验证超过30万的申请必须被拒绝 } }有这套测试在后面改风控规则时才敢下手。如果ZIP里有数据库脚本我会顺手整理一个CHANGELOG.sql把每次迭代的字段变更都记下来。银行系统上线一年后最值钱的就是这份变更记录因为它记录了“为什么加这个状态”“为什么这个字段从21位变更为32位”。很多翻车现场就是没人记得当初那句话。这个方向值不值得投入我的看法是作为SSM框架的经典组合信贷系统包含了事务、动态SQL、权限、跨域、打包部署这些很多业务系统都逃不过的核心问题把它吃透了你将来转SpringBoot能理解“为什么还在用这些老套路”。只是在投入时不要想着改完界面就拿去银行上线监管合规、加密机、全链路日志都还差得远——把它当一个优秀的工程学习样本刚刚好而且这套里程足够让愿意工作到凌晨的你把贷款业务和Vue交互摸得门清。希望帮到你。本文还有配套的精品资源点击获取
返回列表