补齐人工复核转换和列表筛选

This commit is contained in:
andy committed 2026-07-09 18:38:29 +08:00
1 parent e12bfd77e1
commit 1282d4aede
12 files changed
+983 -15

No files matched your search

@@ -6,6 +6,9 @@ import { createMemoryHistory, createRouter } from 'vue-router'
import zhCN from '@/i18n/locales/zh-CN'
import ReservationTaskDetailPanel from '@/components/reservation/ReservationTaskDetailPanel.vue'
import type {
ReservationManualReviewConversionResult,
ReservationOrderListItem,
ReservationOrderListResult,
ReservationTaskAuditLogResult,
ReservationTaskDetailResult,
ReservationTaskFieldResult,
@@ -18,6 +21,8 @@ vi.mock('@/services/reservationService', async (importOriginal) => {
...actual,
fetchReservationTaskDetail: vi.fn(),
fetchReservationTaskAudits: vi.fn(),
fetchReservationOrders: vi.fn(),
convertReservationManualReviewTask: vi.fn(),
saveReservationTaskDraft: vi.fn(),
confirmReservationTask: vi.fn(),
}
@@ -70,6 +75,50 @@ function createMutationResult(taskStatus = 'PENDING_CONFIRM'): ReservationTaskPa
}
}
function createManualReviewConversionResult(
overrides: Partial<ReservationManualReviewConversionResult> = {},
): ReservationManualReviewConversionResult {
return {
task_id: '10001',
order_id: '20001',
original_order_id: '20001',
system_task_type: 'NEW_BOOKING',
task_card_type: 'NEW_BOOKING',
task_status: 'PENDING_CONFIRM',
original_order_logic_deleted: false,
...overrides,
}
}
function createOrderCandidate(overrides: Partial<ReservationOrderListItem> = {}): ReservationOrderListItem {
return {
order_id: '20002',
hotel_id: 'HOTEL-TEST',
order_status: 'ACTIVE',
display_order_key: 'GRP-001',
temporary_order_no: null,
confirmation_number: 'CNF-001',
group_code: 'GRP-001',
display_name: 'GRP-001',
open_task_count: 1,
next_processable_task_id: null,
created_at: '2026-07-08T03:00:00Z',
updated_at: '2026-07-08T03:10:00Z',
...overrides,
}
}
function createOrderCandidateResult(items: ReservationOrderListItem[]): ReservationOrderListResult {
return {
items,
page: {
page_num: 1,
page_size: 8,
total: items.length,
},
}
}
function createAudit(auditId: string, action: string): ReservationTaskAuditLogResult {
return {
audit_id: auditId,
@@ -162,6 +211,8 @@ describe('ReservationTaskDetailPanel', () => {
beforeEach(() => {
vi.mocked(service.fetchReservationTaskDetail).mockReset()
vi.mocked(service.fetchReservationTaskAudits).mockReset()
vi.mocked(service.fetchReservationOrders).mockReset()
vi.mocked(service.convertReservationManualReviewTask).mockReset()
vi.mocked(service.saveReservationTaskDraft).mockReset()
vi.mocked(service.confirmReservationTask).mockReset()
vi.mocked(service.fetchReservationTaskDetail).mockResolvedValue(createTaskDetail())
@@ -169,6 +220,8 @@ describe('ReservationTaskDetailPanel', () => {
task_id: '10001',
items: [],
})
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderCandidateResult([]))
vi.mocked(service.convertReservationManualReviewTask).mockResolvedValue(createManualReviewConversionResult())
})
it('shows draft save errors instead of leaving an unhandled rejection', async () => {
@@ -335,4 +388,115 @@ describe('ReservationTaskDetailPanel', () => {
expect(wrapper.text()).toContain('查看邮件会话')
expect(wrapper.text()).not.toContain('接口待补')
})
it('converts a manual review task to a new booking without a target order', async () => {
vi.mocked(service.fetchReservationTaskDetail)
.mockResolvedValueOnce(
createTaskDetail({
system_task_type: 'MANUAL_REVIEW',
task_card_type: 'MANUAL_REVIEW',
}),
)
.mockResolvedValueOnce(
createTaskDetail({
system_task_type: 'NEW_BOOKING',
task_card_type: 'NEW_BOOKING',
}),
)
vi.mocked(service.fetchReservationTaskAudits)
.mockResolvedValueOnce({
task_id: '10001',
items: [createAudit('audit-1', 'TASK_LOADED')],
})
.mockResolvedValueOnce({
task_id: '10001',
items: [createAudit('audit-2', 'MANUAL_REVIEW_CONVERT')],
})
const wrapper = await mountPanel()
expect(wrapper.text()).toContain('人工转换')
await wrapper.find('select[name="manual_target_task_type"]').setValue('NEW_BOOKING')
await wrapper.find('textarea[name="manual_conversion_reason"]').setValue('识别为新预订')
await findButton(wrapper, '转换任务').trigger('click')
await flushPromises()
expect(service.convertReservationManualReviewTask).toHaveBeenCalledWith('10001', {
target_task_type: 'NEW_BOOKING',
reason: '识别为新预订',
})
expect(service.fetchReservationTaskDetail).toHaveBeenCalledTimes(2)
expect(service.fetchReservationTaskAudits).toHaveBeenCalledTimes(2)
expect(wrapper.text()).toContain('人工转换已完成')
expect(wrapper.text()).toContain('人工转换')
expect(wrapper.text()).not.toContain('MANUAL_REVIEW_CONVERT')
})
it('requires a non-deleted target order when converting manual review to update booking', async () => {
vi.mocked(service.fetchReservationTaskDetail).mockResolvedValue(
createTaskDetail({
system_task_type: 'MANUAL_REVIEW',
task_card_type: 'MANUAL_REVIEW',
}),
)
vi.mocked(service.fetchReservationOrders).mockResolvedValue(
createOrderCandidateResult([
createOrderCandidate({
order_id: '20002',
display_order_key: 'GRP-ACTIVE',
group_code: 'GRP-ACTIVE',
confirmation_number: 'CNF-ACTIVE',
order_status: 'ACTIVE',
}),
createOrderCandidate({
order_id: '20003',
display_order_key: 'GRP-DELETED',
group_code: 'GRP-DELETED',
confirmation_number: 'CNF-DELETED',
order_status: 'LOGIC_DELETED',
}),
]),
)
vi.mocked(service.convertReservationManualReviewTask).mockResolvedValue(
createManualReviewConversionResult({
order_id: '20002',
original_order_id: '20001',
system_task_type: 'UPDATE_BOOKING',
task_card_type: 'UPDATE_BOOKING',
}),
)
const wrapper = await mountPanel()
await wrapper.find('select[name="manual_target_task_type"]').setValue('UPDATE_BOOKING')
await findButton(wrapper, '转换任务').trigger('click')
await flushPromises()
expect(service.convertReservationManualReviewTask).not.toHaveBeenCalled()
expect(wrapper.text()).toContain('请选择目标订单')
await wrapper.find('input[name="manual_target_order_keyword"]').setValue('GRP')
await findButton(wrapper, '搜索订单').trigger('click')
await flushPromises()
expect(service.fetchReservationOrders).toHaveBeenCalledWith(
expect.objectContaining({
keyword: 'GRP',
page_num: 1,
page_size: 8,
}),
)
expect(wrapper.text()).toContain('GRP-ACTIVE')
expect(wrapper.text()).toContain('GRP-DELETED')
expect(wrapper.find('input[value="20003"]').attributes('disabled')).toBeDefined()
await wrapper.find('input[value="20002"]').setValue()
await findButton(wrapper, '转换任务').trigger('click')
await flushPromises()
expect(service.convertReservationManualReviewTask).toHaveBeenCalledWith('10001', {
target_task_type: 'UPDATE_BOOKING',
target_order_id: '20002',
})
})
})