自己写的,各位看官可以自由点评下,用的是GO GRPC GRPC-GATEWAY,自己封装的框架。所以好不好看个人了
先上proto定义代码, 这是我项目的需要返回的分页数据结构代码, GRPC方法只要返回源数据,gateway封装了 具体返回给接口的数据格式
//分页返回数据结构
message PageListRsp {
PageMessage pageMessage = 1 [json_name="pageMessage"];
repeated google.protobuf.Any tableList = 2 [json_name="tableList"];
}
//分页
message PageMessage {
int32 count =1 [json_name="count"]; //总条数
}
在上实现代码
package list
import (
"context"
"errors"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
"gorm.io/gorm"
"ppwc-go/global"
"ppwc-go/proto/gen/message/common"
)
//分页查询通用
type Config struct {
Page int //查询页数
Limit int //查询条数
Start int //查询开始
Where string //查询条件
WhereValue []any //查询条件所替换的参数
Select string //查询字段
OrderBy string //排序
Group string //group by
Model any //指定表或者指定主表模型
Join string //联表查询 类似 "join profiles on profiles.user_id = users.id" 可以left join right 等等
}
type ListReq[T any] struct {
Custom bool //是否自定义结果的结构体,假如你查询某个表分页,不用传,默认, 如果你是几个表查询,然后汇集到特定结果的 struct中, 传true
Data *[]T
}
// 查询,默认第一页,查10条
func List[T any](ctx context.Context, req ListReq[T], protoFunc protoMessage, opts ...Option) (*common.PageListRsp, error) {
//设置参数
config := Config{}
for _, opt := range opts {
opt(&config)
}
//默认查询页数和条数
if config.Page < 1 || config.Limit < 1 {
WithPageLimit(1, 10)
}
//执行分页查询组装
if config.Model == nil {
return nil, errors.New("请用 WithModel 设置主表模型")
}
db := global.Db.WithContext(ctx).Model(config.Model)
//封装查询
if config.Join != "" {
db.Joins(config.Join)
}
if config.Where != "" {
if isValidSlice(config.WhereValue) {
db.Where(config.Where, config.WhereValue...)
} else {
db.Where(config.Where)
}
}
if config.Group != "" {
db.Group(config.Group)
}
//查询总条数
var (
err error
count int64
)
if err = db.Count(&count).Error; err != nil {
return nil, err
}
//封装其他查询
if config.Select != "" {
db.Select(config.Select)
}
if config.OrderBy != "" {
db.Order(config.OrderBy)
}
db.Offset(config.Start).Limit(config.Limit)
//查询数据
if req.Custom {
err = db.Scan(&req.Data).Error
} else {
err = db.Find(&req.Data).Error
}
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
//转换格式
var anys []*anypb.Any
if len(*req.Data) > 0 {
for _, cour := range *req.Data {
tmp, err := anypb.New(protoFunc(cour))
if err != nil {
return nil, err
}
anys = append(anys, tmp)
}
}
return &common.PageListRsp{
PageMessage: &common.PageMessage{Count: int32(count)},
TableList: anys,
}, nil
}
func isValidSlice(values []any) bool {
for _, v := range values {
switch val := v.(type) {
case nil:
// 直接是 nil
continue
case []interface{}:
// 如果是切片,递归检查
if !isValidSlice(val) {
continue
}
return true
default:
return true
}
}
return false
}
type protoMessage func(any2 any) protoreflect.ProtoMessage
type Option func(*Config)
// 连表
func WithJoin(join string) Option {
return func(config *Config) {
config.Join = join
}
}
// 设置主模型 必须带
func WithModel(model any) Option {
return func(config *Config) {
config.Model = model
}
}
// 设置Group byu
func WithGroup(group string) Option {
return func(config *Config) {
config.Group = group
}
}
// 设置查询字段
func WithSelect(selects string) Option {
return func(config *Config) {
config.Select = selects
}
}
// 设置排序
func WithOrderBy(order string) Option {
return func(config *Config) {
config.OrderBy = order
}
}
// 设置查询
func WithWhere(where string, values ...any) Option {
return func(config *Config) {
config.Where = where
config.WhereValue = values
}
}
// 设置查询页数和条数
func WithPageLimit(page, limit int) Option {
if page < 1 {
page = 1
}
if limit < 1 {
limit = 10
}
start := (page - 1) * limit
return func(config *Config) {
config.Limit = limit
config.Page = page
config.Start = start
}
}
接下来是调用代码
// 获取运输商列表
func (t *SettingServices) GetTransporterList(ctx context.Context, req *settingProto.ListSearch) (result *commonProto.PageListRsp, err error) {
// 获取数据库运输商列表信息集合
var whereValue []any
where := "(deleted_at is null OR deleted_at = 0)"
if req.Search != "" {
where += " and courier_name like ?"
whereValue = []any{"%" + req.Search + "%"}
}
return list.List(
ctx,
list.ListReq[model.Courier]{
Data: &([]model.Courier{}),
},
func(any2 any) protoreflect.ProtoMessage {
cour := any2.(model.Courier)
return proto.Message(&settingProto.Courier{
Express: cour.CourierCode,
Name: cour.CourierName,
Logo: cour.CourierLogo,
Sort: cour.Sort,
})
},
list.WithPageLimit(int(req.Page), int(req.Limit)),
list.WithModel(&model.Courier{}),
list.WithOrderBy("sort asc"),
list.WithWhere(where, whereValue),
list.WithSelect("courier_code, courier_name, courier_logo, country_code, courier_phone, courier_url, sort"),
)
}
主要用泛型和any 来实现入参和出参,至少不用每个model 都写一个分页查询的了