Ver Fonte

fix: correct SQLite sync order and metadata handling

Fixes:
- [HIGH] Move _sync_to_sqlite() BEFORE clearing _pending_* queues
  (was being called after .clear(), reading empty data)
- [MED] Separate metadata fields from current_json updates:
  - METADATA_FIELDS (canonical_name/tier/desc/is_protagonist/is_archived)
    now use upsert_entity(update_metadata=True)
  - Only current_updates go to update_entity_current()
- [MED/LOW] Add record_appearance() call when updating entity appearance
  (fixes missing entries in appearances table)
lingfengQAQ há 5 meses atrás
pai
commit
460dc57f23
1 ficheiros alterados com 59 adições e 13 exclusões
  1. 59 13
      .claude/scripts/data_modules/state_manager.py

+ 59 - 13
.claude/scripts/data_modules/state_manager.py

@@ -457,6 +457,9 @@ class StateManager:
                 # 原子写入(锁已持有,不再二次加锁)
                 atomic_write_json(self.config.state_file, disk_state, use_lock=False, backup=True)
 
+                # v5.1: 同步到 SQLite(必须在清空 pending 之前调用)
+                self._sync_to_sqlite()
+
                 # 同步内存为磁盘最新快照,并清空增量队列
                 self._state = disk_state
                 self._pending_entity_patches.clear()
@@ -468,9 +471,6 @@ class StateManager:
                 self._pending_progress_chapter = None
                 self._pending_progress_words_delta = 0
 
-                # v5.1: 同步到 SQLite
-                self._sync_to_sqlite()
-
         except filelock.Timeout:
             raise RuntimeError("无法获取 state.json 文件锁,请稍后重试")
 
@@ -507,8 +507,12 @@ class StateManager:
         if not self._sql_state_manager:
             return
 
+        # 元数据字段(不应写入 current_json)
+        METADATA_FIELDS = {"canonical_name", "tier", "desc", "is_protagonist", "is_archived"}
+
         try:
             from .sql_state_manager import EntityData
+            from .index_manager import EntityMeta
 
             # 同步实体补丁
             for (entity_type, entity_id), patch in self._pending_entity_patches.items():
@@ -527,17 +531,59 @@ class StateManager:
                         is_protagonist=patch.base_entity.get("is_protagonist", False)
                     )
                     self._sql_state_manager.upsert_entity(entity_data)
-                elif patch.current_updates or patch.top_updates:
+
+                    # 记录首次出场
+                    if patch.appearance_chapter is not None:
+                        self._sql_state_manager._index_manager.record_appearance(
+                            entity_id=entity_id,
+                            chapter=patch.appearance_chapter,
+                            mentions=[entity_data.name],
+                            confidence=1.0
+                        )
+                else:
                     # 更新现有实体
-                    updates = {**patch.top_updates}
-                    if patch.current_updates:
-                        updates.update(patch.current_updates)
-                    if updates:
-                        self._sql_state_manager.update_entity_current(entity_id, updates)
-
-                # 更新 last_appearance
-                if patch.appearance_chapter is not None:
-                    self._sql_state_manager._update_last_appearance(entity_id, patch.appearance_chapter)
+                    has_metadata_updates = bool(patch.top_updates and
+                                                 any(k in METADATA_FIELDS for k in patch.top_updates))
+
+                    if has_metadata_updates:
+                        # 有元数据更新:使用 upsert_entity(update_metadata=True)
+                        existing = self._sql_state_manager.get_entity(entity_id)
+                        if existing:
+                            # 合并 current_updates
+                            current = existing.get("current_json", {})
+                            if isinstance(current, str):
+                                import json
+                                current = json.loads(current) if current else {}
+                            if patch.current_updates:
+                                current.update(patch.current_updates)
+
+                            entity_meta = EntityMeta(
+                                id=entity_id,
+                                type=existing.get("type", entity_type),
+                                canonical_name=patch.top_updates.get("canonical_name", existing.get("canonical_name", "")),
+                                tier=patch.top_updates.get("tier", existing.get("tier", "装饰")),
+                                desc=patch.top_updates.get("desc", existing.get("desc", "")),
+                                current=current,
+                                first_appearance=existing.get("first_appearance", 0),
+                                last_appearance=patch.appearance_chapter or existing.get("last_appearance", 0),
+                                is_protagonist=patch.top_updates.get("is_protagonist", existing.get("is_protagonist", False)),
+                                is_archived=patch.top_updates.get("is_archived", existing.get("is_archived", False))
+                            )
+                            self._sql_state_manager._index_manager.upsert_entity(entity_meta, update_metadata=True)
+                    elif patch.current_updates:
+                        # 只有 current 更新
+                        self._sql_state_manager.update_entity_current(entity_id, patch.current_updates)
+
+                    # 更新 last_appearance 并记录出场
+                    if patch.appearance_chapter is not None:
+                        self._sql_state_manager._update_last_appearance(entity_id, patch.appearance_chapter)
+                        # 补充 appearances 记录
+                        self._sql_state_manager._index_manager.record_appearance(
+                            entity_id=entity_id,
+                            chapter=patch.appearance_chapter,
+                            mentions=[],
+                            confidence=1.0
+                        )
 
             # 同步别名
             for alias, entries in self._pending_alias_entries.items():