diff --git a/backend/pkg/proto/asset/asset.pb.go b/backend/pkg/proto/asset/asset.pb.go index cb43c99..05faee7 100644 --- a/backend/pkg/proto/asset/asset.pb.go +++ b/backend/pkg/proto/asset/asset.pb.go @@ -2247,6 +2247,8 @@ func (x *UnlikeAssetResponse) GetLikeCount() int32 { type CheckAssetLikeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID + UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // 用户ID + StarId int64 `protobuf:"varint,3,opt,name=star_id,json=starId,proto3" json:"star_id,omitempty"` // 明星ID unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2288,6 +2290,20 @@ func (x *CheckAssetLikeRequest) GetAssetId() int64 { return 0 } +func (x *CheckAssetLikeRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *CheckAssetLikeRequest) GetStarId() int64 { + if x != nil { + return x.StarId + } + return 0 +} + // 检查是否已点赞响应 type CheckAssetLikeResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -3828,9 +3844,11 @@ const file_asset_proto_rawDesc = "" + "\x13UnlikeAssetResponse\x120\n" + "\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x1d\n" + "\n" + - "like_count\x18\x02 \x01(\x05R\tlikeCount\"2\n" + + "like_count\x18\x02 \x01(\x05R\tlikeCount\"d\n" + "\x15CheckAssetLikeRequest\x12\x19\n" + - "\basset_id\x18\x01 \x01(\x03R\aassetId\"e\n" + + "\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x17\n" + + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x17\n" + + "\astar_id\x18\x03 \x01(\x03R\x06starId\"e\n" + "\x16CheckAssetLikeResponse\x120\n" + "\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x19\n" + "\bis_liked\x18\x02 \x01(\bR\aisLiked\"\x87\x01\n" + diff --git a/backend/proto/asset.proto b/backend/proto/asset.proto index dac2372..0ccfe4a 100644 --- a/backend/proto/asset.proto +++ b/backend/proto/asset.proto @@ -279,6 +279,8 @@ message UnlikeAssetResponse { // 检查是否已点赞请求(内部RPC,供Social Service调用) message CheckAssetLikeRequest { int64 asset_id = 1; // 资产ID + int64 user_id = 2; // 用户ID + int64 star_id = 3; // 明星ID } // 检查是否已点赞响应 diff --git a/backend/scripts/migrations/migrate_drop_asset_likes_star_constraint.sql b/backend/scripts/migrations/migrate_drop_asset_likes_star_constraint.sql new file mode 100644 index 0000000..d3b54eb --- /dev/null +++ b/backend/scripts/migrations/migrate_drop_asset_likes_star_constraint.sql @@ -0,0 +1,6 @@ +-- Drop the overly strict unique constraint that prevents users from liking +-- the same asset across different exhibitions. The correct constraint is +-- (user_id, asset_id, exhibition_id), not (user_id, asset_id, star_id). +-- This allows a user to like the same asset once per exhibition. + +ALTER TABLE asset_likes DROP CONSTRAINT IF EXISTS uk_asset_likes_user_asset_star; \ No newline at end of file diff --git a/backend/services/assetService/provider/asset_provider.go b/backend/services/assetService/provider/asset_provider.go index 35e623a..887caa1 100644 --- a/backend/services/assetService/provider/asset_provider.go +++ b/backend/services/assetService/provider/asset_provider.go @@ -392,13 +392,14 @@ func (p *AssetProvider) LikeAsset(ctx context.Context, req *pb.LikeAssetRequest) zap.Int64("asset_id", req.AssetId), zap.Error(err), ) + // 返回响应但不返回 error,让客户端检查 Base.Code return &pb.LikeAssetResponse{ Base: &pbCommon.BaseResponse{ Code: appErrors.ToStatusCode(err), Message: err.Error(), Timestamp: 0, }, - }, err + }, nil } logger.Logger.Info("LikeAsset successful", @@ -475,13 +476,18 @@ func (p *AssetProvider) UnlikeAsset(ctx context.Context, req *pb.UnlikeAssetRequ func (p *AssetProvider) CheckAssetLike(ctx context.Context, req *pb.CheckAssetLikeRequest) (*pb.CheckAssetLikeResponse, error) { logger.Logger.Info("Received CheckAssetLike request", zap.Int64("asset_id", req.AssetId), + zap.Int64("user_id", req.UserId), + zap.Int64("star_id", req.StarId), ) - // 从 Dubbo attachments 获取用户信息 - userID, starID, err := extractUserInfoFromDubboAttachments(ctx) - if err != nil { - logger.Logger.Error("Failed to extract user info from attachments", - zap.Error(err), + // 直接使用请求中的用户信息 + userID := req.UserId + starID := req.StarId + + if userID == 0 || starID == 0 { + logger.Logger.Error("Invalid user info in request", + zap.Int64("user_id", userID), + zap.Int64("star_id", starID), ) return &pb.CheckAssetLikeResponse{ Base: &pbCommon.BaseResponse{ @@ -489,7 +495,7 @@ func (p *AssetProvider) CheckAssetLike(ctx context.Context, req *pb.CheckAssetLi Message: "user authentication required", Timestamp: 0, }, - }, err + }, fmt.Errorf("invalid user_id or star_id") } // 调用Service层 diff --git a/backend/services/assetService/service/asset_like_service.go b/backend/services/assetService/service/asset_like_service.go index 1f38104..35c62c2 100644 --- a/backend/services/assetService/service/asset_like_service.go +++ b/backend/services/assetService/service/asset_like_service.go @@ -47,6 +47,10 @@ func (s *AssetLikeService) isAssetExhibiting(assetID int64) (int64, error) { Where("asset_id = ? AND expire_at > ?", assetID, nowMs). First(&exhibition).Error if err != nil { + if err == gorm.ErrRecordNotFound { + // 资产不在任何展出中,返回 0 表示未展出 + return 0, nil + } return 0, fmt.Errorf("failed to check exhibition status: %w", err) } return exhibition.ID, nil diff --git a/backend/services/socialService/service/asset_like_service.go b/backend/services/socialService/service/asset_like_service.go index d9ca227..457ba86 100644 --- a/backend/services/socialService/service/asset_like_service.go +++ b/backend/services/socialService/service/asset_like_service.go @@ -62,6 +62,8 @@ func (s *AssetLikeService) LikeAsset(ctx context.Context, assetID, userID, starI // 2. 检查是否已点赞 checkLikeReq := &assetPb.CheckAssetLikeRequest{ AssetId: assetID, + UserId: userID, + StarId: starID, } checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq) if err != nil { @@ -125,6 +127,8 @@ func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, sta // 1. 检查是否已点赞 checkLikeReq := &assetPb.CheckAssetLikeRequest{ AssetId: assetID, + UserId: userID, + StarId: starID, } checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq) if err != nil { @@ -187,6 +191,8 @@ func (s *AssetLikeService) CheckAssetLike(ctx context.Context, assetID, userID, checkLikeReq := &assetPb.CheckAssetLikeRequest{ AssetId: assetID, + UserId: userID, + StarId: starID, } checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq) if err != nil { diff --git a/backend/socialService b/backend/socialService new file mode 100755 index 0000000..7bc2364 Binary files /dev/null and b/backend/socialService differ diff --git a/frontend/pages/components/Header.vue b/frontend/pages/components/Header.vue index 8cf6791..3ba590e 100644 --- a/frontend/pages/components/Header.vue +++ b/frontend/pages/components/Header.vue @@ -383,13 +383,13 @@ const handleAvatarClick = () => { if (pages.length > 0) { const currentPage = pages[pages.length - 1]; // 检查当前页面是否是个人信息页面 - if (currentPage.route === 'pages/profile/myWorks') { + if (currentPage.route === 'pages/profile/profile') { // 已经在个人信息页面,不执行跳转 return; } } uni.navigateTo({ - url: '/pages/profile/myWorks' + url: '/pages/profile/profile' }); }; diff --git a/frontend/pages/profile/myWorks.vue b/frontend/pages/profile/myWorks.vue index 434dc90..a71d901 100644 --- a/frontend/pages/profile/myWorks.vue +++ b/frontend/pages/profile/myWorks.vue @@ -10,9 +10,9 @@ - + diff --git a/frontend/pages/profile/profile.vue b/frontend/pages/profile/profile.vue index ea676b5..ac5efb3 100644 --- a/frontend/pages/profile/profile.vue +++ b/frontend/pages/profile/profile.vue @@ -1296,8 +1296,8 @@ onShow(() => { } .close-icon-img { - width: 80rpx; - height: 80rpx; + width: 48rpx; + height: 48rpx; position: relative; top: 96rpx; left: 32rpx; @@ -1372,15 +1372,11 @@ onShow(() => { } -.uid-value {} - .toggle-icon { width: 32rpx; height: 32rpx; } -.address-value {} - .info-btn { font-size: 20rpx; color: #FFD700; diff --git a/frontend/pages/square/components/ContentTabs.vue b/frontend/pages/square/components/ContentTabs.vue index c7f034a..8208195 100644 --- a/frontend/pages/square/components/ContentTabs.vue +++ b/frontend/pages/square/components/ContentTabs.vue @@ -33,7 +33,7 @@ diff --git a/frontend/pages/square/square.vue b/frontend/pages/square/square.vue index 420e7e3..629d27f 100644 --- a/frontend/pages/square/square.vue +++ b/frontend/pages/square/square.vue @@ -89,14 +89,18 @@ const currentUserNickname = computed(() => store.state.user?.userInfo?.nickname const currentStarId = ref(uni.getStorageSync('star_id') || null) // ========== UI State ========== -const activeContentTab = ref('random') +const activeContentTab = ref('hot') const waterfallKey = ref(0) // 用于重新加载 WaterfallGrid const navExpanded = ref(false) const showRankingModal = ref(false) const isActive = ref(true) // ========== Watch activeContentTab ========== -watch(activeContentTab, () => { +watch(activeContentTab, (newTab) => { + if (newTab === 'myworks') { + uni.navigateTo({ url: '/pages/profile/myWorks' }) + return + } // 切换标签时重置 WaterfallGrid(iOS 需要重新挂载才能重置 CSS 动画) waterfallKey.value++ }) @@ -191,6 +195,7 @@ onMounted(() => { onShow(() => { isActive.value = true + activeContentTab.value = 'hot' // 检查是否需要显示引导,如果需要则跳转到引导页面 // if (shouldShowGuideStartModal()) { // uni.navigateTo({