应用市场 原创
1、功能
2、数据定义
2.1、定义数据类型
在 internal/app/models/app_market.go
文件中增加AppMarket
的Models
,并实现对数据库的操作,如下:
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, ¶m, requests.ValidAppMarketListRequest); !ok {
return
}
svc := services.New(ctx)
res, err := svc.AppMarketList(¶m)
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, ¶m, requests.ValidAppMarketCreateRequest); !ok {
return
}
svc := services.New(ctx)
err := svc.AppMarketCreate(¶m)
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, ¶m, requests.ValidAppMarketUpdateRequest); !ok {
return
}
svc := services.New(ctx)
if err := svc.AppMarketUpdate(¶m); 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, ¶m, requests.ValidCommonIdRequest); !ok {
return
}
svc := services.New(ctx)
if err := svc.AppMarketDelete(¶m); 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 章节有介绍。
这里简单测试创建应用市场接口,如下:
其他接口自行下去测试。
7、代码版本
本节开发完成后,记得生成 swag 和标记代码版本,如下:
go
$ swag init
$ git add .
$ git commit -m "新增k8s集群namespace操作"