1 2 3 4

度量指标-开发产能代码统计

1度量指标-开发产能代码统计

一、背景

公司度量开发指标,需要统计公司每个开发人员的有效代码计算,并作为开发人员的考核指标。包含所有开发语言的代码统计。之前功能也已经上线,没想到本次疫情居家办公以后,公司领导对于本次代码统计的指标尤其看重。故而,分享一下代码度量统计的相关功能。

二、已实现功能

  1. Java代码默认无需配置,规则默认统计;
  1. 非Java代码,通过前端页面配置规则<匹配/不匹配>,配置完成以后,刷新缓存,按照配置规则统计代码;
  1. 正则匹配规则;
  1. T-1 定时跑批、异步处理实现数据入库统计;
  1. 提供人工入口,实现可以实时恢复代码统计数据;

三、业务流程说明

主要实现增量代码行数统计功能,并实现代码增量过滤功能,最终统计出代码的增量行信息。
  1. 每天凌晨2:00调用生成增量代码统计接口;
  2. 增量代码更加commitid比较无差异(差异为0),判断存量数据库是否有数据,如果有,只更新update_time时间,流程结束;
  3. 若增量代码差异为0,且表中没有记录,说明该分支代码已经合并到master分支,流程结束;
  4. 若增量代码大于0,根据匹配规则过滤特定名称的增量代码;
  5. 匹配的增量代码,需要过滤import的增量,空格,注释不计入有效代码;
  6. 工具类生成的代码,比如:bo、vo、dto、example、xml等不计入有效代码统计;
  7. 只统计开发分支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]的背景下,确定需要明确的接口整合,来实现功能。

可行性分析:

  1. 获取所有project
  2. 遍历project获取所有分支,筛选出最后提交时间的分支编号
  3. 根据分支编号获取该分支提交信息,根据提交时间筛选满足提交的提交
  4. 查询当前的提交详情,并统计增量代码,提交人等信息
  5. 统计有效增加代码

涉及到的API接口:

  1. 查询项目 /api/v4/projects
  2. 查询分支 /api/v4/projects/{porjectsid}/repository/branches
  3. 查询提交列表 /api/v4/projects/{projectsid}/repository/commits
  4. 查询提交详情 /api/v4/projects/{projectsid}/repository/commits/{commitId}
  5. 当前版本差异。/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;

    }

八 总结

至此,开发代码统计已经完全实现。后续不管是以何种维度的统计,比如:团队/部门/小组等,都可以出各种统计数据出来了。

参考资料

[1]

GitLab : https://docs.gitlab.com/

posted @ 2022-04-06 11:18  玩出梦想  阅读(470)  评论(1编辑  收藏  举报