
做管理系统开发这些年被问得最多的问题之一就是有没有一套“客户关系管理”类的源码拿来就能跑改改字段就能交付。这类项目看起来简单但客户资料、跟进记录、合同回款、角色权限哪个模块少了都显得不完整。今天分享的这套基于SpringBoot后端 Vue前端 MySQL的客户关系管理系统源码就是典型的前后端分离架构、可本地直接运行的全栈项目。我把它完整跑过几轮也拿它给不少转行的新人拆过下面从项目定位、表设计、代码结构、运行步骤到踩坑记录全部讲清楚给你一份能直接参考的实操手册。1. 这套客户关系管理系统的定位与技术选型1.1 客户管理系统到底在解决什么问题先把业务边界说清楚。市面上叫“CRM”的工具很多但绝大多数中小企业要的无非是这几块把客户基本信息存进系统销售能记录每一次电话、拜访、微信沟通的结果合同从签订到回款有个地方跟踪月底领导能看一眼谁手上有多少意向客户、一共跟进了几次。拆开看就是典型的“增删改查 状态流转 简单统计”。这套源码的价值在于它把这几个模块做成了能直接演示、直接二次开发的完整系统而不是某个单一页面的demo。标题里写“可直接运行”指的是它不需要你从零搭框架导入数据库脚本、配好数据库连接、启动后端、启动前端就能打开浏览器看到登录页。对于正在学SpringBoot和Vue的人来说它是很好的全栈入门样本对于需要接私活或做课程设计的人来说它也是一套能省下大量前期搭建时间的骨架。1.2 为什么这套组合是当前的主流选择SpringBoot负责后端接口Vue负责前端页面MySQL负责数据存储这是目前Java方向Web开发最常见的一套组合。很多人问为什么不直接用JSP、不用原生Servlet答案很简单前后端分离之后后端只输出JSON数据前端负责渲染页面这样后端团队和前端团队可以并行开发后期无论是换页面框架还是增加移动端影响面都小得多。SpringBoot的优势在于“开箱即用”。它内嵌了Tomcat不用再去单独部署一个Web容器配置剥离到application.yml里改数据库地址、改端口都很直观配合Spring全家桶事务、缓存、数据校验这些基础能力全都现成。Vue则把DOM操作从人肉逻辑里解放出来用数据和组件驱动页面表格、表单、弹窗这些CRM里高频出现的东西用Element UI这类组件库能很快堆出来。MySQL就更不用多说了部署简单、文档多、招人好招中小企业项目几乎绕不开它。这套源码还有个特点前端是JavaScript技术栈也就是Vue的基础语言。所以你在下载包里看到很多以.js结尾的文件不要以为是后端逻辑那只是前端工具链的一部分。理解这一点你才不会在二次开发时找错文件。2. 数据建模与后端架构设计2.1 数据库表设计客户、跟进、合同、回款怎么串联一套合格的CRM系统数据库至少要覆盖几个核心实体用户、角色、客户、客户跟进记录、合同、回款记录。下面用最常见的表设计来说明整体思路。表名主要字段作用sys_userid, username, password, real_name, role_id登录用户表密码一般MD5处理sys_roleid, role_name, role_code角色表区分管理员、销售经理、销售员customerid, customer_name, phone, level, status, owner_id, remark客户主表owner_id标识归属哪个销售customer_followid, customer_id, content, follow_type, next_time, create_by跟进记录表关联客户contractid, customer_id, contract_no, amount, sign_time, status合同表记录金额和状态collection_recordid, contract_id, payment_amount, payment_time回款明细按合同关联这套设计里最关键的是customer表的owner_id字段。它决定了每个销售登录后能看到哪些客户是一个简单的数据权限设计。管理员能看到全部客户普通销售只能看到“归属自己”的客户。很多二次开发需求会在此基础上加一个team_id让整个销售团队的成员互看客户你可以根据业务模式自行改动。客户和跟进记录是一对多关系每次跟进都插入一条记录页面上按时间倒序展示就形成了类似“时间线”的效果。合同表和回款记录是一对多关系合同的回款进度可以通过关联的回款记录汇总计算出来。这种表关系不复杂但对于新手来说理清楚“谁关联谁谁归属谁”是看懂整个系统后端代码的关键。2.2 后端代码结构与鉴权方案后端代码一般会按这种经典分层结构组织拿到源码后先看整体包名com.example.crm ├── common 通用模块结果封装、异常处理、工具类 ├── config 配置类跨域、拦截器、静态资源 ├── controller 接口层只做参数接收和数据返回 ├── service 业务层真正处理业务逻辑 ├── mapper 数据访问层对应MyBatis操作数据库 └── entity 实体类对应表结构看代码的顺序建议是先从controller入手看每个接口的URL和参数再跳到service看业务逻辑最后看mapper去对应SQL。很多新手一打开项目就看entity类盯着字段看半天其实方向反了业务入口在controller数据流转顺序是“页面 - controller - service - mapper”。鉴权方面这类源码常用的方案是JWT。用户登录成功后后端生成一个包含用户信息的token字符串返回给前端前端把它存到localStorage里之后每次请求都在Header里带上Authorization: token值。后端的拦截器会拦截所有非登录接口校验token是否有效再放行。这种方案的好处是无状态后端不维护session扩展多节点部署时不需要额外做session同步。2.3 核心业务的实现逻辑解密客户管理的核心业务无非是这几个场景我分别说下实现思路。客户新增前端提交客户表单后端先校验必填项然后把当前登录用户的id写入owner_id字段这样新客户自动归属给当前销售。很多系统会在新增时让用户手动选择归属人其实可以直接取登录上下文里的userId更稳妥。客户跟进前端把内容、方式、下次联系时间提交到后端后端同时写两条数据——一条插入跟进记录表一条更新客户表的最近跟进时间。为什么这样做因为列表页面通常要展示“最近跟进时间”如果每次都先去查跟进表再关联客户表列表会变得非常慢。直接在客户表冗余一个字段查询效率高很多这就是典型的空间换时间思路。合同和回款的联动合同表保存合同总金额回款表保存每笔到账金额。查看合同详情时前端会调用一个接口后端把合同信息、已回款金额、未回款金额一起返回。计算已回款金额的SQL大概长这样SELECT contract_id, IFNULL(SUM(payment_amount), 0) AS paid_amount FROM collection_record GROUP BY contract_id这个按月汇总的统计接口也是类似思路。之前有人说“这种冗余字段和聚合查询很容易数据不一致”实际项目里大家普遍的做法是回款插入时同时更新合同表上的已回款金额冗余字段再配合定时任务做对账。源码如果没做到这一步你在二次开发时可以按这个方向优化。3. Vue 前端与业务交互实现3.1 前端目录结构和页面组织逻辑前端部分如果拿到的项目是用Vue CLI创建的目录结构一般是这样的src ├── api axios请求封装每个模块一个js文件 ├── assets 静态资源 ├── components 公共组件 ├── router 路由配置 ├── store Vuex状态管理 ├── utils 工具类比如request.js请求封装 └── views 页面文件按模块分文件夹你可以在api目录下看到customer.js、contract.js这种文件里面写的是对应模块的请求函数。这样做的好处是页面里不直接写axios请求所有接口统一收敛到api层后面接口地址变了或加上了公共参数只改一处就行。CRM系统的页面大多数是表格页、表单弹窗页、详情页的组合。以客户管理页面为例主区域是客户表格顶部是搜索条件右侧是新增、编辑、删除按钮。点击“跟进”按钮会弹出对话框里面填写跟进内容、选择跟进方式提交后刷新列表。点击“详情”会跳转到详情页详情页上方是客户基本信息下方用标签页切换“跟进记录”“合同记录”“回款记录”。这种组织方式很标准也可以套用到其他企业管理类系统。3.2 axios 封装、路由守卫与跨域处理前端和联调最密切相关的有两点请求封装和路由控制。request.js这层的核心作用有两个第一是自动从localStorage里取token并放到请求头第二是统一处理后端返回的code码。当后端返回401表示token失效时前端自动跳转回登录页。这个逻辑很关键少了它用户登录过期后会看到一堆接口报错而不是被引导重新登录。路由守卫在router/index.js里配典型逻辑是判断目标路由白名单如果访问的页面需要登录而本地又没有token就强制跳转到登录页。看起来简单但实际开发中很多人会漏掉“白名单”这一步导致登录页本身也被重定向陷入死循环。跨域问题主要出现在本地开发阶段。前端跑到8080端口后端跑到8081端口浏览器会拦截“不同源”的请求。最省事的做法是在vue.config.js里配置开发代理而不是在后端放开所有跨域。配置方式如下devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true, pathRewrite: { ^/api: } } } }这样前端请求/api/customer/list开发服务器会转给后端的/customer/list浏览器看着是同源的跨域问题就绕过去了。注意这只是开发环境的方案生产环境要把前端打包文件直接放进SpringBoot的static目录或者用Nginx做反向代理。3.3 把 Vue 打包放进 SpringBoot 的两种方式做单体交付时我们经常把Vue打包后的静态文件放到SpringBoot项目里让整个系统只有一个后端服务部署起来特别简单。方式有两种。第一种是把dist目录里的文件复制到src/main/resources/static下面再把SpringBoot的默认首页指向index.html。重新打包后浏览器访问后端端口直接就是前端页面。前端请求的接口路径和页面路径同源不需要处理跨域。第二种是使用maven插件在构建时自动把前端dist文件复制进target/classes/static这种方式适合用GitLab CI或者Jenkins自动打包的团队。无论哪种方式核心原理都一样SpringBoot启动时会把static目录下的静态文件作为Web资源直接暴露。很多源码自带的是前后端分离结构并不一定内置了打包后的文件你按上面任一种方式集成即可。4. 本地把项目跑起来的完整流程4.1 环境准备JDK、Maven、MySQL拿到源码第一步不是改代码而是先确认环境。这套系统用SpringBoot和Vue对本地环境有硬性要求我列出最省心的版本组合也是我反复测过的组件推荐版本说明JDK1.8 或 11SpringBoot 2.x系列用这两个版本最稳Maven3.6 以上不需要单独配仓库但建议配置阿里云镜像Node.js14 或 16Vue2项目用高版本Node容易出现依赖兼容问题MySQL5.7 或 8.0如果用8.0要注意驱动和连接参数这里重点提醒一下“SpringBoot版本太高”的问题。如果你拿到源码的SpringBoot版本是2.x却用了JDK 17甚至更新的版本启动时经常会出现IllegalArgumentException或者反射相关的错误。解决办法很简单要么把JDK降到11要么把SpringBoot升到3.x后再适配Jakarta命名空间。不要凭感觉升版本先看pom.xml里用的是哪个版本。MySQL安装如果是新机器记得安装完后做两件事第一设置root账号密码第二确认服务是否开机自启。命令行里执行mysql -u root -p能进入就说明客户端和数据库服务正常。遇到版本5.7以上时还要注意字符集设置建议建库时就用utf8mb4避免中文乱码。4.2 导入数据库与修改配置文件环境装好后找到压缩包里的sql目录里面会有一个类似crm.sql的脚本文件。打开MySQL客户端或者Navicat先新建数据库再执行脚本mysql -u root -p create database crm_db default character set utf8mb4; use crm_db; source /你的路径/crm.sql;执行完成后用show tables;确认表都存在。接下来打开后端项目的application.yml找到数据库配置部分把用户名和密码改成你自己的server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/crm_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 你的密码URL参数不要随便删。useUnicodetruecharacterEncodingutf8保证中文正常写入serverTimezoneAsia/Shanghai解决时区错乱MySQL 8.0下不配置allowPublicKeyRetrievaltrue连接时可能直接报错。这些参数都是实战里踩出来的少一个都可能让你在启动阶段白白折腾半小时。4.3 启动后端、前端与自测清单后端启动有两种方式。第一种是在命令行进入项目目录执行mvn spring-boot:run第二种是用IDEA打开项目等Maven下载完依赖后运行启动类里的main方法。如果你想把端口改成别的值在Edit Configurations里的Program arguments里加上--server.port8082即可。启动成功的标志是日志里出现Started Application in x.xxx seconds。前端启动更简单进入前端目录先安装依赖再启动开发服务器npm install npm run serve如果npm install卡住或者下载太慢把npm源切换到国内镜像这个后面会单独讲。前端启动后浏览器访问http://localhost:8080正常情况下会跳转到登录页。用系统初始化管理员账号登录比如admin / admin123或admin / 123456具体看sql脚本里的初始化数据注释。我建议用下面这个自测清单快速验证系统是否完整登录成功并能进入首页看板新增一个客户列表能查到且归属人是你自己给客户添加一条跟进记录详情页能按时间倒序看到新增一份合同回款记录输入一笔金额后已回款金额自动更新清除本地存储后刷新页面会跳回登录页。这五步如果能走通说明整个链路是通的前后端、数据库、鉴权都没有问题。任何一个环节不通按下一章的思路排查基本都能解决。5. 常见踩坑与排查实录5.1 数据库连接相关的坑这类SpringBoot源码最容易的问题集中在数据库连接上。第一次启动就报Access denied for user rootlocalhost九成是密码写错。如果密码本身没问题但报Public Key Retrieval is not allowed这是MySQL 8的加密规则导致的在连接URL后面加上allowPublicKeyRetrievaltrue即可。还有一种是报Connection refused先去确认MySQL服务有没有启动Windows下可以在服务管理器里看MySQL服务状态。另外要特别提醒的是驱动版本和数据库版本要匹配。SpringBoot 2.x默认带的mysql-connector-java是5.x连接MySQL 8.0时可能会因为认证协议报错最简单的方案是用mysql:mysql-connector-java的最新8.0.x版本覆盖。改完pom后记得刷新Maven不要只改不刷改了等于白改。还有个很多新手容易忽略的问题SQL脚本里可能带了初始化的管理员密码但不同系统里加密规则不同。有些源码存的是明文有些是MD5加密。万一登录不上先去库里查select * from sys_user;看看password字段是明文还是密文再决定是重置密码还是直接用脚本里的初始值。5.2 前端启动异常的排查前端报错比后端更碎。最常见的npm ERR! ERESOLVE unable to resolve dependency tree是因为Node版本太新和Vue2项目的旧依赖产生了冲突。两个解决方式一是把Node降到项目要求的版本二是在安装命令后面加上--legacy-peer-deps。这是临时跳过依赖冲突检查不影响绝大多数使用场景。启动后页面一片空白、控制台报Cannot read property xxx of undefined通常是Mock数据或接口没返回导致页面拿不到数据。先用浏览器开发者工具看Network里接口是否返回200再点开Response看结构八成是字段名对不上比如后端返回userName前端读的是username。这种问题在联调阶段特别常见不要急着改页面先把接口的返回内容和前端期望的结构对齐。如果你用的是IDEA自带的终端执行npm命令在Windows下偶尔会遇到命令行编码乱码。在设置里把IDE的Console编码改成UTF-8或者直接把命令放到PowerShell里跑基本能规避。5.3 二次开发建议与源码学习顺序看完代码能跑起来之后大多数人会想加功能。我的建议是不要直接动手改先把后端代码的调用链读一遍再改一个最简单的东西练手。比如修改客户列表的查询条件从固定条件改成支持多条件搜索这个过程你能把“页面传参 - 后端接收 - 拼接SQL - 返回分页”整条链路串起来。新增一个模块时可以参考已有模块照葫芦画瓢数据库里建表entity里建类mapper里写SQLservice里写实现controller里加接口前端api目录下加请求函数views里加页面router里加路由配置。这套流程熟练之后你会发现所谓“快速开发”就是这么回事——框架已经把重复劳动砍到大半剩下的核心是业务逻辑本身。特别提醒一点这类源码里默认没有做复杂的操作日志审计但在真实交付中客户管理系统的操作留痕往往是被甲方重点要求的。二次开发时可以在公共拦截器里加一个日志切面记录谁在什么时间改了哪个客户这样系统才完整。版权和合规方面拿源码改完交付给客户前也注意替换掉默认的版权信息和logo避免出现使用争议。5.4 给新人的一点实操心得我最后说一些个人感觉比较重要的小事。第一拿到源码后先建一个git仓库提交初始版本再开始改。很多源码包改动之后出了问题想回退都找不到原始版本这个习惯尤其重要。第二环境能不动尽量不动文档里说JDK 8就用JDK 8不要“顺便升级一下”。我见过太多次为了追新把SpringBoot 2.x强行升到3.x结果依赖大面积报错最后花一整天回滚。第三跑通之后再回头读代码比对着代码硬读高效得多。先看你能操作的业务再去找对应的接口和SQL印象会很深。等你把客户、跟进、合同、回款这几个模块的代码都摸过一遍再去看SpringBoot的过滤器、拦截器、事务注解这些底层概念就会觉得不那么抽象了。这套源码最大的意义不是“能跑”而是你能从它的表设计、分层方式、前后端对接方式里看到一套通用管理系统应该长什么样。照着它的路子走一遍换成任何别的业务系统你都知道从哪里入手去改。这是比运行成功本身更值钱的收获。