**html
**
第一次在社区发文章,做为一个大学未毕业的前端菜鸟,本身日常也常常逛各类技术社区,今天终于要发表本身的处女文章了,仍是有点小激动的。因为最近在帮学校作开发一个基于微信小程序的投票系统项目,开发时也遇到不少坑,有一些心得,因此想分享给你们,一块儿讨论和进步。(我的开发技术有限,很差勿喷--)。前端
服务器环境:Ubuntu 16.04+Node.js+Express
数据库环境:普惠大众的MySQL
前端及测试:微信小程序开发工具
技术:Javascript+HTML+CSS+Nodemysql
排名这些界面就不展现了,因为还在测试阶段,界面仍是比较学院派的,后期还会增长功能和修改界面。sql
其实整个项目难点很少,大部分是数据的展现和一些简单的交互而已。而要作一个比较完善的投票系统,重头戏必然是投票这个功能是否能完美的实现并展示在用户的眼前。本项目中涉及到的难点就是如何使用户在不进行注册登陆帐号,甚至不用获取微信登陆权限的状况下进行投票,同时系统能准确地记录下每一个用户的投票行为,这里就要说说数据库的设计、小程序登陆机制以及Node的应用了。数据库
因为要实现每一个用户天天只能为同一个做品投一票的限制,大概想到的3钟方案:json
1. 每个用户都有一个对应的投票表,每投一个做品就增长该做品的id,次日删除该id,查询到不存在时便可再次投票。
2. 同理也能够每一个做品有一个对应的投票表,一个用户投一票,就增长该用户的id。
3. 在做品表中添加一个投票用户字段,一个用户投一票则将该用户的id加进去,经过查询该id是否存在来识别是否已经投过票。小程序
前两种方案无形之中就会生成不少个表,而且其实这些表其实都是同一性质的。因此最终选择了第三种方案,做品表中添加一个字段,在该字段中添加每一个投票用户的id(若是用户量很大的状况下应该会使用前面的或其余更好的方案)。可是通常字段所用的类型varchar最大长度只有255,当数据多一些的时候不能知足须要。而Mysql的text类型可以知足,最大的LONGTEXT能有4GB的存储,对于一个校园使用人数来讲也是足够了(好比一个用户的openid有28个英文字符,也能存储千万级别的用户了。不过听说text类型的性能是不如varchar等类型的,至于影响有多大本人没有测试过)。可是同一个字段中增长字符,须要作一下处理,用符号将添加的数据分隔开来,方便后面的查询。微信小程序
那么如何识别每一个投票用户的身份呢?若是不受权获取微信用户的信息,那么能够直接获取微信用户的openid,这个openid是用户惟一的标识。小程序官方的介绍是:小程序能够经过微信官方提供的登陆能力方便地获取微信提供的用户身份标识,快速创建小程序内的用户体系。api
小程序的API中有个wx.login方法,经过提供小程序的Appid和AppSecret以及每次登陆生成的一个不一样的用户登陆凭证,而后在服务器端经过微信官方接口获取到用户的openid等数据。具体官方文档和API等连接:https://developers.weixin.qq....
微信端请求:服务器
wx.login({ success: res => { console.log(res.code) wx.request({ url: 'http://localhost:8080/data', data: { code: res.code, Appid: "wxXXX", AppSecret: "XXX", }, header: { 'content-type': 'application/json' }, success: function (next) { console.log(next.data) that.setData({ showlist: next.data }) } }) } });
服务器端获取openid(Node)
//定义微信小程序发送到服务器的参数 var code = req.query.code; var appid = req.query.Appid; var appsecret = req.query.AppSecret; var index = req.query.id; console.log(appid); //请求外部url,传参返回用户ID等数据 request('https://api.weixin.qq.com/sns/jscode2session?appid='+appid+'&secret='+appsecret+'&js_code='+code+'&grant_type=authorization_code', function (error, response, data) { if (!error && res.statusCode == 200) { const connection = mysql.createConnection({ //连接数据库的配置信息 host: "localhost", user: "root", password: "XXX", port: 3306, database: "display", multipleStatements: true }); // 打开连接 connection.connect(); //将data返回字符串形式转换成JSON格式. var id = JSON.parse(data) var newid = id.openid } })
接着就是往数据库中对应字段添加用户id就好了。可是这里有两个细节,也能够说是两个坑吧,因为本身菜,搞了半天才弄好...
1.若是该用户没有投过票,那么就向字段中添加该用户的openid,若是投过票说明字段中已经存在该用户id,返回提示信息。text类型是没有默认值的,因此初始没有投票用户时为Null。这里我在Node中直接用JS的indexOf函数是否包含该id字符串的方式来查询,因而就形成了一旦该字段为Null时查询就报错的状况。
解决办法:在查询以前,先将该字段为Null的都转换为空字符串。刚开始想用JS判断该字段是否为空再进行操做,可是后来认为更好的解决方案是在查询以前直接用update将该字段的null值修改成空字符串,相比用JS判断清爽了许多。
connection.query( "update display set d_user='' where d_user is null ",[], function (queError,queData){ ... ... })
2.插入用户id数据时,经过","将每一个用户的openid分隔开,使用到concat函数拼接字符串。可是因为拼接的是前面获取的id变量和一个字符串",",因此写法比较坑。concat的第二个参数是字符串,用引号" "包裹,天真觉得里面直接写变量,外面有括号,那出来不就直接是字符串了吗?可是却会报错,显示的并非字符串。
解决办法:若是里面直接用ES6语法写`${newid}`是不行的,得像下面这样写:
connection.query( "update display set d_user = concat(d_user, "+`'${newid},'`+") where d_id = "+index,[], function (err, data) { console.log(err) //null console.log(data) // object });
解决了这两个问题以后,其余就是同时增长票数等代码,再也不赘述。
这是能实现用户天天都能投票的关键,指定时间清除掉字段中的openid信息,就能达到次日用户可以继续投票的功能。这里有多种方法实现,第一种是直接在数据库层操做,经过存储过程或者代码的方式将该字段的内容赋为空值便可。第二种是在Node里用定时器或者其余方法在指定时间将数据库该字段内容从新赋值。
我采用的是用JS定时器来解决,当天天00:00:00时,就从新赋值。可是要注意定时器的启动和清除,不然一直开启确定会有性能影响。
var timer = setInterval(function(){ var date = new Date(); var hours=new Date().getHours(); var minutes=new Date().getMinutes(); var seconds=new Date().getSeconds(); if(hours == 23 && minutes == 59 && seconds == 59){ connection.query("update display set d_user = '' ",[], function (err, data) { console.log(data) }) } },1000)
后面有时间会更新活动倒计时的写法,每一个页面生成二维码的方法等。
因为数据库并非很在行,不合理的地方确定还有不少,但愿获得大佬们的指点。同时,小程序前端仍是有不少细节的地方遇到过坑,后面我会单独写文来分享。若是你们以为还算有点看头的话,但愿点个赞啥的,给菜鸟新人一点鼓励哈哈哈哈。