1
0
Files
coconut-leaf/documents/Principle_zh-CN.md

630 lines
21 KiB
Markdown
Raw Normal View History

2021-01-25 20:42:06 +08:00
# 日历原理
本文档记述此日历所用到的一些数据的注解包含对一些字段API数据结构设计的解读但不包含具体的日历算法。
## 杂项
日历的主要运算负载在循环事件的计算和处理上。在这方面,服务端负责计算循环事件的起始和结束时间。客户端利用服务端计算出的起始和结束时间,计算循环事件到底在哪些日子被循环了,进而进行渲染。
2021-01-29 11:10:25 +08:00
本日历系统基于UNIX时间戳进行处理。在设计上使用Int64进行存储以规避2038问题。同时由于日历设计不需要精确到秒因此本日历中与时间存储有关的时间戳使用标准UNIX时间戳/60来存储构成粒度为分钟的时间戳。但是其余的例如用户登录相关的时间戳仍然为标准UNIX时间戳
2021-01-25 20:42:06 +08:00
2021-01-29 11:10:25 +08:00
本日历目前无条件限定最小时间为1950年1月1日最大时间为2200年12月31日。
2021-01-25 20:42:06 +08:00
2021-01-30 21:27:36 +08:00
API在遇到当前接口产生错误时使用外层结构来进行返回错误例如token无效应用程序错误参数错误等。请注意捕获错误。如果没有错误则表示data字段为有效数据。
2021-01-25 20:42:06 +08:00
## 数据库
### 同步容错
如果当前表中具有名为`ccn_lastChange`的字段,则标名该表具有同步容错功能,客户端上传的请求来操作该表时,会比对这个字段数值,如果相同才允许操作,并更新一个新数值返回给客户端。如果不相同,则表示客户端和服务端之间的数据不同步,需要重新同步各端数据。
在未来会增加一个或几个新的表来专门处理同步冲突并允许用户选择保留哪一个数据。
### 日历表
数据库方面其余表都非常显而易见只有calendar表需要详细讲述每个字段的功能。
```sql
CREATE TABLE calendar(
[ccn_uuid] TEXT NOT NULL,
[ccn_belongTo] TEXT NOT NULL,
[ccn_title] TEXT NOT NULL,
[ccn_description] TEXT NOT NULL,
[ccn_lastChange] TEXT NOT NULL,
[ccn_eventDateTimeStart] BIGINT NOT NULL,
[ccn_eventDateTimeEnd] BIGINT NOT NULL,
[ccn_loopRules] TEXT NOT NULL,
[ccn_loopDateTimeStart] BIGINT NOT NULL,
[ccn_loopDateTimeEnd] BIGINT NOT NULL,
PRIMARY KEY (ccn_uuid),
FOREIGN KEY (ccn_belongTo) REFERENCES collection(ccn_uuid) ON DELETE CASCADE
);
```
`ccn_title`是事件标题。`ccn_description`是事件描述是一段JSON字典其内容交由客户端自行解析例如事件的标题颜色设置提醒等甚至包括ics文件标准中的一些基本用不到的功能例如忙碌状态地点备注等均交由此字段存储交给客户端自行解析此字段决定如何使用这些数据。本日历不负责定义此处需要存储何种内容但是定义其存储必须为JSON为了方便不同的客户端进行解析方便与ics文件之间相互转换。
`ccn_eventDateTimeStart``ccn_eventDateTimeEnd`,分别表示开始时间和结束时间。如果是循环事件,则表示此循环事件的第一个事件发生的时间。对于常用的点事件,或者全天事件,归于前者情况里面,通过设定开始和结束时间为一分钟和全天来解决。
`ccn_loopRules`是事件循环的规则,其格式详见后文的事件循环规则字符串章节。
`ccn_loopDateTimeStart``ccn_loopDateTimeEnd`是事件循环的时间,同时也被用于检索符合条件的事件返回给客户端。因此,对于非循环事件,其数值与`ccn_eventDateTimeStart``ccn_eventDateTimeEnd`保持一致。对于循环事件,则表示循环事件的循环的开始和结束时间。通常来说,`ccn_loopDateTimeStart``ccn_eventDateTimeStart`是一样的无论是循环还是非循环事件。循环结束时间有3种类型如果是无限循环则将结束时间设置为Int64最大值。如果是指定时间则设置成指定时间当天的最后一秒。如果是指定次数则由算法算出最后时间。
需要注意的是,`ccn_loopDateTimeStart``ccn_loopDateTimeEnd`描述的是一段区间,一段包含该循环事件所有事件的开始时间的区间,但不一定包含所有结束时间。如下图所示:
```
Event loop span
+-----------------------------------------------------------------------+
| |
| |
+---------------------+ +----------------------+ +------------------------+
|Event 1 | |Event 2 | |Event 3 |
+---------------------+ +----------------------+ +------------------------+
+----------------------------------------------------------------------------------------------------> Time line
| |
| |
v v
Event start Event end
```
## API
2021-02-01 20:34:40 +08:00
所有API均为POST请求
理论上所有更新操作名字里有update的除去必要的鉴别字段外其余字段均为可选字段可选字段只需要提供至少一个即可。但一些可选字段只有1条的就没有可选字段。
2021-01-29 11:10:25 +08:00
### Common类
Common类下的为通用请求接口一般与用户状态等相关
登录有两种方式一种是走加盐密码的通常登录也就是执行salt和login。另一种是直接传输明文登录通常是用于网页这种脚本语言太垃圾不适合计算HASH的场合请求发到webLogin。
#### salt
请求地址:`/api/common/salt`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|username|string|需要获取盐的用户名|
返回参数一个int表征盐
#### login
请求地址:`/api/common/login`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|username|string|需要登录的用户名|
|password|string|加盐密码计算方法是先将密码求SHA256并16进制输出小写再与盐的字符串形式拼接然后整体做SHA256输出16进制小写|
2021-01-30 17:30:28 +08:00
返回参数一个string作为token由客户端保存在之后的请求中用于鉴权
2021-01-29 11:10:25 +08:00
此请求与上面的盐获取请求配套使用,用于通常意义下的客户端登录。
#### webLogin
请求地址:`/api/common/webLogin`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|username|string|需要登陆的用户名|
|password|string|明文密码|
2021-01-30 17:30:28 +08:00
返回参数一个string作为token由客户端保存在之后的请求中用于鉴权
2021-01-29 11:10:25 +08:00
此请求设计用于Web端进行登录其安全性由HTTPS保证。
#### logout
请求地址:`/api/common/logout`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|要退出用户的一个token即销毁此token|
返回参数一个bool表示是否退出登录
#### tokenValid
请求地址:`/api/common/tokenValid`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|待检测的token|
返回参数一个bool表示是否有效
#### isAdmin
请求地址:`/api/common/isAdmin`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于管理员鉴别的token|
返回参数一个bool表示是否是管理员
#### changePassword
请求地址:`/api/common/changePassword`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|password|string|新的明文密码|
返回参数一个bool表示是否修改成功
此请求的安全性由HTTPS保证。
### Calendar类
Calendar类下的为日历请求接口
日历事件请求有两种一种是设计给网页使用的getFull一次性获取所有信息。另一种是为客户端同步设计的先用getList获取符合条件的事件的uuid和lastChange然后与本地比对然后通过getDetail与服务器进行同步这样可以节约掉一部分没有修改的事件的同步成本。
#### getFull
请求地址:`/api/calendar/getFull`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|startDateTime|int|事件开始时间|
|endDateTime|int|事件结束事件|
返回参数一个json为从calendar数据库中原样select出数据的数组。没有符合条件的则返回空数组。
#### getList
请求地址:`/api/calendar/getList`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|startDatetime|int|事件开始时间|
|endDatetime|int|事件结束事件|
返回参数一个json返回符合条件的calendar数据库中的uuid字段组成的数组。没有符合条件的则返回空数组。
#### getDetail
请求地址:`/api/calendar/getDetail`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|需要获取详细信息的事件的uuid|
返回参数一个json对象为给定uuid对象的数据库条目原样复制。没有符合条件的则返回null。
#### update
请求地址:`/api/calendar/update`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|要修改条目的uuid|
|belongTo|string|事件所属collection的uuid|
|title|string|事件标题|
|description|string|事件描述|
|eventDateTimeStart|int|事件开始时间|
|eventDateTimeEnd|int|事件结束时间|
|loopRules|string|事件循环规则|
2021-01-29 22:10:56 +08:00
|timezoneOffset|int|提交请求的用户的本地时间与UTC时间之间的差值使用本程序指定的粒度为分钟的时间差|
2021-01-29 11:10:25 +08:00
|lastChange|string|用于同步验证|
返回参数新的lastChange用以更新本地缓存
2021-02-01 20:34:40 +08:00
除去tokenuuidtimezoneOffset和lastChange这4项用来鉴别的条目外其余的条目均为可选项提供则更新不提供则不更新。
2021-01-29 11:10:25 +08:00
#### add
请求地址:`/api/calendar/add`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|belongTo|string|事件所属collection的uuid|
|title|string|事件标题|
|description|string|事件描述|
|eventDateTimeStart|int|事件开始时间|
|eventDateTimeEnd|int|事件结束时间|
|loopRules|string|事件循环规则|
2021-01-29 22:10:56 +08:00
|timezoneOffset|int|提交请求的用户的本地时间与UTC时间之间的差值使用本程序指定的粒度为分钟的时间差|
2021-01-29 11:10:25 +08:00
返回参数新事件的uuid用以本地更新
#### delete
请求地址:`/api/calendar/delete`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|要删除条目的uuid|
|lastChange|string|用于同步验证|
返回参数一个bool指示是否删除成功。
### Collection类
Collection类下的为日历集合请求接口
尾缀为Own的为对自己拥有集合的操作尾缀为Sharing的表示对自己拥有的某个集合的分享人员进行操作尾缀为Shared的表示对由他人分享的日历集合的操作。
2021-01-30 21:27:36 +08:00
其中Own结尾的get请求与日历事件获取请求相似也有两种一种是设计给网页使用的getFull。另一种是为客户端同步设计的getList和getDetail。
2021-01-29 11:10:25 +08:00
需要注意的是在数据表中这3部分由2个表描述collection和share在进行Sharing结尾的操作时也就是操作share表同时也会更新其在collection对应条目的lastChange。因此share表里没有lastChange而全部lastChange都在collection表里。
2021-01-30 21:27:36 +08:00
#### getFullOwn
2021-01-29 11:10:25 +08:00
2021-01-30 21:27:36 +08:00
请求地址:`/api/collection/getFullOwn`
2021-01-29 11:10:25 +08:00
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
返回参数一个json返回collection数据表中符合条件的条目组合成的数组
2021-01-30 21:27:36 +08:00
#### getListOwn
请求地址:`/api/collection/getListOwn`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
返回参数一个json返回collection数据表中符合条件的条目的uuid组合成的数组
#### getDetailOwn
请求地址:`/api/collection/getDetailOwn`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|需要获取集合的uuid|
返回参数一个json返回collection数据表中对应uuid的条目
2021-01-29 11:10:25 +08:00
#### addOwn
请求地址:`/api/collection/addOwn`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|name|string|新集合的名称|
返回参数新创建集合的uuid
#### updateOwn
请求地址:`/api/collection/updateOwn`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|需要修改集合的uuid|
|name|string|集合的新名称|
|lastChange|string|用于同步验证|
返回参数一个新的lastChange用于客户端更新
#### deleteOwn
请求地址:`/api/collection/deleteOwn`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|需要删除集合的uuid|
|lastChange|string|用于同步验证|
返回参数一个bool表示是否删除成功
#### getSharing
请求地址:`/api/collection/getSharing`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|用于获取的collection的uuid|
返回参数一个json返回share数据表中符合条件的条目组合成的数组
#### deleteSharing
请求地址:`/api/collection/deleteSharing`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|需要删除的相关集合的uuid|
|target|string|需要删除的用户名|
|lastChange|string|用于同步验证|
返回参数一个新的lastChange用于客户端更新
#### addSharing
请求地址:`/api/collection/addSharing`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|需要添加的相关集合的uuid|
|target|string|需要添加的用户名|
|lastChange|string|用于同步验证|
返回参数一个新的lastChange用于客户端更新
#### getShared
请求地址:`/api/collection/getShared`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
返回参数一个json返回被共享给自己的日历集合在collection数据表中对应条目的数组
### Todo类
Todo类下的为待办事项请求接口
待办请求与日历事件请求相似也有两种一种是设计给网页使用的getFull。另一种是为客户端同步设计的getList和getDetail。
#### getFull
请求地址:`/api/todo/getFull`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
返回参数一个json获取todo数据表中符合自己的条目的数组输出
#### getList
请求地址:`/api/todo/getList`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
返回参数一个json获取todo数据表中符合自己的条目的uuid字段的数组输出
#### getDetail
请求地址:`/api/todo/getDetail`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|待获取条目的uuid字段|
返回参数一个json指定uuid条目的数据库条目的输出不存在则输出null
#### add
请求地址:`/api/todo/add`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
返回参数新添加条目的uuid
#### update
请求地址:`/api/todo/update`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|待修改条目的uuid|
|data|string|新的数据|
|lastChange|string|用于同步验证|
返回参数新的lastChange用于客户端同步
#### delete
请求地址:`/api/todo/delete`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|uuid|string|待删除条目的uuid|
|lastChange|string|用于同步验证|
返回参数一个bool用于表明是否删除成功
### Admin类
Admin类下的为管理员请求接口
目前管理员接口只能对用户列表进行操作,因此请求的名字也没有后缀。
Admin类的操作需要管理员权限的token如果不具有权限服务端会返回错误是否具有管理员权限可以通过common类里面的接口由客户端查询避免不必要的错误返回。
Admin类的操作不涉及任何客户端存储因此不需要lastChange来保护。
#### get
请求地址:`/api/admin/get`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
2021-01-29 22:10:56 +08:00
返回参数一个json返回当前用户列表即返回user表中的name和isAdmin字段组成的数组。需要注意的是isAdmin会转换成bool再传输。
2021-01-29 11:10:25 +08:00
#### add
请求地址:`/api/admin/add`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|username|string|新的用户名|
2021-01-30 21:27:36 +08:00
返回参数一个json返回新建用户的对应条目与get接口返回字段一致。创建成功的用户具有一个随机的密码并且默认非管理员。创建失败返回null
2021-01-29 11:10:25 +08:00
#### update
请求地址:`/api/admin/update`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|username|string|用户名,此处仅仅是确认用户名,用户名是不能修改的|
|password|string|新的明文密码|
|isAdmin|bool|是否是管理员|
返回参数一个bool表示是否修改成功
2021-02-01 20:34:40 +08:00
除去tokenusername这2项用来鉴别的条目外其余的条目均为可选项提供则更新不提供则不更新。
2021-01-29 11:10:25 +08:00
#### delete
请求地址:`/api/admin/delete`
请求参数:
|参数名|参数类型|参数解释|
|:---|:---|:---|
|token|string|用于用户鉴权的字符串|
|username|string|用户名|
返回参数一个bool表示是否删除成功
2021-01-25 20:42:06 +08:00
## 事件循环规则字符串
事件循环规则字符串 是一串用于描述当前事件循环规则的字符串通过解析字符串可以计算出整个时间序列。本字符串借鉴了ics设计但与ics设计毫无相似之处。
一个事件循环规则字符串的格式是`[rules]-[stop]`,其中,`[rules]`是循环规则。而`[stop]`是循环停止规则。
### 循环规则
2021-01-29 11:10:25 +08:00
循环规则中,例如每年循环,究竟循环哪一天,每月循环,`x``y`的数值为多少,则根据向服务器请求的事件开始时间戳和时区相对偏移来自动计算。
2021-01-25 20:42:06 +08:00
#### 按年
2021-01-29 11:10:25 +08:00
格式:`Y[R|F][span]`
2021-01-25 20:42:06 +08:00
2021-01-29 11:10:25 +08:00
每间隔`[span]`年在同样的月份和日期进行循环。`[S|R]`则表示在严格模式Strict mode和粗略模式Rough mode中的选择。
假设在某个闰年在2月29日设置3年循环一次若选择严格模式则实际上是12年循环一次不考虑400年非闰也就是不存在的日子则无视。而选择粗略模式则将会在不存在的日子将事件设置在2月28日。
2021-01-25 20:42:06 +08:00
#### 按月
按月有4种格式
2021-01-29 11:10:25 +08:00
* 每月第`x`天:`M[R|F]A[span]`
* 每月倒数第`x`天:`M[R|F]B[span]`
* 每月第`x`个星期`y``M[R|F]C[span]`
* 每月倒数第`x`个星期`y``M[R|F]D[span]`
2021-01-25 20:42:06 +08:00
2021-01-29 11:10:25 +08:00
`[span]`表示每隔多少个月处理一次此类事件。 需要注意相关数字的钳制,此种类型的事件循环也是算力消耗最大的。
`[S|R]`则表示在严格模式Strict mode和粗略模式Rough mode中的选择。
同理,使用严格模式,对于不存在的日子即视为不存在。若选择粗略模式,将会按照可能的情况,将事件放在一个月的开始或结束,或者一个月开始或结束的某个星期。
2021-01-25 20:42:06 +08:00
#### 按周
格式:`W[T|F][T|F][T|F][T|F][T|F][T|F][T|F][span]`
`[span]`表示每隔多少个周处理一次此类事件。`[T|F]`表示从T和F中选择一个写入共7个表示从星期一到星期日是否循环这个事件。
#### 按日
格式:`D[span]`
每间隔`[span]`天进行循环。
### 循环停止规则
#### 永远
格式:`F`
表示这个事件永远持续下去
#### 指定时间
格式:`D[timestamp]`
在指定时间停止下来,实际上就是手动指定了`ccn_loopDateTimeEnd``[timestamp]`是结束时间的UNIX时间戳。
#### 指定次数
格式:`T[times]`
在指定次数后停止下来。`[times]`是次数为非0正整数。