diff --git a/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java b/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java index 4c6ef08..67d263b 100644 --- a/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java +++ b/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java @@ -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 getPriorityWeights() { + return CommonResult.success(priorityWeightsService.getWeights()); + } + + @PutMapping("/priority-weights") + @Operation(summary = "保存优先级维度权重配置并重算全部任务优先级") + public CommonResult savePriorityWeights( + @RequestBody UserPriorityWeightsEntity weights) throws ErrorParameterException { + return CommonResult.success(priorityWeightsService.saveWeights(weights)); + } + @PostMapping @Operation(summary = "添加新任务") public CommonResult addTask(@RequestBody @Validated({Ops.CreateG.class}) TaskRequest taskRequest) throws ErrorParameterException { diff --git a/src/main/java/com/guo/learningprogresstracker/entity/UserPriorityWeightsEntity.java b/src/main/java/com/guo/learningprogresstracker/entity/UserPriorityWeightsEntity.java new file mode 100644 index 0000000..0d66c2c --- /dev/null +++ b/src/main/java/com/guo/learningprogresstracker/entity/UserPriorityWeightsEntity.java @@ -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; + } +} diff --git a/src/main/java/com/guo/learningprogresstracker/mapper/UserPriorityWeightsMapper.java b/src/main/java/com/guo/learningprogresstracker/mapper/UserPriorityWeightsMapper.java new file mode 100644 index 0000000..c9092d0 --- /dev/null +++ b/src/main/java/com/guo/learningprogresstracker/mapper/UserPriorityWeightsMapper.java @@ -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 { +} diff --git a/src/main/java/com/guo/learningprogresstracker/service/PriorityWeightsService.java b/src/main/java/com/guo/learningprogresstracker/service/PriorityWeightsService.java new file mode 100644 index 0000000..d5e8a46 --- /dev/null +++ b/src/main/java/com/guo/learningprogresstracker/service/PriorityWeightsService.java @@ -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; +} diff --git a/src/main/java/com/guo/learningprogresstracker/service/impl/PriorityWeightsServiceImpl.java b/src/main/java/com/guo/learningprogresstracker/service/impl/PriorityWeightsServiceImpl.java new file mode 100644 index 0000000..95c8172 --- /dev/null +++ b/src/main/java/com/guo/learningprogresstracker/service/impl/PriorityWeightsServiceImpl.java @@ -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.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.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 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; + } +} diff --git a/src/main/java/com/guo/learningprogresstracker/service/impl/TasksServiceImpl.java b/src/main/java/com/guo/learningprogresstracker/service/impl/TasksServiceImpl.java index dd050d7..794a731 100644 --- a/src/main/java/com/guo/learningprogresstracker/service/impl/TasksServiceImpl.java +++ b/src/main/java/com/guo/learningprogresstracker/service/impl/TasksServiceImpl.java @@ -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 private final TaskApplicationMapper taskApplicationMapper; + private final PriorityWeightsService priorityWeightsService; + @Override public Page taskList(Integer pageNum, Integer pageSize) { Page page = tasksMapper.selectPage(new Page(pageNum, pageSize), @@ -62,7 +65,7 @@ public class TasksServiceImpl extends ServiceImpl 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 throw new IllegalArgumentException("任务ID格式错误: " + taskId); } taskEntity.setId(id); + // 维度数据可能变化,更新时重算优先级 + PriorityDto priorityDto = RequestConvert.MAPPER.taskRequestToPriorityDto(updatedTask); + taskEntity.setCalculatedPriority(CalculatedPriorityTool.calculatedPriority(priorityDto, priorityWeightsService.getWeights())); this.updateById(taskEntity); } diff --git a/src/main/java/com/guo/learningprogresstracker/utils/CalculatedPriorityTool.java b/src/main/java/com/guo/learningprogresstracker/utils/CalculatedPriorityTool.java index 8fd8090..d87ce5e 100644 --- a/src/main/java/com/guo/learningprogresstracker/utils/CalculatedPriorityTool.java +++ b/src/main/java/com/guo/learningprogresstracker/utils/CalculatedPriorityTool.java @@ -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 - *

- * 重要性 (Importance) - * 重要性指示任务的重要程度。权重:0.25 - *

- * 内容难度 (Content Difficulty) - * 内容难度衡量任务内容的复杂性。权重:0.20 - *

- * 未来价值 (Future Value) - * 未来价值估计完成任务的长期价值。权重:0.10 - *

- * 主观优先级 (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()); } } diff --git a/src/main/resources/db/migration/V20260703_2__add_user_priority_weights.sql b/src/main/resources/db/migration/V20260703_2__add_user_priority_weights.sql new file mode 100644 index 0000000..4b39527 --- /dev/null +++ b/src/main/resources/db/migration/V20260703_2__add_user_priority_weights.sql @@ -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 '用户自定义的任务优先级维度权重(每用户一行)';