bugfix
This commit is contained in:
@@ -55,6 +55,48 @@ func (d *ClothesDao) ClothesUpdateByUUID(ctx context.Context, clothesUUID string
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClothesUpdateByUUIDIfStatusIn 条件更新衣物:仅当当前图片处理状态在 statuses 内时才更新,返回是否更新成功。
|
||||
// 用于「处理成功回填」这类必须与状态守卫绑定的写操作:恢复任务可能已把该记录置为失败并回退额度,
|
||||
// 无条件写回会把状态改回成功,造成「额度已退但衣物成功」的不一致。
|
||||
func (d *ClothesDao) ClothesUpdateByUUIDIfStatusIn(ctx context.Context, clothesUUID string, statuses []int8, updates map[string]any) (bool, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.ClothesUpdateByUUIDIfStatusIn")
|
||||
defer span.End()
|
||||
|
||||
res := d.db.WithContext(ctx).Model(&mysqlmodel.Clothes{}).
|
||||
Where("clothes_uuid = ? AND image_process_status IN ?", clothesUUID, statuses).
|
||||
Updates(updates)
|
||||
if res.Error != nil {
|
||||
span.RecordError(res.Error)
|
||||
return false, res.Error
|
||||
}
|
||||
return res.RowsAffected > 0, nil
|
||||
}
|
||||
|
||||
// ClothesReferencedCount 统计衣物被「穿搭明细 / 衣柜分享明细」引用的数量(删除前校验,>0 表示不可删除)。
|
||||
// 删除被引用的衣物会留下悬挂引用,公开穿搭与分享会变成空壳(明细里查不到衣物,前端静默跳过)。
|
||||
func (d *ClothesDao) ClothesReferencedCount(ctx context.Context, clothesUUID string) (int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.ClothesReferencedCount")
|
||||
defer span.End()
|
||||
|
||||
subQuery := "clothes_id IN (SELECT id FROM clothes WHERE clothes_uuid = ? AND deleted_at = 0)"
|
||||
|
||||
var total int64
|
||||
if err := d.db.WithContext(ctx).Model(&mysqlmodel.OutfitItem{}).
|
||||
Where(subQuery, clothesUUID).Count(&total).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return 0, err
|
||||
}
|
||||
if total > 0 {
|
||||
return total, nil
|
||||
}
|
||||
if err := d.db.WithContext(ctx).Model(&mysqlmodel.ClosetShareItem{}).
|
||||
Where(subQuery, clothesUUID).Count(&total).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// ClothesGetByUUID 按 UUID 查询衣物
|
||||
func (d *ClothesDao) ClothesGetByUUID(ctx context.Context, clothesUUID string) (*mysqlmodel.Clothes, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.ClothesGetByUUID")
|
||||
|
||||
@@ -86,13 +86,48 @@ func (d *CollectDao) CollectCountByTarget(ctx context.Context, collectType int8,
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CollectListTargetIDsByUserPage 分页查询用户收藏的目标ID列表(仅 status=1),返回 ids 与总数。
|
||||
// CollectCountByTargets 批量统计多个目标的收藏数(仅 status=1),返回 targetID -> count;空入参短路。
|
||||
func (d *CollectDao) CollectCountByTargets(ctx context.Context, collectType int8, targetIDs []int64) (map[int64]int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.CollectCountByTargets")
|
||||
defer span.End()
|
||||
|
||||
counts := make(map[int64]int64, len(targetIDs))
|
||||
if len(targetIDs) == 0 {
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
var rows []struct {
|
||||
TargetID int64 `gorm:"column:target_id"`
|
||||
Cnt int64 `gorm:"column:cnt"`
|
||||
}
|
||||
if err := d.db.WithContext(ctx).Model(&mysqlmodel.Collect{}).
|
||||
Select("target_id, COUNT(*) AS cnt").
|
||||
Where("collect_type = ? AND target_id IN ? AND status = ?", collectType, targetIDs, int8(1)).
|
||||
Group("target_id").
|
||||
Scan(&rows).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rows {
|
||||
counts[r.TargetID] = r.Cnt
|
||||
}
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
// CollectListTargetIDsByUserPage 分页查询用户收藏的目标ID列表(仅 status=1,且目标穿搭对本人可见),返回 ids 与总数。
|
||||
//
|
||||
// 可见性口径与 service.isOutfitPublicVisible 保持一致:本人收藏自己的穿搭始终可见;
|
||||
// 收藏他人穿搭要求「公开(2) + 审核通过(2) + 状态正常(1)」。
|
||||
// 联表过滤而非服务层过滤,是为了让 total 与实际返回条数口径一致
|
||||
//(此前只按 collects 计数,穿搭转私/删除后会返回 total 虚高、页内条数偏少的空洞页)。
|
||||
func (d *CollectDao) CollectListTargetIDsByUserPage(ctx context.Context, collectType int8, userID int64, page, pageSize int) ([]int64, int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.CollectListTargetIDsByUserPage")
|
||||
defer span.End()
|
||||
|
||||
q := d.db.WithContext(ctx).Model(&mysqlmodel.Collect{}).
|
||||
Where("collect_type = ? AND user_id = ? AND status = ?", collectType, userID, int8(1))
|
||||
Joins("JOIN outfits o ON o.id = collects.target_id AND o.deleted_at = 0").
|
||||
Where("collects.collect_type = ? AND collects.user_id = ? AND collects.status = ?", collectType, userID, int8(1)).
|
||||
Where("o.user_id = ? OR (o.is_public = ? AND o.audit_status = ? AND o.status = ?)", userID, 2, 2, 1)
|
||||
|
||||
var total int64
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
@@ -101,7 +136,8 @@ func (d *CollectDao) CollectListTargetIDsByUserPage(ctx context.Context, collect
|
||||
}
|
||||
|
||||
var ids []int64
|
||||
if err := q.Order("id DESC").Offset((page-1)*pageSize).Limit(pageSize).Pluck("target_id", &ids).Error; err != nil {
|
||||
if err := q.Order("collects.id DESC").Offset((page-1)*pageSize).Limit(pageSize).
|
||||
Pluck("collects.target_id", &ids).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
@@ -86,6 +86,34 @@ func (d *LikeDao) LikeCountByTarget(ctx context.Context, likeType int8, targetID
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// LikeCountByTargets 批量统计多个目标的点赞数(仅 status=1),返回 targetID -> count;空入参短路。
|
||||
func (d *LikeDao) LikeCountByTargets(ctx context.Context, likeType int8, targetIDs []int64) (map[int64]int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.LikeCountByTargets")
|
||||
defer span.End()
|
||||
|
||||
counts := make(map[int64]int64, len(targetIDs))
|
||||
if len(targetIDs) == 0 {
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
var rows []struct {
|
||||
TargetID int64 `gorm:"column:target_id"`
|
||||
Cnt int64 `gorm:"column:cnt"`
|
||||
}
|
||||
if err := d.db.WithContext(ctx).Model(&mysqlmodel.Like{}).
|
||||
Select("target_id, COUNT(*) AS cnt").
|
||||
Where("like_type = ? AND target_id IN ? AND status = ?", likeType, targetIDs, int8(1)).
|
||||
Group("target_id").
|
||||
Scan(&rows).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rows {
|
||||
counts[r.TargetID] = r.Cnt
|
||||
}
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
// LikeListUserIDsByTarget 查询点赞用户ID列表(仅 status=1)
|
||||
func (d *LikeDao) LikeListUserIDsByTarget(ctx context.Context, likeType int8, targetID int64) ([]int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.LikeListUserIDsByTarget")
|
||||
|
||||
@@ -165,6 +165,28 @@ func (d *OutfitDao) OutfitListByUserID(ctx context.Context, userID int64, isPubl
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
// OutfitListCommunity 分页查询社区穿搭(其他用户「公开 + 审核通过 + 状态正常」的穿搭)。
|
||||
// 状态字面量与 service.isOutfitPublicVisible 保持一致:is_public 2=公开;audit_status 2=审核通过;status 1=正常。
|
||||
func (d *OutfitDao) OutfitListCommunity(ctx context.Context, excludeUserID int64, page, pageSize int) ([]mysqlmodel.Outfit, int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.OutfitListCommunity")
|
||||
defer span.End()
|
||||
|
||||
var list []mysqlmodel.Outfit
|
||||
var total int64
|
||||
q := d.db.WithContext(ctx).Model(&mysqlmodel.Outfit{}).
|
||||
Where("is_public = ? AND audit_status = ? AND status = ?", int8(2), int8(2), int8(1)).
|
||||
Where("user_id != ?", excludeUserID)
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return nil, 0, err
|
||||
}
|
||||
if err := q.Offset((page - 1) * pageSize).Limit(pageSize).Order("id DESC").Find(&list).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
return nil, 0, err
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
// OutfitItemDao 穿搭明细数据访问
|
||||
type OutfitItemDao struct {
|
||||
db *gorm.DB
|
||||
|
||||
@@ -119,10 +119,18 @@ func (d *OutfitShareReceiverDao) OutfitShareReceivedList(ctx context.Context, re
|
||||
Joins("JOIN outfit_share_receivers r ON r.share_uuid = outfit_shares.share_uuid").
|
||||
Joins("JOIN outfits o ON o.id = outfit_shares.outfit_id").
|
||||
Where("r.receiver_user_id = ?", receiverUserID).
|
||||
Where("outfit_shares.status = ?", 1).
|
||||
Where("o.is_public = ? AND o.audit_status = ? AND o.status = ? AND o.deleted_at = 0", 2, 2, 1)
|
||||
// 可见性:仅排除「已删除」与「已封禁」的穿搭。
|
||||
// 分享与公开状态解耦(仅自己可见的穿搭同样可分享),故不再按 is_public / audit_status 过滤;
|
||||
// 穿搭删除或封禁后不再下发内容。
|
||||
Where("o.status <> ? AND o.deleted_at = 0", 2)
|
||||
if status != 0 {
|
||||
// 显式筛选分享状态:1=正常 2=已取消。
|
||||
// 修复:此前这里无条件叠加 `outfit_shares.status = 1`,与下面的参数条件形成
|
||||
// `status = 1 AND status = 2`,导致传 status=2 恒返回空列表。
|
||||
q = q.Where("outfit_shares.status = ?", status)
|
||||
} else {
|
||||
// 默认只返回正常分享:已取消的分享不应再出现在接收列表(保留「取消分享后不再下发」语义)。
|
||||
q = q.Where("outfit_shares.status = ?", 1)
|
||||
}
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
span.RecordError(err)
|
||||
|
||||
Reference in New Issue
Block a user