HotApp云笔记
来源:互联网 发布:组态软件免费下载 编辑:程序博客网 时间:2024/06/04 19:47
一, 介绍
HotApp云笔记是基于HotApp小程序统计云后台提供的api接口开发的一个微信小程序
功能有: 离线保存笔记、云端数据同步、数据统计分析
项目地址: https://github.com/hotapp888/hotapp-notepad
截图如下:
二, 功能分析
需要数据库
对于一个功能非常简单的小程序来说, 自己去开发一套后台需要话费蛮多时间和精力的, 而且很多小程序开发者不会写后台.
hotapp 提供了key-value形式的免费接口, 支持get, post, delete和searchkey, 使用起来非常简单, 对于一个简单的笔记本来说完全够用了!需要云同步
在离线情况下可以写笔记, 这些笔记应该保存到本地缓存中. 在有网络的时候, 需要把本地的数据上传到hotapp 云后台, 在更换设备之后, 还可以从hotapp 云后台把数据同步到新设备. 在新设备上新增笔记、修改笔记和删除笔记之后, 也可以及时同步到旧设备上需要数据统计
需要知道每日新增用户, 活跃用户, 启动次数, 用户留存率, 增删改笔记的次数等. hotapp 提供了这些免费的接口, 一行代码就接入进去后就可以知道这些数据
三, 技术实现细节
1, key-value的设计
hotapp云笔记有两种数据类型: 笔记和反馈
对于笔记
, 我设置的key是item_openid_rand, 对于反馈, 我设置的key是feedback_openid_rand
这样设计的好处是: 看到item就知道是笔记, 看到feedback就知道是反馈, 如果需要知道是哪个用户的笔记, 就查找item_openid_开头的key就可以了
hotapp云笔记 提供了这些接口, 让我实现起来非常地方便.
获取用户所有的笔记
/*** 获取所有数据*/getItems: function(cb) { var that = this; // 首先从缓存中获取, 如果缓存中有就直接返回 var items = wx.getStorageSync('items'); if (items) { return typeof cb == 'function' && cb(items); } else { // 如果缓存中没有, 就通过hotapp.searchkey去云后台获取 var filters = { prefix: hotapp.getPrefix('item'), pageSize: -1 // -1表示不需要分页, 一次性获取所有的数据 }; hotapp.searchkey(filters, function(res) { if (res.ret == 0) { res.data.items.forEach(function(item) { // 把获取的数据格式稍作处理, 以便在前端显示 item = that.formatItem(item); // state: 1表示未同步, 2表示已同步, 3表示本地已删除. // 数据同步的内容在下面介绍 item.state = 2; }); // 为了提高速度, 把数据都存入缓存, 下次取的时候就直接从缓存中获取而不需要从云后台获取 wx.setStorageSync('items', res.data.items); return typeof cb == 'function' && cb(res.data.items); } else { return typeof cb == 'function' && cb([]); } }); }},
修改/新增笔记
/*** 新增数据*/store: function(item, cb) { var that = this; // 先调用获取所有的笔记的接口 this.getItems(function(items) { // 每次新增笔记的时候, 首先更新version, 以便同步. 关于同步, 下面再介绍 that.updateVersion(function(success) { if (success) { // 版本号更新好了之后, 就调用hotapp.post接口把笔记上传到云后台 hotapp.post(item.key, item.value, function(res) { if (res.ret == 0) { item = res.data; item.state = 2; // 如果上传成功了就把该笔记的状态设置为"已同步" } else { item.state = 1; // 如果上传失败了, 就把该笔记的状态设置为"未同步" } // 把新增的笔记的数据格式稍作处理, 以便在前端显示 item = that.formatItem(item); // 遍历所有的笔记, 判断这条笔记是新增加的还是经过修改过的 var isNew = true; items.forEach(function(oldItem, index, arr) { if (oldItem.key == item.key) { arr[index] = item; isNew = false; } }); if (isNew) { // 向hotapp统计发送新增事件,可知道用户每天新增次数 hotapp.onEvent('new'); // 新增的笔记就push到原来的笔记中, 并且按照创建时间排序 items.push(item); items.sort(function(a, b) { return a.create_time < b.create_time; }); } else { // 向hotapp统计发送保存事件,可知道用户每天保存次数 hotapp.onEvent('store'); } // 最后, 不论是新增的还是修改的笔记, 全部存入缓存中 wx.setStorageSync('items', items); return typeof cb == 'function' && cb(true); }); } }); });},
删除笔记
/*** 删除数据*/destroy: function(item, cb) { // 向hotapp统计发送删除事件,后台可知晓用户删除了哪些标题 hotapp.onEvent('delete', item.value.title); var that = this; // 先获取所有笔记 this.getItems(function(items) { // 每次删除, 都需要更新版本号, 以便数据同步 that.updateVersion(function(success) { if (success) { // 版本号更新好了之后, 就调用delete接口删除云后台的数据 hotapp.del(item.key, function(res) { // 暂时可以先不删除本地数据, 直接修改state的状态为"已删除", // 然后存入数据库, 等到数据同步的时候再统一删除本地数据 items.forEach(function(oldItem, index, arr) { if (oldItem.key == item.key) { oldItem.state = 3; wx.setStorageSync('items', arr); return typeof cb == 'function' && cb(true); } }); }); } }); });},
2, 数据同步的设计
这个项目里面最复杂的就是数据同步的问题了.
在开发的时候, 遇到了下面几个问题:
每次同步的时候都要先请求云后台所有的数据, 然后和本地缓存数据来对比, 这样不仅会给云后台服务器增加压力, 也会浪费用户的很多流量
在和服务器的数据进行对比的时候, 不知道哪些数据是同步过的, 哪些数据是未同步的, 哪些数据是删除过的
用户第一次打开hotapp云笔记时是离线状态, 这时调用hotapp.login接口之后是获取不到用户的openid的, 用户新建的笔记数据中的key是和fakeOpenID相关的, 当用户打开网络的时候, hotapp.login获取到openid之后, 需要把本地的fakeOpenID全部替换成openid, 再把数据上传到服务器
解决方案:
在hotapp云后台添加一个key, 叫做version, 本地的version应该和服务器的version同步.
本地每次做增删改操作的时候, 都要先更新本地缓冲中的version, 再更新服务器上的version, 确保本地和服务器上的version一致
每次同步之前, 先检查本地的version和云后台的version是否一致, 如果一致, 就不需要把服务器上所有的数据
如果同时有两台设备, 这两台设备的缓存中的version是一致的, 那么设备A做增删改操作时, 设备A缓存中的version和服务器上的version都会更新, 这时服务器上的version和设备B缓存中的version是不一致的, 所以设备B同步的时候就需要把服务器上所有的数据拉下来同步.给本地数据的每一条记录都添加一个字段: state, state为1表示未同步, 2表示已同步, 3表示已删除
在和服务器的数据进行对比的时候, 分7种情况考虑:- 对于一条数据, 本地缓存中有记录且state为1, 服务器上也有记录
- 对于一条数据, 本地缓存中有记录且state为2, 服务器上也有记录
- 对于一条数据, 本地缓存中有记录且state为3, 服务器上也有记录
- 对于一条数据, 本地缓存中有记录且state为1, 服务器上没有记录
- 对于一条数据, 本地缓存中有记录且state为2, 服务器上没有记录
- 对于一条数据, 本地缓存中有记录且state为3, 服务器上没有记录
- 对于一条数据, 本地缓存中没有记录, 服务器上有记录
仔细分析后, 可以归纳成下面的逻辑:
- 用服务器上的数据把本地的数据覆盖. 遍历的时候以服务器的数据为准, 来对比本地数据, 如果发现某一条笔记在本地没有, 就直接新增, 如果有, state为1就就改为2, state为2的不变, state为3的就从缓存中删除
- 遍历本地缓存, 如果state为1, 就上传到服务器并设置state为2, 如果state为3, 就调用delete接口删除服务器的数据和本地的数据
下面是具体的实现, 其中函数syncLocalDataToServer()和函数syncServerDatatoLocal()分别实现了上述的逻辑1和逻辑2:
/** * 下拉刷新事件, 数据同步 */ onPullDownRefresh: function() { wx.showToast({ title: '正在同步数据', icon: 'loading' }); // 临时变量 var tempData = this.data.items; var that = this; // 先检查版本, 如果和服务器版本不同, 则需要从服务器拉取数据 app.checkVersion(function(shouldPullData) { // 如果返回true, 则表示本地的version和服务器的version不一致 // 需要把服务器上所有数据都拉下来对比 if (shouldPullData) { var filters = { prefix: app.globalData.hotapp.getPrefix('item') }; // 从服务器拉取所有数据 app.globalData.hotapp.searchkey(filters, function(res) { if (res.ret == 0) { // 拉取成功, 更新版本号 app.updateVersion(function(success) { if (success) { // 更新版本号之后把本地数据和服务器数据合并去重(逻辑1) tempData = that.syncServerDatatoLocal(tempData, res.data.items); tempData.forEach(function(item, index, arr) { arr[index] = app.formatItem(item); arr[index].state = 2; }); // 更新视图数据 that.setData({ items: tempData }); // 把合并好的数据存缓存 wx.setStorageSync('items', tempData); // 把本地数据上传到服务器/从服务器删除数据(逻辑2) that.syncLocalDataToServer(tempData); } }); } }); } else { // 版本号和服务器相同, 则不需要从服务器上拉取数据, 直接同步数据到服务器 that.syncLocalDataToServer(tempData); } }); }, /** * 将本地数据同步到服务器 */ syncLocalDataToServer: function(data) { var that = this; // 遍历所有的数据 data.forEach(function(item, index, items) { app.globalData.hotapp.replaceOpenIdKey(item.key, function(newKey) { if (newKey) { item.key = newKey; // 如果还有数据没有同步过, 则调用post接口同步到服务器 if (item.state == 1) { app.globalData.hotapp.post(item.key, item.value, function(res) { if (res.ret == 0) { // 同步成功后更新状态, 并存缓存 item.state = 2; item = app.formatItem(item); that.setData({ items: items }); wx.setStorageSync('items', items); } }); } // 如果数据被删除过, 则调用delete接口从服务器删除数据 if (item.state == 3) { app.globalData.hotapp.del(item.key, function(res) { if (res.ret == 0 || res.ret == 103) { // 服务器的数据删除成功后, 删除本地数据并更新缓存 items.splice(index, 1); that.setData({ items: items }); wx.setStorageSync('items', items); } }); } } else { return; } }) }); }, /** * 将服务器的数据同步到本地 */ syncServerDatatoLocal: function(localData, serverData) { var that = this; // 通过hash的性质去重, 服务器数据覆盖本地数据 // 但是要保留本地中状态为已删除的数据 // 删除的逻辑不在这里处理 var localHash = new Array(); localData.forEach(function(item) { localHash[item.key] = item; }); var serverHash = new Array(); serverData.forEach(function(item) { serverHash[item.key] = item; }); // 先把服务器上有的数据但是本地没有的数据合并 serverData.forEach(function(item) { var t = localHash[item.key]; // 有新增的数据 if (!t) { localHash[item.key] = item; } // 有相同的key则以服务器端为准 if (t && t.state != 3) { item.state = 2; item = app.formatItem(item); localHash[item.key] = item; } }); // 然后再删除本地同步过的但是服务器上没有的缓存数据(在其它设备上删除过了) localData.forEach(function(item, index, arr) { var t = serverHash[item.key]; if (!t && item.state == 2) { console.log(item); delete localHash[item.key]; } }); // 将hash中的数据转换回数组 var result = new Array(); for (var prob in localHash) { result.push(localHash[prob]); } // 按时间排序 result.sort(function(a, b) { return a.create_time < b.create_time; }); console.log(result); return result; }
- HotApp云笔记
- hotapp使用
- HotApp小程序统计云后台 免费的Https云后台服务器,方便学习小程序
- 小程序用户反馈 - HotApp小程序统计仿微信聊天用户反馈组件,开源
- 针对小程序参数二维码,HotApp小程序统计已提供二维码精准统计服务
- 小程序用户反馈-HotApp小程序统计仿微信聊天用户反馈组件...
- 小程序用户反馈-HotApp小程序统计仿微信聊天用户反馈组件
- 小程序用户反馈 - HotApp小程序统计仿微信聊天用户反馈组件,开源
- HotApp小程序统计开发总结之设置了navigationBarBackgroundColor颜色,android 可以显示,ios不能显示
- HotApp小程序统计开发总结之设置了navigationBarBackgroundColor颜色,android 可以显示,ios不能显示
- 云笔记收藏笔记
- 云笔记
- 云笔记
- 云笔记
- 网易云笔记 linux笔记 2
- 有道云笔记and印象笔记
- 有道云笔记--500篇笔记纪念
- 有道云笔记Markdown笔记添加图片
- 01背包
- 经纬度转换
- 简述Linux下的进程通信方式及其区别
- 报表组查询后显示当前tab页
- DropEditText可拉伸EditText
- HotApp云笔记
- Javascript:谈谈JS的全局变量跟局部变量
- 02-gradle的安装
- 用tagcloud插件制作的超炫酷“云标签”(3D旋转和平铺),附前后台代码及标签配置页面
- nginx配置指南
- Java关键字final、static使用总结
- a~z
- CodeForces 375C
- picasso,glide两种图片框架