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 20 kB

7 months ago
7 months ago
7 months ago
7 months ago
5 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
1 year ago
7 months ago
1 year ago
7 months ago
1 year ago
7 months ago
1 year ago
7 months ago
1 year ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. package services
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "time"
  7. "github.com/samber/lo"
  8. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  9. "gitlink.org.cn/cloudream/common/pkgs/logger"
  10. "gitlink.org.cn/cloudream/common/utils/sort2"
  11. "gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
  12. "gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
  13. "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
  14. "gitlink.org.cn/cloudream/jcs-pub/client/types"
  15. "gitlink.org.cn/cloudream/jcs-pub/common/models/datamap"
  16. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
  17. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/plans"
  18. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/publock/reqbuilder"
  19. "gorm.io/gorm"
  20. )
  21. // ObjectService 定义了对象服务,负责管理对象的上传、下载等操作。
  22. type ObjectService struct {
  23. *Service
  24. }
  25. // ObjectSvc 返回一个ObjectService的实例。
  26. func (svc *Service) ObjectSvc() *ObjectService {
  27. return &ObjectService{Service: svc}
  28. }
  29. func (svc *ObjectService) GetByPath(req api.ObjectListByPath) (api.ObjectListByPathResp, error) {
  30. var resp api.ObjectListByPathResp
  31. maxKeys := 1000
  32. if req.MaxKeys > 0 {
  33. maxKeys = req.MaxKeys
  34. }
  35. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  36. var err error
  37. _, err = svc.DB.Package().GetByID(tx, req.PackageID)
  38. if err != nil {
  39. return fmt.Errorf("getting package by id: %w", err)
  40. }
  41. if !req.IsPrefix {
  42. obj, err := svc.DB.Object().GetByPath(tx, req.PackageID, req.Path)
  43. if err != nil {
  44. return fmt.Errorf("getting object by path: %w", err)
  45. }
  46. resp.Objects = append(resp.Objects, obj)
  47. return nil
  48. }
  49. if !req.NoRecursive {
  50. resp.Objects, err = svc.DB.Object().GetWithPathPrefixPaged(tx, req.PackageID, req.Path, req.ContinuationToken, maxKeys)
  51. if err != nil {
  52. return fmt.Errorf("getting objects with prefix: %w", err)
  53. }
  54. if len(resp.Objects) > 0 {
  55. resp.NextContinuationToken = resp.Objects[len(resp.Objects)-1].Path
  56. }
  57. return nil
  58. }
  59. resp.Objects, resp.CommonPrefixes, resp.NextContinuationToken, err = svc.DB.Object().GetByPrefixGroupedPaged(tx, req.PackageID, req.Path, req.ContinuationToken, maxKeys)
  60. return err
  61. })
  62. return resp, err
  63. }
  64. func (svc *ObjectService) GetByIDs(objectIDs []types.ObjectID) ([]*types.Object, error) {
  65. var ret []*types.Object
  66. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  67. objs, err := svc.DB.Object().BatchGet(tx, objectIDs)
  68. if err != nil {
  69. return err
  70. }
  71. objMp := make(map[types.ObjectID]types.Object)
  72. for _, obj := range objs {
  73. objMp[obj.ObjectID] = obj
  74. }
  75. for _, objID := range objectIDs {
  76. o, ok := objMp[objID]
  77. if ok {
  78. ret = append(ret, &o)
  79. } else {
  80. ret = append(ret, nil)
  81. }
  82. }
  83. return err
  84. })
  85. return ret, err
  86. }
  87. func (svc *ObjectService) UpdateInfo(updatings []api.UpdatingObject) ([]types.ObjectID, error) {
  88. var sucs []types.ObjectID
  89. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  90. updatings = sort2.Sort(updatings, func(o1, o2 api.UpdatingObject) int {
  91. return sort2.Cmp(o1.ObjectID, o2.ObjectID)
  92. })
  93. objIDs := make([]types.ObjectID, len(updatings))
  94. for i, obj := range updatings {
  95. objIDs[i] = obj.ObjectID
  96. }
  97. oldObjs, err := svc.DB.Object().BatchGet(tx, objIDs)
  98. if err != nil {
  99. return fmt.Errorf("batch getting objects: %w", err)
  100. }
  101. oldObjIDs := make([]types.ObjectID, len(oldObjs))
  102. for i, obj := range oldObjs {
  103. oldObjIDs[i] = obj.ObjectID
  104. }
  105. avaiUpdatings, notExistsObjs := pickByObjectIDs(updatings, oldObjIDs, func(obj api.UpdatingObject) types.ObjectID { return obj.ObjectID })
  106. if len(notExistsObjs) > 0 {
  107. // TODO 部分对象已经不存在
  108. }
  109. newObjs := make([]types.Object, len(avaiUpdatings))
  110. for i := range newObjs {
  111. newObjs[i] = oldObjs[i]
  112. avaiUpdatings[i].ApplyTo(&newObjs[i])
  113. }
  114. err = svc.DB.Object().BatchUpdate(tx, newObjs)
  115. if err != nil {
  116. return fmt.Errorf("batch create or update: %w", err)
  117. }
  118. sucs = lo.Map(newObjs, func(obj types.Object, _ int) types.ObjectID { return obj.ObjectID })
  119. return nil
  120. })
  121. return sucs, err
  122. }
  123. // 根据objIDs从objs中挑选Object。
  124. // len(objs) >= len(objIDs)
  125. func pickByObjectIDs[T any](objs []T, objIDs []types.ObjectID, getID func(T) types.ObjectID) (picked []T, notFound []T) {
  126. objIdx := 0
  127. idIdx := 0
  128. for idIdx < len(objIDs) && objIdx < len(objs) {
  129. if getID(objs[objIdx]) < objIDs[idIdx] {
  130. notFound = append(notFound, objs[objIdx])
  131. objIdx++
  132. continue
  133. }
  134. picked = append(picked, objs[objIdx])
  135. objIdx++
  136. idIdx++
  137. }
  138. return
  139. }
  140. func (svc *ObjectService) Move(movings []api.MovingObject) ([]types.ObjectID, error) {
  141. var sucs []types.ObjectID
  142. var evt []*datamap.BodyObjectInfoUpdated
  143. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  144. movings = sort2.Sort(movings, func(o1, o2 api.MovingObject) int {
  145. return sort2.Cmp(o1.ObjectID, o2.ObjectID)
  146. })
  147. objIDs := make([]types.ObjectID, len(movings))
  148. for i, obj := range movings {
  149. objIDs[i] = obj.ObjectID
  150. }
  151. oldObjs, err := svc.DB.Object().BatchGet(tx, objIDs)
  152. if err != nil {
  153. return fmt.Errorf("batch getting objects: %w", err)
  154. }
  155. oldObjIDs := make([]types.ObjectID, len(oldObjs))
  156. for i, obj := range oldObjs {
  157. oldObjIDs[i] = obj.ObjectID
  158. }
  159. // 找出仍在数据库的Object
  160. avaiMovings, notExistsObjs := pickByObjectIDs(movings, oldObjIDs, func(obj api.MovingObject) types.ObjectID { return obj.ObjectID })
  161. if len(notExistsObjs) > 0 {
  162. // TODO 部分对象已经不存在
  163. }
  164. // 筛选出PackageID变化、Path变化的对象,这两种对象要检测改变后是否有冲突
  165. var pkgIDChangedObjs []types.Object
  166. var pathChangedObjs []types.Object
  167. for i := range avaiMovings {
  168. if avaiMovings[i].PackageID != oldObjs[i].PackageID {
  169. newObj := oldObjs[i]
  170. avaiMovings[i].ApplyTo(&newObj)
  171. pkgIDChangedObjs = append(pkgIDChangedObjs, newObj)
  172. } else if avaiMovings[i].Path != oldObjs[i].Path {
  173. newObj := oldObjs[i]
  174. avaiMovings[i].ApplyTo(&newObj)
  175. pathChangedObjs = append(pathChangedObjs, newObj)
  176. }
  177. }
  178. var newObjs []types.Object
  179. // 对于PackageID发生变化的对象,需要检查目标Package内是否存在同Path的对象
  180. checkedObjs, err := svc.checkPackageChangedObjects(tx, pkgIDChangedObjs)
  181. if err != nil {
  182. return err
  183. }
  184. newObjs = append(newObjs, checkedObjs...)
  185. // 对于只有Path发生变化的对象,则检查同Package内有没有同Path的对象
  186. checkedObjs, err = svc.checkPathChangedObjects(tx, pathChangedObjs)
  187. if err != nil {
  188. return err
  189. }
  190. newObjs = append(newObjs, checkedObjs...)
  191. err = svc.DB.Object().BatchUpdate(tx, newObjs)
  192. if err != nil {
  193. return fmt.Errorf("batch create or update: %w", err)
  194. }
  195. sucs = lo.Map(newObjs, func(obj types.Object, _ int) types.ObjectID { return obj.ObjectID })
  196. evt = lo.Map(newObjs, func(obj types.Object, _ int) *datamap.BodyObjectInfoUpdated {
  197. return &datamap.BodyObjectInfoUpdated{
  198. Object: obj,
  199. }
  200. })
  201. return nil
  202. })
  203. if err != nil {
  204. logger.Warn(err.Error())
  205. return nil, err
  206. }
  207. for _, e := range evt {
  208. svc.EvtPub.Publish(e)
  209. }
  210. return sucs, nil
  211. }
  212. func (svc *ObjectService) Download(req downloader.DownloadReqeust) (*downloader.Downloading, error) {
  213. iter := svc.Downloader.DownloadObjects([]downloader.DownloadReqeust{req})
  214. // 初始化下载过程
  215. downloading, err := iter.MoveNext()
  216. if err != nil {
  217. return nil, err
  218. }
  219. if downloading.Object == nil {
  220. return nil, fmt.Errorf("object %v not found", req.ObjectID)
  221. }
  222. return downloading, nil
  223. }
  224. func (svc *Service) checkPackageChangedObjects(tx db.SQLContext, objs []types.Object) ([]types.Object, error) {
  225. if len(objs) == 0 {
  226. return nil, nil
  227. }
  228. type PackageObjects struct {
  229. PackageID types.PackageID
  230. ObjectByPath map[string]*types.Object
  231. }
  232. packages := make(map[types.PackageID]*PackageObjects)
  233. for _, obj := range objs {
  234. pkg, ok := packages[obj.PackageID]
  235. if !ok {
  236. pkg = &PackageObjects{
  237. PackageID: obj.PackageID,
  238. ObjectByPath: make(map[string]*types.Object),
  239. }
  240. packages[obj.PackageID] = pkg
  241. }
  242. if pkg.ObjectByPath[obj.Path] == nil {
  243. o := obj
  244. pkg.ObjectByPath[obj.Path] = &o
  245. } else {
  246. // TODO 有两个对象移动到同一个路径,有冲突
  247. }
  248. }
  249. var willUpdateObjs []types.Object
  250. for _, pkg := range packages {
  251. _, err := svc.DB.Package().GetByID(tx, pkg.PackageID)
  252. if errors.Is(err, gorm.ErrRecordNotFound) {
  253. continue
  254. }
  255. if err != nil {
  256. return nil, fmt.Errorf("getting package by id: %w", err)
  257. }
  258. existsObjs, err := svc.DB.Object().BatchGetByPackagePath(tx, pkg.PackageID, lo.Keys(pkg.ObjectByPath))
  259. if err != nil {
  260. return nil, fmt.Errorf("batch getting objects by package path: %w", err)
  261. }
  262. // 标记冲突的对象
  263. for _, obj := range existsObjs {
  264. pkg.ObjectByPath[obj.Path] = nil
  265. // TODO 目标Package内有冲突的对象
  266. }
  267. for _, obj := range pkg.ObjectByPath {
  268. if obj == nil {
  269. continue
  270. }
  271. willUpdateObjs = append(willUpdateObjs, *obj)
  272. }
  273. }
  274. return willUpdateObjs, nil
  275. }
  276. func (svc *Service) checkPathChangedObjects(tx db.SQLContext, objs []types.Object) ([]types.Object, error) {
  277. if len(objs) == 0 {
  278. return nil, nil
  279. }
  280. objByPath := make(map[string]*types.Object)
  281. for _, obj := range objs {
  282. if objByPath[obj.Path] == nil {
  283. o := obj
  284. objByPath[obj.Path] = &o
  285. } else {
  286. // TODO 有两个对象移动到同一个路径,有冲突
  287. }
  288. }
  289. _, err := svc.DB.Package().GetByID(tx, objs[0].PackageID)
  290. if errors.Is(err, gorm.ErrRecordNotFound) {
  291. return nil, nil
  292. }
  293. if err != nil {
  294. return nil, fmt.Errorf("getting package by id: %w", err)
  295. }
  296. existsObjs, err := svc.DB.Object().BatchGetByPackagePath(tx, objs[0].PackageID, lo.Map(objs, func(obj types.Object, idx int) string { return obj.Path }))
  297. if err != nil {
  298. return nil, fmt.Errorf("batch getting objects by package path: %w", err)
  299. }
  300. // 不支持两个对象交换位置的情况,因为数据库不支持
  301. for _, obj := range existsObjs {
  302. objByPath[obj.Path] = nil
  303. }
  304. var willMoveObjs []types.Object
  305. for _, obj := range objByPath {
  306. if obj == nil {
  307. continue
  308. }
  309. willMoveObjs = append(willMoveObjs, *obj)
  310. }
  311. return willMoveObjs, nil
  312. }
  313. func (svc *ObjectService) Delete(objectIDs []types.ObjectID) error {
  314. var sucs []types.ObjectID
  315. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  316. avaiIDs, err := svc.DB.Object().BatchTestObjectID(tx, objectIDs)
  317. if err != nil {
  318. return fmt.Errorf("batch testing object id: %w", err)
  319. }
  320. sucs = lo.Keys(avaiIDs)
  321. return svc.DB.Object().BatchDeleteComplete(tx, sucs)
  322. })
  323. if err != nil {
  324. return err
  325. }
  326. for _, objID := range sucs {
  327. svc.EvtPub.Publish(&datamap.BodyObjectDeleted{
  328. ObjectID: objID,
  329. })
  330. }
  331. return nil
  332. }
  333. func (svc *ObjectService) Clone(clonings []api.CloningObject) ([]*types.Object, error) {
  334. type CloningObject struct {
  335. Cloning api.CloningObject
  336. OrgIndex int
  337. }
  338. type PackageClonings struct {
  339. PackageID types.PackageID
  340. Clonings map[string]CloningObject
  341. }
  342. var evt []*datamap.BodyNewOrUpdateObject
  343. cloningMap := make(map[types.PackageID]*PackageClonings)
  344. for i, cloning := range clonings {
  345. pkg, ok := cloningMap[cloning.NewPackageID]
  346. if !ok {
  347. pkg = &PackageClonings{
  348. PackageID: cloning.NewPackageID,
  349. Clonings: make(map[string]CloningObject),
  350. }
  351. cloningMap[cloning.NewPackageID] = pkg
  352. }
  353. pkg.Clonings[cloning.NewPath] = CloningObject{
  354. Cloning: cloning,
  355. OrgIndex: i,
  356. }
  357. }
  358. ret := make([]*types.Object, len(cloningMap))
  359. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  360. // 剔除掉新路径已经存在的对象
  361. for _, pkg := range cloningMap {
  362. exists, err := svc.DB.Object().BatchGetByPackagePath(tx, pkg.PackageID, lo.Keys(pkg.Clonings))
  363. if err != nil {
  364. return fmt.Errorf("batch getting objects by package path: %w", err)
  365. }
  366. for _, obj := range exists {
  367. delete(pkg.Clonings, obj.Path)
  368. }
  369. }
  370. // 删除目的Package不存在的对象
  371. newPkg, err := svc.DB.Package().BatchTestPackageID(tx, lo.Keys(cloningMap))
  372. if err != nil {
  373. return fmt.Errorf("batch testing package id: %w", err)
  374. }
  375. for _, pkg := range cloningMap {
  376. if !newPkg[pkg.PackageID] {
  377. delete(cloningMap, pkg.PackageID)
  378. }
  379. }
  380. var avaiClonings []CloningObject
  381. var avaiObjIDs []types.ObjectID
  382. for _, pkg := range cloningMap {
  383. for _, cloning := range pkg.Clonings {
  384. avaiClonings = append(avaiClonings, cloning)
  385. avaiObjIDs = append(avaiObjIDs, cloning.Cloning.ObjectID)
  386. }
  387. }
  388. avaiDetails, err := svc.DB.Object().BatchGetDetails(tx, avaiObjIDs)
  389. if err != nil {
  390. return fmt.Errorf("batch getting object details: %w", err)
  391. }
  392. avaiDetailsMap := make(map[types.ObjectID]types.ObjectDetail)
  393. for _, detail := range avaiDetails {
  394. avaiDetailsMap[detail.Object.ObjectID] = detail
  395. }
  396. oldAvaiClonings := avaiClonings
  397. avaiClonings = nil
  398. var newObjs []types.Object
  399. for _, cloning := range oldAvaiClonings {
  400. // 进一步剔除原始对象不存在的情况
  401. detail, ok := avaiDetailsMap[cloning.Cloning.ObjectID]
  402. if !ok {
  403. continue
  404. }
  405. avaiClonings = append(avaiClonings, cloning)
  406. newObj := detail.Object
  407. newObj.ObjectID = 0
  408. newObj.Path = cloning.Cloning.NewPath
  409. newObj.PackageID = cloning.Cloning.NewPackageID
  410. newObjs = append(newObjs, newObj)
  411. }
  412. // 先创建出新对象
  413. err = svc.DB.Object().BatchCreate(tx, &newObjs)
  414. if err != nil {
  415. return fmt.Errorf("batch creating objects: %w", err)
  416. }
  417. // 创建了新对象就能拿到新对象ID,再创建新对象块
  418. var newBlks []types.ObjectBlock
  419. for i, cloning := range avaiClonings {
  420. oldBlks := avaiDetailsMap[cloning.Cloning.ObjectID].Blocks
  421. for _, blk := range oldBlks {
  422. newBlk := blk
  423. newBlk.ObjectID = newObjs[i].ObjectID
  424. newBlks = append(newBlks, newBlk)
  425. }
  426. }
  427. err = svc.DB.ObjectBlock().BatchCreate(tx, newBlks)
  428. if err != nil {
  429. return fmt.Errorf("batch creating object blocks: %w", err)
  430. }
  431. for i, cloning := range avaiClonings {
  432. ret[cloning.OrgIndex] = &newObjs[i]
  433. }
  434. for i, cloning := range avaiClonings {
  435. var evtBlks []datamap.BlockDistributionObjectInfo
  436. blkType := getBlockTypeFromRed(newObjs[i].Redundancy)
  437. oldBlks := avaiDetailsMap[cloning.Cloning.ObjectID].Blocks
  438. for _, blk := range oldBlks {
  439. evtBlks = append(evtBlks, datamap.BlockDistributionObjectInfo{
  440. BlockType: blkType,
  441. Index: blk.Index,
  442. UserSpaceID: blk.UserSpaceID,
  443. })
  444. }
  445. evt = append(evt, &datamap.BodyNewOrUpdateObject{
  446. Info: newObjs[i],
  447. BlockDistribution: evtBlks,
  448. })
  449. }
  450. return nil
  451. })
  452. if err != nil {
  453. logger.Warnf("cloning objects: %s", err.Error())
  454. return nil, err
  455. }
  456. for _, e := range evt {
  457. svc.EvtPub.Publish(e)
  458. }
  459. return ret, nil
  460. }
  461. // GetPackageObjects 获取包中的对象列表。
  462. // userID: 用户ID。
  463. // packageID: 包ID。
  464. // 返回值: 对象列表和错误信息。
  465. func (svc *ObjectService) GetPackageObjects(packageID types.PackageID) ([]types.Object, error) {
  466. return svc.DB.Object().GetPackageObjects(svc.DB.DefCtx(), packageID)
  467. }
  468. func (svc *ObjectService) GetObjectDetails(objectIDs []types.ObjectID) ([]*types.ObjectDetail, error) {
  469. detailsMp := make(map[types.ObjectID]*types.ObjectDetail)
  470. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  471. var err error
  472. objectIDs = sort2.SortAsc(objectIDs)
  473. // 根据ID依次查询Object,ObjectBlock,PinnedObject,并根据升序的特点进行合并
  474. objs, err := svc.DB.Object().BatchGet(tx, objectIDs)
  475. if err != nil {
  476. return fmt.Errorf("batch get objects: %w", err)
  477. }
  478. for _, obj := range objs {
  479. detailsMp[obj.ObjectID] = &types.ObjectDetail{
  480. Object: obj,
  481. }
  482. }
  483. // 查询合并
  484. blocks, err := svc.DB.ObjectBlock().BatchGetByObjectID(tx, objectIDs)
  485. if err != nil {
  486. return fmt.Errorf("batch get object blocks: %w", err)
  487. }
  488. for _, block := range blocks {
  489. d := detailsMp[block.ObjectID]
  490. d.Blocks = append(d.Blocks, block)
  491. }
  492. // 查询合并
  493. pinneds, err := svc.DB.PinnedObject().BatchGetByObjectID(tx, objectIDs)
  494. if err != nil {
  495. return fmt.Errorf("batch get pinned objects: %w", err)
  496. }
  497. for _, pinned := range pinneds {
  498. d := detailsMp[pinned.ObjectID]
  499. d.PinnedAt = append(d.PinnedAt, pinned.UserSpaceID)
  500. }
  501. return nil
  502. })
  503. if err != nil {
  504. logger.Warn(err.Error())
  505. return nil, err
  506. }
  507. details := make([]*types.ObjectDetail, len(objectIDs))
  508. for i, objID := range objectIDs {
  509. details[i] = detailsMp[objID]
  510. }
  511. return details, nil
  512. }
  513. func (svc *ObjectService) NewMultipartUploadObject(packageID types.PackageID, path string) (types.Object, error) {
  514. var obj types.Object
  515. err := svc.DB.DoTx(func(tx db.SQLContext) error {
  516. oldObj, err := svc.DB.Object().GetByPath(tx, packageID, path)
  517. if err == nil {
  518. obj = oldObj
  519. err := svc.DB.ObjectBlock().DeleteByObjectID(tx, obj.ObjectID)
  520. if err != nil {
  521. return fmt.Errorf("delete object blocks: %w", err)
  522. }
  523. obj.FileHash = types.EmptyHash
  524. obj.Size = 0
  525. obj.Redundancy = types.NewMultipartUploadRedundancy()
  526. obj.UpdateTime = time.Now()
  527. err = svc.DB.Object().BatchUpdate(tx, []types.Object{obj})
  528. if err != nil {
  529. return fmt.Errorf("update object: %w", err)
  530. }
  531. return nil
  532. }
  533. obj = types.Object{
  534. PackageID: packageID,
  535. Path: path,
  536. FileHash: types.EmptyHash,
  537. Size: 0,
  538. Redundancy: types.NewMultipartUploadRedundancy(),
  539. CreateTime: time.Now(),
  540. UpdateTime: time.Now(),
  541. }
  542. objID, err := svc.DB.Object().Create(tx, obj)
  543. if err != nil {
  544. return fmt.Errorf("create object: %w", err)
  545. }
  546. obj.ObjectID = objID
  547. return nil
  548. })
  549. if err != nil {
  550. logger.Warnf("new multipart upload object: %s", err.Error())
  551. return types.Object{}, err
  552. }
  553. return obj, nil
  554. }
  555. func (svc *ObjectService) CompleteMultipartUpload(objectID types.ObjectID, indexes []int) (types.Object, error) {
  556. if len(indexes) == 0 {
  557. return types.Object{}, fmt.Errorf("no block indexes specified")
  558. }
  559. objDe, err := db.DoTx11(svc.DB, svc.DB.Object().GetDetail, objectID)
  560. if err != nil {
  561. return types.Object{}, err
  562. }
  563. _, ok := objDe.Object.Redundancy.(*types.MultipartUploadRedundancy)
  564. if !ok {
  565. return types.Object{}, fmt.Errorf("object %v is not a multipart upload", objectID)
  566. }
  567. if len(objDe.Blocks) == 0 {
  568. return types.Object{}, fmt.Errorf("object %v has no blocks", objectID)
  569. }
  570. objBlkMap := make(map[int]types.ObjectBlock)
  571. for _, blk := range objDe.Blocks {
  572. objBlkMap[blk.Index] = blk
  573. }
  574. lockBld := reqbuilder.NewBuilder()
  575. var compBlks []types.ObjectBlock
  576. var compBlkSpaces []types.UserSpaceDetail
  577. var targetSpace types.UserSpaceDetail
  578. for i, idx := range indexes {
  579. blk, ok := objBlkMap[idx]
  580. if !ok {
  581. return types.Object{}, fmt.Errorf("block %d not found in object %v", idx, objectID)
  582. }
  583. stg := svc.UserSpaceMeta.Get(blk.UserSpaceID)
  584. if stg == nil {
  585. return types.Object{}, fmt.Errorf("storage of user space %d not found", blk.UserSpaceID)
  586. }
  587. compBlks = append(compBlks, blk)
  588. compBlkSpaces = append(compBlkSpaces, *stg)
  589. if i == 0 {
  590. targetSpace = *stg
  591. }
  592. lockBld.UserSpace().Buzy(stg.UserSpace.UserSpaceID)
  593. }
  594. mutex, err := lockBld.MutexLock(svc.PubLock)
  595. if err != nil {
  596. return types.Object{}, fmt.Errorf("acquire lock: %w", err)
  597. }
  598. defer mutex.Unlock()
  599. bld := exec.NewPlanBuilder()
  600. err = plans.CompleteMultipart(compBlks, compBlkSpaces, targetSpace, "shard", bld)
  601. if err != nil {
  602. return types.Object{}, err
  603. }
  604. exeCtx := exec.NewExecContext()
  605. exec.SetValueByType(exeCtx, svc.StgPool)
  606. ret, err := bld.Execute(exeCtx).Wait(context.Background())
  607. if err != nil {
  608. return types.Object{}, err
  609. }
  610. shardInfo := ret["shard"].(*ops2.FileInfoValue)
  611. err = db.DoTx10(svc.DB, svc.DB.Object().BatchUpdateRedundancy, []db.UpdatingObjectRedundancy{
  612. {
  613. ObjectID: objectID,
  614. FileHash: shardInfo.Hash,
  615. Size: shardInfo.Size,
  616. Redundancy: types.NewNoneRedundancy(),
  617. Blocks: []types.ObjectBlock{{
  618. ObjectID: objectID,
  619. Index: 0,
  620. UserSpaceID: targetSpace.UserSpace.UserSpaceID,
  621. FileHash: shardInfo.Hash,
  622. Size: shardInfo.Size,
  623. }},
  624. },
  625. })
  626. if err != nil {
  627. return types.Object{}, err
  628. }
  629. obj, err := svc.DB.Object().GetByID(svc.DB.DefCtx(), objectID)
  630. if err != nil {
  631. return types.Object{}, err
  632. }
  633. return obj, nil
  634. }

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