239
社区成员




这个作业属于哪个课程 | FZU_SE_teacherW_4 |
---|---|
这个作业要求在哪里 | 软工实践——CodeArts团队实战总结 |
团队名称 | 丰农汇 |
这个作业的目标 | 开发一个奥运比赛竞猜平台,实现GUI以及基础,附加功能 |
其他参考文献 | 构建之法 |
截图信息(部分)
次数统计
学号 提交次数 222200302 5 222200301 6 222200303 4 222200426 7 222200417 4 222100110 4 222200427 5 222200332 3
我们的前端项目使用Vue.js
+ Vite
开发,最后使用Vite
打包构建
Vite构建项目命令 pnpm vite build
构建好项目之后再把项目放到云服务上的Nginx
服务器中
我们的前端项目最后部署在 http://8.130.92.86/
N(Need,需求)
体育迷对于参与奥运赛事的热情和兴趣日益增长,他们渴望能够通过一种互动性强、趣味性高的方式参与到奥运会中。用户需要一个平台,可以让他们预测比赛结果,增加观看比赛的参与感和紧张感。
A(Approach,做法)
开发一个在线竞猜平台,用户可以在上面选择不同的比赛进行预测和下注。
协同使用CodeArts进行团队开发。
前端使用ES6,Vue 3,Pinia,Vite,Axios,Element-Plus
后端使用Springboot、MybatisPlus、MySQL、Redis
B (Benefit,好处)
用户可以通过竞猜平台增加观看比赛的乐趣,提高参与度和兴奋感。
平台无需下载,占用内存空间小,运行速度快。
页面简洁,操作简单,对于初次接触的用户十分友好。
C (Competitors,竞争)
我们的主要竞争对手为其他小组的项目。
我方优势
模块清晰,操作简单,界面美观,用户体验好。
无需下载安装,使用方便。
我方劣势
数据主要从官方网站爬取,可能存在数据较少等问题。
我们的平台结构简单,功能较少。
D(Delivery,推广)
可以把平台介绍给身边的同学和朋友等,反馈不错的话再让他们的家人朋友分享给身边的人。
通过微博,b站等社交平台推广,同时给用户提供反馈方式,积极维护提问和评论区。
可以与体育论坛合作,吸引体育迷关注和使用我们的竞猜平台。
功能模块图
用户首先进行注册,设置账号和密码,账号为手机号码。
用户可以通过筛选器,选择比赛日期,包括单场的比赛项目、开始时间、截止时间、选手A、选手B的信息
点击竞猜按键可以选择选手进行投票
当竞猜项目结束的时候,会弹窗提示用户已经完成的竞猜项目
我们设计了4张数据库表,分别是用户表user、竞猜表guess、竞猜投票记录表vote_record、队伍表team,并且我们将数据库部署在服务器上
竞猜表guess:
队伍表team:
用户表user:
竞猜投票记录表vote_record:
Vue.js
Element Plus
pnpm包管理
Pinia持久化存储
Eslint + prettier 代码规范校验
husky git提交验证
SpringBoot框架
mysql数据库
mybatis持久层框架
Jwt 生成和校验Token
共同讨论了接口设计,通过Apifox进行共同设计接口和调试(接口设计地址)
登录接口:
为了保证网站资源的安全性,我们需要进行一个简单的认证处理,如果登录成功,后端会返回一串jwt的token串,要求前端每次在请求头携带上token串,后端利用进行拦截器进行请求拦截,解析令牌
JwtUtil (Jwt token生成和解析类)
public class JwtUtil {
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘钥
*
* @param secretKey jwt秘钥
* @param ttlMillis jwt过期时间(毫秒)
* @param claims 设置的信息
* @return
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定签名的时候使用的签名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的时间
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
// 设置过期时间
.setExpiration(exp);
return builder.compact();
}
/**
* Token解密
*
* @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
* @param token 加密后的token
* @return
*/
public static Claims parseJWT(String secretKey, String token) {
// 得到DefaultJwtParser
Claims claims = Jwts.parser()
// 设置签名的秘钥
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 设置需要解析的jwt
.parseClaimsJws(token).getBody();
return claims;
}
}
拦截器方法实现
/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
//1、从请求头中获取令牌
String token = request.getHeader(userTokenName);
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(userSecretKey, token);
Long userId = Long.valueOf(claims.get("userId").toString());
UserHolder.saveUserId(userId);
log.info("当前用户id:", userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
业务层代码(service),我们会在jwt中存放用户id,但我们不会存放敏感信息如密码、电话等(安全性考虑)
@Override
public Result<UserLoginVo> login(UserParamsDto userParamsDto) {
User user = userMapper.getUser(userParamsDto);
if(user == null) {
return Result.error("用户名或密码输入错误");
}
//登录成功后,生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
String token = JwtUtil.createJWT(
userSecretKey,
userTtl,
claims);
UserLoginVo userLoginVo = UserLoginVo.builder()
.token(token)
.build();
return Result.success(userLoginVo);
}
注册接口:
前端发送请求到后端,后端获取密码、手机号码。接着后端判断手机号码是否唯一,如果唯一,那么成功注册,否则注册失败,并提示用户相应的信息
业务层代码
/**
* 用户注册
* @param userParamsDto
* @return
*/
@Override
public Result register(UserParamsDto userParamsDto) {
User userByPhone = userMapper.getUserByPhone(userParamsDto);
if(userByPhone != null) {
return Result.error("用户已经存在,无需注册");
}
User user = new User();
BeanUtil.copyProperties(userParamsDto, user);
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userMapper.insert(user);
return Result.success("注册成功");
}
修改用户信息接口:(略:不展示)
修改用户密码接口:(略:不展示)
投票接口:
前端传入竞猜比赛id和队伍id,后端接收后写入竞猜记录表,前端为啥不需要传入用户id呢,因为登录后我们将用户id存入token中传回前端,前端每次请求后端接口都会携带token,我们通过拦截器拦截请求,并从请求中获取token解析出用户id存入UserHolder中,UserHolder是我们自己编写的一个工具类,底层是ThreadLocal,所以当业务层需要用户id时直接从UserHolder中获取即可
UserHolder工具类
public class UserHolder {
private static final ThreadLocal<Long> tl = new ThreadLocal<>();
public static void saveUserId(Long id){
tl.set(id);
}
public static Long getUserId(){
return tl.get();
}
public static void removeUserId(){
tl.remove();
}
}
接口层
@PostMapping("/vote")
public Result vote(@RequestBody Map map) {
return guessService.vote(map);
}
业务层(service)
@Override
public Result vote(Map map) {
Long guessId = Long.valueOf(map.get("guessId").toString());
Long userId = UserHolder.getUserId();
VoteRecord voteRecord = voteRecordMapper.selectByGuessIdAndUserId(guessId, userId);
if(voteRecord != null) {
return Result.error("你已经在本次比赛提交过竞猜,不能重复提交");
}
voteRecord = new VoteRecord();
voteRecord.setGuessId(guessId);
voteRecord.setTeamId(Long.valueOf(map.get("teamId").toString()));
voteRecord.setUserId(userId);
voteRecordMapper.insert(voteRecord);
return Result.success("投票成功");
}
获取竞猜列表接口:
接口层:
@GetMapping("/list/{date}")
public Result<List<GuessVo>> getGuessList(@PathVariable("date") String date) {
return guessService.guessList(date);
}
业务层:将前端通过路径传入的日期参数(如,”0724“)解析出来查询数据库对应的竞猜列表,还需返回每支队伍的名字和已获得的票数
/**
* 获取竞猜列表
* @param date 0727
*/
@Override
public Result<List<GuessVo>> guessList(String date) {
//处理日期
String month = date.substring(0, 2);
String day = date.substring(2);
LocalDate localDate =
LocalDate.of(2024, Integer.parseInt(month), Integer.parseInt(day));
LocalDateTime beginOfDay = LocalDateTime.of(localDate, LocalTime.MIN);
LocalDateTime endOfDay = LocalDateTime.of(localDate, LocalTime.MAX);
//查询
List<GuessVo> guessList = guessMapper.selectList(beginOfDay, endOfDay);
List<Long> teamAIds = CollUtil.getFieldValues(guessList, "teamaId", Long.class);
List<Long> teamBIds = CollUtil.getFieldValues(guessList, "teambId", Long.class);
List<Team> teamAList = teamMapper.selectByIds(teamAIds);
List<Team> teamBList = teamMapper.selectByIds(teamBIds);
Map<Long, Team> mapA = CollUtil.fieldValueMap(teamAList, "id");
Map<Long, Team> mapB = CollUtil.fieldValueMap(teamBList, "id");
guessList.forEach(
item -> {
item.setTeamaName(mapA.get(item.getTeamaId()).getRegion());
item.setTeambName(mapB.get(item.getTeambId()).getRegion());
item.setWinnerTeamName(mapA.get(item.getTeamaId()).getRegion());
Integer voteA = voteRecordMapper.selectCountByTeamId(item.getTeamaId(), item.getId());
Integer voteB = voteRecordMapper.selectCountByTeamId(item.getTeambId(), item.getId());
item.setTeamaVotes(voteA);
item.setTeambVotes(voteB);
}
);
return Result.success(guessList);
}
获取我参与过的竞猜列表接口:
接口层
@GetMapping("/myList")
public Result<List<MyGuessVo>> getMyVoteList() {
return guessService.getMyVoteList();
}
业务层:先从竞猜记录表中获取该用户投过的记录,利用获取的竞猜id返回竞猜赛事基本信息,并且通过判断比赛结束决定是否返回胜者消息和用户是否猜对的信息,至于为何前端不需要传入userId在前面投票接口已经解释过
@Override
public Result<List<MyGuessVo>> getMyVoteList() {
Long userId = UserHolder.getUserId();
List<VoteRecord> list = voteRecordMapper.selectByUserId(userId);
List<MyGuessVo> ans = new ArrayList<>(list.size());
for(int i = 0; i < list.size(); ++i) {
MyGuessVo myGuessVo = new MyGuessVo();
VoteRecord voteRecord = list.get(i);
Long guessId = voteRecord.getGuessId();
GuessVo guessVo = guessMapper.selectById(guessId);
Long teamId = voteRecord.getTeamId();
Team team = teamMapper.selectById(teamId);
myGuessVo.setVoteTeamName(team.getRegion());
myGuessVo.setEndTime(guessVo.getEndTime());
myGuessVo.setGameName(guessVo.getGameName());
if(myGuessVo.getEndTime().isBefore(LocalDateTime.now())) {
myGuessVo.setIsCorrect(guessVo.getWinnerTeam().equals(teamId));
myGuessVo.setWinnerTeamName(teamMapper.selectById(guessVo.getWinnerTeam()).getRegion());
}
ans.add(myGuessVo);
}
return Result.success(ans);
}
后端人员在编写接口时,运用http client(idea中下载的插件,下载可以直接在idea中完成接口的测试)
以用户测试登录接口为例 展现测试代码和测试结果
该方法十分方便可以直接在idea进行接口测试,对比postman是个更佳选择
用户登录进去后首页就是竞猜功能,直白明了。
在竞猜结束后会在,用户界面登录后弹窗通知竞赛结果。
使用手机号进行登录,确保一人一号
登录,通过账号密码登录
注册,通过手机号(用户名)注册,确保一人一号
竞猜页面,可以看到待竞猜的各种比赛
竞猜过程,点击投票按钮可以选择竞猜的队伍,竞猜结束切换到“我的竞猜结果”会有弹窗提示竞猜结果
个人页面,可以修改姓名地址等个人信息,还可以修改密码
学号 | 分工 | 贡献比例 |
---|---|---|
222200302 | 解决小组内出现的小问题,提供接口设计和数据库设计,搭建项目结构(包括依赖、配置)完成本项目业务类编写和配置类编写 | 18.5 |
222200301 | 完成部分后端数据持久层接口编写 | 9.5 |
222200303 | 完成部分后端数据持久层接口编写和部分VO类的设计 | 10 |
222200426 | 完成实体类和VO类的设计,编写博客 | 11.5 |
222200417 | 完成DTO类的设计和工具类的编写 | 9.5 |
222100110 | 解决小组内出现的小问题,参与接口设计和数据库设计,搭建前端项目结构,完成前端项目页面渲染,前后端的对接,接口封装 | 18 |
222200427 | 实现了竞猜界面,对小组进行分工,编写博客 | 12.5 |
222200332 | 实现了登陆界面和页面弹窗 | 10.5 |
分为前端和后端小组,分别有110和302同学担任组长,他们两位对此次作业贡献度较大,解决了组员的很多问题
学号 | 遇到的困难 | 解决方法 |
---|---|---|
222200427郭翔宇 | 1. 使用 git push 时,出现了报错无法提交 2.时间紧迫,且是第一次经历一天时间做个小项目,组内同学掌握的技术水平差别较大,很慌 | 1. 发现是ESLint 被集成到了 Git 钩子(hooks)中,在提交时候会判断js的语法正确性,查看错误日志,重新修改不符合规范代码,使得符合规范 2.幸好两个小组长开发经验比较丰富,且善于指导组内编程较不熟练的同学,还是能及时完成任务 |
222100110吴宇航 | 1.我在前端项目中向后端发送请求时出现了跨域问题 2.后端的多个接口在一些输入下都会返回500状态码(服务器内部异常),导致前端无法正确获取数据。 3.使用vite构建项目之后再部署到Nginx服务上之后有一个问题,刷新页面,会提示404(页面没有找到),这种情况应该是页面刷新没有找到根路径导致的,或者当前的路径确实不存在(由于这是vue的路由,实际并没有这个路径) | 1.与后端及时沟通,让后端运行请求的跨域访问 2.及时联系后端,并且对这些接口进行联调 3.vite打包项目时base路径要设置为 / ,不要设置为 ./ (相对路径),同时Nginx服务器的配置也要加上一行 try_files $uri $uri/ /index.html; (没有找到文件或目录时再尝试访问index.html),通过这两个配置就解决了刷新页面提示404的问题了。 |
222200302方金田 | 1.如何保证系统安全性(认证安全) 2.后端传给前端的日期格式前端无法正常解析 | 1.利用Jwt生成令牌,用户访问需要携带令牌访问(JWT使用数字签名或加密算法对数据进行验证和防篡改,确保信息在传输过程中的安全性和完整性,并且使用JSON格式,易于解析和使用,这使得其在网络传输中更加高效) 2. @JsonFormat 注解:在传入前端实体类的时间字段上加上注解@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") |
222200417林宇涛 | 对java语言不熟悉 | 使用AI辅助工具,用gpt生成代码 |
222200303石江强 | 使用 git clone和push时提示没有权限 | 直接在华为云代码仓库上传文件 |
222200426徐俊杰 | 要准确理解每个类的功能和它们之间的关系比较困难,特别是在没有详细需求文档的情况下。 | 与其他人尤其的设计者交流,理解设计 |
222200301王珺琨 | 项目使用的技术栈与之前接触的不同,完成较为吃力 | 通过阅读官方文档、教程和示例代码,向团队成员请教,进行实际编码练习慢慢的渐入佳境 |
222200332康思梦 | 登录页面背景图片无法显示 | 调整图像大小 |
222200427郭翔宇
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 40 |
• Estimate | • 估计这个任务需要多少时间 | 60 | 40 |
Development | 开发 | 405 | 490 |
• Analysis | • 需求分析 (包括学习新技术) | 90 | 85 |
• Design Spec | • 生成设计文档 | 45 | 30 |
• Design Review | • 设计复审 | 30 | 45 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 20 |
• Design | • 具体设计 | 30 | 45 |
• Coding | • 具体编码 | 180 | 200 |
• Code Review | • 代码复审 | 15 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 45 |
Reporting | 报告 | 90 | 70 |
• Test Repor | • 测试报告 | 30 | 30 |
• Size Measurement | • 计算工作量 | 15 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 45 | 30 |
合计 | 555 | 600 |
222100110吴宇航
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 70 |
• Estimate | • 估计这个任务需要多少时间 | 60 | 70 |
Development | 开发 | 750 | 800 |
• Analysis | • 需求分析 (包括学习新技术) | 150 | 160 |
• Design Spec | • 生成设计文档 | 50 | 55 |
• Design Review | • 设计复审 | 50 | 55 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | • 具体设计 | 80 | 85 |
• Coding | • 具体编码 | 250 | 270 |
• Code Review | • 代码复审 | 50 | 60 |
• Test | • 测试(自我测试,修改代码,提交修改) | 100 | 95 |
Reporting | 报告 | 90 | 96 |
• Test Report | • 测试报告 | 30 | 35 |
• Size Measurement | • 计算工作量 | 30 | 32 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 29 |
合计 | 900 | 966 |
222200302方金田
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 50 | 60 |
• Estimate | • 估计这个任务需要多少时间 | 50 | 60 |
Development | 开发 | 810 | 885 |
• Analysis | • 需求分析 (包括学习新技术) | 140 | 150 |
• Design Spec | • 生成设计文档 | 60 | 70 |
• Design Review | • 设计复审 | 40 | 45 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
• Design | • 具体设计 | 80 | 85 |
• Coding | • 具体编码 | 300 | 330 |
• Code Review | • 代码复审 | 60 | 70 |
• Test | • 测试(自我测试,修改代码,提交修改) | 100 | 105 |
Reporting | 报告 | 80 | 85 |
• Test Report | • 测试报告 | 30 | 35 |
• Size Measurement | • 计算工作量 | 20 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1000 | 966 |
222200417林宇涛
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 35 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 35 |
Development | 开发 | 370 | 405 |
• Analysis | • 需求分析 (包括学习新技术) | 60 | 65 |
• Design Spec | • 生成设计文档 | 30 | 35 |
• Design Review | • 设计复审 | 20 | 25 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 10 |
• Design | • 具体设计 | 50 | 55 |
• Coding | • 具体编码 | 120 | 140 |
• Code Review | • 代码复审 | 30 | 25 |
• Test | • 测试(自我测试,修改代码,提交修改) | 45 | 50 |
Reporting | 报告 | 25 | 30 |
• Test Report | • 测试报告 | 10 | 15 |
• Size Measurement | • 计算工作量 | 5 | 5 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 425 | 470 |
222200426徐俊杰
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 400 | 440 |
• Analysis | • 需求分析 (包括学习新技术) | 60 | 70 |
• Design Spec | • 生成设计文档 | 20 | 20 |
• Design Review | • 设计复审 | 30 | 30 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | • 具体设计 | 30 | 40 |
• Coding | • 具体编码 | 200 | 230 |
• Code Review | • 代码复审 | 20 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 10 | 10 |
Reporting | 报告 | 30 | 30 |
• Test Repor | • 测试报告 | 10 | 10 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 460 | 500 |
222200301王珺琨
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 450 | 485 |
• Analysis | • 需求分析 (包括学习新技术) | 90 | 120 |
• Design Spec | • 生成设计文档 | 30 | 30 |
• Design Review | • 设计复审 | 30 | 35 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 60 | 60 |
• Coding | • 具体编码 | 140 | 150 |
• Code Review | • 代码复审 | 30 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 60 |
Reporting | 报告 | 25 | 30 |
• Test Report | • 测试报告 | 10 | 15 |
• Size Measurement | • 计算工作量 | 5 | 5 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 505 | 545 |
222200332康思梦
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 40 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 40 |
Development | 开发 | 375 | 403 |
• Analysis | • 需求分析 (包括学习新技术) | 50 | 55 |
• Design Spec | • 生成设计文档 | 5 | 5 |
• Design Review | • 设计复审 | 10 | 8 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 12 |
• Design | • 具体设计 | 200 | 210 |
• Coding | • 具体编码 | 50 | 60 |
• Code Review | • 代码复审 | 25 | 28 |
• Test | • 测试(自我测试,修改代码,提交修改) | 25 | 25 |
Reporting | 报告 | 15 | 20 |
• Test Repor | • 测试报告 | 5 | 8 |
• Size Measurement | • 计算工作量 | 5 | 5 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 5 | 7 |
合计 | 420 | 463 |
222200303石江强
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 650 | 630 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 100 |
• Design Spec | • 生成设计文档 | 20 | 20 |
• Design Review | • 设计复审 | 20 | 20 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
• Design | • 具体设计 | 60 | 50 |
• Coding | • 具体编码 | 350 | 370 |
• Code Review | • 代码复审 | 20 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 30 |
Reporting | 报告 | 60 | 70 |
• Test Repor | • 测试报告 | 30 | 30 |
• Size Measurement | • 计算工作量 | 10 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 730 | 720 |