chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:19:22 +08:00
commit 6366859bb3
222 changed files with 47313 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
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
}