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:
@@ -11,8 +11,10 @@ import com.guo.learningprogresstracker.entity.CommonResult;
|
||||
import com.guo.learningprogresstracker.dto.request.TaskRequest;
|
||||
import com.guo.learningprogresstracker.dto.request.UpdateTaskApplicationRequest;
|
||||
import com.guo.learningprogresstracker.entity.TaskApplicationEntity;
|
||||
import com.guo.learningprogresstracker.entity.UserPriorityWeightsEntity;
|
||||
import com.guo.learningprogresstracker.exception.ErrorParameterException;
|
||||
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
|
||||
import com.guo.learningprogresstracker.service.PriorityWeightsService;
|
||||
import com.guo.learningprogresstracker.service.TasksService;
|
||||
import com.guo.learningprogresstracker.service.impl.StudySessionsServiceImpl;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -48,6 +50,21 @@ public class TaskController {
|
||||
|
||||
private final StudySessionsServiceImpl studySessionsServiceImpl;
|
||||
|
||||
private final PriorityWeightsService priorityWeightsService;
|
||||
|
||||
@GetMapping("/priority-weights")
|
||||
@Operation(summary = "获取优先级维度权重配置")
|
||||
public CommonResult<UserPriorityWeightsEntity> getPriorityWeights() {
|
||||
return CommonResult.success(priorityWeightsService.getWeights());
|
||||
}
|
||||
|
||||
@PutMapping("/priority-weights")
|
||||
@Operation(summary = "保存优先级维度权重配置并重算全部任务优先级")
|
||||
public CommonResult<UserPriorityWeightsEntity> savePriorityWeights(
|
||||
@RequestBody UserPriorityWeightsEntity weights) throws ErrorParameterException {
|
||||
return CommonResult.success(priorityWeightsService.saveWeights(weights));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "添加新任务")
|
||||
public CommonResult<String> addTask(@RequestBody @Validated({Ops.CreateG.class}) TaskRequest taskRequest) throws ErrorParameterException {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.guo.learningprogresstracker.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户自定义的任务优先级维度权重(每用户一行,依赖多租户拦截器隔离)
|
||||
*/
|
||||
@TableName(value = "user_priority_weights")
|
||||
@Data
|
||||
public class UserPriorityWeightsEntity extends BaseEntity implements Serializable {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
@TableField(value = "urgency_weight")
|
||||
private Double urgencyWeight;
|
||||
|
||||
@TableField(value = "importance_weight")
|
||||
private Double importanceWeight;
|
||||
|
||||
@TableField(value = "content_difficulty_weight")
|
||||
private Double contentDifficultyWeight;
|
||||
|
||||
@TableField(value = "future_value_weight")
|
||||
private Double futureValueWeight;
|
||||
|
||||
@TableField(value = "subjective_priority_weight")
|
||||
private Double subjectivePriorityWeight;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 系统默认权重 */
|
||||
public static UserPriorityWeightsEntity defaults() {
|
||||
UserPriorityWeightsEntity entity = new UserPriorityWeightsEntity();
|
||||
entity.setUrgencyWeight(0.35);
|
||||
entity.setImportanceWeight(0.25);
|
||||
entity.setContentDifficultyWeight(0.20);
|
||||
entity.setFutureValueWeight(0.10);
|
||||
entity.setSubjectivePriorityWeight(0.10);
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.guo.learningprogresstracker.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.guo.learningprogresstracker.entity.UserPriorityWeightsEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface UserPriorityWeightsMapper extends BaseMapper<UserPriorityWeightsEntity> {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+99
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.guo.learningprogresstracker.utils;
|
||||
|
||||
import com.guo.learningprogresstracker.dto.PriorityDto;
|
||||
import com.guo.learningprogresstracker.entity.UserPriorityWeightsEntity;
|
||||
|
||||
/**
|
||||
* 加权优先级计算工具
|
||||
@@ -8,31 +9,21 @@ import com.guo.learningprogresstracker.dto.PriorityDto;
|
||||
public class CalculatedPriorityTool {
|
||||
|
||||
/**
|
||||
* 通过用户选择的5个选项,结合定义的权重计算任务的加权优先级。
|
||||
* 急迫性 (Urgency)
|
||||
* 急迫性代表任务的紧急程度。权重:0.35
|
||||
* <p>
|
||||
* 重要性 (Importance)
|
||||
* 重要性指示任务的重要程度。权重:0.25
|
||||
* <p>
|
||||
* 内容难度 (Content Difficulty)
|
||||
* 内容难度衡量任务内容的复杂性。权重:0.20
|
||||
* <p>
|
||||
* 未来价值 (Future Value)
|
||||
* 未来价值估计完成任务的长期价值。权重:0.10
|
||||
* <p>
|
||||
* 主观优先级 (Subjective Priority)
|
||||
* 主观优先级是用户对任务优先级的个人评估。权重:0.10
|
||||
*
|
||||
* @param priorityDto
|
||||
* @return
|
||||
* 使用系统默认权重计算加权优先级。
|
||||
* 急迫性 0.35 / 重要性 0.25 / 内容难度 0.20 / 未来价值 0.10 / 主观优先级 0.10
|
||||
*/
|
||||
public static Double calculatedPriority(PriorityDto priorityDto) {
|
||||
// 暂时写死权重,也许可以考虑让用户自行配置权重
|
||||
return (priorityDto.getUrgency() * 0.35) +
|
||||
(priorityDto.getImportance() * 0.25) +
|
||||
(priorityDto.getContentDifficulty() * 0.20) +
|
||||
(priorityDto.getFutureValue() * 0.10) +
|
||||
(priorityDto.getSubjectivePriority() * 0.10);
|
||||
return calculatedPriority(priorityDto, UserPriorityWeightsEntity.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用用户自定义权重计算加权优先级。
|
||||
*/
|
||||
public static Double calculatedPriority(PriorityDto priorityDto, UserPriorityWeightsEntity weights) {
|
||||
return (priorityDto.getUrgency() * weights.getUrgencyWeight()) +
|
||||
(priorityDto.getImportance() * weights.getImportanceWeight()) +
|
||||
(priorityDto.getContentDifficulty() * weights.getContentDifficultyWeight()) +
|
||||
(priorityDto.getFutureValue() * weights.getFutureValueWeight()) +
|
||||
(priorityDto.getSubjectivePriority() * weights.getSubjectivePriorityWeight());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
create table user_priority_weights
|
||||
(
|
||||
id int auto_increment comment 'id无业务含义'
|
||||
primary key,
|
||||
urgency_weight double not null default 0.35 comment '紧急性权重',
|
||||
importance_weight double not null default 0.25 comment '重要性权重',
|
||||
content_difficulty_weight double not null default 0.20 comment '内容难度权重',
|
||||
future_value_weight double not null default 0.10 comment '未来价值权重',
|
||||
subjective_priority_weight double not null default 0.10 comment '主观优先级权重',
|
||||
created_time datetime not null comment '创建时间',
|
||||
created_by varchar(255) null,
|
||||
last_modified_time datetime null,
|
||||
last_modified_by varchar(255) null,
|
||||
device_info varchar(50) null comment '操作者设备类型',
|
||||
deleted int default 0 not null comment '逻辑删除符'
|
||||
)
|
||||
comment '用户自定义的任务优先级维度权重(每用户一行)';
|
||||
Reference in New Issue
Block a user