From f89c570ff272b8478537fabf67147517835a125c Mon Sep 17 00:00:00 2001 From: cat_shark <1716967236@qq.com> Date: Mon, 21 Jul 2025 09:18:28 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E9=87=8D=E6=9E=84=E5=80=92?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E7=9B=B8=E5=85=B3=E5=86=85=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E5=B0=86=E5=80=92=E8=AE=A1=E6=97=B6=E9=80=BB=E8=BE=91=E6=94=BE?= =?UTF-8?q?=E5=9C=A8=E5=90=8E=E7=AB=AF=E8=BF=9B=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/StudySessionController.java | 9 +- .../controller/TaskController.java | 3 +- .../dto/response/StudySessionResponse.java | 6 ++ .../entity/StudySessionsEntity.java | 83 ++++++++++++++++++- .../entity/TaskEntity.java | 3 +- .../entity/UserTaskEntity.java | 8 +- .../service/StudySessionsService.java | 5 +- .../impl/StudySessionsServiceImpl.java | 60 ++++++++------ ...20250719_1__add_Field_pointer_position.sql | 3 + 9 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 src/main/resources/db/migration/V20250719_1__add_Field_pointer_position.sql diff --git a/src/main/java/com/guo/learningprogresstracker/controller/StudySessionController.java b/src/main/java/com/guo/learningprogresstracker/controller/StudySessionController.java index f6ea0a8..0d73ac0 100644 --- a/src/main/java/com/guo/learningprogresstracker/controller/StudySessionController.java +++ b/src/main/java/com/guo/learningprogresstracker/controller/StudySessionController.java @@ -1,6 +1,7 @@ package com.guo.learningprogresstracker.controller; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.protobuf.ServiceException; import com.guo.learningprogresstracker.dto.request.EndedStudySessionRequest; import com.guo.learningprogresstracker.dto.response.StudySessionResponse; import com.guo.learningprogresstracker.entity.CommonResult; @@ -49,7 +50,7 @@ public class StudySessionController { * 开始一个【学习会话】 */ @PostMapping("/{sessionNum}/study-sessions/continue") - public CommonResult startStudySession(@PathVariable("sessionNum") String sessionNum) throws ErrorParameterException { + public CommonResult startStudySession(@PathVariable("sessionNum") String sessionNum) throws ErrorParameterException, ServiceException { studySessionsServiceImpl.continueStudySession(sessionNum); return CommonResult.success(); } @@ -58,8 +59,10 @@ public class StudySessionController { * 暂停一个【学习会话】 */ @PostMapping("/{sessionNum}/study-sessions/pause") - public CommonResult pauseStudySession(@PathVariable("sessionNum") String sessionNum, @RequestParam(value = "endTime",required = false) LocalDateTime endTime) throws ErrorParameterException { - studySessionsServiceImpl.pauseStudySession(sessionNum,endTime); + public CommonResult pauseStudySession(@PathVariable("sessionNum") String sessionNum, + @RequestParam(value = "endTime", required = false) LocalDateTime endTime) + throws ErrorParameterException, ServiceException { + studySessionsServiceImpl.pauseStudySession(sessionNum, endTime); return CommonResult.success(); } diff --git a/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java b/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java index 3e427e8..ca03507 100644 --- a/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java +++ b/src/main/java/com/guo/learningprogresstracker/controller/TaskController.java @@ -1,6 +1,7 @@ package com.guo.learningprogresstracker.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.protobuf.ServiceException; import com.guo.learningprogresstracker.common.Ops; import com.guo.learningprogresstracker.dto.TaskInfo; import com.guo.learningprogresstracker.dto.response.StudySessionResponse; @@ -80,7 +81,7 @@ public class TaskController { */ @GetMapping("/{taskNum}/study-sessions/start-or-continue") public CommonResult startOrContinueStudySession(@NotEmpty(message = "taskNum不可为空") - @PathVariable String taskNum) throws NotFindEntitiesException { + @PathVariable String taskNum) throws NotFindEntitiesException, ServiceException { StudySessionResponse response = studySessionsServiceImpl.startOrContinueStudySession(taskNum); return CommonResult.success(response); diff --git a/src/main/java/com/guo/learningprogresstracker/dto/response/StudySessionResponse.java b/src/main/java/com/guo/learningprogresstracker/dto/response/StudySessionResponse.java index d2fb816..1041fac 100644 --- a/src/main/java/com/guo/learningprogresstracker/dto/response/StudySessionResponse.java +++ b/src/main/java/com/guo/learningprogresstracker/dto/response/StudySessionResponse.java @@ -42,4 +42,10 @@ public class StudySessionResponse { @Schema(description = "学习会话的状态(进行中、暂停、已结束)") private String sessionState; + + @Schema(description = "倒计时开始位置(S)") + private double pointerPosition; + + @Schema(description = "系统提示") + private String systemMessage; } diff --git a/src/main/java/com/guo/learningprogresstracker/entity/StudySessionsEntity.java b/src/main/java/com/guo/learningprogresstracker/entity/StudySessionsEntity.java index 83aba96..2baa7df 100644 --- a/src/main/java/com/guo/learningprogresstracker/entity/StudySessionsEntity.java +++ b/src/main/java/com/guo/learningprogresstracker/entity/StudySessionsEntity.java @@ -4,6 +4,7 @@ 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 com.google.protobuf.ServiceException; import com.guo.learningprogresstracker.enums.StudySessionStateEnum; import com.guo.learningprogresstracker.utils.GenerateNumTool; import lombok.Data; @@ -14,6 +15,8 @@ import java.io.Serializable; import java.time.Duration; import java.time.LocalDateTime; +import static com.guo.learningprogresstracker.service.impl.StudySessionsServiceImpl.WORK_DURATION; + /** * 记录每次学习会话的具体数据 * @TableName study_sessions @@ -75,6 +78,12 @@ public class StudySessionsEntity extends BaseEntity implements Serializable { private String sessionState; + /** + * 计时器指针位置,时间戳形式存储(毫秒) + */ + @TableField(value = "pointer_position") + private Long pointerPosition; + @TableField(exist = false) private static final long serialVersionUID = 1L; @@ -94,10 +103,11 @@ public class StudySessionsEntity extends BaseEntity implements Serializable { /** * 继续会话 */ - public void continueStudySession(){ + public void continueStudySession() throws ServiceException { if (this.getSessionState().equals(StudySessionStateEnum.PAUSED.name())){ this.setSessionState(StudySessionStateEnum.ONGOING.name()); this.setLastStartTime(LocalDateTime.now()); + this.calculatePointerPosition(); } else if (this.getSessionState().equals(StudySessionStateEnum.ONGOING.name())) { log.info("进行中的学习会话({})被重新开始",this.getSessionNum()); } else{ @@ -134,4 +144,75 @@ public class StudySessionsEntity extends BaseEntity implements Serializable { this.setSessionState(StudySessionStateEnum.ENDED.name()); } } + + /** + * 第一次运行 + * @return + */ + public boolean isFirstRun() { + if (ObjectUtils.isEmpty(this.lastStartTime)) { + return false; + } else if (this.lastStartTime.equals(this.startTime)) { + return true; + } else { + return false; + } + } + + + /** + * 计算当前倒计时指针的位置(单位:毫秒) + * + * 规则说明: + * - 如果是第一次运行(lastStartTime为null),则倒计时从25分钟开始 + * - 如果是暂停状态,返回上次保存的pointerPosition + * - 如果是进行中状态,返回 pointerPosition -(当前时间 - lastStartTime) + * 即从上次开始后经历的时间,从之前保存的pointerPosition中扣除 + * + * @return 指针当前位置,单位为毫秒 + */ + public void calculatePointerPosition() throws ServiceException { + if (isFirstRun()) { + this.pointerPosition = WORK_DURATION * 60 * 1000L; // 毫秒 + return; + } + + try { + StudySessionStateEnum state = StudySessionStateEnum.valueOf(sessionState); + + switch (state) { + case PAUSED: + // 暂停状态,不修改 pointerPosition + return; + + case ONGOING: + long elapsedMillis = Duration.between(lastStartTime, LocalDateTime.now()).toMillis(); + long newPointerPosition = pointerPosition - elapsedMillis; + if (newPointerPosition <= 0) { + // 如果循环已经结束,则需要重置指针位置 + this.pointerPosition = WORK_DURATION * 1000L; + } else { + this.pointerPosition = newPointerPosition; + } + return; + + case ENDED: + throw new ServiceException("已经结束的任务不应再计算倒计时起始位置"); + + default: + log.error("未知学习会话状态: {}", this.sessionState); + throw new RuntimeException("未知学习会话状态,无法计算倒计时起始位置"); + } + } catch (IllegalArgumentException e) { + log.error("无效的学习会话状态: {}", sessionState, e); + throw new ServiceException("无效的学习会话状态: " + sessionState, e); + } + } + + + public boolean isOverTime() { + LocalDateTime now = LocalDateTime.now(); + long minutes = Duration.between(ObjectUtils.isEmpty(lastStartTime) ? LocalDateTime.now() : lastStartTime, now).toMinutes(); + return minutes >= WORK_DURATION * 2; + } } \ No newline at end of file diff --git a/src/main/java/com/guo/learningprogresstracker/entity/TaskEntity.java b/src/main/java/com/guo/learningprogresstracker/entity/TaskEntity.java index c16f3b3..2733507 100644 --- a/src/main/java/com/guo/learningprogresstracker/entity/TaskEntity.java +++ b/src/main/java/com/guo/learningprogresstracker/entity/TaskEntity.java @@ -1,6 +1,5 @@ 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; @@ -17,7 +16,7 @@ import java.io.Serializable; @Data public class TaskEntity extends BaseEntity implements Serializable { - @TableId(value = "id",type = IdType.AUTO) + @TableId private Integer id; /** diff --git a/src/main/java/com/guo/learningprogresstracker/entity/UserTaskEntity.java b/src/main/java/com/guo/learningprogresstracker/entity/UserTaskEntity.java index 7f7c9b4..19320c3 100644 --- a/src/main/java/com/guo/learningprogresstracker/entity/UserTaskEntity.java +++ b/src/main/java/com/guo/learningprogresstracker/entity/UserTaskEntity.java @@ -1,9 +1,9 @@ package com.guo.learningprogresstracker.entity; import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; -import java.time.LocalDateTime; import lombok.Data; /** @@ -13,10 +13,8 @@ import lombok.Data; @TableName(value ="user_task") @Data public class UserTaskEntity extends BaseEntity implements Serializable { - /** - * - */ - @TableField(value = "id") + + @TableId private String id; /** diff --git a/src/main/java/com/guo/learningprogresstracker/service/StudySessionsService.java b/src/main/java/com/guo/learningprogresstracker/service/StudySessionsService.java index e170252..77a8a4d 100644 --- a/src/main/java/com/guo/learningprogresstracker/service/StudySessionsService.java +++ b/src/main/java/com/guo/learningprogresstracker/service/StudySessionsService.java @@ -1,5 +1,6 @@ package com.guo.learningprogresstracker.service; +import com.google.protobuf.ServiceException; import com.guo.learningprogresstracker.dto.response.StudySessionResponse; import com.guo.learningprogresstracker.entity.StudySessionsEntity; import com.baomidou.mybatisplus.extension.service.IService; @@ -21,7 +22,7 @@ public interface StudySessionsService extends IService { void endedStudySession(String sessionNum, String content) throws ErrorParameterException; - void pauseStudySession(String sessionNum, LocalDateTime endTime) throws ErrorParameterException; + void pauseStudySession(String sessionNum, LocalDateTime endTime) throws ErrorParameterException, ServiceException; - void continueStudySession(String sessionNum) throws ErrorParameterException; + void continueStudySession(String sessionNum) throws ErrorParameterException, ServiceException; } diff --git a/src/main/java/com/guo/learningprogresstracker/service/impl/StudySessionsServiceImpl.java b/src/main/java/com/guo/learningprogresstracker/service/impl/StudySessionsServiceImpl.java index 6421f29..912b3b7 100644 --- a/src/main/java/com/guo/learningprogresstracker/service/impl/StudySessionsServiceImpl.java +++ b/src/main/java/com/guo/learningprogresstracker/service/impl/StudySessionsServiceImpl.java @@ -2,6 +2,7 @@ package com.guo.learningprogresstracker.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.protobuf.ServiceException; import com.guo.learningprogresstracker.dto.StudySessionsDto; import com.guo.learningprogresstracker.dto.response.StudySessionResponse; import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity; @@ -17,6 +18,7 @@ import com.guo.learningprogresstracker.mapper.StudyReportsMapper; import com.guo.learningprogresstracker.mapper.StudySessionsMapper; import com.guo.learningprogresstracker.service.StudySessionsService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -29,46 +31,55 @@ import java.util.stream.Collectors; * @description 针对表【study_sessions(记录每次学习会话的具体数据)】的数据库操作Service实现 * @createDate 2024-06-09 15:28:48 */ +@Slf4j @Service @RequiredArgsConstructor public class StudySessionsServiceImpl extends ServiceImpl implements StudySessionsService { + public static final int WORK_DURATION = 25; private final TasksServiceImpl tasksServiceImpl; private final StudyReportFragmentsServiceImpl studyReportFragmentsServiceImpl; private final StudyReportFragmentsMapper studyReportFragmentsMapper; private final StudyReportsMapper studyReportsMapper; - public StudySessionResponse startOrContinueStudySession(String taskNum) throws NotFindEntitiesException { - // taskNum是否存在 - TaskEntity taskEntity = tasksServiceImpl.getOneOpt(Wrappers.lambdaQuery(TaskEntity.class).eq(TaskEntity::getTaskNum, taskNum)) + public StudySessionResponse startOrContinueStudySession(String taskNum) throws NotFindEntitiesException, ServiceException { + TaskEntity taskEntity = tasksServiceImpl.getOneOpt( + Wrappers.lambdaQuery(TaskEntity.class).eq(TaskEntity::getTaskNum, taskNum)) .orElseThrow(() -> new NotFindEntitiesException(String.format("[%s]不存在", taskNum))); - // 任务taskNum下是否存在非"已结束"的学习会话 - Optional oneOpt = this.getOneOpt(Wrappers.lambdaQuery() - .eq(StudySessionsEntity::getTaskNum, taskNum) - .ne(StudySessionsEntity::getSessionState, StudySessionStateEnum.ENDED.name())); - StudySessionsEntity studySessionsEntity; - // 有则返回该未完成会话,否则创建新的会话 - if (oneOpt.isEmpty()) { - // 返回一个新的会话 - studySessionsEntity = StudySessionsEntity.initNewSession(); - studySessionsEntity.setTaskNum(taskNum); - studySessionsEntity.setLastStartTime(LocalDateTime.now()); - this.save(studySessionsEntity); - } else { - // 返回已经存在的会话 - studySessionsEntity = oneOpt.get(); - // todo 是否调整任务状态,需要再思考思考 -// studySessionsEntity.continueStudySession(); - this.updateById(studySessionsEntity); + + StudySessionsEntity session = this.getOneOpt(Wrappers.lambdaQuery() + .eq(StudySessionsEntity::getTaskNum, taskNum) + .ne(StudySessionsEntity::getSessionState, StudySessionStateEnum.ENDED.name())) + .orElseGet(() -> { + // 如果没有则创建新会话 + StudySessionsEntity newSession = StudySessionsEntity.initNewSession(); + newSession.setTaskNum(taskNum); + newSession.setLastStartTime(LocalDateTime.now()); + this.save(newSession); + return newSession; + }); + + session.calculatePointerPosition(); + + if (session.isOverTime()) { + session.pausedStudySession(session.getLastStartTime().plusMinutes(WORK_DURATION)); + this.updateById(session); } - StudySessionResponse response = StudySessionConvert.MAPPER.toStudySessionResponse(studySessionsEntity); + + StudySessionResponse response = StudySessionConvert.MAPPER.toStudySessionResponse(session); response.setTaskName(taskEntity.getTaskName()); + + if (session.isOverTime()) { + response.setSystemMessage("上段学习任务已经超时25分钟,将仅计算为25分钟的有效学习时间,请注意休息!"); + } + return response; } + @Override - public void pauseStudySession(String sessionNum, LocalDateTime endTime) throws ErrorParameterException { + public void pauseStudySession(String sessionNum, LocalDateTime endTime) throws ErrorParameterException, ServiceException { StudySessionsEntity studySessionsEntity = this.getOneOpt(Wrappers.lambdaQuery(StudySessionsEntity.class) .eq(StudySessionsEntity::getSessionNum, sessionNum)) .orElseThrow(() -> new ErrorParameterException("会话[" + sessionNum + "]不存在")); @@ -76,6 +87,7 @@ public class StudySessionsServiceImpl extends ServiceImpl new ErrorParameterException("会话[" + sessionNum + "]不存在")); diff --git a/src/main/resources/db/migration/V20250719_1__add_Field_pointer_position.sql b/src/main/resources/db/migration/V20250719_1__add_Field_pointer_position.sql new file mode 100644 index 0000000..de8c4c9 --- /dev/null +++ b/src/main/resources/db/migration/V20250719_1__add_Field_pointer_position.sql @@ -0,0 +1,3 @@ +alter table study_sessions + add pointer_position bigint null comment '计时器指针位置'; +