首页 > 其他分享 >实战 图书馆系统管理案例

实战 图书馆系统管理案例

时间:2023-09-04 18:01:03浏览次数:55  
标签:实战 系统管理 tx 案例 token user Error model gorm


实战 图书馆系统管理案例_mysql

  • config :敏感的配置一般都是在配置中心配置,比如consul或者阿波罗上面
  • controller :写一些handler的,拿到参数要去调用service层的逻辑。(只负责接受参数,怎么绑定参数,要去调用哪个service的,handler的一个入口)
  • service:service层才是正真的业务处理层。调用dao层的增删改查操作
  • dao:只关心数据库的一个操作,其实你有数据库和es都在dao层
  • db:负责数据库,中间件层的初始化
  • middleware:存放gin的中间件,在注册路由的时候将中间件引入进来

go常用项目结构


实战 图书馆系统管理案例_mysql_02

dao层只关心数据库的操作,db层只做Mysql层的初始化。model去定义表结构,定义中间表,多对多的结构体。 

中间件有个token的验证。

controller层,先去找路由,这个项目有多少的接口(router.go),每个接口到哪些对应的handler。

controller层接收完参数之后,再看调用到哪个service,service再去干了什么增删改查操作。

数据库增删改查就去dao层查看。

这一层一层非常的分明。上面可以理解为日常开发的目录结构最佳实践。

创建数据库


mysql> create database books charset utf8;

图书管理服务


用户服务:登录,注册


书籍服务:对书籍的增删改查的操作


主要有两个维度,一个是书籍维度,一个是用户维度。用户维度要去写两个功能,一个功能是登入,一个功能是注册。



注册就是数据库插入一条数据。登入就是一个校验。配合token中间件去做一个校验。



在新增书籍的时候要去关联用户,这本书属于谁?书籍的增删改查都需要关联这个用户。这里就涉及到1对多的关系。



开发的时候是从最底层开始,往上开发。第一个先去定义数据库的model层。






  

model层 定义数据库结构


binding标签表示是必填项,token是可以为空的,因为一开始注册的时候token的为空。只有登入的时候才有token。

user.go

package model

type User struct {
	ID       int64  `gorm:"primaryKey" json:"id"`
	UserName string `gorm:"not null" json:"username" binding:"required"`
	PassWord string `gorm:"not null" json:"password" binding:"required"`
	Token    string `json:"token"`
}

func (*User) TableName() string {
	return "user"
}

book.go

package model

type Book struct {
	ID    int64  `gorm:"primaryKey" json:"id"`
	Name  string `gorm:"not null" json:"name" binding:"required"`
	Desc  string `json:"desc"`
	Users []User `gorm:"many2many:book_users"`
}

func (*Book) TableName() string {
	return "book"
}

多对多关系可以在user这层定义也行,在book这一层定义也可以。这个主要看你的一个实际使用的场景,这里在book模型里面去定义就行了。

还需要去定义一个中间表的模型user_m2m_book.go

package model

type BookUser struct {
	UserID int64 `gorm:"primaryKey"`
	BookID int64 `gorm:"primaryKey"`
}

这里不需要自定义表名,它只有一个主键,也没有其他属性了。它也是永了外键,也是使用了那两个模型的主键。

实战 图书馆系统管理案例_初始化_03

DB层


模型定义好之后去做数据库的初始化,这里需要预留,因为可能不仅仅只有MySQL的初始化。

实战 图书馆系统管理案例_mysql_04

这样赋值到一个全局变量,之后使用mysql.DB就可以在任何地方去使用了。 

package mysql

import (
	"book/model"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// DB 要将DB实例放到全局变量,这样就可以使用mysql.DB在任何地方去使用了.
var DB *gorm.DB

func InitMysql() {
	dsn := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/books?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	//这个地方Log和panic都是可以的
	if err != nil {
		fmt.Println(err)
	}

	DB = db
	
	err = DB.AutoMigrate(model.Book{}, model.User{}, model.BookUser{})
	if err != nil {
		fmt.Println(err)
	}
}




DAO层



初始化好之后在dao层开始写,dao层的操作就是数据库的增删改查

package dao

import (
	"book/db/mysql"
	"book/model"
	"errors"
	"fmt"
	"github.com/wonderivan/logger"
	"gorm.io/gorm"
)

//定义user结构体,以及User变量,能够直接跨包调用user下面的方法
//只需要一次初始化即可,不用每次调用的都是先初始化

var User user

type user struct {
}

// Add 新增 用于注册
func (*user) Add(user *model.User) error {
	if tx := mysql.DB.Create(user); tx.Error != nil {
		//打印错误提示
		logger.Error(fmt.Sprintf("添加User失败,错误信息:%s", tx.Error))
		return errors.New(fmt.Sprintf("添加User失败,错误信息:%s", tx.Error))
	}
	return nil
}

// Has 查询 基于name 用于新增
func (*user) Has(name string) (*model.User, bool, error) {
	//初始化要申请内存,不然会报错
	data := &model.User{}
	tx := mysql.DB.Where("username = ?", name).Find(data)

	//如果记录没有查询到,报错是从tx.error里面拿到的,相当于tx.Error = gorm.ErrRecordNotFound
	if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
		return nil, false, nil
	}
	//等会去调用的时候,不会先去判断有没有,要先去判断error,有error就是真正的错误,没有判断是不是false
	if tx.Error != nil {
		logger.Error(fmt.Sprintf("查询用户失败,错误信息:%s", tx.Error))
		return nil, false, errors.New(fmt.Sprintf("查询用户失败,错误信息:%s", tx.Error))
	}

	return data, true, nil
}

// GetByToken 基于token查询,用于中间件token校验
func (*user) GetByToken(token string) (*model.User, bool, error) {
	//初始化要申请内存,不然会报错
	data := &model.User{}
	tx := mysql.DB.Where("token = ?", token).Find(data)

	//如果记录没有查询到,报错是从tx.error里面拿到的,相当于tx.Error = gorm.ErrRecordNotFound
	if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
		return nil, false, nil
	}
	if tx.Error != nil {
		logger.Error(fmt.Sprintf("查询用户失败%s", tx.Error))
		return nil, false, errors.New(fmt.Sprintf("查询用户失败%v/n", tx.Error))
	}
	return data, true, nil
}

// UPDateToken 更新token,这里其实就是找到user的条件去更新就行了
// 第一个参数可以是id也可以是用户名,如果用户名唯一,只要保证找到指定用户即可
// 除了查询的操作以外,增删改只需要返回一个error即可,判断操作有没有成功
func (*user) UPDateToken(user *model.User, token string) error {
	tx := mysql.DB.Model(user).
		Where("username= ? and password = ?", user.UserName, user.PassWord).
		Update("token", token)
	if tx.Error != nil {
		logger.Error(fmt.Sprintf("更新user token失败,错误信息:%s", tx.Error))
		return errors.New(fmt.Sprintf("更新user token失败,错误信息:%s", tx.Error))
	}
	return nil
}


service层


和dao层一样,都定义了相同的结构体,不同的包使用相同的结构体变量,但是点出来的方法都是不同的。

标签:实战,系统管理,tx,案例,token,user,Error,model,gorm
From: https://blog.51cto.com/u_14035463/7353837

相关文章

  • Java对象与json的转换使用的依赖是fastjson,转换的简单案例
    2023-09-04<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency>转换的简单案例packagecom.hh.json;importcom.alibaba.fastjson......
  • 服务器数据恢复- 执行fsck导致Ext4分区无法挂载的数据恢复案例
    Ext4文件系统相关概念:块组:Ext4文件系统的空间被划分为若干个块组,每个块组内的结构大致相同。块组描述符表:每个块组都对应一个块组描述符,这些块组描述符统一放在文件系统的前部,称为块组描述符表。每个块组描述符大小为32字节,其主要描述块位图、i-节点位图及i-节点表的地址等信息。......
  • 数仓资源管控理论已掌握,是时候实战了
    本文分享自华为云社区《直播回顾|数仓资源管控理论已掌握,是时候实战了》,作者:胡辣汤。混合负载场景下,如何高效运维数据库,防止数据库系统过载?GaussDB(DWS)资源管控为数据库平稳可靠运行提供了哪些助力?本期《数仓专家手把手教您资源管控与运维实战》的主题直播中,我们邀请到华为云......
  • 【腾讯云 Cloud Studio 实战训练营】使用在线编程的方式用Nuxt3开发一个后台管理系统(
    前言大家好,我是刘明,开源技术爱好者,十年创业老兵。CSDN近期联合腾讯云、Coding、CloudStudio组织了【腾讯云CloudStudio实战训练营活动】,苦于前些日子一直在备考注册会计师,没有很好的体验CloudStudio的云IDE产品。现在考试结束了,体验了一把云IDE,不禁感慨云端开发原来可以这么......
  • 振弦采集仪应用于水库大坝的解决案例
    水库大坝是保障人民生命财产安全的重要设施,而对大坝的安全监测也是至关重要的。为了更好地了解和监测大坝的运行状态,振弦采集仪被广泛应用于水库大坝的解决方案中。振弦采集仪是一种能够准确测量和监测大坝振动的设备。它利用振弦传感器来捕捉大坝的振动信号,并将信号转化为电信号,......
  • servlet,jsp,jstl用到的依赖与brand.jsp简单案例
    2023-09-03<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsd">&......
  • python案例
    游戏案例:猜数字游戏玩法说明:程序随机生成一个1到100之间的整数作为答案。玩家可以输入自己猜测的数字。如果玩家猜对了答案,则游戏结束,程序输出恭喜信息。如果玩家猜错了,程序会根据玩家的猜测给出一些提示信息,比如太大了或者太小了。代码语法:importrandomanswer=rand......
  • 文件上传实战和分析
    1前言通常再一个web程序中,一般会存在登陆注册功能,登陆后一般会有上传头像等功能,如果上传验证不严格就可能造成恶意第三方直接上传马,进而控制整个web业务控制权。下面通过实例,如果程序只进行了客户端JavaScript检测,咱们如何来绕过。2正文2.1客户端后缀检测工具准备:DVWA程序,burpsu......
  • 什么是 JMX?(Trino JMX 实战讲解)
    目录一、概述二、JMX原理三、实战操作(开启TrinoJMX)1)环境部署2)开启TrinoJMX1、配置config.properties2、配置jvm.config3、重新启动服务4、获取监控数据3)通过jconsole连接JMX4)常用的Trino指标接口和指标一、概述JMX是JavaManagementExtensions(Java管理扩展)的缩......
  • 大数据实战-Hive-技巧实战_2LgaeiFwLs7mCTwG5T3c9M
    大数据实战-Hive-技巧实战_2LgaeiFwLs7mCTwG5T3c9M大数据实战-Hive-技巧实战1.union和unionall前者可以去重selectsex,addressfromtestwheredt='20210218'unionallselectsex,addressfromtestwheredt='20210218';+------+----------+--+|sex|address|......