Skip to content

审计字段

VEF Framework 通过 orm.Model 提供自动维护的审计字段,记录数据的创建和修改信息。

审计字段概述

当模型嵌入 orm.Model 时,会自动获得以下审计字段:

go
type Model struct {
    Id        string      `bun:"id,pk" json:"id"`
    CreatedAt time.Time   `bun:"created_at,notnull,default:current_timestamp" json:"createdAt"`
    CreatedBy null.String `bun:"created_by" json:"createdBy"`
    UpdatedAt time.Time   `bun:"updated_at,notnull,default:current_timestamp" json:"updatedAt"`
    UpdatedBy null.String `bun:"updated_by" json:"updatedBy"`
}

字段说明

字段数据库列JSON 字段类型说明
Idididstring主键,自动生成 UUID
CreatedAtcreated_atcreatedAttime.Time创建时间
CreatedBycreated_bycreatedBynull.String创建人 ID
UpdatedAtupdated_atupdatedAttime.Time最后更新时间
UpdatedByupdated_byupdatedBynull.String最后更新人 ID

自动维护机制

创建记录时

当创建新记录时,框架会自动:

  1. 生成 UUID 作为 Id
  2. 设置 CreatedAt 为当前时间
  3. 设置 CreatedBy 为当前登录用户的 ID
  4. 设置 UpdatedAt 为当前时间
  5. 设置 UpdatedBy 为当前登录用户的 ID
go
// When creating a record, audit fields are automatically set
// POST /api
// {
//     "resource": "app/sys/user",
//     "action": "create",
//     "params": {
//         "username": "john",
//         "email": "john@example.com"
//     }
// }

// The created record will have:
// {
//     "id": "550e8400-e29b-41d4-a716-446655440000",
//     "username": "john",
//     "email": "john@example.com",
//     "createdAt": "2024-01-15T10:30:00Z",
//     "createdBy": "current-user-id",
//     "updatedAt": "2024-01-15T10:30:00Z",
//     "updatedBy": "current-user-id"
// }

更新记录时

当更新记录时,框架会自动:

  1. 更新 UpdatedAt 为当前时间
  2. 更新 UpdatedBy 为当前登录用户的 ID
  3. 保持 CreatedAtCreatedBy 不变
go
// When updating a record, only update fields are modified
// POST /api
// {
//     "resource": "app/sys/user",
//     "action": "update",
//     "params": {
//         "id": "550e8400-e29b-41d4-a716-446655440000",
//         "email": "john.new@example.com"
//     }
// }

// The updated record will have:
// {
//     "id": "550e8400-e29b-41d4-a716-446655440000",
//     "username": "john",
//     "email": "john.new@example.com",
//     "createdAt": "2024-01-15T10:30:00Z",  // Unchanged
//     "createdBy": "original-user-id",       // Unchanged
//     "updatedAt": "2024-01-15T11:00:00Z",  // Updated
//     "updatedBy": "current-user-id"         // Updated
// }

命名约定

VEF Framework 遵循以下命名约定:

数据库列名

使用 snake_case(下划线分隔):

  • id
  • created_at
  • created_by
  • updated_at
  • updated_by

JSON 字段名

使用 camelCase(驼峰命名):

  • id
  • createdAt
  • createdBy
  • updatedAt
  • updatedBy

Go 字段名

使用 PascalCase(大驼峰命名):

  • Id
  • CreatedAt
  • CreatedBy
  • UpdatedAt
  • UpdatedBy

获取审计用户名

默认情况下,CreatedByUpdatedBy 只存储用户 ID。如果需要在查询结果中包含用户名,可以使用 WithAuditUserNames 方法:

go
// Configure FindApi to include audit user names
FindPageApi: apis.NewFindPageApi[models.User, payloads.UserSearch]().
    WithAuditUserNames(&models.User{}),

// The response will include:
// {
//     "id": "...",
//     "username": "john",
//     "createdAt": "2024-01-15T10:30:00Z",
//     "createdBy": "user-001",
//     "createdByName": "Admin User",      // Added by WithAuditUserNames
//     "updatedAt": "2024-01-15T11:00:00Z",
//     "updatedBy": "user-002",
//     "updatedByName": "Another User"     // Added by WithAuditUserNames
// }

自定义审计字段

如果需要额外的审计字段,可以在模型中添加:

go
type Article struct {
    orm.Model
    
    Title       string      `bun:"title,notnull" json:"title"`
    Content     string      `bun:"content,notnull" json:"content"`
    
    // Custom audit fields
    PublishedAt null.Time   `bun:"published_at" json:"publishedAt"`
    PublishedBy null.String `bun:"published_by" json:"publishedBy"`
    
    ReviewedAt  null.Time   `bun:"reviewed_at" json:"reviewedAt"`
    ReviewedBy  null.String `bun:"reviewed_by" json:"reviewedBy"`
}

在钩子中设置自定义审计字段:

go
// Set custom audit fields in hooks
UpdateApi: apis.NewUpdateApi[models.Article, payloads.ArticleParams]().
    WithPreUpdate(func(oldModel, newModel *models.Article, params *payloads.ArticleParams, ctx fiber.Ctx, db orm.Db) error {
        // Set published audit fields when status changes to published
        if params.Status == "published" && oldModel.Status != "published" {
            now := time.Now()
            newModel.PublishedAt = null.TimeFrom(now)
            
            // Get current user from context
            if principal := contextx.GetPrincipal(ctx); principal != nil {
                newModel.PublishedBy = null.StringFrom(principal.Id)
            }
        }
        return nil
    }),

禁用审计字段

如果某些模型不需要审计字段,可以不嵌入 orm.Model,而是只定义需要的字段:

go
// Model without audit fields
type Config struct {
    Key   string `bun:"key,pk" json:"key"`
    Value string `bun:"value,notnull" json:"value"`
}

或者使用自定义的基础模型:

go
// Custom base model with only ID
type BaseModel struct {
    Id string `bun:"id,pk" json:"id"`
}

type Config struct {
    BaseModel
    Key   string `bun:"key,notnull,unique" json:"key"`
    Value string `bun:"value,notnull" json:"value"`
}

下一步

基于 Apache License 2.0 许可发布