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

222000407胡彦杰 学生 2023-03-24 16:53:10
这个作业属于哪个课程2023年福大-软件工程实践-W班
这个作业要求在哪里结对第二次作业--编程实现
结对学号222000407 222000414
这个作业的目标学习相应的原型设计工具 、根据客户给出的需求,设计相应的原型模型,满足其需求 、撰写博客
其他参考文献CSDN,vue官方文档,go菜鸟教程,gin官方文档

目录

  • 一.git仓库和代码规范链接
  • 二.PSP表格
  • 三.项目部署
  • 四.成果展示
  • 五.结对讨论过程描述
  • 六.设计实现过程
  • 6.1后端的设计实现过程
  • 6.2前端的设计实现过程
  • 6.3功能结构图
  • 七.具体实现过程
  • 7.1后端代码
  • 7.2前端代码
  • 八.心路历程和收获
  • 九.评价结对队友

一.git仓库和代码规范链接

代码仓库:https://gitcode.net/qq_51697026/pair_project

后端go规范:https://gitcode.net/qq_51697026/pair_project/-/blob/dev/22200407_222000414/222000407backend/codestyle.md

前端vue规范:https://gitcode.net/qq_51697026/pair_project/-/blob/dev/22200407_222000414/222000414frontend/codestyle.md

二.PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划3030
• Estimate• 估计这个任务需要多少时间3030
Development开发22502570
• Analysis• 需求分析(包括学习新技术)180240
• Design Spec• 生成设计文档3030
• Design Review• 设计复审1030
• Coding Standard• 代码规范2030
• Coding• 具体编码18002000
• Code Review• 代码复审6090
• Test• 测试150150
Reporting报告90175
• Test Repor• 测试报告3040
• Size Measurement• 计算工作量2015
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划40120
合计23702775

三.项目部署

项目部署:http://47.102.219.152/#/

四.成果展示

首页采用轮博图的形式展示澳网的精彩时刻

img

选手排名的页面,每次只展示男子单打排名或者女子单打排名,通过单击切换榜单

img

每日赛程根据场地分类,并且可以通过顶部的按钮切换天数

img

晋级图显示从4th round到final的选手晋级情况(有点不会画晋级线😭)

img

通过时间线的形式展示澳网的大事件

img

五.结对讨论过程描述

首先我们确定了前后端分离的做法,在原型设计的时候就知道了后面还有具体编码的实现提前讨论过,所以我们前后端的分工很快就完成了,前端由LJJ(2220004141)完成,后端和部署由HYJ(222000407)完成,接下来是框架与语言的选择,前端无疑是老三样,html+css+js,由于vue在当下的火热,我们前端选择了vue框架,也当作是一次实操vue的机会,后端没选择java语言和spring框架,而是选择go语言和gin框架,是为了后期团队项目,提前学习和实操代码。当项目进行一部分时,发现前端的学习成本和开发成本确实都要大于后端。

img

img

六.设计实现过程

6.1后端的设计实现过程

后端采用gin架构

img

json数据存储在本地,src中包含了3个软件包

buildmap构造一些json文件中映射,如队伍id到选手id的映射,选手id到名字的映射等。

defstruct中定义了一些结构用于读取json文件(晋级图复用每日赛程的结构)。

handlefunc中为响应get请求的函数,为前端的请求返回json对象。

以选手排名rank为例,说明后端的数据处理以及和前端的交互过程

首先main函数中,路由绑定好相应get请求"/rank"和对应的相应函数rank.go,监听端口,当接收到匹配的get请求时,执行rank函数,rank函数中先运行构造映射的函数,为接下来的json文件读取构造映射,然后打开对应的json文件进行遍历读取,将读取的数据构造成结构,最后将结构数组打包成json传给前端。

6.2前端的设计实现过程

前端采用Vue框架,并且引入element组件来进行界面设计,引入axious来与后端进行交互,请求数据

  • 导航栏主要是通过el-menu组件配合路由router来进行在单页应用中跳转并渲染视图
  • 首页通过设置一个走马灯来轮播澳网比赛期间的精彩时刻
  • 选手排名界面是通过一个el-table组件来映射显示后端传过来的Json数据,并且通过一个点击事件设置并配合相应的类选择器来实现单击页面翻转功能,以显示男单、女单排名
  • 赛程界面通过引用el-scrollbar组件来组织日期滚动,并且每个赛况用一个el-card组件表示,在里面通过css配置组织成所需的样式
  • 晋级图界面中用el-card组件来表示一个晋级框,并通过设置每个div的v-for属性按后端传输来的json数据特定输出每日一轮的晋级情况
  • 澳网历史界面主要是引入特定的css结构以及template来实现的

6.3功能结构图

img

七.具体实现过程

7.1后端代码

(这里以比较复杂的每日赛程和晋级图为例,选手排名实现较简单,省略)

每日赛程

在main中绑定相应的处理函数

//每日赛程
    router.GET("/results", handlefunc.Result)

destruct中的RankStruct定义了读取的结构,Match中包含两个Team,每个Team中包含一至多个选手,采用切片和结构的嵌套来实现这一效果。

package defstruct

//根据比赛场地来展示当天的比赛

type Match struct {
    Event        string //比赛类型
    Match_status string //比赛的状态
    Duration     string //持续的时间
    Round        string //轮次
    Team         []Team //对战的队伍
}

type Team struct {
    Id       string   //队伍的编号
    Players  []Player //队伍中的选手
    Iswinner string   //是否是冠军队伍
    Score    []string //得分情况
}

type Player struct {
    Name        string //选手姓名
    Nationality string //选手的国籍
}

rank方法中先为后续的读取建立映射,遍历matches数组,调用BuildMatch函数构造Match对象,根据场地的名称加入映射,以实现比赛按场地分类。BuildMatch中,遍历队伍,添加选手,胜者信息,遍历添加比分。将其构造为Match对象返回。

var teammap map[string][]string
var eventmap map[string]string
var roundmap map[string]string
var playermap map[string]map[string]string
var courtmap map[string]string

func Result(c *gin.Context) {
    //设置跨域
    c.Header("Access-Control-Allow-Origin", "*")
    day, _ := c.GetQuery("dayid")
    //建立队伍,映射赛事,回合,选手,队伍的映射
    teammap = buildmap.BuildTeamMap(day)
    eventmap = buildmap.BuildEventMap(day)
    roundmap = buildmap.BuildRoundMap(day)
    playermap = buildmap.BuildPlayerMap(day)
    courtmap = buildmap.BuildCourtMap()

    data, err := ioutil.ReadFile("data/result_" + day + ".json")
    if err != nil {
        fmt.Println("文件打开失败")
    }

    //声明要返回的映射
    var courtTomatches map[string][]defstruct.Match
    courtTomatches = make(map[string][]defstruct.Match)

    jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
        court_id, _ := jsonparser.GetString(value, "court_id")
        //添加场地和Match的映射
        courtTomatches[courtmap[court_id]] = append(courtTomatches[courtmap[court_id]], BuildMatch(value))

    }, "matches")

    c.JSON(http.StatusOK, courtTomatches)
}

//构造好match对象
func BuildMatch(value []byte) defstruct.Match {
    //每场比赛的基本信息 场地,状态,回合,持续时间
    event_id, _ := jsonparser.GetString(value, "event_uuid")
    status, _ := jsonparser.GetString(value, "match_status", "name")
    round_id, _ := jsonparser.GetString(value, "round_id")
    duration, _ := jsonparser.GetString(value, "duration")

    var teams []defstruct.Team
    //遍历队伍的信息
    jsonparser.ArrayEach(value, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
        var team defstruct.Team
        team.Id, _ = jsonparser.GetString(value, "team_id")
        players := teammap[team.Id]
        //由队伍的id添加选手
        for i := 0; i < len(players); i++ {
            player := defstruct.Player{playermap[players[i]]["name"], playermap[players[i]]["nationality"]}
            team.Players = append(team.Players, player)
        }
        //判断是否是胜者
        team.Iswinner, _ = jsonparser.GetString(value, "status")
        if team.Iswinner == "" {
            team.Iswinner = "false"
        }
        //遍历添加比分
        jsonparser.ArrayEach(value, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
            score, _ := jsonparser.GetString(value, "game")
            team.Score = append(team.Score, score)
        }, "score")
        teams = append(teams, team)
    }, "teams")

    //构建Match对象
    match := defstruct.Match{eventmap[event_id], status, duration, roundmap[round_id], teams}

    return match
}

晋级图

构造drawmap,以场地名称和order顺序作为key,复用每日赛程的Macth结构,在遍历draws.json的matches中调用result(每日赛程)中的BulidMatch方法,构造Match对象加入映射,由于以场地名称和order作为key,故每个round内的比赛桶排序成了合理的顺序,前端可以直接使用。

func Draw(c *gin.Context) {
    c.Header("Access-Control-Allow-Origin", "*")
    var drawmap map[string]map[int64]defstruct.Match
    drawmap = make(map[string]map[int64]defstruct.Match)

    //建立映射
    teammap = buildmap.BuildTeamMap("draws")
    eventmap = buildmap.BuildEventMap("draws")
    roundmap = buildmap.BuildRoundMap("draws")
    playermap = buildmap.BuildPlayerMap("draws")
    courtmap = buildmap.BuildCourtMap()

    data, err := ioutil.ReadFile("data/draws.json")
    if err != nil {
        fmt.Println("文件打开失败")
    }

    jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
        round_id, _ := jsonparser.GetString(value, "round_id")
        roundname := roundmap[round_id]
        order, _ := jsonparser.GetInt(value, "order")
        if drawmap[roundname] == nil {
            drawmap[roundname] = make(map[int64]defstruct.Match)
        }
        drawmap[roundname][order] = BuildMatch(value)
    }, "matches")

    c.JSON(http.StatusOK, drawmap)
}

7.2前端代码

(这里主要是以导航栏、选手排名以及每日赛程为例,晋级图思路与每日赛程类似)

导航栏

创建一个index.js文件,在里面编写组件与路由之间的映射关系,让Vue Router知道每一个路由所渲染的组件

const router = new createRouter({
    history:createWebHashHistory(), //hash模式:createWebHashHistory,history模式:createWebHistory
    routes: [
        {path: '/',name:'home',component:Home},
        {path: '/rank',name:'rank',component: PlayersRanking},
        {path:'/results',name:'results',component: Schedule},
        {path:'/draws',name:'draws',component: PromotionChart},
        {path:'/history',name:'history',component: History,meta:{path:"/history"}}
    ]
})

export default router

在main.js中引入刚才配置的index.js文件,并使用配置到全局

import { createApp} from 'vue'
import App from './App.vue'
import router from './router/index.js'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import axios from 'axios';

const app = createApp(App)
app.use(ElementPlus)
app.use(router) 
app.config.globalProperties.$axios = axios //全局配置axios
axios.defaults.baseURL = 'http://47.102.219.152:8080'

app.mount('#app')

通过el-menu菜单组件来显示导航栏

<div id="nav">
          <el-menu
          class="el-menu-demo"
          mode="horizontal"
          background-color="#797979"
          text-color="#FFFFFF"
          active-text-color="#ffd04b"
          router>
          <el-menu-item index="/rank"><em class="menu_item">选手排名</em></el-menu-item>
          <el-menu-item index="/results"><em class="menu_item">赛程</em></el-menu-item>
          <el-menu-item index="/draws"><em class="menu_item">晋级图</em></el-menu-item>
          <el-menu-item index="/history"><em class="menu_item">澳网历史</em></el-menu-item>
          </el-menu>
        </div>

选手排名

通过axios向后端请求数据并根据key值将数据赋给相应的数组

 created:function(){
      this.$axios('/rank').then((result)=>{
        console.log(result.data)
          this.tableDataM = result.data['Manranklist']
          this.tableDataF = result.data['Womenranklist']
      })
    },

  data() {
    return {
      flag: true,
      tableDataM: [],
      tableDataF: []
    };

通过handleFlip函数来进行页面翻转

handleFlip() {
      let container = document.getElementById('container')
      container.style.transform = this.flag ? 'rotateY(180deg)':''
      this.flag = !this.flag
    },

赛程

通过设置el-scrollbar-item的点击事件changeData来获取不同日期的json数据,当点击相应的Item时,根据item组件的内容获取所要请求的网址信息,并请求获取相应数据

changeData(e){
          var btnText = e.target.innerText
          var base = 115
          if (btnText[0] == 'D'){
            base += parseInt(btnText.substr(3))
            base = "/results?dayid=0" + base
          }
          else{
            base = "/results?dayid="+btnText
          }
          this.$axios(base).then((result)=>{
            this.matches = result.data
          })
      }

通过el-card组件并结合相应来实现css来进行赛程界面的排版(代码太多了就展示一部分吧)

<div class="area">
      <div v-for="(place,index) in matches" :key="index" >
        <div style="font-size:28px;font-weight:bolder;margin-left:55px">{{index}}</div>
        <div class="place">
          <div v-for="match in place" class="match">
        <el-card class="box-card">
          <!--比赛类型-->
          <div>
            <div style="font-weight:bolder;float:left">
              {{match.Event}}
            </div>
            <div style="font-weight:bolder;float:right">
              {{match.Match_status}}
            </div>
          </div>
          
          <div style="margin-top:30px">
            <div style="color:#AAAAAA;float:left">
              {{match.Round}}
            </div>
            <div style="color:#AAAAAA;float:right">
              {{match.Duration}}
            </div>
          </div>

          <!--分割线-->
          <el-divider style="margin-top:60px;margin-bottom:10px"></el-divider>

          <!--选手1信息-->
          <div>
            <div style="float:left">
              <div style="">
              <img :src="match.Team[0].Players[0].Nationality" alt="" class="flag" />
              <span>&nbsp;&nbsp;</span>
              <span v-if='match.Team[0].Iswinner == "Winner"' class="highlight">
                {{ match.Team[0].Players[0].Name }}
              </span>
              <span v-else>{{ match.Team[0].Players[0].Name }}</span>
            </div>

            <div v-if="match.Team[0].Players.length>1" style="margin-top:10px;">
              <img :src="match.Team[0].Players[1].Nationality" alt="" class="flag" />
              <span>&nbsp;&nbsp;</span>
              <span v-if='match.Team[0].Iswinner == "Winner"' class="highlight">
                {{ match.Team[0].Players[1].Name }}
              </span>
              <span v-else>{{ match.Team[0].Players[1].Name }}</span>
            </div> 
            </div>
              
            <div style="float:right">
              <span v-if='match.Team[0].Iswinner == "Winner"' class="highlight">

八.心路历程和收获

222000407:这次我负责后端部分,为了提前为后面的团队项目准备技术经验,我选择了go+gin来进行后端的开发,时间短暂却还要学习新的语言和新的框架,本来有一些焦虑和担忧,但是真正开始着手学习的时候发现编程语言之间还是有不少的融汇贯通之处的,在学习新技术的部分并没有遇到很多困难。我还认识到,前后端的开发要多进行交流来规范数据传输的一致性,后端要为前端提供数据正确性和便捷性的保证,这次的合作中我负责的后端部分由于传输的json数据没做好处理,导致给前端的同学添加了一些麻烦。还有在git的协作上也有了更深入的认识,懂得如何协调前后端的代码,处理冲突,本地代码发生难以解决的问题时,版本回退。

222000414:刚开始拿到作业的时候,我就想着要用Vue来进行前端开发,但是由于此前根本没用过Vue框架,所以就到b站上找了相应的视频进行学习。由于大多数的视频教程都是使用Vue2进行开发的,并且网上的资源很多都是关于Vue2的,所以在一些具体实现上会有差别。比如路由的配置,以及插槽slot的应用等等。这些问题都困扰了我好久,好在最后通过关键字查询以及队友的帮助,最终这些问题都得到了解决。此外,在这次开发中我还明白了整体布局的重要性,在进行前端开发的过程中要与后端保持紧密的联系,要保证前后端所需要内容的一致性,不然就会出现前端与后端代码协同使用时不一致的错误,导致开发停滞。

九.评价结对队友

222000407->222000414:很高兴能和LJJ同学合作完整这一次结对项目,从原型设计到具体的编程实现,LJJ同学都承担了不少的工作量,特别是具体实现部分,LJJ同学积极承担前端部分开发,在这之上LJJ同学也很配合我提出的新要求的实现,也包容了我后端部分出现的错误导致他不必要的时间浪费,总之,LJJ同学是个不可多得的队友。

222000414->222000407:HYJ同学学习能力很强,在没有尝试过Go+Gin的情况下,选择其进行后端开发,并且能够很快熟练应用。在我遇到问题的时候,会跟我积极探讨,总是能够给我不少灵感。总之,HYJ同学是个非常有责任心的队友,和他合作十分愉快。

...全文
328 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复

1.男女选手排名切换挺有意思;2.选择一个全新的后端框架是基于什么考虑?

688

社区成员

发帖
与我相关
我的任务
社区描述
2023年福州大学软件工程实践课程W班的教学社区
软件工程团队开发软件构建 高校 福建省·福州市
社区管理员
  • FZU_SE_teacherW
  • 张书旖
  • 郭渊伟
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧