一个简洁的小程序,可以快速,方便,高效地进行课外活动,课题研究,大作业等的组队。
- jzh:40%,后端代码和部分前端代码
- wfy:30%,界面设计和部分前端代码
- wyz:30%,负责文档,PPT的编写和部分前端代码
每个人都可以通过组队达人创建一个活动,活动可以设置为私有活动或者公共活动,可以指定截止时间和具体内容,其他人可以通过扫描二维码加入活动。
加入活动页面,可以创建自己的小组,小组可以设置人数限制,说明小组的目标,对成员的要求等等。
活动参与者可以申请加入小组,说明自己的情况,以便组长筛选组员,组长同意申请之后就成功加入了小组。
每个人可以编辑自己的具体信息,以便了解和认识其他人。
主要通过 Java 实现,支持通过 Docker 快速部署。使用微信云托管部署服务,使用腾讯云COS(或阿里云OSS)存储静态文件, 通过 Redis 实现缓存,通过 Swagger3 构建 api 文档,通过 ElasticSearch 实现活动搜索功能。
- SpringBoot 2.7.5
- Docker & Docker-compose
- MySQL 5.7.35
- Redis 3.0.354
- MybatisPlus
- Hutool
- JWT
- Quartz
- OkHttp
- qcloud.cos / aliyun.oss
- Swagger3
- ElasticSearch 7.17.3
主要由 JavaScript 实现。
微信小程序端,通过微信官方API和 Vant Weapp 框架搭建UI。
- WeChat API
- Vant Weapp
浏览器端,通过 npm,axios 等进行构建。
1、docker 部署问题
如果不使用 docker-compose 部署服务,只通过 docker 进行部署的话,由于有多个容器需要部署且容器间需要通信,为了正常通信,需要建立桥接网络,通过创建一个 network(默认为 bridge 模式),将两个容器的网络均指定为该 network, 即可让两个容器互通。
对于 mysql 服务所在容器,假如容器名定为 mysqldb,那么在 java 后端配置文件里对应的 mysql 服务的 url 应该指定为 jdbc:mysql://mysqldb/xxxxxxx
。这样才能顺利连通 mysql 服务。
当然,假如使用 docker-compose 则没有这个问题了。
2、前后端交互认证问题
微信小程序的认证流程大致是这样的:
P1: 在小程序端向用户申请获得用户信息的权限,用户同意后,微信服务器返回一个 code 授权码,小程序端携带该授权码访问后端认证接口。
P2: 后端通过该授权码访问微信提供的接口服务获得该用户的 openId,接着将 openId 通过 Jwt 进行加密生成 token,放入请求头中返回给小程序端。
P3: 小程序端将 token 存入本地,以后每次需要用户认证的请求都带上该请求头。后端尝试获取请求头中 token,如果 token 不存在或者 token 过期则向小程序端返回 token 失效的错误码。
P4: 如果小程序端收到 token 失效错误码后,则进行 P1 操作。
认证流程图如下所示:
3、关于后端返回的 json 数据
对于一个采用驼峰命名法命名的变量,比如 userId,转换后返回前端的 json 属性名是 userId,没有问题。
但是当变量名为 uId时,转换后则变为 uid,这就产生了问题。我还测试了其它一些变量,如下:
# userId
{"code":200,"msg":"ok","obj":{"userId":"hello"}}
# uId
{"code":200,"msg":"ok","obj":{"uid":"hello"}}
# Id
{"code":200,"msg":"ok","obj":{"id":"hello"}}
# uuId
{"code":200,"msg":"ok","obj":{"uuId":"hello"}}
可见当为 uId 和 Id 时,都会出现问题。
一般可以考虑在后端变量命名时,不让第二个字符大写,或者采用 @JsonProperty("uId")
进行解决。
4. 建立适当的索引
有些索引建立了反而会降低效率,比如写多于读的场景:在本项目中,对于一个加入小组的请求,写入到 t_req 表之后, 撤销请求操作和处理请求操作都可能是较为频繁的,也就是增删行操作较多,可能导致索引频繁地进行重建。
这里对 t_uat 表索引的设计进行说明。
当用户获取自己所加入的活动或小组时,需要根据用户 id 进行查询,所以可以建立用户 id 的索引。
当一个用户申请加入某个小组时,需要查询用户是否已经加入了该小组,但由于用户不能加入同一活动下的其它小组,所以只需要根据用户 id,活动 id 进行查询即可,可以用这两者建立一个联合索引:CREATE INDEX t_uat_idx_join ON t_uat(a_id, u_id);
同时,有了这个索引,当要查询某个活动所含小组时,无需再建立 活动 id 的索引,因为 mysql 当前使用默认引擎为 innodb,索引底层数据结构为 B+ 树,根据 B+ 树的最左匹配原则,使用该索引可以命中活动 id 对应的条目。
- t_user:用户表,记录用户基本信息,其中,用户 id 为主键。
- t_activity:活动表,记录活动基本信息,其中,活动 id 为主键,活动创始人 id 外键约束于 t_user。
- t_team:小组表,记录小组基本信息,其中,小组 id 为主键,隶属活动 id 外键约束于 t_activity,小组创始人 id 外键约束于 t_user。
- t_uat:参与情况表,记录用户加入小组(活动)情况,其中,id 为主键,用户 id 外键约束于 t_user,活动 id 外键约束于 t_activity,小组 id 外键约束于 t_team。
- t_req:请求表,记录用户所发送请求,其中,请求 id 为主键,发送者 id 和 目标者 id 外键约束于 t_user,请求对应活动 id 和小组 id 外键约束于 t_activity 和 t_team。
本项目对用户,活动,小组,请求,参与情况等条目均做了缓存优化。
以用户为例,将用户信息进行缓存,键为 “user:{uId}”,值为经过 Json 序列化的对象字串。
用户每次获取个人信息,都会首先访问 Redis 缓存,如果命中缓存,则将值反序列化后得到对象信息,直接返回。如果用户更新个人信息,则直接删除缓存。
本项目针对活动实现了搜索功能,用户可以根据活动名进行模糊搜索活动。
索引名设置为 activity,文档 _id 设置为活动 id。
在进行新增活动时,同时将结果插入到 ElasticSearch 中,删除活动时则同时删除 ElasticSearch。而在更新活动时,仅当活动名发生更改时,对 ElasticSearch 中的对应活动进行更改。查询活动时,将查询所有活动名中含有对应查询字段的活动。
将 application.pub.yaml
重命名为 application.yaml
,然后开始编辑。(“xxxxxx...”为需编辑内容)
需要配置的内容:
- 微信小程序的 appId 和 appSecret
- 数据库用户和密码
- 对象存储相关字段
- 本地静态资源存储位置
- token 密钥和过期时间(单位:s)
- redis 基本配置
- elasticsearch 基本配置
- 是否开启 swagger (生产环境下建议关闭)
执行 init.sql
初始化数据库,之后即可开始开发。
mini-program
为小程序开发目录,后端未使用微信云托管,需通过该文件夹开发。
mini-program-cloud
也为小程序开发目录,若后端使用了微信云托管,需通过该文件夹开发。
cd mini-program # or cd mini-program-cloud
npm install
接着,如果在 mini-program
开发,编辑 app.js
,将“xxxxxx”替换为后端服务地址即可。
App({
globalData: {
baseUrl: "xxxxxx",
},
})
如果在 mini-program-cloud
开发,编辑 app.js
如下(先将 app.pub.js 重命名为 app.js):
that.cloud = new wx.cloud.Cloud({
resourceAppid: 'xxxxxxxxxxxxxxxxxx', // 微信云托管环境所属账号,服务商appid、公众号或小程序appid
resourceEnv: 'xxxxxxxxxxxxxxxxxx', // 微信云托管的环境ID
})
注:后端 api 地址为 localhost:端口号/swagger-ui/index.html
将 docker-compose-pub.yaml
重命名为 docker-compose.yaml
,然后编辑数据库密码。
创建 docker-compose.yaml
所涉及到的挂载卷目录,并将 init.sql
放到 /docker/mysql/init
目录下。
接着,编辑配置文件中 mysql 地址如下:
url: jdbc:mysql://mysqldb/form_team_talent?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
最后,执行 ./publish.sh -u
(-u 字段是为了重新生成 jar包)。
在数据库界面创建账号(对应数据库用户和密码),进入数据库管理界面,运行数据库初始化脚本 init.sql
即可。
在数据库界面获取数据库内网地址,然后编辑配置文件中 mysql 地址如下:
url: jdbc:mysql://数据库内网地址/form_team_talent?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
接着进行 ElasticSearch 和 Redis 服务的部署。
最后,重新生成 jar 包,进入服务界面,创建服务,将jar包上传并部署。
- 可通过 thunder client 进行测试,将
test/api_test_ftt_thunderclient.json
导入即可 - 可通过 postman 进行测试,将
test/api_test_ftt_postman.json
导入即可
主页
个人页
管理页
活动页
小组页
请求
发送加入小组的请求:
处理请求:
Github: akynazh
Mail: akynazh@gmail.com
感谢 JetBrains 对该项目的大力支持!