From cc3f6d90e78deb28f7bc70fba1e1c27680d332a9 Mon Sep 17 00:00:00 2001 From: what Date: Tue, 21 Jul 2026 18:00:39 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84:=20ResAuthDB=20=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E4=B8=BA=20ResScope,=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=BF=87=E6=BB=A4=E8=8C=83=E5=9B=B4=E5=91=BD?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原名字跟"身份认证"(Auth)容易混淆,实际控制的是资源查询/写入时按角色过滤的范围(关闭/仅自身/含关联资源),改用 Scope 更准确。ResAuthOff/On/All 同步改名为 ResScopeOff/On/All,数值保持不变,兼容数据库里已有的 is_auth_db 存量数据。 --- resource.go | 80 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 12 deletions(-) mode change 100644 => 100755 resource.go diff --git a/resource.go b/resource.go old mode 100644 new mode 100755 index 46bd445..7f53b4a --- a/resource.go +++ b/resource.go @@ -4,19 +4,21 @@ import ( "reflect" "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "git.fsdpf.net/go/db/schema" + "git.fsdpf.net/go/reflux" ) type ResDataType string -type ResAuthDB int +type ResScope int const ( // 关闭权限过滤 - ResAuthOff ResAuthDB = iota + ResScopeOff ResScope = iota // 开启权限过滤, 不包括关联资源 - ResAuthOn + ResScopeOn // 开启权限过滤, 包括关联资源 - ResAuthAll + ResScopeAll ) const ( @@ -31,6 +33,7 @@ const ( ResTimestamp ResDataType = "timestamp" ResDate ResDataType = "date" ResDatetime ResDataType = "dateTime" + ResVector ResDataType = "vector" ) const ( @@ -43,22 +46,27 @@ type Resource interface { GetUuid() string GetName() string GetCode() string + GetDescription() string GetPrimarykey() string + GetConn() string GetFields() []ResField GetField(string) (ResField, bool) HasField(string) bool + // GetTable 原始表名 + GetTable() string + // GetTableExpr 可用于 FROM/JOIN 的表引用表达式 + GetTableExpr() exp.LiteralExpression - BeginTransaction() (*db.Transaction, error) + DB() *db.Database + BeginTransaction() (*db.TxDatabase, error) + + GetDBTable(User, ...ResOption) *db.SelectDataset - GetTable() db.Expression - GetDBConn() *db.Connection - GetDBBuilder() *db.Builder - GetDBTable(params ...any) *db.Builder - GetAuthDBTable(u User, params ...any) *db.Builder GetStruct(extends ...reflect.StructField) any GetSliceStruct(extends ...reflect.StructField) any - WithRolesCondition(b *db.Builder, t string, u User) error + // GetHistoryRoles 配置了变更留痕的角色列表,为空表示不留痕 + GetHistoryRoles() []string // 是否虚拟资源 IsVirtual() bool @@ -66,17 +74,65 @@ type Resource interface { IsSystem() bool } +// ResVirtualTable 虚拟表接口,由业务层实现,框架通过 SQLite vtab 机制调用。 +// Select 提供列表查询,Fetch 提供单条完整查询(含 Detail 补充字段), +// Insert/Update/Delete 支持写操作并自动同步缓存。 +type ResVirtualTable interface { + GetResource() Resource + + // Fetch 按主键获取单条完整数据,内部负责调用远端接口并补充 Detail 专属字段。 + // 框架在 item cache miss 时调用,结果写入 item cache 供后续查询复用。 + Fetch(pk any) (map[string]any, error) + + // Detail 对已有的 item 数据进行补充,填充列表接口不返回的详情字段。 + // changed=true 时框架会将修改后的数据回写 item cache。 + Detail(item reflux.R) (changed bool, err error) + + // Select 查询列表数据,filter 为过滤条件,pagesize/page 控制分页。 + // 返回的 items 每项为 map[string]any,total 为总记录数。 + Select(filter reflux.R, pagesize, page int) (items []any, total int64, err error) + + // Insert 新增一条记录,返回新记录的 rowid。 + Insert(item reflux.R) (rowid int64, err error) + + // Update 按 rowid 更新记录。 + Update(rowid any, item reflux.R) error + + // Delete 按 rowid 删除记录。 + Delete(rowid any) error +} + +// ResVirtualTableSetup 由 framework 在 sqlite_vtable build tag 下注册到容器,负责向 SQLite +// 注册虚拟表模块({res.GetTable()}_mod)。当前由上层在资源缓存构建时提前对所有 vtable 连接的资源 +type ResVirtualTableSetup func(res Resource) error + +// GetResource 按 code 或 uuid 查找已注册的资源,找不到返回 ok=false。放在 req 而不是 contracts, +// 是为了让 req 自己的子包(比如 resource 包)也能通过 DI 拿到查找能力,不用反过来 import +// contracts(contracts 依赖 req,import 反过来会成环)。contracts.GetResource 是它的类型别名, +// 兼容原有引用。 +type GetResource func(code string) (Resource, bool) + +// MustResource 按 code 或 uuid 查找已注册的资源,找不到时由具体实现负责报错(通常是 panic)。 +type MustResource func(code string) Resource + type ResField interface { GetName() string GetCode() string GetCodeResource() string GetDataType() ResDataType GetQueryDataType() RouteParamType - GetRawDefault(driver string) db.Expression + GetRawDefault() db.Expression ToStructField(tags ...string) reflect.StructField ToValue(any) any ToBlueprint(table *schema.Blueprint) *schema.ColumnDefinition ToQueryField(t RouteParamType, alias string, options byte) QueryField + IsVirtual() bool + // GetRoles 配置了"能读写该字段真实值"的角色列表,为空表示不限制(所有角色都能读写真实值); + // 非空时,用户所属角色只要有一个在列表里就有权限,否则:查询时这个字段会被替换成脱敏哨兵值 + // (见 resource 包的 MaskFunc),写入时这个字段会被静默丢弃。注意这跟 GetHistoryRoles 等 + // "空=没人有权限"的既有约定相反:这是后加的限制能力,必须保证未配置时不影响任何现有字段的 + // 读写行为。 + GetRoles() []string } type QueryField interface {