
最近刚把手头这个宠物用品商城项目完整落了地从后端接口到PC管理后台再到移动端小程序三端全打通前后忙活了两三个月。回头梳理的时候发现这个项目虽然名义上是“毕设/课设”常客但它覆盖的电商业务链路其实非常完整——商品管理、购物车、下单、库存扣减、支付回调、订单状态机、权限控制这些真实电商系统里最核心的环节全都能碰到。如果你正在做类似题目或者刚入行想找个全栈练手项目我觉得从这个项目入手性价比很高今天把我的完整设计思路和踩坑过程摊开讲。先说下系统的整体技术栈后端用的是SpringBoot 2.7 MyBatis-Plus MySQL 8.0 Redis前端拆成了两个端——PC管理后台用的Vue 3 Element Plus移动商城用的uni-app可以同时编译到微信小程序和H5。因为业务上同时涉及PC和移动端天然就是前后端分离的架构接口层做了统一鉴权管理端走JWT移动端走微信登录换取token。很多人在做这种“SpringBoot Vue uni-app”的三端项目时会遇到一个很现实的困惑用户端和管理端到底怎么组织代码接口怎么复用移动端和后端的联调又怎么处理这篇文章我不会只贴代码而是把每一层的关键设计决策、业务流转逻辑和实际开发中容易翻车的地方都捋一遍希望能帮你省掉几个星期的弯路。1. 三端协同的系统架构为什么是这套技术组合1.1 技术选型的考量从毕业设计到真实生产环境的距离先说后端SpringBoot在这个领域几乎是标配选择但真正让我坚持用它而不是更轻量的框架是因为它的生态太全了。这个项目里我需要做登录鉴权JWT 拦截器、文件上传OSS、缓存Redis、定时任务订单超时关闭、短信通知等等SpringBoot的starter机制能把这些能力快速集成进来而且遇到问题网上的资料量级和别的框架完全不在一个层次。MyBatis-Plus这个选择我需要特别说一下。当时我在MyBatis-Plus和JPA之间犹豫了一下后来选择了前者。原因是电商系统的查询场景比较复杂比如商品列表要按分类、价格区间、销量、上架状态做多条件组合筛选还要做分页MyBatis-Plus的LambdaQueryWrapper写起来很顺手加上分页插件基本不用手写SQL。但涉及多表关联的复杂查询比如订单详情要关联商品标题和买家昵称我还是写了XML里的自定义SQL别硬用Wrapper拼接那样维护成本会爆炸。前端管理端选Vue 3 Element Plus也是顺理成章的事情。Vue 3的组合式APIComposition API在管理后台这种大量表单、表格、弹窗交互的场景下逻辑复用非常方便。比如商品管理的表单和编辑弹窗我抽了一个useGoodsForm的组合式函数表单校验规则、提交逻辑、图片上传状态全部封装在一个函数里代码量减少至少三分之一。再看移动端uni-app的优势很直接一套Vue代码能编译到微信小程序、支付宝小程序、H5和App对于一个商城项目来说这意味着不需要维护多套代码就能覆盖主流用户入口。很多人在网上争论uniapp的坑多不多我的体验是如果你想快速覆盖多端它是目前成本和收益最平衡的方案但要做好平台差异适配的心理准备——后面我会讲几个具体的坑。1.2 项目结构前后端分离后的目录与协作约定这个项目的目录结构一开始就要规划好否则三个端堆在一起会非常痛苦。我的组织方式是分了三个目录pet-server后端SpringBoot、pet-admin-web管理端Vue3、pet-mobile移动端uni-app。后端我严格按照DDD的分层思想做包结构但不要过度设计com.pet.shop ├── controller // 接口层只做参数接收和结果返回 ├── service // 业务层核心逻辑全部在这里 ├── mapper // 数据访问层MyBatis-Plus接口 ├── entity // 数据库实体 ├── dto // 前端传参对象和entity解耦 ├── vo // 返回给前端的数据对象 ├── config // 配置类跨域、Redis、拦截器等 ├── common // 公共类统一返回结果、异常处理等 └── utils // 工具类这种结构一开始会看起来繁琐但项目做到中后期当商品、订单、用户、购物车这些模块互相调用时好处就体现出来了——每个模块的职责边界很清晰不会出现改一个订单功能结果把商品模块搞挂的情况。接口层的约定也很关键。我统一了返回结构{ code: 200, message: success, data: {...} }前端所有请求封装了一层axios拦截器统一判断code值。管理端和移动端用的是同一套后端接口只是权限角色不同管理端需要admin角色才能访问/admin/**移动端走的是用户token只能访问/api/**。这个约定从一开始就要定好不然后端接口越写越乱最终只能靠if-else硬怼。1.3 数据交互与权限设计Token校验和接口路由的划分说到权限这套系统里我用了两种认证方式。管理端用的是JWT登录成功后后端返回一个有效期为2小时的token前端存在localStorage里每次请求带在Authorization请求头里。后端写了一个拦截器解析token解析失败直接返回401。这里有一个细节容易被忽略——JWT的密钥不能明文写在代码里我放在了application.yml中通过环境变量引用实际部署时用${JWT_SECRET}方式注入。移动端的登录方式有点不一样。因为要适配微信小程序我的设计是优先让用户走微信授权登录小程序端调用uni.login获取code然后传给后端后端用这个code去微信接口换openid再根据openid查用户表查到就发token查不到就自动注册一个新用户。这样用户无感知地完成了“登录 注册”两步操作体验最好。同时我也保留了账号密码登录作为备选方便H5端用户使用。接口路由的划分具体是这样的路径模式归属端鉴权方式说明/api/goods/**移动端可选登录商品列表和详情允许未登录访问/api/order/**移动端必须登录下单、查订单需token/api/cart/**移动端必须登录购物车操作需token/admin/**管理端管理员token商品管理、订单管理、用户管理等中间有一个容易踩坑的点是“是否必须登录”的判断。我一开始把所有接口都设计成必须登录后来发现游客想浏览商品列表都看不了转化率肯定受影响。所以商品相关的接口我单独放开用PassToken注解标记拦截器里遇到这个注解就放行。这个设计虽然小但对体验的影响很大。2. 后端核心模块拆解从商品表设计到订单状态机2.1 商品模型SPU/SKU两级结构与数据库设计商城系统的核心是商品模型。很多人做电商第一步就在这上面栽跟头把商品设计成一张大宽表所有的属性、图片、库存全塞在一行里。这在数据量小的时候看着没毛病但一旦商品变多、规格变复杂后面改起来就非常痛苦。我采用的是电商通用的SPU标准产品单元/SKU库存量单位两级模型。SPU是商品抽象层代表“这是一款宠物粮”SKU是具体规格层代表“这款宠物粮2kg装/鸡肉味”。以这个宠物用品商城为例一个狗粮商品SPU可以拆出1.5kg、5kg、10kg三种规格每种规格有独立的图片、库存和价格这就是三个SKU。数据库上的设计是这样的-- SPU表 CREATE TABLE spu ( id bigint NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 商品标题, subtitle varchar(500) DEFAULT NULL COMMENT 副标题, category_id bigint NOT NULL COMMENT 分类ID, main_image varchar(500) DEFAULT NULL COMMENT 主图URL, detail_html longtext COMMENT 商品详情富文本, status tinyint DEFAULT 1 COMMENT 上架状态 1上架 0下架, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- SKU表 CREATE TABLE sku ( id bigint NOT NULL AUTO_INCREMENT, spu_id bigint NOT NULL COMMENT 所属SPU ID, specs varchar(500) DEFAULT NULL COMMENT 规格JSON如{重量:2kg,口味:鸡肉}, price decimal(10,2) NOT NULL COMMENT 售价, stock int NOT NULL DEFAULT 0 COMMENT 库存, sku_image varchar(500) DEFAULT NULL COMMENT 规格图, status tinyint DEFAULT 1 COMMENT 1启用 0禁用, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里的关键是将规格specs以JSON形式存储在SKU表中这样做的好处是无需预先固定规格维度不同类目的商品可以有不同的规格属性。缺点是查询时无法直接用SQL按规格筛选但真实场景中规格筛选通常在前端点击时用代码过滤这个短板可以接受。2.2 购物车与下单Redis缓存与数据库落地的取舍购物车是一个看起来简单、做起来容易出问题的模块。传统的方案是直接在数据库建购物车表用户每次添加商品都insert一条记录。但这个方案在高频操作下会频繁写库性能和数据库压力都不理想。我的做法是“Redis为主、数据库兜底”。移动端添加购物车时先操作Redis中的Hash结构key是cart:用户IDfield是SKU IDvalue是数量和一个简单的商品快照标题、价格、图片。用户查询购物车时直接从Redis里取值展示。这样就规避了每次请求都查数据库的问题接口响应时间从几十毫秒降到几毫秒。但纯Redis方案有个致命问题——数据容易丢。用户A加了一堆购物车商品Redis一重启全都没了用户肯定要骂娘。所以我的策略是兜底购物车数据在每次变更时异步同步一份到MySQLRedis只是作为前台读缓存。不做实时同步的原因是购物车本身对一致性要求没那么高即使丢几条记录也不会造成资金损失顶多用户重新加一下。不过下单的流程就完全不一样了绝不能容忍丢数据。整个下单动作我放在一个事务里同时处理三件事订单主表插入记录、扣减SKU库存、如果需要则锁定优惠券。这里有一个极其重要的细节——扣库存的SQL必须带上库存大于0的条件boolean updated skuMapper.reduceStock(skuId, quantity); // SQL: UPDATE sku SET stock stock - #{quantity} WHERE id #{skuId} AND stock #{quantity} if (!updated) { throw new BizException(库存不足); }如果用“先查库存再判断够不够再update”的写法在高并发场景下一定会出现超卖——两个请求同时读到库存为1都判断可以扣减最后库存变成-1。带上stock #{quantity}条件的原子SQL是从根上杜绝这个问题的方法。这一点面试时也是高频考点值得记下来。支付流程方面我接的是支付宝沙箱环境。因为不是企业资质正式支付接口没法申请但沙箱环境足够跑通整个流程了。用户点击支付时后端生成支付宝支付表单前端通过uni.requestPayment拉起支付小程序端是wx.requestPayment支付完成后支付宝异步回调后端接口后端在回调里校验签名、更新订单状态。沙箱环境有一个要注意的地方回调URL必须是外网能访问的地址否则支付宝服务器通知不到你的后端。本地开发时我用的是natapp内网穿透把本地8080端口映射成一个临时域名这样就能接收到支付宝的异步通知了。2.3 订单状态机从待付款到已完成的状态流转订单模块是商城系统里业务逻辑最密集的地方如果不用状态机的思路来设计写着写着就会变成一坨if-else。我梳理了一下这个项目需要的订单状态一共六个状态状态值含义可执行操作待付款0已下单未支付取消订单、去支付待发货1已支付待商家发货商家发货待收货2已发货待用户确认确认收货已完成3用户已确认收货申请售后已取消4用户取消/超时取消无已退款5退款完成无状态在设计时我特意用int值而不是字符串因为后续写查询和统计SQL会更方便而且前端渲染状态文案时只要做一个映射就行。订单超时关闭这个功能也是必做的。用户下单后如果不支付订单不能一直挂着占用库存。我的方案是用Spring Boot自带的Scheduled定时任务每分钟扫描一次超过30分钟未支付的订单把它们状态更新为已取消同时恢复库存。这种方案实现的成本低但要注意一个问题如果订单量大每分钟全表扫描会变成性能瓶颈。更优雅的方案是使用延迟队列如RabbitMQ的延迟消息或Redis的过期键监听但在毕设/课设这个量级下定时任务完全够用不必过度设计。3. PC管理端商品管理与订单处理的权限设计3.1 后台权限控制Router守卫与动态路由管理端用Vue 3 Element Plus开发整体是一个典型的中后台布局左侧菜单栏、顶部面包屑、中间内容区。整个后台最核心的设计点是权限控制。我做了两层的权限控制路由级别和按钮级别。路由级别的控制用的是Vue Router的全局前置守卫。用户登录后后端会返回当前管理员的角色和权限列表前端把权限列表存到Pinia里然后通过addRoute方法动态添加用户有权限访问的路由。这样做的好处是没权限的路由根本不会注册用户就算手动修改URL进不去因为路由表里根本不存在。按钮级别的话用了一个自定义指令v-permission。比如商品管理页面上的“删除商品”按钮只有管理员角色才能看到普通运营角色只能编辑不能删除。具体实现是// directives/permission.js app.directive(permission, { mounted(el, binding) { const requiredPermission binding.value; const userPermissions useUserStore().permissions; if (!userPermissions.includes(requiredPermission)) { el.parentNode?.removeChild(el); } } });按钮的显隐只是前端体验层面的控制真正的权限控制在后端接口上——后端接口也需要校验角色。管理端的每个接口方法都加了RequirePermission(goods:delete)这样的注解通过AOP拦截做权限校验前端按钮隐藏只是让界面干净后端接口校验才是安全保障。3.2 商品管理动态规格编辑器的前端实现商品管理是管理后台用得最多的页面前端主要包括商品列表、搜索筛选、上下架操作、新增/编辑商品弹窗。最繁琐的是新增商品时的规格编辑功能——因为每个商品可能有不同的规格维度比如宠物粮有“重量”和“口味”两个维度猫咪玩具可能只有“颜色”一个维度。我在这块做了一个动态规格编辑器。初始选择一个SPU下要配置哪些规格维度比如选了“重量”和“口味”前端会动态生成一个表格每一行代表一个SKU列分别是“重量值”“口味值”“价格”“库存”“规格图片”。Vue 3的响应式数据加上Element Plus的Table组件做这个交互非常顺手。图片上传这块踩过一个坑。Element Plus的Upload组件默认会把图片转成base64回传但如果图片大了这个字符串会非常长既占数据库空间又拖慢接口响应。我改成前端先调用后端的/upload接口把图片传给OSSOSS返回一个URL表单里只存这个URL字符串。这套方案还带来一个额外的好处——页面加载图片时走CDN不占自己服务器的带宽。3.3 订单管理发货操作和售后处理订单管理模块对管理后台来说核心操作就是“发货”。订单列表默认展示所有待发货订单管理员点击发货按钮后弹出填写物流公司和运单号的表单。提交后后端更新订单状态为“待收货”同时把物流信息写入订单物流表。这里有一个小设计系统自动给用户发送一条模板消息通知对接了微信小程序的订阅消息告知包裹已发出。售后处理比较容易被忽略。我的处理是用户申请退款后后台生成一条售后单管理员可以查看申请原因、订单金额、支付时间等信息然后选择“同意退款”或“拒绝退款”。同意退款时会调用支付宝的退款接口同时把订单状态更新为“已退款”并恢复SKU库存。退款接口是幂等的——即使支付宝回调重复通知处理逻辑也不会重复退款因为我在处理前先查询订单状态只有“待发货”状态的订单才允许退款处理完立刻变更状态。4. 移动商城端uni-app下的登录、购物车与支付适配4.1 uni-app项目搭建从环境配置到TabBar图标坑移动端我直接用HBuilderX创建了uni-app项目Vue 3版本。第一次打开的小伙伴可能会一脸懵工具链和普通Vue项目不太一样需要在HBuilderX里配置微信开发者工具的路径才能实现“一键运行到微信开发者工具”。装好之后在manifest.json里配置小程序的AppID如果没有就用测试号也能正常跑起来。这里有一个我在热搜里看到频率极高的问题tabBar图标用uni-icons。大部分人都会踩到这个坑——uni-app的tabBar配的是本地图片路径不能直接用组件。常见做法是去iconfont.cn下载两张png选中态和未选中态放到static/tabbar/目录下然后在pages.json里配置tabBar: { color: #999999, selectedColor: #FF6B35, list: [ { pagePath: pages/index/index, text: 首页, iconPath: static/tabbar/home.png, selectedIconPath: static/tabbar/home-active.png }, { pagePath: pages/cart/cart, text: 购物车, iconPath: static/tabbar/cart.png, selectedIconPath: static/tabbar/cart-active.png } ] }如果直接用uni-icons组件小程序编译时会报“tabBar的iconPath必须为本地图片路径”的错误因为组件最终渲染的是字体图标而tabBar要求的是图片资源文件。4.2 商品列表与购物车列表页性能优化和状态同步商城移动端的首页是一个信息流页面展示宠物食品、玩具、医疗用品等分类的商品卡片。每个卡片有封面图、标题、价格三个信息。这里我踩了一个明显的性能坑最开始接口返回的是商品所有字段包括富文本详情一个列表接口返回几MB的JSON在小程序端卡到没法看。后来优化是列表接口只返回卡片需要的字段详情页单独再请求detail接口列表页秒开。购物车页面的关键问题是跨端状态同步。用户在商品详情页点击“加入购物车”此时如果购物车页面的数据还停留在旧状态就会产生不好的体验。我的做法是在uni-app里用一个全局的购物车数量状态管理Pinia每次加入购物车后更新状态同时TabBar的购物车图标上用uni.setTabBarBadge展示商品总件数。这样一来用户在任何页面都能看到购物车的最新状态整体体验会自然很多。购物车“选中商品然后结算”这个交互也需要特别注意。购物车里的每个商品项左侧有一个复选框底部是合计金额和“去结算”按钮。金额要根据勾选的商品实时计算所以在Pinia里维护了一个选中列表的computed属性勾选或取消勾选时金额自动刷新。结算按钮跳转到订单确认页携带选中的SKU ID列表后端根据这些ID生成订单。4.3 支付流程微信小程序拉起支付与回调处理支付是整个移动端最核心也最容易出问题的环节。在小程序端用户点击“立即支付”前端需要先调用后端接口获取支付参数timeStamp、nonceStr、package、signType、paySign再调用uni.requestPayment拉起支付面板。这其中有一个特别容易搞错的地方后端生成这些参数时用的签名密钥商户API密钥不是小程序的AppSecret而是微信支付商户平台里设置的APIv3密钥两者弄混的话签名永远校验不过。用户支付成功或取消后微信会通过异步回调通知后端。这里要注意的是小程序端的支付成功回调并不能完全信任——用户可以用开发者工具模拟支付成功所以后端的订单状态更新必须依赖微信服务器发来的异步通知而不是前端的成功回调。我在后端回调接口里先校验签名参数然后根据trade_state判断是否为SUCCESS是的话才更新订单状态为“待发货”。前端收到支付成功提示后轮询订单状态接口直到确认订单已经是“待发货”再跳转到订单列表页。4.4 页面跳转参数传递如何避免状态丢失聊一个很基础但很多新手会栽跟头的点页面间传参。商品列表页跳详情页需要把商品ID传过去我用的是uni.navigateTo的URL参数方式uni.navigateTo({ url: /pages/goods/detail?id${skuId} });然后在详情页的onLoad生命周期里接收onLoad(options) { this.skuId options.id; this.fetchDetail(); }但这里有个坑如果传递的参数是一个对象直接拼到URL里会被转成[object Object]。正确的做法是用encodeURIComponent(JSON.stringify(obj))编码后再传接收时再JSON.parse(decodeURIComponent(options.data))。我在购物车跳订单确认页时传商品列表就用了这个方案不然拿到手的永远是乱码。另外URL传参还有一个长度限制问题如果参数太长比如某个分享场景可以考虑用uni-app的全局变量或Pinia来传递数据。总之记住一个原则简单参数用URL传递复杂或大体积数据用全局状态管理。5. 部署与踩坑记录从本地联调到上线部署5.1 本地联调跨域配置和代理转发本地开发时前端和后端是分离的两个服务前端跑在8080端口后端跑在8081端口直接请求必然遇到跨域问题。常用的解决方案是后端配置CORS跨域过滤器允许所有来源访问Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(*) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }但在管理端开发时我更推荐用Vite的代理功能。在vite.config.js里配置server: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } }这样前端代码里请求的地址还是/api/goods/list开发服务器收到请求后自动转发到后端8081端口。好处是前端代码里不需要写死后端地址将来部署到生产环境时只要在Nginx里做同样的反向代理配置代码一行都不用改。5.2 uni-app打包从H5到微信小程序的平台差异这里重点想说一下uni-app的多端兼容问题。虽然它宣传的是“一套代码多端运行”但真实情况是每个平台都有一些必须单独处理的差异。我实际项目中最大的差异发生在登录和支付两个环节。登录环节微信小程序只能用uni.login获取code然后通过uni.request发给后端换openidH5端通常是用账号密码登录微信的H5登录需要走wx.config之类的公众号授权逻辑完全不同。我用条件编译来处理在代码里用#ifdef MP-WEIXIN和#ifndef MP-WEIXIN包裹不同的逻辑虽然看起来代码变多了但多端同时运行都不会出问题。支付环节小程序端用的是上一步说的uni.requestPaymentH5端则需要在后端生成支付宝或微信的H5支付链接然后通过window.location.href跳转。开发的时候别指望一端代码直接复用到另一端早点做条件编译是明智的。还有一个小程序特有的问题rich-text组件渲染富文本时不支持CSS中的部分选择器和外部样式直接使用可能会出现样式错乱。我的商品详情页富文本是后端存储的HTML小程序端展示时会出现间距、图片大小问题。解决办法是后端返回前给富文本里的图片自动加上max-width: 100%的行内样式这样不管什么端图片都不会超出屏幕宽度。5.3 上线部署Linux服务器 Nginx Docker部署时我用了一台2核4G的云服务器系统是Ubuntu 22.04。后端和数据库都跑在Docker容器里用docker-compose统一编排。Redis、MySQL、后端服务各一个容器端口做好映射数据目录挂载到宿主机这样后面备份数据库只需要备份宿主机的一个目录就行。部署中最容易出问题的是配置文件。本地开发时application.yml里连的是本地数据库服务器上是另一个地址如果直接把本地打好的jar包丢上去跑一定会报数据库连接失败。我的做法是用Spring Boot的多环境配置spring: profiles: active: ${SPRING_PROFILES_ACTIVE:prod}本地开发时设置SPRING_PROFILES_ACTIVEdev服务器上启动时设成prod两个环境各有一套application-dev.yml和application-prod.yml配置从根上避免“本地好好的服务器上一跑就挂”这个经典问题。前端部署就简单多了。Vue 3项目执行npm run build生成dist目录然后把dist里的文件丢到服务器的Nginx静态目录下。Nginx配置里做两个转发静态文件按路径直接返回/api和/admin开头的请求反向代理到后端服务的8080端口。H5端的uni-app项目打包后和Vue 3项目类似也放在Nginx里。小程序端不需要部署服务器直接在微信开发者工具里上传代码等微信审核通过后发布。5.4 uni-app wgt包热更新失效的问题这里补充一个uni-app社区里特别高频的问题——wgt包热更新不生效。App端的wgt热更新机制是App启动时检查服务器上的版本号如果比本地版本新就下载新的wgt资源包替换掉旧的资源。但这个机制有很严格的前提App的版本号原生壳的版本不能变只能替换wgt资源。很多人的热更新不生效是因为在manifest.json里把版本号也改了导致App认为这是一个全新的版本必须走整包更新。正确的做法是热更新只更新wgt包App版本号保持不变把资源版本号versionCode递增。如果改了原生代码比如新增了一个原生插件那wgt热更新覆盖不了必须走整包更新。我在做App打包时还发现一个问题本地打包用DCloud的云打包服务是最省心的不需要自己配置Android Studio但如果使用了自定义原生插件就必须用Android Studio配合离线打包。热搜里有人问“uni-app x怎么使用Android Studio原生打包”我的建议是先去DCloud官网看离线打包文档把对应的SDK版本和Android版本匹配好然后再处理签名文件。离线打包最大的坑是manifest.json里的appid要和你申请到的DCloud appid保持一致否则插件无法加载。6. 写在最后的实战心得这个宠物用品商城系统做下来我最大的收获倒不是代码量写了多少而是体会到了全栈项目中“串联”的重要性。很多人单独学SpringBoot、单独学Vue、单独学uni-app的时候都觉得不难但真正把它们串起来做一个完整系统时各种问题全冒出来了——前后端接口约定不一致、token鉴权在移动端失效、跨域配置挡住联调、多端平台差异导致功能异常。这些问题中的任何一个单纯看教程都学不到只有自己亲手做一遍才能遇到。如果你正准备复刻这个项目我有几条建议值得提前看第一先定契约再写代码。前端需要的字段、接口路径、返回结构、错误码在和前端联调之前就定义清楚最好写一份简单的接口文档Apifox或者Swagger都可以。很多人做到一半发现接口返回的字段前端匹配不上改来改去浪费大量时间根源就是契约没提前定好。第二日志要尽早加上。后端从第一天就配置好logback将接口入参、出参、耗时做成统一日志输出。线上出了问题一份清晰的日志能节省几个小时的排查时间。第三安全问题别忽视。虽然这是练习项目但至少要做SQL注入防护MyBatis-Plus默认预编译基本安全、JWT密钥不要硬编码在代码里、管理端和后端接口之间做好权限校验。我把JWT密钥和数据库密码用环境变量方式注入而不是明文写在yml里这样即使代码推到公开仓库敏感信息也查不到。第四分批开发别想着一口气全部写完。我自己的节奏是先跑通商品浏览这条链路商品列表 - 详情 - 加入购物车再打通下单支付链路最后才补管理后台。每条链路打通之后立刻自测一遍有问题马上修比全部写完再统一测要高效得多。最后再分享一个我在支付回调调试上用的小经验支付宝沙箱和微信支付沙箱小程序里需要注册开发者账号的回调通知默认是POST一个form表单到你的回调接口用Docker部署时要注意端口和路径的映射是否和支付宝配置一致。我当时本地联调一切正常部署到服务器后怎么都收不到回调排查了半天发现是Nginx没把/api/pay/callback这个路径代理到后端反而被静态资源拦截了。这种问题光看代码是看不出来的要把请求的完整链路一层一层捋用户 - Nginx - 后端容器 - 回调接口每一步都确认一下就能快速定位。以上就是我对这个基于SpringBoot Vue uni-app的宠物用品商城系统从架构设计到部署落地的完整复盘。整个项目麻雀虽小五脏俱全如果你能把每一个环节都吃透无论是对面试还是后续工作都是一个很扎实的积累。有什么细节问题欢迎在评论区和大家交流我看到都会回复。