软件工程实践结对作业二
| 这个作业属于哪个课程 | 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 | 预估消耗时间(分钟) | 实际消耗时间(分钟) |
|---|---|---|
| 计划 | ||
| •估计这个任务需要多久 | 5 | 10 |
| 开发 | ||
| •需求理解与分析 | 10 | 20 |
| •技术学习 | 180 | 240 |
| •分工讨论 | 20 | 50 |
| •制定代码规范 | 10 | 10 |
| •编程 | 240 | 280 |
| •代码复审 | 20 | 15 |
| •部署云服务 | 20 | 30 |
| •撰写博客 | 60 | 90 |
| •计算工作量 | 3 | 3 |
| •事后总结 | 20 | 20 |
| 总计 | 588 | 768 |
3.成品展示
首页概览
首页主要由header、content和footer组成,展示了冬奥的发展历史和本次赛事中国的夺冠事时刻。

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

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

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

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

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

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

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

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

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

4.结对讨论过程描述
4.1解题思路
首先拿到题目先分析项目要求,分为基础功能点和扩展功能点,以及部署到云服务器,画出大致的功能结构图,讨论如何实现。最后采取前后端分离的模式,即一名队员主攻前端,一名队友主攻后端,分别学习不同的技术,同时部署云服务器归为后端任务。通过寻找相关技术框架,例如前端vue,后端springboot进行系统学习,跟随教学视频学习后集成开发部署。
4.2 讨论过程
5设计实现过程
5.1使用技术
前端:vue3 + vite + ts + antd + axios
后端:springboot + mybatis-plus
5.2 功能及项目结构图
功能结构图

代码结构图:
前端:

后端:

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,接口调得非常顺利。在讨论过程中,他对于项目的搭建以及时间规划都非常合理,开发节奏很舒适,面对问题也会帮忙一起寻找解决方法,我的评价是被大佬带的感觉真好。
相关推荐
软件工程实践结对作业二.md
软件工程实践结对作业二.md
软件工程实践——结对作业一
这个作业属于哪个课程 软件工程实践2022年春-F班 这个作业要求在哪里 软件工程实践——结对作业一 结对学号 221900418 221900403 这个作业的目标 完成一个平台网页的原型设计 其他参考文献 CSDN 、BiliBili 、博客园 文章目录一、博客链接二、原型链接三、原型设计1、NABCD模型分析Ⅰ、N(Need,需求)Ⅱ、A(Approach,做法)Ⅲ、B(Benefit,好处)Ⅳ、C(Competitors,竞争)Ⅴ、D(Delivery,推广;Data...
软件工程实践结对作业一
这个作业属于哪个课程 <2022年福大-软件工程、实践-W班> 这个作业要求在哪里 <软件工程实践结对作业一> 这个作业的目标 <结对合作,NABCD模型,需求分析,原型设计> 结对学号 <221900112、221900104> 其他参考文献 见文章末尾 文章目录1、原型设计地址2、NABCD模型分析2.1 N(Need,需求)2.2 A(Approach,做法)2.3 B(Benefit,好处)2.4 C(Competit...
PairProject:W班软件工程实践结对第二次作业仓库
结对第二次作业-顶会热词统计的实现 为了不遗漏作业内容,这里有一个清单检查表方便同学们快速检阅。 fork仓库,和伙伴商讨如何通过github协作/代码规范等 编程实现 功能一,操作爬取的论文列表 功能二,爬取结果分析展示 功能三,爬取论文信息(附加功能,实现有附加分) 其他附加功能 部署 发布发布版本,发起拉取请求 撰写博客,在预先之前提交一份博客 包含作业描述和目录 核对评分标准 上一次结对作业中,大家都和小伙伴完成了原型的设计,那么现在再联合小伙伴动手实现原型中的部分功能吧。采用网络技术来实现原型中的功能。 一,作业内容 1.基础功能 功能1:对已爬取的论文列表进行操作 可对论文列表进行删除; 可对论文列表进行查询详细信息(支持模糊查询,查询结果的展示,排序等功能可自行设计); 功能2:分析已爬取到的论文信息,提取top10个热门领域或热门研究方向 形成如关键字图谱之类直观的查看方式
2020级计算机系软件工程第二次结对编程作业.zip
2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip 2020级计算机系软件工程第二次结对编程作业.zip


