低代码平台详细技术方案 - 后端架构
基于 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 风格
**执行流程:**- 获取当前用户角色列表
- 获取请求路径和 HTTP 方法
- Casbin.Enforce(role, path, method)
- 缓存权限检查结果(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, editor3. 限流中间件 (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 + ZRemRangeByScore5. 查询结果缓存
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
XACK4.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=json7.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 第一阶段:基础框架搭建
- 初始化项目结构
- 配置 Gin + GORM + Redis
- 实现基础中间件(Auth、Logger、Recovery)
- 实现统一响应格式
- 搭建 RBAC 权限体系
9.2 第二阶段:核心功能开发
- 实现元数据服务(页面、组件、数据模型)
- 实现表单引擎(设计、校验、联动)
- 实现动态数据服务(通用 CRUD)
- 集成 Elasticsearch 搜索
9.3 第三阶段:高级功能
- 实现工作流引擎
- 实现规则引擎
- 实现版本管理
- 完善缓存策略
9.4 第四阶段:优化和测试
- 性能优化(查询优化、缓存优化)
- 单元测试和集成测试
- API 文档生成(Swagger)
- 部署和监控
文档版本: v1.0 最后更新: 2024-01-01 维护者: 低代码平台团队
