fix: enforce content visibility and tenant login

This commit is contained in:
2026-01-13 09:28:45 +08:00
parent ca7c799344
commit 342987334a
4 changed files with 231 additions and 27 deletions

View File

@@ -31,6 +31,8 @@ func (s *content) List(ctx context.Context, tenantID int64, filter *content_dto.
if tenantID > 0 {
q = q.Where(tbl.TenantID.Eq(tenantID))
}
// 私密内容不参与公开列表展示。
q = q.Where(tbl.Visibility.Neq(consts.ContentVisibilityPrivate))
if filter.AuthorID != nil && *filter.AuthorID > 0 {
q = q.Where(tbl.UserID.Eq(*filter.AuthorID))
}
@@ -622,28 +624,99 @@ func (s *content) toContentItemDTO(item *models.Content, price float64, authorIs
}
func (s *content) ensureContentReadable(ctx context.Context, userID int64, item *models.Content) error {
if item.Status == consts.ContentStatusPublished {
if item.Status != consts.ContentStatusPublished {
// 未发布内容仅允许作者或租户管理员查看。
return s.ensureContentOwnerOrAdmin(ctx, userID, item, "内容未发布")
}
switch item.Visibility {
case consts.ContentVisibilityPublic, "":
return nil
case consts.ContentVisibilityPrivate:
// 私密内容仅允许作者或租户管理员查看。
return s.ensureContentOwnerOrAdmin(ctx, userID, item, "内容不可见")
case consts.ContentVisibilityTenantOnly:
// 店铺内可见内容需要登录并满足成员/购买条件。
if userID == 0 {
return errorx.ErrForbidden.WithMsg("内容仅限本店铺成员访问")
}
if item.UserID == userID {
return nil
}
isAdmin, err := s.isTenantAdmin(ctx, item.TenantID, userID)
if err != nil {
return err
}
if isAdmin {
return nil
}
// 已购买用户允许访问。
hasAccess, err := models.ContentAccessQuery.WithContext(ctx).
Where(models.ContentAccessQuery.UserID.Eq(userID),
models.ContentAccessQuery.ContentID.Eq(item.ID),
models.ContentAccessQuery.Status.Eq(consts.ContentAccessStatusActive)).
Exists()
if err != nil {
return errorx.ErrDatabaseError.WithCause(err)
}
if hasAccess {
return nil
}
isMember, err := s.isTenantMember(ctx, item.TenantID, userID)
if err != nil {
return err
}
if !isMember {
return errorx.ErrForbidden.WithMsg("内容仅限本店铺成员访问")
}
return nil
default:
return nil
}
// 未发布内容仅允许作者或租户管理员查看。
}
func (s *content) ensureContentOwnerOrAdmin(ctx context.Context, userID int64, item *models.Content, msg string) error {
if userID == 0 {
return errorx.ErrForbidden.WithMsg("内容未发布")
return errorx.ErrForbidden.WithMsg(msg)
}
if item.UserID == userID {
return nil
}
isAdmin, err := s.isTenantAdmin(ctx, item.TenantID, userID)
if err != nil {
return err
}
if !isAdmin {
return errorx.ErrForbidden.WithMsg(msg)
}
return nil
}
func (s *content) isTenantAdmin(ctx context.Context, tenantID, userID int64) (bool, error) {
if tenantID == 0 || userID == 0 {
return false, nil
}
exists, err := models.TenantUserQuery.WithContext(ctx).
Where(models.TenantUserQuery.TenantID.Eq(item.TenantID),
Where(models.TenantUserQuery.TenantID.Eq(tenantID),
models.TenantUserQuery.UserID.Eq(userID),
models.TenantUserQuery.Role.Contains(types.Array[consts.TenantUserRole]{consts.TenantUserRoleTenantAdmin})).
Exists()
if err != nil {
return errorx.ErrDatabaseError.WithCause(err)
return false, errorx.ErrDatabaseError.WithCause(err)
}
if !exists {
return errorx.ErrForbidden.WithMsg("内容未发布")
return exists, nil
}
func (s *content) isTenantMember(ctx context.Context, tenantID, userID int64) (bool, error) {
if tenantID == 0 || userID == 0 {
return false, nil
}
return nil
tbl, q := models.TenantUserQuery.QueryContext(ctx)
exists, err := q.Where(tbl.TenantID.Eq(tenantID), tbl.UserID.Eq(userID), tbl.Status.Eq(consts.UserStatusVerified)).Exists()
if err != nil {
return false, errorx.ErrDatabaseError.WithCause(err)
}
return exists, nil
}
func (s *content) toMediaURLs(assets []*models.ContentAsset) []content_dto.MediaURL {