From 2e969d46fb75d7c7ec903a62e3205efeca46206f Mon Sep 17 00:00:00 2001 From: "meilin.huang" <954537473@qq.com> Date: Fri, 25 Aug 2023 10:20:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20mongo=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 5 +- mayfly_go_web/src/common/enums.ts | 7 - mayfly_go_web/src/common/utils/export.ts | 2 +- .../src/views/ops/mongo/MongoDataOp.vue | 151 +++++--- .../src/views/ops/mongo/MongoDbs.vue | 352 ++++++++++++++++++ .../src/views/ops/mongo/MongoList.vue | 257 +------------ .../src/views/ops/mongo/MongoRunCommand.vue | 196 ++++++++++ mayfly_go_web/yarn.lock | 38 +- server/go.mod | 37 +- server/internal/mongo/api/form/mongo.go | 4 +- server/internal/mongo/api/mongo.go | 14 +- server/internal/mongo/router/mongo.go | 8 +- 12 files changed, 728 insertions(+), 343 deletions(-) delete mode 100644 mayfly_go_web/src/common/enums.ts create mode 100644 mayfly_go_web/src/views/ops/mongo/MongoDbs.vue create mode 100644 mayfly_go_web/src/views/ops/mongo/MongoRunCommand.vue diff --git a/Dockerfile b/Dockerfile index b2beeec4..94a53506 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN yarn RUN yarn build # 构建后端资源 -FROM golang:1.20-alpine3.16 as be-builder +FROM golang:1.21.0 as be-builder ENV GOPROXY https://goproxy.cn WORKDIR /mayfly @@ -31,6 +31,9 @@ FROM alpine:3.16 RUN apk add --no-cache ca-certificates bash expat +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone + WORKDIR /mayfly COPY --from=be-builder /mayfly/mayfly-go /usr/local/bin/mayfly-go diff --git a/mayfly_go_web/src/common/enums.ts b/mayfly_go_web/src/common/enums.ts deleted file mode 100644 index fa42f3cf..00000000 --- a/mayfly_go_web/src/common/enums.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum ResultEnum { - SUCCESS = 200, - ERROR = 400, - PARAM_ERROR = 405, - SERVER_ERROR = 500, - NO_PERMISSION = 501, -} diff --git a/mayfly_go_web/src/common/utils/export.ts b/mayfly_go_web/src/common/utils/export.ts index c77c6ebc..fddccccc 100644 --- a/mayfly_go_web/src/common/utils/export.ts +++ b/mayfly_go_web/src/common/utils/export.ts @@ -28,7 +28,7 @@ export function exportCsv(filename: string, columns: string[], datas: []) { let link = document.createElement('a'); let exportContent = '\uFEFF'; let blob = new Blob([exportContent + csvString], { - type: 'text/plain;charset=utrf-8', + type: 'text/plain;charset=utf-8', }); link.id = 'download-csv'; link.setAttribute('href', URL.createObjectURL(blob)); diff --git a/mayfly_go_web/src/views/ops/mongo/MongoDataOp.vue b/mayfly_go_web/src/views/ops/mongo/MongoDataOp.vue index d06aa284..aa029417 100644 --- a/mayfly_go_web/src/views/ops/mongo/MongoDataOp.vue +++ b/mayfly_go_web/src/views/ops/mongo/MongoDataOp.vue @@ -35,55 +35,81 @@ - - - - - -
- - -
-
- - - - - -
- - - - -
-
- +
+ + + - + + {{ nowColl.stats.ns }} + + + {{ nowColl.stats.count }} + + + {{ formatByteSize(nowColl.stats.avgObjSize) }} + + + {{ formatByteSize(nowColl.stats.size) }} + + + {{ formatByteSize(nowColl.stats.totalSize) }} + + + {{ formatByteSize(nowColl.stats.storageSize) }} + + + {{ formatByteSize(nowColl.stats.freeStorageSize) }} + + + - - - - - - - -
+ + + + + +
+ + +
- -
-
-
-
- + + + + + + +
+ + + + +
+
+ + + + + + + +
+
+
+
+
+ + + +
@@ -120,7 +146,7 @@ @@ -131,7 +157,7 @@ + + diff --git a/mayfly_go_web/src/views/ops/mongo/MongoList.vue b/mayfly_go_web/src/views/ops/mongo/MongoList.vue index 884184fb..89196a5f 100644 --- a/mayfly_go_web/src/views/ops/mongo/MongoList.vue +++ b/mayfly_go_web/src/views/ops/mongo/MongoList.vue @@ -34,136 +34,15 @@ - - - - - - - + - - - - - - - - - {{ databaseDialog.statsDialog.data.db }} - - - {{ databaseDialog.statsDialog.data.collections }} - - - {{ databaseDialog.statsDialog.data.objects }} - - - {{ databaseDialog.statsDialog.data.indexes }} - - - - {{ formatByteSize(databaseDialog.statsDialog.data.avgObjSize) }} - - - {{ formatByteSize(databaseDialog.statsDialog.data.dataSize) }} - - - {{ formatByteSize(databaseDialog.statsDialog.data.totalSize) }} - - - {{ formatByteSize(databaseDialog.statsDialog.data.storageSize) }} - - - - {{ formatByteSize(databaseDialog.statsDialog.data.fsTotalSize) }} - - - {{ formatByteSize(databaseDialog.statsDialog.data.fsUsedSize) }} - - - {{ formatByteSize(databaseDialog.statsDialog.data.indexSize) }} - - - - - - -
- 新建 -
- - - - - - - - - - - {{ collectionsDialog.statsDialog.data.ns }} - - - {{ collectionsDialog.statsDialog.data.count }} - - - {{ formatByteSize(collectionsDialog.statsDialog.data.avgObjSize) }} - - - {{ collectionsDialog.statsDialog.data.nindexes }} - - - - {{ formatByteSize(collectionsDialog.statsDialog.data.size) }} - - - {{ formatByteSize(collectionsDialog.statsDialog.data.totalSize) }} - - - {{ formatByteSize(collectionsDialog.statsDialog.data.storageSize) }} - - - {{ formatByteSize(collectionsDialog.statsDialog.data.freeStorageSize) }} - - - -
- - - - - - - - - - + import { mongoApi } from './api'; -import { ref, toRefs, reactive, onMounted } from 'vue'; +import { defineAsyncComponent, ref, toRefs, reactive, onMounted } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; -import MongoEdit from './MongoEdit.vue'; -import { formatByteSize } from '@/common/utils/format'; import TagInfo from '../component/TagInfo.vue'; import PageTable from '@/components/pagetable/PageTable.vue'; import { TableColumn, TableQuery } from '@/components/pagetable'; +const MongoEdit = defineAsyncComponent(() => import('./MongoEdit.vue')); +const MongoDbs = defineAsyncComponent(() => import('./MongoDbs.vue')); +const MongoRunCommand = defineAsyncComponent(() => import('./MongoRunCommand.vue')); + const pageTableRef: any = ref(null); const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect')]; @@ -193,7 +74,7 @@ const columns = ref([ TableColumn.new('uri', '连接uri'), TableColumn.new('createTime', '创建时间').isTime(), TableColumn.new('creator', '创建人'), - TableColumn.new('action', '操作').isSlot().setMinWidth(100).fixedRight().alignCenter(), + TableColumn.new('action', '操作').isSlot().setMinWidth(145).fixedRight().alignCenter(), ]); const state = reactive({ @@ -215,126 +96,24 @@ const state = reactive({ data: null as any, title: '新增mongo', }, - databaseDialog: { - visible: false, - data: [], - title: '', - statsDialog: { - visible: false, - data: {} as any, - title: '', - }, - }, - collectionsDialog: { - database: '', - visible: false, - data: [], - title: '', - statsDialog: { - visible: false, - data: {} as any, - title: '', - }, - }, - createCollectionDialog: { - visible: false, - form: { - name: '', - }, - }, + dbsVisible: false, + usersVisible: false, }); -const { tags, list, total, selectionData, query, mongoEditDialog, databaseDialog, collectionsDialog, createCollectionDialog } = toRefs(state); +const { tags, list, total, selectionData, query, mongoEditDialog, dbsVisible, usersVisible } = toRefs(state); onMounted(async () => { search(); }); const showDatabases = async (id: number) => { - // state.query.tagPath = row.tagPath state.dbOps.dbId = id; - - state.databaseDialog.data = (await mongoApi.databases.request({ id })).Databases; - state.databaseDialog.title = `数据库列表`; - state.databaseDialog.visible = true; + state.dbsVisible = true; }; -const showDatabaseStats = async (dbName: string) => { - state.databaseDialog.statsDialog.data = await mongoApi.runCommand.request({ - id: state.dbOps.dbId, - database: dbName, - command: { - dbStats: 1, - }, - }); - state.databaseDialog.statsDialog.title = `'${dbName}' stats`; - state.databaseDialog.statsDialog.visible = true; -}; - -const showCollections = async (database: string) => { - state.collectionsDialog.database = database; - state.collectionsDialog.data = []; - setCollections(database); - state.collectionsDialog.title = `'${database}' 集合`; - state.collectionsDialog.visible = true; -}; - -const setCollections = async (database: string) => { - const res = await mongoApi.collections.request({ id: state.dbOps.dbId, database }); - const collections = [] as any; - for (let r of res) { - collections.push({ name: r }); - } - state.collectionsDialog.data = collections; -}; - -/** - * 显示集合状态 - */ -const showCollectionStats = async (collection: string) => { - state.collectionsDialog.statsDialog.data = await mongoApi.runCommand.request({ - id: state.dbOps.dbId, - database: state.collectionsDialog.database, - command: { - collStats: collection, - }, - }); - state.collectionsDialog.statsDialog.title = `'${collection}' stats`; - state.collectionsDialog.statsDialog.visible = true; -}; - -/** - * 删除集合 - */ -const onDeleteCollection = async (collection: string) => { - await mongoApi.runCommand.request({ - id: state.dbOps.dbId, - database: state.collectionsDialog.database, - command: { - drop: collection, - }, - }); - ElMessage.success('集合删除成功'); - setCollections(state.collectionsDialog.database); -}; - -const showCreateCollectionDialog = () => { - state.createCollectionDialog.visible = true; -}; - -const onCreateCollection = async () => { - const form = state.createCollectionDialog.form; - await mongoApi.runCommand.request({ - id: state.dbOps.dbId, - database: state.collectionsDialog.database, - command: { - create: form.name, - }, - }); - ElMessage.success('集合创建成功'); - state.createCollectionDialog.visible = false; - state.createCollectionDialog.form = {} as any; - setCollections(state.collectionsDialog.database); +const showUsers = async (id: number) => { + state.dbOps.dbId = id; + state.usersVisible = true; }; const deleteMongo = async () => { diff --git a/mayfly_go_web/src/views/ops/mongo/MongoRunCommand.vue b/mayfly_go_web/src/views/ops/mongo/MongoRunCommand.vue new file mode 100644 index 00000000..c16a78f8 --- /dev/null +++ b/mayfly_go_web/src/views/ops/mongo/MongoRunCommand.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/mayfly_go_web/yarn.lock b/mayfly_go_web/yarn.lock index 32d9dcfa..2c8b711a 100644 --- a/mayfly_go_web/yarn.lock +++ b/mayfly_go_web/yarn.lock @@ -701,10 +701,10 @@ concat-map@0.0.1: resolved "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -countup.js@^2.0.7: - version "2.0.8" - resolved "https://registry.nlark.com/countup.js/download/countup.js-2.0.8.tgz" - integrity sha1-7KDDHJ2z93acuklNkxXNUtuq8bk= +countup.js@^2.7.0: + version "2.7.0" + resolved "https://registry.npmmirror.com/countup.js/-/countup.js-2.7.0.tgz#a5521bd935f0ae83417d0128e73f2a2d2543c9c7" + integrity sha512-IP9nYLGgW//0If73eXQdFlReGhpFGHaStqB1v82FknxnUWueF6HFuuOXySW4sEDMc88PsZL1EOn/NPkfTZalmQ== cropperjs@^1.5.11: version "1.5.12" @@ -1414,10 +1414,10 @@ mitt@^3.0.1: resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== -monaco-editor@^0.40.0: - version "0.40.0" - resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.40.0.tgz#d10834e15ad50a15ec61fd01892e508464ebe2fe" - integrity sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g== +monaco-editor@^0.41.0: + version "0.41.0" + resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.41.0.tgz#2ba31e5af7e3ae93ac5d7467ec2772ef9b3d967f" + integrity sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA== monaco-sql-languages@^0.11.0: version "0.11.0" @@ -1575,7 +1575,7 @@ postcss@^8.1.10: picocolors "^1.0.0" source-map-js "^1.0.1" -postcss@^8.4.26: +postcss@^8.4.27: version "8.4.27" resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.27.tgz#234d7e4b72e34ba5a92c29636734349e0d9c3057" integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ== @@ -1666,10 +1666,10 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^3.25.2: - version "3.26.2" - resolved "https://registry.npmmirror.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c" - integrity sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA== +rollup@^3.27.1: + version "3.28.0" + resolved "https://registry.npmmirror.com/rollup/-/rollup-3.28.0.tgz#a3c70004b01934760c0cb8df717c7a1d932389a2" + integrity sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw== optionalDependencies: fsevents "~2.3.2" @@ -1855,14 +1855,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -vite@^4.4.7: - version "4.4.7" - resolved "https://registry.npmmirror.com/vite/-/vite-4.4.7.tgz#71b8a37abaf8d50561aca084dbb77fa342824154" - integrity sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw== +vite@^4.4.9: + version "4.4.9" + resolved "https://registry.npmmirror.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d" + integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA== dependencies: esbuild "^0.18.10" - postcss "^8.4.26" - rollup "^3.25.2" + postcss "^8.4.27" + rollup "^3.27.1" optionalDependencies: fsevents "~2.3.2" diff --git a/server/go.mod b/server/go.mod index 1f071a2d..9ad0c45a 100644 --- a/server/go.mod +++ b/server/go.mod @@ -1,30 +1,33 @@ module mayfly-go -go 1.20 +go 1.21 require ( github.com/buger/jsonparser v1.1.1 github.com/gin-gonic/gin v1.9.1 github.com/go-gormigrate/gormigrate/v2 v2.1.0 + github.com/go-playground/locales v0.14.1 + github.com/go-playground/universal-translator v0.18.1 + github.com/go-playground/validator/v10 v10.14.0 github.com/go-sql-driver/mysql v1.7.1 github.com/golang-jwt/jwt/v5 v5.0.0 github.com/gorilla/websocket v1.5.0 - github.com/lib/pq v1.10.7 + github.com/lib/pq v1.10.9 github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d github.com/mojocn/base64Captcha v1.3.5 // 验证码 - github.com/pkg/sftp v1.13.5 + github.com/pkg/sftp v1.13.6 github.com/pquerna/otp v1.4.0 - github.com/redis/go-redis/v9 v9.0.5 + github.com/redis/go-redis/v9 v9.1.0 github.com/robfig/cron/v3 v3.0.1 // 定时任务 github.com/sirupsen/logrus v1.9.3 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 - go.mongodb.org/mongo-driver v1.11.4 // mongo - golang.org/x/crypto v0.11.0 // ssh - golang.org/x/oauth2 v0.10.0 + go.mongodb.org/mongo-driver v1.12.1 // mongo + golang.org/x/crypto v0.12.0 // ssh + golang.org/x/oauth2 v0.11.0 gopkg.in/yaml.v3 v3.0.1 // gorm gorm.io/driver/mysql v1.5.1 - gorm.io/gorm v1.25.2 + gorm.io/gorm v1.25.4 ) require ( @@ -35,9 +38,6 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -48,25 +48,26 @@ require ( github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.1 // indirect - github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/server/internal/mongo/api/form/mongo.go b/server/internal/mongo/api/form/mongo.go index 237e3733..e38b35c2 100644 --- a/server/internal/mongo/api/form/mongo.go +++ b/server/internal/mongo/api/form/mongo.go @@ -16,8 +16,8 @@ type MongoCommand struct { } type MongoRunCommand struct { - Database string `binding:"required" json:"database"` - Command map[string]any `json:"command"` + Database string `binding:"required" json:"database"` + Command []map[string]any `json:"command"` } type MongoFindCommand struct { diff --git a/server/internal/mongo/api/mongo.go b/server/internal/mongo/api/mongo.go index dbfff7f4..336e710c 100644 --- a/server/internal/mongo/api/mongo.go +++ b/server/internal/mongo/api/mongo.go @@ -91,12 +91,24 @@ func (m *Mongo) RunCommand(rc *req.Ctx) { commandForm := new(form.MongoRunCommand) ginx.BindJsonAndValid(rc.GinCtx, commandForm) cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx)) + rc.ReqParam = commandForm + + // 顺序执行 + commands := bson.D{} + for _, cmd := range commandForm.Command { + e := bson.E{} + for k, v := range cmd { + e.Key = k + e.Value = v + } + commands = append(commands, e) + } ctx := context.TODO() var bm bson.M err := cli.Database(commandForm.Database).RunCommand( ctx, - commandForm.Command, + commands, ).Decode(&bm) biz.ErrIsNilAppendErr(err, "执行命令失败: %s") diff --git a/server/internal/mongo/router/mongo.go b/server/internal/mongo/router/mongo.go index 909d2617..102a1ae2 100644 --- a/server/internal/mongo/router/mongo.go +++ b/server/internal/mongo/router/mongo.go @@ -17,6 +17,8 @@ func InitMongoRouter(router *gin.RouterGroup) { TagApp: tagapp.GetTagTreeApp(), } + saveDataPerm := req.NewPermission("mongo:data:save") + reqs := [...]*req.Conf{ // 获取所有mongo列表 req.NewGet("", ma.Mongos), @@ -39,13 +41,13 @@ func InitMongoRouter(router *gin.RouterGroup) { // 执行mongo find命令 req.NewPost(":id/command/find", ma.FindCommand), - req.NewPost(":id/command/update-by-id", ma.UpdateByIdCommand).Log(req.NewLogSave("mongo-更新文档")), + req.NewPost(":id/command/update-by-id", ma.UpdateByIdCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSave("mongo-更新文档")), // 执行mongo delete by id命令 - req.NewPost(":id/command/delete-by-id", ma.DeleteByIdCommand).Log(req.NewLogSave("mongo-删除文档")), + req.NewPost(":id/command/delete-by-id", ma.DeleteByIdCommand).RequiredPermission(req.NewPermission("mongo:data:del")).Log(req.NewLogSave("mongo-删除文档")), // 执行mongo insert 命令 - req.NewPost(":id/command/insert", ma.InsertOneCommand).Log(req.NewLogSave("mogno-插入文档")), + req.NewPost(":id/command/insert", ma.InsertOneCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSave("mogno-插入文档")), } req.BatchSetGroup(m, reqs[:])