feat:去掉重复的点赞约束
This commit is contained in:
parent
057f9bce1c
commit
d7d17d8a38
@ -2247,6 +2247,8 @@ func (x *UnlikeAssetResponse) GetLikeCount() int32 {
|
|||||||
type CheckAssetLikeRequest struct {
|
type CheckAssetLikeRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
|
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
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -2288,6 +2290,20 @@ func (x *CheckAssetLikeRequest) GetAssetId() int64 {
|
|||||||
return 0
|
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 {
|
type CheckAssetLikeResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -3828,9 +3844,11 @@ const file_asset_proto_rawDesc = "" +
|
|||||||
"\x13UnlikeAssetResponse\x120\n" +
|
"\x13UnlikeAssetResponse\x120\n" +
|
||||||
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x1d\n" +
|
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"like_count\x18\x02 \x01(\x05R\tlikeCount\"2\n" +
|
"like_count\x18\x02 \x01(\x05R\tlikeCount\"d\n" +
|
||||||
"\x15CheckAssetLikeRequest\x12\x19\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" +
|
"\x16CheckAssetLikeResponse\x120\n" +
|
||||||
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x19\n" +
|
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x19\n" +
|
||||||
"\bis_liked\x18\x02 \x01(\bR\aisLiked\"\x87\x01\n" +
|
"\bis_liked\x18\x02 \x01(\bR\aisLiked\"\x87\x01\n" +
|
||||||
|
|||||||
@ -279,6 +279,8 @@ message UnlikeAssetResponse {
|
|||||||
// 检查是否已点赞请求(内部RPC,供Social Service调用)
|
// 检查是否已点赞请求(内部RPC,供Social Service调用)
|
||||||
message CheckAssetLikeRequest {
|
message CheckAssetLikeRequest {
|
||||||
int64 asset_id = 1; // 资产ID
|
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.Int64("asset_id", req.AssetId),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
|
// 返回响应但不返回 error,让客户端检查 Base.Code
|
||||||
return &pb.LikeAssetResponse{
|
return &pb.LikeAssetResponse{
|
||||||
Base: &pbCommon.BaseResponse{
|
Base: &pbCommon.BaseResponse{
|
||||||
Code: appErrors.ToStatusCode(err),
|
Code: appErrors.ToStatusCode(err),
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
Timestamp: 0,
|
Timestamp: 0,
|
||||||
},
|
},
|
||||||
}, err
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Logger.Info("LikeAsset successful",
|
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) {
|
func (p *AssetProvider) CheckAssetLike(ctx context.Context, req *pb.CheckAssetLikeRequest) (*pb.CheckAssetLikeResponse, error) {
|
||||||
logger.Logger.Info("Received CheckAssetLike request",
|
logger.Logger.Info("Received CheckAssetLike request",
|
||||||
zap.Int64("asset_id", req.AssetId),
|
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)
|
userID := req.UserId
|
||||||
if err != nil {
|
starID := req.StarId
|
||||||
logger.Logger.Error("Failed to extract user info from attachments",
|
|
||||||
zap.Error(err),
|
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{
|
return &pb.CheckAssetLikeResponse{
|
||||||
Base: &pbCommon.BaseResponse{
|
Base: &pbCommon.BaseResponse{
|
||||||
@ -489,7 +495,7 @@ func (p *AssetProvider) CheckAssetLike(ctx context.Context, req *pb.CheckAssetLi
|
|||||||
Message: "user authentication required",
|
Message: "user authentication required",
|
||||||
Timestamp: 0,
|
Timestamp: 0,
|
||||||
},
|
},
|
||||||
}, err
|
}, fmt.Errorf("invalid user_id or star_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用Service层
|
// 调用Service层
|
||||||
|
|||||||
@ -47,6 +47,10 @@ func (s *AssetLikeService) isAssetExhibiting(assetID int64) (int64, error) {
|
|||||||
Where("asset_id = ? AND expire_at > ?", assetID, nowMs).
|
Where("asset_id = ? AND expire_at > ?", assetID, nowMs).
|
||||||
First(&exhibition).Error
|
First(&exhibition).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
// 资产不在任何展出中,返回 0 表示未展出
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return 0, fmt.Errorf("failed to check exhibition status: %w", err)
|
return 0, fmt.Errorf("failed to check exhibition status: %w", err)
|
||||||
}
|
}
|
||||||
return exhibition.ID, nil
|
return exhibition.ID, nil
|
||||||
|
|||||||
@ -62,6 +62,8 @@ func (s *AssetLikeService) LikeAsset(ctx context.Context, assetID, userID, starI
|
|||||||
// 2. 检查是否已点赞
|
// 2. 检查是否已点赞
|
||||||
checkLikeReq := &assetPb.CheckAssetLikeRequest{
|
checkLikeReq := &assetPb.CheckAssetLikeRequest{
|
||||||
AssetId: assetID,
|
AssetId: assetID,
|
||||||
|
UserId: userID,
|
||||||
|
StarId: starID,
|
||||||
}
|
}
|
||||||
checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq)
|
checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,6 +127,8 @@ func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, sta
|
|||||||
// 1. 检查是否已点赞
|
// 1. 检查是否已点赞
|
||||||
checkLikeReq := &assetPb.CheckAssetLikeRequest{
|
checkLikeReq := &assetPb.CheckAssetLikeRequest{
|
||||||
AssetId: assetID,
|
AssetId: assetID,
|
||||||
|
UserId: userID,
|
||||||
|
StarId: starID,
|
||||||
}
|
}
|
||||||
checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq)
|
checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -187,6 +191,8 @@ func (s *AssetLikeService) CheckAssetLike(ctx context.Context, assetID, userID,
|
|||||||
|
|
||||||
checkLikeReq := &assetPb.CheckAssetLikeRequest{
|
checkLikeReq := &assetPb.CheckAssetLikeRequest{
|
||||||
AssetId: assetID,
|
AssetId: assetID,
|
||||||
|
UserId: userID,
|
||||||
|
StarId: starID,
|
||||||
}
|
}
|
||||||
checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq)
|
checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq)
|
||||||
if err != nil {
|
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) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1];
|
const currentPage = pages[pages.length - 1];
|
||||||
// 检查当前页面是否是个人信息页面
|
// 检查当前页面是否是个人信息页面
|
||||||
if (currentPage.route === 'pages/profile/myWorks') {
|
if (currentPage.route === 'pages/profile/profile') {
|
||||||
// 已经在个人信息页面,不执行跳转
|
// 已经在个人信息页面,不执行跳转
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/profile/myWorks'
|
url: '/pages/profile/profile'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- <text class="nav-title">我的作品</text> -->
|
<!-- <text class="nav-title">我的作品</text> -->
|
||||||
<view class="nav-placeholder"></view>
|
<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>
|
<image class="nav-settings-icon" src="/static/icon/settings.png" mode="aspectFit"></image>
|
||||||
</view>
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="scroll-content">
|
<view class="scroll-content">
|
||||||
|
|||||||
@ -1296,8 +1296,8 @@ onShow(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.close-icon-img {
|
.close-icon-img {
|
||||||
width: 80rpx;
|
width: 48rpx;
|
||||||
height: 80rpx;
|
height: 48rpx;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 96rpx;
|
top: 96rpx;
|
||||||
left: 32rpx;
|
left: 32rpx;
|
||||||
@ -1372,15 +1372,11 @@ onShow(() => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.uid-value {}
|
|
||||||
|
|
||||||
.toggle-icon {
|
.toggle-icon {
|
||||||
width: 32rpx;
|
width: 32rpx;
|
||||||
height: 32rpx;
|
height: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.address-value {}
|
|
||||||
|
|
||||||
.info-btn {
|
.info-btn {
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
color: #FFD700;
|
color: #FFD700;
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
defineProps({
|
defineProps({
|
||||||
modelValue: { type: String, default: 'random' }
|
modelValue: { type: String, default: 'hot' }
|
||||||
})
|
})
|
||||||
|
|
||||||
defineEmits(['update:modelValue'])
|
defineEmits(['update:modelValue'])
|
||||||
@ -42,7 +42,7 @@ const tabs = [
|
|||||||
{ key: 'hot', label: '人气王者', emoji: null, icon: '/static/square/3.png',iconWidth: 80, iconHeight: 80 },
|
{ 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: '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: '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>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -89,14 +89,18 @@ const currentUserNickname = computed(() => store.state.user?.userInfo?.nickname
|
|||||||
const currentStarId = ref(uni.getStorageSync('star_id') || null)
|
const currentStarId = ref(uni.getStorageSync('star_id') || null)
|
||||||
|
|
||||||
// ========== UI State ==========
|
// ========== UI State ==========
|
||||||
const activeContentTab = ref('random')
|
const activeContentTab = ref('hot')
|
||||||
const waterfallKey = ref(0) // 用于重新加载 WaterfallGrid
|
const waterfallKey = ref(0) // 用于重新加载 WaterfallGrid
|
||||||
const navExpanded = ref(false)
|
const navExpanded = ref(false)
|
||||||
const showRankingModal = ref(false)
|
const showRankingModal = ref(false)
|
||||||
const isActive = ref(true)
|
const isActive = ref(true)
|
||||||
|
|
||||||
// ========== Watch activeContentTab ==========
|
// ========== Watch activeContentTab ==========
|
||||||
watch(activeContentTab, () => {
|
watch(activeContentTab, (newTab) => {
|
||||||
|
if (newTab === 'myworks') {
|
||||||
|
uni.navigateTo({ url: '/pages/profile/myWorks' })
|
||||||
|
return
|
||||||
|
}
|
||||||
// 切换标签时重置 WaterfallGrid(iOS 需要重新挂载才能重置 CSS 动画)
|
// 切换标签时重置 WaterfallGrid(iOS 需要重新挂载才能重置 CSS 动画)
|
||||||
waterfallKey.value++
|
waterfallKey.value++
|
||||||
})
|
})
|
||||||
@ -191,6 +195,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
isActive.value = true
|
isActive.value = true
|
||||||
|
activeContentTab.value = 'hot'
|
||||||
// 检查是否需要显示引导,如果需要则跳转到引导页面
|
// 检查是否需要显示引导,如果需要则跳转到引导页面
|
||||||
// if (shouldShowGuideStartModal()) {
|
// if (shouldShowGuideStartModal()) {
|
||||||
// uni.navigateTo({
|
// uni.navigateTo({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user