From 615ab2eb2e3e6ae1f8c2c6a5077e8e89aa9ef866 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 14 Dec 2023 21:01:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E8=A7=92=E8=89=B2=E5=88=9D=E5=A7=8B/=E7=BD=91=E5=85=83?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- restagent/etc/restconf.yaml | 2 +- .../excel/user_import_template_en.xlsx | Bin 9429 -> 9513 bytes .../excel/user_import_template_zh.xlsx | Bin 9465 -> 9581 bytes .../config/config/config.default.yaml | 10 +- src/framework/utils/ssh/files.go | 104 ++++++++++++++++++ src/framework/utils/ssh/scp.go | 25 ++--- src/modules/common/common.go | 8 +- src/modules/common/controller/common.go | 47 -------- src/modules/common/controller/file.go | 43 ++++++++ .../network_element/controller/ne_action.go | 90 +++++++++++++++ .../network_element/network_element.go | 9 +- src/modules/system/controller/sys_user.go | 49 ++++++--- src/modules/trace/controller/tcpdump.go | 2 +- 13 files changed, 297 insertions(+), 92 deletions(-) create mode 100644 src/framework/utils/ssh/files.go diff --git a/restagent/etc/restconf.yaml b/restagent/etc/restconf.yaml index 167382c..a1c4c01 100644 --- a/restagent/etc/restconf.yaml +++ b/restagent/etc/restconf.yaml @@ -58,7 +58,7 @@ redis: # UDM网元用户库 udmuser: port: 6379 # Redis port - host: "192.168.1.187" + host: "192.168.5.57" password: "" db: 0 # Redis db_num # 多个数据源时可以用这个指定默认的数据源 diff --git a/src/assets/template/excel/user_import_template_en.xlsx b/src/assets/template/excel/user_import_template_en.xlsx index 5acb1c4ac80b110bad6757e387730f2bcccfabc6..90f88c41b31382e1247416c54d7cede5c6b334d9 100644 GIT binary patch delta 2067 zcmV+u2<-RONvTS(lK};U#FBMGlb8V>e_u<(Fcih#1;0bddz+-){1e&^RQ9AGI8l5H z$=z1gq#=oR`}U^oRy!0QbI-ZId+sIqVz27x3#_XeT_Bne6oD3vtn{Wpud8KzgCgg- zmb`8>6zBjBEy~M_To@)A3(vMOV0{HH3MB2AFa_FrZy3f-Y(a6C1cwgdy0MD;fAF*$ zY`EC*4d9fJE3CkC$vwv%Fg7C*9g$LuNb_my9v}sV8Wd>nFij}_@p`bz{n^kf6Q`>D z!GxWTai?r4261%nT{RlpwoTfsj}h`y{QmU#(ytm}n9!iL-@Iydp7nRhhJrdy|DTw3L`qlWmd!FYn@@-*K-uR${|)6gP)h>@lOQ4*vlauD0e>&N zV<@-;001!%000;O004MwFL!TpYjbF2Wpr~db7*B{bTKY?ZEW3FOOM+&5WW}ae-L!& z$(AhH>sM_DvAtd|kPU)1P0zGM+rmr>9drfD)~ihaZk8sY#XV;cF0!)G)x zSr}pDN+X|`cBxb~edGnY^3aU-@j=I9&%u4_%zPYh3kp z$F>ghQsa?_W0w1`aWwiI6MyV*>;hGEO;BJ&>8VgWhN+V9Efs`EF zXL609-5gq_O8uZ#6)Tz9Qu+VsvIEJU2a)FDF`5zHqvT7^<kmbjFDn;CpzE2?#}~u#iIuc!gih_XN{*%}3`7hVp1x!kF<30)I3Iu_IHKESI`a z#(+i|!x$@LyJXlaYnZ>qx|~?Um{?m4qq?_wB{7FFHLn=;+PSg$>+ce6DyB$YiVf3g zMKso+nE)pw$ttSUA%7BG&uTV+^^TC=1qGOavH}$u%+5QdmN^GrJBdbDX+Y&N4QpMa zx2<8XYxI={lrQs9Ue`F>)~Kv&47N26)-{f{HLB|xL(s?~sIAM6^Rjv-Ga->#Hlf}u zO<_Gtp_py!z^=@7BQuvt?kWxV%`~iyEcbS(Y^1rbRBEy@h=1A*cGzqr{%D8FM&?DE zTZOesx&p3RouZTr#Cn8?2ctN+G>6UtuoPTc7is(=gnq>FIlqU437Ski{Fp6lDpTuI~p~pqXTVDOReE!4DH(xKF z{seabA!+MxUw^-O``e4flW#5Sk6)iJUtKP*o-Qw+Eni(PzIp?tQZ4<^hOos!KO@NM zSdi=Nqh+qc`A6_Zn8z5y{oq{yESBvI;?33Xi|g+)eEo8>pB`Cxpn04LV!KlY+GHOe z4E$=*sssED;9a}f-2ogr!K%O+Rtq+I_OT~$;xYaomw)%>@#XT__lws*FRouM9{-XR z6?S~CmYDF5_6dJPTU-UbKFX@St1mfCqPRQs{dOjs88T z-L2?d?$v~>svT`mwr4kc|69tIn}Z$L6gwqdHO1G0mS^&Z$lh}E=r57p7mos_WMip56tmQuJxy!9iX)D9%BHMtf9hqw&C#CWIsY z1Vs}Mt`P)};TBOc#C<+eZzA=}!;@^OLtMBg=BC&keW5qk7;YkQ=I&cP;VE~^i=MbHte2f=?Lj==e z2SBbDYbhUpgfrH2)`a9KdpDdVZ@&^e{{pShYDM~l5MOpmkXB+Of3!9ZN?zOMbB^z` zuP>WlyPT;`?gL_38m&jTmH0@85|(sqehdBNF*_JcBnO_@yk%8RN_mo$6Fds!3_s+B zPqp>9`AOK#;%bq?zoa7nq5c6-O9KQH000080EbSqKpUb30d$k&AV2}!lO!QC0stM8 zSs{`Igv63{LzD0!Q~~#sMIt~4PXqt}cx*3|JtQ2Hs3HymC?1ozA~ga}9+U4PEE^~D zkE!$k00064000yK0000000031AOHXWVv{x_K>@v!cq2Liij%V=HUY7d>LWk_h?6TM xJOOZ%Xe1#UFT7(YxC8(IF%SR%7ytkO000000RSKX005kmq$Dc_3?Tpj005qY#%llo delta 1992 zcmV;(2RHbsO4UiQlK}+~3`%Z9lb8V>e^E=rFcgK~1^+|Idz+-4Izrol%AOnuP88ol za<`Q=X-J~o{=I3t)egnS+;i@C&%GpH98?{Bfpt}*3q%uwBG96dmEIQUb-j$|C~}@_ z$?HZ#fsWwNqP)J!g<+zx@N63c)>q)7K+=v0Q=pyqhGFc)4itAuaOfaz8mqVue^0x` zhKoJl0!|6J#R@!^+;iLkV>1%b2`R;hG@rKa0a9S7L4o!T(}dz5uLrB#pAEe-ajME6 zP1xxacgmJx5J&gHRim+O+oa9<7$HB!@2jVme$}|r-K_+mGM9o03*0xh3^&;4cv9$& zS99lA;m(^1@68*-nOx9s6@y+mb7(ln-NoZhU-9ZNGt z$VWH#qQ6Tv6x3<@|HLe&G^TfJGG~Oc*=#~Q1Ij-4mz0kii4vjKk$8l$Uq1ONa8 z4*&od0001ZY%h0ja%*#FWo2}8FLP*RWpptvcx`OeS5I%-HWa@P*mn?k*eQ`L+ws5I zE^0eY3N%Hrb={ekXj=#+Dk5biw_XY~D2ff(av0F-2JEu#v|_>je3{v3zQrC%Nw#b+ zGX``;E~ZKT-sAhd_xOJv)xLM`Q)5OV?y;bQY_o(65;)BDf=LH`@npE)Kn52$a4}^8 z>7Wb3(Y@}+AGH$}o$@Im!T=2c@1Us=Vau|(GbKLeCJRXbJ7X64Sim%zSUijfc9oD% zt#YYUvwZ9YNNZ?CyXau!vFDHhb7G$aLMw>~#RBZ-Q!nJ1wsU`XCw*>&6X2TJvtr^v z@8tUI>V|#3=R}ONu`nIxTiUcumQAZ!;ORRXinch*$I)pV?t|hG$c;Sei3>$HGl+<# zo+L@kB;=+Oq*kmr$gW#N^kXiVKfnT`Zd*m*ab$Jdt_NO}p*JEj?x6d2>$r?yT_K)& zB;hOn4S`3W6H0#^L0m``WXKd4F?K3}4`8Vzbt{0>h#le0NS{#JL7>&(XX*@nfR&%n zZ7=_4rwvuoJdO;PjB!jwpV2QpS4=yoiL$lNSaM9f$y9(T74rZYEEd!Y$U`zC6gHKG zt81X7gX)r{Nvo24YGW#zYI3 zOJ;c~4ePf^S5j#hQ)#BMj$J)VDJ>cJ?Ris7(00Fz}#6t@g<5ki3w z-SfS*O!Z{7mLf6i`Uol zuik$`Fm$;+EQ8sy3C9%MT^n4JPuf;EnTWK9Ws>?hS})G*}S5e5VLP_O3)H<0Xu&&F-CGO7@2EprUt? z9^AYw^mN!fjz|BU)b=|!A|fFK9+dybt=oUEl=FLcbM@!^`iDGk2bI=fOWqbPUwRG8 z-3rvAe6r)6{P6T^`*s89uMzK+TfHsBp~)<(PR`Ya29Zzh8gX53*XZ}lt^RwZ{{d_9 zu+kcC;ihYrDpQ88Q+viEwya<~6y>!x1W)r2I^zkBCLVNOO2*K9OQw96M*6+4CxRx0 zs-=#YfKMC!kb}=l0Y$-umw25`QjwV_p0@6i;mq?~v-$th$GDjrZ(0Q%$!p@p8p= zU6#vCtrvKf(iixyTytC+oX`5ti7Jn-9;KnG9+004MwFO!iZ9FzPa4g%C2lMEv@0{b14 zYaSevUn3A4<*AUE;s5{u#{mEU6aWAK000000RSKX001BwlYSZ&lT;)Slgkef3jhEB z0096X0001clguMJ0Z5YvBsKwWlRhLs0W_0;Bs>9ele{D$8w?twt91kb00a*J02lxO a000000096X0001;llUYn2Dl&q0000fijSxO diff --git a/src/assets/template/excel/user_import_template_zh.xlsx b/src/assets/template/excel/user_import_template_zh.xlsx index 4f4713b253243291cd81705cf73fc989333f218a..0f034a35ceaf406b17c02068f21c7553bc475ad9 100644 GIT binary patch delta 2117 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 OlRPCW2HYS300002X{HhY diff --git a/src/framework/config/config/config.default.yaml b/src/framework/config/config/config.default.yaml index 0515333..ee82bb5 100644 --- a/src/framework/config/config/config.default.yaml +++ b/src/framework/config/config/config.default.yaml @@ -1,19 +1,19 @@ # 项目信息 framework: - name: "ems_agt" - version: "0.0.1" + name: "CN EMS" + version: "2.2312.9" # 应用服务配置 server: # 服务端口 - port: 3040 + port: 3030 # 是否开启代理 proxy: false # 日志 logger: - fileDir: "/usr/local/omc/log" - fileName: "ems_agt.log" + fileDir: "/var/log" + fileName: "omc.log" level: 2 # 日志记录的等级 0:silent<1:info<2:warn<3:error maxDay: 180 # 日志会保留 180 天 maxSize: 10 # 调整按 10MB 大小的切割 diff --git a/src/framework/utils/ssh/files.go b/src/framework/utils/ssh/files.go new file mode 100644 index 0000000..23604a8 --- /dev/null +++ b/src/framework/utils/ssh/files.go @@ -0,0 +1,104 @@ +package ssh + +import ( + "fmt" + "strings" + + "ems.agt/src/framework/cmd" + "ems.agt/src/framework/config" + "ems.agt/src/framework/logger" + "ems.agt/src/framework/utils/parse" +) + +// FileListRow 文件列表行数据 +type FileListRow struct { + FileType string `json:"fileType"` // 文件类型 + FileMode string `json:"fileMode"` // 文件的权限 + LinkCount int64 `json:"linkCount"` // 硬链接数目 + Owner string `json:"owner"` // 所属用户 + Group string `json:"group"` // 所属组 + Size string `json:"size"` // 文件的大小 + ModifiedTime int64 `json:"modifiedTime"` // 最后修改时间,单位为秒 + FileName string `json:"fileName"` // 文件的名称 +} + +// 文件列表 +// neIp 网元IP空字符串为本地 +// search 文件名后模糊* +// +// return 目录大小,行记录,异常 +func FileList(path, neIp, search string) (string, []FileListRow, error) { + totalSize := "" + var rows []FileListRow + rowStr := "" + + // 发送命令 + searchStr := "" + if search != "" { + searchStr = search + "*" + } + pathStr := fmt.Sprintf("cd %s \n", path) + cmdStr := fmt.Sprintf("ls -lht --time-style=+%%s %s \n", searchStr) + + // 是否远程读取 + if neIp != "" { + usernameNe := config.Get("ne.user").(string) // 网元统一用户 + sshHost := fmt.Sprintf("%s@%s", usernameNe, neIp) + resultStr, err := cmd.ExecWithCheck("ssh", sshHost, pathStr, cmdStr) + if err != nil { + logger.Errorf("Ne FileList Path: %s, Search: %s, Error:%s", path, search, err.Error()) + return totalSize, rows, err + } + rowStr = resultStr + } else { + resultStr, err := cmd.Execf(pathStr, cmdStr) + if err != nil { + logger.Errorf("Ne FileList Path: %s, Search: %s, Error:%s", path, search, err.Error()) + return totalSize, rows, err + } + rowStr = resultStr + } + + // 遍历组装 + rowStrList := strings.Split(rowStr, "\n") + for i, rowStr := range rowStrList { + if rowStr == "" { + continue + } + // 使用空格对字符串进行切割 + fields := strings.Fields(rowStr) + + // 无查询过滤会有total总计 + if i == 0 && searchStr == "" { + totalSize = fields[1] + continue + } + + // 拆分不足7位跳过 + if len(fields) != 7 { + continue + } + + // 文件类型 + fileMode := fields[0] + fileType := "file" + if fileMode[0] == 'd' { + fileType = "dir" + } else if fileMode[0] == 'l' { + fileType = "symlink" + } + + // 提取各个字段的值 + rows = append(rows, FileListRow{ + FileMode: fileMode, + FileType: fileType, + LinkCount: parse.Number(fields[1]), + Owner: fields[2], + Group: fields[3], + Size: fields[4], + ModifiedTime: parse.Number(fields[5]), + FileName: fields[6], + }) + } + return totalSize, rows, nil +} diff --git a/src/framework/utils/ssh/scp.go b/src/framework/utils/ssh/scp.go index b303a9d..2a50d78 100644 --- a/src/framework/utils/ssh/scp.go +++ b/src/framework/utils/ssh/scp.go @@ -6,44 +6,39 @@ import ( "os/exec" "path/filepath" - "ems.agt/lib/core/conf" - "ems.agt/lib/log" + "ems.agt/src/framework/config" + "ems.agt/src/framework/logger" ) // 网元NE 文件复制到远程文件 func FileSCPLocalToNe(neIp, localPath, nePath string) error { - usernameNe := conf.Get("ne.user").(string) + usernameNe := config.Get("ne.user").(string) // scp /path/to/local/file.txt user@remote-server:/path/to/remote/directory/ neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath) cmd := exec.Command("scp", "-r", localPath, neDir) - out, err := cmd.CombinedOutput() + _, err := cmd.CombinedOutput() if err != nil { + logger.Errorf("FileSCPLocalToNe %s", err.Error()) return err } - log.Infof("FileSCPLocalToNe %s", string(out)) return nil } // 网元NE 远程文件复制到本地文件 func FileSCPNeToLocal(neIp, nePath, localPath string) error { - // 获取文件所在的目录路径 - dirPath := filepath.Dir(localPath) - // 确保文件夹路径存在 - err := os.MkdirAll(dirPath, os.ModePerm) - if err != nil { - log.Errorf("FileSCPNeToLocal MkdirAll err %v", err) + if err := os.MkdirAll(filepath.Dir(localPath), 0750); err != nil { + logger.Errorf("FileSCPNeToLocal MkdirAll err %v", err) return err } - - usernameNe := conf.Get("ne.user").(string) + usernameNe := config.Get("ne.user").(string) // scp user@remote-server:/path/to/remote/directory/ /path/to/local/file.txt neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath) cmd := exec.Command("scp", "-r", neDir, localPath) - out, err := cmd.CombinedOutput() + _, err := cmd.CombinedOutput() if err != nil { + logger.Errorf("FileSCPNeToLocal %s", err.Error()) return err } - log.Infof("FileSCPNeToLocal %s", string(out)) return nil } diff --git a/src/modules/common/common.go b/src/modules/common/common.go index 6890f58..419a7ec 100644 --- a/src/modules/common/common.go +++ b/src/modules/common/common.go @@ -3,7 +3,6 @@ package common import ( "ems.agt/src/framework/logger" "ems.agt/src/framework/middleware" - "ems.agt/src/framework/middleware/collectlogs" "ems.agt/src/modules/common/controller" "github.com/gin-gonic/gin" @@ -26,12 +25,6 @@ func Setup(router *gin.Engine) { // 系统可暴露的配置信息 indexGroup.GET("/sys-conf", controller.NewCommont.SysConfig) - // 转存上传文件到静态资源 - indexGroup.POST("/transferStaticFile", - middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.helpDoc", collectlogs.BUSINESS_TYPE_UPDATE)), - controller.NewCommont.TransferStaticFile, - ) // 验证码操作处理 indexGroup.GET("/captchaImage", @@ -92,5 +85,6 @@ func Setup(router *gin.Engine) { fileGroup.POST("/chunkCheck", middleware.PreAuthorize(nil), controller.NewFile.ChunkCheck) fileGroup.POST("/chunkUpload", middleware.PreAuthorize(nil), controller.NewFile.ChunkUpload) fileGroup.POST("/chunkMerge", middleware.PreAuthorize(nil), controller.NewFile.ChunkMerge) + fileGroup.POST("/transferStaticFile", middleware.PreAuthorize(nil), controller.NewCommont.TransferStaticFile) } } diff --git a/src/modules/common/controller/common.go b/src/modules/common/controller/common.go index a47d295..4ba3776 100644 --- a/src/modules/common/controller/common.go +++ b/src/modules/common/controller/common.go @@ -1,18 +1,11 @@ package controller import ( - "fmt" - "path/filepath" - "strings" - - "ems.agt/src/framework/config" "ems.agt/src/framework/i18n" "ems.agt/src/framework/utils/ctx" - "ems.agt/src/framework/utils/file" "ems.agt/src/framework/vo/result" commonService "ems.agt/src/modules/common/service" "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" ) // 实例化控制层 CommontController 结构体 @@ -76,43 +69,3 @@ func (s *CommontController) SysConfig(c *gin.Context) { c.JSON(200, result.OkData(data)) } - -// 转存指定对应文件 -// -// POST /transferStaticFile -func (s *CommontController) TransferStaticFile(c *gin.Context) { - language := ctx.AcceptLanguage(c) - var body struct { - UploadPath string `json:"uploadPath" binding:"required"` - StaticPath string `json:"staticPath" binding:"required"` - Language string `json:"language" binding:"required"` - } - if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { - c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - - // 取语言前缀 - lang := strings.SplitN(body.Language, "_", 2)[0] - - // 默认静态资源 - static := config.Get("staticFile.default").(map[string]any) - dir, err := filepath.Abs(static["dir"].(string)) - if err != nil { - c.JSON(400, result.CodeMsg(400, err.Error())) - return - } - - delPrefix := strings.Replace(body.StaticPath, static["prefix"].(string), "", 1) - staticPath := strings.Replace(delPrefix, "{language}", lang, 1) - newFile := fmt.Sprintf("%s%s", dir, staticPath) - - err = file.CopyUploadFile(body.UploadPath, newFile) - if err != nil { - c.JSON(400, result.CodeMsg(400, err.Error())) - return - } - - urlPath := strings.Replace(newFile, dir, static["prefix"].(string), 1) - c.JSON(200, result.OkData(filepath.ToSlash(urlPath))) -} diff --git a/src/modules/common/controller/file.go b/src/modules/common/controller/file.go index 352fee7..d925221 100644 --- a/src/modules/common/controller/file.go +++ b/src/modules/common/controller/file.go @@ -4,8 +4,10 @@ import ( "encoding/base64" "fmt" "net/url" + "path/filepath" "strings" + "ems.agt/src/framework/config" "ems.agt/src/framework/constants/uploadsubpath" "ems.agt/src/framework/i18n" "ems.agt/src/framework/utils/ctx" @@ -13,6 +15,7 @@ import ( "ems.agt/src/framework/vo/result" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" ) // 实例化控制层 FileController 结构体 @@ -190,3 +193,43 @@ func (s *FileController) ChunkUpload(c *gin.Context) { } c.JSON(206, result.OkData(chunkFilePath)) } + +// 转存指定对应文件到静态目录 +// +// POST /transferStaticFile +func (s *CommontController) TransferStaticFile(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + UploadPath string `json:"uploadPath" binding:"required"` + StaticPath string `json:"staticPath" binding:"required"` + Language string `json:"language" binding:"required"` + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 取语言前缀 + lang := strings.SplitN(body.Language, "_", 2)[0] + + // 默认静态资源 + static := config.Get("staticFile.default").(map[string]any) + dir, err := filepath.Abs(static["dir"].(string)) + if err != nil { + c.JSON(400, result.CodeMsg(400, err.Error())) + return + } + + delPrefix := strings.Replace(body.StaticPath, static["prefix"].(string), "", 1) + staticPath := strings.Replace(delPrefix, "{language}", lang, 1) + newFile := fmt.Sprintf("%s%s", dir, staticPath) + + err = file.CopyUploadFile(body.UploadPath, newFile) + if err != nil { + c.JSON(400, result.CodeMsg(400, err.Error())) + return + } + + urlPath := strings.Replace(newFile, dir, static["prefix"].(string), 1) + c.JSON(200, result.OkData(filepath.ToSlash(urlPath))) +} diff --git a/src/modules/network_element/controller/ne_action.go b/src/modules/network_element/controller/ne_action.go index f001b63..d56408a 100644 --- a/src/modules/network_element/controller/ne_action.go +++ b/src/modules/network_element/controller/ne_action.go @@ -66,3 +66,93 @@ func (s *NeActionController) PushFile(c *gin.Context) { neFilePath := fmt.Sprintf("%s/%s", nePath, fileName) c.JSON(200, result.OkData(filepath.ToSlash(neFilePath))) } + +// 从网元端获取文件 +// +// GET /pullFile +func (s *NeActionController) PullFile(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var querys struct { + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` + Path string `form:"path" binding:"required"` + FileName string `form:"fileName" binding:"required"` + } + if err := c.ShouldBindQuery(&querys); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元获取IP + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID) + if neInfo.NeId != querys.NeID || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + nePath := fmt.Sprintf("%s/%s", querys.Path, querys.FileName) + localPath := fmt.Sprintf("/tmp/omc/pullFile/%s", querys.FileName) + err := ssh.FileSCPNeToLocal(neInfo.IP, nePath, localPath) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.FileAttachment(localPath, querys.FileName) +} + +// 网元端文件列表 +// +// GET /files +func (s *NeActionController) Files(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var querys struct { + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` + Path string `form:"path" binding:"required"` + PageNum int64 `form:"pageNum" binding:"required"` + PageSize int64 `form:"pageSize" binding:"required"` + Search string `form:"search"` + } + if err := c.ShouldBindQuery(&querys); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元获取IP + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID) + if neInfo.NeId != querys.NeID || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + totalSize, rows, err := ssh.FileList(querys.Path, neInfo.IP, querys.Search) + if err != nil { + c.JSON(200, result.Ok(map[string]any{ + "path": querys.Path, + "totalSize": totalSize, + "total": len(rows), + "rows": []ssh.FileListRow{}, + })) + return + } + + // 对数组进行切片分页 + lenNum := int64(len(rows)) + start := (querys.PageNum - 1) * querys.PageSize + end := start + querys.PageSize + var splitRows []ssh.FileListRow + if start >= lenNum { + splitRows = []ssh.FileListRow{} + } else if end >= lenNum { + splitRows = rows[start:] + } else { + splitRows = rows[start:end] + } + + c.JSON(200, result.Ok(map[string]any{ + "path": querys.Path, + "totalSize": totalSize, + "total": lenNum, + "rows": splitRows, + })) +} diff --git a/src/modules/network_element/network_element.go b/src/modules/network_element/network_element.go index d156c50..866095e 100644 --- a/src/modules/network_element/network_element.go +++ b/src/modules/network_element/network_element.go @@ -25,7 +25,14 @@ func Setup(router *gin.Engine) { // 网元处理 neActionGroup := neGroup.Group("/action") { - // 发送文件到网元服务器 + neActionGroup.GET("/files", + middleware.PreAuthorize(nil), + controller.NewNeAction.Files, + ) + neActionGroup.GET("/pullFile", + middleware.PreAuthorize(nil), + controller.NewNeAction.PullFile, + ) neActionGroup.POST("/pushFile", middleware.PreAuthorize(nil), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neAction", collectlogs.BUSINESS_TYPE_IMPORT)), diff --git a/src/modules/system/controller/sys_user.go b/src/modules/system/controller/sys_user.go index 6bf77cd..63202a8 100644 --- a/src/modules/system/controller/sys_user.go +++ b/src/modules/system/controller/sys_user.go @@ -507,11 +507,12 @@ func (s *SysUserController) Export(c *gin.Context) { "E1": i18n.TKey(language, "user.export.phone"), "F1": i18n.TKey(language, "user.export.sex"), "G1": i18n.TKey(language, "user.export.status"), - "H1": i18n.TKey(language, "user.export.deptID"), - "I1": i18n.TKey(language, "user.export.deptName"), - "J1": i18n.TKey(language, "user.export.deptLeader"), - "K1": i18n.TKey(language, "user.export.loginIP"), - "L1": i18n.TKey(language, "user.export.loginDate"), + "H1": i18n.TKey(language, "user.export.role"), + "I1": i18n.TKey(language, "user.export.deptID"), + "J1": i18n.TKey(language, "user.export.deptName"), + "K1": i18n.TKey(language, "user.export.deptLeader"), + "L1": i18n.TKey(language, "user.export.loginIP"), + "M1": i18n.TKey(language, "user.export.loginDate"), } // 读取用户性别字典数据 dictSysUserSex := s.sysDictDataService.SelectDictDataByType("sys_user_sex") @@ -532,6 +533,13 @@ func (s *SysUserController) Export(c *gin.Context) { if row.Status == "1" { statusValue = i18n.TKey(language, "dictData.normal") } + // 用户角色, 默认导出首个 + userRole := "" + if len(row.Roles) > 0 { + roleID := row.Roles[0].RoleID + roleName := i18n.TKey(language, row.Roles[0].RoleName) + userRole = fmt.Sprintf("%s-%s", roleID, roleName) + } dataCells = append(dataCells, map[string]any{ "A" + idx: row.UserID, "B" + idx: row.UserName, @@ -540,11 +548,12 @@ func (s *SysUserController) Export(c *gin.Context) { "E" + idx: row.PhoneNumber, "F" + idx: sysUserSex, "G" + idx: statusValue, - "H" + idx: row.Dept.DeptID, - "I" + idx: row.Dept.DeptName, - "J" + idx: row.Dept.Leader, - "K" + idx: row.LoginIP, - "L" + idx: date.ParseDateToStr(row.LoginDate, date.YYYY_MM_DD_HH_MM_SS), + "H" + idx: userRole, + "I" + idx: row.Dept.DeptID, + "J" + idx: row.Dept.DeptName, + "K" + idx: row.Dept.Leader, + "L" + idx: row.LoginIP, + "M" + idx: date.ParseDateToStr(row.LoginDate, date.YYYY_MM_DD_HH_MM_SS), }) } @@ -655,22 +664,32 @@ func (s *SysUserController) ImportData(c *gin.Context) { break } } + // 用户状态 sysUserStatus := common.STATUS_NO - if row["G"] == "Normal" { + if row["G"] == "正常" || row["G"] == "Normal" { sysUserStatus = common.STATUS_YES } + // 用户角色 拿编号 + sysUserRole := "" + if v, ok := row["H"]; ok && v != "" { + sysUserRole = strings.SplitN(v, "-", 2)[0] + if sysUserRole == "1" { + sysUserRole = "" + } + } // 构建用户实体信息 newSysUser := model.SysUser{ UserType: "sys", Password: initPassword, - DeptID: row["H"], + DeptID: row["I"], UserName: row["B"], NickName: row["C"], PhoneNumber: row["E"], Email: row["D"], Status: sysUserStatus, Sex: sysUserSex, + RoleIDs: []string{sysUserRole}, } // 检查手机号码格式并判断是否唯一 @@ -725,7 +744,7 @@ func (s *SysUserController) ImportData(c *gin.Context) { successMsgArr = append(successMsgArr, msg) } else { // 用户编号:%s 登录名称 %s 导入失败 - msg := i18n.TTemplate(language, "user.import.fail", map[string]any{"id": row["A"], "email": newSysUser.UserName}) + msg := i18n.TTemplate(language, "user.import.fail", map[string]any{"id": row["A"], "name": newSysUser.UserName}) failureNum++ failureMsgArr = append(failureMsgArr, msg) } @@ -739,12 +758,12 @@ func (s *SysUserController) ImportData(c *gin.Context) { rows := s.sysUserService.UpdateUser(newSysUser) if rows > 0 { // 用户编号:%s 登录名称 %s 更新成功 - msg := i18n.TTemplate(language, "user.import.successUpdate", map[string]any{"id": row["A"], "email": newSysUser.UserName}) + msg := i18n.TTemplate(language, "user.import.successUpdate", map[string]any{"id": row["A"], "name": newSysUser.UserName}) successNum++ successMsgArr = append(successMsgArr, msg) } else { // 用户编号:%s 登录名称 %s 更新失败 - msg := i18n.TTemplate(language, "user.import.failUpdate", map[string]any{"id": row["A"], "email": newSysUser.UserName}) + msg := i18n.TTemplate(language, "user.import.failUpdate", map[string]any{"id": row["A"], "name": newSysUser.UserName}) failureNum++ failureMsgArr = append(failureMsgArr, msg) } diff --git a/src/modules/trace/controller/tcpdump.go b/src/modules/trace/controller/tcpdump.go index 6924de5..bb7f444 100644 --- a/src/modules/trace/controller/tcpdump.go +++ b/src/modules/trace/controller/tcpdump.go @@ -104,7 +104,7 @@ func (s *TcpdumpController) Download(c *gin.Context) { } nePath := fmt.Sprintf("/tmp/%s", body.FileName) - localPath := fmt.Sprintf("%s/tcpdump/%s", config.Get("ne.scpdir"), body.FileName) + localPath := fmt.Sprintf("/tmp/omc/tcpdump/%s", body.FileName) err = ssh.FileSCPNeToLocal(neInfo.IP, nePath, localPath) if err != nil { c.JSON(200, result.ErrMsg(err.Error()))