补充时间规范并修复非队列任务状态判断
This commit is contained in:
@@ -93,6 +93,8 @@
|
|||||||
- 生成代码前必须先参考 `AGENTS.md`、当前项目后端规范和既有代码习惯;遇到类职责或目录归属不确定时,先询问再继续,不能先写完再统一重构。
|
- 生成代码前必须先参考 `AGENTS.md`、当前项目后端规范和既有代码习惯;遇到类职责或目录归属不确定时,先询问再继续,不能先写完再统一重构。
|
||||||
- 不写魔法值,稳定业务代码使用常量或枚举。
|
- 不写魔法值,稳定业务代码使用常量或枚举。
|
||||||
- 集合、空值、字符串、时间、金额和 `BigDecimal` 使用安全写法。
|
- 集合、空值、字符串、时间、金额和 `BigDecimal` 使用安全写法。
|
||||||
|
- 后端业务时间点统一按 UTC 处理:数据库 `LocalDateTime` 默认表示 UTC,API 返回时间点字段必须使用带 `Z` 的 ISO 8601 UTC 时间;新增响应 DTO 优先使用 `OffsetDateTime`,从数据库快照输出时使用 `UtcTimeFormatter` 统一转换。
|
||||||
|
- 入住日期、离店日期、酒店营业日等酒店本地业务日期不得和 UTC 时间点混用,应使用 `LocalDate` 或明确酒店时区语义的字段。
|
||||||
- 异常不吞掉,日志有上下文但不输出 Secret 或个人敏感信息。
|
- 异常不吞掉,日志有上下文但不输出 Secret 或个人敏感信息。
|
||||||
- 写操作考虑幂等、并发版本、事务边界、审计和失败恢复。
|
- 写操作考虑幂等、并发版本、事务边界、审计和失败恢复。
|
||||||
- 复杂业务规则、外部字段映射、脱敏和幂等逻辑必须有必要中文注释。
|
- 复杂业务规则、外部字段映射、脱敏和幂等逻辑必须有必要中文注释。
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ groupCode
|
|||||||
- 注释必须说明业务含义、来源或代码值范围。
|
- 注释必须说明业务含义、来源或代码值范围。
|
||||||
- 禁止使用“字段1”“备用字段”等模糊注释。
|
- 禁止使用“字段1”“备用字段”等模糊注释。
|
||||||
- 业务时间点以 UTC 写入数据库,API 层负责返回带 `Z` 的 ISO 8601 UTC 时间,例如 `2026-07-08T03:00:00Z`。
|
- 业务时间点以 UTC 写入数据库,API 层负责返回带 `Z` 的 ISO 8601 UTC 时间,例如 `2026-07-08T03:00:00Z`。
|
||||||
|
- 新增 API 响应 DTO 中的时间点字段优先使用 `OffsetDateTime`;从数据库 `LocalDateTime` 快照输出到接口时,应通过 `UtcTimeFormatter` 转换,禁止直接把无时区 `LocalDateTime` 暴露给前端或 SuperAgent。
|
||||||
- 酒店本地业务日期,例如入住日期、离店日期、营业日,优先使用 `LocalDate` 或明确酒店时区语义的字段,不和 UTC 时间点混用。
|
- 酒店本地业务日期,例如入住日期、离店日期、营业日,优先使用 `LocalDate` 或明确酒店时区语义的字段,不和 UTC 时间点混用。
|
||||||
- JSON 字段只用于扩展元数据,不替代需要查询、约束或索引的正式列。
|
- JSON 字段只用于扩展元数据,不替代需要查询、约束或索引的正式列。
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ public class ReservationTaskAvailabilityResolver {
|
|||||||
* 计算任务可处理状态。FAILED 和 COMPLETED 视为前置任务结束,不阻塞后续任务。
|
* 计算任务可处理状态。FAILED 和 COMPLETED 视为前置任务结束,不阻塞后续任务。
|
||||||
*/
|
*/
|
||||||
public ReservationTaskAvailabilityResult calculateAvailability(ReservationTaskSnapshot task) {
|
public ReservationTaskAvailabilityResult calculateAvailability(ReservationTaskSnapshot task) {
|
||||||
|
if (!Boolean.TRUE.equals(task.queueParticipation())) {
|
||||||
|
return calculateAvailability(task, null);
|
||||||
|
}
|
||||||
ReservationTaskSnapshot blockingTask = workflowRepository
|
ReservationTaskSnapshot blockingTask = workflowRepository
|
||||||
.findQueueTasksBefore(task.hotelId(), task.orderId(), task.executionOrder())
|
.findQueueTasksBefore(task.hotelId(), task.orderId(), task.executionOrder())
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package cn.nianxx.thhotel.workflows.reservation.service.impl;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskSnapshot;
|
||||||
|
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationTaskStatus;
|
||||||
|
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationTaskAvailabilityResult;
|
||||||
|
import cn.nianxx.thhotel.workflows.reservation.repository.ReservationAiWorkflowRepository;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class ReservationTaskAvailabilityResolverTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnReadOnlyForNonQueueTaskWithoutQueryingPreviousTasks() {
|
||||||
|
ReservationAiWorkflowRepository workflowRepository = mock(ReservationAiWorkflowRepository.class);
|
||||||
|
when(workflowRepository.findQueueTasksBefore(anyString(), anyLong(), nullable(Integer.class)))
|
||||||
|
.thenReturn(List.of());
|
||||||
|
ReservationTaskAvailabilityResolver resolver = new ReservationTaskAvailabilityResolver(workflowRepository);
|
||||||
|
ReservationTaskSnapshot nonQueueTask = task(false, null, ReservationTaskStatus.PENDING_CONFIRM.name());
|
||||||
|
|
||||||
|
ReservationTaskAvailabilityResult result = resolver.calculateAvailability(nonQueueTask);
|
||||||
|
|
||||||
|
assertThat(result.blocked()).isFalse();
|
||||||
|
assertThat(result.readOnly()).isTrue();
|
||||||
|
assertThat(result.editable()).isFalse();
|
||||||
|
assertThat(result.confirmable()).isFalse();
|
||||||
|
assertThat(result.executable()).isFalse();
|
||||||
|
verify(workflowRepository, never()).findQueueTasksBefore(anyString(), anyLong(), nullable(Integer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReservationTaskSnapshot task(Boolean queueParticipation, Integer executionOrder, String taskStatus) {
|
||||||
|
return new ReservationTaskSnapshot(
|
||||||
|
930000000000001001L,
|
||||||
|
"HOTEL-TEST",
|
||||||
|
930000000000001101L,
|
||||||
|
930000000000001201L,
|
||||||
|
930000000000001301L,
|
||||||
|
"message_notification",
|
||||||
|
"Message Notification",
|
||||||
|
"MESSAGE_NOTIFICATION",
|
||||||
|
"MESSAGE_NOTIFICATION",
|
||||||
|
"message_notification",
|
||||||
|
taskStatus,
|
||||||
|
queueParticipation,
|
||||||
|
executionOrder,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user