ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot + Vue 企业客户管理系统:前后端分离项目实战拆解

Spring Boot + Vue 企业客户管理系统:前后端分离项目实战拆解 项目标题摆在这企业客户管理系统Spring Boot VueJava版。这种组合在技术社区里已经算“经典款”了尤其是源码站转来转去的那些项目标题起得都差不多但真正能一次跑起来、能看懂、能改着用的其实没那么多。你要是正在学前后端分离开发或者期末课程设计、毕业设计想找个完整的练手项目又或者公司内部想快速搭一套简单的客户资料管理后台那这篇文章正好对应——我会按这套系统的实际开发逻辑把后端、前端、数据库、联调、部署这些环节从头到尾拆给你看包括哪些地方容易踩坑哪些配置不改必报错尽量让你拿到代码后少走弯路。我不是第一次碰这种源码站转出来的项目。说实话这类系统的代码质量参差不齐有的写得能直接上生产有的连数据库脚本都是缺的。所以拿到这种项目第一件事不是双击启动而是先看结构、看依赖、看配置把整个系统的脉络摸清了再跑起来。这篇就把我当时接手类似项目的完整思路和操作记录整理出来给你做个参考。1. 项目整体设计与技术选型思路1.1 先搞懂“企业客户管理系统”到底要管什么很多同学一上来就急着部署代码结果跑起来发现不知道点什么、不知道哪里看效果。我建议先反向思考客户管理系统在企业里的核心任务是什么简单说管三件事客户信息、跟进过程、成交结果。客户信息就是公司有哪些客户叫什么、在哪个行业、联系人是谁、电话邮箱多少、地址在哪、谁负责跟进。跟进过程是销售人员今天给这个客户打了电话、发了报价单、约了下周拜访这些动态记录得有地方存。成交结果就是哪些客户已经签了合同、回款多少哪些还停留在意向阶段。所以这套系统再花哨模块也无非就是客户档案管理、联系人管理、跟进记录、商机/成交管理再加一个系统层面的用户登录和权限控制。很多源码站转出来的项目功能基本就围绕这几块展开。你拿到项目后先看首页菜单如果菜单里能对应上这些核心功能那说明这个项目是完整的。我在看这套系统的时候第一件事就是打开SQL脚本文件看里面建了几张表。通常不会少于五张用户表sys_user、客户表customer、联系人表contact、跟进记录表follow_up_record可能还有一张字典表sys_dict或者角色表sys_role。看完表结构整个系统的业务边界就清楚了再去代码里找对应的增删改查效率会高很多。1.2 为什么偏偏是Spring Boot Vue这套组合现在前后端分离开发已经是大主流Spring Boot Vue在Java技术栈里几乎成了“默认选项”。为什么其实不是因为它性能有多极致而是因为它适合绝大多数业务系统。后端选Spring Boot图的是生态成熟、资料全、上手路径清晰。Spring Boot把Spring那一堆复杂的XML配置全部都自动装配掉了你只要引入依赖、写个application.yml就能跑起一个Web服务。配合MyBatis或者MyBatis-Plus操作数据库几行代码就能完成单表CRUD开发效率很能打。前端选Vue图的是学习曲线平缓、组件化开发顺手而且Element UI / Element Plus这套组件库直接把后台管理页面需要的表格、表单、弹窗、分页、菜单全做成了现成组件写页面跟搭积木一样。相比ReactVue的模板语法对中文开发者更友好社区资源也多遇到问题一搜就是答案。这里有个对比可以参考选型适合场景上手成本社区资料Spring Boot VueJava技术栈前后端分离项目中等非常丰富Spring Boot Thymeleaf服务端渲染、简单后台低一般Django VuePython技术栈项目中等较丰富Express Vue轻量Node项目较低较丰富这套系统选Java版并不是说别的语言做不了而是Java在企业里存量最大招人、维护、二次开发都方便。你把这个项目吃透了后面去面试、去接外包、去公司做内部系统核心思路都是通的。1.3 拿到转帖源码后的第一步清点家底源码站转出来的项目最大的问题不是代码写得好不好而是东西全不全。我拿到任何一份这种项目都会先做三件事。第一看目录结构。后端有没有完整的pom.xml有没有src/main/java和src/main/resources有没有application.yml前端有没有package.json有没有src目录有没有vue.config.js或vite.config.js。如果这些都在基本盘就有了。第二看数据库脚本。根目录里有没有.sql文件或者doc、sql、db这种文件夹。没有数据库脚本的项目基本可以直接放弃因为你根本不知道表长什么样就算能启动也只是空壳。第三看README。很多转帖源码会压缩包内附一个说明文档里面可能写了数据库账号密码、启动顺序、默认管理员账号。哪怕写得不全也比没有强。我当时拿到这套系统后按照这三步确认下来项目是完整的后端Maven工程、前端Vue工程、数据库初始化脚本都有。这才进入下一步启动。2. 后端核心模块拆解与实现要点2.1 后端工程结构到底在分什么层Spring Boot项目的后端工程结构很多新手看着一堆包名就懵了。其实核心就三层加一堆辅助控制层Controller、业务层Service、数据访问层Mapper/DAO。这套系统因为是标准的企业应用分层非常典型结构大概是这样的src/main/java/com/example/crm ├── controller/ // 接口入口处理HTTP请求 ├── service/ // 业务逻辑事务都放在这一层 ├── mapper/ // 数据库操作接口 ├── entity/ // 数据库表对应的实体类 ├── dto/ // 前端传参或返回值的封装对象 ├── common/ // 统一返回结果、异常处理、工具类 ├── config/ // 拦截器、跨域、过滤器等配置 └── CrmApplication.java // 启动类Controller层的职责非常单纯接收前端请求、调用Service、把结果包成统一格式返回。它不应该出现SQL不该写业务判断否则后面维护就是灾难。Service层是业务的核心客户是否可以删除、跟进记录是否允许修改、用户登录后能看哪些客户这类规则全部写在Service里。事务注解Transactional也标注在这一层的方法上保证数据库操作要么全成功要么全回滚。Mapper层在用了MyBatis-Plus之后已经很轻量了大多数单表操作不需要手写XML直接继承一个BaseMapper接口就能拿到现成的增删改查方法。复杂的多表联查才需要手写SQL注解或者XML文件。这套系统的代码如果结构清晰你拿到手之后应该是这种模式。如果发现Controller里直接写了一大堆Mapper调用那说明代码质量一般可以参考思路但别盲目照抄。2.2 客户管理模块的数据表设计与关键字段客户表是整套系统的地基。我直接说几个核心字段你对照SQL脚本看就行客户名称customer_name、客户类型customer_type、所属行业industry、客户来源source、负责人IDowner_id、客户状态status、联系电话phone、地址address、备注remark、创建时间和更新时间。这里有个值得注意的设计客户状态这个字段在真正的企业应用里不会直接存“已成交”这种中文而是存一个数字或者编码再通过字典表映射。比如0潜在客户1意向客户2成交客户3流失客户。这样做的好处是灵活以后加状态不用改表结构。这套系统如果用了字典表那就是正规做法如果是硬编码写在SQL里代码维护起来就会痛苦一些。联系人表和客户表是一对多关系一个客户可以挂多个联系人。联系人表至少要有姓名、电话、职位、是否主要联系人这几个字段。跟进记录表则和客户表也是一对多关系每次销售打电话、拜访、发资料都可以写一条跟进记录表里至少要有客户ID、跟进内容、下次跟进时间、跟进人、创建时间。建表脚本的命名规范也要留意。如果表名和字段名都是小写下划线风格customer_name这种说明作者有基本规范。如果是乱七八糟混合命名后面写SQL、写实体类都会很痛苦。这里给一个简化版的客户表建表SQL实际项目在此基础上加索引和更多字段CREATE TABLE customer ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, customer_name varchar(128) NOT NULL COMMENT 客户名称, customer_type tinyint(4) DEFAULT 1 COMMENT 客户类型1企业客户2个人客户, industry varchar(64) DEFAULT NULL COMMENT 所属行业, source varchar(32) DEFAULT NULL COMMENT 客户来源, owner_id bigint(20) DEFAULT NULL COMMENT 负责人用户ID, status tinyint(4) DEFAULT 0 COMMENT 客户状态0潜在1意向2成交3流失, phone varchar(32) DEFAULT NULL COMMENT 联系电话, address varchar(255) DEFAULT NULL COMMENT 地址, remark varchar(500) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT客户信息表;2.3 登录认证与接口权限是怎么做的这套系统既然叫管理系统就一定有多用户概念不然谈不上“管理”。登录认证这块早期单体项目常用Session现在前后端分离项目更流行JWTJSON Web Token或者类似的Token机制。流程很简单用户输入账号密码登录后端校验通过后生成一个Token返回给前端前端把Token存起来之后每次请求都带上这个Token后端通过拦截器校验Token是否有效。这套系统如果做了Spring Boot集成JWT那目录里应该有一个JWT的工具类和一个拦截器或者过滤器配置类。密码存储也有讲究。2010年那种拿明文存密码的做法现在肯定不行了正规项目至少会用BCrypt做哈希加密就是那种每次都生成不同密文、自带盐值的算法。你可以在代码里找一下PasswordEncoder或者BCrypt相关依赖如果发现密码字段能直接从数据库里读出来明文那这个项目的安全性基本为零只能当学习demo看。权限控制上最简单的实现是在用户表里放一个角色字段比如1管理员、2普通销售然后写一个自定义注解或者拦截规则管理员能看全部客户销售只能看自己负责的客户。这套系统如果实现了数据权限——也就是“销售只能看到owner_id是自己”的客户——说明代码质量是过关的因为这是客户管理系统里最容易漏掉的需求。2.4 核心查询与分页的实现细节客户列表页是最典型的业务场景表格带分页、搜索条件多、点查询按钮刷新数据。这类系统通常用MyBatis-Plus的分页插件实现核心代码大概是这样// Service层实现类中的分页查询方法 public PageResultCustomerVO pageCustomers(CustomerQuery query, Long currentUserId) { PageCustomer page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperCustomer wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(query.getCustomerName()), Customer::getCustomerName, query.getCustomerName()) .eq(query.getStatus() ! null, Customer::getStatus, query.getStatus()) .eq(query.getIndustry() ! null, Customer::getIndustry, query.getIndustry()) .eq(Customer::getOwnerId, currentUserId) // 只能查自己的客户 .orderByDesc(Customer::getCreateTime); customerMapper.selectPage(page, wrapper); // 封装返回结果包含总条数、当前页数据列表 return new PageResult(page.getTotal(), page.getRecords()); }这里面有几个细节值得注意。第一条件构造器里用了lambda表达式这是MyBatis-Plus的经典写法好处是类型安全、不容易写错字段名。第二每个查询条件都带了一个布尔判断意思是前端没传这个参数就不拼接这个条件避免生成一堆无用的WHERE条件。第三列表一定要按创建时间倒序否则新加的客户排不到最前面用户会觉得自己添加的数据丢了。分页参数pageNum和pageSize前端传第几页、每页多少条后端直接传给MyBatis-Plus的Page对象。返回给前端的总条数total会用来计算分页组件总共有多少页这个是列表页必须的数据。3. 前端Vue页面搭建与后端联调3.1 Vue工程结构和环境配置的避坑点现在源码站里发的Vue项目老一点的多是Vue 2 Element UI新一点的是Vue 3 Element Plus。拿到手先看package.jsonscripts里写的是vue-cli-service serve还是vite这决定了你的启动命令和端口配置方式。Vue工程的基础结构是src/ ├── api/ // 请求接口封装 ├── components/ // 公共组件 ├── router/ // 路由配置 ├── store/ // 全局状态管理Vuex或Pinia ├── views/ // 页面视图 ├── utils/ // 工具函数 ├── App.vue └── main.js环境配置有个高频坑前后端分离开发必然遇到跨域问题。前端跑在8080端口后端跑在8081端口或者别的端口浏览器出于安全策略会拦截跨域请求。常见解决方案有两种一种是在后端写跨域配置类一种是在前端配代理。前端代理的配置方式Vue 2项目在vue.config.js里module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true, // 可以在路径上做重写比如去掉/api前缀 // pathRewrite: { ^/api: } } } } }Vue 3 Vite项目在vite.config.js里配置export default defineConfig({ server: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } })配置好了之后前端所有请求都走相对路径/api/xxx由devServer转发到后端真实地址。本地开发环境这一步不配好页面永远报跨域错误而且报错信息有时候不直观新手容易蒙圈。3.2 客户管理页面的数据流实现Vue页面做客户管理核心就是“把后端数据拿到前端展示再把前端表单提交给后端保存”。这里面数据流其实不复杂一进一出而已但实现上要把分页、搜索、弹窗、表单验证、状态展示串起来。先说列表页。页面挂载后调用封装好的API方法拉取第一页数据拿到返回的records数组和total总数渲染成表格和分页组件。当用户点下一页、改每页条数、点搜索按钮时重新带着新的查询条件去请求后端。Axios封装是这个环节的关键。不要把axios请求直接写在页面里而是单独建一个api/customer.js文件统一管理所有客户模块的接口函数import request from /utils/request // 分页查询客户列表 export function getCustomerPage(params) { return request({ url: /customer/page, method: get, params }) } // 新增客户 export function addCustomer(data) { return request({ url: /customer, method: post, data }) } // 修改客户 export function updateCustomer(data) { return request({ url: /customer, method: put, data }) } // 删除客户 export function deleteCustomer(id) { return request({ url: /customer/${id}, method: delete }) }对应的utils/request.js里做axios实例化统一设置baseURL添加请求拦截器自动带上Token和响应拦截器统一处理错误码和Token过期跳转登录。这部分是Vue项目里必须有的基础设施没有的话每个页面都写一遍axios的完整逻辑代码会非常冗余。3.3 新增和编辑客户的表单逻辑客户新增和编辑可以共用一个弹窗表单组件。打开弹窗时判断有没有传入要编辑的客户ID没有就是新增有就是编辑然后调用对应接口。Element UI或Element Plus的el-dialog配el-form是非常成熟的组合。表单里放客户名称、客户类型、行业、状态、电话、地址这些字段el-form的rules可以配置必填校验比如客户名称不能为空、电话格式要正确。表单校验不通过时点击确定按钮会提示对应信息这是后台管理系统比较基础但必须做的体验优化。一个容易被忽略的细节是编辑回显时日期和状态这类字段要做类型转换。比如后端返回的客户状态是0、1、2这样的数字下拉框组件需要把数字映射成对应的标签。客户类型同理如果后端返回的是字符类型前端也需要做匹配处理。这套系统如果页面写得规范这个映射逻辑应该会放在一个单独的公共工具里而不是在每个页面各写一遍。这里给一个客户状态映射的小例子// 客户状态下拉选项 export const customerStatusOptions [ { value: 0, label: 潜在客户 }, { value: 1, label: 意向客户 }, { value: 2, label: 成交客户 }, { value: 3, label: 流失客户 } ] // 根据value取标签 export function getCustomerStatusLabel(status) { const item customerStatusOptions.find(item item.value status) return item ? item.label : 未知 }3.4 登录页与Token持久化系统不可能只有客户管理页面登录页一定是入口。登录页的逻辑是用户输入账号密码点登录调用后端登录接口后端返回Token和用户信息前端把Token存起来跳转到首页。Token存哪里三个选择localStorage、sessionStorage、内存。最简单的方案是localStorage刷新页面Token还在用户不用重新登录。但要注意XSS攻击风险万一页面被注入了恶意脚本Token可能被偷走。高级一点的方案是配合HttpOnly Cookie存储前端JS读取不到安全等级更高但是处理起来稍复杂。这套系统如果作为学习项目存localStorage也能接受但要清楚它的安全边界。前端路由守卫是登录校验的关键。Vue Router提供了beforeEach钩子每次跳转路由之前先检查有没有Token没有就跳转到登录页。同时要放行登录页本身否则就成死循环了router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path /login) { next() } else if (!token) { next(/login) } else { next() } })加上这一步之后Vue项目的完整链路才算闭环登录、存Token、发请求带Token、后端校验Token、路由守卫拦截非法访问。4. 本地部署、编译运行与常见问题排查4.1 环境准备JDK、Maven、Node、MySQL怎么配我每次分享这种前后端分离项目都会被问环境安装的问题。其实环境就四大件JDK、Maven、Node.js、MySQL。JDK建议用1.8或者11除非pom.xml里明确写了需要17。这里有个很重要的检查方法打开pom.xml看spring-boot-starter-parent里的版本。Spring Boot 2.x版本配JDK 8或者11都没问题Spring Boot 3.x要求JDK 17起步。很多源码站的老项目用的还是Spring Boot 2.x你非要用JDK 17去跑大概率会因为兼容性问题报奇怪错误。我实测下来Spring Boot 2.5 JDK 8是最不容易出错的组合跑这种源码项目最稳。Maven不用单独装也行用IDE内置的就行。但要检查一下本地仓库的镜像配置国内网络拉Maven依赖建议换成阿里云镜像否则下载依赖能等到怀疑人生。配置在maven的settings.xml文件里加一个mirror节点mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirrorNode.js版本很有讲究。Vue 2项目一般要求Node 12到16之间太高可能不兼容旧版本node-sass。Vue 3 Vite项目则建议Node 16以上。装Node的时候尽量用nvm管理版本方便切换。MySQL版本常见的是5.7和8.0。这两个版本在数据库驱动上有一点差别8.0版本的驱动类名是com.mysql.cj.jdbc.DriverURL还要加时区参数serverTimezoneAsia/Shanghai。如果项目里写的是旧驱动类而你本地装的是MySQL 8启动后端的时候会报无法加载驱动类。我当时就卡在这过后来直接把配置改成MySQL 8的驱动方式就通了。4.2 后端启动的完整流程和验证方法后端启动不是一上来就点运行按钮中间至少有三个准备工作。第一步创建数据库。打开Navicat或者命令行执行create database crm default charset utf8mb4然后导入项目里附带的.sql脚本。导入后检查一下表是否建全用户表里有没有默认数据。如果没有默认管理员账号看SQL脚本里是否包含INSERT语句一般会插一个admin账号密码可能是admin或者123456也有可能是BCrypt加密后的密文。这个是登录入口没有它你啥都验证不了。第二步修改application.yml配置。核心是三块内容数据库地址、数据库账号密码、Redis地址如果项目用了Redis。数据库地址注意localhost:3306/crm要和你的库名对应。有些项目还配置了文件上传路径或者日志路径改成你本地的实际目录。第三步启动启动类。后端项目启动后看控制台输出端口号通常是8081或者8080。如果端口被占用会直接报Port already in use解决方式有两个改application.yml里的server.port或者找到占用进程结束掉。后端是否启动成功的验证方法很简单浏览器直接访问http://localhost:8081/如果返回“Whitelabel Error Page”这种Spring默认错误页说明后端已经活着了。很多项目还会集成Swagger接口文档访问/swagger-ui.html或者/doc.html就能看到所有接口列表和参数说明。有Swagger的话前端联调都要方便很多。4.3 前端启动的常见报错与处理前端启动分两步先装依赖再跑开发服务器。装依赖的命令是npm install但这步的成功率不是百分之百。常见报错有几类一是node-sass下载失败这个老牌报错现在遇到得多因为NPM源访问GitHub上的二进制文件经常被卡住修改为使用npm镜像源或者升级项目到dart-sass能解决。二是依赖版本冲突比如package-lock.json和项目的package.json不一致删掉node_modules和package-lock.json后重新install即可。三是内存溢出如果项目比较大的话在package.json里加一条环境变量配置可以解决。装好依赖之后执行npm run serveVue CLI项目或者npm run devVite项目看到Compiled successfully字样基本就成了。然后浏览器访问http://localhost:8080先看到的应该是登录页。如果页面打不开优先检查devServer配置里改过端口没有和后端端口冲突没有。如果登录页打开了但点登录时请求报404或者跨域那就是代理配置的问题了回3.1节里看代理写法。4.4 高频Bug排查速查表把这类项目最常见的坑整理成一个速查表遇到问题对号入座就行。问题大概率原因解决方案启动报java.lang.UnsupportedClassVersionErrorJDK版本和项目编译级别不匹配升级JDK或检查pom.xml里的java.version前端启动报Node Sass could not find a bindingnode-sass没装好或不兼容删除node_modules重装或换成sass登录接口报404后端路径和前端请求路径不一致检查Controller里的RequestMapping和前端api函数里的url登录接口报500数据库连接失败或密码加密逻辑出错检查application.yml数据库配置和BCrypt工具类前端请求报CORS错误没配跨域或代理没生效检查后端跨域配置类或前端vue.config.js页面刷新后404前端是history路由但服务器没配置fallback改用hash路由或配置historyApiFallback导入SQL报中文乱码数据库字符集不对建库时指定utf8mb4导入前设置字符集Maven依赖下载超时连Maven中央仓库太慢配置阿里云镜像4.5 转帖源码项目如何“调试着学”最后说点实在的。源码站转过来的项目最大的价值不是拿来部署上生产而是当教材拆着学。我建议用下面这个顺序去读代码比随便翻效率高得多。第一遍跑通了之后先玩一遍界面把增删改查、登录、退出、跳转都点一遍弄清楚这个系统有什么功能。第二遍对着功能找代码点客户列表页看它调用了哪个接口、后端哪个Controller处理、Service里写了什么逻辑、SQL是怎么写的。第三遍自己改需求比如在客户列表加一个“按成交时间排序”的按钮或者给登录页加一个验证码通过改代码来加深理解。学完一遍之后你可以尝试做几件“让它更像生产项目”的事给每个接口补充Swagger注解把硬编码的字典值改成数据字典表在增加修改操作后面加一个操作日志记录把返回的时间字段格式化统一。这些改造做完你对这套系统的理解程度已经远超单纯“能跑起来”的级别了。而且以后面试聊项目你说得出自己在源码基础上做过哪些重构和增强比背八股文有说服力得多。这套企业客户管理系统虽然不是什么惊天动地的架构但作为Spring Boot Vue前后端分离项目的完整范式该有的知识点基本都覆盖了。我个人实际用下来的体会是这种转帖项目的代码风格未必多优秀但胜在完整只要你会耐心拆、肯下手改能学到的东西一点不少。至少把这一套啃下来以后再拿到任何同类项目基本都能做到心里有数。
返回列表