审计字段
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 字段 | 类型 | 说明 |
|---|---|---|---|---|
Id | id | id | string | 主键,自动生成 UUID |
CreatedAt | created_at | createdAt | time.Time | 创建时间 |
CreatedBy | created_by | createdBy | null.String | 创建人 ID |
UpdatedAt | updated_at | updatedAt | time.Time | 最后更新时间 |
UpdatedBy | updated_by | updatedBy | null.String | 最后更新人 ID |
自动维护机制
创建记录时
当创建新记录时,框架会自动:
- 生成 UUID 作为
Id - 设置
CreatedAt为当前时间 - 设置
CreatedBy为当前登录用户的 ID - 设置
UpdatedAt为当前时间 - 设置
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"
// }更新记录时
当更新记录时,框架会自动:
- 更新
UpdatedAt为当前时间 - 更新
UpdatedBy为当前登录用户的 ID - 保持
CreatedAt和CreatedBy不变
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(下划线分隔):
idcreated_atcreated_byupdated_atupdated_by
JSON 字段名
使用 camelCase(驼峰命名):
idcreatedAtcreatedByupdatedAtupdatedBy
Go 字段名
使用 PascalCase(大驼峰命名):
IdCreatedAtCreatedByUpdatedAtUpdatedBy
获取审计用户名
默认情况下,CreatedBy 和 UpdatedBy 只存储用户 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"`
}