package req 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 // ResFlags 是一组可以按位组合的权限检查开关,零值表示什么检查都不做。行级过滤 // (ResRow)和字段脱敏(ResMask)是两个独立维度,各自的 xxxRelations 位表示"连关联 // 资源一起处理",目前只保留位定义,还没接入判断逻辑。 type ResFlags int const ( // ResRow 行级权限过滤:按角色过滤能看到/操作的行,只处理当前资源自己 ResRow ResFlags = 1 << iota // ResRowRelations 行级权限过滤扩展到关联资源(依附于 ResRow,单独设置无效果,尚未实现) ResRowRelations // ResMask 字段级脱敏:按 ResField.GetRoles() 脱敏无权限查看/写入的字段,只处理当前资源自己 ResMask // ResMaskRelations 字段级脱敏扩展到关联资源(依附于 ResMask,单独设置无效果,尚未实现) ResMaskRelations // ResAll 常用组合:行级过滤 + 字段脱敏都开启(不含关联资源) ResAll = ResRow | ResMask ) const ( ResString ResDataType = "string" ResText ResDataType = "text" ResInteger ResDataType = "integer" ResSmallInteger ResDataType = "smallInteger" ResDecimal ResDataType = "decimal" ResBoolean ResDataType = "boolean" ResJson ResDataType = "json" ResEnum ResDataType = "enum" ResTimestamp ResDataType = "timestamp" ResDate ResDataType = "date" ResDatetime ResDataType = "dateTime" ResVector ResDataType = "vector" ) const ( IsExpr byte = 1 << iota IsOmitempty Ignored ) 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 DB() *db.Database BeginTransaction() (*db.TxDatabase, error) GetDBTable(User, ...ResOption) *db.SelectDataset GetStruct(extends ...reflect.StructField) any GetSliceStruct(extends ...reflect.StructField) any // GetHistoryRoles 配置了变更留痕的角色列表,为空表示不留痕 GetHistoryRoles() []string // 是否虚拟资源 IsVirtual() bool // 是否系统资源 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() 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 { ResField Type() RouteParamType Alias() string GetCodeOrAlias() string IsExpr() bool IsOmitempty() bool Ignored() bool SetOptions(byte) QueryField ToSql() db.Expression ToStructField(tags ...string) reflect.StructField }