GoFrame学习随记1

一、数组类型-garray

garray支持int/string/interface{}三种常用的数据类型

  1. garray
    

    支持普通数组和排序数组,普通数组的结构体名称定义为

    *Array
    

    格式,排序数组的结构体名称定义为

    Sorted*Array
    

    格式,如下:

    • Array, intArray, StrArray
    • SortedArray, SortedIntArray, SortedStrArray
    • 其中排序数组SortedArray,需要给定排序比较方法,在工具包gutil中也定义了很多Comparator*比较方法
package main

import (
    "fmt"
    "github.com/gogf/gf/v2/container/garray"
)


func main () {
    // 创建并发安全的int类型数组
    a := garray.NewIntArray(true)

    // 添加数据项
    for i := 0; i < 10; i++ {
        a.Append(i)
    }

    // 获取当前数组长度
    fmt.Println(a.Len())

    // 获取当前数据项列表
    fmt.Println(a.Slice())

    // 获取指定索引项
    fmt.Println(a.Get(6))

    // 在指定索引后插入数据项
    a.InsertAfter(9, 11)
    // 在指定索引前插入数据项
    a.InsertBefore(10, 10)
    fmt.Println(a.Slice())

    // 修改指定索引的数据项
    a.Set(0, 100)
    fmt.Println(a.Slice())

    // 搜索数据项,返回搜索到的索引位置
    fmt.Println(a.Search(5))

    // 删除指定索引的数据项
    a.Remove(0)
    fmt.Println(a.Slice())

    // 并发安全,写锁操作
    a.LockFunc(func(array []int) {
        // 将末尾项改为100
        array[len(array) - 1] = 100
    })

    // 并发安全,读锁操作
    a.RLockFunc(func(array []int) {
        fmt.Println(array[len(array) - 1])
    })

    // 清空数组
    fmt.Println(a.Slice())
    a.Clear()
    fmt.Println(a.Slice())
}

输出:

10
[0 1 2 3 4 5 6 7 8 9]
6 true
[0 1 2 3 4 5 6 7 8 9 10 11]
[100 1 2 3 4 5 6 7 8 9 10 11]
5
[1 2 3 4 5 6 7 8 9 10 11]
100
[1 2 3 4 5 6 7 8 9 10 100]
[]

二、完全理解Logic方法包的初始化

type sBookTypeQuery struct{}

func init() {
   service.RegisterBookTypeQuery(New())
}
// RegisterBookTypeQuery注册的是谁?这里:
//func RegisterUser(i IUser) {
// 	localUser = i
//}

func (s *sBookTypeQuery) BookTypeQuery(ctx context.Context) () {
   return
}
1. 定义 sBookTypeQuery 结构体:
type sBookTypeQuery struct{}

定义一个空的结构体 sBookTypeQuery。这个结构体是实现了 service.BookTypeQuery接口的一部分。

结构体的主要作用是为后续的方法提供一个接收者,(即将结构体实例作为方法的接收者 — 绑定)以便我们可以在方法内部操作结构体的数据和状态。它可以包含字段和方法,用于处理业务逻辑和实现特定的功能。

优势:

  1. 封装和组织: 通过将方法与结构体关联,可以将操作数据的逻辑与数据本身封装在一起,提高代码的组织性和可维护性。
  2. 数据共享: 方法内部可以访问结构体的私有字段,从而实现对数据的共享和操作,而无需暴露数据的实现细节。
  3. 代码复用: 可以在多个方法中重复使用相同的操作逻辑,提高代码复用率。
  4. 扩展性: 可以轻松地添加新的方法来扩展结构体的功能,而不需要改变已有的代码。
2. 初始化函数 init:
func init() {
    service.RegisterBookTypeQuery(New())
}

init() 函数是在包被引入时自动执行的函数。在这里,它的主要作用是在应用程序初始化过程中注册服务。具体地,它调用了 service.RegisterBookTypeQuery 函数,并将 New() 返回的对象作为参数传递给它。这样可以在其他地方通过已注册的服务接口调用相关功能,实现代码解耦和模块化。

3. New 函数:
func New() *sBookTypeQuery {
    return &sBookTypeQuery{}
}

New() 函数是一个工厂函数,用于创建并返回一个 sBookTypeQuery 结构体的实例,简单地返回一个新创建的 sBookTypeQuery 结构体指针。

个人总结:

首先创建一个结构体,该结构体是空的,说明它不是用来存储数据的,而是用来关联数据的。我们常看到,多个方法调用同一个结构体,并在内部对其进行“操作”,但是反过来一想,在这种情况下,一个结构体也与多个方法产生了关联(绑定),毕竟力是相互的嘛!你懂的~。

然后是一个初始化init函数,主要作用是在应用程序初始化过程中注册服务。为啥要注册这么一下子?因为它调用了 service.RegisterBookTypeQuery 函数,并将 New() 返回的对象作为参数传递给它。这样可以在其他地方通过已注册的服务接口调用相关功能,实现代码解耦和模块化。

最后是一个New函数,而且是指针类型的。主要作用1是为上面创建的结构体实例化一下子,2是New 函数应该返回实例的指针,以便在方法调用中能够修改实例的状态。

三、服服子

每当添加新的logic文件后,要在/internal/logic下的logic文件里引用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8rucdEE-1692780507051)(D:\Tianyx\学习过程\Mouth2\项目完善\8.10.GoFrame学习随记.assets\image2022-6-22_16-2-37.png)]

四、彻底搞懂数据库DAO

DAO(Data Access Object)是一种设计模式,用于将数据访问逻辑与业务逻辑分离。DAO层通常负责处理数据的持久化操作,即与数据库进行交互,执行数据的增删改查等操作。相当于一个壳子,把数据库包起来了,你想访问它,就得通过DAO。

五、gf gen service

为什么想重新生成注册信息的时候,要把new()去除和加上后各执行一遍?

func New() *sUserInformation {
   return &sUserInformation{}
}

gf gen service是在生成接口及服务注册文件,之后在每个业务模块中加上接口的具体实现注入。该方法每个业务模块加一次即可。也就是:

func init() {
    service.RegisterBookTypeQuery(New())
}

因此,需要用gf gen service重新生成一下

六、搞懂不同权限的功能分配

问题:普通用户登录和管理员用户登录后的功能是不同的,用户的功能和管理员的功能会有重合的,那么他们是直接复用一个功能接口,还是有各自的接口?

如果直接复用一个功能接口,当用户调用该请求时,返回的时候返回什么?

重置WMI服务

winmgmt /resetrepository

七、搞懂GoFrame框架状态码

八、搞透GoFrame框架注册中间件

中间件功能:

1. 负载均衡:将请求分配给多个服务器,使得每个服务器都能够充分利用资源,提高系统的并发处理能力和吞吐量。
2. 缓存:将频繁访问的数据缓存到内存中,减少对数据库的访问,提高系统的响应速度。(设置缓存就是在中间件中设置的嘛?)
3. 安全控制:对请求进行安全过滤和访问控制,保护系统的安全性。
4. 日志记录:记录请求和响应的信息,便于系统的监控和排错。
5. API 管理:对外提供 API 接口的管理和发布,便于开发者使用和集成。
6. 服务发现:提供服务发现和注册功能,方便服务的管理和调用。
7. 数据转换:对请求和响应的数据进行转换和处理,使得系统能够更加灵活和可扩展。
8. 静态资源处理:对静态资源的访问进行处理和缓存,提高系统的访问速度。 9. 集成其他服务:与其他服务进行集成和协作,实现系统的功能扩展和整合。

在 GoFrame 框架中,通过鉴权中间件返回状态码通常涉及两个步骤:
首先,在鉴权中间件中进行鉴权检查,然后根据鉴权结果设置 HTTP 响应状态码。
创建一个鉴权中间件函数,用于进行鉴权检查。这个函数应该在需要鉴权的路由中使用。例如:

func AuthMiddleware(r *ghttp.Request) {
   // 进行鉴权检查    if !CheckAuth(r) {
       // 如果鉴权失败,设置响应状态码为 403 Forbidden
       r.Response.WriteStatus(http.StatusForbidden)
       return
  }
   r.Middleware.Next()
}

在上述示例中,AuthMiddleware 函数会检查鉴权条件(在 CheckAuth函数中实现),如果鉴权失败,则设置响应状态码为 403 Forbidden。
注册鉴权中间件函数。
在项目初始化中,使用 ghttp.Middleware 函数注册上述定义的中间件(得提前声明一下,我要使用鉴权中间件函数了:

func main() {
   s := g.Server()
   // 注册中间件函数    s.Use(AuthMiddleware)
   // 定义需要鉴权的路由规则    s.BindHandler("/secure", YourSecureHandler)
   s.Run()
} 

通过上述步骤创建了一个简单的鉴权中间件,它在需要鉴权的路由中进行鉴权检查,并根据鉴权结果设置 HTTP 响应状态码。

s.Group("/api", func(group *ghttp.RouterGroup) {
   // 该中间件应用于全局接口    
group.Middleware(
          service.Middle().MiddlewareCORS,  // CORS权限       	service.Middle().ResponseHandler, // 返回处理   
 )

总结:

  1. 创建一个鉴权中间件函数(函数内部编写需要鉴权的具体逻辑)
  2. 在项目初始化中注册鉴权中间件函数(分组不同,中间件也可以不同)
  3. 如何调用呢?

附上Demo中的中间件逻辑:

package middle
import (***)

type sMiddle struct{}

func New() *sMiddle {
	return &sMiddle{}
}
func init() {
	service.RegisterMiddle(New())
}

// 中间件,用于作返回处理
func (s *sMiddle) ResponseHandler(r *ghttp.Request) {
	r.Middleware.Next()
	var (
		err = r.GetError()
		// 获取请求的响应对象
		res = r.GetHandlerResponse()
	)
	if err != nil {
		// 系统返回的错误码
		code := gerror.Code(err).Code()
		switch code {
		case 52:
			r.Response.WriteStatus(400, nil)
			r.Response.WriteJsonExit(g.Map{
				"message": "数据库校验错误",
			})
		case 51:
			r.Response.WriteStatus(400, nil)
			r.Response.WriteJsonExit(g.Map{
				"message": "参数校验错误" + gconv.String(err),
				"data":    make([]string, 0),
			})
		default:
			r.Response.WriteJsonExit(res)
		}
	} else {
		r.Response.WriteJsonExit(res)
	}
}

// 开启CORS权限
func (s *sMiddle) MiddlewareCORS(r *ghttp.Request) {
	r.Response.CORSDefault()
	r.Middleware.Next()
}

// 登录中间件
func (s *sMiddle) Auth(r *ghttp.Request) {
	auth.Auth().MiddlewareFunc()(r)
	r.Middleware.Next()
}

九、搞透数据库中的锁

当多个用户或线程**同时访问数据库时,为了维护数据的一致性和完整性,数据库系统需要实施锁机制。锁机制确保在某个时刻只有一个用户或线程可以访问某个数据**,以防止并发操作引发的问题。

锁机制: MySQL的锁机制用于控制并发访问,它可以分为两类主要的锁:共享锁(也称为读锁)和排他锁(也称为写锁)。

  1. 共享锁(S锁):多个用户可以同时获得共享锁,允许多个用户同时读取数据**(读读不冲突)**,但阻止其他用户获取排他锁,从而防止写操作(读写冲突)。适用于读多写少的情景。
  2. 排他锁(X锁):只有一个用户可以获得排他锁,允许用户进行写操作,但阻止其他用户获得任何类型的锁(写写冲突),包括共享锁和排他锁。适用于写操作频繁的情景。

好玩的:

乐观锁悲观锁更多的是体现加锁的思想不同**,**乐观锁是一种无锁的思想,假设并发冲突总是不会发生,提交时检查数据一致性,如果一致性被破坏则放弃提交,更新时带着 version 就是乐观锁。悲观锁假设并发冲突一定会发生,每次操作前都会拿锁,通过锁的互斥顺序执行来控制并发,可以认为数据库中的锁都是悲观锁

共享锁示例:

(当事务对数据加上读锁后,其他事务只能对该数据加读锁,不能加写锁。)

用户1:
在这里插入图片描述

用户2:

在这里插入图片描述

排他锁示例

当事务对数据加上排他锁后,其他事务无法对该数据进行查询或者修改(查都不让查。。。。)

用户1:

在这里插入图片描述

用户2:

在这里插入图片描述
引用:https://blog.csdn.net/wgzblog/article/details/127281354

为什么有了事务这东西,还需要乐观锁悲观锁?

事务是粗粒度的概念、乐观锁悲观锁可以更细粒度的控制;比如抢票,假设余票只有1张;隔离级别可以保证事务A和事务B不能读到对方的数据,也不能更新对方正在更新的数据,但是事务A和事务B都认为还有1张余票,于是出票,并更新为0;

十、service目录接口代码自动生成注意事项

logic目录下具体代码文件的结构体必须以小写字母s开头,例如sBorrowHistory,否则无法自动生成service接口。

对了,service接口是干啥的呢?

为了把业务逻辑进行封装,形成一个接口目录。

// 里面全是接口
type (
   IBookType interface {
      // 图书类别查询方法
      BookTypeQuery(ctx context.Context, req *v1.BookTypeReq) (res *v1.BookTypeRes, err error)
      BookTypeNumQuery(ctx context.Context, BookTypeID int) (res *v1.BookTypeNumRes, err error)
   }
)

用正则指定业务模块结构体定义格式,便于解析业务接口定义名称。在默认的正则下,所有小写s开头,大写字母随后的结构体都将被当做业务模块接口名称。例如:

logic结构体名称service接口名称
sUserUser
sMetaDataMetaData

用正则指定业务模块结构体定义格式,便于解析业务接口定义名称。在默认的正则下,所有小写`s`开头,大写字母随后的结构体都将被当做业务模块接口名称。例如:

| logic结构体名称 | service接口名称 |
| :-------------- | :-------------- |
| `sUser`         | `User`          |
| `sMetaData`     | `MetaData`      |
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值