2022 暑期项目:打卡签到系统

Table of Contents

Brief introduction

完成后端 API,实现基本的打卡签到功能。

打卡签到的形式是:用户在登录以后,进行打卡。每条打卡记录包含:

  1. 打卡 id
  2. 用户 id
  3. 打卡包含图片张数。例如,0表示纯文字签到,1表示该条签到包含一张图片,2表示该条签到包含两张图片
  4. 时间戳
  5. 打卡的文字内容
  6. 打卡图片的地址。如果有多张图片,用逗号分隔

Week 1

规划项目,基本确定初步需求。

做了图片和 base64 互转的 Service(后来证明毫无用处TAT),一部分代码作为框架用在了之后的服务中。

具体的阶段性内容见 用 Base64 上传存储图片

Week 2 & 3

集成 OpenAPI,具体参见的是廖雪峰的教程

简单来说,就是在pom.xml中引入org.springdoc:springdoc-openapi-ui:1.4.0。运行时访问后端 URL 下swagger-ui.html即可。

对 API 的描述信息用@Operation(summary = "XXX"),对参数的描述信息用@Parameter(description = "XXX")

在用户鉴权方面属于是天真了,被 leader 狠狠地批评了。uid这种信息不该由前端给,而是由鉴权层注入,以防止抓包改包。

Springboot 中命名一律小驼峰。

以及,最开始想的是数据库存 base64 字符串,后来想的是 blob 或者 mediumblob,但是吧,还是太天真了。

处理代码感觉还真比较麻烦。其实,我从来没用过在数据库中以二进制存储图片的做法。我们用得更多的是存储图片的路径,实际图片是在磁盘上保存的 (图片二进制放到数据库,把数据库的负担弄重了)。

据我了解,互联网环境中,大访问量,数据库速度和性能方面很重要。一般在数据库存储图片的做法比较少,更多的是将图片路径存储在数据库中,展示图片的时候只需要连接磁盘路径把图片载入进来即可。因为图片是属于大字段。一张图片可能 1m 到几 m。

有个原则:图片尽量不要存储在数据库中 (是指不要二进制形式保存到字段,而只保存图片的路径)。这样的大字段数据会加重数据库的负担,拖慢数据库。在大并发访问的情况下很重要。这是一个经验。去看看 dba 对数据库性能调优方面的分析都能得到这个答案的:就是图片不要存储在数据库中。

就像这个规则一样:文章分为标题、作者、添加时间、更新时间、文章内容、文章关键字

文章内容一般是比较长的。经常使用 text 字段去存储。文章的内容就属于大字段。一般文章内容可以拆分到单独一个表中去。不要与文章信息存储在一张表里面。

我理解的原理是:mysql 中一张表的数据是全部在一个数据文件中的。如果大字段的数据也存储在里面。程序展示列表,比如文章列表。这个时候根本不需要展示文章内容的。但是仍然会影响速度,数据库查找数据其实就是扫描那个数据文件,文件容量越小,速度就会越快 (为什么单表的容量在 1g-2g 的时候基本上要分表了)。拆分出去到一张单独的表,就是单独的文件了。我觉得,举一反三,相互独立,分离的思想不仅在系统开发中用到,在现实生活中经常存在的。相互混合,就会造成相互影响。小巧,简洁是一种思想。

引自这篇文章。虽然是 13 年的文章,但是很有学习价值。


然后一个比较现实的问题是,没有太合适的图床服务。我的替代方案是用自己那台部署了 NextCloud 的服务器。NextCloud 支持 Webdav 方式操作文件,用PUT上传文件,DELETE删除就行了。

但是两个问题:

  1. 产生的图片是需要登录才能查看的,如何产生图片直链?
  2. 上传需要指定文件名,有什么要求?

产生图片直链用的是 Sharing Path,在 NextCloud 上直接下载插件就可以了。开个专门用于此服务的账号,设置共享的文件夹目录,就可以直接产生直链了。

最终形成的直链就是https://{Your cloud url}/apps/sharingpath/{Account name}/{Shared directory}/XXX.png

这个没有做文件格式的限定。我上传.jar文件还是用这个以后再 ssh 跑wget 完成的(

至于文件名,我是用用户 id + 时间戳的方式生成的,保证独一无二就行了。当然,后缀名是根据文件的十六进制头判断的qwq

后端上传的文件大小可以在application.yml中限定,几个 URL 以及诸如 Basic Auth 信息也是写在里面,再注入的。


向数据库中存数据,id 就不要手动生成了。在高并发的情况下,两个创建请求可能拿到相同的 id,然后其中一个创建失败。所以用 mybatis 自增就可以了。

最后一部分就是打包部署了。我用的是 Docker,见 用 Docker 部署 Java 后端项目;当然也可以screen后台跑,但是这样做应该要配置 nginx,而 Docker 部署的话就不需要(因为端口映射是设定好的)。

Week 4

小修小补,首先是加入了管理员审核、删除打卡签到的功能,逆着来删除图片就DELETE就可以了,再删除数据库中的内容。

其次是发现管理员因为不是普通用户所以不能打卡(

把管理员打卡的权限打开就行了。


删除的时候发现用获取数据库实体的时候,除了打卡 id(自增主键)以外,数据全为 null。问了下 leader,原来是因为开了驼峰下划线自动转换(mybatis map-underscore-to-camel-case),实体类要用小驼峰,数据表用下划线。

Extra

加入了管理员的两个功能:获取打卡条数和获取所有打卡。

这都好说,小意思。获取打卡条数,综合网上的资料,用的是select count(id) from markcount(主键)应该是效率最高的方式。

主要是之前忽视了 OpenAPI 的相应数据的结构,所以得再加上。

样例:

@Operation(summary = "管理员获取所有打卡签到", responses = {
    @ApiResponse(responseCode = "200", description = "成功获取所有打卡", content = @Content(
        mediaType = "application/json",
        array = @ArraySchema(schema = @Schema(implementation = Mark.class))
    ))
})

对于得到诸如Integer等的 API 就只需要:

@Operation(summary = "管理员获取打卡条数", responses = {
    @ApiResponse(responseCode = "200", description = "成功获取打卡条数", content = @Content(schema = @Schema(implementation = Integer.class))),
})

Summary

总的来说,在后端开发方面还是很稚嫩、很天真,尚需研究。

项目过程中,经常熬夜到一点、两点,但是能看到从无到有构建的后端系统,仍然是非常令人开心的事情。

Share