FileManager.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. package FileManager
  2. import (
  3. "Cold_Api/conf"
  4. "Cold_Api/controllers/lib"
  5. "Cold_Api/models/Account"
  6. "fmt"
  7. "path/filepath"
  8. "sort"
  9. "strings"
  10. "time"
  11. "github.com/beego/beego/v2/adapter/orm"
  12. orm2 "github.com/beego/beego/v2/client/orm"
  13. "github.com/beego/beego/v2/core/logs"
  14. _ "github.com/go-sql-driver/mysql"
  15. "github.com/google/uuid"
  16. )
  17. // 文件管理主表
  18. type FileManager struct {
  19. Id int `orm:"column(ID);size(11);auto;pk"`
  20. T_uuid string `orm:"size(128);unique"` // 文件唯一标识
  21. T_pid int `orm:"index;size(11);default(0)"` // 公司ID,同公司用户可互相查看
  22. T_name string `orm:"size(255)"` // 文件名
  23. T_path string `orm:"size(1000);index"` // 文件绝对路径
  24. T_url string `orm:"size(500);null"` // 七牛云文件链接
  25. T_size int64 `orm:"size(20);default(0)"` // 文件大小(字节)
  26. T_type string `orm:"size(50);null"` // 文件类型(文件夹/文件后缀+文件)
  27. T_uploaded_by string `orm:"size(128)"` // 上传者
  28. T_state int `orm:"size(2);default(1)"` // 状态: 0删除 1正常
  29. CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` // 创建时间
  30. UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"` // 更新时间
  31. }
  32. // 返回结构体
  33. type FileManager_R struct {
  34. Id int `json:"id"`
  35. T_uuid string `json:"t_uuid"` // 文件唯一标识
  36. T_pid int `json:"t_pid"` // 公司ID
  37. T_name string `json:"t_name"` // 文件名
  38. T_path string `json:"t_path"` // 文件绝对路径
  39. T_url string `json:"t_url"` // 七牛云文件链接
  40. T_size int64 `json:"t_size"` // 文件大小(字节)
  41. T_size_format string `json:"t_size_format"` // 格式化后的文件大小(带单位)
  42. T_type string `json:"t_type"` // 文件类型
  43. T_uploaded_by string `json:"T_uploaded_by"` // 上传者
  44. T_state int `json:"t_state"` // 状态
  45. CreateTime string `json:"create_time"` // 创建时间
  46. UpdateTime string `json:"update_time"` // 更新时间
  47. }
  48. // 简化返回结构体
  49. type FileManager_Simple struct {
  50. Id int `json:"id"`
  51. T_uuid string `json:"t_uuid"` // 文件唯一标识
  52. T_pid int `json:"t_pid"` // 公司ID
  53. T_name string `json:"t_name"` // 文件名
  54. T_path string `json:"t_path"` // 文件绝对路径
  55. T_url string `json:"t_url"` // 七牛云文件链接
  56. T_size int64 `json:"t_size"` // 文件大小(字节)
  57. T_size_format string `json:"t_size_format"` // 格式化后的文件大小(带单位)
  58. T_type string `json:"t_type"` // 文件类型
  59. T_uploaded_by string `json:"T_uploaded_by"` // 上传者
  60. CreateTime string `json:"create_time"` // 创建时间
  61. }
  62. func (t *FileManager) TableName() string {
  63. return "file_manager"
  64. }
  65. func init() {
  66. //注册模型
  67. orm.RegisterModel(new(FileManager))
  68. }
  69. // ---------------- 辅助函数 -------------------
  70. // 格式化文件大小,显示带单位的大小
  71. func FormatFileSize(size int64) string {
  72. if size == 0 {
  73. return "0 B"
  74. }
  75. const unit = 1024
  76. if size < unit {
  77. return fmt.Sprintf("%d B", size)
  78. }
  79. div, exp := int64(unit), 0
  80. for n := size / unit; n >= unit; n /= unit {
  81. div *= unit
  82. exp++
  83. }
  84. units := []string{"KB", "MB", "GB", "TB", "PB"}
  85. return fmt.Sprintf("%.1f %s", float64(size)/float64(div), units[exp])
  86. }
  87. // 生成文件类型标识
  88. func GenerateFileType(fileName, filePath string) string {
  89. // 判断是否为文件夹(路径以/结尾)
  90. if strings.HasSuffix(filePath, "/") {
  91. return "文件夹"
  92. }
  93. // 获取文件扩展名
  94. ext := filepath.Ext(fileName)
  95. if ext == "" {
  96. return "文件" // 无扩展名的文件
  97. }
  98. // 移除点号并转为小写
  99. ext = strings.ToLower(strings.TrimPrefix(ext, "."))
  100. return ext + "文件"
  101. }
  102. // ---------------- 转换方法 -------------------
  103. func FileManagerToFileManager_R(t FileManager) (r FileManager_R) {
  104. r.Id = t.Id
  105. r.T_uuid = t.T_uuid
  106. r.T_pid = t.T_pid
  107. r.T_name = t.T_name
  108. r.T_path = t.T_path
  109. r.T_url = t.T_url
  110. r.T_size_format = FormatFileSize(t.T_size) // 格式化文件大小
  111. if t.T_type == "文件夹" {
  112. r.T_size_format = "-"
  113. }
  114. r.T_type = t.T_type // 文件类型
  115. r.T_uploaded_by = t.T_uploaded_by
  116. r.T_state = t.T_state
  117. if !t.CreateTime.IsZero() {
  118. r.CreateTime = t.CreateTime.Format("2006-01-02 15:04:05")
  119. }
  120. if !t.UpdateTime.IsZero() {
  121. r.UpdateTime = t.UpdateTime.Format("2006-01-02 15:04:05")
  122. }
  123. return r
  124. }
  125. func FileManagerToFileManager_Simple(t FileManager) (r FileManager_Simple) {
  126. r.Id = t.Id
  127. r.T_uuid = t.T_uuid
  128. r.T_pid = t.T_pid
  129. r.T_name = t.T_name
  130. r.T_path = t.T_path
  131. r.T_url = t.T_url
  132. r.T_size = t.T_size
  133. r.T_size_format = FormatFileSize(t.T_size) // 格式化文件大小
  134. r.T_type = t.T_type // 文件类型
  135. r.T_uploaded_by = t.T_uploaded_by
  136. if !t.CreateTime.IsZero() {
  137. r.CreateTime = t.CreateTime.Format("2006-01-02 15:04:05")
  138. }
  139. return r
  140. }
  141. // ---------------- CRUD 方法 -------------------
  142. // 添加文件记录
  143. func Add_FileManager(m FileManager) (id int64, err error) {
  144. o := orm.NewOrm()
  145. // 如果没有UUID,自动生成一个
  146. if m.T_uuid == "" {
  147. m.T_uuid = uuid.New().String()
  148. }
  149. id, err = o.Insert(&m)
  150. if err != nil {
  151. logs.Error(lib.FuncName(), err)
  152. return
  153. }
  154. m.Id = int(id)
  155. return
  156. }
  157. // 根据ID获取文件记录
  158. func Read_FileManager_ById(id int) (r FileManager, err error) {
  159. o := orm.NewOrm()
  160. r = FileManager{Id: id, T_state: 1}
  161. err = o.Read(&r, "Id", "T_state")
  162. if err != nil {
  163. logs.Error(lib.FuncName(), err)
  164. return
  165. }
  166. return r, err
  167. }
  168. // 根据路径获取文件记录
  169. func Read_FileManager_ByPath(T_pid int, T_path string) (r FileManager, err error) {
  170. o := orm.NewOrm()
  171. cond := orm.NewCondition()
  172. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path", T_path)
  173. err = o.QueryTable(new(FileManager)).SetCond((*orm2.Condition)(cond1)).One(&r)
  174. if err != nil {
  175. logs.Error(lib.FuncName(), err)
  176. return
  177. }
  178. return r, err
  179. }
  180. // 修改文件记录
  181. func Update_FileManager(m FileManager, cols ...string) bool {
  182. o := orm.NewOrm()
  183. _, err := o.Update(&m, cols...)
  184. if err != nil {
  185. logs.Error(lib.FuncName(), err)
  186. return false
  187. }
  188. return true
  189. }
  190. // 删除文件记录(软删除)
  191. func Delete_FileManager_ByUuid(T_uuid string) bool {
  192. o := orm.NewOrm()
  193. v := FileManager{T_uuid: T_uuid}
  194. if err := o.Read(&v, "T_uuid"); err == nil {
  195. v.T_state = 0
  196. if _, err = o.Update(&v, "T_state"); err == nil {
  197. return true
  198. }
  199. }
  200. return false
  201. }
  202. // 递归删除文件记录及其子文件和子文件夹(软删除)
  203. func Delete_FileManager_Recursive_ByUuid(T_pid int, T_uuid string) (deletedCount int, err error) {
  204. o := orm.NewOrm()
  205. // 获取要删除的文件/文件夹信息
  206. var targetFile FileManager
  207. err = o.QueryTable(new(FileManager)).Filter("T_uuid", T_uuid).Filter("T_pid", T_pid).Filter("T_state", 1).One(&targetFile)
  208. if err != nil {
  209. return 0, fmt.Errorf("文件不存在或已删除")
  210. }
  211. // 如果是文件夹(路径以/结尾),需要递归删除所有子项
  212. if strings.HasSuffix(targetFile.T_path, "/") {
  213. // 查询所有子文件和子文件夹
  214. var allChildren []FileManager
  215. cond := orm.NewCondition()
  216. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path__startswith", targetFile.T_path)
  217. _, err = o.QueryTable(new(FileManager)).SetCond((*orm2.Condition)(cond1)).All(&allChildren)
  218. if err != nil {
  219. logs.Error(lib.FuncName(), err)
  220. return 0, fmt.Errorf("查询子文件失败: %v", err)
  221. }
  222. // 删除所有子项(包括目标文件夹本身)
  223. for _, child := range allChildren {
  224. child.T_state = 0
  225. if _, updateErr := o.Update(&child, "T_state"); updateErr == nil {
  226. deletedCount++
  227. } else {
  228. logs.Error("删除子文件失败:", child.T_path, updateErr)
  229. }
  230. }
  231. } else {
  232. // 如果是文件,直接删除
  233. targetFile.T_state = 0
  234. if _, updateErr := o.Update(&targetFile, "T_state"); updateErr == nil {
  235. deletedCount = 1
  236. } else {
  237. return 0, fmt.Errorf("删除文件失败: %v", updateErr)
  238. }
  239. }
  240. return deletedCount, nil
  241. }
  242. // 递归删除文件记录及其子文件和子文件夹(基于路径,软删除)
  243. func Delete_FileManager_Recursive_ByPath(T_pid int, T_path string) (deletedCount int, err error) {
  244. o := orm.NewOrm()
  245. // 获取要删除的文件/文件夹信息
  246. var targetFile FileManager
  247. err = o.QueryTable(new(FileManager)).Filter("T_path", T_path).Filter("T_pid", T_pid).Filter("T_state", 1).One(&targetFile)
  248. if err != nil {
  249. return 0, fmt.Errorf("文件不存在或已删除")
  250. }
  251. // 如果是文件夹(路径以/结尾),需要递归删除所有子项
  252. if strings.HasSuffix(targetFile.T_path, "/") {
  253. // 查询所有子文件和子文件夹
  254. var allChildren []FileManager
  255. cond := orm.NewCondition()
  256. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path__startswith", targetFile.T_path)
  257. _, err = o.QueryTable(new(FileManager)).SetCond((*orm2.Condition)(cond1)).All(&allChildren)
  258. if err != nil {
  259. logs.Error(lib.FuncName(), err)
  260. return 0, fmt.Errorf("查询子文件失败: %v", err)
  261. }
  262. // 删除所有子项(包括目标文件夹本身)
  263. for _, child := range allChildren {
  264. child.T_state = 0
  265. if _, updateErr := o.Update(&child, "T_state"); updateErr == nil {
  266. deletedCount++
  267. } else {
  268. logs.Error("删除子文件失败:", child.T_path, updateErr)
  269. }
  270. }
  271. } else {
  272. // 如果是文件,直接删除
  273. targetFile.T_state = 0
  274. if _, updateErr := o.Update(&targetFile, "T_state"); updateErr == nil {
  275. deletedCount = 1
  276. } else {
  277. return 0, fmt.Errorf("删除文件失败: %v", updateErr)
  278. }
  279. }
  280. return deletedCount, nil
  281. }
  282. // 删除文件记录(根据ID)
  283. func Delete_FileManager_ById(id int) bool {
  284. o := orm.NewOrm()
  285. v := FileManager{Id: id}
  286. if err := o.Read(&v, "Id"); err == nil {
  287. v.T_state = 0
  288. if _, err = o.Update(&v, "T_state"); err == nil {
  289. return true
  290. }
  291. }
  292. return false
  293. }
  294. // 获取文件列表(简化版,仅返回当前路径直接子项)
  295. func Read_FileManager_List_Simple(T_pid int, path string, search string, sortBy string, sortOrder string, page int, pageSize int) (FileManagerList []FileManager_R, cnt int64) {
  296. o := orm.NewOrm()
  297. qs := o.QueryTable(new(FileManager))
  298. var allMaps []FileManager
  299. cond := orm.NewCondition()
  300. cond1 := cond.And("T_State", 1)
  301. if T_pid > 0 {
  302. cond1 = cond1.And("T_pid", T_pid)
  303. }
  304. // 路径处理(先查询所有以该路径开始的文件)
  305. if len(path) > 0 {
  306. if !strings.HasSuffix(path, "/") {
  307. path += "/"
  308. }
  309. cond1 = cond1.And("T_path__startswith", path).AndNot("T_path", path)
  310. }
  311. // 搜索条件
  312. if len(search) > 0 {
  313. cond1 = cond1.And("T_name__icontains", search)
  314. }
  315. // 排序处理
  316. var orderBy string
  317. switch sortBy {
  318. case "name":
  319. if sortOrder == "descending" {
  320. orderBy = "-T_name"
  321. } else {
  322. orderBy = "T_name"
  323. }
  324. case "time":
  325. if sortOrder == "descending" {
  326. orderBy = "-CreateTime"
  327. } else {
  328. orderBy = "CreateTime"
  329. }
  330. default:
  331. // 默认按名称排序
  332. orderBy = "T_name"
  333. }
  334. // 首先查询所有符合条件的文件
  335. var err error
  336. _, err = qs.SetCond((*orm2.Condition)(cond1)).OrderBy(orderBy).All(&allMaps)
  337. if err != nil {
  338. logs.Error(lib.FuncName(), err)
  339. }
  340. // 后处理:过滤出只属于当前路径直接子项的文件
  341. var filteredMaps []FileManager
  342. if len(path) > 0 {
  343. for _, v := range allMaps {
  344. // 检查是否为直接子项
  345. relativePath := strings.TrimPrefix(v.T_path, path)
  346. // 如果相对路径不包含更多的"/",则为直接子项
  347. if !strings.Contains(strings.Trim(relativePath, "/"), "/") {
  348. filteredMaps = append(filteredMaps, v)
  349. }
  350. }
  351. } else {
  352. filteredMaps = allMaps
  353. }
  354. // 目录优先排序 + 组内二次排序(名称或时间,升/降序)
  355. sort.SliceStable(filteredMaps, func(i, j int) bool {
  356. iIsDir := strings.HasSuffix(filteredMaps[i].T_path, "/")
  357. jIsDir := strings.HasSuffix(filteredMaps[j].T_path, "/")
  358. if iIsDir != jIsDir {
  359. return iIsDir && !jIsDir
  360. }
  361. switch sortBy {
  362. case "time":
  363. if sortOrder == "descending" {
  364. return filteredMaps[i].CreateTime.After(filteredMaps[j].CreateTime)
  365. }
  366. return filteredMaps[i].CreateTime.Before(filteredMaps[j].CreateTime)
  367. default: // name
  368. iname := strings.ToLower(filteredMaps[i].T_name)
  369. jname := strings.ToLower(filteredMaps[j].T_name)
  370. if sortOrder == "descending" {
  371. return iname > jname
  372. }
  373. return iname < jname
  374. }
  375. })
  376. // 计算总数
  377. cnt = int64(len(filteredMaps))
  378. // 分页处理
  379. var offset int
  380. if page <= 1 {
  381. offset = 0
  382. } else {
  383. offset = (page - 1) * pageSize
  384. }
  385. var maps []FileManager
  386. if offset < len(filteredMaps) {
  387. end := offset + pageSize
  388. if end > len(filteredMaps) {
  389. end = len(filteredMaps)
  390. }
  391. maps = filteredMaps[offset:end]
  392. }
  393. for _, v := range maps {
  394. FileManagerList = append(FileManagerList, FileManagerToFileManager_R(v))
  395. }
  396. return FileManagerList, cnt
  397. }
  398. // 获取文件列表(根据公司ID和路径)
  399. func Read_FileManager_List(T_pid int, T_path string, T_name string, T_type int, page int, page_z int) (FileManagerList []FileManager_R, cnt int64) {
  400. o := orm.NewOrm()
  401. qs := o.QueryTable(new(FileManager))
  402. var maps []FileManager
  403. var offset int64
  404. if page <= 1 {
  405. offset = 0
  406. } else {
  407. offset = int64((page - 1) * page_z)
  408. }
  409. cond := orm.NewCondition()
  410. cond1 := cond.And("T_state", 1)
  411. if T_pid > 0 {
  412. cond1 = cond1.And("T_pid", T_pid)
  413. }
  414. if len(T_path) > 0 {
  415. // 如果路径以/结尾,查询该路径下的直接子项(排除当前路径本身)
  416. if strings.HasSuffix(T_path, "/") {
  417. cond1 = cond1.And("T_path__startswith", T_path).AndNot("T_path", T_path)
  418. } else {
  419. // 精确匹配路径
  420. cond1 = cond1.And("T_path", T_path)
  421. }
  422. }
  423. if len(T_name) > 0 {
  424. cond1 = cond1.And("T_name__icontains", T_name)
  425. }
  426. if T_type > 0 {
  427. cond1 = cond1.And("T_type", T_type)
  428. }
  429. var err error
  430. if page_z == 9999 {
  431. _, err = qs.SetCond((*orm2.Condition)(cond1)).OrderBy("T_type", "-CreateTime").All(&maps)
  432. } else {
  433. _, err = qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond1)).OrderBy("T_type", "-CreateTime").All(&maps)
  434. }
  435. if err != nil {
  436. logs.Error(lib.FuncName(), err)
  437. }
  438. cnt, err = qs.SetCond((*orm2.Condition)(cond1)).Count()
  439. if err != nil {
  440. logs.Error(lib.FuncName(), err)
  441. }
  442. for _, v := range maps {
  443. FileManagerList = append(FileManagerList, FileManagerToFileManager_R(v))
  444. }
  445. return FileManagerList, cnt
  446. }
  447. // 根据公司ID获取文件列表
  448. func Read_FileManager_List_ByPid(T_pid int) (FileManagerList []FileManager_Simple) {
  449. o := orm.NewOrm()
  450. qs := o.QueryTable(new(FileManager))
  451. var maps []FileManager
  452. cond := orm.NewCondition()
  453. cond1 := cond.And("T_state", 1).And("T_pid", T_pid)
  454. _, err := qs.SetCond((*orm2.Condition)(cond1)).OrderBy("T_type", "-CreateTime").All(&maps)
  455. if err != nil {
  456. logs.Error(lib.FuncName(), err)
  457. return FileManagerList
  458. }
  459. for _, v := range maps {
  460. FileManagerList = append(FileManagerList, FileManagerToFileManager_Simple(v))
  461. }
  462. return FileManagerList
  463. }
  464. // 批量删除文件(支持递归删除)
  465. func Batch_Delete_FileManager(T_pid int, uuids []string) error {
  466. for _, uuid := range uuids {
  467. _, err := Delete_FileManager_Recursive_ByUuid(T_pid, uuid)
  468. if err != nil {
  469. logs.Error("Batch_Delete_FileManager error:", uuid, err)
  470. return fmt.Errorf("删除文件失败: %s - %v", uuid, err)
  471. }
  472. }
  473. return nil
  474. }
  475. // ------------ 文件夹相关方法(改为基于路径) ------------
  476. // 创建文件夹
  477. func Add_FileManager_Folder(T_pid int, T_name string, T_path string, T_uploadedby string) (id int64, err error) {
  478. folder := FileManager{
  479. T_pid: T_pid,
  480. T_name: T_name,
  481. T_path: T_path,
  482. T_url: "", // 文件夹没有URL
  483. T_size: 0, // 文件夹大小设置为0
  484. T_type: GenerateFileType(T_name, T_path), // 生成文件类型
  485. T_uploaded_by: T_uploadedby,
  486. T_state: 1,
  487. }
  488. return Add_FileManager(folder)
  489. }
  490. // 获取指定路径下的子项目
  491. func Read_FileManager_Children(T_pid int, T_path string) (FileManagerList []FileManager_R) {
  492. o := orm.NewOrm()
  493. qs := o.QueryTable(new(FileManager))
  494. var maps []FileManager
  495. cond := orm.NewCondition()
  496. // 确保路径以/结尾
  497. if !strings.HasSuffix(T_path, "/") {
  498. T_path += "/"
  499. }
  500. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path__startswith", T_path)
  501. // 使用后续代码过滤来排除子目录中的文件
  502. _, err := qs.SetCond((*orm2.Condition)(cond1)).OrderBy("-CreateTime").All(&maps)
  503. if err != nil {
  504. logs.Error(lib.FuncName(), err)
  505. return FileManagerList
  506. }
  507. // 后处理:过滤出只属于当前路径直接子项的文件
  508. var filteredMaps []FileManager
  509. for _, v := range maps {
  510. // 检查是否为直接子项
  511. relativePath := strings.TrimPrefix(v.T_path, T_path)
  512. // 如果相对路径不包含更多的"/",则为直接子项
  513. if !strings.Contains(strings.Trim(relativePath, "/"), "/") {
  514. filteredMaps = append(filteredMaps, v)
  515. }
  516. }
  517. for _, v := range filteredMaps {
  518. FileManagerList = append(FileManagerList, FileManagerToFileManager_R(v))
  519. }
  520. return FileManagerList
  521. }
  522. // 检查路径下是否存在同名文件或文件夹
  523. func Check_FileManager_Name_Exists(T_pid int, T_path string, T_name string, excludeId int) bool {
  524. o := orm.NewOrm()
  525. qs := o.QueryTable(new(FileManager))
  526. // 构建完整路径
  527. fullPath := strings.TrimSuffix(T_path, "/") + "/" + T_name + "/"
  528. cond := orm.NewCondition()
  529. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path", fullPath)
  530. if excludeId > 0 {
  531. cond1 = cond1.AndNot("Id", excludeId)
  532. }
  533. cnt, err := qs.SetCond((*orm2.Condition)(cond1)).Count()
  534. if err != nil {
  535. logs.Error(lib.FuncName(), err)
  536. return false
  537. }
  538. return cnt > 0
  539. }
  540. // 检查文件是否存在(根据完整路径)
  541. func Check_FileManager_Exists_ByPath(T_pid int, T_path string) bool {
  542. o := orm.NewOrm()
  543. qs := o.QueryTable(new(FileManager))
  544. cond := orm.NewCondition()
  545. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path", T_path)
  546. cnt, err := qs.SetCond((*orm2.Condition)(cond1)).Count()
  547. if err != nil {
  548. logs.Error(lib.FuncName(), err)
  549. return false
  550. }
  551. return cnt > 0
  552. }
  553. // 自动创建路径中不存在的文件夹
  554. func Auto_Create_Folders(T_pid int, T_path string, T_uploadedby string) error {
  555. // 确保路径以/开始
  556. if !strings.HasPrefix(T_path, "/") {
  557. T_path = "/" + T_path
  558. }
  559. // 分割路径
  560. parts := strings.Split(strings.Trim(T_path, "/"), "/")
  561. currentPath := "/"
  562. for _, part := range parts {
  563. if part == "" {
  564. continue
  565. }
  566. // 构建当前文件夹路径
  567. folderPath := currentPath + part
  568. // 当path是文件夹时,在currentPath后加一个/
  569. if !strings.HasSuffix(folderPath, "/") {
  570. folderPath += "/"
  571. }
  572. // 检查文件夹是否存在
  573. if !Check_FileManager_Exists_ByPath(T_pid, folderPath) {
  574. // 文件夹不存在,创建它
  575. _, err := Add_FileManager_Folder(T_pid, part, folderPath, T_uploadedby)
  576. if err != nil {
  577. return fmt.Errorf("创建文件夹 %s 失败: %v", folderPath, err)
  578. }
  579. }
  580. // 更新当前路径,确保文件夹路径以/结尾
  581. currentPath = folderPath
  582. }
  583. return nil
  584. }
  585. // 获取路径的面包屑导航
  586. func Get_FileManager_Breadcrumb(T_path string) []map[string]string {
  587. var breadcrumb []map[string]string
  588. // 根目录
  589. breadcrumb = append(breadcrumb, map[string]string{
  590. "name": "根目录",
  591. "path": "/",
  592. })
  593. if T_path == "/" || T_path == "" {
  594. return breadcrumb
  595. }
  596. // 分割路径
  597. parts := strings.Split(strings.Trim(T_path, "/"), "/")
  598. currentPath := ""
  599. for _, part := range parts {
  600. if part != "" {
  601. currentPath += "/" + part
  602. // 当path是文件夹时,在currentPath后加一个/
  603. pathForBreadcrumb := currentPath
  604. if !strings.HasSuffix(pathForBreadcrumb, "/") {
  605. pathForBreadcrumb += "/"
  606. }
  607. breadcrumb = append(breadcrumb, map[string]string{
  608. "name": part,
  609. "path": pathForBreadcrumb,
  610. })
  611. }
  612. }
  613. return breadcrumb
  614. }
  615. // 判断是否为文件夹
  616. func Is_FileManager_Folder(id int) bool {
  617. folder, err := Read_FileManager_ById(id)
  618. if err != nil {
  619. return false
  620. }
  621. // 文件夹的判断需要根据路径是否以"/"结尾来判断
  622. return strings.HasSuffix(folder.T_path, "/")
  623. }
  624. // 重命名文件夹并递归更新所有子文件和子文件夹的路径
  625. func Update_Folder_And_Children_Path(T_pid int, oldPath, newPath, newName string) error {
  626. o := orm.NewOrm()
  627. o.Begin()
  628. // 1. 先更新文件夹本身
  629. var folder FileManager
  630. err := o.QueryTable(new(FileManager)).Filter("T_pid", T_pid).Filter("T_path", oldPath).Filter("T_state", 1).One(&folder)
  631. if err != nil {
  632. o.Rollback()
  633. return fmt.Errorf("获取文件夹信息失败: %v", err)
  634. }
  635. folder.T_name = newName
  636. folder.T_path = newPath
  637. _, err = o.Update(&folder, "T_name", "T_path", "UpdateTime")
  638. if err != nil {
  639. o.Rollback()
  640. return fmt.Errorf("更新文件夹失败: %v", err)
  641. }
  642. // 2. 查询所有子文件和子文件夹
  643. var children []FileManager
  644. cond := orm.NewCondition()
  645. cond1 := cond.And("T_state", 1).And("T_pid", T_pid).And("T_path__startswith", oldPath)
  646. _, err = o.QueryTable(new(FileManager)).SetCond((*orm2.Condition)(cond1)).All(&children)
  647. if err != nil {
  648. o.Rollback()
  649. return fmt.Errorf("查询子文件失败: %v", err)
  650. }
  651. // 3. 递归更新所有子文件和子文件夹的路径
  652. for _, child := range children {
  653. // 跳过文件夹本身(已经更新过了)
  654. if child.T_path == oldPath {
  655. continue
  656. }
  657. // 替换路径前缀
  658. if strings.HasPrefix(child.T_path, oldPath) {
  659. newChildPath := strings.Replace(child.T_path, oldPath, newPath, 1)
  660. child.T_path = newChildPath
  661. _, err = o.Update(&child, "T_path", "UpdateTime")
  662. if err != nil {
  663. o.Rollback()
  664. return fmt.Errorf("更新子文件 %s 路径失败: %v", child.T_name, err)
  665. }
  666. }
  667. }
  668. o.Commit()
  669. return nil
  670. }
  671. // ---------------- 存储空间管理 -------------------
  672. // 获取公司已使用的存储空间(字节)
  673. func Get_Company_Used_Storage(T_pid int) (int64, error) {
  674. o := orm.NewOrm()
  675. qs := o.QueryTable(new(FileManager))
  676. cond := orm.NewCondition()
  677. cond1 := cond.And("T_state", 1).And("T_pid", T_pid)
  678. var totalSize int64
  679. var maps []FileManager
  680. _, err := qs.SetCond((*orm2.Condition)(cond1)).All(&maps)
  681. if err != nil {
  682. logs.Error(lib.FuncName(), err)
  683. return 0, err
  684. }
  685. for _, file := range maps {
  686. // 只统计文件大小,不统计文件夹(文件夹的T_size为0)
  687. if !strings.HasSuffix(file.T_path, "/") {
  688. totalSize += file.T_size
  689. }
  690. }
  691. return totalSize, nil
  692. }
  693. // 获取公司的文件大小限制(字节)
  694. // 如果公司配置的限制为0或未设置,则使用系统默认配置
  695. func Get_Company_Storage_Limit(T_pid int) (int64, error) {
  696. // 导入配置包
  697. company, err := Account.Read_Company_ById(T_pid)
  698. if err != nil {
  699. return 0, fmt.Errorf("获取公司信息失败: %v", err)
  700. }
  701. // 如果公司设置了存储限制,使用公司配置
  702. if company.T_file_size_limit > 0 {
  703. return company.T_file_size_limit, nil
  704. }
  705. // 否则使用系统默认配置
  706. return conf.DefaultFileSizeLimit, nil
  707. }
  708. // 检查公司是否还有足够的存储空间上传新文件
  709. func Check_Company_Storage_Available(T_pid int, newFileSize int64) (bool, int64, int64, error) {
  710. // 获取已使用空间
  711. usedStorage, err := Get_Company_Used_Storage(T_pid)
  712. if err != nil {
  713. return false, 0, 0, fmt.Errorf("获取已使用存储空间失败: %v", err)
  714. }
  715. // 获取存储限制
  716. storageLimit, err := Get_Company_Storage_Limit(T_pid)
  717. if err != nil {
  718. return false, 0, 0, fmt.Errorf("获取存储限制失败: %v", err)
  719. }
  720. // 检查是否还有足够空间
  721. available := (usedStorage + newFileSize) <= storageLimit
  722. return available, usedStorage, storageLimit, nil
  723. }
  724. // 获取公司存储使用情况统计
  725. type CompanyStorageInfo struct {
  726. UsedStorage int64 `json:"used_storage"` // 已使用存储(字节)
  727. StorageLimit int64 `json:"storage_limit"` // 存储限制(字节)
  728. AvailableStorage int64 `json:"available_storage"` // 可用存储(字节)
  729. UsagePercentage float64 `json:"usage_percentage"` // 使用百分比
  730. UsedStorageFormatted string `json:"used_storage_formatted"` // 格式化的已使用存储
  731. StorageLimitFormatted string `json:"storage_limit_formatted"` // 格式化的存储限制
  732. AvailableStorageFormatted string `json:"available_storage_formatted"` // 格式化的可用存储
  733. }
  734. func Get_Company_Storage_Info(T_pid int) (CompanyStorageInfo, error) {
  735. var info CompanyStorageInfo
  736. // 获取已使用存储
  737. usedStorage, err := Get_Company_Used_Storage(T_pid)
  738. if err != nil {
  739. return info, fmt.Errorf("获取已使用存储失败: %v", err)
  740. }
  741. // 获取存储限制
  742. storageLimit, err := Get_Company_Storage_Limit(T_pid)
  743. if err != nil {
  744. return info, fmt.Errorf("获取存储限制失败: %v", err)
  745. }
  746. // 计算可用存储
  747. availableStorage := storageLimit - usedStorage
  748. if availableStorage < 0 {
  749. availableStorage = 0
  750. }
  751. // 计算使用百分比
  752. usagePercentage := float64(0)
  753. if storageLimit > 0 {
  754. usagePercentage = float64(usedStorage) / float64(storageLimit) * 100
  755. }
  756. // 填充结构体
  757. info.UsedStorage = usedStorage
  758. info.StorageLimit = storageLimit
  759. info.AvailableStorage = availableStorage
  760. info.UsagePercentage = usagePercentage
  761. info.UsedStorageFormatted = FormatFileSize(usedStorage)
  762. info.StorageLimitFormatted = FormatFileSize(storageLimit)
  763. info.AvailableStorageFormatted = FormatFileSize(availableStorage)
  764. return info, nil
  765. }