92 lines
5.5 KiB
Diff
92 lines
5.5 KiB
Diff
--- 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. */
|