qiangdanxitong/扣分逻辑说明.md

5.1 KiB
Raw Blame History

设计师绩效排行榜扣分逻辑说明

一、当前实现逻辑(数据分析页面)

数据分析页面的设计师绩效排行榜使用的是 AnalyticsService.getDesignerPerformance 方法,扣分逻辑如下:

1. 延误扣分计算公式

扣分 = 任务积分 × 延误小时数 × 扣分比例(默认1%)

限制条件:

  • 扣分不能超过任务积分
  • 如果计算结果超过任务积分,则扣分 = 任务积分

扣分比例:

  • 默认值:1%(即 BigDecimal.ONE)
  • 可以从部门配置中获取(DesignerTargetScore.deductionRate)
  • 三级部门继承二级部门的配置

2. 进度未更新扣分

除了延误扣分,还会加上进度未更新扣分:

  • 从 progress_deduction_log 表中查询当月的进度未更新扣分总额
  • 进度未更新扣分 + 延误扣分 = 总扣分

3. 净积分计算

净积分 = 获得积分 - 总扣分
如果净积分 < 0,则净积分 = 0

二、需求文档中的扣分规则(DelayPenaltyService)

需求文档中定义的延误扣分规则(在 DelayPenaltyService 中实现,但当前数据分析页面未使用):

延误程度 延误时长 扣分规则 示例
轻微延误 < 2小时 每小时扣2分 延误1.5小时 → 扣3分
一般延误 2-8小时 每小时扣3分 延误5小时 → 扣15分
严重延误 8-24小时 每小时扣5分 延误10小时 → 扣50分
重大延误 > 24小时 基础50分+每天10分 延误2天 → 扣70分

三、"若兰"被扣10分的情况分析

从图片数据看:

  • 获得积分:10分
  • 延误扣分:10分
  • 净积分:0分

可能的原因:

  1. 情况A:延误扣分达到上限

    • 如果任务积分是10分,延误时间较长
    • 按照公式:10分 × 延误小时数 × 1% = 10分
    • 或者计算结果超过10分,被限制为10分
  2. 情况B:延误扣分 + 进度未更新扣分 = 10分

    • 延误扣分 + 进度未更新扣分 = 10分
    • 例如:延误扣分5分 + 进度未更新扣分5分 = 10分

四、代码位置

当前使用的扣分逻辑:

  • 文件:backend/src/main/java/com/designhub/service/AnalyticsService.java
  • 方法:getDesignerPerformance(第535行)
  • 扣分计算:第680-698行(延误扣分)+ 第701-718行(进度未更新扣分)
  • 扣分比例获取:getDeductionRate(第1146行)

需求文档中的扣分逻辑(未在数据分析页面使用):

  • 文件:backend/src/main/java/com/designhub/service/DelayPenaltyService.java
  • 方法:calculateDelayPenalty(第36行)
  • 规则:按延误等级和固定扣分规则

五、建议复核的问题

  1. 数据分析页面应该使用哪套扣分规则?

    • 当前实现:按百分比扣分(任务积分 × 延误小时数 × 扣分比例)
    • 需求文档:按延误等级固定扣分(轻微/一般/严重/重大)
  2. 扣分比例默认值是否合理?

    • 当前默认:1%
    • 如果任务积分100分,延误10小时,扣分 = 100 × 10 × 1% = 10分
    • 如果任务积分10分,延误100小时,扣分 = min(10 × 100 × 1%, 10) = 10分
  3. 进度未更新扣分是否应该计入?

    • 当前实现:计入总扣分
    • 是否需要单独显示?
  4. 是否需要使用 DelayPenaltyRecord 表的数据?

    • 当前实现:直接从任务表计算,不使用 DelayPenaltyRecord
    • 需求文档中有 DelayPenaltyRecord 表的设计,但数据分析页面未使用

六、相关代码片段

当前扣分计算逻辑(AnalyticsService.java 第680-698行):

// 计算延误扣分(如果超时完成)
if (task.getDeadline() != null && task.getActualEndTime() != null 
    && task.getActualEndTime().isAfter(task.getDeadline())) {
    // 计算延误小时数
    long overdueHours = Duration.between(task.getDeadline(), task.getActualEndTime()).toHours();
    if (overdueHours > 0 && taskScore.compareTo(BigDecimal.ZERO) > 0) {
        // 扣分 = 任务积分 * 延误小时数 * 扣分比例
        BigDecimal taskDeduction = taskScore
            .multiply(BigDecimal.valueOf(overdueHours))
            .multiply(deductionRate)
            .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
        // 扣分不能超过任务积分
        if (taskDeduction.compareTo(taskScore) > 0) {
            taskDeduction = taskScore;
        }
        deductionScore = deductionScore.add(taskDeduction);
    }
}

需求文档中的扣分规则(DelayPenaltyService.java 第56-72行):

if (delayHours < 2) {
    // 轻微延误:每小时扣2分
    delayLevel = "light";
    penaltyScore = (int) Math.ceil(delayHours * 2.0);
} else if (delayHours < 8) {
    // 一般延误:每小时扣3分
    delayLevel = "general";
    penaltyScore = (int) Math.ceil(delayHours * 3.0);
} else if (delayHours < 24) {
    // 严重延误:每小时扣5分
    delayLevel = "severe";
    penaltyScore = (int) Math.ceil(delayHours * 5.0);
} else {
    // 重大延误:基础50分+每天10分
    delayLevel = "major";
    penaltyScore = 50 + (int) delayDays * 10;
}