feat: Condition/ConditionExpr 支持 JSON 序列化/反序列化

给 Condition/ConditionExpr 加 MarshalJSON/UnmarshalJSON,中间过一层 DTO
(conditionDTO/exprDTO)避免 UnmarshalJSON 递归。FromMap(m map[string]any)
方便从已经解析好的 map(比如请求参数)直接构造 Condition,不用先序列化
成 JSON 字符串再解析一遍。
This commit is contained in:
2026-08-21 08:57:07 +08:00
parent 68cd65f70d
commit a14d0f2c72
2 changed files with 287 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
package condition
import (
"encoding/json"
"strings"
)
// conditionDTO 是 Condition 的 JSON 中间结构,用于避免 UnmarshalJSON 递归
type conditionDTO struct {
Type string `json:"type"`
Describe string `json:"describe,omitempty"`
Exprs []*exprDTO `json:"exprs,omitempty"`
Childrens []*conditionDTO `json:"childrens,omitempty"`
}
type exprDTO struct {
Operator string `json:"operator"`
Field string `json:"field"`
FieldResource string `json:"fieldResource"`
FieldSqlFunc string `json:"fieldSqlFunc,omitempty"`
FieldSqlFuncParam string `json:"fieldSqlFuncParam,omitempty"`
IgnoreEmptyParam bool `json:"ignoreEmptyParam,omitempty"`
TokenType TokenType `json:"tokenType"`
Token string `json:"token"`
}
func (this *Condition) UnmarshalJSON(data []byte) error {
var dto conditionDTO
if err := json.Unmarshal(data, &dto); err != nil {
return err
}
conditionFromDTO(this, &dto)
return nil
}
func (this Condition) MarshalJSON() ([]byte, error) {
return json.Marshal(conditionToDTO(&this))
}
func (this *ConditionExpr) UnmarshalJSON(data []byte) error {
var dto exprDTO
if err := json.Unmarshal(data, &dto); err != nil {
return err
}
*this = *exprFromDTO(&dto)
return nil
}
func (this ConditionExpr) MarshalJSON() ([]byte, error) {
return json.Marshal(exprToDTO(&this))
}
// FromMap 将 map[string]any 转换为 *Condition
func FromMap(m map[string]any) (*Condition, error) {
data, err := json.Marshal(m)
if err != nil {
return nil, err
}
cond := New()
if err := json.Unmarshal(data, cond); err != nil {
return nil, err
}
return cond, nil
}
func conditionFromDTO(c *Condition, dto *conditionDTO) {
if strings.EqualFold(dto.Type, "OR") {
c.typ = OR
} else {
c.typ = AND
}
c.describe = dto.Describe
for _, e := range dto.Exprs {
c.SetExpr(exprFromDTO(e))
}
for _, child := range dto.Childrens {
childCond := &Condition{}
conditionFromDTO(childCond, child)
c.SetCondition(childCond)
}
}
func conditionToDTO(c *Condition) *conditionDTO {
typ := "AND"
if c.typ == OR {
typ = "OR"
}
dto := &conditionDTO{Type: typ, Describe: c.describe}
for _, e := range c.exprs {
dto.Exprs = append(dto.Exprs, exprToDTO(e))
}
for _, child := range c.childrens {
dto.Childrens = append(dto.Childrens, conditionToDTO(child))
}
return dto
}
func exprFromDTO(dto *exprDTO) *ConditionExpr {
tokenType := dto.TokenType
if tokenType == "" {
tokenType = STRING
}
return &ConditionExpr{
operator: ToConditionOperator(dto.Operator),
field: dto.Field,
fieldResource: dto.FieldResource,
fieldSqlFunc: dto.FieldSqlFunc,
fieldSqlFuncParam: dto.FieldSqlFuncParam,
ignoreEmptyParma: dto.IgnoreEmptyParam,
tokenType: tokenType,
token: dto.Token,
}
}
func exprToDTO(e *ConditionExpr) *exprDTO {
return &exprDTO{
Operator: operatorToString(e.operator),
Field: e.field,
FieldResource: e.fieldResource,
FieldSqlFunc: e.fieldSqlFunc,
FieldSqlFuncParam: e.fieldSqlFuncParam,
IgnoreEmptyParam: e.ignoreEmptyParma,
TokenType: e.tokenType,
Token: e.token,
}
}
func operatorToString(op ConditionOperator) string {
switch op {
case IS_NULL:
return "IS NULL"
case IS_NOT_NULL:
return "IS NOT NULL"
case EQ:
return "="
case NE:
return "!="
case GT:
return ">"
case GE:
return ">="
case LT:
return "<"
case LE:
return "<="
case LIKE:
return "LIKE"
case NOT_LIKE:
return "NOT LIKE"
case IN:
return "IN"
case NOT_IN:
return "NOT IN"
case REGEXP:
return "REGEXP"
case NOT_REGEXP:
return "NOT REGEXP"
default:
return "="
}
}