318 lines
12 KiB
JavaScript
318 lines
12 KiB
JavaScript
const orderRegistry = require('./erp_order_registry');
|
||
const updateOrderParser = require('./erp_update_order_parser');
|
||
|
||
function registryPathFromOptions(options = {}) {
|
||
return options.registryPath || (options.config && options.config.paths && options.config.paths.orderRegistry) || '';
|
||
}
|
||
|
||
function findOrderRecord(identifier, options = {}) {
|
||
return orderRegistry.findOrderByIdentifier(identifier, {
|
||
registryPath: registryPathFromOptions(options) || undefined,
|
||
});
|
||
}
|
||
|
||
function isConfirmationType(value) {
|
||
const text = String(value || '').toLowerCase();
|
||
if (!text) return false;
|
||
if (/job|beian|备案|ob/.test(text)) return false;
|
||
return /confirm|confirmation|确认/.test(text);
|
||
}
|
||
|
||
function filterConfirmationSources(sources = []) {
|
||
return sources.filter((source) => isConfirmationType(source.type || source.key || source.label || source.path));
|
||
}
|
||
|
||
function requestedExportTypeSet(task = {}) {
|
||
const values = Array.isArray(task.exportTypes) && task.exportTypes.length
|
||
? task.exportTypes
|
||
: ['xingyou-confirm'];
|
||
return new Set(values.map((value) => String(value || '').toLowerCase()));
|
||
}
|
||
|
||
function sourceMatchesRequestedExport(source = {}, requested = new Set()) {
|
||
const text = String(source.type || source.key || source.label || source.path || '').toLowerCase();
|
||
if (/job|beian|teams_beian/.test(text)) return requested.has('job') || requested.has('job-order');
|
||
if (/liantai|orders_confirm_new|联泰/.test(text)) return requested.has('liantai') || requested.has('liantai-confirm');
|
||
if (/xingyou|orders_confirm_news|兴游|星游/.test(text)) {
|
||
return requested.has('xingyou') || requested.has('xingyou-confirm') || requested.has('confirmation') || requested.has('confirm');
|
||
}
|
||
return requested.has('confirmation') || requested.has('confirm');
|
||
}
|
||
|
||
function filterRequestedConfirmationSources(sources = [], task = {}) {
|
||
const requested = requestedExportTypeSet(task);
|
||
return filterConfirmationSources(sources)
|
||
.filter((source) => sourceMatchesRequestedExport(source, requested));
|
||
}
|
||
|
||
function identifierPayload(record = {}) {
|
||
if (record.childOrderNo) {
|
||
return {
|
||
parentGroupNo: record.parentGroupNo || '',
|
||
childOrderNo: record.childOrderNo,
|
||
};
|
||
}
|
||
if (record.parentGroupNo && !record.childOrderNo) {
|
||
return { parentGroupNo: record.parentGroupNo };
|
||
}
|
||
return { groupNo: record.groupNo || record.identifier || '' };
|
||
}
|
||
|
||
function planFromRecord(record = {}, task = {}) {
|
||
return {
|
||
taskId: task.taskId || `operation-${task.operation || 'task'}-${record.identifier || task.identifier || ''}`,
|
||
route: record.route || '',
|
||
orderNature: record.orderNature || '',
|
||
productName: record.product || '',
|
||
productRoute: record.product || '',
|
||
bookingCustomer: record.customer || '',
|
||
channelCustomer: record.customer || '',
|
||
departureDate: record.departureDate || '',
|
||
parentGroupNo: record.parentGroupNo || '',
|
||
delivery: {
|
||
customerReceivesPdfOnly: false,
|
||
sourceOnlyExport: true,
|
||
},
|
||
originalTask: task,
|
||
};
|
||
}
|
||
|
||
function submitResultFromRecord(record = {}) {
|
||
if (record.route === 'split_child') {
|
||
return {
|
||
parentGroupNo: record.parentGroupNo || '',
|
||
parentTid: record.parentTid || record.tid || '',
|
||
childOrderNo: record.childOrderNo || record.identifier || '',
|
||
childDid: record.childDid || record.did || record.ddid || '',
|
||
};
|
||
}
|
||
const group = {
|
||
date: record.departureDate || '',
|
||
groupNo: record.groupNo || record.identifier || '',
|
||
ddid: record.ddid || record.did || '',
|
||
tid: record.tid || record.ddid || record.did || '',
|
||
};
|
||
if (record.route === 'team_batch') {
|
||
return {
|
||
dateToGroupNo: group.date ? { [group.date]: group.groupNo } : {},
|
||
groups: [group],
|
||
};
|
||
}
|
||
return {
|
||
groupNo: group.groupNo,
|
||
group,
|
||
};
|
||
}
|
||
|
||
function resolveRouteOperations(route, options = {}) {
|
||
if (options.operationsByRoute && options.operationsByRoute[route]) {
|
||
return options.operationsByRoute[route];
|
||
}
|
||
const config = options.config || {};
|
||
if (route === 'team_single') {
|
||
return require('./erp_team_single_browser_operations').createTeamSingleOperations(config);
|
||
}
|
||
if (route === 'team_batch') {
|
||
return require('./erp_team_batch_browser_operations').createTeamBatchOperations(config);
|
||
}
|
||
if (route === 'split_child') {
|
||
return require('./erp_split_child_browser_operations').createSplitChildOperations(config);
|
||
}
|
||
return {};
|
||
}
|
||
|
||
function recordNotFoundResult(task = {}) {
|
||
const identifier = task.identifier || '';
|
||
return {
|
||
status: 'blocked',
|
||
reason: 'order_record_not_found',
|
||
identifiers: identifier ? { requested: identifier } : {},
|
||
customerMessage: `没有找到订单编号 ${identifier} 的本地索引记录。请确认使用的是系统返回的团号/子单号,或先补录该订单索引。`,
|
||
};
|
||
}
|
||
|
||
function operationMissingResult(task = {}, record = {}, missing = []) {
|
||
return {
|
||
status: 'blocked',
|
||
reason: 'export_confirmation_operations_missing',
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
missingOperations: missing,
|
||
customerMessage: `已找到订单 ${task.identifier},但 ${record.route || '未知路线'} 的确认件导出执行器还没有启用。请先确认 ERP 浏览器执行器配置。`,
|
||
};
|
||
}
|
||
|
||
function missingDepartureDateResult(task = {}, record = {}) {
|
||
const identifier = task.identifier || record.childOrderNo || record.identifier || '';
|
||
return {
|
||
status: 'blocked',
|
||
reason: 'order_record_missing_departure_date',
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
customerMessage: `已找到子单 ${identifier},但导出确认件还缺少出发日期。请补充“出发日期:YYYY-MM-DD”后重试,避免导出错单。`,
|
||
};
|
||
}
|
||
|
||
async function runExportDocuments(route, operations, plan, submitResult, options, task = {}) {
|
||
const exportOptions = {
|
||
...options,
|
||
exportTypes: Array.isArray(task.exportTypes) && task.exportTypes.length
|
||
? task.exportTypes
|
||
: ['xingyou-confirm'],
|
||
};
|
||
if (route === 'team_batch') {
|
||
return operations.exportDocuments(plan, submitResult, null, exportOptions);
|
||
}
|
||
if (route === 'split_child') {
|
||
return operations.exportDocuments(plan, submitResult, {}, exportOptions);
|
||
}
|
||
return operations.exportDocuments(plan, submitResult, exportOptions);
|
||
}
|
||
|
||
function formatExportMessage(record = {}, artifacts = []) {
|
||
const identifiers = identifierPayload(record);
|
||
const idLine = identifiers.childOrderNo
|
||
? `子单号:${identifiers.childOrderNo}`
|
||
: `团号:${identifiers.groupNo || identifiers.parentGroupNo || record.identifier}`;
|
||
const mediaLines = artifacts
|
||
.map((artifact) => artifact && (artifact.path || artifact.pdfPath || artifact.outputPath))
|
||
.filter(Boolean)
|
||
.map((filePath) => `MEDIA:"${filePath}"`);
|
||
return [
|
||
'确认件源文件已导出。',
|
||
idLine,
|
||
'',
|
||
...mediaLines,
|
||
].join('\n').trim();
|
||
}
|
||
|
||
function updateOperationMissingResult(task = {}, record = {}, missing = []) {
|
||
return {
|
||
status: 'blocked',
|
||
operation: 'update_order',
|
||
reason: 'update_order_operations_missing',
|
||
route: record.route || '',
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
missingOperations: missing,
|
||
customerMessage: `已找到订单 ${task.identifier}(${record.route || '未知路线'}),但该路线的修改执行器还没有接入。请先启用 ${record.route || '当前路线'} 的 updateOrder 操作后再执行修改。`,
|
||
};
|
||
}
|
||
|
||
function updatePlanNeedsClarificationResult(task = {}, record = {}, updatePlan = {}) {
|
||
return {
|
||
status: 'blocked',
|
||
operation: 'update_order',
|
||
reason: 'update_order_needs_clarification',
|
||
route: record.route || '',
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
missingFields: updatePlan.missingFields || ['修改内容'],
|
||
customerMessage: updatePlan.customerMessage || `已找到订单 ${task.identifier},但修改内容不够明确,请补充后再执行。`,
|
||
};
|
||
}
|
||
|
||
function formatUpdateMessage(record = {}, updateResult = {}) {
|
||
const identifiers = identifierPayload(record);
|
||
const id = identifiers.childOrderNo || identifiers.groupNo || identifiers.parentGroupNo || record.identifier || '';
|
||
const count = Array.isArray(updateResult.updatedActions) ? updateResult.updatedActions.length : 0;
|
||
if (record.route === 'split_child') {
|
||
const countText = count ? `,共执行 ${count} 项变更` : '';
|
||
return [
|
||
`已修改子单 ${id}${countText}。`,
|
||
'仅修改该子单,母团下其他历史子单未修改。',
|
||
].join('\n');
|
||
}
|
||
return count
|
||
? `订单 ${id} 已完成修改,共执行 ${count} 项变更。`
|
||
: `订单 ${id} 已完成修改。`;
|
||
}
|
||
|
||
async function executeExportConfirmation(task, options = {}) {
|
||
const record = findOrderRecord(task.identifier, options);
|
||
if (!record) return recordNotFoundResult(task);
|
||
if (record.route === 'split_parent') {
|
||
return {
|
||
status: 'blocked',
|
||
reason: 'split_parent_has_no_confirmation_export',
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
customerMessage: `已找到母团 ${task.identifier},但母团计划本身不导出确认件。请使用具体子单号导出客户确认件。`,
|
||
};
|
||
}
|
||
|
||
if (record.route === 'split_child' && !String(record.departureDate || '').trim()) {
|
||
return missingDepartureDateResult(task, record);
|
||
}
|
||
|
||
const operations = resolveRouteOperations(record.route, options);
|
||
const missing = ['exportDocuments'].filter((name) => typeof operations[name] !== 'function');
|
||
if (missing.length) return operationMissingResult(task, record, missing);
|
||
|
||
const plan = planFromRecord(record, task);
|
||
const submitResult = submitResultFromRecord(record);
|
||
const exportResult = await runExportDocuments(record.route, operations, plan, submitResult, options, task);
|
||
const confirmationSources = filterRequestedConfirmationSources(exportResult.sources || [], task);
|
||
if (!confirmationSources.length) {
|
||
return {
|
||
status: 'blocked',
|
||
reason: 'confirmation_sources_not_found',
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
stageResults: { export: exportResult },
|
||
customerMessage: `已找到订单 ${task.identifier},但导出结果里没有确认件文件,请检查 ERP 导出配置。`,
|
||
};
|
||
}
|
||
|
||
const filteredExportResult = { ...exportResult, sources: confirmationSources };
|
||
const artifacts = filterRequestedConfirmationSources(filteredExportResult.sources || [], task);
|
||
return {
|
||
status: 'completed',
|
||
operation: 'export_confirmation',
|
||
route: record.route,
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
artifacts,
|
||
stageResults: {
|
||
export: filteredExportResult,
|
||
},
|
||
customerMessage: formatExportMessage(record, artifacts),
|
||
};
|
||
}
|
||
|
||
async function executeUpdateOrder(task, options = {}) {
|
||
const record = findOrderRecord(task.identifier, options);
|
||
if (!record) return recordNotFoundResult(task);
|
||
const updatePlan = task.updatePlan || updateOrderParser.parseUpdateOrderText(task.updateText || '');
|
||
if (!updatePlan || updatePlan.status !== 'ready') {
|
||
return updatePlanNeedsClarificationResult(task, record, updatePlan || {});
|
||
}
|
||
const operations = resolveRouteOperations(record.route, options);
|
||
const missing = ['updateOrder'].filter((name) => typeof operations[name] !== 'function');
|
||
if (missing.length) return updateOperationMissingResult(task, record, missing);
|
||
|
||
const plan = planFromRecord(record, task);
|
||
const updateResult = await operations.updateOrder(plan, updatePlan, record, options);
|
||
return {
|
||
status: updateResult.status || 'completed',
|
||
operation: 'update_order',
|
||
route: record.route,
|
||
orderRecord: record,
|
||
identifiers: identifierPayload(record),
|
||
updatePlan,
|
||
stageResults: { update: updateResult },
|
||
customerMessage: record.route === 'split_child'
|
||
? formatUpdateMessage(record, updateResult)
|
||
: (updateResult.customerMessage || formatUpdateMessage(record, updateResult)),
|
||
};
|
||
}
|
||
|
||
module.exports = {
|
||
executeExportConfirmation,
|
||
executeUpdateOrder,
|
||
findOrderRecord,
|
||
filterConfirmationSources,
|
||
planFromRecord,
|
||
submitResultFromRecord,
|
||
};
|