二手车交易系统这种项目,我在实战里接过不止一次。说实话,这类“信息管理系统”乍看像是典型的课程设计或毕业设计选题,但真正落地过就会发现,里面涉及的权限模型、商品状态流转、订单与车辆档案的关联设计,甚至是一张图片上传的存储策略,都能直接决定这个系统能不能从“能跑”进化到“能商用”。这篇就结合我手头一个基于SpringBoot + Vue + MySQL的完整可运行项目,把从设计思路到部署细节的整个链路拆开讲清楚,包括那些只有真正写过才会注意到的坑。
1. 项目到底做了什么:一个二手车交易系统的完整模块拆解
拿到“二手车交易系统信息管理系统”这个标题,先别急着看代码。我习惯先把业务方(或者老师、或者你自己)嘴里的需求翻译成系统功能清单。二手车交易和普通电商最大的区别在于:商品非标、单价高、交易链路长、角色多。一辆车有车况、里程、排放标准、过户状态、保险记录这些强专业属性;交易过程涉及卖家、买家、平台管理员甚至车商多个角色。所以这个系统的核心不是“卖东西”,而是“管信息、控状态、留记录”。
1.1 核心需求解析:从业务场景反推系统模块
正常一个可交付的二手车交易系统,至少要拆出这几大块:
- 用户中心:注册、登录、个人信息维护、密码修改。这里要注意,角色必须区分(普通用户/管理员),因为权限不同,看到的菜单和能操作的功能完全不同。
- 车辆管理:卖家发布车源,管理员审核车源,车辆信息维护(品牌、车型、上牌时间、行驶里程、排量、变速箱、排放标准、价格、车况描述、图片),以及车辆状态的流转(待审核->在售->已下架->已售出)。
- 车辆搜索与浏览:前台按品牌、价格区间、里程、排量等条件筛选车辆,列表展示,详情页查看完整车况信息。
- 预约看车/留言咨询:买家对意向车辆发起看车预约,卖家或管理员能收到预约记录。
- 交易订单管理:生成订单、订单状态流转(待支付/已支付/交易完成/已取消),这里不强制要求真的接入支付网关(毕竟是管理系统,不是电商平台),但订单数据结构必须完整。
- 后台管理系统:用户管理(禁用/启用)、车辆审核(通过/驳回)、订单查看、数据统计(车辆总数、在售数量、成交数量、用户数量)。
- 系统管理:管理员账号管理、菜单权限配置(这个看项目复杂程度,简单版可以直接用角色字段控制,复杂版走RBAC表)。
我见过不少半成品项目,最典型的问题就是车辆模块只做了简单的增删改查,没有状态机概念,导致“审核中”的车也能在前台被搜到,这就是需求分析阶段的疏漏。
1.2 技术选型的理由:为什么偏偏是SpringBoot + Vue + MySQL
这套组合现在几乎是Java全栈项目的事实标准,不是因为跟风,是每个环节都经得起推敲:
- SpringBoot:解决了Spring配置地狱的问题。内嵌Tomcat,java -jar一条命令就能起服务,适合快速交付。对二手交易这种业务逻辑中等复杂度的系统来说,Spring MVC做接口层、MyBatis-Plus做持久层,效率极高。
- Vue:前后端分离的开发模式,前端侧重交互和展示,Element UI或者Element Plus能快速搭出管理后台的表格、表单、弹窗。响应式数据绑定让筛选联动、购物车式操作(这里引申为预约/下单)开发体验比JQuery时代好太多。
- MySQL:关系型数据库,事务支持成熟,价格敏感、车辆信息这类强结构化数据用MySQL再合适不过。而且社区活跃,出问题一搜就有答案。
从成本角度看,三件套全是开源免费,部署要求低(一台2核4G的服务器跑得稳稳的),对学生、小团队、个人开发者都非常友好。缺点的线:MySQL在超级大数据量下需要分库分表,但一个二手车交易系统远没到那个量级,过度设计纯属浪费。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从零跑起来:数据库设计与后端核心实现
很多朋友拿到源码第一步就双击运行,报错了就懵。我建议反过来,先看数据库脚本,再看配置,最后启动。数据库是整套系统的地基,地基歪了,上面代码再对也白搭。
2.1 数据库表结构设计:字段和类型的经验之谈
一个合格的二手车交易系统,数据库至少要有这几张核心表(这里列主要字段,省略公共字段):
| 表名 | 核心字段 | 设计要点 |
|---|---|---|
| sys_user | id, username, password, nickname, phone, role, status | 密码必须加密存储(BCrypt/MD5+盐),role字段区分admin/user |
| car_info | id, user_id, brand, model, license_time, mileage, gearbox, displacement, price, description, cover_image, images, status, audit_status | status区分在售/下架/已售,audit_status区分待审核/通过/驳回 |
| car_order | id, order_no, car_id, buyer_id, seller_id, amount, status, create_time | 订单号用时间戳+随机数生成,拒绝自增ID直接暴露 |
| appointment | id, car_id, user_id, appoint_time, phone, remark, status | 看车预约,状态字段区分待确认/已完成/已取消 |
| car_brand | id, brand_name, brand_logo | 品牌表可做数据字典,方便前台筛选下拉框 |
建表时几个容易踩的坑,我必须重点提一下:
- 金额字段用decimal(10,2),千万别用float/double,否则价格计算会出现精度丢失,比如19.99存进去变成19.989999。
- 时间字段建议用datetime,不要用timestamp。timestamp有2038年问题,虽然看着遥远,但datetime还能存'0000-00-00 00:00:00'这类特殊值,容错更强。
- 图片字段存路径,不存Base64。很多人图省事把图片转Base64直接塞数据库,列表页直接卡死。正确做法是存相对路径,文件本身放本地磁盘或OSS,数据库只存URL。
- 所有表加create_time、update_time、deleted字段。deleted做逻辑删除,避免物理删除导致关联数据断裂。比如一辆车被删了,订单表里还引用着它,物理删除直接毁掉数据完整性。
2.2 后端接口设计:SpringBoot分层架构的核心要点
后端我习惯分五层:Controller(接收请求)、Service(业务逻辑)、Mapper(数据库操作)、Entity(实体类)、DTO/VO(数据传输对象)。这里有个关键点:Entity不要直接返回给前端,因为实体类可能包含密码、数据库备注字段等敏感或冗余信息。用VO封装后返回,既安全又干净。
以车辆发布接口为例,流程是这样的:
- 前端POST请求携带车辆表单数据(品牌、车型、价格、图片等),请求头带token。
- Controller层接收参数,用@Validated做参数校验(价格不能为负、里程不能为负数等)。
- Service层设置初始状态:audit_status=0(待审核),status=0(未上架)。
- Mapper层执行insert,返回主键ID。
- 异步通知管理员(这里简化处理,实际可以存消息表,管理员登录后看到待办)。
鉴权这块,强烈建议用JWT而不是Session。前后端分离后,Session跨域要配一堆CORS策略,而JWT无状态,服务端只要校验签名即可。流程是:用户登录成功后,后端生成token(包含用户ID和角色),前端存localStorage,每次请求放到header的Authorization字段,后端用拦截器统一校验。
核心代码片段(自定义拦截器校验JWT):
java复制public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || token.isEmpty()) {
throw new BusinessException(401, "未登录或登录已过期");
}
// 解析token,验证签名,取用户信息放入ThreadLocal
Claims claims = JwtUtil.parseToken(token.replace("Bearer ", ""));
UserContext.setUserId(claims.get("userId", Integer.class));
UserContext.setRole(claims.get("role", String.class));
return true;
}
}
拦截器里有个细节:白名单配置。登录接口、注册接口、前台车辆列表接口这些不需要鉴权,要放行;后台管理接口必须校验管理员角色。这块配置错一个,就会出现“明明登录了却提示未授权”或者“没登录也能访问后台”这两种极端bug,实测中我两种都踩过。
2.3 MyBatis-Plus的运用:CRUD可以偷懒但多表查询不能含糊
MyBatis-Plus最香的就是内置BaseMapper,单表增删改查不用写一行SQL。比如用户列表分页查询,直接用LambdaQueryWrapper拼条件:
java复制Page<SysUser> page = new Page<>(current, size);
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(username), SysUser::getUsername, username)
.eq(StringUtils.isNotBlank(role), SysUser::getRole, role)
.orderByDesc(SysUser::getCreateTime);
sysUserMapper.selectPage(page, wrapper);
但遇到车辆列表带品牌名、卖家昵称这种跨表查询,就不要勉强用Wrapper了。老老实实写XML里的自定义SQL,用LEFT JOIN关联,否则要么N+1问题严重,要么SQL拼接极其痛苦。车辆列表查询的实际SQL大概是:
sql复制SELECT ci.*, cb.brand_name, su.nickname AS seller_nickname
FROM car_info ci
LEFT JOIN car_brand cb ON ci.brand_id = cb.id
LEFT JOIN sys_user su ON ci.user_id = su.id
WHERE ci.deleted = 0
AND ci.audit_status = 1
AND ci.status = 1
AND (ci.price BETWEEN #{minPrice} AND #{maxPrice} OR #{minPrice} IS NULL)
ORDER BY ci.create_time DESC
LIMIT #{offset}, #{pageSize}
注意WHERE条件里那个价格区间写法:如果前端没传minPrice,那条件就自动失效,避免了拼接SQL的繁琐。
3. 前端Vue项目的搭建与关键业务实现
前端这块,技术栈以Vue2 + Element UI居多(Vue3 + Element Plus也完全可行,写法上略有差异)。核心页面就那几个:首页(车辆列表)、车辆详情、发布车辆、个人中心、后台管理(用户/车辆/订单管理)。
3.1 环境准备与初始化:版本匹配是第一道门槛
Vue项目最烦的就是环境版本不一致。Node版本太高,老项目直接报openssl错误;npm版本太高,依赖树解析不了。我实测下来的稳定组合是:
- Node.js 14.x 或 16.x
- npm 6.x 或 8.x
- Vue CLI 4.x 或 5.x
- Element UI 2.15.x
初始化项目不用从零手写webpack配置,直接用Vue CLI脚手架:
bash复制vue create car-frontend
npm install element-ui axios vue-router vuex --save
安装依赖时如果卡在node-sass,大概率是Node版本和node-sass版本对不上。现在的解决方案是换成dart-sass(sass),兼容性更好,安装速度也更快。
axios需要做统一封装,核心是请求/响应拦截器:
javascript复制// request.js
import axios from 'axios'
import { Message } from 'element-ui'
import router from '@/router'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 通过环境变量控制
timeout: 10000
})
// 请求拦截器:自动携带token
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')
}
Message.error(error.message || '网络异常')
return Promise.reject(error)
}
)
这个封装有两个好处:一是全项目不用重复写token处理逻辑,二是后端返回code非200时前端统一弹错误提示,不用每个页面都写try-catch。
3.2 路由与权限控制:动态菜单和页面守卫的套路
Vue Router做权限控制,分静态路由和动态路由。静态路由放登录页、注册页、首页;动态路由根据用户角色从后端获取菜单权限,再router.addRoutes动态添加。
javascript复制// permission.js 路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
if (!token && to.path !== '/login') {
next('/login')
return
}
const role = localStorage.getItem('role')
if (token && to.meta.roles && !to.meta.roles.includes(role)) {
next('/403')
return
}
next()
})
这里有个细节容易忽略:动态添加路由后,刷新页面时路由会丢失(因为addRoutes是内存操作)。所以刷新后要重新请求用户信息、重新动态添加路由。我一般把获取用户信息和生成动态路由的逻辑放在main.js的初始化函数里,用async/await保证路由注入完成后再挂载App。
车辆列表页面是最能体现Vue价值的地方。筛选条件(品牌、价格区间、里程、排量)联动表格数据,监听表单变化重新请求接口:
javascript复制watch: {
filterForm: {
handler() {
this.getCarList()
},
deep: true
}
}
注意这里用了deep: true,因为filterForm是对象,对象内部属性变化时普通watch不会触发。这是个非常常见的坑。
3.3 核心业务页面:发布车源与订单状态流转的实现细节
发布车辆页面(卖家端)是整个系统表单最复杂的一个。字段多、校验多,还有图片上传。Element UI的Upload组件 + 后端文件接口的配合要小心:
- 前端选择图片后,先调用后端 /file/upload 接口,把图片上传到服务器。
- 后端返回图片的URL(比如 /uploads/2024/01/xxx.jpg)。
- 前端把URL存到表单的images字段里,随车辆信息一起提交。
这么做的好处是:如果用户填到一半放弃发布,图片文件已经上传了但不会造成车辆数据脏数据(大不了定时清理临时文件)。如果反过来先提交车辆信息再传图片,就得处理“车辆已创建但图片还没传完”的中间状态,麻烦得多。
订单状态流转是交易系统的心脏。我的设计是状态机模式,明确每个状态允许转移到哪个状态:
| 当前状态 | 允许流转到 | 触发条件 |
|---|---|---|
| 待支付 | 已支付 / 已取消 | 买家支付 / 买家取消 |
| 已支付 | 交易完成 / 已取消 | 双方确认过户完成 / 卖家取消(需申请) |
| 交易完成 | 终态 | 无 |
| 已取消 | 终态 | 无 |
前端页面会根据订单当前状态,动态渲染可操作按钮(比如待支付状态显示“去支付”和“取消订单”,已支付状态显示“确认完成”)。这个逻辑用v-if控制,千万别在模板里写一堆复杂的计算表达式,提出来做成computed属性,清晰又好测试。
4. 本地快速跑通与部署上线:从源码到可访问的全流程
“可直接运行”这四个字是卖点,也是很多项目的雷点。我接过不少说是“直接跑”的代码,实际上缺配置、缺依赖、缺步骤说明。这里我把一套标准跑通流程写清楚,照着做基本能避免80%的问题。
4.1 本地环境准备:版本选对,少走三天弯路
工具清单如下(版本以我实测为准):
| 工具 | 推荐版本 | 备注 |
|---|---|---|
| JDK | 1.8 或 11 | SpringBoot 2.x对JDK8支持最好,别一上来就装JDK17 |
| Maven | 3.6.x | IDEA自带Maven也行,但要检查settings.xml镜像源 |
| MySQL | 5.7 或 8.0 | 建议8.0,要注意驱动和时区配置 |
| Node.js | 14.x 或 16.x | 前端构建必需 |
| IDEA | 2021+ | 自带SpringBoot插件,方便 |
MySQL安装是个低阶但高频的坑。8.0版本安装时如果选了“Use Strong Password Encryption”,后面用旧版JDBC连接会报Public Key Retrieval is not allowed。解决办法有两个:连接URL加allowPublicKeyRetrieval=true,或者安装时选Legacy Authentication。第二个方案更省事,毕竟本地开发不涉及安全合规问题。
4.2 后端启动步骤:配置文件和启动类一个都不能错
拿到源码后,我的启动顺序是这样的:
- 用Navicat或命令行执行项目根目录的
car_dealership.sql脚本,初始化数据库。 - 修改 application.yml 里的数据库账号密码,重点检查这几项:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/car_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 你的密码
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
这里serverTimezone=Asia/Shanghai必加,很多项目报时区异常都是因为少了它。useSSL=false本地开发必加,否则MySQL 8.0默认开启SSL会报一堆警告。
- 确认Maven依赖下载没问题,IDEA右侧Maven面板刷新,等待下载完成。
- 运行主类上的main方法,看到SpringBoot启动成功的banner图案,同时出现Tomcat started on port 8080,后端就起来了。
实测遇到最多的启动失败原因:端口被占用。IDEA里跑着其他项目占用了8080,解决办法要么停掉旧项目,要么改application.yml里的server.port。我建议本机有多个Java项目的,统一用不同端口管理,比如8080、8081、8082分别对应不同项目,省心。
4.3 前端启动与联调:跨域和代理配置是关键
前端启动命令很简单:
bash复制npm install
npm run serve
Vue CLI默认启动在8080端口,而后端也在8080,端口冲突了。所以前端要改端口,在vue.config.js里配置:
javascript复制module.exports = {
devServer: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
这里的proxy就是前后端联调的关键。前端所有请求走/api开头,代理服务器把请求转发到后端的8080端口,顺便把/api前缀去掉(因为后端接口路径没有这个前缀)。这样就完美绕过了跨域问题,不需要在后端写CORS配置。
启动后浏览器访问 http://localhost:3000,如果看到登录页,输入管理员账号密码能进后台,说明整个链路通了。
4.4 单机部署:如何把前后端打成“一个包”扔到服务器上
如果只是想本地玩,上面的步骤就够了。但要部署到服务器,我推荐一个省事方案:前端打包后扔进SpringBoot的static目录,让后端统一提供服务。
步骤:
- 前端执行
npm run build,生成dist目录。 - 把dist目录里的内容复制到
src/main/resources/static/。 - 重新打包SpringBoot:
mvn clean package -DskipTests。 - 服务器上执行
java -jar car-system.jar。
这时候访问 http://服务器IP:8080,就能直接看到前端页面,因为静态资源由SpringBoot内嵌Tomcat托管了。接口路径和静态资源路径不冲突,因为接口都在/api或/car这类前缀下,而静态资源在/根路径下。
这个方案对个人项目、课程设计、小型商用都非常合适,省去Nginx配置、反向代理这些额外运维工作。缺点是不适合前后端独立扩展的场景,但那是大团队考虑的事,咱不做过度设计。
5. 实战中最常踩的坑:问题排查与解决方案速查
这部分是真正的经验之谈。我把这几年接手类似项目时反复出现的问题整理成表,每一个都是真实场景,附上排查思路。
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
| 前端登录失败,后端控制台报401 | Token校验失败,可能是JWT密钥不一致,或请求头没有正确携带token | 检查axios拦截器是否设置Authorization头;检查后端JWT生成的secret和解析用的secret是否一致 |
| 车辆图片上传成功但前端展示404 | 图片上传到本地磁盘路径,但SpringBoot静态资源映射没配置 | 配置资源映射,把本地磁盘目录映射为静态访问路径 |
| 后台管理页面能打开但列表无数据 | 接口返回的字段名和前端表格prop不一致 | 检查后端VO的字段命名(还是下划线风格),前端el-table的prop要一一对应 |
| 时间显示相差8小时 | Java后端时区是UTC,数据库连接串没有指定serverTimezone | URL加serverTimezone=Asia/Shanghai,Jackson配置time-zone=GMT+8 |
| 分页数据全是第一页 | 前端页码从1开始传,后端PageHelper/MP的current参数计算逻辑不一致 | 统一约定:前端current=1代表第一页,后端不用再+1 |
| 修改密码后旧token还能用 | JWT无状态,无法主动让已签发的token失效 | 引入token黑名单机制(redis缓存失效token),或降低token过期时间 |
5.1 排查工具与方法:遇到报错不要慌,按链路查
后端报错第一件事不是看最后一行堆栈,而是看Caused by后面的内容,那才是异常根源。比如:
code复制Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost'
一眼就能看出来是数据库账号密码不对,不用往下翻。
前端报错则要分场景:
- 白屏:优先打开浏览器F12控制台,看有没有JS报错。最常见的是某个组件没有注册,或者路由配置错误。
- 接口404:看Network面板,请求的URL是否正确,后端是否真的发布了这个接口。用Postman直接测接口,排除前端代码问题。
- 接口500:看后端控制台日志,通常会有完整的堆栈信息。最笨也最有效的办法是在Service层加日志输出,定位到具体哪一行出错。
- CORS报错:前端配置了代理就不会有跨域问题,如果还是报,检查是不是直接访问了后端地址而不是走代理。
5.2 逻辑删除和唯一索引的冲突:一个隐蔽的坑
这个坑我必须单独拎出来说。假设用户表有username唯一索引,用户A删除了(逻辑删除deleted=1),然后新用户注册想用同样的用户名。由于唯一索引存在,insert直接报Duplicate entry。
解决方案一般有三种:
- 唯一索引改成(username, deleted)联合索引,但只对少量逻辑删除数据有效,删很多次后索引还是会冲突。
- 逻辑删除字段不存0/1,存删除时间戳,删除时更新deleted=当前毫秒数。活着的数据deleted=0,这样联合索引基本不会重复。
- 注册时检查deleted=1的用户,做主动清理或改名。
我推荐方案2,实现成本低,索引冲突概率极低,而且还能看出删除时间。
5.3 搜索引擎关键词里的“冷门话题”:为什么我建议你别在项目里搞这些
看热搜词里有一些类似的“旁门左道”内容,比如“python cc攻击源码”“顶底信号98%指标源码”之类。这类东西在正规项目里完全是歧路,一个二手车交易系统如果加入这类功能,大概率不是需求,而是有人想浑水摸鱼塞违规功能。正经开发者的原则很简单:需求里没有的功能,一个都不要加。系统安全靠的是合理的鉴权、参数校验、SQL防注入,不是靠歪门邪道。遇到这种需求,直接拒绝,别给自己挖坑。
重申一下合规底线:我们写的项目代码,必须是能公开分享、符合法律法规和社会公序良俗的正规应用。任何涉及攻击、破解、非法监控的功能,一律不做、不讨论、不分享。
6. 从能用到好用:性能优化与功能扩展方向
项目跑通只是起点。如果这个系统要真正商用(哪怕是小范围使用),还有几个方向值得投入。
6.1 性能优化的几个抓手
- 索引优化:车辆列表是最高频查询,检查car_info表的索引。核心索引组合:
(status, audit_status, create_time)、(brand_id)、(price)。没有索引时,几十万数据量就能感受到明显卡顿。 - 图片懒加载:车辆列表页如果一次加载几十张图片,页面会非常重。Vue的懒加载方案可以直接给img标签加v-lazy指令,或者用IntersectionObserver自己实现。
- 接口数据瘦身:列表页只需要车辆概要信息(封面图、品牌型号、价格、里程),不需要把description这种长文本也查出来。后端做VO拆分,列表用CarListVO,详情用CarDetailVO。
- 缓存热点数据:品牌字典这类几乎不变的数据,可以用Spring Cache注解缓存到内存。商品详情页也可以用Caffeine做本地缓存,减少数据库压力。
6.2 值得扩展的功能模块
- 支付对接:如果要从“管理系统”升级为“交易平台”,接入微信/支付宝支付是第一步。需要考虑回调通知、订单状态同步、退款流程。
- 车辆评估报告:接入第三方评估服务,把车况评估报告结构化存储,能显著提升买家信任度。
- 消息通知:用户预约看车后,通过短信或站内信通知卖家。可以用SpringBoot整合WebSocket做实时消息推送。
- 数据看板:后台加一个ECharts图表页面,统计每日新增车辆、成交转化率、热门品牌排行,这是给决策者看的。
- 权限细粒度控制:现在的admin/user两级角色比较简单,如果要支持“车商入驻”,需要引入RBAC表(角色表、菜单表、用户角色关联表、角色菜单关联表)。
6.3 代码层面的几个“清爽”建议
把业务逻辑从Controller里挪到Service层,Controller只做参数接收和结果返回。我见过一个项目,一个车辆发布接口的Controller方法写了200行,各种if嵌套和业务判断,这种代码维护起来就是灾难。
统一的返回结构也要尽早定。我用的统一返回体是:
json复制{
"code": 200,
"message": "success",
"data": { }
}
所有接口都返回这个格式,前端拦截器才能统一判断code。有些项目图省事,有的接口直接返回数组,有的返回对象,前端处理起来到处是if-else,维护成本翻倍。
7. 一些过来人的实在建议
这个项目做完,如果只是交了作业或者演示完放在硬盘里吃灰,那价值只实现了三成。真正有价值的是把代码吃透,然后在这个基础上做出自己的东西。
第一,动手改一个功能。比如把车辆列表的筛选改成多条件组合查询,把后台的静态表格换成ECharts图表,把用户中心的头像上传实现一遍。改的过程中你会发现很多之前没注意的细节,这些细节才是真正的成长。
第二,学会看日志、查问题。遇到bug不要第一时间去问人,先自己按第六节的排查方法走一遍。我见过太多人把报错信息原封不动发给别人:“大佬帮我看看”。实际上90%的问题只要自己读一遍报错就知道怎么解决。
第三,把代码规范养成习惯。命名用驼峰、常量用全大写、注释写清楚“为什么这么做”而不是“做了什么”。这些习惯在课程设计阶段看不出来重要性,等到参加工作或者接商业项目,规范和不规范的代码,维护成本能差出好几倍。
最后分享一个小技巧,是我后面屡试不爽的:项目里所有鉴权相关的逻辑,包括token校验、角色判断,统一走封装好的工具类和拦截器,不要在任何业务代码里手动判断“用户是否登录”。一开始可能觉得封装麻烦,但随着项目越写越大,你会发现所有业务接口都自动获得了鉴权能力,新增一个接口根本不用考虑安全问题,省下来的时间远比当初封装的那几个小时多得多。
这套二手车交易系统,是我手里复现率比较高的一个模板——它不大不小,正好覆盖了一个完整业务系统该有的所有核心环节。能做出来是一回事,能讲清楚为什么这么做、坑在哪里、怎么扩展是另一回事。希望这篇内容对你有用,也欢迎你在自己动手做的时候,来跟我聊聊你踩到的那些新坑。
