Files
zmkgC/internal/app/system/logic/document/document.go
2025-07-07 20:11:59 +08:00

574 lines
20 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==========================================================================
// 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
}