Files
desktop-app/node_modules/sync.js
2017-01-22 22:41:16 +08:00

1231 lines
34 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var db = require('db');
var async = require('async');
var Common = require('common');
var User = require('user');
var Tag = require('tag');
var Tags = db.tags;
var needle = require('needle');
var fs = require('fs');
var Api = require('api');
var Notebook = require('notebook');
var Note = require('note');
var Web = require('web');
function log(o) {
// console.log(o);
}
// 同步服务
/*
syncProgress 设置
两阶段, 各个50%
1. <-
notebook 10 -> 10%
note 30 -> 40%
tag 10 -> 50%
2. ->
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() {
});
}
});
});
}
};
module.exports = Sync;