import 'dart:convert'; import 'package:hgdj/hj_model/home/collection_model.dart'; import 'package:hgdj/hj_model/video_model.dart'; import 'package:hgdj/hj_utils/const.dart'; import 'package:hgdj/tools_base/cache/history/base_history_record_store.dart'; import 'package:hgdj/tools_base/cache/history/cartoon_history_store.dart'; import 'package:hgdj/tools_base/cache/history/comics_history_store.dart'; import 'package:hgdj/tools_base/cache/history/community_history_store.dart'; import 'package:hgdj/tools_base/cache/history/game_history_store.dart'; import 'package:hgdj/tools_base/cache/history/novel_history_store.dart'; import 'package:hgdj/tools_base/cache/history/pic_history_store.dart'; import 'package:hgdj/tools_base/cache/history/search_history_store.dart'; import 'package:hgdj/tools_base/cache/history/short_video_history_store.dart'; import 'package:hgdj/tools_base/cache/history/video_history_store.dart'; import 'package:hgdj/tools_base/debug_log.dart'; import '../hj_model/cartoon_media_info.dart'; /// 浏览历史 + 搜索历史的统一入口。按 [MediaStyle] 派发到各自的 store,业务层不直接碰 store class HistoryUtil { /// 每种类型独占一个 db 文件,store 自身是单例。返回 null = 该类型不记历史 static BaseHistoryRecordStore? _storeOf(MediaStyle type) => switch (type) { MediaStyle.Video => VideoHistoryStore(), MediaStyle.ShortVideo => ShortVideoHistoryStore(), MediaStyle.Comics => ComicsHistoryStore(), MediaStyle.Cartoon => CartoonHistoryStore(), MediaStyle.Novel => NovelHistoryStore(), MediaStyle.Community => CommunityHistoryStore(), MediaStyle.Pic => PicHistoryStore(), MediaStyle.Game => GameHistoryStore(), // 短剧的观看历史和续播位置是同一张表,走 DramaResumeStore;其余是当前业务无浏览历史需求的类型 MediaStyle.Drama || MediaStyle.Area || MediaStyle.Seed || MediaStyle.Actress || MediaStyle.NakedChat || MediaStyle.GroupChat => null, }; /// 插入浏览历史(已存在则刷新到最前;超上限自动删最旧,上限见 BaseHistoryRecordStore.maxRows) static Future insert(Object? model, MediaStyle type) async { try { final json = _toJson(model); if (json == null) { //类型没在 _toJson/_idOf 里登记 → 静默不记历史。入参是 Object? 编译期拦不住,只能靠日志暴露 debugLog('HistoryUtil.insert 未登记的类型', model?.runtimeType); return; } final id = _idOf(model); final store = _storeOf(type); if (id == null || id.isEmpty || store == null) return; await store.save(modelId: id, modelDataJson: jsonEncode(json)); } catch (e) { debugLog('HistoryUtil.insert', e); } } /// 删除单条浏览历史 static Future delete(Object? model, MediaStyle type) async { try { final id = _idOf(model); if (id == null) { debugLog('HistoryUtil.delete 未登记的类型', model?.runtimeType); return; } final store = _storeOf(type); if (id.isEmpty || store == null) return; await store.remove(id); } catch (e) { debugLog('HistoryUtil.delete', e); } } /// 指定类型的浏览历史(按时间倒序,分页)。解析不出的条目直接丢弃,不让一条脏数据废掉整页 static Future> fetch(MediaStyle type, {int page = 1, int pageSize = 20}) async { try { final store = _storeOf(type); if (store == null) return []; final jsonList = await store.fetch(page: page, pageSize: pageSize); return jsonList.map((e) => _fromJson(e)).whereType().toList(); } catch (e) { debugLog('HistoryUtil.fetch', e); return []; } } /// 三个 _xxx 按实例匹配,新增可记历史的 model 时这里和 [_fromJson] 一起加一行 static String? _idOf(Object? model) => switch (model) { VideoModel m => m.id, CartoonMediaInfo m => m.id, CollectionModel m => m.id, _ => null, }; static Map? _toJson(Object? model) => switch (model) { VideoModel m => m.toJson(), CartoonMediaInfo m => m.toJson(), CollectionModel m => m.toJson(), _ => null, }; /// 反序列化只能按 [T] 分派(拿不到实例)。**必须用 T == 类型比较**: /// 靠 T.toString() 对类名在 release 混淆后必然失配,表现是列表静默空白 static T? _fromJson(String raw) { final json = Map.from(jsonDecode(raw)); if (T == VideoModel) return VideoModel.fromJson(json) as T; if (T == CartoonMediaInfo) return CartoonMediaInfo.fromJson(json) as T; if (T == CollectionModel) return CollectionModel.fromJson(json) as T; return null; } //搜索历史(按时间倒序) static Future> searchHistories() => SearchHistoryStore().fetchAll(); //新增一条搜索历史(已存在则去重提到最前,超上限自动删最旧) static Future addSearch(String keyword) => SearchHistoryStore().save(keyword); //清空搜索历史 static Future clearSearch() => SearchHistoryStore().clean(); }