# 由壹点「视觉需求」系统扣分逻辑说明 ## 一、当前系统扣分机制概述 系统采用**按比例扣分**的机制,扣分主要包含两部分: 1. **延误扣分** - 任务超时完成或进行中任务已超时 2. **进度未更新扣分** - 设计师未按时更新任务进度 --- ## 二、延误扣分计算规则 ### 1. 扣分公式 ``` 延误扣分 = 任务积分 × 延误工作小时数 × 扣分比例(%) ``` **重要说明:** - **延误工作小时数**:只计算工作时间内的延误,非工作时间不计入(工作时间默认为 9:00-18:00,可在系统设置中配置) - **扣分比例**:默认为 1%(即每延误1个工作小时,扣任务积分的1%) - **扣分上限**:单个任务的扣分不能超过该任务的积分 ### 2. 扣分比例配置 | 配置级别 | 说明 | |---------|------| | 默认值 | 1%(每延误1小时扣任务积分的1%)| | 部门配置 | 可在部门目标积分设置中配置 `deductionRate` 字段 | | 继承规则 | 三级部门继承二级部门的配置,如果二级部门未配置则使用默认值 | ### 3. 扣分场景 #### 场景A:已完成任务延误扣分 适用条件: - 任务状态为 `completed`(已完成) - 完成时间晚于截止时间 计算方式: ``` 延误小时数 = 工作时间计算器.计算延误小时(截止时间, 实际完成时间) 扣分 = 任务积分 × 延误小时数 × 扣分比例 / 100 ``` #### 场景B:进行中任务延误扣分 适用条件: - 任务状态为 `in_progress`(进行中) - 当前时间已超过截止时间 计算方式: ``` 延误小时数 = 工作时间计算器.计算延误小时(截止时间, 当前时间) 扣分 = 任务积分 × 延误小时数 × 扣分比例 / 100 ``` **注意**:进行中任务的延误扣分是**实时计算**的,会随着时间推移而增加。 ### 4. 主任务与子任务扣分 系统同时计算主任务和子任务的延误扣分: | 任务类型 | 截止时间来源 | 完成时间来源 | |---------|-------------|-------------| | 主任务 | `task.deadline` 字段 | `task.actualEndTime` 字段 | | 子任务 | 根据 `createTime + estimatedHours` 计算 | `taskItem.completeTime` 字段 | ### 5. 扣分计算示例 **示例1:任务按时完成** - 任务积分:10分 - 截止时间:2026-01-15 18:00 - 完成时间:2026-01-15 17:30 - 延误扣分:0分(未延误) **示例2:任务延误2小时** - 任务积分:10分 - 截止时间:2026-01-15 16:00 - 完成时间:2026-01-15 18:00 - 延误小时数:2小时(工作时间内) - 扣分比例:1% - 延误扣分:10 × 2 × 1% = 0.20分 **示例3:任务延误跨天** - 任务积分:20分 - 截止时间:2026-01-15 17:00 - 完成时间:2026-01-16 11:00 - 延误工作小时数:1小时(15日17:00-18:00)+ 2小时(16日9:00-11:00)= 3小时 - 扣分比例:1% - 延误扣分:20 × 3 × 1% = 0.60分 **示例4:延误扣分达到上限** - 任务积分:5分 - 延误小时数:1000小时 - 理论扣分:5 × 1000 × 1% = 50分 - 实际扣分:5分(不能超过任务积分) --- ## 三、进度未更新扣分 ### 1. 触发条件 - 设计师每日 17:30 后首次登录需要更新进行中任务的进度 - 如果未更新进度,系统将记录扣分 ### 2. 扣分记录 - 存储在 `progress_deduction_log` 表中 - 按月汇总计入设计师的总扣分 ### 3. 查询方式 ```sql SELECT SUM(deduction_amount) FROM progress_deduction_log WHERE user_id = ? AND YEAR(deduction_date) = ? AND MONTH(deduction_date) = ? ``` --- ## 四、净积分计算 ### 计算公式 ``` 总扣分 = 延误扣分 + 进度未更新扣分 净积分 = 获得积分 - 总扣分 如果净积分 < 0,则净积分 = 0 ``` ### 数据展示 在数据分析页面的设计师绩效排行榜中: - **获得积分**:当月完成的所有任务(主任务+子任务)的积分总和 - **延误扣分**:当月所有任务的延误扣分总和 - **净积分**:获得积分 - 延误扣分(最小为0) --- ## 五、任务截止时间计算 ### 1. 计算公式 ``` 截止时间 = 接单时间 + 预估工时(只计算工作时间段) ``` **核心原则**:截止时间的计算只累加工作时间段内的小时数,非工作时间(下班后、周末)自动跳过。 ### 2. 计算逻辑详解 #### 步骤1:确定起始点 | 接单时间 | 起始点处理 | |---------|-----------| | 在工作时间内(9:00-18:00) | 从接单时间开始计算 | | 在上班前(如 8:00) | 从当天 9:00 开始计算 | | 在下班后(如 19:00) | 从次日 9:00 开始计算 | #### 步骤2:累加工时 从起始点开始,逐步累加工作小时数: 1. 计算当天剩余工作时间 2. 如果当天工作时间足够,直接加上剩余工时 3. 如果当天工作时间不够,移动到下一个工作日继续累加 ### 3. 计算示例 #### 示例1:当天可完成 ``` 接单时间:2026-01-15 10:00(周三) 预估工时:5小时 当天剩余工作时间:18:00 - 10:00 = 8小时 ✓ 足够 截止时间 = 10:00 + 5小时 = 2026-01-15 15:00 ``` #### 示例2:需要跨天 ``` 接单时间:2026-01-15 16:00(周三) 预估工时:5小时 当天剩余工作时间:18:00 - 16:00 = 2小时 ✗ 不够 计算过程: - 第1天(周三):消耗 2小时,剩余 3小时 - 第2天(周四):从 9:00 开始,消耗 3小时 截止时间 = 2026-01-16 12:00 ``` #### 示例3:下班后接单 ``` 接单时间:2026-01-15 19:00(周三,下班后) 预估工时:3小时 计算过程: - 起始点移动到次日 9:00 - 从 2026-01-16 09:00 开始计算 截止时间 = 2026-01-16 12:00 ``` #### 示例4:跨多天 ``` 接单时间:2026-01-15 16:00(周三) 预估工时:12小时 当天剩余工作时间:18:00 - 16:00 = 2小时 ✗ 不够 计算过程: - 第1天(周三):消耗 2小时,剩余 10小时 - 第2天(周四):消耗 9小时(9:00-18:00),剩余 1小时 - 第3天(周五):从 9:00 开始,消耗 1小时 截止时间 = 2026-01-17 10:00(周五) ``` **注意**:当前系统按每天 9:00-18:00 计算工作时间,**周六周日也按正常工作日计算**,暂未配置节假日跳过功能。 ### 4. 主任务与子任务的区别 | 任务类型 | 截止时间设置方式 | 说明 | |---------|-----------------|------| | 主任务 | 发布时直接设置 `deadline` | 由任务发布者手动设置 | | 子任务 | 系统自动计算 | 接单时间 + 预估工时 | #### 子任务截止时间触发时机 子任务的截止时间在以下场景自动计算: 1. **接单部门审核通过时** - 子任务状态变为 `in_progress` 2. **设计师接单时** - 从任务大厅抢单 ### 5. 代码实现 **核心方法**:`WorkHoursCalculator.calculateDeadline()` ```java /** * 计算截止时间(只计算工作时间段) * @param startTime 开始时间(接单时间) * @param workHours 需要的工作小时数 * @return 截止时间 */ public LocalDateTime calculateDeadline(LocalDateTime startTime, BigDecimal workHours) { // 1. 获取工作时间配置(默认 9:00-18:00) // 2. 如果开始时间不在工作时间内,移动到下一个工作时段 // 3. 循环累加工时,只在工作时间段内计算 // 4. 返回最终截止时间 } ``` **调用位置**: - `TaskService.updatePipelineSubTasksForAssigned()` - 审核通过时设置 - `TaskItemService.approveTaskItemGroup()` - 指派设计师时设置 --- ## 六、工作时间配置 ### 1. 配置位置 系统设置 → 工作时间配置 ### 2. 配置项 | 配置键 | 说明 | 默认值 | |-------|------|--------| | `work_hours.start_time` | 上班开始时间 | 09:00 | | `work_hours.end_time` | 下班结束时间 | 18:00 | ### 3. 影响范围 - 任务截止时间计算 - 延误小时数计算(只计算工作时间内的延误) - 延误扣分计算 --- ## 七、代码位置参考 ### 核心扣分计算 - **文件**:`backend/src/main/java/com/designhub/service/AnalyticsService.java` - **方法**:`getDesignerPerformance()` - **扣分比例获取**:`getDeductionRate()` 方法(第1325行) ### 工作时间计算器 - **文件**:`backend/src/main/java/com/designhub/util/WorkHoursCalculator.java` - **截止时间计算**:`calculateDeadline()` 方法 - **延误小时计算**:`calculateDelayHours()` 方法 ### 进度未更新扣分 - **记录表**:`progress_deduction_log` - **汇总查询**:`ProgressDeductionLogMapper.sumMonthlyDeduction()` --- ## 八、扣分规则对比(历史参考) ### 当前实现(按比例扣分) ``` 扣分 = 任务积分 × 延误工作小时数 × 扣分比例(1%) 扣分上限 = 任务积分 ``` **优点**: - 与任务价值挂钩,公平合理 - 扣分上限明确,不会出现扣分超过任务价值的情况 - 只计算工作时间,更加人性化 ### 历史设计(按等级固定扣分,已废弃) | 延误程度 | 延误时长 | 扣分规则 | |---------|---------|---------| | 轻微延误 | < 2小时 | 每小时扣2分 | | 一般延误 | 2-8小时 | 每小时扣3分 | | 严重延误 | 8-24小时 | 每小时扣5分 | | 重大延误 | > 24小时 | 基础50分+每天10分 | **注意**:此规则定义在 `DelayPenaltyService.java` 中,但**当前数据分析页面未使用**。 --- ## 九、常见问题 ### Q1:为什么扣分等于获得积分? **原因**:扣分达到上限(任务积分) **示例**: - 任务积分:10分 - 延误100小时 - 理论扣分:10 × 100 × 1% = 10分 - 实际扣分:10分(达到上限) ### Q2:非工作时间(下班后)延误怎么计算? **原因**:系统只计算工作时间内的延误(9:00-18:00) **示例**: - 截止时间:周三 17:00 - 完成时间:周四 11:00 - 延误工作小时:1小时(周三 17:00-18:00)+ 2小时(周四 9:00-11:00)= 3小时 - 晚上 18:00 到次日 9:00 的时间不计入延误 **注意**:当前系统周六周日也按正常工作日计算,暂未配置周末跳过功能。 ### Q3:子任务没有设置截止时间怎么计算? **方式**:根据子任务的创建时间 + 预估工时自动计算截止时间 ``` 子任务截止时间 = 创建时间 + 预估工时(按工作时间计算) ``` --- *文档更新时间:2026-01-15* *文档版本:v2.1*