1. 为什么做旧衣回收小程序:从需求到方案落地
做旧衣回收这个选题,其实是被身边一个做社区环保项目的朋友带起来的。他当时在跑小区废旧织物回收业务,每天靠人工登记、Excel表格记录回收员的回收量,居民想预约上门送衣服还得打电话或者加微信群,运营成本高得吓人。聊天的时候他问我,能不能用微信小程序做一套在线预约系统,让居民在手机上填个预约单,回收员接到订单后上门称重、结算积分,后台还能看到每天的回收数据。这个需求听起来不复杂,但真正拆开之后,涉及的环节非常多:用户端要流畅、回收员端要简单、管理端要能看数据、订单要有完整的生命周期管理。于是就有了这套基于Spring Boot + 微信小程序 + Vue前后端分离的旧衣回收系统。
这套系统最终做成什么样子?简单说是三端架构:微信小程序给普通用户用,提供预约下单、查看订单、积分查询、积分商城兑换等功能;后台管理端用Vue生态搭建,给运营人员用,负责订单审核、回收员管理、区域配置、数据统计;后端全部由Spring Boot支撑,提供统一REST API,小程序和后台共用一套接口。整套项目从零开发到调试跑通,前前后后花了一个多月,过程中踩的坑大多不在业务逻辑本身,而在于微信生态的限制、前后端联调的细节、以及状态机设计的疏漏。这篇文章就把整个系统的设计思路、核心模块实现、以及实际调试中遇到的问题一次性说清楚,如果你也想做类似的回收类小程序系统,可以直接借鉴这套技术方案。
适合阅读这篇文章的人,我理解大致分三类:第一类是毕业设计选题是垃圾分类、旧物回收方向的学生,这套系统从功能设计到技术栈都非常适合作为项目原型;第二类是想做社区回收类创业项目、需要快速上线MVP验证业务的技术人员;第三类是对Spring Boot + 小程序 + Vue这种前后端分离架构感兴趣、想通过一个完整案例学习整套开发流程的人。无论你属于哪一类,这篇文章都尽量做到把原理讲透、把步骤讲细、把坑讲够。
2. 技术选型与整体架构:Spring Boot、小程序、Vue三者怎么分工
先聊聊技术选型。既然标题里明确了Spring Boot + 微信小程序 + Vue + 前后端分离,那架构本身就没有太多悬念,但在实际搭建之前,有几个点需要认真思考:后端该拆成几个模块、小程序和后台管理端是否共用一套接口、数据库怎么设计才能支撑订单状态流转、微信登录怎么和自有用户体系打通。这些决定不是随便拍拍脑袋定的,每个选择背后都对应着实际业务的限制。
2.1 前后端分离的职责边界
前后端分离这句话,很多项目挂在嘴边,但真正做到位的其实不多。我这个项目的做法是:后端只暴露JSON接口,小程序端和Vue管理端都通过HTTP调用同一个后端服务,两边各自维护自己的页面状态和路由。这样做的好处是显而易见的——小程序的逻辑层和渲染层本来就分离,天然适合接口驱动的模式;而Vue管理端用axios封装请求,配合Vue Router做页面跳转,也能很自然地和后端解耦。后端不用关心前端长什么样,前端也不用关心SQL怎么执行,大家只要把接口文档对齐就行。
职责边界具体划分成这样:小程序端负责用户登录、预约下单、支付定金(这个后来说说)、订单详情查看、积分查询、商城兑换、个人中心;后台管理端负责管理员登录、回收员账号管理、区域管理、订单审核、订单派单、回收记录录入、积分审核、数据报表;后端负责所有业务逻辑、数据持久化、微信接口调用、文件存储。一句话总结就是:小程序端做用户操作,管理端做运营控制,后端做业务大脑。
2.2 技术栈选择的核心理由
Spring Boot作为后端框架,理由很直白:生态成熟,内置Tomcat,整合MyBatis Plus和MySQL非常顺滑,尤其适合快速开发CRUD密集型的业务系统。在我这个项目里,Spring Boot承担的不仅仅是简单的增删改查,还包括了微信登录的code2session调用、订单状态机的流转控制、积分流水的事务处理,这些都是业务核心,需要一个稳定可靠的框架兜底。
小程序原生开发还是选了好用的第三方框架?我最终选择了原生小程序开发,没有引入额外的跨端框架。理由是我需要的小程序功能并不复杂,页面数量在十个以内,原生语法完全没有压力,而且原生开发在调试微信登录、获取手机号、订阅消息这些能力时,官方工具的支持最直接,少一层封装少一堆坑。Vue端选了Vue 3 + Element Plus,Vue 3的组合式API写起来比Vue 2的选项式更清爽,Element Plus的表格、表单、弹窗组件正好覆盖管理后台的常见需求,不需要自己造轮子。
数据库用的是MySQL 8.0,存储引擎InnoDB,字符集utf8mb4。可能有人会问,旧衣回收这种业务数据量能有多大,为什么要用MySQL而不是SQLite?核心原因是订单、用户、积分流水之间有强关联关系,事务处理要求高,比如生成订单的同时要扣减用户的预约额度,或者回收员确认收款时同时给用户增加积分,这种操作必须保证原子性,MySQL在事务和锁机制上非常成熟,完全没有必要去折腾轻量级方案。
2.3 后端模块划分与工程结构
后端工程我分成了几个清晰的包结构,这个对于后续维护和扩展特别重要。com.example.recycle下面分别是controller、service、mapper、entity、common、config这六个包。controller只做参数接收和结果封装,service层放业务逻辑,mapper层用MyBatis Plus的BaseMapper减少重复代码,common里放统一的响应体、异常处理器、工具类,config里放微信配置、拦截器配置、跨域配置。
统一响应体是我特别坚持的一个设计,所有的接口返回结构都是{code, message, data}这种格式。一开始后端同事习惯直接返回对象或者Map,前端拿到什么是什么,结果小程序那边解析数据时需要判断各种特殊情况,非常痛苦。后来我写了一个Result类,所有接口强制返回这个结构,配合全局异常处理器把业务异常也统一包装成这个格式,前端只需要判断code是否等于200就能知道请求是否成功。这个改动虽然简单,但大幅降低了联调时的心智负担。
3. 核心数据模型与数据库设计:回收订单是怎么流转的
数据库设计是整个系统的地基,这块如果没想清楚,后面改起来会非常痛苦。我做这个系统时,第一版表结构设计就踩了坑,当时把订单和回收记录混在一张表里,导致状态流转的字段互相冲突,后来硬着头皮拆表重构,花了两天时间。所以这一章我详细拆解一下最终的数据模型设计,你能直接照着建表。
3.1 用户表、地址表与回收员表
用户表user_store,核心字段包括openid(微信唯一标识)、昵称、头像、手机号、积分余额、状态。这里有个关键点:小程序登录拿到的是openid,不能用openid直接当主键,因为用户绑定手机号、修改资料之后,还是要通过用户ID来关联业务数据。我的做法是自增ID做主键,openid建唯一索引。
地址表user_address用来管理用户收货地址。回收业务里的地址和电商收货地址不太一样,它没有收货人这个概念,只有省市区、详细地址、门牌号、经纬度。经纬度很重要,因为回收员上门时要用地图导航定位,如果只有文字地址没有坐标,回收员的体验会很差。小程序端用wx.chooseLocation选好位置后,把经纬度一起提交到后端,这样后台可以直接基于坐标做就近派单。
回收员表recycler存储回收员信息,包括姓名、手机号、身份证号(实名认证用)、所属区域、接单状态(在线/离线)、累计回收重量、评分。回收员本质上是一个轻量级的配送员角色,但因为我们不做实时派单,而是采用抢单或者管理员指派模式,所以接单状态字段就特别关键。
3.2 预约订单表与状态机设计
预约订单表recycle_order是系统的心脏。字段包含订单号、用户ID、回收员ID(可为空)、预约地址ID、预约时间段、订单状态、衣物预估重量、实际重量、回收金额(积分)、备注、创建时间、完成时间。
订单状态我设计了完整的流转链路:待接单 -> 已接单 -> 已上门(回收员到达标记) -> 已完成 -> 已取消。外加一个异议状态:已申诉。这个状态机在代码里用常量类OrderStatus定义,不允许随意跳转,比如待接单直接跳到已完成是非法的,必须经过中间状态。开发时我用了一个状态流转校验方法,在service层更新订单之前检查当前状态和期望状态是否匹配,不匹配直接抛业务异常,这一招真的能拦住很多低级Bug。
为什么一定要有"已上门"这个状态?因为实际业务中,回收员到了用户家门口,用户可能不在,或者需要临时沟通,如果没有这个中间状态,用户在等待过程中的体验是空白的,后台也无法判断订单卡在了哪个环节。有了已上门状态,用户端可以展示"回收员已到达,请留意电话",后台对超时订单也能精准跟进。
3.3 积分流水表与提现记录表
积分是这个系统的核心激励机制。用户每次完成回收,根据重量获得积分,积分可以在商城兑换礼品,也可以提现。积分流水表points_record记录了每一笔积分的变动,字段包括用户ID、变动类型(获得/消费)、变动数值、关联订单ID、备注、创建时间。设计这条表时我特别留了一个关联订单ID字段,因为后续对账时,如果有人质疑积分不对,可以追溯到具体是哪一单产生的,这个追踪能力在运营场景里非常重要。
提现记录表withdraw_record对应回收员的提现申请。回收员不是靠积分激励的,他们有真实的回收收益。每次回收完成后,系统根据废品单价和重量计算金额,计入回收员的待结算余额。回收员可以在小程序端(其实回收员用的也是同一套小程序,只是角色不同)发起提现,管理员审核后通过线下转账或者微信支付商户付款完成打款,然后在后台标记已打款。这张表的核心字段是回收员ID、提现金额、审核状态、打款状态、备注。这里我踩过一个坑:一开始把审核状态和打款状态合成一个字段,结果出现审核通过但打款失败的中间态,无法表达,后来拆成两个独立字段才解决。
3.4 数据表之间的关联关系
让我用一段话来总结这几张表的关系:用户创建了预约订单,订单关联了地址,管理员或回收员接单后订单绑定回收员,完成回收时生成一条积分获得流水,用户积分余额增加,同时在回收员维度生成一笔待结算金额。管理员在后台审核提现请求,审核通过后打款。整个链路是单向但带有回环校验的——订单完成是积分流水的来源,积分流水是对账的依据,提现记录是资金出口。设计表的时候一定要在脑子里把资金流向画清楚,宁可多留字段,不要等上线之后发现追踪不到数据。
4. 微信小程序端的关键实现:从登录到预约下单的全流程
小程序端是整个系统里用户直接接触的部分,体验好不好直接决定业务能不能跑起来。这一章我按用户实际操作路径来讲:进入小程序登录、浏览首页、选择回收品类、预约上门时间、填写地址、提交订单、查看订单状态。每一步都有对应的技术方案和踩坑记录。
4.1 微信登录与用户体系打通
微信小程序登录的流程是:小程序端调用wx.login拿到临时code,把code发给后端,后端拿着code加AppID和AppSecret去调用微信接口,换回openid和session_key。这里有个需要注意的点:session_key不能返回给前端保存,它只能在服务端用来解密手机号、获取用户信息等敏感操作。如果把这个密钥泄露出去,用户数据安全性就无法保证了。
我在实际开发中遇到的第一个坑就是code的时效性。wx.login获取的临时凭证有效期只有五分钟,而且只能用一次。当时我有个需求改动,前端在拿到code之后,又做了一次网络请求失败重试,导致同一个code被后端处理了多次,第二次解密直接失败。解决方式是在小程序端用一个标志变量控制,wx.login之后立即把code传给后端,失败时重新调用wx.login获取新code,而不是复用旧code。
登录之后的用户信息保存走的是JWT方案。后端根据openid查数据库,如果用户已存在就直接生成token返回,如果不存在就先注册一个新用户再生成token。这个token在小程序端的每次请求中都放在header里,后端用拦截器统一解析校验。用JWT的好处是服务端不需要保存会话状态,天然适合多实例部署,而且小程序端只要保存一个token字段,不涉及cookie跨域困扰。
4.2 预约下单的完整流程与参数设计
预约下单是这个系统最核心的用户操作。小程序的几个操作要点:品类选择、旧衣预估重量、预约时间段选择、地址选择、提交订单。品类我提供了T恤/裤子/床上用品/鞋包/其他这几类,每一类对应不同的积分计算系数。这里有一个很重要的业务逻辑:用户在提交订单时填写的预估重量,并不是最终的结算重量,只是让系统判断是否超过回收员单次回收的运力上限,同时也是为了让回收员上门时有个心理预期。
时间段选择是预约类系统的标配功能。后端提供一个接口,根据当前日期返回未来七天的可预约时段,每个时段限制可接单数量。这个限量的实现逻辑是用redis的计数器,用户提交预约时对选中的时间段进行增减。当时没有接redis,用的是MySQL一张时段表配合乐观锁字段,但并发量一上来就出现超卖,后来还是老老实实引入了Spring Data Redis来支撑这个计数逻辑。如果业务规模不大也可以直接先接MySQL乐观锁,但对于想做得更健壮的项目,redis的方案更推荐。
提交订单的接口做了完整的参数校验。预估重量必须是大于0的数字,地址ID必须属于当前用户,预约时间段必须在可预约范围内,订单提交成功后展示订单编号并跳转到订单详情页。这里做了一步很细节的交互:提交成功后通过微信订阅消息给用户发一条模板消息,内容包含订单编号和预约时间段。订阅消息的坑在于用户必须先点击授权才能发送,而且一次性订阅消息只能用一次,所以我是在用户提交订单的按钮点击事件里同时申请订阅消息授权,保证用户授权动作和订单提交动作是同一个用户手势,这样消息触达率最高。
4.3 扫码核销与订单状态更新
回收员端在小程序里如何核销订单?我采用的方案是二维码核销:管理员在后台指派回收员后,用户订单详情页会生成一张二维码,回收员用微信扫码后,小程序跳转到核销确认页,确认无误后点击"确认回收",订单进入已完成状态,同时触发积分发放。这里二维码的生成逻辑是后端用工具库生成,内容包含订单编号,回收员端扫码后调用二维码详情接口,后端校验订单状态和回收员身份之后返回可回收信息。
这个流程里最容易出问题的是扫码权限校验。二维码如果被任意用户扫到,可以看到订单详情,这是不能接受的。我做的处理是核销接口必须校验当前扫码用户的身份是回收员,且回收员与订单绑定关系存在。实际开发时还遇到一个二维码内容中文乱码的问题,排查发现是生成二维码时对订单编号做了URL编码忘记解码,后来在后端统一用Base62编码订单号,二维码内容只包含数字和字母,从根本上规避了字符集问题。
5. Vue后台管理系统的功能设计与联调细节
后台管理端用的是Vue 3 + Element Plus + Axios + Vue Router + Pinia。这个管理端要承担的业务职责很重:运营人员要在上面管理回收员、审核订单、处理申诉、配置回收品类和积分系数、查看回收数据和财务报表。这一章讲一下管理端的设计思路和几个联调细节。
5.1 管理端权限路由与页面结构
管理端采用经典的左侧菜单 + 顶部导航布局。路由设计上使用的是动态路由,管理员登录后根据角色ID从后端拉取菜单权限,前端根据权限数据动态注册路由。一开始我图省事,把所有路由都静态写在router里,结果出现一个运营人员直接在浏览器里输入URL跳转到管理员页面的问题,虽然接口层面有权限拦截不会真的泄露数据,但这种体验很不好,而且容易被安全检查视为漏洞。改成动态路由之后,没有权限的页面根本不会注册,直接跳404,干净利落。
菜单结构上,一级菜单包括:仪表盘、订单管理、用户管理、回收员管理、积分管理、品类配置、提现审核、数据报表。其中订单管理是核心,订单列表页面做了多条件筛选:订单状态、预约时间段、用户手机号、回收员姓名,表格支持分页。筛选条件的实现直接在后端接口里用MyBatis Plus的LambdaQueryWrapper动态拼接条件,前端把筛选参数传给后端,后端根据参数是否存在决定是否拼接where条件。
5.2 订单列表联调中的字段对齐问题
前后端联调阶段最烦的事情就是字段命名不一致。后端返回的字段用的是驼峰命名,比如recycleStatus,前端因为有人习惯写蛇形,代码里用了recycle_status,结果页面一直拿到undefined。这个问题之所以难排查,是因为浏览器控制台不报错,只是表格空数据。后来我们在项目里约定:后端接口返回的字段一律使用驼峰命名,并且专门写了一个前后端接口文档表格,每个接口列出字段名、类型、是否必返。这个表格维护起来有成本,但比起联调时互相猜字段,这点成本太值得了。
订单状态在管理端展示时,需要把数字状态码翻译成中文字段。这一块我做了个前端Filter函数,把订单状态映射为标签颜色:待接单是警告色,已接单是蓝色,已上门是绿色,已完成是灰色。用户和管理员看到的状态文案分开定义,因为用户端要看到"回收员已出发,请耐心等待"这种友好文案,管理端则直接要看到状态术语,这个细节虽然不起眼,但很影响使用体验。
5.3 图片上传与品类配置模块
管理端还有一个重要功能:品类配置。运营人员需要在后台维护回收品类的名称、图标、积分系数、是否启用。图标上传用的是Element Plus的Upload组件,前端把图片上传到后端接口,后端保存到本地磁盘返回访问URL。这块有几点要注意:本地磁盘存储的路径不要放在项目源码目录里,因为重新部署时会丢失文件,我单独配置了一个upload目录映射为静态资源路径;上传接口限制文件类型为jpg/png/webp,大小限制在2MB以内,不然管理员传了一堆几MB的原图,既占空间又拖慢页面加载。
积分系数配置这里有个小设计:每个品类配置两个系数,一个按重量积分(每公斤多少分),一个是按件数积分(每件多少分),运营人员可以切换结算方式。之所以做两套,是因为实际回收场景中,有些用户卖的是小件衣物,称重不方便,按件计算更合理。这个设计在需求评审阶段差点被砍掉,后来实地看了几次回收员的操作过程,发现确实两种场景都有,两种方式并存才是贴合实际的。
6. 部署、调试与踩坑实录:这些坑让我失眠了好几个晚上
这一章节我打算写一些真实的调试经历。开发这套系统的过程中,最折磨人的往往不是业务逻辑写不出来,而是环境和生态层面的问题。把这些坑记录下来,能帮你少走很多弯路。
6.1 微信小程序合法域名与真机调试的坑
微信小程序有个安全限制:线上环境中,小程序发起的网络请求域名必须是已经在微信公众平台配置过的合法域名,而且必须备案、必须HTTPS。开发阶段可以在开发者工具里勾选"不校验合法域名",但真机预览时,如果不做这个配置,请求全部被拦截。我第一次用真机调试时,所有接口都报"request:fail url not in domain list",整整卡了半天才反应过来是域名配置问题。
解决方案是:后端服务配置HTTPS证书,域名走Nginx反向代理,然后在微信公众平台的小程序后台添加request合法域名。这里还要注意,开发环境、测试环境、生产环境的域名都要提前规划好,我因为一开始没有统一域名,小程序端把localhost和服务器IP都写进了代码,上线前全部要改成正式域名,改配置本身不麻烦,麻烦的是排查隐藏在各个页面里写死的请求地址。后来我把所有请求统一封装到一个request.js文件,baseUrl从全局配置读取,换环境只需要改一处。
6.2 跨域、会话与事务的连环坑
后端接口在本地开发时,Vue管理端跑在localhost:8080,后端跑在localhost:8081,浏览器跨域问题直接拦住了所有请求。Spring Boot的跨域配置需要在WebMvcConfigurer里实现addCorsMappings,允许所有来源(开发环境)和特定来源(生产环境)。这个配置本身很简单,但有一个隐蔽的坑:如果同时配置了拦截器,拦截器里对OPTIONS预检请求的处理不当,跨域请求就会被拦在拦截器层,我调试时发现后端日志里没有任何报错,但前端就是显示请求失败,就是因为拦截器没有放行OPTIONS请求。
事务处理也是一个重灾区。订单完成这个操作,涉及更新订单状态、生成积分流水、更新用户积分余额、计算回收员收益,这四步必须在一个事务里完成。我用@Transactional注解包裹了整个service方法,但我一开始犯了个错误:把事务注解写在了controller层。Spring的注解事务默认只在service层方法上生效,控制器层的注解根本不会开启事务,导致测试时发现数据出现了部分更新的情况——订单状态改了但积分没发。发现问题后我把注解移到service实现类上,并且检查了类是否被Spring扫描,这一块的教训是:事务边界必须画在业务方法上,而且要确保方法不是私有方法,因为Spring AOP代理无法拦截私有方法。
6.3 数据库初始化和版本管理的经验
这套系统交付后,别人在本地部署时需要初始化数据库。我准备了一份完整的SQL脚本,包含建表语句、基础数据(管理员账号、默认品类、初始参数)。但第一次给别人调试时出了一个尴尬的情况:对方导入SQL脚本后,系统可以登录,但订单列表打开直接报错,排查了半天发现是MyBatis Plus的自动填充字段没有生效。问题出在我的实体类里用@TableField(fill = FieldFill.INSERT)标记创建时间字段,但没配置MetaObjectHandler实现类,导致insert时createTime字段为null,而数据库该字段设置了NOT NULL约束,insert直接报错。这个问题的根因在于:本地开发时我用的数据库是自己手动加过默认值的,换到对方环境时数据库是严格按SQL脚本来的,差异一下子暴露了。所以经验是:SQL脚本里的字段默认值一定要和代码逻辑一致,不能依赖开发环境的隐藏状态。
数据库版本管理方面,我推荐至少用Flyway或者手工维护一套带版本号的SQL脚本目录。这套系统的迭代过程中,我新增了积分商城表、修改过积分流水表的索引,如果没有版本管理,就很难追踪哪些环境跑了哪些迁移脚本。我用的是最简单的手工版本目录,每个脚本以V加版本号命名,配合一个执行记录表,虽然简陋但够用。
7. 旧衣回收业务特有的设计细节:称重、定价与积分策略
前面几章主要讲技术实现,这一章聊一点业务层面的思考。做旧衣回收系统,技术只是载体,真正决定用户愿不愿意用的,是积分定价策略和业务流程设计。这部分的经验来自和回收员、社区运营人员实际聊天收集到的反馈。
7.1 定价策略与积分换算
旧衣回收的价值评估是一个模糊地带。衣物新旧程度差异极大,不可能像废纸废铁一样按品类统一定价。我设计的方案是:按品类设定基础积分单价,衣物按重量计算积分,鞋包按件数计算积分,同时管理员在后台可以对大件物品(比如被褥)手动调整积分系数。这个方案的优势在于兼顾了系统自动化程度和灵活性,运营人员可以根据活动调整某个品类的积分倍数,比如开学季做旧书衣物回收专场。
积分和现金的换算关系,我设置了100积分等于1元。用户在积分商城兑换礼品时,直接用积分数额标价,后台订单模块生成兑换记录。为什么不用虚拟币而是积分?因为积分的心理账户更偏向奖励性质,用户感知上更正面,而且积分不属于货币体系,合规风险小很多。这个设计在跟运营朋友确认后,也验证了可行——很多回收平台的积分商城都是这么玩的。
7.2 回收员端的工作路径优化
回收员的用户体验直接影响回收效率。我在做回收员端时,遵循了一个原则:每一步只需要一个动作。接单只需要点击"接单",上门前只需要点击"导航"按钮唤起地图,到达后点击"已到达"告知用户,称重后输入实际重量和拍照上传,点击"确认回收"完成订单。核销、称重、拍照这三个动作是关键路径,我专门做了页面流转优化,从接单列表进入订单详情,按钮顺序自上而下,回收员不用过脑子就能完成操作。
回收员接单这块,早期版本是纯指派模式:管理员在后台把订单指派给某个回收员。但实测发现,这种模式对回收员不友好,他可能正在片区的另一头收衣服,还要专程跑一趟。后来我改造成了抢单模式:回收员端展示自己所在区域的新订单,按距离排序,点击抢单。这个改动很受回收员欢迎,但对系统增加了压力——同一笔订单被多个回收员同时抢时,必须用数据库乐观锁防止重复抢单。我用订单表的version字段实现乐观锁,更新时校验version一致才允许更新,否则提示"手慢了,订单已被接走"。
7.3 异常订单与用户申诉的处理
回收过程中不可避免会出现纠纷:用户觉得称重少了,或者预约了时间但回收员没来。为此我设计了申诉模块:用户可以在订单详情页发起床申诉,填写文字说明和图片证据,后台管理人员在申诉管理列表中处理,处理结果有三种:维持原判、补发积分、取消订单。这个模块的代码量不大,但业务价值很高,它给了用户一个反馈出口,而不是让用户在微信群里找客服,对平台形象很重要。
处理申诉的后端逻辑有几点要注意:申诉单和订单是多对一的关系,一个订单只能有一个进行中的申诉,所以用户表结构里我用了orderId + status的唯一索引来防止重复申诉;处理申诉时如果结果是补发积分,生成积分流水的关联对象是申诉单而不是原始订单,保证对账时能说清楚这笔积分是补偿行为。
8. 项目部署上线与后续扩展的思路
最后聊一下部署方案和扩展方向。这套系统完整跑通之后,部署上线并不复杂。后端Spring Boot打成jar包,扔到服务器上用systemd管理,Nginx做HTTPS反向代理和静态资源服务;Vue管理端执行build命令生成dist静态文件,放在Nginx的站点目录;小程序端在微信开发者工具里上传代码,提交审核后发布。整个部署链路里,最常见的坑是HTTPS证书配置和Nginx的proxy_pass路径问题。
8.1 Nginx反向代理与HTTPS配置要点
Nginx配置里,location /api/是一个特殊的代理路径,需要配合rewrite去掉前缀,否则后端接口路径会变成/api/user/login,而后端实际接收的还是/user/login。当时我在Nginx配置里漏了rewrite,调试了一个晚上,后端始终报404。后来用curl命令手动测试后端接口是通的,再curl http://localhost通过Nginx访问就是404,定位到是路径重写问题,加上rewrite之后立刻解决。
HTTPS证书我用了现成的免费证书方案,证书申请后配置到Nginx里。这里提醒一下,小程序合法域名对HTTPS证书的校验非常严格,证书过期或者链不完整都会导致请求失败,上线前一定要测试一下证书链是否完整。对于有真实业务量预期的小程序,建议预留一些运维预算,证书到期前做好续期提醒,避免线上事故。
8.2 功能扩展的三个方向
这套系统做完之后,我有几个明确的扩展方向。第一个是增加支付能力:目前回收完成后给用户发放的是积分,但如果用户选择现金回收模式,就需要在订单完成时把钱打到用户微信钱包里,这涉及微信支付商户平台的JSAPI下单接口和回调处理,是整个系统商业化的关键一步。第二个是数据报表的图形化展示:目前后台的数据报表是简单的表格展示,后续可以引入ECharts做回收量趋势图、区域分布图、品类占比图,让运营决策更直观。第三个是消息通知的升级:目前的订阅消息只能发送一次性通知,低频场景够用,但真正要做活动运营,需要整合公众号模板消息或者小程序长期订阅消息,触达能力会更强。
8.3 关于交付物中的源码、文档与调试
标题里提到源码、文档、调试,这些是项目交付的重要质量保障。源码的组织要保证别人拿到之后能直接跑起来,所以我特别重视README和部署文档的编写。部署文档里要写清楚:JDK版本(我用的JDK 8)、Maven配置、Nacos或application.yml里的数据库连接、Redis地址、微信小程序AppID配置位置、文件上传路径配置。文档写得越细,别人部署越省心,体现的是项目工程化的成熟度。
调试这一块,我把开发过程中常用的Postman接口测试集合也一并导出了,里面包含了所有接口的请求示例、请求头、参数说明,新人在调试时直接导入Postman就能开始联调,不需要翻文档写请求。这套工作流经验,我建议每个项目都养成习惯:接口文档和测试集合跟着代码版本走,发布时一起更新,省去大量沟通成本。
做这个旧衣回收系统,让我对"麻雀虽小五脏俱全"有了更深的体会。虽然业务场景不算复杂,但从需求分析、表结构设计、接口定义、三端联调到部署上线,每一个环节都有它自己的脾气。尤其是微信生态下的小程序开发,有很多平台层面的约束是纯后端思维看不到的。把这一整条链路走下来之后,再去看任何一套前后端分离的系统,心里都会有一个清晰的框架。如果这篇文章能让你在动手之前少踩几个坑,那写它的目的就达到了。