软件工程实践结对作业二

221900336洪曙新 学生 2022-03-25 11:16:59
这个作业属于哪个课程2022年福大-软件工程;软件工程实践-W班
这个作业要求在哪里软件工程实践结对作业二
结对学号221900217、221900336
这个作业的目标编码结对合作,部署云服务器
其他参考文献2022.cctv.com

目录

  • 1.项目链接
  • 1.1Gitcode仓库地址
  • 1.2代码规范链接
  • 1.3云服务器地址
  • 2.PSP表格
  • 3.成品展示
  • 4.结对讨论过程描述
  • 4.1解题思路
  • 4.2 讨论过程
  • 5设计实现过程
  • 5.1使用技术
  • 5.2 功能及项目结构图
  • 6.代码说明
  • 6.1前端
  • 6.1.1 封装axios
  • 6.1.2 分页和选择器
  • 6.1.3 世界地图
  • 6.1.4 路由配置
  • 6.1.5 数据类型定义
  • 6.2后端
  • 6.2.1爬虫实现
  • 6.2.2后端数据接口实现
  • 6.2.3代码补充说明
  • 7.心路历程和收获
  • 8.评价结对队友

1.项目链接

1.1Gitcode仓库地址

https://gitcode.net/DAYTOYk/pairproject


1.2代码规范链接

https://gitcode.net/DAYTOYk/pairproject/-/blob/dev/codestyle.md


1.3云服务器地址

http://101.132.99.44:8080/index.html


2.PSP表格

PSP预估消耗时间(分钟)实际消耗时间(分钟)
计划
•估计这个任务需要多久510
开发
•需求理解与分析1020
•技术学习180240
•分工讨论2050
•制定代码规范1010
•编程240280
•代码复审2015
•部署云服务2030
•撰写博客6090
•计算工作量33
•事后总结2020
总计588768

3.成品展示

  • 首页概览

    首页主要由header、content和footer组成,展示了冬奥的发展历史和本次赛事中国的夺冠事时刻。

img

  • 奖牌榜

    展示了本次参赛国家的获奖情况,其中中国高亮。

img

由于参赛国家较多,这边做了分页,根据页数和页面大小从后端获取数据。

img

  • 每日赛程

    展示冬奥会的所有比赛基本情况,可通过日期、场馆、项目结合搜索,也可以重置数据,默认显示全部数据,因为数据过多,这里也做了分页处理。

img

三个选择内容可一起使用。

img

结合没搜索到数据时,列表显示“暂无数据”。

img

  • 奖牌地图

    通过世界地图展示参赛国家的获奖情况,颜色由浅至深,颜色越深获奖数目越多。

img

鼠标悬停到某个国家,高亮此国家面板,并显示对应国家获奖情况。

img

  • 赛程详情

    雪橇、冬季两项和跳台滑雪写了详情界面。

img

其余没编写的界面,点击详情,显示“暂无数据”。

img

4.结对讨论过程描述

4.1解题思路

首先拿到题目先分析项目要求,分为基础功能点和扩展功能点,以及部署到云服务器,画出大致的功能结构图,讨论如何实现。最后采取前后端分离的模式,即一名队员主攻前端,一名队友主攻后端,分别学习不同的技术,同时部署云服务器归为后端任务。通过寻找相关技术框架,例如前端vue,后端springboot进行系统学习,跟随教学视频学习后集成开发部署。

4.2 讨论过程

img

img

img

img

img

img

5设计实现过程

5.1使用技术

前端:vue3 + vite + ts + antd + axios

后端:springboot + mybatis-plus

5.2 功能及项目结构图

功能结构图

img

代码结构图:

前端:

img

后端:

img

6.代码说明

6.1前端

6.1.1 封装axios

思路:因为这次的请求都是get或者post,所以主要封装了这两个方法,将get和post方法封装成Promise对象返回,调用的时候用async/await异步请求。

实现:

http.ts封装配置

import axios from "axios"; // 引用axios
import config from "./config";        //baseUrl,请求地址

const instance = axios.create({
  baseURL: config.baseUrl.dev,
  timeout: 60000,
});
//get请求
export function get(url: string, params = {}) {
    return new Promise((resolve, reject) => {
      instance
        .get(url, {
          params: params,
        })
        .then((response) => {
          resolve(response.data);
        })
        .catch((err) => {
          reject(err);
        });
    });
}
//post请求
export function post(url: string, data = {}) {
  return new Promise((resolve, reject) => {
    instance.post(url, data).then(
      (response) => {
        resolve(response.data);
      },
      (err) => {
        reject(err);
      }
    );
  });
}

index.ts统一暴露调用方法

import { get, post } from './http';

// 获取奖牌总榜数据
export const reqGetTotal = (data : any) => get("/total",data);
        ...

页面调用

 // 获取奖牌榜数据
const getTotal = async(page = 1, pageSize = 10) => {
   let res:any = await reqGetTotal({page, size: pageSize});
   if(res.code!=0){
       message.warning(res.msg);
       return;
   }
   state.list = res.data.list;
   state.total = res.data.total;
}

// 对Total数据的类型封装 totalConfig.ts
interface TotalItem {
    gold: string, 
    countryName: string, 
    count: string, 
    medalRank: string, 
    silver: string, 
    countryId: string, 
    bronze: string,
    medalId?: string
}

export default TotalItem;

6.1.2 分页和选择器

思路:主要是用了antd的组件,根据官方文档的参数配置,选择项从后端获取

实现:

// 选择器
<a-select v-model:value="form.site" @change="handleSelectChange">
    <a-select-option
        v-for="(item, index) of siteOption"
        v-bind:key="item"
        v-model:value="siteOption[index]"
    >{{ item }}</a-select-option>
</a-select>

// 分页
<a-pagination
    v-model:current="current"
    :total="total"
    show-less-items
    @change="handlePageChange"
/>

6.1.3 世界地图

思路:引了echarts的地图,一开始引入echarts有问题,后来才知道5.0以上版本的echarts没有module.export或者export default,改成了以下引用方式。option配置也是根据官网的配置调整的。

实现:

import * as echarts from "echarts";

let myChart = echarts.init(state.chartBox);
let option = [
    // 地图配置
    ...
]

myChart.setOption(option);

6.1.4 路由配置

思路:先注册好设计结构中的页面组件,在route文件里面进行路由配置。

实现:

import { createRouter, createWebHistory } from "vue-router";
import Home from '../components/Home/index.vue';
//...

const routes = [
    {
      path: "/home",
      name: "home",
      component: Home,
    },
     ...
];

export default createRouter({
  history: createWebHistory(),
  routes,
});

6.1.5 数据类型定义

思路:根据后端传的数据定义类型接口,以便后续使用

实现:

export interface dailyItem {
    matchId: number,
    startdatecn: string, 
    itemcodename: string,
    homename: string,
    awayname: string,
    title: string,
    venuename: string
}

6.2后端

6.2.1爬虫实现

思路:通过在springboot项目中添加HttpClient的依赖,编码实现get的请求获取网站json数据并通过解析json数据获取相应数据。

实现:

public static String sendGet(String url){
        //生成httpclient,相当于该打开一个浏览器
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //设置请求和传输超时时间(工厂模式)
        RequestConfig requestConfig = RequestConfig
                .custom()
                .setSocketTimeout(2000)
                .setConnectTimeout(2000)
                .build();
        CloseableHttpResponse response = null;
        String result = null;
        //创建get请求
        HttpGet request = new HttpGet(url);
        try {
            //设置用户代理
            request.setHeader("User-Agent",USER_AGENT);
            request.setConfig(requestConfig);
            //执行get请求
            response = httpClient.execute(request);
            //判断响应状态是否为200,进行处理
            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                //获取响应内容
                HttpEntity httpEntity = response.getEntity();
                result = EntityUtils.toString(httpEntity, "GBK");
            } else {
                //如果返回状态不是200,比如404(页面不存在)等,根据情况做处理,这里略
                log.info("请求码为:"+response.getStatusLine().getStatusCode());
                log.info(EntityUtils.toString(response.getEntity(), "utf-8"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //finally关闭
            HttpClientUtils.closeQuietly(response);
            HttpClientUtils.closeQuietly(httpClient);
        }
        return result;
    }

通过上述代码获取网络请求的返回json数据。通过编写相应工具类解析json数据再通过dao层方法调用接口存储数据库。


代码补充

//保管网络请求地址
public class URLUtils {
    public final static String TOTAL_MEDAL = "https://api.cntv.cn/olympic/getBjOlyMedals?serviceId=2022dongao&itemcode=GEN-------------------------------&t=jsonp&cb=omedals1";
    public final static String SCHEDULE_MATCH = "https://api.cntv.cn/Olympic/getBjOlyMatchList?startdatecn=%s&t=jsonp&cb=OM&serviceId=2022dongao";

    //返回特定日期网络请求地址
    public static String getScheduleMatchUrl(String date){
        return String.format(SCHEDULE_MATCH,date);
    }
}
//json数据解析
public class JsonUtils {
    public static List<Medal> parseToOlympicMedals(String jsonData);
    public static List<MatchEntity> parseToOlympicMatches(String jsonData);
}

6.2.2后端数据接口实现

思路:采用springboot + mybatis-plus,编写相应的controller层,service层,以及mapper层

实现:以奖牌榜数据为例子

controller:

@GetMapping("/total")
public RestResult totalMedals(int page,int size){
    return olympicService.getOlympicTotalMedals(page,size);
}

service:

@Override
public RestResult getOlympicTotalMedals(Integer page, Integer size) {
    try {
        Page<MedalEntity> medalsByPage = medalService.findMedalsByPage(new Page<>(page, size));
        RestResult result = new RestResult();
        result.putList(medalsByPage.getRecords());
        result.putTotal(medalsByPage.getTotal());
        return result;
    }catch (Exception e){
        e.printStackTrace();
        return ResultUtils.systemError();
    }
}

mapper:

@Mapper
public interface MedalMapper extends BaseMapper<MedalEntity> {
}

6.2.3代码补充说明

统一封装结果集,作用:前后端数据交互

public class RestResult {
    private int code;
    private String msg;
    private Object data;

    @SuppressWarnings("unchecked")
    public void put(String key, Object data) {
        if (this.data == null) {
            this.data = new HashMap<String, Object>();
        }
        ((Map) this.data).put(key, data);
    }

    @SuppressWarnings("unchecked")
    public void putData(Object data) {
        if (this.data == null) {
            this.data = new HashMap<String, Object>();
        }
        ((Map) this.data).put("data", data);
    }
    @SuppressWarnings("unchecked")
    public void putList(Object data) {
        if (this.data == null) {
            this.data = new HashMap<String, Object>();
        }
        ((Map) this.data).put("list", data);
    }

    @SuppressWarnings("unchecked")
    public void putTotal(Object total) {
        if (this.data == null) {
            this.data = new HashMap<String, Object>();
        }
        ((Map) this.data).put("total", total);
    }
}

网络配置,作用:避免跨域问题

public class MyWebConfig implements WebMvcConfigurer {
// 设置允许跨域请求
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry
                        .addMapping("/**")
                        // 允许跨域的域名
                        .allowedOriginPatterns("*") // 允许所有域
                        .allowedMethods("*") // 允许任何方法(post、get等)
                        .allowedHeaders("*") // 允许任何请求头
                        .allowCredentials(true) // 允许证书、cookie
                        .exposedHeaders(HttpHeaders.SET_COOKIE)
                        .maxAge(3600L); // maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
            }
        };
    }
}

7.心路历程和收获

221900217黄欧成:学习了springboot的三层架构,以及如何部署到云服务器,同时也了解到web页面的逻辑处理,以及一些界面元素的巧妙使用,还是收获颇丰的。

221900336洪曙新:这次实践学习了vue3以及vite框架的新特性,中间因为版本升级原因踩了不少坑,好在最后都得以解决。此外还学习了vue全家桶的其他东西比如vue-router等等,对axios进行二次封装,自定义组件并复用,优化代码结构。另外还使用了一些antd的小组件,提高了开发效率。

8.评价结对队友

221900217黄欧成:洪曙新同学在学习过程中非常有上进心很能带动我有去学习新技术的动力,并且我艺术审美有限,她很好补缺补漏了我这方面的缺点,并且在合作过程中非常耐心做事情非常有条理,会跟你说这些的原理,我的评价是跟着大佬学到了很多。

221900336洪曙新:黄欧成同学有很强的编程能力,对于新东西上手得特别快,因此在联调的时候效率很高,基本没什么bug,接口调得非常顺利。在讨论过程中,他对于项目的搭建以及时间规划都非常合理,开发节奏很舒适,面对问题也会帮忙一起寻找解决方法,我的评价是被大佬带的感觉真好。

...全文
165 3 打赏 收藏 举报
写回复
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jingbin-Wang 教师 03-26
  • 打赏
  • 举报
回复

界面的背景图挺好的,颜色不突兀,和谐!结对结束后,再回头分析前后端分工,你们觉得是最合适的吗?

221900336洪曙新 学生 03-26
  • 举报
回复
@FZU_SE_teacherW 谢谢老师!关于分工,我们觉得挺好的,因为两个人技术领域不同,刚好一个前端一个后端,大家各司其职。
  • 举报
回复
@FZU_SE_teacherW 可能说不算最合适的,因为针对这次作业其实分为了两大块,因为时间的原因并且技术也比较难学决定分工合作,一个人负责学习前端知识一个人负责学习后端学习,其实回过头发现这样也会有局限性,后端不清楚前端实现前端不清楚后端实现,总感觉缺少了什么,毕竟这是一个学习向的作业,所以还是希望在后面的中能够互相学习,毕竟全栈工程师来得更香,对于我而言创造一个令自己满意的页面也是很有成就感,在今后也会向洪曙新同学学习。
发帖
2022年福大-软件工程、实践-W班

136

社区成员

2022年福大-软件工程;软件工程实践-W班
软件工程 高校
社区管理员
  • FZU_SE_teacherW
  • 丝雨_xrc
  • Lyu-
加入社区
帖子事件
编辑了帖子 (查看)
2022-03-26 15:42
创建了帖子
2022-03-25 11:16
社区公告
暂无公告