feat: 修改当前点赞逻辑
This commit is contained in:
parent
1ee151630d
commit
02598db333
@ -191,14 +191,17 @@ const (
|
||||
// ========== 点赞记录表模型 ==========
|
||||
|
||||
// AssetLike 点赞记录表模型
|
||||
// 唯一约束:(asset_id, user_id, exhibition_id) - 每次展览每个用户只能点赞一次
|
||||
// 注意:exhibition_id 不设置外键约束,因为迁移时现有数据没有有效的 exhibition_id
|
||||
type AssetLike struct {
|
||||
ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
|
||||
AssetID int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_asset;index:idx_asset_likes_asset;column:asset_id"`
|
||||
UserID int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_asset;index:idx_asset_likes_user_star;column:user_id"`
|
||||
StarID int64 `gorm:"not null;index:idx_asset_likes_user_star;column:star_id"` // 用于数据隔离和查询优化
|
||||
CreatedAt int64 `gorm:"not null;index:idx_asset_likes_user_star,sort:desc;index:idx_asset_likes_asset,sort:desc;column:created_at"`
|
||||
ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
|
||||
AssetID int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_exhibition;index:idx_asset_likes_asset;column:asset_id"`
|
||||
UserID int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_exhibition;index:idx_asset_likes_user_star;column:user_id"`
|
||||
StarID int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_exhibition;index:idx_asset_likes_user_star;column:star_id"` // 用于数据隔离和查询优化
|
||||
ExhibitionID int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_exhibition;index:idx_asset_likes_exhibition;column:exhibition_id"` // 关联展览,同一展览只能点赞一次
|
||||
CreatedAt int64 `gorm:"not null;index:idx_asset_likes_user_star,sort:desc;index:idx_asset_likes_asset,sort:desc;column:created_at"`
|
||||
|
||||
// 关联关系
|
||||
// 关联关系(不设置外键约束,避免迁移问题)
|
||||
Asset Asset `gorm:"foreignKey:AssetID;references:ID;constraint:OnDelete:CASCADE"`
|
||||
User User `gorm:"foreignKey:UserID;references:ID;constraint:OnDelete:CASCADE"`
|
||||
Star Star `gorm:"foreignKey:StarID;references:StarID;constraint:OnDelete:CASCADE"`
|
||||
|
||||
@ -13,10 +13,10 @@ type AssetLikeRepository interface {
|
||||
Create(like *models.AssetLike) error
|
||||
|
||||
// Delete 删除点赞记录
|
||||
Delete(assetID, userID, starID int64) error
|
||||
Delete(assetID, userID, starID, exhibitionID int64) error
|
||||
|
||||
// Exists 检查点赞记录是否存在
|
||||
Exists(assetID, userID, starID int64) (bool, error)
|
||||
// Exists 检查点赞记录是否存在(按 asset_id, user_id, exhibition_id)
|
||||
Exists(assetID, userID, starID, exhibitionID int64) (bool, error)
|
||||
|
||||
// GetByAsset 获取资产的点赞记录列表(分页)
|
||||
GetByAsset(assetID int64, limit, offset int) ([]*models.AssetLike, error)
|
||||
@ -61,8 +61,8 @@ func (r *assetLikeRepository) Create(like *models.AssetLike) error {
|
||||
return errors.New("star_id must be greater than 0")
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
exists, err := r.Exists(like.AssetID, like.UserID, like.StarID)
|
||||
// 检查是否已存在(同一展览同用户只能点赞一次)
|
||||
exists, err := r.Exists(like.AssetID, like.UserID, like.StarID, like.ExhibitionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -78,7 +78,7 @@ func (r *assetLikeRepository) Create(like *models.AssetLike) error {
|
||||
}
|
||||
|
||||
// Delete 删除点赞记录
|
||||
func (r *assetLikeRepository) Delete(assetID, userID, starID int64) error {
|
||||
func (r *assetLikeRepository) Delete(assetID, userID, starID, exhibitionID int64) error {
|
||||
if assetID <= 0 {
|
||||
return errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
@ -91,7 +91,11 @@ func (r *assetLikeRepository) Delete(assetID, userID, starID int64) error {
|
||||
return errors.New("star_id must be greater than 0")
|
||||
}
|
||||
|
||||
result := r.db.Where("asset_id = ? AND user_id = ? AND star_id = ?", assetID, userID, starID).
|
||||
if exhibitionID <= 0 {
|
||||
return errors.New("exhibition_id must be greater than 0")
|
||||
}
|
||||
|
||||
result := r.db.Where("asset_id = ? AND user_id = ? AND star_id = ? AND exhibition_id = ?", assetID, userID, starID, exhibitionID).
|
||||
Delete(&models.AssetLike{})
|
||||
|
||||
if result.Error != nil {
|
||||
@ -105,8 +109,8 @@ func (r *assetLikeRepository) Delete(assetID, userID, starID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists 检查点赞记录是否存在
|
||||
func (r *assetLikeRepository) Exists(assetID, userID, starID int64) (bool, error) {
|
||||
// Exists 检查点赞记录是否存在(按 asset_id, user_id, exhibition_id)
|
||||
func (r *assetLikeRepository) Exists(assetID, userID, starID, exhibitionID int64) (bool, error) {
|
||||
if assetID <= 0 {
|
||||
return false, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
@ -119,9 +123,13 @@ func (r *assetLikeRepository) Exists(assetID, userID, starID int64) (bool, error
|
||||
return false, errors.New("star_id must be greater than 0")
|
||||
}
|
||||
|
||||
if exhibitionID <= 0 {
|
||||
return false, errors.New("exhibition_id must be greater than 0")
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := r.db.Model(&models.AssetLike{}).
|
||||
Where("asset_id = ? AND user_id = ? AND star_id = ?", assetID, userID, starID).
|
||||
Where("asset_id = ? AND user_id = ? AND star_id = ? AND exhibition_id = ?", assetID, userID, starID, exhibitionID).
|
||||
Count(&count).Error
|
||||
|
||||
if err != nil {
|
||||
|
||||
@ -1,329 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/topfans/backend/pkg/models"
|
||||
)
|
||||
|
||||
// TestAssetLikeRepository_Create 测试创建点赞记录
|
||||
func TestAssetLikeRepository_Create(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_create")
|
||||
user := createTestUser(t, db, "19900200001")
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, "测试用户")
|
||||
asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产")
|
||||
|
||||
// 测试创建点赞
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
|
||||
err := repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, like.ID)
|
||||
assert.NotZero(t, like.CreatedAt)
|
||||
|
||||
// 测试重复点赞
|
||||
duplicateLike := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err = repo.Create(duplicateLike)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "already liked")
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_Delete 测试删除点赞记录
|
||||
func TestAssetLikeRepository_Delete(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_delete")
|
||||
user := createTestUser(t, db, "19900200002")
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, "测试用户")
|
||||
asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产")
|
||||
|
||||
// 先创建点赞
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err := repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 删除点赞
|
||||
err = repo.Delete(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 验证已删除
|
||||
exists, err := repo.Exists(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
// 测试删除不存在的记录
|
||||
err = repo.Delete(asset.ID, user.ID, star.StarID)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_Exists 测试检查点赞是否存在
|
||||
func TestAssetLikeRepository_Exists(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_exists")
|
||||
user := createTestUser(t, db, "19900200003")
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, "测试用户")
|
||||
asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产")
|
||||
|
||||
// 初始状态不存在
|
||||
exists, err := repo.Exists(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
// 创建点赞后存在
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err = repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
|
||||
exists, err = repo.Exists(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exists)
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_GetByAsset 测试获取资产的点赞列表
|
||||
func TestAssetLikeRepository_GetByAsset(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_by_asset")
|
||||
// 创建资产的所有者
|
||||
assetOwner := createTestUser(t, db, "19900200004")
|
||||
createTestFanProfile(t, db, assetOwner.ID, star.StarID, "资产所有者")
|
||||
asset := createTestAsset(t, db, assetOwner.ID, star.StarID, "测试资产")
|
||||
|
||||
// 创建多个用户点赞
|
||||
for i := 1; i <= 5; i++ {
|
||||
mobile := "1990020000" + string(rune('4'+i))
|
||||
user := createTestUser(t, db, mobile)
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, fmt.Sprintf("用户%d", i))
|
||||
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err := repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 获取点赞列表
|
||||
likes, err := repo.GetByAsset(asset.ID, 3, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, likes, 3)
|
||||
|
||||
// 获取第二页
|
||||
likes, err = repo.GetByAsset(asset.ID, 3, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, likes, 2)
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_GetByUser 测试获取用户的点赞列表
|
||||
func TestAssetLikeRepository_GetByUser(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_by_user")
|
||||
user := createTestUser(t, db, "19900200009")
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, "测试用户")
|
||||
|
||||
// 创建多个资产并点赞
|
||||
for i := 0; i < 5; i++ {
|
||||
asset := createTestAsset(t, db, user.ID, star.StarID, "资产")
|
||||
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err := repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 获取点赞列表
|
||||
likes, err := repo.GetByUser(user.ID, star.StarID, 3, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, likes, 3)
|
||||
|
||||
// 获取第二页
|
||||
likes, err = repo.GetByUser(user.ID, star.StarID, 3, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, likes, 2)
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_CountByAsset 测试统计资产的点赞数
|
||||
func TestAssetLikeRepository_CountByAsset(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_count_asset")
|
||||
// 创建资产的所有者
|
||||
assetOwner := createTestUser(t, db, "19900200010")
|
||||
createTestFanProfile(t, db, assetOwner.ID, star.StarID, "资产所有者")
|
||||
asset := createTestAsset(t, db, assetOwner.ID, star.StarID, "测试资产")
|
||||
|
||||
// 初始点赞数为0
|
||||
count, err := repo.CountByAsset(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count)
|
||||
|
||||
// 创建3个点赞
|
||||
for i := 1; i <= 3; i++ {
|
||||
mobile := "1990020001" + string(rune('0'+i))
|
||||
user := createTestUser(t, db, mobile)
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, fmt.Sprintf("用户%d", i))
|
||||
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err := repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 验证点赞数
|
||||
count, err = repo.CountByAsset(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count)
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_CountByUser 测试统计用户的点赞数
|
||||
func TestAssetLikeRepository_CountByUser(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
repo := NewAssetLikeRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_count_user")
|
||||
user := createTestUser(t, db, "19900200014")
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, "测试用户")
|
||||
|
||||
// 初始点赞数为0
|
||||
count, err := repo.CountByUser(user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count)
|
||||
|
||||
// 创建3个点赞
|
||||
for i := 0; i < 3; i++ {
|
||||
asset := createTestAsset(t, db, user.ID, star.StarID, "资产")
|
||||
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err := repo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 验证点赞数
|
||||
count, err = repo.CountByUser(user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count)
|
||||
}
|
||||
|
||||
// TestAssetLikeRepository_LikeUnlikeFlow 测试点赞-取消点赞流程
|
||||
func TestAssetLikeRepository_LikeUnlikeFlow(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer cleanupTestDB(t, db)
|
||||
|
||||
assetLikeRepo := NewAssetLikeRepository(db)
|
||||
assetRepo := NewAssetRepository(db)
|
||||
|
||||
// 创建测试数据
|
||||
star := createTestStar(t, db, "test_like_flow")
|
||||
user := createTestUser(t, db, "19900200015")
|
||||
createTestFanProfile(t, db, user.ID, star.StarID, "测试用户")
|
||||
asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产")
|
||||
|
||||
// 1. 初始状态
|
||||
exists, err := assetLikeRepo.Exists(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
assetData, err := assetRepo.GetByID(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(0), assetData.LikeCount)
|
||||
|
||||
// 2. 点赞
|
||||
like := &models.AssetLike{
|
||||
AssetID: asset.ID,
|
||||
UserID: user.ID,
|
||||
StarID: star.StarID,
|
||||
}
|
||||
err = assetLikeRepo.Create(like)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 增加资产点赞数
|
||||
err = assetRepo.IncrementLikeCount(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 验证点赞状态
|
||||
exists, err = assetLikeRepo.Exists(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exists)
|
||||
|
||||
assetData, err = assetRepo.GetByID(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), assetData.LikeCount)
|
||||
|
||||
// 3. 取消点赞
|
||||
err = assetLikeRepo.Delete(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 减少资产点赞数
|
||||
err = assetRepo.DecrementLikeCount(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 验证取消点赞状态
|
||||
exists, err = assetLikeRepo.Exists(asset.ID, user.ID, star.StarID)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
assetData, err = assetRepo.GetByID(asset.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(0), assetData.LikeCount)
|
||||
}
|
||||
@ -54,6 +54,9 @@ type AssetRepository interface {
|
||||
// IsExhibiting 检查资产是否正在展出中
|
||||
IsExhibiting(assetID int64) (bool, error)
|
||||
|
||||
// GetExhibitingID 获取正在展出的展览ID(如果正在展出),返回0表示未展出
|
||||
GetExhibitingID(assetID int64) (int64, error)
|
||||
|
||||
// GetExhibitionStartTime 获取资产展出开始时间(如果正在展出)
|
||||
GetExhibitionStartTime(assetID int64) (int64, error)
|
||||
|
||||
@ -367,6 +370,30 @@ func (r *assetRepository) IsExhibiting(assetID int64) (bool, error) {
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// GetExhibitingID 获取正在展出的展览ID(如果正在展出),返回0表示未展出
|
||||
func (r *assetRepository) GetExhibitingID(assetID int64) (int64, error) {
|
||||
if assetID <= 0 {
|
||||
return 0, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
|
||||
var exhibition struct {
|
||||
ID int64
|
||||
}
|
||||
err := r.db.Model(&models.Exhibition{}).
|
||||
Select("id").
|
||||
Where("asset_id = ? AND expire_at > ?", assetID, time.Now().UnixMilli()).
|
||||
First(&exhibition).Error
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, nil // 未展出
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return exhibition.ID, nil
|
||||
}
|
||||
|
||||
// GetExhibitionStartTime 获取资产展出开始时间(如果正在展出)
|
||||
func (r *assetRepository) GetExhibitionStartTime(assetID int64) (int64, error) {
|
||||
if assetID <= 0 {
|
||||
|
||||
@ -33,17 +33,20 @@ func NewAssetLikeService(
|
||||
}
|
||||
}
|
||||
|
||||
// isAssetExhibiting 检查资产当前是否在展出中
|
||||
func (s *AssetLikeService) isAssetExhibiting(assetID int64) (bool, error) {
|
||||
// isAssetExhibiting 检查资产当前是否在展出中,返回 exhibition_id
|
||||
func (s *AssetLikeService) isAssetExhibiting(assetID int64) (int64, error) {
|
||||
nowMs := time.Now().UnixMilli()
|
||||
var count int64
|
||||
err := s.db.Table("exhibitions").
|
||||
Where("asset_id = ? AND expire_at > ?", assetID, nowMs).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check exhibition status: %w", err)
|
||||
var exhibition struct {
|
||||
ID int64
|
||||
}
|
||||
return count > 0, nil
|
||||
err := s.db.Table("exhibitions").
|
||||
Select("id").
|
||||
Where("asset_id = ? AND expire_at > ?", assetID, nowMs).
|
||||
First(&exhibition).Error
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to check exhibition status: %w", err)
|
||||
}
|
||||
return exhibition.ID, nil
|
||||
}
|
||||
|
||||
// isUniqueConstraintViolation 检查错误是否为唯一约束冲突(PostgreSQL error code 23505)
|
||||
@ -116,8 +119,8 @@ func (s *AssetLikeService) LikeAsset(ctx context.Context, assetID, userID, starI
|
||||
return 0, fmt.Errorf("asset not found: %w", err)
|
||||
}
|
||||
|
||||
// 1.5 检查资产是否当前在展出中
|
||||
exhibiting, err := s.isAssetExhibiting(assetID)
|
||||
// 1.5 检查资产是否当前在展出中,获取 exhibition_id
|
||||
exhibitionID, err := s.isAssetExhibiting(assetID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("Failed to check exhibition status",
|
||||
zap.Error(err),
|
||||
@ -125,12 +128,12 @@ func (s *AssetLikeService) LikeAsset(ctx context.Context, assetID, userID, starI
|
||||
)
|
||||
return 0, err
|
||||
}
|
||||
if !exhibiting {
|
||||
if exhibitionID == 0 {
|
||||
return 0, fmt.Errorf("资产未在展示中,无法点赞")
|
||||
}
|
||||
|
||||
// 2. 检查是否已点赞
|
||||
exists, err := s.assetLikeRepo.Exists(assetID, userID, starID)
|
||||
// 2. 检查是否已点赞(同一展览同用户只能点赞一次)
|
||||
exists, err := s.assetLikeRepo.Exists(assetID, userID, starID, exhibitionID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("Failed to check if already liked",
|
||||
zap.Error(err),
|
||||
@ -229,8 +232,8 @@ func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, sta
|
||||
zap.Int64("star_id", starID),
|
||||
)
|
||||
|
||||
// 0. 检查资产是否当前在展出中(展出结束后不允许取消点赞)
|
||||
exhibiting, err := s.isAssetExhibiting(assetID)
|
||||
// 0. 检查资产是否当前在展出中,获取 exhibition_id(展出结束后不允许取消点赞)
|
||||
exhibitionID, err := s.isAssetExhibiting(assetID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("Failed to check exhibition status",
|
||||
zap.Error(err),
|
||||
@ -238,12 +241,12 @@ func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, sta
|
||||
)
|
||||
return 0, err
|
||||
}
|
||||
if !exhibiting {
|
||||
if exhibitionID == 0 {
|
||||
return 0, fmt.Errorf("资产未在展示中,无法取消点赞")
|
||||
}
|
||||
|
||||
// 1. 检查是否已点赞
|
||||
exists, err := s.assetLikeRepo.Exists(assetID, userID, starID)
|
||||
// 1. 检查是否已点赞(同一展览同用户只能点赞一次)
|
||||
exists, err := s.assetLikeRepo.Exists(assetID, userID, starID, exhibitionID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("Failed to check if liked",
|
||||
zap.Error(err),
|
||||
@ -262,8 +265,8 @@ func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, sta
|
||||
|
||||
// 2. 开启事务
|
||||
err = s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 2.1 删除点赞记录
|
||||
if err := tx.Where("asset_id = ? AND user_id = ? AND star_id = ?", assetID, userID, starID).
|
||||
// 2.1 删除点赞记录(按 exhibition_id 删除)
|
||||
if err := tx.Where("asset_id = ? AND user_id = ? AND star_id = ? AND exhibition_id = ?", assetID, userID, starID, exhibitionID).
|
||||
Delete(&models.AssetLike{}).Error; err != nil {
|
||||
logger.Logger.Error("Failed to delete like record",
|
||||
zap.Error(err),
|
||||
@ -336,7 +339,7 @@ func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, sta
|
||||
return asset.LikeCount, nil
|
||||
}
|
||||
|
||||
// CheckAssetLike 检查是否已点赞
|
||||
// CheckAssetLike 检查是否已点赞(在当前展出中)
|
||||
func (s *AssetLikeService) CheckAssetLike(ctx context.Context, assetID, userID, starID int64) (bool, error) {
|
||||
logger.Logger.Debug("AssetLikeService.CheckAssetLike called",
|
||||
zap.Int64("asset_id", assetID),
|
||||
@ -344,7 +347,22 @@ func (s *AssetLikeService) CheckAssetLike(ctx context.Context, assetID, userID,
|
||||
zap.Int64("star_id", starID),
|
||||
)
|
||||
|
||||
exists, err := s.assetLikeRepo.Exists(assetID, userID, starID)
|
||||
// 获取当前展出中的 exhibition_id
|
||||
exhibitionID, err := s.isAssetExhibiting(assetID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("Failed to check exhibition status",
|
||||
zap.Error(err),
|
||||
zap.Int64("asset_id", assetID),
|
||||
)
|
||||
return false, fmt.Errorf("failed to check exhibition status: %w", err)
|
||||
}
|
||||
|
||||
if exhibitionID == 0 {
|
||||
// 资产不在展出中,视为未点赞
|
||||
return false, nil
|
||||
}
|
||||
|
||||
exists, err := s.assetLikeRepo.Exists(assetID, userID, starID, exhibitionID)
|
||||
if err != nil {
|
||||
logger.Logger.Error("Failed to check like status",
|
||||
zap.Error(err),
|
||||
|
||||
@ -452,17 +452,21 @@ func (s *assetService) GetAsset(req *pb.GetAssetRequest, userID, starID int64) (
|
||||
ownerNickname = profile.Nickname
|
||||
}
|
||||
|
||||
// 4. 检查当前用户是否已点赞
|
||||
isLiked, err := s.assetLikeRepo.Exists(asset.ID, userID, starID)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("Failed to check like status, will return is_liked as false",
|
||||
zap.Int64("asset_id", asset.ID),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("star_id", starID),
|
||||
zap.Error(err),
|
||||
)
|
||||
// 检查失败时,默认为未点赞
|
||||
isLiked = false
|
||||
// 4. 检查当前用户是否已点赞(需要获取当前展出中的 exhibition_id)
|
||||
exhibitionID, _ := s.assetRepo.GetExhibitingID(asset.ID)
|
||||
isLiked := false
|
||||
if exhibitionID > 0 {
|
||||
isLiked, err = s.assetLikeRepo.Exists(asset.ID, userID, starID, exhibitionID)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("Failed to check like status, will return is_liked as false",
|
||||
zap.Int64("asset_id", asset.ID),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("star_id", starID),
|
||||
zap.Error(err),
|
||||
)
|
||||
// 检查失败时,默认为未点赞
|
||||
isLiked = false
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 从 asset_registry 表获取 display_status
|
||||
|
||||
@ -156,17 +156,8 @@ func (w *CleanupWorker) cleanupExpiredExhibitions(now int64) {
|
||||
log.Printf("展品已到期并生成领取记录: ExhibitionID=%d, AssetID=%d, SlotID=%d, OccupierUID=%d, Revenue=%d",
|
||||
e.ID, e.AssetID, e.SlotID, e.OccupierUID, revenue)
|
||||
|
||||
// 5. 清除该资产的点赞记录(不阻断主流程),允许用户在下次展出时再次点赞
|
||||
assetID := e.AssetID
|
||||
go func() {
|
||||
if w.assetClient != nil {
|
||||
if err := w.assetClient.ClearAssetLikeRecords(assetID); err != nil {
|
||||
logger.Logger.Error("清除过期展品点赞记录失败",
|
||||
zap.Int64("asset_id", assetID),
|
||||
zap.Error(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
// 5. 保留点赞记录,允许用户在下次展出时再次点赞(每次展览可点赞一次)
|
||||
// 注意:点赞记录现在按 (asset_id, user_id, exhibition_id) 去重,不会与历史点赞冲突
|
||||
}
|
||||
|
||||
log.Printf("过期展品清理完成: 成功 %d 个, 失败 %d 个", successCount, failedCount)
|
||||
|
||||
@ -593,9 +593,10 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
|
||||
// 计数查询(使用 DISTINCT 因为一个资产可能在多个展位展出)
|
||||
// 显示:展品未下架(deleted_at IS NULL),无论是否过期
|
||||
// 不显示:已领取下架的(deleted_at IS NOT NULL)或从未展出过的
|
||||
// 使用 asset_likes.exhibition_id 关联,确保只返回有效展览的点赞记录
|
||||
countQuery := r.db.Model(&models.AssetLike{}).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id AND e.deleted_at IS NULL").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true)
|
||||
|
||||
@ -604,13 +605,14 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
|
||||
}
|
||||
|
||||
// 数据查询:只过滤已下架的,不过滤过期(用户领取收益后才下架)
|
||||
// 使用 asset_likes.exhibition_id 关联,确保只返回当前有效展览的点赞记录
|
||||
offset := (page - 1) * pageSize
|
||||
err := r.db.Model(&models.AssetLike{}).
|
||||
Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count,
|
||||
asset_likes.created_at as liked_at,
|
||||
(a.tags @> '["craft:lenticular"]') as is_lenticular`).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id AND e.deleted_at IS NULL").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
Group("asset_likes.asset_id, a.name, a.cover_url, a.like_count, asset_likes.created_at, is_lenticular").
|
||||
@ -692,7 +694,7 @@ func (r *socialRepositoryImpl) GetMyTodayLikedAssets(userID, starID int64, page,
|
||||
|
||||
countQuery := r.db.Model(&models.AssetLike{}).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("asset_likes.created_at >= ?", startOfDay).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
@ -707,7 +709,7 @@ func (r *socialRepositoryImpl) GetMyTodayLikedAssets(userID, starID int64, page,
|
||||
Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count,
|
||||
asset_likes.created_at as liked_at`).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("asset_likes.created_at >= ?", startOfDay).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
@ -752,7 +754,7 @@ func (r *socialRepositoryImpl) GetMyWeekLikedAssets(userID, starID int64, page,
|
||||
|
||||
countQuery := r.db.Model(&models.AssetLike{}).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("asset_likes.created_at >= ?", startOfWeekMillis).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
@ -767,7 +769,7 @@ func (r *socialRepositoryImpl) GetMyWeekLikedAssets(userID, starID int64, page,
|
||||
Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count,
|
||||
asset_likes.created_at as liked_at`).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("asset_likes.created_at >= ?", startOfWeekMillis).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
@ -805,7 +807,7 @@ func (r *socialRepositoryImpl) GetUserLikedAssets(userID, starID int64, page, pa
|
||||
|
||||
countQuery := r.db.Model(&models.AssetLike{}).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
Where("e.deleted_at IS NULL AND e.expire_at > ?", now)
|
||||
@ -819,7 +821,7 @@ func (r *socialRepositoryImpl) GetUserLikedAssets(userID, starID int64, page, pa
|
||||
Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count,
|
||||
asset_likes.created_at as liked_at`).
|
||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
||||
Joins("JOIN exhibitions e ON e.id = asset_likes.exhibition_id").
|
||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||
Where("e.deleted_at IS NULL AND e.expire_at > ?", now).
|
||||
|
||||
Loading…
Reference in New Issue
Block a user