Skip to content

低代码平台详细技术方案 - 后端架构 ​

基于 Golang (Gin + GORM + go-redis) + PostgreSQL + Elasticsearch


目录 ​


一、项目整体结构 ​

1.1 目录结构 ​

bash
low-code-backend/
├── cmd/                              # 应用程序入口
│   ├── api-server/                   # 主API服务
│   │   └── main.go
│   ├── worker/                       # 后台任务工作进程
│   │   └── main.go
│   └── migrate/                      # 数据库迁移工具
│       └── main.go
│
├── internal/                         # 私有应用代码
│   ├── api/                          # HTTP处理层
│   │   ├── handler/                  # 请求处理器
│   │   │   ├── metadata/             # 元数据处理器
│   │   │   ├── form/                 # 表单处理器
│   │   │   ├── data/                 # 数据处理器
│   │   │   ├── workflow/             # 流程处理器
│   │   │   ├── auth/                 # 认证处理器
│   │   │   └── system/               # 系统处理器
│   │   ├── middleware/               # 中间件
│   │   ├── router/                   # 路由定义
│   │   └── dto/                      # 数据传输对象
│   │
│   ├── service/                      # 业务逻辑层
│   │   ├── metadata/
│   │   ├── form/
│   │   ├── data/
│   │   ├── workflow/
│   │   ├── rule/
│   │   ├── search/
│   │   └── auth/
│   │
│   ├── repository/                   # 数据访问层
│   │   ├── postgres/
│   │   ├── elasticsearch/
│   │   └── redis/
│   │
│   ├── model/                        # 数据模型
│   │   ├── entity/                   # 实体模型
│   │   ├── domain/                   # 领域模型
│   │   └── vo/                       # 值对象
│   │
│   ├── engine/                       # 引擎实现
│   │   ├── form/
│   │   ├── workflow/
│   │   └── rule/
│   │
│   └── task/                         # 异步任务
│
├── pkg/                              # 公共库
│   ├── database/
│   ├── redis/
│   ├── elasticsearch/
│   ├── logger/
│   ├── jwt/
│   ├── validator/
│   ├── utils/
│   └── config/
│
├── configs/                          # 配置文件
├── scripts/                          # 脚本
├── migrations/                       # 数据库迁移
├── test/                             # 测试
└── api/                              # API文档
```bash

### 1.2 分层架构设计

```bash
┌─────────────────────────────────────┐
│      Handler 层 (HTTP处理)          │
│   - 参数绑定和校验                  │
│   - 调用 Service 层                 │
│   - 返回统一响应                    │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│      Service 层 (业务逻辑)          │
│   - 业务规则实现                    │
│   - 事务控制                        │
│   - 调用 Repository 层              │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│    Repository 层 (数据访问)         │
│   - GORM 操作                       │
│   - Redis 缓存                      │
│   - ES 搜索                         │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│      数据层 (PostgreSQL/ES/Redis)   │
└─────────────────────────────────────┘

二、Gin 中间件体系 ​

2.1 中间件执行顺序 ​

bash
请求 → Recovery → Logger → Trace → CORS → RateLimit → Auth → Permission → Handler → 响应
```bash

### 2.2 核心中间件设计

#### 1. 认证中间件 (Auth)

**功能:**
- JWT Token 验证
- Token 刷新机制
- 白名单路由(登录、注册等)
- 从 Redis 验证 Token 有效性(支持踢人下线)

**执行流程:**
```bash
1. 从 Header 提取 Authorization: Bearer {token}
2. 解析 JWT,获取 UserID 和过期时间
3. 从 Redis 检查 token:{userID} 是否存在且匹配
4. 验证通过,将用户信息注入到 gin.Context
5. 设置 UserID 到日志上下文

关键实现点:

go
// 白名单路由(不需要认证)
var whiteList = []string{
    "/api/v1/auth/login",
    "/api/v1/auth/register",
    "/api/v1/public/*",
    "/health",
}

// Redis Key 设计
lc:token:{userID} -> {token_string}
TTL: 7 days
```bash

#### 2. 权限中间件 (Permission)

**功能:**
- 基于 Casbin 实现 RBAC
- 检查用户是否有权限访问当前路由
- 支持路径参数匹配
- 支持 RESTful 风格

**执行流程:**
  1. 获取当前用户角色列表
  2. 获取请求路径和 HTTP 方法
  3. Casbin.Enforce(role, path, method)
  4. 缓存权限检查结果(5分钟)
bash

**Casbin 模型:**
```ini
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act

策略示例:

bash
p, admin, /api/v1/pages/*, *
p, editor, /api/v1/pages/*, GET
p, editor, /api/v1/pages/*, POST

g, user:1001, admin
g, user:1002, editor

3. 限流中间件 (RateLimit) ​

功能:

  • IP 级别限流:100 req/min
  • 用户级别限流:200 req/min
  • API 级别限流:特定接口自定义
  • 返回 429 Too Many Requests

算法:滑动窗口(基于 Redis Sorted Set)

Redis Key 设计:

bash
lc:rate_limit:ip:{ip}             -> Sorted Set (score=timestamp)
lc:rate_limit:user:{userID}       -> Sorted Set
lc:rate_limit:api:{path}:{minute} -> String (计数器)

实现逻辑:

go
// 滑动窗口实现
func checkRateLimit(key string, limit int64, window time.Duration) bool {
    now := time.Now().Unix()
    windowStart := now - int64(window.Seconds())

    pipe := rdb.Pipeline()
    // 删除窗口外的数据
    pipe.ZRemRangeByScore(ctx, key, "0", fmt.Sprintf("%d", windowStart))
    // 添加当前请求
    pipe.ZAdd(ctx, key, redis.Z{Score: float64(now), Member: now})
    // 统计窗口内请求数
    pipe.ZCount(ctx, key, fmt.Sprintf("%d", windowStart), fmt.Sprintf("%d", now))
    // 设置过期时间
    pipe.Expire(ctx, key, window)

    results, _ := pipe.Exec(ctx)
    count := results[2].(*redis.IntCmd).Val()

    return count <= limit
}
```bash

#### 4. 日志中间件 (Logger)

**功能:**
- 记录请求开始和结束
- 记录耗时、状态码、错误
- 记录 TraceID、UserID
- 慢查询警告(>1s)

**日志格式(JSON):**
```json
{
  "time": "2024-01-01T12:00:00Z",
  "level": "info",
  "trace_id": "20240101120000-abc123",
  "user_id": 1001,
  "method": "POST",
  "path": "/api/v1/pages",
  "query": "status=draft",
  "status": 200,
  "latency": "45ms",
  "ip": "192.168.1.1",
  "user_agent": "Mozilla/5.0...",
  "error": ""
}

基于 Zap 实现:

go
func LoggerMiddleware(logger *zap.Logger) gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path
        query := c.Request.URL.RawQuery

        c.Next()

        latency := time.Since(start)
        status := c.Writer.Status()

        fields := []zap.Field{
            zap.String("trace_id", c.GetString("trace_id")),
            zap.Int("user_id", c.GetInt("user_id")),
            zap.String("method", c.Request.Method),
            zap.String("path", path),
            zap.String("query", query),
            zap.Int("status", status),
            zap.Duration("latency", latency),
            zap.String("ip", c.ClientIP()),
        }

        if latency > time.Second {
            logger.Warn("Slow request", fields...)
        } else if status >= 500 {
            logger.Error("Server error", fields...)
        } else if status >= 400 {
            logger.Warn("Client error", fields...)
        } else {
            logger.Info("Request completed", fields...)
        }
    }
}
```bash

#### 5. 链路追踪 (Trace)

**功能:**
- 每个请求生成唯一 TraceID
- 从 Header 读取 X-Trace-ID(支持分布式追踪)
- 注入到 Context 和响应 Header
- 传递给下游服务

**TraceID 格式:**

{timestamp}-{randomString} 示例:20240101120000-abc123def456

bash

#### 6. 异常恢复 (Recovery)

**功能:**
- 捕获所有 panic
- 记录错误堆栈到日志
- 返回统一错误响应
- 发送告警(生产环境)

#### 7. CORS 中间件

**配置:**
```go
AllowOrigins:     []string{"http://localhost:8080", "https://app.example.com"}
AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
AllowHeaders:     []string{"Origin", "Content-Type", "Authorization", "X-Trace-ID"}
AllowCredentials: true
MaxAge:           12 * time.Hour

三、GORM 模型设计 ​

3.1 基础模型 ​

Base Model ​

go
type BaseModel struct {
    ID        uint           `gorm:"primarykey" json:"id"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 软删除
}

type AuditModel struct {
    BaseModel
    CreatedBy uint `json:"created_by" gorm:"index"`
    UpdatedBy uint `json:"updated_by"`
}
```bash

### 3.2 核心实体模型

#### 1. 页面模型
```go
type Page struct {
    AuditModel
    Name        string         `gorm:"size:100;not null;index" json:"name"`
    Code        string         `gorm:"size:50;uniqueIndex" json:"code"`
    Route       string         `gorm:"size:200;index" json:"route"`
    Config      datatypes.JSON `gorm:"type:jsonb" json:"config"`
    Version     int            `gorm:"default:1" json:"version"`
    Status      string         `gorm:"size:20;default:'draft';index" json:"status"`
    Description string         `gorm:"size:500" json:"description"`

    Versions []PageVersion `gorm:"foreignKey:PageID" json:"versions,omitempty"`
}

func (Page) TableName() string {
    return "lc_pages"
}

2. 组件模型 ​

go
type Component struct {
    BaseModel
    Type        string         `gorm:"size:50;not null;uniqueIndex" json:"type"`
    Name        string         `gorm:"size:100;not null" json:"name"`
    Category    string         `gorm:"size:50;index" json:"category"`
    Icon        string         `gorm:"size:100" json:"icon"`
    Schema      datatypes.JSON `gorm:"type:jsonb;not null" json:"schema"`
    DefaultProps datatypes.JSON `gorm:"type:jsonb" json:"default_props"`
    IsBuiltin   bool           `gorm:"default:false;index" json:"is_builtin"`
    Sort        int            `gorm:"default:0" json:"sort"`
}
```bash

#### 3. 数据模型定义
```go
type DataModel struct {
    AuditModel
    Name        string         `gorm:"size:100;not null" json:"name"`
    Code        string         `gorm:"size:50;uniqueIndex" json:"code"`
    TableName   string         `gorm:"size:100;uniqueIndex" json:"table_name"`
    Fields      datatypes.JSON `gorm:"type:jsonb;not null" json:"fields"`
    Indexes     datatypes.JSON `gorm:"type:jsonb" json:"indexes"`
    Relations   datatypes.JSON `gorm:"type:jsonb" json:"relations"`
    Status      string         `gorm:"size:20;default:'active'" json:"status"`
}

// Fields JSON 结构
type FieldDefinition struct {
    Name         string      `json:"name"`
    Type         string      `json:"type"`
    Length       int         `json:"length,omitempty"`
    Required     bool        `json:"required"`
    DefaultValue interface{} `json:"default_value,omitempty"`
    Unique       bool        `json:"unique"`
    Index        bool        `json:"index"`
    Comment      string      `json:"comment"`
}

4. 工作流模型 ​

go
type WorkflowDefinition struct {
    AuditModel
    Name        string         `gorm:"size:100;not null" json:"name"`
    Code        string         `gorm:"size:50;uniqueIndex" json:"code"`
    BpmnXML     string         `gorm:"type:text" json:"bpmn_xml"`
    Config      datatypes.JSON `gorm:"type:jsonb" json:"config"`
    Version     int            `gorm:"default:1" json:"version"`
    Status      string         `gorm:"size:20;default:'draft'" json:"status"`
}

type WorkflowInstance struct {
    BaseModel
    DefinitionID  uint           `gorm:"index;not null" json:"definition_id"`
    BusinessKey   string         `gorm:"size:100;index" json:"business_key"`
    CurrentNode   string         `gorm:"size:100" json:"current_node"`
    Status        string         `gorm:"size:20;index" json:"status"`
    Variables     datatypes.JSON `gorm:"type:jsonb" json:"variables"`
    StartedBy     uint           `json:"started_by"`
    StartedAt     time.Time      `json:"started_at"`
    EndedAt       *time.Time     `json:"ended_at,omitempty"`
}

type WorkflowTask struct {
    BaseModel
    InstanceID  uint           `gorm:"index;not null" json:"instance_id"`
    NodeID      string         `gorm:"size:100" json:"node_id"`
    NodeName    string         `gorm:"size:100" json:"node_name"`
    Assignee    uint           `gorm:"index" json:"assignee"`
    Status      string         `gorm:"size:20;index" json:"status"`
    FormData    datatypes.JSON `gorm:"type:jsonb" json:"form_data"`
    Comment     string         `gorm:"size:1000" json:"comment"`
    CompletedAt *time.Time     `json:"completed_at,omitempty"`
}
```bash

#### 5. 用户和权限模型
```go
type User struct {
    BaseModel
    Username     string    `gorm:"size:50;uniqueIndex;not null" json:"username"`
    Password     string    `gorm:"size:200;not null" json:"-"`
    Email        string    `gorm:"size:100;uniqueIndex" json:"email"`
    Phone        string    `gorm:"size:20" json:"phone"`
    RealName     string    `gorm:"size:50" json:"real_name"`
    Avatar       string    `gorm:"size:500" json:"avatar"`
    Status       string    `gorm:"size:20;default:'active';index" json:"status"`
    LastLoginAt  *time.Time `json:"last_login_at,omitempty"`
    LastLoginIP  string    `gorm:"size:50" json:"last_login_ip"`

    Roles []Role `gorm:"many2many:lc_user_roles;" json:"roles,omitempty"`
}

type Role struct {
    BaseModel
    Name        string `gorm:"size:50;uniqueIndex;not null" json:"name"`
    Code        string `gorm:"size:50;uniqueIndex;not null" json:"code"`
    Description string `gorm:"size:200" json:"description"`
    Sort        int    `gorm:"default:0" json:"sort"`

    Users       []User       `gorm:"many2many:lc_user_roles;" json:"users,omitempty"`
    Permissions []Permission `gorm:"many2many:lc_role_permissions;" json:"permissions,omitempty"`
}

type Permission struct {
    BaseModel
    Resource    string `gorm:"size:100;not null" json:"resource"`
    Action      string `gorm:"size:20;not null" json:"action"`
    Description string `gorm:"size:200" json:"description"`

    Roles []Role `gorm:"many2many:lc_role_permissions;" json:"roles,omitempty"`
}

3.3 GORM 最佳实践 ​

1. 连接池配置 ​

go
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(10)           // 最小空闲连接
sqlDB.SetMaxOpenConns(100)          // 最大打开连接
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生命周期
```bash

#### 2. 预加载(避免 N+1)
```go
// 单层预加载
db.Preload("Versions").Find(&pages)

// 嵌套预加载
db.Preload("Tasks.Instance").Find(&workflows)

// 条件预加载
db.Preload("Roles", "status = ?", "active").Find(&users)

3. 批量操作 ​

go
// 批量插入
db.CreateInBatches(pages, 100)

// 批量更新
db.Model(&Page{}).Where("status = ?", "draft").Update("status", "published")
```bash

#### 4. 事务处理
```go
// 自动事务
db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&page).Error; err != nil {
        return err
    }
    if err := tx.Create(&version).Error; err != nil {
        return err
    }
    return nil
})

5. 性能优化 ​

go
// 只查询需要的字段
db.Select("id", "name", "status").Find(&pages)

// 分页优化(游标分页)
db.Where("id > ?", lastID).Limit(20).Find(&pages)

// 使用索引
db.Clauses(hint.UseIndex("idx_page_status")).Find(&pages)
```bash

---

## 四、Redis 缓存策略

### 4.1 go-redis 客户端配置

```go
// 单机模式
rdb := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    Password:     "",
    DB:           0,
    PoolSize:     20,
    MinIdleConns: 5,
    MaxRetries:   3,
    DialTimeout:  5 * time.Second,
    ReadTimeout:  3 * time.Second,
    WriteTimeout: 3 * time.Second,
})

// 集群模式
rdb := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs:    []string{"node1:7000", "node2:7001", "node3:7002"},
    PoolSize: 20,
})

4.2 Key 设计规范 ​

bash
格式:项目前缀:模块:业务:ID:字段

示例:
lc:user:info:1001              - 用户信息
lc:page:config:home            - 页面配置
lc:metadata:component:list     - 组件列表
lc:token:1001                  - 用户Token
lc:lock:page:1001              - 分布式锁
lc:rate_limit:ip:192.168.1.1   - 限流计数

4.3 核心应用场景 ​

1. 元数据配置缓存 ​

bash
场景:页面配置、组件定义(读多写少)
数据结构:String (JSON) 或 Hash
TTL:1 hour
缓存策略:Cache-Aside

Key 设计:
lc:page:config:{pageID}
lc:component:schema:{componentType}

更新策略:
1. 读取:Redis → DB → Redis
2. 更新:DB → Delete Redis(延迟双删)

2. 用户会话管理 ​

bash
场景:JWT Token 验证、在线用户
数据结构:String

Token 存储:
lc:token:{userID} -> token_string
TTL: 7 days

Token 黑名单:
lc:token:blacklist:{token} -> 1
TTL: 剩余有效期

在线用户:
lc:online:users -> Set {userID1, userID2}

3. 分布式锁 ​

bash
场景:并发控制(如同一页面多人编辑)
实现:SET NX EX

Key 设计:
lc:lock:page:edit:{pageID}

使用 Lua 脚本保证原子性:
- 获取锁:SET key value NX EX 30
- 释放锁:只能释放自己的锁(检查 value)

4. 限流计数器 ​

bash
场景:API 限流、防刷
算法:滑动窗口(Sorted Set)

Key 设计:
lc:rate_limit:ip:{ip}
lc:rate_limit:user:{userID}

实现:ZAdd + ZCount + ZRemRangeByScore

5. 查询结果缓存 ​

bash
场景:复杂查询、聚合统计
数据结构:String (JSON)
TTL:5-10 minutes

Key 设计:
lc:cache:query:{hash(params)}

防护措施:
- 缓存击穿:互斥锁
- 缓存穿透:空值缓存 + 布隆过滤器
- 缓存雪崩:过期时间加随机值

6. 消息队列 (Stream) ​

bash
场景:异步任务、事件溯源
数据结构:Stream

Stream 设计:
lc:stream:data:sync
lc:stream:file:export

使用消费者组:
XGROUP CREATE
XREADGROUP
XACK

4.4 缓存最佳实践 ​

缓存更新策略 ​

bash
1. Cache-Aside(旁路缓存)- 推荐
   读:缓存 → DB → 写缓存
   写:DB → 删除缓存

2. Write-Through(直写)
   写:同时写 DB 和缓存

3. Write-Behind(回写)
   写:先写缓存,异步写 DB

过期时间设置 ​

bash
元数据配置:1-2 小时
用户会话:7 天
查询结果:5-10 分钟
热点数据:永不过期 + 逻辑过期
临时数据:1-5 分钟

Pipeline 批量操作 ​

go
pipe := rdb.Pipeline()
for _, page := range pages {
    key := fmt.Sprintf("lc:page:config:%d", page.ID)
    pipe.Set(ctx, key, page.Config, time.Hour)
}
_, err := pipe.Exec(ctx)
```bash

---

## 五、API 接口规范

### 5.1 RESTful 风格

GET /api/v1/pages - 获取页面列表 GET /api/v1/pages/:id - 获取单个页面 POST /api/v1/pages - 创建页面 PUT /api/v1/pages/:id - 更新页面 DELETE /api/v1/pages/:id - 删除页面

GET /api/v1/pages/:id/versions - 获取页面版本列表 POST /api/v1/pages/:id/publish - 发布页面 POST /api/v1/pages/:id/rollback/:ver - 回滚到指定版本

bash

### 5.2 统一响应格式

#### 成功响应
```json
{
  "code": 0,
  "message": "success",
  "data": {
    "id": 1001,
    "name": "首页"
  },
  "trace_id": "20240101120000-abc123"
}

分页响应 ​

json
{
  "code": 0,
  "message": "success",
  "data": {
    "list": [...],
    "pagination": {
      "page": 1,
      "page_size": 20,
      "total": 100,
      "total_pages": 5
    }
  },
  "trace_id": "20240101120000-abc123"
}
```bash

#### 错误响应
```json
{
  "code": 40001,
  "message": "页面不存在",
  "error": "page not found",
  "trace_id": "20240101120000-abc123"
}

5.3 错误码设计 ​

go
const (
    ErrSuccess        = 0      // 成功

    // 1xxxx 通用错误
    ErrInternalServer = 10000  // 服务器内部错误
    ErrInvalidParams  = 10001  // 参数错误
    ErrUnauthorized   = 10002  // 未认证
    ErrForbidden      = 10003  // 无权限
    ErrNotFound       = 10004  // 资源不存在

    // 2xxxx 用户相关
    ErrUserNotFound      = 20001
    ErrUserAlreadyExists = 20002
    ErrPasswordIncorrect = 20003

    // 3xxxx 页面相关
    ErrPageNotFound      = 30001
    ErrPageAlreadyExists = 30002
    ErrPageInvalidConfig = 30003

    // 4xxxx 数据相关
    ErrDataModelNotFound = 40001
    ErrDataValidationFailed = 40002
)
```bash

### 5.4 路由分组设计

```go
api := r.Group("/api/v1")
api.Use(middleware.Auth(), middleware.Permission())
{
    // 元数据管理
    metadata := api.Group("/metadata")
    {
        metadata.GET("/pages", handler.ListPages)
        metadata.POST("/pages", handler.CreatePage)
        metadata.GET("/pages/:id", handler.GetPage)
        metadata.PUT("/pages/:id", handler.UpdatePage)
        metadata.DELETE("/pages/:id", handler.DeletePage)

        metadata.GET("/components", handler.ListComponents)
        metadata.POST("/components", handler.CreateComponent)

        metadata.GET("/models", handler.ListDataModels)
        metadata.POST("/models", handler.CreateDataModel)
    }

    // 表单
    form := api.Group("/form")
    {
        form.GET("/designs/:id", handler.GetFormDesign)
        form.POST("/designs", handler.CreateFormDesign)
        form.POST("/validate", handler.ValidateForm)
    }

    // 动态数据
    data := api.Group("/data")
    {
        data.GET("/:model", handler.QueryData)
        data.POST("/:model", handler.CreateData)
        data.GET("/:model/:id", handler.GetData)
        data.PUT("/:model/:id", handler.UpdateData)
        data.DELETE("/:model/:id", handler.DeleteData)
    }

    // 工作流
    workflow := api.Group("/workflow")
    {
        workflow.GET("/definitions", handler.ListWorkflowDefinitions)
        workflow.POST("/definitions", handler.CreateWorkflowDefinition)
        workflow.POST("/instances", handler.StartWorkflowInstance)
        workflow.GET("/tasks/my", handler.GetMyTasks)
        workflow.POST("/tasks/:id/complete", handler.CompleteTask)
    }

    // 认证授权
    auth := api.Group("/auth")
    {
        auth.POST("/login", handler.Login)
        auth.POST("/logout", handler.Logout)
        auth.POST("/refresh", handler.RefreshToken)
        auth.GET("/me", handler.GetCurrentUser)
    }

    // 系统管理
    system := api.Group("/system")
    {
        system.GET("/users", handler.ListUsers)
        system.POST("/users", handler.CreateUser)
        system.GET("/roles", handler.ListRoles)
        system.POST("/roles", handler.CreateRole)
        system.GET("/permissions", handler.ListPermissions)
    }
}

六、核心服务实现方案 ​

6.1 元数据服务 (Metadata Service) ​

职责 ​

  • 管理页面配置、组件定义、数据模型
  • 版本管理和回滚
  • 配置校验

核心功能 ​

1. 页面配置管理

go
// Service 层
type PageService struct {
    pageRepo    repository.PageRepository
    cacheRepo   repository.CacheRepository
    versionRepo repository.PageVersionRepository
}

// 创建页面
func (s *PageService) CreatePage(req *dto.CreatePageRequest) (*entity.Page, error) {
    // 1. 校验配置 JSON Schema
    if err := s.validatePageConfig(req.Config); err != nil {
        return nil, err
    }

    // 2. 检查 Code 唯一性
    exists, _ := s.pageRepo.ExistsByCode(req.Code)
    if exists {
        return nil, errors.ErrPageAlreadyExists
    }

    // 3. 保存页面
    page := &entity.Page{
        Name:   req.Name,
        Code:   req.Code,
        Config: req.Config,
        Version: 1,
        Status: "draft",
    }

    // 4. 事务:保存页面 + 创建版本记录
    err := s.pageRepo.Transaction(func(tx *gorm.DB) error {
        if err := s.pageRepo.Create(tx, page); err != nil {
            return err
        }

        version := &entity.PageVersion{
            PageID:  page.ID,
            Version: 1,
            Config:  req.Config,
        }
        return s.versionRepo.Create(tx, version)
    })

    return page, err
}

// 发布页面
func (s *PageService) PublishPage(pageID uint) error {
    // 1. 更新状态
    if err := s.pageRepo.UpdateStatus(pageID, "published"); err != nil {
        return err
    }

    // 2. 清除缓存
    cacheKey := fmt.Sprintf("lc:page:config:%d", pageID)
    s.cacheRepo.Delete(cacheKey)

    // 3. 发送事件(通知前端刷新)
    s.publishEvent("page.published", pageID)

    return nil
}

// 获取页面(带缓存)
func (s *PageService) GetPage(id uint) (*entity.Page, error) {
    cacheKey := fmt.Sprintf("lc:page:config:%d", id)

    // 1. 尝试从缓存读取
    var page entity.Page
    if err := s.cacheRepo.Get(cacheKey, &page); err == nil {
        return &page, nil
    }

    // 2. 从数据库读取
    page, err := s.pageRepo.FindByID(id)
    if err != nil {
        return nil, err
    }

    // 3. 写入缓存
    s.cacheRepo.Set(cacheKey, page, time.Hour)

    return page, nil
}
```bash

**2. 版本管理**
```go
// 创建新版本
func (s *PageService) CreateVersion(pageID uint, config json.RawMessage, changeLog string) error {
    page, _ := s.pageRepo.FindByID(pageID)

    newVersion := page.Version + 1

    return s.pageRepo.Transaction(func(tx *gorm.DB) error {
        // 更新页面版本号
        s.pageRepo.UpdateVersion(tx, pageID, newVersion)

        // 创建版本记录
        version := &entity.PageVersion{
            PageID:    pageID,
            Version:   newVersion,
            Config:    config,
            ChangeLog: changeLog,
        }
        return s.versionRepo.Create(tx, version)
    })
}

// 回滚到指定版本
func (s *PageService) RollbackToVersion(pageID uint, version int) error {
    // 1. 获取历史版本
    historyVersion, err := s.versionRepo.FindByPageIDAndVersion(pageID, version)
    if err != nil {
        return err
    }

    // 2. 更新当前页面配置
    return s.pageRepo.UpdateConfig(pageID, historyVersion.Config)
}

6.2 表单引擎服务 (Form Engine Service) ​

职责 ​

  • 动态表单配置管理
  • 表单校验引擎
  • 表单联动逻辑

核心功能 ​

1. 表单配置结构

go
type FormSchema struct {
    FormID   string         `json:"form_id"`
    Layout   string         `json:"layout"`    // horizontal/vertical/inline
    Fields   []FormField    `json:"fields"`
    Rules    []LinkageRule  `json:"rules"`     // 联动规则
}

type FormField struct {
    Key         string                 `json:"key"`
    Type        string                 `json:"type"`        // input/select/date...
    Label       string                 `json:"label"`
    Rules       []ValidationRule       `json:"rules"`
    Props       map[string]interface{} `json:"props"`
    Visible     string                 `json:"visible"`     // 表达式
    Disabled    string                 `json:"disabled"`    // 表达式
    DefaultValue interface{}           `json:"default_value"`
}

type ValidationRule struct {
    Type     string      `json:"type"`      // required/pattern/custom
    Message  string      `json:"message"`
    Pattern  string      `json:"pattern,omitempty"`
    Validator string     `json:"validator,omitempty"` // 自定义校验函数名
}

type LinkageRule struct {
    Condition string                 `json:"condition"` // 表达式
    Actions   []LinkageAction        `json:"actions"`
}

type LinkageAction struct {
    Type   string      `json:"type"`   // setValue/setVisible/setDisabled
    Target string      `json:"target"` // 目标字段
    Value  interface{} `json:"value"`
}
```bash

**2. 表单校验引擎**
```go
type FormValidator struct {
    evaluator *rule.Evaluator // 表达式求值器
}

// 校验表单数据
func (v *FormValidator) Validate(schema *FormSchema, data map[string]interface{}) error {
    var errors []string

    for _, field := range schema.Fields {
        value := data[field.Key]

        // 1. 检查字段是否可见(不可见字段不校验)
        if field.Visible != "" {
            visible, _ := v.evaluator.Eval(field.Visible, data)
            if !visible.(bool) {
                continue
            }
        }

        // 2. 执行校验规则
        for _, rule := range field.Rules {
            if err := v.validateRule(field, value, rule, data); err != nil {
                errors = append(errors, err.Error())
            }
        }
    }

    if len(errors) > 0 {
        return fmt.Errorf("validation failed: %s", strings.Join(errors, "; "))
    }
    return nil
}

// 执行单条校验规则
func (v *FormValidator) validateRule(field FormField, value interface{}, rule ValidationRule, data map[string]interface{}) error {
    switch rule.Type {
    case "required":
        if value == nil || value == "" {
            return fmt.Errorf("%s: %s", field.Label, rule.Message)
        }
    case "pattern":
        matched, _ := regexp.MatchString(rule.Pattern, fmt.Sprintf("%v", value))
        if !matched {
            return fmt.Errorf("%s: %s", field.Label, rule.Message)
        }
    case "custom":
        // 执行自定义校验函数
        if !v.executeCustomValidator(rule.Validator, value, data) {
            return fmt.Errorf("%s: %s", field.Label, rule.Message)
        }
    }
    return nil
}

3. 表单联动引擎

go
// 计算联动结果
func (e *FormEngine) ComputeLinkage(schema *FormSchema, data map[string]interface{}) map[string]interface{} {
    result := make(map[string]interface{})

    for _, rule := range schema.Rules {
        // 判断条件是否满足
        conditionMet, _ := e.evaluator.Eval(rule.Condition, data)
        if !conditionMet.(bool) {
            continue
        }

        // 执行动作
        for _, action := range rule.Actions {
            switch action.Type {
            case "setValue":
                result[action.Target] = action.Value
            case "setVisible":
                key := fmt.Sprintf("_visible_%s", action.Target)
                result[key] = action.Value
            case "setDisabled":
                key := fmt.Sprintf("_disabled_%s", action.Target)
                result[key] = action.Value
            }
        }
    }

    return result
}
```bash

### 6.3 动态数据服务 (Dynamic Data Service)

#### 职责
- 基于元数据的通用 CRUD
- 动态查询构建
- 数据权限过滤

#### 核心功能

**1. 通用 CRUD 接口**
```go
type DynamicDataService struct {
    modelRepo repository.DataModelRepository
    db        *gorm.DB
}

// 创建数据
func (s *DynamicDataService) Create(modelCode string, data map[string]interface{}) (uint, error) {
    // 1. 获取数据模型定义
    model, err := s.modelRepo.FindByCode(modelCode)
    if err != nil {
        return 0, err
    }

    // 2. 数据校验
    if err := s.validateData(model, data); err != nil {
        return 0, err
    }

    // 3. 插入数据
    tableName := model.TableName

    // 构建插入 SQL
    columns := []string{}
    placeholders := []string{}
    values := []interface{}{}

    for _, field := range model.Fields {
        if value, ok := data[field.Name]; ok {
            columns = append(columns, field.Name)
            placeholders = append(placeholders, "?")
            values = append(values, value)
        }
    }

    sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s) RETURNING id",
        tableName,
        strings.Join(columns, ", "),
        strings.Join(placeholders, ", "))

    var id uint
    err = s.db.Raw(sql, values...).Scan(&id).Error

    return id, err
}

// 查询数据(支持复杂条件)
func (s *DynamicDataService) Query(modelCode string, query *dto.QueryRequest) (*dto.QueryResponse, error) {
    model, _ := s.modelRepo.FindByCode(modelCode)

    // 构建查询
    qb := NewQueryBuilder(s.db, model.TableName)

    // 添加过滤条件
    for _, filter := range query.Filters {
        qb.AddFilter(filter)
    }

    // 添加排序
    for _, sort := range query.Sorts {
        qb.AddSort(sort.Field, sort.Order)
    }

    // 分页
    qb.Paginate(query.Page, query.PageSize)

    // 执行查询
    var results []map[string]interface{}
    var total int64

    qb.Find(&results)
    qb.Count(&total)

    return &dto.QueryResponse{
        List: results,
        Pagination: dto.Pagination{
            Page:       query.Page,
            PageSize:   query.PageSize,
            Total:      total,
            TotalPages: int(math.Ceil(float64(total) / float64(query.PageSize))),
        },
    }, nil
}

2. 查询构建器

go
type QueryBuilder struct {
    db        *gorm.DB
    tableName string
}

type Filter struct {
    Field    string      `json:"field"`
    Operator string      `json:"operator"` // eq/ne/gt/lt/like/in/between
    Value    interface{} `json:"value"`
}

func (qb *QueryBuilder) AddFilter(filter Filter) {
    switch filter.Operator {
    case "eq":
        qb.db = qb.db.Where(fmt.Sprintf("%s = ?", filter.Field), filter.Value)
    case "ne":
        qb.db = qb.db.Where(fmt.Sprintf("%s != ?", filter.Field), filter.Value)
    case "gt":
        qb.db = qb.db.Where(fmt.Sprintf("%s > ?", filter.Field), filter.Value)
    case "lt":
        qb.db = qb.db.Where(fmt.Sprintf("%s < ?", filter.Field), filter.Value)
    case "like":
        qb.db = qb.db.Where(fmt.Sprintf("%s LIKE ?", filter.Field), fmt.Sprintf("%%%v%%", filter.Value))
    case "in":
        qb.db = qb.db.Where(fmt.Sprintf("%s IN ?", filter.Field), filter.Value)
    case "between":
        values := filter.Value.([]interface{})
        qb.db = qb.db.Where(fmt.Sprintf("%s BETWEEN ? AND ?", filter.Field), values[0], values[1])
    }
}

func (qb *QueryBuilder) AddSort(field string, order string) {
    qb.db = qb.db.Order(fmt.Sprintf("%s %s", field, order))
}

func (qb *QueryBuilder) Paginate(page, pageSize int) {
    offset := (page - 1) * pageSize
    qb.db = qb.db.Limit(pageSize).Offset(offset)
}

func (qb *QueryBuilder) Find(dest interface{}) {
    qb.db.Table(qb.tableName).Find(dest)
}

func (qb *QueryBuilder) Count(count *int64) {
    qb.db.Table(qb.tableName).Count(count)
}
```bash

**3. 数据权限过滤**
```go
// 注入数据权限条件
func (s *DynamicDataService) applyDataPermission(qb *QueryBuilder, userID uint, modelCode string) {
    // 获取用户的数据权限规则
    rules := s.getDataPermissionRules(userID, modelCode)

    for _, rule := range rules {
        switch rule.Type {
        case "own":
            // 只能看自己创建的数据
            qb.AddFilter(Filter{Field: "created_by", Operator: "eq", Value: userID})
        case "dept":
            // 只能看本部门的数据
            deptID := s.getUserDeptID(userID)
            qb.AddFilter(Filter{Field: "dept_id", Operator: "eq", Value: deptID})
        case "custom":
            // 自定义条件
            qb.db = qb.db.Where(rule.Condition, rule.Values...)
        }
    }
}

七、配置文件示例 ​

7.1 主配置文件 (config.yaml) ​

yaml
# 应用配置
app:
  name: "Low-Code Platform"
  version: "1.0.0"
  mode: "debug"  # debug/release
  port: 8080
  read_timeout: 60s
  write_timeout: 60s

# 数据库配置
database:
  postgres:
    host: localhost
    port: 5432
    user: postgres
    password: secret
    dbname: lowcode
    sslmode: disable
    max_idle_conns: 10
    max_open_conns: 100
    conn_max_lifetime: 1h
    log_level: info  # silent/error/warn/info

# Redis 配置
redis:
  mode: single  # single/cluster/sentinel
  addr: localhost:6379
  password: ""
  db: 0
  pool_size: 20
  min_idle_conns: 5
  max_retries: 3
  dial_timeout: 5s
  read_timeout: 3s
  write_timeout: 3s

# Elasticsearch 配置
elasticsearch:
  addresses:
    - http://localhost:9200
  username: elastic
  password: secret
  max_retries: 3
  enable_sniffer: false

# JWT 配置
jwt:
  secret: "your-secret-key-change-in-production"
  expire_hours: 168  # 7 days
  refresh_hours: 720  # 30 days

# 日志配置
logger:
  level: info  # debug/info/warn/error
  format: json  # json/console
  output: stdout  # stdout/file
  file_path: logs/app.log
  max_size: 100  # MB
  max_backups: 10
  max_age: 30  # days
  compress: true

# CORS 配置
cors:
  allow_origins:
    - http://localhost:8080
    - https://app.example.com
  allow_methods:
    - GET
    - POST
    - PUT
    - DELETE
    - OPTIONS
  allow_headers:
    - Origin
    - Content-Type
    - Authorization
    - X-Trace-ID
  allow_credentials: true
  max_age: 12h

# 限流配置
rate_limit:
  ip_limit: 100  # requests per minute
  user_limit: 200
  enable: true

# 文件上传配置
upload:
  max_size: 10485760  # 10MB
  allowed_types:
    - image/jpeg
    - image/png
    - application/pdf
  save_path: uploads/

# Casbin 配置
casbin:
  model_path: configs/rbac_model.conf
  policy_adapter: database  # database/file

# 监控配置
metrics:
  enable: true
  path: /metrics
  port: 9090
```bash

### 7.2 环境变量文件 (.env.example)

```bash
# 应用环境
APP_MODE=debug
APP_PORT=8080

# 数据库
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret
DB_NAME=lowcode

# Redis
REDIS_ADDR=localhost:6379
REDIS_PASSWORD=
REDIS_DB=0

# Elasticsearch
ES_ADDRESSES=http://localhost:9200
ES_USERNAME=elastic
ES_PASSWORD=secret

# JWT
JWT_SECRET=your-secret-key-change-in-production

# 日志
LOG_LEVEL=info
LOG_FORMAT=json

7.3 Casbin 模型文件 (rbac_model.conf) ​

ini
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && (r.act == p.act || p.act == "*")
```bash

---

## 八、技术栈总结

### 后端核心依赖

```go
// Web 框架
github.com/gin-gonic/gin

// ORM
gorm.io/gorm
gorm.io/driver/postgres
gorm.io/datatypes  // JSONB 支持

// Redis
github.com/redis/go-redis/v9

// Elasticsearch
github.com/elastic/go-elasticsearch/v8
github.com/olivere/elastic/v7

// 配置管理
github.com/spf13/viper

// 日志
go.uber.org/zap

// JWT
github.com/golang-jwt/jwt/v5

// 参数校验
github.com/go-playground/validator/v10

// 权限
github.com/casbin/casbin/v2
github.com/casbin/gorm-adapter/v3

// 规则引擎
github.com/Knetic/govaluate
github.com/antonmedv/expr

// 工具库
github.com/google/uuid
github.com/spf13/cast
golang.org/x/crypto

九、开发流程建议 ​

9.1 第一阶段:基础框架搭建 ​

  1. 初始化项目结构
  2. 配置 Gin + GORM + Redis
  3. 实现基础中间件(Auth、Logger、Recovery)
  4. 实现统一响应格式
  5. 搭建 RBAC 权限体系

9.2 第二阶段:核心功能开发 ​

  1. 实现元数据服务(页面、组件、数据模型)
  2. 实现表单引擎(设计、校验、联动)
  3. 实现动态数据服务(通用 CRUD)
  4. 集成 Elasticsearch 搜索

9.3 第三阶段:高级功能 ​

  1. 实现工作流引擎
  2. 实现规则引擎
  3. 实现版本管理
  4. 完善缓存策略

9.4 第四阶段:优化和测试 ​

  1. 性能优化(查询优化、缓存优化)
  2. 单元测试和集成测试
  3. API 文档生成(Swagger)
  4. 部署和监控

文档版本: v1.0 最后更新: 2024-01-01 维护者: 低代码平台团队