ctxflow

package module
v1.10.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 25, 2023 License: MIT Imports: 12 Imported by: 0

README

CtxFlow是一个轻薄的业务分层框架。

2021.9.27变更

  • Dao层提供了SetModel、GetModel方法,可用于替换原来的SetTable

2021.7.14变更

  • 提供了统一的初始化方法puzzle.InitConfig(请参考examples)
  • 初始化时支持忽略DbLog的默认格式化方式(IgnoreDefaultDBLogFormat),原因是有些框架会在gorm底层提供默认的格式化方式

2021.7.13变更

  • 调整了api模块中使用http客户端的方式,由直接将方法封装到api基类变成了通过adapter进行封装。使用者可以根据适配器demo实现更丰富更灵活的功能
  • 新增demo_adapter文件(里面的代码注释掉了),用于给使用者做参考
  • tag版本由2.x变为1.10.x(因为2.x使用引用路径比较麻烦,后续将基于1.10.x向后升级,2.x版本tag仍然可用)
  • puzzle.IHttpClient不再需要

2021.5.20变更

  • 为了便于理解原Domain模块,更名为DataSet
  • api层依赖的http工具放入适配器中
  • example中增加adapter,用于放置适配器

V1.10版本重要变更

  • CtxFlow从v1.10.x开始,全面支持gormV2,不再支持gormV1
  • dao层log简化,直接使用Flow基类提供的log输出mysql的基本信息

主要功能

  • 结合其他主流框架快速搭建项目
  • 兼容主流的gin、gorm、redigo、zap等类库
  • 提供全局的上下文
  • 全局使用log以及log追踪
  • 面向对象的golang代码编写风格
  • 支持跨模块事务操作

框架并不关心的内容(大部分团队是有能力做到)

  • gin提供的并发以及接受请求的封装
  • 统一的配置加载方式和配置文件相关功能
  • 平滑重启以及部署相关逻辑
  • log文件以及log对象的初始配置
  • 数据库对象的初始配置
  • redis对象的初始化配置

DEMO使用

  • examples目录包含几个运行demo,可以copy出来运行
  • 搭建demo需要在本地搭建mysql数据库,并且main.go中设置账号密码
  • 数据库搭建好后需要创建demo需要的表
    create database demo;
    use demo;
    CREATE TABLE `demoUser` (
      `uid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(512)  NOT NULL DEFAULT '' COMMENT '用户名',
      `age` int(20) unsigned NOT NULL DEFAULT '0' COMMENT '年龄',
      `last_modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      PRIMARY KEY (`uid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='用户demo';
    insert into demoUser (`name`,`age`) values ("张三","20");
  • demo访问方法 需要安装jq(用来格式化JSON)命令
curl -X POST 'http://localhost:8989/demo/testLog' | jq
curl -X POST 'http://localhost:8989/demo/testGetUserList' | jq
curl -X POST 'http://localhost:8989/demo/testAddUser' --data '{"name":"李四","age":11}' | jq
curl -X POST 'http://localhost:8989/demo/testHttpGet' | jq
  • 可以通过examples/log.txt查看log

业务分层建议

通常业务代码建议按照业务层次划分主要分为Controller、Service、Data、Dao、Api等多层应用架构。

Controller(控制器)

Controller层是调度层,主要的职责是:

  • 接收接口入参
  • 参数校验
  • 调度Service完成服务
  • 输出数据结果

控制器执行原理

Controller层代码核心是一个继承自layer.Controller的结构体,并且可以使用继承自layer.Controller提供的一系列方法。

type TestAddUser struct {
    layer.Controller
}

func (entity *TestAddUser) Action() {
    entity.LogInfo("test getUser start")

    req := dtoUser.AddUserReq{
        Age: thrift.Int32Ptr(10),//给age赋予默认值
    }

    if err :=entity.BindParamError(&req);err != nil{
        entity.RenderJsonFail(err)
        return
    }

    userService := entity.Use(new(svUser.UserService)).(*svUser.UserService)
    err := userService.AddUser(&req)
    if err != nil {
        entity.RenderJsonFail(err)
        return
    }
    entity.RenderJsonSucc("success")
}

Controller中结构体的入口方法方法是Action方法。路由层入口配置如下:

    demoGroup := engine.Group("/demo")
    {
        demoGroup.POST("/testLog", ctxflow.UseController(new(demo.TestLog)))
        demoGroup.POST("/testGetUserList", ctxflow.UseController(new(demo.TestGetUserList)))
        demoGroup.POST("/testAddUser", ctxflow.UseController(new(demo.TestAddUser)))
        demoGroup.POST("/testHttpGet", ctxflow.UseController(new(demo.TestHttpGet)))
    }
    engine.Run("0.0.0.0:8989")

其中与正常入口配置的方式有所区别。

正常的入口配置是POST方法第二个参数传入一个函数指针。

而ctxflow则是使用ctxflow.UseController(new(data.GetData))的方式。

该方式表示默认初始化的是以Controller的实例化对象为调用目标。
初始化后,会调用Action方法。同时会将上下文信息写入到控制器类中。

控制器提供的方法:

  • BindParam(req interface{}) bool
  • Action()
  • RenderJsonFail(err error)
  • RenderJsonSucc(data interface{})
  • RenderJsonAbort(err error)
  • 继承自CtxFlow的其他方法

Controller编码规范建议

  • router入口必须使用ctxflow.UseController的方式进入
  • 需要定义继承于layer.Controller的结构体,并实现Action方法
  • 入参的Dto结构体定义在dto目录,结构体遵循validator v9的规范和写法https://godoc.org/gopkg.in/go-playground/validator.v9。结构体中可选参数尽量使用指针。
  • 入参的结构体需要赋予默认值时,参考上面范例代码的声明方式。
  • 内容数据输出使用layer.Controller基类提供的RenderXXX方法输出
  • 结构体内对象本身的引用命名统一命名成entity
  • 使用其他模块时,要使用如下方式进行初始化对象,注意后面要有指针类型的强制转换 userService := entity.Use(new(svUser.UserService)).(*svUser.UserService) 注:Use方法会将上下文信息传导到其他的模块中
  • 可以通过entity.Use的第二个(或以上)参数扩展传递,并且这些参数在被use的模块中是可继承的

Service(服务层)

Service层的的代码是业务实现的主要模块,包括

主逻辑控制

  • 调用data层获取数据,进行拼装业务逻辑
  • 对最终的结果负责

Service层的主要执行原理

  • Service层需要使用entity.Use方法进行初始化。调用其他模块也需要通过entity.Use进行初始化替他模块。

Service层编码规范建议

  • 需要使用entity.Use进行初始化,和初始化使用的其他模块
  • 主要的业务实现逻辑放在Service中
  • 需要以结构体对象的方式声明Service,Service需要继承于layer.Service
  • service层的包名以sv前缀开头
  • req结构体只可以在这一层和 Controller层出现
  • 可以通过entity.Use的第二个(或以上)参数扩展传递,并且这些参数在被use的模块中是可继承的

Data

Data层的代码是针对对象个体实现的层,主要功能是

  • 能够抽象的最细颗粒度对象的实现放在Data层

Data层编码规范建议

  • 需要使用entity.Use进行初始化,和初始化使用的其他模块
  • 最细粒度对象的实现放在Data层
  • 需要以结构体对象的方式声明Data,Data需要继承于layer.DataSet
  • Data层的包名以ds前缀开头
  • req结构体不可以传入到该层
  • 可以通过entity.Use的第二个(或以上)参数扩展传递,并且这些参数在被use的模块中是可继承的

Api(接口层)

Api层主要是对与其他服务ral/rpc调用的封装

Api层编码规范建议

  • 需要使用entity.Use进行初始化,和初始化使用的其他模块
  • Api调用的基本封装
  • 需要以结构体对象的方式声明Api,Api需要继承于BaseApi
  • api层的包名以api前缀开头
  • 使用BaseApi提供的方法进行http调用
  • preUse方法相当于Use的前置方法,可以当成钩子(构造)函数使用,需要在preUse方法中初始化调用api的配置
  • 可以通过entity.Use的第二个(或以上)参数扩展传递,并且这些参数在被use的模块中是可继承的

Dao(数据库层)

dao层主要是对数据库操作的封装,与Api层封装相对应

Dao层编码规范建议

  • 需要使用entity.Use进行初始化(其他模块也是如此)
  • 本模块是数据库操作的基本封装
  • 需要以结构体对象的方式声明dao,dao需要继承于BaseDao
  • preUse方法相当于Use的前置方法,可以当成钩子(构造)函数使用,需要在preUse方法中初始化调用数据表的配置
  • dao层的包名以dao前缀开头
  • 全局事务的使用,使用全局事务可以在逻辑中先声明好db对象,如 db := puzzle.GetDefaultGormDb().Begin()
  • 然后entity.Use模块的时候将该db传入(第二个参数)。Use的模块可以是任何模块(包括Service,Data,Dao等)。当这个模块里使用了Dao层的时候,会自动使用这个db,然后逻辑里就可以使用这个db进行统一的提交和回滚操作了。

db使用的优先级--假如依赖关系是A(Service)->B(Data)->C(Dao),既A中通过Use使用了B,B中通过Use使用了C。那么最后C执行的时候会是这样: 1.如果只有在A use B的时候传入了db1,则最终C中执行的是db1这个gorm.DB对象 2.如果步骤1中,A use B的时候传入了db1,同时B use C中传入了db2,则最终C中执行的是db2这个gorm.DB对象,遵循最近的db最优先的原则。

示例代码:

    //db关联其他模块,统一提交事务或者回滚
    db := puzzle.GetDefaultGormDb().Begin()
    //use方法的第二个参数可选,可以是db也可以是其他。如果设置为某个DB,则被这个DB关联了事务
    userData := entity.Use(new(dsUser.UserRepository),db).(*dsUser.UserRepository)
    usr,err:=userData.GetUserByName(req.Name)

    if err != gorm.ErrRecordNotFound {
        entity.LogWarn("用户已存在:%+v",usr)
        db.Rollback()
        return err
    }

    //use方法的第二个参数可选,可以是db也可以是其他。如果设置为某个DB,则被这个DB关联了事务
    userDao := entity.Use(new(daoUser.DemoUserDao),db).(*daoUser.DemoUserDao)
    err = userDao.Create(&daoUser.DemoUser{
        Name: req.Name,
        Age:*req.Age,
    })

    if err != nil {
        entity.LogWarn("创建失败:%+v",usr)
        db.Rollback()
        return err
    }
    db.Commit()

注:当对象被Use的过程中,Use的第2个以上的扩展参数会自动继承到这个对象所有Use的其他对象中,可以通过entity.GetArgs(idx)获取。如果第一个参数是*gorm.DB类型,则里面所有使用的Dao模块都会使用这个DB(就近原则)。

CtxFlow(上下文流)

所有模块的基类都要继承于CtxFlow类,该类提供了Log相关的方法以及Use、SetContext、GetContext、PreUse等方法。各个模块都可以使用

Documentation

Index

Constants

View Source
const (
	NMQ_ERR_STOP     = 5001 //参数错误,ticker停止执行
	NMQ_ERR_CONTINUE = 500  //处理失败,ticker继续执行
	NMQ_SUC_CONTINUE = 200  //处理成功,ticker继续执行
)

Variables

This section is empty.

Functions

func InitFlow added in v1.10.5

func InitFlow(ctx *gin.Context, flow layer.IFlow)

func MakeFlow added in v1.10.3

func MakeFlow(ctx *gin.Context) *layer.Flow

func NoPanic added in v1.2.4

func NoPanic(flow layer.IFlow)

func NoPanicContorller added in v1.2.5

func NoPanicContorller(ctl layer.IController)

func PanicTrace added in v1.2.1

func PanicTrace(kb int) []byte

func Slave added in v1.10.5

func Slave(src interface{}) interface{}

func StackTrace added in v1.2.5

func StackTrace() []byte

func UseController

func UseController(controller layer.IController) func(ctx *gin.Context)

func UseKFKConsumer

func UseKFKConsumer(consumer layer.IConsumer) func(cmd *cobra.Command, args []string)

func UseNMQ

func UseNMQ(nmqMap map[string]reflect.Type) func(ctx *gin.Context)

func UseTask

func UseTask(task layer.ITask) func(cmd *cobra.Command, args []string)

Types

This section is empty.

Directories

Path Synopsis
lib

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL