112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
"""通用高级筛选器出入参(需求 2.7.1)。
|
||||
|
|
|
|||
|
|
请求体形如:
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"model": "student",
|
|||
|
|
"rules": [
|
|||
|
|
{"field": "age", "operator": ">", "value": 25},
|
|||
|
|
{"field": "gender", "operator": "=", "value": "男"},
|
|||
|
|
{"logic": "OR", "sub_rules": [
|
|||
|
|
{"field": "salary", "operator": ">=", "value": 15000},
|
|||
|
|
{"field": "class_name", "operator": "like", "value": "Java"}
|
|||
|
|
]}
|
|||
|
|
],
|
|||
|
|
"order_by": "age",
|
|||
|
|
"order": "desc",
|
|||
|
|
"page": 1,
|
|||
|
|
"page_size": 10
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from typing import Any, Literal
|
|||
|
|
|
|||
|
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|||
|
|
|
|||
|
|
from app.schema.common import SortOrder
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FilterRule(BaseModel):
|
|||
|
|
"""一条规则 = 一个条件 或 一组子规则。"""
|
|||
|
|
|
|||
|
|
field: str | None = Field(None, description="字段名,与 sub_rules 二选一")
|
|||
|
|
operator: str | None = Field(
|
|||
|
|
None,
|
|||
|
|
description="操作符 > < = != >= <= like in not_like not_in between is_null not_null",
|
|||
|
|
)
|
|||
|
|
value: Any = Field(None, description="比较值;in/not_in 传数组,between 传 [a, b]")
|
|||
|
|
|
|||
|
|
logic: Literal["AND", "OR", "and", "or"] | None = Field(None, description="子规则之间的逻辑,AND / OR")
|
|||
|
|
sub_rules: list["FilterRule"] | None = Field(None, description="嵌套子规则,可多层")
|
|||
|
|
|
|||
|
|
@model_validator(mode="after")
|
|||
|
|
def _check(self):
|
|||
|
|
is_group = bool(self.sub_rules)
|
|||
|
|
if is_group:
|
|||
|
|
if self.logic is None:
|
|||
|
|
self.logic = "AND"
|
|||
|
|
return self
|
|||
|
|
if not self.field or not self.operator:
|
|||
|
|
raise ValueError("叶子规则必须同时提供 field 和 operator")
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
@field_validator("operator")
|
|||
|
|
@classmethod
|
|||
|
|
def _lower(cls, v): # noqa: ANN001, ANN201
|
|||
|
|
return v.strip().lower() if isinstance(v, str) else v
|
|||
|
|
|
|||
|
|
|
|||
|
|
FilterRule.model_rebuild()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class QueryRequest(BaseModel):
|
|||
|
|
model: str = Field("student", description="目标模型:student / class / teacher / score / employment / advisor")
|
|||
|
|
rules: list[FilterRule] = Field(default_factory=list, description="筛选规则树")
|
|||
|
|
order_by: str | None = Field(None, description="排序字段(同样受白名单限制)")
|
|||
|
|
order: SortOrder = Field(SortOrder.ASC, description="排序方向 asc / desc")
|
|||
|
|
page: int = Field(1, ge=1)
|
|||
|
|
page_size: int = Field(10, ge=1, le=200)
|
|||
|
|
fields: list[str] | None = Field(None, description="指定返回字段,不传返回全部可读字段")
|
|||
|
|
with_explain: bool = Field(True, description="是否返回规则解析后的说明,方便调试规则")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MetricSpec(BaseModel):
|
|||
|
|
func: Literal["count", "count_distinct", "avg", "sum", "max", "min"] = Field(..., description="聚合函数")
|
|||
|
|
field: str | None = Field(None, description="聚合字段;count 时可省略")
|
|||
|
|
alias: str | None = Field(None, description="结果列名")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AggregationParams(BaseModel):
|
|||
|
|
model: str = Field(..., description="目标模型")
|
|||
|
|
group_by: list[str] = Field(default_factory=list, description="分组字段,可为空=整体聚合")
|
|||
|
|
metrics: list[MetricSpec] = Field(..., min_length=1, description="聚合指标")
|
|||
|
|
rules: list[FilterRule] = Field(default_factory=list, description="筛选规则树")
|
|||
|
|
having: list[FilterRule] = Field(default_factory=list, description="对聚合结果的过滤(HAVING)")
|
|||
|
|
order_by: str | None = Field(None, description="按结果列名排序")
|
|||
|
|
order: SortOrder = SortOrder.DESC
|
|||
|
|
limit: int = Field(50, ge=1, le=500)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AggregationResult(BaseModel):
|
|||
|
|
columns: list[str] = Field(default_factory=list)
|
|||
|
|
rows: list[dict[str, Any]] = Field(default_factory=list)
|
|||
|
|
total: int = 0
|
|||
|
|
sql: str | None = Field(None, description="实际执行的 SQL(已参数化,便于讲解与排查)")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FieldMeta(BaseModel):
|
|||
|
|
field: str
|
|||
|
|
type: str
|
|||
|
|
desc: str
|
|||
|
|
sortable: bool = True
|
|||
|
|
filterable: bool = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ModelMeta(BaseModel):
|
|||
|
|
model: str
|
|||
|
|
label: str
|
|||
|
|
fields: list[FieldMeta] = Field(default_factory=list)
|
|||
|
|
relations: list[str] = Field(default_factory=list, description="可通过 JOIN 使用的关联字段")
|