{"id":20901338,"url":"https://github.com/wenkeshi/wxapp","last_synced_at":"2025-05-13T02:30:39.705Z","repository":{"id":201475628,"uuid":"107931197","full_name":"wenkeShi/wxapp","owner":"wenkeShi","description":"🙋travelib-node 流动图书Node.js后端","archived":false,"fork":false,"pushed_at":"2018-02-03T17:34:51.000Z","size":604,"stargazers_count":145,"open_issues_count":2,"forks_count":46,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-01T15:22:24.692Z","etag":null,"topics":["express","mongoose","nodejs","pm2","websocket"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wenkeShi.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-10-23T04:14:43.000Z","updated_at":"2024-09-13T08:12:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"4338102f-e226-4a92-b4ca-a0ef0a462b43","html_url":"https://github.com/wenkeShi/wxapp","commit_stats":null,"previous_names":["wenkeshi/wxapp"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wenkeShi%2Fwxapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wenkeShi%2Fwxapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wenkeShi%2Fwxapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wenkeShi%2Fwxapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wenkeShi","download_url":"https://codeload.github.com/wenkeShi/wxapp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253859408,"owners_count":21975101,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["express","mongoose","nodejs","pm2","websocket"],"created_at":"2024-11-18T11:29:32.774Z","updated_at":"2025-05-13T02:30:39.192Z","avatar_url":"https://github.com/wenkeShi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 流动图书馆\n流动图书馆是一个图书漂流和借阅工具，旨在共享闲置图书，并链接趣味相投的小伙伴。\n\n# 预览\n[链接](http://xinwenke.top/travelib/)\n\n![image](http://xinwenke.top/assets/images/travelib_code.jpg)\n\n# 技术栈\n\n- **小程序MINA框架**： 一个响应的数据绑定框架。分为两块视图层(View)和逻辑层(App Service)\n- **Flex**：flex弹性布局\n- **Express** : http服务框架\n- **websocket**: 前后端消息的实时推送\n- **mongoose**: 操作mongodb数据库\n- **pm2**: 服务端使用pm2部署，常驻进程\n\n# 截图\n\u003e 首页\n\n![1.png](http://upload-images.jianshu.io/upload_images/7234109-a26a4629a511bd9e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n\u003e 借阅书架\n\n![2.png](http://upload-images.jianshu.io/upload_images/7234109-b493fc714d417e2e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n\u003e 发布的图书\n\n![3.png](http://upload-images.jianshu.io/upload_images/7234109-5445d402963acf4b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n\u003e 借阅的图书\n\n![4.png](http://upload-images.jianshu.io/upload_images/7234109-76ddee279c861e3a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n# 客户端\n\n- 代码结构\n\n![image.png](http://upload-images.jianshu.io/upload_images/7234109-c6fa3ff47400d6a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n微信小程序中每个页面会有四个文件 ```.js``` ```.json``` ```.wxml``` ```.wxss```\njs文件中是页面的逻辑，json文件是页面的一些配置，wxml是小程序的页面结构，wxss为页面的样式。\n\n- 封装http请求\n\n```\nconst request = (obj) =\u003e {\n  if(obj.header){\n    obj.header.sessionId = session.sessionId;\n  }else{\n    obj.header = { sessionId: session.sessionId};\n  }\n  wx.request(obj);\n}\n```\n在请求头中手动加上sessionId,因为小程序没有cookie。\n\n- websocket\n```\n//连接websocket\nwx.connectSocket({\n  url: 'wss://liudongtushuguan.cn/socket?sessionId=' + session.sessionId,\n});\n\nwx.onSocketOpen(function(res){\n\n});\nwx.onSocketClose(function(res){\n  console.log('websocket closed');\n});\n\nwx.onSocketMessage(function(res){  //收到消息的回调\n  let msg = JSON.parse(res.data);\n  let msgs = that.data.borrowMessage;\n  msgs.unshift(msg);\n  that.setData({ borrowMessage: msgs});\n});\n\n```\n```\n//发送socket消息\n  let data = JSON.stringify({\n    targetId: bookData.ownerId,\n    nickName: APP.globalData.userInfo.nickName,\n    bookName: bookData.title,\n    time: new Date().toLocaleString(),\n    bookId: bookId,\n    wxNum: wxNum,\n    phoneNum: phoneNum,\n    msg: msg,\n  });\n  wx.sendSocketMessage({\n    data: data,\n  });\n```\n# 服务端\n\n- 代码目录\n\n![image.png](http://upload-images.jianshu.io/upload_images/7234109-60e7d1528ee6f219.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n- Express框架实现http服务\n```\nconst https = require('https');\nconst fs = require('fs');\nconst express = require('express')\nconst cookieParser = require('cookie-parser');\nconst bodyParser = require('body-parser');\nconst app = express();\nconst queryString = require('querystring');\nconst URL = require('url');\nconst socket = require('./service/socket');\n\nconst router = require('./routes/router').router;\n\n//获取认证证书\nvar key = fs.readFileSync('./key/2_www.liudongtushuguan.cn.key');\nvar cert = fs.readFileSync('./key/1_www.liudongtushuguan.cn_bundle.crt');\n\nvar options = {\n\tkey : key,\n\tcert : cert,\n};\n\napp.use(cookieParser());\napp.use(bodyParser.json());\n\nconst httpsServer = https.createServer(options,app);\nhttpsServer.listen(443,() =\u003e{\n    console.log('listening 443 port');\n});\n\nsocket(httpsServer);  //websocket\n\napp.use(router);\n```\n小程序规定必须要用https协议。\n\n- websocket模块\n\n这里使用ws模块而没有选择使用socket.io，因为小程序客户端不支持socket.io\n\n```\nconst WebSocket = require('ws');  //使用ws模块\nconst queryString = require('querystring');\nconst URL = require('url');\nconst sessions = require('./session');\n\n\n\nmodule.exports = (httpServer) =\u003e {\n\n\tconst wss = new WebSocket.Server({server : httpServer});\n\n\twss.on('connection',(ws, req) =\u003e {\n\t\t\tlet sessionId = queryString.parse(URL.parse(req.url).query).sessionId;\n\t\t\tws.id = sessionId;\n    \t    ws.on('message' , (msg) =\u003e {\n    \t    \t\tlet msgObj = JSON.parse(msg);\n    \t    \t\tif(sessions[msgObj.targetId]){\n    \t    \t\t\twss.clients.forEach((client) =\u003e {\n    \t\t\t\t\tif(client.id === msgObj.targetId){\n    \t\t\t\t\t\tlet data = {\n    \t\t\t\t\t\t\ttime : msgObj.time,\n    \t\t\t\t\t\t\tborrower :msgObj.nickName, \n    \t\t\t\t\t\t\tbook :msgObj.bookName,\n    \t\t\t\t\t\t\tborrowerId : sessions[sessionId],\n    \t\t\t\t\t\t\tbookId : msgObj.bookId,\n    \t\t\t\t\t\t\twxNum : msgObj.wxNum,\n    \t\t\t\t\t\t\tphoneNum : msgObj.phoneNum,\n    \t\t\t\t\t\t\tmsg : msgObj.msg\n    \t\t\t\t\t\t};\n    \t\t\t\t\t\tclient.send(JSON.stringify(data));\n    \t\t\t\t\t}\n    \t\t\t\t});\n    \t    \t\t}\n    \t    });\n\t});\n};\n```\n- mongoose操作数据库\n\n```db.js:```\n\n```\nconst mongoose = require('mongoose');\n\nmongoose.connect('mongodb://app:12345678@127.0.0.1/wxapp');\n\nconst connection = mongoose.connection;\nconnection.once('open', (err) =\u003e {\n\tif(err){\n\t\tconsole.log('Database connection failure');\n\t}else{\n\t\tconsole.log('Database opened');\n\n\t}\n});\n\nconnection.on('error',(err) =\u003e {\n\tconsole.log('Mongoose connected error '+ err);\n});\n\nconnection.on('disconnected', () =\u003e {\n\tconsole.log('Mongoose disconnected');\n});\n\nmodule.exports = {\n\tconnection : connection,\n\tmongoose : mongoose,\n};\n```\n```model.js:```\n```\nconst Schema = mongoose.Schema;\n\nconst UserSchema = new Schema({\n\tonlyId : {type: String},\n\tpublishedBooks : {type: Array},\n\tborrowedBooks : {type: Array},\n\tborrowMessages : Array, \n});\n\nconst BookSchma = new Schema({\n\tisbn : String,\n\ttitle : String,\n\tpress : String,\n\tauthor : String,\n\trate : String,\n\ttags : String,\n\timage : String,\n\tstatus : {type : Boolean,default : true},\n\townerId : String,\n\towner : String,\n\townerImage : String,\n});\n\nconst userModel =  mongoose.model('user' , UserSchema);\nconst bookModel = mongoose.model('book' , BookSchma);\n\nmodule.exports = {\n\tUserModel  : userModel,\n\tBookModel : bookModel,\n}\n```\n- 获得微信用户的openId\n\n小程序前端会请求微信服务器得到一个code, 将code发送给自己的服务器，然后自己的服务器给微信服务器发送请求，得到微信用户的唯一标识openId\n\n```\nconst https = require('https');\nconst queryString = require('querystring');\n\nconst sessions = require('./session');\n\n\nmodule.exports = (req, res, next) =\u003e {\n\t\tlet code = req.query.code;\n\t\tlet otherRes = res;\n\t\tDATA.js_code = code;\n\t\tOPTION.path = PATH + queryString.stringify(DATA);\n\t\tlet wxReq = https.request(OPTION, (res) =\u003e {\n\t\t\tif(res.statusCode == 200){\n\t\t\t\tlet json = '';\n\t\t\t\tres.on('data' , (data) =\u003e {\n\t\t\t\t\tjson+=data;\n\t\t\t\t});\n\t\t\t\tres.on('end' , () =\u003e {\n\t\t\t\t\tjson =JSON.parse(json);\n\t\t\t\t\tlet openId = json.openid;\n\t\t\t\t\tsessions[openId] = openId;\n\t\t\t\t\totherRes.type('application/json');\n\t\t\t\t\totherRes.json({\n\t\t\t\t\t\tdata : {'sessionId' : openId},\n\t\t\t\t\t});\n\t\t\t\t\totherRes.status(200);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\twxReq.end();\n};\n```\n# 使用pm2部署\n\n安装pm2\n```\nnpm install  -g pm2\n```\n启动应用\n```\npm2 start app.js\n```\n# 总结\n流动图书馆小程序是由三个人的小团队设计和开发的。我主要负责前后端的开发工作。这对从来没接触过服务端和小程序开发的我来说是一个挑战当然也是一次难得的学习机会。从最初对小程序，服务端两眼一抹黑，到搭建出应用的雏形，实现基本的效果，再到最后的拆分整合代码，这期间经历了很多，也收获了很多。从数据库的设计到后台数据库操作、会话管理、http服务接口一直到前后端数据交互、小程序前端，我对程序开发的大致流程有了更进一步的了解，弥补的之前对服务端知识的缺失。\n\n三个人的小团队，因为兴趣结在一起，利用工作之余的时间完成自己喜欢的事情真的是一件令人十分有成就感的事情。\n\n有兴趣的可以看源码\n\n[小程序前端](https://github.com/wenkeShi/travelibrary)\n\n[小程序后端](https://github.com/wenkeShi/wxapp)\n\n如果觉得不错，就毫不吝啬地给个star吧。后期项目还会继续更新和完善。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwenkeshi%2Fwxapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwenkeshi%2Fwxapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwenkeshi%2Fwxapp/lists"}