背景
近年来,随着生活水平的提高和健康意识的觉醒,“全民健身”已成为社会热点。高校健身协会及社区体育组织举办的活动种类繁多,包括瑜伽课、跑步团、球类比赛等。然而,传统的活动组织模式依赖微信群接龙、Excel表格统计和线下纸质签到,存在以下显著问题:
- 信息传递低效:活动通知容易被群消息淹没,报名情况无法实时同步。
- 数据统计困难:人工统计报名人数、签到率及用户活跃度耗时耗力,且易出错。
- 资源浪费:无法精准控制活动人数,常出现超员或空缺现象。
- 互动性差:缺乏用户间的互动激励(如打卡分享),难以维持用户粘性。
在此背景下,开发一款轻量级、免安装、易传播的健身协会活动小程序显得尤为重要。利用微信小程序的普及性和云开发的低成本优势,可以实现活动管理的数字化、智能化,提升协会运营效率,增强会员参与感。
系统需求分析
- 活动策划:管理员制定活动计划,设定时间、地点、人数限制及报名要求。
- 宣传推广:通过小程序首页、资讯栏目发布活动信息。
- 用户报名:会员浏览活动,填写必要信息并提交报名。
- 审核与通知:管理员审核报名资格(如需),系统自动发送通知。
- 现场签到:活动现场通过扫描二维码核销报名资格。
- 日常互动:会员每日上传锻炼记录进行打卡,形成社区氛围。
- 数据复盘:管理员查看活动参与度、用户活跃度报表。
功能需求分析
| 模块名称 | 功能点 | 详细描述 |
|---|
| 活动管理 | 活动发布 | 支持设置标题、分类、封面、时间、地点、最大人数、自定义报名表单。 |
| 活动列表 | 按时间、分类筛选展示活动,显示剩余名额。 |
| 报名处理 | 用户提交报名,系统自动检查名额,管理员可手动审核。 |
| 签到核销 | 生成活动专属二维码,管理员扫码完成签到。 |
| 每日打卡 | 打卡发布 | 选择打卡项目,输入心得,上传多张图片。 |
| 打卡广场 | 展示所有用户的打卡记录,支持点赞(可选)。 |
| 统计排行 | 统计用户连续打卡天数,生成排行榜。 |
| 资讯中心 | 资讯发布 | 管理员发布健身知识、协会新闻。 |
| 资讯浏览 | 用户列表浏览及详情阅读。 |
| 个人中心 | 我的报名 | 查看已报名活动的状态(待审核/已通过/已签到)。 |
| 我的打卡 | 查看历史打卡记录。 |
| 个人信息 | 修改昵称、头像、手机号。 |
| 后台管理 | 数据看板 | 可视化展示活动总数、参与人次、新增用户等。 |
| 用户管理 | 查看用户列表,禁用违规用户。 |

系统设计
系统采用典型的分层架构,基于微信云开发实现前后端一体化:
- 表现层(Client):微信小程序端,负责UI渲染与用户交互。
- 逻辑层(Cloud Functions):部署在云端的Node.js函数,处理核心业务逻辑(报名原子操作、权限校验、定时任务)。
- 数据层(Cloud Database & Storage):
- 云数据库:存储结构化业务数据。
- 云存储:存储非结构化多媒体文件。
- 基础设施层:腾讯云服务器集群,由微信平台自动运维。
数据库设计
| 字段名 | 数据类型 | 必填 | 默认值 | 说明 |
|---|
_id | String | 是 | - | 主键,用户的微信 OpenID (如: openid_xxxxx) |
nickName | String | 是 | - | 用户昵称 |
avatarUrl | String | 否 | - | 用户头像图片的云存储地址 (FileID) |
gender | Number | 否 | 0 | 性别:0-未知,1-男,2-女 |
phone | String | 否 | - | 用户手机号 (需授权获取) |
role | String | 是 | "user" | 用户角色:user (普通会员), admin (管理员) |
createTime | Date | 是 | - | 用户首次注册时间 |
lastLoginTime | Date | 否 | - | 最后登录时间 |
| 字段名 | 数据类型 | 必填 | 默认值 | 说明 |
|---|
_id | String | 是 | - | 主键,活动唯一标识 (如: act_20231001) |
title | String | 是 | - | 活动标题 |
categoryId | String | 是 | - | 活动分类 ID (关联分类配置) |
coverImage | String | 是 | - | 活动封面图云存储地址 (FileID) |
content | String | 是 | - | 活动详细介绍 (支持富文本 HTML) |
startTime | Date | 是 | - | 活动开始时间 |
endTime | Date | 是 | - | 活动结束时间 |
location | String | 是 | - | 活动地点 |
maxCount | Number | 是 | - | 最大报名人数限制 |
currentCount | Number | 是 | 0 | 当前已报名人数 (通过原子操作更新) |
status | Number | 是 | 1 | 活动状态:1-报名中, 2-进行中, 3-已结束, 4-已取消 |
customFields | Array | 否 | [] | 自定义报名表单配置,例:[{label:"身份证", type:"text", required:true}] |
publisherId | String | 是 | - | 发布该活动的管理员 OpenID |
createTime | Date | 是 | - | 活动创建时间 |
signQrCode | String | 否 | - | 签到专用二维码内容 (通常为活动 ID) |
| 字段名 | 数据类型 | 必填 | 默认值 | 说明 |
|---|
_id | String | 是 | - | 主键,报名记录唯一标识 |
activityId | String | 是 | - | 外键,关联 activities 表的 _id |
userId | String | 是 | - | 外键,关联 users 表的 _id (OpenID) |
userName | String | 是 | - | 报名时的用户昵称 (冗余字段,便于查询) |
userPhone | String | 是 | - | 报名时的用户手机号 |
formData | Object | 否 | {} | 用户填写的自定义表单数据,例:{"身份证": "110...", "备注": "..."} |
status | Number | 是 | 0 | 报名状态:0-待审核, 1-已通过, 2-已拒绝, 3-已签到 |
signTime | Date | 否 | - | 实际签到时间 (当 status 变为 3 时记录) |
createTime | Date | 是 | - | 提交报名时间 |
auditTime | Date | 否 | - | 管理员审核时间 |
| 字段名 | 数据类型 | 必填 | 默认值 | 说明 |
|---|
_id | String | 是 | - | 主键,打卡记录唯一标识 |
userId | String | 是 | - | 外键,关联 users 表的 _id |
userName | String | 是 | - | 用户昵称 (冗余字段) |
checkDate | String | 是 | - | 打卡日期字符串 (格式: YYYY-MM-DD),用于去重统计 |
itemId | String | 是 | - | 打卡项目 ID (如: run_5km, yoga_30min) |
itemName | String | 是 | - | 打卡项目名称 (如: "晨跑 5 公里") |
content | String | 是 | - | 用户填写的锻炼心得或描述 |
images | Array | 否 | [] | 上传图片的云存储地址列表 (FileID 数组) |
likeCount | Number | 是 | 0 | 点赞数量 |
createTime | Date | 是 | - | 打卡提交时间 |
代码片段
if (event.action === 'createActivity') {
const { activityData } = event;
const user = await db.collection('users').doc(openid).get();
if (!user.data || user.data.role !== 'admin') {
return { code: 403, msg: '无权限' };
}
activityData.currentCount = 0;
activityData.status = 1;
activityData.createTime = db.serverDate();
const res = await db.collection('activities').add({ data: activityData });
return { code: 200, data: res._id };
}
if (event.action === 'signUp') {
const { activityId, formData } = event;
const checkRes = await db.collection('activity_signs')
.where({ activityId, userId: openid }).get();
if (checkRes.data.length > 0) return { code: 400, msg: '请勿重复报名' };
const updateRes = await db.collection('activities').doc(activityId).update({
data: {
currentCount: _.inc(1)
},
});
try {
await db.runTransaction(async (transaction) => {
const actRef = transaction.collection('activities').doc(activityId);
const actSnap = await transaction.get(actRef);
const actData = actSnap.data;
if (actData.currentCount >= actData.maxCount) {
throw new Error('FULL');
}
await transaction.collection('activity_signs').add({
data: {
activityId, userId: openid, formData, status: 0, createTime: db.serverDate()
}
});
await transaction.update(actRef, { currentCount: _.inc(1) });
});
return { code: 200, msg: '报名成功' };
} catch (e) {
if (e.message === 'FULL') return { code: 400, msg: '名额已满' };
throw e;
}
}
UI设计

后台管理系统设计

git代码下载
下载点击