Skip to content

075. Battice Service、自动路由与字段校验

学习目标

这一节把通用服务和自动接口真正跑起来。

学完后,你应该能理解:

  • BatticeService 如何包装 SQLModel 实体;
  • 如何从模型自动生成模块配置;
  • 单条查询如何复用分页查询;
  • 自动路由如何一次生成多个接口;
  • 字段校验规则如何覆盖默认配置。

测试保护代码逻辑

AI 或人工修改代码时,很容易不小心改掉原有逻辑。

如果底层 SQL 构建器有测试保护,问题会更早暴露。

例如原本空值字段应该被跳过。

如果有人把跳过逻辑删掉,测试会发现生成 SQL 多了不该出现的字段。

所以通用模块这种底层代码必须靠测试兜住。

否则上层接口看起来报错时,定位会很痛苦。

BatticeService

BatticeService 是对模型的通用服务包装。

可以把一个 SQLModel 实体类传进去。

它会提供一组通用方法,例如:

  • 分页查询;
  • 单条查询;
  • 新增;
  • 批量新增;
  • 更新;
  • 批量更新;
  • 删除。

这样业务代码可以不直接写 SQLModel 查询,也能使用通用模块能力。

从模型生成配置

通用模块需要模块配置。

手写配置很麻烦。

但 SQLModel 实体类里已经有很多信息:

  • 表名;
  • 字段名;
  • 字段类型;
  • 模型字段定义。

所以 BatticeService 可以根据实体类自动生成模块配置。

例如从模型上读取 __tablename__ 作为表名。

再遍历模型字段生成字段配置。

字段配置自动生成

生成出来的配置会包含字段列表。

例如用户模型里有:

  • 用户名;
  • 姓名;
  • 邮箱;
  • 是否有效;
  • 创建时间;
  • 更新时间。

这些字段会变成通用模块可以识别的列配置。

后续查询、筛选、返回数据都依赖这份配置。

单条查询

单条查询本质上复用分页查询。

例如要按用户名查用户:

py
{"username": "张三"}

服务会把这个字典转成筛选条件:

txt
username = 张三

然后调用列表查询。

只不过分页参数限制为只查一条。

这样单条查询也能复用字段转换、SQL 构建、拦截器等通用能力。

调试单条查询

调试时可以看到几个关键对象。

第一个是 modelConfig

它保存表名和字段配置。

第二个是查询请求体。

它会从简单字典变成标准查询参数。

第三个是生成的 SQL。

真实 SQL 仍然带占位符。

第四个是参数数组。

例如:

txt
["张三", 0, 1]

表示查询用户名为张三,并限制只查一条。

查询结果转换

数据库返回结果后,还要做转换。

例如:

  • 日期对象转字符串;
  • JSON 字符串转数组;
  • 逗号分隔字符串转数组;
  • 数据库字段命名转前端字段命名。

如果当前模型没有需要转换的字段,这一步不会明显改变结果。

但通用模块必须保留这个环节。

因为其他模块可能依赖它。

两套数据访问方式

项目里现在有两套方式。

第一套是 SQLModel。

它适合具体业务逻辑。

例如用户激活这种需要先查状态再决定更新的场景。

第二套是 Battice。

它适合通用后台表格能力。

例如列表查询、动态筛选、批量更新、快速生成接口。

这两套方式不是互斥关系。

按场景选择即可。

自动生成接口

add_battice_route 可以把一个实体类变成一组接口。

类似 LangServe 可以把模型对象变成接口服务。

通用模块也可以把数据模型变成增删改查接口。

一次会生成多类接口。

常见包括:

  • 分页查询;
  • 单条查询;
  • 新增;
  • 批量新增;
  • 更新;
  • 批量更新;
  • 删除。

这样新增一个模块时,不需要手写每个接口。

路由前缀

自动生成接口时会有一个统一前缀。

例如:

txt
/generic/battice-demo/list

其中:

  • generic 是通用接口前缀;
  • battice-demo 是模块标识;
  • list 是动作类型。

接口内部会调用对应服务函数。

分页查询接口

分页查询接口支持通用查询参数。

例如按姓名模糊查询。

也可以只查总数。

还可以在分页查询同时返回总数。

这些能力都来自前面写好的查询构建器。

接口层只是把请求接到服务函数上。

拦截器类型类比

拦截器动作名称类似事件名。

前端里会监听:

js
click

通用模块里会监听:

txt
beforeInsert
afterInsert
beforeList
afterList

如果没有定义这个动作类型,就无法注册对应拦截器。

所以类型定义不是多余的。

它规定了系统允许哪些扩展点。

字段校验覆盖

自动生成配置只能给出默认字段信息。

但业务经常需要额外规则。

例如金额不能超过 100 万。

这时可以通过额外列配置覆盖默认字段配置。

例如给 amount 配:

  • 类型是数字;
  • 必填;
  • 最小值;
  • 最大值;
  • 错误提示。

这样同一个模型可以生成不同接口。

一个接口不做额外校验。

另一个接口增加业务校验。

校验效果

如果普通接口没有配置金额上限,大金额也能写入。

如果校验接口配置了金额最大值,再提交超过限制的值,就会返回错误。

这说明通用模块的字段配置不是只用于展示。

它也参与后端数据校验。

这一节的重点

这一节让通用模块真正从代码结构变成可调用接口。

你需要记住:

  • BatticeService 把 SQLModel 实体包装成通用服务;
  • 模块配置可以从模型字段自动生成;
  • 单条查询可以复用分页查询;
  • add_battice_route 可以一次生成多类接口;
  • 拦截器动作名像事件名,必须先定义才能注册;
  • 额外字段配置可以覆盖默认配置,并用于后端校验;
  • 测试用例是防止底层逻辑被改坏的关键保护。

AI Agent 课程学习文档。