导入问题修复

This commit is contained in:
andy committed 2026-09-15 11:34:59 +08:00
1 parent 0282c30a2a
commit ede1d47c47
26 files changed
+934 -45

No files matched your search

@@ -13,7 +13,7 @@ const ABSOLUTE_MAX_DATA_ROWS = 5_000;
const MAX_PASSENGER_SEQUENCE = 5_000;
const MAX_HEADER_ROW = 100;
export const PASSENGER_ROSTER_WORKBOOK_VERSION = 'ltjt-passenger-roster-workbook-v1.5.0';
export const PASSENGER_ROSTER_WORKBOOK_VERSION = 'ltjt-passenger-roster-workbook-v1.5.1';
const REQUIRED_SOURCE_FIELDS = [
'序号', '姓名', '英文姓名', '性别', '出生日期', '出生地', '护照号码',
@@ -36,12 +36,14 @@ const HEADER_FIELD_ENTRIES: ReadonlyArray<readonly [string, RequiredSourceField]
['性别', '性别'],
['出生日期', '出生日期'],
['出生地', '出生地'],
['出生地点', '出生地'],
['护照号码', '护照号码'],
['证件号码', '护照号码'],
['签发地', '签发地'],
['签发日期', '签发日期'],
['签发日', '签发日期'],
['有效期', '有效期'],
['有效期至', '有效期'],
['电话', '电话'],
['备注', '备注']
];
+81 -4
View File
@@ -1699,6 +1699,11 @@ function executionSummary(value: unknown): Record<string, unknown> {
no_erp_write: result.no_erp_write === true,
write_attempted: result.write_attempted === true,
execution_id: result.execution_id || '',
...(result.execution_phase === 'dispatch_uncertain' ? {
execution_phase: 'dispatch_uncertain',
reconciliation_required: result.reconciliation_required === true,
reconciliation_stale: result.reconciliation_stale === true
} : {}),
...(operationTiming ? { operation_timing: operationTiming } : {})
};
}
@@ -2351,6 +2356,58 @@ export function hasPrewriteNoErpEvidence(value: unknown): boolean {
&& ajaxRecords.every((record) => record.prevented_from_network === true || record.network_request_attempted === false);
}
/** Only a dispatch acknowledgement timeout may be closed by a late, proven
* read-only lookup failure. This is result ingestion, never permission to run
* the task again. The caller still enforces actor, task and execution access. */
export function canResolveDispatchNoWrite(input: {
taskStatus: string; attemptStatus: string; previous: unknown; incoming: unknown;
executionId: string; connectionId: string; claimedConnectionId: string;
}): boolean {
const previous = jsonObject(input.previous);
const result = jsonObject(input.incoming);
const report = jsonObject(result.report);
const preflight = jsonObject(report.preflight);
const timedOut = input.taskStatus === 'failed'
&& previous.reconciliation_required === true && previous.reconciliation_stale === true;
const sameExecution = Boolean(input.executionId)
&& text(previous.execution_id) === input.executionId && text(result.execution_id) === input.executionId;
const sameConnection = Boolean(input.connectionId)
&& input.connectionId === input.claimedConnectionId;
return (input.taskStatus === 'reconciliation_pending' || timedOut)
&& input.attemptStatus === 'reconciliation_pending' && sameExecution && sameConnection
&& text(previous.execution_phase) === 'dispatch_uncertain'
&& ['dispatch', 'reconciliation_timeout'].includes(text(previous.stage))
&& !previous.report && !previous.native_request && !previous.server_response
&& !previous.erp_receipt && !previous.success_receipt && !hasCompletionEvidence(previous)
&& text(result.status) === 'blocked' && text(result.stage) === 'erp_resolution'
&& text(result.failure_source) === 'plugin_executor'
&& result.no_erp_write === true && result.write_attempted === false && result.erp_write_started !== true
&& !/submit|write_started|erp_write/i.test(text(result.execution_phase))
&& text(report.status) === 'erp_resolution_blocked'
&& text(preflight.stage) === 'erp_readonly_resolution'
&& report.no_erp_write === true && report.write_attempted === false
&& preflight.no_erp_write === true && preflight.write_attempted === false
&& report.erp_write_started !== true && preflight.erp_write_started !== true
&& jsonObject(report.submit_safety).live_submit_attempted !== true
&& !report.native_request && !report.server_response && !report.requery
&& !result.native_request && !result.server_response && !result.requery
&& !result.erp_receipt && !result.success_receipt && !report.erp_receipt && !report.success_receipt
&& !hasCompletionEvidence(result)
&& Array.isArray(report.blockers) && report.blockers.length > 0
&& Array.isArray(report.side_effects)
&& report.side_effects.every((item) => ['no_procurement', 'no_payment', 'no_notification', 'no_external_send'].includes(text(item)))
&& (report.ajax_records == null || (Array.isArray(report.ajax_records) && report.ajax_records.length === 0));
}
export function shouldIgnoreDispatchTimeout(previousValue: unknown, incomingValue: unknown): boolean {
const previous = jsonObject(previousValue);
const incoming = jsonObject(incomingValue);
return text(incoming.execution_phase) === 'dispatch_uncertain' && text(incoming.stage) === 'dispatch'
&& Boolean(text(incoming.execution_id)) && text(previous.execution_id) === text(incoming.execution_id)
&& Boolean(text(previous.stage)) && text(previous.stage) !== 'dispatch'
&& ['accepted', 'running', 'completed', 'blocked', 'failed', 'cancelled', 'dry_run', 'reconciliation_pending'].includes(text(previous.status));
}
function publicShadowDifference(row: Record<string, unknown>, config: AppConfig): PublicParserShadowDifference {
const programCandidate = decryptedJson(config, row.program_result_ciphertext);
const aiCandidate = decryptedJson(config, row.ai_result_ciphertext);
@@ -6505,6 +6562,10 @@ export class TaskService {
const previousExecutionResult = jsonObject(
decryptedJson(this.config, row.execution_result_ciphertext) || row.execution_result
);
if (text(jsonObject(attemptRow.details).connection_id) === connectionId
&& shouldIgnoreDispatchTimeout(previousExecutionResult, preparedResult)) {
return { row, event: null };
}
let resultObject = preparedResult;
if (prepared.required && !prepared.failureCode) {
const stored: StoredTaskArtifact[] = [];
@@ -6536,6 +6597,23 @@ export class TaskService {
const stage = text(resultObject.stage || 'erp').slice(0, 160);
if (rawStatus !== status) resultObject.result_status = rawStatus;
resultObject.status = status;
const attemptDetails = jsonObject(attemptRow.details);
const canFinalizeDispatchNoWrite = canResolveDispatchNoWrite({
taskStatus: text(row.status), attemptStatus: text(attemptRow.status),
previous: previousExecutionResult, incoming: resultObject, executionId,
connectionId, claimedConnectionId: text(attemptDetails.connection_id)
});
const repeatsResolvedDispatchResult = text(row.status) === 'blocked'
&& previousExecutionResult.reconciliation_resolution === 'late_dispatch_readonly_failure'
&& text(previousExecutionResult.execution_id) === executionId
&& text(attemptDetails.connection_id) === connectionId;
// Restore server-derived metadata for an exact duplicate's hash. Any
// changed result still fails the immutable-state check below.
if (canFinalizeDispatchNoWrite || repeatsResolvedDispatchResult) {
resultObject.reconciliation_resolved = true;
resultObject.reconciliation_required = false;
resultObject.reconciliation_resolution = 'late_dispatch_readonly_failure';
}
const lifecycle = executionLifecycleFacts(resultObject, status, true);
Object.assign(resultObject, lifecycle);
const message = text(resultObject.message || (uncertain ? 'ERP 结果不确定,请勿重复提交同一任务,等待任务所属账号只读核验。' : '插件已返回状态。')).slice(0, 2_000);
@@ -6550,7 +6628,6 @@ export class TaskService {
if (text(attemptRow.response_hash) === resultHash) {
return { row, event: null };
}
const attemptDetails = jsonObject(attemptRow.details);
const sameConnection = !text(attemptDetails.connection_id) || text(attemptDetails.connection_id) === connectionId;
const failedReconciliation = text(row.status) === 'failed'
&& previousExecutionResult.reconciliation_required === true;
@@ -6569,7 +6646,7 @@ export class TaskService {
&& hasPrewriteNoErpEvidence(previousExecutionResult)
&& hasPrewriteNoErpEvidence(resultObject)
&& sameConnection;
const canFinalizeTerminalReconciliation = canFinalizeReconciliation || canFinalizeNoWriteReconciliation;
const canFinalizeTerminalReconciliation = canFinalizeReconciliation || canFinalizeNoWriteReconciliation || canFinalizeDispatchNoWrite;
if (executionStatusImmutable(text(row.status)) && !canFinalizeTerminalReconciliation) {
throw new TaskError('result_after_terminal', `任务已处于不可变状态 ${row.status},迟到结果已忽略。`);
}
@@ -6652,7 +6729,7 @@ export class TaskService {
execution_phase: text(resultObject.execution_phase)
}
}, context.userId);
await this.audit(client, context, canFinalizeNoWriteReconciliation
await this.audit(client, context, canFinalizeNoWriteReconciliation || canFinalizeDispatchNoWrite
? 'task.reconciliation_no_write_resolved'
: canFinalizeReconciliation
? 'task.reconciliation_resolved'
@@ -6662,7 +6739,7 @@ export class TaskService {
connection_id: connectionId,
terminal,
reconciliation_resolved: canFinalizeTerminalReconciliation,
no_erp_write_resolution: canFinalizeNoWriteReconciliation
no_erp_write_resolution: canFinalizeNoWriteReconciliation || canFinalizeDispatchNoWrite
});
return { row: updatedRow, event };
});