对齐 Parent Group 前端展示与复核

This commit is contained in:
andy
2026-07-12 13:35:01 +08:00
parent 92489af18e
commit bf796946f9
13 changed files with 575 additions and 13 deletions

View File

@@ -587,6 +587,168 @@ describe('ReservationTaskDetailPanel', () => {
expect(wrapper.find('button[title="重试"]').exists()).toBe(false)
})
it('renders R08 parent group manual review with identity candidates on the same task card', async () => {
vi.mocked(service.fetchReservationTaskDetail)
.mockResolvedValueOnce(
createTaskDetail({
result_type: 'manual_review',
ai_task_type: 'Cancel Allotment',
task_subtype: 'cancel_allotment_control_block',
route_code: 'R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_REVIEW',
system_process_category: 'BUSINESS_TASK',
system_task_type: 'CANCEL_BOOKING',
task_card_type: 'CANCEL_ALLOTMENT',
task_status: 'BLOCKED',
review_status: 'PENDING',
manual_review: {
reason_code: 'target_object_unclear',
visible_reason: 'Parent Group 的 group_code 与 block_code 原始候选冲突,请人工确认目标。',
missing_fields: ['/case_keys/group_code', '/case_keys/block_code'],
blocking_points: ['Parent Group identity cannot be safely normalized.'],
conflicting_points: ['PARENT-2608-CONFLICT-A', 'PARENT-2608-CONFLICT-B'],
suggested_human_actions: ['confirm_parent_group_identity'],
evidence_to_check: ['parent_identity_candidates'],
},
context_used: {
parent_identity_candidates: [
{
field: 'group_code',
value: 'PARENT-2608-CONFLICT-A',
evidence_source: 'subject',
},
{
field: 'block_code',
value: 'PARENT-2608-CONFLICT-B',
evidence_source: 'attachment',
normalized_value: 'PARENT-2608-CONFLICT-B',
},
{
candidate_field: 'parent_group_code',
raw_value: 'PARENT-2608-CONFLICT-C',
origin: 'email_body',
},
],
},
availability: {
blocked: true,
read_only: true,
editable: false,
confirmable: false,
executable: false,
blocked_by_task_id: null,
blocked_reason: 'MANUAL_REVIEW_REQUIRED',
},
fields: [
createRequiredField({
field_path: 'case_keys.group_code',
field_pointer: '/case_keys/group_code',
display_name: 'Parent Group 团队号',
value: null,
}),
createRequiredField({
field_path: 'case_keys.block_code',
field_pointer: '/case_keys/block_code',
display_name: 'Parent Group Block Code',
value: null,
}),
],
}),
)
.mockResolvedValueOnce(
createTaskDetail({
result_type: 'manual_review',
ai_task_type: 'Cancel Allotment',
task_subtype: 'cancel_allotment_control_block',
route_code: 'R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_REVIEW',
system_process_category: 'BUSINESS_TASK',
system_task_type: 'CANCEL_BOOKING',
task_card_type: 'CANCEL_ALLOTMENT',
task_status: 'READY',
review_status: 'RESOLVED',
review_resolution: {
confirmed_order_id: '20001',
field_overrides: [
{
field_pointer: '/case_keys/group_code',
field_path: 'case_keys.group_code',
value: 'PARENT-2608-CONFLICT-A',
},
{
field_pointer: '/case_keys/block_code',
field_path: 'case_keys.block_code',
value: 'PARENT-2608-CONFLICT-A',
},
],
},
availability: {
blocked: false,
read_only: false,
editable: false,
confirmable: false,
executable: true,
blocked_by_task_id: null,
blocked_reason: null,
},
opera_operations: [createOperaOperation()],
}),
)
vi.mocked(service.fetchReservationTaskAudits)
.mockResolvedValueOnce({
task_id: '10001',
items: [createAudit('audit-1', 'TASK_LOADED')],
})
.mockResolvedValueOnce({
task_id: '10001',
items: [createAudit('audit-2', 'MANUAL_REVIEW_RESOLVED')],
})
const wrapper = await mountPanel()
expect(wrapper.text()).toContain('Parent Group')
expect(wrapper.text()).toContain('取消 Parent Group 配额')
expect(wrapper.text()).toContain('取消 Parent Group 整块配额')
expect(wrapper.text()).toContain('R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_REVIEW')
expect(wrapper.text()).toContain('人工复核证据')
expect(wrapper.text()).toContain('target_object_unclear')
expect(wrapper.text()).toContain('Parent Group 的 group_code 与 block_code 原始候选冲突')
expect(wrapper.text()).toContain('PARENT-2608-CONFLICT-A')
expect(wrapper.text()).toContain('PARENT-2608-CONFLICT-B')
expect(wrapper.text()).toContain('parent_group_code')
expect(wrapper.text()).toContain('PARENT-2608-CONFLICT-C')
expect(wrapper.text()).toContain('subject')
expect(wrapper.text()).toContain('attachment')
expect(wrapper.text()).toContain('email_body')
expect(wrapper.text()).toContain('normalized_value')
expect(wrapper.text()).not.toContain('适配契约异常')
expect(wrapper.text()).not.toContain('人工转换')
expect(wrapper.text()).not.toContain('保存草稿')
expect(wrapper.text()).not.toContain('确认任务')
await wrapper.find('input[name="manual_resolution_field_override_0"]').setValue('PARENT-2608-CONFLICT-A')
await wrapper.find('input[name="manual_resolution_field_override_1"]').setValue('PARENT-2608-CONFLICT-A')
await wrapper.find('textarea[name="manual_resolution_reason"]').setValue('确认 Parent Group identity')
await findButton(wrapper, '提交复核结果').trigger('click')
await flushPromises()
expect(service.resolveReservationManualReviewTask).toHaveBeenCalledWith('10001', {
confirmed_order_id: '20001',
reason: '确认 Parent Group identity',
field_overrides: [
{
field_pointer: '/case_keys/group_code',
field_path: 'case_keys.group_code',
value: 'PARENT-2608-CONFLICT-A',
},
{
field_pointer: '/case_keys/block_code',
field_path: 'case_keys.block_code',
value: 'PARENT-2608-CONFLICT-A',
},
],
})
expect(wrapper.text()).toContain('人工复核已解阻')
})
it('resolves type-known manual review on the same task card', async () => {
vi.mocked(service.fetchReservationTaskDetail)
.mockResolvedValueOnce(

View File

@@ -132,6 +132,21 @@ function createSourceMessageOnlyTaskListResult(taskSubtype: 'S10' | 'S99' = 'S10
}
}
function createParentGroupCancelAllotmentTaskListResult(): ReservationTaskListResult {
const result = createTaskListResult('PARENT-2608-BLOCK', 'Parent split confirmation')
result.items[0] = {
...result.items[0]!,
task_type: 'CANCEL_BOOKING',
task_subtype: 'cancel_allotment_control_block',
result_type: 'normal_task',
ai_task_type: 'Cancel Allotment',
route_code: 'R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_NORMAL',
system_process_category: 'BUSINESS_TASK',
card_name: 'CANCEL_ALLOTMENT',
}
return result
}
function createOrderDetailResult(displayName = 'GRP-001') {
return {
order: {
@@ -516,6 +531,7 @@ describe('reservation P0 views', () => {
])
expect(taskSubtypeOptions).not.toContain('S000')
expect(taskSubtypeOptions).not.toContain('S999')
expect(taskSubtypeOptions).not.toContain('linked_parent_release_after_child_split')
expect(wrapper.find('select[name="task_type"]').text()).toContain('来源消息只读')
expect(wrapper.find('select[name="task_subtype"]').text()).toContain('纯信息类邮件S10')
expect(wrapper.find('select[name="task_subtype"]').text()).toContain('无法形成业务素材包S99')
@@ -550,6 +566,21 @@ describe('reservation P0 views', () => {
expect(lastTaskRequest).not.toHaveProperty('system_process_category')
})
it('renders R08 parent group cancel allotment as a business card', async () => {
vi.mocked(service.fetchReservationTaskList).mockResolvedValue(createParentGroupCancelAllotmentTaskListResult())
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
expect(wrapper.text()).toContain('PARENT-2608-BLOCK')
expect(wrapper.text()).toContain('Parent Group')
expect(wrapper.text()).toContain('取消 Parent Group 配额')
expect(wrapper.text()).toContain('取消 Parent Group 整块配额')
expect(wrapper.text()).toContain('查看订单')
expect(wrapper.text()).not.toContain('适配契约异常')
expect(wrapper.text()).not.toContain('linked_parent_release_after_child_split')
})
it('renders source-message-only task rows without a visible order action', async () => {
vi.mocked(service.fetchReservationTaskList).mockResolvedValue(createSourceMessageOnlyTaskListResult('S10'))