古文字识别助手与众包平台——项目博客六
背景:
本项目中后端使用了两个基本的数据库进行数据的存储,分别是mongodb和mysql,两个数据库均存于云端服务器。因此此博客记录一下数据库的设计。
实现小程序端获取图片并描绘痕迹提交评分的功能;同时实现排行榜的功能。
MySQL的设计:
mysql分了三个主要的表,分别是用户user表,源图片source表以及上传图片upload表。为了便于统计表,所以每个表都设计了AUTO_INCREMENT的id字段。user表中的openid是绑定在小程序上的用户唯一标识,其他的就是一些从微信接口获取到的基本信息。source表主要是存储源图片的位置,源图片经过前期的处理,已经全部转化为200*200px的图片,总计1145张,通过后端调度算法返回给前端进行描绘并上传。imageUrl字段是图片的在线地址,存储于nginx服务器上。upload即为用户上传的图片的信息。
下面是创建数据库的ddl语句:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`openid` varchar(255) UNIQUE ,
`nickname` varchar(255) DEFAULT NULL,
`sex` int DEFAULT NULL ,
`headimgurl` varchar(255) DEFAULT NULL ,
`province` varchar(255) DEFAULT NULL ,
`score` int DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `source`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`imageUrl` varchar(255),
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `upload`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(255) NOT NULL,
`openid` varchar(255) ,
`time` datetime ,
`score` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
FOREIGN KEY (`openid`) REFERENCES `user`(`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
其中FOREIGN KEY (openid) REFERENCES user(openid)给upload的openid设置了外键,避免数据错误。
mongodb的设计:
mongodb同样是分了三个表,该数据库主要是负责内容管理部分的存储,由于nosql数据库在存储非结构化数据方面的优势,所以项目中的社区部分采用的是nosql存储。包括帖子post、一级评论comment以及一个用于实现mongo自增id的incr表。
其中incr表的作用在第7篇博客中有写到,https://blog.csdn.net/weixin_45774350/article/details/124769587
以下是post对应的实体类的定义:
package com.example.guke.entity;
import com.example.guke.annotation.AutoDec;
import com.example.guke.annotation.AutoInc;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import javax.validation.constraints.NotBlank;
import java.util.Date;
import java.util.List;
/**
* @program: GuKe
* @description: 社区的帖子类, 内含多个回复,类似于百度贴吧的形式
* @author: NiuYiq
* @date: 2022-04-04 16:30
**/
@Data
@Document(value = "post")
public class Post
{
@AutoInc
private long postId; // 帖子的id
private String owner; // 帖子的发起人的所有者 --- openid
@NotBlank
private String title; // 帖子的标题
private Date time; // 发布时间
// private List<CommentToPost> comments; // 用户跟帖
@AutoDec
private int commentCount; // 评论数量
private List<String> likes; // 由于使用的是nosql,所以这里的like不需要再分一个表出去了
// private List<String> annexes; // 博客提供的附件
}
一级评论的定义:
@Data
@Document(value = "commentToPost")
public class CommentToPost
{
@AutoInc
private long C2Pid; // 评论的id
private String owner; // 评论的用户的openid User.openid
private String content; // 用户评论的正文内容
private long belong; // 属于哪一条博客的评论 Post.id
private List<CommentToComment> comments; // 评论下的评论列表
private Date time; // 发布时间
private List<String> likes; // 点赞列表,存的是点赞用户的openid
}
二级评论的定义:
@Data
public class CommentToComment
{
private long C2Pid; // 对应的一级评论的id
private long C2Cid; // 二级评论自身的id
private String owner; // 评论的用户的openid User.openid
private String content; // 评论的正文
private String replyTo; // 回复的哪一个人 默认为回复的层主
private Date time; // 发布时间
}
在mongo数据库中,二级评论不再分出一个表,而是直接存储在一级评论中,当成数组类型进行存储。所以在存取非常频繁的情况下,nosql的性能比多表查询的关系型数据库更好。
获取图片,提交图片并评分:
获取图片:
后端获取图片的接口:
获取的结果格式:
这部分的逻辑,是之前我、后端、算法三位同学进行讨论得到的。后端将会整合所有的待描绘的图片,并按照一定的逻辑推荐给用户。用户描绘之后,提交描绘笔迹,交给后台。后端用算法同学提供的打分算法,给出用户的分数,由此可以积分。根据用户的积分,可以得到一个排行榜,这也是证明我们用户量的一个证据。
在之前,画板部分的背景图片都是静态的那张图片。现在给出了接口,就需要获取了。首先在draw.js中准备数据:
自然,要给出一个默认值。然后在wxml文件对应地方修改:
然后就需要封装方法了。这个还是比较容易的,只需要使用wx.request获取即可,注意仍然使用Promise格式:
然后在onShow中调用:
这样,在加载完成后的效果如下:
提交图片
提交图片的接口:
起初,我打算跟之前的方法一样,使用wx.request来提交图片文件。但与之前不同,按照之前的逻辑封装完毕之后,报了如下错误:
这是多部分文件没有边界的错误。与后端同学交流后,仍然没有解决问题。后来,查阅微信官方文档,发现了这个接口:
wx.uploadFile使用post请求,将本地资源上传到服务器。如果使用我自己封装的post方法有问题的话, 使用它应该就没有问题了。因此,使用wx.uploadFile重新封装方法:
通过name属性设置上传文件的名字,此外id、header都设置好,这样就封装了Promise格式的上传图片方法。然后需要封装一个给按钮调用的方法,在这个方法中应该调用uploadImage方法。
新的方法的逻辑与之前保存图片到本地的逻辑基本一样,需要获取画布像素数据,如果用户点击取消或者中间出现了问题,能够恢复原来的样子。此外,在提交成功后,需要获取新的背景图,并且清空画布。不同的是,在最终需要调用的是uploadImage,而不是保存到本地。具体实现如下:
按照Promise实现链式调用,逻辑还是非常清晰的。
然后,在页面中加入一个按钮:
效果:
进行测试,点击按钮时,发现出错。查找源头,发现错误在这里:
问题在于,获取到的obj.success是undefined,而obj是正常的。这就很奇怪了,这个问题我思考了很久。
后来发现,obj获取到的是json字符串格式,并不是json格式,那么obj.success自然是undefined。具体原因在官方文档中也没有体现,所以我没有查出为什么在这里不是json格式。总之,需要使用JSON.parse解码为json:
排行榜实现:
组件准备
之前的设计里面,在my页面,会有这个按钮:
点击它,可以前往排行榜页面,查看当前的用户积分排行榜。因此,我新设计一个rank页面,用来显示排行榜:
在其中,自然需要通过一个个小组件展示每一项。因此,还需要在Components文件夹下新建一个组件,我称之为RankItem组件:
至此,需要显示的组件已经建立完毕。根据后端同学给出的接口,数据内容如下:
通过接口可以获取到rank数组,其中每一项都是个对象。把对象的数据传给RankItem组件,并在rank页面中引用RankItem组件,即可显示排行榜了。
代码实现:
首先来到RankItem里面,在properties中写好需要父组件传入的数据:
其中要着重利用的就是nickname、headimgurl以及score了。图中没有显示的pos,表示排名,可以在遍历rank数组时传入index+1即可。
然后搭建RankItem组件效果:
RankItem组件的封装还是比较容易的,毕竟不涉及函数。然后,在rank页面中引入RankItem组件。在rank.json中配置:
接下来就需要准备发送网络请求获取rank数组的方法了。先在rank.js中准备数据:
然后封装方法:
在onShow中调用:
这样就能在页面显示的时候获取rankList了。然后,在wxml文件中调用RankItem组件:
至此,页面实现完成。
最后,需要在my页面中,点击“查看当前排行”可以进入rank页面。所以,给它添加一个点击事件的回调函数:
函数实现如下:
至此,所有代码均已实现。
实现效果
效果完成。