feat(reservation): solidify booking email v0.1 intake

This commit is contained in:
鲨鱼辣椒 committed 2026-08-08 16:02:09 +08:00
1 parent 9f29b59c26
commit d91cd717df
61 files changed
+13961 -377

No files matched your search

+951
View File
@@ -0,0 +1,951 @@
export type PrototypeTaskKind =
| 'new'
| 'update'
| 'cancel'
| 'trace'
| 'rooming-list'
| 'payment'
| 'risk'
| 'general'
export type PrototypeTaskCategory = 'booking' | 'service' | 'notification'
export type PrototypeFieldControl =
| 'text'
| 'textarea'
| 'select'
| 'combobox'
| 'date'
| 'number'
| 'derived'
export type PrototypeFieldOption =
| string
| {
value: string
label?: string
group?: string
}
export interface PrototypeField {
key?: string
label: string
value: string
note?: string
editable?: boolean
required?: boolean
control?: PrototypeFieldControl
options?: PrototypeFieldOption[]
placeholder?: string
min?: number
}
export interface PrototypeChange {
label: string
before: string
after: string
note?: string
}
export interface PrototypeRoomLine {
key?: string
roomType: string
quantity: number
occupancy: string
meal?: string
}
export interface PrototypeRequirement {
key?: string
title: string
detail: string
editable?: boolean
}
export interface PrototypeAttachment {
name: string
mediaType: string
size: string
purpose: string
}
export interface PrototypeBusinessCard {
eyebrow: string
title: string
description: string
fields?: PrototypeField[]
changes?: PrototypeChange[]
requirements?: PrototypeRequirement[]
department?: 'FO' | 'HSK' | 'FO+HSK'
departmentOptions?: Array<'FO' | 'HSK' | 'FO+HSK'>
attachment?: PrototypeAttachment
boundary?: string
noticeReason?: string
suggestedAction?: string
}
export interface PrototypeRoomInformation {
modeLabel: string
summary: string
currentTitle?: string
current?: PrototypeField[]
currentRooms?: PrototypeRoomLine[]
targetTitle: string
target: PrototypeField[]
targetRooms: PrototypeRoomLine[]
targetRoomsEditable?: boolean
changes?: PrototypeChange[]
}
export interface PrototypeSourceMessage {
channel: string
senderName: string
senderEmail: string
recipients: string
receivedAt: string
subject: string
body: string[]
attachments: PrototypeAttachment[]
quotedHistory?: string[]
}
export interface PrototypeTask {
key: string
code: string
kind: PrototypeTaskKind
category: PrototypeTaskCategory
icon: string
typeLabel: string
title: string
summary: string
status: string
priority: string
receivedAt: string
sectionLabels: string[]
basicInformation?: PrototypeField[]
businessCard: PrototypeBusinessCard
roomInformation?: PrototypeRoomInformation
sourceMessage: PrototypeSourceMessage
evidence: PrototypeField[]
}
const safeSender = 'reservations@agency-example.test'
const safeRecipient = 'hotel-ops@example.test'
const accountOptions = [
'Siam Atlas Travel',
'Orchid Routes Co.',
'Lotus MICE Partners',
'Direct Guest',
]
const marketOptions = ['Leisure Group', 'FIT Wholesale', 'MICE Group', 'Corporate FIT']
const sourceOptions = ['Email', 'Direct', 'GDS']
export interface PrototypeRateCodeGroup {
label: string
codes: string[]
}
export const prototypeRateCodeGroups: PrototypeRateCodeGroup[] = [
{ label: 'Q.B.D', codes: ['GRPA1', 'GRPA2', 'GRPA3', 'GRPA4'] },
{ label: 'LIAN TAI', codes: ['WHO1', 'GRP1', 'WHO2', 'WHO3'] },
{ label: 'LIANTAI ONLINE/DY', codes: ['LTLT', 'LBLT'] },
{ label: 'RAINBOW/AI', codes: ['LBMS', 'LBSM'] },
{ label: 'FENGRUN', codes: ['GLSPCB', 'LBW1'] },
{ label: 'HONGTAI', codes: ['GL2000LT', 'LBKB'] },
{ label: 'HONGYUN', codes: ['GL2100CN', 'LBLS'] },
{ label: 'HANA TOUR', codes: ['WHKR2100B', 'GL2100B', 'GL2200KR'] },
]
export const prototypeRateCodeOptions: PrototypeFieldOption[] = prototypeRateCodeGroups.flatMap(
({ label, codes }) => codes.map((value) => ({ value, label: `${label} · ${value}`, group: label })),
)
export const prototypeRoomTypeOptions = [
'RM2',
'RM3',
'RM4',
'UG1',
'UG2',
'UG3',
'SU1',
'SU2',
'SU3',
'SU6',
]
export const prototypeOccupancyOptions = ['1 人', '2 人', '3 人']
export const prototypeMealOptions = ['未提供', '含早餐', '不含早餐']
function createBasicInformation(
account: string,
market: string,
source: string,
accountNote: string,
): PrototypeField[] {
return [
{
key: 'account',
label: 'Account',
value: account,
note: accountNote,
editable: true,
required: true,
control: 'select',
options: accountOptions,
},
{
key: 'market',
label: 'Market',
value: market,
editable: true,
required: true,
control: 'select',
options: marketOptions,
},
{
key: 'source',
label: 'Source',
value: source,
editable: true,
required: true,
control: 'select',
options: sourceOptions,
},
]
}
export const prototypeTasks: PrototypeTask[] = [
{
key: 'new-booking',
code: 'LOCAL-NEW-001',
kind: 'new',
category: 'booking',
icon: 'pi pi-plus-circle',
typeLabel: 'New Booking',
title: 'Bangkok Study Tour 新预订',
summary: '18 间房,连续 3 晚,Rate Code 待人工确认',
status: '待人工确认',
priority: '今天处理',
receivedAt: '09:24',
sectionLabels: ['Basic', 'New Booking', 'Room', '邮件原文'],
basicInformation: createBasicInformation(
'Siam Atlas Travel',
'Leisure Group',
'Email',
'由当前发件人映射,可从目录中人工修正',
),
businessCard: {
eyebrow: 'New Booking',
title: '创建一笔团队订单',
description: '本卡只表达邮件要求创建的订单,不混入付款、Trace 或房表动作。',
boundary: 'Rate Code 是订单级字段。当前映射不唯一,需要在 Room Information 中人工选择。',
},
roomInformation: {
modeLabel: '创建目标',
summary: '邮件中的房型、日期和数量被整理为一笔待创建订单。',
targetTitle: '要创建的订单',
target: [
{
key: 'bookingType',
label: 'Group / FIT',
value: 'Group',
control: 'derived',
note: '由总房量自动判定:少于 5 间为 FIT,5 间及以上为 Group',
},
{
key: 'groupCode',
label: 'Group Code',
value: 'BST-0821',
editable: true,
required: true,
control: 'text',
},
{
key: 'groupName',
label: 'Group Name / Block Name',
value: 'Bangkok Study Tour',
editable: true,
required: true,
control: 'text',
},
{
key: 'names',
label: 'Name(s)',
value: 'BKK-STUDY-21A',
note: 'current 邮件以 Tour Code 作为可确认 Name 值;多个 Name 可换行填写',
editable: true,
required: true,
control: 'textarea',
},
{
key: 'arrival',
label: 'Arrival',
value: '2026-08-21',
editable: true,
required: true,
control: 'date',
},
{
key: 'departure',
label: 'Departure',
value: '2026-08-24',
editable: true,
required: true,
control: 'date',
},
{
key: 'nights',
label: 'Nights',
value: '3',
control: 'derived',
note: '由入住和离店日期计算,不直接录入',
},
{
key: 'rateCode',
label: 'Rate Code',
value: '',
editable: true,
required: true,
control: 'combobox',
options: prototypeRateCodeOptions,
placeholder: '选择候选或手动输入',
note: '订单级字段;映射不唯一时由人工补齐',
},
],
targetRooms: [
{ key: 'room-1', roomType: 'RM2', quantity: 14, occupancy: '2 人', meal: '含早餐' },
{ key: 'room-2', roomType: 'RM3', quantity: 4, occupancy: '2 人', meal: '含早餐' },
],
targetRoomsEditable: true,
},
sourceMessage: {
channel: '旅行社邮件',
senderName: 'Siam Atlas Travel',
senderEmail: safeSender,
recipients: safeRecipient,
receivedAt: '2026-08-08 09:24',
subject: 'New booking request: Bangkok Study Tour, 21 Aug',
body: [
'Dear Reservations,',
'Please create a new group booking for Bangkok Study Tour from 21 August to 24 August 2026.',
'We need 14 Deluxe Twin rooms and 4 Deluxe King rooms, including breakfast. Group code is BST-0821.',
'Please use Tour Code BKK-STUDY-21A as the booking Name. The final guest list will follow separately.',
'Please advise the available rate code for our account.',
'Best regards, Siam Atlas Travel',
],
attachments: [],
quotedHistory: ['Previous discussion only confirmed the tentative travel window. It is not a current booking instruction.'],
},
evidence: [
{ label: '识别结论', value: 'New Booking' },
{ label: '定位依据', value: '正文包含明确 create new group booking 指令' },
{ label: '分组依据', value: '同一 Group Code BST-0821' },
{ label: '待核对', value: 'Rate Code 映射不唯一' },
],
},
{
key: 'update-booking',
code: 'LOCAL-UPD-002',
kind: 'update',
category: 'booking',
icon: 'pi pi-pencil',
typeLabel: 'Update Booking',
title: 'River Market FIT 修改离店日期',
summary: '离店延后 1 晚,房型与间数保持不变',
status: '待核对变更',
priority: '今天处理',
receivedAt: '09:10',
sectionLabels: ['Basic', 'Update', 'Room 对比', '邮件原文'],
basicInformation: createBasicInformation(
'Siam Atlas Travel',
'FIT Wholesale',
'Email',
'继承自已定位订单,可从目录中人工修正',
),
businessCard: {
eyebrow: 'Update Booking',
title: '更新已存在的订单',
description: '页面同时保留当前订单与邮件目标,人工确认后才形成最终值。',
changes: [
{ label: 'Departure', before: '2026-08-12', after: '2026-08-13' },
{ label: 'Nights', before: '2', after: '3', note: '随离店日期重新计算' },
],
fields: [
{
key: 'orderReference',
label: 'Order Reference',
value: 'FIT-240817',
editable: true,
required: true,
control: 'text',
note: '如识别错订单,可在确认前人工修正定位值',
},
{ label: 'Unchanged', value: 'UG1,1 间,2 人' },
],
},
roomInformation: {
modeLabel: '变更对比',
summary: '仅日期变化。房型、数量和 Rate Code 均保持当前订单值。',
currentTitle: '当前订单',
current: [
{ key: 'bookingType', label: 'Group / FIT', value: 'FIT' },
{ key: 'names', label: 'Name(s)', value: 'RIVER-MKT-240817' },
{ label: 'Arrival', value: '2026-08-10' },
{ label: 'Departure', value: '2026-08-12' },
{ label: 'Nights', value: '2' },
{ label: 'Rate Code', value: 'WHO1' },
],
currentRooms: [{ roomType: 'UG1', quantity: 1, occupancy: '2 人', meal: '含早餐' }],
targetTitle: '邮件目标',
target: [
{
key: 'bookingType',
label: 'Group / FIT',
value: 'FIT',
control: 'derived',
note: '由目标总房量计算',
},
{
key: 'names',
label: 'Name(s)',
value: 'RIVER-MKT-240817',
editable: true,
required: true,
control: 'textarea',
},
{
key: 'arrival',
label: 'Arrival',
value: '2026-08-10',
editable: true,
required: true,
control: 'date',
},
{
key: 'departure',
label: 'Departure',
value: '2026-08-13',
editable: true,
required: true,
control: 'date',
},
{
key: 'nights',
label: 'Nights',
value: '3',
control: 'derived',
note: '随目标日期自动计算',
},
{
key: 'rateCode',
label: 'Rate Code',
value: 'WHO1',
editable: true,
required: true,
control: 'combobox',
options: prototypeRateCodeOptions,
note: '沿用现有订单;需要变更时由人工修改,不自动重算',
},
],
targetRooms: [{ key: 'room-1', roomType: 'UG1', quantity: 1, occupancy: '2 人', meal: '含早餐' }],
targetRoomsEditable: true,
changes: [
{ label: 'Departure', before: '2026-08-12', after: '2026-08-13' },
{ label: 'Nights', before: '2', after: '3' },
],
},
sourceMessage: {
channel: '旅行社邮件',
senderName: 'Siam Atlas Travel',
senderEmail: safeSender,
recipients: safeRecipient,
receivedAt: '2026-08-08 09:10',
subject: 'Extension request for FIT-240817',
body: [
'Dear Team,',
'Please extend booking FIT-240817 by one night. The new departure date should be 13 August 2026.',
'Room type, guest count and meal plan remain unchanged.',
'Thank you.',
],
attachments: [],
quotedHistory: ['Original confirmation: arrival 10 August, departure 12 August, one Deluxe King room.'],
},
evidence: [
{ label: '识别结论', value: 'Update Booking' },
{ label: '订单定位', value: 'Order Reference FIT-240817' },
{ label: '变更字段', value: 'Departure,Nights' },
{ label: '保持字段', value: 'Room Type,Quantity,Rate Code' },
],
},
{
key: 'cancel-booking',
code: 'LOCAL-CAN-003',
kind: 'cancel',
category: 'booking',
icon: 'pi pi-times-circle',
typeLabel: 'Cancel Booking',
title: 'Orchid Team 取消整笔订单',
summary: '明确取消 Group Code ORC-0902 对应的完整订单',
status: '待确认取消',
priority: '高影响',
receivedAt: '08:58',
sectionLabels: ['Basic', 'Cancel', 'Room 快照', '邮件原文'],
basicInformation: createBasicInformation(
'Orchid Routes Co.',
'Leisure Group',
'Email',
'继承自已定位订单,可从目录中人工修正',
),
businessCard: {
eyebrow: 'Cancel Booking',
title: '取消一整笔订单',
description: 'Cancel 只表达取消目标,不把减少房量误判为整单取消。',
fields: [
{
key: 'orderReference',
label: 'Order Reference',
value: 'GRP-91308',
editable: true,
required: true,
control: 'text',
},
{
key: 'cancellationScope',
label: 'Cancellation Scope',
value: 'Whole order',
editable: true,
required: true,
control: 'select',
options: ['Whole order'],
},
{ label: 'Requested By', value: '当前邮件发件人' },
],
boundary: '这是高影响操作。原型仅展示待确认任务,不会执行 PMS 取消。',
},
roomInformation: {
modeLabel: '取消目标快照',
summary: '确认前展示将被取消的完整订单,避免只凭主题执行。',
targetTitle: '将被取消的订单',
target: [
{ key: 'bookingType', label: 'Group / FIT', value: 'Group', control: 'derived' },
{ key: 'groupCode', label: 'Group Code', value: 'ORC-0902' },
{ key: 'groupName', label: 'Group Name / Block Name', value: 'Orchid Team' },
{ key: 'names', label: 'Name(s)', value: 'ORC-0902-TOUR' },
{ key: 'arrival', label: 'Arrival', value: '2026-09-02' },
{ key: 'departure', label: 'Departure', value: '2026-09-05' },
{ key: 'nights', label: 'Nights', value: '3', control: 'derived' },
{
key: 'rateCode',
label: 'Rate Code',
value: 'GLSPCB',
editable: true,
required: true,
control: 'combobox',
options: prototypeRateCodeOptions,
note: '继承值允许人工修正,但不会自动重算',
},
],
targetRooms: [
{ key: 'room-1', roomType: 'RM2', quantity: 10, occupancy: '2 人', meal: '含早餐' },
{ key: 'room-2', roomType: 'SU1', quantity: 2, occupancy: '2 人', meal: '含早餐' },
],
},
sourceMessage: {
channel: '旅行社邮件',
senderName: 'Orchid Routes Co.',
senderEmail: 'groups@orchid-example.test',
recipients: safeRecipient,
receivedAt: '2026-08-08 08:58',
subject: 'Cancel complete booking ORC-0902',
body: [
'Dear Reservations,',
'Please cancel the complete group booking under group code ORC-0902 and order reference GRP-91308.',
'This request applies to all rooms and all dates in the booking.',
'Please send cancellation confirmation after manual processing.',
],
attachments: [],
},
evidence: [
{ label: '识别结论', value: 'Cancel Booking' },
{ label: '明确措辞', value: 'cancel the complete group booking' },
{ label: '订单定位', value: 'Group Code 与 Order Reference 双重命中' },
{ label: '执行边界', value: '只建卡,不自动取消 PMS 订单' },
],
},
{
key: 'trace-request',
code: 'LOCAL-TRC-004',
kind: 'trace',
category: 'service',
icon: 'pi pi-flag',
typeLabel: 'Trace',
title: 'Lotus Forum 到店服务要求',
summary: '同一团队的两项服务要求,需要前台与客房部落实',
status: '待分派',
priority: '入住前完成',
receivedAt: '08:46',
sectionLabels: ['Basic', 'Trace', 'Room 快照', '邮件原文'],
basicInformation: createBasicInformation(
'Lotus MICE Partners',
'MICE Group',
'Email',
'继承自已定位订单,可从目录中人工修正',
),
businessCard: {
eyebrow: 'Trace',
title: '需要落实的服务要求',
description: '同一 Group Code 的多项要求保留在一张 Trace 中,责任部门以单一选择值表达。',
requirements: [
{
key: 'key-packets',
title: '提前准备钥匙包',
detail: '团队 13:30 到店,希望提前按房表准备钥匙包。',
editable: true,
},
{
key: 'feather-free',
title: '无羽绒寝具',
detail: '两间 SU2 需要使用无羽绒枕和被芯。',
editable: true,
},
],
department: 'FO+HSK',
departmentOptions: ['FO', 'HSK', 'FO+HSK'],
boundary: '邮件中的付款承担说明不进入 Trace,本卡只保留可执行服务要求。',
},
roomInformation: {
modeLabel: '关联订单快照',
summary: 'Room Information 用于说明服务要求影响哪一笔订单和哪些房间。',
targetTitle: '当前订单',
target: [
{ key: 'bookingType', label: 'Group / FIT', value: 'Group', control: 'derived' },
{ key: 'groupCode', label: 'Group Code', value: 'LTF-0828' },
{ key: 'groupName', label: 'Group Name / Block Name', value: 'Lotus Forum' },
{ key: 'names', label: 'Name(s)', value: 'LOTUS-FORUM-28' },
{ key: 'arrival', label: 'Arrival', value: '2026-08-28' },
{ key: 'departure', label: 'Departure', value: '2026-08-30' },
{ key: 'nights', label: 'Nights', value: '2', control: 'derived' },
{
key: 'rateCode',
label: 'Rate Code',
value: 'GL2000LT',
editable: true,
required: true,
control: 'combobox',
options: prototypeRateCodeOptions,
note: '从关联订单继承,可人工修正但不自动重算',
},
],
targetRooms: [
{ key: 'room-1', roomType: 'RM4', quantity: 16, occupancy: '2 人', meal: '含早餐' },
{ key: 'room-2', roomType: 'SU2', quantity: 2, occupancy: '2 人', meal: '含早餐' },
],
},
sourceMessage: {
channel: '会务公司邮件',
senderName: 'Lotus MICE Partners',
senderEmail: 'events@lotus-example.test',
recipients: safeRecipient,
receivedAt: '2026-08-08 08:46',
subject: 'Arrival arrangements for LTF-0828',
body: [
'Dear Hotel Team,',
'Our group LTF-0828 will arrive around 13:30. Please prepare the key packets according to the rooming list before arrival.',
'Two Deluxe King rooms also require feather-free pillows and duvets.',
'Please coordinate this with Front Office and Housekeeping.',
],
attachments: [],
},
evidence: [
{ label: '识别结论', value: 'Trace' },
{ label: '分组依据', value: 'Group Code LTF-0828' },
{ label: '要求数量', value: '2 项,原样保留' },
{ label: '部门选择', value: 'FO+HSK' },
],
},
{
key: 'rooming-list',
code: 'LOCAL-RML-005',
kind: 'rooming-list',
category: 'service',
icon: 'pi pi-file-excel',
typeLabel: 'Rooming List',
title: 'Bangkok Study Tour 房表更新',
summary: '当前邮件附带一份可下载的 Excel 名单',
status: '待下载处理',
priority: '普通',
receivedAt: '08:32',
sectionLabels: ['Basic', 'Rooming List', 'Room 快照', '邮件原文'],
basicInformation: createBasicInformation(
'Siam Atlas Travel',
'Leisure Group',
'Email',
'继承自 New Booking,可从目录中人工修正',
),
businessCard: {
eyebrow: 'Rooming List',
title: '当前邮件中的房表文件',
description: '业务人员下载后自行处理。原型不解析名单,也不生成新的 Excel。',
attachment: {
name: 'BST-0821_rooming-list_v2.xlsx',
mediaType: 'Excel workbook',
size: '84 KB',
purpose: '当前邮件房表附件',
},
boundary: '仅展示并下载当前邮件附件,不解析、不导入 PMS、不展示名单中的个人信息。',
},
roomInformation: {
modeLabel: '关联订单快照',
summary: '房表任务仍需展示其关联订单,帮助人工确认附件对应目标。',
targetTitle: '关联订单',
target: [
{ key: 'bookingType', label: 'Group / FIT', value: 'Group', control: 'derived' },
{ key: 'groupCode', label: 'Group Code', value: 'BST-0821' },
{ key: 'groupName', label: 'Group Name / Block Name', value: 'Bangkok Study Tour' },
{ key: 'names', label: 'Name(s)', value: 'BKK-STUDY-21A' },
{ key: 'arrival', label: 'Arrival', value: '2026-08-21' },
{ key: 'departure', label: 'Departure', value: '2026-08-24' },
{ key: 'nights', label: 'Nights', value: '3', control: 'derived' },
{
key: 'rateCode',
label: 'Rate Code',
value: 'GRPA1',
editable: true,
required: true,
control: 'combobox',
options: prototypeRateCodeOptions,
note: '关联订单继承值,可人工修正;本邮件不触发重算',
},
],
targetRooms: [
{ key: 'room-1', roomType: 'RM2', quantity: 14, occupancy: '2 人', meal: '含早餐' },
{ key: 'room-2', roomType: 'RM3', quantity: 4, occupancy: '2 人', meal: '含早餐' },
],
},
sourceMessage: {
channel: '旅行社邮件',
senderName: 'Siam Atlas Travel',
senderEmail: safeSender,
recipients: safeRecipient,
receivedAt: '2026-08-08 08:32',
subject: 'Updated rooming list for BST-0821',
body: [
'Dear Reservations,',
'Please find the updated rooming list for BST-0821 attached.',
'This version replaces the file sent yesterday. Please use the current attachment for manual processing.',
'No room quantity or stay date changes are requested in this email.',
],
attachments: [
{
name: 'BST-0821_rooming-list_v2.xlsx',
mediaType: 'Excel workbook',
size: '84 KB',
purpose: '当前邮件房表附件',
},
],
quotedHistory: ['Yesterday: an earlier rooming-list version was attached. The current email explicitly replaces it.'],
},
evidence: [
{ label: '识别结论', value: 'Rooming List' },
{ label: '附件范围', value: '仅当前邮件中的 xlsx' },
{ label: '订单定位', value: 'Group Code BST-0821' },
{ label: '处理边界', value: '下载给人工,不解析名单' },
],
},
{
key: 'payment-reference',
code: 'LOCAL-PAY-006',
kind: 'payment',
category: 'service',
icon: 'pi pi-image',
typeLabel: 'Payment',
title: 'Orchid Team 付款图片通知',
summary: '当前邮件含一张付款参考图片,仅供人工核对',
status: '待查看附件',
priority: '普通',
receivedAt: '08:18',
sectionLabels: ['Basic', 'Payment', 'Room 快照', '邮件原文'],
basicInformation: createBasicInformation(
'Orchid Routes Co.',
'Leisure Group',
'Email',
'继承自已定位订单,可从目录中人工修正',
),
businessCard: {
eyebrow: 'Payment',
title: '当前邮件中的付款参考图片',
description: '本卡只确认收到了图片,不判断到账、不验证凭证、不改变订单状态。',
attachment: {
name: 'payment-reference_ORC-0902.jpg',
mediaType: 'JPEG image',
size: '412 KB',
purpose: '付款参考图片',
},
boundary: '附件预览已做安全示意。正式系统应通过受控下载链读取原文件。',
},
roomInformation: {
modeLabel: '关联订单快照',
summary: '付款图片只关联订单,不改变当前房量、日期或价格状态。',
targetTitle: '关联订单',
target: [
{ key: 'bookingType', label: 'Group / FIT', value: 'Group', control: 'derived' },
{ key: 'groupCode', label: 'Group Code', value: 'ORC-0902' },
{ key: 'groupName', label: 'Group Name / Block Name', value: 'Orchid Team' },
{ key: 'names', label: 'Name(s)', value: 'ORC-0902-TOUR' },
{ key: 'arrival', label: 'Arrival', value: '2026-09-02' },
{ key: 'departure', label: 'Departure', value: '2026-09-05' },
{ key: 'nights', label: 'Nights', value: '3', control: 'derived' },
{
key: 'rateCode',
label: 'Rate Code',
value: 'GLSPCB',
editable: true,
required: true,
control: 'combobox',
options: prototypeRateCodeOptions,
note: '关联订单继承值,可人工修正;本邮件不触发重算',
},
],
targetRooms: [
{ key: 'room-1', roomType: 'RM2', quantity: 10, occupancy: '2 人', meal: '含早餐' },
{ key: 'room-2', roomType: 'SU1', quantity: 2, occupancy: '2 人', meal: '含早餐' },
],
},
sourceMessage: {
channel: '旅行社邮件',
senderName: 'Orchid Routes Co.',
senderEmail: 'groups@orchid-example.test',
recipients: safeRecipient,
receivedAt: '2026-08-08 08:18',
subject: 'Payment reference image for ORC-0902',
body: [
'Dear Team,',
'Attached is the payment reference image for group ORC-0902.',
'Please pass it to the colleague responsible for manual checking.',
'No booking changes are requested in this email.',
],
attachments: [
{
name: 'payment-reference_ORC-0902.jpg',
mediaType: 'JPEG image',
size: '412 KB',
purpose: '付款参考图片',
},
],
},
evidence: [
{ label: '识别结论', value: 'Payment' },
{ label: '附件范围', value: '当前邮件中的 1 张图片' },
{ label: '订单定位', value: 'Group Code ORC-0902' },
{ label: '处理边界', value: '不判断到账,不更新订单状态' },
],
},
{
key: 'risk-notification',
code: 'LOCAL-RSK-007',
kind: 'risk',
category: 'notification',
icon: 'pi pi-shield',
typeLabel: 'Risk',
title: '无法判断业务类型的邮件',
summary: '正文只说“按上次方式处理”,缺少可定位的订单与动作',
status: '待人工判断',
priority: '需要关注',
receivedAt: '08:04',
sectionLabels: ['Risk', '邮件原文', '识别依据'],
businessCard: {
eyebrow: 'Risk',
title: '业务意图无法安全分类',
description: '整封 current 邮件存在业务信号,但不足以判断 New、Update、Cancel、Trace、Rooming List 或 Payment。',
noticeReason: '缺少订单号、Group Code、日期和明确动作,附件名称也无法证明业务类型。',
suggestedAction: '阅读邮件原文并联系发件人确认。一个邮件最多保留一张 Risk。',
boundary: '邮件级通知不绑定订单,因此 Basic Information 与 Room Information 不适用。',
},
sourceMessage: {
channel: '未识别渠道邮件',
senderName: 'External Sender',
senderEmail: 'contact@unknown-example.test',
recipients: safeRecipient,
receivedAt: '2026-08-08 08:04',
subject: 'Please handle as before',
body: [
'Hi,',
'Please handle the attached item in the same way as last time.',
'Let me know when it is done.',
],
attachments: [
{
name: 'scan_0808.jpg',
mediaType: 'JPEG image',
size: '238 KB',
purpose: '未能判断用途的附件',
},
],
quotedHistory: ['No quoted message was available to explain what “last time” referred to.'],
},
evidence: [
{ label: '识别结论', value: 'Risk' },
{ label: '缺失信息', value: '订单定位、业务动作、日期' },
{ label: '不可推断', value: '附件用途与 last time 指代' },
{ label: '通知数量', value: '本邮件仅 1 张 Risk' },
],
},
{
key: 'general-notification',
code: 'LOCAL-GEN-008',
kind: 'general',
category: 'notification',
icon: 'pi pi-inbox',
typeLabel: 'General',
title: '旅行社营业时间通知',
summary: '整封邮件没有需要酒店执行的订单或服务动作',
status: '待查看',
priority: '低',
receivedAt: '07:42',
sectionLabels: ['General', '邮件原文', '识别依据'],
businessCard: {
eyebrow: 'General',
title: '仅需知悉的邮件',
description: '只有当整封 current 邮件没有可处理任务时,才生成 General。',
noticeReason: '内容只是通知对方假期营业时间,没有新建、修改、取消订单或服务要求。',
suggestedAction: '阅读后标记已知悉。无需创建订单或关联既有订单。',
boundary: '邮件级通知不绑定订单,因此 Basic Information 与 Room Information 不适用。',
},
sourceMessage: {
channel: '旅行社邮件',
senderName: 'Siam Atlas Travel',
senderEmail: safeSender,
recipients: safeRecipient,
receivedAt: '2026-08-08 07:42',
subject: 'Agency office hours for public holiday',
body: [
'Dear Partners,',
'Our office will close at 16:00 on 12 August for the public holiday.',
'Emergency contacts remain available through the usual channel.',
'This is an information notice only. No action is required for existing bookings.',
],
attachments: [],
},
evidence: [
{ label: '识别结论', value: 'General' },
{ label: '任务扫描', value: '无 New、Update、Cancel 或服务动作' },
{ label: '正文边界', value: '明确写明 no action is required' },
{ label: '订单绑定', value: '不适用' },
],
},
]
export function getPrototypeTask(taskKey: string | string[] | undefined): PrototypeTask | undefined {
const normalizedKey = Array.isArray(taskKey) ? taskKey[0] : taskKey
return prototypeTasks.find((task) => task.key === normalizedKey)
}
export function getAdjacentPrototypeTasks(task: PrototypeTask): {
previous?: PrototypeTask
next?: PrototypeTask
} {
const index = prototypeTasks.findIndex((candidate) => candidate.key === task.key)
return {
previous: index > 0 ? prototypeTasks[index - 1] : undefined,
next: index >= 0 ? prototypeTasks[index + 1] : undefined,
}
}