feat: refine content media access rules

This commit is contained in:
2026-01-13 11:06:32 +08:00
parent 674b66300d
commit 84c8d1b394
3 changed files with 74 additions and 3 deletions

View File

@@ -204,16 +204,39 @@ func (s *content) Get(ctx context.Context, tenantID, userID, id int64) (*content
if item.UserID == uid {
hasAccess = true // Owner
} else {
isAdmin, err := s.isTenantAdmin(ctx, item.TenantID, uid)
if err != nil {
return nil, err
}
if isAdmin {
hasAccess = true
}
}
if !hasAccess {
// Check Purchase
exists, _ := models.ContentAccessQuery.WithContext(ctx).
exists, err := models.ContentAccessQuery.WithContext(ctx).
Where(models.ContentAccessQuery.UserID.Eq(uid),
models.ContentAccessQuery.ContentID.Eq(id),
models.ContentAccessQuery.Status.Eq(consts.ContentAccessStatusActive)).
Exists()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
if exists {
hasAccess = true
}
}
if !hasAccess && item.Visibility == consts.ContentVisibilityTenantOnly {
isMember, err := s.isTenantMember(ctx, item.TenantID, uid)
if err != nil {
return nil, err
}
if isMember {
hasAccess = true
}
}
}
// Filter Assets based on Access
@@ -250,7 +273,7 @@ func (s *content) Get(ctx context.Context, tenantID, userID, id int64) (*content
IsFavorited: isFavorited,
}
// Pass IsPurchased/HasAccess info to frontend?
detail.ContentItem.IsPurchased = hasAccess // Update DTO field logic if needed. IsPurchased usually means "Bought". Owner implies access but not necessarily purchased. But for UI "Play" button, IsPurchased=true is fine.
detail.ContentItem.IsPurchased = hasAccess // 购买或具备可访问权限时统一展示为已解锁。
return detail, nil
}

View File

@@ -116,14 +116,21 @@ func (s *ContentTestSuite) Test_Get() {
models.ContentQuery.WithContext(ctx).Create(content)
member := &models.User{Nickname: "Member", Username: "member1", Phone: "13800000003"}
admin := &models.User{Nickname: "Admin", Username: "admin1", Phone: "13800000005"}
guest := &models.User{Nickname: "Guest", Username: "guest1", Phone: "13800000004"}
models.UserQuery.WithContext(ctx).Create(member, guest)
models.UserQuery.WithContext(ctx).Create(member, admin, guest)
models.TenantUserQuery.WithContext(ctx).Create(&models.TenantUser{
TenantID: 1,
UserID: member.ID,
Role: types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
Status: consts.UserStatusVerified,
})
models.TenantUserQuery.WithContext(ctx).Create(&models.TenantUser{
TenantID: 1,
UserID: admin.ID,
Role: types.Array[consts.TenantUserRole]{consts.TenantUserRoleTenantAdmin},
Status: consts.UserStatusVerified,
})
tenantOnly := &models.Content{
TenantID: 1,
@@ -152,6 +159,38 @@ func (s *ContentTestSuite) Test_Get() {
}
models.ContentAssetQuery.WithContext(ctx).Create(ca)
tenantMain := &models.MediaAsset{
TenantID: 1,
UserID: author.ID,
ObjectKey: "tenant_main.mp4",
Type: consts.MediaAssetTypeVideo,
}
tenantPrev := &models.MediaAsset{
TenantID: 1,
UserID: author.ID,
ObjectKey: "tenant_preview.mp4",
Type: consts.MediaAssetTypeVideo,
}
models.MediaAssetQuery.WithContext(ctx).Create(tenantMain, tenantPrev)
models.ContentAssetQuery.WithContext(ctx).Create(
&models.ContentAsset{
TenantID: 1,
UserID: author.ID,
ContentID: tenantOnly.ID,
AssetID: tenantMain.ID,
Sort: 1,
Role: consts.ContentAssetRoleMain,
},
&models.ContentAsset{
TenantID: 1,
UserID: author.ID,
ContentID: tenantOnly.ID,
AssetID: tenantPrev.ID,
Sort: 2,
Role: consts.ContentAssetRolePreview,
},
)
// Set context to author
ctx = context.WithValue(ctx, consts.CtxKeyUser, author.ID)
@@ -168,6 +207,14 @@ func (s *ContentTestSuite) Test_Get() {
detail, err := Content.Get(ctx, tenantID, member.ID, tenantOnly.ID)
So(err, ShouldBeNil)
So(detail.Title, ShouldEqual, "Member Only")
So(len(detail.MediaUrls), ShouldEqual, 2)
})
Convey("should allow tenant_only content for admin", func() {
detail, err := Content.Get(ctx, tenantID, admin.ID, tenantOnly.ID)
So(err, ShouldBeNil)
So(detail.Title, ShouldEqual, "Member Only")
So(len(detail.MediaUrls), ShouldEqual, 2)
})
Convey("should reject tenant_only content for non-member", func() {