diff --git a/src/sync.js b/src/sync.js index aaeb4279..f0fe7801 100644 --- a/src/sync.js +++ b/src/sync.js @@ -11,7 +11,7 @@ var Note = require('./note'); var Web = require('./web'); function log(o) { - // console.log(o); + // console.log(o); } // 同步服务 @@ -19,1212 +19,1204 @@ function log(o) { syncProgress 设置 两阶段, 各个50% 1. <- - notebook 10 -> 10% - note 30 -> 40% - tag 10 -> 50% + notebook 10 -> 10% + note 30 -> 40% + tag 10 -> 50% 2. -> - notebook 10 -> 60% - note 30 -> 90% - tag 10 -> 100% + notebook 10 -> 60% + note 30 -> 90% + tag 10 -> 100% */ var Sync = { - // 同步的信息, 返回给调用者 - _syncInfo: { - notebook: {changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], changeUpdates: []}, - note: {changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], conflicts: [], errors: []}, - tag: {} - }, - /* - _sendInfo: { - notebook: {adds: [], deletes: [], updates: []}, - note: {adds: [], deletes: [], updates: [], conflicts: []}, - tag: {} - }, - */ - _needIncrSyncAgain: false, - - // notebook - _syncNotebookIsLastChunk: false, - _totalSyncNotebookNum: 0, // 需要同步的数量 - _tocalHasSyncNotebookNum: 0, // 已同步的数量 - _notebookMaxEntry: 200, - - // note - _syncNoteIsLastChunk: false, - _totalSyncNoteNum: 0, // 需要同步的数量 - _noteMaxEntry: 200, - - // tag - _syncTagIsLastChunk: false, - _totalSyncTagNum: 0, // 需要同步的数量 - _tagMaxEntry: 200, - - _initSyncInfo: function() { - var me = this; - - // notebook - me._syncNotebookIsLastChunk = false; - me._totalSyncNotebookNum = 0; - me._totalHasSyncNotebookNum = 0; - me._lockNotebook = 1; - - // note - me._syncNoteIsLastChunk = false; - me._totalSyncNoteNum = 0; - me._totalHasSyncNoteNum = 0; - me._lockNote = 1; - - // tag - me._syncTagIsLastChunk = false; - me._totalSyncTagNum = 0; - me._totalHasSyncTagNum = 0; - me._lockTag = 1; - - // 同步信息 - me._syncInfo = { - notebook: {ok: false, changeAdds: [], changeConflicts: [], changeNeedAdds: [], adds: [], deletes: [], updates: [], changeUpdates: []}, - note: {ok: false, adds: [], changeAdds: [], changeConflicts: [], changeUpdates:[], changeNeedAdds: [], deletes: [], updates: [], conflicts: [], errors: []}, - tag: {ok: false, adds: [], changeAdds: [], changeConflicts: [], changeNeedAdds: [], deletes: [], updates: [], conflicts: []}, - }; - - // 发送改变信息 - /* - me._sendInfo = { - notebook: {ok: false, adds: [], deletes: [], updates: []}, - note: {ok: false, adds: [], deletes: [], updates: [], conflicts: []}, - tag: {ok: false} - }; - */ - // 是否还要来一次增量同步 ? - me._needIncrSyncAgain = false; - }, - - // 停止同步 - // 第一次,note.html -> login.html(使得_stop: true), -> note.html fullSync,一看,是stop - _stop: false, - stop: function() { - var me = this; - me._stop = true; - }, - isStop: function() { - var me = this; - if(me._stop) { - me._stop = false; - return true; - } - return false; - }, - - //--------------- - // notebook - //--------------- - - // 增加, 有锁 - _lockNotebook: 1, - _addSyncNotebookNum: function() { - var me = this; - if(me._lockNotebook) { - me._lockNotebook = 0; - me._totalHasSyncNotebookNum++; - me._lockNotebook = 1; - } else { - me._addSyncNotebookNum(); - } - }, - - // 同步笔记本 - _syncNotebookToLocal: function(notebooks, callback) { - var me = this; - - function canCall() { - // 是最后一块, 且 - me._addSyncNotebookNum(); - // console.log(me._syncNotebookIsLastChunk); - // console.log(me._totalHasSyncNotebookNum + ' ' + me._totalSyncNotebookNum); - // console.log(me._syncInfo.notebook.ok); - if(me._syncNotebookIsLastChunk && - me._totalHasSyncNotebookNum >= me._totalSyncNotebookNum) { - // 防止多次callback - if(!me._syncInfo.notebook.ok) { - me._syncInfo.notebook.ok = true; - callback && callback(true); - } - } - } - - if(!notebooks || notebooks.length == 0) { - return canCall(); - } - - for(var i in notebooks) { - var notebook = notebooks[i]; - // 得到本地的, 与之对比 - - Web.syncProcess('notebook', notebook.Title); - - (function(notebook) { - - var usn = notebook.Usn; - var notebookId = notebook.NotebookId; // 服务器端的 - - // 1) 服务器端删除了, 本地肯定删除 - if(notebook.IsDeleted) { - // console.log('delete: '); - // console.log(notebook); - Notebook.getNotebookIdByServerNotebookId(notebookId, function(localNotebookId) { - Notebook.deleteNotebookForce(notebookId, function() { - // me._syncInfo.notebook.deletes.push(localNotebookId); - me.fixSynced('notebook', 'deletes', localNotebookId); - canCall(); - }) - }); - return; - } - // 2) 查看本地的, 与本地合并 - Notebook.getNotebookByServerNotebookId(notebookId, function(notebookLocal) { - // 2.1 本地没有, 表示是新建 - if(!notebookLocal) { - console.log(' add addNotebookForce...', notebook.Title) - // TODO - Notebook.addNotebookForce(notebook, function(notebook) { - // 要最后一起添加, 因为有层级关系 - console.log(' ', notebook.Title) - me._syncInfo.notebook.adds.push(notebook); - // me.fixSynced('notebook', 'adds', notebook); - canCall(); - }); - } else { - // 如果Usn一样, 表示服务器端并没有修改 - if(notebookLocal.Usn === notebook.Usn) { - console.log('notebookLocal 如果Usn一样, 表示服务器端并没有修改'); - return canCall(); - } - - // 2.2 本地是否修改了, 需要合并, 使用服务端的数据 - if(notebook.IsDirty) { - console.log('冲突....') - // 2.3 服务器是最新的, 用服务器的 - } else { - } - // 这里都是用服务器端的数据, 不处理冲突 - Notebook.updateNotebookForce(notebook, notebookLocal, function(notebook) { - if(notebook) { - // 前端一起渲染 - me._syncInfo.notebook.updates.push(notebook); - // me.fixSynced('notebook', 'updates', notebook); - } - canCall(); - }) - } - }); - })(notebook); - } - }, - - syncNotebook: function(afterUsn, callback) { - var me = this; - if(me.isStop()) { - return; - } - Api.getSyncNotebooks(afterUsn, me._notebookMaxEntry, function(notebooks) { - console.log(' syncNotebook', notebooks) - // console.log(notebooks); - if(Common.isOk(notebooks)) { - me._totalSyncNotebookNum += notebooks.length; - // 证明可能还有要同步的 - if(notebooks.length == me._notebookMaxEntry) { - me._syncNotebookToLocal(notebooks, callback); - var last = notebooks[notebooks.length-1]; - me.syncNotebook(last.Usn, callback); - - // 更新Usn - me.updateSyncUsn('NotebookUsn', last.Usn); - - } else { - console.log(' no more notebooks'); - me._syncNotebookIsLastChunk = true; - me._syncNotebookToLocal(notebooks, callback); - - if (notebooks.length) { - var last = notebooks[notebooks.length-1]; - me.updateSyncUsn('NotebookUsn', last.Usn); - } - } - } else { - // 同步失败 - me._syncInfo.notebook.ok = false; - me._syncInfo.notebook.msg = "cann't get all chunks"; - callback && callback(false); - } - }); - }, - - //------------- - // note - //------------- - - // 增加, 有锁 - _lockNote: 1, - _addSyncNoteNum: function() { - var me = this; - if(me._lockNote) { - me._lockNote = 0; - me._totalHasSyncNoteNum++; - me._lockNote = 1; - } else { - me._addSyncNoteNum(); - } - }, - - // 同步笔记到本地 - _syncNoteToLocal: function(notes, callback) { - var me = this; - - function canCall(isEmpty) { - // 为空时来判断是最后一次了, 可以之前的还没处理完 - if(isEmpty && me._totalHasSyncNoteNum < me._totalSyncNoteNum) { - return; - } - - // 是最后一块, 且 - me._addSyncNoteNum(); - // log('notes: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk); - if(me._syncNoteIsLastChunk && me._totalHasSyncNoteNum >= me._totalSyncNoteNum) { - // 防止多次callback - if(!me._syncInfo.note.ok) { - log('note->next'); - me._syncInfo.note.ok = true; - callback && callback(true); - } - } - } - - // 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了 - if(!notes || notes.length == 0) { - return canCall(true); - } - - for(var i in notes) { - var note = notes[i]; - // 得到本地的, 与之对比 - Web.syncProcess('note', note.Title); - - (function(note) { - - var usn = note.Usn; - var noteId = note.NoteId; - - // 1) 服务器端删除了, 本地肯定删除 - if(note.IsDeleted) { - console.log(' delete: ', note); - Note.getNoteIdByServerNoteId(noteId, function(localNoteId) { - Note.deleteNoteForce(noteId, function() { - // me._syncInfo.note.deletes.push(localNoteId); - me.fixSynced('note', 'deletes', localNoteId); - canCall(); - }); - }); - return; - } - // 2) 查看本地的, 与本地合并 - Note.getNoteByServerNoteId(noteId, function(noteLocal) { - // 2.1 本地没有, 表示是新建 - if(!noteLocal) { - console.log(' add note', note); - Note.addNoteForce(note, function(note) { - // me._syncInfo.note.adds.push(note); - me.fixSynced('note', 'adds', note); - return canCall(); - }); - } else { - // 如果Usn一样, 表示服务器端并没有修改 - if(noteLocal.Usn === note.Usn) { - console.log(' note 如果Usn一样, 表示服务器端并没有修改'); - return canCall(); - } - - // 2.2 本地是否修改了, 冲突, 报告给前端, 前端处理 - // 冲突, 将本地修改的笔记复制一份(设置冲突字段, ConflictNoteId), 远程的覆盖本地的 - // 新方法: 冲突后, 得到最新内容, 看是否与本地内容一致, 如果一致, 则不冲突, 其它数据用服务器上的 - if(noteLocal.IsDirty) { - console.log(' note 冲突 serverNoteId: ' + noteId, note.Title); - // console.log(noteLocal.NoteId); - // console.log(noteLocal.IsDirty); - // console.log(noteLocal); - // note.ServerNoteId = note.NoteId; - // note.NoteId = noteLocal.NoteId; - - Note.getNoteContentFromServer(noteId, function (content) { - // 表示没有获取到content, 则只能标志为冲突了 - // 内容不一样, 也标为冲突 - if (content === false || content != noteLocal.Content) { - me._syncInfo.note.conflicts.push({server: note, local: noteLocal}); - // me.fixSynced('note', 'conflicts', {server: note, local: noteLocal}); - } - // 否则, 内容一样, 标为不冲突, 需要更新 - else { - - // 2.3 服务器是最新的, 用服务器的 - // 服务器是最新的, 本地没动过, 则覆盖之 - Note.updateNoteForce(note, function(note) { - if(note) { - // me._syncInfo.note.updates.push(note); - me.fixSynced('note', 'updates', note); - } - canCall(); - }, false); - } - }); - - return canCall(); - // 2.3 服务器是最新的, 用服务器的 - } else { - // 服务器是最新的, 本地没动过, 则覆盖之 - Note.updateNoteForce(note, function(note) { - if(note) { - // me._syncInfo.note.updates.push(note); - me.fixSynced('note', 'updates', note); - } - canCall(); - }); - } - } - }); - })(note); - } - }, - - syncNote: function(afterUsn, callback) { - var me = this; - if(me.isStop()) { - return; - } - - console.log(' pull notes from server...'); - Api.getSyncNotes(afterUsn, me._noteMaxEntry, function(notes) { - // console.log('syncNote---'); - console.log(' notes:', notes); - if(Common.isOk(notes)) { - me._totalSyncNoteNum += notes.length; - // 证明可能还有要同步的 - if(notes.length == me._noteMaxEntry) { - me._syncNoteToLocal(notes, callback); - var last = notes[notes.length-1]; - - // 500ms延迟 - setTimeout(function () { - me.syncNote(last.Usn, callback); - }, 500); - - // 更新Usn - me.updateSyncUsn('NoteUsn', last.Usn); - - } else { - console.log(' no more notes'); - me._syncNoteIsLastChunk = true; - me._syncNoteToLocal(notes, callback); - - if (notes.length) { - var last = notes[notes.length-1]; - me.updateSyncUsn('NoteUsn', last.Usn); - } - } - } else { - // 同步失败 - me._syncInfo.note.ok = false; - me._syncInfo.note.msg = "cann't get all chunks"; - console.error(' pull notes error', notes) - callback && callback(false); - } - }); - }, - - - //---------------- - // 同步标签 - // --------------- - // 增加, 有锁 - _lockTag: 1, - _addSyncTagNum: function() { - var me = this; - if(me._lockTag) { - me._lockTag = 0; - me._totalHasSyncTagNum++; - me._lockTag = 1; - } else { - me._addSyncTagNum(); - } - }, - - // 同步标签到本地 - _syncTagToLocal: function(tags, callback) { - var me = this; - function canCall(isEmpty) { - // 为空时来判断是最后一次了, 可以之前的还没处理完 - if(isEmpty && me._totalHasSyncTagNum < me._totalSyncTagNum) { - return; - } - - // 是最后一块, 且 - me._addSyncTagNum(); - // log('tags: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk); - if(me._syncTagIsLastChunk && me._totalHasSyncTagNum >= me._totalSyncTagNum) { - // 防止多次callback - if(!me._syncInfo.tag.ok) { - log('tag->next'); - me._syncInfo.tag.ok = true; - callback && callback(true); - } - } - } - - // 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了 - if(!tags || tags.length == 0) { - return canCall(true); - } - - for(var i in tags) { - var tag = tags[i]; - // 得到本地的, 与之对比 - Web.syncProcess('tag', tag.Tag); - - (function(tag) { - - var usn = tag.Usn; - var tagId = tag.TagId; - - // 1) 服务器端删除了, 本地肯定删除 - if(tag.IsDeleted) { - console.log(' delete tag: ', tag); - Tag.deleteTag(tag.Tag, function() { - // me._syncInfo.tag.deletes.push(tag.Tag); - me.fixSynced('tag', 'deletes', tag.Tag); - canCall(); - }, true, me.fullSyncStart); - return; - } - // 2) 查看本地的, 与本地合并 - Tag.getTag(tag.Tag, function(tagLocal) { - // 2.1 本地没有, 表示是新建 - if(!tagLocal) { - console.log(' add tag: ...') - Tag.addOrUpdateTag(tag.Tag, function(tagAdded) { - // me._syncInfo.tag.adds.push(tagAdded); - me.fixSynced('tag', 'adds', tagAdded); - return canCall(); - }, true, usn); - } else { - // 本地有, 不用操作 - Tag.setNotDirtyAndUsn(tag.Tag, tag.Usn); - return canCall(); - } - }); - })(tag); - } - }, - syncTag: function(afterUsn, callback) { - var me = this; - if(me.isStop()) { - return; - } - Api.getSyncTags(afterUsn, me._tagMaxEntry, function(tags) { - // console.log(afterUsn + ' syncTags------------------------------------') - // console.log(tags); - if(Common.isOk(tags)) { - me._totalSyncTagNum += tags.length; - // 证明可能还有要同步的 - if(tags.length == me._tagMaxEntry) { - me._syncTagToLocal(tags, callback); - var last = tags[tags.length-1]; - me.syncTag(last.Usn, callback); - - // 更新Usn - me.updateSyncUsn('TagUsn', last.Usn); - } else { - log('no more'); - me._syncTagIsLastChunk = true; - me._syncTagToLocal(tags, callback); - - if (tags.length) { - var last = tags[tags.length-1]; - me.updateSyncUsn('TagUsn', last.Usn); - } - } - } else { - // 同步失败 - me._syncInfo.tag.ok = false; - me._syncInfo.tag.msg = "cann't get all chunks"; - console.error(' sync tags error...') - // console.log(tags); - callback && callback(false); - } - }); - }, - - //---------- - - // 记录LastSyncUsn, LastUpdateTime 同步时间 - updateLastSyncState: function(callback) { - var me = this; - User.updateLastSyncState(function() { - callback(); - }); - }, - - // 为了避免全都重新来过, 这里保存每次 - updateSyncUsn: function (type, usn) { - // console.error('---') - // console.log(type + ' = ' + usn); - User.updateEachSyncState(type, usn, function() {}); - }, - - // 全量同步 - // callback(error, info) - fullSync: function(callback) { - var me = this; - me._stop = false; - me._initSyncInfo(); - me.fullSyncStart = true; - - User.getAllLastSyncState(function(lastUsn, notebookUsn, noteUsn, tagUsn) { - // 不可能会有lastUsn吧 - if (lastUsn && lastUsn > 0) { - notebookUsn = -1; - noteUsn = -1; - tagUsn = -1; - } - - if (!notebookUsn) { - notebookUsn = -1; - } - if (!noteUsn) { - noteUsn = -1; - } - if (!tagUsn) { - tagUsn = -1; - } - - console.log('fullSync ' + notebookUsn + ' ' + noteUsn + ' ' + tagUsn); - - Api.getLastSyncState(function(serverState) { - if(!Common.isOk(serverState)) { - console.error(' get Server LastSyncState error!!'); - callback && callback(serverState, null); - return; - } - - // Web.syncNotebookFinish(); - // 同步笔记本 - me.syncNotebook(notebookUsn, function(ok) { - if(ok) { - // Web.syncNoteFinish(); - // console.log('------------------') - // 同步笔记 - me.syncNote(noteUsn, function(ok) { - if(ok) { - // Web.syncTagFinish(); - // 同步标签 - me.syncTag(tagUsn, function(ok) { - if (ok) { - me.fullSyncStart = false; - // 更新上次同步时间 - me.updateLastSyncState(function() { - // send changes - // me.sendChanges(); - callback && callback(false, me._syncInfo); - }); - } - else { - me.fullSyncStart = false; - console.error('syncTag error....'); - callback && callback({}, me._syncInfo); - } - }); - } else { - me.fullSyncStart = false; - console.error('syncNote error.... 跳过tag'); - callback && callback({}, me._syncInfo); - } - }); - } else { - me.fullSyncStart = false; - console.error('syncNotebook error.... 跳过note,tag'); - callback && callback({}, me._syncInfo); - } - }); - - }); - - }); - }, - - // 处理同步好的之后的 - // mainType == notebook, note, tag - // type = changeAdds, changeConflicts, adds, deletes, updates, conflicts, errors, - fixSynced: function (mainType, type, data) { - if (this.fullSyncStart) { - return; - } - var o = {}; - if (!Common.isArray(data)) { - data = [data]; - } - o[type] = data; - if (mainType == 'notebook') { - Notebook.fixConflicts(o); - } else if (mainType == 'note') { - Note.fixConflicts(o); - } else { - Web.addOrDeleteTagFromSync(o); - } - }, - - // 前端重新渲染 - fixSyncedNotebook: function () { - var me = this; - if(me.incrSyncStart) { - console.log(' fixSyncedNotebook') - Notebook.fixConflicts(me._syncInfo.notebook); - } - }, - - // 处理冲突 - fixConflicts: function(callback) { - var me = this; - var afterInfo = me._syncInfo; - // log('处理冲突....'); - // log(me._syncInfo); - // log(me._syncInfo.tag); - var tag = me._syncInfo.tag; - - // 如果是incSync, 则要前端处理 - // 不是fullSync - if(me.incrSyncStart) { - - // Notebook.fixConflicts(me._syncInfo.notebook); - - Note.fixConflicts(me._syncInfo.note, function() { - // 避免无限循环, 别send changes了 - if(!me._needIncrSyncAgain) { - // alert("?") - console.log(" not needIncrSyncAgain") - // send changes - callback && callback(); - } else { - - } - }); - - // 添加或删除一些tag - // console.log('怎么可能?') - // console.error(me._syncInfo.tag); // 为空, 不知道原因 - // console.error(); - // console.error(tag); - Web.addOrDeleteTagFromSync(tag); - } - }, - - fixConflictsForSendChanges: function(callback) { - var me = this; - me.fixConflicts(function() { - callback(); - // 已结束 - me.setSyncFinished(); - }) - }, - // 同步状态 - syncProcessStatus: 0, - // 添加 - addSyncProcessStatus: function(n) { - var me = this; - me.syncProcessStatus += n; - if(me.syncProcessStatus >= 100) { - me.syncProcessStatus = 98; - } - Web.syncProgress(me.syncProcessStatus); - }, - // 增量同步 - incrSyncStart: false, - // 如果第一次insync, 网络错误导致incrSyncStart不结束, 第二次就会永远转动 - setSyncFinished: function(hasError) { - var me = this; - // unconnect也会调, 所以, 一旦所有都是unconnect, 那就时不时会有进度条到100 - if (!me.incrSyncStart || !me.incrSyncStart) { - return; - } - me.incrSyncStart = false; - me.fullSyncStart = false; - // Web.syncProgress(0); - Web.syncFinished(hasError); - }, - incrSync: function(again) { - if (User.isLocal()) { - console.log('no sync for local account'); - return; - } - var me = this; - me._stop = false; - me._initSyncInfo(); - - // again表示重来 - if(!again && me.incrSyncStart) { - console.log('上一sync未结束!!'); - return; - } - - me.incrSyncStart = true; - me.syncProcessStatus = 0; - - console.log('inc sync start'); - if(again) { - console.log('again >>'); - } - - // 得到当前LastSyncUsn - User.getLastSyncState(function(lastSyncUsn, lastSyncTime) { - console.log('%cstep1 getLastSyncState', 'color: #68bc7a') - - // 没有上次同步的时间, 则需要进行一次全量同步, 不可能会发生 - if(!lastSyncUsn) { - console.error(' getLastSyncState error!!'); - me.setSyncFinished(); - return; - } - - // 先从服务器上得到usn, 与本地的判断, 是否需要pull - Api.getLastSyncState(function(serverState) { - if(!Common.isOk(serverState)) { - console.error(' get Server LastSyncState error!!'); - me.setSyncFinished(true); - return; - } - console.log(' get Server LastSyncState ret', serverState.LastSyncUsn + ' ' + lastSyncUsn); - if(serverState.LastSyncUsn > lastSyncUsn) { - // 需要同步笔记本 - console.log('%cstep2 pull', 'color: #68bc7a') - // 同步笔记本 - me.syncNotebook(lastSyncUsn, function(ok) { - if(ok) { - me.fixSyncedNotebook(); - console.log(' incr notebook ok', lastSyncUsn); - - me.addSyncProcessStatus(10); - console.log(' incr note start'); - - // 同步笔记 - me.syncNote(lastSyncUsn, function(ok) { - if(ok) { - console.log(' incr note ok', lastSyncUsn); - me.addSyncProcessStatus(30); - // 同步标签 - me.syncTag(lastSyncUsn, function() { - console.log(' incr tag ok', lastSyncUsn); - me.addSyncProcessStatus(10); - // 更新上次同步时间 - me.updateLastSyncState(function() { - // send changes - me.sendChanges(again); - }); - }); - } else { - console.log(' incr note not ok') - me.fixConflicts(); - } - }); - } else { - me.fixConflicts(); - } - }); - - } else { - console.log('%cstep2 不需要pull, skip', 'color: #68bc7a') - me.addSyncProcessStatus(50); - me.sendChanges(again); - } - }); - }); - }, - - //--------- - // 发送改变 - //--------- - - // 发送笔记本的更改 - sendNotebookChanges: function(callback) { - var me = this; - console.log(' 3.1: sendNotebookChanges') - // 获取所有笔记本的更改 - Notebook.getDirtyNotebooks(function(notebooks) { - if (!Common.isEmpty(notebooks)) { - console.log(' dirty notebooks:', notebooks); - } else { - console.log(' no dirty notebooks'); - } - if(!notebooks) { - callback && callback(); - } else { - // 调api, 所有执行后再callback(); - // 一个一个同步执行, 因为要有 - async.eachSeries(notebooks, function(notebook, cb) { - var api = Api.updateNotebook; - - // 本地既是新的, 又是删除的, 删除本地的, 不要同步 - // 5/4 - if(notebook.LocalIsNew && notebook.LocalIsDelete) { - console.log(' 笔记本既是新的, 又是删除的, 不同步, 直接删除本地的'); - Notebook.deleteLocalNotebook(notebook.NotebookId); - return cb(); - } - - if(notebook.LocalIsNew) { - api = Api.addNotebook; - } else if(notebook.LocalIsDelete) { - api = Api.deleteNotebook; - } - - api.call(Api, notebook, function(newNotebook) { - // 更新失败 - if(!newNotebook) { - return cb(); - } - - // 删除操作 - if(notebook.LocalIsDelete) { - return cb(); - } - - // 更新成功, 是否有冲突? - // newNotebook是服务器上的笔记本 - // 没有更新成功 - if(!newNotebook.NotebookId) { - if(newNotebook.Msg == 'conflict') { - // 没用, 前端不会处理的, 按理不会出现这种情况, 因为先sync - // me._syncInfo.notebook.conflicts.push(newNotebook); - me.fixSynced('notebook', 'conflicts', newNotebook); - } else if(newNotebook.Msg == 'notExists') { - // 服务器端没有, 那么要作为添加 - // 可能服务器上已删除, 此时应该要作为添加而不是更新 - // me._syncInfo.notebook.changeNeedAdds.push(notebook); - me.fixSynced('notebook', 'changeNeedAdds', notebook); - } - - // me.checkNeedIncSyncAgain(newNotebook.Usn); - return cb(); - } - else { - // 更新 - // TODO 后端updateNotebook只要传Usn回来即可 - console.log(" 返回来的notebook " + newNotebook.Title) - - Notebook.updateNotebookForceForSendChange(notebook.NotebookId, newNotebook, function() { - if(notebook.LocalIsNew) { - // me._syncInfo.notebook.changeAdds.push(newNotebook); - me.fixSynced('notebook', 'changeAdds', newNotebook); - } else { - // me._syncInfo.notebook.changeUpdates.push(newNotebook); - me.fixSynced('notebook', 'changeUpdates', newNotebook); - } - - // 这里才cb(), 因为先添加父, 再添加子 - me.checkNeedIncSyncAgain(newNotebook.Usn); - cb(); - }); - } - - }); - }, function() { - callback && callback(); - }); - } - }); - }, - - checkNeedIncSyncAgain: function(usn) { - var me = this; - // 如果之前都很正常 - if(!me._needIncrSyncAgain) { - // 检查是否有问题 - if(User.getLastSyncUsn() + 1 == usn) { - // 更新到本地lastSyncUsn - User.updateLastSyncUsn(usn); - } else { - // newNote.Usn > User.getLastSyncUsn + 1, 表示服务器端在本次同步后, sendChanges之前有更新 - // 那么, 还需要来一次incrSync, 之后 - if(User.getLastSyncUsn() > usn) { // 添加标签时如果标签已经存在, 则返回的是旧的 - return; - } - console.error('---?? checkNeedIncSyncAgain ??------' + usn) - console.trace(); - me._needIncrSyncAgain = true; - } - } - }, - - // 发送笔记改变 - // 发送笔记本的更改 - sendNoteChanges: function(callback) { - var me = this; - console.log(' 3.2: sendNoteChanges'); - // 获取所有笔记本的更改 - Note.getDirtyNotes(function(notes) { - if (!Common.isEmpty(notes)) { - console.log(' dirty notes:', notes); - } else { - console.log(' no dirty notes'); - } - if(!notes) { - callback && callback(); - } else { - // 调api, 所有执行后再callback(); - // 一个一个同步执行, 因为要有 - async.eachSeries(notes, function(note, cb) { - - if (note.InitSync) { - console.log(' InitSync is Dirty', note); - return cb(); - } - - if (note.ConflictNoteId && !note.ConflictFixed) { - console.log(' 未解决的冲突不同步', note.Title); - return cb(); - } - - if(note.LocalIsNew) { - // 是新的, 且不是trash和删除的 - if(!note.IsTrash && !note.LocalIsDelete) { - // 添加, newNote的返回不会很多值(server端) - Api.addNote(note, function(err, newNote) { - if(err || !Common.isOk(newNote)) { - console.log(' 添加笔记失败!', err, newNote); - me._syncInfo.note.errors.push({err: err, ret: newNote, note: note}); - return cb(); - } - console.log(' 添加笔记成功!', note.Title); - - newNote.ServerNoteId = newNote.NoteId; - newNote.NoteId = note.NoteId; - - newNote.IsBlog = note.IsBlog; // 前端要用 - - // me._syncInfo.note.changeAdds.push(newNote); - me.fixSynced('note', 'changeAdds', newNote); - - Note.updateNoteForceForSendChange(newNote, true); - - // 这里 - me.checkNeedIncSyncAgain(newNote.Usn); - - cb(); - }); - } - // 5/4 - // 本地已经删除了, 则彻底删除之 - else if(note.LocalIsDelete) { - console.log(' 既是新的, 又是删除的, 则删除笔记的'); - Note.deleteLocalNote(note.NoteId); - return cb(); - } - // isTrash, 不同步, 不删除 - else { - console.log(' 既是新的, 又是trash的, 不要同步'); - return cb(); - } - } - else if(note.LocalIsDelete) { - // 删除, 不管它了 - // TODO - Api.deleteTrash(note, function(ret) { - if(Common.isOk(ret)) { - me.checkNeedIncSyncAgain(ret.Usn); - - // 本地删除了的, 服务端没有, 直接删除本地的 - } else if (typeof ret == 'object' && ret.Msg == 'notExists') { - console.log( '本地删除了的, 服务端没有, 直接删除本地的'); - Note.deleteLocalNote(note.NoteId); - } - return cb(); - }); - } - else { - // 更新 - Api.updateNote(note, function(err, ret) { - if(err || !Common.isOk(ret)) { - console.log(' update error:' + note.Title, ret); - if(typeof ret == 'object') { - if(ret.Msg == 'conflict') { - console.error(' updateNote 冲突'); - - // 这种情况有很少见, 原因是先pull, 肯定会pull过来 - // 处理方法和pull一样, 看内容是否一样 - // 如果一样, 则不标志为冲突, 修改本Usn为serverUsn, 等下次再send changes - Note.getNoteContentFromServer(note.ServerNoteId, function (content) { - if (content === false || content != note.LocalContent) { - me._syncInfo.note.changeConflicts.push(note); - cb(); - } - else { - // 不冲突, 修改之Usn - Api.getNote(note.ServerNoteId, function (serverNote) { - // 取不到, 当作冲突 - if (!serverNote) { - me._syncInfo.note.changeConflicts.push(note); - cb(); - } - else { - Note.updateNoteUsn(note.NoteId, serverNote.Usn); - cb(); - } - }); - } - }); - } - else if(ret.Msg == 'notExists') { - Note.setError(note.NoteId, err, ret); - me.fixSynced('note', 'errors', {err: err, ret: ret, note: note}); - // me._syncInfo.note.errors.push({err: err, ret: ret, note: note}); - // 可能服务器上已删除, 此时应该要作为添加而不是更新 - // me._syncInfo.note.changeNeedAdds.push(note); - cb(); - } - // 其它错误, 不加, notImage 导致不能终止 - else { - Note.setError(note.NoteId, err, ret); - console.error(' updateNote error', err, ret); - me.fixSynced('note', 'errors', {err: err, ret: ret, note: note}); - cb(); - } - } - // 更新失败了, 服务器返回异常 - else { - Note.setError(note.NoteId, err, ret); - console.error(' updateNote error', err); - me.fixSynced('note', 'errors', {err: err, ret: ret, note: note}); - cb(); - } - } - // 更新成功 - else { - console.log(' 更新成功: ', note.Title); - ret.ServerNoteId = ret.NoteId; - ret.NoteId = note.NoteId; - Note.updateNoteForceForSendChange(ret, false); - // me._syncInfo.note.changeUpdates.push(note); - me.fixSynced('note', 'changeUpdates', note); - - me.checkNeedIncSyncAgain(ret.Usn); - - return cb(); - } - }); - } - }, function() { - callback && callback(); - }); - } - }); - }, - // 发送标签改变 - sendTagChanges: function(callback) { - console.log(' 3.3: sendTagChanges'); - var me = this; - // 获取所有笔记本的更改 - Tag.getDirtyTags(function(tags) { - if (!Common.isEmpty(tags)) { - console.log(' dirty tags:', tags); - } else { - console.log(' no dirty tags'); - } - if(!tags) { - callback && callback(); - } else { - // 调api, 所有执行后再callback(); - // 一个一个同步执行, 因为要有 - async.eachSeries(tags, function(tag, cb) { - if(tag.IsDirty) { - if(!tag.LocalIsDelete) { - // 添加 - Api.addTag(tag.Tag, function(newTag) { - if(!Common.isOk(newTag)) { - return cb(); - } - // 更新, 添加usn - Tag.setNotDirtyAndUsn(tag.Tag, newTag.Usn); - - me._syncInfo.tag.changeAdds.push(newTag); // 之前是note.changeAdds - // Tag.updateTagForce(newTag); - me.checkNeedIncSyncAgain(newTag.Usn); - cb(); - }); - } else { - // 删除, 不管它了 - Api.deleteTag(tag, function(ret) { - if(Common.isOk(ret)) { - Tag.setNotDirty(tag.Tag); - me.checkNeedIncSyncAgain(ret.Usn); - // 本地删除了的, 服务端没有, 直接删除本地的 - } else if (typeof ret == 'object') { - if (ret.Msg == 'notExists') { - console.log( 'tag本地删除了的, 服务端没有, 直接删除本地的'); - Tag.deleteLocalTag(tag.Tag); - } else if(ret.Msg == 'conflict') { - Tag.setNotDirty(tag.Tag); - } - } - return cb(); - }); - } - } - }, function() { - callback && callback(); - }); - } - }); - }, - - // again, 再一次sync, 不要send changes - sendChanges: function(again) { - var me = this; - console.log('%cstep3 send changes', 'color: #68bc7a') - // 先处理冲突, 可以同时进行 - if(again) { - console.log(' send changes again....'); - me.fixConflictsForSendChanges(function(){}); - return; - } - me.fixConflicts(function() { - // send changes - console.log(' send changes'); - me._initSyncInfo(); // 重新初始化[] - async.series([ - function(cb) { - me.sendNotebookChanges(cb); - }, - function(cb) { - me.addSyncProcessStatus(10); - me.sendNoteChanges(cb); - }, - function(cb) { - me.addSyncProcessStatus(30); - me.sendTagChanges(cb); - } - ], function() { - me.addSyncProcessStatus(10); - - // 重新再来一次增量同步 - if(me._needIncrSyncAgain) { - console.error(' needIncrSyncAgain') - me.fixConflictsForSendChanges(function() { - me.incrSync(true); - }); - } else { - console.log(' send changes 后解决冲突'); - me.fixConflictsForSendChanges(function() { - }); - } - }); - }); - } + // 同步的信息, 返回给调用者 + _syncInfo: { + notebook: { changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], changeUpdates: [] }, + note: { changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], conflicts: [], errors: [] }, + tag: {} + }, + /* + _sendInfo: { + notebook: {adds: [], deletes: [], updates: []}, + note: {adds: [], deletes: [], updates: [], conflicts: []}, + tag: {} + }, + */ + _needIncrSyncAgain: false, + + // notebook + _syncNotebookIsLastChunk: false, + _totalSyncNotebookNum: 0, // 需要同步的数量 + _tocalHasSyncNotebookNum: 0, // 已同步的数量 + _notebookMaxEntry: 200, + + // note + _syncNoteIsLastChunk: false, + _totalSyncNoteNum: 0, // 需要同步的数量 + _noteMaxEntry: 200, + + // tag + _syncTagIsLastChunk: false, + _totalSyncTagNum: 0, // 需要同步的数量 + _tagMaxEntry: 200, + + _initSyncInfo: function() { + var me = this; + + // notebook + me._syncNotebookIsLastChunk = false; + me._totalSyncNotebookNum = 0; + me._totalHasSyncNotebookNum = 0; + me._lockNotebook = 1; + + // note + me._syncNoteIsLastChunk = false; + me._totalSyncNoteNum = 0; + me._totalHasSyncNoteNum = 0; + me._lockNote = 1; + + // tag + me._syncTagIsLastChunk = false; + me._totalSyncTagNum = 0; + me._totalHasSyncTagNum = 0; + me._lockTag = 1; + + // 同步信息 + me._syncInfo = { + notebook: { ok: false, changeAdds: [], changeConflicts: [], changeNeedAdds: [], adds: [], deletes: [], updates: [], changeUpdates: [] }, + note: { ok: false, adds: [], changeAdds: [], changeConflicts: [], changeUpdates: [], changeNeedAdds: [], deletes: [], updates: [], conflicts: [], errors: [] }, + tag: { ok: false, adds: [], changeAdds: [], changeConflicts: [], changeNeedAdds: [], deletes: [], updates: [], conflicts: [] }, + }; + + // 发送改变信息 + /* + me._sendInfo = { + notebook: {ok: false, adds: [], deletes: [], updates: []}, + note: {ok: false, adds: [], deletes: [], updates: [], conflicts: []}, + tag: {ok: false} + }; + */ + // 是否还要来一次增量同步 ? + me._needIncrSyncAgain = false; + }, + + // 停止同步 + // 第一次,note.html -> login.html(使得_stop: true), -> note.html fullSync,一看,是stop + _stop: false, + stop: function() { + var me = this; + me._stop = true; + }, + isStop: function() { + var me = this; + if (me._stop) { + me._stop = false; + return true; + } + return false; + }, + + //--------------- + // notebook + //--------------- + + // 增加, 有锁 + _lockNotebook: 1, + _addSyncNotebookNum: function() { + var me = this; + if (me._lockNotebook) { + me._lockNotebook = 0; + me._totalHasSyncNotebookNum++; + me._lockNotebook = 1; + } else { + me._addSyncNotebookNum(); + } + }, + + // 同步笔记本 + _syncNotebookToLocal: function(notebooks, callback) { + var me = this; + + function canCall() { + // 是最后一块, 且 + me._addSyncNotebookNum(); + // console.log(me._syncNotebookIsLastChunk); + // console.log(me._totalHasSyncNotebookNum + ' ' + me._totalSyncNotebookNum); + // console.log(me._syncInfo.notebook.ok); + if (me._syncNotebookIsLastChunk && + me._totalHasSyncNotebookNum >= me._totalSyncNotebookNum) { + // 防止多次callback + if (!me._syncInfo.notebook.ok) { + me._syncInfo.notebook.ok = true; + callback && callback(true); + } + } + } + + if (!notebooks || notebooks.length == 0) { + return canCall(); + } + + for (var i in notebooks) { + var notebook = notebooks[i]; + // 得到本地的, 与之对比 + + Web.syncProcess('notebook', notebook.Title); + + (function(notebook) { + + var usn = notebook.Usn; + var notebookId = notebook.NotebookId; // 服务器端的 + + // 1) 服务器端删除了, 本地肯定删除 + if (notebook.IsDeleted) { + // console.log('delete: '); + // console.log(notebook); + Notebook.getNotebookIdByServerNotebookId(notebookId, function(localNotebookId) { + Notebook.deleteNotebookForce(notebookId, function() { + // me._syncInfo.notebook.deletes.push(localNotebookId); + me.fixSynced('notebook', 'deletes', localNotebookId); + canCall(); + }) + }); + return; + } + // 2) 查看本地的, 与本地合并 + Notebook.getNotebookByServerNotebookId(notebookId, function(notebookLocal) { + // 2.1 本地没有, 表示是新建 + if (!notebookLocal) { + console.log(' add addNotebookForce...', notebook.Title) + // TODO + Notebook.addNotebookForce(notebook, function(notebook) { + // 要最后一起添加, 因为有层级关系 + console.log(' ', notebook.Title) + me._syncInfo.notebook.adds.push(notebook); + // me.fixSynced('notebook', 'adds', notebook); + canCall(); + }); + } else { + // 如果Usn一样, 表示服务器端并没有修改 + if (notebookLocal.Usn === notebook.Usn) { + console.log('notebookLocal 如果Usn一样, 表示服务器端并没有修改'); + return canCall(); + } + + // 2.2 本地是否修改了, 需要合并, 使用服务端的数据 + if (notebook.IsDirty) { + console.log('冲突....') + // 2.3 服务器是最新的, 用服务器的 + } else {} + // 这里都是用服务器端的数据, 不处理冲突 + Notebook.updateNotebookForce(notebook, notebookLocal, function(notebook) { + if (notebook) { + // 前端一起渲染 + me._syncInfo.notebook.updates.push(notebook); + // me.fixSynced('notebook', 'updates', notebook); + } + canCall(); + }) + } + }); + })(notebook); + } + }, + + syncNotebook: function(afterUsn, callback) { + var me = this; + if (me.isStop()) { + return; + } + Api.getSyncNotebooks(afterUsn, me._notebookMaxEntry, function(notebooks) { + console.log(' syncNotebook', notebooks) + // console.log(notebooks); + if (Common.isOk(notebooks)) { + me._totalSyncNotebookNum += notebooks.length; + // 证明可能还有要同步的 + if (notebooks.length == me._notebookMaxEntry) { + me._syncNotebookToLocal(notebooks, callback); + var last = notebooks[notebooks.length - 1]; + me.syncNotebook(last.Usn, callback); + + // 更新Usn + me.updateSyncUsn('NotebookUsn', last.Usn); + + } else { + console.log(' no more notebooks'); + me._syncNotebookIsLastChunk = true; + me._syncNotebookToLocal(notebooks, callback); + + if (notebooks.length) { + var last = notebooks[notebooks.length - 1]; + me.updateSyncUsn('NotebookUsn', last.Usn); + } + } + } else { + // 同步失败 + me._syncInfo.notebook.ok = false; + me._syncInfo.notebook.msg = "cann't get all chunks"; + callback && callback(false); + } + }); + }, + + //------------- + // note + //------------- + + // 增加, 有锁 + _lockNote: 1, + _addSyncNoteNum: function() { + var me = this; + if (me._lockNote) { + me._lockNote = 0; + me._totalHasSyncNoteNum++; + me._lockNote = 1; + } else { + me._addSyncNoteNum(); + } + }, + + // 同步笔记到本地 + _syncNoteToLocal: function(notes, callback) { + var me = this; + + function canCall(isEmpty) { + // 为空时来判断是最后一次了, 可以之前的还没处理完 + if (isEmpty && me._totalHasSyncNoteNum < me._totalSyncNoteNum) { + return; + } + + // 是最后一块, 且 + me._addSyncNoteNum(); + // log('notes: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk); + if (me._syncNoteIsLastChunk && me._totalHasSyncNoteNum >= me._totalSyncNoteNum) { + // 防止多次callback + if (!me._syncInfo.note.ok) { + log('note->next'); + me._syncInfo.note.ok = true; + callback && callback(true); + } + } + } + + // 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了 + if (!notes || notes.length == 0) { + return canCall(true); + } + + for (var i in notes) { + var note = notes[i]; + // 得到本地的, 与之对比 + Web.syncProcess('note', note.Title); + + (function(note) { + + var usn = note.Usn; + var noteId = note.NoteId; + + // 1) 服务器端删除了, 本地肯定删除 + if (note.IsDeleted) { + console.log(' delete: ', note); + Note.getNoteIdByServerNoteId(noteId, function(localNoteId) { + Note.deleteNoteForce(noteId, function() { + // me._syncInfo.note.deletes.push(localNoteId); + me.fixSynced('note', 'deletes', localNoteId); + canCall(); + }); + }); + return; + } + // 2) 查看本地的, 与本地合并 + Note.getNoteByServerNoteId(noteId, function(noteLocal) { + // 2.1 本地没有, 表示是新建 + if (!noteLocal) { + console.log(' add note', note); + Note.addNoteForce(note, function(note) { + // me._syncInfo.note.adds.push(note); + me.fixSynced('note', 'adds', note); + return canCall(); + }); + } else { + // 如果Usn一样, 表示服务器端并没有修改 + if (noteLocal.Usn === note.Usn) { + console.log(' note 如果Usn一样, 表示服务器端并没有修改'); + return canCall(); + } + + // 2.2 本地是否修改了, 冲突, 报告给前端, 前端处理 + // 冲突, 将本地修改的笔记复制一份(设置冲突字段, ConflictNoteId), 远程的覆盖本地的 + // 新方法: 冲突后, 得到最新内容, 看是否与本地内容一致, 如果一致, 则不冲突, 其它数据用服务器上的 + if (noteLocal.IsDirty) { + console.log(' note 冲突 serverNoteId: ' + noteId, note.Title); + // console.log(noteLocal.NoteId); + // console.log(noteLocal.IsDirty); + // console.log(noteLocal); + // note.ServerNoteId = note.NoteId; + // note.NoteId = noteLocal.NoteId; + + Note.getNoteContentFromServer(noteId, function(content) { + // 表示没有获取到content, 则只能标志为冲突了 + // 内容不一样, 也标为冲突 + if (content === false || content != noteLocal.Content) { + me._syncInfo.note.conflicts.push({ server: note, local: noteLocal }); + // me.fixSynced('note', 'conflicts', {server: note, local: noteLocal}); + } + // 否则, 内容一样, 标为不冲突, 需要更新 + else { + + // 2.3 服务器是最新的, 用服务器的 + // 服务器是最新的, 本地没动过, 则覆盖之 + Note.updateNoteForce(note, function(note) { + if (note) { + // me._syncInfo.note.updates.push(note); + me.fixSynced('note', 'updates', note); + } + canCall(); + }, false); + } + }); + + return canCall(); + // 2.3 服务器是最新的, 用服务器的 + } else { + // 服务器是最新的, 本地没动过, 则覆盖之 + Note.updateNoteForce(note, function(note) { + if (note) { + // me._syncInfo.note.updates.push(note); + me.fixSynced('note', 'updates', note); + } + canCall(); + }); + } + } + }); + })(note); + } + }, + + syncNote: function(afterUsn, callback) { + var me = this; + if (me.isStop()) { + return; + } + + console.log(' pull notes from server...'); + Api.getSyncNotes(afterUsn, me._noteMaxEntry, function(notes) { + // console.log('syncNote---'); + console.log(' notes:', notes); + if (Common.isOk(notes)) { + me._totalSyncNoteNum += notes.length; + // 证明可能还有要同步的 + if (notes.length == me._noteMaxEntry) { + me._syncNoteToLocal(notes, callback); + var last = notes[notes.length - 1]; + + // 500ms延迟 + setTimeout(function() { + me.syncNote(last.Usn, callback); + }, 500); + + // 更新Usn + me.updateSyncUsn('NoteUsn', last.Usn); + + } else { + console.log(' no more notes'); + me._syncNoteIsLastChunk = true; + me._syncNoteToLocal(notes, callback); + + if (notes.length) { + var last = notes[notes.length - 1]; + me.updateSyncUsn('NoteUsn', last.Usn); + } + } + } else { + // 同步失败 + me._syncInfo.note.ok = false; + me._syncInfo.note.msg = "cann't get all chunks"; + console.error(' pull notes error', notes) + callback && callback(false); + } + }); + }, + + + //---------------- + // 同步标签 + // --------------- + // 增加, 有锁 + _lockTag: 1, + _addSyncTagNum: function() { + var me = this; + if (me._lockTag) { + me._lockTag = 0; + me._totalHasSyncTagNum++; + me._lockTag = 1; + } else { + me._addSyncTagNum(); + } + }, + + // 同步标签到本地 + _syncTagToLocal: function(tags, callback) { + var me = this; + + function canCall(isEmpty) { + // 为空时来判断是最后一次了, 可以之前的还没处理完 + if (isEmpty && me._totalHasSyncTagNum < me._totalSyncTagNum) { + return; + } + + // 是最后一块, 且 + me._addSyncTagNum(); + // log('tags: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk); + if (me._syncTagIsLastChunk && me._totalHasSyncTagNum >= me._totalSyncTagNum) { + // 防止多次callback + if (!me._syncInfo.tag.ok) { + log('tag->next'); + me._syncInfo.tag.ok = true; + callback && callback(true); + } + } + } + + // 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了 + if (!tags || tags.length == 0) { + return canCall(true); + } + + for (var i in tags) { + var tag = tags[i]; + // 得到本地的, 与之对比 + Web.syncProcess('tag', tag.Tag); + + (function(tag) { + + var usn = tag.Usn; + var tagId = tag.TagId; + + // 1) 服务器端删除了, 本地肯定删除 + if (tag.IsDeleted) { + console.log(' delete tag: ', tag); + Tag.deleteTag(tag.Tag, function() { + // me._syncInfo.tag.deletes.push(tag.Tag); + me.fixSynced('tag', 'deletes', tag.Tag); + canCall(); + }, true, me.fullSyncStart); + return; + } + // 2) 查看本地的, 与本地合并 + Tag.getTag(tag.Tag, function(tagLocal) { + // 2.1 本地没有, 表示是新建 + if (!tagLocal) { + console.log(' add tag: ...') + Tag.addOrUpdateTag(tag.Tag, function(tagAdded) { + // me._syncInfo.tag.adds.push(tagAdded); + me.fixSynced('tag', 'adds', tagAdded); + return canCall(); + }, true, usn); + } else { + // 本地有, 不用操作 + Tag.setNotDirtyAndUsn(tag.Tag, tag.Usn); + return canCall(); + } + }); + })(tag); + } + }, + syncTag: function(afterUsn, callback) { + var me = this; + if (me.isStop()) { + return; + } + Api.getSyncTags(afterUsn, me._tagMaxEntry, function(tags) { + // console.log(afterUsn + ' syncTags------------------------------------') + // console.log(tags); + if (Common.isOk(tags)) { + me._totalSyncTagNum += tags.length; + // 证明可能还有要同步的 + if (tags.length == me._tagMaxEntry) { + me._syncTagToLocal(tags, callback); + var last = tags[tags.length - 1]; + me.syncTag(last.Usn, callback); + + // 更新Usn + me.updateSyncUsn('TagUsn', last.Usn); + } else { + log('no more'); + me._syncTagIsLastChunk = true; + me._syncTagToLocal(tags, callback); + + if (tags.length) { + var last = tags[tags.length - 1]; + me.updateSyncUsn('TagUsn', last.Usn); + } + } + } else { + // 同步失败 + me._syncInfo.tag.ok = false; + me._syncInfo.tag.msg = "cann't get all chunks"; + console.error(' sync tags error...') + // console.log(tags); + callback && callback(false); + } + }); + }, + + //---------- + + // 记录LastSyncUsn, LastUpdateTime 同步时间 + updateLastSyncState: function(callback) { + var me = this; + User.updateLastSyncState(function() { + callback(); + }); + }, + + // 为了避免全都重新来过, 这里保存每次 + updateSyncUsn: function(type, usn) { + // console.error('---') + // console.log(type + ' = ' + usn); + User.updateEachSyncState(type, usn, function() {}); + }, + + // 全量同步 + // callback(error, info) + fullSync: function(callback) { + var me = this; + me._stop = false; + me._initSyncInfo(); + me.fullSyncStart = true; + + User.getAllLastSyncState(function(lastUsn, notebookUsn, noteUsn, tagUsn) { + // 不可能会有lastUsn吧 + if (lastUsn && lastUsn > 0) { + notebookUsn = -1; + noteUsn = -1; + tagUsn = -1; + } + + if (!notebookUsn) { + notebookUsn = -1; + } + if (!noteUsn) { + noteUsn = -1; + } + if (!tagUsn) { + tagUsn = -1; + } + + console.log('fullSync ' + notebookUsn + ' ' + noteUsn + ' ' + tagUsn); + + Api.getLastSyncState(function(serverState) { + if (!Common.isOk(serverState)) { + console.error(' get Server LastSyncState error!!'); + callback && callback(serverState, null); + return; + } + + // Web.syncNotebookFinish(); + // 同步笔记本 + me.syncNotebook(notebookUsn, function(ok) { + if (ok) { + // Web.syncNoteFinish(); + // console.log('------------------') + // 同步笔记 + me.syncNote(noteUsn, function(ok) { + if (ok) { + // Web.syncTagFinish(); + // 同步标签 + me.syncTag(tagUsn, function(ok) { + if (ok) { + me.fullSyncStart = false; + // 更新上次同步时间 + me.updateLastSyncState(function() { + // send changes + // me.sendChanges(); + callback && callback(false, me._syncInfo); + }); + } else { + me.fullSyncStart = false; + console.error('syncTag error....'); + callback && callback({}, me._syncInfo); + } + }); + } else { + me.fullSyncStart = false; + console.error('syncNote error.... 跳过tag'); + callback && callback({}, me._syncInfo); + } + }); + } else { + me.fullSyncStart = false; + console.error('syncNotebook error.... 跳过note,tag'); + callback && callback({}, me._syncInfo); + } + }); + + }); + + }); + }, + + // 处理同步好的之后的 + // mainType == notebook, note, tag + // type = changeAdds, changeConflicts, adds, deletes, updates, conflicts, errors, + fixSynced: function(mainType, type, data) { + if (this.fullSyncStart) { + return; + } + var o = {}; + if (!Common.isArray(data)) { + data = [data]; + } + o[type] = data; + if (mainType == 'notebook') { + Notebook.fixConflicts(o); + } else if (mainType == 'note') { + Note.fixConflicts(o); + } else { + Web.addOrDeleteTagFromSync(o); + } + }, + + // 前端重新渲染 + fixSyncedNotebook: function() { + var me = this; + if (me.incrSyncStart) { + console.log(' fixSyncedNotebook') + Notebook.fixConflicts(me._syncInfo.notebook); + } + }, + + // 处理冲突 + fixConflicts: function(callback) { + var me = this; + var afterInfo = me._syncInfo; + // log('处理冲突....'); + // log(me._syncInfo); + // log(me._syncInfo.tag); + var tag = me._syncInfo.tag; + + // 如果是incSync, 则要前端处理 + // 不是fullSync + if (me.incrSyncStart) { + + // Notebook.fixConflicts(me._syncInfo.notebook); + + Note.fixConflicts(me._syncInfo.note, function() { + // 避免无限循环, 别send changes了 + if (!me._needIncrSyncAgain) { + // alert("?") + console.log(" not needIncrSyncAgain") + // send changes + callback && callback(); + } else { + + } + }); + + // 添加或删除一些tag + // console.log('怎么可能?') + // console.error(me._syncInfo.tag); // 为空, 不知道原因 + // console.error(); + // console.error(tag); + Web.addOrDeleteTagFromSync(tag); + } + }, + + fixConflictsForSendChanges: function(callback) { + var me = this; + me.fixConflicts(function() { + callback(); + // 已结束 + me.setSyncFinished(); + }) + }, + // 同步状态 + syncProcessStatus: 0, + // 添加 + addSyncProcessStatus: function(n) { + var me = this; + me.syncProcessStatus += n; + if (me.syncProcessStatus >= 100) { + me.syncProcessStatus = 98; + } + Web.syncProgress(me.syncProcessStatus); + }, + // 增量同步 + incrSyncStart: false, + // 如果第一次insync, 网络错误导致incrSyncStart不结束, 第二次就会永远转动 + setSyncFinished: function(hasError) { + var me = this; + // unconnect也会调, 所以, 一旦所有都是unconnect, 那就时不时会有进度条到100 + if (!me.incrSyncStart || !me.incrSyncStart) { + return; + } + me.incrSyncStart = false; + me.fullSyncStart = false; + // Web.syncProgress(0); + Web.syncFinished(hasError); + }, + incrSync: function(again) { + if (User.isLocal()) { + console.log('no sync for local account'); + return; + } + var me = this; + me._stop = false; + me._initSyncInfo(); + + // again表示重来 + if (!again && me.incrSyncStart) { + console.log('上一sync未结束!!'); + return; + } + + me.incrSyncStart = true; + me.syncProcessStatus = 0; + + console.log('inc sync start'); + if (again) { + console.log('again >>'); + } + + // 得到当前LastSyncUsn + User.getLastSyncState(function(lastSyncUsn, lastSyncTime) { + console.log('%cstep1 getLastSyncState', 'color: #68bc7a') + + // 没有上次同步的时间, 则需要进行一次全量同步, 不可能会发生 + if (!lastSyncUsn) { + console.error(' getLastSyncState error!!'); + me.setSyncFinished(); + return; + } + + // 先从服务器上得到usn, 与本地的判断, 是否需要pull + Api.getLastSyncState(function(serverState) { + if (!Common.isOk(serverState)) { + console.error(' get Server LastSyncState error!!'); + me.setSyncFinished(true); + return; + } + console.log(' get Server LastSyncState ret', serverState.LastSyncUsn + ' ' + lastSyncUsn); + if (serverState.LastSyncUsn > lastSyncUsn) { + // 需要同步笔记本 + console.log('%cstep2 pull', 'color: #68bc7a') + // 同步笔记本 + me.syncNotebook(lastSyncUsn, function(ok) { + if (ok) { + me.fixSyncedNotebook(); + console.log(' incr notebook ok', lastSyncUsn); + + me.addSyncProcessStatus(10); + console.log(' incr note start'); + + // 同步笔记 + me.syncNote(lastSyncUsn, function(ok) { + if (ok) { + console.log(' incr note ok', lastSyncUsn); + me.addSyncProcessStatus(30); + // 同步标签 + me.syncTag(lastSyncUsn, function() { + console.log(' incr tag ok', lastSyncUsn); + me.addSyncProcessStatus(10); + // 更新上次同步时间 + me.updateLastSyncState(function() { + // send changes + me.sendChanges(again); + }); + }); + } else { + console.log(' incr note not ok') + me.fixConflicts(); + } + }); + } else { + me.fixConflicts(); + } + }); + + } else { + console.log('%cstep2 不需要pull, skip', 'color: #68bc7a') + me.addSyncProcessStatus(50); + me.sendChanges(again); + } + }); + }); + }, + + //--------- + // 发送改变 + //--------- + + // 发送笔记本的更改 + sendNotebookChanges: function(callback) { + var me = this; + console.log(' 3.1: sendNotebookChanges') + // 获取所有笔记本的更改 + Notebook.getDirtyNotebooks(function(notebooks) { + if (!Common.isEmpty(notebooks)) { + console.log(' dirty notebooks:', notebooks); + } else { + console.log(' no dirty notebooks'); + } + if (!notebooks) { + callback && callback(); + } else { + // 调api, 所有执行后再callback(); + // 一个一个同步执行, 因为要有 + async.eachSeries(notebooks, function(notebook, cb) { + var api = Api.updateNotebook; + + // 本地既是新的, 又是删除的, 删除本地的, 不要同步 + // 5/4 + if (notebook.LocalIsNew && notebook.LocalIsDelete) { + console.log(' 笔记本既是新的, 又是删除的, 不同步, 直接删除本地的'); + Notebook.deleteLocalNotebook(notebook.NotebookId); + return cb(); + } + + if (notebook.LocalIsNew) { + api = Api.addNotebook; + } else if (notebook.LocalIsDelete) { + api = Api.deleteNotebook; + } + + api.call(Api, notebook, function(newNotebook) { + // 更新失败 + if (!newNotebook) { + return cb(); + } + + // 删除操作 + if (notebook.LocalIsDelete) { + return cb(); + } + + // 更新成功, 是否有冲突? + // newNotebook是服务器上的笔记本 + // 没有更新成功 + if (!newNotebook.NotebookId) { + if (newNotebook.Msg == 'conflict') { + // 没用, 前端不会处理的, 按理不会出现这种情况, 因为先sync + // me._syncInfo.notebook.conflicts.push(newNotebook); + me.fixSynced('notebook', 'conflicts', newNotebook); + } else if (newNotebook.Msg == 'notExists') { + // 服务器端没有, 那么要作为添加 + // 可能服务器上已删除, 此时应该要作为添加而不是更新 + // me._syncInfo.notebook.changeNeedAdds.push(notebook); + me.fixSynced('notebook', 'changeNeedAdds', notebook); + } + + // me.checkNeedIncSyncAgain(newNotebook.Usn); + return cb(); + } else { + // 更新 + // TODO 后端updateNotebook只要传Usn回来即可 + console.log(" 返回来的notebook " + newNotebook.Title) + + Notebook.updateNotebookForceForSendChange(notebook.NotebookId, newNotebook, function() { + if (notebook.LocalIsNew) { + // me._syncInfo.notebook.changeAdds.push(newNotebook); + me.fixSynced('notebook', 'changeAdds', newNotebook); + } else { + // me._syncInfo.notebook.changeUpdates.push(newNotebook); + me.fixSynced('notebook', 'changeUpdates', newNotebook); + } + + // 这里才cb(), 因为先添加父, 再添加子 + me.checkNeedIncSyncAgain(newNotebook.Usn); + cb(); + }); + } + + }); + }, function() { + callback && callback(); + }); + } + }); + }, + + checkNeedIncSyncAgain: function(usn) { + var me = this; + // 如果之前都很正常 + if (!me._needIncrSyncAgain) { + // 检查是否有问题 + if (User.getLastSyncUsn() + 1 == usn) { + // 更新到本地lastSyncUsn + User.updateLastSyncUsn(usn); + } else { + // newNote.Usn > User.getLastSyncUsn + 1, 表示服务器端在本次同步后, sendChanges之前有更新 + // 那么, 还需要来一次incrSync, 之后 + if (User.getLastSyncUsn() > usn) { // 添加标签时如果标签已经存在, 则返回的是旧的 + return; + } + console.error('---?? checkNeedIncSyncAgain ??------' + usn) + console.trace(); + me._needIncrSyncAgain = true; + } + } + }, + + // 发送笔记改变 + // 发送笔记本的更改 + sendNoteChanges: function(callback) { + var me = this; + console.log(' 3.2: sendNoteChanges'); + // 获取所有笔记本的更改 + Note.getDirtyNotes(function(notes) { + if (!Common.isEmpty(notes)) { + console.log(' dirty notes:', notes); + } else { + console.log(' no dirty notes'); + } + if (!notes) { + callback && callback(); + } else { + // 调api, 所有执行后再callback(); + // 一个一个同步执行, 因为要有 + async.eachSeries(notes, function(note, cb) { + + if (note.InitSync) { + console.log(' InitSync is Dirty', note); + return cb(); + } + + if (note.ConflictNoteId && !note.ConflictFixed) { + console.log(' 未解决的冲突不同步', note.Title); + return cb(); + } + + if (note.LocalIsNew) { + // 是新的, 且不是trash和删除的 + if (!note.IsTrash && !note.LocalIsDelete) { + // 添加, newNote的返回不会很多值(server端) + Api.addNote(note, function(err, newNote) { + if (err || !Common.isOk(newNote)) { + console.log(' 添加笔记失败!', err, newNote); + me._syncInfo.note.errors.push({ err: err, ret: newNote, note: note }); + return cb(); + } + console.log(' 添加笔记成功!', note.Title); + + newNote.ServerNoteId = newNote.NoteId; + newNote.NoteId = note.NoteId; + + newNote.IsBlog = note.IsBlog; // 前端要用 + + // me._syncInfo.note.changeAdds.push(newNote); + me.fixSynced('note', 'changeAdds', newNote); + + Note.updateNoteForceForSendChange(newNote, true); + + // 这里 + me.checkNeedIncSyncAgain(newNote.Usn); + + cb(); + }); + } + // 5/4 + // 本地已经删除了, 则彻底删除之 + else if (note.LocalIsDelete) { + console.log(' 既是新的, 又是删除的, 则删除笔记的'); + Note.deleteLocalNote(note.NoteId); + return cb(); + } + // isTrash, 不同步, 不删除 + else { + console.log(' 既是新的, 又是trash的, 不要同步'); + return cb(); + } + } else if (note.LocalIsDelete) { + // 删除, 不管它了 + // TODO + Api.deleteTrash(note, function(ret) { + if (Common.isOk(ret)) { + me.checkNeedIncSyncAgain(ret.Usn); + + // 本地删除了的, 服务端没有, 直接删除本地的 + } else if (typeof ret == 'object' && ret.Msg == 'notExists') { + console.log('本地删除了的, 服务端没有, 直接删除本地的'); + Note.deleteLocalNote(note.NoteId); + } + return cb(); + }); + } else { + // 更新 + Api.updateNote(note, function(err, ret) { + if (err || !Common.isOk(ret)) { + console.log(' update error:' + note.Title, ret); + if (typeof ret == 'object') { + if (ret.Msg == 'conflict') { + console.error(' updateNote 冲突'); + + // 这种情况有很少见, 原因是先pull, 肯定会pull过来 + // 处理方法和pull一样, 看内容是否一样 + // 如果一样, 则不标志为冲突, 修改本Usn为serverUsn, 等下次再send changes + Note.getNoteContentFromServer(note.ServerNoteId, function(content) { + if (content === false || content != note.LocalContent) { + me._syncInfo.note.changeConflicts.push(note); + cb(); + } else { + // 不冲突, 修改之Usn + Api.getNote(note.ServerNoteId, function(serverNote) { + // 取不到, 当作冲突 + if (!serverNote) { + me._syncInfo.note.changeConflicts.push(note); + cb(); + } else { + Note.updateNoteUsn(note.NoteId, serverNote.Usn); + cb(); + } + }); + } + }); + } else if (ret.Msg == 'notExists') { + Note.setError(note.NoteId, err, ret); + me.fixSynced('note', 'errors', { err: err, ret: ret, note: note }); + // me._syncInfo.note.errors.push({err: err, ret: ret, note: note}); + // 可能服务器上已删除, 此时应该要作为添加而不是更新 + // me._syncInfo.note.changeNeedAdds.push(note); + cb(); + } + // 其它错误, 不加, notImage 导致不能终止 + else { + Note.setError(note.NoteId, err, ret); + console.error(' updateNote error', err, ret); + me.fixSynced('note', 'errors', { err: err, ret: ret, note: note }); + cb(); + } + } + // 更新失败了, 服务器返回异常 + else { + Note.setError(note.NoteId, err, ret); + console.error(' updateNote error', err); + me.fixSynced('note', 'errors', { err: err, ret: ret, note: note }); + cb(); + } + } + // 更新成功 + else { + console.log(' 更新成功: ', note.Title); + ret.ServerNoteId = ret.NoteId; + ret.NoteId = note.NoteId; + Note.updateNoteForceForSendChange(ret, false); + // me._syncInfo.note.changeUpdates.push(note); + me.fixSynced('note', 'changeUpdates', note); + + me.checkNeedIncSyncAgain(ret.Usn); + + return cb(); + } + }); + } + }, function() { + callback && callback(); + }); + } + }); + }, + // 发送标签改变 + sendTagChanges: function(callback) { + console.log(' 3.3: sendTagChanges'); + var me = this; + // 获取所有笔记本的更改 + Tag.getDirtyTags(function(tags) { + if (!Common.isEmpty(tags)) { + console.log(' dirty tags:', tags); + } else { + console.log(' no dirty tags'); + } + if (!tags) { + callback && callback(); + } else { + // 调api, 所有执行后再callback(); + // 一个一个同步执行, 因为要有 + async.eachSeries(tags, function(tag, cb) { + if (tag.IsDirty) { + if (!tag.LocalIsDelete) { + // 添加 + Api.addTag(tag.Tag, function(newTag) { + if (!Common.isOk(newTag)) { + return cb(); + } + // 更新, 添加usn + Tag.setNotDirtyAndUsn(tag.Tag, newTag.Usn); + + me._syncInfo.tag.changeAdds.push(newTag); // 之前是note.changeAdds + // Tag.updateTagForce(newTag); + me.checkNeedIncSyncAgain(newTag.Usn); + cb(); + }); + } else { + // 删除, 不管它了 + Api.deleteTag(tag, function(ret) { + if (Common.isOk(ret)) { + Tag.setNotDirty(tag.Tag); + me.checkNeedIncSyncAgain(ret.Usn); + // 本地删除了的, 服务端没有, 直接删除本地的 + } else if (typeof ret == 'object') { + if (ret.Msg == 'notExists') { + console.log('tag本地删除了的, 服务端没有, 直接删除本地的'); + Tag.deleteLocalTag(tag.Tag); + } else if (ret.Msg == 'conflict') { + Tag.setNotDirty(tag.Tag); + } + } + return cb(); + }); + } + } + }, function() { + callback && callback(); + }); + } + }); + }, + + // again, 再一次sync, 不要send changes + sendChanges: function(again) { + var me = this; + console.log('%cstep3 send changes', 'color: #68bc7a') + // 先处理冲突, 可以同时进行 + if (again) { + console.log(' send changes again....'); + me.fixConflictsForSendChanges(function() {}); + return; + } + me.fixConflicts(function() { + // send changes + console.log(' send changes'); + me._initSyncInfo(); // 重新初始化[] + async.series([ + function(cb) { + me.sendNotebookChanges(cb); + }, + function(cb) { + me.addSyncProcessStatus(10); + me.sendNoteChanges(cb); + }, + function(cb) { + me.addSyncProcessStatus(30); + me.sendTagChanges(cb); + } + ], function() { + me.addSyncProcessStatus(10); + + // 重新再来一次增量同步 + if (me._needIncrSyncAgain) { + console.error(' needIncrSyncAgain') + me.fixConflictsForSendChanges(function() { + me.incrSync(true); + }); + } else { + console.log(' send changes 后解决冲突'); + me.fixConflictsForSendChanges(function() {}); + } + }); + }); + } }; module.exports = Sync; \ No newline at end of file