feat: preserve stereo channels and require reviewed speaker roles

This commit is contained in:
2026-10-08 16:15:41 +08:00
parent 13948f5982
commit a20d3565af
28 changed files with 1134 additions and 113 deletions
@@ -40,6 +40,11 @@ final class FollowupAudioApply
FollowupAudioAccess::diagnosis((int) $task['diagnosis_id'], $actor, $info);
$review = FollowupAudioStore::open($taskId, 'review', $task['review_cipher']);
$source = FollowupAudioStore::open($taskId, 'extraction', $task['extraction_cipher']);
$stereo = FollowupAudioStore::hasStereo($source);
$channelRoles = FollowupAudioStore::channelRoles($source, $review);
if ($stereo && $channelRoles === 'unconfirmed') { throw new DomainException('FOLLOWUP_AUDIO_CHANNEL_ROLES_REQUIRED'); }
$roleAnnotation = $stereo ? ['channel_roles' => $channelRoles,
'channel_roles_annotation' => $review['channel_roles_annotation'] ?? []] : [];
$merged = self::mergeItems($review['items'], $items, $source['items']);
$selected = array_values(array_filter($merged, static fn (array $item): bool => $item['selected']));
if ($selected === []) { throw new DomainException('FOLLOWUP_AUDIO_NOTHING_SELECTED'); }
@@ -124,7 +129,7 @@ final class FollowupAudioApply
'time_estimated' => $item['time_estimated'], 'values' => $item['values']];
// Full transcript is NOT copied here. Only adopted item's necessary evidence survives 90-day cleanup.
$evidence = array_intersect_key($sourceById[$item['id']], array_flip(['kind', 'values', 'record_date', 'record_time',
'date_text', 'time_text', 'time_period', 'time_estimated', 'evidence']));
'date_text', 'time_text', 'time_period', 'time_estimated', 'evidence'])) + $roleAnnotation;
Db::name('followup_audio_audit')->insert([
'task_id' => $taskId, 'item_id' => $item['id'], 'diagnosis_id' => (int) $task['diagnosis_id'], 'actor_id' => $actor,
'kind' => $kind, 'table_name' => $table, 'record_id' => $targetId, 'action' => $action,
@@ -139,7 +144,7 @@ final class FollowupAudioApply
}
Db::name('followup_audio_task')->where('id', $taskId)->update([
'status' => 'applied', 'stage' => 'applied', 'version' => (int) $task['version'] + 1,
'applied_cipher' => FollowupAudioStore::seal($taskId, 'applied', ['items' => $applied]),
'applied_cipher' => FollowupAudioStore::seal($taskId, 'applied', ['items' => $applied] + $roleAnnotation),
'applied_at' => time(), 'updated_at' => time(),
]);
return ['id' => $taskId, 'status' => 'applied', 'applied_items' => $applied];