github.com/jianyuezhexue/base/v3


Install
go get github.com/jianyuezhexue/base/v3

Documentation

Base - 业务模型基础能力封装库

Go Version License Go Report Card

基于 Go 泛型的业务模型基础能力封装,实现充血模型架构,助力高效、规范的业务开发。

目录

概述

base 是一个面向企业级应用开发的 Go 基础库,采用**充血模型(Rich Domain Model)**设计思想,通过泛型封装为业务实体提供开箱即用的基础能力。

解决的问题

  • 重复代码:不同业务模型重复编写 CRUD、校验、审计等基础代码
  • 过程式编程:业务复杂时代码难以维护,缺乏面向对象的设计思维
  • 代码质量:通过标准化流程和封装,提升代码一致性和可维护性
  • 开发效率:沉淀通用能力,让开发者专注于业务逻辑

适用场景

  • 企业级 Web 应用开发
  • 需要复杂业务逻辑编排的领域
  • 追求代码规范和长期维护的项目
  • 基于 Gin + GORM 的 Go 项目

特性

  • 泛型支持:基于 Go 1.18+ 泛型实现类型安全的基类封装
  • 标准 CRUD:内置增删改查、分页查询、批量操作等 40+ 基础方法
  • 数据校验:Validate / Repair / Complete 三段式数据处理流程
  • 状态机:内置有限状态机(FSM)支持,轻松实现工作流状态流转
  • 审计追踪:自动维护创建人、更新人、时间戳等审计字段
  • 事务管理:支持跨实体的事务编排,防止重复开启事务
  • 搜索 DSL:基于结构体标签的声明式查询条件构建
  • SQL 注入防护:字段名校验机制,防止非法 SQL 注入
  • 本地缓存:实体级缓存,支持 TTL 过期策略

快速开始

安装

go get github.com/jianyuezhexue/base

依赖要求

  • Go 1.18 或更高版本
  • Gin - Web 框架
  • GORM - ORM 框架
  • FSM - 状态机

最小示例

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/jianyuezhexue/base"
    "gorm.io/gorm"
)

// 1. 定义业务实体
type Product struct {
    base.BaseModel[Product]
    Name  string  `json:"name" gorm:"column:name"`
    Price float64 `json:"price" gorm:"column:price"`
    Code  string  `json:"code" gorm:"column:code"`
}

func (p *Product) TableName() string {
    return "products"
}

// 2. 实现业务接口
type ProductInterface interface {
    base.BaseModelInterface[Product]
}

// 3. 实现必要钩子
func (p *Product) Validate() error {
    if p.Name == "" {
        return fmt.Errorf("商品名称不能为空")
    }
    if p.Price <= 0 {
        return fmt.Errorf("商品价格必须大于0")
    }
    return nil
}

func (p *Product) Repair() error {
    // 自动修复逻辑,如设置默认值
    if p.Code == "" {
        p.Code = generateCode()
    }
    return nil
}

func (p *Product) Complete() error {
    // 数据补全逻辑,如查询关联数据
    return nil
}

// 4. 实体构造函数
func NewProduct(ctx *base.BaseContext, db *gorm.DB) ProductInterface {
    entity := &Product{}
    entity.BaseModel = base.NewBaseModel(ctx, db, entity.TableName(), entity)
    return entity
}

架构设计

分层架构

┌─────────────────────────────────────────┐
│           Logic 层(业务逻辑层)           │
│     - 编排多个实体能力                   │
│     - 处理事务边界                       │
│     - 实现复杂业务流程                   │
├─────────────────────────────────────────┤
│         Entity 层(领域实体层)            │
│     - 业务数据 + 业务行为                │
│     - 继承 BaseModel 基础能力            │
│     - 实现领域特定方法                   │
├─────────────────────────────────────────┤
│          Base 层(基础能力层)             │
│     - CRUD 操作                          │
│     - 数据校验 & 修复                    │
│     - 状态机 & 审计追踪                  │
└─────────────────────────────────────────┘

核心设计原则

  1. 领域划分清晰:按业务领域组织代码,每个领域包含多个业务实体
  2. 充血模型:实体封装数据和行为,避免贫血模型的过程式代码
  3. 组合优于继承:通过组合 BaseModel 获得能力,而非深继承链
  4. 标准化流程:定义统一的 Input/Output/Complex 业务处理流程

核心概念

标准操作流程(SOP)

Input 方向(写入操作)

接收参数 → 实例化实体 → 填充数据 → 数据校验 → 数据修复 →
查询旧数据 → 业务校验 → 保存/更新 → 记录日志

Output 方向(查询操作)

接收参数 → 实例化实体 → 构造条件 → 查询数据 → 数据完善 → 返回结果

复杂业务逻辑

实例化多实体 → 设置/加载数据 → 调用实体能力A → 开启事务 →
调用实体能力B → 事务提交/回滚

核心接口

// BaseModelInterface 定义了 40+ 基础能力
type BaseModelInterface[T any] interface {
    // 数据操作
    Create() (*T, error)                                    // 创建
    CreateWithData(data *T) (*T, error)                     // 使用指定数据创建
    Update() (*T, error)                                    // 更新
    UpdateWithData(data *T) (*T, error)                     // 使用指定数据更新
    Del(ids ...uint64) error                                // 删除

    // 数据加载(加载到当前实体)
    LoadById(id uint64, preloads ...PreloadsType) (*T, error)
    LoadByBusinessCode(field, value string, preloads ...PreloadsType) (*T, error)

    // 数据查询(返回新数据,不修改当前实体)
    GetById(id uint64, preloads ...PreloadsType) (*T, error)
    GetByIds(ids []uint64, preloads ...PreloadsType) ([]*T, error)
    List(conds ...SearchCondition) ([]*T, error)
    Count(conds ...SearchCondition) (int64, error)

    // 数据处理流程
    SetData(data any) (*T, error)   // 填充数据
    Validate() error                // 数据校验(需实现)
    Repair() error                  // 数据修复(需实现)
    Complete() error                // 数据完善(需实现)

    // 事务
    Transaction(fc func(tx *gorm.DB) error) error

    // 状态机
    InitStateMachine(status string, events []fsm.EventDesc, callback fsm.Callback) error
    EventExecution(status, event, eventName string, args ...any) error

    // 唯一性检查
    CheckBusinessCodeExist(field, code string) (bool, error)
    CheckUniqueKeysExist(fields []string, values []string) (bool, error)

    // 查询条件构建
    MakeCondition(data any) SearchCondition
}

搜索 DSL

基于结构体标签的声明式查询:

type ProductSearch struct {
    // 基础字段查询
    Name      string    `search:"type:icontains;column:name;table:products" form:"name"`
    Status    []int     `search:"type:in;column:status;table:products" form:"status[]"`
    MinPrice  float64   `search:"type:gte;column:price;table:products" form:"min_price"`
    MaxPrice  float64   `search:"type:lte;column:price;table:products" form:"max_price"`

    // 排序和分页
    Sort      string    `search:"type:order;column:id;table:products" form:"sort"`
    Page      int       `search:"type:page" form:"page"`
    PageSize  int       `search:"type:pageSize" form:"page_size"`
}

// 使用
search := ProductSearch{Name: "iPhone", Status: []int{1, 2}, Page: 1, PageSize: 20}
condition := entity.MakeCondition(search)
list, _ := entity.List(condition)

支持的查询类型:

类型 说明 示例值
exact / iexact 精确匹配(大小写敏感/不敏感) status=1
contains / icontains 模糊匹配 name=phone
gt / gte 大于 / 大于等于 price=100
lt / lte 小于 / 小于等于 price=1000
startswith / istartswith 前缀匹配 code=PRD
endswith / iendswith 后缀匹配 code=001
in IN 查询 status[]=1&status[]=2
isnull NULL 检查 deleted=1 (is not null)
order 排序 sort=desc

使用指南

1. 定义业务实体

type Order struct {
    base.BaseModel[Order]                                    // 嵌入基础模型
    OrderId     string                     `json:"orderId" gorm:"column:order_id"`
    Status      int                        `json:"status" gorm:"column:status"`
    CustomerId  string                     `json:"customerId" gorm:"column:customer_id"`
    Amount      float64                    `json:"amount" gorm:"column:amount"`
    OrderItems  []*OrderItem               `json:"orderItems" gorm:"foreignKey:OrderId;references:OrderId"`
}

func (o *Order) TableName() string {
    return "orders"
}

2. 定义实体接口

type OrderInterface interface {
    base.BaseModelInterface[Order]
    // 自定义业务能力
    CalculateTotal() error
    Cancel() error
}

3. 实现必要钩子

// Validate 数据校验 - 校验外部输入合法性
func (o *Order) Validate() error {
    if o.CustomerId == "" {
        return fmt.Errorf("客户ID不能为空")
    }
    if o.Amount <= 0 {
        return fmt.Errorf("订单金额必须大于0")
    }
    // 检查客户是否存在...
    return nil
}

// Repair 数据修复 - 设置默认值、补偿缺失数据
func (o *Order) Repair() error {
    if o.OrderId == "" {
        o.OrderId = generateOrderId()
    }
    if o.Status == 0 {
        o.Status = OrderStatusDraft
    }
    return nil
}

// Complete 数据完善 - 补全关联数据、字典翻译
func (o *Order) Complete() error {
    // 查询客户信息
    // 翻译状态码为中文
    // 格式化金额...
    return nil
}

4. 实现构造函数

func NewOrder(ctx *base.BaseContext, db *gorm.DB, opts ...base.Option[Order]) OrderInterface {
    entity := &Order{}
    entity.BaseModel = base.NewBaseModel(ctx, db, entity.TableName(), entity)

    // 应用自定义选项
    for _, opt := range opts {
        opt(&entity.BaseModel)
    }
    return entity
}

5. 使用选项模式配置

// 预加载配置
preloads := map[string][]any{"OrderItems": {}}
withPreloads := base.WithPreloads[Order](preloads)

// 权限条件配置
permissionConds := []base.SearchCondition{
    func(db *gorm.DB) *gorm.DB {
        return db.Where("tenant_id = ?", tenantId)
    },
}
withPermissions := base.WithPermissionConditons[Order](permissionConds...)

// 创建实体
order := NewOrder(ctx, db, withPreloads, withPermissions)

API 参考

创建操作

// 方式一:使用 SetData + Create(推荐)
req := CreateOrderRequest{CustomerId: "C001", Amount: 100}
order.SetData(req)
order.Validate()
order.Repair()
order.Create()

// 方式二:使用 CreateWithData(简洁)
data := &Order{CustomerId: "C001", Amount: 100}
order.CreateWithData(data)

更新操作

// 标准更新流程
order.LoadById(1)           // 加载现有数据
order.SetData(updateReq)    // 应用更新
order.Validate()            // 校验
order.Repair()              // 修复
order.Update()              // 保存

// 或使用 UpdateWithData
order.UpdateWithData(&Order{Id: 1, Status: 2})

查询操作

// 单条查询
order.LoadById(1)
order.LoadByBusinessCode("order_id", "ORD2024001")

// 条件查询
search := OrderSearch{Status: []int{1, 2}}
cond := order.MakeCondition(search)
list, _ := order.List(cond)
total, _ := order.Count(cond)

// 批量查询
orders, _ := order.GetByIds([]uint64{1, 2, 3})

事务处理

order := NewOrder(ctx, db)
product := NewProduct(ctx, db)

err := order.Transaction(func(tx *gorm.DB) error {
    // 在事务中操作
    order.Create()
    product.Update()

    // 返回错误会触发回滚
    return nil
})

状态机

// 定义状态流转
var events = []fsm.EventDesc{
    {Src: []string{"draft"}, Name: "submit", Dst: "pending"},
    {Src: []string{"pending"}, Name: "approve", Dst: "approved"},
    {Src: []string{"pending"}, Name: "reject", Dst: "rejected"},
}

// 初始化状态机
order.InitStateMachine("draft", events, order.OnStateChange)

// 执行状态变更
order.EventExecution("pending", "approve", "审批通过")

示例代码

完整 CRUD 示例

详见 exampleLogic/order_test.go

// 创建订单
func TestCreateOrder(t *testing.T) {
    ctx := createTestContext()
    req := &CreateOrderRequest{
        CustomerName: "张三",
        Address:      "北京市",
        Items: []*OrderItem{{SkuCode: "SKU001", Qty: 2}},
    }

    entity := NewOrder(ctx, db)
    entity.SetData(req)
    entity.Validate()
    entity.Repair()

    err := entity.Transaction(func(tx *gorm.DB) error {
        _, err := entity.Create()
        return err
    })

    assert.Nil(t, err)
}

// 更新订单
func TestUpdateOrder(t *testing.T) {
    ctx := createTestContext()
    req := &UpdateOrderRequest{Id: 1, CustomerName: "李四"}

    entity := NewOrder(ctx, db)
    entity.LoadById(req.Id)
    entity.SetData(req)
    entity.Validate()
    entity.Repair()
    entity.Update()
}

// 查询列表
func TestListOrders(t *testing.T) {
    ctx := createTestContext()
    search := OrderSearch{CustomerName: "张", Page: 1, PageSize: 20}

    entity := NewOrder(ctx, db, base.WithPreloads[Order](
        map[string][]any{"OrderItems": {}}
    ))

    cond := entity.MakeCondition(search)
    total, _ := entity.Count(cond)
    list, _ := entity.List(cond)

    for _, item := range list {
        item.Complete()
    }

    fmt.Printf("Total: %d, List: %v\n", total, list)
}

最佳实践

1. 实体设计原则

  • 单一职责:一个实体只负责一个业务概念
  • 自包含:实体应包含自身的验证、修复逻辑
  • 接口隔离:通过接口暴露能力,隐藏内部实现

2. 事务边界

  • 在 Logic 层控制事务,而非 Entity 层
  • 使用 Transaction() 方法开启事务,库会自动防止重复开启
  • 在事务中操作多个实体时,确保它们共享同一个 Context

3. 上下文传递

确保 Gin Context 中包含必要的信息:

c.Set("currUserId", userId)       // 当前用户ID
c.Set("currUserName", userName)   // 当前用户名称

4. 预加载使用

对于关联数据查询,使用预加载避免 N+1 问题:

preloads := map[string][]any{
    "OrderItems": {},
    "Customer": {func(db *gorm.DB) *gorm.DB {
        return db.Select("id", "name")
    }},
}
entity := NewOrder(ctx, db, base.WithPreloads[Order](preloads))

5. 错误处理

库会返回详细的错误信息,建议按以下方式处理:

if err != nil {
    // 区分业务错误和系统错误
    if strings.Contains(err.Error(), "查询的数据不存在") {
        // 业务错误 - 返回 404
    } else {
        // 系统错误 - 返回 500
    }
}

项目信息

致谢

  • GORM - 强大的 ORM 框架
  • Gin - 高性能 Web 框架
  • FSM - 有限状态机实现