Spring Boot+Vue企业客户管理系统实战:从源码拆解到部署上线
2026/9/24 18:38:15 网站建设 项目流程

先聊个实在的:你手上如果有一套“企业客户管理系统 springboot+vue Java版”这种源码,千万别只把它当毕业设计或者简历项目扔一边。我拿到这类项目的第一反应,是把它当成一套微缩版的企业级应用样板间。Spring Boot + Vue 这个组合本身不新鲜,但里面涉及的权限、分页、报表、文件处理、前后端联调、部署上线,几乎覆盖了Java后端和前端工程师日常工作中最常用到的技能点。这篇博文我就从项目拆解、启动调试、核心代码剖析、踩坑实录这几个角度,把这类系统的玩法和背后的门道掰开揉碎讲清楚。

这个项目适合谁?两类人。一类是刚学完 Java、Spring Boot、Vue 基础,正愁没有一个完整项目把知识串起来的初学者;另一类是在小公司需要一个人撑起一套内部管理系统的“全干工程师”。前者拿它打通技术栈,后者拿它当业务脚手架。不管哪类,只要能把这个项目真正跑起来、看懂每一行关键代码,再把里面几个模块改成自己的需求,你对 Java Web 开发的理解会上一个台阶。

1. 项目定位与技术选型:一个真实可跑的客户管理系统

1.1 这项目到底是干什么的,适合谁拿来练手

先给没接触过这类系统的朋友解释一下,所谓“企业客户管理系统”,本质就是简化版 CRM(Customer Relationship Management)。核心业务就三件事:把客户信息管起来、把销售跟进过程记下来、把成交后的合同和统计做出来。网上流传的这套 Spring Boot + Vue 版本,一般还会带上用户登录、角色权限、数据看板这些模块,算是一个五脏俱全的业务系统。

我见过不少同学拿到源码后第一件事是去启动项目,结果报错一大堆就放弃了。这不对。拿到项目的第一步,应该是把业务理清楚。你脑子里得有这么一条链路:管理员创建账号 -> 销售登录 -> 录入客户 -> 填写跟进记录 -> 客户转商机 -> 成交后关联合同 -> 数据统计展示到首页看板。这条链路理顺了,你再去看代码,就不会迷失在几百个文件里。

适合拿来练手的原因也很简单:项目粒度适中。比那些“图书管理系统”复杂,比真正的电商中台简单。它包含前后端分离架构、RESTful 接口设计、数据库表关联、权限拦截这些企业级系统的标配元素,又没有复杂到让你一个月都跑不起来。对想在简历上写“独立开发过客户管理系统”的同学来说,性价比很高。

1.2 为什么偏偏是 Spring Boot + Vue,组合优势在哪

这个问题几乎是我面试时必问的,也是你写简历、面试答辩时必须能答上来的。先拆开看。Spring Boot 解决了什么问题?它把 Spring 生态里繁琐的 XML 配置干掉了,内嵌 Tomcat,一个 java -jar 就能把后端跑起来,配合 Starter 机制实现“开箱即用”。再加上 Spring Security 或者 Sa-Token 做认证授权、MyBatis-Plus 做数据访问、Redis 做缓存,一套企业级后端的标准件就齐了。

Vue 这边就不用多说了,渐进式框架,组件化开发,配合 Element UI 或 Element Plus 这种组件库,两三天就能搭出一个看着还行的管理后台界面。关键是响应式数据绑定,数据一变页面自动更新,写业务页面比 jQuery 时代舒服太多。

这两者组合在一起,最核心的优势是前后端彻底分离。后端只提供 JSON 接口,不关心页面长什么样;前端只调接口渲染页面,不关心数据从哪张表来。职责边界清晰,两队人可以并行开发。打个比方,后端就像是餐厅后厨,你负责把菜做好并按固定菜单出餐;前端是前厅服务员,把菜品按顾客需求摆盘上桌。后厨不用管餐桌怎么布置,服务员也不用管菜是怎么炒出来的。

有人可能会问,为什么不用传统的 JSP 或者 Thymeleaf 模板方案?现在企业内部新项目大部分都转向前后端分离了,原因很实际:移动端、小程序、PC 管理后台都可能要共用同一套后端接口,如果你用 JSP 渲染页面,接口和页面就绑死了,其他端根本没法复用。前后端分离后,同一套后端接口可以同时支撑 Web 后台、H5、小程序,这是架构层面最实际的考虑。

2. 功能模块拆解:客户管理系统的核心业务闭环

2.1 客户档案:数据模型设计是地基

不管前端页面怎么花哨,客户管理系统的基础永远是数据库表设计。以我见过的一套常见结构为例,核心表至少得有这几张:

  • sys_user:用户表,存账号、密码、姓名、部门、状态
  • sys_role 和 sys_user_role:角色表与用户角色关联表,做权限用
  • customer:客户表,存客户名称、联系人、电话、行业、来源、等级、状态
  • customer_follow:跟进记录表,存跟进内容、跟进方式、下次跟进时间
  • contract:合同表,存客户ID、合同金额、签约日期、状态
  • customer_pool:客户池表,有些系统做公海客户用,这里不一定有

数据模型设计上,最容易踩的坑是客户表和跟进记录表的关联字段没加索引。客户多了以后,按客户 ID 查跟进记录会非常慢。我当初接手过一套系统,客户表才几万条数据,联表查询就卡得不行,后来加了联合索引才解决。所以你在看这个项目源码的时候,重点看一下数据库脚本里的索引设计,这比看业务代码更有价值。

再说得细一点,客户状态字段建议用数字枚举,不要存中文。比如 0-潜在客户、1-意向客户、2-成交客户、3-已流失。前端用字典映射成中文显示。为什么?因为存数字可以方便地做统计聚合,而且修改枚举值不需要改数据库,只改代码里的字典就行。这套项目里如果状态字段用字符串存中文,你自己重构时可以考虑改掉。

2.2 跟进与商机:业务流转的关键链路

客户生命周期管理是这套系统的灵魂。销售每天最重要的动作不是录客户,而是填写跟进记录。所以跟进记录表的设计要非常讲究,核心字段包括:

  • 所属客户ID
  • 跟进人ID
  • 跟进内容(长文本)
  • 跟进方式(电话、拜访、微信等)
  • 下次跟进时间
  • 创建时间

这个模块的业务逻辑其实很直白,但有两个细节值得你仔细研究。第一个是“下次跟进时间”的提醒逻辑,不少系统会在首页做个待办提醒,把今天需要跟进的客户列表拉出来。这个功能用一条 SQL 就能实现,但要注意时区问题。如果你直接用 MySQL 的 NOW() 和字符串日期比,容易因为时区设置不一样导致结果有偏差。建议后端统一用 LocalDateTime,前端只负责展示。

第二个是跟进记录的权限。普通销售只能看到自己的跟进记录,销售主管能看到整个团队的,管理员能看到全部。这个需求真实且典型,实现方式通常是 SQL 里拼上数据权限过滤条件。你去看这套系统的 Mapper XML 或者 MyBatis-Plus 配置时,重点看这里,很多系统的核心业务价值就体现在权限控制够不够细。

2.3 合同、统计与看板:让数据真正有用

再往后的合同管理模块,本质就是把销售成果数字化。合同表关联客户和用户,金额、签约日期、回款计划这些字段是核心。我做这类系统的经验是,合同表的金额字段一定要用 DECIMAL(10,2) 类型,千万别用 DOUBLE,否则一旦金额大了,浮点数精度问题会让你哭不出来。

数据看板是很多初学者容易忽略的部分,但它是老板最关心的页面。一个像样的客户管理系统,首页通常会有这几个统计:

  • 今日新增客户数
  • 本月成交金额
  • 客户来源分布饼图
  • 近六个月成交趋势折线图
  • 销售业绩排行榜

这些统计在后端实现时,核心就是 SQL 的 GROUP BY 和聚合函数。拿“客户来源分布”举例,一条 SQL 就能搞定:SELECT source, COUNT(*) FROM customer GROUP BY source。前端用 ECharts 或者 AntV 把数据渲染成饼图。这部分难度不高,但非常体现你对 SQL 聚合查询的熟练度,面试时也经常被问。

2.4 用户、角色与权限:多租户思路的雏形

权限模块是我建议你花最多时间看的。常规做法是 RBAC(Role-Based Access Control)模型,三张核心表:用户表、角色表、用户角色关联表,加上菜单权限表或按钮权限表。用户登录后,后端返回该用户拥有的角色和权限列表,前端根据权限列表决定显示哪些菜单和按钮,后端接口再通过拦截器或注解校验一遍权限。

这里有个关键思想要理解:前端的权限控制只是用户体验层面的“隐藏”,后端的权限校验才是真正的安全边界。如果只做了前端隐藏,别人直接调接口一样能拿到数据。所以你在项目里一定会看到类似 @RequiresPermissions("system:user:add") 这样的注解,或者自定义拦截器去校验请求路径。这部分代码就是安全的核心。

我见过的网上源码,权限这块的实现五花八门。有基于 Spring Security 的,有基于 Sa-Token 的,也有自己写拦截器用 Redis 存 Session 的。不管用哪种方案,你只要能说清楚“用户 -> 角色 -> 权限”这条链路,以及前端路由守卫和后端拦截器各自的作用,面试基本就能过关。

3. 从零启动项目:环境、配置与前后端联调

3.1 本地环境清单:JDK、Maven、Node、MySQL 一个都不能少

先别急着敲代码,把基础环境准备好,这一步能省后续大量时间。我列一份我常用的环境清单,全部以稳定版本为主:

工具推荐版本备注
JDK1.8 或 8u202大多数 Spring Boot 2.x 项目推荐 JDK 8
Maven3.6.33.8+ 也能用,3.9 注意镜像源
MySQL5.7 或 8.08.0 需要改驱动和时区配置
Node.js14.x 或 16.x如果是 Vue 2 项目别用 Node 20+
npm/yarnnpm 6+建议配置淘宝镜像或使用 pnpm
IDEA / VS Code最新稳定版后端 IDEA,前端 VS Code 都行

这里特别说一句 Java 环境变量配置,很多新手卡在这一步。JAVA_HOME 指向 JDK 安装目录,Path 里加上 %JAVA_HOME%\bin,然后在命令行执行 java -version 验证。JDK 装完之后还有 Maven,Maven 的 settings.xml 里建议配好阿里云镜像,否则下载依赖能等到你怀疑人生。网上热词里全是“java环境变量配置详细教程”“java安装”这类搜索,说明这确实是新人高频卡点。

Node 版本的问题我多提一句。如果你拿到的是 Vue 2 + Element UI 的版本,Node 版本建议 16.x 以内。Node 18 以上跑 Vue 2 项目偶尔会出现 Digital Envelope Routines 这类 OpenSSL 报错。真遇到了,要么降 Node 版本,要么在 package.json 里加一句 "NODE_OPTIONS=--openssl-legacy-provider",但后者只推荐临时救急。

3.2 后端启动实操:改配置、建库、起服务

后端启动流程不算复杂,但细节非常多。我按实际操作的顺序讲讲。

第一步,用 IDEA 打开后端目录,等 Maven 把依赖下载完。这个过程可能从几分钟到十几分钟不等,取决于网络和机器性能。

第二步,找到 application.yml 或 application.properties 配置文件,重点改三处:数据源连接、Redis 连接、端口号。以常见的 MySQL 8.0 为例,配置大概是这个样子的:

spring: datasource: url: jdbc:mysql://localhost:3306/customer_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 server: port: 8080

第三步,把项目里自带的 SQL 脚本导入数据库。网上这套系统的 SQL 文件名可能叫 customer.sql 或 init.sql,打开看一眼,直接 Ctrl+A 全选执行就行。导入之后你会在数据库里看到一堆表,以及几条初始数据,包括 admin 账号。这里要特别提醒,SQL 脚本执行之前看一眼编码,如果是 UTF-8 编码的脚本,导入时数据库连接也要保持一致,否则中文全变乱码。

第四步,启动后端主类。如果你用的 Spring Boot 2.7 左右,主类上会有 @SpringBootApplication 注解,入口类名一般叫 CrmApplication 或者 SystemApplication。右键 Run 起来,看到 Banner 和控制台出现 Started 字样就是成功了。有个细节:如果 Redis 没启动 or 账号密码不对,项目可能启动失败或登录时直接报错,所以本地开发建议先把 Redis 起起来,Windows 用户直接下载 Redis-x64 的 zip 解压运行 redis-server.exe 即可。

3.3 前端启动实操:依赖安装、代理设置、路由联调

后端跑起来后,前端相对简单。打开前端目录,先执行 npm install 装依赖。如果是老项目带 package-lock.json 的可能快一点,没有的话大概率要卡几分钟。遇到 node-sass 安装报错是最常见的,我已经数不清帮多少人解决过这个问题了。node-sass 真不行就换成 sass(dart-sass),然后在 package.json 里把对应引用改掉。

依赖装完之后,看 vue.config.js 这个文件。网上这套项目一般会在这里配一个开发代理,把 /api 开头的请求转发到后端地址:

module.exports = { devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

为什么一定要配代理?因为浏览器有同源策略,前端跑在 8081,后端在 8080,直接请求就是跨域。虽然后端也可以配跨域过滤器,但开发阶段用代理是更规范的做法,不但解决了跨域问题,还让前端里所有接口路径都可以写成相对路径,后续部署也更灵活。

然后执行 npm run serve,控制台会提示访问 http://localhost:8081。这时候用管理员账号登录试一下,如果能看到首页的数据看板,说明基础链路已经通了。要是登录接口报了“网络错误”或者 404,多半是代理配置没生效或者后端没起来。

前后端联调时有一个非常实用的小技巧——打开浏览器开发者工具的 Network 面板,把 Fetch/XHR 过滤打开,然后重新登录。你能清楚地看到前端到底请求了哪个 URL、后端返回了什么状态码、响应体是什么。工作以后所有前后端扯皮的接口问题,基本就是靠这个面板定位的,比看代码猜要快得多。另外,热词里提到的 vue devtools 插件,建议也装上,Vue 调试组件数据、看路由状态非常方便。

4. 核心业务实现细节:这些代码值得仔细看

4.1 JWT 登录鉴权与全局异常处理

登录鉴权是前后端分离系统里绕不开的话题。网上这套系统用的最多的是 JWT(JSON Web Token),流程是:用户输入用户名密码,后端校验通过后生成一个 token 返回给前端;前端把 token 存在 localStorage 里;之后的每个请求,前端在请求头里带上 Authorization: Bearer ;后端写一个拦截器或过滤器,对除登录接口之外的请求进行 token 校验。

JWT 的优点是无状态,服务器不需要保存 Session,水平扩展更友好。但它的缺点也要心里有数:token 一旦签发,到期之前无法主动失效。所以很多实际项目会把 JWT 和 Redis 结合,把 token 存入 Redis 并设置过期时间,每次请求时先查 Redis。这套项目如果只是纯 JWT 没加 Redis 校验,你可以自己尝试加上,这算是一个很好的练手改进点。

代码层面,登录接口的核心逻辑大致如下:

public Result login(@RequestBody LoginDTO dto) { User user = userService.getUserByUsername(dto.getUsername()); if (user == null || !BCrypt.checkpw(dto.getPassword(), user.getPassword())) { return Result.error("用户名或密码错误"); } if (user.getStatus() == 0) { return Result.error("账号已被禁用"); } String token = JwtUtil.createToken(user.getId(), user.getUsername()); return Result.success(token); }

密码必须加密存储,常见方案是 BCrypt。这个项目数据库里如果管理员的密码是明文,说明源码被简化过,你自己用的时候一定要改成 BCrypt 加密。具体方法也不难,Spring Security 的 crypto 包里自带 BCryptPasswordEncoder,单独引一个依赖就能用。

再一个值得研究的是全局异常处理,就是常见的 @RestControllerAdvice 配合 @ExceptionHandler。这套项目里几乎所有顶层 Controller 异常都会统一走这个类处理,返回 JSON 格式的错误信息。这样做的好处是接口层不需要到处写 try-catch,代码清爽很多。你自己写项目时也应该养成这个习惯,这属于企业级开发的基础素养。

4.2 MyBatis-Plus 分页插件与条件查询

数据列表页是后台管理系统最常见的内容,而列表页的核心就是分页加条件查询。这套项目如果用了 MyBatis-Plus,你会发现分页可以优雅到让人感动。只需要配置一个分页拦截器:

@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }

然后在 Mapper 接口里写一个分页查询方法:

Page<CustomerVO> selectCustomerPage(Page<CustomerVO> page, @Param("query") CustomerQuery query);

对应的 XML 里,只需注意查询条件的动态拼接:

<select id="selectCustomerPage" resultType="com.example.entity.Customer"> SELECT * FROM customer <where> <if test="query.name != null and query.name != ''"> AND name LIKE CONCAT('%', #{query.name}, '%') </if> <if test="query.status != null"> AND status = #{query.status} </if> </where> ORDER BY create_time DESC </select>

调用时把当前页和每页条数传给 Page 对象,MyBatis-Plus 会自动在 SQL 后面拼接 LIMIT。返回的结果集封装了 total、records 等字段,前端直接就能用。这里有个值得注意的点,分页用的 Page 对象一定要是循环外创建并在 select 时传入,如果在一个 for 循环里反复执行分页查询,性能会非常难看,而且容易 OOM。这个错误我在代码评审里见过不止一次。

还有个小知识点,如果你要连表查询、聚合统计,分页插件的总记录数是基于你的 SQL 自动生成 count 语句的。复杂 SQL 自动生成的 count 可能有问题,这时候可以手写 count 语句,或者用嵌套子查询绕一下。遇到数据量大的场景再研究,练手项目一般碰不到。

4.3 Axios 请求封装、路由守卫与按钮级权限

前端部分,我不建议你只看页面组件,更要关注两个基础设施:Axios 封装和路由守卫。

Axios 封装通常是写在 request.js 文件里。核心逻辑是创建一个 axios 实例,设置 baseURL、超时时间,然后通过请求拦截器从 localStorage 取出 token 放进请求头,通过响应拦截器统一处理后端的响应结果和错误码。伪代码大概是:

const service = axios.create({ baseURL: '/api', 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 => { const res = response.data if (res.code !== 200) { Message.error(res.message) return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') router.push('/login') } return Promise.reject(error) } )

这个封装的价值体现在:项目里几百个接口方法都不需要自己处理 token 和错误,统一收敛到一个地方。而且当后端返回 401 的时候,前端可以全局处理自动跳登录页,用户体验好得多。很多源代码仓库里这个文件已经写好了,你直接用没问题,但要能看懂。

路由守卫就是在 router/index.js 里配置全局前置守卫:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path === '/login') { next() } else if (!token) { next('/login') } else { next() } })

和路由守卫配套的还有按钮级权限,比如“新增客户”按钮只有销售主管和管理员能看到。实现方式通常是登录后,后端把该用户的权限编码列表返回,前端用一个自定义指令 v-permission 去控制按钮的显示。这一层只做 UI 收敛,后端接口依然要做权限校验,这是我一直强调的红线。

4.4 数据统计报表的后端 SQL 与前端图表联动

数据看板看似简单,真正做起来也有不少细节。我拆开来讲,后端只管出 JSON 数据,前端负责绘图。

后端统计类的 SQL,典型的有这几个写法:

查询近六个月的成交趋势:

SELECT DATE_FORMAT(sign_time, '%Y-%m') AS month, SUM(amount) AS total_amount FROM contract WHERE sign_time >= DATE_SUB(NOW(), INTERVAL 6 MONTH) GROUP BY DATE_FORMAT(sign_time, '%Y-%m') ORDER BY month

查询客户来源分布:

SELECT source, COUNT(*) AS count FROM customer GROUP BY source

查询销售排行榜:

SELECT u.real_name, COUNT(c.id) AS customer_count, IFNULL(SUM(ct.amount), 0) AS total_amount FROM sys_user u LEFT JOIN customer c ON c.owner_id = u.id LEFT JOIN contract ct ON ct.customer_id = c.id AND ct.status = 2 GROUP BY u.id ORDER BY total_amount DESC LIMIT 10

这类聚合 SQL 写起来不难,难在业务口径。什么叫成交?合同状态是 2 算成交还是回款完成算成交?不同公司定义不一样。所以看源码时,你要注意这些统计 SQL 里的业务条件,理解了之后再去改需求就很容易。

前端部分,ECharts 的用法很固定,无非就是拿到后端返回的 JSON 数组,转换成 series 需要的格式,setOption 设置进去。真正容易踩的坑是图表容器宽高问题。ECharts 初始化的容器如果一开始没有明确的高度,图表会渲染不出来,控制台也不报错。新手遇到这个问题经常一脸懵。解决办法很简单:给图表容器加一个固定高度或者用自适应方案。

5. 实战踩坑记录与排查手册

5.1 环境层面的经典翻车现场与解决思路

我见过太多人卡在项目启动阶段,所以专门整理一份高频问题清单,每一类都是我实际遇到过或者帮别人排查过的:

现象原因解决方案
启动报 Failed to configure a DataSource数据源配置没生效或 MySQL 没启动检查 yml 核心配置,启动 MySQL 服务
Access denied for user 'root'MySQL 账号密码错误用 Navicat/命令行重新验证账号密码
Public Key Retrieval is not allowedMySQL 8.0 的认证插件问题在 JDBC URL 上加 allowPublicKeyRetrieval=true
连接超时等待 120000msMaven 下载依赖慢配置阿里云镜像,检查网络
java.lang.NoClassDefFoundErrorJDK 版本不匹配切换项目 JDK 到 1.8 或项目要求版本
Redis 连接失败Redis 服务没有启动先启动 Redis,再启动后端
GET 请求 405/404前端代理没配好或后端路径不对看 Network 面板请求 URL,和后端 Controller 注解比对

Spring Boot 版本太高也是个高频热搜词。如果你拿到的是基于 Spring Boot 2.x 的源码,自己不小心把版本升到 3.x,那就麻烦大了。Spring Boot 3 把 javax 包替换成 jakarta 包,MyBatis-Plus、Sa-Token 等大量旧依赖都要随之升级适配,不然编译直接报错。我的建议是:练手阶段不要乱升版本,先让项目跑起来,等理解透了再逐步迁移。

5.2 业务联调中的字段与状态问题

跑通基础流程后,真正耗费时间的是业务联调。最常见的一类坑是字段命名不一致。后端实体类用的驼峰命名(createTime),数据库字段是下划线(create_time),如果直接返回 JSON,前端拿到的一会是 createTime 一会是 create_time,乱成一团。MyBatis-Plus 默认开了驼峰转下划线,所以数据库映射通常没问题。但如果你在后端 VO 里手动定义字段时命名不一致,就会出问题。排查思路很简单,打开 Network 看接口实际返回的 JSON 字段名,和前端取的是不是同一个。

第二类坑是状态码设计不统一。有的接口成功返回 code=200,有的返回 code=0,前端封装时只判断了一种,就会导致部分接口明明成功了却提示错误信息。所以在开发自测时,要把每个接口的响应体都看一遍,必要时在后端统一 Result 包装类的状态码。

第三类坑是时间格式问题。后端返回一个 LocalDateTime,默认格式一堆“T”,前端直接拿去显示就是类似 2024-08-01T10:30:00 这种,用户看了想打人。解决办法是在 yml 里配置全局时间格式,或者在字段上加 @JsonFormat 注解。

5.3 部署上线时的五个隐藏坑

项目本地跑通不是终点,部署上线才是。我在帮朋友公司部署类似系统时,总结了几个容易被忽略的点。

第一个是部署环境的 Java 版本和时间时区。服务器上装的 JDK 必须和开发环境一致或兼容,时区要设置为 Asia/Shanghai,否则后台记录的时间会差 8 小时。启动 jar 包时可以在命令里加上 -Duser.timezone=GMT+8。

第二个是数据库的数据库账号权限问题。开发时用的 root 账号在服务器上往往不能用,需要单独创建一个业务账号,只给它当前数据库的增删改查权限。别嫌麻烦,这是基本的安全底线。

第三个是文件上传路径。本地开发时可以传到本地磁盘,但部署到服务器后,路径要改成服务器的绝对路径。很多源码习惯把路径写死在代码里,部署时必须改掉。建议把文件存储路径放到配置文件中,用 @Value 注入。

第四个是 Nginx 配置。前端打包成 dist 文件后,用 Nginx 托管静态资源,同时要做一层反向代理:把 /api 代理到后端服务的地址。这里特别容易踩的是刷新页面出现 404,原因是 Vue Router 用了 history 模式,需要在 Nginx 配置里加一句 try_files $uri $uri/ /index.html;。

第五个是日志。上线后服务出问题,靠肉眼和 print 根本不够看。项目里至少要有 error 级别的日志输出到文件,或者直接用 logback 把日志滚动切分。你看源码时如果发现没有日志配置,部署前自己要补上。

6. 这套系统后续还能怎么升级

写到这里,整套系统的技术细节已经讲得差不多了。最后聊点我个人在实际操作中的体会。

我前后拿这种 Spring Boot + Vue 的客户管理系统给朋友公司部署过几套,第一版基本能用,但离“好用”还有很远。如果你想在这套源码的基础上继续练手或交付给真实客户,我建议优先做这几个方向的升级。

第一个是引入流程引擎。热词里有 flowable springboot ui,说明很多人关注这块。客户管理里确实有审批流的场景,比如合同审批、客户转公海审批。自己写状态机不是不行,但一旦流程复杂了,用 Flowable 这类流程引擎会更省心,而且这块经验写在简历上含金量不低。

第二个是数据权限的精细化。现在很多源码只做了功能权限,细分到“销售只能看自己的客户”这层逻辑经常写得不够严谨。建议自己实现一下基于部门、负责人、数据范围的数据权限过滤,这在真实企业里是刚需。

第三个是缓存策略。用户登录信息、客户详情这些数据如果不做缓存,高并发场景数据库压力会很大。可以把 Redis 切实用起来,比如把客户详情缓存到 Redis,更新时再同步失效。

第四个是前端体验升级。Vue 2 升级到 Vue 3 + Vite + Element Plus 是一个很有价值的重构项目,虽然工作量大,但你会深刻体会 Vue 3 的组合式 API 带来的代码组织优势。如果不想整体升级,也可以只把登录页、首页看板这些高频页面重写一遍练手。

这套系统的源码我建议大家不要只看不写,挑两三个模块自己从头做一遍。比如你可以把“客户跟进提醒”这个功能从数据库设计到接口、页面完全重写,不用太久,但收获比翻十遍代码都大。这是我在踩了无数坑之后最推荐的学习方式。

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

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

立即咨询