接入订单列表V4继续处理入口
This commit is contained in:
@@ -5,14 +5,14 @@
|
||||
| 最近更新 | 2026-07-20 |
|
||||
| 当前分支 | `feature/huangting` |
|
||||
| 当前阶段 | M002 V4 入站、多卡模型、持久化基线、入站写入、查询接口、卡片确认、复核解阻、目录校验、订单详情时间线、DB 目录、Lookup API、前端 lookup 接入、目录管理后台 CP1 前后端和订单列表 V4 继续处理入口 |
|
||||
| 当前重点 | M002 V4 已完成订单列表 V4 继续处理入口字段,`GET /api/reservation/orders` 可返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数;后续可做测试机联调、真实 PMS / OPERA / OHIP 同步或 SuperAgent 目录供给方案 |
|
||||
| 当前重点 | M002 V4 已完成订单列表 V4 继续处理入口字段和前端消费,`GET /api/reservation/orders` 可返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数;前端“继续处理”已按 V4 优先、旧任务回退跳转;后续可做测试机联调、真实 PMS / OPERA / OHIP 同步或 SuperAgent 目录供给方案 |
|
||||
|
||||
## 1. 当前 Checkpoint
|
||||
|
||||
- 名称:`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` 保持兼容。
|
||||
- 状态: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`。前端订单列表已优先使用 `next_v4_order_task_id` 跳转 V4 订单任务详情,并在无 V4 入口时回退旧 `next_processable_task_id`。
|
||||
- 目标:让前端订单列表“继续处理”在存在 V4 待处理订单任务时可优先跳转 V4 订单任务详情;无 V4 待处理时继续回退旧 V2/V3 任务入口。
|
||||
- 边界:本轮不改前端页面、不改 V4 确认 / 复核接口、不接真实 OPERA / OHIP、不废弃旧 V2/V3 任务入口、不把 S10/S99 来源通知放进订单列表。
|
||||
- 边界:本轮不改后端接口、不改 V4 确认 / 复核接口、不接真实 OPERA / OHIP、不废弃旧 V2/V3 任务入口、不把 S10/S99 来源通知放进订单列表。
|
||||
|
||||
## 2. 当前优先级
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
- 后续每完成一个 Feature 或 Checkpoint,需要更新本文件,避免项目状态继续沉淀在聊天记录里。
|
||||
- M010 Rooming List Excel 生成后端 CP1 和前端 V1 已实现:前端 `/reservation/rooming-lists/new` 上传来源名单和手工字段,后端同步生成 `.xlsx` 直接下载,第一版不落库、不上传 OSS。
|
||||
- M011 Booking Excel 附件预处理 CP1/CP2/CP3 已实现:后端可排除人员名单类 Excel,按最近 6 个月候选窗口选择实际存在的最新 3 个业务月,抽取 Booking Update / 附加费表高亮行业务 JSON;Debug EML 和 AgentBus dispatch 在各自 include 开关与总开关同时启用时,会在调用 SuperAgent 前追加 `attachment_extractions[]`。AgentBus 生产链路默认关闭,测试机验证后再评估开启。
|
||||
- M002 V4 CP1 当前已完成入站解析和现有任务链路过渡适配;M002 V4 CP2 已完成订单任务与多卡领域模型设计;M002 V4 CP3 已完成 V4 订单任务、多卡和 S10/S99 来源通知表结构与 Repository 基线;M002 V4 CP4 已完成入站写入新模型;M002 V4 CP5 已完成前端查询接口并补齐订单详情 `v4_order_tasks[]` 时间线;M002 V4 CP6 已完成普通卡片确认和 S10/S99 来源通知 ack;M002 V4 CP7 已完成 `REVIEW_REQUIRED` 卡复核解阻和复核场景订单归属确认;M002 V4 CP8 已完成目录校验、V4 卡片 `fields[]` 字段白名单、确认写入白名单收口和嵌套业务字段目录校验;M002 V4 CP11 已完成数据库目录、初始化种子、启动补种子、Account / Room Type / Rate Code lookup API,并把 V4 入站、确认、复核目录校验切换到当前酒店数据库目录;M002 V4 CP12 已完成前端 lookup 接入第一版和 V4 订单任务时间线消费;M002 V4 CP13 目录管理后台 CP1 已完成前后端列表、新增、启用 / 停用闭环;M002 V4 CP14 已完成订单列表 V4 继续处理入口字段,`GET /api/reservation/orders` 返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数。真实 PMS 同步和 SuperAgent 目录机器接口仍未完成。
|
||||
- M002 V4 CP1 当前已完成入站解析和现有任务链路过渡适配;M002 V4 CP2 已完成订单任务与多卡领域模型设计;M002 V4 CP3 已完成 V4 订单任务、多卡和 S10/S99 来源通知表结构与 Repository 基线;M002 V4 CP4 已完成入站写入新模型;M002 V4 CP5 已完成前端查询接口并补齐订单详情 `v4_order_tasks[]` 时间线;M002 V4 CP6 已完成普通卡片确认和 S10/S99 来源通知 ack;M002 V4 CP7 已完成 `REVIEW_REQUIRED` 卡复核解阻和复核场景订单归属确认;M002 V4 CP8 已完成目录校验、V4 卡片 `fields[]` 字段白名单、确认写入白名单收口和嵌套业务字段目录校验;M002 V4 CP11 已完成数据库目录、初始化种子、启动补种子、Account / Room Type / Rate Code lookup API,并把 V4 入站、确认、复核目录校验切换到当前酒店数据库目录;M002 V4 CP12 已完成前端 lookup 接入第一版和 V4 订单任务时间线消费;M002 V4 CP13 目录管理后台 CP1 已完成前后端列表、新增、启用 / 停用闭环;M002 V4 CP14 已完成订单列表 V4 继续处理入口字段和前端入口消费,`GET /api/reservation/orders` 返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数,前端按 V4 优先、旧任务回退跳转。真实 PMS 同步和 SuperAgent 目录机器接口仍未完成。
|
||||
- M002 V4 CP2 已确认:V4 工作台统一列表草案为 `/api/reservation/workbench-items`,业务订单任务接口新开 `/api/reservation/order-tasks/**`,S10/S99 来源通知详情草案为 `/api/reservation/source-notifications/{notificationId}`;S10/S99 使用来源通知模型,不再挂隐藏技术订单;`FIT + BOOKING_CODE` 不建 ACTIVE 唯一约束,匹配多条进人工复核;Basic Information 必须先确认;Account / Market / Source 当前通过数据库目录读取和派生;旧 V2/V3 任务详情和草稿确认接口后续可逐步废弃。
|
||||
|
||||
## 5. Next Steps
|
||||
|
||||
@@ -391,6 +391,11 @@ describe('reservationService real API mode', () => {
|
||||
display_name: 'GRP-001',
|
||||
open_task_count: 2,
|
||||
next_processable_task_id: '10002',
|
||||
next_v4_order_task_id: '9001',
|
||||
next_v4_action_card_id: '9101',
|
||||
next_v4_action_type: 'REVIEW',
|
||||
next_v4_action_status: 'REVIEW_REQUIRED',
|
||||
v4_open_order_task_count: 1,
|
||||
created_at: '2026-07-08T03:00:00Z',
|
||||
updated_at: '2026-07-08T03:10:00Z',
|
||||
},
|
||||
@@ -415,6 +420,10 @@ describe('reservationService real API mode', () => {
|
||||
)
|
||||
expect(result.items[0]?.display_order_key).toBe('GRP-001')
|
||||
expect(result.items[0]?.next_processable_task_id).toBe('10002')
|
||||
expect(result.items[0]?.next_v4_order_task_id).toBe('9001')
|
||||
expect(result.items[0]?.next_v4_action_type).toBe('REVIEW')
|
||||
expect(result.items[0]?.next_v4_action_status).toBe('REVIEW_REQUIRED')
|
||||
expect(result.items[0]?.v4_open_order_task_count).toBe(1)
|
||||
})
|
||||
|
||||
it('passes order status and business-key filters to the backend', async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import ReservationSourceMessageConversationView from '@/views/reservation/Reserv
|
||||
import ReservationTaskListView from '@/views/reservation/ReservationTaskListView.vue'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
import type {
|
||||
ReservationOrderListResult,
|
||||
ReservationV4OrderTaskListResult,
|
||||
ReservationV4WorkbenchListResult,
|
||||
SourceMessageConversationResult,
|
||||
@@ -38,7 +39,8 @@ function createOrderListResult(
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
},
|
||||
) {
|
||||
orderOverrides: Partial<ReservationOrderListResult['items'][number]> = {},
|
||||
): ReservationOrderListResult {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
@@ -54,6 +56,7 @@ function createOrderListResult(
|
||||
next_processable_task_id: '10002',
|
||||
created_at: '2026-07-08T03:00:00Z',
|
||||
updated_at: '2026-07-08T03:10:00Z',
|
||||
...orderOverrides,
|
||||
},
|
||||
],
|
||||
page,
|
||||
@@ -455,7 +458,8 @@ async function mountWithPlugins(component: object, initialPath = '/', stubs: Rec
|
||||
plugins: [pinia, i18n, router],
|
||||
stubs: {
|
||||
RouterLink: {
|
||||
template: '<a><slot /></a>',
|
||||
props: ['to'],
|
||||
template: '<a :href="to"><slot /></a>',
|
||||
},
|
||||
...stubs,
|
||||
},
|
||||
@@ -512,6 +516,83 @@ describe('reservation P0 views', () => {
|
||||
expect(wrapper.text()).not.toContain('接口待接入')
|
||||
})
|
||||
|
||||
it('routes continue action to the V4 order task when the backend returns next_v4_order_task_id', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult('GRP-001', {
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
}, {
|
||||
next_v4_order_task_id: '9001',
|
||||
next_v4_action_card_id: '9101',
|
||||
next_v4_action_type: 'CONFIRM',
|
||||
next_v4_action_status: 'PENDING_CONFIRM',
|
||||
v4_open_order_task_count: 1,
|
||||
}))
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders/20001"]').text()).toContain('查看详情')
|
||||
expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').text()).toContain('继续处理')
|
||||
expect(wrapper.find('a[href="/reservation/tasks/10002"]').exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('待确认')
|
||||
})
|
||||
|
||||
it('shows review state for V4 review entrypoints in the order list', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult('GRP-001', {
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
}, {
|
||||
next_v4_order_task_id: '9002',
|
||||
next_v4_action_card_id: '9102',
|
||||
next_v4_action_type: 'REVIEW',
|
||||
next_v4_action_status: 'REVIEW_REQUIRED',
|
||||
v4_open_order_task_count: 1,
|
||||
}))
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/order-tasks/9002"]').text()).toContain('继续处理')
|
||||
expect(wrapper.text()).toContain('待复核')
|
||||
})
|
||||
|
||||
it('falls back to the legacy task detail route when there is no V4 order task entrypoint', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult())
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders/20001"]').text()).toContain('查看详情')
|
||||
expect(wrapper.find('a[href="/reservation/tasks/10002"]').text()).toContain('继续处理')
|
||||
expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows no open task state when neither V4 nor legacy entrypoint exists', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult('GRP-001', {
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
}, {
|
||||
open_task_count: 0,
|
||||
next_processable_task_id: null,
|
||||
next_v4_order_task_id: null,
|
||||
next_v4_action_card_id: null,
|
||||
next_v4_action_type: 'NONE',
|
||||
next_v4_action_status: null,
|
||||
v4_open_order_task_count: 0,
|
||||
}))
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders/20001"]').text()).toContain('查看详情')
|
||||
expect(wrapper.find('a[href^="/reservation/tasks/"]').exists()).toBe(false)
|
||||
expect(wrapper.find('a[href^="/reservation/order-tasks/"]').exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('无待处理任务')
|
||||
})
|
||||
|
||||
it('filters order list with order status and business-key fields', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult())
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ export interface ReservationOrderListFilters {
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export type ReservationV4OrderListActionType = 'CONFIRM' | 'REVIEW' | 'NONE'
|
||||
export type ReservationV4OrderListActionStatus = 'PENDING_CONFIRM' | 'REVIEW_REQUIRED'
|
||||
|
||||
export interface ReservationOrderListItem {
|
||||
order_id: string
|
||||
hotel_id: string
|
||||
@@ -69,6 +72,11 @@ export interface ReservationOrderListItem {
|
||||
display_name: string
|
||||
open_task_count: number | null
|
||||
next_processable_task_id: string | null
|
||||
next_v4_order_task_id?: string | null
|
||||
next_v4_action_card_id?: string | null
|
||||
next_v4_action_type?: ReservationV4OrderListActionType | null
|
||||
next_v4_action_status?: ReservationV4OrderListActionStatus | null
|
||||
v4_open_order_task_count?: number | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
@@ -132,14 +132,20 @@
|
||||
{{ t('orderList.viewDetail') }}
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="order.next_processable_task_id"
|
||||
v-if="resolveContinueTarget(order)"
|
||||
class="row-action"
|
||||
:to="`/reservation/tasks/${order.next_processable_task_id}`"
|
||||
:to="resolveContinueTarget(order) ?? '/'"
|
||||
>
|
||||
{{ t('orderList.continueTask') }}
|
||||
</RouterLink>
|
||||
<span
|
||||
v-else
|
||||
v-if="formatContinueActionLabel(order)"
|
||||
class="row-action-tag"
|
||||
>
|
||||
{{ formatContinueActionLabel(order) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="!resolveContinueTarget(order)"
|
||||
class="row-action row-action--muted"
|
||||
>
|
||||
{{ t('orderList.noOpenTask') }}
|
||||
@@ -302,6 +308,29 @@ function formatOrderKey(order: ReservationOrderListItem): string {
|
||||
order.order_id
|
||||
)
|
||||
}
|
||||
|
||||
function resolveContinueTarget(order: ReservationOrderListItem): string | null {
|
||||
if (order.next_v4_order_task_id) {
|
||||
return `/reservation/order-tasks/${order.next_v4_order_task_id}`
|
||||
}
|
||||
if (order.next_processable_task_id) {
|
||||
return `/reservation/tasks/${order.next_processable_task_id}`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function formatContinueActionLabel(order: ReservationOrderListItem): string {
|
||||
if (!order.next_v4_order_task_id) {
|
||||
return ''
|
||||
}
|
||||
if (order.next_v4_action_type === 'REVIEW' || order.next_v4_action_status === 'REVIEW_REQUIRED') {
|
||||
return t('status.REVIEW_REQUIRED')
|
||||
}
|
||||
if (order.next_v4_action_type === 'CONFIRM' || order.next_v4_action_status === 'PENDING_CONFIRM') {
|
||||
return t('status.PENDING_CONFIRM')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -374,6 +403,15 @@ function formatOrderKey(order: ReservationOrderListItem): string {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.row-action-tag {
|
||||
border-radius: 999px;
|
||||
background: var(--th-color-blue-100);
|
||||
color: var(--th-color-blue-600);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
min-height: 220px;
|
||||
display: grid;
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
| 接口 | 本轮变化 | 前端接入注意 |
|
||||
| --- | --- | --- |
|
||||
| `GET /api/reservation/orders` | 补齐订单列表 V4 继续处理入口字段。 | `order_status` 不传时默认查询全部订单状态;`page_num` 从 1 开始;`page_size` 后端有最大值保护;`open_task_count` 排除 `COMPLETED` 和 `FAILED`;旧 `next_processable_task_id` 继续保留。V4 新增 `next_v4_order_task_id`、`next_v4_action_card_id`、`next_v4_action_type`、`next_v4_action_status`、`v4_open_order_task_count`;前端“继续处理”如存在 `next_v4_order_task_id` 应优先跳 `/reservation/order-tasks/{next_v4_order_task_id}`,否则再回退旧 `/reservation/tasks/{next_processable_task_id}`。 |
|
||||
| `GET /api/reservation/orders` | 补齐订单列表 V4 继续处理入口字段;前端已接入。 | `order_status` 不传时默认查询全部订单状态;`page_num` 从 1 开始;`page_size` 后端有最大值保护;`open_task_count` 排除 `COMPLETED` 和 `FAILED`;旧 `next_processable_task_id` 继续保留。V4 新增 `next_v4_order_task_id`、`next_v4_action_card_id`、`next_v4_action_type`、`next_v4_action_status`、`v4_open_order_task_count`;前端“继续处理”已按优先级实现:存在 `next_v4_order_task_id` 时跳 `/reservation/order-tasks/{next_v4_order_task_id}`,否则回退旧 `/reservation/tasks/{next_processable_task_id}`;两者都没有时展示无待处理状态。 |
|
||||
| `GET /api/reservation/tasks` | 补齐来源邮件会话摘要字段,并新增 `order_status` 查询参数。 | `order_status` 按任务所属订单状态过滤,支持 `TEMPORARY`、`ACTIVE`、`ENDED`、`LOGIC_DELETED`;列表仍然只返回安全摘要,不返回正文、HTML、附件 URL 或 AI 原始 payload;点击邮件入口时使用 `source_message_id` 调会话详情。 |
|
||||
| `GET /api/reservation/orders/{orderId}` | 补齐旧 `tasks[]` 来源邮件会话摘要字段,并新增 V4 `v4_order_tasks[]` 订单任务时间线。 | `include_tasks=false` 可只取订单摘要,此时 `tasks[]` 和 `v4_order_tasks[]` 都为空;旧 `tasks[]` 顺序由后端按订单队列返回,V4 `v4_order_tasks[]` 按 `source_received_at`、`source_message_id`、`order_context_index`、`created_at`、数字 ID 正序返回;前端不要自行重排。`include_source_summary` 第一版不作为前端裁剪字段的强约束,前端暂不要依赖它减少返回字段。 |
|
||||
| `GET /api/reservation/tasks/{taskId}` | 补齐顶层来源邮件字段,并扩展 `fields[]` 元数据。 | 顶层来源字段用于打开邮件会话;`fields[]` 中的 `result_type`、`task_type`、`task_subtype`、`default_value_source` 用于前端字段分组、调试和白名单对齐。 |
|
||||
@@ -185,7 +185,7 @@ POST /api/auth/logout
|
||||
- `next_processable_task_id` 是后端按同订单队列实时计算出的下一条可处理任务;前端可以用它做“继续处理”入口。
|
||||
- V4 订单任务入口字段由后端实时派生:`v4_open_order_task_count` 统计当前订单下 `order_task_status!=COMPLETED` 的 V4 订单任务;`next_v4_order_task_id` 是同订单第一条仍需用户处理的 V4 订单任务;`next_v4_action_card_id` 是该订单任务下第一张待处理卡;`next_v4_action_type` 取 `CONFIRM` / `REVIEW` / `NONE`;`next_v4_action_status` 取 `PENDING_CONFIRM` / `REVIEW_REQUIRED` 或空。
|
||||
- V4 派生规则:Basic Information 必须优先于业务卡;Basic 已确认后,业务卡中 `REVIEW_REQUIRED` 优先于普通 `PENDING_CONFIRM`;`COMPLETED` 的 V4 订单任务不计入 open;S10/S99 来源通知不挂订单,不进入这些订单列表字段。
|
||||
- 前端订单列表“继续处理”建议优先级:有 `next_v4_order_task_id` 时跳 V4 订单任务详情;否则有旧 `next_processable_task_id` 时跳旧任务详情;`next_v4_action_type=NONE` 且旧字段为空时展示查看详情或无待处理状态。
|
||||
- 前端订单列表“继续处理”已按该优先级接入:有 `next_v4_order_task_id` 时跳 V4 订单任务详情;否则有旧 `next_processable_task_id` 时跳旧任务详情;`next_v4_action_type=NONE` 且旧字段为空时展示无待处理状态,查看详情仍固定进入订单详情。
|
||||
- `display_order_key` 是前端优先展示的订单业务号或临时订单号;`group_code` 和 `confirmation_number` 只有在当前订单业务号类型匹配时返回。
|
||||
- 订单 ID、任务 ID、SourceMessage ID 在这些前端接口中按字符串返回,前端不要转换成 JavaScript number。
|
||||
- 当前 V3 / 过渡实现中,源邮件只读通知卡背后有隐藏技术订单用于满足后端任务外键,但订单列表不会返回该订单;任务列表中该类任务的 `display_order_key`、`temporary_order_no`、`group_code`、`confirmation_number` 可能为空,前端不要因此隐藏整条任务。V4 S10/S99 目标模型已改为独立来源通知,不再挂隐藏技术订单。
|
||||
|
||||
@@ -279,7 +279,7 @@ GET /api/reservation/orders/{orderId}
|
||||
GET /api/reservation/orders
|
||||
```
|
||||
|
||||
当前状态:后端已完成第一版。默认查询全部订单状态;`open_task_count` 排除 `COMPLETED` 和 `FAILED`,`next_processable_task_id` 按旧 V2/V3 同订单队列可处理状态实时计算。M002 V4 CP14 已补齐 V4 继续处理入口字段,前端有 `next_v4_order_task_id` 时应优先进入 V4 订单任务详情。
|
||||
当前状态:后端已完成第一版。默认查询全部订单状态;`open_task_count` 排除 `COMPLETED` 和 `FAILED`,`next_processable_task_id` 按旧 V2/V3 同订单队列可处理状态实时计算。M002 V4 CP14 已补齐 V4 继续处理入口字段,前端已按 `next_v4_order_task_id` 优先进入 V4 订单任务详情,并在无 V4 入口时回退旧任务详情。
|
||||
|
||||
默认排序:按后端维护的订单最近业务活动时间倒序返回,保证最近有业务活动的订单排在前面。后端当前使用 `workflow_reservation_order.latest_activity_at` 作为排序字段,并在订单创建、任务创建、草稿保存、最终确认、人工复核解阻、任务状态变更等写路径维护;前端不要再基于任务时间或更新时间自行重排。
|
||||
|
||||
@@ -337,7 +337,7 @@ V4 继续处理字段说明:
|
||||
| `next_v4_action_status` | `PENDING_CONFIRM` / `REVIEW_REQUIRED`;`NONE` 时为空。 |
|
||||
| `v4_open_order_task_count` | 当前订单下未完成 V4 订单任务数,`COMPLETED` 不计入。 |
|
||||
|
||||
前端“继续处理”入口建议优先级:优先使用 `next_v4_order_task_id` 跳转 V4 订单任务详情;没有 V4 待处理时再使用旧 `next_processable_task_id` 跳旧任务详情。S10/S99 来源通知不创建订单,不进入订单列表字段统计。
|
||||
前端“继续处理”入口优先级:优先使用 `next_v4_order_task_id` 跳转 V4 订单任务详情;没有 V4 待处理时再使用旧 `next_processable_task_id` 跳旧任务详情;两者都没有时展示无待处理状态。S10/S99 来源通知不创建订单,不进入订单列表字段统计。
|
||||
|
||||
## 6. 前端联调演示数据 seed 接口
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# M002 V4 CP14 Order List Entrypoint Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 接入 `GET /api/reservation/orders` 的 CP14 V4 下一步字段,让订单列表“继续处理”优先进入 V4 订单任务详情,并保留旧 V2/V3 任务详情回退。
|
||||
|
||||
**Architecture:** 类型层扩展订单列表返回字段,页面层新增一个纯展示/路由解析函数,按 `next_v4_order_task_id`、`next_processable_task_id`、`next_v4_action_type` 的优先级输出继续处理链接和标签。服务层继续透传后端 JSON,不引入前端伪造逻辑;S10/S99 仍不进入订单列表。
|
||||
|
||||
**Tech Stack:** Vue 3, TypeScript, Vue Router, Vue I18n, Vitest, Vue Test Utils.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- 不改后端接口。
|
||||
- 不改 V4 卡片确认 / 复核提交逻辑。
|
||||
- 不做 S10/S99 订单列表展示。
|
||||
- 不废弃旧 V2/V3 任务详情页面。
|
||||
- 不做真实 OPERA / OHIP。
|
||||
- 提交时不带入 `.idea`、`client/dist.zip` 或其他无关工作区改动。
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Failing Tests For Entrypoint Priority
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/src/tests/reservationViews.spec.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `ReservationOrderListItem.next_v4_order_task_id`, `next_processable_task_id`, `next_v4_action_type`, `next_v4_action_status`
|
||||
- Produces: 测试覆盖 V4 优先、旧任务回退、无待处理、查看详情不变
|
||||
|
||||
- [x] **Step 1: Add test cases**
|
||||
|
||||
Add order list fixtures with:
|
||||
- V4 `next_v4_order_task_id='9001'` and legacy `next_processable_task_id='10002'`
|
||||
- legacy-only `next_processable_task_id='10002'`
|
||||
- no next task and `next_v4_action_type='NONE'`
|
||||
|
||||
- [x] **Step 2: Verify red**
|
||||
|
||||
Run `CI=true pnpm --dir client test -- reservationViews.spec.ts`.
|
||||
Expected: V4 priority assertion fails because the current component still links “继续处理” to `/reservation/tasks/10002`.
|
||||
|
||||
### Task 2: Types And UI Logic
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/src/types/reservation.ts`
|
||||
- Modify: `client/src/views/reservation/ReservationOrderListView.vue`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `ReservationV4OrderListActionType`, `ReservationV4OrderListActionStatus` unions and `resolveContinueTarget(order)` page helper
|
||||
|
||||
- [x] **Step 1: Extend types**
|
||||
|
||||
Add optional nullable CP14 fields to `ReservationOrderListItem`.
|
||||
|
||||
- [x] **Step 2: Implement route priority**
|
||||
|
||||
`resolveContinueTarget(order)` returns `/reservation/order-tasks/{id}` when V4 ID exists, otherwise `/reservation/tasks/{id}` when legacy ID exists, otherwise `null`.
|
||||
|
||||
- [x] **Step 3: Add label**
|
||||
|
||||
Show a compact V4 action tag for `CONFIRM` / `REVIEW`, reusing status text for `PENDING_CONFIRM` / `REVIEW_REQUIRED`.
|
||||
|
||||
- [x] **Step 4: Verify green**
|
||||
|
||||
Run `CI=true pnpm --dir client test -- reservationViews.spec.ts`.
|
||||
|
||||
### Task 3: Documentation And Final Verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/project/frontend-backend/backend-to-frontend-notes.md`
|
||||
- Modify: `docs/project/frontend-backend/frontend-to-backend-api-requests.md`
|
||||
- Modify: `PROJECT_STATE.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: 前端已接入 CP14 的说明
|
||||
|
||||
- [x] **Step 1: Update docs**
|
||||
|
||||
Record that the frontend order list now consumes CP14 V4 entry fields and keeps legacy fallback.
|
||||
|
||||
- [x] **Step 2: Run verification**
|
||||
|
||||
Run:
|
||||
- `CI=true pnpm --dir client lint`
|
||||
- `CI=true pnpm --dir client typecheck`
|
||||
- `CI=true pnpm --dir client test`
|
||||
- `CI=true pnpm --dir client build`
|
||||
|
||||
- [x] **Step 3: Code review**
|
||||
|
||||
Review routing, types, docs, tests, and unrelated workspace files.
|
||||
|
||||
- [x] **Step 4: Commit**
|
||||
|
||||
Stage only related files and commit with a Chinese message.
|
||||
Reference in New Issue
Block a user