139
社区成员
![](https://csdnimg.cn/release/cmsfe/public/img/topic.427195d5.png)
![](https://csdnimg.cn/release/cmsfe/public/img/me.40a70ab0.png)
![](https://csdnimg.cn/release/cmsfe/public/img/task.87b52881.png)
![](https://csdnimg.cn/release/cmsfe/public/img/share-circle.3e0b7822.png)
这个作业属于哪个课程 | 2022年福大-软件工程;软件工程实践-W班 |
---|---|
这个作业要求在哪里 | 软件工程实践结对作业二 |
结对学号 | 221900217、221900336 |
这个作业的目标 | 编码结对合作,部署云服务器 |
其他参考文献 | 2022.cctv.com |
https://gitcode.net/DAYTOYk/pairproject
https://gitcode.net/DAYTOYk/pairproject/-/blob/dev/codestyle.md
http://101.132.99.44:8080/index.html
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 |
首页概览
首页主要由header、content和footer组成,展示了冬奥的发展历史和本次赛事中国的夺冠事时刻。
奖牌榜
展示了本次参赛国家的获奖情况,其中中国高亮。
由于参赛国家较多,这边做了分页,根据页数和页面大小从后端获取数据。
每日赛程
展示冬奥会的所有比赛基本情况,可通过日期、场馆、项目结合搜索,也可以重置数据,默认显示全部数据,因为数据过多,这里也做了分页处理。
三个选择内容可一起使用。
结合没搜索到数据时,列表显示“暂无数据”。
奖牌地图
通过世界地图展示参赛国家的获奖情况,颜色由浅至深,颜色越深获奖数目越多。
鼠标悬停到某个国家,高亮此国家面板,并显示对应国家获奖情况。
赛程详情
雪橇、冬季两项和跳台滑雪写了详情界面。
其余没编写的界面,点击详情,显示“暂无数据”。
首先拿到题目先分析项目要求,分为基础功能点和扩展功能点,以及部署到云服务器,画出大致的功能结构图,讨论如何实现。最后采取前后端分离的模式,即一名队员主攻前端,一名队友主攻后端,分别学习不同的技术,同时部署云服务器归为后端任务。通过寻找相关技术框架,例如前端vue,后端springboot进行系统学习,跟随教学视频学习后集成开发部署。
前端:vue3 + vite + ts + antd + axios
后端:springboot + mybatis-plus
功能结构图
代码结构图:
前端:
后端:
思路:因为这次的请求都是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;
思路:主要是用了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"
/>
思路:引了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);
思路:先注册好设计结构中的页面组件,在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,
});
思路:根据后端传的数据定义类型接口,以便后续使用
实现:
export interface dailyItem {
matchId: number,
startdatecn: string,
itemcodename: string,
homename: string,
awayname: string,
title: string,
venuename: string
}
思路:通过在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);
}
思路:采用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> {
}
统一封装结果集,作用:前后端数据交互
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秒内,不需要再发送预检验请求,可以缓存该结果
}
};
}
}
221900217黄欧成:学习了springboot的三层架构,以及如何部署到云服务器,同时也了解到web页面的逻辑处理,以及一些界面元素的巧妙使用,还是收获颇丰的。
221900336洪曙新:这次实践学习了vue3以及vite框架的新特性,中间因为版本升级原因踩了不少坑,好在最后都得以解决。此外还学习了vue全家桶的其他东西比如vue-router等等,对axios进行二次封装,自定义组件并复用,优化代码结构。另外还使用了一些antd的小组件,提高了开发效率。
221900217黄欧成:洪曙新同学在学习过程中非常有上进心很能带动我有去学习新技术的动力,并且我艺术审美有限,她很好补缺补漏了我这方面的缺点,并且在合作过程中非常耐心做事情非常有条理,会跟你说这些的原理,我的评价是跟着大佬学到了很多。
221900336洪曙新:黄欧成同学有很强的编程能力,对于新东西上手得特别快,因此在联调的时候效率很高,基本没什么bug,接口调得非常顺利。在讨论过程中,他对于项目的搭建以及时间规划都非常合理,开发节奏很舒适,面对问题也会帮忙一起寻找解决方法,我的评价是被大佬带的感觉真好。
界面的背景图挺好的,颜色不突兀,和谐!结对结束后,再回头分析前后端分工,你们觉得是最合适的吗?