/* * @Author: liling * @Date: 2022-06-23 * @LastEditors: liling * @LastEditTime: 2022-06-23 * @FilePath: \SCD\controllers\AttachmentController.go * @Description:附件管理相关对外API * * Copyright (c) 2022 by liling/jujutong, All Rights Reserved. */ package controllers import ( "crypto/md5" "fmt" "io" "io/fs" "io/ioutil" "log" "os" "os/exec" "path" "path/filepath" "scd_check_tools/global" "scd_check_tools/logger" "scd_check_tools/models/bo" "scd_check_tools/tools" "strconv" "strings" "time" "github.com/astaxie/beego/orm" ) //文件/附件服务 type AttachmentController struct { BaseController } func init() { } // @Summary 上传文件 // @Description 上传文件 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param file formData file true "文件流" // @Param station_id formData int true "变电站ID" // @Param is_checkin formData int false "是否是管控文档" // @Param data_type formData string false "业务类型。值为:a_scd|ccd|icd|cid|空。" // @Param attachment_type formData string false "文件类别。值关联系统字典file_types或者为空。" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /uploadfile [post] func (c *AttachmentController) UploadFile() { _, fh, err := c.GetFile("file") if err != nil { logger.Logger.Error(err) //c.Ctx.WriteString("上传失败:" + err.Error()) c.Data["json"] = c.ResultError("上传失败:" + err.Error()) c.ServeJSON() return } station_id, _ := c.GetInt32("station_id") if station_id == 0 { c.Data["json"] = c.ResultError("无效的变电站编号") c.ServeJSON() return } PthSep := string(os.PathSeparator) is_checkin, _ := c.GetInt("is_checkin") //是否是管控文档 datatype := strings.ToLower(c.GetString("data_type")) //业务数据类型 attachment_type := c.GetString("attachment_type") //附件文件类型 fileext := path.Ext(fh.Filename) file_name := fh.Filename logger.Logger.Debug("上传文件,业务数据类型:" + datatype + " file:" + fh.Filename + " fileext:" + fileext) if tools.HasChineseChar(file_name) { isgbk := tools.IsGBK([]byte(file_name)) isutf8 := tools.IsUTF8([]byte(file_name)) logger.Logger.Debug(fmt.Sprintf("文件名称编码 ISGBK:%+v ISUTF8:%+v", isgbk, tools.IsUTF8([]byte(file_name)))) if isgbk && !isutf8 { file_name = tools.ConvertByte2String([]byte(file_name), "UTF-8") logger.Logger.Debug("文件名称转换结果:" + file_name) } } newFilename := fmt.Sprintf("%d_%d%s", station_id, time.Now().Unix(), fileext) file := strings.Join([]string{".", "static", "upload", newFilename}, PthSep) os.MkdirAll(strings.Join([]string{".", "static", "upload"}, PthSep), fs.ModeDir) err = c.SaveToFile("file", file) doc_id := int32(0) if err != nil { logger.Logger.Error(err) c.Data["json"] = c.ResultError(err.Error()) } else { if is_checkin == 1 { file_type_id := "" if datatype != "" { file_type_row, _ := global.GoCahce.Get("global_code_file_typesfile_" + strings.ToLower(datatype)) file_type_id = tools.IsEmpty(file_type_row.(orm.Params)["id"]) } //清理指定变电站已上传但不使用的指定类型文件 new(bo.AttachmentMgr).ClearDityFile(station_id, file_type_id) filelist := map[string]string{} //文件列表。主要是zip时用于存放压缩包内的文件名 if datatype == "a_scd" { datatype = "scd" } if fileext == ".zip" && datatype == "scd" { //解压包 unzipDir := strings.Join([]string{".", "static", "upload", strings.Replace(fh.Filename, ".zip", "", 1)}, PthSep) saveTargetDir := strings.Join([]string{".", "static", "download", datatype}, PthSep) os.MkdirAll(saveTargetDir, fs.ModePerm) err = tools.Unzip(file, unzipDir) if err != nil { log.Println(err) c.Data["json"] = c.ResultError("压缩包解压失败:" + err.Error()) c.ServeJSON() return } dir, err2 := ioutil.ReadDir(unzipDir) if err2 != nil { log.Println(err2) c.Data["json"] = c.ResultError("压缩包解压失败:" + err2.Error()) c.ServeJSON() return } suffix := "." + datatype //忽略后缀匹配的大小写 findscd := "" ind := int64(0) for _, fi := range dir { if fi.IsDir() { // 忽略目录 continue } file_name = fi.Name() isgbk := tools.IsGBK([]byte(file_name)) isutf8 := tools.IsUTF8([]byte(file_name)) if isgbk && !isutf8 { logger.Logger.Debug(fmt.Sprintf("文件名称:%s 编码 ISGBK:%+v ISUTF8:%+v", file_name, isgbk, tools.IsUTF8([]byte(file_name)))) file_name = tools.ConvertByte2String([]byte(file_name), "UTF-8") logger.Logger.Debug("文件名称转换结果:" + file_name) } if strings.HasSuffix(strings.ToLower(file_name), suffix) { //匹配文件 findscd = unzipDir + PthSep + file_name scdFileName := saveTargetDir + PthSep + fmt.Sprintf("%d_%d%s", station_id, time.Now().Unix()+ind, suffix) os.Rename(findscd, scdFileName) filelist[file_name] = scdFileName ind = ind + 1 } } if len(filelist) == 0 { c.Data["json"] = c.ResultError("压缩包内没有发现" + datatype + "文件!") c.ServeJSON() return } os.RemoveAll(unzipDir) os.RemoveAll(file) } else if fileext == ".zip" && (datatype == "icd" || datatype == "cid" || datatype == "ccd") { has := md5.Sum([]byte(fh.Filename)) randmdirname := fmt.Sprintf("%x", has) //解压包 unzipDir := strings.Join([]string{".", "static", "upload", strings.Replace(fh.Filename, ".zip", "", 1)}, PthSep) saveTargetDir := strings.Join([]string{".", "static", "download", datatype, randmdirname}, PthSep) os.MkdirAll(saveTargetDir, fs.ModePerm) err = tools.Unzip(file, unzipDir) if err != nil { log.Println(err) c.Data["json"] = c.ResultError("压缩包解压失败:" + err.Error()) c.ServeJSON() return } dir, err2 := ioutil.ReadDir(unzipDir) if err2 != nil { log.Println(err2) c.Data["json"] = c.ResultError("压缩包解压失败:" + err2.Error()) c.ServeJSON() return } suffix := "." + datatype //忽略后缀匹配的大小写 findscd := "" for _, fi := range dir { if fi.IsDir() { // 忽略目录 continue } tmp_file_name := fi.Name() if strings.HasSuffix(strings.ToLower(tmp_file_name), suffix) { //匹配文件 findscd = unzipDir + PthSep + tmp_file_name scdFileName := saveTargetDir + PthSep + tmp_file_name os.Rename(findscd, scdFileName) filelist[tmp_file_name] = scdFileName } } if len(filelist) == 0 { c.Data["json"] = c.ResultError("压缩包内没有发现" + datatype + "文件!") c.ServeJSON() return } os.RemoveAll(unzipDir) filelist[file_name] = file //保存原始文件记录 } else { filelist[file_name] = file } attachmentMgrObj := new(bo.AttachmentMgr) attachmentMgrObj.SetUserInfo(c.GetCurrentUserInfo()) attachmentMgrObj.Model.StationId = station_id for fn, fpath := range filelist { f, _ := os.Stat(fpath) size := tools.FormatFileSize(f.Size()) attachmentMgrObj.Model.ScdId = int64(-1) //临时设置为-1。scd解析完成后,需要更新附件表中该站scdid为-1的值为实际的scdid attachmentMgrObj.Model.FileName = fn attachmentMgrObj.Model.SavePath = strings.TrimLeft(fpath, ".") attachmentMgrObj.Model.FileSize = size attachmentMgrObj.Model.CheckFlag = 1 attachmentMgrObj.Model.FileSuffix = strings.ToLower(path.Ext(fpath)[1:]) if file_type_id != "" { attachmentMgrObj.Model.FileType = file_type_id } else { attachmentMgrObj.Model.FileType = "" } attachmentMgrObj.Model.IedName = "" if datatype != "scd" { attachmentMgrObj.Model.IedName = strings.Split(fn, ".")[0] } doc_id, _ = attachmentMgrObj.Save() } } else if attachment_type != "" { //写附件表 //非需要解压的文件处理 f, ferr := os.Stat(file) if ferr != nil { c.Data["json"] = c.ResultError(ferr.Error()) c.ServeJSON() return } ied_name := c.GetString("ied_name") size := tools.FormatFileSize(f.Size()) attachmentMgrObj := new(bo.AttachmentMgr) attachmentMgrObj.SetUserInfo(c.GetCurrentUserInfo()) attachmentMgrObj.Model.StationId = station_id attachmentMgrObj.Model.FileName = file_name attachmentMgrObj.Model.SavePath = strings.TrimLeft(file, ".") attachmentMgrObj.Model.FileSize = size attachmentMgrObj.Model.FileSuffix = strings.ToLower(fileext[1:]) attachmentMgrObj.Model.FileType = attachment_type attachmentMgrObj.Model.IedName = ied_name attachmentMgrObj.Model.CheckFlag = 0 doc_id, _ = attachmentMgrObj.Save() } f, _ := exec.LookPath(os.Args[0]) fp, _ := filepath.Abs(f) i := strings.LastIndex(fp, string(os.PathSeparator)) res := string(fp[0:i]) if res != "" { res = file[1:] } logger.Logger.Debug(fmt.Sprintf("fileid=====%d", doc_id)) returninfo := map[string]string{"filename": file_name, "path": res, "fileid": tools.IsEmpty(doc_id)} c.Data["json"] = c.ResultOK(returninfo, 0) } c.ServeJSON() } // @Summary 根据id获取附件信息 // @Description 根据id获取附件信息 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param id query int true "附件ID" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/one [get] func (c *AttachmentController) AttachmentRecord() { obj := new(bo.AttachmentMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) id, _ := c.GetInt32("Id", 0) t := new(bo.T_sys_attachment) t.Id = id obj.Model = *t tableData, err := obj.One() if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(tableData, 1) } c.ServeJSON() } // @Summary 查询附件列表 // @Description 查询附件列表 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param pageindex query int true "当前查询的分页页码,默认为1" // @Param pagesize query int true "每页显示的数据条数,默认为20" // @Param station_id query int true "变电站ID" // @Param scd_id query int false "SCD文件ID" // @Param is_checkin query int false "是否管控文档。0 其他文件 1 管控文件 2 裁剪文件" // @Param name query string false "附件名称" // @Param ied_name query string false "装置名称" // @Param type query int false "文件类别ID" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/list [get] func (c *AttachmentController) AttachmentList() { obj := new(bo.AttachmentMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) station_id, _ := c.GetInt32("station_id") if station_id == 0 { c.Data["json"] = c.ResultError("变电站编号不能为空!") c.ServeJSON() return } scd_id := c.GetString("scd_id") is_checkin := c.GetString("is_checkin") //文件类型。0 其他文件 1 管控文件 2 裁剪文件(仅支持ccd\icd\cid) filename := c.GetString("name") ied_name := c.GetString("ied_name") filetype := c.GetString("type") pageno, _ := c.GetInt32("pageno", 1) pagesize, _ := c.GetInt32("pagesize", 20) tableData, n, err := obj.List(scd_id, station_id, ied_name, filename, filetype, is_checkin, pageno, pagesize) if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(tableData, n) } c.ServeJSON() } // @Summary 下载附件 // @Description 查询附件列表 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param ids formData string true "需要下载的附件id。多个附件id使用逗号分隔。" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/download [post] func (c *AttachmentController) AttachmentMutlDownload() { ids := c.GetString("ids") if ids == "" { c.Data["json"] = c.ResultError("下载文件不能为空!") c.ServeJSON() return } dirChar := string(os.PathSeparator) f, _ := exec.LookPath(os.Args[0]) fp, _ := filepath.Abs(f) i := strings.LastIndex(fp, string(os.PathSeparator)) fp = string(fp[0:i]) var err error fpathlist := []string{} obj := new(bo.AttachmentMgr) zipFileName := dirChar + "static" + dirChar + "download" + dirChar + tools.IsEmpty(time.Now().Unix()) //fmt.Println("zipFileName:" + zipFileName) err = os.MkdirAll(fp+zipFileName, fs.ModePerm) if err != nil { log.Println(err) c.Data["json"] = c.ResultError(err.Error()) c.ServeJSON() return } defer func() { _, ferr := os.Stat(fp + zipFileName) if ferr != nil && os.IsNotExist(ferr) { return } os.RemoveAll(fp + zipFileName) }() for _, idstr := range strings.Split(ids, ",") { id, _ := strconv.Atoi(idstr) obj.Model = bo.T_sys_attachment{Id: int32(id)} m, err2 := obj.One() if err2 != nil { c.Data["json"] = c.ResultError(err2.Error()) c.ServeJSON() return } savep := strings.ReplaceAll(m.SavePath, "/", dirChar) if savep[0:1] == "." { savep = savep[1:] } //fmt.Println("sourceFile:" + fp + savep) source, err1 := os.Open(fp + savep) if err1 != nil { c.Data["json"] = c.ResultError(err1.Error()) c.ServeJSON() return } newFile := fp + zipFileName + dirChar + m.FileName //fmt.Println("newFile:" + newFile) destination, err2 := os.Create(newFile) if err2 != nil { c.Data["json"] = c.ResultError(err2.Error()) c.ServeJSON() return } io.Copy(destination, source) source.Close() destination.Close() fpathlist = append(fpathlist, newFile) } zipFileNameA := zipFileName + ".zip" err = tools.ZipMutile(fpathlist, fp+zipFileNameA) if err != nil { c.Data["json"] = c.ResultError(err.Error()) c.ServeJSON() return } //fmt.Println("zipfile:" + zipFileNameA) c.Data["json"] = c.ResultOK(zipFileNameA, 0) c.ServeJSON() } // @Summary 删除附件 // @Description 删除指定的附件 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param id formData int true "需要删除的附件id。" // @Param ids formData string true "需要删除的附件id。多个附件id使用逗号分隔。" // @Param scd_id formData int false "SCD文件ID。不为空时表示删除该SCD关联的文件" // @Param check_flag formData int false "是否是管控文件。1表示是,否则表示为否" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/delete [post] func (c *AttachmentController) AttachmentDel() { obj := new(bo.AttachmentMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) id, _ := c.GetInt("id", 0) checkFlag := c.GetString("check_flag") scdId := c.GetString("scd_id") checkFlagInt := 0 if checkFlag != "" { checkFlagInt, _ = strconv.Atoi(checkFlag) } var err error if id == 0 { //判断是否批量删除 ids := c.GetString("ids") if ids == "" { c.Data["json"] = c.ResultError("删除文件的编号不能为空!") c.ServeJSON() return } for _, idstr := range strings.Split(ids, ",") { id, _ = strconv.Atoi(idstr) obj.Model = bo.T_sys_attachment{Id: int32(id)} obj.Model.CheckFlag = checkFlagInt err = obj.Delete(true) if err != nil { break } } } else { obj.Model = bo.T_sys_attachment{Id: int32(id)} obj.Model.CheckFlag = checkFlagInt if scdId != "" { obj.Model.ScdId, _ = strconv.ParseInt(scdId, 10, 64) obj.Model.FileSuffix = "scd" } err = obj.Delete(true) } if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(nil, 0) } c.ServeJSON() } // @Summary 查询指定附件下载历史记录 // @Description 查询指定附件下载历史记录。(预留) // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param id query int true "附件id。" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/download/rec [post] func (c *AttachmentController) AttachmentDownloadRec() { obj := new(bo.AttachmentMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) id, _ := c.GetInt("id", 0) err := obj.DownloadRec(id, c.GetString("desc")) if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK("", 0) } c.ServeJSON() } // @Summary 获取指定的数据及附件自动清理配置 // @Description 获取指定的数据及附件自动清理配置 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param id query int true "配置记录id。" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/cfg/one [get] func (c *AttachmentController) AttachmentCfgRecord() { obj := new(bo.AttachmentClearMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) id, _ := c.GetInt("Id", 0) t := new(bo.T_data_attachment) t.Id = id obj.Model = *t tableData, err := obj.One() if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(tableData, 1) } c.ServeJSON() } // @Summary 获取数据及附件自动清理配置列表 // @Description 获取数据及附件自动清理配置列表 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/cfg/list [get] func (c *AttachmentController) AttachmentCfgList() { obj := new(bo.AttachmentClearMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) tableData, n, err := obj.List() if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(tableData, n) } c.ServeJSON() } // @Summary 保存数据及附件自动清理配置 // @Description 保存数据及附件自动清理配置 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param id formData int true "配置记录id。不为0时表示编辑保存" // @Param name formData string true "配置名称" // @Param skeepday formData int true "数据及附件保留的天数" // @Param tablename formData string true "数据模型名称" // @Param colname formData string true "数据模型中的记录创建日期列名" // @Param filterwhere formData string false "数据过滤条件。支持SQL语法" // @Param memo formData string false "配置说明" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/cfg/save [post] func (c *AttachmentController) SaveAttachmentCfg() { param_name := tools.FilterSpecialChar(c.GetString("name")) skeepday, _ := c.GetInt("skeepday", 0) tablename := tools.FilterSpecialChar(c.GetString("tablename")) colname := tools.FilterSpecialChar(c.GetString("colname")) filterwhere := tools.FilterSpecialChar(c.GetString("filterwhere"), "'") memo := c.GetString("memo") id, _ := c.GetInt("id", 0) t := bo.T_data_attachment{Id: id, Name: param_name, Skeepday: skeepday, Tablename: tablename, Colname: colname, Filterwhere: filterwhere, Memo: memo} obj := new(bo.AttachmentClearMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) obj.Model = t err := obj.Save() if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK("", 0) } c.ServeJSON() } // @Summary 删除指定的数据及附件自动清理配置 // @Description 删除指定的数据及附件自动清理配置 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param id formData int true "配置记录id" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/cfg/delete [post] func (c *AttachmentController) AttachmentCfgDel() { obj := new(bo.AttachmentClearMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) id, _ := c.GetInt("id", 0) obj.Model = bo.T_data_attachment{Id: id} err := obj.Delete() if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(nil, 0) } c.ServeJSON() } // @Summary 重新解析指定的SCD文件 // @Description 重新解析指定的SCD文件 // @Tags 文件/附件服务接口 // @Accept x-www-form-urlencoded // @Produce json // @Param station_id formData int true "变电站ID" // @Param scd_name formData string true "SCD文件名称" // @Param scd_path formData string true "SCD附件路径" // @Success 200 {object} ResultOK 成功 // @Failure 500 {object} ResultError 失败 // @router /attachment/scdparse/start [post] func (c *AttachmentController) TmpParseScd() { //统计当前正在解析中的scd数量 //需要对同时解析的scd数量进行限制,否则可能会造成大量内存占用使服务器崩溃 scdMgr := new(bo.ScdMgr) if h, msg := scdMgr.CheckParseMaxLimit(); h { c.Data["json"] = c.ResultError("系统繁忙:" + msg + ",请稍候(约5分钟)再试") c.ServeJSON() return } obj := new(bo.AttachmentMgr) obj.SetUserInfo(c.GetCurrentUserInfo()) stationid, _ := c.GetInt("station_id", 0) err := obj.StartScdTempParse(stationid, c.GetString("scd_name"), c.GetString("scd_path")) if err != nil { c.Data["json"] = c.ResultError(err.Error()) } else { c.Data["json"] = c.ResultOK(nil, 0) } c.ServeJSON() }