这个作业属于哪个课程 | 软件工程-23年春季学期 |
---|---|
这个作业要求在哪里 | 结对第二次作业–编程实现 |
结对学号 | 222000202陈仕燊、222000212黄志腾 |
这个作业的目标 | 1、fork仓库,和伙伴商讨协作细节等。 2、编程实现。 3、撰写博客 |
其他参考文献 | CSDN、博客园 |
目录
- 一、Gitcode仓库链接和代码规范链接
- 1.1 Gitcode仓库地址
- 1.2 代码规范地址
- 1.3 项目部署地址
- 二、PSP表格
- 三、项目的访问链接
- 四、项目成果展示
- 五、结对讨论过程描述
- 六、设计实现过程
- 七、代码说明
- 八、心路历程和收获
- 九、 结对队友互评
一、Git仓库链接和代码规范链接
1.1 Gitcode仓库地址
1.2 代码规范地址
1.3 项目部署地址
二、PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 2160 | 2270 |
• Analysis | • 需求分析(包括学习新技术) | 360 | 400 |
• Design Spec | • 生成设计文档 | 60 | 60 |
• Design Review | • 设计复审 | 45 | 40 |
• Coding Standard | • 代码规范(为目前的开发制定合适的规范) | 45 | 30 |
• Design | • 具体设计 | 90 | 60 |
• Coding | • 具体编码 | 1320 | 1500 |
• Code Review | • 代码复审 | 60 | 60 |
• Test | • 测试(接口测试、修改代码、提交修改) | 180 | 240 |
Reporting | 报告 | 140 | 70 |
• Test and use Repor | • 接口、使用报告 | 60 | 10 |
• Size Measurement | • 计算工作量 | 20 | 10 |
• Postmortem & Process Improved Plan | • 事后总结,并提出过程改进计划 | 60 | 60 |
合计 | 650 | 900 |
三、项目的访问链接
四、项目成果展示
-
首页:其中是含有四种图片的轮播图,循环播放赛程期间的热点图片
-
选手排名:其中两种表格,分别存放选手的Rank、Matches、Aces信息。
-
每日赛程:里面放在不同日期、不同场地、不同比赛的信息
-
详细赛况:放在特定比赛的详细信息,并且可以下拉选择不同比赛场次
-
晋级图:里面存放晋级路线图,并高亮显示获胜者
-
种子选手:展示未来最具潜力的选手排名。
-
了解更多:放在有关澳大利亚历史的时间线并配上两张图片,有李娜夺冠照片哦
五、结对讨论过程描述
前期任务分工
css同学有过一定的后端开发经验,并且对项目部署和数据库比较熟悉,所以由css同学负责后端接口设计和编写、数据库设计。因为css同学主动选择了后端,因此hzt同学只能选择前端了,虽然开发经验较少,但是审美比css同学好,所以完全能够胜任前端的任务。
博客和学习资源的分享:
问题交流和解决
六、设计实现过程
6.1 功能结构图
6.2 数据库设计
6.3 设计描述
前端实现描述:
- 采用技术:Vue3框架、axios库、element-ui插件、vue-country-flag插件、router路由
- 设计思路
- 页面设计:前端界面设计是利用Vue框架,但是没有使用vue-cli的部署脚手架,基本结构和前端开发的三件套还是一样的。 页面基本分为主页、选手排名、详细赛况、每日赛程、晋级图。
- 后端接口数据获取:利用axios库通过get请求并获取后端接口的数据,并把它存在data的一个数组里面。
- 导航栏设计:利用keep-alive和component来实现,通过给每个导航添加监听按钮来改变条件,从而实现导航栏的效果。
- 首页和关于更多界面设计:首页是一个轮播图,关于更多里面是一个时间线,实现方式是通过element-ui插件,利用里面的走马灯以及Timeline时间线的组件。element-ui网站。
- 选手排名界面设计:在用axios分别获取男、女选手数据并存在data里面后,用两个table和v-for遍历数据并输出显示实现的。
- 每日赛程界面设计:赛程的日期左右移动是通过标签的部分隐藏并编写两个函数来控制div标签的左右移动来实现的,而后给每个日期添加一个按钮监听时间,最后下方的每日赛程具体信息显示是用v-show和更换条件做到点击不同日期显示不同的数据。
- 晋级图界面设计:晋级图同赛程日期一样可以进行左右移动,两者实现方式的一致的。
- 详细赛况界面设计:以上界面的设计都是通过前后端分离、前端获取后端数据实现的,由于时间的原因,详细赛况界面是唯一用存前端设计的,需要事先把部分数据放到data里面。界面设计里用select标签去一个下拉选项,并添加按钮监听事件来实现不同场次的数据显示。
后端实现描述:
- 采用技术: Tomcat(项目部署)、MySQL(数据库)、MyBatis(持久层框架)、JavaServlet(服务端技术)
- 设计思路:
- 数据库的数据获取: 数据库的数据获取:第一次结对作业已经将json数据文件解析为Java Bean对象,因此可以利用上次作业的代码获取数据并转换为对象list,然后利用MyBatis将list中的对象一个一个放入数据库中
- 流程图展示:
- 后端接口数据处理: 后端接口数据处理:这部分主要处理三个部分的数据——选手、赛程、晋级图。对于选手数据,根据前端传回的参数来返回满足指定条件的选手结果集,并把结果集转换为json字符串传给前端。对于赛程数据,因为数据库中的match表取出来的数据不能直接返回给前端使用,所以创建AOMatch类专门用于前端的数据处理,AOMatch类会根据Match类的数据来进行重新处理和封装某场比赛数据。按照比赛地点来对赛程进行分类并转换成HashMap,最后将HashMap转换为json字符串返回给前端。对于晋级图数据,同样采用AOMatch类来封装某场比赛数据,但是还要按照比赛轮次(1st Round, 2nd Round…)的顺序来封装成二维链表数据,最后转换为json字符串返回给前端。
- 流程图展示:
6.4 遇到的问题及解决方式
前端遇到的问题及解决方式:
- 1.npm报错问题:由于前端是用Vue开发的,先前我是没有接触过这个框架,所以需要先去安装并配置npm,但是在过程中我使用命令修改文件保存目录就碰到npm报错 operation not permitted, open...问题。
- 解决方式:.将npm_global和node_cache两个文件的普通用户权限打开。附上具体解决方式参考、 Vue3安装及环境变量配置以及 使用vscode运行vue项目。一开始我是使用vscode,后面在队友劝说下改成了IDEA,两者都可以用。
- 2.不一样的npm报错问题:设计界面时候会用到一些插件来辅助我们设计,就需要去下载,可是用npm下载的时候发现全是error(npm ERR! code ERESOLVE),可能是版本不一样的问题吧。
- 解决方式:.直接强制安装,在命令后面加上--legacy-peer-deps或者--force,简单粗暴,就是可能后面会碰到一些版本问题,目前我还是没遇到。小tip:可以在IDEA或者vscode上终端直接安装,就不需要去打开cmd了。
- 3.路由的使用问题:期初导航栏我是想使用路由来实现跳转的,可我一直不会用,上网找了许多方法就是一直不成功,根本原因应该是我对这个东西不熟悉,有些细节东西没有配置好,所以这个可能也是我耗时最久的东西,最后只好用别的方式来写导航栏。但是后面需要用到路由进行不同界面的传参,又需要用到这个东西。
- 解决方式:.具体的操作步骤我是参考以下文章:1 , 2。也可以去router的官网上去看如何使用,可能会更加清楚。所以,其实还是我不够了解路由的原因,导致我在配置index文件时出现了错误。但是最后,我也是成功使用了用router进行传参,并了解到了父子路由的一些内容。
- 4.不能使用element-UI问题:我在设计轮播图的时候还没用到element-ui,后来发现可能用插件来实现走马灯的组件以此来设计轮播图,可惜刚开始用就碰到问题了,我在引用 elementUI 时控制台报错了。
- 解决方式:.还是版本问题,又是版本问题,我是使用vue3的,但是网格给的下载方式是vue2的,后来我从网上重新找到了相应的方法(点这),是需要下载它的plus版本。
- 5.使用路由导致组件重复渲染问题:我是使用路由进行页面跳转,来实现点击每日赛程的比赛跳到详细赛况界面的,但是会出现一个严重的bug,就是每日赛程界面的内容重复渲染,一直出现在详细赛况内容的上方,不能实现页面的完全跳转。
- 解决方式:其实在网上是有相应的解决方法,但是我试了没有什么效果,后来我选择用v-show来隐藏父组件,可是我发现我跳转回去时候界面就变成空的,最后我还是选择用实现导航栏方式的components中添加一个新的链接跳转,并通过传参来显示界面,可惜这个方法并不能完美解决问题,它会存在再次点击导航栏无法跳转的问题,所以我把详细赛况的导航栏去掉了。
- 5.图片问题:在前后端交互的时候,由于爬取的数据没有国旗的url地址,只有他们网站的相对地址,所以需要用自己的图片,但是过程中会遇到不知道如何用后端传来的数据来加载本地图片,而且要存的国旗照片又很多的问题。
- 解决方式:利用vue-country-flag插件可以完美解决国旗问题,而且后端只需要传来国家名字的缩写,前端就能根据数据直接显示相应的国旗照片。具体操作在这(点这里你懂的)。
后端遇到的问题及解决方式
- 数据库设计问题:在把数据存入数据库时发现选手和队伍存在多对多联系,即一个选手可以在不同队伍,一个队伍可以有不同的选手。而一开始只有一张player表并添加一个队伍id字段来表示选手与队伍的关系,但是这样无法处理一些双打比赛的数据。因此需要单独设计一个teamRecord表来处理选手和队伍之间的多对多关系,这样也方便后续的后端接口的数据获取和处理
- 接口设计问题:最初的接口设计中我们认为晋级赛和赛程数据都是返回比赛数据,因此晋级赛图数据和赛程数据共用一个接口。后来发现晋级赛图需要的数据结构与赛程的数据结构有所区别,需要体现出晋级关系和轮次顺序,因此需要用两个接口来分别处理这两类数据。晋级赛图数据需要根据获胜队伍的uuid获取上一轮的比赛数据,最终通过处理将数据保存在一个二维链表。
- 项目部署问题:项目需要部署在服务器上,而项目运行需要MysQL数据库和Tomcat,因此还要在服务器上安装MySQL和Tomcat。由于服务器是Linux系统且没有图形化界面,需要重新学习已经忘光的linux命令行操作,因此安装起来比较麻烦。安装完MySQL 8.0后测试连接发现一直连接不上,后来发现通过阅读相关博客了解到root用户一般用于本地连接,而远程连接需要新建其他用户组,因此重新设置用户组权限后解决了问题。
七、代码说明
7.1 前端关键代码
- 导航栏的实现:利用keep-alive以及在component导入相应界面来实现、最后添加按钮监听事件来改变状态值以及利用v-show来进行不用界面的显示。
//html的部分代码
<div id="app">
<div class="page" v-show="this.$route.meta.index != 5">
<nav class="button">
<a class="el-menu-item" @click="show(1)"
:class="index===1? 'active':''">首页</a>
<a class="el-menu-item" @click="show(2)"
:class="index===2? 'active':''">选手排名</a>
<a class="el-menu-item" @click="show(3)"
:class="index===3? 'active':''">每日赛程</a>
<a class="el-menu-item" @click="show(4)"
:class="index===4? 'active':''">晋级图</a>
<a class="el-menu-item" @click="show(6)"
:class="index===6? 'active':''">了解更多</a>
</nav>
<div class="tab_content">
<keep-alive>
<component :is="comp" v-show="isShow"></component>
</keep-alive>
</div>
</div>
</div>
//script的部分代码
methods: {
show (value) {
if (this.index === value) {
this.index = 0
this.isShow = !this.isShow
} else {
this.index = value
this.isShow = true
}
if (value === 1) this.comp = 'one'
if (value === 2) this.comp = 'two'
if (value === 3) this.comp = 'three'
if (value === 4) this.comp = 'four'
if (value === 5) this.comp = 'five'
if (value === 6) this.comp = 'six'
this.$route.meta.index = 0
}
},
- 轮播图的实现:利用element-ui插件中的走马灯组件实现,通过v-for来添加图片。
<template>
<el-carousel :interval="4000" type="card" class="all_img">
<el-carousel-item v-for="item in imgList" :key="item">
<img :src="item.imgsrc" class="single_img"/>
</el-carousel-item>
</el-carousel>
</template>
- 日期左右滑动的实现:利用overflow: hidden来隐藏部分日期,再给按钮添加监听事情,点击时来改变显示的位置来实现
// 左滑动
scrollLeft() {
const allLength = this.monitorList.length * 120
const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return
const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength - 360 < boxLength) {
// 到最开始的时候
listEl.style.left = '0px'
} else {
listEl.style.left = '-' + (leftMove - 360) + 'px'
}
},
// 右滑动
scrollRight() {
const allLength = this.monitorList.length * 120
const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return
const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength + 360 > allLength) {
listEl.style.left = '-' + (allLength - boxLength) + 'px'
} else {
listEl.style.left = '-' + (leftMove + 360) + 'px'
}
},
- 获取后端每日赛况数据的实现:通过添加一个监听事件,点击不同日期的按钮,传递不同参数,将数据处理过后,用axios的get方式去获取后端数据
//按钮的监听事件
show (value) {
this.num === value ? this.isShow = this.isShow : this.isShow = true
this.num = value
var _this = this
let s = ''
if (value >= 4) {
console.log(value)
let data = value - 4 + 116
let str_data = data.toString()
s = '/PairProject/matches?matchType=0' + str_data
} else {
let data = value + 1
let str_data = data.toString()
s = '/PairProject/matches?matchType=Q' + str_data
}
Axios({
method: 'get',
url: s
}).then(function (resp) {
_this.race_mes = resp.data
console.log(resp.data)
})
},
- 详细赛况界面跳转的实现:同样通过添加一个监听事件,然后通过router进行界面间的跳转和数据的传递
//跳转页面
goNavbar (value) {
this.$router.push({ path: '/navbar' })
console.log(value)
}
//router配置
{
path: '/navbar',
name: 'navbar',
component: () => import('@/components/navbar.vue'),
meta: {
index: 5,
showFooter: false
}
}
//数据获取
created () {
if (this.$route.meta.index === 5) {
this.num++
this.comp = 'five'
}
}
//界面显示
<div class="page" v-show="this.$route.meta.index == 5 && num == 0">
<div class="tab_content">
<keep-alive>
<component :is="later" v-show="isShow"></component>
</keep-alive>
</div>
</div>
7.2 后端关键代码
- 数据库数据写入:将json文件的数据转换为JavaBean对象并写入数据库中,需要写入的表有players,matches,teamrecords。teamrecords和players可以在一个函数完成。
/**
* 根据已有的json文件获取选手数据并写入到数据库中
* @param filePath 选手json数据文件路径
* @throws IOException
*/
public static void getPlayersResult(String filePath) throws IOException
{
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
String json = fileToJson(filePath);
int id = 1;
Map playerMap = JSON.parseObject(json, Map.class);
List<HashMap> playerMapList = (List<HashMap>) playerMap.get("players");
for (HashMap map : playerMapList)
{
String uuid = (String) map.get("uuid");//获取选手信息
String fullName = (String) map.get("full_name");
String shortName = (String) map.get("short_name");
String gender = (String) map.get("gender");
Random random = new Random();
Integer ranking = getPlayerRanking(gender, (List<HashMap>) map.get("rankings"));
int randomInt = 90 - ranking * (1 + random.nextInt(1));
int ace = ranking != 0 && randomInt > 0 ? randomInt : 0;
int matches = 2 + random.nextInt(6);
String nationality = (String) ((HashMap) map.get("nationality")).get("name");
String url = (String) ((HashMap) ((HashMap) map.get("nationality")).get("flag")).get("url");//处理国籍的url
int index = url.lastIndexOf("/")+1;
String nationalityUrl = url.substring(index, index+2);
String profileUrl = map.get("image") != null ? (String) ((HashMap) map.get("image")).get("url") : " ";
Player player = new Player(id++, uuid, fullName, shortName,
gender, ranking, ace, matches, 0, nationality, nationalityUrl, profileUrl);
System.out.println(player);
mapper.addPlayer(player);
}
sqlSession.commit();
sqlSession.close();
getTeamResult(filePath);
}
/**
* 根据已有的json文件获取比赛数据并写入到数据库中
* @param matchType 比赛类型,Q1-Q4,Day1-Day14,draw等
* @throws IOException
*/
static public void getMatchesResult(String matchType) throws IOException
{
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
String filePath = "src/main/data/schedule/" + matchType + ".json";
Map matchMap = JSON.parseObject(fileToJson(filePath), Map.class);
List<HashMap> matchMapList = (List<HashMap>) matchMap.get("matches");
List<HashMap> roundMapList = (List<HashMap>) matchMap.get("rounds");
List<HashMap> courtMapList = (List<HashMap>) matchMap.get("courts");
List<HashMap> eventMapList = matchMap.get("events") == null ?
Arrays.asList((HashMap) matchMap.get("event")) : (List<HashMap>) matchMap.get("events");
int id = 1;
for (HashMap map : matchMapList)
{
String matchName = getValueByMap((String) map.get("event_uuid"), eventMapList);//获取比赛名字、比赛场地、轮次信息、比赛状态
String matchCourt = getValueByMap((String) map.get("court_id"), courtMapList);
String roundName = getValueByMap((String) map.get("round_id"), roundMapList);
String matchStatus = (String) ((HashMap) map.get("match_status")).get("name");
List<HashMap> teamList = (List<HashMap>) map.get("teams");
HashMap teamAMap = teamList.get(0), teamBMap = teamList.get(1);
String teamAUuid = (String) teamAMap.get("team_id");//获取A、B、胜利队伍的id,比赛开始时间、持续时间
String teamBUuid = (String) teamBMap.get("team_id");
String winnerTeamUuid = teamList.get(0).get("status") != null ? teamAUuid : teamBUuid;
String actualStartTime = (String) map.get("actual_start_time");
String duration = (String) map.get("duration");
List<HashMap> scoresAMap = (List<HashMap>) teamAMap.get("score");//获取A、B队分数
List<HashMap> scoresBMap = (List<HashMap>) teamBMap.get("score");
String scoresA = "", scoresB = "";
for (int j = 0; j < scoresAMap.size(); j++)
{
HashMap gameA = scoresAMap.get(j);
HashMap gameB = scoresBMap.get(j);
scoresA = scoresA + gameA.get("game") + "|";
scoresB = scoresB + gameB.get("game") + "|";
}
mapper.addMatch(new Match(id++, matchName, matchStatus, matchCourt, matchType, actualStartTime,
duration, teamAUuid, teamBUuid, winnerTeamUuid, scoresA, scoresB, roundName));
}
sqlSession.commit();
sqlSession.close();
}
/**
* 根据已有的json文件获取组队数据并写入到数据库中
* @param filePath
* @throws IOException
*/
public static void getTeamResult(String filePath) throws IOException
{
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
String json = fileToJson(filePath);
int id = 1;
Map teamMap = JSON.parseObject(json, Map.class);
List<HashMap> teamMapList = (List<HashMap>) teamMap.get("teams");
for (HashMap map : teamMapList)
{
String teamUuid = (String) map.get("uuid");
String seedString = map.get("seed") != null && !map.get("seed").equals("")? (String) map.get("seed") : "0";
Integer seed = map.get("seed") != null ? Integer.parseInt(seedString) : 0;
ArrayList<String> teamPlayerList = (ArrayList<String>) (map.get("players"));
for (String playerUuid : teamPlayerList)
{
mapper.addTeamRecord(new TeamRecord(id++, teamUuid, playerUuid, seed));
if(teamPlayerList.size() == 1)mapper.updatePlayerSeed(playerUuid, seed);
}
}
sqlSession.commit();
sqlSession.close();
}
- 数据库类和前端数据交互类的转换:用于数据库match表的Match类不能直接给前端使用,需要转换为AOMatch类来传给前端,相较于Match类,AOMatch类封装了更多的信息,方便了前端的代码编写。
/**
* match是用于数据库数据写入用的,传给前端的数据需要转换为AOMatch来实现数据的封装
* @param match
*/
public AOMatch(Match match)
{
this.matchName = match.getMatchName();
this.matchType = match.getMatchType();
this.matchStage = match.getRoundName();
this.matchLocation = match.getMatchCourt();
this.duration = match.getDuration();
this.winner = match.getWinnerTeamUuid().equals(match.getTeamAUuid()) ? 1 : 0;
this.scoresA = new ArrayList<>();
this.scoresB = new ArrayList<>();
this.teamA = new ArrayList<>();
this.teamB = new ArrayList<>();
String[] resA = match.getScoresA().split("\\|");
for (String str : resA)
{
if(str.matches("-?[0-9]+(\\\\.[0-9]+)?"))
{
this.scoresA.add(Integer.parseInt(str));
}
}
String[] resB = match.getScoresB().split("\\|");
for (String str : resB)
{
if(str.matches("-?[0-9]+(\\\\.[0-9]+)?"))
{
this.scoresB.add(Integer.parseInt(str));
}
}
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
teamA = mapper.selectPlayersByTeam(match.getTeamAUuid());
teamB = mapper.selectPlayersByTeam(match.getTeamBUuid());
sqlSession.close();
}
- 后端接口代码:设置了三个接口,分别返回1. players返回选手数据,2. matches返回比赛数据,3. draws返回晋级图数据
/**
* 根据参数值返回选手数据
* @param gender,值为M表示返回男性,值F表示返回女性,默认按ranking排序
* @param seed,true表示按seed排序
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "*");
String gender = req.getParameter("gender");
String seed = req.getParameter("seed");
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
List<Player> players = new ArrayList<>();
List<Player> res = new ArrayList<>();
if (gender == null || gender.length() == 0)
players = mapper.selectPlayerAll();
else if (gender.equals("M"))
{
if(seed != null && seed.equals("true"))
players = mapper.selectMalePlayersOrderedBySeeds();
else
players = mapper.selectMalePlayersOrderedByRanking();
}
else if (gender.equals("F"))
{
if(seed != null && seed.equals("true"))
players = mapper.selectFemalePlayersOrderedBySeeds();
else
players = mapper.selectFemalePlayersOrderedByRanking();
}
sqlSession.close();
if(seed != null && seed.equals("true"))
{
for(int i=0; res.size() < 20 && i <players.size() ; i++)
{
if(res.size() == 0)
{
res.add(players.get(i));
continue;
}
if(res.get(res.size()-1).getSeeds() != players.get(i).getSeeds())
res.add(players.get(i));
}
}
else
{
for(int i=0 ; i<50; i++)
res.add(players.get(i));
}
String jsonString = JSON.toJSONString(res);
resp.getWriter().write(jsonString);
}
/**
* 根据参数值返回比赛数据
* @param matchType 可能取值为Q1-Q4,0116-0129,draws
* @param roundName 表示轮次
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
{
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "*");
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
String matchType = req.getParameter("matchType");
String roundName = req.getParameter("roundName");
ArrayList<Match> matchArrayList = mapper.selectMatchesByCondition(matchType, roundName);
HashMap<String, ArrayList<AOMatch>> hashMap = new HashMap<>();
for(Match match : matchArrayList)
{
if(hashMap.containsKey(match.getMatchCourt()))
{
hashMap.get(match.getMatchCourt()).add(new AOMatch(match));
}
else
{
hashMap.put(match.getMatchCourt(), new ArrayList<>());
}
}
ArrayList<HashMap> hashMapArrayList = new ArrayList<>();//根据前端的需求改写返回的数据组织结构
for(String key: hashMap.keySet())
{
HashMap<String, Object> stringHashMap = new HashMap<>();
stringHashMap.put("matchLocation", key);
stringHashMap.put("matches",hashMap.get(key));
hashMapArrayList.add(stringHashMap);
}
String jsonString = JSON.toJSONString(hashMapArrayList);
resp.getWriter().write(jsonString);
}
/**
* 根据晋级关系和轮次顺序获取晋级图数据,并返回给前端
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
{
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "*");
SqlSessionFactory factory = SqlSessionFactoryUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
Match finalMatch = mapper.selectDrawsMatchByCondition("", MyUtil.rounds.get(0));
ArrayList<ArrayList<Match>> matchArrayLists = new ArrayList<>();
ArrayList<ArrayList<AOMatch>> aoMatchArrayLists = new ArrayList<>();
matchArrayLists.add(new ArrayList<>());
aoMatchArrayLists.add(new ArrayList<>());
matchArrayLists.get(0).add(finalMatch);
aoMatchArrayLists.get(0).add(new AOMatch(finalMatch));
//根据晋级关系和轮次顺序获取晋级赛图数据
int roundCnt= 0;
while(roundCnt < MyUtil.rounds.size() - 1)
{
matchArrayLists.add(new ArrayList<>());
aoMatchArrayLists.add(new ArrayList<>());
for(Match match : matchArrayLists.get(roundCnt))
{
String teamAuuid = match.getTeamAUuid();
String teamBuuid = match.getTeamBUuid();
Match match1 = mapper.selectDrawsMatchByCondition(teamAuuid, MyUtil.rounds.get(roundCnt+1));
Match match2 = mapper.selectDrawsMatchByCondition(teamBuuid, MyUtil.rounds.get(roundCnt+1));
matchArrayLists.get(roundCnt+1).add(match1);
matchArrayLists.get(roundCnt+1).add(match2);
aoMatchArrayLists.get(roundCnt+1).add(new AOMatch(match1));
aoMatchArrayLists.get(roundCnt+1).add(new AOMatch(match2));
}
roundCnt++;
}
Collections.reverse(aoMatchArrayLists);
String jsonString = JSON.toJSONString(aoMatchArrayLists);
resp.getWriter().write(jsonString);
}
八、心路历程和收获
222000202同学的心路历程:
- 错误预估了这次作业的工作量,以为大概3天能够完成,因此并没有很快开始做这次的作业,导致后面需要不断熬夜来挤出时间完成作业。后来复盘和反思,觉得是因为想要实现后端数据接口而不是纯前端,所以需要花大量时间在后端接口设计,数据的获取和处理上。因此需要提升项目经验,提高工作量预估的准确度。
- 没有尽快和负责前端的同学充分交流,设计出一份完整合理的数据库设计和接口设计文档,导致后续需要不断地修正接口和数据库的结构,来满足前端的需求变化。如果一开始就作出一份合理的数据库设计和接口设计文档,那会省下很多浪费的时间。因此需要重视需求分析和结构设计方面的工作。
- 会使用的框架不是很多,导致很多需求实现起来比较麻烦,花的时间也更多。会使用的框架也使用得不够熟练,总是遇到各种各样的Bug,然后浪费很多时间在解决bug上。因此需要加强和加深对一些后端框架的学习,加强自己的技术储备。
- 虽然作为软件工程专业的学生,但自身的技术储备和学习能力还远远达不到软件工程专业的预期要求,也离成为一名软件工程师还有很大距离,还需要不断加强自身各方的能力,提高技术和知识储备,这样开发项目时才会更加得心应手,更加从容。
222000212同学的心路历程:
- 起初在我看到这个作业时,心态就不良好了,时间紧、任务重,虽然说我们可以直接使用纯前端节省掉很多时间,但是最终我们小队两人还是选择了前后端分离,而我主要负责的是前端部分。对于很久没有用三件套开发的我选择了Vue框架来开发,可先前我又没有接触过该类框架,就需要花费时间去学习、探索,经历了上次结对作业,这次我选择边学习边开发,在开发设计过程中不断了解学习vue框架的内容,所以呀,前期刚刚开始的时候真的非常痛苦,啥也不懂,啥也不会,就导致我导航栏部分花费了大量时间。慢慢的,经过了解,发现其实vue框架和前端三件套原来是一样的,这心态不是一下就好起来了,接下来就是漫长的设计、开发、调试,一步步完成网页界面的设计。
- 本次结对还是收获满满的,我对前端的设计又有了进一的了解和认识,也学会了用Vue框架,但是没有使用它的脚手架(听说用起来也不太方便),此次也锻炼了我前端代码的编写能力,虽然有些细节做得并不是很到位,但总归还是顺利的完成了这次任务的设计。还有,此次作业我也认识到了和队友交流的重要性,前后端交互是需要两个人之间确定好接口名字以及字段属性名字,这样才能更好的交互。总之,此次结对作业让我学习颇丰,下次继续努力加油。
九、结对队友互评
222000202同学对222000212队友的评价: hzt同学开发项目时时积极负责,能够及时完成任务,基本实现了所有项目需求。遇到问题时都会及时解决,如果遇到了解决不了的问题也会和我交流讨论,如果有需求变化也会及时和我说。我指出他的一些问题他也会及时改正,提出的建议也会虚心接受,进步速度很快。虽然这次作业的工作量很大,但是他也没有抱怨。希望他能够通过这次作业能力有所提升,继续打磨自己的编码水平。期待与他的下次合作。
222000212同学对222000202队友的评价: 此次解决过程中,css同学发挥了相对关键的作用。在过程中,他会经常性对我的前端设计提出正确性的建议,在不懂或不会的地方也会提供我思路,我们也经常进行交流,发现他对待作业的态度十分认真,对于一些细节上的实现也会严格对待,争取把结对作业做到最好,我十分庆幸此次作业与他合作,他真的帮助我许多。最后还是那句话,这个结对,没有他就不行了呀。