结对第二次作业——编程实现

这个作业属于哪个课程2302软件工程
这个作业要求在哪里结对第二次作业——编程实现
结对学号222100304&222100313
这个作业的目标完成上次结对作业原型设计的代码编写,实现所要求的功能
其他参考文献《构建之法》、《阿里巴巴Java开发手册》

1.Gitcode仓库地址与代码规范连接

雪山来信 / pair-project

代码规范


2.PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1520
• Estimate• 估计这个任务需要多少时间1520
Development开发12101980
• Analysis• 需求分析 (包括学习新技术)150460
• Design Spec• 生成设计文档3040
• Design Review• 设计复审2050
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)2030
• Design• 具体设计1080
• Coding• 具体编码8001200
• Code Review• 代码复审60120
• Test• 测试(自我测试,修改代码,提交修改)120180
Reporting报告110150
• Test Repor• 测试报告6090
• Size Measurement• 计算工作量2030
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划3030
合计13352150

3.作品网址

Doha 2024 - Diving
严正声明:数据爬取是为学习使用,该爬取行为仅用于课程教学!!!


4.作品详解

4.1 基础功能与附加功能

功能1:选手信息

功能说明:显示Diving项目所有选手的信息
功能展示如下:
功能1
功能1详解:
前端ATHLETES页面接收后端传入的,已过滤的json数据
在主框体里面,以表格的形式渲染athletes页面,表格样式设置为斑马线,单数行和双数行有不同的背景色
展示了Doha - 2024中跳水项目的所有运动员的信息
信息包含Country,Athlete,Gender,DOB
附加功能:
可通过国家(输入国家名)、性别(通过下拉框选择)进行筛选

功能2:每日赛况

功能说明:展示每一天的赛事,显示比赛类型(男子1m跳板,女子10m跳台等),比赛时间(对于决赛需要突出显示)
功能展示如下:
在这里插入图片描述
功能2详解:
前端CALENDAR页面接收后端传入的,已过滤的json数据
通过element plus的滚动框组件,可以点选2nd->10th的日期
前端calendar根据选中的日期,更改对应的数据,响应式循环渲染Match组件
通过点选日期,来渲染出对应日期的赛况,显示比赛类型,比赛时间
通过点击Event Details旁的按钮,能够跳转到RESULTS页面,直接查看对应的详细赛况
附加功能:
根据页面路由query传输选中对应比赛的相关参数到RESULTS页面,达到直接显示对应详细赛况的效果

功能3:详细赛况

功能说明*展示比赛的成绩,包含本场比赛参赛选手,选手排名,比赛积分,落后积分等
功能展示如下:
功能3
功能3详解
前端RESULTS页面通过后端传入的已过滤的json数据
循环渲染MatchResults组件,显示各比赛
一是通过CALENDAR跳转传来的路由query参数直接渲染对应的子组件MatchResult下的子组件MatchTable
二是通过点选RESULTS页面里的对应下拉按钮,通过条件渲染,渲染对应的MatchTable组件
MatchTable组件可以展示比赛的详细赛况,实现展示比赛的成绩,包含本场比赛参赛选手,选手排名,比赛积分,落后积分。

功能4:奖牌榜

功直观友好地展示各个参赛国的获奖情况
功能展示如下:
功能4
功能4详解
前端medalTable页面通过后端传入的已过滤的json数据以表格形式渲染页面,表格样式设置为斑马线
表格中金银铜奖牌三列,为表头分别设置了对应的背景色
并且设置表头背景为对应的奖牌图片(金牌少,银牌中,铜牌多),为表格单元设置了较浅的金银铜背景色
展示信息包含Rank,Country,Gold,Silver,Bronze,Total


4.2 附加页面

了解更多

功能说明*提供更多相关信息,方便用户了解
功能展示如下:
附加功能

了解更多详解
一是用英文介绍了世界游泳锦标赛的主办方——世界游泳联合会,介绍文字有正常、加粗、蓝色样式,有较好的视觉效果
二是Lastest栏目,提供多哈跳水项目最新信息,图文丰富,设置了hover,鼠标移到图片上或图片说明上会有动态效果,有良好的互动效果,点击跳转相应的新闻页面

首页
通过精美的轮播图,展现运动健儿们的风采
首页
导航栏
通过导航栏,用户可以跳转到对应的页面,同时导航栏还有还原原型设计的丰富动效
在这里插入图片描述
部署至云服务器
在这里插入图片描述


5.结对讨论过程


5.1 结对分工

304:Home页面、Athlete页面、MedalTable页面、More页面;页面相关数据解析以及填充;部分博客撰写
313:页面路由;Calendar页面、Result页面;页面相关数据解析以及填充;部署云服务器;部分博客撰写


5.2 结对进度

本次作业发布后,我们并没有急着上手~~(两个拖延症患者)~~,先确定了 Vue3 + SpringBoot 的开发框架后,我们讨论了是一人负责前端开发一人负责后端开发还是两人一起进行前后端开发,为了想在这个学习向的作业里让自己能够掌握前后端技术或者学会基础框架、更好了解整体进度了解队友的工作、遇到困难时能互相提供帮助、省去前后端数据交互式的沟通问题,我们选择了第二种方案。

  • 作业发布第一天,因为YS同学已经有了一点Vue基础,我们选择Vue3框架进行开发,并较为系统学习了这个框架。
  • 第二天下午,大概学完Vue框架后,我们准备开始项目开发。
    首先对于Git的协作,看过网上的简短教程之后还是一知半解,于是我们直接上手试验,实践出真知!
  • 第三天,创建了前端Vue项目,实现了导航栏以及页面路由配置。
    简单学习了SpringBoot框架后创建了后端SpringBoot项目。
  • 第四天,前端完成版头Header组件、页脚Footer组件、Home页面、More页面、部分Calendar页面。
    后端实现athletes.json文件解析。
  • 第五天,前端完成Athlete页面、Calendar页面、Result页面。
    实现前后端数据交互为Athlete页面填充数据。
  • 第六天,前端完成MedalTable页面。
    后端实现medals.json文件解析、calendar.json文件解析。
    实现前后端数据交互为MedalTable页面、Calendar页面、Results页面填充数据。
    至此,项目已基本完成。
  • 第七天,编写博客、将项目部署到云服务器。

5.2 结对图片

聊天记录截图:
讨论git

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

线下图片
被拍了TAT

6.设计实现过程

本次作业采取前后端分离的架构设计
开发工具:IDEA + VSCode
前端:Vue3 + Vite + VueRouter + ElementUI + Axios
后端:SpringBoot框架+maven管理+fastjson解析


6.1 功能结构图

项目功能

包含附加功能

前端项目结构

前端

后端项目结构
后端


6.2 具体实现过程

分析了作业要求后,我们把实现这个小型项目分为以下几步:

6.2.1 前端页面设计
  • 我们将整个项目分为6个页面,分别是HOME、ATHLETES、CALENDAR、RESULTS、MEDALTABLE、MORE。
  • 基于 Vue3 开发,在 script 部分定义数据、生命周期钩子和计算属性等,在 template 部分呈现数据和绑定事件,在 style 部分进行页面美化。
6.2.2 后端进行数据解析
  • 在 entity 包下根据前端所需信息定义实体类,用于解析json数据时将所需要的信息封装成实体类对象。
  • 在 resolver 包下创建 Search 类,共有 AthleteSearch、MedalSearch、ResultSearch、ScheduleSearch 4个类,在类里面编写方法解析json文件
  • (部署云端环节,新增一个JsonResolver类,确保打包为jar包后照常能够读取文件)
6.2.3 前端请求后端数据进行页面数据填充
  • 前端使用 Axios 库发送一个 GET 请求到指定的 URL(与后端定义的路径一致)根据请求结果进行相应的处理。
  • 后端使用@RestController注解定义RESTful服务的控制器用于处理HTTP请求并返回相应的数据。
  • 使用@CrossOrigin注解用于解决前后端因端口不同而产生的跨域问题,允许来自不同域的请求访问RESTful服务。
6.2.4 将项目部署到云服务器
  • 1.购买云服务器获得ip地址
  • 2.下载安装Xshell与服务器连接
  • 3.通过宝塔面板,在云服务器上安装宝塔便于管理项目(java一键部署,nginx)
  • 4.确保前端后端ip地址使用公网,前端请求后端端口(8080),后端跨域注解填写前端端口(5173)
  • 5.通过nginx配置前端部署监听端口,vue项目npm run build打包dist
  • 6.通过工件打包方法,打包后端java程序包为.jar,上传至云服务器
  • 7.debug(后端无法访问json文件,前端资源文件路径丢失)
  • 8.回到java后端,解决jar包无法访问路径的问题(通过ClassPathResource获得文件流)
  • 9.回到vue3前端,配置图片动态路径(通过直接引入图片路径,相当于把图片当作组件使用)
  • 10.成功部署

7.代码说明

以Athlete页面的实现为例

  1. 前端(省略style)
    在页面挂载之前通过axios发出请求获取后端传来的数据并定义运动员过滤函数,使用v-model绑定filterByCountry和filterByGender实现View与Model中数据的双向绑定,通过输入框获取用户输入的国家名、通过下拉框获取用户选择的性别对运动员进行过滤。若存在目标数据,使用v-for将数据循环渲染到表格中。
import { ref, onBeforeMount, computed } from 'vue'
import Head from '@/components/Head.vue'
import Footer from '@/components/Footer.vue'
import axios from 'axios'

const athletes = ref([]) //所有运动员数据
const filterByCountry = ref('') //经过国家过滤后的运动员数据
const filterByGender = ref('') //经过性别过滤后的运动员数据

//在页面挂载之前通过axios发出请求获取后端传来的数据
onBeforeMount(async () => { 
  try {
    const response = await axios.get("http://localhost:8080/athletes") 
    athletes.value = response.data
    console.log(response.data)
  } catch (error) {
    console.error('Error fetching athletes data:', error)
  }
})

//运动员过滤
const filteredAthletes = computed(() => {
  return athletes.value.filter(athlete => {
    if (
      (!filterByCountry.value || athlete.Country.toLowerCase() === filterByCountry.value.toLowerCase()) &&
      (!filterByGender.value || athlete.Gender.toLowerCase() === filterByGender.value.toLowerCase())
    ) {
      return true
    }
    return false
  })
})
</script>

<template>
  <Head />
  <div class="container">
    <div class="main">
      <div class="filters">
        <label for="countryFilter">Filter by Country : </label>
        <input id="countryFilter" v-model="filterByCountry" placeholder="Enter country">
        <label for="genderFilter">Filter by Gender : </label>
        <select class="custom-select" v-model="filterByGender">
          <option value="">All</option>
          <option value="Male">Male</option>
          <option value="Female">Female</option>
        </select>
      </div>
      
      <table class="athlete-table">
        <thead>
          <tr>
            <th>Country</th>
            <th>Athlete</th>
            <th>Gender</th>
            <th>DOB</th>
          </tr>
        </thead>
        <tbody>
          <template v-if="filteredAthletes.length"> 
            <tr v-for="athlete in filteredAthletes" :key="athlete.FullName">
              <td>{{ athlete.Country }}</td>
              <td>{{ athlete.FullName }}</td>
              <td>{{ athlete.Gender }}</td>
              <td>{{ athlete.DOB }}</td>
            </tr>
          </template>
          <template v-else>
            <tr>
              <td colspan="4">No matching athletes found.</td>
            </tr>
          </template>
        </tbody>
      </table>
    </div>
  </div>
  <Footer />
</template>
  1. 后端
  • Athlete类
    根据我们在Athlete页面需要展示的信息,Athlete类应该具有的属性有:Country、FullName、Gender和DOB。
/**
 * 选手实体类
 */
public class Athlete {
    @JsonProperty("Country")
    private String Country; // 选手国籍

    @JsonProperty("FullName")
    private String FullName; // 选手全名

    @JsonProperty("Gender")
    private String Gender; // 选手性别

    @JsonProperty("DOB")
    private String DOB; //选手出生日期

    public Athlete(String country, String fullName, String gender, String dOB) {
        Country = country;
        FullName = fullName;
        Gender = gender;
        DOB = dOB;
    }
    @JsonProperty("Country")
    public String getCountry() {
        return Country;
    }

    public void setCountry(String country) {
        Country = country;
    }

    @JsonProperty("FullName")
    public String getFullName() {
        return FullName;
    }

    public void setFullName(String fullName) {
        FullName = fullName;
    }

    @JsonProperty("Gender")
    public String getGender() {
        return Gender;
    }

    public void setGender(String gender) {
        Gender = gender;
    }

    @JsonProperty("DOB")
    public String getDOB() {
        return DOB;
    }

    public void setDOB(String DOB) {
        this.DOB = DOB;
    }
}
  • AthleteSearch类(解析jaon)
    使用ClassPathResource,以便打包后仍然能获取资源文件流
    解析文件流,处理为json字符串
    遍历 JSON 数组,逐个解析每个国家的运动员信息。
    对每个运动员的信息进行处理,提取国家名称、性别、出生日期等信息,并将其封装成 Athlete 对象。
    最终将所有解析得到的 Athlete 对象存储在一个列表中,并返回该列表。
public class AthleteSearch {
    public List<Athlete> resolveAthlete(){
        List<Athlete> athleteList = new ArrayList<>();
        try{
            Resource resource = new ClassPathResource("athletes.json");
            InputStream inputStream = resource.getInputStream();
            String json=JsonResolver.streamToJson(inputStream);
            JSONArray countryArray = JSON.parseArray(json);
            for (Object country : countryArray) {
                String countryName = ((JSONObject) country).getString("CountryName");
                 // 获取运动员数组
                JSONArray athleteArray = ((JSONObject) country).getJSONArray("Participations"); 
                 // 遍历运动员数组
                for (int i = 0; i < athleteArray.size(); i++) {
                    JSONObject athlete = athleteArray.getJSONObject(i);
                    String gender = athlete.getInteger("Gender") == 0 ? "Male" : "Female";
                    String dob = athlete.getString("DOB").substring(0, athlete.getString("DOB").length() - 9); // 截取日期部分
                    athleteList.add(new Athlete(countryName, (athlete.getString("PreferredLastName") + " " + athlete.getString("PreferredFirstName")),
                            gender, dob));
                }
            }
        }catch(IOException e){
            throw new RuntimeException(e);
        }
        return athleteList;
    }
}

  • AthleteController类
    处理HTTP请求返回数据
@RestController
@CrossOrigin
public class AthleteController {
    @CrossOrigin("http://120.55.14.246:5173")
    @RequestMapping(value = "/athletes", method = RequestMethod.GET)
    public String getAthlete(){
        AthleteSearch search = new AthleteSearch();
        List<Athlete> athletes = search.resolveAthlete();
        return JSON.toJSONString(athletes);
    }
}


  • MatchResult.vue组件
  • 从跨页父组件Match.vue传来路由query传参获得Id,并把Id传给子组件MatchTable
  • 实现点击calendar的Event Details按钮,能够直接跳转到results页面,打开对应比赛的详细赛况
<script setup>
import {ArrowDownBold,ArrowUpBold} from '@element-plus/icons-vue'
import { ref,watch,onBeforeMount} from 'vue'
import MatchTable from '@/components/MatchTable.vue'
import axios from 'axios'
const selectedMatch = ref(null);
const props = defineProps({
  Id:String,
  matches:Array
});

const notViewMatchDetails = ()=>{selectedMatch.value=null}
//使用watch来监视,
watch(() => props.Id, () => {
  selectedMatch.value = props.matches
  Id.value=props.Id
});

const Id=ref(null)
const combinedName=(str1,str2)=>{
  return str1 + ' ' + str2
}

const matches = ref([
]);

onBeforeMount(async () => {
  try {
    const response = await axios.get("http://120.55.14.246:8080/results")
    matches.value = response.data
    findMatchingElement(matches.value,props.Id)
    console.log(props.Id)
  } catch (error) {
    console.error('Error fetching matches data:', error)
  }
})
const viewMatchDetails = (matchId1,matchId2) => {
  findMatchingElement(matches.value,combinedName(matchId1,matchId2))
};

function findMatchingElement(arr, searchString) {
  for (const match of arr) {
    for (const heat of match.Heats) {
      const combinedName = `${match.DisciplineName} ${heat.PhaseName}`;
      if (combinedName === searchString) {
        selectedMatch.value=heat.Results
      }
    }
  }
}

</script>

8.结对心路历程与评价

8.1 结对感想

  • 304:这次结对作业真的收获很多。首先大概掌握了Vue3框架的使用,同时复习了html、css、javascript的语法,能够较为完全地还原原型设计。对SpringBoot框架的学习还较浅,在时间这么紧的情况下只根据作业要求进行了点对点的学习,希望之后还能深入学习这个使用的框架。另一方面,我更加体会了结对编程的好处,可以知识技术共享、通过有效的沟通提高工作效率、产生更好的解决方案等。

  • 313: 这次结对作业,让我知道了人原来可以那么久不吃饭,结对队友很棒,和我一同前行,让我分清楚当下该做什么,怎样才能高效开发。让我能够坚持写到部署完云服务器,帮我分担了非常多压力~~(不想写的让她写hhhh),非常可靠!最大的一个感想就是,用着有一点基础但不多的框架来真实地从零写一个网页框架,非常累。除此之外,学到了很多新知识恶补了大二的web知识。。~~,学习vue3框架(组件套娃,参数传递,动态组件,前端打包),学习用新方法来解析json文件(创建JavaBean类型,使用GsonFormat工具分析json结构,自动生成,几行代码就能输出json数据),后端打包,云服务器部署处理。总结来说,都是前面欠下来的账hhh,不过有学到东西是真的好,算是基本入门这个行业了

8.2 队友评价

  • 304->313:队友真的太靠谱了,与第一次结对作业相比这次作业需要掌握前后端框架编写代码,这让我看到了队友身上的专业素养。因为我们两都同时进行前后端开发,相对来说我负责的是比较简单的页面,而队友负责的Calendar和Result页面则需要比较复杂的逻辑,最后队友的完成度也让我十分满意。在我遇到一些错误时,队友总是能帮我定位到错误的源头提供解决思路,他的细心、使用工具的能力非常值得学习。
  • 313->304:相比于第一次结对作业,队友水平依旧棒,能很有条理的处理好双方分配的任务,清楚知道此时此刻要做啥(没有队友我真是一头乱麻,不知道要做啥)。也能很好沟通交流,不会说问个话等一年这样,沟通交流在线,那么剩下的就是做代码了!我主要负责前端,不过遇到难题也会找队友帮忙,队友遇到难题我也能帮帮忙,总结来说,双方都能得到进步,非常不错!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

selfsuki

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值