From 0c1b9c04169827c1fb62000d2da8766c6eea95bd Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 9 Aug 2026 11:25:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AE=A2=E9=98=85=E5=B9=B4=E4=BB=BD?= =?UTF-8?q?=E5=86=99=E5=BA=93=E5=89=8D=E8=BD=AC=E4=B8=BA=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=EF=BC=8C=E9=81=BF=E5=85=8D=E9=9F=B3=E4=B9=90=E8=AE=A2?= =?UTF-8?q?=E9=98=85=E6=97=B6=E6=8A=A5=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 音乐识别链路返回的年份是数字,而订阅表 year 列为字符串, 直接写入会导致 PostgreSQL 严格类型检查失败(expected str, got int)。 在 SubscribeOper 写库前统一做字符串归一化,并补充回归测试。 --- app/db/subscribe_oper.py | 14 ++++++++++++-- tests/test_subscribe_oper.py | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/db/subscribe_oper.py b/app/db/subscribe_oper.py index 0c3a3bd9c..d0b7f4794 100644 --- a/app/db/subscribe_oper.py +++ b/app/db/subscribe_oper.py @@ -21,6 +21,16 @@ def _normalize_integer_flags(payload: dict, fields: Tuple[str, ...] = INTEGER_FL return normalized_payload +def _normalize_year(year: Optional[int | str]) -> Optional[str]: + """ + 订阅表的 year 列为字符串类型,而识别链路的媒体年份可能是数字 + (音乐等来源),写库前统一转换为字符串避免数据库类型错误。 + """ + if year is None: + return None + return str(year) + + class SubscribeOper(DbOper): """ 订阅管理 @@ -55,7 +65,7 @@ class SubscribeOper(DbOper): subscribe = Subscribe.exists(self._db, **identity_params) kwargs.update({ "name": mediainfo.title, - "year": mediainfo.year, + "year": _normalize_year(mediainfo.year), "type": mediainfo.type.value, "tmdbid": mediainfo.tmdb_id, "imdbid": mediainfo.imdb_id, @@ -117,7 +127,7 @@ class SubscribeOper(DbOper): subscribe = await Subscribe.async_exists(self._db, **identity_params) kwargs.update({ "name": mediainfo.title, - "year": mediainfo.year, + "year": _normalize_year(mediainfo.year), "type": mediainfo.type.value, "tmdbid": mediainfo.tmdb_id, "imdbid": mediainfo.imdb_id, diff --git a/tests/test_subscribe_oper.py b/tests/test_subscribe_oper.py index 4a789aa5d..9e3731563 100644 --- a/tests/test_subscribe_oper.py +++ b/tests/test_subscribe_oper.py @@ -120,6 +120,29 @@ def test_music_subscribe_persists_release_cover_as_poster_and_backdrop(): assert payload["backdrop"] == media.cover_url +def test_music_subscribe_persists_numeric_year_as_string(): + """音乐识别链路的年份可能是数字,写库前必须转字符串避免 PostgreSQL 类型错误。""" + persisted = SimpleNamespace(id=93) + created = SimpleNamespace(create=MagicMock()) + media = MusicInfo( + source="musicbrainz", + media_id="2af54891-e954-40e8-8b90-b7e98c740f21", + title="心愿", + year=2025, + cover_url="https://coverartarchive.org/release/example/front-500", + ) + + with patch("app.db.subscribe_oper.Subscribe") as subscribe_model: + subscribe_model.exists.side_effect = [None, persisted] + subscribe_model.return_value = created + + sid, _ = SubscribeOper(db=object()).add(mediainfo=media, season=None) + + assert sid == 93 + payload = subscribe_model.call_args.kwargs + assert payload["year"] == "2025" + + @pytest.mark.parametrize("episode_group", [None, "eg-1"]) def test_async_add_scopes_duplicate_lookup_by_episode_group(episode_group): """异步新增与同步路径使用相同的剧集组身份契约。"""