微信小程序> 微信小程序十大排行榜-微信小程序实现运动步数排名与发布个人动态&服务器部署-小程序排名

微信小程序十大排行榜-微信小程序实现运动步数排名与发布个人动态&服务器部署-小程序排名

浏览量:2717 时间: 来源:欧先森的猫
1.项目规划

1.本项目为基于微信手机应用平台的一款运动互动型小程序,实现了用户即时运动步数群内PK与个人动态的发布,小程序前端采用原生框架,后端采用基于Node的koa2框架,数据库采用MYSQL,对象存储采用七牛云,服务器采用阿里ECS,域名采用CA认证。

2.运行效果如下

2.支撑技术点分析2.1七牛云存储

3.在这个项目中,有个功能为动态发布,允许用户上传图片,动态发布后,所有人可在动态广场看到该动态,存储图片有很多方式,例如通过表单将文件转为为二级制发送给后端,然后存数据库中,但是,这样我就要多写很多代码,所有我决定将图片存储到图床,我数据库中保存图片链接即可,图床有很多,不一一描述了,我这次用的是七牛云,个人认证成功后将获得一定空间的免费存储空间。

4.建立存储空间(ydonline),选定存储区域(华北)。

5.

6.注册成功后,将获得两组秘钥,这东西很重要,上传文件时,需要知道uptoken,uptoken是根据AK与SC生成的,七牛云开发文档中建议后端生成uptoken值,但我嫌麻烦,直接在线生成了uptoken,也就是说该uptoken是写死的。

7.uptoken生成地址:http://pchou.qiniudn.com/qiniutool/uptoken.htmldeadline的时间设置长一些

8.

9.引入官方开发文件:qiniuUploader.js

10.小程序端存储图片关键代码:

releaseNotice.jsconstqiniuUploader=require("qiniuUploader.js");functiondidPressChooesImage(that){initQiniu();//微信API选文件wx.chooseImage({count:1,success:function(res){varfilePath=res.tempFilePaths[0];//交给七牛上传qiniuUploader.upload(filePath,(res)={letimageurl=that.data.imageurl;imageurl.push(getApp().data.qiniu_domain+res.key);that.setData({'imageurl':imageurl});},(error)={console.error('error:'+JSON.stringify(error));});}})}functioninitQiniu(){//配置详解https://github.com/gpake/qiniu-wxapp-sdk/blob/master/README.mdvaroptions={region:'NCN',//华东区根据存储区域填写//uptokenURL:'UpTokenURL.com/uptoken',////从指定url通过HTTPGET获取uptoken,返回的格式必须是json且包含uptoken字段,例如:{"uptoken":"0MLvWPnyy..."}uptoken:getApp().data.qiniu_uptoken,domain:getApp().data.qiniu_domain};qiniuUploader.init(options);}---------------------------------------------------------------------------------------app.jsApp({data:{qiniu_domain:'http://pgwn32i53.bkt.clouddn.com/',//七牛图片外链域名qiniu_uptoken:'uxQXOgxXDtF-1uM_V15KQSIky5Xkdww0GhoAksLF:LWUt0HMVbICEDaSOMnMF3YLoUH4=:eyJzY29wZSI6Inlkb25saW5lIiwiZGVhZGxpbmUiOjE4NTU2NzA0MDF9'},})

11.上传文件到指定的存储空间,七牛会返回文件的key值,加上七牛给你的外链域名,这样,你就可以显示文件了。

2.2服务器与域名

12.我买过两次服务器,第一次是阿里的,另外一次也是阿里的。但这次,我买的是windows版云ECS,首先,明确需求。

远程连接部署项目(安装环境,运行后台,域名解析,外网访问)进行域名CA认证,小程序的请求必须得是HTTPS。

13.在windows服务器上部署项目简单,远程链接时,选择共享本地某个硬盘的资料,连接成功后,把本地的环境软件全部安装上去,本次需要在服务器上安装node.js、git、mysql这三个软件。

14.购买SSL证书

15.https://yundunnext.console.aliyun.com/?p=casnext#/overview/cn-hangzhou

16.阿里有对单域名免费的证书,时间为1年,于是我为该项目的域名购买了https。

17.

18.点击下载=选择其他

19.

20.解压后,里面有两个文件,一个是crt,一个是key,将这两个文件发在后台文件夹下/ssl包下(可随便命名)

21.后台加载这两个文件。

app.js关键代码varapp=require('koa'),https=require('https');app=newapp();varoptions={key:fs.readFileSync('./ssl/key.key'),//ssl文件路径cert:fs.readFileSync('./ssl/crt.crt')//ssl文件路径};//starttheserverhttps.createServer(options,app.callback()).listen(443);

22.这样,后台就跑在htpps下了。

23.在服务器上运行后台:

24.

25.然后你用自己的电脑打开该域名,你会发现根本连接不上。

26.那么,这是什么鬼?

27.原来阿里的windows服务器防火墙做了限制,且服务器安全组也做了限制。

28.打开防火墙=高级设置=入站规则=新建规则。

29.

30.选择端口=填写端口

31.

32.本次需要填写三个端口号,后台的80(http)443(https)3306(mysql)

33.在阿里云控制器那里配置安全组,如下图所示。

34.

35.

36.填写那三个端口号,授权对象那里填写0.0.0.0/0

37.

38.做完这一步,大功告成了,你的域名可以被访问了。

39.

3.数据库设计

40.数据表有两张,分为动态表与运动数据表,如下图所示。

41.

4.前端设计与开发4.1首页

42.首页主要由动态广场与底部的tabbar组成,动态广场显示状态正常(state==1)的动态,私人动态与禁止动态不能被他人所看见,在数据表设计中,uid其实就是openid。用户上传了图片后,数据库中保存的是其访问地址,多个地址之间用逗号分隔,形成字符串,前端拿到图片地址后,将其转成数组。

43.

44.关键代码如下:

onShow:asyncfunction(){varthat=this;letpageno=1;letpagesize=this.data.pagesize;this.setData({pageno:1});noticesource.findalllnotice(pageno,pagesize).then(function(res){console.log(res);for(letiinres){letimage=res[i].photo.split(',');res[i].imageurl=image;}that.setData({allnotice:res});})},

45.动态页面使用分页加载,每次下拉加载10条,内容展示区使用scroll-view组件,设置bindscrolltolower=“upper”,下拉时触发upper方法。

46.关键代码如下:

upper:function(){varallnotice=this.data.allnotice;varpageno=++this.data.pageno;varpagesize=this.data.pagesize;console.log(pageno);varthat=this;this.setData({pageno:this.data.pageno++});noticesource.findalllnotice(pageno,pagesize).then(function(res){console.log(res);for(letiinres){letimage=res[i].photo.split(',');res[i].imageurl=image;}if(res.length0){allnotice=allnotice.concat(res);}console.log(allnotice);that.setData({allnotice:Array.from(newSet(allnotice))});})}4.2动态发布页

47.动态发布页主要有文本输入框,图片上传区、发布按钮组成。

48.

49.该页面为重要页面,在发布前,我们需要获取发布者的头像、昵称、openid,如果这三个必要条件缺一的话,我们就会阻止用户发布动态并且提示用户授权登录。

50.获取openid关键代码如下:

app.jswx.login({success(res){console.log('code:'+res.code);if(res.code){loginsource.login(res.code).then(function(data){console.log(data);wx.setStorage({key:'openid',data:data.openid,})wx.setStorage({key:'session_key',data:data.session_key,})})}else{console.log('登录失败!'+res.errMsg)}}});

51.用户在进入小程序时,我就会让一次后台请求,根据wx.login返回的code解密生成openid与session_key,我将其存放在storage中,**其实,我不推荐将session_key放在storage中,按正常开发方式,应该是后台临时存储session_key,并返回一个sessionid给用户,不应该把session_key返回给用户。**但是,我在用koa-session时,一直没有成功,不知道什么鬼!没办法,我只好把session_key返回给用户了。

52.上传图片关键代码:

didPressChooesImage:function(){if(this.data.imageurl.length=4){wx.showToast({title:"不能超过4张",})return}varthat=this;didPressChooesImage(that);//没有写错,只是同名,见2.1节},//删除指定图片deleteImage:function(e){letindex=e.currentTarget.dataset.index;varthat=this;varimagetemp=that.data.imageurl;imagetemp.splice(index,1);that.setData({imageurl:imagetemp});},4.3我的页面

53.我的页面包含了用户的数据统计信息与小程序的推广信息,目前有待完善,从我的页面进去的动态页面只包含用户自己发布的。

54.

在未登陆时,页面显示默认头像,点击头像,授权个人信息,显示微信头像与昵称。

55.

4.4群间运动PK

56.接下来,重头戏来了,运动数据pk为该小程序的核心功能,

57.功能点:

58.1)获取用户此时的运动步数并展示出来

59.2)分享自己的运动步数到微信群并在页面上形成pk排名区

60.3)其他用户通过分享进入小程序,系统获取其群id与运动步数与同一微信群的用户进行pk

61.4)每次分享或点击分享链接,系统将自动更新该用户的运动步数

62.5)pk排名区只展示当日的排名情况,晚上12点后自动清空pk区

63.技术点:

64.1)获取用户运动步数

65.2)获取群id

各群之间间运动数据隔离

66.效果如下图所示

67.

68.授权后显示步数。

69.

70.点击选择一个聊天后,将发布分享到微信群里,分享成功后,前端获取到ShareTicket,群内其他人通过该链接进来,前端也会获取到ShareTicket,调用wx.getShareInfo()将加密数据发送给后端解密,可获得openGid,将用户的步数与openGid等信息存储起来,形成了groupsport表。

71.保存当日已分享的群id,获取ShareTicket关键代码:

onLoad:function(opt){//在storage中创建用户的当日分享情况也就是分享到了哪些群,将这些群id存在一个与日期挂钩的对象中,到了第二天,清空群id.varthat=this;varnowDate=newDate();varyear=nowDate.getFullYear();varmonth=nowDate.getMonth()+110?"0"+(nowDate.getMonth()+1):nowDate.getMonth()+1;varday=nowDate.getDate()10?"0"+nowDate.getDate():nowDate.getDate();vardateStr=year+"-"+month+"-"+day;varvalue=wx.getStorageSync('openGidlist')if(value!=undefined||value!=null){console.log(value);if(value.date!=dateStr||value.Gidlist==undefined||value.Gidlist==null){wx.setStorageSync('openGidlist',{date:dateStr,Gidlist:[]});}}//设置开启ShareTicketwx.showShareMenu({withShareTicket:true})}

72.获取个人运动数据

onShow:function(){wx.getWeRunData({success:function(res){console.log(res);letsession_key=wx.getStorageSync("session_key");console.log(session_key);sportsource.getsportdata(res,session_key).then(function(data){console.log(data);that.setData({todaysportcount:data.data.stepInfoList[30].step});})}})}

73.分享时获取群id

onShareAppMessage:function(){varnowDate=newDate();varyear=nowDate.getFullYear();varmonth=nowDate.getMonth()+110?"0"+(nowDate.getMonth()+1):nowDate.getMonth()+1;varday=nowDate.getDate()10?"0"+nowDate.getDate():nowDate.getDate();vardateStr=year+"-"+month+"-"+day;varthat=this;return{title:'我已经运动了'+this.data.todaysportcount+'步,你敢来PK吗?',path:'pages/sportpk/sportpk',success:function(res){varshareTickets=res.shareTickets;if(shareTickets.length==0){returnfalse;}console.log('shareTickets'+shareTickets[0]);wx.getShareInfo({shareTicket:shareTickets[0],success:function(res){varencryptedData=res.encryptedData;variv=res.iv;console.log(res);letsession_key=wx.getStorageSync("session_key");sportsource.getsportdata(res,session_key).then(function(data){console.log(data);letopenGid=data.data.openGId;that.setData({openGid:openGid});letopenid=that.data.openid;lettodaysportcount=that.data.todaysportcount;letavatarUrl=that.data.userInfo.avatarUrl;letnickname=that.data.userInfo.nickName;if(todaysportcount==undefined||todaysportcount==null){wx.showToast({title:'请重新打开运动权限',duration:2000})return}if(avatarUrl==undefined||avatarUrl==null||nickname==undefined||nickname==null){wx.showToast({title:'请先点击头像登录',duration:2000})return}//letopenGidlist=that.data.openGidlist;letopenGidlist=wx.getStorageSync('openGidlist');openGidlist.Gidlist.push(openGid);openGidlist.Gidlist=[...newSet(openGidlist.Gidlist)]console.log("---------------------------------------");console.log('openGidlist.Gidlist'+openGidlist.Gidlist);console.log("---------------------------------------");wx.setStorageSync('openGidlist',{date:dateStr,Gidlist:openGidlist.Gidlist});sportsource.creategroupsport({openGid,openid,todaysportcount,avatarUrl,nickname}).then(function(){that.setData({sharegroupdata:[]});that.getfirstgroupsport();})})}})},fail:function(res){//转发失败}}}

74.刷新群内pk

getfirstgroupsport:asyncfunction(){varthat=this;letopenGidlist=wx.getStorageSync('openGidlist');if(openGidlist.Gidlist!=undefined||openGidlist.Gidlist!=null){if(openGidlist.Gidlist.length0){for(letiinopenGidlist.Gidlist){letdata=awaitsportsource.getgroupsport(openGidlist.Gidlist[i]);console.log(data);letsharegroupdata=that.data.sharegroupdata;sharegroupdata.push(data.data.rows);that.setData({sharegroupdata:sharegroupdata})}}}},

75.点击分享链接获取shareTicket,通过链接进入小程序的场景值是1044

app.jsonShow(opt){console.log("opt.scene"+opt.scene);if(opt.scene==1044){this.globalData.shareTicket=opt.shareTicket;}},

76.

5.后端开发

77.后端采用的是koa2,通过sequelize.js实现与mysql的连接。

5.1获取用户openid与session_keyfunctiongetOpenId(code){console.log(code);returnnewPromise((resolve,reject)={constid='';//AppID(小程序ID)constsecret='';//AppSecret(小程序密钥)leturl=`https://api.weixin.qq.com/sns/jscode2session?appid=${id}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;axios.get(url).then(function(response){console.log("response.data:"+response.data);resolve(response.data);}).catch(function(error){console.log(error);reject(error);});})}asyncfunctionlogin(ctx){const{code}=ctx.query;constdata=awaitgetOpenId(code);ctx.body=data;}5.2运动数据与群id数据获取

78.这两个数据属于隐私数据,需要用算法解密,具体的流程,微信开发手册上有,我就不多说了,需要用到上一步的session_key来解密。

//WXBizDataCryptconst:微信提供的解密方法WXBizDataCryptconstWXBizDataCrypt=require('./WXBizDataCrypt')varappId='';asyncfunctiongetsportdata(ctx){varencryptedData=ctx.query.encryptedData;variv=ctx.query.iv;varsession_key=ctx.query.session_key;console.log("session_key"+session_key);varpc=newWXBizDataCrypt(appId,session_key);vardata=pc.decryptData(encryptedData,iv);console.log('解密后data:',data);ctx.body={success:true,data:data}}5.3创建与读取运动数据

79.当用户分享自己的运动数据到微信群内时或者微信群内其他用户通过该链接进入小程序时,后端将获创建或者更新该用户的群内运动数据。

constcreategroupsport=asyncfunction(data){//给某个用户创建一条群运动记录letnowDate=newDate();letyear=nowDate.getFullYear();letmonth=nowDate.getMonth()+110?"0"+(nowDate.getMonth()+1):nowDate.getMonth()+1;letday=nowDate.getDate()10?"0"+nowDate.getDate():nowDate.getDate();letdateStr=year+"-"+month+"-"+day;varcountdata=awaitTodolist.findAndCount({where:{openGid:data.openGid,createdate:dateStr,openid:data.openid}})varcount=0;if(countdata!=undefined||countdata!=null){count=countdata.count;}if(count==0){awaitTodolist.create({openGid:data.openGid,openid:data.openid,todaysportcount:data.todaysportcount,createdate:dateStr,avatarUrl:data.avatarUrl,nickname:data.nickname})}else{awaitTodolist.update({todaysportcount:data.todaysportcount,avatarUrl:data.avatarUrl,nickname:data.nickname,openid:data.openid,},{where:{id:countdata.rows[0].id}})}returntrue}//读取群内运动数据constgetgroupsport=asyncfunction(openGid){console.log('openGid'+openGid);letnowDate=newDate();letyear=nowDate.getFullYear();letmonth=nowDate.getMonth()+110?"0"+(nowDate.getMonth()+1):nowDate.getMonth()+1;letday=nowDate.getDate()10?"0"+nowDate.getDate():nowDate.getDate();letdateStr=year+"-"+month+"-"+day;constdata=awaitTodolist.findAndCount({where:{openGid:openGid,createdate:dateStr},order:[['todaysportcount','DESC']]})console.log(data);returndata;}5.4动态的获取

80.对用户发布的动态,后台目前主要有增,改,查三类方法,我说一下分页查询。

constfindallnotice=asyncfunction(ctx){//查询所有letpageno=ctx.pageno;letpagesize=ctx.pagesize;console.log(pageno,pagesize);constdata=awaitTodolist.findAndCount({where:{state:1},offset:(pageno-1)*pagesize,//开始的数据索引,比如当page=2时offset=10,而pagesize我们定义为10,则现在为索引为10,也就是从第11条开始返回数据条目limit:pagesize*1//每页限制返回的数据条数})console.log(data);returndata;}constfindmynotice=asyncfunction(ctx){//查询自己的letpageno=ctx.pageno;letpagesize=ctx.pagesize;letuid=ctx.openid;console.log(pageno,pagesize,uid);constdata=awaitTodolist.findAndCount({where:{uid:uid},offset:(pageno-1)*pagesize,//开始的数据索引,比如当page=2时offset=10,而pagesize我们定义为10,则现在为索引为10,也就是从第11条开始返回数据条目limit:pagesize*1//每页限制返回的数据条数})console.log(data);returndata;}6.总结

81.我洋洋洒洒写了几千字,看上去举重若轻,但是在实际开发中经常会碰见各种各样的问题,该项目从原型设计与开发到部署都是我独自完成的,中间也踩了一些坑,这个项目最终没能上线,是因为,个人主体账号不能发布关于GUC的小程序。

82.本文首发于我的个人博客:https://www.catac.cn,转载时请注明来源,

83.该项目源码地址:https://github.com/oumingliang/ydonline.git

84.也欢迎各位与我交流,个人QQ:2541511219

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎