通常意义上,我们在编写对应某个资源的管理页面的时候,我们往往会面对一大类功能大致相似的需求,例如:
传统意义上的curd
带筛选条件的查询
更新某个具体的值
对某个具体的值进行更新
与回收站的交互操作
....等等一系列的操作
在此基础上,我们可能还需要对具体的权限管理系统进行相应的对接。从而完成以下操作:
资源的所有权管理
键入'/'获得帮助
考虑面对如此大量的相似需求,我们可以通过编写一系列工具库来完成。
主要页面框架
资源页面通常意义上以表格为呈现主体。其大致的页面布局为
其中,涉及以下功能:
mutation相关:
创建新Item
更新Item
回收站功能
将Item移动到回收站
单个
选中
全部
将回收站中的Item还原
单个
选中
全部
彻底删除
单个
选中
全部
对tem进行某类具体的操作
单个
选中
全部
键入'/'获得帮助
query相关:
查询条件的key化
区分回收站内外(软删除相关)
查询场景的通用配置参数
通用操作接口
对Item操作大致可以分为一下几类:
在gql层面,我们受到如下限制:
无法同时更新跨表的链接信息(relation),只允许单个修改
对于修改的值({[key]:value}
),存在如下格式要求:
对于String|Number|Boolean
类型的变量,需要构造{[key]:{set:value}}
的格式。
对于关联关系,
对于关联的一个,需要构造{[key]:{connect:{id:value.id}}}
的格式,此时value一定是ID的类型。
对于关联的多个,无法同时更新!!!。
对于Object|Array
的值,不做改变。(它们在数据库中以JSON为存储值)
故我们需要对于更新值,按照其原本的类型进行转化。
键入'/'获得帮助
以上设计基于了一个假设,即所有的realtion关联都适用graphql友好的一种格式来组织。如下:
通用查询接口
当前业务中主要存在两类查询,
一类查询为与table相关的查询
另一类查询为与Select相关的查询
table相关的查询
该类查询的本质上为分页相关的查询,即需要提供如下类型的返回值:
其输入参数,为页面的QueryForm
的输入值。同理,受gql的要求:
对于查询的值({[key]:value}
)
对于deletetime
字段来说,它仅有如下情况
非空,实际注入值为 {deletetime:{not:{equals:null}}}
空,实际注入值为{deletetime:{equals:null}}
对于string
类型,我们需要转换为{[key]:{contains:value}}
对于Ord
类型(可比较大小)
比较类型有lt|lte|gt|gte
需要转化为{[key]:{[method]:value}}
对于关联关系,
对单个的关联:需要转化为{[`${key}Id`]:{equals:value}}
对多个的关联:较为复杂(尽可能少的使用)
针对于以上情况,我们需要对query object
生成key:
该函数同时返回用来判断当前所在缓存是否为回收站的。
select相关的查询
select相关的查询通常意义上是通过select的searc字段进行对应字段的查询。本质上,由于并不需要分页,故为了避免大量数据的返回,通常限定仅仅查100条。
与mutation之间的关联
为了达到快速响应的目的,原则上,我们希望所有的mutation操作都可以于当前缓存进行交互。这种交互按使用场景,基本上可以分为以下几类:
字段更新:
单个更新,由于返回id的原因,apollo会为我们自动处理这类更新
选中更新,此时仅仅返回影响的行数,
行数满足,按照id,在字段上,进行手动更新。
行数不满足,进行message提醒,要求用户手动刷新所有。
查询条件更新,此时需要先通过readQuery拿到当前更新的行数
行数满足,对于所有的在字段上,进行手动更新。(这里存疑,可能失败)
行数不满足,进行message提醒,要求用户手动刷新所有。
item删除:按发生场景,又可以细分为:
移动到垃圾桶。
单个,选中:对于id存在的进行删除。(优化:仅仅处理非垃圾桶缓存)
查询条件:对于当前查询对应的storeFieldName所在的数组置空。
从垃圾桶收回
单个,选中:对于id存在的进行删除。(优化:仅仅处理垃圾桶缓存)
查询条件:对于当前查询对应的storeFieldName所在的数组置空。
item新增:由于通常意义上,并不确定当前新增是否可以满足当前查询条件,故此时只能依赖后台返回数据
按照上一次查询条件,重新发起请求。
强制刷新,由于上述情况并不能覆盖所有情况,故在多次复杂操作时,缓存可能更新不及时,此时,需要清空当前查询所对应的storeFieldName,并按照上一次查询条件进行查询。
键入'/'获得帮助
通常来说,强制刷新的场景往往出现在一下场景:
进行条件查询A
进行条件查询B(A,B两次查询之间存在交集a)
变更不在a中的某一项,或者新增。(此时使得更新的项恰好符合查询A)
变更结束,条件查询B符合情况(代码自动更新正确)
返回条件查询A,(代码无法自动更新,导致后端与缓存数据不一致)
键入'/'获得帮助
以上情况存在一些通用逻辑,整理如下:
基于查询条件,生成storeFieldName
(即,上一小节的convertQueryObjectKey
)
用于判断当前storeFieldName
是否在垃圾桶
这些操作,本质上需要返回记录上一次查询的结果。
其它
还有一些需要注意的点:
在回收站关闭时,需要手动刷新当前页。
每次移动回收站时,需要同时刷新回收站当前页。
键入'/'获得帮助
键入'/'获得帮助