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.

object.go 18 kB

11 months ago
11 months ago
2 years ago
11 months ago
11 months ago
11 months ago
11 months ago
2 years ago
11 months ago
11 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. package mq
  2. import (
  3. "errors"
  4. "fmt"
  5. "gitlink.org.cn/cloudream/storage/common/pkgs/db2"
  6. "gorm.io/gorm"
  7. "github.com/samber/lo"
  8. "gitlink.org.cn/cloudream/common/consts/errorcode"
  9. "gitlink.org.cn/cloudream/common/pkgs/logger"
  10. "gitlink.org.cn/cloudream/common/pkgs/mq"
  11. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  12. "gitlink.org.cn/cloudream/common/sdks/storage/cdsapi"
  13. "gitlink.org.cn/cloudream/common/utils/sort2"
  14. stgmod "gitlink.org.cn/cloudream/storage/common/models"
  15. coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
  16. )
  17. func (svc *Service) GetObjects(msg *coormq.GetObjects) (*coormq.GetObjectsResp, *mq.CodeMessage) {
  18. var ret []*cdssdk.Object
  19. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  20. // TODO 应该检查用户是否有每一个Object所在Package的权限
  21. objs, err := svc.db2.Object().BatchGet(tx, msg.ObjectIDs)
  22. if err != nil {
  23. return err
  24. }
  25. objMp := make(map[cdssdk.ObjectID]cdssdk.Object)
  26. for _, obj := range objs {
  27. objMp[obj.ObjectID] = obj
  28. }
  29. for _, objID := range msg.ObjectIDs {
  30. o, ok := objMp[objID]
  31. if ok {
  32. ret = append(ret, &o)
  33. } else {
  34. ret = append(ret, nil)
  35. }
  36. }
  37. return err
  38. })
  39. if err != nil {
  40. logger.WithField("UserID", msg.UserID).
  41. Warn(err.Error())
  42. return nil, mq.Failed(errorcode.OperationFailed, "get objects failed")
  43. }
  44. return mq.ReplyOK(coormq.RespGetObjects(ret))
  45. }
  46. func (svc *Service) GetObjectsByPath(msg *coormq.GetObjectsByPath) (*coormq.GetObjectsByPathResp, *mq.CodeMessage) {
  47. var objs []cdssdk.Object
  48. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  49. var err error
  50. _, err = svc.db2.Package().GetUserPackage(tx, msg.UserID, msg.PackageID)
  51. if err != nil {
  52. return fmt.Errorf("getting package by id: %w", err)
  53. }
  54. if msg.IsPrefix {
  55. objs, err = svc.db2.Object().GetWithPathPrefix(tx, msg.PackageID, msg.Path)
  56. if err != nil {
  57. return fmt.Errorf("getting objects with prefix: %w", err)
  58. }
  59. } else {
  60. objs, err = svc.db2.Object().GetByPath(tx, msg.PackageID, msg.Path)
  61. if err != nil {
  62. return fmt.Errorf("getting object by path: %w", err)
  63. }
  64. }
  65. return nil
  66. })
  67. if err != nil {
  68. logger.WithField("PathPrefix", msg.Path).Warn(err.Error())
  69. return nil, mq.Failed(errorcode.OperationFailed, "get objects with prefix failed")
  70. }
  71. return mq.ReplyOK(coormq.RespGetObjectsByPath(objs))
  72. }
  73. func (svc *Service) GetPackageObjects(msg *coormq.GetPackageObjects) (*coormq.GetPackageObjectsResp, *mq.CodeMessage) {
  74. var objs []cdssdk.Object
  75. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  76. _, err := svc.db2.Package().GetUserPackage(tx, msg.UserID, msg.PackageID)
  77. if err != nil {
  78. return fmt.Errorf("getting package by id: %w", err)
  79. }
  80. objs, err = svc.db2.Object().GetPackageObjects(tx, msg.PackageID)
  81. if err != nil {
  82. return fmt.Errorf("getting package objects: %w", err)
  83. }
  84. return nil
  85. })
  86. if err != nil {
  87. logger.WithField("UserID", msg.UserID).WithField("PackageID", msg.PackageID).
  88. Warn(err.Error())
  89. return nil, mq.Failed(errorcode.OperationFailed, "get package objects failed")
  90. }
  91. return mq.ReplyOK(coormq.RespGetPackageObjects(objs))
  92. }
  93. func (svc *Service) GetPackageObjectDetails(msg *coormq.GetPackageObjectDetails) (*coormq.GetPackageObjectDetailsResp, *mq.CodeMessage) {
  94. var details []stgmod.ObjectDetail
  95. // 必须放在事务里进行,因为GetPackageBlockDetails是由多次数据库操作组成,必须保证数据的一致性
  96. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  97. var err error
  98. _, err = svc.db2.Package().GetByID(tx, msg.PackageID)
  99. if err != nil {
  100. return fmt.Errorf("getting package by id: %w", err)
  101. }
  102. details, err = svc.db2.Object().GetPackageObjectDetails(tx, msg.PackageID)
  103. if err != nil {
  104. return fmt.Errorf("getting package block details: %w", err)
  105. }
  106. return nil
  107. })
  108. if err != nil {
  109. logger.WithField("PackageID", msg.PackageID).Warn(err.Error())
  110. return nil, mq.Failed(errorcode.OperationFailed, "get package object block details failed")
  111. }
  112. return mq.ReplyOK(coormq.RespPackageObjectDetails(details))
  113. }
  114. func (svc *Service) GetObjectDetails(msg *coormq.GetObjectDetails) (*coormq.GetObjectDetailsResp, *mq.CodeMessage) {
  115. detailsMp := make(map[cdssdk.ObjectID]*stgmod.ObjectDetail)
  116. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  117. var err error
  118. msg.ObjectIDs = sort2.SortAsc(msg.ObjectIDs)
  119. // 根据ID依次查询Object,ObjectBlock,PinnedObject,并根据升序的特点进行合并
  120. objs, err := svc.db2.Object().BatchGet(tx, msg.ObjectIDs)
  121. if err != nil {
  122. return fmt.Errorf("batch get objects: %w", err)
  123. }
  124. for _, obj := range objs {
  125. detailsMp[obj.ObjectID] = &stgmod.ObjectDetail{
  126. Object: obj,
  127. }
  128. }
  129. // 查询合并
  130. blocks, err := svc.db2.ObjectBlock().BatchGetByObjectID(tx, msg.ObjectIDs)
  131. if err != nil {
  132. return fmt.Errorf("batch get object blocks: %w", err)
  133. }
  134. for _, block := range blocks {
  135. d := detailsMp[block.ObjectID]
  136. d.Blocks = append(d.Blocks, block)
  137. }
  138. // 查询合并
  139. pinneds, err := svc.db2.PinnedObject().BatchGetByObjectID(tx, msg.ObjectIDs)
  140. if err != nil {
  141. return fmt.Errorf("batch get pinned objects: %w", err)
  142. }
  143. for _, pinned := range pinneds {
  144. d := detailsMp[pinned.ObjectID]
  145. d.PinnedAt = append(d.PinnedAt, pinned.StorageID)
  146. }
  147. return nil
  148. })
  149. if err != nil {
  150. logger.Warn(err.Error())
  151. return nil, mq.Failed(errorcode.OperationFailed, "get object details failed")
  152. }
  153. details := make([]*stgmod.ObjectDetail, len(msg.ObjectIDs))
  154. for i, objID := range msg.ObjectIDs {
  155. details[i] = detailsMp[objID]
  156. }
  157. return mq.ReplyOK(coormq.RespGetObjectDetails(details))
  158. }
  159. func (svc *Service) UpdateObjectRedundancy(msg *coormq.UpdateObjectRedundancy) (*coormq.UpdateObjectRedundancyResp, *mq.CodeMessage) {
  160. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  161. return svc.db2.Object().BatchUpdateRedundancy(tx, msg.Updatings)
  162. })
  163. if err != nil {
  164. logger.Warnf("batch updating redundancy: %s", err.Error())
  165. return nil, mq.Failed(errorcode.OperationFailed, "batch update redundancy failed")
  166. }
  167. return mq.ReplyOK(coormq.RespUpdateObjectRedundancy())
  168. }
  169. func (svc *Service) UpdateObjectInfos(msg *coormq.UpdateObjectInfos) (*coormq.UpdateObjectInfosResp, *mq.CodeMessage) {
  170. var sucs []cdssdk.ObjectID
  171. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  172. msg.Updatings = sort2.Sort(msg.Updatings, func(o1, o2 cdsapi.UpdatingObject) int {
  173. return sort2.Cmp(o1.ObjectID, o2.ObjectID)
  174. })
  175. objIDs := make([]cdssdk.ObjectID, len(msg.Updatings))
  176. for i, obj := range msg.Updatings {
  177. objIDs[i] = obj.ObjectID
  178. }
  179. oldObjs, err := svc.db2.Object().BatchGet(tx, objIDs)
  180. if err != nil {
  181. return fmt.Errorf("batch getting objects: %w", err)
  182. }
  183. oldObjIDs := make([]cdssdk.ObjectID, len(oldObjs))
  184. for i, obj := range oldObjs {
  185. oldObjIDs[i] = obj.ObjectID
  186. }
  187. avaiUpdatings, notExistsObjs := pickByObjectIDs(msg.Updatings, oldObjIDs, func(obj cdsapi.UpdatingObject) cdssdk.ObjectID { return obj.ObjectID })
  188. if len(notExistsObjs) > 0 {
  189. // TODO 部分对象已经不存在
  190. }
  191. newObjs := make([]cdssdk.Object, len(avaiUpdatings))
  192. for i := range newObjs {
  193. newObjs[i] = oldObjs[i]
  194. avaiUpdatings[i].ApplyTo(&newObjs[i])
  195. }
  196. err = svc.db2.Object().BatchUpdate(tx, newObjs)
  197. if err != nil {
  198. return fmt.Errorf("batch create or update: %w", err)
  199. }
  200. sucs = lo.Map(newObjs, func(obj cdssdk.Object, _ int) cdssdk.ObjectID { return obj.ObjectID })
  201. return nil
  202. })
  203. if err != nil {
  204. logger.Warnf("batch updating objects: %s", err.Error())
  205. return nil, mq.Failed(errorcode.OperationFailed, "batch update objects failed")
  206. }
  207. return mq.ReplyOK(coormq.RespUpdateObjectInfos(sucs))
  208. }
  209. // 根据objIDs从objs中挑选Object。
  210. // len(objs) >= len(objIDs)
  211. func pickByObjectIDs[T any](objs []T, objIDs []cdssdk.ObjectID, getID func(T) cdssdk.ObjectID) (picked []T, notFound []T) {
  212. objIdx := 0
  213. idIdx := 0
  214. for idIdx < len(objIDs) && objIdx < len(objs) {
  215. if getID(objs[objIdx]) < objIDs[idIdx] {
  216. notFound = append(notFound, objs[objIdx])
  217. objIdx++
  218. continue
  219. }
  220. picked = append(picked, objs[objIdx])
  221. objIdx++
  222. idIdx++
  223. }
  224. return
  225. }
  226. func (svc *Service) MoveObjects(msg *coormq.MoveObjects) (*coormq.MoveObjectsResp, *mq.CodeMessage) {
  227. var sucs []cdssdk.ObjectID
  228. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  229. msg.Movings = sort2.Sort(msg.Movings, func(o1, o2 cdsapi.MovingObject) int {
  230. return sort2.Cmp(o1.ObjectID, o2.ObjectID)
  231. })
  232. objIDs := make([]cdssdk.ObjectID, len(msg.Movings))
  233. for i, obj := range msg.Movings {
  234. objIDs[i] = obj.ObjectID
  235. }
  236. oldObjs, err := svc.db2.Object().BatchGet(tx, objIDs)
  237. if err != nil {
  238. return fmt.Errorf("batch getting objects: %w", err)
  239. }
  240. oldObjIDs := make([]cdssdk.ObjectID, len(oldObjs))
  241. for i, obj := range oldObjs {
  242. oldObjIDs[i] = obj.ObjectID
  243. }
  244. // 找出仍在数据库的Object
  245. avaiMovings, notExistsObjs := pickByObjectIDs(msg.Movings, oldObjIDs, func(obj cdsapi.MovingObject) cdssdk.ObjectID { return obj.ObjectID })
  246. if len(notExistsObjs) > 0 {
  247. // TODO 部分对象已经不存在
  248. }
  249. // 筛选出PackageID变化、Path变化的对象,这两种对象要检测改变后是否有冲突
  250. var pkgIDChangedObjs []cdssdk.Object
  251. var pathChangedObjs []cdssdk.Object
  252. for i := range avaiMovings {
  253. if avaiMovings[i].PackageID != oldObjs[i].PackageID {
  254. newObj := oldObjs[i]
  255. avaiMovings[i].ApplyTo(&newObj)
  256. pkgIDChangedObjs = append(pkgIDChangedObjs, newObj)
  257. } else if avaiMovings[i].Path != oldObjs[i].Path {
  258. newObj := oldObjs[i]
  259. avaiMovings[i].ApplyTo(&newObj)
  260. pathChangedObjs = append(pathChangedObjs, newObj)
  261. }
  262. }
  263. var newObjs []cdssdk.Object
  264. // 对于PackageID发生变化的对象,需要检查目标Package内是否存在同Path的对象
  265. checkedObjs, err := svc.checkPackageChangedObjects(tx, msg.UserID, pkgIDChangedObjs)
  266. if err != nil {
  267. return err
  268. }
  269. newObjs = append(newObjs, checkedObjs...)
  270. // 对于只有Path发生变化的对象,则检查同Package内有没有同Path的对象
  271. checkedObjs, err = svc.checkPathChangedObjects(tx, msg.UserID, pathChangedObjs)
  272. if err != nil {
  273. return err
  274. }
  275. newObjs = append(newObjs, checkedObjs...)
  276. err = svc.db2.Object().BatchUpdate(tx, newObjs)
  277. if err != nil {
  278. return fmt.Errorf("batch create or update: %w", err)
  279. }
  280. sucs = lo.Map(newObjs, func(obj cdssdk.Object, _ int) cdssdk.ObjectID { return obj.ObjectID })
  281. return nil
  282. })
  283. if err != nil {
  284. logger.Warn(err.Error())
  285. return nil, mq.Failed(errorcode.OperationFailed, "move objects failed")
  286. }
  287. return mq.ReplyOK(coormq.RespMoveObjects(sucs))
  288. }
  289. func (svc *Service) checkPackageChangedObjects(tx db2.SQLContext, userID cdssdk.UserID, objs []cdssdk.Object) ([]cdssdk.Object, error) {
  290. if len(objs) == 0 {
  291. return nil, nil
  292. }
  293. type PackageObjects struct {
  294. PackageID cdssdk.PackageID
  295. ObjectByPath map[string]*cdssdk.Object
  296. }
  297. packages := make(map[cdssdk.PackageID]*PackageObjects)
  298. for _, obj := range objs {
  299. pkg, ok := packages[obj.PackageID]
  300. if !ok {
  301. pkg = &PackageObjects{
  302. PackageID: obj.PackageID,
  303. ObjectByPath: make(map[string]*cdssdk.Object),
  304. }
  305. packages[obj.PackageID] = pkg
  306. }
  307. if pkg.ObjectByPath[obj.Path] == nil {
  308. o := obj
  309. pkg.ObjectByPath[obj.Path] = &o
  310. } else {
  311. // TODO 有两个对象移动到同一个路径,有冲突
  312. }
  313. }
  314. var willUpdateObjs []cdssdk.Object
  315. for _, pkg := range packages {
  316. _, err := svc.db2.Package().GetUserPackage(tx, userID, pkg.PackageID)
  317. if errors.Is(err, gorm.ErrRecordNotFound) {
  318. continue
  319. }
  320. if err != nil {
  321. return nil, fmt.Errorf("getting user package by id: %w", err)
  322. }
  323. existsObjs, err := svc.db2.Object().BatchGetByPackagePath(tx, pkg.PackageID, lo.Keys(pkg.ObjectByPath))
  324. if err != nil {
  325. return nil, fmt.Errorf("batch getting objects by package path: %w", err)
  326. }
  327. // 标记冲突的对象
  328. for _, obj := range existsObjs {
  329. pkg.ObjectByPath[obj.Path] = nil
  330. // TODO 目标Package内有冲突的对象
  331. }
  332. for _, obj := range pkg.ObjectByPath {
  333. if obj == nil {
  334. continue
  335. }
  336. willUpdateObjs = append(willUpdateObjs, *obj)
  337. }
  338. }
  339. return willUpdateObjs, nil
  340. }
  341. func (svc *Service) checkPathChangedObjects(tx db2.SQLContext, userID cdssdk.UserID, objs []cdssdk.Object) ([]cdssdk.Object, error) {
  342. if len(objs) == 0 {
  343. return nil, nil
  344. }
  345. objByPath := make(map[string]*cdssdk.Object)
  346. for _, obj := range objs {
  347. if objByPath[obj.Path] == nil {
  348. o := obj
  349. objByPath[obj.Path] = &o
  350. } else {
  351. // TODO 有两个对象移动到同一个路径,有冲突
  352. }
  353. }
  354. _, err := svc.db2.Package().GetUserPackage(tx, userID, objs[0].PackageID)
  355. if errors.Is(err, gorm.ErrRecordNotFound) {
  356. return nil, nil
  357. }
  358. if err != nil {
  359. return nil, fmt.Errorf("getting user package by id: %w", err)
  360. }
  361. existsObjs, err := svc.db2.Object().BatchGetByPackagePath(tx, objs[0].PackageID, lo.Map(objs, func(obj cdssdk.Object, idx int) string { return obj.Path }))
  362. if err != nil {
  363. return nil, fmt.Errorf("batch getting objects by package path: %w", err)
  364. }
  365. // 不支持两个对象交换位置的情况,因为数据库不支持
  366. for _, obj := range existsObjs {
  367. objByPath[obj.Path] = nil
  368. }
  369. var willMoveObjs []cdssdk.Object
  370. for _, obj := range objByPath {
  371. if obj == nil {
  372. continue
  373. }
  374. willMoveObjs = append(willMoveObjs, *obj)
  375. }
  376. return willMoveObjs, nil
  377. }
  378. func (svc *Service) DeleteObjects(msg *coormq.DeleteObjects) (*coormq.DeleteObjectsResp, *mq.CodeMessage) {
  379. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  380. err := svc.db2.Object().BatchDelete(tx, msg.ObjectIDs)
  381. if err != nil {
  382. return fmt.Errorf("batch deleting objects: %w", err)
  383. }
  384. err = svc.db2.ObjectBlock().BatchDeleteByObjectID(tx, msg.ObjectIDs)
  385. if err != nil {
  386. return fmt.Errorf("batch deleting object blocks: %w", err)
  387. }
  388. err = svc.db2.PinnedObject().BatchDeleteByObjectID(tx, msg.ObjectIDs)
  389. if err != nil {
  390. return fmt.Errorf("batch deleting pinned objects: %w", err)
  391. }
  392. err = svc.db2.ObjectAccessStat().BatchDeleteByObjectID(tx, msg.ObjectIDs)
  393. if err != nil {
  394. return fmt.Errorf("batch deleting object access stats: %w", err)
  395. }
  396. return nil
  397. })
  398. if err != nil {
  399. logger.Warnf("batch deleting objects: %s", err.Error())
  400. return nil, mq.Failed(errorcode.OperationFailed, "batch delete objects failed")
  401. }
  402. return mq.ReplyOK(coormq.RespDeleteObjects())
  403. }
  404. func (svc *Service) CloneObjects(msg *coormq.CloneObjects) (*coormq.CloneObjectsResp, *mq.CodeMessage) {
  405. type CloningObject struct {
  406. Cloning cdsapi.CloningObject
  407. OrgIndex int
  408. }
  409. type PackageClonings struct {
  410. PackageID cdssdk.PackageID
  411. Clonings map[string]CloningObject
  412. }
  413. // TODO 要检查用户是否有Object、Package的权限
  414. clonings := make(map[cdssdk.PackageID]*PackageClonings)
  415. for i, cloning := range msg.Clonings {
  416. pkg, ok := clonings[cloning.NewPackageID]
  417. if !ok {
  418. pkg = &PackageClonings{
  419. PackageID: cloning.NewPackageID,
  420. Clonings: make(map[string]CloningObject),
  421. }
  422. clonings[cloning.NewPackageID] = pkg
  423. }
  424. pkg.Clonings[cloning.NewPath] = CloningObject{
  425. Cloning: cloning,
  426. OrgIndex: i,
  427. }
  428. }
  429. ret := make([]*cdssdk.Object, len(msg.Clonings))
  430. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  431. // 剔除掉新路径已经存在的对象
  432. for _, pkg := range clonings {
  433. exists, err := svc.db2.Object().BatchGetByPackagePath(tx, pkg.PackageID, lo.Keys(pkg.Clonings))
  434. if err != nil {
  435. return fmt.Errorf("batch getting objects by package path: %w", err)
  436. }
  437. for _, obj := range exists {
  438. delete(pkg.Clonings, obj.Path)
  439. }
  440. }
  441. // 删除目的Package不存在的对象
  442. newPkg, err := svc.db2.Package().BatchTestPackageID(tx, lo.Keys(clonings))
  443. if err != nil {
  444. return fmt.Errorf("batch testing package id: %w", err)
  445. }
  446. for _, pkg := range clonings {
  447. if !newPkg[pkg.PackageID] {
  448. delete(clonings, pkg.PackageID)
  449. }
  450. }
  451. var avaiClonings []CloningObject
  452. var avaiObjIDs []cdssdk.ObjectID
  453. for _, pkg := range clonings {
  454. for _, cloning := range pkg.Clonings {
  455. avaiClonings = append(avaiClonings, cloning)
  456. avaiObjIDs = append(avaiObjIDs, cloning.Cloning.ObjectID)
  457. }
  458. }
  459. avaiDetails, err := svc.db2.Object().BatchGetDetails(tx, avaiObjIDs)
  460. if err != nil {
  461. return fmt.Errorf("batch getting object details: %w", err)
  462. }
  463. avaiDetailsMap := make(map[cdssdk.ObjectID]stgmod.ObjectDetail)
  464. for _, detail := range avaiDetails {
  465. avaiDetailsMap[detail.Object.ObjectID] = detail
  466. }
  467. oldAvaiClonings := avaiClonings
  468. avaiClonings = nil
  469. var newObjs []cdssdk.Object
  470. for _, cloning := range oldAvaiClonings {
  471. // 进一步剔除原始对象不存在的情况
  472. detail, ok := avaiDetailsMap[cloning.Cloning.ObjectID]
  473. if !ok {
  474. continue
  475. }
  476. avaiClonings = append(avaiClonings, cloning)
  477. newObj := detail.Object
  478. newObj.ObjectID = 0
  479. newObj.Path = cloning.Cloning.NewPath
  480. newObj.PackageID = cloning.Cloning.NewPackageID
  481. newObjs = append(newObjs, newObj)
  482. }
  483. // 先创建出新对象
  484. err = svc.db2.Object().BatchCreate(tx, &newObjs)
  485. if err != nil {
  486. return fmt.Errorf("batch creating objects: %w", err)
  487. }
  488. // 创建了新对象就能拿到新对象ID,再创建新对象块
  489. var newBlks []stgmod.ObjectBlock
  490. for i, cloning := range avaiClonings {
  491. oldBlks := avaiDetailsMap[cloning.Cloning.ObjectID].Blocks
  492. for _, blk := range oldBlks {
  493. newBlk := blk
  494. newBlk.ObjectID = newObjs[i].ObjectID
  495. newBlks = append(newBlks, newBlk)
  496. }
  497. }
  498. err = svc.db2.ObjectBlock().BatchCreate(tx, newBlks)
  499. if err != nil {
  500. return fmt.Errorf("batch creating object blocks: %w", err)
  501. }
  502. for i, cloning := range avaiClonings {
  503. ret[cloning.OrgIndex] = &newObjs[i]
  504. }
  505. return nil
  506. })
  507. if err != nil {
  508. logger.Warnf("cloning objects: %s", err.Error())
  509. return nil, mq.Failed(errorcode.OperationFailed, err.Error())
  510. }
  511. return mq.ReplyOK(coormq.RespCloneObjects(ret))
  512. }

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