第 8 章:报名系统设计
"填表单是世界上最枯燥的事,但我们可以让它稍微好玩一点。"
报名系统是连接活动主办方和参与者的桥梁。本章我们将实现一个高度可配置的报名系统,支持动态表单和支付凭证审核。
8.1 自定义表单配置
不同的活动需要收集的信息不同。聚餐需要统计"是否吃辣",讲座需要统计"所在部门"。我们不能把表单字段写死在代码里。
数据结构设计
在 activities 表的 registrationConfig 字段中:
json
{
"fields": [
{ "id": "name", "type": "text", "label": "姓名", "required": true },
{ "id": "phone", "type": "number", "label": "手机号", "required": true },
{
"id": "diet",
"type": "radio",
"label": "饮食偏好",
"options": ["正常", "微辣", "特辣"]
}
]
}
前端渲染 (DynamicForm)
前端组件遍历 fields 数组,根据 type 动态渲染组件:
text-><uni-easyinput>radio-><uni-data-checkbox>image-><uni-file-picker>
8.2 报名流程与状态机
报名不仅仅是填个表,它是一个完整的业务事务。
状态流转图
graph LR
Start((开始)) --> Pending[待审核]
Pending -->|管理员通过| Approved[报名成功]
Pending -->|管理员拒绝| Rejected[已拒绝]
subgraph 支付流程
Pending -->|上传凭证| PaymentPending[支付确认中]
PaymentPending -->|管理员确认收款| Approved
PaymentPending -->|管理员驳回| Pending
end
并发控制 (Race Condition)
当活动限制名额(如仅限 50 人)时,如何防止第 51 个人报名成功?
解决方案: "先查后写"的乐观检查策略。 虽然 MySQL 支持强事务,但为了简化云函数逻辑,我们目前暂未开启严格的数据库事务锁。而是采用了应用层的检查机制:
js
count = await count(registrations where activityId=xxx)
if (count >= limit) return Error("名额已满")
await create(registration)`
注意: 这种方式在极高并发下(如 1秒内涌入 1000 人)可能会导致少量超卖。但对于社团活动这种量级,这已经足够安全且高效了。如果真遇到了"秒杀"场景,再考虑引入 Redis 锁或数据库事务也不迟。
8.3 支付凭证上传与审核
对于社团活动,接入微信支付门槛太高(需要企业资质)。最接地气的方式是:上传转账截图。
流程实现
- 主办方: 在活动配置中上传自己的收款二维码(微信/支付宝)。
- 参与者 :
- 保存二维码,去微信支付转账。
- 截图保存。
- 在报名表单中点击"上传支付凭证",选择截图。
- 前端调用
uniCloud.uploadFile,获取图片FileID。
- 提交 :
FileID随表单数据一同提交到createRegistration云函数。
8.4 报名管理后台
管理员在手机端管理报名记录。
扫码核销
如何快速验证参与者身份?
- 生成码 : 用户端根据报名ID生成二维码 (
qrcode). - 扫码 : 管理员使用小程序自带的
scanCode接口扫描。 - 核销 : 前端解析出报名ID,调用云函数
verifyRegistration,将状态改为"已签到"。
数据导出
社团联需要 Excel 表格备案? 云函数支持将数据导出为 CSV/Excel 文件,生成下载链接,管理员复制链接即可在电脑上下载。
本章小结 : 我们实现了一个灵活的报名系统,既满足了多样化的数据收集需求,又巧妙规避了微信支付的资质限制。下一章,我们将让活动"动"起来------实现投票互动系统。