From 43bf4b9608408e1d3482f556c584a673f0688035 Mon Sep 17 00:00:00 2001 From: fermatmind <17551046983@163.com> Date: Wed, 1 Jul 2026 23:23:25 +0800 Subject: [PATCH] fix(personality): suppress duplicate V8.5 legacy sections --- .../API/V0_5/Cms/PersonalityController.php | 34 +++++++++++- ...ityMbti64CmsRevisionPromoteCommandTest.php | 55 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/backend/app/Http/Controllers/API/V0_5/Cms/PersonalityController.php b/backend/app/Http/Controllers/API/V0_5/Cms/PersonalityController.php index 0561d81cf..29af70bc4 100644 --- a/backend/app/Http/Controllers/API/V0_5/Cms/PersonalityController.php +++ b/backend/app/Http/Controllers/API/V0_5/Cms/PersonalityController.php @@ -29,13 +29,22 @@ class PersonalityController extends Controller private const MBTI64_V85_SECTION_PREFIX = 'v8_5_'; private const MBTI64_V85_DUPLICATE_LEGACY_SECTION_KEYS = [ + 'quick_answer', 'meaning', 'a_t_difference', 'core_traits', 'careers_work_style', 'relationships_communication', 'strengths_blind_spots', + 'common_misreads', 'similar_types', + 'faq', + ]; + + private const MBTI64_V85_DUPLICATE_LEGACY_SECTION_PREFIXES = [ + 'career.', + 'growth.', + 'relationships.', ]; use RespondsWithNotFound; @@ -1636,7 +1645,15 @@ private function publicSectionPayloads(PersonalityProfile $profile, ?Personality } if ($this->hasV85FirstClassSections($sections->keys()->all())) { - foreach (self::MBTI64_V85_DUPLICATE_LEGACY_SECTION_KEYS as $sectionKey) { + foreach ($sections->keys()->all() as $sectionKey) { + if (! is_string($sectionKey)) { + continue; + } + + if (! $this->shouldSuppressV85DuplicateSection($sectionKey)) { + continue; + } + $sections->forget($sectionKey); } } @@ -1664,6 +1681,21 @@ private function hasV85FirstClassSections(array $sectionKeys): bool return false; } + private function shouldSuppressV85DuplicateSection(string $sectionKey): bool + { + if (in_array($sectionKey, self::MBTI64_V85_DUPLICATE_LEGACY_SECTION_KEYS, true)) { + return true; + } + + foreach (self::MBTI64_V85_DUPLICATE_LEGACY_SECTION_PREFIXES as $prefix) { + if (str_starts_with($sectionKey, $prefix)) { + return true; + } + } + + return false; + } + private function sanitizeV85PublicBody(string $sectionKey, mixed $body): mixed { if (! str_starts_with($sectionKey, 'v8_5_module_') || ! is_string($body)) { diff --git a/backend/tests/Feature/Console/PersonalityMbti64CmsRevisionPromoteCommandTest.php b/backend/tests/Feature/Console/PersonalityMbti64CmsRevisionPromoteCommandTest.php index 8d34583f2..8aee7ef94 100644 --- a/backend/tests/Feature/Console/PersonalityMbti64CmsRevisionPromoteCommandTest.php +++ b/backend/tests/Feature/Console/PersonalityMbti64CmsRevisionPromoteCommandTest.php @@ -836,6 +836,19 @@ public function test_write_promotes_only_fixed_v8_5_v5_bilingual_sixty_four_subs foreach ($this->expectedV85V5FirstClassSectionKeys() as $legacySectionKey) { $this->assertNotContains($legacySectionKey, $sectionKeys, $legacySectionKey); } + foreach ($this->expectedV85V5SuppressedPublicSectionKeys() as $legacySectionKey) { + $this->assertNotContains($legacySectionKey, $sectionKeys, $legacySectionKey); + } + foreach ($sectionKeys as $sectionKey) { + $this->assertFalse(str_starts_with($sectionKey, 'career.'), $sectionKey); + $this->assertFalse(str_starts_with($sectionKey, 'growth.'), $sectionKey); + $this->assertFalse(str_starts_with($sectionKey, 'relationships.'), $sectionKey); + } + + $this->assertContains('related_content', $sectionKeys); + $this->assertContains('mbti64_promotion_metadata', $sectionKeys); + $this->assertNoExactDuplicateVisibleApiParagraphs((array) ($detail['sections'] ?? [])); + $apiModule = collect((array) ($detail['sections'] ?? [])) ->firstWhere('section_key', 'v8_5_module_01_core_reading'); $this->assertIsArray($apiModule); @@ -2023,6 +2036,48 @@ private function expectedV85V5FirstClassSectionKeys(): array ]; } + /** + * @return list + */ + private function expectedV85V5SuppressedPublicSectionKeys(): array + { + return [ + 'quick_answer', + 'common_misreads', + 'faq', + ]; + } + + /** + * @param array> $sections + */ + private function assertNoExactDuplicateVisibleApiParagraphs(array $sections): void + { + $seen = []; + foreach ($sections as $section) { + $sectionKey = (string) ($section['section_key'] ?? ''); + $body = (string) ($section['body_md'] ?? ''); + foreach (preg_split('/\R{2,}/u', $body) ?: [] as $index => $paragraph) { + $normalized = trim((string) preg_replace('/\s+/u', ' ', $paragraph)); + if (mb_strlen($normalized) < 18) { + continue; + } + + $this->assertArrayNotHasKey( + $normalized, + $seen, + sprintf( + 'Duplicate visible API paragraph in %s[%d]; first seen at %s.', + $sectionKey, + $index, + $seen[$normalized] ?? 'unknown' + ) + ); + $seen[$normalized] = sprintf('%s[%d]', $sectionKey, $index); + } + } + } + /** * @return list */