度量指标-开发产能代码统计
1度量指标-开发产能代码统计
一、背景
公司度量开发指标,需要统计公司每个开发人员的有效代码计算,并作为开发人员的考核指标。包含所有开发语言的代码统计。之前功能也已经上线,没想到本次疫情居家办公以后,公司领导对于本次代码统计的指标尤其看重。故而,分享一下代码度量统计的相关功能。
二、已实现功能
Java代码默认无需配置,规则默认统计;
非Java代码,通过前端页面配置规则<匹配/不匹配>,配置完成以后,刷新缓存,按照配置规则统计代码;
正则匹配规则;
T-1 定时跑批、异步处理实现数据入库统计;
提供人工入口,实现可以实时恢复代码统计数据;
三、业务流程说明
主要实现增量代码行数统计功能,并实现代码增量过滤功能,最终统计出代码的增量行信息。
每天凌晨2:00调用生成增量代码统计接口; 增量代码更加commitid比较无差异(差异为0),判断存量数据库是否有数据,如果有,只更新update_time时间,流程结束; 若增量代码差异为0,且表中没有记录,说明该分支代码已经合并到master分支,流程结束; 若增量代码大于0,根据匹配规则过滤特定名称的增量代码; 匹配的增量代码,需要过滤import的增量,空格,注释不计入有效代码; 工具类生成的代码,比如:bo、vo、dto、example、xml等不计入有效代码统计; 只统计开发分支feature开头的分支,title merage的不计入统计;
四、数据库设计
4.1 详细表
字段 | 类型 | 长度 | 描述 |
---|---|---|---|
curr_date | 当前日期 | varchar(8) | |
project_name | 项目名称 | varchar(50) | |
git_address | 仓库地址 | varchar(100) | |
branch_name | 分支名称 | varchar(100) | |
commit_id | 提交编号 | varchar(8) | |
title | 标题 | varchar(1000) | |
additions | 增加行数 | int(11) | |
deletions | 删除行数 | int(11) | |
filtered_lines | 有效代码行数 | int(11) | |
author_name | 作者姓名 | varchar(100) | |
author_date | 提交日期 | datetime | |
committer_name | 提交人姓名 | varchar(100) | |
committer_date | 提交日期 | datetime | |
author_email | 作者邮箱 | varchar(100) | |
committer_email | 提交者邮箱 | varchar(100) | |
create_time | 创建时间 | datetime | |
update_time | 更新时间 | datetime | |
filtered_lines_map | 有效代码行数按照语言分类的map | varchar(512) |
4.2 统计表
该表(个人代码统计汇总)功能实现按照研发人员纬度,统计每一个人的增删行数,实现该研发人员的汇总代码统计。
字段 | 类型 | 长度 | 描述 |
---|---|---|---|
author_name | 作者姓名 | varchar(100) | |
additions | 增加行数 | int(11) | |
deletions | 删除行数 | int(11) | |
filtered_lines | 有效代码行数 | int(11) | |
committer_date | 提交日期 | datetime | |
curr_date | 统计时间 | datetime | |
create_time | 创建时间 | datetime | |
update_time | 更新时间 | datetime |
五、定时触发统计
//个人的代码统计,每天凌晨一点主动出发
@Scheduled (cron = "0 0 1 * * ?")
public void personCodeStatistics(){
BatchBo bo = new BatchBo();
dto.setBatchEnum(BatchEnum.PERSON_CODE_STATISTICS);
serviceManager.getPersonCodeStatistics().execute(dto);
}
六、Git API调研结论
万事万物,官方API实现API[1]的背景下,确定需要明确的接口整合,来实现功能。
可行性分析:
获取所有project 遍历project获取所有分支,筛选出最后提交时间的分支编号 根据分支编号获取该分支提交信息,根据提交时间筛选满足提交的提交 查询当前的提交详情,并统计增量代码,提交人等信息 统计有效增加代码
涉及到的API接口:
查询项目 /api/v4/projects 查询分支 /api/v4/projects/{porjectsid}/repository/branches 查询提交列表 /api/v4/projects/{projectsid}/repository/commits 查询提交详情 /api/v4/projects/{projectsid}/repository/commits/{commitId} 当前版本差异。/api/v4/projects/{projectsid}/repository/commits/{commitId}/diff
七、功能实现开发
部分核心开发代码如下
@Service
public class PersonCodeChangeStatisticsImpl implements IBatchService {
/**
* 个人代码量统计
* <p>
* 1.查询gitlab上应用列表
* 2.查询feature分支信息并获取修改信息
* 3.获取分支提交列表
* 4.获取每次提交增加行、删除行数、有效代码行数
* 5.统计有效代码行数
* 6.统计个人代码增加行数、有效代码行数
*/
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private GitLabProjectService projectService;
@Autowired
private CacheTemplate cacheTemplate;
@Autowired
private ParameterInfoDao parameterInfoDao;
@Override
public void execute(BatchBO dto) throws BizException {
logger.info("-------开始执行个人代码量批处理---------");
//1. 查询日切时间,并修改时间(当前时间,前已处理时间)
List<ParameterInfo> list = parameterInfoDao.queryByParamTypeAndParamKey("DayCut", "CurrDate");
if (list == null || list.size() <= 0) {
logger.error("未配置日切日期");
throw new BizException(RespEnum.ERR_998);
}
ParameterInfo parameterInfo = list.get(0);
if (Integer.valueOf(parameterInfo.getParamValue()) >= Integer.valueOf(DateFormatUtils.format(new Date(), "yyyyMMdd"))) {
logger.error("{} 统计已处理", parameterInfo.getParamValue());
throw new BizException(RespEnum.ERR_949);
}
ParameterInfo upd = new ParameterInfo();
upd.setParamValue(DateFormatUtils.format(new Date(), "yyyyMMdd"));
upd.setResv1(parameterInfo.getParamValue());
upd.setResv2(parameterInfo.getResv1());
parameterInfoDao.updateSelectiveByKey(upd, parameterInfo.getParamType(), parameterInfo.getParamKey());
//2. 所有应用信息
List<GitLabProject> projects = projectService.getGitProjects();
//3. 统计数据, 由于获取增量是多线程完成,只有执行完所有commit统计,才能触发统计才能计算代码行
// 缓存定义自行处理
projects.stream().forEach(item -> {
CommitStatisticsArcherBO bo = new CommitStatisticsArcherBO();
bo.setDateCutInfo(upd);
bo.setProject(item);
//异步实现,需要自行处理逻辑
});
logger.info("-------执行个人代码量批处理结束---------");
public void statistics(ParameterInfo upd, GitLabProject project) {
//查询有效分支
try {
int personDelDays = propertyManager.getPersonDelDays();
logger.info("个人代码统计设置时间间隔:【{}】",personDelDays);
String delDate = TimeUtil.plusDay(personDelDays, DateUtils.parseDate(upd.getResv1(), Constants.YYYYMMDD), "yyyyMMdd");
logger.info("个人代码统计延迟之前的日期:【{}】,延迟之后的日期:【{}】",upd.getResv1(),delDate);
// List<GitLabBranch> availBranches = branchService.getAvailFeatureBranches(project.getId(), DateUtils.parseDate(upd.getResv1(), Constants.YYYYMMDD));
List<GitLabBranch> availBranches = branchService.getAvailFeatureBranches(project.getId(), DateUtils.parseDate(delDate, Constants.YYYYMMDD));
if (availBranches == null || availBranches.size() <= 0) {
return;
}
availBranches.stream().forEach(branch -> {
statistics(upd, project, branch);
});
} catch (ParseException e) {
logger.error("日期转换失败: {}", upd.getResv1());
} finally {
//判断按照项目统计
logger.info("当前应用: {}, 已处理处理应用数:{},总应用数: {}", project.getName(), processed, all);
if (StringUtils.isBlank(all) || (StringUtils.isNotBlank(all) && Integer.parseInt(all) <= processed)) {
//触发项目统计
personCodeChangeStatisticsDao.processStatistics();
}
}
}
private void statistics(ParameterInfo upd, GitLabProject project, GitLabBranch branch) {
//查询提交列表
try {
int personDelDays = propertyManager.getPersonDelDays();
String delDate = TimeUtil.plusDay(personDelDays, DateUtils.parseDate(upd.getResv1(), Constants.YYYYMMDD), "yyyyMMdd");
List<GitLabCommit> availCommits = commitService.getAvailCommits(project.getId(), branch.getName(), DateUtils.parseDate(delDate, Constants.YYYYMMDD), DateUtils.parseDate(upd.getParamValue(), Constants.YYYYMMDD));
if (availCommits == null || availCommits.size() <= 0) {
return;
}
availCommits.stream().forEach(commit -> {
if(commitCodeChangeStatisticsDao.queryByProjectCommitId(project.getName(), commit.getId()) <=0){
if (StringUtils.isNotBlank(commit.getTitle()) && commit.getTitle().startsWith("Merge")) {
logger.info("代码合并 merge request 不统计, {}", commit.getTitle());
}else {
statistics(upd, project, branch, commit);
}
}
});
} catch (ParseException e) {
logger.error("日期转换失败: {}", upd.getResv1());
}
}
public List<Pattern> getIncludePatternList(String gitUrl){
Map<String, Object> statisticsIncludeRule = cacheParamInfoService.statisticsIncludeRule();
List<Pattern> includePatternList = new ArrayList<>();
if (statisticsIncludeRule != null && statisticsIncludeRule.size() > 0) {
String includeRule = String.valueOf(statisticsIncludeRule.get(gitUrl));
if (StringUtils.isNotBlank(includeRule) && (!includeRule.equals("null"))){
logger.info("{} 统计规则为:{}", gitUrl, includeRule);
List<String> includeRuleList = Arrays.asList(includeRule.split(","));
includeRuleList.forEach(item -> {
includePatternList.add(Pattern.compile(item));
});
}
}
return includePatternList;
}
八 总结
至此,开发代码统计已经完全实现。后续不管是以何种维度的统计,比如:团队/部门/小组等,都可以出各种统计数据出来了。
参考资料
GitLab : https://docs.gitlab.com/