判断修改

This commit is contained in:
andy committed 2026-09-14 12:47:15 +08:00
1 parent e06afc8c33
commit 4194a200c1
65 files changed
+9617 -109

No files matched your search

+46 -18
View File
@@ -133,7 +133,11 @@ const minimumLabels = {
修改散拼母团计划收客数: ['出发日期', '目标计划收客数'],
追加散拼子单订房说明: ['预订客户', '出发日期', '追加内容'],
独立团信息修改: ['预订客户', '出发日期', '目标修改'],
安排变更: ['团号', '目标变更'],
安排酒店变更: ['团号', '入住日期', '离店日期', '酒店搜索', '间数'],
安排导游变更: ['团号', '导游搜索'],
安排用车变更: ['团号', '结算单位搜索', '用车日期', '数量'],
安排大交通变更: ['团号', '结算单位搜索', '交通日期', '数量'],
'安排其他/备案变更': ['团号', '结算单位搜索', '业务日期'],
取消订单: ['预订客户', '出发日期'],
恢复订单: ['预订客户', '出发日期'],
导出团队文件: ['预订客户', '出发日期', '文件类型']
@@ -169,6 +173,17 @@ const fieldLines = (block) =>
.slice(1)
.filter((line) => line.includes(':'));
const arrangementUpdateDirectives = ['安排酒店变更', '安排导游变更', '安排用车变更', '安排大交通变更', '安排其他/备案变更'];
const catalogPairs = operationSections.flatMap((section) => {
const directives = section.directive === '安排变更' ? arrangementUpdateDirectives : [section.directive];
const blocks = textBlocks(section.body);
return directives.map((directive) => {
const matching = blocks.filter((block) => block.startsWith(`${directive}\n`));
return { directive, blocks: matching, template: matching[0], example: matching[1] };
});
});
const pairByDirective = new Map(catalogPairs.map((pair) => [pair.directive, pair]));
const assertTwoBlockContract = (document, directives, source) => {
const blocks = textBlocks(document);
@@ -192,17 +207,22 @@ test('catalog has exactly 19 ordered operations with adjacent template and examp
);
assert.deepEqual(operationSections.map((section) => section.directive), allDirectives);
for (const pair of catalogPairs) {
assert.equal(pair.blocks.length, 2, `${pair.directive}: expected template and example`);
assert.ok(pair.template, `${pair.directive}: missing template block`);
assert.ok(pair.example, `${pair.directive}: missing example block`);
assert.match(pair.template, /(必填):/u);
assert.doesNotMatch(pair.example, /(必填|选填)/u);
}
for (const section of operationSections) {
const template = labeledBlock(section, '模板');
const example = labeledBlock(section, '示例');
assert.ok(template, `${section.directive}: missing template block`);
assert.ok(example, `${section.directive}: missing example block`);
assert.equal(textBlocks(section.body).length, section.directive === '安排变更' ? 7 : 2, `${section.directive}: unexpected text blocks`);
if (section.directive === '安排变更') {
assert.match(textBlocks(section.body)[2], /^安排酒店变更\n团号:.+\n入住日期:.+\n离店日期:.+\n酒店搜索:.+\n间数:9$/u);
const blocks = textBlocks(section.body);
assert.deepEqual(blocks.map((block) => block.split('\n')[0]), arrangementUpdateDirectives.flatMap((directive) => [directive, directive]));
continue;
}
assert.equal(template.split(/\r?\n/, 1)[0], section.directive);
assert.equal(example.split(/\r?\n/, 1)[0], section.directive);
assert.equal(textBlocks(section.body).length, 2);
assert.ok(labeledBlock(section, '模板'));
assert.ok(labeledBlock(section, '示例'));
assert.ok(section.body.indexOf('#### 模板') < section.body.indexOf('#### 示例'));
}
@@ -331,9 +351,9 @@ test('Agent parse Schema accepts business facts and rejects execution metadata',
});
test('template fields are marked and examples contain only concrete values', () => {
for (const section of operationSections) {
const template = labeledBlock(section, '模板');
const example = labeledBlock(section, '示例');
for (const section of catalogPairs) {
const template = section.template;
const example = section.example;
const fields = fieldLines(template);
assert.ok(fields.length > 0, `${section.directive}: expected at least one field`);
@@ -351,8 +371,8 @@ test('template fields are marked and examples contain only concrete values', ()
test('user templates never request ERP internal identifiers and use canonical customer label', () => {
const forbiddenLabels = /^(?:母团\s*)?(?:tid|ddid|row_id|行\s*ID|账号|运行编号|测试标记|allowlist)((?:必填|选填)):/im;
for (const section of operationSections) {
const template = labeledBlock(section, '模板');
for (const section of catalogPairs) {
const template = section.template;
assert.doesNotMatch(template, forbiddenLabels);
assert.doesNotMatch(template, /^预定客户((?:必填|选填)):/m, 'template must use 预订客户');
}
@@ -416,7 +436,7 @@ test('the two desktop-approved examples remain exactly copyable', () => {
test('every operation preserves its explicit minimum-required field set', () => {
for (const [directive, expectedLabels] of Object.entries(minimumLabels)) {
const template = labeledBlock(sectionByDirective.get(directive), '模板');
const template = pairByDirective.get(directive).template;
const actualLabels = fieldLines(template)
.filter((line) => line.includes('(必填):'))
.map((line) => line.slice(0, line.indexOf('(必填):')));
@@ -441,9 +461,9 @@ test('modification input asks only for targets and export stays minimal', () =>
'修改散拼母团计划收客数',
'追加散拼子单订房说明',
'独立团信息修改',
'安排变更'
...arrangementUpdateDirectives
]) {
assert.doesNotMatch(labeledBlock(sectionByDirective.get(directive), '模板'), forbiddenPreviousValueField);
assert.doesNotMatch(pairByDirective.get(directive).template, forbiddenPreviousValueField);
}
assert.deepEqual(minimumLabels['导出团队文件'], ['预订客户', '出发日期', '文件类型']);
@@ -497,7 +517,15 @@ test('independent-order updates preserve broad parser targets while arrangement
assert.match(updatingSkill, /兼容旧三段 `人数改为8\+1(占)\+1`/);
assert.match(updatingSkill, /pax\.child_no_bed=1/);
assert.match(sectionByDirective.get('独立团信息修改')?.body ?? '', /未出现的类别保持原值/);
assert.match(labeledBlock(sectionByDirective.get('安排变更'), '示例'), /离店日期改为11-05;房间数改为15间/);
const updates = sectionByDirective.get('安排变更').body;
const [current, legacy] = updates.split('#### 旧版酒店指令兼容说明');
assert.ok(legacy, 'legacy hotel syntax must be separately explained');
assert.doesNotMatch(current, /^安排变更$|^目标变更[(:]|^安排类型[(:]/mu);
assert.match(current, /日期填写原安排日期,只用于定位,不修改日期/);
assert.match(legacy, /只接受酒店/);
assert.match(legacy, /会实际修改离店日期/);
assert.match(legacy, /默认针对酒店表单第一行/);
assert.equal(textBlocks(legacy).length, 0, 'legacy syntax must not be presented as a current copyable template');
assert.doesNotMatch(catalog, /需求登记:/);
});