From dfafd2c095814d04d399f5003a85a228e625feb4 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 20 Jul 2026 11:00:31 +0700 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E8=AE=A2=E5=8D=95=E5=88=97?= =?UTF-8?q?=E8=A1=A8V4=E5=85=A5=E5=8F=A3=E5=9B=9E=E5=BD=92=E9=AA=8C?= =?UTF-8?q?=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PROJECT_STATE.md | 14 +++---- ...002-v4-order-task-card-domain-model-cp2.md | 10 ++--- ...eservationFrontendQueryControllerTest.java | 40 +++++++++++++++++++ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index 56dbd52..c616d1f 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -2,17 +2,17 @@ | 项 | 内容 | | --- | --- | -| 最近更新 | 2026-07-19 | +| 最近更新 | 2026-07-20 | | 当前分支 | `feature/huangting` | -| 当前阶段 | M002 V4 入站、多卡模型、持久化基线、入站写入、查询接口、卡片确认、复核解阻、目录校验、订单详情时间线、DB 目录、Lookup API、前端 lookup 接入和目录管理后台 CP1 前后端 | -| 当前重点 | M002 V4 已完成 DB 管理目录与 Lookup API V1 后端、前端 V4 多卡字段 lookup 第一版接入,并完成系统设置下 Account / Room Type / Rate Code 目录管理前端 CP1;后续可做测试机联调、真实 PMS / OPERA / OHIP 同步或 SuperAgent 目录供给方案 | +| 当前阶段 | M002 V4 入站、多卡模型、持久化基线、入站写入、查询接口、卡片确认、复核解阻、目录校验、订单详情时间线、DB 目录、Lookup API、前端 lookup 接入、目录管理后台 CP1 前后端和订单列表 V4 继续处理入口 | +| 当前重点 | M002 V4 已完成订单列表 V4 继续处理入口字段,`GET /api/reservation/orders` 可返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数;后续可做测试机联调、真实 PMS / OPERA / OHIP 同步或 SuperAgent 目录供给方案 | ## 1. 当前 Checkpoint -- 名称:`M002-V4-CP13-catalog-admin-cp1` -- 状态:Done,后端已开放 Account / Room Type / Rate Code 目录管理后台 CP1 接口,前端已在系统设置新增 `/system/reservation-catalogs` 页面;支持列表查询、新增、启用 / 停用;权限使用 `RESERVATION_CATALOG_MANAGE`,强制 Bearer 登录、权限码、酒店访问权和管理审计。 -- 目标:让测试机和系统设置页面可以维护第一版数据库目录,停用目录不再进入普通 V4 lookup。 -- 边界:本轮不接真实 PMS / OPERA / OHIP 同步,不做 Market / Source 独立管理页面,不新增 `workflow_reservation_catalog_sync_run`,不做 SuperAgent 目录机器接口,不废弃旧 V2/V3 接口。 +- 名称:`M002-V4-CP14-order-list-v4-entrypoint` +- 状态:Done,订单列表接口已补齐 V4 下一步处理入口字段:`next_v4_order_task_id`、`next_v4_action_card_id`、`next_v4_action_type`、`next_v4_action_status`、`v4_open_order_task_count`。旧 `open_task_count` 和 `next_processable_task_id` 保持兼容。 +- 目标:让前端订单列表“继续处理”在存在 V4 待处理订单任务时可优先跳转 V4 订单任务详情;无 V4 待处理时继续回退旧 V2/V3 任务入口。 +- 边界:本轮不改前端页面、不改 V4 确认 / 复核接口、不接真实 OPERA / OHIP、不废弃旧 V2/V3 任务入口、不把 S10/S99 来源通知放进订单列表。 ## 2. 当前优先级 diff --git a/docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md b/docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md index 5fcc63a..6972272 100644 --- a/docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md +++ b/docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md @@ -4,9 +4,9 @@ | 项目 | 内容 | | --- | --- | -| 文档版本 | 0.6 | -| 日期 | 2026-07-19 | -| 状态 | CP2 设计已确认;CP3-CP8 已实现;CP11 DB 目录与 Lookup API V1 已实现 | +| 文档版本 | 0.7 | +| 日期 | 2026-07-20 | +| 状态 | CP2 设计已确认;CP3-CP8、CP11、CP13、CP14 已实现 | | 适用范围 | M002 V4 入站后的订单任务、多卡、状态、查询和写操作设计 | | 不适用范围 | V4 复核接口、真实 PMS / OPERA / OHIP、前端页面视觉稿、历史数据迁移 | @@ -16,7 +16,7 @@ M002 V4 CP1 已完成 SuperAgent V4 回调包入站解析、基础校验、路 本文是 CP2 设计文档,用于把 2026-07-18 V4 字段契约落成后续可开发的数据模型和接口草案。 -截至 CP11,后端已实现本文第 10、11 节中的持久化基线,并已把 SuperAgent V4 入站结果写入新表:普通业务包创建 V4 订单任务、来源邮件展示卡、Basic Information 卡和业务卡;V4 S10/S99 创建来源通知。当前已开放 V4 工作台、订单任务列表 / 详情、来源通知详情查询接口、订单详情 V4 订单任务时间线、V4 卡片确认接口、S10/S99 来源通知 ack 接口、V4 `REVIEW_REQUIRED` 卡复核解阻接口、当前酒店数据库目录校验、卡片 `fields[]` 白名单,以及 Account / Room Type / Rate Code lookup API。真实 PMS 同步和目录管理后台继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`。 +截至 CP14,后端已实现本文第 10、11、12 节中的持久化和查询基线,并已把 SuperAgent V4 入站结果写入新表:普通业务包创建 V4 订单任务、来源邮件展示卡、Basic Information 卡和业务卡;V4 S10/S99 创建来源通知。当前已开放 V4 工作台、订单任务列表 / 详情、来源通知详情查询接口、订单详情 V4 订单任务时间线、V4 卡片确认接口、S10/S99 来源通知 ack 接口、V4 `REVIEW_REQUIRED` 卡复核解阻接口、当前酒店数据库目录校验、卡片 `fields[]` 白名单、Account / Room Type / Rate Code lookup API、目录管理后台 CP1,以及订单列表 V4 继续处理入口字段。真实 PMS 同步继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`。 后续如本文与 `M002-v4-agent-callback-field-contract.md` 的字段契约冲突,以字段契约为准;如与安全边界冲突,以 `security-access-control-boundary.md` 为准。 @@ -389,7 +389,7 @@ uk_reservation_v4_notification_message_batch(hotel_id, source_message_id, ai_bat | V4 草稿表 | V4 已确认不保存草稿 | | V4 OPERA operation 表 | 当前不做真实 OPERA / OHIP,也不生成 OPERA 模拟 | | 技术错误用户任务表 | 技术错误不进入用户任务体系,先用 AI transition 和 dispatch run | -| 目录表 | CP11 已新增 `workflow_reservation_catalog_account`、`workflow_reservation_catalog_code`;真实同步 run 和目录管理后台后置 | +| 目录表 | CP11 已新增 `workflow_reservation_catalog_account`、`workflow_reservation_catalog_code`;CP13 已完成目录管理后台 CP1;真实同步 run 后置 | ## 11. Entity / Mapper / Repository 边界草案 diff --git a/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/ReservationFrontendQueryControllerTest.java b/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/ReservationFrontendQueryControllerTest.java index c1fd7c1..c3b69b8 100644 --- a/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/ReservationFrontendQueryControllerTest.java +++ b/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/ReservationFrontendQueryControllerTest.java @@ -26,6 +26,7 @@ import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOrderSnapsh import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskDraft; import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationV4OrderTaskDraft; import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationV4OrderTaskSnapshot; +import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationV4SourceNotificationDraft; import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationV4TaskCardDraft; import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationV4TaskCardSnapshot; import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOrderKeyType; @@ -34,9 +35,11 @@ import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOrderVisi import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationTaskStatus; import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationV4CardStatus; import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationV4CardType; +import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationV4NotificationStatus; import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationV4OrderTaskStatus; import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationV4TargetResolutionStatus; import cn.nianxx.thhotel.workflows.reservation.repository.ReservationAiWorkflowRepository; +import cn.nianxx.thhotel.workflows.reservation.repository.ReservationV4SourceNotificationRepository; import cn.nianxx.thhotel.workflows.reservation.repository.ReservationV4WorkflowRepository; import java.sql.Timestamp; import java.time.Instant; @@ -86,6 +89,9 @@ class ReservationFrontendQueryControllerTest { @Autowired private ReservationV4WorkflowRepository v4WorkflowRepository; + @Autowired + private ReservationV4SourceNotificationRepository sourceNotificationRepository; + @Autowired private JdbcTemplate jdbcTemplate; @@ -640,6 +646,11 @@ class ReservationFrontendQueryControllerTest { ReservationV4CardStatus.PENDING_CONFIRM.name(), null, "{}"); insertV4TaskCard(basicFirstOrderTask, ReservationV4CardType.ROOM_INFORMATION.name(), "NEW_BOOKING", 1, 30, ReservationV4CardStatus.REVIEW_REQUIRED.name(), "PENDING", "{}"); + insertV4SourceNotification( + source.inboxId(), + 930000000000004901L, + "S10", + Instant.parse("2026-07-08T06:45:00Z")); ReservationV4OrderTaskSnapshot laterOrderTask = insertV4OrderTask( 930000000000004102L, source, @@ -700,6 +711,15 @@ class ReservationFrontendQueryControllerTest { completedOrderTask.id(), ReservationV4OrderTaskStatus.COMPLETED.name(), completedOrderTask.updatedAt()); + SourceMessageCaptureResult standaloneNotificationSource = captureSourceMessage( + "mail-frontend-order-list-v4-cp14-s99-standalone", + "GRP-FRONTEND-V4-CP14-S99 standalone notification", + Instant.parse("2026-07-08T11:30:00Z")); + insertV4SourceNotification( + standaloneNotificationSource.inboxId(), + 930000000000004902L, + "S99", + Instant.parse("2026-07-08T11:30:00Z")); performAuthorized(mockMvc, adminToken(), get("/api/reservation/orders") .param("hotel_id", HOTEL_ID) @@ -707,6 +727,7 @@ class ReservationFrontendQueryControllerTest { .param("page_num", "1") .param("page_size", "20")) .andExpect(status().isOk()) + .andExpect(jsonPath("$.page.total").value(5)) .andExpect(jsonPath("$.items[?(@.order_id=='" + basicFirstOrderId + "')].next_processable_task_id") .value(contains(legacyTaskId.toString()))) .andExpect(jsonPath("$.items[?(@.order_id=='" + basicFirstOrderId + "')].v4_open_order_task_count") @@ -857,6 +878,25 @@ class ReservationFrontendQueryControllerTest { now)); } + private void insertV4SourceNotification( + Long sourceMessageId, + Long aiBatchId, + String routeCode, + Instant sourceReceivedAt) { + sourceNotificationRepository.findOrCreateSourceNotification(new ReservationV4SourceNotificationDraft( + HOTEL_ID, + sourceMessageId, + aiBatchId, + null, + routeCode, + ReservationV4NotificationStatus.ACK_REQUIRED.name(), + """ + {"route_code":"%s","source_message":{"body":"S10/S99 notification should stay outside orders"}} + """.formatted(routeCode), + LocalDateTime.ofInstant(sourceReceivedAt, ZoneOffset.UTC), + LocalDateTime.ofInstant(sourceReceivedAt.plusSeconds(20), ZoneOffset.UTC))); + } + private ReservationV4TaskCardSnapshot insertV4TaskCard( ReservationV4OrderTaskSnapshot orderTask, String cardType,