上个周末,也就是10月24、25号,参加了人生中第一次 黑客马拉松 (hackathon),虽然最终没有获奖,但是这个比赛过程中还是 hack 的挺爽,趁现在还有余热,纪录下比赛时的一些心得与收获。
当在公司得知有黑客马拉松之后,我就立即报名了,觉得这件事本身就很酷,虽然身边一些同事说此类比赛没意思,大部分人都是奔着投资去的,但我还是觉得要你怎么看了,你如果去是为了那奖品、钱,我觉得那失去了 hackathon 的意义了,hackathon 我理解的就是
做一些很酷的事情,而这些事情在平时的工作中“用不到”,但是完成这些事能够让我们有很强的满足感。
也就是玩,虽然当知道自己的作品没有获奖时会有些许失落,但那是一时的,最起码那一天一夜 coding,让我确实很 high。
由于时间短,我就想着这次比赛用 nodejs 来做,参加比赛前的一周,我停止了 SICP 的阅读,转而进攻 《Node.js实战》 ,这本书之前断断续续翻过前两者,这次基本把这本书看完了,主要是学习了下如果系统的开发一个完整的 node 应用,包括常用模块、通用架构等,之前写的 node 都是玩具,没有错误处理,没有单元测试(这次比赛虽然也没用上,但是知道了如何使用相应工具去测了)。
然后书上最后一章介绍了 node 中较为底层的知识,像 net
库,node 的定位就是提供小而美的核心类库,常用的模块都是基于这些核心类库构建。下面纪录两个书中比较有趣的例子:
varnet =require("net"); varsocket = net.connect({host:"github.com", port:22}); socket.on("data",function(chunk){ console.log(chunk.toString()); socket.end(); }) // 启动后,会输出 SSH-2.0-libssh-0.7.0
下面的代码片段实现了类似于 nc 命令:
varnet =require("net"); varsocket = net.connect({host: process.argv[2], port: process.argv[3]}); socket.on("connect",function(){ process.stdin.pipe(socket); socket.pipe(process.stdout); process.stdin.resume(); }); socket.on("close",function(){ console.log("bye..."); });
其次在看 expressjs 时,无意间发现其作者 tj 早在 2014年4月份,就已经 抛弃 nodejs,投向 go 的怀抱 ,心中难免有些忧伤,大牛总是这样,在我们还在学习某东西时,人家已经发现其缺点,转向更高深的地方……
这次的比赛是命题制——技术改变生活,这基本上是没有限制,经过有赞小伙伴的一番讨论,最终定了3个题目,然后就开始组队做了,我和劲风一组,做的是一个超市扫码购物的微信应用,想要解决的基本问题是——超市排长队付款。对于我来说,主要是想做一些有难度的技术,挑战自己,也没想为什么现在超市为什么不推行扫码购物,当然这也是后来评委问我们的问题。这个题目主要的技术难点有:
如何扫码是我们遇到的第一个问题,是借助微信还是自己做原生应用,由于我俩都不会 Android 与 IOS 开发,所以微信成了唯一选择。
微信开发需要有公众号,如果调用 JS-SDK ,需要有备过案的域名,我们都没有,这时我想到了 大宝 兄,他很慷慨的给我提供云主机、mysql、nginx,加上宝贵的域名,很是感谢。(后面知道了可以用 测试号 )。
这里必须吐槽下微信的开发文档,真是烂:排版烂、个别语句不通顺、经常有死链接,真不知道微信团队里都是些什么人。
比如这里的 签名算法 ,微信的人不知道 命名锚(named anchors) ,所以你需要在打开上面的链接后,需要用 Ctrl + F
来搜索 “签名算法” 才能找到我这里所说的签名算法,最最坑人的是,由于签名是针对网页 URL 的,所以一个网页需要签名一个,而这个 URL 必须是以 /
结尾,比如,如果我们用 http://1024.sundabao.com
这个 URL 来签名是不对的,必须是 http://1024.sundabao.com/
,这个真的好坑。
相比之下,Github的 开发者文档 ,看起来就很让人舒服,希望微信的好好学学。
扫码问题解决了,剩下的就是一个集成购物车的订单系统,之前在公司虽然也是在数据部,但是报表做的不多,真是没想到这订单系统是多么麻烦,我当时遇到问题就是,购物车选好后,点击提交,这时,按理说应该生成订单的,但是生成订单的同时是否需要把购物车的商品删除呢,第一感觉是需要,但是后来发现不是这样的,如果顾客发现还有商品没有购买,这时他会返回上一页继续购买,所以正确的做法是在确认支付订单后,再去把购物车的商品删掉。但是这样也会有问题,因为顾客确认支付方式后,有可能支付失败了,这时按理说购物车里的东西还是应该有的,但是我们这里比较简单,只要用户点击支付,我们就认为这成功了。可见,要做一个完整的交易+订单系统,是多么不容易的事。
由于订单系统的逻辑比较多,涉及很多数据库的操作,而我们使用 nodejs 也没用什么 ORM 系统,只是用原生的 sql 来做,这时就陷入了 callback hell ,之前写 node 程序一般都不怎么关注错误处理, 所以一直没怎么发现这个问题,这次在做这个订单系统,真是暴露无疑,太难维护了。
下面代码片段的功能是:扫描一个商品,向购物车列表中增加一个商品的 callback hell
exports.add = function(userId, goodsId, goodsNum, cb) { varthat = this; vardbPool = db.getPool(); varsql ="insert into 1024_cart values (?,?,?) ON DUPLICATE KEY UPDATE goods_num=goods_num+1"; sql = db.formatSQL(sql, [userId, goodsId, goodsNum]); dbPool.query(sql, function(err, result) { if(err) { logger.error("exec:"+ sql +" error:"+ err); cb({code:-2, msg:"服务器内部错误!"}); } elseif(result.affectedRows >0) { goodsDAO.select(goodsId, function(err, result) { if(err) { logger.error("exec:goodsDAO.select. error:"+ err); cb({code:-2, msg:"服务器内部错误!"}); } elseif(result.length ==0) { that.delete(userId, goodsId); cb({code:-1, msg:"数据库中没有该商品!"}); } else{ vargoods =result[0]; getGoodsNum(userId, goodsId, function(err, result) { if(err) { cb({code:-2, msg:"服务器内部错误!"}); } else{ goods["num"] =result[0]["goods_num"]; cb({code:0, data: goods}); } }) } }); } else{ cb({code:-3, msg:"修改失败!"}); } }); }
在整个比赛过程中(大概20个小时),我睡了不到4个小时,大脑一直处于兴奋状态,一直在解决问题,从动态获取微信签名,到解决订单系统的 bug,到最后的测试,都是极度兴奋的,coding 的比较 high。
记得在学生时代就不断听到有人说,程序员是青春饭,做几年后要转向管理岗,真不知道说这些话的人是出于什么心理,当然有部分人是把编码当成为一份养家糊口的工作,但是我相信更多人是因为热爱编码而编码的,从编码中能汲取无限快乐。
如果你身边在有人 balabala 的说诸如此类的话,我劝你最好离这种人远些,道不同不相为谋,世界这么大,为什么不去做自己喜欢的事呢?
注:图片均来自 sf 官方,如涉及个人隐私请告知。
这次参加比赛,玩的很开心,没什么遗憾。至于代码就不开源了,写的比较烂,后面等功力提升了在说这事。感兴趣可以看看我们作品 易购 EasyGo 的简介。
这里我想回答当时评委问我们组的问题—— 为什么现在的超市不推广扫码支付 :
对于这些,我只能说,经济基础决定上层建筑,商家还是以盈利为目的的。
比赛是结束了,但生活的挑战还在继续,SICP 要继续搞起了,这次停了有两个多星期了,真要多下功夫了。
希望大家都能够 happy hacking !