项目名称:运动会管理系统
技术栈:
后端:SpringBoot,SpringMVC,MyBatis,tkmapper,Maven聚合工程等
前端:Vue.js,Element-ui等
前后端接口文档:Swagger,Swagger-ui
用户注册界面
通过用户数据校验后,则注册成功
其他用户注册,因数据库表中没有记录其信息的数据表,因此需将个人信息收录完整
时间已过,则不可再报(此处本人设计不合理,因为此处的逻辑是只要比赛开始前,都可以报)
若系统检测到当前用户为男生,则他不可以报名女子项目,反之也是如此
由于此用户刚注册并报名,其比赛还未进行,也没有结果,因此我们登录另一个所报比赛已结束的用户进行测试
赛事可按三种方式进行查询,分别是根据编号查询,根据项目名查询(模糊查询),根据赛事类别查询
成绩公布页面
接下来测试删除
题目要求:登录比赛项目,提出报名的限制要求(包括运动员所报项目,班级运动员人数),各个班级运动员报名后,登录报名情况,检查是否符合要求,为符合要求的运动员自动编号,并为比赛安排裁判员。比赛时先进行检录(运动员报到),检查实际与报名是否相符,为符合要求的运动员安排比赛,比赛后,录入成绩,并计算单项名次,取前若干名,登记领取奖品,为班级累计总分,全部比赛结束后,公布全体总分,并取前若干名。在比赛过程中和比赛结束后,可查询报名和比赛情况,并可进行打印。
用户端—主要功能页面
注册页面
:学生提供基本信息——>注册——>成为用户——>跳转:登录页面登录页面
:输入账号、密码——>登录成功——>跳转:首页页面主页页面
:
报名页面
:输入基本信息——>符合要求——>报名成功——>提示:可去报名查询页面再次核对查询页面
:报名查询,成绩查询,单项名次查询,团体总分查询,班级累计总分查询赛事公布页面
:类别、具体项目信息,赛事查询成绩公布页面
:单项名次公布,团体总分公布,班级累计总分公布用户个人信息页面
:用户基本信息管理员端—主要功能页面
登录页面
:输入账号、密码——>登录成功——>跳转:管理页面管理页面(单页应用)
:
1、用户
2、学生
3、班级
5、运动员
6、团体
8、项目
10、安排信息单元
11、比赛记录
12、奖品
13、获奖情况
15、登录日志
用户端—主要功能页面
注册页面
注册页面
:学生提供基本信息——>注册——>成为用户——>跳转:登录页面登录页面
登录页面
:输入账号、密码——>登录成功——>跳转:首页页面主页页面
主页页面
:
报名页面
报名页面
:输入基本信息——>符合要求——>报名成功——>提示:可去报名查询页面再次核对查询页面
查询页面
:报名查询,成绩查询(单项成绩查询,团体成绩查询),班级总分查询,学院总分查询赛事公布页面
赛事公布页面
:类别、具体项目信息,赛事查询成绩公布页面
成绩公布页面
:单项成绩公布,团体成绩公布,班级成绩公布用户个人信息页
用户个人信息页
:用户个人基本信息管理员端—主要功能页面
登录页面
登录页面
:输入账号、密码——>登录成功——>跳转:管理页面管理页面
管理页面(单页应用)
:
后端采用Maven聚合工程来搭建项目,从而并将项目模块化,模块之间建立相应的依赖关系
Maven聚合工程概念图
因本项目的数据库种所涉及的数据表就有15张,因此需要进行相关联的测试数据的导入。
关于测试数据的思路:
(1)首先录入一条管理员的记录,以便在项目进行过程种需要进行某种管理员功能测试时可以拿来即用
(2)再分别录入学院,班级,学院则为信息科学与工程学院和政法学院,班级为计数19H2和政法1901
(3)再录入学生表,学生则为7条测试记录数据,其中前6人参与之后比赛等其他关系表中关联数据的录入,最后1人留出,用于测试学生报名运动会的功能,因此,前6人的运动员标识需要设为1,而第7人的为0,同时应有两学院的学生中中应有各有一名女生,用于进行女子项目的报名,从而在测试报名功能中报名制定项目比赛的时候可以进行性别的测试
(4)之后也应在用户表中录入对应学生表中的前6人的信息
(5)录入裁判员表
(6)录入项目表,项目有男子100米赛跑,女子100米赛跑,男子2*200米接力赛(用于进行团体型比赛项目的测试)
(7)录入运动员表,参照学生表来写
(8)录入安排信息单元表,给男子100米赛跑,女子100米赛跑,男子2*200米接力赛安排指定时间、地点
(9)录入队伍表,可将计数19H2中两名男生用来录入到一个队伍中,例如,将周龙超与机器人2放入一个队伍
(10)录入安排表,计数19H2一男vs政法1901一 男,计数19H2一女vs政法1901一 女,计数19H2两男以团体名义安排,其中第5条记录的裁判员id为Null,因为,根据项目分析,裁判员也应该通过报名来参与到赛事安排表中,因此,此条记录用于测试裁判员的报名
(11)录入比赛记录表,计数19H2一男vs政法1901一 男 比赛成绩录入(机器人2,机器人4)
补充说明:
男子100米赛跑:机器人2 vs 机器人4 ----------结束
女子100米赛跑:机器人3 vs 机器人5 --------未开始
男子2x200米接力赛:(周龙超+机器人2)vs ? --------结束
男子跳远:机器人2
4.2.1 admin表
4.2.2 college表
(学院表)
4.2.3 class表
4.2.4 student表
(学生表)
4.2.5 user表
4.2.6 referee表
(裁判员表)
4.2.7 project表
4.2.8 athlete表
4.2.9 arrange_info表
(安排信息单元表)
4.2.10 team表
4.2.11 arrangement表
(安排表)
4.2.12 competition表
(比赛记录表)
首先构建Maven父工程sports-metting-server
本项目采用Maven聚合工程的方式进行创建,首先创建总父Maven工程,并删除不必要文件
首先对父工程的pom.xml文件进行如下配置:
packing设置为 pom
父工程继承继承spring-boot-starter-parent
(注:其实一开始也可以将父工程建成SpringBoot项目)
pom.xml进行上述操作后,内容如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.4.4version>
<relativePath/>
parent>
<groupId>com.zlcgroupId>
<artifactId>sports-metting-serverartifactId>
<version>2.0version>
<packaging>pompackaging>
<properties>
<java.version>1.8java.version>
properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
选择sports-metting-server,右键—New—Module (Maven工程)
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sports-metting-serverartifactId>
<groupId>com.zlcgroupId>
<version>2.0version>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>commonartifactId>
project>
lombok
:用于生成实体类的基本方法,如构造方法,Getter/Setter,toString等方法springfox-swagger2
:用于生成前后端接口文档,定义common子工程中ResultVO对象的Api说明信息<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
选择sports-metting-server,右键—New—Module (Maven工程)
lombok
:(同上)
springfox-swagger2
:(同上)
mapper-spring-boot-starter
:tkmapper的starter依赖,提供现成的单表查询方法,与数据库表逆向转为JavaBean并自动配置好Mapper文件的字段映射
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapper-spring-boot-starterartifactId>
<version>2.1.5version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
选择sports-metting-server,右键—New—Module (Maven工程)
mysql-connector-java
:mysql的JDBC驱动包spring-boot-starter
:springboot starter依赖mybatis-spring-boot-starter
:mybatis starter依赖,用于springboot整合mybatisdruid-spring-boot-starter
:德鲁伊 starter依赖,Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
<dependency>
<groupId>com.qfedugroupId>
<artifactId>beanartifactId>
<version>2.0.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.4.4version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.22version>
dependency>
选择sports-metting-server,右键—New—Module (Maven工程)
在service子工程的pom.xml,依赖common子工程和mapper子工程
java-jwt
和jjwt
:前后端分离项目中解决用户认证问题<dependency>
<groupId>com.zlcgroupId>
<artifactId>commonartifactId>
<version>2.0version>
dependency>
<dependency>
<groupId>com.zlcgroupId>
<artifactId>mapperartifactId>
<version>2.0version>
dependency>
<dependency>
<groupId>com.auth0groupId>
<artifactId>java-jwtartifactId>
<version>3.10.3version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.1version>
dependency>
选择sports-metting-server,右键—New—Module (Maven工程)
在api子工程的pom.xml,依赖service子工程
springfox-swagger-ui
:前后端接口文档swagger的可视化页面swagger-bootstrap-ui
:swagger的可视化页面的增强版管理页面spring-boot-starter-web
:spring web的 starter依赖spring-boot-starter-test
:spring test的 starter依赖junit
:Java单元测试框架依赖<dependency>
<groupId>com.zlcgroupId>
<artifactId>serviceartifactId>
<version>2.0.1version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.9.2version>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>swagger-bootstrap-uiartifactId>
<version>1.9.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<scope>testscope>
dependency>
注册各个子模块
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.4.4version>
<relativePath/>
parent>
<groupId>com.zlcgroupId>
<artifactId>sports-metting-serverartifactId>
<version>2.0version>
<modules>
<module>commonmodule>
<module>beanmodule>
<module>mappermodule>
<module>servicemodule>
<module>apimodule>
modules>
<packaging>pompackaging>
<properties>
<java.version>1.8java.version>
properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
1、首先配置mapper子工程中的数据库和mybatis的配置
application.yml
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sports?characterEncoding=utf-8
username: root
password: 123456
mybatis:
mapper-locations: classpath:mappers/*Mapper.xml
type-aliases-package: com.zlc.sports.entity
2、在api子工程中创建SpringBoot启动类,同时指定MapperScan扫描的具体包
@SpringBootApplication
@MapperScan("com.zlc.sports.dao")
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
3、启动ApiApplication
运行正常,则说明之前的配置并无较大问题,可进行下阶段的编码
建立软件包
在vo包中:
ResStatus:状态码封装类
public class ResStatus {
public static final int OK = 10000;
public static final int NO = 10001;
public static final int LOGIN_SUCCESS = 2000; //认证成功
public static final int LOGIN_FAIL_NOT = 20001; //用户未登录
public static final int LOGIN_FAIL_OVERDUE = 20002; //用户登录失效
}
ResultVO:后端传给前端的信息单元类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "响应的VO对象", description = "封装接口返回给前端的数据")
public class ResultVO {
//传递给前端的状态码
@ApiModelProperty(value = "响应状态码", dataType = "int")
private int code;
//响应给前端的信息
@ApiModelProperty(value = "响应提示信息", dataType = "string")
private String msg;
//响应给前端的数据
@ApiModelProperty(value = "响应数据", dataType = "obj")
private Object data;
}
建立软件包
建立软件包
利用tkmapper的逆向工程来初步构建Dao层,将数据库中的表映射为JavaBean并配置相关mybatis的Mapper映射配置的主要内容
使用逆向工程的方法,需进行如下步骤的配置:
(1)在mapper子工程中添加tkmapper依赖:已添加,因在bean子工程中已经引入tkmapper依赖—mapper-spring-boot-starter
,而mapper子工程又引入了bean子工程依赖
(2)在mapper子工程的general包中创建一个继承Mapper和MySqlMapper的泛型类,这样就可以使之后在dao包中自动生成的Mapper类只需对接这一个接口即可
public interface GeneralDAO<T> extends Mapper<T>, MySqlMapper<T> {
}
(4)配置mapper子工程的pom.xml
(5)执行逆向生成插件
执行完成后,即可在各个预先指定的包和目录中得到JavaBean实体类,Mapper类以及Mapper.xml配置,之后还需将entity包中的类移动到bean子工程下,并删除mapper子工程下的entity包即可
建立软件包
首先建立好各个JavaBean的接口与实现类(也可以边写边建立,看个人习惯)
建立软件包
编写Swagger配置类
本项目欲将管理员的前端页面开发为多页浏览的方式,因此,只需建立一个空文件夹,并用IDEA打开,在其中新建前端文件,并创建必要目录,进行开发即可。
本项目欲将管理员的前端页面开发为单页浏览的方式,因此可用Vue.js的脚手架进行前端的搭建(但也可以自己去写单页应用)。
由于在前后端分离项目开发中,前后端之间是通过异步交互完成数据访问的,请求是无状态的,因此不能基于session实现用户的认证。
关于session和jwt详细介绍的一篇优秀文章
基于Session的用户认证:
- 用户输入其登录信息
- 服务器验证信息是否正确,并创建一个session,然后将其存储在数据库中
- 服务器为用户生成一个sessionId,将具有sesssionId的Cookie将放置在用户浏览器中
- 在后续请求中,会根据数据库验证sessionID,如果有效,则接受请求
- 一旦用户注销应用程序,会话将在客户端和服务器端都被销毁
基于token(令牌)的用户认证:
用户输入其登录信息
服务器验证信息是否正确,并返回已签名的token
token储在客户端,例如存在local storage或cookie中
之后的HTTP请求都将token添加到请求头里
服务器解码JWT,并且如果令牌有效,则接受请求
一旦用户注销,令牌将在客户端被销毁,不需要与服务器进行交互一个关键是,令牌是无状态的。后端服务器不需要保存令牌或当前session的记录。
在进行登录验证的时候,若登录成功,则进行token的签发
前端将服务器签发的token保存到cookie中。
当前端请求访问受限资源的时候,若想成功访问,则请求头中需要携带服务器端签发的token。
当前端请求访问受限资源的时候,则根据请求路径和拦截器配置类中的预先设置进行拦截,若请求头中携带了token,则需拦截器类进行校验,校验成功才可以放行,从而进行受限资源的访问。
UserService
/**
* 用户注册,可能是学生,裁判,或另外身份,分别处理
*
* @param info
* @return
*/
public ResultVO addUserToRegister(Map<String, String> info);
UserServiceImpl
/**
* 用户注册,可能是学生,裁判,或另外身份,分别处理
*
* @param info
* @return
*/
@Override
public ResultVO addUserToRegister(Map<String, String> info) {
String type = info.get("type");
//System.out.println(type);
//若是学生注册
if (type.equals("0")) {
//首先根据学号得到学生在学生表中的主键id,从而插入用户表中的type_id字段
String studentNumber = info.get("studentNumber");
Example example = new Example(Student.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("studentNumber", studentNumber);
List<Student> students = studentMapper.selectByExample(example);
//System.out.println("students = " + students);
//如果学号正确
if (students.size() > 0) {
Student student = students.get(0);
//则构建此学生用户,并插入用户表
User user = new User();
user.setType("0");
user.setTypeId(student.getStudentId());
user.setUsername(info.get("username"));
user.setPassword(info.get("password"));
user.setEmail(info.get("email"));
user.setImg("default.png");
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
int i = userMapper.insert(user);
if (i > 0) {
return new ResultVO(ResStatus.OK, "success", null);
} else {
return new ResultVO(ResStatus.NO, "fail", null);
}
}
} else {
// 如果不是学生
//则构建此非学生用户,并插入用户表
User user = new User();
user.setType(info.get("type"));
user.setUsername(info.get("username"));
user.setPassword(info.get("password"));
user.setEmail(info.get("email"));
user.setRealname(info.get("realname"));
user.setGender(info.get("gender"));
user.setIdnumber(info.get("idnumber"));
user.setPhoneNumber(info.get("phoneNumber"));
user.setImg("default.png");
String birthday = info.get("birthday");
birthday = birthday.substring(0, 10);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date dateTime = null;
try {
dateTime = simpleDateFormat.parse(birthday);
} catch (ParseException e) {
e.printStackTrace();
}
user.setBirthday(dateTime);
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
int i = userMapper.insert(user);
if (i > 0) {
return new ResultVO(ResStatus.OK, "success", null);
} else {
return new ResultVO(ResStatus.NO, "fail", null);
}
}
return new ResultVO(ResStatus.NO, "fail", null);
}
UserController
@PostMapping("/register")
@ApiOperation("用户注册")
public ResultVO register(@RequestBody Map<String, String> info) {
return userService.addUserToRegister(info);
}
register.html
<el-form ref="form" :model="formInfo" label-width="80px">
<el-form-item label="身份选择">
<el-select v-model="formInfo.identity" placeholder="请首先声明您的身份" @change="changeIdentity">
<el-option label="学生" value="0">el-option>
<el-option label="教师" value="1">el-option>
<el-option label="裁判员" value="2">el-option>
<el-option label="校内其他工作人员" value="3">el-option>
<el-option label="非本校人员" value="4">el-option>
el-select>
el-form-item>
<el-form-item label="用户名">
<el-input v-model="formInfo.username">el-input>
el-form-item>
<el-form-item label="密码">
<el-input v-model="formInfo.password">el-input>
el-form-item>
<el-form-item label="确认密码">
<el-input v-model="formInfo.rePassword">el-input>
el-form-item>
<el-form-item label="邮箱">
<el-input v-model="formInfo.email">el-input>
el-form-item>
<template v-if="isStudent">
<el-form-item label="学号">
<el-input v-model="formInfo.studentNumber">el-input>
el-form-item>
template>
<template v-if="!isStudent">
<el-form-item label="真实姓名">
<el-input v-model="formInfo.realname">el-input>
el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="formInfo.gender">
<el-radio label="1">男el-radio>
<el-radio label="0">女el-radio>
el-radio-group>
el-form-item>
<el-form-item label="身份证号">
<el-input v-model="formInfo.idnumber">el-input>
el-form-item>
<el-form-item label="出生日期">
<el-col :span="11">
<el-date-picker type="date" placeholder="选择日期" v-model="formInfo.birthday"
style="width: 100%;">el-date-picker>
el-col>
el-form-item>
<el-form-item label="联系电话">
<el-input v-model="formInfo.phoneNumber">el-input>
el-form-item>
template>
<el-form-item>
<el-button type="primary" @click="onSubmit" class="move-right-mid">注册el-button>
<el-button @click="reWrite" class="move-right-large">重置el-button>
el-form-item>
el-form>
<script>
var baseUrl = "http://localhost:8080";
var vm = new Vue({
el: '#container',
data: {
formInfo: {
identity: '',
username: '',
password: '',
rePassword: '',
email: '',
studentNumber: '',
realname: '',
gender: '',
idnumber: '',
birthday: '',
phoneNumber: ''
},
isStudent: false
},
created: function () {
},
watch: {},
methods: {
onSubmit() {
// console.log('submit!');
console.log(this.formInfo);
//进行输入校验
//校验密码
// 发送 POST 请求
axios({
method: 'post',
url: baseUrl + '/register',
data: {
studentNumber: this.formInfo.studentNumber,
type: this.formInfo.identity,
username: this.formInfo.username,
password: this.formInfo.password,
email: this.formInfo.email,
realname: this.formInfo.realname,
gender: this.formInfo.gender,
idnumber: this.formInfo.idnumber,
birthday: this.formInfo.birthday,
phoneNumber: this.formInfo.phoneNumber
}
}).then((res)=>{
var vo = res.data;
console.log(vo);
if (vo.code == 10000) {
// console.log(vo);
window.location.href = "login.html";
}
});
},
reWrite() {
this.formInfo.identity = '';
this.formInfo.username = '';
this.formInfo.password = '';
this.formInfo.rePassword = '';
this.formInfo.email = '';
this.formInfo.studentNumber = '';
this.formInfo.realname = '';
this.formInfo.gender = '';
this.formInfo.idnumber = '';
this.formInfo.birthday = '';
this.formInfo.phoneNumber = '';
// console.log(this.form);
},
changeIdentity() {
console.log(this.formInfo.identity);
//如果是学生身份
if (this.formInfo.identity == '0') {
// console.log(this.formInfo.identity);
this.isStudent = true;
} else {
this.isStudent = false;
}
}
}
});
script>
关于此项目剩余功能的开发教程、思路、代码文档,本人在开发的同时已整理成详细md格式文档
此项目附带文件如下
因此项目的开发以及文档的编写耗费了本人大量的精力和时间,因此决定将此项目的源码和剩余文档设为付费内容,有意向的小伙伴可以前往链接下载,感谢小伙伴们的支持。
附下载地址:
https://download.csdn.net/download/m0_46360532/19853247?spm=1001.2014.3001.5501
(可加 wx:zlc777687,wx 支付优惠 5 元)