Appearance
基服务
关于服务
- 服务层的职责是处理业务逻辑,一般也会先做业务层面的参数合法性检查,如用户名是否存在、余额是否足够,再继续完成业务的逻辑编排、事务控制等。
- 控制器和服务隔离的一大好处是,服务层不依赖 HTTP 请求参数,你可以使用其他方式调用服务(方便扩展和测试等),只要把参数传好服务就能单独跑。
- 基服务代码位于
internal\service\base.go。
基服务 service.Service[T] 封装了一些通用方法:
| 方法名 | 作用 | 实现 |
|---|---|---|
| Get | 查询单条记录 | 通过 BuildRepoOpts 组装仓储层 Get 方法需要的选项;然后调用仓储层 Get 方法 |
| List | 查询数据列表 | 同上;支持筛选、排序、分页 等 |
| Count | 获取数据条数 | 同上;使用 List 相同筛选条件,但忽略分页 |
| Delete | 批量删除数据 | 同上;调用仓储层批量删除方法 |
| Create | 创建一条数据 | 同上;额外接受控制器传递的数据实体,透传至仓储 |
| Update | 更新一条数据 | 同上 |
| Sort | 排序 | 使用 增量重排法 对数据进行排序,前端的拖拽排序功能的实现 |
创建、更新、删除
这几个功能主要是选项和数据的透传,并无特殊,核心在于 BuildRepoOpts 方法,它简单的构建好仓储层需要的所有选项数据后就传递到仓储做出入库了。
查询数据列表
服务层之下的仓储层极简(只负责把数据从 DB 搬到内存,或反过来),而 查询数据列表 包含了 排序、复杂 where 组装、分页、关联表 where 等很多功能,此时就引入了服务层 List 方法的核心理念:
抽 Scopes
Scopes是GORM提供的查询片段复用机制:把一段可复用的查询逻辑(条件、排序、分页、关联等任何)封装成一个函数,命名后可以在多条查询里重复叠加使用。
分页是一个 Scope,排序也是,筛选条件是,关联表的筛选条件等都是 Scope,如此一来,仓储层只需要一句 Scopes(opts.Scopes...) 即可应用所有查询片段,我们封装了如下抽 Scopes 的方法,你也可以在子业务模块使用它们:
| 方法 | 作用 | 说明 |
|---|---|---|
BuildOrderScope | 构建排序 Scope | 用户排序优先,字段不存在则跳过;最后追加主键 DESC 托底,保证分页结果稳定 |
BuildPaginateScope | 构建分页 Scope | 分页;page < 1 置 1,limit < 1 置 10 |
BuildWhereScopes | 构建 Where Scope | 功能特别强大的 Where 组装器,前端传递 Where 数据可参考手动拼装表格筛选数据 的 查询筛选条件预设 |
BuildScopes | 统一调用 | 依次组装 Where → Order → Paginate 三个 scope |
查询单条数据
系统中此方法用于获取单条编辑数据,也可以理解为 获取详情 的方法;它在执行查询时,也调用了 Scopes(opts.Scopes...),所以如果你在查询单条数据时有什么想法,可以如上 List 方法,写 Scopes 通过 opts 传进去。
排序
排序的实现,使用了 增量重排法 或叫 区间位移法(而不是交换法或全量重排),特点是所见即所得,前端想怎么排就怎么排,而且会尽量减少波及行;
对其实现感兴趣可以翻翻源码,一般情况下直接使用就行了,后台可视化创建CRUD时选择一个 权重 字段,前端就会自带拖拽排序功能。
选项
如下接口定义代码片段,您可以注意到很多方法都接受一个 opts Options(服务层选项,仓储层也有,请注意区分)。
go
// IService 通用服务接口
type IService[T any] interface {
Create(ctx context.Context, tri *bindx.Tri[T], opts Options) error
Get(ctx context.Context, opts Options) (*T, error)
List(ctx context.Context, opts Options) ([]T, error)
Count(ctx context.Context, opts Options) (int64, error)
}我们预设了一系列选项,除 Sort 以外的每个方法所需要的选项都可以在此找到,但并非每个方法都会使用全部的选项;方法们直接接受整个 opts,而无需将选项逐一定义为方法的参数(未来扩展参数也会方便很多)。
| 字段 | 类型 | 作用 |
|---|---|---|
OmitFields | []string | 排除出入库字段(黑名单) |
SelectFields | []string | 选择出入库字段(白名单) |
Wheres | []WhereGroup | 查询条件组 |
SortField | string | 排序字段 |
SortOrder | string | 排序方式(asc / desc) |
Page | int | 页码 |
Limit | int | 每页条数 |
Selector | bool | 请求是否来自选择器(前端标记,一般是下拉框做不同渲染使用) |
PrimaryKeyValue | string | 主键值,定位单行 |
PrimaryKeyValues | []string | 主键切片,批量删除 |
Extension | any | 任意自定义扩展参数 |
Preloads | []repository.Preload | 预加载关联 |
