From 78990c6094cec97eac45a0aa4269925a5fac98c4 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sun, 17 Aug 2025 18:25:13 +0800 Subject: [PATCH] =?UTF-8?q?add=20=20app=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devcloud/mcenter/apps/label/api/api.go | 57 ++++++ devcloud/mcenter/apps/label/api/label.go | 32 ++++ devcloud/mcenter/apps/label/impl/label.go | 5 + .../mcenter/apps/label/impl/label_test.go | 1 + devcloud/mcenter/apps/label/interface.go | 1 + devcloud/mcenter/apps/registry.go | 1 + devcloud/mpaas/apps/application/api/api.go | 23 +++ .../mpaas/apps/application/api/application.go | 38 ++++ .../apps/application/impl/application.go | 3 + .../apps/application/impl/application_test.go | 1 + devcloud/mpaas/apps/application/interface.go | 20 +- devcloud/web/src/api/client.js | 2 +- devcloud/web/src/api/mcenter.js | 3 + devcloud/web/src/api/mppas.js | 9 + devcloud/web/src/layout/DashboardLayout.vue | 2 +- devcloud/web/src/layout/FrontendLayout.vue | 2 +- devcloud/web/src/layout/MenuLayout.vue | 2 +- devcloud/web/src/pages/develop/AppPage.vue | 145 ++++++++++----- .../pages/develop/components/AppFormModel.vue | 175 ++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- 21 files changed, 467 insertions(+), 61 deletions(-) create mode 100644 devcloud/mcenter/apps/label/api/api.go create mode 100644 devcloud/mcenter/apps/label/api/label.go create mode 100644 devcloud/web/src/pages/develop/components/AppFormModel.vue diff --git a/devcloud/mcenter/apps/label/api/api.go b/devcloud/mcenter/apps/label/api/api.go new file mode 100644 index 0000000..2808f94 --- /dev/null +++ b/devcloud/mcenter/apps/label/api/api.go @@ -0,0 +1,57 @@ +package api + +import ( + "122.51.31.227/go-course/go18/devcloud/audit/audit" + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/label" + "122.51.31.227/go-course/go18/devcloud/mcenter/permission" + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/gorestful" + + restfulspec "github.com/emicklei/go-restful-openapi/v2" + "github.com/emicklei/go-restful/v3" +) + +func init() { + ioc.Api().Registry(&LabelRestfulApiHandler{}) +} + +type LabelRestfulApiHandler struct { + ioc.ObjectImpl + + // 依赖控制器 + svc label.Service +} + +func (h *LabelRestfulApiHandler) Name() string { + return "labels" +} + +func (h *LabelRestfulApiHandler) Init() error { + h.svc = label.GetService() + + tags := []string{"标签管理"} + ws := gorestful.ObjectRouter(h) + // required_auth=true/false + ws.Route(ws.GET("").To(h.QueryLabel). + Doc("标签列表查询"). + Metadata(restfulspec.KeyOpenAPITags, tags). + // 这个开关怎么生效 + // 中间件需求读取接口的描述信息,来决定是否需要认证 + Metadata(permission.Auth(true)). + Metadata(permission.Permission(false)). + Metadata(permission.Resource("labels")). + Metadata(permission.Action("list")). + Metadata(audit.Enable(true)). + Param(restful.QueryParameter("page_size", "分页大小").DataType("integer")). + Param(restful.QueryParameter("page_number", "页码").DataType("integer")). + Writes(Set{}). + Returns(200, "OK", Set{})) + return nil +} + +// *types.Set[*Label] +// 返回的泛型, API Doc这个工具 不支持泛型 +type Set struct { + Total int64 `json:"total"` + Items []label.Label `json:"items"` +} diff --git a/devcloud/mcenter/apps/label/api/label.go b/devcloud/mcenter/apps/label/api/label.go new file mode 100644 index 0000000..3ca5067 --- /dev/null +++ b/devcloud/mcenter/apps/label/api/label.go @@ -0,0 +1,32 @@ +package api + +import ( + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/label" + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token" + "github.com/emicklei/go-restful/v3" + "github.com/gin-gonic/gin/binding" + "github.com/infraboard/mcube/v2/http/restful/response" + "github.com/infraboard/mcube/v2/ioc/config/log" +) + +func (h *LabelRestfulApiHandler) QueryLabel(r *restful.Request, w *restful.Response) { + req := label.NewQueryLabelRequest() + + // 过滤条件在认证完成后的上下文中 + tk := token.GetTokenFromCtx(r.Request.Context()) + log.L().Debug().Msgf("resource scope: %s", tk.ResourceScope) + + // 用户的参数 + if err := binding.Query.Bind(r.Request, &req); err != nil { + response.Failed(w, err) + return + } + + set, err := h.svc.QueryLabel(r.Request.Context(), req) + if err != nil { + response.Failed(w, err) + return + } + + response.Success(w, set) +} diff --git a/devcloud/mcenter/apps/label/impl/label.go b/devcloud/mcenter/apps/label/impl/label.go index 8fa0fc7..82545cd 100644 --- a/devcloud/mcenter/apps/label/impl/label.go +++ b/devcloud/mcenter/apps/label/impl/label.go @@ -30,6 +30,11 @@ func (i *LabelServiceImpl) QueryLabel(ctx context.Context, in *label.QueryLabelR set := types.New[*label.Label]() query := datasource.DBFromCtx(ctx).Model(&label.Label{}) + + if in.Key != "" { + query = query.Where("`key` = ?", in.Key) + } + err := query.Count(&set.Total).Error if err != nil { return nil, err diff --git a/devcloud/mcenter/apps/label/impl/label_test.go b/devcloud/mcenter/apps/label/impl/label_test.go index cb67b0e..449cb69 100644 --- a/devcloud/mcenter/apps/label/impl/label_test.go +++ b/devcloud/mcenter/apps/label/impl/label_test.go @@ -29,6 +29,7 @@ func TestCreateLabel(t *testing.T) { func TestQueryLabel(t *testing.T) { req := label.NewQueryLabelRequest() + req.Key = "team" set, err := svc.QueryLabel(ctx, req) if err != nil { t.Fatal(err) diff --git a/devcloud/mcenter/apps/label/interface.go b/devcloud/mcenter/apps/label/interface.go index 9b3303f..0c5ae8d 100644 --- a/devcloud/mcenter/apps/label/interface.go +++ b/devcloud/mcenter/apps/label/interface.go @@ -49,6 +49,7 @@ func NewQueryLabelRequest() *QueryLabelRequest { type QueryLabelRequest struct { *request.PageRequest + Key string `json:"key" form:"key"` } type DescribeLabelRequest struct { diff --git a/devcloud/mcenter/apps/registry.go b/devcloud/mcenter/apps/registry.go index 269fc01..3ccb0a4 100644 --- a/devcloud/mcenter/apps/registry.go +++ b/devcloud/mcenter/apps/registry.go @@ -9,6 +9,7 @@ import ( // 鉴权 _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint/impl" + _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/label/api" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/label/impl" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace/impl" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/policy/impl" diff --git a/devcloud/mpaas/apps/application/api/api.go b/devcloud/mpaas/apps/application/api/api.go index 363b58d..ae0aecc 100644 --- a/devcloud/mpaas/apps/application/api/api.go +++ b/devcloud/mpaas/apps/application/api/api.go @@ -31,6 +31,18 @@ func (h *UserRestfulApiHandler) Init() error { tags := []string{"应用管理"} ws := gorestful.ObjectRouter(h) + + ws.Route(ws.POST("").To(h.CreateApplication). + Doc("应用创建"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Metadata(permission.Auth(true)). + Metadata(permission.Permission(true)). + Metadata(permission.Resource("application")). + Metadata(permission.Action("create")). + Metadata(audit.Enable(true)). + Writes(application.Application{}). + Returns(200, "OK", application.Application{})) + // required_auth=true/false ws.Route(ws.GET("").To(h.QueryApplication). Doc("应用列表查询"). @@ -47,6 +59,17 @@ func (h *UserRestfulApiHandler) Init() error { Writes(Set{}). Returns(200, "OK", Set{})) + ws.Route(ws.DELETE("/{id}").To(h.DeleteApplication). + Doc("应用删除"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Metadata(permission.Auth(true)). + Metadata(permission.Permission(true)). + Metadata(permission.Resource("application")). + Metadata(permission.Action("delete")). + Metadata(audit.Enable(true)). + Writes(). + Returns(200, "OK", nil)) + return nil } diff --git a/devcloud/mpaas/apps/application/api/application.go b/devcloud/mpaas/apps/application/api/application.go index 6e508ba..c22ff72 100644 --- a/devcloud/mpaas/apps/application/api/application.go +++ b/devcloud/mpaas/apps/application/api/application.go @@ -31,3 +31,41 @@ func (h *UserRestfulApiHandler) QueryApplication(r *restful.Request, w *restful. response.Success(w, set) } + +func (h *UserRestfulApiHandler) CreateApplication(r *restful.Request, w *restful.Response) { + req := application.NewCreateApplicationRequest() + if err := r.ReadEntity(req); err != nil { + response.Failed(w, err) + return + } + + // 过滤条件在认证完成后的上下文中 + tk := token.GetTokenFromCtx(r.Request.Context()) + req.SetNamespaceId(*tk.NamespaceId) + req.CreateBy = tk.UserName + + set, err := h.svc.CreateApplication(r.Request.Context(), req) + if err != nil { + response.Failed(w, err) + return + } + + response.Success(w, set) +} + +func (h *UserRestfulApiHandler) DeleteApplication(r *restful.Request, w *restful.Response) { + req := application.NewDeleteApplicationRequest(r.PathParameter("id")) + + // 过滤条件在认证完成后的上下文中 + tk := token.GetTokenFromCtx(r.Request.Context()) + req.ResourceScope = tk.ResourceScope + log.L().Debug().Msgf("resource scope: %s", tk.ResourceScope) + + set, err := h.svc.DeleteApplication(r.Request.Context(), req) + if err != nil { + response.Failed(w, err) + return + } + + response.Success(w, set) +} diff --git a/devcloud/mpaas/apps/application/impl/application.go b/devcloud/mpaas/apps/application/impl/application.go index 8a04f54..786df09 100644 --- a/devcloud/mpaas/apps/application/impl/application.go +++ b/devcloud/mpaas/apps/application/impl/application.go @@ -40,6 +40,9 @@ func (i *ApplicationServiceImpl) QueryApplication(ctx context.Context, in *appli if in.Ready != nil { query = query.Where("ready = ?", *in.Ready) } + if in.Keywords != "" { + query = query.Where("name LIKE ?", "%"+in.Keywords+"%") + } err := query.Count(&set.Total).Error if err != nil { diff --git a/devcloud/mpaas/apps/application/impl/application_test.go b/devcloud/mpaas/apps/application/impl/application_test.go index 8504b21..5e3e91f 100644 --- a/devcloud/mpaas/apps/application/impl/application_test.go +++ b/devcloud/mpaas/apps/application/impl/application_test.go @@ -29,6 +29,7 @@ func TestQueryApplication(t *testing.T) { // dev01.% req.SetNamespaceId(1) req.SetScope("team", []string{"dev01%"}) + req.Keywords = "dev01" // req.SetScope("env", []string{"prod"}) ins, err := svc.QueryApplication(ctx, req) if err != nil { diff --git a/devcloud/mpaas/apps/application/interface.go b/devcloud/mpaas/apps/application/interface.go index ad0d4be..4018861 100644 --- a/devcloud/mpaas/apps/application/interface.go +++ b/devcloud/mpaas/apps/application/interface.go @@ -46,11 +46,13 @@ type QueryApplicationRequest struct { type QueryApplicationRequestSpec struct { *request.PageRequest // 应用ID - Id string `json:"id" bson:"_id"` + Id string `json:"id" form:"id"` // 应用名称 - Name string `json:"name" bson:"name"` + Name string `json:"name" form:"name"` // 应用是否就绪 - Ready *bool `json:"ready" bson:"ready"` + Ready *bool `json:"ready" form:"ready"` + // 关键字 + Keywords string `json:"keywords" form:"keywords"` } type UpdateApplicationRequest struct { @@ -60,10 +62,22 @@ type UpdateApplicationRequest struct { CreateApplicationSpec } +func NewDeleteApplicationRequest(appId string) *DeleteApplicationRequest { + return &DeleteApplicationRequest{ + DescribeApplicationRequest: *NewDescribeApplicationRequest(appId), + } +} + type DeleteApplicationRequest struct { DescribeApplicationRequest } +func NewDescribeApplicationRequest(appId string) *DescribeApplicationRequest { + return &DescribeApplicationRequest{ + Id: appId, + } +} + type DescribeApplicationRequest struct { policy.ResourceScope // 应用ID diff --git a/devcloud/web/src/api/client.js b/devcloud/web/src/api/client.js index af2fe5d..b3e176f 100644 --- a/devcloud/web/src/api/client.js +++ b/devcloud/web/src/api/client.js @@ -4,7 +4,7 @@ import { Message } from '@arco-design/web-vue' // 封装一个axios的实例,http cient实例 // https://axios-http.com/zh/docs/instance const client = axios.create({ - timeout: 3000, + timeout: 5000, }) // 拦截API的返回结果, 如果是异常 提取异常信息,并展示 diff --git a/devcloud/web/src/api/mcenter.js b/devcloud/web/src/api/mcenter.js index a26e1d1..2d197ba 100644 --- a/devcloud/web/src/api/mcenter.js +++ b/devcloud/web/src/api/mcenter.js @@ -4,6 +4,9 @@ var MCENTER_API = { Login: (data) => { return client.post('/api/devcloud/v1/token', data) }, + LabelList: (params) => { + return client.get('/api/devcloud/v1/labels', { params }) + }, } export default MCENTER_API diff --git a/devcloud/web/src/api/mppas.js b/devcloud/web/src/api/mppas.js index 00cee91..6fee80c 100644 --- a/devcloud/web/src/api/mppas.js +++ b/devcloud/web/src/api/mppas.js @@ -4,6 +4,15 @@ var MPAAS_API = { AppList: (params) => { return client.get('/api/devcloud/v1/applications', { params }) }, + AppCreate: (data) => { + return client.post('/api/devcloud/v1/applications', data) + }, + AppUpdate: (id, data) => { + return client.put(`/api/devcloud/v1/applications/${id}`, data) + }, + AppDelete: (id) => { + return client.delete(`/api/devcloud/v1/applications/${id}`) + }, } export default MPAAS_API diff --git a/devcloud/web/src/layout/DashboardLayout.vue b/devcloud/web/src/layout/DashboardLayout.vue index cdd7dcc..cd76db4 100644 --- a/devcloud/web/src/layout/DashboardLayout.vue +++ b/devcloud/web/src/layout/DashboardLayout.vue @@ -134,7 +134,7 @@ const handleUserOption = (option) => { .router-view-wrapper { flex: 1; padding: 20px; - // min-height: calc(100vh - 180px); + min-height: calc(100vh - 180px); /* 调整最小高度 */ } diff --git a/devcloud/web/src/layout/FrontendLayout.vue b/devcloud/web/src/layout/FrontendLayout.vue index da59038..03b1f9d 100644 --- a/devcloud/web/src/layout/FrontendLayout.vue +++ b/devcloud/web/src/layout/FrontendLayout.vue @@ -174,7 +174,7 @@ .router-view-wrapper { flex: 1; padding: 20px; - // min-height: calc(100vh - 180px); + min-height: calc(100vh - 180px); /* 调整最小高度 */ } diff --git a/devcloud/web/src/layout/MenuLayout.vue b/devcloud/web/src/layout/MenuLayout.vue index 18d2434..1a1f955 100644 --- a/devcloud/web/src/layout/MenuLayout.vue +++ b/devcloud/web/src/layout/MenuLayout.vue @@ -418,7 +418,7 @@ const systemMenus = shallowReactive({ .router-view-wrapper { flex: 1; padding: 0px 20px 0px 40px; - // min-height: calc(100vh - 180px); + min-height: calc(100vh - 180px); /* 动态计算最小高度 */ } diff --git a/devcloud/web/src/pages/develop/AppPage.vue b/devcloud/web/src/pages/develop/AppPage.vue index 8931be8..8506812 100644 --- a/devcloud/web/src/pages/develop/AppPage.vue +++ b/devcloud/web/src/pages/develop/AppPage.vue @@ -1,63 +1,71 @@ diff --git a/devcloud/web/src/pages/develop/components/AppFormModel.vue b/devcloud/web/src/pages/develop/components/AppFormModel.vue new file mode 100644 index 0000000..b0bb590 --- /dev/null +++ b/devcloud/web/src/pages/develop/components/AppFormModel.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/go.mod b/go.mod index 4a9d4f2..961440a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/caarlos0/env/v6 v6.10.1 github.com/emicklei/go-restful-openapi/v2 v2.11.0 github.com/emicklei/go-restful/v3 v3.12.2 - github.com/gin-gonic/gin v1.10.0 + github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.6.0 github.com/infraboard/devops v0.0.6 github.com/infraboard/mcube/v2 v2.0.63 diff --git a/go.sum b/go.sum index 443bea1..929b41d 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=