package sqlite import ( "context" "database/sql" "encoding/json" "fmt" "time" "git.kingecg.top/kingecg/gomog/internal/database" "git.kingecg.top/kingecg/gomog/pkg/types" _ "github.com/mattn/go-sqlite3" // Import SQLite driver ) // SQLiteAdapter SQLite适配器 type SQLiteAdapter struct { *database.BaseAdapter } // NewSQLiteAdapter 创建SQLite适配器 func NewSQLiteAdapter() *SQLiteAdapter { return &SQLiteAdapter{ BaseAdapter: database.NewBaseAdapter("sqlite3"), } } // CollectionExists 检查集合是否存在 func (a *SQLiteAdapter) CollectionExists(ctx context.Context, name string) (bool, error) { query := "SELECT name FROM sqlite_master WHERE type='table' AND name=?" var exists bool err := a.GetDB().QueryRowContext(ctx, query, name).Scan(&exists) if err != nil { if err == sql.ErrNoRows { return false, nil } return false, err } return true, nil } // ListCollections 获取所有集合(表)列表 func (a *SQLiteAdapter) ListCollections(ctx context.Context) ([]string, error) { query := `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name` rows, err := a.GetDB().QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var tables []string for rows.Next() { var table string if err := rows.Scan(&table); err != nil { return nil, err } tables = append(tables, table) } return tables, rows.Err() } // FindPage 分页查询文档 func (a *SQLiteAdapter) FindPage(ctx context.Context, collection string, skip, limit int) (database.PageResult, error) { query := fmt.Sprintf("SELECT id, data, created_at, updated_at FROM %s LIMIT ? OFFSET ?", collection) rows, err := a.GetDB().QueryContext(ctx, query, limit, skip) if err != nil { return database.PageResult{}, err } defer rows.Close() var docs []types.Document for rows.Next() { var doc types.Document var jsonData []byte var createdAtStr, updatedAtStr string err := rows.Scan(&doc.ID, &jsonData, &createdAtStr, &updatedAtStr) if err != nil { return database.PageResult{}, err } if err := json.Unmarshal(jsonData, &doc.Data); err != nil { return database.PageResult{}, err } // 解析时间字符串 if parsedTime, err := parseSQLiteTime(createdAtStr); err == nil { doc.CreatedAt = parsedTime } if parsedTime, err := parseSQLiteTime(updatedAtStr); err == nil { doc.UpdatedAt = parsedTime } docs = append(docs, doc) } // 检查是否还有更多数据 checkQuery := fmt.Sprintf("SELECT 1 FROM %s LIMIT 1 OFFSET ?", collection) checkRows, err := a.GetDB().QueryContext(ctx, checkQuery, skip+limit) if err != nil { return database.PageResult{}, err } defer checkRows.Close() hasMore := checkRows.Next() return database.PageResult{ Documents: docs, HasMore: hasMore, }, rows.Err() } // parseSQLiteTime 解析SQLite时间字符串 func parseSQLiteTime(timeStr string) (time.Time, error) { layouts := []string{ "2006-01-02 15:04:05", "2006-01-02T15:04:05Z", "2006-01-02T15:04:05.000Z", time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05.000000", "2006-01-02 15:04:05.000", } for _, layout := range layouts { if t, err := time.Parse(layout, timeStr); err == nil { return t, nil } } // 如果标准布局失败,则尝试解析Unix时间戳 if unixTime, err := time.Parse("2006-01-02 15:04:05.000000", timeStr); err == nil { return unixTime, nil } // 默认返回当前时间 return time.Now(), fmt.Errorf("无法解析时间字符串: %s", timeStr) } // Connect 连接 SQLite 数据库 func (a *SQLiteAdapter) Connect(ctx context.Context, dsn string) error { // SQLite 需要启用 JSON1 扩展(大多数构建已默认包含) if err := a.BaseAdapter.Connect(ctx, dsn); err != nil { return err } // 设置 SQLite 特定的 PRAGMA _, err := a.GetDB().Exec("PRAGMA journal_mode = WAL") return err } // CreateCollection 创建集合(SQLite 表) func (a *SQLiteAdapter) CreateCollection(ctx context.Context, name string) error { // SQLite 使用 CHECK 约束验证 JSON query := fmt.Sprintf(` CREATE TABLE IF NOT EXISTS %s ( id TEXT PRIMARY KEY, data TEXT NOT NULL CHECK(json_valid(data)), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`, name) _, err := a.GetDB().ExecContext(ctx, query) return err } // FindAll 查询所有文档(使用 SQLite JSON 函数) func (a *SQLiteAdapter) FindAll(ctx context.Context, collection string) ([]types.Document, error) { query := fmt.Sprintf("SELECT id, data, created_at, updated_at FROM %s", collection) rows, err := a.GetDB().QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var docs []types.Document for rows.Next() { var doc types.Document var jsonData []byte err := rows.Scan(&doc.ID, &jsonData, &doc.CreatedAt, &doc.UpdatedAt) if err != nil { return nil, err } if err := json.Unmarshal(jsonData, &doc.Data); err != nil { return nil, err } docs = append(docs, doc) } return docs, rows.Err() } // InsertMany 批量插入(SQLite 优化版本) func (a *SQLiteAdapter) InsertMany(ctx context.Context, collection string, docs []types.Document) error { tx, err := a.GetDB().BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() for _, doc := range docs { jsonData, err := json.Marshal(doc.Data) if err != nil { return err } query := fmt.Sprintf( "INSERT INTO %s (id, data, created_at, updated_at) VALUES (?, json(?), ?, ?)", collection, ) now := doc.CreatedAt if now.IsZero() { now = doc.UpdatedAt } if now.IsZero() { now = doc.UpdatedAt } _, err = tx.ExecContext(ctx, query, doc.ID, string(jsonData), now, now) if err != nil { return err } } return tx.Commit() }