feat:去掉重复的点赞约束
This commit is contained in:
parent
057f9bce1c
commit
d7d17d8a38
@ -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" +
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
// 检查是否已点赞响应
|
||||
|
||||
@ -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;
|
||||
@ -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层
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
BIN
backend/socialService
Executable file
BIN
backend/socialService
Executable file
Binary file not shown.
@ -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'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
</view>
|
||||
<!-- <text class="nav-title">我的作品</text> -->
|
||||
<view class="nav-placeholder"></view>
|
||||
<view class="nav-settings" @tap="goToSettings">
|
||||
<!-- <view class="nav-settings" @tap="goToSettings">
|
||||
<image class="nav-settings-icon" src="/static/icon/settings.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<view class="scroll-content">
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
modelValue: { type: String, default: 'random' }
|
||||
modelValue: { type: String, default: 'hot' }
|
||||
})
|
||||
|
||||
defineEmits(['update:modelValue'])
|
||||
@ -42,7 +42,7 @@ const tabs = [
|
||||
{ key: 'hot', label: '人气王者', emoji: null, icon: '/static/square/3.png',iconWidth: 80, iconHeight: 80 },
|
||||
{ key: 'new', label: '新鲜上架', emoji: null, icon: '/static/square/2.png',iconWidth: 80, iconHeight: 80 },
|
||||
{ key: 'potential', label: '潜力之星', emoji: null, icon: '/static/square/1.png',iconWidth: 96, iconHeight: 96 },
|
||||
{ key: 'random', label: '随机寻宝', emoji: null, icon: '/static/square/4.png',iconWidth: 80, iconHeight: 80 },
|
||||
{ key: 'myworks', label: '我的', emoji: null, icon: '/static/square/4.png',iconWidth: 80, iconHeight: 80 },
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
@ -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({
|
||||
|
||||
Loading…
Reference in New Issue
Block a user