这几年每到下课时分,大学校园里的快递驿站几乎都是同一副景象:排队的人从取件码货架绕到大门口,晚到一步就可能错过营业时间。取一个十几秒就能找到的包裹,真正耗掉的却是十分钟起步的等待。如果是大促期间,快递驿站会把货架塞得连落脚的地方都没有,更别说手上有好几个不同快递公司的取件码要来回找。我在设计“python快递校园帮互助微信小程序”这个题目时,脑海里最先闪过的就是这种场景——它本质上不是做一个电商后台,也不是模仿代取平台,而是把“发快递单、随手帮取、任务互换”这些行为放在校园熟人与半熟人的关系里重新组织一遍。
简单说,这个小程序解决的核心问题是:有人不方便按时取件,有人恰好要去驿站,通过一个任务撮合机制,让“取件”这件事从个人任务变成可协作的互助任务。后端用 Python 实现业务逻辑和接口,前端跑在微信小程序里,用户不用额外装 App,扫码就能用。适合做毕业设计、课程项目,或者单纯想拿微信小程序练手顺带巩固 Python 全栈能力的同学参考。
1. 这个题目真正的需求起点:驿站排队是表,时间错配是里
1.1 大学校园取件场景里的三种典型状态
我在做需求分析时没有一上来就画用例图,而是先把真实场景拆成了三类人。
第一类人是“时间被钉死”的发单者。上午满课,下课时间正好卡在实验课最后一节,等赶到驿站,快递已经入库五六个小时;有的同学周末要回家,包裹如果当天不取就可能被退回;还有一种更常见的情况是,一个人手里同时有三四个不同快递公司的取件码,今天取不了就必须明天再跑一趟。这类人不是懒,是真的在时间上错不开。
第二类人是“顺手就能帮”的接单者。住在驿站同一栋楼、同一条宿舍路线上的同学,本来每天就要去驿站拿自己的快递,多拿一件只是顺手。问题在于,他们不知道身边谁需要帮忙,谁又愿意把取件码放心交出来。因为缺少信息渠道,这份“顺手”往往就白白浪费了。
第三类人是平台维护方。在真实的校园项目里,管理员不会天天去删帖子,但必须能处理纠纷、下架异常任务、审核明显有问题的信息。尤其涉及取件码、手机号这类隐私信息时,平台需要给用户一种“系统是可控的”感觉。
1.2 用户角色与功能需求的差异
基于这三类人,我按毕业设计的常规范畴拆出了三个角色。
发单者关心的是发单效率。他们希望填表尽量少,越少越好。我把发单页设计成“快递公司下拉选择 + 取件码 + 最迟取件时间 + 送达楼栋”四件套,其他像包裹大小、快递单号后四位这种信息都不是必填项。这样中午下课到驿站门口排队时,两分钟内就能把单子发出去。
接单者关心的是接单成本。他们要看清楚自己顺不顺路、包裹大不大、时间来不来得及。所以在任务列表里,我会把“发件楼栋”和“送达楼栋”放在明显位置,包裹大小用标签而不是照片来区分,避免后续因为图片不清晰产生争议。
管理员关心的是异常处理。我不会真做一套复杂的后台管理系统,而是给管理员做一个独立的简易管理页面以及后端管理接口,能下架任务、查看用户信用状态就足够了。校园互助平台核心是服务,不是风控后台,做得过度反而会拖垮开发节奏。
1.3 先想清楚哪些功能不做
这个题最容易被带偏的地方,是写着写着就把目标从小程序互助做成了快递物流系统。我刻意避开了三块内容。
第一块是真实支付。学生之间的代取金额可能就一两块钱,接入微信支付需要商户号、结算、售后,复杂度会立刻上升;如果不接入支付而以积分或无偿互助为主,系统反而更贴合“帮互助”的定位。我在需求里把奖励设置成积分,不发钱,只在排行榜和个人主页里体现。
第二块是快递公司订单同步。接入顺丰、中通、圆通的物流接口不是不可以,但需要企业资质和真实货源,个人开发者做毕业设计几乎不可能申请下来。所以系统里的“快递公司”和“运单号后四位”只是便于发单人核对,不承担真正物流跟踪职责。
第三块是全局路径规划。驿站到宿舍楼的路线其实很固定,没必要调度车辆,也没必要做实时地图。我让步进简化成“从驿站A送到宿舍B”,发送方和接单方都对校园里的位置了然于心,地图反而显得多余。
把这三块排除之后,剩下的功能才真正服务于题目里的“帮互助”:发单、抢单、确认、评价。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Python后端与微信小程序的组合,不只是因为题目这么写
2.1 后端框架选择:Flask还是Django还是FastAPI
“Python后端”这个说法其实很宽泛。真正写代码之前,我先在 Flask、Django、FastAPI 之间做了一次直接对比,因为选错框架会直接影响后期数据库迁移和管理页面的开发量。
| 框架 | 适合场景 | 后台管理 | 上手门槛 | 我的选择理由 |
|---|---|---|---|---|
| Flask | 轻量项目、接口服务 | 需自行造或集成 | 低 | 接口少,路由清晰,适合毕业设计快速出效果 |
| Django | 重型系统、内容管理 | 自带Admin | 中 | 如果要做复杂权限和后台,很合适但偏重 |
| FastAPI | 高并发异步服务 | 需自行集成 | 中 | 性能好、文档自动生成,但异步不是这个项目的重心 |
我最终选了 Flask + SQLAlchemy。原因是我这个系统需要写的 API 数量并不多,核心接口也就是十几二十个,用 Flask 可以直接让每个业务函数保持很简单的结构。如果选了 Django,虽然省了写后台的时间,但项目结构会让想在论文里讲清楚“请求如何处理”的同学多绕一层弯。
需要补充的是,这里用了 Flask 但不意味着放弃数据库模型管理。我自己写了轻量的 Flask CLI 命令做数据初始化,把管理员账号和基础选项数据通过初始化脚本一次性写入,避免每次手工往数据库里塞数据。
2.2 前端形态:原生小程序还是uni-app
一开始我也纠结过到底用 uni-app 还是原生微信小程序。印象里每年都有不少同学在“HBuilderX开发微信小程序”“小程序a跳转小程序b要在微信公众平台上做什么操作”这类问题上卡住,归根到底不少人是被多端框架的抽象层绕晕了。
这个项目是纯微信端,不存在同时上支付宝小程序的需求,我选择原生微信小程序。原因很直接:
原生小程序里遇到问题,能找到的解决方案最多。无论是“微信小程序自定义tabbar”还是“顶部导航栏高度”这类被问爆的问题,原生教程的适用范围比 uni-app 更广。另一个原因是原生小程序的开发者工具和调试面板之间是零距离的,接口返回的数据结构能被很直观地看到,不至于出现框架做了二次封装后你还要先定位到底是谁改了参数。
如果你已经会 Vue,用 uni-app 也完全没有问题。只是要记得在微信开发者工具里打开项目时,确认项目目录指向的是包含 project.config.json 的那一层,而不是 HBuilderX 的源码根目录。真机调试时小程序的 appid 一旦对不上,所有权限接口都会报错,这不是代码问题,是工具配置问题。
2.3 接口风格:用RESTful还是简单函数式
我在设计接口时也没完全按所谓的 RESTful 宗教式标准来。前端小程序端和后端之间走 JSON,接口地址统一以 /api 开头,版本信息放在 /api/v1 下。动作语义尽量清晰,比如:
- POST /api/v1/auth/login:微信登录
- POST /api/v1/order:发布互助单
- POST /api/v1/order/
/accept:接单 - POST /api/v1/order/
/complete:确认完成
有人会批评这些动词在 REST 里不够纯正,但毕设项目的核心是别人一眼看懂这个接口是干嘛的,过于严格的资源语义会增加阅读成本。只要前端调用和后端处理能在文档里对得上,项目维护起来就顺。
3. 数据模型不是先从代码写起,而是把互助单的生命周期想透
3.1 核心表结构:用户、互助单、状态变更记录
动手建表之前,我把业务对象拆成四个核心模型。一个模型是用户 User,一个模型是互助单 HelpOrder,还有一个是订单状态变更记录 OrderLog,最后是基础配置项 Config。
用户表里有一个很容易被忽略的字段:openid。很多第一次做微信小程序的同学会把用户表主键直接设计成自己生成的 user_id,然后在登录时先查 openid 是否已存在,如果不存在就新建用户。这样设计没有问题,但要注意 openid 必须单独存字段并加唯一索引,不能拿它当逻辑主键到处用。因为后续还要维护用户积分、接单次数、记录状态变更,user_id 作为稳定主键会更合适。
互助单表是整张表里最要紧的。我把它拆成长度合理的必要字段,而不是把所有信息都塞进一个字段:
- id:自增主键
- publisher_id:发单者
- taker_id:接单者,初始为 null
- express_company:快递公司名称
- pickup_code:驿站取件码,接单后对接单者可见
- delivery_location:送达的楼栋或位置
- need_time:最晚希望送达的时间
- status:当前状态
- reward_points:额外积分
- created_time、accepted_time、completed_time
把 pickup_code 单独存并控制接口的返回逻辑是有讲究的。在任务列表中,任何人都能看到快递公司和楼栋位置,但不能直接看到取件码。只有接单者身份验证通过后,后端才会在返回订单详情时带上取件码。这样就减少了很多潜在隐私风险。
状态变更记录表也不可省略。一开始我想省掉这张表,后来发现一旦用户投诉订单,或者论文答辩时被问“这个订单是怎么从待接单变成已完成的”,没有过程记录就很难回答。每执行一个关键动作,后端就往 OrderLog 里插入一条记录,内容包含操作者ID、动作、变更前状态、变更后状态。
3.2 互助单状态机:让每一步操作都有合法的前因后果
校园互助单不像购物订单那样需要有退款、售后这些复杂状态。我归纳后的状态一共六个,刚好能覆盖完整闭环:
| 状态值 | 含义 | 可以进入的后续状态 |
|---|---|---|
| PENDING | 待接单 | ACCEPTED / CANCELLED |
| ACCEPTED | 已接单待取件 | PICKED_UP / CANCELLED |
| PICKED_UP | 已取件配送中 | COMPLETED |
| COMPLETED | 已完成 | 无 |
| CANCELLED | 已取消 | 无 |
| EXPIRED | 已超时 | 无 |
值得注意的是“取消”这个动作不是谁都能做。发单者在订单 PENDING 状态下可以取消;而一旦有人接了单,随意取消会对接单者造成伤害,此时需要双方协商后通过管理员处理,或者在规则里明确超时后自动取消。我在系统里做了一个偏保守的设置:只有发单者在 PENDING 状态能主动取消,接单后不允许前端直接提交取消操作。
超时状态我用了一个简单的定时扫描任务实现,后端每分钟查一次所有 ACCEPTED 且 need_time 已过当前时间的订单,统一标记为 EXPIRED。这里没有用复杂的消息队列,因为校园项目的并发量根本到不了需要队列的程度,一个可靠的后台定时任务就够了。
3.3 防“两个人同时抢到同一单”:事务与行锁的最小实现
从业务上看,用户点击“接单”按钮的那一瞬间,后端必须保证只有一个人能成功。最容易踩的坑是:先 SELECT 再 UPDATE。两个请求都 SELECT 到订单还是 PENDING,然后都执行 UPDATE,结果两个人都会以为自己抢单成功。
我在代码里把这个判断写成一个原子操作。SQL 层面直接更新状态,并且只更新当前状态为 PENDING 的记录:
sql复制UPDATE help_order
SET status = 'ACCEPTED', taker_id = :uid, accepted_time = NOW()
WHERE id = :order_id AND status = 'PENDING'
这条 SQL 执行之后,再检查影响行数。如果影响行数是 1,说明当前用户抢单成功;如果影响行数是 0,说明这个订单已经被其他人接走了,后端直接给前端返回“手慢了”。不需要在应用层加锁,也不用引入 Redis 分布式锁,这个方案在这种并发量级下是最直接、最不容易出错的。
为了让接单操作和日志写入保持一致性,我会把 UPDATE 和 OrderLog 的 INSERT 放在同一个数据库事务里:
python复制from flask import current_app
from extensions import db
def bind_order(order_id, taker_id):
row = db.session.execute(
db.text("""
UPDATE help_order
SET status = 'ACCEPTED',
taker_id = :taker_id,
accepted_time = NOW()
WHERE id = :order_id AND status = 'PENDING'
"""),
{"order_id": order_id, "taker_id": taker_id}
)
if row.rowcount == 0:
db.session.rollback()
return False
log = OrderLog(
order_id=order_id,
operator_id=taker_id,
action="accept",
from_status="PENDING",
to_status="ACCEPTED"
)
db.session.add(log)
db.session.commit()
return True
用事务还有一个附加好处:如果日志插入失败,订单状态不会已经变更,避免了状态已经变为已接单、但系统没有任何人接单记录这种数据的脏状态。
4. 小程序登录与鉴权:从wx.login到后端Token的完整链路
4.1 openid获取的两种方式及流程
微信小程序的登录流程,看起来就是前端调一次 wx.login,再把 code 发给后端,后端拿着 code 去微信接口换 openid。但这条链路里的每一个环节都有容易忽略的细节。
wx.login 返回的 code 有效期很短且只能使用一次。拿到 code 后要在当天用完,否则再拿同一个 code 去换 openid 会得到一个错误提示。后端拿到 code 之后,通过 requests 请求微信接口,需要带上小程序 appid 和 appsecret。这里的安全要点是:appsecret 只能保存在 Python 后端的环境变量里,绝不能被编译进小程序前端代码。
还有一种不需要后端保存 openid 的体验优化方案:在小程序端直接调用 wx.getUserProfile 获取昵称头像,把资料传给后端。但真正决定用户唯一身份的仍然是 openid,前端展示的用户资料只是用做展示。我在 User 表里既保留了 openid 作唯一标识,也保留了 nickname、avatar_url 这些冗余展示字段,避免每次打开小程序都要向微信要一次头像权限。
4.2 为什么不能让小程序端直接保存业务主键
一般小程序登录后,后端会返回一个 token,前端把 token 写入 wx.setStorageSync。之后每一次请求都带着这个 token,后端通过解析 token 拿到用户 ID。这里我不建议把 user_id 直接返回给前端并由前端在后续请求里提交,因为用户如果在开发者工具里手动改 user_id,就可能越权操作别人的订单。
我的做法是:登录成功后,Token 由 Python 端生成并返回,Token 中只包含用户身份标识和过期时间。后端在业务请求里从请求头读取 Authorization,再根据 Token 还原当前用户。为了不破坏毕业设计的精简度,我用的是 Json Web Token 的轻量方案:
python复制import jwt
import datetime
SECRET_KEY = "please-change-this-key"
def generate_token(user_id):
now = datetime.datetime.utcnow()
payload = {
"uid": user_id,
"exp": now + datetime.timedelta(days=7)
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
def parse_token(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return payload.get("uid")
except Exception:
return None
过期时间设置成 7 天,对小程序用户来说,基本可以做到一周内不用重复登录。如果对安全要求更高,可以把过期时间缩短到 2 小时并加入刷新机制,但对这个项目来说,和微信自身的静默登录配合起来,7 天是体验和安全的平衡点。
4.3 小程序请求拦截器与登录态过期处理
小程序端所有请求会统一经过一个 request 函数。我把 token 读取和失败处理放在这里,避免每一个页面都重复判断登录状态。
javascript复制function request(url, method, data) {
return new Promise((resolve, reject) => {
const token = wx.getStorageSync('token')
wx.request({
url: getApp().globalData.baseUrl + url,
method: method || 'GET',
data: data || {},
header: { 'Authorization': 'JWT ' + token },
success(res) {
if (res.data.code === 401) {
// 登录态过期,跳回登录页或重新登录
wx.removeStorageSync('token')
wx.navigateTo({ url: '/pages/login/login' })
} else {
resolve(res.data)
}
},
fail(err) {
reject(err)
}
})
})
}
微信小程序有一个容易被忽略的机制:当用户已经授权登录过一次,后续打开小程序并不会主动清理 storage。也就是说,如果后端 token 已经过期,但前端没有做 401 拦截,用户会看到一个页面永远加载不出来的假象。把 401 处理集中到一个拦截器里之后,这类问题就能被快速识别。
5. 发单到完成:前端表单、后端接口与状态联动
5.1 发单页的最小表单设计
发单页核心信息只需要四个:快递公司、取件码、最迟送达时间、送达楼栋。小程序端取件码输入框需要适当做说明,比如提示用户“取件码只在你发单后对接单者可见”。这样能显著降低用户交付取件码的心理负担。
最迟送达时间我用微信小程序提供的 picker 组件做选择,而不是让用户手填时间。这样可以统一后端的时间格式,避免有人输入“下午三点”,有人输入“15:00”这类后续难以解析的数据。
发布动作的按钮要加防重复提交。用户如果手抖点了两下发布,后端可能会生成两个一模一样的互助单。我用了一个简单的方案:按钮在点击后立即进入 loading 状态,等到后端返回结果后再恢复。
javascript复制async function submitOrder() {
if (this.submitting) return
this.submitting = true
wx.showLoading({ title: '发布中' })
try {
const res = await request('/api/v1/order', 'POST', {
express_company: this.data.expressCompany,
pickup_code: this.data.pickupCode,
need_time: this.data.needTime,
delivery_location: this.data.deliveryLocation
})
// 发布成功,回到列表页并刷新
wx.showToast({ title: '发布成功' })
wx.navigateBack()
} finally {
this.submitting = false
wx.hideLoading()
}
}
5.2 接单后取件码的可见性处理
订单在 PENDING 状态下,后端返回给前端的订单列表不能包含 pickup_code 字段。前端即使写了读取取件码的逻辑,也拿不到内容。当用户点击“接单”并且后端校验成功之后,前端可以跳转到订单详情页,这时后端订单详情接口会根据当前用户的身份决定是否返回取件码。
后端权限控制的判断逻辑并不复杂:
python复制if current_user.id == order.taker_id and order.status in ("ACCEPTED", "PICKED_UP"):
data["pickup_code"] = order.pickup_code
else:
data["pickup_code"] = None
这样设计之后,即使有同学通过请求篡改为管理员身份,后端也能根据订单归属拒绝返回取件码。数据安全不能只依赖前端隐藏,后端接口必须同样做身份校验。
5.3 确认完成与积分结算
接单者把包裹放到发单者指定的宿舍楼下或者当面交接后,发单者点“确认完成”,后端把订单状态改成 COMPLETED,同时给接单者增加积分。这里有一点要处理:积分结算必须和状态更新放在一个事务里。不然订单已经完成但积分没有到账,用户就会来投诉。
如果没人主动确认完成,系统不能一直卡着。我设置了一个非常直观的办法:订单状态变为 PICKED_UP 后,系统会生成一个确认时间段,超过这个时段后,如果发单者没有主动完成,系统会自动将订单标记为 COMPLETED 并结算积分。这样做不是为了偏袒一方,而是为了避免大量僵尸订单堆积在后台。
当然,发单者也可以申请发起争议。遇到争议订单时,订单状态会变成一种 REFUNDING 状态,这个状态不再参与正常的自动结算,而是留给管理员处理。毕业设计做到这个程度,已经比大多数固定状态流转订单系统完整得多。
6. 小程序订阅消息:让接单反馈主动找到用户
6.1 微信订阅消息的一次性授权逻辑
校园互助类系统非常依赖“有人接单了”“快递已经送达”这类事件通知。但微信小程序的通知机制和 Web 站点的消息推送差距很大:小程序必须使用“订阅消息”,并且一次订阅只能发给用户一条消息,除非用户勾选了“总是保持以上选择,不再询问”。
我在需求设计时把订阅消息的使用场景严格控制在三个:
- 发单者发布订单后,订阅“有人接单”通知
- 接单者接单后,订阅“发单者确认完成、积分配到账”通知
- 发单者订单即将超时前,订阅“订单即将超时”通知
如果让用户订阅太多无用通知,用户会拒绝授权,后续任何消息都发不出去。正确做法是在具体场景触发前一秒请求授权,比如发单者点击“发布订单”按钮时,表单数据已经校验通过,弹窗请求订阅“有人接单”。用户如果拒绝,不影响发单,只是接单者接单后用户无法收到即时通知,需要主动打开小程序查看。
6.2 后端发送订阅消息的代码结构
后端发送订阅消息,需要先拿到用户的小程序 openid、消息模板 ID 和页面跳转路径。模板 ID 在微信公众平台的小程序后台申请,个人主体也能申请部分公共模板。
python复制import requests
def send_subscribe_message(openid, template_id, page, data):
access_token = get_access_token()
url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"
params = {"access_token": access_token}
body = {
"touser": openid,
"template_id": template_id,
"page": page,
"data": data
}
resp = requests.post(url, params=params, json=body, timeout=5)
result = resp.json()
if result.get("errcode") != 0:
# 记录日志,用户拒收或模板参数错误时需要排查
current_app.logger.warning(result)
订阅消息的 data 字段要严格按照模板里声明的字段来填。比如模板里有“订单号”“状态”两个变量,后端传 JSON 时就要写成 {"thing1": {"value": "ABC"}, "phrase2": {"value": "已被接单"}}。这个字段名一旦写错,微信会返回参数错误,而不会帮你自动纠错。
6.3 拦截订阅消息的常见坑
最容易出现的问题是用户已经订阅过一次,但后端发送时还是提示 43101。原因是订阅消息是“一次性”的,用户授权一次只能发一条。如果发单者发布了 10 个订单,只订阅了 1 次,那么只有在第一个订单被接单时能收到通知,后面 9 个都收不到。
所以我在发单页面文案里专门写了一句:“需要接收每次接单提醒,请在弹窗中选择‘总是保持以上选择’。”这样的提示听起来简单,实际能解决的问题非常多。否则用户会以为小程序坏了,明明开了通知却收不到提醒。
7. 编译、上线与演示:在微信开发者工具里最容易翻车的几件事
7.1 真实机调试时网络请求连不上后端的排查
后端 Flask 服务跑在本机时,小程序开发者工具里访问 http://127.0.0.1:5000 通常是没有问题的。但一旦切到真机预览,手机就无法再通过 127.0.0.1 访问电脑。这里需要让手机和电脑处于同一个局域网,并把请求地址改成电脑的局域网 IP。
改完后手机仍然可能连不上,原因基本是后端启动时只监听了 127.0.0.1。我在启动命令里显式指定监听所有网卡:
bash复制flask run --host=0.0.0.0 --port=5000
这一步做完,真机多数情况下就能访问到本机服务了。但要注意,这种通过局域网 IP 访问的方式只适合开发阶段演示,不能作为正式上线方案。
7.2 HTTPS与合法域名:开发环境能跑不等于上线能跑
小程序正式上线时,wx.request 请求的域名必须在微信公众平台后台配置为合法域名,并且必须支持 HTTPS。也就是说,后端的 Python 服务不能只部署在裸机 HTTP 端口上。我一般会用 Nginx 做反向代理,把 https://api.example.com 转发到本机的 Flask 服务,同时把证书配置在 Nginx 层。
这一步有两个很隐蔽的坑。一是 Nginx 代理时若是没有把 POST 请求的请求体完整转发,小程序会出现“请求成功但收不到数据”的怪问题,通常是 Nginx 配置里少了 proxy_set_header。二是在微信开发者工具里,如果把“不校验合法域名”这个选项开着,开发时一切正常,但正式发布时关闭这个选项就会出现 wx.request fail。所有接口在发布前,都要在“不校验合法域名”关闭的情况下完整跑一遍。
7.3 个人主体与企业主体的功能限制
如果是用个人主体注册小程序,要提前确认校园互助里的类目能不能审核通过。实践下来,个人主体对工具类目基本能覆盖任务列表和简单交易信息展示,但如果功能描述里出现“代收费用”“报酬结算”这类字眼,审核很容易被驳回。
我的建议是项目描述里突出“校园内部互助”“积分激励”“学生交流”,不要在文案里出现“资金”“交易”“代付款”这类词。这不是绕开平台规则,而是让产品定位更准确:这个系统本来就不是支付平台,核心是提供互助撮合。
答辩演示时尤其要提前测试审核版和开发版的差异。比如自定义 tabbar 的图标必须是 PNG 格式,网络图标下载回来后如果没有补齐文件和尺寸,编译过程中页面会一片空白;再比如上传图片后需要把临时路径转成真实可访问路径,不能直接把 wxfile:// 开头的路径交给后端。
7.4 给后来者的一点点实操建议
如果这个项目是你第一次把 Python 后端和微信小程序串起来,建议不要把战线拉得太长。先跑通“登录接口返回用户信息→小程序拿到 token→同一 token 能访问订单接口”这条最小链路,再开始写页面。这个链路通了,后面的发单、接单、确认操作都只是对业务数据的增删改查。
我在最终测试时,会把五种典型账号提前准备好:管理员、发单者、接单者、未注册用户、被禁用用户。用这些账号把一张互助单从 PENDING 一路走到 COMPLETED,记录每一步后端返回的 code 和状态变化。把这张表整理进论文或答辩文档里,评委一眼就能看出项目不是停留在截图阶段,而是真正过了一遍完整数据闭环。
最后再分享一个小技巧:把后端所有涉及订单状态变更的接口,在写完后统一加一条日志打印,把请求用户、订单号、变更前后状态输出到控制台。开发时看日志排错,答辩演示时通过日志展示现场操作过程,这个习惯能让你减少大量复盘时间。
