Files
ai_site/platform/internal/crud/memory.go
2026-07-31 10:19:22 +08:00

170 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package crud
import (
"context"
"fmt"
"sync"
"time"
"aijianzhan/platform/internal/meta"
"github.com/google/uuid"
)
// MemoryEngine 骨架默认引擎:不依赖外部 DB方便本地验证动态 CRUD。
type MemoryEngine struct {
mu sync.RWMutex
rows map[string][]map[string]any // schema.table
}
func NewMemoryEngine() *MemoryEngine {
return &MemoryEngine{rows: map[string][]map[string]any{}}
}
func tableKey(ref *meta.ResourceRef) string {
return ref.App.SchemaName + "." + ref.Entity.Table
}
func (e *MemoryEngine) List(ctx context.Context, ref *meta.ResourceRef, tenantID int64, page, pageSize int, filters map[string]string, sortBy string) ([]map[string]any, int, error) {
if err := ensureOp(ref, "list"); err != nil {
return nil, 0, err
}
if err := validateFilters(ref, filters); err != nil {
return nil, 0, err
}
if err := validateSort(ref, sortBy); err != nil {
return nil, 0, err
}
page, pageSize = pageBounds(ref, page, pageSize)
e.mu.RLock()
defer e.mu.RUnlock()
all := e.rows[tableKey(ref)]
filtered := make([]map[string]any, 0, len(all))
for _, row := range all {
if toInt64(row["tenant_id"]) != tenantID {
continue
}
if !matchOrgScope(row, RowScopeFrom(ctx).OrgUnitIDs) {
continue
}
if matchFilters(row, filters) {
filtered = append(filtered, cloneRow(row))
}
}
sortRows(filtered, sortBy)
total := len(filtered)
start := (page - 1) * pageSize
if start >= total {
return []map[string]any{}, total, nil
}
end := start + pageSize
if end > total {
end = total
}
return filtered[start:end], total, nil
}
func (e *MemoryEngine) Get(ctx context.Context, ref *meta.ResourceRef, tenantID int64, id string) (map[string]any, error) {
if err := ensureOp(ref, "get"); err != nil {
return nil, err
}
e.mu.RLock()
defer e.mu.RUnlock()
pk := ref.Entity.PrimaryKey
for _, row := range e.rows[tableKey(ref)] {
if toInt64(row["tenant_id"]) == tenantID && fmt.Sprint(row[pk]) == id {
if !matchOrgScope(row, RowScopeFrom(ctx).OrgUnitIDs) {
break
}
return cloneRow(row), nil
}
}
return nil, fmt.Errorf("not found")
}
func (e *MemoryEngine) Create(ctx context.Context, ref *meta.ResourceRef, tenantID, userID int64, body map[string]any) (map[string]any, error) {
if err := ensureOp(ref, "create"); err != nil {
return nil, err
}
row, err := sanitizeBody(ref.Entity, body, false)
if err != nil {
return nil, err
}
pk := ref.Entity.PrimaryKey
if _, ok := row[pk]; !ok {
if isAutoPK(ref.Entity) {
row[pk] = time.Now().UnixNano()
} else {
row[pk] = uuid.NewString()
}
}
now := time.Now().UTC().Format(time.RFC3339)
row["tenant_id"] = tenantID
row["created_by"] = userID
row["created_at"] = now
row["updated_at"] = now
if scope := RowScopeFrom(ctx); scope.WriteOrgUnit > 0 {
row["org_unit_id"] = scope.WriteOrgUnit
}
e.mu.Lock()
defer e.mu.Unlock()
k := tableKey(ref)
e.rows[k] = append(e.rows[k], row)
return cloneRow(row), nil
}
func (e *MemoryEngine) Update(_ context.Context, ref *meta.ResourceRef, tenantID int64, id string, body map[string]any) (map[string]any, error) {
if err := ensureOp(ref, "update"); err != nil {
return nil, err
}
patch, err := sanitizeBody(ref.Entity, body, true)
if err != nil {
return nil, err
}
e.mu.Lock()
defer e.mu.Unlock()
pk := ref.Entity.PrimaryKey
rows := e.rows[tableKey(ref)]
for i, row := range rows {
if toInt64(row["tenant_id"]) == tenantID && fmt.Sprint(row[pk]) == id {
for k, v := range patch {
if k == pk || k == "tenant_id" {
continue
}
row[k] = v
}
row["updated_at"] = time.Now().UTC().Format(time.RFC3339)
rows[i] = row
return cloneRow(row), nil
}
}
return nil, fmt.Errorf("not found")
}
func (e *MemoryEngine) Delete(_ context.Context, ref *meta.ResourceRef, tenantID int64, id string) error {
if err := ensureOp(ref, "delete"); err != nil {
return err
}
e.mu.Lock()
defer e.mu.Unlock()
pk := ref.Entity.PrimaryKey
k := tableKey(ref)
rows := e.rows[k]
out := rows[:0]
found := false
for _, row := range rows {
if toInt64(row["tenant_id"]) == tenantID && fmt.Sprint(row[pk]) == id {
found = true
continue
}
out = append(out, row)
}
if !found {
return fmt.Errorf("not found")
}
e.rows[k] = out
return nil
}