Files
zyt/artifacts/ai_no_answer_fix_20261027/TcmClinicalFactStateGuard.php.patch
2026-10-27 14:03:35 +08:00

92 lines
5.5 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- server/app/adminapi/service/TcmClinicalFactStateGuard.php.before
+++ server/app/adminapi/service/TcmClinicalFactStateGuard.php
@@ -468,9 +468,10 @@
$evidenceView = preg_replace('/^\s*(?:(?:主要|具体|直接)(?:依据|根据)\s*[::]?|依据为)\s*/u', '', $assertionView) ?? $assertionView;
$referenceView = self::sourceReferenceProse($ledger,
self::negatedInferenceProse(self::inferenceHeadProse($evidenceView)), $invalid);
+ $patientReviewView = self::patientReviewQuestionProse($referenceView);
$pendingHead = preg_match('/^\s*适用前(?:需|须|应)(?:核实|复核|确认)[::]/u', $referenceView) === 1;
$questionView = ($question || $pendingHead || $pendingQuestion)
- ? self::terminatedQuestionProse($referenceView) : $referenceView;
+ ? self::terminatedQuestionProse($patientReviewView) : $patientReviewView;
preg_match('/^[。;;!?!?\r\n]+/u', $terminator, $separator);
// Only the immediately preceding fully neutral question can
// supply the antecedent of this closed contingency operator.
@@ -1145,6 +1146,19 @@
return implode('', $pieces);
}
+ /** A requested patient review owns its bounded whether-object, not its tail. */
+ private static function patientReviewQuestionProse(string $clause): string
+ {
+ if (!preg_match('/^\s*(?:(?:仍|还)?(?:需|须|应|请)(?:进一步|再次|逐项)?)?'
+ . '(?:补问|询问|核实|复核|核对|追问|确认|查明)[::\s]*(?:患者|本例)[::\s]*'
+ . '((?:是否|有无).+)$/uD', $clause, $request)) return $clause;
+ // Reuse the existing neutral-object grammar and assertion boundaries.
+ // A refused object keeps its original action/current/record markers;
+ // only a proved question may remove this collection instruction.
+ $view = self::terminatedQuestionProse($request[1]);
+ return $view !== $request[1] ? $view : $clause;
+ }
+
/** A modal + collection action governs only its own neutral object list. */
private static function collectionRequestProse(string $clause): string
{
@@ -1624,7 +1638,7 @@
foreach ($pieces as $piece) array_push($issues, ...self::fieldIssues($ledger, $piece, $pendingFields));
return $issues;
}
- $text = trim($clause, " \t#>*-");
+ $text = self::unwrappedFieldUnknown(trim($clause, " \t#>*-"));
$carried = $pendingFields;
$pendingFields = [];
foreach (self::FIELD_GROUPS as $group => $definition) {
@@ -1636,6 +1650,13 @@
$dataGap = self::dataGapPart($text);
foreach (self::FIELD_GROUPS as $group => $definition) {
$state = $ledger['field_states'][$group] ?? ['state' => 'unknown', 'source_id' => 'report', 'date' => ''];
+ $aliases = implode('|', array_map(static fn (string $alias): string => preg_quote($alias, '/'), $definition['aliases']));
+ $value = null;
+ if (preg_match('/(?:' . $aliases . ')[::]\s*(.+)$/uD', $text, $match)) $value = $match[1];
+ elseif (in_array($group, $carried, true)) $value = $text;
+ $unwrapped = $value !== null ? self::unwrappedFieldUnknown($value) : null;
+ $wrappedUnknown = $unwrapped !== $value;
+ $value = $unwrapped;
if ($state['state'] !== 'unknown') {
// A missing named examination is different from a missing
// examination image; it cannot erase an existing result.
@@ -1648,15 +1669,14 @@
|| ($dataGap !== null && $dataGap['topic'] === ''))) {
$issues[] = ['code' => 'RECORDED_TO_UNKNOWN', 'source_id' => $state['source_id']];
}
+ if ($wrappedUnknown && self::sameTime($ledger['sources'][$state['source_id']] ?? [], self::scope($text, 'current'), $text)) {
+ $issues[] = ['code' => 'RECORDED_TO_UNKNOWN', 'source_id' => $state['source_id']];
+ }
continue;
}
if ($dataGap !== null) continue;
$dates = self::calendarDates($text);
if ($dates !== [] && $state['date'] !== '' && !in_array($state['date'], $dates, true)) continue;
- $aliases = implode('|', array_map(static fn (string $alias): string => preg_quote($alias, '/'), $definition['aliases']));
- $value = null;
- if (preg_match('/(?:' . $aliases . ')[::]\s*(.+)$/uD', $text, $match)) $value = $match[1];
- elseif (in_array($group, $carried, true)) $value = $text;
if ($value !== null && (preg_match('/^(?:' . self::UNKNOWN . '|未填|待补)/u', $value)
|| self::conditional($value) || self::evidenceOnly($value))) continue;
$absent = preg_match('/(?:' . self::OBSERVED_ABSENT . ')[^,,::]{0,30}(?:' . $aliases . ')/u', $text)
@@ -1669,6 +1689,14 @@
}
}
return $issues;
+ }
+
+ /** Paired punctuation around one unknown value is formatting, never a finding. */
+ private static function unwrappedFieldUnknown(string $value): string
+ {
+ if (preg_match('/^\s*(?:(\s*(' . self::UNKNOWN . ')\s*)|\(\s*(' . self::UNKNOWN . ')\s*\))\s*$/uD',
+ $value, $wrapped)) return ($wrapped[1] ?? '') !== '' ? $wrapped[1] : $wrapped[2];
+ return $value;
}
/** Explicit lexical polarity with inherited scope across enumerated items. */