agent_chat_store.dart 3.1 KB
import 'dart:convert';

import 'package:path/path.dart' as p;
import 'package:sqflite/sqflite.dart';

/// 会话持久化记录,结构保持简单,页面负责与 UI 模型互转。
class AgentChatRecord {
  AgentChatRecord({
    required this.id,
    required this.role,
    required this.content,
    this.card,
    required this.ts,
    required this.status,
  });

  final String id;
  final String role;
  final String content;
  final Map<String, dynamic>? card;
  final int ts;
  final String status;

  factory AgentChatRecord.fromDbMap(Map<String, dynamic> map) {
    final dynamic cardValue = map['card'];
    return AgentChatRecord(
      id: map['id'] as String,
      role: map['role'] as String,
      content: map['content'] as String? ?? '',
      card: cardValue == null
          ? null
          : jsonDecode(cardValue as String) as Map<String, dynamic>,
      ts: map['ts'] as int,
      status: map['status'] as String? ?? 'done',
    );
  }

  Map<String, dynamic> toDbMap(String storeId) {
    return <String, dynamic>{
      'id': id,
      'store_id': storeId,
      'role': role,
      'content': content,
      'card': card == null ? null : jsonEncode(card),
      'ts': ts,
      'status': status,
    };
  }
}

/// 按门店隔离的本地会话存储。
class AgentChatStore {
  AgentChatStore._();

  static final AgentChatStore instance = AgentChatStore._();

  static const String _dbName = 'agent_chat.db';
  static const String _table = 'agent_messages';

  Database? _db;

  Future<Database> _open() async {
    if (_db != null) return _db!;
    final String path = p.join(await getDatabasesPath(), _dbName);
    _db = await openDatabase(
      path,
      version: 1,
      onCreate: (Database db, int version) async {
        await db.execute('''
          CREATE TABLE $_table(
            id TEXT PRIMARY KEY,
            store_id TEXT NOT NULL,
            role TEXT NOT NULL,
            content TEXT NOT NULL,
            card TEXT,
            ts INTEGER NOT NULL,
            status TEXT NOT NULL
          )
        ''');
        await db.execute(
          'CREATE INDEX idx_agent_store_ts ON $_table(store_id, ts)',
        );
      },
    );
    return _db!;
  }

  Future<List<AgentChatRecord>> load(String storeId) async {
    final Database db = await _open();
    final List<Map<String, dynamic>> rows = await db.query(
      _table,
      where: 'store_id = ?',
      whereArgs: <Object?>[storeId],
      orderBy: 'ts ASC',
    );
    return rows
        .map((Map<String, dynamic> row) => AgentChatRecord.fromDbMap(row))
        .toList();
  }

  Future<void> save(String storeId, List<AgentChatRecord> records) async {
    final Database db = await _open();
    final Batch batch = db.batch();
    batch.delete(_table, where: 'store_id = ?', whereArgs: <Object?>[storeId]);
    for (final AgentChatRecord record in records) {
      batch.insert(
        _table,
        record.toDbMap(storeId),
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
    }
    await batch.commit(noResult: true);
  }

  Future<void> clearAll() async {
    final Database db = await _open();
    await db.delete(_table);
  }
}