ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java全栈毕设源码:SpringBoot+Vue客户关系管理系统(CRM)完整实战

Java全栈毕设源码:SpringBoot+Vue客户关系管理系统(CRM)完整实战 我做毕设辅导这些年被问得最多的一个需求就是“有没有一套完整的Java全栈项目源码”。要么是学校要求SpringBootVue的技术栈要么是课设需要能现场演示的业务系统要么是自学Java想找个能跑通全流程的项目练手。今天这套客户关系管理系统管理平台就是为这些场景准备的。它本质上是一个标准的前后端分离CRM系统后端用SpringBoot整合MyBatis-Plus操作MySQL前端用Vue.js搭配Element UI做界面包含客户信息管理、跟进记录、商机管理、合同管理、数据统计、用户权限登录这些核心业务模块。相比网上那种只给你一个登录页加一张用户表的半成品这套源码的业务链路是完整的从客户录入到商机转化到数据看板每一环都打通了。换句话说它不只是给你演示用的皮囊而是能让你真正理解“企业级系统大概长什么样”的活标本。如果你正在准备毕业设计、课程设计或者刚学完SSM/SpringBoot想找个综合性项目沉淀一下这套源码可以当脚手架用。接下来我会把这套系统的技术选型逻辑、核心模块设计、实操跑通步骤和我踩过的坑全部拆开讲清楚。1. 项目整体设计与技术选型解析1.1 为什么是SpringBoot Vue.js这个组合市场上毕设项目那么多有的用JSP有的用Thymeleaf服务端渲染但近几年高校和企业面试的导向已经非常明确前后端分离成了主流。SpringBoot负责RESTful API接口Vue.js负责单页应用交互两者通过JSON格式的数据进行通信这种架构在企业里是面试基本盘在毕设答辩里也容易出彩。选SpringBoot而不是传统SpringMVC核心原因是它解决了大量繁琐的配置问题。以前写一个SSM项目要配置web.xml、Spring配置文件、MyBatis映射文件、事务管理器光搭环境就得折腾大半天。SpringBoot通过自动配置和starter机制把常用组件都集成好了你只要引入依赖并写好application.yml十分钟之内就能把后端服务跑起来。比如下面这段配置就完成了数据源和MyBatis-Plus的初始化server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/crm_db?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0Vue.js这边则解决了前后端分离后“数据到UI”的绑定问题。给一张客户表格传统JSP要在服务端循环输出HTML标签每次筛选客户都必须刷新整个页面。Vue的数据驱动视图机制不一样页面加载时发一个Ajax请求拿到客户数组数据一变表格自动重新渲染整个过程无刷新。配合Element UI的表格组件实现分页排序搜索几行代码就能完成开发效率比原生JavaScript拼接字符串高太多。这套组合的另一个好处是分工明确。你写后端接口时不需要关心前端长什么样写前端页面时只需要mock接口数据联调阶段再统一对接。我的经验是一个人做全栈项目时这种分离能有效降低自己的心智负担调试接口用Postman调试页面用浏览器哪一层出了问题一目了然。1.2 系统的功能模块全景一个合格的CRM系统核心业务通常围绕“客户生命周期”来展开——从获取潜在客户开始到跟进沟通再到商机推进、签订合同最后统计分析每一步都需要在系统中留痕。这套源码的模块划分也是按照这个逻辑来做的。客户管理维护客户基本信息支持按名称、行业、等级、来源筛选分页列表展示可新增、编辑、删除、批量导入导出。跟进记录每条客户下可记录多次跟进包含沟通方式、跟进内容、下次跟进时间形成完整的客户交流历史。商机管理把有潜在购买意向的客户转化为商机设置商机阶段意向客户、方案报价、谈判中、赢单/输单记录预计成交金额。合同管理赢单后与客户签约维护合同编号、金额、签约日期、合同状态。数据统计用ECharts展示客户来源分布饼图、商机阶段漏斗图、月度成交趋势折线图等。系统管理用户登录、角色分配、菜单管理使用JWT做身份认证不同角色登录后可见页面不同。这套模块设计最大的好处是业务完整且复杂度适中。你说它简单它有角色权限和统计图表你说它复杂每个模块的增删改查结构又是统一的。这种“边缘业务复杂但主干清晰”的特点恰好是毕设和课设需要的太简单没体现工作量太难则做不完。1.3 数据库模型的设计思路数据库是整个系统的地基。我的建议是配色消息表E-R分析图先理清实体关系再动手建表。这套系统涉及的实体包括用户、角色、客户、跟进记录、商机、合同它们之间的关系是用户与角色多对多通过user_role中间表连接。客户与跟进记录一对多一个客户对应多条跟进记录。客户与商机一对多一个客户可以产生多个商机。商机与合同一对一一个赢单商机对应一份合同。数据库命名上要养成好习惯表名统一复数形式字段名使用snake_case主键都用id创建时间和更新时间统一叫create_time和update_time。这里给出客户表的大致结构作为参考CREATE TABLE customer ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, name VARCHAR(100) NOT NULL COMMENT 客户名称, industry VARCHAR(50) COMMENT 所属行业, level TINYINT COMMENT 客户等级1重要,2普通,3次要, source VARCHAR(50) COMMENT 客户来源, phone VARCHAR(20) COMMENT 联系电话, address VARCHAR(200) COMMENT 联系地址, remark TEXT COMMENT 备注, deleted TINYINT DEFAULT 0 COMMENT 逻辑删除标记, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT客户信息表;注意我在表里加了deleted字段做逻辑删除。这是企业开发的通用做法不用DELETE语句物理删除数据而是把记录标记为已删除哪天误删了还能恢复。MyBatis-Plus内置了逻辑删除支持配置好之后你调用deleteById它自动帮你执行UPDATE语句查询时自动过滤掉deleted1的数据。这个细节在毕业论文里写一笔属于加分项。2. 核心细节解析与实操要点2.1 后端接口的分层设计与统一响应很多初学者写Controller时习惯把业务逻辑全堆在方法里一个方法几十行SQL操作写是快但后期维护和答辩都很难看。这套源码的分层是标准的五层架构Controller接收请求→ Service接口业务逻辑→ ServiceImpl实现类具体实现→ Mapper数据访问→ 实体类数据模型。举个保存客户的例子。Controller层只负责接收前端传来的JSON数据并调用Service自己不做任何业务判断RestController RequestMapping(/api/customer) public class CustomerController { Resource private CustomerService customerService; PostMapping public Result save(RequestBody Customer customer) { customerService.saveCustomer(customer); return Result.success(); } }Service层处理校验逻辑和业务规则比如判断客户名称是否重复、手机号格式是否正确然后再调用Mapper持久化数据。这样做的好处是将来换成别的前端调用Controller不变Service不变接口还是同一套逻辑某个字段校验要改也只需要改Service这一个位置。系统里所有接口都返回统一的Result响应格式结构是public class Result { private Integer code; // 200成功500失败 private String message; // 提示信息 private Object data; // 业务数据 }前端拿到Response后先判断code是否为200再决定是渲染数据还是弹出错误提示。这么做避免了“接口返回A类型、那个接口又返回B类型”的混乱局面前端封装的请求拦截器也能统一处理401未登录和500系统异常。2.2 登录认证JWT比Session更适合前后端分离CRM系统里的用户登录最经典的做法就是用JWTJSON Web Token。和传统Session方案相比JWT是无状态的服务端不存储会话信息用户登录成功后服务端签发一个加密的Token字符串返回给前端前端存在本地之后每次请求都在Header里带上。服务端只需验证Token的签名是否有效就能确认用户身份。流程是用户提交用户名密码后端校验通过后用userId和角色信息生成Token过期时间设置为24小时。前端把Token存到localStorage在axios请求拦截器中自动添加到Authorization头。后端写一个JWT拦截器对需要认证的接口做校验同时把解析出来的用户信息放入请求上下文。代码层面Token的生成和解析用io.jsonwebtoken的jjwt库实现核心逻辑如下public String generateToken(User user) { return Jwts.builder() .setSubject(user.getUsername()) .claim(userId, user.getId()) .claim(role, user.getRole()) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }用JWT方案的关键好处是接口天然支持跨域调用。因为Token是放在Header里的不依赖Cookie前端部署在8081端口、后端在8080端口两者跨域也能正常鉴权。如果用Session方案还得配置Cookie的跨域属性稍不注意就会出现“登录成功但请求又401”的诡异问题真的能把人逼疯。关于密码安全再补充一句数据库里永远不要存明文密码。源码里用的BCrypt加密注册时把密码哈希后再落库。BCrypt的特点是你每次hash同一个明文都会得到不同结果但都能校验通过这能有效防止彩虹表攻击。2.3 前端路由与状态管理Vue.js项目使用的是Vue Router负责页面跳转。关键点在于路由守卫用户没登录就访问客户列表页直接被重定向到登录页。实现方式是在router/index.js中注册全局前置守卫router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else if (to.path /login token) { next(/) } else { next() } })这套判断逻辑能做到已登录用户进登录页自动跳首页未登录用户访问任何业务页面都会被打回登录页。Vuex则用来存储全局状态比如用户昵称、头像、角色权限信息、侧边栏菜单折叠状态。刷新页面后Vuex数据会丢失所以页面刷新时N级菜单的当前选中状态往往要同步到localStorage才能保留。我特别想提醒一句路由守卫要写在入口文件main.js或路由配置文件里别写在某个页面组件的mounted函数里。后者只能拦截单个页面的访问用户直接改URL路径访问其他页面就绕过了登录检查这在答辩演示的时候容易翻车。3. 实操过程与核心环节实现3.1 环境准备推荐的版本组合搭建环境这一步很多人会卡在版本兼容上。网上教程一大把但SpringBoot版本太高、JDK版本对不上、Vue版本语法不一样这三座大山足以劝退新手。我的推荐组合如下亲测稳定性很好组件推荐版本说明JDK1.8最稳妥兼容性最好SpringBoot2.7.x不要用3.x3.x要求JDK17且部分依赖不兼容MyBatis-Plus3.5.x配合SpringBoot 2.x最顺手Vue2.6.x配合Element UI 2.x绕开Vue3的语法差异Element UI2.15.x适合管理系统组件成熟MySQL5.7或8.05.7占用低8.0功能强都可以Node.js14.x或16.x运行Vue项目所需需要特别说明的是SpringBoot版本问题。我曾经踩过一个坑用SpringBoot 3.0.5搭项目启动时报错找不到javax.servlet包——因为3.x把javax迁移到了jakarta命名空间很多老版本依赖全部失效。所以做这类毕设项目别追求新版本稳定压倒一切。2.7.x版本在各大依赖兼容性上都是最优解网上能找到的教程也最多。MySQL安装完后记得设置root密码并创建数据库CREATE DATABASE crm_db DEFAULT CHARACTER SET utf8mb4;。utf8mb4编码很关键它支持存储Emoji表情和生僻字能避免中文乱码问题。3.2 后端代码快速搭建指南拿到源码后不要急着run先看分布式结构再动数据库再启动。建议按这个顺序操作第一步用Navicat或命令行执行项目根目录下的crm_db.sql脚本把数据表结构和测试数据导入MySQL。脚本里默认带了一个管理员账号admin和一个普通业务员账号方便你登录测试。第二步修改application.yml里的数据库账号密码。注意URL中已经带上了serverTimezoneAsia/Shanghai没有这个参数MySQL 8.x连接会报时区异常。第三步启动SpringBoot主类看到控制台打印出“Started Application in XX seconds”且没有红色报错说明后端启动成功。然后在浏览器访问http://localhost:8080/api/customer/list需要先在Header里放Token才能访问。用Postman配合测试一下登录接口成功后会返回Token字符串。后端代码的结构化阅读顺序是先看pom.xml了解依赖再看entity包的实体类然后是mapper包通常是接口加XML或注解SQL接着是service接口和impl实现最后看controller。这个顺序是从数据层到展示层的递进符合数据流动的方向。MyBatis-Plus的无SQL设计是效率神器。写一个分页查询只需要让Mapper接口继承BaseMapper然后在Service里调用lambdaQueryWrapper构建查询条件public IPageCustomer queryCustomerPage(PageCustomer page, CustomerQuery query) { LambdaQueryWrapperCustomer wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(query.getName()), Customer::getName, query.getName()) .eq(query.getLevel() ! null, Customer::getLevel, query.getLevel()) .orderByDesc(Customer::getCreateTime); return customerMapper.selectPage(page, wrapper); }这里用Lambda写法的好处是不会硬编码数据库字段名。哪怕数据库某天把字段industry改成trade代码里用的是实体类属性引用编译期就能发现错误不会出现运行到查询时才报“Unknown column”的情况。3.3 前端页面和接口联调要点前端项目拿到手后先在命令行执行npm install安装依赖。这一步如果卡住不动或者报权限错误多半是网络问题可以换成淘宝镜像源npm config set registry https://registry.npmmirror.com。依赖装完之后执行npm run serveVue项目默认跑在8081端口同时要确保后端8080端口已经处于启动状态。前端开发环境访问后端存在跨域问题源码里已经在Vue项目的vue.config.js配置了代理转发module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这个配置的意思是浏览器请求前端的/api/customer/list由Vue开发服务器转发到后端的http://localhost:8080/api/customer/list浏览器只看到同源的8081端口请求跨域限制自然解除。前端代码里axios请求也做了统一封装。src/utils/request.js里定义了拦截器请求发出前从localStorage取Token放到Header响应回来后如果code为401则清空本地信息并跳转登录页。你的所有页面请求都应该走这个封装实例而不是直接创建新的axios实例否则Token不会自动带上登录状态验证必然失败。联调阶段我的经验是“窗口排列法”左边浏览器跑前端页面右边Postman测后端接口遇到问题先判断是哪一层的。前端报错优先看浏览器F12的Network面板后端异常看IDEA控制台的Error日志。定位问题的效率高于在页面上瞎猜。3.4 打包部署体验一把真实上线流程很多没接触过企业开发流程的同学对项目的认识停留在“IDEA里能跑就行”。但毕设答辩时如果导师要求现场演示并不是开开发环境而是能双击运行的东西那就涉及到部署这步了。这套源码提供了两种部署方式。方式一推荐用于演示把前端打包后放进后端。前端目录执行npm run build生成dist目录。把dist目录下的所有文件复制到后端src/main/resources/static目录下。重启SpringBoot访问http://localhost:8080就能看到完整页面。这个方式的原理是SpringBoot默认把static目录当静态资源根目录前端打包后的index.html、js和css都会被托管。原本的前后端分离项目变成“单进程部署”你只需要启一个Java进程对环境的要求更简单。唯一要注意的是前端路由需要改用hash模式URL里会有#符号否则index.html刷新页面时会出现404。源码里已配置好hash模式不需要你额外操作。方式二前后端分别部署。前端dist目录放到Nginx的html目录里配置Nginx把/api开头的请求反向代理到8080端口。这种方式更接近真实的企业环境但演示时需要同时启动Nginx和Java进程复杂度稍高。4. 常见问题与排查技巧实录说实话我把这套源码给过不少同学和朋友用每个人遇到的问题几乎都集中在下面这几个地方。我把高频问题整理成一个速查表你照着排除比盲改效率高得多。现象排查方向解决方案SpringBoot启动报“APPLICATION FAILED TO START”端口占用或配置错误检查8080端口是否被占用改server.port或杀进程首页能进但接口全部401Token未携带或已过期重新登录获取新Token检查前端请求是否走统一封装的axios数据库中文乱码数据库或连接URL编码问题建库时使用utf8mb4URL加characterEncodingutf-8前端npm install报错网络问题或Node版本不兼容换淘宝镜像源用Node 16.x重装打包后刷新页面404前端路由模式问题用hash模式或Nginx配置try_files回调index.htmlCORS跨域报错前端代理或后端配置问题开发环境用vue.config.js代理生产环境用Nginx代理MyBatis打印SQL不执行事务未提交或Mapper扫描遗漏检查启动类是否加MapperScan注解MySQL 8.0连不上提示Public Key Retrieval连接参数缺失URL连接串加allowPublicKeyRetrievaltrue再展开说两个最典型的坑。第一个是SpringBoot版本太高。如果你新建项目时选了3.x版本很快会发现导入jjwt、MyBatis-Plus等依赖时控制台一片红。解决办法是回到2.7.x版本线。不用纠结新版本SpringBoot 2.7仍然广泛用于实际企业项目和大量教学体系它一点都不过时。第二个是前端路由404问题。这个现象很经典开发环境在npm run serve下一切正常一打包部署到后端static目录刷新页面就404。原因是默认的history路由模式在服务端找不到对应的物理路径。解决方式已在源码中处理——改成hash模式URL形如/#/customer/list刷新时前端自己根据hash再做路由分发服务端永远不会因为路径问题返回404。还有个隐藏很深的问题值得单独记录docker安装MySQL导致启动失败。我在帮助一个同学配环境时遇到他反馈容器起不来报错内容是权限相关数据目录无法访问。后来排查发现是挂载的数据目录权限不对。如果你打算用Docker跑MySQL记得在启动命令中把宿主机数据目录的权限授权给容器内MySQL用户。对于一般毕设来说不熟悉容器环境的话就直接用本机安装版MySQL省去一层隔离带来的环境变量问题。5. 把毕设/课设做出彩源码学习的进阶建议5.1 源码学习的正确打开姿势拿到源码后最忌讳的操作是上来就敲npm install和java -jar跑起来就觉得自己会了。那样的话答辩时导师随便问一句“这个功能的数据流是怎么走的”你就卡住了。我推荐的源码学习路径是“三层递进”。第一层跑通业务。把系统所有页面点一遍客户增删改查、商机流转、数据统计、角色切换完整走一遍业务闭环。这一层让你理解系统是“干什么的”。第二层重点突破一个模块。选客户管理模块把从数据库表字段设计到Mapper查询到Service处理逻辑再到Controller接口最后到前端页面的数据展示整条链路读懂。这一层让你理解系统是“怎么做的”。第三层动手改造一个功能。比如增加一个“客户来源按周统计”的折线图或者给跟进记录增加附件上传功能。哪怕是一个很小的改动只要你亲手完成了后端表设计、接口开发和前端页面编写这个项目才真正变成你的一部分。5.2 毕设答辩中容易暴露的三个问题第一只演示不讲解。讲台上打开摄像头一脸沉默地点击页面看两分钟。这样再好看的系统也让人觉得只是“会运行”而已。正确做法是边演示边画业务流程图讲清楚一个事件的完整流转过程。第二对核心技术点没有准备。比如导师问“你的系统为什么用JWT而不是Session”“MyBatis-Plus相比MyBatis优势在哪里”这种基础问题答不上来会很尴尬。建议答辩前针对项目里每个技术选型都准备一段“为什么”的回答话术。第三对常见异常没有准备。导师随机问你“如果某客户id不存在时系统怎么处理”你如果答不上来给人的印象会大打折扣。我建议在源码中找到每一个接口的异常处理逻辑理解兜底方案是怎么设计的——这套源码中Controller使用全局异常处理器捕获了业务异常并返回友好提示辩解的时候这就是一个很好的切入点。5.3 容易被忽略的代码规范细节源码整体风格无论后端还是前端都遵循了基本的代码规范但你在学习和拓展时还是要刻意保持住。后端方面类名用大驼峰命名方法名和变量名用小驼峰命名常量全大写加下划线拆分。方法要尽量控制体积超过50行就要考虑拆分。Controller只做参数接收和结果返回不要在Controller里写SQL操作更不要出现一个方法里查三次数据库的“面条代码”。前端方面组件目录按功能模块划分不要把页面全放在一个目录里。Vue组件中模板、脚本、样式三块区域的顺序固定业务方法不要超过20行能拆分到computed或methods中就用这些API。另外每个接口请求都要写注释说明用途和返回数据结构保持良好的可读性。这些规范可能在课设里没那么重要但养成习惯后到了实习和面试环节写出来的代码跟培训机构的模板拉开的差距就是在这些细节里积累出来的。6. 关于这个项目的后续扩展方向源码本身是一个完整可用的CRM底座如果你想在它的基础上玩出新花样我有几个实际操作中验证过的方向供参考。第一个方向是数据分析增强。目前统计模块已经接入了ECharts你可以加上客户价值RFM分析围绕最近购买时间、购买频率、购买金额三个维度给客户打标签把结果用雷达图展示出来。这块代码不复杂但视觉效果和数据深度会明显升级在毕设答辩里属于亮点。第二个方向是消息通知。商机进入“方案报价”阶段后给创建人发送站内信和邮件通知。可以用SpringBoot整合WebSocket实现页面实时消息推送前端接一个通知铃铛。这个扩展能体现你对实时通信的理解。第三个方向是导入导出能力升级。目前源码支持基础的Excel导入导出你可以用EasyExcel重写增加模板下载、错误数据标注、大数据量分批处理功能。EasyExcel在导出上万行数据的性能明显优于POI原生方式做性能对比试验也非常适合写进论文。我个人在实际操作中的体会是一个项目的深度往往不在于削弱了多少新技术而在于你对业务链条的完整理解加上一两个亮点功能。先把这套源码吃透再定一个方向深挖精致毕设的结果一定差不了。希望这份拆解对你手头的项目有所帮助也祝你在这个过程里真正把前后端分离开发的血肉摸通。
返回列表