Skip to content

应用市场 原创

1、功能

d100039edbdaa7c26e339caed457f8d4 MD5

2、数据定义

2.1、定义数据类型

internal/app/models/app_market.go 文件中增加AppMarketModels,并实现对数据库的操作,如下:

go
package models

import "gorm.io/gorm"

// 应用市场

type AppMarket struct {
	*Base
	AppName            string `json:"app_name"`             // 应用名
	AppType            string `json:"app_type"`             // 应用类型
	AppAvatar          string `json:"app_avatar"`           // 应用图标
	AppVersion         string `json:"app_version"`          // 应用版本
	ChartURL           string `json:"chart_url"`            // Chart地址
	RepoName           string `json:"repo_name"`            // ChartRepo 名
	RepoURL            string `json:"repo_url"`             // Chart Repo URL
	ChartDeployCommand string `json:"chart_deploy_command"` // Chart部署命令
	Architecture       string `json:"architecture"`         // 支持的系统架构
	Desc               string `json:"desc"`                 // 描述
}

func (k *AppMarket) TableName() string {
	return "app_market"
}

// Create 插入数据
func (k *AppMarket) Create(db *gorm.DB) error {
	return db.Create(&k).Error
}

// GetByName 根据集群名获取集群信息
func (k *AppMarket) GetByName(db *gorm.DB) (*AppMarket, error) {
	var kc *AppMarket
	if k.AppName != "" {
		db.Where("app_name =? and is_del=?", k.AppName, 0).First(&kc)
	}
	return kc, nil
}

// GetByID 通过ID获取集群信息
func (k *AppMarket) GetByID(db *gorm.DB) (*AppMarket, error) {
	var kc *AppMarket
	db.Where("id =? and is_del=?", k.ID, 0).First(&kc)
	return kc, nil
}

// List 列出集群信息
func (k *AppMarket) List(db *gorm.DB, page, limit int) ([]*AppMarket, error) {
	var (
		kc  []*AppMarket
		err error
	)

	startIndex := (page - 1) * limit
	db = db.Offset(startIndex).Limit(limit)

	if k.AppName != "" {
		db = db.Where("app_name=?", k.AppName)
	}

	if err = db.Where("is_del = ?", 0).Find(&kc).Error; err != nil {
		return nil, err
	}
	return kc, nil
}

// Update 更新数据
func (k *AppMarket) Update(db *gorm.DB, values interface{}) error {
	if err := db.Model(k).Where("id = ? AND is_del = ?", k.ID, 0).First(&AppMarket{}).Updates(values).Error; err != nil {
		return err
	}
	return nil
}

// Delete 删除数据
func (k *AppMarket) Delete(db *gorm.DB) error {
	var kc AppMarket
	if err := db.Where("id=? AND is_del=?", k.ID, 0).First(&kc).Error; err != nil {
		return err
	}
	kc.IsDel = 1
	if err := db.Updates(&kc).Error; err != nil {
		return err
	}
	return nil
}

// Save 保存数据
func (k *AppMarket) Save(db *gorm.DB) error {
	return db.Save(k).Error
}

2.2、实现增删改查

internal/app/dao/app_market.go文件,输入以下内容,实现对AppMarket的数据操作。

go
package dao

import (
	"github.com/joker-bai/hawkeye/internal/app/requests"
	"time"

	"github.com/joker-bai/hawkeye/internal/app/models"
)

// AppMarketCreate 创建集群
func (d *Dao) AppMarketCreate(param *requests.AppMarketCreateRequest) error {
	nowTime := uint32(time.Now().Unix())
	kc := models.AppMarket{
		Base: &models.Base{
			CreatedAt:  nowTime,
			ModifiedAt: nowTime,
			IsDel:      0,
		},
		AppName:            param.AppName,
		AppType:            param.AppType,
		AppVersion:         param.AppVersion,
		AppAvatar:          param.AppAvatar,
		ChartURL:           param.ChartURL,
		ChartDeployCommand: param.ChartDeployCommand,
		RepoName:           param.RepoName,
		RepoURL:            param.RepoURL,
		Architecture:       param.Architecture,
		Desc:               param.Desc,
	}
	return kc.Create(d.engine)
}

// AppMarketGetByName 通过集群名称获取集群
func (d *Dao) AppMarketGetByName(name string) (*models.AppMarket, error) {
	kc := models.AppMarket{
		AppName: name,
	}
	return kc.GetByName(d.engine)
}

func (d *Dao) AppMarketGetById(id uint32) (*models.AppMarket, error) {
	kc := models.AppMarket{
		Base: &models.Base{
			ID: id,
		},
	}
	return kc.GetByID(d.engine)
}

// AppMarketUpdate 更新集群
func (d *Dao) AppMarketUpdate(param *requests.AppMarketUpdateRequest) error {
	nowTime := uint32(time.Now().Unix())
	kc := models.AppMarket{
		Base: &models.Base{
			ID: param.ID,
		},
	}
	values := map[string]interface{}{
		"app_name":             param.AppName,
		"app_version":          param.AppVersion,
		"app_avatar":           param.AppAvatar,
		"chart_url":            param.ChartURL,
		"repo_name":            param.RepoName,
		"repo_url":             param.RepoURL,
		"chart_deploy_command": param.ChartDeployCommand,
		"architecture":         param.Architecture,
		"desc":                 param.Desc,
		"modified_at":          nowTime,
	}
	return kc.Update(d.engine, values)
}

// AppMarketList 列出集群信息
func (d *Dao) AppMarketList(appName string, page, limit int) ([]*models.AppMarket, error) {
	kc := models.AppMarket{
		AppName: appName,
	}
	return kc.List(d.engine, page, limit)
}

// AppMarketDelete 删除集群信息
func (d *Dao) AppMarketDelete(id uint32) error {
	kc := models.AppMarket{
		Base: &models.Base{
			ID: id,
		},
	}
	return kc.Delete(d.engine)
}

3、实现 services 方法

3.1、请求参数校验

internal/app/requests 目录中新建 app_market.go 文件,写入以下内容以完成请求参数校验:

go
package requests

import (
	"github.com/gin-gonic/gin"
	"github.com/joker-bai/hawkeye/pkg/app"
	"github.com/thedevsaddam/govalidator"
)

type AppMarketCreateRequest struct {
	AppName            string `json:"app_name" form:"app_name" valid:"app_name"`
	AppType            string `json:"app_type" form:"app_type" valid:"app_type"`
	AppVersion         string `json:"app_version" form:"app_version" valid:"app_version"`
	AppAvatar          string `json:"app_avatar" form:"app_avatar" valid:"app_avatar"`
	ChartURL           string `json:"chart_url" form:"chart_url" valid:"chart_url"`
	RepoName           string `json:"repo_name" form:"repo_name" valid:"repo_name"`
	RepoURL            string `json:"repo_url" form:"repo_url" valid:"repo_url"`
	ChartDeployCommand string `json:"chart_deploy_command" form:"chart_deploy_command" valid:"chart_deploy_command"`
	Architecture       string `json:"architecture" form:"architecture" valid:"architecture"`
	Desc               string `json:"desc" form:"desc" valid:"desc"`
}

func ValidAppMarketCreateRequest(data interface{}, ctx *gin.Context) map[string][]string {
	rules := govalidator.MapData{
		"app_name":             []string{"required"},
		"app_type":             []string{"required"},
		"app_version":          []string{"required"},
		"chart_url":            []string{"required"},
		"repo_url":             []string{"required"},
		"repo_name":            []string{"required"},
		"chart_deploy_command": []string{"required"},
	}
	messages := govalidator.MapData{
		"app_name": []string{
			"required: 应用名为必填字段,字段为 app_name",
		},
		"app_type": []string{
			"required: 应用类型为必填字段,字段为 app_type",
		},
		"app_version": []string{
			"required: 集群版本为必填项,字段为 app_version",
		},
		"chart_url": []string{
			"required: Chart应用URL为必填字段,字段为 chart_url",
		},
		"repo_url": []string{
			"required: Helm Repo地址为必填字段,字段为 repo_url",
		},
		"repo_name": []string{
			"required: Helm Repo名为必填字段,字段为 repo_name",
		},
		"chart_deploy_command": []string{
			"required: Chart部署命令为必填字段,字段为 chart_deploy_command",
		},
	}

	// 校验入参

	return app.ValidateOptions(data, rules, messages)
}

type AppMarketUpdateRequest struct {
	ID                 uint32 `json:"id,omitempty" form:"id" valid:"id"`
	AppName            string `json:"app_name" form:"app_name" valid:"app_name"`
	AppType            string `json:"app_type" form:"app_type" valid:"app_type"`
	AppVersion         string `json:"app_version" form:"app_version" valid:"app_version"`
	AppAvatar          string `json:"app_avatar" form:"app_avatar" valid:"app_avatar"`
	ChartURL           string `json:"chart_url" form:"chart_url" valid:"chart_url"`
	RepoName           string `json:"repo_name" form:"repo_name" valid:"repo_name"`
	RepoURL            string `json:"repo_url" form:"repo_url" valid:"repo_url"`
	ChartDeployCommand string `json:"chart_deploy_command" form:"chart_deploy_command" valid:"chart_deploy_command"`
	Architecture       string `json:"architecture" form:"architecture" valid:"architecture"`
	Desc               string `json:"desc" form:"desc" valid:"desc"`
}

func ValidAppMarketUpdateRequest(data interface{}, ctx *gin.Context) map[string][]string {
	rules := govalidator.MapData{
		"id":                   []string{"required"},
		"app_name":             []string{"required"},
		"app_type":             []string{"required"},
		"app_version":          []string{"required"},
		"chart_url":            []string{"required"},
		"repo_url":             []string{"required"},
		"repo_name":            []string{"required"},
		"chart_deploy_command": []string{"required"},
	}
	messages := govalidator.MapData{
		"id": []string{
			"required: 用户名ID不能为空",
		},
		"app_name": []string{
			"required: 用户名为必填字段,字段为 app_name",
		},
		"app_type": []string{
			"required: 应用类型为必填字段,字段为 app_type",
		},
		"app_version": []string{
			"required: 集群版本为必填项,字段为 app_version",
		},
		"chart_url": []string{
			"required: Chart应用URL为必填字段,字段为 chart_url",
		},
		"repo_url": []string{
			"required: Helm Repo地址为必填字段,字段为 repo_url",
		},
		"repo_name": []string{
			"required: Helm Repo名为必填字段,字段为 repo_name",
		},
		"chart_deploy_command": []string{
			"required: Chart部署命令为必填字段,字段为 chart_deploy_command",
		},
	}

	// 校验入参

	return app.ValidateOptions(data, rules, messages)
}

type AppMarketListRequest struct {
	AppName string `json:"app_name" form:"app_name"`
	Page    int    `json:"page" form:"page" valid:"page"`
	Limit   int    `json:"limit" form:"limit" valid:"limit"`
}

func ValidAppMarketListRequest(data interface{}, ctx *gin.Context) map[string][]string {
	rules := govalidator.MapData{
		"page":  []string{"required"},
		"limit": []string{"required"},
	}
	messages := govalidator.MapData{
		"page": []string{
			"required: 页数不能为空",
		},
		"limit": []string{
			"required: 每页条数不能为空",
		},
	}

	// 校验入参

	return app.ValidateOptions(data, rules, messages)
}

3.2、实现 services 方法

internal/app/services/app_market.go 文件中新增 AppMarket 操作的 services 方法,如下:

go
package services

import (
	"github.com/joker-bai/hawkeye/internal/app/models"
	"github.com/joker-bai/hawkeye/internal/app/requests"
)

func (s *Services) AppMarketCreate(param *requests.AppMarketCreateRequest) error {
	return s.dao.AppMarketCreate(param)
}

func (s *Services) AppMarketUpdate(param *requests.AppMarketUpdateRequest) error {
	return s.dao.AppMarketUpdate(param)
}

func (s *Services) AppMarketDelete(param *requests.CommonIdRequest) error {
	return s.dao.AppMarketDelete(param.ID)
}

func (s *Services) AppMarketList(param *requests.AppMarketListRequest) ([]*models.AppMarket, error) {
	return s.dao.AppMarketList(param.AppName, param.Page, param.Limit)
}

4、新增 controllers 方法

在 internal/app/controllers/api/v1 目录中新增 app_market.go 文件,实现如下方法:

go
package v1

import (
	"github.com/gin-gonic/gin"
	"github.com/joker-bai/hawkeye/global"
	"github.com/joker-bai/hawkeye/internal/app/requests"
	"github.com/joker-bai/hawkeye/internal/app/services"
	"github.com/joker-bai/hawkeye/pkg/app"
	"github.com/joker-bai/hawkeye/pkg/errorcode"
	"go.uber.org/zap"
)

type AppMarketController struct{}

// List godoc
// @Summary 列出应用
// @Description 列出应用
// @Tags 应用市场管理
// @Produce json
// @Param app_name query string false "应用名" maxlength(100)
// @Param page query int true "页码"
// @Param limit query int true "每页数量"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/app-market/list [get]
func (u *AppMarketController) List(ctx *gin.Context) {
	param := requests.AppMarketListRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidAppMarketListRequest); !ok {
		return
	}

	svc := services.New(ctx)
	res, err := svc.AppMarketList(&param)
	if err != nil {
		global.Log.Error("获取应用列表失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorAppMarketListFail)
		return
	}

	response.ToResponseList(res, len(res))
}

// Create godoc
// @Summary 创建应用
// @Description 创建应用
// @Tags 应用市场管理
// @Produce json
// @Param body body requests.AppMarketCreateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/app-market/create [post]
func (u *AppMarketController) Create(ctx *gin.Context) {
	param := requests.AppMarketCreateRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidAppMarketCreateRequest); !ok {
		return
	}

	svc := services.New(ctx)
	err := svc.AppMarketCreate(&param)
	if err != nil {
		global.Log.Error("创建应用失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorAppMarketCreateFail)
		return
	}

	response.ToResponse(gin.H{
		"data": "创建应用成功",
	})
}

// Update
// @Summary 修改应用
// @Description 修改应用
// @Tags 应用市场管理
// @Produce json
// @Param body body requests.AppMarketUpdateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1/app-market/update [post]
func (u *AppMarketController) Update(ctx *gin.Context) {
	param := requests.AppMarketUpdateRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidAppMarketUpdateRequest); !ok {
		return
	}

	svc := services.New(ctx)
	if err := svc.AppMarketUpdate(&param); err != nil {
		global.Log.Error("应用修改失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorAppMarketUpdateFail)
		return
	}

	response.ToResponse(gin.H{
		"data": "应用修改成功",
	})
}

// Delete
// @Summary 删除应用
// @Description 删除应用
// @Tags 应用市场管理
// @Produce json
// @Param body body requests.CommonIdRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errorcode.Error "请求错误"
// @Failure 500 {object} errorcode.Error "内部错误"
// @Router /api/v1//app-market/delete [post]
func (u *AppMarketController) Delete(ctx *gin.Context) {
	param := requests.CommonIdRequest{}
	response := app.NewResponse(ctx)

	if ok := app.Validate(ctx, &param, requests.ValidCommonIdRequest); !ok {
		return
	}

	svc := services.New(ctx)
	if err := svc.AppMarketDelete(&param); err != nil {
		global.Log.Error("应用删除失败", zap.String("error", err.Error()))
		response.ToErrorResponse(errorcode.ErrorAppMarketDeleteFail)
		return
	}

	response.ToResponse(gin.H{
		"data": "应用删除成功",
	})
}

再到 pkg/errorcode/app_market.go 文件中新增如下错误代码:

go
package errorcode

var (
	ErrorAppMarketListFail   = NewError(600001, "获取应用列表失败")
	ErrorAppMarketCreateFail = NewError(600002, "创建应用失败")
	ErrorAppMarketUpdateFail = NewError(600003, "更新应用失败")
	ErrorAppMarketDeleteFail = NewError(600004, "删除应用失败")
)

5、新增路由

internal/app/routers/app-market.go 文件中新增 AppMarket操作的路由,如下:

go
package routers

import (
	"github.com/gin-gonic/gin"
	v1 "github.com/joker-bai/hawkeye/internal/app/controllers/api/v1"
)

type AppMarketRouter struct{}

func (a *AppMarketRouter) Inject(r *gin.RouterGroup) {
	ac := new(v1.AppMarketController)
	r.GET("/app-market/list", ac.List)
	r.POST("/app-market/create", ac.Create)
	r.POST("/app-market/update", ac.Update)
	r.POST("/app-market/delete", ac.Delete)
}

再在initialize/router.go中增加AppMarket的路由。

go
package initialize

import (
	"github.com/gin-gonic/gin"
	_ "github.com/joker-bai/hawkeye/docs"
	"github.com/joker-bai/hawkeye/internal/app/routers"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
)

type injector interface {
	Inject(router *gin.RouterGroup)
}

func (s *Engine) injectRouterGroup(router *gin.RouterGroup) {
	{
		for _, r := range []injector{
			new(routers.HelloWorldRouter),
		} {
			r.Inject(router.Group("/api"))
		}
	}

	// 需要鉴权
	{
		for _, r := range []injector{
			new(routers.UserRouter),
			new(routers.K8sRouter),
			new(routers.AppMarketRouter),
		} {
			//r.Inject(router.Group("/api/v1", middleware.AuthJWT()))
			r.Inject(router.Group("/api/v1"))
		}
	}

	// 不需要鉴权
	ar := new(routers.AuthRouter)
	ar.Inject(router.Group("/api/v1"))

	// swagger
	router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}

6、测试一下

PS:测试之前都需要先初始化集群,在 4.3.1 Pod 章节有介绍。

这里简单测试创建应用市场接口,如下:

2b432455bb7d1fd8804fb238a9ab4bcf MD5

其他接口自行下去测试。

7、代码版本

本节开发完成后,记得生成 swag 和标记代码版本,如下:

go
$ swag init
$ git add .
$ git commit -m "新增k8s集群namespace操作"
最近更新