实战 图书馆系统管理案例

news2024/12/29 10:38:54

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

go常用项目结构 


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"`
}

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


DB层


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

这样赋值到一个全局变量,之后使用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层一样,都定义了相同的结构体,不同的包使用相同的结构体变量,但是点出来的方法都是不同的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/946289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT6修改程序图标和名字以及打包部署

首先确定已经编译成功无错误 修改窗口的名字 修改图标 当有pro工程文件时, 只需要将ico文件放在工程文件的同级文件夹中,然后在pro文件中加入RC_ICONSico文件的名字 当使用cmake建立工程时 参考:https://blog.csdn.net/chqaz123/article…

PowerBuilder调用外部VB6 ActiveX EXE公共对象

学习的同时习练PowerBuilder对VB6 ActiveX exe公共对象的调用,初步感觉PowerBuilder调用DLL还是要求比较严格的,APP和powerbuilder本身都比较脆弱、易崩溃,因此, 1. 直接调用外部DLL时,传递地址参数,尽量不…

音频接口电路的PCB设计注意事项

Audio接口是音频插孔,即音频接口,可分为Audio in接口和Audio out接口。音频接口是连接麦克风和其他声源与计算机的设备,其在模拟和数字信号之间起到了桥梁连接的作用。 其余走线要求如下: 1、所有CLK信号建议串接22ohm电阻&#…

【零基础算法】Vector动态数组

为什么开始先更新数据结构?博主其实一开始也不怎么喜欢调这些数据,觉得用C语言造轮子才是最好的。后面学习过程中学习的算法逐渐复杂,实际上会发现,了解和调用一些已经写好的库工具是很方便的一件事,我们需要做的是知道…

计算机竞赛 基于机器视觉的手势检测和识别算法

0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng…

伺服电机驱动器EMC处理方案

伺服驱动器内部也有低压单元, 很可能受到驱动器外围设备的噪音干扰,受到干扰的信号可能会引起设备做出意想不到的动作 为防止伺服驱动器和其外围设备之间的相互电磁干扰, 可根据采取以下的对策: ● 请务必使驱动器及电机良好的接…

USB与蓝牙通信原理图设计

蓝牙模块设计: USB2.0 ----ESD保护芯片---- USB转串口芯片(CP2104-F03-GMR)--------- PTR5618蓝牙模块 ---------- PTR5618的GPIO0\GPIO1作为IIC与EEPROM芯片通信 (AT24CS04-STUM-T) 磁珠的作用是一直EMI干扰。 一…

《Go 语言第一课》课程学习笔记(十二)

函数 Go 函数与函数声明 在 Go 语言中,函数是唯一一种基于特定输入,实现特定任务并可返回任务执行结果的代码块(Go 语言中的方法本质上也是函数)。在 Go 中,我们定义一个函数的最常用方式就是使用函数声明。 第一部…

虚拟直播从0到1全套解决方案,码住这个神器,小白也能轻松涨粉过万!

直播行业历经十余年生长,用户规模持续上升,成为平台经济领域的中流砥柱。同时,元宇宙的兴起也推动了虚拟现实等先进技术在直播行业的应用,越来越多的老板开始尝试使用虚拟直播完成各类形式的直播活动,与传统型的直播间…

Web核心基础总结

尚硅谷课程:293-尚硅谷-Filter-什么是Filter过滤器_哔哩哔哩_bilibili JavaWeb核心技术点: Servlet程序、Filter过滤器、Listener监听器、jsp页面、EL表达式、JSTL标签库、jQuery框架、Cookie技术、Session会话、JSON使用、Ajax请求, 第一…

独立服务编排逻辑引擎:jvs-logic服务原子组件介绍

逻辑引擎的本质是可视化的服务编排是指使用图形化工具、配置和管理原子服务间的工作步骤和调用关系。这种方法可以快速的实现业务功能,使非开发人员也能轻松地创建和管理服务的工作流程。 可视化服务编排工具提供了直观的界面和丰富的功能,以及便捷的执行…

【 ARMv9 Cluster BUS QoS 配置】

文章目录 ARM Cluster QoS ARM Cluster QoS QoS(Quality of Service,服务质量)在 ARM 架构中,主要指的是一种机制,它可以控制和管理系统资源(如内存、总线带宽等)的使用,以满足各种…

关于#include<bits/stdc++.h>的说明

关于#include<bits/stdc.h>的说明 在看C/C竞赛题时&#xff0c;经常看到#include<bits/stdc.h>。什么意思呢&#xff1f; 有人称<bits/stdc.h>为万能头文件。其 优点&#xff1a;   1、在竞赛中节约时间。   2、减少了编写所有必要头文件的工作量。  …

SSM - Springboot - MyBatis-Plus 全栈体系(二)

第一章 Maven 三、Maven 核心功能依赖和构建管理 1. 依赖管理和配置 Maven 依赖管理是 Maven 软件中最重要的功能之一。Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题&#xff0c;使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程…

安全帽人脸联动闸机开关算法

安全帽人脸联动闸机开关算法通过yolov7python网络模型深度学校框架 &#xff0c;安全帽人脸联动闸机开关算法能够判断人员是否穿戴规定的工装是不是现场人员&#xff0c;当穿戴合规且为现场人员&#xff0c;闸机门禁才打开。YOLO的结构非常简单&#xff0c;就是单纯的卷积、池化…

PostgreSQL中字符串与ASCII码互转

PostgreSQL中字符串与ASCII码互转 一、字符串转ASCII码&#xff1a;ascii ( text ) → integer二、ASCII码转字符串&#xff1a;chr ( integer ) → text 一、字符串转ASCII码&#xff1a;ascii ( text ) → integer 返回参数的第一个字符的数字代码。在UTF8编码中&#xff0c…

I IntelliJ IDEA 2023.2 最新解锁方式,支持java20

在 IntelliJ IDEA 2023.1 中&#xff0c;我们根据用户的宝贵反馈对新 UI 做出了大量改进。 我们还实现了性能增强&#xff0c;从而更快导入 Maven&#xff0c;以及在打开项目时更早提供 IDE 功能。 新版本通过后台提交检查提供了简化的提交流程。 IntelliJ IDEA Ultimate 现在支…

解决Docker镜像国内无法下载问题

近期由于docker镜像仓库禁止国内下载&#xff0c;国内各平台均无法下载更新最新docker镜像。为解决此问题&#xff0c;大家可以使用dockerproxy代理下载。 使用方法&#xff1a; 大家只需打开docker proxy代理网站&#xff0c;在快捷命令中输入所需镜像和版本&#xff0c;点击…

基于野狗算法优化的BP神经网络(预测应用) - 附代码

基于野狗算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于野狗算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.野狗优化BP神经网络2.1 BP神经网络参数设置2.2 野狗算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

23款奔驰S500加装车载冰箱系统,快乐就是这么朴实无华呀

凉爽餐饮随时触手可及。容积10升的可拆卸冷藏箱与后排扶手和谐融合。如此一来&#xff0c;即使在炎炎夏日&#xff0c;也可享受沁凉的冷饮。