本文还有配套的精品资源点击获取简介直接可用的汽车4S店后台管理项目基于Spring Boot后端和Vue前端分离开发适合毕业设计或中小规模4S店业务落地。系统涵盖用户、角色、部门、岗位、菜单、字典、参数等基础配置模块支持树形组织结构展示和数据级权限控制。内置通知公告、操作日志、登录日志、在线用户实时查看、定时任务调度带执行记录、缓存状态与服务运行监控等功能。提供可视化表单构建器可拖拽生成HTML表单配套代码生成器支持一键生成Java实体类、Mapper接口、Service逻辑、Controller层及对应Vue页面和建表SQL语句覆盖完整CRUD流程。包含MySQL初始化脚本ry_20210908.sql、quartz.sql、多环境配置文件dev/prod/staging、Windows.bat与Linux.sh启动脚本以及详细部署说明文档。项目采用标准模块划分含ruoyi-system、ruoyi-quartz、ruoyi-ui等子模块结构清晰便于二次开发和功能扩展。1. 项目概述这不是一个“模板”而是一套可直接跑通的4S店业务底盘我带过六届计算机专业毕业设计也给三家本地4S店做过轻量级系统升级。每次学生一说“想做个汽车管理系统”我就知道大概率会陷入两个坑要么前端页面花里胡哨但后端连客户档案增删都卡在权限校验上要么数据库表建了一堆结果登录模块调不通、日志查不到、定时任务根本没触发——最后答辩前一周还在改PreAuthorize注解里的SpEL表达式。这套源码包我去年在东风日产一家二级经销商落地时用过它不是那种“看起来很全、跑起来就报错”的教学Demo而是真正把4S店后台最常踩的坑提前焊死在架构里了。核心关键词你已经看到了4S店系统、Spring Boot、Vue、权限管理、代码生成器。但光看词容易误解——它不是“又一个若依RuoYi二次封装”而是以若依为基座做了大量面向汽车服务场景的深度适配。比如它的“部门树”不是简单展示行政层级而是按4S店真实组织结构预设了“销售部→销售顾问组→试驾专员”、“售后部→机电车间→钣金组→喷漆组”这样的多级嵌套它的“字典管理”里直接内置了“车辆品牌奔驰/宝马/奥迪/比亚迪/蔚来…”、“维修工单状态待派工/施工中/质检中/已结算/已开票”、“客户等级潜客/保有客户/流失预警客户”等业务字段它的“参数配置”模块甚至预留了“首保里程阈值”、“续保提醒天数”、“事故车定损时效上限”这类运营参数开关。这些不是后期加的补丁是骨架里就长出来的。它适合谁如果你是学生做毕业设计这套系统能让你在两周内搭出一个有模有样、能演示完整业务流比如新增客户→分配销售顾问→录入试驾记录→生成意向单→关联金融方案的后台答辩老师点开“操作日志”能看到每一步谁在什么时候干了什么点开“在线用户”能实时看到模拟的销售主管正在查看库存报表——这种真实感比写一百行“本系统采用B/S架构”强得多。如果你是小规模4S店的信息员或IT外包它能省掉你从零搭建基础框架的三个月时间你只需要把“ry_20210908.sql”导入MySQL改几处配置文件里的数据库地址和账号密码双击ry.bat就能启动一个带完整权限体系的后台然后立刻开始配置你们店自己的销售流程、维修工单字段、配件库存分类。它不承诺替代DMS经销商管理系统但它能快速补上那些DMS没覆盖到的毛细血管级需求比如内部培训通知、员工排班看板、客户回访计划跟踪。我特别想强调一点很多人看到“代码生成器”就以为是偷懒工具其实恰恰相反。它逼你必须先想清楚业务实体。比如你要做一个“事故车定损单”模块生成器不会替你判断“定损金额”该用BigDecimal还是Double也不会自动给你加上“保险公司核损通过”这个状态流转逻辑。但它会强制你打开可视化表单构建器拖拽出“车牌号输入框”、“出险日期日期选择器”、“初估损失数字输入框”、“关联维修工单下拉选择”这几个字段并为你生成对应的Java实体类、MyBatis Mapper XML里的resultMap映射、Service层的空方法骨架、Controller里的REST接口以及Vue页面里完整的Element UI表单组件和API调用逻辑。这个过程本质上是在帮你把模糊的业务想法翻译成可执行、可调试、可协作的代码契约。后面你再往里面填业务逻辑心里就有底了。2. 架构设计与核心模块拆解为什么选这个组合而不是其他方案2.1 后端选型Spring Boot不是为了“时髦”而是为了“少写样板代码”你可能会问为什么不用更轻量的Spring MVC或者更激进的Quarkus答案很实在稳定、生态成熟、团队熟悉度高、问题有迹可循。我见过太多学生用Spring MVC手写拦截器、自己拼接SQL、手动处理事务传播结果一个登录失败的日志都打不出来。Spring Boot的核心价值在于它把那些重复、枯燥、极易出错的“胶水代码”全部封装好了。自动配置Auto-Configuration比如你引入spring-boot-starter-data-jpa它就自动帮你配好数据源、事务管理器、JPA EntityManagerFactory你不用再翻《Spring实战》第7章去抄XML配置。在这个4S店系统里ruoyi-system模块的pom.xml里明确依赖了spring-boot-starter-web、spring-boot-starter-jdbc、mybatis-spring-boot-starter这意味着HTTP服务、数据库连接、ORM框架这三块基石开箱即用。起步依赖Starter Dependenciesspring-boot-starter-quartz让定时任务调度变得像写一个Scheduled注解一样简单。你看ruoyi-quartz模块它不只是简单调用Quartz API而是封装了一个可视化的“定时任务管理”页面你可以在这里新增、暂停、立即执行一个任务并且所有执行日志都自动记录到数据库的qrtz_job_log表里。这对4S店太实用了——比如每天凌晨2点自动同步一次厂家的配件价格表或者每周一上午9点给所有未回访的潜客发送短信提醒这些逻辑你只需要写一个Java方法然后在后台页面点几下就配好了。Actuator监控端点这是系统自带“健康检查”的心脏。/actuator/health返回服务整体状态/actuator/metrics告诉你JVM内存用了多少、HTTP请求平均耗时多少、数据库连接池是否紧张。我在部署到那家日产店的测试服务器上就靠/actuator/env端点一眼看出他们运维同事把spring.profiles.active配成了dev而不是prod导致日志级别是DEBUG磁盘空间三天就满了。没有Actuator这种问题得翻十几份日志文件才能定位。提示别小看application.yml里那一堆spring:开头的配置。比如spring.cache.typeredis这行决定了你的热点数据如菜单树、字典项是存在本地JVM内存里还是统一放在Redis里供集群节点共享。对于4S店这种可能有多个销售顾问同时查同一款车库存的场景用Redis缓存能避免数据库被高频查询压垮。2.2 前端选型Vue 2 Element UI是“够用”与“可控”的平衡点为什么不是Vue 3 Composition API也不是React因为交付效率和维护成本。Vue 2的Options API虽然不如Composition API灵活但它的生命周期钩子created,mounted、计算属性computed、侦听器watch概念极其清晰一个刚学完JavaScript基础的学生两天就能看懂ruoyi-ui/src/views/system/user/index.vue里是怎么发起用户列表请求、怎么渲染表格、怎么触发编辑弹窗的。Element UI则提供了开箱即用的、符合国内后台审美的一整套UI组件表格、表单、弹窗、树形控件、时间选择器……它们不是“好看就行”而是经过了海量企业级应用验证的稳定性。路由守卫Router Guard实现权限控制Vue Router的beforeEach全局前置守卫是整个前端权限体系的闸门。当你点击“客户管理”菜单时它不会直接跳转而是先去调用getInfo()接口拿到当前用户的菜单权限列表比如[system:user:list, system:user:add]然后动态addRoutes()只把用户有权限的路由注入到Vue Router实例中。这样即使有人手动在浏览器地址栏输入/user/edit/123只要他没这个system:user:edit权限页面就会空白或跳转到403页面。这个逻辑在ruoyi-ui/src/permission.js里写得明明白白。指令Directive封装复用逻辑比如v-permission[system:user:edit]这个自定义指令它背后就是一段简单的权限比对逻辑。你不需要在每个按钮的v-if里都写一遍$store.getters.roles.includes(system:user:edit)一行指令搞定。这种封装让前端代码干净得像教科书。Axios拦截器统一处理所有HTTP请求都经过src/utils/request.js里的拦截器。请求发出前自动带上AuthorizationBearer Token响应回来后如果状态码是401未登录就自动跳转到登录页如果是500就统一弹出错误提示。你在写一个“生成维修报价单”的接口时完全不用操心Token过期怎么办、网络超时怎么提示这些脏活累活框架已经替你干了。2.3 权限模型RBACABAC混合解决4S店最头疼的“数据隔离”问题很多系统只讲“角色能访问什么菜单”这在4S店是远远不够的。销售顾问A只能看自己名下的客户不能看隔壁顾问B的客户售后经理只能审批自己车间的工单不能批别的车间的。这就需要数据权限DataScope而不仅仅是菜单权限MenuPermission。这套系统采用的是RBAC基于角色的访问控制 ABAC基于属性的访问控制的混合模型-RBAC部分sys_user用户、sys_role角色、sys_menu菜单、sys_role_menu角色-菜单关联这四张表构成了经典的权限骨架。你给“销售总监”角色分配了“客户管理”菜单那么所有拥有这个角色的用户都能看到客户管理的入口。-ABAC部分数据权限关键在sys_role表里的data_scope字段它有五个取值1.1全部数据权限如超级管理员2.2自定义数据权限可手动勾选部门3.3本部门数据权限销售部的人只能看销售部的数据4.4本部门及以下数据权限销售总监能看到销售部所有销售顾问组的数据5.5仅本人数据权限销售顾问只能看自己创建的客户这个逻辑在后端SysRoleServiceImpl.java的selectDeptListByRoleId()方法里实现。当一个销售顾问角色data_scope5去查客户列表时MyBatis的XML里会自动拼上AND create_by #{userId}这个条件当他data_scope4时SQL里就会变成AND dept_id IN (SELECT dept_id FROM sys_dept WHERE ancestors LIKE CONCAT((SELECT ancestors FROM sys_dept WHERE dept_id #{deptId}), %))也就是查他所在部门及其所有子部门的数据。这种动态SQL拼接是通过MyBatis的if标签和DataScope注解配合完成的你几乎感觉不到它的存在但它无处不在。注意数据权限的生效依赖于你在业务代码里正确使用DataScope。比如在SysUserController.java的list()方法上必须加上DataScope(deptAlias u, userAlias u)框架才知道要给u这个表别名加数据权限过滤。漏掉这个注解数据权限就形同虚设。3. 核心功能实操详解从启动到定制手把手带你跑通第一个业务模块3.1 环境准备与一键启动Windows和Linux的差异在哪别被一堆.bat和.sh脚本吓住它们本质都是“一键启动”的包装纸。核心逻辑就三步编译后端、安装前端依赖、启动前后端服务。Windows环境ry.batecho off echo 正在启动若依后台服务... cd /d %~dp0ruoyi-admin call mvn clean package -Dmaven.test.skiptrue if %errorlevel% neq 0 goto error echo 正在启动若依前端服务... cd /d %~dp0ruoyi-ui call npm install if %errorlevel% neq 0 goto error start cmd /k npm run dev echo 后台服务启动中... cd /d %~dp0ruoyi-admin java -jar target/ruoyi-admin.jar --spring.profiles.activedev goto end :error echo 构建或启动失败请检查日志 pause :end这段脚本的关键点在于-cd /d %~dp0ruoyi-admin%~dp0是批处理的魔法变量代表当前.bat文件所在的目录。ruoyi-admin是后端模块的目录名确保路径正确。-mvn clean package -Dmaven.test.skiptrue跳过单元测试加快打包速度。对于毕业设计测试覆盖率不是首要目标。-start cmd /k npm run dev/k参数保证命令窗口不关闭这样你就能看到前端npm run dev的实时日志比如Webpack编译进度、热更新提示。-java -jar target/ruoyi-admin.jar --spring.profiles.activedev这才是真正的后端启动命令。--spring.profiles.activedev指定了激活application-dev.yml配置文件里面包含了开发环境的数据库地址、Redis地址等。Linux环境ry.sh#!/bin/bash echo 正在启动若依后台服务... cd $(dirname $0)/ruoyi-admin mvn clean package -Dmaven.test.skiptrue if [ $? -ne 0 ]; then echo 后端构建失败 exit 1 fi echo 正在启动若依前端服务... cd $(dirname $0)/ruoyi-ui npm install if [ $? -ne 0 ]; then echo 前端依赖安装失败 exit 1 fi nohup npm run dev frontend.log 21 echo 前端服务已后台启动日志见 frontend.log echo 后台服务启动中... cd $(dirname $0)/ruoyi-admin nohup java -jar target/ruoyi-admin.jar --spring.profiles.activedev backend.log 21 echo 后台服务已后台启动日志见 backend.logLinux脚本的核心差异是nohup和它们让服务在终端关闭后依然运行。nohup忽略挂起信号SIGHUP让进程在后台运行。日志重定向 backend.log 21把标准输出和标准错误都写入backend.log文件方便排查问题。如果你在云服务器上部署这就是生产环境的标准姿势。实操心得第一次启动失败90%的原因是数据库没配好。打开ruoyi-admin/src/main/resources/application-dev.yml找到spring: datasource:部分把url、username、password改成你本地MySQL的真实信息。比如我的MySQL root密码是123456那么password: 123456。千万别用默认的root和空密码除非你确定MySQL允许。3.2 数据库初始化ry_20210908.sql和quartz.sql分别管什么项目根目录下的两个SQL文件分工非常明确ry_20210908.sql这是主业务数据库的初始化脚本。它创建了ry这个数据库并在里面建了几十张表包括sys_user用户信息账号、昵称、头像、状态sys_role角色信息角色名、描述、数据权限范围sys_menu菜单信息菜单名、路径、图标、排序号、是否显示sys_dept部门信息部门名、父部门ID、负责人、排序号并用ancestors字段实现了高效的树形查询比如查“销售部”及其所有子部门只需WHERE ancestors LIKE 0,100,sys_dict_typesys_dict_data字典类型和字典数据比如“车辆品牌”是一个类型“奔驰”、“宝马”是它的数据项sys_config系统参数比如“是否开启注册功能”、“邮件服务器地址”这个脚本里还插入了初始数据一个admin/admin123的超级管理员账号一个common/common123的普通用户账号以及“系统管理”、“监控管理”、“工具管理”等顶级菜单。你导入后直接用admin/admin123登录就能看到一个功能完整的后台。quartz.sql这是Quartz定时任务调度框架的专用数据库表。Quartz本身不依赖数据库但为了实现集群、持久化任务状态、记录执行日志就必须用数据库来存储。这个脚本创建了qrtz_job_details任务详情、qrtz_triggers触发器、qrtz_fired_triggers已触发的任务、qrtz_job_log执行日志等11张表。ruoyi-quartz模块的所有功能都建立在这套表结构之上。如果你不导入它那么“定时任务管理”页面能打开但你新建的任务永远不会被执行因为Quartz找不到地方存它的状态。提示导入顺序很重要必须先执行ry_20210908.sql创建ry库再执行quartz.sql。因为quartz.sql里的建表语句默认是针对ry库的。如果你先执行quartz.sql它会报错“Database ‘ry’ does not exist”。3.3 权限管理实战如何为“销售顾问”角色配置“仅看自己客户”的权限这是4S店最典型的权限需求。我们来走一遍完整流程从后台配置到代码验证。第一步创建角色1. 用admin/admin123登录系统进入【系统管理】-【角色管理】。2. 点击【新增】填写角色名称为“销售顾问”角色权限字符串填sales_advisor这个字符串是后端代码里用来识别角色的可以自定义但要唯一。3. 在【数据权限】下拉框中选择“仅本人数据权限”。这一步至关重要它设置了sys_role.data_scope 5。第二步分配菜单权限1. 在角色编辑页面切换到【菜单权限】Tab。2. 展开左侧菜单树勾选【客户管理】下的所有子菜单客户列表、客户新增、客户编辑、客户删除。注意这里只是给了他“看到这个菜单并点击进去”的权利具体能看到哪些客户数据由上一步的“数据权限”决定。第三步创建用户并绑定角色1. 进入【系统管理】-【用户管理】点击【新增】。2. 填写用户名zhangsan昵称张三手机号13800138000邮箱zhangsan4s.com。3. 在【角色】区域勾选刚才创建的“销售顾问”角色。4. 点击【提交】。第四步验证效果1. 退出管理员账号用zhangsan/123456新用户默认密码是123456登录。2. 点击【客户管理】-【客户列表】你会看到一个空表格或者只有你自己刚刚创建的客户如果你在创建用户后又用这个账号创建了一个客户。3. 打开浏览器开发者工具F12切换到Network标签页刷新客户列表页面。找到/user/list这个请求点开它看Response。你会发现返回的JSON数据里rows数组是空的或者只有一条记录。4. 此时后端SysUserController.list()方法被调用由于该用户所属角色的data_scope5MyBatis的SQL会自动加上AND create_by zhangsancreate_by是sys_user表里的用户名字段。这就是数据权限在起作用。注意事项数据权限的字段名这里是create_by是写死在SysUserMapper.xml里的。如果你的业务表里记录创建人不是用create_by而是用creator_id或者owner_name那么你必须修改SysUserMapper.xml里对应的SQL片段否则权限会失效。这是一个常见的二次开发点。3.4 代码生成器如何为“维修工单”模块生成一套CRUD这是提升开发效率的核武器。我们以“维修工单repair_order”为例演示从零开始生成一个完整模块。第一步设计数据库表在MySQL客户端如Navicat或DBeaver里连接到ry数据库执行以下建表语句CREATE TABLE repair_order ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, order_no varchar(50) NOT NULL COMMENT 工单编号, car_vin varchar(17) NOT NULL COMMENT 车辆VIN码, customer_name varchar(50) NOT NULL COMMENT 客户姓名, phone varchar(20) DEFAULT NULL COMMENT 联系电话, repair_type varchar(20) DEFAULT NULL COMMENT 维修类型保养/故障维修/事故维修, status varchar(20) DEFAULT pending COMMENT 工单状态pending待派工/processing施工中/done已完成, create_by varchar(64) DEFAULT COMMENT 创建者, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_by varchar(64) DEFAULT COMMENT 更新者, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修工单表;第二步使用可视化表单构建器1. 登录系统进入【工具管理】-【表单构建】。2. 点击【新建表单】表单名称填“维修工单”表单编码填repairOrderForm。3. 在右侧画布区拖拽组件- 一个“文本框”设置字段名order_no标签为“工单编号”并勾选“必填”、“唯一校验”。- 一个“文本框”字段名car_vin标签为“车辆VIN码”勾选“长度限制17”。- 一个“下拉选择框”字段名repair_type标签为“维修类型”选项来源选择“字典数据”字典类型填repair_type你需要先在【系统管理】-【字典管理】里创建这个字典。- 一个“单选框”字段名status标签为“工单状态”选项为pending|待派工,processing|施工中,done|已完成。4. 点击【保存】系统会生成一个HTML表单代码你可以复制它粘贴到任何地方使用。第三步使用代码生成器1. 进入【工具管理】-【代码生成】。2. 点击【查询】在表名搜索框里输入repair_order点击搜索你会看到这张表出现在列表里。3. 点击右侧的【编辑】填写- 业务名称维修工单- 模块名称repair- 功能作者yourname- 生成模板crud这是默认的生成完整CRUD- 表前缀repair_去掉表名前缀生成的Java类名就是RepairOrder- 包路径com.ruoyi.repair这是后端Java代码的包名- 前端路径repair/order这是Vue页面的路由路径4. 点击【生成代码】系统会打包下载一个ZIP文件。第四步导入生成的代码1. 解压ZIP你会看到src/main/java/com/ruoyi/repair/后端Java代码和src/views/repair/order/前端Vue代码两个文件夹。2. 将java文件夹下的内容复制粘贴到你本地项目的ruoyi-admin/src/main/java/com/ruoyi/repair/目录下。3. 将views文件夹下的内容复制粘贴到ruoyi-ui/src/views/repair/order/目录下。4. 修改ruoyi-admin/pom.xml确保dependencies里有ruoyi-common的依赖通常已有。5. 修改ruoyi-ui/src/router/index.js在routes数组里添加一条新的路由js { path: /repair/order, component: () import(/views/repair/order/index), name: RepairOrder, meta: { title: 维修工单, icon: form } }6. 重启前后端服务。现在你就可以在菜单里看到【维修工单】了点击进去就是一个功能完备的CRUD页面列表、新增、编辑、删除、搜索全部都有。实操心得生成的代码不是终点而是起点。比如你可能需要在“新增工单”时自动填充当前登录用户的姓名到create_by字段。这需要修改生成的RepairOrderController.java里的addSave()方法在repairOrder.setCreateBy(getUsername());这一行。再比如前端列表页的“状态”列你想显示中文而不是英文pending-待派工就需要在index.vue的columns配置里把status字段的formatter指向一个字典翻译函数。这些微调才是体现你业务理解的地方。4. 高级功能与避坑指南监控、日志、部署中的真实陷阱4.1 系统监控/actuator端点不是摆设是你的“系统仪表盘”Spring Boot Actuator暴露了一系列生产就绪的端点它们是诊断系统健康状况的第一道防线。不要只把它当成一个“能访问就行”的功能要真正用起来。/actuator/health这是最常用的。返回一个JSONstatus字段是UP表示一切正常DOWN表示有问题。但它背后有玄机details里会列出各个组件的健康状态。比如json { status: UP, details: { diskSpace: {status: UP, details: {total: 500000000000, free: 200000000000}}, redis: {status: UP, details: {version: 6.2.6}}, db: {status: UP, details: {database: MySQL, validationQuery: isValid()}} } }如果redis状态是DOWN说明Redis连接不上那么所有依赖Redis缓存的功能如菜单树、字典项都会变慢或失效。这时候你应该立刻检查application.yml里的spring.redis.host和port是否正确。/actuator/metrics这是性能分析的宝库。它返回所有可用的指标名称比如jvm.memory.usedJVM已用内存、http.server.requestsHTTP请求数、datasource.hikaricp.connections.active活跃数据库连接数。你可以用/actuator/metrics/{name}来获取具体指标的值。例如/actuator/metrics/jvm.memory.used会返回json { name: jvm.memory.used, measurements: [ { statistic: VALUE, value: 256000000 } ], availableTags: [ { tag: area, values: [heap, nonheap] } ] }如果jvm.memory.used的值持续飙升接近你设置的-Xmx最大堆内存那就说明有内存泄漏需要做Heap Dump分析。/actuator/loggers这是日志级别的动态开关。你可以用POST请求临时把某个包的日志级别调成DEBUG而不用重启服务。比如你想查“为什么定时任务没执行”可以向/actuator/loggers/org.quartz发送一个JSONjson {configuredLevel: DEBUG}然后去看backend.log就能看到Quartz内部详细的调度日志了。排查完问题再把它调回INFO避免日志爆炸。提示Actuator端点默认只开放了health和info。要启用metrics、loggers等必须在application.yml里显式配置yaml management: endpoints: web: exposure: include: health,info,metrics,loggers,env,beans,threaddump4.2 日志追踪操作日志、登录日志、定时任务日志它们都存在哪日志是系统的“黑匣子”出了问题它是唯一的线索。这套系统把三类关键日志都落到了数据库里而不是仅仅写在文件里这极大地方便了查询和审计。操作日志sys_oper_log表记录用户每一次“有意义”的操作。比如点击【客户管理】-【新增客户】按钮提交表单后端SysUserController.addSave()方法执行成功就会在sys_oper_log里插入一条记录title: “客户管理”business_type: “1” 1新增2编辑3删除4导出method: “com.ruoyi.web.controller.system.SysUserController.addSave”request_method: “POST”operator_type: “1” 1后台用户2手机APP用户oper_name: “admin”dept_name: “研发部”oper_url: “/system/user/add”oper_ip: “127.0.0.1”oper_location: “内网IP”oper_param: “{“userName”:”test”, “nickName”:”测试”}” 请求参数的JSON字符串status: “0” 0成功1失败error_msg: “” 失败时的错误信息oper_time: “2023-10-01 10:00:00”这张表的设计非常聪明oper_param字段是TEXT类型可以存很长的JSONstatus和error_msg字段让你一眼就能区分成功和失败的操作。在【系统监控】-【操作日志】页面你可以按用户名、操作模块、时间段、状态来筛选找出所有失败的操作然后根据error_msg去定位Bug。登录日志sys_logininfor表专门记录登录行为字段更精简login_name: 用户名ipaddr: IP地址login_location: 地理位置通过IP库解析browser: 浏览器类型os: 操作系统status: “0”成功或“1”失败msg: “登录成功” 或 “密码错误”login_time: 登录时间这张表是安全审计的核心。你可以用它来发现异常登录比如同一个账号在一分钟内从北京和广州两个IP同时登录那基本可以判定账号被盗了。定时任务日志qrtz_job_log表这是quartz.sql脚本创建的表记录Quartz任务的每一次执行job_name: 任务名如sysTaskjob_group: 任务组如DEFAULTinvoke_target: 调用的目标方法如com.ruoyi.quartz.task.SysTask.syncData()job_message: 执行结果消息如“同步成功共处理12条数据”status: “0”成功或“1”失败exception_info: 失败时的完整堆栈信息create_time: 创建时间当你发现某个定时任务“应该每天执行但今天没执行”时第一件事就是查这张表。如果表里根本没有今天的记录说明Quartz调度器根本没触发这个任务问题出在调度配置或Quartz集群状态上如果表里有记录但status1那就直接看exception_info99%的问题都能在这里找到答案。注意日志表的数据会越积越多必须定期清理。系统自带了一个定时任务【系统监控】-【定时任务】里的“清除系统日志”你可以把它配置成每天凌晨执行自动删除30天前的操作日志和登录日志。别忘了给qrtz_job_log也配一个类似的清理任务否则几个月后这张表会达到百万级记录查询会变得非常慢。4.3 部署上线从dev到prod配置文件切换的致命细节开发环境dev和生产环境prod的配置绝不仅仅是数据库地址不同。一个疏忽就可能导致线上事故。数据库连接池HikariCPapplication-dev.yml里spring.datasource.hikari.maximum-pool-size可能是20这在开发机上绰绰有余。但在生产环境面对几十个销售顾问同时刷客户列表20的连接池可能瞬间被打满导致后续所有请求排队等待系统假死。application-prod.yml里这个值应该根据你的服务器CPU核心数和MySQL的最大连接数来设定。一个经验公式是maximum-pool-size (CPU核心数 * 2) 有效磁盘数。比如一台4核服务器maximum-pool-size可以设为10。日志级别Logging Levelapplication-dev.yml里logging.level.root: DEBUG方便你看到每一行SQL和每一个Bean的创建过程。但application-prod.yml里必须是logging.level.root: INFO否则一天就能产生几个G的日志文件把磁盘撑爆。更重要的是DEBUG级别会打印出所有HTTP请求的Body里面可能包含客户的身份证号、手机号等敏感信息这是严重的安全风险。Redis连接开发环境可能用的是本地localhost:6379密码为空。生产环境的Redis一定是集群模式有密码有连接超时时间。application-prod.yml里spring.redis.password、spring.redis.timeout、spring.redis.cluster.nodes这些字段一个都不能少而且必须经过严格测试。静态资源路径Static Resourcesapplication-dev.yml里spring.resources.static-locations可能是classpath:/static/,classpath:/public/意思是前端打包后的dist文件夹里的静态资源由Spring Boot的WebMvc自动提供。但在生产环境最佳实践是用Nginx来托管静态资源Spring Boot只提供API。这时application-prod.yml里应该把spring.resources.static-locations注释掉或者指向一个空目录避免Spring Boot和Nginx争抢资源。实操心得我吃过最大的亏是在application-prod.yml里忘了改spring.redis.host它还是localhost。结果上线后所有缓存失效系统响应时间从200ms飙升到2秒。排查了整整一天最后发现Redis客户端连的根本不是生产Redis而是连到了本地一个根本没启动的Redis服务上。所以上线前的 checklist 必须有一条“逐行核对application-prod.yml里的所有spring.*配置项确保没有遗漏”。4.4 常见问题速查表那些让我熬夜到凌晨三点的Bug问题现象可能原因排查步骤解决方案启动时报错Failed to configure a DataSourceapplication.yml里数据库配置缺失或格式错误1. 检查spring.datasource.url是否以jdbc:mysql://开头2. 检查username和password是否正确3. 检查MySQL服务是否已启动端口3306是否被占用修正配置确认MySQL服务状态检查防火墙登录后菜单栏一片空白sys_menu表里顶级菜单parent_id 0的visible字段为0隐藏1. 连接MySQL执行SELECT * FROM sys_menu WHERE parent_id 0;2. 查看visible字段是否为1手动执行UPDATE sys_menu SET visible 1 WHERE parent_id 0;点击“客户管理”报403 Forbidden当前用户的角色没有被分配“客户管理”菜单权限1. 用admin账号登录进入【角色管理】2. 编辑该用户的角色切换到【菜单权限】Tab3. 确认“客户管理”及其子菜单是否被勾选勾选对应菜单点击【提交】定时任务在后台页面显示“已启动”但从未执行quartz.sql未导入或application.yml里spring.quartz.job-store-type配置错误1. 检查ry库下是否有qrtz_*开头的11张表2. 检查application.yml里是否有spring.quartz.job-store-typejdbc导入quartz.sql确保配置正确前端页面显示“请求失败请检查网络”前端vue.config.js里的proxy代理配置与后端application.yml里的server.port不一致1. 查看vue.config.js里的devServer.proxy确认target地址如http://localhost:80802. 查看application.yml里的server.port是否为8080保持两者端口一致或修改proxy的target最后一个小技巧当你遇到一个全新的、文档里没写过的Bug时最快的解决办法不是百度而是看源码。比如你发现“字典管理”页面加载不出来F12看到一个404请求路径是/system/dict/data/list。那么你立刻去ruoyi-admin模块里搜索GetMapping(/system/dict/data/list)就能找到对应的Controller方法然后顺着它调用的Service、Mapper一层层往下跟90%的谜团都能解开。源码永远是你最可靠的朋友。本文还有配套的精品资源点击获取简介直接可用的汽车4S店后台管理项目基于Spring Boot后端和Vue前端分离开发适合毕业设计或中小规模4S店业务落地。系统涵盖用户、角色、部门、岗位、菜单、字典、参数等基础配置模块支持树形组织结构展示和数据级权限控制。内置通知公告、操作日志、登录日志、在线用户实时查看、定时任务调度带执行记录、缓存状态与服务运行监控等功能。提供可视化表单构建器可拖拽生成HTML表单配套代码生成器支持一键生成Java实体类、Mapper接口、Service逻辑、Controller层及对应Vue页面和建表SQL语句覆盖完整CRUD流程。包含MySQL初始化脚本ry_20210908.sql、quartz.sql、多环境配置文件dev/prod/staging、Windows.bat与Linux.sh启动脚本以及详细部署说明文档。项目采用标准模块划分含ruoyi-system、ruoyi-quartz、ruoyi-ui等子模块结构清晰便于二次开发和功能扩展。本文还有配套的精品资源点击获取
汽车4S店后台管理系统源码包:Spring Boot+Vue架构,含权限管理、代码生成与系统监控
发布时间:2026/6/6 15:00:24
本文还有配套的精品资源点击获取简介直接可用的汽车4S店后台管理项目基于Spring Boot后端和Vue前端分离开发适合毕业设计或中小规模4S店业务落地。系统涵盖用户、角色、部门、岗位、菜单、字典、参数等基础配置模块支持树形组织结构展示和数据级权限控制。内置通知公告、操作日志、登录日志、在线用户实时查看、定时任务调度带执行记录、缓存状态与服务运行监控等功能。提供可视化表单构建器可拖拽生成HTML表单配套代码生成器支持一键生成Java实体类、Mapper接口、Service逻辑、Controller层及对应Vue页面和建表SQL语句覆盖完整CRUD流程。包含MySQL初始化脚本ry_20210908.sql、quartz.sql、多环境配置文件dev/prod/staging、Windows.bat与Linux.sh启动脚本以及详细部署说明文档。项目采用标准模块划分含ruoyi-system、ruoyi-quartz、ruoyi-ui等子模块结构清晰便于二次开发和功能扩展。1. 项目概述这不是一个“模板”而是一套可直接跑通的4S店业务底盘我带过六届计算机专业毕业设计也给三家本地4S店做过轻量级系统升级。每次学生一说“想做个汽车管理系统”我就知道大概率会陷入两个坑要么前端页面花里胡哨但后端连客户档案增删都卡在权限校验上要么数据库表建了一堆结果登录模块调不通、日志查不到、定时任务根本没触发——最后答辩前一周还在改PreAuthorize注解里的SpEL表达式。这套源码包我去年在东风日产一家二级经销商落地时用过它不是那种“看起来很全、跑起来就报错”的教学Demo而是真正把4S店后台最常踩的坑提前焊死在架构里了。核心关键词你已经看到了4S店系统、Spring Boot、Vue、权限管理、代码生成器。但光看词容易误解——它不是“又一个若依RuoYi二次封装”而是以若依为基座做了大量面向汽车服务场景的深度适配。比如它的“部门树”不是简单展示行政层级而是按4S店真实组织结构预设了“销售部→销售顾问组→试驾专员”、“售后部→机电车间→钣金组→喷漆组”这样的多级嵌套它的“字典管理”里直接内置了“车辆品牌奔驰/宝马/奥迪/比亚迪/蔚来…”、“维修工单状态待派工/施工中/质检中/已结算/已开票”、“客户等级潜客/保有客户/流失预警客户”等业务字段它的“参数配置”模块甚至预留了“首保里程阈值”、“续保提醒天数”、“事故车定损时效上限”这类运营参数开关。这些不是后期加的补丁是骨架里就长出来的。它适合谁如果你是学生做毕业设计这套系统能让你在两周内搭出一个有模有样、能演示完整业务流比如新增客户→分配销售顾问→录入试驾记录→生成意向单→关联金融方案的后台答辩老师点开“操作日志”能看到每一步谁在什么时候干了什么点开“在线用户”能实时看到模拟的销售主管正在查看库存报表——这种真实感比写一百行“本系统采用B/S架构”强得多。如果你是小规模4S店的信息员或IT外包它能省掉你从零搭建基础框架的三个月时间你只需要把“ry_20210908.sql”导入MySQL改几处配置文件里的数据库地址和账号密码双击ry.bat就能启动一个带完整权限体系的后台然后立刻开始配置你们店自己的销售流程、维修工单字段、配件库存分类。它不承诺替代DMS经销商管理系统但它能快速补上那些DMS没覆盖到的毛细血管级需求比如内部培训通知、员工排班看板、客户回访计划跟踪。我特别想强调一点很多人看到“代码生成器”就以为是偷懒工具其实恰恰相反。它逼你必须先想清楚业务实体。比如你要做一个“事故车定损单”模块生成器不会替你判断“定损金额”该用BigDecimal还是Double也不会自动给你加上“保险公司核损通过”这个状态流转逻辑。但它会强制你打开可视化表单构建器拖拽出“车牌号输入框”、“出险日期日期选择器”、“初估损失数字输入框”、“关联维修工单下拉选择”这几个字段并为你生成对应的Java实体类、MyBatis Mapper XML里的resultMap映射、Service层的空方法骨架、Controller里的REST接口以及Vue页面里完整的Element UI表单组件和API调用逻辑。这个过程本质上是在帮你把模糊的业务想法翻译成可执行、可调试、可协作的代码契约。后面你再往里面填业务逻辑心里就有底了。2. 架构设计与核心模块拆解为什么选这个组合而不是其他方案2.1 后端选型Spring Boot不是为了“时髦”而是为了“少写样板代码”你可能会问为什么不用更轻量的Spring MVC或者更激进的Quarkus答案很实在稳定、生态成熟、团队熟悉度高、问题有迹可循。我见过太多学生用Spring MVC手写拦截器、自己拼接SQL、手动处理事务传播结果一个登录失败的日志都打不出来。Spring Boot的核心价值在于它把那些重复、枯燥、极易出错的“胶水代码”全部封装好了。自动配置Auto-Configuration比如你引入spring-boot-starter-data-jpa它就自动帮你配好数据源、事务管理器、JPA EntityManagerFactory你不用再翻《Spring实战》第7章去抄XML配置。在这个4S店系统里ruoyi-system模块的pom.xml里明确依赖了spring-boot-starter-web、spring-boot-starter-jdbc、mybatis-spring-boot-starter这意味着HTTP服务、数据库连接、ORM框架这三块基石开箱即用。起步依赖Starter Dependenciesspring-boot-starter-quartz让定时任务调度变得像写一个Scheduled注解一样简单。你看ruoyi-quartz模块它不只是简单调用Quartz API而是封装了一个可视化的“定时任务管理”页面你可以在这里新增、暂停、立即执行一个任务并且所有执行日志都自动记录到数据库的qrtz_job_log表里。这对4S店太实用了——比如每天凌晨2点自动同步一次厂家的配件价格表或者每周一上午9点给所有未回访的潜客发送短信提醒这些逻辑你只需要写一个Java方法然后在后台页面点几下就配好了。Actuator监控端点这是系统自带“健康检查”的心脏。/actuator/health返回服务整体状态/actuator/metrics告诉你JVM内存用了多少、HTTP请求平均耗时多少、数据库连接池是否紧张。我在部署到那家日产店的测试服务器上就靠/actuator/env端点一眼看出他们运维同事把spring.profiles.active配成了dev而不是prod导致日志级别是DEBUG磁盘空间三天就满了。没有Actuator这种问题得翻十几份日志文件才能定位。提示别小看application.yml里那一堆spring:开头的配置。比如spring.cache.typeredis这行决定了你的热点数据如菜单树、字典项是存在本地JVM内存里还是统一放在Redis里供集群节点共享。对于4S店这种可能有多个销售顾问同时查同一款车库存的场景用Redis缓存能避免数据库被高频查询压垮。2.2 前端选型Vue 2 Element UI是“够用”与“可控”的平衡点为什么不是Vue 3 Composition API也不是React因为交付效率和维护成本。Vue 2的Options API虽然不如Composition API灵活但它的生命周期钩子created,mounted、计算属性computed、侦听器watch概念极其清晰一个刚学完JavaScript基础的学生两天就能看懂ruoyi-ui/src/views/system/user/index.vue里是怎么发起用户列表请求、怎么渲染表格、怎么触发编辑弹窗的。Element UI则提供了开箱即用的、符合国内后台审美的一整套UI组件表格、表单、弹窗、树形控件、时间选择器……它们不是“好看就行”而是经过了海量企业级应用验证的稳定性。路由守卫Router Guard实现权限控制Vue Router的beforeEach全局前置守卫是整个前端权限体系的闸门。当你点击“客户管理”菜单时它不会直接跳转而是先去调用getInfo()接口拿到当前用户的菜单权限列表比如[system:user:list, system:user:add]然后动态addRoutes()只把用户有权限的路由注入到Vue Router实例中。这样即使有人手动在浏览器地址栏输入/user/edit/123只要他没这个system:user:edit权限页面就会空白或跳转到403页面。这个逻辑在ruoyi-ui/src/permission.js里写得明明白白。指令Directive封装复用逻辑比如v-permission[system:user:edit]这个自定义指令它背后就是一段简单的权限比对逻辑。你不需要在每个按钮的v-if里都写一遍$store.getters.roles.includes(system:user:edit)一行指令搞定。这种封装让前端代码干净得像教科书。Axios拦截器统一处理所有HTTP请求都经过src/utils/request.js里的拦截器。请求发出前自动带上AuthorizationBearer Token响应回来后如果状态码是401未登录就自动跳转到登录页如果是500就统一弹出错误提示。你在写一个“生成维修报价单”的接口时完全不用操心Token过期怎么办、网络超时怎么提示这些脏活累活框架已经替你干了。2.3 权限模型RBACABAC混合解决4S店最头疼的“数据隔离”问题很多系统只讲“角色能访问什么菜单”这在4S店是远远不够的。销售顾问A只能看自己名下的客户不能看隔壁顾问B的客户售后经理只能审批自己车间的工单不能批别的车间的。这就需要数据权限DataScope而不仅仅是菜单权限MenuPermission。这套系统采用的是RBAC基于角色的访问控制 ABAC基于属性的访问控制的混合模型-RBAC部分sys_user用户、sys_role角色、sys_menu菜单、sys_role_menu角色-菜单关联这四张表构成了经典的权限骨架。你给“销售总监”角色分配了“客户管理”菜单那么所有拥有这个角色的用户都能看到客户管理的入口。-ABAC部分数据权限关键在sys_role表里的data_scope字段它有五个取值1.1全部数据权限如超级管理员2.2自定义数据权限可手动勾选部门3.3本部门数据权限销售部的人只能看销售部的数据4.4本部门及以下数据权限销售总监能看到销售部所有销售顾问组的数据5.5仅本人数据权限销售顾问只能看自己创建的客户这个逻辑在后端SysRoleServiceImpl.java的selectDeptListByRoleId()方法里实现。当一个销售顾问角色data_scope5去查客户列表时MyBatis的XML里会自动拼上AND create_by #{userId}这个条件当他data_scope4时SQL里就会变成AND dept_id IN (SELECT dept_id FROM sys_dept WHERE ancestors LIKE CONCAT((SELECT ancestors FROM sys_dept WHERE dept_id #{deptId}), %))也就是查他所在部门及其所有子部门的数据。这种动态SQL拼接是通过MyBatis的if标签和DataScope注解配合完成的你几乎感觉不到它的存在但它无处不在。注意数据权限的生效依赖于你在业务代码里正确使用DataScope。比如在SysUserController.java的list()方法上必须加上DataScope(deptAlias u, userAlias u)框架才知道要给u这个表别名加数据权限过滤。漏掉这个注解数据权限就形同虚设。3. 核心功能实操详解从启动到定制手把手带你跑通第一个业务模块3.1 环境准备与一键启动Windows和Linux的差异在哪别被一堆.bat和.sh脚本吓住它们本质都是“一键启动”的包装纸。核心逻辑就三步编译后端、安装前端依赖、启动前后端服务。Windows环境ry.batecho off echo 正在启动若依后台服务... cd /d %~dp0ruoyi-admin call mvn clean package -Dmaven.test.skiptrue if %errorlevel% neq 0 goto error echo 正在启动若依前端服务... cd /d %~dp0ruoyi-ui call npm install if %errorlevel% neq 0 goto error start cmd /k npm run dev echo 后台服务启动中... cd /d %~dp0ruoyi-admin java -jar target/ruoyi-admin.jar --spring.profiles.activedev goto end :error echo 构建或启动失败请检查日志 pause :end这段脚本的关键点在于-cd /d %~dp0ruoyi-admin%~dp0是批处理的魔法变量代表当前.bat文件所在的目录。ruoyi-admin是后端模块的目录名确保路径正确。-mvn clean package -Dmaven.test.skiptrue跳过单元测试加快打包速度。对于毕业设计测试覆盖率不是首要目标。-start cmd /k npm run dev/k参数保证命令窗口不关闭这样你就能看到前端npm run dev的实时日志比如Webpack编译进度、热更新提示。-java -jar target/ruoyi-admin.jar --spring.profiles.activedev这才是真正的后端启动命令。--spring.profiles.activedev指定了激活application-dev.yml配置文件里面包含了开发环境的数据库地址、Redis地址等。Linux环境ry.sh#!/bin/bash echo 正在启动若依后台服务... cd $(dirname $0)/ruoyi-admin mvn clean package -Dmaven.test.skiptrue if [ $? -ne 0 ]; then echo 后端构建失败 exit 1 fi echo 正在启动若依前端服务... cd $(dirname $0)/ruoyi-ui npm install if [ $? -ne 0 ]; then echo 前端依赖安装失败 exit 1 fi nohup npm run dev frontend.log 21 echo 前端服务已后台启动日志见 frontend.log echo 后台服务启动中... cd $(dirname $0)/ruoyi-admin nohup java -jar target/ruoyi-admin.jar --spring.profiles.activedev backend.log 21 echo 后台服务已后台启动日志见 backend.logLinux脚本的核心差异是nohup和它们让服务在终端关闭后依然运行。nohup忽略挂起信号SIGHUP让进程在后台运行。日志重定向 backend.log 21把标准输出和标准错误都写入backend.log文件方便排查问题。如果你在云服务器上部署这就是生产环境的标准姿势。实操心得第一次启动失败90%的原因是数据库没配好。打开ruoyi-admin/src/main/resources/application-dev.yml找到spring: datasource:部分把url、username、password改成你本地MySQL的真实信息。比如我的MySQL root密码是123456那么password: 123456。千万别用默认的root和空密码除非你确定MySQL允许。3.2 数据库初始化ry_20210908.sql和quartz.sql分别管什么项目根目录下的两个SQL文件分工非常明确ry_20210908.sql这是主业务数据库的初始化脚本。它创建了ry这个数据库并在里面建了几十张表包括sys_user用户信息账号、昵称、头像、状态sys_role角色信息角色名、描述、数据权限范围sys_menu菜单信息菜单名、路径、图标、排序号、是否显示sys_dept部门信息部门名、父部门ID、负责人、排序号并用ancestors字段实现了高效的树形查询比如查“销售部”及其所有子部门只需WHERE ancestors LIKE 0,100,sys_dict_typesys_dict_data字典类型和字典数据比如“车辆品牌”是一个类型“奔驰”、“宝马”是它的数据项sys_config系统参数比如“是否开启注册功能”、“邮件服务器地址”这个脚本里还插入了初始数据一个admin/admin123的超级管理员账号一个common/common123的普通用户账号以及“系统管理”、“监控管理”、“工具管理”等顶级菜单。你导入后直接用admin/admin123登录就能看到一个功能完整的后台。quartz.sql这是Quartz定时任务调度框架的专用数据库表。Quartz本身不依赖数据库但为了实现集群、持久化任务状态、记录执行日志就必须用数据库来存储。这个脚本创建了qrtz_job_details任务详情、qrtz_triggers触发器、qrtz_fired_triggers已触发的任务、qrtz_job_log执行日志等11张表。ruoyi-quartz模块的所有功能都建立在这套表结构之上。如果你不导入它那么“定时任务管理”页面能打开但你新建的任务永远不会被执行因为Quartz找不到地方存它的状态。提示导入顺序很重要必须先执行ry_20210908.sql创建ry库再执行quartz.sql。因为quartz.sql里的建表语句默认是针对ry库的。如果你先执行quartz.sql它会报错“Database ‘ry’ does not exist”。3.3 权限管理实战如何为“销售顾问”角色配置“仅看自己客户”的权限这是4S店最典型的权限需求。我们来走一遍完整流程从后台配置到代码验证。第一步创建角色1. 用admin/admin123登录系统进入【系统管理】-【角色管理】。2. 点击【新增】填写角色名称为“销售顾问”角色权限字符串填sales_advisor这个字符串是后端代码里用来识别角色的可以自定义但要唯一。3. 在【数据权限】下拉框中选择“仅本人数据权限”。这一步至关重要它设置了sys_role.data_scope 5。第二步分配菜单权限1. 在角色编辑页面切换到【菜单权限】Tab。2. 展开左侧菜单树勾选【客户管理】下的所有子菜单客户列表、客户新增、客户编辑、客户删除。注意这里只是给了他“看到这个菜单并点击进去”的权利具体能看到哪些客户数据由上一步的“数据权限”决定。第三步创建用户并绑定角色1. 进入【系统管理】-【用户管理】点击【新增】。2. 填写用户名zhangsan昵称张三手机号13800138000邮箱zhangsan4s.com。3. 在【角色】区域勾选刚才创建的“销售顾问”角色。4. 点击【提交】。第四步验证效果1. 退出管理员账号用zhangsan/123456新用户默认密码是123456登录。2. 点击【客户管理】-【客户列表】你会看到一个空表格或者只有你自己刚刚创建的客户如果你在创建用户后又用这个账号创建了一个客户。3. 打开浏览器开发者工具F12切换到Network标签页刷新客户列表页面。找到/user/list这个请求点开它看Response。你会发现返回的JSON数据里rows数组是空的或者只有一条记录。4. 此时后端SysUserController.list()方法被调用由于该用户所属角色的data_scope5MyBatis的SQL会自动加上AND create_by zhangsancreate_by是sys_user表里的用户名字段。这就是数据权限在起作用。注意事项数据权限的字段名这里是create_by是写死在SysUserMapper.xml里的。如果你的业务表里记录创建人不是用create_by而是用creator_id或者owner_name那么你必须修改SysUserMapper.xml里对应的SQL片段否则权限会失效。这是一个常见的二次开发点。3.4 代码生成器如何为“维修工单”模块生成一套CRUD这是提升开发效率的核武器。我们以“维修工单repair_order”为例演示从零开始生成一个完整模块。第一步设计数据库表在MySQL客户端如Navicat或DBeaver里连接到ry数据库执行以下建表语句CREATE TABLE repair_order ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, order_no varchar(50) NOT NULL COMMENT 工单编号, car_vin varchar(17) NOT NULL COMMENT 车辆VIN码, customer_name varchar(50) NOT NULL COMMENT 客户姓名, phone varchar(20) DEFAULT NULL COMMENT 联系电话, repair_type varchar(20) DEFAULT NULL COMMENT 维修类型保养/故障维修/事故维修, status varchar(20) DEFAULT pending COMMENT 工单状态pending待派工/processing施工中/done已完成, create_by varchar(64) DEFAULT COMMENT 创建者, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_by varchar(64) DEFAULT COMMENT 更新者, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修工单表;第二步使用可视化表单构建器1. 登录系统进入【工具管理】-【表单构建】。2. 点击【新建表单】表单名称填“维修工单”表单编码填repairOrderForm。3. 在右侧画布区拖拽组件- 一个“文本框”设置字段名order_no标签为“工单编号”并勾选“必填”、“唯一校验”。- 一个“文本框”字段名car_vin标签为“车辆VIN码”勾选“长度限制17”。- 一个“下拉选择框”字段名repair_type标签为“维修类型”选项来源选择“字典数据”字典类型填repair_type你需要先在【系统管理】-【字典管理】里创建这个字典。- 一个“单选框”字段名status标签为“工单状态”选项为pending|待派工,processing|施工中,done|已完成。4. 点击【保存】系统会生成一个HTML表单代码你可以复制它粘贴到任何地方使用。第三步使用代码生成器1. 进入【工具管理】-【代码生成】。2. 点击【查询】在表名搜索框里输入repair_order点击搜索你会看到这张表出现在列表里。3. 点击右侧的【编辑】填写- 业务名称维修工单- 模块名称repair- 功能作者yourname- 生成模板crud这是默认的生成完整CRUD- 表前缀repair_去掉表名前缀生成的Java类名就是RepairOrder- 包路径com.ruoyi.repair这是后端Java代码的包名- 前端路径repair/order这是Vue页面的路由路径4. 点击【生成代码】系统会打包下载一个ZIP文件。第四步导入生成的代码1. 解压ZIP你会看到src/main/java/com/ruoyi/repair/后端Java代码和src/views/repair/order/前端Vue代码两个文件夹。2. 将java文件夹下的内容复制粘贴到你本地项目的ruoyi-admin/src/main/java/com/ruoyi/repair/目录下。3. 将views文件夹下的内容复制粘贴到ruoyi-ui/src/views/repair/order/目录下。4. 修改ruoyi-admin/pom.xml确保dependencies里有ruoyi-common的依赖通常已有。5. 修改ruoyi-ui/src/router/index.js在routes数组里添加一条新的路由js { path: /repair/order, component: () import(/views/repair/order/index), name: RepairOrder, meta: { title: 维修工单, icon: form } }6. 重启前后端服务。现在你就可以在菜单里看到【维修工单】了点击进去就是一个功能完备的CRUD页面列表、新增、编辑、删除、搜索全部都有。实操心得生成的代码不是终点而是起点。比如你可能需要在“新增工单”时自动填充当前登录用户的姓名到create_by字段。这需要修改生成的RepairOrderController.java里的addSave()方法在repairOrder.setCreateBy(getUsername());这一行。再比如前端列表页的“状态”列你想显示中文而不是英文pending-待派工就需要在index.vue的columns配置里把status字段的formatter指向一个字典翻译函数。这些微调才是体现你业务理解的地方。4. 高级功能与避坑指南监控、日志、部署中的真实陷阱4.1 系统监控/actuator端点不是摆设是你的“系统仪表盘”Spring Boot Actuator暴露了一系列生产就绪的端点它们是诊断系统健康状况的第一道防线。不要只把它当成一个“能访问就行”的功能要真正用起来。/actuator/health这是最常用的。返回一个JSONstatus字段是UP表示一切正常DOWN表示有问题。但它背后有玄机details里会列出各个组件的健康状态。比如json { status: UP, details: { diskSpace: {status: UP, details: {total: 500000000000, free: 200000000000}}, redis: {status: UP, details: {version: 6.2.6}}, db: {status: UP, details: {database: MySQL, validationQuery: isValid()}} } }如果redis状态是DOWN说明Redis连接不上那么所有依赖Redis缓存的功能如菜单树、字典项都会变慢或失效。这时候你应该立刻检查application.yml里的spring.redis.host和port是否正确。/actuator/metrics这是性能分析的宝库。它返回所有可用的指标名称比如jvm.memory.usedJVM已用内存、http.server.requestsHTTP请求数、datasource.hikaricp.connections.active活跃数据库连接数。你可以用/actuator/metrics/{name}来获取具体指标的值。例如/actuator/metrics/jvm.memory.used会返回json { name: jvm.memory.used, measurements: [ { statistic: VALUE, value: 256000000 } ], availableTags: [ { tag: area, values: [heap, nonheap] } ] }如果jvm.memory.used的值持续飙升接近你设置的-Xmx最大堆内存那就说明有内存泄漏需要做Heap Dump分析。/actuator/loggers这是日志级别的动态开关。你可以用POST请求临时把某个包的日志级别调成DEBUG而不用重启服务。比如你想查“为什么定时任务没执行”可以向/actuator/loggers/org.quartz发送一个JSONjson {configuredLevel: DEBUG}然后去看backend.log就能看到Quartz内部详细的调度日志了。排查完问题再把它调回INFO避免日志爆炸。提示Actuator端点默认只开放了health和info。要启用metrics、loggers等必须在application.yml里显式配置yaml management: endpoints: web: exposure: include: health,info,metrics,loggers,env,beans,threaddump4.2 日志追踪操作日志、登录日志、定时任务日志它们都存在哪日志是系统的“黑匣子”出了问题它是唯一的线索。这套系统把三类关键日志都落到了数据库里而不是仅仅写在文件里这极大地方便了查询和审计。操作日志sys_oper_log表记录用户每一次“有意义”的操作。比如点击【客户管理】-【新增客户】按钮提交表单后端SysUserController.addSave()方法执行成功就会在sys_oper_log里插入一条记录title: “客户管理”business_type: “1” 1新增2编辑3删除4导出method: “com.ruoyi.web.controller.system.SysUserController.addSave”request_method: “POST”operator_type: “1” 1后台用户2手机APP用户oper_name: “admin”dept_name: “研发部”oper_url: “/system/user/add”oper_ip: “127.0.0.1”oper_location: “内网IP”oper_param: “{“userName”:”test”, “nickName”:”测试”}” 请求参数的JSON字符串status: “0” 0成功1失败error_msg: “” 失败时的错误信息oper_time: “2023-10-01 10:00:00”这张表的设计非常聪明oper_param字段是TEXT类型可以存很长的JSONstatus和error_msg字段让你一眼就能区分成功和失败的操作。在【系统监控】-【操作日志】页面你可以按用户名、操作模块、时间段、状态来筛选找出所有失败的操作然后根据error_msg去定位Bug。登录日志sys_logininfor表专门记录登录行为字段更精简login_name: 用户名ipaddr: IP地址login_location: 地理位置通过IP库解析browser: 浏览器类型os: 操作系统status: “0”成功或“1”失败msg: “登录成功” 或 “密码错误”login_time: 登录时间这张表是安全审计的核心。你可以用它来发现异常登录比如同一个账号在一分钟内从北京和广州两个IP同时登录那基本可以判定账号被盗了。定时任务日志qrtz_job_log表这是quartz.sql脚本创建的表记录Quartz任务的每一次执行job_name: 任务名如sysTaskjob_group: 任务组如DEFAULTinvoke_target: 调用的目标方法如com.ruoyi.quartz.task.SysTask.syncData()job_message: 执行结果消息如“同步成功共处理12条数据”status: “0”成功或“1”失败exception_info: 失败时的完整堆栈信息create_time: 创建时间当你发现某个定时任务“应该每天执行但今天没执行”时第一件事就是查这张表。如果表里根本没有今天的记录说明Quartz调度器根本没触发这个任务问题出在调度配置或Quartz集群状态上如果表里有记录但status1那就直接看exception_info99%的问题都能在这里找到答案。注意日志表的数据会越积越多必须定期清理。系统自带了一个定时任务【系统监控】-【定时任务】里的“清除系统日志”你可以把它配置成每天凌晨执行自动删除30天前的操作日志和登录日志。别忘了给qrtz_job_log也配一个类似的清理任务否则几个月后这张表会达到百万级记录查询会变得非常慢。4.3 部署上线从dev到prod配置文件切换的致命细节开发环境dev和生产环境prod的配置绝不仅仅是数据库地址不同。一个疏忽就可能导致线上事故。数据库连接池HikariCPapplication-dev.yml里spring.datasource.hikari.maximum-pool-size可能是20这在开发机上绰绰有余。但在生产环境面对几十个销售顾问同时刷客户列表20的连接池可能瞬间被打满导致后续所有请求排队等待系统假死。application-prod.yml里这个值应该根据你的服务器CPU核心数和MySQL的最大连接数来设定。一个经验公式是maximum-pool-size (CPU核心数 * 2) 有效磁盘数。比如一台4核服务器maximum-pool-size可以设为10。日志级别Logging Levelapplication-dev.yml里logging.level.root: DEBUG方便你看到每一行SQL和每一个Bean的创建过程。但application-prod.yml里必须是logging.level.root: INFO否则一天就能产生几个G的日志文件把磁盘撑爆。更重要的是DEBUG级别会打印出所有HTTP请求的Body里面可能包含客户的身份证号、手机号等敏感信息这是严重的安全风险。Redis连接开发环境可能用的是本地localhost:6379密码为空。生产环境的Redis一定是集群模式有密码有连接超时时间。application-prod.yml里spring.redis.password、spring.redis.timeout、spring.redis.cluster.nodes这些字段一个都不能少而且必须经过严格测试。静态资源路径Static Resourcesapplication-dev.yml里spring.resources.static-locations可能是classpath:/static/,classpath:/public/意思是前端打包后的dist文件夹里的静态资源由Spring Boot的WebMvc自动提供。但在生产环境最佳实践是用Nginx来托管静态资源Spring Boot只提供API。这时application-prod.yml里应该把spring.resources.static-locations注释掉或者指向一个空目录避免Spring Boot和Nginx争抢资源。实操心得我吃过最大的亏是在application-prod.yml里忘了改spring.redis.host它还是localhost。结果上线后所有缓存失效系统响应时间从200ms飙升到2秒。排查了整整一天最后发现Redis客户端连的根本不是生产Redis而是连到了本地一个根本没启动的Redis服务上。所以上线前的 checklist 必须有一条“逐行核对application-prod.yml里的所有spring.*配置项确保没有遗漏”。4.4 常见问题速查表那些让我熬夜到凌晨三点的Bug问题现象可能原因排查步骤解决方案启动时报错Failed to configure a DataSourceapplication.yml里数据库配置缺失或格式错误1. 检查spring.datasource.url是否以jdbc:mysql://开头2. 检查username和password是否正确3. 检查MySQL服务是否已启动端口3306是否被占用修正配置确认MySQL服务状态检查防火墙登录后菜单栏一片空白sys_menu表里顶级菜单parent_id 0的visible字段为0隐藏1. 连接MySQL执行SELECT * FROM sys_menu WHERE parent_id 0;2. 查看visible字段是否为1手动执行UPDATE sys_menu SET visible 1 WHERE parent_id 0;点击“客户管理”报403 Forbidden当前用户的角色没有被分配“客户管理”菜单权限1. 用admin账号登录进入【角色管理】2. 编辑该用户的角色切换到【菜单权限】Tab3. 确认“客户管理”及其子菜单是否被勾选勾选对应菜单点击【提交】定时任务在后台页面显示“已启动”但从未执行quartz.sql未导入或application.yml里spring.quartz.job-store-type配置错误1. 检查ry库下是否有qrtz_*开头的11张表2. 检查application.yml里是否有spring.quartz.job-store-typejdbc导入quartz.sql确保配置正确前端页面显示“请求失败请检查网络”前端vue.config.js里的proxy代理配置与后端application.yml里的server.port不一致1. 查看vue.config.js里的devServer.proxy确认target地址如http://localhost:80802. 查看application.yml里的server.port是否为8080保持两者端口一致或修改proxy的target最后一个小技巧当你遇到一个全新的、文档里没写过的Bug时最快的解决办法不是百度而是看源码。比如你发现“字典管理”页面加载不出来F12看到一个404请求路径是/system/dict/data/list。那么你立刻去ruoyi-admin模块里搜索GetMapping(/system/dict/data/list)就能找到对应的Controller方法然后顺着它调用的Service、Mapper一层层往下跟90%的谜团都能解开。源码永远是你最可靠的朋友。本文还有配套的精品资源点击获取简介直接可用的汽车4S店后台管理项目基于Spring Boot后端和Vue前端分离开发适合毕业设计或中小规模4S店业务落地。系统涵盖用户、角色、部门、岗位、菜单、字典、参数等基础配置模块支持树形组织结构展示和数据级权限控制。内置通知公告、操作日志、登录日志、在线用户实时查看、定时任务调度带执行记录、缓存状态与服务运行监控等功能。提供可视化表单构建器可拖拽生成HTML表单配套代码生成器支持一键生成Java实体类、Mapper接口、Service逻辑、Controller层及对应Vue页面和建表SQL语句覆盖完整CRUD流程。包含MySQL初始化脚本ry_20210908.sql、quartz.sql、多环境配置文件dev/prod/staging、Windows.bat与Linux.sh启动脚本以及详细部署说明文档。项目采用标准模块划分含ruoyi-system、ruoyi-quartz、ruoyi-ui等子模块结构清晰便于二次开发和功能扩展。本文还有配套的精品资源点击获取