Database - Graan/open GitHub Wiki

本地表

  • User
  • Issue
  • IssueComment
  • Label
  • Repos

数据同步

方案一:通过Event

测试了如下接口:

  1. List repository events GET /repos/:owner/:repo/events
  2. List issue events for a repository GET /repos/:owner/:repo/issues/events

发现的问题:

  1. 接口1返回的事件只有几种类型:IssueCommentEvent只有created状态没有修改和删除事件,IssuesEvent只有opened和closed状态没有修改的事件
  2. 接口2返回的事件只发现了labeled和unlabeled,未发现关于修改的事件

综上所述,目前通过Event方式暂时行不通

方案二:每次打开app拉取所有Issue,IssueComment,Label更新

同步:定时从本地检查有修改的数据并上传,每次打开app优先上传本地待上传数据
更新:每次打开app上传完有改动数据后重新拉取所有数据更新本地,用户手动点更新按钮拉取所有数据更新本地,不做定时拉取所有数据(或拉取时间设置大一些),Issue和IssueComment接口提供了since参数,本地记录每次更新时间time,下次更新time以后的数据
缺点:

  1. 拉取数据时会把所有数据都拉取一遍,不太友好(通过since参数减少拉取量)
  2. 多设备覆盖问题,同一条日记,假设3.1日A设备修改数据后未上传,3.2日B设备修改了数据上传了,3.3日打开A设备,会向上同步数据覆盖掉3.2日的数据

方案三:在.graan文件夹创建一个log文件

本地维护log表,同步到github
大体逻辑:

  1. 本地修改内容后首先同步到数据库中,并做标记
  2. 开启定时任务,检测表中数据改动并上传,上传成功后在log文件中标记该数据(类型,id,isSync,time ...
  3. 上传log文件覆盖远端log文件
  4. 定时向下拉取log文件,跟本地log文件对比,把差异内容做同步

缺点:

  1. log文件的处理比较复杂,怎么对比,怎么解决多端登录后的冲突,数据也会越来越多,每次对比本地log和远端log产生的逻辑判断比较复杂且耗时
  2. 用户手动在github修改内容后无法写入到log文件中,因此无法根据log文件同步到最新内容

表结构

User

{
	"login": "tianyu704",
	"id": 7546212,//PrimaryKey
	"avatar_url": "https://avatars3.githubusercontent.com/u/7546212?v=4",
	"name": "Hugo.Guo",
	"email": "[email protected]",
	"created_at": "2014-05-11T02:07:59Z",
	"updated_at": "2019-03-26T08:35:21Z"
}

Issue

{
	"id": 410647133,//PrimaryKey
	"number": 5,
        "repository_url":"https://api.github.com/repos/tianyu704/Test"
	"title": "记录",
	"labels": [{
		"id": 1217139811,
		"url": "https://api.github.com/repos/tianyu704/Test/labels/_note",
		"name": "_note",
		"color": "ededed",
		"default": false
	}],
	"state": "open",
	"locked": false,
	"comments": 1,
	"created_at": "2019-02-15T07:56:07Z",
	"updated_at": "2019-03-25T10:07:56Z",
	"closed_at": null,
	"body": "这里记录   大发发",
        "has_local_change":1
}

IssueComment

{
	"id": 463037067,//PrimaryKey
	"created_at": "2019-02-13T03:00:29Z",
	"updated_at": "2019-02-14T06:33:22Z",
        "issue_url": "https://api.github.com/repos/tianyu704/Test/issues/1",
	"body": "# 大标题\n```code hejeueu```\n* wws\n* zzzzz\n~~哈哈~~\n\n## 小标题",
        "has_local_change":1,
        "state":"" //delete/create/update
}

Reaction

{
	"id": 1232112341234,
	"content": "heart",
	"comment_id": 1231341234,
	"has_local_change": 0,
	"state": "delete" // delete/create
}

Label

{
	"id": 1258786034,//PrimaryKey
	"url": "https://api.github.com/repos/tianyu704/Test/labels/_bookmark",
	"name": "_bookmark",
	"color": "ededed",
	"default": false,
        "state":"", //delete/create/""
        "has_local_change":1
}

Repos

{
	"id": 174089690,
	"name": "butterknife",
        "full_name": "tianyu704/butterknife",
	"private": false,
	"description": "Bind Android views and callbacks to fields and methods.",
	"created_at": "2019-03-06T06:58:21Z",
	"updated_at": "2019-03-06T06:58:24Z"
}

本地数据库操作及数据同步细节

  1. 创建仓库暂时只支持在线创建。创建完仓库的同时要在线创建默认的日记Issue,在线创建默认的日记IssueComment
  2. 用户切换到日记页面,检测当前仓库有没有日记Issue,没有先创建日记Issue,再创建日记IssueComment
  3. 其他Issue,IssueComment,Label表都离线操作,数据定时(暂定60s)统一上传和更新
  4. 循环拉取的顺序Repository->Issue->IssueComment->Reaction,Label可与Issue同时开始拉取

Issue表

  1. 用户修改笔记/书签的state(删除操作)、标题、内容、标签后优先修改到Issue表中,将has_local_change值改成1
  2. 用户创建笔记/书签,取当前时间戳作为Id存储到数据库中,并将has_local_change值改成1
  3. 定时上传Issue,检查has_local_change=1的数据,分为两组:第一组是number=0,代表新创建的Issue,遍历并执行创建接口,创建成功后将这条数据从数据库中删除并且把接口返回的Issue信息存入数据库完成上传操作;第二组是number≠0,代表是修改的Issue,遍历执行更新接口并把返回结果更新到本地数据库中完成上传操作
  4. 数据都上传完后,执行下载接口,下载前取当前系统时间sinceNew作为临时变量,请求Issue列表时传入since=上次更新时间,下载完成后将结果更新到数据库中,并更新本地since=sinceNew

Label表

  1. 用户创建Label后取当前时间戳作为Id,state="create",存入数据库中
  2. 用户删除Label,将state="delete",更新数据库
  3. 定时上传Label信息,取state="delete"的数据执行删除Label接口,删除成功后本地删除此Label
  4. 3执行完后执行更新Label操作,并更新本地数据库
  5. 去除冗余Label,查询state="create"且数据库中无关联的Issue的Label集合,遍历集合查询每条Label的url在Label库中是否有重复,有则删除此Label

说明:由于同步Issue表时,新创建的Label会通过更新Issue的方式在github端创建,因此上传Label表时无需再上传新建的Label,只需把删除的Label上传即可。冗余Label来自Issue上传后新Label会存储到Label库中,旧Label失效,但不能直接删除,有可能被其他Issue引用,但经过5中的过滤后可以保证此Label是冗余,再删除。本地创建且没有被Issue引用的Label上传不上传没有关系

IssueComment

  1. 用户修改日记时,修改相应字段,state="update",has_local_change=1,更新IssueComment表,如果有reaction的添加或删除,同时更新Reaction表
  2. 用户创建日记时,取时间戳作为Id,state="create",has_local_change=1存入IssueComment表中,如果有reaction的添加或删除,同时更新Reaction表
  3. 用户删除日记时,state="delete",has_local_change=1更新IssueComment表,将此IssueComment关联的reaction的state="delete"并更新
  4. 定时上传,查询has_local_change=1的数据根据state字段分类
    • state="update",将更新内容上传并将返回结果更新到数据库,检查该IssueComment关联的reaction,把has_local_change=1的reaction根据state执行删除或创建的上传操作,成功后添加上新的reaction并删除旧的reaction
    • state="create",将创建内容上传,成功后将旧IssueComment删除,将返回结果更新到数据库,检查旧IssueComment关联的reaction,更新comment_id为新IssueComment的id,再把has_local_change=1的reaction根据state执行删除或创建的上传操作,成功后添加上新的reaction并删除旧的reaction
    • state="delete",将删除的IssueComment执行删除接口,成功后删除本地IssueComment并删除关联的reaction
  5. 4执行完后,执行更新IssueComment接口,更新本地表

Repository表

  1. 用拉取.graan文件方式获取repos列表
  2. 遍历repos根据id查询本地数据库中是否有repo,如果数据库中没有但存在相同名字的repo,则删除库中的repo及issue,comment,reaction,label等信息后存入新repo,否则直接存入新repo。
  3. 查询库中所有repo,判断是否都在拉取的repos列表中,如果有不存在的,请求拉取repo接口,如果接口返回404,则删除本地repo及其他相关表
  4. 拉取顺序。处理完Repository,可同时异步拉取Issue和label,拉取完Issue依次同步拉取->IssueComment->Reaction