From 5cdf1cc00dc201c07e6f343cf1fdea2fd23a43fb Mon Sep 17 00:00:00 2001 From: what Date: Thu, 20 Aug 2026 15:58:03 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84:=20=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=94=B9=E7=94=A8=20NewVirtualResource=20?= =?UTF-8?q?=E6=98=BE=E5=BC=8F=E6=9E=84=E9=80=A0=EF=BC=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=20WithVirtual?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WithVirtual(bool) 只是给 New() 打一个标记位,虚拟资源需要的表达式(子查询)没有 承载的地方,之前借用 table string 字段硬拼字符串。改成 NewVirtualResource(res, code, table, opts...) 专门构造:table 参数直接接收 exp.SQLExpression, GetTableExpr 对虚拟资源直接用它生成子查询表达式,不用再手工拼括号和字符串; container/conn/historyRoles 从传入的父资源上直接复用。 GetTableExpr 返回值类型从 exp.LiteralExpression 放宽成 exp.Aliaseable, 兼容 db.T(...)(IdentifierExpression)和 db.L(...)(LiteralExpression)两种 返回值。 --- resource.go | 2 +- resx/res_interceptor_test.go | 9 ++++----- resx/resource.go | 17 ++++++++--------- resx/virtual_resource.go | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 resx/virtual_resource.go diff --git a/resource.go b/resource.go index 349e023..d927c81 100755 --- a/resource.go +++ b/resource.go @@ -64,7 +64,7 @@ type Resource interface { // GetTable 原始表名 GetTable() string // GetTableExpr 可用于 FROM/JOIN 的表引用表达式 - GetTableExpr() exp.LiteralExpression + GetTableExpr() exp.Aliaseable DB() *db.Database BeginTransaction() (*db.TxDatabase, error) diff --git a/resx/res_interceptor_test.go b/resx/res_interceptor_test.go index 57d9d1c..cd6cd3b 100644 --- a/resx/res_interceptor_test.go +++ b/resx/res_interceptor_test.go @@ -565,14 +565,13 @@ func (t *hooksTest) TestVirtualResource_SkipsRolesConditionAndOnAfter() { }, nil }) - res := resx.New(app, "User", "users", - resx.WithConn("default"), - resx.WithVirtual(true), + baseRes := resx.New(app, "User", "users", resx.WithConn("default")) + res := resx.NewVirtualResource(baseRes, "User", db.From(db.T("users")), resx.WithFields(resx.NewResField("name", "User", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString))), ) - // 实际生成的 SQL(虚拟资源用字面量拼表名,且没有 owned_user 权限条件): - // UPDATE (users) AS `User` SET `name`='新名字' WHERE (`id` = 1) + // 实际生成的 SQL(虚拟资源拿子查询拼表名,且没有 owned_user 权限条件): + // UPDATE (SELECT * FROM `users`) AS `User` SET `name`='新名字' WHERE (`id` = 1) sql, _, _ := res.GetDBTable(base.GetAnonymous()).Update(). Set(db.Record{"name": "新名字"}). Where(db.C("id").Eq(1)). diff --git a/resx/resource.go b/resx/resource.go index f269e8c..7566351 100644 --- a/resx/resource.go +++ b/resx/resource.go @@ -95,6 +95,10 @@ type resource struct { conn string isResVirtual bool table string + // virtualTable 只有虚拟资源(NewVirtualResource 构造的)才会设置,table 留空。GetTableExpr + // 用它代替 table 拼子查询:db.V(sd.Expression()) 让方言自己生成/加括号,不用手写 "(" + sql + ")" + // 拼字符串。 + virtualTable exp.SQLExpression primarykey string historyRoles []string fields []req.ResField @@ -150,11 +154,6 @@ func WithConn(v string) Option { return func(r *resource) { r.conn = v } } -// WithVirtual 标记为虚拟资源 -func WithVirtual(v bool) Option { - return func(r *resource) { r.isResVirtual = v } -} - // WithPrimarykey 设置主键字段名,不设置则默认为 "id" func WithPrimarykey(v string) Option { return func(r *resource) { r.primarykey = v } @@ -290,17 +289,17 @@ func (this *resource) autoCreateTable(conn *db.Database) error { }) } -// GetTable 原始表名 +// GetTable 原始表名;虚拟资源没有物理表名,返回空字符串 func (this *resource) GetTable() string { return this.table } // GetTableExpr 获取资源对应的可用于 FROM/JOIN 的表引用表达式 -func (this *resource) GetTableExpr() exp.LiteralExpression { +func (this *resource) GetTableExpr() exp.Aliaseable { if this.isResVirtual { - return db.L("(" + this.table + ")") + return db.L("?", this.virtualTable.Clone()) } - return db.V(db.T(this.table)) + return db.T(this.table) } // GetDBTable 获取资源对应的数据库连接 diff --git a/resx/virtual_resource.go b/resx/virtual_resource.go new file mode 100644 index 0000000..6a7fff3 --- /dev/null +++ b/resx/virtual_resource.go @@ -0,0 +1,33 @@ +package resx + +import ( + "sync" + + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/req" +) + +func NewVirtualResource(res req.Resource, code string, table exp.SQLExpression, opts ...Option) req.Resource { + pRes, ok := res.(*resource) + if !ok { + panic("resx.NewVirtualResource: res 必须是 resx.New 构造出来的 req.Resource") + } + + r := &resource{ + container: pRes.container, + conn: pRes.conn, + historyRoles: pRes.historyRoles, + initOnce: &sync.Once{}, + uuid: code, + code: code, + name: code, + table: code, + isResVirtual: true, + virtualTable: table, + primarykey: "id", + } + for _, opt := range opts { + opt(r) + } + return r +}