From 280a02eac7b43adaa61c98ca0532cd4cd25655f9 Mon Sep 17 00:00:00 2001 From: hequan2017 Date: Tue, 10 Feb 2026 18:06:31 +0800 Subject: [PATCH] feat(server): add PCDN APIs, router, RBAC and menu permissions --- server/api/v1/enter.go | 2 + server/api/v1/pcdn/enter.go | 15 +++ server/api/v1/pcdn/pcdn_dispatch_task.go | 26 +++++ server/api/v1/pcdn/pcdn_node.go | 114 ++++++++++++++++++++++ server/api/v1/pcdn/pcdn_policy.go | 114 ++++++++++++++++++++++ server/initialize/gorm_biz.go | 3 +- server/initialize/router_biz.go | 4 + server/model/pcdn/dispatch_task.go | 18 ++++ server/model/pcdn/node.go | 19 ++++ server/model/pcdn/policy.go | 19 ++++ server/model/pcdn/request/pcdn.go | 51 ++++++++++ server/router/enter.go | 2 + server/router/pcdn/enter.go | 9 ++ server/router/pcdn/pcdn.go | 35 +++++++ server/service/enter.go | 2 + server/service/pcdn/enter.go | 7 ++ server/service/pcdn/pcdn_dispatch_task.go | 33 +++++++ server/service/pcdn/pcdn_node.go | 61 ++++++++++++ server/service/pcdn/pcdn_policy.go | 65 ++++++++++++ server/source/system/api.go | 18 +++- server/source/system/casbin.go | 17 ++++ server/source/system/menu.go | 6 ++ 22 files changed, 638 insertions(+), 2 deletions(-) create mode 100644 server/api/v1/pcdn/enter.go create mode 100644 server/api/v1/pcdn/pcdn_dispatch_task.go create mode 100644 server/api/v1/pcdn/pcdn_node.go create mode 100644 server/api/v1/pcdn/pcdn_policy.go create mode 100644 server/model/pcdn/dispatch_task.go create mode 100644 server/model/pcdn/node.go create mode 100644 server/model/pcdn/policy.go create mode 100644 server/model/pcdn/request/pcdn.go create mode 100644 server/router/pcdn/enter.go create mode 100644 server/router/pcdn/pcdn.go create mode 100644 server/service/pcdn/enter.go create mode 100644 server/service/pcdn/pcdn_dispatch_task.go create mode 100644 server/service/pcdn/pcdn_node.go create mode 100644 server/service/pcdn/pcdn_policy.go diff --git a/server/api/v1/enter.go b/server/api/v1/enter.go index 2cebe15..b4656df 100644 --- a/server/api/v1/enter.go +++ b/server/api/v1/enter.go @@ -5,6 +5,7 @@ import ( "github.com/flipped-aurora/gin-vue-admin/server/api/v1/example" "github.com/flipped-aurora/gin-vue-admin/server/api/v1/imageregistry" "github.com/flipped-aurora/gin-vue-admin/server/api/v1/instance" + "github.com/flipped-aurora/gin-vue-admin/server/api/v1/pcdn" "github.com/flipped-aurora/gin-vue-admin/server/api/v1/product" "github.com/flipped-aurora/gin-vue-admin/server/api/v1/system" ) @@ -18,4 +19,5 @@ type ApiGroup struct { ComputenodeApiGroup computenode.ApiGroup ProductApiGroup product.ApiGroup InstanceApiGroup instance.ApiGroup + PcdnApiGroup pcdn.ApiGroup } diff --git a/server/api/v1/pcdn/enter.go b/server/api/v1/pcdn/enter.go new file mode 100644 index 0000000..665f597 --- /dev/null +++ b/server/api/v1/pcdn/enter.go @@ -0,0 +1,15 @@ +package pcdn + +import "github.com/flipped-aurora/gin-vue-admin/server/service" + +type ApiGroup struct { + PcdnNodeApi + PcdnPolicyApi + PcdnDispatchTaskApi +} + +var ( + pcdnNodeService = service.ServiceGroupApp.PcdnServiceGroup.PcdnNodeService + pcdnPolicyService = service.ServiceGroupApp.PcdnServiceGroup.PcdnPolicyService + pcdnDispatchTaskService = service.ServiceGroupApp.PcdnServiceGroup.PcdnDispatchTaskService +) diff --git a/server/api/v1/pcdn/pcdn_dispatch_task.go b/server/api/v1/pcdn/pcdn_dispatch_task.go new file mode 100644 index 0000000..1b288a4 --- /dev/null +++ b/server/api/v1/pcdn/pcdn_dispatch_task.go @@ -0,0 +1,26 @@ +package pcdn + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + pcdnReq "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type PcdnDispatchTaskApi struct{} + +func (a *PcdnDispatchTaskApi) GetPcdnDispatchTaskList(c *gin.Context) { + var req pcdnReq.PcdnDispatchTaskSearch + if err := c.ShouldBindQuery(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := pcdnDispatchTaskService.GetPcdnDispatchTaskList(c.Request.Context(), req) + if err != nil { + global.GVA_LOG.Error("获取PCDN调度任务列表失败", zap.Error(err)) + response.FailWithMessage("获取PCDN调度任务列表失败:"+err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{List: list, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取PCDN调度任务列表成功", c) +} diff --git a/server/api/v1/pcdn/pcdn_node.go b/server/api/v1/pcdn/pcdn_node.go new file mode 100644 index 0000000..fcb30c0 --- /dev/null +++ b/server/api/v1/pcdn/pcdn_node.go @@ -0,0 +1,114 @@ +package pcdn + +import ( + "strconv" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn" + pcdnReq "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type PcdnNodeApi struct{} + +func (a *PcdnNodeApi) CreatePcdnNode(c *gin.Context) { + var req pcdn.PcdnNode + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnNodeService.CreatePcdnNode(c.Request.Context(), &req); err != nil { + global.GVA_LOG.Error("创建PCDN节点失败", zap.Error(err)) + response.FailWithMessage("创建PCDN节点失败:"+err.Error(), c) + return + } + response.OkWithMessage("创建PCDN节点成功", c) +} + +func (a *PcdnNodeApi) DeletePcdnNode(c *gin.Context) { + id, err := strconv.ParseUint(c.Query("id"), 10, 64) + if err != nil { + response.FailWithMessage("id参数错误", c) + return + } + if err = pcdnNodeService.DeletePcdnNode(c.Request.Context(), uint(id)); err != nil { + global.GVA_LOG.Error("删除PCDN节点失败", zap.Error(err)) + response.FailWithMessage("删除PCDN节点失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除PCDN节点成功", c) +} + +func (a *PcdnNodeApi) UpdatePcdnNode(c *gin.Context) { + var req pcdn.PcdnNode + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnNodeService.UpdatePcdnNode(c.Request.Context(), req); err != nil { + global.GVA_LOG.Error("更新PCDN节点失败", zap.Error(err)) + response.FailWithMessage("更新PCDN节点失败:"+err.Error(), c) + return + } + response.OkWithMessage("更新PCDN节点成功", c) +} + +func (a *PcdnNodeApi) FindPcdnNode(c *gin.Context) { + id, err := strconv.ParseUint(c.Query("id"), 10, 64) + if err != nil { + response.FailWithMessage("id参数错误", c) + return + } + ret, err := pcdnNodeService.GetPcdnNode(c.Request.Context(), uint(id)) + if err != nil { + global.GVA_LOG.Error("查询PCDN节点失败", zap.Error(err)) + response.FailWithMessage("查询PCDN节点失败:"+err.Error(), c) + return + } + response.OkWithData(ret, c) +} + +func (a *PcdnNodeApi) GetPcdnNodeList(c *gin.Context) { + var req pcdnReq.PcdnNodeSearch + if err := c.ShouldBindQuery(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := pcdnNodeService.GetPcdnNodeList(c.Request.Context(), req) + if err != nil { + global.GVA_LOG.Error("获取PCDN节点列表失败", zap.Error(err)) + response.FailWithMessage("获取PCDN节点列表失败:"+err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{List: list, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取PCDN节点列表成功", c) +} + +func (a *PcdnNodeApi) SwitchPcdnNodeOnline(c *gin.Context) { + var req pcdnReq.PcdnNodeOnlineRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnNodeService.SwitchOnline(c.Request.Context(), req); err != nil { + global.GVA_LOG.Error("切换PCDN节点上下线失败", zap.Error(err)) + response.FailWithMessage("切换PCDN节点上下线失败:"+err.Error(), c) + return + } + response.OkWithMessage("切换PCDN节点上下线成功", c) +} + +func (a *PcdnNodeApi) UpdatePcdnNodeWeight(c *gin.Context) { + var req pcdnReq.PcdnNodeWeightRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnNodeService.UpdateWeight(c.Request.Context(), req); err != nil { + global.GVA_LOG.Error("调整PCDN节点权重失败", zap.Error(err)) + response.FailWithMessage("调整PCDN节点权重失败:"+err.Error(), c) + return + } + response.OkWithMessage("调整PCDN节点权重成功", c) +} diff --git a/server/api/v1/pcdn/pcdn_policy.go b/server/api/v1/pcdn/pcdn_policy.go new file mode 100644 index 0000000..c996cf7 --- /dev/null +++ b/server/api/v1/pcdn/pcdn_policy.go @@ -0,0 +1,114 @@ +package pcdn + +import ( + "strconv" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn" + pcdnReq "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn/request" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type PcdnPolicyApi struct{} + +func (a *PcdnPolicyApi) CreatePcdnPolicy(c *gin.Context) { + var req pcdn.PcdnPolicy + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnPolicyService.CreatePcdnPolicy(c.Request.Context(), &req); err != nil { + global.GVA_LOG.Error("创建PCDN策略失败", zap.Error(err)) + response.FailWithMessage("创建PCDN策略失败:"+err.Error(), c) + return + } + response.OkWithMessage("创建PCDN策略成功", c) +} + +func (a *PcdnPolicyApi) DeletePcdnPolicy(c *gin.Context) { + id, err := strconv.ParseUint(c.Query("id"), 10, 64) + if err != nil { + response.FailWithMessage("id参数错误", c) + return + } + if err = pcdnPolicyService.DeletePcdnPolicy(c.Request.Context(), uint(id)); err != nil { + global.GVA_LOG.Error("删除PCDN策略失败", zap.Error(err)) + response.FailWithMessage("删除PCDN策略失败:"+err.Error(), c) + return + } + response.OkWithMessage("删除PCDN策略成功", c) +} + +func (a *PcdnPolicyApi) UpdatePcdnPolicy(c *gin.Context) { + var req pcdn.PcdnPolicy + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnPolicyService.UpdatePcdnPolicy(c.Request.Context(), req); err != nil { + global.GVA_LOG.Error("更新PCDN策略失败", zap.Error(err)) + response.FailWithMessage("更新PCDN策略失败:"+err.Error(), c) + return + } + response.OkWithMessage("更新PCDN策略成功", c) +} + +func (a *PcdnPolicyApi) FindPcdnPolicy(c *gin.Context) { + id, err := strconv.ParseUint(c.Query("id"), 10, 64) + if err != nil { + response.FailWithMessage("id参数错误", c) + return + } + ret, err := pcdnPolicyService.GetPcdnPolicy(c.Request.Context(), uint(id)) + if err != nil { + global.GVA_LOG.Error("查询PCDN策略失败", zap.Error(err)) + response.FailWithMessage("查询PCDN策略失败:"+err.Error(), c) + return + } + response.OkWithData(ret, c) +} + +func (a *PcdnPolicyApi) GetPcdnPolicyList(c *gin.Context) { + var req pcdnReq.PcdnPolicySearch + if err := c.ShouldBindQuery(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + list, total, err := pcdnPolicyService.GetPcdnPolicyList(c.Request.Context(), req) + if err != nil { + global.GVA_LOG.Error("获取PCDN策略列表失败", zap.Error(err)) + response.FailWithMessage("获取PCDN策略列表失败:"+err.Error(), c) + return + } + response.OkWithDetailed(response.PageResult{List: list, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取PCDN策略列表成功", c) +} + +func (a *PcdnPolicyApi) GrayReleasePcdnPolicy(c *gin.Context) { + var req pcdnReq.PcdnPolicyGrayReleaseRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnPolicyService.GrayRelease(c.Request.Context(), req); err != nil { + global.GVA_LOG.Error("PCDN策略灰度发布失败", zap.Error(err)) + response.FailWithMessage("PCDN策略灰度发布失败:"+err.Error(), c) + return + } + response.OkWithMessage("PCDN策略灰度发布成功", c) +} + +func (a *PcdnPolicyApi) SwitchPcdnPolicy(c *gin.Context) { + var req pcdnReq.PcdnPolicySwitchRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.FailWithMessage(err.Error(), c) + return + } + if err := pcdnPolicyService.SwitchPolicy(c.Request.Context(), req); err != nil { + global.GVA_LOG.Error("PCDN策略启停失败", zap.Error(err)) + response.FailWithMessage("PCDN策略启停失败:"+err.Error(), c) + return + } + response.OkWithMessage("PCDN策略启停成功", c) +} diff --git a/server/initialize/gorm_biz.go b/server/initialize/gorm_biz.go index e5f2f97..c1f0d80 100644 --- a/server/initialize/gorm_biz.go +++ b/server/initialize/gorm_biz.go @@ -5,12 +5,13 @@ import ( "github.com/flipped-aurora/gin-vue-admin/server/model/computenode" "github.com/flipped-aurora/gin-vue-admin/server/model/imageregistry" "github.com/flipped-aurora/gin-vue-admin/server/model/instance" + "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn" "github.com/flipped-aurora/gin-vue-admin/server/model/product" ) func bizModel() error { db := global.GVA_DB - err := db.AutoMigrate(imageregistry.ImageRegistry{}, computenode.ComputeNode{}, product.ProductSpec{}, instance.Instance{}) + err := db.AutoMigrate(imageregistry.ImageRegistry{}, computenode.ComputeNode{}, product.ProductSpec{}, instance.Instance{}, pcdn.PcdnNode{}, pcdn.PcdnPolicy{}, pcdn.PcdnDispatchTask{}) if err != nil { return err } diff --git a/server/initialize/router_biz.go b/server/initialize/router_biz.go index ce44c87..45a127d 100644 --- a/server/initialize/router_biz.go +++ b/server/initialize/router_biz.go @@ -29,4 +29,8 @@ func initBizRouter(routers ...*gin.RouterGroup) { instanceRouter := router.RouterGroupApp.Instance instanceRouter.InitInstanceRouter(privateGroup, publicGroup) } + { + pcdnRouter := router.RouterGroupApp.Pcdn + pcdnRouter.InitPcdnRouter(privateGroup) + } } diff --git a/server/model/pcdn/dispatch_task.go b/server/model/pcdn/dispatch_task.go new file mode 100644 index 0000000..385666c --- /dev/null +++ b/server/model/pcdn/dispatch_task.go @@ -0,0 +1,18 @@ +package pcdn + +import "github.com/flipped-aurora/gin-vue-admin/server/global" + +// PcdnDispatchTask PCDN调度任务 +type PcdnDispatchTask struct { + global.GVA_MODEL + TraceID string `json:"traceId" form:"traceId" gorm:"column:trace_id;size:128;index;comment:追踪ID"` + Status string `json:"status" form:"status" gorm:"column:status;size:32;index;comment:任务状态"` + NodeID uint `json:"nodeId" form:"nodeId" gorm:"column:node_id;comment:节点ID"` + PolicyID uint `json:"policyId" form:"policyId" gorm:"column:policy_id;comment:策略ID"` + RequestURI string `json:"requestUri" form:"requestUri" gorm:"column:request_uri;size:255;comment:请求路径"` + Result string `json:"result" form:"result" gorm:"column:result;type:text;comment:执行结果"` +} + +func (PcdnDispatchTask) TableName() string { + return "pcdn_dispatch_task" +} diff --git a/server/model/pcdn/node.go b/server/model/pcdn/node.go new file mode 100644 index 0000000..f32d3cd --- /dev/null +++ b/server/model/pcdn/node.go @@ -0,0 +1,19 @@ +package pcdn + +import "github.com/flipped-aurora/gin-vue-admin/server/global" + +// PcdnNode PCDN节点 +type PcdnNode struct { + global.GVA_MODEL + Name string `json:"name" form:"name" gorm:"column:name;size:128;comment:节点名称" binding:"required"` + Region string `json:"region" form:"region" gorm:"column:region;size:128;comment:节点区域"` + Address string `json:"address" form:"address" gorm:"column:address;size:255;comment:节点地址" binding:"required"` + Weight int `json:"weight" form:"weight" gorm:"column:weight;default:100;comment:节点权重"` + Online bool `json:"online" form:"online" gorm:"column:online;default:true;comment:上下线状态"` + Status string `json:"status" form:"status" gorm:"column:status;size:32;default:'active';comment:节点状态"` + Remark string `json:"remark" form:"remark" gorm:"column:remark;size:512;comment:备注"` +} + +func (PcdnNode) TableName() string { + return "pcdn_node" +} diff --git a/server/model/pcdn/policy.go b/server/model/pcdn/policy.go new file mode 100644 index 0000000..31e332e --- /dev/null +++ b/server/model/pcdn/policy.go @@ -0,0 +1,19 @@ +package pcdn + +import "github.com/flipped-aurora/gin-vue-admin/server/global" + +// PcdnPolicy PCDN策略 +type PcdnPolicy struct { + global.GVA_MODEL + Name string `json:"name" form:"name" gorm:"column:name;size:128;comment:策略名称" binding:"required"` + Template string `json:"template" form:"template" gorm:"column:template;type:text;comment:策略模板" binding:"required"` + GrayPercent int `json:"grayPercent" form:"grayPercent" gorm:"column:gray_percent;default:0;comment:灰度比例"` + Enabled bool `json:"enabled" form:"enabled" gorm:"column:enabled;default:false;comment:是否启用"` + Published bool `json:"published" form:"published" gorm:"column:published;default:false;comment:是否已发布"` + Version string `json:"version" form:"version" gorm:"column:version;size:64;comment:版本"` + PublishedNote string `json:"publishedNote" form:"publishedNote" gorm:"column:published_note;size:255;comment:发布说明"` +} + +func (PcdnPolicy) TableName() string { + return "pcdn_policy" +} diff --git a/server/model/pcdn/request/pcdn.go b/server/model/pcdn/request/pcdn.go new file mode 100644 index 0000000..9a7d0ab --- /dev/null +++ b/server/model/pcdn/request/pcdn.go @@ -0,0 +1,51 @@ +package request + +import ( + "time" + + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" +) + +type PcdnNodeSearch struct { + CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"` + Name string `json:"name" form:"name"` + Region string `json:"region" form:"region"` + Online *bool `json:"online" form:"online"` + request.PageInfo +} + +type PcdnNodeOnlineRequest struct { + ID uint `json:"id" binding:"required"` + Online bool `json:"online"` +} + +type PcdnNodeWeightRequest struct { + ID uint `json:"id" binding:"required"` + Weight int `json:"weight" binding:"required"` +} + +type PcdnPolicySearch struct { + CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"` + Name string `json:"name" form:"name"` + Enabled *bool `json:"enabled" form:"enabled"` + Published *bool `json:"published" form:"published"` + request.PageInfo +} + +type PcdnPolicyGrayReleaseRequest struct { + ID uint `json:"id" binding:"required"` + GrayPercent int `json:"grayPercent" binding:"required,gte=0,lte=100"` + Note string `json:"note"` +} + +type PcdnPolicySwitchRequest struct { + ID uint `json:"id" binding:"required"` + Enabled bool `json:"enabled"` +} + +type PcdnDispatchTaskSearch struct { + CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"` + TraceID string `json:"traceId" form:"traceId"` + Status string `json:"status" form:"status"` + request.PageInfo +} diff --git a/server/router/enter.go b/server/router/enter.go index 8b8e800..0a72aa2 100644 --- a/server/router/enter.go +++ b/server/router/enter.go @@ -5,6 +5,7 @@ import ( "github.com/flipped-aurora/gin-vue-admin/server/router/example" "github.com/flipped-aurora/gin-vue-admin/server/router/imageregistry" "github.com/flipped-aurora/gin-vue-admin/server/router/instance" + "github.com/flipped-aurora/gin-vue-admin/server/router/pcdn" "github.com/flipped-aurora/gin-vue-admin/server/router/product" "github.com/flipped-aurora/gin-vue-admin/server/router/system" ) @@ -18,4 +19,5 @@ type RouterGroup struct { Computenode computenode.RouterGroup Product product.RouterGroup Instance instance.RouterGroup + Pcdn pcdn.RouterGroup } diff --git a/server/router/pcdn/enter.go b/server/router/pcdn/enter.go new file mode 100644 index 0000000..563fc8e --- /dev/null +++ b/server/router/pcdn/enter.go @@ -0,0 +1,9 @@ +package pcdn + +import api "github.com/flipped-aurora/gin-vue-admin/server/api/v1" + +type RouterGroup struct { + PcdnRouter +} + +var pcdnApi = api.ApiGroupApp.PcdnApiGroup diff --git a/server/router/pcdn/pcdn.go b/server/router/pcdn/pcdn.go new file mode 100644 index 0000000..58ddafb --- /dev/null +++ b/server/router/pcdn/pcdn.go @@ -0,0 +1,35 @@ +package pcdn + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/gin-gonic/gin" +) + +type PcdnRouter struct{} + +func (r *PcdnRouter) InitPcdnRouter(Router *gin.RouterGroup) { + pcdnRouter := Router.Group("pcdn").Use(middleware.OperationRecord()) + pcdnRouterWithoutRecord := Router.Group("pcdn") + + { + pcdnRouter.POST("node/create", pcdnApi.CreatePcdnNode) + pcdnRouter.DELETE("node/delete", pcdnApi.DeletePcdnNode) + pcdnRouter.PUT("node/update", pcdnApi.UpdatePcdnNode) + pcdnRouter.PUT("node/online", pcdnApi.SwitchPcdnNodeOnline) + pcdnRouter.PUT("node/weight", pcdnApi.UpdatePcdnNodeWeight) + + pcdnRouter.POST("policy/create", pcdnApi.CreatePcdnPolicy) + pcdnRouter.DELETE("policy/delete", pcdnApi.DeletePcdnPolicy) + pcdnRouter.PUT("policy/update", pcdnApi.UpdatePcdnPolicy) + pcdnRouter.PUT("policy/grayRelease", pcdnApi.GrayReleasePcdnPolicy) + pcdnRouter.PUT("policy/switch", pcdnApi.SwitchPcdnPolicy) + } + + { + pcdnRouterWithoutRecord.GET("node/find", pcdnApi.FindPcdnNode) + pcdnRouterWithoutRecord.GET("node/list", pcdnApi.GetPcdnNodeList) + pcdnRouterWithoutRecord.GET("policy/find", pcdnApi.FindPcdnPolicy) + pcdnRouterWithoutRecord.GET("policy/list", pcdnApi.GetPcdnPolicyList) + pcdnRouterWithoutRecord.GET("task/list", pcdnApi.GetPcdnDispatchTaskList) + } +} diff --git a/server/service/enter.go b/server/service/enter.go index 79a8fd6..ad2616b 100644 --- a/server/service/enter.go +++ b/server/service/enter.go @@ -5,6 +5,7 @@ import ( "github.com/flipped-aurora/gin-vue-admin/server/service/example" "github.com/flipped-aurora/gin-vue-admin/server/service/imageregistry" "github.com/flipped-aurora/gin-vue-admin/server/service/instance" + "github.com/flipped-aurora/gin-vue-admin/server/service/pcdn" "github.com/flipped-aurora/gin-vue-admin/server/service/product" "github.com/flipped-aurora/gin-vue-admin/server/service/system" ) @@ -18,4 +19,5 @@ type ServiceGroup struct { ComputenodeServiceGroup computenode.ServiceGroup ProductServiceGroup product.ServiceGroup InstanceServiceGroup instance.ServiceGroup + PcdnServiceGroup pcdn.ServiceGroup } diff --git a/server/service/pcdn/enter.go b/server/service/pcdn/enter.go new file mode 100644 index 0000000..2bfdc7b --- /dev/null +++ b/server/service/pcdn/enter.go @@ -0,0 +1,7 @@ +package pcdn + +type ServiceGroup struct { + PcdnNodeService + PcdnPolicyService + PcdnDispatchTaskService +} diff --git a/server/service/pcdn/pcdn_dispatch_task.go b/server/service/pcdn/pcdn_dispatch_task.go new file mode 100644 index 0000000..c4e424d --- /dev/null +++ b/server/service/pcdn/pcdn_dispatch_task.go @@ -0,0 +1,33 @@ +package pcdn + +import ( + "context" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn" + pcdnReq "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn/request" +) + +type PcdnDispatchTaskService struct{} + +func (s *PcdnDispatchTaskService) GetPcdnDispatchTaskList(_ context.Context, info pcdnReq.PcdnDispatchTaskSearch) (list []pcdn.PcdnDispatchTask, total int64, err error) { + db := global.GVA_DB.Model(&pcdn.PcdnDispatchTask{}) + if len(info.CreatedAtRange) == 2 { + db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1]) + } + if info.TraceID != "" { + db = db.Where("trace_id = ?", info.TraceID) + } + if info.Status != "" { + db = db.Where("status = ?", info.Status) + } + err = db.Count(&total).Error + if err != nil { + return + } + if info.PageSize > 0 { + db = db.Limit(info.PageSize).Offset(info.PageSize * (info.Page - 1)) + } + err = db.Order("id desc").Find(&list).Error + return +} diff --git a/server/service/pcdn/pcdn_node.go b/server/service/pcdn/pcdn_node.go new file mode 100644 index 0000000..3453a52 --- /dev/null +++ b/server/service/pcdn/pcdn_node.go @@ -0,0 +1,61 @@ +package pcdn + +import ( + "context" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn" + pcdnReq "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn/request" +) + +type PcdnNodeService struct{} + +func (s *PcdnNodeService) CreatePcdnNode(_ context.Context, in *pcdn.PcdnNode) error { + return global.GVA_DB.Create(in).Error +} + +func (s *PcdnNodeService) DeletePcdnNode(_ context.Context, id uint) error { + return global.GVA_DB.Delete(&pcdn.PcdnNode{}, id).Error +} + +func (s *PcdnNodeService) UpdatePcdnNode(_ context.Context, in pcdn.PcdnNode) error { + return global.GVA_DB.Model(&pcdn.PcdnNode{}).Where("id = ?", in.ID).Updates(&in).Error +} + +func (s *PcdnNodeService) GetPcdnNode(_ context.Context, id uint) (out pcdn.PcdnNode, err error) { + err = global.GVA_DB.First(&out, id).Error + return +} + +func (s *PcdnNodeService) GetPcdnNodeList(_ context.Context, info pcdnReq.PcdnNodeSearch) (list []pcdn.PcdnNode, total int64, err error) { + db := global.GVA_DB.Model(&pcdn.PcdnNode{}) + if len(info.CreatedAtRange) == 2 { + db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1]) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + if info.Region != "" { + db = db.Where("region LIKE ?", "%"+info.Region+"%") + } + if info.Online != nil { + db = db.Where("online = ?", *info.Online) + } + err = db.Count(&total).Error + if err != nil { + return + } + if info.PageSize > 0 { + db = db.Limit(info.PageSize).Offset(info.PageSize * (info.Page - 1)) + } + err = db.Order("id desc").Find(&list).Error + return +} + +func (s *PcdnNodeService) SwitchOnline(_ context.Context, in pcdnReq.PcdnNodeOnlineRequest) error { + return global.GVA_DB.Model(&pcdn.PcdnNode{}).Where("id = ?", in.ID).Updates(map[string]any{"online": in.Online}).Error +} + +func (s *PcdnNodeService) UpdateWeight(_ context.Context, in pcdnReq.PcdnNodeWeightRequest) error { + return global.GVA_DB.Model(&pcdn.PcdnNode{}).Where("id = ?", in.ID).Updates(map[string]any{"weight": in.Weight}).Error +} diff --git a/server/service/pcdn/pcdn_policy.go b/server/service/pcdn/pcdn_policy.go new file mode 100644 index 0000000..3b3f5f4 --- /dev/null +++ b/server/service/pcdn/pcdn_policy.go @@ -0,0 +1,65 @@ +package pcdn + +import ( + "context" + + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn" + pcdnReq "github.com/flipped-aurora/gin-vue-admin/server/model/pcdn/request" +) + +type PcdnPolicyService struct{} + +func (s *PcdnPolicyService) CreatePcdnPolicy(_ context.Context, in *pcdn.PcdnPolicy) error { + return global.GVA_DB.Create(in).Error +} + +func (s *PcdnPolicyService) DeletePcdnPolicy(_ context.Context, id uint) error { + return global.GVA_DB.Delete(&pcdn.PcdnPolicy{}, id).Error +} + +func (s *PcdnPolicyService) UpdatePcdnPolicy(_ context.Context, in pcdn.PcdnPolicy) error { + return global.GVA_DB.Model(&pcdn.PcdnPolicy{}).Where("id = ?", in.ID).Updates(&in).Error +} + +func (s *PcdnPolicyService) GetPcdnPolicy(_ context.Context, id uint) (out pcdn.PcdnPolicy, err error) { + err = global.GVA_DB.First(&out, id).Error + return +} + +func (s *PcdnPolicyService) GetPcdnPolicyList(_ context.Context, info pcdnReq.PcdnPolicySearch) (list []pcdn.PcdnPolicy, total int64, err error) { + db := global.GVA_DB.Model(&pcdn.PcdnPolicy{}) + if len(info.CreatedAtRange) == 2 { + db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1]) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + if info.Enabled != nil { + db = db.Where("enabled = ?", *info.Enabled) + } + if info.Published != nil { + db = db.Where("published = ?", *info.Published) + } + err = db.Count(&total).Error + if err != nil { + return + } + if info.PageSize > 0 { + db = db.Limit(info.PageSize).Offset(info.PageSize * (info.Page - 1)) + } + err = db.Order("id desc").Find(&list).Error + return +} + +func (s *PcdnPolicyService) GrayRelease(_ context.Context, in pcdnReq.PcdnPolicyGrayReleaseRequest) error { + return global.GVA_DB.Model(&pcdn.PcdnPolicy{}).Where("id = ?", in.ID).Updates(map[string]any{ + "gray_percent": in.GrayPercent, + "published": true, + "published_note": in.Note, + }).Error +} + +func (s *PcdnPolicyService) SwitchPolicy(_ context.Context, in pcdnReq.PcdnPolicySwitchRequest) error { + return global.GVA_DB.Model(&pcdn.PcdnPolicy{}).Where("id = ?", in.ID).Updates(map[string]any{"enabled": in.Enabled}).Error +} diff --git a/server/source/system/api.go b/server/source/system/api.go index 5c7f2d2..8439b3b 100644 --- a/server/source/system/api.go +++ b/server/source/system/api.go @@ -237,7 +237,23 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { {ApiGroup: "instance", Method: "POST", Path: "/instance/restartContainer", Description: "重启容器"}, {ApiGroup: "instance", Method: "POST", Path: "/instance/startContainer", Description: "启动容器"}, {ApiGroup: "instance", Method: "POST", Path: "/instance/stopContainer", Description: "停止容器"}, - {ApiGroup: "instance", Method: "GET", Path: "/instance/terminal", Description: "容器终端WebSocket"}, + {ApiGroup: "PCDN节点", Method: "POST", Path: "/pcdn/node/create", Description: "创建PCDN节点"}, + {ApiGroup: "PCDN节点", Method: "DELETE", Path: "/pcdn/node/delete", Description: "删除PCDN节点"}, + {ApiGroup: "PCDN节点", Method: "PUT", Path: "/pcdn/node/update", Description: "更新PCDN节点"}, + {ApiGroup: "PCDN节点", Method: "GET", Path: "/pcdn/node/find", Description: "查询PCDN节点"}, + {ApiGroup: "PCDN节点", Method: "GET", Path: "/pcdn/node/list", Description: "获取PCDN节点列表"}, + {ApiGroup: "PCDN节点", Method: "PUT", Path: "/pcdn/node/online", Description: "PCDN节点上下线"}, + {ApiGroup: "PCDN节点", Method: "PUT", Path: "/pcdn/node/weight", Description: "调整PCDN节点权重"}, + + {ApiGroup: "PCDN策略", Method: "POST", Path: "/pcdn/policy/create", Description: "创建PCDN策略"}, + {ApiGroup: "PCDN策略", Method: "DELETE", Path: "/pcdn/policy/delete", Description: "删除PCDN策略"}, + {ApiGroup: "PCDN策略", Method: "PUT", Path: "/pcdn/policy/update", Description: "更新PCDN策略"}, + {ApiGroup: "PCDN策略", Method: "GET", Path: "/pcdn/policy/find", Description: "查询PCDN策略"}, + {ApiGroup: "PCDN策略", Method: "GET", Path: "/pcdn/policy/list", Description: "获取PCDN策略列表"}, + {ApiGroup: "PCDN策略", Method: "PUT", Path: "/pcdn/policy/grayRelease", Description: "PCDN策略灰度发布"}, + {ApiGroup: "PCDN策略", Method: "PUT", Path: "/pcdn/policy/switch", Description: "PCDN策略启停"}, + + {ApiGroup: "PCDN调度", Method: "GET", Path: "/pcdn/task/list", Description: "查询PCDN调度任务"}, // 端口转发API {ApiGroup: "端口转发", Method: "POST", Path: "/portForward/createPortForward", Description: "创建端口转发规则"}, diff --git a/server/source/system/casbin.go b/server/source/system/casbin.go index a8eee95..a797600 100644 --- a/server/source/system/casbin.go +++ b/server/source/system/casbin.go @@ -228,6 +228,23 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "888", V1: "/computeNode/findComputeNode", V2: "GET"}, {Ptype: "p", V0: "888", V1: "/computeNode/getComputeNodeList", V2: "GET"}, + // PCDN相关权限 + {Ptype: "p", V0: "888", V1: "/pcdn/node/create", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/pcdn/node/delete", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/pcdn/node/update", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/pcdn/node/find", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/pcdn/node/list", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/pcdn/node/online", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/pcdn/node/weight", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/create", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/delete", V2: "DELETE"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/update", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/find", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/list", V2: "GET"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/grayRelease", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/pcdn/policy/switch", V2: "PUT"}, + {Ptype: "p", V0: "888", V1: "/pcdn/task/list", V2: "GET"}, + // 镜像库相关权限 {Ptype: "p", V0: "888", V1: "/imageRegistry/createImageRegistry", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/imageRegistry/deleteImageRegistry", V2: "DELETE"}, diff --git a/server/source/system/menu.go b/server/source/system/menu.go index 5a438ea..02c50a7 100644 --- a/server/source/system/menu.go +++ b/server/source/system/menu.go @@ -68,6 +68,7 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "computeNode", Name: "computeNode", Component: "view/computenode/computeNode/computeNode.vue", Sort: 0, Meta: Meta{Title: "算力节点", Icon: "chicken"}}, {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "productSpec", Name: "productSpec", Component: "view/product/productSpec/productSpec.vue", Sort: 0, Meta: Meta{Title: "产品规格", Icon: "baseball"}}, {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "instance", Name: "instance", Component: "view/instance/instance/instance.vue", Sort: 0, Meta: Meta{Title: "实例管理", Icon: "briefcase"}}, + {MenuLevel: 0, Hidden: false, ParentId: 0, Path: "pcdn", Name: "pcdn", Component: "view/routerHolder.vue", Sort: 0, Meta: Meta{Title: "PCDN管理", Icon: "connection"}}, } // 先创建父级菜单(ParentId = 0 的菜单) @@ -116,6 +117,11 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}}, {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "anInfo", Name: "anInfo", Component: "plugin/announcement/view/info.vue", Sort: 5, Meta: Meta{Title: "公告管理[示例]", Icon: "scaleToOriginal"}}, + // pcdn子菜单 + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["pcdn"], Path: "pcdnNode", Name: "pcdnNode", Component: "view/pcdn/node/index.vue", Sort: 1, Meta: Meta{Title: "节点管理", Icon: "coin"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["pcdn"], Path: "pcdnPolicy", Name: "pcdnPolicy", Component: "view/pcdn/policy/index.vue", Sort: 2, Meta: Meta{Title: "策略管理", Icon: "set-up"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["pcdn"], Path: "pcdnTask", Name: "pcdnTask", Component: "view/pcdn/task/index.vue", Sort: 3, Meta: Meta{Title: "调度任务", Icon: "tickets"}}, + // portForward子菜单 {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["portForward"], Path: "portForwardRules", Name: "portForwardRules", Component: "plugin/portforward/view/portForward.vue", Sort: 1, Meta: Meta{Title: "转发规则", Icon: "list"}}, }