GoWeb 开发者必读:掌握 GORM 实现高效数据库操作的技巧(附完整代码)

1、前言

什么是web应用,Web应用程序简单流程如下图所示

在这里插入图片描述
通过上图我们发现,我们 GoWeb 实战项目还缺少最后关键的环节数据库。那么如何使用 GO 语言操作数据库并完成增删改查等操作是我们今天学习的目标。

从零开始实现一个 GoWeb MVC 框架:让你更深入地理解 MVC 架构设计

2、什么是ORM

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。程序员会在自己的业务逻辑代码中夹杂很多 SQL 语句用来增加、读取、修改、删除相关数据,而这些代码通常都是重复的。

ORM 的出现充当了对象和数据库层次的桥梁作用。

3、MySQL

3.1、什么是MySQL

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。

MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型和大型网站的开发都选择 MySQL 作为网站数据库。

3.2、MySQL 安装

3.2.1 Docker 环境

Docker 环境 MySQL8 主从配置

3.2.2 菜鸟教程

MySQL 菜鸟教程

3.3、创建数据库

通过 Navicat Premium 快速创建数据库,数据库名示例:go_gorm,字符集选择:utf8mb4,排序规则选择: utf8mb4_general_ci。最后点击确认即可

在这里插入图片描述

3.4、创建数据表

创建一张系统用户表,用户完成我们后续的 CRUD 等操作,建表语句如下:

-- ----------------------------
-- 系统用户
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`
(
    `id`             bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键 ID',
    `name`           varchar(10)  NOT NULL DEFAULT '' COMMENT '用户名',
    `account`        varchar(20)  NOT NULL DEFAULT '' COMMENT '账号',
    `password`       varchar(64)  NOT NULL DEFAULT '' COMMENT '密码',
    `password_salt`  varchar(64)  NOT NULL DEFAULT '' COMMENT '密码盐值',
    `mail_address`   varchar(64)  NOT NULL DEFAULT '' COMMENT '邮箱地址',
    `phone_number`   varchar(20)  NOT NULL DEFAULT '' COMMENT '手机号码',
    `age`            tinyint unsigned NOT NULL DEFAULT '0' COMMENT '用户年龄',
    `is_using`       tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否启用  10(默认0)',
    `is_delete`      tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除  10(默认0)',
    `create_time`    datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`    datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
    `remark`         varchar(100) NOT NULL DEFAULT '' COMMENT '描述',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='系统用户';

3.5、新增数据

新增一条超级管理员数据。注意:当前用户密码使用明文存储存在安全隐患

INSERT INTO `sys_user` ( `name`, `account`, `password` ) VALUES ('超级管理员', 'admin', '123456')

4、编写代码

此项目根据 《GoWeb 项目实战:使用 Iris 框架构建一个安全可靠的 API 服务(附 JWT 认证实现)》源代码进阶,如果只想了解 CRUD 可以忽略

4.1、安装软件包

# gorm 安装
go get -u gorm.io/gorm

# mysql 驱动
go get -u gorm.io/driver/mysql

4.2、编写代码

4.2.1、加载 MySQL 数据库

在 utils/db 目录新建文件 mysql.go,完整代码如下:

package db

import (
	"fmt"
	"log"
	"os"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// 全局变量
var GormDb *gorm.DB

func init() {
    
    
	fmt.Println("开始加载 MySQL 数据库")
	GormDb = InitMysql()
}

/*
* <h2>初始化 MySQL 数据库</h2>
**/
func InitMysql() *gorm.DB {
    
    
	// GORM 定义了这些日志级别:Silent、Error、Warn、Info
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
		logger.Config{
    
    
			SlowThreshold:             time.Second, // 慢 SQL 阈值
			LogLevel:                  logger.Info, // 日志级别
			IgnoreRecordNotFoundError: false,       // 忽略ErrRecordNotFound(记录未找到)错误
			Colorful:                  false,       // 禁用彩色打印
		},
	)

	// MySQL 数据库地址和用户信息
	dsn := "替换自己数据库用户名:替换自己数据库密码@tcp(数据库IP地址:数据库端口)/go_gorm?charset=utf8mb4&parseTime=True&loc=Local"

	db, err := gorm.Open(mysql.New(mysql.Config{
    
    
		DSN:                       dsn,   // DSN data source name
		DefaultStringSize:         256,   // string 类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	}), &gorm.Config{
    
    Logger: newLogger})

	if err != nil {
    
    
		fmt.Println("加载数据库异常信息:", err)
		return nil
	}

	// 获取通用数据库对象 sql.DB,然后使用其提供的功能
	mysqlDB, err := db.DB()
	if err != nil {
    
    
		fmt.Println("获取通用数据库对象异常信息:", err)
		return nil
	}

	// Ping
	err = mysqlDB.Ping()
	if err != nil {
    
    
		fmt.Println("数据库连接异常信息:", err.Error())
		return nil
	}

	fmt.Println("数据库连接成功", mysqlDB)

	// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
	mysqlDB.SetMaxIdleConns(10)

	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	mysqlDB.SetMaxOpenConns(100)

	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	mysqlDB.SetConnMaxLifetime(time.Hour)

	return db
}

4.2.2、业务异常自定义

在 utils/constant 目录修改文件 errors.go,完整代码如下:

package constant

import "errors"

/**
 * <h1>业务异常自定义</h1>
 * Created by woniu
 */

var (
	ResErrAuthorizationIsNilErr = errors.New("1001_token 为空")
	ResErrSysUserPasswordErr    = errors.New("2001_密码错误")
	ResErrIdIsNil               = errors.New("2002_主键 ID 不能为空")
	ResErrSysUserRepeat         = errors.New("2003_用户已存在")
	ResErrSysUserIsNil          = errors.New("2004_用户不存在")
)

4.2.3、系统用户模型

在 app/model 目录新建文件 sys_user.go,完整代码如下:

package model

import "time"

// 系统用户信息
type SysUser struct {
    
    
	Id           int64     `gorm:"primaryKey"`           // 用户 ID
	Name         string    `gorm:"column:name"`          // 用户姓名
	Account      string    `gorm:"column:account"`       // 用户账号
	Password     string    `gorm:"column:password"`      // 用户密码
	PasswordSalt string    `gorm:"column:password_salt"` // 密码盐值
	MailAddress  string    `gorm:"column:mail_address"`  // 邮箱地址
	PhoneNumber  string    `gorm:"column:phone_number"`  // 手机号码
	Age          int       `gorm:"column:age"`           // 用户年龄
	IsUsing      int       `gorm:"column:is_using"`      // 是否启用  1是 0否(默认0)
	IsDelete     int       `gorm:"column:is_delete"`     // 是否删除  1是 0否(默认0)
	CreateTime   time.Time `gorm:"column:create_time"`   // 创建时间
	UpdateTime   time.Time `gorm:"column:update_time"`   // 修改时间
	Remark       string    `gorm:"column:remark"`        // 描述
}

// TableName 指定表名
func (SysUser) TableName() string {
    
    
	return "sys_user"
}

4.2.4、系统用户响应结构

在 app/vo 目录修改文件 sys_user_vo.go,完整代码如下:

package vo

// 系统用户信息
type SysUserVo struct {
    
    
	Id          int64  `json:"id"`          // 用户 ID
	Name        string `json:"name"`        // 用户姓名
	Account     string `json:"account"`     // 用户账号
	MailAddress string `json:"mailAddress"` // 邮箱地址
	PhoneNumber string `json:"phoneNumber"` // 手机号码
	Age         int    `json:"age"`         // 用户年龄
	IsUsing     int    `json:"isUsing"`     // 是否启用  1是 0否(默认0)
	IsDelete    int    `json:"isDelete"`    // 是否删除  1是 0否(默认0)
	CreateTime  string `json:"createTime"`  // 创建时间
	UpdateTime  string `json:"updateTime"`  // 修改时间
	Remark      string `json:"remark"`      // 描述
	Token       string `json:"token"`       // token
}


4.2.5、分页数据响应结构

注意:此结构体是分页核心
在 app/vo 目录新建文件 page.go,完整代码如下:

package vo

// 分页数据结构
type Page struct {
    
    
	Current int64       `json:"current"` // 当前页码
	Size    int64       `json:"size"`    // 每页条数
	Total   int64       `json:"total"`   // 总数据量
	Pages   int64       `json:"pages"`   // 总分页数
	Records interface{
    
    } `json:"records"` // 分页数据
}

/**
 * <h2>提供了一个构造方法</h2>
 * 需要传入当前页码和每页条数
 */
func NewPage(current, size int64) *Page {
    
    
	return &Page{
    
    Current: current, Size: size}
}

/**
 * <h2>设置总数据量</h2>
 */
func (p *Page) SetTotal(t int64) {
    
    
	p.Total = t

	// 总分页数 = 总数据量 / 每页条数
	s := p.GetTotal() / p.GetSize()

	// 如果总数据量 / 每页条数有余数,则总分页数 + 1
	if p.GetTotal()%p.GetSize() != 0 {
    
    
		s = s + 1
	}

	p.Pages = s
}

/**
 * <h2>设置分页数据</h2>
 */
func (page *Page) SetRecords(records interface{
    
    }) {
    
    
	page.Records = records
}

/**
 * <h2>获取当前页码</h2>
 * 默认当前页码 1
 */
func (p *Page) GetCurrent() int64 {
    
    
	if p.Current < 1 {
    
    
		p.Current = 1
	}
	return p.Current
}

/**
 * <h2>获取数据偏移量</h2>
 */
func (p *Page) GetOffset() int64 {
    
    
	if p.GetCurrent() > 0 {
    
    
		return (p.GetCurrent() - 1) * p.GetSize()
	}
	return 0
}

/**
 * <h2>获取每页条数</h2>
 * 默认每页 10 条数据
 */
func (p *Page) GetSize() int64 {
    
    
	if p.Size < 1 {
    
    
		p.Size = 10
	}
	return p.Size
}

/**
 * <h2>获取总数据量</h2>
 */
func (p *Page) GetTotal() int64 {
    
    
	return p.Total
}

/**
 * <h2>获取总分页数</h2>
 */
func (p *Page) GetPages() int64 {
    
    
	return p.Pages
}

/**
 * <h2>获取分页数据</h2>
 */
func (page *Page) GetRecords() interface{
    
    } {
    
    
	return page.Records
}

4.2.6、系统用户业务处理

注意:此业务处理类本次项目进阶核心代码

  • 用户登录校验由固定密码改为数据库动态校验
  • 系统用户增加数据
  • 系统用户修改数据
  • 系统用户删除数据
  • 系统用户查询数据
  • 系统用户查询数据列表

在 app/service 目录修改文件 sys_user_service.go,完整代码如下:

package service

import (
	"fmt"
	"go-iris/app/dto"
	"go-iris/app/model"
	"go-iris/app/vo"
	"go-iris/utils/constant"
	"go-iris/utils/db"
	"go-iris/utils/tool"
)

/**
 * <h1>系统用户业务处理</h1>
 * Created by woniu
 */
var SysUser = new(sysUserService)

type sysUserService struct{
    
    }

/**
 * <h2>用户账号 + 用户密码登录</h2>
 */
func (service *sysUserService) PasswordLogin(account string, password string) (*vo.SysUserVo, error) {
    
    
	// 用户账号为空
	if tool.IsEmpty(account) {
    
    
		return nil, constant.ResErrSysUserIsNil
	}

	// 用户密码为空
	if tool.IsEmpty(password) {
    
    
		return nil, constant.ResErrSysUserPasswordErr
	}

	// 根据用户账号查询用户信息
	sysUser := service.GetSysUserByAccount(account)

	if sysUser == nil {
    
    
		return nil, constant.ResErrSysUserIsNil
	}

	// 密码不匹配(暂时使用明文密码比对)
	if password != sysUser.Password {
    
    
		return nil, constant.ResErrSysUserPasswordErr
	}

	// 用户名 + 密码 校验正确,返回用户信息
	return service.GetSysUserVo(sysUser), nil
}

/**
 * <h2>根据用户账号查询用户信息</h2>
 */
func (service *sysUserService) GetSysUserByAccount(account string) *model.SysUser {
    
    
	var sysUser = &model.SysUser{
    
    }
	// 根据用户账号查询一条数据,主键 ID 排序
	result := db.GormDb.Table(sysUser.TableName()).Where("account = ?", account).First(sysUser)
	// 查询不到记录报错
	if result.Error != nil {
    
    
		fmt.Println("数据查询异常:", result.Error)
		return nil
	}
	return sysUser
}

/**
 * <h2>新增系统用户</h2>
 */
func (service *sysUserService) Save(sysUser model.SysUser) (*vo.SysUserVo, error) {
    
    

	// 根据用户账号查询用户信息
	sysUserOld := service.GetSysUserByAccount(sysUser.Account)
	if sysUserOld != nil {
    
    
		return nil, constant.ResErrSysUserRepeat
	}

	// 主键采用数据库自增
	sysUser.Id = 0
	// 创建时间
	sysUser.CreateTime = tool.DataTimeNow()
	// 修改时间
	sysUser.UpdateTime = sysUser.CreateTime

	// 创建记录
	result := db.GormDb.Table(sysUser.TableName()).Create(&sysUser)
	// 返回 error
	if result.Error != nil {
    
    
		fmt.Println("数据新增异常:", result.Error)
		return nil, result.Error
	}
	fmt.Println("数据新增成功,主键ID:", sysUser.Id)
	return service.GetSysUserVo(&sysUser), nil
}

/**
 * <h2>根据主键 ID 修改用户</h2>
 */
func (service *sysUserService) UpdateById(sysUser model.SysUser) (*model.SysUser, error) {
    
    
	if sysUser.Id < 1 {
    
    
		return nil, constant.ResErrIdIsNil
	}

	// 如果修改用户账号,则需要判断账号是否重复
	if tool.IsNotEmpty(sysUser.Account) {
    
    
		// 根据用户账号查询用户信息
		sysUserOld := service.GetSysUserByAccount(sysUser.Account)
		// 如果用户账号存在并且ID不相等
		if sysUserOld != nil && sysUserOld.Id != sysUser.Id {
    
    
			return nil, constant.ResErrSysUserRepeat
		}
	}

	// 修改时间
	sysUser.UpdateTime = tool.DataTimeNow()

	// Updates 方法支持 struct 和 map[string]interface{} 参数。
	// 当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段
	result := db.GormDb.Table(sysUser.TableName()).Model(&sysUser).Updates(&sysUser)
	// 返回 error
	if result.Error != nil {
    
    
		fmt.Println("数据修改异常:", result.Error)
		return nil, result.Error
	}
	return service.GetById(sysUser)
}

/**
 * <h2>根据主键 ID 查询用户</h2>
 */
func (service *sysUserService) GetById(sysUser model.SysUser) (*model.SysUser, error) {
    
    
	if sysUser.Id < 1 {
    
    
		return nil, constant.ResErrIdIsNil
	}
	// 当目标对象有一个主要值时,将使用主键构建条件
	result := db.GormDb.Table(sysUser.TableName()).First(&sysUser)
	// 返回 error
	if result.Error != nil {
    
    
		fmt.Println("数据修改异常:", result.Error)
		return nil, result.Error
	}
	return &sysUser, nil
}

/**
 * <h2>根据主键 ID 删除用户</h2>
 */
func (service *sysUserService) RemoveById(sysUser model.SysUser) error {
    
    
	if sysUser.Id < 1 {
    
    
		return constant.ResErrIdIsNil
	}
	// 删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete
	result := db.GormDb.Table(sysUser.TableName()).Delete(&sysUser)
	// 返回 error
	if result.Error != nil {
    
    
		fmt.Println("数据删除异常:", result.Error)
		return result.Error
	}
	return nil
}

/**
 * <h2>检索全部对象</h2>
 */
func (service *sysUserService) List(sysUser model.SysUser) ([]*vo.SysUserVo, error) {
    
    
	var sysUserList []model.SysUser
	// 检索全部对象
	result := db.GormDb.Table(sysUser.TableName()).Find(&sysUserList)
	// 返回 error
	if result.Error != nil {
    
    
		fmt.Println("数据列表查询异常:", result.Error)
		return nil, result.Error
	}
	return service.GetSysUserVoList(sysUserList), nil
}

/**
 * <h2>检索全部对象 - 分页查询</h2>
 */
func (service *sysUserService) Page(current int64, size int64, sysUserDto dto.SysUserDto) (*vo.Page, error) {
    
    
	page := vo.NewPage(current, size)

	var sysUserList []model.SysUser

	var sysUser model.SysUser
	query := db.GormDb.Table(sysUser.TableName())
	var count int64 // 统计总的记录数
	query.Count(&count)

	if count > 0 {
    
    
		result := query.Limit(int(page.GetSize())).Offset(int(page.GetOffset())).Find(&sysUserList)
		// 返回 error
		if result.Error != nil {
    
    
			fmt.Println("数据分页查询异常:", result.Error)
			return nil, result.Error
		}
	}

	page.SetTotal(count)
	page.SetRecords(sysUserList)

	fmt.Println(page.GetRecords())

	return page, nil
}

/**
 * <h2>将 SysUser 集合 转换为 SysUserVo 集合</h2>
 */
func (service *sysUserService) GetSysUserVoList(sysUserList []model.SysUser) []*vo.SysUserVo {
    
    
	var sysUserVoList []*vo.SysUserVo

	for i := range sysUserList {
    
    
		// 将 SysUser 转换为 SysUserVo
		vo := service.GetSysUserVo(&sysUserList[i])
		sysUserVoList = append(sysUserVoList, vo)
	}

	return sysUserVoList
}

/**
 * <h2>将 SysUser 转换为 SysUserVo</h2>
 */
func (service *sysUserService) GetSysUserVo(sysUser *model.SysUser) *vo.SysUserVo {
    
    
	var sysUserVo = vo.SysUserVo{
    
    
		Id:          sysUser.Id,
		Name:        sysUser.Name,
		Account:     sysUser.Account,
		MailAddress: sysUser.MailAddress,
		PhoneNumber: sysUser.PhoneNumber,
		Age:         sysUser.Age,
		IsUsing:     sysUser.IsUsing,
		IsDelete:    sysUser.IsDelete,
		CreateTime:  tool.DataTimeFormat(sysUser.CreateTime),
		UpdateTime:  tool.DataTimeFormat(sysUser.UpdateTime),
		Remark:      sysUser.Remark,
	}

	return &sysUserVo
}

4.2.7、系统用户控制器

在 app/controller 目录修改文件 sys_user_controller.go,完整代码如下:

package controller

import (
	"fmt"
	"go-iris/app/dto"
	"go-iris/app/model"
	"go-iris/app/service"
	"go-iris/utils/common"
	"go-iris/utils/tool"

	"github.com/kataras/iris/v12"
)

/**
 * <h1>系统用户控制器</h1>
 * Created by woniu
 */
var SysUser = new(SysUserController)

type SysUserController struct{
    
    }

/**
 * <h2>用户名密码登录</h2>
 */
func (sc *SysUserController) PasswordLogin(ctx iris.Context) {
    
    

	// 登录参数
	var requestModel dto.LoginDto

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 用户登录,用户账号 + 用户密码登录
	sysUser, err := service.SysUser.PasswordLogin(requestModel.Account, requestModel.Password)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 生成 JWT 的 Token
	sysUser.Token = tool.SignedJwtTokenBySysUser(sysUser)

	// 响应成功
	ctx.JSON(common.ResponseSuccess(sysUser))
}

/**
 * <h2>新增用户</h2>
 */
func (sc *SysUserController) Save(ctx iris.Context) {
    
    
	// 参数
	var requestModel model.SysUser

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 新增系统用户
	_, err := service.SysUser.Save(requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(nil))
}

/**
 * <h2>根据主键 ID 修改用户</h2>
 */
func (sc *SysUserController) UpdateById(ctx iris.Context) {
    
    
	// 参数
	var requestModel model.SysUser

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 根据主键 ID 修改用户
	_, err := service.SysUser.UpdateById(requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(nil))
}

/**
 * <h2>根据主键 ID 查询用户</h2>
 */
func (sc *SysUserController) GetById(ctx iris.Context) {
    
    
	// 参数
	var requestModel model.SysUser

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 根据主键 ID 查询用户
	sysUser, err := service.SysUser.GetById(requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(sysUser))
}

/**
 * <h2>根据主键 ID 删除用户</h2>
 */
func (sc *SysUserController) RemoveById(ctx iris.Context) {
    
    
	// 参数
	var requestModel model.SysUser

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 根据主键 ID 删除用户
	err := service.SysUser.RemoveById(requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(nil))
}

/**
 * <h2>条件查询系统用户列表</h2>
 */
func (sc *SysUserController) List(ctx iris.Context) {
    
    
	// 参数
	var requestModel model.SysUser

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 检索全部对象
	sysUser, err := service.SysUser.List(requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(sysUser))
}

/**
 * <h2>条件查询系统用户列表</h2>
 */
func (sc *SysUserController) Page(ctx iris.Context) {
    
    
	// 参数
	var requestModel dto.SysUserDto

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	fmt.Println("requestModel = ", requestModel)

	// 检索全部对象 - 分页查询
	page, err := service.SysUser.Page(requestModel.Current, requestModel.Size, requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(page))
}

4.2.8、请求路由

在 router 目录新建文件 router.go,完整代码如下:

package router

import (

	// 自己业务 controller 路径
	"fmt"
	"go-iris/app/controller"
	"go-iris/utils/common"
	"go-iris/utils/constant"
	"go-iris/utils/tool"

	"github.com/kataras/iris/v12"
)

/**
 * <h2>注册路由</h2>
 */
func RegisterRouter(app *iris.Application) {
    
    

	// 跨域解决方案
	app.Use(Cors)
	// 登录验证中间件
	app.Use(CheckAuthorization)

	// 系统用户
	login := app.Party("/sysUser")
	{
    
    
		// 用户名 + 密码登录
		login.Post("/passwordLogin", controller.SysUser.PasswordLogin)
		// 新增用户
		login.Post("/save", controller.SysUser.Save)
		// 根据主键 ID 修改用户
		login.Post("/updateById", controller.SysUser.UpdateById)
		// 根据主键 ID 查询用户
		login.Post("/getById", controller.SysUser.GetById)
		// 根据主键 ID 删除用户
		login.Post("/removeById", controller.SysUser.RemoveById)
		// 条件查询系统用户列表
		login.Post("/list", controller.SysUser.List)
		// 条件查询系统用户列表 - 分页
		login.Post("/page", controller.SysUser.Page)
	}

	// 通用异常
	err := app.Party("/error")
	{
    
    
		err.Get("/responseError", controller.Error.ResponseError)
		err.Get("/responseErrorCode", controller.Error.ResponseErrorCode)
		err.Get("/responseErrorMessage", controller.Error.ResponseErrorMessage)
	}
}

/**
 * <h2>跨域解决方案</h2>
 */
func Cors(ctx iris.Context) {
    
    
	ctx.Header("Access-Control-Allow-Origin", "*")
	if ctx.Request().Method == "OPTIONS" {
    
    
		ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS")
		ctx.Header("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization")
		ctx.StatusCode(204)
		return
	}
	ctx.Next()
}

/**
 * <h2>登录验证中间件</h2>
 */
func CheckAuthorization(ctx iris.Context) {
    
    
	fmt.Println("登录验证中间件", ctx.Path())
	// 放行设置
	urlItem := []string{
    
    "/sysUser/passwordLogin", "/sysUser/login"}
	if !tool.IsStringInArray(ctx.Path(), urlItem) {
    
    
		// 从请求头中获取Token
		token := ctx.GetHeader("Authorization")

		// 请求头 Authorization 为空
		if tool.IsEmpty(token) {
    
    
			ctx.JSON(common.ResponseErrorMessage(constant.ResErrAuthorizationIsNilErr.Error()))
			return
		}

		claims, err := tool.ParseWithClaims(token)
		if err != nil {
    
    
			fmt.Println("token 解析异常信息:", err)
			ctx.JSON(common.ResponseErrorMessage(err.Error()))
			return
		}

		// 打印消息
		claims.ToString()
	}
	// 前置中间件
	ctx.Application().Logger().Infof("Runs before %s", ctx.Path())
	ctx.Next()
}

相关代码已完成编写,最终项目结构及文件如下图

在这里插入图片描述

5、启动并测试

5.1、启动项目

在 VS Code 终端输入以下命令并执行

# 启动项目
go run main.go

有以下信息代表启动成功

Iris Version: 12.2.0-beta6

Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.

5.2、测试接口

5.2.1、用户登录

注意:需要确保系统用户表存在一条初始化数据,没有数据会提示用户不存在

在这里插入图片描述

account = admin 并且 password = 123456,响应成功

在这里插入图片描述

5.2.2、新增数据

注意:请求头添加 Authorization

在这里插入图片描述

5.2.3、主键 ID 修改

注意:请求头添加 Authorization

在这里插入图片描述

5.2.4、主键 ID 查询

注意:请求头添加 Authorization

在这里插入图片描述

5.2.5、查询数据列表

注意:请求头添加 Authorization

在这里插入图片描述

响应结果数据示例如下:

{
    
    
    "code": 0,
    "message": "操作成功",
    "data": [
        {
    
    
            "id": 1,
            "name": "超级管理员",
            "account": "admin",
            "mailAddress": "",
            "phoneNumber": "",
            "age": 0,
            "isUsing": 0,
            "isDelete": 0,
            "createTime": "2022-12-12 17:54:39",
            "updateTime": "2022-12-12 17:54:39",
            "remark": "",
            "token": ""
        },
        {
    
    
            "id": 2,
            "name": "测试用户名称-修改后",
            "account": "test",
            "mailAddress": "[email protected]",
            "phoneNumber": "158XXXXXXXX",
            "age": 18,
            "isUsing": 0,
            "isDelete": 0,
            "createTime": "2022-12-12 17:59:59",
            "updateTime": "2022-12-12 18:15:35",
            "remark": "我是通过接口创建的-修改后",
            "token": ""
        }
    ]
}

5.2.6、分页查询

注意:请求头添加 Authorization

在这里插入图片描述

响应结果数据示例如下:

{
    
    
    "code": 0,
    "message": "操作成功",
    "data": {
    
    
        "current": 1,
        "size": 1,
        "total": 2,
        "pages": 2,
        "records": [
            {
    
    
                "Id": 1,
                "Name": "超级管理员",
                "Account": "admin",
                "Password": "123456",
                "PasswordSalt": "",
                "MailAddress": "",
                "PhoneNumber": "",
                "Age": 0,
                "IsUsing": 0,
                "IsDelete": 0,
                "CreateTime": "2022-12-12T17:54:39+08:00",
                "UpdateTime": "2022-12-12T17:54:39+08:00",
                "Remark": ""
            }
        ]
    }
}

5.2.7、主键 ID 删除

注意:请求头添加 Authorization

在这里插入图片描述

通过上述测试验证,项目达到预期目标,小伙伴自己赶紧动手试试吧。

6、每日一记

GO 的 ORM框架有那些?哪个才是最优雅的Go ORM 框架?

6.1、GORM

Golang 的出色 ORM 库旨在对开发人员友好。

6.1.1、Github

Github

6.1.2、文档

中文文档

6.1.3、安装

# gorm 安装
go get -u gorm.io/gorm

# mysql 驱动
go get -u gorm.io/driver/mysql

6.2、XORM

xorm 是一个简单而强大的 Go 语言 ORM 库. 通过它可以使数据库操作非常简便。

6.2.1、Github

Github

6.2.2、文档

中文文档

6.2.3、安装

# xorm 安装
go get -u github.com/go-xorm/xorm

# mysql 驱动
go get -u github.com/go-sql-driver/mysql

6.3、Ent

ent 是一个简单而又功能强大的 Go 语言实体框架,ent 易于构建和维护应用程序与大数据模型。

6.3.1、Github

Github

6.3.2、文档

中文文档

6.2.3、安装

# ent 安装
go get -u entgo.io/ent/cmd/ent@latest

6.4、其他

还有其他一些ORM框架,例如:gorose、upper/db 等等。

6.5、如何选择?

没有最好的只有比较合适的,自己根据实际需求按需选择即可。

本文教程到此结束,有问题欢迎大家讨论。

实践是检验真理的唯一标准,一键送三连关注不迷路。

猜你喜欢

转载自blog.csdn.net/u011374856/article/details/128286993