feat(优先级):维度权重可配置并即时重算

- 新表 user_priority_weights(每用户一行,默认 0.35/0.25/0.20/0.10/0.10)
- PriorityWeightsService:读取/保存权重,权重和必须为 1,保存后重算全部任务
- TaskController:GET/PUT /tasks/priority-weights
- CalculatedPriorityTool 支持自定义权重
- 修复 updateTask 不重算优先级的问题
This commit is contained in:
2026-07-03 23:41:50 +08:00
parent b67cf238c2
commit 7461b6b5ad
8 changed files with 234 additions and 25 deletions
@@ -0,0 +1,21 @@
package com.guo.learningprogresstracker.service;
import com.guo.learningprogresstracker.entity.UserPriorityWeightsEntity;
import com.guo.learningprogresstracker.exception.ErrorParameterException;
/**
* 优先级权重配置服务
*/
public interface PriorityWeightsService {
/**
* 获取当前用户的权重配置,不存在时返回系统默认值
*/
UserPriorityWeightsEntity getWeights();
/**
* 保存权重配置并重算当前用户全部任务的优先级。
* 五项权重之和必须为 1(容差 0.001)。
*/
UserPriorityWeightsEntity saveWeights(UserPriorityWeightsEntity weights) throws ErrorParameterException;
}
@@ -0,0 +1,99 @@
package com.guo.learningprogresstracker.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.guo.learningprogresstracker.dto.PriorityDto;
import com.guo.learningprogresstracker.entity.TaskEntity;
import com.guo.learningprogresstracker.entity.UserPriorityWeightsEntity;
import com.guo.learningprogresstracker.exception.ErrorParameterException;
import com.guo.learningprogresstracker.mapper.TasksMapper;
import com.guo.learningprogresstracker.mapper.UserPriorityWeightsMapper;
import com.guo.learningprogresstracker.service.PriorityWeightsService;
import com.guo.learningprogresstracker.utils.CalculatedPriorityTool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 优先级权重配置服务实现
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PriorityWeightsServiceImpl implements PriorityWeightsService {
private static final double SUM_TOLERANCE = 0.001;
private final UserPriorityWeightsMapper weightsMapper;
private final TasksMapper tasksMapper;
@Override
public UserPriorityWeightsEntity getWeights() {
UserPriorityWeightsEntity existing = weightsMapper.selectOne(
Wrappers.<UserPriorityWeightsEntity>lambdaQuery().last("LIMIT 1"));
return existing != null ? existing : UserPriorityWeightsEntity.defaults();
}
@Override
@Transactional
public UserPriorityWeightsEntity saveWeights(UserPriorityWeightsEntity weights) throws ErrorParameterException {
validate(weights);
UserPriorityWeightsEntity existing = weightsMapper.selectOne(
Wrappers.<UserPriorityWeightsEntity>lambdaQuery().last("LIMIT 1"));
if (existing == null) {
weightsMapper.insert(weights);
} else {
weights.setId(existing.getId());
weightsMapper.updateById(weights);
}
recalculateAllTasks(weights);
return weights;
}
/** 用新权重重算当前用户的全部任务优先级 */
private void recalculateAllTasks(UserPriorityWeightsEntity weights) {
List<TaskEntity> tasks = tasksMapper.selectList(Wrappers.lambdaQuery(TaskEntity.class));
for (TaskEntity task : tasks) {
PriorityDto dto = new PriorityDto();
dto.setUrgency(orZero(task.getUrgency()));
dto.setImportance(orZero(task.getImportance()));
dto.setContentDifficulty(orZero(task.getContentDifficulty()));
dto.setFutureValue(orZero(task.getFutureValue()));
dto.setSubjectivePriority(orZero(task.getSubjectivePriority()));
task.setCalculatedPriority(CalculatedPriorityTool.calculatedPriority(dto, weights));
tasksMapper.updateById(task);
}
log.info("权重更新,已重算 {} 个任务的优先级", tasks.size());
}
private void validate(UserPriorityWeightsEntity weights) throws ErrorParameterException {
double[] values = {
orZero(weights.getUrgencyWeight()),
orZero(weights.getImportanceWeight()),
orZero(weights.getContentDifficultyWeight()),
orZero(weights.getFutureValueWeight()),
orZero(weights.getSubjectivePriorityWeight())};
double sum = 0;
for (double v : values) {
if (v < 0 || v > 1) {
throw new ErrorParameterException("权重必须在 0 到 1 之间");
}
sum += v;
}
if (Math.abs(sum - 1.0) > SUM_TOLERANCE) {
throw new ErrorParameterException("五项权重之和必须为 1,当前为 " + sum);
}
}
private static int orZero(Integer v) {
return v == null ? 0 : v;
}
private static double orZero(Double v) {
return v == null ? 0 : v;
}
}
@@ -17,6 +17,7 @@ import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
import com.guo.learningprogresstracker.mapStruct.RequestConvert;
import com.guo.learningprogresstracker.mapStruct.TaskConvert;
import com.guo.learningprogresstracker.mapper.TaskApplicationMapper;
import com.guo.learningprogresstracker.service.PriorityWeightsService;
import com.guo.learningprogresstracker.service.TasksService;
import com.guo.learningprogresstracker.mapper.TasksMapper;
import com.guo.learningprogresstracker.utils.CalculatedPriorityTool;
@@ -42,6 +43,8 @@ public class TasksServiceImpl extends ServiceImpl<TasksMapper, TaskEntity>
private final TaskApplicationMapper taskApplicationMapper;
private final PriorityWeightsService priorityWeightsService;
@Override
public Page<TaskInfo> taskList(Integer pageNum, Integer pageSize) {
Page<TaskEntity> page = tasksMapper.selectPage(new Page<TaskEntity>(pageNum, pageSize),
@@ -62,7 +65,7 @@ public class TasksServiceImpl extends ServiceImpl<TasksMapper, TaskEntity>
task.setTaskNum(GenerateNumTool.generateNum("TASK"));
PriorityDto priorityDto = RequestConvert.MAPPER.taskRequestToPriorityDto(taskRequest);
task.setCalculatedPriority(CalculatedPriorityTool.calculatedPriority(priorityDto));
task.setCalculatedPriority(CalculatedPriorityTool.calculatedPriority(priorityDto, priorityWeightsService.getWeights()));
this.save(task);
return task.getTaskNum();
}
@@ -87,6 +90,9 @@ public class TasksServiceImpl extends ServiceImpl<TasksMapper, TaskEntity>
throw new IllegalArgumentException("任务ID格式错误: " + taskId);
}
taskEntity.setId(id);
// 维度数据可能变化,更新时重算优先级
PriorityDto priorityDto = RequestConvert.MAPPER.taskRequestToPriorityDto(updatedTask);
taskEntity.setCalculatedPriority(CalculatedPriorityTool.calculatedPriority(priorityDto, priorityWeightsService.getWeights()));
this.updateById(taskEntity);
}