每年毕业季,计算机专业的学生都要面对一个经典难题:毕业设计到底选什么题目。选图书管理系统太老套,选电商项目已经烂大街,选纯人工智能又怕自己啃不下来。但有一个方向,既能覆盖主流技术栈,又有真实业务场景,还能把 AI 大模型这个热点真正用起来——基于 SpringBoot + 微信小程序 + AI 的智能快递代收系统。
这个选题的逻辑很简单:快递代收不是一个虚构的业务,而是你几乎每天都会接触的真实场景。小区驿站、学校快递点、公司前台,到处都有“快递到了没人管、取件排长队、拿错快递扯皮”的问题。而 AI 大模型在这个系统里也不是为了“蹭热点”,它能落在智能客服、面单识别、取件核验等几个具体环节上。这篇文章会完整拆解这个系统的架构设计、数据库建模、后端接口、小程序端实现,以及 AI 能力的接入方式,帮助你理解一个毕业设计项目从 0 到 1 的完整过程。
全文会按照“选题判断 — 架构设计 — 数据库建模 — 后端实现 — 小程序实现 — AI 落点 — 联调验证 — 常见问题 — 工程建议”的顺序展开。无论你是正在选题的学生,还是想做一个能写进简历的实战项目,这篇文章都值得收藏慢慢看。
1. 为什么快递代收系统值得作为毕业设计选题
1.1 选题的三个判断标准
选毕业设计题目,很多人只看“这个技术新不新”,却忽略了三个更重要的标准:技术覆盖度、业务真实度、可完成度。
技术覆盖度决定了答辩时有没有内容可讲。一个快递代收系统天然包含微信小程序前端、SpringBoot 后端、MySQL 数据库、Redis 缓存,再加上 AI 大模型接口对接,几乎覆盖了企业级应用的主流技术栈。评委老师问前端有答案,问后端有答案,问数据库设计有答案,问 AI 如何落地也有答案。
业务真实度决定了系统有没有实际价值。图书管理、学生选课这类系统,业务逻辑相对简单,做完以后也很难拿去实际使用。快递代收则不同,它的业务链路非常清晰:快递到达 → 扫码入库 → 通知用户 → 用户代收或预约取件 → 身份核验 → 取件出库。每一个环节都有明确的状态流转和异常场景,这样的系统做出来,是真的可以部署到一家驿站去试运行的。
可完成度决定了你能不能按时毕业。这个项目的难点分布合理,一个人在一到两个月内可以完成,不会像某些纯算法课题那样,光是复现论文就耗掉半年。简单说,这是一个“投入产出比”很高的选题。
1.2 AI 不是噱头,而是业务必须
很多学生在毕业设计里加入 AI,做法是放一个“通过 TensorFlow 训练 MNIST 手写数字识别模型”,这个模型和业务系统毫无关系,答辩时老师一问就露馅。
智能快递代收系统的 AI 落点则不同。它有至少三个环节是真正需要 AI 能力的:
- 智能客服:用户在小程序里输入“帮我查一下尾号 1234 的快递到没到”,系统需要理解这句话的意图,提取“尾号 1234”这个查询条件,再结合业务数据返回结果。
- 面单 OCR 识别:快递员或管理员拍照录入快递面单,传统做法是手工输入运单号、收件人姓名、电话、地址,效率低且容易出错。用 OCR 技术可以自动提取面单文字,再通过大模型做信息结构化。
- 取件身份核验:用户到店取件时,需要输入取件码或扫码验证。更进一步的方案是做人脸比对,但这个必须经过用户授权并符合合规要求。
这意味着 AI 不是系统之外的装饰品,而是嵌在业务闭环里的必要环节。就算只做了智能客服这一个落点,也足够说明你理解了 AI 应用的完整链路。
1.3 这个系统做完能拿到什么
从实际收益看,完成这个系统后,你至少能收获三样东西:一套完整的业务系统源码、一份可以直接写进简历的项目描述、一个可以在毕业答辩中讲清楚“大模型怎么落地”的真实案例。如果你的项目还把 AI 客服、OCR 识别、订阅消息通知都做了,那么这个项目的含金量会超过大多数管理系统类毕设。
2. 系统总体架构与核心功能拆解
2.1 技术架构分层
整个系统建议采用前后端分离加小程序端的架构,可以分为五层:
| 层级 | 技术选型 | 职责说明 |
|---|---|---|
| 用户端 | 微信小程序(原生或 uni-app) | 用户登录、查快递、一键代收、预约取件、消息通知 |
| 管理端 | Web 管理后台(可选 Vue 或简单页面) | 快递入库、出库核销、库存管理、数据统计 |
| 后端服务 | SpringBoot + MyBatis-Plus | 业务逻辑、接口鉴权、状态流转、AI 服务封装 |
| 数据层 | MySQL + Redis | MySQL 存业务数据,Redis 存验证码、高频查询缓存 |
| AI 能力层 | 大模型 API、OCR API 或本地模型 | 智能客服、面单识别、身份核验与文案生成 |
这里要说明一点:AI 能力层不要直接写死在小程序业务代码里,而是通过后端单独封装一个AiService,对外提供统一的方法。这样切换模型或服务商时,只需要修改这一个类,不会影响其他业务。
2.2 角色与功能模块
系统的角色主要有三类:普通用户、驿站管理员、系统管理员。
普通用户在小程序端可以完成:微信授权登录、查看快递到达通知、一键代收、预约取件、取件码查看、在线咨询智能客服。驿站管理员在管理端可以完成:快递入库登记(手工或 OCR)、货架位置分配、出库核销、逾期快递处理。系统管理员则负责账号管理、数据统计和系统参数配置。
从功能模块上划分,核心模块包括:用户登录模块、快递管理模块、代收与取件模块、通知消息模块、AI 智能客服模块。其中快递管理模块是整个系统的状态中枢,下面单独说明。
2.3 核心业务流程与状态设计
快递从入库到出库,至少要经历以下状态:
已入库 → 已通知 → 已代收/待取件 → 已取件 ↓ 已逾期数据库里建议用状态字段表示,例如0-待入库、1-已入库、2-已通知、3-已代收、4-已取件、5-已逾期。状态流转变更时,需要记录操作人、操作时间,方便后续追溯。很多初学者在写快递系统时,只用一个布尔字段表示“有没有取走”,这样遇到“用户没收到通知”“快递超时未取”“代收后又被取走”等场景就完全没法处理。
3. 数据库设计:快递代收业务如何建模
3.1 核心表结构概览
数据库设计是毕业设计答辩中老师非常喜欢追问的部分。建议至少设计以下五张核心表:
| 表名 | 用途说明 |
|---|---|
| t_user | 存储小程序用户信息 |
| t_express | 存储快递单信息,是整个系统的核心表 |
| t_pickup_record | 存储取件记录,每次取件留痕 |
| t_notice | 存储通知记录,包括订阅消息推送状态 |
| t_admin | 存储管理员账号信息 |
3.2 关键表设计说明
t_express快递单表是整个系统的核心,字段设计要注意几个关键点:
CREATE TABLE `t_express` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tracking_no` varchar(64) DEFAULT NULL COMMENT '快递运单号', `express_no` varchar(16) DEFAULT NULL COMMENT '取件码/货架码', `company` varchar(32) DEFAULT NULL COMMENT '快递公司', `receiver_name` varchar(32) DEFAULT NULL COMMENT '收件人姓名', `receiver_phone` varchar(20) DEFAULT NULL COMMENT '收件人手机号(脱敏展示)', `receiver_address` varchar(255) DEFAULT NULL COMMENT '收件地址', `status` tinyint(4) DEFAULT '1' COMMENT '状态:1-已入库 2-已通知 3-已代收 4-已取件 5-已逾期', `shelf_code` varchar(16) DEFAULT NULL COMMENT '货架位置编码', `in_time` datetime DEFAULT NULL COMMENT '入库时间', `notify_time` datetime DEFAULT NULL COMMENT '通知时间', `out_time` datetime DEFAULT NULL COMMENT '出库时间', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_status` (`status`), KEY `idx_phone` (`receiver_phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='快递单表';这里最容易忽略的是索引设计。如果快递数量达到几万条,按手机号查、按状态筛选就会变慢。给status和receiver_phone建索引,是成本最低的优化方式。
t_pickup_record取件记录表用来记录每次取件的详细信息:
CREATE TABLE `t_pickup_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `express_id` bigint(20) NOT NULL COMMENT '快递单ID', `user_id` bigint(20) DEFAULT NULL COMMENT '取件用户ID', `pickup_code` varchar(16) DEFAULT NULL COMMENT '取件码', `verify_type` tinyint(4) DEFAULT '1' COMMENT '核验方式:1-取件码 2-扫码 3-人脸', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_express_id` (`express_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='取件记录表';3.3 敏感信息处理原则
需要注意,快递面单上包含收件人姓名、电话、地址,属于敏感个人信息。在设计时要注意以下几点,这也是答辩加分项:
- 列表页和前端展示时,手机号要脱敏,例如显示为
138****1234。 - 数据库中的姓名、电话、地址,有条件时可以使用加密存储,或至少做好权限隔离。
- 调用 AI 模型接口时,不能把完整地址和电话直接发给外部服务。如果确实需要识别面单,应做到数据最小化,并在识别完成后及时清理。
- 涉及用户授权、人脸核验等功能,必须获得用户的明确授权,并保留授权记录。
4. SpringBoot 后端环境准备与工程搭建
4.1 版本选择建议
SpringBoot 的版本是新手最容易踩坑的地方。很多网上的教程用的是 SpringBoot 2.x,但你从 Spring Initializr 或 IDEA 里新建项目时,默认很可能已经是 3.x。
SpringBoot 3.x 比较大的变化是javax.servlet包迁移到了jakarta.servlet,一些老版本的 MyBatis-Plus、PageHelper 等工具在没有升级时会出现兼容性问题。如果你参考的教程以 2.x 为主,建议直接指定 SpringBoot 2.7.x 版本,把项目先跑通,再考虑升级到 3.x。
推荐环境如下,具体小版本以实际为准:
- JDK:1.8 或 11,SpringBoot 3.x 需要 JDK 17 以上
- Maven:3.6 以上
- 数据库:MySQL 5.7 或 8.0
- 缓存:Redis 5.x 以上(可选)
- 开发工具:IDEA + 微信开发者工具
4.2 创建工程与 Maven 依赖
创建一个 SpringBoot 工程后,核心依赖可以参考下面的pom.xml片段,实际使用时请以你自己的项目为准:
<!-- pom.xml --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>需要注意,MyBatis-Plus 的版本和 SpringBoot 版本要匹配。如果你的项目使用 SpringBoot 3.x,需要选择 mybatis-plus-spring-boot3-starter,而不是旧的mybatis-plus-boot-starter。这是常见的“SpringBoot 版本太高导致项目启动失败”的原因之一。
4.3 application.yml 配置
在src/main/resources/application.yml中,基础配置如下:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/express_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 servlet: multipart: max-file-size: 10MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 wx: appid: your-appid secret: your-app-secret llm: api-url: https://your-llm-api.example.com/v1/chat/completions api-key: your-api-key model: your-model-name注意,wx.appid、wx.secret、llm.api-key这类敏感配置不能提交到 Git 仓库。实际项目中使用application-prod.yml和application-dev.yml做环境隔离,或者使用配置中心统一管理。答辩时主动提到这一点,会让人觉得你有生产环境意识。
5. 后端核心接口设计与实现
5.1 微信登录接口
小程序端登录的主流流程是:小程序调用wx.login()获取临时code,后端拿着code调用微信的code2Session接口,换取用户的openid。
// 文件路径:src/main/java/com/example/express/controller/UserController.java @RestController @RequestMapping("/api/user") public class UserController { @Autowired private UserService userService; @PostMapping("/login") public Result login(@RequestBody LoginRequest request) { // 1. 使用 code 换取 openid String openid = userService.code2Session(request.getCode()); // 2. 根据 openid 查找用户,不存在则自动注册 User user = userService.findOrCreate(openid); // 3. 生成登录态 token 返回给小程序 String token = JwtUtil.generateToken(user.getId()); return Result.success(token); } }这里要强调的是,openid是用户在某个小程序下的唯一标识,同一个用户在不同小程序下openid不同。不要把openid当作全局用户 ID 直接展示给前端。更推荐的做法是后端自己生成userId,并把openid和userId做一一映射。
5.2 快递入库接口
快递入库是整个业务流的第一环。管理员在小程序端或管理后台录入快递信息,后端生成取件码,并把快递状态设为“已入库”。
// 文件路径:src/main/java/com/example/express/controller/ExpressController.java @RestController @RequestMapping("/api/express") public class ExpressController { @Autowired private ExpressService expressService; @PostMapping("/inbound") public Result inbound(@RequestBody ExpressInboundDTO dto) { // 1. 根据收件人手机号匹配用户 User user = userService.findByPhone(dto.getReceiverPhone()); // 2. 生成取件码,例如 4 位随机数字 String expressNo = RandomUtil.randomNumbers(4); // 3. 保存快递单,状态为已入库 Express express = expressService.inbound(dto, user, expressNo); // 4. 发送微信订阅消息通知用户 noticeService.sendExpressArrivedNotice(user, express); return Result.success(express); } }这个接口需要处理一个常见场景:收件人还没有注册过小程序,此时findByPhone返回 null。这种情况下不能直接报错,而是要把快递和手机号绑定,等用户登录后通过手机号匹配到自己的快递。在设计用户表时,最好给手机号字段建唯一索引。
5.3 取件核销接口
取件核销是防止快递被错拿的关键环节。最简单的做法是校验取件码,同时为防止用户到达驿站后忘记取件码,也可以支持扫码核销。
@PostMapping("/pickup") public Result pickup(@RequestBody PickupRequest request) { // 1. 查询快递单 Express express = expressService.getById(request.getExpressId()); if (express == null) { return Result.error("快递单不存在"); } // 2. 校验状态,只有已通知或已代收状态可以取件 if (express.getStatus() != ExpressStatus.NOTIFIED.getCode() && express.getStatus() != ExpressStatus.RECEIVED.getCode()) { return Result.error("当前状态不允许取件"); } // 3. 校验取件码 if (!express.getExpressNo().equals(request.getPickupCode())) { return Result.error("取件码不正确"); } // 4. 更新状态并记录取件流水 expressService.pickup(express, request); return Result.success(); }这里最重要的是状态机校验。如果不对状态做判断,就可能出现“已经取走的快递再次被取走”的漏洞。很多刚工作的开发者在实际项目里也是因为状态判断不严格,导致数据错乱,所以在毕业设计里把状态机设计清楚,是非常明显的加分项。
6. 微信小程序端核心功能实现
6.1 登录流程实现
小程序端使用原生 JavaScript 实现登录,核心代码位于pages/login/login.js:
// 文件路径:miniprogram/pages/login/login.js Page({ data: { isLogin: false, userInfo: {} }, onLoad() { this.checkLoginStatus(); }, checkLoginStatus() { const token = wx.getStorageSync('token'); if (token) { this.setData({ isLogin: true }); } else { this.doLogin(); } }, doLogin() { wx.login({ success: (res) => { if (res.code) { wx.request({ url: 'https://your-domain.com/api/user/login', method: 'POST', data: { code: res.code }, success: (response) => { const { token } = response.data.data; wx.setStorageSync('token', token); this.setData({ isLogin: true }); }, fail: (err) => { console.error('登录失败', err); wx.showToast({ title: '登录失败,请重试', icon: 'none' }); } }); } } }); } });需要注意的是,wx.login返回的code有效期很短,且只能使用一次。如果后端返回“登录失败”,不要反复重试同一个code,而应该重新调用wx.login获取新的code。这个小细节,在很多真实项目里也容易踩坑。
6.2 快递列表与一键代收
用户登录后,首页会展示绑定手机号的所有快递。核心是通过手机号查询快递,并在快递详情页显示“一键代收”按钮。
// 文件路径:miniprogram/pages/express/express.js Page({ data: { expressList: [], loading: false }, onShow() { this.loadExpressList(); }, loadExpressList() { const token = wx.getStorageSync('token'); this.setData({ loading: true }); wx.request({ url: 'https://your-domain.com/api/express/list', method: 'GET', header: { Authorization: `Bearer ${token}` }, success: (res) => { const list = res.data.data || []; this.setData({ expressList: list, loading: false }); }, fail: () => { this.setData({ loading: false }); wx.showToast({ title: '加载失败', icon: 'none' }); } }); }, onReceiveExpress(e) { const id = e.currentTarget.dataset.id; wx.showModal({ title: '确认代收', content: '确认代收该快递吗?请确保快递已到达驿站。', success: (res) => { if (res.confirm) { this.receiveExpress(id); } } }); }, receiveExpress(id) { const token = wx.getStorageSync('token'); wx.request({ url: 'https://your-domain.com/api/express/receive', method: 'POST', header: { Authorization: `Bearer ${token}` }, data: { expressId: id }, success: (res) => { wx.showToast({ title: '代收成功', icon: 'success' }); this.loadExpressList(); } }); } });“一键代收”这个动作在业务上意味着用户远程确认快递可以放在代收点。这里后端要注意:不是所有快递都可以直接代收,生鲜、贵重物品、到付件等类别可能需要强制当面验收,所以快递表里最好有一个goods_type字段,代收前做拦截判断。
6.3 订阅消息通知
快递到达后要通知用户,微信小程序目前常用的方案是订阅消息。用户需要先授权订阅,授权后小程序可以向用户推送一次消息。
// 文件路径:miniprogram/pages/express/detail.js Page({ onLoad(options) { this.expressId = options.id; this.requestSubscribe(); }, requestSubscribe() { wx.requestSubscribeMessage({ tmplIds: ['your-template-id'], success: (res) => { // res['your-template-id'] === 'accept' 表示用户同意订阅 console.log('订阅结果', res); }, fail: (err) => { console.error('订阅消息授权失败', err); } }); } });这里要提醒一下:微信订阅消息的模板 ID 需要在微信公众平台申请,并且一次订阅只能发送一次通知。如果用户每次进入页面都弹授权框,体验会非常差。更合理的做法是,在快递到达并入库后,用户需要点击“开启取件通知”按钮时才触发订阅授权,这样既合规,用户体验也更好。
7. AI 大模型能力的三个业务落点
7.1 智能客服:自然语言查快递
这是最能体现 AI 大模型价值的功能。传统查快递需要输入手机号或运单号,而有了大模型,用户可以直接输入:“我有个快递到了吗?手机尾号 6688 的。”
后端实现思路是:把用户输入交给大模型,让模型从自然语言中抽取查询条件,然后再去数据库里查。也可以用“工具调用”的方式,让模型判断需要调用哪个查询函数、参数是什么。
// 文件路径:src/main/java/com/example/express/service/AiSupportService.java @Service public class AiSupportService { @Value("${llm.api-url}") private String apiUrl; @Value("${llm.api-key}") private String apiKey; @Value("${llm.model}") private String model; private final RestTemplate restTemplate = new RestTemplate(); public String chatWithExpressQuery(String userMessage, String openid) { // 1. 组装大模型请求 String prompt = buildPrompt(userMessage); Map<String, Object> requestBody = new HashMap<>(); requestBody.put("model", model); requestBody.put("messages", List.of( Map.of("role", "system", "content", "你是快递代收系统的智能助手,可以从用户话术中提取查询条件。"), Map.of("role", "user", "content", prompt) )); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers); ResponseEntity<Map> response = restTemplate.postForEntity(apiUrl, request, Map.class); // 2. 解析模型返回的查询条件 String queryCondition = parseQueryCondition(response.getBody()); // 3. 根据条件查询快递业务数据 List<Express> expressList = expressService.queryByCondition(queryCondition, openid); // 4. 将查询结果整理后返回给模型生成回复 return buildReply(expressList); } }要注意,调用大模型接口必须放在服务端完成,不能在小程序端直接调用,否则 API Key 会被暴露。另外,AI 接口调用可能超时或失败,必须有降级方案。最简单的方式是捕获异常后返回一个固定提示:“当前智能助手繁忙,请稍后重试或使用取件码查询。”
7.2 面单 OCR 识别
快递面单上有运单号、收件人、电话、地址等信息,完全手输效率太低。OCR 识别流程是:管理员在小程序或管理端拍摄面单照片,上传到后端,后端调用 OCR 服务识别文字,再通过规则或大模型把文字整理成结构化数据。
这里注意两个问题。第一,面单图片包含敏感个人信息,识别完成后应尽快删除原图或加密存储。第二,OCR 识别结果不一定 100% 正确,系统必须提供“人工校对”页面,让管理员快速修正识别结果后再入库。
7.3 取件身份核验与智能提醒
在取件环节,除取件码外,还可以加入 AI 能力做“智能风险控制”。例如,当某个手机号短时间内多次取件、或取件时间异常时,提醒管理员进行二次核验。这个规则既可以写在代码里,也可以通过大模型对取件日志进行分析,给出风险建议。
此外,AI 还可以用于生成更人性化的提醒文案。传统系统只会发一条固定模板:“您的快递已到,请凭取件码 1234 取件。”而基于大模型,可以根据快递类型、天气、用户习惯生成不同的话术。例如:“您的快递已入库,货架 A-12。今天下雨,来取件记得带伞。”这种细节虽然不会增加核心功能,但放在论文里作为 AI 应用场景,观感会好很多。
8. 完整联调与运行验证
8.1 后端启动与接口验证
后端启动前,先确认 MySQL 中已经创建数据库并执行了初始化 SQL。启动命令很简单:
mvn spring-boot:run启动成功后,控制台会显示 Tomcat started on port 8080。接着可以用 curl 验证登录接口:
curl -X POST http://localhost:8080/api/user/login \ -H "Content-Type: application/json" \ -d '{"code": "test-code"}'如果返回了包含 token 的数据,说明后端接口基本正常。注意,使用测试 code 会走到微信接口并返回错误,所以这个 curl 命令主要用于验证接口连通性和参数传递,真正登录时需要在微信开发者工具中触发。
8.2 小程序端联调
在微信开发者工具中导入小程序项目,需要修改项目中的app.js或请求封装文件里的baseUrl,指向你自己的后端地址。开发环境下可以直接使用局域网 IP,例如http://192.168.1.100:8080,但要注意在开发者工具中勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”,否则请求会被拦截。
这里要强调:这个“不校验合法域名”选项只用于本地开发调试。上线后,小程序要求所有请求域名必须为 HTTPS 且在微信公众平台配置白名单,否则正式版小程序无法访问。这个规范必须在论文的“部署与配置”章节中写清楚。
8.3 业务闭环验证
完整验证流程建议按下面顺序执行:
- 管理员登录管理端,录入一条测试快递,手机号填写自己正在调试的微信号绑定的手机号。
- 在小程序端触发订阅消息授权,后端发送快递到达通知。
- 小程序首页显示该快递,点击“一键代收”,后端将状态改为已代收。
- 到“取件记录”页面模拟取件,输入取件码,确认状态变为已取件。
- 打开智能客服,输入“我的快递到哪了”,确认可以返回快递状态。
如果以上 5 步全部通过,说明这个业务闭环是完整的。
9. 常见问题与排查思路
下面是这个项目中高频出现的问题,按“现象 — 原因 — 排查 — 解决”整理,方便你在开发或答辩前快速定位。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 后端启动失败,报 javax.servlet 不存在 | SpringBoot 3.x 使用了 jakarta 包,项目代码仍引用 javax | 查看启动日志和依赖树 | 将 SpringBoot 降级到 2.7.x,或统一把所有 javax 改成 jakarta |
| 小程序登录失败,报错形如 wx1cb4398e1413dce7 | appid 与 secret 不一致、code 过期、接口参数错误 | 检查小程序后台的 appid 与 secret,确认后端日志中 code2Session 的返回结果 | 核对配置,重新获取 code 再调用 |
| wx.request 请求后无响应 | 后端地址是 HTTP,未勾选“不校验合法域名” | 查看微信开发者工具的 Network 面板 | 本地调试时勾选不校验合法域名,生产环境配置 HTTPS 域名 |
| 订阅消息发送失败 | 模板 ID 未审核、用户未授权订阅、类目不匹配 | 查看后端日志中微信接口返回的 errcode | 检查模板 ID 和类目审核状态,重新触发授权订阅 |
| MyBatis-Plus 自动填充时间不生效 | 没有配置 MetaObjectHandler 或版本不兼容 | 检查控制台 SQL 是否包含 create_time | 添加自动填充处理器,或手动维护时间字段 |
| 大模型接口调用超时 | 外部 API 响应慢、网络不稳定 | 查看调用耗时和超时配置 | 设置合理的超时时间,增加降级返回逻辑 |
| Redis 连接失败 | Redis 未启动,或密码配置错误 | 检查 Redis 服务状态,用 redis-cli 测试连接 | 启动 Redis 或修改连接配置 |
如果你遇到微信小程序相关的登录报错,建议按这个顺序排查:先看小程序后台的 appid 是否和后端配置一致,再看 code 是否是一次性的新 code,最后看后端调用微信接口时返回的 errcode。绝大多数登录问题都出在这三个环节。
10. 工程建议与答辩加分思路
10.1 工程化建议
虽然是毕业设计,但代码风格会直接影响老师的第一印象。建议做到以下几点:
- 统一返回结果结构,使用类似
Result.success(data)的封装,而不是每个接口返回不同的 Map。 - 使用全局异常处理器,在 Controller 层捕获业务异常,而不是把异常堆栈直接返回给前端。
- 接口鉴权统一使用拦截器或 Spring Security,小程序端请求时在 Header 中携带 token。
- 关键业务操作记录日志,尤其是入库、代收、取件、核销这类敏感操作。
- 敏感配置(数据库密码、AppSecret、API Key)使用环境变量,不要把明文提交到代码仓库。
10.2 答辩讲解建议
答辩时不要从“我用了 SpringBoot、小程序、大模型”开始讲,而是从业务痛点切入:“传统快递代收点存在通知不及时、取件流程复杂、错拿率高三个痛点,我的系统分别用订阅消息、取件码核验和 AI 客服来解决了这三个问题。”
然后展示一张业务流程图,说明数据从入库到出库的完整流转。接着再讲技术架构和关键接口实现。最后把 AI 智能客服作为亮点现场演示。哪怕只演示一个自然语言查快递的完整链路,也比讲十页 PPT 有效。
10.3 可扩展方向
如果你的时间充裕,或想让项目更有竞争力,可以考虑以下扩展方向:
- 基于 Redis 实现取件码的过期策略,比如超过 72 小时未取件自动进入“逾期”状态。
- 引入 WebSocket,在小程序端实时推送快递状态变化。
- 增加数据统计大屏,展示每日入库量、取件量、逾期率等指标。
- 将 AI 客服升级为支持语音输入,用户可以直接发语音查快递。
- 把管理端做成独立的 Vue 后台,形成“小程序 + Web 管理端 + SpringBoot 服务端”的完整前后端分离架构。
这些方向都不需要重写系统,只是在现有架构上增加模块,但对论文的“创新点”和答辩的“工作量”都有明显提升。
最后想说的是,做这种业务系统,最重要的不是炫技,而是把业务的每个状态、每个边界情况想清楚。快递代收这个场景看着简单,真正梳理起来,涉及用户身份、快递状态、消息通知、权限控制、AI 降级等多个问题,每一个都值得在论文里认真写一段。先把代码跑通,再回头优化流程、补充异常处理,你会发现这不仅是完成一个毕业设计,更像是完成了一次从需求到交付的完整训练。