配合 v1-legacy 维护分支的拆分,记录 master 这条线(Condition/ConditionExpr 表达式树、TokenValue、JSON 序列化、engine 规则引擎)目前有哪些能力,跟 v1-legacy 分支做区分;顺带记一下 engine.go 那个已知的 nil user 问题。
3.2 KiB
git.fsdpf.net/go/condition(master)
条件表达式 DSL:把一组条件(等值/比较/模糊匹配/IN 等)组织成可嵌套的 AND/OR 树,渲染成
db-v2 的表达式(db.Expression),供权限过滤、规则引擎等场景复用同一套条件描述。配合
"-v2" 系列项目使用。
这个仓库有什么
Condition/ConditionExpr
Condition:条件树的节点,AppendTo/SetCondition/SetExpr组装子条件/表达式,ToSql(m TokenValue) db.Expression渲染成db-v2的表达式树——内部用exp.NewExpressionList(this.typ, conditions...)构造,不是手工拼 SQL 字符串。ConditionType是exp.ExpressionListType(AND/OR)的类型别名。ConditionExpr:单个表达式(字段 + 操作符 + 值),ConditionOperator是exp.BooleanOperation的类型别名(EQ/NE/GT/LIKE/IN等),跟db-v2的操作符 体系直接对齐,不用自己维护一份字符串常量再转换。util.go:ToConditionOperator(op string) ConditionOperator把字符串(比如从配置/请求 参数解析出来的)转成ConditionOperator。
TokenValue(token_value.go)
条件渲染时取值用的接口:GetParam(k string) valuex.Accessor(配合 reflux/valuex 统一属性
访问方式)+ User() req.User。NewTokenValue(x valuex.Accessor, opts ...tValueOpt) 构造,
WithUser(user) 显式指定用户;不传时会尝试从 x 上探测 User() 方法,两者都拿不到会
panic——TokenValue 必须绑定一个具体用户,不支持匿名/无用户场景。
JSON 序列化(condition_json.go)
Condition/ConditionExpr 都实现了 MarshalJSON/UnmarshalJSON(内部过一层 DTO 结构避免
递归),FromMap(m map[string]any) (*Condition, error) 直接从已解析好的 map 构造,不用先转
成 JSON 字符串再解析一遍——典型场景是权限配置、规则条件存在数据库的 JSON 列里,读出来直接
反序列化成 Condition 用。
engine 子包:规则引擎
Engine[T any]:按 Case(cond *condition.Condition, cb func(data T, g req.GlobalParams) error)
注册一组"条件 -> 回调"规则,Execute(data T) 对传入数据求值,命中第一个条件为真的分支就
调用对应回调(Default(cb) 兜底)。内部通过 db/engine(sqlite3 内存库)把 data(用
reflux.R 包装)当成一行虚拟数据跑 SQL 判断条件是否成立,不用自己写一套表达式求值器。
EngineOption(Debug()/Relation(...))控制调试日志和关联字段展开。
已知问题
engine.go 在调用方没有显式传 GlobalParams 时,会用 nil user 构造一个兜底值,这跟
TokenValue 现在强制要求非 nil user 冲突,TestEngine 目前会 panic——待 Engine.Execute
支持显式传 user 时一并解决,暂时未修。
分支状态
master 是持续开发中的新架构,跟老仓库 v1-legacy 分支不兼容——Condition.ToSql 从手工
拼 SQL 字符串换成了 db-v2 表达式树,TokenValue 从 req.GlobalParams 换成了
reflux/valuex,engine 包的连接管理也换成了 db/engine。还在用老版 API 的老项目,参见
v1-legacy 分支。