You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

package.go 9.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. package db
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/samber/lo"
  6. jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types"
  7. "gorm.io/gorm"
  8. )
  9. type PackageDB struct {
  10. *DB
  11. }
  12. func (db *DB) Package() *PackageDB {
  13. return &PackageDB{DB: db}
  14. }
  15. func (db *PackageDB) GetByID(ctx SQLContext, packageID jcstypes.PackageID) (jcstypes.Package, error) {
  16. var ret jcstypes.Package
  17. err := ctx.Table("Package").Where("PackageID = ?", packageID).First(&ret).Error
  18. return ret, err
  19. }
  20. func (db *PackageDB) GetByName(ctx SQLContext, bucketID jcstypes.BucketID, name string) (jcstypes.Package, error) {
  21. var ret jcstypes.Package
  22. err := ctx.Table("Package").Where("BucketID = ? AND Name = ?", bucketID, name).First(&ret).Error
  23. return ret, err
  24. }
  25. func (db *PackageDB) GetDetail(ctx SQLContext, packageID jcstypes.PackageID) (jcstypes.PackageDetail, error) {
  26. var pkg jcstypes.Package
  27. err := ctx.Table("Package").Where("PackageID = ?", packageID).First(&pkg).Error
  28. if err != nil {
  29. return jcstypes.PackageDetail{}, err
  30. }
  31. var ret struct {
  32. ObjectCount int64
  33. TotalSize int64
  34. }
  35. err = ctx.Table("Object").
  36. Select("COUNT(*) as ObjectCount, SUM(Size) as TotalSize").
  37. Where("PackageID = ?", packageID).
  38. Scan(&ret).
  39. Error
  40. if err != nil {
  41. return jcstypes.PackageDetail{}, err
  42. }
  43. return jcstypes.PackageDetail{
  44. Package: pkg,
  45. ObjectCount: ret.ObjectCount,
  46. TotalSize: ret.TotalSize,
  47. }, nil
  48. }
  49. func (db *PackageDB) BatchGetIDPaged(ctx SQLContext, lastPkgID jcstypes.PackageID, count int) ([]jcstypes.PackageID, error) {
  50. var ret []jcstypes.PackageID
  51. err := ctx.Table("Package").
  52. Select("PackageID").
  53. Where("PackageID > ?", lastPkgID).
  54. Order("PackageID ASC").
  55. Limit(count).
  56. Find(&ret).Error
  57. return ret, err
  58. }
  59. func (db *PackageDB) BatchGetDetailPaged(ctx SQLContext, lastPkgID jcstypes.PackageID, count int) ([]jcstypes.PackageDetail, error) {
  60. var pkgs []jcstypes.Package
  61. err := ctx.Table("Package").
  62. Where("PackageID > ?", lastPkgID).
  63. Order("PackageID ASC").
  64. Limit(count).
  65. Find(&pkgs).Error
  66. if err != nil {
  67. return nil, err
  68. }
  69. if len(pkgs) == 0 {
  70. return nil, nil
  71. }
  72. minPkgID := pkgs[0].PackageID
  73. maxPkgID := pkgs[len(pkgs)-1].PackageID
  74. type detail struct {
  75. ObjectCount int64
  76. TotalSize int64
  77. }
  78. var details []detail
  79. err = ctx.
  80. Table("Package").
  81. Select("COUNT(ObjectID) as ObjectCount, SUM(Size) as TotalSize").
  82. Joins("left join Object o on Package.PackageID = o.PackageID").
  83. Where("Package.PackageID >= ? AND Package.PackageID <= ?", minPkgID, maxPkgID).
  84. Group("Package.PackageID").
  85. Order("Package.PackageID ASC").
  86. Find(&details).Error
  87. if err != nil {
  88. return nil, err
  89. }
  90. ret := make([]jcstypes.PackageDetail, len(pkgs))
  91. for i := range pkgs {
  92. ret[i] = jcstypes.PackageDetail{
  93. Package: pkgs[i],
  94. ObjectCount: details[i].ObjectCount,
  95. TotalSize: details[i].TotalSize,
  96. }
  97. }
  98. return ret, nil
  99. }
  100. func (db *PackageDB) BatchTestPackageID(ctx SQLContext, pkgIDs []jcstypes.PackageID) (map[jcstypes.PackageID]bool, error) {
  101. if len(pkgIDs) == 0 {
  102. return make(map[jcstypes.PackageID]bool), nil
  103. }
  104. var avaiIDs []jcstypes.PackageID
  105. err := ctx.Table("Package").
  106. Select("PackageID").
  107. Where("PackageID IN ?", pkgIDs).
  108. Find(&avaiIDs).Error
  109. if err != nil {
  110. return nil, err
  111. }
  112. avaiIDMap := make(map[jcstypes.PackageID]bool)
  113. for _, pkgID := range avaiIDs {
  114. avaiIDMap[pkgID] = true
  115. }
  116. return avaiIDMap, nil
  117. }
  118. func (*PackageDB) BatchGetAllPackageIDs(ctx SQLContext, start int, count int) ([]jcstypes.PackageID, error) {
  119. var ret []jcstypes.PackageID
  120. err := ctx.Table("Package").Select("PackageID").Limit(count).Offset(start).Find(&ret).Error
  121. return ret, err
  122. }
  123. func (db *PackageDB) GetBucketPackages(ctx SQLContext, bucketID jcstypes.BucketID) ([]jcstypes.Package, error) {
  124. var ret []jcstypes.Package
  125. err := ctx.Table("Package").
  126. Select("Package.*").
  127. Where("BucketID = ?", bucketID).
  128. Find(&ret).Error
  129. return ret, err
  130. }
  131. func (db *PackageDB) GetBucketPackagesByName(ctx SQLContext, bucketName string) ([]jcstypes.Package, error) {
  132. var ret []jcstypes.Package
  133. err := ctx.Table("Package").
  134. Select("Package.*").
  135. Joins("JOIN Bucket ON Package.BucketID = Bucket.BucketID").
  136. Where("Bucket.Name = ?", bucketName).
  137. Find(&ret).Error
  138. return ret, err
  139. }
  140. // 在指定名称的Bucket中查找指定名称的Package
  141. func (*PackageDB) GetByFullName(ctx SQLContext, bucketName string, packageName string) (jcstypes.Package, error) {
  142. var ret jcstypes.Package
  143. err := ctx.Table("Package").
  144. Select("Package.*").
  145. Joins("JOIN Bucket ON Package.BucketID = Bucket.BucketID").
  146. Where("Package.Name = ? AND Bucket.Name = ?", packageName, bucketName).
  147. First(&ret).Error
  148. return ret, err
  149. }
  150. func (db *PackageDB) Create(ctx SQLContext, bucketID jcstypes.BucketID, name string, createTime time.Time) (jcstypes.Package, error) {
  151. var packageID int64
  152. err := ctx.Table("Package").
  153. Select("PackageID").
  154. Where("Name = ? AND BucketID = ?", name, bucketID).
  155. Scan(&packageID).Error
  156. if err != nil {
  157. return jcstypes.Package{}, err
  158. }
  159. if packageID != 0 {
  160. return jcstypes.Package{}, gorm.ErrDuplicatedKey
  161. }
  162. newPackage := jcstypes.Package{Name: name, BucketID: bucketID, CreateTime: createTime}
  163. if err := ctx.Create(&newPackage).Error; err != nil {
  164. return jcstypes.Package{}, fmt.Errorf("insert package failed, err: %w", err)
  165. }
  166. return newPackage, nil
  167. }
  168. func (*PackageDB) Delete(ctx SQLContext, packageID jcstypes.PackageID) error {
  169. err := ctx.Delete(&jcstypes.Package{}, "PackageID = ?", packageID).Error
  170. return err
  171. }
  172. // 删除与Package相关的所有数据
  173. func (db *PackageDB) DeleteComplete(ctx SQLContext, packageID jcstypes.PackageID) error {
  174. if err := db.Package().Delete(ctx, packageID); err != nil {
  175. return fmt.Errorf("delete package state: %w", err)
  176. }
  177. if err := db.ObjectAccessStat().DeleteInPackage(ctx, packageID); err != nil {
  178. return fmt.Errorf("delete from object access stat: %w", err)
  179. }
  180. if err := db.ObjectBlock().DeleteInPackage(ctx, packageID); err != nil {
  181. return fmt.Errorf("delete from object block failed, err: %w", err)
  182. }
  183. if err := db.PinnedObject().DeleteInPackage(ctx, packageID); err != nil {
  184. return fmt.Errorf("deleting pinned objects in package: %w", err)
  185. }
  186. if err := db.Object().DeleteInPackage(ctx, packageID); err != nil {
  187. return fmt.Errorf("deleting objects in package: %w", err)
  188. }
  189. if err := db.PackageAccessStat().DeleteByPackageID(ctx, packageID); err != nil {
  190. return fmt.Errorf("deleting package access stat: %w", err)
  191. }
  192. return nil
  193. }
  194. func (*PackageDB) ChangeState(ctx SQLContext, packageID jcstypes.PackageID, state string) error {
  195. err := ctx.Exec("UPDATE Package SET State = ? WHERE PackageID = ?", state, packageID).Error
  196. return err
  197. }
  198. // 返回ErrRecordNotFound表示没有找到指定名称的Bucket,nil表示找到了
  199. func (*PackageDB) HasPackageIn(ctx SQLContext, bucketID jcstypes.BucketID) error {
  200. var pkg jcstypes.Package
  201. return ctx.Table("Package").Where("BucketID = ?", bucketID).First(&pkg).Error
  202. }
  203. func (*PackageDB) Move(ctx SQLContext, packageID jcstypes.PackageID, newBktID jcstypes.BucketID, newName string) error {
  204. err := ctx.Table("Package").Where("PackageID = ?", packageID).Update("BucketID", newBktID).Update("Name", newName).Error
  205. return err
  206. }
  207. type AddAccessStatEntry struct {
  208. ObjectID jcstypes.ObjectID `json:"objectID"`
  209. PackageID jcstypes.PackageID `json:"packageID"`
  210. UserSpaceID jcstypes.UserSpaceID `json:"userSpaceID"`
  211. Counter float64 `json:"counter"`
  212. }
  213. func (db *PackageDB) BatchAddPackageAccessStat(ctx SQLContext, entries []AddAccessStatEntry) error {
  214. pkgIDs := make(map[jcstypes.PackageID]bool)
  215. objIDs := make(map[jcstypes.ObjectID]bool)
  216. for _, e := range entries {
  217. pkgIDs[e.PackageID] = true
  218. objIDs[e.ObjectID] = true
  219. }
  220. avaiPkgIDs, err := db.Package().BatchTestPackageID(ctx, lo.Keys(pkgIDs))
  221. if err != nil {
  222. return fmt.Errorf("batch test package id: %w", err)
  223. }
  224. avaiObjIDs, err := db.Object().BatchTestObjectID(ctx, lo.Keys(objIDs))
  225. if err != nil {
  226. return fmt.Errorf("batch test object id: %w", err)
  227. }
  228. var willAdds []AddAccessStatEntry
  229. for _, e := range entries {
  230. if avaiPkgIDs[e.PackageID] && avaiObjIDs[e.ObjectID] {
  231. willAdds = append(willAdds, e)
  232. }
  233. }
  234. if len(willAdds) > 0 {
  235. err := db.PackageAccessStat().BatchAddCounter(ctx, willAdds)
  236. if err != nil {
  237. return fmt.Errorf("batch add package access stat counter: %w", err)
  238. }
  239. err = db.ObjectAccessStat().BatchAddCounter(ctx, willAdds)
  240. if err != nil {
  241. return fmt.Errorf("batch add object access stat counter: %w", err)
  242. }
  243. }
  244. return nil
  245. }
  246. // 尝试创建指定名称的Bucket和Package,如果Bucket不存在,则创建Bucket,如果Package已存在,则直接返回已有的Package
  247. func (db *PackageDB) TryCreateAll(ctx SQLContext, bktName string, pkgName string) (jcstypes.Package, error) {
  248. bkt, err := db.Bucket().GetByName(ctx, bktName)
  249. if err == gorm.ErrRecordNotFound {
  250. bkt, err = db.Bucket().Create(ctx, bktName, time.Now())
  251. if err != nil {
  252. return jcstypes.Package{}, fmt.Errorf("create bucket: %w", err)
  253. }
  254. } else if err != nil {
  255. return jcstypes.Package{}, fmt.Errorf("get bucket by name: %w", err)
  256. }
  257. pkg, err := db.GetByName(ctx, bkt.BucketID, pkgName)
  258. if err == nil {
  259. return pkg, nil
  260. }
  261. if err != gorm.ErrRecordNotFound {
  262. return jcstypes.Package{}, fmt.Errorf("get package by name: %w", err)
  263. }
  264. pkg, err = db.Create(ctx, bkt.BucketID, pkgName, time.Now())
  265. if err != nil {
  266. return jcstypes.Package{}, fmt.Errorf("create package: %w", err)
  267. }
  268. return pkg, nil
  269. }
  270. func (db *PackageDB) SetPinned(ctx SQLContext, packageID jcstypes.PackageID, pinned bool) error {
  271. err := ctx.Table("Package").Where("PackageID = ?", packageID).Update("Pinned", pinned).Error
  272. return err
  273. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。