ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于UniApp+SpringBoot的高校校园微活动系统设计与实现

基于UniApp+SpringBoot的高校校园微活动系统设计与实现 1. 项目概述与技术选型思路1.1 这个系统到底解决什么问题校园里的活动组织一直是个挺头疼的事。社团办个讲座靠QQ群接龙报名统计起来麻烦学生会搞个晚会签到靠纸质表格事后对账对半天班级组织个志愿活动参与人数和时长记录容易出错。这些场景听起来不复杂但真要在学校里跑起来问题一堆报名渠道分散、数据不统一、通知触达效率低、活动结束后的数据沉淀几乎为零。我做的这套高校校园微活动系统核心就是把这些线下流程搬到微信小程序里。学生打开微信就能看到活动列表、报名参加、扫码签到管理员在后台发布活动、审核报名、导出数据。整套流程围绕“发布—报名—签到—统计”这条主线展开覆盖了校园活动管理最关键的四个环节。1.2 为什么选UniApp而不是原生小程序说实话如果只做微信小程序一个端原生小程序完全够用而且性能上还有优势。但选择UniApp主要是看中它“一套代码多端运行”的能力。校园场景里老师和学生用的手机五花八门有人习惯微信小程序有人装了企业微信还有人用安卓App。UniApp可以一套代码同时编译到微信小程序、H5、App等多个平台省去了重复开发的成本。另外UniApp基于Vue语法对于熟悉Vue的开发者来说上手几乎无门槛。我在项目里用的是Vue 2语法UniApp对Vue 3的支持虽然已经成熟但Vue 2的生态插件更全踩坑更少适合做毕设或快速上线。如果你准备把项目扩展到其他平台比如后续要出安卓端App只需要改少量条件编译代码就能复用大部分逻辑。注意UniApp的HBuilderX开发工具自带“运行到小程序模拟器”功能但需要先在微信开发者工具里设置“服务端口”为开启状态否则会一直提示“运行失败”或者没有反应。1.3 后端与数据库选型后端我选的是SpringBoot MyBatis-Plus MySQL这套组合。SpringBoot是目前Java后端的事实标准MyBatis-Plus让单表CRUD基本不用写SQL直接继承BaseMapper就能用。MySQL存结构化数据完全够用而且学校机房、服务器上最常见的也是MySQL部署起来不折腾。数据库表设计上面核心是这几张表用户表、活动表、活动报名表、活动签到表、活动分类表。用户表字段包括openid、昵称、头像、学号、角色活动表字段包括标题、封面图、活动时间、地点、报名截止时间、人数上限、积分值、状态报名表记录谁报了哪个活动、报名时间、状态签到表记录签到时间和签到方式。后面我会把建表SQL详细贴出来。这里有一个容易被忽略的点openid是微信小程序用户的唯一标识一定要加索引。而且同一个微信号在不同小程序下的openid是不同的所以用户表设计时不能只存nicknameopenid才是业务关联的主键。2. 系统整体设计与核心功能拆解2.1 角色权限模型设计整个系统我分了三种角色学生、社团管理员、系统管理员。这三种角色的权限边界必须一开始就划清楚不然后面开发起来会乱。学生角色浏览活动列表、查看活动详情、报名/取消报名、扫码签到、查看自己的报名记录和积分。社团管理员创建活动、管理自己社团发布的活动编辑、取消、导出报名名单。系统管理员审核活动、管理用户、管理全部活动数据、发布系统公告。权限控制在后端做前端只是做展示层的判断。比如社团管理员创建活动后活动状态默认是“待审核”系统管理员审核通过后才会上线展示。这种设计是参考了真实校园场景的运营需求避免任何人随便发个活动就全校可见。前端通过用户表里的role字段判断是否显示某些按钮但真正控制权限的校验在后端的拦截器里完成。2.2 活动发布流程与状态流转活动状态我设计了五个0草稿、1待审核、2已发布、3已结束、4已取消。状态的流转用一张状态机图就能清晰表达创建时是草稿提交审核后变待审核管理员通过后变已发布活动时间过了自动变已结束管理员在任意时间可以取消活动。这里有个细节活动报名表的唯一约束。一个用户对同一个活动只能有一条报名记录所以建表时要在user_id和activity_id上加联合唯一索引。否则前端连续点击两次报名按钮后端就插入了两条重复数据。这个坑我踩过后来通过“数据库唯一索引 后端事务 前端按钮防重复点击”三重保障才算彻底解决。前端页面上活动详情页要展示封面图、标题、活动时间、地点、主办方、报名截止时间、人数上限/已报名人数、活动介绍富文本、报名按钮。报名按钮的状态根据活动状态和当前用户是否已报名来动态切换未开始报名时置灰、已报名显示“取消报名”、活动结束显示“已结束”。2.3 签到模块的两种实现方式签到是校园活动管理的核心环节我实现了两种签到方式扫码签到和定位签到。扫码签到管理员在活动详情页生成一个带活动ID的小程序码学生到场后用小程序“扫一扫”扫这个码会跳转到带有scene参数的小程序页面页面获取到scence参数后调用签到接口完成签到。这里涉及扫码的小程序码生成用微信官方的getwxacodeunlimit接口参数scene里拼接activityId页面通过options.scene拿到参数再解密。需要注意的是scene参数只能传32位可见字符所以得用短一点的ID比如活动ID本身是数字就直接传数字。定位签到学生打开签到页面时调用uni.getLocation获取当前位置后端计算和活动地点的距离小于设定阈值比如500米才允许签到。这个方案需要在小程序管理后台配置地理位置接口的权限说明而且getLocation现在需要用户手动授权代码里要做好未授权的兜底逻辑。注意微信小程序的getWxacodeunlimit接口生成的码是永久的但接口有调用频率限制所以生成后一定要缓存起来不要每次打开页面都重新请求一次。2.4 关注/收藏与消息通知学生可以收藏感兴趣的活动收藏后活动状态有变化比如时间调整、取消时通过微信订阅消息推送通知。微信订阅消息现在是一次订阅一次推送所以在用户点击“收藏”时就要引导用户订阅授权一次才能在活动变动时推送一次。这里要注意订阅消息模板的选择和参数拼接模板里的字段活动名称、活动时间、活动地点必须和后台申请模板时的字段一一对应。3. 核心模块实现与开发过程实录3.1 开发环境准备与项目初始化项目从零开始第一步是搭开发环境。前端用HBuilderX开发工具后端用IntelliJ IDEA。HBuilderX直接在官网下载打开后选择“文件—新建—项目—uni-app”模板选“默认模板”框架选Vue 2。创建完项目后在manifest.json里配置小程序的AppID如果没有就去微信公众平台注册一个测试号。后端用Spring Initializr生成项目Java版本用8稳定兼容性好依赖选Spring Web、MyBatis Plus手动引入、MySQL Driver、Lombok。这里一个小细节MyBatis-Plus需要手动引入依赖Spring Initializr默认不含这个。!-- 后端pom.xml关键依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency启动项目前记得在application.yml里配置数据库连接和MyBatis-Plus的日志输出方便调试SQLspring: datasource: url: jdbc:mysql://localhost:3306/campus_activity?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl3.2 用户登录与微信授权流程小程序登录的核心是wx.login获取code然后把code发给后端后端拿着code去微信接口换取openid和session_key。这里要注意不要把session_key下发到前端也不需要在前端缓存用户信息所有敏感数据都在后端处理。前端login页面的核心代码如下// 前端登录逻辑 uni.login({ provider: weixin, success: async (loginRes) { // 获取用户信息头像昵称 const profileRes await uni.getUserProfile({ desc: 用于完善会员资料 }); // 把code和用户信息发给后端 uni.request({ url: https://你的域名/api/user/login, method: POST, data: { code: loginRes.code, nickname: profileRes.userInfo.nickName, avatar: profileRes.userInfo.avatarUrl }, success: (res) { // 存储token到storage uni.setStorageSync(token, res.data.data.token); uni.setStorageSync(userInfo, res.data.data.userInfo); } }); } });后端收到code后调用https://api.weixin.qq.com/sns/jscode2session接口换取openid然后判断数据库里是否已有这个用户没有就自动注册有就登录。登录成功生成一个token可以用JWT也可以用UUID存Redis前端后续请求都带上这个token。这里有一个从实践里总结出来的经验老版本微信小程序可以直接uni.getUserProfile拿到头像昵称但2022年10月之后微信调整了规则getUserProfile返回的已经变成默认头像和“微信用户”这些默认信息。所以现在的做法是首次登录时不强行获取用户信息等用户首次报名或首次发布活动时再弹窗引导用户完善资料比如填学号、姓名、选头像。这个调整很多教程里没提但你做新项目时一定会遇到。3.3 活动首页与列表加载优化活动首页是用户看到的第一屏加载速度和体验直接影响留存。列表接口设计成分页加载每页10条。前端用onReachBottom触底加载下一页用onPullDownRefresh下拉刷新。首页活动列表的一个细节是图片懒加载image标签加lazy-load属性让图片在进入可视区域时才加载避免首屏一次性请求大量图片。列表数据里有一个字段需要后端计算返回活动是否已结束、已报名人数。这些数据如果是实时去count并发高时可能拖慢接口。我采用的方式是活动表冗余一个registered_count字段报名成功时加1取消报名时减1用事务保证一致性。这样列表页只管读不用查报名表统计。3.4 活动详情的富文本渲染活动介绍用UEditor或wangEditor生成富文本存到后端小程序端需要把HTML字符串渲染出来。这里不能用v-html小程序里不支持我用了rich-text组件rich-text :nodesactivity.detail/rich-text但rich-text对很多HTML标签支持不完整尤其是图片的宽度控制。富文本里如果贴了一张大图不限制宽度就会撑破页面。我的处理方式是在后端返回详情时对富文本里的img标签做正则替换加上max-width:100%; height:auto的样式String detail activity.getDetail(); detail detail.replaceAll(img, img style\max-width:100%;height:auto;\);这个操作在保存时做一次展示时就不用额外处理了。如果富文本里的图片是外链的注意在小程序管理后台配置downloadFile合法域名否则图片会加载不出来。3.5 报名/取消报名的事务一致性报名接口看似简单真正写起来要处理的问题不少活动是否存在且状态为“已发布”当前是否在报名时间内是否已经报名过唯一索引兜底报名人数是否已满扣减活动剩余名额我这里用一个方法把校验和更新放在同一个事务里。关键代码Transactional(rollbackFor Exception.class) public Result createRegistration(RegistrationRequest req) { // 1. 使用selectForUpdate锁住活动记录防止并发超卖 Activity activity activityMapper.selectByIdForUpdate(req.getActivityId()); // 2. 一系列校验... if (activity.getRegisteredCount() activity.getMaxCount()) { return Result.error(名额已满); } // 3. 插入报名记录 Registration reg new Registration(); reg.setUserId(currentUserId()); reg.setActivityId(activity.getId()); reg.setStatus(1); registrationMapper.insert(reg); // 4. 更新活动已报名人数 activity.setRegisteredCount(activity.getRegisteredCount() 1); activityMapper.updateById(activity); return Result.success(); }MySQL的InnoDB默认行锁select ... for update能把这条活动记录锁住并发情况下后到的请求会阻塞等待这样就避免了名额超卖。Transactional保证报名记录插入失败时活动人数的更新也会回滚不会出现数据不一致。3.6 数据统计可视化管理系统后台需要展示统计数据活动总数、报名总人次、活跃用户数、每个分类下的活动占比等。前端用UniApp里的echarts实现图表需要单独安装qiun/ucharts或者lime-echart插件。我用的方式是lime-echart它是UniApp生态里对小程序支持比较好的图表库。图表的数据来源是后端统计接口返回聚合后的JSON数组。比如“近7日新增活动数”的折线图后端返回[{date: 2024-01-01, count: 3}, ...]这种结构前端直接setOption就行。统计接口用SQL的GROUP BY按天分组不需要额外引入复杂的聚合工具。4. 开发过程中遇到的典型问题与排查记录4.1 uniapp运行到微信开发者工具没反应这是我最初遇到的第一个坎。在HBuilderX里点了“运行到小程序模拟器”微信开发者工具一直没反应。后来发现原因是没有开启微信开发者工具的“服务端口”选项。解决办法打开微信开发者工具点击右上角“设置—安全设置”把“服务端口”开关打开。然后回到HBuilderX重新运行就会自动打开微信开发者工具并加载项目。还有一个坑如果电脑上装了多个版本的微信开发者工具HBuilderX可能找不到正确的安装路径需要在HBuilderX的“运行—运行到小程序模拟器—运行设置”里手动指定微信开发者工具的安装路径。4.2 手机软键盘遮挡查询内容这个问题的场景是在活动列表页顶部的搜索框输入关键字时手机上弹出的软键盘会遮挡住下面的搜索结果导致用户看不到输入的内容。HBuilderX里pages.json的app-plus配置可以设置softinputMode微信小程序端需要在页面配置文件里设置disableScroll: false并配合adjust-position处理。我最终的解决方案是搜索框设置为fixed定位输入框聚焦时把页面滚动到底部让搜索框保持在可视区域上方并且在input事件中防抖调用搜索接口避免每输入一个字就请求一次。同时给input组件加了confirm-typesearch用户点键盘上的搜索键才触发搜索而不是每次输入都实时过滤性能和体验都好很多。4.3 popup弹框背景跟着滚动活动详情页里有个“选择签到方式”的popup弹窗弹窗打开后底部的页面还是能滑动体验很割裂。解决方案是弹窗打开时给页面根节点加overflow: hidden关闭时移除同时使用catchtouchmove阻止事件穿透。UniApp里的简洁做法// 在popup打开时 this.showPopup true; // 阻止背景滚动 document.body.style.overflow hidden; // 或者用uni-app的方式 uni.pageScrollTo({ scrollTop: 0, duration: 0 });如果用的是uni-ui的uni-popup组件它在关闭时已经处理了阻止背景滚动但部分场景会有兼容bug这里加了一层兜底处理才彻底解决。4.4 uniapp的onShareAppMessage被全局方法覆盖很多时候我们会把分享逻辑抽成全局方法在main.js里覆盖uni.share或者对onShareAppMessage做统一处理。但实际开发中我发现如果在全局混入了mixin来统一处理后端下发的分享配置页面里再重写onShareAppMessage时两者会产生冲突导致分享的标题和图片不生效。最终的做法是封装一个share.js工具模块里面导出setupShare(shareData)函数在每个页面onLoad时调用把页面级的分享数据传进去页面内再定义onShareAppMessage返回这份数据。这样既统一了分享参数的入口每个页面又能灵活配置自己的分享内容。4.5 微信支付V3签名问题这个系统里有一个功能是收费活动的在线报名需要对接微信支付。微信支付V3的签名机制比V2复杂很多要求用商户私钥对请求做RSA-SHA256签名请求头里要带上Authorization信息格式是固定的Template。我踩过的坑主要在签名这块一是私钥的格式必须是PKCS#8Java要求从微信商户平台下载的是apiclient_key.pemJava里需要转换成PKCS8EncodedKeySpec格式二是签名的字符串拼接顺序必须严格按官方文档来字段顺序错一个就会报“签名错误”三是每次请求的nonce_str和timestamp不能缓存必须动态生成。如果是毕设项目建议直接用微信支付官方的Java SDKwechatpay-java它封装好了签名和验签逻辑直接注入商户配置就能调用。我在对接之前自己手写签名排错排了一整天后来换成SDK十分钟就通了。4.6 视频列表自动播放管理校园活动系统里有个“活动回顾”模块支持上传活动视频。视频列表页面如果允许多个视频同时播放体验会很糟糕而且微信小程序里同屏多个video组件播放会卡顿、耗电。我的实现思路是在onHide和onShow生命周期里控制暂停在视频组件上绑定play事件当新视频开始播放时遍历所有视频上下文执行pause。用uni.createVideoContext创建视频实例handlePlay(e) { const currentId e.currentTarget.id; this.videoContexts.forEach(video { if (video.id ! currentId) { video.pause(); } }); }另外视频组件还需要监听滑出可视区域自动暂停。可以用uni.createIntersectionObserver监听video元素和视口的交叉状态当交叉比例低于一定阈值时调用pause()。这个功能在热词里被频繁提及说明是真实开发中的高频需求。4.7 用户拒绝隐私协议时退出App的处理这个需求在打包成安卓App时特别重要。手机应用市场审核要求用户不同意隐私政策时App必须退出不能继续使用。UniApp在manifest.json里配置了隐私协议弹窗但弹窗如果被用户拒绝了框架默认不会自动退出。我的处理方式是监听plus的隐私协议回调。在App.vue的onLaunch里加判断当用户拒绝时调用plus.runtime.quit()退出应用// App.vue onLaunch let privacyAgreement uni.getStorageSync(privacyAgreement); if (!privacyAgreement) { // 显示自定义隐私弹窗 this.privacyPopupShow true; } agreePrivacy() { uni.setStorageSync(privacyAgreement, true); this.privacyPopupShow false; }, refusePrivacy() { // #ifdef APP-PLUS plus.runtime.quit(); // #endif }这里的关键点是不要依赖框架自带的弹窗逻辑自己用自定义弹窗控制同意和拒绝的分支逻辑更加可控。如果直接调用plus.runtime.quit()在某些安卓机型上无效需要调用plus.key.hideSoftKeybord()后延迟200ms再退出。5. 小程序上线前的高频坑点与审核经验5.1 manifest.json的关键配置项HBuilderX项目里manifest.json是核心配置文件上线前必须逐个检查基础配置里uni-app应用标识appid不能乱填微信小程序端的AppID要在微信公众平台单独申请vue版本根据项目实际选择。微信小程序配置里要重点设置requiredPrivateInfos地理位置接口在2022年后必须在这里声明后才能调用、permissionscope.userLocation的说明文案要写清楚用途。不配置这个调uni.getLocation时会直接报错。App端的配置里模块权限配置蓝牙、定位等需要手动勾选打包时没勾选对应的模块运行时调用对应API会提示“未配置模块”。我遇到过一位读者跟我反馈他用了一个第三方蓝牙打印插件打包时没勾蓝牙模块整天报错找不到方法。5.2 安卓应用市场上架注意事项如果要把项目打包成安卓App上架除了常规的软著、隐私政策这些材料还有几个特别注意的点应用市场要求App必须能正常退出。如果App里没有主动退出的入口审核人员会直接拒绝。我是在“我的”页面设置里加了“退出登录”按钮同时配合隐私拒绝退出的逻辑才算过审。另外现在主流应用市场要求适配Android 11以上的分区存储如果是旧项目打包的APK需要确认targetSdkVersion不低于28。还有一个细节是包名一旦应用商店上架后包名基本不能改所以刚打包时就要规划好包名格式com.你的域名.项目名这种规范格式不要用默认的com.example.demo这种。5.3 微信小程序审核要点微信小程序审核比安卓应用市场更严格我遇到过几次被拒的情况总结下来主要就三类原因第一类是类目选择不对。校园活动平台如果包含“报名”功能就需要选择“教育”类目下的“培训机构”或者“工具”类目。选错类目会被打回。第二类是虚拟支付问题。如果涉及付费报名小程序只能走微信支付不能诱导用户加微信转账或者跳转H5付款。第三类是内容审核。活动名称、活动介绍里不能出现“抽奖”、“彩票”、“赌”等违规词这个需要在前端发布活动时做一次敏感词过滤。提示小程序上线前一定要在微信公众平台后台配置服务器域名。所有请求的API域名必须是HTTPS的而且要先在后台添加白名单否则开发工具里能跑、真机预览却全部请求失败。这个最常见的错误是开发者在本地调试用http://localhost没问题但一上传体验版请求全部挂掉。5.4 uniapp如何正确打包发布UniApp的打包发布流程分为两部分小程序端直接在HBuilderX里“发行—小程序-微信”生成dist目录后用微信开发者工具打开上传App端则选择“发行—原生App-云打包”上传证书后在云端生成安装包或上传到应用市场。这里有一个重要的区别小程序端的包体积限制是2MB主包如果项目加入echarts、sqlite这类大插件很容易超限。超限后的处理方式是做分包加载把活动列表、活动详情这些页面放入subPackages子包首包只保留tabBar页面和公共逻辑。我在项目里把“我的报名”“个人中心”这些低频页面全拆到了分包里主包体积从2.3MB压缩到1.6MB。App端云打包需要配置证书安卓用.keystore或者.jks签名文件生成后要妥善保管每次更新版本都要用同一个签名否则无法覆盖安装。苹果端需要p12描述文件这个通常需要开发者账号才能生成。6. 数据安全与性能优化补充6.1 登录态的token管理与安全策略前端登录后拿到token存在uni.getStorageSync(token)里每次请求在拦截器里统一带上Authorization头。这里要加一个全局请求封装统一处理token过期时的跳转逻辑比如转到登录页并给出提示不然每个接口单独判断代码会非常冗余。// request.js 请求拦截器 uni.request({ url: BASE_URL url, data: data, header: { token: uni.getStorageSync(token), Content-Type: application/json }, success: (res) { if (res.data.code 401) { // token失效清除本地缓存并跳转登录页 uni.removeStorageSync(token); uni.navigateTo({ url: /pages/login/login }); return; } resolve(res.data); } });6.2 首页列表和图片的性能优化列表数据返回时大图的URL如果直接拼接微信的图片处理参数可以显著提升加载速度。比如使用image的src时微信小程序支持在图片URL后面拼接?imageView2/2/w/300/h/300这是腾讯云OSS或图片处理服务的参数视你的图片存储方案而定让服务器返回压缩后的缩略图而不是原图。另外活动列表接口返回的数据里只保留前端展示必需的字段不要把活动详情的大段富文本内容也一起返回否则列表接口会非常慢。我是用MyBatis-Plus的wrapper.select()指定查询字段列表接口只查id、标题、封面、时间、地点这几个字段。6.3 数据库索引与慢查询排查活动列表页有筛选条件按分类、按状态、按时间排序数据库索引如果没建好数据量一上来就各种慢。我给活动表建了这些索引分类ID普通索引、状态普通索引、活动时间普通索引。报名表除了前面说的联合唯一索引还要给activity_id加普通索引因为“查看一个活动的所有报名人”是高频查询。定位功能涉及的坐标字段如果要做附近活动的地图查询建议用MySQL的空间索引或者直接在代码里做距离筛选。对于校园小范围场景直接用经纬度范围筛纬度范围±0.01经度范围±0.014左右配合索引就能获得非常好的性能不一定要引入复杂的GIS组件。6.4 后端接口的幂等性设计报名、点赞、收藏这类操作客户端可能会因为网络原因重复提交。除了传token/sign让前后端配合避免重复请求后端接口设计时要考虑幂等性。我用了一个简单方案前端在按钮点击后立即置为loading状态防止重复点击后端对于报名接口使用数据库唯一索引兜底对于点赞、收藏这类操作用“先查后插”加唯一索引的写法保证一个用户对一个活动只能有一条记录。7. 从开发到上线的整体复盘这个项目从零开始开发到小程序通过审核、安卓包成功上架整个过程大概用了三周时间。第一周搭框架和基础页面第二周写核心业务报名、签到、统计第三周主要在处理各种兼容性bug和审核材料。如果让我重新做一遍有几件事我会在第一天就规划好一是UI组件库尽早确定。项目用了uView UIuni-app生态比较成熟的组件库但uView和Vue 3的兼容性有问题如果你用Vue 3就需要改选uni-ui或者wot-design-uni。组件库定下来后再开发页面效率会高很多中途换组件库是灾难。二是数据字典集中管理。活动分类、活动状态、报名状态这些枚举值一开始就集中放到一个常量文件里前后端保持一致不要散落在各个页面的业务代码里。否则后期改个状态值要全局搜索替换容易漏。三是后端接口的返回结构统一。所有接口的返回格式都固定为{code: 200, message: success, data: {...}}前端拦截器统一处理code非200的情况。这个约定能帮你省下大量的错误处理代码。四是测试数据要模拟真实场景。我在开发时没有批量生成测试数据导致列表分页、统计接口的效果一直没有彻底验证。后来加了个模拟数据脚本一次性生成500个测试用户和200个活动记录很多性能问题才暴露出来。还有一个值得分享的经验小程序上线后的数据统计。在App.vue的onLaunch里初始化了友盟统计或腾讯移动分析记录新增用户、启动次数、页面访问时长这些核心指标。没有统计数据支撑的功能迭代基本靠猜有了数据才能知道哪些页面留存差、哪个活动分类最受欢迎。这个系统的后续扩展空间还挺大。可以接入校园一卡通接口做身份验证可以增加社团管理模块让每个社团有自己的独立后台可以做活动积分的兑换商城来提升用户粘性。如果读者正在做相关方向的毕设或者校内实践项目这套从设计、开发到上线的完整流程可以直接作为参考模板来用。
返回列表