// ========================================================================== // GFast自动生成logic操作代码。 // 生成日期:2023-09-16 10:32:04 // 生成路径: internal/app/system/logic/document.go // 生成人:gfast // desc:母板 // company:云南奇讯科技有限公司 // ========================================================================== package logic import ( "context" "errors" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" "github.com/tiger1103/gfast/v3/api/v1/common/coryCommon" "github.com/tiger1103/gfast/v3/api/v1/system" "github.com/tiger1103/gfast/v3/internal/app/system/consts" "github.com/tiger1103/gfast/v3/internal/app/system/dao" "github.com/tiger1103/gfast/v3/internal/app/system/model" "github.com/tiger1103/gfast/v3/internal/app/system/model/do" "github.com/tiger1103/gfast/v3/internal/app/system/service" "github.com/tiger1103/gfast/v3/library/liberr" "net/url" "os" "path/filepath" "strconv" "strings" ) func init() { service.RegisterDocument(New()) } func New() *sDocument { return &sDocument{} } type sDocument struct{} func (s *sDocument) TemplateRecycleBinFunc(ctx context.Context, req *system.TemplateRecycleBinReq) (err error) { err = g.Try(ctx, func(ctx context.Context) { if req.Type == "1" { _, err = dao.Document.Ctx(ctx).Unscoped().Where(dao.DocumentData.Columns().Id+" in (?)", req.Ids).Update(g.Map{"deleted_at": nil}) } else { var pathArr []string //删除还需要删除下面的所有子数据 var idArr []int64 for i := range req.Ids { idList := RecursiveDeletion(ctx, req.Ids[i]) if idList != nil { idArr = append(idArr, idList...) } data, _ := dao.Document.Ctx(ctx).Unscoped().Where("id", req.Ids[i]).Fields("filen_path").Value() pathArr = append(pathArr, data.String()) } idArr = append(idArr, req.Ids...) _, err = dao.Document.Ctx(ctx).Unscoped().Delete(dao.Document.Columns().Id+" in (?)", idArr) //如果删除成功,那么删除文件/文件夹 for _, data := range pathArr { os.RemoveAll(coryCommon.FileToFunc(data, 2)) } } liberr.ErrIsNil(ctx, err, "删除失败") }) return } func RecursiveDeletion(ctx context.Context, id int64) (ids []int64) { //1、获取某个文件的字符串id_str value, _ := dao.Document.Ctx(ctx).Unscoped().Where("id", id).Fields("id_str").Value() if value.String() == "" { return nil } //2、查看当前需要删除的数据下面是否有子数据 var dlr []*model.DocumentListRes dao.Document.Ctx(ctx).Unscoped().Where("pid", value.String()).Fields("id,id_str").Scan(&dlr) for i := range dlr { ids = append(ids, dlr[i].Id) deletion := RecursiveDeletion(ctx, dlr[i].Id) ids = append(ids, deletion...) } return ids } func (s *sDocument) AllList(ctx context.Context, req *system.AllDocumentSearchReq) (res *system.AllDocumentSearchRes, err error) { err = g.Try(ctx, func(ctx context.Context) { res = new(system.AllDocumentSearchRes) var dataList []*model.DocumentInfoRes m := dao.Document.Ctx(ctx) if req.Type == "2" { m = m.Unscoped().WhereNotNull("deleted_at") } else if req.Type == "3" { m = m.Unscoped() } err = m.Scan(&dataList) res.List = dataList liberr.ErrIsNil(ctx, err, "获取数据失败!") }) return } func RecursiveQueryFunc(dr []*model.DocumentInfoRes, idStr string) (idStrs []string) { for _, data := range dr { if data.Pid == idStr { idStrs = append(idStrs, data.IdStr) strs := RecursiveQueryFunc(dr, data.IdStr) if len(strs) > 0 { idStrs = append(idStrs, strs...) } } } return } func (s *sDocument) TreeStructureFunc(ctx context.Context, req *system.TreeStructureReq) (res *system.TreeStructureRes, err error) { err = g.Try(ctx, func(ctx context.Context) { res = new(system.TreeStructureRes) var entity []*model.TreeStructureRes err = dao.Document.Ctx(ctx).Where("pid = '0'").WithAll().Scan(&entity) res.List = entity liberr.ErrIsNil(ctx, err, "获取数据失败!") }) return } func (s *sDocument) List(ctx context.Context, req *system.DocumentSearchReq) (listRes *system.DocumentSearchRes, err error) { listRes = new(system.DocumentSearchRes) err = g.Try(ctx, func(ctx context.Context) { m := dao.Document.Ctx(ctx).Where(dao.Document.Columns().Type, "1") //如果查询全部那么就先递归获取到所有的pid然后去查询,否则就只查询前端传递过来的数据 if req.Switch == "1" { var dr []*model.DocumentInfoRes err = dao.Document.Ctx(ctx).Where("type = 2").Scan(&dr) strs := RecursiveQueryFunc(dr, req.IdStr) strs = append(strs, req.IdStr) m = m.Where(dao.Document.Columns().Pid+" in (?)", strs) } else { m = m.Where(dao.Document.Columns().Pid, req.IdStr) } if req.Name != "" { m = m.Where(dao.Document.Columns().Name+" like ?", "%"+req.Name+"%") } if len(req.DateRange) != 0 { m = m.Where(dao.Document.Columns().CreatedAt+" >=? AND "+dao.Document.Columns().CreatedAt+" <=?", req.DateRange[0], req.DateRange[1]) } if !strings.EqualFold(req.IsPaging, "YES") { listRes.Total, err = m.Count() liberr.ErrIsNil(ctx, err, "获取总行数失败") if req.PageNum == 0 { req.PageNum = 1 } listRes.CurrentPage = req.PageNum if req.PageSize == 0 { req.PageSize = consts.PageSize } m = m.Fields(system.DocumentSearchRes{}).Page(req.PageNum, req.PageSize) } order := "id desc" if req.OrderBy != "" { order = req.OrderBy } var res []*model.DocumentInfoRes err = m.Order(order).Scan(&res) liberr.ErrIsNil(ctx, err, "获取数据失败") listRes.List = make([]*model.DocumentListRes, len(res)) for k, v := range res { // 对 URL 进行编码 //split := strings.Split(v.FilenPath, "/") //s2 := split[len(split)-1] //encodedPath := url.QueryEscape(s2) s2 := v.FilenPath[6 : len(v.FilenPath)-1] encodedPath := url.QueryEscape(s2) listRes.List[k] = &model.DocumentListRes{ Id: v.Id, Name: v.Name, FilenPath: v.FilenPath, FilenPathCoding: strings.ReplaceAll(v.FilenPath, s2, encodedPath), Suffix: v.Suffix, CreatedAt: v.CreatedAt, } } }) return } func (s *sDocument) GetById(ctx context.Context, id int) (res *model.DocumentInfoRes, err error) { err = g.Try(ctx, func(ctx context.Context) { err = dao.Document.Ctx(ctx).WithAll().Where(dao.Document.Columns().Id, id).Scan(&res) liberr.ErrIsNil(ctx, err, "获取信息失败") }) return } func (s *sDocument) Add(ctx context.Context, req *system.DocumentAddReq) (err error) { //大文件上传 req.FilePath.Url = strings.ReplaceAll(req.FilePath.Url, "/resource/public/", "/file/") err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { err = g.Try(ctx, func(ctx context.Context) { if req.FileType == "1" { //1、压缩文件夹导入 filenPath := "" if req.Pid != "" { value, _ := dao.Document.Ctx(ctx).Where("id_str", req.Pid).Fields("filen_path").Value() filenPath = value.String() } newPath := strings.ReplaceAll(req.FilePath.Url, "/file", coryCommon.GetCWD()+"/resource/public") path, err := coryCommon.FileZipFunc(newPath, filenPath, coryCommon.Template) //解压相对路径,并删除压缩文件,返回解压后的相对路径 if err != nil { liberr.ErrIsNil(ctx, err) return } one, err := coryCommon.Traversal(ctx, path, req.Pid, dao.Document.Table(), coryCommon.Template, 0, "1") //遍历解压后的文件,插入数据 //err = Traversal(ctx, path, req.Pid) //遍历解压后的文件,插入数据 liberr.ErrIsNil(ctx, err) _, err = g.DB().Model(dao.Document.Table()).Ctx(ctx).Insert(one) liberr.ErrIsNil(ctx, err, "新增失败!") return } else if req.FileType == "2" { //2、文件导入 wornPath := filepath.ToSlash(strings.ReplaceAll(req.FilePath.Url, "/file/", coryCommon.GetCWD()+"/resource/public/")) value, _ := dao.Document.Ctx(ctx).Where("id_str", req.Pid).Fields("filen_path").Value() newPath := strings.Replace(value.String(), "/file/", coryCommon.GetCWD()+"/resource/public/", 1) //2、文件存在就随机给一个文件名 _, err = os.Stat(newPath + "/" + req.FilePath.Name) if err == nil { name := coryCommon.FileName("mb") + "." + strings.Split(req.FilePath.Name, ".")[1] newPath = newPath + "/" + name req.FilePath.Name = name } else { newPath = newPath + "/" + req.FilePath.Name } //3、文件移动 err = os.Rename(wornPath, newPath) //4、数据库记录文件 _, err = dao.Document.Ctx(ctx).Insert(model.DocumentListRes{ Pid: req.Pid, Name: strings.Split(req.FilePath.Name, ".")[0], Suffix: req.FilePath.FileType, FilenPath: filepath.ToSlash(strings.Replace(newPath, coryCommon.GetCWD()+"/resource/public/", "/file/", 1)), Type: "1", }) liberr.ErrIsNil(ctx, err, "新增失败!") return } }) return err }) return } func (s *sDocument) NewFolderFunc(ctx context.Context, req *system.NewFolderReq) (err error) { err = g.Try(ctx, func(ctx context.Context) { //1、组装数据 resEntity := model.DocumentListRes{ Name: req.FileName, Suffix: "", Type: "2", } path := "" if req.Pid != "" { value, _ := dao.Document.Ctx(ctx).Where("id_str", req.Pid).Fields("filen_path").Value() path = value.String() + "/" + req.FileName resEntity.Pid = req.Pid } else { template := strings.Replace(coryCommon.Template, "/resource/public/", "/file/", 1) path = template + "/" + req.FileName resEntity.Pid = "0" } //2、判断是否重名,不重复就新增 count, _ := dao.Document.Ctx(ctx).Unscoped().Where("filen_path", path).Count() if count > 0 { err = errors.New("文件夹已重复!") liberr.ErrIsNil(ctx, err) return } else { resEntity.IdStr = coryCommon.SHA256(path) resEntity.FilenPath = path _, insertDataErr := dao.Document.Ctx(ctx).Insert(resEntity) if insertDataErr != nil { err = errors.New("新增文件夹失败!") liberr.ErrIsNil(ctx, err) return } err = coryCommon.CreateDirectory(strings.Replace(path, "/file/", coryCommon.GetCWD()+"/resource/public/", 1)) if err != nil { err = errors.New("新增文件夹失败!") liberr.ErrIsNil(ctx, err) return } } liberr.ErrIsNil(ctx, err, "新增文件夹失败!") }) return } //// Traversal 遍历文件夹 //func Traversal(ctx context.Context, root string, pidstr string) (err error) { // // var dataOne []*model.DocumentListRes // // template := strings.Replace(coryCommon.Template, "/resource/public", "/file", 1) // err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { // if err != nil { // return err // } // // 获取相对路径 // relativePath, err := filepath.Rel(root, path) // if err != nil { // return err // } // // 根目录下创建,还是指定文件夹下面创建? // p := "" // if pidstr != "" { // value, _ := dao.Document.Ctx(ctx).Where("id_str", pidstr).Fields("filen_path").Value() // split := strings.Split(root, "/") // p = value.String() + "/" + split[len(split)-1] + "/" + relativePath // } else { // p = template + "/" + relativePath // } // p = strings.ReplaceAll(p, "\\", "/") // // 获取当前项的深度 // depth := strings.Count(relativePath, string(filepath.Separator)) // // 判断父子关系并打印结果 // if depth == 0 && info.IsDir() { // if relativePath == "." { // split := strings.Split(root, "/") // n := split[len(split)-1] // // 根目录下创建,还是指定文件夹下面创建? // p := "" // if pidstr != "" { // value, _ := dao.Document.Ctx(ctx).Where("id_str", pidstr).Fields("filen_path").Value() // p = value.String() + "/" + n // } else { // p = template + "/" + n // } // p = strings.ReplaceAll(p, "\\", "/") // template = template + "/" + n // var dataTwo = new(model.DocumentListRes) // dataTwo.IdStr = SHA256(p) // if pidstr != "" { // dataTwo.Pid = pidstr // } else { // dataTwo.Pid = "0" // } // dataTwo.Name = n // dataTwo.FilenPath = p // ////如果文件夹路径重复,就提示 解压文件夹的时候就已经判断了,这里就不需要了 // //err := IsFolderExist(ctx, p) // //if err != nil { // // return err // //} // dataTwo.Type = "2" // dataOne = append(dataOne, dataTwo) // } else { // dir, n := filepath.Split(p) // dir = strings.TrimSuffix(dir, "/") // var dataTwo = new(model.DocumentListRes) // dataTwo.IdStr = SHA256(p) // dataTwo.Pid = SHA256(dir) // dataTwo.Name = n // dataTwo.FilenPath = p // ////如果文件夹路径重复,就提示 // //err := IsFolderExist(ctx, p) // //if err != nil { // // return err // //} // dataTwo.Type = "2" // dataOne = append(dataOne, dataTwo) // } // } else if info.IsDir() { // // 子文件夹 // dir, n := filepath.Split(p) // dir = strings.TrimSuffix(dir, "/") // var dataTwo = new(model.DocumentListRes) // dataTwo.IdStr = SHA256(p) // dataTwo.Pid = SHA256(dir) // dataTwo.Name = n // dataTwo.FilenPath = p // dataTwo.Type = "2" // dataOne = append(dataOne, dataTwo) // } else { // dir, n := filepath.Split(p) // dir = strings.TrimSuffix(dir, "/") // var dataTwo = new(model.DocumentListRes) // dataTwo.Pid = SHA256(dir) // lastDotIndex := strings.LastIndex(n, ".") // if lastDotIndex == -1 || lastDotIndex == 0 { // dataTwo.Name = strings.Split(n, ".")[0] // } else { // dataTwo.Name = n[:lastDotIndex] // } // dataTwo.Suffix = n[lastDotIndex:] // dataTwo.FilenPath = p // dataTwo.Type = "1" // //文件只能是这三种类型,其他类型进不来 // s := n[lastDotIndex:] // if strings.EqualFold(s, ".xlsx") || strings.EqualFold(s, ".docx") || strings.EqualFold(s, ".doc") || strings.EqualFold(s, ".ppt") { // dataOne = append(dataOne, dataTwo) // } // } // return err // }) // // if err != nil { // //fmt.Println("遍历文件夹时发生错误:", err) // return // } // dao.Document.Ctx(ctx).Insert(dataOne) // return err //} // //func IsFolderExist(ctx context.Context, path string) (err error) { // count, _ := dao.Document.Ctx(ctx).Where("filen_path", path).Count() // if count > 0 { // err = errors.New("文件夹已存在!") // return // } // return nil //} // //func SHA256(str string) (hx string) { // // 创建 SHA-256 哈希对象 // hash := sha256.New() // // 将字符串转换为字节数组并进行哈希计算 // hash.Write([]byte(str)) // // 计算 SHA-256 哈希值 // hashedBytes := hash.Sum(nil) // // 将哈希值转换为十六进制字符串 // hashStr := hex.EncodeToString(hashedBytes) // return hashStr //} func (s *sDocument) Edit(ctx context.Context, req *system.DocumentEditReq) (err error) { if req.Type == "1" { err = updateFile(ctx, req) } else if req.Type == "2" { err = updateFolder(ctx, req) } liberr.ErrIsNil(ctx, err) return } func RecursiveQueryEntityFunc(ctx context.Context, entity []*model.TreeStructureResTwo) (idStrs []int64) { for i := range entity { idStrs = append(idStrs, entity[i].Id) if len(entity[i].TreeStructureResTwo) > 0 { strs := RecursiveQueryEntityFunc(ctx, entity[i].TreeStructureResTwo) idStrs = append(idStrs, strs...) } } return } // 修改文件夹 func updateFolder(ctx context.Context, req *system.DocumentEditReq) (err error) { err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { err = g.Try(ctx, func(ctx context.Context) { //1、判断修改后的文件夹是否有重复,如果有就提示当前文件夹已存在! var dif *model.DocumentInfoRes dao.Document.Ctx(ctx).WherePri(req.Id).Scan(&dif) replace := strings.Replace(dif.FilenPath, dif.Name, req.Name, 1) s2 := coryCommon.FileToFunc(replace, 2) if _, err = os.Stat(s2); err == nil { err = errors.New("当前文件夹已存在!") liberr.ErrIsNil(ctx, err) return } else { count := strings.Count(dif.FilenPath, dif.Name) hx := coryCommon.SHA256(strings.ReplaceAll(dif.FilenPath, dif.FilenPath, replace)) //2、如果不存在,那么就修改文件夹的同时,还得修改路径,以及及其子文件/文件夹下的所有路径 var entity []*model.TreeStructureResTwo err = dao.Document.Ctx(ctx).Unscoped().Where("pid", dif.IdStr).WithAll().Scan(&entity) strs := RecursiveQueryEntityFunc(ctx, entity) _, err = dao.Document.Ctx(ctx).Unscoped().Where("id in (?)", strs).Update(g.Map{"filen_path": gdb.Raw("CONCAT(" + "SUBSTRING_INDEX(filen_path, '" + dif.Name + "', " + strconv.Itoa(count) + ")," + "'" + req.Name + "'," + "SUBSTRING_INDEX(filen_path, '" + dif.Name + "', -1)" + ")")}) if err != nil { err = errors.New("修改失败!") liberr.ErrIsNil(ctx, err) return } //3、重新生成并修改文件夹的id_str等信息 _, err = dao.Document.Ctx(ctx).Where("id", dif.Id).Update(g.Map{ "id_str": hx, "name": req.Name, "filen_path": gdb.Raw("CONCAT(" + "SUBSTRING_INDEX(filen_path, '" + dif.Name + "', " + strconv.Itoa(count) + ")," + "'" + req.Name + "'," + "SUBSTRING_INDEX(filen_path, '" + dif.Name + "', -1)" + ")"), }) _, err = dao.Document.Ctx(ctx).Unscoped().Where("pid", dif.IdStr).Update(g.Map{"pid": hx}) //4、修改文件夹 oldPath := filepath.Join(coryCommon.FileToFunc(dif.FilenPath, 2)) newPath := filepath.Join(filepath.Dir(oldPath), req.Name) err = os.Rename(oldPath, newPath) } liberr.ErrIsNil(ctx, err, "修改失败") return }) return err }) return } // 修改文件 func updateFile(ctx context.Context, req *system.DocumentEditReq) (err error) { err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { err = g.Try(ctx, func(ctx context.Context) { var dif *model.DocumentInfoRes dao.Document.Ctx(ctx).WherePri(req.Id).Scan(&dif) //1、判断修改后的名字是否有重复,如果有就提示当前文件名已存在! replace := strings.Replace(dif.FilenPath, dif.Name, req.Name, 1) s2 := coryCommon.FileToFunc(replace, 2) if _, err = os.Stat(s2); err == nil { err = errors.New("当前文件名已存在!") liberr.ErrIsNil(ctx, err) return } //2、如果不存在,那么就修改文件名的同时,还得修改路径 _, err = dao.Document.Ctx(ctx).WherePri(req.Id).Update(do.Document{ Name: req.Name, FilenPath: replace, }) //3、修改具体文件的文件名 if err == nil { oldPath := coryCommon.FileToFunc(dif.FilenPath, 2) newPath := filepath.Join(filepath.Dir(oldPath), req.Name+dif.Suffix) err = os.Rename(oldPath, newPath) if err != nil { err = errors.New("修改失败") liberr.ErrIsNil(ctx, err) return } } liberr.ErrIsNil(ctx, err, "修改失败") }) return err }) return } func (s *sDocument) Delete(ctx context.Context, ids []int) (err error) { err = g.Try(ctx, func(ctx context.Context) { //1、先判断删除的是文件还是文件夹,然后进行修改需要删除的文件或文件夹名称 var dataList []*model.DocumentInfoRes err = dao.Document.Ctx(ctx).Where(dao.Document.Columns().Id+" in (?)", ids).Scan(&dataList) liberr.ErrIsNil(ctx, err, "删除失败") if len(dataList) > 0 { for i := range dataList { t := dataList[i].Type upEntity := system.DocumentEditReq{ Id: dataList[i].Id, Name: dataList[i].Name + coryCommon.FileName("delete"), Type: t, } if t == "1" { err = updateFile(ctx, &upEntity) liberr.ErrIsNil(ctx, err, "删除失败") } else { err = updateFolder(ctx, &upEntity) liberr.ErrIsNil(ctx, err, "删除失败") } } //1、删除文件夹 _, err = dao.Document.Ctx(ctx).Delete(dao.Document.Columns().Id+" in (?)", ids) liberr.ErrIsNil(ctx, err, "删除失败") } }) return }