使用Gin-Vue- Admin框架手动实现crud
在gva框架下自己手动实现一个CRUD的操作,该操作将会结合gen进行探讨学习,具体实现可以看下面代码的实现,项目目录层级分为api层,service层,model层,common层,router层,gen层。
我的这个实现方式有些许不太一样就是,我的修改并非传统的restful风格
通过json方式进行传递这个id,也确保安全,所以这里的请求我不会用Get请求
。
创建一个Banner
在gva下的server文件夹中找到plugin文件,然后在这里创建一个banner文件夹,我们的手动CRUD实现的代码就放置在这里,查看项目目录结构,banner包的路径是/server/plugin/banner
。
model层
在model层中我们分为前端请求的参数request
,以及后端返回的只response
。在我们的model下会有一个创建表的结构体,也就是对应数据库字段的一个struct
banner的结构体
package model
import "github.com/flipped-aurora/gin-vue-admin/server/global"
type Banner struct {
global.GVA_MODEL
Url string `json:"url" gorm:"column:url;comment:图片地址"`
Path string `json:"path" gorm:"column:path;comment:轮播图跳转地址"`
Description string `json:"description" gorm:"column:description;comment:轮播介绍"`
}
model.request层
用于接收前端的参数
package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/common"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model/dao"
"gorm.io/gen"
)
type BannerList struct {
common.PageInfo
Url string `json:"url" gorm:"column:url;comment:图片地址"`
Path string `json:"path" gorm:"column:path;comment:轮播图跳转地址"`
Description string `json:"description" gorm:"column:description;comment:轮播介绍"`
}
func (r BannerList) BannerList() model.Banner {
return model.Banner{
Url: r.Url,
Path: r.Path,
Description: r.Description,
}
}
type BannerCreate struct {
Url string `json:"url" gorm:"column:url;comment:图片地址"`
Path string `json:"path" gorm:"column:path;comment:轮播图跳转地址"`
Description string `json:"description" gorm:"column:description;comment:轮播介绍"`
}
func (r BannerCreate) BannerCreate() model.Banner {
return model.Banner{
Url: r.Url,
Path: r.Path,
Description: r.Description,
}
}
type BannerUpdate struct {
common.GetById
Url string `json:"url" gorm:"column:url;comment:图片地址"`
Path string `json:"path" gorm:"column:path;comment:轮播图跳转地址"`
Description string `json:"description" gorm:"column:description;comment:轮播介绍"`
}
func (r BannerUpdate) BannerUpdate() model.Banner {
return model.Banner{
Url: r.Url,
Path: r.Path,
Description: r.Description,
}
}
func (r *BannerList) Search() common.GenScopes {
return func(tx gen.Dao) gen.Dao {
if r.Url != "" {
tx = tx.Where(dao.Banner.Url.Eq(r.Url))
}
if r.Path != "" {
tx = tx.Where(dao.Banner.Path.Eq(r.Path))
}
if r.Description != "" {
tx = tx.Where(dao.Banner.Description.Eq(r.Description))
}
return tx
}
}
model.response
返回给前端的一个模型
package response
type Banner struct {
Url string `json:"url" gorm:"column:url;comment:图片地址"`
Path string `json:"path" gorm:"column:path;comment:轮播图跳转地址"`
Description string `json:"description" gorm:"column:description;comment:轮播介绍"`
}
gen层
可以查看gorm的gen的使用,文档地址:https://gorm.io/gen/ 具体详细的看gorm的文档地址,关于gen的相关使用!!!(可以看目录结构我的文件存放地)
gen是基于gorm封装的,在我们最后的banner/main.go
中需要装配一下传入db,判断有没有db,没有db的调用需要传入db。
$ go get -u gorm.io/gen
package main
import (
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model"
"os"
"path/filepath"
"gorm.io/gen"
)
// generate code
func main() {
// 获取当前文件夹路径
pwd, _ := os.Getwd()
g := gen.NewGenerator(gen.Config{
// 文件输出的位置(根据自己的情况而定)
OutPath: filepath.Join(pwd, "plugin", "banner", "model", "dao"),
// 输出的文件名
OutFile: "query.go",
// 输出模式
Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
FieldCoverable: true,
WithUnitTest: false,
})
// 挂在自己的结构体在这里(根据自己的业务而定)
g.ApplyBasic(
new(model.Banner),
)
g.Execute()
}
common层
common层一般是放置一些公共资源需要使用的,比如一些返回信息,雪花算法生成id等。
common.common.go
package common
import (
"gorm.io/gen"
)
type GetById struct {
ID uint `json:"id" form:"id"`
}
type GetByIds struct {
IDs []uint `json:"ids" form:"ids"`
}
// PageInfo 分页字段
type PageInfo struct {
Page int `json:"page" form:"page"` // 页码
PageSize int `json:"pageSize" form:"pageSize"` // 每页大小
Keyword string `json:"keyword" form:"keyword"` //关键字
}
// 分页函数
func (c *PageInfo) Paginate() func(tx gen.Dao) gen.Dao {
return func(tx gen.Dao) gen.Dao {
switch {
case c.PageSize > 100:
c.PageSize = 100
case c.PageSize <= 0:
c.PageSize = 10
}
offset := (c.Page - 1) * c.PageSize
return tx.Offset(offset).Limit(c.PageSize)
}
}
type GenScopes func(tx gen.Dao) gen.Dao
// 前端传什么返回什么
type PageResult struct {
PageInfo
List interface{} `json:"list" swaggertype:"string" example:"interface 数据"`
Count int64 `json:"count" swaggertype:"string" example:"int64 总数"`
}
// list 返回函数
func NewPageResult(list interface{}, count int64, pageInfo PageInfo) PageResult {
if list == nil {
return PageResult{
PageInfo: PageInfo{
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
},
List: []struct{}{},
Count: count,
}
}
return PageResult{
PageInfo: PageInfo{
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
},
List: list,
Count: count,
}
}
common.message.go
存放返回信息还可以自定义一些返回状态码!
package common
const (
SuccessCreate = "创建成功"
ErrorCreate = "创建失败"
SuccessUpdate = "更新成功"
ErrorUpdate = "更新失败"
SuccessDelete = "删除成功"
ErrorDelete = "删除失败"
SuccessDeletes = "批量删除成功"
ErrorDeletes = "批量删除失败"
SuccessFirst = "查询一条成功"
ErrorFirst = "查询一条失败"
SuccessList = "查询成功"
ErrorList = "查询失败"
ErrorCount = "查询条数失败"
ErrorRequest = "参数有误"
SuccessServer = "服务端返回成功"
ErrorServer = "服务端返回失败"
)
service层
这里我一般是用于业务处理。
service.banner.go
package service
import (
"context"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/common"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model/dao"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model/request"
"github.com/pkg/errors"
)
var Banner = new(banner)
type banner struct{}
// List 查询多条
func (b *banner) List(ctx context.Context, info request.BannerList) (entity []*model.Banner, count int64, err error) {
query := dao.Q.WithContext(ctx).Banner
query = query.Scopes(info.Search())
count, err = query.Count()
if err != nil {
return nil, 0, errors.Wrap(err, common.ErrorList)
}
entity, err = query.Scopes(info.Paginate()).Find()
if err != nil {
return nil, 0, errors.Wrap(err, common.ErrorCount)
}
return entity, count, nil
}
// First 查询单条
func (b *banner) First(ctx context.Context, info common.GetById) (entity *model.Banner, err error) {
query := dao.Q.WithContext(ctx).Banner
entity, err = query.Where(dao.Banner.ID.Eq(info.ID)).First()
if err != nil {
return nil, errors.Wrap(err, common.ErrorFirst)
}
return entity, nil
}
// Create 新增
func (b *banner) Create(ctx context.Context, info request.BannerCreate) error {
create := info.BannerCreate()
query := dao.Q.WithContext(ctx).Banner
err := query.Create(&create)
if err != nil {
return errors.Wrap(err, common.ErrorCreate)
}
return nil
}
// Update 修改
func (b *banner) Update(ctx context.Context, info request.BannerUpdate) error {
update := info.BannerUpdate()
query := dao.Q.WithContext(ctx).Banner
_, err := query.Where(dao.Banner.ID.Eq(info.ID)).Updates(update)
if err != nil {
return errors.Wrap(err, common.ErrorUpdate)
}
return nil
}
// Delete 单个删除
func (b banner) Delete(ctx context.Context, info common.GetById) error {
query := dao.Q.WithContext(ctx).Banner
_, err := query.Where(dao.Banner.ID.Eq(info.ID)).Delete()
if err != nil {
return errors.Wrap(err, common.ErrorDelete)
}
return nil
}
// Deletes 批量删除
func (b *banner) Deletes(ctx context.Context, info common.GetByIds) error {
query := dao.Q.WithContext(ctx).Banner
_, err := query.Where(dao.Banner.ID.In(info.IDs...)).Delete()
if err != nil {
return errors.Wrap(err, common.ErrorDeletes)
}
return nil
}
api层
这里我一般是处理接受前端的请求
api.banner.go
这里与service.banner.go的接口一一对应
package api
import (
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/common"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model/request"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/response"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/service"
"github.com/gin-gonic/gin"
"net/http"
)
var Banner = new(banner)
type banner struct{}
func (b banner) List(c *gin.Context) response.Response {
var list request.BannerList
err := c.ShouldBindJSON(&list)
if err != nil {
return response.Response{Code: http.StatusPaymentRequired, Message: common.ErrorRequest}
}
lists, count, err := service.Banner.List(c.Request.Context(), list)
if err != nil {
return response.Response{Code: http.StatusInternalServerError, Message: common.ErrorServer}
}
return response.Response{
Code: http.StatusOK,
Data: common.NewPageResult(lists, count, list.PageInfo),
Message: common.SuccessList,
}
}
func (b *banner) First(c *gin.Context) response.Response {
var info common.GetById
err := c.ShouldBindJSON(info)
if err != nil {
return response.Response{Code: http.StatusPaymentRequired, Message: common.ErrorRequest}
}
data, err := service.Banner.First(c.Request.Context(), info)
if err != nil {
return response.Response{Code: http.StatusInternalServerError, Message: common.ErrorServer}
}
return response.Response{
Code: http.StatusOK,
Data: data,
Message: common.SuccessFirst,
}
}
func (b *banner) Create(c *gin.Context) response.Response {
var create request.BannerCreate
err := c.ShouldBindJSON(&create)
if err != nil {
return response.Response{Code: http.StatusPaymentRequired, Message: common.ErrorRequest}
}
err = service.Banner.Create(c.Request.Context(), create)
if err != nil {
return response.Response{Code: http.StatusInternalServerError, Message: common.ErrorCreate}
}
return response.Response{Code: http.StatusOK, Message: common.SuccessCreate}
}
func (b *banner) Update(c *gin.Context) response.Response {
var update request.BannerUpdate
err := c.ShouldBindJSON(&update)
if err != nil {
return response.Response{Code: http.StatusPaymentRequired, Message: common.ErrorRequest}
}
err = service.Banner.Update(c.Request.Context(), update)
if err != nil {
return response.Response{Code: http.StatusInternalServerError, Message: common.ErrorUpdate}
}
return response.Response{Code: http.StatusOK, Message: common.SuccessUpdate}
}
func (b *banner) Delete(c *gin.Context) response.Response {
var info common.GetById
err := c.ShouldBindJSON(&info)
if err != nil {
return response.Response{Code: http.StatusPaymentRequired, Message: common.ErrorRequest}
}
err = service.Banner.Delete(c.Request.Context(), info)
if err != nil {
return response.Response{Code: http.StatusInternalServerError, Message: common.ErrorDelete}
}
return response.Response{Code: http.StatusOK, Message: common.SuccessDelete}
}
func (b *banner) Deletes(c *gin.Context) response.Response {
var info common.GetByIds
err := c.ShouldBindJSON(&info)
if err != nil {
return response.Response{Code: http.StatusPaymentRequired, Message: common.ErrorRequest}
}
err = service.Banner.Deletes(c.Request.Context(), info)
if err != nil {
return response.Response{Code: http.StatusInternalServerError, Message: common.ErrorDeletes}
}
return response.Response{Code: http.StatusOK, Message: common.SuccessDeletes}
}
router层
这里是路由注册的地方
router.router.go
package router
import (
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/api"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/response"
"github.com/gin-gonic/gin"
)
type Banner struct {
router *gin.RouterGroup
}
func NewBanner(router *gin.RouterGroup) *Banner {
return &Banner{router}
}
func (r Banner) Init() {
group := r.router.Group("banner")
{ // 不带日志中间件
group.POST("list", response.Handler()(api.Banner.List))
group.POST("first", response.Handler()(api.Banner.First))
}
{ // 带日志中间件
group.Use(middleware.OperationRecord())
group.POST("create", response.Handler()(api.Banner.Create))
group.PUT("update", response.Handler()(api.Banner.Update))
group.DELETE("delete", response.Handler()(api.Banner.Delete))
group.DELETE("deletes", response.Handler()(api.Banner.Deletes))
}
}
banner/main.go
package banner
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/model/dao"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/banner/router"
"github.com/gin-gonic/gin"
)
var Plugin = new(plugin)
type plugin struct{}
func (p plugin) Register(group *gin.RouterGroup) {
router.NewBanner(group).Init()
// 这里需要自动装配一下调用db
if global.GVA_DB != nil {
dao.SetDefault(global.GVA_DB)
}
}
func (p plugin) RouterPath() string {
return ""
}