685
社区成员
这个作业属于哪个课程 | <2023年福大-软件工程实践-W班> |
---|---|
这个作业要求在哪里 | <结对第二次作业> |
结对学号 | <222000230 222000329> |
这个作业的目标 | <Gitcode的合作使用、原型设计的编码实现> |
其他参考文献 | 无 |
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 840 | 1015 |
• Analysis | • 需求分析 (包括学习新技术) | 180 | 150 |
• Design Spec | • 生成设计文档 | 60 | 55 |
• Design Review | • 设计复审 | 10 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 90 | 110 |
• Design | • 具体设计 | 60 | 45 |
• Coding | • 具体编码 | 360 | 480 |
• Code Review | • 代码复审 | 20 | 15 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 150 |
Reporting | 报告 | 220 | 225 |
• Test Repor | • 测试报告 | 180 | 200 |
• Size Measurement | • 计算工作量 | 20 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 15 |
合计 | 1080 | 1260 |
本次的设计以蓝白灰色调为主,追求简洁明了的界面,使用户对本项目功能的操作更加得心应手。主页上方的导航栏可链接到不同的页面,左侧导航可去往首页的不同位置。
开头使用一张图片介绍Singles Aces Leaders第一名选手的比赛情况,使用户能够快速地获取信息,并在下方的列表中得到其它选手的比赛信息。
当用户点击某一具体的比赛阶段时,即可呈现对应阶段的选手晋级路线。晋级图展示了选手的国籍、姓名与比分,获胜选手的姓名加粗显示,并且有“√”图标。当鼠标移到某场比赛时,会出现蓝色边框。
介绍澳大利亚网球公开赛的举办背景,通过丰富的图文使平台更具吸引力,引起引起人们对澳大利亚网球公开赛的兴趣。
从澳大利亚网球公开赛官网上,找到选手排名、每日赛况等相应页面,利用浏览器的开发者工具,在network中爬取到了本次作业所需的数据。
说明:本次爬取行为和爬取数据仅用于学习!
由于林雯雯同学在之前就接触过Vue框架、Servlet以及Tomcat的使用,所以我们就决定了使用这几个技术完成项目。
<el-container style="height:950px;">
<!--侧边栏-->
<el-aside width="200px">
<el-row class="tac">
<el-menu
default-active="1"
class="el-menu-vertical-demo"
background-color="#D3DCE6">
<el-menu-item index="1" >
<i class="el-icon-location"></i>
<span><a href="about.html" target="aboutFrame" style="text-decoration:none;color:black;">关于澳网</a></span>
</el-menu-item>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title"><a href="moment.html" target="aboutFrame" style="text-decoration:none;color:black;">精彩瞬间</a></span>
</el-menu-item>
<el-menu-item index="3">
<i class="el-icon-document"></i>
<span slot="title"><a href="introduce.html" target="aboutFrame" style="text-decoration:none;color:black;">场馆介绍</a></span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title"><a href="style.html" target="aboutFrame" style="text-decoration:none;color:black;">中国风采</a></span>
</el-menu-item>
</el-menu>
</el-row>
</el-aside>
<!--Main-->
<el-main>
<!-- 默认是about.html内容-->
<iframe id="aboutFrame" name="aboutFrame" src="about.html" style="overflow: visible;"
scrolling="yes" frameborder="no" width="100%" height="100%">
</iframe>
</el-main>
</el-container>
<el-row :gutter="20">
<!--男子-->
<el-col :span="12"><div class="grid-content">
<div class="title">Men’s Singles Aces <br>Leaders</div>
<el-table
:data="tableData"
stripe
style="width: 100%;margin-right: 10px;margin-top: 20px">
<el-table-column
prop="name"
label="Name"
width="180px">
</el-table-column>
<el-table-column
prop="rank"
label="Rank">
</el-table-column>
<el-table-column
prop="matches"
label="Match">
</el-table-column>
<el-table-column
prop="aces"
label="Aces">
</el-table-column>
</el-table>
</div></el-col>
<!--女子-->
<el-col :span="12"><div class="grid-content">
<div class="title">Womens’s Singles Aces <br>Leaders</div>
<el-table
:data="tableData2"
stripe
style="width: 100%;margin-right: 10px;margin-top: 20px">
<el-table-column
prop="name"
label="Name"
width="180px">
</el-table-column>
<el-table-column
prop="rank"
label="Rank">
</el-table-column>
<el-table-column
prop="matches"
label="Match">
</el-table-column>
<el-table-column
prop="aces"
label="Aces">
</el-table-column>
</el-table>
</div></el-col>
</el-row>
</el-main>
<script>
new Vue ({
el: "#app",
mounted() {
this.selectMen();
this.selectWomen();
},
data() {
return {
activeIndex: '2',
urlFront: "http://localhost:8081/AOWeb/",
// 男子表格
tableData: [],
// 女子表格
tableData2: []
}
},
methods: {
// 获取男子数据
selectMen() {
axios({
method: "get",
url: this.urlFront + "topPlayersServlet"
}).then(resp=> {
this.tableData = resp.data;
})
},
// 获取女子数据
selectWomen() {
axios({
method: "get",
url: this.urlFront + "topWomPlayersServlet"
}).then(resp=> {
this.tableData2 = resp.data;
})
}
}
})
</script>
后端Servlet框架负责调用AOSearch类中的解析方法,并将数据传回给前端
@WebServlet("/topPlayersServlet")
public class TopPlayersServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AOSearch search = new AOSearch();
List<Player> topPlayers = search.getTopPlayers(0);
// 将List集合转为json数据
String json = JSON.toJSONString(topPlayers);
//System.out.println(json);
// 写数据
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
使用fastjson解析选手排名json文件
public List<Player> getTopPlayers(int sex) {
// 读取json文件
URL path = this.getClass().getResource("/rank.json");
File file = new File(path.getFile());
String content = "";
List<Player> topPlayers = new ArrayList<>(); // 存放球员数据的集合
try {
content = FileUtils.readFileToString(file); // rank.json文件内容
// 解析数据
// 将json字符串转为JSON对象
JSONObject jsonObject = JSONObject.parseObject(content);
// 获取男、女选手合集
JSONArray rankings = jsonObject.getJSONArray("statistics").getJSONObject(0)
.getJSONArray("rankings");
JSONArray players = rankings.getJSONObject(sex).getJSONArray("players"); // 男(女)选手
// 解析选手
for (int i = 0; i < players.size(); i++) {
JSONObject player = players.getJSONObject(i);
// 获取数据
String id = player.getString("player_id");
Integer matches = player.getInteger("matches");
Integer rank = player.getInteger("rank");
Integer aces = player.getInteger("value");
String name = getTopPlayerName(id, content);
// 封装Player对象
Player topPlayer = new Player();
topPlayer.setId(id);
topPlayer.setMatches(matches);
topPlayer.setAces(aces);
topPlayer.setRank(rank);
topPlayer.setName(name);
topPlayers.add(topPlayer);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return topPlayers;
}
//获取排名前二十的选手的名字
public String getTopPlayerName(String id, String content) {
// 将json字符串转为JSON对象
JSONObject jsonObject = JSONObject.parseObject(content);
// 获取players
JSONArray players = jsonObject.getJSONArray("players");
// 要找的名字
String name = "";
// 遍历,找名字
for (int i = 0; i < players.size(); i++) {
JSONObject player = players.getJSONObject(i);
String uuid = player.getString("uuid");
if (id.equals(uuid)) {
// 找到了
name = player.getString("full_name");
break;
}
}
return name;
}
table:hover {
border: 2px solid #1184F0;
}
<div style="float:left;">
<div>
<table>
<tr>
<td class="left" style="font-size: 15px; font-weight: bold;padding: 15px 10px 15px 10px;width: 60%">
<img src="img/lan.png" style="float: left">
<span>S.Tsitsipas</span>
</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">
<span style="margin-bottom: 10px;float: right">7 6 6 6</span>
<img src="img/gou.png" style="float: right"></td></tr>
<tr>
<td class="left" style="font-size: 15px;padding: 15px 10px 15px 10px">
<img src="img/jp.png" style="float: left">
<span>K.Khachanov</span>
</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">6 4 7 3</td></tr>
</table>
</div>
<div>
<table>
<tr>
<td class="left" style="font-size: 15px; font-weight: bold;padding: 15px 10px 15px 10px;width: 60%">
<img src="img/yang.png" style="float: left">
<span>N.Djokovic</span>
</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">
<span style="margin-bottom: 10px;float: right">7 6 6</span>
<img src="img/gou.png" style="float: right"></td></tr>
<tr>
<td class="left" style="font-size: 15px;padding: 15px 10px 15px 10px">
<img src="img/jin.png" style="float: left">
<span>T.Paul</span>
</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">5 1 2</td></tr>
</table>
</div>
</div>
<div style="float:right; margin-top:70px;">
<div>
<table>
<tr>
<td class="left" style="font-size: 15px; font-weight: bold;padding: 15px 10px 15px 10px;width: 60%">
<img src="img/yang.png" style="float: left">
<span>N.Djokovic</span>
</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">
<span style="margin-bottom: 10px;float: right">6 7 7</span>
<img src="img/gou.png" style="float: right"></td></tr>
<tr>
<td class="left" style="font-size: 15px;padding: 15px 10px 15px 10px">
<img src="img/lan.png" style="float: left">
<span>S.Tsitsipas</span>
</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">3 6 6</td></tr>
</table>
</div>
</div>
每日赛况 根据前端传来的比赛日期date,解析对应比赛日的json文件,提取出有用的信息并封装为Match对象,将所有Match对象装入List集合中并返回。
public List<Match> getMatches(String date) {
// 读取json文件
URL path = this.getClass().getResource("/" + date + ".json");
File file = new File(path.getFile());
String content = ""; // json文件内容
List<Match> dayMatches = new ArrayList<>();
try {
content = FileUtils.readFileToString(file); // 日期.json文件内容
// 将json字符串转为JSON对象
JSONObject jsonObject = JSONObject.parseObject(content);
// 获取courts数组
JSONArray courts = jsonObject.getJSONObject("schedule").getJSONArray("courts");
JSONObject court = null;
JSONArray sessions = null;
for (int i = 0, size = courts.size(); i < size; i++) {
court = courts.getJSONObject(i);
sessions = court.getJSONArray("sessions");
String courtId = court.getString("court_id"); // 比赛场地ID
String place = getCourt(courtId, jsonObject);// 获取比赛场地
JSONObject session = null;
JSONArray activities = null;
for (int j = 0, sessionSize = sessions.size(); j < sessionSize; j++) {
session = sessions.getJSONObject(j);
activities = session.getJSONArray("activities");
JSONObject activity = null;
JSONArray teams = null;
JSONObject matchStatus = null;
String matchAbbr = null;
for (int k = 0, actSize = activities.size(); k < actSize; k++) {
activity = activities.getJSONObject(k);
if (activity.getString("actual_start_time") != null) {
String time = activity.getString("duration");// 获取比赛时间
time = handleTime(time); // 转换比赛时间的格式
teams = activity.getJSONArray("teams");
String winner = getWinner(teams, jsonObject); // 获取胜利者
String loser = getLoser(teams, jsonObject); // 获取失败者
String loserScore = getScoreByflag(teams, jsonObject, 0); // 获取失败者分数
String winScore = getScoreByflag(teams, jsonObject, 1); // 获取获胜者分数
String eventUuid = activity.getString("event_uuid");
String type = getMatchType(eventUuid, content); // 获取比赛类型
// 封装Match对象
Match match = new Match();
match.setTime(time);
match.setLoser(loser);
match.setWinner(winner);
match.setLoserScore(loserScore);
match.setWinnerScore(winScore);
match.setType(type);
match.setPlace(place);
dayMatches.add(match);
} else {
matchStatus = activity.getJSONObject("match_status");
if (matchStatus != null) {
matchAbbr = matchStatus.getString("abbr");
if ("W/O".equals(matchAbbr)) {
// 弃赛
}
}
}
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return dayMatches;
}
public String readMatchInfo(JSONArray courts, JSONObject jsonObject) {
String matches = "";
JSONObject court = null;
JSONArray sessions = null;
for (int i = 0, size = courts.size(); i < size; i++) {
court = courts.getJSONObject(i);
sessions = court.getJSONArray("sessions");
JSONObject session =null;
JSONArray activities = null;
for (int j = 0, sessionSize = sessions.size(); j < sessionSize; j++) {
session = sessions.getJSONObject(j);
activities = session.getJSONArray("activities");
JSONObject activity = null;
JSONArray teams = null;
JSONObject matchStatus = null;
String matchAbbr = null;
for (int k = 0, actSize = activities.size(); k < actSize; k++) {
activity = activities.getJSONObject(k);
if (activity.getString("actual_start_time") != null) {
String time = activity.getString("actual_start_time");// 获取比赛时间
teams = activity.getJSONArray("teams");
String winner = getWinner(teams, jsonObject); // 获取胜利者
String loser = getLoser(teams, jsonObject); // 获取失败者
//courtStr += "score:" + getScore(teams, jsonObject) + "\n";
} else {
matchStatus = activity.getJSONObject("match_status");
if (matchStatus != null) {
matchAbbr = matchStatus.getString("abbr");
if ("W/O".equals(matchAbbr)) {
// 弃赛
}
}
}
}
}
}
return matches;
}
/**
* 获取胜利者
* @param teams
*/
public String getWinner(JSONArray teams, JSONObject jsonObject) {
String names = "";
JSONObject team = null;
String isWin = null;
String teamId = null;
for (int i = 0, size = teams.size(); i < size; i++) {
team = teams.getJSONObject(i);
isWin = team.getString("status");
if ("Winner".equals(isWin)) {
// 获取胜者名字
teamId = team.getString("team_id");
names += getNamesByteamId(teamId, jsonObject);
return names;
}
}
return names;
}
/**
* 获取失败者
* @param teams
*/
public String getLoser(JSONArray teams, JSONObject jsonObject) {
String names = "";
JSONObject team = null;
String isWin = null;
String teamId = null;
for (int i = 0, size = teams.size(); i < size; i++) {
team = teams.getJSONObject(i);
isWin = team.getString("status");
if (!"Winner".equals(isWin)) {
// 获取胜者名字
teamId = team.getString("team_id");
names += getNamesByteamId(teamId, jsonObject);
return names;
}
}
return names;
}
/**
* 根据id找出名字
* @param teamId 队伍id
* @param jsonObject 指定比赛日的JSON对象
*/
public String getNamesByteamId(String teamId, JSONObject jsonObject) {
JSONArray teams = jsonObject.getJSONArray("teams");
ArrayList<String> playersCode = new ArrayList<>(); // 装着players的编号
ArrayList<String> playersName = new ArrayList<>(); // 装着players的名字
// 根据teamID查找队伍
JSONObject team = null;
for (int i = 0, size = teams.size(); i < size; i++) {
team = teams.getJSONObject(i);
if (team.getString("uuid").equals(teamId)) {
// 找到了队伍
JSONArray players = team.getJSONArray("players");
for (int j = 0, pSize = players.size(); j < pSize; j++) {
playersCode.add(players.getString(j));
}
break;
}
}
// 根据球员编号获取球员名字
JSONArray players = jsonObject.getJSONArray("players");
JSONObject player = null;
String uuid = null;
for (int i = 0, size = playersCode.size(); i < size; i++) {
for (int j = 0; j < players.size(); j++) {
player = players.getJSONObject(j);
uuid = player.getString("uuid");
if (uuid.equals(playersCode.get(i))) {
// 找到了
playersName.add(player.getString("short_name"));
break;
}
}
}
String names = "" + playersName.get(0);
for (int i = 1, size = playersName.size(); i < size; i++) {
names += " & " + playersName.get(i);
}
return names;
}
/**
* 将时间格式转换 1:50 ==> 1h50m
* @param time
* @return
*/
public String handleTime(String time) {
String[] s = time.split(":"); // 1:50 用“:”分割字符串
time = s[0] + "h" + s[1] + "m";
return time;
}
/**
* 根据flag来取分数 0:失败 1:获胜
* @param teams
* @param jsonObject
* @param flag
* @return
*/
public String getScoreByflag(JSONArray teams, JSONObject jsonObject, int flag) {
String score = "";
JSONObject team = null;
String isWin = null;
for (int i = 0, size = teams.size(); i < size; i++) {
team = teams.getJSONObject(i);
isWin = team.getString("status");
if ("Winner".equals(isWin) && flag == 1) {
// 胜者
JSONArray scoreArray = team.getJSONArray("score"); // 分数数组
for (int j = 0; j < scoreArray.size(); j++) {
score += scoreArray.getJSONObject(j).getString("game") + " ";
}
break;
} else if (!("Winner".equals(isWin)) && flag == 0){
// 输
JSONArray scoreArray = team.getJSONArray("score"); // 分数数组
for (int j = 0; j < scoreArray.size(); j++) {
score += scoreArray.getJSONObject(j).getString("game") + " ";
}
break;
}
}
return score;
}
/**
* 根据比赛类型的id找比赛类型
* @param eventUuid
* @param content
* @return
*/
public String getMatchType(String eventUuid, String content) {
String type = "";
JSONObject jsonObject = JSONObject.parseObject(content);
JSONArray events = jsonObject.getJSONArray("events");
for (int i = 0; i < events.size(); i++) {
// 根据id寻找比赛类型
JSONObject event = events.getJSONObject(i);
if (eventUuid.equals(event.getString("uuid"))) {
// 找到了
type = event.getString("name");
return type;
}
}
return type;
}
/**
* 根据比赛场地的id查找比赛场地
* @param courtId
* @param jsonObject
* @return
*/
public String getCourt(String courtId, JSONObject jsonObject) {
String courtName = "";
JSONArray courts = jsonObject.getJSONArray("courts");
for (int i = 0; i < courts.size(); i++) {
// 遍历,根据id查找比赛场地
JSONObject court = courts.getJSONObject(i);
if (courtId.equals(court.getString("uuid"))) {
// 找到了
courtName = court.getString("name");
return courtName;
}
}
return courtName;
}
后端获取前端传来的数据 前端将要传递的数据写在url后,以参数的形式传递给后端Servlet。Servlet从请求体Request中获取数据。
// 接收是哪一天的数据 url?date=0116
String date = request.getParameter("date");
<script>
new Vue ({
el: "#app",
mounted() {
this.selectMatchesByDate();
},
data() {
return {
activeIndex: '3',
urlFront: "http://localhost:8888/AOWeb_war/",
// 比赛日期,默认为Q1
matchDate: 'Q1',
matches: []
}
},
methods: {
// 根据比赛日期发送请求
selectMatchesByDate() {
axios ({
method: "get",
url: this.urlFront + "dayMatchServlet?date=" + this.matchDate
}).then(resp=>{
this.matches = resp.data;
})
},
setMatchDate(newDate) {
this.matchDate = newDate; // 重新设置日期
this.selectMatchesByDate(); // 重新查询
},
jumpToDetail() {
location.href = this.urlFront + "detailMatch.html";
}
}
})
</script>
<el-row :gutter="40">
<el-col :span="8" :key="index" v-for="(item, index) in matches">
<table @click="jumpToDetail">
<tr><td colspan="2" class="left" style="font-size: 5px;;padding: 5px">{{item.type}}</td></tr>
<tr><td class="left" style="font-size: 5px;padding: 5px">{{item.place}}</td><td class="right" style="font-size: 5px;;padding: 5px">{{item.time}}</td></tr>
<tr><td class="left" style="font-size: 15px; font-weight: bold;padding: 15px 10px 15px 10px;width: 60%">{{item.winner}}</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">
<span style="margin-bottom: 10px;float: right">{{item.winnerScore}}</span>
<img src="img/gou.png" style="float: right"></td></tr>
<tr><td class="left" style="font-size: 15px;padding: 15px 10px 15px 10px">{{item.loser}}</td>
<td class="right" style="font-size: 15px;padding: 15px 10px 15px 0px">{{item.loserScore}}</td></tr>
</table>
</el-col>
</el-row>
<tr style="border-bottom: 0px">
<td rowspan="2" style="width: 50px;border-right: 0px;border-bottom: 0px"><img src="img/be.svg" width="50px" height="30px" style="margin: 15px 10px"></td>
<td style="border-left: 0px;border-bottom: 0px"><span style="font-weight: bold;font-size: 13px">
Point to Z. Bergs • 1:26</span>
<span style="float: right;font-weight: bold;margin-right: 10px">Game</span>
</td>
</tr>
<tr style="border-top: 0px">
<td style="border-left: 0px;border-top: 0px;font-size: 13px">
J. Jin loses the point with a Backhand Unforced Error
</td>
</tr>
甘佳欣同学的心路历程和收获:此次作业让我有点质壁分离 。前端设计以及后端数据处理这倒不算什么,主要是Tomcat的配置以及云服务器的部署,由于我的IDEA和同学的不一样导致我的Tomcat也和别人不一样,于是我在配置上就走了很多坑,而由于不熟悉云服务器的部署,一直出现页面无法访问也让我接近崩溃,好在最后都解决了!抛开这些配置问题,本次的作业还是让我收获很多新知识的,如Vue的语法、Element UI的使用、Servlet框架的使用、fastjson的使用(之前作业使用的是Gson)、云服务器的部署以及Tomcat的配置。我觉得coding能力固然重要,发现问题来源,环境的配置也十分重要,希望我以后对于这些问题的处理可以更加从容应对吧!
林雯雯同学的心路历程和收获:由于之前已经有了一些Java Web基础,也曾经动手开发过几个简单的小案例,所以这次的作业对我来说是一次检验和巩固。当拿到题目时,我就迅速决定了要用哪些技术,脑海中一下就有了整体项目代码的设计思路与框架,引领着我的队友学习,一点点把项目做出来。本次作业检验了我之前自学的成果,进一步加深了我对这些技术的掌握程度。同时我也体会到了提前自主学习的好处,这些编程基础让我不会手忙脚乱、茫然不知所措,给了我很大的信心与底气。今后,我仍会持续自主学习,以应对未来更加复杂的需求。
甘佳欣 To 林雯雯:林雯雯同学真的真的真的好强!她在很早之前就自学了这些技术,令我十分佩服。在本次作业中,我跟着她学习了很多新的知识(Maven的优点、Vue框架中Element的用法、Servlet的使用、Tomcat的部署、fastjson的使用),真的受益良多,希望以后还有机会和她合作。
林雯雯 To 甘佳欣:甘佳欣同学特别特别特别愿意学习新技术,且学习速度非常之快,让我很是佩服。她学习、做事特别积极,丝毫不拖泥带水,和我的做事风格非常一致,给我的两次结对体验都很舒服。她善于解决问题,由于IDEA版本的不一致,很多问题都是她自己摸索着解决的,值得我学习。