简介:这是一份面向文化馆、图书馆、文体中心、青少年活动中心、少年宫等公益机构的微信小程序报名系统设计源码,用于发布公告通知、展示课堂风采、维护报名列表并完成在线报名登记,解决公益培训活动组织中的报名管理难题。压缩包共464个文件,以176个JavaScript逻辑文件、96个WXSS样式文件、78个WXML页面结构文件、71个JSON配置文件和35个PNG图片为主,仅4.94MB,整体结构清晰,适合微信小程序前后端开发学习者参考。包内附带安装使用手册、后台报名管理与导出功能截图,以及随机数据、二维码生成、页面处理、数据库操作等辅助脚本,便于对照真实业务场景理解前后端交互并做二次开发。目前已有90人学习下载,可作为公益培训类小程序从设计、实现到后台管理的完整案例。
1. 公益培训报名小程序:先想清楚名额和名单这两件事
社区活动室每周末开公益美术班、老年手机课,报名靠微信群接龙,等要把名单整理到 Excel 时,总会发现三条接龙里有一两条手机号少一位。这类业务不需要购物车,也不需要复杂的会员体系,真正要处理的是三件事:课程信息哪里看、名额还够不够、报名记录能不能追溯。基于微信小程序的公益培训报名小程序设计源码,就是把这三件事用页面和接口固定下来,前端能展示课程、填表报名,后端能记账、查重、扣名额,装到源码包里,下载改一改就能换皮上线。下面用大多数可运行源码都采用的组合来讲:小程序端用 uni-app,接口端用 Spring Boot 和 MySQL,适合做毕业设计演示,也适合公益机构做内部报名工具。
2. 用 uni-app 搭公益培训报名小程序的前端:页面、表单与请求层
2.1 四个页面组成的报名闭环
我一般会把前端拆成四个页面:首页课程列表、课程详情、报名表单、我的报名。首页拉取课程列表,点击跳详情,详情页底部一个“我要报名”按钮进入报名表单页,提交后回到“我的报名”查看状态。这个循环已经覆盖公益培训报名的主流程。如果用原生小程序写,要维护四个 json、wxml、wxss、js 文件;用 uni-app 写,页面放在 pages 目录下,每个页面是一个 vue 文件,但能同时跑微信和 H5 两端,源码维护时少写一套样式适配。HBuilderX 新建 uni-app 项目后,pages.json 是路由和窗口表现的总控,下面是首页的常用配置:
{ "pages": [ { "path": "pages/index/index", "style": { "navigationBarTitleText": "公益培训", "enablePullDownRefresh": true, "navigationBarBackgroundColor": "#12b886" } } ], "globalStyle": { "navigationBarTextStyle": "white", "backgroundColor": "#f5f6f7" } }这段配置里,navigationBarTitleText 决定微信导航栏中间显示的标题,公益培训报名小程序的主题色可以用 navigationBarBackgroundColor 统一导航栏底色;enablePullDownRefresh 打开后,用户可以下拉首页刷新课程列表,对应页面里的 onPullDownRefresh 生命周期。pages 数组的第一项就是小程序的启动页,想修改刚进入的加载页面,把第一个 path 换掉即可,不需要改业务代码。列表页如果要做分页加载,另外维护 page、size、hasMore 三个字段,onReachBottom 触发时页数加一再请求,返回的数据量小于 size 就把 hasMore 置为 false,避免重复请求到底的翻页。
2.2 报名表单的字段与校验
公益培训报名不用收集收货地址,但姓名、手机号这两个字段几乎是标配;如果课程标明“亲子家庭”,还得加一个儿童人数。用 uni-forms 做校验,比在 submit 里写一串 if 更清晰:
<template> <uni-forms ref="signupForm" :modelValue="form" :rules="rules"> <uni-forms-item label="姓名" name="name"> <uni-easyinput v-model="form.name" placeholder="报名人姓名" /> </uni-forms-item> <uni-forms-item label="手机号" name="phone"> <uni-easyinput v-model="form.phone" placeholder="用于接收课程通知" /> </uni-forms-item> <button type="primary" @click="submit">确认报名</button> </uni-forms> </template>接着在 script 里定义 rules,uni-forms 的 name 要和 rules 里的字段一一对应。手机号校验我用/^1[3-9]\d{9}$/,公益场景下不建议用太复杂的号段规则,否则会把老年用户拦在门外。这两个字段必填,姓名限制 20 个字符以内。这里的关键是 uni-forms 的 validate 方法是异步的,submit 里必须 await 之后再提交,不能在校验结果还没返回时就去调接口,否则会出现“手机号格式不对但报名已提交”的怪问题。
2.3 请求封装:token 与 baseUrl 分开写
前端所有接口调用都走一个 request.js,后端地址换成正式域名时只改一处。小程序 wx.request 换成 uni.request 后,能力几乎等价,下面是常用的封装:
const BASE_URL = 'https://api.example.cn' // 改成你自己的正式域名 function request(path, method = 'GET', data = {}) { return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + path, method, data, header: { 'Content-Type': 'application/json', 'Authorization': uni.getStorageSync('token') || '' }, success: res => { if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data) } else if (res.statusCode === 401) { uni.navigateTo({ url: '/pages/login/login' }) } else { uni.showToast({ title: res.data.msg || '请求失败', icon: 'none' }) } }, fail: reject }) }) }这段封装把 token 从 storage 里取出来拼到 Authorization 头,后端从请求头解析用户身份;BASE_URL 单独抽出来,是为了开发时指向局域网 IP,上线时切换到备案域名。401 跳登录只在受保护接口出现,像课程列表这种公开数据不需要带登录态,也就不该把未登录用户硬跳到登录页。公益培训报名小程序里,用户第一次进入首页没必要强制登录,等到提交报名时再引导,报名完成的占比会明显更高。
3. 公益培训报名的后端源码设计:接口、表结构与防重报名
3.1 三张表解决核心问题
后端我不建议一开始就上复杂的用户体系,公益培训报名的最小表结构是三张:课程表、报名记录表、用户表。课程表存课时信息与剩余名额,报名记录表存每次报名动作,用户表只存 openid 和手机号。下面是简化后的建表 SQL:
CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL COMMENT '课程名称', teacher VARCHAR(50) DEFAULT '' COMMENT '授课老师', start_time DATETIME NOT NULL COMMENT '开课时间', quota INT NOT NULL DEFAULT 20 COMMENT '名额总数', signed INT NOT NULL DEFAULT 0 COMMENT '已报名人数', status TINYINT NOT NULL DEFAULT 1 COMMENT '1上架 0下架' ); CREATE TABLE signup_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, user_id BIGINT NOT NULL, name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, status TINYINT NOT NULL DEFAULT 0 COMMENT '0待确认 1已确认 2已取消', UNIQUE KEY uk_course_user (course_id, user_id) );表结构是这份源码最值得看的部分。uk_course_user 这个唯一索引是防重复报名的最后一道防线,哪怕前端按钮连点了十次,数据库也只允许同一用户对同一课程插入一行。status 字段表示报名后的状态,公益培训里常见“报名成功但需要工作人员人工确认到场”,就用 0 表示待确认。用户表单独建,字段至少包含 id、openid、phone、create_time,openid 要加唯一索引,用户换设备登录后仍能识别同一身份。课程下架时,报名接口还需要再判断一次 course.status,把已下架课程的名额校验直接拦在入口,避免用户看到旧页面还能提交。
3.2 接口设计:列表、详情、报名、我的报名
后端接口按 REST 风格对外暴露,一般划分成课程列表、课程详情、提交报名、我的报名四个。课程列表接口需要分页参数 page 和 size,前端滚动到底部时加载下一页。报名接口是 POST,入参包含课程 ID、姓名、手机号,后端从 token 解析用户。所有接口返回统一包一层 code、msg、data,前端 request.js 里正好对应。
| 方法 | 路径 | 入参 | 作用 |
|---|---|---|---|
| GET | /api/course/list | page, size, keyword | 分页获取可报名课程 |
| GET | /api/course/detail | id | 课程详情与剩余名额 |
| POST | /api/signup | courseId, name, phone | 提交报名 |
| GET | /api/signup/my | 无 | 查看我的报名记录 |
分页参数有个容易踩的坑:前端下拉加载时 page 从 1 开始,后端如果不做参数校验,page=0 会查出来空列表,导致第二页永远加载不出来。我一般在 Controller 里加@Min(1),前端也初始化成 1,两边同时兜底。keyword 参数用于按课程名搜索,公益培训的课程数量一般不多,SQL 里用 LIKE 拼接即可,不需要引入全文检索。
3.3 名额校验与幂等写入
报名接口是源码里并发控制的核心。公益培训热门课程一放出来就可能被同时点击,只靠先查再写会超发名额。常见做法是让数据库自己保证一致性,用一个带条件的更新把名额扣掉:
@Transactional public boolean signup(Long courseId, Long userId, SignupRequest req) { int rows = courseMapper.cutQuota(courseId); if (rows == 0) { return false; // 名额已满 } int inserted = signupMapper.insert(req.toRecord(courseId, userId)); if (inserted == 0) { throw new BusinessException("请勿重复报名"); } return true; }cutQuota 对应的 SQL 是update course set signed = signed + 1 where id = ? and signed < quota。这个 update 语句命中行锁,多个请求同时进来时,只有一个能把 signed 改到小于 quota,其他请求影响行数为 0,直接返回 false。如果报名记录插入失败,事务回滚时扣掉的名额也会回滚,因为方法上有 @Transactional。幂等依赖 signup_record 的唯一索引,insert 报 DuplicateKeyException 时转成业务异常提示,不会导致名额被白白扣掉。
4. 公益培训报名小程序上线前:AppID、合法域名与审核
4.1 先把 AppID 和运行环境理顺
从源码到能真机看效果,第一步是注册小程序账号。用企业主体注册能得到完整的接口权限,个人主体做公益培训也能跑,但手机号快捷填充、微信支付这类能力会被限制。HBuilderX 里右键项目“运行到手机或模拟器”前,要在 manifest.json 的小程序配置里填入 AppID,测试阶段可以填测试号,真机预览时暂时忽略域名校验。源码包里通常会有几个不同的 manifest 备份,正式发布前要检查 AppID 是否替换干净,否则上传代码后会提示 AppID 不匹配。
4.2 合法域名与请求失败的排错
开发时我一般打开“详情 -> 本地设置 -> 不校验合法域名”,因为接口跑在本机 IP 上;预览给同事看时,会遇到 request 请求失败,控制台提示域名不在合法域名列表。解决办法是把后端接口部署到 HTTPS + 已备案域名,然后在微信公众平台“开发管理 -> 开发设置 -> 服务器域名”里把 request 合法域名加进去。这里有个经常忽略的细节:域名必须备案,端口只能是 443,不能带路径。如果后端临时没有 HTTPS,也可以先用微信云开发的云函数转发,云端请求不校验白名单,调试效率更高,等正式域名申请下来再切换。
4.3 登录与手机号授权的资质边界
公益培训报名的登录流程,我用 wx.login 拿到 code,后端拿 code 向微信接口换 openid,然后把 openid 作为用户唯一标识,签发自定义 token 返回小程序端。手机号这一个字段,在个人主体下不建议依赖 getPhoneNumber 组件,因为它要求企业认证主体;比较稳妥的做法是把手机号作为报名表单的可输入项,提交时后端再做格式校验。公益培训的报名数据不涉及支付,对隐私接口的依赖本来就不高,表单收集已经能满足使用。审核时注意在小程序简介里写清楚运营主体和公益性质,避免被误判为信息收集类目而要求补充资质。报名页的隐私提示文案应放在表单下方,例如“仅用于课程通知,不会对外公开”,小程序后台的隐私保护指引里也要同步声明手机号的使用目的,两处不一致容易被驳回。
5. 公益培训报名小程序的进阶技巧:分享带参与并发扣减
5.1 分享卡片带课程 ID,打开直达详情
公益培训主要靠熟人转发,分享功能值得做透。在课程详情页写 onShareAppMessage,返回的 path 带上课程 ID,朋友点开后直接进入该课程详情:
onShareAppMessage() { return { title: '免费公益课,名额有限', path: '/pages/course/detail?id=' + this.courseId } }如果走小程序码推广,分享路径要换成 scene 参数,然后在 onLoad 里解析 options.scene 并 decodeURIComponent。常见做法是把 courseId 编码后放进 scene,打开页面时先 decode 再调详情接口,这样分享链路才完整。注意 onShareAppMessage 只能由右上角菜单触发,页面里自定义“分享给朋友”按钮需要用 button 的 open-type="share",不能直接在普通按钮的 click 里调用分享接口。
5.2 名额扣减的并发进阶:候补名单与状态闭环
人数少时第 3 章的行锁 update 就够了,将来并发量上来,可以把扣名额改为 Redis 的 Lua 脚本原子操作,但引入了缓存与数据库的一致性成本,维护难度偏高。我倾向于保持在数据库层面解决,前端再配合按钮 loading 和防抖,报名成功率已经很高。更实用的扩展是候补名单:当名额满时,报名接口返回明确提示,前端把用户录入候补表,后面有人取消,按顺序补录。实现不复杂,新增一张 waitlist 表,course 表加一个 wait_count 字段;抢不到名额时往 waitlist 插记录,有人取消时事务里先删 signup_record,再从 waitlist 挑 create_time 最早的一条转正,转正后把 wait_count 减掉。取消和补录必须放在同一个事务里,避免出现“名额空着但没人通知”的断层。校验时可以直接在数据库里查 waitlist 的顺序,不需要单独维护队列组件的状态。
本文还有配套的精品资源,点击获取