新增 resx 包剩余实现:字段/查询字段/脱敏/变更行等类型及测试

res_field.go/res_field_option.go(字段定义)、res_query_field.go/res_query_field_option.go(查询字段)、res_change_row.go(变更行)、res_mask_field.go(字段脱敏标记)及对应测试文件,配合此前已提交的 resource.go/res_interceptor.go 组成完整的 resx 资源实现包。
This commit is contained in:
2026-07-22 09:11:50 +08:00
parent 1868c8dd15
commit 09f4c40d67
10 changed files with 2427 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
package resx
import (
"database/sql"
"github.com/samber/lo"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/reflux"
"git.fsdpf.net/go/req"
)
// ResEventType 资源操作类型:读(SELECT+ 写(INSERT/UPDATE/DELETE
type ResEventType string
const (
ResEventSelect ResEventType = "SELECT"
ResEventInsert ResEventType = "INSERT"
ResEventUpdate ResEventType = "UPDATE"
ResEventDelete ResEventType = "DELETE"
)
// ResChangeEvent 描述一次写操作触发的变更:类型、资源、用户、操作结果,不对应具体某一行数据。
// 只对应写操作,Type() 恒为 INSERT/UPDATE/DELETE 之一,不会是 ResEventSelect。
type ResChangeEvent interface {
Type() ResEventType // INSERT | UPDATE | DELETE
Res() req.Resource // 触发这次变更的资源
User() req.User // 触发这次变更的用户
Result() sql.Result // 本次写操作的执行结果
}
// resChangeEvent 是 ResChangeEvent 的具体实现
type resChangeEvent struct {
typ ResEventType
res req.Resource
user req.User
result sql.Result
}
func (e resChangeEvent) Type() ResEventType { return e.typ }
func (e resChangeEvent) Res() req.Resource { return e.res }
func (e resChangeEvent) User() req.User { return e.user }
func (e resChangeEvent) Result() sql.Result { return e.result }
// NewResChangeEvent 构造一个 ResChangeEvent
func NewResChangeEvent(typ ResEventType, res req.Resource, u req.User, result sql.Result) ResChangeEvent {
return resChangeEvent{typ: typ, res: res, user: u, result: result}
}
// ResChangeRow 描述一次资源写操作中某一行的变更内容。只对应写操作,Type() 恒为
// INSERT/UPDATE/DELETE 之一,不会是 ResEventSelect。
type ResChangeRow interface {
Type() ResEventType // INSERT | UPDATE | DELETE
Res() req.Resource // 触发这次变更的资源
User() req.User // 触发这次变更的用户
ID() int64 // 行主键(INSERT 时也是已经算好的自增 id)
Old() reflux.R // 变更前快照,INSERT 时为 reflux.Nil
New() reflux.R // 本次写入值,DELETE 时为 reflux.Nil
Cur() reflux.R // Old 与 New 合并后的视图(New 覆盖 Old 同名字段)
// Tx 与原始写操作共享的事务,仅当调用方要写入的资源与原始资源使用同一个数据库连接时才可复用。
Tx() *db.TxDatabase
}
type resChangeRow struct {
typ ResEventType
res req.Resource
user req.User
id int64
old map[string]any
new map[string]any
tx *db.TxDatabase
}
func (r resChangeRow) Type() ResEventType { return r.typ }
func (r resChangeRow) Res() req.Resource { return r.res }
func (r resChangeRow) User() req.User { return r.user }
func (r resChangeRow) ID() int64 { return r.id }
func (r resChangeRow) Tx() *db.TxDatabase { return r.tx }
func (r resChangeRow) Old() reflux.R {
if len(r.old) == 0 {
return reflux.Nil
}
return reflux.New(r.old)
}
func (r resChangeRow) New() reflux.R {
if len(r.new) == 0 {
return reflux.Nil
}
return reflux.New(r.new)
}
func (r resChangeRow) Cur() reflux.R {
if len(r.old) == 0 && len(r.new) == 0 {
return reflux.Nil
}
return reflux.New(lo.Assign(r.old, r.new))
}
// ResChangeRowOption 用于设置 NewResChangeRow 里非必填的字段
type ResChangeRowOption func(r *resChangeRow)
// WithOld 设置变更前快照,不设置则为 reflux.Nil
func WithOld(old map[string]any) ResChangeRowOption {
return func(r *resChangeRow) { r.old = old }
}
// WithNew 设置本次写入值,不设置则为 reflux.Nil
func WithNew(new map[string]any) ResChangeRowOption {
return func(r *resChangeRow) { r.new = new }
}
// WithTx 设置与原始写操作共享的事务,不设置则为 nil
func WithTx(tx *db.TxDatabase) ResChangeRowOption {
return func(r *resChangeRow) { r.tx = tx }
}
// NewResChangeRow 构造一个 ResChangeRow
func NewResChangeRow(typ ResEventType, res req.Resource, u req.User, id int64, opts ...ResChangeRowOption) ResChangeRow {
r := resChangeRow{typ: typ, res: res, user: u, id: id}
for _, opt := range opts {
opt(&r)
}
return r
}