☰
SpringBoot+Vue.js客户关系管理系统:Java Web毕设选题与前后端分离实战全解析
2026/10/9 18:02:14 网站建设 项目流程

做Java Web毕设最尴尬的是什么?不是不会写代码,而是题目选得毫无记忆点。图书管理、宿舍管理、超市收银,这类系统做完,答辩老师连追问的兴趣都没有。如果你还没定题,或者已经定了但想换个有含金量的方向,可以认真看看这套SpringBoot+Vue.js客户关系管理系统(CRM)。它是典型的Java Web企业级应用,包含完整项目源码、SQL脚本、接口文档,属于可以直接照着学、照着改、照着自己讲的那类毕设资源。

这套系统解决的不仅仅是一个“网页增删改查”的问题,而是把客户管理、联系人维护、商机跟进、销售漏斗这些真实业务场景搬进了代码里。对毕设来说,它最大的价值在于:既有SpringBoot后端工程的分层规范,又有Vue.js前端工程组件化思维,还配好了数据库初始化脚本和接口说明文档,省去了到处拼凑代码的时间。适合正在做Java Web毕设的同学、想系统学习前后端分离开发的新手,以及准备在简历里写一个完整项目的求职者。这篇文章我会从选题逻辑、功能设计、SQL脚本、后端接口、前端联调、部署排错到答辩加分,把整条链路完整过一遍。

1. 毕设选题:为什么SpringBoot+Vue.js能打

1.1 客户关系管理系统在毕设中的天然优势

选毕设题目不是越难越好,而是“业务上能讲清楚、技术上能展示出来”。客户关系管理系统恰好满足这两个条件。它不像电商系统那样需要对接支付、库存、物流等复杂环节,也不需要像物联网平台那样依赖硬件设备。CRM的业务边界非常清晰:谁在管客户、客户处在什么阶段、最近一次跟进是什么时候、这个商机大概值多少钱。这些概念任何老师都能快速理解,你演示的时候不需要解释太多背景。

另一个优势是CRM天然有数据可看。客户列表、跟进记录、商机金额,这些数据本身就适合用图表来展示。答辩时你只需要说“我用ECharts做了一个销售额趋势分析”,就已经把普通管理系统甩开一截了。更重要的是,CRM这套业务模型在企业里是真实存在的,你可以在论文里写“本系统参考了Salesforce等主流CRM产品的业务模型”,这个话术听起来就比“本系统实现了用户登录和留言板”专业得多。

1.2 SpringBoot+Vue.js组合的合理性在哪里

SpringBoot现在的地位不用多说,Java后端开发的主流框架,里面所有东西都是“约定优于配置”,启动一个Web项目只需要几个注解,非常适合毕设阶段展示工程化能力。Vue.js同样是前端领域的热门框架,组件化开发方式让页面模块可以复用,一个表格组件、一个弹出框组件写好以后到处用,代码量比纯jQuery时代少一半以上。

这套组合最值钱的地方是“前后端分离”。前端项目通过HTTP请求调用后端接口,双方只通过JSON数据交换,前端不管数据库长什么样,后端不管页面怎么渲染。这种架构模式在企业里已经成为标配,你把这个项目放进简历,面试官看到SpringBoot和Vue.js同时出现,就已经默认你具备全栈开发的基本认知了。相比之下,用JSP写页面、用SSH框架套模板的老项目,放到现在的招聘市场里几乎没有竞争力。

1.3 答辩时怎么把这个题目讲出价值

很多同学做完了项目,答辩时只会说“我实现了登录、注册、增删改查”,这等于把一桌好菜描述成了白米饭。同样是一个CRM系统,你可以换一个讲法。比如“本系统的核心是客户跟进流程管理,通过商机阶段的状态流转,帮助销售团队掌握每一个潜在的成交机会”。再比如“系统采用基于JWT的无状态认证机制,后端不再依赖Session,这种设计天然适配前后端分离架构”。

说白了,你能讲出多少东西,取决于你设计时埋了多少东西。这套项目里的权限管理、数据隔离、操作日志,每一个都可以单独拉出来作为答辩亮点。论文里也可以对应写“多角色权限控制”“敏感操作审计”等章节,而不是干巴巴的“系统使用了SSM框架”。

2. 平台功能模块与数据库设计拆解

2.1 核心业务模块梳理

一套功能完整的CRM系统,模块划分是很有讲究的。如果只是做一个客户表然后CRUD,那你做的只是个“通讯录”,不配叫管理系统。一个能上得了台面的CRM,至少要包含下面这几块:

  • 登录认证模块:用户名密码登录,签发JWT令牌,带角色信息
  • 客户管理模块:客户信息的建档、编辑、删除、分页查询、按行业/来源筛选,还有客户认领与公海机制
  • 联系人管理模块:一个客户下挂多个联系人,记录联系人姓名、职务、电话、微信、备注
  • 商机管理模块:商机名称、关联客户、预计金额、销售阶段(初步接触、需求确认、方案报价、谈判、成交)、预计成交日期
  • 跟进记录模块:每次跟进的时间、方式、内容摘要,作为客户后续经营的分析依据
  • 系统管理模块:用户管理、角色管理、菜单权限配置、数据字典
  • 统计看板模块:客户数量趋势、商机金额汇总、跟进频次分析等可视化图表

每个模块之间不是孤立的,而是有业务逻辑串联。比如一个销售员新建了客户,然后往客户下面加联系人,再给这个客户创建一条商机,接着每天写跟进记录,最后商机走到了成交阶段,整条销售线索闭环就出来了。答辩的时候你把这个闭环讲一遍,老师就明白你不是在瞎堆功能。

2.2 表结构设计与关系分析

数据库是整套项目的底盘,设计得好不好直接影响开发效率和后期维护。我在设计这套系统时,表结构遵循“先主数据、后业务数据、再关联数据”的思路。大致是这样一些核心表:

表名用途关键字段
sys_user用户表id, username, password, real_name, role_id, status
sys_role角色表id, role_name, role_key, data_scope
sys_menu菜单/权限表id, parent_id, menu_name, permission
sys_role_menu角色权限关联表role_id, menu_id
customer客户表id, customer_name, industry, source, level, phone, address, owner_id, status, create_time
contact联系人表id, customer_id, contact_name, position, phone, wx, remark
business_opportunity商机表id, customer_id, opportunity_name, amount, stage, expected_time
follow_record跟进记录表id, customer_id, follow_time, type, content, next_follow_time
operation_log操作日志表id, user_id, operation, method, params, ip, create_time

表之间的关系可以简单归纳为:sys_user通过外键owner_id关联customer,表示“这个客户归谁负责”;customer一对多关联contact,表示“一个客户有多个联系人”;business_opportunity和follow_record都挂在customer下面,形成围绕客户主数据的业务记录集合。这种设计很符合常规CRM的建模逻辑,论文里画ER图也顺手。

2.3 项目目录结构与分层思想

拿到源码之后,首先要把目录结构看一遍。后端是标准SpringBoot分层结构:controller层负责接收请求和参数校验,service层处理业务逻辑,mapper层操作数据库,entity是数据库实体映射,dto和vo分别是接收前端参数和返回前端数据的对象。再加上config目录放配置类,util目录放JWT工具类、日期工具类,common目录放统一响应结果和异常处理。

前端部分围绕Vue.js的工程化习惯组织:api目录统一存放接口请求方法,views目录按业务模块划分页面,components目录放公共组件,router配置路由表,store用Vuex或Pinia管理用户状态和令牌信息。这种前后端分层的做法,不仅养眼,更重要的是它让修改成本变得很低。想改一个字段,后端改实体类,前端改表单,互不干扰。对这个结构越熟悉,答辩的时候你被问到“项目之间是怎么调用的”就越有底。

3. SQL脚本:初始化数据库的全部细节

3.1 脚本里到底包含了什么

很多同学拿到项目先急着启动后端,结果数据库没建,启动直接报错。所以第一步永远是先把SQL脚本看懂。一个完整的SQL脚本通常做三件事:创建数据库,创建数据表,写入初始化数据。这套项目的SQL脚本里,除了建库建表语句,还会把初始管理员账号(用户名admin、密码通常是加密后的字符串)、角色数据、菜单权限数据都写进去,有的版本还会附带几条演示用的客户记录,方便你启动以后页面不是空的。

建议你先用文本编辑器打开SQL脚本从头到尾翻一遍,重点看CREATE TABLE语句的字段类型和注释,以及INSERT INTO语句。字段类型里要注意金额字段用的是decimal而不是double,因为金额精度是不能打折的;时间字段用的是datetime;备注类字段用text或varchar(500)。这些细节虽然不起眼,但论文里写“数据库设计”章节时,你至少能说清楚为什么字段要这么定。

3.2 四步把数据库跑起来

第一步,确认你本机安装了MySQL。命令行执行mysql --version看看有没有输出,没有的话先去装一个MySQL 5.7或8.0版本。第二步,连接MySQL,在Navicat或命令行工具里执行SQL脚本。用命令行的话就是source命令加脚本路径,注意Windows下路径别带中文。第三步,执行完以后用show tables;看一下表是否都建出来了,select * from sys_user;看一下管理员账号是否初始化成功。第四步,修改后端项目里的application.yml或application.properties配置文件,把数据库地址、账号、密码改成你自己的。改完之后可以顺便启动一下后端,看到SpringBoot启动日志里出现Tomcat started on port说明数据库连接已经是通的了。

3.3 执行SQL脚本时最容易踩的坑

MySQL这地方坑很多,第一个是字符集问题。执行脚本前建议先SET NAMES utf8mb4;,否则中文字段可能变成问号。第二个是MySQL 8.0和5.7的差异。8.0默认密码插件是caching_sha2_password,有些老版本的驱动连不上会报“Unable to load authentication plugin”,解决办法把密码改成mysql_native_password规则,或者升级MySQL驱动版本。第三个是时区问题,连接串上最好带上serverTimezone=Asia/Shanghai,不然报错让你一脸懵。第四是别忘看SQL文件编码,文件本身是UTF-8编码,用记事本打开另存为时别手滑存成ANSI。

还有一个小细节,SQL脚本里如果带了DROP TABLE IF EXISTS语句,执行完就别抱怨“我的数据怎么没了”,这种语句就是为了让你可以重复执行脚本而设计的,开发阶段无所谓,但演示前千万不要对着已经有数据的库再刷一遍。

4. SpringBoot后端与接口文档的配套使用

4.1 后端工程的核心配置

后端工程拿到手,先看pom.xml。核心依赖就那么几组:spring-boot-starter-web提供Web能力,mybatis-plus简化数据库操作,mysql-connector-java负责和MySQL通信,jjwt用来生成和解析JWT令牌,hutool或commons-lang3提供工具类,knife4j或springfox用来集成Swagger接口文档。这套依赖组合是当前Java Web项目的常见搭配,你在简历上写“熟悉MyBatis-Plus、JWT、Swagger”就是从这里来的。

application.yml里的配置我会重点关注三个位置:数据源的url、username、password;MyBatis-Plus的mapper扫描路径;JWT的密钥和过期时间。记住,JWT密钥不要用默认值,答辩前随便改长一点,这个属于安全细节,老师一问就知道你有没有真正理解这个机制。另外如果集成了Swagger,还要注意swagger的开启状态,生产环境记得关闭,虽然毕设没什么人真关,但你能提出来就是加分项。

4.2 接口文档的正确阅读姿势

接口文档的价值在联调阶段体现得最充分。拿到文档以后,不要急着打开代码,先把核心接口的请求路径、请求方式、参数结构过一遍。这套项目的接口风格基本是RESTful风格,我来列几个有代表性的接口:

接口含义请求方式路径简要说明
登录POST/api/auth/login提交username、password,返回JWT令牌
获取当前用户GET/api/auth/info携带令牌,返回用户信息和角色权限
客户分页列表GET/api/customer/page带keyword、pageNum、pageSize等查询参数
新增客户POST/api/customer提交客户表单数据
修改客户PUT/api/customer/{id}按ID更新客户信息
删除客户DELETE/api/customer/{id}按ID删除客户

看接口文档时有一个习惯要养成:先看响应结构里那个data字段是什么形态。登录接口返回的是token,客户列表的data里包含records数组和total总数,这就是前端分页组件要用的数据。前端代码里为什么能直接res.data.records?因为后端统一接口就是这么设计的。所以接口文档不只是给前端人看的,它其实是整个团队对数据契约的共同理解。

4.3 核心接口实现思路

登录接口是整套系统最值得研究的入口。它的流程大概是这样的:controller接收一个loginDTO,包含用户名和密码;service层先根据用户名查用户,再用BCrypt校验密码,密码通过之后生成JWT令牌并返回给前端。生成令牌时会把用户ID和角色信息塞进claims里,这样后续接口就能随时从令牌中解出当前用户是谁。

客户分页查询接口也很有意思,它体现的是MyBatis-Plus的便捷性。直接用LambdaQueryWrapper构造条件,like方法做模糊搜索,eq方法做精确匹配,最后用Page分页对象包裹返回。前端传过来的keyword参数在controller层校验一下,非空再拼进条件里,避免出现“没传关键词返回全表”这种低级问题。新增和修改接口要注意的是创建时间和更新时间的处理方式,聪明点的做法是直接让数据库的字段默认值来处理,或者用MyBatis-Plus的自动填充功能。

4.4 关于安全机制要会说清楚

做管理系统,密码绝对不能明文存储,数据库里看到的密码是BCrypt加密后的一串字符串。登录验证密码时调用BCrypt的matches方法做比对,这个逻辑一定要记住。JWT令牌要设置过期时间,一般2到12个小时,前端拿到的令牌过期后再发请求就返回401,前端就会跳回登录页。还有一个细节是参数校验,比如手机号字段、邮箱字段,可以借助javax.validation的注解快速实现,controller里的@Validated注解一定写上,一个小注解就能挡掉很多脏数据。

5. Vue.js前端工程:页面如何承接后端能力

5.1 前端启动与环境准备

前端项目启动之前先确认Node.js版本。Vue CLI项目在Node 14到16版本下表现最稳,版本高了会有兼容问题,低版本也不行。启动命令就两条:npm install装依赖,npm run serve跑开发模式。npm install如果卡在某个包上,很常见的原因是网络问题,这时候配一个镜像源,设置成registry.npm.taobao.org或者npmmirror的源,基本上都能顺利过。

启动以后浏览器访问localhost:8080,如果页面出来了,先别急着开心,打开浏览器的开发者工具看看有没有红色的报错。登录页大概率会提示接口请求失败,因为前端默认走的是代理路径/api,你得确认前端vue.config.js或vite.config.js里的proxy配置代理到后端的8080端口。代理这块是整个前后端联调的核心,弄懂了它,你也就弄懂了“为什么前端页面能直接调后端接口而不报跨域”。

5.2 axios请求封装与身份令牌管理

前端所有HTTP请求都要走axios,但绝对不要在每个页面里裸写axios.get,一定要封装一层公共请求模块。封装的逻辑很直观:创建axios实例,设置baseURL、请求超时时间;请求拦截器里把本地存储的token取出来放进Authorization请求头;响应拦截器里统一处理返回码,后端返回401就清空登录状态并跳转到登录页,返回其他错误码就统一弹出错误提示。

代码大概长这个样子,思路比代码本身重要:

import axios from 'axios' import { getToken, removeToken } from '@/utils/auth' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) service.interceptors.request.use(config => { const token = getToken() if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response && error.response.status === 401) { removeToken() window.location.href = '/login' } return Promise.reject(error) } )

把这段思路吃透,你就能解释“前端是怎么做到登录一次、到处请求的”。项目里所有接口的调用都通过这个封装层,代码整洁度立刻上个档次。

5.3 路由守卫与菜单权限落地

前端路由不能只是简单注册一堆页面路径,还要做权限控制。Vue Router的beforeEach守卫会在每次路由跳转前执行,逻辑是:判断要去的是不是登录页,如果没登录就强制跳到登录页;如果登录了,再判断当前路由是否在用户的菜单权限里,不在就提示无权限。这里的菜单权限通常不是写死的,而是登录之后从后端的/me或/info接口拉取当前用户的菜单列表,动态注册路由,这样才能实现不同角色看到不同的菜单。比如管理员能看到系统管理菜单,普通销售员只看到客户和商机模块。

5.4 页面联调时的实用技巧

联调阶段最有效的工具就是浏览器开发者工具里的Network面板。点击一个按钮,里面能看到请求是否发出、请求头是否正确、响应状态码是什么、返回的数据长什么样。如果接口返回500,立刻切到后端控制台,看异常日志。如果返回的是200但页面没数据,多半是前端解析数据的字段路径不对,比如后端返回的是res.data.records,你代码里写成了res.data.list。这种问题一天能遇到十次,熟练了以后基本扫一眼就能定位。

还有一个经验:前端修改了接口地址或者新增了页面,刷新浏览器发现还是老样子,先按Ctrl+Shift+R强制刷新清缓存。有时候改了代码但页面没变,不是代码问题,是开发服务没编译完,盯着终端看它把编译日志打完再刷新。

6. 从0到1把整套项目跑通

6.1 推荐的操作顺序

跑项目不是碰运气,顺序对了百分之八十的问题都不会出现。我的推荐顺序是:第一步,把SQL脚本导入MySQL并确认数据表和数据都就绪;第二步,修改后端配置文件,启动SpringBoot,确认后端控制台没有报错,这时候你可以单独用浏览器访问一下swagger地址,看看接口文档能否打开;第三步,启动前端开发服务,登录页面能看到,输入admin账号密码,点击登录,能跳转到首页,说明全链路已经通了。

这个顺序的本质是从底层往上层验证。数据库不通,后面全是白搭;后端接口不通,前端页面一定拿不到数据。有些人习惯把前端先跑起来界面看看,结果一堆网络请求报红,最后发现是后端都没启动,白白浪费半小时。

6.2 两种部署方式,选哪种看场景

毕设答辩通常有两种部署方式要求。第一种是前后端分离部署:前端npm run build打包成静态文件,扔给Nginx托管,后端打jar包用java -jar命令启动。这种最贴近企业真实部署方式,缺点是配置麻烦一点。第二种是前后端合并部署:把前端打包后的dist目录复制进SpringBoot的static目录,然后只启动后端这一个进程,浏览器访问8080端口就能打开页面。第二种方式非常省心,演示环境不会因为忘记启动Nginx而翻车。

个人建议答辩演示用第二种,简历里写的时候说“项目也支持Nginx反向代理的前后端分离部署”,两全其美。打包后端时注意用mvn clean package -DskipTests跳过测试,然后去target目录拿jar包,不要直接在IDE里跑。

6.3 演示环境怎么准备才不翻车

演示翻车最常见的原因不是代码问题,而是环境问题。答辩前两天就应该把演示用的数据库清理干净,删掉测试期间产生的垃圾数据,准备好一套像样的演示数据。客户名称用“某某科技有限公司”而不是“测试1”,商机金额用有零有整的数字,跟进记录写上日期,这些细节会让演示画面看起来更像一个真实使用的系统。

账号密码抄在一张纸上带去现场,不要现场登录时个人信息遗忘。演示时只保留一个浏览器窗口,后台日志、数据库客户端全部关掉,只开后端的jar包和浏览器页面。如果答辩现场网络环境不好,记得把前端的代理地址改成127.0.0.1,全程走本地请求。

7. 常见问题与排查速查表

做这种项目,几乎每个人都会在几个固定的地方卡壳,我按出现频率排列了一份排查速查表:

问题现象可能原因解决办法
后端启动报数据库连接失败数据库没启动,或yml配置信息写错检查MySQL服务是否运行,核对url、用户名、密码
SQL脚本执行报语法错误脚本文件编码或MySQL版本不符确认文件是UTF-8编码,按当前MySQL版本调整写法
前端请求接口报跨域请求没走代理,或代理配置错误检查vue.config.js的proxy配置,确认后端地址正确
登录返回401用户名密码错误,或token过期检查初始密码,重新生成有效token
后端启动端口被占用8080端口被其他进程占用改端口号,或查到占用进程后结束它
前端npm install缓慢/失败网络原因或Node版本不匹配换镜像源,检查Node版本与Vue CLI的兼容性
接口返回500但后端没日志参数类型转换异常,或空指针看控制台完整堆栈,重点看Controller入参格式
页面白屏JS报错导致编译失败打开控制台查看具体报错,通常是组件引入路径出错

排查问题有一个万金油思路:先看后端日志,再看浏览器Network,最后看数据库数据。只要按这个方向走,绝大多数问题都能定位到具体一行代码或者一个配置。很多时候问题本身不可怕,可怕的是东一下西一下乱试,最后项目被越改越乱。

8. 让毕设从及格变优秀的4个扩展方向

8.1 用数据可视化放大亮点

一套只做表格的系统再完整也很难让人眼前一亮,加上数据看板就不一样了。用ECharts在首页展示客户数量按行业分布的饼图、近六个月新增客户的柱状图、商机金额排行榜。实现起来并不难,后端写几个统计类接口,前端引入ECharts或者用封装好的Vue图表组件。答辩时你说“销售管理层可以通过看板实时掌握业务进度”,这句话涵盖的视野已经超出课程设计了。

8.2 加一个定时任务,体现真实业务

CRM里有一个很有意思的真实需求叫“公海客户回收”。约定规则是,客户负责人超过30天没有写任何跟进记录,这个客户自动回到公海,其他销售可以认领。SpringBoot里用@Scheduled注解加一个定时任务,每天凌晨跑一次,查出超时客户修改归属状态。这个小功能会让系统一下子“活”起来,因为它不是单纯的CRUD,而是有业务规则的自动流转,论文里也多了“定时任务模块”这一章可以写。

8.3 增加消息通知模块

客户跟进到关键节点、商机到了该报价的阶段,系统能不能主动提醒用户?引入一个简单的站内信通知机制,或者对接短信服务商的HTTP接口,Java后端用HttpClient或RestTemplate发起请求,在客户生日、商机逾期节点给销售发消息。这个扩展点虽然不大,但它展示了“SpringBoot调用外部接口”的能力,在简历里写“使用RestTemplate对接第三方短信服务”,是会让人眼前一亮的。

8.4 操作日志与数据导出

操作日志已经列在表结构里了,关键是把它发挥出来。每次增删改操作记录操作人、操作类型、请求参数、IP地址,后台可以按时间范围查询。再加上一个“客户数据导出Excel”功能,用EasyExcel或POI写一个导出接口,前端一个按钮下载表格。这两个功能都属于“工程完整性”层面的补强,真正跑到企业里都是刚需,放在毕设里则是让系统闻起来有生产环境的味道。

最后再分享一点我个人的体会。拿到一套完整的SpringBoot+Vue.js毕设项目源码,最重要的不是让它跑起来,而是真正把它读透。每一行配置、每一个接口、每一个组件的存在,背后都对应一个设计决策。你把登录认证、权限控制、数据库设计这三个点搞清楚了,答辩的时候就有讲不完的话;你要是只是重新换个皮肤然后说“项目不是我做的”,老师在三个问题之内就能让你下不来台。这个项目的起点很好,业务场景真实、技术栈主流、文档齐全,接下来就是你的工作,把它变成自己的东西。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询