开发者学堂课程【玩转EMAS Serverless精品课-疫苗预约小程序:实战:小程序疫苗预约 - 框架搭建 & 数据库管理】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/926/detail/14578
实战:小程序疫苗预约 - 框架搭建 & 数据库管理
内容介绍:
一、小程序疫苗预约项目介绍
二、业务场景设计
三、数据库设计
四、数据操作命令学习
五、云数据库控制台功能介绍
一、小程序疫苗预约项目介绍
1.背景:
最近两年新冠疫情在全球感染人数接近3亿,国内疫苗接种人数12亿人左右,疫苗接种剂次达到26亿。在这样一个背景下,如果做一个疫苗预约的小程序,让人们更加有序的去接种疫苗(疫苗预约小程序不会对接医院真实的库存记录,只是模拟的数据)。
2.疫苗预约小程序的功能页面
主界面:
展示的是疫苗列表,这里不光是接种新冠疫苗,还可以接种更多的疫苗
用户打开小程序后将浏览到这样一个列表,以及每个疫苗的子类型,当用户选择某个疫苗后,进入第二级子页面。
接种点疫苗:
该页面展示疫苗的详情信息,如疫苗简介、接种对象、接种地点及该接种地点可以接种的剂次。当用户预约了该地点的疫苗接种,相应的剂次会减少一剂。
预约单列表:
该页面展示的是个人详情,包括用户头像、昵称、用户已预约的疫苗、疫苗数量及已完成接种的疫苗数量。该页面还有疫苗记录表,展示了该用户总共预约的疫苗数及预约地点和预约的状态(预约状态包括:已预约、以完成接种)
完成接种:
该界面是疫苗接种详情,对于该疫苗是否已完成接种或者是否要取消接种的操作可以在该页面完成。
3.疫苗预约小程序的准备工作
主要包括3个部分。第一部分是准备一个小程序开发者账号,登录支付宝开放平台,创建一个小程序以及在小程序上配置密钥,同时将该密钥配置到阿里云控制平台。第二部分是前端开发部分,需要下载安装最新版的小程序 IDE ,同时为大家准备了小程序的 demo 工程。第三部分是学会 Serverless 开发的一些技能,包括云数据库、云函数、云存储以及云调用。
4.Serverless 工作原理
Serverless 工作原理可以看为3部分,最左边是一个独立的用户,用户操作开发好的小程序,小程序界面再去调用云函数,云函数通过弹性调度,每个云函数会通过数据操作命令去请求云数据库,云数据库得到结果后将结果返回给云函数,云函数再将结果返回给小程序,小程序再将结果显示给用户。
本节最重要的工作是云数据库的开发。将基于该小程序疫苗预约的场景,去设计数据库。
二、业务场景设计
1.设计小程序之前分析疫苗预约小程序有哪些业务场景?
下图是简化的流程图:
预约人首先通过小程序预约要接种的疫苗,若预约成功,会生成一个预约单,预约单会通知预约人预约成功,预约人凭该预约单到达相应接种点完成疫苗接种。
上面完成了一整个场景的链路设计,接下来细化该设计。
2.细化
(1)用户场景:
登录注册
更新用户头像、昵称
(2)预约场景:
预约
完成接种
取消预约
查着历史预约记录
(3)疫苗场景
查看全部疫苗
获取该疫苗接种地信息
基于以上业务场景,要设计数据库支撑该业务场景
三、数据库设计
1.关系型数据库设计
矩形代表实体,其存储内容可以理解为一张表的存储信息。一共有4个实体,分别是用户、疫苗、接种点和预约单。然后勾勒出实体之间的关系,一张预约单应该写明某一个具体的用户、要接种某一个疫苗、在某一个具体的接种点。因此可以画出预约单和疫苗及接种点的关系。同时,疫苗和接种点也存在关系,某一类疫苗可以分发到多个接种点,一个接种点也可以接种多种疫苗,因此,可以设计出一张实体关系设计。
2.实体关系设计图:
3.接下来,根据该实体关系设计一个模型
补充:
每一个表代表一个实体。
有了这样一个关系型设计,就可以支撑在上一个步骤完成的所有场景设计,储存所需的数据。
这是一个关系型数据库设计,但是 EMAs Serverless 的云数据库是基于 Mongo db 的,也就是文档型数据库,文档型数据库和关系型数据库的区别就是不需要严格遵守关系型设计来具体的设计每一张表,也就是不需要像关系型数据库设计这样画出5张表。因此只需要做出4张表(后面会详细介绍):
注意:该图可知有五张实体表,因为在关系型数据库设计中,表示疫苗和接种点之间这种多对多的关系时,必须使用一张额外的关系表(疫苗库存的表),该关系表储存的是每一个疫苗在每一个接种点下的库存数量。
额外的关系表:
4.Mongo 数据库设计
MongoDB的基本了解:MongoDB 是一个文档型数据库,其里面的表可以理解为集合,集合中的数据记录叫做一条文档,就像下面的 user 表中 JSON 格式的一条文档。以 user 为例,储存了3个字段,分别是 userId、username、userAvatar,分别代表用户的 id 、用户的昵称以及用户的头像。
(1)user 表
(2)site 表(接种地点的设计)
只需要关心接种的 siteId (能够代表接种点的编号),接种点的具体接种信息(address):
{
" address":"杭州市余杭区余杭街道社区卫生服务中心",
"sitgId": 108
}
(3)vaccine 表
首先定义第一个字段 vaccineId ,代表疫苗的 id(全局唯一,代表某一种具体类型的疫苗的种类)
第二个字段是 subType ,表示疫苗的子分类
第三个字段 Type 是主类型,第四个和第五个字段是小程序第二页面要展示的信息。
第六个字段 siteList ,储存了该疫苗能够接种的接种点的信息储存(与关系型数据库中最大的不一样,这里使用文档嵌入的方式将关系直接存储到父文档中,非常方便,并且符合人为的直觉)
siteList 包含的信息:
由中括号包裹,说明其代表的是一个数组,每一个元素包含两个字段,第一个字段是 siteId ,代表的是某一个接种点(可以与第二张表联系起来),第二个字段 remainingQuantity ,是指疫苗的剩余数量(可以理解为库存),因此该文档中存储的数据可以理解为某一个疫苗的主类型的父类型,以及其接种对象,疫苗的数量,可以接种的地点列表,每个地点的 id 和该地点的库存。这样就设计完成了该表。
(4) appointment 表
appointment 主要包含了在关系型数据库设计中已经清楚的与其他三个实体都有关系,所以要存储的是用户的 id(userId ,代表预约单是哪一个用户的),vassineId(代表预约单对应的是哪一个疫苗),siteId (该预约单要到哪一个接种地点)。上面三个字段分别代表预约单与另外三个实体的关系。第四个字段是 stauts ,是一个 Boolean 值,该字段代表预约单的状态(用户接种或者取消接种),就是当前预约单是否取消接种的信息。后面还加了两个对象,vaccineId 和 site ,对应疫苗的详细详细,包含了疫苗的 id,疫苗的主类型和子类型。包含该信息是根据业务的实际场景设计的。回顾第三个界面,展示的是用户的预约单,预约单除了展示预约的疫苗和预约的地点,预约的状态是否完成预约。发现如果存储这三个主件的话,每次请求预约单信息时,会多次查表,因此为了节约查表的时间,直接以文档的形式嵌入一个子文档,通过空间换取时间的方式加速预约单的查询速度。site 文档与 vaccineId 目的一样。最后一个 date 字段,表示完成接种的日期。
{
"userId" : "2088612894348124",
"vaccineId" : "80b87192-2df9-46c4-9046-a05f7d7b7438",
"siteId" : 174,
"status" : true,
"vaccine" : {
"vaccineId": "80b87192-2df9-46c4-9046-a05f7d7b7438",
"type":"流脑疫苗",
" subType" : "A群C群脑膜炎球菌多糖疫苗"
},
"site": {
"siteId": 174,
"address" :"杭州市拱墅区康桥街道社区卫生服务中心",
}
"date" : "2022/12/25"}
}
5.围绕上面四张表的实际操作:
回到控制台,选中创建好的服务空间(EMAS2022),选中云数据库控制台,重新创建上面的表。
选中 EMAS2022 这个服务空间
然后新添加几张数据表(4张),依次创建
创建完成:
将模拟数据一键导入表中:
数据导入成功:
导入数据中的 _id 是云数据库为每一条数据生成的的记录,因此该数据不需要考虑。
另外两条分别代表接种点的地点和接种点的详细信息。
然后将疫苗信息导入,和上面的操作相同,导入成功结果:
疫苗数据中,可以忽略 _id 。
user 表没有提供模拟数据,是在实际操作小程序时完成的一个场景(用户登录或注册),当用户登录或注册时会在 user 表中插入一条数据(用户数据记录)
appointment 表(预约单)没有模拟数据,在小程序的实际操作中会生成并向该表中插入数据。
由操作步骤可知,文档型数据库实践非常方便。
四、数据操作命令学习
1.MongoDB 基础命令
(1)增:
db.users.insertOne{ ——> collection
{
name : "sue", ——> field: value
age: 26, ——> field: value
status:" pending” } ——> field: value
}
上面3个 field: value 为 document
db 表示指向的数据库实例,.user 代表要操作的这张表(这个集合),插入一条数据叫 insertOne
在控制台实际操作:
user 为空,切换到高级模式:
这里提供了一个可以编辑的命令输入框:
在该命令输入框中省略了具体输入的集合,直接写操作命令(传参用大括号)
控制台返回执行结果:
“ok”:1 表示有一条文档执行成功
insertedId 表示这个文档的 _id (系统默认)
切回普通模式,刷新:
数据插入成功
(2)改:
db. users.updateMany( ——> collection
{ age: { $lt: 18 } }, ——>update filter
{ $set: { status: "reject"}} ——>update action
)
包含两个对象,{ age: { $lt: 18 } },表示要更新文档的过滤器,以该行为例,更新 age 这个字段,做一个年龄小于18岁的筛选。
第二字段是在筛选文档后的操作,该条命令的意思是将所有 status 字段改为 reject 。
实际演示:
改命令的意思是,找到所有 userName 为 hello 的用户,将其年龄改为18
执行后:
回到普通模式,刷新,发现该数据多了一条年龄的字段:
(3)查:
db . users.find( ——> collection
{ age: { $age: 18 ] }, ——>query criteria
{ name: 1, address: 1 } ——>projection
).limit(5) ——>cursor modifier
第一个对象为查询条件
第二个是将想要的字段标记为1
limit 操作,假设有10条数据符合,通过 limit 操作可以限定返回的数量
实际演示:
注意:云数据库的操作查询与上面的例子不是完全一致的,上面的例子是 Mongo Shell 的操作查询,实际操作的是云数据库控制台的查询,更详细的操作命令可以在文档中心打开基础操作的数据库管理以及查询记录的模式,这里可以更详细的学习。其里面的命令是在端侧小程序使用的语法。
(4)删:
db , users.deleteMany( ——>collection
{ status: "reject" } ——>delete filter
)
2.MongoDB 进阶命令:aggregate(聚合管理)
当有复杂的查询,例如链表,聚合,过滤等等复杂的操作时,使用 aggregate 这个命令。
其命令为 db.集合名字.命令(这里为 db.orders.aggregate),接收的是数组,该数组的每一个元素包括要操作的步骤(可以理解为一个管道,把每一步的操作结果给到下一个步骤完成)。以第一个命令为例:$match:{status:”A”},过滤所有字段为 A 的数据记录。第二个操作为 group ,根据某一个 id 去做聚合 ,然后对 amount 这个字段求和,再赋值给 total 这个字段。
理解上面的表:
原始表叫 orders ,订单中有4条记录,每一个记录中有3条字段,包括用户的 id 、金额和状态。通过 match 命令符对于云数据库(基于 MongoDB )筛选所有 status 字段为 A 的文档,筛选出3条文档。然后通过 match 操作符得到中间结果,该结果会传递给第二个命令(group 命令),通过 group 命令对 cust_id 聚合,对 amount 字段求和,再将结果赋值给 total ,因此导出记录(这里输出两条记录)。
3.常用操作符:
(更多可以在云数据库文档中学习)
常用管理 |
解析 |
常用表达式 |
含义 |
$group |
将collection中的document分组,可用于统计结果 |
$sum |
计算总和,{$sum:1}表示返回总和×的值(即总和的数量),使用{$sum: ‘$制定字段’也能直接获取 |
$match |
过滤数据。只输出符合结果的文档 |
$avg |
求平均值 |
$ project |
修改输入文档的结构(例如重命名,增加、删除字段。创建结算结果等) |
$min |
求miin值 |
$sort |
将结果进行排序后输出 |
$max |
求max值 |
$limit |
限制管道输出的结果个数 |
$push |
将结果文档中插入值到一个数组中 |
$skip |
跳过制定致量的结果。并且返回剩下的结果 |
$first |
根据文档的排序获取第一个文档数据 |
$unwind |
将数组类型的字段进行拆分 |
$last |
同理,获取最后一个数据 |
五、云数据库控制台功能介绍
1.索引:
打开一张数据表
该数据表一共包含3个字段,其中一个字段是系统默认生成的,另外两个是自己定义的。这时点开索引,会发现每一个集合都有一个默认的索引,并且是不可删除的:
为什么要添加索引?
数据表在查询数据时基础的步骤,例如打开 find 命令查询数据:
从结果看已经将地点查询出来,但背后的查询原理是:查询时先判断是否存在 siteId 该字段的索引,如果不存在该集合只能一个一个查找(遍历查询),效率低,需要更多时间。因此我们要根据实际业务添加相应的索引记录。
接下来添加索引
先指定该索引的名字,选择索引的属性,判断真实字段的场景(不可能有两个接种点其 siteId 完全相同),所以这里选择唯一索引,然后添加索引字段(必须和文档中的索引完全一致)选择升序:
点击确定,索引创建完成:
这时再做一次查询:
如果有详细的查询时间,会发现查询速度会比之前的查询速度快很多。(查询效率可能为几百倍甚至上千倍)
2.权限:
权限管理的权限:
默认权限是仅管理员可写,也就是开发者可以自己操作数据,但是通过 SDK 实际操作时发现并没有权限,这时只需要选择创建者和管理员都可写这个权限:
点击保存,其他三张表都需要该权限管理的操作。
3.回档
点开回档发现每一天都会在上午或中午某一个不固定的时间产生数据备份,当业务发生故障时,可以选择业务回档到某一个具体的时间点(免费提供7天的回档记录)
可以以某一个集合回档
举个例子:
之前将 user 表删除了,可以选择 user 表并回档到 user_bak ,刷新发现该表已经回档完成:
4.课后:打卡任务
在你的服务空间 EMAS2022下面完成以下任务:
新建数据集合 appointment, site,user, vaccine(打卡必备)
导入疫苗模拟数据
练习数据操作命令
5.预约疫苗小程序 demo:
将 appId 、spaceId、clientSecret、endpoint 设置为自己的参数
代码为:
my.serverless=my.serverless ||new MPServerless(my,{
appId:’2021042192659673’,
spaceId:’dba4e91c-a48b-432e-8540-98475d11b016’,
clientSecret:’xxjffwjax5NIBh2rhWH4yg==’,
endpoint:’
https://api.bspapp.com
’
});
cloud.init(my.serverless);
页面的具体实现:
async onload(){
},
async changeSelected(e){-
},
async listVaccine(){
},
scrollToAlpha(e){
},
说明:
操作 serverless.db.collection(‘vaccine’) 这个列表,命令为 find ,然后是过滤条件及希望显示哪些字段。
在 vaccine 中包含了很多字段,但是在首页展示时并不需要这么多字段,因此在前端进行操作时可以将想要的字段标记为1来加速数据的传输。