diff --git a/backend/src/main/java/com/designhub/service/DeptMonthlyScoreService.java b/backend/src/main/java/com/designhub/service/DeptMonthlyScoreService.java index c1f8810..0f492fe 100644 --- a/backend/src/main/java/com/designhub/service/DeptMonthlyScoreService.java +++ b/backend/src/main/java/com/designhub/service/DeptMonthlyScoreService.java @@ -1,6 +1,5 @@ package com.designhub.service; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.designhub.entity.Department; import com.designhub.entity.DeptMonthlyScore; import com.designhub.mapper.DepartmentMapper; @@ -26,13 +25,13 @@ public class DeptMonthlyScoreService { private final DepartmentMapper departmentMapper; /** - * 获取或创建当月积分记录 + * 获取或创建指定月份积分记录(基于完成时间) */ @Transactional(rollbackFor = Exception.class) - public DeptMonthlyScore getOrCreateCurrentMonth(Long deptId) { - LocalDateTime now = LocalDateTime.now(); - int year = now.getYear(); - int month = now.getMonthValue(); + public DeptMonthlyScore getOrCreateMonth(Long deptId, LocalDateTime scoreTime) { + LocalDateTime targetTime = scoreTime != null ? scoreTime : LocalDateTime.now(); + int year = targetTime.getYear(); + int month = targetTime.getMonthValue(); DeptMonthlyScore record = deptMonthlyScoreMapper.getByDeptAndMonth(deptId, year, month); if (record == null) { @@ -53,20 +52,28 @@ public class DeptMonthlyScoreService { record.setCompletedTaskCount(0); record.setDelayedTaskCount(0); record.setTotalDelayHours(BigDecimal.ZERO); - record.setCreateTime(now); - record.setUpdateTime(now); + record.setCreateTime(targetTime); + record.setUpdateTime(targetTime); deptMonthlyScoreMapper.insert(record); } return record; } + /** + * 获取或创建当月积分记录 + */ + @Transactional(rollbackFor = Exception.class) + public DeptMonthlyScore getOrCreateCurrentMonth(Long deptId) { + return getOrCreateMonth(deptId, LocalDateTime.now()); + } + /** * 增加部门获得积分(任务完成时调用) */ @Transactional(rollbackFor = Exception.class) - public void addEarnedScore(Long deptId, BigDecimal score, boolean isDelayed, BigDecimal delayHours) { - DeptMonthlyScore record = getOrCreateCurrentMonth(deptId); + public void addEarnedScore(Long deptId, BigDecimal score, boolean isDelayed, BigDecimal delayHours, LocalDateTime scoreTime) { + DeptMonthlyScore record = getOrCreateMonth(deptId, scoreTime); // 增加获得积分 record.setEarnedScore(record.getEarnedScore().add(score)); @@ -87,12 +94,20 @@ public class DeptMonthlyScoreService { deptMonthlyScoreMapper.updateById(record); } + /** + * 增加部门获得积分(兼容旧调用,使用当前时间) + */ + @Transactional(rollbackFor = Exception.class) + public void addEarnedScore(Long deptId, BigDecimal score, boolean isDelayed, BigDecimal delayHours) { + addEarnedScore(deptId, score, isDelayed, delayHours, LocalDateTime.now()); + } + /** * 增加部门扣除积分(延误扣分时调用) */ @Transactional(rollbackFor = Exception.class) - public void addDeductedScore(Long deptId, BigDecimal deductedScore) { - DeptMonthlyScore record = getOrCreateCurrentMonth(deptId); + public void addDeductedScore(Long deptId, BigDecimal deductedScore, LocalDateTime scoreTime) { + DeptMonthlyScore record = getOrCreateMonth(deptId, scoreTime); record.setDeductedScore(record.getDeductedScore().add(deductedScore)); // 重新计算净积分 @@ -102,12 +117,20 @@ public class DeptMonthlyScoreService { deptMonthlyScoreMapper.updateById(record); } + /** + * 增加部门扣除积分(兼容旧调用,使用当前时间) + */ + @Transactional(rollbackFor = Exception.class) + public void addDeductedScore(Long deptId, BigDecimal deductedScore) { + addDeductedScore(deptId, deductedScore, LocalDateTime.now()); + } + /** * 扣除部门获得积分(删除已完成任务时调用,用于回退积分) */ @Transactional(rollbackFor = Exception.class) - public void deductEarnedScore(Long deptId, BigDecimal score) { - DeptMonthlyScore record = getOrCreateCurrentMonth(deptId); + public void deductEarnedScore(Long deptId, BigDecimal score, LocalDateTime scoreTime) { + DeptMonthlyScore record = getOrCreateMonth(deptId, scoreTime); // 扣除获得积分(不能小于0) BigDecimal newEarnedScore = record.getEarnedScore().subtract(score); @@ -131,6 +154,14 @@ public class DeptMonthlyScoreService { log.info("已扣除部门月度积分: deptId={}, score={}, newEarnedScore={}", deptId, score, newEarnedScore); } + /** + * 扣除部门获得积分(兼容旧调用,使用当前时间) + */ + @Transactional(rollbackFor = Exception.class) + public void deductEarnedScore(Long deptId, BigDecimal score) { + deductEarnedScore(deptId, score, LocalDateTime.now()); + } + /** * 获取部门当月积分 */ diff --git a/backend/src/main/java/com/designhub/service/TaskItemService.java b/backend/src/main/java/com/designhub/service/TaskItemService.java index c0a6e4b..08ad8d5 100644 --- a/backend/src/main/java/com/designhub/service/TaskItemService.java +++ b/backend/src/main/java/com/designhub/service/TaskItemService.java @@ -818,7 +818,8 @@ public class TaskItemService { item.getReceiverDeptId(), item.getTotalScore(), isDelayed, - delayHours + delayHours, + item.getCompleteTime() ); log.info("🎉 子任务 {} 积分已发放: deptId={}, score={}, isDelayed={}, delayHours={}", diff --git a/backend/src/main/java/com/designhub/service/TaskService.java b/backend/src/main/java/com/designhub/service/TaskService.java index 6a5a44d..c0da0ff 100644 --- a/backend/src/main/java/com/designhub/service/TaskService.java +++ b/backend/src/main/java/com/designhub/service/TaskService.java @@ -3684,15 +3684,16 @@ public class TaskService { if (task.getReceiverDeptId() != null) { try { deptMonthlyScoreService.addEarnedScore( - task.getReceiverDeptId(), + task.getReceiverDeptId(), task.getScoreReward() != null ? task.getScoreReward() : BigDecimal.ZERO, isDelayed, - delayHours + delayHours, + completeTime ); // 如果有延误扣分,单独记录扣除 if (deductedScore.compareTo(BigDecimal.ZERO) > 0) { - deptMonthlyScoreService.addDeductedScore(task.getReceiverDeptId(), deductedScore); + deptMonthlyScoreService.addDeductedScore(task.getReceiverDeptId(), deductedScore, completeTime); } log.info("更新部门月度积分: deptId={}, earned={}, deducted={}", @@ -4275,7 +4276,11 @@ public class TaskService { log.info("已回退主任务积分: deptId={}, score={}", task.getReceiverDeptId(), task.getScoreReward()); // 回退部门月度积分统计 - deptMonthlyScoreService.deductEarnedScore(task.getReceiverDeptId(), task.getScoreReward()); + deptMonthlyScoreService.deductEarnedScore( + task.getReceiverDeptId(), + task.getScoreReward(), + task.getCompleteTime() + ); log.info("已回退部门月度积分: deptId={}, score={}", task.getReceiverDeptId(), task.getScoreReward()); } else { log.warn("部门积分池不存在,跳过主任务积分回退: deptId={}", task.getReceiverDeptId()); @@ -4304,7 +4309,11 @@ public class TaskService { item.getItemNo(), item.getReceiverDeptId(), item.getTotalScore()); // 回退部门月度积分统计 - deptMonthlyScoreService.deductEarnedScore(item.getReceiverDeptId(), item.getTotalScore()); + deptMonthlyScoreService.deductEarnedScore( + item.getReceiverDeptId(), + item.getTotalScore(), + item.getCompleteTime() + ); } else { log.warn("部门积分池不存在,跳过子任务积分回退: itemNo={}, deptId={}", item.getItemNo(), item.getReceiverDeptId()); } diff --git a/database/repair_monthly_scores.sql b/database/repair_monthly_scores.sql new file mode 100644 index 0000000..7510723 --- /dev/null +++ b/database/repair_monthly_scores.sql @@ -0,0 +1,54 @@ +-- 按完成时间修正部门月度积分(earned_score / completed_task_count) +-- 注意:本脚本不会修改 deducted_score / 延误统计,仅重算 earned_score 与 net_score + +-- 1) 设定需要修正的年月 +SET @year = 2026; +SET @month = 2; +SET @start = STR_TO_DATE(CONCAT(@year, '-', LPAD(@month, 2, '0'), '-01 00:00:00'), '%Y-%m-%d %H:%i:%s'); +SET @end = DATE_ADD(@start, INTERVAL 1 MONTH); + +-- 2) 汇总该月完成的主任务/子任务积分(主任务排除流水线任务) +CREATE TEMPORARY TABLE tmp_monthly_earned AS +SELECT dept_id, SUM(score) AS earned_score, COUNT(*) AS completed_task_count +FROM ( + SELECT t.receiver_dept_id AS dept_id, t.score_reward AS score + FROM task t + WHERE t.status = 'completed' + AND t.receiver_dept_id IS NOT NULL + AND t.score_reward IS NOT NULL + AND t.score_reward > 0 + AND (t.is_pipeline IS NULL OR t.is_pipeline = 0) + AND t.complete_time >= @start AND t.complete_time < @end + UNION ALL + SELECT ti.receiver_dept_id AS dept_id, ti.total_score AS score + FROM task_item ti + WHERE ti.status = 'completed' + AND ti.receiver_dept_id IS NOT NULL + AND ti.total_score IS NOT NULL + AND ti.total_score > 0 + AND ti.complete_time >= @start AND ti.complete_time < @end +) u +GROUP BY dept_id; + +-- 3) 更新已有记录 +UPDATE dept_monthly_score d +JOIN tmp_monthly_earned e + ON d.dept_id = e.dept_id AND d.year = @year AND d.month = @month +SET d.earned_score = e.earned_score, + d.completed_task_count = e.completed_task_count, + d.net_score = e.earned_score - d.deducted_score, + d.update_time = NOW(); + +-- 4) 插入缺失记录 +INSERT INTO dept_monthly_score + (dept_id, dept_name, year, month, earned_score, deducted_score, net_score, + completed_task_count, delayed_task_count, total_delay_hours, create_time, update_time) +SELECT e.dept_id, dept.dept_name, @year, @month, e.earned_score, 0, + e.earned_score, e.completed_task_count, 0, 0, NOW(), NOW() +FROM tmp_monthly_earned e +JOIN department dept ON dept.id = e.dept_id +LEFT JOIN dept_monthly_score d + ON d.dept_id = e.dept_id AND d.year = @year AND d.month = @month +WHERE d.id IS NULL; + +DROP TEMPORARY TABLE tmp_monthly_earned;