Write the Code. Change the World.

分类目录
4月 26

goframe 生成不重复的 7 位数字,并和数据库 user 表中字段不重复

func generateUniqueID() int {
    src := rand.NewSource(time.Now().UnixNano())
    min := 1000000
    max := 10000000
    return rand.New(src).Intn(max-min) + min
}

func generateUniqueViewID(ctx context.Context) (int, error) {
    for {
        viewId := generateUniqueID()
        id, err := dao.Users.Ctx(ctx).Where(do.Users{Viewid: viewId}).Fields("id").Value()
        if err != nil {
            return 0, err
        }

        if id == nil {
            return viewId, nil
        }
    }
}
9月 18

不能只让 go 在本地跑,得让 go 编译后,在服务器上跑。

好,那就这么干

  • 编译。
  • 放到服务端,使用守护进程的方式启动它。比如可以用 nohub、supervisord、systemctl 等。
  • 使用 nginx 做重定向或代理,配置域名访问。

编译

先看看我的服务器信息。

lsb_release -a

LSB Version:    :core-4.1-amd64:core-4.1-noarch
Distributor ID: AlibabaCloud
Description:    Alibaba Cloud Linux release 3 (Soaring Falcon)
Release:    3
Codename:   SoaringFalcon

hack/config.yaml 中,增加编译的配置信息

  build:
    name:     "main"
    arch:     "amd64"
    system:   "linux"
    mod:      "none"
    packSrc:  "resource,manifest"
    version:  "v1.0.0"
    output:   "./out"
    cgo:      0
    extra:    ""

注意,manifest/config 下的 mysql,redis 等配置,一定要和服务端一致。好了,可以打包了。

gf build

继续阅读

9月 11

orm with 处理也轻微使用了下。现在来处理输出。处理大输出。抱怨总是包裹一层太久了,抱怨无论啥都返回 http 200 。现在要改变这个状况。

在处理这个问题之前,我们得先找到整个框架的返回是怎么实现的。其实,在之前也想到了用后置中间件来处理。看官方自己也确实是用后置中间件来统一处理返回的。我们建立一个自己的中间件,替换掉官方自己的中间件不就好了。

官方默认中间件。

import  "github.com/gogf/gf/v2/net/ghttp"
ghttp.MiddlewareHandlerResponse

之前也说过一次。gf 中间件的函数都有且仅有一个参数 r *ghttp.Request。只要实现了该方法即可。当然 r.Middleware.Next() 这个方法是要调用的。

在这个方法之前处理的逻辑,叫前置。在这个方法之后处理的逻辑,叫后置。

go 的中间件的思路和 laravel 的也是一样的。估计其他语言也是这个思路。就是前端的 axios 等的拦截器也是有着这方面的思路。

继续阅读

9月 10

该后台使用 vite + ts + pnpm + vue3 + element-plus + tailwindcss 等技术栈构成。没有添加任意可视化图标等插件。以最小功能,最基础功能展现。用户可以额外添加可使用的插件逻辑。

该后台后端使用 php8.2 + laravel 10 + mysql

该后台后端 go 语言版本开发中。将使用 gframe2.5.2

源码: https://github.com/vini123/simpleAdmin

在线体验: https://www.zeipan.com/admin

权限以及密码一键复位: https://v3test.yuepaibao.com/admin/api/reset

测试账号以及密码: zhoulin@xiangrong.pro、 111111 (如果发现登录不了,可一键复位谢谢)

继续阅读

9月 09

goframe 没有其他 orm 的 hasOne,hasMany, belongsTo 等等这样的模型关联。 goframe 说这个对开发者有一定的心智负担。goframe 有 scanList 和 自己的 with。先了解试试用吧。

https://goframe.org/pages/viewpage.action?pageId=1114326

https://goframe.org/pages/viewpage.action?pageId=7297190

之前完成了用户的登录、注册以及个人信息的获取。这个个人信息的获取,只是单表的信息。现在想要用户的其他信息。这个时候,就需要更多的查询。

如果使用 orm,就可以通过 with 就可以搞定的。但是 with 只有定义好了规则才有用。所以,这个时候就需要组合想要的 dao 了。而 dao 是通过 gf gen dao 生的,只要表不变, dao 就不变。重复生也不变,所以就不能去修改 dao。那么只能新文件中来组合了。

因为之前定义 req 和 res 的时候,已经在 model 里边加了一个文件。那么在其中再加一个组合关系就好。

于是,修改 internal/model/admin/user.go,增加以下组合。

type User struct {
    entity.Users
    UserExtends *entity.UserExtends      `orm:"with:user_id=id" json:"user_extends"`
    Roles       []map[string]interface{} `json:"roles,omitempty"`
    Permissions []Permission             `json:"permissions,omitempty"`
}

// 用户权限
type Permission struct {
    entity.Permissions
    Permission []Permission `json:"permission,omitempty"`
}

从组合中可以看到使用 orm 来获取的仅仅是扩展信息。另外的 roles 和 permissions 是另外赋值进去的。

roles 和 permissions 之所以是另外赋值。是因为它们的获取会关联更多的表。不太适合嵌套去处理。

虽然 User 中定义了 with 相关的 UserExtends。 如果调用的时候,没使用 with 也是不会去查询的。其他的两个,如果不赋值也是不会有的。最基础的 Users 依然还是 entity 里的那个 user。

后来还是改了,表名还是用复数吧。所以命令回来生成的 dao 和 model 也是复数。复数就复数吧。

继续阅读

9月 09

获取图片验证码、登录、获取个人信息都好了。现在做注册功能。

在 ctrl 中调整好姿势。

internal/controller/user/user_admin_sign_up.go

package user

import (
    "context"

    "goSimpleAdmin/api/user/admin"
    model "goSimpleAdmin/internal/model/admin"
    "goSimpleAdmin/internal/service"
)

func (c *ControllerAdmin) SignUp(ctx context.Context, req *admin.SignUpReq) (res *admin.SignUpRes, err error) {
    data, err := service.User().SignUp(ctx, model.SignUpReq{
        Nickname:   req.Nickname,
        Account:    req.Account,
        Password:   req.Password,
        VerifyKey:  req.VerifyKey,
        VerifyCode: req.VerifyCode,
    })

    if err != nil {
        return
    }

    res = new(admin.SignUpRes)
    res.SignUpRes = data
    return res, err
}

继续阅读

9月 08

上一步中,生成图片验证码接口已经好了。现在完善登录和注册的业务逻辑。这里准备使用 token 的方式来维护用户的状态,选用 jwt 。所以登录或注册成功后,只需返回用户一个 token 就可以了。过期时间暂时先不要。所以登录和注册的返回需要修改一番。

登录注册输出重新定义一下

internal/model/admin/user.go 中定义输入和输出的结构。

type SignInRes struct {
    Token string `json:"token"`
}

type SignUpRes struct {
    Token string `json:"token"`
}

api/user/admin/user.go 中组合输入输出

package admin

import (
    model "goSimpleAdmin/internal/model/admin"

    "github.com/gogf/gf/v2/frame/g"
)

type SignInRes struct {
    model.SignInRes
}

type SignUpRes struct {
    model.SignUpRes
}

因为 api 定义的时候已经定义好了输出。所以 ctrl 和 service 这些不需要再重新生一次了。

但是在 logic 和 service 中没有定义 res。所以需要补上,再重新生一次。 service 就是接口的定义,实现还是在 logic 中。

继续阅读

9月 07

随着业务逻辑的充实,发现前边有些地方还是需要修改和完善。哪里呢。就是最最开始的地方,就是定义 api 的那地方。其实在那里,最开始,只需要定义 Req 和 Res。里边不需要先填充任意的东西。为什么这么说呢。因为 路由指向 ctrl,ctrl 通过 service 的绑定去调用 logic。而 logic 里的输入输出却是在 mode 里定义的。所以,model 里边的输入输出和 api 里的输入输出一般都是一样的。这个时候,只需要定义 model 里的输出输出。 api 那边直接继承过来就可以了。所以,步骤是这样的。

  • 定义 api 里的 req 和 res,空的就行(接口path 和 method 还是要加上),名字和方法名字想好。执行 gf gen ctrl 生就好。
  • 然后再生成 dao 和 model。 执行 gf gen dao 生就好。
  • 再在 model 下定义输入和输出。这个定义输入和输出也有一点矛盾。是业务场景带来的。model 和 dao 自身一般是不会有矛盾的,因为他们对应的数据库里的表。而输出和输入却要对应业务场景。比如一个项目有后台登录和 app 登录两个场景。往往,会分出两个路由组来处理这两种业务场景。虽然都是登录,输入和输出一般都不会都是相同的。这个时候 model 下定义输入和输出就得加个分组了。比如当前逻辑是后台逻辑。我会在 internal/model/ 下再多建立一层文件夹,就叫 admin。然后在里边再建立对应的输入输出文件。其实叫的这个 admin 和 api 下的文件夹是一个意思。就是初始化项目时候 api 下的 v1 的意思。这个时候都是 v1 或都是 admin,继承的时候就要用别名哈。下边会给出例子。
  • 再去建立 logic 的初步实现。然后去生 service,再回头完善 logic。 gf gen service

在 api req 的时候,里边一定要有 g.Meta。否则,不会生成对应的 ctrl 文件。

go 语言刚开始学,goframe 也是第一次用。如果理解错了,可以留言哈。

继续阅读

9月 07

从 http 请求到自定义路由,再对应的返回已经跑通了。但是,还没具体的实现业务。那就先从图片验证码开始。从以下几点开始。

  • 先去找一个比较成熟的生成图片验证码的库
  • 写生成逻辑,并加入缓存功能。比如缓存 5 分钟。
  • 添加配置。图片的默认宽高,复杂度等。以及上边说的缓存 5 分钟这些,最好有一个统一配置。
  • 再就是核验验证码(该步骤留到需要使用验证码的地方使用)

redis 配置

在做这些之前,我们先把 redis 配置和使用搞好。就是准备使用 redis 来对图片验证码的值进行缓存。

goframe 官方配置介绍: https://goframe.org/pages/viewpage.action?pageId=1114194

redis 官方介绍: https://goframe.org/display/gf/NoSQL+Redis

配置文件的文件类型就使用 yaml,配置文件就放在 *manifest/config下。

好吧。那么就在 *manifest/config/config.yaml 下补充。

# Redis 配置
redis:
  default:
    address: 192.168.56.56:6379
    db:      1

  cache:
    address:     192.168.56.56:6379
    db:          2

配置好了,那就开始使用测试下看看。

继续阅读

9月 06

做项目。一般都会选择框架。很少会直接从 0 开始撸的。之所以选择框架,是因为框架既帮你做好了架构,又准备好了很多工具,还定制了一些规范。适合快速稳定的做项目。一般一个语言对应的开源框架会比较多,选一个或两个应用到项目中就可以了。准备进入 go 领域,也得选一个框架。初步了解了 gin 和 goframe。这里还是选择 goframe。gin 还是太轻了,项目中需要的一些工具不够多和完善。

官网: https://goframe.org/

安装升级

这里想做一个接口服务项目。仅仅提供接口给到前端使用就好。现在大多业务场景都是前后端分离的。比如app、小程序、管理后台这些。app,小程序是必须分离。管理后台也可以不分离,但分离起来更好写更爽。

按照官网文档,先安装好 gf。当前最新版本是 2.5.2。

创建项目。

gf init goSimpleAdmin

cd goSimpleAdmin

# 查看版本信息
gf -v

GoFrame CLI Tool v2.5.2, https://goframe.org
GoFrame Version: v2.3.1 in current go.mod
CLI Installed At: /Users/vini123/go/bin/gf
CLI Built Detail:
  Go Version:  go1.20.6
  GF Version:  v2.5.2
  Git Commit:  2023-08-18 09:57:25 cf299273c499aafb5d19829809dfb6b8ef280c12
  Build Time:  2023-08-18 16:23:18

发现 goframe 版本是 2.3.1。可以对刚创建的项目进行升级。在升级前,先做好版本控制。

git init -b main
git add .
git commit -m 'goframe initialize'

使用 gf up 对项目进行升级。

继续阅读