feat(clothes): 支持通过社区公开穿搭只读查看衣物

新增 outfit_uuid 查询参数,非属主可通过社区公开穿搭查看衣物详情。
同时调整点赞通知逻辑,仅在首次点赞时发送通知。
This commit is contained in:
2026-09-16 02:28:59 +08:00
parent e9b9754b57
commit c2519c4b6d
5 changed files with 64 additions and 9 deletions
+47 -3
View File
@@ -391,7 +391,8 @@ func (s *ClothesService) ProcessStatusGetByUUID(ctx context.Context, userID int6
}
// GetByUUID 获取衣物详情。
// 属主始终可见;非属主仅允许携带有效穿搭分享(share_uuid)时,只读查看该分享穿搭内的衣物。
// 属主始终可见;非属主只有两个只读入口:
// 携带有效穿搭分享(share_uuid)查看该分享穿搭内的衣物,或携带社区公开穿搭(outfit_uuid)查看已公开可见穿搭内的衣物。
func (s *ClothesService) GetByUUID(ctx context.Context, userID int64, request *dto.ClothesGetByUUIDRequest) (*dto.ClothesGetByUUIDResponse, *errcode.Error) {
ctx, span := global.Tracer.Start(ctx, "svc.ClothesGetByUUID")
defer span.End()
@@ -404,18 +405,61 @@ func (s *ClothesService) GetByUUID(ctx context.Context, userID int64, request *d
return nil, errcode.DatabaseError
}
if clothes.UserID != userID {
if cerr := s.assertClothesVisibleViaOutfitShare(ctx, userID, request.ShareUUID, clothes.ID); cerr != nil {
if cerr := s.assertClothesVisibleToVisitor(ctx, userID, request, clothes.ID); cerr != nil {
return nil, cerr
}
}
response := buildClothesResponse(clothes)
// 非属主只读查看(穿搭分享):不暴露内部失败原因
// 非属主只读查看:不暴露内部失败原因;社区(未携带 share_uuid 的公开穿搭入口)
// 是陌生人可见场景,属主私有备注同样不下发。
if clothes.UserID != userID {
response.FailReason = ""
if request.ShareUUID == "" {
response.Remark = ""
}
}
return &response, nil
}
// assertClothesVisibleToVisitor 校验非属主可以只读查看该衣物。
// 两个入口互斥:优先穿搭分享,其次社区公开穿搭;都未携带则直接拒绝。
func (s *ClothesService) assertClothesVisibleToVisitor(ctx context.Context, userID int64, request *dto.ClothesGetByUUIDRequest, clothesID int64) *errcode.Error {
if request.ShareUUID != "" {
return s.assertClothesVisibleViaOutfitShare(ctx, userID, request.ShareUUID, clothesID)
}
if request.OutfitUUID != "" {
return s.assertClothesVisibleViaPublicOutfit(ctx, request.OutfitUUID, clothesID)
}
return errcode.ClothesNoPermission
}
// assertClothesVisibleViaPublicOutfit 校验非属主可通过社区公开穿搭只读查看该衣物。
// 可见性口径与 OutfitService.GetByUUID 的非属主分支保持一致(公开 + 审核通过 + 正常)。
func (s *ClothesService) assertClothesVisibleViaPublicOutfit(ctx context.Context, outfitUUID string, clothesID int64) *errcode.Error {
outfit, err := s.outfitDao.OutfitGetByUUID(ctx, outfitUUID)
if err != nil {
if isNotFound(err) {
// 不区分「穿搭不存在」与「穿搭不可见」,统一按无权限返回,避免探测他人穿搭
return errcode.ClothesNoPermission
}
return errcode.DatabaseError
}
if !isOutfitPublicVisible(outfit) {
return errcode.ClothesNoPermission
}
// 该衣物必须属于该公开穿搭的明细。
items, ierr := s.outfitItemDao.OutfitItemListByOutfitID(ctx, outfit.ID)
if ierr != nil {
return errcode.DatabaseError
}
for _, it := range items {
if it.ClothesID == clothesID {
return nil
}
}
return errcode.ClothesNoPermission
}
// assertClothesVisibleViaOutfitShare 校验非属主可通过某穿搭分享只读查看该衣物。
func (s *ClothesService) assertClothesVisibleViaOutfitShare(ctx context.Context, userID int64, shareUUID string, clothesID int64) *errcode.Error {
if shareUUID == "" {