You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

94 lines
2.9 KiB

import 'dart:convert';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../../core/utils/logger.dart';
import '../models/emai_message.dart';
/// EMAI 助手的对话记录,手机页面和耳机会话**共用同一份**。
///
/// 之所以要独立成一个常驻服务而不是挂在页面控制器上:耳机按键唤醒的对话
/// 发生时页面很可能根本没打开(人在路上、手机在兜里)。用户要求这些内容
/// 在打开界面时也能看到,所以记录必须活在页面之外。
///
/// 落盘用 GetStorage,只保留最近 [_maxKeep] 条。
///
/// ⚠️ 注意语义:这里存的是**展示用的聊天记录**,不是模型上下文。
/// 百炼的上下文按 `dialog_id` 维护在服务端,每次重新连接就是一轮新对话——
/// 也就是说界面上能看到旧消息,但模型并不记得它们。
class EmaiChatStore extends GetxService {
static const String _tag = 'EmaiChatStore';
static EmaiChatStore get to => Get.find<EmaiChatStore>();
static const String _storageKey = 'emai_chat_history';
static const int _maxKeep = 200;
final GetStorage _storage = GetStorage();
final messages = <EmaiMessage>[].obs;
@override
void onInit() {
super.onInit();
_load();
}
void _load() {
try {
final raw = _storage.read<String>(_storageKey);
if (raw == null || raw.isEmpty) return;
final list = json.decode(raw) as List;
messages
.assignAll(list.map((e) => EmaiMessage.fromJson(e as Map)).toList());
Logger.i(_tag, '恢复历史对话 ${messages.length} 条');
} catch (e) {
Logger.w(_tag, '历史对话读取失败,丢弃: $e');
_storage.remove(_storageKey);
}
}
/// 落盘。流式过程中每帧都写太浪费,只在一句话收完时调。
void persist() {
try {
final keep = messages.length > _maxKeep
? messages.sublist(messages.length - _maxKeep)
: messages;
_storage.write(
_storageKey, json.encode(keep.map((m) => m.toJson()).toList()));
} catch (e) {
Logger.w(_tag, '历史对话落盘失败: $e');
}
}
EmaiMessage append(EmaiMessage m) {
messages.add(m);
return m;
}
/// 流式文本一律整体替换,不做拼接——理由见 EmaiController._applyText
void applyText(EmaiMessage m, String incoming) {
if (incoming.isEmpty) return;
m.text = incoming;
messages.refresh();
}
/// 一个字都没收到的流式气泡:从列表里撤掉,别留白气泡
void dropIfEmpty(EmaiMessage? m) {
if (m == null) return;
if (m.text.trim().isEmpty) messages.remove(m);
}
void finalize(EmaiMessage? m) {
if (m == null) return;
m.isFinal = true;
dropIfEmpty(m);
messages.refresh();
persist();
}
void clear() {
messages.clear();
_storage.remove(_storageKey);
}
}