first commit
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/global"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/dto"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqldao"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqlmodel"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/redisdao"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/pkg/errcode"
|
||||
)
|
||||
|
||||
// LikeService 点赞业务服务。
|
||||
type LikeService struct {
|
||||
likeDao *mysqldao.LikeDao
|
||||
outfitDao *mysqldao.OutfitDao
|
||||
}
|
||||
|
||||
// NewLikeService 创建点赞服务。
|
||||
func NewLikeService() *LikeService {
|
||||
return &LikeService{
|
||||
likeDao: mysqldao.NewLikeDao(),
|
||||
outfitDao: mysqldao.NewOutfitDao(),
|
||||
}
|
||||
}
|
||||
|
||||
// LikeCreate 点赞或取消点赞社区穿搭。
|
||||
func (s *LikeService) LikeCreate(ctx context.Context, userID int64, request *dto.LikeCreateRequest) (*dto.LikeResponse, *errcode.Error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.LikeLikeCreate")
|
||||
defer span.End()
|
||||
|
||||
outfit, cerr := s.getPublicOutfit(ctx, userID, request.TargetUUID)
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
targetID := outfit.ID
|
||||
|
||||
existing, err := s.likeDao.LikeGetByTargetAndUser(ctx, LikeTypeOutfit, targetID, userID)
|
||||
notExist := false
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
notExist = true
|
||||
} else {
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
}
|
||||
|
||||
switch request.Action {
|
||||
case ActionDo: // 点赞
|
||||
if cerr := s.likeDao.LikeUpsertOn(ctx, &mysqlmodel.Like{
|
||||
LikeType: LikeTypeOutfit,
|
||||
TargetID: targetID,
|
||||
UserID: userID,
|
||||
Status: InteractStatusOn,
|
||||
}); cerr != nil {
|
||||
return nil, errcode.LikeFailed
|
||||
}
|
||||
_ = redisdao.LikeAdd(ctx, LikeTypeOutfit, targetID, userID)
|
||||
|
||||
// 仅在「从取消/无记录变为点赞」时发通知,避免并发/重复点赞重复通知。
|
||||
wasLiked := !notExist && existing.Status == InteractStatusOn
|
||||
if !wasLiked && outfit.UserID != userID {
|
||||
sendNotification(ctx, outfit.UserID, userID, NotificationTypeLike, "收到新的点赞", "你的穿搭「"+outfit.OutfitName+"」收到了一个赞")
|
||||
}
|
||||
|
||||
case ActionCancel: // 取消点赞
|
||||
if !notExist && existing.Status == InteractStatusOn {
|
||||
if uerr := s.likeDao.LikeUpdateStatus(ctx, existing.ID, InteractStatusOff); uerr != nil {
|
||||
return nil, errcode.LikeFailed
|
||||
}
|
||||
_ = redisdao.LikeRemove(ctx, LikeTypeOutfit, targetID, userID)
|
||||
}
|
||||
}
|
||||
|
||||
return &dto.LikeResponse{}, nil
|
||||
}
|
||||
|
||||
// GetByTargetID 查询穿搭点赞状态与点赞数(Redis 优先)。
|
||||
func (s *LikeService) GetByTargetID(ctx context.Context, userID int64, request *dto.LikeGetByTargetIDRequest) (*dto.LikeGetByTargetIDResponse, *errcode.Error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.LikeGetByTargetID")
|
||||
defer span.End()
|
||||
|
||||
outfit, cerr := s.getPublicOutfit(ctx, userID, request.TargetUUID)
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
targetID := outfit.ID
|
||||
|
||||
// 是否已点赞:Redis 优先,回落 MySQL。
|
||||
isLiked, err := redisdao.LikeIsMember(ctx, LikeTypeOutfit, targetID, userID)
|
||||
if err != nil {
|
||||
if rec, derr := s.likeDao.LikeGetByTargetAndUser(ctx, LikeTypeOutfit, targetID, userID); derr == nil {
|
||||
isLiked = rec.Status == InteractStatusOn
|
||||
}
|
||||
}
|
||||
|
||||
// 点赞总数:Redis 优先,回落 MySQL。
|
||||
count, err := redisdao.LikeCount(ctx, LikeTypeOutfit, targetID)
|
||||
if err != nil || count == 0 {
|
||||
if dbCnt, derr := s.likeDao.LikeCountByTarget(ctx, LikeTypeOutfit, targetID); derr == nil {
|
||||
count = dbCnt
|
||||
}
|
||||
}
|
||||
|
||||
return &dto.LikeGetByTargetIDResponse{
|
||||
UserIsLiked: isLiked,
|
||||
LikeCount: count,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getPublicOutfit 按 UUID 查询社区穿搭(须存在),非本人仅可操作公开可见的穿搭。
|
||||
func (s *LikeService) getPublicOutfit(ctx context.Context, userID int64, outfitUUID string) (*mysqlmodel.Outfit, *errcode.Error) {
|
||||
outfit, err := s.outfitDao.OutfitGetByUUID(ctx, outfitUUID)
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
return nil, errcode.TargetNotFound
|
||||
}
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
if outfit.UserID != userID && !isOutfitPublicVisible(outfit) {
|
||||
return nil, errcode.OutfitNoPermission
|
||||
}
|
||||
return outfit, nil
|
||||
}
|
||||
Reference in New Issue
Block a user