优化回执排序
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# 微信回执文本分段
|
||||
|
||||
## Scope / 授权
|
||||
|
||||
- 用户要求回执增加空格和换行,适合微信聊天框阅读。
|
||||
- 调整成功回执、团队进度和安排明细的纯文本排版;不改变业务记录、账号归属、ERP 写入或投递时机。
|
||||
- 不部署、不重启、不修改插件或外发真实消息。单线程实施。
|
||||
|
||||
## Worktree Gate
|
||||
|
||||
- Branch: main;Worktree: /Users/andy/IdeaProjects/LWLT-AIBOT。
|
||||
- Base: 1797c713af179e33364ac22ce2956a8d232aa06c;唯一 worktree。
|
||||
- 既有未跟踪 `.idea/`、`dist/ltjt-order-assistant-0.5.189.zip` 保留,源码无未提交修改。
|
||||
- Overlap: Clear;Feature 模式,仅写本任务项目记忆。
|
||||
|
||||
## Progress
|
||||
|
||||
- 已完成:本次操作标题与正文留空行,多团号/子单号/文件逐行缩进;酒店英文和中文模板之间留空行。
|
||||
- 团队基本信息、办理进度、每个子单、每类安排独立分段;记录时间单独一行,修改字段与各条安排字段逐行显示,不再通过竖线挤成一行。
|
||||
- 安排名细仅资源名称已知时仍显示该名称;完全缺失明细继续显示“明细未记录”。其余业务值、统计规则、附件、投递时机不变。
|
||||
- 保持纯文本,平台与 AgentBus 共用格式。更新既有回执测试的期望空行;未新增排版专用测试。
|
||||
- 未更新插件、数据库迁移或发布包,未部署、重启或发送真实微信消息。
|
||||
|
||||
## Validation / Promotion Candidates
|
||||
|
||||
- `check`、`build` 通过;控制面 293/293、旧功能 439/439 通过。
|
||||
- 工作树 `check:repo` 8/10,失败仅为既有 `.idea/`、`.DS_Store` 和被忽略的历史日志,原文件均保留。当前源码独立快照 10/10 通过。
|
||||
- 使用合成数据生成并检查完整纯文本回执,段落、字段、多个安排均清晰分开;未进行真实微信外发测试。[验证与样例](../../../archive/evidence/2026-09-22/wechat-receipt-layout-01a0858b/README.md)。
|
||||
- Promotion:后续 Integration 可在回执展示相关 canonical 说明中加入“纯文本段落、逐字段换行与缩进”约定;业务与架构规则不变,无待决产品问题。
|
||||
@@ -88,6 +88,8 @@
|
||||
|
||||
平台保存完整回执。AgentBus 最终文本超过现有 20,000 字符限制时,按行截取并明确提示“完整内容请在平台任务回执中查看”,不静默截断;阶段消息和附件交付规则不变。
|
||||
|
||||
微信回执使用纯文本分段:本次结果标题与正文之间、办理进度与安排明细之间、各子单及各类安排之间留空行。多团号、多文件和多条安排逐条换行,安排字段及记录时间缩进显示;不依赖 Markdown 表格、HTML 或空格对齐列。排版不改变字段值、统计范围或附件内容。
|
||||
|
||||
## 酒店安排客户模板
|
||||
|
||||
酒店新增成功且执行结果包含完整酒店字段时,使用以下格式。变量全部由真实回查结果填充;英文和中文日期使用同一组实际日期。
|
||||
@@ -98,6 +100,7 @@ GROUP CODE: {group_code}
|
||||
IN. {in_date_dd_mm_yyyy}
|
||||
OUT {out_date_dd_mm_yyyy}
|
||||
{nights}Night(s). {room_count}{room_type_en}
|
||||
|
||||
②新订单
|
||||
团号:{group_code}{customer_suffix}
|
||||
入住日期:{in_date_yyyy_mm_dd_with_dots}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# 微信回执排版验证
|
||||
|
||||
日期:2026-09-22。此目录仅保存历史验证证据,不定义当前业务规则。
|
||||
|
||||
当前规则:[AgentBus 用户回复契约](../../../../agent设计规范/agentbus-reply-contract.md)。
|
||||
|
||||
## 验证结果
|
||||
|
||||
- `node --run check`、`node --run build`:通过。
|
||||
- `node --run test:control-plane`:293/293 通过;既有精确文本断言同步新的空行。
|
||||
- `node --run test:legacy`:439/439 通过;在批准的本机临时 HTTP 测试环境执行,无 ERP 或外部发送。
|
||||
- `node --run check:repo`:工作树受原有 IDE 元数据、`.DS_Store` 和被忽略历史日志影响为 8/10;未删除这些文件,当前源码独立快照为 10/10。
|
||||
- `git diff --check`:通过。
|
||||
|
||||
## 文本样例
|
||||
|
||||
[完整样例回执](receipt-preview.txt) 由当前回执构造函数生成,全部内容为合成数据;已检查酒店双语段落、办理进度、记录时间、修改字段及两条酒店安排的分段与缩进。仅资源名称已知时不再隐藏名称,其他字段和统计语义不变。
|
||||
|
||||
不使用 Markdown 表格、HTML 或空格对齐列;通过真实换行和空行分段。微信客户端会自行换行,未进行真实微信发送或截图验收。本次仅修改控制面文本渲染和契约,插件、数据库 Schema、ERP 执行、附件与投递时机未改动。
|
||||
@@ -0,0 +1,63 @@
|
||||
①NEW BOOKING
|
||||
GROUP CODE: LW-SAMPLE-A
|
||||
IN. 01/10/2026
|
||||
OUT 02/10/2026
|
||||
1Night. 10Twin
|
||||
|
||||
②新订单
|
||||
团号:LW-SAMPLE-A
|
||||
入住日期:2026.10.01
|
||||
退房日期:2026.10.02
|
||||
几 晚:1晚
|
||||
房型/房数:标间 10间
|
||||
|
||||
本系统团队办理进度(当前账号)
|
||||
|
||||
团号:LW-SAMPLE-A
|
||||
客户:示例客户
|
||||
产品:示例行程
|
||||
出发日期:2026-10-01
|
||||
|
||||
【办理进度】
|
||||
订单状态:已创建
|
||||
下单:已完成
|
||||
时间:2026-09-22 14:00
|
||||
|
||||
名单:已导入,最近一次20人
|
||||
时间:2026-09-22 14:00
|
||||
|
||||
最近修改:
|
||||
标间改为10
|
||||
大床房改为2
|
||||
时间:2026-09-22 14:00
|
||||
|
||||
文件:暂无本系统导出记录
|
||||
|
||||
【安排情况与明细】
|
||||
|
||||
导游:已安排,共1条
|
||||
1. 示例导游
|
||||
电话:示例电话
|
||||
|
||||
用车:未安排
|
||||
|
||||
酒店:已安排,共2条
|
||||
1. 示例酒店甲
|
||||
房型:TWN
|
||||
入住日期:2026-10-01
|
||||
离店日期:2026-10-02
|
||||
房数:10间
|
||||
|
||||
2. 示例酒店乙
|
||||
房型:DBL
|
||||
入住日期:2026-10-02
|
||||
离店日期:2026-10-03
|
||||
房数:2间
|
||||
|
||||
大交通:未安排
|
||||
|
||||
其他/备案:未安排
|
||||
|
||||
仅统计当前账号通过本系统成功完成的操作。
|
||||
未记录不代表 ERP 中没有。
|
||||
已安排表示至少一条,不代表全行程已安排齐全。
|
||||
@@ -48,6 +48,17 @@ function firstText(...values: unknown[]): string {
|
||||
return texts(...values)[0] || '';
|
||||
}
|
||||
|
||||
function businessText(lines: string[]): string {
|
||||
const [title, ...details] = lines.filter(Boolean);
|
||||
return [title, details.join('\n')].filter(Boolean).join('\n\n');
|
||||
}
|
||||
|
||||
function labelledList(label: string, values: string[]): string {
|
||||
return values.length > 1
|
||||
? `${label}:\n${values.map(value => ` ${value}`).join('\n')}`
|
||||
: `${label}:${values[0]}`;
|
||||
}
|
||||
|
||||
function firstNumber(...values: unknown[]): number | null {
|
||||
for (const value of values) {
|
||||
if (value === undefined || value === null || value === '') continue;
|
||||
@@ -144,7 +155,7 @@ function identifierLine(
|
||||
|| refs.kind === 'shared_child_order';
|
||||
if (child && orderNumbers.length) {
|
||||
return {
|
||||
text: `子单号:${orderNumbers.join('、')}`,
|
||||
text: labelledList('子单号', orderNumbers),
|
||||
identifier: orderNumbers[0],
|
||||
groupNumbers,
|
||||
orderNumbers
|
||||
@@ -152,7 +163,7 @@ function identifierLine(
|
||||
}
|
||||
if (groupNumbers.length) {
|
||||
return {
|
||||
text: `团号:${groupNumbers.join('、')}`,
|
||||
text: labelledList('团号', groupNumbers),
|
||||
identifier: groupNumbers[0],
|
||||
groupNumbers,
|
||||
orderNumbers
|
||||
@@ -160,7 +171,7 @@ function identifierLine(
|
||||
}
|
||||
if (orderNumbers.length) {
|
||||
return {
|
||||
text: `订单号:${orderNumbers.join('、')}`,
|
||||
text: labelledList('订单号', orderNumbers),
|
||||
identifier: orderNumbers[0],
|
||||
groupNumbers,
|
||||
orderNumbers
|
||||
@@ -303,6 +314,7 @@ function hotelNewBookingReply(
|
||||
`IN. ${englishDate(values.start)}`,
|
||||
`OUT ${englishDate(values.end)}`,
|
||||
`${nights}${nightLabel} ${values.roomCount}${englishRoom}`,
|
||||
'',
|
||||
'②新订单',
|
||||
`团号:${identifier}${customerSuffix}`,
|
||||
`入住日期:${chineseDate(values.start)}`,
|
||||
@@ -413,7 +425,7 @@ function arrangementReply(
|
||||
const id = identifier ? `团号:${identifier}` : '';
|
||||
if (mode === 'clear') {
|
||||
return {
|
||||
text: [`${actionTitle(action).replace('成功', '已清除')}`, id].filter(Boolean).join('\n'),
|
||||
text: businessText([`${actionTitle(action).replace('成功', '已清除')}`, id]),
|
||||
profile: 'standard-arrangement-v1',
|
||||
fields: { identifier, mode: 'clear' }
|
||||
};
|
||||
@@ -448,7 +460,7 @@ function arrangementReply(
|
||||
else if (date) lines.push(`日期:${date}`);
|
||||
if (quantity !== null) lines.push(action === 'arrangement_hotel' ? `房数:${quantity}间` : `数量:${quantity}`);
|
||||
return {
|
||||
text: lines.filter(Boolean).join('\n'),
|
||||
text: businessText(lines),
|
||||
profile: 'standard-arrangement-v1',
|
||||
fields: { identifier, resource: text(ref.name), item, start_date: start, end_date: end, date, quantity, mode: mode || 'create' }
|
||||
};
|
||||
@@ -506,7 +518,7 @@ export function buildCustomerSuccessReply(
|
||||
fields = { ...fields, ...reply.fields };
|
||||
} else if (action.startsWith('order_update_')) {
|
||||
const lines = [actionTitle(action), identity.text, ...updateLines(operationValue, result, context)].filter(Boolean);
|
||||
textValue = lines.join('\n');
|
||||
textValue = businessText(lines);
|
||||
profile = 'standard-update-v1';
|
||||
fields = { ...fields, updated_fields: updateLines(operationValue, result, context) };
|
||||
} else if (action === 'passenger_list_import') {
|
||||
@@ -520,7 +532,7 @@ export function buildCustomerSuccessReply(
|
||||
object(data.passenger_list).row_count,
|
||||
requery.expected_persisted_count
|
||||
);
|
||||
textValue = [actionTitle(action), identity.text, count === null ? '' : `本次导入:${count}人`].filter(Boolean).join('\n');
|
||||
textValue = businessText([actionTitle(action), identity.text, count === null ? '' : `本次导入:${count}人`]);
|
||||
profile = 'standard-passenger-import-v1';
|
||||
fields = { ...fields, imported_count: count };
|
||||
} else if (action === 'order_cancel' || action === 'order_restore') {
|
||||
@@ -531,19 +543,19 @@ export function buildCustomerSuccessReply(
|
||||
&& result.side_effects.some((value) => /child|子单/iu.test(String(value)))
|
||||
? '子单状态已同步'
|
||||
: '';
|
||||
textValue = [actionTitle(action), identity.text, targetStatus ? `目标状态:${targetStatus}` : '', sideEffect].filter(Boolean).join('\n');
|
||||
textValue = businessText([actionTitle(action), identity.text, targetStatus ? `目标状态:${targetStatus}` : '', sideEffect]);
|
||||
profile = 'standard-lifecycle-v1';
|
||||
fields = { ...fields, target_status: targetStatus, side_effect: sideEffect };
|
||||
} else if (action === 'confirmation_export') {
|
||||
const names = attachments.map((item) => item.name);
|
||||
textValue = [actionTitle(action), identity.text, names.length ? `文件:${names.join('、')}` : '文件已保存到任务附件'].filter(Boolean).join('\n');
|
||||
textValue = businessText([actionTitle(action), identity.text, names.length ? labelledList('文件', names) : '文件已保存到任务附件']);
|
||||
profile = 'standard-file-delivery-v1';
|
||||
fields = { ...fields, file_names: names, attachment_count: attachments.length };
|
||||
} else if (['team_order_create', 'team_order_batch_create', 'shared_plan_create', 'shared_child_order_create', 'shared_child_order_batch_create'].includes(action)) {
|
||||
textValue = [actionTitle(action), identity.text].filter(Boolean).join('\n');
|
||||
textValue = businessText([actionTitle(action), identity.text]);
|
||||
profile = 'standard-newbooking-v1';
|
||||
} else {
|
||||
textValue = [actionTitle(action), identity.text].filter(Boolean).join('\n');
|
||||
textValue = businessText([actionTitle(action), identity.text]);
|
||||
}
|
||||
|
||||
if (!textValue) return null;
|
||||
|
||||
@@ -146,16 +146,18 @@ function displayTime(value: string): string {
|
||||
hour: '2-digit', minute: '2-digit' }).format(new Date(value));
|
||||
}
|
||||
function arrangementLine(action: string, details: Json): string {
|
||||
const fields = [txt(details.resource_name), txt(details.item),
|
||||
details.start_date ? `${txt(details.start_date)} 至 ${txt(details.end_date) || '离店/结束日未记录'}` : txt(details.date),
|
||||
details.quantity === undefined ? '' : `${txt(details.quantity)}${action === 'arrangement_hotel' ? '间' : '(数量)'}`,
|
||||
const hotel = action === 'arrangement_hotel';
|
||||
const fields = [txt(details.resource_name), details.item ? `${hotel ? '房型' : '项目'}:${txt(details.item)}` : '',
|
||||
details.start_date ? `${hotel ? '入住日期' : '开始日期'}:${txt(details.start_date)}` : details.date ? `日期:${txt(details.date)}` : '',
|
||||
details.start_date ? `${hotel ? '离店日期' : '结束日期'}:${txt(details.end_date) || '未记录'}` : '',
|
||||
details.quantity === undefined ? '' : `${hotel ? '房数' : '数量'}:${txt(details.quantity)}${hotel ? '间' : ''}`,
|
||||
details.phone ? `${action === 'arrangement_vehicle' ? '司机电话' : '电话'}:${txt(details.phone)}` : '',
|
||||
details.vehicle_number ? `车号:${txt(details.vehicle_number)}` : '', details.grade ? `等级:${txt(details.grade)}` : '',
|
||||
details.filing_number ? `备案号:${txt(details.filing_number)}` : '', details.filing_date ? `备案日期:${txt(details.filing_date)}` : '',
|
||||
details.filing_entry_port ? `入境口岸:${txt(details.filing_entry_port)}` : '', details.filing_exit_port ? `出境口岸:${txt(details.filing_exit_port)}` : '',
|
||||
details.remark ? `备注:${txt(details.remark)}` : ''].filter(Boolean);
|
||||
if (!details.resource_name) fields.unshift('资源名称未记录');
|
||||
return fields.length > 1 ? fields.join('|') : '已安排,明细未记录';
|
||||
return fields.length > 1 || details.resource_name ? fields.join('\n ') : '已安排,明细未记录';
|
||||
}
|
||||
|
||||
export function teamProgressText(value: unknown): string {
|
||||
@@ -172,49 +174,52 @@ export function teamProgressText(value: unknown): string {
|
||||
if (!facts.product && facts.product_condition) lines.push(`产品条件:${txt(facts.product_condition)}`);
|
||||
const status = latest(team, e => e.action === 'order_cancel' || e.action === 'order_restore'
|
||||
|| ['team_order_create','team_order_batch_create','shared_plan_create'].includes(e.action));
|
||||
lines.push(`订单状态:${status ? txt(status.details.order_status) || '已创建' : '暂无本系统状态记录'}`);
|
||||
lines.push('', '【办理进度】', `订单状态:${status ? txt(status.details.order_status) || '已创建' : '暂无本系统状态记录'}`);
|
||||
const subjects = [...new Set(events.filter(e => e.subject_kind === 'child').map(e => e.subject_number))].sort();
|
||||
const shared = facts.team_kind === 'shared_plan' || subjects.length > 0;
|
||||
const creation = latest(team, e => ['team_order_create','team_order_batch_create','shared_plan_create'].includes(e.action));
|
||||
lines.push(`${shared ? '建计划' : '下单'}:${creation ? `已完成(${displayTime(creation.effective_at)})` : '暂无本系统成功记录'}`);
|
||||
lines.push(`${shared ? '建计划' : '下单'}:${creation ? `已完成\n 时间:${displayTime(creation.effective_at)}` : '暂无本系统成功记录'}`);
|
||||
const subjectProgress = (subset: ProgressEvent[], label: string, parent = false): void => {
|
||||
const roster = latest(subset, e => e.action === 'passenger_list_import');
|
||||
if (!parent) lines.push(`${label}名单:${roster ? `已导入,最近一次${roster.details.roster_count === undefined ? '人数未记录' : `${roster.details.roster_count}人`}(${displayTime(roster.effective_at)})` : '暂无本系统导入记录'}`);
|
||||
if (!parent) lines.push('', `${label}名单:${roster ? `已导入,最近一次${roster.details.roster_count === undefined ? '人数未记录' : `${roster.details.roster_count}人`}\n 时间:${displayTime(roster.effective_at)}` : '暂无本系统导入记录'}`);
|
||||
const update = latest(subset, e => e.action.startsWith('order_update_'));
|
||||
lines.push(`${label}最近修改:${update ? `${list(update.details.updates).map(u => `${txt(u.label)}${u.mode === 'append' ? '追加' : '改为'}${txt(u.value)}`).join(';') || '修改成功,明细未记录'}(${displayTime(update.effective_at)})` : '暂无本系统修改记录'}`);
|
||||
lines.push('', `${label}最近修改:${update ? `\n ${list(update.details.updates).map(u => `${txt(u.label)}${u.mode === 'append' ? '追加' : '改为'}${txt(u.value)}`).join('\n ') || '修改成功,明细未记录'}\n 时间:${displayTime(update.effective_at)}` : '暂无本系统修改记录'}`);
|
||||
const exports = new Map<string, ProgressEvent>();
|
||||
for (const e of subset) if (e.action === 'confirmation_export') for (const f of strings(e.details.files)) exports.set(f, e);
|
||||
const unknownExport = latest(subset, e => e.action === 'confirmation_export' && !strings(e.details.files).length);
|
||||
if (!exports.size && !unknownExport) lines.push(`${label}文件:暂无本系统导出记录`);
|
||||
if (unknownExport) lines.push(`${label}文件:已导出,文件类型未记录(${displayTime(unknownExport.effective_at)})`);
|
||||
if (!exports.size && !unknownExport) lines.push('', `${label}文件:暂无本系统导出记录`);
|
||||
if (unknownExport) lines.push('', `${label}文件:已导出,文件类型未记录\n 时间:${displayTime(unknownExport.effective_at)}`);
|
||||
for (const [file, e] of exports) {
|
||||
const changed = events.some(change => change.action !== 'confirmation_export' && change.effective_at > e.effective_at
|
||||
&& (change.subject_kind === 'team' || subset.includes(change) || (file === 'visitor-list' && shared)));
|
||||
lines.push(`${label}${parent && file === 'visitor-list' ? '整团游客信息' : FILES[file] || '团队文件'}:已导出(${displayTime(e.effective_at)})${changed ? ';之后有业务变更,可能需要重新导出' : ''}`);
|
||||
lines.push('', `${label}${parent && file === 'visitor-list' ? '整团游客信息' : FILES[file] || '团队文件'}:已导出\n 时间:${displayTime(e.effective_at)}${changed ? '\n 之后有业务变更,可能需要重新导出' : ''}`);
|
||||
}
|
||||
};
|
||||
if (!shared) subjectProgress(team, '');
|
||||
else {
|
||||
lines.push(`本系统记录子单:${subjects.length}个(不代表母团全部子单)`);
|
||||
lines.push('', `本系统记录子单:${subjects.length}个`, '(不代表母团全部子单)');
|
||||
// Parent modifications and whole-group exports belong to the parent.
|
||||
subjectProgress(team, '母团', true);
|
||||
for (const child of subjects) {
|
||||
const subset = events.filter(e => e.subject_kind === 'child' && e.subject_number === child);
|
||||
const created = subset.some(e => ['shared_child_order_create','shared_child_order_batch_create'].includes(e.action));
|
||||
const state = latest(subset, e => ['order_cancel','order_restore','shared_child_order_create','shared_child_order_batch_create'].includes(e.action));
|
||||
lines.push(`子单 ${child}:${created ? '已新增' : '暂无本系统新增记录'};状态:${state ? txt(state.details.order_status) || '已创建' : '未记录'}`);
|
||||
lines.push('', `子单 ${child}:${created ? '已新增' : '暂无本系统新增记录'};状态:${state ? txt(state.details.order_status) || '已创建' : '未记录'}`);
|
||||
subjectProgress(subset, ` ${child} `);
|
||||
}
|
||||
}
|
||||
lines.push('【安排情况与明细】');
|
||||
lines.push('', '【安排情况与明细】');
|
||||
for (const [action, label] of Object.entries(ARRANGEMENT_LABELS)) {
|
||||
const items = entry.arrangements.filter(a => a.action === action);
|
||||
lines.push(`${label}:${items.length ? `已安排,共${items.length}条` : '未安排'}`);
|
||||
items.forEach((a,i) => lines.push(` ${i+1}. ${arrangementLine(action, a.details)}`));
|
||||
lines.push('', `${label}:${items.length ? `已安排,共${items.length}条` : '未安排'}`);
|
||||
items.forEach((a,i) => {
|
||||
if (i > 0) lines.push('');
|
||||
lines.push(` ${i+1}. ${arrangementLine(action, a.details)}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!snap.groups.length || snap.unlinked_subjects.length) lines.push(`\n${snap.unlinked_subjects.length ? `对象:${snap.unlinked_subjects.join('、')}。` : ''}暂无法关联团号,未推断安排状态。`);
|
||||
lines.push('\n仅统计当前账号通过本系统成功完成的操作;未记录不代表 ERP 中没有。已安排表示至少一条,不代表全行程已安排齐全。');
|
||||
lines.push('\n仅统计当前账号通过本系统成功完成的操作。', '未记录不代表 ERP 中没有。', '已安排表示至少一条,不代表全行程已安排齐全。');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ test('hotel arrangement uses the customer bilingual booking template', () => {
|
||||
'IN. 03/06/2026',
|
||||
'OUT 04/06/2026',
|
||||
'1Night. 6Twin',
|
||||
'',
|
||||
'②新订单',
|
||||
'团号:LW-260603Z-A 老挝联泰',
|
||||
'入住日期:2026.06.03',
|
||||
@@ -65,7 +66,7 @@ test('hotel clear and update replies stay business-readable when the booking tem
|
||||
}
|
||||
);
|
||||
assert.ok(clearReply);
|
||||
assert.equal(clearReply.text, '酒店安排已清除\n团号:LW-260603Z-A');
|
||||
assert.equal(clearReply.text, '酒店安排已清除\n\n团号:LW-260603Z-A');
|
||||
|
||||
const updateReply = buildCustomerSuccessReply(
|
||||
{ success: true, group_number: 'LW-260603Z-A' },
|
||||
@@ -79,7 +80,7 @@ test('hotel clear and update replies stay business-readable when the booking tem
|
||||
}
|
||||
);
|
||||
assert.ok(updateReply);
|
||||
assert.equal(updateReply.text, '酒店安排成功\n团号:LW-260603Z-A\n退房日期:2026-06-05\n房数:9间');
|
||||
assert.equal(updateReply.text, '酒店安排成功\n\n团号:LW-260603Z-A\n退房日期:2026-06-05\n房数:9间');
|
||||
});
|
||||
|
||||
test('hotel replacement reply describes the new hotel and count while retaining the original dates', () => {
|
||||
@@ -100,7 +101,7 @@ test('hotel replacement reply describes the new hotel and count while retaining
|
||||
}
|
||||
}
|
||||
);
|
||||
assert.equal(reply?.text, '酒店安排成功\n团号:LW-TEST-A\n资源:测试新酒店\n入住日期:2026-09-15\n退房日期:2026-09-16\n房数:9间');
|
||||
assert.equal(reply?.text, '酒店安排成功\n\n团号:LW-TEST-A\n资源:测试新酒店\n入住日期:2026-09-15\n退房日期:2026-09-16\n房数:9间');
|
||||
assert.equal(reply?.fields.resource, '测试新酒店');
|
||||
assert.equal(reply?.fields.quantity, 9);
|
||||
});
|
||||
@@ -112,7 +113,7 @@ test('batch newbooking receipt preserves every group number', () => {
|
||||
{ action: 'team_order_batch_create', data: {} }
|
||||
);
|
||||
assert.ok(reply);
|
||||
assert.equal(reply.text, '批量下单成功\n团号:LW-260603A-A、LW-260605A-A');
|
||||
assert.equal(reply.text, '批量下单成功\n\n团号:\n LW-260603A-A\n LW-260605A-A');
|
||||
});
|
||||
|
||||
test('update and file replies expose business fields without technical evidence', () => {
|
||||
@@ -128,7 +129,7 @@ test('update and file replies expose business fields without technical evidence'
|
||||
}
|
||||
);
|
||||
assert.ok(updateReply);
|
||||
assert.equal(updateReply.text, '散拼母团信息修改成功\n团号:LW-260603A-A\n计划收客数:45');
|
||||
assert.equal(updateReply.text, '散拼母团信息修改成功\n\n团号:LW-260603A-A\n计划收客数:45');
|
||||
assert.doesNotMatch(updateReply.text, /sha256|verification|blocker|execution/iu);
|
||||
|
||||
const baseReceipt = buildCustomerSuccessReceipt(
|
||||
@@ -146,7 +147,7 @@ test('update and file replies expose business fields without technical evidence'
|
||||
},
|
||||
{ action: 'confirmation_export', data: { existing_refs: { identifier: 'LW-260603A-A' } } }
|
||||
);
|
||||
assert.equal(replyTextFromReceipt(baseReceipt), '团队文件已准备\n团号:LW-260603A-A\n文件:liantai-confirm.docx');
|
||||
assert.equal(replyTextFromReceipt(baseReceipt), '团队文件已准备\n\n团号:LW-260603A-A\n文件:liantai-confirm.docx');
|
||||
assert.deepEqual(replyAttachmentsFromReceipt(baseReceipt), [{
|
||||
name: 'liantai-confirm.docx',
|
||||
content_type: 'application/msword',
|
||||
@@ -181,7 +182,7 @@ test('visitor export reply exposes only the mobile XLSX delivery artifact', () =
|
||||
},
|
||||
{ action: 'confirmation_export', data: { existing_refs: { identifier: 'LW-260816Z-A' } } }
|
||||
);
|
||||
assert.equal(replyTextFromReceipt(receipt!), '团队文件已准备\n团号:LW-260816Z-A\n文件:LW-260816Z-A Visitor.xlsx');
|
||||
assert.equal(replyTextFromReceipt(receipt!), '团队文件已准备\n\n团号:LW-260816Z-A\n文件:LW-260816Z-A Visitor.xlsx');
|
||||
assert.deepEqual(replyAttachmentsFromReceipt(receipt!), [{
|
||||
name: 'LW-260816Z-A Visitor.xlsx',
|
||||
content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
@@ -206,7 +207,7 @@ test('legacy ERP mojibake filenames are repaired when a stored receipt is read',
|
||||
},
|
||||
{ action: 'confirmation_export', data: { existing_refs: { identifier: 'LW-261202A-A' } } }
|
||||
);
|
||||
assert.equal(replyTextFromReceipt(receipt), '团队文件已准备\n团号:LW-261202A-A\n文件:LLW-261202A-A 客户确认书.doc');
|
||||
assert.equal(replyTextFromReceipt(receipt), '团队文件已准备\n\n团号:LW-261202A-A\n文件:LLW-261202A-A 客户确认书.doc');
|
||||
assert.deepEqual(replyAttachmentsFromReceipt(receipt), [{
|
||||
name: 'LLW-261202A-A 客户确认书.doc',
|
||||
content_type: 'application/msword',
|
||||
|
||||
Reference in New Issue
Block a user